【SpringCloud】CircuitBreaker断路器之Resilience4J快速入门

【SpringCloud】CircuitBreaker断路器之Resilience4J快速入门

文章目录

  • 【SpringCloud】CircuitBreaker断路器之Resilience4J快速入门
    • 1. 概述
    • 2. 服务熔断+服务降级(CircuitBreaker)
      • 2.1 案例说明
        • 2.1.1 基于计数的滑动窗口
        • 2.1.2 测试
        • 2.2.1 基于时间的滑动窗口
        • 2.2.2 测试
    • 3. 隔离(BulkHead)
      • 3.1 信号量舱壁
      • 3.2 测试
      • 3.3 固定线程池舱壁
    • 4. 限流(RateLimiter)
      • 4.1 测试

1. 概述

官网地址:[点击跳转](CircuitBreaker (readme.io))

中文手册:点击跳转

Resilience4J主要模块如下所示:

image-20240425233038811


2. 服务熔断+服务降级(CircuitBreaker)

断路器有三种状态,状态之间的转换有两种策略(如下所示):

image-20240426003019671

断路器主要配置参数如下所示:

image-20240426004145012

其他参数可参考:官方文档


2.1 案例说明

2.1.1 基于计数的滑动窗口

使用基于计数的滑动窗口

1)编写服务提供方接口如下:

@RestController
public class PayCircuitController {//=========Resilience4j CircuitBreaker 的例子@GetMapping(value = "/pay/circuit/{id}")public String myCircuit(@PathVariable("id") Integer id) {if (id == -4) throw new RuntimeException("----circuit id 不能负数");if (id == 9999) {try {TimeUnit.SECONDS.sleep(5);} catch (InterruptedException e) {e.printStackTrace();}}return "Hello, circuit! inputId:  " + id + " \t " + IdUtil.simpleUUID();}
}

提示:id为-4时,模拟服务提供方接口报错情况;id为9999时,模拟接口阻塞情况;id为其他值时模拟正常情况。

2)在OpenFeign接口中编写接口方法:

@FeignClient(value = "cloud-payment-service")
public interface PayFeignApi {/*** Resilience4j CircuitBreaker 的例子* @param id* @return*/@GetMapping(value = "/pay/circuit/{id}")public String myCircuit(@PathVariable("id") Integer id);
}

3)在服务调用方引入依赖:

<!--resilience4j-circuitbreaker-->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
</dependency>
<!-- 由于断路保护等需要AOP实现,所以必须导入AOP包 -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency>

4)修改服务调用方yml配置(屏蔽了其他配置,只写了circuitBreaker相关配置):

spring:cloud:openfeign:circuitbreaker:enabled: truegroup:enabled: true #没开分组永远不用分组的配置。精确优先、分组次之(开了分组)、默认最后# Resilience4j CircuitBreaker 按照次数:COUNT_BASED 的例子
#  6次访问中当执行方法的失败率达到50%时CircuitBreaker将进入开启OPEN状态(保险丝跳闸断电)拒绝所有请求。
#  等待5秒后,CircuitBreaker 将自动从开启OPEN状态过渡到半开HALF_OPEN状态,允许一些请求通过以测试服务是否恢复正常。
#  如还是异常CircuitBreaker 将重新进入开启OPEN状态;如正常将进入关闭CLOSE闭合状态恢复正常处理请求。
resilience4j:circuitbreaker:configs:default:failureRateThreshold: 50 #设置50%的调用失败时打开断路器,超过失败请求百分⽐CircuitBreaker变为OPEN状态。slidingWindowType: COUNT_BASED # 滑动窗口的类型slidingWindowSize: 6 #滑动窗⼝的⼤⼩配置COUNT_BASED表示6个请求,配置TIME_BASED表示6秒minimumNumberOfCalls: 6 #断路器计算失败率或慢调用率之前所需的最小样本(每个滑动窗口周期)。如果minimumNumberOfCalls为10,则必须最少记录10个样本,然后才能计算失败率。如果只记录了9次调用,即使所有9次调用都失败,断路器也不会开启。automaticTransitionFromOpenToHalfOpenEnabled: true # 是否启用自动从开启状态过渡到半开状态,默认值为true。如果启用,CircuitBreaker将自动从开启状态过渡到半开状态,并允许一些请求通过以测试服务是否恢复正常waitDurationInOpenState: 5s #从OPEN到HALF_OPEN状态需要等待的时间permittedNumberOfCallsInHalfOpenState: 2 #半开状态允许的最大请求数,默认值为10。在半开状态下,CircuitBreaker将允许最多permittedNumberOfCallsInHalfOpenState个请求通过,如果其中有任何一个请求失败,CircuitBreaker将重新进入开启状态。recordExceptions:- java.lang.Exceptioninstances:cloud-payment-service:baseConfig: default

