Microservices with Spring Boot, Docker and Kubernetes Part 1

WeiTang Lau
8 min readOct 22, 2020

Microservice over Monolithic

Monolithic Architecture

Prior to Microservice architecture, most companies adopted the Monolithic architecture. Monolithic architecture provides a centralised code base which is easy to develop and test. Furthermore, it provides ease in deployment as we are dealing with only one executable file, e.g. single jar file for Spring Boot application. However, its weakness starts to emerge as codebase grows and during scaling of the application. Monolithic applications tend to be large, which causes long deployment time and scaling of multiple instances of the application. The analogy that I like to use to describe Monolithic applications is having one superhero to save the world.

Microservice Architecture

Microservice architecture provides highly extensible code base and clear segregation of the business logics. In Microservice architecture, the application is made up of multiple microservices. You can view each of these microservices as a component of your business logic. These components are identified by dividing the business logics using bounded context. These bounded contexts can be developed and viewed as a microservice. For instance, an e-commerce application can be divided into multiple microservices. These microservices include Payment, Order, Customer and more. Moreover, due to the recent popularity among companies in having smaller teams, each of these microservices can be developed by a small team. In terms of deployment and scaling, each of the microservices can be individually deployed and scaled based on business needs. However, because the microservices are individually deployed, there is a need to have a mechanism that allow each microservices to discovery the presence of other microservices. Thus, in this guide, we will develop a simple e-commerce Spring Boot application adopting the Microservice architecture. In part 2, we will demonstrate why Docker and Kubernetes are so popular for Microservice applications.

Microservices using Spring Boot

The architectural of our simple e-commerce application is shown below. In our overly simplified example, we only have one customer, and all the payments and orders are made by him. There are three main components, namely API Gateway, Service Discovery Server, and Microservices.

Architectural Diagram

API Gateway

API Gateway provides a single point-of-entry to the application. It redirects the request received to the appropriate microservices. This redirection is transparent to the user. Thus, allowing the user to use the application via the same host / url.

Service Discovery Server

Service Discovery Server applies the service discovery mechanism to allow microservices to communicate with each other. Each microservices will register with the Service Discovery Server such that it can be discovered by other microservices.

Microservice

Microservice is a component of the application. It usually contains the implementation details and logic of the application. It will register with the Service Discovery Server such that other microservices can call its APIs. Strictly speaking, the API Gateway and Service Discovery Server are microservice.

All the source code will be available at my github repo.

Now that we have a clear understanding of the application that we will developing, let’s begin!

Project Setup using Spring Initializr

Creating a Spring application from scratch is effortless using Spring Initializr. All we have to do is to fill in the relevant details and dependencies of the application.

Project — The type of project, Maven or Gradle. In this example, we will be using Gradle.

Language — The programming language of the application. We will be using Java 8 as our programming language.

Spring Boot — The spring boot version. Recommended to use the stable release version (those without any suffix). We will be using version 2.3.4.

Group — It can be anything you want, usually the company name followed by application name. Keep it consistent throughout the all other projects.

Artifact & Name — The name of the application. Make sure that the words are separated by an hyphen and not underscore. This is because java.net.URI doesn’t understand underscore and will cause an error.

Description — As the name suggests.

Package name —Keep it as it is

Packing — The format of the application. We will create use WAR if the project is used as a web application and JAR otherwise.

Java — Java version

Dependencies — The dependecies or packages to be imported for this project

After the details have been setup, click generate to generate the source code for the project and unzip all projects into your work directory.

API Gateway

API Gateway has two main dependencies, namely Eureka Discovery Client and Gateway.

As mentioned above, API Gateway redirects requests it receives from the user. Thus, it must be able to discover other microservices as well, as such API Gateway is an Eureka client. We will be using Eureka as our Service Discovery Server. Thus, importing the Eureka Discovery Client dependecy.

Moreover, for API Gateway to acts as the Gateway, it has to import the Gateway dependency.

API Gateway Project Setup

Service Discovery Server

Service Discovery Server provides the Service Discovery mechanism to our application. Importing the Eureka Server dependency enables this functionality.

Service Discovery Server Project Setup

Payment, Order and Customer Services

Payment, Order and Customers services have the same dependencies. Similar to API Gateway, these services are Eureka Client. Thus, importing the Eureka Discovery Client dependency.

Communication between microservices are done using RESTful APIs. Spring Web dependency enables the microservices to send a REST API request.

Microservices Project Setup

Now that we have generated all the source code, unzip the folders and put all in a your work directory. Use the editor of your choice. I will be using Intellij for this demonstration.

Coding

After you have setup your project, let’s begin coding!

Service Discovery Server

How do you specify that this application is an Eureka server? Thankfully, there is an annotation that we can use in our Main class, @EnableEurekaServer. This annotation tells Spring Boot that this is an Eureka server and it will setup everything under the hood.

