25-Spring Cloud 负载均衡详解

4 阅读6分钟

Spring Cloud 负载均衡详解

一、知识概述

负载均衡是微服务架构中的关键技术,它将请求分发到多个服务实例上,提高系统的吞吐量和可用性。Spring Cloud 提供了多种负载均衡解决方案,包括 Ribbon(已维护模式)和 Spring Cloud LoadBalancer。

负载均衡的核心功能:

  • 请求分发:将请求分发到多个实例
  • 实例选择:根据策略选择合适的实例
  • 健康检查:剔除不健康的实例
  • 重试机制:失败后重试其他实例

理解负载均衡的原理和配置,是构建高可用微服务系统的重要技能。

二、知识点详细讲解

2.1 负载均衡分类

服务端负载均衡
客户端 → 负载均衡器(Nginx/F5) → 服务实例1
                             → 服务实例2
                             → 服务实例3

特点:

  • 集中管理
  • 需要独立部署
  • 有单点故障风险
客户端负载均衡
客户端(内置负载均衡)→ 服务实例1
                      → 服务实例2
                      → 服务实例3

特点:

  • 去中心化
  • 无需独立部署
  • 客户端逻辑复杂

2.2 负载均衡算法

算法说明适用场景
轮询依次访问每个实例实例性能相近
随机随机选择实例实例性能相近
加权轮询按权重轮询实例性能不同
加权随机按权重随机实例性能不同
最少连接选择连接数最少的长连接场景
一致性哈希相同请求路由到相同实例缓存场景
响应时间加权响应时间短权重高性能敏感场景

2.3 Spring Cloud LoadBalancer

Spring Cloud LoadBalancer 是 Spring Cloud 官方推荐的负载均衡器,用于替代 Ribbon。

核心组件
  • ServiceInstanceListSupplier:服务实例列表提供者
  • ReactorServiceInstanceLoadBalancer:负载均衡器
  • LoadBalancerClient:负载均衡客户端
  • LoadBalancerClientFactory:负载均衡客户端工厂

2.4 Ribbon 核心组件

虽然 Ribbon 已进入维护模式,但很多项目仍在使用:

  • ServerList:服务器列表
  • IRule:负载均衡规则
  • IPing:健康检查
  • ServerListFilter:服务器过滤

2.5 负载均衡配置

spring:
  cloud:
    loadbalancer:
      ribbon:
        enabled: false  # 禁用 Ribbon
      cache:
        enabled: true
        ttl: 35s
      health-check:
        initial-delay: 0
        interval: 25s

三、代码示例

3.1 RestTemplate + 负载均衡

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
@RestController
public class LoadBalancerApplication {
    
    public static void main(String[] args) {
        SpringApplication.run(LoadBalancerApplication.class, args);
    }
    
    @Bean
    @LoadBalanced  // 启用负载均衡
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
    
    @Autowired
    private RestTemplate restTemplate;
    
    @GetMapping("/call")
    public String callService() {
        // 使用服务名调用,自动负载均衡
        return restTemplate.getForObject(
            "http://user-service/api/users/1", 
            String.class
        );
    }
}

3.2 WebClient + 负载均衡

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.reactive.function.client.WebClient;

@SpringBootApplication
@RestController
public class ReactiveLoadBalancerApplication {
    
    public static void main(String[] args) {
        SpringApplication.run(ReactiveLoadBalancerApplication.class, args);
    }
    
    @Bean
    @LoadBalanced
    public WebClient.Builder webClientBuilder() {
        return WebClient.builder();
    }
    
    @Autowired
    private WebClient.Builder webClientBuilder;
    
    @GetMapping("/reactive")
    public Mono<String> reactiveCall() {
        return webClientBuilder.build()
            .get()
            .uri("http://user-service/api/users/1")
            .retrieve()
            .bodyToMono(String.class);
    }
}

3.3 自定义负载均衡策略

import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.loadbalancer.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import reactor.core.publisher.Mono;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;

