定义采用 Node.js 的 Lambda 函数处理程序 - AWS Lambda

定义采用 Node.js 的 Lambda 函数处理程序

Lambda 函数处理程序是函数代码中处理事件的方法。当调用函数时,Lambda 运行处理程序方法。您的函数会一直运行,直到处理程序返回响应、退出或超时。

Node.js 处理程序基础知识

以下示例函数记录事件对象的内容并返回日志的位置。

注意

本页显示了 CommonJS 和 ES 模块处理程序的示例。要了解这两种处理程序之间的差异,请参阅 将函数处理程序指定为 ES 模块

ES module handler
export const handler = async (event, context) => { console.log("EVENT: \n" + JSON.stringify(event, null, 2)); return context.logStreamName; };
CommonJS module handler
exports.handler = async function (event, context) { console.log("EVENT: \n" + JSON.stringify(event, null, 2)); return context.logStreamName; };

配置函数时,处理程序设置的值是文件的名称,也是导出的处理程序方法的名称(由点分隔)。控制台中的默认值为 index.handler,这也是本指南所用示例的值。这表示从 handler 文件中导出的 index.js 方法。

运行时会将参数传递到处理程序方法。第一个参数是 event 对象,它包含来自调用方的信息。调用程序在调用 Invoke 时将该信息作为 JSON 格式字符串传递,运行时将它转换为对象。当 AWS 服务调用您的函数时,事件结构因服务而异

第二个参数是 context 对象,它包含有关调用、函数和执行环境的信息。在前面的示例中,函数将从 context 对象获取日志流的名称,然后将其返回给调用方。

您也可以使用回调参数,这是一种可在非异步处理程序中进行调用以发送响应的函数。我们建议您使用 Async/await(异步/等待),而不使用 Callback(回调)。Async/await 具有更好的易读性、错误处理能力和效率。有关 Async/await 与 Callback 之间差异的更多信息,请参阅 使用回调

命名

配置函数时,处理程序设置的值是文件的名称,也是导出的处理程序方法的名称(由点分隔)。控制台中创建的函数的默认值为 index.handler,这也是本指南所用示例的值。这表示从 index.jsindex.mjs 文件中导出的 handler 方法。

如果您在控制台中使用不同的文件名或函数处理程序名称创建函数,则必须编辑默认处理程序名称。

更改函数处理程序名称(控制台)
  1. 打开 Lambda 控制台的函数页面,然后选择一个函数。

  2. 选择节点选项卡。

  3. 向下滚动到运行时设置窗格并选择编辑

  4. 处理程序中,输入函数处理程序的新名称。

  5. 选择保存

使用异步/等待

如果您的代码执行异步任务,则使用 Async/await 模式来确保处理程序会完成运行。Async/await 是一种简洁、易读的 Node.js 异步代码编写方式,无需嵌套回调或链式承诺。使用 Async/await 时,您编写的代码看起来与同步代码类似,同时仍然是异步和非阻止式的。

async 关键字会将函数标记为异步,await 关键字会暂停函数的执行,直到 Promise 完成解析为止。

注意

确保等待异步事件完成。如果函数在异步事件完成之前返回,则该函数可能会失败或导致应用程序出现意外行为。当 forEach 循环包含异步事件时可能会发生这种情况。forEach 循环需要同步调用。有关更多信息,请参阅 Mozilla 文档中的 Array.prototype.forEach()

