应直接替换ip_hash为hash $remote_addr consistent;,因其设计目标是会话稳定而非流量均匀;若前端有CDN/WAF/NAT,需用real_ip模块提取真实IP;还可按业务分层分流或改用纯轮询配合健康检查与监控。

直接换掉 ip_hash,而不是试图调它。它的设计目标是会话稳定,不是流量均匀;所谓“不均”,其实是它在真实网络环境下的正常表现。
确认是不是真有问题
先看日志里 $remote_addr 是不是用户真实 IP:如果前面有 CDN、WAF 或 NAT 网关,Nginx 收到的其实是中间设备的 IP,所有请求自然全打到一台后端。加一句 log_format 验证:
log_format main '$remote_addr - $http_x_forwarded_for - $request';
- 若 $remote_addr 是一堆重复的公网 IP(比如 120.236.10.1),说明被 NAT 或代理覆盖了
- 若 $http_x_forwarded_for 里有真实 IP,就得配合 real_ip 模块提取
- 如果 upstream 里配置了 weight、backup、max_fails,ip_hash 会自动失效,退化成轮询
用 hash + consistent 替代 ip_hash
这是最直接有效的升级方式。它保留“同一客户端固定后端”的好处,又大幅降低节点变动或 IP 集中带来的抖动:
- 配置写法:hash $remote_addr consistent;
- 想更均衡可加虚拟节点数:hash $remote_addr consistent=1000;
- 支持组合字段增强区分度,比如:hash "$remote_addr$http_user_agent" consistent;
按业务分层分流
单一策略扛不住异构流量。把不同类型的请求拆开处理,比硬撑一个 ip_hash 更可靠:
- 登录用户用 cookie 哈希:hash $cookie_sessionid;
- API 请求按路径分离:map $request_uri $upstream_group { ~^/api/ "api_backend"; ~^/static/ "static_backend"; }
- 爬虫或低优先级流量单独指向冗余节点,避免冲击主集群
后端配合无状态设计
如果服务本身已支持 Redis 共享 Session 或 JWT 无状态鉴权,就彻底放弃任何基于客户端标识的粘性策略:
- 改用纯轮询或加权轮询,天然均匀
- 健康检查开启 max_fails/fail_timeout,自动剔除异常节点
- 加 Prometheus 监控各节点 request_rate,设置倾斜告警(例如某台 QPS 超均值 2.5 倍)


















