消息队列
队列如何承接异步任务?
生产者 → 持久队列 → 消费者领取 → 执行 → 确认完成
失败或确认丢失 → 按策略重试
- 消息队列: 暂存待处理工作,让生产和消费按各自速度推进,同一任务通常由一个消费者负责处理;
- 收益: 削峰、解耦并允许独立扩容,前台不用等待长任务完成;
- 结果语义: “已入队”与“业务已完成”是两个状态,应返回任务标识并允许查询结果;
- 故障条件: 只有启用并正确配置持久化与确认机制,才能把离线期间的可靠缓冲作为保证;
推送、拉取和任务调度如何选择?
- 推送: 代理主动把可用消息交给消费者,减少主动轮询,但需要控制消费者接收速度;
- 拉取: 消费者按自己的处理能力请求消息,可通过长轮询减少空轮询;
- 延迟与定时: 在到期后才让消息可见,适合延后处理,不等于任务一定在精确时刻完成;
- 任务队列: 在消息传递基础上管理任务执行、结果与调度,适合后台计算;
如何理解投递次数与顺序保证?
| 保证 | 含义 | 应用需要处理什么 |
|---|---|---|
| 至少一次 | 失败后可能重复投递 | 幂等、去重 |
| 恰好一次 | 在明确范围内避免重复生效 | 事务边界、去重期限、外部副作用 |
| FIFO | 指定范围内保持先入先出 | 分区、并发处理和重试后的顺序 |
- 幂等: 同一任务执行多次仍只有一次业务效果,例如以业务请求 ID 对结果做唯一约束;
- 顺序范围: 分区或消息组内顺序不等于整个系统的全局顺序,发送顺序也不自动等于处理完成顺序;
- 原文修正: FIFO 不自动保证端到端恰好一次,队列去重不能阻止外部服务被重复扣款;
无法处理的消息应该去哪里?
- 死信队列: 把超过重试次数或无法处理的消息隔离,以便检查和修复,避免堵住正常工作;
- 毒消息: 因格式、数据或代码问题持续导致处理失败,重试前应判断是否有恢复可能;
- 停止哨兵: 有些系统用 poison pill 表示主动要求消费者结束的控制消息,应与故障消息区分;
- 安全: 生产与消费应分别鉴权,并按需求加密传输和存储,队列不能成为绕过权限的旁路;
消费赶不上生产时如何施加背压?
- 背压: 下游忙时限制上游进入速度,用有限队列避免无限积压耗尽内存、磁盘和等待时间;
- 拒绝策略: 队满时根据接口语义返回繁忙或限流响应,让客户端在受控时间后重试;
- 重试策略: 使用退避并加入抖动,避免所有客户端同时重试形成新的峰值;
- 观测重点: 除队列长度外,还要观察最老消息年龄、消费失败率和生产消费速率差;
- 原文示例: SQS、RabbitMQ、ActiveMQ、ZeroMQ,其中持久化与代理形态需要按产品区分;