高性能-负载均衡策略详解
# 一、引言:为什么需要负载均衡
在现代分布式系统中,单台服务器的处理能力总是有限的。
当业务量增长、用户并发增加时,单台服务器很快就会遇到瓶颈:
- 性能瓶颈:单台服务器CPU、内存、网络带宽打满
- 可用性风险:单点故障会导致整个服务不可用
- 扩展性受限:无法通过增加机器来提升系统容量
负载均衡是解决这些问题的核心技术:
用户请求
↓
[负载均衡器]
↓
服务器1 服务器2 服务器3 ... 服务器N
负载均衡的核心价值:
- 提升性能:将流量分散到多台服务器,充分利用集群资源
- 提高可用性:单台服务器故障不影响整体服务
- 水平扩展:通过增加服务器数量线性提升系统容量
- 灵活调度:可以根据服务器状态动态调整流量分配
本文将深入探讨负载均衡的核心原理、常见算法、实现方式以及最佳实践。
# 二、负载均衡的基本原理
# 2.1 负载均衡的分层架构
负载均衡可以在不同的网络层次实现:
| 层次 | 负载均衡类型 | 典型技术 | 特点 |
|---|---|---|---|
| 二层 | 数据链路层 | MAC地址转发 | 性能极高,但灵活性差 |
| 三层 | 网络层 | IP路由 | 基于IP地址,简单高效 |
| 四层 | 传输层 | TCP/UDP端口 | LVS、HAProxy(TCP模式) |
| 七层 | 应用层 | HTTP/HTTPS | Nginx、HAProxy(HTTP模式) |
四层 vs 七层负载均衡:
四层负载均衡:
- 基于IP和端口转发,不解析应用层协议
- 性能高,延迟低
- 无法根据请求内容做路由决策
- 适合高性能、流量大的场景
七层负载均衡:
- 解析HTTP协议,可以根据URL、Header等做路由
- 功能强大,支持内容路由、会话保持等
- 性能相对较低,有一定延迟
- 适合需要灵活路由策略的场景
# 2.2 负载均衡的核心组件
客户端
↓
[负载均衡器]
├─ 健康检查模块:监测后端服务器状态
├─ 调度算法模块:选择合适的服务器
├─ 会话保持模块:保证同一用户请求到同一服务器
└─ 连接管理模块:管理与后端服务器的连接
↓
后端服务器集群
健康检查:
- 定期检测后端服务器是否可用
- 发现故障后自动摘除节点
- 恢复后自动加回节点
调度算法:
- 根据策略选择后端服务器
- 是负载均衡的核心逻辑
会话保持:
- 保证同一用户的请求发送到同一服务器
- 解决有状态应用的一致性问题
# 三、负载均衡核心算法
# 3.1 轮询(Round Robin)
原理:按顺序依次将请求分配给每台服务器。
public class RoundRobinLoadBalancer {
private List<Server> servers;
private AtomicInteger currentIndex = new AtomicInteger(0);
public Server select() {
if (servers.isEmpty()) {
return null;
}
int index = currentIndex.getAndIncrement() % servers.size();
return servers.get(index);
}
}
优点:
- 实现简单
- 请求分配均匀
- 适合服务器性能相近的场景
缺点:
- 不考虑服务器当前负载
- 不考虑服务器性能差异
- 可能导致性能差的服务器过载
适用场景:
- 后端服务器性能相近
- 请求处理时间相似
- 无状态服务
# 3.2 加权轮询(Weighted Round Robin)
原理:根据服务器性能设置权重,性能高的服务器获得更多请求。
public class WeightedRoundRobinLoadBalancer {
private List<WeightedServer> servers;
private AtomicInteger currentWeight = new AtomicInteger(0);
public Server select() {
int totalWeight = servers.stream()
.mapToInt(WeightedServer::getWeight)
.sum();
int targetWeight = currentWeight.getAndIncrement() % totalWeight;
int currentSum = 0;
for (WeightedServer server : servers) {
currentSum += server.getWeight();
if (targetWeight < currentSum) {
return server.getServer();
}
}
return servers.get(0).getServer();
}
}
class WeightedServer {
private Server server;
private int weight; // 权重值,如1、2、3
// getters...
}
优点:
- 可以根据服务器性能分配流量
- 充分利用高性能服务器
- 平滑流量分配
缺点:
- 权重设置需要人工配置
- 不能动态适应服务器负载变化
适用场景:
- 服务器性能差异较大
- 可以预估服务器处理能力
- 需要优先使用高性能服务器
# 3.3 随机(Random)
原理:随机选择一台服务器处理请求。
public class RandomLoadBalancer {
private List<Server> servers;
private Random random = new Random();
public Server select() {
if (servers.isEmpty()) {
return null;
}
int index = random.nextInt(servers.size());
return servers.get(index);
}
}
优点:
- 实现简单
- 无需维护状态
- 长期看分配均匀
缺点:
- 短期可能分配不均
- 不考虑服务器负载
适用场景:
- 服务器性能相近
- 对短期分配均匀性要求不高
- 无状态服务
# 3.4 加权随机(Weighted Random)
原理:根据权重随机选择服务器。
public class WeightedRandomLoadBalancer {
private List<WeightedServer> servers;
private Random random = new Random();
public Server select() {
int totalWeight = servers.stream()
.mapToInt(WeightedServer::getWeight)
.sum();
int randomWeight = random.nextInt(totalWeight);
int currentSum = 0;
for (WeightedServer server : servers) {
currentSum += server.getWeight();
if (randomWeight < currentSum) {
return server.getServer();
}
}
return servers.get(0).getServer();
}
}
# 3.5 最少连接(Least Connections)
原理:将请求分配给当前活跃连接数最少的服务器。
public class LeastConnectionsLoadBalancer {
private List<ServerWithConnections> servers;
public Server select() {
return servers.stream()
.min(Comparator.comparingInt(ServerWithConnections::getActiveConnections))
.map(ServerWithConnections::getServer)
.orElse(null);
}
public void onRequestStart(Server server) {
servers.stream()
.filter(s -> s.getServer().equals(server))
.forEach(ServerWithConnections::incrementConnections);
}
public void onRequestEnd(Server server) {
servers.stream()
.filter(s -> s.getServer().equals(server))
.forEach(ServerWithConnections::decrementConnections);
}
}
class ServerWithConnections {
private Server server;
private AtomicInteger activeConnections = new AtomicInteger(0);
public void incrementConnections() {
activeConnections.incrementAndGet();
}
public void decrementConnections() {
activeConnections.decrementAndGet();
}
public int getActiveConnections() {
return activeConnections.get();
}
// getters...
}
优点:
- 考虑服务器当前负载
- 动态适应请求处理速度
- 避免某台服务器过载
缺点:
- 需要维护连接数状态
- 实现相对复杂
- 不适合短连接场景
适用场景:
- 长连接服务(如WebSocket、数据库连接)
- 请求处理时间差异大
- 服务器性能有差异
# 3.6 加权最少连接(Weighted Least Connections)
原理:结合权重和连接数,计算 连接数/权重 最小的服务器。
public class WeightedLeastConnectionsLoadBalancer {
private List<WeightedServerWithConnections> servers;
public Server select() {
return servers.stream()
.min(Comparator.comparingDouble(s ->
(double) s.getActiveConnections() / s.getWeight()))
.map(WeightedServerWithConnections::getServer)
.orElse(null);
}
}
class WeightedServerWithConnections {
private Server server;
private int weight;
private AtomicInteger activeConnections = new AtomicInteger(0);
// methods...
}
# 3.7 IP哈希(IP Hash)
原理:根据客户端IP计算哈希值,相同IP总是路由到同一服务器。
public class IpHashLoadBalancer {
private List<Server> servers;
public Server select(String clientIp) {
if (servers.isEmpty()) {
return null;
}
int hash = clientIp.hashCode();
int index = Math.abs(hash) % servers.size();
return servers.get(index);
}
}
优点:
- 天然实现会话保持
- 实现简单
- 相同客户端总是访问同一服务器
缺点:
- 服务器数量变化时会导致大量请求重新路由
- 可能导致负载不均(某些IP访问量大)
- 不考虑服务器当前负载
适用场景:
- 需要会话保持的有状态应用
- 服务器数量相对稳定
- 客户端IP分布均匀
# 3.8 一致性哈希(Consistent Hash)
原理:使用哈希环,将服务器和请求都映射到环上,请求路由到顺时针最近的服务器。
public class ConsistentHashLoadBalancer {
private TreeMap<Integer, Server> hashRing = new TreeMap<>();
private int virtualNodes = 150; // 每个真实节点对应的虚拟节点数
public void addServer(Server server) {
for (int i = 0; i < virtualNodes; i++) {
String virtualNodeName = server.getAddress() + "#" + i;
int hash = hash(virtualNodeName);
hashRing.put(hash, server);
}
}
public void removeServer(Server server) {
for (int i = 0; i < virtualNodes; i++) {
String virtualNodeName = server.getAddress() + "#" + i;
int hash = hash(virtualNodeName);
hashRing.remove(hash);
}
}
public Server select(String key) {
if (hashRing.isEmpty()) {
return null;
}
int hash = hash(key);
// 找到顺时针方向第一个节点
Map.Entry<Integer, Server> entry = hashRing.ceilingEntry(hash);
if (entry == null) {
// 如果没找到,返回环上第一个节点
entry = hashRing.firstEntry();
}
return entry.getValue();
}
private int hash(String key) {
// 使用MurmurHash或其他哈希算法
return key.hashCode();
}
}
优点:
- 服务器增减时影响范围小(只影响相邻节点)
- 实现会话保持
- 负载分布相对均匀(通过虚拟节点)
缺点:
- 实现相对复杂
- 需要维护哈希环
- 不考虑服务器实时负载
适用场景:
- 分布式缓存(如Redis集群、Memcached集群)
- 服务器经常扩缩容
- 需要会话保持
# 3.9 最短响应时间(Least Response Time)
原理:选择平均响应时间最短的服务器。
public class LeastResponseTimeLoadBalancer {
private List<ServerWithMetrics> servers;
public Server select() {
return servers.stream()
.min(Comparator.comparingLong(ServerWithMetrics::getAvgResponseTime))
.map(ServerWithMetrics::getServer)
.orElse(null);
}
public void recordResponseTime(Server server, long responseTime) {
servers.stream()
.filter(s -> s.getServer().equals(server))
.forEach(s -> s.updateResponseTime(responseTime));
}
}
class ServerWithMetrics {
private Server server;
private AtomicLong totalResponseTime = new AtomicLong(0);
private AtomicLong requestCount = new AtomicLong(0);
public void updateResponseTime(long responseTime) {
totalResponseTime.addAndGet(responseTime);
requestCount.incrementAndGet();
}
public long getAvgResponseTime() {
long count = requestCount.get();
return count == 0 ? 0 : totalResponseTime.get() / count;
}
// getters...
}
优点:
- 考虑服务器实际性能
- 动态适应服务器负载变化
- 优先使用响应快的服务器
缺点:
- 需要收集和维护响应时间数据
- 实现复杂
- 需要定期重置统计数据
适用场景:
- 服务器性能差异大
- 请求处理时间波动大
- 对响应时间敏感的业务
# 四、负载均衡算法选择指南
# 4.1 算法对比总结
| 算法 | 复杂度 | 会话保持 | 动态适应 | 适用场景 |
|---|---|---|---|---|
| 轮询 | 低 | ✗ | ✗ | 无状态、性能相近 |
| 加权轮询 | 低 | ✗ | ✗ | 性能差异大 |
| 随机 | 低 | ✗ | ✗ | 简单场景 |
| 加权随机 | 低 | ✗ | ✗ | 性能差异大 |
| 最少连接 | 中 | ✗ | ✓ | 长连接、请求耗时差异大 |
| 加权最少连接 | 中 | ✗ | ✓ | 性能差异大+长连接 |
| IP哈希 | 低 | ✓ | ✗ | 会话保持、稳定集群 |
| 一致性哈希 | 高 | ✓ | ✗ | 分布式缓存、动态扩缩容 |
| 最短响应时间 | 高 | ✗ | ✓ | 性能敏感、实时优化 |
# 4.2 选择决策树
需要会话保持?
├─ 是 → 服务器经常变化?
│ ├─ 是 → 一致性哈希
│ └─ 否 → IP哈希
└─ 否 → 长连接服务?
├─ 是 → 服务器性能差异大?
│ ├─ 是 → 加权最少连接
│ └─ 否 → 最少连接
└─ 否 → 服务器性能差异大?
├─ 是 → 加权轮询
└─ 否 → 轮询
# 4.3 常见场景推荐
Web应用:
- 无状态:轮询或加权轮询
- 有状态:IP哈希或一致性哈希
API网关:
- 加权轮询 + 健康检查
- 支持动态权重调整
数据库连接池:
- 最少连接或加权最少连接
- 避免某个节点过载
分布式缓存:
- 一致性哈希
- 方便扩缩容
长连接服务(WebSocket):
- 最少连接
- 考虑连接生命周期
# 五、负载均衡的实现方式
# 5.1 软件负载均衡
# 5.1.1 Nginx
特点:
- 七层负载均衡
- 高性能,单机可处理数万并发
- 支持多种负载均衡算法
- 功能丰富,易于配置
配置示例:
http {
# 定义后端服务器组
upstream backend {
# 负载均衡算法
# 默认为轮询,可选: ip_hash、least_conn
least_conn;
# 服务器列表
server 192.168.1.10:8080 weight=3 max_fails=2 fail_timeout=30s;
server 192.168.1.11:8080 weight=2;
server 192.168.1.12:8080 weight=1;
# 备用服务器
server 192.168.1.13:8080 backup;
# 健康检查
keepalive 32;
}
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://backend;
# 传递客户端真实IP
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
# 超时设置
proxy_connect_timeout 5s;
proxy_send_timeout 10s;
proxy_read_timeout 10s;
}
}
}
会话保持(Sticky Session):
upstream backend {
ip_hash; # 基于IP的会话保持
server 192.168.1.10:8080;
server 192.168.1.11:8080;
}
或使用第三方模块实现更灵活的会话保持:
upstream backend {
# 基于Cookie的会话保持
sticky cookie srv_id expires=1h domain=.example.com path=/;
server 192.168.1.10:8080;
server 192.168.1.11:8080;
}
# 5.1.2 HAProxy
特点:
- 支持四层和七层负载均衡
- 性能极高,专为负载均衡设计
- 强大的健康检查机制
- 丰富的统计信息
配置示例:
global
maxconn 50000
log 127.0.0.1 local0
defaults
mode http
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms
# 前端配置
frontend http-in
bind *:80
default_backend servers
# 后端配置
backend servers
balance roundrobin
# 健康检查
option httpchk GET /health
http-check expect status 200
# 服务器列表
server server1 192.168.1.10:8080 check weight 3
server server2 192.168.1.11:8080 check weight 2
server server3 192.168.1.12:8080 check weight 1
server server4 192.168.1.13:8080 check backup
# 统计页面
listen stats
bind *:8080
stats enable
stats uri /stats
stats refresh 30s
# 5.1.3 LVS (Linux Virtual Server)
特点:
- 四层负载均衡
- 性能极高,可达百万并发
- 工作在内核态
- 配置相对复杂
工作模式:
NAT模式:
客户端 → LVS(NAT) → 真实服务器
↓
修改目标IP
DR模式(Direct Routing):
客户端 → LVS(修改MAC地址) → 真实服务器
↓
直接响应客户端
TUN模式(IP Tunneling):
客户端 → LVS(IP隧道) → 真实服务器
↓
直接响应客户端
# 5.2 硬件负载均衡
典型产品:
- F5 BIG-IP:业界标杆,功能全面,价格昂贵
- Citrix NetScaler:应用交付控制器,功能强大
- A10 Networks:性价比高,性能优秀
特点:
- 性能极高,可达数百万并发
- 稳定可靠,专用硬件
- 功能全面,支持复杂场景
- 价格昂贵,维护成本高
适用场景:
- 超大流量场景(每秒百万级请求)
- 对可用性要求极高的核心业务
- 有充足预算的企业
# 5.3 云负载均衡
主流云厂商产品:
| 云厂商 | 产品名称 | 特点 |
|---|---|---|
| AWS | ELB/ALB/NLB | 经典/应用/网络负载均衡 |
| 阿里云 | SLB | 支持四层和七层 |
| 腾讯云 | CLB | 高可用、高性能 |
| Azure | Load Balancer | 区域/全局负载均衡 |
优势:
- 无需购买硬件,按需付费
- 自动扩展,弹性伸缩
- 高可用,多地域部署
- 与云服务深度集成
配置示例(阿里云SLB):
// 通过SDK配置SLB
LoadBalancerClient client = new LoadBalancerClient();
CreateLoadBalancerRequest request = new CreateLoadBalancerRequest();
request.setLoadBalancerName("my-lb");
request.setAddressType("internet"); // 公网
request.setLoadBalancerSpec("slb.s2.small");
CreateLoadBalancerResponse response = client.createLoadBalancer(request);
String loadBalancerId = response.getLoadBalancerId();
// 添加后端服务器
AddBackendServersRequest addRequest = new AddBackendServersRequest();
addRequest.setLoadBalancerId(loadBalancerId);
addRequest.setBackendServers([
{"ServerId": "i-abc123", "Weight": 100},
{"ServerId": "i-def456", "Weight": 100}
]);
client.addBackendServers(addRequest);
# 六、健康检查与故障转移
# 6.1 健康检查机制
主动健康检查:
public class HealthChecker {
private ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
private List<Server> servers;
private Set<Server> healthyServers = new ConcurrentHashMap().newKeySet();
public void startHealthCheck() {
scheduler.scheduleAtFixedRate(() -> {
for (Server server : servers) {
if (isHealthy(server)) {
healthyServers.add(server);
} else {
healthyServers.remove(server);
}
}
}, 0, 5, TimeUnit.SECONDS); // 每5秒检查一次
}
private boolean isHealthy(Server server) {
try {
// HTTP健康检查
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("http://" + server.getAddress() + "/health"))
.timeout(Duration.ofSeconds(2))
.GET()
.build();
HttpResponse<String> response = client.send(request,
HttpResponse.BodyHandlers.ofString());
return response.statusCode() == 200;
} catch (Exception e) {
return false;
}
}
public List<Server> getHealthyServers() {
return new ArrayList<>(healthyServers);
}
}
被动健康检查:
public class PassiveHealthChecker {
private Map<Server, FailureCounter> failureCounters = new ConcurrentHashMap<>();
private int maxFailures = 3;
private long failureWindow = 30000; // 30秒
public void recordFailure(Server server) {
FailureCounter counter = failureCounters.computeIfAbsent(
server, k -> new FailureCounter());
counter.recordFailure();
if (counter.getFailureCount() >= maxFailures) {
markUnhealthy(server);
}
}
public void recordSuccess(Server server) {
FailureCounter counter = failureCounters.get(server);
if (counter != null) {
counter.reset();
}
}
private void markUnhealthy(Server server) {
// 将服务器标记为不健康
server.setHealthy(false);
// 启动恢复定时器
scheduleRecovery(server);
}
private void scheduleRecovery(Server server) {
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
scheduler.schedule(() -> {
// 尝试恢复
server.setHealthy(true);
failureCounters.remove(server);
}, failureWindow, TimeUnit.MILLISECONDS);
}
}
class FailureCounter {
private AtomicInteger count = new AtomicInteger(0);
private long lastFailureTime = 0;
public void recordFailure() {
count.incrementAndGet();
lastFailureTime = System.currentTimeMillis();
}
public void reset() {
count.set(0);
}
public int getFailureCount() {
return count.get();
}
}
# 6.2 故障转移策略
快速故障转移:
public class FailoverLoadBalancer {
private LoadBalancer primaryBalancer;
private LoadBalancer backupBalancer;
public Server select(String key) {
try {
Server server = primaryBalancer.select(key);
if (server != null && server.isHealthy()) {
return server;
}
} catch (Exception e) {
// 主负载均衡器失败,使用备份
}
// 使用备份负载均衡器
return backupBalancer.select(key);
}
}
重试机制:
public class RetryableLoadBalancer {
private LoadBalancer loadBalancer;
private int maxRetries = 3;
public Response execute(Request request) {
Exception lastException = null;
for (int i = 0; i < maxRetries; i++) {
Server server = loadBalancer.select(request.getKey());
try {
Response response = sendRequest(server, request);
return response;
} catch (Exception e) {
lastException = e;
// 标记服务器故障
markServerFailed(server);
// 继续重试其他服务器
}
}
throw new RuntimeException("All retries failed", lastException);
}
private void markServerFailed(Server server) {
// 通知健康检查器
healthChecker.recordFailure(server);
}
}
# 七、会话保持(Session Persistence)
# 7.1 会话保持的必要性
问题场景:
用户第一次请求 → 服务器A → 登录成功,Session存储在A
用户第二次请求 → 服务器B → Session不存在,需要重新登录
解决方案:
- 无状态化:推荐方案,使用JWT等Token机制
- Session复制:服务器间同步Session
- Session集中存储:使用Redis等存储Session
- 会话保持:确保同一用户请求同一服务器
# 7.2 基于Cookie的会话保持
public class CookieBasedSessionLoadBalancer {
private LoadBalancer loadBalancer;
private String sessionCookieName = "JSESSIONID";
public Server select(HttpRequest request) {
// 尝试从Cookie中获取服务器标识
String serverId = extractServerIdFromCookie(request);
if (serverId != null) {
Server server = findServerById(serverId);
if (server != null && server.isHealthy()) {
return server;
}
}
// 没有绑定服务器,使用负载均衡算法选择
Server server = loadBalancer.select(request.getKey());
// 设置Cookie绑定服务器
setServerIdCookie(request.getResponse(), server.getId());
return server;
}
private String extractServerIdFromCookie(HttpRequest request) {
Cookie[] cookies = request.getCookies();
for (Cookie cookie : cookies) {
if (sessionCookieName.equals(cookie.getName())) {
return cookie.getValue();
}
}
return null;
}
private void setServerIdCookie(HttpResponse response, String serverId) {
Cookie cookie = new Cookie(sessionCookieName, serverId);
cookie.setMaxAge(3600); // 1小时
cookie.setPath("/");
response.addCookie(cookie);
}
}
# 7.3 基于IP的会话保持
前面已介绍IP哈希算法,这里不再赘述。
# 7.4 会话复制
// 使用Spring Session + Redis实现Session共享
@Configuration
@EnableRedisHttpSession
public class SessionConfig {
@Bean
public LettuceConnectionFactory connectionFactory() {
return new LettuceConnectionFactory();
}
}
配置后,所有服务器共享Redis中的Session,无需会话保持。
# 八、负载均衡的最佳实践
# 8.1 监控与告警
关键指标:
public class LoadBalancerMetrics {
// 请求分布
private Map<Server, AtomicLong> requestCounts = new ConcurrentHashMap<>();
// 响应时间
private Map<Server, AtomicLong> responseTimes = new ConcurrentHashMap<>();
// 错误率
private Map<Server, AtomicLong> errorCounts = new ConcurrentHashMap<>();
// 健康状态
private Map<Server, Boolean> healthStatus = new ConcurrentHashMap<>();
public void recordRequest(Server server, long responseTime, boolean success) {
requestCounts.computeIfAbsent(server, k -> new AtomicLong())
.incrementAndGet();
responseTimes.computeIfAbsent(server, k -> new AtomicLong())
.addAndGet(responseTime);
if (!success) {
errorCounts.computeIfAbsent(server, k -> new AtomicLong())
.incrementAndGet();
}
}
public LoadBalancerStats getStats() {
LoadBalancerStats stats = new LoadBalancerStats();
for (Server server : requestCounts.keySet()) {
ServerStats serverStats = new ServerStats();
serverStats.setServer(server);
serverStats.setRequestCount(requestCounts.get(server).get());
serverStats.setErrorCount(errorCounts.getOrDefault(server,
new AtomicLong()).get());
long totalTime = responseTimes.get(server).get();
long count = requestCounts.get(server).get();
serverStats.setAvgResponseTime(count > 0 ? totalTime / count : 0);
stats.addServerStats(serverStats);
}
return stats;
}
}
# 8.2 动态权重调整
public class DynamicWeightAdjuster {
private ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
private LoadBalancerMetrics metrics;
private WeightedLoadBalancer loadBalancer;
public void startAdjustment() {
scheduler.scheduleAtFixedRate(() -> {
adjustWeights();
}, 0, 60, TimeUnit.SECONDS); // 每分钟调整一次
}
private void adjustWeights() {
LoadBalancerStats stats = metrics.getStats();
for (ServerStats serverStats : stats.getServerStats()) {
int newWeight = calculateWeight(serverStats);
loadBalancer.updateWeight(serverStats.getServer(), newWeight);
}
}
private int calculateWeight(ServerStats stats) {
// 基于响应时间和错误率计算权重
long avgResponseTime = stats.getAvgResponseTime();
double errorRate = stats.getErrorRate();
// 响应时间越短,权重越高
int timeWeight = (int) (1000 / Math.max(avgResponseTime, 1));
// 错误率越低,权重越高
int errorWeight = (int) ((1 - errorRate) * 100);
// 综合权重
return Math.max((timeWeight + errorWeight) / 2, 1);
}
}
# 8.3 优雅上下线
优雅下线:
public class GracefulShutdown {
private LoadBalancer loadBalancer;
private Server server;
public void shutdown() {
// 1. 从负载均衡器中移除服务器
loadBalancer.removeServer(server);
// 2. 等待已有请求处理完成
waitForRequestsToComplete();
// 3. 关闭服务器
server.stop();
}
private void waitForRequestsToComplete() {
long timeout = 30000; // 30秒超时
long start = System.currentTimeMillis();
while (server.getActiveConnections() > 0) {
if (System.currentTimeMillis() - start > timeout) {
break;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
}
}
优雅上线:
public class GracefulStartup {
private LoadBalancer loadBalancer;
private Server server;
private HealthChecker healthChecker;
public void startup() {
// 1. 启动服务器
server.start();
// 2. 预热服务器
warmup();
// 3. 健康检查通过后加入负载均衡器
if (healthChecker.isHealthy(server)) {
// 初始权重设低,逐步提升
loadBalancer.addServer(server, 1);
// 逐步提升权重
graduallyIncreaseWeight();
}
}
private void warmup() {
// 发送预热请求,让JIT编译、缓存等生效
for (int i = 0; i < 100; i++) {
sendWarmupRequest(server);
}
}
private void graduallyIncreaseWeight() {
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
scheduler.scheduleAtFixedRate(() -> {
int currentWeight = loadBalancer.getWeight(server);
int targetWeight = 10;
if (currentWeight < targetWeight) {
loadBalancer.updateWeight(server, currentWeight + 1);
} else {
scheduler.shutdown();
}
}, 0, 10, TimeUnit.SECONDS);
}
}
# 8.4 容量规划
评估公式:
单台服务器容量 = 单台QPS × 平均响应时间
所需服务器数量 = 峰值QPS / 单台服务器容量 × 冗余系数(1.5-2)
示例:
假设:
- 峰值QPS: 10000
- 单台服务器QPS: 1000
- 平均响应时间: 100ms
- 冗余系数: 1.5
所需服务器数量 = 10000 / 1000 × 1.5 = 15台
# 九、总结:构建高效负载均衡系统的关键要点
负载均衡是现代分布式系统的基础设施,是实现高可用、高性能的关键技术。
核心要点回顾:
选择合适的算法:根据业务特点选择负载均衡算法
- 无状态服务:轮询、加权轮询
- 长连接服务:最少连接
- 有状态服务:IP哈希、一致性哈希
- 分布式缓存:一致性哈希
健康检查必不可少:主动+被动健康检查,快速发现故障
会话保持要权衡:优先无状态化,必要时使用会话保持
监控告警很重要:实时监控负载分布、响应时间、错误率
优雅上下线:避免请求失败,保证服务质量
实践建议:
- 从简单开始:优先使用轮询等简单算法,根据需要优化
- 选型要务实:软件负载均衡满足大多数场景,硬件负载均衡仅用于极端场景
- 云原生优先:云环境下优先使用云厂商负载均衡服务
- 动态调整:根据监控数据动态调整权重和策略
- 定期演练:定期进行故障演练,验证故障转移机制
进阶方向:
- Service Mesh:使用Istio、Linkerd等实现更智能的流量管理
- 全局负载均衡:跨地域、跨云的全局流量调度
- 智能负载均衡:基于机器学习的流量预测和调度
当你和你的团队掌握了负载均衡的核心原理和最佳实践,你们将能够:
- 构建高可用、高性能的分布式系统
- 从容应对流量增长和服务器故障
- 实现系统的水平扩展和弹性伸缩
- 为用户提供稳定、快速的服务体验
祝你变得更强!