一、Hystrix簡介
1.1、分布式系統面臨的問題
複雜的分布式系統的應用程式有數十個依賴關系,每個依賴關系在某些時候将不可避免的失敗。
對于高并發的系統,若某個依賴系統出問題會導緻系統瞬時飽和,也會導緻服務間的延遲增加,隊列、線程和其他系統資源緊張,進而導緻系統發生更多級聯故障。
上述問題表示需要對系統的故障進行隔離,以達到單個依賴關系失敗,不影響整個系統的目的。
1.2、Hystrix是什麼
Hystrix是一個用于處理分布式系統的延遲和容錯的開源庫。
在分布式系統裡,許多依賴不可避免的會調用失敗,如逾時、異常等,Hystrix能夠保證在一個依賴出現問題的時候,不會導緻整體服務失敗,避免級聯故障,以提高分布式系統的彈性。
1.3、Hystrix能幹什麼
Hystrix能幹的事情主要有:
- 服務隔離、降級、熔斷、限流、快速失敗
- 請求合并、請求緩存
- 接近實時的監控
1.4、Hystrix的設計原則
- 防止任何單獨的依賴使用容器(如Tomcat)所有的使用者線程。
- 切斷負載并快速失敗,而不是排隊。
- 盡可能提供回退以保護使用者受故障。
- 使用隔離技術(如艙壁、泳道、斷路器模式)來限制任何一個依賴的影響。
- 通過接近實時的名額,監控和報警,優化發現時間。
- 通過配置更改的低延遲傳播,優化恢複時間。
- 防止各種用戶端執行失敗,而不僅僅是網絡通信。
二、java項目直接使用Hystrix
2.1、加入依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
官方示例:https://github.com/Netflix/Hystrix/wiki/How-To-Use
2.2、基本使用(直接在java中引入jar)
2.2.1、HystrixCommand和HystrixObservableCommand
1、前者的指令邏輯寫在run(); 後者的指令邏輯寫在construct() 。
2、前者的run()是由新建立的線程執行; 後者的construct()是由調用程式線程執行。
3、前者一個執行個體隻能向調用程式發送單條資料,比如上面例子中run()隻能傳回一個String結果; 後者一個執行個體可以順序發送多條資料,可以順序調用多個onNext(),便實作了向調用程式發送多條資料。
2.2.2、指令執行方法
execute(), queue(), observe(), toObservable() 這四個方法來觸發執行 run()/construct(), 一個執行個體隻能執行一次這4個方法,注意:HystrixObservableCommand沒有execute()和queue()。
1、execute():以同步阻塞的方式執行run()。調用exectue()後,Hystrix先建立一個新線程運作run(),接着調用程式要在execute()調用處一緻阻塞着,直到run()運作完成。
2、queue():以異步非阻塞方式執行run()。一調用queue(),就直接傳回一個future對象,同時Hystrix建立一個新線程運作run(), 調用程式通過Future.get()拿到run()傳回結果,而Future.get()是阻塞執行的。
3、observe():事件注冊前執行run()/construct():
第一步是事件注冊前,先調用observe()自動觸發執行run()/construct():如果繼承的時HystrixCommand, hystrix将建立新線程非阻塞執行run();如果繼承的時HystrixObservableCOmmand,将以調用程式線程阻塞執行construct()。
第二部是從observe()傳回迪歐澳洋程式調用subscribe()完成事件注冊,如果run()/construct()執行成本,則觸發onNext()和onCompleted(),如果執行異常則觸發onError()。
4、toObservable():事件注冊後執行run()/construct():
第一步是事件注冊前,一調用toObservable()方法就直接傳回一個Observable對象
第二部調用subscribe()完成事件注冊後自動觸發執行run()/construct(): 如果繼承的時HystrixCommand, hystrix将建立新線程非阻塞執行run(), 調用程式不必等待run(); 如果繼承的時HystrixObservableCommand, 将以調用程式線程阻塞執行construct(), 調用程式等待construct()執行完才能繼續往下走,如果run()/construct()執行成功,則觸發onNext()和onCompleted(), 如果執行異常則觸發onError()。
2.2.3、指令名稱
預設情況下,指令名是從類名派生的:getClass(), getSimpleName(): 要明确地定義名字,通過HystrixCommand或HystrixObservableCommand構造函數傳入,例如:
super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("HelloWorld1"))
.andCommandKey(HystrixCommandKey.Factory.asKey("HelloWorld")
.andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey(HelloWorldPool")));
1、GroupKey: 是HystrixCommand不可缺少的配置,其他配置均為可選,例如:
HystrixCommandGroupKey的作用有2個:
- 起到分組監控、報警的作用
- 是在不配置HystrixThreadPoolKey的情況下,起到分組線程池的作用。即預設使用HystrixCommandGroupKey去命名線程池。使用同一個HystrixCommandGroupKey且沒有自定義HystrixThreadPoolKey的HystrixCommand将使用同一個線程池。
2、commandKey:指令的辨別名稱
3、ThreadPoolKey:線程池的辨別名稱。
2.2.4、Command Thread Pool 設定
雖然HystrixCommandGroupKey可以起到隔離線程池的作用,但無法起到對線程池進行精細配置的作用。這裡就需要線程池進行配置,例如:
super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("HelloWorld1")) .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("MyThreadPool")) .andThreadPoolPropertiesDefaults(
HystrixThreadPoolProperties.Setter() .withCoreSize(20) .withKeepAliveTimeMinutes(1)
.withMaxQueueSize(-1)
)
.andCommandPropertiesDefaults( HystrixCommandProperties.Setter()
.withExecutionTimeoutInMilliseconds(100)
) );
1、andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey(“MyThreadPool”)) 這是配置ThreadPoolKey。如果需要在同一個GroupKey下面配置不同的ThreadPool,就需要這個配置。
2、andThreadPoolPropertiesDefaults 表示設定線程池預設的屬性值,包括:
1)withCoreSize(20):用來配置線程池大小,不配置的話使用的預設值是10。
2)withKeepAliveTimeMinutes(1):用來配置核心線程數空閑時keep alive的時長,預設 1 mins,一般不需要修改。
3)withMaxQueueSize(-1):配置線程池任務隊列的大小,預設值為-1,表示SynchronousQueue将被使用,即意味着其實這個隊列隻是一個交換器,任務将直接交給工作線程處理。如果工作線程不足,那任務将被拒絕;如果使用任務正整數,LinkedBlockingQueue将被使用。
2.2.5、使用注意事項
編寫完自己的Command智慧,使用的時候,每次都需要new一個新對象,再調用execute()方法。注意:不要調用run()方法,否則熔斷、隔離等功能是不生效的。
2.2.6、錯誤傳播
run()裡面抛出的HystrixBadRequestException隻用于計數,方法抛出的所有其他異常都作為失敗,會觸發getFallBack()和斷路器邏輯。
你可以包裝你想要抛出的異常,HystrixBadRequestException适用的情況,如舉報非法參數或非系統故障,不會計入失敗的名額,不會觸發回退邏輯。
2.2.7、快速失敗
快速失敗是指沒有重寫getFallBack,遇到異常後直接抛出,程式停止運作。
2.2.8、回退/降級
所謂降級,指在Hystrix執行非核心鍊路功能失敗的情況下,我們如何處理,比如我們傳回預設值等。觸發時,會調用fallback設定降級方法,在降級方法中,可以設定預設的降級傳回資料。
使用fallback機制很簡單,繼承HystrixCommand隻需要重新getFallback(),繼承HystrixObservableCommand隻需要重新resumeWithFallback().
如下情況将會觸發回退:
- 非HystrixBadRequestException異常
- run()/construct()異常
- 熔斷器啟動
- 線程池/信号量已滿
2.2.9、請求緩存
Hystrix支援将一個請求結果緩存起來,下一個具有相同key的請求将直接從緩存中取出結果,減少請求開銷。使用方式如下:
- 通過getCacheKey()在一個HystrixCommand或一個HystrixObservableCommand對象上實作該方法來啟動請求緩存。
- 另外要求這多個請求必須在同一個上下文。通過HystrixRequestContext.initialContext()和context.shutdown()可以建立一個context,這兩條語句間的所有請求都處于同一個context。
- 通過isResponseFromCache()可檢測傳回結果是否來自緩存。
2.2.10、合并請求
Hystrix支援n個請求自動合并為一個請求,這将是多次網絡互動變為一次,極大節省開銷。注意一點,兩個請求能自動合并的前提是兩者足夠”近“,即兩者啟動執行的間隔時長要足夠小,預設為10ms,即超過10ms将不自動合并。
Hystrix支援2中請求合并方式:請求範圍和全局範圍。這是在collapser構造中配置的,預設為request-scoped。請求範圍是指在一個HystrixRequestContexts上下文中的請求;全局範圍指跨HystrixRequestContexts的請求。
三、SpringCloud中使用Hystrix
3.1、加入依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
</dependency>
3.2、啟動類上添加
@SpringBootApplication
@EnableHystrixDashboard
@EnableCircuitBreaker
@EnableHystrix
3.3、在Controller的方法上添加Hystrix配置
@HystrixCommand(fallbackMethod = "error", commandProperties = {
@HystrixProperty(name="execution.isolation.strategy", value = "THREAD"),
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "4000"),
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = ”50") }, threadPoolProperties = {
@HystrixProperty(name = "coreSize", value = "1"),
@HystrixProperty(name = "maxQueueSize", value = "10"),
@HystrixProperty(name = "keepAliveTimeMinutes", value = "1000"), @HystrixProperty(name = "queueSizeRejectionThreshold", value = "8"), @HystrixProperty(name = "metrics.rollingStats.numBuckets", value = "12"), @HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds", value = "1500")
})
Hystrix支援兩種方式定義HystrixCommand,一種是将類繼承自HystrixCommand類,并重寫run方法;另一種是在方法頭上寫注解的方式,使用注解的方式代碼會比較清晰,将Hystrix代碼和業務代碼隔離開。
3.4、Hystrix的Dashboard
3.4.1、概述
Hystrix自帶了Dashboard,如果監控單個執行個體,可以很友善的通過Hystrix的dashboard進行檢視運作情況,直接進入http://localhost:8080/hystrix
Hystrix Dashboard共支援三種不同的監控方式,依次為:
- 預設的叢集監控:通過 http://turbine-hostname:port/turbine.stream開啟,實作對預設叢集的監控。
- 指定的叢集監控:通過 http://turbine- hostname:port/turbine.stream?cluster=[clusterName] 開啟,實作對clusterName叢集的監控。
- 單體應用的監控:通過 http://hystrix-app:port/hystrix.stream 開啟,實作對具體某個服務執行個體的監控。
前兩者都對叢集的監控,需要整合Turbine才能實作。
例子如下:
說明如下:
1、delay:該參數用來控制伺服器上輪詢監控資訊的延遲時間,預設為2000毫秒,可以通過配置該屬性來降低用戶端的網絡和CPU消耗。
2、title:該參數對應了頭部标題Hystrix Stream之後的内容,預設會使用具體監控執行個體的url,可以通過配置該資訊來展示更合适的标題。
3、監控資訊的左上部分找到兩個重要的圖形資訊:一個實心圓和一條曲線:
- 實心圓:共有兩種含義:它通過顔色的變化代表了執行個體的健康程度,它的監控度從綠色、黃色、橙色、紅色遞減。該實心圓出了顔色的變化之外,它的大小也會根據執行個體的請求流量發生變化,流量越大該實心圓就越大。是以通過該實心圓的展示,就可以在大量的執行個體中快速的發現故障執行個體和高壓力執行個體。
- 曲線:用來記錄2分鐘内的流量相對變化,可以通過它來觀察到流量的上升和下降趨勢。
3.5、Hystrix的參數配置
3.5.1、屬性配置
Hystrix使用Netflix的配置管理庫Archaius作為配置屬性的預設實作。官方配置文檔:https://github.com/Netflix/Hystrix/wiki/Configuration
每個屬性有四個優先級,依次增大:
1、代碼的全局預設值
2、動态全局預設屬性:可以使用全局屬性檔案來更改全局預設屬性
3、代碼示例預設:定義特定于執行個體的預設值,比如在HystrixCommand構造函數中設定的值。
4、動态執行個體屬性:可以動态設定執行個體特定的值,進而覆寫前面三個預設級别,格式是:hystrix.command.指令key.屬性名稱=值
3.5.2、請求上下文
1、requestCache.enabled:設定是否開啟請求的緩存功能,預設為true
2、requestLog.enabled:設定是否開啟請求的日志功能,預設為true
3.5.3、指令執行
1、execution.isolation.strategy
訓示HystrixCommand.run()執行哪個隔離政策,選項:
- THREAD:它在單獨的線程上執行,并發請求受線程池中的線程數限制
- SEMAPHORE:它在調用線程上執行,并發請求受信号計數的限制
說明:
- 官方推薦使用線程隔離政策,預設也是按照線程隔離進行處理
- 信号量隔離的方式是限制了總的并發數,每一次請求過了,請求線程和調用依賴服務的線程是同一個線程,那麼如果不設計遠端RPC調用(沒有網絡開銷)則使用信号量來隔離,更為輕量,開銷更小。
- 信号量的大小可以動态調整,線程池的大小不可用動态調整
- 配置示例:
@HystrixCommand(fallbackMethod = "error", commandProperties = {
@HystrixProperty(name="execution.isolation.strategy", value = "THREAD") })
2、execution.isolation.thread.timeoutInMilliseconds
表示請求線程總逾時時間,如果超過這個設定的時間,hystrix就會調用fallback方法。value為毫秒,預設為1000ms。
示例:
@HystrixCommand(fallbackMethod = "error",
commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",
value = "4000") })
3、execution.timeout.enabled
表示當逾時後是否會觸發fallback方法,預設為true
4、execution.isolation.thread.interrupteOnTimeout
表示HystrixCommand.run()在發生逾時時是否應該中斷執行,預設為true。
5、execution.isolation.thread.interruptOnCance
表示HystrixCommand.run()在發生取消時,是否應該中斷執行,預設為false
6、execution.isolation.semaphore.maxConcurrentRequests
當隔離政策使用semaphore時,最大的兵法請求量,如果請求超過這個最大值将拒絕後續的請求,預設值為10。
3.5.4、回退
1、fallback.isolation.semaphore.maxConcurrentRequests
設定HystrixCommand.getFallback()方法允許從調用線程進行請求的最大數量,預設10;如果達到最大并發限制,則随後的請求将被拒絕,并抛出異常。
2、fallback.enable
開啟fallback功能,預設為true
3.5.5、斷路器
1、circuitBreaker.enabled
設定是否将使用斷路器來跟蹤健康狀況,并且如果斷路器跳閘,則将其斷路。預設為true。
2、circuitBreaker.requestVolumeThreshold
設定滾動視窗中将使電路跳閘的最小請求數量,預設為20.
熔斷器在整個統計時間内是否開啟的門檻值,每個熔斷器預設維護10個bucket,每秒一個bucket,每個bucket記錄成功、失敗、逾時、拒絕的狀态,該門檻值預設為20次。也就是一個統計視窗時間内(10秒鐘)至少請求20次,熔斷器才會啟動。
3、circuitBreaker.sleepWindowInMilliseconds
熔斷器預設工作時間,預設為5秒,熔斷器中斷請求5秒後會進入半打開狀态,放部分流量過去重試,如果重試成功則會恢複正常請求。
4、circuitBreaker.errorThresholdPercentage
熔斷器錯誤百分比門檻值,預設為50%。擋在一個時間視窗内出錯率超過50%後,熔斷器會自動啟動。熔斷器啟動後會自動轉發到配置的fallbackMethod,進行降級處理。
5、circuitBreaker.forceOpen
斷路器強制開關,如果設定為true, 則表示強制打開熔斷器,所有請求都會拒絕,預設為false
6、circuitBreaker.forceClose
斷路器強制開關,如果設定為true, 則表示強制關閉熔斷器,所有請求都會允許,預設false。
3.5.6、度量名額
1、metrics.rollingStats.timeInMilliseconds
設定統計滾動視窗的持續時間,以毫秒為機關,預設10秒。
2、metrics.rollingStats.numBuckets
該屬性設定滾動統計視窗分詞的桶的數量,預設10
3、metrics.rollingPercentile.enable
表示執行延遲是否應該跟蹤和計算為百分比。如果被禁用,則所有彙總統計傳回為-1。預設為true。
4、metrics.rollingPercentile.timeInMilliseconds
設定滾動視窗的持續時間,在該視窗中保留執行時間以允許百分數計算(機關毫秒),預設一分鐘。
5、metrics.rollingPercentile.numBuckets
設定rollingPercentile視窗被分成的桶的數量,預設為6
6、metrics.rollingPercentile.bucketSize
設定每個存儲桶的最大執行次數,如果在執行多次,将在桶的開頭重寫,預設100
例如,如果存儲區大小設定為100,并表示10秒的存儲區視窗,但在此期間發生500次執行,則隻有最後100次執行将保留在該10秒存儲區中。
7、metrics.healthSnapshot.intervalInMilliseconds
設定允許執行健康快照之間等待的時間(以毫秒為機關),預設500
3.5.7、ThreadPool配置
1、coreSize
線程池核心線程數,預設10
2、MAXIMUMSIZE
設定線程池大小,預設10
3、maxQueueSize
配置線程池任務隊列的大小,預設值為-1。當使用-1時,SynchronousQueue将被使用,即意味着其實這個隊列隻是一個交換器,任務将被直接交給工作線程處理。如果工作線程不足,那任務将被拒絕;如果使用任何正整數,LinkedBlockingQueue将被使用。
4、queueSizeRejectionThreshold
表示等待隊列超過門檻值後,開始拒絕線程請求,預設值為5,如果maxQueueSize為-1,則該屬性失效。
5、keepAliveTimeMinutes
設定活動保持時間,以分鐘為機關,預設1分鐘
6、allowMaxinumSizeToDivergeFromCoreSize
設定maxinumSize剩下。這個值可以等于或高于coreSize,預設為false。
四、Hystrix的工作流程
4.1、Hystrix的工作流程圖
4.2、工作流程說明
1、建立一個HystrixCommand或HystrixObservableCommand執行個體來想其他元件發出操作請求,通過構造方法來建立執行個體。
2、執行指令
3、緩存判斷:檢查緩存内是否有對應指令的結果,如果有,則将緩存結果直接以Observable對象的形式傳回。
4、斷路器判斷:檢查Circuit Breaker的狀态,如果狀态為開啟,Hystrix将不會執行對應指令,而是直接進入失敗處理狀态;如果狀态為關閉,Hystrix會繼續執行。
5、線程池、任務隊列、信号量的檢查:确認是否有足夠資源執行操作指令。當線程池和隊列(或者信号量,當不使用線程池隔離模式的時候)資源滿的時候,hystrix将不會執行對應指令,并且會直接進入失敗的處理狀态。
6、HystrixObservableCommand.construct()和HystrixCommand.run():如果資源充足,Hystrix将會執行操作指令,調用最終都會到這兩個方法:HystrixObservableCommand.construct()或HystrixCommand.run()。如果執行指令的時間逾時,執行線程會抛出TimeoutException異常。Hystrix會抛棄結果,并進入失敗處理狀态。如果指令執行成本,Hystrix會進行一系列的資料記錄,然後傳回執行的結果。
7、統計斷路器的健康情況:Hystrix會根據記錄的資料來計算失敗比率,一旦失敗比率達到某一門檻值,将自動開啟Circuit Breaker。
8、降級:如果在Command中實作了HystrixCommand.getFallback()或HystrixObservableCommand.resumeWithFallback()方法,Hystrix會傳回對應方法的結果。如果沒有實作這些方法的話,仍然Hystrix會傳回一個空的Observable對象,并且可以通過onError來終止并處理錯誤。
調用不同的方法傳回不同的結果:
- execute():将會抛出異常
- queue():将會傳回一個Future對象,如果調用它的get()方法将會抛出異常。
-
observe()和toObservable():都會傳回上述的Observable對象
9、傳回成功:如果Hystrix執行成功,傳回的響應取決于步驟2中調用指令。
execute():阻塞型方法,傳回單個結果(或者抛出異常)
queue():異步方法,傳回一個Future對象,可以從中取出單個結果(或者抛出異常)
observe():傳回Observable對象
toObservable():傳回Observable對象。
五、熔斷機制
5.1、雪崩效應概述
多個微服務之間調用的時候,假設微服務A調用微服務B和微服務C,微服務B和微服務C又調用了其他的微服務,這就是所謂的“扇出”。如果扇出的鍊路上,某個微服務的調用響應時間過長或不可用,對微服務A的調用就會占用越來越多的系統資源,進而引起系統崩潰,這就是所謂的“雪崩效應”。
熔斷機制是應對雪崩效應的一種微服務鍊路保護機制。當扇對外連結路的某個微服務不可用或相應時間太長,會進行服務的降級,進而熔斷該節點微服務的調用,快速傳回錯誤的響應資訊。當檢測到該節點微服務調用響應正常後,恢複調用鍊路。
在Spring Cloud架構裡,熔斷機制通過Hystrix實作。Hystrix會監控微服務間調用的狀況,當失敗的調用到一定門檻值,預設是5秒内20次調用失敗,就會啟動熔斷機制。熔斷機制的注解是@HystrixCommand。
5.2、熔斷類型
在Hystrix裡,熔斷又分為三種情況:半熔斷、熔斷打開、熔斷關閉。
1、熔斷打開:請求不再進行調用目前服務,内部設定時鐘一般為MTTR(平均故障處理時間),當打開時長達到所設時鐘,則進入半熔斷狀态。
2、半熔斷:部分請求根據規則調用目前服務,如果請求成功,且符合規則,則認為目前服務恢複正常,關閉熔斷。
3、熔斷關閉:熔斷關閉不會對服務進行熔斷。
5.3、斷路器原理
5.3.1、斷路器在上面情況下開始起作用
涉及到斷路器的三個重要參數:快照時間窗、請求總數門檻值、錯誤百分比門檻值。
1、快照時間窗:段利器确定是否打開,需要統計一些請求和錯誤資料,而統計的時間範圍就是快照時間窗,預設是最近10秒。
2、請求總數門檻值:在快照時間窗内,必須滿足請求總數門檻值才有資格熔斷。預設為20,意味着在10秒内,如果該Hystrix指令的調用次數不足20次,及時所有的請求都逾時或其他原因失敗,斷路器都不會打開。
3、錯誤百分比一緻:當請求綜述在快照時間窗内超過了門檻值,比如發生了30次調用,如果在這30次調用中,有15次發生了逾時異常,也就是超過50%的錯誤百分比,在預設設定50%門檻值情況下,這時就會将斷路器打開。
5.3.2、斷路器開啟或關閉的條件
1、當滿足一定門檻值的時候(預設10秒内超過20個請求次數)
2、當失敗率達到一定的時候(預設10秒内超過50%的請求失敗)
3、到達以上門檻值,斷路器将會開啟
4、當開啟的時候,所有請求都不會進行轉發
5、一段時間之後(預設是5秒),這個時候斷路器是半開狀态,會讓其中一個請求進行轉發:如果成功,斷路器會關閉;若失敗,繼續開啟,重複4和5步驟。
5.3.3、斷路器打開之後
1、在用請求調用的時候,将不會調用主邏輯,而是直接調用降級fallback。通過斷路器,實作了自動地發現錯誤,并将降級邏輯切換為主邏輯,減少響應延遲的效果。
2、原來的主邏輯要如何恢複呢?
對于這一問題,hystrix也為我們實作了自動恢複的功能。
當斷路器打開,對主邏輯進行熔斷後,hystrix會啟動一個休眠時間窗,在這個時間窗内,降級邏輯是臨時的成為主邏輯,當休眠時間窗到期,斷路器将進入半開狀态,釋放一次請求到原來的主邏輯上,如果此次請求正常傳回,那麼斷路器将繼續閉合,主邏輯恢複,如果這次請求依然有問題,斷路器繼續進入打開狀态,休眠時間窗重新計時。
六、線程隔離機制
6.1、線程隔離示意圖
6.2、依賴隔離機制
Hystrix提供了兩種隔離政策:線程池隔離和信号量隔離,預設采用線程池隔離。
6.2.1、線程池隔離
Hystrix使用艙壁模式來實作線程池的隔離。他會為每一個Hystrix指令建立一個獨立的線程池,不同服務通過使用不同線程池,彼此間将不受影響,這樣就算某個Hystrix指令包裝下的依賴服務出現延遲過高的情況,也隻是對該依賴服務的調用産生影響,而不會拖慢其他的服務。
這種方式需要為每個依賴的服務申請線程池,有一定的資源消耗;通過線程池大小可以控制并發量,當線程池飽和時,可以提前拒絕服務,防止依賴問題擴散。建議線程池不要設定過大,否則大量阻塞線程有可能會拖慢伺服器。
6.2.1.1、線程池隔離的好處
1、應用自身得到了完全的保護,不會受不可控的依賴服務影響。
2、可以有效的降低接入新服務的風險。
3、當依賴的服務從失效恢複正常後,它的線程池會被清理,并能夠馬上恢複健康的服務,相比之下,容器級别的清理恢複速度要慢得多。
4、當依賴的服務出現配置錯誤的時候,線程池會快速的反應出此問題(通過失敗次數、延遲、逾時、拒絕等名額的增減情況)。同時,可以在不影響應用功能的情況下,通過實時的動态屬性重新整理來處理它。
5、當依賴的服務因失血機制調整等原因,造成其性能出現很大變化時,此時線程池的監控名額資訊會反映出這樣的變化。同時,可以通過實時動态重新整理自身應用對依賴服務的門檻值進行調整以适應依賴方的改變。
6.2.2、信号量隔離
線程隔離會帶來線程開銷,有些成績(比如無網絡請求場景)可能會因為用開銷換隔離得不償失,為此hystrix提供了信号量隔離,當服務的并發數大于信号量門檻值時,将進入fallback。
實作方式是使用一個原子計數器(或信号量)來記錄目前有多少個線程在允許,請求過來了,先判斷計數器的數值,若超過設定的最大線程個數,則丢棄該類型的新請求,若不超過,則執行技術操作請求來計數器+1。
信号隔離與線程隔離最大不同在于執行依賴代碼的線程依然是請求線程;而線程池方式下業務請求線程和執行依賴的服務的線程,不是同一個線程。
重學Hystrix原理
一、背景
微服務架構下,多個服務經常互相依賴,一個系統故障,會導緻依賴他的系統出現“雪崩”。
為了解決不同服務間依賴導緻雪崩問題,就需要做“故障隔離”,即當你依賴的服務不可用是,你的服務能自動開啟自我保護功能。這就是Hystrix的初衷。
二、Hystrix簡介
Hystrix 中文含義是豪豬,因為背上長滿刺,可以用自我保護的能力。
Hystrix是Netflix開源的容錯架構,使服務有自我保護的能力。
Hystrix用來解決的問題:
- 自動發現依賴服務故障–提供近實時的監控
- 提供優雅降級機制
- 可快速失敗
Hystrix設計的原則:
- 防止依賴的資源故障而hang住主線程進而導緻資源耗盡
- 失敗達到門檻值後,使用者請求直接拒絕
- 提供回退接口給使用方來處理依賴故障
- 故障恢複時,可快速發現并恢複
Hystrix如何實作的這些設計目标
- 使用指令模型,把所有對外部服務的依賴調用封裝到了HystrixCommand或HystrixObservableCommand對象中,并把對象放到獨立的線程中
- 每個依賴都單獨有個線程池,若線程池資源耗盡,則拒絕請求
- 記錄下請求成功、失敗、逾時、線程拒絕
- 當服務錯誤百分比超過門檻值,熔斷器開關自動打開,一段時間内停止對該服務的所有請求
- 當請求失敗(包括逾時、被熔斷等),可執行降級邏輯
- 可近實時地配置監控名額
2.1、Hystrix的工作流程
1、構造一個HystrixCommand和HystrixObservableCommand對象,用于封裝請求,并在構造方法配置請求被執行需要的參數
2、執行指令,Hystrix提供了4種執行指令的方法
3、判斷是否使用緩存響應請求,如果用了緩存,且緩存可用,則直接使用緩存響應請求。Hystrix支援緩存請求,但需要使用者自定義啟動。-- 這個功能用的少
4、判斷熔斷器是否打開,若打開,則跳到第八步
5、判斷線程池、隊列、信号量是否已滿,已滿則跳到第八步
6、執行HystrixCommand.run()或HystrixObservalbeCommand.construct(), 如果執行失敗或逾時,跳到第八步;否則,跳到第九步
7、統計熔斷器監控名額
8、走Fallback備用邏輯
9、傳回請求響應。
從流程圖上可知,第五步線程池、隊列、信号量如已滿,則會執行第七步邏輯,更新熔斷器統計資訊。
2.1.1、執行指令的4種方法
1、execute():相當于異步串行。
以同步阻塞的方式執行run(),支援接收一個值對象,hystrix會從線程池中取一個線程來執行,并等待傳回值。
2、queue():
以異步非阻塞方式執行run(), 支援接收一個值對象,調用queue()直接傳回一個Future對象,可通過Future.get()拿到run()的傳回結果。
3、observe()
事件注冊前執行run()/construct(),支援接收多個值對象,取決于發射源。調用observe()會自動觸發run()/construct(), 無論是否存在訂閱者
observe()的特點:
1)調用observe()會傳回一個Observable對象
2)調用這個Observable對象的subscribe()方法完成事件注冊,進而擷取結果。
2.2、Hystrix容錯
Hystrix主要提供下面的幾種容錯方法:
1)資源隔離
2)熔斷
3)降級
2.2.1、資源隔離
資源隔離,主要指調用依賴服務的請求線程與請求主線程的隔離。Hystrix提供了兩種線程隔離方式:線程池和信号量
線程隔離-- 線程池
Hystrix通過指令模式,把發送請求的線程,和調用依賴服務的線程,分成2個線程,并為每個依賴服務即每一種command建立一個線程池。這樣就可以通過線程池,一是做異步,避免級聯雪崩故障;二是通過線程池的飽和政策來做拒絕服務。
線程池的缺點:增加了計算開銷,即新啟動線程增加了排隊、排程、上下文切換等開銷
線程隔離 – 信号量
上面說了線程池的缺點,對于延遲極低(即響應速度快)的依賴服務調用時,線程池方式代理的開銷超過了它帶來的好處。是以這樣的場景适合用信号量來隔離。
信号量隔離:發起請求的線程 和 調用依賴服務的線程 是同一個線程。
信号量的方式,當向依賴服務發起請求時,首先要擷取一個信号量,然後才能發起請求,但信号量有限,當并發請求超過了信号量個數時,後續的請求都會被拒絕,進入fallback流程。
信号量隔離方式主要通過控制并發請求裡,防止請求線程大面積阻塞。
信号量方式由于不必新開線程,開銷小于線程池方式。
Hystrix預設是線程池方式做隔離。
2.2.2、熔斷
熔斷的比喻:就好比是保險絲,當電流達到門檻值時,保險絲會斷,保證家裡電器不會被損壞。這就是熔斷。
Hystrix的熔斷器(Circuit Breaker)也是類似作用:Hystrix在運作過程中會向每個commandKey對應的熔斷器報告成功、失敗、逾時、拒絕的狀态,熔斷器統計這些資訊來決策是否觸發熔斷。
熔斷打開後,後續請求會快速傳回。然後隔一段時間(預設5s)後熔斷器嘗試半開,放入一部分流量盡量,相當于對依賴服務做健康檢查,如果發現依賴服務恢複了,則關閉熔斷器。
熔斷器的工作原理:
熔斷器的工作詳細流程:
第一步:調用allowRequest()判斷是否允許将請求發到線程池。即看熔斷器是否強制打開或強制關閉:circuitBreaker.forceOpen
第二步:調用isOpen() 判斷熔斷器開關是否打開,如關閉則拒絕,如打開,則判斷是否達到設定的門檻值
第三步:調用allowSingleTest() 判斷是否允許單個請求同行,檢查依賴服務是否恢複
2.2.3、回退降級
Hystrix的降級,主要指當依賴服務故障時,保證目前服務仍可運作,進而提高目前服務的健壯性。
要支援回退或降級處理,可以重新HystrixCommand的getFallBack方法或HystrixObservableCommand的resumeWithFallback方法
Hystrix在以下幾種情況下會走降級邏輯:
1)執行construct() 或 run() 抛出異常
2)熔斷器打開
3)指令的線程池和隊列或信号量的容量超門檻值,指令被拒絕
4)指令執行逾時
總之,回退政策,不單是指在熔斷器開啟後會執行,在調用依賴服務失敗後都會執行。
降級回退的方式
1)Fail Fast 快速失敗
即沒有重新降級邏輯,直接抛出異常
2)Fail Silent 無聲失敗
指不抛出異常,而是在降級方法中傳回null, 空Map,空List等
3)Fallback:Static
在降級方法中傳回靜态預設值
4)Fallback:Stubbed
當指令傳回一個包含多個字段的複合對象時,适合以Stubbed方式回退
5)Fallback:Cache via Network
有時調用依賴服務失敗,可以從緩存服務(如reids)中查詢舊資料版本。由于又會發起遠端調用,是以建議重新封裝一個Command , 使用不同的ThreadPoolKey,與主線程池進行隔離。
6)Primary+Secondary with Fallback
有時系統有兩種行為:主要和次要,或主要和故障轉移
更多詳見:https://blog.csdn.net/loushuiyifan/article/details/82702522