@Configuration
public class CustomLoadBalancerConfig {
    
    // 自定义轮询负载均衡器
    @Bean
    ReactorServiceInstanceLoadBalancer customLoadBalancer(
            ServiceInstanceListSupplier serviceInstanceListSupplier) {
        return new CustomRoundRobinLoadBalancer(serviceInstanceListSupplier);
    }
}

class CustomRoundRobinLoadBalancer implements ReactorServiceInstanceLoadBalancer {
    
    private final ServiceInstanceListSupplier serviceInstanceListSupplier;
    private final AtomicInteger position = new AtomicInteger(0);
    
    public CustomRoundRobinLoadBalancer(ServiceInstanceListSupplier serviceInstanceListSupplier) {
        this.serviceInstanceListSupplier = serviceInstanceListSupplier;
    }
    
    @Override
    public Mono<Response<ServiceInstance>> choose(Request request) {
        return serviceInstanceListSupplier.get()
            .next()
            .map(instances -> {
                if (instances.isEmpty()) {
                    return new EmptyResponse();
                }
                
                int pos = Math.abs(position.incrementAndGet() % instances.size());
                ServiceInstance instance = instances.get(pos);
                
                return new DefaultResponse(instance);
            });
    }
}

3.4 加权负载均衡

import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.loadbalancer.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import reactor.core.publisher.Mono;
import java.util.*;
import java.util.concurrent.ThreadLocalRandom;

@Configuration
public class WeightedLoadBalancerConfig {
    
    @Bean
    ReactorServiceInstanceLoadBalancer weightedLoadBalancer(
            ServiceInstanceListSupplier supplier) {
        return new WeightedLoadBalancer(supplier);
    }
}

class WeightedLoadBalancer implements ReactorServiceInstanceLoadBalancer {
    
    private final ServiceInstanceListSupplier supplier;
    
    public WeightedLoadBalancer(ServiceInstanceListSupplier supplier) {
        this.supplier = supplier;
    }
    
    @Override
    public Mono<Response<ServiceInstance>> choose(Request request) {
        return supplier.get()
            .next()
            .map(instances -> {
                if (instances.isEmpty()) {
                    return new EmptyResponse();
                }
                
                // 计算总权重
                int totalWeight = instances.stream()
                    .mapToInt(this::getWeight)
                    .sum();
                
                // 加权随机选择
                int random = ThreadLocalRandom.current().nextInt(totalWeight);
                int currentWeight = 0;
                
                for (ServiceInstance instance : instances) {
                    currentWeight += getWeight(instance);
                    if (random < currentWeight) {
                        return new DefaultResponse(instance);
                    }
                }
                
                return new DefaultResponse(instances.get(0));
            });
    }
    
    private int getWeight(ServiceInstance instance) {
        Map<String, String> metadata = instance.getMetadata();
        if (metadata != null && metadata.containsKey("weight")) {
            try {
                return Integer.parseInt(metadata.get("weight"));
            } catch (NumberFormatException e) {
                return 1;
            }
        }
        return 1;
    }
}

3.5 一致性哈希负载均衡

import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.loadbalancer.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import reactor.core.publisher.Mono;
import java.util.*;

@Configuration
public class ConsistentHashLoadBalancerConfig {
    
    @Bean
    ReactorServiceInstanceLoadBalancer consistentHashLoadBalancer(
            ServiceInstanceListSupplier supplier) {
        return new ConsistentHashLoadBalancer(supplier);
    }
}

class ConsistentHashLoadBalancer implements ReactorServiceInstanceLoadBalancer {
    
    private final ServiceInstanceListSupplier supplier;
    private final SortedMap<Integer, ServiceInstance> circle = new TreeMap<>();
    private static final int VIRTUAL_NODES = 150;
    
    public ConsistentHashLoadBalancer(ServiceInstanceListSupplier supplier) {
        this.supplier = supplier;
    }
    
