缓存
- 减少了冗余的数据传输,节省了贷款。
- 减少了服务器的负担, 大大提高了网站的性能
- 加快了客户端加载网页的速度
Chrome://cache
缓存分强制缓存和对比缓存
强制缓存如果生效,不需要再和服务器发生交互,而对比缓存不管是否生效,都需要与服务端发生交互,强制缓存优先级高于对比缓存
强制缓存 (第一次访问走服务器,在一段时间内走缓存)
//Expires 过期时间,设置都是绝对时间 http,1.0 //Cache-Control http:1.1
<body>
<img src='/favicon.ico' alt="">
</body>
复制代码
//服务器
let http = require('http');
let url = require('url');
let path = require('path');
let fs = require('fs');
let zlib = require('zlib');
let mime = require('mime'); // mime可以根据路径判断当前文件是什么类型 npm install mime
let server = http.createServer(function (req,res) {
let {pathname} = url.parse(req.url,true);
if(pathname === '/' || pathname === '\\') pathname = '\index.html';
//如果访问的是localhost:3000/ 会访问c盘
let p = path.join(__dirname,pathname); //判断路径是否存在
fs.stat(p,function (err,stat) {
if(!err){
let d = new Date(Date.now()+5000).toUTCString();//科学时间
res.setHeader('Expires', d); // http1.0 哪天哪秒要过期 默认1.0
res.setHeader('Cache-Control', 'max-age=5') // http1.1 最大存活时间 单位是秒
res.setHeader('Content-Type', mime.getType(p) + ';charset=utf8');//mime根据当前文件名判断文件类型,mime需要安装
fs.createReadStream(p).pipe(res);
}else{
res.statusCode = 404;
res.end();
}
})
})
server.listen(3000);
复制代码
对比缓存 (每次请求的时候对比)
第一次来的时候给一个标识,下一次再来会带上这个标识,然后通过标识进行对比,根据修改时间判断
//if-modified-since | last-modified
let http = require('http');
let url = require('url');
let path = require('path');
let fs = require('fs');
let zlib = require('zlib');
let mime = require('mime'); // mime可以根据路径判断当前文件是什么类型 npm install mime
let server = http.createServer(function (req,res) {
let {pathname} = url.parse(req.url,true);
if(pathname === '/' || pathname === '\\') pathname = '\index.html';
let p = path.join(__dirname,pathname);
fs.stat(p,function (err,stat) {
if(!err){
//对比缓存,看是否修改过,最后的修改时间
if (req.headers['if-modified-since'] === stat.ctime.toUTCString()){
res.statusCode = 304;
res.end();
}else{
// 第一次设置Last-Modified 下一次请求时 会提供一个头 if-modified-since
res.setHeader('Last-Modified', stat.ctime.toUTCString());//stat.ctime.toUTCString()当前时间
res.setHeader('Cache-Control','no-cache');//关掉浏览器默认缓存机制
fs.createReadStream(p).pipe(res);
}
}else{
res.statusCode = 304;
res.end();
}
})
})
server.listen(3000);
复制代码
参考: ctime 修改文件属性和修改内容都会改变 if-modified-since的时间并不准确,比如我们使用cdn发送到服务器的时间不同,我们可以对比内容,但是这样就会大大消耗性能
内容缓存对比
//etag实体标签,将文件读出来传过来,一般会用MD5摘要 返回 if -none-match
let http = require('http');
let url = require('url');
let path = require('path');
let fs = require('fs');
let zlib = require('zlib');
let mime = require('mime'); // mime可以根据路径判断当前文件是什么类型 npm install mime
let crypto = require('crypto'); // 常见的加密模块
let server = http.createServer(function (req,res) {
let {pathname} = url.parse(req.url,true);
if(pathname === '/' || pathname === '\\') pathname = '\index.html';
let p = path.join(__dirname,pathname);
fs.stat(p,function (err,stat) {
// 强制缓存 协商缓存 = 三种都用,强制缓存期间不会请求服务器了
if(!err){
let md5 = crypto.createHash('md5');
let rs = fs.createReadStream(p,{highWaterMark:3});
rs.on('data',function (data) {
md5.update(data); // update可以调用多次 data是buffer
});
rs.on('end', function (data) {
//拿最新请求的加密值和客户端去比较
lqt r = md5.digest('hex');
if(req.heasers['if-none-match'] === r){
res.statusCode = 304;
res.end();
}else{
res.setHeader('Etag', r);
res.setHeader('Cache-Control','no-cache');
fs.createReadStream(p).pipe(res);
}
})
}else{
res.statusCode = 304;
res.end();
}
//对比缓存,看是否修改过,最后的修改时间
})
})
server.listen(3000);
复制代码
上个方法很消耗性能,我们一般三个结合用,MD5摘要一般不会真的读,可能会用ctime时间戳和文件大小来作为摘要
终极方案
- 强制缓存 协商缓存 = 三种都用,强制缓存期间不会请求服务器了
let http = require('http');
let url = require('url');
let path = require('path');
let fs = require('fs');
let zlib = require('zlib');
let mime = require('mime');
let crypto = require('crypto');
let server = http.createServer(function (req, res) {
let { pathname } = url.parse(req.url, true);
let p = path.join(__dirname, pathname);
fs.stat(p, function (err, stat) {
if (!err) {
//是否修改过
if (req.headers['if-modified-since'] === stat.ctime.toUTCString()){
res.statusCode = 304;
res.end();
}else{
let rs = fs.createReadStream(p);
let md5 = crypto.createHash('md5');
rs.on('data',function (data) {
md5.update(data);
});
// 可以用文件的大小和修改时间搭配使用 强制缓存 协商缓存 = 三种都用
rs.on('end',function () {
let value = md5.digest('hex');
let head = req.headers['if-none-match'];
if(head === value){
res.statusCode = 304;
res.end();
}else{
//过期时间
res.setHeader('Last-Modified', stat.ctime.toUTCString());
let d = new Date(Date.now()+5000).toUTCString();
res.setHeader('Expires', d); // http1.0
res.setHeader('Cache-Control', 'max-age=5') // http1.1
res.setHeader('Content-Type', mime.getType(p) + ';charset=utf8');
res.setHeader('Etag', value);
fs.createReadStream(p).pipe(res);
}
})
}
} else {
res.statusCode = 304;
res.end();
}
})
});
server.listen(3000);
复制代码