资讯专栏INFORMATION COLUMN

[译]关于缓存的那些风流事儿

Eric / 3369人阅读

摘要:于是,让继续前行通过,从而在网络栈这片神奇的土地里继续寻找她需要的资源。而一旦进入网络栈,最容易找到资源的地方就是缓存缓存有时候也被他的朋友成为磁盘缓存和之前遇到过的缓存不太一样。

原文链接

最近大家针对preload、HTTP/2 push和ServiceWorker的浏览器缓存实现展开了激烈的讨论,而这也引起了很多人的疑惑。

鉴于此,我想讲个故事来让大家了解一个请求如何完成他的使命并找到匹配的缓存资源,

以下内容均基于 Chromium 的术语,不过其余浏览器的实现本质上没有太大的差异。

Questy 的旅程

Questy 是一个请求。她是在渲染引擎内(也叫渲染器)诞生的。她渴望能在这个标签页关闭前找到一个让她的“人生”再无遗憾的资源。

所以 Questy 展开了她追求幸福的旅程。 但是她会在哪里找到一个恰恰适合的资源呢?

此时离她最近的是……

内存缓存(Memory Cache)

内存缓存中包含了大量的资源。他包含了所有渲染引擎请求的资源。这些资源都是现有文档的一部分。在文档的生命周期中他们都会被储存在此。这意味着,如果 Questy 寻找的资源已经被文档中的其余部分加载了,那么他们会在此相遇。

确切来说,“短期内存缓存”这个名字可能会更适合。因为内容缓存仅在导航结束前保存这些资源,在某些情况下,时间甚至会更短。

事实上,很多种情况都会导致 Questy 寻找的资源已经被加载。

预加载器(preloader)可能是最常发生的情况。如果 Questy 是由 HTML 解析器创造的 DOM节点所激发的,那么她很可能会发现,她所寻找的资源早已在 HTML 标记化阶段加载完毕了。

显示 preload 指令()则是另一种较为可能发生的情况。该指令会让浏览器预加载资源并存储在内存缓存中。

除此之外,还有可能是因为所请求的资源与之前的 DOM 节点或者 CSS 规则所需要的资源相同。例如,一个页面中可能会含有多个具有相同 src 属性的 元素,但是他们会得到同一个资源。而实现这种机制的正是内存缓存。

然而,内存缓存不会轻易匹配我们的资源请求。当然了,为了使请求和资源相匹配,他们必须要有相同的 URL 。不过,这还不是全部。他们还必须要有相同的资源类型(这样子一个脚本资源才不会被一个图片请求所匹配),相同的 CORS 策略和一些其他特性。

规范并没有十分地明确定义内存缓存所需要匹配的特性,所以不同的浏览器的实现可能会有一定的差异。

有一样东西是内存缓存不关心的,那就是 HTTP 语义。无论资源的头部是是否带有 max-age=0 或者 no-cacheCache-Control标签,内存缓存都不关心。因为在当前导航中,资源是可以重用的,所以 HTTP 语义并不重要。

唯一例外的是no-store指令。在某些特定的情况下浏览器会尊重他。(例如,当资源被多带带节点重用时)。

所以,Questy 走上前询问内存缓存是否有匹配的资源。唉,然而并没有。

Questy 并没有放弃。她走过资源计时器和开发者工具的网络注册点。在那里,她注册为寻找资源的请求(这意味着如果她能找到匹配的资源,则会出现在开发中工具和资源计时器中)。

完成了这些官方登记后,她继续向前……

Service Worker 缓存

和内存缓存不一样,Service Woker喜欢不走寻常路。他的行为难以预测。因为他只遵循开发者告诉他的规则。

首先,Service Worker只有安装后才会存在。而且因为他的逻辑是由开发者编写的 JavaScript 而不是浏览器控制的,所以 Questy 完全不知道她能不能在这里找到那个他?那个资源长成什么的?他是被存储在缓存里吗?还是说他是由 Service Worker 的主人精心伪造的响应?

这些问题没有人可以回答她。因为 Service Worker 自成一套,无论是资源的匹配方式还是响应的包装方法,他们都能按照自己的的想法去完成。

