设置

向编译依赖项中添加 resilient 4j 的 Spring Boot 2 Starter。

该模块期望 org.springframework.boot:spring-boot-starter-actuatororg.springframework.boot:spring-boot-starter-aop 已经在运行时提供。 如果你在 spring boot2 中使用 webflux,你还需要 io.github.resilience4j:resilience4j-reactor

Groovy

repositories {
    jCenter()
}

dependencies {
  compile "io.github.resilience4j:resilience4j-spring-boot2:${resilience4jVersion}"
  compile('org.springframework.boot:spring-boot-starter-actuator')
  compile('org.springframework.boot:spring-boot-starter-aop')
}

示例

Spring Boot 2 中的设置和使用在 demo 中进行了演示.

配置

您可以在 Spring Boot 的 application.yml 配置文件中配置 CircuitBreaker、Retry、RateLimiter、Bulkhead、Thread pool bulkhead 和 TimeLimiter 实例。
例如:

YAML

resilience4j.circuitbreaker:
    instances:
        backendA:
            registerHealthIndicator: true
            slidingWindowSize: 100
        backendB:
            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:
            maxAttempts: 3
            waitDuration: 10s
            enableExponentialBackoff: true
            exponentialBackoffMultiplier: 2
            retryExceptions:
                - org.springframework.web.client.HttpServerErrorException
                - java.io.IOException
            ignoreExceptions:
                - io.github.robwin.exception.BusinessException
        backendB:
            maxAttempts: 3
            waitDuration: 10s
            retryExceptions:
                - org.springframework.web.client.HttpServerErrorException
                - java.io.IOException
            ignoreExceptions:
                - io.github.robwin.exception.BusinessException
                
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

您还可以覆盖默认配置,定义共享配置并在 Spring Boot 的 application.yml 配置文件中覆盖它们。
例如:

YAML

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

您还可以使用定制器为特定实例名称覆盖特定 CircuitBreaker、Bulkhead、Retry、RateLimiter 或 TimeLimiter 实例的配置。 下面显示了如何在 YAML 文件中覆盖上述配置的 CircuitBreaker backendA 的示例:

Java

@Bean
public CircuitBreakerConfigCustomizer testCustomizer() {
    return CircuitBreakerConfigCustomizer
        .of("backendA", builder -> builder.slidingWindowSize(100));
}

Resilience4j 有自己的定制器类型,可以如上所示使用:

Resilienc4j 类型

实例定制器类

Circuit breaker

CircuitBreakerConfigCustomizer

Retry

RetryConfigCustomizer

Rate limiter

RateLimiterConfigCustomizer

Bulkhead

BulkheadConfigCustomizer

ThreadPoolBulkhead

ThreadPoolBulkheadConfigCustomizer

Time Limiter

TimeLimiterConfigCustomizer

注解

Spring Boot2启动器提供了自动配置的注解和AOP aspect。
RateLimiter、Retry、CircuitBreaker 和 Bulkhead 注释支持同步返回类型和异步类型,如 CompletableFuture 和反应类型,如 Spring Reactor 的 Flux 和 Mono(如果你导入了适当的包,如 resilience4j-reactor)。

Bulkhead 注释有一个 type 属性来定义将使用哪个 Bulkhead 实现。 默认情况下它是信号量,但您可以通过在注解中设置 type 属性来切换到线程池:

Java

@Bulkhead(name = BACKEND, type = Bulkhead.Type.THREADPOOL)
public CompletableFuture<String> doSomethingAsync() throws InterruptedException {
        Thread.sleep(500);
        return CompletableFuture.completedFuture("Test");
 }

回退的方法

回退方法机制的工作原理类似于 try/catch 块。如果配置了一个回退方法,那么每个执行都会转发到一个回退方法执行器。回退方法执行程序正在搜索能够处理异常的最佳匹配的回退方法。类似于 catch 块。

举个例子:

