在微服务架构中,客户端负载均衡是保障服务高可用与高性能的关键组件。Spring Cloud LoadBalancer 作为 Spring Cloud 官方推出的新一代负载均衡器,不仅提供了轻量级的内置策略,还支持灵活的自定义扩展,完美适配现代响应式编程模型。本文将深入解析其核心原理、与 Ribbon 的主要区别,并通过从基础使用到自定义策略的完整示例,帮助您快速掌握这一重要工具,为构建健壮的微服务系统打下坚实基础。

一,Spring Cloud LoadBalancer‌原理

Spring Cloud LoadBalancer是Spring Cloud官方推出的‌客户端负载均衡组件‌,核心工作原理如下:

1. 核心执行流程

客户端从Nacos/Eureka等注册中心拉取可用服务实例列表,缓存到本地。
通过LoadBalancerClientFactory获取对应服务的负载均衡器实例。
调用内置的choose()方法,基于轮询/随机等预设策略,从实例列表中选中一个目标服务节点。
重构请求URL,直接向选中的实例发起远程调用。

2. 核心内置实现

它提供了两个默认负载均衡器:

RoundRobinLoadBalancer:轮询策略,按顺序依次分发请求,是默认策略。
RandomLoadBalancer:随机策略,随机从可用实例中挑选节点。
同时支持通过实现ReactorServiceInstanceLoadBalancer接口,快速自定义负载均衡策略。

3. 关键特性

它同时支持阻塞式和响应式编程模型,适配RestTemplate、Feign、WebClient等多种调用方式,完全替代已停更的Ribbon组件,适配新版Spring Cloud全生态。

二,Spring Cloud LoadBalancer和Ribbon的核心区别如下:

一、基础定位与维护状态

‌Ribbon‌:Netflix开源的老牌负载均衡组件,目前已‌停止官方维护‌,仅适配Spring Cloud早期版本。
‌Spring Cloud LoadBalancer‌:Spring Cloud官方自研的新一代负载均衡组件,是官方指定的Ribbon替代方案,持续迭代更新,适配所有新版本Spring Cloud。

二、核心特性差异

‌编程模型‌:Ribbon仅支持传统阻塞式编程;LoadBalancer同时支持阻塞式和响应式编程,适配WebFlux等现代技术栈。
‌内置策略‌:Ribbon提供7种以上内置负载均衡策略;LoadBalancer默认仅内置轮询、随机两种基础策略,更轻量化,支持快速自定义扩展。
‌生态兼容性‌:Ribbon和Eureka、Hystrix等旧版Netflix生态深度绑定;LoadBalancer原生适配Nacos、Spring Cloud Gateway等新一代Spring Cloud生态组件。

三、适用场景

‌Ribbon‌:仅适合维护老旧Spring Cloud项目,不建议新项目使用。
‌Spring Cloud LoadBalancer‌:所有新开发的微服务项目的首选方案,性能更优、扩展性更强,长期维护性更好。

三、简单示例

下面是一个使用 Spring Cloud LoadBalancer 配合 WebClient 进行服务调用的简单示例。

1. 添加依赖

在 pom.xml 中添加必要的依赖:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

2. 配置启用负载均衡

确保您的应用已连接到服务注册中心(如 Nacos、Eureka)。

3. 创建负载均衡的 WebClient

使用 @LoadBalanced 注解创建一个具备负载均衡能力的 WebClient.Builder Bean。

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.client.WebClient;

@Configuration
public class WebClientConfig {

    @Bean
    @LoadBalanced
    public WebClient.Builder loadBalancedWebClientBuilder() {
        return WebClient.builder();
    }
}

4. 使用 WebClient 调用服务

在您的服务类中注入 WebClient.Builder,并使用服务名(而非具体IP和端口)发起请求。

import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

@Service
public class UserService {

    private final WebClient webClient;

    public UserService(WebClient.Builder webClientBuilder) {
        // 使用服务名 "user-service" 构建 WebClient
        this.webClient = webClientBuilder.baseUrl("http://user-service").build();
    }

    public Mono<String> getUserInfo(Long userId) {
        return this.webClient
                .get()
                .uri("/users/{id}", userId)
                .retrieve()
                .bodyToMono(String.class);
    }
}

示例说明:

  • 当调用 getUserInfo 方法时,WebClient 会通过 Spring Cloud LoadBalancer 将请求 http://user-service/users/{id} 负载均衡到名为 user-service 的某个健康实例上。
  • LoadBalancer 会自动从注册中心获取 user-service 的实例列表,并依据默认的轮询策略选择一个实例。

通过以上几步,您就完成了一个简单的、具备客户端负载均衡能力的服务调用。

