Cache

文章目录

缓存

缓存具有优先级,根据其位置不同可分为下面。浏览器查找缓存的时候,依次根据优先级查找,如果都没有找到的时候,便会发起网络请求:

  1. Service Worker
  2. Memory Cache
  3. Disk Cache
  4. Push Cache
  5. network request

Service Worker

navigation.serviceWorker

Service Worker 是运行在浏览器的另一个独立线程

  • 传输协议必须是HTTPS(Service Worker 中涉及到请求拦截,所以必须使用 HTTPS 协议来保障安全。)

步骤:

  1. 注册Server Worker
1
navigator.serviceWorker.register('worker.js').then(e => console.log('success')).catch(e => console.log('error'))
  1. 监听install事件,缓存需要的文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// worker.js
self.addEventListener('install', (e) => {
e.waitUntil(
caches.open('my-cache').then(cache => {
return cache.addAll(['./index.html', './index.js'])
})
)
})

// 拦截请求
self.addEventListener('fetch', (e) => {
e.respondWith(
cache.match(e.request).then(response => {
if (responese) {
return responese
}
})
)
})
  1. 在下次用户访问的时候就可以通过拦截请求的方式查询是否有缓存,如果存在缓存就使用缓存,如果没有就去请求数据
  2. 当Service Worker没有命中缓存的时候,会根据缓存优先度去查找数据。(当时无论是从Memory Disk中还是从网络中获取数据,浏览器都是显示是从Service Worker中获取的)

Memory Cache

Memory Cache 就是内存中的缓存,它会随着进程的释放而释放。持续时间很短,存储量少

Disk Cache

Disk Cache 是存储在硬盘中的缓存,它较于Memory Cache具有时效性长(根据http返回的header设置时间),容量大的特点。

Push Cache

Push Cache 是HTTP/2.0 的内容 ,当上面三种缓存都没命中的时候,才会触发这种缓存。

它的缓存时间很短,只在一个会话(session)中存在,一点会话结束就会被释放

HTTP/2 push is tougher than I thought

  • 所有的资源都能被推送,但是 Edge 和 Safari 浏览器兼容性不怎么好
  • 可以推送 no-cache 和 no-store 的资源
  • 一旦连接被关闭,Push Cache 就被释放
  • 多个页面可以使用相同的 HTTP/2 连接,也就是说能使用同样的缓存
  • Push Cache 中的缓存只能被使用一次
  • 浏览器可以拒绝接受已经存在的资源推送
  • 你可以给其他域名推送资源

缓存策略

通常浏览器的缓存策略分为两种:协商缓存 和 强缓存

强缓存

强缓存代表在缓存期间不需要请求,直接返回200

  • Expires:HTTP/1.0产物,表示资源在某个时间后过期,并且受制于系统时间
  • Cache-Control:max-age:30 优先级高于Expires,表示该资源在30s后过期,需要再次请求。

Cache-Control常见指令:

  • public 响应可以同时被客户端和代理服务器缓存
  • private 相应只能被客户端缓存
  • max-age 缓存存活时间
  • s-max-age 覆盖max-age,作用一样,只在代理服务器生效
  • no-store 不缓存任何响应
  • no-cache 资源被缓存,但是立即失效,下次请求验证资源是否过期
  • max-stale ns内,即使缓存过期,也使用该缓存
  • max-fresh 希望在30s内获取最新响应

max-age=0, no-cache 等同 must-revalidate

协商缓存

如果缓存过期了,就需要发起请求验证资源是否过期。如果资源没有改变,服务器端返回304表示无更新,并更新缓存有效期

  • Last-Modified
  • ETag

Last-Modified 和 If-Modified-Since

Last-Modified 表示本地文件最后的修改日期,其中If-Modified-Since会将Last-Modified的值发送给服务器,询问服务器在该日期后资源是否有更新,如果有就将新的资源发送回来,如果没有就返回304

弊端:

  • 如果在本地打开了文件,即使没有修改也会造成Last-Modified的值被修改,从而导致服务端不能命中缓存导致发送相同资源
  • 因为Last-Modified只能以秒计算,所以如果在1s内修改了文件,服务端会认为资源还是有效的,从而不能发送正确的资源

ETag 和 If-None-Match

ETag类似于文件指纹,优先级高于Last-Modified
If-None-Match会将当前的ETag发送给服务端,如果有变动就发送新的资源回来

ETag

语法:

1
ETag: W/"<ETag value>"
  • W 为可选值(大小写敏感),表示使用弱验证器
  • 表示位于双引号的字符串。没有明确指定生成ETag值的方法,通常使用内容的hash以及最后修改时间的时间戳的hash值,或简单地使用版本号。

Apache将文件索引节(inode),大小(size)和最后修改时间作为输入求值得到
在3.23版本后移除inode,只留下大小和时间戳

  1. ETag可以避免“空中碰撞”:

如果正在编辑文章的时候,当前wiki内容被hash,然后将ETag内容放入响应中(服务端)

将更改保存到wiki页面的时候(发布数据),POST请求可以附带含有ETag值的If-Match来检查是否最新版本。

如果不是最新版本,意味着文档已经被编辑,抛出412提示条件判断错误。

  1. ETag可以缓存未更改的资源

如果用户再次访问已经过期的含有ETag资源的时候,客户端就会发送一个含有If-None-Match的字段到服务端

服务端判断两个ETag的值是否一致,如果一致则返回304状态码,告诉客户端缓存可用

如果服务端同时设置了ETag和Last-Modified,那么这两个谁的优先度高?

一般来说,ETag的优先度会比Last-Modified优先度高

from https://www.rfc-editor.org/rfc/rfc7232.txt, RFC 7232:
A recipient MUST ignore If-Modified-Since if the request contains an
If-None-Match header field; the condition in If-None-Match is
considered to be a more accurate replacement for the condition in
If-Modified-Since, and the two are only combined for the sake of
interoperating with older intermediaries that might not implement
If-None-Match.
What if both If-Modified-Since and If-None-Match are present in HTTP headers
What takes precedence: the ETag or Last-Modified HTTP header?

既然有了 ETag,那么 Last-Modified 还有存在的必要吗?

有必要存在:

  1. 兼容性考虑
  2. ETag计算需要耗费性能,对于要求不高的资源可以使用Last-Modified
  3. ETag在分布式系统中,可能不同的系统产生不同的ETag,从而导致ETag不统一

vary

这个是告知代理服务器在缓存文件的时候要区分vary字段里面的header,他会根据vary指定的字段不同而缓存不同的文件(比如一个是使用过gzip压缩过的文件,另外一个不是通过gzip压缩过的文件)

没有缓存策略的情况

如果没有缓存策略的情况下,浏览器会采用采用一个启发式算法:去响应头中的Date减去Last-Modifiled的值的10%作为缓存时间

实际场景

  1. 通常HTML不缓存或者缓存时间很短,所以可以通过打包工具,对文件名进行处理,当文件改变的时候就会更改文件名,从而使其发起新的请求。设置其他缓存为一个很长的时间。
  2. 对于频繁变动的文件,可以设置Cache-Control: no-cache使浏览器每一次都验证资源的有效性,这样做虽然不能减少请求数量,但是可以减少数据的传输。

Reference

HTTP 缓存

HTTP Cache(google-developer)

分享到: