來源公眾號孤獨煙 , 作者孤獨煙
引言
首先,之所以談這個話題呢,是發現現在很多人對微服務的設計缺乏認識,所以寫一篇掃盲文。當然,考慮到目前大多微服務的文章都是口水文,煙哥爭取將實現方式講透,點清楚,讓大家有所收獲!
OK,我要先說明一下,我有很長一段時間將服務降級和服務熔斷混在一起,認為是一回事!
為什么我會有這樣的誤解呢?
針對下面的情形,如圖所示
當Service A調用Service B,失敗多次達到一定閥值,Service A不會再去調Service B,而會去執行本地的降級方法!
對于這么一套機制:在Spring cloud中結合Hystrix,將其稱為熔斷降級!
所以我當時就以為是一回事了,畢竟熔斷和降級是一起發生的,而且這二者的概念太相近了!
后面接觸了多了,發現自己理解的還是太狹隘了,因此本文中帶著點我自己的見解,大家如果有不同意見,請輕噴!畢竟還有很多人認為兩者是一致的!
正文
服務雪崩
OK,我們從服務雪崩開始講起!假設存在如下調用鏈
而此時,Service A的流量波動很大,流量經常會突然性增加!那么在這種情況下,就算Service A能扛得住請求,Service B和Service C未必能扛得住這突發的請求。
此時,如果Service C因為抗不住請求,變得不可用。那么Service B的請求也會阻塞,慢慢耗盡Service B的線程資源,Service B就會變得不可用。緊接著,Service A也會不可用,這一過程如下圖所示
如上圖所示,一個服務失敗,導致整條鏈路的服務都失敗的情形,我們稱之為服務雪崩。
ps:誰發明的這個詞,真是面試裝13必備!
那么,服務熔斷和服務降級就可以視為解決服務雪崩的手段之一。
服務熔斷
那么,什么是服務熔斷呢?
服務熔斷:當下游的服務因為某種原因突然變得不可用或響應過慢,上游服務為了保證自己整體服務的可用性,不再繼續調用目標服務,直接返回,快速釋放資源。如果目標服務情況好轉則恢復調用。
需要說明的是熔斷其實是一個框架級的處理,那么這套熔斷機制的設計,基本上業內用的是斷路器模式,如Martin Fowler提供的狀態轉換圖如下所示
- 最開始處于closed狀態,一旦檢測到錯誤到達一定閾值,便轉為open狀態;
- 這時候會有個reset timeout,到了這個時間了,會轉移到half open狀態;
- 嘗試放行一部分請求到后端,一旦檢測成功便回歸到closed狀態,即恢復服務;
業內目前流行的熔斷器很多,例如阿里出的Sentinel,以及最多人使用的Hystrix
在Hystrix中,對應配置如下
//滑動窗口的大小,默認為20 circuitBreaker.requestVolumeThreshold //過多長時間,熔斷器再次檢測是否開啟,默認為5000,即5s鐘 circuitBreaker.sleepWindowInMilliseconds //錯誤率,默認50% circuitBreaker.errorThresholdPercentage
每當20個請求中,有50%失敗時,熔斷器就會打開,此時再調用此服務,將會直接返回失敗,不再調遠程服務。
直到5s鐘之后,重新檢測該觸發條件,判斷是否把熔斷器關閉,或者繼續打開。
這些屬于框架層級的實現,我們只要實現對應接口就好!
服務降級
那么,什么是服務降級呢?
這里有兩種場景:
- 當下游的服務因為某種原因響應過慢,下游服務主動停掉一些不太重要的業務,釋放出服務器資源,增加響應速度!
- 當下游的服務因為某種原因不可用,上游主動調用本地的一些降級邏輯,避免卡頓,迅速返回給用戶!
其實乍看之下,很多人還是不懂熔斷和降級的區別!
其實應該要這么理解:
- 服務降級有很多種降級方式!如開關降級、限流降級、熔斷降級!
- 服務熔斷屬于降級方式的一種!
可能有的人不服,覺得熔斷是熔斷、降級是降級,分明是兩回事啊!其實不然,因為從實現上來說,熔斷和降級必定是一起出現。
因為當發生下游服務不可用的情況,這個時候為了對最終用戶負責,就需要進入上游的降級邏輯了。因此,將熔斷降級視為降級方式的一種,也是可以說的通的!
我撇開框架,以最簡單的代碼來說明!上游代碼如下
try{ //調用下游的helloWorld服務 xxRpc.helloWorld(); }catch(Exception e){ //因為熔斷,所以調不通 doSomething(); }
注意看,下游的helloWorld服務因為熔斷而調不通。此時上游服務就會進入catch里頭的代碼塊,那么catch里頭執行的邏輯,你就可以理解為降級邏輯!
什么,你跟我說你不捕捉異常,直接丟頁面?
OK,那我甘拜下風,當我理解錯誤!
服務降級大多是屬于一種業務級別的處理。當然,我這里要講的是另一種降級方式,也就是開關降級!這也是我們生產上常用的另一種降級方式!
做法很簡單,做個開關,然后將開關放配置中心!在配置中心更改開關,決定哪些服務進行降級。至于配置變動后,應用怎么監控到配置發生了變動,這就不是本文該討論的范圍。
那么,在應用程序中部下開關的這個過程,業內也有一個名詞,稱為埋點!
那接下來最關鍵的一個問題,哪些業務需要埋點?
一般有以下方法
(1)簡化執行流程
自己梳理出核心業務流程和非核心業務流程。然后在非核心業務流程上加上開關,一旦發現系統扛不住,關掉開關,結束這些次要流程。
(2)關閉次要功能
一個微服務下肯定有很多功能,那自己區分出主要功能和次要功能。然后次要功能加上開關,需要降級的時候,把次要功能關了吧!
(3)降低一致性
假設,你在業務上發現執行流程沒法簡化了,愁啊!也沒啥次要功能可以關了,桑心啊!那只能降低一致性了,即將核心業務流程的同步改異步,將強一致性改最終一致性!
可是這些都是手動降級,有辦法自動降級么?
這里我摸著良心說,我們在生產上沒弄自動降級!因為一般需要降級的場景,都是可以預見的,例如某某活動。假設,平時真的有突發事件,流量異常,也有監控系統發郵件通知,提醒我們去降級!
當然,這并不代表自動降級不能做,因此以下內容可以認為我在胡說八道,因為我在生產上沒實踐過,只是頭腦大概想了下,如果讓我來做自動降級我會怎么實現:
- (1)自己設一個閾值,例如幾秒內失敗多少次,就啟動降級
- (2)自己做接口監控(有興趣的可以了解一下RxJAVA),達到閾值就走推送邏輯。怎么推呢?比如你配置是放在Git上,就用Jgit去改配置中心的配置。如果配置放數據庫,就用Jdbc去改。
- (3)改完配置中心的配置后,應用就可以自動檢測到配置的變化,進行降級!(這句不了解的,了解一下配置中心的熱刷新功能)