51Testing软件测试论坛

 找回密码
 (注-册)加入51Testing

QQ登录

只需一步,快速开始

微信登录,快人一步

手机号码,快捷登录

查看: 1232|回复: 0
打印 上一主题 下一主题

[原创] .NET 某娱乐聊天流平台 CPU 爆高分析的前因后果

[复制链接]
  • TA的每日心情
    擦汗
    昨天 09:00
  • 签到天数: 1025 天

    连续签到: 4 天

    [LV.10]测试总司令

    跳转到指定楼层
    1#
    发表于 2022-11-3 14:54:54 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
    一、背景
      1.讲故事
      前段时间有位朋友加微信,说他的程序直接 CPU=100%,每次只能手工介入重启,让我帮忙看下到底怎么回事,哈哈,这种CPU打满的事故,程序员压力会非常大, 我让朋友在 CPU 高的时候抓 2 个 dump 下来,然后发给我分析。
      二、WinDbg 分析
      1. CPU 真的被打满了吗?
      为了防止南辕北辙,一定要用 !tp 命令去验证下是不是真的 CPU 爆高。
    1. <font size="3">0:000> !tp

    2.   CPU utilization: 100%

    3.   Worker Thread: Total: 21 Running: 7 Idle: 0 MaxLimit: 32767 MinLimit: 4

    4.   Work Request in Queue: 3

    5.       AsyncTimerCallbackCompletion TimerInfo@00000000042d2430

    6.       AsyncTimerCallbackCompletion TimerInfo@00000000042d2f90

    7.       AsyncTimerCallbackCompletion TimerInfo@000000000420c150

    8.   --------------------------------------

    9.   Number of Timers: 0

    10.   --------------------------------------

    11.   Completion Port Thread:Total: 18 Free: 9 MaxFree: 8 CurrentLimit: 18 MaxLimit: 1000 MinLimit: 4</font>
    复制代码


    从代码中看确实 100%,太牛了,而且 WorkRequest 还有任务堆积现象,确认无疑后,接下来看下是谁引发的?
      2. 谁导致的 CPU 爆高
      根据惯例首先怀疑是不是 GC 触发所致,可以用 !t? 查看下线程列表,观察下有没有 GC 字样。



    1. <font size="3">:000> !t

    2.   ThreadCount:      53

    3.   UnstartedThread:  0

    4.   BackgroundThread: 53

    5.   PendingThread:    0

    6.   DeadThread:       0

    7.   Hosted Runtime:   no

    8.                                                                                                           Lock  

    9.          ID OSID ThreadOBJ           State GC Mode     GC Alloc Context                  Domain           Count Apt Exception

    10.      4    1 1240 00000000021cdf30    2a220 Preemptive  0000000000000000:0000000000000000 00000000021d94c0 0     MTA

    11.     23    2 4db4 00000000041cdaa0    2b220 Preemptive  0000000000000000:0000000000000000 00000000021d94c0 0     MTA (Finalizer)

    12.     ...

    13.     65  156 22f4 000000000b1a3f60  8029220 Preemptive  00000004527751F0:0000000452775EE8 00000000021d94c0 0     MTA (Threadpool Completion Port)

    14.     66  205 2ef8 000000000b1a1080  8029220 Preemptive  0000000157641DE0:00000001576435B0 00000000021d94c0 0     MTA (Threadpool Completion Port)

    15.     ...</font>
    复制代码


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


    1. <font size="3">0:000> !cpuid

    2.   CP  F/M/S  Manufacturer     MHz

    3.    0  6,79,1  <unavailable>   2299

    4.    1  6,79,1  <unavailable>   2299

    5.    2  6,79,1  <unavailable>   2299

    6.    3  6,79,1  <unavailable>   2299

    7.   0:000> ~*e !clrstack

    8.   OS Thread Id: 0x2cc4 (68)

    9.           Child SP               IP Call Site

    10.   000000000c14e758 00007ffadeb86e4a [GCFrame: 000000000c14e758]

    11.   000000000c14e840 00007ffadeb86e4a [GCFrame: 000000000c14e840]

    12.   000000000c14e878 00007ffadeb86e4a [HelperMethodFrame: 000000000c14e878] System.Threading.Monitor.Enter(System.Object)

    13.   000000000c14e970 00007ffaceb40491 System.Net.ConnectionGroup.Disassociate(System.Net.Connection) [f:\dd\NDP\fx\src\net\System\Net\_ConnectionGroup.cs @ 148]

    14.   000000000c14e9d0 00007ffaceb3fc93 System.Net.Connection.PrepareCloseConnectionSocket(System.Net.ConnectionReturnResult ByRef) [f:\dd\NDP\fx\src\net\System\Net\_Connection.cs @ 3048]

    15.   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]

    16.   000000000c14eb00 00007ffacebc4118 System.Net.Connection.ReadComplete(Int32, System.Net.WebExceptionStatus) [f:\dd\NDP\fx\src\net\System\Net\_Connection.cs @ 3387]

    17.   000000000c14eb80 00007ffacebe3dc5 System.Net.LazyAsyncResult.Complete(IntPtr) [f:\dd\NDP\fx\src\net\System\Net\_LazyAsyncResult.cs @ 415]

    18.   000000000c14ebe0 00007ffacebe3d07 System.Net.LazyAsyncResult.ProtectedInvokeCallback(System.Object, IntPtr) [f:\dd\NDP\fx\src\net\System\Net\_LazyAsyncResult.cs @ 368]

    19.   000000000c14ec20 00007ffacf3a476f System.Net.Security._SslStream.StartFrameBody(Int32, Byte[], Int32, Int32, System.Net.AsyncProtocolRequest)

    20.   000000000c14ec80 00007ffacebb3ed8 System.Net.Security._SslStream.ReadHeaderCallback(System.Net.AsyncProtocolRequest) [f:\dd\NDP\fx\src\net\System\Net\SecureProtocols\_SslStream.cs @ 1007]

    21.   000000000c14ece0 00007ffacebae5ee System.Net.AsyncProtocolRequest.CompleteRequest(Int32) [f:\dd\NDP\fx\src\net\System\Net\SecureProtocols\_HelperAsyncResults.cs @ 142]

    22.   000000000c14ed10 00007ffacf3a3567 System.Net.FixedSizeReader.CheckCompletionBeforeNextRead(Int32)

    23.   000000000c14ed40 00007ffacebae507 System.Net.FixedSizeReader.ReadCallback(System.IAsyncResult) [f:\dd\NDP\fx\src\net\System\Net\SecureProtocols\_FixedSizeReader.cs @ 148]

    24.   000000000c14ed90 00007ffacebe3dc5 System.Net.LazyAsyncResult.Complete(IntPtr) [f:\dd\NDP\fx\src\net\System\Net\_LazyAsyncResult.cs @ 415]

    25.   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]

    26.   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]

    27.   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]

    28.   000000000c14ef40 00007ffaceba60cf System.Net.ContextAwareResult.Complete(IntPtr) [f:\dd\NDP\fx\src\net\System\Net\_ContextAwareResult.cs @ 463]

    29.   000000000c14ef90 00007ffacebe3d07 System.Net.LazyAsyncResult.ProtectedInvokeCallback(System.Object, IntPtr) [f:\dd\NDP\fx\src\net\System\Net\_LazyAsyncResult.cs @ 368]

    30.   000000000c14efd0 00007ffaceba5e2f System.Net.Sockets.BaseOverlappedAsyncResult.CompletionPortCallback(UInt32, UInt32, System.Threading.NativeOverlapped*) [f:\dd\NDP\fx\src\net\System\Net\Sockets\_BaseOverlappedAsyncResult.cs @ 399]

    31.   000000000c14f040 00007ffadcc2ffef System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32, UInt32, System.Threading.NativeOverlapped*) [f:\dd\ndp\clr\src\BCL\system\threading\overlapped.cs @ 135]

    32.   000000000c14f1f0 00007ffade9a6d93 [GCFrame: 000000000c14f1f0]

    33.   OS Thread Id: 0x5d6c (70)

    34.           Child SP               IP Call Site

    35.   000000000943e920 00007ffacf139bfb System.Net.Connection.HandleError(Boolean, Boolean, System.Net.WebExceptionStatus, System.Net.ConnectionReturnResult ByRef)

    36.   OS Thread Id: 0x4ad4 (75)

    37.           Child SP               IP Call Site

    38.   ...

    39.   000000000c94e5a0 00007ffacf139bfb System.Net.Connection.HandleError(Boolean, Boolean, System.Net.WebExceptionStatus, System.Net.ConnectionReturnResult ByRef)

    40.   ...

    41.   OS Thread Id: 0x1d70 (80)

    42.           Child SP               IP Call Site

    43.   ...

    44.   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]

    45.   ...</font>
    复制代码


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


    1. <font size="3">0:000> ~80s

    2.   clr!AwareLock::Contention+0x194:

    3.   00007ffa`deb86e40 4883e801        sub     rax,1

    4.   0:080> !mdso

    5.   Thread 80:

    6.   Location          Object            Type

    7.   ------------------------------------------------------------

    8.   000000000d24e098  000000015765e028  System.Net.WebException

    9.   000000000d24e0f8  0000000340b07110  System.Collections.ArrayList

    10.   000000000d24e110  000000015765e2b8  System.Net.HttpWebRequest[]

    11.   000000000d24e1c0  0000000340b070b8  System.Net.ConnectionGroup

    12.   000000000d24e258  0000000144a79678  System.Net.Connection

    13.   0:080> !mdt 000000015765e028

    14.   000000015765e028 (System.Net.WebException)

    15.       _className:NULL (System.String)

    16.       _exceptionMethod:NULL (System.Reflection.MethodBase)

    17.       _exceptionMethodString:NULL (System.String)

    18.       _message:000000015765df70 (System.String) Length=77, String="The underlying connection was closed: The connection was closed unexpectedly."

    19.       ...</font>
    复制代码


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


    1. <font size="3">0:080> !dumpheap -stat

    2.   Statistics:

    3.                 MT    Count    TotalSize Class Name

    4.   ...

    5.   00007ffacecc38b0    13315      2343440 System.Net.WebException

    6.   00007ffadcdf6570    11369      1909992 System.IO.IOException

    7.   00007ffadcdf5fb8    13380      2247840 System.ObjectDisposedException

    8.   ...</font>
    复制代码


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


    1. <font size="3">0:048> !dumpheap -stat

    2.   Statistics:

    3.                 MT    Count    TotalSize Class Name

    4.   00007ffacecc38b0    26745      4707120 System.Net.WebException

    5.   00007ffadcdf6570    26722      4489296 System.IO.IOException

    6.   00007ffadcdf5fb8    28745      4829160 System.ObjectDisposedException</font>
    复制代码


    可以看到,2 min 之内异常增加了合计 4w? 左右,这就验证了程序确实是疯狂的抛异常,在 Windows 平台上不管是硬件异常?还是软件异常都由 Windows SEH 异常处理框架统一处理,会出现用户态和内核态的切换,这样疯狂的抛出,必然会导致 CPU 爆高,终于找到原因了,接下来就是寻找诱发因素。
      3. 异常是谁诱发的
      再回头看 HandleError? 函数的调用栈都是底层的库函数,从线程栈的 PerformIOCompletionCallback? 函数来看是 IO线程? 诱发的,能被 IO 线程兜到是因为这是做了异步处理,既然是 异步?,自然 OverlappedData 也会非常多。



    1. <font size="3"> 0:080> !gchandles -stat

    2.   Statistics:

    3.                 MT    Count    TotalSize Class Name

    4.   00007ffadc6f7b98    14511      1625232 System.Threading.OverlappedData

    5.   Total 17550 objects

    6.   Handles:

    7.       Strong Handles:       426

    8.       Pinned Handles:       23

    9.       Async Pinned Handles: 14511

    10.       Ref Count Handles:    24

    11.       Weak Long Handles:    2430

    12.       Weak Short Handles:   132

    13.       SizedRef Handles:     4</font>
    复制代码


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


    1. <font size="3">0:080> !mdso

    2.   Thread 80:

    3.   Location          Object            Type

    4.   ------------------------------------------------------------

    5.   ...

    6.   000000000d24e488  0000000358c57918  System.Net.HttpWebRequest

    7.   000000000d24e2e8  00000001407b5b40  System.String  "net_io_readfailure"

    8.   ...

    9.   0:080> !mdt -r:2 0000000358c57918

    10.   0000000358c57918 (System.Net.HttpWebRequest)

    11.       _Uri:0000000358c57210 (System.Uri)

    12.           m_String:00000002407ee430 (System.String) Length=98, String="https://api.xxxx/peer_messages"

    13.           ....</font>
    复制代码


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


    分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
    收藏收藏
    回复

    使用道具 举报

    本版积分规则

    关闭

    站长推荐上一条 /1 下一条

    小黑屋|手机版|Archiver|51Testing软件测试网 ( 沪ICP备05003035号 关于我们

    GMT+8, 2024-9-27 06:12 , Processed in 0.060568 second(s), 22 queries .

    Powered by Discuz! X3.2

    © 2001-2024 Comsenz Inc.

    快速回复 返回顶部 返回列表