游戏卡顿白皮书 › TCP 重传的根本原因
零窗口(看起来像重传的停顿) Zero window, often mistaken for retransmission
原因 ID rt-zero-window · 主责 研发团队·客户端开发 · 配合 研发团队·服务器开发, 运维团队·系统运维
在含图示和实验的完整版中打开此卡片 →
接收方程序没有及时读取 socket,缓冲区满了之后,发送方就会停止发送,只发零窗口探测。这与线路无关。
起因 客户端帧停住或服务器线程阻塞,读不了 socket → 结果 接收窗口变为 0,发送方停止发送,只发探测(间隔越来越长) → 画面表现 卡住后快进。抓包中能看到“ZeroWindow”,没有丢包
- 症状
- 卡住, 快进
- 因素
- 停顿
- 谁会遇到
- 只有我, 全服
- 何时出现
- 人多的时候, 偶尔随机
- 负责方
- 主责 研发团队·客户端开发 · 配合 研发团队·服务器开发, 运维团队·系统运维
- 研发团队要做的事
- 先在抓包中确认是哪一方发出 ZeroWindow(读不了 socket 的一方);网络接收放在独立线程中持续读取;接收缓冲区设为合适大小。客户端:解决加载、GC 等导致帧停住的原因。服务器:解决读 socket 的线程被阻塞的原因。
- 运维团队要做的事
- 把服务器 nstat 的 TcpExtTCPToZeroWindowAdv(服务器通告接收窗口为 0 的次数)加入监控(增加说明问题在服务器侧,转交服务器开发);提供服务器侧抓包。
- 监控图上
- 断流后集中到达 · 每个连接的接收量,零窗口次数
- 查看位置
- 在抓包中用 Wireshark 过滤条件 tcp.analysis.zero_window 找出通告窗口为 0 的一方。服务器 nstat 中分别看 TcpExtTCPToZeroWindowAdv(服务器通告窗口为 0)和 TcpExtTCPWinProbe(对方窗口为 0 时发送探测),并看服务器 socket 的 Recv-Q(ss 中程序尚未读取的字节)
- 确认依据
- 停顿期间没有重传,只有零窗口和探测在往来。服务器的 TcpExtTCPToZeroWindowAdv 或服务器 socket 的 Recv-Q 增加,说明服务器没能及时读取;TcpExtTCPWinProbe 增加,说明客户端没能及时读取
- 排除依据
- 抓包中没有零窗口,且同样的数据被重新发送,看丢包或虚假重传方面的原因
- 确认手段
- 运维工具即可确认(无需游戏代码)
- 真实案例
- Roblox 2021: Roblox 73 小时故障:服务发现(Consul)集群的争用问题
出处
- RFC 9293: Transmission Control Protocol (TCP) IETF
接收窗口为 0 时,发送方发送零窗口探测,探测间隔按指数增长 - 7.5. TCP Analysis Wireshark
TCP ZeroWindow:接收方通告窗口为 0、让发送方停止发送的数据包 - Display Filter Reference: Transmission Control Protocol Wireshark
tcp.analysis.zero_window、tcp.analysis.zero_window_probe 显示过滤器 - SNMP counter Linux kernel
TcpExtTCPToZeroWindowAdv:接收窗口从非 0 值通告为 0 的次数 - net/ipv4/proc.c Linux kernel
nstat 中的计数器名 TCPToZeroWindowAdv、TCPWinProbe - net/ipv4/tcp_output.c Linux kernel
TCPWinProbe:对方接收窗口为 0 时,每发送一次探测(tcp_send_probe0)就加一 - net/ipv4/tcp_diag.c Linux kernel
对已建立的连接而言,ss 的 Recv-Q 是已收到但程序尚未读取的字节数
相关原因
同一层:TCP 重传的根本原因
其他层中同样导致“卡住”的原因
查看含图示和实验的原卡片