Caching best practices & max-age

来源:互联网 发布:js全角转半角 编辑:程序博客网 时间:2024/06/04 19:59

文章: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的浏览器获益(SafariIE/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.




















   


 

原创粉丝点击