当前位置: 首页 > news >正文

Spring Cloud CircuitBreaker服务熔断+隔离+限流

目录

    • 介绍
    • 实现原理
    • 服务熔断 (CircuitBreaker)
      • 基于计数的滑动窗口 (COUNT_BASED) 案例
      • 断路器开启/关闭的条件
    • 服务隔离 (BulkHead)
      • 基于信号量 (SemaphoreBulkhead) 案例
    • 服务限流 (RateLimiter)
      • 令牌桶算法 (Token Bucket) 案例
    • 总结

介绍


Spring Cloud CircuitBreaker 是 Spring Cloud 提供的熔断器实现框架,用于增强微服务系统的稳定性和容错能力。它通过熔断机制,在服务调用失败或延迟过高时快速失败,防止故障扩散。

实现原理


CircuitBreaker 的目的是保护分布式系统免受故障和异常,提高系统的可用性和健壮性
断路器状态:

  • Closed: 服务调用正常,断路器处于关闭状态。
  • Open: 服务调用失败次数超过阈值,断路器打开,所有请求直接失败。
  • HAIF_OPEN: 经过一段时间后,断路器进入半开状态,允许部分请求通过以测试服务是否恢复。

原理: 当一个组件或服务出现故障时,CircuitBreaker 会迅速切换到开放 OPEN 状态(保险丝跳闸断电),避免更多的请求发送到该组件或服务。这可以减少对该组件或服务的负载,防止该组件或服务进一步崩溃,并使整个系统能够继续正常运行。同时 CircuitBreaker 还可以提高系统的可用性和健壮性,因为它可以在分布式系统的各个组件之间自动切换,从而避免单点故障的问题。

服务熔断 (CircuitBreaker)


滑动窗口类型有两种,推荐使用基于计数的滑动窗口 (COUNT_BASED)

  • 基于计数的滑动窗口 (COUNT_BASED)
  • 基于时间的滑动窗口 (TIME_BASED)

基于计数的滑动窗口 (COUNT_BASED) 案例

支付模块 cloud-payment8001:PayCircuitController

@RestController
public class PayCircuitController {// Resilience4j CircuitBreaker@GetMapping("/pay/circuit/{id}")public String myCircuitBreaker(@PathVariable("id") Integer id) {if (id < 0) {throw new RuntimeException("circuit id 不能为负数!");}if (id == 999) {try {TimeUnit.SECONDS.sleep(5);} catch (InterruptedException e) {e.printStackTrace();}}return "Hello circuit! inputId: " + id;}
}

通用模块 cloud-common-api:PayFeignApi

@FeignClient("cloud-payment-service")
public interface PayFeignApi {@GetMapping("/pay/circuit/{id}")public String myCircuitBreaker(@PathVariable("id") Integer id);
}

订单模块 cloud-feign-order9002 引入依赖

<!-- 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>

订单模块 cloud-feign-order9002 配置 yml

spring:cloud:openfeign:circuitbreaker:  # 开启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

订单模块 cloud-feign-order9002:OrderCircuitController

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

测试结果

A: 调用 http://localhost:9002/feign/pay/circuit/8,返回 Hello circuit! inputId: 8
B: 调用 http://localhost:9002/feign/pay/circuit/-9,返回 myCircuitFallback: 系统繁忙,请稍后再试!

操作步骤: 执行A => B => A => B => A => B,达到6次且50%调用失败,断路器开启,接着执行A,此时返回 myCircuitFallback: 系统繁忙,请稍后再试!等待5s后再次执行A,此时返回 Hello circuit! inputId: 8,服务恢复正常。

断路器开启/关闭的条件

1. 当满足一定的峰值和失败率达到一定条件后,断路器将会进入 OPEN 状态(保险丝跳闸),服务熔断。

2. 当进入 OPEN 状态时,所有请求都不会调用主业务逻辑方法,而是直接走fallbackmetnod 兜底方法,服务降级。

