需用lua替代原生ip_hash,因其不支持子网聚合、nat主机位忽略、ip段排除及ipv4/ipv6统一处理;lua可通过set_by_lua_block自定义ip分组逻辑,配合map和动态proxy_pass实现精细路由控制。

直接在 upstream 块里加 ip_hash 是最简方式,但它的哈希逻辑固定(IPv4 取前三段、IPv6 取前64位),无法按业务需求做 IP 段归类或掩码控制。若需更精细的路由控制——比如“把 192.168.10.0/24 和 192.168.20.0/24 都映射到同一台后端”,就得用 Lua 自定义哈希逻辑。
为什么需要 Lua 替代原生 ip_hash
原生 ip_hash 不支持以下场景:
- 按子网掩码(如 /24、/16)聚合客户端 IP,再哈希
- 忽略内网 NAT 中的主机位(如把 10.0.1.100 和 10.0.1.200 视为同一组)
- 排除特定 IP 段(如运维网段不参与哈希)
- 混合 IPv4/IPv6 统一处理逻辑
基础 Lua 哈希配置步骤
需启用 ngx_http_lua_module(主流编译版通常已内置,Windows 用户需确认):
- 在
http块中定义共享字典(可选,用于缓存计算结果):
lua_shared_dict ip_group_cache 5m; - 用
map+set_by_lua_block提取并标准化 IP:
例如将 IPv4 地址截取前两段(192.168.x.x → 192.168)作为分组键 - 在
upstream外使用hash指令配合 Lua 变量(注意:不能在 upstream 内写 Lua,需靠 proxy_pass 动态拼接或 use upstream name)
典型自定义 IP 段哈希示例
目标:所有 172.16.0.0/12 网段请求固定打到 server A,其余走轮询
- 先在
http块中定义变量:
map $remote_addr $ip_group {
~^172\.16\. $remote_addr;
default "other";
} - 更灵活的做法是用 Lua 提取子网:
set_by_lua_block $ip_subnet {
local ip = ngx.var.remote_addr
if ip and string.match(ip, "^172%.16%.") then
ngx.var.ip_subnet = "172.16"
elseif ip and string.match(ip, "^10%.") then
ngx.var.ip_subnet = "10"
else
ngx.var.ip_subnet = "public"
end
} - 然后在 location 中动态选择 upstream:
proxy_pass http://backend_$ip_subnet;(需提前定义好upstream backend_172.16、backend_10等)
关键注意事项
原生 ip_hash 的限制在 Lua 方案中依然存在,但可绕过:
- 后端列表变动仍会导致映射偏移,除非用
consistent哈希(Nginx 1.19+ 支持hash $variable consistent;) - 真实 IP 必须已正确提取(
set_real_ip_from+real_ip_header配置不可少) - 不要在 Lua 中做耗时操作(如 DNS 查询、Redis 调用),否则拖慢整个请求链路
- IPv6 地址需用
ngx.ipv6:parse()等函数处理,避免字符串匹配出错











