Resilience4J服务熔断隔离与限流

news2024/11/15 8:18:34

为了保障文章的流畅性(文章穿插大量的环境搭建没意思,会干扰文章的主题,无聊的很),将环境的搭建与测试,工具的版本说明放了文末: 六、环境搭建。

一、Circuit Breaker是什么

1.1、官网

https://spring.io/projects/spring-cloud-circuitbreaker

image-20240824083558893

image-20240824083623090

1.2、实现原理

CircuitBreaker的目的是保护分布式系统免受故障和异常,提高系统的可用性和健壮性。

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

https://resilience4j.readme.io/docs/circuitbreaker#create-and-configure-a-circuitbreaker

image-20240824084633020

image-20240824084701502

1.3、总结

Circuit Breaker只是一套规范和接口,落地实现者是Resilience4J

image-20240824085248952

二、Resilience4J

2.1、概述

https://github.com/resilience4j/resilience4j#1-introduction

image-20240824085526113

image-20240824085550046

2.2、作用

https://github.com/resilience4j/resilience4j#3-overview

image-20240824172809360

image-20240824090115846

2.3、官网

https://github.com/resilience4j/resilience4j

image-20240824090357764

三、实战:服务熔断与降级

3.1、概念解释

**服务熔断:**是指当某个服务的调用失败次数或异常比例达到一定阈值时,自动切断对该服务的调用,让请求快速失败,避免影响其他服务而导致雪崩效应。熔断后,一段时间内不再调用该服务,直到服务恢复正常或者超过最大等待时间。

​ 当服务A调用的某个服务B不可用时,上游服务A为了保证自己不受影响,从而不再调用服务B,直接返回一个结果,减轻服务A和服务B的压力,直到服务B恢复。

​ 类比保险丝,保险丝闭合状态(CLOSE)可以正常使用,当达到最大服务访问后,直接拒绝访问跳闸限电(OPEN),此刻调用方会接受服务降级的处理并返回友好兜底提示就是家里保险丝,从闭合CLOSE供电状态→跳闸OPEN打开状态

**服务降级:**是指当某个服务不可用或响应缓慢时,提供一个备用的处理逻辑,例如返回默认值、缓存值、错误提示等,以保证服务的可用性和容错性。降级可以在熔断时触发,也可以在其他情况下触发,例如系统负载过高、资源紧张等。

​ 服务器忙,请稍后再试。不让客户端等待并立刻返回一个友好提示,fallback

相同点:

  1. 都是为了防止系统崩溃

  2. 都让用户体验到某些功能暂时不可用

不同点:熔断是下游服务故障触发的,降级是为了降低系统负载

3.2、断路器的三大状态

https://resilience4j.readme.io/docs/circuitbreaker#create-and-configure-a-circuitbreaker

image-20240824084633020

CircuitBreaker 通过有限状态机实现,具有三种正常状态:CLOSED、OPEN 和 HALF_OPEN,以及两种特殊状态 DISABLED 和 FORCED_OPEN。

3.3、断路器三大状态之间的转化

https://resilience4j.readme.io/docs/circuitbreaker#failure-rate-and-slow-call-rate-thresholds

image-20240824093506648

image-20240824093636065

原文:

The state of the CircuitBreaker changes from CLOSED to OPEN when the failure rate is equal or greater than a configurable threshold. For example when more than 50% of the recorded calls have failed.
By default all exceptions count as a failure. You can define a list of exceptions which should count as a failure. All other exceptions are then counted as a success, unless they are ignored. Exceptions can also be ignored so that they neither count as a failure nor success.

The CircuitBreaker also changes from CLOSED to OPEN when the percentage of slow calls is equal or greater than a configurable threshold. For example when more than 50% of the recorded calls took longer than 5 seconds. This helps to reduce the load on an external system before it is actually unresponsive.

The failure rate and slow call rate can only be calculated, if a minimum number of calls were recorded. For example, if the minimum number of required calls is 10, then at least 10 calls must be recorded, before the failure rate can be calculated. If only 9 calls have been evaluated the CircuitBreaker will not trip open even if all 9 calls have failed.

The CircuitBreaker rejects calls with a CallNotPermittedException when it is OPEN. After a wait time duration has elapsed, the CircuitBreaker state changes from OPEN to HALF_OPEN and permits a configurable number of calls to see if the backend is still unavailable or has become available again. Further calls are rejected with a CallNotPermittedException, until all permitted calls have completed.
If the failure rate or slow call rate is then equal or greater than the configured threshold, the state changes back to OPEN. If the failure rate and slow call rate is below the threshold, the state changes back to CLOSED.

