4. 负载均衡策略:轮询、加权轮询、最少连接、一致性哈希
各位同学,咱们今天聊聊负载均衡策略。说实话,这是路由系统里最核心的环节之一。你想想看,上游有几十上百个报价源,下游有成千上万的请求涌进来,怎么把请求分出去,直接决定了系统的吞吐和稳定性。
我这些年踩过的坑,有一半都跟负载策略选型有关。选对了,系统稳如老狗;选错了,半夜三点被报警电话叫醒。嗯,咱们一个一个来看。
4.1 轮询(Round Robin)—— 最朴素的公平
轮询,说白了就是「挨个来」。请求1给节点A,请求2给节点B,请求3给节点C,然后回到A继续。这是最简单的策略,没有之一。
我刚开始做路由系统时,第一个版本用的就是轮询。为什么?因为快,因为无状态,因为不需要知道后端任何信息。
但这里有个坑。我曾经在一个项目里,所有报价源服务器配置一模一样,心想轮询总没问题吧?结果跑了一个月,发现某台机器的CPU总是比其他机器高20%。查了半天,原来是这台机器上跑了个定时任务,虽然配置一样,但实际可用资源已经不一样了。
4.2 加权轮询(Weighted Round Robin)—— 给强者更多机会
加权轮询,就是在轮询的基础上,给每个节点配一个权重。权重高的节点,被选中的次数就多。
举个例子:
# 假设三个报价源节点
节点A: 权重 5
节点B: 权重 3
节点C: 权重 2
# 每10个请求的分配模式
A A A A A B B B C C
我个人习惯用加权轮询来应对「异构集群」。比如你刚扩容了一批新机器,性能是老机器的两倍,那新机器权重设2,老机器设1,流量自然就倾斜过去了。
这里有个小技巧:权重不要设得太大。我见过有人把权重设成100、50、10这种跨度,结果流量分配非常不均匀。建议权重范围控制在1-10之间,颗粒度够用,也容易调整。
4.3 最少连接(Least Connections)—— 动态感知负载
最少连接,顾名思义,就是每次请求都发给当前活跃连接数最少的那个节点。
这个策略比轮询聪明一点。它不假设节点性能相同,而是实时感知每个节点正在处理多少请求。你想想看,如果某个节点处理得慢,它的活跃连接数就会堆积,新请求自然就不会再发给它了。
我在做实时报价系统时,特别喜欢用这个策略。因为报价请求的响应时间波动很大——有的请求100ms就回来了,有的可能要等2秒。如果用轮询,慢请求会堵在某个节点上;用最少连接,系统会自动把新请求分流到空闲节点。
但要注意,最少连接需要维护每个节点的连接计数,这会带来额外的内存开销。对于高并发场景(每秒几万请求),这个计数器的更新和查询必须非常高效。我一般用原子操作或者无锁队列来实现,避免锁竞争。
4.4 一致性哈希(Consistent Hashing)—— 缓存友好的路由
一致性哈希,这个稍微复杂一点。它的核心思想是:把请求的某个特征(比如用户ID、报价ID)哈希到一个环上,然后顺时针找到最近的节点。
为什么要用这个?因为它在节点增减时,只会影响少量请求的路由。你想想看,如果用普通哈希取模,加一台机器,所有请求的路由都会变,缓存全部失效,这就是所谓的「缓存雪崩」。
我画了一张图,帮你理解一致性哈希的工作原理:
你看,每个请求根据它的哈希值落在环上,然后顺时针找到第一个节点。如果节点B挂了,原来路由到B的请求会顺延到C,只有这部分请求受影响,其他请求的路由不变。
我在做报价缓存系统时,就用了一致性哈希。每个报价源的请求根据报价ID哈希,同一个报价ID永远路由到同一个节点。这样缓存命中率极高,而且加节点时,只有少量缓存需要重建。
4.5 四种策略对比
说了这么多,咱们用一张表总结一下:
| 策略 | 核心思想 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| 轮询 | 依次分配 | 实现简单,无状态 | 不考虑节点差异 | 同构集群,请求处理时间均匀 |
| 加权轮询 | 按权重分配 | 支持异构集群 | 权重需要手动调整 | 节点性能不同,但相对稳定 |
| 最少连接 | 动态感知负载 | 自动适应慢节点 | 需要维护连接状态 | 请求处理时间波动大 |
| 一致性哈希 | 哈希环路由 | 节点增减影响小 | 实现复杂,有哈希倾斜风险 | 需要缓存亲和性的场景 |
4.6 我的选型建议
说实话,没有银弹。我一般这样选:
- 初期系统:用轮询,简单可靠,出问题容易排查。
- 异构集群:加权轮询,配合监控动态调整权重。
- 响应时间波动大:最少连接,比如实时报价、API网关。
- 需要缓存亲和性:一致性哈希,比如报价缓存、会话保持。
最后说一句:不管你选哪种策略,一定要加「熔断」和「重试」机制。我见过太多系统,负载策略选得完美,结果一个节点挂了,重试请求全部打到另一个节点,直接把那个节点也打挂了。嗯,这就是所谓的「级联故障」。
公众号:蓝海资料掘金营,微信deep3321