How to Use Spring Retry Template
Welcome readers, in this tutorial, we will see the retry mechanism in a simple spring boot application.
1. Introduction
Before going further in this tutorial we will take a look at the common terminology such as introduction to Spring Boot, and Retry mechanism in spring framework.
1.1 What is Spring boot?
- Spring boot is a module that provides rapid application development feature to the spring framework including auto-configuration, standalone-code, and production-ready code
- It creates applications that are packaged as jar and are directly started using embedded servlet container (such as Tomcat, Jetty or Undertow). Thus, no need to deploy the war files
- It simplifies the maven configuration by providing the starter template and helps to resolve the dependency conflicts. It automatically identifies the required dependencies and imports them in the application
- It helps in removing the boilerplate code, extra annotations, and XML configurations
- It provides a powerful batch processing and manages the rest endpoints
- It provides an efficient JPA-starter library to effectively connect the application with the relational databases
- It offers a Microservice architecture and cloud configuration that manages all the application related configuration properties in a centralized manner.
1.2 What is the Retry mechanism in Spring boot?
Before we start, let us first understand a few basics about the Retry pattern –
- A retry is used only if you think that it may meet your use case and should NOT be used for every use case
- The retry may cause resource clogging and sometimes make things even worse. Therefore the retries have to be limited
- A retry should not be done for every exception. It should always be coded for a specific exception type like
SQLException.class
,TimeoutException.class
etc - A retry can cause multiple threads trying to access the same shared resource and hence locking can be an issue
- Retry mechanism should take care of idempotency i.e. triggering the same request should not trigger a duplicate transaction
The Spring Retry mechanism is used to add retry logic to a spring application and is not probably well known because it is not listed on the Spring documentation overview. This mechanism offers features like –
- Based on the Exception
- Back off time
- Graceful handle
Now let us create a simple example to showcase the retry mechanism in the spring application. But before going any further I’m assuming that readers are aware of the concepts of creating and running a basic spring boot application.
2. How to use Spring Retry Template
Here is a systematic guide for implementing this tutorial.
2.1 Tools Used and Project Structure
We are using Eclipse, JDK 8, and Maven. In case you’re confused about where you should create the corresponding files or folder, let us review the project structure of the spring boot application.
Let us start building the application!
3. Creating a Spring boot application
Below are the steps involved in developing the application.
3.1 Maven Dependency
Here, we specify the dependency for the Spring boot and Spring retry. Maven will automatically resolve the other dependencies. The updated file will have the following code.
pom.xml
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | <? xml version = "1.0" encoding = "UTF-8" ?> xsi:schemaLocation = "http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" > < modelVersion >4.0.0</ modelVersion > < groupId >com.spring.retry</ groupId > < artifactId >spring-retry</ artifactId > < version >0.0.1-SNAPSHOT</ version > < name >tbp-spring-retry</ name > < description >Demo project for retry in springboot</ description > < parent > < groupId >org.springframework.boot</ groupId > < artifactId >spring-boot-starter-parent</ artifactId > < version >2.3.1.RELEASE</ version > </ parent > < properties > < java.version >1.8</ java.version > </ properties > < dependencies > < dependency > < groupId >org.springframework.boot</ groupId > < artifactId >spring-boot-starter-web</ artifactId > </ dependency > < dependency > < groupId >org.springframework.retry</ groupId > < artifactId >spring-retry</ artifactId > </ dependency > < dependency > < groupId >org.aspectj</ groupId > < artifactId >aspectjweaver</ artifactId > </ dependency > </ dependencies > < build > < plugins > < plugin > < groupId >org.springframework.boot</ groupId > < artifactId >spring-boot-maven-plugin</ artifactId > </ plugin > </ plugins > </ build > </ project > |
3.2 Application Properties file
Create a new properties file at the location: spring-retry/src/main/resources/
and add the application configuration to this file.
application.properties
1 2 | # Application server port server.port=9098 |
3.3 Java Classes
Let us write all the java classes involved in this application.
3.3.1 Implementation/Main class
Add the following code to the main class to bootstrap the application from the main method and enable the retry mechanism in the application.
SpringRetryApplication.java
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 | package com.spring.retry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.retry.annotation.EnableRetry; // Main implementation class which serves two purposes in a spring boot application: Configuration and bootstrapping. @SpringBootApplication // To enable spring retry in spring boot project. @EnableRetry public class SpringRetryApplication { private static final Logger LOGGER = LoggerFactory.getLogger(SpringRetryApplication. class ); public static void main(String[] args) { SpringApplication.run(SpringRetryApplication. class , args); LOGGER.info( "SpringRetryApplication application started successfully." ); } } |
3.3.2 Service class
Add the following code to the service class that has the invoke(……)
method to talk to a 3rd party service. In case the 3rd party service does not respond due to any XYZ reason, the invoke(……)
method will attempt three retries. If the response is not received during these retries, the exception will be thrown and the exception fallback method (i.e. recover(……)
) would be invoked. Thus this fallback method will then send a response to the user.
SocialSecurityService.java
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | package com.spring.retry.service; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.retry.annotation.Backoff; import org.springframework.retry.annotation.Recover; import org.springframework.retry.annotation.Retryable; import org.springframework.stereotype.Service; @Service public class SocialSecurityService { private static final Logger LOGGER = LoggerFactory.getLogger(SocialSecurityService. class ); int counter = 1 ; // To indicate any method to be a candidate of retry. // 'maxAttempts' attribute tells that how many times we would need to retry the 3rd party service to fetch the details. // 'value' attribute tells the type of exceptions (e.g. TimeoutException, IOException, etc.) that we can happen when retry takes place. // 'backoff' attribute tells to create a gap between the retries. @Retryable (maxAttempts = 3 , value = { RuntimeException. class }, backoff = @Backoff (delay = 2000 , multiplier = 2 )) public String invoke( final String ssn) { // For simplicity we are returning the user-input. In ideal scenario it will call the // social-security service to fetch the details. // return ssn; // As we are showing the retry mechanism in this tutorial, we will comment this to assume that the 3rd-party service is down. // So to perform this retry mechanism we'll throw some dummy exception // (considering the 3rd-party service is down and throwing an exception). LOGGER.info( "Executed counter= {}." , counter); // This counter will help us to understand that after 3 retry attempts the fallback method would be called. counter++; throw new RuntimeException( "Some random Exception" ); } // To specify the fallback method. // The exception in this method should match the exception defined in the @Retryable annotation. @Recover public String recover( final RuntimeException e, final String ssn) { LOGGER.info( "Sending the fall-back method response to the user as the number of max-attempts for the " + "3rd-party service has been reached." ); return "Not able to connect to the social security details portal at this time." ; } } |
3.3.3 Controller class
Add the following code to the controller class designed to handle the incoming requests. The class is annotated with the @RestController
annotation where every method returns a domain object as a JSON response instead of a view.
SocialSecurityController.java
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | package com.spring.retry.controller; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import com.spring.retry.service.SocialSecurityService; @RestController public class SocialSecurityController { private static final Logger LOGGER = LoggerFactory.getLogger(SocialSecurityController. class ); @Autowired SocialSecurityService socialSecurityService; // Sample url- http://localhost:9098/getssn?ssn=1234 @GetMapping (value = "/getssn" , consumes = "application/json" , produces = "application/json" ) public String getSocialSecurityDetails( @RequestParam (value = "ssn" ) String ssn) { LOGGER.info( "Invoking the service with ssn= {}." , ssn); return socialSecurityService.invoke(ssn); } } |
4. Run the Application
To execute the application, right-click on the SpringRetryApplication.java
class, Run As -> Java Application
.
5. Project Demo
Open the Postman tool or any browser of your choice and hit the following URLs to display the fallback response from the service.
1 2 | // Get the social security details for a social security number |
Now on hitting this URL you will get the fallback response after the retry limit is exceeded (while waiting for a response from the 3rd party service) and the same can be verified from the logs shown below.
1 2 3 4 5 | 2020-07-11 17:05:53.050 INFO 17880 --- [nio-9098-exec-1] c.s.r.c.SocialSecurityController : Invoking the service with ssn= 1234. 2020-07-11 17:05:53.106 INFO 17880 --- [nio-9098-exec-1] c.s.retry.service.SocialSecurityService : Executed counter= 1. 2020-07-11 17:05:55.108 INFO 17880 --- [nio-9098-exec-1] c.s.retry.service.SocialSecurityService : Executed counter= 2. 2020-07-11 17:05:59.109 INFO 17880 --- [nio-9098-exec-1] c.s.retry.service.SocialSecurityService : Executed counter= 3. 2020-07-11 17:05:59.111 INFO 17880 --- [nio-9098-exec-1] c.s.retry.service.SocialSecurityService : Sending the fall-back method response to the user as the number of max-attempts for the 3rd-party service has been reached. |
That is all for this tutorial and I hope the article served you whatever you were looking for. Happy Learning and do not forget to share!
6. Summary
In this section, we learned:
- Spring Boot and Retry mechanism
- Steps to implement the retry mechanism in spring boot application
You can download the sample application as an Eclipse project in the Downloads section.
7. Download the Eclipse Project
This was an example of a Retry mechanism in a Spring boot application.
You can download the full source code of this example here: How to use Spring Retry Template
Unfortunately, although the title of the article is “How to use Spring Retry Template”, the example code does not in fact use a RetryTemplate.