http 缓存(强制缓存、协商缓存原理示例)
强制缓存
强制缓存直接减少请求数,是提升最大的缓存策略。
强制缓存就是向浏览器缓存查找该请求结果,并根据该结果的缓存规则来决定是否使用该缓存结果的过程,强制缓存的情况有三种:
1、不存在该缓存结果和缓存标识,强制缓存失效,则直接向服务器发起请求(跟第一次发起请求一致),如下图:
2、存在该缓存结果和缓存标识,但该结果已失效,强制缓存失效,则使用协商缓存,如下图:
3、存在该缓存结果和缓存标识,且该结果尚未失效,强制缓存生效,直接返回该结果,如下图:
强缓存规则
当浏览器向服务器发起请求时,服务器会将缓存规则放入HTTP响应报文的HTTP头中和请求结果一起返回给浏览器,告诉浏览器当前资源的缓存规则,控制强缓存的响应头字段分别是Cache-Control
和Expires
,其中Cache-Control优先级比Expires高。如果当前资源被缓存,则浏览器在下次发起请求时会从缓存中寻找资源。
- Expires:
- HTTP1.0
- 由于是绝对时间,用户可能会将客户端本地的时间进行修改,而导致浏览器判断缓存失效,重新请求该资源。此外,即使不考虑自信修改,时差或者误差等因素也可能造成客户端与服务端的时间不一致,致使缓存失效。
- 写法太复杂了。表示时间的字符串多个空格,少个字母,都会导致非法属性从而设置失效
- Cache-control
- HTTP1.1
- 优先级高
max-age
:即最大有效时间,单位秒must-revalidate
:如果超过了max-age
的时间,浏览器必须向服务器发送请求,验证资源是否还有效。no-cache
:虽然字面意思是“不要缓存”,但实际上还是要求客户端缓存内容的,在发布缓存副本之前,强制要求缓存把请求提交给原始服务器进行验证 (协商缓存验证)。no-store
: 真正意义上的“不要缓存”。所有内容都不缓存。public
:所有的内容都可以被缓存,包括客户端(浏览器)和代理服务器(CDN)private
:所有的内容只有客户端才可以缓存,代理服务器不能缓存。默认值。
协商缓存
控制强制缓存的响应头字段分别是 Etag
和 Last-Modified
,请求头字段是 If-None-Match
和 If-Modified-Since
。
每次请求需要进行缓存新鲜度校验。新鲜度校验通过请求头 If-None-Match
与响应头 ETag
(由服务端生成) 进行对比,或请求头 If-Modified-Since
与响应头 Last-Modified
进行对比。
协商缓存就是强制缓存失效后(强制缓存时请求的缓存结果失效,只返回缓存标识),浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程,主要有以下两种情况:
协商缓存请求数上和没有缓存是一致的,但如果是 304 的话,返回的仅仅是一个状态码而已,并没有实际的文件内容,因此 在响应体体积上的节省是它的优化点。
1、协商缓存生效,返回304,如下:
2、协商缓存失效,返回200和请求结果结果,如下:
Etag/If-None-Match
Etag
Etag是属于HTTP 1.1属性,它是由服务器(Apache或者其他工具)生成保存并返回给前端并,用来帮助服务器控制Web端的缓存验证。
Etag 的优先级高于 Last-Modified
If-None-Match
如果If-None-Match
传递给后台的缓存ETag
值和后台对应该文件的ETag
值一样,说明该缓存新鲜,服务器返回 304 状态码,浏览器使用本地缓存;
如果If-None-Match
传递给后台的缓存ETag
值和后台对应该文件的ETag
值不一样,说明该缓存不新鲜,服务器返回更新的资源和新的 Etag
值
Last-Modifed/If-Modified-Since的时间精度是秒,而Etag可以更精确。
Etag优先级是高于Last-Modifed的,所以服务器会优先验证Etag
Last-Modifed/If-Modified-Since
Last-Modified
- 服务器通过
Last-Modified
字段告知客户端,资源最后一次被修改的时间- 浏览器将这个值和内容一起记录在缓存数据库中。
- 下一次请求相同资源时时,浏览器从自己的缓存中找出“不确定是否过期的”缓存。因此在请求头中将上次的
Last-Modified
的值写入到请求头的If-Modified-Since
字段- 服务器会将
If-Modified-Since
的值与Last-Modified
字段进行对比。如果相等,则表示未修改,响应 304;反之,则表示修改了,响应 200 状态码,并返回数据。- 如果资源更新的速度是秒以下单位,那么该缓存是不能被使用的,因为它的时间单位最低是秒。
- 如果文件是通过服务器动态生成的,那么该方法的更新时间永远是生成的时间,尽管文件可能没有变化,所以起不到缓存的作用。
If-Modified-Since
如果If-Modified-Since
值 就是 服务器端该文件的最新修改日期,说明缓存是新鲜的,服务器返回304,浏览器使用本地缓存;
如果If-Modified-Since
值 小于 服务器端该文件的最新修改日期,说明缓存不新鲜,服务器返回新的该资源,并且更新该资源Last-Modified
日期
使用场景
一般来说,我们对经构建工具打包后带有hash的资源进行一年的强制缓存 Cache-Control: max-age=3153600
,
对于文件名不带hash的资源进行协商缓存或Cache-Control: max-age=较小值
或 Cache-Control: no-cache
。
一种缓存策略是 index.html 适合走协商缓存,文件名带hash | 相对稳定 | 不常更新 的静态资源(JS、CSS、IMAGES) 等使用强缓存。
nginx 配置缓存策略
server {
location ~* \.(html)$ {
add_header Cache-Control max-age=no-cache;
}
location ~* \.(css|js|png|jpg|jpeg|gif|gz|svg|mp4|ogg|ogv|webm|htc|xml|woff)$ {
# 同上,通配所有以 .css/.js/...结尾的请求
add_header Cache-Control max-age=31536000;
}
# 或者对指定目录设置
location /static {
add_header Cache-Control "public, max-age=31536000";
}
}
刷新页面跳过缓存
- 当
ctrl+f5
刷新网页时(清空缓存并强制刷新),直接从服务器加载,跳过强缓存和协商缓存; - 当
f5
刷新网页时(强制刷新),跳过强缓存,但是会检查协商缓存;
强制刷新的实现原理:设置 Cache-Control 为 no-cache
清空缓存并强制刷新的功能:清掉本地的缓存再去协商,能保证一定是拿到最新的资源
关于缓存的常见误区
上面提到的知识估计就是平时大家最常背到的,不过大家有没有认真想过一个问题?我们取到的缓存数据,一定缓存在浏览器里面吗?
实际上是不然的:资源的缓存通常是有多级的,一些缓存专门用于单个用户,一些缓存专用于多个用户。有些是由服务器控制的,有些是由用户控制的,有些则由中介层控制。
- 浏览器缓存:一般并专用于单个用户,在浏览器客户端中实现。它们通过避免多次获取相同的响应来提高性能。
- 本地代理:可能是用户自己安装的,也可能是由某个中介层管理的:比如公司的网络层或者网络提供商。本地代理通常会为多个用户缓存单个响应,这就构成了一种“公共”缓存。
- 源服务器缓存/CDN。由服务器控制,源服务器缓存的目标是通过为多个用户缓存相同的响应来减少源服务器的负载。CDN 的目标是相似的,但它分布在全球各个地区,然后通过分配给最近的一组用户来达到减少延迟的目的。
另外,我们也经常会使用本地配置的代理,这些代理能够通过配置信任证书来缓存 HTTPS
资源。