AWS SNS/SQS消息队列实战:从零搭建高可用异步通信架构

📅 · AWS 国际版实战教程 - ChengziCloud

一句话结论

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 KB3000条/秒订单、交易等需顺序的场景
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提供了:

  1. 缓冲能力:突发流量先进队列排队,Lambda按自己的速率消费
  2. 自动重试:Lambda失败后消息回到队列,下次轮询再处理
  3. 死信兜底:多次失败的消息自动进死信队列,不阻塞正常流程
  4. 多消费者隔离:库存系统崩溃不影响邮件系统

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 Size10每次最多拉取的消息数
Maximum Batching Window5秒等待凑批的时间
concurrent instances根据队列深度调整并发执行的Lambda数量
Error destination死信队列ARNLambda异步调用失败时的目标
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价格
SQS100万次请求$0.40/百万次$0.50/百万次
SNS100万次请求$0.50/百万次—
数据传输(同区域)—免费免费
数据传输(跨区域)—$0.01/万条$0.01/万条

实际成本估算(月处理100万条消息):

场景SQS标准SQS FIFOSNS+SQS组合
消息数100万100万100万+100万(订阅)
请求费用$0.40$0.50$0.50 + $0.40 = $0.90
数据传输免费免费免费
月度总计$0.40**0.50$0.90

5.2 省钱技巧

  1. 批量操作:一次API调用发送/接收10条消息,API调用次数减少到1/10
  2. 长轮询:设置WaitTimeSeconds=20,减少空轮询的无效请求
  3. 选择标准队列:只有真正需要顺序保证时才用FIFO,标准队列便宜20%
  4. 合理设置消息保留期:不需要的消息及时删除,避免长期存储费用
  5. 利用免费额度:每月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 五个必须遵守的安全规则

  1. 最小权限IAM:生产者只给sns:Publish,消费者只给sqs:ReceiveMessage和sqs:DeleteMessage
  2. 加密传输:所有队列启用SSE-SQS或SSE-KMS加密
  3. VPC端点:内网流量走VPC端点,不出公网
  4. 消息内容脱敏:不要在消息体里放密码、信用卡号等敏感信息
  5. 死信队列必须配:没有DLQ的消息队列就像没有刹车的汽车

七、架构选型速查表

你的需求推荐方案原因
一个服务通知多个下游SNS → 多个SQS发布订阅,天然一对多
任务队列(如图片处理)SQS + Lambda按需伸缩,按量计费
需要消息顺序SQS FIFO保证严格顺序
跨服务事件路由EventBridge比SNS更灵活的规则路由
邮件/短信通知SNS直接订阅原生支持Email/SMS/HTTP
高吞吐日志流Kinesis或MSKSQS不适合流式处理
延迟任务(如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 提供,点击访问首页了解更多