浏览器缓存控制讲解

缓存的作用

在你访问互联网中的任何资源其所产生的任何链路中的每一个节点几乎都会进行缓存,整个缓存体系和细节十分复杂。比如浏览器缓存,服务器缓存,代理服务器缓存,CDN缓存等。

但是缓存又十分重要,不可缺少,为什么这么说呢?

由于HTTP请求的链路漫长,环境复杂,把一些必要的信息在关键的节点进行缓存,下次请求的时候可以尽可能的复用数据,就可以节省一部分资源的消耗,减少HTTP请求应答的成本,节约带宽,加快响应速度。

那么,基于请求-应答模式的特点,缓存大致可以分为服务器缓存和客户端缓存,而服务器缓存经常与代理服务关联在一起。这次我们主要关注浏览器是如何进行缓存的。

缓存的形式

Memory Cache(内存缓存)

  • 时间间隔较短时, 重复请求相同的静态资源时, 一般情况下资源还在内存中, 那直接从内存中获取资源, 这种方式是最快的资源获取方式。
  • 通常情况下, 关闭 tab 页签时, 会清除内存中的资源。

Disk Cache(硬盘缓存)

  • 把资源存储到本机的硬盘上, 当再次需要数据时, 可以直接从硬盘上获取。
  • 缓存的时间一般会根据响应头的设置而定。

CDN 缓存

  • 当用户请求资源时会经过 CDN 服务器, 服务器会对可以缓存的资源进行缓存。
  • 当有另外的用户也需要这个资源时, CDN 服务器就可以直接返回资源, 不需要再找原服务器要了。

服务端缓存

  • 当一些数据需要服务端进行大量计算才能获得时, 服务端为了避免重复的处理影响服务器性能, 开发人员会对处理过的数据进行缓存, 下次再需要时直接取缓存中的数据。
  • 服务端缓存一般是一些接口的缓存处理,缓存时效可能根据数据的性质不同而不同。

缓存控制

假设,现在没有缓存,我们想象一下获取资源的方式是什么样的?

客户端请求资源,服务器返回资源,等下一次想要获取同样资源的时候,哪怕服务器的资源并没有更新,还是要重新走一遍网络请求,然后服务器返回资源的完整链路。

那如果有了缓存,在客户端第一次获取到请求的资源后,把资源缓存到本地,下次再去请求的时候,发现本地有这个资源,直接拿来用就好了,完全不用去走网络请求。

HTTP缓存都是从第二次请求开始的,可以概述如下:

  • 第一次请求资源时:

    服务器返回资源,并在 response header(响应头) 中回传资源的缓存策略;

  • 第二次请求时:

    浏览器判断这些请求参数,击中强缓存就直接200

    否则就把请求参数加到request header(请求头)中传给服务器,看是否击中协商缓存,击中则返回304,否则服务器会返回新的资源。

我们仔细的阅读一下这个简单的缓存资源请求流程,发现其中有几个重要的节点。

首先,服务器在返回该资源时,要标记该资源的有效期。然后,浏览器初次请求肯定是没缓存的,再次请求的时候,它要根据该资源的有效期来判断下一步该怎么办。

我们再简单一点,如果我们试图去获取缓存资源,其实是要看服务器的标记的。那么换句话说,服务器标记缓存资源,浏览器会验证该缓存资源的标记

浏览器每次发起请求时,会先在浏览器缓存中查找该请求的结果以及缓存标识,根据缓存标识来判断是否使用本地缓存。如果缓存有效,则使用本地缓存;否则,则向服务器发起请求并携带缓存标识。

根据是否需向服务器发起请求,将缓存过程划分为两个部分:强制缓存协商缓存,强缓存优先于协商缓存。

浏览器缓存控制机制有两种:HTML Meta 标签 和 HTTP 头信息

使用HTML Meta 标签

HTML Meta标签是应用在HTML文件中的head头部分。

<meta http-equiv="Cache-Control" content="max-age=7200" />
// or 
<meta http-equiv="Cache-Control" content="no-cache" />