Java

@CircuitBreaker(name = BACKEND, fallbackMethod = "fallback")
@RateLimiter(name = BACKEND)
@Bulkhead(name = BACKEND, fallbackMethod = "fallback")
@Retry(name = BACKEND)
@TimeLimiter(name = BACKEND)
public Mono<String> method(String param1) {
    return Mono.error(new NumberFormatException());
}

private Mono<String> fallback(String param1, CallNotPermittedException e) {
    return Mono.just("Handled the exception when the CircuitBreaker is open");
}

private Mono<String> fallback(String param1, BulkheadFullException e) {
    return Mono.just("Handled the exception when the Bulkhead is full");
}

private Mono<String> fallback(String param1, NumberFormatException e) {
    return Mono.just("Handled the NumberFormatException");
}

private Mono<String> fallback(String param1, Exception e) {
    return Mono.just("Handled any other exception");
}

重要的是要记住,回退方法应该放在同一个类中,并且必须具有相同的方法签名,只有一个额外的目标异常参数。

如果有多个 fallbackMethod 方法,将调用最接近匹配的方法,例如:

如果您尝试从NumberFormatException中恢复,将调用带有签名 String fallback(String parameter, NumberFormatException exception)} 的方法。

仅当多个方法具有相同的返回类型并且您想一劳永逸地为它们定义相同的回退方法时,您才可以定义一个带有异常参数的全局回退方法。

Aspect 顺序

Resilience4j Aspects 顺序如下:
Retry ( CircuitBreaker ( RateLimiter ( TimeLimiter ( Bulkhead ( Function ) ) ) ) ) 所以在最后应用 Retry(如果需要)。
如果你需要不同的顺序,你必须使用函数链接样式而不是Spring注释样式,或者使用以下属性显式设置aspect 顺序:

Text

- resilience4j.retry.retryAspectOrder
- resilience4j.circuitbreaker.circuitBreakerAspectOrder
- resilience4j.ratelimiter.rateLimiterAspectOrder
- resilience4j.timelimiter.timeLimiterAspectOrder
- resilience4j.bulkhead.bulkheadAspectOrder

例如 - 要使断路器在重试完成后启动,您必须将 retryAspectOrder 属性设置为大于 circuitBreakerAspectOrder 值的值(更高的值 = 更高的优先级)。

YAML

resilience4j:
  circuitbreaker:
    circuitBreakerAspectOrder: 1
  retry:
    retryAspectOrder: 2

指标端点(Metrics endpoint)

CircuitBreaker、Retry、RateLimiter、Bulkhead 和 TimeLimiter 指标会自动发布在指标端点上。 要检索可用指标的名称,请向/actuator/metrics发出 GET 请求。 请参阅 Actuator Metrics 文档 了解更多详情。

JSON

{
    "names": [
        "resilience4j.circuitbreaker.calls",
        "resilience4j.circuitbreaker.buffered.calls",
        "resilience4j.circuitbreaker.state",
        "resilience4j.circuitbreaker.failure.rate"
        ]
}

要检索指标,请向/actuator/metrics/{metric.name}发出 GET 请求。
例如: /actuator/metrics/resilience4j.circuitbreaker.calls

JSON

{
    "name": "resilience4j.circuitbreaker.calls",
    "measurements": [
        {
            "statistic": "VALUE",
            "value": 3
        }
    ],
    "availableTags": [
        {
            "tag": "kind",
            "values": [
                "not_permitted",
                "successful",
                "failed"
            ]
        },
        {
            "tag": "name",
            "values": [
                "backendB",
                "backendA"
            ]
        }
    ]
}

当您想在 Prometheus 端点上发布 CircuitBreaker 端点时,您必须添加依赖项 io.micrometer:micrometer-registry-prometheus

要检索指标,请向/actuator/prometheus发出 GET 请求。 更多详细信息请参阅 Micrometer 入门

健康端点(Health endpoint)

Spring Boot Actuator 健康信息可用于检查正在运行的应用程序的状态。 如果生产系统出现严重问题,监控软件通常使用它来提醒某人。
默认情况下,CircuitBreaker 或 RateLimiter 健康指标是禁用的,但您可以通过配置启用它们。 运行状况指标被禁用,因为当断路器处于打开(OPEN)状态时应用程序状态为关闭(DOWN)。 这可能不是你想要的结果。

YAML

management.health.circuitbreakers.enabled: true
management.health.ratelimiters.enabled: true

resilience4j.circuitbreaker:
  configs:
    default:
      registerHealthIndicator: true


resilience4j.ratelimiter:
  configs:
    instances:
      registerHealthIndicator: true

CircuitBreaker 闭合状态映射为 UP,打开状态映射为 DOWN,半打开状态映射为 UNKNOWN

例如:

JSON

{
  "status": "UP",
  "details": {
    "circuitBreakers": {
      "status": "UP",
      "details": {
        "backendB": {
          "status": "UP",
          "details": {
            "failureRate": "-1.0%",
            "failureRateThreshold": "50.0%",
            "slowCallRate": "-1.0%",
            "slowCallRateThreshold": "100.0%",
            "bufferedCalls": 0,
            "slowCalls": 0,
            "slowFailedCalls": 0,
            "failedCalls": 0,
            "notPermittedCalls": 0,
            "state": "CLOSED"
          }
        },
        "backendA": {
          "status": "UP",
          "details": {
            "failureRate": "-1.0%",
            "failureRateThreshold": "50.0%",
            "slowCallRate": "-1.0%",
            "slowCallRateThreshold": "100.0%",
            "bufferedCalls": 0,
            "slowCalls": 0,
            "slowFailedCalls": 0,
            "failedCalls": 0,
            "notPermittedCalls": 0,
            "state": "CLOSED"
          }
        }
      }
    }
  }
}

事件端点(Events endpoint)

发出的 CircuitBreaker、Retry、RateLimiter、Bulkhead 和 TimeLimiter 事件存储在单独的循环事件消费者缓冲区中。 事件消费者缓冲区的大小可以在 application.yml 文件 (eventConsumerBufferSize) 中配置。

端点/actuator/circuitbreakers列出了所有 CircuitBreaker 实例的名称。 该端点也可用于 Retry、RateLimiter、Bulkhead 和 TimeLimiter。
例如:

JSON

{
    "circuitBreakers": [
      "backendA",
      "backendB"
    ]
}

默认情况下,端点/actuator/circuitbreakerevents列出所有 CircuitBreaker 实例的最新 100 个发出的事件。 该端点也可用于 Retry、RateLimiter、Bulkhead 和 TimeLimiter。

JSON

{
    "circuitBreakerEvents": [
        {
            "circuitBreakerName": "backendA",
            "type": "ERROR",
            "creationTime": "2017-01-10T15:39:17.117+01:00[Europe/Berlin]",
            "errorMessage": "org.springframework.web.client.HttpServerErrorException: 500 This is a remote exception",
            "durationInMs": 0
        },
        {
            "circuitBreakerName": "backendA",
            "type": "SUCCESS",
            "creationTime": "2017-01-10T15:39:20.518+01:00[Europe/Berlin]",
            "durationInMs": 0
        },
        {
            "circuitBreakerName": "backendB",
            "type": "ERROR",
            "creationTime": "2017-01-10T15:41:31.159+01:00[Europe/Berlin]",
            "errorMessage": "org.springframework.web.client.HttpServerErrorException: 500 This is a remote exception",
            "durationInMs": 0
        },
        {
            "circuitBreakerName": "backendB",
            "type": "SUCCESS",
            "creationTime": "2017-01-10T15:41:33.526+01:00[Europe/Berlin]",
            "durationInMs": 0
        }
    ]
}