静态网站在重新发布时保持在线
以前在 Mythex 上重新发布静态网站时,会先删除线上的文件,然后再上传新构建,因此在上传期间,一些访客会看到 404 页面,或是缺少脚本的页面。上传失败时,网站可能被清空,或者只更新了一半。现在,新构建会覆盖旧构建,页面最后写入,多余的文件会在几分钟后删除。第二项修复让重新发布的更改能在几秒内生效,而不是一分钟——对于 logo 这类文件,则是最多一天。
静态网站如何提供服务
静态网站是纯文件构成的网站:HTML 页面、JavaScript、CSS、图片。在 Mythex 上发布时,我们会构建它,把输出上传到对象存储中你网站专属的文件夹,然后通过边缘网络提供服务——边缘服务器靠近访客,从存储中获取文件,并在一段时间内保存它们的副本。(如果你对此还不熟悉,我们关于什么是 CDN的指南涵盖了这个概念。)
当时有两个问题:重新发布时文件变更的顺序,以及旧副本被保留的时间。
问题一:上传前先清空了网站
重新发布会先删除网站文件夹中的所有内容,然后再上传新构建。在上传所用的时间里——在我们的本地测试中,一个 250 个文件的网站需要 12 秒——如果访客的请求尚未在边缘缓存中,就会得到 404,或者得到一个脚本返回 404 的 HTML 页面。
上传失败的情况更糟。文件夹已经被清空,而且在两条上传路径之一中,新的 index.html 可能先于失败的那个文件落地。一次被报告为失败的发布,可能会让一个半新半旧的网站继续在线。
修复方案:覆盖写入,页面最后写,之后再清理
- 不要先删除。 新构建直接上传到旧构建之上。
- 资源先于页面。 页面会声明它需要的脚本和样式,因此页面最后上传。在新页面落地之前,访客拿到的是旧页面,而旧页面引用的是旧资源,这些资源仍然存在。新页面落地后,它们引用的新资源早已就位。
- 稍后删除残留文件。 上传前,我们会列出网站包含的文件。成功上传三分钟后,列表中新构建没有覆盖的文件会被删除。三分钟比页面在边缘和浏览器中可以缓存的 60 秒更长,因此仍持有旧页面的访客,也能找到它引用的资源。
- 不确定时,什么都不删。 如果上传前的文件列表获取失败,就不做任何清理。残留文件无害,下一次发布会清除它们。在不知道原先有什么的情况下删除,正是网站变空的原因。
现在,一次失败的上传会让之前的网站保持原样。
我们在本地进行了端到端测试,使用了真实的上传脚本、真实的上传路由和存储服务,并模拟了边缘缓存为冷的访客。使用旧代码时,重新发布期间有 91 次访问中的 55 次出现故障,失败的发布期间则为 92 次中的 61 次。使用新代码后,在约 3,500 次访问中,0 次出现故障,覆盖了两条上传路径、一次失败上传,以及之后的发布;而且恰好只清理了残留文件。
问题二:重新发布生效太慢
即使上传顺利完成,更改也要过很久才会显示。边缘会把页面缓存 60 秒,其他所有文件缓存一天,并告诉浏览器也这样做。使用固定文件名的文件——logo.png、字体,以及 public/ 文件夹中的任何内容——在重新发布后可能会显示旧版本,最长达一天。
修复方案:在构建下生成文件副本
当边缘向我们的 API 询问某个网站位于何处时,回答现在会包含一个构建标记,每次发布成功时都会变化。边缘会按该标记存放副本。因此,一旦边缘 30 秒的查询缓存过期,一次发布就会变成一整套全新的缓存键。旧副本再也不会被找到,所以边缘可以保留副本一整天,而永远不会提供超过取代它的那次发布的内容。
浏览器则适用两种不同的规则:
- 带哈希的构建产物——例如
assets/index-CzLcxDO8.js这样的文件,其名称会随内容变化——缓存一年,并标记为 immutable。在该名称下,它永远不会过期。 - 其他所有内容——页面以及 logo 这类固定名称的文件——标记为
no-cache,意思是「使用前先检查」。如果没有变化,现在我们已经支持浏览器的If-None-Match检查,回应只是一个很小的304 Not Modified,没有正文。
区分这两者需要小心:从 public/assets/ 复制的文件会保留它自己的名称,如果把 assets/logo.png 缓存一年,就会把旧 logo 固定一年。因此,只有当文件位于构建输出文件夹中**,并且**其名称中有一段看起来像哈希的部分(而不是普通单词)时,才算作带哈希的文件。
在本地,新页面和新 logo 在发布上线后 6 秒即可看到。之前,页面需要 60 秒,logo 则需要一天。
另外两个较小的修复
- HEAD 请求导致的空白页面。
HEAD请求只获取页面的响应头而不获取正文。我们之前会缓存这些空响应,因此下一位访问该页面的访客会看到空白页。现在不再缓存HEAD响应。 - 「没有内容」时的真实页面。 访问一个没有对应应用的地址,过去会返回一段纯文本「此已发布应用处于离线状态或未找到链接」,并带有 502 错误码。现在它会显示一个正式页面「这里没有应用」,状态码为 404,并附带返回 Mythex 的链接,方便网站所有者。未知的自定义域名也会显示同样的页面。
我们的收获
- 顺序本身就是正确性的一部分。 以不同顺序上传相同的文件,就是网站损坏与正常运行之间的区别。
- 延后删除,并且只删除你知道的内容。 使用上传前获取的列表进行清理是安全的;清空文件夹并寄希望于上传能够完成,则不安全。
- 按版本缓存,而不是按时间缓存。 过期时间只是对内容何时变化的猜测。随构建变化的键才是一个事实。
相关阅读:你的已发布应用在重新发布时仍保持在线。更多关于发布的内容,请参阅文档。