当前位置: 首页 > 后端技术 > Node.js

nodejs篇-http缓存

时间:2023-04-03 16:33:00 Node.js

缓存是一种将资源保存一个副本,下次请求时直接使用副本的技术。通过重用以前获取的资源,可以显着提高网站性能,减轻服务器处理压力,减少等待时间和网络流量。通过使用HTTP缓存提高响应速度。本文将介绍nodejs中三种缓存机制的实现,分别是:强制缓存Cache-Control/Expiresvs缓存Last-Modified/If-Modified-SinceEtag/If-None-Match两种缓存规则针对不同的强制缓存If生效,无需与服务器交互。不管比对缓存是否有效,都需要和服务器进行交互。强制缓存的优先级高于比较缓存。当强制缓存生效后,将不再执行比较缓存规则。创建一个新的http服务为了便于测试,创建一个简单的http服务用于调试:consthttp=require("http")consturl=require("url")constmime=require("mime")constfs=require("fs")constserver=http.createServer((req,res)=>{const{pathname}=url.parse(req.url,true)constabspath=process.cwd()+pathnamefs.stat(abspath,handleRequest)//判断是文件还是文件夹functionhandleRequest(err,statObj){if(err||statObj.isDirectory())returnsendError(err)sendFile()}//响应错误请求functionsendError(error){res.statusCode=404res.end(`NotFound\r\n${error.toString()}`)}//响应文件请求函数sendFile(){res.setHeader("Content-Type",mime.getType(abspath)+";charset=utf-8")fs.createReadStream(abspath).pipe(res)}})server.listen(3000,()=>console.log("servinghttp://127.0.0.1:3000"))强制缓存强制缓存是指在缓存数据不失效的情况下可以直接使用缓存数据,浏览器通过服务器响应的头部获取缓存规则信息。对于强制缓存,响应头使用Cache-Control/Expires来指示过期规则。ExpiresExpires是HTTP1.0的东西。现在默认浏览器默认使用HTTP1.1,所以它的作用基本被忽略了。我们在响应头header中设置Expires,浏览器根据其过期时间来决定是否使用缓存数据:res。setHeader("Expries",newDate(Date.now()+5*1000).toUTCString());Cache-ControlCache-Control是最重要的规则。常用值有private、public、no-cache、max-age、no-store,默认为private。privateclient可以缓存public可以被任何中介(如中间代理,CDN等)缓存max-age=xxx缓存的内容会在xxx秒后过期(单位是秒)no-cache需要使用比较缓存来验证缓存datano-store不会缓存所有内容,强制缓存,不会触发比较缓存。可以在handleRequest方法的响应头中添加Cache-Control,刷新浏览器查看效果:functionhandleRequest(err,statObj){...res.setHeader("Cache-Control","max-age=10");sendFile()}如果经常调试前端项目的开发者经常查看consoleDisablecache,记得在这里关闭:不出意外的话,可以看到Networkrequest中的信息:StatusCode:200OK(fromdiskcache)比对缓存比对缓存,服务端将文件修改信息发送给客户端(浏览器),客户端下次请求的时候会带上,然后服务端就可以拿到上次的修改信息,进行比对与本地文件修改信息,并告诉客户端是使用缓存数据还是最新数据Last-Modified/If-Modified-Since比较时间可以通过statObj的ctime属性获取文件的修改时间,并通过请求头的Last-Modified属性将这个修改信息发送给浏览器:constserverTime=statObj.ctime.toUTCString()res.setHeader("Last-Modified",serverTime)下次客户端请求时,会同样通过if-modified-since带入请求头:constclientTime=req.headers["if-modified-since"]修改handleRequest方法如下:functionhandleRequest(err,statObj){if(err||statObj.isDirectory())返回sendError(err)constclientTime=req.headers["if-modified-since"]constserverTime=statObj.ctime.toUTCString()//如果本地文件的修改时间与浏览器返回的修改时间相同,则使用缓存的数据,返回304if(clientTime===serverTime){res.statusCode=304returnres.end()}res.setHeader("Last-Modified",serverTime)res.setHeader("Cache-Control","no-cache")//比较缓存,验证缓存数据sendFile()}但是这种方法有两个缺点:如果一个文件被误修改,然后撤销修改,虽然内容没有变化,但是最新的修改时间会变文件是周期性修改的,文件内容没有变化,但是最新修改时间会改变Etag/If-None-Match内容比较以上两个缺点可以通过Etag/If-None-Match的方式解决,即内容比较,但是Etag的生成有一定的开销,如果文件经常变动,会对服务器造成额外的压力当然,我们不可能把所有的内容都存储在header中。这里我们可以通过crypto将文件内容加密成一串秘钥,写入header的Etag属性中:constcrypto=require("crypto");...constmd5=crypto。创建哈希(“md5”);//md5加密constrs=fs.createReadStream(abspath);//创建可读流constdata=[];rs.on("data",(buffer)=>{md5.update(buffer);//加密data.push(buffer);});rs.on("end",()=>{constserverMatch=md5.digest("base64");//加密文件constclientMatch=req.headers["if-none-match"];//下一个请求来自客户端的会带上serverMatchif(clientMatch===serverMatch){//检查文件内容是否相同res.statusCode=304;returnres.end(null);}//设置ETagres.setHeader("ETag",serverMatch)res.end(Buffer.concat(data));})我们可以在业务中根据自己的需要进行集成,设置相应的缓存模式,这里写一个通用的方法来集成这三种模式:functioncache(statObj){//强制缓存res.setHeader("Cache-Control","max-age=60")//时间比较letlastModified=statObj.ctime.toUTCString()letmodifiedSince=req.headers["if-modified-since"]res.setHeader("Last-Modified",lastModified)if(modifiedSince!==lastModified)returnfalse//内容比较letetag=statObj.size+""letnoneMatch=req.headers["if-none-match"]res.setHeader("ETag",etag)if(etag!==noneMatch)returnfalsereturntrue}...if(cache(statObj)){res.statusCode=304returnres.end(null)}参考文章:彻底理解HTTP缓存机制和原理HTTP缓存协商缓存