AWS Lambda
开发人员指南

使用 AWS Lambda 函数的最佳实践

以下是推荐的使用 AWS Lambda 的最佳实践:

函数代码

  • 从核心逻辑中分离 Lambda 处理程序 (入口点)。 这样您可以创建更容易进行单元测试的函数。在 Node.js 中可能如下所示:

    exports.myHandler = function(event, context, callback) { var foo = event.foo; var bar = event.bar; var result = MyLambdaFunction (foo, bar); callback(null, result); } function MyLambdaFunction (foo, bar) { // MyLambdaFunction logic here }
  • 利用执行上下文重用来提高函数性能。 确保您的代码检索到的外部化配置或依赖关系在初次执行后在本地存储和引用。限制变量/对象在每次调用时的重新初始化,而是使用静态初始化/构造函数、全局/静态变量以及单例。保持运行并重复使用连接 (HTTP,数据库等),它们在上次调用时建立。

  • 使用 环境变量 将操作参数传递到您的函数。例如,您在写入 Amazon S3 存储桶时,不应对要写入的存储桶名称进行硬编码,而应将存储桶名称配置为环境变量。

  • 控制函数部署程序包中的依赖关系。 AWS Lambda 执行环境中包括若干库,例如适用于 Node.js 和 Python 运行时的 AWS 开发工具包 (完整列表位于此处:Lambda 执行环境和可用库)。Lambda 会定期更新这些库,以支持最新的功能组合和安全更新。这些更新可能会使 Lambda 函数的行为发生细微变化。要完全控制您的函数所用的依赖关系,建议在部署程序包中包装所有依赖关系。

  • 将部署程序包大小精简为只包含运行时必要的部分。 这样会减少调用前下载和解压缩部署程序包所需的时间。对于用 Java 或 .NET 内核编写的函数,请不要将整个 AWS 开发工具包库作为部署程序包的一部分上传,而是要根据所需的模块有选择地挑选开发工具包中的组件 (例如 DynamoDB、Amazon S3、开发工具包模块和 Lambda 核心库)。

  • 将依赖关系 .jar 文件置于单独的 /lib 目录中,可减少 Lambda 解压缩部署程序包 (用 Java 编写) 所需的时间。这样比将函数的所有代码置于具有大量 .class 文件的同一 jar 中要快。

  • 将依赖关系的复杂性降至最低。首选执行上下文启动时可以快速加载的更简单的框架。例如,首选更简单的 Java 依赖关系注入 (IoC) 框架,如 DaggerGuice,而不是更复杂的 Spring Framework

  • 避免在 Lambda 函数中使用递归代码,因为如果使用递归代码,函数便会自动调用自身,直到满足某些任意条件为止。这可能会导致意想不到的函数调用量和升级成本。如果您意外地执行此操作,请立即将函数并发执行数限制设置为 0 来限制对函数的所有调用,同时更新代码。

函数配置

  • 对您的 Lambda 函数进行性能测试是确保选择最佳内存大小配置的关键环节。增加内存大小会触发函数可用 CPU 的同等水平的增加。函数的内存使用率是根据调用情况确定的,可以在 AWS CloudWatch 日志中查看。每次调用都将生成一个 REPORT: 条目,如下所示:

    REPORT RequestId: 3604209a-e9a3-11e6-939a-754dd98c7be3 Duration: 12.34 ms Billed Duration: 100 ms Memory Size: 128 MB Max Memory Used: 18 MB

    分析 Max Memory Used: 字段能够确定函数是否需要更多内存,或函数的内存大小是否过度配置。

  • 对您的 Lambda 函数进行加载测试,确定最佳超时值。分析函数的运行时间很重要,这样更容易确定依赖关系服务是否有问题,这些问题可能导致并发函数的增加超出您的预期。如果您的 Lambda 函数进行网络调用的资源无法处理 Lambda 扩展,这就更加重要。

  • 设置 IAM 策略时使用最严格的权限。了解您的 Lambda 函数所需的资源和操作,并限制这些权限的执行角色。有关更多信息,请参阅 AWS Lambda 的身份验证和访问控制

  • 熟悉 AWS Lambda 限制在确定运行时资源限制时,负载大小、文件描述符和 /tmp 空间通常会被忽略。

  • 删除不再使用的 Lambda 函数。这样,未使用的函数就不会不必要地占用有限的部署程序包空间。

  • 如果您使用 Amazon Simple Queue Service 作为事件源,请确保该函数的预计执行时间值不超过队列上的可见性超时值。这将应用 CreateFunctionUpdateFunctionConfiguration

    • 对于 CreateFunction,AWS Lambda 将使函数创建流程失败。

    • 对于 UpdateFunctionConfiguration,它可能会导致该函数的重复调用。

警报与指标

  • 使用 AWS Lambda 指标 CloudWatch 警报,不要在您的 Lambda 函数代码中创建和更新指标。跟踪 Lambda 函数的运行状况是更加有效的方式,这样您就可以在开发过程的早期发现问题。例如,您可以根据 Lambda 函数执行时间的预计持续时间配置警报,以解决函数代码引起的瓶颈或延迟。

  • 利用您的日志记录库和 AWS Lambda 指标和维度捕捉应用程序错误 (例如,ERR、ERROR、WARNING 等)

流事件调用

  • 测试不同批处理和记录的大小,这样每个事件源的轮询频率都会根据函数完成任务的速度进行调整。BatchSize 控制每次调用可向您的函数发送记录的最大数量。批处理大小如果较大,通常可以更有效地吸收大量记录的调用开销,从而增加吞吐量。

    注意

    如果没有足够数量的记录需要处理,较少数量的记录也可调用流处理函数,而不会等待。

  • 通过增加分片提高 Kinesis 流处理吞吐量。一个 Kinesis 流由一个或多个分片组成。Lambda 轮询每个分片时最多会使用一个并发调用。例如,如果您的流有 100 个活跃分片,则最多可以并发运行 100 个 Lambda 函数调用。增加分片数量会直接增加 Lambda 函数并发调用的最大数量,还可增加 Kinesis 流处理的吞吐量。如果您增加 Kinesis 流中的分片数量,请确保您已为数据选择了合适的分区键 (请参阅分区键),这样相关记录将会位于同一分片中,而且您的数据也可合理分配。

  • 在 IteratorAge 上使用 Amazon CloudWatch,确定是否正在处理您的 Kinesis 流。例如,将 CloudWatch 警报的最大值设置配置为 30000 (30 秒)。

Async 调用

  • 创建并使用 死信队列,解决并重放 async 函数错误。

Lambda VPC

  • 下图指导您通过决策树了解是否应该使用 VPC (Virtual Private Cloud):

  • 除非迫不得已,否则不要将您的 Lambda 函数置于 VPC 中。使用它只是为了访问您无法公开的资源,如私有 Amazon 关系数据库实例,除此之外没有其他好处。诸如 Amazon Elasticsearch Service 之类的服务可通过 IAM 的访问策略确保其安全,所以将终端节点公开是安全的,您不需要在 VPC 中运行函数以确保其安全。

  • Lambda 在您的 VPC 中创建弹性网络接口 (ENI) 以访问内部资源。在请求增加并发前,请确保您拥有足够的 ENI 容量 (公式可在此处找到:配置 Lambda 函数以访问 Amazon VPC 中的资源) 和 IP 地址空间。如果没有足够的 ENI 容量,需要请求增加。如果没有足够的 IP 地址空间,您可能需要创建更大的子网。

  • 在您的 VPC 中创建专用 Lambda 子网:

    • 这样可以更轻松地为 NAT 网关流量应用自定义路由表,而无需更改您的其他公有/私有子网。有关更多信息,请参阅 配置 Lambda 函数以访问 Amazon VPC 中的资源

    • 这样您还可以为 Lambda 留出专门的地址空间,无需与其他资源共享。