2023年5月1日

加速无服务器应用程序的最佳实践

应用程序的快速性能对于提供出色的用户体验至关重要!在本文中,我们将探讨优化无服务器应用程序中冷启动和处理程序性能的陷阱和最佳实践。

Best Practices To Speed Up Your Serverless Applications

目录

简介

通过函数即服务(FaaS)的无服务器部署范式允许开发人员以可扩展且经济高效的方式轻松部署其应用程序。然而,这种便利性和灵活性伴随着一系列需要注意的复杂性。

在以前使用长时间运行服务器的部署模型中,只要您的服务器正常运行,您的执行环境就始终可用。这使得您的应用程序能够立即响应传入的请求。

新的无服务器范式要求我们开发人员找到方法来确保您的函数尽快可用并响应请求。

无服务器函数中的性能陷阱

在无服务器环境中,您的函数可以缩减到零。这使您可以将运营成本降至最低,但确实会带来技术成本。当您的函数没有可用实例来响应请求时,必须实例化一个新实例。这被称为冷启动。

注意:有关冷启动的详细解释以及我们如何在使用Prisma ORM时尽可能缩短冷启动时间,请阅读我们最近的文章:我们如何将Prisma的无服务器冷启动速度提升9倍

缓慢的冷启动会导致您的用户体验非常糟糕,并最终降低他们对您产品的体验。这是问题1。

除了冷启动问题,实际处理函数(handler function)的性能也极其重要。无服务器应用程序通常由许多通过HTTP、事件总线、队列等协议相互交互的小型、独立的函数组成。

单个函数之间的这种内部通信在每个请求上都会形成一个依赖链。如果其中一个函数速度极慢,它将影响链中的其余部分。因此,处理程序性能是问题2。

FaaS中优化性能的最佳实践

在Prisma,我们过去几个月深入研究了无服务器环境,并优化了Prisma在其中的行为。在此过程中,我们发现了许多最佳实践,您可以在自己的应用程序中采用这些实践,以尽可能保持高性能。

本文的其余部分,我们将介绍我们发现的一些最佳实践。

将函数托管在与数据库相同的区域

任何时候您托管需要访问传统关系数据库的应用程序或函数时,您都需要启动与该数据库的连接。这需要时间并伴随着延迟。您执行的任何查询也是如此。

您的目标是将时间和延迟降至绝对最低。目前最好的方法是确保您的应用程序或函数部署在与您的数据库服务器相同的地理区域。

Blog image

您的请求到达数据库服务器的距离越短,连接建立的速度就越快。在部署无服务器应用程序时,这是一件非常重要的事情,因为不这样做所产生的负面影响可能非常显著。

不这样做会影响以下操作所需的时间:

  • 完成TLS握手
  • 与数据库建立安全连接
  • 执行查询

所有这些因素都在冷启动期间激活,因此会影响使用带有Prisma的数据库对应用程序冷启动产生的影响。

在研究这对冷启动的影响时,我们羞愧地发现,我们最初的几次测试是在AWS Lambda的eu-central-1区域运行无服务器函数,而RDS PostgreSQL实例托管在us-east-1区域。我们迅速修复了这个问题,并且“之后”的测量清楚地显示了这可能对数据库延迟造成的巨大影响,无论是连接的创建还是执行的任何查询

Before

之前

After

之后

使用距离您的函数不够近的数据库将直接增加冷启动的持续时间,并且在处理热请求期间稍后执行任何查询时也会产生相同的开销。

在处理程序外部运行尽可能多的代码

考虑以下无服务器函数

AWS Lambda在某些情况下,在函数执行环境的初始启动期间会为虚拟环境分配更多的内存和CPU。之后,在您的“热”函数调用期间,您的函数可用的内存和CPU实际上将保证是您函数配置中的配置值——这可能比函数外部少。

注意:如果您好奇,这里有一些资源可以解释上面提到的资源分配差异

通过将代码移到处理程序范围之外,可以利用这些知识来提高函数性能。这确保了在环境拥有更多可用资源时执行处理程序外部的代码。

例如,您可能在无服务器函数中执行类似操作

上述处理函数计算斐波那契数列中的第40个数字。计算完成后,您的函数将继续处理请求并最终返回响应。

将其移到处理程序外部,可以在环境拥有更多可用资源时进行该计算,并且仅运行一次,而不是每次调用都运行。

更新后的代码将如下所示

另一件需要记住的事情是,AWS Lambda支持顶级await,这允许您在处理程序外部运行异步代码。

我们发现显式地在处理程序外部运行Prisma Client的$connect函数对您的函数性能有积极影响

保持函数尽可能简单

无服务器函数旨在成为非常小的、独立的代码片段。如果您的函数的JavaScript和依赖树庞大、复杂或分布在许多文件中,您会发现运行时读取和解释它们需要更长时间。

以下是一些您可以采取的措施来提高启动性能

  • 只包含您的函数实际完成其工作所需的代码
  • 不要使用加载大量您不需要的东西的库和框架

这里的普遍观点是:需要解释的代码越少,依赖树越简单,请求处理的速度就越快。

不要做超出需要的工作

任何在每次函数调用时可能重复使用的值计算或昂贵操作都应作为变量缓存到处理程序范围之外。这样做可以避免在每次调用函数时都执行这些昂贵操作。

考虑一种情况,从数据库中获取一个不经常更改的值,例如可配置的重定向

虽然这段代码可以工作,但每次调用函数时都会运行查询以查找重定向。这并不理想,因为它需要访问数据库以查找您在上次调用期间已经找到的值。

更好的写法是首先在处理程序外部检查缓存的值。如果未找到,则运行查询并将结果存储以备下次使用

现在,只有在您的函数第一次被调用时才会运行查询。随后的任何调用都将使用缓存的值。

预置并发

最后要考虑的一点是,如果您正在使用AWS Lambda,可以利用预置并发来保持您的Lambda函数“温暖”。

根据AWS文档

注意:预置并发会初始化所需数量的执行环境,以便它们准备好立即响应您的函数调用。请注意,配置预置并发会产生AWS账户费用。

这允许您维护指定数量的可用执行环境,这些环境可以响应请求而无需冷启动。

虽然这听起来很棒,但有几点需要注意:

  • 使用预置并发会产生额外费用
  • 您的应用程序永远不会缩减到0

这些是重要的考虑因素,因为对于您的特定场景来说,增加的成本可能不值得。在采用此措施之前,我们建议您评估它为您的应用程序带来的价值,并考虑增加的成本是否合理。

结论

在本文中,我们探讨了一些我们建议开发人员使用Prisma ORM构建和部署无服务器函数的最佳实践。本文中提到的增强功能和最佳实践并非详尽列表。

快速回顾一下,我们建议您:

  • 将您的数据库托管在尽可能靠近部署函数的位置
  • 尽可能在处理程序外部运行代码
  • 尽可能缓存可重用的值和计算结果
  • 保持您的函数尽可能简单
  • 如果您愿意承担财务权衡,请考虑使用预置并发

感谢您的阅读,希望这些信息对您有所帮助!

不要错过下一篇文章!

订阅 Prisma 新闻通讯

© . This site is unofficial and not affiliated with Prisma Data, Inc.