HTTP缓存

前言

一直以来都对浏览器缓存很感兴趣,但老是只知其一;不知其二,只是很碎片化的了解一些相关知识,此次就打算把全部的碎片整合一下,基于我目前的理解整理一份总结。等之后认识的更全面了,再逐步添加修改。node

浏览器缓存的做用

合理使用浏览器的缓存,能够有效的减小请求的访问次数,一个是减少服务器压力,更重要的是可以下降页面的加载时间。算法

HTTP缓存的几种形式及实现

Pragma

这是一种比较古老的请求头,只建议在向下兼容http1.0的时候使用,当值为Pragma:no-cache,效果和Cache-Control:no-cache同样。浏览器

res.setHeader('Pragma', 'no-cache');
复制代码

以nodejs为例,文件每次都会向服务器从新发起请求,哪怕设置了其余缓存方式,具体优先级待会会详细介绍。缓存

Expires

Exprires也是兼容http1.0的一项请求头。bash

经过服务端设置,给Response Headers添加一个GMT(格林尼治时间)做为Express的响应头,客户端在发请求前,判断Express的时间是否已通过期来决定是否向服务端发送请求。服务器

// 过时时间在当前时间下添加5秒
const expires = new Date(Date.parse(new Date())+5000);
res.setHeader("Expires", expires.toUTCString());
复制代码

其中Date为上一次请求的时间异步

因为客户端在作判断的时候取的是本身电脑的时间,因此当用户的电脑时间出现问题时,会影响到缓存的正常使用。性能

Cache-Control

当http发展到1.1时,简单的请求头已经没法覆盖全部的操做,因此须要更加灵活的Cache-Control来充实浏览器缓存的形式。spa

客户端能够在HTTP请求中使用的标准Cache-Control代理

//告诉服务端,愿意接收一个请求时间为seconds秒的资源
Cache-Control: max-age=<seconds>
//告诉服务端,愿意接收一个超出缓存时间seconds秒的资源,若是未定义,则容许超出任意时间
Cache-Control: max-stale[=<seconds>]
//告诉服务端,但愿接收一个seconds秒内被更新过的资源
Cache-Control: min-fresh=<seconds>
//告诉服务端,不直接使用缓存,跳过强缓存的步骤
Cache-control: no-cache
//告诉服务端,全部内容都不被缓存到浏览器中
Cache-control: no-store
//告诉服务端,但愿获取实体数据没有被转换过(如压缩)的资源
Cache-control: no-transform
//告诉服务端,但愿获取缓存的内容(如有)
Cache-control: only-if-cached
复制代码

服务器能够在响应中使用的标准Cache-Control

//缓存必须在使用以前验证旧资源的状态,而且不可以使用过时资源
Cache-control: must-revalidate
//不直接使用缓存,跳过强缓存的步骤
Cache-control: no-cache
//全部内容都不被缓存到浏览器中
Cache-control: no-store
//不得对资源进行转换或转变。Content-Encoding, Content-Range, Content-Type等HTTP头不能由代理修改。例如,非透明代理能够对图像格式进行转换,以便节省缓存空间或者减小缓慢链路上的流量
Cache-control: no-transform
//代表响应能够被任何对象(包括:发送请求的客户端,代理服务器,等等)缓存
Cache-control: public
//代表响应只能被单个用户缓存,不能做为共享缓存(即代理服务器不能缓存它),能够缓存响应内容
Cache-control: private
//与must-revalidate做用相同,但它仅适用于共享缓存(例如代理),并被私有缓存忽略
Cache-control: proxy-revalidate
//设置缓存存储的最大周期,超过这个时间缓存被认为过时(单位秒)。与Expires相反,时间是相对于请求的时间
Cache-Control: max-age=<seconds>
//覆盖max-age 或者 Expires 头,可是仅适用于共享缓存(好比各个代理),而且私有缓存中它被忽略
Cache-control: s-maxage=<seconds>
复制代码

扩展Cache-Control指令(不是核心HTTP缓存标准文档的一部分,有兼容问题)

//表示响应正文不会随时间而改变。资源(若是未过时)在服务器上不发生改变,所以客户端不该发送从新验证请求头(例如If-None-Match或If-Modified-Since)来检查更新,即便用户显式地刷新页面
Cache-control: immutable 
//代表客户端愿意接受陈旧的响应,同时在后台异步检查新的响应。秒值指示客户愿意接受陈旧响应的时间长度
Cache-control: stale-while-revalidate=<seconds>
//表示若是新的检查失败,则客户愿意接受陈旧的响应。秒数值表示客户在初始到期后愿意接受陈旧响应的时间
Cache-control: stale-if-error=<seconds>
复制代码

