SpringBoot项目中如何使用Redisson队列详解

一、SpringBoot配置Redisson

1.1 引入依赖

xml 复制代码
<!--Redisson延迟队列-->
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.13.1</version>
</dependency>

1.2 代码配置

java 复制代码
@Data
@Component
@RefreshScope
@ConfigurationProperties("spring.redis")
public class RedisConfigProperties {
    String host;
    String password;
    Cluster cluster;
}

@Data
class Cluster {
    Boolean enable;
    List<String> nodes;
}
java 复制代码
@Component
@Configuration
@RequiredArgsConstructor
public class RedissonConfig {

    private final RedisConfigProperties redisConfig;


    @Bean
    public RedissonClient redissonClient() {
        Config config = new Config();
        if (redisConfig.getCluster() != null && Boolean.TRUE.equals(redisConfig.getCluster().getEnable())) {
            ClusterServersConfig clusterServersConfig = config.useClusterServers();
            for(String node : redisConfig.getCluster().getNodes()) {
                clusterServersConfig.addNodeAddress("redis://" + node);
            }
            if (StrUtil.isNotBlank(redisConfig.getPassword())){
                clusterServersConfig.setPassword(redisConfig.getPassword());
            }
        } else {
            SingleServerConfig serverConfig = config.useSingleServer();
            serverConfig.setAddress("redis://"+redisConfig.getHost()+":6379");
            System.out.println("============================================================");
            System.out.println("redisson设置的地址为:" + "redis://"+redisConfig.getHost()+":6379");
            System.out.println("============================================================");
            if (StrUtil.isNotBlank(redisConfig.getPassword())){
               serverConfig.setPassword(redisConfig.getPassword());
            }
        }
        return Redisson.create(config);
    }
}

1.3 application.yml中配置

yaml 复制代码
spring:
  redis:
    host: 127.0.0.1

二、延时队列具体使用

2.1 编写一个工具类RedisDelayQueueUtil

java 复制代码
/**
 * @Description: redission延迟队列工具类
 */
@Slf4j
@Component
@RefreshScope
public class RedisDelayQueueUtil {
    
    // day代表单位是天,minutes代表单位是分钟(也可以是秒seconds, 但这个不在下面代码示例处理)
    @Value("${spring.mode}")
    private String mode;

    @Autowired
    private RedissonClient redissonClient;

    /**
     * 添加延迟队列
     * @param queueCode 队列键
     * @param value 队列值
     * @param delay 延迟时间
     * @param <T>
     */
    public <T> void addDelayQueue(String queueCode, String value, long delay) {
        try {
            RBlockingDeque<Object> blockingDeque = redissonClient.getBlockingDeque(queueCode);
            RDelayedQueue<Object> delayedQueue = redissonClient.getDelayedQueue(blockingDeque);

            delayedQueue.offer(value, delay, "day".equals(testMode) ? TimeUnit.MINUTES : TimeUnit.DAYS);
            log.info("(添加延时队列成功) 队列键:{},队列值:{},延迟时间:{}", queueCode, value, delay + "天");
        } catch (Exception e) {
            throw new RuntimeException("(添加延时队列失败)");
        }

    }

    /**
     * 删除延迟队列
     * @param queueCode 队列键
     * @param value 队列值
     * @param <T>
     */
    public <T> void removeDelayQueue(String queueCode, String value){
        try {
            RBlockingDeque<Object> blockingDeque = redissonClient.getBlockingDeque(queueCode);
            RDelayedQueue<Object> delayedQueue = redissonClient.getDelayedQueue(blockingDeque);
            delayedQueue.remove(value);
            log.info("(删除延时队列成功) 队列键:{},队列值:{}", queueCode, value);
        } catch (Exception e) {
            throw new RuntimeException("(删除延时队列失败)");
        }
    }

    /**
     * 获取延迟队列
     * @param queueCode 队列键
     * @param <T>
     * @return
     * @throws InterruptedException
     */
    public <T> T getDelayQueue(String queueCode) throws InterruptedException {
        RBlockingDeque<Map> blockingDeque = redissonClient.getBlockingDeque(queueCode);
        redissonClient.getDelayedQueue(blockingDeque);
        T value  = (T) blockingDeque.take();
        return value;
    }


    /**
     * @param 移除延时队列全部任务
     * @param code
     * @param task
     */
    public void removeTask(String code, String value) {
        RBlockingDeque<Object> blockingDeque = redissonClient.getBlockingDeque(code);
        RDelayedQueue<Object> delayedQueue = redissonClient.getDelayedQueue(blockingDeque);
        List<String> c = new ArrayList<>();
        c.add(value);
        delayedQueue.removeAll(c);
    }


}

2.2 在application.yml中配置时间单位

yaml 复制代码
spring:
  mode: day

2.3 延迟队列枚举类

java 复制代码
@Getter
@NoArgsConstructor
@AllArgsConstructor
public enum RedisDelayQueueEnum {
    
    // 这里可以配置多个枚举项,每个枚举项对应一个实现类
    OVER_TIME("OVER_TIME", "超时触发", "overTimeImpl"),
    ;

    // 延迟队列 Redis Key
    private String code;

    // 中文描述
    private String name;

    /**
     * 延迟队列具体业务实现的 Bean
     * 可通过 Spring 的上下文获取
     */
    private String beanId;

}

2.4 延迟队列枚举类中配置的实现类

java 复制代码
@Slf4j
@Component
public class ActOverTimeImpl implements RedisQueueHandle<String> {

    @Autowired
    @Lazy
    TestService testService;

    /**
     * 任务超时,监听
     * 可以在里面调用service的代码去处理具体的业务逻辑
     * @param value
     */
    @Override
    public void execute(String value) {
			
        log.info("接收到延迟任务【超时提醒】:{}", value);
        testService.dealDelayQueueExpire(value);
    }
}

2.5 项目启动时使用其它线程控制全部延时队列

java 复制代码
@Slf4j
@Component
@AllArgsConstructor
public class AppStartRunner implements ApplicationRunner {

    private final RedisDelayQueueUtil redisDelayQueueUtil;
    private final RedissonClient redissonClient;

    @Override
    @Order(value = 1)
    public void run(ApplicationArguments args) {

        log.info("服务启动了!");

        RedisDelayQueueEnum[] queueEnums = RedisDelayQueueEnum.values();

        for (RedisDelayQueueEnum queueEnum : queueEnums) {

            new Thread(() -> {
                while (true) {
                    try {
                        Object value = redisDelayQueueUtil.getDelayQueue(queueEnum.getCode());

                        if (value != null) {
                            RedisQueueHandle redisQueueHandle = SpringContextHolder.getBean(queueEnum.getBeanId());
                            redisQueueHandle.execute(value);
                        }
                    } catch (Exception e) {
                        log.error("(Redis延迟队列异常中断) {}", e.getMessage());
                    }
                }
            }).start();
        }
        log.info("(Redis延迟队列启动成功)");
    }

}

除了希望在一定时间之后触发某些任务外,平时还会有一些资源消耗比较大的任务,如果接口直接对外暴露,多人同时调用时有可能造成系统变慢甚至直接宕机。

在不改变系统配置,不升级系统硬件的情况下,我们可以将这种任务放到一个对列当中排队执行。

三、普通阻塞队列的使用

3.1 管理普阻塞通队列枚举类

java 复制代码
@Getter
@AllArgsConstructor
public enum RedisBlockingQueueEnum {

    CONSUME_RESOURCES_TASK("CONSUME_RESOURCES_TASK", "消耗资源的任务", "consume resourcesImpl"),
    ;


    private final String code;
    private final String name;

    /**
     * 延迟队列具体业务实现的 Bean
     * 可通过 Spring 的上下文获取
     */
    private final String beanId;
}

