简介:DNS 延迟的原因和缓解措施
随着网页变得越来越复杂,DNS 查询会成为浏览体验中的重要瓶颈。每当客户端需要通过网络查询 DNS 解析器时,产生的延迟可能就会很明显,具体取决于解析器必须查询的域名服务器的距离和数量(虽然很少会出现,但可能会发生超过 2 个)。例如,以下屏幕截图显示了 Page Speed 网页性能衡量工具所报告的时间。 每个竖条表示从页面中引用的资源;黑色段表示 DNS 查询次数。在该页面中,系统在加载该页面的前 11 秒内会进行 13 次查询。 虽然几次查询是并行执行的,但屏幕截图显示需要 5 次串行查询时间,占总 11 秒网页加载时间的几秒钟。
DNS 延迟时间有两个组成部分:
- 客户端(用户)与 DNS 解析服务器之间的延迟时间。 在大多数情况下,这主要是由于联网系统中通常的往返时间 (RTT) 限制造成的:客户端和服务器机器之间的地理位置距离;网络拥塞;丢包和较长的重新传输延迟(平均一秒);服务器过载、拒绝服务攻击等。
- 解析服务器与其他域名服务器之间的延迟时间。
导致延迟时间的主要原因包括:
- 缓存未命中。 如果无法从解析器的缓存传送响应,但需要以递归方式查询其他域名服务器,则增加的网络延迟时间会相当大,尤其是当权威服务器在地理位置上远程运行时更是如此。
- 预配不足。 如果 DNS 解析器过载,它们必须将 DNS 解析请求和响应加入队列,并且可能会开始丢弃和重新传输数据包。
- 恶意流量。 即使过度预配 DNS 服务,DoS 流量也可能会给服务器带来过重的负载。同样,Kaminsky 式攻击可能涉及对查询进行泛洪攻击,而查询一定会绕过缓存并需要传出请求才能进行解析。
我们认为缓存未命中因素是导致 DNS 延迟的最主要原因,下文将对此进行进一步讨论。
缓存未命中
即使解析器具有丰富的本地资源,也很难避免与远程域名服务器通信相关的基本延迟。换言之,假设解析器配置得足够好,使缓存命中在服务器端的时间为零,则缓存未命中在延迟时间方面仍然非常昂贵。 为了处理未命中的情况,解析器必须与至少一个(但通常是两个或多个)外部域名服务器通信。 运行 Googlebot 网页抓取工具时,我们发现域名服务器的平均解析时间为 130 毫秒。 但是,由于 UDP 数据包丢失和服务器无法访问,完全有 4-6% 的请求只是超时。如果我们考虑诸如数据包丢失、域名服务器不可用、DNS 配置错误等故障,则实际的平均端到端解析时间为 300-400 毫秒。但是,差异也会很长,并且尾巴也会很长。
虽然各 DNS 服务器之间的缓存未命中率可能会有所不同,但从根本上说,很难避免缓存未命中,原因如下:
- 互联网规模和增长情况。 简单来说,随着互联网的不断发展,无论是新用户还是新网站,大部分内容都只关注边缘利益。虽然少数网站(并由此引发了 DNS 名称)非常受欢迎,但大多数只对少数用户感兴趣,很少访问,因此大多数请求都会导致缓存未命中。
- 存留时间 (TTL) 值较短。 DNS TTL 值呈下降趋势意味着解析需要更频繁的查询。
- 缓存隔离。 DNS 服务器通常部署在负载平衡器后面,负载平衡器会将查询随机分配给不同的机器。这会导致每个服务器维护单独的缓存,而无法重复使用共享池中的缓存分辨率。
缓解措施
在 Google 公共 DNS 中,我们实现了多种方法来缩短 DNS 查找时间。其中一些方法是相当标准的;另一些是实验性方法:
- 充分预配服务器,以处理来自客户端流量(包括恶意流量)的负载。
- 防止 DoS 和放大攻击。 虽然这主要是一个安全问题,并且对已关闭的解析器的影响小于打开的解析器,但防范 DoS 攻击还可以消除 DNS 服务器带来的额外流量负担,从而提高性能。如需了解我们用于最大限度降低攻击几率的方法,请参阅有关安全优势的页面。
- 针对共享缓存提供负载均衡,以提高整个服务集群的汇总缓存命中率。
- 覆盖全球,靠近所有用户。
充分预配服务集群
缓存 DNS 解析器必须执行比权威域名服务器执行更昂贵的操作,因为许多响应无法从内存传送;相反,它们需要与其他域名服务器通信,因此需要大量的网络输入/输出。此外,开放的解析器非常容易受到缓存中毒尝试的攻击,这会提高缓存未命中率(此类攻击专门针对无法通过缓存解析的虚假名称发送请求),以及会增加流量负载的 DoS 攻击。 如果解析器未充分预配,无法跟上负载,则可能会对性能产生非常不利的影响。数据包会被丢弃并需要重新传输,域名服务器请求必须加入队列,等等。所有这些因素都会造成延误。
因此,请务必配置 DNS 解析器,以应对大量输入/输出。 这包括处理可能的 DDoS 攻击,对于这种情况,唯一的有效解决方案是过度预配多台机器。但同时,切勿在添加机器时降低缓存命中率;这需要实现有效的负载均衡政策,我们将在下文中对此进行讨论。
针对共享缓存的负载均衡
实际上,如果负载均衡未正确完成,通过添加机器来扩缩解析器基础架构可能会适得其反,并降低缓存命中率。在典型的部署中,多台机器位于一个负载平衡器后面,负载平衡器使用轮循等简单的算法将流量平均分配给每台机器。这样做的结果是,每台机器都会维护自己的独立缓存,缓存的内容在不同的机器之间会隔离开来。如果每个传入查询都分配到一台随机机器,具体取决于流量的性质,实际缓存未命中率可能会按比例提高。 例如,对于重复查询的 TTL 较长的名称,缓存未命中率可以按集群中的机器数量增加。(对于 TTL 非常短、查询频率非常低或导致响应无法缓存(0 TTL 和错误)的名称,添加机器并不会真正影响缓存未命中率。)
如需提高可缓存名称的命中率,请务必对服务器进行负载均衡,以免缓存被碎片化。在 Google 公共 DNS 中,我们有两个级别的缓存。在一个机器池中,离用户很近,每台机器的小型缓存都包含最常用的名称。如果无法从此缓存中满足某个查询,该查询会被发送到按名称分区的另一个机器池。对于此第二级缓存,具有相同名称的所有查询都会发送到同一台机器,在该机器中,名称要么已缓存,要么未被缓存。
分布服务集群以实现广泛的地理覆盖范围
对于封闭的解析器,这其实并不是问题。对于开放式解析器,您的服务器离用户越近,他们在客户端端看到的延迟就越少。此外,具有足够的地理覆盖范围可以间接缩短端到端延迟时间,因为域名服务器通常会返回针对 DNS 解析器位置进行了优化的结果。也就是说,如果 content provider 托管全球的镜像网站,该提供商的域名服务器将返回距离 DNS 解析器最近的 IP 地址。
Google 公共 DNS 托管在世界各地的数据中心,它使用任播路由将用户发送到地理位置最近的数据中心。
此外,Google 公共 DNS 支持 EDNS 客户端子网 (ECS)。ECS 是一种 DNS 协议扩展,可供解析器将客户端位置转发到域名服务器,后者可以返回针对实际客户端 IP 地址(而非解析器 IP 地址)进行了优化的位置敏感响应。如需了解详情,请参阅此常见问题解答。 Google 公共 DNS 会自动检测支持 EDNS 客户端子网的域名服务器。