背景:
昨天調程序的時候遇到了一個 BUG,前端無法將文件正確傳給后端,后端報錯 EOFException(EOF 代表 End Of File)就是在程序嘗試從一個數據流中讀取數據時,發現已經到達了數據流的末尾,但它卻期望還能繼續讀取更多數據時拋出的異常。
分析概括為:前端與后端建立了 TCP 連接,后端 tomcat 要從這個連接中讀取信息,但一直沒有讀到就報這個錯誤了。
問題的起因令我發笑,分析了一堆代碼,卻忘了我前端服務器帶寬 1Mbps,后端服務器帶寬 3Mbps,導致我的整個 TCP 鏈路帶寬只有 1Mbps,請求被限流,而后端 tomcat 在建立 TCP 連接后就一直嘗試從連接中獲取信息,遲遲不來就報 EOFException。
那就借此機會分析一下云廠商的服務器是如何實現帶寬限制的吧
-
請求到達 nginx 反向代理獲取目標地址會向目標地址(IP+端口)發送一個新的 TCP 鏈接,這個過程會經過 3 次握手,然后后端服務器會創建一個對應的 socket,這時候后端服務(tomcat)就進入了“備戰狀態”,準備接受和處理數據,且 tomcat 是知道被調用的接口是哪一個,信息在請求的 URI 中。
-
nginx 將文件分片數據通過建立的連接發送給后端服務器,圖中會經過很多基礎設施:邊緣路由器、交換機、虛擬網絡設備。
-
流量控制具體實現:
云廠商通常采用 流量整形 (Traffic Shaping) 或 流量控制 (Traffic Control) 的技術來實現對云服務器帶寬的限制。這就像在您的服務器網卡前面放了一個“水龍頭”,控制著流向服務器的數據流量的最大速度。
這個“水龍頭”通常部署在云廠商的網絡設備上,例如:
邊緣路由器或交換機: 位于云廠商數據中心網絡的邊緣,靠近您的云服務器實例。
虛擬網絡設備: 在虛擬化環境中,可能存在一個虛擬的網絡設備層,負責管理虛擬機實例的網絡流量。
帶寬限制邏輯出現的時間點:
帶寬限制的邏輯并不是在您的云服務器實例與外部建立 TCP 連接時才“出現”的。它是一個 持續存在且作用于所有進出該云服務器實例的流量 的限制。
更準確地說,當外部網絡的數據包到達云廠商的網絡基礎設施,并且目標是您的 3Mbps 帶寬的云服務器實例時,帶寬限制的邏輯就會開始工作。
具體的工作流程(簡化版):
外部數據包到達云廠商網絡: 外部網絡(例如互聯網)的數據包通過云廠商的骨干網絡傳輸,最終到達離您的云服務器實例最近的云廠商網絡設備。
識別目標服務器: 云廠商的網絡設備根據數據包的目標 IP 地址(您的云服務器公網 IP)識別出該數據包是發往您的特定云服務器實例的。
應用流量控制策略: 云廠商的網絡設備會查找與您的云服務器實例關聯的帶寬限制配置(例如 3Mbps)。
流量整形/控制:
對于入站流量(進入您的服務器): 網絡設備會根據 3Mbps 的限制,控制將數據包發送到您的云服務器實例的速度。如果瞬間到達的數據量超過 3Mbps,網絡設備會將多余的數據包進行緩沖或延遲發送,直到平均速度不超過 3Mbps。
對于出站流量(從您的服務器發出): 類似地,網絡設備也會控制從您的服務器發出的數據包的速度,確保不超過 3Mbps 的限制。
數據包轉發到云服務器: 經過流量整形/控制后,數據包才會被轉發到您的云服務器實例的虛擬網卡。
說簡單點就是,云廠商的基礎設施會存儲我的云服務器 IP 以及其帶寬限制為 3Mbps 的信息,當我的請求到達這些設備的時候會經過判斷然后做一些相應的限流操作,這期間我的數據包會在這些網絡設備中被緩存或者是被延遲發送,從而達到限流操作。
這個時候 tomcat 達到了時間限制,就會認為連接出了問題,就會拋異常如SocketTimeoutException
,在文件上傳場景下可能是EOFException
。
云主機限制了帶寬,也是一種限流,這令我想到了 Sentinel 實現的對微服務接口以及 Gateway 的限流,那就先分析一下 Sentinel:
-
微服務限流(Sentinel)是更細粒化、更上層的,目標是主機上的特定服務或接口,限制的是到達該服務或接口的請求速率。它是在請求到達服務進程后才開始判斷和限制的。
-
Gateway 限流介于兩者之間,它是一個獨立的微服務,但它處理的是 HTTP 請求,可以基于 URI 等信息進行限流,比云廠商的 IP 級限流更細,但比 Sentinel 集成在業務服務內部的限流可能稍微粗一些(取決于 Gateway 的配置)。Gateway 的限流也是在請求到達 Gateway 服務后進行的。
兩種限流機制的對比:
本篇文章很易懂但也很淺顯,歡迎訂閱合集一起探索網絡的世界🎉