5)编写服务调用方接口:

@RestController
public class OrderCircuitController {@Resourceprivate PayFeignApi payFeignApi;@GetMapping(value = "/feign/pay/circuit/{id}")@CircuitBreaker(name = "cloud-payment-service", fallbackMethod = "myCircuitFallback")public String myCircuitBreaker(@PathVariable("id") Integer id) {return payFeignApi.myCircuit(id);}//myCircuitFallback就是服务降级后的兜底处理方法public String myCircuitFallback(Integer id, Throwable t) {// 这里是容错处理逻辑,返回备用结果return "myCircuitFallback,系统繁忙,请稍后再试-----/(ㄒoㄒ)/~~";}
}

到此为止,案例编写成功,接下来测试。


2.1.2 测试

http://localhost/feign/pay/circuit/1 接口和 http://localhost/feign/pay/circuit/-4 来回各调用三次,失败率就达到了50%,触发了断路器功能,状态由 CLOSED 转为 OPEN 。5s后状态由 OPEN 转为 HALF_OPEN

正常调用:

image-20240426012409517

失败调用,触发降级方法:

image-20240426012425712

触发断路器,状态为 OPEN 时调用:

image-20240426012615216


2.2.1 基于时间的滑动窗口
spring:cloud:openfeign:circuitbreaker:enabled: truegroup:enabled: true #没开分组永远不用分组的配置。精确优先、分组次之(开了分组)、默认最后# Resilience4j CircuitBreaker 按照时间:TIME_BASED 的例子
resilience4j:timelimiter:configs:default:timeout-duration: 10s #神坑的位置,timelimiter 默认限制远程1s,超于1s就超时异常,配置了降级,就走降级逻辑circuitbreaker:configs:default:failureRateThreshold: 50 #设置50%的调用失败时打开断路器,超过失败请求百分⽐CircuitBreaker变为OPEN状态。slowCallDurationThreshold: 2s #慢调用时间阈值,高于这个阈值的视为慢调用并增加慢调用比例。slowCallRateThreshold: 30 #慢调用百分比峰值,断路器把调用时间⼤于slowCallDurationThreshold,视为慢调用,当慢调用比例高于阈值,断路器打开,并开启服务降级slidingWindowType: TIME_BASED # 滑动窗口的类型slidingWindowSize: 2 #滑动窗口的大小配置,配置TIME_BASED表示2秒minimumNumberOfCalls: 2 #断路器计算失败率或慢调用率之前所需的最小样本(每个滑动窗口周期)。permittedNumberOfCallsInHalfOpenState: 2 #半开状态允许的最大请求数,默认值为10。waitDurationInOpenState: 5s #从OPEN到HALF_OPEN状态需要等待的时间recordExceptions:- java.lang.Exceptioninstances:cloud-payment-service:baseConfig: default 

2.2.2 测试

测试慢调用是否会触发断路器的断路,连续调用 http://localhost/feign/pay/circuit/9999 接口,再调用一次正常接口看是否触发降级服务。

正常调用接口:

image-20240426231859143

