Building Spring Boot Microservices , Monitoring with prometheus and grafana and log aggregation using ELK stack: Part I

Firas Messaoudi
Nerd For Tech
Published in
8 min readJan 15, 2021

Introduction

while developing a number of complex applications (E-commerce,streaming video, banking…), most developers are facing a lot of challenges such as:

-Availabilty: How can we make sure our application will always be available regardless of the behaviour of the users?

-Maintainability: Our application must evolve frequently and respond to quickly to new features without shutting down the whole application.

-Evolution: Technology used to develop the application evovle every day , so we must make sure our application has the ability to adapt to this evolution

And of course a lot of other problems and challenges that we are going to face.

During the first part of this post we are going to find out how the microservice architecture came to solve this issues and satisfy the challenges.

What is the microservice architecture ?

The Microservices architecture offers a simple solution: split the application into small services , called Microservices, which are perfectly autonomous, and expose a REST API that other Microservices can consume.

The figure below represents a simplified overview of the microservice architecture:

Global architecture

How does the microservice architecture responds to the above issues ?

As we can see in the previous image, each microservice is perfectly autonomous and can be deployed on various servers and different instances which make our microservices available and scalable. In that case the microservice responsible of the UI will call one of the instances and receive the same response regardless of the instance chosen.

We don’t have to worry about the maintainability anymore , each microservice works independently from the others , so whenever we want to work on a microservice we don’t have to stop the whole application (the case of the monolothic application) we just have to access the microservice in question.

And due to the various technology offered, each microservice can use a different technology ,microservice A can be in java , microservice B can be in php … the main thing is that each one exposes a rest API. This advantage will give us the ability to take care about the business logic of the application without worrying about the technology.

After we took a quick look about the microservice architecture and its benefits we are going to put all this in practice in order to cover most of the topics.

We are going to use the famous Spring boot framework to create s simple e-commerce application with three microservices (ProductMicroservice ,OrderMicroservice and clientMicroservices).

Creating the microservices

The first step consists of creating three spring initializer projects which will represents our microservices.

These projects will have one class each (Product, order and Client ) and these dependencies for now :

Dependencies

The product Microservice will have two simple operations: find a product by id and get all the product.

The order microservice: Add a new order and find and order by its id.

The client will consume the microservices.

After creating the microservices and their methods we have to see this tree of packages in each microservice:

Package

Don’t worry about the source code you will find the link to the whole code at the end of this course, now we just have to understand each step.

For now we have two spring boot application running independently, everything is familiar so far.

Eureka Server

As we said, for now everything is so familiar, each microservice with its instances is running independently. But we need something to register all this instances.

Eureka service owned by Netflix respond to this need, eureka will register every microservice.

Each microservice registered in eureka server is called eureka client.

Eureka

The figure above how our microservices interact with eureka server.

To set up Eureka server we need to create an eureka microservice and add its configuration.

Eureka service

After adding the dependencies go to application.properties and add this configurations

server.port:9102

spring.application.name=eureka-server

eureka.client.serviceUrl.defaultZone:http://localhost:9102/eureka/

eureka.client.registerWithEureka:false
#set to false because we're not in cluster mode
eureka.client.fetchRegistry:false
#set to false because we're not in cluster mode

Enable Eureka server:

@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {

SpringApplication.run(EurekaServerApplication.class, args);

}

}

Then go to http: // localhost: 9102 /. You will then arrive on a page which presents a certain amount of information about your Eureka server.

This is where you will see the list of Microservices instances that will register.

Eureka Client

In order for the different Microservices to start registering in our Eureka server, we need to add the Eureka client to each of them.

Go to pom.xml and add this dependency:

<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-server</artifactId></dependency>

Then to the application.properties :

eureka.client.serviceUrl.defaultZone: http://localhost:9102/eureka/

This line indicates the URL of Eureka to which to register.

Finally go to each microservice and the annotation @EnableDiscoveryClient:

@SpringBootApplication

@EnableDiscoveryClient

public class ProductApplication {

public static void main(String[] args) {

SpringApplication.run(ProductApplication.class, args);

}



}

Your Microservice now benefits from the Eureka client, which will register your instance on each startup.

Start your service and go to http: // localhost: 9102

Communicate your microservices with Feign

For now the two microservices Product and Order and a client that are running independently and registered on Eureka. But what if we need one of them to communicate with the other. That’s why we will need a service or protocol to assure that.

Feign is an HTTP client that greatly facilitates the call of APIs exposed by the different Microservices. It is therefore capable of creating and executing HTTP requests based on the annotations and information provided

In our case we will need the client microservice to communicate with the product and order microservices.

Let’s start by adding Feign to our pom.xml in the client Microservice

<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency>

In order to activate Feign in this Microservice, go to ClientUiApplication and add the @EnableFeignClients annotation :

@SpringBootApplication@EnableFeignClients("com.ecommerce.clientui")public class ClientUiApplication {public static void main(String[] args) {SpringApplication.run(ClientUiApplication.class, args);}}

The annotation @EnableFeignClients asks Feign to scan the "com. ecommerce .clientui" package to find classes that declare themselves Feign clients.

FeignClient

While the client mis will use feign to retrieve the list of product , we will have to create a Product class in the client mis similar to the one on the product microservice in order to store the objects:

We are going to create a class product under the package DTO.

@Data
public class ProductDTO {

private int id;

private String titre;

private String description;

private String image;

private Double prix;
}

We are now going to create an interface which will group together the requests that we want to pass to the ProductMicroservice. This interface is what we call a proxy, because it is positioned as an intermediate class which makes the link with the external Microservices to be called.

@FeignClient(name = "microservice-product", url = "localhost:9001")
public interface ProductMicroserviceProxy{

@GetMapping(value = "/Products")
List<ProductDTO> findAll();

@GetMapping( value = "/products/{id}")
ProductDTO findById(@PathVariable("id") int id);


}

-Feign will use the information provided to make the appropriate HTTP requests to invoke the ProductMicroservice.

-The first param is the name of the microservice which will be called. This name is already set in the application.properties

- The second parameter is the URL of the Microservice (localhost: 9001)

All that remains is to use this proxy. Go back to the controller:

@RestController
@CrossOrigin("*")
@RequestMapping("/client")
public class ClientController {

@Autowired
private ProductMicroserviceProxy ProductProxy;


@GetMapping("/products")
public List<ProductDTO> findAll(){

List<ProductDTO> list= ProducstsProxy.findAll();
return LIST;

}


@GetMapping("/findById/{id}")
public ProductDTO findByd(@PathVariable int id){

ProductBean prod = ProductsProxy.findById(id);

return prod;
}

Let’s do a quick resume , so far we managed to create 3 microservices (Product, Order and client ) each is registered on Eureka Server and communicating with FeignClient.

We achieved a great step for now but as we can see , we’re still missing some pieces.

ZULL API gateway

ZUUL is going to be a unique entry point for our application. So when Client microservice for example, wants to call one of the Microservices, it will go through ZUUL . which will take care of dispatching, modifying and applying filters and rules to these requests.

ZUUL works natively with Eureka. It retrieves the list of all Microservices available in Eureka and exposes them via the URL: localhost: 9004 / microservice-name .

Thus, to retrieve the list of products, just call: localhost: 9004 / microservice-products/ Products .

You then retrieve the list of products as if you had called Microservice-products directly.

Zuul

We have to create another microservice (Zuul) and register it in eureka.

Add the following properties to zuul microservice properties

server.port 9004
#Eureka
eureka.client.serviceUrl.defaultZone:http://localhost:9102/eureka/

And activate Eureka and Zuul

@SpringBootApplication
@EnableZuulProxy
@EnableDiscoveryClient
public class ZuulServerApplication {

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

Start zuul and verify Eureka you’ll find the zuul microservice among the other microservices.

In order for our client to go through ZUUL, we need to tell the Feign proxies to contact ZUUL and not the Microservices directly.

Then replace the name of the destination Microservice with that of ZUUL

We add / microservice-products / in front of all URIs. Feign will then contact ZUUL with the URI “/ microservice-products / Products”. As ZUUL then extracts the name of the Microservice concerned from the “microservice-products” URL, it can redirect the request to the desired destination.

@FeignClient(name = "zuul-server")
public interface ProductMicroserviceProxy{

@GetMapping(value = "microservice-product/Products")
List<ProductDTO> findAll();

@GetMapping( value = "microservice-product/products/{id}")
ProductDTO findById(@PathVariable("id") int id);


}

Conclusion

The first part of this post has already covered how we create a microservice , register it in Eureka , communicate with other microservice and how to create an API gateway.

In the next part we will discover how we can monitor our microservices with Prometheus and Grafana.

As promissed this is the link to the github repository for this part

--

--