javahystrixspring-cloud-feignfeigncircuit-breaker

Java Feign client Fallback - can't call fallback


I am trying to understand with the work of the Feign client in Java and calling Fallback in case of an error, and I encountered some difficulties. I wrote the simplest example to understand how it works, but it seems like I'm confused. Please help me understand where my mistake is.

I also posted this example on a github to make it more convenient: https://github.com/ksereda/feign-fallback-example

The client calls service-one through Feign and receives the string "Get String from SERVICE-ONE".

The сlient also calls service-two through Feign and gets the string "Get String from SERVICE-TWO".

Prerequisite: in case of unavailability of service-two, the client must call the Fallback class in which the call to service-three is indicated and instead of "Get String from SERVICE-TWO", the client should get "Get String from SERVICE-THREE" from service-three.

Service-two and service-three are the same, only return different strings.

Service-one code:

@RestController
@RequestMapping("/")
public class OneController {

    Logger logger = Logger.getLogger(OneController.class.getName());

    @GetMapping("/getStringFromServiceOne")
    public String getString() {
        logger.info("Get String from SERVICE-ONE");
        return "Get String from SERVICE-ONE";
    }

}

Service-two code:

@RestController
@RequestMapping("/")
public class TwoController {

    Logger logger = Logger.getLogger(TwoController.class.getName());

    @GetMapping("/getStringFromServiceTwo")
    public String getString() {
        logger.info("Get String from SERVICE-TWO");
        return "Get String from SERVICE-TWO";
    }

}

Service-three code:

@RestController
@RequestMapping("/")
public class ThreeController {

    Logger logger = Logger.getLogger(ThreeController.class.getName());

    @GetMapping("/getStringFromServiceThree")
    public String getString() {
        logger.info("Get String from SERVICE-THREE");
        return "Get String from SERVICE-THREE";
    }

}

Client code:

Controller:

@RestController
@RequestMapping("/")
public class ClientController {

   Logger logger = Logger.getLogger(ClientController.class.getName());

    @Autowired
    private ServiceOneFeignClient serviceOneFeignClient;

    @Autowired
    private ServiceTwoFeignClient serviceTwoFeignClient;

    @RequestMapping(path = "/getDataFromServiceOneByFeign")
    public String getDataFromServiceOne() {
        String result = serviceOneFeignClient.getString();
        logger.info("Calling through Feign Client");
        return result;
    }

    @RequestMapping(path = "/getDataFromServiceTwoByFeign")
    public String getDataFromServiceTwo() {
        String result = serviceTwoFeignClient.getString();
        logger.info("Calling through Feign Client");
        return result;
    }

}

Feign 1:

@FeignClient(name = "service-one", url = "http://localhost:8081/")
public interface ServiceOneFeignClient {

    @GetMapping("/getStringFromServiceOne")
    String getString();

}

and Feign 2:

@FeignClient(name = "service-two", url = "http://localhost:8082/", fallback = Fallback.class)
public interface ServiceTwoFeignClient {

    @GetMapping("/getStringFromServiceTwo")
    String getString();

}

and Feign 3:

@FeignClient(name = "service-three", url = "http://localhost:8083/")
public interface ServiceThreeFeignClient {

    @GetMapping("/getStringFromServiceThree")
    String getString();

}

Main class:

@SpringBootApplication
@EnableFeignClients
@EnableCircuitBreaker
@EnableHystrix
public class ClientApplication {

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

}

and application.yml file

server:
  port: 8070

feign:
  hystrix:
    enabled: true

and here I created Fallback class

@Component
public class Fallback implements ServiceThreeFeignClient {

    @Override
    public String getString() {
        System.out.println("Called MOVIE-SERVICE with Fallback class!");
        return new String("FALLBACK STRING");
    }

}

Here I want to call service-three if service-two is unavailable.

But when I launch my application, I immediately get an error:

Caused by: java.lang.IllegalStateException: Incompatible fallback instance. Fallback/fallbackFactory of type class com.example.client.service.Fallback is not assignable to interface com.example.client.service.ServiceTwoFeignClient for feign client service-two
    at org.springframework.cloud.openfeign.HystrixTargeter.getFromContext(HystrixTargeter.java:86) ~[spring-cloud-openfeign-core-2.1.1.RELEASE.jar:2.1.1.RELEASE]
    at org.springframework.cloud.openfeign.HystrixTargeter.targetWithFallback(HystrixTargeter.java:70) ~[spring-cloud-openfeign-core-2.1.1.RELEASE.jar:2.1.1.RELEASE]
    at org.springframework.cloud.openfeign.HystrixTargeter.target(HystrixTargeter.java:46) ~[spring-cloud-openfeign-core-2.1.1.RELEASE.jar:2.1.1.RELEASE]
    at org.springframework.cloud.openfeign.FeignClientFactoryBean.getTarget(FeignClientFactoryBean.java:284) ~[spring-cloud-openfeign-core-2.1.1.RELEASE.jar:2.1.1.RELEASE]
    at org.springframework.cloud.openfeign.FeignClientFactoryBean.getObject(FeignClientFactoryBean.java:247) ~[spring-cloud-openfeign-core-2.1.1.RELEASE.jar:2.1.1.RELEASE]
    at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:171) ~[spring-beans-5.1.7.RELEASE.jar:5.1.7.RELEASE]

I understand what he writes: I must indicate

implements ServiceTwoFeignClient

instead

implements ServiceThreeFeignClient

but I want to call service three if service two is unavailable.

I don’t understand how to do this. I would be grateful for your help. thanks


Solution

  • I decided it in my Fallback class:

    @Component
    public class Fallback implements ServiceTwoFeignClient {
    
        @Autowired
        private ServiceThreeFeignClient serviceThreeFeignClient;
    
        @Override
        public String getString() {
            return serviceThreeFeignClient.getString();
        }
    
    }