3.2 使用一个RedisBlockingQueueOperator去统一管理添加阻塞队列

java 复制代码
@Slf4j
@Component
@RequiredArgsConstructor
public class RedisBlockingQueueOperator {

    private final RedissonClient redissonClient;

    public void addConsumeResourcesTaskQueue(Long userId, Long tenantId) {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("userId", userId);
        jsonObject.put("tenantId", tenantId);
        RBlockingQueue<String> queue = redissonClient.getBlockingQueue(RedisBlockingQueueEnum.CONSUME_RESOURCES_TASK.getCode());
        queue.offer(jsonObject.toJSONString());
    }
}

3.3 Controller中将请求加入阻塞队列

java 复制代码
@RestController
@RequiredArgsConstructor
@RequestMapping("/test")
@Tag(description = "test", name = "测试Controller")
@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)
public class TestController {
    
    private final RedisBlockingQueueOperator queueOperator;
    
    
    @PostMapping("/consume_resource_task")
    public R consumeResourcesTask() throws Exception{
		queueOperator.addCombinedReleaseQueue(1 TenantContextHolder.getTenantId());
        return R.ok("success!");
    }

}

3.4 编写实现类

java 复制代码
@Slf4j
@Component
public class ConsumeResourcesTaskImpl implements RedisQueueHandle<String> {

    @Autowired
    @Lazy
    TestService testService;

    @Override
    public void execute(String value) throws Exception {
        JSONObject jsonObject = JSON.parseObject(value);
        log.info("延迟队列触发【处理耗时任务】:{}", value);
        testService.dealConsumeResourcesTask(value);
    }
}

3.5 项目启动时使用其它线程控制全部普通阻塞队列

java 复制代码
@Slf4j
@Component
@AllArgsConstructor
public class AppStartRunner implements ApplicationRunner {

    private final RedissonClient redissonClient;

    @Override
    @Order(value = 1)
    public void run(ApplicationArguments args) {

        log.info("服务启动了!");

        for (RedisBlockingQueueEnum queueEnum : RedisBlockingQueueEnum.values()) {
            new Thread(() -> {
                RBlockingQueue<String> queue = redissonClient.getBlockingQueue(queueEnum.getCode());
                while (true) {
                    try {
                        String value = queue.take();
                        if (value == null) continue;
                        RedisQueueHandle redisQueueHandle = SpringContextHolder.getBean(queueEnum.getBeanId());
                        redisQueueHandle.execute(value);
                    } catch (Exception e) {
                        log.error("(Redis阻塞队列异常中断) {}", e.getMessage());
                    }
                }

            }).start();
        }
        log.info("(Redis Blocking Queue 启动成功)");
    }

}
相关推荐
P.H. Infinity14 分钟前
【RabbitMQ】04-发送者可靠性
java·rabbitmq·java-rabbitmq
生命几十年3万天18 分钟前
java的threadlocal为何内存泄漏
java
caridle30 分钟前
教程:使用 InterBase Express 访问数据库(五):TIBTransaction
java·数据库·express
^velpro^35 分钟前
数据库连接池的创建
java·开发语言·数据库
苹果醋338 分钟前
Java8->Java19的初步探索
java·运维·spring boot·mysql·nginx
秋の花43 分钟前
【JAVA基础】Java集合基础
java·开发语言·windows
小松学前端1 小时前
第六章 7.0 LinkList
java·开发语言·网络
Wx-bishekaifayuan1 小时前
django电商易购系统-计算机设计毕业源码61059
java·spring boot·spring·spring cloud·django·sqlite·guava
customer081 小时前
【开源免费】基于SpringBoot+Vue.JS周边产品销售网站(JAVA毕业设计)
java·vue.js·spring boot·后端·spring cloud·java-ee·开源
全栈开发圈1 小时前
新书速览|Java网络爬虫精解与实践
java·开发语言·爬虫