基于Redission实现的延时队列

基于Redission实现的延时队列

背景

  • 当前业务中存在着超时关闭各种类型的订单的场景。
  • 项目里已集成了RocketMq,可以基于此实现延时队列。
  • 由于采用RocketMq实现延时队列有个缺点,那就是它不能灵活的支持各个精度的延时,只能按照事先配置好的延时级别进行。
  • 基于以上三点考虑,并在对比各种实现延时队列的方案后,决定采用Redission(项目里也有集成Redission)+ RocketMq 进行实现。

实现

几个重要的API

  • RBlockingQueue
  • RDelayedQueue

生产者
参数说明:

  • QueueEntity :业务实体,里面就是封装具体的业务数据。
public void addQueue(QueueEntity queueEntity, TimeUnit timeUnit, Long time) {
        RBlockingQueue<String> queue = redissonClient.getBlockingQueue(queueEntity.getRedisQueueEnum().getQueueKey());
        RDelayedQueue rDelayedQueue = redissonClient.getDelayedQueue(queue);
        String value = JSON.toJSONString(queueEntity);
        rDelayedQueue.offer(value, time, timeUnit);
        //释放队列
        rDelayedQueue.destroy();
      
    }

消费者
关于消费者的实现,可以在项目启动时,就开启监听:

@Component
public class QueueRunner implements CommandLineRunner {

    @Autowired
    private RDelayQueueTask rDelayQueueTask;

    @Override
    public void run(String... args) throws Exception {
        rDelayQueueTask.comsumeQueue();
    }
}

RDelayQueueTask实现:

  • RedisQueueEnum: 这个也是业务里定义的一个枚举类,存放所有需要延时操作的枚举key。
public void comsumeQueue() {
        for (RedisQueueEnum redisQueueEnum : RedisQueueEnum.values()) {
            RBlockingQueue<String> queue = redissonClient.getBlockingQueue(redisQueueEnum.getQueueKey());
            // 重新初始化一次延时队列
            // 有多少枚举就启动多少个线程进行监听
            new Thread(() -> {
                while (true) {
                    String json = null;
                    try {
                        json = queue.take();
						// 发送到MQ
                    
                    } catch (Exception e) {
                       
                        
                    }
                }
            }).start();
        }
    }

原理

图片来自:Redis延时队列,这次彻底给你整明白了
在这里插入图片描述
两个重要延时队列:

  • redisson_delay_queue:
  • redisson_delay_queue_timeout:

通过观察redis种的数据存储,可以发现前者是个list类型数据结构,后者是一个ZSet类型数据结构。

  • 在消费端启动的时候,就会订阅这redisson_delay_queue
  • 当生产者有数据offer时,redisson先把数据放到redisson_delay_queue_timeout (ZSet集合,按延时到期时间的时间戳为score排序)。
  • 同时发布内容到上面订阅的key,此时客户端进程开启一个延时任务,延时时间为发布的timeout。(HashedWheelTimer,时间轮)
  • 客户端进程的延时任务到了时间执行,从zset分页取出过了当前时间的数据,再将数据rpush到第一步的阻塞队列里。
  • 最终回调到 RBlockingQueue 的 take方法上,从而取到数据。

总结

优点:通过结合RocketMQ,基于Redission实现的延时队列会更具有可靠性。在MQ的重试机制、持久化等手段加持下,会更加可靠和可控。

缺点:随着业务规模的发展,越来越多的延时操作会导致线程数目不断增加,这里可能会有性能影响。另一方面如果某个时间点的数据突增,可能会产生一种情况,实际发送消息的时间比定好的延迟时间会更加久。

其它

有赞延时队列的实现方案
6种实现延时队列的方案

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

legendaryhaha

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值