主要作用就是告诉浏览器此HTML页面不被缓存,每次访问都去服务器上下载。

使用上很简单,但这个是 IE 时代的私有属性,在 IE9 以前支持的,而现在主流的 Chrome / Firefox / Safari,包括 IE9 ~ IE11 都不支持。而且所有缓存代理服务器都不支持。因为代理不解析HTML内容本身。

所以我们常说的浏览器缓存还是通过HTTP头信息来控制缓存。

http-equiv 倒确实在 HTML 规范中有几个值可以设( content-security-policycontent-type、default-style、x-ua-compatiblerefresh),但都跟缓存无关,可以参阅文档 http-equiv。

使用HTTP头信息控制缓存

使用 HTTP 头信息进行缓存处理一般是通过设置相应的请求头/响应头实现的

常见的处理方式有 4 种

  • Cache-Control
  • Expires
  • Etag / If-None-Match
  • Last-Modified / If-Modified-Since

后3种是 http 1.0 就已经支持的,Cache-Control 是 http 1.1 支持的

缓存优先级: Cache-Control > Expires > Etag > Last-Modified

Cache-ControlExpires 首部用于指定缓存时间,Last-ModifiedETag 首部提供验证机制。

通过浏览器开发者工具我们可以看到,浏览器请求服务器静态资源的响应状态码主要就是下图的四种:

强缓存

通过 Expires(http1.0) 和 Cache-Control(http1.1) 两个响应头字段来控制,如果同时存在,则后者优先级高于前者。

服务器通知浏览器一个缓存时间,在缓存时间内,下次请求直接从本地缓存中读取资源而不发起请求。不在时间内,执行比较缓存策略。

  • 强缓存命中则直接读取浏览器本地的资源,在network中显示的是from memory cache或者from disk cache

  • Cache-Control是一个相对时间,用以表达自上次请求正确的资源之后的多少秒的时间段内缓存有效。

  • Cache-Control的出现是为了解决 Expires 在浏览器时间被手动更改导致缓存判断错误的问题。

Expires 缓存过期时间

Expires是http响应头字段,是一个绝对时间,用以告诉浏览器这个时间点之前发起请求可以直接从浏览器中读取数据,而无需发起请求。

  • Expires 字段为一个日期, 客户端请求该资源时将这个日期与客户端当前日期进行比对。
  • 如果当前时间小于这个日期, 则表示资源未过期, 使用缓存。
  • 如果当前时间大于这个日期, 则表示资源已过期, 客户端就会重新请求该资源。

优势:

  • HTTP 1.0 产物,可以在HTTP 1.0和1.1中使用,简单易用。
  • 以时刻标识失效时间,简易易理解

劣势:

  • 我们依赖客户端时间, 就会因为客户端的时间不准确导致判断错误,致使缓存失效。如果客户端时间早于服务器的时间, 会导致资源还未过期就重新请求。反之, 会导致客户端还在使用过期的旧资源。
Cache-Control 缓存控制

Cache-Control 通用消息头字段,被用于在 http 请求和响应中,通过指定指令来实现缓存机制。

缓存指令是单向的,这意味着在请求中设置的指令,不一定被包含在响应中。

一般用在http响应中进行客户端缓存设置。

Cache-Control 是在 HTTP/1.1 中才有, 算是一个"新"的API (新是相对以前的处理, 因为现在已经有 HTTP3 了)

可选的参数有很多: max-age=no-storeno-cachemust-revalidateprivatepublic