    @Override
    public Mono<Response<ServiceInstance>> choose(Request request) {
        return supplier.get()
            .next()
            .map(instances -> {
                if (instances.isEmpty()) {
                    return new EmptyResponse();
                }
                
                // 构建哈希环
                buildCircle(instances);
                
                // 获取哈希键
                String hashKey = getHashKey(request);
                int hash = hash(hashKey);
                
                // 顺时针查找
                SortedMap<Integer, ServiceInstance> tailMap = circle.tailMap(hash);
                Integer key = tailMap.isEmpty() ? circle.firstKey() : tailMap.firstKey();
                
                return new DefaultResponse(circle.get(key));
            });
    }
    
    private void buildCircle(List<ServiceInstance> instances) {
        circle.clear();
        for (ServiceInstance instance : instances) {
            for (int i = 0; i < VIRTUAL_NODES; i++) {
                String virtualNode = instance.getHost() + ":" + instance.getPort() + "#" + i;
                int hash = hash(virtualNode);
                circle.put(hash, instance);
            }
        }
    }
    
    private String getHashKey(Request request) {
        // 从请求中获取哈希键(如用户ID、请求ID等)
        if (request instanceof DefaultRequest) {
            Object context = ((DefaultRequest<?>) request).getContext();
            if (context instanceof HttpRequestData) {
                // 可以从请求头、参数等获取
                return ((HttpRequestData) context).getPath();
            }
        }
        return String.valueOf(System.currentTimeMillis());
    }
    
    private int hash(String key) {
        return key.hashCode();
    }
}

3.6 服务实例过滤器

import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import reactor.core.publisher.Flux;
import java.util.*;
import java.util.stream.Collectors;

@Configuration
public class ServiceInstanceFilterConfig {
    
    @Bean
    public ServiceInstanceListSupplier healthyInstanceSupplier(
            ServiceInstanceListSupplier delegate) {
        return new HealthyServiceInstanceSupplier(delegate);
    }
}

class HealthyServiceInstanceSupplier implements ServiceInstanceListSupplier {
    
    private final ServiceInstanceListSupplier delegate;
    
    public HealthyServiceInstanceSupplier(ServiceInstanceListSupplier delegate) {
        this.delegate = delegate;
    }
    
    @Override
    public String getServiceId() {
        return delegate.getServiceId();
    }
    
    @Override
    public Flux<List<ServiceInstance>> get() {
        return delegate.get()
            .map(instances -> instances.stream()
                .filter(this::isHealthy)
                .collect(Collectors.toList()));
    }
    
    private boolean isHealthy(ServiceInstance instance) {
        // 检查实例是否健康
        Map<String, String> metadata = instance.getMetadata();
        if (metadata != null) {
            String status = metadata.get("status");
            return status == null || "UP".equalsIgnoreCase(status);
        }
        return true;
    }
}

// 版本过滤
class VersionServiceInstanceSupplier implements ServiceInstanceListSupplier {
    
    private final ServiceInstanceListSupplier delegate;
    private final String targetVersion;
    
    public VersionServiceInstanceSupplier(ServiceInstanceListSupplier delegate, 
                                          String targetVersion) {
        this.delegate = delegate;
        this.targetVersion = targetVersion;
    }
    
    @Override
    public String getServiceId() {
        return delegate.getServiceId();
    }
    
    @Override
    public Flux<List<ServiceInstance>> get() {
        return delegate.get()
            .map(instances -> instances.stream()
                .filter(instance -> {
                    Map<String, String> metadata = instance.getMetadata();
                    if (metadata != null && metadata.containsKey("version")) {
                        return targetVersion.equals(metadata.get("version"));
                    }
                    return true;
                })
                .collect(Collectors.toList()));
    }
}

3.7 重试机制

# application.yml
spring:
  cloud:
    loadbalancer:
      retry:
        enabled: true
        retry-on-all-operations: false  # 只对 GET 请求重试
        max-retries-on-next-service-instance: 1
        max-retries-on-same-service-instance: 0
import org.springframework.cloud.client.loadbalancer.*;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;

@Configuration
@LoadBalancerClient(name = "user-service", configuration = RetryConfig.class)
public class RetryConfig {
    
    @Bean
    public ServiceInstanceListSupplier retryAwareSupplier(
            ServiceInstanceListSupplier delegate) {
        return new RetryAwareServiceInstanceSupplier(delegate);
    }
}

@Service
public class RetryService {
    
    @Autowired
    private RestTemplate restTemplate;
    
    @Retryable(
        value = {Exception.class},
        maxAttempts = 3,
        backoff = @Backoff(delay = 1000, multiplier = 2)
    )
    public String callWithRetry() {
        return restTemplate.getForObject(
            "http://user-service/api/users/1",
            String.class
        );
    }
}

3.8 负载均衡指标

import io.micrometer.core.instrument.*;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.loadbalancer.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import reactor.core.publisher.Mono;

@Configuration
public class LoadBalancerMetricsConfig {
    
    @Bean
    ReactorServiceInstanceLoadBalancer meteredLoadBalancer(
            ServiceInstanceListSupplier supplier,
            MeterRegistry meterRegistry) {
        return new MeteredLoadBalancer(supplier, meterRegistry);
    }
}

class MeteredLoadBalancer implements ReactorServiceInstanceLoadBalancer {
    
    private final ServiceInstanceListSupplier supplier;
    private final MeterRegistry meterRegistry;
    private final Counter chooseCounter;
    private final Timer chooseTimer;
    
    public MeteredLoadBalancer(ServiceInstanceListSupplier supplier, 
                               MeterRegistry meterRegistry) {
        this.supplier = supplier;
        this.meterRegistry = meterRegistry;
        this.chooseCounter = Counter.builder("loadbalancer.choose.total")
            .tag("service", supplier.getServiceId())
            .register(meterRegistry);
        this.chooseTimer = Timer.builder("loadbalancer.choose.duration")
            .tag("service", supplier.getServiceId())
            .register(meterRegistry);
    }
    
    @Override
    public Mono<Response<ServiceInstance>> choose(Request request) {
        return chooseTimer.record(() -> {
            chooseCounter.increment();
            
            return supplier.get()
                .next()
                .map(instances -> {
                    if (instances.isEmpty()) {
                        return new EmptyResponse();
                    }
                    
                    // 简单轮询
                    int index = (int) (chooseCounter.count() % instances.size());
                    ServiceInstance instance = instances.get(index);
                    
                    // 记录选择的实例
                    Tags tags = Tags.of(
                        "service", supplier.getServiceId(),
                        "instance", instance.getHost() + ":" + instance.getPort()
                    );
                    meterRegistry.counter("loadbalancer.instance.selected", tags)
                        .increment();
                    
                    return new DefaultResponse(instance);
                });
        });
    }
}

四、实战应用场景

4.1 灰度发布

import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.loadbalancer.core.*;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Mono;
import java.util.*;
import java.util.stream.Collectors;

@RestController
public class GrayReleaseController {
    
    @Autowired
    private LoadBalancerClientFactory loadBalancerClientFactory;
    
    @GetMapping("/gray/{version}")
    public Mono<String> grayRelease(
            @PathVariable String version,
            @RequestHeader(value = "X-User-Id", required = false) String userId) {
        
        // 获取灰度负载均衡器
        ReactorLoadBalancer<ServiceInstance> loadBalancer = 
            loadBalancerClientFactory.getInstance("user-service", 
                GrayLoadBalancer.class);
        
        // 根据用户ID和版本选择实例
        GrayRequest request = new GrayRequest(version, userId);
        
        return Mono.from(loadBalancer.choose(request))
            .map(response -> {
                ServiceInstance instance = response.getServer();
                return "Routed to: " + instance.getHost() + ":" + instance.getPort();
            });
    }
}

class GrayLoadBalancer implements ReactorServiceInstanceLoadBalancer {
    
    private final ServiceInstanceListSupplier supplier;
    
