跳到主要内容

负载均衡

问题

Spring Cloud 中的负载均衡是怎么实现的?有哪些负载均衡策略?

答案

客户端负载均衡 vs 服务端负载均衡

类型实现特点
服务端负载均衡Nginx、LVS、F5独立部署,所有请求先到负载均衡器
客户端负载均衡Ribbon、Spring Cloud LoadBalancer内嵌在客户端,从注册中心获取实例列表后自行选择

Spring Cloud LoadBalancer(推荐)

Spring Cloud 2020+ 推荐使用 spring-cloud-loadbalancer 替代 Ribbon:

使用 @LoadBalanced
@Configuration
public class RestConfig {

@Bean
@LoadBalanced // 开启负载均衡(拦截 http://service-name/... 的调用)
public RestTemplate restTemplate() {
return new RestTemplate();
}
}

@Service
public class OrderService {
@Autowired
private RestTemplate restTemplate;

public User getUser(Long userId) {
// user-service 会被替换为实际的 IP:Port
return restTemplate.getForObject(
"http://user-service/api/users/" + userId, User.class);
}
}

负载均衡策略

策略说明适用场景
轮询(Round Robin)按顺序依次选择(默认)实例性能相近
随机(Random)随机选择一个实例简单场景
加权轮询按权重分配流量实例性能不均
最少连接选择当前连接数最少的长连接场景
一致性哈希相同请求总路由到同一实例会话粘性、缓存命中
同区域优先优先选择同区域/机房实例多机房部署

自定义负载均衡策略

自定义负载均衡器
public class CustomLoadBalancer implements ReactorServiceInstanceLoadBalancer {

private final ObjectProvider<ServiceInstanceListSupplier> supplier;
private final AtomicInteger position = new AtomicInteger(0);

public CustomLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> supplier) {
this.supplier = supplier;
}

@Override
public Mono<Response<ServiceInstance>> choose(Request request) {
return supplier.getIfAvailable().get().next()
.map(instances -> {
if (instances.isEmpty()) {
return new EmptyResponse();
}
// 自定义选择逻辑(这里用轮询示例)
int index = Math.abs(position.incrementAndGet()) % instances.size();
ServiceInstance instance = instances.get(index);
return new DefaultResponse(instance);
});
}
}

// 注册自定义负载均衡器
@LoadBalancerClient(name = "user-service", configuration = CustomLBConfig.class)
public class CustomLBConfig {
@Bean
public ReactorLoadBalancer<ServiceInstance> customLoadBalancer(
ObjectProvider<ServiceInstanceListSupplier> supplier) {
return new CustomLoadBalancer(supplier);
}
}

Ribbon vs Spring Cloud LoadBalancer

对比RibbonSpring Cloud LoadBalancer
维护Netflix 停止维护Spring 官方维护
编程模型阻塞式响应式(支持 WebFlux)
策略数量7 种内置策略2 种内置(可扩展)
缓存30s 更新可配合 ServiceInstanceListSupplier 缓存
推荐旧项目新项目推荐

常见面试问题

Q1: Ribbon 的负载均衡原理?

答案

Ribbon 是客户端负载均衡器。通过 @LoadBalanced 注解在 RestTemplate 上注册一个拦截器(LoadBalancerInterceptor),拦截 http://service-name/... 格式的请求,从注册中心获取服务实例列表,根据负载均衡策略选择一个实例,将服务名替换为实际的 IP:Port 后发起 HTTP 调用。

Q2: 常用的负载均衡算法?

答案

  • 轮询:按顺序逐个选择,简单公平
  • 加权轮询:按权重分配,性能高的分更多流量
  • 随机:随机选择
  • 最少连接:选择当前连接数最少的实例
  • 一致性哈希:相同 key 的请求路由到同一实例

Q3: @LoadBalanced 注解的原理?

答案

@LoadBalanced 本质是一个 @Qualifier,Spring Cloud 自动配置会找到所有标注了 @LoadBalanced 的 RestTemplate,给它们添加一个拦截器 LoadBalancerInterceptor。该拦截器在请求发出前将服务名解析为实际地址。

Q4: 客户端负载均衡和服务端负载均衡的区别?

答案

  • 服务端(Nginx):独立部署的负载均衡器,客户端不感知实例列表。适合对外暴露的入口
  • 客户端(Ribbon/LoadBalancer):客户端从注册中心拉取实例列表,自行选择。适合微服务内部调用,省去中间节点

相关链接