記一次 .NET某網路邊緣計算系統 卡死分析

来源:https://www.cnblogs.com/huangxincheng/p/18277831
-Advertisement-
Play Games

一:背景 1. 講故事 早就聽說過有什麼 網路邊緣計算,這次還真給遇到了,有點意思,問了下 chatgpt 這是幹嘛的 ? 網路邊緣計算是一種計算模型,它將計算能力和數據存儲位置從傳統的集中式數據中心向網路邊緣的用戶設備、感測器和其他物聯網設備移動。這種模型的目的是在接近數據生成源頭的地方提供更快速 ...


一:背景

1. 講故事

早就聽說過有什麼 網路邊緣計算,這次還真給遇到了,有點意思,問了下 chatgpt 這是幹嘛的 ?

網路邊緣計算是一種計算模型,它將計算能力和數據存儲位置從傳統的集中式數據中心向網路邊緣的用戶設備、感測器和其他物聯網設備移動。這種模型的目的是在接近數據生成源頭的地方提供更快速的計算和數據處理能力,從而減少數據傳輸延遲並提高服務質量。網路邊緣計算使得在設備本地進行數據處理和決策成為可能,同時也有助於減輕對中心數據中心的網路流量和負載。

看到.NET還有這樣的應用場景還是挺欣慰的,接下來就來分析下這個dump到底是怎麼回事?

二:WinDbg 分析

1. 為什麼會卡死

不同程式的卡死有不同的分析方式,所以要先鑒別下程式的類型以及主線程的調用棧即可,參考如下:


0:000> !eeversion
5.0.721.25508
5.0.721.25508 @Commit: 556582d964cc21b82a88d7154e915076f6f9008e
Server mode with 64 gc heaps
SOS Version: 8.0.10.10501 retail build

0:000> k
 # Child-SP          RetAddr               Call Site
00 0000ffff`e0dddac0 0000fffd`c194c30c     libpthread_2_28!pthread_cond_wait+0x238
...
18 (Inline Function) --------`--------     libcoreclr!RunMain::$_0::operator()::{lambda(Param *)#1}::operator()+0x14c [/__w/1/s/src/coreclr/src/vm/assembly.cpp @ 1536] 
19 (Inline Function) --------`--------     libcoreclr!RunMain::$_0::operator()+0x188 [/__w/1/s/src/coreclr/src/vm/assembly.cpp @ 1538] 
1a 0000ffff`e0dde600 0000fffd`c153e860     libcoreclr!RunMain+0x298 [/__w/1/s/src/coreclr/src/vm/assembly.cpp @ 1538] 
...
20 0000ffff`e0dded10 0000fffd`c1bf7800     libhostpolicy!corehost_main+0xc0 [/root/runtime/src/installer/corehost/cli/hostpolicy/hostpolicy.cpp @ 409] 
21 (Inline Function) --------`--------     libhostfxr!execute_app+0x2c0 [/root/runtime/src/installer/corehost/cli/fxr/fx_muxer.cpp @ 146] 
22 (Inline Function) --------`--------     libhostfxr!<unnamed-namespace>::read_config_and_execute+0x3b4 [/root/runtime/src/installer/corehost/cli/fxr/fx_muxer.cpp @ 520] 
23 0000ffff`e0ddeeb0 0000fffd`c1bf6840     libhostfxr!fx_muxer_t::handle_exec_host_command+0x57c [/root/runtime/src/installer/corehost/cli/fxr/fx_muxer.cpp @ 1001] 
24 0000ffff`e0ddf000 0000fffd`c1bf4090     libhostfxr!fx_muxer_t::execute+0x2ec
25 0000ffff`e0ddf130 0000aaad`c9e1d22c     libhostfxr!hostfxr_main_startupinfo+0xa0 [/root/runtime/src/installer/corehost/cli/fxr/hostfxr.cpp @ 50] 
26 0000ffff`e0ddf200 0000aaad`c9e1d468     dotnet!exe_start+0x36c [/root/runtime/src/installer/corehost/corehost.cpp @ 239] 
27 0000ffff`e0ddf370 0000fffd`c1c63fe0     dotnet!main+0x90 [/root/runtime/src/installer/corehost/corehost.cpp @ 302] 
28 0000ffff`e0ddf3b0 0000aaad`c9e13adc     libc_2_28!_libc_start_main+0xe0
29 0000ffff`e0ddf4e0 00000000`00000000     dotnet!start+0x34

從卦中的指標來看,這是一個 Linux 上部署的 Web網站,既然是網站的卡死,那就要關註各個線程都在做什麼。

2. 線程都在幹嘛

以我多年的分析經驗,絕大多數都是由於 線程饑餓 或者說 線程池耗盡 導致的,首先我們看下線程池的情況。