连续调用阻塞接口后:

image-20240426231935634

测试成功。


3. 隔离(BulkHead)

Resilience4j提供了两种隔离的实现方式,可以限制并发执行的数量,即限制对于下游服务的最大并发数量。

  • SemaphoreBulkhead使用了信号量
  • FixedThreadPoolBulkhead使用了有界队列和固定大小线程池

SemaphoreBulkhead可以在各种线程和I/O模型上正常工作。与Hystrix不同,它不提供基于shadow的thread选项。由客户端来确保正确的线程池大小与隔离配置一致。


3.1 信号量舱壁

1)在服务提供方编写测试接口:

//=========Resilience4j bulkhead 的例子
@GetMapping(value = "/pay/bulkhead/{id}")
public String myBulkhead(@PathVariable("id") Integer id) {if (id == -4) throw new RuntimeException("----bulkhead id 不能-4");if (id == 9999) {try {TimeUnit.SECONDS.sleep(5);} catch (InterruptedException e) {e.printStackTrace();}}return "Hello, bulkhead! inputId:  " + id + " \t " + IdUtil.simpleUUID();
}

2)在公用模块编写Feign接口:

/*** Resilience4j Bulkhead 的例子* @param id* @return*/
@GetMapping(value = "/pay/bulkhead/{id}")
public String myBulkhead(@PathVariable("id") Integer id);

3)在服务调用方添加依赖:

<!--resilience4j-bulkhead-->
<dependency><groupId>io.github.resilience4j</groupId><artifactId>resilience4j-bulkhead</artifactId>
</dependency>

4)配置yml:

信号量模式有如下两个核心参数:

配置属性默认值描述
maxConcurrentCalls25隔离允许线程并发执行的最大数量
maxWaitDuration0当达到并发调用数量时,新的线程执行时将被阻塞,这个属性表示最长的等待时间。

示例yml配置如下:

####resilience4j bulkhead 的例子
resilience4j:bulkhead:configs:default:maxConcurrentCalls: 2 # 隔离允许并发线程执行的最大数量maxWaitDuration: 1s # 当达到并发调用数量时,新的线程的阻塞时间,我只愿意等待1秒,过时不候进舱壁兜底fallbackinstances:cloud-payment-service:baseConfig: defaulttimelimiter:configs:default:timeout-duration: 20s

5)编写服务调用方测试接口:

/*** (船的)舱壁,隔离** @param id* @return*/
@GetMapping(value = "/feign/pay/bulkhead/{id}")
@Bulkhead(name = "cloud-payment-service", fallbackMethod = "myBulkheadFallback", type = Bulkhead.Type.SEMAPHORE)
public String myBulkhead(@PathVariable("id") Integer id) {return payFeignApi.myBulkhead(id);
}public String myBulkheadFallback(Throwable t) {return "myBulkheadFallback,隔板超出最大数量限制,系统繁忙,请稍后再试-----/(ㄒoㄒ)/~~";
}

3.2 测试

根据上面的配置,接口必定只能有两个线程同时调用,那么我们用浏览器两个窗口访问 http://localhost:80/feign/pay/bulkhead/9999 接口,那么就能够达到最大并发,然后再开一个窗口访问 http://localhost:80/feign/pay/bulkhead/1 ,这个接口本来是正常的,但是会因为触发了隔离机制调用降级方法。

测试结果如下:

image-20240426235627145


3.3 固定线程池舱壁

1)引依赖

<!--resilience4j-bulkhead-->
<dependency><groupId>io.github.resilience4j</groupId><artifactId>resilience4j-bulkhead</artifactId>
</dependency>

2)配置yml:

重要参数如下:

image-20240427003812006

####resilience4j bulkhead -THREADPOOL的例子
resilience4j:timelimiter:configs:default:timeout-duration: 10s #timelimiter默认限制远程1s,超过报错不好演示效果所以加上10秒thread-pool-bulkhead:configs:default:core-thread-pool-size: 1max-thread-pool-size: 1queue-capacity: 1instances:cloud-payment-service:baseConfig: default# spring.cloud.openfeign.circuitbreaker.group.enabled 请设置为false 新启线程和原来主线程脱离

