教程:将 Amazon Lambda 与 Amazon Simple Notification Service 结合使用 - Amazon Lambda
Amazon Web Services 文档中描述的 Amazon Web Services 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅 中国的 Amazon Web Services 服务入门 (PDF)

本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。

教程:将 Amazon Lambda 与 Amazon Simple Notification Service 结合使用

在本教程中,您将使用某个 Amazon Web Services 账户 中的 Lambda 函数,订阅独立 Amazon Web Services 账户 中的 Amazon Simple Notification Service(Amazon SNS)主题。当您向 Amazon SNS 主题发布消息时,您的 Lambda 函数会读取消息内容并将其输出到亚马逊日志。 CloudWatch 要完成此教程,需要使用 Amazon Command Line Interface(Amazon CLI)。


      该图显示了本教程中使用的服务:与连接到日志组的 Lambda 函数关联的 Amazon SNS 主题。 CloudWatch

要完成本教程,请执行以下步骤:

  • 账户 A 中,创建 Amazon SNS 主题。

  • 账户 B 中,创建可从该主题读取消息的 Lambda 函数。

  • 账户 B 中,创建该主题的订阅。

  • 向账户 A 中的 Amazon SNS 主题发布消息,并确认账户 B 中的 Lambda 函数将其输出到日志。 CloudWatch

通过完成这些步骤,您将了解如何配置 Amazon SNS 主题以调用 Lambda 函数。您还将了解如何创建 Amazon Identity and Access Management(IAM)策略,向其他 Amazon Web Services 账户 中的资源授予调用 Lambda 的权限。

在本教程中,您将使用两个独立的 Amazon Web Services 账户。Amazon CLI 命令通过以下方式对此进行说明:使用两个名为 accountAaccountB 的命名配置文件,每个文件配置为用于不同的 Amazon Web Services 账户。要了解如何配置 Amazon CLI 以使用不同的配置文件,请参阅《Amazon Command Line Interface User Guide for Version 2》中的 Configuration and credential file settings。确保为两个配置文件配置相同的默认值 Amazon Web Services 区域。

如果您为两个 Amazon Web Services 账户 创建的 Amazon CLI 配置文件使用不同名称,或者使用默认配置文件和一个命名配置文件,请根据需要按照以下步骤修改 Amazon CLI 命令。

先决条件

如果您还没有 Amazon Web Services 账户,请完成以下步骤来创建一个。

注册 Amazon Web Services 账户
  1. 打开 https://portal.aws.amazon.com/billing/signup

  2. 按照屏幕上的说明进行操作。

    在注册时,将接到一通电话,要求使用电话键盘输入一个验证码。

    当您注册 Amazon Web Services 账户时,系统将会创建一个 Amazon Web Services 账户根用户。根用户有权访问该账户中的所有 Amazon Web Services 和资源。作为安全最佳实践,请为管理用户分配管理访问权限,并且只使用根用户执行 需要根用户访问权限的任务

注册过程完成后,Amazon 会向您发送一封确认电子邮件。在任何时候,您都可以通过转至 https://aws.amazon.com/ 并选择我的账户来查看当前的账户活动并管理您的账户。

注册 Amazon Web Services 账户 后,启用多重身份验证 (MFA) 保护您的管理用户。有关说明,请参阅《IAM 用户指南》中的为为 IAM 用户(控制台)启用虚拟 MFA 设备

要授予其他用户访问您的 Amazon Web Services 账户资源的权限,请创建 IAM 用户。为了保护您的 IAM 用户,请启用 MFA 并仅向 IAM 用户授予执行任务所需的权限。

有关创建和保护 IAM 用户的更多信息,请参阅《IAM 用户指南》中的以下主题:

如果您尚未安装 Amazon Command Line Interface,请按照安装或更新最新版本的 Amazon CLI 中的步骤进行安装。

本教程需要命令行终端或 Shell 来运行命令。在 Linux 和 macOS 中,可使用您首选的 Shell 和程序包管理器。

注意

在 Windows 中,操作系统的内置终端不支持您经常与 Lambda 一起使用的某些 Bash CLI 命令(例如 zip)。安装 Windows Subsystem for Linux,获取 Ubuntu 和 Bash 与 Windows 集成的版本。

创建 Amazon SNS 主题(账户 A)


        显示您正在 Amazon SNS 主题步骤中创建主题的教程工作流程图
创建 主题
  • 账户 A 中,使用以下 Amazon CLI 命令创建 Amazon SNS 标准主题。

    aws sns create-topic --name sns-topic-for-lambda --profile accountA

    您应该可以看到类似于如下所示的输出内容。

    { "TopicArn": "arn:aws:sns:us-west-2:123456789012:sns-topic-for-lambda" }

    记下主题的 Amazon 资源名称(ARN)。稍后在本教程中向 Lambda 函数添加权限以订阅主题时,将需要使用此 ARN。

创建函数执行角色(账户 B)


        显示您正在函数步骤中创建执行角色的教程工作流程图

执行角色是一个 IAM 角色,用于向 Lambda 函数授予访问 Amazon 服务和资源的权限。在账户 B 中创建函数之前,您需要创建一个角色,该角色向该函数授予将日志写入日志的基本权限。 CloudWatch 我们将在后续步骤中添加读取 Amazon SNS 主题的权限。

创建执行角色
  1. 账户 B 中,打开 IAM 控制台中的角色页面

  2. 选择创建角色

  3. 可信实体类型中选择 Amazon 服务

  4. 使用案例中选择 Lambda

  5. 选择下一步

  6. 通过执行以下操作,向角色添加基本权限策略:

    1. 权限策略搜索框中输入 AWSLambdaBasicExecutionRole

    2. 选择下一步

  7. 通过执行以下操作,完成角色创建:

    1. 角色详细信息下的角色名称中输入 lambda-sns-role

    2. 选择创建角色

创建 Lambda 函数(账户 B)


        显示您正在函数步骤中创建函数的教程工作流程图

创建一个处理您的 Amazon SNS 消息的 Lambda 函数。函数代码将每条记录的消息内容记录到 Amazon CloudWatch Logs 中。

本教程使用 Node.js 18.x 运行时系统,但我们还提供了其他运行时系统语言的示例代码。您可以选择以下框中的选项卡,查看适用于您感兴趣的运行时系统的代码。您将在此步骤中使用的 JavaScript 代码位于JavaScript选项卡中显示的第一个示例中。

.NET
Amazon SDK for .NET
注意

还有更多相关信息 GitHub。在无服务器示例存储库中查找完整示例,并了解如何进行设置和运行。

使用 .NET 将 SNS 事件与 Lambda 结合使用。

using Amazon.Lambda.Core; using Amazon.Lambda.SNSEvents; // Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class. [assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] namespace SnsIntegration; public class Function { public async Task FunctionHandler(SNSEvent evnt, ILambdaContext context) { foreach (var record in evnt.Records) { await ProcessRecordAsync(record, context); } context.Logger.LogInformation("done"); } private async Task ProcessRecordAsync(SNSEvent.SNSRecord record, ILambdaContext context) { try { context.Logger.LogInformation($"Processed record {record.Sns.Message}"); // TODO: Do interesting work based on the new message await Task.CompletedTask; } catch (Exception e) { //You can use Dead Letter Queue to handle failures. By configuring a Lambda DLQ. context.Logger.LogError($"An error occurred"); throw; } } }
Go
适用于 Go V2 的 SDK
注意

还有更多相关信息 GitHub。在无服务器示例存储库中查找完整示例,并了解如何进行设置和运行。

使用 Go 将 SNS 事件与 Lambda 结合使用。

