3. 线程基础与生命周期

负载均衡这东西,说白了就是「把流量分给多台机器干」。

我刚开始做高并发系统时,以为负载均衡就是加个 Nginx 完事。后来线上流量一上来,才发现不同层次的负载均衡各有各的战场。今天咱们就把 DNS、Nginx、LVS 和一致性哈希这四块掰开揉碎讲清楚。

核心观点:没有万能的负载均衡方案。DNS 解决入口分散,LVS 解决四层高吞吐,Nginx 解决七层精细化,一致性哈希解决缓存分片。四者组合使用才是生产常态。

负载均衡分层架构图 负载均衡分层架构 DNS 负载均衡(全局入口) 按地域 / 运营商返回不同 IP LVS 四层负载均衡(集群入口) VIP → DIP,DR / NAT / TUN 模式 Nginx 七层反向代理 HTTP 路由 / 限流 / 健康检查 应用服务 A 实例池 应用服务 B 实例池 应用服务 C 实例池 一致性哈希:用于缓存分片 / 数据库分库,保证节点增减时最小化数据迁移

3.1 DNS 负载均衡:最容易被忽视的入口层

DNS 负载均衡,其实就是同一个域名配多个 A 记录。用户访问时,DNS 服务器轮询或按地域返回不同 IP。

我记得有一次做跨国业务,所有流量都打到北京机房,海外用户延迟高得离谱。后来我们在 DNS 层面做了分地域解析——北美用户走 AWS 新加坡节点,欧洲用户走法兰克福节点。效果立竿见影,延迟从 300ms 降到了 80ms。

特性 说明
优点 天然分布式,无中心节点;成本极低;支持地域/运营商调度
缺点 DNS 缓存导致生效慢(TTL 期间不可控);无法感知后端健康状态;粒度粗(只能到 IP 级别)
典型场景 多机房入口分流、CDN 调度、容灾切换

避坑:我曾经遇到过 DNS 缓存雪崩——某个大版本升级时改了 DNS 记录,但大量用户设备缓存了旧 IP,导致新老集群同时被访问,数据不一致。后来我们强制要求:DNS 变更前先把 TTL 降到 60 秒,观察 24 小时再正式切换。

3.2 Nginx 反向代理:七层负载均衡的瑞士军刀

Nginx 工作在 OSI 第七层,能读懂 HTTP 协议。这意味着它可以做很多「聪明」的事——按 URL 路径路由、按 Header 分发、甚至根据 Cookie 做会话保持。

