Spring Cloud LoadBalancer 默认采用的轮训(RoundRobinLoadBalancer)策略,如果你希望使用随机策略(RandomLoadBalancer),则可以通过手动配置实现策略切换。下面将以随机策略算法为例,介绍如何将它配置为全局策略和特定服务的策略。
注意:下面将使用“LoadBalancer 准备工作:环境搭建”中的 USER-SERVICE 作为目标服务。
Spring Cloud LoadBalancer 允许我们通过 @LoadBalancerClient 注解,为单个特定服务定制负载均衡客户端的配置(如负载均衡规则、服务实例过滤器等)。当需要为某个具体的服务(如 user-service)设置专属的负载均衡策略时,使用该注解。
@LoadBalancerClient 注解定义:
package org.springframework.cloud.loadbalancer.annotation;
//...
/**
* 负载均衡客户端的声明式配置注解
*
* 可将此注解添加到任何标注了 Configuration 的配置类上,
* 然后通过注入 LoadBalancerClientFactory 来访问创建的负载均衡客户端实例
*/
//...
public @interface LoadBalancerClient {
/**
* 与name属性同义(表示客户端名称)
* @see #name()
* @return 负载均衡客户端的名称
*/
@AliasFor("name") // 与name属性互为别名,两者值会同步
String value() default "";
/**
* 负载均衡客户端的名称,用于唯一标识一组客户端资源(包括负载均衡器)
* @return 负载均衡客户端的名称
*/
@AliasFor("value") // 与value属性互为别名,两者值会同步
String name() default "";
/**
* 负载均衡客户端的自定义配置类
* 可以包含用于覆盖客户端组成部分的 @Bean 定义
* @see LoadBalancerClientConfiguration 查看默认配置
* @return 负载均衡客户端的配置类数组
*/
Class<?>[] configuration() default {};
}属性说明:
value / name:指定要配置的服务名称(必填),即服务在注册中心的名称(如 user-service)。
configuration:指定自定义配置类(可选),该类中可定义负载均衡规则、服务实例过滤器等 Bean,覆盖默认配置。
假设为 USER-SERVICE 服务配置自定义负载均衡规则:
(1)定义自定义配置类,不要使用 @Configuration 注解进行修饰。
package com.hxstrive.springcloud.loadbalancer_demo.demo5.config;
import org.springframework.cloud.loadbalancer.core.RandomLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
/**
* 随机负载均衡器配置类
* 用于自定义负载均衡策略,此处配置随机负载均衡器替代默认策略
*/
public class RandomLoadBalancerConfig {
/**
* 定义随机负载均衡器的Bean
* 该Bean会被Spring容器管理,作为当前服务的负载均衡策略实现
*
* @param environment 环境变量对象,用于获取当前服务的相关配置
* @param loadBalancerClientFactory 负载均衡客户端工厂,用于获取服务实例列表提供者
* @return 随机负载均衡器实例,实现了ReactorServiceInstanceLoadBalancer接口
*/
@Bean
public ReactorServiceInstanceLoadBalancer randomLoadBalancer(
Environment environment,
LoadBalancerClientFactory loadBalancerClientFactory) {
// 从环境变量中获取当前服务的服务ID(服务名)
// LoadBalancerClientFactory.PROPERTY_NAME 是存储服务ID的环境变量键名
String serviceId = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
// 创建并返回RandomLoadBalancer实例
// 参数1:服务实例列表提供者(通过工厂延迟获取,基于服务ID)
// 参数2:当前服务的服务ID
return new RandomLoadBalancer(
loadBalancerClientFactory.getLazyProvider(serviceId, ServiceInstanceListSupplier.class),
serviceId);
}
}(2)用 @LoadBalancerClient 关联服务和配置
package com.hxstrive.springcloud.loadbalancer_demo.demo5;
import com.hxstrive.springcloud.loadbalancer_demo.demo5.config.GlobalLoadBalancerConfig;
import com.hxstrive.springcloud.loadbalancer_demo.demo5.config.RandomLoadBalancerConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
@SpringBootApplication
@EnableDiscoveryClient
// 为 USER-SERVICE 服务指定自定义随机策略配置
@LoadBalancerClient(value = "USER-SERVICE", configuration = RandomLoadBalancerConfig.class)
public class LoadbalancerDemoApplication {
@Autowired
private RestTemplate restTemplate;
public static void main(String[] args) {
SpringApplication.run(LoadbalancerDemoApplication.class, args);
}
@GetMapping("/getData")
public String getData() throws Exception {
StringBuilder result = new StringBuilder();
// 发起10次调用,仅截取每次返回结果的钱40个字符
for(int i=0; i<10;i++) {
// 关键:用服务名代替具体 IP:端口,LoadBalancer 会自动处理
String url = "http://USER-SERVICE/api/users"; // 服务名与提供者的 spring.application.name 一致
String value = restTemplate.getForObject(url, String.class);
if(StringUtils.hasText(value)) {
result.append(value.replaceAll("\n", "")
.substring(0, Math.min(40, value.length())) + "...").append("<br/>");
}
}
return result.toString();
}
}启动应用,浏览器访问 http://localhost:8080/getData 地址,效果如下图:

