浏览器上的cache

156 阅读9分钟

📜 cache的作用:

  • 缓存发现请求的资源已经被存储,它会拦截请求,返回该资源的拷贝,而不会去源服务器重新下载。缓解了服务器端压力。
  • 减少了等待时间和网络流量,因此减少了显示资源表示形式所需的时间。显著提高网站和应用程序的性能。

*怎么样工作? *当客户端请求后,会先访问缓存数据库看缓存是否存在。如果存在则直接返回;不存在则请求真的服务器,响应后再写入缓存数据库。

三级缓存原理:

  1. 先去内存查找,如果有,直接加载
  1. 如果内存没有,择取硬盘获取,如果有直接加载
  1. 如果硬盘也没有,那么就进行网络请求,加载到的资源缓存到硬盘和内存

📜 cache的类型:

  • 强制缓存:相比于协商缓存客户端请求会先读取强缓存。Cache-control 的优先级高于 Expires,为了兼容 HTTP/1.0 和 HTTP/1.1,实际项目中两个字段我们都会设置。

    • http/1.0版本,Expires表示缓存到期时间,是一个绝对的时间戳 (当前时间+缓存时间)。

      缺陷:由于是绝对时间,用户可能会将客户端本地的时间进行修改,而导致浏览器判断缓存失效,重新请求该资源。此外,即使不考虑修改,时差或者误差等因素也可能造成客户端与服务端的时间不一致,致使缓存失效。

    • http/1.1版本,Cache-control该字段表示资源缓存的最大有效时间,在该时间内,客户端不需要向服务器发送请求

      Cache-control 字段常用的值:
      s-maxage: 设置代理服务器缓存的最大的有效时间,单位为秒(s)。s-maxage会覆盖掉max-age。
      max-age:即最大有效时间,在上面的例子中我们可以看到。max-age会覆盖掉expires。
      must-revalidate:如果超过了 max-age 的时间,浏览器必须向服务器发送请求,验证资源是否还有效。
      no-cache:虽然字面意思是“不要缓存”,但实际上还是要求客户端缓存内容的,只是是否使用这个内容由后续的对比来决定。
      no-store: 真正意义上的“不要缓存”。所有内容都不走缓存,包括强制和对比。
      public:所有的内容都可以被缓存 (包括客户端和代理服务器, 如 CDN)
      private:所有的内容只有客户端才可以缓存,代理服务器不能缓存。默认值。
      
  • 协商缓存:当没有使用强制缓存或强缓存失效(超过规定时间)时,就需要使用协商缓存,由服务器决定缓存内容是否失效。浏览器先请求缓存数据库,返回一个缓存标识。之后浏览器拿这个标识和服务器通讯。如果缓存未失效,则返回 HTTP 状态码 304 表示继续使用,于是客户端继续使用缓存;如果失效,则返回新的数据和缓存规则,浏览器响应数据后,再把规则写入到缓存数据库。Etag 的优先级高于 Last-Modified 在协商缓存没有失效时,服务端只会返回响应头,并不会返回实体。

    • http/1.0版本,Last-Modified & If-Modified-Since

      1. Last-Modified & If-Modified-Since,服务器通过 Last-Modified 字段告知客户端,资源最后一次被修改的时间。
      1. 下一次请求相同资源时时,浏览器从自己的缓存中找出“不确定是否过期的”缓存。因此在请求头中将上次的 Last-Modified 的值写入到请求头的 If-Modified-Since 字段。
      1. 服务器会将 If-Modified-Since 的值与 Last-Modified 字段进行对比。如果相等,则表示未修改,响应 304;反之,则表示修改了,响应 200 状态码,并返回数据。

      • 缺陷:

        1. 如果资源更新的速度是秒以下单位,那么该缓存是不能被使用的,因为它的时间单位最低是秒;
        1. 如果文件是通过服务器动态生成的,那么该方法的更新时间永远是生成的时间,尽管文件可能没有变化,所以起不到缓存的作用;
        1. 编辑了文件,但文件的内容没有改变。服务端并不清楚我们是否真正改变了文件,它仍然通过最后编辑时间进行判断。因此这个资源在再次被请求时,会被当做新资源,进而引发一次完整的响应——不该重新请求的时候,也会重新请求。
    • http/1.1版本,Etag & If-None-Match

      Etag 存储的是文件的特殊标识(一般都是 hash 生成的),服务器存储着文件的 Etag 字段。之后的流程和 Last-Modified 一致,只是 Last-Modified 字段和它所表示的更新时间改变成了 Etag 字段和它所表示的文件 hash,把 If-Modified-Since 变成了 If-None-Match。服务器同样进行比较,命中返回 304, 不命中返回新资源和 200。

📜 cache的位置:

关于缓存存放位置的划分规则,纠结了很久。查了很多文章,内存是有限的,很多时候需要先考虑即时呈现的内存余量,再根据具体的情况决定分配给内存和磁盘的资源量的比重——资源存放的位置具有一定的随机性。

虽然不知道划分规则,但根据日常开发中观察的结果,我们至少可以总结出这样的规律:Base64 格式的图片,几乎永远可以被塞进 memory cache;此外,体积不大的 JS、CSS 文件,也有较大地被写入内存的几率——相比之下,较大的 JS、CSS 文件就没有这个待遇了,内存资源是有限的,它们往往被直接甩进磁盘。

根据浏览器内存空闲情况,如果内存使用率很低就优先放入内存,反之则放入磁盘。对于一些大的文件,浏览器还是放在了磁盘内存里,因为大文件太占内存空间。

graph TD;
   ServiceWorker-->MemoryCache-->DiskCache-->网络请求;

webkit的资源分类主要分为两大类:主资源和派生资源。

  • WebKit派生资源包含的类型主要如下:

    Javascript脚本(CachedScript);

    CSS样式文本(CachedCSSStyleSheet);

    图片(CachedImage);

    字体(CachedFont);

    XSL样式表(CachedXSLStyleSheet);

200 from memory cache

不访问服务器,直接读缓存,从内存中读取缓存。此时的数据时缓存到内存中的,当kill进程后,也就是浏览器关闭以后,数据将不存在。但是这种方式只能缓存派生资源。

200 from disk cache

不访问服务器,直接读缓存,从磁盘中读取缓存,当kill进程时,数据还是存在。这种方式也只能缓存派生资源

304 Not Modified

访问服务器,发现数据没有更新,服务器返回此状态码。然后从缓存中读取数据。(协商缓存每次都会发起 HTTP 请求,但当缓存内容仍有效时可以跳过 HTTP 响应体的下载)

memory cache和disk cache的特性

  • 内存缓存(from memory cache):内存缓存具有两个特点,分别是快速读取和时效性:

    • 时效性:一旦该进程关闭,则该进程的内存则会清空。
    • 快速读取:内存缓存会将编译解析后的文件,直接存入该进程的内存中,占据该进程一定的内存资源,以方便下次运行使用时的快速读取。
  • 硬盘缓存(from disk cache):硬盘缓存则是直接将缓存写入硬盘文件中,读取缓存需要对该缓存存放的硬盘文件进行I/O操作,然后重新解析该缓存内容,读取复杂,速度比内存缓存慢。
  • [*push cache** ] *:是指 HTTP2 在 server push 阶段存在的缓存。这块的知识比较新:

    • Push Cache 是缓存的最后一道防线。浏览器只有在 Memory Cache、HTTP Cache 和 Service Worker Cache 均未命中的情况下才会去询问 Push Cache。
    • Push Cache 是一种存在于会话阶段的缓存,当 session 终止时,缓存也随之释放。
    • 不同的页面只要共享了同一个 HTTP2 连接,那么它们就可以共享同一个 Push Cache。
  • prefetch cache:只能说很香。

📜 列举cache的几种应用模式:

缓存静态资源,不常变化的资源

Cache-Control: public, max-age=31536000  //public表示那么它既可以被浏览器缓存,也可以被代理服务器缓存

//private则表示该资源只能被浏览器缓存。private 为默认值。

Cache-Control: max-age=36000, s-maxage=31536000//如果设置了 s-maxage,但没设置 public,那么 CDN 还是可以缓存这个资源的。

经常变化的资源

no-cache 绕开了浏览器:我们为资源设置了 no-cache 后,每一次发起请求都不会再去询问浏览器的缓存情况,而是直接向服务端去确认该资源是否过期(协商缓存)。

no-store 顾名思义就是不使用任何缓存策略。在 no-cache 的基础上,它连服务端的缓存确认也绕开了,只允许你直接向服务端发送请求、并下载完整的响应。

Cache-Control: no-cache //指定 no-cache  max-age=0, must-revalidate 表示客户端可以缓存资源,
//每次使用缓存资源前都必须重新验证其有效性。

Cache-control: no-store

💡客户端缓存导致用户无法及时更新到新版本?

location ~ /xxxx {
   ...other config
	 if ($request_filename ~* .*.(htm|html)$) {
				add_header Pragma no-cache; //Pragma: no-cache可以兼容http 1.0和http 1.1,
       // add_header Cache-Control "private, no-store, no-cache, must-revalidate, proxy-revalidate";
   }
   if ($request_filename ~* .*.(js|css|svg|eot|woff|ttf|gif|jpe?g|png|bmp|webp|swf|mp3|ico)$) {
       add_header expires 60d;
       add_header Cache-Control max-age=5184000;
   }
}