重启服务后Redisson队列一直阻塞 不消费过期数据

前言

  • Redisson + Redis做了个延迟队列,但是我重启之后居然不消费到期的数据了,非要我再往队列新增一条才开始消费。blockingDeque.take()一直阻塞状态。

解决方案

  • 之前是这样写的。
    public boolean delayOffer(RedisDelayQueueMessage msg, long time, TimeUnit timeUnit) {
        RBlockingDeque<Object> blockingDeque = redissonClient.getBlockingDeque(delayQueueName);
        RDelayedQueue<Object> delayedQueue = redissonClient.getDelayedQueue(blockingDeque);
        delayedQueue.offer(msg, time, timeUnit);
        return Boolean.TRUE;
    }
  • blockingDequedelayedQueue启动的时候就初始化好。

/**
 * @author Zhou Zhongqing
 * @ClassName RedisDelayQueueConfig
 * @description: 预先初始化延迟队列和阻塞队列对象
 * @date 2022-08-20 14:35
 */
@Configuration
public class RedisDelayQueueConfig {
    @Value("${xxx.delay.queue.name:xxxs_delay_queue}")
    private String delayQueueName;

    @Bean
    public RBlockingDeque blockingDeque(RedissonClient redissonClient) {
        return redissonClient.getBlockingDeque(delayQueueName);
    }

    @Bean
    public RDelayedQueue delayedQueue(RedissonClient redissonClient) {
        return redissonClient.getDelayedQueue(blockingDeque(redissonClient));
    }
}
  • 然后修改下使用的地方。
   @Resource
   private RBlockingDeque<Object> blockingDeque;
    @Resource
    private RDelayedQueue<Object> delayedQueue;

    @Override
    public boolean delayOffer(RedisDelayQueueMessage msg, long time, TimeUnit timeUnit) {
        delayedQueue.offer(msg, time, timeUnit);
        return Boolean.TRUE;
    }
  • 监听消息的代码如下
... 省略 ...
 private void initListener() {
        threadPoolTaskExecutor.execute(() -> { // threadPoolTaskExecutor是Spring封装的线程池类
            while (!destroy) {  // destroy 标记是否程序关闭
                try {
                    RedisDelayQueueMessage result = redisQueueService.delayTake();
                    if (!destroy) {
                        log.info("接收到消息 {}", objectMapperFace.writeValueAsString(result));
                        if (!ObjectUtils.isEmpty(result)) {
                        	// 根据不同消息类型走不同实现类 
                            RedisDelayQueueHandlerService redisDelayQueueHandler = redisDelayQueueHandlerMapping.get(result.getMessageType());
                            if (!ObjectUtils.isEmpty(redisDelayQueueHandler)) {
                                R res = redisDelayQueueHandler.execute(result);
                                if (!res.isSuccess()) {
                                
                                    log.error("消息处理失败了 {} ", objectMapperFace.writeValueAsString(result));
                                }
                            } else {
                                log.error("不能处理的消息 ");
                            }
                        }
                    }
                } catch (Exception e) {
                    log.error("延迟队列消费消息出现异常" + e);
                }
            }
        });
    }
  
  ...省略...
  RedisQueueServiceImpl.java
    @Override
    public RedisDelayQueueMessage delayTake() {
        try {
            return (RedisDelayQueueMessage) blockingDeque.take();
        } catch (InterruptedException e) {
            if (!destroy) {
                e.printStackTrace();
                throw new ServiceException(e.getMessage());
            }
        }
        return null;
    }
  • 现在消息过期后,服务已启动就能消费消息了,如下图所示。
    在这里插入图片描述

关闭程序时的异常

    private volatile boolean destroy = false;
    @Override
    public RedisDelayQueueMessage delayTake() {
        try {
            return (RedisDelayQueueMessage) blockingDeque.take();
        } catch (InterruptedException e) {
            if (!destroy) {
            // 如果是正在关闭程序就不要抛异常了
                e.printStackTrace();
                throw new ServiceException(e.getMessage());
            }
        }
        return null;
    }

    @PreDestroy
    public void destroy() {
        destroy = true;
    }

     private void initListener() {
        threadPoolTaskExecutor.execute(() -> {
            while (!destroy) {
                try {
                    RedisDelayQueueMessage result = redisQueueService.delayTake();
                    if (!destroy) {
                        log.info("接收到消息 {}", objectMapperFace.writeValueAsString(result));
                        if (!ObjectUtils.isEmpty(result)) {
                            ...省略...
                        }
                    }
                } catch (Exception e) {
                    log.error("延迟队列消费消息出现异常" + e);
                }
            }
        });
    }

这里我感觉会有一致性的问题,建议增加消费日志,记录消费每个消息的状态,方便补偿。

参考

posted on 2022-12-09 08:59  愤怒的苹果ext  阅读(993)  评论(0编辑  收藏  举报

导航