概述
为提高浏览器加载文件性能,http 提供了静态资源缓存机制,主要为强制缓存和协商缓存,通过请求头和响应头来标志。请求头的相关字段,如Last-Modified,If-None-Match是根据上一个请求的响应头生成的。响应头中相关字段为Expires、Cache-Control、Last-Modified、Etag。HTTP/1.0 和HTTP/1.1 又有所区分。这里先说一句,HTTP/1.1 是 HTTP/1.0 的升级版,配置优先级高,直接按照1.1的配置即可。
缓存过程
浏览器首次发送请求时,通过响应头的标识来确定缓存类型。再次请求时,先判断是否有强制缓存、协商缓存。具体过程,如图:
缓存分类
强制缓存
强制缓存状态码为200,size显示为 from memory cache,from disk cache。
使用强缓存时,http 实际上并未向后端发出请求,直接从本地缓存中读取文件。但是可能有人会问,本都读取是不是后台服务停止了也不受影响?答案肯定是受影响的,因为虽然未向后端发出请求,但是tcp的长连接还是不能断开的,否则就拒绝连接了。
关于强缓存的设置,HTTP/1.0使用的是 Expires,HTTP/1.1 使用的是Cache-Control。
Expires
请求头会带一个Expires字段,表示资源过期时间,下次请求时,只需将当前时间与Expires比对,即可获知缓存是否可用。Expires头的一般形式如下:
Expires: Tue, 18 Jul 2023 08:11:30 GMT
Expires 的缺点是:使用过期时间,客户端存在与服务端时间不一致的情况。
Cache-Control
Cache-Control 采用 时间段来避免 Expires 过期时间的问题,可以使用max-age 设置过期时长。配置如下:
Cache-Control:max-age=31536000
另外还提供了其他更多的配置项来实现缓存配置:
- public:表示客户端和代理服务器都可以缓存。因为通常一个请求可能给要经过不同的代理服务器最后才到达目标服务器,那么结果就是不仅仅浏览器可以缓存数据,中间的任何代理节点都可以进行缓存
- private:这种情况下就是只有浏览器才能缓存,中间的代理服务器不能缓存
- no-cache:跳过当前的强缓存,发送http请求,直接进入协商缓存阶段
- no-store:不进行任何形式的缓存
- s-maxage:这和max-age长得比较像,但是区别在于s-maxage是针对代理服务器的缓存时间
- must-revalidate:加上这个字段,一旦缓存过期。就必须回到源服务器验证(因为 HTTP 规范是允许客户端在某些特殊情况下直接使用过期缓存的,比如校验请求发送失败的时候,还比如有配置一些特殊指令(stale-while-revalidate、stale-if-error等)的时候)
Cache-Control 比 Expires 拥有更高的优先级。
协商缓存
协商缓存状态码为304,同样有两个配置可以设置。
Last-Modified
HTTP/1.0 提供的 协商缓存配置。客户端首次发送请求的时候,服务端会在响应头里带上 Last-Modified,该字段表示资源最后修改时间。当客户端再次请求时,会在请求头带上 If-Modified-Since 字段,这个字段也就是上次请求返回回来的 Last-Modified 的值
服务端在拿到 If-Modified-Since 字段后,与服务器中资源最后修改时间进行比对:
- 如果 If-Modified-Since 值小于服务器资源最后修改时间,证明资源已经更新。此时将最新的资源与最新的修改时间返回
- 否则,返回304,告诉客户端缓存可用
ETag
上面提到的 Last-Modified有个缺点,如果文件在极短的时间内(比如1s)发生变化,Last-Modified 无法快速响应,这时候就要用到ETag。ETag 一般为文件的hash 值,根据文件内容生成,以此作为文件是否修改的依据。
客户端接收到ETag的值,会在下次请求时,将这个值作为If-None-Match这个字段的内容,并放到请求头中,然后发给服务器。
服务器接收到If-None-Match后,会跟服务器上该资源的ETag进行比对:
- 如果两者不一样,说明要更新了。服务器返回新的资源,跟常规的HTTP请求响应的流程一样
- 否则,返回304,告诉客户端缓存可用
ETag 相比 Last-Modified 对文件的变化更精确,但是多增加了一个文件的hash 过程,个人觉得还是值得的。
强制缓存位置
强制缓存可以在network 的size 栏中清晰的看到缓存的位置,一般是from memory cache 或from disk cache。
内存缓存(from memory cache):读取速度块,但是时效性低,关闭页面,缓存会消失,一般是未关闭页面时,会一直保存再内存中。
硬盘缓存(from disk cache):硬盘缓存则是直接将缓存写入硬盘文件中,读取缓存需要对该缓存存放的硬盘文件进行I/O操作,然后重新解析该缓存内容,读取复杂,速度比内存缓存慢。时效性高,未修改以前会一直保存。关闭浏览器,再重新打开,会从硬盘缓存中读取。
协商缓存位置
查了网上的资料并没有找到说明协商缓存保存位置。
但是一般缓存的位置要么在磁盘,要么在内存中,为了验证猜想,分别对同一个文件,进行强缓存和协商缓存,再比较waterfall。
根据强缓存中不同缓存下的download时间发现,协商缓存与强缓存from disk时,时间应该差不多,推测应该是保存在磁盘中。
强缓存,from memory时,waterfall 图
强缓存,from disk 时,waterfall 图
协商缓存waterfall图:
示例
nginx 配置示例
koa 配置示例:
强制缓存:
协商缓存: