Resilience4j 实践

微服务设计模式 – circuit breaker

circuit breaker 熔断器,在很多不同的领域都有这个定义,例如电路里面的熔断器,股票行业里面的熔断,当然我们这里的熔断指的是在计算机编程里面的,设计模式里的”熔断”。

简单的说,这就是一个状态机,然后状态机之间的转换逻辑;这个状态机有三个状态,分别是:

  • 开 open
  • 关 closed
  • 半开 half-open

这三个状态的转换关系,如下图:即正常状态下,熔断器是处于关闭的状态,内部维护一个错误数计数器,如果有错误发生,就增加这个计数器,直到 一段时间内错误率达到或者超过阈值(failure threshold),此时熔断器打开,处于开的状态。熔断器开的状态下,所有新的操作都不进行了,这是为了给集群一点时间进行恢复,开启状态会有一个超时时间,超过这个时间之后,熔断器转为半开的状态;在半开状态下,熔断器会允许一定数量的操作,如果这些操作全部成功,那么熔断器会重置错误计数器,并回到关闭的状态;如果有一次失败了,那么熔断器又会变为开的状态,开启下一次超时计时。

Resilience4j 实践

熔断器的思想也非常简单,只需要将你想要保护的方法包裹在一个熔断器里面,一旦熔断器开启了,后续请求都将返回error,或者其他降级处理,而根本不会再执行你保护的这个方法。一般熔断器开启也会配合告警一起使用。

Resilience4j 实践

参考资料:
circuit breaker

Resilience4j – circuit breaker

这里介绍一些resilience4j中的circuit breaker的基础用法

maven 引入


    io.github.resilience4j
    resilience4j-all
    1.7.0

circuit breaker 初始化

CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom()
        .failureRateThreshold(20)
        .waitDurationInOpenState(Duration.ofMillis(500))
        .slidingWindowSize(5)
        .permittedNumberOfCallsInHalfOpenState(3)
        .recordException(new Predicate() {
            @Override
            public boolean test(Throwable throwable) {
                return true;
            }
        })
        .automaticTransitionFromOpenToHalfOpenEnabled(true)
        .build();

//注册
CircuitBreakerRegistry circuitBreakerRegistry = CircuitBreakerRegistry.of(circuitBreakerConfig);

CircuitBreaker circuitBreaker = circuitBreakerRegistry.circuitBreaker("test");

几个熔断器的关键参数:

  • failureRateThreshold 即失败阈值的百分比,默认是50
  • waitDurationInOpenState 即熔断器开启时的超时时间设置
  • slidingWindowSize 即上文所述一段时间的这个一段时间的定义
  • permittedNumberOfCallsInHalfOpenState 即半开状态允许执行的操作数
  • recordException 定义一个Predicate,来判断是否发生的一个exception需要被错误计数器计数,这里直接返回true,就是任何exception发生,都会增加错误计数器
  • automaticTransitionFromOpenToHalfOpenEnabled 顾名思义,就是是否允许熔断器自动从开启状态超时后转到半开状态

保护我们的方法

假设我们需要保护的方法是:

public class RemoteService{
  public CompletableFuture echoName(String name){
    System.out.println(name);
    return CompletableFuture.completedFuture(name);
  }
}

用circuit breaker 包裹一下:

String name = "rachel";
RemoteService remoteService = new RemoteService();
CheckedFunction0> checkedSupplier = circuitBreaker.decorateCheckedSupplier(()->remoteService.echoName(name));
Try> result = Try.of(checkedSupplier)
        .recover(CallNotPermittedException.class, throwable ->{
            System.out.println("熔断器已经打开,拒绝访问被保护方法~");
            return CompletableFuture.completedFuture("not permit");
        });

recover

javadoc文档上recover的介绍是: 此处提供了让我们可以从错误发生后进行降级处理等的地方~

Resilience4j 实践

一个小验证

package com.example.http.resilience4j;

import io.github.resilience4j.circuitbreaker.CallNotPermittedException;
import io.github.resilience4j.circuitbreaker.CircuitBreaker;
import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig;
import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry;
import io.vavr.CheckedFunction0;
import io.vavr.control.Try;
import org.junit.jupiter.api.Test;

import java.time.Duration;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Predicate;

public class CircuitBreakerTest {
    private EchoService echoService;
    private CircuitBreakerRegistry circuitBreakerRegistry;
    public class EchoService {
        private AtomicInteger count = new AtomicInteger(0);
        public CompletableFuture echoName(String name) {
            int num = count.getAndIncrement();
            if (num % 3 == 1) {
                throw new RuntimeException(name);
            }
            System.out.println("hello " + name + " ~ " + num);
            return CompletableFuture.completedFuture(name);
        }
    }
    @Test
    public void testCircuitBreaker() throws Throwable {
        //配置
        CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom()
                .failureRateThreshold(20)
                .waitDurationInOpenState(Duration.ofMillis(500))
                .slidingWindowSize(5)
                .permittedNumberOfCallsInHalfOpenState(3)
                .recordException(new Predicate() {
                    @Override
                    public boolean test(Throwable throwable) {
                        return true;
                    }
                })
                .automaticTransitionFromOpenToHalfOpenEnabled(true)
                .build();

        //注册
        circuitBreakerRegistry = CircuitBreakerRegistry.of(circuitBreakerConfig);
        echoService = new EchoService();

        for(int i= 0;i echoWrapper(String name) {
        CircuitBreaker circuitBreaker = circuitBreakerRegistry.circuitBreaker(name);
        CheckedFunction0> checkedSupplier = circuitBreaker.decorateCheckedSupplier(()->echoService.echoName(name));
        Try> result = Try.of(checkedSupplier)
                .recover(CallNotPermittedException.class, throwable ->{
                    System.out.println("熔断器已经打开,拒绝访问被保护方法~");
                    getCircuitBreakerStatus("熔断器打开中:", circuitBreaker);
                    return CompletableFuture.completedFuture("not permit");
                }).recover(throwable -> {
                    System.out.println(throwable.getLocalizedMessage() + ",方法被降级了~~");
                    getCircuitBreakerStatus("降级方法中:",circuitBreaker);
                    if("jojo".equals(name)){
                        return CompletableFuture.completedFuture("no available students");
                    }
                    return echoWrapper("jojo");
                });
        getCircuitBreakerStatus(circuitBreaker.getName() + "执行结束后:",circuitBreaker);
        return result.get();
    }

    public static void getCircuitBreakerStatus(String msg, CircuitBreaker circuitBreaker){
        CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics();
        // Returns the failure rate in percentage.

        float failureRate = metrics.getFailureRate();
        // Returns the current number of buffered calls.

        int bufferedCalls = metrics.getNumberOfBufferedCalls();
        // Returns the current number of failed calls.

        int failedCalls = metrics.getNumberOfFailedCalls();
        // Returns the current number of successed calls.

        int successCalls = metrics.getNumberOfSuccessfulCalls();
        // Returns the current number of not permitted calls.

        long notPermittedCalls = metrics.getNumberOfNotPermittedCalls();

        System.out.println(msg + "state=" +circuitBreaker.getState() + " , metrics[ failureRate=" + failureRate +
                ", bufferedCalls=" + bufferedCalls +
                ", failedCalls=" + failedCalls +
                ", successCalls=" + successCalls +
                ", notPermittedCalls=" + notPermittedCalls +
                " ]"
        );
    }
}

结果:


Process finished with exit code 0

Original: https://www.cnblogs.com/rachel-aoao/p/resilience4j_circuitbreaker.html
Author: rachel_aoao
Title: Resilience4j 实践

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/601482/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 5个必知的高级SQL函数

    5个必知的高级SQL函数 SQL是关系数据库管理的标准语言,用于与数据库通信。它广泛用于存储、检索和操作数据库中存储的数据。SQL不区分大小写。用户可以访问存储在关系数据库管理系统…

    数据库 2023年6月11日
    0107
  • Facade 外观(结构型)

    Facade 外观 (结构型) 一:描述: Facade 外观模式是为子系统至客户端之间提供简单的一致的接口,来降低耦合度。 二:模式图 三:实现代码简单例子: 1 、业务模块; …

    数据库 2023年6月11日
    0106
  • Maven常用命令

    一、Maven常用命令及其介绍 mvn clean 对项目进行清理,删除target目录下编译的内容 mvn compile 编译项目源代码 mvn test 对项目进行运行测试 …

    数据库 2023年6月16日
    094
  • MySQL45讲之更新缓存

    本文介绍 MySQL的更新缓存 Change Buffer,以及唯一索引和普通索引如何选择。 唯一索引和普通索引的选择 查询过程 在唯一索引下,查询索引树,找到第一个匹配的行并返回…

    数据库 2023年5月24日
    095
  • Redis进阶(一)

    通过简单的KV数据库理解Redis 分为访问模块,操作模块,索引模块,存储模块 底层数据结构 除了String类型,其他类型都是一个键对应一个集合,键值对的存储结构采用哈希表 哈希…

    数据库 2023年6月16日
    091
  • Python学习笔记2(未完待续)

    Python学习笔记2(未完待续) 解决plt中文乱码问题-方法1 plt.rcParams[“font.sans-serif”]=[“SimH…

    数据库 2023年6月14日
    076
  • java死锁(Java-level deadlock)

    java-level deadlock 如下代码可以模拟java死锁。注意:当出现死锁时,应用程序是无响应的。错误信息: Found one Java-level deadlock…

    数据库 2023年6月9日
    096
  • Docker安装配置Oracle详细教程(以作持久化处理)

    Docker安装Oracle 1,拉取Oracle镜像,拉取成功后查看  docker pull registry.cn-ha…

    数据库 2023年6月11日
    0176
  • MySQL45讲之查询慢或者阻塞

    前言 本文介绍了表锁定和执行速度慢的实例,以及表锁定时的故障排除方法。 [En] This paper introduces examples of table locking a…

    数据库 2023年5月24日
    0117
  • 异常详解

    🦔异常 发现错误的理想时机是在编译阶段,也就是在运行程序之前。然而编译期间并不能找到所有的错误,余下的问题必须在运行期间解决。这就需要错误源能够通过某种方式,把适当的信息传递给某个…

    数据库 2023年6月14日
    096
  • 三种移除list中的元素(可靠)

    /** * 直接使用foreach方法移除list中的元素会抛异常 * Exception in thread "main" java.util.Concurr…

    数据库 2023年6月14日
    0136
  • Consul 入门-初识

    背景 现状:单体架构逐渐被微服务架构所替代,原本两个功能模被拆分成了两个服务。原本两个模块块间的通信只需要 函数调…

    数据库 2023年6月6日
    098
  • SQL学习日记(一) 语法篇

    对象名 关键字 描述 表 table 存储数据的逻辑单元,以行和列存在,行是数据记录,列是(属性)字段 系统表(数据字典) 存放数据库相关信息的表 程序员只可查看,不可修改 约束 …

    数据库 2023年5月24日
    079
  • 测试计划

    ​ 1.测试计划的定义:描述需要完成的所有工作,包括被测项目的目的、背景、范围、资源、进度、环境、任务、策略,以及相应的风险和措施。 ​ 2.测试计划的作用: 对后面的测试过程起到…

    数据库 2023年6月16日
    083
  • nexus,docker服务器限制外网时,需要添加的例外

    Mavenhttps://maven.aliyun.com/https://repo.maven.apache.org Dockerhttps://hub.docker.com/h…

    数据库 2023年6月9日
    0111
  • 奶奶常说,黑白照片看的不清晰,还好我会Python,分分钟给她变成彩色的~

    咳咳~ 其实是奶奶常说,艾欧尼亚昂扬不灭,正义将指引着我们! 好吧,并不是奶奶说,只是最近回家发现一些黑白老照片,看着不够清晰,然后实验了一波用Python把老照片变成彩色的。 代…

    数据库 2023年6月14日
    098
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球