自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

記一次 .NET 某酒店后臺(tái)服務(wù)卡死分析

開(kāi)發(fā) 前端
我現(xiàn)在知道這個(gè) url 某個(gè)時(shí)段可能響應(yīng)出了問(wèn)題,但我線程池里的線程增速應(yīng)該很快呀,多余的線程不是可以響應(yīng)客戶端請(qǐng)求嗎?為什么我發(fā)現(xiàn)的情況是全部卡死呢?

一、背景

1. 講故事

停了一個(gè)月沒(méi)有更新文章了,主要是忙于寫 C#內(nèi)功修煉系列的PPT,現(xiàn)在基本上接近尾聲,可以回頭繼續(xù)更新這段時(shí)間分析dump的一些事故報(bào)告,有朋友微信上找到我,說(shuō)他們的系統(tǒng)出現(xiàn)了大量的http超時(shí),程序不響應(yīng)處理了,讓我?guī)兔聪略趺椿厥?,dump也抓到了。

二、WinDbg分析

1. 為什么會(huì)出現(xiàn)請(qǐng)求超時(shí)

既然超時(shí)說(shuō)明server端不響應(yīng)這個(gè)請(qǐng)求,繼而達(dá)到了超時(shí)時(shí)間的一種異常情況,所以首先要想到的就是 線程池的健康度,可以用 !tp 命令觀察,輸出如下:

0:000> !tp
CPU utilization: 0%
Worker Thread: Total: 537 Running: 537 Idle: 0 MaxLimit: 32767 MinLimit: 12
Work Request in Queue: 82
    Unknown Function: 00007fff566a17d0  Context: 0000020f08cbd658
    Unknown Function: 00007fff566a17d0  Context: 0000020f09acfa80
    Unknown Function: 00007fff566a17d0  Context: 0000020f08702198
    Unknown Function: 00007fff566a17d0  Context: 0000020f09ad9068
    Unknown Function: 00007fff566a17d0  Context: 0000020f09abffe8
    Unknown Function: 00007fff566a17d0  Context: 0000020f093c9948
    Unknown Function: 00007fff566a17d0  Context: 0000020f093cfd28
    Unknown Function: 00007fff566a17d0  Context: 0000020f093d9358
    Unknown Function: 00007fff566a17d0  Context: 0000020f093c34e8
    Unknown Function: 00007fff566a17d0  Context: 0000020f093dc568
    ...
--------------------------------------
Number of Timers: 2
--------------------------------------
Completion Port Thread:Total: 2 Free: 2 MaxFree: 24 CurrentLimit: 2 MaxLimit: 1000 MinLimit: 12

從上面的卦象看異常非常明顯,線程池總共有 537個(gè)工作線程都是處于運(yùn)行狀態(tài),相信有經(jīng)驗(yàn)的朋友應(yīng)該一眼就知道是怎么回事,專業(yè)術(shù)語(yǔ)叫:線程饑餓,并且線程池隊(duì)列也積壓了 82個(gè) 待處理的任務(wù)。

2. 線程為什么會(huì)饑餓

線程饑餓的原因有更多,我特意問(wèn)了下 chatgpt,列舉如下:

  • 優(yōu)先級(jí)傾斜:如果某些線程的優(yōu)先級(jí)設(shè)置過(guò)高,而其他線程的優(yōu)先級(jí)設(shè)置過(guò)低,高優(yōu)先級(jí)的線程可能會(huì)長(zhǎng)時(shí)間占用CPU資源,導(dǎo)致低優(yōu)先級(jí)線程無(wú)法獲得執(zhí)行機(jī)會(huì)。
  • 死鎖:當(dāng)多個(gè)線程相互等待對(duì)方釋放資源時(shí),可能會(huì)導(dǎo)致死鎖。在死鎖情況下,所有線程都無(wú)法繼續(xù)執(zhí)行,從而導(dǎo)致線程饑餓。
  • 資源競(jìng)爭(zhēng):多個(gè)線程競(jìng)爭(zhēng)有限的資源(如共享內(nèi)存、文件、網(wǎng)絡(luò)連接等)時(shí),可能會(huì)導(dǎo)致某些線程長(zhǎng)時(shí)間無(wú)法獲取到所需的資源而處于饑餓狀態(tài)。
  • 不公平的調(diào)度策略:調(diào)度器可能存在不公平的調(diào)度策略,導(dǎo)致某些線程無(wú)法獲得公平的CPU時(shí)間片,從而長(zhǎng)時(shí)間無(wú)法執(zhí)行。
  • 線程阻塞:某些線程可能由于等待I/O操作、鎖或其他原因而被阻塞,如果阻塞時(shí)間過(guò)長(zhǎng),可能導(dǎo)致其他線程饑餓。
  • 線程池配置不當(dāng):如果線程池中的線程數(shù)量設(shè)置不當(dāng),可能會(huì)導(dǎo)致某些任務(wù)長(zhǎng)時(shí)間等待執(zhí)行,從而引發(fā)線程饑餓。

