最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

Asp dotnet core 記一次應(yīng)用拒絕響應(yīng)調(diào)試 開啟線程等待同步用光線程池

2020-09-27 09:54 作者:朝夕教育  | 我要投稿


https://lindexi.gitee.io:本文來源林德熙的博客

微軟MVP 林德熙:作者

? ? ? 我有一個上古的庫,我使用這個庫用來上報日志,而剛才日志服務(wù)掛了。然后我就發(fā)現(xiàn)了我的應(yīng)用拒絕響應(yīng)了,通過 VisualStudio 斷點調(diào)試可以發(fā)現(xiàn)線程池的線程全部被占用了。因為沒有可用線程因此所有對 asp dotnet core 應(yīng)用的訪問全部都不會收到響應(yīng),為什么我的另一個應(yīng)用日志服務(wù)掛了會讓我的業(yè)務(wù)應(yīng)用拒絕響應(yīng)?為什么我的業(yè)務(wù)應(yīng)用會使用線程池所有的線程,為什么線程池的所有線程被占用將會讓應(yīng)用拒絕響應(yīng)?

? ? ? ?其實很好復(fù)現(xiàn)這個坑,但是在開始復(fù)現(xiàn)之前,需要聊一下背景:

我有一個業(yè)務(wù)應(yīng)用和一個日志服務(wù),基本上可以認(rèn)為日志服務(wù)和業(yè)務(wù)沒有任何關(guān)聯(lián),而且我從上層業(yè)務(wù)調(diào)用可以看到,都是異步使用。而在日志服務(wù)全部掛掉的時候,開始業(yè)務(wù)應(yīng)用還能使用,但是當(dāng)請求大概訪問了 100 次,就發(fā)現(xiàn)后續(xù)的訪問都沒有任何返回。同時在業(yè)務(wù)應(yīng)用的本機控制臺和日志文件里面都沒有任何記錄,而控制臺也沒有收到 50x 等錯誤,也就是業(yè)務(wù)應(yīng)用還在工作,但是沒有任何響應(yīng)。

? ? ? ?我在本地上可以復(fù)現(xiàn),使用VisualStudio開啟所有異常,也什么都沒收到。在應(yīng)用配置文件appsettings.json文件里面將日志配置設(shè)置為Debug也沒有拿到任何有用的信息

? ? ? ?原本每次的請求都會在默認(rèn)的asp dotnet core日志輸出至少一條日志,但是此時什么日志都沒有輸出

? ? ? ?而此時的業(yè)務(wù)應(yīng)用的cpu和內(nèi)存占用都很少,在沒有請求的時候,可以看到cpu幾乎沒有占用

? ? ? 在點擊VisualStudio暫停的時候,可以看到業(yè)務(wù)應(yīng)用創(chuàng)建了大量的線程


? ? ? 其實調(diào)試到線程的時候,大概半個下午了,哈哈

? ? ? 其實我不知道如果一個asp dotnet core應(yīng)用對所有的請求都沒有返回,也沒有報錯的時候可以如何調(diào)試

? ? ? 在看到有大量的線程被創(chuàng)建的時候,此時可以調(diào)試的是打開【調(diào)試->窗口->并行堆?!窟@個工具可以輔助調(diào)查所有線程問題

? ? ? ?如果一個應(yīng)用創(chuàng)建了大量線程,如果這些線程都是通過 Task.Run 創(chuàng)建,那么意味著線程池里面的線程全部都使用了。也就是此時的下一次調(diào)用 Task.Run 需要等待線程池重新分配或創(chuàng)建線程。如果線程池沒有空閑的可以分配需要等待一段時間才能創(chuàng)建新的線程,于是此時的應(yīng)用就會卡住沒有返回值.

? ? ? ?而根據(jù)Eleven老師的asp dotnet core源代碼分析課程可以了解到,在asp dotnet core服務(wù)主機里面的線程是主線程固定的,但是調(diào)用到對應(yīng)的控制器需要通過線程池調(diào)度。

? ? ? ?在用光線程池的線程,此時的請求可以被主機處理,因此不會拋出遠(yuǎn)程服務(wù)器拒絕請求。但是主機通過線程池調(diào)度到對應(yīng)控制器,因為線程池沒有足夠的線程,因此將會進(jìn)入很長的等待。特別是有后續(xù)請求,那么將需要不斷排隊。這就是為什么我看到的業(yè)務(wù)應(yīng)用拒絕服務(wù)

? ? ? ?進(jìn)一步的調(diào)試可以通過并行堆棧找到最多相同的堆棧,也就是有多少線程都在相同的堆棧里,那么證明這部分邏輯有鍋

? ? ? ?我在調(diào)試中看到如下代碼:

? ? ? ?我的底層庫給我的方法是異步的上報日志方法,但是這個日志上報方法的核心是通過Task.Run一個線程進(jìn)行同步調(diào)用

? ? ? ?其實在asp dotnet core的性能優(yōu)化中,要盡量不使用 Task.Run 方法,在 ASP.NET Core Performance Best Practices 官方文檔 和譯文ASP.NET Core性能優(yōu)化最佳實踐 - Newbe36524 - 博客園都有提到,原因還請小伙伴看這兩篇博客

? ? ? ?那么為什么上面的代碼將會讓線程池的線程都在等待?原因是 GetResponse 是一句同步的代碼,同步的代碼等待網(wǎng)絡(luò)的返回,而此時我的日志服務(wù)大概寫了如下代碼


? ? ? ?這是我寫出的最簡單的日志服務(wù)的代碼,這個代碼的所有請求都會進(jìn)入到await? Task.Task;?等待一個不會返回的任務(wù),也就是任何的請求進(jìn)來只能等待超時而剛好上面業(yè)務(wù)應(yīng)用的等待是沒有設(shè)置超時的,在同步的調(diào)用等待一個不會返回的請求,此時的線程就被占用了。

? ? ? ?如果業(yè)務(wù)應(yīng)用對每次請求都需要進(jìn)行如上面的從線程池獲取線程然后進(jìn)行同步訪問,那么線程池的將會被用光。在線程池的線程都被占用的時候,下次調(diào)用 Task.Run 就會先等待一段時間,如果等待一段時間還沒有線程可以調(diào)度,那么此時才會在線程池新建線程。

? ? ? ?所以應(yīng)用如果拒絕響應(yīng),首先需要調(diào)查應(yīng)用是否用光了線程池,然后再調(diào)查連接數(shù)。如果是線程池用光,那么打開并行堆棧,看線程最多的堆棧是什么,然后通過堆棧和源代碼可以找到是否存在鎖或者調(diào)用 IO 同步。

? ? ? ? 如果發(fā)現(xiàn)這個的 asp dotnet core 應(yīng)用的性能不足,因為線程開啟過多,那么此時可以全局找 Task.Run 的代碼,盡可能干掉這部分邏輯。

? ? ? ?本文的坑,可以使用將同步修改為異步的方法解決,換句話說,不需要通過線程池開啟線程的方法,通過IO自帶的異步方法進(jìn)行異步IO請求。此時在IO的異步里面將會自動出讓CPU執(zhí)行,這部分是硬件的支持,因此進(jìn)入異步的 IO 將不會占用線程,線程可以回到線程池給其他業(yè)務(wù)調(diào)用。

? ? ? ?一個可選的方法是將一些不重要但是需要慢慢執(zhí)行的任務(wù)放在生產(chǎn)者消費者隊列里面,如果這部分任務(wù)很小,可以嘗試使用我的AsyncQueue 高性能低資源占用的類。


本文轉(zhuǎn)自博客


Asp dotnet core 記一次應(yīng)用拒絕響應(yīng)調(diào)試 開啟線程等待同步用光線程池的評論 (共 條)

分享到微博請遵守國家法律
渝北区| 苗栗县| 原阳县| 栖霞市| 宣武区| 盐边县| 略阳县| 天气| 闽侯县| 鄂伦春自治旗| 礼泉县| 张家川| 同江市| 江陵县| 兴文县| 如皋市| 保山市| 库尔勒市| 望江县| 开江县| 上思县| 菏泽市| 静安区| 临泉县| 壤塘县| 沭阳县| 靖安县| 保康县| 闽清县| 汤阴县| 黔江区| 巩留县| 都昌县| 锦州市| 淮安市| 昆山市| 长春市| 凤凰县| 达尔| 金华市| 明星|