3)编写服务调用方测试接口:

/*** (船的)舱壁,隔离,THREADPOOL** @param id* @return*/
@GetMapping(value = "/feign/pay/bulkhead/{id}")
@Bulkhead(name = "cloud-payment-service", fallbackMethod = "myBulkheadPoolFallback", type = Bulkhead.Type.THREADPOOL)
public CompletableFuture<String> myBulkheadTHREADPOOL(@PathVariable("id") Integer id) {System.out.println(Thread.currentThread().getName() + "\t" + "enter the method!!!");try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "\t" + "exist the method!!!");return CompletableFuture.supplyAsync(() -> payFeignApi.myBulkhead(id) + "\t" + " Bulkhead.Type.THREADPOOL");
}public CompletableFuture<String> myBulkheadPoolFallback(Integer id, Throwable t) {return CompletableFuture.supplyAsync(() -> "Bulkhead.Type.THREADPOOL,系统繁忙,请稍后再试-----/(ㄒoㄒ)/~~");
}

4. 限流(RateLimiter)

1)编写服务提供方测试接口:

//=========Resilience4j ratelimit 的例子
@GetMapping(value = "/pay/ratelimit/{id}")
public String myRatelimit(@PathVariable("id") Integer id) {return "Hello, myRatelimit欢迎到来 inputId:  " + id + " \t " + IdUtil.simpleUUID();
}

2)编写公用模块feign接口:

/*** Resilience4j Ratelimit 的例子** @param id* @return*/
@GetMapping(value = "/pay/ratelimit/{id}")
public String myRatelimit(@PathVariable("id") Integer id);

3)引入依赖:

<!--resilience4j-ratelimiter-->
<dependency><groupId>io.github.resilience4j</groupId><artifactId>resilience4j-ratelimiter</artifactId>
</dependency>

4)yml配置

限流重要参数如下:

属性默认值描述
timeoutDuration5秒线程等待权限的默认等待时间
limitRefreshPeriod500纳秒限流器每隔limitRefreshPeriod刷新一次,将允许处理的最大请求数量重置为limitForPeriod。
limitForPeriod50在一次刷新周期内,允许执行的最大请求数

yml配置示例如下:

####resilience4j ratelimiter 限流的例子
resilience4j:ratelimiter:configs:default:limitForPeriod: 2 #在一次刷新周期内,允许执行的最大请求数limitRefreshPeriod: 1s # 限流器每隔limitRefreshPeriod刷新一次,将允许处理的最大请求数量重置为limitForPeriodtimeout-duration: 1 # 线程等待权限的默认等待时间instances:cloud-payment-service:baseConfig: default

5)编写服务调用方接口:

@GetMapping(value = "/feign/pay/ratelimit/{id}")
@RateLimiter(name = "cloud-payment-service", fallbackMethod = "myRatelimitFallback")
public String myRateLimiter(@PathVariable("id") Integer id) {return payFeignApi.myRatelimit(id);
}public String myRatelimitFallback(Integer id, Throwable t) {return "你被限流了,禁止访问/(ㄒoㄒ)/~~";
}

4.1 测试

按照上面的配置,一秒能够处理2个请求,我们访问接口并疯狂刷新必能触发限流:

image-20240427010834642

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

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

相关文章

前端单元测试的艺术:专业化策略与Vue项目高效实践指南

单元测试是软件开发中的基石&#xff0c;尤其对于前端领域&#xff0c;它为保证代码质量、提升开发效率、强化项目稳定性提供了不可或缺的支持。本文将深入剖析单元测试的核心理念&#xff0c;揭示其在前端开发中的独特价值&#xff0c;并提炼出一套专业且高效的实践策略&#…

git 基础知识(全能版)

