Master Spring Cloud LoadBalancer: Quick Start and Custom Strategies
This guide shows how to quickly integrate Spring Cloud LoadBalancer into a Spring Cloud 2020 project, configure built‑in round‑robin or random policies, create custom gray load‑balancing strategies, and streamline bean registration for advanced load‑balancing scenarios.
How to use the heavily recommended load balancer Spring Cloud LoadBalancer (SCL) in Spring Cloud 2020 and extend load‑balancing strategies? This article provides the answers.
Quick Start SCL
If you want to use SCL in a project, simply add the following Maven dependency:
<code><dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
</code>SCL builds on service discovery. Since Spring Cloud Alibaba does not yet support SCL (see compatibility solution in reference [1]), you can test with Eureka.
When combining RestTemplate with client‑side load balancing, add the
@LoadBalancedannotation to the bean definition.
<code>@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
</code>Personalized Load‑Balancing Strategies
The current Spring Cloud 2020 version includes round‑robin and random strategies, with round‑robin as the default.
You can specify a service‑level strategy using the
LoadBalancerClientannotation.
<code>@LoadBalancerClient(value = "demo-provider", configuration = RandomLoadbalancerConfig.class)
</code> <code>public class RandomLoadbalancerConfig {
@Bean
public ReactorLoadBalancer<ServiceInstance> reactorServiceInstanceLoadBalancer(
Environment environment,
LoadBalancerClientFactory loadBalancerClientFactory) {
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
return new RandomLoadBalancer(
loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class),
name);
}
}
</code>Custom Load‑Balancing Strategy
SCL supports fewer strategies than Ribbon, so developers can implement custom ones. Below is an example of a gray load‑balancing strategy based on registration‑center metadata.
Define the gray load‑balancing strategy:
<code>@Slf4j
public class GrayRoundRobinLoadBalancer extends RoundRobinLoadBalancer {
private ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;
private String serviceId;
@Override
public Mono<Response<ServiceInstance>> choose(Request request) {
ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider
.getIfAvailable(NoopServiceInstanceListSupplier::new);
return supplier.get(request).next()
.map(serviceInstances -> getInstanceResponse(serviceInstances, request));
}
Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances, Request request) {
// No available instances, return empty response
if (CollUtil.isEmpty(instances)) {
log.warn("No instance available {}", serviceId);
return new EmptyResponse();
}
DefaultRequestContext requestContext = (DefaultRequestContext) request.getContext();
RequestData clientRequest = (RequestData) requestContext.getClientRequest();
HttpHeaders headers = clientRequest.getHeaders();
String reqVersion = headers.getFirst(CommonConstants.VERSION);
if (StrUtil.isBlank(reqVersion)) {
return super.choose(request).block();
}
// Match instance metadata with version
for (ServiceInstance instance : instances) {
NacosServiceInstance nacosInstance = (NacosServiceInstance) instance;
Map<String, String> metadata = nacosInstance.getMetadata();
String targetVersion = MapUtil.getStr(metadata, CommonConstants.VERSION);
if (reqVersion.equalsIgnoreCase(targetVersion)) {
log.debug("gray request match success :{} {}", reqVersion, nacosInstance);
return new DefaultResponse(nacosInstance);
}
}
// Fallback to round‑robin
return super.choose(request).block();
}
}
</code>Inject the gray strategy into the client:
<code>@LoadBalancerClient(value = "demo-provider", configuration = GrayRoundLoadbalancerConfig.class)
</code>Define version number on service instances (illustrated below).
Test by sending a request with a version header:
<code>curl --location --request GET 'http://localhost:6060/req?key=b' \
--header 'VERSION: b'
</code>Optimizing Load‑Balancing Strategy Injection
Manually injecting each custom strategy via
LoadBalancerClientis cumbersome. You can follow the batch injection logic of
LoadBalancerClientsto create a custom BeanRegistrar.
<code>public class GrayLoadBalancerClientConfigurationRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
Field[] fields = ReflectUtil.getFields(ServiceNameConstants.class);
// Iterate service names and inject gray load balancers
for (Field field : fields) {
Object fieldValue = ReflectUtil.getFieldValue(ServiceNameConstants.class, field);
registerClientConfiguration(registry, fieldValue, GrayLoadBalancerClientConfiguration.class);
}
}
}
</code>Reference
[1] Compatibility solution for Spring Cloud 2020: https://gitee.com/log4j/pig
Java Architecture Diary
Committed to sharing original, high‑quality technical articles; no fluff or promotional content.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.