1. Important Points#
Amazon SQS 是 managed message queue,用来解耦 producer 和 consumer。它不是 event bus,也不是 durable event log;核心语义是 message 被 consumer 成功处理后删除。
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 间并行。核心原则:
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