Service Worker 拥有和缓存相关的 API ,这让他可以储存资源。和内存储存不同的是这种存储方式是持久的。即使该标签页被关闭甚至浏览器重启,这些被存储的资源都不会丢失。只有当开发者明确表示要移除他们的时候(使用 cache.delete(resource)),他们才会被移除。另外一种情况就是当浏览器的存储空间不足时,他会将整个 Service Worker 缓存还有其他源存储如 indexedDB、localStorage 等都清除掉。也因此,Service Worker 能确保他的存储和其他源存储是同步的。

Service Worker 只负责特定的域,换言之,他最多只能管理一个 host。因此,Service Worker 只能控制来自特定域内的文档的请求。

Questy 走向 Service Worker 询问他有没有合适的资源。可惜的是 Service Worker 从来没有见过那个域的资源,所以他也找不到 Questy 寻找的请求了。于是,Service Worker 让 Questy 继续前行(通过 fetch()),从而在网络栈这片神奇的土地里继续寻找她需要的资源。

而一旦进入网络栈,最容易找到资源的地方就是……

HTTP 缓存

HTTP 缓存(有时候也被他的朋友成为“磁盘缓存”)和 Questy 之前遇到过的缓存不太一样。

一方面,他们的存储是持久的,而且能被不同的会话甚至不同的网站重用。如果一个资源被一个网站下载了,他也可以被其他网站重用,

而另一方面,HTTP 缓存遵循 HTTP 语义(名字早已暗示了一切)。他乐于提供他认为觉得是“新鲜”的资源(基于由响应的缓存头声明的生命周期)、校验那些需要重新验证的资源、并拒绝存储那些它不应该存储的资源。

既然他是一个持久性的缓存,他也需要移除资源。但和 Service Worker 不一样的事,他会在觉得他需要空间来存储更重要或者会被更多人需要的资源时,逐个移除那些旧资源。

HTTP 缓存拥有一个基于内存的组件。他负责为请求匹配资源。可是一旦资源匹配成功,它需要从磁盘中获取资源内容,这是一个较为昂贵的操作。

上文我们提到 HTTP 缓存遵循 HTTP 语义。这基本是正确的。除了一个例外情况,HTTP 缓存会存储一些资源一段时间。浏览器能够为下次导航预取资源。我们可以通过显示的指令()或者依靠浏览器内部机制完成。这些被预取的资源会被保存下来直到下次导航,尽管它们可能是不允许缓存的。所以当预取资源到达 HTTP 缓存时,它会被缓存(并且不需要校验就会被提供)大概五分钟。

尽管 HTTP 缓存看起来十分的严厉,但 Questy 还是鼓起勇气上前询问有没有匹配的资源。然而答案依旧是没有。

她还是得继续随着网络往前走。这段旅程时可怕而且未知的,然而 Questy 知道无论如何她都要找到她需要的资源。所以她只能继续。这时候她找到了一个对应的 HTTP/2 会话。并且准备通过网络继续前行,这时候她忽然看到了……

推送“缓存”

推送缓存(其实他更应该被描述为“待认领的推送流存储器”,不过那实在是太拗口了)是存储 HTTP/2 推送资源的地方。它们是 HTTP/2 会话的一部分,这有几个特殊的含义。

这个容器并不是持久的。当会话结束后,未被认领的资源(例如,从来没有被请求匹配到的)就会被移除。如果资源是由不同的 HTTP/2 会话获取的,他们并不会匹配。除此之外,推送缓存只会存储资源一段时间(在基于 Chromium 的浏览器里,这个时长约为五分钟)。

推送缓存根据请求的 URL 和请求头匹配相应,但他不遵循严格的 HTTP 语义。

规范里也没有明确定义推送缓存,所以再各个浏览器、系统或者 HTTP/2 客户端间的实现可能会不一样。

尽管信心不大,Questy 还是上前询问是否有匹配的请求。令人惊讶的是,他真的有!!Questy 喜出望外的认领了这个资源(这也意味着它将这个 HTTP/2 流从待认领容器中移除)。现在她可以回去渲染这个资源了。

在他们回程的路上,他们走过了 HTTP 缓存,并且话费了一些时间去复制了一份资源以备日后使用。

