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 再次刷新我们可以看到变成协商缓存

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/87593.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

[虚幻引擎 UE5] EditableText(可编辑文本) 限制只能输入数字并且设置最小值和最大值

本蓝图函数可以格式化 EditableText 控件输入的数据,让其只能输入一定范围内的整数。 蓝图函数 调用方法 下载蓝图(5.2.1版本)https://dt.cq.cn/archives/618

放苹果(巧用递归)--夏令营

题目 tips: 1.写递归要有递归边界条件,递归过程就是向边界不断靠近 这里注意:虽然题目给的m,n输入数据范围是>1的,但不代表边界就是这个; 首先,n0肯定是不存在的,所以n的边界肯定是1&#…

Spring Cloud Alibaba-实现服务调用的负载均衡

1. 什么是负载均衡 通俗的讲, 负载均衡就是将负载(工作任务,访问请求)进行分摊到多个操作单元(服务器,组件)上进行执行。 根据负载均衡发生位置的不同,一般分为服务端负载均衡和客户端负载均衡。 服务端负…

PyTorch学习笔记(十七)——完整的模型验证(测试,demo)套路

完整代码: import torch import torchvision from PIL import Image from torch import nnimage_path "../imgs/dog.png" image Image.open(image_path) print(image)# 因为png格式是四个通道,除了RGB三通道外,还有一个透明度通…

Android Lottie加载gson文件动画

一&#xff1a;Lottie的使用 在你工程的build.gradle文件里添加如下配置 implementation com.airbnb.android:lottie:3.4.0二&#xff1a;布局文件直接引入LottieAnimationView <com.airbnb.lottie.LottieAnimationViewandroid:id"id/lottie_view"android:layout…

原生JS实现拾色器功能

没事儿干&#xff0c;写一个拾色器&#xff0c;原生 JS 实现&#xff0c;先看效果图&#xff1a; 一、写页面 <div class"circle"></div>.circle {width: 200px;height: 200px;border: 1px #999 solid;margin: 200px 0 0 200px;border-radius: 50%;back…

【TI毫米波雷达笔记】CCS雷达工程调试(以IWR6843AOP为例)

【TI毫米波雷达笔记】CCS雷达工程调试&#xff08;以IWR6843AOP为例&#xff09; 先前我们讨论了如何建立工程并编译 包括DSS和MSS部分 也就是DSP部分和cortex-r4f部分 通过编译 可以生成一个.out文件 如图 同样的 也有xer4f格、xe674格式等等 这取决于编译的工程配置 但这…

JUC学习笔记(一)

1. JUC概述及回顾 1.1. JUC是什么&#xff1f; 在 Java 5.0 提供了 java.util.concurrent(简称JUC)包&#xff0c;在此包中增加了在并发编程中很常用的工具类。此包包括了几个小的、已标准化的可扩展框架&#xff0c;并提供一些功能实用的类&#xff0c;没有这些类&#xff0…

C#|如何调试进依赖动态库中

第一步&#xff1a;打开项目属性 第二步 打开debug的本地调试可用 第三步 把要调试的代码拖进主界面打断点就可以进断点了

用友T3 T6 服务无法启动 windows10 11等操作系统 T3服务没有开启

windows 10 11 等高版本操作系统故障。 于2023-08-23日大量爆发。。 导致原因&#xff0c;windows操作系统根证书颁发机构吊销或已到期。 正版软件请打11.2最新补丁即可解决。 如果是老版本需要修复证书才可以。

Spring复习:(55)ApplicationContext中BeanFactoryPostProcessor是怎么添加到容器的?

容器创建时会调用AbstractApplicationContext的refresh方法&#xff0c;其中会调用invokeBeanFactoryPostProcessor方法&#xff0c;如下图 invokeBeanFactoryPostProcessors代码如下&#xff1a; 其中调用的PostProcessorRegistrationDelegate的invokeBeanFactoryPostProcess…

