广阔天地大有作为

你想拥有什么,就去追求什么

29 Feb 2024

go 1.14.1 timer bug

go 1.14.1 的timer 包存在bug,会导致服务hang死,问题发生在两年前,而go目前的版本也已经迭代到1.22,还是整理出来以记录当时定位问题的思路。

问题描述

有个服务在压测时发现多容器压测,每次必有一台容器出现CPU跑满的现象。

Pasted image 20240228145604.png

定位

尝试进入该容器使用 pprof 抓火焰图进行分析,但进入容器后却发现 pprof 监听的端口根本连不上,只能另想办法。使用 delve 来试试。 首先执行 top 找到 cpu 跑满的线程。

Pasted image 20240228151019.png

容器设置是4 core,这里看到也是前面4个线程占满了所有CPU。 接着执行 dlv attach <PID> 进入线程,看到以下信息

Pasted image 20240228151356.png

这里看到看起来与redis相关,刚好这次修改有一处redis连接处的变更,猜测与此有关于是回退这个变更再次压测,很遗憾,很快问题又出现了。 继续进入上图中的goroutine,看到下图

Pasted image 20240228151840.png

这里漏了一个关键的堆栈,因为总结时现场已经破坏,正是通过这个堆栈在github找到这个issue,堆栈内容与issue中描述相同,可以参考issue。

为了验证是否因为这个timer 的bug导致,将go升级为14.2,再次压测,问题没有重现,基本上可以确定解决。

原因分析

简单来说就是

如何解决

对于我们服务来说,直接升级go版本即可,现在我们团队在选择go版本的时候有一个原则,Golang社区发布的倒数第二个大版本的最后一个小版本。 比如当前已发布go 1.22.x,那么应该选择1.21.7。

go 社区解决方案在这个issue中说得很清楚。从修复diff来看,在修改timer状态的时候加了一个锁。