Chrome Predictor的预测功能优化

Chrome会随着使用变得更快。 它这个特性是通过一个单例对象Predictor来实现的。这个对象在浏览器内核进程(Browser Kernel Process)中实例化,它唯一的职责就是观察和学习当前网络活动方式,提前预估用户下一步的操作。下面是一个示例:

  • 用户将鼠标停留在一个链接上,就预示着一个用户的偏好以及下一步的浏览行为。这时Chrome就可以提前进行DNS Lookup及TCP握手。用户的点击操作平均需要将近200ms,在这个时间就可能处理完DNS和TCP相关的操作, 也就是省去几百毫秒的延迟时间。
  • 当在地址栏(Omnibox/URL bar) 触发高可能性选项时,就同样会触发一个DNS lookup和TCP预连接(pre-connect),甚至在一个不可见的页签中进行预渲染(pre-render)!
  • 我们每个人都一串天天会访问的网站, Chrome会研究在这些页面上的子资源, 并且尝试进行预解析(pre-resolve), 甚至可能会进行预加载(pre-fetch)以优化浏览体验。

除了上面三项,还有很多..

Chrome会在你使用过程中学习Web的拓扑结构,而不单单是你的浏览模式。理想的话,它将为你省去数百毫秒的延迟, 更接近于即时页面加载的状态. 正是为了这个目标,Chrome投入了以下的核心优化技术:

DNS预解析(pre-resolve) 提前解析主机地址,以减少DNS延迟
TCP预连接(pre-connect) 提前连接到目标服务器,以减少TCP握手延迟
资源预加载(prefetching) 提前加载页面的核心资源,以加载页面显示
页面预渲染(prerendering)     提前获取整个页面和相关子资源,这样可以做到及时显示

每一个决策都包含着一个或多个的优化, 用来克服大量的限制因素.  不过毕竟都只是预测性的优化策略,如果效果不理想,就会引入多余的处理和网络传输。甚至可能会带来一些加载时间上的负体验。

Chrome如何处理这些问题呢? Predictor会尽量收集各种信息,诸如用户操作,历史浏览数据,以及来自渲染引擎(render)和网络模块自身的信息。

和Chrome中负责网络事务调度的ResourceDispatcherHost不同,Predictor对象会针对用户和网络事务创建一组过滤器(filter):

  • IPC channel filter用来监控来自render进程的事务。
  • 每个请求上都会加一个ConnectInterceptor 对象,这样就可以跟踪网络传输的模式以及每一个请求的度量数据。

渲染进程(render process)会在一系列的事件下发送消息到浏览器进程(browser process), 这些事件被定义在一个枚举(ResolutionMotivation)中以便于使用 (url_info.h):

enum ResolutionMotivation {
MOUSE_OVER_MOTIVATED, // 鼠标悬停.
OMNIBOX_MOTIVATED, // Omni-box建议进行解析.
STARTUP_LIST_MOTIVATED, // 这是在前10个启动项中的资源.
EARLY_LOAD_MOTIVATED, // 有时需要使用prefetched来提前建立连接. // 下面定义了预加载评估的方式,会由一个navigation变量指定.
// referring_url_也需要同时指定.
STATIC_REFERAL_MOTIVATED, // 外部数据库(External Database)建议进行解析。
LEARNED_REFERAL_MOTIVATED, // 前一次浏览(prior navigation建议进行解析.
SELF_REFERAL_MOTIVATED, // 猜测下一个连接是不是需要进行解析. // <略> ...
};

通过这些给定的事件,Predictor的目标就可以评估它成功的可能性, 然后再适时触发操作。每一项事件都有其成功的机率、优先级以及时间戳,这些可以在内部维护一个用优先级管理的队列,也是优化的一个手段。最终,对于这个队列中发出的每一个请求的成功率,都可以被Predictor追踪到。基于这些数据,Predictor就可以进一步优化它的决策。

Chrome网络架构小结

  • Chrome使用多进程架构,将渲染进程同浏览器进程隔离开来。
  • Chrome维护着一个资源分发器的实例(a single instance of the resource dispatcher), 它运行在浏览器内核进程,并在各个渲染进程间共享。
  • 网络层是跨平台的,大部分情况下以单进程库存在。
  • 网络层使用非阻塞式(no-blocking)操作来管理所有网络任务。
  • 共享的网络层支持有效的资源排序、复用、并为浏览器提供在多进程间进行全局优化的能力。
  • 每一个渲染进程通过IPC和资源分发器(resource dispatcher)通讯。
  • 资源分发器(Resource dispatcher)通过自定义的IPC Filter解析资源请求。
  • Predictor在解析资源请求和响应网络事务中学习,并对后续的网络请求进行优化。
  • Predictor会根据学习到的网络事务模式预测性的进行DNS解析, TCP握手,甚至是资源请求,为用户实际操作时节省数百毫秒的时间。

