• Chrome高性能的秘密:Web应用面对的性能问题

    连接的资源越来越多
    服务器君一共花费 20.185 ms 进行了 4 次数据库查询,努力地为您提供了这个页面。
    广告很萌的

    前面我们提到了,WebKit是一个开源的项目,该项目专注于网页内容的展示,开发出一流的网页渲染引擎。Chrome是使用WebKit的典型浏览器(虽然现在转Blink,但是Blink也是WebKit的扩展)。我们这里就探讨一下关于Chrome浏览器的一些知识与特性。

    驱动 Chrome 继续前进的核心原则包括:

    • Speed: 做最快的(fastest)的浏览器。
    • Security:为用户提供最为安全的(most secure)的上网环境。
    • Stability: 提供一个健壮且稳定的(resilient and stable)的 Web 应用平台。
    • Simplicity: 以简练的用户体验(simple user experience)封装精益求精的技术(sophisticated technology)。

    关于性能

    一个现代浏览器就是一个和操作系统一样的平台。在 Chrome 之前的浏览器都是单进程的应用,所有页面共享相同的地址空间和资源。引入多进程架构这是 Chrome 最为著名的改进。

    一个进程内,Web 应用主要需要执行三个任务:

    • 获取资源;
    • 页面排版及渲染;
    • 运行 JavaScript。

    渲染和脚本都是在运行中交替以单线程的方式运行的,其原因是为了保持 DOM 的一致性,而 JavaScript 本身也是一个单线程的语言。所以优化渲染和脚本运行无论对于页面开发者还是浏览器开发者都是极为重要的。

    Chrome 的渲染引擎是 WebKit,JavaScript Engine 则使用深入优论的 V8 (“V8″ JavaScript runtime)。但是,如果网络不畅,无论优化 V8 的 JavaScript 执行,还是优化 WebKit 的解析和渲染,作用其实很有限。巧妇难为无米之炊,数据没来就得等着。

    相对于用户体验,作用最为明显的就是如何优化网络资源的加载顺序、优先级及每一个资源的延迟时间(latency)。也许你察觉不 到,Chrome 网络模块每天都在进步,逐步降低每个资源的加载成本:向 DNS lookups 学习,记住页面拓扑结构(topology of the web), 预先连接可能的目标网址,等等,还有很多。从外面来看就是一个简单的资源加载的机制,但在内部却是一个精彩的世界。

    关于 Web 应用

    开始正题前,还是先来了解一下现在网页或者 Web 应用在网络上的需求。

    HTTP Archive 项目一直在追踪网页构建。除了页面内容外,它还会分析流行页面使用的资源数量,类型,头信息以及不同目标地址的元数据(metadata)。下面是 2013 年 1 月的统计资料,由 300,000 目标页面得出的平均数据:

    1280 KB 包含 88 个资源(Images,JavaScript,CSS …)

    连接 15 个以上的不同主机(distinct hosts)。

    这些数字在过去几年中一直持续增长( steadily increasing ),没有停下的迹象。这说明我们正不断地建构一个更加庞大的、野心勃勃的网络应用。还要注意,平均来看每个资源不过 12KB, 表明绝大多数的网络传输都是短促(short and bursty)的。这和 TCP 针对大数据、流式(streaming)下载的方向不一致,正因为如此,而引入了一些并发症。下面就用一个例子来抽丝剥茧,一窥究竟……

    一个 Resource Request 的一生 W3C 的 Navigation Timing specification 定义了一组 API,可以观察到浏览器的每一个请求(request)的时序和性能数据。下面了解一些细节:

    给定一个网页资源地址后,浏览器就会检查本地缓存和应用缓存。如果之前获取过并且有相应的缓存信息(appropriate cache headers)(如 Expires, Cache-Control, etc.), 就会用缓存数据填充这个请求,毕竟最快的请求就是没有请求(the fastest request is a request not made)。否则,我们重新验证资源,如果已经失效(expired),或者根本就没见过,一个耗费网络的请求就无法避免地发送了。

    给定了一个主机名和资源路径后,Chrome 先是检查现有已建立的连接(existing open connections)是否可以复用, 即 sockets 指定了以(scheme、host 和 port)定义的连接池(pool)。但如果配置了一个代理,或者指定了 proxy auto-config (PAC)脚本,Chrome 就会检查与 proxy 的连接。PAC 脚本基于 URL 提供不同的代理,或者为此指定了特定的规则。与每一个代理间都可以有自己的 socket pool。最后,上述情况都不存在,这个请求就会从 DNS 查询(DNS lookup)开始了,以便获得它的 IP 地址。

    幸运的话,这个主机名已经被缓存过。否则,必须先发起一个 DNS Query。这个过程所需的时间和 ISP,页面的知名度,主机名在中间缓存(intermediate caches)的可能性,以及 authoritative servers 的响应时间这些因素有关。也就是说这里变量很多,不过一般还不致于到几百毫秒那么夸张。

    拿到解析出的 IP 后,Chrome 就会在目标地址间打开一个新 TCP 连接,我们就要执行一个 3 度握手(“three-way handshake”): SYN > SYN-ACK > ACK。这个操作每个新的 TCP 连接都必须完成,没有捷径。根据远近,路由路径的选择,这个过程可能要耗时几百毫秒,甚至几秒。而到现在,我们连一个有效的字节都还没收到。

    当 TCP 握手完成了,如果我们连接的是一个 HTTPS 地址,还有一个 SSL 握手过程,同时又要增加最多两轮的延迟等待。如果 SSL 会话被缓存了,就只需一次。

    最后,Chrome 终于要发送 HTTP 请求了 (如上面图示中的 requestStart)。 服务器收到请求后,就会传送响应数据(response data)回到客户端。这里包含最少的往返延迟和服务的处理时间。然后一个请求就完成了。但是,如果是一个 HTTP 重定向(redirect)的话?我们又要从头开始这个过程。如果你的页面里有些冗余的重定向,最好三思一下。

    你得出所有的延迟时间了吗? 我们假设一个典型的宽带环境:没有本地缓存,相对较快的 DNS lookup (50ms), TCP 握手,SSL 协商,以及一个较快服务器响应时间(100ms)和一次延迟(80ms,在美国国内的平均值):

    • 50ms for DNS
    • 80ms for TCP handshake (one RTT)
    • 160ms for SSL handshake (two RTT’s)
    • 40ms (发送请求到服务器)
    • 100ms (服务器处理)
    • 40ms (服务器回传响应数据)

    一个请求花了 470 毫秒, 其中 80% 的时间被网络延迟占去了。看到了吧,我们真得有很多事情要做!事实上,470 毫秒已经很乐观了。

    如果服务器没有达到到初始 TCP 的拥塞窗口(congestion window),即4-15KB,就会引入更多的往返延迟。 SSL 延迟也可能变得更糟。如果需要获取一个没有的认证(certificate)或者执行 online certificate status check (OCSP), 都会让我们需要一个新的 TCP 连接,又增加了数百至上千毫秒的延迟。

更多 推荐条目

Welcome to NowaMagic Academy!

现代魔法 推荐于 2013-02-27 10:23   

本章最新发布
随机专题
  1. [软件工程与项目管理] 浏览器的CSS解析 7 个条目
  2. [PHP程序设计] PHP中的Hash算法 3 个条目
  3. [JavaScript程序设计] 关于HTTP Keep-Alive 6 个条目
  4. [移动开发] Layout_weight属性解析 5 个条目
  5. [运维管理] 路由器与交换机 4 个条目
  6. [Python程序设计] Django数据库模型 6 个条目
  7. [Python程序设计] Django 入门知识浅介 10 个条目
  8. [移动开发] 刷机与root相关 2 个条目
  9. [智力开发与知识管理] 整体性学习策略 9 个条目
  10. [JavaScript程序设计] jQuery与表单操作 2 个条目
  11. [移动开发] Android抽屉导航NavigationDrawer 5 个条目
  12. [Python程序设计] Python语言概述 6 个条目
窗口 -- [博客]