DNS(域名系统)
DNS 的作用是什么?
在本节中,我们将介绍几个常见的应用层(第七层)协议,它们运行在我们之前讨论过的各层之上。

我们要介绍的第一个应用是 DNS,它是一种运行在 1-4 层之上的协议,用于提供重要的网络功能 —— 名称解析。
互联网通常有两种索引方式。人类通过易读的名称(如google.com和eecs.berkeley.edu)访问网站,而计算机则通过 IP 地址(如172.217.4.174和23.195.69.108)访问网站。DNS(域名系统) 就是用于在这两者之间进行转换的协议。

DNS 的简要历史
要理解 DNS 的设计初衷,不妨回顾一下它的发展历史。
在早期的互联网及其前身 ARPANET 中,主要有三类应用。那时候还没有万维网和网页浏览器,大多数应用都在命令行中运行。
远程终端(telnet)允许用户远程连接到另一台机器,并在该远程计算机上运行命令。你可能听说过 SSH,它是这一协议的现代安全版本。
文件传输允许用户在本地计算机和远程计算机之间传输文件。你可能听说过 FTP,它是用于文件传输的应用层协议。
电子邮件允许用户相互交换消息。现代电子邮件配有浏览器中的网页客户端,但最初,用户必须在终端中输入类似mail alice@``46.0.1.2的命令,其中 46.0.1.2 是收件人所在机器的 IP 地址,alice 是该机器上的收件用户。
在这三种情况下,执行操作都需要指定远程主机。但如前所述,记住远程主机的 IP 地址很困难,且不够用户友好。
解决这个问题的第一次尝试是为每个 IP 地址分配一个主机名。每台计算机都会有一个名为hosts.txt的文件,用于将每个主机名映射到其对应的 IP 地址。例如,我们可以将主机名ucb-arpa映射到 10.0.0.78,这样就可以把mail mosher@``10.0.0.78写成mail mosher@ucb-arpa。
这个概念在今天仍然存在。如果你在自己的电脑上启动过服务器,它的 IP 地址可能是 127.0.0.1,主机名是localhost。在浏览器中输入这两个中的任何一个,结果都是一样的。
我们必须确保不同计算机上的hosts.txt文件是相同的。如果你在另一台电脑上输入mail mosher@ucb-arpa,应该能发送到同一个人。最初,这个主机文件由一个人(Elizabeth Feinler)维护,用户通过物理复制纸质文档来传递这个文件。
最初的纸质主机文件是人类可读的。它不仅映射主机名和地址,还包含用户的全名、运行的协议(如 TCP、FTP),甚至电话号码等信息。

正如 1973 年的 RFC606 中所提到的,大家都认为这种情况很不合理。拿到纸质文件后,你必须手动输入到电脑中。而且,文件是通过纸质传递的,所以你拿到的可能是过时的文件。
第一个改进是将这个列表改为机器可读的形式。现在,至少我们可以使用 FTP 等协议来共享这个文件,而不是纸质副本。但这仍然不可扩展。我们不能让一个人永远维护这个文件。此外,随着文件变大,下载速度会变慢,而且如果网络连接中断,可能会得到不完整的文件。
1983 年,DNS 在 RFC882 中首次被提出,作为解决这些问题的方案。从那以后,虽然有一些修改,但基本系统至今仍在使用。
趣闻:用于在 Unix 上运行 DNS 服务器的第一个软件是 BIND(1984 年,加州大学伯克利分校开发),至今仍被广泛使用。
DNS 设计目标
这段简短的历史揭示了 DNS 的一些设计目标,我们需要牢记。
DNS 必须具备可扩展性。互联网上有大量的主机,每秒都要执行大量的查询操作。主机也可以频繁地添加和移除。
DNS 必须具有高可用性、轻量级和快速的特点。几乎每一次互联网连接都始于 DNS 查询,将主机名转换为 IP 地址。因此,DNS 必须非常快,否则每一次连接都会变慢。此外,不能有单点故障,否则一旦出现故障,整个互联网都可能无法工作。
名称服务器
如果有一个集中式的服务器存储所有域名到 IP 地址的映射,供所有人查询,那当然很好,但不幸的是,没有任何服务器能大到足以存储互联网上所有域名的 IP 地址,并且快到能处理全球范围内产生的 DNS 请求量。相反,DNS 使用大量名称服务器的集合,这些服务器专门用于响应 DNS 请求。
每个名称服务器负责特定的域名区域,因此没有单个服务器需要存储互联网上的所有域名。例如,负责.com区域的名称服务器只需要回答以.com结尾的域名查询。该服务器不需要存储任何与wikipedia.org相关的 DNS 信息。同样,负责berkeley.edu区域的名称服务器不需要存储任何与stanford.edu相关的 DNS 信息。
尽管名称服务器有专门的用途(响应 DNS 请求),但它和互联网上其他你可以联系的服务器一样 —— 每个都有易读的域名(如a.edu-servers.net)和计算机可读的 IP 地址(如192.5.6.30)。注意不要将域名与区域混淆。例如,这个名称服务器的域名包含.net,但它负责响应.edu域名的 DNS 请求。
名称服务器层次结构
你可能会注意到这种设计存在两个问题。首先,.com区域可能比整个互联网小,但让一个名称服务器存储所有以.com结尾的域名仍然不切实际。其次,如果有很多名称服务器,你的电脑怎么知道该联系哪一个呢?
DNS 通过引入一个新想法解决了这两个问题:当你查询一个名称服务器时,它不一定总是返回你所查询域名的 IP 地址,还可以指引你去另一个名称服务器获取答案。这使得像.edu这样大区域的名称服务器可以将查询重定向到像berkeley.edu这样小区域的名称服务器。现在,.edu区域的名称服务器不需要存储eecs.berkeley.edu、math.berkeley.edu等的信息。相反,.edu名称服务器存储berkeley.edu名称服务器的信息,并将eecs.berkeley.edu、math.berkeley.edu等的请求重定向到berkeley.edu名称服务器。
DNS 将所有名称服务器按其负责的区域组织成树形层次结构:

树顶层的根服务器负责其区域内的所有域名(这个区域通常写作.)。树较低层级的名称服务器负责更小、更具体的区域。
DNS 查询(概念层面)
DNS 查询总是从根服务器开始。根服务器会将你的查询指引到它的一个子名称服务器。然后你向这个子名称服务器发送查询,该服务器再将你重定向到它的一个子服务器。这个过程不断重复,直到你查询到一个知道答案的名称服务器,它会返回与该域名对应的 IP 地址。
为了将你重定向到子名称服务器,父名称服务器必须提供子服务器的区域、易读的域名和 IP 地址,这样你才能联系到该子名称服务器获取更多信息。
举个例子,查询eecs.berkeley.edu的 DNS 过程可能如下(这个查询的漫画版本可在howdns.works查看)。

-
你对根名称服务器说:请告诉我
eecs.berkeley.edu的 IP 地址。 -
根服务器对你:我不知道,但我可以指引你去另一个有更多信息的名称服务器。这个服务器负责
.edu区域,它的域名是a.edu-servers.net,IP 地址是192.5.6.30。 -
你对
.edu名称服务器说:请告诉我eecs.berkeley.edu的 IP 地址。 -
.edu名称服务器对你:我不知道,但我可以指引你去另一个有更多信息的名称服务器。这个服务器负责berkeley.edu区域,它的域名是adns1.berkeley.edu,IP 地址是128.32.136.3。 -
你对
berkeley.edu名称服务器说:请告诉我eecs.berkeley.edu的 IP 地址。 -
berkeley.edu名称服务器对你:好的,eecs.berkeley.edu的 IP 地址是23.185.0.1。
一旦我们得到答案,就可以将其存储在缓存中,这样以后再次需要这个记录时就不必再查询了。
存根解析器和递归解析器
最初,终端主机(如你的电脑)会执行 DNS 查询,联系各个名称服务器。
如今,你的本地计算机通常会将 DNS 查询任务委托给DNS 递归解析器,由它为你查询名称服务器。执行查询时,你电脑上的DNS 存根解析器向递归解析器发送查询,让解析器完成所有工作,然后从解析器接收响应。

