redis 实现延迟队列



1、延迟队列工厂



package cn.xs.qishi.micro.plan.common.queue;

import cn.xs.ambi.bas.util.StringUtils;
import cn.xs.ambi.mgt.redis.RedisManager;
import lombok.CustomLog;
import org.springframework.util.CollectionUtils;

import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;

/**
* redis 实现延时对列 planReminder
*
* @author liuxn
* @date 2021/10/9
*/
@CustomLog
public abstract class RedisDelayQueueFactory {


public abstract void initDelayQueue();

/**
* 最终执行的任务方法
*
* @param message 任务消息
*/
public abstract void invoke(String message);


/**
* 设置队列名字
*/
public abstract String setQueueName();

/**
* 发送消息
*
* @param content 内容
* @param delayTime 延迟时间
*/
public void send(String content, long delayTime) {
RedisManager.getRedisTemplate().opsForZSet().add(setQueueName(), content, (double) delayTime);
log.info(">> redis 延迟队列:[" + setQueueName() + "],消息内容:[" + content + "],delayTime:[" + delayTime + "]");
}

/**
* 队列消费者
*/
public void startDelayQueueMachine(Executor asyncExecutor) {
while (true) {
try {
long min = 0;
long max = System.currentTimeMillis() ;
Set<String> messages = RedisManager.getRedisTemplate().opsForZSet().rangeByScore(setQueueName(), min, max);
assert messages != null;
log.debug(">> 监控队列:[" + setQueueName() + "],消息数:[" + messages.size() + "]");
// 如果不为空则遍历判断其是否满足取消要求
if (!CollectionUtils.isEmpty(messages)) {
for (String message : messages) {
if (StringUtils.isBlank(message)) {
continue;
}
long num = RedisManager.getRedisTemplate().opsForZSet().remove(setQueueName(), message);
//如果移除成功, 消费
if (num > 0) {
asyncExecutor.execute(() -> invoke(message));
}
}
}
} catch (Exception e) {
log.error(">> 延迟队列监听异常", e);
} finally {
// 间隔30秒钟搞一次
try {
TimeUnit.SECONDS.sleep(30L);
} catch (InterruptedException e) {
log.error(">> 延迟队列监听异常", e);
}
}
}
}

}


2、计划队列实现



package cn.xs.qishi.micro.plan.common.queue;

import cn.xs.qishi.entity.pojo.CcPlanSendLog;
import cn.xs.qishi.micro.plan.common.constant.Constant;
import cn.xs.qishi.micro.plan.common.constant.RedisConstant;
import cn.xs.qishi.micro.plan.common.vo.PlanReminderVo;
import cn.xs.qishi.micro.plan.service.PlanService;
import com.alibaba.fastjson.JSON;
import lombok.CustomLog;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;

/**
* 计划任务提醒
*
* @author liuxn
* @date 2021/10/11
*/
@Component
@CustomLog
public class PlanReminderQueue extends RedisDelayQueueFactory {


@Autowired
private PlanService planService;

@Override
public void initDelayQueue() {
List<CcPlanSendLog> list = planService.getPlanSendLog(Constant.PLAN_SEND_LOG_STATUS_0);
for (CcPlanSendLog sendLog : list) {
PlanReminderVo reminderVo = new PlanReminderVo();
reminderVo.setPlanId(sendLog.getPid());
reminderVo.setPlanLogId(sendLog.getId());
send(reminderVo.toString(),sendLog.getSendTime().getTime());
}
log.info(">> 项目启动,提醒任务初始化..条数:"+list.size());
}

/**
* 最终执行的任务方法
*
* @param message 任务消息
*/
@Override
public void invoke(String message) {
log.info(">> 计划任务提醒队列接收到延迟消息:" + message);
PlanReminderVo vo = JSON.parseObject(message, PlanReminderVo.class);
planService.reminder(vo);
}

/**
* 设置队列名字
*/
@Override
public String setQueueName() {
return RedisConstant.QUEUE_PLAN_REMIND;
}
}


  3、项目启动时初始化。以及开启监听

 



package cn.xs.qishi.micro.plan.common.runner;

import cn.xs.qishi.micro.plan.common.queue.PlanReminderQueue;
import lombok.CustomLog;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

import java.util.concurrent.Executor;

/**
* 计划定时提醒初始类
*
* @author liuxn
* @date 2021/10/9
*/
@Component
@CustomLog
public class ScheduleReminderRunner implements ApplicationRunner {

@Autowired
private Executor asyncExecutor;
@Autowired
private PlanReminderQueue planReminderQueue;

@Override
public void run(ApplicationArguments args) throws Exception {
log.info(">> 项目启动完毕,初始化计划任务闹钟提醒.启动队列监听 ");
asyncExecutor.execute(() -> planReminderQueue.startDelayQueueMachine(asyncExecutor));
//队列初始化
planReminderQueue.initDelayQueue();
log.info(">> 项目启动完毕,初始化计划任务闹钟提醒完毕 !!! ");

}
}


4、注意

private Executor asyncExecutor; 是线程池
planReminderQueue.send("消息内容","延迟时间"); 代码调用
initDelayQueue 方法根据业务需求进行实现