01 它是干嘛的
SQS 与 SNS 解决的是「系统之间怎么可靠地互相通知」这件事。SQS 是一个排队信箱:生产者把消息放进去,消费者按自己的节奏取走处理;SNS 是一个广播喇叭:一条消息可以同时推送给很多个订阅者。它们共同实现了四个目标:解耦、削峰、异步、重试。
02 为什么会有它
两个程序直接对话,为什么一拥堵就会连环雪崩
系统之间的合作,最早都是「直接调用」:订单服务下的单,立刻去通知库存、通知支付、通知发货。看起来干脆,其实非常脆弱——只要其中一个慢了一点,前面的服务就得一直等着;等的人越多,积压越多,最终整条链路一起卡死。这就像你打电话给客服,对方没接你就得一直握着电话,什么也干不了。
大促时这个问题会放大成灾难。下单量瞬间暴涨,但下游处理能力有限,一瞬间涌来的请求把下游压垮,连带把上游也拖死。为了那几小时的峰值,把每一环都扩到能扛峰值,成本高得离谱,而且平时全在浪费。
消息服务的思路是把「直接对话」改成「留个条子」。发送方把要做的事写进队列就可以走了,不关心谁处理、什么时候处理;接收方按自己的速度慢慢取、慢慢做。这样发送方不会因为接收方慢而被拖累,接收方也不会被瞬间的洪流冲垮。这条「先存下来、慢慢处理」的思路,正是现代分布式系统稳定运行的基石之一。
03 它怎么工作
消息服务的核心是一条流水线:生产者只管把消息放进队列,消息会被可靠保存,消费者按自己的节奏取走处理,处理成功后才删除,失败则被重新投递。
一条消息的旅程:从生产者投递到消费者确认
- 1① 生产者把消息放进队列
业务程序发一条消息就可以立刻返回,不必等待任何人处理,这正是「解耦」与「异步」的开始。
- 2② 消息被可靠保存
消息会在队列中留存一段时间,期间即使消费者宕机或重启,消息也不会丢。
- 3③ 消费者按自己的节奏取走
SQS 是「谁空谁来取」的点对点模式,多个消费者同时工作会自动分摊消息,处理能力可以随时增加。
- 4④ 成功后删除,失败则重新投递
处理完成必须显式删除消息;如果处理到一半程序崩了,消息会在超时后重新出现,被再次处理,保证「至少做一次」。
- 5⑤ 多次失败送进死信队列
反复处理不成功的「坏消息」被单独收进死信队列,避免它一直循环,也方便人工事后排查。
04 谁在用它
下单请求先进入队列,订单系统按自己能承受的速度慢慢处理,躲过一瞬间的流量洪峰。
下单之后要发短信、发邮件、更新积分,全部改成发消息,任何一环出问题都不影响下单主流程。
生成报表、压缩图片、处理视频等耗时操作交给队列后台慢慢做,用户不必在页面上干等。
用 SNS 广播一条事件,数据分析、监控、通知等多个下游各自订阅、互不影响。
第三方接口临时出错时,消息自动回队列重试,配合死信队列兜底,避免任务凭空丢失。
05 怎么用
概念不难,难点在于「想清楚哪些环节对重复处理是安全的」,否则重试会带来数据错乱。
- 01在控制台创建一个队列,先选标准队列(追求吞吐)还是先进先出队列(追求严格顺序)。
- 02给队列配一个死信队列,并设置「最多重试几次后转入死信」,避免坏消息无限循环。
- 03让业务程序作为生产者把消息发进队列,发送成功后即可继续执行,不等下游。
- 04编写消费者程序循环拉取消息、处理业务、成功后删除消息。
- 05需要一对多广播时改用 SNS 主题,让多个队列或系统同时订阅同一条消息。
- 06上线后监控队列积压量,积压持续增长说明消费者处理太慢,需要增加消费者数量。
import { SQSClient, SendMessageCommand, ReceiveMessageCommand } from '@aws-sdk/client-sqs'
const sqs = new SQSClient({ region: 'us-east-1' })
const QueueUrl = 'https://sqs.us-east-1.amazonaws.com/123456789012/orders'
// 生产者:把订单消息放进队列后立即返回,不必等谁处理
await sqs.send(new SendMessageCommand({
QueueUrl,
MessageBody: JSON.stringify({ orderId: 'A-1001', action: 'create' })
}))
// 消费者:拉取消息,处理成功后必须显式删除,否则超时后会被重投
const { Messages = [] } = await sqs.send(new ReceiveMessageCommand({
QueueUrl, MaxNumberOfMessages: 1
}))
console.log(Messages[0]?.Body)避坑提示
- !SQS 保证「至少处理一次」,同一消息可能被处理两次,业务逻辑要做到重复执行也不会出错。
- !SQS 是点对点拉取,一条消息只会被一个消费者拿走;SNS 是发布订阅推送,一条消息会送到所有订阅者,二者常搭配使用。
- !一定要设置死信队列,否则一条永远处理失败的消息会把队列卡住甚至无限重试,悄悄消耗成本。
06 关键概念
- 生产者与消费者
- 前者负责把消息放进队列,后者负责取出来处理,两者互不等待。
- 削峰填谷
- 把瞬间的流量高峰先存进队列,让下游按稳定速度处理,避免被冲垮。
- 死信队列
- 反复处理失败的消息被单独存放的地方,便于排查且不会卡住正常消息。
- 可见性超时
- 消息被取出后暂时对其他人隐藏的一段时间,处理超时未确认就会重新出现。
- 发布订阅
- 一条消息同时推送给所有订阅者,像广播一样,而点对点队列则是一条消息只给一个人。