Use Spring Retry instead of do-while

Lejdi Prifti
3 min readNov 1, 2023

--

In this article, I’ll outline the advantages of switching from the standard do-while method to Spring Retry when contacting external services.

Spring Retry

Consider the following situation. The source and target microservices are our two. To begin processing its own data, the source microservice needs to get in touch with the target microservice to find out if the data is ready. The data readiness procedure is time-consuming, therefore we are unsure of when it will be finished. Let’s assume that, among a plethora of possible design solutions to this type of issue, we opted to verify the data ready state by a straightforward HTTP request.

Our target microservice includes a very simple logic. It basically defines an endpoint /readiness that based on the number of attemps either throws an exception or returns OK .

@Slf4j
@RestController
@RequestMapping("/target")
public class TargetController {

private static int NUMBER_OF_ATTEMPTS = 0;

@GetMapping("/readiness")
public String isDataReady() throws Exception {
if (NUMBER_OF_ATTEMPTS < 5) {
NUMBER_OF_ATTEMPTS++;
throw new Exception("data is not ready");
}
log.info("data is ready");
NUMBER_OF_ATTEMPTS = 0;
return "OK";
}

}

Now, let’s have a look at the source microservice. It defines a service called ExternalService which makes the call to the target microservices for getting the information needed. The method checkWithDoWhile makes an HTTP call using RestTemplate to the target microservice and returns the body if the request goes well. Otherwise, it catches the exception thrown and puts the thread to sleep in order to give time to the target microservice to finish making the data ready. If you look at the code, it is confusing.

@Slf4j
@Service
public class ExternalService {

static final String URL = "http://localhost:8081/target/readiness";

@Autowired
private RestTemplate restTemplate;

public String checkWithDoWhile() {
long timeToWait = 1000;
int numRetry = 1;
int maxAttempts = 10;
boolean isDataReady = false;
String readiness = null;
do {
log.info("tentative num: " + numRetry + " for getting the readiness of data from external service");
try {
HttpEntity<?> entity = new HttpEntity<Object>(null, null);
ResponseEntity<String> response = restTemplate.exchange(URL, HttpMethod.GET, entity, String.class);
readiness = response.getBody();
isDataReady = true;
} catch (Exception e) {
try {
Thread.sleep(timeToWait);
} catch (InterruptedException exception) {
log.error(e.getMessage());
}
numRetry++;
}
} while (!isDataReady && numRetry <= maxAttempts);
return readiness;
}
}

Now, let’s look at how you can accomplish the same thing using Spring Retry.

@Slf4j
@Service
public class ExternalService {

static final String URL = "http://localhost:8081/target/readiness";

@Autowired
private RestTemplate restTemplate;

@Retryable(retryFor = Exception.class, maxAttempts = 10, backoff = @Backoff(delay = 1000))
public String checkWithRetry() {
HttpEntity<?> entity = new HttpEntity<Object>(null, null);
ResponseEntity<String> response = restTemplate.exchange(URL, HttpMethod.GET, entity, String.class);
return response.getBody();
}

}

The @Retryable annotation is used to specify that the method should be retried in case of exceptions. It specifies the following attributes:

  • retryFor = Exception.class: This means the method should be retried for any exception type.
  • maxAttempts = 10: It specifies a maximum of 10 retry attempts.
  • backoff = @Backoff(delay = 1000): It sets a backoff period of 1000 milliseconds (1 second) between retries.

Simple and clean. Use it!

In order to use Spring Retry, you need the following dependencies.

<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</dependency>

Additionally, Spring Retry must be activated in the Configuration class using @EnableRetry annotation.

@EnableRetry
@Configuration
public class CommonConfig {

@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}

--

--

Lejdi Prifti

Software Developer | ML Enthusiast | AWS Practitioner | Kubernetes Administrator