Spring-Cloud-CircuitBreaker-Resilience4j (3.1.1)

介绍

Resilience4j 是一个专为函数式编程而设计的轻量级容错库。Resilience4j 提供高阶函数(装饰器),以增强任何功能接口、lambda 表达式或方法引用,包括断路器、速率限制器、重试或隔板。您可以在任何函数接口、lambda 表达式或方法引用上堆叠多个装饰器。优点是您可以选择所需的装饰器,而没有别的。
注意:Resilience4j 2 需要 Java 17。

Resilience4j 提供高阶函数(装饰器),以增强任何功能接口、lambda 表达式或方法引用,包括断路器、速率限制器、重试或隔板。您可以在任何函数接口、lambda 表达式或方法引用上堆叠多个装饰器。优点是您可以选择所需的装饰器,而没有别的。

使用 Resilience4j,可不全部使用,可以选择您需要的东西。

示例

以下示例演示如何使用 CircuitBreaker 和 Retry 修饰 lambda 表达式,以便在发生异常时重试调用 3 次。

您可以配置重试之间的等待间隔,也可以配置自定义回退算法。

该示例使用 Vavr 的 Try monad 从异常中恢复,并在所有重试都失败时调用另一个 lambda 表达式作为回退。

// 创建一个默认配置的CircuitBreaker 
CircuitBreaker circuitBreaker = CircuitBreaker
  .ofDefaults("backendService");

// 创建一个默认配置的Retry
// 3次重试,每次间隔500ms
Retry retry = Retry
  .ofDefaults("backendService");

// Create a Bulkhead with default configuration
Bulkhead bulkhead = Bulkhead
  .ofDefaults("backendService");

Supplier<String> supplier = () -> backendService
  .doSomething(param1, param2)

// Decorate your call to backendService.doSomething() 
// with a Bulkhead, CircuitBreaker and Retry
// **note: you will need the resilience4j-all dependency for this
Supplier<String> decoratedSupplier = Decorators.ofSupplier(supplier)
  .withCircuitBreaker(circuitBreaker)
  .withBulkhead(bulkhead)
  .withRetry(retry)  
  .decorate();

// When you don't want to decorate your lambda expression,
// but just execute it and protect the call by a CircuitBreaker.
String result = circuitBreaker
  .executeSupplier(backendService::doSomething);

// You can also run the supplier asynchronously in a ThreadPoolBulkhead
 ThreadPoolBulkhead threadPoolBulkhead = ThreadPoolBulkhead
  .ofDefaults("backendService");

// The Scheduler is needed to schedule a timeout 
// on a non-blocking CompletableFuture
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3);
TimeLimiter timeLimiter = TimeLimiter.of(Duration.ofSeconds(1));

CompletableFuture<String> future = Decorators.ofSupplier(supplier)
    .withThreadPoolBulkhead(threadPoolBulkhead)
    .withTimeLimiter(timeLimiter, scheduledExecutorService)
    .withCircuitBreaker(circuitBreaker)
    .withFallback(asList(TimeoutException.class, 
                         CallNotPermittedException.class, 
                         BulkheadFullException.class),  
                  throwable -> "Hello from Recovery")
    .get().toCompletableFuture();

所有核心模块和 Decorators 类

  • resilience4j-all 弹性4J-全部

Core modules 核心模块

  • resilience4j-circuitbreaker:熔断
  • resilience4j-ratelimiter:速率限制
  • resilience4j-bulkhead: 舱壁
  • resilience4j-retry:自动重试(同步和异步)
  • resilience4j-cache:结果缓存
  • resilience4j-timelimiter:超时处理

Add-on modules 附加模块

  • resilience4j-feign:假装适配器
  • resilience4j-consumer:循环缓冲区事件消费者
  • resilience4j-kotlin:Kotlin 协程支持
  • resilience4j-vavr:Vavr 支持

