net.parseip仅做ip格式校验和标准化,不查归属地;查归属地需用ip2region等第三方库或http api,并注意ipv6处理、超时控制、降级策略等生产细节。

用 net.ParseIP 解析字符串 IP,但别指望它返回归属地
直接说结论:net.ParseIP 只做格式校验和标准化,不查归属地。它把 "192.168.1.1"、"2001:db8::1" 转成 net.IP 类型,仅此而已。想查“这个 IP 在哪”,必须调用外部服务或加载本地数据库。
- 常见错误:以为
net.ParseIP("8.8.8.8").String()会返回带国家信息的字符串——实际只是还原为标准格式(如"8.8.8.8"或"2001:4860:4860::8888") - 使用场景:验证用户输入是否为合法 IP、做 ACL 判断、构造 TCP 地址前缀等
- 注意
net.ParseIP对 IPv6 带括号写法(如"[::1]")不支持,需先去掉括号再解析
查归属地得靠第三方库或 HTTP API,ip2region 是轻量首选
Go 生态里没有官方归属地库,ip2region(Go 版本)是目前最常用的选择:二进制文件小(几 MB)、查询快(微秒级)、纯内存加载、无依赖。
- 别用
github.com/oschwald/maxminddb-golang直接读 GeoLite2——它需要额外下载 .mmdb 文件,且 Go 的 mmap 实现对大文件在某些容器环境有兼容问题 -
ip2region的XdbSearcher必须预加载索引(searcher.LoadBytes()或searcher.LoadFile()),否则每次查都 panic - 查询返回的是字符串,如
"中国|0|北京市|市辖区|联通",字段顺序固定,用strings.SplitN(..., "|", 5)拆即可
用 HTTP API 查归属地时,http.Client 超时和重试必须手动控制
调用 ip-api.com 或 ip.sb 这类公开接口看似简单,但生产环境极易翻车。
- 默认
http.DefaultClient没设超时,DNS 卡住或对方响应慢会导致 goroutine 积压 - 必须显式设置:
&http.Client{Timeout: 3 * time.Second},并搭配context.WithTimeout - 免费 API 通常限流(如 ip-api.com 每分钟 45 次),没做错误重试或降级(比如 fallback 到本地
ip2region)会直接丢数据 - 别硬编码 URL,把域名和路径抽成配置项,方便后续切私有服务
IPv6 地址查归属地更麻烦,net.IP.To16() 不等于可查地址
IPv6 地址常以压缩形式传入(如 "::1"),但归属地库只认完整 128 位格式。直接传 net.ParseIP("::1") 给 ip2region 会查不到结果。
- 必须先转成标准格式:
ip := net.ParseIP(v).To16(),再确认ip != nil才能继续 - 有些 IPv6 是内网地址(如
fd00::/8)或链路本地(fe80::/10),这类地址本身无地理意义,查出来也是空或“保留地址” - 如果业务允许,建议统一转成 IPv4 映射(如
::ffff:192.168.1.1),再取后 4 字节查——但要注意这仅适用于 IPv4-mapped IPv6 场景
真正难的不是写几行代码,而是决定:什么 IP 必须查、查不到怎么兜底、缓存策略怎么定、IPv6 和私有地址怎么分类处理。这些没想清楚,库换十次也没用。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











