项目部署之后页面没有刷新怎么办?

news2024/11/16 12:44:29

最近项目部署成功之后,突然产品找我,上线之后,页面没有生效,这是怎么回事?我这是第一次部署这个项目,也不太清楚历史问题,接下来就慢慢寻找答案吧, 如果心急的可以直接看后面的总结,下面我们好好聊聊缓存的问题。

浏览器输入url之后,就会进行下面一系列判断,来实现页面渲染。

首先讲一下常见的http缓存~

HTTP缓存常见的有两类:

  • 强缓存:可以由这两个字段其中一个决定
    • expires
    • cache-control(优先级更高)
  • 协商缓存:可以由这两对字段中的一对决定
    • Last-Modified,If-Modified-Since
    • Etag,If-None-Match(优先级更高)

强缓存

使用的是express框架

expires

app.get('/login', function(req, res){
  // 设置 Expires 响应头
  const time = new Date(Date.now() + 300000).toUTCString()
  res.header('Expires', time)
  res.render('login');
});

然后我们在前端页面刷新,我们可以看到请求的资源的响应头里多了一个expires的字段, 取消Disable cache

刷新

勾选Disable cache

但是,Expires已经被废弃了。对于强缓存来说,Expires已经不是实现强缓存的首选。

因为Expires判断强缓存是否过期的机制是:获取本地时间戳,并对先前拿到的资源文件中的Expires字段的时间做比较。来判断是否需要对服务器发起请求。这里有一个巨大的漏洞:“如果我本地时间不准咋办?”

是的,Expires过度依赖本地时间,如果本地与服务器时间不同步,就会出现资源无法被缓存或者资源永远被缓存的情况。所以,Expires字段几乎不被使用了。现在的项目中,我们并不推荐使用Expires,强缓存功能通常使用cache-control字段来代替Expires字段。

cache-control

其实cache-control跟expires效果差不多,只不过这两个字段设置的值不一样而已,前者设置的是秒数,后者设置的是毫秒数

app.get('/login', function(req, res){
  // 设置 Expires 响应头
  // const time = new Date(Date.now() + 300000).toUTCString()
  // res.header('Expires', time)
  // 设置 Cache-Control 响应头
  res.header('Cache-Control', 'max-age=300')
  res.render('login');
});

前端页面响应头多了cache-control这个字段,且300s内都走本地缓存,不会去请求服务端

Cache-Control:max-age=N,N就是需要缓存的秒数。从第一次请求资源的时候开始,往后N秒内,资源若再次请求,则直接从磁盘(或内存中读取),不与服务器做任何交互。

Cache-control中因为max-age后面的值是一个滑动时间,从服务器第一次返回该资源时开始倒计时。所以也就不需要比对客户端和服务端的时间,解决了Expires所存在的巨大漏洞。

Cache-control的多种属性:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Cache-Control

但是使用最多的就是no-cache和no-store,接下来就重点学习这两种

no-cache和no-store

no_cache是Cache-control的一个属性。它并不像字面意思一样禁止缓存,实际上,no-cache的意思是强制进行协商缓存。如果某一资源的Cache-control中设置了no-cache,那么该资源会直接跳过强缓存的校验,直接去服务器进行协商缓存。而no-store就是禁止所有的缓存策略了。

app.get('/login', function(req, res){
  // 设置 Expires 响应头
  // const time = new Date(Date.now() + 300000).toUTCString()
  // res.header('Expires', time)
  // 设置 Cache-Control 响应头
  res.header('Cache-Control', 'no-cache')
  res.render('login');
});

no-cache(进行协商缓存,下次再次请求,没有勾选控制台Disable cache,状态码是304)

app.get('/login', function(req, res){
  // 设置 Cache-Control 响应头
  res.header('Cache-Control', 'no-store')
  res.render('login');
});

no-store(每次都请求服务器的最新资源,没有缓存策略)

强制缓存就是以上这两种方法了。现在我们回过头来聊聊,Expires难道就一点用都没有了吗?也不是,虽然Cache-control是Expires的完全替代品,但是如果要考虑向下兼容的话,在Cache-control不支持的时候,还是要使用Expires,这也是我们当前使用的这个属性的唯一理由。

