rpc handler必须各自加defer+recover,因recover仅对当前goroutine有效,主线程的recover无法捕获rpc子协程panic;每个handler需在开头注册defer func(){if r:=recover();r!=nil{log...;return nil,errors.new("internal error")}}(),且recover后不可继续使用可能已破坏的状态。

RPC handler 必须各自加 defer+recover
Go 的 recover 对 goroutine 隔离,RPC 方法调用(如 gRPC 的 handler、net/rpc 的 method)都在独立协程中执行。在 main 函数里加一层 defer func() { recover() }() 完全无效——它只管主线程,不管 RPC 请求协程。
正确做法是在每个 RPC 方法内部开头就注册 defer:
func (s *MyService) DoSomething(ctx context.Context, req *Request) (*Response, error) {
defer func() {
if r := recover(); r != nil {
log.Printf("panic in DoSomething: %v", r)
// 注意:这里不能 return response,必须显式返回 error
}
}()
// 业务逻辑...
}
- 必须是
defer func() { ... }(),不是defer recover()(后者会立即执行,返回nil) - 如果使用命名返回值(如
func(...) (resp *Response, err error)),可在recover块里赋值err;否则需手动构造错误并返回 - gRPC 的
UnaryInterceptor或 net/rpc 的 wrapper 是更干净的统一入口,但底层仍是为每个调用帧单独加defer
recover 后不能继续用原参数或状态
RPC panic 往往由非法输入、空指针解引用、并发写 map 等引发,recover 只能阻止崩溃,不修复已破坏的状态。比如 req.Name 是 nil 导致 panic,recover 后再读 req.Name 仍会二次 panic。
诊断并恢复通过 SSH 隧道连接的 OpenClaw 节点。用于解决配对必需错误、隧道冲突、远程端点错误以及 SSH 目标配置错误等问题。
- 不要在
recover块里尝试继续处理req、ctx或任何可能已被污染的局部变量 - 不要调用
close(ch)、向已损坏的sync.Map写入、或访问刚被panic("index out of range")破坏的 slice - 推荐模式:
if r := recover(); r != nil { log.Error(...); return nil, errors.New("internal error") }—— 记录完立刻返回,不碰任何业务变量
跨协程 panic 要在启动处包一层
RPC 服务常伴随后台任务:比如在 handler 中启一个 go func() { ... }() 做异步通知、日志上报或清理。这类子协程若 panic,主 handler 的 recover 完全无感,整个进程可能因未捕获 panic 而退出(尤其当它触发 runtime.fatalerror)。
- 每个
go启动点都必须自己包defer func() { recover() }() - 别依赖“全局兜底”,例如:
go func() { defer func(){recover()}(); doAsyncWork() }() - 第三方库(如
robfig/cron)默认不 recover,定时任务 panic 会导致服务重启;需封装cron.AddFunc(spec, func(){ defer final(); work() })
recover 不等于熔断,必须配合失败计数
RPC 接口高频 panic 时,光靠 recover 打日志、返回 500 是危险的——下游重试会加剧雪崩,资源泄漏会持续累积。真正的高可用需要把 recover 和熔断联动起来。
- 每次
recover捕获后,必须调用熔断器的MarkFailed()(如gobreaker.NewCircuitBreaker(...).MarkFailed()) - 入口处(如 gRPC interceptor)要先
breaker.Allow(),返回false就直接降级,不进业务逻辑 - 避免用
hystrix-go.Do,它内部不 recover;改用DoC,它才做兜底并转成可判断的*hystrix.Error - recover 后别调
os.Exit(),k8s 会判定为 crash;应走srv.Shutdown()并配合理超时(10–30 秒)
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










