4、负载均衡技术:DNS负载均衡、四层负载均衡(LVS/HAProxy)、七层负载均衡(Nginx)、一致性哈希在负载均衡中的应用

负载均衡,说白了就是「把流量分给多个人干」。

我刚开始做推荐系统时,觉得这玩意儿不就是个反向代理嘛。后来线上出了几次事故,才明白——负载均衡选不对,高可用就是一句空话。你想想看,流量来了,你往哪分?分给谁?分错了怎么办?

这一章,我把负载均衡的四个层次掰开揉碎讲清楚。从DNS到四层、七层,再到一致性哈希,咱们一层层往上走。

4.1 DNS负载均衡:最便宜的「分流器」

DNS负载均衡,其实就是域名解析时返回多个IP。客户端随机选一个去连。成本最低,但控制力也最弱。

核心原理:DNS服务器针对同一个域名,配置多条A记录。每次解析时,轮询或随机返回一个IP。

我在项目中遇到过一个问题:某次大促,DNS缓存没刷新,流量全打到了旧IP上。那台机器直接被打挂了。嗯,这里要注意——DNS有缓存,TTL没设好,你改配置也没用。

优缺点对比:

优点 缺点
部署简单,零成本 控制粒度粗,无法感知后端健康状态
天然跨地域调度 DNS缓存导致生效慢(分钟级)
无单点故障 不支持会话保持

我的建议:DNS负载均衡只适合做第一层流量调度。后面必须跟四层或七层做精细化控制。别指望DNS帮你做健康检查,它连后端挂了都不知道。

4.2 四层负载均衡:LVS与HAProxy

四层负载均衡,工作在传输层(TCP/UDP)。它不看请求内容,只看IP和端口。转发效率极高,适合做流量入口。

4.2.1 LVS(Linux Virtual Server)

LVS是Linux内核自带的负载均衡方案。性能极强,单机可以扛百万并发。我早期在广告系统里用过,确实稳。

LVS有三种工作模式:

  • NAT模式:修改目标IP和端口,流量经过LVS进出。适合小规模集群。
  • DR模式(直接路由):只修改MAC地址,响应直接回客户端。性能最好,我推荐用这个。
  • TUN模式:通过IP隧道转发,适合跨机房场景。

避坑指南:我曾经在DR模式下踩过一个坑——后端服务器的ARP配置没调,导致LVS的VIP被后端抢答了。结果流量全乱了。记住,DR模式下后端必须抑制ARP响应。

4.2.2 HAProxy

HAProxy是纯用户态的负载均衡器。虽然性能不如LVS,但功能更丰富。支持HTTP健康检查、ACL规则、会话保持等。

我个人习惯用HAProxy做四层转发,因为它配置灵活。举个例子:

frontend recommend_front
    bind *:80
    default_backend recommend_back

backend recommend_back
    balance roundrobin
    server srv1 10.0.0.1:8080 check
    server srv2 10.0.0.2:8080 check
    server srv3 10.0.0.3:8080 check

你看,几行配置就能搞定。而且HAProxy支持「慢启动」——新加入的服务器流量慢慢增加,防止被瞬间打爆。这个特性在推荐服务扩缩容时特别有用。

4.3 七层负载均衡:Nginx

七层负载均衡,工作在应用层(HTTP/HTTPS)。它能看懂请求内容,比如URL、Cookie、Header。适合做精细化路由。

Nginx是七层负载均衡的王者。我几乎在每个项目里都用它。为什么?因为它能做的事情太多了:

  • 根据URL路径分发到不同后端
  • 根据Cookie实现会话保持
  • 根据请求头做灰度发布
  • 限流、熔断、缓存

举个例子,推荐服务经常需要根据用户ID做分片:

upstream recommend_cluster {
    hash $arg_user_id consistent;
    server 10.0.0.1:8080;
    server 10.0.0.2:8080;
    server 10.0.0.3:8080;
}

server {
    listen 80;
    location /recommend {
        proxy_pass http://recommend_cluster;
    }
}

这里用了hash $arg_user_id consistent,就是一致性哈希。后面会细讲。

七层 vs 四层:七层能看懂业务,但性能比四层差一个数量级。我的经验是——入口用LVS扛流量,内部用Nginx做路由。各司其职。

4.4 一致性哈希:解决「扩缩容雪崩」

一致性哈希,说白了就是「怎么把请求稳定地分到同一台机器上」。你想想看,如果用户A的请求每次都被分到不同的服务器,那缓存就白建了。

传统哈希取模有个大问题:服务器数量一变,大部分请求的映射关系都变了。这就是「缓存雪崩」的根源。

一致性哈希怎么解决的?它把服务器和请求都映射到一个环上。请求找离它最近的服务器。增加或减少服务器时,只影响环上相邻的一小部分请求。

我画个简单的示意图:

         [Node A]
            |
    [Node D] +--+-- [Node B]
            |    |
         [Node C]

请求 key1 -> 顺时针找最近的节点 -> Node B
请求 key2 -> 顺时针找最近的节点 -> Node C

增加Node E时,只影响Node E和它逆时针方向第一个节点之间的请求。其他请求不受影响。

我的经验:一致性哈希有个问题——节点少时分布不均匀。解决方案是引入「虚拟节点」。每个物理节点对应多个虚拟节点,分布在环上。这样负载就均匀了。Nginx的consistent参数默认就支持虚拟节点。

我曾经在推荐系统的缓存层用过一致性哈希。当时有20台Redis,每次扩容都要迁移大量数据。改用一致性哈希后,扩容只影响1/20的数据。效果立竿见影。

4.5 总结:怎么选?

嗯,说了这么多,到底怎么选?我给出一个通用方案:

场景 推荐方案
跨地域流量调度 DNS负载均衡 + 地域解析
高并发入口(百万级) LVS(DR模式)
内部服务路由 HAProxy 或 Nginx
需要会话保持 一致性哈希 + Nginx
灰度发布/流量染色 Nginx七层路由

最后说一句:负载均衡不是越高级越好。DNS能解决的问题,别上LVS。LVS能解决的问题,别上Nginx。每多一层,就多一层延迟和复杂度。我的原则是——够用就好,别过度设计。

下一章,咱们聊聊「限流与熔断」。那才是真正考验架构师功底的地方。