文章目录 一 、git 有三个分区二、git 基本操作1、克隆—git clone2、拉取—git fetch / git pull3、查看—git status / git diff3.1 多人开发代码暂存技巧 本地代码4、提交—git add / git commit / git push5、日志—git log / git reflog6、删除—git rm ‘name’7、撤销恢…

阿里云服务器(Ubuntu22)上的MySQL8更改为大小写不敏感

因为windows上默认的mysql8.0是大小写不敏感的&#xff0c;部署到服务器上之后发现ubuntu默认的是大小写敏感&#xff0c;所以为了不更改代码&#xff0c;需要将mysql数据库设置为大小写不敏感的。 &#xff01;&#xff01;&#xff01;重要一定要做好数据库的备份&#xff0…

使用JavaScript日历小部件和DHTMLX Gantt的应用场景(一)

DHTMLX Suite UI 组件库允许您更快地构建跨平台、跨浏览器 Web 和移动应用程序。它包括一组丰富的即用式 HTML5 组件&#xff0c;这些组件可以轻松组合到单个应用程序界面中。 DHTMLX Gantt是用于跨浏览器和跨平台应用程序的功能齐全的Gantt图表&#xff0c;可满足项目管理应用…

「51媒体」城市推介会,地方旅游推荐,怎么做好媒体宣传

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 城市推介会和地方旅游推荐是城市形象宣传的重要组成部分&#xff0c;通过有效的媒体宣传可以提升城市的知名度和吸引力。&#xff1a; 一&#xff0c;活动内容层面&#xff1a; 突出亮点…

pyqt设置标签显示图片并设置大小

pyqt设置标签显示图片并设置大小 标签显示图片效果代码 标签显示图片 使用 QPixmap 加载图片进行图片大小设置把图片对象设置到标签上 效果 代码 from PyQt5.QtWidgets import QApplication, QLabel, QVBoxLayout, QWidget from PyQt5.QtGui import QPixmap import sys from…

某赛通电子文档安全管理系统 多处 SQL注入漏洞复现

0x01 产品简介 某赛通电子文档安全管理系统(简称:CDG)是一款电子文档安全加密软件,该系统利用驱动层透明加密技术,通过对电子文档的加密保护,防止内部员工泄密和外部人员非法窃取企业核心重要数据资产,对电子文档进行全生命周期防护,系统具有透明加密、主动加密、智能…

采购数据分析驾驶舱分享,照着它抄作业

今天我们来看一张采购管理驾驶舱。这是一张充分运用了多种数据可视化图表、智能分析功能&#xff0c;从物料和供应商的角度全面分析采购情况的BI数据可视化报表&#xff0c;主要分为三个部分&#xff0c;接下来就分部分来了解一下。 第一部分&#xff1a;关键指标计算及颜色预…

从零入门区块链和比特币(第一期)

欢迎来到我的区块链与比特币入门指南&#xff01;如果你对区块链和比特币感兴趣&#xff0c;但不知道从何开始&#xff0c;那么你来对地方了。本博客将为你提供一个简明扼要的介绍&#xff0c;帮助你了解这个领域的基础知识&#xff0c;并引导你进一步探索这个激动人心的领域。…

rabbitmq集群配置

1&#xff0c;配置环境变量 MY_POD_NAME&#xff1a;当前Pod的名称 RABBITMQ_ERLANG_COOKIE&#xff1a;设置Erlang Cookie用于节点间通信安全验证&#xff0c;值来自/nfs/rabbitmq/lib/.erlang.cookie文件内容 RABBITMQ_NODENAME&#xff1a;根据Pod名称动态生成了RabbitMQ…

【GO】命令行解析 os 与 flag

目录 OS解析命令 简单用法 进阶用法 flag命令解析 基础实例 1. 自定义数据类型 2. 创建多个 FlagSet 3. 整合环境变量和配置文件 os与flag 关键点解析 程序的作用 示例命令行调用 在 Go 语言中&#xff0c;命令行解析是一项基本且常用的功能&#xff0c;它允许开发者…

