最稳的ip地理围栏方案是用oschwald/maxminddb-golang读取geolite2-city.mmdb,配合cf-connecting-ip或nginx可信ip头获取真实ip,白名单用map、围栏用s2库做点面判断,高实时场景选tile38。

用 maxminddb 查 IP 归属做围栏,别碰 geoip2
Go 官方不带 GeoIP 支持,必须靠第三方库读取 GeoLite2-City.mmdb。最稳的选择是 oschwald/maxminddb-golang——纯 Go 实现、零 C 依赖、内存映射友好,适合高并发服务。geoip2 库已归档,它只是对 maxminddb 的一层封装,字段访问绕、更新停滞,还容易因嵌套结构解错字段。
- 下载数据库到本地,比如
/etc/geoip/GeoLite2-City.mmdb,别放进 Git 或临时目录 - 全局复用一个
*maxminddb.Reader,别每次请求都调maxminddb.Open(),否则文件句柄和 mmap 开销会拖垮性能 - 定义 record 结构体时,只声明你要的字段,比如:
type CityRecord struct { Country struct { IsoCode string `maxminddb:"iso_code"` } `maxminddb:"country"` City struct { Names map[string]string `maxminddb:"names"` } `maxminddb:"city"` } - 查 IPv6 也一样:传
net.ParseIP("2001:db8::1"),不是字符串;net.ParseIP对 v4/v6 都兼容
HTTP 中间件里做地理拦截,真实 IP 怎么拿才靠谱
地理围栏拦错人,90% 出在 IP 拿错了。别信 X-Forwarded-For,它能被客户端伪造。真正可信的来源,取决于你前面有没有 Nginx、Cloudflare 或 ALB。
- 如果用了 Cloudflare,优先读
CF-Connecting-IP头;Nginx 配了set_real_ip_from+real_ip_recursive on,再用req.RemoteAddr才可靠 - 中间件里别写
strings.Split(req.Header.Get("X-Forwarded-For"), ",")[0]—— 这等于开门揖盗 - 查库前加超时控制:
ctx, cancel := context.WithTimeout(r.Context(), 100*time.Millisecond),避免磁盘卡顿或损坏 DB 拖死整个 handler - 白名单匹配建议用
map[string]bool{"CN": true, "JP": true},O(1) 查,别用 slice 遍历
用 S2 做点在多边形内判断,比手写 PIP 快一个数量级
IP 归属只能到城市/国家,真要判断“用户是否在某个园区、商圈、机场跑道内”,得上经纬度 + 多边形围栏。这时候 S2 库(如 github.com/golang/geo/s2)比自己实现光线投射或射线法快得多,且精度可控、支持层级索引。
-
S2把球面转成 Hilbert 曲线上的整数 cell ID,围栏预处理成s2.CellUnion,查询就是一次整数包含判断 - 别直接把 GeoJSON 多边形顶点喂给
s2.LatLonRect——它只支持矩形;要用s2.Polygon+s2.Loop构建闭合环 - 实时性要求高的场景(比如 Uber 那种万级围栏+QPS 上万),建议把围栏预加载进内存,并用
sync.Map缓存cellID → fenceName映射 - 注意:S2 默认单位是度 × 1e7 的 int32,别传 float64 经纬度进去就完事,先用
s2.LatLngFromDegrees(lat, lng)
用 Tile38 做实时地理围栏,别当纯数据库用
Tile38 是 Go 写的内存优先地理空间服务器,原生支持 NEARBY、FENCE、Webhook 回调,适合需要“进入/离开某区域立刻通知”的场景,比如电动车入电子围栏报警、快递员抵达门店自动打卡。
- 启动后默认监听
:9851,用curl -X POST http://localhost:9851/commands或redis-cli -p 9851都能操作,协议完全兼容 RESP - 存点别只用
SET,带OBJECT字段才能后续做属性过滤,例如:SETHOOK mywebhook http://myapi/fence-event FENCE NEARBY fleet POINT 33.462 -112.268 6000
- 它不校验 GeoJSON 有效性,
LOAD时出错只会静默失败——先用jq或在线验证器确认格式合法 - 数据落盘靠
AOF,别关掉;但别指望它替代 PostgreSQL + PostGIS 做复杂空间分析,它的强项是快、轻、实时
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











