SpringCloud CircuitBreaker斷路器詳解
Hystrix簡介
- Hystrix是一個(gè)用于處理分布式系統(tǒng)的延遲和容錯(cuò)的開源庫,在分布式系統(tǒng)里,許多依賴不可避免的會(huì)調(diào)用失敗,比如超時(shí)、異常等,Hystrix能夠保證在一個(gè)依賴出問題的情況下,不會(huì)導(dǎo)致整體服務(wù)失敗,避免級(jí)聯(lián)故障,以提高分布式系統(tǒng)的彈性
- 已進(jìn)入停更運(yùn)維階段,后續(xù)采用resilience4j

Circuit Breaker介紹
服務(wù)雪崩
多個(gè)微服務(wù)之間調(diào)用的時(shí)候,假設(shè)微服務(wù)A調(diào)用微服務(wù)B和微服務(wù)C,微服務(wù)B和微服務(wù)C又調(diào)用其它的微服務(wù),這就是所謂的“扇出”。如果扇出的鏈路上某個(gè)微服務(wù)的調(diào)用響應(yīng)時(shí)間過長或者不可用,對(duì)微服務(wù)A的調(diào)用就會(huì)占用越來越多的系統(tǒng)資源,進(jìn)而引起系統(tǒng)崩潰,所謂的“雪崩效應(yīng)”.
對(duì)于高流量的應(yīng)用來說,單一的后端依賴可能會(huì)導(dǎo)致所有服務(wù)器上的所有資源都在幾秒鐘內(nèi)飽和。比失敗更糟糕的是,這些應(yīng)用程序還可能導(dǎo)致服務(wù)之間的延遲增加,備份隊(duì)列,線程和其他系統(tǒng)資源緊張,導(dǎo)致整個(gè)系統(tǒng)發(fā)生更多的級(jí)聯(lián)故障。這些都表示需要對(duì)故障和延遲進(jìn)行隔離和管理,以便單個(gè)依賴關(guān)系的失敗,不能取消整個(gè)應(yīng)用程序或系統(tǒng)。
所以,通常當(dāng)你發(fā)現(xiàn)一個(gè)模塊下的某個(gè)實(shí)例失敗后,這時(shí)候這個(gè)模塊依然還會(huì)接收流量,然后這個(gè)有問題的模塊還調(diào)用了其他的模塊,這樣就會(huì)發(fā)生級(jí)聯(lián)故障,或者叫雪崩。
- Circuit Breaker的目的是保護(hù)分布式系統(tǒng)免受故障和異常,提高系統(tǒng)的可用性和健壯性
- 當(dāng)一個(gè)組件或服務(wù)出現(xiàn)故障時(shí),CircuitBreaker會(huì)迅速切換到開放OPEN狀態(tài)(保險(xiǎn)絲跳閘斷電),阻止請(qǐng)求發(fā)送到該組件或服務(wù)從而避免更多的請(qǐng)求發(fā)送到該組件或服務(wù)。
- 這可以減少對(duì)該組件或服務(wù)的負(fù)載,防止該組件或服務(wù)進(jìn)一步崩潰,并使整個(gè)系統(tǒng)能夠繼續(xù)正常運(yùn)行。同時(shí),CircuitBreaker還可以提高系統(tǒng)的可用性和健壯性,因?yàn)樗梢栽诜植际较到y(tǒng)的各個(gè)組件之間自動(dòng)切換,從而避免單點(diǎn)故障的問題。
- Circuit Breaker只是一套規(guī)范和接口,落地實(shí)現(xiàn)者是Resilience4J

Resilience4J介紹
- 官網(wǎng)地址:
- 說明

- 主要作用

熔斷(Circuit Breaker)案例

斷路器狀態(tài)

斷路器常用參數(shù)配置

