2、负载均衡策略:DNS、Nginx、LVS、F5的原理与选型,一致性哈希与动态权重
负载均衡这东西,说白了就是“把流量分给多台机器干活”。
我刚开始带团队那会儿,总觉得加台服务器就能扛住。结果呢?流量一上来,某台机器直接被打挂,其他机器还在那儿闲着。嗯,从那以后我才明白——没有好的负载均衡策略,加再多机器也是白搭。
2.1 四层 vs 七层:先搞清楚你在哪一层干活
选负载均衡之前,得先分清楚四层和七层。我见过不少新手把这两个混为一谈,结果架构搭出来四不像。
| 对比维度 | 四层(L4) | 七层(L7) |
|---|---|---|
| 工作层级 | 传输层(TCP/UDP) | 应用层(HTTP/HTTPS) |
| 核心能力 | IP+端口转发 | URL、Header、Cookie解析 |
| 性能 | 极高(内核态处理) | 较高(用户态解析) |
| 典型代表 | LVS、F5(L4模式) | Nginx、HAProxy |
| 适用场景 | 数据库、RPC、长连接 | Web应用、API网关 |
我的建议是:能四层就别七层,除非你需要做内容路由。四层转发快,资源消耗小;七层灵活,但每多一层解析就多一分延迟。
2.2 四种主流负载均衡方案
2.2.1 DNS 负载均衡
DNS 是最“简单粗暴”的方式。同一个域名配多个 A 记录,DNS 服务器轮询返回不同 IP。
优点:免费、配置简单、天然地理分散。
缺点:DNS 缓存导致生效慢、无法感知后端健康状态。
我记得有一次线上事故,就是因为 DNS 缓存没刷新,用户死活访问不到新切的机房。从那以后,我对 DNS 负载均衡就多留了个心眼——它只能做流量调度,不能做高可用。
2.2.2 Nginx / OpenResty
Nginx 是目前最主流的七层负载均衡器。我个人的习惯是,所有 HTTP 流量先过 Nginx,再做转发。
upstream backend {
server 192.168.1.10:8080 weight=3;
server 192.168.1.11:8080 weight=2;
server 192.168.1.12:8080 backup;
}
server {
listen 80;
location / {
proxy_pass http://backend;
proxy_set_header Host $host;
}
}
这段配置里,weight 就是权重,backup 表示备用节点。Nginx 默认用轮询,但你可以改成 least_conn(最少连接)或 ip_hash(IP哈希)。
避坑指南:我曾经把 upstream 里的 server 写成了 IP 加端口,但忘了配健康检查。结果某台服务器挂了,Nginx 还在往里转发请求,导致大量 502。后来我养成了习惯——所有生产环境的 upstream 必须配 max_fails 和 fail_timeout。
2.2.3 LVS(Linux Virtual Server)
LVS 是 Linux 内核自带的四层负载均衡。性能极高,单机轻松扛百万并发。
LVS 有三种工作模式,我简单列一下:
| 模式 | 原理 | 性能 | 典型场景 |
|---|---|---|---|
| NAT | 修改目标 IP 和端口 | 中等 | 后端服务器少 |
| TUN | IP 隧道封装 | 高 | 跨机房部署 |
| DR | 直接路由,不修改报文 | 最高 | 同网段高性能场景 |
我个人最常用 DR 模式。它只修改 MAC 地址,响应流量直接回给客户端,不经过 LVS 节点。说白了,LVS 只负责“问路”,不负责“带路”,所以性能极高。
2.2.4 F5 硬件负载均衡
F5 是商业硬件方案。贵,但稳定。我接触 F5 是在一个金融项目里,客户要求 99.999% 可用性,软件方案他们不放心。
F5 的优势在于:
- 专用硬件,不占用业务服务器资源
- 内置 SSL 卸载、DDoS 防护
- 支持脚本化策略(iRules)
但说实话,大部分互联网公司用不到 F5。LVS + Nginx 的组合已经能覆盖 99% 的场景。F5 更适合银行、证券这些“不差钱”的行业。
2.3 一致性哈希:解决缓存雪崩的利器
普通哈希取模有个大问题——节点增减时,大量 key 会重新映射。比如你有 10 台缓存服务器,加一台变成 11 台,原来落在第 1 台的 key 可能跑到第 5 台去了。缓存全部失效,数据库瞬间被打穿。
一致性哈希就是来解决这个问题的。它把哈希值空间组织成一个环,每个节点落在环上。数据找节点时,顺时针找最近的节点。
# 简单的一致性哈希实现(Python 伪代码)
import hashlib
class ConsistentHash:
def __init__(self, nodes=None, replicas=150):
self.replicas = replicas # 虚拟节点数
self.ring = {}
for node in nodes:
self.add_node(node)
def add_node(self, node):
for i in range(self.replicas):
key = self._hash(f"{node}:{i}")
self.ring[key] = node
def get_node(self, key):
if not self.ring:
return None
hash_val = self._hash(key)
# 顺时针找第一个大于等于 hash_val 的节点
for h in sorted(self.ring.keys()):
if h >= hash_val:
return self.ring[h]
return self.ring[min(self.ring.keys())]
def _hash(self, key):
return int(hashlib.md5(key.encode()).hexdigest(), 16)
这段代码里有个关键参数——replicas(虚拟节点数)。为什么要虚拟节点?因为真实节点少的时候,环上分布不均匀,容易导致“倾斜”。虚拟节点越多,分布越均匀。我一般设 150 个虚拟节点,效果不错。
注意:一致性哈希只能减少 rehash 的影响,不能完全消除。节点增减时,只有相邻节点的数据会受影响。如果你需要“零迁移”,那就得用分布式缓存中间件(如 Redis Cluster)了。
2.4 动态权重:让负载均衡“活”起来
静态权重有个硬伤——它假设所有机器性能一样。但现实是,同一批机器,有的 CPU 已经 80% 了,有的还在 20% 闲着。静态权重不会自动调整,结果就是“忙的忙死,闲的闲死”。
动态权重就是让负载均衡器根据后端实时负载来调整权重。我常用的做法是:
- 每台后端服务器上报自己的 CPU、内存、连接数
- 负载均衡器根据这些指标计算一个“健康分数”
- 分数高的权重高,分数低的权重低
# 动态权重计算示例
def calculate_weight(cpu_usage, mem_usage, conn_count):
# CPU 权重占 40%,内存占 30%,连接数占 30%
score = (100 - cpu_usage) * 0.4 + (100 - mem_usage) * 0.3 + (100 - conn_count) * 0.3
# 归一化到 1-10 之间
weight = max(1, min(10, int(score / 10)))
return weight
我曾经在一个直播平台项目里用过这个方案。高峰期流量波动大,静态权重根本扛不住。上了动态权重后,整体吞吐量提升了 30%,而且再也没出现过单机被打挂的情况。
2.5 选型建议:到底该用哪个?
这个问题没有标准答案。我根据自己的经验,给一个参考思路:
- 小规模(日活 < 10万):Nginx 就够了。配置简单,社区活跃,出问题好排查。
- 中规模(日活 10万-100万):LVS(DR 模式)+ Nginx 组合。LVS 扛四层流量,Nginx 做七层路由。
- 大规模(日活 > 100万):DNS 做地理调度 + LVS 集群 + Nginx 集群。三层架构,各司其职。
- 特殊场景(金融、政府):F5 硬件方案。稳定第一,成本第二。
核心原则:不要为了用技术而用技术。如果你的业务只有两台服务器,用 Nginx 轮询就够了。搞一套 LVS + Keepalived + Nginx 集群,反而增加了运维复杂度。
2.6 本章知识体系图
下面这张图把本章的核心知识点串起来了。你可以把它当作一个“选型决策树”。
这张图从四层/七层分类开始,一直延伸到动态权重和一致性哈希。你可以在做架构选型时把它当作参考——先定层,再选工具,最后配策略。