we encountered an unusual SSL handshake error affecting a subset of our applications. The issue exhibited a sporadic nature, with approximately a 20% reproduction rate, and intriguingly, a retry attempt consistently resolved it. For a straightforward workaround, we aim to implement the retry logic elegantly. Spring Retry module provides the ability, let's dig the usage of it and apply into our application. <dependencies> <dependency> <groupId>org.springframework.retry</groupId> <artifactId>spring-retry</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> </dependency> </dependencies>
Enabling Spring Retry with @EnableRetry@EnableRetry @Configuration public class RetryConfig { ... }
@Retryable@Retryable(retryFor = {Exception.class}, maxAttempts = 3, backoff = @Backoff(delay = 100)) public String retryOperation(...);
RetryTemplate private final RetryTemplate retryTemplate = RetryTemplate.builder() .maxAttempts(3) .fixedBackoff(100) .retryOn(Arrays.asList(SSLException.class, WebServiceIOException.class)) .build();
return retryTemplate.execute(ctx -> { log.info("Invoking soap call..."); res = webServiceTemplate .marshalSendAndReceive(request); log.info("finished soap call"); return res; });
Retry Debuglogging: level: org.springframework.retry: DEBUG
Invoking soap call... 2024-04-15 22:03:03,096 DEBUG [hostName=CAMC02F617BMD6P, threadName=http-nio-9009-exec-32, transactionId=00730103051822678907] org.springframework.retry.support.RetryTemplate : Checking for rethrow: count=1 2024-04-15 22:03:03,096 DEBUG [hostName=CAMC02F617BMD6P, threadName=http-nio-9009-exec-32, transactionId=00730103051822678907] org.springframework.retry.support.RetryTemplate : Retry: count=1 2024-04-15 22:03:03,096 INFO [hostName=CAMC02F617BMD6P, threadName=http-nio-9009-exec-32, transactionId=00730103051822678907] com.bestbuy.activate.ca.domain.BountyLookUpDetails : Invoking soap call...
@Retryable not work for spring webflux, but RetryTemplate works@Retryable is designed for use with synchronous methods and doesn't work out-of-the-box with Spring WebFlux, which is built around reactive programming principles.
For handling retries in a reactive context, you can leverage the resilience capabilities provided by libraries like Project Reactor, which is the reactive programming library used by Spring WebFlux. One approach is to use the retry operator provided by Reactor. Here's a basic example of how you can use retry in a WebFlux application: import reactor.core.publisher.Mono; import reactor.util.retry.Retry; public class MyService { public Mono<String> doSomething() { return callExternalService() .retryWhen(Retry.backoff(3, Duration.ofSeconds(5))); } private Mono<String> callExternalService() { // Call the external service here // For example: return WebClient.create().get().uri("https://example.com").retrieve().bodyToMono(String.class); } }
In this example: retryWhen is used to specify when to retry the operation.Retry.backoff(3, Duration.ofSeconds(5)) configures the retry strategy to retry up to 3 times with a backoff delay of 5 seconds between retries.
This approach allows you to handle retries in a reactive and non-blocking manner, which aligns with the principles of Spring WebFlux. Keep in mind that you may need to adjust the retry strategy based on your specific use case and requirements. Additionally, you can handle retryable errors and customize the retry behaviour further as needed. Conclusion This tutorial has enlightened us on leveraging the Spring Retry module to gracefully handle retry-based remote API invocations, adept at managing sporadic runtime exceptions or network glitches. Happy Learning! |