The Circuit Breaker supports two more special states, DISABLED (always allow access) and FORCED_OPEN (always deny access). In these two states no Circuit Breaker events (apart from the state transition) are generated, and no metrics are recorded. The only way to exit from those states are to trigger a state transition or to reset the Circuit Breaker.

翻译:

当故障率等于或大于可配置阈值时,断路器的状态将从关闭变为打开。例如,当超过 50% 的记录呼叫失败时。
默认情况下,所有异常都算作失败。您可以定义应算作失败的异常列表。然后,除非忽略所有其他异常,否则它们将被视为成功。也可以忽略异常,这样它们既不算作失败也不算作成功。

当慢速呼叫的百分比等于或大于可配置阈值时,断路器也会从关闭变为打开。例如,当超过 50% 的记录呼叫花费的时间超过 5 秒时。这有助于在外部系统实际无响应之前减少其负载。

只有在记录了最少数量的呼叫时,才能计算故障率和慢速呼叫率。例如,如果所需的最少呼叫数为 10,则必须记录至少 10 个呼叫,然后才能计算故障率。如果仅评估了 9 个调用,即使所有 9 个调用都失败,CircuitBreaker 也不会跳闸。

CircuitBreaker 在 OPEN 时会拒绝调用并抛出 CallNotPermittedException。等待一段时间后,CircuitBreaker 状态将从 OPEN 更改为 HALF_OPEN,并允许可配置数量的调用,以查看后端是否仍然不可用或再次可用。其他调用将被拒绝并抛出 CallNotPermittedException,直到所有允许的调用都完成。
如果失败率或慢速调用率等于或大于配置的阈值,则状态将变回 OPEN。如果失败率和慢速调用率低于阈值,则状态将变回 CLOSED。

Circuit Breaker 支持另外两种特殊状态,DISABLED(始终允许访问)和 FORCED_OPEN(始终拒绝访问)。在这两种状态下,不会生成任何 Circuit Breaker 事件(状态转换除外),也不会记录任何指标。退出这些状态的唯一方法是触发状态转换或重置断路器。

简单概述:

  • 断路器有三个普通状态:关闭(CLOSED)、开启(OPEN)、半开(HALF_OPEN),还有两个特殊状态:禁用(DISABLED)、强制开启(FORCED_OPEN)。

  • 当熔断器关闭时,所有的请求都会通过熔断器。

    • 如果失败率超过设定的阈值,熔断器就会从关闭状态转换到打开状态,这时所有的请求都会被拒绝。
    • 当经过一段时间后,熔断器会从打开状态转换到半开状态,这时仅有一定数量的请求会被放入,并重新计算失败率
    • 如果失败率超过阈值,则变为打开状态,如果失败率低于阈值,则变为关闭状态。
  • 断路器使用滑动窗口来存储和统计调用的结果。你可以选择于调用数量的滑动窗口或者基于时间的滑动窗口。

    • 基于访问数量的滑动窗口统计了最近N次调用的返回结果。

    • 居于时间的滑动窗口统计了最近N秒的调用反回结果。

  • 除此以外,熔断器还会有两种特殊状态:DISABLED(始终允许访问)和FORCED_OPEN(始终拒绝访问)。

    • 这两个状态不会生成熔断器事件(除状态装换外),并且不会记录事件的成功或者失败。
    • 退出这两个状态的唯一方法是触发状态转换或者重置熔断器。

3.4、配置参数

https://resilience4j.readme.io/docs/circuitbreaker#create-and-configure-a-circuitbreaker

image-20240824100602663

非官方的中文文档翻译:

https://github.com/lmhmhl/Resilience4j-Guides-Chinese/blob/main/core-modules/CircuitBreaker.md

image-20240824101539145

创建和配置CircuitBreaker

你可以自定义CircuitBreakerConfig,为了创建自定义的CircuitBreakerConfig,你可以使用CircuitBreakerConfig建造器,你可以使用建造者模式来配置下面的属性。

配置属性默认值描述
failureRateThreshold50以百分比配置失败率阈值。当失败率等于或大于阈值时,断路器状态并关闭变为开启,并进行服务降级。
slowCallRateThreshold100以百分比的方式配置,断路器把调用时间大于slowCallDurationThreshold的调用视为满调用,当慢调用比例大于等于阈值时,断路器开启,并进行服务降级。
slowCallDurationThreshold60000 [ms]配置调用时间的阈值,高于该阈值的呼叫视为慢调用,并增加慢调用比例。
permittedNumberOfCallsInHalfOpenState10断路器在半开状态下允许通过的调用次数。
maxWaitDurationInHalfOpenState0断路器在半开状态下的最长等待时间,超过该配置值的话,断路器会从半开状态恢复为开启状态。配置是0时表示断路器会一直处于半开状态,直到所有允许通过的访问结束。
slidingWindowTypeCOUNT_BASED配置滑动窗口的类型,当断路器关闭时,将调用的结果记录在滑动窗口中。滑动窗口的类型可以是count-based或time-based。如果滑动窗口类型是COUNT_BASED,将会统计记录最近slidingWindowSize次调用的结果。如果是TIME_BASED,将会统计记录最近slidingWindowSize秒的调用结果。
slidingWindowSize100配置滑动窗口的大小。
minimumNumberOfCalls100断路器计算失败率或慢调用率之前所需的最小调用数(每个滑动窗口周期)。例如,如果minimumNumberOfCalls为10,则必须至少记录10个调用,然后才能计算失败率。如果只记录了9次调用,即使所有9次调用都失败,断路器也不会开启。
waitDurationInOpenState60000 [ms]断路器从开启过渡到半开应等待的时间。
automaticTransition FromOpenToHalfOpenEnabledfalse如果设置为true,则意味着断路器将自动从开启状态过渡到半开状态,并且不需要调用来触发转换。创建一个线程来监视断路器的所有实例,以便在WaitDurationInOpenstate之后将它们转换为半开状态。但是,如果设置为false,则只有在发出调用时才会转换到半开,即使在waitDurationInOpenState之后也是如此。这里的优点是没有线程监视所有断路器的状态。
recordExceptionsempty记录为失败并因此增加失败率的异常列表。 除非通过ignoreExceptions显式忽略,否则与列表中某个匹配或继承的异常都将被视为失败。 如果指定异常列表,则所有其他异常均视为成功,除非它们被ignoreExceptions显式忽略。
ignoreExceptionsempty被忽略且既不算失败也不算成功的异常列表。 任何与列表之一匹配或继承的异常都不会被视为失败或成功,即使异常是recordExceptions的一部分。
recordExceptionthrowable -> true· By default all exceptions are recored as failures.一个自定义断言,用于评估异常是否应记录为失败。 如果异常应计为失败,则断言必须返回true。如果出断言返回false,应算作成功,除非ignoreExceptions显式忽略异常。
ignoreExceptionthrowable -> false By default no exception is ignored.自定义断言来判断一个异常是否应该被忽略,如果应忽略异常,则谓词必须返回true。 如果异常应算作失败,则断言必须返回false。

默认CircuitBreaker.java配置类,官网源码:

https://github.com/resilience4j/resilience4j/blob/master/resilience4j-circuitbreaker/src/main/java/io/github/resilience4j/circuitbreaker/CircuitBreakerConfig.java

image-20240824094942015

常用展现总结:

failure-rate-threshold以百分比配置失败率峰值
sliding-window-type断路器的滑动窗口期类型 可以基于“次数”(COUNT_BASED)或者“时间”(TIME_BASED)进行熔断,默认是COUNT_BASED。
sliding-window-size若COUNT_BASED,则10次调用中有50%失败(即5次)打开熔断断路器;若为TIME_BASED则,此时还有额外的两个设置属性,含义为:在N秒内(sliding-window-size)100%(slow-call-rate-threshold)的请求超过N秒(slow-call-duration-threshold)打开断路器。
slowCallRateThreshold以百分比的方式配置,断路器把调用时间大于slowCallDurationThreshold的调用视为慢调用,当慢调用比例大于等于峰值时,断路器开启,并进入服务降级。
slowCallDurationThreshold配置调用时间的峰值,高于该峰值的视为慢调用。
permitted-number-of-calls-in-half-open-state运行断路器在HALF_OPEN状态下时进行N次调用,如果故障或慢速调用仍然高于阈值,断路器再次进入打开状态。
minimum-number-of-calls在每个滑动窗口期样本数,配置断路器计算错误率或者慢调用率的最小调用数。比如设置为5意味着,在计算故障率之前,必须至少调用5次。如果只记录了4次,即使4次都失败了,断路器也不会进入到打开状态。
wait-duration-in-open-state从OPEN到HALF_OPEN状态需要等待的时间

3.5、实战

3.5.1、需求

6次访问中当执行方法的失败率达到50%时CircuitBreaker将进入开启OPEN状态(保险丝跳闸断电)拒绝所有请求。
等待5秒后,CircuitBreaker 将自动从开启OPEN状态过渡到半开HALF_OPEN状态,允许一些请求通过以测试服务是否恢复正常。
如还是异常CircuitBreaker 将重新进入开启OPEN状态;如正常将进入关闭CLOSE闭合状态恢复正常处理请求

3.5.2、COUNT_BASED(计数的滑动窗口)

https://resilience4j.readme.io/docs/circuitbreaker#count-based-sliding-window

image-20240824135906157

image-20240824102438658

3.5.2.1、改POM
     <!--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>

image-20240824130644041

3.5.2.2、写YML
    # 开启circuitbreaker和分组激活 spring.cloud.openfeign.circuitbreaker.enabled
    circuitbreaker:
      enabled: true
      group:
        enabled: true #没开分组永远不用分组的配置。精确优先、分组次之(开了分组)、默认最后

image-20240824132021449

#===========计数滑动窗口=====================================
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.Exception
    instances:
      # 谁来用(服务名)
      resilience4j-provider:
        baseConfig: default
#=========================================================================================

image-20240824132946976

3.5.2.3、写代码
package com.resilience4j.consumer.web;

import com.resilience4j.feginapi.apis.FeignAPI;
import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Author: 史小创
 * @Time: 2024/8/24 下午12:48
 * @Description:
 */

@RestController
public class Resilience4JConsumerController {
    @Resource
    private FeignAPI feignAPI;

    @GetMapping(value = "/consumer/circuit/{id}")
    @CircuitBreaker(name = "resilience4j-provider", fallbackMethod = "myCircuitFallback")
    public String myCircuitBreaker(@PathVariable("id") Integer id) {
        return feignAPI.myCircuit(id);
    }

    /**
     * myCircuitFallback就是服务降级后的兜底处理方法
     * @param id
     * @param t
     * @return
     */
    public String myCircuitFallback(Integer id, Throwable t) {
        // 这里是容错处理逻辑,返回备用结果
        return "myCircuitFallback,系统繁忙,请稍后再试-----/(ㄒoㄒ)/~~";
    }

}

image-20240824134144318

3.5.2.4、测试
3.5.2.4.1、正确的与错误的
http://127.0.0.1:8766/consumer/circuit/88

image-20240824134309841

http://127.0.0.1:8766/consumer/circuit/-4

image-20240824134412956

image-20240824134442788

3.5.2.4.2、一次错误一次正确
http://127.0.0.1:8766/consumer/circuit/88
http://127.0.0.1:8766/consumer/circuit/-4

看动态图:

4f6c1dde-b9be-4738-b4ed-2dc05691bb3a

50%错误后触发熔断并给出服务降级,告知调用者服务不可用
此时就算是翰入正确的访问地址也无法调用服务我明明是正确的也不让用川(TOT)~),它还在断路中(PEN状态),一会儿过度到绊开并继续王确他扯访问,侵慢切换回C.OSE状态,可以正常访问了链酪回复

3.5.2.4.3、多次填写错误
http://127.0.0.1:8766/consumer/circuit/88
http://127.0.0.1:8766/consumer/circuit/-4

看动态图:

60e21cd0-c02e-44dd-bfb3-db7b12a123ef

多次故意填写错误值(负4),然后慢慢填写正确值(正整数88),发现刚开始不满足条件,就算是正确的访问地址也不能进行

3.5.3、TIME_BASED(时间的滑动窗口)

https://resilience4j.readme.io/docs/circuitbreaker#time-based-sliding-window

image-20240824140015401

image-20240824140103556

3.5.3.1、写yml
##===========按照时间: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.Exception
    instances:
      resilience4j-provider:
        baseConfig: default

image-20240824142725926

3.5.3.2、测试
3.5.3.2.1、一次超时,一次正常访问,同时进行

image-20240824144946550

http://127.0.0.1:8766/consumer/circuit/88
http://127.0.0.1:8766/consumer/circuit/9999

a4c025cf-1ee5-4a4c-9722-007cb68eba43

3.5.3.2.2、第1~4个超时,整多一点干4个,一次正常访问,同时进行
http://127.0.0.1:8766/consumer/circuit/88
http://127.0.0.1:8766/consumer/circuit/9999

8c39714e-d8eb-4941-820d-08855d6e8015

正常访问也受到了牵连,因为服务熔断不能访问了

image-20240824145013266

3.6、总结

断路器开启或者关闭的条件

image-20240824145150755

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

当OPEN的时候,所有请求都不会调用主业务逻辑方法,而是直接走fallbackmetnod兜底背锅方法,服务降级

一段时间之后,这个时候断路器会从OPEN进入到HALF_OPEN半开状态,会放几个请求过去探探链路是否通?

如成功,断路器会关闭CLOSE(类似保险丝闭合,恢复可用);如失败,继续开启。重复上述

建议不要混合用,推荐按照调用次数count_based,一家之言仅供参考

四、实战:隔离

4.1、官网

https://resilience4j.readme.io/docs/bulkhead#introduction

image-20240824145506228

image-20240824145702722

4.2、作用

依赖隔离&负载保护:用来限制对于下游服务的最大并发数量的限制

4.3、实战

4.3.1、信号量舱壁(SemaphoreBulkhead)

4.3.1.1、概述

基本上就是我们JUC信号灯内容的同样思想

image-20240824150236335

信号量舱壁(SemaphoreBulkhead)原理

当信号量有空闲时,进入系统的请求会直接获取信号量并开始业务处理。

当信号量全被占用时,接下来的请求将会进入阻塞状态,SemaphoreBulkhead提供了一个阻塞计时器,

如果阻塞状态的请求在阻塞计时内无法获取到信号量则系统会拒绝这些请求。

若请求在阻塞计时内获取到了信号量,那将直接获取信号量并执行相应的业务处理。

https://github.com/resilience4j/resilience4j/blob/master/resilience4j-bulkhead/src/main/java/io/github/resilience4j/bulkhead/internal/SemaphoreBulkhead.java

image-20240824151106268

image-20240824151203052

4.3.1.2、改POM
 <!--resilience4j-bulkhead-->
        <dependency>
            <groupId>io.github.resilience4j</groupId>
            <artifactId>resilience4j-bulkhead</artifactId>
        </dependency>

image-20240824152714126

4.3.1.3、写YML
https://resilience4j.readme.io/docs/bulkhead#create-and-configure-a-bulkhead

image-20240824152835642

你可以提供一个自定义的全局BulkheadConfig,你可以使用BulkheadConfig建造者模式来创建自定义的全局BulkheadConfig,可以使用builder来配置下面的属性。

配置属性默认值描述
maxConcurrentCalls25隔离允许线程并发执行的最大数量
maxWaitDuration0当达到并发调用数量时,新的线程执行时将被阻塞,这个属性表示最长的等待时间。
##resilience4j bulkhead 的例子
resilience4j:
  bulkhead:
    configs:
      default:
        maxConcurrentCalls: 2 # 隔离允许并发线程执行的最大数量
        maxWaitDuration: 1s # 当达到并发调用数量时,新的线程的阻塞时间,我只愿意等待1秒,过时不候进舱壁兜底fallback
    instances:
      resilience4j-provider:
        baseConfig: default
  timelimiter:
    configs:
      default:
        timeout-duration: 20s

image-20240824154526010

4.3.1.4、代码
 /**
     * (船的)舱壁,隔离
     *
     * @param id
     * @return
     */
    @GetMapping(value = "/consumer/bulkhead/{id}")
    @Bulkhead(name = "resilience4j-provider", fallbackMethod = "myBulkheadFallback", type = Bulkhead.Type.SEMAPHORE)
    public String myBulkhead(@PathVariable("id") Integer id) {
        return feignAPI.myBulkhead(id);
    }

    public String myBulkheadFallback(Throwable t) {
        return "myBulkheadFallback,隔板超出最大数量限制,系统繁忙,请稍后再试-----/(ㄒoㄒ)/~~";
    }

image-20240824154545084

4.3.1.5、测试
http://127.0.0.1:8766/consumer/bulkhead/88
http://127.0.0.1:8766/consumer/bulkhead/9999

6e599ba3-3222-49ce-b201-0fccc76881f6

4.3.2、固定线程池舱壁(FixedThreadPoolBulkhead)

4.3.2.1、概述

image-20240824162522200

固定线程池舱壁(FixedThreadPoolBulkhead)

FixedThreadPoolBulkhead的功能与SemaphoreBulkhead一样也是用于限制并发执行的次数的,但是二者的实现原理存在差别而且表现效果也存在细微的差别。FixedThreadPoolBulkhead使用一个固定线程池和一个等待队列来实现舱壁。

当线程池中存在空闲时,则此时进入系统的请求将直接进入线程池开启新线程或使用空闲线程来处理请求。

当线程池中无空闲时时,接下来的请求将进入等待队列,

若等待队列仍然无剩余空间时接下来的请求将直接被拒绝,

在队列中的请求等待线程池出现空闲时,将进入线程池进行业务处理。

另外:ThreadPoolBulkhead只对CompletableFuture方法有效,所以我们必创建返回CompletableFuture类型的方法

https://github.com/resilience4j/resilience4j/blob/master/resilience4j-bulkhead/src/main/java/io/github/resilience4j/bulkhead/internal/FixedThreadPoolBulkhead.java

image-20240824162747208

image-20240824162921471

4.3.2.2、写YML
https://resilience4j.readme.io/docs/bulkhead#create-and-configure-a-threadpoolbulkhead

image-20240824164857329

###resilience4j bulkhead -THREADPOOL的例子
resilience4j:
  timelimiter:
    configs:
      default:
        timeout-duration: 10s #timelimiter默认限制远程1s,超过报错不好演示效果所以加上10秒
  thread-pool-bulkhead:
    configs:
      default:
        core-thread-pool-size: 1
        max-thread-pool-size: 1
        queue-capacity: 1
    instances:
      resilience4j-provider:
        baseConfig: default
#  spring.cloud.openfeign.circuitbreaker.group.enabled 请设置为false 新启线程和原来主线程脱离
#操作如下:
#circuitbreaker:
#  enabled: true
#  group:
#    #        enabled: true #没开分组永远不用分组的配置。精确优先、分组次之(开了分组)、默认最后
#    enabled: false #没开分组永远不用分组的配置。精确优先、分组次之(开了分组)、默认最后
4.3.2.3、代码
/**
     * (船的)舱壁,隔离  threadPool
     *
     * @param id
     * @return
     */
    @GetMapping(value = "/consumer/pool/bulkhead/{id}")
    @Bulkhead(name = "resilience4j-provider", fallbackMethod = "myBulkheadPoolFallback", type = Bulkhead.Type.THREADPOOL)
    public CompletableFuture<String> myBulkheadTHREADPOOL(@PathVariable("id") Integer id) {
        System.out.println(Thread.currentThread().getName() + "\t" + "---开始进入");
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "\t" + "---准备离开");

        return CompletableFuture.supplyAsync(() -> feignAPI.myBulkhead(id) + "\t" + "Bulkhead.Type.THREADPOOL");
    }

    public CompletableFuture<String> myBulkheadPoolFallback(Integer id, Throwable t) {
        return CompletableFuture.supplyAsync(() -> "Bulkhead.Type.THREADPOOL,系统繁忙,请稍后再试-----/(ㄒoㄒ)/~~");
    }

image-20240824165223464

4.2.3.4、测试
http://127.0.0.1:8766/consumer/pool/bulkhead/1
http://127.0.0.1:8766/consumer/pool/bulkhead/2
http://127.0.0.1:8766/consumer/pool/bulkhead/3
http://127.0.0.1:8766/consumer/pool/bulkhead/4

看动态图演示

3c4fda09-76fd-4767-9b91-c0944a9c593a

五、实战:限流

5.1、官网

https://resilience4j.readme.io/docs/ratelimiter

image-20240824165527156

5.2、作用

限流 就是限制最大访问流量。系统能提供的最大并发是有限的,同时来的请求又太多,就需要限流。

比如商城秒杀业务,瞬时大量请求涌入,服务器忙不过就只好排队限流了,和去景点排队买票和去医院办理业务排队等号道理相同。

所谓限流,就是通过对并发访问/请求进行限速,或者对一个时间窗口内的请求进行限速,以保护应用系统,一旦达到限制速率则可以拒绝服务、排队或等待、降级等处理。

排队示意图

5.3、常见的限流算法

限流算法是指用于限制单位时间内服务的请求数量的算法,目的是防止服务被过高的请求压力所击垮。常见的限流算法包括计数器算法、滑动窗口算法、漏桶算法、令牌桶算法。

5.4、测试

5.4.1、写YML

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

image-20240824170514988

5.4.2、写代码

 /**
     * 限流
     *
     * @param id
     * @return
     */
    @GetMapping(value = "/consumer/ratelimit/{id}")
    @RateLimiter(name = "resilience4j-provider", fallbackMethod = "myRatelimitFallback")
    public String myRatelimit(@PathVariable("id") Integer id) {
        return feignAPI.myRatelimit(id);
    }

    public String myRatelimitFallback(Integer id, Throwable t) {
        return "你被限流了,禁止访问/(ㄒoㄒ)/~~";
    }

image-20240824170902732

5.4.3、测试

http://127.0.0.1:8766/consumer/ratelimit/88

183c28f9-4da7-46f8-bf94-29816dda1e72

六、环境搭建

SpringBoot+SpringCloud的版本:

<spring.boot.version>3.2.0</spring.boot.version>
<spring.cloud.version>2023.0.0</spring.cloud.version>

注册中心:

  <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-consul-discovery</artifactId>
        </dependency>

远程调用:

   <!--openfeign-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

