Spring Cloud 学习笔记 —— Resilience4 简介和基本用法

    科技2022-08-15  113

    10.1 Resilience4 简介

    Resilience4j 是 Spring Cloud Greenwich 版推荐的容错解决方案。 可能成为主流的解决方案,因为 Hystrix 的公司 Netflix 不再更新, Resilience4j 是受 Hystrix 启发设计出来的 ;相比 Hystrix,Resilience4j 专门为 Java 8 以及函数式编程而设计,更加轻量级,没有任何外部依赖性,除了 resilience4j 自身的依赖,不需要其他依赖

    Resilience4j 主要提供功能 (1)断路器 (2)限流 (3)基于信号量的隔离 (4)缓存 (5)请求限时 (6)请求重试

    10.2 Resilience4j 在 JavaSe基本用法

    搭建环境

    (1)创建一个普通的 maven 项目 resilience4j 作为 javaboycloud 项目的 module。并添加 junit 依赖 <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13</version> </dependency>

    10.2.1 断路器

    Resilience4j 提供了很多功能,不同功能对应不同依赖,按需添加

    (1)引入 resilience4j-circuitbreaker 依赖 <dependency> <groupId>io.github.resilience4j</groupId> <artifactId>resilience4j-circuitbreaker</artifactId> <version>1.5.0</version> </dependency>

    (2)在 test 目录,创建一个包路径 org.javaboy.resilience4j,新建类 Resilience4jTest, 获取一个 CircuitBreakRegistry 实例,可以调用 ofDefault 方法,也可以自定义。 一个正常执行的例子:

    @Test public void test1(){ // 获取一个 CircuitBreakerRegistry 实例,可以 ofDefaults 来获取 CircuitBreakerRegistr 实例,也可以自定义属性 CircuitBreakerRegistry registry = CircuitBreakerRegistry.ofDefaults(); CircuitBreakerConfig config = CircuitBreakerConfig.custom() //故障率阈值百分比,超过这个阈值,断路器就会打开 .failureRateThreshold(50) //断路器保持打开的时间,在到达设置的时间后,断路器会进入 half open 状态 .waitDurationInOpenState(Duration.ofMillis(1000)) // 当断路器处于 half open 状态,环形缓冲区的大小 .ringBufferSizeInHalfOpenState(2) //当断路器关闭时,环形缓冲区的大小 .ringBufferSizeInClosedState(2) .build(); CircuitBreakerRegistry r1 = CircuitBreakerRegistry.of(config); //第一种写法 CircuitBreaker cb1 = r1.circuitBreaker("javaboy"); CircuitBreaker cb2 = r1.circuitBreaker("javaboy2", config); CheckedFunction0<String> supplier = CircuitBreaker.decorateCheckedSupplier(cb1, () -> "Hello resilience4j"); Try<String> result = Try.of(supplier) .map(v -> v + "hello world"); System.out.println(result.isSuccess()); System.out.println(result.get()); }

    运行结果:

    一个出异常的断路器

    @Test public void test2(){ CircuitBreakerConfig config = CircuitBreakerConfig.custom() //故障率阈值百分比,超过这个阈值,断路器就会打开,默认也是 50% .failureRateThreshold(50) //断路器保持打开的时间,在到达设置的时间后,断路器会进入 half open 状态 .waitDurationInOpenState(Duration.ofMillis(1000)) // 当断路器处于 half open 状态,唤醒缓冲区的大小 .ringBufferSizeInClosedState(2) .build(); CircuitBreakerRegistry r1 = CircuitBreakerRegistry.of(config); CircuitBreaker cb1 = r1.circuitBreaker("javaboy"); //获取断路器状态 System.out.println(cb1.getState());//CLOSED //第一条数据 cb1.onError(0, TimeUnit.SECONDS,new RuntimeException()); //获取断路器状态 System.out.println(cb1.getState());//CLOSED //第二条数据 cb1.onSuccess(0,TimeUnit.SECONDS); //获取断路器状态 System.out.println(cb1.getState());//OPEN,因为一共两次,其中一共错误,故障率到达 50%,断路器开启,不在向下执行 CheckedFunction0<String> supplier = CircuitBreaker.decorateCheckedSupplier(cb1, () -> "Hello resilience4j"); //有 Try.of 也有 Try.run Try<String> result = Try.of(supplier) .map(v -> v + "hello world"); System.out.println(result.isSuccess());//false System.out.println(result.get()); }

    运行结果: .ringBufferSizeInClosedState(2)表示当有两条数据时才会统计故障率,所以,下面的手动故障,至少调用两次 onError 或者一次 onError 、一次 onSuccess 断路器才会打开。

    10.2.2 限流

    Resilience4j 中的限流 RateLimiter 本身和前面的断路器很像,有 RateLimiterRegistry、RateLimiterConfig.

    (1)添加依赖 <dependency> <groupId>io.github.resilience4j</groupId> <artifactId>resilience4j-ratelimiter</artifactId> <version>1.5.0</version> </dependency> (2)测试 @Test public void test3(){ // 每个周期为 1 秒(limitRefreshPeriod(Duration.ofMillis(1000))) RateLimiterConfig config = RateLimiterConfig.custom().limitRefreshPeriod(Duration.ofMillis(1000)) // 为了测试方便,只允许两个请求,即每秒两个请求 .limitForPeriod(2) // 限流之后的冷却时间 .timeoutDuration(Duration.ofMillis(1000)) .build(); RateLimiter rateLimiter = RateLimiter.of("javaboy", config); CheckedRunnable checkedRunnable = RateLimiter.decorateCheckedRunnable(rateLimiter, () -> { System.out.println(new Date()); }); // 调用 Java 8 流式编程的 run 方法 //一共调用四次,因为每秒只能处理两个,所以应该分两秒打印结果 Try.run(checkedRunnable) .andThenTry(checkedRunnable) .andThenTry(checkedRunnable) .andThenTry(checkedRunnable) //失败了,打印错误 .onFailure(t-> System.out.println(t.getMessage())); }

    运行结果: 如果把.limitForPeriod(4)参数改为4,那么结果在一秒打印:

    10.2.3 请求重试

    Spring 家族中使用的比较多的时 Spring-retry,Resilience4j 中也提供了重试的功能,而且 Resilience4j 的重试更加轻量级,而且可以根据结果来决定是否要进行重试。一句话:Resilience4j 更好用一些。

    (1)添加依赖 <dependency> <groupId>io.github.resilience4j</groupId> <artifactId>resilience4j-retry</artifactId> <version>1.5.0</version> </dependency> (2)测试: @Test public void test4(){ RetryConfig config = RetryConfig.custom() // 最大重试次数 2 次 .maxAttempts(4) // 重试时间间隔 .waitDuration(Duration.ofMillis(500)) // 发生指定异常重试 .retryExceptions(RuntimeException.class) .build(); Retry retry = Retry.of("javaboy", config); // 开启重试功能之后,run 方法如果抛异常,会自动触发重试功能 Retry.decorateRunnable(retry,new Runnable(){ int count = 0; @Override public void run() { System.out.println(count); if(count++ < 3){ throw new RuntimeException(); } System.out.println("retry"); } }).run(); }

    运行结果:

    这里的重试,是以面向切面的方式来代理 run() 方法,count 的属性时会递增的,重试了两次还不成功,就抛出异常了,即代理不重试了,抛出异常;如果把 .maxAttempts(4)改成 4 次,那第四次 count = 3,那么就会不走 if 判断结果就成功了: 这就是重试功能,本来应该报错的,经过重试后,返回了正确的结果

    Processed: 0.015, SQL: 9