Context
I'm developing a project based on a microservices architecture. The communication between them happens like this: Client send a request to the API Gateway that routes the request to the first service called serviceA; this one, forward the request to a second service called serviceB, and so on up to serviceD. So these microservices are like a service chain.
I'm using:
Eureka as service registry
Spring Cloud Gateway as API Gateway
Prometheus and Grafana for metrics aggregation and visualization
Docker for containerization (and a docker-compose file)
Spring Cloud Circuitbreaker
Spring Cloud Version: 2021.0.5
Spring Boot Version: 2.7.9
JRE Version: 1.8
Problem
I tried to use the @CircuitBreaker
annotation to implement a circuitbreaker pattern on the method that forwards the request from serviceA to serviceB, so that if serviceB is down or there are other issues, a fallback method will be called to return a fallback string.
However, when i run the containers and try to stop the serviceB container to test the fallback method i get a 500 status code, and no fallback message.
If I use instead a CircuitBreakerFactory
to implement the circuitbreaker pattern, it works.
What i tried to do to solve the problem
- Adding different circuitbreaker configs in the application.yml
- Changing maven dependencies
- Changing Spring Cloud version
- Changing JRE version
Code Snippets
ServiceAServiceImplementation.java with @CircuitBreaker
annotation implementation
@Service
public class ServiceAServiceImplementation implements ServiceAService {
@Value("${serviceBUrl}")
private String serviceBUrl;
private Logger logger = Logger.getLogger(ServiceAServiceImplementation.class.getName());
private final RestTemplate restTemplate;
public ServiceAServiceImplementation (RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
@Override
public String getIBM() {
return "IBM";
}
@Override
@CircuitBreaker(name = "breakerAtoB", fallbackMethod = "getInfoFallbackAtoB")
public String callB() {
return restTemplate.getForEntity(serviceBUrl + "/getCloud", String.class).getBody();
}
public String getInfoFallbackAtoB(Throwable t) {
logger.log(Level.SEVERE, "[Fallback (ServizioA)]: Errore durante la chiamata al servizio B: " + t.getMessage(), t);
return "[Fallback] Cloud Academy Marzo 2023";
}
}
ServiceAServiceImplementation.java with CircuitBreakerFactory
implementation
@Value("${serviceBUrl}")
private String serviceBUrl;
private Logger logger = Logger.getLogger(ServiceAServiceImplementation.class.getName());
private final RestTemplate restTemplate;
private final CircuitBreakerFactory circuitBreakerFactory;
public ServiceAServiceImplementation (RestTemplate restTemplate, CircuitBreakerFactory circuitBreakerFactory) {
this.restTemplate = restTemplate;
this.circuitBreakerFactory = circuitBreakerFactory;
}
@Override
public String getIBM() {
return "IBM";
}
@Override
public String callB() {
return circuitBreakerFactory.create("breakerAtoB", "breakerAtoB").run(() -> restTemplate.getForEntity(serviceBUrl + "/getCloud", String.class).getBody(),
throwable -> getInfoFallbackAtoB(throwable));
}
public String getInfoFallbackAtoB(Throwable t) {
logger.log(Level.SEVERE, "[Fallback (ServizioA)]: Errore durante la chiamata al servizio B: " + t.getMessage(), t);
return "[Fallback] Cloud Academy Marzo 2023";
}
serviceA pom.xml file
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.9</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.academy.cic</groupId>
<artifactId>serviceA</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>serviceA</name>
<description>Primo esercizio</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>2021.0.5</spring-cloud.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-reactor-resilience4j</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-micrometer</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>