溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

SpringCloud中Hystrix.功能有什么用

發(fā)布時間:2021-09-28 11:42:40 來源:億速云 閱讀:161 作者:小新 欄目:編程語言

這篇文章主要介紹SpringCloud中Hystrix.功能有什么用,文中介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們一定要看完!

一、概述

在微服務(wù)架構(gòu)中,我們將系統(tǒng)拆分成了很多服務(wù)單元,各單元的應(yīng)用間通過服務(wù)注冊與訂閱的方式互相依賴。由于每個單元都在不同的進程中運行,依賴通過遠程調(diào)用的方式執(zhí)行,這樣就有可能因為網(wǎng)絡(luò)原因或是依賴服務(wù)自身間題出現(xiàn)調(diào)用故障或延遲,而這些問題會直接導(dǎo)致調(diào)用方的對外服務(wù)也出現(xiàn)延遲,若此時調(diào)用方的請求不斷增加,最后就會因等待出現(xiàn)故障的依賴方響應(yīng)形成任務(wù)積壓,最終導(dǎo)致自身服務(wù)的癱瘓。

所以我們引入了斷路器,類似于物理上的電路,當(dāng)電流過載時,就斷開電路,就是我們俗稱的“跳閘”。同理,服務(wù)間的調(diào)用也是如此,當(dāng)不斷的出現(xiàn)服務(wù)延遲、故障等影響到系統(tǒng)性能的調(diào)用,就把這個服務(wù)調(diào)用切斷!

Spring Cloud Hystrix 實現(xiàn)了斷路器、線程隔離等一系列服務(wù)保護功能。它也是基于 Net?ix 的開源框架 Hystrix 實現(xiàn)的,該框架的目標(biāo)在于通過控制那些訪問遠程系統(tǒng)、服務(wù)和第三方庫的節(jié)點,從而對延遲和故障提供更強大的容錯能力。Hystrix 具備服務(wù)降級、服務(wù)熔斷、線程和信號隔離、請求緩存、請求合并以及服務(wù)監(jiān)控等強大功能。

二、Hystrix 工作流程

1.創(chuàng)建 HystrixCommand(用在依賴的服務(wù)返回單個操作結(jié)果的時候) 或 HystrixObserableCommand(用在依賴的服務(wù)返回多個操作結(jié)果的時候) 對象。

2.命令執(zhí)行。其中 HystrixComand 實現(xiàn)了下面前兩種執(zhí)行方式;而 HystrixObservableCommand 實現(xiàn)了后兩種執(zhí)行方式:

execute():同步執(zhí)行,從依賴的服務(wù)返回一個單一的結(jié)果對象, 或是在發(fā)生錯誤的時候拋出異常。  queue():異步執(zhí)行, 直接返回 一個Future對象, 其中包含了服務(wù)執(zhí)行結(jié)束時要返回的單一結(jié)果對象。  observe():返回 Observable 對象,它代表了操作的多個結(jié)果,它是一個 Hot Obserable(不論 "事件源" 是否有 "訂閱者",都會在創(chuàng)建后對事件進行發(fā)布,所以對于 Hot Observable 的每一個 "訂閱者" 都有可能是從 "事件源" 的中途開始的,并可能只是看到了整個操作的局部過程)。  toObservable(): 同樣會返回 Observable 對象,也代表了操作的多個結(jié)果,但它返回的是一個Cold Observable(沒有 "訂閱者" 的時候并不會發(fā)布事件,而是進行等待,直到有 "訂閱者" 之后才發(fā)布事件,所以對于 Cold Observable 的訂閱者,它可以保證從一開始看到整個操作的全部過程)。

3.若當(dāng)前命令的請求緩存功能是被啟用的, 并且該命令緩存命中, 那么緩存的結(jié)果會立即以 Observable 對象的形式 返回。

4.檢查斷路器是否為打開狀態(tài)。如果斷路器是打開的,那么Hystrix不會執(zhí)行命令,而是轉(zhuǎn)接到 fallback 處理邏輯(第 8 步);如果斷路器是關(guān)閉的,檢查是否有可用資源來執(zhí)行命令(第 5 步)。