我们如何知道解析器的 IP 地址呢?当你第一次连接到互联网时,会有人告诉你解析器的地址。你也可以手动输入你想使用的解析器地址。
互联网上一些知名的解析器包括 1.1.1.1(由 Cloudflare 运营)和 8.8.8.8(由谷歌运营)。它们通常有容易记住的 IP 地址,这样我们就不必通过名称来引用它们。否则,我们就得先进行 DNS 查询来找到它们的 IP 地址,而这些服务器的作用正是为我们做 DNS 查询。
除了科技公司,互联网服务提供商(ISPs)也运营解析器,作为其向客户提供的互联网服务的一部分。(如果你付费购买了互联网服务,却还得在浏览器中输入 IP 地址,那体验就太糟糕了。)
你家里的路由器也可以充当解析器。如果你使用的路由器在物理位置上离你很近(你和路由器之间的延迟更小),DNS 查询可能会更快。
解析器的一个主要好处是更好的缓存。解析器处理来自许多不同终端主机(不仅仅是你自己的电脑)的查询,因此它会构建一个更大的缓存。如果你向解析器查询eecs.berkeley.edu,而之前有人最近向该解析器查询过同样的问题,解析器可以直接给你答案,而无需联系其他任何名称服务器。
请注意,尽管递归解析器存储更大的缓存,存根解析器仍然可以维护自己单独的缓存。有些查询可以由存根解析器的缓存直接回答,甚至无需询问递归解析器。
冗余
到目前为止,我们一直在说 “berkeley.edu名称服务器”,但实际上,每个区域都有多个名称服务器。一个区域的所有名称服务器功能相同,每个都可以回答该区域内的任何查询。
这确保了该区域的可用性,如果一个名称服务器出现故障,其他服务器可以继续回答该区域的查询。按照惯例,区域需要有两个名称服务器,但实际上,大多数区域至少有三个。
通常,其中一个名称服务器被指定为主服务器,实际管理该区域。其余的服务器是辅助镜像服务器,仅存储和提供主服务器上信息的副本。
在 DNS 查询中,名称服务器可能会回复你:“我不知道,但你应该询问.edu区域。该区域有 13 个名称服务器,这是每个服务器的域名和 IP 地址。” 然后,你可以选择联系其中一个名称服务器。
DNS API
了解了 DNS 的概念后,我们来看看它实际上是如何实现的。
首先,程序员如何使用 DNS 进行查询呢?
有一些相对简单、通用且稳定的 API 可用于执行 DNS 查询。不同语言的 API 相当类似。
在标准 C 库中,可以使用gethostbyname("``foo.com``")来查询foo.com对应的 IP 地址。不过,这个函数仅支持 IPv4,现在已经被弃用(尽管你可能还会看到它被使用)。
标准 C 库中的更新版本是getaddrinfo("``example.com``", NULL, NULL, &result),它会查询example.com的 IP 地址,并将答案存储在result结构体中。不必太在意具体细节或这里设置为 null 的额外参数。这个替代 API 支持除 IPv4 之外的更多类型(如 IPv6)。
作为程序员,你无需担心 DNS 的复杂性,如递归解析器或特定的名称服务器。你只需调用标准库函数即可。这些函数通常会向操作系统中的存根解析器发出请求。
DNS 使用 UDP,而非 TCP
从本质上讲,DNS 是一种客户端 - 服务器协议。一方(客户端)发送问题,另一方(服务器)发送答案。客户端通常是用户主机或递归解析器,服务器通常是名称服务器。客户端和服务器应该如何格式化它们的消息呢?
DNS 使用 UDP(尽力而为的数据包,无 TCP 握手),以使 DNS 轻量且快速。我们不必等待三次 TCP 握手完成。我们不关心数据包是否按顺序到达,因为查询和响应通常可以放入单个 UDP 数据包中。
使用 UDP,服务器不需要为每个连接保持状态(与 TCP 不同,TCP 中服务器必须维护缓冲区)。每个数据包都可以独立处理。这也有助于保持 DNS 的轻量性,因为名称服务器接收大量请求,为每个请求建立新连接会很昂贵。
我们如何处理 UDP 的不可靠性和数据包丢失问题呢?可以通过简单的重试机制来解决。如果在一定时间内没有收到回复,就重新发送查询。超时时间因操作系统而异,不过可能会相当长。
UDP 的不可靠性和超时的缓慢,使得使用附近且能可靠联系的解析器变得很重要。例如,你家里路由器中的解析器离你很近,而且可能相当可靠(家庭网络中的拥塞不会太严重)。你也可以通过设置多个备用解析器(如家庭路由器和 8.8.8.8)来提高可靠性。
如前所述,DNS 查询和响应通常可以放入单个数据包中。一个值得注意的例外是当我们在主名称服务器和辅助名称服务器之间传输区域数据时。辅助名称服务器必须说:把你所有的记录都给我,这样我才能帮你提供服务。这个响应可能会非常大,因此这些传输通常通过 TCP 进行。
DNS 的最新进展增加了安全功能(例如,阻止攻击者在传输过程中更改记录),这也可能需要从 UDP 切换到 TCP。
回想一下,UDP 通过端口来支持单个服务器上的多个应用。按照惯例,所有名称服务器都在 UDP 端口 53 上监听 DNS 查询。这个端口号是众所周知且固定的,这样每个人都能联系到名称服务器上的正确端口。
DNS 消息格式

