六大主流负载均衡算法
1. 简介
在现代分布式系统(如微服务、云计算、大型网站)中,一个应用通常会部署在多个服务器或容器实例上。如果没有合理的请求分发机制,可能会出现以下问题:
- 某些服务器“很忙”,而另一些却在“摸鱼”,造成资源浪费,还可能使得部分用户请求响应慢,系统整体性能下降。
- 增加了单点故障风险,使得系统整体的可用性降低。
负载均衡算法(Load Balancing Algorithm)的作用就是它决定用户请求该发给哪个后端实例。
更高级的表达:用于在多个服务器、服务实例或计算资源之间合理分配网络流量或请求任务的策略或规则。
2. 负载均衡算法的分类
负载均衡器(可以是硬件设备、也可以是软件如 Nginx、HAProxy,或云服务如阿里云SLB等)接收客户端请求后,根据预设的算法,从一组健康的后端服务实例中选择一个来处理该请求。根据选择的依据划分,这里对6种主流算法进行讲解。
负载均衡算法的使用(Java)
在Java后端开发中,通常不会从零实现负载均衡算法,而是通过集成成熟的框架、中间件或云服务,利用其内置的负载均衡能力。负载均衡的实现主要分为两类:客户端负载均衡(如Spring Cloud LoadBalancer)和服务端负载均衡(如Nginx、阿里云SLB)。
1. 轮询(Round Robin)
按顺序将客户端请求依次分发给不同的服务实例。该算法通常要求服务是无状态的。
在 Spring Cloud 微服务架构中,Spring Cloud LoadBalancer 默认使用轮询算法。无需额外编码,只要引入相关依赖并启用负载均衡即可:
@LoadBalanced
@Bean
public RestTemplate restTemplate() {return new RestTemplate();
}
在调用时直接使用服务名:
@Autowired
private RestTemplate restTemplate;public String callUserService() {return restTemplate.getForObject("http://user-service/api/users", String.class);
}
其中 user-service
是注册在Nacos或Eureka中的服务名,Spring Cloud LoadBalancer 会自动采用轮询策略选择实例。
适用场景:无状态微服务之间的调用,如订单服务调用用户服务。
2. 粘性会话(Sticky Session)
轮询算法的改进版本,粘性会话确保同一个客户端的请求始终被路由到同一个后端实例,适用于有状态的应用。
依赖前置负载均衡器(如Nginx、HAProxy或云SLB)来实现。例如,在 Nginx 中配置:
upstream backend {ip_hash; # 基于客户端IP保持会话server 192.168.1.10:8080;server 192.168.1.11:8080;
}
为了支持故障转移和会话共享,通常结合 Spring Session + Redis 实现会话持久化:
<!-- pom.xml -->
<dependency><groupId>org.springframework.session</groupId><artifactId>spring-session-data-redis</artifactId>
</dependency>
@EnableRedisHttpSession
@SpringBootApplication
public class Application { }
这样即使后端实例重启或切换,用户会话也不会丢失。
适用场景:传统Web应用、电商购物车、登录状态未完全无状态化的系统。
3. 加权轮询(Weighted Round Robin)
加权轮询允许为不同性能的服务器分配不同权重,高性能机器(设置高权重)处理更多请求。
在 Nginx 或 HAProxy 中可直接配置权重:
upstream backend {server 192.168.1.10:8080 weight=3;server 192.168.1.11:8080 weight=1;
}
在 Spring Cloud + Nacos 场景中,Nacos 支持为服务实例设置权重。在Nacos控制台或通过API设置实例权重后,Spring Cloud LoadBalancer 可结合自定义逻辑读取该权重并实现加权轮询。
例如,自定义一个加权负载均衡器:
public class WeightedRoundRobinLoadBalancer implements ReactorServiceInstanceLoadBalancer {private int currentIndex = 0;private List<ServiceInstance> instances = new ArrayList<>();@Overridepublic Mono<Response<ServiceInstance>> choose(Request request) {// 获取所有实例,并根据 metadata.weight 计算加权轮询List<ServiceInstance> instances = ... // 从注册中心获取int totalWeight = instances.stream().mapToInt(ins -> Integer.parseInt(ins.getMetadata().getOrDefault("weight", "1"))).sum();// 实现加权轮询逻辑(如平滑加权)// ...return Mono.just(new DefaultResponse(chosenInstance));}
}
适用场景:混合部署环境(新旧机器性能不同)、灰度发布时逐步放量。
4. 哈希(Hash-based)
哈希算法根据请求的某些特征(如IP、URL、用户ID)进行哈希计算,确保相同特征的请求路由到同一实例。
在 Nginx 中可配置基于IP或URL的哈希:
upstream backend {hash $request_uri; # 基于URL哈希server 192.168.1.10:8080;server 192.168.1.11:8080;
}
若需自定义哈希逻辑(如基于用户ID),可实现一个哈希负载均衡器:
public class UserIdHashLoadBalancer implements ReactorServiceInstanceLoadBalancer {@Overridepublic Mono<Response<ServiceInstance>> choose(Request request) {String userId = (String) request.getContext().get("userId");int hash = Math.abs(userId.hashCode());List<ServiceInstance> instances = // 获取实例列表int index = hash % instances.size();return Mono.just(new DefaultResponse(instances.get(index)));}
}
调用时传入上下文:
RequestContext context = new DefaultRequestContext("userId", "12345");
Response<ServiceInstance> instance = loadBalancer.choose(Request.create(context)).block();
适用场景:缓存一致性(如Redis分片)、CDN节点选择、避免重复计算。
5. 最少连接数(Least Connections)
将新请求分配给当前并发连接数最少的服务实例。
Nginx Plus、HAProxy 或 云厂商负载均衡器等最这个算法提供了支持,开源版Nginx不支持此算法。
在 HAProxy 中配置:
backend appbalance leastconnserver app1 192.168.1.10:8080server app2 192.168.1.11:8080
阿里云SLB的云控制台支持在监听配置中选择“最小连接数”调度算法。
适用场景:WebSocket长连接网关、视频转码、IM消息推送等长耗时服务。
6. 最短响应时间(Least Response Time)
该算法综合考虑响应时间和连接数,选择“最快”的实例。
HAProxy 支持这一算法,配置示例:
backend appbalance leasttimeserver app1 192.168.1.10:8080 checkserver app2 192.168.1.11:8080 check
可通过监控系统(如Prometheus + Grafana)收集各实例响应时间,结合自定义调度器实现近似效果,但复杂度较高,不建议自己手搓。
适用场景:金融交易系统、支付网关、对延迟极度敏感的高并发服务。
3. 总结
算法 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
轮询 | - 实现简单,易于理解 - 请求分布均匀,适合无状态服务 - 在服务实例性能相近时负载均衡效果良好 | - 不考虑服务实例的实际负载或响应能力 - 若服务有状态且未做会话共享,可能导致会话丢失 | - 阿里云 SLB、AWS ALB 在默认配置下支持轮询 - 微服务架构中无状态 API 网关的后端负载均衡 - Nginx 默认负载策略,适用于轻量级服务集群 |
粘性轮询 (又称会话保持) | - 保证同一客户端请求始终路由到同一后端实例 - 避免会话状态同步开销,适用于有状态服务 | - 可能导致负载不均(热点实例) - 实例宕机时会话丢失,需配合会话复制或持久化机制 | - 阿里云 SLB 支持基于 Cookie 或源 IP 的会话保持 - 传统 Web 应用(如 J2EE)依赖服务器端 Session 存储 - 电商购物车、用户登录状态等有状态业务场景 |
加权轮询 | - 可根据服务器性能分配权重,实现资源合理利用 - 适合异构服务器环境(如新旧机器混合) | - 权重需手动配置,难以动态调整 - 无法感知实时负载变化 | - 腾讯云 CLB 支持为后端节点配置权重 - 服务升级过渡期:新服务器性能更强,可设置更高权重 - 混合云环境中,高性能物理机分配更高权重 |
哈希 (IP/URL 哈希) | - 相同 IP 或 URL 的请求始终路由到同一实例 - 提高缓存命中率,减少重复计算 - 适合内容缓存、CDN 场景 | - 服务器增减时哈希分布变化大,可能导致缓存雪崩 - 负载可能不均(某些 IP 请求集中) | - CDN 节点负载均衡广泛使用 URL 哈希(如阿里云 CDN) - Nginx 使用 ip_hash 实现客户端 IP 哈希路由- 分布式缓存前置负载均衡,保证相同 key 路由到同一缓存节点 |
最少连接数 | - 动态感知后端负载,将请求分配给最空闲的实例 - 更适合长连接或处理时间差异大的场景 | - 需要维护连接状态,增加负载均衡器开销 - 初始连接可能不均衡 | - AWS ALB 和 Google Cloud Load Balancer 默认支持最少连接算法 - 即时通讯(IM)长连接网关负载均衡 - 视频转码等耗时任务调度 |
最短响应时间 | - 综合考虑连接数和响应延迟,选择“最快”实例 - 提升整体服务响应性能 | - 实现复杂,需持续监控响应时间 - 可能受网络抖动影响,导致路由不稳定 | - HAProxy 支持 leasttime 策略(响应时间 + 连接数)- 高性能交易系统(如金融、支付网关)对延迟敏感场景 - 多区域部署中选择延迟最低的可用区 |