在HTTP/1.1中增加的字段。属于相对时间。该字段表示资源缓存的最大有效时间,在该时间内,客户端不需要向服务器发送请求。比Expires多了很多选项设置。

  1. max-age

    可以用来表示过期时长,单位是秒。表达式是这样子的:

    Cache-Control: max-age=30
    

    该资源的有效期是30秒。

    这是一个相对时间,时间计算的起点是报文创建的时刻,也就是响应头Date字段的时间,是指资源离开服务器的时刻,而不是客户端收到报文的时候。

    换句话说,假设我们设置的时间是5秒,但是链路请求很长,花费了4秒的时间,那么缓存对于浏览器的有效时间只是1秒。

    那你可能会说,就这一秒有啥用啊~~假设你网站的访问量特别大,每秒有上百万的访问,那你可以想象到这仅仅一秒的时间能节省服务器多大的压力了吧。当然,只是举个极限的例子~

    我们先搞个max-age的小例子,看看缓存能否生效:

    res.setHeader("Cache-Control", "max-age=20");
    

    我们可以看到在响应头中返回了我们设置好的缓存字段:

    除了max-age这个最常用的属性以外,还有三个属性可以更精确的指示浏览器如何使用缓存

  2. no-store

    浏览器和代理服务器禁止缓存,每次只能去服务器请求最新资源。用于某些变化非常频繁的页面,比如秒杀页面。

    Cache-Control: no-store
    
  3. no-cache

    它的字面意思和no-store很容易搞混,实际上它的意思并不是不允许缓存,而是可以缓存,只是不使用强缓存,每次使用前必须要去服务器验证是否过期,是否有最新的版本。同时,代理服务器也不能对资源进行缓存。一般用于长期不变的资源,客户端只需要发送很小的信息跟服务端进行确认 (协商缓存验证)。

  4. must-revalidate

    no-cache又很相似,它的意思是缓存不过期就可以继续使用,但是过期了如果还想用就必须去服务器验证一下。

  5. private

    只有浏览器可以缓存

  6. public

    浏览器,服务器,代理服务器都可以缓存

Cache-Control时指定 no-cachemax-age=0, must-revalidate 表示客户端可以缓存资源,但每次使用缓存资源前都必须重新验证其有效性。这意味着每次都会发起 HTTP 请求,但当缓存内容仍有效时可以跳过 HTTP 响应体的下载。

Cache-Control: no-cache
# or
Cache-Control: max-age=0, must-revalidate

优势:

  • HTTP 1.1 产物,以时间间隔标识失效时间,解决了Expires服务器和客户端相对时间的问题。
  • Expires多了很多选项设置。

劣势:

  • 存在版本问题,到期之前的修改客户端是不可知的。
协商缓存

协商缓存有 2 组字段(不是两个),控制协商缓存的字段有:Last-Modified / If-Modified-Since(http1.0)和 Etag / If-None-Match(http1.1)。如果同时存在,则后者优先级高于前者。

Last-Modified / If-Modified-Since

Last-Modified很好理解,就是服务端返回最后一次修改文件的时间。

If-Modified-Since(客服端发起): 本地文件的修改时间(可以理解为上次请求返回的 Last-Modified的值),以后每次请求,请求头都会都带上。

如果强制缓存未命中,但协商缓存可用,需要第一次请求的时候,HTTP响应头中返回Last-ModifiedETag,当第二次请求的时候请求头会自动设置 If-Modified-Since 或者 If-None-Match 的值,服务端根据值,去决策验证是否命中协商缓存。

如果命中了协商缓存,那么服务器仅返回**304(不返回数据实体)**状态码,更新下资源的有效时间,使用本地缓存,因此在响应体体积上的节省是它的优化点。没有命中则返回200状态码。

优势:

  • 不存在版本问题,每次请求都会去服务器进行校验。

劣势:

  • 有新资源服务端却返回304,因为Last-Modified是以秒级为记录的,如果资源在1秒内改变的话,Last-Modified是无感的。
Etag / If-None-Match

ETag实体标签(Entity Tag) 的缩写。请求数据成功后, 服务端返回数据时响应头会携带 Etag,他是根据文件内容生成的一个字符串, 当文件变化时他也会跟着改变,一般都是 hash 生成的,是资源的唯一标识。

If-None-Match 是客户端发起的上次返回的 Etag 值。服务器会将 If-None-Match 的值与自己本地资源的 ETag 的值进行对比。如果相等,则表示未修改,响应 304;反之,则表示修改了,响应 200 状态码,并返回最新数据。整个流程基本上和 (Last-Modified / If-Modified-Since) 很类似。

