[Guide] Implementing API Gateway pattern with Netflix Zuul and Spring Cloud

This post will give an introduction to the API Gateway pattern and demonstrate how to implement it using Spring Cloud and the Netflix OSS project Zuul.

But first, lets have a look at the pattern. Chris Richardson describes this pattern here covering the usual aspects of a pattern. I'll try to give a recap and compare with the description supplied by Sam Newman in his book "Building Microservices - designing fine grained systems". If you want a full description have a look at his book, and read Chris' description on his website.

The API Gateway Pattern

Context:

Building a microservice architecture, you have multiple clients communicating with your backend. The clients communicate with individual services and thereby have to communicate with many different services to aggregate the data internally upon reception of responses from these services

Problem:

How do clients minimize the number of requests to the backend, e.g. reduce these chatty interfaces? and how to allow clients to communicate with individual services in a microservice based application?

Forces:

Chris Richardsen describes some of the many forces as follows:

  • Granularity of APIs provided by microservices is often different than what a client needs
  • Different clients need different data
  • Network performance is different for different types of clients
  • The number of service instances and their locations (host+port) changes dynamically
  • Partitioning into services can change over time and should be hidden from clients
Solution:

The solution is to implement a server-side aggregation endpoint or API gateway. The API Gateway is responsible for aggregating data or, in some cases, acting as a simple routing layer for appropriate services. The API gateway can be seen as a single point of failure or, as Sam Newman describes it, as a single monolithic gateway. A better approach may be to create multiple API gateways for the different platform or frontends that your application needs to support. This is also referred to as backends for frontends.

Resulting context:

Benefits:

  • Clients are isolated from the partitioning of the microservice architecture behind the gateway
  • Clients do not have to worry about locations of specific services
  • Reduces the number of requests/roundtrips
  • Simplifies the clients by moving the aggregation logic into the API gateway

Drawbacks:

  • Increased complexity - API gateway is one more moving part added to the list within you microservices architecture
  • Increased response time compared to direct calls, because of the additional network hop through the API gateway
  • Danger of implementing to much logic in the aggregation layer

Netflix Zuul and Spring Cloud

Now that we understand the pattern, let's have a look at how to implement it using Spring Boot and Spring Cloud. Netflix has been so nice to open source their implementation of the API gateway pattern under the name Zuul. Further the guys at Spring Cloud have made this super simple to implement into your Spring Boot applications. The name 'Zuul' originates from the movie Ghostbusters where it appears as the Gatekeeper of Gozer.

Tutorial - Implementing Netflix Zuul in Spring Cloud

In this tutorial we are going to build a reverse proxy application that will use Netflix Zuul to forward simple request to the service application.

Building the service application - Movie

We will be building a very simple service application that will return a list of Ghostbusters movies when hitting the endpoint /ghostbusters.

There are a lot of ways to get started with a Spring Boot project, I will be using the Spring Initializr.

At the Spring Initializr add the Web dependency and fill out the Group and Artifact's fields - hereafter press 'Generate Project'-button and we are ready to start coding.

Open the movie project you just created in your favorite editor and add the following REST endpoint to the MovieApplication.java file.

@SpringBootApplication
@RestController
public class MovieApplication {

    @RequestMapping(value = "/ghostbusters")
    public List<String> available() {
        List<String> movies = new ArrayList<>();
        movies.add("Ghostbusters (1984)");
        movies.add("Ghostbusters (2016)");
        return movies;
    }

    public static void main(String[] args) {
        SpringApplication.run(MovieApplication.class, args);
    }
}

For the sake of simplicity we add the RestController in our MainApplication file, you would of course normally move this to a separate file.

Change the settings in application.properties, so that we avoid both applications fighting for the same port.

server.port=9000  

To verify that everything works as they should, go to localhost:9000/ghostbusters and verify that you see the following.

Building the API Gateway

Great, now we have our REST service and the only thing missing is of course the API Gateway or the reverse proxy as it is also referred to.

Again, go to Spring Initializr and create a new project as follows
Note that instead of only adding Web as our dependency we also add Zuul. Pretty sweet!

Generate and download the project and open it in your favorite editor.

In the ApiGatewayApplication.java file enable Zuul with the annotation as follows

@SpringBootApplication
@EnableZuulProxy
public class ApiGatewayApplication {

    public static void main(String[] args) {
        SpringApplication.run(ApiGatewayApplication.class, args);
    }
}

Last thing we need to do is to make Zuul handle the routing of the incoming requests. Open the application.properties file and add the following:

zuul.routes.book.path=/movies/**  
zuul.routes.book.url=http://localhost:9000  
ribbon.eureka.enabled=false  
server.port=8080  

This tell Zuul that all request arriving at /movies will be routed to localhost:9000 which is our movie service. Note that we have to disable ribbon.eureka. This is because Spring Cloud Netflix Zuul uses Ribbon to deal with the client-side load balancing. We do not need that in this example.

To verify that everything is setup correctly, open a browser and enter localhost:8080/movies/ghostbusters and you should see the Ghostbuster movies returned.

The last thing we would like to add before signing off for now is to add logging to our API Gateway. To do this we can add one of the default filters provided by Zuul.

In the same package as your ApiGatewayApplication.java create a new package called filters, inside this package create another package called pre and then create a file called PreFilter. Insert the following in the newly created file

public class PreFilter extends ZuulFilter {  
    private static Logger log = LoggerFactory.getLogger(PreFilter.class);

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

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

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

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

        log.info(String.format("%s request to %s", request.getMethod(), request.getRequestURL().toString()));
        return null;
    }
}

As you probably have figured out by now, we are implementing af Pre filter, which is executed before the request is routed. In the run() method we are implementing a write to the log containing the request. This makes it easy to track the incoming request in the API-gateway. Pre is one of four options, the other filtering methods in Zuul are: routing, post, and error, which I will not be covering in this post.

To verify that this works, start both the applications and make a request to the gateway just like before, and you should see the following being outputted in the console.

2016-04-04 18:34:52.506  INFO 83772 --- [nio-8080-exec-1] com.rpicloud.filters.pre.SimpleFilter    : GET request to http://localhost:8080/movies/ghostbusters  

That's it! Thank you for reading. All code can be found at GitHub: API Gateway and Movie Service

Happy Hacking - over and out!