开发中,可能存在这样的需求:如果订单长时间不支付,需要自动关闭订单。
针对该需求可以通过如下一些方案实现:
通过定时任务定时扫描订单表,将达到过期时间的订单关闭;
通过mq发送延迟消息;
通过延迟队列实现。
本例,通过redisson的RDelayQueue(分布式延迟队列)实现。
配置类
package com.renr.rdelayedqueuetest.test;
import org.redisson.Redisson;
import org.redisson.api.RBlockingQueue;
import org.redisson.api.RDelayedQueue;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RedissonConfig {
@Bean
public RedissonClient redissonClient() {
Config config = new Config();
config.useSingleServer()
.setAddress("redis://127.0.0.1:6379")
.setRetryInterval(5000)
.setTimeout(10000)
.setConnectTimeout(10000);
return Redisson.create(config);
}
// 创建阻塞队列
@Bean
public RBlockingQueue<String> blockQueue() {
// 泛型表示存储的数据的类型
RBlockingQueue<String> blockingQueue = redissonClient().getBlockingQueue("delay-queue");
return blockingQueue;
}
// 根据阻塞队列创建延迟队列
@Bean
public RDelayedQueue<String> delayedQueue() {
RDelayedQueue<String> delayedQueue = redissonClient().getDelayedQueue(blockQueue());
return delayedQueue;
}
}
向延迟队列中添加和删除数据
package com.renr.rdelayedqueuetest.test;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RDelayedQueue;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
@Slf4j
@RestController
public class TestController {
@Resource
private RDelayedQueue<String> delayedQueue;
@PostMapping("/add")
public String add(String orderId, Integer timeout) {
log.info("add msg:" + orderId);
delayedQueue.offer(orderId, Objects.isNull(timeout) ? 30 : timeout, TimeUnit.SECONDS);
return "success";
}
@PostMapping("/delete")
public String remove(String orderId) {
delayedQueue.removeIf(messageId -> messageId.equals(orderId));
return "success";
}
}
从阻塞队列中取数据
package com.renr.rdelayedqueuetest.test;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBlockingQueue;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
@Slf4j
@Service
public class DelayQueueService {
@Resource
private RBlockingQueue<String> blockingQueue;
@Resource
private ThreadPoolTaskExecutor threadPoolExecutor;
@PostConstruct
public void init() {
// 在线程中阻塞读
threadPoolExecutor.execute(() -> {
// 通过死循环不断读取阻塞队列中数据
while (true) {
String msg = null;
try {
// 阻塞读
msg = blockingQueue.take();
// 实现相关的业务逻辑,比如根据订单号,修改数据表中订单为关闭状态
log.info("take msg:" + msg);
} catch (Exception e) {
log.warn("error:{}", e.getMessage());
}
}
});
}
}
测试


经过测试,
向延迟队列中添加数据后,如果数据还没有持久化到磁盘,redis挂了,那么数据丢失;
如果已经持久化到磁盘,哪怕已经超过数据的超时时间,只要启动redis,依旧可以读取到延迟队列中数据
434

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