第一个字段是 16 位的标识字段,每个查询随机选择,用于将请求与响应匹配。发送 DNS 查询时,ID 字段填充随机位。由于 UDP 是无状态的,DNS 响应必须在 ID 字段中返回相同的位,以便原始查询发送者知道该响应对应的是哪个 DNS 查询。
接下来的 16 位用于标志。QR 位指定消息是查询(位为 0)还是响应(位为 1)。RD 位指示你是否希望解析器执行递归查询,还是仅返回名称服务器的结果(即使结果是 “我不知道”)。
理论上,可以在标志中指定查询类型,不过IQUERY类型已过时,STATUS类型也没有明确定义,因此QUERY类型基本上用于所有情况。
标志还可以指定查询是否成功(例如,如果查询成功,回复中会设置NOERROR标志;如果查询的是不存在的名称,回复中会设置NXDOMAIN标志)。
下一个字段指定所问问题的数量(实际上,这个数量总是 1)。后面的三个字段用于响应消息,指定消息中包含的资源记录(RRs) 的数量。我们稍后会详细描述这些类别的资源记录。
消息的其余部分包含 DNS 查询 / 响应的实际内容。这些内容总是以一组资源记录的形式组织,每个资源记录是一个带有相关类型的键值对。
为完整起见,DNS 记录键正式定义为三元组<名称,类别,类型>,其中名称是实际的键数据,类别对于互联网来说总是IN(除了用于获取 DNS 自身信息的特殊查询),类型指定记录类型。DNS 记录值包含<TTL,值>,其中TTL是生存时间(记录可被缓存的时间,以秒为单位),值是实际的价值数据。
DNS 中有三种主要的记录类型。A 类型记录将域名映射到 IPv4 地址。键是域名,值是 IP 地址。类似地,AAAA类型记录将域名映射到 IPv6 地址。NS 类型记录将区域映射到域名。键是区域,值是域名。
你可能有时会遇到CNAME类型记录,它用于别名或重定向。名称和值都是域名,该记录表示这两个域名具有相同的 IP 地址。
本节的重要结论:每个 DNS 数据包都有一个 16 位的随机 ID 字段、一些元数据和一组资源记录。每个记录属于四个类别(问题、答案、权威、附加)之一,每个记录包含类型、键和值。存在 A 类型记录和 NS 类型记录。
DNS 查询(实现层面)
现在,让我们详细了解查询eecs.berkeley.edu的 IP 地址的实际 DNS 查询过程。你可以在家使用dig工具尝试 —— 记得设置+norecurse标志,这样你可以自己逐步解开递归过程。
每个 DNS 查询都从根服务器开始。根服务器的名称和 IP 地址通常硬编码到解析器中,以根提示文件的形式存在。如果你好奇,这里有一个根提示文件:https://www.internic.net/domain/named.root。
第一个根服务器的域名是a.root-servers.net,IP 地址是198.41.0.4。我们可以使用dig向这个地址发送 DNS 请求,查询eecs.berkeley.edu的 IP 地址。
\$ dig +norecurse eecs.berkeley.edu @198.41.0.4
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 26114
;; flags: qr; QUERY: 1, ANSWER: 0, AUTHORITY: 13, ADDITIONAL: 27
;; QUESTION SECTION:
;eecs.berkeley.edu. IN A
;; AUTHORITY SECTION:
edu. 172800 IN NS a.edu-servers.net.
edu. 172800 IN NS b.edu-servers.net.
edu. 172800 IN NS c.edu-servers.net.
...
;; ADDITIONAL SECTION:
a.edu-servers.net. 172800 IN A 192.5.6.30
b.edu-servers.net. 172800 IN A 192.33.14.30
c.edu-servers.net. 172800 IN A 192.26.92.30
...
在答案的第一部分,我们可以看到头部信息,包括 ID 字段(26114)、返回标志(NOERROR)以及每个部分返回的记录数量。
问题部分包含 1 条记录(你可以通过头部中的QUERY: 1来验证)。它的键是eecs.berkeley.edu,类型是A,值为空。这表示我们查询的域名(值为空是因为我们不知道对应的 IP 地址)。
答案部分为空(头部中的ANSWER: 0),因为根服务器没有直接提供我们查询的答案。
权威部分包含 13 条记录。第一条的键是.edu,类型是NS,值是a.edu-servers.net。这是根服务器告诉我们下一个可以联系的名称服务器的区域和域名。该部分中的每条记录对应一个我们接下来可以查询的潜在名称服务器。
注意:通常,名称在字母或数字上最早的服务器是主服务器(这里是a.edu-servers.net),其余的是镜像服务器。另外,多个记录具有相同的名称(.edu)是正常的。这只是告诉我们有多个名称服务器可以回答该区域的查询。
附加部分包含 27 条记录。第一条的键是a.edu-servers.net,类型是A,值是192.5.6.30。这是根服务器通过将权威部分中的域名映射到 IP 地址,告诉我们下一个名称服务器的 IP 地址。
权威部分和附加部分一起为我们提供了下一个名称服务器的区域、域名和 IP 地址。这些信息分布在两个部分中,以保持 DNS 消息的键值结构。
为完整起见:172800是每条记录的 TTL(生存时间),这里设置为 172800 秒 = 48 小时。IN是互联网类别,基本上可以忽略。有时你会看到AAAA类型的记录,它们对应 IPv6 地址(通常的A类型记录对应 IPv4 地址)。
合理性检查:我们接下来查询哪个名称服务器?我们如何知道该名称服务器的位置?我们向该名称服务器查询什么?\footnote {查询a.edu-servers.net,我们通过附加部分中的记录知道它的位置。像之前一样,查询eecs.berkeley.edu的 IP 地址。}
\$ dig +norecurse eecs.berkeley.edu @192.5.6.30
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 36257
;; flags: qr; QUERY: 1, ANSWER: 0, AUTHORITY: 3, ADDITIONAL: 5
;; QUESTION SECTION:
;eecs.berkeley.edu. IN A
;; AUTHORITY SECTION:
berkeley.edu. 172800 IN NS adns1.berkeley.edu.
berkeley.edu. 172800 IN NS adns2.berkeley.edu.
berkeley.edu. 172800 IN NS adns3.berkeley.edu.
;; ADDITIONAL SECTION:
adns1.berkeley.edu. 172800 IN A 128.32.136.3
adns2.berkeley.edu. 172800 IN A 128.32.136.14
adns3.berkeley.edu. 172800 IN A 192.107.102.142
...
下一次查询的答案部分也为空,权威部分有NS记录,附加部分有A记录,这些记录为我们提供了负责berkeley.edu区域的名称服务器的域名和 IP 地址。
\$ dig +norecurse eecs.berkeley.edu @128.32.136.3
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 52788
;; flags: qr aa; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; QUESTION SECTION:
;eecs.berkeley.edu. IN A
;; ANSWER SECTION:
eecs.berkeley.edu. 86400 IN A 23.185.0.1
最后,最后一次查询在答案部分以一条A类型记录的形式给出了eecs.berkeley.edu对应的 IP 地址。
实际上,由于递归解析器会尽可能多地缓存答案,大多数查询可以跳过前几个步骤,使用缓存的记录,而不必每次都询问根服务器和像.edu这样的高层名称服务器。缓存有助于加快 DNS 速度,因为将域名转换为 IP 地址时需要在网络上发送的数据包更少。缓存还有助于减少高层名称服务器的请求负载。
现在我们知道了 DNS 是如何实现查询功能的,接下来可以看看 DNS 在实际中是如何工作的,并探索 DNS 在现实世界中的商业方面。
DNS 权限层次结构
我们所绘制的树形层次结构实际上代表了 DNS 在三个不同方面的层级关系。
正如我们已经看到的,DNS 名称是分层的。这就是为什么我们的域名(如eecs.berkeley.edu)是由多个用点分隔的单词组成的。

我们也看到了 DNS 的基础设施是分层的。我们可以将名称服务器组织成树形层次结构,其中每个名称服务器只了解树中自己的部分。

我们要介绍的第三个层级是权限层级。它告诉我们谁定义了树中存在的名称。例如,运营.edu名称服务器的组织负责.edu区域内的所有域名。.edu组织可以将其区域的部分权限委托给树中的下属。
例如,.edu组织可以说:berkeley.edu(及其所有子域名)属于我的区域,我将把该区域的这部分控制权转移给加州大学伯克利分校组织。现在,我们创建了一个由加州大学伯克利分校拥有的新区域,.edu组织不需要知道这个新的berkeley.edu区域中的更新。加州大学伯克利分校有权在其区域中创建新域名,或者可能将其区域的部分权限进一步委托给更低级别的组织。

当我们结合这三个层级来绘制这棵树时,可以更精确地说,每个节点代表一个区域。区域正式定义为负责层次结构中某部分的管理机构。
从命名的角度来看,每个区域包含一个或多个名称 - IP 映射,其中名称是该区域的子域名。eecs.berkeley.edu区域可以包含名称eecs.berkeley.edu或iris.eecs.berkeley.edu,但不能包含名称math.berkeley.edu。
从基础设施的角度来看,该区域由一个或多个名称服务器支持,这些服务器回答关于该区域内域名的查询。
从权限的角度来看,该区域由某个组织控制,该组织由其上级赋予管理该区域内名称的权限。例如,控制berkeley.edu区域的加州大学伯克利分校可以将eecs.berkeley.edu区域委托给 EECS 系。
实际中的 DNS 区域
现实生活中这些组织是谁呢?