4. 自定义负载均衡策略示例

Spring Cloud LoadBalancer 支持通过实现 ReactorServiceInstanceLoadBalancer 接口来自定义负载均衡策略。下面展示一个简单的基于权重的选择策略示例,假设每个服务实例有一个权重属性,权重越高的实例被选中的概率越大。

4.1 实现自定义负载均衡器

创建一个实现 ReactorServiceInstanceLoadBalancer 接口的类:

import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.DefaultResponse;
import org.springframework.cloud.client.loadbalancer.EmptyResponse;
import org.springframework.cloud.client.loadbalancer.Request;
import org.springframework.cloud.client.loadbalancer.Response;
import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer;
import reactor.core.publisher.Mono;

import java.util.List;
import java.util.concurrent.ThreadLocalRandom;

public class WeightedLoadBalancer implements ReactorServiceInstanceLoadBalancer {

    private final String serviceId;
    private final List<ServiceInstance> serviceInstances;

    public WeightedLoadBalancer(String serviceId, List<ServiceInstance> serviceInstances) {
        this.serviceId = serviceId;
        this.serviceInstances = serviceInstances;
    }

    @Override
    public Mono<Response<ServiceInstance>> choose(Request request) {
        if (serviceInstances.isEmpty()) {
            return Mono.just(new EmptyResponse());
        }

        // 计算总权重
        int totalWeight = serviceInstances.stream()
                .mapToInt(instance -> getWeight(instance))
                .sum();

        if (totalWeight <= 0) {
            // 如果所有实例权重都<=0,则随机选择一个
            int randomIndex = ThreadLocalRandom.current().nextInt(serviceInstances.size());
            return Mono.just(new DefaultResponse(serviceInstances.get(randomIndex)));
        }

        // 生成一个 [0, totalWeight) 的随机数
        int randomWeight = ThreadLocalRandom.current().nextInt(totalWeight);
        int currentWeight = 0;

        // 按权重区间选择实例
        for (ServiceInstance instance : serviceInstances) {
            currentWeight += getWeight(instance);
            if (randomWeight < currentWeight) {
                return Mono.just(new DefaultResponse(instance));
            }
        }

        // 兜底:返回第一个实例
        return Mono.just(new DefaultResponse(serviceInstances.get(0)));
    }

    /**
     * 从实例的元数据(metadata)中获取权重值,默认权重为1
     */
    private int getWeight(ServiceInstance instance) {
        String weightStr = instance.getMetadata().get("weight");
        if (weightStr != null) {
            try {
                return Integer.parseInt(weightStr);
            } catch (NumberFormatException e) {
                // 解析失败则返回默认权重
            }
        }
        return 1; // 默认权重
    }
}

4.2 配置自定义负载均衡器

创建一个配置类,将自定义的负载均衡器注册到 Spring 容器中:

import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

import java.util.List;

@Configuration
public class LoadBalancerConfiguration {

    @Bean
    @Primary
    public ReactorServiceInstanceLoadBalancer customLoadBalancer(
            DiscoveryClient discoveryClient,
            ServiceInstanceListSupplier supplier) {
        
        return serviceInstanceList -> supplier.get()
                .next()
                .map(instances -> {
                    // 这里可以针对特定服务进行配置
                    String serviceId = "user-service"; // 替换为你的服务名
                    List<ServiceInstance> serviceInstances = discoveryClient.getInstances(serviceId);
                    return new WeightedLoadBalancer(serviceId, serviceInstances);
                });
    }
}

4.3 为服务实例配置权重

在服务实例的注册信息中添加权重元数据。以 Nacos 为例,在服务的 bootstrap.yml 或 application.yml 中配置:

spring:
  cloud:
    nacos:
      discovery:
        metadata:
          weight: "3"  # 权重值,数字越大被选中的概率越高

4.4 使用自定义策略

应用启动后,所有对 user-service 的负载均衡请求都会使用我们实现的权重策略。你无需修改之前的 WebClient 调用代码,LoadBalancer 会自动使用注册的自定义负载均衡器。

实现原理说明:

  1. WeightedLoadBalancer 从服务实例的元数据中读取 weight 字段作为权重值。
  2. 选择时根据所有权重值总和生成随机数,落在哪个实例的权重区间就选择哪个实例。
  3. 权重越高,区间范围越大,被选中的概率就越高。
  4. 通过 LoadBalancerConfiguration 将自定义负载均衡器注册为特定服务的默认实现。

通过这个示例,你可以看到 Spring Cloud LoadBalancer 的自定义扩展非常灵活,只需实现一个接口即可定义复杂的负载均衡逻辑。

更多推荐