用途
HTTP 缓存主要用在对一些实时性要求不高的静态文件进行的缓存, 往往都是存在浏览器端, 防止这些 "多余" 的请求重复的访问服务器, 对服务器造成压力, 从而提高网站的性能.
原理
现有两端, 浏览器 C 和服务器端 S.
浏览器向服务器发送请求, 获取一个文件 f
服务器就把 f 给返回浏览器
如何解决呢?
浏览器把请求后拿到的文件存到本地, 等下次请求的时候, 看看本地是否有缓存文件, 如果有, 直接拿本地的文件, 岂不是就不用请求服务器了? 这其实就是 http 缓存的最最根本的原理.
C 端浏览器端把请求来的文件缓存到如图下 f 的小方格内
等到下次 C 端再次请求此文件时, 就直接从浏览器缓存的文件中拿, 而不再向 S 服务器端发起请求了
以下浏览器截图中标红的部分, 就是没有发起请求, 直接从浏览器缓存中获取的数据
两种缓存方式
浏览器端有了缓存之后, 不能一直有效吧, 如果文件更新了, 我们还继续使用浏览器缓存中的数据, 虽说时效性不强, 但长期使用旧文件也不算合理吧.
http 协议提供了两种维度来让缓存失效: 时间和文件的修改.
利用时间来让缓存失效
时间维度很简单, 就是设定一个缓存时间段, 过了这个时间段, 缓存就自动失效了, 浏览器就会发起请求获取文件. 这个设定时间的 http 字段就是 cache-control 字段.
cache-control 可设置的字段值有:
private : 客户端可以缓存
public : 客户端和代理服务器都可缓存, 大部分情况可以认为 public 和 private 是一样的
max-age=xxx : 缓存的内容将在 xxx 秒后失效 (时间就是在这儿设置的)
no-cache : 需要使用另外一种 http 缓存策略来验证缓存数据
no-store : 所有缓存策略都不会进行(这里指的是两种缓存策略都不会进行)
cache-control 缓存原理
第一次访问请求, 客户端 C 向服务端 S 发起一个文件请求, 服务器返回文件并在 response 中加了响应头 "Cache-Control:max-age=60", 这样一来, 这个 f 文件只能在浏览器端存
60 秒
在这 60 秒钟, 客户端请求服务器的 f 文件会直接从缓存中拿取
60 秒过后, 缓存失效, 浏览器再次请求文件需要重新向服务器发起请求.
注意: 假如说请求中包含 "Cache-Control:max-age=0" 或者 "Cache-Control:no-store" 无论响应中返回的 "max-age" 值是多少, 都不会缓存到服务器. 浏览器中对于地址栏中直接输入文件地址的请求做了优化处理, 加上了 "Cache-Control:max-age=0", 也就是说, 如果这个 CSS,js 或者其他静态文件是通过你在浏览器上直接输入获得的, 将会每时每刻都是获取最新的.
通过查看文件的修改来让缓存失效
这种维度比较的科学: 浏览器先请求服务获得文件后, 服务器会返回该文件的最后修改时间 Last-Modified, 作为文件的一个标识, 下次浏览器请求的时候, 会带着这个标识去请求(此时为 If-Modified-Since), 然后服务器做校验, 如果说时间标识 If-Modified-Since 等于服务器的文件修改时间, 则说明没有修改, 返回 304 状态码, 浏览器从缓存中获取文件, 但是如果浏览器保存的时间标识 If-Modified-Since 小于服务器端的文件修改时间, 那么, 说明文件发生了修改, 浏览器就会重新获取新的文件.
(If-Modified-Since 的时间如果大于服务器端文件的时间, 会被认为是错误的请求)
如图, 浏览器 C 向服务器发 S 起请求, 服务器 S 返回文件的同时还会返回文件的最后修改时间 Last-Modified 作为文件时间标识, 浏览器会将文件和文件时间标识都缓存起来.
假如服务器端的文件 f 并没有被修改, 服务器通过判断请求头带的时间标识 If-Modified-Since 得出结论后, 都会返回状态码
304
告诉浏览器文件没有被修改, 让浏览器使用缓存.
假如服务器端的文件 f 修改了, 那么, 浏览器将重新获取文件, 并缓存到浏览器中.
虽然通过文件最后修改时间作为标识已经很完美了, 但是, 还是可能存在一个问题: 就是有可能服务器端的文件修改后, 又改回原来的样子, 这样, 虽然文件最后修改时间变了, 但是, 文件内容并没有改变. 这样还是会有多余的请求到达服务器, 该如何处理呢?
可以将文件内容作为一个唯一标识, 例如可以对文件内容取 MD5 值作为字段 (etag) 也传给浏览器端, 假如这个文件内容没变化, 那么 MD5 值也不会改变. 那么, 处理流程就变成了这样: 服务器端先判断文件修改时间是否发生了变化, 如果发生了变化, 那么再对比浏览器传来的 If-None-Match 即浏览器端保留的 E-tag 值, 如果发生了变化, 则证明文件修改了, 需要浏览器重新下载文件, 如果没有, 则证明文件内容没变化, 返回 304 状态码.
如图, 浏览器 C 要访问服务器 S 的 f 文件, 服务器 S 返回了文件最后修改时间 Last-Modified 和文件的内容标识 E-tag, 浏览器将这两个字段及其文件缓存了起来
当文件最后修改时间没变, 文件内容也没变的时候, 返回 304, 让浏览器从缓存中拿取文件.
当文件最后修改时间变了, 文件内容没变的时候, 返回 304, 让浏览器从缓存中拿取文件.
当文件修改时间变了, 文件内容也变了的时候, 服务器会重新下发新的文件给浏览器.
此维度让缓存失效牵扯的 http 字段有点多, 我们最后整理一下:
文件最后修改时间字段:
- Response:Last-Modified
- Request:If-Modified-Since
文件内容标识字段:
- Response:E-tag
- Request:If-None-Match
来源: https://juejin.im/entry/5ae4bc606fb9a07a9e4cfc78