Spring Cloud Eureka
Introduction
In the world of microservices architecture, applications are composed of multiple small, independent services that communicate with each other to fulfill business requirements. One of the challenges in this architecture is service discovery: How does one service find and communicate with another service when there might be multiple instances running on different hosts and ports?
Spring Cloud Eureka is a solution to this problem. It's an implementation of the Service Discovery Pattern based on Netflix's Eureka project, integrated into the Spring ecosystem. Eureka allows microservices to register themselves with a central server (Eureka Server) and discover other services without knowing their exact location.
How Eureka Works
Eureka uses a client-server model:
- Eureka Server: Acts as a registry that maintains information about all client service applications
- Eureka Client: Service instances that register themselves with the Eureka server and query it to discover other services
The basic workflow is:
- Service instances register (and renew) their information with the Eureka Server
- Eureka Server maintains a registry of service instances
- Clients can query the registry to find other services
- Eureka provides built-in load balancing and failover through integration with Spring Cloud LoadBalancer
Setting Up a Eureka Server
Let's create a simple Eureka server application.
Step 1: Create a Spring Boot project
You can use Spring Initializr (https://start.spring.io/) to create a new project with the following dependencies:
- Spring Web
- Eureka Server
Step 2: Configure the Eureka Server
Add the @EnableEurekaServer
annotation to your main application class:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
Step 3: Configure application properties
In your application.properties
(or application.yml
) file, add the following configuration:
# Application name
spring.application.name=eureka-server
# Server port
server.port=8761
# Don't register the server itself as a client
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
# Logging settings
logging.level.com.netflix.eureka=OFF
logging.level.com.netflix.discovery=OFF
Alternatively, in YAML format:
spring:
application:
name: eureka-server
server:
port: 8761
eureka:
client:
register-with-eureka: false
fetch-registry: false
logging:
level:
com.netflix.eureka: OFF
com.netflix.discovery: OFF
Step 4: Run the Eureka Server
When you run the application, you can access the Eureka dashboard at http://localhost:8761
. Initially, you'll see no instances registered because we haven't created any Eureka clients yet.
Creating a Eureka Client
Now, let's create a simple service that registers with our Eureka Server.
Step 1: Create a Spring Boot project
Use Spring Initializr to create a new project with the following dependencies:
- Spring Web
- Eureka Client
Step 2: Configure the Eureka Client
Add the @EnableEurekaClient
annotation to your main application class:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
public class ProductServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ProductServiceApplication.class, args);
}
}
Step 3: Configure application properties
In your application.properties
file, add the following:
# Application name (will be used as the service identifier)
spring.application.name=product-service
# Random port assignment
server.port=0
# Eureka server location
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
# Unique instance ID
eureka.instance.instance-id=${spring.application.name}:${random.uuid}
Using server.port=0
means Spring Boot will assign a random available port, which is useful when running multiple instances.
Step 4: Create a REST controller
Let's create a simple REST controller to expose some endpoints:
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/products")
public class ProductController {
@Value("${server.port}")
private String serverPort;
@GetMapping
public String getProducts() {
return "List of products from instance running on port: " + serverPort;
}
@GetMapping("/info")
public String info() {
return "Product Service running on port: " + serverPort;
}
}
Step 5: Run multiple instances
When you run the application, it will register itself with the Eureka server. You can run multiple instances of this service, and each will register as a separate instance of the same service.
If you refresh the Eureka dashboard at http://localhost:8761
, you should see the PRODUCT-SERVICE
registered as an instance.
Service Discovery in Action
Now that we have a Eureka server and at least one client service, let's create another service that will discover and communicate with our product service.
Step 1: Create an Order Service
Create a new Spring Boot application with the following dependencies:
- Spring Web
- Eureka Client
- OpenFeign (for easy REST client)
Step 2: Configure the application
Add the necessary annotations to enable Eureka Client and Feign clients:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
}
Step 3: Configure application properties
spring.application.name=order-service
server.port=8080
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
Step 4: Create a Feign client to communicate with Product Service
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
@FeignClient(name = "product-service")
public interface ProductServiceClient {
@GetMapping("/api/products")
String getProducts();
@GetMapping("/api/products/info")
String getProductServiceInfo();
}
Step 5: Create a controller that uses the Feign client
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/orders")
public class OrderController {
private final ProductServiceClient productServiceClient;
@Autowired
public OrderController(ProductServiceClient productServiceClient) {
this.productServiceClient = productServiceClient;
}
@GetMapping
public String createOrder() {
String productResponse = productServiceClient.getProducts();
return "Order created with products: " + productResponse;
}
@GetMapping("/product-info")
public String getProductInfo() {
return "Product Info: " + productServiceClient.getProductServiceInfo();
}
}
Step 6: Test the Discovery and Communication
- Start your Eureka Server
- Start multiple instances of the Product Service
- Start the Order Service
- Access the Order Service endpoints:
http://localhost:8080/api/orders
http://localhost:8080/api/orders/product-info
You'll notice that calls to the Product Service are automatically load-balanced across the available instances. This happens because Feign integrates with Spring Cloud LoadBalancer, which uses Eureka's registry to know about all available instances.
Eureka High Availability
In a production environment, having a single Eureka server would create a single point of failure. Eureka is designed to run in a peer-to-peer mode where multiple Eureka servers communicate with each other to maintain a distributed registry.
Setting up a Eureka Server Cluster
You can create a cluster by configuring each server to register with the others. Here's an example of a two-node configuration:
application.yml for eureka-server-1:
spring:
application:
name: eureka-server
server:
port: 8761
eureka:
instance:
hostname: eureka-server-1
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://eureka-server-2:8762/eureka/
application.yml for eureka-server-2:
spring:
application:
name: eureka-server
server:
port: 8762
eureka:
instance:
hostname: eureka-server-2
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://eureka-server-1:8761/eureka/
You'll need to update your hosts file to map the hostnames eureka-server-1
and eureka-server-2
to localhost for testing on a single machine.
Common Configurations and Features
Heartbeat Mechanism
Eureka uses heartbeats to determine if a client is still alive:
# How often the client sends heartbeats to server to indicate that it is alive
eureka.instance.lease-renewal-interval-in-seconds=30
# The time the server waits since last heartbeat before removing the instance
eureka.instance.lease-expiration-duration-in-seconds=90
Metadata for Instances
You can add custom metadata to your service instances:
eureka.instance.metadata-map.zone=zone1
eureka.instance.metadata-map.version=1.0.0
eureka.instance.metadata-map.profile=production
Self-Preservation Mode
Eureka has a self-preservation mode to handle network partitions:
# Disable self-preservation (not recommended in production)
eureka.server.enable-self-preservation=false
In production, you should keep self-preservation enabled as it helps during network issues by not removing potentially healthy instances.
Summary
Spring Cloud Eureka is a powerful tool for service discovery in a microservices architecture. It provides:
- A centralized registry for service instances
- Automatic service registration and discovery
- Load balancing through integration with Spring Cloud LoadBalancer
- High availability through peer-to-peer replication
- Health monitoring through heartbeats
In this tutorial, we've covered:
- Setting up a Eureka Server
- Creating Eureka Client services
- Inter-service communication using Feign and service discovery
- Eureka high availability setup
- Common configuration options
With Eureka, your microservices can find and communicate with each other dynamically without hardcoding hostnames and ports, making your system more resilient and scalable.
Additional Resources
Exercises
-
Create a three-service system using Eureka:
- A product service that provides product details
- An inventory service that tracks product availability
- An order service that communicates with both services to create orders
-
Implement a circuit breaker pattern using Spring Cloud Circuit Breaker with your Eureka services to handle failures gracefully.
-
Set up a three-node Eureka server cluster and configure your services to connect to the cluster.
-
Implement custom health indicators for your services that integrate with Eureka's health checks.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)