淺析Spring Boot單體應(yīng)用熔斷技術(shù)的使用
壹、入圍方案
Sentinel
- github地址:https://sentinelguard.io/zh-cn/docs/introduction.html
- 阿里出品,Spring Cloud Alibaba限流組件,目前持續(xù)更新中
- 自帶Dashboard,可以查看接口Qps等,并且可以動態(tài)修改各種規(guī)則
- 流量控制,直接限流、冷啟動、排隊
- 熔斷降級,限制并發(fā)限制數(shù)和相應(yīng)時間
- 系統(tǒng)負載保護,提供系統(tǒng)級別防護,限制總體CPU等
- 主要核心:資源,規(guī)則(流量控制規(guī)則、熔斷降級規(guī)則、系統(tǒng)保護規(guī)則、來源訪問控制規(guī)則 和 熱點參數(shù)規(guī)則。),和指標
- 文檔非常清晰和詳細,中文
- 支持動態(tài)規(guī)則(推模式和拉模式)
Hystrix
- github地址:https://github.com/Netflix/Hystrix/wiki
- Netflix出品,Spring Cloud Netflix限流組件,已經(jīng)停止新特性開發(fā),只進行bug修復(fù),最近更新為2018年,功能穩(wěn)定
- 有簡單的dashboard頁面
- 以隔離和熔斷為主的容錯機制,超時或被熔斷的調(diào)用將會快速失敗,并可以提供 fallback 機制的初代熔斷框架,異常統(tǒng)計基于滑動窗口
resilience4j
- github地址:https://resilience4j.readme.io/docs
- 是一款輕量、簡單,并且文檔非常清晰、豐富的熔斷工具。是Hystrix替代品,實現(xiàn)思路和Hystrix一致,目前持續(xù)更新中
- 需要自己對micrometer、prometheus以及Dropwizard metrics進行整合
- CircuitBreaker 熔斷
- Bulkhead 隔離
- RateLimiter QPS限制
- Retry 重試
- TimeLimiter 超時限制
- Cache 緩存
自己實現(xiàn)(基于Guava)
- 基于Guava的令牌桶,可以輕松實現(xiàn)對QPS進行限流
貳、技術(shù)對比

叁、應(yīng)用改造
3.1、sentinel
3.1.1、引入依賴
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> <version>2.0.3.RELEASE</version> </dependency>
3.1.2、改造接口或者service層
@SentinelResource(value = "allInfos",fallback = "errorReturn")
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface SentinelResource {
//資源名稱
String value() default "";
//流量方向
EntryType entryType() default EntryType.OUT;
//資源類型
int resourceType() default 0;
//異常處理方法
String blockHandler() default "";
//異常處理類
Class<?>[] blockHandlerClass() default {};
//熔斷方法
String fallback() default "";
//默認熔斷方法
String defaultFallback() default "";
//熔斷類
Class<?>[] fallbackClass() default {};
//統(tǒng)計異常
Class<? extends Throwable>[] exceptionsToTrace() default {Throwable.class};
//忽略異常
Class<? extends Throwable>[] exceptionsToIgnore() default {};
}
@RequestMapping("/get")
@ResponseBody
@SentinelResource(value = "allInfos",fallback = "errorReturn")
public JsonResult allInfos(HttpServletRequest request, HttpServletResponse response, @RequestParam Integer num){
try {
if (num % 2 == 0) {
log.info("num % 2 == 0");
throw new BaseException("something bad with 2", 400);
}
return JsonResult.ok();
} catch (ProgramException e) {
log.info("error");
return JsonResult.error("error");
}
}
3.1.3、針對接口配置熔斷方法或者限流方法
默認過濾攔截所有Controller接口
/**
* 限流,參數(shù)需要和方法保持一致
* @param request
* @param response
* @param num
* @return
* @throws BlockException
*/
public JsonResult errorReturn(HttpServletRequest request, HttpServletResponse response, @RequestParam Integer num) throws BlockException {
return JsonResult.error("error 限流" + num );
}
/**
* 熔斷,參數(shù)需要和方法保持一直,并且需要添加BlockException異常
* @param request
* @param response
* @param num
* @param b
* @return
* @throws BlockException
*/
public JsonResult errorReturn(HttpServletRequest request, HttpServletResponse response, @RequestParam Integer num,BlockException b) throws BlockException {
return JsonResult.error("error 熔斷" + num );
}
注意也可以不配置限流或者熔斷方法。通過全局異常去捕獲UndeclaredThrowableException或者BlockException避免大量的開發(fā)量
3.1.4、接入dashboard
spring:
cloud:
sentinel:
transport:
port: 8719
dashboard: localhost:8080

3.1.5、規(guī)則持久化和動態(tài)更新
接入配置中心如:zookeeper等等,并對規(guī)則采用推模式
3.2、hystrix
3.2.1、引入依賴
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId> <version>2.0.4.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> <version>2.0.4.RELEASE</version> </dependency>
3.2.2、改造接口
@HystrixCommand(fallbackMethod = "timeOutError")
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface HystrixCommand {
String groupKey() default "";
String commandKey() default "";
String threadPoolKey() default "";
String fallbackMethod() default "";
HystrixProperty[] commandProperties() default {};
HystrixProperty[] threadPoolProperties() default {};
Class<? extends Throwable>[] ignoreExceptions() default {};
ObservableExecutionMode observableExecutionMode() default ObservableExecutionMode.EAGER;
HystrixException[] raiseHystrixExceptions() default {};
String defaultFallback() default "";
}
@RequestMapping("/get")
@ResponseBody
@HystrixCommand(fallbackMethod = "fallbackMethod")
public JsonResult allInfos(HttpServletRequest request, HttpServletResponse response, @RequestParam Integer num){
try {
if (num % 3 == 0) {
log.info("num % 3 == 0");
throw new BaseException("something bad whitch 3", 400);
}
return JsonResult.ok();
} catch (ProgramException | InterruptedException exception) {
log.info("error");
return JsonResult.error("error");
}
}
3.2.3、針對接口配置熔斷方法
/**
* 該方法是熔斷回調(diào)方法,參數(shù)需要和接口保持一致
* @param request
* @param response
* @param num
* @return
*/
public JsonResult fallbackMethod(HttpServletRequest request, HttpServletResponse response, @RequestParam Integer num) {
response.setStatus(500);
log.info("發(fā)生了熔斷??!");
return JsonResult.error("熔斷");
}
3.2.4、配置默認策略
hystrix:
command:
default:
execution:
isolation:
strategy: THREAD
thread:
# 線程超時15秒,調(diào)用Fallback方法
timeoutInMilliseconds: 15000
metrics:
rollingStats:
timeInMilliseconds: 15000
circuitBreaker:
# 10秒內(nèi)出現(xiàn)3個以上請求(已臨近閥值),并且出錯率在50%以上,開啟斷路器.斷開服務(wù),調(diào)用Fallback方法
requestVolumeThreshold: 3
sleepWindowInMilliseconds: 10000
3.2.5、接入監(jiān)控


