优化 nginx ip_hash 策略导致的负载不均,核心思路是放弃单一的全局哈希,改用基于上游服务器权重的动态哈希或一致性哈希算法。具体做法是在 upstream 块中启用 hash $request_uri consistent;,利用 ketama 算法让请求更均匀地分布到不同节点,同时配合 least_conn 或 weight 参数来平衡后端服务器的处理能力差异。

为什么 ip_hash 会导致负载不均
ip_hash 根据客户端 IP 的哈希值固定分配给某台服务器,当存在大量来自同一网段的用户(如公司内网、校园网)时,单台服务器会承受不成比例的高流量。此外,如果后端服务器性能不一致,静态的哈希映射无法自动调整权重,导致高性能服务器闲置而低性能服务器过载。

upstream backend {
# 使用一致性哈希,key 为请求 URI
hash $request_uri consistent;
server 192.168.1.10:8080 weight=3;
server 192.168.1.11:8080 weight=1;
server 192.168.1.12:8080 weight=1;
}
上述配置中,consistent 关键字启用了 Ketama 一致性哈希算法。相比传统的 ip_hash,它在服务器增减时只有少量请求需要重新路由,且通过 weight 参数可以明确指定每台服务器的承载能力。例如,192.168.1.10 的性能是其他两台的两倍,因此设置 weight=3,其余设为 1。这种组合既保证了会话粘性(相同 URI 总是去同一台机器),又实现了基于性能的负载均衡。
如何结合 least_conn 解决热点请求问题
即使使用了加权哈希,某些特定资源(如大文件下载)仍可能因请求时长过长而导致连接堆积。此时应引入 least_conn 策略,优先将新请求分配给当前活跃连接数最少的服务器。在 Nginx 1.11.5 之后,可以将 hash 与 least_conn 结合使用,实现更精细的控制。

upstream backend_optimized {
# 先按 least_conn 选择服务器组,再在组内用 hash 保证粘性
least_conn;
hash $request_uri consistent;
server 192.168.1.10:8080 weight=3 max_fails=3 fail_timeout=30s;
server 192.168.1.11:8080 weight=1 max_fails=3 fail_timeout=30s;
server 192.168.1.12:8080 weight=1 max_fails=3 fail_timeout=30s;
}
该配置首先通过 least_conn 确保长连接请求不会集中在某一台机器上,因为 least_conn 会实时计算每台服务器的当前连接数。随后,hash $request_uri consistent 确保同一个用户的重复请求尽量落在同一台服务器上,以利用本地缓存。同时,max_fails 和 fail_timeout 参数增加了健康检查机制,当某台服务器连续失败 3 次后,将在 30 秒内被暂时剔除,避免流量打到故障节点。
基于程序中某个输入、条件或结果的具体问题
在使用 hash $request_uri consistent 时,如果请求 URI 中包含动态查询参数(如时间戳或随机数),会导致哈希值剧烈波动,破坏会话粘性。例如,用户刷新页面时,URL 中的 ?t=123456 变化会使请求被分发到不同的后端服务器,导致缓存失效和状态丢失。
# 错误示例:包含动态参数的 URI
location /api/data {
proxy_pass http://backend_optimized;
}
# 正确示例:过滤掉动态参数或使用固定字段
map $request_uri $stable_uri {
~^(?<base>.*)\?.* $base;
default $request_uri;
}
upstream backend_stable {
hash $stable_uri consistent;
server 192.168.1.10:8080;
server 192.168.1.11:8080;
}
为解决此问题,可以使用 map 指令提取 URI 的基础路径,忽略查询字符串中的动态部分。如上所示,$stable_uri 变量通过正则表达式去除了 ? 之后的所有内容,确保哈希计算基于稳定的资源路径。这样,即使用户携带不同的查询参数访问同一接口,请求也会被哈希到相同的后端服务器,从而维持会话一致性和缓存命中率。


















