大型網站架構:熔斷、降級、限流

微服務架構中,服務數量增加,整體系統可用性會存在潛在問題,因此,需要一些額外的措施。

具體幾個方面:

  1. 問題:分佈式系統架構中,存在的問題
  2. 解決方法:上述問題的解決辦法?
  3. 注意事項
  4. HyStrix 框架的原理

特別說明:

Hystrix 已經進入「維護狀態」,現在 Netflix 已經啓用「resilience4j」框架,作爲替代方案。

分佈式系統,隨着業務複雜度提高,系統不斷拆分,一個面向 C 端的 API 調用,其內部的 RPC 調用層層嵌套,調用鏈變長,會造成下述 2 個問題:

  1. API 接口可用性降低
    • 假設一次 api 請求,內部涉及 30 次 rpc 調用
    • 每個微服務可用性 99.99%
    • 則,api 請求的可用性爲 99.99% 的 30 次方 = 99.7% ,即,0.3% 的失敗率
  2. 系統阻塞,拒絕請求接入
    • 假設一次 api 請求,內部涉及 10 次 rpc 調用
    • 只要 10 次 rpc 中,有一次請求超時,則,整個 api 調用就超時了
    • 如果大量請求突發訪問,則,大量的線程都阻塞(block 等待超時)在這一服務上,新的請求無法接入

爲了解決上述「API 接口可用性降低」和「系統阻塞、拒絕請求接入」問題,可以採用下述 4 中方法:

  1. 熔斷:服務熔斷,一旦觸發「異常統計條件」,則,直接熔斷服務,在「調用方」直接返回,不再 rpc 調用遠端服務;
  2. 降級:降級是配合「熔斷」的,熔斷後,不再調用遠端服務器的 rpc 接口,而採用本地的 fallback 機制,返回一個「備用方案」/「默認取值」;
  3. 限流:限制「速率」,或從業務層限制「總數」,被限流的請求,直接進入「降級」fallback 流程;
  4. 異步 RPC:通過異步訪問,提升系統訪問性能;

爲了防止大量請求都被 block 到某個服務上,導致大量線程、端口被佔用,無法處理新請求,則,引入「服務熔斷」概念。

  • 一旦觸發「異常統計條件」,則,不再 block 等待服務的響應
  • 異常統計條件,一般有下述幾種:
    • 指定的時間窗口內,rpc 調用失敗次數的佔比,超過設定的閾值,則,不再 rpc 調用,直接返回「降級邏輯」

HyStrix 框架下,實現「服務熔斷」的底層原理:

  • 封裝:「客戶端」不直接調用「服務器」的rpc接口,而是在「客戶端」包裝一層
  • 屏蔽:在「客戶端」的「包裝層」裏面,實現熔斷邏輯

具體 HyStrix 框架的 hello world 邏輯:


 
  1. // 客戶端:封裝原生的 rpc 調用邏輯
  2. public class CommandHelloWorld extends HystrixCommand<String> {
  3. private final String name;
  4. public CommandHelloWorld(String name) {
  5. super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"));
  6. this.name = name;
  7. }
  8. @Override
  9. protected String run() {
  10. //關鍵點:把一個RPC調用,封裝在一個HystrixCommand裏面
  11. return "Hello " + name + "!";
  12. }
  13. }
  14. // 客戶端調用:以前是直接調用遠端RPC接口,現在是把RPC接口封裝到HystrixCommand裏面,它內部完成熔斷邏輯
  15. String s = new CommandHelloWorld("World").execute();

隔離策略線程池 vs. 信號量

上面的代碼實例中,默認情況下,HystrixCommand 是「線程隔離策略」,即,直接在線程池中,獲取新的線程,來執行 rpc 調用。

另外,還有一種策略是「信號量隔離策略」,直接在「調用線程」中執行,通過「信號量」進行隔離。

Think:

上述隔離策略「線程池」和「信號量」,有什麼優劣?適用場景?

上述「線程池」和「信號量」2 種隔離方式,其優缺點:

  • 線程池
    • 缺點:增加計算開銷,每個 rpc 請求,被獨立的線程執行,其中,涉及線程調度、上下文切換、請求排隊等時間。
    • Note:Netflix 公司內部實踐認爲,線程隔離的開銷足夠小,不會產生重大成本或者性能影響。
    • 適用:依賴網絡訪問的請求,可以採用「線程池隔離」,只依賴內存緩存的情況下,建議採用「信號量隔離」。
  • 信號量
    • 適用:只依賴內存緩存、不涉及網絡訪問,建議採用「信號量隔離」方式。

熔斷參數的設置:熔斷器的參數

  • circuitBreaker.requestVolumeThreshold://滑動窗口的大小,默認爲20
  • circuitBreaker.errorThresholdPercentage: //錯誤率,默認50%
  • circuitBreaker.sleepWindowInMilliseconds: //過多長時間,熔斷器再次檢測是否開啓,默認爲5000,即5s

上述 3 個參數,放在一起的物理含義:

每當20個請求中,有50%失敗時,熔斷器就會斷開,此時,再調用此服務,將不再調遠程服務,直接返回失敗。

5s 後,重新檢測該觸發條件,判斷是否熔斷器連接,或者繼續保持斷開。

降級,是配合「熔斷」存在的。

降級,就是服務熔斷之後,不再調用服務器的 rpc 接口,客戶端直接準備一個本地的 fallback 回調,返回一個缺省值。

影響

  • 上述「服務降級」,直接本地 fallback,給一個缺省值/備用方案
  • 相對直接掛掉,要好一些,具體還要看業務場景,特別是採用「合適的 fallback 方案」

限流,在現實生活中,也比較常見,比如節假日去旅遊景點,管理部門通常會在外面設置攔截,限制景點的進入人數(等有人出來之後,再放新的人進去)。

對應到分佈式系統中,比如活動、秒殺,也會限流。

關鍵點:限流的目標和依據,是什麼?

常見的,限流依據下述參數進行:

  1. HyStrix 中:
    1. 線程隔離時,線程數 + 排隊隊列大小,來限流
    2. 信號量隔離時,設置「最大併發請求數」,來限流
  2. 併發數量:QPS、併發連接數等
  3. 總量:業務層中,限制「庫存」總量等

限流技術原理:

  • 限制「速率」:令牌桶算法
  • 限制「總數」:限制某個業務量的 count 值,則,具體業務場景具體分析。

TODO:

  • Guava 的 RateLimiter 也已經有成熟做法。

異步 RPC,主要目標:提升系統的接口性能,從而提升併發處理能力。

啓用「異步 RPC」,有一個前提,異步 RPC 之間,不存在「相互依賴」。

實例:

  1. 比如你的接口,內部調用了3個服務,時間分別爲T1, T2, T3。
  2. 如果是順序調用,則總時間是T1 + T2 + T3;
  3. 如果併發調用,總時間是Max(T1,T2,T3)。

一般成熟的RPC框架,本身都提高了異步化接口,Future或者Callback形式。

同樣,Hystrix 也提高了同步調用、異步調用方式,此處不再詳述。

熔斷、降級、限流、異步 RPC,都是分佈式服務框架中,常用的策略,能夠提升系統穩定性、容錯性。當前大部分服務框架,都對這些策略,有比較好的原生支持。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章