原文地址:https://segmentfault.com/a/1190000007122250?utm_source=weekly&utm_medium=email&utm_campaign=email_weekly

静态资源(Static Resources): js、css、img 等非服务器动态运行生成的文件。

在一般的网站中,静态资源使用频率高,流量占用大。对于有追求(访问量稍大)的网站,都会把静态资源放置到 CDN 服务器,不占用业务服务器的网络网络带宽,而达到更好的用户体验。那大公司的静态资源是怎么发布的呢?

发布要求

1. 静态资源必须走 CDN

如非自建 CDN,那我推荐 又拍云。(我『发四』绝壁没拿广告费)。

2. 平滑升级--非覆盖式发布

在不修改线上静态资源的情况下,发布新的静态资源。即 CDN 上的静态资源必须共存多个静态资源版本。

负责百度 FIS 的核心功能开发者张云龙已经给出了 部署的方案,非覆盖式发布。他的文中提到:

  1. 先部署页面,再部署资源:在二者部署的时间间隔内,如果有用户访问页面,就会在新的页面结构中加载旧的资源,并且把这个旧版本的资源当做新版本缓存起来,其结果就是:用户访问到了一个样式错乱的页面,除非手动刷新,否则在资源缓存过期之前,页面会一直执行错误。
  2. 先部署资源,再部署页面:在部署时间间隔之内,有旧版本资源本地缓存的用户访问网站,由于请求的页面是旧版本的,资源引用没有改变,浏览器将直接使用本地缓存,这种情况下页面展现正常;但没有本地缓存或者缓存过期的用户访问网站,就会出现旧版本页面加载新版本资源的情况,导致页面执行错误,但当页面完成部署,这部分用户再次访问页面又会恢复正常了。

3. 精确的缓存控制——文件版本号

更新静态资源,只更新修改的部分。不能修改未更新的资源的引用。

在 CDN 的配置中,会把缓存的时间设置很长。文件的更新就需要一种灵活的机制,也不能每次发布版本就把所有文件更新一遍,全量更新静态资源对于用户和服务提供者来说都是非常不友好的,极度浪费 CDN 资源和用户的流量。
比如: a.html 页面引用了 100 个静态资源文件,但是本次发版本只修改了一个a.js文件。那么就只能修改 a.js 文件的引用,可能修改为 a.hash.js

解决办法:根据文件的内容算出一个值,如果文件内容不改变,那么这个值就不会改变。

张云龙的文章中给出了『精确的缓存控制』的原理————我们会很自然的联想到利用 数据摘要要算法 对文件求摘要信息,摘要信息与文件内容一一对应,就有了一种可以精确到单个文件粒度的缓存控制依据了。

Grunt,gulp 也有对应的插件用来解决这个问题。如果你使用 webpack,那么可以配置下 chunkhash 或者 hash。更厉害的,文章末尾还有相关的解决方案干活。

发布和问题

1. 发布流程

如果我们把静态资源的实际访问域名设置为 res.company.com

  1. 功能开发结束后,开发人员把静态资源提交到测试环境 Git 仓库。
  2. 测试人员在测试环境通过绑定 host 的方式测试,测试通过后,进入发布阶段,需要静态资源代码提交到产品环境 Git 仓库。(使用测试和产品两个仓库,是为了测试和产品环境完全隔离)
  3. 源站(origin.res.company.com)从产品环境 Git 拉取静态资源的代码,并且为静态资源提供访问服务(nginx 等),该域名不提供文件缓存服务,仅仅作为 CDN 的回源地址。所以代码中一定不要写源站的信息。
  4. 在 CDN 服务商提供的空间中,将回源地址配置为源站(origin.res.company.com)。
  5. CDN 服务器会提供一个默认的访问域名,比如 cdn-1.res.cdn_company.com, 然后把实际需求访问的静态资源域名res.company.com CNAME 到 cdn-1.res.cdn_company.com
  6. 代码发布完毕。用户访问的时候,会访问到最近的 CDN 阶段。CDN 结点去文件中心取文件,如果不存在文件则回源到源站,获取文件。(有些 CDN 产品提供文件存储功能,异地备份)
  7. 通过这样的流程,就能让用户访通过 CDN 访问到我们的实际资源。

2. 怎么做到多个项目共存

结论:我们会按照目录来划分业务的静态资源。

res.company.com 域名下面会共存多个业务。A 业务、B 业务都是使用同样的发布方案。
比如某静态资源的访问路径为:http://res.company.com/:业务名/js/test.de5b0b0c.js

多个团队可以使用同一个静态资源仓库,res.git,然后按照目录来区分业务。web 服务器直接指向到 res/即可。静态资源更新只需要 pull 代码即可,而不需求做其他改动。

3. 是否同步源站文件到 CDN

结论:同步源站文件到 CDN 能确保实现非覆盖式发布。

又拍云等 CDN 服务商都提供了这些贴心的服务,就算你提交了新的同名文件,那也是不会生效的。

  1. 终端用户就近访问 又拍云 加速节点上的资源
  2. 若 又拍云 加速节点上不存在资源,则回源获取资源
  3. 回源获取的资源持久化存储到 又拍云
  4. 返回资源内容给终端用户
  5. 当终端用户再次访问相同的资源时,又拍云 的加速节点将直接返回资源内容,不需要重新回源获取

另外我还做了测试,在未做源站迁移的情况下,删掉源站文件,会出现大面积的 404 响应。

大公司是怎么发布静态资源的