在複雜的分布式系統架構中,每個服務都有很多的依賴服務,而每個依賴服務都可能會故障, 如果服務沒有和自己的依賴服務進行隔離,那麼可能某乙個依賴服務的故障就會拖垮當前這個服務
舉例來說:某個服務有 30 個依賴服務,每個依賴服務的可用性非常高,已經達到了 99.99% 的高可用性
那麼該服務的可用性就是 99.99% - (100% - 99.99% * 30 = 0.3%)= 99.69%, 意味著 3% 的請求可能會失敗,因為 3% 的時間內系統可能出現了故障不可用了
對於 1 億次訪問來說,3% 的請求失敗也就意味著 300萬 次請求會失敗,也意味著每個月有 2個 小時的時間系統是不可用的, 在真實生產環境中,可能更加糟糕
上面的描述想表達的意思是:即使你每個依賴服務都是 99.99% 高可用性,但是一旦你有幾十個依賴服務, 還是會導致你每個月都有幾個小時是不可用的
下面畫圖分析說,當某乙個依賴服務出現了呼叫延遲或者呼叫失敗時,為什麼會拖垮當前這個服務? 以及在分布式系統中,故障是如何快速蔓延的?
簡而言之:
1.假設只有系統承受併發能力是 100 個執行緒,
2.c 出問題的時候,耗時增加,將導致當前進入的 40 個執行緒得不到釋放
3.後續大量的請求湧進來,也是先呼叫 c,然後又在這裡了
4.最後 100 個執行緒都被卡在 c 了,資源耗盡,導致整個服務不能提供服務
5.那麼其他依賴的服務也會出現上述問題,導致整個系統全盤崩潰
hystrix 是如何實現它的目標的?
通過 hystrixcommand 或者 hystrixobservablecommand 來封裝對外部依賴的訪問請求 d這個訪問請求一般會執行在獨立的執行緒中,資源隔離
對於超出我們設定閾值的服務呼叫,直接進行超時,不允許其耗費過長時間阻塞住。這個超時時間預設是 99.5% 的訪問時間,但是一般我們可以自己設定一下
為每乙個依賴服務維護乙個獨立的執行緒池,或者是 semaphore(訊號量),當執行緒池已滿時,直接拒絕對這個服務的呼叫
對依賴服務的呼叫的成功次數、失敗次數、拒絕次數、超時次數,進行統計
如果對乙個依賴服務的呼叫失敗次數超過了一定的閾值,自動進行熔斷
在一定時間內對該服務的呼叫直接降級,一段時間後再自動嘗試恢復
當乙個服務呼叫出現失敗、被拒絕、超時、短路(熔斷)等異常情況時,自動呼叫 fallback 降級機制
對屬性和配置的修改提供近實時的支援
偶發高併發狀態下:我們常用的處 理方式???
1.限流
2.削峰
3.請求合併
由上圖可見,使用請求合併,能夠減少併發執行 hystrixcommand 執行所需的執行緒數和網路連線數,當我們遇見偶發性高併發場景時,可以使用請求合併來降低伺服器和資料庫的壓力。
我們可以看到上圖:
1.當請求進來時,我們將請求放入佇列中;
2.設定定時任務去定時處理這些任務。如單個查詢id=1,2,3的去請求合併為批量查詢。
3.將批量查詢返回的結果集,通過請求的唯一標識code分發到每個請求中去。
1.請求發起時,建立hystrixcollapser實力,判斷是否存在快取,如果存在直接返回。否則繼續執行。
2.判斷當前合併請求設定的作用域,不同的作用域獲得不同請求合併器:
作用域如下:
1. global context
tomcat 所有呼叫執行緒,對乙個依賴服務的任何乙個 command 呼叫都可以被合併在一起,hystrix 就傳遞乙個hystrixrequestcontext對於這一級別,沒有更多的解釋,官方推薦使用下面的那乙個
2. user request context
tomcat 內某乙個呼叫執行緒,將某乙個 tomcat 執行緒對某個依賴服務的多個 command 呼叫合併在一起 此種方式在前面 request cache 中已經演示過怎麼配置請求上下文了 此種級別是 hystrix預設級別
3. object modeling 基於物件的請求合併
如果有幾百個物件,遍歷後依次呼叫每個物件的某個方法,可能導致發起幾百次網路請求,基於 hystrix
可以自動將對多個物件模型的呼叫合併到一起此方式也不推薦使用,沒有更多的描述 上面的乙個能實現的重要點是:使 用請求合併 command 的時候,是需要單獨提供乙個批量獲取資料的介面。 hystrix 負責把某乙個時間視窗內的 command 合併成乙個 collapser ,然後由你去呼叫這個批量獲取資料介面
3.拿到請求合併器之後,呼叫submitrequest()方法將提交的批量請求進行合併。這裡面使用到了
timerlistener-》collapsedtask類設定了定時任務,間隔10ms執行中斷,將10ms內的所有請求執行
createobservablecommand,將返回的結果集,response仍然塞入collapsedrequest(shardrequests)中,將返回結果設定為observable,其中observable就持有請求和響應的所有資訊。
4.每乙個請求執行緒,在發起請求之後,會呼叫this.toobservable().toblocking().tofuture().get(),通過阻塞的方式從future中拿去響應結果,在合併執行緒執行完之後,會將結果集放入observable中,此時future.get()就會拿到結果,介面返回。
使用請求合併技術最大的代價就是:導致延遲大幅度增強,因為需要將一定時間內的多個請求合併
比如:傳送 10 個請求,每個請求大概是 5 毫秒可以返回,要把 10 個請求合併在乙個 command
內,統一執行,那麼就有乙個等待時間,假設是 10 毫秒,執行時間就可能變成 15 毫秒,延遲了 10
毫秒;通常情況下請求不會恰好在等待時間到達剛好 10 毫秒的時候才發出去請求,所以延遲還可以降一半,本例子為 5ms。
這個延遲開銷是否是有意義的取決於正在執行的
command,並且與併發數量也有關係,因為有兩個策略會觸發合併請求發出去,乙個是請求達到閥值,乙個是等待時間達到閥值。請求合併並不適合在單執行緒中等待執行,這樣會加大延遲。
比如 10 個請求單個資料總耗時將花費 50 毫秒,平均 5 毫秒;當併發 10 執行緒請求的時候,那麼只需要 5 毫秒即可拿到資料;
同樣,為了盡快達到合併發出請求閥值,在併發下會更快的達到閥值,批量請求資料可能需要耗時 10 毫秒。
總起看起來,不合併至需要 5 毫秒,合併之後可能需要 15 毫秒(10 毫秒請求介面,因為是批量資料的介面,假設會比單個耗時一點 + 5毫秒的等待時間)
單個介面獲取資料與批量介面獲取資料去掉獲取資料的時間因素(往往是業務聚合等因素),那麼就是網路開銷了;
如果特定命令同時大量使用並且可以將數十個甚至數百個呼叫一起批量處理,那麼由於 hystrix減少了所需的執行緒數量和網路連線數量,因此實現的吞吐量通常遠遠超過了這個代價。所以總結:
請求合併適合在高延遲 + 大量併發的情況下使用
每個請求就 2ms,batch 需要 8~10ms,延遲增加了 4~5 倍
每個請求本來就 30ms~50ms,batch 需要35ms~55ms,延遲增加不太明顯
Hystrix 簡單請求合併
頻繁的呼叫provider接太浪費了,就有了將多個請求合併為乙個請求的方式。首先在provider中提供乙個請求合併的介面 restcontroller public class usercontroller public list getuserbyids pathvariable string ...
Hystrix熔斷原理
netflix的開源元件hystrix的流程 圖中流程的說明 將遠端服務呼叫邏輯封裝進乙個hystrixcommand。對於每次服務呼叫可以使用同步或非同步機制,對應執行execute 或queue 判斷熔斷器 circuit breaker 是否開啟或者半開啟狀態,如果開啟跳到步驟8,進行回退策略...
Hystrix熔斷原理
netflix的開源元件hystrix的流程 圖中流程的說明 將遠端服務呼叫邏輯封裝進乙個hystrixcommand。對於每次服務呼叫可以使用同步或非同步機制,對應執行execute 或queue 判斷熔斷器 circuit breaker 是否開啟或者半開啟狀態,如果開啟跳到步驟8,進行回退策略...