Spring Cloud Netflix Zuul is another important part of the Spring Cloud project family. As the name suggests, Spring Cloud Netflix Zuul is also originally created by the Netflix team. It was later adopted into the Spring Cloud project.

Spring Cloud Netflix Zuul helps implementing the API Gateway pattern. This is a very common pattern for Microservices Architecture.

Zuul acts as a gateway deriving its name from the Gatekeeper made famous by the original Ghostbusters movie.

Remember this guy!

spring cloud netflix zuul

Netflix claims that they use Zuul heavily for purposes such as dynamic routing, monitoring, security and so on. Basically, Zuul acts as an API Gateway.

How Spring Cloud Netflix Zuul fits into Microservices Architecture?

A common problem in microservices architecture is to provide a single point of entry for the client applications.

We might build several microservices depending on the domain model. However, usually we don’t want all the microservices to be exposed to the outside world. If that is done, it might result in substantial increase in development cost and maintenance overhead on the client side.

Spring Cloud Netflix Zuul aims to solve this problem.

At its simplest, Zuul is a proxy. It also acts as an edge service that proxies incoming requests to backing microservices. In other words, Zuul acts as the front door to your application. Any client app whether it be a web application or a mobile app can reach your backing services through this gateway.

Zuul can also seamlessly integrate with other Netflix components such as Spring Cloud Eureka or Hystrix. See below illustration for a typical architecture leveraging Zuul.

spring cloud netflix zuul architecture

Creating a Spring Cloud Netflix Zuul Application

With Spring Cloud Netflix Zuul, you can create a Zuul Gateway Proxy like a simple Spring Boot application. You can bootstrap the application easily using https://start.spring.io.

Below are the dependencies we include in the POM.xml.

<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-actuator</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
</dependencies>

We have included common dependencies like the web-starter and actuator. Also, we want the Zuul gateway to register with Eureka. Therefore, we have included spring-cloud-starter-netflix-eureka-client as well.

Next, we need to annotate the main class with a couple of annotations. One to enable the application to act as Zuul Proxy. The second is to enable the Zuul application to register with Eureka. See below:

@SpringBootApplication
@EnableZuulProxy
@EnableDiscoveryClient
public class ZuulGatewayApplication {

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

}

We have a detailed guide about setting Spring Cloud Netflix Eureka if you want to know more about it.

Lastly, we set the port number and application name in the application.properties file.

spring.application.name=zuul-gateway
server.port=8085

If we start the application now and visit the Eureka dashboard, we should be able to see our Zuul application. We have also started our Config Server application and the Product application. You can read more about those applications in our guide about Spring Cloud Config Server.

spring cloud eureka dashboard

Configuring Zuul Routes

Right now, our Spring Cloud Netflix Zuul gateway is not doing much. However, we can make it act like a gateway by adding Routes information in the application.properties file as below:

zuul.routes.product-application.url=http://localhost:8080

Let’s understand what is happening here.

In order to forward requests from the Gateway, we need to tell Zuul about the routes it needs to watch. Also, we need to specify the services it needs to forward those requests to.

We specify those routes using the property zuul.routes. In a typical setup, each of our microservices can have an entry as zuul.routes.NAME. Here, NAME is the application name.

In our case, this application name is product-application. Therefore, any request that comes to the path /product-application will be routed to the service located at http://localhost:8080.

To see it in action, we will add a /products end-point to our product-application.

@RestController
class ProductController{

	@GetMapping(value = "/products")
	public Product getProduct(){
		return new Product("Laptop", "The best laptop in the world");
	}

}

class Product{

	String productName;

	String productDescription;

	public Product(String productName, String productDescription) {
		this.productName = productName;
		this.productDescription = productDescription;
	}

	public String getProductName() {
		return productName;
	}

	public void setProductName(String productName) {
		this.productName = productName;
	}

	public String getProductDescription() {
		return productDescription;
	}

	public void setProductDescription(String productDescription) {
		this.productDescription = productDescription;
	}
}

Now, we can start up all our application i.e. product-application, zuul-gateway, eureka and config-server.

If we now make a request to http://localhost:8085/product-application/products, we will get the below response.

{"productName":"Laptop","productDescription":"The best laptop in the world"}

Additional Zuul Components

Spring Cloud Netflix Zuul also comes pre-bundled with filters. There are mainly 4 types of filters that you can use:

  • pre-filters that are invoked before the request is routed.
  • post-filters that are invoked after the request has been routed.
  • route-filters are used to route the request.
  • error-filters that are invoked when there is an error in handling the request.

To write a filter, below are the primary steps:

Step 1 is to extend the ZuulFilter class.

In Step 2, you need to override filterType, filterOrder, shouldFilter and run methods. Basically, filterType returns one of the above mentioned type of filter.

In Step 3, add the required logic in the run method of the filter.

Note that the filterOrder determines the priority of the filter in case there are multiple filters of a particular type.

Below is an implementation of the filters:

Pre-Filter

public class PreFilter extends ZuulFilter {

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

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

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

    @Override
    public Object run() throws ZuulException {
        RequestContext ctx = RequestContext.getCurrentContext();

        HttpServletRequest request = ctx.getRequest();

        System.out.println("Request Method " + request.getMethod() + " Request URL : " + request.getRequestURL().toString());
        return null;
    }
}

Post-Filter

public class PostFilter extends ZuulFilter {

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

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

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

    @Override
    public Object run() throws ZuulException {
        System.out.println("Inside Post Filter");

        return null;
    }
}

Route-Filter

public class RouteFilter extends ZuulFilter {

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

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

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

    @Override
    public Object run() throws ZuulException {
        System.out.println("Inside Route Filter");
        return null;
    }
}

Error-Filter

public class ErrorFilter extends ZuulFilter {

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

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

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

    @Override
    public Object run() throws ZuulException {
        System.out.println("Inside Error Filter");
        return null;
    }
}

At this point, we are only printing some messages in the run method of these filters.

Lastly, we create the bean definitions for these filters so that they are auto-registered.

@SpringBootApplication
@EnableZuulProxy
@EnableDiscoveryClient
public class ZuulGatewayApplication {

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

	@Bean
	public PreFilter preFilter() {
		return new PreFilter();
	}
	@Bean
	public PostFilter postFilter() {
		return new PostFilter();
	}
	@Bean
	public ErrorFilter errorFilter() {
		return new ErrorFilter();
	}
	@Bean
	public RouteFilter routeFilter() {
		return new RouteFilter();
	}

}

Now if you start the applications and access the product-application via the Zuul gateway, you would be able to see the messages in the filter in the console.

Request Method GET Request URL : http://localhost:8085/product-application/products
Inside Route Filter
Inside Post Filter

Conclusion

We now have successfully implemented Spring Cloud Netflix Zuul gateway. Using the gateway we have been able to proxy the requests to the backing services such as product-application.

The code till this point is available on Github for reference.

Sound off your comments or queries in the comments section below.


Saurabh Dashora

Saurabh is a Software Architect with over 12 years of experience. He has worked on large-scale distributed systems across various domains and organizations. He is also a passionate Technical Writer and loves sharing knowledge in the community.

2 Comments

Dinesh · October 18, 2020 at 12:06 pm

Nice work

    Saurabh Dashora · October 19, 2020 at 2:13 am

    Thanks

Leave a Reply

Your email address will not be published. Required fields are marked *