java redis延迟队列_Redis实现延迟队列

本文介绍了如何利用Redis的ZSET和LIST特性构建一个延迟队列,用于处理如订单超时关闭等业务场景。设计目标包括实时性、高可用性和消息可靠性,并详细阐述了设计思路、数据结构和核心代码实现,同时也指出了系统的优化方向。
摘要由CSDN通过智能技术生成

延迟队列,顾名思义它是一种带有延迟功能的消息队列。那么,是在什么场景下我才需要这样的队列呢?

1. 背景

我们先看看以下业务场景:

当订单一直处于未支付状态时,如何及时的关闭订单

如何定期检查处于退款状态的订单是否已经退款成功

在订单长时间没有收到下游系统的状态通知的时候,如何实现阶梯式的同步订单状态的策略

在系统通知上游系统支付成功终态时,上游系统返回通知失败,如何进行异步通知实行分频率发送:15s 3m 10m 30m 30m 1h 2h 6h 15h

1.1 解决方案

最简单的方式,定时扫表。例如对于订单支付失效要求比较高的,每2S扫表一次检查过期的订单进行主动关单操作。优点是简单,缺点是每分钟全局扫表,浪费资源,如果遇到表数据订单量即将过期的订单量很大,会造成关单延迟。

使用RabbitMq或者其他MQ改造实现延迟队列,优点是,开源,现成的稳定的实现方案,缺点是:MQ是一个消息中间件,如果团队技术栈本来就有MQ,那还好,如果不是,那为了延迟队列而去部署一套MQ成本有点大

使用Redis的zset、list的特性,我们可以利用redis来实现一个延迟队列RedisDelayQueue

2. 设计目标

实时性:允许存在一定时间的秒级误差

高可用性:支持单机、支持集群

支持消息删除:业务会随时删除指定消息

消息可靠性:保证至少被消费一次

消息持久化:基于Redis自身的持久化特性,如果Redis数据丢失,意味着延迟消息的丢失,不过可以做主备和集群保证。这个可以考虑后续优化将消息持久化到MangoDB中

3. 设计方案

设计主要包含以下几点:

将整个Redis当做消息池,以KV形式存储消息

使用ZSET做优先队列,按照Score维持优先级

使用LIST结构,以先进先出的方式消费

ZSET和LIST存储消息地址(对应消息池的每个KEY)

自定义路由对象,存储ZSET和LIST名称,以点对点的方式将消息从ZSET路由到正确的LIST

使用定时器维护路由

根据TTL规则实现消息延迟

3.1 设计图

还是基于有赞的延迟队列设计,进行优化改造及代码实现。有赞设计

5cf85c4d1cd77ea095c63a2e9da865c2.png

3.2 数据结构

ZING:DELAY_QUEUE:JOB_POOL 是一个Hash_Table结构,里面存储了所有延迟队列的信息。KV结构:K=prefix+projectName field = topic+jobId V=CONENT;V由客户端传入的数据,消费的时候回传

ZING:DELAY_QUEUE:BUCKET 延迟队列的有序集合ZSET,存放K=ID和需要的执行时间戳,根据时间戳排序

ZING:DELAY_QUEUE:QUEUE LIST结构,每个Topic一个LIST,list存放的都是当前需要被消费的JOB

c7fd724b05f16400336083af36cc0aed.png

图片仅供参考,基本可以描述整个流程的执行过程,图片源于文末的参考博客中

3.3 任务的生命周期

新增一个JOB,会在ZING:DELAY_QUEUE:JOB_POOL中插入一条数据,记录了业务方消费方。ZING:DELAY_QUEUE:BUCKET也会插入一条记录,记录执行的时间戳

搬运线程会去ZING:DELAY_QUEUE:BUCKET中查找哪些执行时间戳的RunTimeMillis比现在的时间小,将这些记录全部删除;同时会解析出每个任务的Topic是什么,然后将这些任务PUSH到TOPIC对应的列表ZING:DELAY_QUEUE:QUEUE中

每个TOPIC的LIST都会有一个监听线程去批量获取LIST中的待消费数据,获取到的数据全部扔给这个TOPIC的消费线程池

消费线程池执行会去ZING:DELAY

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值