1. 程式人生 > 其它 >架構設計(三) 服務降級,限流和熔斷的設計

架構設計(三) 服務降級,限流和熔斷的設計

隨著公司業務不斷的複雜化,隨之而來的就是微服務的爆發式增長,流量一下就起來了,對於閘道器來說就不得不考慮服務的保護了,也就是下面講到的降級,限流和熔斷

1. 服務降級的設計

什麼是服務降級?

當伺服器壓力劇增的情況下,根據實際業務情況及流量,對一些服務和頁面有策略的不處理或換種簡單的方式處理,從而釋放伺服器資源以保證核心交易正常運作或高效運作。

服務降級的目標

保證核心服務可用,非核心服務弱可用,甚至不可用

服務降級的層級

服務層降級
- SLB (Service Load Balance)
- Nginx / Apache
- Spring Cloud Gateway
- Microservice

資料層DB降級

1.1 降級分類

降級按照是否自動化可分為:自動開關降級和人工開關降級。
降級按照功能可分為:讀服務降級、寫服務降級。
降級按照處於的系統層次可分為:多級降級。

詳情可參考:架構設計 -- 服務降級策略詳解

1.2 自動降級分類

超時降級 —— 主要配置好超時時間和超時重試次數和機制,並使用非同步機制探測恢復情況
失敗次數降級 —— 主要是一些不穩定的API,當失敗呼叫次數達到一定閥值自動降級,同樣要使用非同步機制探測回覆情況
故障降級 —— 如要呼叫的遠端服務掛掉了(網路故障、DNS故障、HTTP服務返回錯誤的狀態碼和RPC服務丟擲異常),則可以直接降級
限流降級 —— 當觸發了限流超額時,可以使用暫時遮蔽的方式來進行短暫的遮蔽

1.3 服務降級 - 分散式開關

我們可以設定一個分散式開關,用於實現服務的降級,然後集中式管理開關配置資訊即可。具體方案如下

1.啟動拉取配置,並啟動定時拉取和訂閱配置資訊。
2.視覺化管理端變更配置項
3.操作配置中心配置資訊
4.通知或被定時拉取最新配置,然後變更記憶體中的配置資訊

1.4 服務降級 - 配置中心

微服務降級的配置資訊是集中式的管理,然後通過視覺化介面進行友好型的操作。配置中心和應用之間需要網路通訊,因此可能會因網路閃斷或網路重啟等因素,導致配置推送資訊丟失、重啟或網路恢復後不能再接受、變更不及時等等情況,因此服務降級的配置中心需要實現以下幾點特性,從而儘可能的保證配置變更即使達到

啟動主動拉取配置 —— 用於初始化配置(減少第一次定時拉取週期)

釋出訂閱配置 —— 用於實現配置及時變更(可以解決90%左右的配置變更)

定時拉取配置 —— 用於解決釋出訂閱失效或消失丟失的情況(可以解決9%左右的釋出訂閱失效的訊息變更)

離線檔案快取配置 —— 用於臨時解決重啟後連線不上配置中心的問題

可編輯式配置文件 —— 用於直接編輯文件的方式來實現配置的定義

提供Telnet命令變更配置 —— 用於解決配置中心失效而不能變更配置的常見

1.5 服務降級要考慮的問題

核心和非核心服務(核心服務 - 中臺服務,和錢打交道的服務 非核心 - OA服務) 
是否支援降級,降級策略
業務放通的場景,策略

1.6 處理策略

當觸發服務降級後,新的請求再次到達時,我們該如何來處理這些請求呢?從微服務架構全域性的視角來看,我們通常有以下是幾種常用的降級處理方案:

頁面降級 —— 視覺化介面禁用點選按鈕、調整靜態頁面
延遲服務 —— 如定時任務延遲處理、訊息入MQ後延遲處理
寫降級 —— 直接禁止相關寫操作的服務請求
讀降級 —— 直接禁止相關度的服務請求
快取降級 —— 使用快取方式來降級部分讀頻繁的服務介面

1.6.1 讀寫請求降級的服務降級手段

1.關閉部分服務(業務相關)

2.拒絕部分請求

2.1 拒絕部分老請求 
- 減輕微服務請求處理的數量
- 確保“新”請求正常響應
- RPC 佇列方式  請求入隊,出隊時間處理

