c++ grpc拦截器 c++如何实现grpc的客户端和服务端interceptor

张开发
2026/5/18 8:58:26 15 分钟阅读
c++ grpc拦截器 c++如何实现grpc的客户端和服务端interceptor
gRPC C 无原生拦截器API稳定方案为客户端封装Stub重载方法、服务端继承Service并重写Request*方法experimental::Interceptor已废弃且不可靠。gRPC C 没有原生 interceptor API别直接搜 grpc::InterceptorgRPC 官方 C 库截至 v1.60压根没提供类似 Java 或 Go 那种开箱即用的拦截器接口。你搜到的很多文章讲的是 grpc::experimental::Interceptor 或基于 ChannelArguments 的 hack 方式但这些要么已废弃要么只在特定版本/构建配置下可用且不被稳定 ABI 保证。强行套用会导致编译失败、链接不到符号或者运行时静默失效。真正稳定可用的机制只有客户端靠 grpc::Channel 封装 自定义 stub服务端靠继承 grpc::Service 并重写 Request* 方法所有“拦截逻辑”必须手动注入到 RPC 调用链路中没有中间件注册点如果你依赖 Bazel 构建且用了 grpc_cpp_plugin 生成的 stub那它默认不预留拦截钩子 —— 钩子得你自己加客户端怎么加日志/超时/Token 注入封装 Stub 重载方法不能改生成的 *.grpc.pb.h但可以写一个包装类在调用 stub-Method() 前后插逻辑。关键不是“拦截”而是“代理”。构造时传入原始 std::shared_ptr:channel/:channel用它 new 出原始 stub每个 RPC 方法都重写比如 MyServiceStub::SayHello() 内部先调 PreCallHook()注入 header、打日志再调 raw_stub_-SayHello()最后调 PostCallHook()统计耗时、处理 status注意 grpc::ClientContext 必须由调用方传入或内部 new —— 如果你在包装里 new外部就无法控制 deadline/cancellation建议要求调用方传入你只往里面 AddMetadata()异步流式调用AsyncClientReaderWriter没法简单包装因为生命周期和回调绑定紧密得用 std::shared_ptr 管理上下文并透传 tagclass LoggingStub {public: explicit LoggingStub(std::shared_ptrgrpc::Channel ch) : raw_stub_(greeter::Greeter::NewStub(ch)) {}pgrpc::Status SayHello(grpc::ClientContextem ctx,const greeter::HelloRequest req,greeter::HelloReply/em resp) {ctx-AddMetadata(x-request-id, GenerateID());auto start std::chrono::steady_clock::now();auto status rawemstub/em-SayHello(ctx, req, resp);auto dur std::chrono::steady_clock::now() - start;LOG(INFO) SayHello: status.ok() in dur.count() ms;return status;}/ppprivate:std::unique_ptrgreeter::Greeter::Stub rawemstub/em;};/p服务端如何做鉴权/审计重写 Service::Request* 和 Service::AsyncNotifyWhenDone服务端唯一可控入口是 Service 子类里的 Request* 方法如 RequestSayHello它们在 RPC 刚到达、还没进业务 handler 时被调用。这里能拿到 grpc::ServerContext* 和原始 buffer但拿不到反序列化后的 request 对象 —— 所以鉴权得在反序列化前做比如检查 header审计日志得在反序列化后、handler 前/后补。RequestSayHello 是同步阻塞式入口适合轻量检查如 ctx-auth_context()-FindProperty(x-token)如果要解析完整 request 再鉴权比如校验 body 签名得自己调 ParseFromBoundedZeroCopyStream()但要注意 buffer 生命周期 —— 它只在当前 callback 有效不要试图在 Request* 里直接调业务逻辑这会破坏 gRPC 的线程模型导致并发错乱正确做法是把 context/request/refptr 交给业务线程池再用 Finish() 回写 response所有 AsyncService 的 Request* 方法都必须配对调用 NotifyWhenDone否则 server 会漏掉 completion queue 事件连接卡死为什么不用 grpc::experimental::InterceptedChannel它早就不维护了这个 experimental 接口在 v1.25 后就被标记为 deprecatedv1.40 彻底移除头文件声明。即使你从旧版本拷代码过来也会遇到 知网AI智能写作 知网AI智能写作写文档、写报告如此简单

更多文章