AWS SQS 完整教程:消息队列创建、标准队列与 FIFO、Lambda 集成与计费方式

AWS SQS 教程

Amazon SQS(Simple Queue Service)是 AWS 提供的全托管消息队列服务,用于在应用程序的不同组件之间可靠地传递消息。它解决的核心问题是服务解耦——当一个组件产生的任务量超过另一个组件即时处理的能力时,消息队列充当缓冲层,让生产者和消费者以各自的速度独立运行,互不影响。

没有消息队列的系统,生产者必须等待消费者处理完当前任务才能继续,或者消费者需要持续轮询生产者是否有新任务。
两种方式都会造成资源浪费或响应延迟。SQS 将消息持久化存储,生产者投递完消息立即返回,消费者按自己的节奏取消息处理,解除两端之间的直接依赖。

SQS 的核心工作方式

SQS 的模型只有三个角色:生产者(Producer)、队列(Queue)、消费者(Consumer)。生产者向队列发送消息,消息持久存储在 SQS 中;消费者轮询队列取消息,处理完成后显式删除消息。这个”取消息 → 处理 → 删除”的三步流程是 SQS 的基础操作模式,所有的配置参数都围绕这三步展开。

SQS 没有主动推送能力,消费者必须主动向队列发送 ReceiveMessage 请求才能获取消息。这和 SNS(Simple Notification Service)的推送模式不同——SNS 将消息主动推送给订阅者,SQS 需要消费者轮询拉取。两者经常配合使用:SNS 扇出广播消息到多个 SQS 队列,每个队列后面挂接不同的消费者,实现一对多的分发架构。

标准队列与 FIFO 队列

SQS 提供两种队列类型,核心差别在于消息投递的顺序保证和吞吐量上限。

标准队列

标准队列支持近乎无限的吞吐量,每秒可以处理的 SendMessage、ReceiveMessage 和 DeleteMessage 调用次数没有实际上限。它提供”至少一次”投递保证:消息在队列中冗余存储于多个可用区,极少数情况下同一条消息可能被投递多次。消息的投递顺序是”尽最大努力”,整体上尽量维持发送顺序,但不做严格保证。

这意味着使用标准队列的消费者必须具备幂等性——同一条消息被处理两次,不应产生两次效果。以订单发货通知为例,重复处理同一条消息应该有机制检测并跳过,而不是重复发送两封邮件。

标准队列适合大多数高吞吐量场景:图片处理任务分发、电商后台订单排队、日志异步写入、批量发送通知。

FIFO 队列

FIFO(First-In-First-Out)队列提供严格的顺序保证和”精确一次”投递。发送顺序和接收顺序严格一致,同一条消息只会被投递和处理一次。代价是吞吐量受限:标准模式下每秒最多 300 次事务(每次事务最多包含 10 条消息,即每秒最多 3,000 条),开启高吞吐量模式后可以提升到每秒 3,000 次事务(即每秒最多 30,000 条消息)。

FIFO 队列的名称必须以 .fifo 结尾,一旦创建无法更改为标准队列(反之亦然),选择前应确认业务是否真正需要严格顺序。

FIFO 队列适合的场景:金融交易系统(账户借记必须在贷记之前完成)、需要严格按顺序处理命令的业务流(创建账户 → 设置权限 → 激活账户)、与第三方系统集成时对方要求按顺序接收事件。

对比项标准队列FIFO 队列
吞吐量近乎无限300–3,000 TPS
消息投递至少一次,可能重复精确一次
消息顺序尽最大努力严格保证
消费者幂等性要求必须不需要
适合场景高吞吐量、对顺序容忍严格顺序、金融事务

创建队列与关键配置参数

在 AWS 控制台搜索 SQS,进入后点击「创建队列」。以下几个配置项决定了队列的行为,需要在创建时根据业务需求判断。

可见性超时(Visibility Timeout)

