5 Core Techniques to Build Resilient, High‑Availability APIs with Spring Boot

This article walks through building a fault‑tolerant Spring Boot 3.5.0 API using Resilience4j, covering circuit breaking, rate limiting, timeout handling, bulkhead isolation, and retry strategies, complete with configuration snippets, code examples, and execution‑order adjustments.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
5 Core Techniques to Build Resilient, High‑Availability APIs with Spring Boot

Introduction

In distributed systems and micro‑service architectures, inter‑service dependencies introduce latency, timeouts, and failures that can cascade into system‑wide outages. Protecting critical API endpoints with resilience patterns—circuit breaking, rate limiting, isolation, timeout, and retry—helps prevent resource exhaustion and ensures rapid failure recovery.

Environment

Spring Boot version 3.5.0 is used for all examples.

Practical Case

2.1 Dependency Setup

Add the following Maven dependencies (choose the appropriate Resilience4j starter for Spring Boot 2 or 3):

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
  <groupId>io.github.resilience4j</groupId>
  <artifactId>resilience4j-spring-boot3</artifactId>
  <version>2.3.0</version>
</dependency>

For Spring Boot 2, use resilience4j-spring-boot2 instead.

2.2 Circuit Breaker

The circuit breaker is implemented as a finite‑state machine with states CLOSED , OPEN , HALF‑OPEN , plus DISABLED and FORCED‑OPEN . It can use a count‑based or time‑based sliding window to record recent call outcomes.

Example code:

@GetMapping("/query")
@CircuitBreaker(name = "api.cb", fallbackMethod = "cbFallback")
public ResponseEntity<?> circuitbreaker(String name) {
    // Simulate error
    System.err.println(1 / 0);
    return ResponseEntity.ok("api ok...");
}

public ResponseEntity<?> cbFallback(String name, Exception e) {
    return ResponseEntity.ok(Map.of(
        "code", -1,
        "message", "熔断, " + e.getMessage(),
        "param", name));
}

Configuration (YAML):

resilience4j:
  ratelimiter:
    instances:
      '[api.rate]':
        # Maximum requests per refresh period
        limit-for-period: 20
        # Refresh period
        limit-refresh-period: 10s
        # Default wait time for a thread to acquire permission
        timeout-duration: 1s

Running result (image omitted for brevity).

2.3 Rate Limiter

Rate limiting protects API scalability by rejecting excess traffic, improving stability.

Example code:

@GetMapping("/query")
@CircuitBreaker(name = "api.cb", fallbackMethod = "cbFallback")
@RateLimiter(name = "api.rate", fallbackMethod = "rateFallback")
public ResponseEntity<?> circuitbreaker(String name) {
    // ...
}

public ResponseEntity<?> rateFallback(String name, Exception e) {
    return ResponseEntity.ok(Map.of(
        "code", -1,
        "message", "限流, " + e.getMessage(),
        "param", name));
}

Configuration (YAML):

resilience4j:
  ratelimiter:
    instances:
      '[api.rate]':
        limit-for-period: 2
        limit-refresh-period: 10s
        timeout-duration: 1s

Running result (image omitted).

2.4 Timeout

Timeout protection prevents long‑running downstream calls from blocking threads and exhausting resources. Setting an appropriate timeout enables fast failure.

Example code (asynchronous return type required):

// Return type must be asynchronous, e.g., CompletableFuture
@GetMapping("/query")
@TimeLimiter(name = "api.timeout", fallbackMethod = "timeoutFallback")
public CompletableFuture<?> circuitbreaker(String name) throws Exception {
    return CompletableFuture.supplyAsync(() -> {
        // Simulate delay
        try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException ignored) {}
        return "api ok";
    });
}

public CompletableFuture<?> timeoutFallback(String name, Exception e) {
    return CompletableFuture.supplyAsync(() ->
        Map.of("code", -1, "message", "超时, " + e.getMessage(), "param", name));
}

Configuration (YAML):

resilience4j:
  timelimiter:
    instances:
      '[api.timeout]':
        timeout-duration: 1s

Running result (image omitted).

2.5 Isolation (Bulkhead)

Resilience4j offers two bulkhead implementations:

SemaphoreBulkhead – uses a semaphore and works with any thread or I/O model.

FixedThreadPoolBulkhead – uses a bounded queue and a fixed‑size thread pool.

Example code (semaphore bulkhead):

@GetMapping("/query")
@Bulkhead(name = "api.bulk", fallbackMethod = "bulkFallback", type = Type.SEMAPHORE)
public CompletableFuture<?> query(String name) throws Exception {
    System.err.printf("%s - 执行任务%n", Thread.currentThread().getName());
    TimeUnit.SECONDS.sleep(5);
    return CompletableFuture.completedFuture("api ok");
}

public CompletableFuture<?> bulkFallback(String name, Exception e) {
    return CompletableFuture.supplyAsync(() ->
        Map.of("code", -1, "message", "隔离, " + e.getMessage(), "param", name));
}

Configuration (YAML):

resilience4j:
  bulkhead:
    instances:
      '[api.bulk]':
        max-concurrent-calls: 1
        max-wait-duration: 1s

Running result (images omitted).

2.6 Retry

Retry automatically re‑issues failed requests after transient faults such as network glitches or timeouts. Properly tuning attempts and intervals avoids cascading failures.

Example code:

@GetMapping("/query")
@Retry(name = "api.retry", fallbackMethod = "retryFallback")
public ResponseEntity<?> query(String name) throws Exception {
    System.err.printf("%s - 执行任务%n", Thread.currentThread().getName());
    System.err.println(1 / 0);
    return ResponseEntity.ok("api ok");
}

public ResponseEntity<?> retryFallback(String name, Exception e) {
    return ResponseEntity.ok(Map.of(
        "code", -1,
        "message", "重试, " + e.getMessage(),
        "param", name));
}

Configuration (YAML):

resilience4j:
  retry:
    instances:
      '[api.retry]':
        max-attempts: 3
        wait-duration: 2s
        retry-exceptions:
          - java.lang.ArithmeticException

Running result (images omitted).

2.7 Summary of the Five Features

All five resilience features are implemented as Spring AOP aspects. Their default execution order is shown in the diagram (image omitted). To adjust the order, modify the aspect order properties:

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

Adjusting these values changes which aspect runs first when a request hits the endpoint.

Conclusion

By integrating Resilience4j with Spring Boot 3, developers can protect API endpoints against failures, overload, and latency, achieving a more robust and highly available micro‑service architecture.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Spring BootRetryTimeoutCircuit BreakerBulkheadRate LimiterResilience4j
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.