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));
    }

}
相关推荐
西岸行者5 天前
学习笔记:SKILLS 能帮助更好的vibe coding
笔记·学习
悠哉悠哉愿意5 天前
【单片机学习笔记】串口、超声波、NE555的同时使用
笔记·单片机·学习
别催小唐敲代码5 天前
嵌入式学习路线
学习
毛小茛5 天前
计算机系统概论——校验码
学习
babe小鑫5 天前
大专经济信息管理专业学习数据分析的必要性
学习·数据挖掘·数据分析
winfreedoms5 天前
ROS2知识大白话
笔记·学习·ros2
在这habit之下5 天前
Linux Virtual Server(LVS)学习总结
linux·学习·lvs
我想我不够好。5 天前
2026.2.25监控学习
学习
im_AMBER5 天前
Leetcode 127 删除有序数组中的重复项 | 删除有序数组中的重复项 II
数据结构·学习·算法·leetcode
CodeJourney_J5 天前
从“Hello World“ 开始 C++
c语言·c++·学习