当前位置: 首页 > 科技观察

Spring-Cloud使用Resilience4j实现熔断限流

时间:2023-03-12 11:26:51 科技观察

由于hystrix官方已经停止维护,spring-cloud目前推荐使用resilience4j而不是hystrix实现熔断限流。熔断一般是指客户端调用服务端接口发生异常时在客户端的处理。当然也可以在服务器处理异常时快速返回。可以暂时切断对下游服务的调用。牺牲部分来保留整个Measures(同时这个client也可能是server)是一种有效的方法。限流一般是指在指定的时间间隔内限制请求的数量,避免请求过多导致服务崩溃。限流被视为服务器的自我保护能力。1、客户端openfeign使用resilience4j实现fusing模拟一个client,通过feign调用一个server接口来模拟这个功能。使用spring-boot的版本是2.7.3,spring-cloud的版本是2021.0.4。关键依赖如下:import关键依赖org.springframework.cloudspring-cloud-starter-circuitbreaker-resilience4jorg.springframework.cloudspring-cloud-starter-openfeignorg.springframework.cloudspring-cloud-复制代码starter-loadbalancerio.github.resilience4jresilience4j-feignapplication.yaml添加如下熔断配置。resilience4j:circuitbreaker:instances:ApiService:registerHealthIndicator:trueslidingWindowSize:7slidingWindowType:COUNT_BASEDwaitDurationInOpenState:5000permittedNumberOfCallsInHalfOpenState:6feign:client:config:default:readTimeout:1000connectTimeout:1000circuitbreaker:enabled:truelogging:level:root:信息模式:控制台:"%d{${LOG_DATEFORMAT_PATTERN:HH:mm:ss.SSS}}%m%n"需要注意的是feign需要开启circuitbreaker,在启动类中添加@EnableFeignClients。spring-boot默认会使用logback来管理日志,调整日志格式以观察调整参数的效果。在这种情况下,默认日志格式在org.springframework.boot.logging.logback.DefaultLogbackConfiguration类的defaults方法中定义,该类位于spring-boot:2.7.3包中。添加feign客户端请求类,需要添加fallback方法来处理熔断后的默认返回。只需编写一个测试类即可完成测试并观察相应的参数。写一个假客户端。//ApiClient.java@FeignClient(name="ApiService",url="http://localhost:8082")publicinterfaceApiClient{@GetMapping("/api/test")@CircuitBreaker(name="ApiService",fallbackMethod="getUserFallback")StringgetUser(@RequestParam(name="param")Stringparam,@RequestParam(name="time")inttime);defaultStringgetUserFallback(Stringparam,inttime,Exceptionexc){返回“默认值”;}}编写模拟调用的单元测试。//ResilienceTest.java@Slf4j@SpringBootTestclassResilienceTest{@AutowiredprivateApiClientapiClient;@Autowired私有CircuitBreakerRegistrycircuitBreakerRegistry;@Testvoidtest()throwsException{for(inti=0;i<1000;i++){apiClient.getUser("test",500);地位();线程.睡眠(500);}}privatevoidstatus(){CircuitBreakerbreaker=circuitBreakerRegistry.circuitBreaker("ApiService");CircuitBreaker.Metrics指标=breaker.getMetrics();log.info("state={},metrics[failureRate={},bufferedCalls={},failedCalls={},successCalls={},maxBufferCalls={},notPermittedCalls={}]",breaker.getState(),metrics.getFailureRate()、metrics.getNumberOfBufferedCalls()、metrics.getNumberOfFailedCalls()、metrics.getNumberOfSuccessfulCalls()、metrics.getNumberOfBufferedCalls()、metrics.getNumberOfNotPermittedCalls());}}在没有在任务服务器的情况下,可以运行单元测试来模拟熔断效果。另外需要注意配置的feigncall的超时时间。在单元测试中加入metrics并打印出来观察效果。运行结果如下:简单分析,slidingWindowSize为7,slidingWindowType为计数器,fuse从打开到半开等待5秒,fuse的permittedNumberOfCallsInHalfOpenState为6。。failureRateThreshold的含义:比如60(即%60),这个阈值控制两种状态的变化。CLOSE=>OPEN表示错误率高于60%时熔断器导通,HALF_OPEN=>CLOSE表示错误率低于60%时关闭熔断器。2、服务端使用resilience4j实现限流。使用服务器端接口来简单模拟限流方法。比如设置5秒内最多请求10次,观察异常情况。同样使用spring-boot2.7.3版本和spring-cloud2021.0.4版本进行模拟。引入关键依赖。<依赖>org.springframework.cloudspring-cloud-starter-circuitbreaker-resilience4jorg.springframework.bootspring-boot-starter-aopapplication.yaml配置文件添加ratelimiter配置。resilience4j:ratelimiter:instances:ApiService:limitForPeriod:10limitRefreshPeriod:5stimeoutDuration:0limitRefreshPeriod表示刷新周期,limitForPeriod表示一段时间内的请求总数,配置表示5秒内允许10次请求。写一个controller接收请求,service处理请求并增加限流控制,在限流后增加异常处理。//ApiController.java@Slf4j@RequestMapping("/api")@RestControllerpublicclassApiController{@AutowiredprivateApiServiceapiService;@GetMapping("/limit")publicStringlimit(){returnapiService.limit();}}//ApiService.java@Slf4j@ServicepublicclassApiService{@RateLimiter(name="ApiService",fallbackMethod="testFallback")publicStringlimit(){return"success";}@SneakyThrowspublicStringtestFallback(java.lang.Throwableexception){抛出异常;}}//Advice.java@Slf4j@ControllerAdvicepublicclassAdvice{@ExceptionHandler(RequestNotPermitted.class)@ResponseStatus(HttpStatus.TOO_MANY_REQUESTS)publicvoiderror(){log.error("太多请求");}}编写ControllerAdvice,通过处理RequestNotPermitted异常返回客户端响应码429。写一个测试类来模拟节流情况。@Slf4j@SpringBootTestclassResilienceTest{privateRestTemplaterestTemplate=newRestTemplate();@Testvoidlimit()抛出异常{for(inti=1;i<=99;i++){try{ResponseEntityresponse=restTemplate.getForEntity("http://localhost:9999/api/limit",字符串.class);log.info("状态码{}{}",String.format("%02d",i),response.getStatusCode());}catch(HttpClientErrorExceptione){log.error("状态码{}",e.getStatusCode());}Thread.sleep(400);}}}可以适当减少单元测试的sleep时间,观察出现的限流情况。