【浏览器】缓存

为什么有缓存?

在一个C/S结构中,最基本的缓存分为两种:

  • 客户端缓存
  • 服务器缓存

以下重点讲客户端缓存

所谓客户端缓存,顾名思义,是将某一次的响应结果保存在客户端(比如浏览器)中,而后续的请求仅需要从缓存中读取即可,极大的降低了服务器的处理压力。

来自服务器的缓存指令

当客户端发出一个get请求到服务器,服务器希望客户端将相应资源缓存起来,服务器在响应头中加入了以下内容:

Cache-Control:max-age=3600
ETag:W/"121-171ca289ebf"
Date:Thu,30 Apr 2020 12:39:56 GMT
Last-Modified:Thu, 30Apr2o20 08:16:31GMT

这个响应头表达了下面的信息:

  • Cache-Control:max-age=3600, 缓存时间是3600秒(1小时)
  • ETag:W/“121-171ca289ebf”, 这个资源的编号是W/“121-171ca289ebf”
  • Date:Thu,30Apr202012:39:56GMT, 我给你响应这个资源的服务器时间是格林威治时间2020-04-3012:39:56
  • Last-Modified:Thu,30Apr2020g8:16:31GMT, 这个资源的上一次修改时间是格林威治时间2020-04-3088:16:31

如果客户端是其他应用程序,可能并不会理会服务器的愿望,也就是说,可能根本不会缓存任何东西。

如果刚好是一个浏览器,那么就会执行缓存:

  • 浏览器把这次请求得到的响应体缓存到本地文件中
  • 浏览器标记这次请求的请求方法和请求路径
  • 浏览器标记这次缓存的时间是3600秒
  • 浏览器记录服务器的响应时间是格林威治时间2020-04-3012:39:56
  • 浏览器记录服务器给予的资源编号W/“121-171ca289ebf”
  • 浏览器记录资源的上一次修改时间是格林威治时间2020-04-3088:16:31

来自客户端的缓存协议

当客户端准备再次请求GET /index.js时,它突然想起了一件事:我需要的东西在不在缓存里呢?
此时,客户端会到缓存中去寻找是否有缓存的资源,寻找的过程如下:

  1. 缓存中是否有匹配的请求方法和路径?
  2. 如果有,该缓存资源是否有效呢?

以上两个验证会导致浏览器产生不同的行为

image.png

缓存无效(协商缓存)

如果缓存有效则直接使用缓存结果。当浏览器发现缓存已经过期,它并不会简单的把缓存删除,而是抱着一丝希望,想问问服务器,我这个缓存还能继续使用吗?

于是,浏览器向服务器发出了一个带缓存的请求,又称之为协商缓存。

所谓带缓存的请求,无非就是加入了以下的请求头:

If-Modified-Since: Thu,30 Apr 2020 08:16:31 GMT
If-None-Match: W/"121-171ca289ebf"

它们表达了下面的信息:

  • If-Modified-Since:Thu,30Apr202088:16:31GMT 这个资源
    的上一次修改时间是格林威治时间2020-04-3088:16:31,请问这个资源在这个时间之后有
    发生变动吗?
  • If-None-Match:W/“121-171ca289ebf” 这个资源的编号是W/"121-171ca289ebf, 请问这个资源的编号发生变动了吗?

其实,这两个问题可以合并为一个问题:就是问服务器资源到底变化了没有!

之所以要发两个信息,是为了兼容不同的服务器,因为有些服务器只认If-Modified-Since,有些服务器只认If-None-Match(优先级更高),有些服务器两个都认。

服务器可能会产生两个情况:

  • 缓存已经失效
  • 缓存仍然有效

如果是第一种情况一一缓存已经失效,那么非常简单,服务器再次给予一个正常的响应(响应码 200 带响应体),同时可以附带上新的缓存指令,这就回到了上一节一一来自服务器的缓存指令。

这样一来,客户端就会重新缓存新的内容。

但如果服务器觉得缓存仍然有效,它可以通过一种极其简单的方式告诉客户端:

  • 响应码为 304 Not Modified (未修改)
  • 无响应体
  • 响应头带上新的缓存指令,见上一节一一来自服务器的缓存指令

这样一来,就相当于告诉客户端:「你的缓存资源仍然可用,我给你一个新的缓存时间,你那边更新一下就可以了」

于是,客户端就继续使用缓存了。

这样一来,可以最大程度的减少网络传输,因为如果资源还有效,服务器就不会传输消息体。

强缓存和协商缓存

http 缓存分为强缓存和协商缓存。他们用于优化网站性能并减少服务器负载。这两种缓存都通过 HTTP 响应头来控制,它们分别基于不同的缓存验证方式,可以根据资源的特性和需求来选择合适的缓存策略。

客户端

<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<button id="btn">send</button>
<script>
const btn = document.querySelector('#btn')
btn.addEventListener('click', ()=> {
    fetch('http://localhost:3000/api3')
})
</script>
</body>
</html>

在这里插入图片描述

在这里插入图片描述

1. 强缓存(Strong Cache)

  • 客户端在请求资源时,会检查缓存的相关响应头(如使用Cache-ControlExpires来控制缓存在本地的有效期),如果资源的缓存尚未过期,客户端将直接从本地缓存中获取资源,而不会发送请求到服务器。
  • 常用的强缓存响应头包括:
    • Cache-Control: max-age=<seconds>:指定资源的缓存时间,单位为秒。HTTP1.1 提出,优先级更高,请求头和响应头都支持这个属性,通过它提供的不同的值来定义缓存策略。此外,Cache-Control 还支持其他的属性字段,如 no-cache 、no-store、public、private 等。
    • Expires: <date>:指定资源的过期时间,是一个日期字符串,表示绝对时间,由服务器返回。它是一个 HTTP 1.0 的头部字段。受限于本地时间,修改了本地时间,可能会导致缓存失效。如果在Expires之内,则浏览器会直接读取缓存,不再请求服务器。
  • 从浏览器读取缓存分为内存缓存(memory cache,浏览器内存,关闭页面后会被清除,通常用于在同一个页面会话中的快速重复请求)和硬盘缓存(disk cache,计算机硬盘,空间大,读取效率低,持久化存储资源,直到缓存策略决定缓存到期或被手动清除),而这两种缓存策略由浏览器自身分配。(此外, 还有 Service Worker 缓存:应用程序控制的缓存层,通过 Service Worker 脚本精确控制缓存和资源请求逻辑)
  • 状态码为 200
import express from 'express'
import cors from 'cors'
import fs from 'node:fs'
import crypto from 'node:crypto'

const app = express()
app.use(cors())

//静态资源缓存 html, css, js, png
// app.use(express.static('./static', {
//     maxAge: 100 * 60 * 5,
//     lastModified: true
// }))

//动态资源缓存
//Expires
app.get('/api', (req, res) => {
    // 到了过期时间之后将不再进行缓存
    res.setHeader('Expires', new Date('2024-4-6 8:57:00').toUTCString())
    res.send('Expires1111')
})
// Cache-Control
// public 任何服服务器都可以缓存包括代理服务器 cdn
// private 只能浏览器缓存 不包括代理服务器
// max-age 缓存的时间
app.get('/api2', (req, res) => {
    res.setHeader('Cache-Control', 'public, max-age=10')
    res.send('Cache-Control1111')
})

app.listen(3000, () => {
    console.log('3000端口已启用')
})

2. 协商缓存(Conditional Cache)

