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

一:背景

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: 3AsyncTimerCallbackCompletion TimerInfo@00000000042d2430AsyncTimerCallbackCompletion TimerInfo@00000000042d2f90AsyncTimerCallbackCompletion 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:   noLock  ID OSID ThreadOBJ           State GC Mode     GC Alloc Context                  Domain           Count Apt Exception4    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     MHz0  6,79,1  <unavailable>   22991  6,79,1  <unavailable>   22992  6,79,1  <unavailable>   22993  6,79,1  <unavailable>   22990: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.Connection0: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 objectsHandles:Strong Handles:       426Pinned Handles:       23Async Pinned Handles: 14511Ref Count Handles:    24Weak Long Handles:    2430Weak Short Handles:   132SizedRef 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 爆高现象。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/281637.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linux下mariadb大小写敏感

2019独角兽企业重金招聘Python工程师标准>>> Linux下安装好mariadb后&#xff0c;在使用时会发现mariadb对大小写敏感&#xff0c;这对开发带来一定的不利&#xff0c;这时只要在配置文件中配置一下&#xff0c;取消大小写敏感即可&#xff1a; sudo vi /etc/MySQL/…

评论列表显示及排序,个人中心显示

1.显示所有评论{% for foo in ques.comments %} 2.所有评论排序uquestion db.relationship(Question, backrefdb.backref(comments, order_bycreat_time.desc)) 3.显示评论条数{{ ques.comments|length }} 1题代码如下&#xff1a; <h3>评论区:({{ ques.comments|length…

软件工程现行国标汇集

GB/T 8566-2007 《信息技术 软件生存周期过程》GB/T 8567-2006 《计算机软件文档编制规范》 GB/T 9385-2008《计算机软件需求规格说明规范》GB/T 9386-2008 《计算机软件测试文档编制规范》 GB/T 11457-2006《信息技术 软件工程术语》GB/T 14394-2008《计算机软件可靠性和可维护…

oracle listagg方法,Oracle实现字符串拼接和分离功能的方法(LISTAGG函数),oraclelistagg...

Oracle实现字符串拼接和分离功能的方法(LISTAGG函数)&#xff0c;oraclelistagg字符串拼接(String Aggregation Techniques)是数据处理时经常需要用到一个技术&#xff0c;比如需要按时间顺序拼装一个快递的运输记录&#xff0c;或者将流程中各个环节的处理人拼装为一个字符串。…

他俩都曾是技术大牛,创业这些年来有怎样的苦与乐?

这是头哥侃码的第263篇原创国庆假期回来&#xff0c;「头哥唠 B 唠」的直播仍在继续。这次我邀请了我工作上的老板和朋友&#xff0c;一起聊了聊关于 “技术创业路上的苦与乐”。熟悉他们两位的都知道&#xff0c;可以说是技术出身&#xff0c;然后创业当老板的代表。大家都知道…

maven,gradle本地缓存位置

gradle: 配置系统环境变量GRADLE_USER_HOME即可&#xff0c;值为缓存位置。 maven: 修改settings文件&#xff1a;maven的home路径下的conf文件夹下的settings.xml 对于有些IDEA&#xff0c;还需要配置。但是不要再打开项目后的FILE-->settings配置&#xff0c;而是需要在选…

git和php的区别,Git与Github的有什么区别

Git是一款免费&#xff0c;开源的分布是版本&#xff0c;用于敏捷高效的处理任何或小或大的项目。分布式相对于集中式的最大区别在于开发者可以提到本地&#xff0c;每个开发者通过克隆&#xff0c;在本地磁盘内拷贝一个完整的GIt仓库。Git的功能特性&#xff1a;1.从服务器上克…

E20171214-sl

well-grounded  地基打的好 relevent adj. 有关的&#xff0c;中肯的; 相关联的proposal n. 建议; 提议; 求婚; 〈美〉投标;转载于:https://www.cnblogs.com/lancgg/p/8281675.html

客户端嵌套 Web 页面如何选择

客户端嵌套 Web 页面如何选择客户端嵌套 Web 页面如何选择作者&#xff1a;驚鏵在使用客户端嵌套WEB页面有一下几种方案&#xff1a;WebView2[1]Electron[2]NW.js[3]sciter[4]miniblink[5]现在国内众多桌面程序都是用了以下五种&#xff0c;因为它跨平台更为方便&#x1f447;。…

每天练习50个shell

