go语言程序cpu过高问题排查的方法详解
作者:zoudaohoutianI
一、前言
Go程序像C/C++一样,如果开发编码考虑不当,会出现cpu负载过高的性能问题。如果程序是线上环境或者特定场景下出现负载过高,问题不好复现,则需要利用当前负载过高的进程进行调用栈分析。
C/C++中一般先通过top -d 1 -p $pid -H 命令查看负载过高的线程号(TID),然后使用gdb attach到该进程,通过thread info获取线程信息,然后切换到对应负载高的线程,输入bt查看调用栈。
结合对应代码中的函数,进一步分析。Go语言中方法也类似,我们将通过dlv来分析负载高的协程调用栈。
二、问题排查过程
2.1 通过top查看高cpu的进程pid
通过top -d 1,可以发现进程cava_smu(pid=11205)的cpu过高。
2.2 通过top查看高cpu的线程tid
通过上一步,我们确定了是pid=11205的cava_smu进程cpu过高,那么可以通过top -d 1 -p 11205 -H 来确认cpu过载的线程tid,如下图所示:
通过以上操作,可以确认tid=11208,11212,11213三个线程的cpu过高。
2.3 通过dlv附加到进程,分析线程/协程cpu过载的堆栈
首先,如果生产环境没有dlv,则可以拷贝对应的dlv到/usr/local/bin下。
接着 dlv attach 11205,确认tid=11208的goroutine 序号,如下图所示:
2.4 在dlv中切换到对应高cpu协程,并查看堆栈
如下图所示:
通过以上操作,可以确认业务底层的栈帧是第6→5帧,business.go:18行的disPatchTask ->business.go:168 行的dispatchIdleTeu方法相关,查看对应版本代码如下:
代码执行到下图中,dispatchIdleTeu返回了错误qferror.ErrNoTeu。
代码执行到下图中,189行dispatchIdleTeu返回了错误qferror.ErrNoTeu,所以189 if的执行语句192~212无法进入进行,而外层是一个for死循环,则会造成该协程一直占用cpu,导致cpu过载。
修复方法可以是在for 循环内增加sleep休眠,例如在214行处增加time.Sleep(200 * time.Millisecond),效果请自行验证。
总结
到此这篇关于go语言程序cpu过高问题排查的文章就介绍到这了,更多相关go程序cpu过高排查内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!