Reactor integrates Resilence4j
1 Import the pom package
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-all</artifactId>
</dependency>
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-spring-boot2</artifactId>
</dependency>
2 Configuration Instructions
2.1 ratelimiter
Two current limiting configurations: a maximum of 10 requests are allowed in backendA 1s;
backendB allows up to 6 requests every 500ms.
resilience4j.ratelimiter:
instances:
backendA:
limitForPeriod: 10
limitRefreshPeriod: 1s
timeoutDuration: 10ms
registerHealthIndicator: true
eventConsumerBufferSize: 100
backendB:
limitForPeriod: 6
limitRefreshPeriod: 500ms
timeoutDuration: 3s
configuration properties | Defaults | describe |
---|---|---|
timeoutDuration | 5【s】 | The default wait time for a thread to wait for permission |
limitRefreshPeriod | 500【ns】 | Limit the refresh cycle. After each period, the rate limiter sets its permission count back to the limitForPeriod value |
limitForPeriod | 50 | A limitRefreshPeriod (period) The number of accesses allowed (number of licenses) |
2.2 Retry retry
Note that specifying the exception that needs to be retried, not all exception retries are valid. For example, DB related validation exceptions, such as unique constraints, etc., retry will not be successful.
Retry configuration:
resilience4j.retry:
instances:
backendA:
maxAttempts: 3
waitDuration: 10s
enableExponentialBackoff: true
exponentialBackoffMultiplier: 2
retryExceptions:
- org.springframework.web.client.HttpServerErrorException
- java.io.IOException
backendB:
maxAttempts: 3
waitDuration: 10s
retryExceptions:
- org.springframework.web.client.HttpServerErrorException
- java.io.IOException
configuration properties | Defaults | describe |
---|---|---|
maxAttempts | 3 | Maximum number of retries (including the first time) |
waitDuration | 500【ms】 | wait interval between retries |
intervalFunction | numOfAttempts -> waitDuration | A function that waits for the interval after the modification fails. By default, the wait time is constant. |
retryOnResultPredicate | result->false | Configure a predicate function that determines whether the result should be retried. Predicate must return true if the result should be retried, otherwise it must return false. |
retryExceptionPredicate | throwable -> true | Similar to retryOnResultPredicate, the Predicate must return true if the retry is to be performed, and false otherwise. |
retryExceptions | null | List of exception types that need to be retried |
ignoreExceptions | null | List of exception types that do not need to be retried |
failAfterMaxAttempts | false | Boolean value to enable or disable throwing MaxRetriesExceededException when retries reach configured maxAttempts and result still does not pass retryOnResultPredicate |
intervalBiFunction | (numOfAttempts, Either<throwable, result>) -> waitDuration | A function that modifies the wait interval after failure based on maxAttempts and results or exceptions. Throws IllegalStateException when used with intervalFunction. |
2.3 Timeout TimeLimiter
Timeout configuration:
resilience4j.timelimiter:
instances:
backendA:
timeoutDuration: 2s
cancelRunningFuture: true
backendB:
timeoutDuration: 1s
cancelRunningFuture: false
The timeout configuration is relatively simple, mainly configuring timeoutDuration, which is the timeout period.
cancelRunningFuture means: should the running Future call cancel to remove the call.
2.4 circuit breaker
A circuit breaker has several states: closed, open, half-open. Note: Open, meaning no access, will fail quickly.
CircuitBreaker uses a sliding window to store and summarize call results. You can choose between count-based sliding windows and time-based sliding windows. A count-based sliding window aggregates the results of the last N calls. A time-based sliding window aggregates the last N seconds of invocation results.
Circuit breaker configuration:
resilience4j.circuitbreaker:
instances:
backendA:
// 健康指标参数,非断路器属性
registerHealthIndicator: true
slidingWindowSize: 100
configuration properties | Defaults | describe |
---|---|---|
slidingWindowSize | 100 | Sliding window size to record calls in the circuit breaker closed state (when accessible) |
failureRateThreshold | 50 (percentage) | When the failure ratio exceeds the failureRateThreshold, the circuit breaker will open and start a short circuit call |
slowCallDurationThreshold | 60000【ms】 | The threshold at which requests are defined as slow requests |
slowCallRateThreshold | 100 (percentage) | When the percentage of slow requests is greater than or equal to this value, open the circuit breaker switch |
permittedNumberOfCalls | 10 | The number of requests allowed to pass in the half-open state |
maxWaitDurationInHalfOpenState | 0 | Configure the maximum wait duration, which controls the maximum time the circuit breaker can remain half-open before switching to open. |
A value of 0 means that the circuit breaker will wait indefinitely in the HalfOpen state until all allowed calls have completed.
2.5 Bulkhead
resilience4j provides two ways to implement wall storage:
-
SemaphoreBulkhead
Implemented using Semaphore -
FixedThreadPoolBulkhead
implemented using bounded queue and fixed thread pool
resilience4j.bulkhead:
instances:
backendA:
maxConcurrentCalls: 10
backendB:
maxWaitDuration: 10ms
maxConcurrentCalls: 20
resilience4j.thread-pool-bulkhead:
instances:
backendC:
maxThreadPoolSize: 1
coreThreadPoolSize: 1
queueCapacity: 1
2.5.1 Semaphore Bulkhead
configuration properties | Defaults | describe |
---|---|---|
maxConcurrentCalls | 25 | The number of concurrent executions allowed |
maxWaitDuration | 0 | The maximum time a thread should be blocked when trying to enter a saturated partition |
2.5.2 FixedThreadPoolBulkhead
configuration properties | Defaults | describe |
---|---|---|
maxThreadPoolSize | Runtime.getRuntime().availableProcessors() | The maximum number of threads in the thread pool |
coreThreadPoolSize | Runtime.getRuntime().availableProcessors()-1 | The number of core threads in the thread pool |
queueCapacity | 100 | thread pool queue capacity |
keepAliveDuration | 20【ms】 | After the number of threads exceeds the number of core threads, the maximum time that an idle thread waits before terminating |
3 use
3.1 Configuration
Add the following resilience4j configuration in the application.yml file:
resilience4j.circuitbreaker:
instances:
backendA:
registerHealthIndicator: true
slidingWindowSize: 100
resilience4j.retry:
instances:
backendA:
maxAttempts: 3
waitDuration: 10s
enableExponentialBackoff: true
exponentialBackoffMultiplier: 2
retryExceptions:
- org.springframework.web.client.HttpServerErrorException
- java.io.IOException
backendB:
maxAttempts: 3
waitDuration: 10s
retryExceptions:
- org.springframework.web.client.HttpServerErrorException
- 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: 10ms
registerHealthIndicator: true
eventConsumerBufferSize: 100
backendB:
limitForPeriod: 6
limitRefreshPeriod: 500ms
timeoutDuration: 3s
resilience4j.timelimiter:
instances:
backendA:
timeoutDuration: 2s
cancelRunningFuture: true
backendB:
timeoutDuration: 1s
cancelRunningFuture: false
3.2 Implementation using annotations
Directly add annotations to the method that needs to limit the current @RateLimiter
to achieve current limiting; add annotations @Retry
@CircuitBreaker
@Bulkhead
retry; @Bulkhead
implement the wall bin. The name attribute is filled with the names of the current limiter, retry, fuse, and wall bin components respectively.
@Bulkhead(name = "backendA")
@CircuitBreaker(name = "backendA")
@Retry(name = "backendA")
@RateLimiter(name = "backendA")
public Mono<List<User>> list() {
long startTime = System.currentTimeMillis();
return Mono.fromSupplier(() -> {
return userRepository.findAll();
}).doOnError(e -> {
// 打印异常日志&增加监控(自行处理)
logger.error("list.user.error, e", e);
})
.doFinally(e -> {
// 耗时 & 整体健康
logger.info("list.user.time={}, ", System.currentTimeMillis() - startTime);
});
}
@Bulkhead(name = "backendA")
@CircuitBreaker(name = "backendA")//最多支持10个并发量
@Retry(name = "backendA")//使用 backendA 重试器,如果抛出 IOException 会重试三次。
@RateLimiter(name = "backendA")// 限流 10 Qps
public Mono<Boolean> save(User user) {
long startTime = System.currentTimeMillis();
return Mono.fromSupplier(() -> {
return userRepository.save(user) != null;
})
.doOnError(e -> {
// 打印异常日志&增加监控(自行处理)
logger.error("save.user.error, user={}, e", user, e);
})
.doFinally(e -> {
// 耗时 & 整体健康
logger.info("save.user.time={}, user={}", user, System.currentTimeMillis() - startTime);
});
}
Note: All the above components support customization.
The above code can be found in the following repository: https://github.com/prepared48/spring-boot-learning
This article participated in the Sifu technical essay , and you are welcome to join.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。