1、统计日志每天IP访问量前十 awk {print $1} 1.log | sort -n | uniq -c | sort -n 解释&#xff1a;&#xff08;1&#xff09;awk 命令在分段方面比较有优势&#xff0c;这里的{print $1}将第一段打印出来&#xff0c;awk可以用-F指定分隔符&#xff0c;如果不指定分隔符&am…

【Tomcat】Tomcat配置与优化(内存、并发、管理)【自己配置】

一、JVM内存配置优化 主要通过以下的几个jvm参数来设置堆内存的&#xff1a; -Xmx512m 最大总堆内存&#xff0c;一般设置为物理内存的1/4 -Xms512m 初始总堆内存&#xff0c;一般将它设置的和最大堆内存一样大&#xff0c;这样就不需要根据当前堆使用情况而调整堆的大…

oracle 网关下载,oracle透明网关访问sqlserver2000

oracle透明网关访问sqlserver2000介绍oracle 10g透明网关访问sqlserver2000一、环境如下:ORACLE 10g 安装在:192.168.0.250 的window2003 server(版本&#xff1a;Oracle Database 10g Enterprise Edition Release 10.2.0.3.0 - 64bit Production) SqlServer 2000安装在:192.16…

C++和C#的指针小解

昨天和赵崇说了一下工作的事情&#xff0c;说起了性能问题就讨论起了数据结果和指针对性能的影响。曾经一直没有想到这方面的事情&#xff0c;这几天专门抽时间回想一下这方面的知识&#xff0c;然后一点一点的总结一下&#xff0c;看看数据结构和指针在咱们代码中是怎样实现效…

使用RoleBasedAuthorization实现基于用户角色的访问权限控制

本文将介绍如何通过 Sang.AspNetCore.RoleBasedAuthorization[1] 库实现 RBAC 权限管理。使用介绍Step 1添加库 Sang.AspNetCore.RoleBasedAuthorizationInstall-Package Sang.AspNetCore.RoleBasedAuthorizationStep 2在 Program.cs 中添加builder.Services.AddSangRoleBasedA…

【2018-11-15】中证1000指数的估值详情

中证1000指数选取中证500和沪深300指数样本股以外的&#xff0c;流动性好的1000只股票组成&#xff0c;与沪深300 和中证500 等形成互补。 中证1000的个股大多数是由市场上流通市值排名在 800 到 1800 名之间的个股组成&#xff0c;是一个适用范围较广的小盘指数。 中证1000的…

[Leetcode Week15]Populating Next Right Pointers in Each Node

Populating Next Right Pointers in Each Node 题解 原创文章&#xff0c;拒绝转载 题目来源&#xff1a;https://leetcode.com/problems/populating-next-right-pointers-in-each-node/description/ Description Given a binary tree struct TreeLinkNode {TreeLinkNode *left…

php 数组 1 开始,php数组使用1

1、array_values($arr) 将数组转换成索引数组$arr1 [id>10,name>杨过,sex>male,salary>8900];echo .var_export(array_values($arr1),true).;2、array_column($arr,$col,$boll); 获取多维数组的列表组成的数组。$arr2 [];$arr2 [[id>10,name>杨过,sex>…

印度光伏巨头Adani与华为签署500MW采购合同

日前&#xff0c;印度光伏巨头Adani与华为签订了采购合同。Adani未来一年的项目全部采用华为FusionSolar3.0智能光伏解决方案&#xff0c;首期500MW采购合同已经签署&#xff0c;将采购最新的智能光伏控制器&#xff08;组串逆变器&#xff09;SUN2000-43KTL、数据采集器SmartL…

宣布 .NET MAUI 支持 .NET 7 RC 2

点击上方蓝字关注我们&#xff08;本文阅读时间&#xff1a;6分钟)支持 .NET 7 Release Candidate 2 的 .NET 多平台应用程序 UI (MAUI) 现在可在 Windows 和 Mac 上的 Visual Studio 17.4 Preview 4 中使用。RC2 的主要主题是质量和对带有 iOS 16 的 Xcode 14 的 .NET 支持。此…

linux c文件操作,Linux C 文件的输入/输出操作

10.1 文件I/O操作概述在Linux系统中&#xff0c;文件I/O操作可以分为两类&#xff0c;一类是基于文件描述符的I/O操作&#xff0c;另一类是基于数据流的I/O操作。10.1.1 文件描述符简介在文件操作一章中&#xff0c;也经常提到文件描述符这个概念。所谓文件描述符&#xff0c;就…