那到底是哪一種情況呢?可以用 ~*e !clrstack 看一下各個(gè)線程此時(shí)正在做什么,輸出如下:

0:000> ~*e !clrstack
...
OS Thread Id: 0x2924 (74)
        Child SP               IP Call Site
000000e0ef47dc30 00007fff60fd6974 [GCFrame: 000000e0ef47dc30] 
000000e0ef47dd58 00007fff60fd6974 [HelperMethodFrame_1OBJ: 000000e0ef47dd58] System.Threading.Monitor.ObjWait(Boolean, Int32, System.Object)
000000e0ef47de70 00007ffef33e7269 System.Threading.ManualResetEventSlim.Wait(Int32, System.Threading.CancellationToken)
000000e0ef47df00 00007ffef33e6b58 System.Threading.Tasks.Task.SpinThenBlockingWait(Int32, System.Threading.CancellationToken)
000000e0ef47df70 00007ffef33e69e1 System.Threading.Tasks.Task.InternalWait(Int32, System.Threading.CancellationToken)
000000e0ef47e040 00007ffef60cce33 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(System.Threading.Tasks.Task)
000000e0ef47e070 00007ffef9df2c73 Exceptionless.Submission.DefaultSubmissionClient.SendHeartbeat(System.String, Boolean, Exceptionless.ExceptionlessConfiguration)
000000e0ef47e110 00007ffef109f03f System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
000000e0ef47e1e0 00007ffef109e784 System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
000000e0ef47e210 00007ffef15b670b System.Threading.TimerQueueTimer.CallCallback()
000000e0ef47e270 00007ffef15b644d System.Threading.TimerQueueTimer.Fire()
000000e0ef47e2e0 00007ffef15b5613 System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
000000e0ef47e320 00007ffef10b8319 System.Threading.ThreadPoolWorkQueue.Dispatch()
000000e0ef47e7a0 00007fff4fa06993 [DebuggerU2MCatchHandlerFrame: 000000e0ef47e7a0] 
000000e0ef47e908 00007fff4fa06993 [ContextTransitionFrame: 000000e0ef47e908] 
000000e0ef47eb40 00007fff4fa06993 [DebuggerU2MCatchHandlerFrame: 000000e0ef47eb40] 
...

發(fā)現(xiàn)有 473 個(gè)線程都在 Exceptionless.Submission.DefaultSubmissionClient.SendHeartbeat 方法上進(jìn)行等待,這就有意思了,原來(lái)是開(kāi)源的日志收集組件發(fā)送的心跳檢測(cè)方法,接下來(lái)趕緊看一下這個(gè)方法的源碼。

public void SendHeartbeat(string sessionIdOrUserId, bool closeSession, ExceptionlessConfiguration config)
{
 if (!config.IsValid)
 {
  return;
 }
 string requestUri = $"{GetHeartbeatServiceEndPoint(config)}/events/session/heartbeat?id={sessionIdOrUserId}&close={closeSession}";
 try
 {
  _client.Value.AddAuthorizationHeader(config.ApiKey);
  _client.Value.GetAsync(requestUri).ConfigureAwait(continueOnCapturedContext: false).GetAwaiter()
   .GetResult();
 }
 catch (Exception exception)
 {
  config.Resolver.GetLog().Error("Error submitting heartbeat: " + exception.GetMessage());
 }
}

從源碼看,居然用同步的方式發(fā)送 http請(qǐng)求,在這異步方法滿天飛的世界里,上面的寫法實(shí)屬異類。

3. 該如何解決呢?

既然是 Exceptionless 內(nèi)部寫的 SendHeartbeat 方法,我們程序員基本上無(wú)法干預(yù),能做到的無(wú)非如下兩點(diǎn):

  • 升級(jí)框架

看下了用的還是超老的 4.3 版本,可以升級(jí)到目前最新的 6.0.4 觀察試試。

[assembly: AssemblyTitle("Exceptionless")]
[assembly: AssemblyProduct("Exceptionless")]
[assembly: AssemblyCompany("Exceptionless")]
[assembly: AssemblyTrademark("Exceptionless")]
[assembly: AssemblyCopyright("Copyright (c) 2017 Exceptionless.  All rights reserved.")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("4.3.2027.0")]
[assembly: AssemblyInformationalVersion("4.3.2027$(VERSION_SUFFIX) f8d73f2fd7")]
[assembly: TargetFramework(".NETFramework,Version=v4.5", FrameworkDisplayName = ".NET Framework 4.5")]
[assembly: AssemblyVersion("4.3.2027.0")]

圖片圖片

  • 使用替代品,或者不用

哈哈,不用它,這是萬(wàn)能的治根之法。

三、對(duì)線程注入速度的解答

