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

游戏卡顿白皮书 › 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 调用)等待,看“锁竞争”“游戏线程中的同步调用”
确认手段
运维工具即可确认(无需游戏代码)

出处

  1. send(2) — Linux manual page Linux man-pages
    发送缓冲区没有空间时 send() 阻塞,非阻塞模式下立即返回 EAGAIN
  2. send function (winsock2.h) Microsoft
    Winsock 也一样,缓冲区没有空间时,只要不是非阻塞模式,send 就会阻塞
  3. net/ipv4/tcp_diag.c (Linux v6.12) Linux kernel
    ss 的 Recv-Q、Send-Q 值:监听 socket 上是等待 accept 的连接数和 backlog 上限,已连接 socket 上是应用尚未读取的字节数和已发送但尚未收到 ACK 的字节数

相关原因

同一层:L8 Socket 与协议

其他层中同样导致“卡住”的原因

查看含图示和实验的原卡片