当前位置: 首页 > news >正文

【前端】深入浅出缓存原理

缓存的基本原理

对于前端来说,缓存主要分为浏览器缓存(比如 localStorage、sessionStorage、cookie等等)以及http缓存,也是本文主要讲述的。

当然叫法也不一样,比如客户端缓存大概包括浏览器缓存和http缓存

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

http缓存的原理如下:

这只是一个简易的原理图,实际情况可能有差异

这里就设计到一个缓存策略的问题,这些问题包括:

  • 哪些资源需要加入到缓存,哪些不需要?
  • 缓存的时间是多久呢?
  • 如果服务器的资源有改动,客户端如何更新缓存呢?
  • 如果缓存过期了,可是服务器上的资源并没有发生变动,又该如何处理呢?

要回答这些问题,就必须要清楚http中关于缓存的协议

理解了http的缓存协议,自然就能回答上面的问题了。

来自服务器的缓存指令

当客户端发出一个get请求到服务器,服务器可能有以下的内心活动:「你请求的这个资源,我很少会改动它,干脆你把它缓存起来吧,以后就不要来烦我了」

为了表达这个美好的愿望,服务器在响应头中加入了以下内容:

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

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

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

这个美好的缓存愿望,就这样通过响应头传递给客户端了

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

但是凑巧客户端是一个浏览器,它和服务器一直以来都是相亲相爱的小伙伴,当它看到服务器的这个响应头表达的美好愿望后,立即忙起来:

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

这一次的记录非常重要,它为以后浏览器要不要去请求服务器提供了各种依据。

来自客户端的缓存指令

当客户端收拾好行李,准备再次请求GET /index.js时,它突然想起了一件事:我需要的东西在不在缓存里呢?

此时,客户端会到缓存中去寻找是否有缓存的资源

寻找的过程如下:

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

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

要验证是否有匹配的缓存非常简单,只需要验证当前的请求方法GET和当前的请求路径/index.js是否有对应的缓存存在即可

如果没有,就直接请求服务器,就和第一次请求服务器时一样,这种情况没有什么好讨论的

关键在于验证缓存是否有效

如何验证呢?

非常简单,就是把max-age + Date,得到一个过期时间,看看这个过期时间是否大于当前时间,如果是,则表示缓存还没有过期,仍然有效,如果不是,则表示缓存失效。

缓存有效

当浏览器发现缓存有效时,完全不会请求服务器,直接使用缓存即可得到结果

此时,如果你断开网络,会发现资源仍然可用

这种情况会极大的降低服务器压力,但当服务器更改了资源后,浏览器是不知道的,只要缓存有效,它就会直接使用缓存

缓存无效

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

于是,浏览器向服务器发出了一个带缓存的请求

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

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

它们表达了下面的信息:

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

其实,这两个问题可以合并为一个问题:快说!资源到底变了没有!

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