按照COUNT_BASED(計(jì)數(shù)的滑動(dòng)窗口)
- 在客戶端微服務(wù)引入依賴
<!--resilience4j-circuitbreaker-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
</dependency>
<!-- 由于斷路保護(hù)等需要AOP實(shí)現(xiàn),所以必須導(dǎo)入AOP包 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>- 客戶端的YML配置開啟circuit breaker
spring:
application:
name: cloud-consumer-openfeign-order
####Spring Cloud Consul for Service Discovery
cloud:
consul:
host: localhost
port: 8500
discovery:
prefer-ip-address: true #優(yōu)先使用服務(wù)ip進(jìn)行注冊(cè)
service-name: ${spring.application.name}
openfeign:
# 開啟circuitbreaker和分組激活 spring.cloud.openfeign.circuitbreaker.enabled
circuitbreaker:
enabled: true
group:
enabled: true #沒開分組永遠(yuǎn)不用分組的配置。精確優(yōu)先、分組次之(開了分組)、默認(rèn)最后
# Resilience4j CircuitBreaker 按照次數(shù):COUNT_BASED 的例子
# 6次訪問中當(dāng)執(zhí)行方法的失敗率達(dá)到50%時(shí)CircuitBreaker將進(jìn)入開啟OPEN狀態(tài)(保險(xiǎn)絲跳閘斷電)拒絕所有請(qǐng)求。
# 等待5秒后,CircuitBreaker 將自動(dòng)從開啟OPEN狀態(tài)過渡到半開HALF_OPEN狀態(tài),允許一些請(qǐng)求通過以測試服務(wù)是否恢復(fù)正常。
# 如還是異常CircuitBreaker 將重新進(jìn)入開啟OPEN狀態(tài);如正常將進(jìn)入關(guān)閉CLOSE閉合狀態(tài)恢復(fù)正常處理請(qǐng)求。
resilience4j:
circuitbreaker:
configs:
default:
failureRateThreshold: 50 #設(shè)置50%的調(diào)用失敗時(shí)打開斷路器,超過失敗請(qǐng)求百分?CircuitBreaker變?yōu)镺PEN狀態(tài)。
slidingWindowType: COUNT_BASED # 滑動(dòng)窗口的類型
slidingWindowSize: 6 #滑動(dòng)窗?的??配置COUNT_BASED表示6個(gè)請(qǐng)求,配置TIME_BASED表示6秒
minimumNumberOfCalls: 6 #斷路器計(jì)算失敗率或慢調(diào)用率之前所需的最小樣本(每個(gè)滑動(dòng)窗口周期)。如果minimumNumberOfCalls為10,則必須最少記錄10個(gè)樣本,然后才能計(jì)算失敗率。如果只記錄了9次調(diào)用,即使所有9次調(diào)用都失敗,斷路器也不會(huì)開啟。
automaticTransitionFromOpenToHalfOpenEnabled: true # 是否啟用自動(dòng)從開啟狀態(tài)過渡到半開狀態(tài),默認(rèn)值為true。如果啟用,CircuitBreaker將自動(dòng)從開啟狀態(tài)過渡到半開狀態(tài),并允許一些請(qǐng)求通過以測試服務(wù)是否恢復(fù)正常
waitDurationInOpenState: 5s #從OPEN到HALF_OPEN狀態(tài)需要等待的時(shí)間
permittedNumberOfCallsInHalfOpenState: 2 #半開狀態(tài)允許的最大請(qǐng)求數(shù),默認(rèn)值為10。在半開狀態(tài)下,CircuitBreaker將允許最多permittedNumberOfCallsInHalfOpenState個(gè)請(qǐng)求通過,如果其中有任何一個(gè)請(qǐng)求失敗,CircuitBreaker將重新進(jìn)入開啟狀態(tài)。
recordExceptions:
- java.lang.Exception
instances:
cloud-payment-service:
baseConfig: default- 客戶端的controller類方法使用注解@CircuitBreaker
@RestController
public class OrderCircuitController{
@Resource
private PayFeignApi payFeignApi;
@GetMapping(value = "/feign/pay/circuit/{id}")
@CircuitBreaker(name = "cloud-payment-service", fallbackMethod = "myCircuitFallback")
public String myCircuitBreaker(@PathVariable("id") Integer id){
return payFeignApi.myCircuit(id);
}
//myCircuitFallback就是服務(wù)降級(jí)后的兜底處理方法
public String myCircuitFallback(Integer id,Throwable t) {
// 這里是容錯(cuò)處理邏輯,返回備用結(jié)果
return "myCircuitFallback,系統(tǒng)繁忙,請(qǐng)稍后再試-----/(ㄒoㄒ)/~~";
}
}按照TIME_BASED(時(shí)間的滑動(dòng)窗囗)

