为了解决上面的问题,1983 年 Paul Mockapetris 提出了 DNS(Domain Name System,域名系统),这是一种层次的、基于域的命名方案,并且用一个分布式数据库系统加以实现。当我们需要访问一个 Domain(计算机的别名)时,应用程序会向 DNS 服务器发起一个 DNS Query Request,DNS 服务器返回该 Domain 对应的 IP 地址。
DNS 通过下面三种手段解决了上述的问题:
用户计算机上并没有存储所有的 Hostname 到 IP 的映射,这样避免了 hosts 文件过于庞大。
图中域名空间划分为 A, B, C, D, E, F, G 七个 DNS 区域,每个 DNS 区域都有多个权威域名服务器,这些域名服务器里面保存了许多域名解析记录。对于上图的 DNS 区域 E 来说,它的权威域名服务器里面保存的记录如图中表格所示。
仔细观察上图你可能会发现区域 A、B 并没有父区域,他们之间并没有一条路径连在一起。这将导致一个很麻烦的问题,那就是区域 A 的权威域名服务器可能根本不知道区域 B 的存在。于是引入了根域名服务器 “.”,它保存了所有顶级区域的权威域名服务器记录。现在通过根域名服务器,我们可以找到所有的顶级区域的权威域名服务器,然后就可以往下一级一级找下去了。下图为全球根域名服务器的分布图:
域名服务器类型
由高向低进行层次划分,可分为以下几大类:
根域名服务器:最高层次的域名服务器,也是最重要的域名服务器,本地域名服务器如果解析不了域名就会向根域名服务器求助。全球共有 13 个不同 IP 地址的根域名服务器,它们的名称用一个英文字母命名,从 a 一直到 m。这些服务器由各种组织控制,并由 ICANN(互联网名称和数字地址分配公司)授权,由于每分钟都要解析的名称数量多得令人难以置信,所以实际上每个根服务器都有镜像服务器,每个根服务器与它的镜像服务器共享同一个 IP 地址,中国大陆地区内只有 6 组根服务器镜像。当你对某个根服务器发出请求时,请求会被路由到该根服务器离你最近的镜像服务器。所有的根域名服务器都知道所有的顶级域名服务器的域名和地址,如果向根服务器发出对 jocent.me 的请求,则根服务器是不能在它的记录文件中找到与 jocent.me 匹配的记录。但是它会找到 me 的顶级域名记录,并把负责 me 地址的顶级域名服务器的地址发回给请求者。
顶级域名服务器:负责管理在该顶级域名服务器下注册的二级域名。当根域名服务器告诉查询者顶级域名服务器地址时,查询者紧接着就会到顶级域名服务器进行查询。比如还是查询 jocent.me,根域名服务器已经告诉了查询者 me 顶级域名服务器的地址,me 顶级域名服务器会找到 jocent.me 的域名服务器的记录,域名服务器检查其区域文件,并发现它有与 jocent.me 相关联的区域文件。在此文件的内部,有该主机的记录。此记录说明此主机所在的 IP 地址,并向请求者返回最终答案。
权威域名服务器:负责一个 Domain Zone 的域名解析工作。
本地域名服务器:当一个主机发出 DNS 查询请求的时候,这个查询请求首先就是发给本地域名服务器的。
域名解析
我们已经有了一个域名服务器集群,该集群合理地保存了域名空间和域名资源记录的对应关系。现在我们要做的就是发送一个 DNS 查询请求给域名服务器,然后坐等它返回正确的域名资源记录,这个过程叫作域名解析。
域名解析的过程最早要追溯到建立网络连接。因为每当连接上网络之后,计算机会自动获得一个默认的 DNS 服务器,当然你也可以用自己信任的 DNS 服务器,比如:8.8.8.8、114.114.114.114。我们把这个域名服务器也叫作本地域名服务器。
资源数据:该字段是一个可变长字段,表示按照查询段的要求返回的相关资源记录的数据。可以是 Address(表明查询报文想要的回应是一个 IP 地址)或者 CNAME(表明查询报文想要的回应是一个规范主机名)等。
Wireshark 抓包分析
域名解析总体可分为两大步骤,第一个步骤是本机向本地域名服务器发出一个 DNS 请求报文,报文里携带需要查询的域名;第二个步骤是本地域名服务器向本机回应一个 DNS 响应报文,里面包含域名对应的 IP 地址。从下面对 jocent.me 进行域名解析的报文中可明显看出这两大步骤。注意:第二大步骤中采用的是迭代查询,其实是包含了很多小步骤的,详情见下面的流程分析。
其具体的流程可描述如下:
主机 10.74.36.90 先向本地域名服务器 10.74.1.11 进行递归查询。
本地域名服务器采用迭代查询,向一个根域名服务器进行查询。
根域名服务器告诉本地域名服务器,下一次应该查询的顶级域名服务器 dns.me 的 IP 地址。
本地域名服务器向顶级域名服务器 dns.me 进行查询。
顶级域名服务器 me 告诉本地域名服务器,下一步查询权限服务器 dns.jocent.me 的 IP 地址。
本地域名服务器向权限服务器 dns.jocent.me 进行查询。
权限服务器 dns.jocent.me 告诉本地域名服务器所查询的主机的 IP 地址。
本地域名服务器最后把查询结果告诉 10.74.36.90。
其中有两个概念递归查询和迭代查询,其实在整个描述的过程中已经体现的很明显,这里再说明一下:
递归查询:本机向本地域名服务器发出一次查询请求,就静待最终的结果。如果本地域名服务器无法解析,自己会以 DNS 客户机的身份向其它域名服务器查询,直到得到最终的 IP 地址告诉本机。
RFC6891 提供的 DNS 扩展机制能够帮助我们在一定程度上解决大数据包被截断的问题,减少了使用 TCP 协议进行重试的需要,但是由于最大传输单元的限制,这并不能解决所有问题。
DNS 出现之后的 30 多年,RFC7766 才终于提出了使用 TCP 协议作为主要协议来解决 UDP 无法解决的问题,TCP 协议也不再只是一种重试时使用的机制,随后出现的 DNS over TLS 和 DNS over HTTP 也都是对 DNS 协议的一种补充。
从这段发展时来看,DNS 并不只是使用 UDP 数据包进行通信,在 DNS 的标准中就一直能看到 TCP 协议的身影。
使用 UDP
UDP 协议在过去的几十年中其实都是 DNS 主要使用的协议,作为互联网的标准,目前的绝大多数 DNS 请求和响应都会使用 UDP 协议进行数据的传输,我们通过抓包工具就能轻松获得以 UDP 协议为载体的 DNS 请求和响应。
DNS 请求的数据都会以二进制的形式封装成如下的所示的 UDP 数据包中,下面就是一个调用 DNS 服务器获取 www.baidu.com 域名 IP 地址的请求,从第四行的 05 字节开始到最后就是 DNS 请求的内容,整个数据包中除了 DNS 协议相关的内容之外,还包含以太网、IP 和 UDP 的协议头:
所以,我们在 DNS 中存储较多的内容时,TCP 三次握手以及协议头带来的额外开销就不是关键因素了,不过我们 TCP 三次握手带来的三次网络传输耗时还是没有办法避免的,这也是我们在目前的场景下不得不接受的问题。
总结
很多人认为 DNS 使用了 UDP 协议来获取域名对应的 IP 地址,这个观点虽然没错,但是还是有一些片面,更加准确的说法其实是 DNS 查询在刚设计时主要使用 UDP 协议进行通信,而 TCP 协议也是在 DNS 的演进和发展中被加入到规范的:
DNS 在设计之初就在区域传输中引入了 TCP 协议,在查询中使用 UDP 协议;
当 DNS 超过了 512 字节的限制,我们第一次在 DNS 协议中明确了『当 DNS 查询被截断时,应该使用 TCP 协议进行重试』这一规范;
随后引入的 EDNS 机制允许我们使用 UDP 最多传输 4096 字节的数据,但是由于 MTU 的限制导致的数据分片以及丢失,使得这一特性不够可靠;
在最近的几年,我们重新规定了 DNS 应该同时支持 UDP 和 TCP 协议,TCP 协议也不再只是重试时的选择;
在这里我们重新回顾一下 DNS 查询选择 UDP 或者 TCP 两种不同协议时的主要原因:
UDP 协议
DNS 查询的数据包较小、机制简单;
UDP 协议的额外开销小、有着更好的性能表现;
TCP 协议
DNS 查询由于 DNSSEC 和 IPv6 的引入迅速膨胀,导致 DNS 响应经常超过 MTU 造成数据的分片和丢失,我们需要依靠更加可靠的 TCP 协议完成数据的传输;
随着 DNS 查询中包含的数据不断增加,TCP 协议头以及三次握手带来的额外开销比例逐渐降低,不再是占据总传输数据大小的主要部分;
无论是选择 UDP 还是 TCP,最核心的矛盾就在于需要传输的数据包大小,如果数据包小到一定程度,UDP 协议绝对最佳的选择,但是当数据包逐渐增大直到突破 512 字节以及 MTU 1500 字节的限制时,我们也只能选择使用更可靠的 TCP 协议来传输 DNS 查询和相应。
DNS 缓存机制
回顾一下平时浏览网站的情况,我们会发现两个比较有意思的现象:
80% 的时间我们都在看那些 20% 的网站;
我们会在一个网站的不同网页之间跳转,也就是不断地访问同一个域名,类似程序访问的局部性原理。
另外,DNS 域名系统给应用访问带来了额外的时延,另外由于 DNS 域名解析采用不可靠的 UDP 协议通讯,受内外部网络环境的影响较大,特别是在有丢包的情况下,导致的时延可能达到数秒。为缓解此问题,DNS 解析采用了缓存(Cache)机制。DNS 缓存可极大提升 DNS 域名解析的效率,一定程度上减少了客户端到用户之间环境对 DNS 域名解析的影响。
DNS 缓存为 DNS 技术中普遍使用的功能,在提升客户访问体验中发挥了重要作用,在实际配置使用过程中,DNS缓存使用不合理,可能会对客户体验及访问带来严重的影响。
高速缓存的缺点在于它需要消耗一定的系统资源,并增加了域名系统的复杂性。决定解析结果在高速缓存中保留多长时间是在 DNS 域名建设和维护时需要重点考虑的问题。如果缓存时间过短,则可能会导致产生多余的不必要的解析请求,如果缓存时间过长,则可能导致域名变更时客户访问恢复时间过长。
当用户访问民生网站时,在WEB浏览器里输入 www.cmbc.com.cn 时,浏览器先检查是否有该网站的缓存(域名与 IP 的映射关系),有则直接使用访问,如果没有 DNS 请求发送到客户端解析器。
假设后续环节都没有缓存,解析器将 DNS 请求发送给运营商的 Local DNS 服务器处理,运营商 Local DNS 依次向 cn.、com.cn.、cmbc.com.cn. 域名服务器发起迭代查询,并缓存查询结果,同时将结果返回给用户,实现业务访问。
在高速缓存未超时的时间内,客户浏览器再次发送到域名 www.cmbc.com.cn 的访问将不再需要经过以上迭代查询过程,将直接由客户本地电脑解析器缓存或本地 DNS 服务器缓存直接返回域名对应的 IP 地址。企业内网域名解析系统一般设计为一级(仅权威 DNS 服务器)或两级架构(包含本地 DNS 服务器和权威 DNS 服务器),不需要经过上述运营商多级域名迭代查询过程,其它解析过程与上述过程基本一致,客户端和本地 DNS 服务器同样可提供高速缓功能。
在互联网环境,缓存存在于浏览器、操作系统、运营商 Local DNS 服务器,其中运营商 Local DNS 是最重要且最不受用户和网站管理员控制的一个环节,如果缓存值设置不合理,将直接影响用户访问,会给企业形象带来负面影响。对于网站管理员来说,互联网环境的缓存面临两大方面问题:
缓存刷新不受控:一般企业为实现线路冗余都会使用多家运营商,因此域名就会被缓存在多家运营商 Local DNS 服务器,当企业域名发生改变时,涉及 Local DNS 服务器众多,在当前环境下,管理员无法刷新运营商 Local DNS 服务器缓存,在缓存未超时的情况下,客户无法获取到正确的域名进行业务访问,只能等待 Local DNS 服务器缓存超时后才能解析到正确的域名,正常访问业务,此种情况可能严重影响客户体验,导致大量的客户投诉。
刷新运营商 Local DNS 缓存:当我行一条运营商线路故障时,这条线路对应业务 DNS 记录被用户及对应运营商 Local DNS 服务器缓存,经了解,三大运营商正在开发刷新缓存的功能,电信和联通支持基于单个域名自助刷新全国对应 Local DNS 缓存,目前正处于测试阶段,预计三分钟可以完成全国 Local DNS 缓存强制刷新;移动目前支持北京地区 Local DNS 缓存记录刷新,其他地区正在计划开发中。三大运营商都支持刷新缓存的情况下,将很大程度解决缓存生效慢的问题。但是一些小运营商不在合作范围内,目前仍然没有办法更新他们的缓存,可能会有少量地区客户仍然会有所影响。
内网生产环境:生产系统变更的概率较大,且若缓存更新不及时可能会带来重大的业务影响,因此生产系统不适宜配置较长的缓存超时时间,建议参考 DNS 性能配置为秒到分钟级。
HTTP DNS 解析流程
互联网环境最大的问题是 Local DNS 缓存,缓存时间被修改,有没有方式可以避开 Local DNS?近几年 HTTP DNS 技术出现了,它是基于 HTTP 协议向 HTTP DNS 服务器发送域名解析请求,替代了基于 DNS 协议向运营商 Local DNS 发起解析请求的传统方式,有效的避开了运营商 Local DNS,就避免了缓存值被第三方修改。
客户端通过 HTTP/HTTPs 协议向 HTTP DNS 集群发起查询请求,携带用户域名和终端 IP。
服务集群查询 CDN 内部调度系统,将域名最佳访问节点 IP 响应给客户端。
客户端收到响应结果向节点发起请求。
客户端拿到最优 IP 后,发起业务访问。
若客户端访问 HTTP DNS 集群失败,可自动切换为传统方式通过运营商 Local DNS 进行解析查询。
HTTP DNS 优点
绕过运营商 Local DNS,防劫持。
能直接获取到客户端 IP 地址,更精准的返回结果,避免跨运营商。
无运营商缓存,域名变更快速生效。
HTTP DNS 可以在终端 APP DNS 缓存超时之前提前进行解析,规避了缓存再解析导致延时的问题。
与传统方式互备,当 HTTP DNS 不可用时可选择传统方式备份访问。
HTTP DNS 适用场景
HTTP DNS 主要是为 APP 类或桌面应用提供服务,如:游戏、电商、金融、音视频、社交类 APP。HTTP DNS 需要在 APP 加载相应的 SDK 对默认的 DNS 请求方式进行修改,因此 HTTP DNS 的实现需要开发配合和深度介入,另外一般一个 APP 的访问会涉及多个域名,每个域名都使用 HTTP DNS 还是只是部分域名访问使用 HTTP DNS 需要评估。更细节的实现如缓存规则、更新规则、监控及切换规则也需要进行详细的考虑和设计。