ngx_stream_geo_module是Nginx在Stream(四层)场景下提供的地理信息映射模块,它允许我们在TCP或UDP连接刚建立时,就根据源IP地址得出一个自定义变量,从而用于访问控制、流量调度或日志记录。与HTTP层的geo模块不同,流模块工作在更底层的网络栈,不解析应用协议,因此性能极高且通用性强。

模块原理与指令结构
ngx_stream_geo_module的核心思想是先定义一份IP到值的对照表,然后将其绑定到一个变量上。当新连接到来,Nginx会依据客户端地址在对照表中查找,命中后把对应值赋给变量。查找过程基于 Radix 树或哈希结构,时间复杂度接近O(1),即便配置了成千上万条规则也不会明显拖慢握手。
在配置中,我们使用geo指令(注意这是Stream上下文里的geo,不是HTTP里的同名指令)来声明。它必须写在stream块内,而不能放在http或server中。基本语法为:geo $变量名 源IP变量 { ... },其中源IP变量通常为$remote_addr。模块还支持ranges参数,用来显式声明后续条目按区间书写,提升大段IP的录入效率。
除了普通CIDR写法,该模块允许用default定义未命中时的返回值,用proxy设置受信任代理的地址以便正确获取真实客户端IP。如果前端有CDN或负载均衡,必须配置proxy并配合set_real_ip_from类似逻辑(Stream中靠geo的proxy参数)才能避免地理判断失效。
stream {
geo $client_region $remote_addr {
default unknown;
proxy 10.0.0.0/8;
127.0.0.1 local;
192.168.1.0/24 internal;
203.0.113.0/24 overseas;
}
}
与map模块配合实现分流
单独拿到地域变量往往不够,我们还需要把它转换成具体的上游动作,例如选择后端集群。这时候就要用到Stream里的map指令。map可以根据$client_region的值映射出另一个变量,比如$upstream_pool,再在upstream块或proxy_pass中引用。
一个常见误区是认为geo能直接写条件跳转,其实它只负责“打标签”。所有路由逻辑应交由map或if(谨慎使用)处理。由于Stream阶段没有HTTP的if作用域限制,我们可以在server块中通过变量名动态决定proxy_pass的目标,实现就近接入。
下面示例展示了如何依据地理变量把流量导到不同上游组。注意map必须定义在stream块顶层,且变量在使用前已存在。这种写法比在应用代码里查库更省资源,也更稳定。
stream {
geo $client_region $remote_addr {
default unknown;
10.0.0.0/8 internal;
172.16.0.0/12 lab;
}
map $client_region $backend_addr {
internal unix:/var/run/internal.sock;
lab 127.0.0.1:9001;
default 127.0.0.1:9000;
}
server {
listen 53 udp;
proxy_pass $backend_addr;
}
}
生产环境避坑与性能调优
不少团队在迁移HTTP GeoIP到Stream时发现规则不生效,多半是因为IPv6客户端进来后,$remote_addr变成了IPv6格式,而旧规则只写了IPv4段。ngx_stream_geo_module完全支持IPv6,但需要显式添加::/0之类的条目,否则默认走default。建议在配置里同时覆盖双栈地址,或者使用ranges方式统管。
另一个隐藏问题是变量作用域。Stream里的geo变量仅在当前连接生命周期有效,不能被不同server块“共享状态”,如果需要跨连接统计,要借助limit_conn或外部模块。此外,当规则文件非常大时,可用include拆分到独立.conf,通过geo块内部的include指令加载,保持主配置清爽。
从性能看,该模块本身开销极小,真正的成本在正则或复杂map上。若地域映射后还要做大量字符串匹配,应考虑把结果缓存到lua共享内存(需OpenResty)或减少map分支。以下为带IPv6与文件引入的较完整范例,展示如何兼顾可读与效率。
stream {
geo $client_geo $remote_addr {
ranges;
default 0;
127.0.0.1 1;
10.0.0.0-10.255.255.255 1;
::1 1;
2001:db8::/32 2;
include /etc/nginx/stream_geo_ext.conf;
}
map $client_geo $route {
1 internal_backend;
2 overseas_backend;
0 default_backend;
}
upstream internal_backend { server 10.0.0.5:3306; }
upstream overseas_backend { server 192.168.0.1:3306; }
upstream default_backend { server 127.0.0.1:3306; }
server {
listen 3306;
proxy_pass $route;
}
}
通过上述结构,我们能在不改动业务代码的前提下,完成基于物理位置的透明分流。对于需要合规隔离或降低跨国延迟的系统,ngx_stream_geo_module提供了一种轻量且原生的高性能方案。
ngx_stream_geo_moduleNginx_streamIP地理映射修改时间:2026-08-15 15:52:16