SpringBoot使用Resilience4j容错熔断重试
Resilience4j是一个轻量级、易于使用的轻量级容错包。它受NeflixHystrix启发但只有一个依赖(Vavr),而不像Hystrix很多很多的依赖。
Resilience4j在容错方面提供了各种模式:断路器(CircuitBreaker)、重试(Retry)、限时器(TimeLimiter)、限流器(RateLimiter)、隔板(BulkHead)。
在SpringBoot下,Resilience4j比Hystrix更适合用在容错的各种模式下。我们只要在程序中使用简单的注解即可实现。
那我们新建一个演示项目来演示功能:
暂时的依赖只需要SpringWeb、SpringBootActuator。1、项目准备1。1添加Resilience4j依赖
在SpringBoot下使用Resilience4j,需要加上下面两个依赖gradle:implementationio。github。resilience4j:resilience4jspringboot2:1。7。1implementationorg。springframework。boot:springbootstarteraopmaven:dependencygroupIdio。github。resilience4jgroupIdresilience4jspringboot2artifactIdversion1。7。1veriondependencydependencygroupIdorg。springframework。bootgroupIdspringbootstarteraopartifactIddependency
2、断路器(CircuitBreaker)
断路器来自于生活中的断路器,是指当电流超过规定值时,以本身产生的热量使熔体熔断,断开电路的一种电器。
软件开发中的断路器有三个状态:
关闭(CLOSED):正常情况,所有的请求都正常通过断路器,没有任何限制。
打开(OPEN):在过去的请求或者时间中,如果故障或者慢的响应率大于或者等于一个配置的阈值,断路器就会打开。在这种情况下,所有的请求都会受到限制。
半开(HALFOPEN):在打开状态下经过可配置的等待时间后,断路器允许少量(数值可配置)的请求通过。若失败慢响应超过阈值,断路器重新打开;低于阈值,则断路器进入关闭状态。
断路器通过限制上游服务调用,在下游服务在部分或者全部停止服务的情况下,对下游服务进行保护。
我们新建控制器,并在当前的控制器上编写断路器的相关代码:RestControllerpublicclassResilience4jController{privatefinalRestTemplaterestTemplate;publicResilience4jController(RestTemplateBuilderbuilder){this。restTemplatebuilder。build();}GetMapping(circuitbreaker)CircuitBreaker(namecircuitBreakerDemo)publicStringcircuitBreaker(){returnrestTemplate。getForObject(http:nosuchsiteapiexternal,String。class);}}
我们在控制器方法circuitBreaker()调用下游的服务,本例演示中的下游服务并不存在。
我们可以简单的在方法上使用CircuitBreaker注解即可使用Resilience4j提供的断路器,name中定义的circuitBreakerDemo是当前断路器的名称。
我们可以通过application。properties对断路器的具体行为进行配置,instances后面的名称是在CircuitBreaker中配置的名字:resilience4j。circuitbreaker。instances。circuitBreakerDemo。slidingwindowtypeCOUNTBASEDresilience4j。circuitbreaker。instances。circuitBreakerDemo。slidingwindowsize10resilience4j。circuitbreaker。instances。circuitBreakerDemo。failureratethreshold50resilience4j。circuitbreaker。instances。circuitBreakerDemo。permittednumberofcallsinhalfopenstate3resilience4j。circuitbreaker。instances。circuitBreakerDemo。minimumnumberofcalls5resilience4j。circuitbreaker。instances。circuitBreakerDemo。waitdurationinopenstate5s
1、slidingwindowtype:断路器的滑动窗口期类型可以基于次数(COUNTBASED)或者时间(TIMEBASED)进行熔断,默认是COUNTBASED。
2、failureratethreshold:设置50的调用失败时打开断路器。
3、slidingwindowsize:若COUNTBASED,则10次调用中有50失败(即5次)打开熔断断路器;若为TIMEBASED则,此时有额外的两个设置属性,含义为:在秒内(slidingwindowsize)100(slowcallratethreshold)的请求超过2秒(slowcalldurationthreshold)打开断路器。resilience4j。circuitbreaker。instances。circuitBreakerDemo。slowcallratethreshold100resilience4j。circuitbreaker。instances。circuitBreakerDemo。slowcalldurationthreshold2000
4、permittednumberofcallsinhalfopenstate:运行断路器在HALFOPEN状态下时进行3次调用,如果故障或慢速调用仍然高于阈值,断路器再次进入打开状态。
5、minimumnumberofcalls:在每个滑动窗口期,配置断路器计算错误率或者慢调用率的最小调用数。本例中设置的5意味着,在计算故障率之前,必须至少调用5次。如果只记录了4次,即使4次都失败了,断路器也不会进入到打开状态。
6、waitdurationinopenstate:一旦断路器是打开状态,它会拒绝请求5秒钟,然后转入半开状态。
更多关于断路器的配置可参考官方文档:https:resilience4j。readme。iodocscircuitbreakercreateandconfigureacircuitbreaker2。1断路器的Acturator
在application。properties里添加相关的配置:management。endpoints。web。exposure。includemanagement。endpoint。health。showdetailsalways显示断路器的健康状态management。health。circuitbreakers。enabledtrueresilience4j。circuitbreaker。instances。circuitBreakerDemo。registerhealthindicatortrue2。3断路器的异常处理
当断路器处于打开和半开状态时,会抛出CallNotPermittedException异常,我们可以来进行全局处理。RestControllerAdvicepublicclassResilience4jExceptionHandler{ExceptionHandler({CallNotPermittedException。class})ResponseStatus(HttpStatus。SERVICEUNAVAILABLE)publicStringhandleCallNotPermittedException(){return调用不被允许;}}2。4演示效果
启动程序,用postman访问:http:localhost:8080circuitbreaker,调用5次。第6次,断路器被打开:
健康状态
访问:http:localhost:8080actuatorhealth
指标
访问:http:localhost:8080actuatormetrics
我们可以继续访问:
http:localhost:8080actuatormetricsresilience4j。circuitbreaker。buffered。calls
http:localhost:8080actuatormetricsresilience4j。circuitbreaker。calls
http:localhost:8080actuatormetricsresilience4j。circuitbreaker。failure。rate
http:localhost:8080actuatormetricsresilience4j。circuitbreaker。not。permitted。calls
http:localhost:8080actuatormetricsresilience4j。circuitbreaker。slow。call。rate
http:localhost:8080actuatormetricsresilience4j。circuitbreaker。slow。call
http:localhost:8080actuatormetricsresilience4j。circuitbreaker。state
事件
http:localhost:8080actuatorcircuitbreakerevents
http:localhost:8080actuatorcircuitbreakerevents{name}{eventType}
http:localhost:8080actuatorcircuitbreakerevents{name}
http:localhost:8080actuatorcircuitbreakers
http:localhost:8080actuatorcircuitbreakers{name}
3、重试(Retry)
重试在远程服务调用时是必须的能力。我们在控制器中添加演示重试的控制器方法:RestControllerpublicclassResilience4jController{privatefinalRestTemplaterestTemplate;publicResilience4jController(RestTemplateBuilderbuilder){this。restTemplatebuilder。build();}GetMapping(retry)Retry(nameretryDemo,fallbackMethodfallback)publicStringretry(){returnrestTemplate。getForObject(http:nosuchsiteapiexternal,String。class);}publicStringfallback(Exceptionexception){return系统故障,无法访问;}}
同样,我们可以用Retry注解轻松使用Resilience4j的重试能力。
同样,我们可以在application。properties中配置重试。resilience4j。retry。instances。retryDemo。maxattempts3resilience4j。retry。instances。retryDemo。waitduration1sresilience4j。retry。metrics。legacy。enabledtrueresilience4j。retry。metrics。enabledtrue
1、maxattempts:最大尝试重试次数。
2、waitduration:每次重试之间的间隔时间。
更多的重试配置,请参考官网:https:resilience4j。readme。iodocsretry3。1演示效果
访问:http:localhost:8080retry,重试后,进入后备方法fallback。
指标
actuatormetrics
actuatormetrics{requiredMetricName}事件
actuatorretries
actuatorretryevents
actuatorretryevents{name}
actuatorretryevents{name}{eventType}4、隔板(Bulkhead)
隔板来自造船行业,床仓内部一般会分成很多小隔舱,一旦一个隔舱漏水因为隔板的存在而不至于影响其它隔舱和整体船。
隔板(Bulkhead)可以用来限制对于下游服务的最大并发数量的限制。
我们在控制器中添加方法来演示隔板:GetMapping(bulkhead)Bulkhead(namebulkheadDemo)publicStringbulkhead(){returnrestTemplate。getForObject(https:reqres。inapiusers,String。class);}
使用Bulkhead注解实现隔板功能,在application。properties中配置隔板:resilience4j。bulkhead。instances。bulkheadDemo。maxconcurrentcalls3resilience4j。bulkhead。instances。bulkheadDemo。maxwaitduration1resilience4j。bulkhead。metrics。enabledtrue
1、maxconcurrentcalls:隔板允许的最大并发执行数量
2、maxwaitduration:当试图进入一个饱和的隔板时,线程应被阻断的最大时间。
Resilience4j的隔板支持两种类型:
默认的Semaphore:使用用户请求的线程,而不创建新的线程。
线程池:创建新的线程用来处理。配置如:resilience4j。threadpoolbulkhead。instances。bulkheadDemo。maxthreadpoolsize3resilience4j。threadpoolbulkhead。instances。bulkheadDemo。corethreadpoolsize2resilience4j。threadpoolbulkhead。instances。bulkheadDemo。queuecapacity1
更多隔板的配置,参考官网:https:resilience4j。readme。iodocsbulkhead4。1异常处理
当隔板饱和的时候会抛出BulkheadFullException异常,添加到Resilience4jExceptionHandler。ExceptionHandler({BulkheadFullException。class})ResponseStatus(HttpStatus。BANDWIDTHLIMITEXCEEDED)publicStringhandleBulkheadFullException(){return隔板已满;}4。2演示演示
用ApiFox访问:http:localhost:8080bulkhead,并发设置为4,得到结果:
我们看下失败请求的明细:
指标
actuatormetrics
actuatormetrics{requiredMetricName}事件
actuatorbulkheads
actuatorbulkheadevents
actuatorbulkheadevents{name}
actuatorbulkheadevents{name}{eventType}5、限流器(RateLimiter)
限流器用来限制访问下游服务的速度。控制器中添加演示方法:GetMapping(ratelimiter)RateLimiter(namerateLimitDemo)publicStringrateLimit(){returnrestTemplate。getForObject(https:reqres。inapiusers,String。class);}
同样限流只需要使用注解RateLimiter即可。在application。properties添加配置:resilience4j。ratelimiter。instances。rateLimitDemo。limitforperiod5resilience4j。ratelimiter。instances。rateLimitDemo。limitrefreshperiod60sresilience4j。ratelimiter。instances。rateLimitDemo。timeoutduration0sresilience4j。ratelimiter。instances。rateLimitDemo。allowhealthindicatortofailtrueresilience4j。ratelimiter。instances。rateLimitDemo。subscribeforeventstrueresilience4j。ratelimiter。instances。rateLimitDemo。eventconsumerbuffersize50resilience4j。ratelimiter。metrics。enabledtrueresilience4j。ratelimiter。instances。rateLimitDemo。registerhealthindicatortrue
上面的配置限制调用的限制是每60秒(limitrefreshperiod)的访问次数是5次(limitforperiod)。
更多关于限流器的配置请参考官网:https:resilience4j。readme。iodocsratelimiter5。1异常处理
当达到允许的速度的时候,请求将会被拒绝,程序抛出RequestNotPermitted异常。ExceptionHandler({RequestNotPermitted。class})ResponseStatus(HttpStatus。TOOMANYREQUESTS)publicStringhandleRequestNotPermitted(){return请求不被允许;}5。2效果效果
访问:http:localhost:8080ratelimiter,在60秒内访问次数超过5次:
健康
actuatorhealth指标
actuatormetrics
actuatormetrics{requiredMetricName}事件
actuatorratelimiters
actuatorratelimiterevents
actuatorratelimiterevents{name}
actuatorratelimiterevents{name}{eventType}6、限时器(TimeLimiter)
限时器用来限制在另外一个线程中执行的服务调用的时间。在控制器中新建方法,我们需要调用一个异步服务,即在另一个线程中执行的服务:RestControllerpublicclassResilience4jController{privatefinalRestTemplaterestTemplate;privatefinalAsyncServiceasyncService;publicResilience4jController(RestTemplateBuilderbuilder,AsyncServiceasyncService){this。restTemplatebuilder。build();this。asyncServiceasyncService;}GetMapping(timelimiter)TimeLimiter(nametimeLimiterDemo)publicCompletableFutureStringtimeLimiter(){returnasyncService。doSomething();}}
通过TimeLimiter注解使用显示器功能,我们程序还需使用EnableAsync开启异步的支持:SpringBootApplicationEnableAsyncpublicclassResilience4jDemoApplication{publicstaticvoidmain(String〔〕args){SpringApplication。run(Resilience4jDemoApplication。class,args);}}
异步服务:ServicepublicclassAsyncService{privatefinalRestTemplaterestTemplate;publicAsyncService(RestTemplateBuilderbuilder){this。restTemplatebuilder。build();}AsyncpublicCompletableFutureStringdoSomething(){try{Thread。sleep(3000);}catch(InterruptedExceptione){thrownewRuntimeException(e);}returnnewAsyncResultString(restTemplate。getForObject(https:reqres。inapiusers,String。class))。completable();}}
在这个异步线程任务里,线程睡眠了3秒钟。
我们在application。properties中对限时器进行配置:resilience4j。timelimiter。instances。timeLimiterDemo。timeoutduration2sresilience4j。timelimiter。instances。timeLimiterDemo。cancelrunningfuturetrueresilience4j。timelimiter。metrics。enabledtrue
配置意味着当异步线程处理时间超过2s(timeoutduration)后将会限制访问。上面异步线程处理中线程睡了3秒,那请求必然超时。
更多关于限时器的配置,请查看官网:https:resilience4j。readme。iodocstimeout6。1异常处理
当异步处理线程超时后,将会抛出TimeoutException异常。ExceptionHandler({TimeoutException。class})ResponseStatus(HttpStatus。REQUESTTIMEOUT)publicStringhandleTimeoutException(){return访问超时;}6。2演示演示
访问:http:localhost:8080timelimiter,提示访问超时。
指标
actuatormetrics
actuatormetrics{requiredMetricName}事件
actuatortimelimiters
actuatortimelimiterevents
actuatortimelimiterevents{name}
actuatortimelimiterevents{name}{eventType}
感谢对我的书《从企业级开发到云原生微服务:SpringBoot实战》的支持。
转载请注明出处:今日头条:爱科学的卫斯理(此处已添加书籍卡片,请到今日头条客户端查看)