STM32F4X USART串口使用

STM32F4X USART串口使用 串口概念起始位波特率数据位停止位校验位串口间接线 STM32F4串口使用步骤GPIO引脚复用函数串口初始化函数串口例程 串口概念 串口是MCU与外部通信的重要通信接口&#xff0c;也是MCU在开发过程中的调试利器。串口通信有几个重要的参数&#xff0c;分别…

11、vue3

一、为什么要学 Vue3 1.1 为什么要学 Vue3 1.2 Vue3的优势 1.3 Vue2 选项式 API vs Vue3 组合式API Vue3 组合式API vs Vue2 选项式 API 二、create-vue搭建Vue3项目 2.1 认识 create-vue 2.2 使用create-vue创建项目 前提环境条件 已安装 16.0 或更高版本的 Node.js node -…

【论文阅读】自动驾驶安全的研究现状与挑战

文章目录 摘要1.引言1.1.自动驾驶安全1.2.攻击面1.3.内容和路线图 2.自动驾驶技术2.1.组成2.2.技术 3.传感器安全3.1.照相机3.2.GNSS&#xff08;全球导航系统&#xff09;/IMU&#xff08;惯性测量单元&#xff09;3.3.超声波传感器3.4.毫米波雷达3.5.激光雷达3.6.多传感器交叉…

容器化微服务:用Kubernetes实现弹性部署

随着云计算的迅猛发展&#xff0c;容器化和微服务架构成为了构建现代应用的重要方式。而在这个过程中&#xff0c;Kubernetes&#xff08;常简称为K8s&#xff09;作为一个开源的容器编排平台&#xff0c;正在引领着容器化微服务的部署和管理革命。本文将深入探讨容器化微服务的…

32、启用 HTTP 响应压缩和编程式配置Web应用

★ 启用HTTP压缩 就是前端页面如果改动的比较多&#xff0c;那么响应就会比较慢&#xff0c;可以通过设置HTTP响应压缩来提高响应&#xff0c;如果前端改动少&#xff0c;那么就不需要启动这个响应压缩。 目的&#xff1a;为了提高HTTP响应数据在网络上的传输效率。▲ 设置如…

【C++练习】普通方法+利用this 设置一个矩形类(Rectangle), 包含私有成员长(length)、 宽(width), 定义一下成员函数

题目 设置一个矩形类(Rectangle), 包含私有成员长(length)、 宽(width), 定义成员函数: void set_ len(int l); //设置长度 设置宽度void set_ wid(int w); 获取长度: int get len(); 获取宽度: int get _wid); 显示周长和面积: v…

oracle警告日志\跟踪日志磁盘空间清理

oracle警告日志\跟踪日志磁盘空间清理 问题现象&#xff1a; 通过查看排查到alert和tarce占用大量磁盘空间 警告日志 /u01/app/oracle/diag/rdbms/orcl/orcl/alert 跟踪日志 /u01/app/oracle/diag/rdbms/orcl/orcl/trace 解决方案&#xff1a; 用adrci清除日志 确定目…

系统架构设计师-计算机系统基础知识(1)

目录 一、计算机系统概述 1、冯诺依曼计算结构​编辑 二、存储系统 三、操作系统概述 1、特殊的操作系统 四、进程管理 1、进程与线程的概念 2、进程的同步与互斥 3、PV操作 4、死锁与银行家算法 一、计算机系统概述 1、冯诺依曼计算结构 二、存储系统 从上到下依次&#…

solidity0.8.0的应用案例9:代理合约

代码由OpenZeppelin的Proxy合约简化而来。 代理模式 Solidity合约部署在链上之后,代码是不可变的(immutable)。这样既有优点,也有缺点: 优点:安全,用户知道会发生什么(大部分时候)。坏处:就算合约中存在bug,也不能修改或升级,只能部署新合约。但是新合约的地址与…