ETag主要是用来解决修改时间无法准确区分文件变化的问题。

比如,文件的修改时间是秒级甚至更短的,所以一秒内的新版本是无法区分的,再比如,一个文件定期更新,但有时内容没有变化,用修改时间就会以为发生了变化,发送给客户端以为是新的资源,浪费带宽。使用ETag就可以精确的识别资源的变动情况,让浏览器更有效地利用缓存。

ETag还有强弱之分。

ETag 要求资源在字节级别必须完全相符,弱 ETag 在值前有个W/标记,只要求资源在语义上没有变化,但内部可能会有部分发生了改变(例如 HTML 里的标签顺序调整,或者多了几个空格)。至于是强还是弱,其实是由服务器自主决定的。弱也可以强,强也可以弱。

优势:

  • 可以更加精确的判断资源是否被修改,可以识别一秒内多次修改的情况。
  • 不存在版本问题,每次请求都会去服务器进行校验。

劣势:

  • 计算ETag值需要性能损耗。
  • 分布式服务器存储的情况下,计算ETag的算法如果不一样,会导致浏览器从一台服务器上获得页面内容后到另外一台服务器上进行验证时出现ETag不匹配的情况。

浏览器自身的缓存控制

客户端也可以控制缓存,客户端是怎么控制的呢?

当你点击浏览器的刷新按钮的时候,实际上,浏览器就在HTTP请求中夹带了Cache-Control:max-age=0,之前说过,max-age是生存时间,纪录的是从服务器生成的那一刻的有效期,而浏览器本地的资源,肯定不可能是0,所以当浏览器加上了max-age=0的时候,每次都会向服务器请求最新的资源。

而当你使用Control+F5强制刷新的时候其实是浏览器发了一个Cache-Control: no-cache。基本上和max-age=0差不多一样的效果,但是含义肯定是不一样的,就看服务器要怎么理解和处理不同的字段。

那么什么时候缓存才能派上用场呢?当我们点击浏览器的前进后退按钮的时候,就会直接从缓存中获取数据,另外,重定向的时候,也可能会使用到缓存。那这两类操作有啥区别呢。其实本质上来说,就是前进、后退、跳转这样的操作,浏览器不会私自加上Cache-Control字段,并且会清空If-Modified-SinceIf-None-Match字段,所以就会检查缓存,直接利用之前的资源,不再进行网络通信。

大家还可以自己尝试设置no-storeno-cache等字段。当你设置了no-store属性后,你会发现,哪怕使用浏览器的前进,后退按钮,每次也是重新从服务器获取资源,但是no-cachemax-age则会使用缓存。

哪些请求不会被缓存?

  • HTTP 信息头中包含 Cache-Control:no-cache,pragma:no-cache,或 Cache-Control:max-age=0 等告诉浏览器不用缓存的请求。

  • HTTP响应头中不包含Cache-Control/Expires,也不包含Last-Modified/ETag的请求无法被缓存。

  • POST 请求无法被缓存

部署时缓存的问题

我们不仅要缓存代码,还需要更新代码。如果静态资源名字不变,怎么让浏览器既能缓存,又能在有新代码时更新?

最简单的解决方式就是静态资源路径添加一个版本值。

版本不变就走缓存策略,版本变了就加载新资源。如下:

<script src="xx/xx.js?v=24334452"></script>

然而这种处理方式在部署时有问题。

背景:静态资源和页面是分开部署的

无论是先部署页面,还是先部署静态资源,在特定的阶段访问,就会出现页面和js文件不匹配进而报错。

这些问题的本质是以上的部署方式是“覆盖式发布”,解决方式是“非覆盖式发布”。

即用静态资源的文件摘要信息给文件命名,这样每次更新资源不会覆盖原来的资源,先将资源发布上去。

这时候存在两种资源,用户用旧页面访问旧资源,然后再更新页面,用户变成新页面访问新资源,就能做到无缝切换。

简单来说就是给静态文件名加hash值。

那如何实现呢?使用webpack持久化缓存