协商缓存

与强缓存不同的是,强缓存是在时效时间内,不走服务端,只走本地缓存;而协商缓存是要走服务端的,如果请求某个资源,去请求服务端时,发现命中缓存则返回304,否则则返回所请求的资源,那怎么才算命中缓存呢?接下来讲讲

Last-Modified,If-Modified-Since

简单来说就是:

  • 第一次请求资源时,服务端会把所请求的资源的最后一次修改时间当成响应头中Last-Modified的值发到浏览器并在浏览器存起来
  • 第二次请求资源时,浏览器会把刚刚存储的时间当成请求头中If-Modified-Since的值,传到服务端,服务端拿到这个时间跟所请求的资源的最后修改时间进行比对
  • 比对结果如果两个时间相同,则说明此资源没修改过,那就是命中缓存,那就返回304,如果不相同,则说明此资源修改过了,则没命中缓存,则返回修改过后的新资源

基于last-modified的协商缓存实现方式是:

  1. 首先需要在服务器端读出文件修改时间,
  2. 将读出来的修改时间赋给响应头的last-modified字段。
  3. 最后设置Cache-control:no-cache

三步缺一不可。

app.get('/login', function(req, res){
  // 设置 Expires 响应头
  // const time = new Date(Date.now() + 300000).toUTCString()
  // res.header('Expires', time)

  const { mtime } = fs.statSync(path.join(__dirname, 'public/index.css')) // 读取最后修改时间
  console.log(mtime.toUTCString(), '--------')
  // 响应头的last-modified字段
  res.header('last-modified', mtime.toUTCString())
  // 设置 Cache-Control 响应头
  res.header('Cache-Control', 'no-cache')
  res.render('login');
});

当index.css发生改变再次请求时

终端输出的时间变化

服务端的时间跟last-modified的值是一致的

使用以上方式的协商缓存已经存在两个非常明显的漏洞。这两个漏洞都是基于文件是通过比较修改时间来判断是否更改而产生的。

1.因为是更具文件修改时间来判断的,所以,在文件内容本身不修改的情况下,依然有可能更新文件修改时间(比如修改文件名再改回来),这样,就有可能文件内容明明没有修改,但是缓存依然失效了。

2.当文件在极短时间内完成修改的时候(比如几百毫秒)。因为文件修改时间记录的最小单位是秒,所以,如果文件在几百毫秒内完成修改的话,文件修改时间不会改变,这样,即使文件内容修改了,依然不会 返回新的文件。

为了解决上述的这两个问题。从http1.1开始新增了一个头信息,ETag(Entity 实体标签)

Etag,If-None-Match

ETag就是将原先协商缓存的比较时间戳的形式修改成了比较文件指纹。

其实Etag,If-None-Match跟Last-Modified,If-Modified-Since大体一样,区别在于:

  • 后者是对比资源最后一次修改时间,来确定资源是否修改了
  • 前者是对比资源内容,来确定资源是否修改

那我们要怎么比对资源内容呢?我们只需要读取资源内容,转成hash值,前后进行比对就行了!

app.get('/login', function(req, res){
  // 设置 Expires 响应头
  // const time = new Date(Date.now() + 300000).toUTCString()
  // res.header('Expires', time)

  // const { mtime } = fs.statSync(path.join(__dirname, 'public/index.css')) // 读取最后修改时间
  // console.log(mtime.toUTCString(), '--------')
  // 响应头的last-modified字段
  // res.header('last-modified', mtime.toUTCString())


  // 设置ETag
  const ifNoneMatch = req.header['if-none-match']
  const hash = crypto.createHash('md5')
  const fileBuf = fs.readFileSync(path.join(__dirname, 'public/index.css'))
  hash.update(fileBuf, 'utf8')
  const etag = `"${hash.digest('hex')}"`
  console.log(etag, '---etag----')
  // 对比hash值
  if (ifNoneMatch === etag) {
    res.status = 304
  } else {
    res.header('etag', etag)
    // ctx.body = fileBuffer
  }
  // 设置 Cache-Control 响应头
  res.header('Cache-Control', 'no-cache')
  res.render('login');
});

