业务场景:在电商项目中,往往会有这样的一个功能设计,当用户下单后一段时间没有付款,系统就会在超时后关闭该订单。
通常我们会做一个定时任务每分钟来检查前半小时的订单,将没有付款的订单列表查询出来,然后对订单中的商品进行库存的恢复,然后将该订单设置为无效。
比如我们这里使用Spring Schedule
的方式做一个定时任务:
注:打开Spring Schedule 的自动注解扫描,在Spring配置中添加
<task:annotation-driven/>
@Component
@Slf4j
public class CloseOrderTask {
@Autowired
private IOrderService iOrderService;
@Scheduled(cron = "0 */1 * * * ? ")
public void closeOrderTaskV1() {
log.info("定时任务启动");
//执行关闭订单的操作
iOrderService.closeOrder();
log.info("定时任务结束");
}
}
在单服务器下这样执行并没有问题,但是随着业务量的增多,势必会演进成集群模式,在同一时刻有多个服务执行一个定时任务就会带来问题,首先是服务器资源的浪费,同时会带来业务逻辑的混乱,如果定时任务是做的数据库操作将会带来很大的风险。
Redis分布式锁
下面分析一下分布式情况下定时任务的解决方案
通常使用Redis作为分布式锁来解决这类问题,Redis分布式锁流程如下:
Redis分布式锁v1版本:
//注意:以下为了测试方便,定时时间都设置为10s
@Scheduled(cron = "0/10 * * * * ? ")
public void closeOrderTaskV1() {
log.info("定时任务启动");
long lockTime = 5000;//5秒
Long lockKeyResult = RedisShardedPoolUtil.setnx(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK, String.valueOf(System.currentTimeMillis() + lockTime));
//如果获得了分布式锁,执行关单业务
if (lockKeyResult != null && lockKeyResult.intValue() == 1) {
closeOrder(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
}else {
log.info("没有获得分布式锁");
}
log.info("定时任务结束================================");
}
//关闭订单,并释放锁
private void closeOrder(String lockName) {
Red