Shopify如何在浏览器之外使用WebAssembly

2020-12-19 23:44:21

在Shopify,我们的目标是使大多数商人所需的一切变得容易,而其余的则变为可能。我们通过公开接口来查询,扩展和更改我们的平台,从而使剩下的一切成为可能。这些界面使合作伙伴的丰富生态系统能够解决各种问题。该生态系统的主要机制是“应用程序”,这是一个独立托管的Web服务,可通过网络与Shopify通信。该模型功能强大,但存在许多技术问题。合作伙伴无法提供可用的资源,因为他们必须构建可以在Shopify规模上运行的网络服务。即使合作伙伴的资源是无限的,与Shopify进行通信时所产生的网络延迟也不能将Apps用于时间敏感的用例。

我们希望合作伙伴专注于利用他们的领域知识来解决问题,而不是管理可伸缩的Web服务。为了实现这一点,我们保留了不受信任的合作伙伴代码的灵活性,但要在我们自己的基础架构上执行它。我们为该代码选择一种通用格式,以确保其性能,安全性和灵活性:WebAssembly。

“ WebAssembly(缩写为Wasm)是用于基于堆栈的虚拟机的二进制指令格式。 Wasm被设计为编程语言的可移植编译目标,从而可以在Web上为客户端和服务器应用程序进行部署。”

要了解更多信息,请参阅Mozilla的Lin Clark撰写的带插图的系列文章,其中包含有关WebAssembly及其历史的信息。

Wasm通常被表示为一种性能语言,它与浏览器中的JavaScript一起运行。但是,我们在浏览器外部执行Wasm,并且不涉及Javascript。 Wasm不仅是Java脚本的替代品,还为Web和非Web嵌入而设计。它解决了在不受信任的环境中执行程序执行的更普遍的问题,该问题存在于浏览器和代码执行引擎中。 Wasm满足我们的三个主要技术要求:安全性,性能和灵活性。

执行不受信任的代码是很危险的事情-本质上很难预测,并且有可能对Shopify的整个平台造成损害。尽管没有一种应用程序是完全安全的,但我们既需要防止安全漏洞,又要在发生安全漏洞时减轻其影响。

Wasm在基于沙盒的堆栈环境中执行,依赖于显式导入来允许与主机进行通信。因此,您无法在Wasm中表达任何恶意内容。您只能表达对虚拟环境的操纵并使用提供的导入。这与字节码不同,字节码引用了它们希望直接在语法中运行的计算机或操作系统。

Wasm还具有许多保护用户免受错误代码攻击的功能,包括受保护的调用堆栈和运行时类型检查。有关Wasm安全模型的更多详细信息,请访问WebAssembly.org。

在电子商务中,速度是商家推动销售所需要的竞争优势。如果我们交付给商家的功能没有正确地将加载时间权衡为定制价值,那么我们也可能根本不交付该功能。

Wasm旨在利用常见的硬件功能,在各种平台上提供接近本机的性能。寻求性能最佳化的浏览器开发人员社区可以使用它。结果,建立了Wasm和周围的工具,并将继续以性能为重点。

代码执行服务仅与使用它的开发人员富有成效的一样有用。这意味着以他们熟悉的多种语言提供一流的开发经验。作为字节码格式,Wasm是许多不同编译器的目标。这使我们能够支持多种语言供开发人员使用,而无需更改基础执行模型。

我们在目标和设计上有着根本的一致性,这提供了使用Wasm的“工程理由”。不仅如此,它还有更多的意义-与人员和技术有关。如果没有人在Wasm生态系统上工作,或者即使只是在当前状态下依靠生命支持,我们也不会使用它。 WebAssembly是一个充满活力的社区,它不断在开发新事物,并且还有很多潜力可挖掘。通过成为该社区的一部分,Shopify将从这种热情中显着受益。

我们也为自己的热情做出了贡献。我们正在收集用户反馈,讨论功能差距,最重要的是为我们依赖的开源工具做出了贡献。我们认为这是我们与WebAssembly社区之间建立健康互惠关系的开始,并且我们希望在将来扩大这些努力。

既然我们已经介绍了WebAssembly以及为什么要使用它,让我们继续执行它。

我们使用一种称为Lucet的开源工具(最初由Fastly编写)。作为一家公司,Fastly提供了一个可编程的边缘云平台。他们正试图使高容量,寿命短且不受信任的模块的执行更接近要求的位置。这与我们尝试使用合作伙伴代码解决的问题相同,因此使用相同的工具自然是合适的。

Lucet既是Wasm的运行时又是编译器。为了保证表示的安全性,模块以Wasm表示。回想一下,您无法在Wasm中表达任何恶意内容。 Lucet充分利用了这一点,并使用Wasm模块的验证作为安全检查。验证之后,该模块将编译为具有几乎裸机性能的可执行工件。它还支持提前编译,使我们能够在运行时准备好执行这些工件。 Lucet容器的启动时间令人印象深刻,为35μs。那是因为它是一个容器,根本不需要执行任何操作即可启动。如果您想了解整个情况,Fastly的首席技术官Tyler McMullan进行了精彩演讲,概述了Lucet及其工作原理。