当资源发生改变时,状态码变成200,更新缓存

比如更改css样式

ETag也有缺点

  • ETag需要计算文件指纹这样意味着,服务端需要更多的计算开销。如果文件尺寸大,数量多,并且计算频繁,那么ETag的计算就会影响服务器的性能。显然,ETag在这样的场景下就不是很适合。
  • ETag有强验证和弱验证,所谓将强验证,ETag生成的哈希码深入到每个字节。哪怕文件中只有一个字节改变了,也会生成不同的哈希值,它可以保证文件内容绝对的不变。但是,强验证非常消耗计算量。ETag还有一个弱验证,弱验证是提取文件的部分属性来生成哈希值。因为不必精确到每个字节,所以他的整体速度会比强验证快,但是准确率不高。会降低协商缓存的有效性。

值得注意的一点是,不同于cache-control是expires的完全替代方案(说人话:能用cache-control就不要用expiress)。ETag并不是last-modified的完全替代方案。而是last-modified的补充方案(说人话:项目中到底是用ETag还是last-modified完全取决于业务场景,这两个没有谁更好谁更坏)。

disk cache & memory cache

磁盘缓存+内存缓存,这两种缓存不属于http缓存,而是本地缓存了~

我们直接打开掘金官网,点击network,类型选择all

可以看的很多请求,这里请求包括了静态资源+接口请求

这里我们能够看的很多请求的size中有很多是disk cache(磁盘缓存)

也有一些图片是memory cache(内存缓存)

这两者有什么区别呢?

disk cache: 磁盘缓存,很明显将内容存储在计算机硬盘中,很明显,这种缓存可以占用比较大的空间,但是由于是读取硬盘,所以速度低于内存

memory cache: 内存缓存,速度快,优先级高,但是大小受限于计算机的内存大小,很大的资源还是缓存到硬盘中

上面的浏览器缓存已经有三个大点了,那它们的优先级是什么样的呢?

缓存的获取顺序如下:

1.内存缓存

2.磁盘缓存

3.强缓存

4.协商缓存

如果勾选了Disable cache,那磁盘缓存都不存在了,之还有内存缓存

我还发现,勾选了Disable cache,就base64图片一定会在内存缓存中,其他图片则会发起请求;而不勾选了Disable cache,则大多数图片都在内存缓存中

CDN缓存

CDN缓存是一种服务端缓存,CDN服务商可以将源站上的资源缓到其各地的边缘服务器节点上。当用户访问该资源时,CDN再通过负载均衡将用户的请求调度到最近的缓存节点上,有效减少了链路回源,提高了资源访问效率及可用性,降低带宽消耗。

如果客户端没有命中缓存,那接下来就要发起一次网络请求,根据网络环境,一般大型站点都会配置CDN,CDN会找一个最合适的服务节点接管网络请求。CDN节点都会在本地缓存静态文件数据,一旦命中直接返回,不会穿透去请求应用服务器。并且CDN会通过在不同的网络,策略性地通过部署边缘服务器和应用大量的技术和算法,把用户的请求指定到最佳响应节点上。所以会减少非常多的网络开销和响应延迟。

如果没有部署CDN或者CDN没有命中,请求最终才会落入应用服务器,现在的http服务器都会添加一层反向代理,例如nginx,在这一层同样会添加缓存层,代表技术是squid,varnish,当然nginx作为http服务器而言也支持静态文件访问和本地缓存技术,当然也可以使用远程缓存,如redis,memcache,这里缓存的内容一般为静态文件或者由服务器已经生成好的动态页面,在返回用户之前缓存。

如果前面的缓存机制全部失效,请求才会落入真正的服务器节点。

总结

1.如果页面是协商缓存,如何获取页面最新内容?

协商缓存比较好办,那就刷新页面,不过需要勾选Disable cache,但是用户不知道打开控制台怎么办?

那就右击页面的刷新按钮,然后选择硬性重新加载,或者清空缓存并硬性重新加载,页面就获取到最新资源了

