一文看懂静态资源服务沉浮及其在携程的演进( 七 )


于是 , 如何更新同名资源的内容 , 成了一件让人头痛的事 。CDN 厂商急人所急 , 通常都会提供“清理缓存”的接口 。但是 , 想象一下整个内容交付网络中数以千计的节点和数以万计的服务器 , 就知道这件事不像按一下 Ctrl+F5 这么轻松 , 可以偶尔为之 , 却不适合作为常规操作 。何况 , 边缘节点的缓存可以清理 , 可是终端用户浏览器上的缓存 , 依然无解 。
所以 , 必须尽力避免覆盖同名静态资源 。这是共识!
很久很久以前 , 在静态资源文件和虚拟目录之间 , 还有一层特殊的目录 , 称为“槽位” 。每个虚拟目录下 , 默认设置 R0 至 R9 共计十个槽位 。每次发布时 , 目标槽位依次向前递进 , 用尽之后再从头开始 , 如此更新周而复始 , 以求资源万古长青 。

一文看懂静态资源服务沉浮及其在携程的演进

文章插图
 
缺点是显而易见的 , 最大的问题是不支持增量更新 , 哪怕只是修改了某个脚本中的一行代码甚至一个字符 , 客户端和 CDN 都需要重新下载整个目录下的所有资源 。但是评价方案的优劣 , 不能脱离时代背景 。毕竟 , 当下国内首屈一指的 CDN 厂商网宿公司直到2005年才推出自己的 CDN 系统 , 而携程在两年前就已经上市了 。有些古老的办法 , 因为简单 , 因为可靠 , 往往能够在新兴技术的冲击下顽强生存 , 保有一席之地 。比如纸笔 , 比如槽位 。
不过 , 全量更新实在太不经济了 。去 Windows 完成之后 , 我们就尝试优化 。Ilya Grigorik在《HTTP 缓存》中这样写道:“如何才能鱼和熊掌兼得:客户端缓存和快速更新?您可以在资源内容发生变化时更改其网址 , 强制用户下载新响应 。通常情况下 , 可以通过在文件名中嵌入文件的指纹或版本号来实现—例如style.x234dff.css 。”
【一文看懂静态资源服务沉浮及其在携程的演进】就这样?就这样 。Ilya 的这篇文章最后更新于2019年初 , 可见这些年来 , 对于如何解决缓存冲突 , 依然没有什么更好的办法 。Webpack 是这么干的 , 我们也是 。
有时候 , 一行代码所包含的逻辑 , 千言万语也诉说不尽 。有时候 , 一句话能说明白的方案 , 实施起来却千头万绪 。下面这张图 , 粗略描述了我们的静态资源增量更新流程 。
一文看懂静态资源服务沉浮及其在携程的演进

文章插图
 
携程“静态资源模块”方案构成(第一版)
在文件名称中嵌入“指纹” , 对于图片、文档这类资源轻而易举 , 只需用 MD5 或类似的数据摘要算法计算出内容摘要即可 。但是对于 JS、CSS 这类可能涉及资源嵌套的前端代码 , 就麻烦了 。
比如 , 对于 CSS 文件 , 需要?解析代码 , 找到其中涉及的图片、字体文件等关联资源的文件名称 , ?获取关联资源嵌入指纹后的文件名称 , ?替换 CSS 文件内容 , 然后计算内容摘要 。?假如 CSS 文件中还依赖(include)其他的 CSS 文件 , 那么还要注意先后顺序 。对于JS 文件 , 情况就更复杂一些 , 不仅要?事前用特定的宏方法界定关联资源名称(因为我们无法通过语法分析来判断哪些字面量代表资源名称) , 如果提交的是已经混淆过的代码 , 还需要?考虑 SourceMap 文件的重构问题 。其中 , 第5点尤其令前端开发人员不满 。鱼和熊掌兼得?不付出点代价怎么行呢 。
在实施增量发布的过程中 , “静态资源模块”的概念代替了原来的虚拟目录 。同时 , 引入语义化版本(Semantic Version)来控制模块的更迭 , 希望借此推动跨团队的静态资源共享 。但是这件事并不顺利 , 直到2019年借国际化的东风 , 推出第二版的携程静态资源模块化方案之后 , 情况才有所好转 。
六、上云 , 上云!程序员们勤耕不辍 , 静态资源的数量每天都在增长 , 服务器磁盘的剩余空间渐渐归零 。虽然是虚拟服务器 , 磁盘空间的扩容依然不易 , 而且受宿主机的约束 , 可以腾挪的余地有限 。分拆应用虽然能解燃眉之急 , 实现难度也不大 , 却势必加剧日常运维的难度 , 也非一劳永逸之策 。幸好此时 , 有了云存储!


推荐阅读