๐ก️ Resilience Patterns in Distributed Systems
Welcome back to The Code Hut Distributed Systems series! In this post, we’ll explore patterns that help distributed systems remain resilient under failure conditions. ๐ช⚡
๐ Why Resilience Patterns Matter
In distributed systems, failures are inevitable. Resilience patterns help:
- ❌ Prevent cascading failures
- ⚡ Maintain availability
- ๐ Improve user experience during partial outages
1. ๐ Circuit Breaker
A circuit breaker stops requests to a failing service to prevent overwhelming it and allows the system to recover gracefully. Circuit breaker states:
- Closed: Requests flow normally.
- Open: Requests fail immediately; fallback triggers.
- Half-Open: A few trial requests check if the service has recovered.
Example with Resilience4j:
// Using Resilience4j circuit breaker
CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("service");
Supplier decorated = CircuitBreaker
.decorateSupplier(circuitBreaker, () -> remoteService.call());
String result = Try.ofSupplier(decorated)
.recover(throwable -> "fallback response").get();
2. ๐ก Fallback Strategies
When a service fails, fallback strategies ensure users still get meaningful responses:
- Return a default value or cached response.
- Call an alternative service.
- Return a friendly error message.
3. ๐ Retry with Exponential Backoff
Retries can help recover from transient errors but must be combined with backoff to avoid overwhelming the service:
// Retry with Resilience4j and exponential backoff
RetryConfig config = RetryConfig.custom()
.maxAttempts(5)
.waitDuration(Duration.ofSeconds(2))
.build();
Retry retry = Retry.of("serviceRetry", config);
Supplier decorated = Retry.decorateSupplier(retry, () -> remoteService.call());
String result = Try.ofSupplier(decorated)
.recover(throwable -> "fallback response").get();
4. ⏲ Timeouts
Prevent a slow service from blocking requests indefinitely:
// Timeout using Resilience4j
TimeLimiter timeLimiter = TimeLimiter.of(Duration.ofSeconds(3));
Supplier> futureSupplier =
() -> CompletableFuture.supplyAsync(() -> remoteService.call());
Supplier> decorated = TimeLimiter.decorateFutureSupplier(timeLimiter, futureSupplier);
String result = Try.ofSupplier(() -> decorated.get().get())
.recover(throwable -> "timeout fallback").get();
5. ๐งฑ Bulkhead
Bulkheads isolate resources to prevent one failing part from affecting others. Think of it as compartmentalizing ship sections to avoid sinking completely.
// Using Resilience4j bulkhead
Bulkhead bulkhead = Bulkhead.ofDefaults("serviceBulkhead");
Supplier decorated = Bulkhead
.decorateSupplier(bulkhead, () -> remoteService.call());
String result = Try.ofSupplier(decorated)
.recover(throwable -> "fallback response").get();
Next in the Series
In the next post, we’ll explore Service Discovery & Load Balancing ๐⚖️, including registry-based and client-side strategies.
Label for this post: Distributed Systems, Resilience Patterns
Comments
Post a Comment