導讀
今天和大家聊一聊在Spring Cloud微服務框架實踐中,比較核心但是又很容易把人搞得稀裡糊塗的一個問題,那就是在Spring Cloud中Hystrix、Ribbon以及Feign它們三者之間在處理微服務呼叫超時從而觸發熔斷降級的關係是什麼?
我們知道在Spring Cloud微服務體系下,微服務之間的互相呼叫可以通過Feign進行宣告式呼叫,在這個服務呼叫過程中Feign會通過Ribbon從服務註冊中心獲取目標微服務的伺服器地址列表,之後在網路請求的過程中Ribbon就會將請求以負載均衡的方式打到微服務的不同例項上,從而實現Spring Cloud微服務架構中最為關鍵的功能即服務發現及客戶端負載均衡呼叫。
另一方面微服務在互相呼叫的過程中,為了防止某個微服務的故障消耗掉整個系統所有微服務的連線資源,所以在實施微服務呼叫的過程中我們會要求在呼叫方實施針對被呼叫微服務的熔斷邏輯。而要實現這個邏輯場景在Spring Cloud微服務框架下我們是通過Hystrix這個框架來實現的。
呼叫方會針對被呼叫微服務設定呼叫超時時間,一旦超時就會進入熔斷邏輯,而這個故障指標資訊也會返回給Hystrix元件,Hystrix元件會根據熔斷情況判斷被調微服務的故障情況從而開啟熔斷器,之後所有針對該微服務的請求就會直接進入熔斷邏輯,直到被調微服務故障恢復,Hystrix斷路器關閉為止。
Hystrix、Feign及Ribbon的配置說明
接下來我們先來看看在Spring Cloud微服務系統中Hystrix、Feign及Ribbon的常用配置都有哪些以及它們的使用場景分別是什麼?
Hystrix配置說明
在Spring Cloud微服務體系中Hystrix主要被用於實現實現微服務之間網路呼叫故障的熔斷、過載保護及資源隔離等功能。而要正確使用Hystrix提供的這些功能就需要對Hystrix常用的配置有一定深入的瞭解,否則你會發現使用過程中認為一定會生效的配置常常不會起作用,接下來我們就按照引數配置的型別對Hystrix中的常見配置做一個梳理。
1)、執行緒隔離相關配置
Hystrix具備的重要關鍵特性之一就是它能夠實現對第三方服務依賴的資源隔離,而隔離最常見的方式是通過執行緒池資源的隔離來實現的,Hystrix會為每個第三方服務依賴配置單獨的執行緒池資源,從而避免對第三方服務依賴的請求佔用應用主執行緒資源以免造成系統雪崩。Hystrix中關於執行緒隔離相關的配置如下:
hystrix: command: #全域性預設配置 default: #執行緒隔離相關 execution: timeout: #是否給方法執行設定超時時間,預設為true。一般我們不要改。 enabled: true isolation: #配置請求隔離的方式,這裡是預設的執行緒池方式。還有一種訊號量的方式semaphore,使用比較少。 strategy: threadPool thread: #方式執行的超時時間,預設為1000毫秒,在實際場景中需要根據情況設定 timeoutInMilliseconds: 1000 #發生超時時是否中斷方法的執行,預設值為true。不要改。 interruptOnTimeout: true #是否在方法執行被取消時中斷方法,預設值為false。沒有實際意義,預設就好! interruptOnCancel: false
2)、熔斷器相關配置
熔斷器是Hystrix最主要的功能,它開啟和關閉的時機、靈敏度及準確性是Hystrix是否能夠發揮重要的關鍵,而在Hystrix中與熔斷器相關的幾個配置如下:
hystrix: command: #全域性預設配置 default: #熔斷器相關配置 circuitBreaker: #說明:是否啟動熔斷器,預設為true。我們使用Hystrix的目的就是為了熔斷器,不要改,否則就不要引入Hystrix。 enabled: true #說明1:啟用熔斷器功能視窗時間內的最小請求數,假設我們設定的視窗時間為10秒, #說明2:那麼如果此時預設值為20的話,那麼即便10秒內有19個請求都失敗也不會開啟熔斷器。 #說明3:此配置項需要根據介面的QPS進行計算,值太小會有誤開啟熔斷器的可能,而如果值太大超出了時間視窗內的總請求數,則熔斷永遠也不會被觸發 #說明4:建議設定一般為:QPS*視窗描述*60% requestVolumeThreshold: 20 #說明1:熔斷器被開啟後,所有的請求都會被快速失敗掉,但是何時恢復服務是一個問題。熔斷器開啟後,Hystrix會在經過一段時間後就放行一條請求 #說明2:如果請求能夠執行成功,則說明此時服務可能已經恢復了正常,那麼熔斷器會關閉;相反執行失敗,則認為服務仍然不可用,熔斷器保持開啟。 #說明3:所以此配置的作用是指定熔斷器開啟後多長時間內允許一次請求嘗試執行,官方預設配置為5秒。 sleepWindowInMilliseconds: 5000 #說明1:該配置是指在通過滑動視窗獲取到當前時間段內Hystrix方法執行失敗的機率後,根據此配置來判斷是否需要開啟熔斷器 #說明2:這裡官方的預設配置為50,即視窗時間內超過50%的請求失敗後就會開啟熔斷器將後續請求快速失敗掉 errorThresholdPercentage: 50 #說明:是否強制啟用熔斷器,預設false,沒有什麼場景需要這麼配置,忽略! forceOpen: false #說明:是否強制關閉熔斷器,預設false,沒有什麼場景需要這麼配置,忽略! forceClosed: false
3)、Metrics(統計器)相關配置
Hystrix是否正常工作最主要的依賴就是根據捕獲的呼叫指標資訊來判斷是否開啟或者關閉熔斷器,而影響Hystrix行為很重要的因素就是以下Hystrix關於Metrics的配置。
hystrix: command: #全域性預設配置 default: metrics: rollingStats: #說明:此配置用於設定Hystrix統計滑動視窗的時間,單位為毫秒,預設設定為10000毫秒,即一個滑動視窗預設統計的是10秒內的請求資料。 timeInMilliseconds: 10000 #說明2:此屬性指定了滑動統計視窗劃分的桶數。預設為10。 #說明2:需要注意的是,metrics.rollingStats.timeInMilliseconds % metrics.rollingStats.numBuckets == 0必須成立,否則就會丟擲異常 numBuckets: 10 rollingPercentile: #說明1:此屬性配置統計方法是否響應時間百分比,預設為true。 #說明2:Hystrix會統計方法執行1%,10%,50%,90%,99%等比例請求的平均耗時用來生成統計圖表。 #說明3:如果禁用該引數設定false,那麼所有彙總統計資訊(平均值、百分位數)將返回-1。 enabled: true #說明:統計響應時間百分比時的視窗大小,預設為60000毫秒,即1分鐘 timeInMilliseconds: 60000 #說明1:此屬性用於設定滑動百分比視窗要劃分的桶數,預設為6。 #說明2:需要注意的是,metrics.rollingPercentile.timeInMilliseconds % metrics.rollingPercentile.numBuckets == 0必須成立,否則會丟擲異常 numBuckets: 6 #說明1:該屬性表示統計響應時間百分比,每個滑動視窗的桶內要儲存的請求數,預設為100。 #說明2:即預設10秒的桶內,如果執行了500次請求,那麼只有最後100次請求執行的資訊會被儲存到桶內。 #說明3:增加這個值會增加記憶體消耗量,一般情況下無需更改。 bucketSize: 100 healthSnapshot: #說明1:該引數配置了健康資料統計器(會影響Hystrix熔斷)中每個桶的大小,預設為500毫秒。 #說明2:在統計時Hystrix通過metrics.rollingStats.timeInMilliseconds / metrics.healthSnapshot.intervalInMilliseconds計算出桶數。 #說明3:在視窗滑動時,每滑過一個桶的時間就統計一次當前視窗內請求的失敗率。 intervalInMilliseconds: 500
在上述關於Metrics的配置中出現了兩個比較頻繁的概念:滑動視窗、桶,Hystrix的統計器就是由滑動視窗來實現的。關於滑動視窗舉一個非常形象的例子,假設你坐在一輛大巴車的上,車窗外是一排排筆直的大樹,車輛在高速地行駛著,大樹迅速地從車窗滑過,如果用每棵樹來代表一個網路請求,用大巴車的行駛代表時間的流逝。那麼大巴車的視窗就是一個非常典型的滑動視窗,而你通過這個車窗能夠看到的大樹就是Hystrix要統計的資料。
桶(bucket)是Hystrix統計滑動視窗資料時的最小單位。同樣以上面的例子為例,在大巴車行駛非常快的情況下,如果每掠過一棵樹就統計一次視窗內大樹的資料的話,會造成比較大的開銷,而如果我們將車窗分成10份,大巴車行駛時每掠過視窗的1/10就統計一次資料,那麼開銷就會小很多了。所以,Hystrix中的桶就是滑動視窗1/N的概念。
4)、執行緒池相關配置
在前面提到過Hystrix實現對第三方服務依賴資源隔離最主要的方式就是通過執行緒池,而Hystrix內執行緒的使用是基於Java內建執行緒池的簡單封裝,通過以下Hystrix執行緒池引數我們可以控制執行Hystrix命令的執行緒池的行為。具體如下:
hystrix: command: default: #說明:核心執行緒池的大小,預設值是10 coreSize: 10 #說明:是否允許執行緒池擴充套件到最大執行緒池數量,預設為false。 allowMaximumSizeToDivergeFromCoreSize: false #說明:執行緒池中執行緒的最大數量,預設值是10。此配置項單獨配置時並不會生效,需要啟用allowMaximumSizeToDivergeFromCoreSize maximumSize: 10 #說明1:作業佇列的最大值,預設值為-1。表示佇列會使用SynchronousQueue,此時值為0,Hystrix不會向佇列記憶體放作業。 #說明2:如果此值設定為一個正int型,佇列會使用一個固定size的LinkedBlockingQueue,此時在核心執行緒池都忙碌的情況下,會將作業暫時存放在此佇列內,但是超出此佇列的請求依然會被拒絕 maxQueueSize: -1 #設定佇列拒絕請求的閥值,預設為5。 queueSizeRejectionThreshold: 5 #控制執行緒在釋放前未使用的時間,預設為1分鐘。 keepAliveTimeMinutes: 1
Ribbon配置說明
Ribbon在Spring Cloud中對於支援微服之間的通訊發揮著非常關鍵的作用,其主要功能包括客戶端負載均衡器及用於中間層通訊的客戶端。在基於Feign的微服務通訊中無論是否開啟Hystrix,Ribbon都是必不可少的,Ribbon的配置引數主要如下:
ribbon: #說明:同一臺例項的最大自動重試次數,預設為1次,不包括首次 MaxAutoRetries: 1 #說明:要重試的下一個例項的最大數量,預設為1,不包括第一次被呼叫的例項 MaxAutoRetriesNextServer: 1 #說明:是否所有的操作都重試,預設為true OkToRetryOnAllOperations: true #說明:從註冊中心重新整理伺服器列表資訊的時間間隔,預設為2000毫秒,即2秒 ServerListRefreshInterval: 2000 #說明:使用Apache HttpClient連線超時時間,單位為毫秒 ConnectTimeout: 3000 #說明:使用Apache HttpClient讀取的超時時間,單位為毫秒 ReadTimeout: 3000 #說明:初始伺服器列表,不需要手工配置,在執行時動態根據註冊中心更新 listOfServers: www.microsoft.com:80,www.yahoo.com:80,www.google.com:80
以上配置方式將對所有的微服務呼叫有效,如果想針對單獨的微服務進行配置,使用“微服務名.ribbon”這樣的配置方式即可,例如:
bike: ribbon: ReadTimeout: 30000 operation: ribbon: ReadTimeout: 30000
Feign配置說明
Feign是一款Java語言編寫的HttpClient繫結器,在Spring Cloud微服務中用於實現微服務之間的宣告式呼叫,Feign自身可以支援多種HttpClient工具包,例如OkHttp及Apache HttpClient,針對不同的HttpClient其預設常見配置如下:
feign: hystrix: enabled: true client: config: #JDK預設HttpURLConnection 實現的 Http Client配置 default: #連線超時時間 connectTimeout: 5000 #讀取超時時間 readTimeout: 5000 #錯誤解碼器 errorDecoder: com.wudimanong.client.common.FeignClientErrorDecoder #解碼器 encoder: com.wudimanong.client.common.FeignClientEncoder #編碼器 decoder: com.wudimanong.client.common.FeignClientDecoder
Feign、Hystrix、Ribbon的超時配置關係
在前面的內容我們分別單獨梳理了Feign、Hystrix及Ribbon三者常見的配置,針對各自的特性功能配置我們並沒有異議,但是我們也看到它們都有針對微服務超時的配置,而在開啟熔斷器功能後,這些超時配置會影響到熔斷器及服務降級邏輯的行為,那麼它們之間超時的配置有什麼關係呢?如下:
如上圖所示,在Spring Cloud中使用Feign進行微服務呼叫分為兩層:Ribbon的呼叫及Hystrix的呼叫。所以Feign的超時時間就是Ribbon和Hystrix超時時間的結合,而如果不啟用Hystrix則Ribbon的超時時間就是Feign的超時時間配置,Feign自身的配置會被覆蓋。
而如果開啟了Hystrix,那麼Ribbon的超時時間配置與Hystrix的超時時間配置則存在依賴關係,因為涉及到Ribbon的重試機制,所以一般情況下都是Ribbon的超時時間小於Hystrix的超時時間,否則會出現以下錯誤:
2019-07-12 11:10:20,238 397194 [http-nio-8084-exec-2] WARN o.s.c.n.z.f.r.s.AbstractRibbonCommand - The Hystrix timeout of 40000ms for the command operation is set lower than the combination of the Ribbon read and connect timeout, 80000ms.
那麼Ribbon和Hystrix的超時時間配置的關係具體是什麼呢?如下:
Hystrix的超時時間=Ribbon的重試次數(包含首次)*(ribbon.ReadTimeout+ribbon.ConnectTimeout)
而Ribbon的重試次數的計算方式為:
Ribbon重試次數(包含首次)=1+ribbon.MaxAutoRetries+ribbon.MaxAutoRetriesNextServer+(ribbon.MaxAutoRetries*ribbon.MaxAutoRetriesNextServer)
以上圖中的Ribbon配置為例子,Ribbon的重試次數=1+(1+1+1)*(30000+10000),所以Hystrix的超時配置應該>=160000毫秒。在Ribbon超時但Hystrix沒有超時的情況下,Ribbon便會採取重試機制;而重試期間如果時間超過了Hystrix的超時配置則會立即被熔斷(fallback)。
如果不配置Ribbon的重試次數,則Ribbon預設會重試一次,加上第一次呼叫Ribbon的重試次數為2次,以上述配置為例Hystrix超時時間配置為2*40000=80000,由於很多情況下,大家一般不會主動配置Ribbon的重試次數,所以這裡需要注意下!強調下,以上超時配置的值只是示範,超時配置有點大不太合適實際的線上場景,大家根據實際情況設定即可!
PS:以上整理應該是全網最全的一份,如果覺得有用辛苦轉發下,給在使用Spring Cloud微服務的朋友們送去一點小溫暖!^_^
「微信搜尋公眾號-》無敵碼農」 獲取更多Spring Cloud微服務技能!