您好, 欢迎访问小ben成长手册, 现在是2019年08月26日

小ben成长手册 QQ:1329877829(请注明来自网站)  


  • 读者墙
  • 本站统计
  • 文章总数:365篇
  • 评论总数:906条
  • 分类总数:41个
  • 标签总数:338个
  • 友链总数:41个
  • 建站日期:2016-05-11
  • 为什么Chrome比其他浏览器快?
    作者: benen005 | 发布时间: 2016-07-15 09:57:03 | 点击量: 1809

    作者:Ilya Grigorik

    Google Chrome的历史和指导性原则

    Google Chrome最初是2008年下半年作为Windows平台上的一个beta版本发布的。Google还将自己编写的Chrome在BSD许可下进行了开源——称为Chromium。在很多人看来,这一串事件的发生颇为意外:浏览器战争要再次重启了吗?Google真的能做的更好吗?

    “它非常优秀让我不得不改变主意……”。埃里克•施密特在谈到他一开始反对开发Google Chrome时这样说道。

    答案是,他们能。时至今日,Chrome已经成为使用最广泛的网络浏览器(根据StatCounter的数据,市场份额超过35%),并且兼容Windows、Linux、OS X、Chrome OS多种操作系统,还包括Android和iOS等移动平台。显然,Chrome的特性和功能很对用户的胃口,其很多创新之举还被其他流行的浏览器所吸收和学习。

    有一本解释Google Chrome的思想和创新的原版38页漫画书,它很好地概括了Chrome受欢迎背后的思路和设计过程。但这只是开始。最初推动着Chrome开发的那些核心原则仍然是它持续优化的指导性原则:

    快速

        做出最快的浏览器

    安全

        为用户提供最安全的环境

    稳定

        提供有弹性且稳定的网络应用平台

    简单

        技术精妙蕴含在简单的用户体验

    正如开发团队所看到的那样,我们今天所使用的很多网站都不再仅仅是网页,而是应用程序。这些越来越有野心的应用需要速度、安全和稳定。这些方面,每个都值得单独成文来介绍,不过,因为我们的主题是性能,我们将重点介绍性能。

    性能的多个方面

    现代浏览器是一个平台,就像你的操作系统一样,Google Chrome也遵循这样的设计理念。在Google Chrome之前,所有主流浏览器都是单进程的应用程序。所有打开的页面共享同一地址空间,争夺同一资源。任何页面或浏览器本身出现bug,整体体验都可能被破坏。

    与此相反,Chrome运行于多进程模型,这种模型可以保持进程和内存的隔离性,每个标签页都能拥有一个严密的安全沙盒。随着多核架构的流行,隔绝进程并使各个打开的标签页免受其他出错页面的影响,单是这一点就能证明Chrome在浏览器的竞争中具有显著的性能优势。实际上,我们会发现绝大多数其他浏览器都纷纷效仿Chrome,或者开始转向类似的架构。

    分派进程之后,一个Web程序的执行主要包括三项任务:获取资源,页面布局与呈现,以及执行JavaScript。呈现和脚本执行步骤遵循单线程、交错执行的模型——无法对所得到的DOM(文档对象模型)进行并发式的修改。原因之一是JavaScript本身就是单线程的语言。所以,无论是对于应用程序的开发者还是浏览器的开发者来说,优化呈现和脚本执行运行时的协作方式,是极其重要的。

    在呈现这一步,Chrome使用的是Blink,这是一种快速、开源、遵守良好标准的布局引擎。在JavaScript这一步,Chrome自带了一个高度优化的V8 JavaScript运行时,它也作为单独开源项目发布,并已经被其他很多流行的项目所吸纳,例如Node.js的运行时。但是如果浏览器的网络连接是阻塞的(等待资源到来),那么优化V8 JavaScript执行或者Blink解析和呈现管道都不会有太大作用。

    浏览器优化各项网络资源的次序、优先级和延迟的能力对整体用户体验是最关键的影响因素。你可能没有注意到,毫不夸张地说,Chrome的网络栈每天都会变得更加聪明,尝试着隐藏或减少各项资源的延迟开销:它会学习可能出现的DNS查询,会记住网络的拓扑结构,会预连接可能的目标站点,等等。从外部看来,它展示出的是一种简单的资源获取机制,但是从内部看来则是对如何优化网络性能并带给用户最佳体验的一次详细而精彩的案例学习。

    让我们进入正题吧。

    什么是现代Web应用?

    在我们接触如何优化网络交互的具体细节之前,先来了解我们所面对的这个问题的发展趋势和背景。换句话说就是,“现代网页或者应用是什么样的?”

    HTTP Archive项目记录了网络的构造过程,可以帮助我们回答这个问题。这个项目并不是为了爬取网页内容,而是周期性地爬取访问量最大的站点,记录和加总各个站点所用资源数量、内容类型、标头和其他元数据的分析数据。2013年1月的数据,可能会令你吃惊。访问量最大前30万个网络站点来看,一个页面平均:

    • 为1280KB大小

    • 由88个资源组成

    • 连接15个以上不同的主机

    好好琢磨一下。大小平均超过1MB,包含88个如图片、JavaScript、CSS这样的资源,从15个不同的自有和第三方主机传送出来。而且,这些数字在过去几年还在持续增长,丝毫没有减缓的迹象。这说明,我们所开发的网络应用正变得越来越大,越来越有野心。

    根据HTTP Archive的数据,做个简单的算术,我们可知每个资源平均大小为15KB(1280KB/88项资源),这意味着浏览器中大多数的网络传输是短小但突发的。这就造成了一些问题,因为基础传输(TCP)是针对大型、流式下载进行优化的。让我们深入地看一看这些网络请求。

    线上资源请求的生命周期

    W3C的浏览时序规范(Navigation Timing specification)提供了一个浏览器API,让我们可以看到浏览器中每项请求的生命周期背后的时序和性能数据。让我们看看这些组成部分,每一块都是影响最佳用户体验的关键点:

    图1.1 浏览时序图

    对于一个网络资源的URL,浏览器首先会检查其本地缓存和应用程序缓存。如果你此前获取过该资源,并且提供了适当的缓存标头(Expires, Cache-Control等),则我们可能被允许使用本地副本来响应原请求——最快的请求就是不请求。或者,如果我们需要重新校验该资源(如果资源已过期),或是我们根本从未获取过该资源,那么就必须发起一个高开销的网络请求。

    有了主机名称和资源路径,Chrome首先检查现有的已开启连接中是否有可以重用的——socket按照{scheme, host, port}的格式储存在池中。或者,如果你已经配置了代理,或指定了代理自动配置脚本(PAC),那么Chrome就会通过适当的代理来检查连接。PAC脚本允许基于URL的不同代理或其他指定规则,它们都可以有自己的socket池。最后,如果上述条件都不满足,则请求必须通过将主机名解析为IP地址的方式发起,也称为DNS查询。

    如果幸运的话,主机名可能已经在缓存当中了,此时通常只需要一次快速的系统调用就得到响应。如若不然,就必须调度一个DNS查询才能继续下去。DNS查询耗费的时间由网络提供商、站点的热门程度、主机名可能存在过渡缓存中的概率以及该域名的权威服务器的响应时间所决定。换句话说,影响变量有很多,但是耗费数百毫秒来进行一次DNS查询也并非罕见。天啊。

    图1.2 三次握手

    得到了解析后的IP地址,Chrome现在可以打开一个新的与目标站点的TCP连接,这意味着我们需要进行“三次握手”:SYN > SYN-ACK > ACK。这一交换过程又为每个新的TCP连接增加了一个往返延迟——此处没有捷径可走。根据客户端与服务器的距离和所选定的路由路径不同,这可能产生几十、几百甚至几千毫秒的延迟。这些工作和延迟是甚至还没有一个字节的应用程序数据开始传输之前就发生的。

    完成了TCP握手之后,如果我们连接的是安全站点(HTTPS),那么还要进行SSL握手。这又增加了客户端和服务器之间两个往返延迟。如果SSL会话进行了缓存,那么可以“免去”其中一次额外的往返延迟。

    最后,Chrome要调度HTTP请求(图1.1中requestStart)。服务器接收到请求之后,会处理该请求,然后通过数据流把响应数据返给客户端。这至少会产生一个往返延迟,还要算上服务器的处理时间。正常情况下这样就结束了,但如果真正的响应是一个HTTP重定向,那么我们可能还需要把整个过程再重走一遍。你的页面上有不必要的重定向吗?那你可能需要重新考虑这个决定了。

    你算过这所有的延迟时间了吗?为了便于说明问题,我们假设一个典型的宽带连接的最坏情况:没有本地缓存、相对较快的DNS查询速度(50ms)、TCP握手、SSL协商、相对较快的服务器响应时间(100ms)、80ms的往返延迟(这是美国大陆往返延迟的平均时间):

    • DNS需要50ms

    • TCP握手需要80ms(一次往返延迟)

    • SSL握手需要160ms(两次往返延迟)

    • 请求传输到服务器需要40ms

    • 服务器处理需要100ms

    • 服务器返回响应需要40ms

    这样算下来,单次请求需要470毫秒,与服务器真正处理请求的时间相比,其中80%都是网络延迟开销。实际上,470毫秒都算是一个乐观的估计了:

    • 如果服务器响应不匹配初始的TCP拥塞窗口(4-15KB),还会额外产生一个或多个往返延迟。【注1】

    • 如果我们需要获取缺失证书或者需要执行在线证书状态检查(OCSP),SSL延迟得还会更厉害,因为这两种情况都需要一次全新的TCP连接,这又增加了成百上千毫秒的额外延迟。

    “足够快”有多快?

    DNS、握手、往返延迟的网络开销是决定前例中总时间的因素——服务器响应时间仅占总延迟的20%。但是,从大局看,这些延迟重要吗?如果你正在阅读本文,你很可能已经知道了答案:不但有影响,而且影响很大。

    过去一些用户体验的研究描述了我们作为用户对应用程序(在线或离线)的响应速度作何预期:

    表1.1 用户对延迟的感知

    延迟用户感知
    0 -100 ms立刻
    100 – 300 ms略感延迟
    300 – 1000 ms机器在运行
    1 s+是不是出问题了
    10 s+等会再来吧……

    表1.1还能够解释网络性能领域中一条不成文的经验法则:如果不能直接呈现页面,至少也要在250毫秒以内提供视觉上的反馈以保持用户不会失去兴趣。其他因素也会影响速度。对Google、Amazon、Microsoft和数千个其他站点的研究显示,额外的延迟会直接影响站点的获利能力:速度快的网站能生成更多的页面请求,用户参与度也更高,从而转化率也更高。

    所以,我们知道了,最佳的延迟应该控制在250毫秒,但我们在前例中看到,DNS查询、TCP和SSL握手再加上请求传递的时间总共有370毫秒。我们已经超出50%了,这还是我们没算上服务器处理时间的情况!

    对于大多数用户乃至一些网络开发者来说,DNS、TCP和SSL的延迟是完全透明的(无须关心的),它们是在网络层协商的,而我们极少深入到甚至不会去想这个层面的事。但是,这其中的每一步对整体用户体验都是非常关键的,因为每增加额外的网络请求都会增加几十或几百毫秒的延迟。这就是为什么Chrome的网络栈要比一个简单的socket处理器复杂的多得多。

    找到了症结所在,我们继续来看一些实现细节。

    Chrome的网络栈概览

    多进程架构

    Chrome的多进程架构对各个网络请求如何在浏览器中进行处理具有重大影响。在底层,Chrome实际上支持四种不同的执行模型用于确定如何进行进程的分配。

    默认情况下,桌面上的Chrome浏览器使用“站点对应进程”模型,将不同站点隔离开来,而把同一站点的所有实例分组在同一进程中。不过为了简便起见,我们假设最简单的情况:每个打开的标签页对应一个单独的进程。从网络性能的角度看,这种差别并不重要,但“标签页对应进程”模型要容易理解得多。

    图1.3 多进程架构

    这个架构为每个标签页配给一个专用的呈现进程。每个呈现进程包含Blink布局引擎和V8 JavaScript引擎,配合上一些胶水代码把这几个部分(再加上其他一些部分)联系起来。【注2】

    这些呈现进程的每一个都在一个沙盒环境内执行,这个环境对用户计算机——包括网络,只有有限的访问权限。要获得访问这些资源的权限,每个呈现进程要与主浏览器进程(或称为内核进程)进行通信,由内核进程负责管理每个呈现器的安全和权限策略。

    进程间通信和多进程资源加载

    Chrome中呈现器和内核进程之间的所有通信都是通过进程间通信(IPC)完成的。在Linux和OS X上使用的是socketpair(),这个方法提供一个命名的管道传输进行异步通信。来自呈现器的每条消息经过序列化处理再传给专用的I/O线程,再由它将其派发给主浏览器进程。在接收端,内核进程提供一个过滤接口,允许Chrome拦截应该由网络栈处理的资源IPC请求(参见ResourceMessageFilter),

    图1.4 进程间通信

    这种架构的一个优点是,所有的资源请求都完全在I/O线程上处理,用户接口产生的活动与网络事件之间互不干扰。资源过滤器运行在浏览器进程中的I/O线程中,截获资源请求消息,并将其转发给浏览器进程中的ResourceDispatcherHost【注3】单例。

    这个单例接口允许浏览器控制各个呈现器的网络访问权限,它还能实现高效和一致性的资源共享。一些例子包括:

    • socket池和连接限制:浏览器能够对每个profile、代理和{scheme, host, port}组所对应的已开启socket数量进行限制(分别为256、32和6个)。注意,按照这个规则,同一{host, port}最多可以进行6个HTTP和6个HTTPS连接。

    • socket重用:持久性的TCP连接会在请求处理之后在socket池中保留一段时间,以供连接重用,这样能够避免发起新的连接额外带来的DNS、TCP和SSL(如有需要)的启动开销。

    • socket后期绑定:当socket准备好分派应用程序请求时,请求才与基础的TCP连接绑定起来,这样一来可以获得更好的请求次序优化(例如:当socket在连接中时,更高优先级的请求到达),更大的流量(例如:在现有socket可用而新连接正在打开时,重用“刚使用过”的TCP连接)以及TCP预连接的通用机制和其他一些优化。

    • 一致的会话状态:所有呈现进程的身份鉴证、cookies和缓存数据都是共享的。

    • 全局性资源和网络优化:浏览器可以从所有呈现进程和未完成请求的全局做出决策。例如,对前景标签页发起的请求赋予网络优先级。

    • 预测性优化:通过观测所有的网络流量情况,Chrome能够构建和修正预测性模型提升性能。

    就呈现进程而言,它只是通过IPC发送资源请求消息,这个请求被打上对应浏览器进程的唯一请求ID,剩下的部分都是由浏览器内核进程处理的。

    跨平台资源获取

    跨Linux、Windows、OS X、Chrome OS、Android和iOS等不同平台的可移植性是Chrome网络栈实现中的一个重要问题。要解决这个挑战性的问题,网络栈被实现为一个几乎单线程(有单独的缓存和代理线程)的跨平台库,使Chrome可以重用相同的基础设施并提供相同的性能优化水平,更有机会进行跨平台的优化。

    移动平台的架构和性能

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

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

    • 桌面用户通过鼠标来浏览,可以进行窗口重叠,屏幕更大,几乎没有电量的约束,网络连接通常更稳定,能够访问更大的存储和内存池。

    • 移动端用户使用触摸和手势浏览,屏幕更小,受制于电池和电量的约束,往往使用流量计量的网络,本地存储和内存也较为有限。

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

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

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


    2017-06-26 13:49:20
    非常不错。。。。。。。
    2016-07-16 17:28:11
    @斯托克笔记 没用过,我可以试试
    2016-07-16 12:31:49
    我现在觉得WIN10的Edge也挺不错的,以前一直用IE。
    2016-07-15 19:01:06
    虽然我用谷歌浏览器,没怎么了解过
    2016-07-15 16:58:05
    不错,这篇文章很专业

    名称(*)

    邮箱

    网址

    一百五十减一百二十七等于几?

    (*)

    本站资源均来自网络,如有侵权请联系我们删除e-mail:benen005@sina.com