最近有接触到Http缓存机制的问题,自己进行一个总结。

浏览器加载一个页面的缓存流程如下:1. 浏览器先根据Http Header信息来判断是否命中强缓存。如果命中则直接加载本地缓存中的资源,并不会将请求发送到服务器。
2. 如果未命中强缓存,则浏览器会将资源加载请求发送到服务器。服务器来判断浏览器本地缓存是否失效。若可以使用,则服务器并不会返回资源信息,浏览器继续从缓存加载资源,虽然与强缓存加载的是“同一份缓存”,但是由于流程与性质不一样,我们把它叫做协商缓存
3. 如果未命中协商缓存,则服务器会将完整的资源返回给浏览器,浏览器加载新资源,并更新缓存。

强缓存:不会向服务器发送请求,直接从本地缓存中读取资源返回200的状态码。

from memory cache一般脚本、字体、图片会存在内存当中

from disk cache一般非脚本会存在磁盘当中,如css等

协商缓存:向服务器发送请求,服务器根据请求中的Header的字段判断是否命中协商缓存,如果命中,则返回304状态码并带上新的响应头通知浏览器从缓存中读取资源

与之相关的字段为:
强缓存:cache-control、expires
协商缓存:Last-Modified/if-Modified-Since、Etag/if-None-Match.

其实整个缓存机制也就是围绕着这几个字段所展开

二、强缓存流程

强缓存是由Http的Response Header中的Expires或者Cache-Control两个字段来控制的,用来表示资源的缓存时间。如果Cache-control与expires同时存在的话,Cache-control的优先级高于expires。

Expires

是一个http1.0提出的概念,它描述的是一个绝对时间,由服务端返回

expires: Mon, 11 Jun 2029 08:34:12 GMT

Cache-Control

Catche-control是http1.1提出的概念,优先级高于expires,描述的是一个相对时间

cache-control: max-age=315360000

除了max-age外,cache-control还有其他几个参数:
-no-cache:不使用本地缓存。需要使用缓存协商,先与服务器确认返回的响应是否被更改,如果之前的响应中存在ETag,那么请求的时候会与服务端验证,如果资源未被更改,则可以避免重新下载。
-no-store:直接禁止浏览器缓存数据,每次用户请求该资源,都会向服务器发送一个请求,每次都会下载完整的资源。
-public:可以被所有的用户缓存,包括终端用户和CDN等中间代理服务器。
-private:只能被终端用户的浏览器缓存,不允许CDN等中继缓存服务器对其缓存。

如过Cache-Control和Expires条件都不满足,也就是说:像cache-control的字段为-no-cache和-no-store以及max-age不满足条件或者当前时间大于Expires的时间的时候,那么强缓存是没有被命中的,接下来要继续进行协商缓存的流程。

三、协商缓存流程

协商缓存相对于强缓存流程就复杂一点了,主要通过:Last-Modified/If-Modified-SinceETag/If-None-Match来控制。Etag是服务器自动生成或者由开发者生成的对应资源在服务器端的唯一标识符,能够更加准确的控制缓存。Last-Modified与ETag是可以一起使用的,服务器会优先验证ETag,一致的情况下,才会继续比对Last-Modified,最后才决定是否返回304

Last-Modified/If-Modified-Since

Last-Modified 表示本地文件最后修改日期,浏览器会在Request Header加上If-Modified-Since(上次返回的Last-Modified的值),询问服务器在该日期后资源是否有更新,有更新的话就会将新的资源发送回来。

但是单纯使用Last-Modified 会有以下问题:

  1. Last-Modified标注的最后修改只能精确到秒级,如果某些文件在1秒钟以内,被修改多次的话,它将不能准确标注文件的修改时间

  2. 如果某些文件会被定期生成,当有时内容并没有任何变化,但Last-Modified却改变了,导致文件没法使用缓存

  3. 有可能存在服务器没有准确获取文件修改时间,或者与代理服务器时间不一致等情形

所以在HTTP/1.1的时候加入了ETag/If-None-Match来解决这些问题,因而ETag的优先级高于Last-Modified。

ETag/If-None-Match

ETag可以保证每一个资源是唯一的,资源变化都会导致ETag变化。ETag值的变更则说明资源状态已经被修改。服务器根据浏览器上发送的If-None-Match值来判断是否命中缓存。

If-None-Match的header会将上次返回的Etag发送给服务器,询问该资源的Etag是否有更新,有变动就会发送新的资源回来.

Etag是服务器自动生成或者由开发者生成的对应资源在服务器端的唯一标识符,能够更加准确的控制缓存。Last-Modified与ETag是可以一起使用的,服务器会优先验证ETag,一致的情况下,才会继续比对Last-Modified,最后才决定是否返回304。

四、总结

一、由于HTTP一直在发展,所以对于HTTP的缓存字段也变得越来越复杂,其实我们很清晰的可以知道Expires与Last-Modified/If-Modified-Since是Http/1.0时代的产物。 Cache-Control与ETag/If-None-Match是HTTP/1.1为解决HTTP/1.0新增出来的字段,这样对比去记忆理解起来,其实缓存机制也就变得很好理解了。

二、对于第一次请求(肯定是没有任何缓存的),那么直接向服务器请求资源并将下载好的资源进行缓存,为下一次请求做缓存准备。

三、对于第二次之后的请求,那么本地是有缓存的,那么先通过cache-control的规则判断(对于Http1.0还是Expires)来判断本地缓存是否过期,如果没过期,那么直接使用。如果过期了,就再判断Etag(具体流程可以参考:Etag与HTTP缓存机制),通过发送If-None-Match(也就是上次存入的Tag的值),服务器进行一个决策判断返回200还是304。之前有说到,Last-Modified与ETag是可以一起使用的,服务器会优先验证ETag,也就是Etag不存在或者其他情况那么会使用Last-Modified来进行判断,通过向服务器发送If-Modified-Since,然后服务器进行一次决策。

看流程图可能一下子就明白了:

图片来自https://images2018.cnblogs.com/blog/940884/201804/940884-20180423141951735-912699213.png

参考:

https://www.cnblogs.com/ranyonsue/p/8918908.html
https://www.jianshu.com/p/19c2e397e22a