2.2 優先順序請求方式
- 非核心功能直接丟棄
- 業務緊密

2.3 隨機拒絕方式
- 隨機丟掉一定比例的請求,網站一會可用一會不可用

針對後端程式碼層面的降級處理策略,則我們通常使用以下幾種處理措施進行降級處理

拋異常
返回NULL
呼叫Mock資料
呼叫Fallback處理邏輯

1.7 服務降級 - 高階特性

我們已經為每個服務都做好了一個降級開關,也已經在線上驗證通過了,感覺完全沒問題了。

場景一:某一天,運營搞了一次活動,突然跑過來說,現在流量已經快漲到上限了,有沒有批量降級所有不重要服務的方式?開發一臉懵逼的看著,這又不是操作DB,哪裡有批量操作呀。

場景二:某一天,運營又搞事了,說我們等下要搞一個活動,讓我們趕緊提前把不重要的服務都降級了,開發又是一臉懵逼,我怎麼知道要降級哪些服務呀。

反思:服務降級的功能雖然是實現了,可是沒有考慮實施時的體驗。服務太多,不知道該降級哪些服務,單個操作降級速度太慢……

1.7.1 分級降級

當微服務架構發生不同程度的情況時,我們可以根據服務的對比而進行選擇式捨棄(即丟車保帥的原則),從而進一步保障核心的服務的正常運作。

如果等線上服務即將發生故障時,才去逐個選擇哪些服務該降級、哪些服務不能降級,然而線上有成百上千個服務,則肯定是來不及降級就會被拖垮。同時,在大促或秒殺等活動前才去梳理,也是會有不少的工作量,因此建議在開發期就需要架構師或核心開發人員來提前梳理好,是否能降級的初始評估值,即是否能降級的預設值。

為了便於批量操作微服務架構中服務的降級,我們可以從全域性的角度來建立服務重要程度的評估模型,如果有條件的話,建議可以使用層次分析法(The analytic hierarchy process,簡稱AHP)的數學建模模型(或其它模型)來進行定性和定量的評估(肯定比架構師直接拍腦袋決定是否降級好很多倍,當然難度和複雜度也會高許多,即你需要一個會數學建模人才),而層次分析法的基本思路是人對一個複雜的決策問題的思維和判斷過程大體上是一樣的。

以下是個人給出的最終評價模型,可作為服務降級的評價參考模型進行設計

我們利用數學建模的方式或架構師直接拍腦袋的方式,結合服務能否降級的優先原則,並根據颱風預警(都屬於風暴預警)的等級進行參考設計,可將微服務架構的所有服務進行故障風暴等級劃分為以下四種

評估模型

藍色風暴 (S4)—— 表示需要小規模降級非核心服務

黃色風暴 (S3)—— 表示需要中等規模降級非核心服務

橙色風暴(S2) —— 表示需要大規模降級非核心服務

紅色風暴 (S1)—— 表示必須降級所有非核心服務

設計說明

故障嚴重程度為:S4<S3<S2<S1

建議根據二八原則可以將服務劃分為:80%的非核心服務+20%的核心服務

以上模型只是整體微服務架構的服務降級評估模型,具體大促或秒殺活動時,建議以具體主題為中心進行建立(不同主題的活動,因其依賴的服務不同,而使用不同的進行降級更為合理)。當然模型可以使用同一個,但其資料需要有所差異。最好能建立一套模型庫,然後實施時只需要輸入相關服務即可輸出最終降級方案,即輸出本次大促或秒殺時,當發生藍色風暴時需要降級的服務清單、當發生黃色風暴時需要降級的服務清單……

1.7.2 降級權值

微服務架構中有服務權值的概念,主要用於負載時的權重選擇,同樣服務降級權值也是類似,主要用於服務降級選擇時的細粒度優先順序抉擇。所有的服務直接使用以上簡單的四級劃分方式進行統一處理,顯然粒度太粗,或者說出於同一級的多個服務需要降級時的降級順序該如何?甚至我想要人工智慧化的自動降級,又該如何更細粒度的控制?

基於上述的這些AI化的需求,我們可以為每一個服務分配一個降級權值,從而便於更加智慧化的實現服務治理。而其評估的數值,同樣也可以使用數學模型的方式進行定性與定量的評估出來,也可以架構師根據經驗直接拍腦袋來確定。

2. 服務限流的設計

限流可以認為服務降級的一種,限流就是限制系統的輸入和輸出流量已達到保護系統的目的。一般來說系統的吞吐量是可以被測算的,為了保證系統的穩定執行,一旦達到的需要限制的閾值,就需要限制流量並採取一些措施以完成限制流量的目的。比如:延遲處理,拒絕處理,或者部分拒絕處理等等。

2.1 服務限流應該怎麼做?

對系統服務進行限流,一般有如下幾個模式

1.熔斷:
這個模式是需要系統在設計之初,就要把熔斷措施考慮進去。當系統出現問題時,如果短時間內無法修復,系統要自動做出判斷,開啟熔斷開關,拒絕流量訪問,避免大流量對後端的過載請求。系統也應該能夠動態監測後端程式的修復情況,
當程式已恢復穩定時,可以關閉熔斷開關,恢復正常服務。 2.服務降級: 將系統的所有功能服務進行一個分級,當系統出現問題,需要緊急限流時,可將不是那麼重要的功能進行降級處理,停止服務,這樣可以釋放出更多的資源供給核心功能的去用。 例如在電商平臺中,如果突發流量激增,可臨時將商品評論、積分等非核心功能進行降級,停止這些服務,釋放出機器和CPU等資源來保障使用者正常下單,而這些降級的功能服務可以等整個系統恢復正常後,再來啟動,進行補單
/補償處理。 除了功能降級以外,還可以採用不直接操作資料庫,而全部讀快取、寫快取的方式作為臨時降級方案。 3.延遲處理: 這個模式需要在系統的前端設定一個流量緩衝池,將所有的請求全部緩衝進這個池子,不立即處理。然後後端真正的業務處理程式從這個池子中取出請求依次處理,常見的可以用佇列模式來實現。這就相當於用非同步的方式去減少了後端的處理壓力,
但是當流量較大時,後端的處理能力有限,緩衝池裡的請求可能處理不及時,會有一定程度延遲。 4.特權處理: 這個模式需要將使用者進行分類,通過預設的分類,讓系統優先處理需要高保障的使用者群體,其它使用者群的請求就會延遲處理或者直接不處理。

那在實際專案中,對訪問流量的限制,可採用如下幾種技術方法

1.熔斷技術
熔斷的技術可以參考Resilience4j的CircuitBreaker

2.計數器方法
系統維護一個計數器,來一個請求就加1,請求處理完成就減1,當計數器大於指定的閾值,就拒絕新的請求。基於這個簡單的方法,可以再延伸出一些高階功能,比如閾值可以不是固定值,是動態調整的。
另外,還可以有多組計數器分別管理不同的服務,以保證互不影響等。 3.佇列方法 就是基於FIFO佇列,所有請求都進入佇列,後端程式從佇列中取出待處理的請求依次處理。基於佇列的方法,也可以延伸出更多的玩法來,比如可以設定多個佇列以配置不同的優先順序。 4.令牌桶方法 首先還是要基於一個佇列,請求放到佇列裡面。但除了佇列以外,還要設定一個令牌桶,另外有一個指令碼以持續恆定的速度往令牌桶裡面放令牌,後端處理程式每處理一個請求就必須從桶裡拿出一個令牌,
如果令牌拿完了,那就不能處理請求了。我們可以控制指令碼放令牌的速度來達到控制後端處理的速度,以實現動態流控。

這篇部落格有一些限流演算法:https://www.cnblogs.com/hlkawa/p/13111003.html

2.2 服務限流的注意事項

2.2.1 服務限流時的需要注意的一些原則和事項

實時監控:系統必須要做好全鏈路的實時監控,才能保證限流的及時檢測和處理。

手動開關:除系統自動限流以外,還需要有能手動控制的開關,以保證隨時都可以人工介入。

限流的效能:限流的功能理論上是會在一定程度影響到業務正常效能的,因此需要做到限流的效能優化和控制。

2.2.2限流演算法分散式改造

