跳到主要内容

错误处理与生命周期

如何识别 RPC 的失败层次?​

  • RPC 异常: 请求没有按预期完成远程调用,例如超时、无实例、网络错误或治理拒绝;
  • 业务异常: 请求已到达并完成业务判断,但结果不满足业务规则,例如用户不存在;
类别含义处理
BizStatusError请求到达并完成业务判断按业务码处理,不盲目重试
timeout超过连接或调用预算判断阶段、幂等与剩余预算
discovery/LB无实例或选择失败检查控制面与实例状态
remote/network对端或网络异常保留错误链并结合链路定位
circuit/overlimit/ACL治理主动拒绝降载、退避或返回明确状态
panic/internalHandler 或框架内部异常报警并定位堆栈

如何传递并读取业务异常?​

  • 业务异常: Handler 用 kerrors.NewBizStatusError(code, message) 或 WithExtra 版本表达业务拒绝;Client 用 kerrors.FromBizStatusError(err) 读取 code、message、extra;
  • 传输前提: Thrift / Kitex Protobuf 需要 TTHeader 及双方 MetaHandler;gRPC 使用 HTTP/2 元信息,不能假设任意 Thrift 对端都认识 BizStatus;
  • 中间件: Handler 的 BizStatus 被框架放入 RPCInfo,next 可能返回 nil;读取 rpcinfo.GetRPCInfo(ctx).Invocation().BizStatusErr() 才能统计业务拒绝;
  • 监控: RPC 完成与业务成功是两个维度;不用 ErrorHandler 把业务拒绝改成网络失败;
  • 消息: 业务码稳定、文本可公开;SQL、DSN 和堆栈只在服务端记录;

如何安全地异步读取 RPCInfo?​

  • RPCInfo: 一次调用的服务、方法、地址、耗时与配置对象;请求结束后可能进入 sync.Pool 被其他请求复用;
  • 冻结: 异步读取前复制 RPCInfo,异步任务仍需独立管理 deadline 与退出,不把冻结当作无限延长整个请求;
frozen := rpcinfo.FreezeRPCInfo(ctx)
go func() {
ri := rpcinfo.GetRPCInfo(frozen)
log.Println(ri.To().Method())
}()

如何处理 Handler panic?​

  • 恢复: Kitex 将可恢复的 Handler panic 转为带 [happened in biz handler] 的错误与堆栈;按故障处理,不用 panic 表达正常业务拒绝;
  • 边界: 并发写 map 等 fatal runtime error 不属于可恢复的普通 panic,应通过并发设计和 race 检查预防;

如何停止服务并释放资源?​

  • 优雅退出: 服务停止接收新流量,但给正在处理的请求留出完成时间,再关闭进程和资源;
  • 第一步: readiness 先变为不可接收新流量;
  • 第二步: Registry/编排平台完成摘流;
  • 第三步: Server 等待在途请求与 stream;
  • 第四步: 达到 ExitWaitTime 后强制结束;
  • 第五步: 关闭 tracer/exporter、注册中心 Client 与其他资源;