- 客戶端的YML配置開啟circuit breaker
spring:
application:
name: cloud-consumer-openfeign-order
####Spring Cloud Consul for Service Discovery
cloud:
consul:
host: localhost
port: 8500
discovery:
prefer-ip-address: true #優(yōu)先使用服務(wù)ip進(jìn)行注冊(cè)
service-name: ${spring.application.name}
openfeign:
# 開啟circuitbreaker和分組激活 spring.cloud.openfeign.circuitbreaker.enabled
circuitbreaker:
enabled: true
group:
enabled: true #沒開分組永遠(yuǎn)不用分組的配置。精確優(yōu)先、分組次之(開了分組)、默認(rèn)最后
# Resilience4j CircuitBreaker 按照時(shí)間:TIME_BASED 的例子
resilience4j:
timelimiter:
configs:
default:
timeout-duration: 10s #神坑的位置,timelimiter 默認(rèn)限制遠(yuǎn)程1s,超于1s就超時(shí)異常,配置了降級(jí),就走降級(jí)邏輯
circuitbreaker:
configs:
default:
failureRateThreshold: 50 #設(shè)置50%的調(diào)用失敗時(shí)打開斷路器,超過失敗請(qǐng)求百分?CircuitBreaker變?yōu)镺PEN狀態(tài)。
slowCallDurationThreshold: 2s #慢調(diào)用時(shí)間閾值,高于這個(gè)閾值的視為慢調(diào)用并增加慢調(diào)用比例。
slowCallRateThreshold: 30 #慢調(diào)用百分比峰值,斷路器把調(diào)用時(shí)間?于slowCallDurationThreshold,視為慢調(diào)用,當(dāng)慢調(diào)用比例高于閾值,斷路器打開,并開啟服務(wù)降級(jí)
slidingWindowType: TIME_BASED # 滑動(dòng)窗口的類型
slidingWindowSize: 2 #滑動(dòng)窗口的大小配置,配置TIME_BASED表示2秒
minimumNumberOfCalls: 2 #斷路器計(jì)算失敗率或慢調(diào)用率之前所需的最小樣本(每個(gè)滑動(dòng)窗口周期)。
permittedNumberOfCallsInHalfOpenState: 2 #半開狀態(tài)允許的最大請(qǐng)求數(shù),默認(rèn)值為10。
waitDurationInOpenState: 5s #從OPEN到HALF_OPEN狀態(tài)需要等待的時(shí)間
recordExceptions:
- java.lang.Exception
instances:
cloud-payment-service:
baseConfig: default隔離(BulkHead)案例
- 依賴隔離&負(fù)載保護(hù):用來限制對(duì)于下游服務(wù)的最大并發(fā)數(shù)量
SemaphoreBulkhead(信號(hào)量艙壁)

- JUC信號(hào)燈內(nèi)容的同樣思想

