Spring Cloud LoadBalancer 常用负载均衡算法

  1. 轮询 (Round Robin)

    • 原理:将请求依次分配给每个服务实例,从第一个实例开始,直到最后一个实例,然后重新开始。
    • 优点:简单且能够平衡负载,适用于每个实例处理能力相似的场景。
    • 示例配置:Spring Cloud LoadBalancer 默认使用 Round Robin 策略。
      spring:
      cloud:
        loadbalancer:
          strategy:
            round-robin: true  # 设置轮询策略
          ribbon:
            enabled: false  # 禁用 Ribbon
      
  2. 随机 (Random)

    • 原理:将请求随机分配给一个服务实例。
    • 优点:简单且在负载分布上具有随机性,能防止请求集中到某个实例上。
    • 示例配置:通过配置文件来指定使用随机策略。
      spring:
        cloud:
          loadbalancer:
            strategy:
              random: true
            ribbon:
              enabled: false  # 禁用 Ribbon
      
  3. 权重 (Weighted)

    • 原理:根据实例的权重分配请求,权重越高的实例收到的请求越多。
    • 优点:适用于服务实例处理能力不同的场景,通过权重调整负载分布。
    • 自定义实现:需要自定义实现 Weighted 负载均衡策略。
  4. 基于响应时间 (Response Time)

    • 原理:根据实例的响应时间分配请求,响应时间短的实例优先接收请求。
    • 优点:提高整体响应速度,适用于实例响应时间差异较大的场景。
    • 自定义实现:需要自定义实现 Response Time 负载均衡策略。

自定义负载均衡策略

如果内置的负载均衡算法无法满足需求,Spring Cloud LoadBalancer 允许开发者自定义负载均衡策略。以下是一个自定义负载均衡策略的示例:

  1. 实现 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);
                    });
        }
    }
    
  2. 配置自定义负载均衡策略

    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); // 返回缓存中的响应时间或默认值
    }
}