AWS(Amazon Web Services) 提供了一系列的云计算服务,其中 SNS (Simple Notification Service)、SQS (Simple Queue Service) 和 Lambda 是其中的三项核心服务
SNS (Simple Notification Service)
SQS (Simple Queue Service):
Lambda:
这三项服务通常可以协同工作,例如,您可以使用 SNS 将消息发布到主题,然后通过 SQS 将这些消息传递到队列,最后通过 Lambda 处理队列中的消息。这样的组合提供了灵活性和可扩展性,适用于各种云计算场景
下面来分别说明:
以下用nestjs框架来说明
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等方式发送通知,还支持自定义端点。这使得可以根据不同的场景选择合适的通知通道,以确保消息能够准确地传递到目标。
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
deleteMessage(receiptHandle: string): Promise: 删除队列中的消息。
其使用场景能应用如下:
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能用于以下场景:
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 队列进行后续处理。
另外一个例子是: