Spring Boot Tutorial
Service Discovery with Eureka and Load Balancing
In a dynamic environment, service instances come and go: autoscaling adds three more order-service instances, a deployment replaces them all, a crashed instance disappears. Hard-coding host names and ports does not work. Service discovery lets services register themselves in a registry and look each other up by name, and client-side load balancing spreads calls across the healthy instances.
This lesson sets up a Eureka server, registers services as Eureka clients, calls services by name with a @LoadBalanced RestClient and HTTP service clients, and explains when Kubernetes DNS makes Eureka unnecessary.
How Eureka Works
The Eureka server keeps a registry of instances. Each client registers on startup with its application name, host, port and health URL, sends a heartbeat every 30 seconds, and fetches a cached copy of the registry. If heartbeats stop, the instance is evicted. Because clients cache the registry, they can keep calling each other even if the Eureka server is briefly unavailable. For high availability, run several Eureka servers that replicate to each other.
Client-Side Load Balancing
Spring Cloud LoadBalancer resolves a logical URL like http://payment-service to one of the registered instances, using round-robin by default (random and custom strategies are available). Mark a RestClient.Builder or WebClient.Builder bean with @LoadBalanced, and HTTP service clients built from it inherit the behaviour. Combine with retries and circuit breakers for resilience.
Eureka or Kubernetes?
Kubernetes already provides discovery (a Service gives stable DNS: payment-service.default.svc.cluster.local) and load balancing, and its readiness probes remove unhealthy pods. On Kubernetes you usually do not need Eureka. Eureka remains useful on VMs, bare metal, or mixed environments without a platform registry.
Examples
The Eureka server
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
@SpringBootApplication
@EnableEurekaServer
public class DiscoveryServerApplication {
public static void main(String[] args) {
SpringApplication.run(DiscoveryServerApplication.class, args);
}
}
# application.yml
server:
port: 8761
eureka:
client:
register-with-eureka: false # the server does not register with itself
fetch-registry: false
Started DiscoveryServerApplication on port 8761
Dashboard: http://localhost:8761
Registering services as Eureka clients
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
# payment-service application.yml
spring:
application:
name: payment-service
server:
port: 0 # random port: run as many instances as you like
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka
instance:
prefer-ip-address: true
instance-id: ${spring.application.name}:${random.value}
DiscoveryClient_PAYMENT-SERVICE/payment-service:3f9a... - registration status: 204
Eureka dashboard -> Instances currently registered:
PAYMENT-SERVICE UP (3) payment-service:3f9a..., payment-service:81c2..., payment-service:d07e...
ORDER-SERVICE UP (2)
Calling a service by name with a load-balanced RestClient
@Configuration
public class LoadBalancedClients {
@Bean
@LoadBalanced
RestClient.Builder loadBalancedRestClientBuilder() {
return RestClient.builder();
}
}
@Service
public class PaymentGatewayClient {
private final RestClient client;
public PaymentGatewayClient(@LoadBalanced RestClient.Builder builder) {
this.client = builder.baseUrl("http://payment-service").build(); // logical name, no host/port
}
public PaymentResult charge(ChargeRequest req) {
return client.post().uri("/api/payments").body(req).retrieve().body(PaymentResult.class);
}
}
charge #1 -> payment-service instance 10.0.0.12:53120
charge #2 -> payment-service instance 10.0.0.14:49811
charge #3 -> payment-service instance 10.0.0.17:50277 (round-robin)
Common Mistakes
- Running Eureka on Kubernetes when Services and DNS already provide discovery and load balancing.
- Running a single Eureka server in production, a single point of failure for new registrations.
- Forgetting @LoadBalanced, so http://payment-service fails with UnknownHostException.
- Using fixed ports for multiple instances on the same host instead of server.port=0.
- Expecting instant removal of crashed instances; eviction takes time, so combine with retries and circuit breakers.
Key Points to Remember
- Eureka server (@EnableEurekaServer) keeps a registry; clients register and heartbeat.
- spring-cloud-starter-netflix-eureka-client registers a service under spring.application.name.
- @LoadBalanced RestClient/WebClient builders resolve logical service names to instances.
- Spring Cloud LoadBalancer uses round-robin by default.
- On Kubernetes, platform Services usually replace Eureka.
Practice the examples
Change an input, predict the result, then compare it with the output. Explain why the result changes.
Use your local JDK or project IDE for these examples. Codelab currently runs Python and HTML/CSS/JavaScript; framework examples may need project dependencies.