net.LookupIP仅支持DNS解析,无法获取IP归属地;归属地需依赖纯真库(QQWry.dat)、GeoLite2或在线API(如ipinfo.io),并注意编码处理、IPv4校验、超时重试及输入输出格式兼容性。

用 net.LookupIP 只能查 DNS,查不了归属地
很多人一上来就试 net.LookupIP("baidu.com"),拿到 IP 就以为能继续查归属地——不行。net.LookupIP 只做 DNS 解析,不带任何地理位置信息。归属地需要额外数据源,比如纯真 IP 库、GeoLite2 或在线 API(如 ipapi.co、ipinfo.io)。本地查快但需维护数据文件,在线查省事但依赖网络和配额。
加载纯真 IP 库(QQWry.dat)要处理编码和二进制格式
纯真库是 GB18030 编码的二进制文件,Go 原生不直接支持解析。得用第三方包,比如 github.com/oschwald/maxminddb-golang 不适用,它只读 mmdb;得选 github.com/axgle/mahonia 处理编码 + 手动解析偏移结构,或更稳妥用 github.com/ie310muu/qqwry-go(已适配 Go module 且处理了边界情况)。
实操建议:
- 下载最新
qqwry.dat到项目目录(如./data/qqwry.dat),别用网上过期的镜像 - 初始化时用
qqwry.NewQQWry("./data/qqwry.dat"),检查返回 error,文件损坏会导致后续所有查询 panic - 查 IPv4 用
FindIP("114.114.114.114"),IPv6 不支持——纯真库本身就不含 IPv6 数据
调用在线 API 要控制超时和错误重试
用 http.Client 直接 GET https://ipinfo.io/1.1.1.1?token=xxx 很简单,但线上环境容易出问题:DNS 失败、连接超时、API 限流返回 429、JSON 解析失败。不能裸写 http.Get。
立即学习“go语言免费学习笔记(深入)”;
关键配置:
- 设置
http.Client.Timeout = 5 * time.Second,避免卡住 goroutine - 用
context.WithTimeout包裹请求,比仅设 client timeout 更可靠 - 对 429 或 5xx 响应,最多重试 1 次,且加
time.Sleep(100 * time.Millisecond)避免雪崩 - 响应体必须用
defer resp.Body.Close(),否则 fd 泄露
示例片段:
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() req, _ := http.NewRequestWithContext(ctx, "GET", "https://ipinfo.io/1.1.1.1?token="+token, nil) resp, err := http.DefaultClient.Do(req)
命令行参数和输出格式要兼顾调试和管道消费
用户可能想查单个 IP:./iploc 114.114.114.114,也可能批量处理:cat ips.txt | ./iploc -f -。硬编码输入方式会让人皱眉。
建议做法:
- 用
flag.String("f", "", "input file path, use '-' for stdin") - 输出默认为人类可读格式(如
114.114.114.114\t中国|北京|北京|DNS服务器|CN),加-jsonflag 切换结构化输出 - 查不到时输出空行或
{"ip":"...","error":"not found"},别 panic,方便上游脚本判断
别忽略 IPv4 地址合法性校验——net.ParseIP 对 "127.0.0.1abc" 也返回非 nil,得配合 ip.To4() != nil 二次确认。
纯真库的日期字段藏在文件头里,但很多 Go 解析器根本不读;在线 API 的 ASN 信息各平台字段名不一致(org vs asn.organization),这些细节不验证,上线后第一波查询就会暴露。










