目录
DNS
DNS(Domain Name System,域名系统)是互联网的一项服务。它作为将 Domain 和 IP 地址相互映射的一个分布式数据库,能够使用户更方便地访问互联网。
源起
要想访问网络上的一台计算机,我们必须要知道它的 I P地址,但是这些地址(e.g. 243.185.187.39)只是一串数字,没有规律,因此我们很难记住。并且如果一台计算机变更 IP 后,它必须通知所有的人使用新的 IP 来找到它。
显然,直接使用 IP 地址是一个愚蠢的方案。于是人们想出了一个替代的方法,即为每一台计算机起一个名字(Hostname),然后建立 Hostname 到 IP 地址的映射关系。我们访问 Hostname,剩下的名字到地址的转换过程则由专门的服务器自动完成。
早期,名字到地址的转换过程十分简单。每台计算机保存一个 hosts 文件(e.g. /etc/hosts),里面列出所有 Hostname 和对应的 IP 地址,然后定期从一个维护此文件的站点更新里面的记录。当我们访问某个 Hostname 时,先在 hosts 文件找到对应的 IP,然后就可以建立连接。
早期的 ARPANET 就是这样做的,但是随着网络规模的扩大,这种方法渐渐吃不消了。主要有以下三个原因:
- hosts 文件变得非常大;
- Hostname 很可能冲突;
- 集中的维护站点会不堪重负(需要为几百万机器提供 hosts 文件)。
为了解决上面的问题,1983 年 Paul Mockapetris 提出了 DNS(Domain Name System,域名系统),这是一种层次的、基于域的命名方案,并且用一个分布式数据库系统加以实现。当我们需要访问一个 Domain(计算机的别名)时,应用程序会向 DNS 服务器发起一个 DNS Query Request,DNS 服务器返回该 Domain 对应的 IP 地址。
DNS 通过下面三种手段解决了上述的问题:
- 用户计算机上并没有存储所有的 Hostname 到 IP 的映射,这样避免了 hosts 文件过于庞大。
- 规定了 Domain 的命名规则,保证主机别名不会重复。
- DNS 服务器不再是单一的一台机器,而是一个层次的、合理组织的服务器集群。
域名结构
为了实现 DNS 首先需要制定一套命名规则,防止 Domain 出现重复。命名规则和我们生活中的邮寄系统类似,使用层次的地址结构。邮寄系统中要给某人邮寄物品,地址可能是这样:中国、广东省、广州市、番禺区、中山西路 12 号 XXX。而一个 Domain 看起来则是 groups.google.com。
对于整个 Internet 来说,域名层次结构的顶级由 ICANN(互联网名称与数字地址分配机构)负责管理。目前,已经有超过 250 个顶级域名,每个顶级域名可以进一步划为一些子域(二级域名),这些子域可被再次划分(三级域名),依此类推。所有这些域名可以组织成一棵树,如下图所示:
DNS 设计之初是用来建立 Domain 到 IP 地址的映射,理论上对于每一个 Domain 我们只需要在域名服务器上保存一条记录即可。这里的记录一般叫作 域名资源记录,它是一个五元组:
Domain_name Time_to_live Class Type Value
- Domain_name:指出这条记录适用于哪个域名;
- Time_to_live:用来表明记录的生存周期,也就是说最多可以缓存该记录多长时间(后面会讲到缓存机制);
- Class:一般总是 IN;
- Type:记录的类型;
- Value:记录的值,如果是 A 记录,则 value 是一个 IPv4 地址;如果是 AAAA 记录,则是一个 IPv6 地址。
因为对于一个域名来说,通常并非只记录其 IP 地址,还可能需要一些其他种类的记录,所以需要一个 Type 字段作为标识,一些常见的记录类型如下:
域名服务器集群
我们知道不能只用一台域名服务器来响应所有的 DNS 查询,因为没有一台机器能够给全球的用户提供查询服务,计算能力、存储、带宽都不允许。只能合理组织一个域名服务器集群,使他们协同工作,共同提供域名解析服务。
域名区域
接下来首先要面对的一个问题是如何合理地将所有的域名资源记录存储到不同的域名服务器上。前面说过域名的名字空间可以组织为一棵树,这里我们可以进一步将其划分为不重叠的区域(DNS zone),针对上图的域名空间,一种可能的域名划分如下图:
然后将每个区域中的多个域名服务器(其中一个是 Master,其他 Slave 服务器则用来提供数据备份、加快解析速度、保证服务可用性)关联起来,这些域名服务器统称为该区域的 “权威域名服务器(Authoritative Name Servers)”,它保存两类域名资源记录:
- 该区域内所有域名的域名资源记录。
- 父区域和子区域的域名服务器对应的域名资源记录(主要是 NS 记录)。
这样,所有的域名资源记录被分散保存在多个域名服务器中,并且所有的域名服务器也组成了一个层次的索引结构。
图中域名空间划分为 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。我们把这个域名服务器也叫作本地域名服务器。
接下来当我们需要知道一个域名对应的资源记录时,会向本地域名服务器发起请求,如果该域名恰好在本地域名服务器所辖属的域名区域(DNS Zone)内,那么可以直接返回记录;如果在本地域名服务器没有发现该域名的资源记录,就需要在整个域名空间搜索该域名。而整个域名空间的资源记录存储在一个分层的、树状联系的一系列域名服务器上,所以本地域名服务器首先要从根域名服务器开始往下搜索。
这里有一个问题就是本地域名服务器如何找到根域名服务器在哪里呢?其实域名服务器启动的时候,就会加载一个配置文件,里面保存了根域名服务器的 NS 记录(要知道根域名服务器地址一般非常稳定,不会轻易改变,并且数量很少,所以这个配置文件会很小)。找到根域名服务器之后,就可以一级一级地往下查找啦。
现在假设区域E内的某个用户想访问 math.sysu.edu.cn,那么请求的过程如下:
DNS 的协议
会话标识(2 字节):是 DNS 报文的 ID 标识,对于请求报文和其对应的应答报文,这个字段是相同的,通过它可以区分 DNS 应答报文是哪个请求的响应。
标志(2 字节):
数量字段(总共 8 字节):Questions、Answer RRs、Authority RRs、Additional RRs 各自表示后面的四个区域的数目。
- Questions 表示查询问题区域节的数量
- Answers 表示回答区域的数量
- Authoritative namesversers 表示授权区域的数量
- Additional recoreds 表示附加区域的数量
Queries 区域:
查询名:长度不固定,且不使用填充字节,一般该字段表示的就是需要查询的域名(如果是反向查询,则为 IP,反向查询即由 IP 地址反查域名),一般的格式如下图所示。
查询类:通常为 1,表明是 Internet 数据。
资源记录(RR)区域:该区域有三个,但格式都是一样的,分别是:回答区域,授权区域和附加区域。
域名(2 字节或不定长):它的格式和 Queries 区域的查询名字字段是一样的。有一点不同就是,当报文中域名重复出现的时候,该字段使用 2 个字节的偏移指针来表示。比如:在资源记录中,域名通常是查询问题部分的域名的重复,因此用 2 字节的指针来表示,具体格式是最前面的两个高位是 11,用于识别指针。其余的 14 位从 DNS 报文的开始处计数(从 0 开始),指出该报文中的相应字节数。一个典型的例子,C00C(1100000000001100,12 正好是头部的长度,其正好指向 Queries 区域的查询名字字段)。
查询类型:表明资源纪录的类型,见 1.2 节的查询类型表格所示。
查询类:对于 Internet 信息,总是 IN。
生存时间(TTL):以秒为单位,表示的是资源记录的生命周期,一般用于当地址解析程序取出资源记录后决定保存及使用缓存数据的时间,它同时也可以表明该资源记录的稳定程度,极为稳定的信息会被分配一个很大的值(比如:86400,这是一天的秒数)。
资源数据:该字段是一个可变长字段,表示按照查询段的要求返回的相关资源记录的数据。可以是 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 地址告诉本机。
- 迭代查询:本地域名服务器向根域名服务器查询,根域名服务器告诉它下一步到哪里去查询,然后它再去查,每次它都是以客户机的身份去各个服务器查询。
请求:
响应:
为什么 DNS 使用 UDP 协议
在绝大多数情况下,DNS 都是使用 UDP 协议进行通信的,DNS 协议在设计之初也推荐我们在进行域名解析时首先使用 UDP,这确实能解决很多需求,但是不能解决全部的问题。
实际上,DNS 不仅使用了 UDP 协议,也使用了 TCP 协议,不过在具体介绍今天的问题之前,我们还是要对 DNS 协议进行简单的介绍:DNS 查询的类型不止包含 A 记录、CNAME 记录等常见查询,还包含 AXFR 类型的特殊查询,这种特殊查询主要用于 DNS 区域传输,它的作用就是在多个命名服务器之间快速迁移记录,由于查询返回的响应比较大,所以会使用 TCP 协议来传输数据包。
作为被广泛使用的协议,我们能够找到非常多 DNS 相关的 RFC 文档,DNS Camel Viewer 中列出了将近 300 个与 DNS 协议相关的 RFC 文档,其中有 6 个是目前的互联网标准,有 102 个是 DNS 相关的提案,这些文档共同构成了我们目前对于 DNS 协议的设计理解。
《RFC1034 · Domain Names - Concepts and Facilities Internet Standard, 1987-11》
- DNS 查询可以通过 UDP 数据包或者 TCP 连接进行传输;
- 由于 DNS 区域传输的功能对于数据的准确有着较强的需求,所以我们必须使用 TCP 或者其他的可靠协议来处理 AXFR 类型的请求;
《RFC1035 · Domain Names - Implementation and Specification》
- 互联网支持 DNS 通过 TCP 或者 UDP 协议进行访问;
- UDP 协议携带的消息不应该超过 512 字节,超过的消息会被截断并设置 DNS 协议的 TC 位,UDP 协议对于区域传输功能是不可接受的,不过是互联网上标准查询的推荐协议。通过 UDP 协议发送的查询可能会丢失,所以需要重传策略解决这个问题;
《RFC1123 · Requirements for Internet Hosts – Application and Support Internet Standard, 1989-10》
- 未来定义的新 DNS 记录类型可能会包含超过 512 字节的信息,所以我们应该使用 TCP 协议来传输 DNS 记录;因此解析器和命名服务需要使用 TCP 协议作为 UDP 无法满足需求时的备份;
- DNS 解析器和递归服务器必须支持 UDP 协议,并且应该支持使用 TCP 协议发送非区域传输的查询;也就是说,DNS 解析器或者服务器在发送非区域传输查询时,必须先发送一个 UDP 查询,如果该查询的响应被截断,它应该尝试使用 TCP 协议重新请求;
《RFC3596 · DNS Extensions to Support IP Version 6 Internet Standard, 2003-10》
- 通过 DNS 扩展支持 IPv6 协议,每个 IPv6 占 16 个字节是 IPv4 的四倍;
《RFC5011 · Automated Updates of DNS Security (DNSSEC) Trust Anchors Independent, 2007-10》
- 新增多种资源记录为 DNS 客户端的 DNS 数据来源进行认证,记录包含的数据往往较大;
《RFC6376 · DomainKeys Identified Mail (DKIM) Signatures Internet Standard, 2011-09》
- 选择合适的键大小进行加密是需要在成本、性能和风险之间的权衡,然而大的键(4096-bit)可能没有办法直接放到 DNS UDP 响应包中直接返回;
《RFC6891 · Extension Mechanisms for DNS (EDNS(0)) Internet Standard, 2013-04》
- 使用 UDP 进行传输的 DNS 查询和响应最大不能超过 512 字节,不能支持大量 IPv6 地址或者 DNS 安全签名等记录的传输;
- EDNS 为 DNS 提供了扩展功能,让 DNS 通过 UDP 协议携带最多 4096 字节的数据;
《RFC7766 · DNS Transport over TCP - Implementation Requirements Proposed Standard, 2016-03》
- 当客户端接收到一个被阶段的 DNS 响应时,应该通过 TC 字段判断是否需要通过 TCP 协议重复发出 DNS 查询请求;
- NSSEC 的引入使得截断的 UDP 数据包变得非常常见;
- 使用 UDP 传输 DNS 的数据包大小超过最大传输单元(MTU)时可能会导致 IP 数据包的分片,RFC1123 文档中预测的未来已经到来了,唯一一个用于增加 UDP 能够携带数据包大小的 EDNS 机制被认为不够可靠;
- 所有通用 DNS 实现必须要同时支持 UDP 和 TCP 传输协议,其中包括权威服务器、递归服务器以及桩解析器;
- 桩解析器和递归解析器可以根据情况选择使用 TCP 或者 UDP 查询直接请求目标服务器,以 UDP 协议来开始发起 DNS 请求不再是强制性的,TCP 协议与 UDP 协议在 DNS 查询中可以互相替代,而不是作为重试机制;
《Specification for DNS over Transport Layer Security (TLS) Proposed Standard, 2016-05》
- 在 DNS 协议中引入 TLS 来为用户提供隐私,减少对 DNS 查询的窃听和篡改,但是 TLS 协议的引入会带来一些性能方面的额外开销;
《RFC8484 · DNS Queries over HTTPS (DoH) Proposed Standard, 2018-10》
- 义了一种通过 HTTPS 发送 DNS 查询和获取 DNS 响应的协议;
简单总结一下 DNS 的发展史,1987 年的 RFC1034 和 RFC1035 定义了最初版本的 DNS 协议,刚被设计出来的 DNS 就会同时使用 UDP 和 TCP 协议,对于绝大多数的 DNS 查询来说都会使用 UDP 数据报进行传输,TCP 协议只会在区域传输的场景中使用,其中 UDP 数据包只会传输最大 512 字节的数据,多余的会被截断;两年后发布的 RFC1123 预测了 DNS 记录中存储的数据会越来越多,同时也第一次显式的指出了发现 UDP 包被截断时应该通过 TCP 协议重试。
过了将近 20 年的时间,由于互联网的发展,人们发现 IPv4 已经不够分配了,所以引入了更长的 IPv6,DNS 也在 2003 年发布的 RFC3596 中进行了协议上的支持;随后发布的 RFC5011 和 RFC6376 增加了在鉴权和安全方面的支持,但是也带来了巨大的 DNS 记录,UDP 数据包被截断变得非常常见。
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 的协议头:
0000 b0 6e bf 6a 4c 40 38 f9 d3 ce 10 a6 08 00 45 00 .n.jL@8.......E.
0010 00 3b 97 ae 00 00 40 11 0b 0a c0 a8 32 6d 72 72 .;....@.....2mrr
0020 72 72 f3 27 00 35 00 27 6b ee 0c 5a 01 00 00 01 rr.'.5.'k..Z....
0030 00 00 00 00 00 00 03 77 77 77→05 62 61 69 64 75 .......www.baidu
0040 03 63 6f 6d 00 00 01 00 01 .com.....
虽然每一个 UDP 数据包中都包含了很多以太网、IP、UDP 以及 DNS 协议的相关内容,但是上面的 DNS 请求大小只有 73 个字节,上述 DNS 请求的响应也只有 132 个字节,这对于今天其他的常见请求来讲都是非常小的数据包:
0000 38 f9 d3 ce 10 a6 b0 6e bf 6a 4c 40 08 00 45 00 8......n.jL@..E.
0010 00 76 00 00 00 00 96 11 4c 7d 72 72 72 72 c0 a8 .v......L}rrrr..
0020 32 6d 00 35 f3 27 00 62 5b c2 0c 5a 81 80 00 01 2m.5.'.b[..Z....
0030 00 03 00 00 00 00 03 77 77 77 05 62 61 69 64 75 .......www.baidu
0040 03 63 6f 6d 00 00 01 00 01 c0 0c 00 05 00 01 00 .com............
0050 00 02 cb 00 0f 03 77 77 77 01 61 06 73 68 69 66 ......www.a.shif
0060 65 6e c0 16 c0 2b 00 01 00 01 00 00 01 18 00 04 en...+..........
0070 3d 87 a9 7d c0 2b 00 01 00 01 00 00 01 18 00 04 =..}.+..........
0080 3d 87 a9 79 =..y
UDP 和 TCP 的通信机制非常不同,作为可靠的传输协议,TCP 协议需要通信的双方通过 三次握手 建立 TCP 连接后才可以通信,但是在 30 年前的 DNS 查询的场景中我们其实并不需要稳定的连接(或者以为不需要),每一次 DNS 查询都会直接向命名服务器发送 UDP 数据报,与此同时常见 DNS 查询的数据包都非常小,TCP 建立连接会带来以下的额外开销:
- TCP 建立连接需要进行三次网络通信;
- TCP 建立连接需要传输 ~130 字节的数据;
- TCP 销毁连接需要进行四次网络通信;
- TCP 销毁连接需要传输 ~160 字节的数据;
假设网络通信所消耗的时间是可以忽略的不计的,如果我们只考虑 TCP 建立连接时传输的数据的话,可以简单来算一笔账:
- 使用 TCP 协(共 330 字节)
- 三次握手 — 14x3(Ethernet) + 20x3(IP) + 44 + 44 + 32 字节
- 查询协议头 — 14(Ethernet) + 20(IP) + 20(TCP) 字节
- 响应协议头 — 14(Ethernet) + 20(IP) + 20(TCP) 字节
- 使用 UDP 协议(共 84 字节)
- 查询协议头 — 14(Ethernet) + 20(IP) + 8(UDP) 字节
- 响应协议头 — 14(Ethernet) + 20(IP) + 8(UDP) 字节
需要注意的是,我们在这里计算结果的前提是 DNS 解析器只需要与一个命名服务器或者权威服务器进行通信就可以获得 DNS 响应,但是在实际场景中,DNS 解析器可能会递归地与多个命名服务器进行通信,这也加倍地放大了 TCP 协议在额外开销上的劣势。
如果 DNS 查询的请求体和响应分别是 15 和 70 字节,那么 TCP 相比于 UDP 协议会增加 ~250 字节和 ~145% 的额外开销,所以当请求体和响应的大小比较小时,通过 TCP 协议进行传输不仅需要传输更多的数据,还会消耗更多的资源,多次通信以及信息传输带来的时间成本在 DNS 查询较小时是无法被忽视的,TCP 连接带来的可靠性在 DNS 的场景中没能发挥太大的作用。
使用 TCP
今天的网络状况其实没有几十年前设计的那么简单,我们不仅遇到了 IPv4 即将无法分配的状况,而且还需要引入 DNSSEC 等机制来保证 DNS 查询和请求的完整性以及传输安全,总而言之,DNS 协议需要处理的数据包越来越大、数据也越来越多。
从理论上来说,一个 UDP 数据包的大小最多可以达到 64KB,这对于一个常见的 DNS 查询其实是一个非常大的数值;但是在实际生产中,一旦数据包中的数据超过了传送链路的最大传输单元(MTU,也就是单个数据包大小的上限,一般为 1500 字节),当前数据包就可能会被分片传输、丢弃,部分的网络设备甚至会直接拒绝处理包含 EDNS(0) 选项的请求,这就会导致使用 UDP 协议的 DNS 不稳定。
TCP 作为可靠的传输协议,可以非常好的解决这个问题,通过序列号、重传等机制能够保证消息的不重不漏,消息接受方的 TCP 栈会对分片的数据重新进行拼装,DNS 等应用层协议可以直接使用处理好的完整数据。同时,当数据包足够大的时候,TCP 三次握手带来的额外开销比例就会越来越小,与整个包的大小相比就会趋近于 0:
- 当 DNS 数据包大小为 500 字节时,TCP 协议的额外开销为 ~41.2%;
- 当 DNS 数据包大小为 1100 字节时,TCP 协议的额外开销为 ~20.7%;
- 当 DNS 数据包大小为 2300 字节时,TCP 协议的额外开销为 ~10.3%;
- 当 DNS 数据包大小为 4800 字节时,TCP 协议的额外开销为 ~5.0%;
所以,我们在 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 域名建设和维护时需要重点考虑的问题。如果缓存时间过短,则可能会导致产生多余的不必要的解析请求,如果缓存时间过长,则可能导致域名变更时客户访问恢复时间过长。
基于缓存的 DNS 域名解析流程
企业的 DNS 域名解析系统一般分为 互联网域名解析系统 和 企业内网域名解析系统。
- 互联网域名解析系统主要实现互联网用户对企业系统的访问,如:企业官方网站、电子商务平台、手机银行等,互联网域名解析系统需要企业自己的权威域名系统与运营商多级域名解析节点配合使用,以实现域名的解析功能。
- 企业内网域名解析系统一般由企业自行搭建和维护,实现企业内部用户对企业内部系统的域名解析或企业内部系统间互访的域名解析功能。
针对具体某一具体域名的访问,DNS域名解析过程实际上是一个相当复杂的过程,需要经过多级迭代查询才能成功获取业务地址,以用户通过互联网访问民生银行官方网站系统 www.cmbc.com.cn 为例,其详细过程如下图所示:
- 当用户访问民生网站时,在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 服务器同样可提供高速缓功能。
DNS 缓存使用面临的问题
缓存技术在用户业务访问过程中起到了加速访问、提升用户体验的重要作用,若缓存设置使用不当,则可能给用户访问体验带来负面影响,甚至影响用户正常业务访问。缓存被使用在互联网非受控环境和内网相对可控环境,在不同的环境会面临不同的使用问题,下面来看看缓存在互联网和内网环境使用中可能会面临哪些问题。
互联网环境缓存应用面临的问题
在互联网环境,缓存存在于浏览器、操作系统、运营商 Local DNS 服务器,其中运营商 Local DNS 是最重要且最不受用户和网站管理员控制的一个环节,如果缓存值设置不合理,将直接影响用户访问,会给企业形象带来负面影响。对于网站管理员来说,互联网环境的缓存面临两大方面问题:
-
缓存刷新不受控:一般企业为实现线路冗余都会使用多家运营商,因此域名就会被缓存在多家运营商 Local DNS 服务器,当企业域名发生改变时,涉及 Local DNS 服务器众多,在当前环境下,管理员无法刷新运营商 Local DNS 服务器缓存,在缓存未超时的情况下,客户无法获取到正确的域名进行业务访问,只能等待 Local DNS 服务器缓存超时后才能解析到正确的域名,正常访问业务,此种情况可能严重影响客户体验,导致大量的客户投诉。
-
解析权和缓存值被修改:一些小运营商出于规模、节约成本的考虑,将域名转发到其他运营商进行解析,并把收到的域名缓存值更改为较长的值,这会带来两个方面的问题:1)权威DNS接收到的请求IP地址不是客户所在运营商IP地址,客户的请求可能会被引导至错误的线路,导致客户访问慢;2)企业域名发生改变后将等待更长的超时时间才能正常访问业务;同样会给客户带来较差的体验。
企业内网缓存应用面临的问题
企业内网环境相对简单,通常情况下用户会直接向权威服务器发起域名解析请求,内网域名解析路径较互联网可控且环境稳定,无运营商递归查询环节,因此基本不存在不可控的三方服务器和缓存被修改的问题。
企业内网环境包括两类用户,一类为终端用户,例如柜员、业务管理人员、办公人员,这类用户使用电脑通过浏览器访问目标业务;另一类为生产系统,这类用户为服务器,通过应用软件访问目标业务。两类用户涉及的基础运行环境各不相同,缓存在使用中面临的问题也会有所不同。
-
终端用户面临的问题:内网终端用户与互联网用户类似,差异是内网无中间运营商,它的缓存存在于浏览器、终端操作系统,若缓存值设置过长,当出现业务地址变更的时候,用户将无法访问业务,可能会影响办公或者影响客户办理业务,须等待本地缓存超时方可恢复。在终端用户层面,目标业务域名改变一般为单个系统,不会出现大面积变更的情况,且对象为内网用户,可建立统一的标准规范,因此影响范围相对较小。
-
生产系统面临的问题:为实现业务快速切换以及运维便利,企业生产系统之间也开始大力推广通过域名方式访问,但生产系统使用的操作系统、编程语言等各不相同。
DNS 缓存在实际应用中的考虑
DNS 缓存在使用中会面临缓存无法刷新、被修改以及缓存控制复杂等问题,无论在互联网还是内网,缓存设置不合理都将导致用户无法访问业务,影响用户体验,那么缓存设置就显得尤为重要,它在实际应用中应该如何考虑?
首先,哪些情况下域名会发生改变,从而出现影响用户访问的情况:
- 主动操作:由于系统新建迁移、灾备切换等有计划的变更操作。
- 被动操作:当出现故障需要紧急更改域名对应的服务地址恢复业务。
不同场景产生的影响时间以及紧迫程度有所不同,就有不同的考虑因素,下面针对不同的场景分别进行介绍。
主动操作之缓存考虑因素
针对主动操作的情况,有计划性,会有充足的时间准备,内网和互联网环境均可提前将域名对应的缓存值更改为更小的值,操作等待时间大于设置的缓存值即可,以确保递归服务器缓存了设置较小的 TTL 值,待操作完成后再更改回原有 TTL 值,无需人工干预。
另外,避免缓存带来影响,还可以规范架构部署,例如通过负载均衡或者虚地址方式对外提供服务,可减少域名变化的情况,负载均衡或者虚地址方式可把操作隐藏在服务端,不会传达到客户端,可直接避免缓存时间给用户带来的影响。
被动操作之互联网缓存考虑因素
互联网用户直接面向运营商,面临的问题是无法刷新缓存和缓存值被修改,要解决域名更改后无法快速生效的问题,需要从运营商入手:
- 刷新运营商 Local DNS 缓存:当我行一条运营商线路故障时,这条线路对应业务 DNS 记录被用户及对应运营商 Local DNS 服务器缓存,经了解,三大运营商正在开发刷新缓存的功能,电信和联通支持基于单个域名自助刷新全国对应 Local DNS 缓存,目前正处于测试阶段,预计三分钟可以完成全国 Local DNS 缓存强制刷新;移动目前支持北京地区 Local DNS 缓存记录刷新,其他地区正在计划开发中。三大运营商都支持刷新缓存的情况下,将很大程度解决缓存生效慢的问题。但是一些小运营商不在合作范围内,目前仍然没有办法更新他们的缓存,可能会有少量地区客户仍然会有所影响。
- 避免缓存被修改:使用 HTTP DNS。
被动操作之内网缓存考虑因素
内网生产环境的域名访问在推广初期,从无到有,看似简单的域名请求,需要增加到JDK、操作系统等环节的访问,这些环节缓存开启情况不同,要实施域名访问需重点考虑是否要开启缓存,缓存时间配置为多少合适。
如何设置 TTL 值
缓存超时间(TTL)是缓存技术中的一个重要参数,对缓存效果起着决定性的作用。TTL 时间设置较长,域名就能缓存更长时间,用户就能更快实现访问,TTL 时间设置较短,域名就更快超时,用户就需要等待域名解析才能正常访问。因此,TTL 时间设置的长短将直接影响用户的体验效果。TTL 值设置大小需要根据业务特性、业务环境、DNS 服务器性能等因素综合考虑,针对不同应用场景的缓存值设置规则可参考如下建议:
- 互联网环境:互联网环境由于运营商 Local DNS 缓存服务器的存在,具有不可控性,因此不适宜配置较长的缓存时间,建议参考 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 需要评估。更细节的实现如缓存规则、更新规则、监控及切换规则也需要进行详细的考虑和设计。
参考文档
https://mp.weixin.qq.com/s/G8iTlYj5b5Lw4fJDo9O-xA
https://mp.weixin.qq.com/s/owtl7hQYcz5Um2RS_5sUHw
https://mp.weixin.qq.com/s/x_lE2zhvD8pZjmHdWEZXEQ
https://blog.csdn.net/tianxuhong/article/details/74922454
转载:https://blog.csdn.net/Jmilk/article/details/105719125