文章:https://jakearchibald.com/2016/caching-best-practices/

正确使用caching能提高网页的的响应的速度,节省带宽,也减少web服务器成本。但是许多网站对他们的caching使用一只半解,导致相互依赖的网页去竞争资源,从而无法同步。

最佳使用缓冲的两种模式:

  一、Pattern 1: Immutable content + long max-age(不变的内容+long max-age)

Cache-Control: max-age=31536000s=365天

  • 这个URL代表的的内容从不改变,因此...
  • 浏览器/CDN能缓存这个资源一年,没问题
  • 在max-age 指定的时间里,缓存的文件可以直接拿来用,不用去tomcat里取需要的文件。
  交互的对象:
  1、page:浏览器需要的文件
  2、cache:浏览器缓存
  3、Server:tomcat 服务方

第一天:Page:喂;您好,我需要 "/script-v1.js", "/styles-v1.css" and "/cats-v1.jpg" 三个文件10:24

Cache:我没有, 服务器,你有吗?10:24

Server:有, 给你, Cache 记住,把这3个资源保存起来,一年之内就不用和我打交道了。10:25

Cache:多谢!
10:25

Page:哦,耶,太好了!10:25

第二天:

Page:你好, 我需要 "/script-v2.js", "/styles-v2.css" and "/cats-v1.jpg" 三个文件08:14

Cache: 我只有"/cats-v1.jpg" 这个文件, 给你.  Server,你有其它两个文件吗?08:14

Server:有, 这些是新的CSS 和JS. 对了,Cache,把这些资源也保存起来,你留着用一年吧!08:15

Cache:超赞!08:15

Page:多谢!

08:15

后来:
Cache:     唉!, "/script-v1.js" & "/styles-v1.css"两个文件,这么久也没人用,为了减少浏览器缓存的负担,我准备删除他们,

不想留一年,删了。

总结:在这种模式中,对于一个指定的URL, 你改变的是URL,从不改变内容。

<scriptsrc="/script-f93bca2c.js"></script>

<linkrel="stylesheet"href="/styles-a837cb1e.css">

<imgsrc="/cats-0e9a2ef4.jpg"alt="…">

上面包含三个静态的URL,每一个URL包含随内容改变的一些信息,如:一个版本号、修改日期、一个hash内容- 我的博客也是这样干的。

大部分服务端框架提供工具来轻松完成一个文件改变的信息,(我使用用 Django 的 ManifestStaticFilesStorage),还有一些很更小的 Node.js 库可以实现同样功能,例如 gulp-rev。

但是,像博客,我经常修改,内容时刻发生变化,这些页面 URL 不能包含版本号,上面的模式不适应了。

二、Pattern 2: Mutable content, always server-revalidated

变化的内容+总是服务器校验

服务器校验:判断URL所需的内容是否已经改变,即文件是否已经更新了,在博客上,我们时时更新,读者也希望看见最新的内容。

Cache-Control: no-cache

第一天;

Page:您好,我需要 "/about/" and "/sw.js" 两个文件.11:32

Cache:  我搞不定啊, Server?11:32

Server:    哈, 我有, 给你, Cache: 你可以存一份,但是用之前,先问问我,不然用不了.11:33

Cache:   明白!11:33

Page: 多谢!
11:33

第二天:

Page:你好,我又需要 "/about/" and "/sw.js" 这两个文件09:46

Cache:   等一下. Server: 我可以用我上次保存的两个文件吗?  保存"/about/"的副本,最后修改日期是周一,  "/sw.js"最后修改是昨天.09:46

Server:自从昨天后 "/sw.js"就没改过呢!09:47

Cache:爽. Page: 给你  "/sw.js".09:47

Server:…但是  "/about/" , 有一个新版本. Cache: 像以前一样, 你能保存一份,但是用之前还是需要和我联系.09:47

Cache:明白!09:47

Page:太好了!

注意: no-cache 并不是指本地浏览器不缓存,耶!我以前看见单词,第一眼给人感觉就是浏览器不缓存。

这意味着,在使用cached资源之前,必须由服务器去验证一下。

no-store 告诉浏览器,完全不用缓存所取得文件。

must-revalidate  并不意味着必须由服务方验证,它意味着,如果本地资源缓存的时间小于max-age时间,那么本地资源可以用, 否则,必须去服务器验证. Yeah. I know.

在这种模式中,你能在响应头增加一个 ETag (你选择的版本ID) or Last-Modified时间。

下一次,客户端请求这些资源时,会通过If-None-Match或If-Modified-Since 带上这上面增加的值。 server说 "直接使用你已经得到的资源, 它是最新的", or 术语就是 "HTTP 304".