2.如果页面没有设置cache-control,那默认的缓存机制是什么样的?

默认是协商缓存,这也符合浏览器设计,缓存可以减少宽度流量,加快响应速度

3.如果项目重新部署还是没有更新,怎么办?

在确定项目已经部署成功

这样子,可以去问一下公司的运维同事,这个项目是否有CDN缓存

如果项目的域名做了CDN缓存,就需要刷新CDN目录,来解决缓存问题了,不然就只能等,等CDN策略失效,来请求最新的内容

向如下配置的缓存策略,只有过30天才会去真正服务器去请求最新内容

当然你可以测试一下是否为CDN缓存,在url后面拼接一个参数,就能够获取到最新资源了,比如有缓存的链接是https://baidu.com/abc

你可以在浏览器中输入https://baidu.com/abc&t=1234来判断一下,如果访问的是更改后的内容了,那就是原来的链接有CDN缓存导致没有刷新

当然特定场景,我们不能随意给链接后面添加参数,所以这也只适用于测试一下是否有CDN缓存

所以最好的解决办法还是需要让运维同事去刷新目录,这样就能快速解决CDN缓存问题。

参考链接

https://juejin.cn/post/7127194919235485733

https://juejin.cn/post/7177568033316012088

https://xiaolincoding.com/network/2_http/http_interview.html#http-%E7%BC%93%E5%AD%98%E6%8A%80%E6%9C%AF

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

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

相关文章

桃子叶片病害识别(Python代码,pyTorch框架,深度卷积网络模型,很容易替换为其它模型,带有GUI识别界面)

1.分为三类 健康的桃子叶片 ,251张 桃疮痂病一般,857张 桃疮痂病严重,770 张 2. GUI界面识别效果和predict.py识别效果如视频所示桃子叶片病害识别(Python代码,pyTorch框架,深度卷积网络模型&#xff0…

python实现的淘宝自动发货脚本

pyhton # 文件用ini # 自动应答,自动点发货

从零做软件开发项目系列之五——系统开发过程

前言 在软件项目的设计开发过程中,除了前期的用户需求调研确认,系统设计、数据库设计等工作之外,还有一些重要的工作需要考虑,比如软件开发模式,如何制定开发计划,如何协调开发人员开展开发工作等。本文将…

分布式事务篇-2.4 Spring-Boot整合Seata

文章目录 前言一、pom jar导入:二、项目配置:2.1 配置 说明:2.1 .1 seata server 端:2.1 .2 seata client 端: 2.2 开启seata 对于数据源的代理:2.3 seata-client 的注册中心:2.4 seata-client 的配置中心:2.5 去掉手写的数据源代…

-9501 MAL系统没有配置或者服务器不是企业版(dm8达梦数据库)

dm8达梦数据库 -9501 MAL系统没有配置或者服务器不是企业版) 环境介绍1 环境检查2 问题原因 环境介绍 搭建主备集群时,遇到报错-9501 MAL系统没有配置或者服务器不是企业版 1 环境检查 检查dmmal.ini配置文件权限正确 dmdba:dinstall,内容正…

2023年下软考信息安全工程师报名时间及汇总(附备考攻略)

信息安全工程师是近几年新增的中级考试科目,一般在下半年考试,难度还是有的,但是只要你有恒心,愿意付出,认真刷题备考,拿下证书还是没有问题的! 2023年下半年由于考试改革,报名时间…

kubernetes如何使用kruise-rollout进行分批灰度发布

前言 部署在 Kubernetes 集群中的应用,在升级发布时可能会存在的问题: 1,由于 Kuberneter 底层 Pod 容器生命周期与网络组件生命周期是异步管理的,在升级时如果没有处理好应用优雅退出的问题,就很容易导致 http 访问请…

【JAVA】抽象类与接口--下

⭐ 作者:小胡_不糊涂 🌱 作者主页:小胡_不糊涂的个人主页 📀 收录专栏:浅谈Java 💖 持续更文,关注博主少走弯路,谢谢大家支持 💖 抽象类与接口 1. 实现多个接口2. 接口间…

Linux常用命令_文件处理命令