上图中,Spring Cloud LoadBalancer 随机从 node1、node2 和 node3 三个服务中进行访问。
Spring Cloud LoadBalancer 允许我们通过 @LoadBalancerClients 注解,批量配置多个服务的负载均衡客户端,或指定全局默认配置。当需要为多个服务分别设置自定义配置,或定义全局通用配置时,使用该注解。
@LoadBalancerClient 注解定义:
package org.springframework.cloud.loadbalancer.annotation;
//...
/**
* 便捷注解,允许用户在单个类上组合多个 @LoadBalancerClient 注解(包括Java 7环境)
* 用于批量配置多个负载均衡客户端,避免在类上重复标注多个 @LoadBalancerClient
*/
//...
// 导入负载均衡客户端配置注册器,与@LoadBalancerClient共用相同的配置处理逻辑
@Import(LoadBalancerClientConfigurationRegistrar.class)
public @interface LoadBalancerClients {
/**
* 多个 @LoadBalancerClient 注解的数组
* 用于批量定义多个负载均衡客户端的配置
* @return 负载均衡客户端配置数组,默认为空数组
*/
LoadBalancerClient[] value() default {};
/**
* 全局默认配置类数组
*
* LoadBalancerClientConfigurationRegistrar 会使用这些类创建 LoadBalancerClientSpecification,
* 并将其作为默认上下文添加到 LoadBalancerClientFactory 中。
* 当 LoadBalancerClient#configuration() 未指定配置时,将使用这些类中定义的配置作为默认值
*
* @return 全局默认配置类数组,默认为空数组
*/
Class<?>[] defaultConfiguration() default {};
}属性说明:
value:数组类型,包含多个 @LoadBalancerClient 注解,用于指定多个服务的单独配置(可选)。
defaultConfiguration:指定全局默认配置类(可选),当某个服务没有单独的 @LoadBalancerClient 配置时,会使用该全局配置。
(1)创建全局配置类,注意配置类不要使用 @Configuration 注解进行修饰。
package com.hxstrive.springcloud.loadbalancer_demo.demo6.config;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.loadbalancer.core.RandomLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;
// 作为 @LoadBalancerClient 或 @LoadBalancerClients配置参数传递的类
// 要么不应使用 @Configuration 进行注解,要么应处于组件扫描范围之外。
public class GlobalLoadBalancerConfig {
// 全局默认使用 RandomLoadBalancer(随机策略)
@Bean
public ReactorServiceInstanceLoadBalancer globalRandomLoadBalancer(
LoadBalancerClientFactory loadBalancerClientFactory,
Environment environment) { // 环境变量用于获取工厂中的 serviceId
// 1. 从工厂获取当前服务的 serviceId(非 null)
String serviceId = LoadBalancerClientFactory.getName(environment);
// 2. 获取 "延迟加载的实例供应器"(返回类型是 ObjectProvider<ServiceInstanceListSupplier>)
ObjectProvider<ServiceInstanceListSupplier> supplierProvider =
loadBalancerClientFactory.getLazyProvider(serviceId, ServiceInstanceListSupplier.class);
// 3. 传入实例供应器和 serviceId,创建 RandomLoadBalancer
return new RandomLoadBalancer(supplierProvider, serviceId);
}
}(2)用 @LoadBalancerClients 批量配置
package com.hxstrive.springcloud.loadbalancer_demo.demo6;
import com.hxstrive.springcloud.loadbalancer_demo.demo6.config.GlobalLoadBalancerConfig;
import com.hxstrive.springcloud.loadbalancer_demo.demo6.config.RandomLoadBalancerConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClients;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
@SpringBootApplication
@EnableDiscoveryClient
// 设置全局的负载均衡策略
@LoadBalancerClients(defaultConfiguration = GlobalLoadBalancerConfig.class)
public class LoadbalancerDemoApplication {
@Autowired
private RestTemplate restTemplate;
public static void main(String[] args) {
SpringApplication.run(LoadbalancerDemoApplication.class, args);
}
@GetMapping("/getData")
public String getData() throws Exception {
StringBuilder result = new StringBuilder();
// 发起10次调用,仅截取每次返回结果的钱40个字符
for(int i=0; i<10;i++) {
// 关键:用服务名代替具体 IP:端口,LoadBalancer 会自动处理
String url = "http://USER-SERVICE/api/users"; // 服务名与提供者的 spring.application.name 一致
String value = restTemplate.getForObject(url, String.class);
if(StringUtils.hasText(value)) {
result.append(value.replaceAll("\n", "")
.substring(0, Math.min(40, value.length())) + "...").append("<br/>");
}
}
return result.toString();
}
}启动应用,浏览器访问 http://localhost:8080/getData 地址,效果如下图:

如果我们在指定了全局策略的同时,还想对某个,或者某几个服务定义局部策略,可以向下面这样做:
(1)定义 USER-SERVICE 服务的策略配置,也不要使用 @Configuration 进行修饰
package com.hxstrive.springcloud.loadbalancer_demo.demo6.config;
import org.springframework.cloud.loadbalancer.core.RandomLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
// 作为 @LoadBalancerClient 或 @LoadBalancerClients配置参数传递的类
// 要么不应使用 @Configuration 进行注解,要么应处于组件扫描范围之外。
public class RandomLoadBalancerConfig {
// 定义随机负载均衡器
@Primary
@Bean
public ReactorServiceInstanceLoadBalancer randomLoadBalancer(
Environment environment,
LoadBalancerClientFactory loadBalancerClientFactory) {
// 获取当前服务 ID(从环境变量中读取)
String serviceId = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
// 创建 RandomLoadBalancer 实例,依赖 Eureka 提供的服务实例列表
return new RandomLoadBalancer(
loadBalancerClientFactory.getLazyProvider(serviceId, ServiceInstanceListSupplier.class),
serviceId);
}
}(2)通过 @LoadBalancerClients 注解的 value 属性进行配置,如下:
package com.hxstrive.springcloud.loadbalancer_demo.demo6;
import com.hxstrive.springcloud.loadbalancer_demo.demo6.config.GlobalLoadBalancerConfig;
import com.hxstrive.springcloud.loadbalancer_demo.demo6.config.RandomLoadBalancerConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClients;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
@SpringBootApplication
@EnableDiscoveryClient
// 设置全局的负载均衡策略
@LoadBalancerClients(value = {
// 为 USER-SERVICE 服务指定自定义随机策略配置
@LoadBalancerClient(value = "USER-SERVICE", configuration = RandomLoadBalancerConfig.class)
}, defaultConfiguration = GlobalLoadBalancerConfig.class)
public class LoadbalancerDemoApplication {
@Autowired
private RestTemplate restTemplate;
public static void main(String[] args) {
SpringApplication.run(LoadbalancerDemoApplication.class, args);
}
@GetMapping("/getData")
public String getData() throws Exception {
StringBuilder result = new StringBuilder();
// 发起10次调用,仅截取每次返回结果的钱40个字符
for(int i=0; i<10;i++) {
// 关键:用服务名代替具体 IP:端口,LoadBalancer 会自动处理
String url = "http://USER-SERVICE/api/users"; // 服务名与提供者的 spring.application.name 一致
String value = restTemplate.getForObject(url, String.class);
if(StringUtils.hasText(value)) {
result.append(value.replaceAll("\n", "")
.substring(0, Math.min(40, value.length())) + "...").append("<br/>");
}
}
return result.toString();
}
}特别注意:这里有一个点需要注意,当我们使用如下配置时:
// 设置全局的负载均衡策略
@LoadBalancerClients(value = {
// 为 USER-SERVICE 服务指定自定义随机策略配置
@LoadBalancerClient(value = "USER-SERVICE", configuration = RandomLoadBalancerConfig.class)
}, defaultConfiguration = GlobalLoadBalancerConfig.class)会在 USER-SERVICE 的子上下文中存在两个类型为 ReactorServiceInstanceLoadBalancer 的策略实例,如下图:

此时,会导致调用服务时 ReactorServiceInstanceLoadBalancer 不唯一,出现调用失败。
这个问题的核心原因是:全局策略与局部策略的配置发生了冲突,导致 Spring 容器中存在多个同类型的负载均衡策略 Bean,触发了 “Bean 不唯一” 的异常。
在 Spring Cloud LoadBalancer 中,@LoadBalancerClients 的 defaultConfiguration 用于定义全局默认策略(对所有服务生效),而内部的 @LoadBalancerClient 用于定义局部策略(仅对指定服务生效)。
但需要注意:局部策略配置(RandomLoadBalancerConfig)会覆盖全局策略(GlobalLoadBalancerConfig)对 USER-SERVICE 的生效,但如果两种配置中都定义了同类型的策略 Bean(例如都配置了ReactorServiceInstanceLoadBalancer 类型的 Bean),Spring 容器会同时加载这两个 Bean(全局策略 Bean 不会因局部配置而被移除),导致调用时无法确定使用哪个 Bean,最终抛出NoUniqueBeanDefinitionException。
为了解决该问题,我们可以在局部策略的配置类中,为自定义策略 Bean 添加 @Primary 注解,明确指定其为 USER-SERVICE 的优先策略,如下:
@Primary
@Bean
public ReactorServiceInstanceLoadBalancer randomLoadBalancer(
Environment environment,
LoadBalancerClientFactory loadBalancerClientFactory) {
// 获取当前服务 ID(从环境变量中读取)
String serviceId = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
// 创建 RandomLoadBalancer 实例,依赖 Eureka 提供的服务实例列表
return new RandomLoadBalancer(
loadBalancerClientFactory.getLazyProvider(serviceId, ServiceInstanceListSupplier.class),
serviceId);
}再次重启应用,就能正常调用了。