负载均衡是把同一批请求分摊到多台后端服务器上,既提升整体吞吐,也避免单点故障,一台机器挂掉时服务还能继续。nginx 用 upstream 块定义一组后端,再在 location 里把 proxy_pass 指向组名即可。本文给出一份可以直接复制的负载均衡配置,逐行说明 weight、max_fails、fail_timeout、backup 各自的作用,对比轮询、加权、ip_hash、least_conn 四种调度策略分别适合什么场景,解释长连接为什么必须配合两个请求头才能真的生效,并讲清开源版只有被动健康检查这一限制。最后说明改完配置后,如何验证流量确实被分摊到了多台机器上,以及后端扩容时只需要改动哪一行。
nginx
upstream backend {
server 10.0.0.11:8080 weight=3 max_fails=2 fail_timeout=30s;
server 10.0.0.12:8080 weight=2 max_fails=2 fail_timeout=30s;
server 10.0.0.13:8080 backup;
keepalive 32;
}
server {
listen 80;
server_name app.example.com;
location / {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
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;
}
}1.upstream backend 定义后端组,组名 backend 是自定义的,后面的 proxy_pass 引用它。组里的每个 server IP:端口 就是一台后端,可以写多行。weight=3 是权重,代表这台分到的请求数量是 weight=1 的 3 倍。max_fails=2 配合 fail_timeout=30s 构成故障判定:在 30 秒内失败 2 次,就认为这台不可用,暂停向它分配流量 30 秒,超时之后再试探一次。backup 标记备份机,只有前面所有主服务器都不可用时才接收流量。
2.keepalive 32 让 nginx 与每个后端保持 32 个空闲长连接,省掉反复握手的开销。单独写这一行不会生效,必须配合后面两个与 Connection 相关的指令,原理见本段之后,proxy_pass http://backend 里写的是组名,不是某个具体的 IP:端口,这是负载均衡和单机反向代理在写法上唯一的区别。proxy_http_version 1.1 是因为长连接需要 HTTP/1.1,默认的 1.0 不支持。proxy_set_header Connection "" 把 Connection 头清空,否则这个逐跳头会把长连接切断。这三行是一个整体:只写 keepalive 32 而不配后两行,长连接不会生效,等于白写。
默认是轮询,什么都不用加,请求按顺序分给每台后端,适合后端配置一致、请求耗时接近的场景。机器性能不一致时用加权轮询,在 server 后面写 weight=3,能力强的多分一些,需要会话保持时可以在 upstream 块内加一行 ip_hash;,让同一个 IP 的请求固定落到同一台。但这只是临时手段,不是最优解:后端水平扩容或缩容时,hash 结果会整体偏移,用户的会话照样丢。更稳的做法是把 session 放到 Redis 之类的共享存储里,让后端本身无状态,这样才能真正自由扩缩,请求耗时差异大、或者长连接很多时,用 least_conn;,把新请求优先给当前连接数最少的机器,比轮询更均衡。
开源版 nginx 只有被动健康检查,靠 max_fails 和 fail_timeout 实现:只有当请求真的失败了,它才发现后端有问题。也就是说,第一批发往故障机器的请求必然失败,之后才会被摘掉,主动健康检查,也就是定期探测 /health 而不依赖用户请求,是 NGINX Plus 的商业功能。开源方案有三条路:换 Tengine(自带 ngx_http_upstream_check_module)、编译第三方模块、或者在业务层自己摘节点。
配套的还有 proxy_next_upstream,默认值是 error timeout,意思是后端连接失败或超时时,自动把这次请求换到下一台,用户侧感知不到。想让它对 500 也重试,需要显式写 proxy_next_upstream error timeout http_500;,但要谨慎,非幂等请求重复提交可能造成重复下单。
bash
sudo nginx -t
sudo systemctl reload nginx给每台后端的首页返回一个能区分自己的标识,例如各自的 hostname,然后连发多次请求,如果按 3:2 的比例交替出现两台主机名,说明加权轮询生效;如果始终是同一台,检查是不是误写了 ip_hash,或者只有一台机器被判定可用。更直接的办法是看各台后端的访问日志,统计单位时间内的请求数是否接近权重比。
for i in $(seq 1 10); do curl -s http://127.0.0.1/; echo; done
—— 评论区 ——