面试知识库
中 进阶

gRPC拦截器与错误处理#

一句话答案#

gRPC 拦截器分客户端/服务端两侧,类似 Spring 的 AOP 切面,用于统一处理日志、认证、限流、链路追踪等横切关注点;错误处理通过 Status Code + Metadata 传递结构化错误信息。

核心要点

拦截器分类#

客户端拦截器(ClientInterceptor):
  请求前 → 添加 Token/TraceId/Metadata → 发送 → 响应后 → 记录耗时/错误

服务端拦截器(ServerInterceptor):
  收到请求 → 鉴权/限流/日志 → 转发给 Service 实现 → 返回前 → 后处理
plaintext

服务端拦截器示例#

客户端拦截器示例#

gRPC Status Code#

Code含义对应场景
OK (0)成功-
CANCELLED (1)调用被取消(通常由调用方发起)客户端主动取消 / 上游请求已取消
INVALID_ARGUMENT (3)参数错误校验失败
NOT_FOUND (5)资源不存在按 ID 查询不到(列表查询为空应返回 OK + 空列表)
PERMISSION_DENIED (7)无权限RBAC 拒绝
UNAUTHENTICATED (16)未认证Token 无效/过期
RESOURCE_EXHAUSTED (8)资源耗尽限流
INTERNAL (13)内部错误未知异常
UNAVAILABLE (14)服务不可用熔断/宕机
DEADLINE_EXCEEDED (4)超时deadline 到期

结构化错误传递#

// 服务端返回详细错误
Status status = Status.INVALID_ARGUMENT
    .withDescription("order_id must be positive");
Metadata trailers = new Metadata();
trailers.put(ERROR_DETAIL_KEY, "{\"field\":\"order_id\",\"reason\":\"negative\"}");
responseObserver.onError(status.asRuntimeException(trailers));

// 客户端解析
try {
    stub.getOrder(request);
} catch (StatusRuntimeException e) {
    Status status = e.getStatus();
    Metadata trailers = e.getTrailers();
    // 根据 status.getCode() 做不同处理
}
java

拦截器链执行顺序#

grpc-java:ServerInterceptors.intercept(service, i1, i2) / ClientInterceptors.intercept(channel, i1, i2)
  → 列表中最后一个(i2)最先执行:i2 → i1 → Service 实现 / 发送请求
  → 想按列表顺序执行用 ServerInterceptors.interceptForward / ClientInterceptors.interceptForward
grpc-go:ChainUnaryInterceptor(i1, i2) → i1 在最外层,按列表顺序执行
plaintext

面试回答(2分钟版)

gRPC 拦截器分客户端和服务端两侧,类似 Spring AOP 的切面机制。服务端 ServerInterceptor 拦截请求做鉴权、限流、日志;客户端 ClientInterceptor 在请求前注入 TraceId、Token 等 Metadata,响应后记录耗时。多个拦截器形成链(注意 grpc-java 的 intercept() 是列表里最后一个最先执行)。错误处理方面,gRPC 定义了 17 种 Status Code(0–16),比如 UNAUTHENTICATED 未认证、RESOURCE_EXHAUSTED 限流、DEADLINE_EXCEEDED 超时。服务端通过 Status + Metadata 返回结构化错误,客户端 catch StatusRuntimeException 按 Code 分类处理。实践中常用拦截器实现:JWT 鉴权拦截器、Sentinel 限流拦截器、OpenTelemetry 链路追踪拦截器。和 Spring 拦截器的区别是 gRPC 拦截器工作在 RPC 层而非 HTTP 层,粒度更细(可以拦截 Streaming 的每条消息)。

追问与易错

追问方向:

  • “拦截器和 Spring AOP 什么关系?”→ 独立的,gRPC 拦截器在 RPC 层,Spring AOP 在 Bean 方法层
  • “怎么做全局异常处理?”→ 服务端拦截器 catch 所有异常,统一转为 gRPC Status 返回
  • “Deadline 怎么传播?”→ 客户端设置 deadline,以 grpc-timeout 请求头(剩余时长)发给服务端;服务端把它设为当前 Context 的 deadline,在同一 Context 里再调下游时自动带上剩余时间(Java 靠 io.grpc.Context,Go 靠 ctx),每一跳检查剩余时间,到期返回 DEADLINE_EXCEEDED

易错点:

  • ❌ “gRPC 用 HTTP 状态码”——用自己的 17 种 Status Code(0–16),放在 grpc-status trailer 里;正常响应的 HTTP 状态码都是 200
  • ❌ “拦截器只能改 Metadata,不能碰消息体”——可以:Java 里包装 ServerCall.Listener#onMessage / ClientCall#sendMessage,Go 的 unary 拦截器直接拿到 req,都能读取或替换消息(流式场景逐条拦截)
  • ❌ “UNAVAILABLE 就是服务挂了”——官方定义是”大概率是暂时性的,可退避重试”:连接失败、服务端正在关闭、负载均衡无可用节点、代理返回 HTTP 429/502/503 都会映射成它;主动限流按规范应返回 RESOURCE_EXHAUSTED