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 ThreadPoolBulkheadThreadPoolBulkhead 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 的比较
- 在 Hystrix 中,对外部系统的调用必须包装在 HystrixCommand 中。相比之下,resilience4j提供高阶函数(装饰器),以增强任何功能接口、lambda 表达式或带有断路器、限速器或隔板的方法引用。此外,resilience4j还提供装饰器来重试失败的调用或缓存调用结果。您可以在任何函数接口、lambda 表达式或方法引用上堆叠多个装饰器。这意味着,您可以将 Bulkhead、RateLimiter 和 Retry 装饰器与 CircuitBreaker 装饰器组合在一起。优点是您可以选择所需的装饰器,而没有其他选择。任何修饰的函数都可以使用 CompletableFuture 或 RxJava 同步或异步执行。
- Hystrix 仅在半开状态下执行一次执行,以确定是否关闭 CircuitBreaker。resilience4j允许执行可配置的执行次数,并将结果与可配置的阈值进行比较,以确定是否关闭断路器。
- resilience4j提供自定义 Reactor 或 RxJava 运算符,以使用断路器、隔板或速率限制器装饰任何反应类型。
- 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 |
---|---|---|
failureRateThreshold | 50 | 以百分比为单位配置故障率阈值。 当故障率等于或大于阈值时,断路器将转换为打开并开始短路呼叫。 |
slowCallRateThreshold | 100 | 以百分比为单位配置阈值。当呼叫持续时间大于 slowCallDurationThreshold 当慢速呼叫的百分比等于或大于阈值时,断路器将转换为打开并开始短路呼叫。 |
slowCallDurationThreshold | 60000 [ms] | Configures the duration threshold above which calls are considered as slow and increase the rate of slow calls. 配置持续时间阈值,超过该阈值的呼叫将被视为慢速呼叫,并提高慢速呼叫的速率。 |
permittedNumberOfCalls InHalfOpenState | 10 | 配置断路器半开时允许的呼叫次数。 |
maxWaitDurationInHalfOpenState | 0 [ms] | 配置最大等待持续时间,该持续时间控制断路器在切换为打开之前可以保持半开状态的最长时间。 值 0 表示断路器将在半开状态下无限等待,直到完成所有允许的调用。 |
slidingWindowType | COUNT_BASED | 配置滑动窗口的类型,该滑动窗口用于在断路器关闭时记录呼叫结果。 滑动窗口可以是基于计数的,也可以是基于时间的。 如果滑动窗口为COUNT_BASED,则记录并汇总最后的 slidingWindowSize 呼叫。如果滑动窗口TIME_BASED,则记录并汇总最后 slidingWindowSize 几秒的呼叫。 |
slidingWindowSize | 100 | 配置滑动窗口的大小,该滑动窗口用于记录断路器关闭时的呼叫结果。 |
minimumNumberOfCalls | 100 | 配置断路器计算错误率或慢速呼叫率之前所需的最小呼叫数(每个滑动窗口周期)。 例如,如果 minimumNumberOfCalls 为 10,则必须至少记录 10 个调用,然后才能计算失败率。 如果只记录了 9 个请求,即使所有 9 个请求都失败,断路器也不会转换为打开。 |
waitDurationInOpenState | 60000 [ms] | 断路器在从打开状态转换到半打开状态之前应等待的时间。 |
automaticTransition FromOpenToHalfOpenEnabled | false | 如果设置为 true,则表示 CircuitBreaker 将自动从打开状态转换为半打开状态,无需调用即可触发转换。创建一个线程来监视 CircuitBreakers 的所有实例,以便在 waitDurationInOpenState 通过后将它们转换为HALF_OPEN。然而,如果设置为 false,则仅在进行调用时才会转换为 HALF_OPEN,即使在传递 waitDurationInOpenState 之后也是如此。这样做的好处是没有线程监控所有断路器的状态。 |
recordExceptions | empty | 记录为失败并因此增加失败率的异常列表。 任何匹配或从其中一个列表继承的异常都算作失败,除非通过 ignoreExceptions 显式忽略 .如果指定异常列表,则所有其他异常都算作成功,除非 ignoreExceptions 显式忽略它们。 |
ignoreExceptions | empty | 被忽略且既不算作失败也不算成功的异常列表。 任何匹配或从其中一个列表继承的异常都不会算作失败或成功,即使异常是 recordExceptions 的一部分。 |
recordFailurePredicate | throwable -> true 默认情况下,所有异常都作为失败进行重构。 | 一个自定义谓词,用于评估是否应将异常记录为失败。 如果异常应计为失败,则谓词必须返回 true。如果异常,则谓词必须返回 false 应算作成功,除非 ignoreExceptions 显式忽略异常。 |
ignoreExceptionPredicate | throwable -> 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: truegroup: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.Exceptioninstances: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
{@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ㄒ)/~~";}
}
Bulkhead(隔离)
介绍
Resilience4j 提供了两种隔离模式的实现,可用于限制并发执行的数量:
- a
SemaphoreBulkhead
使用信号量 - a
FixedThreadPoolBulkhead
使用有界队列和固定线程池。
它应该 SemaphoreBulkhead
在各种线程和 I/O 模型中都能很好地工作。它基于信号量,与 Hystrix 不同,它不提供“影子”线程池选项。客户端需要确保正确的线程池大小,该大小将与隔板配置一致。
创建 BulkheadRegistry
就像 CircuitBreaker 模块一样,此模块提供了一个内存 BulkheadRegistry
中和一个 ThreadPoolBulkheadRegistry
可用于管理(创建和检索)Bulkhead 实例的内存。
BulkheadRegistry bulkheadRegistry = BulkheadRegistry.ofDefaults();ThreadPoolBulkheadRegistry threadPoolBulkheadRegistry = ThreadPoolBulkheadRegistry.ofDefaults();
创建和配置隔板
Config property | Default value | Description |
---|---|---|
maxConcurrentCalls | 25 | 隔离允许的最大并行执行量 |
maxWaitDuration | 0 | 尝试进入饱和隔板时应阻塞并发的最大时间。 |
// 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 |
---|---|---|
maxThreadPoolSize | Runtime.getRuntime() .availableProcessors() | 配置最大线程池大小。 |
coreThreadPoolSize | Runtime.getRuntime() .availableProcessors() - 1 | 配置核心线程池大小 |
queueCapacity | 100 | 配置队列的容量。 |
keepAliveDuration | 20 [ms] | 当线程数大于核心数时,这是多余的空闲线程在终止之前等待新任务的最长时间。 |
writableStackTraceEnabled | true | 引发隔板异常时输出堆栈跟踪错误。 如果为 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秒,过时不候进舱壁兜底fallbackinstances:cloud-payment-service:baseConfig: defaulttimelimiter: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刷新一次,将允许处理的最大请求数量重置为limitForPeriodtimeout-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ㄒ)/~~";
}
相关文章:

Spring-Cloud-CircuitBreaker-Resilience4j (3.1.1)
介绍 Resilience4j 是一个专为函数式编程而设计的轻量级容错库。Resilience4j 提供高阶函数(装饰器),以增强任何功能接口、lambda 表达式或方法引用,包括断路器、速率限制器、重试或隔板。您可以在任何函数接口、lambda 表达式或…...

重构与优化-组织数据(3)
重构组织数据是一个系统性的工程,旨在改进数据的存储方式、访问效率、质量和可用性,以更好地支持业务运营、分析决策和未来发展。以下是重构组织数据的一些关键说明点: 目的与动机 提升效率:通过优化数据结构、减少冗余数据和改善索引策略,加快数据查询和处理速度。 增强…...

游戏交易平台源码游戏帐号交易平台系统源码
功能介绍 1:后台可以添加删除游戏分类 2:会员中心可以出售游戏币,账号,装备 3:后台可以对会员和商品进行管理 4:多商家入驻,商家发布信息 5:手机版功能可以生成APP 6:在线支付可支持微信和支…...

Matlab里面的浮点数与FPGA定点数的相互转化应用(含Matlab代码,封装成函数可直接调用)
微信公众号获取更多FPGA相关源码: 1.前言 Matlab里面计算通常用的是浮点数,而FPGA在做数字信号处理时,为了节约资源,常常使用的是定点数。在实践中,我们经常需要将Matlab实现中的算法,用FPGA进行实现。 …...

机器学习笔记——欠拟合、过拟合
欠拟合 将训练损失和测试损失都比较大的拟合叫欠拟合,那么他的预测精度很低 1.一般出现在模型的复杂度小于数据本身的复杂度导致的,这个可能就是模型对数据的分布和实际数据分布之间的差异,这个就可能需要更换模型 2.还可能出现在梯度下降算…...

【二进制部署k8s-1.29.4】七、验证master的安装
文章目录 简介 一.确认kubectl命令是否正常运行二.确认etcd安装是否正常运行三.确认kube-apiserver,kube-controller-manager,kube-scheduler安装是否正常四.配置apiserver和kubelet的访问授权五.master端安装脚本4.1.安装master端所需文件4.2.master快捷安装脚本 简介 本章节主…...

springboot获取当前数据库连接
要获取当前 Spring DataSource 的 URL,可以通过以下几种方法: 方法一:使用 JdbcTemplate 如果你使用的是 Spring 的 JdbcTemplate,可以通过 javax.sql.DataSource 获取连接,再获取它的 URL。 示例代码: …...

【学习笔记】Windows GDI绘图(九)Graphics详解(上)
文章目录 Graphics 定义创建Graphics对象的方法通过Graphics绘制不同的形状、线条、图像和文字等通过Graphics操作对象坐标 Graphics属性Clip(裁切/绘制区域)ClipBounds获取裁切区域矩形范围CompositiongMode合成方式CompositingQuality渲染质量DpiX和DpiY 水平、垂直分辨率Int…...

公告:公众号铁粉粉丝介绍以及说明
大家好,我是公众号博主--夏目 机械电气电机杂谈是我个人建立,为分享机械,电气,电机知识为主,闲谈杂聊社会时事,职场见闻,生活琐事,成长趣事,学习心得,读书观影…...

BioTech - 使用 CombFold 算法 实现 大型蛋白质复合物结构 的组装过程
欢迎关注我的CSDN:https://spike.blog.csdn.net/ 本文地址:https://spike.blog.csdn.net/article/details/139242199 CombFold 是用于预测大型蛋白质复合物结构的组合和分层组装算法,利用 AlphaFold2 预测的亚基之间的成对相互作用。 CombFold 算法的关键特点包括: 组合和…...

代码随想录算法训练营第36期DAY46
DAY46 完全背包 在闫氏DP法里学过:第i个物品选k个,纸质直至不能选,k从0开始取。就有递推式了。 代码随想录的视频也看了。 518零钱兑换ii 注意与 目标和 那题区分开。 完全背包问题,正向遍历背包容量,就能实现“多次…...

港湾周评|李小加“刀刃向内”裁员
《港湾商业观察》李镭 近年来争议颇大的滴灌通风波不断。 在交100万付费上班不久,最新又被曝出裁员。这位前港交所总裁、金融圈鼎鼎大名的李小加,没想到成立不足三年便迎来了重大挑战。 日前,滴灌通确认了公司组织架构已经调整,…...

超大功率光伏并网逆变器学习(三相)
1.超大功率用的IGBT开关频率通常很低,比如6KHz 2.线电压和相电压的关系 相电压 A AB线电压-CA线电压 相电压 B BC线电压-AB线电压 相电压 C CA线电压-BC线电压 3.坐标变换 ABC三相信号通过Clark坐标变换得到αβ两相静止信号,其中α与A相重合,β与α…...

大豆、棉花深度学习数据集大合集
最近收集了一大波关于大豆和棉花的深度学习数据集,主要有叶片的识别、分类、计数以及病害检测等。 数据集的价值 科研价值:这些数据集为植物学、农业信息技术、机器学习等领域的科研人员提供了宝贵的资源。它们可以用于训练和优化各种深度学习模型&…...

教育数字展馆助力全球教育传播,科技引领数字化教育潮流
一、教育数字展馆助力教育传播 1、提高教育资源的可及性 教育数字展馆通过VR和WEB3D技术,将丰富的教育资源呈现在用户面前。不论是名校的经典课程,还是专家的精彩讲座,均可通过教育数字展馆实现线上展示。用户只需登录平台,即可…...

14.微信小程序之地理定位功能
目录 1.地理定位介绍 1.1 申请开通 1.2 使用方法 2.拒绝授权后的解决方案 3.开通腾讯位置服务 4.LBS 逆地址解析 1.地理定位介绍 小程序地理定位是指通过小程序开发平台提供的 API,来获取用户的地理位置信息。用户在使用小程序时,可以授权小程序获…...

理解lambda表达式
Lambda表达式: 这里不再过多叙述什么事lambda表达式,就说下怎么使用,首先和lambda表达式同时存在的就是另一个定义,就是匿名内部类。匿名内部类首先需要一个接口。 下面用一个例子说明lambda表达式: public class Hel…...

【面试】Java的前端编译器和后端编译器
目录 1. 说明2. 前端编译器2.1 主要功能2.2 工作原理 3. 后端编译器3.1 主要功能3.2 工作原理 1. 说明 1.在Java的编译过程中,编译器通常被划分为前端编译器和后端编译器,各自负责不同的任务。2.前端编译器主要负责源代码的词法分析、语法分析和语义检查…...

教育小程序的性能优化:从前端到后端的综合提升策略
随着教育小程序的普及,其性能直接影响用户体验和教学效果。本文将从前端到后端,详细探讨教育小程序的性能优化策略,帮助开发者打造高效、流畅的教育应用。 一、前端性能优化策略 代码优化 减少HTTP请求:合并CSS、JavaScript文件…...

单链表实现通讯录
之前我们完成了基于顺序表(动态)实现通讯录,现在我们链表学完了,可以尝试着使用链表来实现我们的通讯录。 首先我们要明白我们写的通讯录是由一个个节点组成的,每个节点里存储的就是我们的联系人信息。也就是说 我们需…...

Linux 命令操作技巧
Linux命令行界面提供了丰富的快捷键来提高操作效率,以下是一些常用的Linux终端快捷键,主要基于Bash shell: Tab - 自动补全:输入命令、文件名、目录名或命令选项的开头部分,然后按Tab键,系统会自动补全剩余…...

深度学习21天 —— 卷积神经网络(CNN):识别验证码( 第12天)
目录 一、前期准备 1.1 标签数字化 1.2 加载数据 1.3 配置数据 二、其他 2.1 损失函数 categorical_crossentropy 2.2 plt.legend(loc ) 2.3 history.history 活动地址:CSDN21天学习挑战赛 学习:深度学习100例-卷积神经网络(CNN&…...

利用 Docker 简化Redis部署:快速搭建Redis服务
利用 Docker 简化Redis部署:快速搭建Redis服务 目录 利用 Docker 简化Redis部署:快速搭建Redis服务为什么选择 Docker准备工作拉取Redis镜像快速运行Redis容器验证Redis服务总结 在现代软件开发中,Redis作为一种高性能的键值数据库࿰…...

Web前端框架:深入探索与实践
Web前端框架:深入探索与实践 在当下数字化飞速发展的时代,Web前端框架的选择与应用成为了开发者们关注的焦点。Node.js,作为一种强大的后端技术,在前端框架的构建中也发挥着不可或缺的作用。本文将围绕Node.js Web前端框架&#…...

【算法】贪心算法——柠檬水找零
题解:柠檬水找零(贪心算法) 目录 1.题目2.题解3.参考代码4.证明5.总结 1.题目 题目链接:LINK 2.题解 分情况讨论 贪心算法 当顾客为5元时,收下当顾客为10元时,收下10元并找回5元当顾客为20元时,收下20元并找回10…...

Jmeter安装教程
1 Jmeter下载 Jmeter下载地址:https://jmeter.apache.org/download_jmeter.cgi,选择需要的版本点击下载 解压jmeter安装包 解压后的安装包如下: 2 配置Jmeter环境变量 进入环境变量配置页面:计算机->属性->高级系统设置-&…...

关于磁盘管理
磁盘管理是操作系统提供的一项功能,用于高效地组织、维护和控制计算机的硬盘驱动器及其卷(分区)。通过磁盘管理工具,用户和管理员可以执行多种与存储相关的高级任务,主要包括: 初始化新磁盘: …...

人大金仓数据库大小写不敏感确认
1、图形化确认(管理—其他选项—预设选项) 2、命令行确认 # ksql -p 54321 -U system test # show enable_ci; 查看是否大小写敏感,on表示大小敏感,off表示大小写不敏感,使用某些项目的时候,需要设置数据库大小写不敏感&#…...

【Java】还有人不懂继承?25 个 Case 包教包会
还有人不懂继承?25 个 Case 包教包会 1.Implement single inheritance2.Implement multilevel inheritance3.Implement hierarchical inheritance4.Override a base class method into a derived class5.Demonstrate the protected access specifier6.Create an Stu…...

Qt实现窗口失去焦点抖动功能
一、失去焦点检测 当窗口失去焦点时会发出FocusOut事件,具体实现如下: 首先给窗口安装事件过滤器: this->installEventFilter(this);然后在事件过滤器函数中判断有没有失去焦点 bool MessageDialog::eventFilter(QObject *object, QEve…...