一、模块定位与核心价值
- 层次:工作在 Stream (TCP/UDP) 层,和
ngx_http_geo_module
的 L7 语义互补。 - 作用:基于客户端 IP 前缀 / 范围生成一个 Nginx 变量,可在后续
proxy_pass
、map
、limit_conn
、access
等指令中使用,实现按国家/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 与多条记录匹配时,总是返回最长掩码或最窄区段对应的值。
四、核心原理拆解
- 字典结构:普通 CIDR 用 二叉前缀树,
ranges
模式改为 二分查表;两者均在 master 进程启动期构建。 - 惰性求值:变量仅在被引用时查表;若未使用则完全不耗 CPU。
- 容错策略:当被匹配字符串无法解析为 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 模块。
八、常见坑与调优
- 顺序不当:把
default
写在最前会导致所有连接直接命中默认值,务必放在最后。 - 未排序文件 + ranges:加载速度取决于升序排序;乱序会让构建 O(N log N),影响启动。
- IPv4 映射 IPv6:升级到 1.25.0-plus 及以上可避免老版本在
::ffff:1.2.3.4
场景下匹配异常。([mailman.nginx.org][2]) - 热更新文件:在外部脚本生成新文件后先
nginx -t
,再软链切换,可确保无缝 reload。
九、结语
ngx_stream_geo_module
让我们在 L4 代理 里也能像 HTTP 层一样进行精准的 IP 标签化与策略分发,不必依赖外部防火墙或 GeoIP 数据库,即可轻松实现 IDC 分流、国别限速、风控白名单等高级玩法。理解其 最精确优先匹配 与 惰性求值 原理,再配合 include
/ ranges
组织大规模网段,就能在保证启动速度与运行性能的同时,做到配置简洁、逻辑清晰、上线安全。