Skip to main content

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:

  1. Eureka Server: Acts as a registry that maintains information about all client service applications
  2. 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:

java
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:

properties
# 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:

yaml
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:

java
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:

properties
# 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:

java
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:

java
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

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

java
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

java
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

  1. Start your Eureka Server
  2. Start multiple instances of the Product Service
  3. Start the Order Service
  4. 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:

yaml
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:

yaml
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:

properties
# 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:

properties
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:

properties
# 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:

  1. Setting up a Eureka Server
  2. Creating Eureka Client services
  3. Inter-service communication using Feign and service discovery
  4. Eureka high availability setup
  5. 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

  1. 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
  2. Implement a circuit breaker pattern using Spring Cloud Circuit Breaker with your Eureka services to handle failures gracefully.

  3. Set up a three-node Eureka server cluster and configure your services to connect to the cluster.

  4. 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! :)