Apache原生不支持ip_hash指令,需通过RemoteIP获取真实IP、stickysession绑定ROUTEID、mod_rewrite按IP哈希映射route,并用mod_headers注入Cookie,实现等效IP Hash绑定。

要让 Apache 的 mod_proxy_balancer 实现基于源 IP 哈希的资源访问绑定,核心是启用 IP Hash 调度算法(即 lbmethod=bytraffic 不适用,必须用 ip_hash),并确保后端服务能稳定响应、客户端 IP 不被中间设备篡改。这不是默认行为,需显式配置且依赖正确模块支持。
必须启用的模块
Apache 2.4 默认不加载 mod_lbmethod_byrequests 等调度算法模块,而 ip_hash 并非内置在 mod_proxy_balancer 中——它实际由独立模块 mod_lbmethod_byrequests 提供(注意:官方文档中并无 mod_lbmethod_iphash 模块,所谓“IP Hash”在 Apache 中是通过 ProxySet lbmethod=byrequests + stickysession 配合实现逻辑等效,或更准确地说:Apache 原生不支持标准 IP Hash,但可通过 stickysession + 客户端 IP 提取模拟)。
真正可行的做法是:
- 使用
mod_remoteip获取真实客户端 IP - 配置
stickysession关联该 IP(需后端配合或 Apache 自行注入) - 或借助
mod_headers+mod_rewrite提取并哈希 IP,再映射到固定 route
但最直接、生产环境广泛采用的方式是:
用
stickysession绑定客户端 IP 字符串本身作为会话标识,强制同一 IP 总落到同一节点
步骤一:启用必要模块(缺一不可)
确认以下模块已加载(Ubuntu/Debian 可用 a2enmod,RHEL/CentOS 编辑 /etc/httpd/conf.modules.d/00-proxy.conf):
LoadModule proxy_module modules/mod_proxy.so LoadModule proxy_http_module modules/mod_proxy_http.so LoadModule proxy_balancer_module modules/mod_proxy_balancer.so LoadModule remoteip_module modules/mod_remoteip.so # 获取真实IP必需 LoadModule headers_module modules/mod_headers.so # 可选,用于注入X-Forwarded-For
验证命令:
httpd -M | grep -E "(proxy|remoteip|headers)"
输出应含 proxy_module, proxy_balancer_module, remoteip_module 等。
步骤二:配置 RemoteIP 识别真实源 IP
若前端有 Nginx、CDN 或云 WAF,客户端真实 IP 通常在 X-Forwarded-For 头里。需让 Apache 解析它:
RemoteIPHeader X-Forwarded-For RemoteIPInternalProxy 192.168.1.0/24 # 内网代理IP段,按实际填写 # 若前端是单台机器,可写具体IP如 192.168.1.5
这样 REMOTE_ADDR 就变成真实用户 IP,后续 stickysession 才能可靠工作。
步骤三:定义带 IP 绑定的 balancer 组
<proxy>
BalancerMember http://192.168.1.10:8080 route=node1 loadfactor=1
BalancerMember http://192.168.1.11:8080 route=node2 loadfactor=1
BalancerMember http://192.168.1.12:8080 route=node3 loadfactor=1
ProxySet lbmethod=byrequests
ProxySet stickysession=ROUTEID
</proxy>
关键点:
-
stickysession=ROUTEID表示 Apache 会查找请求中名为ROUTEID的 Cookie 或 URL 参数,并据此路由 - 但我们希望“自动按 IP 绑定”,所以需让 Apache 自己生成 ROUTEID = hash(IP),这需配合重写规则
步骤四:自动为每个 IP 分配固定 route(无需后端改代码)
在 或主配置中添加:
<ifmodule mod_rewrite.c>
RewriteEngine On
# 提取 REMOTE_ADDR,转成小写并哈希(取前4位作route)
RewriteCond %{REMOTE_ADDR} ^([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})$
RewriteRule ^ - [E=CLIENT_IP:%1]
# 使用简单的模运算映射到 node1/node2/node3(3台后端)
RewriteCond %{ENV:CLIENT_IP} ^(.+)$
RewriteCond %{ENV:CLIENT_IP} ^(.+)\.([0-9]+)\.([0-9]+)\.([0-9]+)$
RewriteRule ^ - [E=IP_HASH:%4] # 取最后1段,简单分组
RewriteCond %{ENV:IP_HASH} ^([0-2])$
RewriteRule ^ - [E=ROUTE:node%1]
RewriteCond %{ENV:IP_HASH} ^([3-5])$
RewriteRule ^ - [E=ROUTE:node%1]
RewriteCond %{ENV:IP_HASH} ^([6-9]|1[0-9]|2[0-5])$
RewriteRule ^ - [E=ROUTE:node1]
RewriteCond %{ENV:IP_HASH} ^2[6-9]|3[0-9]|4[0-9]|5[0-9]|6[0-3])$
RewriteRule ^ - [E=ROUTE:node2]
RewriteCond %{ENV:IP_HASH} ^6[4-9]|7[0-9]|8[0-9]|9[0-9]|1[0-9][0-9]|2[0-5][0-5])$
RewriteRule ^ - [E=ROUTE:node3]
</ifmodule>
# 把 route 注入到 Cookie(首次访问时设置)
Header always append Set-Cookie "ROUTEID=%{ROUTE}e; Path=/; HttpOnly; Max-Age=31536000" env=ROUTE
这样,每个客户端 IP 首次访问就会被分配一个固定 ROUTEID(如 node1),后续请求携带该 Cookie,stickysession=ROUTEID 即可确保始终打到同一台后端。
✅ 效果等同于 IP Hash:相同 IP → 相同 route → 相同后端节点
❌ 不依赖后端生成 session ID,也不要求应用层改代码
补充:验证与调试技巧
- 查看
balancer-manager页面(需放行/balancer-manager并启用mod_status)观察各节点请求分布是否均匀且稳定 - 在日志中加入
%{REMOTE_ADDR}e和%{ROUTE}e字段,确认 IP 与 route 映射关系 - 用
curl -H "X-Forwarded-For: 1.2.3.4" http://yourdomain/模拟不同 IP 测试绑定效果
Apache 本身没有原生 ip_hash 指令(不像 Nginx),但通过 RemoteIP + stickysession + mod_rewrite/mod_headers 的组合,完全可以实现稳定、可预测、基于源 IP 的资源访问绑定。关键是把 IP 映射逻辑前置到 Apache 层,而不是交给后端或依赖不稳定的 Cookie 生成机制。











