动态可写的四层路由利器ngx_stream_keyval_module

发布于:2025-06-08 ⋅ 阅读:(15) ⋅ 点赞:(0)

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.最佳实践 & 踩坑提示

  1. 合理规划 zone 大小

    • 32 k ≈ 256 条左右键值;按平均键长估算,留 20% 冗余。
  2. 启用持久化

    • 防止 graceful restart 时丢失映射;JSON 文件由 Nginx 生成,不要人工编辑。
  3. 大量写操作

    • 并发 PUT 建议走 单独管理节点,业务节点只读;否则锁竞争可能增大延迟。
  4. 监控指标

    • Plus Dashboard / Prometheus 暴露 keyval_zone.capacityused,提早告警扩容。
  5. 跨机房同步

    • sync 只保证“最终一致”,不要依赖于亚秒级删除。

9.总结

ngx_stream_keyval_module 赋予了 Nginx Stream 网关 “分布式 KV + 热更新路由” 的能力:

  • API 驱动:告别 reload,秒级动态生效
  • 多种匹配:精确 / CIDR / 前缀 三套索引覆盖 80% 以上场景
  • 高可用:持久化 + 超时淘汰 + 节点同步,保障故障恢复
  • 弹性扩展:配合 njs/Lua 可自助注册,实现服务自发现、自愈

如果你的四层代理还在为 “配置变更慢、灰度困难、封禁繁琐” 而头疼,不妨升级到 Nginx Plus,并开启 ngx_stream_keyval_module,让 “Key 动态、Value 即路由” 成为日常运维的标配。