我们将Lucet包装在Rust网络服务中,该服务管理模块的I / O和存储,我们将其称为Wasm引擎。 Shopify在运行时过程(通常是Web请求)中调用此引擎,以满足某些功能。然后,它将输出应用到呼叫站点的上下文中。此应用程序可能涉及折扣的创建,约束的实施或商人想要在平台内自定义的任何形式的同步行为。

以下是一些根据最近的性能测试得出的指标。在此测试期间,每分钟执行10万个模块,持续大约5分钟。这些模块包含了对购物车中购买的商品数量实施限制的简单实现。

该图表演示了执行模块所需时间的细分,包括使用容器进行I / O和执行模块的时间。 y轴是以毫秒为单位的时间,x轴是测试运行的时间。

浅紫色条显示了在Lucet中执行模块所需的时间,该模块的宽度徘徊在100μs左右。其余的小节处理I / O和引擎的细节,执行的总时间约为4毫秒。所有时间均为第99个百分位(p99)。为便于了解,我们将这些时间与我们的高性能在线商店渲染服务Storefront Renderer的请求时间进行比较:

该图表演示了一段时间后到Storefront Renderer的请求时间。 y轴是以秒为单位的请求时间。 x轴是获取值的时间。代表第99个百分位的浅蓝色线徘徊在700毫秒左右。

然后,如果我们认为模块执行过程所花费的时间通常不超过5毫秒,那么可以说Lucet执行对性能的影响可以忽略不计。

为了从我们的高性能执行引擎中获取价值,我们需要授权开发人员创建兼容的Wasm模块。 Wasm主要用作编译目标,而不是您手工编写的东西(尽管您可以手工编写Wasm)。这给我们留下了一个问题,我们将支持哪种语言以及在何种程度上支持。

从理论上讲,可以支持任何以Wasm为目标的语言,但是开发人员为遵守我们的API而花费的精力更好地集中于为商人解决问题。因此,我们选择为单一语言提供一流的支持,其中包括使开发人员快速启动和运行的工具。在Shopify,我们选择的语言是Ruby。但是,由于Ruby是一种动态语言,因此我们无法直接将其编译为Wasm。我们探索了涉及编译解释器的解决方案,但发现性能损失很大。因此,我们决定使用静态编译语言,并在将来重新考虑使用动态语言的可能性。

通过我们的研究,我们发现生态系统中的开发人员最熟悉Javascript。不幸的是,由于它是一种动态语言(如Ruby),因此Javascript被排除在外。相反,我们选择了一种具有类似于TypeScript语法的语言,称为AssemblyScript。

乍一看,有许多语言支持WebAssembly目标。不幸的是,我们无法使用两大类WebAssembly编译器:

生成特定于环境或语言的工件(即节点或浏览器)的编译器。 (例如:Asterius,Blazor)

旨在仅与特定运行时一起使用的编译器。这些编译器生成的模块依赖于特定于语言的导入。通常这样做是为了支持一种语言的标准库,该库需要某些系统调用或运行时功能可用。由于我们不想被锁定在某种语言或工具上,因此我们不使用这些编译器。 (例如:流明)

这些工具在适当的条件下是功能强大的工具,但并非针对我们的用例而构建。我们需要生成WebAssembly的工具,而不是由WebAssembly支持的工具。 AssemblyScript就是这样一种工具。

像WebAssembly领域中的许多工具一样,AssemblyScript仍在开发中。它缺少一些关键功能(例如关闭支持),并且仍然存在许多边缘错误。这就是社区重要性的体现。

自从我们于2019年首次使用Shopify以来,AssemblyScript的语言和工具就拥有一个活跃的爱好者和维护者社区,他们一直为Shopify提供支持。我们已经编写了语言服务器,在实现闭包方面取得了一些进展,并为编译器和周围的工具编写了错误修复程序。

我们还将AssemblyScript集成到我们自己的早期工具中。我们已经在Shopify CLI中建立了集成,开发人员可以通过命令行来创建,测试和部署模块。为了改善开发人员的人机工程学,我们提供了SDK,用于处理Shopify定义的对象(例如“ Money”)的底层实现问题。除了这些工具之外,我们还在构建系统,使合作伙伴可以监视其模块并在模块发生故障时接收警报。最终目标是使合作伙伴能够将其代码移动到我们的服务上,而不会失去他们在自己的平台上拥有的任何灵活性或可观察性。

当我们打破合伙人和商人之间的界限时,我们将商人与准备解决他们的问题的企业家联系起来。如果您对我们的代码执行可以如何帮助您以及您拥有或使用的应用程序有任何想法,请通过@ShopifyEng发推文。要了解有关Shopify上的Apps以及如何入门的更多信息,请访问我们的开发人员页面。

Duncan是Shopify的高级开发人员。他目前正在脚本团队工作,该团队致力于在Shopify中为商人和开发人员启用和管理不受信任的代码执行。

如果您喜欢使用开放源代码工具,并且对API设计和可扩展性充满热情,并且希望远程工作,那么我们始终在招聘!与我们联系或在我们的职业页面上申请。

来自构建和扩展Shopify的团队的故事,Shopify是领先的基于云的多渠道商业平台,为全球超过1,000,000家企业提供支持。