断路器与舱壁隔离
使用 Resilience4j 断路器、舱壁和回退方法保护下游调用。
断路器与舱壁隔离 是 CoddyKit 上的免费 Spring Boot 4 Complete Guide 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Spring Boot 4 Complete Guide 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Spring Boot 4 Complete Guide 课程共包含 4 节课。
本课时的部分内容尚未翻译,以英文显示。
Why Resilience Matters
In a distributed system, a single slow or failing downstream service can cascade into a total outage. If your service keeps calling a dead dependency, threads pile up waiting on timeouts until the whole application becomes unresponsive.
Resilience engineering is about containing failure. Two foundational patterns:
- Circuit Breaker — stop calling a failing dependency for a while, fail fast instead of waiting.
- Bulkhead — isolate resources so one saturated dependency cannot exhaust the threads or connections needed by the rest.
In Spring Boot 4 we implement these with Resilience4j, a lightweight, functional fault-tolerance library that replaced the now end-of-life Hystrix.
Adding Resilience4j
Spring Boot 4 integrates Resilience4j through the resilience4j-spring-boot3 starter (compatible with Boot 3.x/4.x) plus Spring AOP. The annotations are activated by an aspect that wraps your method calls.
You also pull in spring-boot-starter-aop and, for metrics, spring-boot-starter-actuator with Micrometer.
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-spring-boot3</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>How a Circuit Breaker Works
A circuit breaker is a state machine that monitors the failure rate of calls:
- CLOSED — calls flow through normally. Failures are recorded in a sliding window.
- OPEN — once the failure rate crosses a threshold, the breaker trips. Further calls fail immediately (no downstream call) for a wait duration.
- HALF_OPEN — after the wait, a limited number of trial calls are permitted. If they succeed, it returns to CLOSED; if they fail, it returns to OPEN.
The key benefit: when a dependency is down, you fail fast instead of blocking threads on doomed calls, and you give the dependency room to recover.
Annotating a Protected Call
The @CircuitBreaker annotation wraps a method. The name ties it to a configuration instance, and fallbackMethod names a method to invoke when the call fails or the breaker is open.
The fallback must have the same signature plus a trailing Throwable (or a more specific exception) parameter.
@Service
public class PaymentClient {
private final RestClient restClient;
public PaymentClient(RestClient restClient) {
this.restClient = restClient;
}
@CircuitBreaker(name = "paymentService", fallbackMethod = "fallbackCharge")
public ChargeResult charge(ChargeRequest request) {
return restClient.post()
.uri("/charges")
.body(request)
.retrieve()
.body(ChargeResult.class);
}
private ChargeResult fallbackCharge(ChargeRequest request, Throwable t) {
return ChargeResult.deferred(request.id(), "payment temporarily unavailable");
}
}Configuring the Breaker
Circuit breaker behavior is tuned in application.yml. You define default settings and per-instances overrides keyed by the name you used in the annotation.
sliding-window-type— COUNT_BASED or TIME_BASED.failure-rate-threshold— percent of failures to open the breaker.wait-duration-in-open-state— how long to stay OPEN before HALF_OPEN.permitted-number-of-calls-in-half-open-state— trial calls allowed.slow-call-duration-threshold/slow-call-rate-threshold— treat slow calls as failures.
resilience4j:
circuitbreaker:
configs:
default:
sliding-window-type: COUNT_BASED
sliding-window-size: 20
failure-rate-threshold: 50
slow-call-duration-threshold: 2s
slow-call-rate-threshold: 80
wait-duration-in-open-state: 10s
permitted-number-of-calls-in-half-open-state: 5
automatic-transition-from-open-to-half-open-enabled: true
instances:
paymentService:
base-config: default
failure-rate-threshold: 40Modeling the State Machine in Plain Java
To internalize the logic, here is a stripped-down, framework-free model of the CLOSED to OPEN transition using a count-based window. This is conceptually what Resilience4j does for you behind the annotation.
Run it to see the breaker trip once the failure rate crosses the threshold.
public class MiniBreaker {
enum State { CLOSED, OPEN }
static State state = State.CLOSED;
static int window = 10, threshold = 50;
static boolean[] results = new boolean[window];
static int idx = 0, count = 0;
static void record(boolean failure) {
results[idx] = failure;
idx = (idx + 1) % window;
if (count < window) count++;
int fails = 0;
for (int i = 0; i < count; i++) if (results[i]) fails++;
int rate = count == 0 ? 0 : (fails * 100 / count);
if (count == window && rate >= threshold) state = State.OPEN;
}
public static void main(String[] args) {
boolean[] calls = {false,true,false,true,true,false,true,true,false,true};
for (boolean fail : calls) {
record(fail);
System.out.println((fail ? "FAIL" : "OK ") + " -> state=" + state);
}
}
}Counting Records vs Ignoring Exceptions
Not every exception should trip the breaker. A 400 Bad Request means your input was wrong, not that the dependency is unhealthy — counting it as a failure would open the breaker for valid traffic.
Use record-exceptions to list throwables that count as failures, and ignore-exceptions for those that should pass through without affecting breaker state.
resilience4j:
circuitbreaker:
instances:
paymentService:
base-config: default
record-exceptions:
- java.io.IOException
- java.util.concurrent.TimeoutException
- org.springframework.web.client.HttpServerErrorException
ignore-exceptions:
- com.example.payments.InvalidCardException
- org.springframework.web.client.HttpClientErrorException$BadRequestBulkhead Isolation
The bulkhead pattern (named after a ship's watertight compartments) limits how many concurrent calls a dependency can consume, so one slow service cannot drain the entire thread pool.
Resilience4j offers two flavors:
- SemaphoreBulkhead — caps concurrent calls on the caller's thread. Lightweight, no extra threads.
- ThreadPoolBulkhead — runs calls on a dedicated, bounded thread pool with a queue. Provides true isolation and only works with asynchronous returns (
CompletableFuture).
If the bulkhead is full, the call is rejected with BulkheadFullException, which your fallback can handle.
Applying a Bulkhead
The @Bulkhead annotation limits concurrency. With type = THREADPOOL the method must return a CompletableFuture and runs on the bulkhead's own pool. You can stack it with @CircuitBreaker — the annotations compose.
@Service
public class InventoryClient {
private final RestClient restClient;
public InventoryClient(RestClient restClient) {
this.restClient = restClient;
}
@CircuitBreaker(name = "inventory", fallbackMethod = "fallbackStock")
@Bulkhead(name = "inventory", type = Bulkhead.Type.THREADPOOL)
public CompletableFuture<StockLevel> getStock(String sku) {
return CompletableFuture.completedFuture(
restClient.get().uri("/stock/{sku}", sku)
.retrieve().body(StockLevel.class));
}
private CompletableFuture<StockLevel> fallbackStock(String sku, Throwable t) {
return CompletableFuture.completedFuture(StockLevel.unknown(sku));
}
}Configuring Bulkheads
Semaphore and thread-pool bulkheads have separate config sections.
bulkhead(semaphore):max-concurrent-callsandmax-wait-duration(how long a call waits for a permit before being rejected).thread-pool-bulkhead:max-thread-pool-size,core-thread-pool-size, andqueue-capacity.
Right-size these to the dependency's real capacity: a bulkhead larger than the downstream can handle defeats the purpose.
resilience4j:
bulkhead:
instances:
paymentService:
max-concurrent-calls: 25
max-wait-duration: 50ms
thread-pool-bulkhead:
instances:
inventory:
core-thread-pool-size: 8
max-thread-pool-size: 16
queue-capacity: 20Ordering, Fallbacks, and Observability
When multiple Resilience4j annotations decorate one method, they apply in a fixed aspect order (highest precedence first): Bulkhead → TimeLimiter → RateLimiter → CircuitBreaker → Retry. So Retry wraps the circuit breaker — a retried call that still fails counts toward the breaker.
Fallback design rules:
- Keep fallbacks fast and side-effect-free; never call the failing dependency again.
- Return degraded-but-valid data (cached value, default, queued-for-later).
- Inspect the
Throwableto distinguishCallNotPermittedException(breaker open) fromBulkheadFullException(overloaded).
Actuator exposes state and metrics at /actuator/circuitbreakers and via Micrometer (resilience4j_circuitbreaker_state), so you can alert on OPEN breakers.
private ChargeResult fallbackCharge(ChargeRequest request, CallNotPermittedException ex) {
return ChargeResult.deferred(request.id(), "breaker open");
}
private ChargeResult fallbackCharge(ChargeRequest request, BulkheadFullException ex) {
return ChargeResult.rejected(request.id(), "system busy");
}
private ChargeResult fallbackCharge(ChargeRequest request, Throwable t) {
return ChargeResult.deferred(request.id(), "payment unavailable");
}Quick Check
Test your understanding of bulkhead isolation.
Recap
You learned how to protect downstream calls in Spring Boot 4 with Resilience4j:
- Circuit breakers fail fast via the CLOSED → OPEN → HALF_OPEN state machine, tuned with sliding-window size, failure-rate and slow-call thresholds, and wait duration.
- Use
record-exceptions/ignore-exceptionsso client errors (4xx) don't trip the breaker. - Bulkheads cap concurrency — SemaphoreBulkhead on the caller thread, ThreadPoolBulkhead for true isolation with
CompletableFuture. - Fallback methods share the signature plus a trailing
Throwable; return degraded-but-valid data and never re-call the failing dependency. - Annotation order is Bulkhead → TimeLimiter → RateLimiter → CircuitBreaker → Retry; observe state through Actuator and Micrometer.
Together these patterns keep one failing dependency from taking down your whole service.
常见问题解答
「断路器与舱壁隔离」课时是免费的吗?
是的 — 「断路器与舱壁隔离」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Spring Boot 4 Complete Guide 课程的其余内容,请升级到 CoddyKit PRO。 Spring Boot 4 Complete Guide 课程共包含 4 节课。
「断路器与舱壁隔离」这节课中我会学到什么?
使用 Resilience4j 断路器、舱壁和回退方法保护下游调用。 你通过在浏览器中直接运行的动手代码来练习 Spring Boot 4 Complete Guide,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Spring Boot 4 Complete Guide 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Spring Boot 4 Complete Guide 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。
「断路器与舱壁隔离」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Spring Boot 4 Complete Guide 课中编写并运行代码吗?
能。每节 Spring Boot 4 Complete Guide 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。