浏览器的小改进让 Facebook 网站减少 60% 的网络请求

过去两年以来 ,Facebook 网站一直与浏览器厂家合作来改善浏览器缓存性能。最近 Chrome 和 Firefox 推出的新功能,使其缓存性能对于我们以及整个Web 而言都得到了显著提升。这些功能已让用户访问我们服务器静态资源的网络请求降低了 60%,同时极大地改善了页面加载时间。(静态资源是那些需要服务器从磁盘读取的文件,而后仅仅提供它,而无需运行额外代码。)本文将详细介绍我们在 Chrome 和 Firefox 中所做的努力并最终获得上述结果 — 但是我们要先从一些上下文和定义开始,来弄清需解决的问题。 首先,重新验证。

每个重新验证意味着一个新的请求

当用户浏览网页时,会经常重复使用相同的资源 – 诸如 logo 或 JavaScript 代码等,这些资源会在跨页面间被重复使用。 如果浏览器重复地的并下载这些资源,那是非常浪费的。

为了避免这些不必要下载, HTTP 服务器可以为每个请求指定一个到期时间和一个验证器,这样在有效期内可以向浏览器指示不需要下载它。 到期时间会告诉浏览器能重复使用目前最新响应多长时间,并通过Cache-Control 头发送给浏览器。验证器是即便在过期时间以后,也依然允许浏览器继续重新使用网络响应的一种工作方式。 验证器允许浏览器检查资源是否依然有效并重新使用网络响应。验证器通过 Last-Modified 或 Etag 标头发送。

这里是一个示例资源:在1小时内到期并具有 last-modified 验证字段。

该示例中: 接下来的1小时内,接收到这个响应的浏览器可以重复使用它,而不用联系example.com。 之后,浏览器必须通过发送条件性请求来重新验证资源,以便检查图像是否仍然最新:

如果资源未被修改,则发送未修改(304)网络响应。 相对于重新下载整个资源,这将非常有用,因为需要传输的数据将会非常少,但这却并不能消除浏览器与服务器的通讯延迟。 每次发送一个未修改的响应时,浏览器都已经拥有了正确的资源。 我们想要通过允许客户端缓存更长时间来避免这些浪费的重新验证。

长期都无需下载的信号

重新验证会引发一个困扰:到期时间应该需要多久? 如果你发送了1小时的到期时间,浏览器将不得不与服务器通讯,来检查该资源是否每小时修改一次。 但有许多资源,如 Logo 甚至 JavaScript 代码很少改变; 在这些情况下,每个小时就过于频繁了。 另外,如果设定的到期时间过长,浏览器则会将缓存中过期的资源提供并显示给用户。

为解决此问题,Facebook 使用了内容寻址URL的概念。 这里的 URL 不是描述逻辑资源(“logo.png”,“library.js”)的 URL,是我们内容的哈希值。 每当发布网站时,我们都会哈希每个静态资源。 而且我们会维护一个数据库,用于存储这些哈希值,并将它们映射到各自具体的内容上。 服务器提供资源时,不按照名称提供,而是创建一个具有哈希值的 URL。 例如,如果 logo.png 的哈希是 abc123 ,则使用 URL http://www.facebook.com/rsrc.php/abc123.png。

因为该方案使用了文件内容的哈希作为 URL ,它能保证:内容寻址的URL永远不变。 因此,我们就能提供所有内容寻址的 URLs 并设定较长一个的过期时间(目前为 1 年)。 另外,因为这些 URLs 的内容永远不变,所以服务器总会对任何及所有条件性的静态资源请求做出 304 未修改的响应。 这样不但节省了 CPU 周期,也使服务器能够更快地响应这些请求。

重新加载的问题

浏览器里的 reload 按钮,允许用户获取当前页面的最新内容。 因此,当用户选择重新加载时,即使页面未过期,浏览器也会重新验证当前所在页面。 而且,还会进一步重新验证页面上所有的子资源 – 例如图像和JavaScript文件。

这种对子资源的重新验证意味着,即使用户已经访问了正在重新加载的站点,每个子资源也都必须经过服务器读写操作。 对使用内容寻址 URLs 方式的网站(如 Facebook ),这些重新验证请求都是徒劳的。 内容寻址 URLs 的内容永远不变,这样重新验证的响应结果总是 304 未修改。 换而言之,整个重新加载过程中的重新验证、请求和资源都不是必要的。

太多条件性请求

在 2014 年,我们发现 60% 的静态资源网络请求导致了 304 响应.由于内容寻址的 URLs 从来不变,这意味着将我们有机会优化 60% 的静态资源网络请求。 通过使用 Scuba,我们开始探索有关条件性请求的数据。 我们发现不同浏览器的性能有实质的差异。

当发现 Chrome 产生最多的是 304 响应时,我们开始与其合作,确认该浏览器为何发送如此多的条件性请求。

Chrome浏览器

