SoFunction
Updated on 2025-04-14

Four solutions to implement the retry mechanism of SpringBoot

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 (() &gt; 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:

  • @RetryableAnnotations define methods that need to be retryed, including the type of exception that triggers retry, the maximum number of retries, and the backoff strategy.
  • backoffAttribute sets the initial delay of 1 second, and the delay time is doubled for each time (exponential backoff)
  • @RecoverThe 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 -&gt; {
            // Retry business logic            if (() &gt; 0.7) {
                throw new ServiceException("Service is temporarily unavailable");
            }
            return "The call succeeded: " + param;
        }, context -&gt; {
            // 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 (() &gt; 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&lt;String&gt; retryableFunction = (
            retry, () -&gt; 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 (() &gt; 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&lt;String&gt; retryer = RetryerBuilder.&lt;String&gt;newBuilder()
            .retryIfException() // Retry if any exception occurs            .retryIfResult(result -&gt; 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 &lt;V&gt; void onRetry(Attempt&lt;V&gt; attempt) {
                    ("The{}Try again", ());
                    if (()) {
                        ("abnormal: {}", ().getMessage());
                    }
                }
            })
            .build();
        
        try {
            return (() -&gt; {
                ("Calling external services,parameter: {}", param);
                
                // Simulate service calls                if (() &gt; 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 &lt;T&gt; Retryer&lt;T&gt; defaultRetryer() {
        return RetryerBuilder.&lt;T&gt;newBuilder()
            .retryIfException()
            .retryIfResult(())
            .withWaitStrategy((100, 1000, ))
            .withStopStrategy((3))
            .build();
    }
}

@Service
public class ServiceWithRetry {
    
    private final Retryer&lt;String&gt; retryer;
    
    @Autowired
    public ServiceWithRetry(Retryer&lt;String&gt; retryer) {
         = retryer;
    }
    
    public String executeWithRetry(String input) throws ExecutionException, RetryException {
        return (() -&gt; {
            // Business logic            return processInput(input);
        });
    }
}

4. Advanced Features

Guava Retrying offers a wealth of customization options:

Retryer&lt;String&gt; complexRetryer = RetryerBuilder.&lt;String&gt;newBuilder()
    // Customize retry conditions    .retryIfExceptionOfType()
    .retryIfException(e -&gt; e instanceof TimeoutException)
    .retryIfResult(result -&gt; result != null &amp;&amp; ("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.&lt;String&gt;builder()
                .handle(, )
                .withMaxRetries(3)
                .withDelay((1))
                .withMaxDuration((10))
                .withBackoff((100), (2))
                .onRetry(event -&gt; ("The{}Try again,Last time abnormal: {}", 
                                         (), 
                                         ().getMessage()))
                .onFailure(event -&gt; ("Retry failed,Number of attempts: {}, Total time consumption: {}ms", 
                                            (),
                                            ().toMillis()))
                .build()
        )
        .get(() -&gt; {
            ("Perform an action,parameter: {}", param);
            
            // Simulation operation            if (() &gt; 0.7) {
                throw new IOException("The operation failed temporarily");
            }
            
            return "The operation was successful: " + param;
        });
    }
    
    // Asynchronous retry example    public CompletableFuture&lt;String&gt; executeWithRetryAsync(String param) {
        return (
            RetryPolicy.&lt;String&gt;builder()
                .handle()
                .withMaxRetries(3)
                .withBackoff((100), (1))
                .build()
        )
        .getAsync(() -&gt; {
            ("异步Perform an action,parameter: {}", param);
            
            // Simulate asynchronous operations            if (() &gt; 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.&lt;String&gt;builder()
                .handle()
                .withMaxRetries(3)
                .build(),
            // Downgrade strategy            (e -&gt; "Downgrade response: " + param)
        )
        .get(() -&gt; {
            // Business logic            if (() &gt; 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&lt;Object&gt; defaultRetryPolicy() {
        return ()
            .handle()
            .withMaxRetries(3)
            .withBackoff((100), (1), 2.0)
            .build();
    }
    
    @Bean
    public Fallback&lt;Object&gt; defaultFallback() {
        return (e -&gt; {
            if (e instanceof ServiceException) {
                return "Exceptionally downgraded service";
            }
            return "General downgrade response";
        });
    }
}

@Service
public class ServiceWithFailsafeRetry {
    
    private final RetryPolicy&lt;Object&gt; defaultRetryPolicy;
    private final Fallback&lt;Object&gt; defaultFallback;
    
    @Autowired
    public ServiceWithFailsafeRetry(RetryPolicy&lt;Object&gt; defaultRetryPolicy, 
                               Fallback&lt;Object&gt; defaultFallback) {
         = defaultRetryPolicy;
         = defaultFallback;
    }
    
    public String executeWithRetry(String input) {
        return (defaultRetryPolicy, defaultFallback)
            .get(() -&gt; {
                // 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!