通过hash $remote_addr实现TCP代理的源地址会话保持,即同一客户端IP始终转发至同一后端服务器,适用于Redis、MySQL等无状态协议;nginx stream模块需配置hash $remote_addr consistent并确保获取真实IP。

通过 hash $remote_addr 实现 TCP 代理的源地址会话保持,核心是让同一客户端 IP 的所有连接始终被转发到后端同一台服务器。这在无状态协议(如 Redis、MySQL、自定义 TCP 服务)中很常见,能避免会话不一致或连接复用问题。
前提:使用支持 hash 负载均衡的代理工具
不是所有 TCP 代理都支持基于客户端 IP 的一致性哈希。主流选择是:
-
nginx(1.9.0+,需启用 stream 模块):原生支持
hash $remote_addr用于 TCP/UDP 流量 -
HAProxy:用
balance source或balance hdr(src_ip)(需提取 IP)实现类似效果 -
Envoy / Traefik 等现代代理:需配置对应的负载均衡策略(如
maglev+ 客户端 IP 提取)
本文以 nginx stream 模块为例,因其语法最贴近问题中的写法。
nginx stream 配置示例(关键部分)
在 /etc/nginx/nginx.conf 的顶层(不在 http{} 内),添加:
stream {
upstream backend {
hash $remote_addr consistent; # 启用一致性哈希,防节点增减时大量连接漂移
server 192.168.1.10:6379;
server 192.168.1.11:6379;
server 192.168.1.12:6379;
}
<pre class="brush:php;toolbar:false;">server {
listen 6379;
proxy_pass backend;
proxy_timeout 1s;
proxy_responses 1;
}}
说明:
-
hash $remote_addr consistent表示按客户端真实 IP 做一致性哈希;consistent可选,提升集群扩缩容时的稳定性 - 若客户端经多层 NAT 或 CDN,
$remote_addr是最近一跳代理 IP(如 LB 或网关),此时会话保持对象是该代理而非真实用户——需配合proxy_protocol或真实 IP 透传 - 确保 nginx 编译时启用了
--with-stream模块(多数发行版预编译包已包含)
注意真实客户端 IP 的获取场景
直接用 $remote_addr 成立的前提是:nginx 能拿到真实的客户端 IP。常见例外:
-
前端有 L4 负载均衡(如 AWS NLB、F5):需开启 PROXY protocol,在 upstream server 和 nginx 中双向启用(
proxy_protocol on;),并改用$proxy_protocol_addr - 前端是 L7 代理(如 nginx http 层反向代理 TCP 流量):无法直接透传 TCP 层原始 IP,应避免嵌套;推荐用 stream 模块直连客户端
-
客户端在内网且共用出口 NAT:多个用户共享同一个
$remote_addr,会误绑定到同一后端——此时需更高维度标识(如 TLS SNI、自定义协议头),$remote_addr不适用
验证与调试方法
确认是否生效,可从三方面入手:
-
日志记录:在 stream 块中加
log_format和access_log,输出$remote_addr和$upstream_addr,观察相同 IP 是否总打到同一地址 -
手动测试:用 telnet / nc 多次连接代理端口,配合
ss -tn或netstat查看后端 ESTABLISHED 连接归属 - 哈希分布检查:临时把 upstream server 改为不同端口(如 6379/6380/6381),用脚本批量请求,统计各端口命中次数是否大致均匀(哈希本身不保证绝对均匀,但应无明显倾斜)
不复杂但容易忽略的是:stream 模块的日志和变量作用域与 http 模块分离,调试时别错配上下文。










