08-Resilience4j
Resilience4j是一个轻量级、高性能并易于使用容错框架,设计灵感来源于Netflix 的Hystrix框架,采用广受欢迎的函数式编程风格。
第一步:导入jar
spring boot环境:
<!-- springboot3.x 使用resilience4j-spring-boot3-->
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-spring-boot2</artifactId>
<version>2.3.0</version>
</dependency>spring cloud环境:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-circuitbreaker-resilience4j</artifactId>
</dependency>resilience4j的核心模块
- resilience4j-circuitbreaker: 熔断
- resilience4j-ratelimiter: 限流
- resilience4j-bulkhead: 舱壁
- resilience4j-retry: 重试
- resilience4j-cache: 缓存
- resilience4j-timelimiter: 超时处理
一、熔断机制
1.1 创建CircuitBreaker
/// CircuitBreaker的配置 5秒内,最少2次调用后,失败超过50%,则触发熔断关闭状态到熔断开启状态
// 熔断开启状态到熔断半开状态,自动实现,熔断开启时间为1秒,1秒内不接受请求,1后熔断开启状态自动到熔断半开状态。
// 熔断半开状态下,允许通过3次调用,3次调用成功超过50%,则关闭熔断。
CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom()
.slidingWindowType(CircuitBreakerConfig.SlidingWindowType.TIME_BASED)
.slidingWindowSize(5)
.minimumNumberOfCalls(2)
.failureRateThreshold(50)
.automaticTransitionFromOpenToHalfOpenEnabled(true)
.waitDurationInOpenState(Duration.ofMillis(1000))
.permittedNumberOfCallsInHalfOpenState(3)
.slowCallDurationThreshold(Duration.ofSeconds(2))
.slowCallRateThreshold(60)
.recordExceptions(ArithmeticException.class, RuntimeException.class)
.ignoreExceptions(IndexOutOfBoundsException.class)
.build();
// 创建CircuitBreaker的注册器
CircuitBreakerRegistry circuitBreakerRegistry = CircuitBreakerRegistry.ofDefaults();
// 创建CircuitBreaker
CircuitBreaker circuitBreaker = circuitBreakerRegistry.circuitBreaker("circuitBreaker", circuitBreakerConfig);circuitbreaker的详细配置信:
| 配置属性 | 默认值 | 描述 |
|---|---|---|
| slidingWindowType | COUNT_BASED | 配置滑动窗口的类型,当断路器关闭时,将调用的结果记录在滑动窗口中。滑动窗口的类型可以是count-based或time-based。如果滑动窗口类型是COUNT_BASED,将会统计记录最近slidingWindowSize次调用的结果。如果是TIME_BASED,将会统计记录最slidingWindowSize秒的调用结果。 |
| slidingWindowSize | 100 | 配置滑动窗口的大小,即统计的时间窗口。 |
| failureRateThreshold | 50 | 以百分比配置失败率阈值。当失败率等于或大于阈值时,断路器状态并关闭变为开启,并进行服务降级。 |
| slowCallRateThreshold | 100 | 以百分比的方式配置,断路器把调用时间大于slowCallDurationThreshold |
| slowCallDurationThreshold | 60000 [ms] | 配置调用时间的阈值,高于该阈值的呼叫视为慢调用,并增加慢调用比例。 |
| permittedNumberOfCallsInHalfOpenState | 10 | 断路器在半开状态下允许通过的调用次数。 |
| maxWaitDurationInHalfOpenState | 0 | 断路器在半开状态下的最长等待时间,超过该配置值的话,断路器会从半开状态恢复为开启状态。配置是0时表示断路器会一直处于半开状态,直到所有允许通过的访问结束。 |
| minimumNumberOfCalls | 100 | 断路器计算失败率或慢调用率之前所需的最小调用数(每个滑动窗口周期)。例如,如果minimumNumberOfCalls为10,则必须至少记录10个调用,然后才能计算失败率。如果只记录了9次调用,即使所有9次调用都失败,断路器也不会开启。 |
| waitDurationInOpenState | 60000 [ms] | 断路器从开启过渡到半开应等待的时间。 |
| automaticTransitionFromOpenToHalfOpenEnabled | false | 如果设置为true,则意味着断路器将自动从开启状态过渡到半开状态,并且不需要调用来触发转换。创建一个线程来监视断路器的所有实例,以便在WaitDurationInOpenstate之后将它们转换为半开状态。但是,如果设置为false,则只有在发出调用时才会转换到半开,即使在waitDurationInOpenState之后也是如此。这里的优点是没有线程监视所有断路器的状态。 |
| recordExceptions | empty | 记录为失败并因此增加失败率的异常列表。除非通过ignoreExceptions显式忽略,否则与列表中某个匹配或继承的异常都将被视为失败。如果指定异常列表,则所有其他异常均视为成功,除非它们被ignoreExceptions显式忽略。 |
| ignoreExceptions | empty | 被忽略且既不算失败也不算成功的异常列表。任何与列表之一匹配或继承的异常都不会被视为失败或成功,即使异常是recordExceptions的一部分。 |
| recordException | throwable -> true·By default all exceptions are recored as failures. | 自定义函数判断一个异常是否应记录为失败。函数返回true,异常应计为失败。函数返回false,异常计为成功,,除非ignoreExceptions显式忽略异常。 |
| ignoreException | throwable -> falseBy default no exception is ignored. | 自定义函数来判断一个异常是否应该被忽略,如果应忽略异常,函数返回true。 |
| 如果异常应算作失败,则函数来须返回false。 |
1.2 使用CircuitBreaker
// 应用CircuitBreaker
String result = circuitBreaker.executeCheckedSupplier(() -> System.out.println("execute retryMethod"));
System.out.println(result);circuitbreaker对Callable、Runnable)、Supplier(数据提供者)、Consumer(数据消费者)、CheckedRunable、CheckedSupplier、CheckedConsumer或CompletionStage接口都提供了相应的执行方法:
executeCallable()
executeCheckedRunnable()
executeCheckedSupplier()
executeCompletionStage()
executeRunnable()
executeSupplier()上面是直接执行,也可以将装饰和执行分开,这样性能会高一些:
CircuitBreaker circuitBreaker = circuitBreakerRegistry.circuitBreaker("circuitBreaker", circuitBreakerConfig);
// 应用CircuitBreaker
Supplier supplier = circuitBreaker.decorateSupplier(() -> {
System.out.println("class=TestApplication,method=test,message=execute");
int i = 10 / 0;
return "ok";
});
Object result = supplier.get();
System.out.println(result);circuitbreaker对Callable、Runnable)、Supplier(数据提供者)、Consumer(数据消费者)、CheckedRunable、CheckedSupplier、CheckedConsumer或CompletionStage接口都提供了相应的装饰方法:
decorateCallable()
decorateCheckedRunnable()
decorateCheckedSupplier()
decorateCompletionStage()
decorateRunnable()
decorateSupplier()
decorateConsumer()
decorateFuture()1.3 监听CircuitBreaker
监听CircuitBreakerRegistry的注册和注销事件:
circuitBreakerRegistry.getEventPublisher()
.onEntryAdded(entryAddedEvent -> {
// 注册了新的circuitbreaker
CircuitBreaker addedCircuitBreaker = entryAddedEvent.getAddedEntry();
System.out.println("CircuitBreaker added:"+addedCircuitBreaker.getName());
})
.onEntryRemoved(entryRemovedEvent -> {
// 注销circuitbreaker
CircuitBreaker removedCircuitBreaker = entryRemovedEvent.getRemovedEntry();
System.out.println("CircuitBreaker removed:"+removedCircuitBreaker.getName());
});监听circuitbreaker的状态转换和调用结果等事件:
// 分别处理不同的事件
circuitBreaker.getEventPublisher().onSuccess(event -> System.out.println("Success"+event));
circuitBreaker.getEventPublisher().onError(event -> System.out.println("Error"+event));
circuitBreaker.getEventPublisher().onIgnoredError(event -> System.out.println("IgnoredError"+event));
circuitBreaker.getEventPublisher().onReset(event -> System.out.println("Reset"+event));
circuitBreaker.getEventPublisher().onStateTransition(event -> System.out.println("StateTransition"+event));
circuitBreaker.getEventPublisher().onEvent(event -> System.out.println("event"+event));简单的监听测试:
@SpringBootTest
public class TestApplication {
@Test
public void test() throws Throwable {
CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom()
.failureRateThreshold(50)
.slowCallRateThreshold(50)
.slowCallDurationThreshold(Duration.ofSeconds(2))
.permittedNumberOfCallsInHalfOpenState(3)
.waitDurationInOpenState(Duration.ofMillis(1000))
.minimumNumberOfCalls(10)
.slidingWindowType(CircuitBreakerConfig.SlidingWindowType.TIME_BASED)
.slidingWindowSize(5)
.recordExceptions(ArithmeticException.class, RuntimeException.class)
.ignoreExceptions(IndexOutOfBoundsException.class)
.build();
// 创建CircuitBreaker的注册器
CircuitBreakerRegistry circuitBreakerRegistry = CircuitBreakerRegistry.ofDefaults();
// 注册和注销事件
circuitBreakerRegistry.getEventPublisher()
.onEntryAdded(entryAddedEvent -> {
// 注册了新的circuitbreaker
CircuitBreaker addedCircuitBreaker = entryAddedEvent.getAddedEntry();
System.out.println("CircuitBreaker 注册:" + addedCircuitBreaker.getName());
})
.onEntryRemoved(entryRemovedEvent -> {
// 注销circuitbreaker
CircuitBreaker removedCircuitBreaker = entryRemovedEvent.getRemovedEntry();
System.out.println("CircuitBreaker 移除:" + removedCircuitBreaker.getName());
});
// 创建CircuitBreaker
CircuitBreaker circuitBreaker = circuitBreakerRegistry.circuitBreaker("circuitBreaker", circuitBreakerConfig);
// 分别处理不同的事件
circuitBreaker.getEventPublisher().onSuccess(event -> System.out.println("成功:" + event));
circuitBreaker.getEventPublisher().onError(event -> System.out.println("失败异常:" + event));
circuitBreaker.getEventPublisher().onIgnoredError(event -> System.out.println("忽略异常:" + event));
circuitBreaker.getEventPublisher().onReset(event -> System.out.println("重置:" + event));
circuitBreaker.getEventPublisher().onStateTransition(event -> System.out.println("StateTransition:" + event));
circuitBreaker.getEventPublisher().onEvent(event -> System.out.println("event:" + event));
// 应用CircuitBreaker
for (int e = 0; e < 5; e++) {
try {
int finalE = e;
circuitBreaker.executeSupplier(() -> {
System.out.println("circuitBreaker executeSupplier : " + finalE);
int i = finalE % 4;
if(i == 0){
throw new ArithmeticException();
}else if(i == 1){
throw new RuntimeException();
}else if (i == 2){
throw new IndexOutOfBoundsException();
}
return finalE;
});
} catch (Exception ex) {
System.out.println("circuitBreaker Exception : " + ex);
}
}
circuitBreakerRegistry.remove("circuitBreaker");
}
}结果:
CircuitBreaker 注册:circuitBreaker
circuitBreaker executeSupplier : 0
event:2025-07-09T13:49:47.107+08:00[Asia/Shanghai]: CircuitBreaker 'circuitBreaker' recorded an error: 'java.lang.ArithmeticException'. Elapsed time: 0 ms
失败异常:2025-07-09T13:49:47.107+08:00[Asia/Shanghai]: CircuitBreaker 'circuitBreaker' recorded an error: 'java.lang.ArithmeticException'. Elapsed time: 0 ms
circuitBreaker Exception : java.lang.ArithmeticException
circuitBreaker executeSupplier : 1
event:2025-07-09T13:49:47.111+08:00[Asia/Shanghai]: CircuitBreaker 'circuitBreaker' recorded an error: 'java.lang.RuntimeException'. Elapsed time: 0 ms
失败异常:2025-07-09T13:49:47.111+08:00[Asia/Shanghai]: CircuitBreaker 'circuitBreaker' recorded an error: 'java.lang.RuntimeException'. Elapsed time: 0 ms
circuitBreaker Exception : java.lang.RuntimeException
circuitBreaker executeSupplier : 2
event:2025-07-09T13:49:47.112+08:00[Asia/Shanghai]: CircuitBreaker 'circuitBreaker' recorded an error which has been ignored: 'java.lang.IndexOutOfBoundsException'. Elapsed time: 0 ms
忽略异常:2025-07-09T13:49:47.112+08:00[Asia/Shanghai]: CircuitBreaker 'circuitBreaker' recorded an error which has been ignored: 'java.lang.IndexOutOfBoundsException'. Elapsed time: 0 ms
circuitBreaker Exception : java.lang.IndexOutOfBoundsException
circuitBreaker executeSupplier : 3
event:2025-07-09T13:49:47.112+08:00[Asia/Shanghai]: CircuitBreaker 'circuitBreaker' recorded a successful call. Elapsed time: 0 ms
成功:2025-07-09T13:49:47.112+08:00[Asia/Shanghai]: CircuitBreaker 'circuitBreaker' recorded a successful call. Elapsed time: 0 ms
circuitBreaker executeSupplier : 4
event:2025-07-09T13:49:47.113+08:00[Asia/Shanghai]: CircuitBreaker 'circuitBreaker' recorded an error: 'java.lang.ArithmeticException'. Elapsed time: 0 ms
失败异常:2025-07-09T13:49:47.113+08:00[Asia/Shanghai]: CircuitBreaker 'circuitBreaker' recorded an error: 'java.lang.ArithmeticException'. Elapsed time: 0 ms
circuitBreaker Exception : java.lang.ArithmeticException
CircuitBreaker 移除:circuitBreaker1.4 熔断演示:
@SpringBootTest
public class TestApplication {
@Test
public void test() throws Throwable {
// CircuitBreaker的配置 5秒内,最小5次调用后,失败超过50%,则触发熔断关闭状态到熔断开启状态
// 熔断开启状态到熔断半开状态,自动实现,熔断开启时间为1秒,1秒内不接受请求,1后熔断开启状态自动到熔断半开状态。
// 熔断半开状态下,允许通过3次调用,3次调用成功超过50%,则关闭熔断。
CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom()
.slidingWindowType(CircuitBreakerConfig.SlidingWindowType.TIME_BASED)
.slidingWindowSize(5)
.minimumNumberOfCalls(5)
.failureRateThreshold(50)
.automaticTransitionFromOpenToHalfOpenEnabled(true)
.waitDurationInOpenState(Duration.ofMillis(1000))
.permittedNumberOfCallsInHalfOpenState(3)
.slowCallDurationThreshold(Duration.ofSeconds(2))
.slowCallRateThreshold(60)
.recordExceptions(ArithmeticException.class, RuntimeException.class)
.ignoreExceptions(IndexOutOfBoundsException.class)
.build();
// 创建CircuitBreaker的注册器
CircuitBreakerRegistry circuitBreakerRegistry = CircuitBreakerRegistry.ofDefaults();
// 创建CircuitBreaker
CircuitBreaker circuitBreaker = circuitBreakerRegistry.circuitBreaker("circuitBreaker", circuitBreakerConfig);
// 应用CircuitBreaker
for (int e = 1; e < 16; e++) {
if(e == 8){
Thread.sleep(1000);
}
try {
int finalE = e;
circuitBreaker.executeSupplier(() -> {
System.out.println("circuitBreaker executeSupplier : " + finalE);
if(finalE == 1 || finalE == 2){
throw new RuntimeException();
}else if (finalE == 5){
throw new IndexOutOfBoundsException();
}
return finalE;
});
} catch (Exception ex) {
System.out.println("circuitBreaker Exception : " + ex);
}
}
}
}结果:
circuitBreaker executeSupplier : 1
circuitBreaker Exception : java.lang.RuntimeException
circuitBreaker executeSupplier : 2
circuitBreaker Exception : java.lang.RuntimeException
circuitBreaker executeSupplier : 3
circuitBreaker executeSupplier : 4
circuitBreaker executeSupplier : 5
circuitBreaker Exception : java.lang.ArithmeticException
circuitBreaker Exception : io.github.resilience4j.circuitbreaker.CallNotPermittedException: CircuitBreaker 'circuitBreaker' is OPEN and does not permit further calls
circuitBreaker Exception : io.github.resilience4j.circuitbreaker.CallNotPermittedException: CircuitBreaker 'circuitBreaker' is OPEN and does not permit further calls
circuitBreaker executeSupplier : 8
circuitBreaker executeSupplier : 9
circuitBreaker executeSupplier : 10
circuitBreaker executeSupplier : 11
circuitBreaker executeSupplier : 12
circuitBreaker executeSupplier : 13
circuitBreaker executeSupplier : 14
circuitBreaker executeSupplier : 15分析:
第1、2次出异常虽然异常比例超过50%,但是不满足最小调用次数大于等于5次,所以3、4、5次依然可以正常访问。
第5次又出现异常,由于之前第1、2次出异常,异常比例超过50%且满足最小调用次数大于等于5次,所以6和7次熔断机制拦截。
第8次访问之前等待了1秒,半开状态下访问的成功了100%。关闭熔断,后续正常访问。
二、限流机制
ratelimiter可以限制单位时间内可以调用的次数,以避免系统过载,引发故障,甚至造成系统崩溃。
实现ratelimiter一般有以下思路:
- 滚动时间窗口:在指定的时间窗口内最多调用指定的次数。窗口每次移动窗口大小的时间,相邻窗口时间不重叠。这种方式调用速率可能不均衡,可能出现突发峰值速率。
- 滑动时间窗口:与滚动时间窗口相同,在指定的时间窗口内最多调用指定的次数。但窗口每次只移动指定步长(步长小于窗口大小,比如十分之一的窗口大小)的时间,相邻窗口时间重叠。
- 令牌桶:令牌桶会以固定的速率往令牌桶中放入令牌,令牌桶中的令牌数有一个最大值,任何时候令牌桶中的令牌数不会超过最大值。调用前需要先到令牌桶中获取相应数量的令牌,只有令牌桶中具有相应的令牌时才运行调用
- 漏桶:漏桶像一个装有水或沙子的漏斗,调用像漏桶中的水或沙子一样按固定的速率流出
resilience4j的ratelimiter有两种实现,一种基于SemaphoreBasedRateLimiter(令牌桶),另一种基于AtomicRateLimiter(时间窗口)
public class AtomicRateLimiter implements RateLimiter
public class SemaphoreBasedRateLimiter implements RateLimiter@SpringBootTest
public class TestApplication {
@Test
public void test() throws Throwable {
// 自定义配置 1秒 3次 超时25ms
RateLimiterConfig config = RateLimiterConfig.custom()
.limitRefreshPeriod(Duration.ofSeconds(1))
.limitForPeriod(3)
.timeoutDuration(Duration.ofMillis(25))
.build();
// 使用自定义配置创建
// 创建Registry
RateLimiterRegistry rateLimiterRegistry = RateLimiterRegistry.of(config);
RateLimiter rateLimiter = rateLimiterRegistry.rateLimiter("rateLimiter");
// 使用熔断的使用方式一致
for (int i = 0; i < 10; i++) {
if(i == 7){
Thread.sleep(1000);
}
int finalI = i;
rateLimiter.executeSupplier(() -> {
System.out.println("rateLimiter executeSupplier i:" + finalI);
return null;
});
}
}
}结果:
rateLimiter executeSupplier i:0
rateLimiter executeSupplier i:1
rateLimiter executeSupplier i:2
rateLimiter Exception : io.github.resilience4j.ratelimiter.RequestNotPermitted: RateLimiter 'rateLimiter' does not permit further calls
rateLimiter Exception : io.github.resilience4j.ratelimiter.RequestNotPermitted: RateLimiter 'rateLimiter' does not permit further calls
rateLimiter Exception : io.github.resilience4j.ratelimiter.RequestNotPermitted: RateLimiter 'rateLimiter' does not permit further calls
rateLimiter Exception : io.github.resilience4j.ratelimiter.RequestNotPermitted: RateLimiter 'rateLimiter' does not permit further calls
rateLimiter executeSupplier i:7
rateLimiter executeSupplier i:8
rateLimiter executeSupplier i:9| 配置属性 | 默认值 | 描述 |
|---|---|---|
| tmeoutDuration | 5秒 | 线程等待调用许可(如可用令牌)的等待时间,一般比limitRefreshPeriod刷新周期大 |
| limitRefreshPeriod | 500纳秒 | 限流器每隔limitRefreshPeriod刷新一次,将允许处理的最大请求数量重置为limitForPeriod。 |
| limitForPeriod | 50 | 在一次刷新周期内,允许执行的最大请求数 |
三、超时处理
超时处理表示调用的方法超时如何处理,timelimiter与其他的组件有一个区别,就是它的调用必须放在与timelimiter不同的线程中执行,否则不生效。示例代码如下:
TimeLimiterConfig config = TimeLimiterConfig.custom()
// 是否停止正在异步执行的调用
.cancelRunningFuture(true)
// 超时时间
.timeoutDuration(Duration.ofMillis(500))
.build();
// 使用自定义的全局配置创建一个TimeLimiterRegistry
TimeLimiterRegistry timeLimiterRegistry = TimeLimiterRegistry.of(config);
//registry使用默认的配置创建一个TimeLimiter
TimeLimiter timeLimiter = timeLimiterRegistry.timeLimiter("timeLimiter");
// 被调用服务的运行的线程池
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(3);
// 非阻塞方式:返回CompletableFuture类型的变量
CompletableFuture<String> executeCompletionStage = timeLimiter.executeCompletionStage(
scheduler, () -> CompletableFuture.supplyAsync(() -> {
System.out.println("executeCompletionStage Thread Name:"+ Thread.currentThread().getName());
return "executeCompletionStage";
})).toCompletableFuture();
// 阻塞方式:实际上是调用了future.get(timeoutDuration, MILLISECONDS)
String executeFutureSupplier = timeLimiter.executeFutureSupplier(() -> CompletableFuture.supplyAsync(() -> {
System.out.println("executeFutureSupplier Thread Name:"+ Thread.currentThread().getName());
return "executeFutureSupplier";
}));
System.out.println("1:"+executeFutureSupplier);
System.out.println("2:"+executeCompletionStage.get());解释:TimeLimiter的方法executeCompletionStage和executeFutureSupplier
// executeFutureSupplier方法返回值是Future
default <T, F extends Future<T>> T executeFutureSupplier(Supplier<F> futureSupplier) throws Exception {
return decorateFutureSupplier(this, futureSupplier).call();
}
// executeFutureSupplier方法返回值是CompletionStage
default <T, F extends CompletionStage<T>> CompletionStage<T> executeCompletionStage(ScheduledExecutorService scheduler, Supplier<F> supplier) {
return (CompletionStage)decorateCompletionStage(this, scheduler, supplier).get();
}CompletableFuture.supplyAsync方法:
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) {
return asyncSupplyStage(asyncPool, supplier);
}
// CompletableFuture实现了Future和CompletionStage接口:
public class CompletableFuture<T> implements Future<T>, CompletionStage<T>四、舱壁机制
bulkhead实质上是限制调用的并发度。它有两种实现:SemaphoreBulkhead和FixedThreadPoolBulkhead。
- SemaphoreBulkhead通过semaphore数量控制并发数,而不限制调用在哪个线程池中执行。
- FixedThreadPoolBulkhead则通过创建指定大小的线程池来控制并发数,调用发生在同一个线程池中。
bulkhead配置参数:
| 配置属性 | 默认值 | 描述 |
|---|---|---|
| maxConcurrentCalls | 25 | 允许并发执行的最大数量 |
| maxWaitDuration | 0 | 当达到并发调用数量时,新的调用将被阻塞,这个属性表示最长的等待时间。 |
创建SemaphoreBulkhead:
//为Bulkhead创建自定义的配置 BulkheadConfig config = BulkheadConfig
.custom()
.maxConcurrentCalls(150)
.maxWaitDuration(Duration.ofMillis(500))
.build();
// 使用默认的配置从registry中创建Bulkhead
Bulkhead bulkheadWithDefaultConfig = registry.bulkhead(“bulkhead");。创建FixedThreadPoolBulkhead:
ThreadPoolBulkheadConfig config = ThreadPoolBulkheadConfig
.custom()
.maxThreadPoolSize(10)
.coreThreadPoolSize(2)
.queueCapacity(20)
.build();
ThreadPoolBulkhead bulkhead = ThreadPoolBulkhead.of("bulkhead", config);
CompletionStage<String> supplier = ThreadPoolBulkhead .executeSupplier(bulkhead, ()->{System.out.println("executeFutureSupplier Thread Name:"+ Thread.currentThread().getName());});五、重试机制
@SpringBootTest
public class TestApplication {
@Test
public void test() throws Throwable {
RetryConfig config = RetryConfig.custom()
.maxAttempts(2)
.waitDuration(Duration.ofMillis(1000))
.intervalFunction(IntervalFunction.of(Duration.ofMillis(1000)))
.retryOnResult(response -> {
// response可以强转为自定义的的response类型
return response == null;
})
.retryOnException(e -> e instanceof WebServiceException)
.retryExceptions(IOException.class, TimeoutException.class)
.ignoreExceptions(RuntimeException.class, IndexOutOfBoundsException.class)
.build();
// 使用自定义的配置创建RetryRegistry
RetryRegistry registry = RetryRegistry.of(config);
// 使用默认的配置从Registry中获取和创建一个Retry
Retry retry = registry.retry("retry");
retry.executeSupplier(() -> {
System.out.println("hello world");
return null;
});
}
}| 配置属性 | 默认值 | 描述 |
|---|---|---|
| maxAttempts | 3 | 最大重试次数 |
| waitDuration | 500 [ms] | 两次重试之间的时间间隔 |
| intervalFunction | numOfAttempts -> waitDuration | 修改重试间隔的函数。默认情况下,等待时间保持不变。 |
| retryOnResult | Predicateresult -> false | 配置用于计算是否应重试的断言。如果要重试,断言必须返回true,否则返回false。 |
| retryOnException | Predicatethrowable -> true | 配置一个断言,判断某个异常发生时,是否要进行重试。如果要重试,断言必须返回true,否则必须返回false。 |
| retryExceptions | empty | 配置一个Throwable类型的列表,被记录为失败类型,需要进行重试,支持子类型。 |
| ignoreExceptions | empty | 配置一个Throwable类型的列表,被记录为忽略类型,不会进行重试,支持子类型。 |
六、通过注解使用resilience4j
application.yml:
resilience4j.circuitbreaker:
configs:
default:
slidingWindowSize: 100
permittedNumberOfCallsInHalfOpenState: 10
waitDurationInOpenState: 10000
failureRateThreshold: 60
eventConsumerBufferSize: 10
registerHealthIndicator: true
someShared:
slidingWindowSize: 50
permittedNumberOfCallsInHalfOpenState: 10
instances:
backendA:
baseConfig: default
waitDurationInOpenState: 5000
backendB:
baseConfig: someShared
registerHealthIndicator: true
slidingWindowSize: 10
permittedNumberOfCallsInHalfOpenState: 3
slidingWindowType: TIME_BASED
minimumNumberOfCalls: 20
waitDurationInOpenState: 50s
failureRateThreshold: 50
eventConsumerBufferSize: 10
recordFailurePredicate: io.github.robwin.exception.RecordFailurePredicate
resilience4j.retry:
instances:
backendA:
waitDuration: 10s
enableExponentialBackoff: true
exponentialBackoffMultiplier: 2
retryExceptions:
- org.springframework.web.client.HttpServerErrorException
- java.io.IOException
backendB:
waitDuration: 10s
retryExceptions:
- org.springframework.web.client.HttpServerErrorException
ignoreExceptions:
- java.io.IOException
resilience4j.bulkhead:
instances:
backendA:
maxConcurrentCalls: 10
backendB:
maxWaitDuration: 10ms
maxConcurrentCalls: 20
resilience4j.thread-pool-bulkhead:
instances:
backendC:
maxThreadPoolSize: 1
coreThreadPoolSize: 1
queueCapacity: 1
resilience4j.ratelimiter:
instances:
backendA:
limitForPeriod: 10
limitRefreshPeriod: 1s
timeoutDuration: 0
registerHealthIndicator: true
eventConsumerBufferSize: 100
backendB:
limitForPeriod: 6
limitRefreshPeriod: 500ms
timeoutDuration: 3s
resilience4j.timelimiter:
instances:
backendA:
timeoutDuration: 2s
cancelRunningFuture: true
backendB:
timeoutDuration: 1s
cancelRunningFuture: false然后就可以在需要保护的接口上添加相应的组件了:
@RestController @RequestMapping(value = "/")
public class TestController {
@CircuitBreaker(name = "backendB", fallbackMethod = "fallback")
@RateLimiter(name = "backendB")
@Bulkhead(name = "backendB")
@Retry(name = "backendB", fallbackMethod = "fallback")
@TimeLimiter(name = "backendB")
@GetMapping(value = "/test")
public String test() {
return "hello";
}
private String fallback(IllegalArgumentException e) {
return "hello fallback";
}
}需要注意的是,通过注解的方式默认执行的顺序如下,bulkhead先执行,最后是retry:
Retry ( CircuitBreaker ( RateLimiter ( TimeLimiter ( Bulkhead ( CalledFunction ) ) ) ) )然而,你可以通过如下配置修改执行的顺序AspectOrder越小越先执行:
resilience4j:
circuitbreaker:
circuitBreakerAspectOrder: 1
retry:
retryAspectOrder: 2