前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >HTTP之缓存控制

HTTP之缓存控制

原创
作者头像
Ashen
修改2020-06-01 14:42:07
5690
修改2020-06-01 14:42:07
举报
文章被收录于专栏:Ashenの前端技术Ashenの前端技术

下文以Chrome浏览器和nodeJs举例。

一、查看浏览器缓存

查看浏览器缓存需要下载一个工具ChromeCacheView

http://www.nirsoft.net/utils/chrome_cache_view.html

二、缓存控制

HTTP/1.1定义的Cache-Control用来区分对缓存机制的支持情况,请求头和响应头均可以使用该字段。该字段有很多指令和参数,参见: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control#Directives

2.1 禁止缓存

2.1.1 no-cache

no-cache指令可以来自服务器也可以来自客户端,使用no-cache指令的目的是为了防止从缓存中返回过期的资源。

客户端发送的请求中如果包含no-cache指令,则表示客户端将不会接收缓存过的响应,于是中间的缓存服务器需要将来自客户端的请求转发给源服务器。

服务器返回的响应中如果包含no-cache指令,那么缓存服务器不能对资源进行缓存。源服务器以后也将不再对缓存服务器请求中提出的资源有效性进行确认,且禁止其对响应资源进行缓存操作。

代码语言:javascript
复制
Cache-Control:no-cache

如上定义,每次有请求发出时,缓存会将此请求发到源服务器,源服务器端会验证请求中所描述的缓存是否过期,若未过期(实际就是返回304),则缓存才使用本地缓存副本。

2.1.2 no-store

该指令规定缓存不能在本地存储请求或响应的任一部分。

需要注意的是:no-cache不是不缓存,而是不缓存过期的资源,缓存会向源服务器进行有效性确认后再处理资源。no-store才是真正的不缓存资源。

代码语言:javascript
复制
Cache-Control:no-store

2.2 缓存过期机制

主要是max-age指令,该指令的参数是一个整数,单位为秒,表示资源能够被缓存(保持新鲜)的最大时间。相对Expires而言,max-age是距离请求发起的时间的秒数。

代码语言:javascript
复制
Cache-Control:max-age=3600

Null

如上定义,表示请求资源将被缓存3600秒。

2.3 缓存验证确认

代码语言:javascript
复制
Cache-Control:must-revalidate

如上定义,当使用了must-revalidate指令,那就意味着缓存在考虑使用一个陈旧的资源时,必须先验证它的状态,已过期的缓存将不被使用。

三、不使用缓存示例

代码语言:javascript
复制
const http = require("http");
const fs = require("fs");

http.createServer(function(req,res){
    if(req.url === "/"){
        const readStream = fs.createReadStream("./index.html");
        res.setHeader("Content-Type","text/html");
        readStream.pipe(res);
    }else if(req.url === "/loadString"){
        const readStream = fs.createReadStream("./text.txt");
        res.setHeader("Content-Type","text/plain");
        readStream.pipe(res);
    }else{
        res.statusCode = 404;
        res.end()
    }
}).listen(4000);

四、使用强缓存

4.1 基本示例

使用强缓存,最简单的办法就是响应头设置max-age:

代码语言:javascript
复制
res.setHeader("Cache-Control","max-age=20");

该资源将被缓存20秒。

4.2 问题

  1. 设置的强缓存时间失效后,再次请求同一个资源,客户端会重新发送请求给源服务器。但如果该资源并没有发生改变,缓存中又存在该资源,那么从缓存中直接获该资源不是更好吗?
  2. 设置的强缓存时间较长比如1年,期间资源发生了改变。在强缓存有效期内重新发送请求,此时客户端会从缓存获取资源,但是此时,我想要的是修改后的资源,如何解决?

解决这2个问题需要用到:协商缓存

五、协商缓存示例

协商缓存需要用到2个字段: * 响应头的Last-Modified字段 * 请求头的If-Modified-Since字段

其参数值为UTC时间字符串。

5.1 原理

  1. 第一次请求资源时,资源在响应头中设置last-modified字段,并随着响应体一起存到缓存中
  2. 下一次需要再发送请求时,请求体中会将上一次修改时间(last-modified)设置到if-modified-since字段中带到服务端
  3. 若在if-modified-since字段值之后对应的资源都没有更新过,则返回304 Not Modified状态码
  4. 否则读取资源并返回,同时更新last-modified

5.2 示例

代码语言:javascript
复制
const mtime = fs.statSync("./text.txt").mtime.toUTCString();
if(req.headers["if-modified-since"] === mtime){
    res.writeHead(304,"Not Modified",{
        "Cache-Control":"max-age=5",
        "Last-Modified":mtime
    });
    res.end();
}else{
    res.writeHead(200,"Ok",{
        "Cache-Control":"max-age=5",
        "Last-Modified":mtime
    });
    readStream.pipe(res);
}

fs模块的statSync方法用于同步获取一个文件的时间,mtime属性表示其修改时间。

六、一直使用协商缓存

上例是强缓存+协商缓存的方式。

如何一直使用协商缓存呢?只需要将:

代码语言:javascript
复制
"Cache-Control":"max-age=5"

修改为:

代码语言:javascript
复制
"Cache-Control":"no-cache",

注意:no-store不可以实现协商缓存。

七、优化协商缓存

协商缓存的Last-Modified可能会存在一些问题: * 某些服务端没有办法获取精确的修改时间,导致last-modified有问题 * 文件时间修改了,但文件内容却没有变

所以,通常还会用到另一个首部字段:ETag

7.1 ETag

该字段属于响应首部字段,能告知客户端资源实体标记,将资源以字符串形式做唯一标识,服务器为每份新资源分配对应的ETag值,ETag的生成并没有统一的算法规则。

7.2 If-None-Match

该字段属于请求首部字段,与ETag对应,二者的关系类似If-Not-Modified与Last-Modified。

通常的服务端处理流程: 1. 首先判断请求头的etag字段与对应请求资源本身的ETag是否相等 2. 如果相等,走第5步 3. 如果不相等,则继续判断请求头的If-Not-Modified与对应资源的Last-Modified是否相等 4. 如果相等,走第5步 5. 响应304,结束请求 6. 否则,读取实体资源返回

八、最后

  • HTTP/1.1 请求首部和响应首部字段对大小写不敏感,通常服务端设置响应头采用首字符大写的方式,但读取的请求头则全为小写。比如,服务端设置res.setHeader("Last-Modified":"Tue, 13 Aug 2019 12:02:47 GMT"),读取为:req.headers["if-not-modified"]
缓存控制流程图
缓存控制流程图

End~

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、查看浏览器缓存
  • 二、缓存控制
    • 2.1 禁止缓存
      • 2.1.1 no-cache
      • 2.1.2 no-store
    • 2.2 缓存过期机制
      • 2.3 缓存验证确认
      • 三、不使用缓存示例
      • 四、使用强缓存
        • 4.1 基本示例
          • 4.2 问题
          • 五、协商缓存示例
            • 5.1 原理
              • 5.2 示例
              • 六、一直使用协商缓存
              • 七、优化协商缓存
                • 7.1 ETag
                  • 7.2 If-None-Match
                  • 八、最后
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档