google-site-verification: google61fe8ba583a51912.html
Service Discovery with Eureka in Java: Complete Guide

Eureka is a REST-based service registry for locating services in cloud environments. It enables service discovery in microservices architecture, allowing services to find and communicate with each other without hard-coded hostnames and ports.

Overview

  • Eureka Server: Service registry that maintains a list of available service instances
  • Eureka Client: Services that register themselves with Eureka and discover other services
  • Service Discovery: Dynamic location of service instances in a distributed system

Dependencies

Maven Dependencies

<!-- Spring Cloud Dependencies Management --> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>2023.0.0</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <!-- Eureka Server --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> <!-- Eureka Client --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <!-- Spring Boot Web (for REST controllers) --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Spring Boot Actuator (for health checks) --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <!-- Config Client (if using Spring Cloud Config) --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency>

Gradle Dependencies

plugins { id 'org.springframework.boot' version '3.2.0' id 'io.spring.dependency-management' version '1.1.4' } ext { set('springCloudVersion', "2023.0.0") } dependencyManagement { imports { mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}" } } dependencies { implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-server' implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client' implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-actuator' implementation 'org.springframework.cloud:spring-cloud-starter-config' }

Eureka Server Implementation

Example 1: Basic Eureka Server

package com.example.eurekaserver; 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); } }

Example 2: Eureka Server Configuration

# application.yml for Eureka Server server: port: 8761 spring: application: name: eureka-server eureka: client: register-with-eureka: false # Don't register itself with Eureka fetch-registry: false # Don't fetch the registry from other Eureka servers service-url: defaultZone: http://localhost:8761/eureka/ server: enable-self-preservation: true # Prevents eviction of instances in case of network issues eviction-interval-timer-in-ms: 10000 # How often to check for expired instances renewal-percent-threshold: 0.85 # Threshold for self-preservation instance: hostname: localhost # Management endpoints management: endpoints: web: exposure: include: health,info,metrics endpoint: health: show-details: always

Example 3: High Availability Eureka Server

package com.example.eurekaserver; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; import org.springframework.context.annotation.Bean; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.web.SecurityFilterChain; @SpringBootApplication @EnableEurekaServer public class EurekaServerHAApplication { public static void main(String[] args) { SpringApplication.run(EurekaServerHAApplication.class, args); } // Disable security for Eureka dashboard (for development) @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .csrf(csrf -> csrf.disable()) .authorizeHttpRequests(auth -> auth .anyRequest().permitAll() ); return http.build(); } }

Example 4: Multi-Node Eureka Server Configuration

# application-peer1.yml - First Eureka Server Node spring: application: name: eureka-server profiles: peer1 server: port: 8761 eureka: instance: hostname: peer1 appname: eureka-server client: service-url: defaultZone: http://peer2:8762/eureka/,http://peer3:8763/eureka/ register-with-eureka: true fetch-registry: true --- # application-peer2.yml - Second Eureka Server Node spring: application: name: eureka-server profiles: peer2 server: port: 8762 eureka: instance: hostname: peer2 appname: eureka-server client: service-url: defaultZone: http://peer1:8761/eureka/,http://peer3:8763/eureka/ register-with-eureka: true fetch-registry: true --- # application-peer3.yml - Third Eureka Server Node spring: application: name: eureka-server profiles: peer3 server: port: 8763 eureka: instance: hostname: peer3 appname: eureka-server client: service-url: defaultZone: http://peer1:8761/eureka/,http://peer2:8762/eureka/ register-with-eureka: true fetch-registry: true

Eureka Client Implementation

Example 5: Basic Service Registration

package com.example.orderservice; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.web.bind.annotation.*; @SpringBootApplication @EnableDiscoveryClient public class OrderServiceApplication { public static void main(String[] args) { SpringApplication.run(OrderServiceApplication.class, args); } } @RestController @RequestMapping("/orders") class OrderController { @GetMapping("/{id}") public Order getOrder(@PathVariable String id) { return new Order(id, "Order " + id, 99.99); } @PostMapping public Order createOrder(@RequestBody Order order) { // Simulate order creation return new Order("ORD-123", order.getDescription(), order.getAmount()); } // Order DTO public static class Order { private String id; private String description; private double amount; public Order() {} public Order(String id, String description, double amount) { this.id = id; this.description = description; this.amount = amount; } // getters and setters public String getId() { return id; } public void setId(String id) { this.id = id; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public double getAmount() { return amount; } public void setAmount(double amount) { this.amount = amount; } } }

Example 6: Eureka Client Configuration

# application.yml for Order Service spring: application: name: order-service # This is the service ID used for discovery server: port: 8081 eureka: client: service-url: defaultZone: http://localhost:8761/eureka/ register-with-eureka: true fetch-registry: true enabled: true instance: instance-id: ${spring.application.name}:${spring.application.instance_id:${random.value}} appname: ${spring.application.name} hostname: localhost prefer-ip-address: false lease-renewal-interval-in-seconds: 30 # How often to send heartbeat lease-expiration-duration-in-seconds: 90 # Time before instance is expired metadata-map: zone: primary version: 1.0.0 environment: development # Health check configuration management: endpoints: web: exposure: include: health,info,metrics endpoint: health: show-details: always show-components: always # Enable health checks for Eureka eureka: client: healthcheck: enabled: true

Service Discovery and Communication

Example 7: Service Discovery using DiscoveryClient

package com.example.orderservice.service; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; import java.util.List; @Service public class ServiceDiscoveryService { private final DiscoveryClient discoveryClient; private final RestTemplate restTemplate; public ServiceDiscoveryService(DiscoveryClient discoveryClient, RestTemplate restTemplate) { this.discoveryClient = discoveryClient; this.restTemplate = restTemplate; } // Get all available services public List<String> getAvailableServices() { return discoveryClient.getServices(); } // Get instances of a specific service public List<ServiceInstance> getServiceInstances(String serviceId) { return discoveryClient.getInstances(serviceId); } // Get service URL for a specific service public String getServiceUrl(String serviceId) { List<ServiceInstance> instances = discoveryClient.getInstances(serviceId); if (instances.isEmpty()) { throw new RuntimeException("No instances available for service: " + serviceId); } // Simple round-robin (for demonstration) ServiceInstance instance = instances.get(0); return instance.getUri().toString(); } // Call another service using discovery public String callUserService(String userId) { String userServiceUrl = getServiceUrl("user-service"); String url = userServiceUrl + "/users/" + userId; return restTemplate.getForObject(url, String.class); } // Get service instances with metadata public void printServiceInstances(String serviceId) { List<ServiceInstance> instances = discoveryClient.getInstances(serviceId); System.out.println("Instances for service: " + serviceId); for (ServiceInstance instance : instances) { System.out.println("Instance ID: " + instance.getInstanceId()); System.out.println("URI: " + instance.getUri()); System.out.println("Host: " + instance.getHost()); System.out.println("Port: " + instance.getPort()); System.out.println("Metadata: " + instance.getMetadata()); System.out.println("---"); } } }

Example 8: Using @LoadBalanced RestTemplate

package com.example.orderservice.config; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate; @Configuration public class RestTemplateConfig { @Bean @LoadBalanced // Enables client-side load balancing public RestTemplate loadBalancedRestTemplate() { return new RestTemplate(); } } package com.example.orderservice.service; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; @Service public class UserServiceClient { private final RestTemplate restTemplate; public UserServiceClient(RestTemplate restTemplate) { this.restTemplate = restTemplate; } // Uses service ID instead of actual URL public User getUserById(String userId) { // Note: Using service ID (user-service) instead of host:port String url = "http://user-service/users/{userId}"; return restTemplate.getForObject(url, User.class, userId); } public List<User> getAllUsers() { String url = "http://user-service/users"; User[] users = restTemplate.getForObject(url, User[].class); return Arrays.asList(users != null ? users : new User[0]); } public User createUser(User user) { String url = "http://user-service/users"; return restTemplate.postForObject(url, user, User.class); } // User DTO public static class User { private String id; private String name; private String email; // constructors, getters, setters public User() {} public User(String id, String name, String email) { this.id = id; this.name = name; this.email = email; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } } }

Example 9: Using OpenFeign for Declarative REST Clients

// Add Feign dependency // implementation 'org.springframework.cloud:spring-cloud-starter-openfeign' package com.example.orderservice.config; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.context.annotation.Configuration; @Configuration @EnableFeignClients public class FeignConfig { } package com.example.orderservice.client; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.*; import java.util.List; @FeignClient(name = "user-service", path = "/users") public interface UserServiceFeignClient { @GetMapping("/{id}") User getUserById(@PathVariable("id") String userId); @GetMapping List<User> getAllUsers(); @PostMapping User createUser(@RequestBody User user); @PutMapping("/{id}") User updateUser(@PathVariable("id") String userId, @RequestBody User user); @DeleteMapping("/{id}") void deleteUser(@PathVariable("id") String userId); // User DTO class User { private String id; private String name; private String email; // constructors, getters, setters public User() {} public User(String id, String name, String email) { this.id = id; this.name = name; this.email = email; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } } } package com.example.orderservice.service; import com.example.orderservice.client.UserServiceFeignClient; import org.springframework.stereotype.Service; @Service public class OrderProcessingService { private final UserServiceFeignClient userServiceClient; public OrderProcessingService(UserServiceFeignClient userServiceClient) { this.userServiceClient = userServiceClient; } public ProcessedOrder createOrderWithUserValidation(String userId, Order order) { // Validate user exists UserServiceFeignClient.User user = userServiceClient.getUserById(userId); if (user == null) { throw new RuntimeException("User not found: " + userId); } // Process order String orderId = generateOrderId(); return new ProcessedOrder(orderId, user, order); } private String generateOrderId() { return "ORD-" + System.currentTimeMillis(); } public static class ProcessedOrder { private final String orderId; private final UserServiceFeignClient.User user; private final Order order; public ProcessedOrder(String orderId, UserServiceFeignClient.User user, Order order) { this.orderId = orderId; this.user = user; this.order = order; } // getters public String getOrderId() { return orderId; } public UserServiceFeignClient.User getUser() { return user; } public Order getOrder() { return order; } } public static class Order { private String description; private double amount; // constructors, getters, setters public Order() {} public Order(String description, double amount) { this.description = description; this.amount = amount; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public double getAmount() { return amount; } public void setAmount(double amount) { this.amount = amount; } } }

Advanced Eureka Configurations

Example 10: Custom Eureka Instance Configuration

package com.example.orderservice.config; import org.springframework.cloud.commons.util.InetUtils; import org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; import java.util.HashMap; import java.util.Map; @Configuration public class EurekaInstanceConfig { @Bean public EurekaInstanceConfigBean eurekaInstanceConfig(Environment env, InetUtils inetUtils) { EurekaInstanceConfigBean config = new EurekaInstanceConfigBean(inetUtils); // Custom instance configuration config.setAppname(env.getProperty("spring.application.name", "order-service")); config.setInstanceId(env.getProperty("eureka.instance.instance-id", config.getAppname() + ":" + config.getNonSecurePort())); // Custom metadata Map<String, String> metadata = new HashMap<>(); metadata.put("zone", "primary"); metadata.put("region", "us-east"); metadata.put("version", "1.0.0"); metadata.put("environment", env.getProperty("spring.profiles.active", "default")); metadata.put("startup-time", String.valueOf(System.currentTimeMillis())); config.setMetadataMap(metadata); // Health check configuration config.setStatusPageUrlPath("/actuator/info"); config.setHealthCheckUrlPath("/actuator/health"); // Lease configuration config.setLeaseRenewalIntervalInSeconds(30); config.setLeaseExpirationDurationInSeconds(90); return config; } }

Example 11: Custom Health Check Handler

package com.example.orderservice.health; import org.springframework.boot.actuate.health.Health; import org.springframework.boot.actuate.health.HealthIndicator; import org.springframework.stereotype.Component; import java.util.concurrent.atomic.AtomicBoolean; @Component public class CustomHealthCheck implements HealthIndicator { private final AtomicBoolean isHealthy = new AtomicBoolean(true); @Override public Health health() { if (!isHealthy.get()) { return Health.down() .withDetail("reason", "Manual health check failure") .build(); } // Add custom health checks here boolean databaseConnected = checkDatabaseConnection(); boolean externalServiceAvailable = checkExternalService(); if (databaseConnected && externalServiceAvailable) { return Health.up() .withDetail("database", "connected") .withDetail("external-service", "available") .build(); } else { return Health.down() .withDetail("database", databaseConnected ? "connected" : "disconnected") .withDetail("external-service", externalServiceAvailable ? "available" : "unavailable") .build(); } } public void setHealthy(boolean healthy) { isHealthy.set(healthy); } private boolean checkDatabaseConnection() { // Implement actual database health check return true; } private boolean checkExternalService() { // Implement actual external service health check return true; } }

Monitoring and Management

Example 12: Eureka Dashboard and Monitoring

package com.example.orderservice.monitoring; import com.netflix.appinfo.InstanceInfo; import org.springframework.cloud.netflix.eureka.server.event.*; import org.springframework.context.event.EventListener; import org.springframework.stereotype.Component; @Component public class EurekaEventListener { @EventListener public void listen(EurekaInstanceRegisteredEvent event) { InstanceInfo instanceInfo = event.getInstanceInfo(); System.out.println("Service Registered: " + instanceInfo.getAppName() + " - " + instanceInfo.getInstanceId()); } @EventListener public void listen(EurekaInstanceRenewedEvent event) { System.out.println("Service Heartbeat: " + event.getAppName() + " - " + event.getInstanceId()); } @EventListener public void listen(EurekaInstanceCanceledEvent event) { System.out.println("Service Unregistered: " + event.getAppName() + " - " + event.getServerId()); } @EventListener public void listen(EurekaRegistryAvailableEvent event) { System.out.println("Eureka Registry Started"); } @EventListener public void listen(EurekaServerStartedEvent event) { System.out.println("Eureka Server Started"); } }

Example 13: Service Discovery Controller

package com.example.orderservice.controller; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.web.bind.annotation.*; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @RestController @RequestMapping("/discovery") public class DiscoveryController { private final DiscoveryClient discoveryClient; public DiscoveryController(DiscoveryClient discoveryClient) { this.discoveryClient = discoveryClient; } @GetMapping("/services") public List<String> getServices() { return discoveryClient.getServices(); } @GetMapping("/services/{serviceId}") public List<ServiceInstance> getServiceInstances(@PathVariable String serviceId) { return discoveryClient.getInstances(serviceId); } @GetMapping("/services/{serviceId}/instances") public List<Map<String, Object>> getServiceInstancesDetails(@PathVariable String serviceId) { return discoveryClient.getInstances(serviceId).stream() .map(instance -> Map.of( "instanceId", instance.getInstanceId(), "serviceId", instance.getServiceId(), "host", instance.getHost(), "port", instance.getPort(), "uri", instance.getUri().toString(), "metadata", instance.getMetadata(), "secure", instance.isSecure() )) .collect(Collectors.toList()); } @GetMapping("/health") public Map<String, Object> getDiscoveryHealth() { return Map.of( "discoveryClient", discoveryClient.description(), "services", discoveryClient.getServices().size(), "timestamp", System.currentTimeMillis() ); } }

Testing Eureka Services

Example 14: Integration Tests

package com.example.orderservice.test; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.http.ResponseEntity; import org.springframework.test.context.ActiveProfiles; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @ActiveProfiles("test") class OrderServiceIntegrationTest { @LocalServerPort private int port; @Autowired private TestRestTemplate restTemplate; @Autowired private DiscoveryClient discoveryClient; @Test void contextLoads() { assertThat(discoveryClient).isNotNull(); } @Test void serviceRegistration() { List<ServiceInstance> instances = discoveryClient.getInstances("order-service"); assertThat(instances).isNotEmpty(); ServiceInstance instance = instances.get(0); assertThat(instance.getPort()).isEqualTo(port); assertThat(instance.getServiceId()).isEqualTo("order-service"); } @Test void healthEndpoint() { ResponseEntity<String> response = restTemplate.getForEntity( "http://localhost:" + port + "/actuator/health", String.class); assertThat(response.getStatusCode().is2xxSuccessful()).isTrue(); } @Test void orderEndpoint() { ResponseEntity<String> response = restTemplate.getForEntity( "http://localhost:" + port + "/orders/123", String.class); assertThat(response.getStatusCode().is2xxSuccessful()).isTrue(); } }

Example 15: Test Configuration

# application-test.yml spring: application: name: order-service-test eureka: client: enabled: false # Disable Eureka for tests register-with-eureka: false fetch-registry: false server: port: 0 # Random port for tests management: endpoints: web: exposure: include: health,info endpoint: health: enabled: true

Best Practices

1. Production Configuration

# application-prod.yml spring: application: name: order-service eureka: client: service-url: defaultZone: http://eureka-server1:8761/eureka/,http://eureka-server2:8762/eureka/ register-with-eureka: true fetch-registry: true healthcheck: enabled: true instance: prefer-ip-address: true ip-address: ${POD_IP:localhost} instance-id: ${spring.application.name}:${POD_ID:${random.value}} lease-renewal-interval-in-seconds: 30 lease-expiration-duration-in-seconds: 90 metadata-map: zone: ${ZONE:unknown} environment: production version: ${APP_VERSION:1.0.0} management: endpoints: web: exposure: include: health,info,metrics,prometheus endpoint: health: show-details: always show-components: always # Circuit breaker configuration resilience4j: circuitbreaker: instances: userService: register-health-indicator: true sliding-window-size: 10 minimum-number-of-calls: 5 wait-duration-in-open-state: 10s

2. Security Configuration

package com.example.eurekaserver.security; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.Customizer; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.provisioning.InMemoryUserDetailsManager; import org.springframework.security.web.SecurityFilterChain; @Configuration @EnableWebSecurity public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .csrf(csrf -> csrf.disable()) .authorizeHttpRequests(auth -> auth .requestMatchers("/eureka/**").permitAll() .requestMatchers("/actuator/**").hasRole("ADMIN") .anyRequest().authenticated() ) .httpBasic(Customizer.withDefaults()); return http.build(); } @Bean public UserDetailsService userDetailsService() { UserDetails user = User.builder() .username("admin") .password(passwordEncoder().encode("password")) .roles("ADMIN") .build(); return new InMemoryUserDetailsManager(user); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } }

Summary

Eureka provides robust service discovery for microservices:

  • Service Registry: Centralized registry of available service instances
  • Health Monitoring: Automatic health checks and instance eviction
  • Load Balancing: Client-side load balancing with Ribbon
  • High Availability: Multi-node Eureka server clusters
  • Integration: Seamless integration with Spring Cloud ecosystem

Key benefits:

  • Dynamic service discovery
  • Automatic failover and load distribution
  • Simplified service communication
  • Production-ready with health monitoring
  • Excellent Spring Boot integration

By implementing Eureka service discovery, you create a resilient and scalable microservices architecture where services can dynamically find and communicate with each other without hard-coded configurations.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper