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)

大家都在看

  • 2022-8-26 jq简单了解

    Query 是一个 JavaScript 函数库。 jQuery 是一个轻量级的”写的少,做的多”的 JavaScript 库。jQuery 库包含以下功能…

    数据库 2023年6月14日
    0126
  • 第十五章 Spring动态代理开发

    创建原始对象 public class UserServiceImpl implements UserService{ @Override public void register…

    数据库 2023年6月14日
    0114
  • openstack 安装neutron网络服务安装 报错:Unknown operation ‘enabled’

    注:这个脚本文件有一个地方是错误的,最后一行需要修改一下 vim /usr/local/bin/iass-install-neutron-controller-gre.sh 改sy…

    数据库 2023年6月14日
    094
  • django中的auth模块与admin后台管理

    1. auth模块 在创建完django项目之后,执行数据库迁移之后,数据库里会增加很多新表,其中有一张名为auth_user的表,当访问django自带的路由admin的时候,需…

    数据库 2023年6月14日
    094
  • Python–反射

    反射是一个很重要的概念,它可以把字符串映射到实例的变量或者实例的方法然后可以去执行调用、修改等操作。它有四个重要的方法: 1、getattr 获取指定字符串名称的对象属性 2、se…

    数据库 2023年6月9日
    083
  • proxySQL with SemiSync

    配置读写分离 先查看一下规则表的表结构 https://github.com/sysown/proxysql/wiki/Main-(runtime)#mysql_query_rul…

    数据库 2023年6月16日
    086
  • 容器化 | 构建 RadonDB MySQL 集群监控平台

    上一篇文章我们演示了如何《在 S3 备份恢复 RadonDB MySQL 集群数据》,本文将演示在 KubeSphere[1] 中使用 Prometheus[2] + Grafan…

    数据库 2023年5月24日
    090
  • 博客怎么写才能更安全和简洁

    前言 博客实现本地存储 Markdown语法的介绍 博客对于我们普通人来说就是为了更好的去实现个人知识的一个整理融合然后把知识共享可以帮助其他去实现自己的一些工作或者学习中的一些疑…

    数据库 2023年6月6日
    088
  • 最全面的MySQL数据库讲解,老杜带你从基础入门mysql

    Original: https://www.cnblogs.com/chaichaichai/p/15624323.htmlAuthor: 牛仔码农Title: 最全面的MySQL…

    数据库 2023年5月24日
    091
  • Atlas快速入门

    之前的公司在数据中台的项目上调研决定启用了Atlas作为我们数据血缘管理的工具,让我给大家写了一份Atlas快速入门的文档,所以在这里我将这篇文档以一个纯新手视角的方式再一次优化,…

    数据库 2023年6月11日
    0101
  • 0811JDBC随笔

    1.JDBC体系系统 一组规范:接口 JDBC接口(API)包括两个层次: 面向应用的API:Java API,抽象接口,供应用开发人员使用(连接数据库,执行SQL语句,获得结果)…

    数据库 2023年6月14日
    093
  • super 和 this 的区别

    一、二者的区别 1.属性的区别:this访问本类中的属性,如果本类没有此属性则从父类中继续查找。super访问父类中的属性。2.方法的区别:this访问本类中的方法,如果本类没有此…

    数据库 2023年6月11日
    091
  • JavaWeb核心篇(3)——JSP,MVC,三层架构

    JavaWeb核心篇(3)——JSP,MVC,三层架构 在本篇文章中我们会学习到JSP,MVC,三层架构 虽然JSP已经快被时代所淘汰,但是在一些老旧的工作场所还是有在使用,所以了…

    数据库 2023年6月14日
    073
  • LeetCode 28. 实现strStr()

    实现strStr()函数。 给你两个字符串haystack和needle,请你在haystack字符串中找出needle字符串出现的第一个位置(下标从0开始)。如果不存在,则返回-…

    数据库 2023年6月11日
    080
  • Ajax

    AJAX(Asynchronous Javascript And Xml) 传统请求及缺点 传统的请求都有哪些? 直接在浏览器地址栏上输入URL。 点击超链接 提交form表单 使…

    数据库 2023年6月14日
    0103
  • zabbix 报表动作日志 报错”503“

    本文来自博客园,作者:xiao智,转载请注明原文链接:https://www.cnblogs.com/yuwen01/p/16216868.html Original: https…

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