负载均衡算法与高可用架构

深入理解 L4/L7 负载均衡原理,掌握轮询、加权、一致性哈希等算法实现与 Nginx、Envoy 配置

负载均衡是分布式系统的核心组件,它将请求合理地分发到多个后端服务器,实现水平扩展、故障转移和高可用。

一、负载均衡层次

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 对比

特性L4L7
性能更高(不解包)较低(解析 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;
        }
    }
}

五、总结

组件层级算法场景
NginxL7/L4轮询、加权、IP 哈希、最少连接Web 入口、API 网关
LVSL4多种极高性能场景
EnvoyL7丰富的负载均衡策略云原生、Service Mesh
DNSL3地理、轮询多活架构、全球调度

负载均衡不仅是请求分发,更是系统高可用的核心保障。合理的算法选型、完善的监控告警、自动化的故障转移,才能构建真正可靠的服务架构。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「network」更多文章

  1. 网络安全:TLS/SSL、证书与加密通信
  2. DNS 系统与智能解析
  3. WebSocket 协议与实时通信