Spring cloud - 断路器 Resilience4J

news2024/12/23 21:01:15

其实文章的标题应该叫 Resilience4J,而不是Spring Cloud Resilience4J,不过由于正在对Spring cloud的一系列组件进行学习,为了统一,就这样吧。

概念区分

首先区分几个概念

  1. Spring cloud 断路器:Spring Cloud的官网对Spring Cloud Circuit Breaker的描述:
    在这里插入图片描述
    Spring Cloud支持的几种断路器实现,其中就包含Resilience4J。

  2. Spring gateway 断路器过滤器:前面一篇文章学习过,Spring Cloud Gateway提供的过滤器中就包括断路器过滤器,断路器过滤器的默认实现也是Resilience4J。

  3. Resilience4J:轻量级的断路器实现,Spring Cloud抛弃Hystrix之后(不知道算不算是抛弃…)的替代品。

今天的文章以学习Resilience4J为主。

Resilience4j 介绍

Resilience4j is a lightweight fault tolerance library designed for functional programming. Resilience4j provides higher-order functions (decorators) to enhance any functional interface, lambda expression or method reference with a Circuit Breaker, Rate Limiter, Retry or Bulkhead. You can stack more than one decorator on any functional interface, lambda expression or method reference. The advantage is that you have the choice to select the decorators you need and nothing else.

Resilience4j 是一个轻量级的容错处理库,提供一些列高阶功能(装饰器)以增强任意的功能接口,以lambda 表达式或方法引用的方式提供Circuit Breaker, Rate Limiter, Retry或Bulkhead。你可以通过表达式或者方法引用启用一个或多个装饰器。

以上内容来自于Resilience4j 官网,内容很容易看得懂但是翻译起来感觉却有些费劲…

Resilience4j 运行在java17上,在spring项目上或springboot、springcloud项目上都可以用,所以他不是spring cloud专有的技术,只不过spring cloud对Resilience4j 做了很好的集成(比如通过spring cloud gateway)。

核心模块

Resilience4j 包含如下核心模块:

  1. resilience4j-circuitbreaker: Circuit breaking 断路器
  2. resilience4j-ratelimiter: Rate limiting 限流
  3. resilience4j-bulkhead: Bulkheading 舱壁,或者叫隔板
  4. resilience4j-retry: Automatic retrying (sync and async) 重试
  5. resilience4j-timelimiter: Timeout handling 超时控制器
  6. resilience4j-cache: Result caching缓存

There are also add-on modules for metrics, Feign, Kotlin, Spring, Ratpack, Vertx, RxJava2 and more.
除此之外还提供了一些其他的附加模块,主要是用来将Resilience4j 集成到其他知名框架中的。

Resilience patterns

官网提供了一张表格,说明Resilience 包含的各模块的工作方式及功能描述。
在这里插入图片描述
Retry: 被调用服务出现异常后的重试功能,Resilience 的设计思路是认为好多服务出现异常之后其实都有可能在短时间内自我修复,所以Retry模块在这种场景下就会发挥作用。
Circuit Breaker: 暂时阻止可能发生错误的调用,避免雪崩效应。
Rate Limiter: 限流,限制一定时间范围内对某一服务的调用次数。
Time Limiter: timeout超时控制功能。
Bulkhead: 舱壁/调用隔离,限制并发数。
Cache: 缓存功能。
Fallback: 也就是所谓的降级服务。

Resilience 的核心功能中,不太容易理解的可能是这个Bulkhead,字面意思是舱壁或隔板的意思,他要实现的功能其实就是限制并发,有两种方式:信号量或线程池。目的也是为了限流,防止某一时刻突然出现的请求井喷从而导致服务异常。

应用

本文采用Spring cloud项目案例来简单说明Resilience核心组件的用法。

在以往springcloud项目的基础上,新建一个circuitbreaker模块,项目结构如下:
在这里插入图片描述

pom引用

pom文件引入Resilience4j :

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>springCloud</artifactId>
        <groupId>com.example</groupId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>circuitbreaker</artifactId>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
        </dependency>
    </dependencies>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
    </properties>

</project>

创建BackendService接口及实现类

创建一个特别简单的BackendService接口及实现类,模仿被调用的后台服务,BackendService的目的就是为了测试服务出现问题的时候Resilience4j 各核心模块的具体表现,所以我们把BackendService的逻辑设计的非常的简单:
一个接口:

@Service
public interface BackendService {
    String doSomething();
    public void setC(int c);
}

一个实现类:

package com.example.service.impl;

import com.example.service.BackendService;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

@Service
public class BackendServiceImpl implements BackendService {
    private int c;
    @Override
    public String doSomething() {
        System.out.println("i m dosomething...c="+c);
        int i=10/c;
       return "do something...c="+c;
    }

    @Override
    public void setC(int c) {
        this.c=c;
    }
}

只有一个doSomething方法,方法打印一句话,用方法属性c做了一个除法运算,目的是为了一会儿在测试的时候可以灵活控制其抛出异常。

CircuitBreaker, Retry and Fallback应用

创建一个CircuitBreakerTest类,CircuitBreakerTest是启用CircuitBreaker的关键,我们设置一个test方法,该方法为将BackendService的测试方法doSomething进行包装,也就是增加CircuitBreaker的装饰器,使其具有CircuitBreaker的各种特性:

@Service
public class CircuitBreakerTest {

    @Autowired
    BackendService backendService;
    public String test(int c){
        backendService.setC(c);
        // Create a CircuitBreaker (use default configuration)
        CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("backendName");
        // Create a Retry with at most 3 retries and a fixed time interval between retries of 500ms
        Retry retry = Retry.ofDefaults("backendName");

        // Decorate your call to BackendService.doSomething() with a CircuitBreaker
        Supplier<String> decoratedSupplier = CircuitBreaker
                .decorateSupplier(circuitBreaker, backendService::doSomething);

        // Decorate your call with automatic retry
        decoratedSupplier = Retry
                .decorateSupplier(retry, decoratedSupplier);

        // Use of Vavr's Try to
        // execute the decorated supplier and recover from any exception
        String result = Try.ofSupplier(decoratedSupplier)
                .recover(throwable -> "Hello from Recovery").get();
        return result==null?"hello resi":result;
    }

}

test方法首先使用默认配置创建一个CircuitBreaker,之后再用默认配置增加Retry装饰器,Retry的默认配置会指定:如果目标方法调用失败,会以500ms的时间间隔、最多调用3次目标方法,每次调用都失败的话,才认为是最终的调用失败。

之后将装饰器绑定到目标方法BackendService的doSomething方法上。

最后,调用目标方法,并且绑定fallback:方法返回throwable异常的话,返回"Hello from Recovery"作为降级服务(而不是将异常直接返回给调用方)。

新增测试用Controller

新增一个controller:

@RestController
@RequestMapping("/hello")
public class CommonController {
    @Autowired
    CircuitBreakerTest circuitBreakerTest;
    @GetMapping("/resi/{c}")
    public String resi(@PathVariable(value="c",required = false) int c){
        return circuitBreakerTest.test(c);
    }
}

代码准备好了,可以测试验证了。

测试验证

启动circuitbreaker模块服务,因为我们是将该模块创建在我们之前的springcloud项目框架下了,所以最好也把eureka的配置加进来,否则会有报错:
application.yml:

spring:
  application:
    name: circuitbreaker
  cloud:
    loadbalancer:
      enabled: true
eureka:
  client:
    service-url: # eureka ?????
      defaultZone: http://127.0.0.1:10086/eureka/
server:
  port: 9098

之后,启动注册中心,启动circuitbreaker。
在这里插入图片描述

后台log:
在这里插入图片描述

异常测试

输入异常参数,让BackendService的test方法抛出异常,前台得到了降级的fallback返回:
在这里插入图片描述
后台:
在这里插入图片描述
参数为0的时候,前端提交1次,后台Retry工作、重试了3次之后,仍然得到异常返回,最后fallback生效。

RateLimiter应用

RateLimiter是Resilience4j用于限流的组件,为了验证RateLimiter应用,我们前面的案例代码不太使用,需要稍作调整。

CircuitBreakerTest类修改为:

public class CircuitBreakerTest {
    private Supplier<String> decoratedSupplier;

