游戏卡顿白皮书 › 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、原生服务器用线程 dump 确认同样的内容
- 确认依据
- CPU 使用率远低于 100%,线程数却持续缓慢增加或顶在上限,队列堆积,大部分工作线程在等同一个外部调用(DB、HTTP)的响应
- 排除依据
- 队列是空的仍然慢,说明被调用方本身慢,看“级联故障”或“依赖外部服务”
- 确认手段
- 运维工具即可确认(无需游戏代码)
- 深入了解
- 如果接收数据包和游戏逻辑共用同一个工作线程池,几个慢操作占满所有工作线程的那一刻,整台服务器的数据包处理都会停住。
- 真实案例
- Riot Games 2021: League of Legends EUW 5 小时故障:一个辅助 DB 导致全服停摆
出处
- Debug ThreadPool Starvation Microsoft
池里没有剩余线程、新任务排队时响应会变慢,原因是占住线程的阻塞代码。在 dotnet-counters 中,CPU 远低于 100% 而 dotnet.thread_pool.thread.count 持续缓慢增加,就是耗尽的信号(dotnet.thread_pool.queue.length 往往也很大);用 dotnet-stack 确认线程在哪里等待 - Avoiding insurmountable queue backlogs AWS
并发处理数 = 到达率 × 延迟(利特尔法则)。每秒 100 个请求时,延迟从 100 ms 增至 10 秒,线程就从 10 个增至 1,000 个,线程池耗尽 - Bulkhead Pattern Microsoft Azure
每个被调用方单独设连接池、线程池,一个被调用方故障只会堵住它自己的池 - .NET runtime metrics .NET
dotnet.thread_pool.thread.count(线程池线程数)、dotnet.thread_pool.queue.length(排队的任务数)从 .NET 9 起提供 - Well-known EventCounters in .NET Microsoft
.NET 8 及以前的 ThreadPool Thread Count(threadpool-thread-count)、ThreadPool Queue Length(threadpool-queue-length)
相关原因
同一层:L9 服务器游戏进程
其他层中同样导致“连不上/无限加载”的原因
查看含图示和实验的原卡片