Spring Boot 微服务的完整弹性指南 — 使用所有 Resilience4j 注解

发布: (2025年12月2日 GMT+8 17:44)
4 min read
原文: Dev.to

Source: Dev.to

假设

您正在使用 Spring Boot 并集成 resilience4j-spring-boot2/resilience4j-spring-boot3(Resilience4j 1.x/2.x 的使用方式类似)。示例代码为普通的 Java + Spring(非响应式)。

Maven (pom.xml)

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>io.github.resilience4j</groupId>
        <artifactId>resilience4j-spring-boot3</artifactId>
        <version>1.7.1</version>
    </dependency>

    <dependency>
        <groupId>io.github.resilience4j</groupId>
        <artifactId>resilience4j-all</artifactId>
        <version>1.7.1</version>
    </dependency>

    <dependency>
        <groupId>io.micrometer</groupId>
        <artifactId>micrometer-registry-prometheus</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>

    <dependency>
        <groupId>com.github.tomakehurst</groupId>
        <artifactId>wiremock-jre8</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

Gradle (Kotlin DSL)

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-web")
    implementation("io.github.resilience4j:resilience4j-spring-boot3:1.7.1")
    implementation("io.github.resilience4j:resilience4j-all:1.7.1")
    implementation("io.micrometer:micrometer-registry-prometheus")
    implementation("org.springframework.boot:spring-boot-starter-actuator")
    testImplementation("org.springframework.boot:spring-boot-starter-test")
    testImplementation("com.github.tomakehurst:wiremock-jre8:2.27.2")
}

选择与您的 Spring Boot 版本兼容的依赖版本。

application.yml – 配置示例

resilience4j:
  circuitbreaker:
    configs:
      default:
        registerHealthIndicator: true
        slidingWindowType: COUNT_BASED
        slidingWindowSize: 20
        minimumNumberOfCalls: 10
        permittedNumberOfCallsInHalfOpenState: 5
        waitDurationInOpenState: 30s
        failureRateThreshold: 50
        automaticTransitionFromOpenToHalfOpenEnabled: false
    instances:
      externalServiceCB:
        baseConfig: default
        waitDurationInOpenState: 10s
        failureRateThreshold: 40

  retry:
    instances:
      externalServiceRetry:
        maxAttempts: 3
        waitDuration: 500ms
        retryExceptions:
          - java.io.IOException
          - java.util.concurrent.TimeoutException

  timelimiter:
    instances:
      externalServiceTL:
        timeoutDuration: 2s
        cancelRunningFuture: true

  bulkhead:
    configs:
      default:
        maxConcurrentCalls: 10
        maxWaitDuration: 0ms    # for semaphore bulkhead
      threadpool-default:
        maxThreadPoolSize: 10
        coreThreadPoolSize: 5
        queueCapacity: 50
        keepAliveDuration: 30s
    instances:
      semaphoreBulkhead:
        baseConfig: default
        maxConcurrentCalls: 20
      threadPoolBulkhead:
        baseConfig: threadpool-default

  ratelimiter:
    instances:
      externalServiceRateLimiter:
        limitForPeriod: 10
        limitRefreshPeriod: 1s
        timeoutDuration: 0

management:
  endpoints:
    web:
      exposure:
        include: health,info,prometheus
  endpoint:
    health:
      show-details: always

示例组件

