一:背景

1.讲故事

前段时间有位朋友加微信,说他的程序直接 CPU=100%,每次只能手工介入重启,让我帮忙看下到底怎么回事,哈哈,这种CPU打满的事故,程序员压力会非常大, 我让朋友在 CPU 高的时候抓 2 个 dump 下来,然后发给我分析。

二:WinDbg 分析

1. CPU 真的被打满了吗?

为了防止南辕北辙,一定要用 !tp 命令去验证下是不是真的 CPU 爆高。


0:000> !tp
CPU utilization: 100%
Worker Thread: Total: 21 Running: 7 Idle: 0 MaxLimit: 32767 MinLimit: 4
Work Request in Queue: 3
AsyncTimerCallbackCompletion TimerInfo@00000000042d2430
AsyncTimerCallbackCompletion TimerInfo@00000000042d2f90
AsyncTimerCallbackCompletion TimerInfo@000000000420c150
--------------------------------------
Number of Timers: 0
--------------------------------------
Completion Port Thread:Total: 18 Free: 9 MaxFree: 8 CurrentLimit: 18 MaxLimit: 1000 MinLimit: 4

从卦中看确实 100%,太牛了,而且 WorkRequest 还有任务堆积现象,确认无疑后,接下来看下是谁引发的?

2. 谁导致的 CPU 爆高

根据惯例首先怀疑是不是 GC 触发所致,可以用 !t 查看下线程列表,观察下有没有 GC 字样。


:000> !t
ThreadCount: 53
UnstartedThread: 0
BackgroundThread: 53
PendingThread: 0
DeadThread: 0
Hosted Runtime: no
Lock
ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception
4 1 1240 00000000021cdf30 2a220 Preemptive 0000000000000000:0000000000000000 00000000021d94c0 0 MTA
23 2 4db4 00000000041cdaa0 2b220 Preemptive 0000000000000000:0000000000000000 00000000021d94c0 0 MTA (Finalizer)
...
65 156 22f4 000000000b1a3f60 8029220 Preemptive 00000004527751F0:0000000452775EE8 00000000021d94c0 0 MTA (Threadpool Completion Port)
66 205 2ef8 000000000b1a1080 8029220 Preemptive 0000000157641DE0:00000001576435B0 00000000021d94c0 0 MTA (Threadpool Completion Port)
...

从卦中看没有 GC 字样,也表明这个程序并不是由 GC 触发所致,接下来该怎么排查呢? 一般来说 CPU 的爆高是由线程抬起来的,所以接下来就是看下 CPU 的档次和各个线程栈,看有没有什么新线索, 可以使用 ~*e !clrstack


