业务场景
业务上有个简单的延时队列的需求(对下单超过15分钟没有支付的订单进行取消操作),整个消息中间件吧,项目上以前本身也没设立消息中间件,加一个似乎也没必要,对方不会运维的话,就惨了。
轮询查找订单表吧?订单表可能还挺长的,心疼sql。
遂折中一下,轮询redis吧,反正redis本身项目就有。
思路如下:
1、使用zset数据结构存储,订单号为key,时间为score。
2、新增订单的时候,将订单号插入zset。
3、设定轮询,每分钟轮询一次zset,找出score小于当前秒数的数据,进行处理,然后将key在zset内删除。
添加操作
不需要提前设定zset,zadd的时候如果没有该zset,则会自动新增的。
我们还是使用Jedis。
//前置准备
//log,按照你们自己的习惯来吧
private final static Logger logger = LoggerFactory.getLogger(xxxxx.class);
//使用的是JedisPool
@Autowired
public JedisPool jedisPool;
//业务代码
//设定一个固定的延时队列的名字
String queueKey= "OrderTask";
try {
jedis = jedisPool.getResource();
if (null == jedis) {
logger.info("无可用Jedis线程,返回============");
} else {
//将订单号,插入到延时队列,1000为1秒
jedis.zadd(queueKey,System.currentTimeMillis()+900000,“填写你要存的内容,这边我存了订单号”);
}
} catch (Exception e) {
logger.error("订单号入队延时队列失败——————{}", “自己的报错信息”);
logger.error(e.getMessage());
} finally {
if (jedis != null) {
//别忘了关闭链接
jedis.close();
}
}
轮询操作
业务代码肯定写在一个轮询里面。
避免大家不会开启轮询,贴一下示例。
先贴一下依赖
<!--quartz定时调度依赖-->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.quartz-scheduler/quartz-jobs -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz-jobs</artifactId>
<version>2.3.0</version>
</dependency>
建一个定时类,具体轮询时间,查询cron表达式吧,这边就不再赘述
@Configuration //1.主要用于标记配置类,兼备Component的效果。
@EnableScheduling // 2.开启定时任务
public class SaticScheduleTask {
@Scheduled(cron = "0 */1 * * * ?")
public void myTask(){
//每一分钟轮询一次
}
}
处理定时任务
因为现在的时间间隔是1分钟,还是比较长的,如果时间比较短,处理的运行时间比较长,建议先lock一下,避免重复处理。
String queueKey= "OrderTask";
Jedis jedis = null;
try {
jedis = jedisPool.getResource();
if (null == jedis) {
//logger,按照自己喜欢的来
//logger.info("无可用Jedis线程,返回============");
return;
}
//获取过期的数据
Set<String> values=jedis.zrangeByScore(queueKey,0,System.currentTimeMillis());
//轮询遍历处理
Iterator<String> iterator=values.iterator();
while(iterator.hasNext()){
//取出的数据
String data=iterator.next();
//todo 处理数据
//用完之后,将key从zset处删除,避免重复
jedis.zrem(queueKey,data);
}
}catch (Exception e) {
//logger.error("处理15分钟支付状态检查", e);
} finally {
if (jedis != null) {
jedis.close();
}
}
结语
这真的是一个比较简陋的延时队列,不过持久化依靠redis,感觉还行。
有意外报错的话,大不了就多轮询几次。或者等人发现了再解决,也没有ack机制,所以也是挺不靠谱的,使用在不太重要的地方还行,优点就是比较简单。