Table of Contents
Introduction
In modern microservice architectures, communication between services is critical, but often these communications can fail due to network issues, service downtime, or other transient faults. To ensure resilience and avoid complete failures, it’s essential to implement retry logic for these inter-service calls. In Spring Boot, if you’re using Feign for client-side load balancing and API calls, implementing retry logic is straightforward and can greatly enhance the robustness of your system.
In this post, we’ll walk through how to implement retry logic in Feign with Spring Boot.
Setting Up Feign Client in Spring Boot
Feign is a declarative web service client that makes writing web service clients easy. You only need to declare the interfaces, and Feign handles the rest. Here’s how you can start with a basic Feign client.
First, add the necessary dependencies for Feign in your pom.xml
:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
Enable Feign clients in your Spring Boot application by adding the annotation @EnableFeignClients
to your main class:
@SpringBootApplication
@EnableFeignClients
public class LearnSpringBootOnlineApplication {
public static void main(String[] args) {
SpringApplication.run(LearnSpringBootOnlineApplication.class, args);
}
}
Next, define your Feign client interface. For example, to communicate with an external service:
@FeignClient(name = "externalService", url = "https://api.external.com")
public interface ExternalServiceClient {
@GetMapping("/data")
ResponseEntity<Data> getData();
}
Implementing Retry Logic in Feign
Spring Boot’s Feign client doesn’t natively support retries out of the box, but you can implement retries by integrating Spring Retry or configuring Resilience4j for Feign. Both approaches are widely used and effective.
Using Spring Retry
To implement retry logic using Spring Retry, you first need to add the Spring Retry dependency:
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
Next, enable retry in Feign by adding the spring-retry
configuration to your application.yml
or application.properties
:
feign:
client:
config:
default:
retryer: true
This configuration enables retry functionality in Feign. However, you’ll need to customize the retry behavior by defining a Retryer
bean:
import feign.Retryer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static java.util.concurrent.TimeUnit.SECONDS;
@Configuration
public class FeignRetryConfig {
@Bean
public Retryer retryer() {
// Retryer(Default period, max period, max attempts)
return new Retryer.Default(100, SECONDS.toMillis(1), 5);
}
}
Here, we’ve configured Feign to retry up to 5 times, with a 100ms initial interval, which grows exponentially up to 1 second between retries.
Using Resilience4j
Another approach is to use Resilience4j, a lightweight fault tolerance library designed for Java. Resilience4j allows for retries, circuit breakers, rate limiters, and more. To add retry functionality using Resilience4j, you’ll need to include the following dependencies:
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-spring-boot2</artifactId>
</dependency>
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-feign</artifactId>
</dependency>
Then, configure Resilience4j retry in your application.yml
:
resilience4j:
retry:
instances:
externalService:
max-attempts: 5
wait-duration: 1000ms
Finally, annotate your Feign client with the @Retry
annotation:
@FeignClient(name = "externalService", url = "https://api.external.com")
public interface ExternalServiceClient {
@Retry(name = "externalService")
@GetMapping("/data")
ResponseEntity<Data> getData();
}
This will ensure that the Feign client retries the call up to 5 times with a 1-second delay between attempts.
Handling Specific Exceptions
Sometimes you may want to retry only on certain types of exceptions. For example, you might want to retry on network errors but not on 4xx HTTP errors. You can specify which exceptions should trigger a retry.
For Spring Retry:
@Bean
public Retryer retryer() {
return new Retryer.Default(100, SECONDS.toMillis(1), 5) {
@Override
public boolean continueOrPropagate(RetryableException e) {
if (e.status() >= 400 && e.status() < 500) {
// Don't retry on 4xx errors
return false;
}
return super.continueOrPropagate(e);
}
};
}
For Resilience4j:
resilience4j:
retry:
instances:
externalService:
max-attempts: 5
wait-duration: 1000ms
retry-exceptions:
- java.io.IOException
ignore-exceptions:
- feign.FeignException
This configuration ensures that the retry logic is applied only to exceptions like IOException
and ignores FeignException
, which represents Feign-specific errors like 4xx or 5xx.
Conclusion
Retry logic is an essential mechanism to improve the resilience of your service communication. Feign, combined with Spring Retry or Resilience4j, offers a powerful way to implement retries for transient failures.
Using Spring Retry gives you a simple, declarative way to manage retries, while Resilience4j offers more advanced features, such as circuit breakers and rate limiting, alongside retry logic.
By implementing retry strategies effectively, your Spring Boot application can recover gracefully from transient errors, ensuring smoother and more reliable inter-service communication.
Explore our diverse collection of blogs covering a wide range of topics here.