Fading Coder

An Old Coder’s Final Dance

Home > Tech > Content

Building Service Registration and Discovery with Spring Boot and Spring Cloud Eureka

Tech 2

Overview

Eureka, created by Netflix, is a registry for service discovery in microservice architectures. Each provider registers itself with Eureka, while consumers query Eureka to locate instances. Although Netflix has ended active maintenance of Eureka 2.x, the Eureka 1.x implementation in Spring Cloud remains widely used and stable in production. Alternatives like Nacos and Consul offer broader capabilities, but this guide focuses on a minimal Eureka-based setup.

This example builds a small system:

  • Two Eureka Server nodes (ports 7001 and 7002) forming a cluster
  • Two provider instances (ports 8001 and 8002) registered to Eureka
  • One consumer (port 8087) that discovers and calls the providers with client-side load balancing

Version Alignment

Spring Cloud versions are tight coupled with Spring Boot. For a Hoxton-based stack, use Spring Boot 2.2.x. The parent BOM coordinates these versions for all modules.

Parent POM snippet:

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-dependencies</artifactId>
      <version>2.2.2.RELEASE</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-dependencies</artifactId>
      <version>Hoxton.SR1</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>

Project Layout

Create a multi-module Maven project:

  • registry-7001 (Eureka Server node A)
  • registry-7002 (Eureka Server node B)
  • student-svc-8001 (provider A)
  • student-svc-8002 (provider B)
  • student-consumer-8087 (consumer)

Eureka Server Cluster

Add a module for an Eureka Server. The second node will reuse the same code with different configuraton.

POM dependencies:

<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
  </dependency>
  <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>

Node A configuration (application.yml):

server:
  port: 7001

eureka:
  client:
    register-with-eureka: false
    fetch-registry: false
    service-url:
      defaultZone: http://eureka-b.local:7002/eureka
  instance:
    hostname: eureka-a.local
  server:
    enable-self-preservation: false

Main class:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

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

For node B, duplicate the module or profile and change:

  • server.port to 7002
  • eureka.instance.hostname to eureka-b.local
  • eureka.client.service-url.defaultZone to http://eureka-a.local:7001/eureka

Add host entries for local testing:

127.0.0.1  eureka-a.local
127.0.0.1  eureka-b.local

Start both registry nodes and confirm each shows the other under the "DS Replicas" section in the Eureka dashboard.

Provider Service (clustered on 8001/8002)

Create a web service that registers with Eureka. We’ll run two instances with different ports.

POM dependencies:

<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
  </dependency>
</dependencies>

Instance A configuration (application.yml on port 8001):

server:
  port: 8001

spring:
  application:
    name: STUDENT-SVC

eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://eureka-a.local:7001/eureka,http://eureka-b.local:7002/eureka
  instance:
    instance-id: student-svc-8001
    prefer-ip-address: true

REST controller:

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;

import java.time.format.DateTimeFormatter;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;

@RestController
@RequestMapping("/students")
public class StudentsApi {

  @Value("${server.port}")
  private String port;

  @GetMapping
  public List<String> getAll() {
    return Arrays.asList("Ada", "Grace", "Linus", "Ken");
  }

  @GetMapping("/version")
  public String version() {
    String ts = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmm"));
    return "port=" + port + ",build=" + ts;
  }
}

Main class:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

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

For instance B, run a second module or process with:

server:
  port: 8002
spring:
  application:
    name: STUDENT-SVC

eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://eureka-a.local:7001/eureka,http://eureka-b.local:7002/eureka
  instance:
    instance-id: student-svc-8002
    prefer-ip-address: true

After starting both providers, verify they appear in the registry under the same application name (STUDENT-SVC) with two healthy instances.

Consumer (8087) with Ribbon Load Balancing

Create a consumer that discovers STUDENT-SVC and calls it via service name using a load-balanced RestTemplate.

POM dependencies:

<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.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
  </dependency>
</dependencies>

Configuration (application.yml):

server:
  port: 8087

spring:
  application:
    name: student-consumer

eureka:
  client:
    fetch-registry: true
    register-with-eureka: false
    service-url:
      defaultZone: http://eureka-a.local:7001/eureka,http://eureka-b.local:7002/eureka

RestTemplate configuration and consumer controller:

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@Configuration
class ClientHttpConfig {
  @Bean
  @LoadBalanced
  RestTemplate restTemplate() {
    return new RestTemplate();
  }
}

@RestController
@RequestMapping("/students")
class StudentClientController {
  private final RestTemplate http;

  StudentClientController(RestTemplate http) {
    this.http = http;
  }

  @GetMapping("/version")
  public String version() {
    // Ribbon chooses an instance of STUDENT-SVC and forwards the request
    return http.getForObject("http://STUDENT-SVC/students/version", String.class);
  }
}

Main class:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

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

Running the System

  • Start registry-7001 and registry-7002
  • Start student-svc-8001 and student-svc-8002
  • Start student-consumer-8087
  • Call http://localhost:8087/students/version repeatedly; you should see alternating outputs reflecting ports 8001 and 8002, demonstrating client-side load balancing

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.