根区域由 ICANN(互联网名称与数字地址分配机构)控制。他们负责将根区域(代表整个互联网)的部分权限委托给特定的顶级域名。
树中直接位于根下方的所有区域都是顶级域名(TLDs)。互联网最初在美国发展,因此最早的顶级域名根据用途将互联网划分为不同区域,如.com(商业)、.gov(政府)、.edu(教育)和.mil(军事)。最终,也为各国创建了顶级域名,如.fr(法国)和.jp(日本)。
历史上,顶级域名相对较少。最近,ICANN 开始以 15 万美元的注册价(加上持续的维护费用)出售新的顶级域名,这导致了新顶级域名的爆发式增长。截至 2024 年,已有超过 1500 个顶级域名,包括公司特定的顶级域名(如.google)和更具特色的顶级域名(如.travel 或.pizza)。
每个顶级域名由某个组织运营,该组织可以决定如何进一步划分该顶级域名的结构。例如,.uk 顶级域名由 Nominet 管理,他们决定按用途划分.uk 区域,创建如.co.uk(商业)、.ac.uk(学术界)等区域。
树中更深层次的区域可以按其深度来称呼。example.com是二级域名,blog.example.com是三级域名。这些区域通常由各种组织和公司控制,他们从父区域购买这些区域。当你购买一个区域时,你还需要告诉父区域你正在使用的名称服务器。这使得父区域可以将用户重定向到你的名称服务器。
树中较深层的名称服务器通常由域名注册商运营。注册商是拥有特定区域的公司,允许用户在该区域的特定域名上托管服务。例如,我可能每月付费在blog.foo.com上托管我的网站。注册商通常会提供将相应的域名到 IP 的映射添加到其名称服务器的服务。
除了注册商,像谷歌这样的公司也可能为自己的应用运行自己的名称服务器(例如,提供maps.google.com的记录)。亚马逊网络服务也有一个名为 Route 53 的名称服务器,用户可以在上面添加要提供的记录。
根服务器的任播可用性
如果根服务器不可用,后果会非常严重。缓存为空的用户将无法进行任何 DNS 请求。最终,随着 TTL 过期,所有人的缓存都会清空,互联网将停止工作。
为了实现冗余,全球有 13 个根服务器。我们可以查询根服务器的IP 地址,这些地址是公开且众所周知的。
考虑到整个互联网都依赖它们,13 个似乎还是有点少。为了提供极高的可用性,实际上根服务器的数量远不止 13 个,但列表中只包含 13 个 IP 地址,这得益于一种称为任播的巧妙技巧。
在任播技巧中,我们部署许多根服务器的镜像,并为所有镜像使用相同的 IP 地址。如果你联系域名k.root-servers.net或其对应的 IP 地址,你可能正在与它的任何一个镜像通信。
这 13 个根服务器中的每一个实际上都由大量镜像组成。例如,f.root-servers.net有超过 3000 个实例。这些镜像可以由不同的网络运营商运行(例如,谷歌和 Cloudflare 可能帮助运行根镜像)。
要实现任播,在路由协议中,每个镜像都会宣告相同的 IP 地址。其余的路由协议仍然正常工作。你可能会收到许多关于k.root-servers.net路由的宣告,你可以接受其中任何一个,最终数据包会路由到其中一个镜像。如果一个镜像出现故障,其余的镜像仍在发送宣告,你可以接受不同的路由以保持可用性。
任播还意味着,即使添加或移除镜像,根 IP 也很少改变。因此,根提示文件中包含的根名称服务器记录具有很长的 TTL(42 天)。

