原创

HTTP之缓存控制

下文以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指令,那么缓存服务器不能对资源进行缓存。源服务器以后也将不再对缓存服务器请求中提出的资源有效性进行确认,且禁止其对响应资源进行缓存操作。

Cache-Control:no-cache

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

2.1.2 no-store

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

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

Cache-Control:no-store

2.2 缓存过期机制

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

Cache-Control:max-age=3600

Null

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

2.3 缓存验证确认

Cache-Control:must-revalidate

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

三、不使用缓存示例

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:

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 示例

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属性表示其修改时间。

六、一直使用协商缓存

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

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

"Cache-Control":"max-age=5"

修改为:

"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~

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

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

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Node理论笔记:异步I/O

    ”异步“对于前端已经非常熟悉了,ajax、事件都是异步的。但在绝大多数高级编程语言中,异步并不多见,主要原因是:程序员不太适合通过异步来进行程序设计。

    Ashen
  • Node理论笔记:模块实现

    这个笔记是基于《深入浅出nodeJs》的,这本书出版较早是基于v0.6.0版本的,而现在node已经更新到v10的版本了,所以很多东西可能在新的版本都已经不适用...

    Ashen
  • windows宿主机如何SSH连接VMware的Linux虚拟机

    那么我的虚拟机IP地址就是:192.168.240.129(如果提示ifconfig命令不存在,按提示安装即可)

    Ashen
  • 一文搞懂浏览器缓存策略

    编者按:本文作者高峰 http://verymuch.site/,奇舞团前端工程师,W3C性能工作组成员,同时在WOT工作组学习。

    coder_koala
  • 反向代理的攻击面 (下)

    让我们接着上节的内容,继续探讨。建议读者先阅读第一部分,这将有助于理解本节的内容。

    随心助手
  • [日常] HTTP的缓存

    陶士涵
  • Java缓存深入理解

    对于缓存大家都不会陌生,但如何正确和合理的使用缓存还是需要一定的思考,本文将基于Java技术栈对缓存做一个相对详细的介绍,内容分为基本概念、本地缓存、远程缓存和...

    用户1216676
  • WEB缓存探究

    由于项目越来越大,即使了使用代码压缩工具减少文件大小,js文件还是不可避免的越变越大。而对于用户来说每次重新下载都有可能会消耗大量时间,让我们的首屏展示有较长时...

    疯狂的技术宅
  • http缓存与离线缓存

    一、http协议实现缓存 1. 缓存头部 通用缓存、条件缓存、缓存控制三大类 头部名称 说明 请求/响应 通用缓存头部 控制客户端是否向服务器发送请求...

    sam dragon
  • Redis 缓存 + Spring 的集成示例 (不错的bolg)

    http://blog.csdn.net/defonds/article/details/48716161  

    bear_fish

扫码关注云+社区

领取腾讯云代金券