當(dāng)信號(hào)量有空閑時(shí),進(jìn)入系統(tǒng)的請(qǐng)求會(huì)直接獲取信號(hào)量并開始業(yè)務(wù)處理。
當(dāng)信號(hào)量全被占用時(shí),接下來的請(qǐng)求將會(huì)進(jìn)入阻塞狀態(tài),SemaphoreBulkhead提供了一個(gè)阻塞計(jì)時(shí)器,
如果阻塞狀態(tài)的請(qǐng)求在阻塞計(jì)時(shí)內(nèi)無法獲取到信號(hào)量則系統(tǒng)會(huì)拒絕這些請(qǐng)求。
若請(qǐng)求在阻塞計(jì)時(shí)內(nèi)獲取到了信號(hào)量,那將直接獲取信號(hào)量并執(zhí)行相應(yīng)的業(yè)務(wù)處理。
- 客戶端引入依賴
<!--resilience4j-bulkhead-->
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-bulkhead</artifactId>
</dependency>- 客戶端YML配置文件
####resilience4j bulkhead 的例子
resilience4j:
bulkhead:
configs:
default:
maxConcurrentCalls: 2 # 隔離允許并發(fā)線程執(zhí)行的最大數(shù)量
maxWaitDuration: 1s # 當(dāng)達(dá)到并發(fā)調(diào)用數(shù)量時(shí),新的線程的阻塞時(shí)間,我只愿意等待1秒,過時(shí)不候進(jìn)艙壁兜底fallback
instances:
cloud-payment-service:
baseConfig: default
timelimiter:
configs:
default:
timeout-duration: 20s- 客戶端controller類方法使用注解@Bulkhead,設(shè)置類型為Bulkhead.Type.SEMAPHORE
@GetMapping(value = "/feign/pay/bulkhead/{id}")
@Bulkhead(name = "cloud-payment-service",fallbackMethod = "myBulkheadFallback",type = Bulkhead.Type.SEMAPHORE)
public String myBulkhead(@PathVariable("id") Integer id){
return payFeignApi.myBulkhead(id);
}
public String myBulkheadFallback(Throwable t){
return "myBulkheadFallback,隔板超出最大數(shù)量限制,系統(tǒng)繁忙,請(qǐng)稍后再試-----/(ㄒoㄒ)/~~";
}FixedThreadPoolBulkhead(固定線程池艙壁)

- JUC-線程池內(nèi)容的同樣思想

FixedThreadPoolBulkhead的功能與SemaphoreBulkhead一樣也是用于限制并發(fā)執(zhí)行的次數(shù)的,但是二者的實(shí)現(xiàn)原理存在差別而且表現(xiàn)效果也存在細(xì)微的差別。FixedThreadPoolBulkhead使用一個(gè)固定線程池和一個(gè)等待隊(duì)列來實(shí)現(xiàn)艙壁。
當(dāng)線程池中存在空閑時(shí),則此時(shí)進(jìn)入系統(tǒng)的請(qǐng)求將直接進(jìn)入線程池開啟新線程或使用空閑線程來處理請(qǐng)求。
當(dāng)線程池中無空閑時(shí)時(shí),接下來的請(qǐng)求將進(jìn)入等待隊(duì)列,
若等待隊(duì)列仍然無剩余空間時(shí)接下來的請(qǐng)求將直接被拒絕,
在隊(duì)列中的請(qǐng)求等待線程池出現(xiàn)空閑時(shí),將進(jìn)入線程池進(jìn)行業(yè)務(wù)處理。
- 依賴同上
- 客戶端YML配置文件
####resilience4j bulkhead -THREADPOOL的例子
resilience4j:
timelimiter:
configs:
default:
timeout-duration: 10s #timelimiter默認(rèn)限制遠(yuǎn)程1s,超過報(bào)錯(cuò)不好演示效果所以加上10秒
thread-pool-bulkhead:
configs:
default:
core-thread-pool-size: 1
max-thread-pool-size: 1
queue-capacity: 1
instances:
cloud-payment-service:
baseConfig: default
# spring.cloud.openfeign.circuitbreaker.group.enabled 請(qǐng)?jiān)O(shè)置為false 新啟線程和原來主線程脫離- 客戶端controller類方法使用注解@Bulkhead,設(shè)置類型為Bulkhead.Type.THREADPOOL
- ThreadPoolBulkhead只對(duì)CompletableFuture方法有效,所以必創(chuàng)建返回CompletableFuture類型的方法
@GetMapping(value = "/feign/pay/bulkhead/{id}")
@Bulkhead(name = "cloud-payment-service",fallbackMethod = "myBulkheadPoolFallback",type = Bulkhead.Type.THREADPOOL)
public CompletableFuture<String> myBulkheadTHREADPOOL(@PathVariable("id") Integer id){
System.out.println(Thread.currentThread().getName()+"\t"+"enter the method!!!");
try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println(Thread.currentThread().getName()+"\t"+"exist the method!!!");
return CompletableFuture.supplyAsync(() -> payFeignApi.myBulkhead(id) + "\t" + " Bulkhead.Type.THREADPOOL");
}
public CompletableFuture<String> myBulkheadPoolFallback(Integer id,Throwable t){
return CompletableFuture.supplyAsync(() -> "Bulkhead.Type.THREADPOOL,系統(tǒng)繁忙,請(qǐng)稍后再試-----/(ㄒoㄒ)/~~");
}限流(RateLimiter)案例
- 對(duì)并發(fā)訪問/請(qǐng)求進(jìn)行限速,或者對(duì)一個(gè)時(shí)間窗口內(nèi)的請(qǐng)求進(jìn)行限速,以保護(hù)應(yīng)用系統(tǒng),一旦達(dá)到限制速率則可以拒絕服務(wù)、排隊(duì)或等待、降級(jí)等處理

常見限流算法
漏斗算法(Leaky Bucket)
- 一個(gè)固定容量的漏桶,按照設(shè)定常量固定速率流出水滴,類似醫(yī)院打吊針,不管你源頭流量多大,我設(shè)定勻速流出。如果流入水滴超出了桶的容量,則流入的水滴將會(huì)溢出了(被丟棄),而漏桶容量是不變的

- 這里有兩個(gè)變量,一個(gè)是桶的大小,支持流量突發(fā)增多時(shí)可以存多少的水(burst),另一個(gè)是水桶漏洞的大?。╮ate)。因?yàn)槁┩暗穆┏鏊俾适枪潭ǖ膮?shù),所以,即使網(wǎng)絡(luò)中不存在資源沖突(沒有發(fā)生擁塞),漏桶算法也不能使流突發(fā)(burst)到端口速率。因此,漏桶算法對(duì)于存在突發(fā)特性的流量來說缺乏效率。

令牌桶算法(Token Bucket)
- Spring Cloud默認(rèn)使用該算法

滾動(dòng)時(shí)間窗(tumbling time window)
- 允許固定數(shù)量的請(qǐng)求進(jìn)入(比如1秒取4個(gè)數(shù)據(jù)相加,超過25值就over)超過數(shù)量就拒絕或者排隊(duì),等下一個(gè)時(shí)間段進(jìn)入。由于是在一個(gè)時(shí)間間隔內(nèi)進(jìn)行限制,如果用戶在上個(gè)時(shí)間間隔結(jié)束前請(qǐng)求(但沒有超過限制),同時(shí)在當(dāng)前時(shí)間間隔剛開始請(qǐng)求(同樣沒超過限制),在各自的時(shí)間間隔內(nèi),這些請(qǐng)求都是正常的

- 間隔臨界的一段時(shí)間內(nèi)的請(qǐng)求就會(huì)超過系統(tǒng)限制,可能導(dǎo)致系統(tǒng)被壓垮。
假如設(shè)定1分鐘最多可以請(qǐng)求100次某個(gè)接口,如12:00:00-12:00:59時(shí)間段內(nèi)沒有數(shù)據(jù)請(qǐng)求但12:00:59-12:01:00時(shí)間段內(nèi)突然并發(fā)100次請(qǐng)求,緊接著瞬間跨入下一個(gè)計(jì)數(shù)周期計(jì)數(shù)器清零;在12:01:00-12:01:01內(nèi)又有100次請(qǐng)求。那么也就是說在時(shí)間臨界點(diǎn)左右可能同時(shí)有2倍的峰值進(jìn)行請(qǐng)求,從而造成后臺(tái)處理請(qǐng)求加倍過載的bug,導(dǎo)致系統(tǒng)運(yùn)營能力不足,甚至導(dǎo)致系統(tǒng)崩潰

