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.

程序员伍六七
201 声望597 粉丝