haxqer's recent timeline updates
haxqer

haxqer

V2EX member #739641, joined on 2025-03-13 17:51:23 +08:00
Today's activity rank 3601
haxqer's recent replies
@xiaoshouchen

谢谢~ ns 级主要还是靠换了数据结构,加上按需降低精度,不是什么黑科技😂

你说的镜像体积和 RPC 这两个痛点,正好是我做这个的初衷之一:

体积:大部分业务其实只需要国家或省份。country 档转出来只有 1.7 MiB ,region 档 13 MiB ,city 档 74 MiB ,都比原始的 mmdb ( 123 MiB )小。只要国家的话,直接 go:embed 进二进制都没压力:

//go:embed country.geoc
var geoc []byte
db, _ := ip2geo.OpenBytes(geoc)

这样就不用单独打包数据文件了,镜像几乎不会变大。

RPC:一次 RPC 来回至少几十到几百微秒,进程内查一次是几十纳秒,差了三个数量级。文件小了以后,每个服务自己带一份直接查就行,不用再维护一个独立的 IP 服务。

如果确实需要城市级别,可以不打进镜像,改成挂载卷或在 init container 里下载 .geoc 。启动时 mmap 打开只要几十毫秒,更新数据也不用重新发版。
@hzz2
感谢分享~顺手看了下,goip 底层是 maxminddb-golang v1 ,每次 Lookup 进一个带全部语言 map 的结构体。我用同一份 GeoIP2-City 简单压了一下( M3 ,200 万随机 IPv4 ):

goip 这种用法:~2.8 µs/次,每次分配 70 次 / ~2 KB ,8 核总吞吐 ~1.5M/s
ip2geo city 档:~90 ns/次,零分配,8 核 ~48M/s

不过对 HTTP 服务来说,一次请求本身就几十微秒,这点差距基本体现不出来。ip2geo 更适合进程内大批量查询,比如日志清洗、网关里每个请求都要查一次。另外 ip2geo 目前没有代理/VPN 标记字段,Merged-IP 的那部分数据还替代不了。
@hzz2
感谢分享,看了一下 goip ,部署成服务开箱即用挺方便的👍

两者定位稍有不同:ip2geo 是一个 Go 库,在进程内直接调用,适合日志批处理、网关里每个请求都要查一次这类对单次耗时敏感的场景。走 HTTP 的话,网络开销本身就是几十微秒到毫秒级,查询快几十倍基本感觉不到。

如果 goip 底层用的是 geoip2-golang 或 mmdb ,倒是可以试试把 mmdb.Open 换进去当后端,接口差不多,可以直接替换。
@xiaoshouchen

不算空间换时间,其实是反过来的:文件比原版 mmdb 还小。

拿 123 MiB 的 GeoIP2-City 转出来,city 档是 73.7 MiB ,country 档只有 1.7 MiB 。加载方式跟 geoip2-golang 一样,都是 mmap 整个文件,所以占的内存也更少。

快主要靠两点:
1. 查找结构换了。mmdb 用的是按位走的二叉 trie ,一次查询要在内存里随机跳 20 ~ 30 次,缓存命中很差。ip2geo 改成 DXR:先拿 IP 的高 20 位直接定位到一个桶,再在桶里做一次很短的二分,几次访存就够了。
2. 不在查询时解码。mmdb 每查一次都要反序列化一遍 map 结构( geoip2-golang 走的是反射加接口),ip2geo 在构建时就把记录整理成定长字段,查询时直接读。

另外可以按需取精度:只要国家就用 country 档,边界数从几百万降到二十多万,查一次大约 30 ns 。
@shench 暂时没有哦
@hallomou 哈哈哈
@NoDataNoBB 嘿嘿
@sodesga 嘿嘿
@victorkk 可以用 listUserHighlights
About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Privacy   ·   Solana   ·   2863 Online   Highest 6679   ·     Select Language
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.8.5 · 19ms · UTC 13:28 · PVG 21:28 · LAX 06:28 · JFK 09:28
♥ Do have faith in what you're doing.