Frameworks modules 框架模块

  • resilience4j-spring-boot3: Spring Boot 3 Starter
  • resilience4j-spring-boot2: Spring Boot 2 Starter
  • resilience4j-micronaut: Micronaut Starter

Reactive modules 电抗模块

  • resilience4j-rxjava2:自定义 RxJava2 运算符
  • resilience4j-rxjava3:自定义 RxJava3 运算符
  • resilience4j-reactor:定制弹簧反应器操作器

Metrics modules 指标模块

  • resilience4j-micrometer: 微米度量导出器
  • resilience4j-metrics:Dropwizard 指标导出器

与 Netflix Hystrix 的比较

  1. 在 Hystrix 中,对外部系统的调用必须包装在 HystrixCommand 中。相比之下,resilience4j提供高阶函数(装饰器),以增强任何功能接口、lambda 表达式或带有断路器、限速器或隔板的方法引用。此外,resilience4j还提供装饰器来重试失败的调用或缓存调用结果。您可以在任何函数接口、lambda 表达式或方法引用上堆叠多个装饰器。这意味着,您可以将 Bulkhead、RateLimiter 和 Retry 装饰器与 CircuitBreaker 装饰器组合在一起。优点是您可以选择所需的装饰器,而没有其他选择。任何修饰的函数都可以使用 CompletableFuture 或 RxJava 同步或异步执行。
  2. Hystrix 仅在半开状态下执行一次执行,以确定是否关闭 CircuitBreaker。resilience4j允许执行可配置的执行次数,并将结果与可配置的阈值进行比较,以确定是否关闭断路器。
  3. resilience4j提供自定义 Reactor 或 RxJava 运算符,以使用断路器、隔板或速率限制器装饰任何反应类型。
  4. Hystrix 和此库发出事件流,这些事件流对系统操作员监控有关执行结果和延迟的指标很有用。

核心模块

CircuitBreaker 断路器

介绍

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

CircuitBreaker 使用滑动窗口来存储和聚合调用结果。您可以在基于计数的滑动窗口基于时间的滑动窗口之间进行选择。基于计数的滑动窗口聚合了最后 N 次调用的结果。基于时间的滑动窗口聚合了最后 N 秒的调用结果。

基于计数的滑动窗口

基于计数的滑动窗口是通过 N 个测量值的圆形阵列实现的。

如果计数窗口大小为 10,则圆形数组始终具有 10 个测量值。

滑动窗口以增量方式更新总聚合。记录新的呼叫结果时,总聚合将更新。当驱逐最早的测量值时,将从总聚合中减去该测量值,并重置存储桶。(逐出时减法)


检索 Snapshot 的时间是常量 O(1),因为 Snapshot 是预先聚合的,并且与窗口大小无关。

此实现的空间要求(内存消耗)应为 O(n)。

基于时间的滑动窗口

基于时间的滑动窗口是通过 N 个部分聚合(桶)的循环数组实现的。

如果时间窗口大小为 10 秒,则圆形数组始终具有 10 个部分聚合(存储桶)。每个存储桶聚合在特定纪元秒内发生的所有调用的结果。(部分聚合)。圆形数组的 head 桶存储当前 epoch 秒的调用结果。其他部分聚合存储前几秒的调用结果。

滑动窗口不会单独存储调用结果(元组),而是以增量方式更新部分聚合(存储桶)和总聚合。

当记录新的呼叫结果时,总聚合将以增量方式更新。当最旧的存储桶被逐出时,将从总聚合中减去该存储桶的部分总聚合,并重置该存储桶。(逐出时减法)


检索 Snapshot 的时间是常数 O(1),因为 Snapshot 是预先聚合的,并且与时间窗口大小无关。

此实现的空间要求(内存消耗)应几乎为常量 O(n),因为调用结果(元组)不是单独存储的。仅创建 N 个部分聚合和 1 个总聚合。


部分聚合由 3 个整数组成,用于计算失败呼叫数、慢速呼叫数和呼叫总数。一个长,用于存储所有调用的总持续时间。

故障率和慢速请求率阈值

情况一:当故障率等于或大于可配置阈值时,断路器的状态将从 CLOSED 更改为 OPEN。例如,当超过 50% 的请求失败时。

默认情况下,所有异常都算作失败。可以定义应计为失败的异常列表。然后,所有其他异常都算作成功,除非它们被忽略。也可以忽略异常,这样它们既不算作失败也不算成功。


情况二:当慢速调用的百分比等于或大于可配置的阈值时,断路器也会从 CLOSED 更改为 OPEN。例如,当超过 50% 的请求时间超过 5 秒时。这有助于在外部系统实际无响应之前减少外部系统的负载。


只有在记录了最小呼叫数的情况下,才能计算故障率和慢速呼叫率。例如,如果所需的最小呼叫数为 10,则必须至少记录 10 个呼叫,然后才能计算故障率。如果仅评估了 9 个呼叫,即使所有 9 个呼叫都失败,断路器也不会跳闸打开。


CircuitBreaker 在 OPEN 时拒绝带有 的 CallNotPermittedException 请求。等待时间过后,断路器状态将从 OPEN 更改为 HALF_OPEN,并允许可配置的调用次数,以查看后端是否仍然不可用或已再次可用。进一步的请求将被拒绝, CallNotPermittedException 直到 所有允许的呼叫都完成。

如果故障率或慢速调用率等于或大于配置的阈值,则状态将变回 OPEN。如果故障率和慢速调用率低于阈值,则状态将变回 CLOSED。


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

CircuitBreaker 是线程安全的,如下所示:

  • CircuitBreaker 的状态存储在 AtomicReference 中
  • CircuitBreaker 使用原子操作通过无副作用功能更新状态。
  • 从滑动窗口录制通话和读取快照是同步的


这意味着应该保证原子性,并且只有一个线程能够在某个时间点更新状态或滑动窗口。


但 CircuitBreaker 不会同步函数调用。这意味着函数调用本身不是关键部分的一部分。否则,断路器将引入巨大的性能损失和瓶颈。缓慢的函数调用将对整体性能/吞吐量产生巨大的负面影响。


如果 20 个并发线程请求执行函数的权限,并且 CircuitBreaker 的状态为关闭,则允许所有线程调用该函数。即使滑动窗口大小为 15。滑动窗口并不意味着只允许同时运行 15 个调用。如果要限制并发线程数,请使用 Bulkhead。可以组合使用隔板和断路器。

创建 CircuitBreakerRegistry

Resilience4j 带有一个基于 ConcurrentHashMap 的内存 CircuitBreakerRegistry ,它提供线程安全性和原子性保证。您可以使用 CircuitBreakerRegistry 管理(创建和检索)CircuitBreaker 实例。您可以为所有 CircuitBreaker 实例创建具有全局默认值 CircuitBreakerConfig 的 CircuitBreakerRegistry,如下所示。

CircuitBreakerRegistry circuitBreakerRegistry = 
  CircuitBreakerRegistry.ofDefaults();

创建和配置断路器

您可以提供自己的自定义全局 CircuitBreakerConfig .要创建自定义全局 CircuitBreakerConfig,可以使用 CircuitBreakerConfig 构建器。您可以使用构建器配置以下属性。

Config property Config Default Value Description
failureRateThreshold50以百分比为单位配置故障率阈值。
当故障率等于或大于阈值时,断路器将转换为打开并开始短路呼叫。
slowCallRateThreshold100以百分比为单位配置阈值。当呼叫持续时间大于 slowCallDurationThreshold

当慢速呼叫的百分比等于或大于阈值时,断路器将转换为打开并开始短路呼叫。
slowCallDurationThreshold60000 [ms]Configures the duration threshold above which calls are considered as slow and increase the rate of slow calls.
配置持续时间阈值,超过该阈值的呼叫将被视为慢速呼叫,并提高慢速呼叫的速率。
permittedNumberOfCalls
InHalfOpenState 
10配置断路器半开时允许的呼叫次数。
maxWaitDurationInHalfOpenState0 [ms] 配置最大等待持续时间,该持续时间控制断路器在切换为打开之前可以保持半开状态的最长时间。
值 0 表示断路器将在半开状态下无限等待,直到完成所有允许的调用。
slidingWindowTypeCOUNT_BASED
配置滑动窗口的类型,该滑动窗口用于在断路器关闭时记录呼叫结果。

滑动窗口可以是基于计数的,也可以是基于时间的。

如果滑动窗口为COUNT_BASED,则记录并汇总最后的 slidingWindowSize 呼叫。

如果滑动窗口TIME_BASED,则记录并汇总最后 slidingWindowSize 几秒的呼叫。
slidingWindowSize100配置滑动窗口的大小,该滑动窗口用于记录断路器关闭时的呼叫结果。
minimumNumberOfCalls100配置断路器计算错误率或慢速呼叫率之前所需的最小呼叫数(每个滑动窗口周期)。

例如,如果 minimumNumberOfCalls 为 10,则必须至少记录 10 个调用,然后才能计算失败率。

如果只记录了 9 个请求,即使所有 9 个请求都失败,断路器也不会转换为打开。
waitDurationInOpenState60000 [ms] 断路器在从打开状态转换到半打开状态之前应等待的时间。
automaticTransition
FromOpenToHalfOpenEnabled

 
false如果设置为 true,则表示 CircuitBreaker 将自动从打开状态转换为半打开状态,无需调用即可触发转换。创建一个线程来监视 CircuitBreakers 的所有实例,以便在 waitDurationInOpenState 通过后将它们转换为HALF_OPEN。然而,如果设置为 false,则仅在进行调用时才会转换为 HALF_OPEN,即使在传递 waitDurationInOpenState 之后也是如此。这样做的好处是没有线程监控所有断路器的状态。
recordExceptionsempty
记录为失败并因此增加失败率的异常列表。

任何匹配或从其中一个列表继承的异常都算作失败,除非通过 ignoreExceptions 显式忽略 .

如果指定异常列表,则所有其他异常都算作成功,除非 ignoreExceptions 显式忽略它们。
ignoreExceptionsempty被忽略且既不算作失败也不算成功的异常列表。

任何匹配或从其中一个列表继承的异常都不会算作失败或成功,即使异常是 recordExceptions 的一部分。
recordFailurePredicatethrowable -> true 


默认情况下,所有异常都作为失败进行重构。
一个自定义谓词,用于评估是否应将异常记录为失败。

如果异常应计为失败,则谓词必须返回 true。如果异常,则谓词必须返回 false

应算作成功,除非 ignoreExceptions 显式忽略异常。
ignoreExceptionPredicatethrowable -> false 


默认情况下,不会忽略任何异常。

一个自定义谓词,用于评估是否应忽略异常,并且既不算作失败也不算成功。

如果应忽略异常,则 Predicate 必须返回 true。
如果异常应计为失败,则谓词必须返回 false。
// Create a custom configuration for a CircuitBreaker
CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom()
  .failureRateThreshold(50)
  .slowCallRateThreshold(50)
  .waitDurationInOpenState(Duration.ofMillis(1000))
  .slowCallDurationThreshold(Duration.ofSeconds(2))
  .permittedNumberOfCallsInHalfOpenState(3)
  .minimumNumberOfCalls(10)
  .slidingWindowType(SlidingWindowType.TIME_BASED)
  .slidingWindowSize(5)
  .recordException(e -> INTERNAL_SERVER_ERROR
                 .equals(getResponse().getStatus()))
  .recordExceptions(IOException.class, TimeoutException.class)
  .ignoreExceptions(BusinessException.class, OtherBusinessException.class)
  .build();

