In a microservices architecture, many things can go wrong: middleware can fail, the network may be unstable, or the service you’re calling might be down. In this uncertain environment, anticipating failures is crucial to prevent breaking the entire chain and delivering errors to the end-user, when you could instead offer a partially degraded experience.

This article explains how to implement the circuit breaker pattern using Hystrix, Feign Client, and Spring Boot.

Feign Client Crash Course

Feign is an HTTP client created by Netflix to simplify HTTP communications. It is integrated into Spring Boot with the spring-cloud-starter-feign starter.

To create a client to consume an HTTP service, you need to create an interface annotated with @FeignClient. Endpoints can be declared in this interface using an API similar to the Spring MVC API. You also need to add the @EnableFeignClients annotation to a Spring Configuration class.

@Configuration
@EnableFeignClients
public class FeignConfiguration {

}
@FeignClient(name = "videos", url = "http://localhost:9090/videos")
public interface VideoClient {

    @PostMapping(value = "/api/videos/suggest")
    List<Suggestion> suggest(@RequestBody ViewingHistory history);

}

An instance of VideoClient is automatically injected into the Spring application context and can be autowired and used throughout the application. Moreover, if the videos microservice is registered with the same discovery service as the current microservice, there is no need for a URL, as it will be retrieved for you based on the name.

If the videos service, a middleware, or the network happens to be down or overloaded, the suggest method will throw a FeignException that will be propagated throughout the stack if not caught.

Create a Fallback Implementation

Fortunately, Spring Cloud comes with a solution to this problem: a circuit breaker. In this article, we will use Hystrix. It was also created by Netflix and is integrated into Spring Boot using the spring-cloud-starter-hystrix starter.

The idea is to create an implementation of the VideoClient and mark it as the default behaviour if videos is unreachable or overloaded. Like a lot of other Spring features, it is enabled using an annotation: @EnableCircuitBreaker.

@Configuration
@EnableFeignClients
@EnableCircuitBreaker
public class FeignConfiguration {

}
@FeignClient(name = "videos", url = "http://localhost:9090/videos", fallback = VideoClientFallback.class)
public interface VideoClient {

    @PostMapping(value = "/api/videos/suggest")
    List<Suggestion> suggest(@RequestBody ViewingHistory history);

}
@Component
public class VideoClientFallback implements VideoClient {

    @Override
    public List<Suggestion> suggest(ViewingHistory history) {
      // Degraded service: no suggestion to offer
      return new ArrayList<>();
    }

}

A configuration property needs to be added to the application.yml file of the Spring Boot application to enable Hystrix in Feign.

feign:
    hystrix:
        enabled: true

Voilà! Whenever the remote service is unavailable, the suggest method of the VideoClientFallback will be called, and the end-user will not get an error violently thrown at them.

Keep Track of the Source Error

With this setup, the fallback will be called regardless of the initial error. If you want to retrieve this error and do something with it, you can use a FallbackFactory.

@FeignClient(name = "videos", url = "http://localhost:9090/videos", fallbackFactory = VideoClientFallbackFactory.class)
public interface VideoClient {

  @PostMapping(value = "/api/videos/suggest")
  List<Suggestion> suggest(@RequestBody ViewingHistory history);

}
@Component
public class VideoClientFallbackFactory implements FallbackFactory<VideoClient> {

    @Override
    public VideoClient create(Throwable throwable) {
        return new VideoClientFallback(throwable);
    }

}
public class VideoClientFallback implements VideoClient {

    private final Throwable cause;

    public VideoClientFallback(Throwable cause) {
      this.cause = cause;
    }

    @Override
    public List<Suggestion> suggest(ViewingHistory history) {
        if (cause instanceof FeignException && ((FeignException) cause).status() == 404) {
            // Treat the HTTP 404 status
        }

        return new ArrayList<>();
    }

}

Conclusion

Microservices foster low coupling between components and resiliency. Hence, throwing an error every time a middleware or service is down would be unfortunate. The circuit breaker pattern explained in this article allows you to ensure the continuity of service. As is often the case, Spring Boot is a great help to set up this mechanism readily.