Search code examples
springcloudcircuit-breaker

How to correctly handle "FeignException$ServiceUnavailableException" with fallback and without deprecated @EnableCircuitbreaker annotation


When using OpenFeign I implement fallbacks returning empty results so lists should simply appear as being empty. E.g. such as

@FeignClient(name = "objects", fallback = ObjectsClientFallback.class)
public interface ObjectsClient {
    @RequestMapping("/objects/count")
    Long count();
}

and

@Component
public class ObjectsClientFallback implements ObjectsClient {
    @Override
    public Long count() {
        return 0L;
    }
}

However, if the service is not started the application produces the ServiceUnavailableException when calling objectsClient.count() instead of using the fallback.

What is the correct way to use the fallback as @EnableCircuitBreaker has been deprecated recently? I do not want to add try-catch blocks if possible, especially in the context of lambdas or wrap this in service methods.

The application uses the @EnableDiscoveryClient annotation, like so

@SpringBootApplication
@EnableJpaRepositories
@EnableFeignClients
@EnableDiscoveryClient
@ServletComponentScan
public class Application {
   //..
}

I've seen this question and checked the mentioned documentation, but it didn't help. Library versions are Spring-Boot 2.6.2 and Spring-Cloud 2021.0.0


Solution

  • Ended up using resilience4j for spring cloud. Next to feign.circuitbreaker.enabled=true in application.properties which sets up a circuitbreaker with a default config.

    One can add a custom configuration like this:

    @Configuration
    public class FeignConfiguration {
        @Bean
        public Customizer<Resilience4JCircuitBreakerFactory> circuitBreakerFactoryCustomizer() {
            CircuitBreakerConfig circuitBreakerConfig =
                    CircuitBreakerConfig.custom()
                            .ignoreException(FeignException.ServiceUnavailable.class::isInstance)
                            .build();
            return circuitBreakerFactory -> circuitBreakerFactory
                    .configure(builder -> builder.circuitBreakerConfig(circuitBreakerConfig),
                            ObjectsClient.class.getSimpleName());
        }
    }
    

    Also, the resilience4j dependency needs to be in place, here a maven pom.xml is used.

    <project> 
        <!-- .. --> 
        <dependencies>
            <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>
       </dependencies>
    

    That way you can replace the deprecated hystrix implementation and remove the @EnableCircuitBreaker annotation. In case you want a more fine-grained setup, here are is a discussion how to implement fallback with resilience4j via default methods.