当浏览器对某个资源的请求没有命中强缓存,就会发一个请求(包含If-Modified-SinceIf-None-Match的请求头)到服务器,验证协商缓存是否命中,如果协商缓存命中,请求响应返回的HTTP状态为304 (Not Modified),该请求不携带实体数据,若未命中,则返回200并携带资源实体数据。协商缓存是利用的是Last-Modified,If-Modified-Since和ETag、If-None-Match这两对Header来管理的。

  • 常用的协商缓存响应头包括:
    • Last-Modified:指定资源的最后修改时间。HTTP1.0 提出。浏览器会在请求头加上If-Modified-Since即上次响应的Last-Modified的值,询问服务器在该日期后资源是否有更新,有更新的话就会将新的资源发送回来,但是如果在本地打开缓存文件,就会造成Last-Modified被修改,所以在HTTP 1.1出现了ETag。
    • ETag:指定资源的实体标签(根据内容生成一个哈希),是一个唯一标识符。HTTP1.1 提出,只有资源变化才会被修改(跟最后修改时间没有关系)。If-None-Match的请求头字段会将上次返回的Etag发送给服务器,询问该资源的Etag是否有更新,有变动就会发送新的资源回来。ETag的优先级比Last-Modified更高。
  • 强缓存和协商缓存同时出现,默认强缓存的优先级更高。
  • Last-Modified 和 ETag 的区别:Last-Modified精度只能到秒,但是性能更好。ETag更精细,但是性能不如前者,因为文件变化都要重新计算hash值。
import express from 'express'
import cors from 'cors'
import fs from 'node:fs'
import crypto from 'node:crypto'

const app = express()
app.use(cors())

//获取文件的最后修改时间
const getFileModifyTime = ()=> {
    return fs.statSync('./index.js').mtime.toISOString()
}
app.get('/api3', (req,res)=> {
    // no-cache 使用协商缓存而不是强缓存
    // no-store 不走任何缓存
    res.setHeader('Cache-Control' ,'no-cache')
    const modifyTime = getFileModifyTime();
    // 浏览器根据 Last-Modified 的值自动设置 if-modified-since 的值(与上一次 Last-Modified 的值相同)
    const ifModifiedSince = req.headers['if-modified-since']
    // 如果文件不修改则不用重新缓存
    if (ifModifiedSince === modifyTime) {
        console.log('缓存了')
        res.statusCode = 304
        res.end()
        return
    }
    console.log('没有缓存')
    res.setHeader('Last-Modified', modifyTime)
    res.send('Last-Modified111')
})

app.listen(3000, () => {
    console.log('3000端口已启用')
})
import express from 'express'
import cors from 'cors'
import fs from 'node:fs'
import crypto from 'node:crypto'

const app = express()
app.use(cors())

//获取文件内容哈希
const getFileHash = ()=> {
    return crypto.createHash('sha256').update(fs.readFileSync('index.js')).digest('hex')
}
app.get('/api3', (req,res)=> {
    // no-cache 使用协商缓存而不是强缓存
    // no-store 不走任何缓存
    res.setHeader('Cache-Control' ,'no-cache')
    const fileHash = getFileHash();
    const ifNoneMatch = req.headers['if-none-match']
    // 如果文件不修改则不用重新缓存
    if (ifNoneMatch === fileHash) {
        console.log('缓存了')
        res.statusCode = 304
        res.end()
        return
    }
    console.log('没有缓存')
    res.setHeader('ETag', fileHash)
    res.send('Etag111')
})

app.listen(3000, () => {
    console.log('3000端口已启用')
})

强缓存和协商缓存可以结合使用,以提高缓存效果。通常情况下,可以首先使用强缓存来尽可能减少对服务器的请求,如果资源已经过期或者需要重新验证,则使用协商缓存来验证缓存的有效性。这样可以在保证性能的同时,确保客户端始终能够获取到最新的资源。

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

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

相关文章

产品原型设计

&#x1f923;&#x1f923;目录&#x1f923;&#x1f923; 一、Axure原型设计&#xff08;Axure RP 9 &#xff09;1.1 软件下载安装1.2 产品原型展示1.3 产品原型下载1.4 视频课程推荐 二、磨刀原型设计2.1 软件下载安装2.2 产品原型展示2.3 产品原型下载2.4 视频课程推荐 什…

