游戏卡顿白皮书 › L8 Socket 与协议
慢客户端导致的阻塞发送 Blocking send on a full socket
原因 ID sk-block-send · 主责 研发团队·服务器开发
在含图示和实验的完整版中打开此卡片 →
某个线路慢的玩家发送缓冲区已满时,如果用阻塞方式(缓冲区腾出空间之前调用不返回的发送)发送,服务器线程就会一直等这一个人。
起因 慢客户端的发送缓冲区已满 → 结果 阻塞发送,服务器线程一直等到缓冲区腾出空间 → 画面表现 这个线程负责的所有人都卡住、慢动作
- 症状
- 卡住, 慢动作
- 因素
- 停顿
- 谁会遇到
- 特定地点/分线, 全服
- 何时出现
- 偶尔随机, 人多的时候
- 负责方
- 主责 研发团队·服务器开发
- 研发团队要做的事
- 非阻塞发送;给每个客户端的发送队列设上限;丢弃过时的更新。
- 监控图上
- 偶发随机尖峰 · 服务器 tick 耗时、每个连接的 Send-Q
- 查看位置
- 用 ss -tn 找出 Send-Q(未收到 ACK 或尚未发出的字节)塞满发送缓冲区的连接,在 tick 冲高的瞬间查看游戏服务器的线程 dump(堆栈),看有没有线程停在 send 调用上
- 确认依据
- 有 Send-Q 塞满的慢连接时,负责该连接的线程停在 send 上,而且只有同一线程负责的人一起卡住
- 排除依据
- 停住的线程在 send 之外(锁、DB 调用)等待,看“锁竞争”“游戏线程中的同步调用”
- 确认手段
- 运维工具即可确认(无需游戏代码)
出处
- send(2) — Linux manual page Linux man-pages
发送缓冲区没有空间时 send() 阻塞,非阻塞模式下立即返回 EAGAIN - send function (winsock2.h) Microsoft
Winsock 也一样,缓冲区没有空间时,只要不是非阻塞模式,send 就会阻塞 - net/ipv4/tcp_diag.c (Linux v6.12) Linux kernel
ss 的 Recv-Q、Send-Q 值:监听 socket 上是等待 accept 的连接数和 backlog 上限,已连接 socket 上是应用尚未读取的字节数和已发送但尚未收到 ACK 的字节数
相关原因
同一层:L8 Socket 与协议
其他层中同样导致“卡住”的原因
查看含图示和实验的原卡片