Chrome 中的一段代码为我们寻找答案提供了暗示。 这行代码列出了一些原因,包括重新加载、为什么 Chrome 会要求重新验证页面上的资源。比如我们发现, Chrome 会重新验证通过POST请求加载到网页上的所有资源。 Chrome团队告诉我们,原因是 POST 请求的往往是要更改的页面,例如购物或发送电子邮件,这些用户希望拥有最新的页面。 然而,像Facebook这样的网站使用POST请求作为用户登录过程的一部分。 每当用户登录到 Facebook 时,浏览器会忽略其缓存并重新验证以前下载的所有资源。 我们与 Chrome 的产品经理和工程师们合作并确定认 Chrome 的这种请求行为是否必要。 在处理完该问题后,Chrome 所有网络请求中的条件性请求比例从 63% 降低到 24% 。

与 Chrome 在登录问题上的合作,是 Facebook 与浏览器厂商共同努力,迅速消除 bug 的很好事例。 通常来说,在查阅数据时,我们经常会在每个浏览器级别上分解它们。 如果发现某种浏览器异常,表明我们可以优化该浏览器中的某些内容。 然后,我们就与该浏览器厂商合作解决问题。

实际上,Chrome 条件性请求的百分比依然高于其他浏览器,这也似乎预示着 Chrome 还有一些优化空间。 于是我们开始研究 reload,并且发现 Chrome 在处理相同 的 URL 导航时与reload动作行为相同,而其他浏览器并非如此。 同一个 URL 导航是指用户在访问某个页面后,试图通过导航栏再次加载到该同一页面上的过程。 尽管 Chrome 修复了同一个 URL 动作行为 bug ,但我们并没有看到明显的性能变化。 我们开始与 Chrome 小组讨论,如何更改 reload 按钮的动作行为。

更改 reload 按钮的重新验证行为是对 Web 长期设计做出的一个变更。 而正如我们讨论的这个问题,我们意识到开发者并不太依赖于这种行为。 网站的最终用户对有效期和有条件性请求一无所知。 虽然有些用户在想更新最新页面时可能会按下 reload 按钮,但 Facebook 的统计数据显示,大多数用户不使用 reload 按钮。 因此,如果开发人员正在更改当前具有X的到期时间的资源,要么开发人员让用户在此之前拥有过期数据,要么更改 URL 。 如果开发者已经这样做了,那就没有理由重新验证子资源。

关于做什么,曾有一些辩论,我们也提出了一个妥协的处理方法:永远不重新验证那些长期不使用的资源,但对于较短寿命的资源,它们将适用旧的动作行为。 Chrome 团队考虑到这一点,并决定更改所有缓存的资源,而不仅仅是长期存在的资源。您在这里可以读到关于它们解决过程的更多详细内容。 Chrome的一系列措施,使得所有开发人员和网站都可以看到浏览器的改进,而并不需要他们自己做任何更改。

该示例中: 与以往不同,当重新加载页面上的每个子资源都需要一个网络请求时,用户可以直接从缓存中读取每个文件,而不会阻塞在网络上。

在Chrome 推出这一最终更改后, Chrome 的条件性请求的百分比急剧下降 – 这对我们的服务器而言也是一场胜利,它使得服务器发送更少的未修改请求,而对用户而言,他们能更快地重新加载 Facebook 了。

Firefox浏览器

在 Chrome 解决了问题后,我们便开始与其他浏览器厂商合作解决 reload 按钮的动作行为问题。 我们向 Firefox 提交了一个 Bug ,而但他们选择不改变重新加载按钮的长期动作行为。 而相反的, Firefox 采纳了我们一位工程师的建议,为某些资源添加了一个新的缓存控制头,以便告诉浏览器无须重新验证该资源。 这个控制头背后的想法是,对开发人员及浏览器而言,这是一个额外的承诺:这个资源在其最大使用寿命期间永远不会改变。 Firefox 选择以用 cache-control: immutable 的头格式表示此指示。

使用新添加的 headers 后,一个资源到 Facebook 的请求现在将返回如下响应:

在 Chrome 完全推出其重新加载按钮的最终修复版本时,Firefox 也在快速实施缓存 cache-control: immutable 更改并将其推出。 你可以在这里阅读有关 Firefox 更改的更多信息。

Firefox 的更改使得开发人员开销更多,但在我们修改了服务器以添加不可变 headers 后,我们开始得到非常好的结果。

修复后

Chrome 和 Firefox 的改善措施有效地消除了这些现代版浏览器对我们的重新验证请求 bug 。 不但降低了服务器的拥堵,更重要的是还改善了用户访问 Facebook 的加载时间。

不幸的是,用这种更改方式,我们很难去衡量具体的改善内容- 相比而言,新版本的浏览器包含了许太多更改项目,也几乎不可能将一个特定更改项目所产生的影响单独隔离开来。 然而,在测试这一更改时,Chrome团队能够进行 A / B 测试,该测试发现对于所有网站使用 3G 访问连接的移动用户而言,使用更改后浏览器中 90% 的加载速度都快了 1.6 秒。

总结

这是一个棘手的课题,因为需要我们修改长期的网络动作行为。 它也突出显示了Web浏览器的特性和功能,以及如何与 Web 开发人员合作,使网络更有利于每个人。 我们很高兴在与 Chrome 和 Firefox 团队协作中建立如此良好的工作关系,并对彼此持续的合作来改善每个人的网络而感到兴奋。

1 收藏 评论

关于作者:飞飞_由西向东飞

电子业余爱好者一枚,目前供职某国企。 个人主页 · 我的文章 · 10

可能感兴趣的话题



直接登录
跳到底部
返回顶部