한국어English日本語简体中文繁體中文DeutschไทยTiếng ViệtРусскийPortuguês (Brasil)EspañolBahasa Indonesia

遊戲 Lag 白皮書 › L9 伺服器遊戲程式

執行緒池耗盡 Thread pool starvation

原因 ID sp-threadpool · 主要負責 遊戲開發團隊(伺服器開發)

在含圖解與實驗的完整版中開啟卡片 →

負責處理工作的工作執行緒全被慢的工作綁住時,新請求只能無止盡地等待。

為什麼 工作執行緒都被綁在等待外部 API 或 DB 回應上 → 於是 沒有執行緒可以接新請求 → 畫面上 登入、商城等特定功能出現無限讀取

症狀
連不上/無限讀取, 輸入延遲, 定格
因素
停滯
誰會遇到
只有特定功能, 整個伺服器
何時
人潮湧入時, 剛登入/維護剛結束
負責單位
主要負責 遊戲開發團隊(伺服器開發)
遊戲開發團隊要做的事
為慢的呼叫設逾時、依功能分開執行緒池、改為非同步。
圖表上
碰到上限後持平 · 執行緒池的執行緒數與佇列長度、請求處理時間
查看位置
.NET 看 dotnet-counters monitor 的執行緒池執行緒數與佇列長度(.NET 9 以後為 dotnet.thread_pool.thread.count、dotnet.thread_pool.queue.length,8 以前為 ThreadPool Thread Count、ThreadPool Queue Length),並用 dotnet-stack 確認工作執行緒在哪裡等待。JVM 與原生伺服器則用 thread dump 確認同樣的內容
符合的跡象
CPU 使用率遠低於 100%,執行緒數卻緩慢持續增加或卡在上限,佇列不斷累積,大部分 worker 都在等同一個外部呼叫(DB、HTTP)的回應
不符合的跡象
佇列是空的卻仍然很慢時,是被呼叫的對象本身慢,要查連鎖故障或依賴外部服務
確認方式
用基礎設施工具確認(不需要遊戲程式碼)
深入了解
若封包接收與遊戲邏輯共用同一個工作執行緒池,只要幾個慢的工作占滿所有 worker,整台伺服器的封包處理就會停住。
實際案例
Riot Games 2021: League of Legends EUW 5 小時事故:一個附屬 DB 讓整個伺服器停擺

出處

  1. Debug ThreadPool Starvation Microsoft
    池中沒有剩餘執行緒、新工作只能等待時,回應就會變慢,原因是占住執行緒的阻塞程式碼。在 dotnet-counters 中,CPU 遠低於 100%,dotnet.thread_pool.thread.count 卻緩慢持續增加,就是耗盡的訊號(dotnet.thread_pool.queue.length 通常也很大);用 dotnet-stack 確認執行緒等待的位置
  2. Avoiding insurmountable queue backlogs AWS
    同時處理數 = 抵達率 × 延遲(利特爾法則,Little's law)。每秒 100 筆時,延遲從 100ms 增加到 10 秒,執行緒就從 10 個增加到 1,000 個,池因此耗盡
  3. Bulkhead Pattern Microsoft Azure
    為每個被呼叫的對象分別建立連線池與執行緒池,一個對象故障時只會卡住它自己的池
  4. .NET runtime metrics .NET
    dotnet.thread_pool.thread.count(執行緒池執行緒數)、dotnet.thread_pool.queue.length(等待中的工作數)自 .NET 9 起提供
  5. Well-known EventCounters in .NET Microsoft
    .NET 8 以前的 ThreadPool Thread Count(threadpool-thread-count)、ThreadPool Queue Length(threadpool-queue-length)

相關原因

同一層:L9 伺服器遊戲程式

同一症狀(連不上/無限讀取)在其他層的原因

查看含圖解與實驗的完整版卡片