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:
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: Acting as a service registry and running on port
8761
. - User Service: A simple REST service that has a single endpoint of
/getPublicAddress
and is running on port8100
. - Product Service: A simple REST service that has a single endpoint of
/categories
and is running on port8200
.
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:
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/:
Check out our hands-on, practical guide to learning Git, with best-practices, industry-accepted standards, and included cheat sheet. Stop Googling Git commands and actually learn it!
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:
Similarly, for the product-service navigate your browser to http://localhost:8050/product-service/categories:
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:
We will be running the 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/:
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 the 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.