Resilience4j 断路器学习

一. 示例demo

1. maven依赖

xml 复制代码
<!--resilience4j-->
<dependency>
    <groupId>io.github.resilience4j</groupId>
    <artifactId>resilience4j-circuitbreaker</artifactId>
    <!--<version>2.2.0</version>-->
    <version>0.13.2</version>
</dependency>

2. ServerB

java 复制代码
public class ServerB {

    private CircuitBreakerRegistry breakerRegistry;

    private ServerC serverC = new ServerC(); //让服务B持有一个服务C的引用,用来表示正常服务间调用里的一个连接引用

    ServerB() {
        //初始化breaker注册器,可以利用该对象生产各种breaker对象(注:凡是用同一个注册器生产出来的breaker,都会继承注册器的配置属性)
        breakerRegistry = CircuitBreakerRegistry.of(CircuitBreakerConfig.custom() //of方法里面放的就是breaker的配置属性对象
            .enableAutomaticTransitionFromOpenToHalfOpen() //开启从全开状态经过下面的waitDurationInOpenState时间后自动切换到半开状态
            .failureRateThreshold(50) //熔断器闭合状态下的错误率阈值,50表示50%,如果错误率达到这个阈值,那么熔断器将进入全熔断状态
            .ringBufferSizeInClosedState(100) //熔断器闭合状态下,以该值为单位请求数,计算错误率,跟上面错误率阈值综合理解,这个值表示至少有100个请求,且错误50个以上才会触发全熔断
            .ringBufferSizeInHalfOpenState(10) //熔断器半熔断状态下,以该值为单位请求数,计算错误率,跟上面错误率阈值综合理解,这个值表示至少有10个请求,且错误5个以上会再次触发全熔断,相比闭合状态,半熔断状态下更容易再次进入全熔断状态
            .waitDurationInOpenState(Duration.ofMillis(1000L)) //熔断器全熔断状态持续的时间,全熔断后经过该时间后进入半熔断状态
            .build());
    }

    //服务B通过服务C来获取到C的info信息,该方法就是用来干这个的,它会发起对服务C的调用
    public String getCInfo(int id) {
        //breaker对象是按照name划分全局单例的
        CircuitBreaker breaker = breakerRegistry.circuitBreaker("getCInfo"); //这里给熔断器取个名,一般情况就是一个服务的path或方法名
        try {
            return breaker.executeCallable(() -> serverC.getCInfo(id));
        } catch (CircuitBreakerOpenException e) { //一旦抛出该异常说明已经进入全熔断状态
            //被熔断后的降级逻辑
            return "服务C出错,触发服务B的降级逻辑";
        } catch (Exception e) {
            //熔断关闭或者半熔断状态下,C抛出的错误会被catch到这里
            return "调用服务C出错";
        }
    }

    public CircuitBreaker getBreaker() {
        return breakerRegistry.circuitBreaker("getCInfo"); //为了方便做测试,这里返回对应的breaker对象
    }
}

3. ServerC

java 复制代码
public class ServerC {

    public String getCInfo(int id) {
        if (id == 0) {
            throw new RuntimeException("输入0异常");
        }
        return "id=" + id + "的C信息";
    }

}

4. Test01

java 复制代码
public class Test01 {

    private static ServerB serverB = new ServerB();

    public static void main(String[] args) throws Exception {
        testBreak();
    }