ExternalClient.java – 轻量 HTTP 客户端(使用 RestTemplate

@Service
public class ExternalClient {

    private final RestTemplate restTemplate;

    public ExternalClient(RestTemplateBuilder builder) {
        this.restTemplate = builder
            .setReadTimeout(Duration.ofSeconds(5))
            .setConnectTimeout(Duration.ofSeconds(2))
            .build();
    }

    public String getRemoteData(String id) {
        String url = "https://external.service/api/resource/" + id;
        return restTemplate.getForObject(url, String.class);
    }
}

ResilientService.java – 应用 Resilience4j 注解

@Service
public class ResilientService {

    private final ExternalClient externalClient;

    public ResilientService(ExternalClient externalClient) {
        this.externalClient = externalClient;
    }

    // RateLimiter → Semaphore Bulkhead → Retry → CircuitBreaker
    @RateLimiter(name = "externalServiceRateLimiter", fallbackMethod = "rateLimiterFallback")
    @Bulkhead(name = "semaphoreBulkhead", type = Bulkhead.Type.SEMAPHORE, fallbackMethod = "bulkheadFallback")
    @Retry(name = "externalServiceRetry", fallbackMethod = "retryFallback")
    @CircuitBreaker(name = "externalServiceCB", fallbackMethod = "circuitFallback")
    public String getData(String id) {
        return externalClient.getRemoteData(id);
    }

    // --- Fallback methods (signatures must match) ---
    public String circuitFallback(String id, Throwable t) {
        return "circuit-fallback: cached-or-default";
    }

    public String retryFallback(String id, Throwable t) {
        return "retry-fallback: sorry";
    }

    public String bulkheadFallback(String id, BulkheadFullException ex) {
        return "bulkhead-fallback: overloaded";
    }

    public String rateLimiterFallback(String id, RequestNotPermitted ex) {
        return "rate-limited-fallback: try-later";
    }
}

AsyncResilientService.java – 异步模式(TimeLimiter + 线程池 Bulkhead)

@Service
public class AsyncResilientService {

    private final ExternalClient externalClient;
    private final ExecutorService executor = Executors.newFixedThreadPool(10);

    public AsyncResilientService(ExternalClient externalClient) {
        this.externalClient = externalClient;
    }

    // TimeLimiter expects a CompletableFuture (async)
    @Bulkhead(name = "threadPoolBulkhead", type = Bulkhead.Type.THREADPOOL, fallbackMethod = "tpbFallback")
    @TimeLimiter(name = "externalServiceTL", fallbackMethod = "tlFallback")
    public CompletableFuture<String> getDataAsync(String id) {
        return CompletableFuture.supplyAsync(() -> externalClient.getRemoteData(id), executor);
    }

    public CompletableFuture<String> tpbFallback(String id, BulkheadFullException ex) {
        return CompletableFuture.completedFuture("threadpool-bulkhead-fallback");
    }

    public CompletableFuture<String> tlFallback(String id, TimeoutException ex) {
        return CompletableFuture.completedFuture("time-limiter-fallback");
    }
}

组合注解 – 常见顺序

一种常见的模式是:

RateLimiter → Bulkhead → CircuitBreaker → TimeLimiter → Retry
  • RateLimiter 用于防止下游服务被请求压垮。
  • Bulkhead(信号量或线程池)限制您进程内部的并发使用。
  • CircuitBreaker 快速阻止对出现故障的服务的调用。
  • TimeLimiter 限制异步调用的延迟。
  • Retry 重新执行瞬时失败(通常放在 CircuitBreaker 内部,以免放大负载)。

根据业务语义调整顺序;例如,当您希望每一次尝试都受到相同保护时,可以把 Retry 放在 CircuitBreaker 外部

Fallback 方法要求

  • Fallback 方法名必须与 fallbackMethod 属性完全一致。
  • 返回类型必须与被保护方法的返回类型相同。
  • 参数:原方法的所有参数 加上 一个可选的最后 Throwable/Exception(或特定异常类型,如 BulkheadFullException)。

这些规则确保在发生弹性事件时 Spring 能够正确地路由到对应的 fallback。

Back to Blog

相关文章

阅读更多 »

切换账户

@blink_c5eb0afe3975https://dev.to/blink_c5eb0afe3975 正如大家所知,我正重新开始记录我的进展,我认为最好在一个不同的…

Strands 代理 + Agent Core AWS

入门指南:Amazon Bedrock AgentCore 目录 - 前置要求(requisitos‑previos) - 工具包安装(instalación‑del‑toolkit) - 创建…