延迟队列,顾名思义它是一种带有延迟功能的消息队列。那么,是在什么场景下我才需要这样的队列呢?1. 背景
我们先看看以下业务场景:
1.当订单一直处于未支付状态时,如何及时的关闭订单
2.如何定期检查处于退款状态的订单是否已经退款成功
3.在订单长时间没有收到下游系统的状态通知的时候,如何实现阶梯式的同步订单状态的策略
4.在系统通知上游系统支付成功终态时,上游系统返回通知失败,如何进行异5.步通知实行分频率发送:15s 3m 10m 30m 30m 1h 2h 6h 15h
6.对于红包场景,账户 A 对账户 B 发出红包通常在 1 天后会自动归还到原账户。
7.对于实时支付场景,如果账户 A 对商户 S 付款 100 元,5秒后没有收到支付方回调将自动取消订单。
2. 设计目标
实时性:允许存在一定时间的秒级误差
高可用性:支持单机、支持集群
支持消息删除:业务会随时删除指定消息
消息可靠性:保证至少被消费一次
消息持久化:基于Redis自身的持久化特性,如果Redis数据丢失,意味着延迟消息的丢失,不过可以做主备和集群保证。这个可以考虑后续优化将消息持久化到MangoDB中
方案一:
采用通过定时任务采用数据库/非关系型数据库轮询方案。
优点:
1. 实现简单,对于项目前期这样是最容易的解决方案。
缺点:
1. DB 有效使用率低,需要将一部分的数据库的QPS分配给 JOB 的无效轮询。
2. 服务资源浪费,因为轮询需要对所有的数据做一次 SCAN 扫描 JOB 服务的资源开销很大。
方案二:
采用延迟队列:
优点:
1. 服务的资源使用率较高,能够精确的实现超时任务的执行。
2. 减少 DB 的查询次数,能够降低数据库的压力
缺点:
1. 对于延迟队列来说本身设计比较复杂,目前没有通用的比较好过的方案。
基于 Redis 的延迟队列实现
基于以上的分析,我决定通过 Redis 来实现分布式队列。
设计思路:
1. 第一步将需要发放的消息发送到延迟队列中。
2. 延迟队列将数据存入 Redis 的 ZSet 有序集合中 score 为当前时间戳,member 存入需要发送的数据。
3. 添加一个 schedule 来进行对 Redis 有序队列的轮询。
4. 如果到达达到消息的执行时间,那么就进行业务的