拦截器是什么
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
}拦截器里做远程鉴权会放大每次调用的延迟,校验结果可按令牌做短时缓存,但缓存有效期要明显短于令牌本身的签发有效期。
流式拦截器容易被遗漏:只注册一元拦截器时,流式方法不会经过鉴权与限流,形成安全缺口,上线前要用流式接口实测一遍。