package main import ( "context" "fmt" "github.com/aws/aws-lambda-go/events" "github.com/aws/aws-lambda-go/lambda" ) func handler(ctx context.Context, snsEvent events.SNSEvent) { for _, record := range snsEvent.Records { processMessage(record) } fmt.Println("done") } func processMessage(record events.SNSEventRecord) { message := record.SNS.Message fmt.Printf("Processed message: %s\n", message) // TODO: Process your record here } func main() { lambda.Start(handler) }
Java
SDK for Java 2.x
注意

还有更多相关信息 GitHub。在无服务器示例存储库中查找完整示例,并了解如何进行设置和运行。

使用 Java 在 Lambda 上使用 SNS 事件。

package example; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.LambdaLogger; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.lambda.runtime.events.SNSEvent; import com.amazonaws.services.lambda.runtime.events.SNSEvent.SNSRecord; import java.util.Iterator; import java.util.List; public class SNSEventHandler implements RequestHandler<SNSEvent, Boolean> { LambdaLogger logger; @Override public Boolean handleRequest(SNSEvent event, Context context) { logger = context.getLogger(); List<SNSRecord> records = event.getRecords(); if (!records.isEmpty()) { Iterator<SNSRecord> recordsIter = records.iterator(); while (recordsIter.hasNext()) { processRecord(recordsIter.next()); } } return Boolean.TRUE; } public void processRecord(SNSRecord record) { try { String message = record.getSNS().getMessage(); logger.log("message: " + message); } catch (Exception e) { throw new RuntimeException(e); } } }
JavaScript
适用于 JavaScript (v2) 的软件开发工具包
注意

还有更多相关信息 GitHub。在无服务器示例存储库中查找完整示例,并了解如何进行设置和运行。

使用 Lambda JavaScript 消费 SNS 事件。

exports.handler = async (event, context) => { for (const record of event.Records) { await processMessageAsync(record); } console.info("done"); }; async function processMessageAsync(record) { try { const message = JSON.stringify(record.Sns.Message); console.log(`Processed message ${message}`); await Promise.resolve(1); //Placeholder for actual async work } catch (err) { console.error("An error occurred"); throw err; } }

使用 Lambda TypeScript 消费 SNS 事件。

import { SNSEvent, Context, SNSHandler, SNSEventRecord } from "aws-lambda"; export const functionHandler: SNSHandler = async ( event: SNSEvent, context: Context ): Promise<void> => { for (const record of event.Records) { await processMessageAsync(record); } console.info("done"); }; async function processMessageAsync(record: SNSEventRecord): Promise<any> { try { const message: string = JSON.stringify(record.Sns.Message); console.log(`Processed message ${message}`); await Promise.resolve(1); //Placeholder for actual async work } catch (err) { console.error("An error occurred"); throw err; } }
PHP
适用于 PHP 的 SDK
注意

还有更多相关信息 GitHub。在无服务器示例存储库中查找完整示例,并了解如何进行设置和运行。

使用 PHP 将 SNS 事件与 Lambda 结合使用。

<?php /* Since native PHP support for AWS Lambda is not available, we are utilizing Bref's PHP functions runtime for AWS Lambda. For more information on Bref's PHP runtime for Lambda, refer to: https://bref.sh/docs/runtimes/function Another approach would be to create a custom runtime. A practical example can be found here: https://aws.amazon.com/blogs/apn/aws-lambda-custom-runtime-for-php-a-practical-example/ */ // Additional composer packages may be required when using Bref or any other PHP functions runtime. // require __DIR__ . '/vendor/autoload.php'; return function ($event, $context) { foreach ($event["Records"] as $record) { processMessage($record); } echo "Done!" . PHP_EOL; }; function processMessage($record) { try { $message = $record['Sns']['Message']; echo "Processed Message: {$message}" . PHP_EOL; } catch (Exception $e) { echo "Error occured: {$e->getMessage()}" . PHP_EOL; throw $e; } }
Python
SDK for Python (Boto3)
注意

还有更多相关信息 GitHub。在无服务器示例存储库中查找完整示例,并了解如何进行设置和运行。

使用 Python 将 SNS 事件与 Lambda 结合使用。

def lambda_handler(event, context): for record in event['Records']: process_message(record) print("done") def process_message(record): try: message = record['Sns']['Message'] print(f"Processed message {message}") # TODO; Process your record here except Exception as e: print("An error occurred") raise e
Ruby
适用于 Ruby 的 SDK
注意

还有更多相关信息 GitHub。在无服务器示例存储库中查找完整示例,并了解如何进行设置和运行。

使用 Ruby 使用 Lambda 消费 SNS 事件。

def lambda_handler(event:, context:) event['Records'].map { |record| process_message(record) } end def process_message(record) message = record['Sns']['Message'] puts("Processing message: #{message}") rescue StandardError => e puts("Error processing message: #{e}") raise end
Rust
适用于 Rust 的 SDK
注意

还有更多相关信息 GitHub。在无服务器示例存储库中查找完整示例,并了解如何进行设置和运行。

通过 Rust 将 SNS 事件与 Lambda 结合使用。

use aws_lambda_events::event::sns::SnsEvent; use aws_lambda_events::sns::SnsRecord; use lambda_runtime::{run, service_fn, Error, LambdaEvent}; use tracing::info; // Built with the following dependencies: // aws_lambda_events = { version = "0.10.0", default-features = false, features = ["sns"] } // lambda_runtime = "0.8.1" // tokio = { version = "1", features = ["macros"] } // tracing = { version = "0.1", features = ["log"] } // tracing-subscriber = { version = "0.3", default-features = false, features = ["fmt"] } async fn function_handler(event: LambdaEvent<SnsEvent>) -> Result<(), Error> { for event in event.payload.records { process_record(&event)?; } Ok(()) } fn process_record(record: &SnsRecord) -> Result<(), Error> { info!("Processing SNS Message: {}", record.sns.message); // Implement your record handling code here. Ok(()) } #[tokio::main] async fn main() -> Result<(), Error> { tracing_subscriber::fmt() .with_max_level(tracing::Level::INFO) .with_target(false) .without_time() .init(); run(service_fn(function_handler)).await }
创建函数
  1. 为项目创建一个目录,然后切换到该目录。

    mkdir sns-tutorial cd sns-tutorial
  2. 将示例 JavaScript 代码复制到名为的新文件中index.js

  3. 使用以下 zip 命令创建部署包。

    zip function.zip index.js
  4. 运行以下 Amazon CLI 命令,在账户 B 中创建 Lambda 函数。

    aws lambda create-function --function-name Function-With-SNS \ --zip-file fileb://function.zip --handler index.handler --runtime nodejs18.x \ --role arn:aws:iam::<AccountB_ID>:role/lambda-sns-role \ --timeout 60 --profile accountB

    您应该可以看到类似于如下所示的输出内容。

    { "FunctionName": "Function-With-SNS", "FunctionArn": "arn:aws:lambda:us-west-2:123456789012:function:Function-With-SNS", "Runtime": "nodejs18.x", "Role": "arn:aws:iam::123456789012:role/lambda_basic_role", "Handler": "index.handler", ... "RuntimeVersionConfig": { "RuntimeVersionArn": "arn:aws:lambda:us-west-2::runtime:7d5f06b69c951da8a48b926ce280a9daf2e8bb1a74fc4a2672580c787d608206" } }
  5. 记下函数的 Amazon 资源名称(ARN)。稍后在本教程中添加权限以允许 Amazon SNS 调用函数时,将需要使用此 ARN。

向函数添加权限(账户 B)


        显示您正在函数步骤中添加权限的教程工作流程图

要让 Amazon SNS 调用函数,您需要在基于资源的策略语句中向其授予权限。您可以使用 Amazon CLI add-permission 命令添加此语句。

授予 Amazon SNS 调用函数的权限
  • 账户 B 中,使用之前记下的 Amazon SNS 主题的 ARN 运行以下 Amazon CLI 命令。

    aws lambda add-permission --function-name Function-With-SNS \ --source-arn arn:aws:sns:us-east-1:<AccountA_ID>:sns-topic-for-lambda \ --statement-id function-with-sns --action "lambda:InvokeFunction" \ --principal sns.amazonaws.com --profile accountB

    您应该可以看到类似于如下所示的输出内容。

    { "Statement": "{\"Condition\":{\"ArnLike\":{\"AWS:SourceArn\": \"arn:aws:sns:us-east-1:<AccountA_ID>:sns-topic-for-lambda\"}}, \"Action\":[\"lambda:InvokeFunction\"], \"Resource\":\"arn:aws:lambda:us-east-1:<AccountB_ID>:function:Function-With-SNS\", \"Effect\":\"Allow\",\"Principal\":{\"Service\":\"sns.amazonaws.com\"}, \"Sid\":\"function-with-sns\"}" }
注意

如果在选择加入型 Amazon Web Services 区域 中托管具有 Amazon SNS 主题的账户,则需要在主体中指定区域。例如,假设您正在亚太地区(香港)区域使用一个 Amazon SNS 主题,则需要为主体指定 sns.ap-east-1.amazonaws.com,而不是 sns.amazonaws.com

授予 Amazon SNS 订阅的跨账户权限(账户 A)


        显示您正在订阅步骤中授予权限的教程工作流程图

要让账户 B 中的 Lambda 函数订阅您在账户 A 中创建的 Amazon SNS 主题,您需要向账户 B 授予订阅主题的权限。您可以使用 Amazon CLI add-permission 命令授予此权限。

授予账户 B 订阅主题的权限
  • 账户 A 中,运行以下 Amazon CLI 命令。使用之前记下的 Amazon SNS 主题的 ARN。

    aws sns add-permission --label lambda-access --aws-account-id <AccountB_ID> \ --topic-arn arn:aws:sns:us-east-1:<AccountA_ID>:sns-topic-for-lambda \ --action-name Subscribe ListSubscriptionsByTopic --profile accountA

创建订阅(账户 B)


      显示您正在订阅步骤中创建订阅的教程工作流程图

账户 B 中,您现在将 Lambda 函数订阅到教程开始时您在账户 A 中创建的 Amazon SNS 主题。当消息发送到该主题 (sns-topic-for-lambda) 后,Amazon SNS 会调用账户 B 中的 Lambda 函数 Function-With-SNS

创建订阅
  • 账户 B 中,运行以下 Amazon CLI 命令。使用您在其中创建主题的默认区域以及主题和 Lambda 函数的 ARN。

    aws sns subscribe --protocol lambda \ --region us-east-1 \ --topic-arn arn:aws:sns:us-east-1:<AccountA_ID>:sns-topic-for-lambda \ --notification-endpoint arn:aws:lambda:us-east-1:<AccountB_ID>:function:Function-With-SNS \ --profile accountB

    您应该可以看到类似于如下所示的输出内容。

    { "SubscriptionArn": "arn:aws:sns:us-east-1:<AccountA_ID>:sns-topic-for-lambda:5d906xxxx-7c8x-45dx-a9dx-0484e31c98xx" }

将消息发布到主题(账户 A 和账户 B)


        显示您正在发布步骤中发布消息的教程工作流程图

账户 B 中的 Lambda 函数现已订阅账户 A 中的 Amazon SNS 主题,现在可以通过将消息发布到主题来测试设置了。要确认 Amazon SNS 是否已调用您的 Lambda 函数,您可以使用 CloudWatch 日志来查看函数的输出。

将消息发布到主题并查看函数的输出
  1. 在文本文件中输入 Hello World,并将其另存为 message.txt

  2. 在保存文本文件的同一目录中,在账户 A 中运行以下 Amazon CLI 命令。将 ARN 用于您自己的主题。

    aws sns publish --message file://message.txt --subject Test \ --topic-arn arn:aws:sns:us-east-1:<AccountA_ID>:sns-topic-for-lambda \ --profile accountA

    这将返回一个具有唯一标识符的消息 ID,表明 Amazon SNS 服务已接受该消息。然后,Amazon SNS 则尝试将消息传输给主题订阅用户。要确认 Amazon SNS 是否已调用您的 Lambda 函数,请使用 CloudWatch 日志查看您的函数的输出:

  3. 账户 B 中,打开 Amazon CloudWatch 控制台的日志组页面。

  4. 选择函数 (/aws/lambda/Function-With-SNS) 的日志组。

  5. 选择最新的日志流。

  6. 如果函数已正确调用,您将看到类似于以下内容的输出,其中会显示发布到主题的消息内容。

    2023-07-31T21:42:51.250Z c1cba6b8-ade9-4380-aa32-d1a225da0e48 INFO Processed message Hello World 2023-07-31T21:42:51.250Z c1cba6b8-ade9-4380-aa32-d1a225da0e48 INFO done

清除资源

除非您想要保留为本教程创建的资源,否则可立即将其删除。通过删除您不再使用的 Amazon 资源,可防止您的 Amazon 账户产生不必要的费用。

账户 A 中,清理您的 Amazon SNS 主题。

删除 Amazon SNS 主题
  1. 打开 Amazon SNS 控制台中的 Topics(主题)页面

  2. 选择您已创建的主题。

  3. 选择 Delete(删除)。

  4. 在文本输入字段中输入 delete me

  5. 选择删除

账户 B 中,清理您的执行角色、Lambda 函数和 Amazon SNS 订阅。

删除执行角色
  1. 打开 IAM 控制台的角色页面

  2. 选择您创建的执行角色。

  3. 选择删除

  4. 在文本输入字段中输入角色名称,然后选择 Delete(删除)。

删除 Lambda 函数
  1. 打开 Lamba 控制台的 Functions(函数)页面

  2. 选择您创建的函数。

  3. 依次选择操作删除

  4. 在文本输入字段中键入 delete,然后选择 Delete(删除)。

删除 Amazon SNS 订阅
  1. 在 Amazon SNS 控制台中打开 Subscription(订阅)页面

  2. 选择您已创建的订阅。

  3. 选择 Delete(删除),Delete(删除)。