了解晦涩的内部细节后,让我们来看一下用户可以感受到的优化。一切从全新的Chrome开始。

优化冷启动(Cold-Boot)体验 

第一次启动浏览器,它当然不可能了解你的使用习惯和喜欢的页面。但事实上,我们大多数会在浏览器的冷启动后做些类似的事情,比如到电子邮箱查看邮件,加一些新闻页面、社交页面及内部页面到我的最爱,诸如此类。这些页面各有不同,但它们仍然具有一些相似性,所以Predictor仍然可以对这个过程提速。

Chrome记下了用户在全新启动浏览器时最常用的10个域名。当浏览器启动时,Chrome会提前对这些域名进行DNS预解析。你可以在Chrome中使用chrome://dns查看到这个列表。在打开页面的最上面的表格中会列出启动时的备选域名列表。

 

通过Omnibox优化与用户的交互


引入Omnibox是Chrome的一项创新, 并不是简单地处理目标的URL。除了记录之前访问过的页面URL,它还与搜索引擎的整合,并且支持在历史记录中进行全文搜索(比如,直接输入页面名称)。

当用户输入时,Omnibox自动发起一个行为,要么查找浏览记录中的URL, 要么进行一次搜索。每一次发起的操作都会被加以评分,以统计它的性能。你可以在Chrome输入chrome://predictors来观察这些数据。


Chrome维护着一个历史记录,内容包括用户输入的前置文字,采用的行为,以命中的资数。 在上面的列表,你可以看到,当输入g时,有76%的机会尝试打开Gmail. 如果再补充一个m (就是gm), 打开Gmail的可能性增加到99.8%。

那么网络模块会做什么呢?上表中的黄色和绿色对于ResourceDispatcher非常重要。如果有一个一般可能性的页面(黄色), Chrome就是发起DNS预解析。如果有一个高可能性的页面(绿色),Chrome还会在DNS解析后发起TCP预连接。如果这两项都完成了,用户仍然继续录入,Chrome就会在一个隐藏的页签进行预渲染(pre-render)。

相对的,如果输入的前置文字找不到合适的匹配项目,Chrome会向搜索引擎服务者发起DNS预解析和TCP预连,以获取相似的搜索结果。

 
平均而言用户从填写查询内容到评估给出的建议需要花费数百毫秒。 此时Chrome可以在后台进行预解析,预连接,甚至进行预渲染。再当用户准备按下回车键时,大量的网络延迟已经被提前处理掉了。
 
 

优化缓存性能 

最快的请求就是没有请求。 无论何时讨论性能,都不能不谈缓存。相信你已经为页面上所有资源的都提供了Expires, ETag, Last-Modified和Cache-Control这些响应头信息(response headers)什么? 还没有?那你还是先处理好再来看吧!

Chrome有两种不同的内部缓存的实现:一种备份于本地磁盘(local disk),另一种则存储于内存(in-memory)中。内存模式(in-memory)主要应用于无痕浏览模式(Incognito browsing mode),并在关闭窗口清除掉。 两种方式使用了相同的内部接口(disk_cache::Backend, 和disk_cache::Entry),大大简化了系统架构。如果你想实现一个自己的缓存算法,可以很容易地实现进去。

在内部,磁盘缓存(disk cache)实现了它自己的一组数据结构, 它们被存储在一个单独的缓存目录里。其中有索引文件(在浏览器启动时加载到内存中),数据文件(存储着实际数据,以及HTTP头以及其它信息)。比较有趣的是,16KB以下的文件存储于共同的数据块文件中(data block-files,即小文件集中存储于一个大文件中),其它较大的文件才会存储到自己专属的文件中。最后,磁盘缓存的淘汰策略是维护一个LRU,通过比如访问频率和资源的使用时间(age)的度量进行管理。

在Chrome开个页签,输入chrome://net-internals/#httpCache。 如果你要看到实际的HTTP数据和缓存的响应处理,可以打开chrome://cache, 里面会列出所有缓存中可用的资源。打开每一项,还可以看到详细的数据头等信息。

优化DNS预解析

前面已经多次提到了DNS预解析,在深入实现之前,先汇总一下DNS预解析的场景和理由:

  • 运行在渲染进程中的WebKit文档解析器(document parser), 会为当前页面上所有的链接提供一个主机名(hostname)列表,Chrome可以选择是否提前解析。
  • 当用户要打开页面时,渲染进程先会触发一个鼠标悬停(hover)或按键(button down)事件。
  • Omnibox可能会针对一个高可能性的建议页面发起解析请求。
  • Chrome Predictor会基于过往浏览记录和资源请求数据发起主机解析请求。(下面会详细解释。)
  • 页面本身会显式地要求Chrome对某些主机名称进行预解析。

上述各项对于Chrome都只是一个线索。 Chrome并不保证预解析一定会被执行,所有的线索会由Predictor进行评估,以决定后续的操作。最坏的情况下,可能无法及时解析主机名,用户就必须等待一个DNS解析时间,然后是TCP连接时间,最后是资源加载时间。Predictor会记下这个场景,在未来决策时相应地加以参考。总之,一定是越用越快。

之前提过到Chrome可以记住每个页面的拓扑(topology),并可以基于这个信息进行加速。还记得吧,平均每个页面带有88个资源,分别来自于30多个独立的主机。每打开这个页面,Chrome会记下资源中比较常用的主机名,在后续的浏览过程中,Chrome就会发起针对某些主机或者全部主机的DNS解析,甚至是TCP预连接!

 


使用chrome://dns 就可以观察到上面的数据(Google+页面), 其中有6个子资源对应的主机名,并记录了DNS预解析发生的次数,TCP预连接发生的次数,以及到每个主机的请求次数。这些数据就可以让Chrome Predictor执行相应的优化决策。

除了内部事件通知外,页面设计者可以在页面中嵌入如下的语句请求浏览器进行预解析:

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

之所以有这个需求,一个典型的例子是重定向(redirects). Chrome本身没办法判断出这种模式,通过这种方式则可以让浏览器提前进行解析。

具体的实现也是因版本而有所差异,总体而言,Chrome中的DNS处理有两个主要的实现:1.基于历史数据(historically), 通过调用平台无关的getaddrinfo()系统函数实现。2.代理操作系统的DNS处理方法,这种方法正在被Chrome自行实现的一套异步DNS解析机制(asynchronous DNS resolver)所取代。

依赖于系统的实现,代码少而且简单,但是getaddrinfo()是一个阻塞式的系统调用,无法有效地并行多个查询操作。经验数据还显示,并行请求过多甚至会超出路由器的负额。Chrome为此设计了一个复杂的机制。对于其中带有worker-pool的预解析, Chrome只是简单的发送getaddrinfo() 调用, 同时阻塞worker thread直到收到响应数据。因为系统有DNS缓存的原因,针对解析过的主机,解析操作会立即返回。 这个过程简单,有效。

但还不够! getaddrinfo()隐藏了太多有用的信息,比如Time-to-live(TTL)时间戳, DNS缓存的状态等。于是Chrome决定自己实现一套跨平台的异步DNS解析器。


这个新技术可以支持以下优化:

  • 更好地控制重转的时机,有能力并行执行多个查询操作。
  • 清晰地记录TTLs。
  • 更好地处理IPv4和IPv6的兼容。
  • 基于RTT和其它事件,针对不同服务器进行错误处理(failover)

Chrome仍然进行着持续的优化. 通过chrome://histograms/DNS可以观察到DNS度量数据:

 
上面的柱状图展示了DNS预解析延迟时间的分布:比如将近50%(最右侧)的查询在20ms内完成。这些数据基于最近的浏览操作(采样9869次),用户可以选择是否报告这些使用数据,然后这些数据会以匿名的形式交由工程团队加以分析,这样就可以了解到功能的性能,以及未来如何进一步调整。周而复始,不断优化。

转载请注明出处:http://blog.csdn.net/horkychen
原文地址: http://www.igvita.com/posa/high-performance-networking-in-google-chrome/

 
from :http://blog.csdn.net/horkychen/article/details/10421523

