Spring Cloud LoadBalancer 算法
Spring Cloud LoadBalancer 常用负载均衡算法
-
轮询 (Round Robin)
- 原理:将请求依次分配给每个服务实例,从第一个实例开始,直到最后一个实例,然后重新开始。
- 优点:简单且能够平衡负载,适用于每个实例处理能力相似的场景。
- 示例配置:Spring Cloud LoadBalancer 默认使用 Round Robin 策略。
spring: cloud: loadbalancer: strategy: round-robin: true # 设置轮询策略 ribbon: enabled: false # 禁用 Ribbon
-
随机 (Random)
- 原理:将请求随机分配给一个服务实例。
- 优点:简单且在负载分布上具有随机性,能防止请求集中到某个实例上。
- 示例配置:通过配置文件来指定使用随机策略。
spring: cloud: loadbalancer: strategy: random: true ribbon: enabled: false # 禁用 Ribbon
-
权重 (Weighted)
- 原理:根据实例的权重分配请求,权重越高的实例收到的请求越多。
- 优点:适用于服务实例处理能力不同的场景,通过权重调整负载分布。
- 自定义实现:需要自定义实现 Weighted 负载均衡策略。
-
基于响应时间 (Response Time)
- 原理:根据实例的响应时间分配请求,响应时间短的实例优先接收请求。
- 优点:提高整体响应速度,适用于实例响应时间差异较大的场景。
- 自定义实现:需要自定义实现 Response Time 负载均衡策略。
自定义负载均衡策略
如果内置的负载均衡算法无法满足需求,Spring Cloud LoadBalancer 允许开发者自定义负载均衡策略。以下是一个自定义负载均衡策略的示例:
-
实现
ReactorServiceInstanceLoadBalancer
接口:import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.loadbalancer.reactive.ReactorServiceInstanceLoadBalancer; import org.springframework.cloud.client.loadbalancer.reactive.Request; import org.springframework.cloud.client.loadbalancer.reactive.Response; import org.springframework.cloud.loadbalancer.support.ServiceInstanceListSupplier; import reactor.core.publisher.Mono; import org.springframework.cloud.client.loadbalancer.reactive.DefaultResponse; import org.springframework.cloud.client.loadbalancer.reactive.EmptyResponse; import java.util.List; import java.util.Random; public class RoundRobinLoadBalancer implements ReactorServiceInstanceLoadBalancer { private final String serviceId; private final ServiceInstanceListSupplier serviceInstanceListSupplier; public CustomLoadBalancer(String serviceId, ServiceInstanceListSupplier serviceInstanceListSupplier) { this.serviceId = serviceId; this.serviceInstanceListSupplier = serviceInstanceListSupplier; } @Override public Mono<Response<ServiceInstance>> choose(Request request) { // 获取服务实例列表 return serviceInstanceListSupplier.get(request) .next() // 获取服务实例列表的第一个结果 .map(instances -> { if (instances.isEmpty()) { return new EmptyResponse(); } // 随机选择一个服务实例 ServiceInstance instance = instances.get(new Random().nextInt(instances.size())); return new DefaultResponse(instance); }); } }
-
配置自定义负载均衡策略:
import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.cloud.client.loadbalancer.LoadBalancerClientFactory; import org.springframework.cloud.client.loadbalancer.reactive.ReactorServiceInstanceLoadBalancer; import org.springframework.cloud.loadbalancer.support.ServiceInstanceListSupplier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; import org.springframework.web.client.RestTemplate; @Configuration public class RestTemplateConfig { // 配置 RestTemplate 并启用负载均衡 @Bean @LoadBalanced public RestTemplate restTemplate() { return new RestTemplate(); } // 配置自定义负载均衡器 @Bean public ReactorServiceInstanceLoadBalancer customLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory) { // 从环境变量中获取服务名称 String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME); // 获取 ServiceInstanceListSupplier,用于提供服务实例列表 ServiceInstanceListSupplier supplier = loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class).getIfAvailable(); // 返回自定义负载均衡器实例 return new RoundRobinLoadBalancer(name, supplier); } }
配置文件示例
通过配置文件指定负载均衡策略,例如使用随机策略:
spring:
cloud:
loadbalancer:
strategy:
random: true
ribbon:
enabled: false # 禁用 Ribbon
配置类切换
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClientFactory;
import org.springframework.cloud.client.loadbalancer.reactive.ReactorServiceInstanceLoadBalancer;
import org.springframework.cloud.loadbalancer.support.ServiceInstanceListSupplier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.web.client.RestTemplate;
@Configuration
public class RestTemplateConfig {
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
@Bean
ReactorLoadBalancer<ServiceInstance> roundRobinLoadBalancer(Environment environment,
LoadBalancerClientFactory loadBalancerClientFactory) {
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
// LoadBalancer自带的轮询算法
return new RoundRobinLoadBalancer(loadBalancerClientFactory.getLazyProvider(name,ServiceInstanceListSupplier.class), name);
// LoadBalancer自带的随机算法
return new RandomLoadBalancer(loadBalancerClientFactory.getLazyProvider(name,ServiceInstanceListSupplier.class), name);
}
负载均衡算法详解
详细解释
1. 轮询 (Round Robin)
轮询是最简单的负载均衡算法之一,适用于大多数场景。假设有三个服务实例 A、B、C,轮询算法会依次将请求分配给 A、B、C,然后重新从 A 开始。每个实例接收的请求数量大致相等,能够很好地平衡负载。
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.reactive.ReactorServiceInstanceLoadBalancer;
import org.springframework.cloud.client.loadbalancer.reactive.Request;
import org.springframework.cloud.client.loadbalancer.reactive.Response;
import org.springframework.cloud.loadbalancer.support.ServiceInstanceListSupplier;
import reactor.core.publisher.Mono;
import org.springframework.cloud.client.loadbalancer.reactive.DefaultResponse;
import org.springframework.cloud.client.loadbalancer.reactive.EmptyResponse;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
public class RoundRobinLoadBalancer implements ReactorServiceInstanceLoadBalancer {
private final String serviceId; // 服务 ID
private final ServiceInstanceListSupplier serviceInstanceListSupplier; // 服务实例列表供应商
private final AtomicInteger position; // 原子整数,用于记录当前位置
public RoundRobinLoadBalancer(String serviceId, ServiceInstanceListSupplier serviceInstanceListSupplier) {
this.serviceId = serviceId;
this.serviceInstanceListSupplier = serviceInstanceListSupplier;
this.position = new AtomicInteger(0); // 初始化位置
}
@Override
public Mono<Response<ServiceInstance>> choose(Request request) {
// 获取服务实例列表
return serviceInstanceListSupplier.get(request)
.next()
.map(instances -> {
if (instances.isEmpty()) {
return new EmptyResponse(); // 如果实例列表为空,返回空响应
}
// 使用取模运算计算当前的位置,并选择相应的实例
int pos = Math.abs(position.getAndIncrement() % instances.size());
ServiceInstance instance = instances.get(pos);
return new DefaultResponse(instance); // 返回默认响应
});
}
}
2. 随机 (Random)
随机算法通过随机选择一个实例来处理每个请求。由于随机性,每个实例接收的请求数量不一定相等,但长时间运行后,分配的请求数量会趋于均衡。该算法简单有效,适用于大多数场景。
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.reactive.ReactorServiceInstanceLoadBalancer;
import org.springframework.cloud.client.loadbalancer.reactive.Request;
import org.springframework.cloud.client.loadbalancer.reactive.Response;
import org.springframework.cloud.loadbalancer.support.ServiceInstanceListSupplier;
import reactor.core.publisher.Mono;
import org.springframework.cloud.client.loadbalancer.reactive.DefaultResponse;
import org.springframework.cloud.client.loadbalancer.reactive.EmptyResponse;
import java.util.List;
import java.util.Random;
public class CustomLoadBalancer implements ReactorServiceInstanceLoadBalancer {
private final String serviceId; // 服务 ID
private final ServiceInstanceListSupplier serviceInstanceListSupplier; // 服务实例列表供应商
public CustomLoadBalancer(String serviceId, ServiceInstanceListSupplier serviceInstanceListSupplier) {
this.serviceId = serviceId;
this.serviceInstanceListSupplier = serviceInstanceListSupplier;
}
@Override
public Mono<Response<ServiceInstance>> choose(Request request) {
// 获取服务实例列表
return serviceInstanceListSupplier.get(request)
.next() // 获取服务实例列表的第一个结果
.map(instances -> {
if (instances.isEmpty()) {
return new EmptyResponse(); // 如果实例列表为空,返回空响应
}
// 随机选择一个服务实例
ServiceInstance instance = instances.get(new Random().nextInt(instances.size()));
return new DefaultResponse(instance); // 返回默认响应
});
}
}
3. 权重 (Weighted)
权重算法根据实例的权重分配请求。假设有三个实例 A、B、C,权重分别为 2、3、5,总权重为 10,则 A 处理 20% 的请求,B 处理 30% 的请求,C 处理 50% 的请求。权重可以根据实例的处理能力或其他指标进行设置,从而实现更精细的负载均衡。
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.reactive.ReactorServiceInstanceLoadBalancer;
import org.springframework.cloud.client.loadbalancer.reactive.Request;
import org.springframework.cloud.client.loadbalancer.reactive.Response;
import org.springframework.cloud.loadbalancer.support.ServiceInstanceListSupplier;
import reactor.core.publisher.Mono;
import org.springframework.cloud.client.loadbalancer.reactive.DefaultResponse;
import org.springframework.cloud.client.loadbalancer.reactive.EmptyResponse;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.stream.Collectors;
public class WeightedLoadBalancer implements ReactorServiceInstanceLoadBalancer {
private final String serviceId; // 服务 ID
private final ServiceInstanceListSupplier serviceInstanceListSupplier; // 服务实例列表供应商
private final Map<ServiceInstance, Integer> weights; // 实例权重映射
public WeightedLoadBalancer(String serviceId, ServiceInstanceListSupplier serviceInstanceListSupplier, Map<ServiceInstance, Integer> weights) {
this.serviceId = serviceId;
this.serviceInstanceListSupplier = serviceInstanceListSupplier;
this.weights = weights;
}
@Override
public Mono<Response<ServiceInstance>> choose(Request request) {
return serviceInstanceListSupplier.get(request) // 获取服务实例列表
.next()
.map(instances -> {
if (instances.isEmpty()) {
return new EmptyResponse(); // 如果实例列表为空,返回空响应
}
// 根据权重展开实例列表
List<ServiceInstance> weightedInstances = instances.stream()
.flatMap(instance -> weights.containsKey(instance) ?
java.util.stream.Stream.generate(() -> instance).limit(weights.get(instance)) :
java.util.stream.Stream.empty())
.collect(Collectors.toList());
if (weightedInstances.isEmpty()) {
return new EmptyResponse(); // 如果加权后的实例列表为空,返回空响应
}
// 随机选择一个加权后的实例
ServiceInstance instance = weightedInstances.get(new Random().nextInt(weightedInstances.size()));
return new DefaultResponse(instance); // 返回默认响应
});
}
}
4. 基于响应时间 (Response Time)
基于响应时间的负载均衡算法会监控每个实例的响应时间,将请求优先分配给响应时间短的实例。这种方法可以动态调整负载分配,避免将请求分配给繁忙或性能差的实例,提高整体系统的响应速度。
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.reactive.ReactorServiceInstanceLoadBalancer;
import org.springframework.cloud.client.loadbalancer.reactive.Request;
import org.springframework.cloud.client.loadbalancer.reactive.Response;
import org.springframework.cloud.loadbalancer.support.ServiceInstanceListSupplier;
import reactor.core.publisher.Mono;
import org.springframework.cloud.client.loadbalancer.reactive.DefaultResponse;
import org.springframework.cloud.client.loadbalancer.reactive.EmptyResponse;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Random;
public class ResponseTimeLoadBalancer implements ReactorServiceInstanceLoadBalancer {
private final String serviceId; // 服务 ID
private final ServiceInstanceListSupplier serviceInstanceListSupplier; // 服务实例列表供应商
private final Map<String, Long> responseTimeCache; // 响应时间缓存
public ResponseTimeLoadBalancer(String serviceId, ServiceInstanceListSupplier serviceInstanceListSupplier, Map<String, Long> responseTimeCache) {
this.serviceId = serviceId;
this.serviceInstanceListSupplier = serviceInstanceListSupplier;
this.responseTimeCache = responseTimeCache;
}
@Override
public Mono<Response<ServiceInstance>> choose(Request request) {
return serviceInstanceListSupplier.get(request) // 获取服务实例列表
.next()
.map(instances -> {
if (instances.isEmpty()) {
return new EmptyResponse(); // 如果实例列表为空,返回空响应
}
instances.sort(Comparator.comparing(this::getResponseTime)); // 按响应时间排序实例
ServiceInstance instance = instances.get(0); // 选择响应时间最短的实例
return new DefaultResponse(instance); // 返回默认响应
});
}
private long getResponseTime(ServiceInstance instance) {
// 从监控系统获取实例响应时间的逻辑
return fetchResponseTimeFromMonitoringSystem(instance); // 调用方法获取响应时间
}
private long fetchResponseTimeFromMonitoringSystem(ServiceInstance instance) {
// 模拟从监控系统中获取响应时间
// 在实际应用中,这可以是对监控系统 API 的调用,或从缓存/数据库中获取
String instanceId = instance.getInstanceId(); // 获取实例 ID
return responseTimeCache.getOrDefault(instanceId, Long.MAX_VALUE); // 返回缓存中的响应时间或默认值
}
}
本文是原创文章,采用 CC BY-NC-ND 4.0 协议,完整转载请注明来自 David
评论
匿名评论
隐私政策
你无需删除空行,直接评论以获取最佳展示效果