游戏卡顿白皮书 › L7 服务器操作系统(内核)
连接队列(backlog)溢出 Listen backlog / SYN queue overflow
原因 ID so-backlog · 主责 研发团队·服务器开发 · 配合 运维团队·系统运维, 研发团队·客户端开发
在含图示和实验的完整版中打开此卡片 →
维护结束后数万人同时连接时,内核的连接队列(backlog)会溢出,连接请求被丢弃。
起因 维护一结束,连接涌入的速度就超过了游戏服务器用 accept 处理连接的速度 → 结果 内核的连接队列(backlog,取服务器代码传给 listen 的值与内核上限中较小的一个)已满 → 画面表现 连接请求被丢弃,客户端反复重试,表现为连不上/无限加载
- 症状
- 连不上/无限加载
- 因素
- 丢包
- 谁会遇到
- 全服
- 何时出现
- 刚登录/维护结束后
- 负责方
- 主责 研发团队·服务器开发 · 配合 运维团队·系统运维, 研发团队·客户端开发
- 研发团队要做的事
- 服务器:调大代码里传给 listen 的值;不要让接受连接的线程因为其他工作而停住;做登录排队系统。客户端:拉长重试间隔(随机打散)。
- 运维团队要做的事
- 调大内核 somaxconn(要和服务器代码里的 listen 值一起调大才有效);保持 SYN Cookie 开启;监控溢出次数(nstat 的 TcpExtListenOverflows)。
- 数值参考
- Linux 内核上限(somaxconn)从 5.4 起默认为 4,096(之前为 128),但服务器代码传给 listen 的值更小时,以这个值为上限。Linux 在队列满时会不报任何错误,悄悄丢弃连接请求。客户端操作系统会从 1 秒后开始重发几次,所以玩家看到的往往是长时间加载,很少直接出现“连接失败”。Windows 服务器则会回复拒绝,客户端马上就会看到“连接失败”。
- 监控图上
- 开服/维护后激增 · 连接队列溢出次数(ListenOverflows)、连接请求数
- 查看位置
- 看 nstat -az 中 TcpExtListenOverflows、TcpExtListenDrops 的增量,用 ss -ltn 对比监听 socket 的 Recv-Q(等待 accept 的连接数)和 Send-Q(backlog 上限)
- 确认依据
- 连接集中涌入的时刻 ListenOverflows 增加,监听 socket 的 Recv-Q 贴着 Send-Q 的值
- 排除依据
- ListenOverflows 没有变化则不是这个原因。连接已经建立但加载一直不结束,看“登录激增与 N+1 查询”;人数一到某个固定值就进不来,看“文件描述符上限”
- 确认手段
- 运维工具即可确认(无需游戏代码)
出处
- listen(2) — Linux manual page Linux man-pages
listen 的 backlog 超过 somaxconn 时会被悄悄截断;somaxconn 默认 4,096(从 5.4 起,之前为 128);队列满时可以忽略请求,交给客户端重试 - IP Sysctl Linux kernel
tcp_syn_retries:连接请求(SYN)首次重传等待 1 秒,会重发多次;tcp_abort_on_overflow 默认关闭(溢出时也不回复拒绝);tcp_syncookies 默认开启 - listen function (winsock2.h) Microsoft
Windows 在队列满时,客户端会收到 WSAECONNREFUSED 错误 - SNMP counter Linux kernel
TcpExtListenOverflows:因 accept 队列已满而丢弃连接请求(SYN)的次数,此时 TcpExtListenDrops 也会一起增加 - net/ipv4/tcp_diag.c (Linux v6.12) Linux kernel
ss 的 Recv-Q、Send-Q 值:监听 socket 上是等待 accept 的连接数和 backlog 上限,已连接 socket 上是应用尚未读取的字节数和已发送但尚未收到 ACK 的字节数
相关原因
同一层:L7 服务器操作系统(内核)
其他层中同样导致“连不上/无限加载”的原因
查看含图示和实验的原卡片