近期在協助客戶排查問題時,發現一種詭異的現象:客戶端發起的 TCP 連線請求(SYN 封包)明明已到達服務端,但服務端卻始終不回應 SYN-ACK。經確認,服務端的連線數並未達上限,排除了連線佇列滿載的可能。靈光一閃,想起 net.ipv4.tcp_tw_recycle 這個核心參數可能是禍首,關閉後問題隨即解決。本文將深入剖析背後原理。
為了加速 TIME_WAIT 狀態的連線回收,避免大量佔用資源,許多伺服器會開啟 net.ipv4.tcp_tw_recycle=1。此功能依賴 net.ipv4.tcp_timestamps(預設開啟)提供的時間戳選項。根據 RFC 1323,啟用後,核心會記錄每個遠端主機最後一個到達封包的時間戳。若同一 IP 的新封包時間戳小於記錄值,即判定為無效並直接丟棄。
現代架構常見 LVS 或 Nginx 等四層負載均衡,後端伺服器看到的來源 IP 全是 LB 的地址。由於不同客戶端的系統時間不一致,經過 NAT 後,時間戳在後端看來便出現「亂序」。後端核心誤以為是舊連線的重傳或無效封包,直接丟棄了正常的 SYN,導致客戶端連線超時。
netstat -s | grep timestamp
# 輸出中若見到:
# packets rejects in established connections because of timestamp
# 即證實是時間戳檢查導致丟包。
sysctl -w net.ipv4.tcp_tw_recycle=0,永久生效請寫入 /etc/sysctl.conf。TIME_WAIT 過多,可開啟 net.ipv4.tcp_tw_reuse=1(僅對客戶端發起的連線有效)。tcp_timestamps,但會失去 PAWS(防序號繞回)等保護,不建議。核心結論:處於 NAT/負載均衡後端的伺服器,嚴禁開啟 tcp_tw_recycle,否則會導致正常連線請求被靜默丟棄,極難排查。