AWS SQS
本文缩写(英文全称与中文含义)
| 缩写 | 英文全称/名称 | 中文含义 |
|---|---|---|
| API | Application Programming Interface | 应用程序编程接口 |
| ARN | Amazon Resource Name | AWS 资源名称,用于唯一标识资源 |
| AWS | Amazon Web Services | 亚马逊云服务 |
| DLQ | Dead-Letter Queue | 死信队列 |
| DNS | Domain Name System | 域名系统 |
| FIFO | First In, First Out | 先进先出 |
| HTTP | Hypertext Transfer Protocol | 超文本传输协议 |
| ID | Identifier | 标识符 |
| JSON | JavaScript Object Notation | 一种结构化数据格式 |
| KMS | Key Management Service | 密钥管理服务 |
| MiB | Mebibyte | 二进制容量单位,1 MiB = 2²⁰ 字节 |
| S3 | Simple Storage Service | AWS 对象存储服务 |
| SNS | Simple Notification Service | AWS 消息通知服务 |
| SQS | Simple Queue Service | AWS 消息队列服务 |
| SSE | Server-Side Encryption | 服务端加密 |
1. Important Points#
Amazon SQS 是 managed message queue,用来解耦 producer 和 consumer。它不是 event bus,也不是 durable event log;核心语义是 message 被 consumer 成功处理后删除。
Visibility timeout 只是暂时隐藏消息,不保证业务只执行一次;处理成功后再删除消息,并按业务 ID 幂等。FIFO 的顺序范围是 Message Group。
SQS 是一个消息队列系统,Producer -> 发送消息,Queue -> 存储消息,Consumer -> 拉取并处理消息,消费者必须主动拉取(Pull Model),不是推送(Push)。
Polling(轮询)是 Consumer 通过 ReceiveMessage 请求不断向 SQS 查询是否有消息,Short Polling -> 立即返回,有消息就返回,没有就空返回,Long Polling -> 最多等待 0~20 秒,有消息立即返回,没有则等待超时。
MessageId 是 SQS 自动生成的唯一消息标识,用于标识每条消息,DeduplicationId 是 FIFO 用于去重的 ID,可以由用户提供,也可以通过 Content-based dedup(基于消息内容 hash 自动生成),用于防止消息重复进入队列,不是防止重复消费。
FIFO Queue 中引入 Message Group(MessageGroupId),用于控制顺序,同一个 Message Group 内消息必须严格顺序处理,不同 Message Group 之间可以并行。
FIFO 核心机制是 Message Group Lock,同一 Message Group 同一时刻只能有一个消费进度,msg1 被消费并未 delete 前 msg2 会被阻塞,只有 msg1 delete 后 msg2 才会被允许进入消费流程。
Consumer 在 FIFO 中是竞争关系,不绑定固定 Message Group,谁 poll 到消息谁处理,但同一个 Message Group 在同一时刻只会被一个“顺序执行权”占用。
Visibility Timeout 是消息被 Consumer 获取后在一段时间内对其他 Consumer 隐藏的机制,用于防止重复消费,如果 Consumer 未能 delete 消息,Timeout 到期后消息会重新可见并可能被其他 Consumer 再次消费。
At-least-once delivery 表示每条消息至少会被投递一次,但可能被投递多次,重复指的是同一条业务消息可能被多个 Consumer 多次处理(重复消费),不是消息内容重复。
Standard Queue 不保证顺序但吞吐高并发强,可能重复消费,FIFO Queue 保证顺序(Message Group 内)并通过 Dedup + Group Lock 控制顺序与去重,Group 内串行 Group 间并行。
VisibilityTimeout:
300 秒(Receive 时指定) 消息取出后不可见时长
时刻 0:消息在队列中,可见
时刻 1:Worker A poll → 拿到消息 → 消息马上不可见(Visibility)
时刻 2:Worker B poll → 看不到这条消息(被 A 锁住了)
...
时刻 300s:如果 A 还没 DeleteMessage → 超时 → 消息重新可见
时刻 301s:Worker A/B poll → 又能拿到这条消息了
WaitTimeSeconds:
10 秒(Receive 时指定) 长轮询等多久
MessageRetentionPeriod:
默认 4 天 消息在队列最多存活多久。DLQ 也有自己独立的 MessageRetentionPeriod,默认也是 4 天。
DelaySeconds:
默认 0 无延迟投递
MaxReceiveCount:
MaxReceiveCount=3 + RedrivePolicy 后被转入DLQ
第 1 次 Receive → worker 处理 → 失败 → 消息被锁 300 秒 → 超时释放
第 2 次 Receive → worker 处理 → 失败 → 消息被锁 300 秒 → 超时释放
第 3 次 Receive → worker 处理 → 失败 → SQS 检测到 receiveCount=3
↓
自动转发到 generation-jobs-dlq
↓
从主队列删除,不再重试核心原则:
every consumer must be idempotent
message is not processed until it is deleted
visibility timeout must match processing time
use long polling to reduce empty receives and cost
use DLQ for poison messages
create DLQ before source queue RedrivePolicy references its ARN
monitor queue age, visible messages, in-flight messages, and DLQ count
use FIFO only when ordering requirement is real
message body should be small; large payload should go to S3 and send pointer2. Service Configuration#
queue type#
| Type | Use Case | Delivery / Ordering | Notes |
|---|---|---|---|
| Standard | high throughput, task queue, normal async jobs | at-least-once, best-effort ordering | duplicates and out-of-order delivery must be handled |
| FIFO | ordered workflow, per-user command sequence, financial/order state changes | FIFO within MessageGroupId, deduplication support |
lower default throughput; design message groups carefully |
standard queue:
choose by default
scale consumer horizontally
duplicate messages are possible
order is not guaranteed
FIFO queue:
queue name must end with .fifo
each message needs MessageGroupId
deduplication uses MessageDeduplicationId or content-based dedup
messages in same group are processed one by one
use many message groups for parallelismrecommended baseline#
| Setting | Recommended Default | Notes |
|---|---|---|
| Message retention | 4 days or business retry window |
allowed range is 60 seconds to 14 days |
| Visibility timeout | 2-6x p99 processing time |
default is 30 seconds; max is 12 hours from receive |
| Receive wait time | 20 seconds |
enables long polling |
| Max receive count | 5 or 8 |
then move to DLQ |
| DLQ retention | longer than source queue retention | especially important for standard queues |
| SSE | enabled | use SSE-SQS by default, SSE-KMS for stricter control |
| Tags | env, service, owner, cost-center, data-classification |
cost and ownership |
SQS / SNS naming#
Use hyphen (-) as the default separator. SQS and SNS allow underscore (_), but hyphen naming is more consistent with AWS resource names, Kubernetes-style names, DNS-like names, Terraform variables, dashboards, and alerts.
base pattern:
<env>-<team>-<project>-<service>-<purpose>
fields:
env:
prod / staging / dev
team:
owning team, for example platform / payment / data
project:
product or project boundary, for example checkout / billing / warehouse
service:
service or bounded context, for example order / invoice / user-sync
purpose:
events / worker / task / command / state / notification / export
abbreviation:
allowed when the full name becomes too long
abbreviation must be stable and documented by the team
do not remove env or ownership/project identity
standard queue:
pattern:
<env>-<team>-<project>-<service>-<purpose>
<env>-<team>-<project>-<service>-<purpose>-dlq
examples:
prod-platform-checkout-order-payment-capture
prod-platform-checkout-order-payment-capture-dlq
dev-data-warehouse-user-sync-worker
dev-data-warehouse-user-sync-worker-dlq
SNS topic:
pattern:
<env>-<team>-<project>-<service>-events
examples:
prod-platform-checkout-order-events
prod-payment-billing-invoice-events
FIFO queue:
pattern:
<env>-<team>-<project>-<service>-<purpose>.fifo
<env>-<team>-<project>-<service>-<purpose>-dlq.fifo
examples:
prod-platform-checkout-order-state.fifo
prod-platform-checkout-order-state-dlq.fifo
prod-platform-checkout-order-events.fifo
avoid:
prod_order_payment_capture
prod_order_payment_capture_dlq
prod-order-payment-capture
prod-platform-checkout-order-payment-capture-dead-letter-queuemessage limits#
message size:
max 1 MiB including body and message attributes
message attributes:
max 10 custom attributes
included in 1 MiB message size limit
use for routing hints / trace id / content type / schema version
batch:
SendMessageBatch max 10 messages
DeleteMessageBatch max 10 messages
ChangeMessageVisibilityBatch max 10 messages
delay:
queue delay / per-message delay can delay delivery
max delay is 15 minutes3. Architecture / Core Concepts#
message lifecycle#
producer:
SendMessage
-> message stored durably in SQS
consumer:
ReceiveMessage
-> message becomes invisible for visibility timeout
consumer success:
DeleteMessage
-> message removed from queue
consumer failure / crash:
no DeleteMessage
-> visibility timeout expires
-> message becomes visible again
-> another receive attempt
too many receive attempts:
RedrivePolicy maxReceiveCount reached
-> message moved to DLQvisibility timeout#
visibility timeout controls:
how long a received message is hidden from other consumers
too short:
duplicate processing while first consumer is still working
high ApproximateReceiveCount
too long:
failed message retries slowly
DLQ detection delayed
in-flight messages remain high
best practice:
set queue default to normal processing time
extend with ChangeMessageVisibility for long jobs
never use visibility timeout as job schedulerlong polling#
long polling:
ReceiveMessage WaitTimeSeconds > 0
max 20 seconds
queries all SQS servers instead of a subset
reduces empty responses and cost
recommended:
set ReceiveMessageWaitTimeSeconds = 20 on queue
consumer HTTP/read timeout must be greater than WaitTimeSecondsDLQ#
DLQ is for poison messages:
invalid payload
missing downstream data
permanent business failure
consumer bug
DLQ is not:
normal retry queue
archive
replacement for alertingDLQ rules:
best practice is to keep source queue and DLQ in the same account and region
DLQ retention should be longer than source queue retention
alarm when DLQ visible messages > 0
redrive only after root cause is understood
be careful with FIFO DLQ because moving messages can break end-to-end ordering semanticsSee DLQ for redrive policy, redrive allow policy, and incident runbook.
source queue and DLQ relationship#
source queue:
the queue producer sends to and consumer polls from
DLQ:
another SQS queue created for failed messages from the source queue
must exist before source queue RedrivePolicy can reference its ARN
relation is by deadLetterTargetArn, not by queue name
type matching:
standard source queue -> standard DLQ
FIFO source queue -> FIFO DLQ, and DLQ name must also end with .fifo
recommended:
create one dedicated DLQ per important source queue
do not rely on naming convention alone; bind by ARN in RedrivePolicy4. Producer / Consumer Best Practices#
producer#
producer checklist:
send small JSON message or pointer to S3 object
include schema_version
include idempotency key / business id
include trace id in message attributes
use SendMessageBatch for high throughput
retry throttling or transient network errors with backoff
for FIFO, set MessageGroupId intentionallyStandard message:
{
"schema_version": 1,
"event_type": "payment.capture.requested",
"request_id": "req-123",
"order_id": "ord-1001",
"amount_cents": 1200
}FIFO design:
MessageGroupId:
good:
order_id when order events must be serial
user_id when user commands must be serial
bad:
"default" for all messages
tenant_id only when one tenant can be very hot
MessageDeduplicationId:
use stable business id:
payment_capture:req-123
order_state:ord-1001:version-7consumer#
consumer loop:
ReceiveMessage with WaitTimeSeconds=20 and MaxNumberOfMessages=10
validate schema
process idempotently
DeleteMessage only after successful processing
on recoverable failure, do not delete
on long work, extend visibility timeout
emit metrics for success/failure/latencyidempotency examples:
use order_id + event_type + request_id as processed key
conditional write in DynamoDB
unique constraint in PostgreSQL
external API idempotency key
compare expected state version before updatepoison message policy#
recommended default:
maxReceiveCount = 5
DLQ alert immediately when visible message count > 0
consumer logs message id, request id, schema_version, receive count
do not log sensitive message body
when to increase maxReceiveCount:
downstream failures are commonly transient
retry cost is low
visibility timeout is short
when to decrease maxReceiveCount:
invalid payload should fail fast
duplicate side effect is expensive
DLQ triage should happen early5. Reliability / Failure Handling#
availability:
SQS is regional managed service
messages are stored redundantly across multiple servers
design producer/consumer retry and backoff anyway
backpressure:
queue depth increases when producer > consumer
scale consumers based on visible messages and oldest message age
protect downstream dependencies from worker over-scaling
large messages:
keep SQS message <= 1 MiB
for large payload:
store payload in S3
send bucket/key/version/checksum in SQS
consumer deletes S3 object only when retention policy allows
redrive:
use DLQ redrive after fix
replay slowly when downstream side effects are not fully idempotent
keep original message attributes for traceability