迁移自简书,格式可能未经校对。
事件经过:
- 收到反馈测试环境某个服务打开 swagger ,进程会卡死
- 根据经验,初步判定是有死循环。
- 在本地启动该服务,打开 swagger ,发现 CPU 单核100%,确认有死循环
- pprof 抓取 cpu profile
- 在 marshal 前后打断点,对于特定链接,marshal 没结束(没看到 done 日志)
- 更换为官方 json 库,恢复正常。确认是 json-iterator 导致
- 在其他项目做同样操作,未能复现问题。对比jsoniter 版本,初步判定是版本过老导致。
- 通过 debug,确认是下面这个循环走不出来
- 升级 jsoniter ,恢复正常
- 到官方一看,这个bug 很早就解决了。但是出事的服务还在依赖 2017-8-9 的版本。(修复链接:https://github.com/json-iterator/go/commit/f7063353029dd177a959dbf6b29f48746441fbfa#diff-34ab17b5ece86461ae47905134a8d94c)
第二个问题,为什么整个程序会假死
按理说,for{} 会造成单个协程停不下,CPU 单核打满都好理解,但为什么会让整个进程死掉?
原因在于 GC标记前,需要通知所有 goroutine 停下来,但是问题协程一直停不下来,而正常协程都已经停下来在等待。所以对外无法正常服务,看起就是死掉了。
暴露的问题
- 对 Go 底层完全 hold 不住
- 第三方依赖管理不规范
参考链接
- 历史四年的关于for{}的抢占式讨论 https://github.com/golang/go/issues/10958
- Goroutine调度实例简要分析 https://tonybai.com/2017/11/23/the-simple-analysis-of-goroutine-schedule-examples/
- 滴滴大佬如何定位 golang 进程 hang 死的 bug https://gocn.vip/article/441