// Create a CircuitBreakerRegistry with a custom global configuration
CircuitBreakerRegistry circuitBreakerRegistry = 
  CircuitBreakerRegistry.of(circuitBreakerConfig);

// Get or create a CircuitBreaker from the CircuitBreakerRegistry 
// with the global default configuration
CircuitBreaker circuitBreakerWithDefaultConfig = 
  circuitBreakerRegistry.circuitBreaker("name1");

// Get or create a CircuitBreaker from the CircuitBreakerRegistry 
// with a custom configuration
CircuitBreaker circuitBreakerWithCustomConfig = circuitBreakerRegistry
  .circuitBreaker("name2", circuitBreakerConfig);

您可以添加可由多个 CircuitBreaker 实例共享的配置。

CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom()
  .failureRateThreshold(70)
  .build();

circuitBreakerRegistry.addConfiguration("someSharedConfig", config);

CircuitBreaker circuitBreaker = circuitBreakerRegistry
  .circuitBreaker("name", "someSharedConfig");

您可以覆盖配置。

CircuitBreakerConfig defaultConfig = circuitBreakerRegistry
   .getDefaultConfig();

CircuitBreakerConfig overwrittenConfig = CircuitBreakerConfig
  .from(defaultConfig)
  .waitDurationInOpenState(Duration.ofSeconds(20))
  .build();

如果您不想使用 CircuitBreakerRegistry 管理 CircuitBreaker 实例,也可以直接创建实例。

// Create a custom configuration for a CircuitBreaker
CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom()
  .recordExceptions(IOException.class, TimeoutException.class)
  .ignoreExceptions(BusinessException.class, OtherBusinessException.class)
  .build();

CircuitBreaker customCircuitBreaker = CircuitBreaker
  .of("testName", circuitBreakerConfig);

CircuitBreaker 使用案例

pom文件

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

yml文件配置

spring:
 cloud:
    circuitbreaker:
             enabled: true
             group:
               enabled: true #没开分组永远不用分组的配置。精确优先、分组次之(开了分组)、默认最后

# Resilience4j CircuitBreaker 按照次数:COUNT_BASED 的例子
#  6次访问中当执行方法的失败率达到50%时CircuitBreaker将进入开启OPEN状态(保险丝跳闸断电)拒绝所有请求。
#  等待5秒后,CircuitBreaker 将自动从开启OPEN状态过渡到半开HALF_OPEN状态,允许一些请求通过以测试服务是否恢复正常。
#  如还是异常CircuitBreaker 将重新进入开启OPEN状态;如正常将进入关闭CLOSE闭合状态恢复正常处理请求。
# 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.Exception
    instances:
      cloud-payment-service:
        baseConfig: default

Controller层


package com.atguigu.cloud.controller;

import com.atguigu.cloud.apis.PayFeignApi;
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;

/**
 * @auther zzyy
 * @create 2023-11-13 14:54
 * Resilience4j CircuitBreaker 的例子
 */
@RestController
public class OrderCircuitController
{
    @Resource
    private 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ㄒ)/~~";
    }
}

Bulkhead(隔离)

介绍

Resilience4j 提供了两种隔离模式的实现,可用于限制并发执行的数量:

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

它应该 SemaphoreBulkhead 在各种线程和 I/O 模型中都能很好地工作。它基于信号量,与 Hystrix 不同,它不提供“影子”线程池选项。客户端需要确保正确的线程池大小,该大小将与隔板配置一致。

创建 BulkheadRegistry

就像 CircuitBreaker 模块一样,此模块提供了一个内存 BulkheadRegistry 中和一个 ThreadPoolBulkheadRegistry 可用于管理(创建和检索)Bulkhead 实例的内存。

BulkheadRegistry bulkheadRegistry = BulkheadRegistry.ofDefaults();

ThreadPoolBulkheadRegistry threadPoolBulkheadRegistry = 
  ThreadPoolBulkheadRegistry.ofDefaults();

