Spring Cloud Commons provides foundational abstractions and utilities for service discovery, load balancing, circuit breakers, and cloud-native application development.
—
Client-side load balancing enables applications to distribute requests across multiple service instances with support for multiple algorithms, health checking, retry mechanisms, and both blocking and reactive programming models.
Marks RestTemplate or WebClient beans for automatic load balancing.
/**
* Marks RestTemplate or WebClient beans for load balancing
*/
@LoadBalanced
public @interface LoadBalanced {}Usage Examples:
@Configuration
public class LoadBalancerConfig {
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
@Bean
@LoadBalanced
public WebClient.Builder webClientBuilder() {
return WebClient.builder();
}
}
@Service
public class UserService {
@Autowired
@LoadBalanced
private RestTemplate restTemplate;
public String getUser(String userId) {
// This will be load balanced across user-service instances
return restTemplate.getForObject("http://user-service/users/" + userId, String.class);
}
}Main interface for client-side load balancing operations.
/**
* Main interface for client-side load balancing
*/
public interface LoadBalancerClient extends ServiceInstanceChooser {
/**
* Execute a request using load balancing
* @param serviceId The service ID to load balance across
* @param request The request to execute
* @return The result from the request execution
* @throws IOException If request execution fails
*/
<T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException;
/**
* Execute a request using load balancing with a specific service instance
* @param serviceId The service ID
* @param serviceInstance The service instance to use
* @param request The request to execute
* @return The result from the request execution
* @throws IOException If request execution fails
*/
<T> T execute(String serviceId, ServiceInstance serviceInstance,
LoadBalancerRequest<T> request) throws IOException;
/**
* Create a proper URI with a real host and port for systems to utilize
* @param instance The service instance
* @param original The original URI
* @return A reconstructed URI
*/
URI reconstructURI(ServiceInstance instance, URI original);
}Interface for selecting service instances.
/**
* Interface for selecting service instances
*/
public interface ServiceInstanceChooser {
/**
* Choose a ServiceInstance from the LoadBalancer for the specified service
* @param serviceId The service ID to choose an instance for
* @return A ServiceInstance that matches the serviceId
*/
ServiceInstance choose(String serviceId);
/**
* Choose a ServiceInstance from the LoadBalancer for the specified service and request
* @param serviceId The service ID to choose an instance for
* @param request The request context
* @return A ServiceInstance that matches the serviceId
*/
<T> ServiceInstance choose(String serviceId, Request<T> request);
}Interface for wrapping requests in load balancing context.
/**
* Interface for wrapping requests in load balancing context
*/
public interface LoadBalancerRequest<T> {
/**
* Apply the request to the given ServiceInstance
* @param instance The service instance to apply the request to
* @return The result of applying the request
* @throws Exception If the request fails
*/
T apply(ServiceInstance instance) throws Exception;
}Usage Example:
@Autowired
private LoadBalancerClient loadBalancerClient;
public String callService() {
return loadBalancerClient.execute("user-service",
instance -> {
String url = "http://" + instance.getHost() + ":" + instance.getPort() + "/users";
// Make HTTP call using the selected instance
return restTemplate.getForObject(url, String.class);
});
}Framework for handling load balancer requests and responses.
/**
* Interface for load balancer requests
*/
public interface Request<C> {
/**
* @return The request context
*/
C getContext();
}
/**
* Interface for load balancer responses
*/
public interface Response<T> {
/**
* @return Whether the response has a server
*/
boolean hasServer();
/**
* @return The selected server
*/
T getServer();
}
/**
* Default implementation of Request
*/
public class DefaultRequest<T> implements Request<T> {
public DefaultRequest(T context);
}
/**
* Default implementation of Response with selected ServiceInstance
*/
public class DefaultResponse implements Response<ServiceInstance> {
public DefaultResponse(ServiceInstance serviceInstance);
}
/**
* Response implementation for no available instances
*/
public class EmptyResponse implements Response<ServiceInstance> {
public EmptyResponse();
}Context objects for carrying request information through the load balancing process.
/**
* Default request context implementation
*/
public class DefaultRequestContext implements RequestDataContext {
public DefaultRequestContext(RequestData requestData, String hint);
}
/**
* Request context containing request data
*/
public interface RequestDataContext {
/**
* @return The request data
*/
RequestData getClientRequest();
}
/**
* Request context with load balancer hints
*/
public class HintRequestContext implements RequestDataContext {
public HintRequestContext(RequestData requestData, String hint);
public String getHint();
}
/**
* Context for retryable requests
*/
public class RetryableRequestContext implements RequestDataContext {
public RetryableRequestContext(RequestData requestData, int retryCount);
public int getRetryCount();
}
/**
* HTTP request data holder
*/
public class RequestData {
public RequestData(HttpRequest httpRequest);
public HttpRequest getHttpRequest();
public HttpHeaders getHeaders();
public String getUrl();
}
/**
* HTTP response data holder
*/
public class ResponseData {
public ResponseData(HttpHeaders headers, Object requestData);
public HttpHeaders getHeaders();
public Object getRequestData();
}Configuration properties for load balancer behavior.
/**
* Base load balancer configuration properties
*/
@ConfigurationProperties("spring.cloud.loadbalancer")
public class LoadBalancerProperties {
private HealthCheck healthCheck = new HealthCheck();
private Retry retry = new Retry();
private StickySession stickySession = new StickySession();
private XForwarded xForwarded = new XForwarded();
/**
* Health check configuration
*/
public static class HealthCheck {
private boolean enabled = true;
private Duration interval = Duration.ofSeconds(25);
private String path = "/actuator/health";
// getters and setters
}
/**
* Retry configuration
*/
public static class Retry {
private boolean enabled = true;
private int maxRetriesOnSameServiceInstance = 0;
private int maxRetriesOnNextServiceInstance = 1;
private Set<Class<? extends Throwable>> retryableExceptions = new HashSet<>();
// getters and setters
}
/**
* Sticky session configuration
*/
public static class StickySession {
private boolean enabled = false;
private String instanceIdCookieName = "sc-lb-instance-id";
// getters and setters
}
}
/**
* Client-specific load balancer properties
*/
@ConfigurationProperties("spring.cloud.loadbalancer")
public class LoadBalancerClientsProperties {
private Map<String, LoadBalancerProperties> clients = new HashMap<>();
// getters and setters
}Interceptors for adding load balancing to HTTP clients.
/**
* HTTP client interceptor for load balancing
*/
public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {
public LoadBalancerInterceptor(LoadBalancerClient loadBalancer);
public LoadBalancerInterceptor(LoadBalancerClient loadBalancer,
LoadBalancerRequestFactory requestFactory);
}
/**
* Load balancer interceptor with retry support
*/
public class RetryLoadBalancerInterceptor implements ClientHttpRequestInterceptor {
public RetryLoadBalancerInterceptor(LoadBalancerClient loadBalancer,
LoadBalancerProperties properties);
}
/**
* Async HTTP client interceptor
*/
public class AsyncLoadBalancerInterceptor implements AsyncClientHttpRequestInterceptor {
public AsyncLoadBalancerInterceptor(LoadBalancerClient loadBalancer);
}Interfaces for customizing HTTP clients.
/**
* Interface for customizing RestTemplate
*/
public interface RestTemplateCustomizer {
/**
* Customize the RestTemplate
* @param restTemplate The RestTemplate to customize
*/
void customize(RestTemplate restTemplate);
}
/**
* Interface for customizing AsyncRestTemplate
*/
public interface AsyncRestTemplateCustomizer {
/**
* Customize the AsyncRestTemplate
* @param restTemplate The AsyncRestTemplate to customize
*/
void customize(AsyncRestTemplate restTemplate);
}Support for retrying failed load balanced requests.
/**
* Factory for creating retry policies
*/
public interface LoadBalancedRetryFactory {
/**
* Create a retry policy for the given service
* @param service The service ID
* @param loadBalancerProperties The load balancer properties
* @return A retry policy
*/
LoadBalancedRetryPolicy createRetryPolicy(String service,
LoadBalancerProperties loadBalancerProperties);
}
/**
* Retry policy interface for load balanced requests
*/
public interface LoadBalancedRetryPolicy extends RetryPolicy {
/**
* Check if retry is possible
* @param retryContext The retry context
* @return true if retry is possible
*/
boolean canRetry(LoadBalancedRetryContext retryContext);
/**
* Register a throwable with the retry policy
* @param retryContext The retry context
* @param throwable The throwable that occurred
*/
void registerThrowable(LoadBalancedRetryContext retryContext, Throwable throwable);
}
/**
* Recovery callback for retry scenarios
*/
public abstract class LoadBalancedRecoveryCallback<T, R> implements RecoveryCallback<R> {
/**
* Create a recovery callback
* @param request The original request
* @param originalUri The original URI
* @param loadBalancerClient The load balancer client
*/
protected LoadBalancedRecoveryCallback(LoadBalancerRequest<T> request,
URI originalUri,
LoadBalancerClient loadBalancerClient);
}
/**
* Exception for HTTP status codes that should trigger retries
*/
public class RetryableStatusCodeException extends Exception {
public RetryableStatusCodeException(String serviceId, int statusCode, HttpResponse response);
public int getStatusCode();
public HttpResponse getResponse();
}Reactive load balancing support for WebClient.
/**
* Reactive load balancer interface
*/
public interface ReactiveLoadBalancer<T> {
/**
* Choose a server instance
* @param request The request context
* @return A Mono containing the response
*/
Mono<Response<T>> choose(Request request);
/**
* Choose a server instance with default request
* @return A Mono containing the response
*/
default Mono<Response<T>> choose() {
return choose(REQUEST);
}
Request REQUEST = new DefaultRequest<>();
/**
* Factory interface for creating reactive load balancers
*/
interface Factory<T> {
ReactiveLoadBalancer<T> getInstance(String serviceId);
}
}
/**
* WebClient exchange filter function for load balancing
*/
public class ReactorLoadBalancerExchangeFilterFunction implements ExchangeFilterFunction {
public ReactorLoadBalancerExchangeFilterFunction(ReactiveLoadBalancer.Factory<ServiceInstance> loadBalancerFactory);
}
/**
* WebClient filter with retry support
*/
public class RetryableLoadBalancerExchangeFilterFunction implements ExchangeFilterFunction {
public RetryableLoadBalancerExchangeFilterFunction(RetrySpec retrySpec,
ReactiveLoadBalancer.Factory<ServiceInstance> loadBalancerFactory);
}# Enable/disable load balancer
spring.cloud.loadbalancer.enabled=true
# Health check configuration
spring.cloud.loadbalancer.health-check.enabled=true
spring.cloud.loadbalancer.health-check.interval=25s
spring.cloud.loadbalancer.health-check.path=/actuator/health
# Retry configuration
spring.cloud.loadbalancer.retry.enabled=true
spring.cloud.loadbalancer.retry.max-retries-on-same-service-instance=0
spring.cloud.loadbalancer.retry.max-retries-on-next-service-instance=1
# Sticky session configuration
spring.cloud.loadbalancer.sticky-session.enabled=false
spring.cloud.loadbalancer.sticky-session.instance-id-cookie-name=sc-lb-instance-id
# Client-specific configuration
spring.cloud.loadbalancer.clients.user-service.health-check.enabled=false
spring.cloud.loadbalancer.clients.user-service.retry.max-retries-on-next-service-instance=2Usage Examples:
// Basic load balancing with RestTemplate
@RestController
public class UserController {
@Autowired
@LoadBalanced
private RestTemplate restTemplate;
@GetMapping("/users/{id}")
public String getUser(@PathVariable String id) {
return restTemplate.getForObject("http://user-service/users/" + id, String.class);
}
}
// Reactive load balancing with WebClient
@Service
public class ReactiveUserService {
private final WebClient webClient;
public ReactiveUserService(@LoadBalanced WebClient.Builder webClientBuilder) {
this.webClient = webClientBuilder.build();
}
public Mono<String> getUser(String id) {
return webClient.get()
.uri("http://user-service/users/{id}", id)
.retrieve()
.bodyToMono(String.class);
}
}
// Manual load balancing
@Service
public class ManualLoadBalancingService {
@Autowired
private LoadBalancerClient loadBalancerClient;
public String callService() {
return loadBalancerClient.execute("user-service", instance -> {
String url = "http://" + instance.getHost() + ":" + instance.getPort() + "/users";
RestTemplate restTemplate = new RestTemplate();
return restTemplate.getForObject(url, String.class);
});
}
}Install with Tessl CLI
npx tessl i tessl/maven-org-springframework-cloud--spring-cloud-commons