net.LookupIP 是最快上手的 DNS A 记录查询方式,底层调用系统解析器,需传纯域名、判空遍历;手动发包用 miekg/dns 可控性强但需设超时、用正确 Qtype 和 FQDN;并发查 DNS 易因系统锁变慢,建议换上游或加缓存。

用 net.LookupIP 做最简 DNS A 记录查询
直接调 net.LookupIP 是最快上手的方式,适合查域名对应 IPv4/IPv6 地址。它底层走系统解析器(/etc/resolv.conf),不发原始 DNS 包,所以快、稳、兼容性好。
常见错误现象:lookup example.com: no such host —— 很可能只是本地 DNS 配置问题,不是代码写错了;或者传了带 http:// 前缀的字符串,得先用 strings.TrimPrefix 清掉。
- 只传纯域名,比如
"google.com",别传"https://google.com" - 返回的是
[]*net.IPAddr,遍历时注意判空,否则 panic - 默认查 A 和 AAAA,如果只要 IPv4,后续用
ip.To4() != nil过滤
ips, err := net.LookupIP("github.com")
if err != nil {
log.Fatal(err)
}
for _, ip := range ips {
fmt.Println(ip.IP.String())
}手动发 DNS 查询包:用 github.com/miekg/dns
需要控制查询类型(比如查 TXT、MX、NS)、指定上游服务器、看原始响应头或 EDNS 信息时,就得绕过系统解析器,自己组包发请求。
容易踩的坑:dns.Client 默认不设超时,遇到无响应 DNS 服务器会卡死;另外,dns.Question 的 Qtype 必须用 dns.TypeA 这类常量,不能写数字 1 或字符串 "A",否则查不到。
立即学习“go语言免费学习笔记(深入)”;
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
- 务必设置
client.Timeout = 3 * time.Second - 目标服务器地址格式是
"8.8.8.8:53",端口不能省 - 查
CNAME时,响应里的Answer可能为空,要检查Extra或递归查Auth段
c := new(dns.Client)
m := new(dns.Msg)
m.SetQuestion(dns.Fqdn("cloudflare.com"), dns.TypeTXT)
r, _, err := c.Exchange(m, "1.1.1.1:53")查 SRV 记录时为什么总返回空?
net.LookupSRV 看似简单,但实际使用中几乎必踩一个点:它要求服务名和服务协议拼成 "_service._proto" 格式,并且必须带结尾点(FQDN)。漏点、大小写错、顺序颠倒都会导致查不到。
典型错误:net.LookupSRV("xmpp-client", "tcp", "gmail.com") 返回空——因为没加结尾点,正确是 "gmail.com."(注意末尾的点)。
- 服务名和协议名都必须小写,比如
"_sip._tcp",不能写"SIP" - 第三个参数必须是完整 FQDN,即带末尾点,如
"example.com." - 返回的
target字段也可能是相对域名,需手动补全父域
并发查多个域名时性能突然变差
用 net.LookupIP 并发 100 个域名,可能比串行还慢——因为所有请求共享同一个系统解析器锁(glibc 的 getaddrinfo 在某些系统上是全局锁),尤其在 macOS 或旧版 Linux 上明显。
解决方案不是硬上 goroutine,而是换路径:要么用 miekg/dns 自己连不同上游(避开系统栈),要么加缓存 + 限流。别指望靠开更多 goroutine 解决。
- 单机并发查 DNS 超过 20–30 路,建议切到
miekg/dns+ 固定几台上游(如1.1.1.1,8.8.8.8) - 如果只是做批量域名健康检查,优先用
net.DialTimeout直连 443 端口,比查 DNS 更反映真实可达性 - 缓存 TTL 不要盲目信响应里的
Header.Rcode,有些 DNS 服务器会忽略它,得结合本地时间戳做软过期
DNS 查询看着简单,但每个环节都依赖外部状态:系统配置、上游响应、域名本身是否带重定向、甚至 Go 版本对 net 包的调度策略。写工具时,别急着封装,先在 main 里跑通一两个 case,把错误路径打出来看一眼再抽象。

















