前言
近来系统从 net framework472 升级到 net8,asp net core mvc 服务部署上线后,发现运行着就不响应了。
初步观察与分析
现象一
用户访问访问着,突然就不能响应了,用户 WEB 界面一直加载不出。
为排除链路上其他中间层的影响,立即尝试直接通过 IP+端口 的形式访问,发现确实没响应。
此时能确定就是服务的问题了。
现象二
通过把 nginx 流量全切走之后,服务没有快速恢复,而是过了有半个小时才恢复响应。
通过查看系统打印的日志,发现中间半个小时完全没打日志,有的半个小时前打的请求日志,半个小时后才打的响应日志。
那就说明中间有什么东西在等待堵塞了。
期间,CPU 并没有打满,还剩余挺多的,所以排除CPU资源不够导致响应处理不来了。
同时,发现进程的线程数量涨到好几百,这种现象很符合的情况是,
有什么资源同步阻塞导致线程被持有,而且这个资源肯定是频繁被使用到的,
才会导致线程一下子都用完了,并且需要不断分配新的线程,
新分配的线程还要很快就被抢掉了占用又堵塞了,如此反复,导致了这样的现象。
乍一看代码也没发现问题,那就祭出
dotnet-stack 诊断工具 – .NET CLI – .NET | Microsoft Learn
这玩意可以跟踪托管堆栈,大白话就是能看线程运行到哪了,在卡的时候一执行,那不就一眼就看到都卡在哪些地方了,灰常银杏。
上图感受下:
准备
下载,两种方式挑一种,我用的是直接下载。
打印报告
如图
# 先找出net的进程ID
dotnet-stack ps
# 打印当前托管堆栈
# 还有很多参数,有需要可以自己看官方文档
dotnet-stack report --process-id 8624
# 控制台就会打印出来了,当然也可以把控制台输出到文件里,方便文本编辑器查看
我这里是先再让用户访问,还原出卡的时候,然后立刻打印了几次报告进行分析的。
分析
这里的内容,以 Thread (线程标识) 为开头,到下一个Thread结束,作为一组数据。
看一下线程都在执行哪个函数,就能知道线程堵在哪了。
我发现有很多线程都卡在了同一个方法,是 log4net 的打印日志,
我们用到文本日志和MongoDB日志,先把MongoDB的关掉,发现就正常了。
进一步分析,原来是MongoDB日志是我们自己定义的打印器,MongoDB的client、database、collection 每次打印都会新建,
但是MongoDB官方说了这个是线程安全可重用的,所以就把这里改成单例了,
问题也随之解决了。
结论
如果发现线程数飙升,需要分析线程都堵塞在哪里了,可以使用 dotnet-stack。
发表回复