5.線程池/請求隊列/信號量是否占滿。如果命令依賴服務(wù)的專有線程池和請求隊列,或者信號量(不使用線程池的時候)已經(jīng)被占滿, 那么 Hystrix 也不會執(zhí)行命令, 而是轉(zhuǎn)接到 fallback 處理邏輯(第8步)。

6.Hystrix 會根據(jù)我們編寫的方法來決定采取什么樣的方式去請求依賴服務(wù)。

HystrixCommand.run() :返回一個單一的結(jié)果,或者拋出異常。  HystrixObservableCommand.construct(): 返回一個Observable 對象來發(fā)射多個結(jié)果,或通過 onError 發(fā)送錯誤通知。

7.Hystrix會將 "成功"、"失敗"、"拒絕"、"超時" 等信息報告給斷路器, 而斷路器會維護一組計數(shù)器來統(tǒng)計這些數(shù)據(jù)。斷路器會使用這些統(tǒng)計數(shù)據(jù)來決定是否要將斷路器打開,來對某個依賴服務(wù)的請求進行 "熔斷/短路"。

8.當(dāng)命令執(zhí)行失敗的時候, Hystrix 會進入 fallback 嘗試回退處理, 我們通常也稱該操作為 "服務(wù)降級"。而能夠引起服務(wù)降級處理的情況有下面幾種:

第4步: 當(dāng)前命令處于"熔斷/短路"狀態(tài),斷路器是打開的時候。  第5步: 當(dāng)前命令的線程池、 請求隊列或 者信號量被占滿的時候。  第6步:HystrixObservableCommand.construct() 或 HystrixCommand.run() 拋出異常的時候。

9.當(dāng)Hystrix命令執(zhí)行成功之后, 它會將處理結(jié)果直接返回或是以O(shè)bservable 的形式返回。

tips:如果我們沒有為命令實現(xiàn)降級邏輯或者在降級處理邏輯中拋出了異常, Hystrix 依然會返回一個 Observable 對象, 但是它不會發(fā)射任何結(jié)果數(shù)據(jù), 而是通過 onError 方法通知命令立即中斷請求,并通過onError()方法將引起命令失敗的異常發(fā)送給調(diào)用者。

三、Hystrix 熔斷保護機制

1.假設(shè)大量的請求數(shù)量超過了 HystrixCommandProperties.circuitBreakerRequestVolumeThreshold() 的閾值,熔斷器將會從關(guān)閉狀態(tài)變成打開狀態(tài);

2.假設(shè)依賴調(diào)用失敗的百分比超過了 HystrixCommandProperties.circuitBreakerErrorThresholdPercentage() 的閾值,熔斷器將會從關(guān)閉狀態(tài)變成打開狀態(tài);

3.在熔斷器處于打開狀態(tài)的期間,所有對這個依賴進行的調(diào)用都會短路,即不進行真正的依賴調(diào)用,返回失??;在等待(冷卻)的時間超過 HystrixCommandProperties.circuitBreakerSleepWindowInMilliseconds() 的值后,熔斷器將處于半開的狀態(tài),將允許單個請求去調(diào)用依賴,如果這次的依賴調(diào)用還是失敗,熔斷器狀態(tài)將再次變成打開,這個打開狀態(tài)持續(xù)時間是

4.HystrixCommandProperties.circuitBreakerSleepWindowInMilliseconds() 配置的值;如果這次的依賴調(diào)用成功,熔斷器狀態(tài)將變成關(guān)閉,后續(xù)依賴調(diào)用可正常執(zhí)行。

四、Hystrix 依賴隔離機制

線程池隔離

Hystrix 則使用“艙壁模式”實現(xiàn)線程池的隔離,它會為每一個依賴服務(wù)創(chuàng)建 一個獨立的線程池,這樣就算某個依賴服務(wù)出現(xiàn)延遲過高的情況,也只是對該依賴服務(wù)的調(diào)用產(chǎn)生影響,而不會拖慢其他的依賴服務(wù)。缺點是涉及到線程切換的性能損耗,但是官方給出的結(jié)果是性能損耗是可以接受的。