创建和配置隔板

Config property Default value Description
maxConcurrentCalls25隔离允许的最大并行执行量
maxWaitDuration0
尝试进入饱和隔板时应阻塞并发的最大时间。
// Create a custom configuration for a Bulkhead
BulkheadConfig config = BulkheadConfig.custom()
  	.maxConcurrentCalls(150)
  	.maxWaitDuration(Duration.ofMillis(500))
  	.build();

// Create a BulkheadRegistry with a custom global configuration
BulkheadRegistry registry = BulkheadRegistry.of(config);

// Get or create a Bulkhead from the registry - 
// bulkhead will be backed by the default config
Bulkhead bulkheadWithDefaultConfig = registry.bulkhead("name1");

// Get or create a Bulkhead from the registry, 
// use a custom configuration when creating the bulkhead
Bulkhead bulkheadWithCustomConfig = registry.bulkhead("name2", custom);

创建和配置 ThreadPoolBulkhead

可以提供自定义全局 ThreadPoolBulkheadConfig。若要创建自定义全局 ThreadPoolBulkheadConfig,可以使用 ThreadPoolBulkheadConfig 生成器。您可以使用构建器配置以下属性。

Config property Default value Description
maxThreadPoolSizeRuntime.getRuntime() 
.availableProcessors() 

配置最大线程池大小。
coreThreadPoolSizeRuntime.getRuntime()
.availableProcessors() - 1

配置核心线程池大小
queueCapacity100配置队列的容量。
keepAliveDuration20 [ms] 当线程数大于核心数时,这是多余的空闲线程在终止之前等待新任务的最长时间。
writableStackTraceEnabledtrue引发隔板异常时输出堆栈跟踪错误。
如果为 false,则输出带有隔板异常的单行。
ThreadPoolBulkheadConfig config = ThreadPoolBulkheadConfig.custom()
  .maxThreadPoolSize(10)
  .coreThreadPoolSize(2)
  .queueCapacity(20)
  .build();
        
// Create a BulkheadRegistry with a custom global configuration
ThreadPoolBulkheadRegistry registry = ThreadPoolBulkheadRegistry.of(config);

// Get or create a ThreadPoolBulkhead from the registry - 
// bulkhead will be backed by the default config
ThreadPoolBulkhead bulkheadWithDefaultConfig = registry.bulkhead("name1");

// Get or create a Bulkhead from the registry, 
// use a custom configuration when creating the bulkhead
ThreadPoolBulkheadConfig custom = ThreadPoolBulkheadConfig.custom()
  .maxThreadPoolSize(5)
  .build();

ThreadPoolBulkhead bulkheadWithCustomConfig = registry.bulkhead("name2", custom);

Bulihead使用案例

pom文件

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

yml配置

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

Controller层

@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ㄒ)/~~";
}

RateLimiter限流

限流是准备 API 以进行扩展并建立服务的高可用性和可靠性的必要技术。而且,这种技术还带有一大堆不同的选项,用于如何处理检测到的限制盈余,或者您要限制哪种类型的请求。您可以简单地拒绝此超限请求,或者构建一个队列以稍后执行它们,或者以某种方式组合这两种方法。

内部

Resilience4j 提供了一个 RateLimiter,它将从纪元开始的所有纳秒拆分为多个周期。每个周期的持续时间由 配置。 RateLimiterConfig.limitRefreshPeriod 在每个周期开始时,RateLimiter 将活动权限数设置为 RateLimiterConfig.limitForPeriod 。

对于 RateLimiter 调用者来说,它看起来确实是这样的,但对于 AtomicRateLimiter 实现来说,如果未主动使用 RateLimiter,则在后台进行了一些优化,这些优化将跳过此刷新。

限流使用

pom文件

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

yml配置

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

controller层

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

每日复盘-20240530

