结论: 服务器负载均衡(Load Balancing)的配置分三步:选层级(四层 LVS 或七层 Nginx/HAProxy)、配 upstream 后端池与调度算法、配健康检查与高可用。日 PV 百万以内的网站用 Nginx 七层反向代理即可;需要超高性能或透传客户端 IP 选 LVS DR;需要精细流量治理选 HAProxy;最后用 Keepalived 给调度器本身做主备,消除单点。
为什么需要负载均衡?
一台服务器总有上限:4 核 8G 的云主机跑 PHP-FPM 大概能撑 300~800 QPS,跑 Java 应用到 1500 QPS 左右就接近瓶颈。当业务量超过单机能力,或者不能接受"这台机器挂了整个站就打不开",就必须引入负载均衡。
负载均衡器(Load Balancer)的作用有三点:分流——把请求按规则摊到多台后端;容错——后端某台故障自动摘除;扩展——加机器就能线性提升容量,对前端无感知。
本文是系统性教程,覆盖从层级选型、三种主流实现的完整配置、调度算法、健康检查、会话保持、SSL 终结到调度器自身高可用的全部环节。如果你只是想快速配一个 Nginx upstream,可以看同站的快速上手篇;本文侧重"搭一套能上生产的完整体系"。
一、四层负载均衡与七层负载均衡怎么选?
结论:七层(L7)看得懂 HTTP,能按域名、URL、Cookie 分流,功能强但性能略低;四层(L4)只看 IP 和端口,性能极高但不能做内容路由。 绝大多数业务先用七层。
| 对比项 | 四层负载均衡(L4) | 七层负载均衡(L7) |
|---|---|---|
| 工作层次 | 传输层,基于 IP + 端口 + 协议(TCP/UDP) | 应用层,解析 HTTP/HTTPS 报文内容 |
| 典型实现 | LVS(IPVS)、HAProxy tcp mode、云厂商 CLB 四层 | Nginx、HAProxy http mode、Envoy、云厂商 ALB 七层 |
| 性能上限 | 极高,LVS DR 模式可打满万兆网卡,百万并发连接 | 较高,单台 Nginx 通常 5 万~10 万并发连接、数万 QPS |
| 路由依据 | 源/目标 IP、端口 | 域名(Host)、URL 路径、Header、Cookie、请求方法 |
| 会话保持 | 只能按源 IP 哈希(sh / ip_hash 思路) | 可基于 Cookie、Header、URL 参数做精细粘滞 |
| 附加能力 | 基本没有 | SSL 终结、压缩、缓存、限流、改写 Header、鉴权 |
| 后端要求 | LVS DR 模式要求后端配置 VIP 并抑制 ARP | 无特殊要求,后端是普通 HTTP 服务即可 |
| 适用场景 | 超高性能入口、数据库/Redis/游戏长连接代理 | Web 网站、API 网关、微服务入口、按路径分流 |
选择建议:Web 业务一律从七层 Nginx 起步;当日均请求超过单机 Nginx 承载(约 3 万 QPS)时,在前面叠一层 LVS DR 做四层入口,形成"LVS + Nginx"两级架构,这是国内互联网公司非常经典的组合。
二、主流实现选型:Nginx / HAProxy / LVS
结论:Nginx 综合最省心,HAProxy 健康检查与统计最专业,LVS 性能最高但配置最复杂。
| 实现 | 层级 | 性能 | 健康检查 | 配置难度 | 推荐场景 |
|---|---|---|---|---|---|
| Nginx | 七层(1.9+ 支持四层 stream) | 高 | 开源版仅被动检查,主动检查需 Plus 或第三方模块 | 低 | 网站、API、反向代理、SSL 终结 |
| HAProxy | 七层 + 四层 | 很高 | 内置主动健康检查,支持多种 check 方式 | 中 | 需要精细流量治理、TCP 代理、专业统计页面 |
| LVS(IPVS) | 四层 | 极高(内核态转发) | 需配合 keepalived/ldirectord 或脚本 | 高 | 百万级并发入口、数据库代理、游戏长连接 |
三、Nginx 负载均衡完整配置
结论:核心是 upstream 定义后端池 + proxy_pass 转发,配合 weight、max_fails、fail_timeout、backup 四个参数控制权重与容错。
3.1 基础 upstream 与反向代理
假设有三台后端应用服务器 10.0.0.11、10.0.0.12、10.0.0.13,前端调度器为 10.0.0.10。
# /etc/nginx/conf.d/lb.conf —— 定义后端池
upstream app_pool {
# weight 权重:3 表示这台机器分到的请求是默认机器的 3 倍,按性能配比
server 10.0.0.11:8080 weight=3 max_fails=3 fail_timeout=30s;
server 10.0.0.12:8080 weight=2 max_fails=3 fail_timeout=30s;
server 10.0.0.13:8080 weight=1 max_fails=3 fail_timeout=30s;
# backup 备份节点:所有主节点都挂掉后才会接流量
server 10.0.0.14:8080 backup;
# 长连接复用,显著降低 TIME_WAIT 与握手开销
keepalive 64;
}
server {
listen 80;
server_name www.example.com;
location / {
proxy_pass http://app_pool;
# 传递真实客户端信息给后端
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_set_header X-Forwarded-Proto $scheme;
# 超时控制:connect 建连 5s,send 发送 60s,read 读取 60s
proxy_connect_timeout 5s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
# 失败重试策略:后端返回 500/502/503/504 或超时时切换下一台
proxy_next_upstream error timeout http_500 http_502 http_503 http_504;
proxy_next_upstream_tries 3;
proxy_next_upstream_timeout 10s;
# 复用 upstream 中的 keepalive 长连接,必须配合 HTTP/1.1
proxy_http_version 1.1;
proxy_set_header Connection "";
}
# 调度器自身的健康检查出口,便于外部探活
location = /nginx-status {
stub_status on;
allow 127.0.0.1;
allow 10.0.0.0/24;
deny all;
}
}# 校验配置并热加载,注意用 reload 而不是 restart,避免断连
sudo nginx -t
sudo nginx -s reload
# 观察后端分发是否均匀(连续请求 20 次看命中)
for i in $(seq 1 20); do curl -s http://10.0.0.10/ -o /dev/null -w "%{http_code}\n"; done3.2 健康检查:开源版 Nginx 的准确做法
这里是最容易踩坑的地方:开源版 Nginx 没有主动健康检查指令。health_check 是 NGINX Plus 的专有指令,直接写进开源版配置会报错 unknown directive "health_check"。
开源版 Nginx 的健康检查是被动式的,依靠上面配置中的 max_fails 与 fail_timeout:
- 含义是:在
fail_timeout(30s)窗口内,若与该节点的失败请求数累计达到max_fails(3 次),则判定该节点不可用并摘除 30 秒;30 秒后重新尝试放行一个请求探测。 - 缺点:必须先有真实请求打过去才知道节点挂了,极端情况下会有少量请求失败(但配合
proxy_next_upstream,对客户端是透明的)。
如果需要主动健康检查(定期主动探测后端 /health 接口),有三条路:
- 使用 NGINX Plus 的
health_check指令(商业版); - 使用淘宝开源的 Tengine,它内置
check指令; - 编译第三方模块
nginx_upstream_check_module。
下面是 Tengine / 第三方模块的主动健康检查写法,供参考:
# 主动健康检查(Tengine 或 nginx_upstream_check_module 环境)
upstream app_pool {
server 10.0.0.11:8080 weight=3;
server 10.0.0.12:8080 weight=2;
server 10.0.0.13:8080 weight=1;
# 每 3 秒探测一次 /health,连续 2 次成功视为 up,连续 3 次失败视为 down
check interval=3000 rise=2 fall=3 timeout=2000 type=http;
check_http_send "GET /health HTTP/1.0\r\nHost: app_pool\r\n\r\n";
check_http_expect_alive http_2xx http_3xx;
}
server {
listen 80;
location /status {
check_status; # Tengine 的后端节点状态页面
access_log off;
allow 10.0.0.0/24;
deny all;
}
}3.3 七种调度算法对照表
结论:默认用加权轮询,需要会话保持用 ip_hash 或一致性哈希,后端性能差异大用最少连接。
| 调度算法 | Nginx 指令 | 原理 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|---|
| 轮询(Round Robin) | 默认 | 按顺序逐个分发 | 简单均匀 | 不考虑后端性能差异 | 后端配置完全一致 |
| 加权轮询(Weighted RR) | weight=n | 按权重比例分发 | 可按机器性能分配 | 权重需人工维护 | 后端配置不一致(最常见) |
| 最少连接(Least Conn) | least_conn | 优先发给当前连接数最少的节点 | 能应对长连接、慢请求 | 不看实际负载 | 请求耗时差异大的业务 |
| 源地址哈希(ip_hash) | ip_hash | 按客户端 IP 前 24 位哈希固定到某台 | 天然实现会话保持 | 节点变动会大规模重映射;NAT 出口用户会扎堆 | 需要会话保持且无 Cookie 方案 |
| URL 哈希(url_hash) | 需第三方模块 | 按请求 URI 哈希 | 提升后端缓存命中率 | 热点 URL 会压单台 | 后端有本地缓存/CDN 回源 |
| fair(响应时间) | 需第三方模块 nginx-upstream-fair | 按后端响应快慢动态分配 | 自适应能力强 | 第三方模块,维护成本高 | 后端响应时间波动大 |
| 一致性哈希 | hash $key consistent | 哈希环映射,节点变动只影响 1/N 流量 | 节点上下线影响最小 | 配置略复杂 | 缓存类服务、大规模集群 |
# 一致性哈希示例:按客户端 IP 做 key,节点增减只影响少量请求
upstream cache_pool {
hash $remote_addr consistent;
server 10.0.0.21:11211;
server 10.0.0.22:11211;
server 10.0.0.23:11211;
}3.4 会话保持的三种方案
结论:优先用"共享 Session",其次 Cookie 插入,最后才考虑 ip_hash。 依赖 ip_hash 会让扩容变得痛苦。
- 方案一:共享 Session(推荐)——把 Session 存到 Redis,后端节点无状态,随意扩缩容。
# PHP 场景:php.ini 中把 session 存到 Redis
session.save_handler = redis
session.save_path = "tcp://10.0.0.30:6379?auth=RedisPass2026&timeout=2"- 方案二:Cookie 插入(sticky cookie)——调度器在响应里种一个 Cookie,后续请求按 Cookie 固定到某台。Nginx 开源版需
nginx-sticky-module,HAProxy 原生支持。
# HAProxy 原生 cookie 粘滞(见下一节完整配置)
backend app_pool
balance roundrobin
cookie SERVERID insert indirect nocache maxidle 30m
server web1 10.0.0.11:8080 check cookie s1
server web2 10.0.0.12:8080 check cookie s2- 方案三:ip_hash——最简单但最不灵活,节点数量变化会导致大部分用户的会话失效,且在移动端(基站 IP 频繁变化)表现很差。
3.5 SSL 终结与后端走 HTTP
结论:把 HTTPS 终结在负载均衡器上,后端用 HTTP 通信,既省后端 CPU 又能统一证书管理。 内网可信时这是标准做法;如果内网不可信,需做 SSL 透传或后端也配 HTTPS。
# SSL 终结:客户端 HTTPS → 调度器解密 → 后端 HTTP
upstream app_pool {
server 10.0.0.11:8080 weight=3 max_fails=3 fail_timeout=30s;
server 10.0.0.12:8080 weight=2 max_fails=3 fail_timeout=30s;
}
server {
listen 80;
server_name www.example.com;
return 301 https://$host$request_uri; # HTTP 全量跳转 HTTPS
}
server {
listen 443 ssl;
http2 on;
server_name www.example.com;
ssl_certificate /etc/nginx/ssl/example.com/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 1d;
ssl_session_tickets off;
# 后端拿到 X-Forwarded-Proto: https,应用层据此生成 https 链接
location / {
proxy_pass http://app_pool;
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_set_header X-Forwarded-Proto https;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}四、HAProxy 配置示例
结论:HAProxy 用 frontend 定义入口、backend 定义后端池,内置主动健康检查,比 Nginx 更适合做专业的四七层流量治理。
# /etc/haproxy/haproxy.cfg
global
log /dev/log local0 info
maxconn 100000
user haproxy
group haproxy
daemon
stats socket /run/haproxy/admin.sock mode 660 level admin
defaults
log global
mode http
option httplog
option dontlognull
option redispatch # 连接失败时重分发到其它节点
retries 3
timeout connect 5s
timeout client 60s
timeout server 60s
timeout http-request 15s
default-server inter 3s fall 3 rise 2 # 全局健康检查:3s 间隔,3 次失败摘除,2 次成功恢复
# ---------- 七层 HTTP 入口 ----------
frontend http_in
bind *:80
# 按域名分流
acl is_api hdr_beg(Host) -i api.
acl is_static path_beg /static/ /assets/
use_backend api_pool if is_api
use_backend api_pool if is_static
default_backend web_pool
frontend https_in
bind *:443 ssl crt /etc/haproxy/certs/example.com.pem alpn h2,http/1.1
default_backend web_pool
# ---------- Web 后端池 ----------
backend web_pool
balance roundrobin
# Cookie 粘滞:插入 SERVERID,用户固定到首次命中的节点
cookie SERVERID insert indirect nocache maxidle 30m
# 主动健康检查:GET /health 必须返回 2xx/3xx
option httpchk GET /health HTTP/1.1\r\nHost:\ www.example.com
http-check expect status 200
server web1 10.0.0.11:8080 check weight 3 cookie s1
server web2 10.0.0.12:8080 check weight 2 cookie s2
server web3 10.0.0.13:8080 check weight 1 cookie s3 backup
# ---------- API 后端池(最少连接) ----------
backend api_pool
balance leastconn
option httpchk GET /healthz
http-check expect status 200
server api1 10.0.0.21:9000 check
server api2 10.0.0.22:9000 check
# ---------- 四层 TCP 代理(如 MySQL 只读、Redis) ----------
frontend mysql_in
bind *:3307
mode tcp
option tcplog
default_backend mysql_ro
backend mysql_ro
mode tcp
balance leastconn
server db1 10.0.0.31:3306 check port 3306 inter 5s
server db2 10.0.0.32:3306 check port 3306 inter 5s
# ---------- 统计页面 ----------
listen stats
bind *:8404
mode http
stats enable
stats uri /stats
stats refresh 10s
stats admin if TRUE
stats auth admin:StrongStatsPass2026# 校验 + 热重载(HAProxy 支持 -sf 平滑接管旧进程 socket,不中断连接)
sudo haproxy -c -f /etc/haproxy/haproxy.cfg
sudo systemctl reload haproxy
# 查看统计页面
curl -u admin:StrongStatsPass2026 http://127.0.0.1:8404/stats | head -40五、LVS DR 模式简述
结论:LVS DR(Direct Routing,直接路由)模式下,调度器只改写目标 MAC 地址把包转给后端,响应报文由后端直接回给客户端,不经过调度器,因此性能极高。 代价是后端必须配置 VIP 并抑制 ARP。
# ===== 调度器(Director)10.0.0.10 =====
# 在 lo 接口上绑定 VIP(注意掩码 /32)
sudo ip addr add 10.0.0.100/32 dev lo
sudo ip addr add 10.0.0.100/32 dev eth0 label eth0:0
# 添加虚拟服务(-A)与真实服务器(-a),-g 表示 DR 模式,-w 为权重
sudo ipvsadm -A -t 10.0.0.100:80 -s wlc # wlc = 加权最少连接
sudo ipvsadm -a -t 10.0.0.100:80 -r 10.0.0.11:80 -g -w 3
sudo ipvsadm -a -t 10.0.0.100:80 -r 10.0.0.12:80 -g -w 2
sudo ipvsadm -a -t 10.0.0.100:80 -r 10.0.0.13:80 -g -w 1
# 查看连接分发情况
sudo ipvsadm -Ln --stats
# ===== 后端真实服务器(RS)上必须做的 ARP 抑制 =====
cat <<'EOF' | sudo tee /etc/sysctl.d/90-lvs-dr.conf
net.ipv4.conf.all.arp_ignore = 1
net.ipv4.conf.lo.arp_ignore = 1
net.ipv4.conf.all.arp_announce = 2
net.ipv4.conf.lo.arp_announce = 2
EOF
sudo sysctl --system
# 后端同样要把 VIP 绑在 lo 上(掩码 /32),否则无法接收目标为 VIP 的包
sudo ip addr add 10.0.0.100/32 dev loDR 模式关键点:调度器与后端必须在同一个二层网络(同一 VLAN/交换机),不能跨三层;后端响应直接回客户端,所以客户端 IP 天然透传,无需 X-Forwarded-For。
六、Keepalived 给调度器做高可用
结论:调度器本身是单点,必须用 Keepalived 做主备,通过 VRRP 协议让 VIP 在主备之间漂移。 两台调度器共享 VIP 10.0.0.100,主节点故障时 VIP 秒级漂到备节点。
# 主节点 /etc/keepalived/keepalived.conf(备节点改 state 为 BACKUP、priority 改 90)
global_defs {
router_id LB_01
script_user root
enable_script_security
}
# 检测本机 Nginx 是否存活的脚本
vrrp_script chk_nginx {
script "/usr/bin/killall -0 nginx" # 只检测进程存在,不做重启
interval 2
timeout 2
weight -30 # 检测失败时优先级降 30,触发主备切换
fall 2
rise 2
}
vrrp_instance VI_1 {
state MASTER
interface eth0
virtual_router_id 51 # 同一组主备必须一致,取值 0~255
priority 100 # 主 100,备 90,高者持有 VIP
advert_int 1 # VRRP 通告间隔 1 秒
authentication {
auth_type PASS
auth_pass VrrpPass2026
}
virtual_ipaddress {
10.0.0.100/24 dev eth0 label eth0:0
}
track_script {
chk_nginx
}
notify_master "/usr/bin/systemctl start nginx"
notify_backup "/usr/bin/systemctl start nginx"
notify_fault "/usr/bin/systemctl stop nginx"
}# 两台调度器都启动 Keepalived
sudo systemctl enable --now keepalived
# 在主节点上确认 VIP 已生效
ip addr show eth0 | grep 10.0.0.100
# 手动模拟切换:主节点停 keepalived,观察 VIP 漂移到备节点
sudo systemctl stop keepalived
# 备节点上查看日志确认接管
sudo journalctl -u keepalived -f
企业QQ咨询