相對於單機限流演算法,分散式限流演算法的是指: 演算法可以分散式部署在多臺機器上面,多臺機器協同提供限流功能,可以對同一介面或者服務做限流。分散式限流演算法相較於單機的限流演算法,最大的區別就是介面請求計數器需要中心化儲存,比如以Redis作為 中心計數器來實現分散式限流演算法。分散式限流演算法在引入 Redis 中心計數器這個獨立的系統之後,系統的複雜度一下子高了很多,因為要解決一些分散式系統的共性技術問題:

1. 資料一致性問題
介面限流過程包含三步操作:
Step 1:“讀”當前的介面訪問計數n;
Step 2:”判斷”是否限流;
Step 3:“寫”介面計數 n+1, if 介面限流驗證通過

在併發情況下,這3步CAS操作(compare and swap)存在race condition。在多執行緒環境下,可以通過執行緒的加鎖或者concurrent開發包中的Atomic原子物件來實現。在分散式情況下,思路也是類似的,可以通過分散式鎖,來保證同一時間段只有一個程序在訪問,
但是引入分散式鎖需要引入新的系統和維護鎖的程式碼,代價較大,為了簡單,我們可以選擇另一種思路:藉助Redis單執行緒工作模式 +Lua指令碼完美的支援了上述操作的原子性。
2. 超時問題
對於Redis的各種異常情況,我們處理起來並不是很難,catch住,封裝為統一的exception,向上拋,或者吞掉。但是如果Redis訪問超時,會嚴重影響介面的響應時間甚至導致介面響應超時,這個副作用是不能接受的。
所以在我們訪問Redis時需要設定合理的超時時間,一旦超時,判定為限流失效,繼續執行介面邏輯。Redis訪問超時時間的設定既不能太大也不能太小,太大可能會影響到介面的響應時間,太小可能會導致太多的限流失效。
我們可以通過壓測或者線上監控,獲取到Redis訪問時間分佈情況,再結合服務介面可以容忍的限流延遲時間,權衡設定一個較合理的超時時間。
3. 效能問題
分散式限流演算法的效能瓶頸主要在中心計數器Redis,在沒有做Redis sharding的情況下,基於單例項Redis的分散式限流演算法的效能要遠遠低於基於記憶體的單機限流演算法,此處需要進行壓測看到具體引數
所以在應用分散式限流演算法時,一定要考量限流演算法的效能是否滿足應用場景,如果微服務介面的TPS已經超過了限流框架本身的TPS,則限流功能會成為效能瓶頸影響介面本身的效能。 除了TPS之外,網路延遲也是一個需要特別考慮的問題,特別是如果中心計數器與限流服務跨機房跨城市部署,之間的網路延遲將會非常大,嚴重影響微服務介面的響應時間。

2.2.3 選擇單機限流還是分散式限流

首先需要說明一下:這裡所說的單機限流和分散式限流與之前提到的單機限流演算法和分散式限流演算法並不是一個概念!為了提高服務的效能和可用性,微服務都會多例項叢集部署,所謂單機限流是指:獨立的對叢集中的每臺例項進行介面限流,比如限制每臺例項介面訪問的頻率為最大 1000 次 / 秒,單機限流一般使用單機限流演算法;所謂的分散式限流是指:提供服務級的限流,限制對微服務叢集的訪問頻率,比如限制 A 呼叫方每分鐘最多請求 1 萬次“使用者服務”,分散式限流既可以使用單機限流演算法也可以使用分散式限流演算法。

單機限流的初衷是防止突發流量壓垮伺服器,所以比較適合針對併發做限制。分散式限流適合做細粒度限流或者訪問配額,不同的呼叫方對不同的介面執行不同的限流規則,所以比較適合針對 hits per second 限流。從保證系統可用性的角度來說,單機限流更具優勢,從防止某呼叫方過度競爭服務資源來說,分散式限流更加適合。

分散式限流與微服務之間常見的部署架構有以下幾種

1. 在接入層(api-gateway)整合限流功能  
這種整合方式是在微服務架構下,有api-gateway的前提下,最合理的架構模式。如果api-gateway是單例項部署,使用單機限流演算法即可。如果api-gateway是多例項部署,為了做到服務級別的限流就必須使用分散式限流演算法。

