Hystrix fallback with Zuul and Spring Boot

Hystrix fallback with Zuul and Spring Boot

When we work with a Gateway Service like Zuul, probably we want to include a Circuit Breaker mechanism to avoid ugly errors in case of redirecting to a service which is unavailable or not responding in time. We can do that by connecting Hystrix with Zuul with a ZuulFallbackProvider bean. Let’s see how.

Introduction

What we want to accomplish here is a better error recovery strategy when a service behind a gateway is failing. In that scenario, the problem is that Zuul would return an Internal Server Error, which might end up crashing a web page or just giving a bad time to our REST API consumers.

We can avoid that using a Circuit Breaker pattern and, with Spring Boot, the best-integrated implementation is Spring Cloud Netflix Hystrix.

Zuul and Hystrix

Getting it to work

I’ll use code from my GitHub project microservices-v8. It’s part of the complete microservices system example included in my book Learn Microservices with Spring Boot. To give a short summary of the story here, we have two microservices fully integrated with Zuul for API routing, Eureka for Service Discovery and Ribbon to perform load balancing.

You can find all the source code on GitHub: Microservices V8

For our job here, we’ll focus on the gateway project. Regarding dependency configuration, we need to make sure of including the right libraries from Spring Cloud. I’m using Dalston.SR1 and spring-cloud-starter-zuul.

The routing configuration, as usual, is located in our application.yml:

application.yml
server:
  port: 8000

zuul:
  ignoredServices: '*'
  prefix: /api
  routes:
    multiplications:
      path: /multiplications/**
      serviceId: multiplication
      strip-prefix: false
    results:
      path: /results/**
      serviceId: multiplication
      strip-prefix: false
    leaders:
      path: /leaders/**
      serviceId: gamification
      strip-prefix: false

eureka:
  client:
    service-url:
      default-zone: http://localhost:8761/eureka/

endpoints:
  routes:
    sensitive: false

If you know about Zuul route configuration this is not rocket science. I’m including it here so you can see the configuration for the /api/multiplications/** route. It’s being redirected to a service with id multiplication in Eureka.

The most interesting part is located inside the HystrixFallbackConfiguration class. There we need to inject a bean implementing ZuulFallbackProvider. Let’s check how it looks:

ZuulFallbackProvider configuration
@Configuration
public class HystrixFallbackConfiguration {

    @Bean
    public ZuulFallbackProvider zuulFallbackProvider() {
        return new ZuulFallbackProvider() {

            @Override
            public String getRoute() {
                // Might be confusing: it's the serviceId property and not the route
                return "multiplication";
            }

            @Override
            public ClientHttpResponse fallbackResponse() {
                return new ClientHttpResponse() {
                    @Override
                    public HttpStatus getStatusCode() throws IOException {
                        return HttpStatus.OK;
                    }

                    @Override
                    public int getRawStatusCode() throws IOException {
                        return HttpStatus.OK.value();
                    }

                    @Override
                    public String getStatusText() throws IOException {
                        return HttpStatus.OK.toString();
                    }

                    @Override
                    public void close() {}

                    @Override
                    public InputStream getBody() throws IOException {
                        return new ByteArrayInputStream("{\"factorA\":\"Sorry, Service is Down!\",\"factorB\":\"?\",\"id\":null}".getBytes());
                    }

                    @Override
                    public HttpHeaders getHeaders() {
                        HttpHeaders headers = new HttpHeaders();
                        headers.setContentType(MediaType.APPLICATION_JSON);
                        return headers;
                    }
                };
            }
        };
    }
}

To make it work properly, it’s important to follow some basic instructions:

  • Regarding the fallbackResponse method:
    • Provide consistent results to getStatusCode, getRawStatusCode and getStatusText. To be honest, I don't know why this interface looks so ugly, they're just redundant methods. But you need to use them as they come.
    • Inside the getBody method, provide the fallback result for your request. In our case it's a JSON response matching the format of a proper response to /multiplications/**. You need to wrap that as an InputStream according to the interface.
    • Set the proper headers for the response. In our case, that means including the right content type.
  • To set up the route that will handle this fallback you need to implement the getRoute method.
    • If you want to capture all routes, you can just specify '*'.
    • In case you work with Eureka it's a little bit tricky. You don't specify the route URL but the service id instead. In our example, multiplication.

That’s all we need. When this bean is injected and the service matching the route fails (most probably with a timeout), the request is getting handled by it, responding with the default ClientHttpResponse that we just configured. In our example, that implies showing an error to the user instead of providing them with multiplication factors. It’s not the best user experience ever, but it works better than an internal server error.

Testing it

You can test the provided code if you execute all the Spring Boot applications coming in that repository. It will set up Eureka, Ribbon and Zuul. If you want to start the UI as well, you only need to execute a jetty server with the provided configuration inside the ui folder.

Get the book Practical Software Architecture

The best option to see it working is to start two instances of the multiplication service (running in different ports). Leave the Service Discovery works and then just shut down one of them. In the next seconds, if you make a request to /api/multiplications/random (or use the main page in the UI), the fallback response will be returned. That’s an example of how ZuulFallbackProvider works with an open circuit.

Zuul and Hystrix

If you have any questions about it don’t hesitate to write me a comment. In case you want to know more about Microservices with Spring Boot, explained from a very practical perspective, have a look at my book Learn Microservices with Spring Boot. You’ll learn how to implement microservices with all these kind of related tools and patterns.

Moisés Macero's Picture

About Moisés Macero

Software Developer, Architect, and Author.
Do you need help?

Amsterdam, The Netherlands https://thepracticaldeveloper.com