    public void init(){
        if(decoratedSupplier != null) return;
        log.info("start to create circuitbreaker...");
        // Create a CircuitBreaker (use default configuration)
        CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("backendName");
        // Create a Retry with at most 3 retries and a fixed time interval between retries of 500ms
        Retry retry = Retry.ofDefaults("backendName");

        // Decorate your call to BackendService.doSomething() with a CircuitBreaker
        decoratedSupplier = CircuitBreaker
                .decorateSupplier(circuitBreaker, backendService::doSomething);

        // Decorate your call with automatic retry
        decoratedSupplier = Retry
                .decorateSupplier(retry, decoratedSupplier);

        // Create a custom RateLimiter configuration
        RateLimiterConfig config = RateLimiterConfig.custom()
                .timeoutDuration(Duration.ofMillis(100))
                .limitRefreshPeriod(Duration.ofMinutes(1))
                .limitForPeriod(10)
                .build();
        // Create a RateLimiter
        RateLimiter rateLimiter = RateLimiter.of("backendName", config);

        decoratedSupplier = RateLimiter.decorateSupplier(rateLimiter,decoratedSupplier);
    }
    @Autowired
    BackendService backendService;
    private int counter=0;
    public String test(int c){
        init();
        backendService.setC(c);
        log.info("in test counter:"+ ++counter);
        // Use of Vavr's Try to
        // execute the decorated supplier and recover from any exception
        String result = Try.ofSupplier(decoratedSupplier)
                .recover(throwable -> "Hello from Recovery").get();
        return result==null?"hello resi":result;
    }
}

由于限流装饰器需要统计一定时间范围内的请求次数,所以初始化circuitbreaker的代码不能放在请求内,需要独立出去。

限流器配置为1分钟之内的请求不超过10次,超过10次则限流限制生效、触发降级服务fallback。加了一个调用计数器,调用的时候在log中打印。

启动服务,测试。前端不断刷新,可以发现前10次能够正常获得返回,第11次开始获取不到正常返回了,返回的是fallback:
在这里插入图片描述

查看后台log:
在这里插入图片描述
由于请求都发生在15:26这一分钟之内,所以,前10次请求都能正常调用到BackendService的doSomething方法中,第11次请求之后,就被RateLimiter限流了,请求不能到达调用服务中了,这种情况下前端的反馈是fallback降级服务。

Bulkhead应用

Resilience4j提供了两种Bulkhead隔离策略:SemaphoreBulkhead和ThreadPoolBulkhead。

SemaphoreBulkhead

在上面的CircuitBreakerTest类的init方法尾部加入:

        // Create a custom Bulkhead configuration
        BulkheadConfig bulkheadConfig = BulkheadConfig.custom()
                .maxConcurrentCalls(150)
                .maxWaitDuration(Duration.ofSeconds(1))
                .build();

        Bulkhead bulkhead = Bulkhead.of("backendName", bulkheadConfig);
        decoratedSupplier =Bulkhead.decorateSupplier(bulkhead,decoratedSupplier);

不过项目引入的:

		<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
        </dependency>

并不能包含Bulkhead 所在的包,暂时还没有搞清楚spring cloud集成的resilience4j,其Bulkhead 在哪个依赖中。所以临时加入了:

        <dependency>
            <groupId>io.github.resilience4j</groupId>
            <artifactId>resilience4j-spring-boot2</artifactId>
            <version>1.7.0</version>
        </dependency>

resilience4j针对springboot2的依赖包,引入依赖之后编译能通过了。

上面代码对Bulkhead 配置为:1秒钟时间范围内的并发线程数不能超过150个。

ThreadPoolBulkhead

ThreadPoolBulkhead配置:

        ThreadPoolBulkheadConfig tpconfig = ThreadPoolBulkheadConfig.custom()
                .maxThreadPoolSize(10)
                .coreThreadPoolSize(2)
                .queueCapacity(20)
                .build();

使用线程池进行限流,最大线程数10,核心线程数2,队列容量20。这种情况下,最大并发线程为10,超过并发线程数的请求将进入队列排队等待,队列满之后,请求将被拒绝。

多组件共同作用的顺序

多个组件共同作用的话,先后顺序如下:

Retry ( CircuitBreaker ( RateLimiter ( TimeLimiter ( Bulkhead ( Function ) ) ) ) )

第一个起作用的是Bulkhead ,最后一个起作用的是Retry。

事件机制

CircuitBreaker, RateLimiter, Cache, Bulkhead, TimeLimiter and Retry components emit a stream of events. It can be consumed for logging, assertions and any other purpose.

Resilience4J的各组件支持发布事件,时间可以被其他任何应用消费(比如日志应用、断言等)。

