首页/开发/系统分析与架构设计速查/gRPC 拦截器与横切关注点

gRPC 拦截器与横切关注点

拦截器统一处理鉴权、超时、重试、指标与链路传播:一元与流式拦截器形态不同;顺序与幂等性要设计,避免重复采集或覆盖上下文。

拦截器是什么

gRPC 拦截器是服务端与客户端在方法调用前后插入统一逻辑的机制,形态接近中间件:鉴权、超时控制、日志、指标、链路传播、限流与异常恢复都适合放在这里,避免在每个方法里重复实现。一元调用与流式调用的拦截器签名不同,必须分别实现与注册,只注册一元的实现会让流式接口出现治理缺口。

顺序与幂等为什么关键

拦截器按注册顺序形成调用链,顺序直接决定语义:鉴权应早于业务处理,链路上下文要在日志与指标之前建立,异常恢复应包住重试与业务调用。若重试拦截器排在上下文传播之前,每次重试都会丢失追踪信息;若同一件事在客户端与服务端各做一次,实际重试次数会被放大。设计时先画清链路顺序,再给每一环分配职责。

基本信息

覆盖能力鉴权、日志、指标、链路追踪、限流、异常恢复与重试
形态区分一元拦截器与流式拦截器签名不同,需分别注册
顺序影响注册顺序构成调用链,顺序不同则超时与重试语义不同
上下文传递经元数据传递追踪标识与租户信息,注意元数据大小限制
错误映射统一转换为标准状态码,原始信息放入详情字段

常见拦截器顺序

顺序职责注意
恢复捕获异常并转为标准状态码放在最外层,兜住后续环节
链路传播提取或生成追踪上下文必须在日志与指标之前执行
日志与指标记录耗时、方法与结果采样率与脱敏规则提前约定
鉴权与限流校验令牌与调用配额远程校验结果做短时缓存
重试仅对幂等调用重试客户端与服务端择一实现

典型使用场景

  • 所有接口统一校验访问令牌与租户隔离信息
  • 统一采集调用耗时、错误率与方法维度指标
  • 统一注入与透传追踪标识,串联跨服务调用链路
  • 统一处理异常,返回规范状态码而不是连接中断
  • 在流式接口上实现同样的鉴权与限流,避免治理缺口

Go 一元拦截器骨架

Go 一元拦截器骨架
func UnaryLogger(ctx context.Context, req any, info *grpc.UnaryServerInfo, h grpc.UnaryHandler) (any, error) {
    start := time.Now()      # 记录进入时间
    resp, err := h(ctx, req) # 执行业务方法
    log.Printf("%s dur=%s err=%v", info.FullMethod, time.Since(start), err)
    return resp, err
}
拦截器里做远程鉴权会放大每次调用的延迟,校验结果可按令牌做短时缓存,但缓存有效期要明显短于令牌本身的签发有效期。
流式拦截器容易被遗漏:只注册一元拦截器时,流式方法不会经过鉴权与限流,形成安全缺口,上线前要用流式接口实测一遍。

易错点

  • 只注册一元拦截器,流式接口的鉴权与限流全部失效且不易察觉
  • 拦截器顺序错误,追踪上下文在返程被覆盖,链路断成互不相连的两截
  • 重试同时写在客户端与服务端,实际重试次数成倍放大,故障时冲垮下游
  • 在拦截器里做远程鉴权且不缓存,每次调用额外增加一次网络往返
  • 异常恢复未转为标准状态码,客户端只看到连接中断,无法区分失败原因

数据来源与参考

  • gRPC Go 文档:Interceptors
  • gRPC 官方文档:Metadata
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