现在前端代码都用webpack之类的构建工具打包。浏览器有其缓存机制,想要既能缓存又能在部署时没有问题,需要给静态文件名添加hash值。

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

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

相关文章

NX二次开发UF_CURVE_ask_curve_struct_data 函数介绍

文章作者&#xff1a;里海 来源网站&#xff1a;https://blog.csdn.net/WangPaiFeiXingYuan UF_CURVE_ask_curve_struct_data Defined in: uf_curve.h int UF_CURVE_ask_curve_struct_data(UF_CURVE_struct_p_t curve_struct, int * type, double * * curve_data ) overview…

情感对话机器人的任务体系

人类在处理对话中的情感时&#xff0c;需要先根据对话场景中的蛛丝马迹判断出对方的情感&#xff0c;继而根据对话的主题等信息思考自身用什么情感进行回复&#xff0c;最后结合推理出的情感形成恰当的回复。受人类处理情感对话的启发&#xff0c;情感对话机器人需要完成以下几…

从0开始学习JavaScript--深入了解JavaScript框架

JavaScript框架在现代Web开发中扮演着关键角色&#xff0c;为开发者提供了丰富的工具和抽象层&#xff0c;使得构建复杂的、高性能的Web应用变得更加容易。本文将深入探讨JavaScript框架的核心概念、常见框架的特点以及它们在实际应用中的使用。 JavaScript框架的作用 JavaSc…

STM32 寄存器配置笔记——USART配置中断接收乒乓缓存处理

一、概述 本文主要介绍如何配置USART接收中断&#xff0c;使用乒乓缓存的设计接收数据并将其回显在PC 串口工具上。以stm32f10为例&#xff0c;配置USART1 9600波特率。具体配置参考上一章节STM32 寄存器配置笔记——USART配置 打印。 乒乓缓存的设计应用场景&#xff1a;当后面…

【ceph】如何打印一个osd的op流程,排查osd在干什么

本站以分享各种运维经验和运维所需要的技能为主 《python零基础入门》&#xff1a;python零基础入门学习 《python运维脚本》&#xff1a; python运维脚本实践 《shell》&#xff1a;shell学习 《terraform》持续更新中&#xff1a;terraform_Aws学习零基础入门到最佳实战 《k8…

canvas高级动画001:文字瀑布流

canvas实例应用100 专栏提供canvas的基础知识&#xff0c;高级动画&#xff0c;相关应用扩展等信息。 canvas作为html的一部分&#xff0c;是图像图标地图可视化的一个重要的基础&#xff0c;学好了canvas&#xff0c;在其他的一些应用上将会起到非常重要的帮助。 文章目录 示例…

elk 简单操作手册

1.1. 基础概念 EFK不是一个软件,而是一套解决方案,开源软件之间的互相配合使用,高效的满足了很多场合的应用,是目前主流的一种日志系统。 EFK是三个开源软件的缩写,分别表示:Elasticsearch , Filebeat, Kibana , 其中Elasticsearch负责日志保存和搜索,Filebeat负责收集日志,Ki…

EI期刊完整程序:MEA-BP思维进化法优化BP神经网络的回归预测算法,可作为对比预测模型,丰富内容,直接运行,免费

适用平台&#xff1a;Matlab 2020及以上 本程序参考中文EI期刊《基于MEA⁃BP神经网络的建筑能耗预测模型》&#xff0c;程序注释清晰&#xff0c;干货满满&#xff0c;下面对文章和程序做简要介绍。 适用领域&#xff1a;风速预测、光伏功率预测、发电功率预测、碳价预测等多…

eclipse项目移到idea上部署运行

1.配置web模块 另外&#xff0c;模块这里&#xff0c;也要加上Spring 2.配置Artifact &#xff08;用于tomcat&#xff09; 就是从上面配置的web模块&#xff0c;产生的工件 3.添加lib 一般是在web-inf/lib &#xff0c; 遇到的坑&#xff1a; jdk版本问题&#xff0c;这里…

使用STM32+SPI Flash模拟U盘