ES module handler
例 – 包含 async/await 的 HTTP 请求
const url = "https://aws.amazon.com/"; export const handler = async(event) => { try { // fetch is available in Node.js 18 and later runtimes const res = await fetch(url); console.info("status", res.status); return res.status; } catch (e) { console.error(e); return 500; } };
CommonJS module handler
例 – 包含 async/await 的 HTTP 请求
const https = require("https"); let url = "https://aws.amazon.com/"; exports.handler = async function (event) { let statusCode; await new Promise(function (resolve, reject) { https.get(url, (res) => { statusCode = res.statusCode; resolve(statusCode); }).on("error", (e) => { reject(Error(e)); }); }); console.log(statusCode); return statusCode; };

下一个示例使用 Async/await 来列出您的 Amazon Simple Storage Service 存储桶。

注意

在使用此示例之前,请确保您的函数的执行角色具有 Amazon S3 读取权限。

ES module handler
例 – 使用 Async/await 的 AWS SDK v3

此示例使用 AWS SDK for JavaScript v3,它支持 nodejs18.x 和更高版本的运行时系统。

import {S3Client, ListBucketsCommand} from '@aws-sdk/client-s3'; const s3 = new S3Client({region: 'us-east-1'}); export const handler = async(event) => { const data = await s3.send(new ListBucketsCommand({})); return data.Buckets; };
CommonJS module handler
例 – 使用 Async/await 的 AWS SDK v3

此示例使用 AWS SDK for JavaScript v3,它支持 nodejs18.x 和更高版本的运行时系统。

const { S3Client, ListBucketsCommand } = require('@aws-sdk/client-s3'); const s3 = new S3Client({ region: 'us-east-1' }); exports.handler = async (event) => { const data = await s3.send(new ListBucketsCommand({})); return data.Buckets; };

使用回调

我们建议您使用 Async/await 来声明函数处理程序,而不是使用回调。Async/await 是更好的选择,原因有以下几点:

  • 易读性:Async/await 代码比回调代码更易阅读和理解,回调代码很快就会变得难以理解,从而导致回调失败。

  • 调试和错误处理:基于回调的代码可能非常难以调试。调用堆栈可能变得难以理解,并且很容易出现错误。使用 Async/await 时,您可以使用 try/catch 块来处理错误。

  • 效率:回调通常需要在代码的不同部分之间切换。Async/await 可以减少上下文切换的数量,从而提高代码效率。

当您在处理程序中使用回调时,函数会一直执行,直到 事件循环 为空或函数超时为止。在完成所有事件循环任务之前,不会将响应发送给调用方。如果函数超时,则会返回 error。可以通过将 context.callbackWaitsForEmptyEventLoop 设置为 false,从而将运行时配置为立即发送响应。

callback 函数有两个参数:一个是 Error,一个是响应。响应对象必须与 JSON.stringify 兼容。

以下示例函数检查 URL 并向调用方返回状态代码。

ES module handler
例 – 包含 callback 的 HTTP 请求
import https from "https"; let url = "https://aws.amazon.com/"; export function handler(event, context, callback) { https.get(url, (res) => { callback(null, res.statusCode); }).on("error", (e) => { callback(Error(e)); }); }
CommonJS module handler
例 – 包含 callback 的 HTTP 请求
const https = require("https"); let url = "https://aws.amazon.com/"; exports.handler = function (event, context, callback) { https.get(url, (res) => { callback(null, res.statusCode); }).on("error", (e) => { callback(Error(e)); }); };

在下一示例中,来自 Amazon S3 的响应将在可用时立即返回给调用方。针对事件循环运行的超时被冻结,并在下次调用该函数时继续运行。

注意

在使用此示例之前,请确保您的函数的执行角色具有 Amazon S3 读取权限。

ES module handler
例 – 包含 callbackWaitsForEmptyEventLoop 的 AWS SDK v3

此示例使用 AWS SDK for JavaScript v3,它支持 nodejs18.x 和更高版本的运行时系统。

import AWS from "@aws-sdk/client-s3"; const s3 = new AWS.S3({}); export const handler = function (event, context, callback) { context.callbackWaitsForEmptyEventLoop = false; s3.listBuckets({}, callback); setTimeout(function () { console.log("Timeout complete."); }, 5000); };
CommonJS module handler
例 – 包含 callbackWaitsForEmptyEventLoop 的 AWS SDK v3

此示例使用 AWS SDK for JavaScript v3,它支持 nodejs18.x 和更高版本的运行时系统。

const AWS = require("@aws-sdk/client-s3"); const s3 = new AWS.S3({}); exports.handler = function (event, context, callback) { context.callbackWaitsForEmptyEventLoop = false; s3.listBuckets({}, callback); setTimeout(function () { console.log("Timeout complete."); }, 5000); };

Node.js Lambda 函数的代码最佳实践

在构建 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 }
  • 控制函数部署程序包中的依赖关系。AWS Lambda 执行环境包含许多库。对于 Node.js 和 Python 运行时,其中包括 AWS SDK。Lambda 会定期更新这些库,以支持最新的功能组合和安全更新。这些更新可能会使 Lambda 函数的行为发生细微变化。要完全控制您的函数所用的依赖项,请使用部署程序包来打包所有依赖项。

  • 将依赖关系的复杂性降至最低。首选在执行环境启动时可以快速加载的更简单的框架。

  • 将部署程序包大小精简为只包含运行时必要的部分。这样会减少调用前下载和解压缩部署程序包所需的时间。

  • 利用执行环境重用来提高函数性能。连接软件开发工具包 (SDK) 客户端和函数处理程序之外的数据库,并在 /tmp 目录中本地缓存静态资产。由函数的同一实例处理的后续调用可重用这些资源。这样就可以通过缩短函数运行时间来节省成本。

    为了避免调用之间潜在的数据泄露,请不要使用执行环境来存储用户数据、事件或其他具有安全影响的信息。如果您的函数依赖于无法存储在处理程序的内存中的可变状态,请考虑为每个用户创建单独的函数或单独的函数版本。

  • 使用 keep-alive 指令来维护持久连接。Lambda 会随着时间的推移清除空闲连接。在调用函数时尝试重用空闲连接会导致连接错误。要维护您的持久连接,请使用与运行时关联的 keep-alive 指令。有关示例,请参阅在 Node.js 中通过 Keep-Alive 重用连接

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

  • 避免在 Lambda 函数中使用递归调用,在这种情况下,函数会调用自己或启动可能再次调用该函数的进程。这可能会导致意想不到的函数调用量和升级成本。如果您看到意外的调用量,请立即将函数保留并发设置为 0 来限制对函数的所有调用,同时更新代码。

  • Lambda 函数代码中不要使用非正式的非公有 API。对于 AWS Lambda 托管式运行时,Lambda 会定期为 Lambda 的内部 API 应用安全性和功能更新。这些内部 API 更新可能不能向后兼容,会导致意外后果,例如,假设您的函数依赖于这些非公有 API,则调用会失败。请参阅 API 参考以查看公开发布的 API 列表。

  • 编写幂等代码。为您的函数编写幂等代码可确保以相同的方式处理重复事件。您的代码应该正确验证事件并优雅地处理重复事件。有关更多信息,请参阅如何使我的 Lambda 函数具有幂等性?