负载均衡是分布式系统的核心组件,它将请求合理地分发到多个后端服务器,实现水平扩展、故障转移和高可用。
一、负载均衡层次
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;
}
}
}
负载均衡高可用架构设计
生产环境中,负载均衡器本身不能成为单点故障。高可用的负载均衡架构通常采用主备模式或集群模式,确保即使 LB 节点宕机,流量也能无缝切换到备用节点。
LVS + Keepalived 主备架构
┌─────────────┐
│ Client │
└──────┬──────┘
│
┌──────┴──────┐
│ VIP │ (Virtual IP: 10.0.0.100)
└──────┬──────┘
│
┌─────────┴─────────┐
│ │
┌─────┴─────┐ ┌─────┴─────┐
│ LVS-Master│ │ LVS-Backup│
│ (Active) │◄────►│ (Standby) │
│ Keepalived│ VRRP │ Keepalived│
└─────┬─────┘ └─────┬─────┘
│ │
└─────────┬─────────┘
│
┌─────────┼─────────┐
│ │ │
┌─────┴─┐ ┌────┴─┐ ┌────┴─┐
│RealServer1│ │RealServer2│ │RealServer3│
└─────────┘ └─────────┘ └─────────┘
# Keepalived 配置(Master)
# /etc/keepalived/keepalived.conf
global_defs {
router_id LVS_MASTER
}
vrrp_instance VI_1 {
state MASTER
interface eth0
virtual_router_id 51
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
10.0.0.100/24
}
}
virtual_server 10.0.0.100 80 {
delay_loop 6
lb_algo wrr
lb_kind DR # Direct Routing 模式
protocol TCP
real_server 10.0.0.11 80 {
weight 1
TCP_CHECK {
connect_timeout 3
nb_get_retry 3
delay_before_retry 3
}
}
real_server 10.0.0.12 80 {
weight 1
TCP_CHECK {
connect_timeout 3
}
}
}
LVS 三种工作模式:
| 模式 | 数据流向 | 性能 | 后端要求 | 适用 |
|---|---|---|---|---|
| NAT | LB 修改目的 IP,后端通过 LB 返回 | 中 | 无特殊要求 | 小规模 |
| DR | LB 修改 MAC 地址,后端直接返回客户端 | 高 | 后端需配置 VIP(lo 别名) | 大规模(推荐) |
| TUN | 封装 IP 隧道,后端解封后返回 | 高 | 后端支持 IPIP 隧道 | 跨机房 |
Anycast + BGP 全球负载均衡
对于全球化部署的应用,Anycast 是一种更高级的负载均衡技术。同一个 IP 地址同时广播到多个地理位置的接入点(PoP),用户请求自动路由到最近节点。
全球 Anycast 架构:
┌─────────────┐
│ 用户 (北京) │
└──────┬──────┘
│
┌────────┴────────┐
│ BGP 路由选择 │
│ 最近/最优路径 │
└────────┬────────┘
│
┌──────────┐ ┌──────┴──────┐ ┌──────────┐
│ 北京 PoP │◄───────►│ Anycast IP │◄───────►│ 上海 PoP │
│ 10ms │ │ 1.2.3.4 │ │ 30ms │
└────┬─────┘ └─────────────┘ └────┬─────┘
│ │
┌────┴─────┐ ┌────┴─────┐
│ 源站集群 │◄─────────────────────────────►│ 源站集群 │
│ 华北区 │ 跨区域数据同步 │ 华东区 │
└──────────┘ └──────────┘
Anycast 的优势:
- 自动容灾:某个 PoP 故障时,BGP 自动收敛,流量切换到其他 PoP(通常 < 60 秒)
- 就近接入:无需 DNS 地理解析,路由协议自动选择最优路径
- DDoS 分散:攻击流量被分散到多个 PoP,单一节点承载压力大幅降低
Anycast 的挑战:
- 状态共享困难:同一用户的请求可能被路由到不同 PoP,需使用全局 Session 存储(如 Redis Cluster)
- TCP 连接中断:BGP 收敛期间活跃连接可能中断(长连接应用需设计重连机制)
- 部署复杂:需要与 ISP 协商 BGP peering,或使用 Cloudflare、AWS Global Accelerator 等托管方案
Envoy 高级功能与 Service Mesh 集成
Envoy 作为云原生时代的高性能代理,已成为 Kubernetes 和服务网格(Istio)的事实标准数据面。
Envoy 关键特性
# envoy.yaml 示例
static_resources:
listeners:
- name: http_listener
address:
socket_address:
address: 0.0.0.0
port_value: 8080
filter_chains:
- filters:
- name: envoy.filters.network.http_connection_manager
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
stat_prefix: ingress_http
codec_type: AUTO
route_config:
name: local_route
virtual_hosts:
- name: backend
domains: ["*"]
routes:
- match:
prefix: "/api/v1/"
route:
cluster: api_service
timeout: 30s
retry_policy:
retry_on: "5xx,connect-failure"
num_retries: 3
per_try_timeout: 10s
- match:
prefix: "/"
route:
cluster: frontend_service
# 断路器配置
circuit_breakers:
thresholds:
- priority: DEFAULT
max_connections: 1000
max_pending_requests: 1000
max_requests: 1000
max_retries: 3
http_filters:
- name: envoy.filters.http.cors
- name: envoy.filters.http.local_ratelimit
- name: envoy.filters.http.router
clusters:
- name: api_service
connect_timeout: 5s
type: STRICT_DNS
lb_policy: LEAST_REQUEST
load_assignment:
cluster_name: api_service
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: api.default.svc.cluster.local
port_value: 80
# 主动健康检查
health_checks:
- timeout: 5s
interval: 10s
unhealthy_threshold: 3
healthy_threshold: 2
http_health_check:
path: "/health"
Envoy 相比 Nginx 的优势:
| 特性 | Envoy | Nginx |
|---|---|---|
| 动态配置 | xDS API(无需重启) | 需 reload |
| 可观察性 | 内置 Prometheus 统计 | 需模块扩展 |
| 热重启 | 零停机 | graceful reload |
| 断路器 | 内置 | 需 Lua 或第三方模块 |
| 重试/超时 | 细粒度配置 | 相对简单 |
| 服务网格 | Istio 标准数据面 | 非原生支持 |
五、总结
| 组件 | 层级 | 算法 | 场景 |
|---|---|---|---|
| Nginx | L7/L4 | 轮询、加权、IP 哈希、最少连接 | Web 入口、API 网关 |
| LVS | L4 | 多种 | 极高性能场景 |
| Envoy | L7 | 丰富的负载均衡策略 | 云原生、Service Mesh |
| DNS | L3 | 地理、轮询 | 多活架构、全球调度 |
负载均衡不仅是请求分发,更是系统高可用的核心保障。合理的算法选型、完善的监控告警、自动化的故障转移,才能构建真正可靠的服务架构。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。