Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

性能优化之 DNS Prefetch #3

Open
barretlee opened this issue Mar 2, 2017 · 6 comments
Open

性能优化之 DNS Prefetch #3

barretlee opened this issue Mar 2, 2017 · 6 comments

Comments

@barretlee
Copy link
Owner

barretlee commented Mar 2, 2017

认领人须知

  • 需要讲清楚这个知识点的实现原理
  • 分析它能够解决多大的问题,最大的收益点,最大的收益(这个不好写,我有点经验,可以后续补充)
  • 思考如何不漏掉域名
@barretlee barretlee mentioned this issue Mar 2, 2017
17 tasks
@barretlee barretlee added this to the 性能相关基础知识点研究 milestone Mar 2, 2017
@proYang
Copy link
Collaborator

proYang commented Mar 4, 2017

我来试试吧(✿◡‿◡)

@proYang
Copy link
Collaborator

proYang commented Mar 12, 2017

原理

DNS原理

DNS(Domain Name System, 域名系统),作为域名和IP地址相互映射的一个分布式数据库。
当浏览器访问一个域名的时候,需要解析一次DNS,获得对应域名的ip地址。在解析过程中,按照浏览器缓存系统缓存路由器缓存ISP(运营商)DNS缓存根域名服务器顶级域名服务器主域名服务器的顺序,逐步读取缓存,直到拿到IP地址。

DNS Prefetch原理

DNS Prefetch就是根据浏览器定义的规则,提前解析之后可能会用到的域名,使解析结果缓存到系统缓存中,缩短DNS解析时间,来提高网站的访问速度。

Architecture
Chromium's implementation of DNS prefetching does not use the browser's network stack at all. Instead, it relies on external threads to resolve the names, thereby warming the DNS cache of the operating system (completely ignoring any cache in the application network stack). The advantage of this approach was that it was completely compatible with all network stacks (it is external), and prevented accidental regressions on the main network stack.
Since some DNS resolutions can take a long time, it is paramount that such delays in one resolution should not cause delays in other resolutions. Toward this end (on Windows, where there is no native support for asynchronous DNS resolution), Chromium currently employs 8 completely asynchronous worker threads to do nothing but perform DNS prefetch resolution. Each worker thread simply waits on a queue, gets the next requested domain name, and then blocks on a synchronous Windows resolution function. Eventually the operating system responds with a DNS resolution, the thread then discards it (leaving the OS cache warmed!), and waits for the next prefetch request. With 8 threads, it is rare than more than one or two threads will block extensively, and most resolution proceed rather quickly (or as quickly as DNS can service them!). On Debug builds, the "about:histograms/DNS.PrefetchQueue" has current stats on the queueing delay.

Chromium不使用浏览器的网络堆栈,直接使用操作系统的缓存。通过8个异步线程执行预解析,每个线程处理一个队列,来等待域名的响应,最终操作系统会响应一个DNS结果给线程,然后线程丢弃它,等待下一个预解析请求。

如何使用DNS Prefetch

  • 自动解析
    Chromium使用超链接的href属性来查找要预解析的主机名。当遇到a标签,Chromium会自动将href中的域名解析为IP地址,这个解析过程是与用户浏览网页并行处理的。但是为了确保安全性,在HTTPS页面中不会自动解析。
  • 手动解析
    在页面添加如下标记
    <link rel="dns-prefetch" href="//img.alicdn.com">
    上面的link标签会让Chromium预取"img.alicdn.com"的解析。
  • 自动解析控制
    当我们希望在HTTPS页面开启自动解析功能时,添加如下标记
    <meta http-equiv="x-dns-prefetch-control" content="on">
    当我们希望在HTTP页面关闭自动解析功能时,添加如下标记
    <meta http-equiv="x-dns-prefetch-control" content="off">

解决的问题及收益

DNS Prefetch有效缩短了DNS的解析时间。
如果浏览器最近将一个域名解析为IP地址,所属的操作系统将会缓存,下一次DNS解析时间可以低至0-1ms。 如果结果不在系统本地缓存,则需要读取路由器的缓存,则解析时间的最小值大约为15ms。如果路由器缓存也不存在,则需要读取ISP(运营商)DNS缓存,一般像taobao.combaidu.com这些常见的域名,读取ISP(运营商)DNS缓存需要的时间在80-120ms,如果是不常见的域名,平均需要200-300ms。一般的网站在运营商这里都能查询的到,所以普遍来说DNS Prefetch可以给一个域名的DNS解析过程带来15-300ms的提升,尤其是一些大量引用很多其他域名资源的网站,提升效果就更加明显了。
Chromium对底层缓存进行了建模,当Chrome浏览器启动的时候,就会自动的快速解析浏览器最近一次启动时记录的前10个域名。所以你经常访问的网址就没有DNS解析的延迟,打开速度更快。

如何不漏掉域名

  1. 借助开发者工具,查看所有静态资源域名,并添加link标签,手动解析
  2. 如果是HTTPS网页,考虑是否需要对超链接自动解析,如果需要,添加对应的meta标签
  3. 检查js中发起的跳转至其他域名的情况,对于这些域名,添加link标签,手动解析
  4. 检查是否存在重定向的域名,对于重定向的域名,将重定向之后的域名,添加link标签,手动解析

参考资料

DNS解析过程原理
Chromium DNS Prefetching官方文档

@barretlee
Copy link
Owner Author

DNS Prefetch

一个网络请求的生命周期,需要经过这么几个步骤:DNS 查询、建立 TCP 连接、数据传输、断开连接。