比如:

circuitBreaker.getEventPublisher()
    .onSuccess(event -> logger.info(...))
    .onError(event -> logger.info(...))
    .onIgnoredError(event -> logger.info(...))
    .onReset(event -> logger.info(...))
    .onStateTransition(event -> logger.info(...));
// Or if you want to register a consumer listening to all events, you can do:
circuitBreaker.getEventPublisher()
    .onEvent(event -> logger.info(...));

应用可捕获(消费)成功、失败、重置等事件,针对事件进行进一步处理。

OK,此致!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1315741.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python的数据类型及举例集合、元组、列表之间的转换规则

Python语言有八种数据类型&#xff0c;有数字&#xff08;整数、浮点数、复数&#xff09;、字符串、字典、集合、元组、列表、布尔值、空值&#xff0c;下面我演示八种数据类型及集合、元组、列表三种类型之间的转换规则。 一、数据类型示例 下面我演示了八种数据类型&#…

Git使用rebase和merge区别

Git使用rebase和merge区别 模拟环境使用merge合并使用rebase 模拟环境 本地dev分支中DevTest增加addRole() 远程dev被同事提交增加了createResource() 使用merge合并 使用idea中merge解决冲突后, 推送远程dev后,日志图显示 使用rebase idea中使用功能rebase 解决冲突…

PyQt6 安装Qt Designer

前言&#xff1a;在Python自带的环境下&#xff0c;安装Qt Designer&#xff0c;并在PyCharm中配置designer工具。 在项目开发中&#xff0c;使用Python虚拟环境安装PyQt6-tools时&#xff0c;designer.exe会安装在虚拟环境的目录中&#xff1a;.venv\Lib\site-packages\qt6_a…

模板方法模式(行为型)

目录 一、前言 二、模板模式 三、带钩子的模板模式 四、总结 一、前言 模板方法模式是一种行为型设计模式&#xff0c;它定义了一个操作中的算法框架&#xff0c;将一些步骤延迟到子类中实现。这种模式是基于“开闭原则”的设计思想&#xff0c;即对扩展开放&#xff0c;对…

Microsoft visual studio 2013卸载方法

1、问 题 Microsoft visual studio 2013 无法通过【程序与功能】卸载 2、解决方法 使用微软的Microsoft visual studio 2013 专用卸载工具 工具下载链接&#xff1a;https://github.com/Microsoft/VisualStudioUninstaller/releases 或 链接&#xff1a;https://pan.baidu.c…

分布式事务seata使用示例及注意事项

分布式事务seata使用示例及注意事项 示例说明代码调用方&#xff08;微服务A&#xff09;服务方&#xff08;微服务B&#xff09; 测试测试一 &#xff0c;seata发挥作用&#xff0c;成功回滚&#xff01;测试二&#xff1a;处理feignclient接口的返回类型从Integer变成String,…

数理统计基础:参数估计与假设检验

在学习机器学习的过程中&#xff0c;我充分感受到概率与统计知识的重要性&#xff0c;熟悉相关概念思想对理解各种人工智能算法非常有意义&#xff0c;从而做到知其所以然。因此打算写这篇笔记&#xff0c;先好好梳理一下参数估计与假设检验的相关内容。 1 总体梳理 先从整体结…

OceanBase数据库初识

文章目录 说明分布式数据库发展发展历史OceanBase和传统数据库的对比总结 OceanBase数据库产品简介应用案例 OceanBase数据库产品OceanBase数据库内核OceanBase开发者中心&#xff08;ODC&#xff09;产品架构OMS核心功能简介 说明 本文仅供学习和交流学习内容参考官方的培训资…

年底了,千万不要跳槽..

&#x1f4e2;专注于分享软件测试干货内容&#xff0c;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01;&#x1f4e2;交流讨论&#xff1a;欢迎加入我们一起学习&#xff01;&#x1f4e2;资源分享&#xff1a;耗时200小时精选的「软件测试」资…

关于Linux你必须知道的五件事

Linux是一种开源操作系统 (OS)。操作系统是直接管理系统硬件和资源&#xff08;如 CPU、内存和存储&#xff09;的软件。操作系统位于应用程序和硬件之间&#xff0c;并在所有软件和执行工作的物理资源之间建立连接。 俄罗斯军方计划用 Astra Linux 取代 Windows&#xff01;为…

