首页 > 代码库 > 为什么我执行了发布操作,但是线上的资源并没有更新?
为什么我执行了发布操作,但是线上的资源并没有更新?
随着整个互联网时代的发展,前后端职能的分离,在过去的一段时间里,前后端各自仅只关注自己最擅长的领域。但是,随着“大前端”时代的到来,前端们又一次开始需要关注后端,或者前后端链接的问题了。
本文起源于笔者的一次线上发布经历,事情的前因后果大概就如何题目所提到的,但是诡异的还不仅如此,当笔者执行了release操作之后,如果用之前访问的链接(如http://www.examplae.com)去访问执行了发布的web应用的话,会发现资源并没有更新,但是如果给这个链接加上一个参数(如http://www.example.com/?t=1),那么整个web应用则神奇的更新了,这到底是怎么回事呢?笔者开始了一次意外之旅。
首先,笔者想到通过查看network(chrome dev tools)去看看What`s going on?
我们都知道,一次完整的http请求过程由以下几部分构成:
1、首先客户端发起请求建立连接,这个阶段会先发送header(当然在应用层协议之前,我们需要先构建通过传输层协议也就是常说的TCP,我们常说的三次握手的过程就发生在这个阶段)
2、当服务端验证通过并允许通信之后,客户端开始发送请求(也即AJAX请求,或说XHR请求)
3、然后服务端根据客户端请求的参数,对客户端的请求作出响应,返回响应报文(在响应结束后,连接通常会被关闭,当然header中有connnection:keep-alive除外)
到底是哪一步出了问题呢?既然东西没有变化,那么多半就是缓存了吧?但是当笔者打开network的时候,却发现了更奇怪的东西
http status code为200的情况下,出现了一些from disk cache和from memory cache的资源,难道是这些缓存的原因?难道笔者的问题也是这个原因引起的么?
但是清空缓存之后,笔者发现,虽然同名的资源变成了正常的size显示,但是wep应用并没有得到更新。这个code为200的奇怪的缓存似乎,并不是笔者问题的原因,不过这个code为200的缓存倒是引起了笔者进一步的兴趣。
我们都知道,http status code的状态码常见的大概是有这几种:
1XX 几乎没见过的表示临时响应,并且需要请求者继续执行操作(其实这个并不常见)
200 请求成功,最常见的状态
304 not modified,表示服务端验证发现没有任何修改,直接让客户端使用浏览器缓存
404 not found,服务器没找到该页面
403 forbidden,服务器主动拒绝了访问
503 服务无法使用,服务出错也是这个code
也即是说,常见的缓存都是指的304这个code,但是这个缓存偏偏是从200出来的,这倒是令笔者挺在意的,google了一番之后解释如下:
code为200的请求可以简单的理解成客户端直接没有发起请求,而是根据上次请求时服务端下发的过期时间,做了过期校验(依赖头中的cache-control: max-age字段和expires字段),在不清除缓存的前提下直接使用客户端的缓存
而与之对应的code为304的请求则是客户端依然发起了请求,请求到达服务端之后,服务端检测发现并没有内容修改,则返回304,让客户端使用缓存。
到这从network中能查找的线索基本已经用尽了,笔者觉得需要往更深入的地方去找了。
然后,由于可见部分的原因已经排除,那么笔者觉得就需要往不可见的方向上去探寻原因了。对前端而言,透明的自然是服务端,但是服务端发布又有什么不同呢?笔者试着对比了之前的发布的场景:
第一种的web应用实际上是依托于一个.net项目的,每次发布,.net项目和静态资源走单独的发布通道release,每次发布时都会通过时间戳的方式生成不同的静态资源版本,以免产生缓存,这个在实际运用中并没有出现过缓存的问题;
第二种的发布流程实际上是发布的是一个基于webpack打包的纯静态项目,所有的静态资源每次release的名字几乎都是不一样的(除了那些静态的公共资源以外)来保证不会缓存,虽然设想得很好,但是这样发布的web应用出现了笔者遇到的缓存问题。
看到这里笔者似乎并没有发现什么不同,但是等等,有一个很重要的区别,第二种发布方式html有关的缓存该怎么办呢?第一种方式是基于http请求的,服务端代码更新了对应的cshtml也买你就会得到更新,但是第二种方式则不是,html的缓存更新之前会一直存在,那不就是缓存不更新的原因么?即使静态资源每次都更新了,但是html的缓存却没有更新,用户访问了旧的html拉取了就的静态资源,得到了旧的页面。。
那么问题找到了,但是具体出现在哪呢?
最后,笔者顺藤摸瓜,在确认了只出的服务并没有缓存的前提下,能产生缓存就只剩下CDN了。
问题的答案最后是这样的:当笔者release了最新的v2之后,笔者通过旧的链接访问,浏览器发现有DNS缓存,然后CDN会将上次请求web站点的缓存内容直接返回给客户端,所以产生了笔者虽然release v2但是访问的web应用的内容,仍然是v1的情况;当笔者更换了链接之后,由于CDN并没有关于该链接的缓存,也即没有缓存的内容,于是请求了web站点去获取内容,将其返回给客户端,于是v2便可以访问了。
在日常的工作中,我们常常会碰到一些奇奇怪怪的问题,有时候我们并没有时间去将它们一一解决,我们会用自己的方式“巧妙”的绕开它,但是如果一直绕着走,最终,我们将一无所获。
勇敢的直面生活中的苦难,不正是生活最有意思的一部分么?(哪怕是在后面复盘XD)
后记:
1、在以往的时代,后端离前端其实很远,但是随着时代的更迭,node的出现,我们对后端的认知也一步步深入,更多的,已有的解决方案甚至都不能满足我们的需求(例如内部通信的场景,如果我们仍使用http的协议的话,由于量级与对外时的数量有着很大的区别,且http协议中关于安全验证和很多header透传的处理并不适用于内部通信的场景,使我们不得不基于TCP协议自己重新实现一次类http的通信过程)
2、http其实都算是一个过去时了,http2也意味着新时代的到来,无论是数据传输格式的多样化(从文本到二进制),头部压缩(解决老大难的header体积过大的问题),多路复用(http1.x的pipeline会照成多请求的阻塞,并不是真正的并发),以及server push,都是对http1.x诸多诟病的扩展,想了解更多的话可以参考HTTP2的世界,笔者就不再展开了。
其实,写到这里,笔者想起了刚上本科的,第一节专业基础课,一位教程序设计基础的老教授说的一段话:
“同学们,当你们踏入计算机这个行业,就已经注定你们需要终身学习了,所以,你们做好准备了么?也许,通过这门课的学习,你们就已经可以实现任何你们想要的功能了,但是实现往往只是第一步,后面的路,还远着呢。”
其实,回过头来反观自己的生活,又何尝不是处于一个始终、或说终身学习的过程之中呢?
为什么我执行了发布操作,但是线上的资源并没有更新?