我个人习惯在生产环境用 Nginx 做「网关层」,前面挂 LVS 扛流量,后面挂业务服务。Nginx 负责:

  • 路由转发: /api/* 走后端 A,/web/* 走后端 B
  • 限流熔断: 单 IP 每秒 100 次,超了直接 503
  • 健康检查: 自动摘除宕机节点
  • SSL 卸载: 证书放在 Nginx,后端走 HTTP 明文
# Nginx 负载均衡配置示例
upstream backend {
    # 默认轮询
    server 10.0.1.1:8080 weight=5;
    server 10.0.1.2:8080 weight=3;
    server 10.0.1.3:8080 backup;  # 备用节点

    # 健康检查(需 nginx_upstream_check_module)
    check interval=3000 rise=2 fall=5 timeout=1000;
}

server {
    listen 80;
    location /api/ {
        proxy_pass http://backend;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

经验之谈:Nginx 的 worker_processes 建议设为 CPU 核心数。我见过有人设成 64,结果上下文切换开销比处理请求还大。另外,keepalive 一定要开,能减少大量 TCP 握手开销。

3.3 LVS 四层负载均衡:高并发场景的基石

LVS(Linux Virtual Server)工作在第四层(传输层),只处理 TCP/UDP 包的转发。它不解析 HTTP 内容,所以性能极高——单机轻松跑满 10Gbps 线速。

LVS 有三种工作模式,我简单对比一下:

模式 工作原理 性能 典型场景
DR(直接路由) 修改目标 MAC 地址,RS 直接回包给客户端 ★★★★★ 最常用,要求 RS 和 LVS 在同一二层网络
NAT(网络地址转换) 修改目标 IP 和端口,RS 回包经过 LVS ★★★☆☆ LVS 容易成为瓶颈,适合小规模集群
TUN(IP 隧道) IP 包再封装一层,RS 解封装后直接回包 ★★★★☆ 跨机房部署,RS 可以不在同一网络

我在项目中用得最多的是 DR 模式。配置起来也不复杂:

# LVS DR 模式配置示例(ipvsadm)
# 在 LVS 节点上配置 VIP
ip addr add 192.168.1.100/32 dev lo:0

# 添加虚拟服务
ipvsadm -A -t 192.168.1.100:80 -s rr

# 添加真实服务器(RS)
ipvsadm -a -t 192.168.1.100:80 -r 10.0.1.1:80 -g
ipvsadm -a -t 192.168.1.100:80 -r 10.0.1.2:80 -g
ipvsadm -a -t 192.168.1.100:80 -r 10.0.1.3:80 -g

注意:DR 模式下,RS 必须配置抑制 ARP 响应,否则 RS 会跟 LVS 抢 VIP。我曾经因为忘了配 arp_ignore 和 arp_announce,导致整个集群网络风暴,排查了整整一个下午。

3.4 一致性哈希:缓存分片与数据路由的利器

前面讲的 DNS、Nginx、LVS 都是「无状态」的负载均衡——请求分给谁都可以。但缓存场景不一样:你希望同一个 key 每次都落到同一台缓存节点上,否则缓存命中率会暴跌。

普通哈希取模的问题很明显:节点数一变,几乎所有 key 都要重新映射。这就是所谓的「缓存雪崩」——大量请求穿透到数据库。

一致性哈希解决了这个问题。它把哈希值空间组织成一个环(0 ~ 2^32-1),节点和 key 都哈希到环上。key 顺时针找到的第一个节点就是目标节点。

我举个例子:

  • 有 3 个缓存节点:A、B、C
  • 哈希环上有 100 个 key
  • 现在加一个节点 D
  • 普通哈希:100 个 key 全部重新映射
  • 一致性哈希:只有 D 节点顺时针方向到上一个节点之间的 key 需要迁移,大约 25 个

关键优化:虚拟节点。如果物理节点少,哈希环上分布不均匀会导致「倾斜」——某个节点负载特别高。解决办法是每个物理节点创建 100~200 个虚拟节点,均匀分布在环上。我在 Redis Cluster 和 Memcached 集群中都这么干过,效果很好。

# 一致性哈希的 Python 伪代码(示意)
import hashlib

class ConsistentHash:
    def __init__(self, nodes=None, virtual_nodes=150):
        self.virtual_nodes = virtual_nodes
        self.ring = {}
        self.sorted_keys = []
        if nodes:
            for node in nodes:
                self.add_node(node)

    def _hash(self, key):
        return int(hashlib.md5(key.encode()).hexdigest(), 16)

    def add_node(self, node):
        for i in range(self.virtual_nodes):
            vnode_key = f"{node}#{i}"
            hash_val = self._hash(vnode_key)
            self.ring[hash_val] = node
            self.sorted_keys.append(hash_val)
        self.sorted_keys.sort()

    def get_node(self, key):
        if not self.ring:
            return None
        hash_val = self._hash(key)
        # 二分查找第一个 >= hash_val 的节点
        for k in self.sorted_keys:
            if k >= hash_val:
                return self.ring[k]
        # 如果超出环尾,回到环头
        return self.ring[self.sorted_keys[0]]

实际应用:一致性哈希不只在缓存层有用。我在做数据库分库分表时也用过——按用户 ID 哈希路由到不同的分库,加机器时只需要迁移少量数据。另外,Nginx 的 ngx_http_upstream_consistent_hash 模块可以直接用,省得自己实现。

3.5 四种策略如何组合?

你想想看,一个真正的高并发系统,通常不会只用一种负载均衡策略。我画了个典型的组合链路:

  1. DNS 层: 按地域解析到不同机房的 LVS 集群 VIP
  2. LVS 层: 四层转发,把流量分到机房的 Nginx 集群
  3. Nginx 层: 七层路由,按 URL 路径分到不同的业务服务
  4. 业务服务层: 内部再用一致性哈希路由到缓存或数据库分片

每一层解决不同的问题。DNS 解决「用户该去哪个机房」,LVS 解决「机房内流量怎么分给 Nginx」,Nginx 解决「请求该去哪个服务」,一致性哈希解决「数据该存到哪个节点」。

嗯,这套组合拳打下来,单机房万级 QPS、跨机房百万级 QPS 都不是问题。我参与过的几个日活千万级的项目,底层基本都是这个架构。