滑動(dòng)時(shí)間窗口(sliding time window)
- 窗口:需要定義窗口的大小
- 滑動(dòng):需要定義在窗口中滑動(dòng)的大小,理論上滑動(dòng)的大小不能超過窗口大小
- 滑動(dòng)窗口算法是把固定時(shí)間片進(jìn)行劃分并且隨著時(shí)間移動(dòng),移動(dòng)方式為開始時(shí)間點(diǎn)變?yōu)闀r(shí)間列表中的第2個(gè)時(shí)間點(diǎn),結(jié)束時(shí)間點(diǎn)增加一個(gè)時(shí)間點(diǎn), 不斷重復(fù),通過這種方式可以巧妙的避開計(jì)數(shù)器的臨界點(diǎn)的問題

具體實(shí)現(xiàn)
- 客戶端依賴引入
<!--resilience4j-ratelimiter-->
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-ratelimiter</artifactId>
</dependency>- 客戶端YML配置文件
####resilience4j ratelimiter 限流的例子
resilience4j:
ratelimiter:
configs:
default:
limitForPeriod: 2 #在一次刷新周期內(nèi),允許執(zhí)行的最大請(qǐng)求數(shù)
limitRefreshPeriod: 1s # 限流器每隔limitRefreshPeriod刷新一次,將允許處理的最大請(qǐng)求數(shù)量重置為limitForPeriod
timeout-duration: 1 # 線程等待權(quán)限的默認(rèn)等待時(shí)間
instances:
cloud-payment-service:
baseConfig: default- 客戶端controller類方法使用注解@RateLimite
@GetMapping(value = "/feign/pay/ratelimit/{id}")
@RateLimiter(name = "cloud-payment-service",fallbackMethod = "myRatelimitFallback")
public String myBulkhead(@PathVariable("id") Integer id){
return payFeignApi.myRatelimit(id);
}
public String myRatelimitFallback(Integer id,Throwable t){
return "你被限流了,禁止訪問/(ㄒoㄒ)/~~";
}到此這篇關(guān)于SpringCloud CircuitBreaker斷路器詳解的文章就介紹到這了,更多相關(guān)SpringCloud CircuitBreaker斷路器內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot靜態(tài)資源映射,圖片無法實(shí)時(shí)訪問問題及解決
文章介紹了Spring Boot中靜態(tài)資源映射配置,解決了圖片上傳后無法實(shí)時(shí)訪問的問題,通過配置虛擬路徑,將訪問路徑映射到指定的物理路徑,解決了圖片無法實(shí)時(shí)顯示的問題2025-02-02
Spring依賴注入的兩種方式(根據(jù)實(shí)例詳解)
這篇文章主要介紹了Spring依賴注入的兩種方式(根據(jù)實(shí)例詳解),非常具有實(shí)用價(jià)值,需要的朋友可以參考下2017-05-05
Java SpringMVC數(shù)據(jù)響應(yīng)超詳細(xì)講解
Spring?MVC?是?Spring?提供的一個(gè)基于?MVC?設(shè)計(jì)模式的輕量級(jí)?Web?開發(fā)框架,本質(zhì)上相當(dāng)于?Servlet,Spring?MVC?角色劃分清晰,分工明細(xì),本章來講解SpringMVC數(shù)據(jù)響應(yīng)2022-04-04
Java實(shí)現(xiàn)一鍵將Word文檔轉(zhuǎn)為PDF
在開發(fā)中,經(jīng)常會(huì)碰到需要把 Word 文檔轉(zhuǎn)換成 PDF 格式的需求,Java 有不少好用的庫能實(shí)現(xiàn)這個(gè)功能,本文為大家介紹了兩個(gè)常用的方法,需要的可以了解下2025-02-02
微信java開發(fā)之實(shí)現(xiàn)微信主動(dòng)推送消息
這篇文章主要介紹了微信開發(fā)過程中的使用java實(shí)現(xiàn)微信主動(dòng)推送消息示例,需要的朋友可以參考下2014-03-03