2. 限流功能封裝為RPC服務  
當微服務接收到介面請求之後,會先通過限流服務暴露的RPC介面來查詢介面請求是否超過限流閾值。這種架構模式,需要部署一個限流服務,增加了運維成本。這種部署架構,效能瓶頸會出現在微服務與限流服務之間的RPC通訊上,
即便單機限流演算法可以做到
200萬TPS,但經過RPC框架之後,做到10萬TPS的請求限流就已經不錯了。 3. 限流功能整合在微服務系統內 這種架構模式不需要再獨立部署服務,減少了運維成本,但限流程式碼會跟業務程式碼有一些耦合,不過,可以將限流功能整合在切面層,儘量跟業務程式碼解耦。
如果做服務級的分散式限流,必須使用分散式限流演算法,如果是針對每臺微服務例項進行單機限流,使用單機限流演算法就可以。

2.2.4不同業務使用不同限流熔斷策略

這裡所講的熔斷策略,就是當介面達到限流上限之後,如何來處理介面請求的問題。前面也有提到過一些限流熔斷策略了,所謂否決式限流就是超過最大允許訪問頻率之後就拒絕請求,比如返回 HTTP status code 429 等,所謂阻塞式限流就是超過最大允許訪問頻率之後就排隊請求。除此之外,還有其他一些限流熔斷策略,比如:記錄日誌,傳送告警,服務降級等等。

同一個系統對於不同的呼叫方也有可能有不同的限流熔斷策略,比如對響應時間敏感的呼叫方,我們可能採用直接拒絕的熔斷策略,對於像後臺 job 這樣對響應時間不敏感的呼叫方,我們可能採用阻塞排隊處理的熔斷策略。

我們再來看下其他熔斷策略的一些應用場景:比如限流功能剛剛上線,為了驗證限流演算法的有效性及其限流規則的合理性,確保不誤殺請求,可以先採用日誌記錄 + 告警的限流熔斷策略,通過分析日誌判定限流功能正常工作後,再進一步升級為其他限流熔斷策略。

不同的熔斷策略對於選擇限流演算法也是有影響的,比如令牌桶和漏桶演算法就比較適合阻塞式限流熔斷場景,如果是否決式的限流熔斷場景就比較適合選擇基於時間視窗的限流演算法。

2.2.5 不同層級的限流

限流的原則,是儘量在流量源頭限,並且是需要依據現有團隊所掌握的技能來。

slb節點的限流

slb全稱 ServiceLoad Balancer,slb的限流更準確的描述是攔截

slb流量特點

幾乎來自外部的流量都從這個入口過來,無論是帶業務屬性的還是不帶業務屬性的、ddos的、正常流量、爬蟲等統統從這裡來。

需要攔截是什麼(由於流量過了這個節點就是我們的應用系統了,因此最好是把非業務應用相關的流量擋住,限制住,讓它有序進來,不要衝垮系統)

ddos攻擊流量
其他通用級的不安全流量:sql注入、xss注入等
連線併發限制
每ip請求限流控制
爬蟲流量

上述是slb節點,但是也有團隊考慮到本身技能,以及程式碼git化儲存的原則,會把某些配置往後面的nginx/kong移,因為slb的配置是UI介面化的,程式碼化儲存比較不直接;但是nginx/kong這種就相對容易多了,而且恢復時只要指令碼到位,分分鐘就恢復一套系統,很方便。

nginx節點的流限的

到了這裡,ddos這種攻擊流量應該算是過濾掉了,常見的sql注入等攻擊也過濾掉了;但是還存在爬蟲流量(有時,這種流量是我們需要的,SEO推廣等);也會包含高階攻擊流量

因此nginx節點需要做的限流是(通用級別的限流)

爬蟲限流、併發控制、或者過濾、又或者重定向到蜜罐系統(專門給爬蟲做的系統,和主業務系統隔離)
每ip請求限流

spring cloud gateway節點的限流

到了這裡,一般普通靜態H5資源的訪問已經沒有了(已經重定向到其他nginx節點分流處理掉了);gateway處理的都是動態程式語言需要處理的流量,這裡指java。

也就是說,到了這個節點,開始進入java系統領域,流量承受能力比nginx降了1個等級,需要做更多的限制

普通場景下的限流

突發流量下的限流,如:秒殺等

