遊戲 Lag 白皮書 › L7 伺服器 OS(kernel)
連線等待佇列(backlog)溢位 Listen backlog / SYN queue overflow
原因 ID so-backlog · 主要負責 遊戲開發團隊(伺服器開發) · 協同 基礎設施團隊(伺服器基礎設施), 遊戲開發團隊(用戶端開發)
在含圖解與實驗的完整版中開啟卡片 →
維護剛結束時數萬人同時連線,kernel 的連線等待佇列(backlog)會溢位,連線請求因此被丟棄。
為什麼 維護一結束,連線湧入的速度就超過遊戲伺服器用 accept 處理連線的速度 → 於是 kernel 的連線等待佇列(backlog,取伺服器程式碼傳給 listen 的值與 kernel 上限中較小者)已滿 → 畫面上 連線請求被丟棄後不斷重試,結果連不上/無限讀取
- 症狀
- 連不上/無限讀取
- 因素
- 遺失
- 誰會遇到
- 整個伺服器
- 何時
- 剛登入/維護剛結束
- 負責單位
- 主要負責 遊戲開發團隊(伺服器開發) · 協同 基礎設施團隊(伺服器基礎設施), 遊戲開發團隊(用戶端開發)
- 遊戲開發團隊要做的事
- 伺服器:調高程式碼傳給 listen 的值、避免負責接受連線的執行緒被其他工作卡住、導入登入排隊系統。用戶端:拉長重試間隔(加上隨機值分散重試)。
- 基礎設施團隊要做的事
- 調高 kernel 的 somaxconn(要和伺服器程式碼的 listen 值一起調高才有效)、保持 SYN cookie 開啟、監控溢位次數(nstat 的 TcpExtListenOverflows)。
- 數值參考
- Linux kernel 的上限(somaxconn)從 5.4 起預設為 4,096(之前是 128),但伺服器程式碼傳給 listen 的值較小時,就以該值為上限。佇列滿了之後,Linux 會默默丟棄連線請求,不回傳任何錯誤。用戶端 OS 會從 1 秒後開始重送幾次,所以玩家看到的是遲遲跑不完的讀取畫面,很少直接看到「連線失敗」。Windows 伺服器則會回傳拒絕回應,用戶端馬上就會看到「連線失敗」。
- 圖表上
- 剛開服或維護結束後暴增 · 連線等待佇列溢位次數(ListenOverflows)、連線嘗試次數
- 查看位置
- 看 nstat -az 的 TcpExtListenOverflows、TcpExtListenDrops 增加量,並用 ss -ltn 比較 listen socket 的 Recv-Q(等待 accept 的連線數)與 Send-Q(backlog 上限)
- 符合的跡象
- 連線湧入的時間點 ListenOverflows 增加,listen 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 值:listen socket 是等待 accept 的連線數與 backlog 上限;已連線的 socket 是應用程式尚未讀取的位元組數與尚未收到 ACK 的已送出位元組數
相關原因
同一層:L7 伺服器 OS(kernel)
同一症狀(連不上/無限讀取)在其他層的原因
查看含圖解與實驗的完整版卡片