Google Chrome中的高性能网络(二)的更多相关文章

  1. Google Chrome中的高性能网络 (三)

    使用预连接优化了TCP连接管理 已经预解析到了主机名,也有了由OmniBox和Chrome Predictor提供信号,预示着用户未来的操作.为什么再进一步连接到目标主机,在用户真正发起请求前完成TC ...

  2. Google Chrome中的高性能网络(一)

    以下内容是"The Performance of Open Source Applications" (POSA)的草稿, 也是The Architecture of Open S ...

  3. Google Chrome中的高性能网络-[译]《转载》

    以下内容是"The Performance of Open Source Applications" (POSA)的草稿, 也是The Architecture of Open S ...

  4. google chrome中如何删除一条输入网址提示

    在google chrome中网站栏输入字母的时候会出现网址的提示,如下图: 之前遇到个问题,不知道之前打错了www.baidu.com为wwww.baidu.com(也会跳转到百度)导致一输入“w” ...

  5. frameset 在 Google Chrome 中无法隐藏左边栏解决方法!

    使用Frameset 框架,发现在IE下, <frameset name="mainDefine" cols="200,10,*" frameborder ...

  6. 关于 Google Chrome 中的全屏模式和 APP 模式

    前言:我一直在纠结这篇文章是否应该归类在「前段开发」的范围内,哈哈! 前段时间做了一个项目,涉及到一个要全屏模式去访问网页的需求,因为 Google Chrome 的效率不错,而且专门为 Chrome ...

  7. Google Chrome 中安装 PostMan 扩展

    简介 PostMan 是调试 HTTP 请求的好工具,也是业界的佼佼者,这对于我们开发 Web Service 提供了很好的调试入口,支持请求认证机制.最关键的是,这个工具提供 Google Chro ...

  8. 【转载】关于 Google Chrome 中的全屏模式和 APP 模式

    [来源于]新浪微博:@阿博 http://www.cnblogs.com/abel/p/3235839.html 全屏模式:kiosk 默认全屏打开一个网页呢,只需要在快捷方式中加上 --kiosk ...

  9. Ajax请求在IE和Google Chrome中可以响应,在Firefox中无法响应

    在工作中碰到这么一个问题,发送ajax请求,在IE和chrome中可以正常的响应,但是在Firefox中无法响应,代码如下: JS代码: function Sure(obj) { var statu ...

随机推荐

  1. [转载]《STL源码剖析》阅读笔记之 迭代器及traits编程技法

    本文从三方面总结迭代器   迭代器的思想   迭代器相应型别及traits思想   __type_traits思想 一 迭代器思想 迭代器的主要思想源于迭代器模式,其定义如下:提供一种方法,使之能够依 ...

  2. sea.js说明文档

    Sea.js 手册与文档 首页 | 索引 目录 模块定义 define id dependencies factory exports require require.async require.re ...

  3. leetcode Permutations II 无重全排列

    作者:jostree  转载请注明出处 http://www.cnblogs.com/jostree/p/4051169.html 题目链接:leetcode Permutations II 无重全排 ...

  4. debian 学习记录-2 -账户 -关机

    linux考虑系统安全设定了root账号和user账号 权限较低的user账号下,连关机命令都执行不了…… 用户切换... 用户切换1 命令su(在user账号下,即可开启root账号模式) 用户切换 ...

  5. VPN client on linux debian

    Install the pptp-linux and pptp-linux-client: sudo apt-get install pptp-linux pptp-linux-client Crea ...

  6. VS2010开发环境最佳字体及配色方法

    Fixedsys Excelsior 3.01 1. 首先下载字体:http://www.fixedsysexcelsior.com/   脚本之家字体下载 2. 安装字体:control panel ...

  7. map遍历的三种基础用法

    java中遍历MAP的几种方法 Java代码 Map<String,String> map=new HashMap<String,String>();    map.put(& ...

  8. android apk 反编译

    Apk文件结构 apk文件实际是一个zip压缩包,可以通过解压缩工具解开.以下是我们用zip解开helloworld.apk文件后看到的内容.可以看到其结构跟新建立的工程结构有些类似. java代码: ...

  9. Codeforces Round #326 div2

    Problem_A(588A): 题意: Duff 很喜欢吃肉, 每天都要吃,然而她又懒得下楼. 可以买很多放在家里慢慢吃.然而肉价每天都在变化,现给定一个n, 表示有多少天,然后第i天吃ai kg的 ...

  10. mysql基本内容学习过程

    mysql数据库的基本操作: , 数据库的登录:mysql -u 用户名(root) -p密码 -P (端口) -h服务器名(本地表示:127.0.0.1) . 更改数据库显示:mysql -u ro ...