负载均衡
问题
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
| 对比 | Ribbon | Spring 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):客户端从注册中心拉取实例列表,自行选择。适合微服务内部调用,省去中间节点
相关链接
- Spring Cloud LoadBalancer 文档
- 注册中心 - 服务实例列表来源
- 远程调用 - Feign 内置负载均衡