如果浏览器不能传送ETag/Last-Modified这个值, 服务器将送全部内容(即一个文件的所有版本全部送回给浏览器).

这种模式总是涉及一个网络去提取文件,因此,它不如模式1好,模式1能完全不用网络,从本地缓冲取。

It's not uncommon to be put off by the infrastructure needed for pattern 1

模式1被基础设施影响,同样的,模式2被网络影响,所以又有了中间方案max-age:a smallish max-age+mutable content,缓冲一段时间+可变的内容,这种方法太糟糕,

三、max-age on mutable content is often the wrong choice

在可变内容max-age 经常是一个错误的选择,

…不幸的是,它经常被使用,例如,用在 Github 页。

Imagine:

  • /article/
  • /styles.css
  • /script.js

…all served with:

Cache-Control: must-revalidate, max-age=600

  • 对应URL的内容发生改变,
  • 如果浏览器有一个缓存版本小于 10 分钟,那么直接使用缓冲区,不用咨询服务器。
  • 否则,通过网络去取, 可能的话,还会带上If-Modified-Since 和 If-None-Match请求头。

第一天:

Page:你好,我需要  "/article/", "/script.js" and "/styles.css" 10:21

Cache:这儿没有, Server?10:21

Server: 没问题, 给你.  Cache: 这些资源可以让你存10分钟.10:22

Cache: 明白!10:22

Page:多谢!10:22

6分钟以后:

Page:你好,我有需要 "/article/", "/script.js" and "/styles.css" 10:28

Cache:哦,真对不起,我弄丢了 "/styles.css",但是我有其它的,给你, Server: 你能给我 "/styles.css"?10:28

Server:当然,自从上次你问我之后,实际上内容已经发生变化,同样,你也可以让它缓存10分钟。10:29

Cache: 没问题.10:29

Page:多谢! 等等! 所有的东西都没有了!! 发生了什么?10:29

这种模式可以在测试中模拟出来,但是在现实中很难复现,很难追踪。在上面的例子中,服务方实际上已经修改稿了HTML,CSS&JS,但是页面从缓冲中取出HTML & JS ,从服务器取出新修改的CSS,所以版本出现问题了,导致页面显示的颜色改变出现问题。

Often, when we make significant changes to HTML, we're likely to also change the CSS to reflect the new structure,

经常,当我们对HTML做了重大改变,我们可能内容JS,和风格CSS同时发生了改变,这些资源相互依赖,但是Caching 头不能表示他们。用户也许获得的1,2个新版本,但是其它的是老版本。

  max-age is relative to the response time, so if all the above resources are requested as part of the same navigation

max-age 和响应时间有关,因此,如果上面所有的资源时同时请求,他们将被设置同时过期的时间,但是仍然有有小部分可能竞争资源。

如果你有一些页面并不包括JS,或不同CS,过期时间可以不同步。更糟糕的是,浏览器总是从cach中删除资源,它并不知道HTML,CSS,&JS是相互依赖,所以出现删除部分资源的情况,总的来说,一个页面所需的所有资源版本有可能不一致。

对用户来说,这个能导致布局和功能的破坏,从一个小过错,到完全不能使用得内容。

谢天谢地,对用户来说,有一个解决方案…

四、有时一个刷新能解决上面的问题

如果页面作为刷新一部分被装载,浏览器将总是去服务器验证,忽略了max-age.

因此如果用户使用max-age,但经历了一些破坏,点击刷新应该能修复任何资源。但是,强迫用户去刷新,会减少用户信任yonh 给用户的感觉就是,你的网站很不稳定。

五、一个服务工人能延长bug的生命周期。

你有下面的服务工人代码:

const version = '2';
self.addEventListener('install', event => {
  event.waitUntil(
    caches.open(`static-${version}`)
      .then(cache => cache.addAll([
        '/styles.css',
        '/script.js'
      ]))
  );
});
self.addEventListener('activate', event => {
  // …delete old caches…
});
self.addEventListener('fetch', event => {
  event.respondWith(
    caches.match(event.request)
      .then(response => response || fetch(event.request))
  );
});

This service worker…

  • Caches the script and styles up front。
  • 如果cache有,从cache取,否则去网络取。

如果我们修改我们的CSS/JS,我们也同时修改version, 来让service worker 缓存失效,触发更新.然而, 因为 addAll 会从 HTTP缓存中获取资源(像其它所有取资源一样), 我们可能碰上 max-age race condition and cache CSS&JS 不兼容版本。