环境初始化代码:

https://github.com/shixiaochuangjob/markdownfile/tree/main/20240824/init

image-20240824171607929

Consul

image-20240823181110455

docker pull consul:1.9.6
docker run -d -p 8500:8500 --restart=always --name=consul -v /opt/consul:/consul/data consul:1.9.6 agent -server -bootstrap -ui -node=1 -client='0.0.0.0'
http://192.168.200.129:8500/ui/dc1/services

image-20240823180526926

jdk:

image-20240824171950942

Maven

image-20240823181810973

IDEA

image-20240823181906302

代码汇总:

https://github.com/shixiaochuangjob/markdownfile/tree/main/20240824

image-20240824171731425

https://mp.weixin.qq.com/s?__biz=MzkwOTczNzUxMQ==&mid=2247484580&idx=1&sn=b015f45e9013b0a75cf2c5ccf3c7329f&chksm=c1376fecf640e6fa710494ddaabaf00dd19ae1aa9568876fc81fca6e4889098f50101d90e214#rd

在这里插入图片描述

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

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

相关文章

C++笔记---内存管理

1. 内存分布 在对操作系统有更加深入的了解之前&#xff0c;在写代码的层面我们需要对下面的几个内存区域有所了解&#xff1a; 1. 栈又叫堆栈--非静态局部变量/函数参数/返回值等等&#xff0c;栈是向下增长的。 2. 堆--用于程序运行时动态内存分配&#xff0c;堆是可以上增长…

【数据结构3】哈希表、哈希表的应用(集合与字典、md5算法和文件的哈希值)

1 哈希表 哈希表一个通过哈希函数来计算数据存 储位置的数据结构&#xff0c;通常支持如下操作: 插入(键&#xff0c;值):插入键值对(键&#xff0c;值) Get(key):如果存在键为键的键值对则返回其值&#xff0c;否则返回空值 删除(键):删除键为键的键值对哈希表(Hash Table&am…

数据仓库系列 2:数据仓库的核心特点是什么?

想象一下,你正站在一座巨大的数据金矿前。这座金矿蕴含着海量的商业洞察,可以帮助你的公司做出精准决策,提升效率,远超竞争对手。但是,如何高效地开采、提炼和利用这些数据黄金呢?答案就是:数据仓库。 目录 什么是数据仓库?数据仓库的核心特点面向主题的组织集成性非易失性…

RTL-SDR SpectrumPy频谱显示

GITHUB大佬开源的基于RTL-SDR的python频谱显示程序链接&#xff0c;下载下来后&#xff0c;安装必要的库&#xff0c;编译运行&#xff0c;运行报错。 修改了以下两个地方&#xff1a; 修改点1&#xff1a; 修改前&#xff1a; self.spinBoxFrequency.setValue(self.center_fr…

【Python从入门到进阶】63.Pandas如何实现数据的Merge

接上篇《62、Pandas中DataFrame对象案例实践》 上一篇我们延续之前学习的DataFrame对象的知识&#xff0c;结合一个数据案例进行了实践操作。本篇我们来学习Pandas如何实现数据的Merge。 一、引言 在当今数据驱动的时代&#xff0c;数据分析已成为各行各业不可或缺的一部分。…

【JAVA基础】四则运算符

文章目录 四则运算结合运算符自增运算符关系和boolean运算符 四则运算 在java当中&#xff0c;使用运算符、-、*、/ 表示加减乘除&#xff0c;当参与 / 运算的两个操作数都是整数的时候&#xff0c;表示整数除法&#xff1b;否则表示浮点数。整数的求余操作用 % 表示。 Syste…

【Java】/* 与树有关的一些概念 */

一、关于树的一些概念 1. 树是一种非线性的数据结构&#xff0c;它是由n&#xff08;n>0&#xff09;个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看 起来像一棵倒挂的树&#xff0c;也就是说它是根朝上&#xff0c;而叶朝下的。它具有以下的特点&#xff1a;…

记录一次经历:使用flask_sqlalchemy集成flask造成循环导入问题

前言&#xff1a; 工作需求&#xff0c;写一个接口&#xff0c;用Python来编写&#xff0c;我首先想到用flask小型框架来支撑&#xff0c;配置sqlalchemy来实现&#xff0c;但是在实现的过程中&#xff0c;发生循环导入问题 我想到用蓝图来解决此问题&#xff0c;但是仍然会出死…

UI测试使用webdriver-manager免安装浏览器驱动

