负载均衡是分布式系统的核心组件,它将请求合理地分发到多个后端服务器,实现水平扩展、故障转移和高可用。
一、负载均衡层次
1.1 L4 vs L7
┌────────────────────────────────────────────┐
│ L7 负载均衡(应用层) │
│ - 基于 URL、Host、Header、Cookie │
│ - SSL 终止、HTTP 路由、内容缓存 │
│ - 代表:Nginx、HAProxy、Envoy、Traefik │
├────────────────────────────────────────────┤
│ L4 负载均衡(传输层) │
│ - 基于 IP + Port │
│ - TCP/UDP 流量分发,不解包应用层 │
│ - 代表:LVS、HAProxy、AWS NLB │
├────────────────────────────────────────────┤
│ DNS 负载均衡 │
│ - 基于地理位置、轮询返回不同 IP │
│ - 代表:Cloudflare、Route53 │
└────────────────────────────────────────────┘
1.2 对比
| 特性 | L4 | L7 |
|---|---|---|
| 性能 | 更高(不解包) | 较低(解析 HTTP) |
| 灵活性 | 低 | 高(路由规则丰富) |
| 功能 | 仅转发 | SSL、Rewrite、限流、缓存 |
| 延迟 | 微秒级 | 毫秒级 |
| 适用场景 | 数据库、TCP 服务 | Web 应用、API 网关 |
二、负载均衡算法
2.1 轮询(Round Robin)
public class RoundRobinLoadBalancer {
private final List<String> servers;
private final AtomicInteger counter = new AtomicInteger(0);
public RoundRobinLoadBalancer(List<String> servers) {
this.servers = servers;
}
public String next() {
int index = counter.getAndIncrement() % servers.size();
return servers.get(index);
}
}
// 加权轮询
public class WeightedRoundRobin {
private final List<Server> servers;
private final AtomicInteger currentIndex = new AtomicInteger(-1);
private final AtomicInteger currentWeight = new AtomicInteger(0);
private final int maxWeight;
private final int gcdWeight;
public String next() {
while (true) {
int index = currentIndex.incrementAndGet() % servers.size();
if (index == 0) {
int weight = currentWeight.decrementAndGet();
if (weight <= 0) {
currentWeight.set(maxWeight);
}
}
Server server = servers.get(index);
if (server.weight >= currentWeight.get()) {
return server.address;
}
}
}
}
2.2 最少连接(Least Connections)
public class LeastConnectionsBalancer {
private final Map<String, AtomicInteger> connections = new ConcurrentHashMap<>();
public String next() {
return connections.entrySet().stream()
.min(Map.Entry.comparingByValue())
.map(Map.Entry::getKey)
.orElseThrow();
}
public void acquire(String server) {
connections.computeIfAbsent(server, k -> new AtomicInteger()).incrementAndGet();
}
public void release(String server) {
connections.get(server).decrementAndGet();
}
}
2.3 一致性哈希(Consistent Hashing)
public class ConsistentHashBalancer {
private final TreeMap<Long, String> ring = new TreeMap<>();
private final int virtualNodes; // 虚拟节点数
public ConsistentHashBalancer(List<String> servers, int virtualNodes) {
this.virtualNodes = virtualNodes;
for (String server : servers) {
addServer(server);
}
}
public void addServer(String server) {
for (int i = 0; i < virtualNodes; i++) {
long hash = hash(server + "#" + i);
ring.put(hash, server);
}
}
public void removeServer(String server) {
for (int i = 0; i < virtualNodes; i++) {
long hash = hash(server + "#" + i);
ring.remove(hash);
}
}
public String getNode(String key) {
if (ring.isEmpty()) return null;
long hash = hash(key);
Map.Entry<Long, String> entry = ring.ceilingEntry(hash);
if (entry == null) {
entry = ring.firstEntry();
}
return entry.getValue();
}
private long hash(String key) {
// MurmurHash 或 MD5
return Hashing.murmur3_128().hashString(key, StandardCharsets.UTF_8).asLong();
}
}
// 使用场景:
// - 分布式缓存(Redis Cluster)
// - 分布式存储(MinIO)
// - 有状态服务路由
2.4 算法选型
| 算法 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 轮询 | 简单、均匀 | 不考虑负载差异 | 服务器性能相近 |
| 加权轮询 | 考虑性能差异 | 需手动配置权重 | 异构服务器集群 |
| 最少连接 | 动态适应 | 需维护连接计数 | 长连接服务 |
| 一致性哈希 | 节点增减影响小 | 可能不均匀 | 缓存、有状态服务 |
| IP 哈希 | 会话保持 | 热点风险 | 需要会话粘性 |
| 随机 | 实现简单 | 不够均匀 | 简单场景 |
三、Nginx 负载均衡配置
# HTTP 负载均衡(L7)
upstream backend {
# 加权轮询(默认)
server 192.168.1.10:8080 weight=5;
server 192.168.1.11:8080 weight=3;
server 192.168.1.12:8080 backup; # 备用
# 最少连接
least_conn;
# IP 哈希(会话保持)
# ip_hash;
# 一致性哈希(基于变量)
# hash $request_uri consistent;
# 健康检查
server 192.168.1.10:8080 max_fails=3 fail_timeout=30s;
keepalive 32; # 连接池
}
server {
listen 80;
location /api/ {
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# 超时配置
proxy_connect_timeout 5s;
proxy_read_timeout 30s;
proxy_send_timeout 30s;
}
# 静态资源,基于 URL 路由
location /static/ {
proxy_pass http://static-backend;
}
}
# TCP/UDP 负载均衡(L4,需 stream 模块)
stream {
upstream redis_backend {
server 192.168.1.10:6379;
server 192.168.1.11:6379;
# 健康检查
check interval=3000 rise=2 fall=3 timeout=1000;
}
server {
listen 6379;
proxy_pass redis_backend;
proxy_connect_timeout 3s;
proxy_timeout 30s;
}
}
四、健康检查
public class HealthChecker {
private final List<Server> servers;
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
public HealthChecker(List<Server> servers) {
this.servers = servers;
// 每 5 秒检查一次
scheduler.scheduleAtFixedRate(this::checkAll, 5, 5, TimeUnit.SECONDS);
}
private void checkAll() {
for (Server server : servers) {
boolean healthy = checkServer(server);
if (healthy != server.isHealthy()) {
server.setHealthy(healthy);
System.out.println("Server " + server.getAddress() + " is " + (healthy ? "UP" : "DOWN"));
}
}
}
private boolean checkServer(Server server) {
try (Socket socket = new Socket()) {
socket.connect(new InetSocketAddress(server.getHost(), server.getPort()), 3000);
return true;
} catch (IOException e) {
return false;
}
}
}
五、总结
| 组件 | 层级 | 算法 | 场景 |
|---|---|---|---|
| Nginx | L7/L4 | 轮询、加权、IP 哈希、最少连接 | Web 入口、API 网关 |
| LVS | L4 | 多种 | 极高性能场景 |
| Envoy | L7 | 丰富的负载均衡策略 | 云原生、Service Mesh |
| DNS | L3 | 地理、轮询 | 多活架构、全球调度 |
负载均衡不仅是请求分发,更是系统高可用的核心保障。合理的算法选型、完善的监控告警、自动化的故障转移,才能构建真正可靠的服务架构。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。