今日重点关注&#xff1a; 20240530 六日涨幅最大: ------1--------300637--------- 扬帆新材 五日涨幅最大: ------1--------300637--------- 扬帆新材 四日涨幅最大: ------1--------300637--------- 扬帆新材 三日涨幅最大: ------1--------301129--------- 瑞纳智能 二日涨…

Stable Diffusion AI绘画:从创意词汇到艺术图画的魔法之旅

文章目录 一、Stable Diffusion的工作原理二、从提示词到模型出图的过程三、Stable Diffusion在艺术创作中的应用《Stable Diffusion AI绘画从提示词到模型出图》内容简介作者简介楚天 目录前言/序言本书特色特别提示 获取方式 在科技的飞速发展中&#xff0c;Stable Diffusion…

读取YUV数据到AVFrame并用多线程控制帧率

文件树&#xff1a; 1.xvideo_view.h class XVideoView { public:// 像素格式枚举enum Format { RGBA 0, ARGB, YUV420P };// 渲染类型枚举enum RenderType { SDL 0 };// 创建渲染对象的静态方法static XVideoView* Create(RenderType type SDL);// 绘制帧的方法bool DrawF…

Word下一页怎么操作?6个操作方法分享!

在使用Word进行文档编辑时&#xff0c;经常需要设置分页、跳转页面或者对页面进行特定的布局调整。其中&#xff0c;“下一页”这一操作通常涉及到页面布局、分页符的插入、页面跳转等多个方面。 Word下一页怎么操作&#xff1f;很简单&#xff0c;方法已经准备好啦&#xff0…

Python代码:二十五、有序的列表

1、题目 创建一个依次包含字符串P、y、t、h、o和n的列表my_list&#xff0c;先使用sorted函数对列表my_list进行临时排序&#xff0c;第一行输出排序后的完整列表&#xff0c;第二行输出原始的列表。再使用sort函数对列表my_list进行降序排序&#xff0c;第三行输出排序后完整…

20240531瑞芯微官方Toybrick TB-RK3588开发板在Debian11下的关机尝试

20240531瑞芯微官方Toybrick TB-RK3588开发板在Debian11下的关机尝试 2024/5/31 9:10 https://t.rock-chips.com/forum.php?modforumdisplay&fid51 在Toybrick的论坛以关键词搜索&#xff1a;关机 可以找到抱怨这个的人很多&#xff01; &#xff08;一&#xff09;【失败…

深入分析 Android Service (三)

文章目录 深入分析 Android Service (三)1. Service 与 Activity 之间的通信2. 详细示例&#xff1a;通过绑定服务进行通信2.1 创建一个绑定服务2.2 绑定和通信 3. 优化建议4. 使用场景5. 总结 深入分析 Android Service (三) 1. Service 与 Activity 之间的通信 在 Android …

linux nohup命令详解:持久运行命令,无视终端退出

nohup &#xff08;全称为 “no hang up”&#xff09;&#xff0c;用于运行一个命令&#xff0c;使其在你退出 shell 或终端会话后继续运行。 基本语法 nohup command [arg1 ...] [&> output_file] &command 是你想要运行的命令。[arg1 ...] 是该命令的参数。&am…

Spring Boot 整合 spring-boot-starter-mail 实现邮件发送和账户激活

&#x1f604; 19年之后由于某些原因断更了三年&#xff0c;23年重新扬帆起航&#xff0c;推出更多优质博文&#xff0c;希望大家多多支持&#xff5e; &#x1f337; 古之立大事者&#xff0c;不惟有超世之才&#xff0c;亦必有坚忍不拔之志 &#x1f390; 个人CSND主页——Mi…

Wireshark抓包后的报文太大,如何拆分?

背景&#xff1a;抓包获取到一个400多兆的网络数据包.pcapng文件&#xff0c;使用wireshark软件可以正常打开。但需要把文件导出为.json文件&#xff0c;从而方便对报文内容做过滤分析。使用wireshark自带的导出功能导出后发现生成的.json文件大小为2G多&#xff0c;使用notepa…

