我们使用机器学习技术将英文博客翻译为简体中文。您可以点击导航栏中的“中文(简体)”切换到英文版本。
开发可移植的 亚马逊云科技 Lambda 函数
这篇博客文章由首席无服务器专家解决方案架构师 Uri Segev 撰写
在开发新应用程序或对现有应用程序进行现代化改造时,您可能会面临一个难题:使用哪种计算技术?像
- 认为成本较高或难以估算成本
- 这是一种范式转变,需要学习弥合知识鸿沟
- 对 Lambda 功能和用例的误解
- 担心使用 Lambda 会导致锁定
- 对非无服务器平台和工具的现有投资
这篇博文建议了开发便携式 Lambda 函数的最佳实践,如果您以后选择这样做,可以轻松地将代码移植到容器中。通过这样做,你可以避免锁定,并以无风险的方式尝试无服务器方法。
这篇博文的每个部分都描述了在编写可移植代码时需要考虑的事项,以及将此代码从 Lambda 迁移到容器所需的步骤(如果您以后选择这样做)。
便携式 Lambda 函数的最佳实践
将业务逻辑和 Lambda 处理程序分开
Lambda 函数本质上是事件驱动的。
当特定事件发生时,它通过调用其处理方法来调用 Lambda 函数。
处理器方法接收一个
事件
对象,该对象包含有关函数调用原因的信息。函数执行完成后,它将从处理程序方法返回。无论从处理程序返回什么,都是函数的返回值。
要编写可移植代码,我们建议仅使用处理程序方法作为 Lambda 运行时(事件对象)和业务逻辑之间的接口。使用Hexagonal架构术语,处理程序应该是调用端口(即业务逻辑暴露的接口)的驱动适配器。处理程序应从事件对象中提取所有必需的信息,然后调用实现业务逻辑的单独方法。
当该方法返回时,处理程序会以函数调用者期望的格式构造结果并将其返回。我们还建议将处理程序代码和业务逻辑代码拆分成单独的文件。如果您选择稍后迁移到容器,则只需迁移业务逻辑代码文件而无需进行其他更改。
以下伪代码显示了一个 Lambda 处理程序,该处理程序从事件对象中提取信息并调用业务逻辑。业务逻辑完成后,处理程序将响应放在函数的返回值中:
以下伪代码显示了业务逻辑。它位于单独的文件中,不知道它是从 Lambda 函数中调用的。这是纯粹的逻辑。
这种方法还可以更轻松地在业务逻辑上运行单元测试,而无需构造事件对象和调用 Lambda 处理程序。
如果您稍后迁移到容器,则可以使用下一节所述的新接口代码将业务逻辑文件包含在容器中。
事件源集成
Lambda 函数的一个好处是事件源集成。例如,如果您将 Lambda 与
以下伪代码显示了 SQS 事件源的 Lambda 处理程序是什么样子:
正如您在前面的代码中看到的那样,Lambda 函数几乎不知道它是从 SQS 调用的。没有 SQS API 调用。它只知道事件对象的结构,该结构特定于 SQS。
迁移到容器时,集成责任从 Lambda 服务转移到开发者身上。亚马逊云科技 中有不同的事件源,每个事件源都需要不同的方法来使用事件和调用业务逻辑。例如,如果事件源是
如果事件源是
以下伪代码显示了容器中与 SQS 的集成将是什么样子。请注意,您将丢失一些功能,例如批处理、筛选,当然还有自动缩放。
这里需要考虑的另一点是
将函数作为容器包装
Lambda 支持将函数打包为.zip 文件和容器镜像。要开发可移植代码,我们建议使用容器镜像作为默认打包方法。尽管您将函数打包为容器镜像,但您无法在其他容器平台上运行它,例如
Lambda 的 Dockerfile 示例如下所示:
如果您稍后转到容器,则需要更改 Dockerfile 以使用不同的基础映像,并调整定义如何启动应用程序的 CMD 行。这是对上一节中描述的代码更改的补充。
容器的相应的 Dockerfile 将如下所示:
当我们部署到不同的目标时,部署管道也需要更改。但是,建造文物保持不变。
每个实例单次调用
Lambda 函数在其自己的隔离运行时环境中运行。每个环境一次只能处理一个请求,这对于 Lambda 非常有用。但是,如果您将应用程序迁移到容器,则很可能会在单个进程中同时从多个线程调用业务逻辑。
本节讨论在同一进程中从单个调用转移到多个并发调用的各个方面。
静态变量
静态变量是指实例化一次然后在多次调用中重复使用的变量。此类变量的示例包括数据库连接或配置信息。
为了进行函数优化,特别是为了减少冷启动和热函数调用的持续时间,我们建议在函数处理程序之外初始化所有静态变量并将其存储在全局变量中,以便进一步的调用能够重用它们。
我们建议使用作为业务逻辑模块的一部分编写并从处理程序外部调用的初始化函数。此函数将信息保存在全局变量中,业务逻辑代码在调用中重复使用这些信息。
以下伪代码显示了 Lambda 函数:
业务逻辑代码将如下所示:
这同样适用于容器。你通常会在进程启动时初始化静态变量,而不是为每个请求初始化。迁移到容器时,你所需要做的就是在启动主应用程序循环之前调用初始化函数。
如您所见,业务逻辑代码没有变化。
数据库连接
由于 Lambda 函数在运行时环境之间没有任何共享,因此与容器不同,它们在连接到关系数据库时不能依赖连接池。出于这个原因,我们创建了
要编写便携式 Lambda 函数,我们建议使用具有单个连接的连接池对象。在发出数据库请求时,您的业务逻辑代码将始终要求池中的连接。您仍然需要使用 RDS 代理。
如果您以后迁移到容器,则可以在不做进一步更改的情况下将池中的连接数增加到更大的数量,并且应用程序将在不占用数据库负担的情况下进行扩展。
文件系统
Lambda 函数附带一个大小在 512
MB 到 10 GB 之间的可写的 /tmp
文件夹。由于每个函数实例都在隔离的运行时环境中运行,因此开发人员通常为存储在该文件夹中的文件使用固定文件名。如果您在容器中使用多个线程运行相同的业务逻辑代码,则不同的线程将覆盖其他线程创建的文件。
我们建议在每次调用中使用唯一的文件名。在文件名后附加 UUID 或其他随机数。处理完文件后将其删除,以免空间不足。
如果您稍后将代码移至容器,则无事可做。
便携式 Web 应用程序
如果你开发一个 Web 应用程序,还有另一种方法可以实现可移植性。您可以使用
从容器移植到 Lambda
这篇博文演示了如何开发可轻松移植到容器的便携式 Lambda 函数。总体而言,考虑这些建议还可以帮助开发可移植代码,从而允许您将容器移植到 Lambda 函数。
需要考虑的一些事项:
- 将业务逻辑与容器中的接口代码分开。接口代码应与事件源交互并调用业务逻辑。
- 由于 Lambda 函数只有 /tmp 可写文件夹,因此请将其复制到您的容器中(即使您可以写入不同的位置)。
结论
这篇博文建议了开发 Lambda 函数的最佳实践,让您在不冒锁定风险的情况下获得无服务器方法的好处。
通过遵循这些最佳实践,将业务逻辑与 Lambda 处理程序分开、将函数打包为容器、处理 Lambda 对每个实例的单次调用等,您可以开发可移植的 Lambda 函数。因此,如果您选择稍后迁移到容器,则可以毫不费力地将代码从 Lambda 移植到容器。
在开发下一个应用程序时,请参阅这些最佳实践和代码示例,以简化无服务器方法的采用。
如需更多无服务器学习资源,请访问
*前述特定亚马逊云科技生成式人工智能相关的服务仅在亚马逊云科技海外区域可用,亚马逊云科技中国仅为帮助您发展海外业务和/或了解行业前沿技术选择推荐该服务。