AWS SNS/SQS消息队列实战:从零搭建高可用异步通信架构
Meta Description: AWS SNS与SQS完全实战教程,手把手教你用发布订阅+消息队列构建解耦、高可用的异步通信系统,含架构图、代码示例与价格对比。
- 为什么你的系统需要消息队列?
- SQS实战:消息队列从入门到生产
- SNS实战:发布订阅模式搭建
- Lambda + SQS:无服务器消息处理
- 价格对比与成本优化
- 生产环境最佳实践
- 架构选型速查表
Meta Description: AWS SNS与SQS完全实战教程,手把手教你用发布订阅+消息队列构建解耦、高可用的异步通信系统,含架构图、代码示例与价格对比。
关键词: AWS SNS、AWS SQS、消息队列、发布订阅模式、异步通信、AWS解耦架构、Lambda集成
一、为什么你的系统需要消息队列?
想象一个典型的电商下单场景:用户点击"支付"按钮后,系统需要做哪些事情?扣库存、生成订单、发送确认邮件、通知仓库发货、更新积分、推送APP消息……如果所有操作都在同一个请求里同步执行,任何一个环节卡顿都会导致用户看到"加载中"的转圈圈。更糟糕的是,如果邮件服务宕机,整个下单流程都会失败——用户明明付了钱,系统却返回错误。
决策口诀: 两个服务之间不要直接调用,用消息队列解耦。能异步的绝不同步。
AWS提供了两个核心的消息通信服务:SNS(Simple Notification Service) 负责"一对多广播",SQS(Simple Queue Service) 负责"点对点缓冲"。它们可以独立使用,也可以组合使用,是构建松耦合、高可用系统的基石。
1.1 SNS vs SQS:一张表看懂区别
| 对比维度 | SNS(简单通知服务) | SQS(简单队列服务) |
|---|---|---|
| 核心模式 | 发布/订阅(Pub/Sub) | 点对点队列(Queue) |
| 消费者数量 | 1对多(一条消息广播给所有订阅者) | 1对1(一条消息只被一个消费者处理) |
| 消息保留 | 投递后即丢弃(除非订阅者是SQS) | 最长保留14天 |
| 顺序保证 | 不保证 | FIFO队列保证顺序 |
| 死信队列 | 支持(通过订阅策略) | 原生支持 |
| 适合场景 | 事件广播、通知分发 | 任务缓冲、削峰填谷、重试机制 |
| 价格(标准) | $0.50/百万次请求 | $0.40/百万次请求 |
一句话总结: SNS负责"通知所有人",SQS负责"排队等处理"。实际项目中两者经常组合使用——SNS接收事件,分发到多个SQS队列,各业务系统独立消费。
1.2 AWS消息队列生态全景
| 服务 | 定位 | 最大消息大小 | 吞吐量 | 适合场景 |
|---|---|---|---|---|
| SQS Standard | 通用队列 | 256 KB | 无限 | 一般异步任务 |
| SQS FIFO | 有序队列 | 256 KB | 3000条/秒 | 订单、交易等需顺序的场景 |
| SNS | 通知广播 | 256 KB | 无限 | 事件分发、告警通知 |
| EventBridge | 事件总线 | 256 KB | 无限 | 跨服务事件路由(更智能的SNS) |
| Kinesis Data Streams | 流处理 | 1 MB | 无限 | 实时数据流、日志聚合 |
| MSK (Kafka) | 分布式流 | 1 MB | 无限 | 大数据流处理、CDC |
对于大多数中小型项目的异步通信需求,SNS + SQS组合已经足够,学习成本低、运维简单、价格便宜。
二、SQS实战:消息队列从入门到生产
2.1 创建你的第一个SQS队列
打开AWS控制台,搜索"SQS"进入服务页面:
// 方式一:AWS CLI创建标准队列
aws sqs create-queue \
--queue-name my-first-queue \
--region us-east-1 \
--attributes '{
"VisibilityTimeout": "300",
"MessageRetentionPeriod": "345600",
"ReceiveMessageWaitTimeSeconds": "20"
}'
// 方式二:创建FIFO队列(需以.fifo结尾)
aws sqs create-queue \
--queue-name my-order-queue.fifo \
--region us-east-1 \
--attributes '{
"FifoQueue": "true",
"ContentBasedDeduplication": "true"
}'
关键参数解释:
- VisibilityTimeout(300秒):消息被消费后隐藏的时间。如果消费者在300秒内没处理完,消息会重新可见。设太短会导致重复消费,设太长会影响失败重试速度。
- MessageRetentionPeriod(345600秒=4天):消息未被消费时的最长保留时间。默认4天,最长14天。
- ReceiveMessageWaitTimeSeconds(20秒):长轮询等待时间。设为20秒可以减少空轮询的API调用,节省成本。
2.2 发送与接收消息
// 发送一条消息
aws sqs send-message \
--queue-url https://sqs.us-east-1.amazonaws.com/123456789012/my-first-queue \
--message-body '{"orderId": "20260901001", "amount": 99.99, "items": ["笔记本", "鼠标垫"]}' \
--message-attributes '{
"Priority": {"DataType": "String", "StringValue": "high"},
"Source": {"DataType": "String", "StringValue": "web-app"}
}'
// 接收消息(一次最多10条)
aws sqs receive-message \
--queue-url https://sqs.us-east-1.amazonaws.com/123456789012/my-first-queue \
--max-number-of-messages 10 \
--wait-time-seconds 20 \
--message-attribute-names All
// 删除已处理的消息(必须手动删除,否则消息会重新可见)
aws sqs delete-message \
--queue-url https://sqs.us-east-1.amazonaws.com/123456789012/my-first-queue \
--receipt-handle "AQEBwJnKyrHigUMZj6rYigCgxlaS3SLy0a..."
⚠️ 常见坑: SQS不会自动删除已消费的消息。你必须在处理成功后显式调用delete-message。如果消费者崩溃没来得及删除,消息会在VisibilityTimeout后重新回到队列。这是"至少一次投递"语义——设计消费者时要考虑幂等性。
2.3 SQS高级特性
死信队列(Dead Letter Queue): 设置RedrivePolicy,当消息被消费失败(N次后)自动转入死信队列,避免"毒消息"阻塞整个队列:
// 先创建死信队列
aws sqs create-queue --queue-name my-dlq
// 设置主队列的RedrivePolicy
aws sqs set-queue-attributes \
--queue-url https://sqs.us-east-1.amazonaws.com/123456789012/my-first-queue \
--attributes '{
"RedrivePolicy": "{\"deadLetterTargetArn\":\"arn:aws:sqs:us-east-1:123456789012:my-dlq\",\"maxReceiveCount\":\"3\"}"
}'
延迟队列: SQS支持最大15分钟的单条消息延迟,或队列级别的延迟(0-15分钟)。适合"30分钟后提醒"这类场景。
// 发送一条10分钟后才可见的消息
aws sqs send-message \
--queue-url https://sqs.us-east-1.amazonaws.com/123456789012/my-first-queue \
--message-body '{"task": "sendFollowUpEmail", "userId": "u123"}' \
--delay-seconds 600
三、SNS实战:发布订阅模式搭建
3.1 创建Topic和订阅
// 创建SNS Topic
aws sns create-topic --name my-order-events
// 订阅Topic —— SQS队列订阅
aws sns subscribe \
--topic-arn arn:aws:sns:us-east-1:123456789012:my-order-events \
--protocol sqs \
--notification-endpoint arn:aws:sqs:us-east-1:123456789012:my-first-queue
// 订阅Topic —— HTTP/HTTPS端点(Webhook)
aws sns subscribe \
--topic-arn arn:aws:sns:us-east-1:123456789012:my-order-events \
--protocol https \
--notification-endpoint https://your-api.com/webhooks/sns
// 订阅Topic —— 邮件通知
aws sns subscribe \
--topic-arn arn:aws:sns:us-east-1:123456789012:my-order-events \
--protocol email \
--notification-endpoint [email protected]
// 发布消息(所有订阅者都会收到)
aws sns publish \
--topic-arn arn:aws:sns:us-east-1:123456789012:my-order-events \
--subject "新订单通知" \
--message '{"orderId": "20260901001", "status": "created"}'
3.2 SNS + SQS 组合架构(生产推荐)
这是最经典的AWS解耦模式——SNS作为事件入口,分发到多个SQS队列,各业务系统独立消费:
┌──── SQS队列A ──── Lambda(发送邮件)
│
用户下单 ──── API ──── SNS Topic ──┼──── SQS队列B ──── Lambda(更新库存)
│
└──── SQS队列C ──── Lambda(推送消息)
为什么不让Lambda直接订阅SNS? 因为SNS直接调用Lambda时,如果Lambda失败会立即重试,没有缓冲。而SNS → SQS → Lambda的模式,SQS提供了:
- 缓冲能力:突发流量先进队列排队,Lambda按自己的速率消费
- 自动重试:Lambda失败后消息回到队列,下次轮询再处理
- 死信兜底:多次失败的消息自动进死信队列,不阻塞正常流程
- 多消费者隔离:库存系统崩溃不影响邮件系统
3.3 访问策略:谁能发消息到你的Topic
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowSpecificRole",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::123456789012:role/OrderServiceRole"
},
"Action": "sns:Publish",
"Resource": "arn:aws:sns:us-east-1:123456789012:my-order-events"
}
]
}
⚠️ 安全提醒: 默认情况下,新创建的SNS Topic是私有的。但很多开发者习惯用"*"作为Principal,导致任何人都能往你的Topic发消息。生产环境务必设置最小权限。
四、Lambda + SQS:无服务器消息处理
4.1 用Python Lambda消费SQS消息
import json
import boto3
sqs = boto3.client('sqs')
def lambda_handler(event, context):
for record in event['Records']:
body = json.loads(record['body'])
message_id = record['messageId']
print(f"处理消息: {message_id}")
print(f"订单ID: {body.get('orderId')}")
print(f"金额: {body.get('amount')}")
// 执行业务逻辑
process_order(body)
return {'statusCode': 200, 'body': '处理完成'}
def process_order(order):
// 模拟业务处理:更新数据库、发送通知等
print(f"订单 {order['orderId']} 处理完成")
4.2 触发器配置(关键参数)
在Lambda控制台配置SQS触发器时,这些参数直接影响系统行为:
| 参数 | 推荐值 | 说明 |
|---|---|---|
| Batch Size | 10 | 每次最多拉取的消息数 |
| Maximum Batching Window | 5秒 | 等待凑批的时间 |
| concurrent instances | 根据队列深度调整 | 并发执行的Lambda数量 |
| Error destination | 死信队列ARN | Lambda异步调用失败时的目标 |
| Report batch item failures | 启用 | 允许部分成功(重要!) |
⚠️ 关键配置: 一定要开启"Report batch item failures"。不开启的话,一批10条消息中只要1条失败,整批都会被标记为失败并重新入队,导致已成功处理的8条被重复处理。
4.3 用EventBridge替代SNS做事件路由
如果事件源复杂(多个AWS服务发出事件),EventBridge比SNS更强大:
// Python Lambda发布事件到EventBridge
import boto3
import json
from datetime import datetime
events = boto3.client('events')
def publish_order_event(order):
events.put_entries(
Entries=[{
'Source': 'com.myapp.orders',
'DetailType': 'OrderCreated',
'Detail': json.dumps({
'orderId': order['id'],
'amount': order['amount'],
'timestamp': datetime.utcnow().isoformat()
}),
'EventBusName': 'my-app-events'
}]
)
EventBridge的优势是基于规则路由——你可以设置规则只转发"金额>1000"的订单到特定SQS队列,或者只转发"来自日本区"的事件到特定Lambda。SNS做不到这种精细化过滤。
五、价格对比与成本优化
5.1 SNS/SQS标准定价
| 服务 | 免费额度(每月) | 标准价格 | FIFO价格 |
|---|---|---|---|
| SQS | 100万次请求 | $0.40/百万次 | $0.50/百万次 |
| SNS | 100万次请求 | $0.50/百万次 | — |
| 数据传输(同区域) | — | 免费 | 免费 |
| 数据传输(跨区域) | — | $0.01/万条 | $0.01/万条 |
实际成本估算(月处理100万条消息):
| 场景 | SQS标准 | SQS FIFO | SNS+SQS组合 |
|---|---|---|---|
| 消息数 | 100万 | 100万 | 100万+100万(订阅) |
| 请求费用 | $0.40 | $0.50 | $0.50 + $0.40 = $0.90 |
| 数据传输 | 免费 | 免费 | 免费 |
| 月度总计 | $0.40 | **0.50 | $0.90 |
5.2 省钱技巧
- 批量操作:一次API调用发送/接收10条消息,API调用次数减少到1/10
- 长轮询:设置
WaitTimeSeconds=20,减少空轮询的无效请求 - 选择标准队列:只有真正需要顺序保证时才用FIFO,标准队列便宜20%
- 合理设置消息保留期:不需要的消息及时删除,避免长期存储费用
- 利用免费额度:每月100万次免费请求,中小项目基本够用
六、生产环境最佳实践
6.1 消息幂等性设计
SQS保证"至少一次投递",意味着同一条消息可能被处理多次。你的消费者必须是幂等的——处理一次和处理多次的结果相同:
import hashlib
def process_order_idempotent(order, db):
// 用订单ID作为幂等键
idempotency_key = f"order_{order['orderId']}"
// 检查是否已处理
existing = db.get_item(Key={'id': idempotency_key})
if existing:
print(f"订单 {order['orderId']} 已处理,跳过")
return
// 执行业务逻辑
do_business_logic(order)
// 标记为已处理
db.put_item(Item={
'id': idempotency_key,
'status': 'processed',
'timestamp': datetime.utcnow().isoformat()
})
6.2 监控告警配置
// 监控SQS队列积压(ApproximateNumberOfMessages > 1000持续5分钟告警)
aws cloudwatch put-metric-alarm \
--alarm-name "SQS-QueueBacklog-Alert" \
--metric-name ApproximateNumberOfMessages \
--namespace AWS/SQS \
--dimensions Name=QueueName,Value=my-first-queue \
--statistic Average \
--period 300 \
--threshold 1000 \
--comparison-operator GreaterThanThreshold \
--evaluation-periods 1 \
--alarm-actions arn:aws:sns:us-east-1:123456789012:ops-alerts
// 监控死信队列(有任何消息就告警)
aws cloudwatch put-metric-alarm \
--alarm-name "DLQ-HasMessages" \
--metric-name ApproximateNumberOfMessages \
--namespace AWS/SQS \
--dimensions Name=QueueName,Value=my-dlq \
--statistic Sum \
--period 300 \
--threshold 1 \
--comparison-operator GreaterThanOrEqualToThreshold \
--evaluation-periods 1 \
--alarm-actions arn:aws:sns:us-east-1:123456789012:ops-alerts
6.3 五个必须遵守的安全规则
- 最小权限IAM:生产者只给
sns:Publish,消费者只给sqs:ReceiveMessage和sqs:DeleteMessage - 加密传输:所有队列启用SSE-SQS或SSE-KMS加密
- VPC端点:内网流量走VPC端点,不出公网
- 消息内容脱敏:不要在消息体里放密码、信用卡号等敏感信息
- 死信队列必须配:没有DLQ的消息队列就像没有刹车的汽车
七、架构选型速查表
| 你的需求 | 推荐方案 | 原因 |
|---|---|---|
| 一个服务通知多个下游 | SNS → 多个SQS | 发布订阅,天然一对多 |
| 任务队列(如图片处理) | SQS + Lambda | 按需伸缩,按量计费 |
| 需要消息顺序 | SQS FIFO | 保证严格顺序 |
| 跨服务事件路由 | EventBridge | 比SNS更灵活的规则路由 |
| 邮件/短信通知 | SNS直接订阅 | 原生支持Email/SMS/HTTP |
| 高吞吐日志流 | Kinesis或MSK | SQS不适合流式处理 |
| 延迟任务(如30分钟后发邮件) | SQS + 延迟队列 | 简单可靠,无需额外组件 |
八、常见问题 FAQ
Q: SQS和Kafka(MSK)该怎么选?
A: 如果只是做异步任务队列和事件通知,选SQS——零运维、按量付费、分钟级上手。如果需要流式处理(实时日志分析、CDC数据同步、复杂事件处理),选Kafka(MSK)。Kafka支持消息回放、消费者组、高吞吐,但运维复杂度高很多。
Q: SNS消息丢失了怎么办?
A: SNS本身不持久化消息。如果订阅者是SQS,消息先存入SQS再投递,有持久化保障。如果订阅者是HTTP端点或Lambda,SNS会重试约18次(指数退避),全部失败后消息会丢失。生产环境强烈建议SNS+SQS组合使用。
Q: 如何处理消息消费顺序问题?
A: 标准SQS不保证顺序。如果需要严格顺序,使用SQS FIFO队列。如果只需要"大致有序",可以在消息中加入时间戳,消费者端按时间排序处理。FIFO队列的吞吐量上限是3000条/秒,超过这个量需要分片(Message Group ID)。
Q: 消息太大怎么办?SQS有256KB限制。
A: 使用"大消息模式":将消息体存入S3,SQS消息中只存S3的引用(URL或Key)。消费者收到SQS消息后,从S3拉取完整内容。AWS SDK的SendMessage有内置支持,自动帮你做这个分拆。
Q: Lambda消费SQS的速度怎么控制?
A: 通过Lambda并发控制(Reserved Concurrency)限制同时处理的Lambda实例数。设置为50意味着最多50个Lambda同时运行,不会超过。还可以通过SQS的ApproximateNumberOfMessages指标做自动伸缩——队列积压多时增加并发,队列清空时缩减。
Q: 多个消费者同时读取同一条SQS消息会重复处理吗?
A: 不会。SQS的标准队列使用"长轮询+可见性超时"机制。一条消息被一个消费者拉取后,会变成"不可见"状态(持续VisibilityTimeout秒)。只有当消费者删除消息或超时后,消息才会重新可见。
⚠️ 文中价格为2026年参考价,实际费用随区域和官方调价变动,请以AWS官网为准。
🚀 需要AWS国际版账号?通过 3.chengzicloud.cloud 获取最新注册教程与专属优惠,助你轻松上云。想了解更多云服务实战?查看 AWS Lambda无服务器实战 和 AWS CloudFormation基础设施即代码实战。
本文由 3.chengzicloud.cloud 提供,点击访问首页了解更多