1.模块价值概述
传统 Stream 反向代理想要按域名 / IP / 前缀做动态转发,通常需要:
- 修改 nginx.conf → reload
- 借助外部配置中心或自研 Lua 共享字典
这在 大规模集群 或 服务动态上下线 场景下并不优雅。
ngx_stream_keyval_module
通过 API 可写的 Key-Value 数据库 + $variable
动态查询,让我们在 不 reload、秒级生效 的前提下完成四层路由、灰度、熔断等操作。
2.核心概念
概念 | 说明 |
---|---|
keyval_zone | 定义一块共享内存(zone)存放 K-V 数据;可持久化到 JSON 文件 |
keyval | 声明「用某个 key 查询 zone,结果赋给 $variable 」的语义 |
API | /api/6/http/keyvals/<zone> (HTTP 块开启 api write=on );支持 PUT/GET/DELETE |
type | 指定匹配规则:string 精确、ip 子网包含、prefix 前缀匹配 |
timeout / sync | 键值过期 & 多节点一致性(Nginx Plus Cluster / DNS 发现) |
3.最小可运行示例
3.1 配置文件
# http 块开放 API
http {
server {
listen 8080;
location /api {
api write=on; # 允许写操作
}
}
}
# stream 块按 SNI 动态转发
stream {
# ① 申请 32KB 共享内存;JSON 持久化到本地
keyval_zone zone=sni2up:32k state=/var/lib/nginx/state/sni_up.keyval;
# ② $ssl_server_name 查询 zone,结果写入 $up
keyval $ssl_server_name $up zone=sni2up;
server {
listen 443 ssl;
ssl_certificate /etc/nginx/cert.pem;
ssl_certificate_key /etc/nginx/key.pem;
proxy_pass $up; # 动态 upstream
}
}
3.2 在线修改路由
# 新增 / 更新
curl -X PUT 'http://localhost:8080/api/6/http/keyvals/sni2up' \
-d '{"a.example.com":"10.0.0.1:8443", "b.example.com":"10.0.0.2:9443"}'
# 查询
curl http://localhost:8080/api/6/http/keyvals/sni2up
# 删除条目
curl -X DELETE 'http://localhost:8080/api/6/http/keyvals/sni2up/b.example.com'
无需 nginx -s reload
,客户端下一个 TLS 握手立即生效。
4.多种匹配模式
4.1 IP 子网匹配(type=ip)
keyval_zone zone=ip2dst:128k type=ip;
keyval $remote_addr $dst zone=ip2dst;
server {
listen 3306;
proxy_pass $dst; # 针对不同运营商或 VPC 子网分库
}
# 运营商 A 网段
{"203.0.113.0/24":"db-a.internal:3306"}
# 精确 IP
{"203.0.113.10/32":"db-vip.internal:3306"}
匹配原则:先找精确 IP,其次子网;同级取最先写入的键。
4.2 前缀匹配(type=prefix)
keyval_zone zone=pfx:64k type=prefix;
keyval $ssl_server_name $backend zone=pfx;
- 写入
api.
→ 命中api.foo.com
- 写入
cdn.
→ 命中cdn.img.foo.com
适合 多层子域 共用同一后端的场景。
5.过期与同步
场景 | 设置 | 行为 |
---|---|---|
自动下线 | timeout=10m |
条目 10 分钟后失效并删除 |
集群一致 | timeout=10m sync |
仅在目标节点删除,其余节点等待超时淘汰;配合 DNS/RR 调度 |
注意:
sync
需启用 Nginx Plus 进程集群通信(同一 Anycast VIP 或 DNS Service)。
6.结合 njs 实现自助写入
js_import kv.lua; # 伪代码示例
keyval_zone zone=dyn:32k;
keyval $remote_addr $route zone=dyn;
server {
listen 2525;
js_access kv.put_route; # 在 access 阶段更新路由
proxy_pass $route;
}
kv.put_route
里通过 $control
变量调用 API 或 ngx.fetch()
将新键写入 dyn
,实现“客户端首次访问即注册目标”的自发现能力。
7.实战案例
场景 | 玩法 | 效益 |
---|---|---|
动态 SNI 反向代理 | 域名 → 内网 IP 的灰度、回滚 | SaaS 平台一键切流 |
数据库主从切换 | $remote_addr → 新主库 |
可编排自动故障转移 |
封禁/优选 IP | IPCIDR → deny /proxy_pass |
实时风控,0 reload |
AB 灰度/蓝绿 | 前缀匹配 Header | 支撑万级域名不改配置 |
8.最佳实践 & 踩坑提示
合理规划 zone 大小
- 32 k ≈ 256 条左右键值;按平均键长估算,留 20% 冗余。
启用持久化
- 防止 graceful restart 时丢失映射;JSON 文件由 Nginx 生成,不要人工编辑。
大量写操作
- 并发 PUT 建议走 单独管理节点,业务节点只读;否则锁竞争可能增大延迟。
监控指标
- Plus Dashboard / Prometheus 暴露
keyval_zone.capacity
、used
,提早告警扩容。
- Plus Dashboard / Prometheus 暴露
跨机房同步
sync
只保证“最终一致”,不要依赖于亚秒级删除。
9.总结
ngx_stream_keyval_module
赋予了 Nginx Stream 网关 “分布式 KV + 热更新路由” 的能力:
- API 驱动:告别 reload,秒级动态生效
- 多种匹配:精确 / CIDR / 前缀 三套索引覆盖 80% 以上场景
- 高可用:持久化 + 超时淘汰 + 节点同步,保障故障恢复
- 弹性扩展:配合 njs/Lua 可自助注册,实现服务自发现、自愈
如果你的四层代理还在为 “配置变更慢、灰度困难、封禁繁琐” 而头疼,不妨升级到 Nginx Plus,并开启 ngx_stream_keyval_module
,让 “Key 动态、Value 即路由” 成为日常运维的标配。