node实现缓存

为了减少数据传输,减少请求数,继续添加缓存支持。首先梳理一下缓存的处理流程:

  1. 如果是第一次访问,请求报文首部不会包含相关字段,服务端在发送文件前做如下处理:
    • 设置Expires
    • 设置Cache-Control头(设置其max-age值)
    • 如服务器支持Last-Modified,设置Last-Modified
    • 如服务器支持ETag,设置ETag

    浏览器收到响应后会存下这些标记,并在下次请求时带上与ETag对应的请求首部If-None-Match或与Last-Modified对应的请求首部If-Modified-Since

  2. 如果是重复的请求:
    • 浏览器判断缓存是否过期(通过Cache-ControlExpires确定)
      • 如果未过期,直接使用缓存内容,也就是强缓存命中,并不会产生新的请求
      • 如果已过期,会发起新的请求,并且请求会带上If-None-MatchIf-Modified-Since,或者兼具两者
      • 服务器收到请求,进行缓存的新鲜度再验证:
        • 首先检查请求是否有If-None-Match首部,没有则继续下一步,有则将其值与文档的最新ETag匹配,失败则认为缓存不新鲜,成功则继续下一步
        • 接着检查请求是否有If-Modified-Since首部,没有则保留上一步验证结果,有则将其值与文档最新修改时间比较验证,失败则认为缓存不新鲜,成功则认为缓存新鲜

        当两个首部皆不存在或者验证结果是不新鲜时,发送200及最新文件,并在首部更新新鲜度。

        当验证结果是缓存仍然新鲜时(也就是弱缓存命中),不需发送文件,仅发送304,并在首部更新新鲜度

添加缓存配置

cache: {    //配置服务器支持的缓存情况
    maxAge: 600,    //服务器认为自己文件有效的秒数,也就是说10分钟内这个文件就用本地缓存的
    expires: true,  //Expires 响应头包含日期/时间, 即在此时候之后,响应过期。如果在Cache-Control响应头设置了 "max-age" 或者 "s-max-age" 指令,那么 Expires 头会被忽略。
    cacheControl: true,  //是否支持CacheControl  
    lastModified: true, //是否支持last modified
    etag: true  //是否支持etag
}

缓存工具函数

const {cache} = require('../config/defaultConfig');

//根据stat生成ETag
function generateETag(stat) {
    const mtime = stat.mtime.getTime().toString(16);
    const size = stat.size.toString(16);
    return `W/"${size}-${mtime}"`;
}

function refreshRes(stats, res) {
    const {maxAge, expires, cacheControl, lastModified, etag} = cache;

    if(expires) {
        // Expires 响应头包含日期/时间, 即在此时候之后,响应过期。
        // 无效的日期,比如 0, 代表着过去的日期,即该资源已经过期。
        res.setHeader('Expires', (new Date(Date.now() + maxAge*1000)).toUTCString());
    }

    if(cacheControl) {
        res.setHeader('Cache-Control', `public, max-age=${maxAge}`);
    }

    if(lastModified) {
        //stats.mtime表示修改时间
        res.setHeader('Last-Modified', stats.mtime.toUTCString());
    }
    if(etag) {  //添加etag
        res.setHeader('ETag', generateETag(stats));
    }
}
module.exports = function isFresh(stats, req, res) {

    //为返回的结果【res】添加缓存信息
    refreshRes(stats, res);

    //判断请求【req】是否让客户端继续使用缓存
    const lastModified = req.headers['if-modified-since'];
    const etag = req.headers['if-none-match'];
    //如果这两个信息都没有,说明他很有可能是第一次请求
    if(!lastModified && !etag) {
        return false;
    }
    // 如果有lastModified,并且宇设置的lastModified不一样,说明也过期了
    if(lastModified && lastModified !== res.getHeader('Last-Modified')) {
        return false;
    }
    //检测etag
    if(etag && etag !== res.getHeader('ETag')) {
        return false;
    }

    return true;    //表示让客户端继续用缓存
};

在返回资源之前进行判断

//判断缓存是否可用
if(isFresh(stats, req, res)) {
    res.statusCode = 304;
    res.end();
    return;
}

原文链接:https://github.com/sheila1227/FE-blog/issues/1

猜你喜欢

转载自blog.csdn.net/qq_37746973/article/details/82221758