业务简介
订单创建时会填写一个预期时间,完成时间大于预期时间则需要发送系统通知给管理员
实现方式
- 定时任务轮询
- Redission
- 消息中间件
因为订单不会太多,轮询太过频繁会占用大量资源,故选择了通过Redission的方式来定时发送
AI代码
通过AI生成了代码,并调试验证,现粘贴备份
topic常量
public interface DelayQueueTopic {
String TICKET_TIMEOUT = "ticket_timeout_queue";
}
队列处理器
public interface RedisDelayQueueHandler<T> {
/**
* 队列名称
*/
String getQueueName();
/**
* 队列中到期后的处理逻辑
*/
void execute(T data);
}
队列工具类
@Component
@Slf4j
public class RedisDelayQueueUtil {
@Autowired
private RedissonClient redissonClient;
public void addDelayTask(String queueName, Long ticketId, long delay, TimeUnit unit) {
RDelayedQueue<String> delayed = getDelayedQueue(queueName);
delayed.offer(ticketId.toString(), delay, unit);
log.info("[DelayQueue] 添加任务 queue={} data={} delay={}", queueName, ticketId, delay);
}
public <T> RBlockingQueue<T> getBlockingQueue(String queueName) {
return redissonClient.getBlockingQueue(queueName);
}
public <T> RDelayedQueue<T> getDelayedQueue(String queueName) {
RBlockingQueue<T> queue = getBlockingQueue(queueName);
return redissonClient.getDelayedQueue(queue);
}
/**
* 删除延迟任务(取消订单等场景)
*/
public boolean removeDelayed(String queueName, Long ticketId) {
log.info("[DelayQueue] 删除任务 queue={} data={}", queueName, ticketId);
RDelayedQueue<String> delayedQueue = getDelayedQueue(queueName);
boolean result = delayedQueue.remove(ticketId.toString());
log.info("[DelayQueue] 删除结果 queue={} data={} result={}", queueName, ticketId, result);
return result;
}
}
消费者
@Component
@Slf4j
public class RedisDelayQueueConsumer {
@Autowired
private RedisDelayQueueUtil redisDelayQueueUtil;
@Autowired
private List<RedisDelayQueueHandler<?>> handlers;
@EventListener(ApplicationReadyEvent.class)
public void start() {
log.info("[DelayQueue] 初始化 {} 个队列监听器", handlers.size());
for (RedisDelayQueueHandler handler : handlers) {
String queueName = handler.getQueueName();
new Thread(() -> {
log.info("[DelayQueue] 队列 {} 开始监听", queueName);
RBlockingQueue<Object> queue = redisDelayQueueUtil.getBlockingQueue(queueName);
while (true) {
try {
Object data = queue.take();
handler.execute(data);
} catch (Exception e) {
log.error("[DelayQueue] 队列 {} 异常", queueName, e);
}
}
}, "delay-thread-" + queueName).start();
}
}
}
订单超时处理器
@Component
@Slf4j
public class TicketTimeoutHandler implements RedisDelayQueueHandler<String> {
@Override
public String getQueueName() {
return DelayQueueTopic.TICKET_TIMEOUT;
}
@Override
public void execute(String ticketId) {
log.info("运输工单超时通知:{}", ticketId);
// todo 发送通知
}
}
调用代码
//添加队列任务
redisDelayQueueUtil.addDelayTask(DelayQueueTopic.TICKET_TIMEOUT, 1234,180, TimeUnit.SECONDS);
//移除任务
redisDelayQueueUtil.removeDelayed(DelayQueueTopic.TICKET_TIMEOUT, 1234);
注意事项
超时时间过短
1.可能会导致对象还未创建,消费者处理任务时目标对象数据为空
2.移除对象时对象已经被移入消费者队列,无法移除
移除对象失败
1.超时时间设置过短,比如100ms、1s等
2.传参使用了long这个类型,可能会被redis序列化处理成Integer,导致key对不上删除失败
1314

被折叠的 条评论
为什么被折叠?



