ngx_stream_geo_module在传输层实现高性能 IP Region 路由

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

一、模块定位与核心价值

  • 层次:工作在 Stream (TCP/UDP) 层,和 ngx_http_geo_module 的 L7 语义互补。
  • 作用:基于客户端 IP 前缀 / 范围生成一个 Nginx 变量,可在后续 proxy_passmaplimit_connaccess 等指令中使用,实现按国家/IDC/网段做链路分流、限速、灰度等策略。
  • 性能:所有映射在 启动时一次性 构建基于前缀树的内存索引,请求路径 0 拷贝、0 分支,对高并发 TCP 代理几乎无额外开销。官方曾专门修补内存分配失败与未初始化访问导致的极端崩溃问题,稳定性已被大规模生产验证。

二、典型落地场景

场景 变量取值 策略示例
多活 IDC 就近接入 $region = 0/1/2 内网负载均衡:上海联机流量转向 upstream sh_cluster,北京流量转向 bj_cluster
金融合规 IP 白名单 $risk = safe / block 对高风险网段强制断链;安全网段放通 7001 端口
物联网 MQTT Broker $country = CN / EU / US 国别维度限速、强制 TLS、或变更 Keepalive 参数

三、指令语法与参数

指令 语法 关键点
geo geo [$addr] $var { ... } $addr 默认 $remote_addr;可改为 $arg_remote_addr 等任意变量
块参数 default delete include ranges ranges 告诉解析器后续条目是 连续 IP 段,加载更快

最精确匹配优先:IP 与多条记录匹配时,总是返回最长掩码或最窄区段对应的值。

四、核心原理拆解

  1. 字典结构:普通 CIDR 用 二叉前缀树ranges 模式改为 二分查表;两者均在 master 进程启动期构建。
  2. 惰性求值:变量仅在被引用时查表;若未使用则完全不耗 CPU。
  3. 容错策略:当被匹配字符串无法解析为 IP 时,内部自动使用 255.255.255.255,从而命中 default

五、实战配置示例

# 1) 定义国别变量
geo $country {
    default        ZZ;            # 未知
    include        /etc/nginx/geo/qqwry_cn.conf;
    include        /etc/nginx/geo/qqwry_us.conf;
    127.0.0.1/32   CN;            # 内部调试
}

# 2) TCP 代理 MySQL 并按国别限速
stream {
    log_format  geo '$remote_addr [$country] $status $bytes_sent';

    limit_conn_zone $country zone=per_cc:10m;

    upstream mysql_pool { server 10.0.0.10:3306; }

    server {
        listen 3306 reuseport;

        # 每个国家并发连接上限
        limit_conn per_cc 500;

        # 国外连接带宽限速 1MiB/s;国内不限
        if ($country != CN) { set $limit_rate 1m; }

        proxy_pass mysql_pool;
        access_log /var/log/nginx/mysql_geo.log geo;
    }
}

要点说明

  • 使用 include 将百万级 IP 段分散到多文件,热更新时仅改动单文件后 nginx -s reload 即生效。
  • limit_conn_zone 可用 $country 直接做 key,实现跨连接共享的限流。
  • 如果数据库监听 UNIX-Domain Socket,可把 $addr 换为 $proxy_protocol_addr 兼容 PROXY 协议。

六、使用 ranges 优化加载速度

同一国家拥有大量连续 B 段时,将文件预处理为 start-ip end-ip 形式并加 ranges;

geo $country {
    ranges;
    default ZZ;
    include /etc/nginx/geo/ranges_cn.conf;  # 文件已按 IP 升序
}

官方基准:百万级条目从磁盘到常驻内存 < 1 s,极大缩短 CI/CD 发布窗口。

七、与 GeoIP/GeoIP2 模块的差异

特性 stream_geo http_geoip2
数据来源 手工 CIDR / ranges MaxMind MMDB
依赖 无(内置实现) 需第三方动态模块
查询维度 单字段,返回自定义值 多字段(国家、城市、ASN…)
典型用途 内网网段标记、IDC 分组、合规白名单 Web 大区重定向、广告投放、内容分发

当只需 快速分段业务自定义标签 时,stream_geo 更轻量;若需全球精确地理信息,可在上游负载均衡器使用 GeoIP2,再透传结果给 Stream 模块。

八、常见坑与调优

  1. 顺序不当:把 default 写在最前会导致所有连接直接命中默认值,务必放在最后。
  2. 未排序文件 + ranges:加载速度取决于升序排序;乱序会让构建 O(N log N),影响启动。
  3. IPv4 映射 IPv6:升级到 1.25.0-plus 及以上可避免老版本在 ::ffff:1.2.3.4 场景下匹配异常。([mailman.nginx.org][2])
  4. 热更新文件:在外部脚本生成新文件后先 nginx -t,再软链切换,可确保无缝 reload。

九、结语

ngx_stream_geo_module 让我们在 L4 代理 里也能像 HTTP 层一样进行精准的 IP 标签化与策略分发,不必依赖外部防火墙或 GeoIP 数据库,即可轻松实现 IDC 分流、国别限速、风控白名单等高级玩法。理解其 最精确优先匹配惰性求值 原理,再配合 include / ranges 组织大规模网段,就能在保证启动速度与运行性能的同时,做到配置简洁、逻辑清晰、上线安全