javascript
SpringCloud-06-Hystrix断路器
Hystrix斷路器
1. 概述
1. 分布式系統(tǒng)面臨的問題
復雜分布式體系結(jié)構(gòu)中的應用程序有數(shù)十個依賴關(guān)系,每個依賴關(guān)系在某些時候?qū)⒉豢杀苊獾厥 _@就造成有可能會發(fā)生服務雪崩。那么什么是服務雪崩呢?
多個微服務之間調(diào)用的時候,假設微服務A調(diào)用微服務B和微服務C,微服務B和微服務C又調(diào)用其他的微服務,這就是所謂的“扇出”(向一把打開的折扇)。如果扇出的鏈路上某個微服務的調(diào)用響應時間過長或者不可用,對微服務A的調(diào)用就會占用越來越多的系統(tǒng)資源,進而引起系統(tǒng)崩潰,這就是所謂的”雪崩效應“。也就是系統(tǒng)的 高可用 受到了破壞。
對于高流量的應用來說,單一的后端依賴可能會導致所有服務器上的所有資源在幾秒內(nèi)飽和。比失敗更糟的是,這些應用程序還可能導致服務之間的延遲增加,備份隊列,線程和其他系統(tǒng)資源緊張,導致整個系統(tǒng)發(fā)送更過的級聯(lián)故障。這些都表示需要對故障和延遲進行隔離和管理,以便 單個依賴關(guān)系的失敗,不能取消整個應用程序或系統(tǒng)。
所以,通常當發(fā)現(xiàn)一個模塊下的某個實例失敗后,這時候這個模塊依然還會接受流量,然后這個有問題的模塊還調(diào)用了其他的模塊,這樣就會發(fā)生級聯(lián)故障,或者叫做雪崩。而面對這種糟糕的問題,我們就應該采取 服務降級、服務熔斷 等方式來解決。
2. Hystrix是什么
Hystrix是一個用于處理分布式系統(tǒng)的延遲和容錯的開源庫,在分布式系統(tǒng)里,許多依賴不可避免的會調(diào)用失敗,比如超時、異常等,Hystrix能夠保證在一個依賴出問題的情況下,不會導致整個服務失敗,避免級聯(lián)故障,以提高分布式系統(tǒng)的彈性。
“斷路器”本身是一種開關(guān)裝置,當某個服務單元發(fā)生故障之后,通過斷路器的故障監(jiān)控(類似于物理的熔斷保險絲),向調(diào)用方返回一個符合預期的、可處理的備選響應(FallBack),而不是長時間的等待或者拋出調(diào)用方無法處理的異常,這樣就保證了服務調(diào)用方的線程不會被長時間、不必要的占用,從而避免了故障在分布式系統(tǒng)中的蔓延,乃至雪崩。
3. Hystrix能做什么?
主要有服務降級、服務熔斷、接近實時的監(jiān)控、限流、隔離等等,其官方文檔參考。當然Hystrix現(xiàn)在已經(jīng)停更了,雖然有一些替代品,但是學習Hystrix及其里面的思想還是非常重要的!
2. Hystrix重要概念
1. 服務降級——Fall Back
假設微服務A要調(diào)用的服務B不可用了,需要服務B提供一個兜底的解決方法,而不是讓服務A在那里傻等,耗死。不讓客戶端等待并立刻返回一個友好圖示,比如像客戶端提示服務器忙,請稍后再試等。哪些情況會觸發(fā)服務降級呢?
比如程序運行異常、超時、服務熔斷觸發(fā)服務降級、線程池/信號量打滿也會導致服務降級。
2. 服務熔斷——Break
服務熔斷就相當于物理上的熔斷保險絲。類比保險絲達到最大服務訪問后,直接拒絕訪問,拉閘斷點,然后調(diào)用服務降級的方法并返回友好提示。
3. 服務限流——Flow Limit
秒殺高并發(fā)等操作,嚴禁一窩蜂的過來擁擠,大家排隊,一秒鐘N個,有序進行。
3. Hystrix案例實操
1. 構(gòu)建
新建一個Module:cloud-provider-hystrix-payment8001作為服務提供方的微服務,和之前的服務消費方同樣選擇8001端口,但是在POM文件中需要引入Hystrix的依賴:
<!--hystrix--> <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency>然后編寫其配置文件,端口號為8001,服務名為cloud-provider-hystrix-payment,以使其入駐服務注冊中心。然后編寫其主啟動類,在SpringCloud-01Eureka服務注冊與發(fā)現(xiàn)都有詳細介紹過,之后編寫其業(yè)務類,service編寫入下:
package cn.sher6j.springcloud.service;import org.springframework.stereotype.Service;import java.util.concurrent.TimeUnit;/*** 應該要先寫接口,再寫實現(xiàn)類,這里為了節(jié)約時間,直接用實現(xiàn)類了* @author sher6j* @create 2020-05-21-17:29*/ @Service public class PaymentService {/*** 正常訪問* @param id* @return*/public String paymentInfo_OK(Long id) {return "線程池: " + Thread.currentThread().getName() + " paymentInfo_OK, id: " + id;}/*** 模擬復雜業(yè)務需要3秒鐘* @param id* @return*/public String paymentInfo_TimeOut(Long id) {int time = 3;//暫停幾秒鐘線程,程序本身沒有錯誤,就是模擬超時try {TimeUnit.SECONDS.sleep(time);} catch (InterruptedException e) {e.printStackTrace();}return "線程池: " + Thread.currentThread().getName() + " paymentInfo_TimeOut, id: " + id;} }Controller編寫入下:
package cn.sher6j.springcloud.controller;import cn.sher6j.springcloud.service.PaymentService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController;/*** @author sher6j* @create 2020-05-21-17:34*/ @RestController @Slf4j public class PaymentController {@Autowiredprivate PaymentService paymentService;@Value("${server.port}")private String serverPort;@GetMapping("/payment/hystrix/ok/{id}")public String paymentInfo_OK(@PathVariable("id") Long id) {String result = paymentService.paymentInfo_OK(id);log.info("========result:" + result);return result;}@GetMapping("/payment/hystrix/timeout/{id}")public String paymentInfo_TimeOut(@PathVariable("id") Long id) {String result = paymentService.paymentInfo_TimeOut(id);log.info("========result:" + result);return result;} }也就是說cloud-provider-hystrix-payment服務提供了兩個方法,paymentInfo_OK 方法可以很快訪問,paymentInfo_TimeOut 方法我們模擬了一個復雜的業(yè)物邏輯,通過線程休眠的方式使其模擬一個需要執(zhí)行3秒的服務方法。
在啟動了注冊中心和8001服務后,我們對服務的 paymentInfo_OK(下面用OK代替) 和 paymentInfo_TimeOut (下面用TO代替)分別進行訪問,我們發(fā)現(xiàn),http://localhost:8001/payment/hystrix/ok/1 可以很快的訪問,而http://localhost:8001/payment/hystrix/timeout/1 每次訪問大約需要3秒的時間。
2. 高并發(fā)測試
1. 服務提供方自測壓力測試:
需要3秒的復雜業(yè)務邏輯 TO 訪問時,需要時間很少的 OK 是完全可以正常訪問的,但是在高并發(fā)的情況下,也就是說 TO 有很多訪問量的時候,OK 還能夠這么正常的訪問嗎?下面我們用 Jmeter 進行高并發(fā)壓力測試,用20000個請求都去訪問 TO 服務,在 Jmeter 中新建一個線程組:測試Hystrix用來模擬高并發(fā)訪問 TO 服務,線程組配置參數(shù)如下:
然后我們用該線程組發(fā)送HTTP請求給 TO 服務,創(chuàng)建如下的HTTP請求進行壓力測試:
我們觀察8001服務的后端控制臺,可以看到對 TO 服務進行了大量的訪問:
而此時我們再去訪問 OK 服務時什么樣的呢??
可以看到,OK 服務無妨像之前一樣很快能夠得到訪問,這里我們模擬的是20000的訪問量(沒敢模擬數(shù)字太大的訪問量,怕把系統(tǒng)直接搞死哈哈哈哈),實際中可能會有遠大于20000的訪問量,當訪問量更多的時候,甚至可能卡死服務,原因就是Tomcat的默認的工作線程數(shù)被打滿了,沒有多余的線程來分解壓力和處理。
而剛才做的壓力測試還只是服務提供方8001自己實現(xiàn)的測試,如果此時是外部的服務消費方80來訪問該服務,那么服務消費方只能夠進行干等,消費方顯然會對這樣的等待時間不滿意,服務提供方很有可能直接被拖死。我們發(fā)現(xiàn)8001自測都會出現(xiàn)問題,那如果我們再用服務消費方測試呢?
2.服務消費方進行壓力測試:
新建一個Module:cloud-consumer-feign-hystrix-order80作為服務消費方,服務消費方利用feign訪問提供方的服務,編寫對應的service接口如下:
package cn.sher6j.springcloud.service;import org.springframework.cloud.openfeign.FeignClient; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable;/*** @author sher6j* @create 2020-05-21-20:00*/ @Component @FeignClient("CLOUD-PROVIDER-HYSTRIX-PAYMENT") public interface PaymentHystrixService {@GetMapping("/payment/hystrix/ok/{id}")public String paymentInfo_OK(@PathVariable("id") Long id);@GetMapping("/payment/hystrix/timeout/{id}")public String paymentInfo_TimeOut(@PathVariable("id") Long id); }然后編寫其Controller:
package cn.sher6j.springcloud.controller;import cn.sher6j.springcloud.service.PaymentHystrixService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController;/*** @author sher6j* @create 2020-05-21-20:02*/ @RestController @Slf4j public class OrderHystrixController {@Autowiredprivate PaymentHystrixService paymentHystrixService;@GetMapping("/consumer/payment/hystrix/ok/{id}")public String paymentInfo_OK(@PathVariable("id") Long id) {String result = paymentHystrixService.paymentInfo_OK(id);return result;}@GetMapping("/consumer/payment/hystrix/timeout/{id}")public String paymentInfo_TimeOut(@PathVariable("id") Long id) {String result = paymentHystrixService.paymentInfo_TimeOut(id);return result;} }將80服務啟動,用http://localhost/consumer/payment/hystrix/ok/1 對服務提供方的 OK 服務進行訪問,再進行壓力測試,和之前一樣,無法迅速訪問該服務,如果壓力測試中的線程數(shù)更多的時候,很可能會造成超時錯誤,出現(xiàn)以下錯誤提示:
故障原因:8001同一層次的其他接口服務被困死,因為Tomcat線程池里面的工作線程已經(jīng)被擠占完畢,80此時再調(diào)用8001,必然導致客戶端訪問響應緩慢。正是因為出現(xiàn)了這種現(xiàn)象,所以我們才需要服務降級、容錯、服務限流等技術(shù)。
3. 服務降級Fall Back
1. 服務端服務提供方的服務降級
降級的配置用 @HystrixCommand 注解,在服務提供方自身找問題,設置自身調(diào)用超時時間的峰值,在峰值內(nèi)可以正常運行,超過了峰值需要有兜底的方法處理,用作服務降級。
首先在服務提供方的業(yè)務類上啟用 @HystrixCommand 實現(xiàn)報異常后如何處理,也就是一旦調(diào)用服務方法失敗并拋出了錯誤信息后,會自動調(diào)用 @HystrixCommand 標注好的fallbackMethod服務降級方法。在服務提供方的service中我們修改 TO 服務:
/*** 模擬復雜業(yè)務需要3秒鐘* HystrixCommand配置2秒以內(nèi)走正常的邏輯,超過2秒走服務降級邏輯* @param id* @return*///設置服務降級方法為paymentInfo_TimeOutHandler,超時峰值為2秒@HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler", commandProperties = {@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "2000")})public String paymentInfo_TimeOut(Long id) {int time = 3;//暫停幾秒鐘線程,程序本身沒有錯誤,就是模擬超時try {TimeUnit.SECONDS.sleep(time);} catch (InterruptedException e) {e.printStackTrace();}return "線程池: " + Thread.currentThread().getName() + " paymentInfo_TimeOut, id: " + id;}/*** 兜底方法* @param id* @return*/public String paymentInfo_TimeOutHandler(Long id) {return "系統(tǒng)忙,請稍后再試";}然后在主啟動類上添加 @EnableCircuitBreaker 注解對熔斷器進行激活,TO服務的訪問時間3秒,而我們用Hystrix配置的時間峰值為2秒,也就是當服務超時或服務出錯時,會訪問我們設置的fallbackMethod服務降級方法,再次訪問TO服務,我們發(fā)現(xiàn)其執(zhí)行的方法確實為服務降級方法:
2. 客戶端服務消費方的服務降級
既然服務的提供方可以進行降級保護,那么服務的消費方,也可以更好的保護自己,也可以對自己進行降級保護,也就是說Hystrix服務降級既可以放在服務端(服務提供方),也可以放在客戶端(服務消費方),但是!!!通常是用客戶端做服務降級,下面在服務消費方即客戶端配置自己的服務降級保護,修改80消費方的配置文件,添加如下配置已使其支持Hystrix:
feign:hystrix:enabled: true在80消費方的主啟動類上添加 @EnableHystrix 激活Hystrix服務。然后在80的Controller中同樣加入**@HystrixCommand**注解已實現(xiàn)服務降級:
@GetMapping("/consumer/payment/hystrix/timeout/{id}")@HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler", commandProperties = {@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1500")})public String paymentInfo_TimeOut(@PathVariable("id") Long id) {String result = paymentHystrixService.paymentInfo_TimeOut(id);return result;}/*** 兜底方法* @param id* @return*/public String paymentInfo_TimeOutHandler(Long id) {return "系統(tǒng)忙,請稍后再試";}也就是說如果消費方訪問服務提供方的時間超過了1.5秒,那么就會訪問自己的降級服務方法。
3. 統(tǒng)一全局服務降級方法
而當前的這種處理方式是有問題的,也就是每個業(yè)務方法都對應了一個服務降級犯法,這會導致代碼膨脹,所以我們應該定義一個統(tǒng)一的服務降級方法,統(tǒng)一的方法和自定義的方法分開。而且我們將服務降級方法和業(yè)務邏輯混合在了一起,這會導致代碼混亂,業(yè)務邏輯不清晰。
對于第一個問題,我們可以用feign接口中的 @DefaultProperties(defaultFallback = "") 注解來配置全局的服務降級方法,也就是說自己配置過**@HystrixCommand(fallbackMethod = “”)** fallbackMethod方法的采用自己配置的服務降級方法,而沒有配置過的就采用**@DefaultProperties(defaultFallback = "")** 配置的全局的服務降級方法。這樣的話通用的服務降級方法和獨享的服務降級方法分開,避免了代碼膨脹,合理減少了代碼量,修改服務消費方80的Controller入下:
package cn.sher6j.springcloud.controller;import cn.sher6j.springcloud.service.PaymentHystrixService; import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController;/*** @author sher6j* @create 2020-05-21-20:02*/ @RestController @Slf4j @DefaultProperties(defaultFallback = "payment_Global_FallbackMethod") //全局服務降級 public class OrderHystrixController {@Autowiredprivate PaymentHystrixService paymentHystrixService;@GetMapping("/consumer/payment/hystrix/ok/{id}")@HystrixCommandpublic String paymentInfo_OK(@PathVariable("id") Long id) {int i = 1/0; //手動模擬錯誤String result = paymentHystrixService.paymentInfo_OK(id);return result;}@GetMapping("/consumer/payment/hystrix/timeout/{id}")//定制服務降級@HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler", commandProperties = {@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1500")})public String paymentInfo_TimeOut(@PathVariable("id") Long id) {String result = paymentHystrixService.paymentInfo_TimeOut(id);return result;}/*** 定制服務降級方法* @param id* @return*/public String paymentInfo_TimeOutHandler(Long id) {return "定制異常處理信息";}/*** 全局服務降級方法* @return*/public String payment_Global_FallbackMethod() {return "全局異常處理信息";} }paymentInfo_TimeOut由于定制了降級服務,所以訪問服務提供方會出現(xiàn)超時(服務提供方提供的服務執(zhí)行時間超過1.5秒),而paymentInfo_OK我們用 int i = 1/0; 這行代碼模擬了錯誤,分別訪問http://localhost/consumer/payment/hystrix/timeout/1 和http://localhost/consumer/payment/hystrix/ok/1 ,我們得到如下結(jié)果:
可以看到由于paymentInfo_OK沒有進行定制的服務降級方法,所以其訪問的是全局服務降級方法,而paymentInfo_TimeOut訪問的是定制服務降級方法,這里需要注意的是,無論是否配置了定制服務降級方法,都要在其服務上加入注解 @HystrixCommand, 否則服務降級和該服務沒關(guān)系,比如paymentInfo_OK如果沒有加入該注解,就直接會報除數(shù)為0錯誤。
而對于第二個問題, 我們可以為Feign客戶端定義的接口添加一個服務降級處理的實現(xiàn)類即可實現(xiàn)解耦,我們的80客戶端已經(jīng)有了PaymentHystrixService接口,我們新建一個類PaymentFallbackService實現(xiàn)該接口,并重寫接口中的方法,為接口里的方法進行異常處理,并且我們在PaymentHystrixService聲明其服務降級方法所在的類:
package cn.sher6j.springcloud.service;import org.springframework.cloud.openfeign.FeignClient; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable;/*** @author sher6j* @create 2020-05-21-20:00*/ @Component //當出現(xiàn)錯誤是到PaymentFallbackService類中找服務降級方法 @FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT", fallback = PaymentFallbackService.class) public interface PaymentHystrixService {@GetMapping("/payment/hystrix/ok/{id}")public String paymentInfo_OK(@PathVariable("id") Long id);@GetMapping("/payment/hystrix/timeout/{id}")public String paymentInfo_TimeOut(@PathVariable("id") Long id); } package cn.sher6j.springcloud.service;import org.springframework.stereotype.Component;/*** @author sher6j* @create 2020-05-21-21:27*/ @Component public class PaymentFallbackService implements PaymentHystrixService {@Overridepublic String paymentInfo_OK(Long id) {return "paymentInfo_OK出現(xiàn)異常";}@Overridepublic String paymentInfo_TimeOut(Long id) {return "paymentInfo_TimeOut出現(xiàn)異常";} }然后我們將Controller中耦合的代碼都取消:
package cn.sher6j.springcloud.controller;import cn.sher6j.springcloud.service.PaymentHystrixService; import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController;/*** @author sher6j* @create 2020-05-21-20:02*/ @RestController @Slf4j public class OrderHystrixController {@Autowiredprivate PaymentHystrixService paymentHystrixService;@GetMapping("/consumer/payment/hystrix/ok/{id}")public String paymentInfo_OK(@PathVariable("id") Long id) {String result = paymentHystrixService.paymentInfo_OK(id);return result;}@GetMapping("/consumer/payment/hystrix/timeout/{id}")public String paymentInfo_TimeOut(@PathVariable("id") Long id) {String result = paymentHystrixService.paymentInfo_TimeOut(id);return result;} }然后我們關(guān)閉8001服務提供方服務,模擬服務器宕機,如圖,在服務訪問出現(xiàn)錯誤時,訪問了我們配置的PaymentFallbackService類中的服務降級方法,這樣就實現(xiàn)了代碼的解耦,使業(yè)務邏輯不再混亂。
4. 服務熔斷Break
1. 熔斷機制概述
熔斷機制是應對雪崩效應的一種微服務鏈路保護機制,當扇出鏈路的某個微服務出錯不可用或者響應時間太長時,會進行服務的降級,進而熔斷該節(jié)點微服務的調(diào)用,也就是說服務熔斷會導致服務降級,快速返回錯誤的響應信息。當檢測到該節(jié)點微服務調(diào)用響應正常后,恢復調(diào)用鏈路。也就是說,服務熔斷在服務好了之后會重新允許訪問服務。在SpringCloud框架中,熔斷機制通過Hystrix實現(xiàn)。Hystrix會監(jiān)控微服務間的調(diào)用狀況,當失敗的調(diào)用到一定閾值,缺省是5秒內(nèi)20次調(diào)用失敗,就會啟動熔斷機制。熔斷機制的注解是 @HystrixCommand。關(guān)于熔斷機制,具體可以參考論文CircuitBreaker。
2. 實操
在8001服務提供方的Service中添加如下代碼:
//======服務熔斷/*** fallbackMethod 服務降級方法* circuitBreaker.enabled 是否開啟斷路器* circuitBreaker.requestVolumeThreshold 請求次數(shù)* circuitBreaker.sleepWindowInMilliseconds 時間窗口期* circuitBreaker.errorThresholdPercentage 失敗率達到多少后跳閘* 以下配置意思是在10秒時間內(nèi)請求10次,如果有6此是失敗的,就觸發(fā)熔斷器* 注解@HystrixProperty中的屬性在com.netflix.hystrix.HystrixCommandProperties類中查看* @param id* @return*/@HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback", commandProperties = {@HystrixProperty(name = "circuitBreaker.enabled", value = "true"),@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000"),@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "60")})public String paymentCircuitBreaker(@PathVariable("id") Long id) {if (id < 0) {throw new RuntimeException("id 不能為負數(shù)");}String serialNumber = IdUtil.simpleUUID();return Thread.currentThread().getName() + " 調(diào)用成功,流水號: " + serialNumber;}/*** 服務熔斷觸發(fā)的服務降級方法* @param id* @return*/public String paymentCircuitBreaker_fallback(@PathVariable("id") Long id) {return "id 不能為負數(shù),請稍后再試。id:" + id;}在 @HystrixCommand 注解中配置熔斷機制的參數(shù),配置的參數(shù)含義如下:
| circuitBreaker.enabled | 是否開啟斷路器 | true |
| circuitBreaker.requestVolumeThreshold | 請求次數(shù) | 20 |
| circuitBreaker.sleepWindowInMilliseconds | 時間窗口期 | 5000 |
| circuitBreaker.errorThresholdPercentage | 失敗率達到多少后跳閘 | 50 |
這些屬性名的具體含義一級其默認值可以在 com.netflix.hystrix.HystrixCommandProperties 類中進行查看。而我們在service中配置的意思就是在10秒時間內(nèi)請求10次,如果有6次是失敗的,就觸發(fā)熔斷器。
在Controller中添加該服務:
@GetMapping("payment/circuit/{id}")public String paymentCircuitBreaker(@PathVariable("id") Long id) {String result = paymentService.paymentCircuitBreaker(id);log.info("=========result:" + result);return result;}3. 測試
根據(jù)我們的業(yè)物邏輯,也就是當我們的id為整數(shù)時,服務可以正常訪問,而當id為負數(shù)時,訪問服務出錯。我們先訪問 http://localhost:8001/payment/circuit/1 代表正確的服務請求,可以發(fā)現(xiàn)一切正常!!!:
然后我們進行大量的錯誤訪問,強行觸發(fā)服務熔斷,然后在進行正確的訪問。
我們發(fā)現(xiàn)在進行超出我們閾值的錯誤訪問后,觸發(fā)了服務熔斷,即使再進行正確的訪問也無法進行,但是一定時間后,正確的服務訪問又可以順利進行,這就是服務熔斷的整體過程:在觸發(fā)了服務熔斷后,先進行服務的降級,再逐漸恢復調(diào)用鏈路。
4. 總結(jié)
結(jié)合官網(wǎng)中對熔斷機制的描述,其熔斷過程可以如下描述:
熔斷器打開和關(guān)閉的精確方式如下:
- **1.**假設電路上的訪問達到某個閾值(HystrixCommandProperties.circuitBreakerRequestVolumeThreshold())…
- **2.**并假設誤差百分比超過閾值誤差百分比(HystrixCommandProperties.circuitBreakerErrorThresholdPercentage())…
- **3.**然后,斷路器從CLOSED為OPEN,觸發(fā)熔斷機制。
- **4.**當它斷開時,它會使針對該斷路器的所有請求短路。
- **5.經(jīng)過一段時間(HystrixCommandProperties.circuitBreakerSleepWindowInMilliseconds())后,下一個單個請求被允許通過(這是HALF-OPEN狀態(tài))。如果請求失敗,斷路器將OPEN在睡眠窗口期間返回到該狀態(tài)。如果請求成功,則斷路器切換到,CLOSED,并且1.**中的邏輯再次接管。
也就是在熔斷機制中,熔斷器分為三個狀態(tài):
| 熔斷器關(guān)閉CLOSED | 熔斷關(guān)閉不會對服務進行熔斷。 |
| 熔斷器半開HALF-OPEN | 部分請求根據(jù)規(guī)則調(diào)用當前服務,如果請求成功且符合規(guī)則則認為當前服務恢復正常,關(guān)閉熔斷。 |
下面是官網(wǎng)上的熔斷器流程圖:
-
那么熔斷器在什么情況下開始起作用呢?
涉及到熔斷器的三個重要參數(shù):
-
快照時間窗口期circuitBreaker.sleepWindowInMilliseconds:熔斷器是否打開需要統(tǒng)計一些請求和錯誤數(shù)據(jù),而統(tǒng)計的時間范圍就是快找時間窗,默認為最近的10秒;
-
請求總數(shù)閾值circuitBreaker.requestVolumeThreshold:在快照時間窗內(nèi),必須滿足請求總數(shù)閾值才有資格觸發(fā)熔斷,默認為20次,這意味著在快照時間窗規(guī)定的時間內(nèi),如果該Hystrix命令的調(diào)用次數(shù)不足20次,即使所有請求都超時或其他原因失敗,熔斷器都不會打開;
-
錯誤百分比閾值circuitBreaker.errorThresholdPercentage:當請求總數(shù)在快照時間窗內(nèi)超過了閾值,且在這些調(diào)用中,超過錯誤百分比閾值比例的錯誤調(diào)用,熔斷器就會打開。
-
熔斷器打開后再有請求調(diào)用的時候,將不會調(diào)用主邏輯,而是直接調(diào)用服務降級的方法,實現(xiàn)了自動發(fā)現(xiàn)錯誤并將降級邏輯切換為主邏輯,減少響應延遲的效果。
-
在熔斷器打開后,原來的主邏輯如何恢復呢?
當熔斷器打開后,對主邏輯進行熔斷之后,Hystrix會啟動一個 休眠時間窗 , 在這個時間窗內(nèi),降級邏輯是臨時的主邏輯,當休眠時間窗到期,熔斷器會進入半開狀態(tài),釋放一次請求到原來的主邏輯上,如果此次請求能夠正常訪問,則熔斷器會進入閉合狀態(tài),從而恢復主邏輯,如果注冊請求依然有問題,則熔斷器繼續(xù)保持打開狀態(tài),并且休眠時間窗重新計時。
5. Hystrix工作流程
整體的Hystrix工作流程圖入下:
4. 服務監(jiān)控Hystrix Dashboard
除了隔離依賴服務的調(diào)用以外,Hystrix還提供了準實時的調(diào)用監(jiān)控——Hystrix Dashboard,Hystrix會持續(xù)的記錄所有通過Hystrix發(fā)起的請求的執(zhí)行信息,并以統(tǒng)計報表和圖形的形式展示給用戶,包括每秒執(zhí)行多少請求,多少成功,多少失敗等。SpringCloud也提供了Hystrix Dashboard的整合,對監(jiān)控內(nèi)容轉(zhuǎn)化成可視化界面。
-
新建Module:cloud-consumer-hystrix-dashboard9001作為Hystrix Dashboard服務
-
添加Hystrix Dashboard的依賴:
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId> </dependency> -
寫配置文件application.yml,加個端口即可:
server:port: 9001 -
編寫主啟動類,在主啟動類上添加@EnableHystrixDashboard注解開啟Hystrix Dashboard功能:
package cn.sher6j.springcloud;import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;/*** @author sher6j* @create 2020-05-21-23:47*/ @SpringBootApplication @EnableHystrixDashboard public class HystrixDashboardMain9001 {public static void main(String[] args) {SpringApplication.run(HystrixDashboardMain9001.class);} } -
所有的服務提供方微服務(如我們的8001/8002)都需要監(jiān)控依賴配置:
<!--actuator監(jiān)控信息完善--> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId> </dependency>
訪問 http://localhost:9001/hystrix 我們就可以看見Hystrix Dashboard的圖形化界面
為了讓服務提供方的服務能被Hystrix Dashboard監(jiān)控到,需要在提供方服務的主啟動類中添加如下配置:
在Hystrix Dashboard的圖形化界面中輸入要監(jiān)控的服務提供者:
下面就是Dashboard對服務的監(jiān)控狀態(tài):
總結(jié)
以上是生活随笔為你收集整理的SpringCloud-06-Hystrix断路器的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Jess 7.2p2——Java平台规则
- 下一篇: gradle idea java ssm