网络编程 —— HTTP缓存|8月更文挑战

160 阅读7分钟

这是我参与8月更文挑战的第16天,活动详情查看:8月更文挑战

HTTP 缓存的好处?

  • 减少亢余的数据传输,节约资源
  • 缓解服务器压力,提高网站性能
  • 加快客户加载网页的速度

不想使用缓存的几种方式

  • Ctrl + F5强制刷新,都会直接向服务器提取数据
  • 按F5刷新或浏览器的刷新按钮,默认加上Cache-Control:max-age=0,即会走协商缓存

浏览器的缓存分类

  • 强缓存 200 (from memory cache)和200 (from disk cache)
  • 协商缓存 304 (Not Modified)

刷新操作的缓存策略

  • 正常操作:强制缓存有效,协商缓存有效
  • 手动刷新:强制缓存失效,协商缓存有效
  • 强制刷新:强制缓存失效,协商缓存失效

1 缓存流程

强制缓存优先于协商缓存进行,若强制缓存(Expires和Cache-Control)生效则直接使用缓存,若不生效则进行协商缓存(Last-Modified / If-Modified-Since和Etag / If-None-Match),协商缓存由服务器决定是否使用缓存,若协商缓存失效,那么代表该请求的缓存失效,重新获取请求结果,再存入浏览器缓存中;生效则返回304,继续使用缓存,主要过程如下:

HTTP缓存.jpg

2 强制缓存

如果启用了强缓存,请求资源时不会向服务器发送请求,直接从缓存中读取资源,在chrome控制台的network中看到请求返回的200状态码,并在状态码的后面跟着from disk cache 或者from memory cache关键字。两者的差别在于获取缓存的位置不一样。

  • Pragma:在 http1.1 中被遗弃

  • Cache-Control:http1.1 时出现的header信息。设置过期时间(绝对时间、时间点),超过了这个时间点就代表资源过期。但是用户的本地时间是可以自行调整的,所以会出现问题。

    • max-age=x(单位秒) :缓存内容将在x秒后失效,如 Cache-Control:max-age=36000
    • no-cache:客户端缓存内容,但是是否使用缓存则需要经过协商缓存来验证决定
    • no-store:所有内容都不会被缓存,即不使用强制缓存,也不使用协商缓存
    • private:所有内容只有客户端可以缓存,Cache-Control的默认取值
    • public:所有内容都将被缓存(客户端和代理服务器都可缓存)
  • Expires:是http1.0的规范,它的值是一个绝对时间的GMT格式的时间字符串。设置过期时长(相对时间、时间段),指定一个时间长度,跟本地时间无关,在这个时间段内缓存是有效的。

    • 同在Response Headers中
    • 同为控制缓存过期
    • 已被Cache-Control代替

注意:生效优先级为(从高到低):Pragma > Cache-Control > Expires

2.1 Pragma

在 http1.1 中被遗弃。

2.2 Cache-Control

HTTP缓存-Cache-Control.png

第一步:浏览器首次发起请求,缓存为空,服务器响应:

HTTP缓存-Cache-Control-第一步.png

浏览器缓存此响应,缓存寿命为接收到此响应开始计时 100s 。

第二步:10s 过后,浏览器再次发起请求,检测缓存未过期,浏览器计算 Age: 10 ,然后直接使用缓存,这里是直接去内存中的缓存,from disk 是取磁盘上的缓存:

HTTP缓存-Cache-Control-第二步.png

第三步:100s 过后,浏览器再次发起请求,检测缓存过期,向服务器发起验证缓存请求。如果服务器对比文件已发生改变,则如 1;否则不返回文件数据报文,直接返回 304:

HTTP缓存-Cache-Control-第三步.png

2.3 Expires

Expires是HTTP/1.0控制网页缓存的字段,其值为服务器返回该请求的结果缓存的到期时间,即再次发送请求时,如果客户端的时间小于Expires的值时,直接使用缓存结果。

