作者丨小楼
最近,我在DNS解析中遇到了一个小陷阱。 虽然问题解决了,但是排查过程却相当曲折。 到最后,我还是没有弄清楚一件事。 我想和大家分享一下整个过程。
背景
我负责的服务最近需要更换机器。 很多朋友可能不知道什么是换机,因为平时接触不到。 下面我简单解释一下什么是换机以及为什么需要换机。
通俗地说,机器更换就是更换机器,将服务从一台机器迁移到另一台机器。
为什么要换机? 表面原因可能是机器硬件有故障或者机器已经过保修期。
有的朋友可能要问,我在公司也负责很多服务,为什么我从来没有换过机器呢? 原因可能是使用了容器,并不是直接部署在物理机上,更换机器的任务转移到了云平台的运维人员身上; 也可能是你有专门的运维来帮忙处理这件事。 对于开发者来说,几乎是透明的。
为什么我负责的服务需要更换? 因为机器已经过保修期了。 为什么服务部署在物理机上? 因为它是一项基础服务,所以它不同于一般服务。 它有一些限制,只能部署在物理机上。 为什么没有运维人员帮忙? 因为公司很多基础服务都是自运和维护的,所以开发者既做开发,又做运维。
说完换机,我们再来说说这个基础服务。 它是一个用 Go 编写的服务,不断发送 HTTP 请求。 只要记住这一点,其他的都不重要。
更换该服务的机器后,HTTP 请求时间慢了很多。 如下图所示,黄色机器为旧机,蓝色机器为新机。 该指标的值是HTTP请求时间(毫秒),大约相差1.5倍。 这就是我今天要分享的问题。 接下来我就讲一下我的故障排除过程。
故障排除
遇到这种情况,首先检查机器的各项指标,如CPU、网络情况等,看是否有异常情况,并确认是否受到其他指标的影响。 但看了一圈,发现新机的各项指标甚至比旧机还要好。
然后我就询问提供机器的同学,看看机器有没有什么异常,结果没有。
由于HTTP请求变慢了,我想到看看请求的哪一部分变慢了。 使用以下命令来测试它。 我就用百度的域名代替域名:
卷曲 -o /dev/null -s -w %{}::%{}::%{}"\n"
这里每个参数代表的含义(也可以是其他一些参数):
这样就可以看到域名解析、连接、传输各个阶段的时间消耗。 比较新旧机器。 如果有一项特别高,那么这一项肯定有问题。
只需算一下:
虽然从这次的测试数据来看,新机的DNS解析似乎慢了一些,但如果仔细看这个值,它对请求整体耗时几乎没有影响。 经过多次测试,我们发现两台机器的DNS解析其实是差不多的。
但我还是没有信心。 验证DNS是否有问题,然后使用dig命令重试。
挖
执行的时候明显感觉卡住了,确定是DNS有问题。
问题解决了
起初,我在网上搜索了慢速DNS相关的文章,找到了一篇文章《 DNS in Go net 》,但稍微验证了一下,与我的案例无关。 这篇文章是一篇好文章。 所以我也贴了一个链接,有兴趣的可以看一下。
“解决 Go 网络库中的 DNS 问题”
然后我就去找网络群里的同学。 网络群里的同学一看,就知道原因了。 他们说新机器还没有安装。 这意味着什么? 不要惊慌,先上网查一下,再接电话。
提供DNS缓存和DHCP服务功能。 作为域名解析服务器 (DNS),您可以通过缓存 DNS 请求来提高访问 URL 的连接速度。 作为DHCP服务器,它可以用来分配内网IP地址,并为局域网计算机提供路由。 DNS和DHCP两种功能可以同时实现,也可以分别实现。 轻量级且易于配置,适合个人用户或主机数量少于50台的网络。 此外,它还配备了 PXE 服务器。
简单来说,它起到DNS缓存的作用,提高DNS查询速度。
说到这里,我补充一点知识。 我一直以为DNS会被操作系统缓存。 不知道大家是否有这样的错觉,但实际上,Linux下如果没有特殊处理,每次DNS解析都要查询DNS服务器。 这是一个很好的证明。 您可以尝试使用DNS捕获包。 我当时也尝试过,每次都会远程获取DNS解析结果。 这个结论也可以在《TCP/IP详解卷一》中的相关描述中找到:
只有较新的Linux系统才可以在客户端缓存DNS,而且Linux系统需要手动开启,所以默认情况下必须远程获取DNS缓存。
言归正传,网络组的同学说要么安装一台,要么更改DNS服务器的配置,也就是/etc/.conf文件。 由于机器上已经有服务,因此他们选择了更改配置的更安全的方法。
更改之前,/etc/.conf的第一行是127.0.0.1,这意味着本地机器也被用作DNS服务器,但实际上本地DNS服务并没有启用。 网络群的同学说可以通过配置或者安装第一行来去掉。 。
首先把127.0.0.1的配置去掉,结果还是一样!
然后添加127.0.0.1的配置并安装,时间消耗减少了。
整个解决过程中,程序没有重启,唯一的变量是安装了,所以这肯定是DNS的错。
问题反思
虽然问题解决了,但我还是有几个疑问:
为什么配置了127.0.0.1的DNS却没有开启?
为什么删除 127.0.0.1 配置没有效果?
第一个问题比较容易弄清楚。 我问了系统部的同学,他说本来应该是启用的,但是出了点小错误,只配置了127.0.0.1。
我们再来看第二个问题。 DNS本地缓存和远程查询差距这么大吗? 据网络群的同学介绍,DNS是公司搭建的。 内网传输其实并不慢,使用dig很容易测试。 使用第2行和第3行的DNS进行测试时,发现dig的速度非常快。
挖@主机
为什么127.0.0.1的配置变得这么慢? 我们先从我的一些猜测开始,一一证明。 不过在猜测之前,我们先来了解一下Go程序解析DNS的过程。
Go的DNS解析流程
Go的DNS解析分为两种:
由于需要适配各种平台,所以每个平台都有相应的实现。
这部分代码位于net包下。 追踪它非常简单。 编写代码建立连接,一步步调试,找到域名解析的地方。
我直接从.go文件的方法告诉你吧。 当然,这仅适用于Unix系统,包括Mac和Linux。 然而,Mac 不使用纯 Go 代码。 被迫使用cgo。 在Linux上,没有特殊配置,使用纯Go代码。 Go实现的DNS解析,以下代码以Linux为例:
func (r *Resolver) lookupIP(ctx context.Context, network, host string) (addrs []IPAddr, err error) {
// ①强制走纯Go的DNS解析器
if r.preferGo() {
return r.goLookupIP(ctx, host)
}
// ②根据解析顺序解析
order := systemConf().hostLookupOrder(r, host)
if order == hostLookupCgo {
if addrs, err, ok := cgoLookupIP(ctx, network, host); ok {
return addrs, err
}
// cgo not available (or netgo); fall back to Go's DNS resolver
// ③如果cgo搞不定,降级到先文件再DNS
order = hostLookupFilesDNS
}
ips, _, err := r.goLookupIPCNAMEOrder(ctx, host, order)
return ips, err
}
以下是以下类型的订单:
hostLookupCgo hostLookupOrder = iota // cgo
hostLookupFilesDNS // 文件优先
hostLookupDNSFiles // DNS优先
hostLookupFiles // 只查文件
hostLookupDNS // 只查DNS
这里的文件是/etc/hosts,最终被调用了,但是这个方法的代码太长,所以这里只讲一下大概的流程:
如果需要先查询hosts文件,就先检查一下,找到就直接返回。
读取/etc/.conf文件,取出DNS配置,每5秒更新一次
构造 DNS 请求并将其发送到服务器。 UDP 读取的默认超时时间为 5 秒,可以在 /etc/.conf 文件中配置。 同一域名的不同类型(如ipv4、ipv6)的查询可以配置为并行或串行。 好的
轮询机制用于向 DNS 发送请求。 如果其中一个请求失败,则将推迟到下一个请求。 默认重试次数为2次,可以在/etc/.conf文件中配置。
最后,解析并返回查询结果。 如果结果为空,且配置了hosts文件,则查询一次该文件。
好了,流程就简单介绍到这里,接下来我就验证一下我的一些猜测。
猜测1:Go在程序启动时只读取一次/etc/.conf文件吗?
这个猜想的依据是,如果查询DNS时得到的是127.0.0.1的DNS,而本地没有启用DNS服务,可能会很慢,而且如果修改了配置文件,如果Go程序只读取文件一旦在初始化的时候,那么自然改变配置文件就没有效果了。
但这种情况并非如此。 如上所述,Go 每 5 秒延迟更新一次 DNS 配置文件。
func (conf *resolverConfig) tryUpdate(name string) {
// 初始化,只做一次
conf.initOnce.Do(conf.init)
// ...
now := time.Now()
if conf.lastChecked.After(now.Add(-5 * time.Second)) {
return
}
conf.lastChecked = now
// ...
dnsConf := dnsReadConfig(name)
conf.mu.Lock()
conf.dnsConfig = dnsConf
conf.mu.Unlock()
}
我做了一个实验,写了一个DNS解析的测试代码。 我在配置了 127.0.0.1 但未打开的服务器上运行它。 我抓到了127.0.0.1 53端口(DNS默认端口)的数据包,发现有流量。 ,然后修改/etc/.conf配置,去掉127.0.0.1,发现无法捕获127.0.0.1端口53的流量。 这证明与代码逻辑是一致的,这个猜想不成立。
猜想二:远程DNS查询比本地慢很多
这个很容易证明,还是用上面的程序
在没有 127.0.0.1 配置的服务器上运行
在配置并启用 127.0.0.1 的服务器上运行
结果两者的耗时几乎是一样的,甚至在配置了127.0.0.1但未开启的服务器上耗时也基本相同。
这说明无论怎么查询DNS,都不慢。
猜想三:是否是并发太高导致的?
为什么我会有这个猜想呢? 首先,在线QPS在50左右,与上面测试的场景不同。 其次,我看到了上面代码中的锁。 并发高后,锁带来的开销增加?
我写了一段100并发的代码来查询DNS,发现这段代码在以下三种场景下花费的时间差不多:
没有配置 127.0.0.1 的服务器
有一个服务器已配置并在 127.0.0.1 开启
配置了127.0.0.1但未开启的服务器
同时我也问过网络群里的一个同学,他说DNS可以承受百万QPS,对服务器没有压力。
终于
写到最后,我emo~虽然问题已经解决了,但是我还是不知道为什么当时DNS查询慢。 如果您看完文章知道问题出在哪里,或者有什么更好的排查方法,欢迎大家一起讨论。 反正我是没法再查了。
最后我想说,写文章很辛苦,需要一些鼓励。 请点赞、阅读并关注。 下一期再见。
1、
2、
3.
4.
5.
扫一扫在手机端查看
-
Tags : dns linux dns配置 域名解析
我们凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设、网站改版、域名注册、主机空间、手机网站建设、网站备案等方面的需求,请立即点击咨询我们或拨打咨询热线: 13761152229,我们会详细为你一一解答你心中的疑难。