记一次 .NET 某娱乐聊天流平台 CPU 爆高分析

网站建设2年前 (2022)发布
18 0 0

前段时间有位朋友加微信,说他的程序直接 CPU=100%,每次只能手工介入重启,让我帮忙看下到底怎么回事,哈哈,这种CPU打满的事故,程序员压力会非常大, 我让朋友在 CPU 高的时候抓 2 个 dump 下来,然后发给我分析。,为了防止南辕北辙,一定要用 !tp 命令去验证下是不是真的 CPU 爆高。,从卦中看确实 100%,太牛了,而且 WorkRequest 还有任务堆积现象,确认无疑后,接下来看下是谁引发的?,根据惯例首先怀疑是不是 GC 触发所致,可以用 !t​ 查看下线程列表,观察下有没有 GC 字样。,从卦中看没有 GC​ 字样,也表明这个程序并不是由 GC 触发所致,接下来该怎么排查呢?一般来说 CPU 的爆高是由线程抬起来的,所以接下来就是看下 CPU 的档次和各个线程栈,看有没有什么新线索, 可以使用 ~*e !clrstack 。,从线程栈上看,这个 CPU 是 4个核,刚好对应着 4 个 HandleError​ 报错,看样子是什么网络出问题了,接下来切到 80 号线程看一下有没有什么异常类。,果然看到了 System.Net.WebException​, 从异常信息看貌似是 连接关闭了​,到这里我就有了一个大胆的猜测,是不是高频的异常输出导致的 CPU 爆高呢?为了验证,可以到托管堆上找下 WebException 的个数。,看到这么多异常还是挺吓人的,刚好朋友抓了两个dump可以做个比较。,可以看到,2 min 之内异常增加了合计 4w​ 左右,这就验证了程序确实是疯狂的抛异常,在 Windows 平台上不管是硬件异常​还是软件异常都由 Windows SEH 异常处理框架统一处理,会出现用户态和内核态的切换,这样疯狂的抛出,必然会导致 CPU 爆高,终于找到原因了,接下来就是寻找诱发因素。,再回头看 HandleError​ 函数的调用栈都是底层的库函数,从线程栈的 PerformIOCompletionCallback​ 函数来看是 IO线程​ 诱发的,能被 IO 线程兜到是因为这是做了异步处理,既然是 异步​,自然 OverlappedData 也会非常多。,说明此时有大概 1.5w 的异步请求待回头,请求量还是蛮大的,但还是没找到异常的用户代码,只能找下到底是谁发起了什么请求。,可以看到请求的连接是 https://api.xxxx/peer_messages​,是一个第三方的API接口, 由于底层的连接关闭,导致了最后 net_io_readfailure。,把所有的信息整合一下就是:,当请求量大了之后,访问 https://api.xxxx/peer_messages​ 会出问题,对方关闭了底层连接,导致客户端这边请求出现了大量 IO 回调异常:IOException: Unable to read data from the transport connection: The connection was closed.​,2min之间多达合计 4w​ 的异常抛出,进而引发 CPU 爆高,将信息告诉了朋友,让朋友重点关注下 https://api.xxxx/peer_messages 这个连接。,这次生产事故主要是由于高峰期请求量过大,由于某种原因 Socket 连接关闭,导致了大量的异步回调异常。,解决方法在调用端做好限流,据朋友说减少了不必要的 https://api.xxxx/peer_messages 调用,目前没有出现 CPU 爆高现象。

© 版权声明

相关文章