前端缓存知识-强缓存与协商缓存

news2024/11/16 9:44:09

缓存的作用

  • 减少了冗余的数据传输,节省了网费。
  • 减少了服务器的负担, 大大提高了网站的性能
  • 加快了客户端加载网页的速度

缓存分类

  • 强制缓存如果生效,不需要再和服务器发生交互,而对比缓存不管是否生效,都需要与服务端发生交互
  • 两类缓存规则可以同时存在,强制缓存优先级高于对比缓存,也就是说,当执行强制缓存的规则时,如果缓存生效,直接使用缓存,不再执行对比缓存规则

强缓存

强制缓存,在缓存数据未失效的情况下,可以直接使用缓存数据。

在没有缓存数据的时候,浏览器向服务器请求数据时,服务器会将数据和缓存规则一并返回,缓存规则信息包含在响应 header 中,浏览器就是通过 header 中的缓存信息判断强缓存是否失效

对比缓存(协商缓存)

  • 浏览器第一次请求数据时,服务器会将缓存标识与数据一起返回给客户端,客户端将二者备份至缓存数据库中。
  • 再次请求数据时,客户端将备份的缓存标识发送给服务器,服务器根据缓存标识进行判断,判断成功后,返回 304 状态码,通知客户端比较成功,可以使用缓存数据。

请求流程

首次请求

二次请求

通过最后修改时间来判断缓存是否可用

  • Last-Modified:响应时告诉客户端此资源的最后修改时间
  • If-Modified-Since:当资源过期时(使用 Cache-Control 标识的 max-age),发现资源具有 Last-Modified 声明,则再次向服务器请求时带上头 If-Modified-Since
  • 服务器收到请求后发现有头 If-Modified-Since 则与被请求资源的最后修改时间进行比对。若最后修改时间较新,说明资源又被改动过,则响应最新的资源内容并返回 200 状态码;
  • 若最后修改时间和 If-Modified-Since 一样,说明资源没有修改,则响应 304 表示未更新,告知浏览器继续使用所保存的缓存文件。

代码示例

创建一个 cache.js 文件,在其下添加一个 index.html 文件,运行 cache.js,打开 http://localhost:8000/index.html 测试

let http = require('http');
let fs = require('fs');
let path = require('path');
let mime = require('mime');
http.createServer(function (req, res) {
  let file = path.join(__dirname, req.url);
  fs.stat(file, (err, stat) => {
    if (err) {
      sendError(err, req, res, file, stat);
    } else {
      let ifModifiedSince = req.headers['if-modified-since'];
      if (ifModifiedSince) {
        if (ifModifiedSince == stat.ctime.toGMTString()) {
          res.writeHead(304);
          res.end();
        } else {
          send(req, res, file, stat);
        }
      } else {
        send(req, res, file, stat);
      }
    }
  });
}).listen(8080);

function send (req, res, file, stat) {
  res.setHeader('Last-Modified', stat.ctime.toGMTString());
  res.writeHead(200, { 'Content-Type': mime.getType(file) });
  fs.createReadStream(file).pipe(res);
}

function sendError (err, req, res, file, stat) {
  res.writeHead(400, { "Content-Type": 'text/html' });
  res.end(err ? err.toString() : "Not Found");
}

首次访问:可以发现状态码为 200,且响应头中有 Last-Modified: Wed, 15 Feb 2023 06:28:32 GMT

二次访问,状态码为 204,然后请求头中有 If-Modified-Since: Wed, 15 Feb 2023 06:28:32 GMT

经过对比可以发现二次访问的 If-Modified-Since 和首次访问的 Last-Modified 时间一致,所以二次访问将从缓存中取此文件

存在的问题

  • 某些服务器不能精确得到文件的最后修改时间, 这样就无法通过最后修改时间来判断文件是否更新了。
  • 某些文件的修改非常频繁,在秒以下的时间内进行修改. Last-Modified 只能精确到秒。
  • 一些文件的最后修改时间改变了,但是内容并未改变。 我们不希望客户端认为这个文件修改了。
  • 如果同样的一个文件位于多个 CDN 服务器上的时候内容虽然一样,修改时间不一样。

通过 ETag 来判断缓存是否可用

ETag 是实体标签的缩写,根据实体内容生成的一段 hash 字符串,可以标识资源的状态。当资源发生改变时,ETag 也随之发生变化。 ETagWeb 服务端产生的,然后发给浏览器客户端。

  • 客户端想判断缓存是否可用可以先获取缓存中文档的 ETag,然后通过 If-None-Match 发送请求给 Web 服务器询问此缓存是否可用。
  • 服务器收到请求,将服务器的中此文件的 ETag,跟请求头中的 If-None-Match 相比较,如果值是一样的,说明缓存还是最新的,Web 服务器将发送 304 Not Modified 响应码给客户端表示缓存未修改过,可以使用。
  • 如果不一样则 Web 服务器将发送该文档的最新版本给浏览器客户端

代码示例

let http = require('http');
let fs = require('fs');
let path = require('path');
let mime = require('mime');
let crypto = require('crypto');
http.createServer(function (req, res) {
  let file = path.join(__dirname, req.url);
  fs.stat(file, (err, stat) => {
    if (err) {
      sendError(err, req, res, file, stat);
    } else {
      let ifNoneMatch = req.headers['if-none-match'];
      let etag = crypto.createHash('sha1').update(stat.ctime.toGMTString() + stat.size).digest('hex');
      if (ifNoneMatch) {
        if (ifNoneMatch == etag) {
          res.writeHead(304);
          res.end();
        } else {
          send(req, res, file, etag);
        }
      } else {
        send(req, res, file, etag);
      }
    }
  });
}).listen(8080);

function send (req, res, file, etag) {
  res.setHeader('ETag', etag);
  res.writeHead(200, { 'Content-Type': mime.lookup(file) });
  fs.createReadStream(file).pipe(res);
}

function sendError (err, req, res, file, etag) {
  res.writeHead(400, { "Content-Type": 'text/html' });
  res.end(err ? err.toString() : "Not Found");
}

如何直接不发请求,强缓存

  • 浏览器会将文件缓存到 Cache 目录,第二次请求时浏览器会先检查 Cache 目录下是否含有该文件,如果有,并且还没到 Expires 设置的时间,即文件还没有过期,那么此时浏览器将直接从 Cache 目录中读取文件,而不再发送请求
  • Expires 是服务器响应消息头字段,在响应 http 请求时告诉浏览器在过期时间前浏览器可以直接从浏览器缓存取数据,而无需再次请求,这是 HTTP1.0 的内容,现在浏览器均默认使用 HTTP1.1,所以基本可以忽略
  • Cache-ControlExpires 的作用一致,都是指明当前资源的有效期,控制浏览器是否直接从浏览器缓存取数据还是重新发请求到服务器取数据,如果同时设置的话,其优先级高于 Expires

Cache-Control

  • private:客户端可以缓存
  • public:客户端和代理服务器都可以缓存
  • max-age=60:缓存内容将在 60 秒后失效
  • no-cache:需要使用对比缓存验证数据,强制向源服务器再次验证
  • no-store:所有内容都不会缓存,强制缓存和对比缓存都不会触发

代码示例

html 中引入了 test.css 文件,同时服务端设置了 max-age=10 表示在 10 秒内取的都是缓存文件不用像服务端发请求

<!DOCTYPE html>
<html>
<head>
	<meta charset="UTF-8">
	<link rel="stylesheet" type="text/css" href="./test.css">
</head>
<body>
	hello3
</body>
</html>
let http = require('http');
let fs = require('fs');
let path = require('path');
let mime = require('mime');
http.createServer(function (req, res) {
  let file = path.join(__dirname, req.url);
  console.log(file);
  
  fs.stat(file, (err, stat) => {
    if (err) {
      sendError(err, req, res, file, stat);
    } else {
      send(req, res, file);
    }
  });
}).listen(8000);

function send (req, res, file) {
  let expires = new Date(Date.now() + 60 * 1000);
  res.setHeader('Expires', expires.toUTCString());
  res.setHeader('Cache-Control', 'max-age=10');
  fs.createReadStream(file).pipe(res);
}

function sendError (err, req, res, file, etag) {
  res.writeHead(400, { "Content-Type": 'text/html' });
  res.end(err ? err.toString() : "Not Found");
}

Cache-Control 的 private 与 public

模拟下面一种情况,客户端请求一个资源需要走代理服务器,代理服务器再去目标服务器取此资源

  • 如果设置 Cache-Controlpublic 则客户端和代理服务器都会有缓存,当另一个客户端去获取资源时,如果代理服务器有缓存,则不需要从目标服务器再获取一次
  • 如果设置 Cache-Controlprivate,那么缓存只在当前客户端生效,当另一个客户端去获取资源时,代理服务器依旧要再去目标服务器去获取资源

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

查询蓝牙适配器版本

台式机不支持蓝牙&#xff0c;装了个蓝牙适配器&#xff0c;结果换来换去又忘记自己这个是啥版本了&#xff0c;适配器自己也不写。好在微软官方也给了说明如何查询我的电脑运行哪个蓝牙版本&#xff1f; - Microsoft 支持https://support.microsoft.com/zh-cn/windows/%E6%88%…

day44【代码随想录】动态规划之零钱兑换II、组合总和 Ⅳ、零钱兑换

文章目录前言一、零钱兑换II&#xff08;力扣518&#xff09;二、组合总和 Ⅳ&#xff08;力扣377&#xff09;三、零钱兑换&#xff08;力扣322&#xff09;总结前言 1、零钱兑换II 2、组合总和 Ⅳ 3、零钱兑换 一、零钱兑换II&#xff08;力扣518&#xff09; 给你一个整数…

教你如何实现一个页面自动打字效果

前言&#xff1a; 最近在写一个类似于 windows 启动页的项目&#xff0c;不知道大家是否还记的 windows 很经典的小光标加载页面&#xff0c;我又稍微改造了一下效果如下&#xff1a; 一. 光标闪烁效果的实现 tips&#xff1a; 在这里我们使用了 UnoCSS&#xff0c;如果你不清…

金三银四,如果没准备这些面试题,找工作还是缓一缓吧

前言: 最近金三银四跳槽季&#xff0c;相信很多小伙伴都在面试找工作&#xff0c;怎样才能拿到大厂的offer&#xff0c;没有掌握绝对的技术&#xff0c;那么就要不断的学习… 如何拿下阿里等大厂的offer的呢&#xff0c;今天分享一个秘密武器&#xff0c;资深测试开发师整理的…

【面试问题-java内存模型JMM】

今天面试&#xff0c;我把运行时数据区域答成了java内存模型&#xff0c;回来把这方面的问题给纠正一下。 以下内容阅读自《深入理解Java虚拟机》第12章 下面小段只做了解即可。重点是Java内存模型。 多任务处理在现代计算机操作系统中是必备的功能。 计算机运行速度与它的存储…

【MySQL】数据库基础

目录 1、什么是数据库 2、 数据库基本操作 2.1 查看当前数据库 2.2 创建一个数据库 2.3 选中数据库 2.4 删除数据库 3、常见的数据类型 3.1 数值类型 3.2 字符串类型 3.3 日期类型 4、表的操作 4.1 创建表 4.2 查看指定数据库下的所有表 4.3 查看表的结构 4.…

java常见的异常

异常分类 Throwable 是java异常的顶级类&#xff0c;所有异常都继承于这个类。 Error,Exception是异常类的两个大分类。 Error Error是非程序异常&#xff0c;即程序不能捕获的异常&#xff0c;一般是编译或者系统性的错误&#xff0c;如OutOfMemorry内存溢出异常等。 Exc…

环境变量和进程地址空间

目录 环境变量&#xff1a; env&#xff1a;显示所有的环境变量&#xff1a; echo $环境变量名表示查看环境变量的值 理解环境变量&#xff1a; getenv&#xff1a;显示环境变量的值 export set命令&#xff1a;显示所有变量 unset取消变量&#xff1a; pwd&#xff1a;当…

Django框架之模型查询-关联查询

关联查询 查询书籍为1的所有人物信息 查询人物为1的书籍信息由一到多的访问语法&#xff1a; 一对应的模型类对象.多对应的模型类名小写_set 例&#xff1a; >>> book BookInfo.objects.get(id1) >>> book.peopleinfo_set.all() <QuerySet [<Peopl…

buntu18 安装 openpose(GPU)环境

openpose环境 搭建 很费劲&#xff0c; 需要装软件也多&#xff0c; 还必须要考虑版本的问题。我主要是参考链接 ubuntu18安装openpose详细步骤_litbo的博客-CSDN博客_ubuntu安装openpose 其中&#xff0c;我的实验中 有如下需要更改。 1、我的是 cuda-10.2 2、gcc 和g 必…