一旦他们被缓存,那就意味着直到下次更新service worker 之前, 我们将一直用不兼容的 CSS & JS  -还是假设下次更新不出现竞争的情况

你也可以在 service worker里绕过 HTTP缓存:

self.addEventListener('install', event => {
  event.waitUntil(
    caches.open(`static-${version}`)
      .then(cache => cache.addAll([
        new Request('/styles.css', { cache: 'no-cache' }),
        new Request('/script.js', { cache: 'no-cache' })
      ]))
  );
});

不幸的是当前Chrome/Opera都不支持cache选项,只有最新的 Firefox Nightly才支持,当然你也可以自己解决

self.addEventListener('install', event => {
  event.waitUntil(
    caches.open(`static-${version}`)
      .then(cache => Promise.all(
        [
          '/styles.css',
          '/script.js'
        ].map(url => {
          // cache-bust using a random query string
          return fetch(`${url}?${Math.random()}`).then(response => {
            // fail on 404, 500 etc
            if (!response.ok) throw Error('Not ok');
            return cache.put(url, response);
          })
        })
      ))
  );
});

In the above I'm cache-busting with a random number,

上面,我是通过随机数字绕过缓冲区,但是你能再进一步,利用构建工具增加一个hash 内容(类似于sw-precache所做的工作),这有点像在 JavaScript中实现了模式一,但是只能让 service worker 用户受益,不包括所有的浏览器和 CDN。、四、service worker & the HTTP cache 相互配合,而不是相互打架。

As you can see, you can hack around poor caching in your service worker, but you're way better off fixing the root

正如你能看见,你能在service worker绕过你的糟糕的缓存,但是最好从根解决问题,正确使用缓存,不但可以简化 service worker,而且还可以让那些不支持 service worker的浏览器获益(Safari、IE/Edge),也能用好 CDN

Correct caching headers means you can massively streamline service worker updates too:

正确的配置缓存头意味着,你能大大的简化服务工人更新

const version = '23';
self.addEventListener('install', event => {
  event.waitUntil(
    caches.open(`static-${version}`)
      .then(cache => cache.addAll([
        '/',
        '/script-f93bca2c.js',
        '/styles-a837cb1e.css',
        '/cats-0e9a2ef4.jpg'
      ]))
  );
});

上面,缓存根页面,我使用了模式2(可变内容,每次都走服务端验证)缓存 HTML页面,其它的资源使用模式1(不变内容 +长时间 max-age)。每一个sevie worker 修改将触发一个根页面的请求,而其它资源只有在 URL发生变化时才会再次下载。这个非常棒,无论是从上个版本还是上十个版本更新都能节省带宽、提高性能。

相比有细微改动整个二进制文件就要重新下载,或者要实现复杂的二进制 diff的原生应用,这是一个巨大的优点。只需要少量下载,就可以更新大型 Web应用。

service worker最好用于局部增强而不是提供整套方案,它应该与 HTTP缓存配合使用,而不是相互打架。

五、仔细的使用,max-age +可变内容 非常好。

max-age on mutable content is often the wrong choice, but not always.

max-age 在可变内容上经常是一个错误的选择,但是不完全是。例如,这页设置一个max-age=180s,竞争条件在这儿不是一个问题,因为这一页没有依赖其它模式(我的CSS, JS & image URLs 遵循 pattern 1 - immutable content),即这一页没有同时实现两种模式(1种模式可变,另外一种模式不可变)。

这种模式意味着,如果我足够幸运去写一个流行的文章,我的CDN会帮服务器扛住流量,只要我能接受修改文章要等三分钟才能被用户看到,我确实可以接受。

这种模式用起来没那么容易。如果我增加一个新段到一篇文章中,另一篇文章连接它,我已经建立一个竟争的依赖。用户可能点击链接后看到的是不包含新增内容的缓存副本。

如果我想避免这个,我将修改第一篇文章,刷新 Cloudflare's 缓存,等3分钟后,然后在其它文章中增加连接。你必须非常小心地使用这种模式。

使用正确,caching可以大量的性能增强和带宽节省。支持 不可变内容的URL能轻松的改变,否则用server 验证更安全。如果你很勇敢,你确认你的内容没有依赖别人或别人依赖你,那么才使用max-age+mutable内容 模式。因为这种模式是不同步的。

Used correctly, caching is a massive performance enhancement and bandwidth saver. Favour immutable content for any URL that can easily change, otherwise play it safe with server revalidation. Only mix max-age and mutable content if you're feeling brave, and you're sure your content has no dependancies or dependents that could get out of sync.

