14 10 月 2026

单台服务器扛不住的时候,第一反应是升级配置,但升级总有上限。横向扩展(加机器分摊流量)才是终极方案,而 Nginx 就是最常用的流量分发入口。

这篇讲 Nginx 负载均衡的完整配置:upstream 池、负载策略、健康检查、会话保持。配置可直接用于生产。

需要云服务器可以看看财富云计算:一键开服面板,NAT 型无独立公网 IP,价格比独立 IP 机型省一半以上,主流游戏快速部署。

一、负载均衡基础架构

用户 → Nginx(负载均衡器) → 后端1 (10.0.0.1:8080)
                        → 后端2 (10.0.0.2:8080)
                        → 后端3 (10.0.0.3:8080)

Nginx 收请求,按策略分给后端。后端挂了自动剔除,加机器自动加入。

二、upstream 基础配置

http {
    # 定义后端服务器池
    upstream backend {
        server 10.0.0.1:8080 weight=3;    # weight 权重,默认1
        server 10.0.0.2:8080 weight=2;
        server 10.0.0.3:8080 weight=1;
        
        # 备用机: 主都挂了才用
        server 10.0.0.4:8080 backup;
        
        # 标记不参与负载
        server 10.0.0.5:8080 down;
        
        keepalive 32;    # 保持和后端的长连接,减少握手
    }

    server {
        listen 80;
        server_name api.example.com;

        location / {
            proxy_pass http://backend;
            
            # 传递真实 IP 给后端(关键,后面要用)
            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;
            
            # 超时
            proxy_connect_timeout 5s;
            proxy_read_timeout 60s;
            proxy_send_timeout 60s;
        }
    }
}

权重分配场景:后端1 是新机器(8核),后端2 是旧机器(4核)→ weight=2 和 weight=1,按能力分摊。

三、五种负载策略

1. 轮询(默认)

每个请求按顺序分给下一台。适合后端性能一致的场景。

upstream backend {
    server 10.0.0.1:8080;
    server 10.0.0.2:8080;
}

2. 权重轮询

upstream backend {
    server 10.0.0.1:8080 weight=5;
    server 10.0.0.2:8080 weight=1;
}
# 5:1 分配,机器强的多干活

3. IP Hash(会话保持)

同一个 IP 的请求总是落到同一台后端——用户 session 不用共享:

upstream backend {
    ip_hash;
    server 10.0.0.1:8080;
    server 10.0.0.2:8080;
}

适合:后台管理系统、session 存在本地的老应用。缺点:某 IP 流量大会不均;后端增减会重新分配。

4. 最少连接

请求分给当前连接数最少的后端:

upstream backend {
    least_conn;
    server 10.0.0.1:8080;
    server 10.0.0.2:8080;
}

适合:请求处理时间差异大的场景(有的请求 10ms,有的 3s)。

5. 一致性 Hash

upstream backend {
    hash $request_uri consistent;
    server 10.0.0.1:8080;
    server 10.0.0.2:8080;
}

相同 URL 总是落到同一台——缓存命中率最大化。适合图片站、静态内容分发。

策略选择表

场景策略
无状态 API、同配置机器轮询或权重
Session 在本地ip_hash
请求处理时间不均least_conn
缓存型内容(图片)hash consistent
新老机器混用weight

四、健康检查:自动剔除坏后端

Nginx 开源版没有主动健康检查(Plus 版才有),用被动机制 + max_fails:

upstream backend {
    server 10.0.0.1:8080 max_fails=3 fail_timeout=30s;
    server 10.0.0.2:8080 max_fails=3 fail_timeout=30s;
    
    # 被动检查参数:
    # max_fails=3    30 秒内失败 3 次就标记不可用
    # fail_timeout=30s  标记后 30 秒内不再分流量给它,之后试探恢复
}

被动检查的问题:只有真有请求过去才知道它挂了。想要主动检查,两个方案:

方案 A:nginx_upstream_check_module(需重编译 Nginx,淘宝 Tengine 自带)

upstream backend {
    server 10.0.0.1:8080;
    server 10.0.0.2:8080;
    check interval=3000 rise=2 fall=5 timeout=2000 type=http;
    check_http_send "GET /health HTTP/1.0\r\n\r\n";
    check_http_expect_alive http_2xx http_3xx;
}

方案 B:定时任务剔除(不动 Nginx,适合开源版)

#!/bin/bash
# 后端健康检查,挂了就从 upstream 里摘掉
BACKENDS="10.0.0.1:8080 10.0.0.2:8080"
UPSTREAM_CONF="/etc/nginx/conf.d/upstream.conf"

> "$UPSTREAM_CONF.tmp"
echo "upstream backend {" >> "$UPSTREAM_CONF.tmp"
for b in $BACKENDS; do
  ip=${b%:*}; port=${b#*:}
  if curl -sf -m 2 "http://$ip:$port/health" > /dev/null 2>&1; then
    echo "    server $b max_fails=3 fail_timeout=30s;" >> "$UPSTREAM_CONF.tmp"
  else
    echo "    # 剔除: $b 健康检查失败 $(date)" >> "$UPSTREAM_CONF.tmp"
  fi
done
echo "}" >> "$UPSTREAM_CONF.tmp"

if ! diff -q "$UPSTREAM_CONF" "$UPSTREAM_CONF.tmp" > /dev/null; then
  mv "$UPSTREAM_CONF.tmp" "$UPSTREAM_CONF"
  nginx -t && systemctl reload nginx
fi
crontab -e
* * * * * /opt/scripts/upstream-health.sh

后端必须提供 /health 接口(返回 200 即可):

// PHP 版健康检查
<?php
header('Content-Type: application/json');
echo json_encode(['status' => 'ok', 'db' => checkDb()]);

五、会话保持的三种方案

负载均衡后,session 放哪?

方案做法适用
ip_hash同 IP 固定后端快速方案,后端增减有感觉
session 共享Redis/Memcached 存 session最佳实践,推荐
sticky cookieNginx 按 cookie 路由需要三方模块

Redis 共享 session(PHP 示例):

; php.ini
session.save_handler = redis
session.save_path = "tcp://127.0.0.1:6379?auth=Redis密码"

这样任何后端都能读到 session,后端随便加减,用户无感知。

六、完整生产配置模板

# /etc/nginx/conf.d/lb.conf

# 后端池
upstream app_backend {
    least_conn;                          # 按场景换策略
    server 10.0.0.1:8080 weight=2 max_fails=3 fail_timeout=30s;
    server 10.0.0.2:8080 weight=2 max_fails=3 fail_timeout=30s;
    server 10.0.0.3:8080 backup max_fails=3 fail_timeout=30s;
    keepalive 32;
}

# 限流区(防单 IP 打爆后端)
limit_req_zone $binary_remote_addr zone=api:10m rate=50r/s;

server {
    listen 443 ssl http2;
    server_name api.example.com;

    # 访问日志带后端信息(排障要知道请求分给谁了)
    log_format lb '$remote_addr - [$time_local] "$request" '
                  '$status $upstream_addr rt=$request_time '
                  'urt=$upstream_response_time';
    access_log /var/log/nginx/lb.log lb;

    location / {
        limit_req zone=api burst=100 nodelay;
        
        proxy_pass http://app_backend;
        proxy_http_version 1.1;
        proxy_set_header Connection "";               # keepalive 必须
        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;
        
        proxy_connect_timeout 3s;
        proxy_send_timeout 30s;
        proxy_read_timeout 30s;
        
        # 缓冲设置(慢后端不拖垮 Nginx)
        proxy_buffering on;
        proxy_buffer_size 8k;
        proxy_buffers 16 8k;
    }
    
    # 健康检查端点(给上面的健康脚本用)
    location = /lb-status {
        access_log off;
        return 200 "ok";
    }
}

七、排障:看请求分给谁了

# 1. 日志里的 $upstream_addr 显示实际转发目标
tail -f /var/log/nginx/lb.log
# 10.0.0.1:8080: 10.0.0.1:8080, 10.0.0.2:8080  格式: 请求IP: 尝试的后端列表
# 多个地址 = 发生了重试(第一个挂了)

# 2. 502 常见原因
# - 后端全挂(看 upstream_addr 和 error.log)
# - 后端响应太慢(proxy_read_timeout)
# - keepalive 配置不一致导致连接被拒

# 3. 测试各后端直连
curl -I http://10.0.0.1:8080/health
curl -I http://10.0.0.2:8080/health

八、常见问题

Q:502 Bad Gateway?
后端挂了或连不上。看 error.log 里的 upstream 信息,逐个 curl 后端健康接口。

Q:加了 keepalive 反而 502?
proxy_set_header Connection ""; 和 proxy_http_version 1.1; 必须配对设置,缺一个就会出现连接被后端关闭。

Q:后端 IP 变了要改配置?
用 DNS 域名做 upstream(Nginx 会按 resolve 参数定时解析),或者上服务发现(Consul)。小规模直接改配置 reload 也行。

Q:一台 Nginx 会不会成为单点?
会。高可用方案:两台 Nginx + Keepalived 抢 VIP,或者上云厂商的 LB 服务(ALB/CLB),它们自带健康检查和高可用。

Q:游戏服能用 Nginx 负载均衡吗?
不能!游戏服是长连接 UDP/TCP 有状态协议,不是 HTTP。游戏服的「负载均衡」是玩家分流(不同服不同端口),不是 LB。多游戏共存见多游戏共存配置。

Q:NAT 型服务器怎么做多机负载?
NAT 机型之间没有内网,回源要经过公网,延迟和带宽都吃亏。要做 LB,选有内网的产品,或者干脆单机扛 + CDN 分摊。

小结

Nginx 负载均衡核心三件套:upstream 池 + 合理策略 + 健康检查。无状态应用轮询/最少连接,有 session 用 ip_hash 或 Redis 共享,后端必须提供 /health 接口。配好日志里的 $upstream_addr,排障时一眼看出请求分给了谁。

更多网站架构内容见财富云计算知识库:Nginx 配置、反向代理、CDN、性能优化全系列。需要云服务器可以看看财富云计算:一键开服面板,NAT 型无独立公网 IP,价格比独立 IP 机型省一半以上。

延伸阅读
  1. 网站环境怎么选:LNMP 手动 / 宝塔面板 / Docker / 云开发
  2. 问卷表单系统搭建:SurveyKing / Formbricks,数据自己收
  3. 图床搭建:Lsky Pro / Lychee,自托管图片托管方案
  4. 邮件列表搭建:listmonk 自托管 Newsletter,把读者握在自己手里
  5. 文档站点搭建:MkDocs / VuePress / Docsify,产品文档与技术手册方案
  6. RSS 阅读器搭建:FreshRSS 自托管,把信息流拿回自己手里
  7. 短链接服务搭建:Shlink 自托管,链接数据自己掌握
  8. 网站状态监控页搭建:Uptime Kuma,服务器挂了第一时间知道
更多阅读
  1. 上一篇:Docker 部署网站实战:Nginx + MySQL + PHP 一套带走
  2. 下一篇:网站 SEO 基础优化指南:让搜索引擎收录并排上来
发表评论 3

取消回复