Spring Cloud Zuul example


Goal of this example

This example demonstrates the main features of the Zuul API gateway integrated into spring cloud :

  • Service auto registration via eureka
  • Service registration by address
  • Service registration by service ID
  • Filters (logging, authentication)
  • Serving static content

Technology Used

  • Spring boot 1.3.5.RELEASE
  • Eureka
  • Zuul

API Gateway Overview

The Zuul API Gateway is part of the Netflix OSS package. It is very lightweight and integrates well with Eureka. API Gateway is the single entry point into the microservice ecosystem from the outside world.

zuul-api-gateway

Main features of Zuul

  • Provides a unified access to multiple different microservices
  • Hides internal details of the microservice ecosystem
  • Load balances across multiple service instances
  • Allowes access to services
  • Restricts access to internal only services
  • Looks up services from Eureka
  • Implements filters for authentication or logging purposes

Zuul is yet another Spring-boot application. All we need to do is add the right annotation to the main class

@EnableZuulProxy
@SpringBootApplication
public class SpringCloudZuulExampleApplication {

Zuul integration with Eureka

If Zool is configured to access Eureka then it will look up all services and make them accessible externally. This is very convenient, but rather dangerous as internal only services might be exposed as well. A safer approach is to enable services one by one.

zuul:
  #Service will be mapped under the /api URI
  prefix: /api
#  Uncomment to disable auto-registering all services read from Eureka
#  ignoredServices: '*'
  routes:
    customer-by-address:
      path: /customer-by-address/**
      url: http://localhost:9098
    customer-by-service:
      path: /customer-by-service/**
      serviceId: CUSTOMER-SERVICE
    static:
      path: /static/**

Given a service available at http://localhost:9098 that registers itself into Eureka under the name “customer-service” the above config will results the following URLs be available in Zuul

URI Function
http://localhost:9090/routes Display all available Routes (services)
http://localhost:9090/api/customer-service/customer/1 Service auto registered from Eureka
http://localhost:9090/api/customer-by-address/customer/1 Service registered by address
http://localhost:9090/api/customer-by-service/customer/1 Service registered by Eureka service id

Zuul filters

If a spring Bean extends ZuulFilter then it will be auto registered as a filter. Filters are most commonly used for

  • Authentication
  • Logging
  • Serving static content
  • Dynamic routing based on some conditions (A/B testing)

Logging filter

@Component
public class StaticFilter extends ZuulFilter {

    @Override
    public String filterType() {
        return "pre";
    }

    @Override
    public int filterOrder() {
        return 0;
    }

    @Override
    public boolean shouldFilter() {
        String path = RequestContext.getCurrentContext().getRequest().getRequestURI();
        return "/api/static".equals(path);
    }

    @Override
    public Object run() {

        RequestContext ctx = RequestContext.getCurrentContext();
        // Set the default response code for static filters to be 200
        ctx.setResponseStatusCode(HttpServletResponse.SC_OK);
        // first StaticResponseFilter instance to match wins, others do not set body and/or status
        if (ctx.getResponseBody() == null) {
            ctx.setResponseBody("static content");
            ctx.setSendZuulResponse(false);
        }
        return null;
    }
}

Logging filter

@Component
public class AccessLogFilter extends ZuulFilter {

    Logger logger = LoggerFactory.getLogger(AccessLogFilter.class);

    @Override
    public String filterType() {
        return "post";
    }

    @Override
    public int filterOrder() {
        return 0;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() {
        HttpServletRequest request = RequestContext.getCurrentContext().getRequest();
        HttpServletResponse response = RequestContext.getCurrentContext().getResponse();

        logger.info("REQUEST :: < " + request.getScheme() + " " + request.getLocalAddr() + ":" + request.getLocalPort());
        logger.info("REQUEST :: < " + request.getMethod() + " " + request.getRequestURI() + " " + request.getProtocol());
        logger.info("RESPONSE:: > HTTP:" + response.getStatus());

        return null;
    }
}

Redirect filter

The below filter redirects requests to a different server. It also demonstrates that filters can be written in groovy. The advantage (and danger) of groovy scripts is that they can be loaded from classpath, so the operations team can modify them without the need to release a jar / war file itself

import com.netflix.zuul.ZuulFilter
import com.netflix.zuul.context.RequestContext
import com.netflix.zuul.util.HTTPRequestUtils

public class ExampleSurgicalDebugFilterBean extends ZuulFilter {

    @Override
    String filterType() {
        return "pre"
    }

    @Override
    int filterOrder() {
        return 96
    }

    @Override
    boolean shouldFilter() {
        RequestContext.currentContext.getRequest().getRequestURI().matches("/api/redirect.*")
    }

    @Override
    Object run() {

        RequestContext.currentContext.routeHost = new URL("http://example.com");
        if (HTTPRequestUtils.getInstance().getQueryParams() == null) {
            RequestContext.getCurrentContext().setRequestQueryParams(new HashMap<String, List<String>>());
        }
        HTTPRequestUtils.getInstance().getQueryParams().put("debugRequest", ["true"])
        RequestContext.currentContext.setDebugRequest(true)
        RequestContext.getCurrentContext().zuulToZuul = true

    }

}

beans {
    exampleSurgicalDebugFilterBean(ExampleSurgicalDebugFilterBean) {
        new ExampleSurgicalDebugFilterBean();
    }
}

In a local development environment starting up a complete microservice ecosystem with Eureka is a complex task. Docker can help a lot in this, details are discussed in this blog post : https://exampledriven.wordpress.com/2016/06/24/spring-boot-docker-example/

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: