net/socket

TCP socket、SSL流和socket池的跨平台实现。

net/spdy

实现SPDY协议。

net/url_request

实现URLRequest、URLRequestContext和URLRequestJob。

net/websockets

实现WebSockets协议。

这些组件的代码都让人忍不住想好好读一读,它们文档完备,每个组件你都能找到很多的单元测试。

移动平台的架构和性能

移动端浏览器的使用正在以指数级增长,即使按照保守预测,它也会在不远的将来完全取代桌面浏览。所以不言而喻,提供优化的移动端访问体验一直是Chrome团队的首要任务之一。在2012年初,Chrome for Android发布,数月后Chrome for iOS发布。

关于Chrome的移动端版本,第一件需要注意的事是,它并不是简单地直接在桌面浏览器基础上做一些调整——那样并不能得到最好的用户体验。从本质讲浏览器阅读模式怎么开,移动端环境的资源更加局限,而且有很多根本不同的操作参数:

此外,并不存在一个“典型移动设备”。不同硬件能力的各种设备五花八门,要提供最佳性能,Chrome必须适应每种设备的操作约束条件。所幸,Chrome有多种执行模型正好可以实现这一点。

在Android设备上,Chrome沿用了桌面版本中相同的多进程架构——即一个浏览器进程多个呈现进程。一个区别是,由于移动设备的内存有限,Chrome可能不能为每个开启的标签页运行专用的呈现器。Chrome是根据可用内存和设备的其他约束条件确定一个最优的呈现进程数量,由多个标签页共享呈现进程。

当只有最少资源可用时,或者Chrome无法运行多进程时,它也可以切换回使用单进程、多线程处理模型。实际上,在iOS设备上,由于基础平台对沙盒的限制,它就是这样实现的——运行多线程的单一进程。

网络性能方面呢?首先,Chrome在Android和iOS上使用和其他版本相同的网络栈。这样保证所有平台都有相同的网络优化,Chrome由此获得显著的性能优势。但是,如推测优化技术、socket超时设定与管理逻辑、缓存大小等这样的变量,是有所不同的并且会根据设备功能和所用网络时时调整。

例如,为了节约电池电量,移动端Chrome可能选择使用闲置socket的懒惰关闭方式——即仅当开启新socket时才关闭旧的,这样来尽可能减少使用广播模组。同样地,因为预呈现(见下文)可能需要大量的网络和处理器资源,所以通常仅当用户使用WiFi时才进行。

优化移动端浏览体验是Chrome开发团队的首要优先任务之一,我们可以期待在未来几个月或几年内看到新的改进。实际上,这是一个值得单独行文介绍的内容——或许在POSA系列的下一版中就会出现。

使用Chrome预测器进行推测优化

Chrome会随着你的使用变得越来越快。这项本领是借助Predictor单例对象实现的,它在浏览器内核进程中被实例化,其唯一职责是观测网络模式,并学习和预测未来可能出现的用户行为。Predictor处理信号的一些例子有:

Chrome会随着你的使用,逐步学习网络的拓扑结构和你的浏览模式。如果顺利,它可以为每次浏览减少数百毫秒的延迟,让用户更加接近“页面即刻加载”的理想状态。为了实现这一点,Chrome使用了四个核心的优化技术,见表1.3

表1.3 Chrome所使用的网络优化技术

技术

描述

DNS预解析

提前解析主机名,避免DNS延迟

TCP预连接

提前连接目标服务器,避免TCP握手的延迟

资源预获取

提前获取页面上的关键资源,加速页面的呈现

页面预呈现

提前获取整个页面的所有资源,用户触发时立即展示

每个触发这些技术的决定都是在大量约束条件下优化判断的。毕竟,每一项优化都是推测性的,这意味着如果运用失当,可能会导致不必要任务和网络流量,更糟的是,还有可能对用户实际浏览行为的加载时间产生负面效果。

Chrome是如何解决这个问题的呢?预测器会尽可能多地吸收信号,包括用户产生的行为、历史浏览数据以及来自呈现器和网络栈本身的信号。

与ResourceDispatcherHost负责协调Chrome中所有网络活动的情况类似,Predictor对象也在Chrome内部创建了对一些用户和网络产生活动的过滤器:

下面是一个实际操作的例子,呈现进程可以发出一条带有以下任何提示的消息给浏览器进程,这些提示定义在ResolutionMotivation(url_info.h)中:

百度浏览览器8.0安卓_手机浏览l器_浏览器阅读模式怎么开

收到这样的信号后,预测器的目标是评价其成功的可能性,然后在资源可用的情况下触发相应行为。每条提示可能都有一个成功概率、一个优先级和一个过期时间戳,这组数据可用于维护一个推测优化的内部优先级队列。最后,对于每条从此队列发出的请求,预测器还能够跟踪记录其成功率,这又被用于未来决策的优化中。

Chrome网络架构概要

浏览器会话的生命周期

有了对Chrome网络栈架构的一个概括性认识后,我们接下来仔细研究一下浏览器内部实施的各种面向用户的优化。具体而言,假设我们刚创建了一个新的Chrome profile,准备好开始了。

百度浏览览器8.0安卓_手机浏览l器_浏览器阅读模式怎么开

优化冷启动体验

当你第一次加载浏览器时,它对你的喜爱站点和浏览模式一无所知。但是,我们中的很多人都会在浏览器冷启动后做同样的事:我们会浏览我们的电邮收件箱、喜爱的新闻站点、社交网站、内网入口等等。具体的站点可能因人而异,但是这些会话的相似之处使Chrome的Predictor可以加速你的冷启动过程。

Chrome会记住用户在浏览器启动后前10个最有可能访问的主机名——注意这并不是前10个全局目标站点,而特指全新开启浏览器之后的目标站点。浏览器加载时,Chrome可以为这些可能的目标站点触发DNS预获取行为。如果你对此感兴趣,可以打开一个新标签页浏览地址chrome://dns,看一看你自己的启动主机名列表。在页面顶端,你会找到你profile的前10个最可能启动站点。

手机浏览l器_百度浏览览器8.0安卓_浏览器阅读模式怎么开

图1.5 启动DNS

图1.5的截图是我的Chrome profile的例子。我通常是如何开始浏览的呢,如果我在写文章,比如现在这一篇,可能会频繁访问Google Docs。果不其然,我们在列表中看到很多Google的主机名。

使用Omnibox优化交互过程

Chrome的创新之一是引入了Omnibox,它与此前的地址栏不同,可以处理目标站点URL之外的很多东西。除了记住用户曾经访问过的页面的URL之外,它还提供完整的对历史记录的文本检索功能,与你所选择的搜索引擎的集成性也较好。

随着用户在其中键入内容,Omnibox会自动提供建议的行为,可能是基于你的浏览历史的URL或者是一次检索查询。在后台,每个建议行为都根据查询结果和历史表现进行评分。实际上,Chrome允许我们通过访问chrome://predictors来查看这些数据。

手机浏览l器_浏览器阅读模式怎么开_百度浏览览器8.0安卓

图1.6 Omnibox的URL预测

Chrome会维护用户输入前缀、建议行为以及每一记录的命中率的历史记录。在我的profile中,你可以看到当我在Omnibox中输入“g”时,有76%的可能性我是要访问Gmail。而当我加了一个“m”之后(变成“gm”),则置信水平上升到99.8%,实际上,在记录的412次访问中,我只有一次在输入“gm”之后没有访问Gmail。

这与网络栈有什么关系呢?可能备选站点中黄色和绿色的记录也是ResourceDispatcher的重要信号。如果我们有一个可能的备选站点(黄色),Chrome可能为该目标主机触发DNS预获取。如果我们有一个高度确信的备选站点(绿色),那么Chrome可能还会在主机名解析之后触发TCP预连接。最后,如果这两步都做完时用户还没做出最终决定,那么Chrome甚至可能在隐藏的标签页中预呈现整个页面。

另一方面浏览器阅读模式怎么开,如果根据过去的浏览历史没有为所输入的前缀找到较好的匹配,那么Chrome预计可能会发生检索请求,会向你的搜索引擎发起DNS预获取和TCP预连接。

一个普通用户需要花费数百毫秒来填写查询内容,评价所给出的自动补全建议。在后台,Chrome能够预获取、预连接,并在某些情况下对页面进行预呈现,这样当用户准备好按下“输入”键时,很多网络延迟已经被消除了。

优化缓存性能

最好最快的请求是不发出请求。当我们说到性能时必然会涉及缓存的问题——你正在为你网页上的所有资源提供Expires、ETag、Last-Modified和Cache-Control这些响应标头,对吧?如果你没有这样做,请立刻去改。我们会等你。

Chrome的内部缓存有两种不同的实现方式:一种是由本地磁盘所支持,另一种是把所有内容存储在内存中。内存实现方式用于匿名浏览模式,当你关闭窗口后会把痕迹清除干净。两种方式都实现相同的内部接口(disk_cache:Backend和disk_cache:Entry),这极大地简化了架构并且——如果你非要坚持的话——允许你很方便地试验你自己的缓存实现。

内部来看,磁盘缓存实现了其自己的数据结构,它们都被存储在你的profile的一个单独的缓存文件夹中。这个文件夹中有索引文件,它们在浏览器启动时进行内存映射,还有数据文件,它们存储了实际数据以及HTTP标头和其他簿记信息【注4】。最后,在缓存回收机制上,磁盘缓存会维护一个最近最少使用(LRU)缓存,它会把访问频度和资源新旧度等排序量化指标考虑进去。

如果你对Chrome的缓存状态很感兴趣,可以打开新标签页访问chrome://net-internals/#httpCache。或者,如果你想看看真实的HTTP元数据以及缓存的响应,也可以访问chrome://cache,其中列示了缓存中当前可用的所有资源。在该页面中,检索一项资源之后可以点击URL查看缓存的标头和响应的具体字节内容。

使用预获取优化DNS

我们已经在一些地方提到过DNS预解析,所以在我们深入介绍它的实现方式之前,先回忆一下哪些情况下为什么会触发它:

所有情况下,DNS预解析都被作为提示来处理。Chrome并不保证预解析必然进行,而是综合运用各个信号与其自身的预测器来评估这条提示并动态决策。在“最坏情况”下,如果Chrome未能及时完成主机名预解析,用户就要等待显式DNS解析,接着是TCP连接时间,最后是实际资源获取。但是,如果出现了这种情况,预测器会进行记录并相应调整其未来决策——随着你的持续使用,它会变得更快更聪明。

我们此前没有提到的一项优化是Chrome能够学习每个站点的拓扑结构,并运用这项信息来加速未来的访问过程。具体而言,回忆一下,一个页面平均由88项资源组成,由15个以上不同的主机发送。每一次你进行浏览,Chrome会记录页面上的热门资源的主机名,在以后的访问中,它可能会对其中一些或全部选择触发DNS预解析甚至TCP预连接。

访问chrome://dns并检索你profile对应的热门站点主机名,可以查看Chrome所保存下来的子资源主机名。在上面的例子中,你可以看到Chrome记录了Google+的6个子资源主机名,还统计了DNS预解析触发或TCP预连接执行的次数,还有会由各项处理的请求的估计值。这些内部记录帮助Chrome预测器实现优化。

除了这些内部信号之外,站点的所有者也能在页面中嵌入附加的标记请求浏览器预解析一个主机名:

为何不直接依靠浏览器的自动机制呢?有些时候,你可能希望解析一个页面中任何地方都没有提到过的主机名。重定向就是一个典型的例子:链接可能指向一个主机——就如同一项分析追踪服务一样——这个主机再把用户重定向至真正的目标站点。Chrome依靠自身是无法推断出这种模式的,但你可以手动提供一条提示帮助它,让浏览器提前解析真实目标站点的主机名。

这一切在后台是如何实现的呢?和我们讨论过的其他优化手段一样,这个问题的答案也取决于Chrome的版本,因为开发团队一直试验新的、更好的方式来提升性能。但是,不严格地说,Chrome内部的DNS基础设施有两个主要的实现方式。过去,Chrome依靠操作平台无关的系统调用getaddrinfo(),并把DNS查询的实际职责交由操作系统完成。但是,这种方式正逐步被Chrome自己实现的异步DNS解析器所替代。

依靠操作系统的原有方式具有其优点:代码少而简单,并能够利用操作系统的DNS缓存。但是,getaddrinfo()也是一个阻塞型的系统调用,这意味着Chrome必须创建并维护一个专用的worker线程池,才能够实现多条并行查询。这个未连接池最多只能容纳六个线程,这个上限是基于硬件的最小公分母得出的经验数字——这样并行请求的数量超出的话,有些用户的路由器就会过载。

对于使用worker池的预解析,Chrome就只是调度getaddrinfo()调用,这会一直阻塞worker线程,直至响应就绪后马上丢弃所返回的结果,并开始处理下一条预获取请求。结果由操作系统的DNS缓存来存储,未来实际进行getaddrinfo()查询时,它会立即返回响应。这种方式简单有效,实践中的表现也不错。

但这还不够好。getaddrinfo()调用有很多有用信息不向Chrome公开,比如每条记录的生存时间(TTL)时间戳,以及DNS缓存本身的状态。为了提升性能,Chrome团队决定自己来实现跨平台的异步DNS解析器。

浏览器阅读模式怎么开_百度浏览览器8.0安卓_手机浏览l器

图1.7 启用异步DNS解析器

通过把DNS解析放到Chrome内部来处理,新的异步解析器可以实现一些新的优化手段:

以上所有以及其他很多想法都是在Chrome内持续试验并优化的。这就必然涉及一个问题:我们如何了解并衡量这些想法的效果呢?很简单,Chrome会为每个profile分别记录详细的网络性能统计数据和直方图。要查看所收集到的DNS统计数据,可以打开新标签页,访问chrome://histograms/DNS(见图1.8)。

手机浏览l器_浏览器阅读模式怎么开_百度浏览览器8.0安卓

图1.8 DNS预获取的直方图

上面的直方图显示出了DNS预获取请求延迟的分布情况:大约50%的(最右侧列)预获取查询在20毫秒内完成(最左侧列)。注意,这是基于最近的浏览会话(9869个样本)的统计得到的,并属于用户的隐私数据。如果该用户选择报告Chrome的使用统计数据,则这些数据的摘要会被匿名处理,定期反馈给开发团队,他们就可以看到试验的效果并进行相应的调整。

使用预连接优化TCP连接管理

浏览器阅读模式怎么开_百度浏览览器8.0安卓_手机浏览l器

我们已经预解析了主机名,按照Omnibox或Chrome预测器的估计,我们很有可能即将进行浏览行为。为什么不更进一步,也推测性地预连接到目标主机,在用户发出请求之前完成TCP握手的步骤呢?这样一来,我们又能消除掉一个往返延迟,轻松省去用户数百毫秒的时间。没错,TCP预连接正是这样做的。

打开新标签页访问chrome://dns可以查看已经触发TCP预连接的主机。

图1.9 展示已经触发TCP预连接的主机

首先,Chrome会检查socket池,看看是否有该主机名的可用socket可供重用——存活socket会在池中留存一段时间,以避免TCP握手和慢热启动的惩罚时间。如果没有socket可用,则由其发起TCP握手并放入池中。然后,当用户进行浏览时,HTTP请求就可以立刻调度。

Chrome在chrome://net-internals#sockets中提供了一个工具可以查看Chrome中所有已开启socket的状态。图1.10是相关的截图。

浏览器阅读模式怎么开_百度浏览览器8.0安卓_手机浏览l器

图1.10 已开启的socket

你还可以详细查看每个socket,检查时间线:连接和代理时间,每个包的到达时间等等。最后要说的很重要的一点是:你也可以导出这些数据进行后续分析或报告bug。具有良好的信息统计机制对任何优化都是很关键的,chrome://net-internals就是Chrome中所有功能相互作用的集中展示——如果你还没探索过这个功能,你应该试试!

使用预获取提示优化资源加载

有时,页面的作者能够提供基于其站点结构或布局附加的导航或页面上下文,帮助浏览器优化用户体验。Chrome支持两种这样的提示,可以嵌入页面标记中使用:

子资源和预获取看起来非常类似,但是语义完全不同。当链接资源指定其关系为“预获取”时,它是告诉浏览器,这项资源在以后的浏览中可能用到。换言之,这实际上是一个跨页面提示。而当资源指定其关系为“子资源”时,它是提前告诉浏览器该资源会在当前页面中被用到,在该文档后面的部分遇到它之前可以先发起请求。

可以想见,两者的不同语义会导致资源加载器的行为大相径庭。标记预获取的资源会被看做优先级较低,并在当前页面加载完成后由浏览器进行一次下载。标记为子资源的内容一旦遇到就会作为高优先级资源来获取,并与当前页面上的其余资源相互竞争。

这两种提示如果使用得当可以极大地帮助优化你站点的用户体验。最后,还要注意,预获取是HTML5规范的一部分,目前Firefox和Chrome都支持,而子资源目前只限于Chrome。

使用浏览器预刷新优化资源加载

不巧的是,不是所有站点所有者都能够或愿意在标记中为浏览器提供子资源提示。而且,即使他们这样做,我们还是要等待HTML文档从服务器传送过来之后才能解析这些提示,开始获取这些必要的子资源——根据服务器响应时间和客户端与服务器间的延迟,这可能需要耗费数百乃至数千毫秒。

但是,我们前面看到,Chrome已经通过学习常用资源的主机名来执行DNS预获取了。那么,为什么不如法炮制,更进一步:执行DNS查询,使用TCP预连接然后也推测性地预获取资源呢?没错,这就是预刷新的作用:

资源预刷新是展示Chrome中每个试验性优化手段的工作流的绝佳例子——理论上讲,一项优化应该使性能得到提升,但是也涉及很多因素的此消彼长。只有一种方式能够可靠地确定一项优化是不是有效,是不是适合于Chrome:先实现它,并在一些预先发布的渠道(真正的网络、真实浏览模式、真人用户)上进行A/B试验。

在2013年初,Chrome团队还处于讨论这种实现的早期阶段。如果根据所收集的结果它能奏效,我们我们可能会在年内晚些时候在Chrome中看到预刷新。Chrome的网络性能优化从未止步——开发团队一直在试验新的方法、创意和技术。

使用预呈现优化浏览

目前为止我们介绍过的每一项优化都是帮助减少用户进行浏览的直接请求和标签页上呈现最终页面之间的延迟的。但是,要真正得到即刻展示页面的体验,需要什么呢?根据我们之前看到的UX数据,这样的交互时间需要低于100毫秒,这样,留给网络延迟的余地可不算多。我们怎么才能在100毫秒内把呈现好的页面展示出来呢?

当然,你已经知道答案了,因为这是很多用户所用的共同模式:如果你打开多个标签页,标签页间的切换就是即刻的,比在一个前景标签页中浏览同样资源之间的等待要快得多。那么,如果浏览器提供一个API来实现这一点呢?

你可能猜到了,这就是Chrome中的预呈现。不是如“预获取”提示实现的那样只下载单项资源,“预呈现”属性命令Chrome在隐藏标签页中预呈现页面以及所有子资源。隐藏标签页本身对用户不可见,但当用户触发浏览行为时,该标签页就会从后台切换出来实现“即刻体验”。

想看看这是如何实现的吗?可以访问prerender-test.appspot.com上还在开发中的演示版,要查看你的profile的预呈现页面的历史记录和状态,访问:chrome://net-internals/#prerender。(见图1.11)

浏览器阅读模式怎么开_手机浏览l器_百度浏览览器8.0安卓

图1.11 当前profile的预呈现页面

可以预见的是,在隐藏标签页中呈现完整页面可能会耗费CPU和网络的大量资源,所以仅当我们高度确信应该使用隐藏标签页时才应该使用。例如,当你使用Omnibox时,对高度确信的建议可能会触发预呈现。类似地,Google搜索如果估计认为它的第一条检索结果是高度确信的目标站点,有时会在其标记中添加预呈现提示(也称为Google即开页面)。

你还可以为自己的站点添加预呈现提示。在你这样做之前,请先了解并记住预呈现过程具有的以下局限之处:

换言之,预呈现不保证一定发生,并只适用于安全的页面。此外,因为JavaScript和其他逻辑可能在隐藏页面中被执行,实践中最好使用页面可见性API来检测一下该页面是否可见——这是你本就应该做的。

Chrome会随着你的使用越来越快

无需多言,Chrome的网络栈绝非一个简单的socket管理器。我们这次走马观花的概述介绍了在网站浏览的背后多层次的透明运行的优化手段。Chrome对网站拓扑结构和你的浏览模式了解越是深入,它的效果就越好。就像魔法一样,Chrome会随着你的使用越来越快。可你知道它并不是魔法,你了解这背后的机制。

最后,还要注意一点,Chrome团队持续试验着优化性能的新想法——这个过程从未停止。在你阅读本文时,就可能有新的试验项目和优化手段正在开发、测试或部署着。也许只有当我们能够对每个站点每个页面都实现即刻加载(小于100毫秒)时,才会停下脚步吧。在那之前,总有工作等着我们去完成。

注释:

第10章:《移动网络性能的秘密》详细解释了这个问题。

如果你感兴趣,Chromium的百科页面上有详细的介绍。

#OAMlx_jo-ck/src/content/public/browser/resource_dispatcher_host.h&exact_package=chromium&q=ResourceDispatcherHost.

16KB以内的资源保存在共享数据块文件中,更大的文件在磁盘上有自己的专用文件。

英文原文:

译者:unione

———END———
限 时 特 惠: 本站每日持续更新海量各大内部创业教程,一年会员只需98元,全站资源免费下载 点击网站首页每天更新
站 长 微 信: aiwo51889