Load Balancing
When a service runs several instances, calls must be spread across them. Spring Cloud LoadBalancer does this on the client side: the caller resolves a logical service name against discovery and picks an instance itself — no extra proxy hop. This page wires it into RestClient, RestTemplate, and WebClient, and contrasts it with server-side load balancing.
Client-side load balancing
In client-side load balancing the caller knows the full list of healthy instances (from the registry) and chooses one — by default round-robin.
orders-service registry
│ resolve "inventory-service" ─────────►│
│◄── [10.0.3.9:8082, 10.0.3.11:8082] ───┘
│ pick round-robin → 10.0.3.9:8082
│ next call → 10.0.3.11:8082
The dependency is usually pulled in transitively by Eureka/Consul clients, but you can add it explicitly:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
Note: Spring Cloud LoadBalancer is the modern replacement for the long-retired Netflix Ribbon. Ribbon is gone in current Spring Cloud — do not look for it.
@LoadBalanced RestClient
RestClient is the Spring Boot 3.2+ synchronous HTTP client. Mark its builder @LoadBalanced and you can target services by name with http://service-name/....
@Configuration
public class HttpClientConfig {
@Bean
@LoadBalanced
RestClient.Builder restClientBuilder() {
return RestClient.builder();
}
@Bean
RestClient inventoryClient(RestClient.Builder builder) {
return builder.build();
}
}
@Service
@RequiredArgsConstructor
public class OrderService {
private final RestClient inventoryClient;
public InventoryResponse stock(String sku) {
return inventoryClient.get()
.uri("http://inventory-service/inventory/{sku}", sku) // name, not host
.retrieve()
.body(InventoryResponse.class);
}
}
inventory-service is resolved against the registry and one instance is selected per call.
@LoadBalanced RestTemplate
The same annotation works on a classic RestTemplate:
@Bean
@LoadBalanced
RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.build();
}
InventoryResponse r = restTemplate.getForObject(
"http://inventory-service/inventory/{sku}", InventoryResponse.class, sku);
@LoadBalanced WebClient
For reactive or non-blocking calls, mark the WebClient.Builder:
@Bean
@LoadBalanced
WebClient.Builder webClientBuilder() {
return WebClient.builder();
}
Mono<InventoryResponse> resp = webClientBuilder.build()
.get()
.uri("http://inventory-service/inventory/{sku}", sku)
.retrieve()
.bodyToMono(InventoryResponse.class);
See WebClient & RestClient for the full client API.
Choosing the algorithm
The default is round-robin. To switch to random selection (or a custom policy) define a per-service ReactorLoadBalancer configuration:
public class CustomLbConfig {
@Bean
ReactorLoadBalancer<ServiceInstance> randomLb(
Environment env, LoadBalancerClientFactory factory) {
String name = env.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
return new RandomLoadBalancer(
factory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
}
}
@Configuration
@LoadBalancerClient(name = "inventory-service", configuration = CustomLbConfig.class)
public class LbClients { }
Client-side vs server-side
| Aspect | Client-side (Spring Cloud LoadBalancer) | Server-side (proxy / cloud LB) |
|---|---|---|
| Decision maker | The calling service | A dedicated load balancer |
| Network hops | One (direct to instance) | Two (client → LB → instance) |
| Needs registry | Yes (each client queries it) | No (LB owns the routing) |
| Examples | @LoadBalanced clients, OpenFeign | Kubernetes Service, AWS ELB, NGINX |
| Coupling | Clients embed LB logic | LB is language-agnostic |
Tip: On Kubernetes you often don’t need client-side load balancing for in-cluster calls — a
Servicealready load-balances across pods, and you call it by DNS name. Reach for Spring Cloud LoadBalancer when discovery is registry-based (Eureka/Consul) or you want client-side control.
OpenFeign uses Spring Cloud LoadBalancer automatically, so a @FeignClient(name = "inventory-service") is load-balanced with no extra config — see OpenFeign Client.