CC攻擊+驗籤的過濾(由於公私鑰證書一般加在java節點上,因此此處放java系統範疇,而不是slb之前,或者nginx之前)

可以在gateway動態路由中分別配置各自的限流配置,以及上述自定義的CC+驗籤配置

隔離性

由於gateway流量會轉發給後端一大堆微服務,假設由於哪個java微服務處理不過來hang住了,又不想影響其他後端為服務的轉發,因此需要做隔離性。

可選方案:
resilience4j
sentinel
guava

此處的限流和nginx的限流有啥區別?

nginx的閾值要大,因為nginx覆蓋的範圍不光是java領域,還有H5等其他範圍

nginx的限流配置維度是通用的,但是spring cloud gateway就變化多了,可以通過自定義KeyResolver來決定所需要的維度來限流,如:使用者id維度、ip維度、租戶維度等,靈活度大了。

微服務節點的限流

到了這裡,就是具體的微服務應用了,單個微服務所能承受的流量,做得好的話是能用jmeter測量出來的,可以通過這個值來參考設定。

再然後,到了具體服務中了,其實限流的維度就更多了,當然需要考量下是否需要加很多限流,脆弱敏感的服務就加些,比如計費等,或者用其他技術做限流,如:queue,然後固定住consumer數來消費,穩住速率。

所採用技術和gateway一樣,也可以自己實現,實現難度都還好。

流量大的系統,最好是用特定技術把income請求根據不同的維度劃分好獨立的執行緒池,不要相互影響;由本服務發起的到其他服務的請求呼叫,也需要單獨的執行緒池來處理。總之目標就是都獨立,不互相影響,即便其他服務慢了,自己也不慢。

因此,熔斷又出現了

當其他服務很慢,超時了,我方作為服務呼叫方不能被拖垮啊,這時,就斷開吧,用個指定的協議響應暫且認定為服務不可用之類的,等後續再補償回查。

當然關鍵服務是不能這麼處理的,只能是輔助服務。關鍵服務就必須要jmeter壓測提升效能。
  • 核心服務的梳理

  • 輔助服務熔斷的返回值+應對方式

  • 核心服務的壓側

2.2.6 配置合理的限流規則

限流規則包含三個部分:時間粒度,介面粒度,最大限流值。限流規則設定是否合理直接影響到限流是否合理有效。

對於限流時間粒度的選擇,我們既可以選擇1秒鐘不超過1000次,也可以選擇10毫秒不超過10次,還可以選擇1分鐘不超過6萬次,雖然看起這幾種限流規則都是等價的,但過大的時間粒度會達不到限流的效果,比如限制1分鐘不超過6萬次,就有可能6萬次請求都集中在某一秒內;相反,過小的時間粒度會削足適履導致誤殺很多本不應該限流的請求,因為介面訪問在細時間粒度上隨機性很大。所以,儘管越細的時間粒度限流整形效果越好,流量曲線越平滑,但也並不是越細越合適。

對於訪問量巨大的介面限流,比如秒殺,雙十一,這些場景下流量可能都集中在幾秒內,TPS 會非常大,幾萬甚至幾十萬,需要選擇相對小的限流時間粒度。相反,如果介面TPS很小,建議使用大一點的時間粒度,比如限制1分鐘內介面的呼叫次數不超過1000 次,如果換算成:一秒鐘不超過16次,這樣的限制就有點不合理,即便一秒內超過16次,也並沒有理由就拒絕介面請求,因為對於我們系統的處理能力來說,16次/秒的請求頻率太微不足道了。即便1000次請求都集中在1分鐘內的某一秒內,也並不會影響到系統的穩定性,所以1秒鐘16次的限制意義不大。

除了時間粒度之外,還需要根據不同的限流需求選擇不同介面粒度,比如:

1)限制微服務每個例項介面呼叫頻率
2)限制微服務叢集整體的訪問頻率
2)限制某個呼叫方對某個服務的呼叫頻率
3)限制某個呼叫方對某個服務的某個介面的訪問頻率
4)限制某服務的某個介面的訪問頻率
5)限制某服務的某類介面的訪問頻率

對於最大允許訪問頻率的設定,需要結合效能壓測資料、業務預期流量、線上監控資料來綜合設定,最大允許訪問頻率不大於壓測 TPS,不小於業務預期流量,並且參考線上監控資料。

