spring-cloud / spring-cloud-openfeign

Support for using OpenFeign in Spring Cloud apps
Apache License 2.0
1.22k stars 786 forks source link

Exception is wrapped by NoFallbackAvailableException even using Resilence4J `@CircuitBreaker` fallbackMethod #826

Closed MangKyu closed 1 year ago

MangKyu commented 1 year ago

Describe the bug I cannot find any information whether it is intended, Exception is wrapped by NoFallbackAvailableException even using @CircuitBreaker fallbackMethod


Sample This is my @FeignClient with @CircuitBreaker

@FeignClient(name = "ExchangeRateOpenFeign", url = "${exchange.currency.api.uri}")
public interface ExchangeRateOpenFeign {
     Logger log = LoggerFactory.getLogger(ExchangeRateOpenFeign.class);

    @CircuitBreaker(name = "currency", fallbackMethod = "fallback")
    @GetMapping
    ExchangeRateResponse call(
            @RequestHeader String apiKey,
            @RequestParam Currency source,
            @RequestParam Currency currencies);

    default ExchangeRateResponse fallback(
            @RequestHeader String apiKey,
            @RequestParam Currency source,
            @RequestParam Currency currencies,
            Throwable e) throws Throwable {

        log.error("Here it comes with error", e);
        throw e;
    }
}



Then test below is passed

@SpringBootTest
class ExchangeRateOpenFeignTest {

    @Autowired
    private ExchangeRateOpenFeign openFeign;

    @Test
    void errorWrappedWithNoFallbackAvailableException() {
        assertThatThrownBy(() -> openFeign.call("key", Currency.JPY, Currency.KRW))
                .isInstanceOf(NoFallbackAvailableException.class);
    }

}



Also I can get an error logs with NoFallbackAvailableException (Some logs have been omitted.)

2023-02-08 11:24:33.456 ERROR 65842 --- [pool-1-thread-1] c.m.o.a.openfeign.ExchangeRateOpenFeign  : Here it comes with error

org.springframework.cloud.client.circuitbreaker.NoFallbackAvailableException: No fallback available.
    at org.springframework.cloud.client.circuitbreaker.CircuitBreaker.lambda$run$0(CircuitBreaker.java:31) ~[spring-cloud-commons-3.1.5.jar:3.1.5]
    at io.vavr.control.Try.lambda$recover$6ea7267f$1(Try.java:949) ~[vavr-0.10.2.jar:na]
    at io.vavr.control.Try.of(Try.java:75) ~[vavr-0.10.2.jar:na]
    at io.vavr.control.Try.recover(Try.java:949) ~[vavr-0.10.2.jar:na]
    at org.springframework.cloud.circuitbreaker.resilience4j.Resilience4JCircuitBreaker.run(Resilience4JCircuitBreaker.java:133) ~[spring-cloud-circuitbreaker-resilience4j-2.1.5.jar:2.1.5]
    at org.springframework.cloud.client.circuitbreaker.CircuitBreaker.run(CircuitBreaker.java:30) ~[spring-cloud-commons-3.1.5.jar:3.1.5]
...

Caused by: feign.FeignException$BadRequest: [400] during [GET] to [http://localhost:8090/currency_data/live?source=JPY&currencies=KRW] [ExchangeRateOpenFeign#call(String,Currency,Currency)]: []
    at feign.FeignException.clientErrorStatus(FeignException.java:213) ~[feign-core-11.10.jar:na]
    at feign.FeignException.errorStatus(FeignException.java:194) ~[feign-core-11.10.jar:na]
    at feign.FeignException.errorStatus(FeignException.java:185) ~[feign-core-11.10.jar:na]
...



Since the fallbackmethod is specified, I don't think the error should be wrapped. But it is wrapped where it is called from FeignCircuitBreakerInvocationHandler.java

What I found is that, it is happend by calling fallbackMethod from resilence4J FallbackDecorators.

As Object with @CircuitBreaker is proxy and when resilence4J calls fallbackMethod By DefaultFallbackDecorator, FeignCircuitBreakerInvocationHandler.java is called and error is wrapped when fallback method is called

Thanks for reading!



cbezmen commented 1 year ago

Hey @MangKyu Could you give more information about the spring version and cloud version you are using?

I created a sample repository to understand more about your problem. As far as I can see it doesn't throw NoFallbackAvailableException. One more question from my side why do you want to throw an exception on fallback?

Feel free to add a commit or create a branch so I can check on that. https://github.com/cbezmen/can-feign/tree/b-MangKyu

MangKyu commented 1 year ago

@cbezmen hi can, Thanks for your kindness! I uploaded on my repository which is forked Because 403 forbidden occurs

It occurs when enable circuitbreaker by spring.cloud.openfeign.circuitbreaker.enabled value as true. Because there is no FeignClient`s fallback or fallbackFactory, FeignCircuitBreakerInvocationHandler.java runs with spring-cloud-commons CircuitBreaker interface which wraps exception by NoFallbackAvailableException

In my case, i have something like license code and if it fails to find, exception needs to be thrown.

cbezmen commented 1 year ago

Hey @MangKyu

I updated my branch. you can see diff via there or in here.

If you are using spring open feign, you shouldn't use @CircuitBreaker. There is already an implementation for fallbacks. There is documentation in here. Spring open-feign checks for fallback implementation in the@FeignClient annotation. If it is not implemented it will wrap it with NoFallbackAvailableException.

MangKyu commented 1 year ago

Thanks for reply! I should avoid using @CircuitBreaker with Open Feign.

Thanks!