GO DNS 原理解析

遇到了域名解析超时的问题,顺便梳理下 GO 的 DNS 解析流程。出错的域名是这个 p.qpic.cn, 当用 sudo tcpdump port 53 -nn 分析解析过程发现,ipv4 解析过程非常快,ipv6 很慢,过了一段时间才有响应。也就是说 ipv6 解析慢,导致 go http client dns 解析超时。

可以用 dig 确认下

发现解析的非常快,发现返回的都是 A 记录,也就是 ipv4 的结果,显示的指定查询 ipv6 结果,可以这样

发现,超时解析失败了。

go 进行 dns 查询有两种实现方式, cgo 和 纯 go 的实现。推荐纯 go 的实现。 cgo 在查询时,会占用系统的线程,而 纯 go 的 是使用 goroutine 进行查询的。

下面一步步分析 dns 解析流程。 测试代码如下:

使用 go build net.go 会生成 net 执行文件。使用 sudo strace -T ./net 跟踪执行流程。

会发现有两次 connect 操作, 一次是 4.99 秒后超时了,一次成功返回了。从 go 源码分析下。

net/lookup.go 中,看到 LookupHost 调用的是 DefaultResolver 的 LookupHost。DefaultResolver 是 Resolver 默认实现实例,在相同的文件里可以找到 func (r *Resolver) LookupHost(ctx context.Context, host string) (addrs []string, err error) 定义。

这个函数首先对 host 进行分析,如果 host 直接 是 IP, 不管是 ipv4 或者 ipv6 形式,就直接返回了。否则继续查询。

后面具体的实现是和系统相关的。在 net/lookup_unix.go 中找到 func (r *Resolver) lookupHost(ctx context.Context, host string) (addrs []string, err error) 。我们是找纯 go 的实现,直接找 goLookupHostOrder 定义。在 net/dnsclient_unix.go 可以找到。

goLookupHostOrder 首先是查找 /etc/hosts 里有没有域名的配置,如果有则直接返回,否则继续查找。

继续在本文件找到 goLookupIPCNAMEOrder 实现。

在这里加载了 /etc/resolv.conf 内容,在这个配置里,包含 dns server 地址及查询超时的具体控制。具体内容参考 resolv.conf 。这个配置会加载到 dnsConfig 中。加载的文件在 net/dnsconfig_unix.go 中,参考函数 dnsReadConfig。

在这里有两个参数比较重要, 一个 timeout 查询 dns server 的超时时间,attempts 指的是重试次数。timeout 默认是 5s, strace 跟踪的超时时间得到验证。这两个选项可以在 /etc/resolv.conf 自定义。

我们设置了超时时间为 2s, 重试次数为 1 次。再次使用 strace 进行跟踪的话,可以看到超时时间变成了 2s 左右。

继续来看 goLookupIPCNAMEOrder 的实现。

这里启动了两个 goroutine,分别取查询 ipv4 和 ipv6。然后通过 channel 进行结果的汇总。那也就解释了 就算 ipv4 查询很快,ipv6 查询很慢的话,照样会卡主应用,造成 dns 查询超时。

tryOneName 函数就是 dns 查询的具体逻辑。

如果配置了多个 name server, 那么是通过轮询的方式查询,还是每次都从第一个 server 进行查询。选项 rotate 控制的。配置了,就是轮询,否则就是从第一个开始来。

这里有两层循环,第一层就是 attempts 控制重试次数,第二层就是遍历 name server,如果查到了就返回,失败了就轮询下一个。

总结

如果遇到了 dns 问题,具体怎么做呢?

  1. 直接配置映射关系, 放到 /etc/hosts 中。如果 ip, host 关系固定,好说,否则,就无法实现
  2. 修改 go 源码,控制不解析 ipv6, 小场景下,合适,但不是长期方案
  3. 精心配置 /etc/resolv.conf,尤其是与应用设置的超时时间对应起来,timeout, attempts, rotate 都要考虑好。name server 可以选不同的服务商,保证可用性。
此条目发表在GO分类目录。将固定链接加入收藏夹。

GO DNS 原理解析》有 1 条评论

发表评论

电子邮件地址不会被公开。