spring-bootspring-cloudspring-cloud-feignopenfeignspring-cloud-circuitbreaker

Setting timeout to OpenFeign client


I'm trying to setup a timeout to my feign clients when they try to access to other of my services. In order to test my circuit breaker method. This is my basic setup. I'm using spring-boot 3.0.2 and spring cloud version 2022.0.1

I have a edge-service project this is the pom.xml

<?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>3.0.2</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>e-service</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>e-service</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>17</java.version>
        <spring-cloud.version>2022.0.1</spring-cloud.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <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>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
    <repositories>
        <repository>
            <id>netflix-candidates</id>
            <name>Netflix Candidates</name>
            <url>https://artifactory-oss.prod.netflix.net/artifactory/maven-oss-candidates</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>

</project>

As you can see I have the spring-cloud-starter-openfeign dependency

This is my configuration in the application.properties

spring.application.name=edge-service
server.port=8080

feign.client.config.default.readTimeout=2000

Using this property feign.client.config.default.readTimeout=2000 it should be enough to set my readTimeout to 2 seconds. But for some reason is not working.

In my service where I'm calling to my FeignClient (ProxyServiceClient)

@Service
public class MovieServiceImpl implements MovieService {

    private final Logger logger = LoggerFactory.getLogger(MovieServiceImpl.class);

    @Autowired
    private ProxyServiceClient proxyServiceClient;

    @CircuitBreaker(name = "findHorroMovies", fallbackMethod = "findHorrorMoviesFallback")
    public ResponseDTO findAllHorroMovies() {

        ResponseDTO responseDTO = new ResponseDTO();

        List<MovieCatalogDTO> horroMovies = proxyServiceClient.findByGenre(Genre.HORROR);

        responseDTO.setMovieCatalogs(horroMovies);
        responseDTO.setMessage("Buuuuuhhhh");
        responseDTO.setSize(horroMovies.size());

        return responseDTO;
    }

    public ResponseDTO findHorrorMoviesFallback(Exception e) {
        logger.error(e.getMessage());
        ResponseDTO responseDTO = new ResponseDTO();
        List<MovieCatalogDTO> movieCatalogs = new ArrayList<>();
        responseDTO.setMovieCatalogs(movieCatalogs);
        responseDTO.setSize(0);
        responseDTO.setMessage("Buuuuuhhh (cached)");

        return responseDTO;

    }

}

And in my ProxyService I'm setting a sleep time of 3 seconds to trigger the timeout in every request

@RestController
public class MovieCatalogControllerImpl implements MovieCatalogController {

    @Autowired
    private MovieCatalogService movieCatalogService;

    @GetMapping("/movie-catalog")
    public List<MovieCatalog> findByGenre(@RequestParam Optional<Genre> genre) throws InterruptedException {

        Thread.sleep(3000);

        if(genre.isPresent()) {
            return movieCatalogService.findByGenre(genre.get());
        }
        return movieCatalogService.findAll();
    }
}

I'm doing some tests but it keeps taking more than 2 seconds without closing the connection. I don't know what could I been doing wrong. Hope you can help me.

I tried to add in my application.properties file the property

feign.client.config.default.readTimeout=2000

to set the readTimeout in 2 seconds, but is not working...I don't know if it has to be something with the version of spring-boot or spring-cloud. Because I remember this approach used to work in older projects.


Solution

  • Okay, I found the solution. In the openfeign document there is an explanation where the default configuration in the application.properties has been changed

    spring:
        cloud:
            openfeign:
                client:
                    config:
                        feignName:
                            url: http://remote-service.com
                            connectTimeout: 5000
                            readTimeout: 5000
                            loggerLevel: full
                            errorDecoder: com.example.SimpleErrorDecoder
                            retryer: com.example.SimpleRetryer
                            defaultQueryParameters:
                                query: queryValue
                            defaultRequestHeaders:
                                header: headerValue
                            requestInterceptors:
                                - com.example.FooRequestInterceptor
                                - com.example.BarRequestInterceptor
                            responseInterceptor: com.example.BazResponseInterceptor
                            dismiss404: false
                            encoder: com.example.SimpleEncoder
                            decoder: com.example.SimpleDecoder
                            contract: com.example.SimpleContract
                            capabilities:
                                - com.example.FooCapability
                                - com.example.BarCapability
                            queryMapEncoder: com.example.SimpleQueryMapEncoder
                            micrometer.enabled: false
    

    So I had to update this property

    spring.cloud.openfeign.client.config.default.readTimeout=2000
    

    instead of this

    feign.client.config.default.readTimeout=2000
    

    And that was the solution. I hope it can be helpful to someone facing the same problem as me :D.