这是k.root-servers.net所有镜像的地图。它们都宣告相同的 IP 地址,你的路由器可能会选择与最近的镜像通信。k.root-servers.net由 RIPE(欧洲的组织)运营,这可能解释了为什么欧洲有这么多镜像。
像谷歌的 8.8.8.8 这样的公共解析器也可以采用任播来实现高可用性。
电子邮件中的 DNS
DNS 不仅可以用于存储和提供域名到 IP 的映射。例如,如果你想向evanbot@berkeley.edu这样的地址发送电子邮件,你的电脑仍然需要知道将数据包发送到哪里。
为了将电子邮件地址转换为邮件服务器,我们可以使用MX类型记录。这些记录将berkeley.edu等域名映射到aspmx.l.google.com等邮件服务器。注意,邮件服务器可以位于不同的区域(例如,这里加州大学伯克利分校的邮件服务器由谷歌运营)。
历史上,电子邮件地址对应于特定机器上的用户,因此邮件服务器就是该机器。如今,你可能希望能够从任何地方访问你的电子邮件。这就是为什么 MX 记录映射到像aspmx.l.google.com这样的邮件服务器,这些服务器接收你的邮件,并允许你从任何计算机访问邮件。
MX 记录的一个关键区别是,其值还包含优先级。如果你收到多个 MX 记录,都将同一个域名映射到不同的邮件服务器,你应该先尝试优先级最高(数字最小)的邮件服务器。
DNS 用于负载均衡
在演示中,我们看到最终的名称服务器返回了一条A类型记录,但实际上可能会收到多条A类型记录,将单个域名映射到多个 IP 地址。这些记录没有顺序,服务器可能会在返回前打乱顺序。它们中的任何一个都是有效的,客户端可以选择任何一个(通常是第一个)。提供多个 IP 地址有助于负载均衡和冗余。
名称服务器也可能根据发送查询的对象以及查询的来源,返回不同的A类型记录。这对于像www.google.com这样的热门域名在多个服务器之间进行负载均衡很有用。例如,我们可以尝试将用户发送到最近的服务器。
名称服务器现在需要额外的(可能是专有)逻辑来决定在回复中发送哪些记录。名称服务器可以检查递归解析器是谁。它也可以检查终端客户端是谁,尽管这需要 DNS 的扩展,以便解析器的查询包含客户端地址。它甚至可以检查终端用户的地理位置,尽管这需要将 IP 地址映射到物理位置的某种映射。像 MaxMind 这样的商业数据库可用于将 IP 地址映射到物理位置。
基于用户地理位置的负载均衡并不完美。即使我们知道服务器的物理位置和用户的物理位置,也必须猜测从网络角度来看哪个服务器最近。我们也不知道用户和不同服务器之间的性能(如网络带宽)。

这里有一个实验,看看谷歌的地理负载均衡效果如何。我们在旧金山和俄勒冈州查询www.google.com,得到了两个不同的 IP 地址。
然后我们进行反向查询,将每个 IP 地址与特定的服务器名称(每个服务器不同,不是www.google.com)匹配。结果是PTR类型记录,它将 IP 地址映射到名称(与A类型记录相反)。我们可以看到,谷歌在旧金山提供给我们的 IP 地址映射到某个名称为sfo03s25的服务器。假设sfo代表旧金山,那效果相当好!
如果我们查看延迟,从旧金山的计算机连接到提供给旧金山的 IP 地址需要 20 毫秒,而从旧金山的计算机连接到提供给俄勒冈州的 IP 地址需要 35 秒。谷歌很好地为旧金山的计算机提供了一个更近的服务器!