力扣-数据结构-7【算法学习day.78】

前言 ###我做这类文章一个重要的目的还是给正在学习的大家提供方向&#xff08;例如想要掌握基础用法&#xff0c;该刷哪些题&#xff1f;建议灵神的题单和代码随想录&#xff09;和记录自己的学习过程&#xff0c;我的解析也不会做的非常详细&#xff0c;只会提供思路和一些关…

【文献精读笔记】Explainability for Large Language Models: A Survey (大语言模型的可解释性综述)(一)

****非斜体正文为原文献内容&#xff08;也包含笔者的补充&#xff09;&#xff0c;灰色块中是对文章细节的进一步详细解释&#xff01; 三、传统微调范式&#xff08;Traditional Fine-Tuning Paradigm&#xff09; 在这个范式中&#xff0c;首先在大量未标记的文本数据上预…

基于springboot的膳食问答系统的设计与实现

摘 要 本文介绍了一个基于SpringBoot框架的膳食问答系统&#xff0c;该系统融合了文章查看、膳食问答、用户管理、文章管理、知识点管理、系统日志查看、在线用户查看以及办公管理等多项功能。系统采用主流界面设计风格&#xff0c;前端使用HTML构建用户界面&#xff0c;后端则…

LeetCode - Google 校招100题 第7天 序列(数据结构贪心) (15题)

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/144744418 相关文章&#xff1a; LeetCode 合计最常见的 112 题&#xff1a; 校招100题 第1天 链表(List) (19题)校招100题 第2天 树(Tree) (21…

《Java核心技术 卷II》流的创建

流的创建 Collection接口中stream方法可以将任何集合转换为一个流。 用静态Stream.of转化成数组。 Stream words Stream.of(contents.split("\\PL")); of方法具有可变长参数&#xff0c;可以构建具有任意数量的流。 使用Array.stream(array,from,to)可以用数组…

应用层协议(Https)(超详解)

前言&#xff1a; https是在http基础上的进行一些"加密"操作&#xff0c;也可以认为是http的强化版。 在下面展开对https的讨论中&#xff0c;可能不会再涉及到http的相关协议&#xff0c;如有对http的疑惑或是其他不一样的看法可以浏览上一篇文章&#xff1a;应用层…

ESP32 I2S音频总线学习笔记(一):初识I2S通信与配置基础

文章目录 简介为什么需要I2S&#xff1f;关于音频信号采样率分辨率音频声道 怎样使用I2S传输音频&#xff1f;位时钟BCLK字时钟WS串行数据SD I2S传输模型I2S通信格式I2S格式左对齐格式右对齐格式 i2s基本配置i2s 底层API加载I2S驱动设置I2S使用的引脚I2S读取数据I2S发送数据卸载…

优化租赁小程序提升服务效率与用户体验的策略与实践

内容概要 在这个快速发展的商业环境中&#xff0c;租赁小程序成为了提升服务效率和用户体验的重要工具。通过对用户需求的深入挖掘&#xff0c;我们发现他们对于功能的便捷性、响应速度和界面的友好性有着极高的期待。因此&#xff0c;针对这些需求&#xff0c;完善租赁小程序…

HTML——13.超链接

<!DOCTYPE html> <html><head><meta charset"UTF-8"><title>超链接</title></head><body><!--超链接:从一个网页链接到另一个网页--><!--语法&#xff1a;<a href"淘宝网链接的地址"> 淘宝…

day-102 二进制矩阵中的最短路径

思路 BFS 解题过程 从起点依次向八个方向尝试&#xff08;之后也一样&#xff09;&#xff0c;如果某个位置在矩阵内且值为0且没有访问过&#xff0c;将其添加到一个队列中&#xff0c;依次类推&#xff0c;直到到达出口 Code class Solution {public int shortestPathBinar…

