go代码审查应从main、init、http handler、context.withtimeout及自定义error类型入手,重点检查阻塞操作、资源泄漏、ctx监听、cancel配对、error链完整性。

从main和init开始盯,别通读全文件
Go代码审查不是逐行扫代码,真正高风险的逻辑往往藏在启动入口。func main()和func init()里最容易出现阻塞、资源未释放、全局状态竞争等问题。
常见错误现象:http.ListenAndServe没做错误处理导致进程静默退出;sql.Open后没调用db.Ping验证连接;os.Open后漏掉defer f.Close()。
- 检查所有
main中启动的服务是否包裹了log.Fatal或显式错误分支 - 扫描
init函数:是否存在跨包依赖初始化顺序问题?是否在init里做了I/O或网络调用? - 确认所有打开的资源(
*os.File、*sql.DB、http.Client)都有对应关闭路径,且defer不在goroutine内调用
HTTP handler必须监听ctx.Done()并关闭Body
Handler是外部请求入口,也是并发和超时控制的第一道关卡。不监听context或漏关response body,轻则连接泄漏,重则触发服务雪崩。
使用场景:所有注册到http.HandleFunc或http.ServeMux的函数,以及Gin/echo等框架中的handler函数。
- 检查是否从
r.Context()派生子ctx(如ctx, cancel := context.WithTimeout(r.Context(), 5*time.Second)),且cancel()被defer调用 - 确认
resp, err := http.Get(...)之后有defer resp.Body.Close(),且放在err判断之后(避免nil panic) - 禁止直接用
http.Error(w, "...", 500)而不记录日志;应统一走log.Printf或结构化日志库
自定义error类型要实现Unwrap和Is,否则errors.Is失效
Go 1.13引入的error wrapping机制,让errors.Is和errors.As成为判断错误类型的推荐方式。但很多团队只实现了Error()方法,导致下游无法正确识别业务错误。
参数差异:fmt.Errorf("wrap: %w", err)生成可展开的error;而fmt.Sprintf("wrap: %v", err)会切断链路。
- 所有自定义error struct都应嵌入
error字段或实现Unwrap() error - 若需支持
errors.Is(err, MySpecificError),必须实现Is(target error) bool方法 - 避免在error消息里拼接敏感数据(如密码、token),尤其当error可能被打印到日志时
goroutine泄漏三连问:谁启的、谁停的、谁等的
goroutine不是免费的。泄漏不会立刻报错,但会在压测或长周期运行后暴露为内存持续上涨、CPU空转。
性能影响:一个永远不退出的goroutine至少占用2KB栈空间,叠加channel缓冲区和闭包捕获的变量,实际开销远高于预期。
- 看到
go func() { ... }(),立刻反问:退出条件是什么?是否监听ctx.Done()?是否用了select配default导致忙等? - 检查channel使用:发送端是否在所有路径上
close(ch)?接收端是否用for range ch而非for { ? - 警惕
time.AfterFunc和time.Ticker:它们启动的goroutine必须能被显式停止,否则就是泄漏源
最易被忽略的是defer cancel()写在goroutine内部——它永远不会执行。真正的cancel调用点,必须和WithCancel在同一个goroutine里,且不能被go语句隔开。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











