قواطع الدائرة وعزل الحواجز
احمِ الاستدعاءات إلى الخدمات التابعة باستخدام قواطع دائرة Resilience4j والحواجز وطرائق الاسترداد
قواطع الدائرة وعزل الحواجز درس مجاني في Spring Boot 4 Complete Guide على CoddyKit. هذا هو الدرس 2 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في 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.
الأسئلة الشائعة
هل درس «قواطع الدائرة وعزل الحواجز» مجاني؟
نعم — نص درس «قواطع الدائرة وعزل الحواجز» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Spring Boot 4 Complete Guide، انتقل إلى CoddyKit PRO. تتضمن دورة Spring Boot 4 Complete Guide 4 دروس في المجموع.
ماذا ستتعلم في «قواطع الدائرة وعزل الحواجز»؟
احمِ الاستدعاءات إلى الخدمات التابعة باستخدام قواطع دائرة Resilience4j والحواجز وطرائق الاسترداد تتمرن على Spring Boot 4 Complete Guide مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ Spring Boot 4 Complete Guide؟
لا تُشترط خبرة سابقة. Spring Boot 4 Complete Guide على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 2 من أصل 4.
كم من الوقت يستغرق درس «قواطع الدائرة وعزل الحواجز»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس Spring Boot 4 Complete Guide هذا؟
نعم. كل درس في Spring Boot 4 Complete Guide يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- نشر السياق وتجهيز الامتدادات
- قواطع الدائرة وعزل الحواجز
- تحديد المعدل وإعادة المحاولة ومحددات المهلة
- ربط السجلات بالمقاييس وآثار التتبّع