    /**
     * 测试熔断/半开/关闭的全过程
     * @throws Exception
     */
    public static void testBreak() throws Exception {
        //按照B服务里熔断器的配置,如果进行100次请求,有50次失败了,则对ServerC的调用进入全熔断状态
        //1000ms后恢复为半熔断状态,半熔断状态下进行10次请求,如果有5次依然失败,则再次进入全熔断状态
        for (int i = 0; i < 100; i++) {
            if (i < 50) {
                serverB.getCInfo(0); //前50次全部报错
            } else {
                serverB.getCInfo(1); //后50次全部成功
            }
        }
        //断言:此时熔断器为全熔断状态
        System.out.println(serverB.getBreaker().getState().equals(CircuitBreaker.State.OPEN));
        //全熔断状态下并不会实际调用C,而是会走服务B的降级逻辑,即便我们输入的参数是对的,也一样会被降级
        System.out.println(serverB.getCInfo(1));

        System.out.println("-----------我是分隔符------------");

        Thread.sleep(500L);
        //断言:由于全熔断状态配置的持续时间时1000ms,所以500ms过去后,仍然是全熔断状态
        System.out.println(serverB.getBreaker().getState().equals(CircuitBreaker.State.OPEN));

        Thread.sleep(500L);
        //断言:1000ms过后,熔断器处于半熔断状态
        System.out.println(serverB.getBreaker().getState().equals(CircuitBreaker.State.HALF_OPEN));
        //半熔断状态下会尝试恢复,所以会实际调用C,分别输入正确和错误的参数进行测试
        System.out.println(serverB.getCInfo(1));
        System.out.println(serverB.getCInfo(0));

        System.out.println("-----------我是分隔符------------");

        //半熔断状态下,只需要请求10次,有5次出错即可再次进入全熔断状态
        for (int i = 0; i < 10; i++) {
            if (i < 4) { //因为上面传过一次0了,所以这里只需要4次便可以重新回到全开状态
                serverB.getCInfo(0); //前5次全部报错
            } else {
                serverB.getCInfo(1); //后5次全部成功
            }
        }
        //断言:此时熔断器为全熔断状态
        System.out.println(serverB.getBreaker().getState().equals(CircuitBreaker.State.OPEN));
        //同样的,全熔断状态下并不会实际调用C,而是会走服务B的降级逻辑
        System.out.println(serverB.getCInfo(1));

        System.out.println("-----------我是分隔符------------");


        //这时静待1000ms,再次进入半熔断状态,我们尝试恢复服务C的调用
        Thread.sleep(1000L);

        //这时我们让其10次请求里有6次成功
        for (int i = 0; i < 10; i++) {
            if (i < 6) { //前6次成功
                serverB.getCInfo(1);
            } else { //后4次失败
                serverB.getCInfo(0);
            }
        }
        //由于10次请求里只失败了4次,达不到50%的全开阈值,所以此时会恢复
        //断言:此时熔断器为闭合状态
        System.out.println(serverB.getBreaker().getState().equals(CircuitBreaker.State.CLOSED));
        System.out.println(serverB.getCInfo(1)); //正常输出
        System.out.println(serverB.getCInfo(0)); //走普通异常逻辑

        System.out.println("-----------我是分隔符------------");
    }

}

5. Test02

java 复制代码
public class Test02 {

    private static ServerB serverB = new ServerB();

    public static void main(String[] args) throws Exception {
        // testRate1();
        testRate2();
    }

    /**
     * 测试熔断的次数时机1
     */
    public static void testRate1() {
        //首先闭合状态下单位请求仍然是100,现在让前49次全部失败
        for (int i = 0; i < 100; i++) {
            if (i < 49) {
                serverB.getCInfo(0);
            } else {
                serverB.getCInfo(1);
            }
        }
        //断言:虽然请求了100次,但是错误率并没有达到阈值(50%),所以这里仍然是闭合状态的
        System.out.println(serverB.getBreaker().getState().equals(CircuitBreaker.State.CLOSED));
        //这里再让其失败一次
        serverB.getCInfo(0);
        //断言:这里应该还是闭合状态的,按照100次单位请求来看,第一次失败的那个请求会被这次失败这个请求顶替掉(这里不理解没关系,下面有图)
        System.out.println(serverB.getBreaker().getState().equals(CircuitBreaker.State.CLOSED));
    }

    /**
     * 测试熔断的次数时机2
     */
    public static void testRate2() throws InterruptedException {
        //首先闭合状态下单位请求仍然是100,仍然让其错误49次,但现在让第2~50次失败
        for (int i = 0; i < 100; i++) {
            if (i != 0 && i < 50) { //第2~50次请求失败,总计失败49次
                serverB.getCInfo(0);
            } else {
                serverB.getCInfo(1);
            }
        }
        //断言:跟上面例子一样,错误率并没有达到阈值,仍然是闭合状态
        System.out.println(serverB.getBreaker().getState().equals(CircuitBreaker.State.CLOSED));

        // Thread.sleep(3000);
        //这里再让其失败一次
        serverB.getCInfo(0);
        //断言:这里应该是全开状态,按照100次单位请求来看,第一次成功的那个请求会被这次失败这个请求顶替掉,然后凑够50次失败请求(参考图4)
        System.out.println(serverB.getBreaker().getState().equals(CircuitBreaker.State.OPEN));
    }

}
相关推荐
谢白羽40 分钟前
深度神经网络模型压缩学习笔记三:在线量化算法和工具、实现原理和细节
笔记·学习·dnn
楚疏笃1 小时前
鸿蒙学习自由流转与分布式运行环境-价值与架构定义(1)
学习·架构·harmonyos
架构文摘JGWZ1 小时前
替代Postman ,17.3K star!
学习·工具
NightCyberpunk1 小时前
Ajax与Vue初步学习
vue.js·学习·ajax
宇寒风暖2 小时前
软件工程——UML简介
笔记·学习·软件工程
ZZZ_O^O2 小时前
【贪心算法第五弹——300.最长递增子序列】
c++·学习·算法·leetcode·贪心算法
&黄昏的乐师2 小时前
Opencv+ROS实现颜色识别应用
人工智能·opencv·学习·计算机视觉
蒙特网站2 小时前
网站布局编辑器前端开发:设计要点与关键考量
前端·javascript·学习·html
@曲终2 小时前
C语言学习 12(指针学习1)
c语言·经验分享·笔记·学习
一只小菜鸡..3 小时前
241126学习日志——[CSDIY] [ByteDance] 后端训练营 [19]
学习