使用 hash $request_uri 实现 url 级一致性哈希负载均衡,因其含完整查询参数、区分大小写且未解码,可精确标识逻辑请求;配合 consistent 参数提升节点伸缩稳定性。

在 Nginx 中使用 hash $request_uri 可以实现基于 URL 的一致性哈希负载均衡,确保相同 URI 的请求始终被转发到同一台后端服务器。这在缓存穿透防护、会话局部性、或后端服务有状态(如依赖本地缓存)的场景中非常实用。
为什么用 hash $request_uri 而不是其他变量
$request_uri 包含原始请求路径和完整查询参数(如 /api/user?id=123&type=detail),区分大小写且不经过解码,能精确标识一次“逻辑请求”。相比 $uri(不含 query string)或 $args(仅参数),它更适合作为 URL 级别的唯一键。
- 若需忽略查询参数做哈希,可改用
hash $uri - 若需标准化大小写,可用
map预处理:map $request_uri $lower_uri { ~^(?<l>.*)$ $l; }</l>,再哈希$lower_uri - 注意:
$request_uri中的空格、特殊字符(如 `%20`)会原样参与哈希,可能影响一致性——建议前端统一编码规范
配置示例:基础 URL 哈希负载均衡
在 upstream 块中启用 hash,并指定 $request_uri:
upstream backend {
hash $request_uri consistent;
server 192.168.1.10:8080;
server 192.168.1.11:8080;
server 192.168.1.12:8080;
}
-
consistent启用一致性哈希,增减节点时仅少量 key 重映射,大幅提升稳定性 - 不加
consistent是普通取模哈希,节点变动会导致大部分请求打到新节点,易引发缓存雪崩 - 所有
server行不能带权重(weight=),否则哈希失效
常见问题与避坑提醒
实际部署中容易忽略几个关键细节:
- HTTPS 下客户端可能带 Host 头但无端口,而 HTTP 请求可能带
:80;若混合协议接入,$request_uri内容一致,不影响哈希结果 - URI 中含未编码空格或中文时,Nginx 默认拒绝(400 Bad Request),应在
server块中配置underscores_in_headers on;并确保客户端编码合规 - 哈希值默认是字符串哈希,对超长 URI(如含大段 base64 参数)性能无明显下降,但建议后端做好长度校验
- 调试时可用
add_header X-Upstream-Hash "$request_uri -> $upstream_addr";查看实际路由结果
进阶技巧:结合 map 实现条件哈希
如需对某些路径(如静态资源)固定分发,其余路径按 URI 哈希,可用 map + 多 upstream:
map $request_uri $backend_group {
~^/static/ static;
~^/api/v2/ api_v2;
default uri_hash;
}
upstream static {
server 192.168.1.20:8080;
}
upstream api_v2 {
hash $request_uri consistent;
server 192.168.1.30:8080;
server 192.168.1.31:8080;
}
upstream uri_hash {
hash $request_uri consistent;
server 192.168.1.40:8080;
server 192.168.1.41:8080;
server 192.168.1.42:8080;
}
- 利用
map提前分流,避免所有流量都走复杂哈希计算 - 每个 upstream 独立维护哈希环,互不影响
- 正则匹配注意顺序,越具体的规则越靠前










