使用Worker KV将cdnj迁移到无服务器

2020-09-12 00:07:48

CloudFlare支持cdnjs,这是一个开源项目,通过Cloudflare的网络交付流行的JavaScript库和资源来加速网站。自从12月份的重大更新以来,我们专注于重塑cdnjs以实现可伸缩性和弹性。今天,我们非常高兴地宣布Cloudflare如何交付cdnjs-使用Cloudflare Workers及其分布式键值存储Workers KV迁移到无服务器基础架构!

对于那些不熟悉的人来说,cdnjs是描述JavaScript(JS)的内容交付网络(CDN)的缩写。CDN只是指提供互联网内容的地理上分布的服务器网络,无论它是迷因、猫视频还是HTML页面。在我们的案例中,CDN指的是Cloudflare不断扩大的由200多个分布在全球的数据中心组成的网络。

这就是与您相关的原因:它使页面加载速度快如闪电。实际上,您访问的每个网站都需要获取JS库才能加载,包括这个网站。假设您访问一个位于悉尼的网站,该网站包含来自jQuery的本地文件,jQuery是一个流行的库,在76.2%的网站中都可以找到。如果您位于纽约,您可能会注意到延迟,因为获取文件的时间很容易超过300ms-更不用说TLS握手所需的往返时间了。但是,如果网站使用cdnjs.cloudflare.com引用jQuery,您可以从布法罗最近的Cloudflare数据中心检索文件,从而将延迟降低到惊人的20ms。

虽然cdnjs在幕后运作,但超过11%的网站都在使用cdnjs,这使得互联网成为一个更快、更可靠的地方。7月份,cdnjs服务了近1900亿个请求-一个巨大的3.46PB数据。

过去,Cloudflare核心数据中心的许多负载平衡机器会定期从后备存储中提取cdnjs文件,充当cdnjs.cloudflare.com的来源。当请求新文件时,Cloudflare会对其进行缓存,从而可以快速从我们的任何数据中心获取该文件。

后备存储是以开源GitHub存储库的形式包含JS、CSS和其他Web库的目录。这意味着任何人-包括您-都可以为此做出贡献,但要经过审查和其他过程。

这篇博客文章将解释为什么我们改变了cdnjs背后的基础设施,使其更快、更可靠、更易于维护。首先,我们会讨论社区对社区发展计划的贡献,概述旧制度所带来的痛苦和关注。然后,我们将探索移民到工人KV的好处。之后,我们将深入研究新的架构,以及网站和cdnjs API的升级。最后,我们将回顾cdnjs的历史,以及它未来的发展方向。

对于非技术性读者,拉入请求(PR)是合并您对存储库所做更改的请求。传统上,如果您想要在cdnjs中包含JavaScript库,您需要首先在GitHub上创建一个PR to cdnjs/cdnjs,其中包含一个描述您的包的JSON文件,以及您希望包含的任何版本的附加文件。一旦你的PR被我们的老机器人批准,手动审查,然后由维护员合并,你的包就会与cdnjs集成在一起。

听起来很简单,对吧?你可以分叉回购,克隆它,然后复制粘贴几个文件,不是吗?

一点儿没错。如果您有几个小时的时间来刻录,一个区分大小写的文件系统,以及几百GB的空闲磁盘空间来git克隆300 GB的repo,那么贡献文件就很容易了。如果你时间紧迫--没问题,你总是可以利用你对git稀疏结账的高级知识来完成这项工作。不认识吉特吗?只需通过GitHub的UI一次手动添加一个文件。

我想你明白我的意思了。我知道,当我天真地花了10个小时克隆repo,却发现MacOS默认不区分大小写时,我确实做到了。

然而,更新cdnjs不仅对贡献者来说很困难,而且对维护者来说也很困难。从历史上看,社区能够直接贡献版本文件,这可能是潜在的恶意行为。这给维护人员带来了大量工作,需要他们手动检查每个文件,根据官方库源区分文件,并运行恶意软件检查。那么,包在cdnjs中之后是如何更新的呢?在描述每个包的JSON文件中,有一个可选的自动更新定义,告诉机器人在哪里查找新版本的库。如果存在,当您的包从NPM或GitHub发布新版本时,机器人会下载它,将文件推送到cdnjs/cdnjs,并将计算子资源完整性(SRI)散列推送到cdnjs/SRI。如果缺少自动更新属性,则您有责任进行手动PR以使用任何未来版本更新cdnj。

4月,在我们的一个核心数据中心进行维护期间,一名技术人员意外断开了为我们的其他数据中心提供所有外部连接的电缆,导致数据中心离线约四个小时。这一事件为cdnjs敲响了第一个警钟,特别是因为受影响的数据中心容纳了cdnjs原始web服务器。在本例中,我们确实有一个备份在外部提供程序上运行,但真正节省我们的是Cloudflare的全局缓存,它将宕机的影响降至最低,因为只有未缓存的资产无法加载。

我们开始思考如何提高我们为cdnj提供服务的可靠性和性能。我们直接去了Cloudflare Workers,这是我们自己的边缘开发平台。Worker中内置的一个功能强大的工具是Worker KV,这是一种针对高读取率应用程序优化的低延迟、全球分布式键值存储。

我们将两者放在一起,意识到不是提取cdnjs/cdnjs存储库并从磁盘提供文件,我们可以完全切断物理机,将数据分发到世界各地,并直接从边缘提供文件。这样,cdnjs将能够从任何源数据中心故障中恢复,同时还可以提高其可伸缩性。

乍一看,使用工人KV的决定是不需要动脑筋的。由于cdnjs中的文件从不更改,但需要频繁读取,所以Workers KV非常适合。

但是,当我们计划迁移时,我们开始担心cdnj中有超过700万资产,毫无疑问会存在超过Workers KV 10MiB价值限制的文件。经过调查,我们发现数百个cdnjs文件过大,其中大部分是JavaScript Source Map。

然后我们想到了这个主意。我们可以将cdnjs文件的压缩版本存储在Workers KV中,这不仅解决了文件过大的问题,而且优化了我们提供文件的方式。

如果你付了网费,你就会知道带宽很贵!出于这个原因,所有现代浏览器都会在压缩的Web内容可用时尝试获取这些内容。类似地,在Cloudflare中,我们经常尝试动态压缩以减少带宽,在压缩内容被接受时总是将其提供给眼球。因此,我们决定提前压缩所有cdnjs文件,用优化的Brotli和gzip格式将它们写入Workers KV。这样,与动态压缩相比,我们可以提高压缩级别,因为我们不再有延迟要求。

今天,如果您想将JavaScript库包含在cdnjs中,您首先需要在GitHub上为我们的新存储库cdnjs/Packages创建一个PR。Repo很容易克隆,大小为50MB,由数千个JSON文件组成,每个文件描述一个cdnjs包,以及如何从npm或git自动更新它。一旦您的文件通过我们的自动CI验证-由新的机器人提供支持-并由维护员合并,您的软件包将自动注册到我们的自动更新服务中。

在新系统中,安全性和可维护性被放在首位。对于初学者来说,cdnjs版本文件是由我们的bot创建的,将合并新版本时出现人为错误的可能性降至最低。虽然cdnjs/package中的JSON文件是由容易出错的人添加的,但我们的bot在维护人员批准之前会检查它们。根据JSON模式自动验证每个文件,并检查其在NPM或GitHub上的受欢迎程度。

当机器人发现新版本时,它会将文件的Brotli和gzip压缩版本推送到Workers KV中的文件命名空间。对于每个条目,机器人在Worker KV中为ETag和Last-Modified HTTP标头写入一些元数据。与以前类似,bot还计算解压缩文件的子资源完整性(SRI)散列,但现在将它们推送到Workers KV中的SRIS名称空间。

然后,当从cdnjs.cloudflare.com请求新文件时,Cloudflare Worker将检查客户端的Accept-Encoding头,从Worker KV获取Brotli或gzip压缩版本及其ETag和Last-Modified元数据。当压缩文件通过Cloudflare传回时,它会被缓存以备将来的请求使用,并在需要时进行动态解压缩。

目前,仍有少数文件超过工人KV的大小限制。因此,如果Cloudflare worker无法从Workers KV检索文件,则会从原始git repo支持的源获取该文件。在接下来的几个月里,我们计划逐步拆除这些基础设施。

在cdnjs项目的主页上,您将看到一个由Matt创建的漂亮的新测试版网站。测试版网站由Vue和Nuxt构建,完全由cdnjs API提供支持。因此,它始终是最新的软件包信息,并且需要较低的资源使用量来为站点提供服务-站点在第一个页面加载后完全在客户端运行-帮助我们随着cdnjs永无止境的增长而扩展。

事实上,cdnjs API还增强了其可伸缩性,得益于与我们在cdnjs和Workers KV中看到的架构相近的无服务器架构。

在迁移到Workers KV之前,cdnjs API依赖于一个定期计划的过程,该过程涉及生成大约300MB的元数据。然后,cdnjs API的后端会将这个巨大的“Package.min.js”文件取到内存中,并使用它来操作API。如果您很好奇,该文件仍在这里托管,但请注意-它可能会滞后于您的浏览器!类似地,文件SRI被推送到cdnjs/SRI,API在本地克隆该文件以服务SRI响应。

在将所有cdnjs文件(在允许的大小限制内)移动到Workers KV后,这些遗留过程变得不可持续,需要数百万次读取和不合理的时间。因此,我们决定将找到的所有元数据上传到Workers KV。我们将元数据分为四个名称空间-一个用于包级别的元数据,一个用于特定于版本的元数据,一个包含聚合元数据,一个用于文件SRI。

与cdnjs的无服务器设计类似,Cloudflare工人位于metadata.speedcdnjs.com之上,使用几个公共端点提供来自Workers KV的数据。目前,cdnjs API与这些端点完全集成,随着cdnjs的不断扩展,它们提供了一种优雅的解决方案。

自2011年1月诞生以来,cdnjs始终深深植根于透明度,从社区中汲取力量。即使cdnjs的规模呈爆炸式增长,其创始人Ryan Kirkman和Thomas Davis在2011年6月与我们合作,该项目在GitHub上仍然是完全开源的。

随着时间的推移,创始人保持活跃变得越来越困难,他们严重依赖社区的支持。由于预算几乎为零,对存储库的访问也很少,cdnjs的核心维护人员每天都面临着保持项目活力的挑战。

去年,这让我们联系了创始人,他们很高兴能得到我们对该项目的帮助。随着Cloudflare角色的增加,cdnjs一如既往地稳定,拥有来自Cloudflare和社区的活跃成员。

然而,随着我们消除对遗留系统的依赖,并将文件存储在Workers KV中,人们担心cdnjs将成为专有的。别担心,我们正在努力确保cdnjs保持尽可能透明和开源。为了帮助社区审计Worker KV的更新,有一个新的存储库cdnjs/logs,机器人使用它来记录所有与Worker KV相关的事件。此外,任何人都可以通过从cdnjs API获取SRI来验证cdnjs文件的完整性。

总体而言,过去的一年对cdnjs来说是一个动荡的时期,但它的所有缺点都起到了危险信号的作用,帮助我们建立了一个更好的系统。最近,我们降低了依赖单个位置的物理机的风险,将cdnj迁移到其文件存储在Workers KV中的无服务器基础架构。

今天,cdnjs得到了很好的照顾,短期内不会消失。特别要感谢维护员Sven和Matt,他们为这个项目创造了巨大的动力,他们从事从扩展cdnj到编辑这篇文章的一切工作。

展望未来,我们致力于使cdnjs尽可能透明。随着我们不断改进cdnjs,我们将发布更多的博客帖子以使社区保持最新。如果您感兴趣,请订阅我们的博客。毕竟,是社区让cdnj成为可能!特别感谢我们GitHub的积极贡献者和cdnjs社区论坛的成员们与我们站在一起!

CDNJS Cloudflare Workers KV开源速度和可靠性无服务器