You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
幸运的话,这个主机名已经被缓存过。否则,必须先发起一个 DNS Query。如果没有,则在任何其他工作可能发生之前,必须先发送DNS查询。执行DNS查找所需的时间将根据您的互联网提供商,网站的受欢迎程度以及主机名处于中间缓存的可能性以及该域的权威服务器的响应时间而变化。换句话说,有很多因素在起作用,但一个DNS查找到几百毫秒是不寻常的。
enum ResolutionMotivation {
MOUSE_OVER_MOTIVATED, // Mouse-over initiated by the user.
OMNIBOX_MOTIVATED, // Omni-box suggested resolving this.
STARTUP_LIST_MOTIVATED, // This resource is on the top 10 startup list.
EARLY_LOAD_MOTIVATED, // In some cases we use the prefetcher to warm up
// the connection in advance of issuing the real
// request.
// The following involve predictive prefetching, triggered by a navigation.
// The referring_url_ is also set when these are used.
STATIC_REFERAL_MOTIVATED, // External database suggested this resolution.
LEARNED_REFERAL_MOTIVATED, // Prior navigation taught us this resolution.
SELF_REFERAL_MOTIVATED, // Guess about need for a second connection.
// <snip> ...
};
The many facets of performance
现代web应用
资源请求生命周期
给定Web上的资源的URL,浏览器首先检查其本地和应用程序高速缓存。如果您先前已经获取了资源并提供了适当的缓存头(Expires,Cache-Control等),那么我们可能允许我们使用本地副本来满足请求 - 最快的请求是一个请求没有做或者,如果我们必须重新验证资源,如果它已过期,或者如果我们以前没有看到它,则必须调度昂贵的网络请求。
给定主机名和资源路径,Chrome首先检查检查现有已建立的连接 - 套接字由{scheme,host,port}组成。或者,如果已配置代理或指定代理自动配置(PAC)脚本,则Chrome将通过相应的代理检查连接。 PAC脚本允许基于URL或其他指定规则的不同代理,每个代理可以拥有自己的套接字池。最后,如果上述条件都不匹配,则请求必须首先将主机名解析为其IP地址,即DNS查询。
幸运的话,这个主机名已经被缓存过。否则,必须先发起一个 DNS Query。如果没有,则在任何其他工作可能发生之前,必须先发送DNS查询。执行DNS查找所需的时间将根据您的互联网提供商,网站的受欢迎程度以及主机名处于中间缓存的可能性以及该域的权威服务器的响应时间而变化。换句话说,有很多因素在起作用,但一个DNS查找到几百毫秒是不寻常的。
通过解析的IP地址,Chrome现在可以打开到目的地的新TCP连接,这意味着我们必须执行“三次握手”:SYN> SYN-ACK> ACK。该交换机为每个新的TCP连接添加了一个完整的延迟延迟 - 不需要快捷方式。根据客户端和服务器之间的距离以及选择的路由路径,这可能会产生几十到几百甚至几千毫秒的延迟。所有这些工作和延迟之前,甚至一个单字节的应用程序数据已经触电!
TCP握手完成后,如果我们连接到安全目的地(HTTPS),则必须进行SSL握手。这可以增加客户端和服务器之间的两个额外的RTT。如果SSL会话被缓存,那么我们可以通过一次额外的RTT。
最后,Chrome能够发送HTTP请求(上述的Nav Timing图中的requestStart)。一旦收到请求,服务器可以处理该请求,然后将响应数据返回到客户端。这导致了最少的网络往返时间,以及服务器上的处理时间。然后一个请求就完成了。但是,如果是一个HTTP重定向的话?在这种情况下,我们可能必须重复整个周期。在您的网页上有几个无偿重定向?你可能想重新审视那个决定!
你一直在计算所有的延误吗?为了说明这个问题,我们来假设典型宽带连接的最坏情况:本地缓存未命中,之后是相对较快的DNS查询(50ms),TCP握手,SSL协商以及相对快速(100ms)的服务器响应时间,往返时间为80毫秒(美国大陆的平均往返行程):
DNS的50ms
80ms用于TCP握手(一个RTT)
160ms用于SSL握手(两个RTT)
40ms请求到服务器
100ms用于服务器处理
40ms用于响应服务器
对于单个请求,这是470毫秒,与实际服务器处理时间相比,网络延迟开销超过80%,我们有一些工作要做!实际上,甚至470毫秒可能是一个乐观的估计:
如果服务器响应不适合初始TCP拥塞窗口(4-15 KB),则会引入一个或多个额外的延迟往返时间
如果我们需要获取缺少的证书或执行在线证书状态检查(OCSP),SSL延迟可能会更糟,这两者都需要一个全新的TCP连接,这可以增加数百甚至数千毫秒的额外延迟。
何为“足够快”?
DNS,握手和往返时间的网络开销在我们早期的情况下占主导地位 - 服务器响应时间仅占总延迟的20%! 但是,在宏伟计划的事情上,这些延误有关系吗? 如果你正在阅读这篇文章,那么你可能已经知道答案了:是的,非常重要。
上表同样适用于页面的性能表现:渲染页面,或者至少提供250毫秒以内的视觉反馈,以保持用户的参与。这就是针对速度。谷歌,亚马逊,微软以及数千个其他网站的研究表明,额外的延迟会直接影响到您网站的底线:更快的网站产生更多的网页浏览量,更高的用户参与度,以及更高的转化率。
所以,你有它,我们的最佳延迟预算是250毫秒,但正如我们在上面的例子中看到的,DNS查询,TCP和SSL握手的组合以及请求的传播时间加起来为370毫秒。我们比预算低50%,而且在服务器处理时间我们还没有考虑到!
对于大多数用户甚至Web开发人员来说,DNS,TCP和SSL延迟是完全透明的,并且在我们中的少数人下降或思考的网络层进行协商。然而,这些步骤中的每一个对于整体用户体验至关重要,因为每个额外的网络请求可以增加几十或几百毫秒的延迟。这就是为什么Chrome的网络堆栈远远超过一个简单的套接字处理程序。
深入Chrome的网络模块
多进程架构
Chrome的多进程架构对浏览器中每个网络请求的处理方式都有重要的意义。Chrome实际上支持四种不同的执行模型,以确定如何执行流程分配。
默认情况下,桌面Chrome浏览器使用每个站点的流程模型,将不同的站点彼此隔离,但将同一站点的所有实例分组到同一进程中。 但是,为了简单起见,让我们假设一个最简单的例子:每个打开选项卡的一个不同的过程。 从网络性能的角度来看,这里的差异并不大,但是流程标签模型更容易理解。
每一个tab有一个渲染进程(render process),其中包括了用于解析页面(interpreting)和排版(layout out)的WebKit的排版引擎(layout engine), 即上图中的HTML Render。还有V8引擎和两者之间的DOM Bindings, 如果您对这部分很好奇,可以看Chromium wiki。
这些“渲染”过程中的每一个都在具有对用户计算机(包括网络)的有限访问权限的沙盒环境中执行。 为了获得对这些资源的访问,每个渲染过程都与浏览器(内核)进程通信,这可以对每个渲染器强加安全性和访问策略。
Inter-process communication (IPC) and Multi-process resource loading
Chrome中的渲染器和内核进程之间的所有通信都是通过IPC完成的。 在Linux和OSX上,使用了一个socketpair(),它提供了用于异步通信的命名管道传输。 来自渲染器的每条消息都被序列化并传递给专用的I/O线程,它将其分派到内核进程。 在接收端,内核进程提供了一个过滤器接口,允许Chrome拦截资源IPC请求(请参阅ResourceMessageFilter),这些请求应由网络堆栈处理。
优点:资源请求在I/O线程上处理;UI活动和网络事件不会相互干扰。资源过滤器运行在浏览器进程里的I/O线程,拦截资源请求消息,转发给浏览器进程的ResourceDispatcherHost单例。
单例界面允许浏览器控制每个渲染器对网络的访问,但也可以实现高效,一致的资源共享:
就渲染过程而言,它只是简单地通过IPC发送资源请求消息,该消息被标记为唯一的请求ID到浏览器进程,并且浏览器内核进程处理其它的。
Cross-platform resource fetching
网络堆栈被实现为大多数单线程(有单独的缓存和代理线程)跨平台库,这允许Chrome重用相同的基础架构并提供相同的性能优化,以及提供在所有的平台上更大的优化机会。
网络实现源码:
这些代码是值得阅读的,每个组件都有完善的文档和单元测试。
Architecture and performance on mobile platforms
移动端区别:
没有所谓的“代表性手机设备”。参差不齐的硬件能力。为了提供最佳性能,Chrome必须适应每个设备的操作限制。幸运的是,各种执行模式允许Chrome做到这一点!
针对网络性能优化:使用和其它版本一样的网络协议栈。根据推断优化技术的优先级,套接字超时,管理逻辑,缓存大小这些可变因素调整设备和使用中的网络的能力。
开启WI-FI,才会prerender。保留电池寿命,移动端chrome可能会选择空闲套接字的懒关闭- 套接字只有在打开了新的套接字后才会关闭来最大化降低无线电的使用程度。
Speculative optimization with Chrome's Predictor #
Chrome有一个单例的Predictor对象,在浏览器内核中初始化。负责观察网络方式,学习,预测未来的用户行为。由Predictor处理的一些例子:
当您使用它时,Chrome会学习网络的拓扑结构以及您自己的浏览模式。 如果它的工作很好,它可以消除每个导航数百毫秒的延迟,并使用户更接近“即时页面加载”的圣杯。 为了实现这一目标,Chrome利用四项核心优化技术:
调用这些技术中的一个或多个的每个决定针对大量约束进行了优化。 毕竟,每个都是一个投机优化,这意味着如果做得不好,可能会导致不必要的工作和网络流量,甚至更糟的是,对用户触发的实际导航的加载时间有负面影响。
Chrome如何解决这个问题? 预测器消耗尽可能多的信号,包括用户生成的动作,历史浏览数据以及来自渲染器和网络堆栈本身的信号。
与负责协调Chrome中所有网络活动的ResourceDispatcherHost不同,Predictor对象在Chrome中为用户和网络生成的活动创建了一些过滤器:
作为一个例子,渲染过程可以使用以下任何提示触发浏览器进程的消息,这些提示在ResolutionMotivation(url_info.h)中方便地定义:
给出这样的信号,预测器的目标是评估其成功的可能性,然后在资源可用的情况下触发活动。 每个提示都可能有成功的可能性,优先级和到期时间戳,以及可以用来维护投机优化的内部优先级队列的提示组合。 最后,对于来自该队列的每个派遣请求,预测器还能够跟踪其成功率,从而允许其进一步优化其未来决策。
Chrome network architecture in a nutshell
Lifetime of your browser session...
##Optimizing the cold-boot experience
冷启动时,Chrome会记住十个最可能访问的hostname
地址栏活动优化
根据用户输入,Omnibox会自动建议一个动作,该操作是基于您的导航历史的URL或搜索查询。 在引擎盖下,每个提出的行动都对查询进行评分,以及其过去的表现。 事实上,Chrome允许我们访问chrome://predictors来检查这些数据。
Chrome维护用户输入的前缀的历史记录,它提出的操作以及每个的命中率。对于我自己的个人资料,您可以看到,每当我在Omnibox中输入“g”时,我将有76%的机会进入Gmail。一旦我添加了一个“m”(对于“gm”),那么信心就提高到了99.8% - 其实在412次访问中,我没有最终去Gmail,进入“gm”只有一次!
但是,你在想,这与网络堆栈有什么关系?那么可能的候选人的黄色和绿色的颜色也是ResourceDispatcher的重要信号!如果我们有可能的候选人(黄色),Chrome可能会触发目标主机的DNS预取。如果我们有高可信度候选人(绿色),则Chrome主机名解析后,Chrome也可能会触发TCP预连接。最后,如果在用户仍在审核的同时完成,则Chrome甚至可以在隐藏的选项卡中预渲染整个页面。
或者,如果根据以前的导航历史记录输入的前缀不匹配,则Chrome可能会发出DNS预取和TCP预连接到您的搜索提供商,以预期可能的搜索请求。
一般用户需要数百毫秒来填写他们的查询,并评估自动填充建议。在这种情况下,Chrome可以预取,预连接,甚至在某些情况下甚至预渲染页面,这样当用户准备好按“输入”键时,网络延迟的大部分已经被淘汰了!
优化缓存
最好最快的请求就是没有请求。。 每当我们谈论性能时,如果我们没有谈论缓存,我们会感到失望 - 您正在为您的页面上的所有资源提供Expires,ETag,Last-Modified和Cache-Control响应标头,对吧? 如果没有,停止,去解决它,我们会等待。
Chrome具有两个不同的内部缓存实现:一个由本地磁盘支持,另一个将所有内容存储在内存中。内存中的实现用于隐身浏览模式,并且在您关闭窗口时都将被清除。两者都实现相同的内部接口(disk_cache::Backend和disk_cache::Entry),这大大简化了体系结构,如果您倾向于使用,您可以轻松实验自己的实验缓存实现。
在内部,磁盘缓存实现自己的一组数据结构,所有数据结构都存储在您的配置文件的单个缓存文件夹中。在这个文件夹中,有一些索引文件,当浏览器启动时被记录,并且存储实际数据的数据文件以及HTTP头和其他记账信息。作为一个有趣的脚本,大小为16KB的资源存储在共享数据块文件中,较大的文件在磁盘上获得自己的专用文件。最后,针对回收,磁盘缓存维护LRU,其使用诸如访问频率和资源年龄等等级度量。
如果您对Chrome缓存的状态感到好奇,请打开一个新标签页并导航至chrome://net-internals/#httpCache。 或者,如果您想查看实际的HTTP元数据和缓存响应,还可以访问chrome://cache,它将枚举缓存中当前可用的所有资源。 从该页面搜索您要查找的资源,然后单击该URL以查看精确的缓存的头文件和响应字节。
使用prefetch优化DNS
我们已经多次提到DNS预解决方案,所以在我们深入实施之前,让我们来看看可能触发的情况,以及为什么:
在所有情况下,DNS预解析被视为提示。 Chrome不保证预解析会发生,而是使用每个信号与其自身的预测器结合来评估提示并决定一个行动方案。在“最坏情况”中,如果我们无法及时预先解析主机名,用户将不得不等待明确的DNS解析,其次是TCP连接时间,最后是实际的资源获取。然而,当发生这种情况时,预测器可以记录并调整其未来的决策 - 随着您的使用,它变得更快,更智能。
我们之前未提及的优化之一是Chrome可以了解每个站点的拓扑结构,然后使用此信息来加速未来的访问。具体来说,一个页面平均由88个资源组成,这些资源由30多个不同的主机提供。那么,每次执行导航时,Chrome可能会记录页面上热门资源的主机名,并且在将来访问期间,它可能会选择触发DNS预解析,甚至为某些或全部的TCP预连接他们!
要检查Chrome存储的子资源主机名,请导航至chrome://dns并搜索您的个人资料的任何热门目的地主机名。在上面的示例中,您可以看到Chrome记忆为Google+的六个子资源主机名,以及触发DNS预分辨率或执行TCP预连接的情况的统计数据,以及预期每个请求的请求数。这种内部会计是什么使Chrome预测器能够执行其优化。
除了所有内部信号之外,站点的所有者还可以在其页面上嵌入额外的标记,以请求浏览器预先解析主机名:
<link rel =“dns-prefetch”href =“// host_name_to_prefetch.com”>
为什么不依靠浏览器中的自动化机械?在某些情况下,您可能需要预先解析页面上任何位置未提及的主机名。典型的例子当然是重定向:链接可以指向主机,如分析跟踪服务,然后将用户重定向到实际目的地。 Chrome本身不能推测出这种模式,但是您可以通过提供手动提示来帮助它,并让浏览器提前解决实际目的地的主机名。
那么这是怎么实现的呢?这个问题的答案就像我们涵盖的所有其他优化,都取决于Chrome的版本,因为该团队一直在尝试新的更好的方法来提高性能。然而,广义而言,Chrome中的DNS基础设施有两个主要的实现:从历史上看,Chrome依赖于平台无关的getaddrinfo()系统调用,并将查找的实际责任委托给操作系统,但是这种方法在被Chrome自己实现的异步DNS解析器所取代的过程。
依靠操作系统的原始实现具有以下优点:代码越来越简单,并且能够利用操作系统的DNS缓存。然而,getaddrinfo()也是一个阻塞系统调用,这意味着Chrome必须创建并维护一个专用的工作线程池,以允许它并行执行多个查找。这个未连接的池被限制在六个工作线程,这是一个基于硬件最低公分母的经验数字 - 结果是,更多的并行请求可能会超载一些用户的路由器!
对于使用工作池进行预解析,Chrome只需调度getaddrinfo()调用,阻塞工作线程直到响应准备就绪,此时它只会丢弃返回的结果并开始处理下一个预取请求。丢弃它?结果会被操作系统DNS守护进程缓存缓存起来,用作下一次getaddrinfo() 直接响应。简单,有效,在实践中工作得很好。
好,有效,但不够好! getaddrinfo()调用会隐藏Chrome中的大量有用信息,例如每个记录的生存时间戳(TTL)时间戳以及DNS缓存本身的状态。为了提高性能,Chrome团队决定实施自己的跨平台异步DNS解析器。
通过将DNS解析移动到Chrome中,新的异步解析器启用了许多新的优化:
以上所有这些都是Chrome中持续实验和改进的想法。 这给我们带来了一个明显的问题:我们如何知道和衡量这些想法的影响? 简单,Chrome跟踪每个个人资料的详细网络性能统计信息和直方图。 要检查收集的DNS指标,请打开一个新标签页,然后访问chrome://histograms/DNS。
上述直方图显示DNS预取请求的延迟分布:大约50%(最右边的列)的预取查询在20ms(最左边的列)内完成。 请注意,这是基于最近浏览会话(9869个样本)的数据,对用户是私有的。 如果用户选择在Chrome中报告其使用统计信息,那么该数据的摘要将被匿名化,并定期上传给开发团队,查看其实验的影响并进行相应的调整。
通过预连接优化TCP连接
我们已经预先解析了主机名称,如地址栏或Chrome预测器估计的那样,我们很可能有一个浏览事件即将发生。 为什么不进一步推测性地预连接到目标主机,并在用户发出请求之前完成TCP握手? 通过这样做,我们可以消除延迟延迟的另一个完整的往返行程,这可以轻松地为用户节省数百毫秒。 那么这正是TCP-preconnect是什么,它是如何工作的!
要查看已触发TCP预连接的主机,请打开一个新的选项卡并访问chrome://dns。
首先,Chrome检查其套接字池,以查看主机名是否有可用的套接字,它可能可以重用 - 保持活动的套接字在池中保留一段时间,以避免TCP握手和慢启动处罚(slow-start penalties)。 如果没有可用的套接字,则可以启动TCP握手,并将其放在套接字池中。 然后,当用户启动导航时,可以立即发送HTTP请求。
查看所有打开的套接字的状态:chrome://net-internals#sockets
请注意,您还可以钻入每个套接字并检查时间轴:连接和代理时间,每个数据包的到达时间等。 最后但并非最不重要的是,您还可以导出此数据进行进一步分析或错误报告。有一个良好的工具是任何性能优化的关键,chrome://net-internals是Chrome中所有网络连接的关键。
Optimizing resource loading with prefetch hints
有时,页面的作者可以根据其网站的结构或布局提供额外的导航或页面上下文,并帮助浏览器优化用户体验。 Chrome支持两个这样的提示,可以嵌入到页面的标记中:
<link rel =“subresource”href =“ javascript/myapp.js”>
<link rel =“prefetch”href =“/images/big.jpeg”>
子资源和预取看起来非常相似,但语法非常不同。当链接资源将其关系指定为“预取”时,它是浏览器在未来导航中可能需要此资源的指示。换句话说,这是一个跨页提示。相比之下,当资源将关系指定为“子资源”时,它是对浏览器的早期指示,即资源将在当前页面上使用,并且可能希望稍后在文件中。
正如你所期望的,提示的不同语义导致资源加载器的行为非常不同。标有预取的资源被认为是低优先级,只有在当前页面加载完成后,才可能被浏览器下载。而资源资源一经遇到就会以高优先级获取,并且将与当前页面上的其余资源进行竞争。
在使用良好且正确的上下文中,这两个提示可以帮助您优化网站上的用户体验。最后,还需要注意的是,预取是HTML5规范的一部分,而且Firefox和Chrome已经支持。子资源目前仅在Chrome中可用。
Optimizing resource loading with browser prefreshing
不幸的是,并不是所有网站所有者都能够或愿意为浏览器提供标记中的子资源提示。此外,即使这样做,我们必须等待HTML文档从服务器到达,然后才能解析提示,并开始获取必要的子资源,具体取决于服务器响应时间以及客户端与服务器,这可能需要几百甚至几千毫秒。
不过,正如我们刚才看到的那样,Chrome已经在学习流行资源的主机名来执行DNS预取。那么为什么它不能做同样的事情,但要进一步执行DNS查找,使用TCP预连接,然后还推测预取资源?那么这就是“预付”可以做什么呢?
用户发起对目标URL的请求
Chrome向Predictor查询与目标URL相关联的学习子资源,并启动DNS预取,TCP预连接和资源预清除的顺序
如果学习的子资源在缓存中,则从磁盘加载到内存中
如果学习的子资源丢失或已过期,则进行网络请求
资源脱机是Chrome中每个实验优化的工作流程的一个很好的例子 - 从理论上讲,它应该能够实现更好的性能,但是还有许多权衡。只有一种方法可靠地确定是否将剪切并进入Chrome:将其实现并作为A / B实验在一些预发布渠道中与真实用户,实际网络和实际浏览模式一起运行。
截至2013年初,Chrome团队正处于讨论实施的早期阶段。如果根据收集的结果进行切割,我们可能会在今年晚些时候在Chrome中进行预清。改进Chrome网络性能的过程永远不会停止,团队一直在尝试新的方法,想法和技术。
Optimizing navigation with prerendering
我们已经涵盖的每个优化都有助于减少用户直接导航请求和其选项卡中生成的页面呈现之间的延迟。然而,要真正实现即时的体验呢?基于我们前面看到的UX数据,这种交互必须在不到100毫秒的时间内发生,这对于网络延迟没有太大的空间。我们可以在100毫秒的时间内传送渲染的页面吗?
当然,您已经知道答案了,因为这是许多用户使用的常见模式:如果您打开多个选项卡,则在选项卡之间切换是即时的,并且绝对要等待在单个前台选项卡中的相同资源之间的导航。那么如果浏览器提供了一个API呢?
<link rel =“prerender”href =“http://example.org/index.html”>
你猜到了,这是Chrome中的prerendering!而不是仅仅下载单一资源,而是像“预取”提示所做的那样,“预渲染”属性可以向Chrome表明,应该将隐藏的选项卡中的页面及其所有子资源预先放置。隐藏的选项卡本身对用户是不可见的,但是当用户触发导航时,该选项卡从后台交换为“即时体验”。
好奇试试吧您可以访问prerender-test.appspot.com进行演示,然后访问:chrome://net-internals/#prerender查看您的个人资料的预渲染页面的历史和状态
正如您所期望的,将隐藏选项卡中的整个页面渲染可以消耗大量资源,包括CPU和网络,因此只能在我们高度信任隐藏选项卡被使用的情况下使用! 例如,当您使用地址框时,可能会触发高度可信建议的prerender。 同样地,如果Google搜索结果评估第一条结果是最可能的的话(也称为Google即时网页),Google搜索有时会将prerender提示添加到其标记中:
请注意,您还可以将预先渲染提示添加到您自己的网站!但是,在您做之前,请注意,prerendering有一些限制和限制,您应该牢记:
换句话说,prerendering不能保证发生,只适用于安全的页面。另外,由于JavaScript和其他逻辑可能在隐藏页面中执行,最好的做法是利用页面可见性API来检测页面是否可见 - 这是您应该做的事情!
The text was updated successfully, but these errors were encountered: