Spring Boot Microservices: Registration and Discovery using Eureka
Introduction
In the era of microservices, managing service-to-service communication can be daunting. That’s where the concept of service registration and discovery comes into play, significantly simplifying the interaction between different services in a distributed system. Spring Boot, in combination with Eureka, provides an elegant solution to this challenge. This blog post will guide you through the process of setting up service registration and discovery using Spring Boot and Eureka.
Understanding Service Registration and Discovery
Service Registration and Discovery are key components of a microservices architecture, enabling services to dynamically discover and interact with each other. Here’s how they work:
Service Registration: When a service starts, it registers itself with a service registry, providing metadata such as its network location and health indicators.
Service Discovery: When a service wants to communicate with another service, it consults the service registry to retrieve the network locations of available service instances.
Understanding Eureka in Spring Boot
Eureka, developed by Netflix, is an integral part of the Spring Cloud services suite, designed to handle service registration and discovery effortlessly in a microservices ecosystem. It simplifies the process of microservices communicating with each other in a cloud environment. Eureka consists of two main components: the Eureka Server and Eureka Client.
Eureka Server: The Heartbeat of Service Registration
The Eureka Server acts as a service registry, a phone book for your microservices. When microservices start, they register themselves with the Eureka Server, announcing their presence to the world. The server maintains a catalogue of all available services, which is crucial for service discovery.
Setting up an Eureka Server in Spring Boot is straightforward. You need to add the spring-cloud-starter-netflix-eureka-server dependency and annotate your main application class with @EnableEurekaServer. This minimal setup is enough to get an Eureka Server running.
Eureka Client: Facilitating Service Discovery
On the other side, we have the Eureka Client, embedded within microservices. It’s responsible for registering the service with the Eureka Server and querying the server to discover other services. By using the spring-cloud-starter-netflix-eureka-client dependency and annotating the application class with @EnableEurekaClient, any Spring Boot application can become an Eureka Client.
The client regularly sends heartbeats to the Eureka Server to signify its availability. When a client needs to communicate with another service, it asks the Eureka Server for the service’s instances and details, enabling it to perform its operations.
Implementing Eureka in Spring Boot
What we will build?
We can use Netflix Eureka Server to create a Service Registry and make our microservices (spring-boot-eureka-client-rest-api and spring-boot-eureka-consumer-client-rest-api) as Eureka Clients so that as soon as we start a microservice it will get registered with Eureka Server automatically with a logical Service ID. Then, the other microservices, which are also Eureka Clients, can use Service ID to invoke REST endpoints.
Here is the sequence diagram illustrating the process of service registration and discovery in Spring Boot using Eureka:
Setting Up the Eureka Server
Let us create a Service Registry using Netflix Eureka which is nothing but a SpringBoot application with an Eureka Server starter.
- Add Dependencies: Include spring-cloud-starter-netflix-eureka-server in your build configuration. Here is the pom.xml file for your reference:
<?xml version="1.0" encoding="UTF-8"?>
<project
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.3</version>
<relativePath/>
<!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>spring-boot-eureka-service-registry-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-boot-eureka-service-registry-demo</name>
<description>Demo project for Spring Boot Service Registration With Eureka</description>
<properties>
<java.version>17</java.version>
<spring-cloud.version>2023.0.0</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>
</project>
2. Annotate Application: Use @EnableEurekaServer on your main application class. We need to add @EnableEurekaServer annotation to make our SpringBoot application an Eureka Server-based Service Registry.
package com.example.reet.service.registry;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@EnableEurekaServer
@SpringBootApplication
public class SpringBootEurekaServiceRegistryDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootEurekaServiceRegistryDemoApplication.class, args);
}
}
3. Configure Properties: Define necessary configurations in application.properties or application.yml. By default, each Eureka Server is also an Eureka client and needs at least one service URL to locate a peer. As we are going to have a single Eureka Server node (Standalone Mode), we are going to disable this client-side behaviour by configuring the following properties in the application.yml file.
spring:
application:
name: spring-boot-eureka-service-registry-demo
server:
port: 8761
eureka:
client:
register-with-eureka: false
fetch-registry: false
4. Launch Eureka Server: Netflix Eureka Service provides a UI where we can see all the details about registered services.
Now run SpringBootEurekaServiceRegistryDemoApplication and access http://localhost:8761 which will display the UI similar to the below screenshot.
Setting Up a Eureka Client
- Add Client Dependency: Add spring-cloud-starter-netflix-eureka-client to your project. Here is the pom.xml file for your reference:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example.spring.crud</groupId>
<artifactId>spring-boot-eureka-client-rest-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-boot-eureka-client-rest-api</name>
<description>Demo project for Spring Boot Eureka Service Client</description>
<properties>
<java.version>17</java.version>
<spring-cloud.version>2023.0.0</spring-cloud.version>
</properties>
<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>
<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.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</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>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
2. Configure Client Properties: Specify the Eureka Server’s URL and other settings in your configuration file.
With spring-cloud-starter-netflix-eureka-client on the classpath, we just need to configure the eureka.client.service-url.defaultZone property in application.properties or application.yml to automatically register with the Eureka Server as shown below:
spring:
application:
name: spring-boot-eureka-client-rest-api
server:
port: 8083
eureka:
instance:
hostname: localhost
client:
register-with-eureka: true
fetch-registry: true
service-url:
default-zone: http://localhost:8761/eureka
When a service is registered with Eureka Server it keeps sending heartbeats for certain intervals. If the Eureka server doesn’t receive a heartbeat from any service instance it will assume the service instance is down and take it out from the pool.
3. Run spring-boot-eureka-client-rest-api Eureka Client: With this configuration in place, start spring-boot-eureka-client-rest-api and visit http://localhost:8761.
We should see that spring-boot-eureka-client-rest-api is registered with SERVICE ID as SPRING-BOOT-EUREKA-CLIENT-REST-API. We can also notice the status as UP(1) which means the services are up and running and one instance of spring-boot-eureka-client-rest-api is running.
Similarly, we will set up spring-boot-eureka-consumer-client-rest-api and after starting this app, we can see that spring-boot-eureka-consumer-client-rest-api is registered with SERVICE ID as SPRING-BOOT-EUREKA-CONSUMER-CLIENT-REST-API.
Suppose we want to invoke the spring-boot-eureka-client-rest-api REST endpoint from the spring-boot-eureka-consumer-client-rest-api. We can use RestTemplate to invoke the REST endpoint.
We can register RestTemplate as a Spring bean with @LoadBalanced annotation:
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
The RestTemplate with @LoadBalanced annotation will internally use Ribbon LoadBalancer to resolve the ServiceID and invoke the REST endpoint using one of the available servers.
private final RestTemplate restTemplate;
private static HttpEntity<?> getHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.set("Accept", MediaType.APPLICATION_JSON_VALUE);
return new HttpEntity<>(headers);
}
@GetMapping("/message")
public String getMessage() throws RestClientException {
String baseUrl = "http://SPRING-BOOT-EUREKA-CLIENT-REST-API/hello";
ResponseEntity<String> response = null;
try {
response = restTemplate.getForEntity(baseUrl, String.class);
} catch (Exception ex) {
System.out.println(ex.getMessage());
}
return Objects.requireNonNull(response).getBody();
}
Note: If multiple instances of spring-boot-eureka-client-rest-api are running then also there will be no issue as it is load balanced and one of the running instances of spring-boot-eureka-client-rest-api will be invoked.
When we run the spring-boot-eureka-consumer-client-rest-api and hit the endpoint http://localhost:8084/message it returns Hello World as a response after invoking spring-boot-eureka-client-rest-api http://localhost:8083/hello endpoint.
Benefits of Using Eureka in Spring Boot
- Dynamic Service Registration and Discovery: Eureka simplifies the dynamic discovery of services, allowing for flexible, resilient microservices communication.
- Load Balancing: Eureka supports load balancing, distributing requests efficiently across available service instances.
- Fault Tolerance: Eureka clients can cache service information, enabling them to operate even when the Eureka Server is unavailable.
Conclusion
Integrating Eureka into Spring Boot applications paves the way for efficient service registration and discovery, which are crucial in a microservices architecture. This combination not only enhances service-to-service communication but also contributes to the overall resilience and scalability of your application. By mastering Eureka in Spring Boot, developers can focus more on business logic, leaving the complexities of service discovery and registration to this robust, proven solution. Whether you’re transitioning to microservices or optimizing existing systems, Eureka with Spring Boot is a powerful ally in your microservices journey.
The source code of this blog is available on the Eureka Server, client-API and consumer client API.
Happy Learning !!