Microservice Architecture Using Eureka Server

Introduction

In this article, we are going to learn about microservice architecture in Java. Microservice is a popular architecture used to build software applications. In a microservice architecture, each service can be developed, deployed, and scaled independently. This can be useful to build applications much faster as compared to monoliths as it gets complicated over time. Each service in microservices communicates with each other using REST API or messaging queue.

What are Microservices?

Microservices, as the name implies, refer to a software architecture approach where an application is broken down into a collection of small, self-contained services. These services are designed to perform specific functions or tasks within the larger application context. What distinguishes microservices from traditional monolithic architectures is their emphasis on modularity and independence.

Microservice Architecture Using Spring Cloud
Fig. Microservice Architecture Using Spring Cloud

1. Eureka Server

Eureka Server is an application that contains information about all micro services including the name of the service, port, and IP address. Each microservice has to register itself with the Eureka Server. Each service sends a heartbeat to the Eureka Server that helps the Eureka Server to check if the service is running or not.

Eureka Server Dashboard
Fig. Eureka Server Dashboard

As you can see in this image we have 2 microservices registered with the Eureka Server i.e. API Gateway and Auth Service.

Create an Eureka Server project that includes this dependency;

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

In the Application.properties file add this property;

spring.application.name=eureka-server
server.port=8761
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false

The Eureka Server will run on port 8761.

register-with-eureka=false and client.fetch-registry=false

This means we do not want to register Eureka with itself.

Then we have to add @EnableEurekaServer annotation like this;

@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}

Related read: Spring Boot Microservices and Eureka Server: Setup and Benefits

Watch Our Video: Exploring Microservice Architecture for Healthcare Software

 2. API Gateway

Each microservice runs on a different port so if we have 12 microservices running on different ports. We have different URLs for each service. Instead of having different URLs and ports, what if we have only one URL which will then take care of routing the request to that service.

This can be achieved using API Gateway. API Gateway provides features like routing and filtering the request. API Gateway uses reactive, non-blocking architecture which means it will take all requests sent to it asynchronously and then route the request to service and then return the response back to the client. It acts as a proxy between clients and microservices.

Three Terminology Used in API Gateway

  • Route: Route is basic functionality provided by the Gateway. It contains the ID, destination URL and filter.
  • Predicate: Predicate is a Java function. To forward the request to service predicate should match.
  • Filter: Filter is used to filter the request before sending it to the services. We can use authentication here to protect the endpoints.

Create an API Gateway application which includes the following dependencies;

 <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

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

Add @EnableDiscoveryClient which will help the service to connect with Eureka Server;

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

To add routing to different microservices in the API Gateway we will make use of RouteLocator;

@Configuration
public class ApiGatewayConfiguration {
@Autowired
private ApiFilter apiFilter;
 
@Bean
public RouteLocator gatewayRouter(RouteLocatorBuilder builder)
{
return builder.routes() .route(p->p.path("/auth/*").uri("lb://auth-service") .route(p->p.path("/test").filters(f->f.filter(apiFilter)).uri("lb://auth-service")).build();
}
}

Every request to the API Gateway will come to the routing locator. The route locator will try to match the request pattern and then transfer the request to the respective services. We can also use filters before transferring the request to service which can include authorization of users.

@Component
public class ApiFilter implements GatewayFilter {
private static RestTemplate restTemplate;
static {
restTemplate = new RestTemplate();
}

@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request= exchange.getRequest();
List<String> unsecuredAPIs = List.of("/auth/signup","/auth/login","/auth/validate");
Predicate<ServerHttpRequest> isAPISecured = r-> unsecuredAPIs.stream().noneMatch(uri->r.getURI().getPath().contains(uri));
if(isAPISecured.test(request))
{
if(!request.getHeaders().containsKey("Authorization"))
{
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
 
String token = getJwtTokenFromRequest(request);
 
ResponseEntity<Map> responseEntity = restTemplate.getForEntity("http://localhost:5001/auth/validate/"+token,Map.class);
Map<String,Object> validationResponse=(Map<String, Object>) responseEntity.getBody();
boolean isValid = Boolean.parseBoolean(validationResponse.getOrDefault("isValid", "false").toString());
 
if (!isValid) {
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
}
return chain.filter(exchange);
}
 
 
private String getJwtTokenFromRequest(ServerHttpRequest request)
{
List<String> headers = request.getHeaders().getOrEmpty("Authorization");
if(headers.isEmpty())
{
return null;
}
String bearerToken = headers.get(0);
 
if(StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer"))
{
return bearerToken.substring(7,bearerToken.length());
}
return null; 
}
}

We have to implement GatewayFilter. It has a method named filter which has two parameters named ServerWebExchange exchange, and GatewayFilterChain. We will take request information from the exchange.getRequest();

Then will take out headers from the request. To verify the user we will send a request to the Auth Service which will validate the token. If it is invalid we return the request from Filter with an UnAuthorized Error otherwise we will transfer the request to the service.

Experience Excellence in Software Development - Hire Our Expert Team Today!

3. Services

Each service in the architecture diagram is a microservice built using Spring boot and Spring cloud-config. Each service should follow the Single Responsibility principle. One service failure should not affect the other services. Each service can have its own database.

They should be deployed independently. Services can be written in different languages. They may have different databases one service may use MySQL and another may use MongoDB.

To create a service it should have the following dependencies;

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

To enable the service to connect to the Eureka Server add annotation @EnableEurekaClient.

4. Communication

Service can communicate with others using Rest Template or by using any messaging queue.

We can divide the communication between synchronous and asynchronous.

In synchronous communication we can use;

  • Rest Template: Communication between microservices using HTTP Request.
  • Open Feign: Feign uses Java Interfaces which eliminates the hard-coded URL to communicate with the service.

In asynchronous communication we can use;

coma

Conclusion

In this blog, we learned about microservice architecture using Eureka Server. By using this architecture developers can break down monolithic applications into smaller services that communicate through API. Spring Cloud, with its rich ecosystem of tools and libraries, simplifies the implementation of microservices by providing essential features like service discovery, load balancing, configuration management, and distributed tracing.

Keep Reading

Keep Reading

Struggling with EHR integration? Learn about next-gen solutions in our upcoming webinar on Mar 6, at 11 AM EST.

Register Now

Let's create something together!