Linux下Socket编程利用多进程实现一台服务器与多台客户端并发通信

文章目录前言一、服务器 server二、客户端 client三、并发通信演示四、程序源码前言 前些日子同“ Linux应用编程 ”专栏中发布过的TCP及UDP在Linux或Windows下的通信都为单进程下的Socket编程&#xff0c;若还存在一些套接字相关函数模糊不清&#xff0c;读者可移步“Socket编…

整合K8s+SpringBoot+gRpc

本文使用K8s当做服务注册与发现、配置管理&#xff0c;使用gRpc用做服务间的远程通讯一、先准备K8s我在本地有个K8s单机二、准备service-providerpom<?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.…

Python3 模块实例及演示

在前面的几个章节中我们基本上是用 python 解释器来编程&#xff0c;如果从 Python 解释器退出再进入&#xff0c;那么定义的所有的方法和变量就都消失。 为此 Python 提供了1个办法&#xff0c;把这些定义存放在文件中&#xff0c;为一些脚本或者交互式的解释器实例使用&…

[oeasy]python0085_ASCII之父_Bemer_COBOL_数据交换网络

编码进化 回忆上次内容 上次 回顾了 字符编码的 进化过程 IBM 在数字化过程中 作用 非常大IBM 的 BCDIC 有 黑历史 &#x1f604; 6-bit的 BCDIC 直接进化成 8-bit的 EBCDIC补全了 小写字母 和 控制字符 在ibm就是信息产业的年代 ibm的标准 怎么最终 没有成为 行业的标准 呢…

OCR标注方法

虽然说标注工作不是由算法工程师负责的&#xff0c;但是如何标注&#xff0c;标注要求却是由算法工程师指导标注人员去实施的&#xff0c;如果标注工作人员标注的数据有问题&#xff0c;就会出现模型训练不收敛的问题&#xff0c;导致很多问题的出现&#xff0c;所以标注要求很…

MySql调优基础知识

MySql调优 调优金字塔 系统设计&#xff1a;数据不适合放入mysql&#xff0c;es、MQ、Redis、读写分离。 mysql调优&#xff1a;主要是索引且要熟悉业务。 mysql导致慢查询的原因是因为数据太多了。 1.sql查询 1.1尽量使用覆盖索引 1.2数据表结构&#xff0c;统计汇总&am…

普通单双面板的生产工艺流程之图形转移,华秋一文告诉你

衔接上文&#xff0c;继续为朋友们分享普通单双面板的生产工艺流程。 如图&#xff0c;第五道主流程为图形转移。 图形转移的目的为&#xff1a; 利用光化学原理&#xff0c;将图形线路的形状转移到印制板上&#xff0c;再利用化学原理&#xff0c;将图形线路在印制板上制作出…

Tomcat- AJP协议文件读取/命令执行漏洞(CVE-2020-1938 / CNVD-2020-10487)

CVE-2020-1938 1.概述1.1 tomcat概述1.2 gostcat概述 - 漏洞概述2. 漏洞成因2.1 前置基础2.1.1 Tomcat Connector(连接器)2.1.2 Servlet(服务程序)2.1.3 Tomcat内部处理请求流程2.2 源码追踪分析两个利用方案的执行流程2.2.1 获取利用poc2.2.2 文件读取漏洞关键点1&#xff1a;…

CSDN城市开发者联盟、C友会期待你的加入

文章目录&#x1f31f; 课前小差&#x1f31f; chatGPT&#x1f31f; CSDN中的持续学习&#x1f31f; 23年原力计划&#x1f31f; C友会、CDC&#x1f31f; 如何关联本地的开发者&#xff1f;&#x1f31f; 写在最后&#x1f31f; 课前小差 哈喽&#xff0c;大家好&#xff0c…

2月datawhale组队学习:大数据

文章目录一、大数据概述二、 Hadoop2.1 Hadoop概述2.2 su:Authentication failure2.3 使用sudo命令报错xxx is not in the sudoers file. This incident will be reported.2.4 创建用户datawhale&#xff0c;安装java8&#xff1a;2.5 安装单机版Hadoop2.5.1 安装Hadoop2.5.2 修…