good news is from the spring framework itself, we have these resilience capabilities.
As of 7.0, the core Spring Framework includes a couple of common resilience features, in particular @Retryable and @ConcurrencyLimit annotations for method invocations.
A mechanism that stops calling a failing service after threshold failures, until halfOpenAfter milliseconds elapsed. A successful call resets the failure counter.
int threshold = 5; // configurable long halfOpenAfter = 5000; // configurable
AtomicInteger failureCount = new AtomicInteger(); volatilelong lastFailureTime;
// just an example, in reality, aop with annotation is more often to use. voidcallThirdParty(){ if (failureCount.get() > this.threshold && System.currentTimeMillis() - lastFailureTime < this.halfOpenAfter) { thrownew CircuitBreakerOpenException("Circuit Breaker is open for method: callThirdParty()") } // call thrid party try { // call... // if success, reset the failure counter if (failureCount.get() > 0) { log.debug("Closing Circuit Breaker for method: callThirdParty()"); failureCount.set(0); lastFailure = 0; }
A mechanism that protects its own service from being called by limiting the requests count.
1 2 3 4 5 6 7 8 9 10 11 12 13
// borrow the case from [resilience4j](https://github.com/resilience4j/resilience4j)
private RateLimiter rateLimiter; // distribute permits at a configurable rate.
// again, just a sample, usually wrapped with aop. voidcoreMethod(){ boolean permission = rateLimiter.acquirePermission(); if (!permission) { thrownew RequestNotPermitted("RateLimiter does not permit further calls"); } // method own logic... }
an official example from resilience4j that shows how to restrict the calling rate of some method to be not higher than 1 request/second.
// Decorate your call to BackendService.doSomething() Supplier<String> restrictedSupplier = RateLimiter .decorateSupplier(rateLimiter, backendService::doSomething);
// First call is successful Try<String> firstTry = Try.ofSupplier(restrictedSupplier); assertThat(firstTry.isSuccess()).isTrue();
// Second call fails, because the call was not permitted Try<String> secondTry = Try.of(restrictedSupplier); assertThat(secondTry.isFailure()).isTrue(); assertThat(secondTry.getCause()).isInstanceOf(RequestNotPermitted.class);
@ConcurrencyLimit in spring-framework is another kind of rate limiting.