【数据结构】双链表的定义和操作

目录 1.双链表的定义 2.双链表的创建和初始化 3.双链表的插入节点操作 4.双链表的删除节点操作 5.双链表的查找节点操作 6.双链表的更新节点操作 7.完整代码 &#x1f308;嗨&#xff01;我是Filotimo__&#x1f308;。很高兴与大家相识&#xff0c;希望我的博客能对你有所帮助…

RuoYi-Cloud诺依微服务项目

1、架构图 从图中解析出RuoYi-Cloud 使用微服务技术栈 网关&#xff1a;Gateway远程调用&#xff1a;Ribbon/Feign注册中心&#xff1a;Nacos Discovery熔断降级&#xff1a;Sentinel配置中心&#xff1a;Nacos Config链路追踪&#xff1a;Sleuth ZipKin/SkyWalking &#x…

leetcode(力扣) 89. 格雷编码 (规律题)

文章目录 题目描述思路分析完整代码 题目描述 n 位格雷码序列 是一个由 2n 个整数组成的序列&#xff0c;其中&#xff1a; 每个整数都在范围 [0, 2n - 1] 内&#xff08;含 0 和 2n - 1&#xff09; 第一个整数是 0 一个整数在序列中出现 不超过一次 每对 相邻 整数的二进制表…

vue3 使用antd 报错Uncaught TypeError--【已解决】

问题现象 使用最基本的 ant-design-vue 按钮demo 都报错 报错文字如下 Uncaught TypeError: Cannot read properties of undefined (reading value)at ReactiveEffect.fn (ant-design-vue.js?v597f5366:6693:87)at ReactiveEffect.run (chunk-K2VKR2AM.js?v25c381c3:461:…

用文本创建图表的工具PlantUML

什么是 PlantUML &#xff1f; PlantUML 是一种开源工具&#xff0c;允许用户从纯文本语言创建图表。除了各种 UML 图之外&#xff0c;PlantUML 还支持各种其他软件开发相关格式&#xff0c;以及 JSON 和 YAML 文件的可视化。PlantUML 语言是特定领域语言的一个示例。 什么是 P…

Shopee ERP:提升电商管理效率的终极解决方案

Shopee ERP&#xff08;Enterprise Resource Planning&#xff0c;企业资源规划&#xff09;是一款专为Shopee卖家设计的集成化电商管理软件。通过使用Shopee ERP系统&#xff0c;卖家可以更高效地管理他们的在线商店&#xff0c;实现库存管理、订单处理、物流跟踪、财务管理、…

【理论篇】SaTokenException: 非Web上下文无法获取Request问题解决 -理论篇

在我们使用sa-token安全框架的时候&#xff0c;有时候会提示&#xff1a;SaTokenException:非Web上下文无法获取Request 错误截图&#xff1a; 在官方网站中&#xff0c;查看常见问题排查&#xff1a; 错误追踪&#xff1a; 跟着源码可以看到如下代码&#xff1a; 从源码中&a…

【Spring教程30】Spring框架实战:从零开始学习SpringMVC 之 Rest风格简介与RESTful入门案例

目录 1 REST简介2 RESTful入门案例2.1 环境准备2.2 思路分析2.3 修改RESTful风格 3 知识点总结 欢迎大家回到《Java教程之Spring30天快速入门》&#xff0c;本教程所有示例均基于Maven实现&#xff0c;如果您对Maven还很陌生&#xff0c;请移步本人的博文《如何在windows11下安…

我的世界合成表大全(最新完整版)

我的世界合成表配方是什么? 我的世界是一款非常有趣的高自由度的沙盒游戏&#xff0c;游戏中玩家可以根据合成配方制作各种各样的物品。今天小编就为大家带来我的世界合成表大全(最新完整版)&#xff0c;希望可以帮到大家。 我的世界合成表大全(最新完整版) 基础物品合成表&a…

知识付费小程序开发:构建个性化学习平台的技术实践

随着在线学习和知识付费的兴起&#xff0c;开发一款知识付费小程序成为了创新的热点之一。本文将通过使用Node.js、Express和MongoDB为例&#xff0c;演示如何构建一个基础的知识付费小程序后端&#xff0c;并实现用户认证和知识内容管理。 1. 初始化项目 首先&#xff0c;确…