引言
“从浏览器的地址栏输入 URL,到网页渲染完成,这中间发生了什么?” 相信这是很多人面试时被问过的问题,也是后来作为面试官问过别人的问题。笔者实习面试时也被问过这个问题。并且在工作之后,发现这的确是一个很好的问题。对网络相关知识理解的越深,能往这个问题的回答里填充越多的细节。本文主要介绍这个问题的回答中涉及的第一个流程: DNS(Domain Name System)
DNS 服务器
“从浏览器的地址栏输入 URL,到网页渲染完成,这中间发生了什么?”
这个问题最简单的回答,我理解是:浏览器向网站背后的服务器发送请求,服务器返回数据给浏览器。如下图所示:
接下来,我们开始再往里填充细节。
我们知道域名其实只是一个方便人们记忆的标志符,网络请求实际需要使用的是服务器的 IP 地址。因此我们在进行网络请求前,需要先找到域名对应的 IP 地址。这里就涉及到 DNS 查询。浏览器通过向 DNS 服务器发起查询请求,得到域名的 IP 地址。
要请求 DNS 服务器,我们同样也要知道它的 IP 地址。这个 IP 地址系统网络可能会自动选择,也可以由我们手动指定。在网络上可以搜索到几个有名的 DNS 服务器地址。比如 8.8.8.8(Google 的),1.1.1.1(CloudFlare 的)。
那么 DNS 服务器又是如何知道域名对应的 IP,返回给客户端的呢?是DNS 服务器保存了所有的域名和 IP 的对应关系吗?考虑到互联网上的域名浩瀚如烟,且域名对应的 IP 地址时有变化。显然不可能在一个 DNS 服务器上存下所有对应关系。
因此被浏览器所请求的 DNS 服务器背后还有一套查询流程,来获取域名的 IP 地址。下面就展开说明,完整的 DNS 查询过程。
DNS 查询过程
整体流程
上图展示了一次完整的 DNS 查询流程。总共有八步:
-
浏览器向 DNS 递归服务器 (DNS Recursive Server) 发起一个递归查询(recursive query) ,询问
www.google.com的地址。DNS 递归服务器接收到请求后,如果发现查询域名不在缓存中,则会开始后续的迭代查询(iterative query) 。 -
DNS 递归服务器首先向根域名服务器(Root Nameserver) 询问
www.google.com的 IP 地址。 -
根域名服务器不知道
www.google.com域名的 IP 地址,但知道.com域名服务器的地址。因此返回了.com域名服务器的 IP 地址。告诉请求方可以询问这个地址。 -
DNS 递归服务器接着请求
.com域名服务器,这类服务器也被称为顶级域名服务器(Top Level Domain Nameserver, TLD Namserver) 。 -
.com顶级域名服务器也不知道www.google.com的 IP 地址。但它知道google.com域名服务器的 IP 地址,因此返回了对应 IP 地址。告诉请求方可以询问这个地址。 -
DNS 递归服务器接着请求
google.com的域名服务器,这类域名服务器也被称为权威域名服务器(Authoritative nameserver) 。 -
google.com的域名服务器知道www.google.com的 IP 地址,因此向客户端返回了对应 IP。 -
DNS 递归服务器查询到了
www.google.com的 IP 地址,将其返回给浏览器。
上述八个步骤展示一个 DNS 查询的完整流程。在描述中出现了不少名词。比如四种 DNS 服务器的名字(DNS 递归服务器、根域名服务器、顶级域名服务器、权威域名服务器),两种查询请求(递归查询和迭代查询)。下面会分别对其进行解释。这六个中文名词是由英文名词翻译而来,英文名词主要参考 cloudflare 的文档。
DNS 递归服务器
DNS 递归服务器是和浏览器(以及其他客户端)直接打交道的 DNS 服务器,通常也是唯一需要交互的服务器。这个服务器会替浏览器进行所需的 DNS 查询,并将查询到的结果返回。像前文提到的 8.8.8.8 、1.1.1.1 都属于这类服务器。
为何该服务器叫 DNS 递归服务器呢?这里是从浏览器(客户端)的视角来看的。因为浏览器向 DNS 递归服务器发送的是一个递归请求。这个请求相当于客户端告诉服务端:“我需要这个域名的 IP 地址,请帮我找到这个 IP 地址,并且直接将这个地址返回给我。在找到这个 IP 地址之前,不用返回给我”。
这里的递归请求可以再和迭代请求比较来看,或许会更易理解。DNS 递归服务器依次查询多个域名服务器的过程,是一个迭代查询的过程。当 DNS 递归服务器向根域名服务器发送请求时,得到的其实是 .com 这个顶级域名服务器的地址,而向顶级域名服务器发送请求时,得到的是 google.com 的权威域名服务器的地址。这是一个逐步迭代查询的过程。直到最终从 google.com 的权威域名服务器,获得 google.com 的 ip 地址。迭代请求的含义类似于客户端告诉服务端:“我需要这个域名的 IP 地址,如果你不知道的话,也可以告诉我我需要去问谁”。
域名结构与域名服务器
关于DNS 递归服务器,以及递归请求和迭代请求更详尽的解释,可以参考 cloudflare 文档 what is recursive dns。这里接下来要讨论的是,为什么 DNS 递归服务器,是通过迭代请求的方式来查询呢?
因为世界上的域名浩瀚如烟。无论从物理的服务器部署上,还是逻辑的域名管理和注册上,集中到一处都是不合理的。因此其背后,有一套树状的分层存储与管理结构。这个树状结构和域名的命名方式是相互对应的。
最顶层的是根域名(root),可以表示为 . 。其之下的是各类顶级域名(Top Level Domain),比如 .com, .net, .org 都属于顶级域名。顶级域名之下,是权威域名(Authroitative Domain),如 goole.com, baidu.com,qq.com。每个级别的域名,有其对应的域名服务器。
全世界共有 13 台根域名服务器,可以在 Root Servers 上找到它们的 IP 地址。根域名服务器的地址是默认配置在 DNS 递归服务器中的。不需要通过查询而得。根域名服务器知道各类顶级域名服务器的地址。
顶级域名服务器,则知道其下域名的权威域名服务器的地址。比如 .com 顶级域名服务器知道 google.com 的权威域名服务器地址。而 .net 顶级域名服务器知道 battle.net 的权威域名服务器地址。
而权威域名服务器,知道其下的多级域名地址。 google.com 的权威域名服务器,会知道 www.google.com 、cloud.google.com 、 developers.google.com 等域名的 IP 地址。因此 DNS 查询在权威域名服务器这里也就结束了。此时 DNS 递归服务器就知道了 www.google.com 的 IP 地址,可以返回给服务端。
这种树状分层的管理方式,虽然增加了查询的次数,但也分担了域名存储和响应的压力。并且由于域名和其对应 IP 地址虽然会有变更,但通常变更不会过于频繁,因此域名和 IP 的对应关系通常会有多级缓存。浏览器会有缓存(chrome 可在地址栏输入 chrome://net-internals/#dns 查看),操作系统和 DNS 服务器也有缓存。在缓存过期时,才会进行迭代查询。因此这套系统在效率和复杂度间做了有效的平衡。
DNS 查询实践 - dig
请求 DNS 递归服务器
上文我们描述了 DNS 查询的过程。接下来我们利用命令行工具 - dig(domain information groper) 来跟 DNS 服务器进行互动,可以更具体的展示 DNS 查询的过程。
通过 dig,我们可以向 DNS 服务器发起查询请求,并且可以直观的看到返回的响应。
$ dig @8.8.8.8 www.google.com
上述的命令表示我们向 8.8.8.8 这个域名服务发起了 DNS 查询请求,询问 www.google.com 域名的 IP 地址。我们会得到如下输出:
从图中的 ANSWER SECTION 可以看到,DNS 服务器返回了一个 www.google.com 域名对应的 IP 地址。
我们还看到,每条记录有 300 和 A 这两个值。其中 300 是该记录被缓存时推荐的生命周期(Time To Live, TTL),单位是秒。即服务端告诉请求方,这个 IP 记录推荐的缓存过期时间是 300 秒,300 秒后应该使缓存失效,从新查询。当然,这个值不是强制性的,客户端也可以忽视这个值,自行设置缓存过期时间。
另一个值 A 表示这条 DNS 记录的是 IPv4 地址。常用的 DNS 记录类型还有
-
NS: 表示记录值是域名所对应的域名服务器地址。
-
CNAME: 记录值是一个域名地址。请求方应重定向 DNS 请求到 CNAME 所记录的域名地址上。
-
AAAA : 表示记录是一个 IPv6 类型地址。
更多 DNS 记录类型以及对应的解释,可以参考 cloudflare 的文档:DNS records。
请求根域名服务器
上述我们模拟了一个浏览器(或者其他客户端)访问 DNS 递归服务器的过程。
我们其实可以用 dig 命令。模拟出 DNS 递归服务器迭代查询域名的过程。
我们可以从之前提到的网址找到根域名服务器的地址,然后向它发起 DNS 查询请求。
$ dig @192.33.4.12 www.google.com
可以看到根域名服务器不知道 www.google.com 的 IP 地址,因此没有对应的 ANSWER SECTION。但在 AUTHORITY SECTIGON 中给出了对应的 .com 顶级域名服务器地址,并在 ADDITIONAL SECITON 中给出了对应的 IP 地址。
可以看到这里出现了 NS 这个记录类型。如前所述,它标志的是域名背后的域名服务器地址。即 k.gtld-server.net 等一连串记录,表示的是 .com 域名的域名服务器地址。
请求 .com 顶级域名服务器
得到了 .com 域名服务器的地址后,我们可以继续发起查询请求
$ dig @192.55.83.30 www.google.com
.com 域名服务器同样不知道 www.googl.com 的 IP 地址。但在 AUTHORITY SECTIGON 中给出了对应的 google.com 权威域名服务器地址,并在 ADDITIONAL SECITON 中给出了对应的 IP 地址。
请求 google.com 权威域名服务器
得到了 google.com 的域名服务器地址后,我们再向域名服务器发起查询请求。
$ dig @216.239.34.10 www.google.com
最终我们即可得到 www.google.com 的域名地址。这里利用 dig 模拟的迭代查询过程,对应了前文整体流程中的 3 ~ 7 步骤。
dig trace
除了我们逐个递归查询模拟外。dig 工具还提供了额外的能力,可以直接进行迭代查询。
我们命令上加上 +trace 可以获得下图的结果:
$ dig @8.8.8.8 www.google.com +trace
因为 0 和 1 片段的响应信息包含了地址列表以及其他一些额外信息,为了方便展示,剔除了部分信息,但不影响对流程的理解。
图中第 1 ~ 3 部即对应了迭代查询根域名服务器、顶级域名服务器以及权威域名服务器的过程。第 0 部分是从指定的域名服务器,获得根域名服务器地址。这个部分在实际的 DNS 递归服务器中应该是不需要的, DNS 递归服务中应该就存储着根域名服务器的 IP 地址。
通过 dig 命令,即可以直观了解到 DNS 查询的链路情况。虽然这里仅打印出了 NS 记录,但如果在命令中带上 +additional 字段的话,打印出的记录也会给出 NS 记录域名对应的 IP,就可以追踪到迭代查询过程中的 IP 地址。
总结
DNS 是客户端访问互联网的重要一环。它使普通用户可以不用了解多变却不易记的 IP 地址,而是通过一个简单好记的域名来访问网站。
而由于互联网上有海量的域名和 IP 地址记录,集中一处存储显然不现实。因此有了树状的域名命名体系和对应的域名服务器。级别依次递减为:根域名服务器,顶层域名服务器和权威域名服务器,每层节点仅需要知道下一层节点的域名服务器地址。
而由于域名信息的保存是一个这样的分层结构,因此有了 DNS 递归服务器,针对域名进行迭代查询。通过访问上一级域名服务器,得到下一级域名服务器的地址,直到最终得到所查询域名对应的 IP 地址。
而利用 dig 这个命令行工具,则可以帮助我们去模拟 DNS 查询请求,直观的看到 DNS 查询链路的过程以及响应信息。是后续工作中定位问题的帮手。
参考文献
- What is DNS:关于 DNS 基础的介绍,个人推荐 cloudflare 的这份文档。从多方面对 DNS 进行了介绍。本文未能详尽说明的内容,大部份都能在文档中找到。除了 cloudflare,像微软也有相关的 DNS 文档
- DNS 查询原理详解 : 阮一峰关于 DNS 原理的介绍,简明易懂。
- DNS cache and DNS lookup: What happens from typing in a URL to displaying a website? :和本文类似的对 DNS 流程介绍的文章
- TCP/IP Illustrated Volume 1 THe Protocols, Chapter 14, DNS: The Domain Name System: 这个章节针对 DNS 协议本身有更加具体完整的描述。并且还介绍了一些 DNS 通识文章不太常介绍的内容,比如说 DNS 逆向查询的协议(即可以通过 IP 地址,逆向找到对应的域名)