一、定时任务
1、cron表达式
在spring中只写6个字段,分别是秒分时日月周,中间用空格隔开,每一个字段也有特殊的写法,上面已经介绍过了,这里不再赘述
注意:
- 周的写法一定注意,Quartz定时框架中中1代表周日,7代表周六,这里采用外国人的周计算方法,所以一定分清楚;但是spring中1代表周一,7代表周日,这是和中国人的一样,不过cron表达式做法是一样的;
- 看Cron表达式应该从最后一个非通配符字段开始看起,然后从右往左看,具有通配符的地方代表every每一个都是如此,当然它是被cron表达式右边一点的字段限制着;
- 如何避免定时任务阻塞
2、cron示例
cron表达式生成器:https://cron.qqe2.com
二、具体使用
直接上代码好说明:
/**
* 秒杀商品定时上架配置
*/
@EnableScheduling // 开启定时任务
@EnableAsync // 开启异步任务
@Configuration
public class ScheduledConfig {
@Autowired
private SeckillService seckillService;
@Autowired
private RedissonClient redissonClient;
/**
* 上架明天的秒杀商品
*/
@Scheduled(cron = "0 30 5 * * ?")
public void uploadSeckillGoods() {
// 添加分布式锁,避免同一时间内多个机器同时往redis中添加相关信息
RLock lock = redissonClient.getLock(SeckillConstant.UPLOAD_LOCK);
lock.lock();
try{
// 执行业务代码
seckillService.uploadSeckillGoods();
} finally {
// 解除分布式锁
lock.unlock();
}
}
/**
* 清除前一天的库存相关信息,包括商品、库存,还要把使用之后的库存更新到数据库中
*/
@Scheduled(cron = "0 0 5 * * ?")
public void cleanSeckillSkuInfo() {
// 添加分布式锁,避免同一时间内多个机器同时去清理
RLock lock = redissonClient.getLock(SeckillConstant.CLEAN_LOCK);
lock.lock();
try{
// 执行业务代码
seckillService.cleanSeckillSkuInfo();
} finally {
// 解除分布式锁
lock.unlock();
}
}
}
@EnableScheduling
开启定时任务是必不可少的,但是这个定时任务用到的线程池中只有一个线程,所以相当于是阻塞式的,但是异步任务不应该是阻塞式的,因此我们需要让定时任务异步执行,所以就需要加上@EnableAsync
注解,除此之外还需要在application.yml中添加定时任务中用到的异步任务相关的线程池配置,如下所示:
# 定时任务中用到的异步任务使用到的线程池配置
spring:
task:
execution:
pool:
core-size: 5
max-size: 9
queue-capacity: 100000
keep-alive: 10s
这样才能真正让定时任务异步执行,另外这几个值我们可以复用,比如我们其他地方还需要使用线程池,那我们就可以将这几个配置用上也是可以的
最后还要说一下里面的分布式锁,这是因为异步执行的时候不能让多个线程同时执行同一个定时任务,因此我们需要分布式锁,然后我们还要保证幂等性,不能让商品上架之后再次上架吧,所以我们需要在所使用的方法中进行幂等性判断,比如:
// 查询最近1天的秒杀商品信息集合
List<SeckillSessionEntityVo> seckillVos = couponFeignService.getSeckillGoods();
// 将秒杀商品信息缓存到redis中
if (seckillVos != null && seckillVos.size() > 0) {
*****************
}