0:000> !cpuid
CP F/M/S Manufacturer MHz
0 6,79,1 <unavailable> 2299
1 6,79,1 <unavailable> 2299
2 6,79,1 <unavailable> 2299
3 6,79,1 <unavailable> 2299 0:000> ~*e !clrstack
OS Thread Id: 0x2cc4 (68)
Child SP IP Call Site
000000000c14e758 00007ffadeb86e4a [GCFrame: 000000000c14e758]
000000000c14e840 00007ffadeb86e4a [GCFrame: 000000000c14e840]
000000000c14e878 00007ffadeb86e4a [HelperMethodFrame: 000000000c14e878] System.Threading.Monitor.Enter(System.Object)
000000000c14e970 00007ffaceb40491 System.Net.ConnectionGroup.Disassociate(System.Net.Connection) [f:\dd\NDP\fx\src\net\System\Net\_ConnectionGroup.cs @ 148]
000000000c14e9d0 00007ffaceb3fc93 System.Net.Connection.PrepareCloseConnectionSocket(System.Net.ConnectionReturnResult ByRef) [f:\dd\NDP\fx\src\net\System\Net\_Connection.cs @ 3048]
000000000c14eaa0 00007ffacf139bfb System.Net.Connection.HandleError(Boolean, Boolean, System.Net.WebExceptionStatus, System.Net.ConnectionReturnResult ByRef) [f:\dd\NDP\fx\src\net\System\Net\_Connection.cs @ 3119]
000000000c14eb00 00007ffacebc4118 System.Net.Connection.ReadComplete(Int32, System.Net.WebExceptionStatus) [f:\dd\NDP\fx\src\net\System\Net\_Connection.cs @ 3387]
000000000c14eb80 00007ffacebe3dc5 System.Net.LazyAsyncResult.Complete(IntPtr) [f:\dd\NDP\fx\src\net\System\Net\_LazyAsyncResult.cs @ 415]
000000000c14ebe0 00007ffacebe3d07 System.Net.LazyAsyncResult.ProtectedInvokeCallback(System.Object, IntPtr) [f:\dd\NDP\fx\src\net\System\Net\_LazyAsyncResult.cs @ 368]
000000000c14ec20 00007ffacf3a476f System.Net.Security._SslStream.StartFrameBody(Int32, Byte[], Int32, Int32, System.Net.AsyncProtocolRequest)
000000000c14ec80 00007ffacebb3ed8 System.Net.Security._SslStream.ReadHeaderCallback(System.Net.AsyncProtocolRequest) [f:\dd\NDP\fx\src\net\System\Net\SecureProtocols\_SslStream.cs @ 1007]
000000000c14ece0 00007ffacebae5ee System.Net.AsyncProtocolRequest.CompleteRequest(Int32) [f:\dd\NDP\fx\src\net\System\Net\SecureProtocols\_HelperAsyncResults.cs @ 142]
000000000c14ed10 00007ffacf3a3567 System.Net.FixedSizeReader.CheckCompletionBeforeNextRead(Int32)
000000000c14ed40 00007ffacebae507 System.Net.FixedSizeReader.ReadCallback(System.IAsyncResult) [f:\dd\NDP\fx\src\net\System\Net\SecureProtocols\_FixedSizeReader.cs @ 148]
000000000c14ed90 00007ffacebe3dc5 System.Net.LazyAsyncResult.Complete(IntPtr) [f:\dd\NDP\fx\src\net\System\Net\_LazyAsyncResult.cs @ 415]
000000000c14edf0 00007ffadcbe3a63 System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean) [f:\dd\ndp\clr\src\BCL\system\threading\executioncontext.cs @ 954]
000000000c14eec0 00007ffadcbe38f4 System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean) [f:\dd\ndp\clr\src\BCL\system\threading\executioncontext.cs @ 902]
000000000c14eef0 00007ffadcbe38c2 System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object) [f:\dd\ndp\clr\src\BCL\system\threading\executioncontext.cs @ 891]
000000000c14ef40 00007ffaceba60cf System.Net.ContextAwareResult.Complete(IntPtr) [f:\dd\NDP\fx\src\net\System\Net\_ContextAwareResult.cs @ 463]
000000000c14ef90 00007ffacebe3d07 System.Net.LazyAsyncResult.ProtectedInvokeCallback(System.Object, IntPtr) [f:\dd\NDP\fx\src\net\System\Net\_LazyAsyncResult.cs @ 368]
000000000c14efd0 00007ffaceba5e2f System.Net.Sockets.BaseOverlappedAsyncResult.CompletionPortCallback(UInt32, UInt32, System.Threading.NativeOverlapped*) [f:\dd\NDP\fx\src\net\System\Net\Sockets\_BaseOverlappedAsyncResult.cs @ 399]
000000000c14f040 00007ffadcc2ffef System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32, UInt32, System.Threading.NativeOverlapped*) [f:\dd\ndp\clr\src\BCL\system\threading\overlapped.cs @ 135]
000000000c14f1f0 00007ffade9a6d93 [GCFrame: 000000000c14f1f0] OS Thread Id: 0x5d6c (70)
Child SP IP Call Site
000000000943e920 00007ffacf139bfb System.Net.Connection.HandleError(Boolean, Boolean, System.Net.WebExceptionStatus, System.Net.ConnectionReturnResult ByRef) OS Thread Id: 0x4ad4 (75)
Child SP IP Call Site
...
000000000c94e5a0 00007ffacf139bfb System.Net.Connection.HandleError(Boolean, Boolean, System.Net.WebExceptionStatus, System.Net.ConnectionReturnResult ByRef)
... OS Thread Id: 0x1d70 (80)
Child SP IP Call Site
...
000000000d24e3a0 00007ffacf139bfb System.Net.Connection.HandleError(Boolean, Boolean, System.Net.WebExceptionStatus, System.Net.ConnectionReturnResult ByRef) [f:\dd\NDP\fx\src\net\System\Net\_Connection.cs @ 3119]
...

从线程栈上看,这个 CPU 是 4个核,刚好对应着 4 个 HandleError 报错,看样子是什么网络出问题了,接下来切到 80 号线程看一下有没有什么异常类。


0:000> ~80s
clr!AwareLock::Contention+0x194:
00007ffa`deb86e40 4883e801 sub rax,1
0:080> !mdso
Thread 80:
Location Object Type
------------------------------------------------------------
000000000d24e098 000000015765e028 System.Net.WebException
000000000d24e0f8 0000000340b07110 System.Collections.ArrayList
000000000d24e110 000000015765e2b8 System.Net.HttpWebRequest[]
000000000d24e1c0 0000000340b070b8 System.Net.ConnectionGroup
000000000d24e258 0000000144a79678 System.Net.Connection 0:080> !mdt 000000015765e028
000000015765e028 (System.Net.WebException)
_className:NULL (System.String)
_exceptionMethod:NULL (System.Reflection.MethodBase)
_exceptionMethodString:NULL (System.String)
_message:000000015765df70 (System.String) Length=77, String="The underlying connection was closed: The connection was closed unexpectedly."
...

果然看到了 System.Net.WebException, 从异常信息看貌似是 连接关闭了,到这里我就有了一个大胆的猜测,是不是高频的异常输出导致的 CPU 爆高呢? 为了验证,可以到托管堆上找下 WebException 的个数。


0:080> !dumpheap -stat
Statistics:
MT Count TotalSize Class Name
...
00007ffacecc38b0 13315 2343440 System.Net.WebException
00007ffadcdf6570 11369 1909992 System.IO.IOException
00007ffadcdf5fb8 13380 2247840 System.ObjectDisposedException
...

看到这么多异常还是挺吓人的,刚好朋友抓了两个dump可以做个比较。


0:048> !dumpheap -stat
Statistics:
MT Count TotalSize Class Name
00007ffacecc38b0 26745 4707120 System.Net.WebException
00007ffadcdf6570 26722 4489296 System.IO.IOException
00007ffadcdf5fb8 28745 4829160 System.ObjectDisposedException

可以看到,2 min 之内异常增加了合计 4w 左右,这就验证了程序确实是疯狂的抛异常,在 Windows 平台上不管是硬件异常还是软件异常都由 Windows SEH 异常处理框架统一处理,会出现用户态和内核态的切换,这样疯狂的抛出,必然会导致 CPU 爆高,终于找到原因了,接下来就是寻找诱发因素。

3. 异常是谁诱发的

再回头看 HandleError 函数的调用栈都是底层的库函数,从线程栈的 PerformIOCompletionCallback 函数来看是 IO线程 诱发的,能被 IO 线程兜到是因为这是做了异步处理,既然是 异步,自然 OverlappedData 也会非常多。


0:080> !gchandles -stat
Statistics:
MT Count TotalSize Class Name
00007ffadc6f7b98 14511 1625232 System.Threading.OverlappedData
Total 17550 objects Handles:
Strong Handles: 426
Pinned Handles: 23
Async Pinned Handles: 14511
Ref Count Handles: 24
Weak Long Handles: 2430
Weak Short Handles: 132
SizedRef Handles: 4

说明此时有大概 1.5w 的异步请求待回头,请求量还是蛮大的,但还是没找到异常的用户代码,只能找下到底是谁发起了什么请求。


0:080> !mdso
Thread 80:
Location Object Type
------------------------------------------------------------
...
000000000d24e488 0000000358c57918 System.Net.HttpWebRequest
000000000d24e2e8 00000001407b5b40 System.String "net_io_readfailure"
... 0:080> !mdt -r:2 0000000358c57918
0000000358c57918 (System.Net.HttpWebRequest)
_Uri:0000000358c57210 (System.Uri)
m_String:00000002407ee430 (System.String) Length=98, String="https://api.xxxx/peer_messages"
....

可以看到请求的连接是 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 爆高现象。

记一次 .NET 某娱乐聊天流平台 CPU 爆高分析的更多相关文章

  1. 记一次 .NET 某智慧物流 WCS系统 CPU 爆高分析

    一:背景 1. 讲故事 哈哈,再次见到物流类软件,上个月有位朋友找到我,说他的程序出现了 CPU 爆高,让我帮忙看下什么原因,由于那段时间在苦心研究 C++,分析和经验分享也就懈怠了,今天就给大家安排 ...

  2. 记一次 .NET 某智能交通后台服务 CPU爆高分析

    一:背景 1. 讲故事 前天有位朋友加微信求助他的程序出现了CPU爆高的问题,开局就是一个红包,把我吓懵了! 由于是南方小年,我在老家张罗处理起来不方便,没有第一时间帮他处理,朋友在第二天上午已经找出 ...

  3. 记一次 .NET 某电商交易平台Web站 CPU爆高分析

    一:背景 1. 讲故事 已经连续写了几篇关于内存暴涨的真实案例,有点麻木了,这篇换个口味,分享一个 CPU爆高 的案例,前段时间有位朋友在 wx 上找到我,说他的一个老项目经常收到 CPU > ...

  4. 记一次 .NET游戏站程序的 CPU 爆高分析

    一:背景 1. 讲故事 上个月有个老朋友找到我,说他的站点晚高峰 CPU 会突然爆高,发了两份 dump 文件过来,如下图: 又是经典的 CPU 爆高问题,到目前为止,对这种我还是有一些经验可循的. ...

  5. 记一次 .NET 某医院HIS系统 CPU爆高分析

    一:背景 1. 讲故事 前几天有位朋友加 wx 抱怨他的程序在高峰期总是莫名其妙的cpu爆高,求助如何分析? 和这位朋友沟通下来,据说这问题困扰了他们几年,还请了微软的工程师过来解决,无疾而终,应该还 ...

  6. 记一次 .NET 某旅行社Web站 CPU爆高分析

    一:背景 1. 讲故事 前几天有位朋友wx求助,它的程序内存经常飙升,cpu 偶尔飙升,没找到原因,希望帮忙看一下. 可惜发过来的 dump 只有区区2G,能在这里面找到内存泄漏那真有两把刷子..., ...

  7. 记一次 .NET 车联网云端服务 CPU爆高分析

    一:背景 1. 讲故事 前几天有位朋友wx求助,它的程序CPU经常飙满,没找到原因,希望帮忙看一下. 这些天连续接到几个cpu爆高的dump,都看烦了,希望后面再来几个其他方面的dump,从沟通上看, ...

  8. 记一次 .NET 某机械臂智能机器人控制系统MRS CPU爆高分析

    一:背景 1. 讲故事 这是6月中旬一位朋友加wx求助dump的故事,他的程序 cpu爆高UI卡死,问如何解决,截图如下: 在拿到这个dump后,我发现这是一个关于机械臂的MRS程序,哈哈,在机械臂这 ...

  9. 记一次 .NET 某资讯论坛 CPU爆高分析

    大概有11天没发文了,真的不是因为懒,本想前几天抽空写,不知道为啥最近求助的朋友比较多,一天都能拿到2-3个求助dump,晚上回来就是一顿分析,有点意思的是大多朋友自己都分析了几遍或者公司多年的牛皮藓 ...

随机推荐

  1. 使用.NET简单实现一个Redis的高性能克隆版(六)

    译者注 该原文是Ayende Rahien大佬业余自己在使用C# 和 .NET构建一个简单.高性能兼容Redis协议的数据库的经历. 首先这个"Redis"是非常简单的实现,但是他 ...

  2. python-GUI键盘小工具

    一.tkinter  GUI界面 二.实现功能 连接设备.设备上电.设备使能.键盘按键控制关节移动.配置关节移动速度和角度 三.python源码 1 #coding=utf-8 2 import ms ...

  3. 感谢有你!Apache DolphinScheduler 项目 GitHub star 突破 8k

    本周伊始,Apache DolphinScheduler 项目在 GitHub 上的 Github Star 总数首次突破 8K.目前,Apache DolphinScheduler 社区已经拥有 C ...

  4. 60行自己动手写LockSupport是什么体验?

    60行自己动手写LockSupport是什么体验? 前言 在JDK当中给我们提供的各种并发工具当中,比如ReentrantLock等等工具的内部实现,经常会使用到一个工具,这个工具就是LockSupp ...

  5. ACM模式细节

    牛客网的ACM模式需要自己写输入输出,在这里简单记录一下: 基本答题框架: import java.util.*; public class Main{ public static void main ...

  6. 如何使用.NET 6的IHostedService和BackgroundService?

    大家好,我是张飞洪,感谢您的阅读,我会不定期和你分享学习心得,希望我的文章能成为你成长路上的垫脚石,让我们一起精进. 本章是<定制ASP NET 6.0框架系列文章>的第七篇.本文内容和定 ...

  7. 2020牛客NOIP赛前集训营-提高组(第三场) C - 牛半仙的妹子Tree (树链剖分)

    昨天教练问我:你用树剖做这道题,怎么全部清空状态呢?    我:???不是懒标记就完了???    教练:树剖不是要建很多棵线段树吗,不止log个,你要一个一个清?    我:为什么要建很多棵线段树? ...

  8. 1.2_Selenium的三生三世

  9. 【HTML】学习路径5-预格式标签和字体标签

    <!DOCTYPE html> <html> <head> <title>我是标题</title> <meta charset=&qu ...

  10. 完全彻底的卸载MySQL5.7.35

    最开始接触计算机的时候关于MySQL卸载的这个问题,导致我重装了一次系统.就在今天我又遇到了同样的问题. Setp①: 想要安装新的数据库软件必须要先彻底且干净的删除掉以前的数据库. 第一步要做的是停 ...