Contents

<!-- Eureka Server pom.xml --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> @SpringBootApplication @EnableEurekaServer public class EurekaServerApplication { public static void main(String[] args) { SpringApplication.run(EurekaServerApplication.class, args); } } # application.yml — standalone Eureka Server (dev) server: port: 8761 spring: application: name: eureka-server eureka: instance: hostname: localhost client: register-with-eureka: false # this server doesn't register itself fetch-registry: false # doesn't need a copy of the registry service-url: defaultZone: http://localhost:8761/eureka/ server: enable-self-preservation: false # disable in dev (easier to test) eviction-interval-timer-in-ms: 5000

The Eureka dashboard is available at http://localhost:8761 — it shows all registered instances, their status, and metadata.

<!-- Each microservice pom.xml --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> # application.yml — each microservice spring: application: name: order-service # this is the name other services use to find you eureka: client: service-url: defaultZone: http://eureka-server:8761/eureka/ registry-fetch-interval-seconds: 10 # how often to refresh local copy instance-info-replication-interval-seconds: 10 instance: prefer-ip-address: true # register with IP instead of hostname ip-address: ${POD_IP:127.0.0.1} # use pod IP in Kubernetes instance-id: ${spring.application.name}:${spring.application.instance_id:${random.value}} lease-renewal-interval-in-seconds: 10 # heartbeat interval lease-expiration-duration-in-seconds: 30 // @EnableDiscoveryClient is auto-enabled when spring-cloud-starter-netflix-eureka-client // is on the classpath. No annotation needed in Spring Boot 3. // Explicitly enable if you want to be self-documenting: @SpringBootApplication @EnableDiscoveryClient public class OrderServiceApplication { } # By default, Eureka registers an instance as UP as soon as it starts. # Use Actuator health to make registration dependent on app health: eureka: client: healthcheck: enabled: true # use /actuator/health to determine Eureka UP/DOWN/OUT_OF_SERVICE management: endpoints: web: exposure: include: health,info health: defaults: enabled: true // Add custom metadata to the registration (visible in dashboard + queryable by clients) @Configuration public class EurekaMetadataConfig { @Bean public EurekaInstanceConfigBean eurekaInstanceConfig(InetUtils inetUtils) { EurekaInstanceConfigBean config = new EurekaInstanceConfigBean(inetUtils); config.getMetadataMap().put("version", "2.1.0"); config.getMetadataMap().put("region", "us-east-1"); config.getMetadataMap().put("environment", "prod"); return config; } } // Or via YAML // eureka.instance.metadata-map.version: 2.1.0 // eureka.instance.metadata-map.region: us-east-1 import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.cloud.client.ServiceInstance; @Service @RequiredArgsConstructor public class ServiceLocator { private final DiscoveryClient discoveryClient; // List all registered services public List<String> allServices() { return discoveryClient.getServices(); } // Get all instances of a service public List<ServiceInstance> instances(String serviceName) { return discoveryClient.getInstances(serviceName); } // Get a single instance URL public URI getServiceUri(String serviceName) { return discoveryClient.getInstances(serviceName) .stream() .findFirst() .map(ServiceInstance::getUri) .orElseThrow(() -> new ServiceNotFoundException(serviceName)); } // Access instance metadata public String getVersion(String serviceName) { return discoveryClient.getInstances(serviceName) .stream() .findFirst() .map(i -> i.getMetadata().get("version")) .orElse("unknown"); } } // Spring Cloud LoadBalancer auto-wires with @LoadBalanced RestTemplate or WebClient @Configuration public class WebClientConfig { // Load-balanced WebClient — resolves "order-service" via Eureka, round-robins instances @Bean @LoadBalanced public WebClient.Builder loadBalancedWebClientBuilder() { return WebClient.builder(); } @Bean @LoadBalanced public RestTemplate restTemplate() { return new RestTemplate(); } } @Service @RequiredArgsConstructor public class OrderProxy { private final WebClient.Builder webClientBuilder; private final RestTemplate restTemplate; public Mono<Order> getOrder(Long id) { return webClientBuilder.build() .get() .uri("http://order-service/orders/{id}", id) // "order-service" resolved via Eureka .retrieve() .bodyToMono(Order.class); } public Order getOrderSync(Long id) { return restTemplate.getForObject( "http://order-service/orders/{id}", Order.class, id); } } # Load balancer configuration spring: cloud: loadbalancer: cache: enabled: true # cache service instances locally ttl: 35s # refresh every 35 seconds retry: enabled: true max-retries-on-same-service-instance: 0 max-retries-on-next-service-instance: 1

Self-preservation prevents Eureka from evicting instances when it detects a network partition (many heartbeats failing at once). Instead of evicting what might be healthy services it cannot reach, Eureka keeps all registrations until the situation resolves.

# Eureka Server eureka: server: enable-self-preservation: true # default — recommended for production renewal-percent-threshold: 0.85 # if <85% of expected heartbeats received, enter self-preservation renewal-threshold-update-interval-ms: 900000 # recalculate threshold every 15 min # Development — disable self-preservation for faster instance eviction during testing eureka: server: enable-self-preservation: false eviction-interval-timer-in-ms: 2000 # check every 2 seconds (dev only) In production keep self-preservation enabled. Without it, a brief network hiccup can cause Eureka to evict healthy services, routing all traffic to a subset of instances and causing cascading failures. Clients should also implement circuit breakers as a second line of defence.

Run multiple Eureka Server instances. Each peer registers with the others and replicates the registry — if one server goes down, clients fall back to a cached copy and the remaining peers.

# application-eureka1.yml spring: application: name: eureka-server server: port: 8761 eureka: instance: hostname: eureka1 client: register-with-eureka: true # peers DO register with each other fetch-registry: true service-url: defaultZone: http://eureka2:8762/eureka/,http://eureka3:8763/eureka/ --- # application-eureka2.yml server: port: 8762 eureka: instance: hostname: eureka2 client: service-url: defaultZone: http://eureka1:8761/eureka/,http://eureka3:8763/eureka/ # Clients point to all Eureka servers (comma-separated) — tries first, falls back to others eureka: client: service-url: defaultZone: http://eureka1:8761/eureka/,http://eureka2:8762/eureka/,http://eureka3:8763/eureka/
# Instance declares its zone eureka: instance: metadata-map: zone: us-east-1a # Client prefers instances in the same zone eureka: client: prefer-same-zone-eureka: true availability-zones: us-east-1: us-east-1a, us-east-1b region: us-east-1 service-url: us-east-1a: http://eureka-east-1a:8761/eureka/ us-east-1b: http://eureka-east-1b:8762/eureka/ For Kubernetes deployments, consider replacing Eureka with Kubernetes-native service discovery (spring-cloud-kubernetes) which uses the Kubernetes API to list pod endpoints. This eliminates the need to run a separate Eureka cluster.