redisson实现延迟队列

1.redisson延迟队列工具类

java 复制代码
package com.cloud.app.system.utils;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RDelayedQueue;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

@Slf4j
@Component
@RequiredArgsConstructor
public class RedissonDelayQueueUtil {

    private final RedissonClient redissonClient;

    /**
     * 获取延迟队列实例
     * 注意:RDelayedQueue 底层依赖一个普通的 BlockingDeque 来存储到期任务
     * queueName 是队列的唯一标识
     */
    private <T> RDelayedQueue<T> getDelayedQueue(String queueName) {
        return redissonClient.getDelayedQueue(redissonClient.getBlockingDeque(queueName));
    }

    /**
     * 发送延迟任务
     *
     * @param queueKey 队列名称 (例如: "order:timeout:queue")
     * @param task     任务对象 (必须可序列化,建议实现 Serializable 或使用 JSON 字符串)
     * @param delay    延迟时间数值
     * @param unit     时间单位
     */
    public <T> void sendTask(String queueKey, T task, long delay, TimeUnit unit) {
        try {
            RDelayedQueue<T> delayedQueue = getDelayedQueue(queueKey);
            delayedQueue.offer(task, delay, unit);
            log.debug("任务已加入 Redisson 延迟队列: {}, 延迟: {} {}", queueKey, delay, unit);
        } catch (Exception e) {
            log.error("发送延迟任务失败: {}", queueKey, e);
            throw new RuntimeException("发送延迟任务失败", e);
        }
    }

    /**
     * 取消任务 (在任务执行前移除)
     * 注意:这只能移除尚未转移到"就绪队列"的任务。
     * 如果任务已经到期并转移到了 BlockingDeque 中,则需要从 BlockingDeque 中移除,逻辑较复杂。
     * 通常延迟队列场景下,取消操作较少,或者接受任务执行时再判断状态。
     */
    public <T> boolean cancelTask(String queueKey, T task) {
        try {
            RDelayedQueue<T> delayedQueue = getDelayedQueue(queueKey);
            return delayedQueue.remove(task);
        } catch (Exception e) {
            log.error("取消任务失败", e);
            return false;
        }
    }

    /**
     * 获取关联的就绪队列 (用于消费者监听)
     * 当延迟时间到达后,任务会自动从 DelayedQueue 移动到这里的 BlockingDeque
     */
    public <T> org.redisson.api.RBlockingDeque<T> getReadyQueue(String queueKey) {
        return redissonClient.getBlockingDeque(queueKey);
    }
}

2.延迟队列生产者

java 复制代码
package com.cloud.app.system.queues.produce;


import com.cloud.app.system.config.BackupConfig;
import com.cloud.app.system.queues.ResourceBackUpTask;
import com.cloud.app.system.utils.RedissonDelayQueueUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

@Slf4j
@Component
public class ResourceBackUpProduce {

    @Autowired
    private BackupConfig backupConfig;

    @Autowired
    private  RedissonDelayQueueUtil delayQueueUtil;

    public static final String RESOURCE_CANCEL_QUEUE ="cloud:resource:backup:queue";


    public void createOrder(Long instanceId, Long userId, Long backUpId) {
        ResourceBackUpTask task = new ResourceBackUpTask(instanceId, userId, backUpId);
        delayQueueUtil.sendTask(RESOURCE_CANCEL_QUEUE, task, backupConfig.getBackUpTime(), TimeUnit.SECONDS);
        log.info("已加入延时队列,id为:{}",instanceId);
    }

}

3.延迟队列消费者

java 复制代码
package com.cloud.app.system.queues.consumer;


import com.cloud.app.common.mcclient.domain.CloudPhoneActionDataInfo;
import com.cloud.app.system.domain.CpAppInstanceBackInfo;
import com.cloud.app.system.domain.CpDiskInfo;
import com.cloud.app.system.domain.CpInstanceInfo;
import com.cloud.app.system.domain.vo.CpBackUpVo;
import com.cloud.app.system.mapper.CpAppInstanceBackInfoMapper;
import com.cloud.app.system.mapper.CpDiskInfoMapper;
import com.cloud.app.system.mapper.CpInstanceInfoMapper;
import com.cloud.app.system.queues.ResourceBackUpTask;
import com.cloud.app.system.service.ICpAppInstanceBackInfoService;
import com.cloud.app.system.service.ICpInstanceInfoService;
import com.cloud.app.system.utils.RedissonDelayQueueUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.redisson.api.RBlockingDeque;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

@Slf4j
@Component
@RequiredArgsConstructor
public class ResourceBackUpListener implements CommandLineRunner {

    private final RedissonDelayQueueUtil delayQueueUtil;

    public static final String RESOURCE_CANCEL_QUEUE ="cloud:resource:backup:queue";

    @Autowired
    private ICpInstanceInfoService cpInstanceInfoService;

    @Autowired
    private ICpAppInstanceBackInfoService cpAppInstanceBackInfoService;

    @Autowired
    private CpInstanceInfoMapper cpInstanceInfoMapper;

    @Autowired
    private CpAppInstanceBackInfoMapper cpAppInstanceBackInfoMapper;

    @Autowired
    private CpDiskInfoMapper cpDiskInfoMapper;


    @Override
    public void run(String... args) throws Exception {
        log.info("=== Redisson 延迟队列消费者启动 ===");

        // 获取就绪队列 (任务到期后会自动进入这里)
        // 泛型需要和生产者一致,如果不确定类型,可以用 String 接收 JSON
        RBlockingDeque<ResourceBackUpTask> readyQueue =
                delayQueueUtil.getReadyQueue(RESOURCE_CANCEL_QUEUE);

        while (!Thread.currentThread().isInterrupted()) {
            try {
                // 【核心】阻塞式获取任务
                // 如果队列为空,线程会挂起,不消耗 CPU,直到有新任务到期
                ResourceBackUpTask event = readyQueue.take();

                log.info("【收到到期任务】订单ID: {}, 用户: {}", event.getInstanceId(), event.getUserId());

                // 执行业务逻辑
                handleCancelTask(event);

            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                log.warn("消费者线程被中断,停止运行");
                break;
            } catch (Exception e) {
                log.error("消费任务异常", e);
                // 发生异常时,任务已经出队。如果需要重试,需自行实现补偿机制或记录死信
            }
        }
        log.info("=== 消费者已停止 ===");
    }


    /**
     * 具体的业务处理逻辑
     */
    private void handleCancelTask(ResourceBackUpTask task) {
        try {
            // do something
           
            }
        } catch (Exception e) {
            log.error("任务失败: {}", task.getInstanceId(), e);
        }
    }
}
相关推荐
LaughingZhu17 小时前
Product Hunt 每日热榜 | 2026-05-21
前端·人工智能·经验分享·chatgpt·html
怕浪猫17 小时前
Electron 开发实战(一):从零入门核心基础与环境搭建
前端·electron·ai编程
廿一夏17 小时前
MySql存储引擎与索引
数据库·sql·mysql
Mahir0817 小时前
Spring 循环依赖深度解密:从问题本质到三级缓存源码级解析
java·后端·spring·缓存·面试·循环依赖·三级缓存
小鹏linux18 小时前
Ubuntu 22.04 部署开源免费具有精美现代web页面的Casdoor账号管理系统
linux·前端·ubuntu·开源·堡垒机
RyFit19 小时前
SpringAI 常见问题及解决方案大全
java·ai
石山代码19 小时前
C++ 内存分区 堆区
java·开发语言·c++
前端若水19 小时前
会话管理:创建、切换、删除对话历史
前端·人工智能·python·react.js
绝知此事19 小时前
【算法突围 01】线性结构与哈希表:后端开发的收纳术
java·数据结构·算法·面试·jdk·散列表
Bigger19 小时前
mini-cc:一个轻量级 AI 编程助手的诞生
前端·ai编程·claude