负载均衡策略:ip_hash / hash 会话保持
1. 为何需要会话保持?
在经典的轮询(Round Robin)负载均衡策略下,来自同一客户端的请求可能会被分发到不同的后端服务器上。这适用于AKF扩展立方体中的X轴水平扩展场景。然而,若要实现Z轴扩展,即确保特定用户或客户端的请求总是被转发到同一台上游服务器,从而实现会话(Session)的持久化,就需要借助哈希算法。Nginx提供了ip_hash和hash两种负载均衡策略来满足这一需求。

2. 核心算法解析
在 upstream 模块中,ip_hash和hash是实现会话保持的关键指令:
- ip_hash:根据客户端的IP地址进行哈希计算以决定路由。默认使用
$remote_addr变量作为关键字,IPv4地址取前3个字节,IPv6则使用完整地址。该算法不受服务器权重(weight)影响。ip_hash模块默认编译进Nginx核心。
- hash:根据任意指定的字符串或变量进行哈希计算。你可以基于URL参数、Cookie值等自定义关键字,该算法同样不受服务器权重影响。
hash模块也默认编译进Nginx。
# 基于客户端IP地址哈希的负载均衡方法。
# Syntax: ip_hash;
# Default: —
# Context: upstream
# 基于指定键值哈希的负载均衡方法。
# Syntax: hash key [consistent];
# Default: —
# Context: upstream
关键前提:获取真实客户端IP
在实际生产环境中,Nginx前方通常存在CDN、负载均衡器等代理链。为了确保ip_hash基于真实的客户端IP进行计算,必须正确配置以提取原始IP。这依赖于 http_realip_module 模块,它默认未编译,需要通过 --with-http_realip_module 选项启用。
核心配置指令包括:
set_real_ip_from:指定可信的代理服务器IP段。
real_ip_header:指定包含真实IP的HTTP头部字段(如 X-Forwarded-For)。
real_ip_recursive:启用递归查找,跳过所有可信代理IP,取第一个不可信IP作为真实客户端IP。
关于Nginx处理请求的十一个阶段及realip模块的详细实践,可深入阅读 网络/系统 相关文章进行系统学习。若未启用该模块,配置时可能会遇到 unknown directive "set_real_ip_from" 错误。
3. ip_hash 负载均衡实践
环境准备
创建两个测试后端服务器:
- 服务器A (10.20.172.213):监听 8010 端口。
- 服务器B (10.20.172.214):监听 8011 和 8012 端口。
配置示例 (/usr/local/nginx/conf.d/server.conf):
# 在 10.20.172.214 主机上配置
server {
listen 8011;
return 200 'From 8011 server response!\n';
}
server {
listen 8012;
return 200 'From 8012 server response!\n';
}
# 在 10.20.172.213 主机上配置
server {
listen 8010;
return 200 'From 10.20.172.213:8010 server response!\n';
}
Nginx 反向代理配置
编辑代理配置文件 (proxy_server_iphash.conf),启用 ip_hash:
upstream backend {
ip_hash; # 启用IP哈希负载均衡
server 127.0.0.1:8011 weight=2; # 权重设置对ip_hash无效
server 127.0.0.1:8012;
server 10.20.172.213:8010 max_fails=2 fail_timeout=10s;
keepalive 10;
}
server {
listen 80;
server_name test.weiyigeek.top;
# 从代理链中提取真实客户端IP,这是ip_hash生效的关键
set_real_ip_from 10.20.172.0/24;
real_ip_recursive on;
real_ip_header X-Forwarded-For;
location / {
proxy_pass http://backend;
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_http_version 1.1;
proxy_set_header Connection "";
}
}
验证结果
重启Nginx服务后,使用curl进行测试:
# 同一客户端IP多次请求
for i in $(seq 1 5); do curl http://test.weiyigeek.top; done
# 输出始终指向同一台后端服务器(例如 8011)
# 模拟不同客户端IP
for i in $(seq 1 5); do curl -H 'X-Forwarded-For: 100.10.2.215' http://test.weiyigeek.top; done
for i in $(seq 1 5); do curl -H 'X-Forwarded-For: 100.10.12.215' http://test.weiyigeek.top; done
# 不同IP可能被哈希到不同的后端服务器

结论:ip_hash确保了同一客户端IP的所有请求都落在固定的后端服务器上,完美实现了会话保持,且不同IP的请求被均匀(哈希)分布到不同后端。
4. hash 负载均衡实践
Nginx 配置调整
将负载均衡策略改为基于URL参数的hash算法:
upstream backend {
hash $arg_user; # 基于请求参数 `user` 的值进行哈希计算
server 127.0.0.1:8011 weight=2; # 权重设置对hash同样无效
server 127.0.0.1:8012;
server 10.20.172.213:8010 max_fails=2 fail_timeout=10s;
keepalive 10;
}
# server 部分配置与上文相同,可省略提取真实IP的配置,因为hash不依赖IP。
验证结果
# 相同user参数请求
for i in $(seq 1 5); do curl http://test.weiyigeek.top?user=weiyigeek; done
# 输出始终指向同一台后端服务器
# 不同user参数请求,即使客户端IP相同
for i in $(seq 1 5); do curl -H 'X-Forwarded-For: 100.10.2.215' http://test.weiyigeek.top?user=WeiyiGeek; done
for i in $(seq 1 5); do curl -H 'X-Forwarded-For: 100.10.2.215' http://test.weiyigeek.top?user=Geek; done
# user值不同,请求被路由到不同的后端服务器

结论:hash算法基于自定义的关键字(如$arg_user)实现会话保持。只要关键字不变,请求就会固定转发到特定后端,完全不受客户端IP和服务器权重影响,灵活性远高于ip_hash。
5. 总结与注意事项
通过上述实践,我们验证了ip_hash和hash算法都能有效实现会话保持,是构建AKF扩展立方体中Z轴扩展(数据分区)的基础。hash算法因其可定制性,应用场景更为广泛。
重要警告:
使用标准哈希算法时,切忌直接从上upstream配置中移除宕机或下线的后端服务器。因为节点列表的改变会导致整个哈希环(Hash Ring)发生变化,引发大规模的路由重映射。这意味着大量用户的会话会因路由到新服务器而中断,缓存也可能全面失效,对线上服务是灾难性的。这正是 运维/DevOps 工作中需要密切关注的容量管理与变更风险。
为了解决节点动态增减带来的问题,Nginx的hash指令支持consistent参数,用于启用一致性哈希算法。它能极大程度地缓解节点变动带来的影响,是生产环境更推荐的选择。在后续的 数据库/中间件 优化专题中,我们将深入探讨一致性哈希的原理与配置实践。