Expires是HTTP/1.0的字段,但是现在浏览器的默认使用的是HTTP/1.1,那么在HTTP/1.1中网页缓存还是否由Expires控制?到了HTTP/1.1,Expires已经被Cache-Control替代,原因在于Expires控制缓存的原理是使用客户端的时间服务端返回的时间做对比,如果客户端与服务端的时间由于某些原因(时区不同;客户端和服务端有一方的时间不准确)发生误差,那么强制缓存直接失效,那么强制缓存存在的意义就毫无意义。

3 协商缓存

协商缓存(对比缓存)就是由服务器来确定缓存资源是否可用,所以客户端与服务器端要通过某种标识来进行通信,从而让服务器判断请求资源是否可以缓存访问,这主要涉及到下面两组header字段。这两组搭档都是成对出现的,即第一次请求的响应头带上某个字段(Last-Modified或者Etag),则后续请求则会带上对应的请求字段(If-Modified-Since或者If-None-Match),若响应头没有Last-Modified或者Etag字段,则请求头也不会有对应的字段。

注意:Last-Modified与ETag是可以一起使用的,服务器会优先验证ETag,一致的情况下,才会继续比对Last-Modified,最后才决定是否返回304。

3.1 ETag/If-None-Match

ETag与If-None-Match.jpg

Etag

Etag是服务器响应请求时,返回当前资源文件的一个唯一标识(由服务器生成),如下:

Etag.png

If-None-Match

If-None-Match是客户端再次发起该请求时,携带上次请求返回的唯一标识Etag值,通过此字段值告诉服务器该资源上次请求返回的唯一标识值。服务器收到该请求后,发现该请求头中含有If-None-Match,则会根据If-None-Match的字段值与该资源在服务器的Etag值做对比,一致则返回304,代表资源无更新,继续使用缓存文件;不一致则重新返回资源文件,状态码为200,如下:

If-None-Match.png

注意:Etag/If-None-Match优先级高于Last-Modified/If-Modified-Since,同时存在则只有Etag/If-None-Match生效。

3.2 Last-Modified/If-Modified-Since

Last-Modified与If-Modified-Since.jpg

Last-Modified

Last-Modified是服务器响应请求时,返回该资源文件在服务器最后被修改的时间,如下:

Last-Modified.png

If-Modified-Since

If-Modified-Since则是客户端再次发起该请求时,携带上次请求返回的Last-Modified值,通过此字段值告诉服务器该资源上次请求返回的最后被修改时间。服务器收到该请求,发现请求头含有If-Modified-Since字段,则会根据If-Modified-Since的字段值与该资源在服务器的最后被修改时间做对比,若服务器的资源最后被修改时间大于If-Modified-Since的字段值,则重新返回资源,状态码为200;否则则返回304,代表资源无更新,可继续使用缓存文件,如下:

If-Modified-Since.png

4 常见问题

问题一:为什么要有Etag?

你可能会觉得使用Last-Modified已经足以让浏览器知道本地的缓存副本是否足够新,为什么还需要Etag呢?HTTP1.1中Etag的出现主要是为了解决几个Last-Modified比较难解决的问题:

一些文件也许会周期性的更改,但是他的内容并不改变(仅仅改变的修改时间),这个时候我们并不希望客户端认为这个文件被修改了,而重新GET; 某些文件修改非常频繁,比如在秒以下的时间内进行修改,(比方说1s内修改了N次),If-Modified-Since能检查到的粒度是s级的,这种修改无法判断(或者说UNIX记录MTIME只能精确到秒); 某些服务器不能精确的得到文件的最后修改时间。

问题二:如果什么缓存策略都没设置,那么浏览器会怎么处理?

如果什么缓存策略都没设置,没有Cache-Control也没有Expires,对于这种情况,浏览器会采用一个启发式的算法(LM-Factor),通常会取响应头中的 Date 减去 Last-Modified 值的 10% (不同的浏览器可能不一样)作为缓存时间。