Circuit Breaker: Hystrix vs Resilience4J

In this article I will show how to implement the Circuit Breaker pattern with Hystrix and with Resilience4J. I will explain what’s the circuit breaker pattern and I will show the differences between Hystrix and Resilience4J.

Content:

  • Hystrix
  • The Circuit Breaker Pattern
  • Resilience4J
  • More from Resilience4J

Watch this video for a detailed explanation.

All the code of the article is available in this repository.

Hystrix

I will start implementing the Circuit Breaker pattern with Hystrix which is the oldest library. I will implement the Circuit Breaker pattern in an endpoint where I want to obtain some books information and the price information. Nevertheless, the price information is located in another microservice. So if my price microservice is available, I will get the correct and updated price. But if my price microservice isn’t available, the Circuit Breaker will enter in action.

Let’s start by adding the Maven dependency.

     <dependency>
          <groupId>org.springframework.cloud</groupId>
          <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
          <version>2.2.10.RELEASE</version>
     </dependency>

Now I need to tell my Sprint Boot application to enable Hystrix with the following annotation in the Application class.

@EnableHystrix
public class Application {
     ...

And now, when calling my price microservice, configure Hystrix to have a fallback method.

    @HystrixCommand(fallbackMethod = "noPrice")
    public BigDecimal getPrice(long bookId, BookDto bookDto) {
        var price = servicePrices.getBookPrice(bookId);
        return price.getPrice();
    }

    private BigDecimal noPrice(long bookId, BookDto bookDto) {
        return BigDecimal.ZERO;
    }

With the annotation @HystrixCommand, I tell Spring to surround this method by a proxy, and have a fallback method to noPrice if something goes wrong. But what are the criteria that makes Spring know this method is acting incorrectly? And when will it come back healthy?

The Circuit Breaker Pattern

The Circuit Breaker pattern is a mechanism that avoids the application being blocked on repeated errors. Let’s start by a microservice context. I may have at some point a microservice which becomes unavailable. That’s the lifestyle of a microservice architecture. The best practice here is to be ready for that.

Let’s have a user’s request which comes through multiple microservices. At some point, a single microservice becomes unavailable and returns some kind of error. If my architecture continues to request this unhealthy microservice, this translates to timeouts to the user, or errors to the user.

With the circuit breaker pattern, I detect that the microservice is unhealthy, so I switch to a fallback result: cache results or default results. This way, only the first user will notice the problem, and the rest of the users will see some workaround results. Meanwhile, Hystrix will check in the background if my microservice is still unhealthy or not. When it becomes healthy again, the next user’s request will request the microservice again.

All that said, I can fine tune the Circuit Breaker behavior with the following properties.

    @HystrixCommand(fallbackMethod = "noPrice",
            commandProperties = {
                @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "5"),
                @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000"),
                @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "25"),
            }
    )
    public BigDecimal getPrice(long bookId, BookDto bookDto) {
        ...

With circuitBreaker.requestVolumeThreshold, I specify how many failing requests are necessary to switch to the fallback method in a specified window. The property circuitBreaker.sleepWindowInMilliseconds is the size in milliseconds of the window. And the last one, circuitBreaker.errorThresholdPercentage, indicates the percentage from where the fallback method is applied.

Let’s formulate again. Given a window time, if the percentage of error requests are higher than the threshold and the absolute errors is higher than the volume threshold, the circuit breaker will switch to the fallback method.

Resilience4J

Until now, I’ve used Hystrix which is a dependency which is no more in development. Only in maintenance. But there is an alternative, Resilience4J. Very similar, but with more fine-tuned configuration, and using functional programming. I will start adding the dependency.

     <dependency>
          <groupId>org.springframework.cloud</groupId>
          <artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
     </dependency>

Now, instead of configuring the behavior at each method, I will have a default configuration. If needed, I could have several configurations identified by a qualifier and use them separately as needed.

@Configuration
public class Resilience4JConfig {

    @Bean
    public Customizer<Resilience4JCircuitBreakerFactory> defaultCustomizer() {
        return factory -> factory.configureDefault(id -> new Resilience4JConfigBuilder(id)
                .timeLimiterConfig(TimeLimiterConfig.custom().timeoutDuration(Duration.ofSeconds(3)).build())
                .circuitBreakerConfig(CircuitBreakerConfig.custom()
                        .slidingWindowSize(10)
                        .slidingWindowType(CircuitBreakerConfig.SlidingWindowType.TIME_BASED)
                        .minimumNumberOfCalls(5)
                        .failureRateThreshold(25)
                        .build())
                .build());
    }
}

I’ve used the same configuration as with Hystrix: the window size, the request threshold and the failure rate threshold. Let’s see now how to use it.

    private final CircuitBreakerFactory circuitBreakerFactory;

     public BookDto getBook(long bookId) {
         BookDto bookDto = bookRepository.findById(bookId)
                 .map(bookMapper::toBookDto)
                 .orElseThrow(() -> new AppException("No book found with ID " + bookId, HttpStatus.NOT_FOUND));

<strong>        BigDecimal price = circuitBreakerFactory.create("getPrice").run(() -> priceService.getPrice(bookId, bookDto),
                t -> BigDecimal.ZERO);
        bookDto.setPrice(price);</strong>

         return bookDto;
     }

This time, I don’t need another separated public method as the fallback one. I can do it directly in the lambda. I have the run method and the throwable method, the fallback method.

More from Resilience4J

And now, what are more configurations i can have with Resilience4J? I can use the Bulkhead pattern. Until now, I’ve implemented the pattern over the number of calls I can make. With the Bulkhead pattern, I can configure the number of calls a service, or a method can receive. I can configure how many concurrent requests can be handled. What’s the timeout. How the threads pool is managed. And more.

I can use the Rate Limiter. This pattern allows me to limit the number of calls, in time, on a service. I can specify how many calls can be performed in a cycle, the length of the cycle and what’s the timeout.

I also have the Retry module. When a service is unavailable, I can specify how much time to wait until i retry a new attempt. How many attempts until i consider the service is unhealthy.

I also have some more modules. And the best one, is that as I use a functional programming to configure Resilience4J, I can easily chain all those modules.

Conclusion

For Hystrix:

  • I’ve added the Maven dependency spring-cloud-starter-netflix-hystrix;
  • I’ve enabled Hystrix in my application with the annotation @EnableHytrix;
  • And then used the @HystrixCommand annotation with the fallback method and the properties needed.

And for Resilience4J:

  • I’ve added the dependency spring-cloud-starter-circuit-breaker-resilience4j;
  • I’ve created a configuration bean with all the properties of the circuit breaker.
  • And then use the CircuitBreakerFactory to run the method.

The circuit breaker pattern is very easy to implement, but it has a lot of configuration to obtain a very powerful result.

References

Repository


Never Miss Another Tech Innovation

Concrete insights and actionable resources delivered straight to your inbox to boost your developer career.

My New ebook, Best Practices To Create A Backend With Spring Boot 3, is available now.

Best practices to create a backend with Spring Boot 3

One response to “Circuit Breaker: Hystrix vs Resilience4J”

Leave a comment

Discover more from The Dev World - Sergio Lema

Subscribe now to keep reading and get access to the full archive.

Continue reading