HTTP缓存了解一下

缓存

缓存作用

  • 减少了冗余的数据传输,节省了网费。
  • 减少了服务器的负担, 大大提高了网站的性能
  • 加快了客户端加载网页的速度

缓存分类

强制缓存

强制缓存:说白了就是第一次请求数据时,服务端将数据和缓存规则一并返回,下一次请求时浏览器直接根据缓存规则进行判断,有就直接读缓存数据库,不用连接服务器;没有,再去找服务器。

对比缓存
  • 对比缓存,顾名思义,需要进行比较判断是否可以使用缓存。
  • 浏览器第一次请求数据时,服务器会将缓存标识与数据一起返回给客户端,客户端将二者备份至缓存数据库中。
  • 再次请求数据时,客户端将备份的缓存标识发送给服务器,服务器根据缓存标识进行判断,判断成功后,返回304状态码,通知* 客户端比较成功,可以使用缓存数据。

请求流程

第一次请求,此时没有缓存

第二次请求

从上张图我们可以看到,判断缓存是否可用,有两种方式

  • ETag是实体标签的缩写,根据实体内容生成的一段hash字符串,可以标识资源的状态。当资源发生改变时,ETag也随之发生变化。ETag是Web服务端产生的,然后发给浏览器客户端。
  • Last-Modified是此资源的最后修改时间,
    • 如果客户端在请求到的资源中发现实体首部里有Last-Modified声明,再次请求就会在头里带上if-Modified-Since字段
    • 服务端收到请求后发现if-Modified-Since字段则与被请求资源的最后修改时间进行对比

说了这么多,不如直接来实现一下缓存

缓存的实现

通过最后修改时间来判断缓存是否可用
let http = require('http');
let url = require('url');
let path = require('path');
let fs = require('fs');
let mime = require('mime');//用来生成MimeType
let app = http.createServer((req, res) => {
    // 根据url获取客户端要请求的文件路径
    let { parsename } = url.parse(req.url);
    let p = path.join(__dirname, 'public', '.' + pathname);
    // fs.stat()用来读取文件信息,文件最后修改时间就是stat.ctime
    fs.stat(p, (err, stat) => {
        if (!err) {
            let since = req.headers['if-modified-since'];//客户端发来的文件最后修改时间
            if (since) {
                if (since === stat.ctime.toUTCString()) {//最后修改时间相等,读缓存
                    res.statusCode = 304;
                    res.end();
                } else {
                    sendFile(req, res, p, stat);//最后修改时间不相等,返回新内容
                }
            } else {
                sendError(res);
            }
        }
    })
})
function sendError(res) {
    res.statusCode = 404;
    res.end();
}
function sendFile(req, res, p, stat) {
    res.setHeader('Cache-Control', 'no-cache');// 设置通用首部字段 控制缓存行为
    res.setHeader('Last-Modified', stat.ctime.toUTCString());// 实体首部字段 资源最后修改时间
    res.setHeader('Content-Type', mime.getType(p) + ';charset=utf8')
    fs.createReadStream(p).pipe(res);
}
app.listen(3000, () => {
    console.log('server is starting on port 3000');
});
复制代码

最后修改时间存在问题:
1. 某些服务器不能精确得到文件的最后修改时间, 这样就无法通过最后修改时间来判断文件是否更新了。
2. 某些文件的修改非常频繁,在秒以下的时间内进行修改. Last-Modified只能精确到秒。
3. 一些文件的最后修改时间改变了,但是内容并未改变。 我们不希望客户端认为这个文件修改了。
4. 如果同样的一个文件位于多个CDN服务器上的时候内容虽然一样,修改时间不一样。

通过ETag来判断缓存是否可用

ETag就是根据文件内容来判断,说白了就是采用MD5(md5并不叫加密算法,它不可逆,应该叫摘要算法)产生信息摘要,用摘要来进行比对。

let http = require('http');
let url = require('url');
let path = require('path');
let fs = require('fs');
let mime = require('mime');
// crypto是node.js中实现加密和解密的模块 具体详解请自行了解
let crypto = require('crypto');
let app = http.createServer((req, res) => {
    // 根据url获取客户端要请求的文件路径
    let { parsename } = url.parse(req.url);
    let p = path.join(__dirname, 'public', '.' + pathname);
    // fs.stat()用来读取文件信息,文件最后修改时间就是stat.ctime
    fs.stat(p, (err, stat) => {
        let md5 = crypto.createHash('md5');//创建md5对象
        let rs = fs.createReadStream(p);
        rs.on('data', function (data) {
            md5.update(data);
        });
        rs.on('end', () => {
            let r = md5.digest('hex'); // 对文件进行md5加密
            // 下次就拿最新文件的加密值 和客户端请求来比较
            let ifNoneMatch = req.headers['if-none-match'];
            if (ifNoneMatch) {
                if (ifNoneMatch === r) {
                    res.statusCode = 304;
                    res.end();
                } else {
                    sendFile(req, res, p, r);
                }
            } else {
                sendFile(req, res, p, r);
            }
        });
    })
});
function sendError(res) {
    res.statusCode = 404;
    res.end();
}
function sendFile(req, res, p, stat) {
    res.setHeader('Cache-Control', 'no-cache');// 设置通用首部字段 控制缓存行为
    res.setHeader('Etag', r);// 响应首部字段 资源的匹配信息
    res.setHeader('Content-Type', mime.getType(p) + ';charset=utf8')
    fs.createReadStream(p).pipe(res);
}
app.listen(3000, () => {
    console.log('server is starting on port 3000');
});
复制代码

最后

本人水平有限,有不足之处,望大家指出改正。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值