Kratos 中 panic 恢复必须显式注册 recovery.Recovery()(HTTP,放 middleware 末尾)或 UnaryServerInterceptor(gRPC),并主动写响应、记录带 traceID 的日志,配合熔断与优雅关闭,子 goroutine 需单独 recover。

必须在 Kratos 的 http.Server 或 grpc.Server 初始化时,通过 http.Middleware 或 grpc.Middleware 显式注册自定义 recovery.UnaryServerInterceptor(gRPC)或 recovery.Recovery()(HTTP),不能依赖全局 defer 或框架默认行为——Kratos 不自带 panic 捕获,漏配即崩溃。
recover 必须作为最外层 HTTP 中间件注册
Kratos 的 HTTP 中间件链是“后置注册、前置执行”,recovery.Recovery() 必须放在 http.Middleware(...) 参数列表的最末尾,才能成为实际调用链的最外层。否则 Tracing、Auth 等中间件一旦 panic,会直接退出进程。
- 错误写法:
http.Middleware(tracing.Server(), logging.Server(), recovery.Recovery())→ recovery 在内层,无效 - 正确写法:
http.Middleware(tracing.Server(), logging.Server(), recovery.Recovery())→ 注意顺序:recover 放最后 - 验证方式:故意在 Auth 中间件里写
panic("auth crash"),观察服务是否存活、日志是否输出 panic 堆栈 - 底层原理:Kratos
Chain函数从右往左嵌套中间件,最后一个参数生成的 handler 包裹所有前面的 handler
gRPC recover 拦截器要区分 Unary/Stream 且必须用 UnaryServerInterceptor
gRPC 的 panic 恢复不能复用 HTTP 的 recovery.Recovery(),必须使用 grpc.UnaryServerInterceptor 类型的拦截器,并显式传入 UnaryRecoveryInterceptor 实例;Stream 方法需单独实现 StreamServerInterceptor,否则流式调用 panic 仍会导致进程退出。
-
UnaryRecoveryInterceptor必须返回grpc.UnaryServerInterceptor类型函数,签名严格匹配func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) - 别直接用
func() { recover() }匿名函数塞进grpc.StreamInterceptor—— 类型不兼容,编译失败 - 常见错误:只加了 Unary 拦截器,但业务用了
rpc StreamEcho(stream Echo_StreamEchoServer) error,此时 panic 无法捕获 - 生产建议:即使当前没用 Stream,也应同步实现
StreamRecoveryInterceptor并注册,避免未来迭代引入风险
recover 后必须主动写响应头+体,不能只 log
捕获 panic 后若不调用 w.WriteHeader(500) 和 w.Write(...),客户端将卡在等待响应状态(TCP 连接不关闭),连接堆积、goroutine 泄漏随之而来。
- Kratos 的
recovery.Recovery()默认已处理响应写入,但自定义实现时极易遗漏:w.WriteHeader(http.StatusInternalServerError)和json.NewEncoder(w).Encode(map[string]string{"error": "internal server error"}) - 别在 recover 块里调
c.JSON(500, ...)(c是 Kratoshttp.Context)——它内部可能再次 panic(如 header 已写) - 日志必须带
transport.FromContext(ctx).Operation()和 traceID,否则无法关联请求上下文,报警无意义 - 堆栈信息仅开发环境启用:
debug.Stack()生产环境禁用,防止敏感路径泄露
recover 不等于熔断,必须配合失败计数与优雅 Shutdown
单纯 recover 只是止血,高频 panic 时服务已不可信,必须触发熔断并主动退出。Kratos 本身不提供熔断器,需自行集成 gobreaker 或 sony/gobreaker,并在 recover 后调用 breaker.MarkFailed() 和 srv.Shutdown()。
- 漏掉
breaker.MarkFailed()→ 熔断器状态永远是 Closed,下游持续打挂 - 用
os.Exit(1)替代srv.Shutdown()→ k8s 认定为 CrashLoopBackOff,反复拉起失败实例 -
srv.Shutdown()必须用sync.Once控制,避免多 panic 触发多次 shutdown 导致 panic - Shutdown 超时设为 15 秒:太短丢活跃请求,太长阻塞滚动更新
最容易被忽略的是 panic 发生在子 goroutine 里——比如 HTTP handler 中启动 go func() { db.Exec(...) }(),这个 goroutine 的 panic 完全不会被 recovery.Recovery() 捕获。每个独立 goroutine 入口都得自己包一层 defer func() { recover() }(),没有例外。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











