1. Spring Retry
1. Basic Principles
Spring Retry is a retry framework provided by Spring. As part of the Spring ecosystem, it realizes the retry ability of method calls through AOP (sectional-oriented programming). When a method call fails, Spring Retry automatically re-executes the method according to the configured policy until it succeeds or reaches the maximum number of retries.
The core components of Spring Retry include:
- RetryOperations: Define the interface for retry operation
- RetryTemplate: The default implementation of RetryOperations
- RetryPolicy: Define when to retry (such as maximum number of times, type of retry, etc.)
- BackOffPolicy: Define retry interval strategies (such as fixed intervals, exponential backoff, etc.)
- RecoveryCallback: Define recovery strategy after final failure
2. Integrated configuration
Integrate Spring Retry in SpringBoot project:
<dependency> <groupId></groupId> <artifactId>spring-retry</artifactId> </dependency> <dependency> <groupId></groupId> <artifactId>spring-aspects</artifactId> </dependency>
Then enable the retry feature on the startup class:
@SpringBootApplication @EnableRetry public class Application { public static void main(String[] args) { (, args); } }
3. How to use
Spring Retry provides two ways to use annotation method and programming method.
Annotation method
@Service public class RemoteServiceClient { private static final Logger logger = (); @Retryable(value = {}, maxAttempts = 3, backoff = @Backoff(delay = 1000, multiplier = 2)) public String callRemoteService(String param) { ("Calling remote services,parameter: {}", param); // The mock call failed if (() > 0.7) { ("Service call failed"); throw new ServiceException("Remote service is temporarily unavailable"); } return "The call succeeded: " + param; } @Recover public String recover(ServiceException e, String param) { ("Retry failed,Execute recovery method, parameter: {}", param); return "Downgrade response: " + param; } }
In the above example:
-
@Retryable
Annotations define methods that need to be retryed, including the type of exception that triggers retry, the maximum number of retries, and the backoff strategy. -
backoff
Attribute sets the initial delay of 1 second, and the delay time is doubled for each time (exponential backoff) -
@Recover
The annotation defines the recovery method after the retry failed.
Programming method
@Service public class RemoteServiceClient { private final RetryTemplate retryTemplate; @Autowired public RemoteServiceClient(RetryTemplate retryTemplate) { = retryTemplate; } public String callWithRetry(String param) { return (context -> { // Retry business logic if (() > 0.7) { throw new ServiceException("Service is temporarily unavailable"); } return "The call succeeded: " + param; }, context -> { // Restoration logic after failed retry return "Downgrade response: " + param; }); } @Bean public RetryTemplate retryTemplate() { RetryTemplate template = new RetryTemplate(); // Set a retry policy SimpleRetryPolicy policy = new SimpleRetryPolicy(); (3); // Set back policy ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy(); (1000); (2.0); (policy); (backOffPolicy); return template; } }
4. Pros and cons
advantage
- Perfect integration with Spring ecosystem
- Provides rich retry policies and configuration options
- Supports two ways of annotation and programming, flexible use
- You can accurately control the retry exception type
- Support declarative transaction rollback and commit
shortcoming
- Relying on Spring Framework
- The code is relatively invasive
- A little complicated in complex scenarios
- Integrating with other fault tolerance mechanisms requires additional work
5. Applicable scenarios
- Spring ecosystem projects
- Scenarios where retry conditions and strategies are needed are finely controlled
- Business scenarios combined with Spring transactions
- Method level retry requirement
2. Resilience4j Retry
1. Basic Principles
Resilience4j is a lightweight fault-tolerant library inspired by Netflix Hystrix, where the Resilience4j Retry module provides powerful retry functionality. Unlike Spring Retry, Resilience4j adopts a functional programming style and uses the decorator mode to implement the retry function.
Features of Resilience4j Retry:
- Based on functional interface
- No external dependencies, lightweight design
- Can be seamlessly integrated with other fault-tolerant mechanisms (such as circuit breakers, current limiters)
- Provide rich monitoring indicators
2. Integrated configuration
Integrate Resilience4j Retry in SpringBoot project:
<dependency> <groupId>.resilience4j</groupId> <artifactId>resilience4j-spring-boot2</artifactId> <version>1.7.0</version> </dependency> <dependency> <groupId></groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
Configuration:
: instances: backendService: maxAttempts: 3 waitDuration: 1s enableExponentialBackoff: true exponentialBackoffMultiplier: 2 retryExceptions: - -
3. How to use
Resilience4j also supports annotation methods and programming methods.
Annotation method
@Service public class BackendService { private static final Logger logger = (); @Retry(name = "backendService", fallbackMethod = "fallbackCall") public String callBackendService(String param) { ("Calling backend services,parameter: {}", param); if (() > 0.7) { ("Service call failed"); throw new IOException("Service connection failed"); } return "Backend Service Response: " + param; } public String fallbackCall(String param, Exception ex) { ("All retry failed,Perform a downgrade method,parameter: {}", param); return "Downgrade response: " + param; } }
Programming method
@Service public class BackendService { private final RetryRegistry retryRegistry; private final Logger logger = (); @Autowired public BackendService(RetryRegistry retryRegistry) { = retryRegistry; } public String executeWithRetry(String param) { // Get the configured retry instance Retry retry = ("backendService"); // Create a retryable function CheckedFunction0<String> retryableFunction = ( retry, () -> callBackendService(param)); try { // Execute the retry function return (); } catch (Throwable throwable) { ("Retry failed: {}", ()); return "Downgrade response: " + param; } } private String callBackendService(String param) throws IOException { ("Calling backend services,parameter: {}", param); if (() > 0.7) { throw new IOException("Service connection failed"); } return "Backend Service Response: " + param; } }
4. Pros and cons
advantage
- Lightweight design without external dependencies
- Functional programming style, simple code
- Provide rich monitoring and statistical indicators
- Can be seamlessly integrated with fault-tolerant mechanisms such as circuit breakers and current limiters
- Supports multiple advanced retry strategies
shortcoming
- The learning curve is relatively steep, especially the functional concept
- For developers who are not familiar with functional programming, it may not be intuitive enough
- Some advanced features require additional configuration
5. Applicable scenarios
- Complex scenarios that need to be combined with other fault tolerance mechanisms
- Inter-service calls in microservice architecture
- Systems that require detailed monitoring of indicators
3. Guava Retrying
1. Basic Principles
Guava Retrying is a retry mechanism provided by the Google Guava library. It provides a simple and flexible API to implement the retry function.
Guava Retrying provides flexible retry configuration through the builder mode, which can customize retry conditions, stop policies, wait policies, etc.
2. Integrated configuration
Integrate Guava Retrying in SpringBoot project:
<dependency> <groupId></groupId> <artifactId>guava-retrying</artifactId> <version>2.0.0</version> </dependency>
3. How to use
Guava Retrying is mainly used in programming:
@Service public class ExternalServiceClient { private static final Logger logger = (); public String callExternalService(String param) { Retryer<String> retryer = RetryerBuilder.<String>newBuilder() .retryIfException() // Retry if any exception occurs .retryIfResult(result -> result == null) // Try again when the result is null .withWaitStrategy((1000, 10000, )) // Exponential backoff .withStopStrategy((3)) // Try up to 3 times .withRetryListener(new RetryListener() { @Override public <V> void onRetry(Attempt<V> attempt) { ("The{}Try again", ()); if (()) { ("abnormal: {}", ().getMessage()); } } }) .build(); try { return (() -> { ("Calling external services,parameter: {}", param); // Simulate service calls if (() > 0.7) { throw new RuntimeException("Service is temporarily unavailable"); } return "External Service Response: " + param; }); } catch (RetryException | ExecutionException e) { ("Retry failed: {}", ()); return "Downgrade response: " + param; } } }
Create a reusable Retryer bean in SpringBoot:
@Configuration public class RetryConfig { @Bean public <T> Retryer<T> defaultRetryer() { return RetryerBuilder.<T>newBuilder() .retryIfException() .retryIfResult(()) .withWaitStrategy((100, 1000, )) .withStopStrategy((3)) .build(); } } @Service public class ServiceWithRetry { private final Retryer<String> retryer; @Autowired public ServiceWithRetry(Retryer<String> retryer) { = retryer; } public String executeWithRetry(String input) throws ExecutionException, RetryException { return (() -> { // Business logic return processInput(input); }); } }
4. Advanced Features
Guava Retrying offers a wealth of customization options:
Retryer<String> complexRetryer = RetryerBuilder.<String>newBuilder() // Customize retry conditions .retryIfExceptionOfType() .retryIfException(e -> e instanceof TimeoutException) .retryIfResult(result -> result != null && ("error")) // Customize waiting strategy .withWaitStrategy(( (1000, ), (1000, , 2000, ) )) // Customize the stop strategy .withStopStrategy((30, )) // Customize blocking strategies .withBlockStrategy(()) .build();
5. Pros and cons
advantage
- The API is simple and intuitive, easy to use
- Highly customizable retry conditions, wait policies and stop policies
- It does not depend on the Spring framework and can be used in any Java project
shortcoming
- No annotation support, can only be used programmatically
- Lack of deep integration with the Spring ecosystem
- No built-in monitoring and statistics
- Update stopped
6. Applicable scenarios
- Simple retry requirement
- Non-Spring projects or projects that rely less on Spring
- Scenarios that require highly customization of retry logic
4. Failsafe
1. Basic Principles
failsafe is a relatively new Java retry library focusing on high performance and low latency scenarios. Its design goal is to provide a simple and efficient retry mechanism while maintaining the simplicity of the API and the ease of use. Failsafe supports synchronous and asynchronous retry, with flexible retry strategies and minimal dependencies.
2. Integrated configuration
Integrating failsafe in SpringBoot Project
<dependency> <groupId></groupId> <artifactId>failsafe</artifactId> <version>3.3.2</version> </dependency>
Failsafe is usually used by the :failsafe library, which is a modern retry and fault-tolerant library.
3. How to use
Failsafe is mainly used in programming and has a streaming API design
@Service public class FailsafeService { private static final Logger logger = (); public String executeWithRetry(String param) { return ( // Configure the retry policy RetryPolicy.<String>builder() .handle(, ) .withMaxRetries(3) .withDelay((1)) .withMaxDuration((10)) .withBackoff((100), (2)) .onRetry(event -> ("The{}Try again,Last time abnormal: {}", (), ().getMessage())) .onFailure(event -> ("Retry failed,Number of attempts: {}, Total time consumption: {}ms", (), ().toMillis())) .build() ) .get(() -> { ("Perform an action,parameter: {}", param); // Simulation operation if (() > 0.7) { throw new IOException("The operation failed temporarily"); } return "The operation was successful: " + param; }); } // Asynchronous retry example public CompletableFuture<String> executeWithRetryAsync(String param) { return ( RetryPolicy.<String>builder() .handle() .withMaxRetries(3) .withBackoff((100), (1)) .build() ) .getAsync(() -> { ("异步Perform an action,parameter: {}", param); // Simulate asynchronous operations if (() > 0.7) { throw new IOException("Async operation failed temporarily"); } return "Async operation succeeded: " + param; }); } // Retry example with downgrade public String executeWithFallback(String param) { return ( RetryPolicy.<String>builder() .handle() .withMaxRetries(3) .build(), // Downgrade strategy (e -> "Downgrade response: " + param) ) .get(() -> { // Business logic if (() > 0.7) { throw new IOException("Operation failed"); } return "The operation was successful: " + param; }); } }
Create reusable RetryPolicy beans in SpringBoot
@Configuration public class FailsafeConfig { @Bean public RetryPolicy<Object> defaultRetryPolicy() { return () .handle() .withMaxRetries(3) .withBackoff((100), (1), 2.0) .build(); } @Bean public Fallback<Object> defaultFallback() { return (e -> { if (e instanceof ServiceException) { return "Exceptionally downgraded service"; } return "General downgrade response"; }); } } @Service public class ServiceWithFailsafeRetry { private final RetryPolicy<Object> defaultRetryPolicy; private final Fallback<Object> defaultFallback; @Autowired public ServiceWithFailsafeRetry(RetryPolicy<Object> defaultRetryPolicy, Fallback<Object> defaultFallback) { = defaultRetryPolicy; = defaultFallback; } public String executeWithRetry(String input) { return (defaultRetryPolicy, defaultFallback) .get(() -> { // Business logic return processInput(input); }); } }
4. Pros and cons
advantage
- Extremely high performance, suitable for high-frequency call scenarios
- Supports synchronous and asynchronous retry
- Lightweight, less dependency
- Good integration with CompleteFuture
- Built-in rich listener mechanism
shortcoming
- No annotation support, can only be used programmatically
- Not very integrated with Spring framework
- Updates have not been active in recent years
5. Applicable scenarios
- Applications with high performance and low latency requirements
- Scenarios that require asynchronous retry capability
- Scenarios where retry behavior needs to be controlled in fine-grained manner
5. Comparison of four retry mechanisms
characteristic | Spring Retry | Resilience4j Retry | Guava Retrying | Failsafe |
---|---|---|---|---|
Programming Model | AOP + command | Functional formula | Command | Streaming |
Annotation support | support | support | Not supported | Not supported |
rely | Spring | No external dependencies | Guava | Minimum dependency |
Performance overhead | medium | Low | medium | Extremely low |
Asynchronous support | limited | good | limited | excellent |
Monitoring integration | limited | Rich | none | Basic |
Configuration method | Annotation/Programming | Configuration file/annotation/programming | programming | programming |
Integrate with other fault tolerance mechanisms | limited | Native support | none | good |
Learning curve | medium | Steeper | gentle | gentle |
Customizable | high | high | high | high |
Applicable scenarios | Spring Project | Microservices/cloud native applications | Simple scene/non-Spring project | High-performance scenarios |
6. Best practices and precautions
1. General Best Practices
- Ensure idempotence: The retry mechanism is best suited for idempotent operations, that is, the operation that produces the same result is performed multiple times. Special care is required for non-idempotent operations.
- Set reasonable timeouts and retry times: Avoid infinite retry or retry time too long, usually 3-5 times are enough to handle most temporary failures.
- Using an exponential backoff strategy: As the number of retry increases, gradually increase the retry interval to avoid excessive pressure on the target service.
- Distinguish between temporary and permanent failures: Retry only for temporary failures that may be recovered by themselves, and should not be retryed for permanent errors.
- Add monitoring and logs: Record indicators such as the number of retry times, success rate, etc., to facilitate problem investigation and performance optimization.
2. Avoid common pitfalls
- Try the storm again: When multiple clients retry a failed service at the same time, it may cause a surge in service load.
- Resource leak: During the retry process, make sure that resources (such as database connections, HTTP connections) are released correctly.
- Try too much: Excessive retry may cause performance degradation, and a reasonable maximum number of retryes and total timeout should be set.
- Retry Cost: Some operations are expensive to retry (such as third-party paid APIs), and retry strategies need to be designed with caution.
7. Summary
The selection of the appropriate retry mechanism should be based on the project's technology stack, complexity and requirements. Regardless of the mechanism you choose, best practices of retry should be followed, avoid common pitfalls, and ensure the stability and reliability of your system.
The above is the detailed content of the four solutions for SpringBoot to implement the retry mechanism. For more information about SpringBoot retry mechanism, please pay attention to my other related articles!