如何構建高可用的系統(三):服務治理篇

如何構建高可用的系統(一):Overview中, 我提到了以下幾點:

  1. 系統依賴的一切下游系統都是不可靠的, 它們隨時可能出問題
  2. 系統的上游可能有多個, 而且每個上游的行爲都是不可預知的。如果某個上游抽風把我的系統搞崩, 那麼就會影響所有的上游
  3. 高可用是有前提條件的,一個系統對當前的負載能提供高可用, 不代表負載上升之後仍然高可用

如果把這幾個問題,映射到現在非常流行的“微服務架構”下,就可以統一用“服務治理”來概括。 下面我們具體探討下,如何解決這幾個問題。

1. 下游管理

系統所依賴的各個下游系統是不可靠的, 它們隨時可能會出問題。 比如我們開發了一個用戶服務, 會訪問數據庫、緩存等, 那這些下游的數據庫、緩存出問題的時候, 我們該怎麼辦呢?

1.1 降級措施

當下游出現問題時, 我們的服務或多或少都會受影響。 爲了保證核心場景仍然高可用, 我們需要把相關功能降級,以減少損失。 比如以下場景:

當數據庫掛掉的時候,所有對數據庫的讀寫都會失敗。 爲了使創建用戶的功能仍然對外可用, 我們可以選擇打開降級開關, 把創建用戶的請求發到消息隊列中, 在數據庫恢復之後, 從隊列中拉取消息, 再把數據寫入數據庫中。

對上游來說,寫入成功之後, 當然得能通過讀接口獲取到數據。 因此, 我們還得把數據寫入到緩存中, 這樣即使數據庫掛了, 上游也能夠儘量不受影響。

這裏只是隨便舉了個降級的例子, 在實際場景中, 降級處理方案還是得仔細思考,具體問題具體分析。

1.2 超時

在很多場景下, 服務接收到一個請求後, 會調用非常多的下游, 當調用某個下游的請求遲遲沒有得到響應, 會造成兩個結果:

  1. 整個請求的響應時長上升, 調用方在等待一段時間後,不再等待對本服務的調用結果
  2. 服務實例的資源被一個請求長時間佔據, 未能釋放, 從而降低整體的吞吐量

因此我們需要控制對下游依賴的超時情況, 一般來說, 對核心依賴的超時可以適當大一些,對非核心依賴的超時可以適當小一些。

1.3 熔斷

當某個下游出問題後, 對它的調用失敗率就會上升。 當上升到某個程度時, 往往沒必要再每次都去調用了, 而是默認失敗, 只發出一小部分的請求。 當這小部分的請求成功率達到某個閾值時, 才恢復到正常狀態。這種策略就叫“熔斷”。

現在已經有一些成熟的組件來幫助我們做了熔斷這件事, 比如Hystrix等。

2. 資源隔離

爲了防止某個上游的抽風行爲把系統打掛, 或者爲了防止離線系統的異常流量影響在線系統的正常運行, 我們往往會採用資源隔離的方式。

假設我們在維護一個用戶服務, 服務的上游有很多, 包括在線系統和離線系統:

  • 暴露給客戶端的api:比如訪問個人主頁時,會調用用戶服務來獲取用戶信息
  • 後臺審覈系統: 比如審覈用戶頭像是否合規,需要調用用戶服務來獲取用戶信息

對於這種情況, 我們可以部署多套環境, 來爲不同的上游提供服務。 “一套環境”,在這個例子中, 包括用戶服務的實例、 用戶服務所依賴的數據庫和緩存等。

這種資源隔離的思想落到具體實現,則可以通過服務發現機制來解決。 比如我們用consul來做服務註冊和發現, 那可以通過給實例打不同的tag來做區分。

3.限流

一個系統對外提供的服務量是有上限的, 如果我們對外提供的承諾是爲1w/s的請求量提供4個9的可用性, 那在請求量超出了1w/s, 影響可用性的時候, 就應該採用限流的方式來避免系統被打掛。

3.1常見限流算法

限流算法有很多, 比較經典的有漏桶算法和令牌桶算法等。 兩者的區別是,漏桶算法的限流是勻速的,不支持突發的流量增長, 而令牌桶算法則是在一個時間窗口內提供一定的總量, 並不限制在時間窗口內的流量分配。

3.2單機限流與分佈式限流

爲了實現限流算法, 我們往往需要記錄時間戳、請求餘量等數據。 如果是單機限流的話, 只需要把這些數據保存在內存中即可。

在實際場景下, 我們需要的往往是分佈式限流,即多臺機器對外提供的總量是有限制的。 比如用戶服務所依賴的緩存和數據庫加起來只能扛100w/s的讀, 那麼所有的服務實例能支持的qps, 就不應該超過100w/s。

對於分佈式限流, 上面保存在內存中的方案就不適用了, 因此需要依賴外部存儲來保存相關數據, 通常來說,redis是一個比較好的選擇。

3.3業務/用戶優先級優先級限流

上面談到的限流, 是比較簡單粗暴的: 如果一個服務無法處理所有的請求, 那麼就隨機拋棄一部分請求。

但是這種限流方案並不一定能有效緩解整個系統的負載, 並且浪費了很多計算資源,比如以下場景:

假設api層處理一個客戶端請求時,依賴N個下游服務, 而這N個下游服務都處於過載狀態, 以p的概率隨機拒絕服務, 那麼這個客戶端請求最後被成功處理的概率就是(1-p)^N。 如果P和N很大, 那麼這個請求很可能沒有被成功處理,但是卻浪費了很多計算資源(比如N-1個服務都處理了這個請求,但是1個服務拒絕了)。

因此微信提出了面向業務和用戶優先級的限流方案, 可以有效解決上述問題。 我在之前的文章中對微信的解決方案做了解讀, 感興趣的朋友可以去看下。

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