试验目的&#xff1a;使用STM32F103C8T6 SPI Flash&#xff08;WSQ16&#xff09;实现模拟U盘的功能 SPI Flash读写说明&#xff1a; Step1 设置SPI1 用于读取SPI Flash&#xff1b; Step2&#xff1a;设置SPI Flash 的使能信号 Step3&#xff1a;使能USB通信 Step4&#xf…

人机交互2——任务型多轮对话的控制和生成

1.自然语言理解模块 2.对话管理模块 3.自然语言生成模块

C++模拟如何实现vector的方法

任意位置插入&#xff0c;insert的返回值为新插入的第一个元素位置的迭代器&#xff1b;因为插入可能会进行扩容&#xff0c;导致start的值改变&#xff0c;所以先定义一个变量保存pos与start的相对位置&#xff1b;判断是否需要扩容&#xff1b;从插入位置开始&#xff0c;将所…

WorldWind Android上加载白模数据

这篇文章介绍下如何加载白模数据。这个白模数据的格式是shapefile格式的文件。白模数据拷贝到手机本地&#xff0c;然后读取白模数据&#xff0c;进行加载展示。 worldwind android本身是不支持加载白模数据的&#xff0c;但是可以根据现有提供的加载Polygons的方式&#xff0c…

【JUC】一篇通关JUC并发之共享模型

目录 1. 共享带来的问题1-1. 临界区 Critical Section1-2. 竞态条件 Race Condition1-3. synchronized 解决方案 1. 共享带来的问题 1-1. 临界区 Critical Section 一个程序运行多个线程本身是没有问题的问题出在多个线程访问共享资源 多个线程读共享资源其实也没有问题在多个…

Linux:Ubuntu实现远程登陆

1、查看sshd服务是否存在 Ubuntu默认是没有安装sshd服务的&#xff0c;所以&#xff0c;无法远程登陆。 检查22端口是否存在 netstat -anp 该命令执行后&#xff0c;查看不到22端口的进程。 如果netstat无法使用&#xff0c;我们需要安装一下netstat服务 sudo apt-get install…

模板初阶(1):函数模板,类模板

一、函数模板 1.1 概念 函数模板代表了一个函数家族&#xff0c;该函数模板与类型无关&#xff0c;在使用时被参数化&#xff0c;根据实参类型产生函数的特定类型版本。 格式&#xff1a; template <typename T>或template <class T> template <class T>…

jetson NX部署Yolov8

一,事情起因,由于需要对无人机机载识别算法进行更新,所以需要对yolov8算法进行部署到边缘端。 二,环境安装 安装虚拟环境管理工具,这个根据个人喜好。 我们需要选择能够在ARM架构上运行的conda,这里我们选择conda-forge 下载地址 安装即可 剩下的就是和conda 创建虚拟…

管理类联考——数学——汇总篇——知识点突破——应用题——路程——记忆

路程——【考频&#xff1a;高】——【解题提示&#xff1a;根据题意画图&#xff0c;找等量关系&#xff08;一般是时间和路程&#xff09;&#xff0c;列方程求解。】 【 应用题 ⟹ \Longrightarrow ⟹ 路程 ⟹ \Longrightarrow ⟹ 直线 ⟹ \Longrightarrow ⟹ 匀速、相遇、…

Kafka 集群如何实现数据同步

Kafka 介绍 Kafka 是一个高吞吐的分布式消息系统&#xff0c;不但像传统消息队列&#xff08;RaabitMQ、RocketMQ等&#xff09;那样能够【异步处理、流量消峰、服务解耦】 还能够把消息持久化到磁盘上&#xff0c;用于批量消费。除此之外由于 Kafka 被设计成分布式系统&…

数字图像处理(实践篇)二 画出图像中目标的轮廓

目录 一 涉及的OpenCV函数 二 代码 三 效果图 一 涉及的OpenCV函数 contours, hierarchy cv2.findContours(image, mode, method[, contours[, hierarchy[, offset ]]]) image&#xff1a;源图像。mode&#xff1a;轮廓的检索方式。cv2.RETR_EXTERNAL&#xff08;只检测…