72 # http 缓存策略

简介: 72 # http 缓存策略

前面实现了一个 http-server,并且实现了 gzip 的压缩,下面通过前面几节学习的缓存知识来添加一下缓存。

大致就是先强制缓存 10s,然后采用协商(对比)缓存,大致图如下

在之前的 http-server 的代码基础上添加 cache 方法的实现,实现如下:

// 核心模块
const http = require("http");
const path = require("path");
const url = require("url");
const fs = require("fs").promises;
const crypto = require("crypto");
const { createReadStream, createWriteStream, readFileSync } = require("fs");
// 第三方模块
const ejs = require("ejs"); // 服务端读取目录进行渲染
const mime = require("mime");
const chalk = require("chalk");
const debug = require("debug")("server");
// 根据环境变量来进行打印 process.env.EDBUG
debug("hello kaimo-http-server");
// 同步读取模板
const template = readFileSync(path.resolve(__dirname, "template.ejs"), "utf-8");
class Server {
    constructor(config) {
        this.host = config.host;
        this.port = config.port;
        this.directory = config.directory;
        this.template = template;
    }
    async handleRequest(req, res) {
        let { pathname } = url.parse(req.url);
        // 需要对 pathname 进行一次转义,避免访问中文名称文件找不到问题
        console.log(pathname);
        pathname = decodeURIComponent(pathname);
        console.log(pathname);
        // 通过路径找到这个文件返回
        let filePath = path.join(this.directory, pathname);
        console.log(filePath);
        try {
            // 用流读取文件
            let statObj = await fs.stat(filePath);
            // 判断是否是文件
            if (statObj.isFile()) {
                this.sendFile(req, res, filePath, statObj);
            } else {
                // 文件夹的话就先尝试找找 index.html
                let concatFilePath = path.join(filePath, "index.html");
                try {
                    let statObj = await fs.stat(concatFilePath);
                    this.sendFile(req, res, concatFilePath, statObj);
                } catch (e) {
                    // index.html 不存在就列出目录
                    this.showList(req, res, filePath, statObj, pathname);
                }
            }
        } catch (e) {
            this.sendError(req, res, e);
        }
    }
    // 列出目录
    async showList(req, res, filePath, statObj, pathname) {
        // 读取目录包含的信息
        let dirs = await fs.readdir(filePath);
        console.log(dirs, "-------------dirs----------");
        try {
            let parseObj = dirs.map((item) => ({
                dir: item,
                href: path.join(pathname, item) // url路径拼接自己的路径
            }));
            // 渲染列表:这里采用异步渲染
            let templateStr = await ejs.render(this.template, { dirs: parseObj }, { async: true });
            console.log(templateStr, "-------------templateStr----------");
            res.setHeader("Content-type", "text/html;charset=utf-8");
            res.end(templateStr);
        } catch (e) {
            this.sendError(req, res, e);
        }
    }
    gzip(req, res, filePath, statObj) {
        if (req.headers["accept-encoding"] && req.headers["accept-encoding"].includes("gzip")) {
            // 给响应头添加内容编码类型头,告诉浏览器内容是什么编码类型
            res.setHeader("Content-Encoding", "gzip");
            // 创建转化流
            return require("zlib").createGzip();
        } else {
            return false;
        }
    }
    // 设置缓存
    async cache(req, res, filePath, statObj) {
        // 先设置强制缓存
        res.setHeader("Expires", new Date(Date.now() + 10 * 1000).toGMTString());
        res.setHeader("Cache-Control", "max-age=10");
        // 再设置协商缓存
        let fileContent = await fs.readFile(filePath);
        // 指纹
        let ifNoneMatch = req.headers["if-none-match"];
        let etag = crypto.createHash("md5").update(fileContent).digest("base64");
        // 修改时间
        let ifModifiedSince = req.headers["if-modified-since"];
        let ctime = statObj.ctime.toGMTString();
        res.setHeader("Last-Modified", ctime);
        res.setHeader("ETag", etag);
        // 文件变动了就不缓存,读取新文件
        if (ifNoneMatch !== etag) {
            return false;
        }
        if (ifModifiedSince !== ctime) {
            return false;
        }
        return true;
    }
    // 读取文件返回
    async sendFile(req, res, filePath, statObj) {
        // 缓存
        let cache = await this.cache(req, res, filePath, statObj);
        // 有缓存直接让用户查找缓存即可
        if (cache) {
            res.statusCode = 304;
            return res.end();
        }
        // 设置类型
        res.setHeader("Content-type", mime.getType(filePath) + ";charset=utf-8");
        // 读取文件进行响应
        // 先判断浏览器是否支持 gzip 压缩
        let gzip = this.gzip(req, res, filePath, statObj);
        if (gzip) {
            createReadStream(filePath).pipe(gzip).pipe(res);
        } else {
            createReadStream(filePath).pipe(res);
        }
    }
    // 专门处理错误信息
    sendError(req, res, e) {
        debug(e);
        res.statusCode = 404;
        res.end("Not Found");
    }
    start() {
        const server = http.createServer(this.handleRequest.bind(this));
        server.listen(this.port, this.host, () => {
            console.log(chalk.yellow(`Starting up kaimo-http-server, serving ./${this.directory.split("\\").pop()}\r\n`));
            console.log(chalk.green(`       http://${this.host}:${this.port}`));
        });
    }
}
module.exports = Server;

