-
我在 Go 的 for-select 循环里用 time.After 做超时、自以为简洁标准,结果服务跑久了内存一路缓慢上涨、profile 一看堆里全是没释放的定时器,最后才搞懂 time.After 每次求值都新建一个定时器而它要等到到期那一刻才会被回收的深度复盘
我有个后台 goroutine 在 for 循环里用 select 处理一个高频消息 channel,同时想加一个多久没消息就做点别的的超时,很自然写成 case- 6
- 0
-
我在 Go 的 for 循环里写 select 处理 channel、为了让它别卡住顺手加了个 default 分支,结果服务一启动某个 CPU 核心就直接飙到百分之百风扇狂转,我盯着那段逻辑明明没干什么重活百思不得其解最后才反应过来那个 default 让 select 永远不阻塞整个循环变成了疯狂空转的忙轮询的深度复盘
我有个后台 goroutine 在 for 循环里用 select 监听几个 channel(收任务、收停止信号),担心万一所有 channel 都没消息 select 会卡住、就给它加了个 default 分支,想着没消息时走 default 不卡住挺稳妥,功能也正常。可一上线就发现:服务明明很闲没什么任务、某个 CPU 核心却常年 100% 风扇狂转、整体性能还下降了。top 看是我的进程吃满…- 0
- 0
-
我用 WaitGroup 等一批 goroutine 全部干完,却把 wg.Add 写进了 goroutine 内部,结果主协程的 Wait 在 Add 之前就跑完直接返回,程序以为活儿都干完了其实大半还没开始:一次 WaitGroup 时序用错的深度复盘
我要启动一批 goroutine 并发处理任务、再用 WaitGroup 等它们全部干完汇总结果,写成了在循环里 go func(){ wg.Add(1); defer wg.Done(); doWork() }()——把 wg.Add(1) 放在了 goroutine 内部。线上现象诡异:不报错,但汇总结果经常不完整,明明启动 100 个任务却只有零星几个的结果。复盘才搞懂:WaitGroup …- 0
- 0
-
我用一个 channel 收集多个 worker 的结果,某个 worker 干完顺手把 channel 关了,其他还在干活的 worker 一发送就 panic:send on closed channel,一次 Go channel 关闭所有权的深度复盘
我用多个 worker goroutine 并发处理任务,每个 worker 把结果发到一个共享的 results channel,主 goroutine 收集。为了发完就通知结束,我让每个 worker 干完后 close(results)。功能偶尔能跑,可线上常崩 panic: send on closed channel。推演时序才明白:这个 channel 是多个 worker 共享发送的…- 0
- 0
-
我在 for range 循环里起了一批 goroutine 并发处理任务,结果它们全都处理了同一个、也就是最后一个任务:一次 Go 循环变量被复用、闭包捕获到的全是最后一个值的深度复盘
我要并发处理一批任务,for range 遍历任务列表、每个任务起一个 goroutine 去处理。结果诡异:明明有 10 个不同任务,日志里 10 个 goroutine 处理的全是同一个、也就是最后一个任务,前 9 个一个都没处理。查清才发现:在 Go 1.22 之前,for range 的循环变量 task 是整个循环复用的同一个变量,每轮只是赋新值;而 goroutine 闭包捕获的是这个…- 0
- 0
-
一个每次请求都起一个 goroutine 却没人保证它能退出的服务,goroutine 越积越多、内存缓慢上涨,跑几天就 OOM:一次 goroutine 泄漏的深度复盘
服务内存缓慢上涨、跑几天就被 OOM,pprof 一看 goroutine 从几十个涨到几十万、只增不减,且全卡在同一行 channel 接收/发送上。根因是每请求起 goroutine 通过无缓冲 channel 返回结果,而主流程超时/取消后提前返回、不再接收,那个负责发送的 goroutine 就永远阻塞在 ch- 0
- 0
-
一个被多个 goroutine 同时读写的普通 map,把整个 Go 服务以 fatal error 直接干崩、连 recover 都拦不住:一次 map 并发不安全的深度复盘
高并发 Go 服务平时稳如磐石,一到高峰就毫无征兆整个进程挂掉,日志只留一行 fatal error: concurrent map read and map write,而且入口的 recover 居然拦不住。根因是用普通 map 做内存缓存、多个 goroutine 无同步地并发读写它——Go 内置 map 为性能不做并发同步,并发写会破坏其哈希结构,运行时检测到就抛不可 recover 的 …- 2
- 0
-
我在 for 循环里起了一堆 goroutine 处理每个元素,结果它们全在处理同一个、还是最后一个,我对着循环变量被闭包捕获排查了大半天的复盘
用 Go 写批处理,想为切片每个元素并发起一个 goroutine,经典的 for + go 写法,行云流水。结果目瞪口呆:10 个元素起了 10 个 goroutine,处理的却全是同一个、还是最后一个,前 9 个一个没处理。盯着代码反复看,循环明明遍历了每个元素,凭什么 goroutine 全拿到最后一个?排查大半天才理解 Go 1.22 之前坑了无数人的经典陷阱:for 循环变量被闭包捕获。…- 0
- 0
-
我的 Go 服务内存和 goroutine 数量只涨不跌、跑久了必 OOM,最后揪出是一堆 goroutine 永远卡在 channel 上、泄漏了出不来的深度复盘
我的 Go 服务有个怪病:跑得越久内存越高、只涨不跌,goroutine 数量也持续上爬,最终 OOM 崩溃。我一开始当普通内存泄漏找"哪个对象没释放",直到发现 goroutine 数量同步上涨才醒悟——是 goroutine 泄漏:有段代码启动 goroutine 把结果通过无缓冲 channel 发回,主流程却会因超时提前返回、不再接收,那个 goroutine 就永久卡…- 0
- 0
-
内存慢性漏气定期 OOM:Go 协程泄漏避坑复盘
这是一个让我和运维被半夜告警折磨了快一周的事故:一个 Go 写的后端服务上线后一切正常,可跑上两三天内存就会悄悄爬到上限,然后被 OOM 杀掉自动重启,重启后归零又开始新一轮缓慢爬升,周而复始。它不是一上线就崩的急性病,而是跑着跑着就虚了的慢性病,每隔两三天准时 OOM 一次,最折磨人的是 QPS 没涨流量很平稳,内存却像漏气的轮胎只进不出。真正破案靠的是 Go 自带的神器 pprof:把 gor…- 0
- 0
-
内存只涨不降、几万协程卡死:Go goroutine 泄漏避坑
有个 Go 后台服务跑了几周后运维找上门:进程内存像吹气球一样一天涨一点、从不回落,最后逼近上限被 OOM 杀掉,重启再涨周而复始。我先按内存泄漏的老套路查对象没揪出元凶,直到顺手看了眼 runtime.NumGoroutine 返回的协程数量当场倒吸一口凉气:这个数字也在只涨不降,从启动时几十个涨到几万个还在稳步往上爬。真相浮出水面——不是对象泄漏,是 goroutine 泄漏:每处理一个请求代…- 3
- 0
-
进程偶发猝死、recover 拦不住:Go 并发读写 map 避坑
一个 Go 写的网关服务,上线大半年一直省心,直到某天开始毫无征兆地整个进程崩掉:不是某个请求报错,而是整个服务"啪"地一下没了,被守护进程拉起来,过几小时再崩一次。本地怎么压都不崩,只在生产高并发下偶尔发作;到处写了 recover 兜底,却根本拦不住。崩溃日志最后一行 fatal error: concurrent map read and map write——这不是 p…- 2
- 0
-
Go 并发踩坑实录:一个共享 map 如何让整个服务瞬间崩溃
这次事故没有任何前兆:一个跑了大半年、从没出过岔子的 Go 服务,突然整个进程没了。登上机器翻日志,最后一行让我后背一凉——fatal error: concurrent map writes。不是 panic,是 fatal error,recover 写再多都拦不住一个字。根因朴素得讽刺:一个做本地缓存的普通 map,被好几个 goroutine 同时读写,平时并发不高一直没事,流量一冲高,两…- 2
- 0
-
从一套裸起 goroutine 不管生命周期加无缓冲 channel 发送方阻塞下游一慢就泄漏到 OOM 加全局 map 当缓存被多 goroutine 无锁并发读写高并发下 concurrent map fatal 崩进程加到处 if err != nil 直接 return 把错误上下文丢得一干二净加压根不传 context 超时不可控取消不传播加靠 interface{} 加类型断言硬凑运行时 panic 加用 GOPATH 加手动 vendor 锁不住依赖加靠 fmt.Println 调试的早期 Go 祖传微服务、核心是支撑公司订单聚合与支付回调的微服务功能上一直能用在并发量不大的那些年里默默聚合订单接收支付回调直到一次大促把并发推高一个数量级这套从骨子里就没认真对待过 goroutine 生命周期和并发安全的代码在最不该出事的那一夜集中爆炸、把我们打醒的是大促当晚的连环崩溃第一记重拳来自那些没人管死活的 goroutine 网关给每个请求裸起一个 goroutine 去并发调下游再用无缓冲 channel 把结果收回来可一旦下游变慢上游请求因超时提前 return 走了那个还在傻等着往无缓冲 channel 里发结果的 goroutine 就因为再也没有接收方而永远阻塞在那条发送语句上再也退不出去大促那晚下游一变慢这种泄漏的 goroutine 开始成千上万地堆积每个都死死攥着自己那份请求上下文和缓冲区不放内存一路飙升最终 OOM 进程被杀几乎同时第二记重拳砸下另一处用一个全局 map 当本地缓存好几个 goroutine 并发地读它写它却没有加任何锁大促高并发下并发的读和写终于真正撞在一起 Go runtime 检测到 concurrent map read and map write 直接抛出一个连 recover 都救不回来的 fatal error 把整个进程当场干掉 → 2026 现代 Go 工程体系 context 控制生命周期加超时取消加 errgroup 编排 goroutine 启动即规划退出 + RWMutex 加 sync.Map 保护并发安全加 go test -race 检测竞态写代码时就被揪出 + fmt.Errorf 的 %w 包装错误链加 errors.Is/As 精确判断错误带完整来龙去脉 + context.Context 贯穿调用链超时与取消沿链传播一处慢则一起及时放手 + generics 类型参数编译期类型安全一份逻辑适配多类型无运行时断言 + 规范 defer 紧跟获取显式生命周期循环内资源即时释放绝不泄漏句柄 + Go Modules 加 go.mod go.sum 锁定整棵依赖树任何机器拉出完全一致的版本 + slog 结构化日志分级别带字段可检索聚合告警 87 天战役复盘:8 个 P0 复盘 + 6 条工程哲学 + 7 个关键数字
5 人的后端团队 87 天把一套支撑公司订单聚合与支付回调的核心 Go 微服务,从一堆裸起 goroutine 不管生命周期、用全局 map 当缓存却不加锁、到处 if err != nil 直接 return 把错误上下文丢光、压根不传 context、靠 interface{} 加类型断言硬凑、用 GOPATH 加手动 vendor 锁不住依赖、靠 fmt.Println 调试的早期 Go 代…- 7
- 0
-
从粗放 Go 微服务裸 go 无限开 goroutine 泄漏暴涨几十万 OOM + 无 context 传递被慢下游拖垮全链路无限干等堆积雪崩 + err 被 _ 丢掉漏接出错不知哪层报的 + 滥用 panic 当错误处理没 recover 一个边角崩整个进程 + 共享变量裸读写 data race 偶发脏数据和 concurrent map writes 崩溃 + interface{} 加类型断言丢类型安全运行时动不动 panic + 手撸 WaitGroup 加 error channel 协调并发又长又易错死锁漏收 + 热点频繁分配小对象 GC 压力大 STW 停顿肉眼可见 + 线上黑盒哪慢哪漏全靠猜 + 部署直接 kill 硬断在途请求连接 → 2026 现代高并发 Go 工程 worker pool 受控并发加 context 退出 + context 全链路传递超时取消 + 显式 error 加 %w wrapping 加 errors.Is/As + Mutex/atomic/channel 保护加 -race 检测 + 泛型 type-safe 编译期保证 + errgroup 统一并发错误取消 + sync.Pool 复用减少逃逸 + pprof/trace/metrics 可观测 + signal 加 context 优雅关闭 drain 排空 87 天战役复盘:47 套工程修法 + 7 个 P0 复盘 + 6 条工程哲学
10 人的 Go 基础设施与后端团队 87 天把一套用了四年、流量从每秒几千涨到每秒几十万后种种粗放并发写法集中爆雷的 Go 微服务集群——到处是裸 go 启动 goroutine 不加任何数量限制也没有退出机制高峰期 goroutine 暴涨到几十万 OOM、很多 goroutine 因为在阻塞 channel 上永远等不到信号而悄无声息泄漏越积越多、请求链路上完全没有 context 传递一个…- 4
- 0
-
Go 1.22 gRPC 推送网关 P99 从 45ms 飙到 6.2 秒的 6 天并发雪崩复盘:map 并发读写 + channel 缓冲不足 + 单 Mutex 三重叠加 + 11 条 Go 并发纪律
我们一个 Go 1.22 + gRPC 推送网关,单 Pod 维护 60 万长连接、推送 QPS 280 万,在压测中 P99 从 45ms 飙到 6.2 秒,部分 Pod 被 K8s 重启,直播弹幕延迟 6 秒收 3000 工单。6 天定位发现 map 并发读写触发 throw + channel 缓冲不足 + 单 Mutex 保护 60w-entry 大 map 三重反模式叠加。修复路径分片 …- 2
- 0
-
Go time.After 在 for-select 循环里每秒 5 万次的内存泄漏 3 天复盘:18 小时准时 OOM + ticker / NewTimer.Reset / Go 1.23 三套正解
event-fanout 服务跑 18 小时稳定 OOM,goroutine 不泄漏 channel 不堆积,heap profile 显示 time.NewTimer 占 63% 分配。根因是 for-select 里 time.After(30s) 每秒触发 5 万次,稳态留 150 万个未到期 timer。本文讲清 time.After 的 runtime 行为、3 种修法、Go 1.23 …- 3
- 0
-
Go sync.Pool 在小对象高频场景反而拖慢 P99 + GC 抖动放大的 5 天复盘:victim cache + GC 周期耦合 + Batch Pool 正解
18 万 QPS 推送网关给 256 字节 PushContext 加了 sync.Pool,benchmark 漂亮但生产 P99 从 8ms 飙到 23ms,GC 频率反而涨到 2.34/秒。5 天复盘揭开 victim cache + GC 周期耦合的反直觉负优化机制,最终落地按对象大小/寿命决定是否池化的判断准则 + Batch Pool 正解。- 0
- 0
-
Go goroutine 泄漏的 5 个真实场景:11 万协程 OOM 复盘 + 检测方案
一个 Go 微服务每天涨 200MB,heap profile 一切正常,goroutine profile 却显示 11 万个活跃协程,绝大多数卡在 chan receive 上等一个永远不会写入的 channel。本文整理了生产中最常见的 5 种 goroutine 泄漏场景,每种给出最小复现 + 修法,并给出 pprof + goleak 的 CI 检测方案。- 0
- 0
-
Go 高并发 7 大坑实录:Goroutine 泄漏 50w 雪崩复盘
Go 订单网关大促雪崩复盘:QPS 12w 时 Goroutine 飙到 50w 直接 OOM。复盘 7 个高并发坑:HTTP Client 超时 + Goroutine 泄漏 + Channel 死锁 + sync.Pool 误用 + map 并发 + Context 泄漏 + 限流熔断。优化后 P99 从 3s 降到 80ms。- 0
- 0
-
Go goroutine 泄漏:跑了 47 天 OOMKilled 后我们找到的 5 个真实场景
Go 服务跑了 47 天内存涨到 6.2GB,OOMKilled。本文复盘 goroutine 泄漏的 5 个真实场景:channel 写没人读、for-select 缺退出分支、HTTP body 没读完、time.After 滥用、WaitGroup 卡死。每个场景附最小复现 + 修法 + pprof 排查命令。- 0
- 0
-
Go 并发编程完全指南:goroutine、channel 与 select 的正确打开方式
Go 的并发模型常被一句话概括:"不要通过共享内存来通信,而要通过通信来共享内存。"这句话很漂亮,但新手听完往往还是不会用。这篇文章不停留在口号,而是把 goroutine、channel、select 三件套讲透 —— 它们各自解决什么问题,怎么配合,以及最容易踩的几个坑。 goroutine:几乎免费的并发单元 goroutine 是 Go 运行时管理的轻量级线程。它&qu…- 0
- 0
goroutine
幸运之星正在降临...
点击领取今天的签到奖励!
恭喜!您今天获得了{{mission.data.mission.credit}}积分
我的优惠劵
-
¥优惠劵使用时效:无法使用使用时效:
之前
使用时效:永久有效优惠劵ID:×
没有优惠劵可用!






















