在使用grpc的时候遇到了一个问题: 如何设置client端的超时时间? 网上搜了一大圈, 没有太明显的例子.
这里我们先看下看看grpc的helloworld例子:
client
1
2
3
4
5
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
func main() { // Set up a connection to the server. conn, err := grpc.Dial(address, grpc.WithInsecure()) if err != nil { log.Fatalf("did not connect: %v", err) } defer conn.Close() c := pb.NewGreeterClient(conn) // Contact the server and print out its response. name := defaultName if len(os.Args) > 1 { name = os.Args[1] } r, err := c.SayHello(context.Background(), &pb.HelloRequest{Name: name}) if err != nil { log.Fatalf("could not greet: %v", err) } log.Printf("Greeting: %s", r.Message) } |
稍微跟一下SayHello
的调用, 最后是调用到invoke
函数:
google.golang.org/grpc/call.go
1
2
3
4
5
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
func invoke(ctx context.Context, method string, args, reply interface{}, cc *ClientConn, opts ...CallOption) (e error) { c := defaultCallInfo mc := cc.GetMethodConfig(method) if mc.WaitForReady != nil { c.failFast = !*mc.WaitForReady } // 值得注意 if mc.Timeout != nil && *mc.Timeout >= 0 { var cancel context.CancelFunc ctx, cancel = context.WithTimeout(ctx, *mc.Timeout) defer cancel() } opts = append(cc.dopts.callOptions, opts...) for _, o := range opts { if err := o.before(&c); err != nil { return toRPCErr(err) } } defer func() { for _, o := range opts { o.after(&c) } }() |
这里值得注意的是context的超时设置. 仔细读下context的文档, 会发现context里面有对应的超时设置:
WithTimeout
1
2
3
4
5
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
package main
import ( "context" "fmt" "time" ) func main() { // Pass a context with a timeout to tell a blocking function that it // should abandon its work after the timeout elapses. ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond) defer cancel() select { case <-time.After(1 * time.Second): fmt.Println("overslept") case <-ctx.Done(): fmt.Println(ctx.Err()) // prints "context deadline exceeded" } } |
我们尝试在client调用之前设置context的超时:
client
1
2
3
4
5
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
func main() { // Set up a connection to the server. conn, err := grpc.Dial(address, grpc.WithInsecure()) if err != nil { log.Fatalf("did not connect: %v", err) } defer conn.Close() c := pb.NewGreeterClient(conn) // Contact the server and print out its response. name := defaultName if len(os.Args) > 1 { name = os.Args[1] } // 改一下 ctx, cancel := context.WithTimeout(context.Background(), 100 * time.Millisecond) defer cancel() r, err := c.SayHello(ctx, &pb.HelloRequest{Name: name}) if err != nil { log.Fatalf("could not greet: %v", err) } log.Printf("Greeting: %s", r.Message) } |
为保证超时, 在sever代码中休息一会儿:
server
扫描二维码关注公众号,回复:
8363082 查看本文章
1
2
3
4
5
|
// SayHello implements helloworld.GreeterServer
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) { time.Sleep(1 * time.Second) return &pb.HelloReply{Message: "Hello " + in.Name}, nil } |
分别运行client和server两个程序, 会看见client端打印:
1
|
could not greet: rpc error: code = DeadlineExceeded desc = context deadline exceeded |