    public GrayLoadBalancer(ServiceInstanceListSupplier supplier) {
        this.supplier = supplier;
    }
    
    @Override
    public Mono<Response<ServiceInstance>> choose(Request request) {
        return supplier.get()
            .next()
            .map(instances -> {
                if (instances.isEmpty()) {
                    return new EmptyResponse();
                }
                
                GrayRequest grayRequest = (GrayRequest) request;
                
                // 根据版本过滤实例
                List<ServiceInstance> filtered = instances.stream()
                    .filter(instance -> {
                        Map<String, String> metadata = instance.getMetadata();
                        if (metadata != null) {
                            String instanceVersion = metadata.get("version");
                            return grayRequest.version.equals(instanceVersion);
                        }
                        return false;
                    })
                    .collect(Collectors.toList());
                
                // 如果没有匹配的灰度实例,使用默认实例
                if (filtered.isEmpty()) {
                    filtered = instances.stream()
                        .filter(instance -> {
                            Map<String, String> metadata = instance.getMetadata();
                            return metadata == null || 
                                   !"gray".equals(metadata.get("type"));
                        })
                        .collect(Collectors.toList());
                }
                
                if (filtered.isEmpty()) {
                    return new DefaultResponse(instances.get(0));
                }
                
                // 随机选择
                int index = new Random().nextInt(filtered.size());
                return new DefaultResponse(filtered.get(index));
            });
    }
}

class GrayRequest extends DefaultRequest<String> {
    private final String version;
    private final String userId;
    
    public GrayRequest(String version, String userId) {
        super(userId);
        this.version = version;
        this.userId = userId;
    }
}

4.2 区域感知负载均衡

import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.loadbalancer.core.*;
import reactor.core.publisher.Mono;
import java.util.*;
import java.util.stream.Collectors;

@Configuration
public class ZoneAwareLoadBalancerConfig {
    
    @Bean
    ReactorServiceInstanceLoadBalancer zoneAwareLoadBalancer(
            ServiceInstanceListSupplier supplier) {
        return new ZoneAwareLoadBalancer(supplier);
    }
}

class ZoneAwareLoadBalancer implements ReactorServiceInstanceLoadBalancer {
    
    private final ServiceInstanceListSupplier supplier;
    private final String currentZone;
    
    public ZoneAwareLoadBalancer(ServiceInstanceListSupplier supplier) {
        this.supplier = supplier;
        this.currentZone = System.getProperty("zone", "default");
    }
    
    @Override
    public Mono<Response<ServiceInstance>> choose(Request request) {
        return supplier.get()
            .next()
            .map(instances -> {
                if (instances.isEmpty()) {
                    return new EmptyResponse();
                }
                
                // 优先选择同区域的实例
                List<ServiceInstance> sameZone = instances.stream()
                    .filter(instance -> {
                        Map<String, String> metadata = instance.getMetadata();
                        if (metadata != null) {
                            return currentZone.equals(metadata.get("zone"));
                        }
                        return false;
                    })
                    .collect(Collectors.toList());
                
                if (!sameZone.isEmpty()) {
                    // 同区域实例随机选择
                    int index = new Random().nextInt(sameZone.size());
                    return new DefaultResponse(sameZone.get(index));
                }
                
                // 没有同区域实例,使用其他区域
                int index = new Random().nextInt(instances.size());
                return new DefaultResponse(instances.get(index));
            });
    }
}

五、总结与最佳实践

负载均衡策略选择

场景推荐策略
实例性能相近轮询
实例性能差异大加权轮询
有状态服务一致性哈希
灰度发布标签路由
多区域部署区域感知

最佳实践

  1. 健康检查:配置合理的健康检查机制
  2. 重试机制:设置合理的重试次数
  3. 超时配置:避免长时间等待
  4. 指标监控:监控负载均衡效果

负载均衡是微服务架构的核心技术,选择合适的负载均衡策略,配置合理的参数,能够显著提高系统的性能和可用性。