@SpringBootApplication
@EnableEurekaServer
public class ServiceDiscoveryServerApplication {

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

}

Under resource folder, create application.yml file with the following configurations.

spring:
application:
name: service-discovery-server
server:
port: 8761
eureka:
instance:
hostname: localhost
client:
registerWithEureka: false
fetchRegistry: false
serviceUrl:
defaultZone:
http://${eureka.instance.hostname}:${server.port}/eureka/

After setting up the project, start the project using Gradle’s bootRun task as shown below.

Intellij Gradle bootRun

Similarly, you can use the following command.

./gradlew bootRun

You will see the following result to indicate successful starting of Eureka server at http://localhost:8761.

Eureka server started at localhost:8761

API Gateway

The key feature of API Gateway is that it is able to redirect, with built-in Load Balancing functionality, to the respective microservice based on the request it receives. You can specify such behaviours at application.yml file.

The configurations below tell Spring Boot the routes to the specify microservices based on the predicate of the path. Notice that in the uri section, the service name is prepended with lb:// . This enabled the load balancing feature to the respective service. If you have multiple instances of the microservice, it will automatically load balance the traffic.

spring:
cloud:
gateway:
discovery:
locator:
enabled: true
routes:
- id: payment-service
uri: lb://payment-service
predicates:
- Path=/api/payment**
- id: order-service
uri: lb://order-service
predicates:
- Path=/api/order**
- id: customer-service
uri: lb://customer-service
predicates:
- Path=/api/customer**
application:
name: api-gateway
server:
port: 8080
eureka:
client:
serviceUrl:
defaultZone: ${EUREKA_SERVER:http://localhost:8761/eureka}

In the API Gateway’s Main class, add the @EnableDiscoveryClient and @Autowired DiscoveryClient variable. Even though we can use @EnableEurekaClient annotation, this prepares us to use Kubernetes in Part 2 of the guide. Moreover, the autowired shown below injects the service discovery to the project.

@SpringBootApplication
@EnableDiscoveryClient

public class ApiGatewayApplication {

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

@Autowired
private DiscoveryClient discoveryClient;

}

Now start up the API Gateway using Gradle’s bootRun command and enter http://localhost:8080 to see the Gateway is up. Furthermore, if you visit the Eureka server again, you will see that the API Gateway has been registered. Now other eureka clients can communicate with API Gateway.

Payment, Order and Customer Services

As this is a guide on microservices, I won’t be explaining how @RestController or @Service annotations work. For more information you can visit here.

As these services serve the same purpose, which serves the business logic. Thus, the setup for these three services are similar, just with minor adjustments.

In the application.yml file, add these configurations

spring:
application:
name: payment-service
server:
port: 8081
forward-headers-strategy: framework
eureka:
client:
serviceUrl:
defaultZone: ${EUREKA_SERVER:http://localhost:8761/eureka}

In the main file, the code added is similar to API Gateway. The only difference now is having a bean instead of a discoveryClient variable. This is necessary for sending REST API requests.

@SpringBootApplication
@EnableDiscoveryClient
public class PaymentServiceApplication {

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

@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}

Proceed to develop your payment logic using the same path indicated in API-Gateway /api/payment . As such, any request with the prefix will be routed to Payment Service microservice. After you have started the payment service, the instance will be captured in the Eureka server.

Now when you enter http://localhost/api/payment , you will see that the request will be routed to the Payment Service.

Create the Order and Customer Services with the same setting and configuration as Payment Service.

REST API Request

As mentioned above, our customer-service interacts with order-service and payment-service. Thus, we have to adjust our customer-service to send the REST request to the relevant microservices.

@Autowired
private RestTemplate restTemplate;

public Customer getCustomerDetails() {
OrderList orders = restTemplate.getForObject(
"http://order-service/api/order", OrderList.class);
PaymentList payments = restTemplate.getForObject(
"http://payment-service/api/payment", PaymentList.class);
return new Customer(0, orders, payments);
}

The @Autowired annotation here injections the discovery client into the restTemplate variable. In other words, it contains the location of other microservices. Due to the discovery client injection, we are able to address the microservice by name when sending the HTTP request. For instance, instead of sending http://DOMAIN/PATH/TO/RESOURCE , we can simply use the name of the microservice as the domain.

Now that the project has been setup, enter http://localhost:8761 to ensure that the services are registered. Following which enter http://localhost:8080/api/customer to verify that the customer-service requested resource from order-service and payment-service.

In this Part 1 guide, I have went through the benefits of Microservice architecture over Monolithic architecture. This guide also provided the code and configuration to start up your Microservice Spring Boot Application. In the Part 2 guide, I will go through how we can utilise Docker and Kubernetes for our Microservice Spring Boot application.

--

--

WeiTang Lau

An undergraduate Computer Science student at National University of Singapore interested in Backend Development