可能大家都知道或者被问过一个问题,那就是很经典的「从浏览器输入 URL 再到页面展示,都发生了什么」。这个问题虽然简单,但是真的能够从回答的各种细节上看出不同人之间的水平差距。
这篇文章主要是聊一聊输入 URL 之后的第一步——域名解析
域名就类似于 www.google.com,而通过 ping
命令,就可以查询到对应域名的 IP 地址了。
那为什么又要有域名,又要有 IP 呢?
域名、IP 共存
首先还是解释一下,为什么会出现现在这种域名、IP 地址共存的情况。主要有两个点:
- 提升用户体验
- 提高运行效率
分别解释一下,IP 地址长度为 32 位,平时用十进制来表示的话,就长这样——192.168.1.0
,但是想象一下,如果我们要访问某个网站需要让我们输入这么一长串数字,体验肯定相当差,首先记忆这么长串数字对很多人来说就很痛苦,更何况我们常用的网站肯定不止一个。
除此之外,如果你给其他人推广你的网站,你吧啦吧啦说了一大堆,然后来个「如果你感兴趣,请访问我们的网站 192.168.1.0」,然后就没有然后了。
这也是为啥现在仍然在使用域名,方便人脑去记忆。
那为啥还需要 IP 地址呢?因为 IPv4 中的 IP 地址只需要 4 个字节,而用字符串表示的域名最少也需要几十个字节,长的甚至达到几百字节,而这会大大的增加底层路由器的负担。
这也是为啥 IP 地址仍然在被使用。人来使用域名,而路由器层则使用 IP 地址,就跟我们书写的是我们能认识的字符,而最终计算机认识的是一堆二进制一样。
DNS 解析
知道了这个背景之后,我们就可以来看看「域名」是如果变成「IP 地址」的。
首先我们知道,会往 DNS 服务器发送请求,那问题就来了,浏览器怎么知道 DNS 服务器的地址是啥?
答案是提前配置好的。当然这不是唯一的方式,DNS 也有可能通过 DHCP(Dynamic Host Configuration Protocol) 动态分配的。
例如,MacOS 中的 DNS 配置就长下面这样。
当然,你也可以通过命令行来查看、修改,地址在 /etc/resolv.conf
。
有了 DNS 服务器,那么你可能会觉得,接下来的事情就很简单了:
我给你传个域名,你返给我对应的 IP 地址即可。那问题来了,现在互联网中有数万台的 DNS 服务器,我怎么知道数据在哪台服务器上?难道要一台一台的遍历请求这数万台服务器吗?
我相信你肯定没有感知到在浏览器中输入域名到页面展示会花费那么久,这也说明肯定不是一台一台服务器进行遍历的。
域名的组成
要了解 DNS 是如何对其进行优化的,我们需要先知道域名的组成部分。看到这,很可能你会这么想:
啥组成?不就是一堆字符串吗?
实际上,域名是有由不同的域组成的,每个 .
隔开的部分就是一个域。
这里举个例子,假设我们分析的域名为 www.google.com
,从我们平时写快递的收货地址的惯性思维来看,这个域的各个部分大小可能是这样的:
www > google > com
但是实际上并不是这样,而是:
. > com > google > www
你甚至发现,最大的还是个 .
。其实完整的域名应该是 www.google.com.
,.
代表根域,因为根域对于所有的域名来说,意义都一样,所以平时我们都把最后的点给省略了。
每个域都有自己的专属名词:
. > com > google > www 根域 | 一级级域|二级域名|(子域名)|主机名
当然,我们知道还可以针对二级域名再划分子域名,类似于 mail.google.com
。
所以看到这,你应该能够理解域名是由层次的这个概念了,我再举个比较的通俗的例子。
com 公司的 google 部门的 www。https://mail.google.com/mail/u/0/#inbox
DNS 的分层
了解完域名的分层之后,DNS 是如何优化域名解析的问题就迎刃而解了,那就是——分层。
DNS 服务器会将域名的数据分布式的存储在各个 DNS 服务器上,但是同一个域的数据,会存储在同一台 DNS 服务器上,同一台 DNS 服务器可以存储多个域的数据。
这么说可能会有些抽象,一图胜千言,其实就是这样:
有了对数据的分层,那么查询数据就会很有节奏感。
查询域名数据
一图胜千言,有了分层的机制,整个的查询过程就会长这样:
首先会去配置的 DNS 服务器中查询,这个其实一般都是本地或者内网中的 DNS 服务器。如果没有找到,就会去问根域要,说哥们,我这里需要 www.google.com
的 IP 地址。
根域一看,我这里没有啊,但是我知道 com
域的 DNS 服务器地址,他可能知道。
然后 com
域的 DNS 服务器一看,www.google.com
的 IP 地址我也不知道,但是我知道 google.com
域的 DNS 服务器的地址,他可能知道,你再去问问他。
就这样一路问下去,最终就能够找到 www.google.com
所对应的 IP 地址了。
根域名服务器
看了上面的流程,可能你还是会有点疑问。因为去找 DNS 服务器查询 IP 地址时,初始的 DNS 的服务器的 IP 地址是走的本地计算机的配置的。那在分层查询时,我怎么知道有哪些根服务器?以及我怎么知道这些根服务器的 IP 地址是啥?
答案是内置。
我们的设备,或者说所有能上网的设备都会内置根服务器的列表。总共有 13 台根 DNS 服务器,分别是[a-m].root-servers.net
,这些根服务器的地址根本不需要查询就能直接获取。
当然,稍微想想也知道,13 台服务器是很难扛住全球互联网用户的请求的,实际上对于这 13 台服务器有很多的镜像服务器。
眼见为实
说了这么多虚的概念,接下来我们通过 dig
命令来实际操作一下。
可以看到,在 QUESTION SECTION
下的完整域名是 www.google.com.
是带了根域的,那后面的这个 IN
和 A
又是啥意思呢?
这是因为,在向 DNS 服务器查询请求的时候,需要三个参数,分别是:
- 域名(例如 www.google.com)
- 网络类型(Class 设计之初,考虑到了多种网络并存的场景,但是目前实际上只有一种网络——互联网,所以该参数的值一直都会为 ——
IN
) - 类型(例如
A
表示 IP 地址,而MX
则表示邮件服务器的地址)
而在 ANSWER SECTION
中,则是 DNS 服务的响应结果,上图中显示了总有 6
条 DNS 记录,并且在后面返回了其对应的 IP 地址。
而其中的 69
则是 TTL,单位是秒,代表了在 69 之内都不用再次发送请求了。
最下面则是统计的信息,本次 DNS 查询所话费的时间,以及请求的 DNS 服务器的地址和端口。这个服务器地址是我们本机配置的 DNS 服务器的地址。
眼尖的可能发现了,上图中根本没有设计到对根服务器的请求。这是因为这个命令把这部分给省略掉了,我们可能通过加上 trace
命令行参数来查看详细的分级查询过程。
这次我们以 www.36kr.com
来作为例子。
可以看到,上图中列出了所有的根域名服务器,然后去找 com
域要,然后再找 36kr.com
域去要,最终是拿到了 www.36kr.com
的 IP 地址。
缓存机制
当然,如果每次都从根服务器开始往下找,明显是不合理的,因为域名和 IP 地址的对应关系本来变动的就不频繁,所以 DNS 服务器是都会将结果缓存的。
并且,在下图中:
我只写了一个 DNS 服务器中有同级别的域信息,但是实际上不同层级的域信息可能存在于同一个 DNS 服务器,举个例子,com
域和 google.com
域可能在同一台机器上。
但是,这个缓存是有有效期的。如果在这个有效期内 DNS 数据发生了变化,那么缓存中的数据就会不正确,此时需要手动的将 DNS 删除。