Caching best practices max-age相关推荐

  1. cookie的max age

    max age=0   命令浏览器立即删除该cookie max age 设置为负值的话,命令浏览器保存该cookie,关闭浏览器就失效.

  2. 查询name和age当name相同时选择age最大的

    name age 小张 20 小王 21 小李 22 小赵 23 小张 24 小张 25 小赵 26 小李 27 小王 28 1.查询name和age当name相同时选择age最大的 select [ ...

  3. pythonmax对字符_(MAX第五篇)Python--字符串操作(三)

    字符串操作(三) 此篇总结包含字符串的替换.转换以及字符串格式化 替换或调整字符串 Code Return string.replace(str1, str2, num=string.count(st ...

  4. avg最多用多少列 mysql_MySQL之聚合数据(AVG,COUNT,MAX,MIN,SUM)

    1.首先我们需要了解下什么是聚合函数 聚合函数aggregation function又称为组函数.认情况下 聚合函数会对当前所在表当做一个组进行统计. 2.聚合函数的特点 1.每个组函数接收一个参数 ...

  5. mongodb更新操作符$min,$max

    mycode db.person.deleteOne({name:'lnj'}) db.person.insertOne({name:'zs',age:18}) //$min是用min指定的值和原来的 ...

  6. T-SQL MAX Functions

    SQL Server MAX()函数,运算时返回字段值最大值,排除NULL值. 下面示例中,是计算出会员表中最大年龄: 数据源: SQL语句: SELECT MAX([Age]) AS [MaxAge ...

  7. max等聚合函数和group by搭配使用的注意事项

    1 group by的特点 1.1 定义 group_by的意思是根据by对数据按照哪个字段进行分组,或者是哪几个字段进行分组. 如果不在group by 后的分组中使用聚合函数,一般只会返回各个分组 ...

  8. oracle函数max,oracle max()函数和min()函数

    当需要了解一列中的最大值时,可以使用MAX()函数:同样,当需要了解一列中的最小值时,可以使用MIN()函数.语法如下. SELECT MAX (column_name) / MIN (column_ ...

  9. 差分龄期(age of diff)对RTK定位精度的影响分析(包含RTKLIB实际测试)

    本篇博客分为实际数据测试部分,以及公式推导部分. 差分龄期(Age of differential)即基站和移动站之间的时间差.在实时定位中,可能由于时间延迟.网络故障等原因未能接收到当前时刻的基站信 ...

最新文章

  1. 前端如何转换 schema 和 xml
  2. NVelocity模板引擎初学总结。[zhuan]
  3. RabbitMQ消息重复消费问题
  4. Java Serializable:明明就一个空的接口嘛
  5. 关于Java你不知道的10件事
  6. Qt for Android 部署流程分析
  7. 03MyBatis动态sql
  8. JVM面试重点总结(一)——java内存区域与内存溢出异常
  9. 基于 libevent 开源框架实现的 web 服务器
  10. 【linux网络】net_device及注册与销毁
  11. Trimble Sketchup Pro 2013 英文破解版
  12. 全文检索同时高亮多个字段
  13. Pollard-Rho算法模板(POJ 1811 Prime Test)
  14. 技术人员谈管理之帕累托法则(80/20法则)
  15. HTTP 错误 404.5 - Not Found
  16. word提示 由于宏安全设置,无法找到宏或宏被禁用 隐藏的模块中的编译错误Declaration32 解决方法
  17. Unity(一)入门:Unity Hub下载 Unity安装
  18. 【收藏】六度分隔、六度空间(Six Degrees of Separation)理论
  19. 安卓 usb音量调节_各大厂商不重视的音量调节键,被一个安卓APP玩出花了
  20. 【网络安全】溯源NAT之前的IP地址

热门文章

  1. vue实现词云图(echarts/Highcharts)
  2. 前端VUE3+Vite -- 框架搭建
  3. rsync不覆盖已经存在文件的方法
  4. 分享一种高效伪随机数生成算法
  5. Windows11设置共享打印机
  6. 年产一万吨苹果醋车间布置图、年产50吨羧乙基壳聚糖的工艺设计车间平面布置图、砂石骨料加工系统废水处理车间工艺流程图、氯化苄生产车间平面布置图、年产2000吨聚丙烯腈原丝生产工艺设计车间平面布置图……
  7. CSS 列表样式(ul)
  8. deeplearning.38序列模型(搭建RNN与LSTM应用实践)
  9. 剑走偏锋——老女人教你另类情人节攻略
  10. was mutated while being enumerated