Redisson 延时队列实现(Java)

一、依赖

1.1 pom

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>3.21.1</version>
</dependency>

1.2 yml

spring:
  # Redis 相关配置
  redis:
    host: 127.0.0.1 # Redis 服务器主机。
    port: 13678 # Redis 服务器端口。(默认:6379)
    database: 0 # 连接工厂使用的数据库索引。(默认:0)
    password: 123456
    
# redission 配置
redisson:
  singleServerConfig:
    address: "redis://${spring.redis.host}:${spring.redis.port}"
    password: "${spring.redis.password}"
    database: "${spring.redis.database}"

二、RedissonConfig 配置类

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @Author cb
 * @CreateTime 2023/6/12 22:07 星期一
 */
@Configuration
public class RedissonConfig {

    /**
     * 延时队列名称
     */
    public static final String DELAY_NAME = "warning-record";

    @Value("${redisson.singleServerConfig.address}")
    private String address;
    @Value("${redisson.singleServerConfig.password}")
    private String password;
    @Value("${redisson.singleServerConfig.database}")
    private int database;

    @Bean(destroyMethod = "shutdown")
    public RedissonClient redissonClient() {
        Config config = new Config();
        config.useSingleServer().setAddress(address).setDatabase(database).setPassword(password);
        return Redisson.create(config);
    }

}

三、RedisDelayQueueUtil 工具类

import com.ccbx.rmbmanage.common.exception.BusinessException;
import com.ccbx.rmbmanage.config.RedissonConfig;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBlockingDeque;
import org.redisson.api.RDelayedQueue;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @Author cb
 * @CreateTime 2023/6/12 22:18 星期一
 */
@Slf4j
@Component
public class RedisDelayQueueUtil {

    /**
     * 延时队列名称
     */
    public static final String DELAY_NAME = "warning-record";

    @Resource
    private RedissonConfig redissonConfig;

    /**
     * 添加消息进入延迟队列
     *
     * @param value     消息值
     * @param delay     延迟时间
     * @param timeUnit  时间单位
     * @param queueCode 队列键
     * @param <T>       值类型
     */
    public <T> void addDelayQueue(T value, long delay, TimeUnit timeUnit, String queueCode) {
        if (!StringUtils.hasText(queueCode)) {
            queueCode = DELAY_NAME;
        }
        try {
            RedissonClient redissonClient = redissonConfig.redissonClient();
            RBlockingDeque<Object> blockingDeque = redissonClient.getBlockingDeque(queueCode);
            RDelayedQueue<Object> delayedQueue = redissonClient.getDelayedQueue(blockingDeque);
            // TODO cb 删除之前进来的id值 (判断之前是否已存在值)
            // delayedQueue.remove(value);
            delayedQueue.offer(value, delay, timeUnit);
            log.info("(添加延时队列成功) 队列键:{},队列值:{},延迟时间:{}", queueCode, value, timeUnit.toSeconds(delay) + "秒");
        } catch (Exception e) {
            log.error("(添加延时队列失败) {}", e.getMessage());
            throw new BusinessException("(添加延时队列失败)");
        }
    }

    /**
     * 获取延迟队列消息
     *
     * @param queueCode 队列键
     * @param <T>       队列中获取的值类型
     * @return 队列中返回的值
     * @throws InterruptedException
     */
    public <T> T getDelayQueue(String queueCode) throws InterruptedException {
        if (!StringUtils.hasText(queueCode)) {
            queueCode = DELAY_NAME;
        }
        RBlockingDeque<Map> blockingDeque = redissonConfig.redissonClient().getBlockingDeque(queueCode);
        return (T) blockingDeque.take();
    }

    /**
     * 删除延迟队列中的元素
     *
     * @param queueCode 队列名
     * @param <T>       删除元素值
     * @return 删除的元素
     */
    public <T> T removeDelayQueue(T value, String queueCode) {
        if (!StringUtils.hasText(queueCode)) {
            queueCode = DELAY_NAME;
        }
        RBlockingDeque<Object> blockingDeque = redissonConfig.redissonClient().getBlockingDeque(queueCode);
        RDelayedQueue<Object> delayedQueue = redissonConfig.redissonClient().getDelayedQueue(blockingDeque);
        delayedQueue.remove(value);
        return value;
    }

    /**
     * 项目重启时操作延时队列,消费因服务停止造成的阻塞的消息
     */
    @PostConstruct
    public void reScheduleDelayedTasks() {
        removeDelayQueue(null, null);
    }

}

四、RedisDelayQueueRunner 消费者

import com.ccbx.rmbmanage.domain.redisson.BicycleState;
import com.ccbx.rmbmanage.utils.RedisDelayQueueUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

