Go 服务里的 goroutine 泄漏怎么查:从现象到定位的排查顺序

Go 的并发能力很好用,但也正因为 goroutine 很轻,很多泄漏问题在早期并不明显。服务刚跑起来时一切正常,过一段时间后内存涨、CPU 抖、响应慢,再看监控才发现 goroutine 数量一直在往上走。
goroutine 泄漏最麻烦的地方在于,它通常不是立刻把服务打崩,而是慢慢侵蚀资源。等你真正注意到的时候,问题往往已经跑在线上很久了。
第一步先确认:数量上涨是不是持续性的
看到 goroutine 数量变多,不要立刻下结论。有些服务在流量高峰期 goroutine 数量会自然升高,关键要看它会不会回落。
更值得警惕的通常是这两种情况:
- 流量回落后 goroutine 数仍然不降
- 每次执行某个功能后数量都会多一点
这说明问题更像“没收干净”,而不是“业务正常并发”。
常见泄漏源头通常就那几类
在中小项目里,goroutine 泄漏高频来源通常有:
- channel 没有消费者或没人关闭
- `select` 缺少退出分支
- 网络请求超时没设好
- 后台 worker 没有停止机制
- 定时任务不断创建新 goroutine
- 外部订阅或监听在退出后仍然挂着
所以排查时不要一开始就到处乱看,先从这些位置下手,效率会高很多。
第二步:先拿 goroutine profile
如果服务已经暴露了 pprof,那排查会快很多。你最需要看的不是某个局部函数,而是 goroutine 大头都卡在哪些栈上。
很多时候 profile 一拉出来,你就能直接看到:
- 一堆 goroutine 卡在 channel receive
- 一堆卡在网络 IO 等待
- 一堆卡在 timer 或 ticker
- 一堆卡在某个 select 循环里
这一步的价值在于,它能先帮你锁定问题类型,而不是靠猜。
第三步:沿“创建点”和“退出条件”一起查
光看到 goroutine 卡在哪里还不够,你还得回头看它是在哪被创建的,以及它原本应该如何退出。
排查时我通常会问两个问题:
- 这类 goroutine 是谁启动的
- 它在什么条件下应该结束
很多泄漏本质上不是因为代码“不会跑”,而是因为代码“没有结束条件”。
比如一个消费循环写成:
go func() {
for {
select {
case msg := <-ch:
handle(msg)
}
}
}()
这段逻辑看上去能跑,但如果 ch 不再写入、或者服务准备关闭,它依然会一直挂着。
更稳的写法通常会引入:
- `context.Context`
- `done` channel
- 明确的关闭动作
定时器和 ticker 是很典型的坑
不少项目会在某个请求里临时开一个 ticker,或者在后台任务里无限循环开定时器,但结束时又没 Stop()。
这类问题早期特别不明显,因为每次多出来的不多,但时间一长就会积成问题。
所以只要你看到:
- `time.NewTicker`
- `time.NewTimer`
- 带循环的定时调度
都值得重点检查有没有停止和回收。
外部调用超时如果没做好,也会拖住 goroutine
另一个很常见的问题是下游调用没有设置超时。例如:
- HTTP 客户端没有 timeout
- RPC 调用没带 context deadline
- 数据库或消息系统连接等待过长
结果就是上层业务结束了,但底下 goroutine 还在挂着等返回。
所以 goroutine 泄漏很多时候不是并发逻辑本身的问题,而是依赖调用没有边界。
生产环境里最有价值的信号
如果你在线上想早点发现这类问题,我更建议盯下面这些指标:
- goroutine 数量趋势
- 内存曲线是否缓慢爬升
- GC 压力是否升高
- 某类接口耗时是否随运行时间变长
- 发布重启后指标是否立刻恢复
尤其是“重启后恢复、运行久了又恶化”这种现象,往往很像泄漏而不是瞬时抖动。
一个更稳的编码习惯
如果你想减少 goroutine 泄漏,平时写代码时最好养成这几个习惯:
- 每个长期运行 goroutine 都有退出路径
- 每个下游调用都带超时
- ticker 和 timer 用完就停
- channel 的生产和消费边界清楚
- 后台 worker 有统一生命周期管理
这些看起来像小事,但比等线上出问题再排查轻松太多。
结语
goroutine 泄漏真正难的,不是“查不到”,而是很多团队太晚才开始看。只要你先抓数量趋势,再看 profile,再沿创建点和退出条件回查,问题通常都能逐步缩小。
对 Go 服务来说,稳定性往往不是靠并发开得多,而是靠并发开出去之后收得干净。


钱哆哆♥官方正规流量卡♥1 个月前
生死门虽繁星灿烂,但活着的人才是最重要。
钱哆哆♥官方正规流量卡♥1 个月前
《技术博客图文文章怎么做得不单一:封面、结构图与场景插图的组合方法》已更新:技术博客图文文章怎么做得不单一:封面、结构图与场景插图的组合方法 很多技术博客的正文其实不差,问题常常出在视觉层太单一。首页列表里大家都只有一张封面,点进去以后又是一大段连续文字,读者很难在几秒钟内判断这篇文章到底值不值得继续看。内容本身也许很扎实,但呈现方式没有把价值推出来。…
钱哆哆♥官方正规流量卡♥1 个月前
《技术博客图文文章怎么做得不单一:封面、结构图与场景插图的组合方法》已更新:技术博客图文文章怎么做得不单一:封面、结构图与场景插图的组合方法 很多技术博客的正文其实不差,问题常常出在视觉层太单一。首页列表里大家都只有一张封面,点进去以后又是一大段连续文字,读者很难在几秒钟内判断这篇文章到底值不值得继续看。内容本身也许很扎实,但呈现方式没有把价值推出来。…
钱哆哆♥官方正规流量卡♥1 个月前
《技术博客图文文章怎么做得不单一:封面、结构图与场景插图的组合方法》已更新:技术博客图文文章怎么做得不单一:封面、结构图与场景插图的组合方法 很多技术博客的正文其实不差,问题常常出在视觉层太单一。首页列表里大家都只有一张封面,点进去以后又是一大段连续文字,读者很难在几秒钟内判断这篇文章到底值不值得继续看。内容本身也许很扎实,但呈现方式没有把价值推出来。…
钱哆哆♥官方正规流量卡♥1 个月前
你和学霸的区别就是,你所有的灵光一闪,都是他的基本题型。