.NET CPU爆高事故事故分析某供應鏈WEB網站
一:背景
1. 講故事
年前有位朋友加微信求助,說他的程序出現瞭偶發性CPU爆高,尋求如何解決,截圖如下:
我建議朋友用 procdump 在 cpu 高的時候連抓兩個dump,這樣分析起來比較穩健,朋友也如期的成功抓到,接下來就用 windbg 一起來分析下吧。
二:Windbg 分析
1. 查看CPU占用率
先用 !tp 查看兩個 dump 的cpu 利用率
0:112> !tp CPU utilization: 100% Worker Thread: Total: 138 Running: 128 Idle: 10 MaxLimit: 2000 MinLimit: 400 Work Request in Queue: 17 Unknown Function: 00007ffe1a6617d0 Context: 000001fd9bcb20c8 ... -------------------------------------- Number of Timers: 2 -------------------------------------- Completion Port Thread:Total: 2 Free: 2 MaxFree: 16 CurrentLimit: 2 MaxLimit: 2000 MinLimit: 400 0:014> !tp CPU utilization: 96% Worker Thread: Total: 173 Running: 67 Idle: 106 MaxLimit: 2000 MinLimit: 400 Work Request in Queue: 1 Unknown Function: 00007ffe1a6617d0 Context: 000001fda1a20be8 -------------------------------------- Number of Timers: 2 -------------------------------------- Completion Port Thread:Total: 7 Free: 7 MaxFree: 16 CurrentLimit: 7 MaxLimit: 2000 MinLimit: 400
果然如朋友所述,接下來就可以試探的看下是不是 GC 觸發導致 ?
2. 查看是否 GC 觸發
幹脆一點就是用 ~*e !dumpstack 導出所有線程的托管和非托管棧,然後搜索 GarbageCollectGeneration 就好瞭。
果然是觸發瞭 GC,從調用棧信息看,當前托管層可能正在高頻的 new 操作,導致隻往某一個heap上狂寫數據從而致 heap 失衡,服務器模式GC為瞭讓多 heap 均衡,做瞭 heap balance 操作,接下來的線索是為什麼有狂寫的情況? 還得看下托管層,使用 !clrstack 命令。
0:112> !clrstack OS Thread Id: 0x3278 (112) Child SP IP Call Site 000000b4ddc79098 00007ffe28b9fa74 [HelperMethodFrame: 000000b4ddc79098] 000000b4ddc791a0 00007ffda6c229cb System.Data.Entity.ModelConfiguration.Utilities.EdmPropertyPath.System.Collections.Generic.IEnumerable<System.Data.Entity.Core.Metadata.Edm.EdmProperty>.GetEnumerator() 000000b4ddc79200 00007ffe01a179eb System.Linq.Enumerable.SequenceEqual[[System.__Canon, mscorlib]](System.Collections.Generic.IEnumerable`1<System.__Canon>, System.Collections.Generic.IEnumerable`1<System.__Canon>, System.Collections.Generic.IEqualityComparer`1<System.__Canon>) 000000b4ddc79280 00007ffda6c2297e System.Data.Entity.ModelConfiguration.Configuration.Mapping.EntityMappingConfiguration+c__DisplayClass14.b__11(System.Data.Entity.Core.Mapping.ColumnMappingBuilder) 000000b4ddc792b0 00007ffe01a13f8f System.Linq.Enumerable.SingleOrDefault[[System.__Canon, mscorlib]](System.Collections.Generic.IEnumerable`1<System.__Canon>, System.Func`2<System.__Canon,Boolean>) 000000b4ddc79330 00007ffda6c2087c System.Data.Entity.ModelConfiguration.Configuration.Mapping.EntityMappingConfiguration.Configure(System.Data.Entity.Core.Metadata.Edm.DbDatabaseMapping, System.Data.Entity.Core.Common.DbProviderManifest, System.Data.Entity.Core.Metadata.Edm.EntityType, System.Data.Entity.Core.Mapping.StorageEntityTypeMapping ByRef, Boolean, Int32, Int32) 000000b4ddc79520 00007ffda6c20128 System.Data.Entity.ModelConfiguration.Configuration.Types.EntityTypeConfiguration.ConfigureUnconfiguredType(System.Data.Entity.Core.Metadata.Edm.DbDatabaseMapping, System.Data.Entity.Core.Common.DbProviderManifest, System.Data.Entity.Core.Metadata.Edm.EntityType) 000000b4ddc795a0 00007ffda6c1ffaf System.Data.Entity.ModelConfiguration.Configuration.Types.EntityTypeConfiguration.ConfigureTablesAndConditions(System.Data.Entity.Core.Mapping.StorageEntityTypeMapping, System.Data.Entity.Core.Metadata.Edm.DbDatabaseMapping, System.Data.Entity.Core.Common.DbProviderManifest) 000000b4ddc79620 00007ffda6c055c0 System.Data.Entity.ModelConfiguration.Configuration.ModelConfiguration.ConfigureEntityTypes(System.Data.Entity.Core.Metadata.Edm.DbDatabaseMapping, System.Data.Entity.Core.Common.DbProviderManifest) 000000b4ddc79680 00007ffda6c05474 System.Data.Entity.ModelConfiguration.Configuration.ModelConfiguration.Configure(System.Data.Entity.Core.Metadata.Edm.DbDatabaseMapping, System.Data.Entity.Core.Common.DbProviderManifest) 000000b4ddc796d0 00007ffda69ae5c2 System.Data.Entity.DbModelBuilder.Build(System.Data.Entity.Core.Common.DbProviderManifest, System.Data.Entity.Infrastructure.DbProviderInfo) 000000b4ddc79740 00007ffda6649ccf System.Data.Entity.DbModelBuilder.Build(System.Data.Common.DbConnection) 000000b4ddc79780 00007ffda7b4b2d3 System.Data.Entity.Infrastructure.EdmxWriter.WriteEdmx(System.Data.Entity.DbContext, System.Xml.XmlWriter) 000000b4ddc797c0 00007ffda7b4acbe Class125.smethod_0(System.Data.Entity.DbContext) 000000b4ddc79820 00007ffda7b4aba4 Class617.smethod_22(System.Data.Entity.DbContext) 000000b4ddc79860 00007ffda7b4aa90 Class617.smethod_27(System.Data.Entity.DbContext) 000000b4ddc798c0 00007ffda7b3e9ec DbContextExtensions.GetModel(System.Data.Entity.DbContext) 000000b4ddc79910 00007ffda7b3e49b Class124.smethod_0(System.Data.Entity.DbContext, System.String) 000000b4ddc79950 00007ffda7b3d6c3 Class486.smethod_3[[System.__Canon, mscorlib]](System.Data.Entity.DbContext, Z.BulkOperations.BulkOperation`1<System.__Canon>, System.Collections.Generic.IEnumerable`1<System.__Canon>, System.Collections.Generic.List`1<System.Object>) 000000b4ddc79a00 00007ffda7b36871 DbContextExtensions.BulkInsert[[System.__Canon, mscorlib]](System.Data.Entity.DbContext, System.Collections.Generic.IEnumerable`1<System.__Canon>, System.Action`1<Z.EntityFramework.Extensions.EntityBulkOperation`1<System.__Canon>>) 000000b4ddc79ab0 00007ffda7b32c65 xxx.EFRepository`1[[System.__Canon, mscorlib]].BulkInsert(System.__Canon[]) ...
從棧信息看,大概有如下三點信息:
- 正在用 EF 做批量插入操作 BulkInsert。
- 用瞭 Z.EntityFramework 插件。
- 大量的 Build, Configure 字樣,貌似是在做什麼配置,構建啥的。
3. 是插入數據過多導致的嗎?
第一個能想到的就是 list 過大,為瞭驗證,可以用 !clrstack -a 把 BulkInsert 方法的 list 參數給導出來。
0:112> !clrstack -a OS Thread Id: 0x3278 (112) Child SP IP Call Site 000000b4ddc79b90 00007ffda7b31ee8 xxx.BLL.BaseService`1[[System.__Canon, mscorlib]].BulkInsert(System.__Canon[]) PARAMETERS: this (0x000000b4ddc79d10) = 0x000001fa14bbb630 _tArr (0x000000b4ddc79d18) = 0x000001fa14c1a2f8 0:112> !do 0x000001fa14c1a2f8 Name: xxx.EntityModel.xxx[] MethodTable: 00007ffda9437968 EEClass: 00007ffe02f556b0 Size: 56(0x38) bytes Array: Rank 1, Number of elements 4, Type CLASS (Print Array) Fields: None
從輸出看,當前的list.length=4,這就很疑惑瞭,既然 heap 都在 balance ,那是不是有幾個線程在猛攻? 為瞭驗證就用 DbContextExtensions.BulkInsert 在所有的托管線程棧上搜關鍵詞看看。
可以看到當前有 10 處在猛攻,依次看他們的list都不大,疑惑哈。
4. 對問題的預判斷
有瞭這些思路,但總覺得觸發GC的由頭太怪瞭,不過可以肯定的是問題出在瞭 Z.EntityFramework 插件上,按照 4S店的傳統經驗,隻換不修肯定沒問題,由於我對 Z.EntityFramework 不熟悉,也隻能這樣給到朋友瞭。
說來也奇怪,朋友第二天發現瞭一個奇怪現象,說每次 CPU 爆高之前都出現瞭一次 w3wp的異常重啟,而重啟之後由於 Z.EntityFramework 需要預熱,導致後續請求阻塞引發的 CPU 階段性爆高。
從朋友的留言加上剛才的 dump 分析,問題基本就能定位瞭, Build, Configure 和 banlance 操作都能解釋的通,而且還發現這個所謂的預熱並沒有做到串行化,而是10個線程一起來,直到預熱結束,CPU 下降。
三:總結
總的來說,這次CPU階段性爆高的事故是由於 w3wp 進程的意外重啟,導致多線程並發對 Z.EntityFramework 預熱,在預熱的過程中導致瞭多次 GC 觸發,至於 w3wp 為什麼被意外終止,這就是另外一個話題瞭,不過好消息是朋友在後續的幾天中從抓取的 crash dump中找到瞭問題代碼。
以上就是.NET CPU爆高事故事故分析某供應鏈WEB網站的詳細內容,更多關於.NET WEB網站CPU爆高的資料請關註WalkonNet其它相關文章!
推薦閱讀:
- Entity Framework使用配置夥伴創建數據庫
- EF使用Code First模式生成單數形式表名
- Entity Framework管理一對二實體關系
- Entity Framework使用Fluent API配置案例
- Entity Framework加載控制Loading Entities