使用 Appwrite 0.13,让您的无服务器函数运行速度更上一层楼。
🙋 什么是云函数?
💻 架构概述
📦 依赖管理
⏳ 基准测试
💪 工程挑战
🌅 结论
🙋 什么是云函数?
云函数是一种扩展云服务提供商服务的方式,它可以执行您的代码并添加以前不存在的功能。许多服务都具备这种功能!例如 AWS Lambda、Google Cloud Functions 和 Vercel Functions。
亚马逊在 2014 年推出 AWS Lambda 时率先进入云函数领域,四年后谷歌紧随其后,于 2018 年向所有人开放了 Google Cloud Functions。这一切促成了今天的 Appwrite,它推出了第二代 Appwrite Functions,其执行模型得到了显著改进。
💻 架构概述
那么,与之前的版本相比,有哪些变化呢?正如前面提到的,我们的执行模型已经过全面重新设计,以速度为核心。
我们首先需要了解原有的执行模型是如何运作的,才能理解这些变化。
上图展示了原始函数执行模型的工作原理。每次执行都会执行此操作。因此,本质上,每次执行都会启动一个新的 Docker 容器。这种流程耗时较长,并且会给宿主机带来相当大的压力。
更新后的模型要复杂得多(尽管这里展示的是一个经过大幅简化的示意图),它不再每次执行都启动一个新的运行时环境。不仅如此,每个运行时环境现在都内置了一个 Web 服务器来处理执行。我们现在使用 HTTP 请求而不是命令行执行,从而大大提高了执行速度。使用这种执行方式确实会给用户带来一些变化。例如,用户必须输入脚本的文件名,而不是完整的命令。他们现在还需要导出函数。更多详情请参阅我们的函数文档。
📦 依赖管理
还记得以前需要手动将依赖项打包到函数代码中吗?现在不用了!Appwrite 0.13 为函数引入了构建阶段,它可以自动安装所需的所有依赖项。构建阶段还会构建已编译的运行时环境,使其可以执行。某些语言可能需要特定的步骤,因此我们建议您查看更新后的函数文档。
⏳ 基准测试
得益于我们全新的执行模型,函数速度现在比以前快了 10 倍以上!我们甚至首次在 Appwrite 中引入了对编译型语言的支持,包括 Rust 和改进后的 Swift 运行时,带来了令人惊叹的执行速度。不妨让我们来看看一些可靠的基准测试数据,比较一下 0.12 和 0.13 版本在执行时间和规模上的差异。
我们的第一个测试是使用 NodeJS 17.0 的异步执行模型实现一个简单的“Hello World!”响应。我们使用异步方法来比较这两个版本,因为 0.12 不支持同步函数,直接比较异步和同步是不公平的。我们使用k6作为基准测试工具,所有脚本都在本地设备上运行。为了正确地进行基准测试,我们准备了一个代理,它可以冻结请求并统计在特定时间段内有多少请求到达该代理。流程如下:
- 创建 Appwrite 函数标签并激活它
- 启动一个启用请求冻结功能的代理服务器。
- 运行 k6 基准测试 60 秒
- 解冻代理服务器
- 等待所有操作完成
在这种配置下,k6 将在 60 秒内创建尽可能多的执行实例。在此期间,只有第一个执行实例会启动,但只有在代理服务器冻结后才会完成。这种冻结机制确保在测试 Appwrite 可以创建多少个执行实例时,不会因执行实例而占用过多 CPU 资源。
一分钟后,k6 基准测试完成,我们解冻代理服务器,从而恢复执行队列。我们等待所有执行完成,同时跟踪代理服务器上的计时数据。
这次基准测试的结果令人惊叹!Appwrite 0.12 生成了20700 个执行,而 0.13 版本则生成了20824 个执行。由于我们在这个版本中没有重构创建过程,因此 0.6% 的提升幅度在意料之中。但从函数执行时间的对比来看,性能的提升之大令人震惊。Appwrite 0.12 的执行速度为每分钟360 次,而 Appwrite 0.13 的执行速度更是惊人地达到了每分钟5820 次!
我们进行的第二个测试是在实际场景中使用 Appwrite,以查看平均执行时间的改进情况。为此,我们在六个不同的运行时环境中准备了相同的脚本。我们使用了一个将电话号码转换为国家/地区代码的示例脚本,该脚本来自Open Runtimes 示例 GitHub 存储库。该脚本运行了以下命令:
- 从 Appwrite Locale SDK 获取国家/地区电话前缀数据库
- 验证请求负载
- 在前缀中查找匹配项
- 返回国家信息
为了对这些功能进行基准测试,我们使用了相同的 k6 技术,但这次流程更简单:
- 创建功能部署并激活它
- 运行基准测试 60 秒
- 等待执行完成
- 计算平均执行时间
我们在 0.12 和 0.13 版本中,分别使用六种不同的运行时环境(语言)运行了这些脚本,结果令人震惊!最令人惊讶的结果是使用 Dart 语言,我们竟然在不到一毫秒的时间内运行了该脚本!
说到这里,我们先来说说 Dart。Dart 是一种编译型语言,这意味着编译后的结果是二进制代码。这使得它的执行速度极快,因为所有代码都以 0 和 1 的形式准备就绪,可以直接在服务器上运行。由于 0.12 版本对编译型语言的支持不佳,我们函数的平均执行时间达到了 1895 毫秒。在 0.13 版本中运行相同的脚本时,我们的预期很高,但平均执行时间竟然骤降至0.98 毫秒,这着实让我们震惊不已!
我们继续来看另一种常用语言——NodeJS。同样的函数,之前在 0.12 秒内需要 325 毫秒才能执行完毕,现在在 0.13 秒内仅需1.45 毫秒!这个结果让我非常惊讶,因为我没想到解释型语言能有如此出色的性能。
为了进一步比较 NodeJS,我们又对比了Deno,其 0.13 版本的结果与 NodeJS 类似,平均耗时约为3 毫秒。差距略小,因为该函数在 0.12 版本中仅耗时145 毫秒。
我们继续使用PHP进行测试,PHP 是一种广为人知的语言,互联网上有很多网站都在使用它。在 0.12 版本中,平均延迟为 106 毫秒;而在 0.13 版本中,平均延迟降至7 毫秒。
我们继续使用Python和Ruby进行测试,结果相似。在 0.12 版本中,Python 的执行时间为 254 毫秒,而 Ruby 的平均执行时间为 358 毫秒。令人难以置信的是,在 0.13 版本中,Python 的执行时间仅为11 毫秒,Ruby 的速度也略有提升,为9.5 毫秒。
正如您所看到的,此次版本发布后执行速度显著提高,我们期待看到使用 Appwrite 的开发者将如何利用这些新功能。
💪 工程挑战
为了实现同步执行并优先考虑速度,我们决定放弃大多数工作进程使用的基于任务的系统,转而为 Appwrite 创建一个名为执行器 (executor) 的新组件。执行器将负责所有编排和执行职责,并从函数工作进程中移除 Docker 套接字。执行器是一个使用Swoole和Utopia构建的 HTTP 服务器,并使用各种 Appwrite 库与数据库进行交互。
最初的挑战之一是创建一个编排库,以便将来如果需要,可以轻松切换到其他编排工具,例如 Kubernetes 或 Podman。这一改变将使我们能够使用其他编排工具,例如 Kubernetes 或 Podman,并允许用户使用他们喜欢的编排工具运行 Appwrite。目前,我们为该库提供了两个适配器:Docker CLI 和 Docker API;不过,我们计划随着时间的推移不断扩展适配器的选择范围。
下一个挑战是,Swoole 的协程 cURL 钩子存在一些 bug,而我们正是利用这个钩子与 Docker API 版本的编排库进行通信。这些 bug 迫使我们使用 Docker CLI 适配器,这导致了更长的等待时间和速率限制问题,进而造成了高负载下的不稳定。
我们提出了一种解决方案,即引入类似队列的系统来使用 Docker。这项改动会略微降低运行时的启动速度,但实际执行始终使用 cURL 请求,因此执行时间不会受到此改动的负面影响。
🌅 结论
通过这些改进,我们希望 Appwrite 能提供更多功能,帮助您构建梦想中的应用程序,同时又不牺牲速度和灵活性。我们都非常期待看到大家利用这些新功能和函数执行速度的提升,创造出怎样的作品。
我们鼓励您加入我们的Discord 服务器,在那里您可以随时了解 Appwrite 的最新动态,并在需要时获得 Appwrite 开发人员的帮助!
文章来源:https://dev.to/appwrite/take-your-serverless-functions-to-new-speeds-with-appwrite-013-5868