我们启动服务

kaimo-http-server

访问: http://localhost:3000/public/index.html

我们立即刷新,可以看到强制缓存

过了 10s 再次刷新我们可以看到变成协商缓存

目录
相关文章
|
27天前
|
缓存 算法 数据挖掘
深入理解缓存更新策略:从LRU到LFU
【10月更文挑战第7天】 在本文中,我们将探讨计算机系统中缓存机制的核心——缓存更新策略。缓存是提高数据检索速度的关键技术之一,无论是在硬件还是软件层面都扮演着重要角色。我们会详细介绍最常用的两种缓存算法:最近最少使用(LRU)和最少使用频率(LFU),并讨论它们的优缺点及适用场景。通过对比分析,旨在帮助读者更好地理解如何选择和实现适合自己需求的缓存策略,从而优化系统性能。
42 3
|
10天前
|
存储 缓存 监控
利用 Redis 缓存特性避免缓存穿透的策略与方法
【10月更文挑战第23天】通过以上对利用 Redis 缓存特性避免缓存穿透的详细阐述,我们对这一策略有了更深入的理解。在实际应用中,我们需要根据具体情况灵活运用这些方法,并结合其他技术手段,共同保障系统的稳定和高效运行。同时,要不断关注 Redis 缓存特性的发展和变化,及时调整策略,以应对不断出现的新挑战。
40 10
|
6天前
|
Web App开发 缓存 UED
如何设置浏览器的缓存策略?
【10月更文挑战第23天】通过合理地设置浏览器的缓存策略,可以在提高网页性能、减少网络流量的同时,确保用户能够获取到最新的内容,从而提升用户体验和网站的性能优化效果。
36 4
|
7天前
|
存储 消息中间件 缓存
缓存策略
【10月更文挑战第25天】在实际应用中,还需要不断地监控和调整缓存策略,以适应系统的变化和发展。
|
10天前
|
缓存 监控 NoSQL
Redis 缓存穿透及其应对策略
【10月更文挑战第23天】通过以上对 Redis 缓存穿透的详细阐述,我们对这一问题有了更深入的理解。在实际应用中,我们需要根据具体情况综合运用多种方法来解决缓存穿透问题,以保障系统的稳定运行和高效性能。同时,要不断关注技术的发展和变化,及时调整策略,以应对不断出现的新挑战。
31 4
|
14天前
|
存储 缓存 NoSQL
保持HTTP会话状态:缓存策略与实践
保持HTTP会话状态:缓存策略与实践
|
1月前
|
存储 缓存 监控
|
30天前
|
缓存 分布式计算 NoSQL
大数据-47 Redis 缓存过期 淘汰删除策略 LRU LFU 基础概念
大数据-47 Redis 缓存过期 淘汰删除策略 LRU LFU 基础概念
60 2
|
1月前
|
存储 缓存 监控
HTTP:强缓存优化实践
HTTP强缓存是提升网站性能的关键技术之一。通过精心设计缓存策略,不仅可以显著减少网络延迟,还能降低服务器负载,提升用户体验。实施上述最佳实践,结合持续的监控与调整,能够确保缓存机制高效且稳定地服务于网站性能优化目标。
44 3
|
21天前
|
缓存 前端开发 安全
前端开发者必备:HTTP状态码含义与用途解析,常见错误码产生原因及解决策略
前端开发者必备:HTTP状态码含义与用途解析,常见错误码产生原因及解决策略
85 0