As Resilience4J creator Robert Winkler answered, use the decorator to combine/apply the 3 resilience-patterns on the supplier. Adjust their order as needed for execution and resiliency.
Order of decorators has effect on execution
The JavaDoc of the Decorators
builder explains the effect of build-chain order:
Decorators are applied in the order of the builder chain.
For example, consider:
Supplier<String> supplier = Decorators
.ofSupplier(() -> service.method()) // 1. called and result handled in order:
.withCircuitBreaker(CircuitBreaker.ofDefaults("id")) // 2. circuit-breaker
.withRetry(Retry.ofDefaults("id")) // 3. retry
.withFallback(CallNotPermittedException.class,
e -> service.fallbackMethod()) // 4. fallback
.decorate();
(I added comments to emphasize execution order)
This results in the following composition when executing the supplier:
Fallback(Retry(CircuitBreaker(Supplier)))
This means the Supplier is called first, then its result is handled by the CircuitBreaker, then Retry and then Fallback.
Integrating client-side RateLimiter
As recommended in Reflectoring's tutorial Implementing Rate Limiting with Resilience4j - Reflectoring:
Using RateLimiter and Retry Together
[..]
We would create RateLimiter
and Retry
objects as usual. We then decorate a rate-limited Supplier
and wrap it with a Retry
: [example]
Order suggestion
Comparing this "Retry with RateLimiter" with above JavaDoc example and answer from Robert I would suggest to choose following execution order:
- supplier, then decorate in order with resilience:
RateLimiter
(prevent a call if rate-limit exceeded)
TimeLimiter
(time-out a call)
CircuitBreaker
(fail-fast)
Retry
(retry on exceptions)
Fallback
(fallback as last resort)
A suitable reference order is for example auto-configured in the Spring-Boot extension. See the official Guides, Getting started with resilience4j-spring-boot2
about Aspect order:
The Resilience4j Aspects order is following:
Retry ( CircuitBreaker ( RateLimiter ( TimeLimiter ( Bulkhead ( Function ) ) ) ) )
so Retry is applied at the end (if needed).
See also:
withRateLimiter(rateLimiter)
go in the above example? – Assiniboine