timer定时器错误使用,导致ticker对象泄露
应用机器的cpu占用率一直稳定在300%以上,如下图
一直以为是中心服务器要管理上万台服务器的数据收发是正常的业务负载,直到新系统上线后,所有服务器的流量就迁移到了新的服务节点,新的服务节点的cpu负载是64%。老的应用节点在没有流量的情况下cpu负载一直稳定在300%左右。
使用golang的pprof查看cpu的使用情况(go tool pprof ...),如下图:

从上图可以看主要的时间占用就是time包里面的siftdownTimer函数,在网上查阅该函数主要功能就是操作保存timer的堆队列。如果这个操作频繁的话,就说明创造了太多的定时器,导致堆队列长度过长,说明timer定时器存在对象泄露。 查看代码是在创建定时任务的时候错误的使用了time.Tick()方法。
pprof的使用方法,详细参考 juejin.cn/post/684490…
我们参考下Tick函数的定义:
⚙ Tick is a convenience wrapper for NewTicker providing access to the ticking channel only. While Tick is useful for clients that have no need to shut down// the Ticker, be aware that without a way to shut it down the underlying// Ticker cannot be recovered by the garbage collector; it "leaks". Unlike NewTicker, Tick will return nil if d <= 0.从上可知,Tick方法产生的timer是不会被垃圾回收器收集的,所以造成了timer对象的泄露。如下图:
select {
case <-time.Tick(time.Second * 1):
result, err := agentService.GetFilteredAgentJobs("", job.JobId, "")
if err != nil {
logrus.Error(err)
return nil, err
}
if (*result)[0].Status != "pending" {
j = (*result)[0]
break
}
case <-time.After(time.Second * 4):
logrus.Error("查询超时,请在任务管理里面查看结果.")
return nil, err
}