/**
 * 启动延迟队列消费
 *
 * @Author cb
 * @CreateTime 2023/6/12 22:30 星期一
 */
@Slf4j
@Component
public class RedisDelayQueueRunner implements CommandLineRunner {

    @Resource
    private RedisDelayQueueUtil redisDelayQueueUtil;

    @Override
    public void run(String... args) throws Exception {
        new Thread(() -> {
            try {
                while (true) {
                    BicycleState value = redisDelayQueueUtil.getDelayQueue(RedisDelayQueueUtil.DELAY_NAME);
                    // 消费
                    consumerMessage(value);
                }
            }catch (InterruptedException e) {
                log.error("Redis 延迟队列消费监听器异常中断,{}", e.getMessage());
            }
        }).start();
        log.info("Redis 延迟队列消费监听器启动成功");
    }

    private void consumerMessage(BicycleState bicycleState) {
        log.warn("收到延迟消息进行处理,消息内容:{}", bicycleState);
    }

}

五、消息生产者

package com.ccbx.rmbmanage.controller.redisson;

import cn.hutool.core.lang.UUID;
import com.ccbx.rmbmanage.common.ApiResp;
import com.ccbx.rmbmanage.domain.redisson.BicycleState;
import com.ccbx.rmbmanage.utils.RedisDelayQueueUtil;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @Author cb
 * @CreateTime 2023/6/12 22:59 星期一
 */
@RestController
@RequestMapping("redisson")
public class RedissonController {

    @Resource
    private RedisDelayQueueUtil redisDelayQueueUtil;

    @PostMapping("create")
    public ApiResp<List<BicycleState>> createDelayMsg() {
        List<BicycleState> rltList = new ArrayList<>(5);
        for (int i = 0; i < 5; i++) {
            BicycleState bicycleState = new BicycleState();
            bicycleState.setBicycleSerialNumber(UUID.randomUUID().toString(true));
            bicycleState.setTerminalSerialNumber(UUID.randomUUID().toString(true));
            bicycleState.setState(i%2 == 0);

            redisDelayQueueUtil.addDelayQueue(bicycleState, 1, TimeUnit.MINUTES, null);
            rltList.add(bicycleState);
        }
        return ApiResp.success(rltList);
    }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
概要介绍:本门课程属于“Java分布式中间件大汇聚实战”系列课程,主要介绍了企业级项目中真实的应用场景的实现及主流的Java核心技术栈(Redis、RabbitMQ、Spring AOP、Redisson、ZooKeeper…)的实战等等。除此之外,还介绍了如何基于Redis设计并实战一款点赞系统(点赞、取消点赞、排行榜、用户中心、文章点赞用户列表…)可以说技术干货甚多,不仅可以巩固企业级应用系统的开发实战能力,相信在面试、跳槽涨薪方面也能带来相应的帮助!课程内容:传说中的金三银四、面试跳槽涨薪季已经来临,Debug特地为大家准备了一系列跟面试、跳槽、巩固核心技术栈相关的课程,本门课程属于第一季,其中的内容包括企业级项目中真实的应用场景实战、面试相关的技术点分享、主流的Java技术栈(Undertow、Redis、RabbitMQ、Spring AOP、Redisson、ZooKeeper…)实战等等。除此之外,我们还基于Redis设计并实战了一款点赞系统,可以说技术干货甚多。在课程的最后,Debug给大家整理了一份最新的面向BAT大厂招聘 ~ 2020年程序猿最新的Java面试题(附带目录和答案),希望对各位小伙伴的成长有所帮助!值得一提的是,本季课程实战的应用场景包括“日志记录”、“邮件发送”、“通告消息通知”、“短信验证码失效验证”、“会员到期自动提醒/到期前N天自动提醒”以及“点赞系统”的设计与实战,其大纲如下所示:其中,涉及到的技术栈包括Spring Boot2.0、Mybatis、Undertow、Redis、RabbitMQ、Redisson、Spring AOP、 Java8…下面罗列出本门课程重点介绍的价格应用案例以及业务场景的实现流程图!(1)基于Spring的消息驱动模型实现日志的异步记录:(2)基于消息中间件RabbitMQ的消息队列实现日志的异步记录:(3)基于缓存中间件Redis的订阅发布机制实现商户公告消息通知:(4)基于Redis的Key失效与定任务实现实现短信验证码的过期失效验证:其他核心、典型的应用案例和业务场景的实战可以详细参考“课程目录”!除此之外,我们还基于缓存中间件Redis设计并实战实现了点赞系统中的点赞功能模块,下面罗列出其中涉及到的相关功能模块的实战流程图:其课程收益如下所示:

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值