3.一段时间之后断路器会从 OPEN 状态进入 HALF_OPEN 半开状态,先放几个请求过去探探链路是否通,若成功,则断路器进入 CLOSE (类似保险丝闭合,恢复可用) 状态;若失败,则继续开启。

服务隔离 (BulkHead)


Resilience4j 提供了如下两种隔离的实现方式,可以限制并发执行的数量

  • 基于信号量 (SemaphoreBulkhead)
  • 基于固定线程池 (FixedThreadPoolBulkhead)

基于信号量 (SemaphoreBulkhead) 案例

支付模块 cloud-payment8001:PayCircuitController

@RestController
public class PayCircuitController {// Resilience4j BulkHead@GetMapping("/pay/bulkhead/{id}")public String myBulkhead(@PathVariable("id") Integer id) {if (id < 0) {throw new RuntimeException("bulkhead id 不能为负数!");}if (id == 999) {try {TimeUnit.SECONDS.sleep(5);} catch (InterruptedException e) {e.printStackTrace();}}return "Hello bulkhead! inputId: " + id;}

通用模块 cloud-common-api:PayFeignApi

@FeignClient("cloud-payment-service")
public interface PayFeignApi {@GetMapping("/pay/bulkhead/{id}")public String myBulkhead(@PathVariable("id") Integer id);
}

订单模块 cloud-feign-order9002 引入依赖

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

订单模块 cloud-feign-order9002 配置 yml

spring:cloud:openfeign:circuitbreaker:  # 开启circuitbreaker和分组激活enabled: truegroup:enabled: true  # 没开分组永远不用分组的配置。精确优先、分组次之(开了分组)、默认最后
# Resilience4j BulkHead
resilience4j:bulkhead:configs:default:maxConcurrentCalls: 2  # 隔离允许并发线程执行的最大数量maxWaitDuration: 1s    # 当达到并发调用数量时,新线程的阻塞时间,只等待1s,过时执行兜底fallback方法instances:cloud-payment-service:  # 指定服务baseConfig: default

订单模块 cloud-feign-order9002:OrderCircuitController

@RestController
public class OrderCircuitController {@Resourceprivate PayFeignApi payFeignApi;@GetMapping("/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: 系统繁忙,请稍后再试!";}
}

测试结果

A: 调用 http://localhost:9002/feign/pay/bulkhead/8,返回 Hello bulkhead! inputId: 8
B: 调用 http://localhost:9002/feign/pay/bulkhead/999,等待5s后返回 Hello bulkhead! inputId: 999

操作步骤: 连续执行B两次,达到允许并发线程数2,服务隔离,接着执行A (前两次B都处于执行中这段时间内),返回 myBulkheadFallback: 系统繁忙,请稍后再试!等待某一次B正常返回后,再次执行A,返回 Hello bulkhead! inputId: 8,服务恢复正常。

服务限流 (RateLimiter)


常见的限流算法:

  • 漏斗算法
  • 令牌桶算法
  • 滚动时间窗口
  • 滑动时间窗口

Resilience4j 的 RateLimiter 默认采用令牌桶算法。

令牌桶算法 (Token Bucket) 案例

支付模块 cloud-payment8001:PayCircuitController

@RestController
public class PayCircuitController {// Resilience4j RateLimiter@GetMapping("/pay/ratelimiter/{id}")public String myRateLimiter(@PathVariable("id") Integer id) {return "Hello, ratelimiter! inputId: " + id;}

通用模块 cloud-common-api:PayFeignApi

@FeignClient("cloud-payment-service")
public interface PayFeignApi {@GetMapping("/pay/ratelimiter/{id}")public String myRateLimiter(@PathVariable("id") Integer id);
}

订单模块 cloud-feign-order9002 引入依赖

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

订单模块 cloud-feign-order9002 配置 yml

spring:cloud:openfeign:circuitbreaker:  # 开启circuitbreaker和分组激活enabled: truegroup:enabled: true  # 没开分组永远不用分组的配置。精确优先、分组次之(开了分组)、默认最后
# Resilience4j RateLimiter
resilience4j:ratelimiter:configs:default:limitForPeriod: 2  # 在一次刷新周期内,允许执行的最大请求数limitRefreshPeriod: 1s  # 限流器每隔limitRefreshPeriod刷新一次,将允许处理的最大请求数量重置为limitForPeriodtimeout-duration: 1  # 线程等待权限的默认等待时间instances:cloud-payment-service:  # 指定服务baseConfig: default

订单模块 cloud-feign-order9002:OrderCircuitController

@RestController
public class OrderCircuitController {@Resourceprivate PayFeignApi payFeignApi;@GetMapping("/feign/pay/ratelimiter/{id}")@RateLimiter(name = "cloud-payment-service", fallbackMethod = "myRateLimiterFallback")public String myRateLimiter(@PathVariable("id") Integer id) {return payFeignApi.myRateLimiter(id);}public String myRateLimiterFallback(Integer id, Throwable t) {return "myRateLimiterFallback: 已被限流,请稍后再试!";}
}

测试结果

A: 调用 http://localhost:9002/feign/pay/ratelimiter/8,返回 Hello ratelimiter! inputId: 8

操作步骤: 连续多次快速执行A,1s内达到允许最大请求数2,服务限流,返回 myRateLimiterFallback: 已被限流,请稍后再试!降低频率执行A,返回 Hello ratelimiter! inputId: 8,服务恢复正常。

总结


以上主要介绍了 Spring Cloud CircuitBreaker 服务熔断、隔离、限流的相关知识,想了解更多 Spring Cloud CircuitBreaker 知识的小伙伴请参考 Spring Cloud CircuitBreaker 官网 进行学习,学习更多 Spring Cloud 实战实用技巧的小伙伴,请关注后期发布的文章,认真看完一定能让你有所收获。

http://www.xdnf.cn/news/37945.html

相关文章:

  • 【解决】torch引入过程中的ImportError: __nvJitLinkAddData_12_1, version libnvJitLink.so.12
  • 编程技能:调试04,逐语句命令
  • 08-DevOps-向Harbor上传自定义镜像
  • 【数字IC进阶】整数除3和模3的高效实现
  • 网络开发基础(游戏方向)之 概念名词
  • ESP32-S3上跑通红外重复码发送(7)
  • Linux cmp 命令使用详解
  • SQL注入绕过一些过滤的方式
  • 【数据结构】_栈和队列相关面试题
  • Photoshop安装与配置--简单攻略版
  • 数字化转型四步走:企业的进化密码
  • 新手记录--从零开始[labelme安装及使用]
  • springAi---智能客服
  • 微信、抖音、小红书emoji符号大全
  • Step文件无法编辑怎么办?
  • 案例驱动的 IT 团队管理:创新与突破之路:第六章 组织进化:从案例沉淀到管理体系-6.1 案例库建设方法论-6.1.1结构化案例采集模板
  • 220V转5V转12V电机驱动供电WT5105
  • Java Date 类深度解析
  • k8s教程4:Kubernetes中的服务发现与负载均衡
  • 【信息系统项目管理师】高分论文:论信息系统项目的采购管理(CRM实施)
  • 【Arduino项目】电机驱动模块介绍
  • 数据结构实验6.2:稀疏矩阵的基本运算
  • 【信息系统项目管理师】高分论文:论信息系统项目的采购管理(信息化办公系统)
  • 日常开发记录
  • Kubernetes控制平面组件:高可用 APIServer
  • 基于vue框架的点餐系统设计及实现w93q6(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
  • JDK8 HashMap的实现原理
  • 【教程】DVWA靶场渗透
  • 使用Service对外发布集群中的应用程序
  • 树莓派超全系列教程文档--(34)树莓派配置GPIO