在大促,秒殺,或者其他異常流量到來之前,我們需要事先通過實驗來驗證限流功能的有效性,用資料來證明限流功能確實能夠攔截非預期的異常流量。否則,就有可能會因為限流演算法的選擇不夠合適或者限流規則設定不合理,導致真正超預期流量到來的時候,限流不能起到保護服務的作用,超出預期的異常流量壓垮系統。

如何測試限流功能正確有效呢?儘管可以通過模擬流量或者線上流量回放等手段來測試,但是最有效的測試方法還是:通過導流的方式將流量集中到一小組機器上做真實場景的測試。對於測試結果,我們至少需要記錄每個請求的如下資訊:對應介面,請求時間點,限流結果 (通過還是熔斷),然後根據記錄的資料繪製成如下圖表:

從圖表中,我們可以一目瞭然的瞭解限流前與限流後的流量情況,可以清晰的看到限流規則和演算法對流量的整形是否合理有效。

除了事先驗證之外,我們還需要時刻監控限流的工作情況,實時瞭解限流功能是否執行正常。一旦發生限流異常,能夠在不重啟服務的情況下,做到熱更新限流配置:包括開啟關閉限流功能,調整限流規則,更換限流演算法等等。

3. 服務熔斷的設計

在股票市場,熔斷這個詞大家都不陌生,是指當股指波幅達到某個點後,交易所為控制風險採取的暫停交易措施。相應的,服務熔斷一般是指軟體系統中,由於某些原因使得服務出現了過載現象,為防止造成整個系統故障,從而採用的一種保護措施,所以很多地方把熔斷亦稱為過載保護。

3.1 服務熔斷和服務降級比較

兩者其實從有些角度看是有一定的類似性的

1.目的很一致,都是從可用性可靠性著想,為防止系統的整體緩慢甚至崩潰,採用的技術手段
2.最終表現類似,對於兩者來說,最終讓使用者體驗到的是某些功能暫時不可達或不可用
3.粒度一般都是服務級別,當然,業界也有不少更細粒度的做法,比如做到資料持久層(允許查詢,不允許增刪改)
4.自治性要求很高,熔斷模式一般都是服務基於策略的自動觸發,降級雖說可人工干預,但在微服務架構下,完全靠人顯然不可能,開關預置、配置中心都是必要手段

而兩者的區別也是明顯的:

1.觸發原因不太一樣,服務熔斷一般是某個服務(下游服務)故障引起,而服務降級一般是從整體負荷考慮
2.管理目標的層次不太一樣,熔斷其實是一個框架級的處理,每個微服務都需要(無層級之分),而降級一般需要對業務有層級之分(比如降級一般是從最外圍服務開始)
3.實現方式不太一樣

3.2 雪崩效應

在微服務架構中通常會有多個服務層呼叫,基礎服務的故障可能會導致級聯故障,進而造成整個系統不可用的情況,這種現象被稱為服務雪崩效應。服務雪崩效應是一種因“服務提供者”的不可用導致“服務消費者”的不可用,並將不可用逐漸放大的過程。

如果下圖所示:a作為服務提供者,b為c的服務消費者,d和e是b的服務消費者。a不可用引起了b的不可用,並將不可用像滾雪球一樣放大到d和e時,雪崩效應就形成了

3. 熔斷器(CircuitBreaker)

熔斷器的原理很簡單,如同電力過載保護器。它可以實現快速失敗,如果它在一段時間內偵測到許多類似的錯誤,會強迫其以後的多個呼叫快速失敗,不再訪問遠端伺服器,從而防止應用程式不斷地嘗試執行可能會失敗的操作,使得應用程式繼續執行而不用等待修正錯誤,或者浪費CPU時間去等到長時間的超時產生。熔斷器也可以使應用程式能夠診斷錯誤是否已經修正,如果已經修正,應用程式會再次嘗試呼叫操作。

熔斷器模式就像是那些容易導致錯誤的操作的一種代理。這種代理能夠記錄最近呼叫發生錯誤的次數,然後決定使用允許操作繼續,或者立即返回錯誤。
熔斷器開關相互轉換的邏輯如下圖