AWS Serverless 入门 - SNS
AWS 安全直播!
太长不看
在本系列文章中,我将尝试讲解 AWS 上无服务器架构的基础知识,帮助您构建自己的无服务器应用程序。在上一篇文章中,我们学习了如何使用 Aurora Serverless 在 AWS 上部署 SQL 数据库并与之交互。在本文中,我们将探讨 SNS 相关主题,它可以让您在应用程序中创建发布/订阅模式!
我们今天要做什么?
本文将创建一个 SNS 主题,并使用它来向多个 Lambda 函数发送通知。我们将了解如何利用此功能,根据上下文向应用程序的特定部分发送定向通知,以及如何使用它来实现代码解耦。
⬇️ 我会定期发布无服务器内容,如果您想了解更多 ⬇️
快速公告:我还在开发一个名为🛡 sls-mentor 🛡的库。它汇集了 30 条无服务器最佳实践,可以自动检查您的 AWS 无服务器项目(无论使用什么框架)。它是免费开源的,欢迎体验!
亚马逊简单通知服务(SNS)
什么是SNS?
Amazon SNS 是一种无服务器的发布/订阅服务。它允许您创建主题。主题包含生产者和消费者。生产者可以向主题发布消息,消费者可以订阅主题以接收消息。当消息发布到主题时,每个消费者最终都会收到它,这称为扇出模式。
这是一种非常强大的模式,因为它允许生产者和消费者解耦。生产者无需知道谁会消费他们的消息,消费者也无需知道谁会生产他们的消息。一个操作可以触发多个消费者。
SNS 还允许您创建主题过滤器,以便用户可以订阅特定子集的消息。这有助于创建定向通知,并进一步解耦您的应用程序。例如,RequestDelivery Lambda 函数可以只关注与交付请求相关的消息,而不关注与支付请求相关的消息。
让我们构建一个简单的应用程序来演示社交网络服务 (SNS)!
今天,我们将构建一个非常简单的应用程序:
- Lambda函数
OrderItem由 REST API 上的 POST 请求触发。用户可以指定是否接收通知,以及是否请求配送其订单。 - Lambda函数
OrderItem会将订单详情发布到 SNS 主题。 -
下游,SNS 主题将触发三个 lambda 函数:
- 一个
ExecuteOrderLambda 函数,用于模拟支付。 - Lambda函数
RequestDelivery仅在用户请求时才会模拟送货。 - Lambda函数
Notification,仅在用户请求时才会向用户发送通知。
- 一个
我们的应用程序架构如下所示:
每个操作都将通过简单的模拟来呈现console.log,但结合本系列课程的知识,您可以轻松地将它们替换为 DynamoDB、S3、SES 或任何其他 AWS 服务上的真实操作!
创建 SNS 主题
为了构建这个应用,我们将使用 AWS CDK。如果您还不熟悉它,我建议您先阅读我的系列文章的第一篇,其中有详细的介绍。我们将创建一个新的 CDK 项目,将@aws-cdk/aws-sns所需的软件包添加到项目中,并在堆栈中创建一个新的 SNS 主题,以及一个 API 网关来触发我们的 OrderItem Lambda 函数。
import * as cdk from 'aws-cdk-lib';
import path from 'path';
export class ArticleSNS extends cdk.Stack {
constructor(scope: Construct, id: string) {
super(scope, id);
const topic = new cdk.aws_sns.Topic(this, 'topic');
const api = new cdk.aws_apigateway.RestApi(this, 'api', {});
const orderItem = new cdk.aws_lambda_nodejs.NodejsFunction(this, 'OrderItem', {
entry: path.join(__dirname, 'orderItem', 'handler.ts'),
handler: 'handler',
environment: {
TOPIC_ARN: topic.topicArn,
},
});
topic.grantPublish(orderItem);
api.root.addResource('orderItem').addMethod('POST', new cdk.aws_apigateway.LambdaIntegration(orderItem));
}
}
请注意,我们已授予Lambdapublish函数权限OrderItem,使其能够向该主题发布消息。我们还将主题 ARN 作为环境变量传递给 Lambda 函数,以便其使用该 ARN 发布消息。
将 Lambda 订阅到 SNS 主题
现在,我们来创建 3 个下游 Lambda 函数,并让它们订阅该主题。我们将在该主题上实现过滤器,以便每个 Lambda 函数只接收它感兴趣的消息。
// ... previous code
const executeOrder = new cdk.aws_lambda_nodejs.NodejsFunction(this, 'ExecuteOrder', {
entry: path.join(__dirname, 'executeOrder', 'handler.ts'),
handler: 'handler',
});
topic.addSubscription(new cdk.aws_sns_subscriptions.LambdaSubscription(executeOrder));
const requestDelivery = new cdk.aws_lambda_nodejs.NodejsFunction(this, 'RequestDelivery', {
entry: path.join(__dirname, 'requestDelivery', 'handler.ts'),
handler: 'handler',
});
topic.addSubscription(
new cdk.aws_sns_subscriptions.LambdaSubscription(requestDelivery, {
filterPolicy: {
// Only triggers when the "requestDelivery" attribute is set to "true"
requestDelivery: cdk.aws_sns.SubscriptionFilter.stringFilter({ allowlist: ['true'] }),
},
}),
);
const sendNotification = new cdk.aws_lambda_nodejs.NodejsFunction(this, 'SendNotification', {
entry: path.join(__dirname, 'sendNotification', 'handler.ts'),
handler: 'handler',
});
topic.addSubscription(
new cdk.aws_sns_subscriptions.LambdaSubscription(sendNotification, {
filterPolicy: {
// Only triggers when the "sendNotification" attribute is set to "true"
sendNotification: cdk.aws_sns.SubscriptionFilter.stringFilter({ allowlist: ['true'] }),
},
}),
);
你看,这并不复杂。通过 filterPolicy 参数,我们可以指定哪些消息应该触发 Lambda 函数。在本例中,我们希望仅当属性requestDelivery设置为 true时才触发 RequestDelivery Lambda 函数true,仅当sendNotification属性设置为 false时才触发 SendNotification Lambda 函数true。
向 SNS 主题发布消息
最后,到了最有趣的部分:编写 Lambda 函数的代码!让我们从OrderItemLambda 函数开始,它将向主题发布消息。
// orderItem/handler.ts
import { PublishCommand, SNSClient } from '@aws-sdk/client-sns';
const client = new SNSClient({});
export const handler = async (event: { body: string }): Promise<{ statusCode: number; body: string }> => {
const topicArn = process.env.TOPIC_ARN;
if (topicArn === undefined) {
throw new Error('TOPIC_ARN is undefined');
}
const { requestDelivery, sendNotification, item, quantity } = JSON.parse(event.body) as {
requestDelivery?: boolean;
sendNotification?: boolean;
item?: string;
quantity?: number;
};
if (requestDelivery === undefined || sendNotification === undefined || item === undefined || quantity === undefined) {
return {
statusCode: 400,
body: 'Bad request',
};
}
await client.send(
new PublishCommand({
Message: JSON.stringify({ item, quantity }),
TopicArn: topicArn,
MessageAttributes: {
sendNotification: {
DataType: 'String',
StringValue: sendNotification.toString(),
},
requestDelivery: {
DataType: 'String',
StringValue: requestDelivery.toString(),
},
},
}),
);
return {
statusCode: 200,
body: 'Item ordered',
};
};
这段代码做了三件事:
- 它从环境变量中获取主题 ARN。
- 它解析请求体,并提取
requestDelivery、sendNotification和属性。itemquantity - 它向该主题发布了一条消息:
- 消息正文包含业务数据,
item以及quantity。 - 属性 `
requestDeliveryand` 的值根据请求的不同而sendNotification设置为true`or`false或 `or`。这些属性将由我们之前定义的过滤器使用。
- 消息正文包含业务数据,
对于三个下游 Lambda 函数,我们将使用 console.log 在 AWS 控制台中查看它们是否被触发。
// executeOrder/handler.ts
export const handler = async (event: {
Records: {
Sns: {
Message: string;
};
}[];
}): Promise<void> => {
event.Records.forEach(({ Sns: { Message } }) => {
const { item, quantity } = JSON.parse(Message) as { item: string; quantity: number };
console.log(`ORDER EXECUTED - Item: ${item}, Quantity: ${quantity}`);
});
};
在这个 Lambda 函数中,我记录了从主题接收到的消息内容。有趣的是事件参数的类型:它是一个记录数组。这是因为 SNS 可以一次发送多条消息,所以我们需要处理这种情况。
// requestDelivery/handler.ts
export const handler = (): Promise<void> => {
console.log('DELIVERY REQUESTED');
};
// sendNotification/handler.ts
export const handler = (): Promise<void> => {
console.log('NOTIFICATION SENT');
};
正如我之前所说,如果你从一开始就跟着这个系列学习,你应该能够用实际操作替换这些 console.log 语句,例如更新 DynamoDB 或 SQL 数据库、使用 SES 发送电子邮件等等!
是时候测试一下了!
首先,让我们借助 AWS CDK 部署该应用程序:
npm run cdk deploy
接下来,让我们用 Postman 发送 API 调用来测试一下:
在第一次调用中,我将sendNotification`and`设置requestDelivery为 `false`。ExecuteOrder正如预期,只有 Lambda 函数被触发。我们可以在 CloudWatch 日志中看到消息内容。
在第二次调用中,我将其设置sendNotification为 true。Lambda 函数ExecuteOrder被SendNotification触发。我们可以在 CloudWatch 中看到日志SendNotification。
在第三次调用中,我将其设置requestDelivery为 true。Lambda 函数ExecuteOrder被RequestDelivery触发。我们可以在 CloudWatch 中看到日志RequestDelivery。
结论
这篇教程只是对社交网络服务 (SNS) 的一个非常浅显的介绍。我演示了如何创建主题、订阅 Lambda 函数以及向主题发布消息。我没有编写任何实际的副作用代码,但你应该能够运用本系列前几篇文章中学到的知识自行完成。
社交网络服务(SNS)还允许向移动应用发送电子邮件、短信或推送通知,但本文并未涉及这方面内容。未来我可能会专门撰写一篇文章来介绍它!
我计划继续以双月刊的形式更新这个系列文章。我已经介绍了如何创建简单的 Lambda 函数和 REST API,以及如何与 DynamoDB 数据库和 S3 存储桶进行交互。您可以在我的代码仓库中关注更新进度!接下来,我将介绍前端部署、类型安全、更高级的模式等新主题……如果您有任何建议,欢迎随时联系我!
如果您能点赞并分享这篇文章给您的朋友和同事,我将不胜感激。这对我扩大读者群非常有帮助。另外,别忘了订阅,以便在下一篇文章发布时收到通知!
如果你想和我保持联系,这是我的推特账号。我经常发布或转发一些关于AWS和无服务器架构的有趣内容,欢迎关注我!
文章来源:https://dev.to/slsbytheodo/learn-serverless-on-aws-step-by-step-sns-2b46