微信小程序简单实现购物车功能

微信小程序简单实现购物车结算和购物车列表展示功能 实现在微信小程序中对每一个购物车界面的商品订单&#xff0c;进行勾选结算和取消结算的功能&#xff0c;相关界面截图如下&#xff1a; 具体实现示例代码为&#xff1a; 1、js代码&#xff1a; Page({/*** 页面的初始数…

K8s: 公有镜像中心和私有镜像中心的搭建

公有镜像中心的搭建和使用 1 &#xff09;在 官方docker镜像中心推送 在 hub.docker.com 上注册账号 (国内一般访问不了&#xff0c;原因不多说) 找到 Create Repository 按钮就行仓库的创建 这样就在官方创建了一个仓库&#xff0c;比如地址为: xx/y-y xx 是我的账户名y-y 是…

数之寻软件怎么样?

数之寻软件是一款功能强大的数据恢复和备份软件&#xff0c;以下是对其特点和功能的详细评价&#xff1a; 一、数据恢复方面&#xff1a; 高效的数据恢复能力&#xff1a;数之寻软件采用了先进的算法和数据恢复技术&#xff0c;能够快速有效地恢复丢失或损坏的数据。无论是文…

Python中matplotlib将多张遥感影像绘制为多个子图并分别设定子图标题的方法

本文介绍基于Python语言的matplotlib模块与gdal模块&#xff0c;读取大量长时间序列遥感影像&#xff0c;分别将其不同时相的图像作为子图&#xff0c;绘制在1个完整的大图中&#xff0c;并分别为每1个子图构建、显示标题的方法。 首先&#xff0c;我们明确一下本文的需求。现有…

Hadoop之路

hadoop更适合在liunx环境下运行&#xff0c;会节省后期很多麻烦&#xff0c;而用虚拟器就太占主机内存了&#xff0c;因此后面我们将把hadoop安装到wsl后进行学习,后续学习的环境是Ubuntu-16.04 &#xff08;windows上如何安装wsl&#xff09; 千万强调&#xff0c;有的命令一…

Web前端一套全部清晰 ② day2 HTML 标签之文字排版,图片、链接、音视频链接

虽然辛苦&#xff0c;我还是会选择那种滚烫的人生 —— 24.4.25 HTML初体验 1.HTML定义 HTML 超文本标记语言 超文本 —— 链接 标记 —— 标记也叫标签&#xff0c;带尖括号的文本 标签语法 开始标签 需要加粗的文字 结束标签 标签成对出现&#xff0c;中间包裹内容 <>里…

Rabbitmq消息应答,持久化,权重分配(7)

消息应答 概览 消息应答机制是 RabbitMQ 中确保消息处理的可靠性和一致性的重要机制之一。当消费者从队列中接收到消息并处理完成后&#xff0c;通常需要向 RabbitMQ 发送一个明确的消息应答&#xff0c;以告知 RabbitMQ 消息已经被处理&#xff0c;并可以安全地从队列中移除…

如何消除浏览器SmartScreen对网站“不安全”提示?

面对互联网时代用户对网站安全性和可信度的严苛要求&#xff0c;网站运营者时常遭遇Microsoft Defender SmartScreen&#xff08;SmartScreen&#xff09;提示网站不安全的困扰。本文将剖析SmartScreen判定网站不安全的原因&#xff0c;并为运营者提供应对策略&#xff0c;以恢…

[最新]CentOS7设置开机自启动Hadoop集群

安装好Hadoop后我们可以使用开机自启动的方式&#xff0c;节约敲命令的时间。注意是centOS7版本!!!和centOS6版本区别非常大!!! 1、切换到系统目录 [rootmaster ~]# cd /etc/systemd [rootmaster systemd]# ll total 32 -rw-r--r-- 1 root root 720 Jun 30 23:11 bootcha…