[譯] 負載性能

Netflix 的自適應併發限制前端

Eran Landau、William Thurston、Tim Bozarth 做android

在 Netflix,咱們沉迷於服務可用性的研究,關於如何實現目標這幾年也寫了幾篇博客文章。這些技術包括了斷路器模式、併發限制、混沌測試等等。如今咱們宣佈最近的創新:自適應併發限制。自適應併發限制從根本上提高了應用程序在極端負載下的表現,並使得咱們能夠避免級聯服務故障。咱們移除了斷定系統併發限制的艱難工做,又保證了延遲保持較低水平。本文發佈的同時,咱們開源了一個簡單的 Java 類庫,集成了 servlet、executor 和 GRPC 框架支持。ios

簡要的背景介紹

併發量其實就是系統在任意時刻可以處理的請求數,一般是由硬件資源決定的,好比 CPU。一般咱們使用利特爾法則(Little’s Law)計算系統的併發量:穩定狀態下的系統,併發量等於平均服務時間與平均服務速率的乘積(L = 𝛌W)。任何超過該併發量的請求都不能當即獲得服務,必須排隊或被拒絕。如此說來,即使請求到達速率和服務時間都不均勻,作些排隊處理就能令整個系統被充分利用起來,所以它是很必要的。git

當隊列沒有強制限制時系統會出問題,好比在很長的時間內請求到達速度都超過請求結束速度。隨着隊列增加,延遲也增加了,直到全部的請求都開始超時,最終系統耗盡內存而後崩潰。若是留下未經檢測的延遲,它就會對其調用者產生不利影響,從而致使系統的級聯故障。github

強制進行併發限制不是什麼新奇玩意兒;難的是在大型的動態分佈式系統中找到併發量限制,並且這個系統的併發量和延遲特徵還在不斷改變。咱們解決方案給出的主要目的,就是動態地肯定併發量限制。這一併髮量能夠被視爲在系統性能降低前(如延遲)所容許的最大請求數量(實時併發量+排隊中的請求)。算法

解決方案

之前,在 Netflix 咱們手動配置了由複雜的性能測試和分析獲得的併發限制。雖然這在某一時刻很準確,可是一旦系統部分停運致使拓撲結構變化、系統自動擴展(auto scaling)或是推送代碼上線引發延遲特徵變化,這一測量值都會很快過期。後端

咱們知道咱們能夠作得比靜態的併發限制更好,因此咱們探索了自動肯定系統固有併發限制的方案,該方案:bash

1. 不須要人工

2. 不須要集中協調

3. 不須要硬件信息或系統拓撲結構就能肯定併發限制

4. 能適應系統的拓撲結構變更

5. 計算容易,執行簡單
複製代碼

爲了解決該問題,咱們轉向嘗試真正的 TCP 擁塞控制算法,不會引發超時或增長延遲就能探明能夠同時傳輸多少個數據包(好比擁塞窗口大小)。這些算法持續跟蹤各類指標,以估算系統的併發限制,並持續調節擁塞窗口大小。服務器

咱們能夠看到這個系統用藍線表明了未知的實際併發量。客戶端起初發送的請求被限制在較低的併發水平,系統在延遲不增長的狀況下經過增長併發窗口頻繁探測更高的併發量。當延遲真的開始增加時,傳感器假定到達了併發極限,降回擁塞窗口大小。併發限制的探測持續進行,就產生了上圖的鋸齒狀圖像。併發

咱們的算法創建在基於延遲的 TCP 擁塞控制算法的基礎上,該算法查看最小的延遲(表明沒有排隊的最佳狀況)除以按時間進行採樣測量的延遲之間的比率,做爲識別隊列產生並開始致使延遲增長的衡量參照。從該比值能夠獲知延遲變化的梯度或級別:gradient=(RTTnoload/RTTactual) (譯者注:RTT 指 TCP 鏈接中一個數據包的往返時間,RTTnoload 指沒有延遲的最小 RTT 值,RTTactual 指實際的 RTT 值)。值爲 1 表示沒有出現排隊,能夠增大併發限制;小於 1 的值表示造成了過多隊列,應該減小併發限制。對於每一個新樣本,利用此比率調整併發限制,並使用一個簡單公式增長所容許的隊列大小:

newLimit = currentLimit × gradient + queueSize
複製代碼

通過幾回迭代後,算法收斂到某個保持延遲較低的併發限制水平,同時容許一些排隊處理來處理突發請求。所容許的隊列大小是可調整的,而且該值決定了併發限制的增加速度。咱們認定當前併發限制的算術平方根是個不錯的默認值。選擇算數平方根,主要是由於它能很大程度上反映出當前併發限制在較低請求數時的有用特性,容許迭代時快速增加,而在出現大量請求時又能靈活減少,從而保證了系統穩定。

啓用自適應併發限制

啓用後,自適應服務器端併發限制會拒絕過多的 RPS(Request Per Second) 來保持較低的延遲,從而保護實例自身及其所依賴的服務。若是不拒絕過多的併發請求,RPS 或者延遲的持續增長都會轉化爲更糟糕的延遲並最終致使系統故障。服務如今可以減小過多的負載並保持較低的延遲,而其餘緩和措施(如自動擴展)則會起做用。

注意這一點很重要:在服務器層面強制執行併發限制(不進行協調),每一個服務器的流量限制可能應用得至關迅速。 所以,獲得的併發請求限制和數量在不一樣服務器間可能相差較大,從雲服務商租用的多個服務器尤爲如此。 當其餘地方有足夠的承載能力時,可能引發某臺服務器脫離服務集羣。話雖如此,使用了客戶端側負載均衡,客戶端只需重試一次請求,幾乎就能 100% 到達一臺可用的服務實例。更棒的是,因爲服務可以在亞毫秒時間內極速減小流量,這對服務性能的影響微乎其微,所以不用再擔憂客戶端重試請求引發的 DDOS 和重試風暴(譯者注:應該是指大量客戶端由於請求被拒絕而反覆重試服務請求)。

總結

隨着咱們推出自適應的併發限制,咱們再也不須要像看小孩同樣看着系統性能,而後手動調整服務負載。 更重要的是,它同時提升了咱們整個基於微服務的生態系統的可靠性和可用性。

咱們很高興在一個小型開源庫中分享咱們的思路實現和常見框架集成:http://github.com/Netflix/concurrency-limits。 咱們但願,任何想要保護其服務免於級聯故障和與負載引發的延遲降級的人,均可以利用咱們的代碼來實現更好的可用性。 咱們期待社區的反饋,並樂意接受新算法或框架集成的 Pull Request。

若是發現譯文存在錯誤或其餘須要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可得到相應獎勵積分。文章開頭的 本文永久連接 即爲本文在 GitHub 上的 MarkDown 連接。


掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章
相關標籤/搜索