關於服務限流的一些思考

限流的價值感

限流必然是頗有價值的,在系統資源不足時面對外部世界的不肯定性(突發流量,超預期的用戶)而造成的一種自我保護機制。 可是價值感是很低的,由於99.99%的時候系統老是工做在安全線之下,甚至一年到頭都碰不到一次撞線的機會。這就比如法律,它始終存在,可是大部分時候對於大多數人它幾乎不存在,或者說感知不到它的存在。算法

一個軟件系統每每會存在不少隱藏的bug,最經常使用的功能bug每每不多。不經常使用的功能由於長時間不被人關注缺乏重現的機會會一直隱藏在那裏乘機爆發。並且隨着軟件系統的迭代更新,不受關注的功能極有可能被測試人員忽視從覆蓋測試中被遺忘了。結果一旦遇到了突發的場景(墨菲定律),這一段被忽視的存在bug的代碼邏輯被喚醒的,而後就會致使系統出錯甚至奔潰。限流功能就是這些不被關注的功能之一。數據庫

爲了解決限流的價值感問題,工程師們須要對限流功能進行週期性演練,須要使用單元測試和壓力測試進行屢次重演。總之就是想盡各類辦法來觸發限流功能撞線,以重現那一段生產環境幾乎不會被執行的 else 邏輯。安全

限流算法

業界較爲常見的兩個基礎限流算法是漏斗算法和令牌算法,這兩個算法大同小異。漏斗算法能夠理解爲每個請求都會消耗必定的空氣,而漏斗裏的空氣是有限的,經過漏水的方式得到空氣的速率也是有限的。令牌算法能夠理解爲每個請求都會消耗一個令牌,而令牌桶生產令牌的能力是有限的,存放令牌的桶容量也是有限的。網絡

生產環境的請求QPS在曲線上看是平滑的,由於大多數統計系統都採用了平滑算法(一段時間內的均值),可是在實際的運行過程當中會有必定的隨機性和波動性,會有突發的一些離羣點,這個通常被稱之爲突發流量。分佈式

限流算法須要考慮這種突發流量,它應該能夠短時間內容忍,容忍也是有上限的,就是容忍時間必須很短。上面兩個算法都具備這個容忍能力,這個容忍就體如今漏斗中空氣的積蓄,令牌桶中令牌的積蓄,積蓄耗光就達到了容忍的上限。性能

分佈式限流

若是你的應用是單個進程,那麼限流就很簡單,請求的計數算法均可以在內存裏完成。限流算法幾乎沒有損耗,都是純內存的計算。可是互聯網世界的應用都是多節點的分佈式的,每一個節點的請求處理能力還不必定同樣。咱們須要考慮的是這多個節點的總體請求處理能力。單元測試

單個進程的處理能力是1w QPS 並不意味着總體的請求處理能力是 N * 1w QPS,由於總體的處理能力還會有共享資源的能力限制。這個共享資源通常是指數據庫,也能夠是同一臺機器的多個進程共享的 CPU 和 磁盤 等資源,還有網絡帶寬因素也會制約總體的請求量。測試

這時候請求的計數算法就須要集中在一個地方(限流中間件)來完成。應用程序在處理請求以前都須要向這種集中管理器申請流量(空氣、令牌桶)。 每個請求都須要一次網絡 IO,從應用程序到限流中間件之間。cdn

好比咱們可使用 Redis + Lua 來實現這個限流功能,可是 Lua 的性能要比 C 弱不少,一般這個限流算法能達到 1w 左右的 QPS就到頂了。還可使用 Redis-Cell 模塊,其內部使用 Rust 實現,它能達到 5w 左右的 QPS 也就到極限了。這時候它們都進入了滿負荷狀態,可是在生產環境中咱們不會但願它們一直滿負荷工做。中間件

那如何完成 10w QPS 的限流呢?

一個簡單的想法就是將限流的 key 分桶,而後使用 Redis 集羣來擴容,讓限流的申請指令通過客戶端的 hash 分桶後打散的集羣的多個幾點,藉此分散壓力。

那如何完成 百萬 QPS (1M)的限流呢?

若是還使用上面的方法那須要的帶寬資源和Redis實例也是驚人的。咱們可能須要幾十個 Redis 節點,外加上百M(1M * 20 字節 * 8) 的帶寬來完成這個工做。

這時咱們必須轉換思路,再也不使用這種集中管控的方式來工做了。就比如一個國家人太多了,就要分省、市、縣來分別進行管理 —— 放權。

咱們將總體的 QPS 按照權重分散多每一個子節點,每一個字節點在內存中進行單機限流。若是每一個節點都是對等的,那麼每一個子節點就能夠分得 1/n 的 QPS。

它的優勢在於分散了限流壓力,將 IO 操做變成純內存計算,這樣就能夠很輕鬆地應對超高的 QPS 限流。可是這也增長了系統的複雜度,須要有一個集中的配置中心來向每一個子節點來分發 QPS 閾值,須要每一個應用字節點向這個配置中心進行註冊,須要有一個配置管理後臺來對系統的 QPS 分配進行管理。

若是沒有一個完善易用成熟的開源軟件的話,這樣的一個控制中心服務和SDK每每須要一個小型團隊來完成。這對於中小型企業來講每每是承受不起的,再想一想限流的價值感如此之低,這種可能性就更加微乎其微了。

相關文章
相關標籤/搜索