In this lesson, we will demonstrate how we can make a Spring Boot based microservice which will reside behind an authentication service and the Netflix Zuul API Gateway. We will be using the Spring Initializr tool for setting up the project quickly.
During the course of the lesson, we will work on three different projects to construct microservice based architecture in our application. These three projects will be:
- API Gateway based on Netflix Zuul which will perform the task of filtering, routing etc.
- Service discovery server which will maintain the records for each microservice present in the system. This server will be based on Spring Eureka
- Finally, a microservice which can be accessed via the API Gateway
This sounds a lot. Let’s get started with the Eureka Server first which will act as a Service Registration and Discovery server.
Please make sure that you are running on Java 8. Else code below will not work. Java 9 and above require extra dependencies.
Service Discovery
For maintaining the record of each microservice which is present in the system, we will Eureka server. Let’s setup the project to start.
Setting up the project
We will use Spring Initializr tool to quickly get started with the project setup. Here are the dependencies we need to add:
For this project, we need just one Maven dependency, the Eureka Server.
If you want to explicitly use Maven for dependency definitions, here is the part of pom.xml file:
<groupId>com.javabeginnerstutorial</groupId>
<artifactId>discovery-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging><name>discovery-server</name>
<description>Demo project for Spring Boot</description><parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Finchley.M8</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
Find the latest version of the dependencies here.
Making Eureka Server
With Spring Boot, it is extremely easy to turn a service into a running Eureka server for service registration and discovery. Let’s start with the main class:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@EnableEurekaServer
@SpringBootApplication
public class DiscoveryServerApplication {
public static void main(String[] args) {
SpringApplication.run(DiscoveryServerApplication.class, args);
}
}
The only key part here is the @EnableEurekaServer annotation. This turns the application into a Eureka server.
Finally, we need to insert some configuration for Eureka server as well:
#Port for Registry service
server.port=8761
#Service should not register with itself
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
#Managing the logging
logging.level.com.netflix.eureka=OFF
logging.level.com.netflix.discovery=OFF
We just defined the port for the service and the logging level for some classes along with Eureka related properties. Now, we can run our application using the command:
mvn spring-boot:run
When the application is up, we can visit this URL:
http://localhost:8761/
Eureka will be up and running:
For now, that list of application which has registered with Eureka is empty. Once we initialize our microservice, we will see its instance in this service as well.
Spring boot Eureka Server Codebase
Preparing the microservice
For the next part of the system, we will start making the microservice application as well. We are making this before an API Gateway and the authentication service to make clear about the things which will be different later once those components come into the picture.
Setting up the project
Again, let’s quickly set up the project:
We need two dependencies in this project. Let’s have a look at the Maven pom.xml file:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
Find the latest version of the dependencies here.
Making a Controller
For now, we just need a single URL which is part of this microservice and can be reached via an API Gateway which we will be making next. Let’s add this controller now:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication
@RestController
@EnableEurekaClient
public class MicroserviceBeginnersApplication {
public static void main(String[] args) {
SpringApplication.run(MicroserviceBeginnersApplication.class, args);
}
@RequestMapping
public String helloWorld() {
return "Hello World";
}
}
In above class definition, we added @EnableEurekaClient annotation as well. With this, we need to add some more configuration as:
# Application Config
server.port=8081
spring.application.name=Beginner-Microservice
# Eureka Config
eureka.client.eureka-server-port=8761
Finally, we can run our application using the command:
mvn spring-boot:run
When the application is up, we can visit this URL:
http://localhost:8081/
We will see the following output:
Also, notice the change in Eureka server as well. When this application started, it registered itself as a Eureka client and so, it will now appear in the Eureka dashboard as well:
Ignore the warning message and notice the instances listed in the Eureka as registered services. The name which we supplied to the service appears on the Dashboard, so it is important we define specific names for each application only.
Preparing the API Gateway
API Gateway is one of the biggest components in a microservice based application. With Netflix Zuul and Spring Boot, its configuration is super easy to do! Let’s use the Initializr first.
Setting up the project
Again, let’s quickly set up the project:
Here’s the Maven pom.xml file:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Finchley.M8</spring-cloud.version>
</properties>
<dependencies>
<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>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
Zuul Filter
Though not required as of now, it is still a good practice to always make a Zuul Filter in the application. This is due to the reason that in the filer, we can do anything with the incoming requests like analyzing response time, logging, metric etc.
Here is how we can make a simple ZuulFilter:
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.HttpServletRequest;
public class SimpleFilter extends ZuulFilter {
private static Logger log = LoggerFactory.getLogger(SimpleFilter.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;
}
}
Here, we just log the incoming request along with its timestamp and request URL.
Next, we make this application a reverse proxy so that it can forward the requests to other microservices:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
@EnableEurekaClient
@EnableZuulProxy
public class ApiGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(ApiGatewayApplication.class, args);
}
@Bean
public SimpleFilter simpleFilter() {
return new SimpleFilter();
}
}
This is a Eureka client as well, as mentioned by the @EnableEurekaClient annotation. Finally, we will provide configuration for the hello world service in application.properties file:
# Application Config
server.port=8082
spring.application.name=API-Gateway
# Eureka Config
eureka.client.eureka-server-port=8761
# Test service mapping
zuul.routes.hello.path=/hello/**
zuul.routes.hello.serviceId=Beginner-Microservice
Finally, we can run our application using the command:
mvn spring-boot:run
When the application is up, we can visit this URL:
http://localhost:8082/hello/
We will see the following output:
See how this URL returns the same output. This is because API Gateway forwarded the request to the hello world application which responded back to the API Gateway and we finally saw the result.
Notice how all the request and response forwarding has happened with just a few lines of code and configuration.
Conclusion
That is excellent. We saw we can set up microservice-based architecture with Spring Boot and one of the most popular API Gateway of all times, Netflix Zuul. Zuul can take care of routing and filtering on the fly and is an excellent choice for any scale of application.
To protect our microservices, we can restrict access to the microservice port itself on the host machine and allow only the API Gateway to access the microservices directly. This can vary based on your use-case of the architecture.
in last application, API gateway, its showing Bad Request
Could you please provide some more details. Like if service has been registered and all the application are up and running. And also paste full exception.
Very good approach for beginners
Thanks sir very knowledgable article. Would request you kindly write an article for authorization and authentication on zuul end.
Hey, Wonderful and good explanaton
Very simple and very good tutorial.
I was having trouble with Zuul and after several tutorials, yours was the first one to run perfectly.
Thanks a lot
good one
Very nice ,examples and steps . Great posting.
It would be great if you provided a download of your complete source code. I have followed all of the steps, but I can’t get the microservice routing to work. Having the original source code might help determine what step was missed.
The codebase is already there. Forgot to add the link in the article.
https://github.com/JBTAdmin/Spring-Boot
Hey Buddy,
Where’s authentication here ?
Authentication part is not yet written.