离开网络栈后,他们回到 Service Worker 的辖区,而 Service Worker 也将一份资源的拷贝存储到自己的缓存中才让他们回到渲染器里。

最终,一旦它们会到渲染器,内存缓存就会保存一份资源的引用(而不是拷贝)。这样子在稍后如果在同一个导航会话中需要这份资源,他就可以将相同的资源分配给他。

于是,它们就幸福快活的住在了一起,直到文档被移除,然后他们都被垃圾回收了。

不过那是另外一天的故事了。

要点

所以,从 Questy 旅程中我们能学习到什么呢?

不同的请求可以从不同的浏览器缓存中匹配的资源。

请求匹配资源的缓存的不同会影响这个请求是否会被开发者工具和资源计时器所展示。

推送资源不会被持续存储除非他们被请求所认领。

不能存储的预加载资源在下一个导航时不会存在。这是预加载(preload)和预取(prefetch)间的最大区别。

因为还有很多地方规范没有明确定义,所以不同的浏览器实现会有差异。我们需要弥补这些差异。

总而言之,如果你使用预加载,HTTP/2 推送, Service Worker 又或者其他高级技术来加速你的网站,你可能会注意到内部缓存的实现情况。了解这些内部缓存和他们的运作方式能让你更好的解决问题并且减少不必要的麻烦。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/56112.html

相关文章

  • 3月份前端资源分享

    摘要:面试如何防骗一份优秀的前端开发工程师简历是怎么样的作为,有哪些一般人我都告诉他,但是他都不听的忠告如何面试前端工程师 更多资源请Star:https://github.com/maidishike... 文章转自:https://github.com/jsfront/mo... 3月份前端资源分享 1. Javascript 使用judge.js做信息判断 javascript...

    nanchen2251 评论0 收藏0
  • android开发案例

    摘要:多个可以提高你安卓开发技能的开源学习的最佳方式就是阅读,对程序员来说也是如此。年初你绝对想尝试的个新安卓库这是一份年,月份发布的个最佳安卓库的列表,你应该会喜欢,虽然是按顺序排列的,但排名不分先后。 20 多个可以提高你安卓开发技能的开源 app 学习的最佳方式就是阅读,对程序员来说也是如此。如果你想成为一个更优秀的程序员,你必须阅读更多的代码,就是这么简单。书籍,博客,论坛在某种程度...

    LeexMuller 评论0 收藏0
  • [聊一聊系列]聊一聊百度移动端首页前端速度那些事儿

    摘要:要快,但是我们的服务也必须万无一失,后续我会分享百度移动端首页的前端架构设计那么这样的优化,是如何做到的呢,又如何兼顾稳定性,架构性,与速度呢别急,让我们把这些优化一一道来。百度移动端首页的很多就是这样缓存在客户端的。 欢迎大家收看聊一聊系列,这一套系列文章,可以帮助前端工程师们了解前端的方方面面(不仅仅是代码):https://segmentfault.com/blog/fronte...

    The question 评论0 收藏0
  • 58沈剑用3个小时视频告诉你高可用那些事儿

    摘要:本文是到家技术总监沈剑在北京站上的演讲视频。全面解析单点系统的可用性架构与优化消息系统的可达性架构与优化事务系统的一致性架构与优化。时长分钟,建议收藏和转发后在环境下观看。沈剑到家技术总监,互联网架构技术专家,架构师之路公众号作者。 showImg(https://segmentfault.com/img/bVz49R);本文是58到家技术总监沈剑在MPD2016 北京站上的演讲视频。...

    dreamans 评论0 收藏0
  • 前端性能优化

    摘要:端优谈谈关于前端的缓存的问题我们都知道对页面进行缓存能够有利于减少请求发送,从而达到对页面的优化。而作为一名有追求的前端,势必要力所能及地优化我们前端页面的性能。这种方式主要解决了浅谈前端中的过早优化问题过早优化是万恶之源。 优化向:单页应用多路由预渲染指南 Ajax 技术的出现,让我们的 Web 应用能够在不刷新的状态下显示不同页面的内容,这就是单页应用。在一个单页应用中,往往只有一...

    Dean 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<