目前的很多服务器,只要发现If-None-Match存在,就不会去看``If-Modified-Since`

If-Modified-Sincehttp1.0版本的规范,If-None-Matchhttp1.1的规范

此时,问题又抛给了服务器,接下来,就是服务器的表演时间了

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

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

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

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

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

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

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

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

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

它们完整的交互过程如下:

细节

上面描述了客户端缓存的基本概念和过程

但其中仍然有不少细节值得我们注意

Cache-Control

在上述的讲解中,Cache-Control是服务器向客户端响应的一个消息头,它提供了一个max-age用于指定缓存时间。

实际上,Cache-Control还可以设置下面一个或多个值:

  • public:指示服务器资源是公开的。比如有一个页面资源,所有人看到的都是一样的。这个值对于浏览器而言没有什么意义,但可能在某些场景可能有用。本着「我告知,你随意」的原则,http协议中很多时候都是客户端或服务器告诉另一端详细的信息,至于另一端用不用,完全看它自己。
  • private:指示服务器资源是私有的。比如有一个页面资源,每个用户看到的都不一样。这个值对于浏览器而言没有什么意义,但可能在某些场景可能有用。本着「我告知,你随意」的原则,http协议中很多时候都是客户端或服务器告诉另一端详细的信息,至于另一端用不用,完全看它自己。
  • no-cache:告知客户端,你可以缓存这个资源,但是不要直接使用它。当你缓存之后,后续的每一次请求都需要附带缓存指令,让服务器告诉你这个资源有没有过期。见:「来自客户端的缓存指令 - 缓存无效」
  • no-store:告知客户端,不要对这个资源做任何的缓存,之后的每一次请求都按照正常的普通请求进行。若设置了这个值,浏览器将不会对该资源做出任何的缓存处理。
  • max-age:不再赘述

比如,Cache-Control: public, max-age=3600表示这是一个公开资源,请缓存1个小时。

Expire

http1.0版本中,是通过Expire响应头来指定过期时间点的,例如:

Expire: Thu, 30 Apr 2020 23:38:38 GMT

到了http1.1版本,已更改为通过Cache-Controlmax-age来记录了。

记录缓存时的有效期

浏览器会按照服务器响应头的要求,自动记录缓存到本地文件,并设置各种相关信息

在这些信息中,有效期尤为关键,它决定了这个缓存可以使用多久

浏览器会根据服务器不同的响应情况,设置不同的有效期

具体的有效期设置,按照下面的流程进行:

例如,当max-age设置为0时,缓存立即过期

虽然立即过期,但缓存仍然被记录下来,后续的请求通过缓存指令发送到服务器,来确认资源是否被更改。

因此,Cache-Control: max-age=0类似于Cache-Control: no-cache

Pragma

这是http1.0版本的消息头

当该消息头出现在请求中时,是向服务器表达:不要考虑任何缓存,给我一个正常的结果。

http1.1版本中,可以在请求头中加入Cache-Control: no-cache实现同样的含义。

是的,Cache-Control可以出现在请求头中

Chrome浏览器中调试时,如果勾选了Disable cache,则发送的请求中会附带该信息

image-20200501080330131

Vary

有的时候,是否有缓存,不仅仅是判断请求方法和请求路径是否匹配,可能还要判断头部信息是否匹配。

此时,就可以使用Vary字段来指定要区分的消息头

比如,当使用GET /personal.html请求服务器时,请求头中cookie的值不一样,得到的页面也不一样

如果还按照之前的做法,仅仅匹配请求方法和请求路径,如果cookie变动,你可能得到的仍然是之前的页面。

正确的做法如下:

使用版本号或hash

如果你是一个前端工程师,使用过vue或其他基于webpack搭建的工程

你会发现打包的结果中很多文件名类似于这样:

app.68297cd8.css

文件的中间部分使用了hash

这样做的好处是,可以让客户端大胆的、长时间的缓存该文件,减轻服务器的压力

当文件改动后,它的文件hash值也会随之而变,比如变成了app.446fccb8.css

这样一来,客户端要请求新的文件时,就会发现路径从/app.68297cd8.css变成了app.446fccb8.css,由于之前的缓存路径无法匹配到,因此就会发送新的请求来获取新资源了。

以上是现代流行的做法。

而在古老的年代,还没有构建工具出现时,人们使用的办法是在资源路径后面加入版本号来获取新版本的文件

比如,页面中引入了一个css资源app.css,它可能的引入方式是:

<link href="/app.css?v=1.0.0">

这样一来,缓存的路径是/app.css?v=1.0.0

当服务器的版本发生变化时,可以给予新的版本号,让html中的路径发生变动

<link href="/app.css?v=1.0.1">

由于新的路径无法命中缓存,于是浏览器就会发送新的普通请求来获取这个资源

总结

最后,通过客户端和服务器两位大佬的视角,来总结一下以上内容

服务器视角

服务器无法知道客户端到底有没有像浏览器那样缓存文件,它只管根据请求的情况来决定如何响应

image-20200501083702987

很多后端语言搭建的服务器都会自带自己的默认缓存规则,当然也支持不同程度的修改

浏览器视角

浏览器在发出请求时会判断要不要使用缓存

当收到服务器响应时,会自动根据缓存指令进行处理

相关文章:

【前端】深入浅出缓存原理

缓存的基本原理 对于前端来说&#xff0c;缓存主要分为浏览器缓存&#xff08;比如 localStorage、sessionStorage、cookie等等&#xff09;以及http缓存&#xff0c;也是本文主要讲述的。 当然叫法也不一样&#xff0c;比如客户端缓存大概包括浏览器缓存和http缓存 所谓htt…...

单调栈图文详解(附Java模板)

&#x1f34f;&#x1f350;&#x1f34a;&#x1f351;&#x1f352;&#x1f353;&#x1fad0;&#x1f951;&#x1f34b;&#x1f349;&#x1f95d; 啥是"单调栈"&#xff0c;它能解决什么样的问题&#xff1f; 文章目录&#x1f9a9;单调栈的概念&a…...

彻底理解Session、Cookie、Token,入门及实战

文章目录Session Cookie的使用Token的使用Session Cookie的使用 1. Session存储数据 HttpSession session request.getSession(); //Servlet底层通过的SESSIONID&#xff0c;获取Session对象。 session.setAttribute("loginTime",new Date()); out.println(&q…...

为什么运营商大数据可以精准获客?

“获客难”&#xff0c;“获客成本高”&#xff0c;一直是困扰企业的大问题&#xff0c;身边的许多朋友在吐槽客户的意向度不高&#xff0c;总是无法成交&#xff0c;员工非常积极主动去跟踪客户了&#xff0c;但始终事倍功半&#xff0c;这就像是老人们常说的一句老话“热脸贴…...

【数据结构】栈的实现

&#x1f4af;&#x1f4af;&#x1f4af; 本篇主要利用数组来实现栈&#xff0c;对于栈的各种操作都作详细介绍&#xff0c;压栈&#xff0c;出栈以及获取栈中元素的操作都是学习栈的必备知识&#xff0c;快来学起来吧&#xff01;&#xff01;&#xff01;©Ⅰ.栈的概念及…...

【链表OJ题(六)】链表分割

​ ​&#x1f4dd;个人主页&#xff1a;Sherry的成长之路 &#x1f3e0;学习社区&#xff1a;Sherry的成长之路&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;数据结构 &#x1f3af;长路漫漫浩浩&#xff0c;万事皆有期待 文章目录链表OJ题(六)1. 链表…...

C++类中的三大函数(构造,析构,拷贝)

下面一段话与大家共勉&#xff1a;每个人的一生都会遇到很多边界&#xff0c;有些边界可以突破&#xff0c;有些则不能。那些无法突破的边界就是你的极限&#xff0c;而划分边界的标准就是“阈值”。每次突破阈值之后&#xff0c;人生轨迹就会发生剧烈变化&#xff0c;其间需要…...

【2024考研】计算机考研,4轮复习时间安排

文章目录&#x1f3a8;第1轮复习&#xff08;暑假前&系统课&#xff09;英语1/2数学1/2专业课408&#x1f3a8;第2轮复习&#xff08;开学前&真题&#xff09;英语1/2试卷数学1/2试卷专业课408试卷&#x1f3a8;第3轮复习&#xff08;报名前&政治&#xff09;政治试…...

(十二)python网络爬虫(理论+实战)——实战:使用BeautfulSoup解析baidu热搜新闻数据

系列文章: python网络爬虫专栏 目录 序言 本节学习目标 特别申明 4.7 使用BeautfulSoup解析h...

【经验】项目管理:瀑布式、Scrum

1、瀑布式开发 流程关键词关键人员输出立项简述、周期、预算领导立项申请表、立项评审表策划计划项目经理、QA、CM各种计划书&#xff08;项目、配置、测试等&#xff09;&#xff0c;评审需求功能项目经理功能列表、需求规格书、需求开发计划等&#xff0c;评审设计UML开发设…...

Learning C++ No.17【STL No.7】双端队列

引言&#xff1a; 北京时间&#xff1a;2023/3/17/7:18&#xff0c;刚刚快乐的早锻炼回来&#xff08;不对 &#xff0c;应该说回来有一会了&#xff09;&#xff0c;因为此时我已经吃完早饭&#xff0c;洗过澡了&#xff1b;现在回想起上学期&#xff0c;就算是第二天需要晨跑…...

Snackbar

1.简介 位于底部的提示View 支持侧滑消失 同一时间只有一个 不支持跨Activity展示 国内使用率很低 2.基础使用 2.1 基本展示 Snackbar.make(view, "Content", Snackbar.LENGTH_LONG).show()2.2 设置点击事件 注意不设置点击事件回调&#xff0c;点击按钮的文字不…...

HummerRisk 使用教程:主机检测

1. 概述 HummerRisk 是开源的云原生安全平台&#xff0c;以非侵入的方式解决云原生环境的安全和治理问题。核心能力包括混合云的安全治理和容器云安全检测。 本文将介绍HummerRisk中的主机检测部分功能&#xff0c;包括如何管理主机、管理凭证&#xff0c;以及使用主机检测规…...

【Arduino无线气象站项目】

【Arduino无线气象站项目】 1. 概述2. Arduino无线气象站电路图3. 定制设计电路板4. Arduino无线气象站代码5. 总结1. 概述 使用DHT22传感器测量室外温度和湿度,并使用NRF24L01收发器模块将这些数据无线发送到室内机。在室内机,还有另一个用于测量室内温度和湿度的DHT22传感…...

HTTP详解

一&#xff0c;什么是HTTPHTTP(全称为“超文本传输协议”)&#xff0c;是一种应用非常广泛的应用层协议&#xff0c;之前在《初识网络原理》的博客(初识网络原理_徐憨憨&#xff01;的博客-CSDN博客)中&#xff0c;有详细讲解过TCP/IP五层模型&#xff0c;其中应用层描述了数据…...

cpufreq--处理器功耗控制

cpu 功耗控制 参考框架&#xff1a; cpufreq 框架。 cpufreq 框架提供 cpu 功耗管理接口&#xff0c;以及功耗管理方案。 用户可以通过功耗管理接口&#xff08;以文件形式提供&#xff09;来选择管理方案&#xff0c;并设置相关参数。 管理方案的实现则由具体的驱动来完成。…...

做技术,最忌讳东张西望

又好长时间没更新&#xff0c;研二了&#xff0c;忙着做实验、写论文、发论文&#xff0c;再加上给我导做一些事情&#xff08;都习惯了&#xff0c;以前很不爽的事情&#xff0c;现在居然能这么平静的说出来&#xff09;。 但这不是我今天说的重点&#xff0c;而是另外一件事…...

Oracle 常见报错问题汇总

Oracle 常见报错问题汇总 报错:ORA-01017: invalid username/password; logon denied报错:ORA-01031: insufficient privileges报错:"ORA-01034: ORACLE not available" 和 "ORA-27101: shared memory realm does not exist"报错:“ORA-00119: invalid…...

单片机连接有人云上传数据

首先采用有人物联网的模块 &#xff0c;连接有人云平台服务器 看云平台相关配置配置连接设备在线后 添加设备添加设备完成后 添加变量模板 变量模板的添加方式如下 &#xff1a;本次采用的是标准的MODbus 协议添加一个温度变量温度变量如下显示云平台 下发数据 采集01 03 00 00…...

系统集成项目管理工程师:第18章项目风险管理学习笔记

第18章项目风险管理 一、目录 18.1 风险概述 18.1.1 风险的定义 18.1.2 风险的分类 18.1.3 风险的性质 18.2 项目风险管理 18.3 规划风险管理 18.3.1 规划风险管理的输入 18.3.2 规划风险管理的工具与技术 18.3.3 规划风险管理的输出 18.4 识别风险...

网络编程(Modbus进阶)

思维导图 Modbus RTU&#xff08;先学一点理论&#xff09; 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议&#xff0c;由 Modicon 公司&#xff08;现施耐德电气&#xff09;于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…...

【根据当天日期输出明天的日期(需对闰年做判定)。】2022-5-15

缘由根据当天日期输出明天的日期(需对闰年做判定)。日期类型结构体如下&#xff1a; struct data{ int year; int month; int day;};-编程语言-CSDN问答 struct mdata{ int year; int month; int day; }mdata; int 天数(int year, int month) {switch (month){case 1: case 3:…...

ES6从入门到精通:前言

ES6简介 ES6&#xff08;ECMAScript 2015&#xff09;是JavaScript语言的重大更新&#xff0c;引入了许多新特性&#xff0c;包括语法糖、新数据类型、模块化支持等&#xff0c;显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var&#xf…...

IGP(Interior Gateway Protocol,内部网关协议)

IGP&#xff08;Interior Gateway Protocol&#xff0c;内部网关协议&#xff09; 是一种用于在一个自治系统&#xff08;AS&#xff09;内部传递路由信息的路由协议&#xff0c;主要用于在一个组织或机构的内部网络中决定数据包的最佳路径。与用于自治系统之间通信的 EGP&…...

java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别

UnsatisfiedLinkError 在对接硬件设备中&#xff0c;我们会遇到使用 java 调用 dll文件 的情况&#xff0c;此时大概率出现UnsatisfiedLinkError链接错误&#xff0c;原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用&#xff0c;结果 dll 未实现 JNI 协…...

如何将联系人从 iPhone 转移到 Android

从 iPhone 换到 Android 手机时&#xff0c;你可能需要保留重要的数据&#xff0c;例如通讯录。好在&#xff0c;将通讯录从 iPhone 转移到 Android 手机非常简单&#xff0c;你可以从本文中学习 6 种可靠的方法&#xff0c;确保随时保持连接&#xff0c;不错过任何信息。 第 1…...

QT: `long long` 类型转换为 `QString` 2025.6.5

在 Qt 中&#xff0c;将 long long 类型转换为 QString 可以通过以下两种常用方法实现&#xff1a; 方法 1&#xff1a;使用 QString::number() 直接调用 QString 的静态方法 number()&#xff0c;将数值转换为字符串&#xff1a; long long value 1234567890123456789LL; …...

C++八股 —— 单例模式

文章目录 1. 基本概念2. 设计要点3. 实现方式4. 详解懒汉模式 1. 基本概念 线程安全&#xff08;Thread Safety&#xff09; 线程安全是指在多线程环境下&#xff0c;某个函数、类或代码片段能够被多个线程同时调用时&#xff0c;仍能保证数据的一致性和逻辑的正确性&#xf…...

七、数据库的完整性

七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...

GitFlow 工作模式(详解)

今天再学项目的过程中遇到使用gitflow模式管理代码&#xff0c;因此进行学习并且发布关于gitflow的一些思考 Git与GitFlow模式 我们在写代码的时候通常会进行网上保存&#xff0c;无论是github还是gittee&#xff0c;都是一种基于git去保存代码的形式&#xff0c;这样保存代码…...