如何让gRPC服务端所有请求处理函数可访问指定公共变量
gRPC服务端全局变量注入请求处理函数最优方案
结论
优先选择「将变量添加到所有gRPC请求的context中」的方案,另外两种方案存在明显缺陷,不推荐使用。
具体实现步骤
1. 定义context专属key
避免和其他组件的context key冲突:
// 私有类型防止外部key冲突 type ctxKey string const fooCtxKey ctxKey = "foo"
2. 编写一元服务端拦截器
通过gRPC拦截器统一将foo注入到所有请求的context中:
func fooInjectInterceptor(foo string) grpc.UnaryServerInterceptor { return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { ctx = context.WithValue(ctx, fooCtxKey, foo) return handler(ctx, req) } }
如果有流式请求,可对应编写流式拦截器注册即可。
3. 注册拦截器到gRPC服务端
修改Run函数中服务端初始化逻辑:
func Run(...) ... { foo := "bar" // 初始化时传入拦截器 grpcServer := grpc.NewServer( grpc.UnaryInterceptor(fooInjectInterceptor(foo)), ) grpc_health_v1.RegisterHealthServer(grpcServer, health.NewServer()) pb.RegisterMyServer(grpcServer, newServer()) signal.Notify(sigChan, syscall.SIGTERM, syscall.SIGINT) go func() { if err := grpcServer.Serve(lis); err != nil { errChan <- err } }() return grpcServer, nil }
4. 请求处理函数中取值
在Ping等所有处理函数中直接从context读取即可:
func (s *myServer) Ping( ctx context.Context, pingRequest *pb.PingRequest, ) (*pb.PingResponse, error) { foo := ctx.Value(fooCtxKey).(string) // 正常使用foo变量即可 return &pb.PingResponse{}, nil }
其他方案缺陷说明
- 全局变量:存在全局状态污染问题,多gRPC服务实例部署时变量会互相干扰,单元测试无法隔离不同用例的变量值,后续扩展性差
- 单例模式:本质和全局变量问题一致,强耦合全局状态,不支持多实例部署场景,测试成本高
内容的提问来源于stack exchange,提问作者dbzuk
相关产品推荐
相关产品推荐

