遊戲 Lag 白皮書 › TCP 重傳的根本原因
MTU 黑洞(只有大封包反覆遺失) PMTU black hole
原因 ID rt-mtu · 主要負責 基礎設施團隊(網路基礎設施) · 協同 基礎設施團隊(伺服器基礎設施), 遊戲開發團隊(伺服器開發)
在含圖解與實驗的完整版中開啟卡片 →
中途區段能接收的大小變小,而「封包太大」的通知(ICMP)又被擋掉時,大封包不管重送幾次都會一直消失。
為什麼 VPN 或通道區段的最大大小變小,大小超過的通知被防火牆擋掉 → 於是 傳送端不知道原因,持續重傳同一個大封包,RTO 每次加倍 → 畫面上 平常一切正常,但在背包、人多的地方、進場載入這類有大量資料往來的瞬間,連後面的小封包也全部卡住而定格,最後斷線或無限讀取
- 症狀
- 定格, 斷線, 連不上/無限讀取
- 因素
- 遺失
- 誰會遇到
- 特定地區/電信業者, 只有我
- 何時
- 做特定動作時, 剛登入/維護剛結束
- 負責單位
- 主要負責 基礎設施團隊(網路基礎設施) · 協同 基礎設施團隊(伺服器基礎設施), 遊戲開發團隊(伺服器開發)
- 遊戲開發團隊要做的事
- 要從伺服器端直接調低,就設定 socket 的最大區段大小(TCP_MAXSEG);只在遊戲程式碼中把訊息切小並不能避免(TCP 會把要送的資料重新依 MSS 大小打包)。
- 基礎設施團隊要做的事
- 網路:在邊界設備做 MSS 調整(clamping)、在防火牆與雲端網路 ACL 允許大小超過的 ICMP(類型 3 代碼 4,fragmentation needed)。伺服器設備/OS:設定路徑 MTU、確認伺服器防火牆與雲端安全群組也沒有擋掉大小超過的 ICMP、以 Linux tcp_mtu_probing=1 作為最後一道防線。
- 數值參考
- 通常是 1,500 位元組,經過通道後為 1,400 左右。同一個封包重傳 5~6 次,定格就會超過 10 秒。
- 圖表上
- 只有部分偏高 · 各連線的 RTO 與 backoff、各地區/電信業者的斷線次數
- 查看位置
- 用伺服器端的封包擷取或 bcc tcpretrans -s(顯示序號)查看問題連線的重傳,並用 ss -ti 查看該連線的 mss、pmtu、backoff。從伺服器對該玩家位址分別送出小 ping 與開啟 DF 的 1,500 位元組 ping(ping -M do -s 1472)比較
- 符合的跡象
- 塞滿 MSS 的封包以相同序號持續重傳,間隔每次加倍,比它小的封包則能通過。沒有收到大小超過的 ICMP(Wireshark 篩選條件 icmp.type == 3 and icmp.code == 4),小 ping 有回應,只有大的 DF ping 沒有回應就消失
- 不符合的跡象
- 小封包也一起消失時,是與大小無關的遺失(「瓶頸佇列溢位」、「路由變更、ECMP 不良路徑」)。收到大小超過的 ICMP 且 ss -ti 的 pmtu 變小時,代表路徑 MTU 探索正常運作
- 確認方式
- 用基礎設施工具確認(不需要遊戲程式碼)
- 深入了解
- tcp_mtu_probing=1 要等重傳逾時持續幾秒(相當於 tcp_retries1=3)之後,才會判定為黑洞,並把 MSS 降到 1,024 位元組。這段期間連線會停住,所以只把它當作最後一道防線,優先採用事先預防的 MSS 調整。
出處
- RFC 1191: Path MTU discovery IETF
路徑 MTU 探索:過大的封包會以 ICMP「fragmentation needed and DF set」(類型 3 代碼 4)通知 - RFC 2923: TCP Problems with Path MTU Discovery IETF
ICMP 被擋,導致只有大封包一直消失的 PMTU 黑洞問題 - RFC 4821: Packetization Layer Path MTU Discovery IETF
不靠 ICMP、由傳輸層探索封包大小的方法(Linux tcp_mtu_probing 的基礎) - IP Sysctl Linux kernel
tcp_mtu_probing:0 為關閉,1 為只在偵測到黑洞時開啟,2 為一律開啟(起始 MSS 為 tcp_base_mss)。tcp_retries1 預設 3 - net/ipv4/tcp_timer.c Linux kernel
RTO 重傳持續 tcp_retries1 次時,視為偵測到黑洞,開啟 MTU 探索並調低 MSS - include/net/tcp.h Linux kernel
TCP_BASE_MSS = 1,024 位元組 - RFC 6298: Computing TCP's Retransmission Timer IETF
每次重傳計時器到期就把 RTO 加倍 - iptables-extensions(8) — Linux manual page netfilter
TCPMSS --clamp-mss-to-pmtu:調整 SYN 中的 MSS,避開中途區段擋掉 ICMP 而讓大封包卡住的問題 - tcp(7) — Linux manual page Linux man-pages
TCP_MAXSEG:送出封包的最大區段大小,在連線前設定時,告知對方的 MSS 也會跟著改變 - Network maximum transmission unit (MTU) for your EC2 instance AWS
網際網路閘道與 VPN 的 MTU 為 1,500,PMTUD 需要 ICMP 類型 3 代碼 4,被安全群組或網路 ACL 擋掉就收不到 - MTU considerations | Cloud VPN Google Cloud
Cloud VPN 閘道 MTU 為 1,460 位元組,IPv4 通道的酬載 MTU 為 1,406 位元組(經過通道後為 1,400 左右) - Demonstrations of tcpretrans, the Linux eBPF/bcc version IO Visor
每次重傳顯示一行,-s 會一併顯示重傳封包的序號 - ss(8) — Linux manual page iproute2
ss -i 的 mss、pmtu(路徑 MTU)、backoff(RTO 加倍的次數) - ping(8) — Linux manual page iputils
-M do 會開啟 DF,不送出大於 kernel 所知路徑 MTU 的封包;-s 為資料大小(另加 ICMP 標頭 8 位元組) - Display Filter Reference: Internet Control Message Protocol Wireshark
icmp.type、icmp.code 顯示篩選條件
相關原因
同一層:TCP 重傳的根本原因
同一症狀(定格)在其他層的原因
查看含圖解與實驗的完整版卡片