应使用 error 而非 panic 处理业务错误,因 panic 会中断 goroutine 并可能导致服务崩溃;所有 handler/service 函数须返回 (result, error),显式检查并用 fmt.errorf 包装以保留堆栈;模块内定义结构化错误类型(如 ordererror),中间件统一序列化为标准 json 错误响应;对临时性错误(如超时、429)可重试,永久性错误(如 400、404)不可重试;关键路径需记录重试次数与最终错误;所有 i/o 操作必须使用 request-scoped context 并定期检查 ctx.done() 避免 goroutine 泄漏。

用 error 而不是 panic 处理业务错误
绝大多数 HTTP 请求失败、参数校验不通过、数据库查不到记录,都属于可预期、可恢复的业务错误,panic 会中断 goroutine 并可能触发整个服务崩溃,完全不适合这类场景。
常见错误现象:把 errors.New("user not found") 包进 panic,再用中间件 recover —— 这既掩盖了错误语义,又让调用链失去控制权,还干扰了监控指标(比如 http_status_500 误报)。
- 所有 handler、service 层函数返回
(result, error),错误必须显式检查 - 用
fmt.Errorf("xxx: %w", err)包装下游错误,保留原始堆栈上下文 - 避免在 defer 中 recover 业务错误;只用于捕获真正不可控的 panic(如第三方库空指针)
模块内定义结构化错误类型并导出构造函数
全局错误码表(如 errors.go 里一堆 const ErrCodeX = 1001)很快就会冲突、难维护。更可靠的方式是每个业务模块自己管自己的错误。
使用场景:API 返回 JSON 错误时需带 errCode、errorString、details 字段供前端解析或重试逻辑判断。
- 在
order/service.go中定义type OrderError struct { Code int; Message string; Details map[string]interface{} } - 实现
Error() string方法,满足error接口 - 导出预设变量如
ErrInvalidOrderID = &OrderError{Code: 2001, Message: "order ID is invalid"} - 中间件统一检查
errors.As(err, &OrderError{}),序列化为标准错误响应
对临时性失败做幂等重试,但跳过永久性错误
网络超时、DB 事务冲突、下游限流返回 429 —— 这类错误适合重试;而 400 参数错误、401 认证失败、404 资源不存在,重试只会放大问题。
性能影响:无条件重试 + 指数退避,可能在雪崩初期加剧下游压力。
- 封装重试逻辑时,先用
errors.Is(err, context.DeadlineExceeded)或自定义错误类型判断是否可重试 - 使用
github.com/avast/retry-go时,通过retry.RetryIf(func(err error) bool { return isTransient(err) })控制重试边界 - 重试次数建议 ≤ 3,首次延迟 ≤ 100ms,避免堆积请求
- 关键路径上记录重试次数和最终错误,用于定位是否频繁触发临时故障
context 传递超时与取消,避免 goroutine 泄漏
一个 HTTP 请求超时,但其启动的 goroutine 还在跑 DB 查询或调第三方 API,这是最典型的资源泄漏来源。
容易踩的坑:只传 context.Background() 或忽略 ctx.Done() 检查。
- 所有 I/O 操作(
http.Client.Do、db.QueryContext、redis.Get(ctx, key))必须传入 request-scoped context - 自定义函数签名统一以
func(ctx context.Context, ...)开头,强制上游注入 - 在循环或长耗时处理中定期检查
select { case - 不要用
context.WithCancel(context.Background())替代 request context —— 它无法被父级 cancel,且无 deadline
真正难的不是写重试或定义错误类型,而是判断某个错误到底算“临时”还是“永久”,这需要结合协议语义、下游文档和线上行为共同决策;很多团队卡在这一步,最后退回到全量重试或全量 panic,反而破坏了容错设计的初衷。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