1. 朋友提了一個(gè)疑問(wèn)

我現(xiàn)在知道這個(gè) url 某個(gè)時(shí)段可能響應(yīng)出了問(wèn)題,但我線程池里的線程增速應(yīng)該很快呀,多余的線程不是可以響應(yīng)客戶端請(qǐng)求嗎?為什么我發(fā)現(xiàn)的情況是全部卡死呢?

2. 疑問(wèn)的簡(jiǎn)單解答

這個(gè)問(wèn)題其實(shí)是考察對(duì)線程池底層的了解,尤其是多久會(huì)向線程池注入一個(gè)活線程,在 .NET Framework 時(shí)代,在線程饑餓的情況下線程池內(nèi)部的 GateThread線程 會(huì) 1s 注入一個(gè)活線程,那如何驗(yàn)證呢?我們觀察后續(xù)的線程創(chuàng)建時(shí)間即可,使用 ~*e .ttime 。

0:000> ~*e .ttime
...
Created: Thu Nov 16 11:10:21.582 2023 (UTC + 8:00)
Kernel:  0 days 0:00:00.000
User:    0 days 0:00:00.000
Created: Thu Nov 16 11:10:22.593 2023 (UTC + 8:00)
Kernel:  0 days 0:00:00.000
User:    0 days 0:00:00.000
Created: Thu Nov 16 11:10:23.562 2023 (UTC + 8:00)
Kernel:  0 days 0:00:00.000
User:    0 days 0:00:00.000
Created: Thu Nov 16 11:10:24.062 2023 (UTC + 8:00)
Kernel:  0 days 0:00:00.000
User:    0 days 0:00:00.000
Created: Thu Nov 16 11:10:24.577 2023 (UTC + 8:00)
Kernel:  0 days 0:00:00.000
User:    0 days 0:00:00.000
Created: Thu Nov 16 11:10:25.562 2023 (UTC + 8:00)
Kernel:  0 days 0:00:00.000
User:    0 days 0:00:00.000
Created: Thu Nov 16 11:10:26.562 2023 (UTC + 8:00)
Kernel:  0 days 0:00:00.000
User:    0 days 0:00:00.015
Created: Thu Nov 16 11:10:27.562 2023 (UTC + 8:00)
Kernel:  0 days 0:00:00.000
User:    0 days 0:00:00.015
Created: Thu Nov 16 11:10:28.562 2023 (UTC + 8:00)
Kernel:  0 days 0:00:00.000
User:    0 days 0:00:00.015
Created: Thu Nov 16 11:10:29.577 2023 (UTC + 8:00)
Kernel:  0 days 0:00:00.000
User:    0 days 0:00:00.015
Created: Thu Nov 16 11:10:30.562 2023 (UTC + 8:00)
Kernel:  0 days 0:00:00.000
User:    0 days 0:00:00.000

從卦中的輸出來(lái)看,每一個(gè) Created 大概差 1s 鐘,這也是 GateThread 的功勞,這種注入速度在 .NET8 中已經(jīng)做了優(yōu)化,比如上面這種情況,Task 內(nèi)部會(huì)主動(dòng)喚醒 GateThread 線程讓其立即注入新線程,從而提升程序的響應(yīng)速度。

四、總結(jié)

很多時(shí)候分析下來(lái)發(fā)現(xiàn)是 第三方組件 拖垮了程序,自己又沒(méi)有太多的介入能力,真的很無(wú)奈,框架都用了那么久,現(xiàn)在看到了一只蒼蠅,已是食之無(wú)味,棄之可惜。

責(zé)任編輯:武曉燕 來(lái)源: 一線碼農(nóng)聊技術(shù)
相關(guān)推薦

2022-10-13 18:40:05

.NETOA后端

2024-07-01 13:00:24

.NET網(wǎng)絡(luò)邊緣計(jì)算

2024-09-14 10:28:56

.NET卡死程序

2022-01-17 21:28:36

管理系統(tǒng).NET

2023-05-15 11:15:50

.NET門診語(yǔ)句

2024-11-29 10:06:59

2023-09-27 07:23:10

.NET監(jiān)控軟件

2024-05-28 10:18:30

WPF程序數(shù)據(jù)

2024-06-06 10:51:15

自動(dòng)化系統(tǒng)推測(cè)

2024-03-28 12:56:36

2023-04-06 10:52:18

2023-06-26 00:12:46

2024-12-27 13:31:18

.NETdump調(diào)試

2024-05-31 12:56:06

.NET代碼方法

2021-10-27 07:30:32

.NETCPU論壇

2022-10-25 14:17:01

.NET代碼程序

2023-06-29 17:55:00

.NET日志WinDbg

2023-10-07 13:28:53

.NET軟件賬本

2024-07-09 11:51:20

Windows線程池源碼

2023-03-26 20:24:50

ERP網(wǎng)站系統(tǒng)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)