Java-----Comparable接口和Comparator接口

在Java中&#xff0c;我们会经常使用到自定义类&#xff0c;那我们如何进行自定义类的比较呢? 1.Comparable接口 普通数据的比较 int a10;int b91;System.out.println(a<b); 那自定义类型可不可以这样比较呢&#xff1f;看一下代码 我们发现会报错&#xff0c;因为自定义…

电机控制系列模块解析(25)—— 过压抑制与欠压抑制

一、概念解析 变频器作为一种重要的电机驱动装置&#xff0c;其内置的保护功能对于确保系统安全、稳定运行至关重要。以下是关于变频器过压抑制、欠压抑制&#xff08;晃电抑制&#xff09;、发电功率限制、电动功率限制等保护功能的详细说明&#xff1a; 过压抑制 过压抑制是…

YoloV8改进策略:卷积篇|基于PConv的二次创新|附结构图|性能和精度得到大幅度提高(独家原创)

摘要 在PConv的基础上做了二次创新,创新后的模型不仅在精度和速度上有了质的提升,还可以支持Stride为2的降采样。 改进方法简单高效,需要发论文的同学不要错过! 论文指导 PConv在论文中的描述 论文: 下面我们展示了可以通过利用特征图的冗余来进一步优化成本。如图3所…

golang web补充知识:单元测试

文章目录 golang 单元测试引言单元测试的重要性Go语言单元测试的优势 Go语言单元测试基础testing包辅助测试函数运行单元测试 Mock技术Mock简介GoMock框架介绍编写Mock代码使用Mock进行单元测试 面向测试编程&#xff08;TDD&#xff09;TDD简介TDD的优势TDD的基本步骤Go语言中…

discuz如何添加主导航

大家好&#xff0c;今天教大家怎么样给discuz添加主导航。方法其实很简单&#xff0c;大家跟着我操作既可。一个网站的导航栏是非常重要的&#xff0c;一般用户进入网站的第一印象就是看网站的导航栏。如果大家想看效果的话可以搜索下网创有方&#xff0c;或者直接点击查看效果…

大数据中的电商数仓项目:探秘业务的核心

我学习完一个电商数仓的项目和电影实时推荐项目&#xff0c;便兴冲冲的去面试大数据开发岗&#xff0c;在面试的时候&#xff0c;面试官总是喜欢问&#xff0c;聊聊你为什么要做这个项目以及你这个项目有哪些业务&#xff1f; 我心想&#xff0c;为什么要做这个业务&#xff1f…

重学java 52.集合 斗地主案例

你太锐利了&#xff0c;这些年来&#xff0c;风霜雨雪&#xff0c;踉跄清冷&#xff0c;我相信你所有的苦楚 —— 24.5.30 1 案例介绍 按照斗地主的规则&#xff0c;完成洗牌发牌的动作。 具体规则: 使用54张牌打乱顺序,三个玩家参与游戏&#xff0c;三人交替摸牌&#xff0c…

ipv6基础

地址 前缀子网主机位 PI法则3.14 前缀&#xff1a;3个16位 子网&#xff1a;1个16位 接口ID&#xff1a;4个16位 地址分类 未指定地址 ::/128 &#xff0c;类似于0.0.0.0 本地回环地址 ::1/128 &#xff0c;用于本地测试&#xff0c;类似于127.0.0.1 本地链路地址&#x…

【云原生】kubernetes中pod的生命周期、探测钩子的实战应用案例解析

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

正则匹配优化:匹配排除多个字符串的其他字符串

(^entity|^with|...)\w优化 (?!entity|with|has|index|associations|input)\w(?!): 匹配排除项 效果 继续优化 匹配会过滤掉带有关键字的字段&#xff0c;在过滤的时候是可以加上尾部结束匹配符的 效果&#xff1a;