Spring Cloud: Routing with Zuul and Gateway

Overview

In this article, we'll introduce you to routing your applications via Netflix's Zuul and Spring Cloud Gateway.

In a typical microservice architecture we have many small applications running on different hosts and ports. The problem in this type of architecture is how clients (Web Applications in browsers, Mobile apps, Third-party apps making a web service call, etc.) can access these end microservices without knowing their hosts and ports. For example, browser restricts calls to different domains (also known as CORS).

What we need is a common entry point to our microservices. Using this, we not only free the clients from knowing deployment details about all of the backend services, but also reduce development effort on the server side. At the same time, if an end microservice has multiple instances running, we can do load balancing at this entry point.

Furthermore, we can also write all of the authentication and authorization mechanisms at this level. This reduces significant development on the end microservices side.

Edge Servers

To solve this problem, Netflix created Zuul server and later open-sourced it. Spring provided a nice wrapper around it for easily incorporating it to the Spring stack.

Note: Netflix recently released Zuul 2, but Spring hasn't added it to its ecosystem yet. Because of this we'll be using Zuul 1 in this article.

Spring also released its own router called Spring Cloud Gateway. It has non-blocking APIs and supports long-lived connections like WebSockets.

We will look into both of these solutions in this article. The architecture diagram looks like:

spring-cloud-routing-with-zuul-gateway

Setup

This article assumes that you already have knowledge of Netflix's Eureka project, which is used as a service registry and for load balancing. We have the following setup up for the backend service:

  • Eureka Server: Acts as a service registry and running on port 8761.
  • User Service: A simple REST service that has a single endpoint of /getPublicAddress and running on port 8100.
  • Product Service: A simple REST service that has a single endpoint of /categories and running on port 8200.

spring-cloud-routing-with-zuul-gateway-eureka-setup

Netflix Zuul

The best way to start with a skeleton project is to use Spring Initializr. Select your preferred version of Spring Boot and add the "Zuul" and "Eureka Discovery" dependencies, and generate as a Maven project:

spring-zuul-server-setup

To make it a Zuul proxy server, all we need to do is add the @EnableZuulProxy annotation to our main class:

@SpringBootApplication
@EnableZuulProxy
public class ZuulApplication {

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

We will be running the Zuul server on port 8050 and it also needs to register itself to the Eureka server. So in application.properties we'll add the following:

server.port=8050

spring.application.name=zuul-edge-server
  
eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/

Let's start this server and navigate your browser to the Eureka server at http://localhost:8761/:

spring-zuul-server-display

Now that Zuul has been registered with Eureka, let's test routing to our user-service through it by navigating the browser to the http://localhost:8050/user-service/getPublicAddress endpoint:

spring-zuul-route-user-service

Similarly, for the product-service navigate your browser to http://localhost:8050/product-service/categories:

spring-zuul-route-product-service

As you can see, we are calling the backend services through Zuul. By default, Eureka client IDs become part of the URIs. For example, here we made a call to Zuul using /product-service/categories. Zuul will check if there is any service registered as product-service in Eureka. If it's there, it will get the URL for the service and append the remaining original URL part, /categories to it and make the call.

Also, Zuul is Ribbon aware, so it will automatically load balance the call if there are multiple instance of the backend service running.

The defaults can, of course, be changed by tweaking the properties file, which can be found here. It's also not necessary for all backend services to be registered on Eureka. We can also route to other domains too.

Spring Cloud Gateway

Let's see another popular edge server called Spring Cloud Gateway, which is built on Spring Framework 5, Project Reactor and Spring Boot 2.0. Once again let's create a new project with Spring Initializr. Select your preferred version of Spring Boot and add the "Gateway" and "Eureka Discovery" dependencies, and generate as a Maven project:

spring-gateway-server-setup

We will be running Zuul server on port 8060 and it also needs to register itself to the Eureka server. So in application.properties we'll add:

server.port=8060

spring.application.name=gateway-edge-server
  
eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/

spring.cloud.gateway.discovery.locator.enabled=true
spring.cloud.gateway.discovery.locator.lowerCaseServiceId=true

Unlike Zuul, Spring cloud Gateway doesn't automatically look in Eureka for routing calls. So we enabled it by adding a couple of additional properties.

Let's start this server and navigate your browser to the Eureka server at http://localhost:8761/:

spring-zuul-server-display

Similar to the previous example, we can test our routing to user-service and product-service by navigating our browser to http://localhost:8060/user-service/getPublicAddress and http://localhost:8060/product-service/categories, respectively.

Just like Zuul, Spring Cloud Gateway checks for a service in Eureka by the first path variable. Other ways to change default are in its documentation, which can be found here.

Conclusion

In this article, we've covered how to use Spring Cloud Zuul and Gateway for routing traffic to backend microservices. We created two simple REST services that registered with Eureka server. We then created Zuul server that also registered with Eureka and then routes traffic based on it. We then saw an alternate approach with Spring Cloud Gateway.

As always, the code for the examples used in this article can be found on Github.