问题
服务器长期开着,遇到一个问题,没有几个有效负载,CPU 占用了 2 个核
go tool pprof 了下,大致如下调用消耗:
top100 ,显示 runtime.siftdownTimer 占了大头(2 个图,取至不同服务)
问题分析
可以看到,均为定时器相关代码调用
review 代码,不少定时器用完没有 Stop 释放
模拟做下类似情景性能测试:
1. 创建 2w 个 timer + 1 个 timer 在工作:
如图,CPU 占用了 61.5%
2. 只有 1 个 timer 在工作
看不到 CPU 消耗
因此可以得出结论:定时器泄漏,会导致 CPU 占用高
为什么会消耗 CPU
参考 https://www.cnblogs.com/Zereker/p/11396639.html
该作者分析的很透彻了
再做个试验
把泄漏的定时器,时间间隔改大到秒级别。单个定时器工作时,CPU也不会上去
因此,要避免同时存在大量毫秒级别的定时器,golang 底层需要维护优先队列数据结构会消耗大量 CPU
因为最坏的操作,每次维护优先队列数据结构都是 O(LogN) 的时间复杂度。而大量毫秒级别的定时器会把时间复杂度拉到最坏情况 O(M*LogN)
golang 定时器问题总汇
到目前为止,至少遇到 3 处 golang 定时器问题,及解决方案:
定时器问题 | 解决方案 |
---|---|
定时器泄漏 | 避免内存泄漏,比如可以封装 timer ,引用计数,随意可以查看定时器对象数量等 |
同时刻存在大量毫秒级定时器 | 业务上要避免这种情况的代码,没有通用解决方案 |
session 级别秒级定时器 | 业务上做到被动触发,来规避该问题 |