引言&#xff1a; selenium传统的方式是下载浏览器对应的driver&#xff08;驱动&#xff09;&#xff0c;放到本地的指定位置&#xff0c;然后写代码加载这个driver&#xff08;驱动&#xff09;再执行相应的操作。 弊端&#xff1a; 传统方法存在两个麻烦的地方: 1.需要下…

安全面试常见问题任意文件下载

《网安面试指南》http://mp.weixin.qq.com/s?__bizMzkwNjY1Mzc0Nw&mid2247484339&idx1&sn356300f169de74e7a778b04bfbbbd0ab&chksmc0e47aeff793f3f9a5f7abcfa57695e8944e52bca2de2c7a3eb1aecb3c1e6b9cb6abe509d51f&scene21#wechat_redirect 1.1 任意文件下…

Git的使用教程及常用语法03

七.如何从版本库中删除文件 第一种方式&#xff1a;直接在工作区删除文件&#xff0c;然后提交 rm ffile1.txt (注意&#xff1a;这个不是git命令&#xff0c;而是linux命令) 看到状态发现&#xff0c;文件file1.txt已经被删除&#xff0c;提示需要提交到暂存区。 因为我们只…

蓝牙对象交换协议(OBEX) - 概念介绍

零.声明 本专栏文章我们会以连载的方式持续更新&#xff0c;本专栏计划更新内容如下&#xff1a; 第一篇:蓝牙综合介绍 &#xff0c;主要介绍蓝牙的一些概念&#xff0c;产生背景&#xff0c;发展轨迹&#xff0c;市面蓝牙介绍&#xff0c;以及蓝牙开发板介绍。 第二篇:Trans…

SpringBoot集成kafka-监听器注解

SpringBoot集成kafka-监听器注解 1、application.yml2、生产者3、消费者4、测试类5、测试 1、application.yml #自定义配置 kafka:topic:name: helloTopicconsumer:group: helloGroup2、生产者 package com.power.producer;import com.power.model.User; import com.power.uti…

Windows系统上进行项目管理工具VisualSVN Server服务端的保姆级安装教程与配置和SVN客户端保姆级安装教程和使用

一、VisualSVN Server简介 Subversion Server for Windows | VisualSVN ServerGet an easy to use Subversion (SVN) server for Windows. It works out-of-the-box and is suitable both for small business and enterprises. Available for free!https://www.visualsvn.com/…

4.Redis单线程和多线程

1.Redis的单线程 Redis的单线程主要是指Redis的网络IO和键值对读写是由一个线程完成的&#xff0c;Redis在处理客户端的请求时包括获取&#xff08;Socket读&#xff09;、解析、执行、内容返回&#xff08;Socket写&#xff09;等都由一个顺序串行的主线程处理&#xff0c;这…

Linux 下命令行参数和环境变量

Linux 下命令行参数和环境变量 命令行参数为什么要有命令行参数谁可以做到结论 环境变量一些现象查看环境变量添加环境变量添加内存级环境变量永久有效 其他环境变量HOMEPWDSHELLHISTSIZE 自定义环境变量定义取消 本地变量整体理解环境变量环境变量的组织方式Linux 代码获取环境…

SpringBoot集成kafka接收对象消息

SpringBoot集成kafka接收对象消息 1、生产者2、消费者3、工具类4、消息实体对象5、配置文件6、启动类7、测试类8、测试结果 1、生产者 package com.power.producer;import com.power.model.User; import com.power.util.JSONUtils; import org.springframework.kafka.core.Kaf…

UEStudio V24 中文授权版

UEStudio是一款集成开发环境&#xff08;IDE&#xff09;软件&#xff0c;主要用于编写和编辑各种类型的代码&#xff0c;包括C/C、Java、HTML、PHP、Perl、Python等。 软件截图&#xff1a; 使用说明&#xff1a; 解压后&#xff0c;双击start_UEStudio.bat来运行软件 下载地…

【计算机组成原理】计算机系统概述<1>

学习目标&#xff1a; 掌握计算机组成原理的基础知识巩固 例如&#xff1a; 信息化世界的组成 计算机系统概述 计算机硬件基本组成 各个硬件的工作原理 计算机软件 计算机系统的多层次结构 计算机系统的工作原理 计算机性能指标 学习内容&#xff1a; 1.0、初入计算机组成原…

Apollo9.0 PNC源码学习之Planning模块—— Lattice规划(七):横纵向运动轨迹的优选

参考文章: (1)Apollo6.0代码Lattice算法详解——Part 7: 获得最优轨迹 (2)Lattice算法详解 0 前言 // 优选出cost最小的trajectory// 7. always get the best pair of trajectories to combine; return the first// collision-free trajectory.size_t constraint_failure…