信號量隔離

信號量隔離可實現(xiàn)對依賴調(diào)用最高并發(fā)請求數(shù)的限制,每次依賴調(diào)用都會先判斷信號量是否達到閾值,如果達到極限值則拒絕調(diào)用。信號量的開銷遠比線程池的開銷小,但是它不能設(shè)置超時和實現(xiàn)異步訪問。所以,只有在依賴服務(wù)是足夠可靠的情況下才使用信號量 。以下是兩種配置信號量隔離的方式:

Setter 方式

HystrixCommand.Setter setter = HystrixCommand.Setter    .withGroupKey(HystrixCommandGroupKey.Factory.asKey("strGroupCommand"))    .andCommandKey(HystrixCommandKey.Factory.asKey("strCommand"))    .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("strThreadPool"));// 配置信號量隔離HystrixCommandProperties.Setter commandPropertiesSetter = HystrixCommandProperties.Setter().withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.SEMAPHORE);setter.andCommandPropertiesDefaults(commandPropertiesSetter);

注解方式

@HystrixCommand(fallbackMethod = "fallbackMethod",       commandProperties = {          // 設(shè)置隔離策略,THREAD 表示線程池 SEMAPHORE:信號池隔離          @HystrixProperty(name = "execution.isolation.strategy", value = "THREAD"),          // 當(dāng)隔離策略選擇信號池隔離的時候,用來設(shè)置信號池的大小(最大并發(fā)數(shù))          @HystrixProperty(name = "execution.isolation.semaphore.maxConcurrentRequests", value = "10"),      }  )

五、hystrix 實戰(zhàn)

SpringBoot 版本號:2.1.6.RELEASE

SpringCloud 版本號:Greenwich.RELEASE

1. pom.xml

<dependency>   <groupId>org.springframework.cloud</groupId>   <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>  </dependency>

2. 在 SpringBoot 的啟動類上引入 @EnableCircuitBreaker 注解,開啟斷路器功能。

3. 上面 hystrix 工作原理中提到斷路器有四種執(zhí)行方式:

execute() - 同步執(zhí)行

@HystrixCommand(fallbackMethod = "fallbackMethod") public String strConsumer() {  ResponseEntity<String> result = restTemplate.getForEntity("http://cloud-eureka-client/hello", String.class);  return result.getBody(); }

fallbackMethod —— 回調(diào)方法,在服務(wù)調(diào)用異常、斷路器打開、線程池/請求隊列/信號量占滿時會走回調(diào)邏輯。必須和服務(wù)方法定義在同一個類中,對修飾符沒有特定的要求,定義為 private、 protected、 public 均可。

queue() - 異步執(zhí)行

@HystrixCommand(fallbackMethod = "fallbackMethod", ignoreExceptions = {IllegalAccessException.class}) public Future<String> asyncStrConsumer() {  Future<String> asyncResult = new AsyncResult<String>() {   @Override   public String invoke() {    ResponseEntity<String> result = restTemplate.getForEntity("http://cloud-eureka-client/hello", String.class);    return result.getBody();   }  };  return asyncResult; }

ignoreExceptions 表示拋出該異常時不走降級回調(diào)邏輯,忽略此異常。

observe () 執(zhí)行方式

@HystrixCommand(observableExecutionMode = ObservableExecutionMode.EAGER) protected Observable<String> construct() {  ResponseEntity<String> result = restTemplate.getForEntity("http://cloud-eureka-client/hello", String.class);  return Observable.just(result.getBody()); }

toObservable() 執(zhí)行方式

@HystrixCommand(observableExecutionMode = ObservableExecutionMode.LAZY) protected Observable<String> construct() {  ResponseEntity<String> result = restTemplate.getForEntity("http://cloud-eureka-client/hello", String.class);  return Observable.just(result.getBody()); }

4. 命令名稱、分組以及線程池劃分

Hystrix 會根據(jù)組來組織和統(tǒng)計命令的告警、儀表盤等信息。

默認(rèn)情況下,Hystrix 會讓相同組名的命令使用同一個線程池。

通常情況下,盡量通過 HystrixThreadPoolKey 的方式來指定線程池的劃分,而不是通過組名的默認(rèn)方式實現(xiàn)劃分,因為多個不同的命令可能從業(yè)務(wù)邏輯上來看屬于同一個組,但是往往從實現(xiàn)本身上需要跟其他命令進行隔離。

@HystrixCommand(fallbackMethod = "fallbackMethod", groupKey = "strGroupCommand", commandKey = "strCommand", threadPoolKey = "strThreadPool") public String strConsumer(@CacheKey Long id) {  ResponseEntity<String> result = restTemplate.getForEntity("http://cloud-eureka-client/hello", String.class);  return result.getBody(); }

groupKey 默認(rèn)是類名,commandKey 默認(rèn)是方法名 ,threadPoolKey 默認(rèn)和 groupKey 一致。

5. 請求緩存和請求合并

5.1 請求緩存

緩存的作用和好處,真的是無需多言了。請求緩存,顧名思義,就是將對同一個 key 的請求結(jié)果,緩存下來。那么下次對這個 key 的請求,數(shù)據(jù)就直接在緩存中返回,減少響應(yīng)時間;

在 Hystrix 中使用緩存,主要是三個注解:@CacheResult、@CacheKey、@CacheRemove

@CacheResult:用來標(biāo)記請求結(jié)果應(yīng)該被緩存,必須與 @HystrixCommand 一起使用。  @CacheKey:用來修飾方法參數(shù),表示緩存的 key 名,優(yōu)先級高于 @CacheResult 設(shè)置的 key 名。  @CacheRemove:當(dāng)數(shù)據(jù)更新的時候,為了數(shù)據(jù)的一致性,我們需要使緩存失效。@CacheRemove 就是用來標(biāo)記請求結(jié)果的緩存失效(commandKey 是必填參數(shù),表示要失效的緩存 key)。

@CacheResult(cacheKeyMethod = "getCacheKey") @HystrixCommand(fallbackMethod = "fallbackMethod") public String strConsumer(@CacheKey Long id) {  ResponseEntity<String> result = restTemplate.getForEntity("http://cloud-eureka-client/hello", String.class);  return result.getBody(); }

5.2 請求合并

微服務(wù)架構(gòu)中的依賴通常通過遠程調(diào)用實現(xiàn),而遠程調(diào)用中最常見的問題就是通信消耗與連接數(shù)占用。

在高并發(fā)的情況之下,因通信次數(shù)的增加,總的通信時間消耗將會變得不那么理想。同時,因為依賴服務(wù)的線程池資源有限,將出現(xiàn)排隊等待與響應(yīng)延遲的清況。為了優(yōu)化這兩個問題,Hystrix 提供了 HystrixCollapser 來實現(xiàn)請求的合并,以減少通信消耗和線程數(shù)的占用。

@RestControllerpublic class UserConsumer { @Autowired private UserService userService; /**  * 通過 id 獲取用戶接口  *  * @param id  * @return  */ @HystrixCollapser(batchMethod = "getByIds", collapserProperties = {   // 10ms 內(nèi)的請求合并為一次批量請求   @HystrixProperty(name = "timerDelayInMilliseconds", value = "10"),   // 批處理過程中是否開啟緩存   @HystrixProperty(name = "requestCache.enabled", value = "10"), }) public String getById(Long id) {  return userService.getUserById(id); } /**  * 通過 ids 批量獲取用戶信息接口  *  * @param ids id 集合  * @return  */ @HystrixCommand public Set<String> getByIds(List<Long> ids) {  return userService.getUserByIds(ids); }}

雖然通過請求合并可以減少請求的數(shù)量以緩解依賴服務(wù)線程池的資源,但是在使用的時候也需要注意它所帶來的額外開銷:用于請求合并的延遲時間窗會使得依賴服務(wù)的請求延遲增高。

是否開啟緩存合并,我們一般考慮下面兩個因素:

如果依賴服務(wù)的請求命令本身是一個高延遲的命令,那么可以使用請求合并器,因為高延遲,時間窗的時間消耗顯得微不足道了。    如果一個時間窗內(nèi)只有1-2個請求,那么這樣的依賴服務(wù)不適合使用請求合并器。這種情況不但不能提升系統(tǒng)性能,反而會成為系統(tǒng)瓶頸;相反,如果一個時間窗內(nèi)具有很高的并發(fā)量,并且服務(wù)提供方也實現(xiàn)了批量處理接口,那么使用請求合并器可以有效減少網(wǎng)絡(luò)連接數(shù)量并極大提升系統(tǒng)吞吐量。

6. HystrixCommand 屬性介紹

@HystrixCommand(fallbackMethod = "fallbackMethod", groupKey = "strGroupCommand", commandKey = "strCommand", threadPoolKey = "strThreadPool",   commandProperties = {     // 設(shè)置隔離策略,THREAD 表示線程池 SEMAPHORE:信號池隔離     @HystrixProperty(name = "execution.isolation.strategy", value = "THREAD"),     // 當(dāng)隔離策略選擇信號池隔離的時候,用來設(shè)置信號池的大?。ㄗ畲蟛l(fā)數(shù))     @HystrixProperty(name = "execution.isolation.semaphore.maxConcurrentRequests", value = "10"),     // 配置命令執(zhí)行的超時時間     @HystrixProperty(name = "execution.isolation.thread.timeoutinMilliseconds", value = "10"),     // 是否啟用超時時間     @HystrixProperty(name = "execution.timeout.enabled", value = "true"),     // 執(zhí)行超時的時候是否中斷     @HystrixProperty(name = "execution.isolation.thread.interruptOnTimeout", value = "true"),     // 執(zhí)行被取消的時候是否中斷     @HystrixProperty(name = "execution.isolation.thread.interruptOnCancel", value = "true"),     // 允許回調(diào)方法執(zhí)行的最大并發(fā)數(shù)     @HystrixProperty(name = "fallback.isolation.semaphore.maxConcurrentRequests", value = "10"),     // 服務(wù)降級是否啟用,是否執(zhí)行回調(diào)函數(shù)     @HystrixProperty(name = "fallback.enabled", value = "true"),     // 是否啟用斷路器     @HystrixProperty(name = "circuitBreaker.enabled", value = "true"),     // 該屬性用來設(shè)置在滾動時間窗中,斷路器熔斷的最小請求數(shù)。例如,默認(rèn)該值為 20 的時候,如果滾動時間窗(默認(rèn)10秒)內(nèi)僅收到了19個請求, 即使這19個請求都失敗了,斷路器也不會打開。     @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "20"),     // 該屬性用來設(shè)置在滾動時間窗中,表示在滾動時間窗中,在請求數(shù)量超過 circuitBreaker.requestVolumeThreshold 的情況下,如果錯誤請求數(shù)的百分比超過50, 就把斷路器設(shè)置為 "打開" 狀態(tài),否則就設(shè)置為 "關(guān)閉" 狀態(tài)。     @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50"),     // 該屬性用來設(shè)置當(dāng)斷路器打開之后的休眠時間窗。 休眠時間窗結(jié)束之后,會將斷路器置為 "半開" 狀態(tài),嘗試熔斷的請求命令,如果依然失敗就將斷路器繼續(xù)設(shè)置為 "打開" 狀態(tài),如果成功就設(shè)置為 "關(guān)閉" 狀態(tài)。     @HystrixProperty(name = "circuitBreaker.sleepWindowinMilliseconds", value = "5000"),     // 斷路器強制打開     @HystrixProperty(name = "circuitBreaker.forceOpen", value = "false"),     // 斷路器強制關(guān)閉     @HystrixProperty(name = "circuitBreaker.forceClosed", value = "false"),     // 滾動時間窗設(shè)置,該時間用于斷路器判斷健康度時需要收集信息的持續(xù)時間     @HystrixProperty(name = "metrics.rollingStats.timeinMilliseconds", value = "10000"),     // 該屬性用來設(shè)置滾動時間窗統(tǒng)計指標(biāo)信息時劃分"桶"的數(shù)量,斷路器在收集指標(biāo)信息的時候會根據(jù)設(shè)置的時間窗長度拆分成多個 "桶" 來累計各度量值,每個"桶"記錄了一段時間內(nèi)的采集指標(biāo)。     // 比如 10 秒內(nèi)拆分成 10 個"桶"收集這樣,所以 timeinMilliseconds 必須能被 numBuckets 整除。否則會拋異常     @HystrixProperty(name = "metrics.rollingStats.numBuckets", value = "10"),     // 該屬性用來設(shè)置對命令執(zhí)行的延遲是否使用百分位數(shù)來跟蹤和計算。如果設(shè)置為 false, 那么所有的概要統(tǒng)計都將返回 -1。     @HystrixProperty(name = "metrics.rollingPercentile.enabled", value = "false"),     // 該屬性用來設(shè)置百分位統(tǒng)計的滾動窗口的持續(xù)時間,單位為毫秒。     @HystrixProperty(name = "metrics.rollingPercentile.timeInMilliseconds", value = "60000"),     // 該屬性用來設(shè)置百分位統(tǒng)計滾動窗口中使用 “ 桶 ”的數(shù)量。     @HystrixProperty(name = "metrics.rollingPercentile.numBuckets", value = "60000"),     // 該屬性用來設(shè)置在執(zhí)行過程中每個 “桶” 中保留的最大執(zhí)行次數(shù)。如果在滾動時間窗內(nèi)發(fā)生超過該設(shè)定值的執(zhí)行次數(shù),     // 就從最初的位置開始重寫。例如,將該值設(shè)置為100, 滾動窗口為10秒,若在10秒內(nèi)一個 “桶 ”中發(fā)生了500次執(zhí)行,     // 那么該 “桶” 中只保留 最后的100次執(zhí)行的統(tǒng)計。另外,增加該值的大小將會增加內(nèi)存量的消耗,并增加排序百分位數(shù)所需的計算時間。     @HystrixProperty(name = "metrics.rollingPercentile.bucketSize", value = "100"),     // 該屬性用來設(shè)置采集影響斷路器狀態(tài)的健康快照(請求的成功、 錯誤百分比)的間隔等待時間。     @HystrixProperty(name = "metrics.healthSnapshot.intervalinMilliseconds", value = "500"),     // 是否開啟請求緩存     @HystrixProperty(name = "requestCache.enabled", value = "true"),     // HystrixCommand的執(zhí)行和事件是否打印日志到 HystrixRequestLog 中     @HystrixProperty(name = "requestLog.enabled", value = "true"),   },   threadPoolProperties = {     // 該參數(shù)用來設(shè)置執(zhí)行命令線程池的核心線程數(shù),該值也就是命令執(zhí)行的最大并發(fā)量     @HystrixProperty(name = "coreSize", value = "10"),     // 該參數(shù)用來設(shè)置線程池的最大隊列大小。當(dāng)設(shè)置為 -1 時,線程池將使用 SynchronousQueue 實現(xiàn)的隊列,否則將使用 LinkedBlockingQueue 實現(xiàn)的隊列。     @HystrixProperty(name = "maxQueueSize", value = "-1"),     // 該參數(shù)用來為隊列設(shè)置拒絕閾值。 通過該參數(shù), 即使隊列沒有達到最大值也能拒絕請求。     // 該參數(shù)主要是對 LinkedBlockingQueue 隊列的補充,因為 LinkedBlockingQueue 隊列不能動態(tài)修改它的對象大小,而通過該屬性就可以調(diào)整拒絕請求的隊列大小了。     @HystrixProperty(name = "queueSizeRejectionThreshold", value = "5"),   } ) public String strConsumer() {  ResponseEntity<String> result = restTemplate.getForEntity("http://cloud-eureka-client/hello", String.class);  return result.getBody(); }

以上是“SpringCloud中Hystrix.功能有什么用”這篇文章的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對大家有幫助,更多相關(guān)知識,歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI