使用 gorilla/mux 的 matcherfunc 结合 ip 地理定位库(如 ip2region 或 geoip2)可轻量实现按国家路由,需准确提取真实 ip、预加载地理库、避免 http 调用,并区分场景选库。

直接用 gorilla/mux 的 MatcherFunc + IP 地理定位库(如 ip2region 或 geoip2)就能实现,不需要重写整个网关或引入复杂服务发现。
用 MatcherFunc 注入地理匹配逻辑
gorilla/mux 不提供开箱即用的“按国家路由”功能,但它的 MatcherFunc 允许你在匹配阶段任意读取请求、调用定位逻辑、返回布尔结果。这是最轻量且可控的方式。
- 匹配发生在路由选择早期,早于中间件,因此可避免无效转发
- 必须确保定位逻辑快(
ip2region内存加载后单次查询 ≈ 0.1ms;geoip2依赖磁盘 I/O,需预加载*geoip2.Reader到全局变量) - 不要在
MatcherFunc里做 HTTP 调用(比如请求 ipstack API),否则会拖慢所有路由匹配 - 示例:只对
cn用户启用促销页路由
router.HandleFunc("/promo", promoHandler).MatcherFunc(func(r *http.Request, rm *mux.RouteMatch) bool {
ip := getRealIP(r) // 见下节
country, _ := geoLocator.GetCountry(ip)
return country == "CN"
})
准确提取客户端真实 IP 是前提
多数线上服务跑在 Nginx / ELB / CDN 后,r.RemoteAddr 是上游代理地址,不是用户真实 IP。必须解析 X-Forwarded-For 或 X-Real-IP,且要防伪造。
当代理已经知道网站路由或内容URL,并且在启动前需要有效的sitemap XML、sitemap索引或robots.txt引用时,请使用sitemap。这是一个发布构件技能,而不是爬虫或SEO平台。
- 优先信任你控制的最外层代理设置的
X-Real-IP(更安全,不可被客户端篡改) - 若只能依赖
X-Forwarded-For,需限定可信跳数,例如只取第一个非私有 IP:strings.Split(r.Header.Get("X-Forwarded-For"), ",")[0]不够健壮,应过滤掉10.0.0.0/8、172.16.0.0/12、192.168.0.0/16等内网段 - 建议封装为独立函数
getRealIP(*http.Request),并在测试中覆盖代理链场景
选 ip2region 还是 geoip2?看你的部署约束
两者都可用,但适用场景差异明显,选错会导致性能或维护问题。
-
ip2region:纯 Go 实现,db 文件小(~4MB),支持内存 mmap 加载,无 CGO 依赖,适合容器化部署和 CI 构建。缺点是数据更新频率低(靠社区维护),城市级精度一般 -
geoip2:MaxMind 官方库,数据权威、更新勤(每月)、支持 ASN/时区/威胁评分等扩展字段。但需cgo编译,db 文件大(~100MB),首次加载慢,不适合无 cgo 环境(如某些 Alpine 镜像) - 如果只是做国家/省份分流,
ip2region足够且更稳;如果要做合规(GDPR)、反欺诈或货币切换,geoip2的额外字段价值更高
避免把地理路由和动态规则混在一起
有人试图把“Header['X-Env'] == 'prod' && Country == 'US'”这种条件塞进 govaluate 表达式引擎,这容易出问题。
- 地理信息是请求级上下文,不是静态配置项;每次都要实时查,不能缓存在规则引擎的表达式 AST 中
- govaluate 不支持自定义函数注入(除非自己 patch),硬塞会导致表达式难维护、类型不安全、无法单元测试
- 正确做法:先用
MatcherFunc或中间件完成地理判定,把结果(如country=CN)写入context.Context;后续规则引擎只消费这个已计算好的值 - 这样既保持路由层轻量,又让策略层专注逻辑组合
真正麻烦的不是怎么写代码,而是地理库的数据时效性、IP 源头可信度、以及不同 CDN 对 X-Forwarded-For 的处理差异——这些不会报编译错误,但会让路由在灰度发布时突然失效。










