當請求通過負載平衡器發送並且響應太大而無法放入單個數據包時,為什麼我沒有收到響應?
在某些情況下,我無法從 HTTP 請求接收到對特定服務的響應:請求是通過負載平衡器發送的,並且響應太大而無法放入單個數據包中。在這些條件下,永遠不會收到響應。兩者都不是錯誤。伺服器發送一個 TCP ACK 來響應包含 HTTP 請求的數據包,但之後什麼都沒有。連接只是掛起等待發生的事情。無論請求是通過 CURL、節點的 HTTP 庫還是 Postman 發送的,都會觀察到該行為。
什麼可能導致這種行為?如何調試導致問題的原因?
- 我已經驗證負載均衡器後面的服務正在接收並響應 HTTP 請求,但是響應在某處失去了。
- 我們還有其他類似設置的服務不會出現此問題。
- 我們嘗試設置負載均衡器的不同實例,但它有同樣的問題。
在伺服器上的 tshark 中,
[TCP Retransmission]
在它通過負載均衡器接收到一個有問題的請求後,我看到了一些條目(當直接接收到相同的請求時不會發生這種情況)。這似乎表明客戶端和伺服器之間的某些東西誤導了 TCP 流量(高度暗示負載平衡器),但我不明白只有當 HTTP 響應被拆分為多個數據包時才會導致這種情況發生,而且只有這個特定的服務。導致失敗的條件:
網路拓撲結構:
Client -> Load Balancer -> Service
原來我發現了一個PMTUD 黑洞。
由於 Cloudflare 的一篇關於數據包碎片的文章,我首先懷疑存在碎片問題。我確認了使用 ping 發送不同大小的數據包的問題:
ping -s SIZE
.首先,我嘗試了 1500,這是乙太網的預設 MTU。
> ping 172.16.5.1 -s 1500
但是什麼也沒發生。它只是掛起並沒有列印任何內容。如果出現問題,您應該會收到如下錯誤:
ping: sendto: Message too long Request timeout for icmp_seq 0
最終,我找到了一篇更詳細地描述了這個問題的文章,並推薦了一個修復方法:設置
/proc/sys/net/ipv4/tcp_mtu_probing
為1
啟用黑洞發現(Linux 中預設情況下不啟用此功能🤷♂️)。我實際上無法修改該文件,因此我按照建議進行了設置,/etc/sysctl.conf
然後通過sysctl --system
. 畢竟,我能夠收到來自伺服器的響應。MTU 探測增加了響應的成本(明顯延遲),所以我仍然想弄清楚為什麼沒有收到 ICMP 數據包,但至少我現在理解了這個問題並有了一個可行的解決方案。
**更新:**原來 LAG 的 MTU 為 1500,因此它正在丟棄數據包。