下面是nodejs经过给Cache-Control添加了过时时间一个小时,每次加载文件时,客户端会对Date进行比对,知道过时,不然不会向服务端发送新的请求

res.setHeader('Cache-Control', 'max-age=3600');
复制代码

Cache-Control的比对比较复杂,是须要响应头和请求头同时做用的

(这里关于响应头和请求头之间互相冲突时,客户端是如何决定缓存结果的,我在后面再找时间添加)

Last-Modified

第一次请求资源时,会将资源最后更改的时间以Last-Modified: GMT的形式加在实体首部上一块儿返回给客户端,当再一次发起请求时,客户端会带上返回的GMT,赋值给If-Modified-Since,若服务端判断时间没有更改,则返回304 NOT MODIFIED

const stats = fs.statSync(pathname);
const lastModified = stats.mtime.toUTCString();
const ifModifiedSince = "If-Modified-Since".toLowerCase();
res.setHeader("Last-Modified", lastModified);
//校验时判断下当前文件的更新时间和请求返回的时间是否相同
if (
  req.headers[ifModifiedSince] &&
  lastModified == req.headers[ifModifiedSince]
) {
  //返回
  res.writeHead(304, "Not Modified");
}
复制代码

服务端比对服务器中的文件和请求头中的更新时间,发现时间一致,返回304,客户端再也不从新更新文件。

PS:通常这时候不会立刻出现304的状况,会发现浏览器根本没有发送请求,而是直接拿了本地的缓存。

(此处内容是腾讯Bugly分享截取)这是由于浏览器的一种缓存过时策略。在没有提供任何浏览器缓存过时策略的状况下,浏览器遵循一个启发式缓存过时策略:

根据响应头中2个时间字段 Date 和 Last-Modified 之间的时间差值,取其值的10%做为缓存时间周期。

如下完整的缓存策略三要素:

这里须要注意的点,根据图中的公式能够看出,若是上次缓存的文件时间较长,加上仅仅配置了Last-Modified,有可能致使用户一直没法拿到最新的文件。

ETag

服务端在第一次请求资源时,会在响应头ETag上添加一个由某种算符得出的随机字符串,当客户端再一次请求该项资源时,会将以前缓存的字符串,自动带上给If-None-Match,服务端将获得的字符和服务器中的进行对比。若是不一样,则从新抓取文件,若相同,则返回304

//文件更新时从新设置etag(模拟服务器获取etag)
const etag = '123456789abcdef';
const ifNoneMatch = "If-None-Match".toLowerCase();
res.setHeader("Etag", etag);
if (
  req.headers[ifNoneMatch] &&
  etag == req.headers[ifNoneMatch]
) {
  res.writeHead(304, "Not Modified");
}
复制代码

不一样缓存方式的优先级关系

我这里就不写详细例子,直接将他们的优先级展现出来。

其中Pragma>Cache-Control>Exprise,优先级从左到右。

强缓存

服务器返回200 from cache属于强缓存,客户端经过Pragma、Cache-Control和Exprise判断是否有缓存,若是有缓存,直接访问本地的缓存,不访问服务器,能更有效的节省资源。

磁盘缓存(from cache || from disk cache)

内存缓存(from memory cache)

协商缓存(弱缓存)

服务器返回304 Not Modified属于协商缓存,客户端经过向服务端发请求验证资源是否过时,若发现已通过期,则从新拉去,若没有过时,服务端返回304,客户端则从本地缓存中获取资源,不从新拉去。

分析不一样缓存方式的优劣性

头部字段 优点 劣势
Pragma 基于http1.0,兼容性强 可操做性不强,基本已经再也不使用
Expires 基于http1.0,兼容性比较强,客户端判断是否过时,无需向服务端确认 1.因为是基于用户本地时间进行计算,因此可能存在不许确的问题。2.强缓存在到期以前用户没法知道资源是否过时。
Cache-Control 操做比较丰富,组合性强 一样因为是强缓存,可能存在更新不及时的问题
Last-Modified 兼容性强,更新及时 资源的任何变化都会改变动新时间,可能会重复加载彻底同样的文件
ETag 文件的更新由服务端控制,操做性强,且兼容性强 计算ETag须要消耗性能,且不一样的服务端计算的算法可能不一致,致使重复加载
相关文章
相关标签/搜索