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
I decided it in my Fallback class:
@Component
public class Fallback implements ServiceTwoFeignClient {
@Autowired
private ServiceThreeFeignClient serviceThreeFeignClient;
@Override
public String getString() {
return serviceThreeFeignClient.getString();
}
}