AWS(Amazon Web Services) 提供了一系列的云计算服务,其中 SNS (Simple Notification Service)、SQS (Simple Queue Service) 和 Lambda 是其中的三项核心服务
SNS (Simple Notification Service)
- 定义: SNS 是一项用于构建分布式系统中发布/订阅模式的服务。它允许发布者(发布消息的服务或应用程序)将消息发送到一个主题,而订阅者(接收消息的服务或应用程序)可以订阅这些主题以接收消息。
- 使用场景: 适用于需要在多个组件或服务之间进行异步通信的场景。常见的用例包括通知、事件处理等。
SQS (Simple Queue Service):
- 定义: SQS 是一项托管的消息队列服务,用于通过分离消息的生产者和消费者来实现分布式系统的解耦。消息在队列中存储,等待被消费者处理。
- 使用场景: 适用于构建分布式系统,其中不同的组件需要以异步方式进行通信,并且希望实现松耦合。可以用于削峰填谷、任务分发等场景。
Lambda:
- 定义: Lambda 是一项无服务器计算服务,允许您运行代码而无需管理服务器。您只需上传代码并定义触发器,当触发条件满足时,AWS Lambda 将自动执行您的代码。
- 使用场景: 适用于事件驱动的计算,可以在需要时执行代码,而无需一直保持服务器运行。常见用例包括数据处理、图像处理、API 执行等。
这三项服务通常可以协同工作,例如,您可以使用 SNS 将消息发布到主题,然后通过 SQS 将这些消息传递到队列,最后通过 Lambda 处理队列中的消息。这样的组合提供了灵活性和可扩展性,适用于各种云计算场景
下面来分别说明:
以下用nestjs框架来说明
SNS
import {
Injectable } from '@nestjs/common';
import * as AWS from 'aws-sdk';
@Injectable()
export class NotificationService {
private readonly sns: AWS.SNS;
private topicArn: string;
constructor() {
// 初始化 AWS SNS 实例
this.sns = new AWS.SNS();
AWS.config.update({
region: 'your-region' }); // 替换为您的 AWS 区域
}
// 创建 SNS 主题
async createTopic(topicName: string): Promise<void> {
const topicParams: AWS.SNS.CreateTopicInput = {
Name: topicName,
};
const result = await this.sns.createTopic(topicParams).promise();
this.topicArn = result.TopicArn;
console.log(`Topic created with ARN: ${
this.topicArn}`);
}
// 发布消息到已创建的主题
async publishMessage(message: string): Promise<void> {
if (!this.topicArn) {
throw new Error('Topic not created. Call createTopic() first.');
}
const publishParams: AWS.SNS.PublishInput = {
TopicArn: this.topicArn,
Message: message,
};
await this.sns.publish(publishParams).promise();
console.log('Message published to the topic.');
}
// 订阅已创建的主题
async subscribeToTopic(endpoint: string, protocol: string): Promise<void> {
if (!this.topicArn) {
throw new Error('Topic not created. Call createTopic() first.');
}
const subscribeParams: AWS.SNS.SubscribeInput = {
Protocol: protocol,
TopicArn: this.topicArn,
Endpoint: endpoint,
};
const result = await this.sns.subscribe(subscribeParams).promise();
console.log(`Subscribed to the topic with subscription ARN: ${
result.SubscriptionArn}`);
}
}
其使用场景除了发布/订阅模型的通知系统;还有如下:
-
实时事件通知:
场景: 当您的应用程序需要实时响应事件,例如新用户注册、订单状态变更等时,使用 SNS 可以确保消息的即时传递,而不需要轮询或长轮询。 -
异步任务通知:
场景: 在分布式系统中,当异步任务完成时,可以使用 SNS发送通知。例如,后台任务处理完成后,可以发布一条通知,而前端应用程序可以订阅该通知以及时更新用户界面。 -
系统健康检查和报警:
场景: 在监控系统中,当系统的健康状态发生变化时,例如服务器故障、资源超过阈值等,SNS可以用于发送报警通知给相关的团队或个人,以便他们可以及时采取行动。 -
移动推送通知:
场景: 对于移动应用程序,SNS 提供了移动推送服务,可以用于向 iOS、Android 和 Kindle设备发送推送通知。这对于推送应用程序更新、提醒用户等方面非常有用。 -
日志和审计事件:
场景: 当您需要记录和审计系统中的关键事件时,SNS 可以用于将日志信息发送到相关团队或存储中。这有助于及时发现和解决潜在的问题。 -
多通道通知:
场景: SNS 不仅支持通过电子邮件、短信、HTTP/S等方式发送通知,还支持自定义端点。这使得可以根据不同的场景选择合适的通知通道,以确保消息能够准确地传递到目标。
SQS
import {
Injectable } from '@nestjs/common';
import * as AWS from 'aws-sdk';
@Injectable()
export class SqsService {
private readonly sqs: AWS.SQS;
private queueUrl: string;
constructor() {
// 初始化 AWS SQS 实例
this.sqs = new AWS.SQS();
AWS.config.update({
region: 'your-region' }); // 替换为您的 AWS 区域
}
// 创建 SQS 队列
async createQueue(queueName: string): Promise<void> {
const queueParams: AWS.SQS.CreateQueueRequest = {
QueueName: queueName,
};
const result = await this.sqs.createQueue(queueParams).promise();
this.queueUrl = result.QueueUrl;
console.log(`Queue created with URL: ${
this.queueUrl}`);
}
// 发送消息到 SQS 队列
async sendMessage(message: string): Promise<void> {
if (!this.queueUrl) {
throw new Error('Queue not created. Call createQueue() first.');
}
const sendMessageParams: AWS.SQS.SendMessageRequest = {
QueueUrl: this.queueUrl,
MessageBody: message,
};
await this.sqs.sendMessage(sendMessageParams).promise();
console.log('Message sent to the queue.');
}
// 从 SQS 队列接收消息
async receiveMessage(): Promise<string | null> {
if (!this.queueUrl) {
throw new Error('Queue not created. Call createQueue() first.');
}
const receiveMessageParams: AWS.SQS.ReceiveMessageRequest = {
QueueUrl: this.queueUrl,
MaxNumberOfMessages: 1,
VisibilityTimeout: 30, // 设置消息不可见的时间(秒)
};
const result = await this.sqs.receiveMessage(receiveMessageParams).promise();
if (result.Messages && result.Messages.length > 0) {
const message = result.Messages[0].Body;
// 删除已接收的消息,避免其他消费者再次接收到
await this.deleteMessage(result.Messages[0].ReceiptHandle);
return message;
} else {
console.log('No messages available in the queue.');
return null;
}
}
// 删除 SQS 队列中的消息
async deleteMessage(receiptHandle: string): Promise<void> {
const deleteMessageParams: AWS.SQS.DeleteMessageRequest = {
QueueUrl: this.queueUrl,
ReceiptHandle: receiptHandle,
};
await this.sqs.deleteMessage(deleteMessageParams).promise();
console.log('Message deleted from the queue.');
}
}
上述代码中SqsService 提供了四个主要方法:
createQueue(queueName: string): Promise: 创建 SQS 队列。
sendMessage(message: string): Promise: 发送消息到已创建的队列。
receiveMessage(): Promise<string | null>: 从队列中接收消息。
deleteMessage(receiptHandle: string): Promise: 删除队列中的消息。
其使用场景能应用如下:
- 削峰填谷:
场景: 当应用程序面临突发的请求激增时,可以使用 SQS缓冲这些请求,避免超负荷情况。生产者将请求发送到队列,而消费者按照其自身速度处理这些请求,实现了削峰填谷的效果。 - 异步任务处理:
场景: 当应用程序需要处理一些耗时的任务,例如图像处理、数据分析等,可以将任务放入 SQS队列中,由后台异步处理,以避免阻塞主要应用程序流程。 - 解耦系统组件:
场景: 在分布式系统中,不同的组件可能需要进行通信,但为了降低耦合性,可以使用 SQS作为消息队列,使得这些组件之间只需要了解消息的格式而不需要直接通信。 - 事件驱动架构:
场景: SQS 可以作为事件驱动架构中的重要组成部分。当一个组件发生某个事件时,可以将相关信息发布到 SQS主题,而其他组件则可以订阅这个主题以接收事件通知。 - 数据流处理:
场景: 当需要处理大量实时数据流时,SQS可以作为缓冲器,将数据发送到队列,而后台服务按照其处理能力逐步处理这些数据,以确保数据不会丢失且处理过程不会超负荷。 - 微服务通信:
场景: 在微服务架构中,各个微服务可以使用 SQS 进行异步通信。例如,一个微服务可以将某个状态更改的通知发布到 SQS
主题,而其他微服务则可以订阅这个主题以获取状态更改通知。 - 跨地理位置的通信:
场景: 当系统的组件分布在不同的地理位置时,SQS可以作为它们之间进行异步通信的可靠方式,确保消息在网络不稳定的环境中仍然可靠传递。 - 错误处理和重试:
场景: SQS具有内置的消息保留和重试机制,可以在处理消息时发生错误时进行重试。这使得在出现故障时能够更可靠地处理消息。
Lambda
import {
Injectable } from '@nestjs/common';
import * as AWS from 'aws-sdk';
@Injectable()
export class LambdaService {
private readonly lambda: AWS.Lambda;
private functionArn: string;
constructor() {
// 初始化 AWS Lambda 实例
this.lambda = new AWS.Lambda();
AWS.config.update({
region: 'your-region' }); // 替换为您的 AWS 区域
}
// 创建 Lambda 函数
async createLambdaFunction(
functionName: string,
handler: string,
roleArn: string,
runtime: string,
): Promise<void> {
const functionParams: AWS.Lambda.CreateFunctionRequest = {
FunctionName: functionName,
Handler: handler,
Role: roleArn,
Runtime: runtime,
// 可以根据您的需求添加其他参数
};
const result = await this.lambda.createFunction(functionParams).promise();
this.functionArn = result.FunctionArn;
console.log(`Lambda function created with ARN: ${
this.functionArn}`);
}
// 调用 Lambda 函数
async invokeLambdaFunction(payload: any): Promise<any> {
if (!this.functionArn) {
throw new Error('Lambda function not created. Call createLambdaFunction() first.');
}
const invokeParams: AWS.Lambda.InvocationRequest = {
FunctionName: this.functionArn,
Payload: JSON.stringify(payload),
};
const result = await this.lambda.invoke(invokeParams).promise();
const responsePayload = JSON.parse(result.Payload as string);
console.log('Lambda function invoked with response:', responsePayload);
return responsePayload;
}
// 删除 Lambda 函数
async deleteLambdaFunction(): Promise<void> {
if (!this.functionArn) {
throw new Error('Lambda function not created. Call createLambdaFunction() first.');
}
const deleteParams: AWS.Lambda.DeleteFunctionRequest = {
FunctionName: this.functionArn,
};
await this.lambda.deleteFunction(deleteParams).promise();
console.log('Lambda function deleted.');
}
}
Lambda能用于以下场景:
- 事件驱动的计算: 场景: 在 Lambda 中,您可以配置不同的事件源,如 Amazon S3、Amazon 、DynamoDB、Amazon Kinesis 等。Lambda 函数会在事件发生时被触发,例如在对象上传到 S3 桶时执行某些操作。
- API 后端服务: 场景: 使用 Lambda 作为后端服务来处理 API 请求。通过结合 API Gateway,您可以创建一个可扩展的、无服务器的 API 后端,Lambda 会根据请求处理并返回响应。
- 数据处理和转换: 场景: Lambda 可以用于处理、转换和清理大量的数据。例如,对于在 S3 存储的大型数据集,Lambda可以对每个对象执行特定的数据处理任务。
- 实时文件处理: 场景: 当有新的文件上传到存储服务时,Lambda可以自动触发,并执行相关的文件处理任务。这对于实现实时文件处理非常有用。
- 后台任务和异步处理: 场景: Lambda 可以用于执行异步任务,例如发送电子邮件通知、生成报告等。通过将任务放入消息队列(如SQS)中,Lambda 可以在任务可用时进行处理。
- 自动化和脚本: 场景: Lambda 可以用于自动化任务,例如定时运行脚本、执行清理任务等。通过使用 CloudWatch Events或者直接配置定时触发器,Lambda 函数可以按计划执行。
- 实时数据流处理: 场景: 对于实时数据流,Lambda 可以与 Kinesis等服务一起使用,以进行实时的数据处理和分析。这对于构建实时分析和仪表板非常有用。
- 身份验证和授权: 场景: Lambda 可以用于自定义身份验证和授权逻辑。例如,您可以使用 Lambda 来验证 API请求的令牌或执行自定义授权检查。
- 图像和媒体处理: 场景: Lambda 可以用于处理图像和媒体文件,例如生成缩略图、转码视频等。与 S3事件结合使用,可以实现自动的图像和媒体处理流程。
- 物联网(IoT)应用: 场景: Lambda 可以与 AWS IoT 服务结合使用,处理从设备发送的消息,执行设备控制和数据处理。
AWS Lambda、Amazon SQS 和 Amazon SNS 组合在一起可以构建灵活的、高度可扩展的分布式系统。
以下演示了如何使用 Node.js(使用 AWS SDK for JavaScript)在 NestJS 中组合使用这三个服务。假定已经在 AWS 控制台上创建了相应的 Lambda 函数、SQS 队列和 SNS 主题。
import {
Injectable } from '@nestjs/common';
import * as AWS from 'aws-sdk';
@Injectable()
export class LambdaSqsSnsService {
private readonly lambda: AWS.Lambda;
private readonly sqs: AWS.SQS;
private readonly sns: AWS.SNS;
private lambdaFunctionName: string;
private sqsQueueUrl: string;
private snsTopicArn: string;
constructor() {
// 初始化 AWS Lambda、SQS 和 SNS 实例
this.lambda = new AWS.Lambda();
this.sqs = new AWS.SQS();
this.sns = new AWS.SNS();
AWS.config.update({
region: 'your-region' }); // 替换为您的 AWS 区域
// 替换为您的 Lambda 函数名称
this.lambdaFunctionName = 'your-lambda-function-name';
// 替换为您的 SQS 队列 URL
this.sqsQueueUrl = 'your-sqs-queue-url';
// 替换为您的 SNS 主题 ARN
this.snsTopicArn = 'your-sns-topic-arn';
}
// 触发 Lambda 函数
async invokeLambdaFunction(payload: any): Promise<any> {
const invokeParams: AWS.Lambda.InvocationRequest = {
FunctionName: this.lambdaFunctionName,
Payload: JSON.stringify(payload),
};
const result = await this.lambda.invoke(invokeParams).promise();
const responsePayload = JSON.parse(result.Payload as string);
console.log('Lambda function invoked with response:', responsePayload);
return responsePayload;
}
// 发送消息到 SQS 队列
async sendMessageToQueue(message: string): Promise<void> {
const sendMessageParams: AWS.SQS.SendMessageRequest = {
QueueUrl: this.sqsQueueUrl,
MessageBody: message,
};
await this.sqs.sendMessage(sendMessageParams).promise();
console.log('Message sent to the SQS queue.');
}
// 订阅 SNS 主题
async subscribeToTopic(endpoint: string): Promise<void> {
const subscribeParams: AWS.SNS.SubscribeInput = {
Protocol: 'sqs',
TopicArn: this.snsTopicArn,
Endpoint: this.sqsQueueUrl,
};
const result = await this.sns.subscribe(subscribeParams).promise();
console.log(`Subscribed SQS queue to SNS topic with subscription ARN: ${
result.SubscriptionArn}`);
}
// 发布消息到 SNS 主题
async publishMessageToTopic(message: string): Promise<void> {
const publishParams: AWS.SNS.PublishInput = {
TopicArn: this.snsTopicArn,
Message: message,
};
await this.sns.publish(publishParams).promise();
console.log('Message published to the SNS topic.');
}
}
invokeLambdaFunction(payload: any): Promise: 调用 Lambda 函数。
sendMessageToQueue(message: string): Promise: 发送消息到 SQS 队列。
subscribeToTopic(endpoint: string): Promise: 订阅 SNS 主题。
publishMessageToTopic(message: string): Promise: 发布消息到 SNS 主题
以下是一个实际的应用场景,可以使用 AWS Lambda、Amazon SQS 和 Amazon SNS 的组合来构建一个分布式、可伸缩、异步处理的系统。
场景描述:
假设当前正在构建一个在线电商平台,用户可以上传商品图片,而系统需要进行图像处理,包括生成缩略图和执行图像识别。在这个场景中,我们将使用 Lambda 函数处理图像,SQS 队列存储待处理的图像任务,而 SNS 主题用于发布图像处理完成的通知。
创建 NestJS 服务
// image-processing.service.ts
import {
Injectable } from '@nestjs/common';
import * as AWS from 'aws-sdk';
import * as sharp from 'sharp';
@Injectable()
export class ImageProcessingService {
private s3 = new AWS.S3();
private sqs = new AWS.SQS();
private sns = new AWS.SNS();
async processImage(bucket: string, key: string): Promise<void> {
try {
// 从 S3 下载图像
const image = await this.s3.getObject({
Bucket: bucket, Key: key }).promise();
// 图像处理 - 生成缩略图
const thumbnailBuffer = await sharp(image.Body).resize(100, 100).toBuffer();
// 将缩略图上传到 S3
const thumbnailKey = `thumbnails/${
key}`;
await this.s3.putObject({
Bucket: bucket, Key: thumbnailKey, Body: thumbnailBuffer }).promise();
// 发送图像处理完成的通知到 SNS 主题
await this.sns.publish({
TopicArn: 'your-sns-topic-arn', // 替换成实际的 SNS 主题 ARN
Message: `Image processed: ${
key}`,
}).promise();
// 可选:将原图和缩略图信息发送到 SQS 队列进行进一步处理
await this.sqs.sendMessage({
QueueUrl: 'your-sqs-queue-url', // 替换成实际的 SQS 队列 URL
MessageBody: JSON.stringify({
originalKey: key, thumbnailKey }),
}).promise();
} catch (error) {
console.error('处理图像出错:', error);
throw new Error('处理图像出错');
}
}
}
创建 NestJS 控制器:
// image-processing.controller.ts
import {
Controller, Post, UploadedFile, UseInterceptors } from '@nestjs/common';
import {
FileInterceptor } from '@nestjs/platform-express';
import {
ImageProcessingService } from './image-processing.service';
@Controller('images')
export class ImageProcessingController {
constructor(private readonly imageProcessingService: ImageProcessingService) {
}
@Post('upload')
@UseInterceptors(FileInterceptor('image'))
async uploadImage(@UploadedFile() file): Promise<void> {
// 假设 'image' 是表单数据中上传图像的字段名
const bucket = 'your-s3-bucket-name'; // 替换成实际的 S3 存储桶名
const key = file.originalname; // 假设使用原始文件名作为键
// 上传图像到 S3
await this.imageProcessingService.processImage(bucket, key);
// 返回成功响应
return;
}
}
NestJS 模块配置
// image-processing.module.ts
import {
Module } from '@nestjs/common';
import {
ImageProcessingController } from './image-processing.controller';
import {
ImageProcessingService } from './image-processing.service';
@Module({
controllers: [ImageProcessingController],
providers: [ImageProcessingService],
})
export class ImageProcessingModule {
}
应用根模块配置:
// app.module.ts
import {
Module } from '@nestjs/common';
import {
ImageProcessingModule } from './image-processing/image-processing.module';
@Module({
imports: [ImageProcessingModule],
})
export class AppModule {
}
通过 FileInterceptor 拦截器,NestJS 控制器接收上传的图像文件。然后,图像处理服务 (ImageProcessingService) 负责处理图像,生成缩略图,将其上传到 S3 存储桶,并发送通知到 SNS 主题。如果需要,还可以将原图和缩略图的信息发送到 SQS 队列进行后续处理。
另外一个例子是:
- 用户下单:用户在网站上下单购买商品。
- 订单处理:通过 AWS API Gateway,前端应用将订单数据发送到 AWS Lambda 函数进行订单处理。
- Lambda 函数执行:Lambda 函数执行订单处理逻辑,例如验证订单、计算总价、生成订单号等。
- 发送订单到 SQS 队列:订单处理完成后,Lambda 函数将订单数据发送到 SQS 队列。SQS 队列充当一个消息缓冲区,存储待处理的订单。
- 订单处理微服务:后端微服务(可以是单个服务或多个微服务)作为 SQS 队列的消费者,定期轮询队列,获取待处理的订单消息,并进行进一步的订单处理,如更新数据库、生成发货单等。
- 订单通知:当订单处理完成时,微服务通过 Amazon SNS 发布订单通知到相关主题。例如,有一个主题用于发送订单确认邮件,另一个主题用于将订单状态推送到用户的移动设备。
- 用户通知:订单确认邮件通过 Amazon SNS 发送给用户的邮箱。用户还可以通过移动设备接收到订单状态的推送通知。
优势和用例:
- 弹性和可伸缩性:使用 Lambda 处理订单可以根据请求量自动扩展,无需手动管理服务器资源。SQS
队列可以处理突发性订单,确保订单不会丢失。 - 异步处理: 使用 SQS 队列将订单数据异步传递给后端微服务,实现异步处理。这有助于提高系统的可响应性和吞吐量。 解耦和
- 可维护性:将订单数据通过 SQS 队列传递可以解耦前端 Lambda 函数和后端微服务,使它们能够独立演化和扩展。
- 通知和推送:使用 SNS 主题可以方便地实现通知和推送功能,将订单状态通知用户。