然后我们来编写客户端:
package main import ( "context" "fmt" "google.golang.org/grpc" "google.golang.org/grpc/metadata" "matsuri/yoyoyo" ) func main() { conn, err := grpc.Dial("127.0.0.1:33333", grpc.WithInsecure()) if err != nil { fmt.Println(err) return } defer func() { _ = conn.Close() }() client := yoyoyo.NewHelloClient(conn) // 先不传递 md := metadata.New(map[string]string{}) ctx := metadata.NewOutgoingContext(context.Background(), md) response, _ := client.SayHello(ctx, &yoyoyo.HelloRequest{}) fmt.Println(response.Info["error"]) // 缺少名为 "matsuri" 的 key,或者该 key 对应的 value 不正确 // 传递 md = metadata.New(map[string]string{"matsuri": "fubuki"}) ctx = metadata.NewOutgoingContext(context.Background(), md) response, _ = client.SayHello(ctx, &yoyoyo.HelloRequest{}) fmt.Println(response.Info) // map[name:神乐七奈] }我们看到以上便是拦截器,在进入方法之前先进入拦截器,如果不符合要求,那么拦截器直接就给你返回了;如果参数符合要求,那么再执行 handler(ctx, req)、也就是对应的方法,还是很好理解的。但是客户端也是可以设置拦截器的,但是说实话客户端的拦截器没有太大意义,客户端的拦截器更像是 Python 的装饰器,举个栗子:
package main import ( "context" "fmt" "google.golang.org/grpc" "google.golang.org/grpc/metadata" "matsuri/yoyoyo" "time" ) // 客户端的拦截器函数是如下类型 // type UnaryClientInterceptor func(ctx context.Context, method string, req, reply interface{}, cc *ClientConn, invoker UnaryInvoker, opts ...CallOption) error func ClientInterceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { start := time.Now() // 客户端对应的函数逻辑 err := invoker(ctx, method, req, reply, cc, opts...) fmt.Printf("用时 %s\n", time.Since(start)) return err } func main() { // 生成拦截器 opt := grpc.WithUnaryInterceptor(ClientInterceptor) // 这里将拦截器传进去 conn, err := grpc.Dial("127.0.0.1:33333", grpc.WithInsecure(), opt) if err != nil { fmt.Println(err) return } defer func() { _ = conn.Close() }() client := yoyoyo.NewHelloClient(conn) // 先不传递 md := metadata.New(map[string]string{}) ctx := metadata.NewOutgoingContext(context.Background(), md) response, _ := client.SayHello(ctx, &yoyoyo.HelloRequest{}) fmt.Println(response.Info["error"]) // 传递 md = metadata.New(map[string]string{"matsuri": "fubuki"}) ctx = metadata.NewOutgoingContext(context.Background(), md) response, _ = client.SayHello(ctx, &yoyoyo.HelloRequest{}) fmt.Println(response.Info) /* 用时 1.9953ms 缺少名为 "matsuri" 的 key,或者该 key 对应的 value 不正确 用时 998µs map[name:神乐七奈] */ }