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)

大家都在看

  • mybatisplus不支持sum,但支持这个

    我们知道,要对数据求和,写sql很简单:select sum(exp) from table_name我们在用mybatisplus做求和计算的时候,mybatisplus的Wra…

    数据库 2023年6月9日
    0224
  • 正则表达式与SQL

    在我心中正则表达式和SQL就是一样的东西。 SQL是结构化查询语言,是根据某个查询、修改规则来查询修改数据,是描述一个规则给数据库,数据库来执行,数据库返回结果,过程不需要考虑,不…

    数据库 2023年6月9日
    066
  • [SQLServer]NetCore中将SQLServer数据库备份为Sql脚本

    描述: 最近写项目收到了一个需求, 就是将 SQL Server数据库备份为Sql脚本, 如果是My Sql之类的还好说, 但是在网上搜了一大堆, 全是教你怎么操作 SSMS的, …

    数据库 2023年6月9日
    087
  • 探究MySQL中SQL查询的成本

    成本 什么是成本,即SQL进行查询的花费的时间成本,包含IO成本和CPU成本。 IO成本:即将数据页从硬盘中读取到内存中的读取时间成本。通常1页就是1.0的成本。 CPU成本:即是…

    数据库 2023年5月24日
    0100
  • IPFS 集群部署

    IPFS 和 IPFS-Cluster 默认的端⼝:IPFS: 4001 – 与其他节点通信端⼝ 5001 – API server 8080 – Gateway server I…

    数据库 2023年6月9日
    076
  • mysql中group by,having,order by,limit,distinct的用法和简单的的多表查询

    group:组 by:通过 group by :通过….。分组group by列名:通过指定列来分组 一般情况下在题目中出现 “每个” &#82…

    数据库 2023年5月24日
    089
  • jmeter并发设置的原理

    简介 广义并发 绝对并发 简介 ​ 性能测试过程中是否需要进行同步定时器的设置,需要根据实际情况来考虑。 ​ 举个栗子来讲是我们的双十一秒杀活动,这时候就必须实现请求数量达到一定数…

    数据库 2023年6月6日
    071
  • 【转】 一条 SQL 的执行过程详解

    MySQL 体系架构 – 连接池组件 1、负责与客户端的通信,是半双工模式,这就意味着某一固定时刻只能由客户端向服务器请求或者服务器向客户端发送数据,而不能同时进行。 …

    数据库 2023年5月24日
    097
  • Stack

    供自己巩固集合知识时写的笔记,不会对所有的内容都介绍栈(Stack)是一种后进先出(LIFO:Last In First Out)的数据结构 Stack只有入栈和出栈的操作: 把元…

    数据库 2023年6月9日
    068
  • 深度干货!一篇Paper带您读懂HTAP | StoneDB学术分享会第①期

    在最新一届国际数据库顶级会议 ACM SIGMOD 2022 上,来自清华大学的李国良和张超两位老师发表了一篇论文:《HTAP Database: What is New and …

    数据库 2023年6月11日
    074
  • Mybatis第三方PageHelper分页插件原理

    欢迎关注公号:BiggerBoy,看更多文章 往期精品 此时commentAnalyses为Page对象(PageHelper插件包内定义的) 而Page对象继承自JDK中的Arr…

    数据库 2023年6月11日
    076
  • ASP.NET MVC通用权限管理系统源代码开源发布(AngelRM_MVC)v2.1

    一、Angel工作室简单通用权限系统简介 AngelRM(Asp.net MVC)是基于asp.net(C#)MVC打造后端原生态代码+前端bootstrap+ztree+loda…

    数据库 2023年6月14日
    073
  • 【黄啊码】MySQL复制以及调优

    一. 简介 MySQL自带复制方案,带来好处有: 数据备份。负载均衡。分布式数据。 概念介绍: 主机(master):被复制的数据库。从机(slave):复制主机数据的数据库。 复…

    数据库 2023年6月16日
    097
  • MySQLB+树

    书名《MySQL是怎样运行的:从根儿上理解MySQL》。 这本书真的很好。如果你想学习,我建议你去😊看看。 [En] This book is really good. I sug…

    数据库 2023年5月24日
    094
  • Java对象的序列化和反序列化小结

    在进入正文之前我们先明白两个概念,序列化和反序列化; 序列化:将对象的状态信息转换为可以存储或者传输格式的过程; 反序列化:从网络或者存储读取对象的状态信息,重新创建该对象的过程;…

    数据库 2023年6月14日
    076
  • MySQL8新增降序索引

    MySQL8新增降序索引 桃花坞里桃花庵,桃花庵里桃花仙。桃花仙人种桃树,又摘桃花卖酒钱。 一、MySQL5.7 降序索引 MySQL 在语法上很早就已经支持降序索引,但实际上创建…

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