0:000> !t
ThreadCount:      365
UnstartedThread:  0
BackgroundThread: 354
PendingThread:    0
DeadThread:       10
Hosted Runtime:   no
                                                                                                            Lock  
 DBG   ID     OSID ThreadOBJ           State GC Mode     GC Alloc Context                  Domain           Count Apt Exception
   0    1    31eaf 0000AAADF267C600  2020020 Preemptive  0000000000000000:0000000000000000 0000aaadf26634b0 -00001 Ukn 
...
 423  363    36d30 0000FFDDB4000B20  1020220 Preemptive  0000000000000000:0000000000000000 0000aaadf26634b0 -00001 Ukn (Threadpool Worker) 
 424  364    36d31 0000FFDDA8000B20  1020220 Preemptive  0000000000000000:0000000000000000 0000aaadf26634b0 -00001 Ukn (Threadpool Worker) 
 425  365    36d32 0000FFDDAC000B20  1020220 Preemptive  0000000000000000:0000000000000000 0000aaadf26634b0 -00001 Ukn (Threadpool Worker) 

0:000> !tp
Using the Portable thread pool.

CPU utilization:  9%
Workers Total:    252
Workers Running:  236
Workers Idle:     13
Worker Min Limit: 64
Worker Max Limit: 32767

Completion Total:   0
Completion Free:    0
Completion MaxFree: 128
Completion Current Limit: 0
Completion Min Limit:     64
Completion Max Limit:     1000

從卦中看當前有 365 個托管線程,這個算多嗎?對於64core 來說,這個線程其實算是正常,訓練營里的朋友都知道,server版的gc僅gc線程就有 64*2=128 個,接下來再看一個指標就是當前是否存在任務積壓? 可以使用 !ext tpq 命令,參考輸出如下:


0:000> !ext tpq
global work item queue________________________________

local per thread work items_____________________________________

從卦中看當前沒有任務積壓,這就有點反經驗了。

3. 真的不是線程饑餓嗎

最後一招比較徹底,就是看各個線程棧都在做什麼,可以使用 ~*e !clrstack 命令。

這不看不知道,一看嚇一跳,有 193 個線程在 Task.Result 上等待,這玩意太經典了,然後從上面的調用棧 UIUpdateTimer_Elapsed 來看,貌似是一個定時器導致的,接下來我就好奇這代碼是怎麼寫的?

分析上面的代碼之後,我發現它是和 Linux Shell 視窗進行命令交互,不知道為何 Shell 沒有響應導致代碼在這裡卡死。

4. 為什麼線程池沒有積壓

相信有很多朋友對這個反經驗的東西很好奇為什麼請求沒有積壓線上程池,其實這個考驗的是你對 PortableThreadPool 的底層瞭解,這裡我就簡單說一下吧。

  1. 在 ThreadPool 中有一個 GateThread 線程是專門給線程池動態註入線程的,參考代碼如下:

private static class GateThread
{
    private static void GateThreadStart()
    {
        while (true)
        {
            bool wasSignaledToWake = DelayEvent.WaitOne((int)delayHelper.GetNextDelay(tickCount));

            WorkerThread.MaybeAddWorkingWorker(threadPoolInstance);
        }
    }
}

  1. 一旦有人調用了 Task.Result 代碼,內部會主動喚醒 DelayEvent 事件,告訴 GateThread 趕緊通過 MaybeAddWorkingWorker 方法給我註入新的線程,參考代碼如下:

private bool SpinThenBlockingWait(int millisecondsTimeout, CancellationToken cancellationToken)
{
    bool flag3 = ThreadPool.NotifyThreadBlocked();

}
internal static bool NotifyThreadBlocked()
{
    if (UsePortableThreadPool)
    {
        return PortableThreadPool.ThreadPoolInstance.NotifyThreadBlocked();
    }
    return false;
}
public bool NotifyThreadBlocked()
{
    GateThread.Wake(this);
}

上面這種主動喚醒的機制是 C# 版 PortableThreadPool 做的優化來緩解線程饑餓的,這裡有一個重點就是它只能緩解,換句話說如果上游太猛了還是會有請求積壓的,但為什麼這裡沒有積壓呢? 很顯然上游不猛唄,那如何眼見為實呢? 這就需要看 timer 的周期數即可,到當前的線程棧上給扒出來。


0:417> !DumpObj /d 0000ffee380757f8
Name:        System.Timers.Timer
MethodTable: 0000fffd4ab24030
EEClass:     0000fffd4ad6e140
Size:        88(0x58) bytes
File:        /home/user/env/dotnet/shared/Microsoft.NETCore.App/5.0.7/System.ComponentModel.TypeConverter.dll
Fields:
              MT    Field   Offset                 Type VT     Attr            Value Name
