
因为 ip_hash 的设计目标是保证客户端 IP 到后端节点的映射绝对稳定,而 backup 机制依赖动态状态判断(如健康检查失败、节点被标记为 down),二者逻辑根本冲突。
ip_hash 要求路由完全确定且不可变
ip_hash 用客户端真实 IP(默认是 $remote_addr)做哈希计算,结果直接映射到固定一台后端服务器。这个过程不考虑节点是否“可用”,只看 IP 和当前存活节点列表——只要节点在 upstream 中且未被显式标记 down,就参与哈希空间划分。一旦启用 ip_hash,Nginx 就会把所有非 down 的 server 当作“始终在线”来构建哈希表。
- 哈希表大小 = 当前非 down 的 server 数量
- 每个客户端 IP 经哈希后,落在 0 ~ (N−1) 的整数区间,再取模得到具体 server 下标
- 如果某台 server 临时故障但未被手动设为
down,请求仍会发过去,可能失败——这是 ip_hash 的已知行为,不是 bug
backup 参数需要调度器主动绕过它
backup 的作用是在轮询模式下,等所有非 backup 的 server 都被判定为不可用(比如连续 max_fails 次失败、进入 fail_timeout 状态)后,才把流量切过去。这要求调度器能动态识别“主节点失效”,并临时重排请求分发路径。
- 轮询模式可以做到:它维护一个当前可用节点列表,失效后自动跳过
- ip_hash 不行:它的哈希结果和节点索引强绑定,没有“临时跳过某节点再重算”的机制;若强行剔除一个节点,哈希空间就变了,大量已有客户端会突然被重定向到其他机器,session 断开
配置共存会导致语义矛盾甚至报错
Nginx 在解析 upstream 时会做静态校验:
- 旧版本(如 1.18 之前):允许写 backup,但忽略该标记,backup server 仍参与哈希计算,实际无备份效果
- 新版本(1.20+):直接拒绝加载配置,报错类似
"backup is incompatible with ip_hash" - 即使配置通过,只要 upstream 含
ip_hash,所有backup、weight、max_fails等参数都会被静默丢弃
替代方案:想兼顾会话保持与高可用怎么办
不能靠 backup + ip_hash,但有更稳妥的组合方式:
- 用
hash $cookie_sessionid或hash $arg_token替代 ip_hash,配合backup和轮询——把会话标识从 IP 换成业务可控的字段 - 前端加一致性哈希负载均衡器(如 Envoy、Traefik),由它处理故障转移,Nginx 只做透传
- 后端服务自身支持 session 复制或集中存储(Redis),放弃绑定 IP,改用 least_conn 或 round-robin + backup











