基于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延时队列,这次彻底给你整明白了
基于Redission实现的延时队列_第1张图片
两个重要延时队列:

  • 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种实现延时队列的方案

你可能感兴趣的:(Redis,redission,延时队列,MQ,超时关闭,订单)