0000fffd4c947498  400001c        8 ...ponentModel.ISite  0 instance 0000000000000000 _site
0000000000000000  400001d       10 ....EventHandlerList  0 instance 0000000000000000 _events
0000fffd479195d8  400001b       98        System.Object  0   static 0000000000000000 s_eventDisposed
0000fffd47926f60  400000e       40        System.Double  1 instance 3000.000000 _interval
0000fffd4791fb10  400000f       48       System.Boolean  1 instance                1 _enabled
0000fffd4791fb10  4000010       49       System.Boolean  1 instance                0 _initializing
0000fffd4791fb10  4000011       4a       System.Boolean  1 instance                0 _delayedEnable
0000fffd4ab241d8  4000012       18 ...apsedEventHandler  0 instance 0000ffee3807aae8 _onIntervalElapsed
0000fffd4791fb10  4000013       4b       System.Boolean  1 instance                1 _autoReset
0000fffd4c944ea0  4000014       20 ...SynchronizeInvoke  0 instance 0000000000000000 _synchronizingObject
0000fffd4791fb10  4000015       4c       System.Boolean  1 instance                0 _disposed
0000fffd49963e28  4000016       28 ...m.Threading.Timer  0 instance 0000ffee38098dc8 _timer
0000fffd48b90a30  4000017       30 ...ing.TimerCallback  0 instance 0000ffee3807aaa8 _callback
0000fffd479195d8  4000018       38        System.Object  0 instance 0000ffee38098db0 _cookie

從卦中看當前是 3s 為一個周期,這就能解釋為什麼線程池沒有積壓的底層原因了。

三:總結

這個卡死事故還是蠻好解決的,如果有一些經驗直接用dotnet-counter也是能搞定的,重點在於這是一個 Linux的dump,同時又是 .NET上的一個很好玩的場景,故此分享出來。

圖片名稱
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 主題介紹 WaterDrop 是 水滴 的意思,其實並沒有什麼特殊含義,只是因為每一次項目取名都絞盡腦汁,太麻煩了,於是就想著效法一些大佬,乾脆取名隨性一點。例如,Java 語言因作者經常在辦公室喝 Java 咖啡而得名,MySQL和MariaDB的作者是同一人,命名分別是他兩個女兒的名字(看樣子作 ...
  • 集合工廠 List<String> friends = Arrays.asList("Raphael", "Olivia"); friends.set(0, "Richard"); friends.add("Thibaut"); ← 拋出一個UnsupportedModificationExcept ...
  • 1、概述 Spring MVC是Spring Framework的Web開發部分,是基於Java實現MVC的輕量級Web框架。 官方文檔:https://docs.spring.io/spring-framework/docs/4.3.24.RELEASE/spring-framework-refe ...
  • 後兩次PTA總結 首先來看看第七次: 第七次相比於之前,添加了互斥開關元器件而且引入了並聯互串等等接法,按照我之前的設計,作出改動不算太難,我之前的遞歸已經可以按照順序儲存所以的元器件到一起去了,主要還是歸功於將串並聯電路繼承自元器件的方式十分有效,這樣就能夠將串並聯電路當作元器件一起處理,再按照遞 ...
  • 1. Spring MVC 獲取三個域(request請求域,session 會話域,application 應用域)對象的方式 @目錄1. Spring MVC 獲取三個域(request請求域,session 會話域,application 應用域)對象的方式2. Servlet中的三個域對象3 ...
  • ​《FFmpeg開發實戰:從零基礎到短視頻上線》一書的“10.2 FFmpeg推流和拉流”提到直播行業存在RTSP和RTMP兩種常見的流媒體協議。除此以外,還有比較兩種比較新的流媒體協議,分別是SRT和RIST。 其中SRT全稱為Secure Reliable Transport,中文叫做安全可靠傳 ...
  • 主題介紹 BeaconNav是基於typecho開發的一款導航主題,Beacon是燈塔的意思,希望使用者在知識的海洋里能夠如同有燈塔指引一樣目標明確,永遠不會迷失方向。 演示站點:https://nav.ilaozhu.com 主題特點 響應式設計,適配手機、平板、電腦等設備; 支持自定義 LOGO ...
  • 簡介: Redis是一款開源的使用ANSI C語言編寫、遵守BSD協議、支持網路、可基於記憶體也可持久化的日誌型、Key-Value高性能資料庫。Redis與其他Key-Value緩存產品相比有以下三個特點: 支持數據持久化,可以將記憶體中的數據保存在磁碟中,重啟可再次載入使用 支持簡單的Key-Val ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...