缓存

2020 M12 20

概述

缓存是一种保存资源副本并在下次请求时直接使用该副本的技术。同样的,缓存也是http层面重要的优化手段。

缓存分类

缓存可分为强缓存和协商缓存。强缓存指的是缓存有效期内客户端无需二次向服务端发送请求,直接从缓存中获取,状态码为200。网络请求中的from disk cache ,from memory就表示命中强缓存
协商缓存指的是强缓存失效后,客户端需要带上对应的缓存头向服务端发送请求进行协商,服务端对比后决定缓存是否可用。
如命中协商缓存,则状态码为304,响应体无内容。更新缓存时间,继续使用,下一轮次将是强缓存(这是一个循环)。 如强缓存和协商缓存都未命中,客户端需要从服务端获取数据,此时状态码为200。

相关http头

对于http头而言,除了方法区分大小写(GET,POST...),其他都是大小写不敏感的,建议首字母大写。
强缓存:Cache-Control ,Expires,二者同时出现Cache-Control优先生效。
协商缓存:Last-Modified If-Modified-Since Etag If-None-Match。
响应头:Last-Modified,Etag ,二者同时出现Etag优先生效。
请求头:If-Modified-Since,If-None-Match,前者用于和Last-Modified值比对,后者用于和Etag值比对。
出现类似功能的头是因为历史原因,Cache-Control基本上可以取代Expires,但是Etag无法完全取代Last-Modified。
Etag根据文件内容摘要比对,Last-Modified根据修改时间比对,维度不同。
Etag虽然更精准,但是比起Last-Modified要额外读取文件,进行摘要处理,如果是大文件会很耗时。
一般情况下,会读取部分文件内容生成摘要。耗时和精准度上来讲,这也是一个取舍。

缓存策略

缓存策略,其实就是通过合理的指定各种缓存相关头的值,从而达到性能优化的一种有效方法。
对于网站logo这种近乎一两年,甚至十几年都不会变动的,可以直接强缓存Cache-Control设置max-age为年级别的数值。
涉及频繁读写的场景可以设置强缓存的时间短一些,或者直接Cache-Control:no-cache,进入协商缓存。
如果是实时读写,缓存可能不太实用,压缩会更有效一些。Cache-Control:no-store 不进行任何数据的缓存。
抛开实际场景谈缓存意义不大,要根据实际场景按需制定缓存策略。

协商缓存实现



    cache(stat, filePath, req, res) {

        //协商缓存:如果last-modified 和 etag 同时存在,etag优先生效
        //读取文件内容生成唯一标识
        const Etag = crypto.createHash('md5').update(fs.readFileSync(filePath)).digest('base64')
        res.setHeader('Etag', Etag)
        const ifNoneMatch = req.headers['if-none-match']
        //返回比对结果
        if (ifNoneMatch) return ifNoneMatch === Etag
        //上次修改时间
        let lastModified = stat.ctime.toGMTString()
        res.setHeader('Last-Modified', lastModified)
        const ifModifiedSince = req.headers['if-modified-since']
        //返回比对结果
        if (ifModifiedSince) return ifModifiedSince === lastModified
        //首次没缓存 返回fasle
        return false
    }

设置强缓存


res.setHeader('Cache-Control', `max-age=10`)
res.setHeader('Expires', new Date(Date.now() + 10 * 1000).toGMTString())