可见性超时是 SQS 最重要的配置参数,也是最容易误解的一个。当消费者取出一条消息后,这条消息不会立即从队列中删除,而是变成”不可见”状态,持续可见性超时所设定的时长。在这段时间内,其他消费者看不到这条消息,当前消费者有足够时间处理并删除它。如果超时到期前消费者没有删除消息,消息重新变为可见,可以被其他消费者再次取走处理。

可见性超时的设置原则是:略大于消费者处理一条消息所需的最长时间。设置过短,消费者还没处理完消息就重新可见,导致重复处理;设置过长,消费者处理失败后队列长时间看不到这条消息,延迟了重试。可见性超时范围是 0 秒到 12 小时,默认 30 秒,配合 Lambda 使用时应设为 Lambda 函数超时时间的 6 倍。

用 AWS CLI 创建一个标准队列,设置 5 分钟可见性超时和 14 天消息保留期:

aws sqs create-queue \

  –queue-name order-processing-queue \

  –attributes ‘{

    “VisibilityTimeout”: “300”,

    “MessageRetentionPeriod”: “1209600”,

    “ReceiveMessageWaitTimeSeconds”: “20”

  }’

消息保留期(Message Retention Period)

消息在队列中保存的最长时间,范围 1 分钟到 14 天,默认 4 天。超过保留期未被消费的消息自动删除。对于关键业务消息,建议设为 14 天,给消费者故障后的充足恢复时间;对于时效性强的实时任务(如即时通知),较短的保留期可以避免队列积压过期消息。

长轮询 vs 短轮询

SQS 的消费者需要主动发起请求才能取消息。默认的短轮询(ReceiveMessageWaitTimeSeconds = 0)会立即返回响应,即使队列中暂时没有消息也会返回空响应,每次空响应同样计费。

长轮询(设置 ReceiveMessageWaitTimeSeconds 为 1–20 秒)让 SQS 在收到消息或等待时间到期后才返回响应,大幅减少空响应次数。在消息间歇性到达的场景下,长轮询可以减少约 90% 的空响应,直接降低请求次数对应的费用。生产环境中应始终启用长轮询,设置 ReceiveMessageWaitTimeSeconds = 20 是最常见的推荐配置。

发送和接收消息

用 AWS CLI 向队列发送一条消息:

# 获取队列 URL

QUEUE_URL=$(aws sqs get-queue-url \

  –queue-name order-processing-queue \

  –query QueueUrl –output text)

# 发送消息

aws sqs send-message \

  –queue-url $QUEUE_URL \

  –message-body ‘{“orderId”: “order-001”, “userId”: “user-123”, “amount”: 299.99}’ \

  –message-attributes ‘{

    “OrderType”: {

      “DataType”: “String”,

      “StringValue”: “express”

    }

  }’

用 Python boto3 接收并处理消息:

import boto3

import json

sqs = boto3.client(‘sqs’, region_name=’ap-east-1′)

queue_url = ‘https://sqs.ap-east-1.amazonaws.com/123456789012/order-processing-queue’

# 接收最多 10 条消息,长轮询等待最多 20 秒

response = sqs.receive_message(

    QueueUrl=queue_url,

    MaxNumberOfMessages=10,

    WaitTimeSeconds=20,

    MessageAttributeNames=[‘All’]

)

messages = response.get(‘Messages’, [])

for message in messages:

    body = json.loads(message[‘Body’])

    receipt_handle = message[‘ReceiptHandle’]

    print(f”处理订单:{body[‘orderId’]}”)

    # 处理业务逻辑

    process_order(body)

    # 处理成功后删除消息

    sqs.delete_message(

        QueueUrl=queue_url,

        ReceiptHandle=receipt_handle

    )

消息处理完成后必须显式调用 delete_message 删除,这是 SQS 设计的关键点。消费者不删除消息,超时后消息重新变为可见,另一个消费者实例会再次取走处理,这也是标准队列”至少一次”投递特性的实现基础。

死信队列(DLQ)配置

死信队列(Dead Letter Queue)是处理反复失败消息的机制。当一条消息在主队列中被取走超过 maxReceiveCount 次仍未被成功删除,SQS 将这条消息自动转移到指定的死信队列,不再继续重试。

死信队列的价值在于防止”毒消息”循环:一条消息因为格式错误或业务异常无法被正常处理,如果没有 DLQ,它会在可见性超时到期后不断重新出现,消耗消费者大量资源,同时阻碍后续正常消息的处理。将这类消息隔离到 DLQ,开发人员可以单独查看消息内容,分析失败原因,修复问题后再将消息从 DLQ 重新投递回主队列处理。

创建死信队列并关联到主队列:

# 先创建死信队列

aws sqs create-queue \

  –queue-name order-processing-dlq \

  –attributes ‘{“MessageRetentionPeriod”: “1209600”}’

# 获取 DLQ 的 ARN

DLQ_ARN=$(aws sqs get-queue-attributes \

  –queue-url https://sqs.ap-east-1.amazonaws.com/123456789012/order-processing-dlq \

  –attribute-names QueueArn \

  –query Attributes.QueueArn –output text)

# 为主队列配置 DLQ,超过 3 次接收后转移

aws sqs set-queue-attributes \

  –queue-url $QUEUE_URL \

  –attributes “{

    \”RedrivePolicy\”: \”{\\\”deadLetterTargetArn\\\”:\\\”$DLQ_ARN\\\”,\\\”maxReceiveCount\\\”:\\\”3\\\”}\”

  }”

maxReceiveCount 的值应根据业务对重试次数的容忍度来设定,通常设为 3–5 次。死信队列必须和主队列在同一 AWS 账号和区域,且类型相同(标准队列的 DLQ 必须也是标准队列,FIFO 队列的 DLQ 必须是 FIFO 队列)。

SQS 与 Lambda 集成

SQS 和 Lambda 的组合是 AWS 异步处理架构的核心模式:Lambda 自动轮询 SQS 队列,有消息时触发函数执行,处理完成后自动删除消息,不需要维护任何轮询基础设施。关于 Lambda 函数的创建和事件触发配置,可以参考 AWS Lambda教程

创建事件源映射

在 Lambda 控制台的触发器配置中选择 SQS,或用 CLI 创建事件源映射:

aws lambda create-event-source-mapping \

  –function-name order-processor \

  –event-source-arn arn:aws:sqs:ap-east-1:123456789012:order-processing-queue \

  –batch-size 10 \

  –maximum-batching-window-in-seconds 5 \

  –function-response-types ReportBatchItemFailures

事件源映射建立后,Lambda 服务开始轮询 SQS 队列,有消息时将一批消息作为一个事件传递给 Lambda 函数。Lambda 从 5 个并发批次开始,随着队列消息积压增加,每分钟最多增加 60 个并发函数,最高达到 1,000 个并发实例。

可见性超时的黄金规则

Lambda + SQS 配置中最常见的问题是可见性超时设置不当。可见性超时必须满足以下公式:

可见性超时 ≥ Lambda 函数超时时间 × 6 + 批处理窗口时间(MaximumBatchingWindowInSeconds)

Lambda 函数超时时间如果设为 60 秒,批处理窗口 5 秒,则可见性超时至少应设为 365 秒。设置过短时,Lambda 函数还在处理中,队列就认为处理失败,将消息重新投入队列,导致另一个 Lambda 实例重复处理同一条消息。

部分批处理失败(ReportBatchItemFailures)

默认情况下,Lambda 处理一批消息时,只要有一条失败,整批消息都会重新回到队列。这意味着 10 条消息里 9 条处理成功、1 条失败,成功的 9 条也会被重复处理。

启用 ReportBatchItemFailures 后,Lambda 函数可以在响应中指明哪些消息处理失败,只有失败的消息回到队列,成功的消息正常删除。Lambda 函数需要返回对应的格式:

def lambda_handler(event, context):

    failed_message_ids = []

    for record in event[‘Records’]:

        message_id = record[‘messageId’]

        body = json.loads(record[‘body’])

        try:

            process_order(body)

        except Exception as e:

            print(f”处理消息 {message_id} 失败:{e}”)

            failed_message_ids.append(message_id)

    # 返回失败消息的 ID,只有这些消息会重新回队列

    return {

        ‘batchItemFailures’: [

            {‘itemIdentifier’: msg_id}

            for msg_id in failed_message_ids

        ]

    }

生产环境中应始终启用 ReportBatchItemFailures,并配合死信队列设置 maxReceiveCount,确保反复失败的消息最终被隔离到 DLQ 而不是无限重试。

IAM 权限配置

Lambda 执行角色需要具备访问 SQS 的权限才能读取和删除消息。最小权限策略应包含 sqs:ReceiveMessage、sqs:DeleteMessage 和 sqs:GetQueueAttributes。如果队列启用了 KMS 加密,还需要添加对应 KMS 密钥的 kms:Decrypt 权限。关于 IAM 角色和权限策略的配置方式,可以参考 AWS IAM 权限管理教程。

计费方式

SQS 按请求次数计费,每月前 100 万次请求永久免费。超出免费额度后,标准队列 $0.40/百万次请求,FIFO 队列 $0.50/百万次请求。同一 AWS 区域内 SQS 与 EC2、Lambda 之间的数据传输免费,数据传输到公网才收费。

几个影响实际成本的关键点:

长轮询显著降低请求成本。 消费者使用短轮询时,每次空响应也计费。一个消费者每秒发一次请求,每月产生约 260 万次请求,大部分可能是空响应。启用长轮询后,同一个消费者每月请求次数可以减少 90% 以上,在消息不密集的场景下效果尤其明显。

批量发送和接收降低单条消息的请求成本。 SendMessageBatch 支持单次发送最多 10 条消息,ReceiveMessage 支持单次接收最多 10 条消息(标准队列)或 10 条(FIFO 队列),每次批量操作计为 1 次请求。高吞吐量场景下使用批量操作可以将请求次数减少到 1/10。

超过 256KB 的消息需要使用 S3 扩展客户端。 SQS 单条消息上限为 256KB,大于这个大小的消息(如处理结果、序列化对象)应存入 S3,SQS 消息只传递 S3 对象的引用。这个模式同时减少了 SQS 请求的费用,因为实际传输的消息体变小,处理消息的 Lambda 函数也只需要从 S3 读取必要的数据。

关于 AWS 各产品账单的构成和估算方式,可以参考 亚马逊云服务器价格 的完整分析。

常见使用场景

订单异步处理

电商的典型架构是:用户下单 API 接收请求后立即将订单消息投入 SQS,返回”订单已提交”给用户;后端 Worker(EC2 或 Lambda)异步从队列取出订单,依次完成库存扣减、支付验证、物流对接等操作。用户无需等待这些耗时操作完成,前端响应时间从数秒降到毫秒级,后端各环节可以独立扩缩容。

微服务之间的解耦

多个微服务直接调用时,一个服务故障会级联导致调用方超时或失败。引入 SQS 作为中间层,上游服务投递消息后立即返回,下游服务故障期间消息在队列中积压,服务恢复后继续处理,不会丢失数据。结合 AWS DynamoDB 存储处理结果,可以构建完整的异步处理链路,各环节状态持久化且相互独立。

图片和文件异步处理

用户上传文件后,应用立即返回上传成功,同时将处理任务(生成缩略图、提取文字、格式转换)投入 SQS。Lambda 订阅队列,异步执行处理任务,结果写回 S3。这个模式将高延迟的处理任务完全从请求路径中移出,同时 Lambda 根据队列积压自动扩缩并发实例,峰值时能处理大量并发上传,空闲时缩容到零,不产生计算费用。

账号开通与代理充值

使用 SQS 需要有效的 AWS 账号,SQS 本身没有资源配额申请门槛,创建队列和开始使用不需要额外审批。对于长期运行消息处理业务、月均 SQS 请求量超过千万次的团队,通过 AWS 代理商 渠道充值可以享受代理专属折扣(低至 7 折),付款支持 USDT 和对公转账,适合没有海外信用卡或需要企业对公结算的团队,账号免实名、免绑卡,1 分钟极速交付。

AWS 代理

滚动至顶部