'Resilience4j never call fallback method

I am new to Resilience4j and circuit breaker pattern.

I write a sample for resilience4j. Details is as below:

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>2.3.2.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>ir.co.isc</groupId>
<artifactId>circuit-breaker</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>circuit-breaker</name>
<description>Demo project for Spring Boot</description>

<properties>
    <java.version>11</java.version>
    <spring-cloud.version>Hoxton.SR6</spring-cloud.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- resilience4j dependency-->
    <dependency>
        <groupId>io.github.resilience4j</groupId>
        <artifactId>resilience4j-spring-boot2</artifactId>
        <version>1.5.0</version>
        <type>pom</type>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
        <version>2.3.0.RELEASE</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
        <version>2.3.2.RELEASE</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
        <exclusions>
            <exclusion>
                <groupId>org.junit.vintage</groupId>
                <artifactId>junit-vintage-engine</artifactId>
            </exclusion>
        </exclusions>
    </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>

application.yml:

resilience4j:
circuitbreaker:
    configs:
        default:
            registerHealthIndicator: true
            slidingWindowSize: 10
            minimumNumberOfCalls: 5
            permittedNumberOfCallsInHalfOpenState: 3
            automaticTransitionFromOpenToHalfOpenEnabled: true
            waitDurationInOpenState: 5s
            failureRateThreshold: 20
            eventConsumerBufferSize: 10
    instances:
        mainService:
            baseConfig: default

Controller class:

package ir.co.isc.circuitbreaker;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class MainServiceController {


@Autowired
private MainService mainService;


@GetMapping("/getSleuthTest")
@ResponseStatus(HttpStatus.OK)
public ResponseEntity<String> getSleuthTest(){
    String response = mainService.getResponse();
    return new ResponseEntity<>(response, HttpStatus.OK);
  }
 }

Service class:

package ir.co.isc.circuitbreaker;

import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

@Service
public class MainService {

private static final String MAIN_SERVICE = "mainService";

@Autowired
private RestTemplate restTemplate;

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

@CircuitBreaker(name = MAIN_SERVICE, fallbackMethod="testFallBack")
public String getResponse(){
    return restTemplate.getForObject("http://localhost:8081/serviceOne", String.class);
}

private ResponseEntity<String> testFallBack(Exception e){
    return new ResponseEntity<>("In fallback method", HttpStatus.INTERNAL_SERVER_ERROR);
  }
}

SpringBootApplication class:

package ir.co.isc.circuitbreaker;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class CircuitBreakerApplication {

public static void main(String[] args) {
    SpringApplication.run(CircuitBreakerApplication.class, args);
   }

}

I use postman runner to call my API. I set runner iterations to 200. after 10 successful API call I stop third-party in this url : http://localhost:8081/serviceOne

As I understood, after stopping third party API and record minimum number of successful call, resilience4j start to calculate fault rate and when fault rate is more than failureRateThreshold, fallback method (here testFallBack in service class) called, circuit status change from close to open mode and return my desire answer which describe in testFallBack() method.

But this is never happen (testFallBack() method never called). What is wrong with my application?



Solution 1:[1]

Overall after so much Google, i have feeling that fallBack can only be called from Controller. I have tried multiple times, but from Service it didn't work.

Since you are also calling it from Service, that's why you are facing this issue, instead if you call it from controller it should work for you.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Mitesh Upadhyay