现象

线上版本出现cpu占用率过高的问题,负责的小组经过排查,在最近几次迭代的版本中找不到问题。



排除

Golang带有pprof可用于排查运行时的cpu状态和内存占用,根据官方说明引入net/http/pprof

Cpu状态



定位siftdownTimer源码,在time.go内,找到timerproc引用



timerproc是唤醒处理定时器的一个函数,基本已经可以确定是与timer执行相关。如果频繁激活timerproc那么唯一可能就是某个地方一直在增加timer任务,从而导致触发越来越频繁。timerproc是从timers.t获取任务列表,那么就去找给timers.t增加任务的地方,因此找到addtimerLocked函数。



找到addtimerLocked引用两处,timeSleep和addtimer,排除timeSleep,锁定addtimer。



向上追踪到startTimer



找到startTimer后,找不到引用startTimer的地方了。

懒人懒办法,打断点看一下调用堆栈!



抓住了,这个写法明显存在问题



看看NewTicker里面的执行方法


NewTicker里找到了startTimer的调用方法,重点是上面说明最后一句:Stop the ticker to release associated resources.

将来定时器方法修改一下,并且找出所有NewTicker检查一遍



重新打包更新线上包,经过一段时间观察后,已经正常了。



总结:

Golang里Ticker是需要手动stop的,不然它会一直在timers对象内工作,导致gc无法释放。

95%以上的bug都是因为不仔细和坏习惯造成的,这个NewTicker().C的用法不知源于哪里,只是以前只有少量并且间隔范围也大,因此没有影响到cpu占用率,而这段时间拆分服务并减少时钟周期,这个问题就暴露出来了。

程序员编写代码和review的时候,最好还是多抽点时间看看接口的相关说明,Golang最好的地方就是很多函数点进去就能直接看到说明,不需要自己去找的,学习非常方便。