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

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

浏览器输入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

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

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

相关文章

RE:从零开始的车载Android HMI(四) - 收音机刻度尺

最近比较忙,研究复杂的东西需要大量集中的时间,但是又抽不出来,就写点简单的东西吧。车载应用开发中有一个几乎避不开的自定义View,就是收音机的刻度条。本篇文章我们来研究如何绘制一个收音机的刻度尺。 本系列文章的目的是在讲…

4.12 TCP 连接,一端断电和进程崩溃有什么区别?

目录 TCP keepalive TCP 的保活机制 主机崩溃 进程崩溃 有数据传输的场景 客户端主机宕机,又迅速重启 客户端主机宕机,一直没有重启 TCP连接服务器宕机和进程退出情况总结 TCP keepalive TCP 的保活机制 TCP 保活机制需要通过 socket 接口设置 S…

pytorch 实现VGG

VGG全称是Visual Geometry Group,因为是由Oxford的Visual Geometry Group提出的。AlexNet问世之后,很多学者通过改进AlexNet的网络结构来提高自己的准确率,主要有两个方向:小卷积核和多尺度。而VGG的作者们则选择了另外一个方向&a…

在Ubuntu上安装和设置RabbitMQ服务器,轻松实现外部远程访问

文章目录 前言1.安装erlang 语言2.安装rabbitMQ3. 内网穿透3.1 安装cpolar内网穿透(支持一键自动安装脚本)3.2 创建HTTP隧道 4. 公网远程连接5.固定公网TCP地址5.1 保留一个固定的公网TCP端口地址5.2 配置固定公网TCP端口地址 前言 RabbitMQ是一个在 AMQP(高级消息队列协议)基…

编码基础一:侵入式链表

一、简介概述 1、普通链表数据结构 每个节点的next指针指向下一个节点的首地址。这样会有如下的限制: 一条链表上的所有节点的数据类型需要完全一致。对某条链表的操作如插入,删除等只能对这种类型的链表进行操作,如果链表的类型换了&#…

牛客网Verilog刷题 | 入门特别版本

文章目录 1、 VL1 输出12、VL2 wire连线3、 VL3 多wire连接4、VL4 反相器5、VL5 与门6、VL6 NOR 门7、VL7 XOR 门8、VL8 逻辑运算10、VL10 逻辑运算211、VL11 多位信号12、VL12 信号顺序调整13、VL13 位运算与逻辑运算14、VL14 对信号按位操作15、VL15 信号级联合并16、VL16 信…

安装Docker并配置镜像加速器、容器

1.安装docker服务,配置镜像加速器 安装软件包 [rootlocalhost ~]# yum install -y yum-utils device-mapper-persistent-data lvm2 设置yum源 [rootlocalhost ~]# yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo…

【问题处理】解决Spring事务@Transactional多层嵌套失效

场景: 在 AService 中,我会直接调用 A 的数据操作层去操作 A的数据 以及 A关联密切的其它数据,在操作完之后,会去调用 BService 和 CService 中更新对应的数据,并在每个方法上使用了事务,但在调用 BService…

VMware 中Centos8的NAT网络设置

1、先将虚拟机设置为NAT模式 2、打开虚拟网络编辑器,记录以下信息 NAT设置:子网掩码、网关 DHCP设置:I P 范围 (自动时) 3、进入Centos8的网络设置页面,按照记录的信息进行配置 4、重载、重启网卡 nmcli c reload ensl60 n…

4G电力摄像机如何通过AT指令对接到国网平台呢?

对于针对电网安全运行的迫切需求,”输电线路智能可视化监测系统”被研发并应用,通过视频监控和AI智能分析技术,实现了对输电线路远程视频在线监测、外力破坏智能分析,可实现对输电线路的全天候实时监测和预警,有效保障…

element plus 的图片上传组件回显

element图片回显是通过修改file-list属性的url属性实现的。 <!-- 图片上传 --><el-form-item label"景区图片" prop"s_img"><el-uploadlist-type"picture-card":action"网址":on-change"handleChange":befor…

【KingSCADA】问题处理:记录KS历史报警查询异常

哈喽&#xff0c;大家好&#xff01;我是雷工。 本篇记录KingSCADA的历史报警应用中的一个问题&#xff0c;及处理过程。 一、问题描述 最近客户遇到这么一个问题&#xff1a;当打开历史报警窗界面&#xff0c;自动加载的报警信息中有显示最近几天的报警信息&#xff0c;但当…

[JavaWeb]【十二】web后端开发-事务管理AOP

目录 一、事务管理 1.1 事务回顾 1.2 Spring事务管理 1.2.1 案例 1.2.1.1 EmpMapper新增deleteByDeptId方法 1.2.1.2 DeptServiceImpl 1.2.1.3 启动服务-测试 1.2.2 模拟异常 1.2.3 分析问题 1.2.4 Spring事务管理&#xff08;一般用在类似多次delete&#xff09; 1.2.4…

【快速傅里叶变换(fft)和逆快速傅里叶变换】生成雷达接收到的经过多普勒频移的脉冲雷达信号(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

Jupyter Notebook 配置根目录

注&#xff1a;本文是在 Windows 10 上配置 Jupyter Notebook 打开的默认根目录&#xff0c;Linux 同。 步骤一&#xff1a;创建 Jupyter Notebook 配置文件 使用以下命令创建 Jupyter Notebook 配置文件&#xff08;如果尚未创建&#xff09;&#xff1a; jupyter notebook …

SecureBridge安全文件下载的组件Crack

SecureBridge安全文件下载的组件Crack SecureBridge包括SSH、SSL和SFTP客户端和服务器组件。它使用SSH或SSL安全传输层协议和加密消息语法来保护任何TCP流量&#xff0c;这些协议为客户端和服务器提供身份验证、强数据加密和数据完整性验证。SecureBridge组件可以与数据访问组件…

Neo4j实现表字段级血缘关系

需求背景 需要在前端页面展示当前表字段的所有上下游血缘关系&#xff0c;以进一步做数据诊断治理。大致效果图如下&#xff1a; 首先这里解释什么是表字段血缘关系&#xff0c;SQL 示例&#xff1a; CREATE TABLE IF NOT EXISTS table_b AS SELECT order_id, order_status F…

十五、systemctl命令如何使用?

在Linux系统中&#xff0c;一些内置服务可以通过systemctl控制&#xff0c;部分第三方软件也可以通过systemctl控制。 1、基础语法 start&#xff1a;开启服务&#xff1b; stop&#xff1a;关闭服务&#xff1b; status&#xff1a;查看服务当前状态&#xff1b; enable&a…

Centos 7.6 安装mongodb

以下是在CentOS 7.6上安装MongoDB的步骤&#xff1a; 打开终端并以root用户身份登录系统。 创建一个新的MongoDB存储库文件 /etc/yum.repos.d/mongodb-org-4.4.repo 并编辑它。 sudo vi /etc/yum.repos.d/mongodb-org-4.4.repo在编辑器中&#xff0c;添加下面的内容到文件中并…

Vue中使用element-plus中的el-dialog定义弹窗-内部样式修改-v-model实现-demo

效果图 实现代码 <template><el-dialog class"no-code-dialog" v-model"isShow" title"没有收到验证码&#xff1f;"><div class"nocode-body"><div class"tips">请尝试一下操作</div><d…