一:布景1. 讲故事

不断在逃那个系列的伴侣应该能感触感染到,我给那个行业中无数的目生人阐发过各类dump,末于在上周有位老同窗找到我,仍是个大妹子,必需有求必应 。

妹子公司的系统比来在某次晋级之后,在顶峰期会碰到 CPU 爆高的现象,有些单元你懂的,很强势,所以就苦逼了法式媛,不管怎么说,既然找上我,得想各类法子给处理掉,用长途的体例告诉了老同窗怎么用 PRocdump 去抓 dump,在一个小时之后 dump 胜利拿到,接下来就来阐发了。

二:WinDbg 阐发1. CPU 实的爆高吗

是不是爆高必然要拿数据说话,能够用 !tp 号令验证。

0:234> !tpCPU utilization: 44%Worker Thread: Total: 68 Running: 38 Idle: 10 MaxLimit: 4800 MinLimit: 48Work Request in Queue: 0--------------------------------------Number of Timers: 2--------------------------------------Completion Port Thread:Total: 1 Free: 1 MaxFree: 96 CurrentLimit: 1 MaxLimit: 4800 MinLimit: 48

从输出看 CPU=44% 就拿到了 dump ,实的有点奇异,我设置的阈值是 70% ,可能是老同窗后来改了阈值,固然不是我的预期但仍是能够探究一下的。

接下来怎么排查呢?我们都晓得 CPU 高素质都是由 线程 形成的,所以到每个线程栈上去兜兜,看看有没有什么收成,利用号令 ~*e !clrstack 即可。

0:234> ~*e !clrstackOS Thread Id: 0x6de8 (234) Child SP IP Call Site000000959273dfd8 00007ff89d287174 [HelPErMethodFrame_2OBJ: 000000959273dfd8] System.Security.Cryptography.Utils._AcquireCSP(System.Security.Cryptography.CspParameters, System.Security.Cryptography.SafeProvHandle ByRef)000000959273e0d0 00007ff88af3074e System.Security.Cryptography.Utils.AcquireProvHandle(System.Security.Cryptography.CspParameters)000000959273e110 00007ff88b85e585 System.Security.Cryptography.CryptoAPITransform..ctor(Int32, Int32, Int32[], System.Object[], Byte[], System.Security.Cryptography.PaddingMode, System.Security.Cryptography.CipherMode, Int32, Int32, Boolean, System.Security.Cryptography.CryptoAPITransformMode)000000959273e230 00007ff88b86057d System.Security.Cryptography.DESCryptoServiceProvider._NewEncryptor(Byte[], System.Security.Cryptography.CipherMode, Byte[], Int32, System.Security.Cryptography.CryptoAPITransformMode)000000959273e2e0 00007ff88b8602f3 System.Security.Cryptography.DESCryptoServiceProvider.CreateDecryptor(Byte[], Byte[])0000009588e7e300 00007ff82f7bd49a xxx.DesDecrypt(System.String)0000009588e7e370 00007ff82f9848e2 xxx.b__17(xxx_UserInfo)0000009588e7e3a0 00007ff887d7778e System.Linq.Enumerable+WhereListIterator`1[[System.__Canon, mscorlib]].MoveNext()0000009588e7e3e0 00007ff887d7767e System.Linq.Enumerable.FirstOrDefault[[System.__Canon, mscorlib]](System.Collections.Generic.IEnumerable`1<System.__Canon>)...记一次 .NET 某医保平台 CPU 爆高分析  第1张

从输出以及截图来看,有 38 个线程正在施行 xxx.DesDecrypt 办法,看起来是一个 DES 解密操做,一般来说 加解密 比力费CPU资本,看样子找到问题根了,按照线程栈找一下代码到底是怎么写的,为了保密起见,那里就多用几个 XXX 来替代吧,参考代码如下:

protected void xxx(string xxx) { xxx userInfo = xxxUserInfoList.Where((xxx p) => p.xxx.Name == xxx.DesDecrypt(base.User.Name)).FirstOrDefault(); }

说实话第一眼看到在 Where 中利用 DesDecrypt(base.User.Name) 办法,固然有点别扭,心里仍是觉得扩展办法应该会帮我优化把它零丁给提出来的,好比下面如许。

protected void xxx(string xxx) { var nameWhere= xxx.DesDecrypt(base.User.Name); xxx userInfo = xxxUserInfoList.Where((xxx p) => p.xxx.Name == nameWhere).FirstOrDefault(); }

但看了线程栈上的 WhereListIterator 和 FirstOrDefault 办法,貌似没有做优化,为了验证我的设法,我还特意写了段代码。

internal class Program { static void Main(string[] args) { var query = new List<string>() { "1", "2","3","4" }; var text = "hello world"; query.Where(i => i == Run(text)).FirstOrDefault(); } public static string Run(string str) { Console.WriteLine(str); return str; } }记一次 .NET 某医保平台 CPU 爆高分析  第2张

从输出成果看 hello world 输出了 4 次,也就表白当前并没有做任何优化。

再说点题外话,比来在研究 SQLSERVER,我觉得它的 SQL优化器应该可以处置那种情况,截图如下:

SELECT OrderID FROM dbo.OrdersWHERE OrderDate > SUBSTRING('1996-07-10 00:00:10.000', 0, 11) AND OrderDate < SUBSTRING('1996-07-20 00:00:20.000', 0, 11);记一次 .NET 某医保平台 CPU 爆高分析  第3张

固然 SQLSERVER 十分智能的做了优化,但后来想一想C#不优化是对的,因为框架代码没法包管向 xxx.DesDecrypt 办法中传入不异参数,返回的成果必然是不异的,所以接纳守旧的办法可以理解。

2. 解密操做实的会爆高吗

就算WHERE中逐项处置解密操做就必然会爆高吗?那是一个需要求证的问题,能够切到某个线程上用 !dso 找出阿谁 list,然后 !do 即可,参考代码如下:

0:234> !DumpObj /d 000001f631799240Name: System.Collections.Generic.List`1[[xxxx_UserInfo,xxxx]]MethodTable: 00007ff82f5f3b20EEClass: 00007ff88ab59f90Size: 40(0x28) bytesFile: C:\Windows\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dllFields: MT Field Offset Type VT Attr Value Name00007ff88b12d6c8 4001843 8 System.__Canon[] 0 instance 000001f6b14ae380 _items00007ff88b123e98 4001844 18 System.Int32 1 instance 3506 _size00007ff88b123e98 4001845 1c System.Int32 1 instance 3506 _version00007ff88b121c98 4001846 10 System.Object 0 instance 0000000000000000 _syncRoot00007ff88b12d6c8 4001847 8 System.__Canon[] 0 static <no information>

从输出能够明晰的看到,当前的 list.count=3506 个,在加上有 38 个线程在并发处置,所以最坏情况下是 3506 * 38 = 13w 的解密操做,难怪说顶峰期的时候 CPU 爆高。

处理法子也很简单,把 xxx.DesDecrypt 给提出来,将解密操做由本来的 13w 优化到 38 次,下战书大妹子反应问题已经处理。

记一次 .NET 某医保平台 CPU 爆高分析  第4张

看到大妹子高兴的笑了,我的饭有下落了

三:总结

那个问题是一个不良习惯的写法形成的,说实话,我不是看到线程栈上的 WhereListIterator 办法我也不相信会施行屡次,可能比来中了 SQLSERVER 的毒,不管怎么说,那些都是小事,有大餐吃才是最重要的!