曲線:用來記錄2分鐘內(nèi)流量的相對變化,我們可以通過它來觀察到流量的上升和下降趨勢。
集群監(jiān)控需要用到注冊中心
3.3、resilience4j
3.3.1、引入依賴
dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>io.github.resilience4j</groupId> <artifactId>resilience4j-spring-boot2</artifactId> <version>1.6.1</version> </dependency> <dependency> <groupId>io.github.resilience4j</groupId> <artifactId>resilience4j-bulkhead</artifactId> <version>1.6.1</version> </dependency> <dependency> <groupId>io.github.resilience4j</groupId> <artifactId>resilience4j-ratelimiter</artifactId> <version>1.6.1</version> </dependency> <dependency> <groupId>io.github.resilience4j</groupId> <artifactId>resilience4j-timelimiter</artifactId> <version>1.6.1</version> </dependency>
可以按需要引入:bulkhead,ratelimiter,timelimiter等
3.3.2、改造接口
@RequestMapping("/get")
@ResponseBody
//@TimeLimiter(name = "BulkheadA",fallbackMethod = "fallbackMethod")
@CircuitBreaker(name = "BulkheadA",fallbackMethod = "fallbackMethod")
@Bulkhead(name = "BulkheadA",fallbackMethod = "fallbackMethod")
public JsonResult allInfos(HttpServletRequest request, HttpServletResponse response, @RequestParam Integer num){
log.info("param----->" + num);
try {
//Thread.sleep(num);
if (num % 2 == 0) {
log.info("num % 2 == 0");
throw new BaseException("something bad with 2", 400);
}
if (num % 3 == 0) {
log.info("num % 3 == 0");
throw new BaseException("something bad whitch 3", 400);
}
if (num % 5 == 0) {
log.info("num % 5 == 0");
throw new ProgramException("something bad whitch 5", 400);
}
if (num % 7 == 0) {
log.info("num % 7 == 0");
int res = 1 / 0;
}
return JsonResult.ok();
} catch (BufferUnderflowException e) {
log.info("error");
return JsonResult.error("error");
}
}
3.3.3、針對接口配置熔斷方法
/**
* 需要參數(shù)一致,并且加上相應(yīng)異常
* @param request
* @param response
* @param num
* @param exception
* @return
*/
public JsonResult fallbackMethod(HttpServletRequest request, HttpServletResponse response, @RequestParam Integer num, BulkheadFullException exception) {
return JsonResult.error("error 熔斷" + num );
}
3.3.4、配置規(guī)則
resilience4j.circuitbreaker:
instances:
backendA:
registerHealthIndicator: true
slidingWindowSize: 100
backendB:
registerHealthIndicator: true
slidingWindowSize: 10
permittedNumberOfCallsInHalfOpenState: 3
slidingWindowType: TIME_BASED
minimumNumberOfCalls: 20
waitDurationInOpenState: 50s
failureRateThreshold: 50
eventConsumerBufferSize: 10
recordFailurePredicate: io.github.robwin.exception.RecordFailurePredicate
resilience4j.retry:
instances:
backendA:
maxRetryAttempts: 3
waitDuration: 10s
enableExponentialBackoff: true
exponentialBackoffMultiplier: 2
retryExceptions:
- org.springframework.web.client.HttpServerErrorException
- java.io.IOException
ignoreExceptions:
- io.github.robwin.exception.BusinessException
backendB:
maxRetryAttempts: 3
waitDuration: 10s
retryExceptions:
- org.springframework.web.client.HttpServerErrorException
- java.io.IOException
ignoreExceptions:
- io.github.robwin.exception.BusinessException
resilience4j.bulkhead:
instances:
backendA:
maxConcurrentCalls: 10
backendB:
maxWaitDuration: 10ms
maxConcurrentCalls: 20
resilience4j.thread-pool-bulkhead:
instances:
backendC:
maxThreadPoolSize: 1
coreThreadPoolSize: 1
queueCapacity: 1
resilience4j.ratelimiter:
instances:
backendA:
limitForPeriod: 10
limitRefreshPeriod: 1s
timeoutDuration: 0
registerHealthIndicator: true
eventConsumerBufferSize: 100
backendB:
limitForPeriod: 6
limitRefreshPeriod: 500ms
timeoutDuration: 3s
resilience4j.timelimiter:
instances:
backendA:
timeoutDuration: 2s
cancelRunningFuture: true
backendB:
timeoutDuration: 1s
cancelRunningFuture: false
配置的規(guī)則可以被代碼覆蓋
3.3.5、配置監(jiān)控
如grafana等
肆、關(guān)注點
- 是否需要過濾部分異常
- 是否需要全局默認規(guī)則
- 可能需要引入其他中間件
- k8s流量控制
- 規(guī)則存儲和動態(tài)修改
- 接入改造代價
【后面的話】
個人建議的話,比較推薦sentinel,它提供了很多接口便于開發(fā)者自己拓展,同時我覺得他的規(guī)則動態(tài)更新也比較方便。最后是相關(guān)示例代碼:單體應(yīng)用示例代碼
以上就是淺析Spring Boot單體應(yīng)用熔斷技術(shù)的使用的詳細內(nèi)容,更多關(guān)于Spring Boot單體應(yīng)用熔斷技術(shù)的資料請關(guān)注腳本之家其它相關(guān)文章!
- SpringBoot如何使用feign實現(xiàn)遠程接口調(diào)用和錯誤熔斷
- SpringBoot應(yīng)用快速部署到K8S的詳細教程
- 淺談HBase在SpringBoot項目里的應(yīng)用(含HBaseUtil工具類)
- SpringBoot應(yīng)用整合ELK實現(xiàn)日志收集的示例代碼
- Jenkins Pipeline 部署 SpringBoot 應(yīng)用的教程詳解
- 如何在Spring Boot應(yīng)用中優(yōu)雅的使用Date和LocalDateTime的教程詳解
- SpringBoot集成WebSocket長連接實際應(yīng)用詳解
- Shiro + JWT + SpringBoot應(yīng)用示例代碼詳解
相關(guān)文章
mybatis-plus 表名添加前綴的實現(xiàn)方法
這篇文章主要介紹了mybatis-plus 表名添加前綴的實現(xiàn)方法,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08
java.lang.UnsupportedClassVersionError異常正確解決方法
java.lang.UnsupportedClassVersionError異常通常發(fā)生在嘗試在較低版本的Java虛擬機上運行使用更高版本的Jav 編譯器編譯的類文件時,下面就來介紹一下解決方法,感興趣的可以了解一下2024-05-05
SpringCloud Alibaba微服務(wù)實戰(zhàn)之遠程Feign請求頭丟失問題解決方案
這篇文章主要介紹了SpringCloud Alibaba微服務(wù)實戰(zhàn)之遠程Feign請求頭丟失問題,對SpringCloud Alibaba Feign請求頭問題感興趣的朋友跟隨小編一起看看吧2024-02-02
springboot批量接收對象參數(shù),接收List方式
在Spring Boot項目中,批量接收對象參數(shù)可以通過自定義對象和使用`@RequestBody`注解來實現(xiàn),首先,定義一個包含列表的自定義對象,然后在Controller中使用該對象接收前端傳遞的JSON數(shù)組,通過Postman模擬請求,可以成功批量接收并處理對象參數(shù)2025-02-02

