RandomLoadBalancer 是 Spring Cloud LoadBalancer 中基于随机算法的负载均衡器实现,核心功能是从可用服务实例列表中随机选择一个实例处理请求,适用于简单分布式场景。
RandomLoadBalancer 实现了 ReactorServiceInstanceLoadBalancer 接口,因此支持响应式编程(基于 Reactor 框架),适配 Spring Cloud 的异步非阻塞场景。
注意:ReactorServiceInstanceLoadBalancer 接口是 Spring Cloud 中基于响应式编程模型的负载均衡器核心接口,负责从服务实例列表中筛选出一个可用实例,以实现请求的负载分发。该接口定义了负载均衡的核心行为,所有响应式负载均衡器(如默认的RoundRobinLoadBalancer)都需实现此接口。
随机策略的实现逻辑非常简单直接,无需复杂计算或状态维护,核心步骤仅两步:
(1)获取可用实例列表:负载均衡器首先筛选出健康的、在线的服务实例,排除故障或离线实例。
(2)随机选择实例:通过随机数生成算法(如 Java 的java.util.Random),在可用实例列表的索引范围内生成一个随机索引,选择该索引对应的实例处理请求。
例如:可用实例列表为[A, B, C],生成随机索引为 1 时,会选择“B”处理请求。
优点如下:
实现简单:无复杂状态维护(如轮询计数、权重计算),代码逻辑轻量,易理解和调试。
性能开销低:仅需一次随机数生成和列表索引查询,对负载均衡器本身性能影响极小。
天然去中心化:长期来看,请求在多个实例中分布趋近均匀,避免固定规则导致的流量倾斜。
缺点如下:
短期负载不均:随机算法存在概率波动,可能短时间内将多次请求分配给同一实例,导致临时过载。
不感知实例差异:默认不考虑实例性能(如 CPU / 内存配置)或当前负载,可能将请求分配给性能较弱的实例。
无会话亲和性:无法保证同一客户端请求路由到同一实例,不适合依赖会话状态的业务(如未做会话共享的登录场景)。
随机策略适合如下场景:
实例性能差异小:所有服务实例的硬件配置、性能几乎一致,无需区分权重的场景。
低精度负载均衡需求:业务对短期负载波动不敏感(如非核心查询接口、日志收集服务)。
轻量级场景:开发 / 测试环境快速验证,或低流量服务(无需复杂策略)。
为解决“不考虑实例差异”的缺点,随机策略的常见增强版是加权随机策略(Weighted Random LoadBalancer),核心改进是“为实例分配权重,权重越高,被随机选中的概率越大”。
加权随机的核心实现逻辑如下:
(1)为实例配置权重:如性能强的“实例 A” 权重设为 5,性能弱的“实例 B” 权重设为 1,“实例 C” 权重设为 2。
(2)计算权重总和与区间:总权重 = 5+1+2=8,划分权重区间:实例 A(0-4)、实例 B(5-5)、实例 C(6-7)。
(3)随机数匹配区间:生成 0-7 之间的随机数,如果随机数为 3,落在实例 A 的区间内,选择实例 A;如果为 5,则选择实例 B。
通过权重配置,加权随机策略既能保留“随机”的低开销优势,又能让高性能实例承担更多请求,避免资源浪费或弱实例过载。
RandomLoadBalancer 的工作原理如下:
(1)获取可用实例列表:通过 ServiceInstanceListSupplier 组件获取当前健康的服务实例,例如从服务注册中心 Nacos/Eureka/Consul 拉取并过滤故障实例。
(2)随机选择实例:如果实例列表非空,使用 ThreadLocalRandom(线程安全的随机数生成器)生成一个介于 [0, 实例数量-1] 之间的随机索引,选择该索引对应的实例。
(3)返回结果:
如果选中了实例,封装为 DefaultResponse 返回。
如果实例列表为空,则返回 EmptyResponse 并记录警告日志。
下面将通过分析 RandomLoadBalancer 类的源码,了解内部实现原理:
package org.springframework.cloud.loadbalancer.core;
//...
/**
* 基于随机算法的负载均衡器实现,实现了 ReactorServiceInstanceLoadBalancer 接口。
* 核心逻辑:从可用服务实例列表中随机选择一个实例处理请求,适用于实例性能同质化的场景。
*
* @author Olga Maciaszek-Sharma
* @author Nan Chiu
* @since 2.2.7
*/
public class RandomLoadBalancer implements ReactorServiceInstanceLoadBalancer {
// 日志记录器,用于输出警告或调试信息
private static final Log log = LogFactory.getLog(RandomLoadBalancer.class);
// 当前负载均衡器对应的服务ID(即服务名称)
private final String serviceId;
// 单例提供者,用于获取服务实例列表的供应器(ServiceInstanceListSupplier)
// 采用 SingletonSupplier 确保供应器实例唯一,避免重复创建
private final SingletonSupplier<ServiceInstanceListSupplier> serviceInstanceListSingletonSupplier;
/**
* 初始化负载均衡器,关联服务ID和服务实例供应器
*
* @param serviceInstanceListSupplierProvider
* 服务实例列表供应器的提供者(ObjectProvider)用于获取可用的服务实例列表供应器
* @param serviceId 服务ID(服务名称),表示当前负载均衡器为哪个服务提供负载均衡
*/
public RandomLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider,
String serviceId) {
this.serviceId = serviceId;
// 初始化单例供应器:若提供者中无可用实例,则使用默认的空实现(NoopServiceInstanceListSupplier)
this.serviceInstanceListSingletonSupplier = SingletonSupplier
.of(() -> serviceInstanceListSupplierProvider.getIfAvailable(NoopServiceInstanceListSupplier::new));
}
/**
* 【核心方法】
* 选择一个服务实例处理请求(响应式实现)
*
* @param request 客户端请求对象(包含请求上下文等信息,此处随机策略暂不依赖具体请求内容)
* @return 包含选中服务实例的响应式对象(Mono<Response<ServiceInstance>>)
*/
@SuppressWarnings("rawtypes")
@Override
public Mono<Response<ServiceInstance>> choose(Request request) {
// 获取服务实例列表供应器(从单例提供者中获取,确保唯一)
ServiceInstanceListSupplier supplier = serviceInstanceListSingletonSupplier.obtain();
// 调用供应器获取可用服务实例列表(响应式流),并处理结果
return supplier.get(request)
.next() // 取流中的第一个元素(即最新的服务实例列表)
// 处理实例列表,选择目标实例
.map(serviceInstances -> processInstanceResponse(supplier, serviceInstances));
}
/**
* 处理服务实例列表,完成实例选择并触发回调(若有)
*
* @param supplier 服务实例列表供应器(可能包含实例选择后的回调逻辑)
* @param serviceInstances 可用的服务实例列表
* @return 包含选中实例的响应对象
*/
private Response<ServiceInstance> processInstanceResponse(ServiceInstanceListSupplier supplier,
List<ServiceInstance> serviceInstances) {
// 调用核心逻辑选择一个实例
Response<ServiceInstance> serviceInstanceResponse = getInstanceResponse(serviceInstances);
// 若供应器实现了选中实例的回调接口,且成功选中了实例,则触发回调(如记录选中日志等)
if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) {
((SelectedInstanceCallback) supplier).selectedServiceInstance(serviceInstanceResponse.getServer());
}
return serviceInstanceResponse;
}
/**
* 随机选择服务实例的核心逻辑
*
* @param instances 可用的服务实例列表
* @return 包含选中实例的响应对象(若列表为空则返回空响应)
*/
private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances) {
// 若实例列表为空,记录警告日志并返回空响应
if (instances.isEmpty()) {
if (log.isWarnEnabled()) {
log.warn("No servers available for service: " + serviceId);
}
return new EmptyResponse();
}
// 生成随机索引:范围为 0 到实例列表大小-1(线程安全的随机数生成器)
int index = ThreadLocalRandom.current().nextInt(instances.size());
// 根据随机索引从列表中获取实例
ServiceInstance instance = instances.get(index);
// 返回包含选中实例的默认响应
return new DefaultResponse(instance);
}
}上述代码源码中,通过 ThreadLocalRandom.current().nextInt(instances.size()) 获取范围为 0 到实例列表大小-1 的索引值,然后从实例列表中获取对应的 ServiceInstance 实例。
更多信息请参考官方文档……