Search code examples
javaservletsspring-bootjersey-2.0hystrix

Using a Hystrix Java Servlet & Servlet Filter in Jersey 2


I'm using Netflix' Hystrix libraries to act as a circuit breaker when connecting to remote services in a REST client I am building. I would like to setup the event streaming and dashboard monitoring via the libraries they provide. Looking at their example application here, it appears that I need to apply their servlet filters and servlet classes to my web application.

I'm using Spring Boot with Jersey 2 and wiring up my resources and filters in a JerseyConfig.java (no web.xml). I know that Jersey Filters are not the same as Servlet Filters and am struggling to integrate the two together.

So, how do you take a Java Servlet Filter and make it work as a Jersey Filter and how do you take a Java Servlet and make it work as a Jersey Resource?

My current strategy for the Servlets is to wrap them like so. One for each.

@Path("/hystrix.stream")
public class HystrixResource extends HystrixUtilizationSseServlet {

    @Context
    HttpServletRequest httpRequest;

    @Context
    HttpServletResponse httpResponse;

    //This returns void because it is a text/stream output that must remain open, 
    //so the httpResponse is continually written to until the conenction is closed
    @GET
    public void doGet() throws ServletException, IOException {
        doGet(httpRequest, httpResponse);
    }
}

This might be working, but the data is basically empty for some reason. I am guessing that reason is because the Filters are not working.

data: {"type":"HystrixUtilization","commands":{},"threadpools":{}}

It is less clear to me how to wrap the Servlet Filters because they expect different inputs and outputs than a Jersey ContainerRequestFilter. The following implementation in my JerseyConfig seems to do nothing because the logs are not indicating that the filters are being registered and I cannot break on lines in these files in debug mode.

@Component
@ApplicationPath("/")
public class JerseyConfig extends ResourceConfig {
    private static final Logger LOGGER = Logger.getLogger("JerseyConfig");
    public JerseyConfig(){
        //filter to provide a bridge between JAX-RS and Spring request attributes
        register(RequestContextFilter.class);
        register(SpringComponentProvider.class);
        //handles custom serialization
        register(new ObjectMapperContextResolver());
        //try to register the filters - which doesn't work because these aren't Jersey Filters
        register(HystrixRequestContextServletFilter.class);
        register(HystrixRequestLogViaResponseHeaderServletFilter.class);
        registerResources();

        /*
         * Enable the logging filter to see the HTTP response for each request.
         */
        register(new LoggingFilter(LOGGER, true));
    }
}

Solution

  • An alternative route, and the one I ended up eventually going with, is to use the Spring cloud/boot starters if you're in a Spring Boot project. This prevented me from having to explicitly define beans and filters as shown in the other answer. Eventually basically worked out of the box.

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-hystrix</artifactId>
            <exclusions>
                <!--We're running our Jersey server w/ Jackson 2. This import uses Jackson 1.x and creates a breaking conflict.-->
                <exclusion>
                    <groupId>javax.ws.rs</groupId>
                    <artifactId>jsr311-api</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
    

    Reference Circuit Breaker getting started guide. The one issue I faced was the Jackson 1 vs Jackson 2 conflict and was able to add the library exclusion. I basically had the Hystrix library jar before, but nothing wired up to make it work.