文章目录 1. 命令格式与目录处理命令ls1.1 命令格式1.2 目录处理命令:ls 2. 目录处理命令2.1 目录处理命令:mkdir2.2 目录处理命令:cd2.3 目录处理命令:pwd2.4 目录处理命令:rmdir2.5 目录处理命令:cp2.6 目…

openGauss学习笔记-50 openGauss 高级特性-DB4AI

文章目录 openGauss学习笔记-50 openGauss 高级特性-DB4AI50.1 关键字解析50.2 使用指导 openGauss学习笔记-50 openGauss 高级特性-DB4AI openGauss当前版本支持了原生DB4AI能力,通过引入原生AI算子,简化操作流程,充分利用数据库优化器、执…

【PHP】字符串数组

文章目录 字符串类型字符串转义字符串相关函数 数组定义方式数组特点二维数组数组遍历foreach遍历语法for循环遍历数组相关函数相关题目 字符串类型 定义语法 引号方式:比较适合定义那些比较短(不超过一行)或者没有结构要求的字符串 1&#…

基于Python的图书馆大数据可视化分析系统设计与实现【源码+论文+演示视频+包运行成功】

博主介绍:✌csdn特邀作者、博客专家、java领域优质创作者、博客之星,擅长Java、微信小程序、Python、Android等技术,专注于Java、Python等技术领域和毕业项目实战✌ 🍅文末获取源码联系🍅 👇🏻 …

vscode 与 C++

序 具体流程的话,官方文档里都有的:C programming with Visual Studio Code 浏览器下载一个mingw64,解压,配置环境变量vscode里安装c相关的插件没了 第一步只看文字,可能有点抽象,相关视频: …

云计算企业私有云平台建设方案PPT

导读:原文《云计算企业私有云平台建设方案PPT》(获取来源见文尾),本文精选其中精华及架构部分,逻辑清晰、内容完整,为快速形成售前方案提供参考。 喜欢文章,您可以点赞评论转发本文,…

Linux常用命令_权限管理命令

文章目录 1. 权限管理命令: chmod2. 其他权限管理命令2.1 权限管理命令: chown2.2 权限管理命令: chgrp2.3 权限管理命令: umask 1. 权限管理命令: chmod {ugoa}中分别为:u-user、g-group、a-all;谁创建文件,谁是所有者;所属组为所…

HOOPS Exchange SDK 2023 Service Pack 2 Crack

内容摘自互联网,具体功能以官网介绍为准。。。 HOOPS SDK是全球领先开发商TechSoft 3D旗下的原生产品,专注于Web端、桌面端、移动端3D工程应用程序的开发。长期以来,HOOPS通过卓越的3D技术,帮助全球600多家知名客户推动3D软件创新…

Qt ui对某控件进行全局提升报错:no such file or directory

问题 在Qt项目中,设计师界面,对某一控件进行提升,设置完“提升的类名称”、“头文件”、全局包含后,构建时,报“no such file or directory”错误,但文件命名存在呀。 解决 根据问题就应该明白&#xf…

并查集(C++实现)

目录 一、并查集原理 二、并查集应用 2.1 并查集举例 2.2 并查集数组规律 2.3 并查集功能 三、并查集实现 3.1 并查集 3.2 根据名字查找 四、例题 4.1 省份数量 4.2 等式方程的可满足性 一、并查集原理 再一些应用问题中,需要将n个不同的元素划分成一些…

Python读取Excel:实现数据高效处理的利器

目录 一、Python读取Excel的常用库二、Python读取Excel的步骤三、具体案例和使用场景四、Python读取Excel的优势与其他编程语言比较 摘要 本文将介绍Python读取Excel的方法,并通过具体案例和使用场景展示如何实现数据高效处理。我们将介绍常用的Python库&#xff0c…

QT基础使用:组件和代码关联(信号和槽)

自动关联 ui文件在设计环境下,能看到的组件可以使用鼠标右键选择“转到槽”就是开始组件和动作关联。 在自动关联这个过程中软件自动动作的部分 需要对前面头文件进行保存,才能使得声明的函数能够使用。为了方便,自动关联时先对所有文件…