Redisson的DelayedQueue最佳实践

build.gradle

compile 'org.redisson:redisson:3.11.4'

RedissonConfig.java

@Configuration
public class RedissonConfig {

    @Value("${spring.redis.host}")
    private String host;

    @Value("${spring.redis.port}")
    private String port;

    @Value("${spring.redis.password}")
    private String password;

    /**
     * 单机模式
     * @return
     */
    @Bean(destroyMethod = "shutdown")
    public RedissonClient redissonClient() {
        Config config = new Config();
        SingleServerConfig singleServerConfig = config.useSingleServer();
        singleServerConfig.setAddress("redis://" + host + ":" + port);
        if (StringUtils.isNotBlank(password)) {
            singleServerConfig.setPassword(password);
        }
        return Redisson.create(config);
    }

}

RedissonService.java

@Service
public class RedissonService {

    @Autowired
    private RedissonClient redissonClient;

    public void addDelay() {
        RBlockingQueue<CallDTO> blockingFairQueue = redissonClient.getBlockingQueue("delay_queue_call");
        RDelayedQueue<CallDTO> delayedQueue = redissonClient.getDelayedQueue(blockingFairQueue);
        delayedQueue.offer(new CallDTO(), 5, TimeUnit.SECONDS);
        // 不要调用下面的方法,否者会导致消费不及时
//        delayedQueue.destroy();
    }

}

说明:delayedQueue.destroy()方法是取消客户端定时任务,调用之后不能主动获取到监听的数据,建议不要调用,让它跟redisson实例一起销毁(spring容器关闭的时候)

AppStartupRunner.java

/**
 * spring-boot项目启动完成运行
 */
@Log4j2
@Component
public class AppStartupRunner implements CommandLineRunner {

    @Autowired
    private RedissonClient redissonClient;

    @Override
    public void run(String... args) {
        new Thread(()->{
            RBlockingQueue<CallDTO> blockingFairQueue = redissonClient.getBlockingQueue("delay_queue_call");
            // 开启客户端监听(必须调用),否者系统重启时拿不到已过期数据,要等到系统第一次调用getDelayedQueue方法时才能开启监听
            redissonClient.getDelayedQueue(blockingFairQueue);
            while (true){
                CallDTO dto = null;
                try {
                    dto = blockingFairQueue.take();
                } catch (Exception e) {
                    continue;
                }
                if (Objects.isNull(dto)) {
                    continue;
                }
                System.out.println(String.format("receive1=======dto:%s", JSON.toJSONString(dto)));
            }
        }).start();
    }

}

注意:延时队列的过期机制是靠客户端的监听(定时任务+redis订阅)实现的,所以在消费数据的时候要先开启监听,监听的开启逻辑在redissonClient.getDelayedQueue()里面(会触发一次任务执行,并开启定时任务)

延时队列的具体实现最好查看源码,涉及三个队列和一个发布订阅通道
参考:http://www.gxitsky.com/article/1615808250172713

  • 阻塞队列 List:KEY = queueName,执行 BLPOP 命令从左端弹出元素,右端插入元素。当一条数据到达过期时间的时候,会从redisson_delay_queue:{DelayMessage}中移除,加入到这个队列,客户端监听的就是这个队列,这个队列里面的全都是已经过期的数据。
  • 有序集合 Sorted Set:KEY = redisson_delay_queue_timeout:{queueName},score 是元素的过期时间,按从小到大排序,过期时间小于当前时间表示已过期,删除集合中的元素,同时将普通集合 List中对应的元素删除,并将元素添加到阻塞队列 List等待客户端消费。
  • 普通集合 List:KEY = redisson_delay_queue:{DelayMessage},按顺序从右端添加元素,元素过期会被删除。
  • 发布/订阅通道:redisson_delay_queue_channel,往延时队列中放入一个数据时,会将延时时间publish出去,客户端收到之后会按这个时间延时之后再执行定时任务。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值