王佩丰24节Excel学习笔记——第十八讲:Lookup和数组

【以 Excel2010 系列学习&#xff0c;用 Office LTSC 专业增强版 2021 实践】 【本章技巧】 地址栏公式可以使用 F9 查看&#xff0c;取消请按Esc键&#xff0c;或者公式前的红色叉&#xff1b;使用数组时一定要注意使用绝对引用&#xff0c;方便下拉&#xff1b;使用数组时一…

Java - 日志体系_Simple Logging Facade for Java (SLF4J)日志门面_SLF4J集成Log4j1.x 及 原理分析

文章目录 Pre官网集成Log4j1.x步骤POM依赖使用第一步&#xff1a;编写 Log4j 配置文件第二步&#xff1a;代码 原理分析1. 获取对应的 ILoggerFactory2. 根据 ILoggerFactory 获取 Logger 实例3. 日志记录过程 小结 Pre Java - 日志体系_Apache Commons Logging&#xff08;JC…

嵌入式开发中的机器人表情绘制

机器人的表情有两种&#xff0c;一种是贴图&#xff0c;一钟是调用图形API自绘。 贴图效果相对比较好&#xff0c;在存储空间大的情况下是可以采用的。 自绘比较麻烦&#xff0c;但在资源和空缺少的情况下&#xff0c;也是很有用的。而且自绘很容易通过调整参数加入随机效果&…

LLM高性能并行训练技术

LLM高性能并行训练技术 研究背景与意义 深度学习的重要性:人工智能成为国际竞争焦点,深度学习是其核心技术,在众多领域取得突破,推动社会向智能化跃升。面临的挑战:数据、模型规模呈指数级增长,硬件算力发展滞后。单个 GPU 难以满足大规模模型训练需求,分布式训练面临通…

Docker镜像瘦身:从1.43G到22.4MB

Docker镜像瘦身:从1.43G到22.4MB 背景1、创建项目2、构建第一个镜像3、修改基础镜像4、多级构建5、使用Nginx背景 在使用 Docker 时,镜像大小至关重要。我们从 create-react-app (https://reactjs.org/docs/create-a-new-react-app.html)获得的样板项目通常都超过 1.43 GB…

【电路理论四】正弦电流电路

正弦电流 正弦量是随时间按正弦规律变动的电路变量。 随时间按正弦规律变动的电流称为正弦电流。 正弦电流的瞬时值表达式&#xff1a; 称为正弦电流的三要素。 分别为振幅/幅值&#xff0c;角频率&#xff0c;初相。 幅值为正弦电流的最大值&#xff0c;恒为正。 为正弦电…

深度学习使用Anaconda打开Jupyter Notebook编码

新手入门深度学习使用Anaconda打开Jupyter Notebook编码 1. 安装Anaconda 第一种是Anaconda官网下载安装包&#xff0c;但是很慢&#xff0c;不太建议 第二种使用国内清华大学镜像源下载 选择适合自己电脑的版本&#xff0c;支持windows&#xff0c;linux系统 下载完之后自行…

【MySQL】搞懂mvcc、read view:MySQL事务原理深度剖析

前言&#xff1a;本节内容是事务里面最难的一部分&#xff0c; 就是理解mvcc快照读和read view。这两个部分需要了解隔离性里面的四种隔离级别。 博主之前讲过&#xff0c;但是担心友友们不了解&#xff0c; 所以这里开头进行了复习。 下面开始我们的学习吧&#xff01; ps&…

VITUREMEIG | AR眼镜 算力增程

根据IDC发布的《2024年第三季度美国AR/VR市场报告》显示&#xff0c;美国市场AR/VR总出货量增长10.3%。其中&#xff0c;成立于2021年的VITURE增长速度令人惊艳&#xff0c;同比暴涨452.6%&#xff0c;成为历史上增长最快的AR/VR品牌。并在美国AR领域占据了超过50%的市场份额&a…