遊戲 Lag 白皮書 › L7 伺服器 OS(kernel)
Kernel socket 緩衝區不足 Small socket buffers
原因 ID so-sockbuf · 主要負責 基礎設施團隊(伺服器基礎設施) · 協同 遊戲開發團隊(伺服器開發)
在含圖解與實驗的完整版中開啟卡片 →
傳送與接收緩衝區太小時,一遇到突發流量,UDP 收到的封包就會被丟棄,TCP 則因緩衝區沒有空間而無法傳送。
為什麼 SO_SNDBUF、SO_RCVBUF 維持預設值或設得太小 → 於是 突發流量或接收執行緒短暫停住時,UDP 接收緩衝區溢位而丟棄封包;TCP 則因傳送緩衝區沒有空間而等待 → 畫面上 瞬移(UDP 遺失)或快轉(TCP 等待)
- 症狀
- 瞬移, 快轉
- 因素
- 遺失, 停滯
- 誰會遇到
- 整個伺服器
- 何時
- 人潮湧入時
- 負責單位
- 主要負責 基礎設施團隊(伺服器基礎設施) · 協同 遊戲開發團隊(伺服器開發)
- 遊戲開發團隊要做的事
- 在程式碼中依流量設定緩衝區大小(SO_SNDBUF、SO_RCVBUF);注意 TCP 一旦直接指定大小,Linux 的自動調整(autotuning)就會關閉;設得太大會讓舊資料堆在緩衝區裡、延遲增加,所以大小要適中;避免接收執行緒停住。
- 基礎設施團隊要做的事
- 調整 kernel 上限(rmem_max、wmem_max,程式碼設定的緩衝區大小也不能超過這個值)與預設值(rmem_default),監控緩衝區溢位計數器(RcvbufErrors)。
- 數值參考
- Linux 的 UDP 接收緩衝區預設值約 208KB。即使是小封包,每個在 kernel 記憶體中占用的空間也遠大於實際大小,數十~數百個就會塞滿。每秒接收 10 萬個封包的伺服器,接收執行緒只要停住幾 ms 就會溢位。
- 圖表上
- 偶爾隨機飆高 · UDP 接收緩衝區溢位(UdpRcvbufErrors)
- 查看位置
- 看 nstat -az 的 UdpRcvbufErrors 增加量與 ss -uamn 的 skmem(rb 是接收緩衝區大小,d 是無法放進 socket 而丟棄的封包數);TCP 則看 ss -tm 的 skmem 中,傳送等待記憶體(w)是否碰到傳送緩衝區大小(tb)
- 符合的跡象
- 突發流量或接收執行緒停住的時間點 UdpRcvbufErrors(或 socket 的 d)增加,rb 在預設值(約 208KB)附近。TCP 則是 w 貼齊 tb,send 卡住
- 不符合的跡象
- 計數器沒有變化卻有遺失時,問題在 NIC 階段(「Ring buffer 不足」)或網路區段
- 確認方式
- 用基礎設施工具確認(不需要遊戲程式碼)
出處
- socket(7) — Linux manual page Linux man-pages
SO_RCVBUF、SO_SNDBUF 的預設值為 rmem_default、wmem_default,上限為 rmem_max、wmem_max,設定值會被 kernel 加倍 - include/net/sock.h (Linux v6.18) Linux kernel
預設 socket 緩衝區定義為含 sk_buff 額外開銷的 256 位元組封包 256 個份(SKB_TRUESIZE(256)×256);小訊框也以 sk_buff+MTU 計算(約 208KB 是 x86-64 上的計算值) - IP Sysctl Linux kernel
tcp_rmem、tcp_wmem:直接設定 SO_RCVBUF、SO_SNDBUF 時,該 socket 的自動調整會關閉 - net/ipv4/udp.c (Linux v6.12) Linux kernel
UDP 接收佇列超過 socket 緩衝區大小時會直接丟棄,並增加 RcvbufErrors - net/ipv4/proc.c (Linux v6.12) Linux kernel
nstat 顯示的計數器名稱:Udp 群組的 RcvbufErrors、SndbufErrors - ss(8) — Linux manual page iproute2
-m 的 skmem:rb 接收緩衝區大小,tb 傳送緩衝區大小,w 傳送等待記憶體,d 放進 socket 前就丟棄的封包數
相關原因
同一層:L7 伺服器 OS(kernel)
同一症狀(瞬移)在其他層的原因
查看含圖解與實驗的完整版卡片