GRPC客户端链式自定义拦截器/DialOptions仅最后一个生效问题排查
gRPC客户端多Unary拦截器仅最后一个生效的解决方法
在配置gRPC客户端时,尝试通过多个grpc.WithUnaryInterceptor添加自定义拦截器,发现只有最后一个拦截器被执行,前面的会被跳过。示例代码如下:
myOpt1 := grpc.WithUnaryInterceptor(func(ctx context.Context, method string, req, reply any, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { print("testInterceptor invoked") return invoker(ctx, method, req, reply, cc, opts...) }) myOpt2 := grpc.WithUnaryInterceptor(func(ctx context.Context, method string, req, reply any, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { print("testInterceptor2 invoked") return invoker(ctx, method, req, reply, cc, opts...) }) opts := []grpc.DialOption{ grpc.WithTransportCredentials(insecure.NewCredentials()), myOpt1, // 被跳过 myOpt2, // 仅这个生效 } conn, err := grpc.DialContext(context.Background(), "my-adress:443", opts...) // err is nil myService := pbAugment.NewMyServiceClient(conn) myService.MyFunction(context.Background()) // 输出: testInterceptor2 invoked
问题原因
grpc.WithUnaryInterceptor的设计逻辑是替换而非追加拦截器。每调用一次该函数生成的DialOption,都会覆盖之前设置的Unary拦截器。因此当你在DialOption切片中放入多个WithUnaryInterceptor生成的选项时,只有最后一个会生效。
解决方案
使用grpc.WithChainUnaryInterceptor替代多个WithUnaryInterceptor,这个方法支持传入多个Unary拦截器函数,会自动将它们按顺序链式组合,执行时会按传入顺序依次调用每个拦截器。
修改后的代码示例:
// 定义两个拦截器函数 interceptor1 := func(ctx context.Context, method string, req, reply any, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { print("testInterceptor invoked") return invoker(ctx, method, req, reply, cc, opts...) } interceptor2 := func(ctx context.Context, method string, req, reply any, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { print("testInterceptor2 invoked") return invoker(ctx, method, req, reply, cc, opts...) } opts := []grpc.DialOption{ grpc.WithTransportCredentials(insecure.NewCredentials()), // 用WithChainUnaryInterceptor组合多个拦截器 grpc.WithChainUnaryInterceptor(interceptor1, interceptor2), } conn, err := grpc.DialContext(context.Background(), "my-adress:443", opts...) if err != nil { // 处理错误 } myService := pbAugment.NewMyServiceClient(conn) myService.MyFunction(context.Background()) // 输出: testInterceptor invokedtestInterceptor2 invoked
调用时会先执行interceptor1,再执行interceptor2,最后执行实际的RPC调用。如果需要调整执行顺序,只需调整WithChainUnaryInterceptor中参数的顺序即可。
内容的提问来源于stack exchange,提问作者NotX
相关产品推荐
相关产品推荐