DNS 查询就是将域名转换成 IP 的过程,这个过程短的话 2ms 几乎无感,长则可能达到几秒钟。DNS Prefetch 的原理就是在 HTTP 建立之前,将 DNS 查询的结果缓存到系统/浏览器中,提升网页的加载效率。

现代浏览器在 DNS Prefetch 上做了两项工作:

  • html 源码下载完成后,会解析页面的包含链接的标签,提前查询对应的域名
  • 对于访问过的页面,浏览器会记录一份域名列表,当再次打开时,会在 html 下载的同时去解析 DNS

这个能力,各浏览器厂商很早就已经支持了,包括 IE9+、Firefox 3.5+、Chrome 等,具体可以查看 Caniuse DNS prefetch.

效果查看

打开 WebPageTest,输入 www.taobao.com,经过大概半分钟的分析后,可以从下方的 Request Details 列表中找到 DNS Lookup 项,点击此项,可以看到页面所有的资源按照 DNS 查询时长的排序时间:

Taobao - DNS Prefetch

由于是从其他国家访问大陆的淘宝网首页,所以看到加载速度是很慢的;有 6 个域名的查询时间超过了 100ms,也就是说这 6 个域名下的资源,至少需要在 100ms 后才会开始加载,Time To First Byte 时间特别长。打开淘宝网首页源码,看看 DNS Prefetch 的几个标签,你会发现,这几个域名也并没有出现在 meta 中。

用这个栗子来解说还是可以很好说明 DNS Prefetch 对性能的影响的。

使用场景

如果你对 DNS Prefetch 有了比较全面的理解,那应该也会更加清楚这项技术的使用场景。并非所有页面都要加上此项,一般在整个站点的入口页做这个工作就行了,毕竟一个站点下用到的大多数域名都会在首页体现。

还有几个比较常见的场景:

  • 登录页,我们可以提前在页面上进行下跳页用到资源的 DNS Prefetch
  • 新用户访问,后端可以通过 Cookie 判断是否为首次进入站点,对于这类用户,DNS Prefetch 可以比较明显地提升访问速度

并非所有的域名都需要提前 Prefetch,超过 10 个(左右)并发的 Preftch 并不一定会提升网页的加载效率,你可以在网页上写 1000 个 Prefetch (随机域名,系统肯定无混存)试试看,是否对页面性能有损耗。

小结

DNS Prefetch 是对网页性能优化的一个通用方案,对国际化的站点来说可能效果更加明显。学习成本和理解成本低,开发者可以放心大胆地用到自己的网页上。

@rj03hou
Copy link

rj03hou commented Nov 22, 2018

但是看一些资料上说,安全方面建议将dns prefetch关闭

@fenfenyan
Copy link

看到一篇文章讲 DNS-prefetch 存在的安全隐患 https://www.htmlgoodies.com/beyond/webmaster/how-your-browser-speeds-up-cross-domain-loading-using-dns-prefetching.html
DNS prefetching may amplify disclosure attacks by inferring the likely search terms issued by clients using a given DNS resolver. Sites like Google already try to guess at which site's you're likely to visit (or might want to visit) based on previous search queries. Anyone who can intercept your search criteria can also send back dubious or malicious links.

@WeijieZhu0204
Copy link

DNS prefetch即DNS预解析,通过向DNS服务器提前获取域名的实际指向,可以有效减少浏览器在发送请求时的DNS解析时间。

DNS解析是一个多级处理的过程,通过向不同层级的DNS服务器查询,最终得到域名指向。当然不同层级都可实现各自的缓存,我们电脑本地就有一层DNS缓存,一般缓存有50-200个域名解析。在这里不深入解释DNS解析的过程。

如何设置DNS Prefetch

1、浏览器自动处理

浏览器在加载一个html页面的时候,会自动获取当前页面所有a标签href属性中的域名,并且执行DNS预解析。a标签以外的标签不做处理。

需要注意的是,当浏览器遇到https页面,处于安全性考虑,不会自动进行DNS预解析。需要手动加上一个meta开关。

<meta http-equiv="x-dns-prefetch-control" content="on">	

2、手动配置

在页面顶部加入如下标签即可。

<link rel="dns-prefetch" href="//host_name_to_prefetch.com">

Chromium实现

DNS解析可能耗费大量的时间,在某些系统上(如Windows)甚至不支持异步的DNS解析。为了防止DNS预解析阻塞页面渲染影响用户体验,Chromium内核没有使用浏览器的网络堆栈实现DNS prefetch。而是启动了8个异步的worker线程专门负责DNS预解析。因此DNS预解析与首屏加载没有直接关系,既不会加速首屏渲染,也不会阻塞首屏渲染

应用场景

参考上面的结论,我们知道声明在html中的JavaScript、CSS、图片等静态资源的加载,并不会直接受益于DNS prefetch。

那么我们什么时候会用上DNS预解析呢?

1、在整个站点的入口声明DNS预解析。

通过在站点入口声明了后续其他子页面和子站点可能会访问的域名,浏览器提前进行了DNS预解析。这样用户在访问其他子站点的时候将会间接受益于入口的预解析,降低了DNS lookup所消耗的时间。

2、根据实际情况配置当前页面的DNS预解析。

声明后续操作可能会用到的域名,如发送的异步请求、动态加载图片等等。

3、并不是DNS prefetch设置的越多越好,虽然是异步线程,实际上还是占用设备的带宽,造成资源竞争。

Referer

The Chromium Projects - DNS Prefetching

Done.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants