利用Quartz将库存和随机码保存到Redis
1.创建Job接口实现类
2.创建配置类,配置JobDetail和Trigger
在seckill包下创建timer.job包
在seckill包下创建timer.config包
首先我们编写缓存预热的操作,在job包下创建类SeckillInitialJob
- @Slf4j
- public class SeckillInitialJob implements Job {
- @Autowired
- private SeckillSkuMapper skuMapper;
- @Autowired
- private SeckillSpuMapper spuMapper;
- @Autowired
- private RedisTemplate redisTemplate;
-
- /*
- RedisTemplate对象在保存数据到Redis时,会将当前数据序列化后保存
- 这样做的好处是将序列化后的数据保存到Redis,读写效率高,缺点是不能在Redis中修改数据
- 我们现在要预热的信息包含sku的库存数,这个库存数如果也用上面的序列化的方式保存
- 就会因为高并发情况下的线程安全问题引发"超卖"
- 解决方案,我们需要一个能够直接在Redis中减少库存的方法来避免超卖的发生
- SpringDataRedis提供一个可以直接在Redis中操作数值的对象:StringRedisTemplate
- 使用StringRedisTemplate向Redis中保存数据,数据都会以字符串的方式保存
- 又因为Redis可以直接操作数值类型的字符串,所以可以通过它实现直接修改库存数
- 这样就不需要编写java代码判断了,再配合Redis天生单线程的特性,避免线程安全问题,防止超卖
- */
-
- @Autowired
- private StringRedisTemplate stringRedisTemplate;
-
- @Override
- public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
- // 当前方法是Quartz调度运行的,运行时是要预热的需求,所以秒杀还没有到时间
- // 我们设计的是提前5分钟预热
- // 所以我们要查询5分钟之后进行秒杀的商品信息
- LocalDateTime time = LocalDateTime.now().plusMinutes(5);
- // 查询这个时间所有的秒杀商品
- List
seckillSpus = spuMapper.findSeckillSpusByTime(time); - // 遍历所有即将进行秒杀的商品,将它们的库存数保存到Redis
- for (SeckillSpu spu : seckillSpus) {
- // 当前spu是商品的品类,并没有库存数
- // 库存数保存在具体规格商品表sku中,所以要先根据spuId查询sku列表
- List
seckillSkus = skuMapper. - findSeckillSkusBySpuId(spu.getSpuId());
- // 当前循环是变量spu的,查询到的sku列表需要再嵌套一层循环
- for (SeckillSku sku : seckillSkus) {
- log.info("开始将{}号sku商品的库存预热到Redis", sku.getSkuId());
- // 下面要确定当前sku的库存数的key
- // SeckillCacheUtils.getStockKey是能够获得事先准备好的库存key常量名称的方法
- // 所以skuStockKey可能是"mall:seckill:sku:stock:1"
- String skuStockKey=
- SeckillCacheUtils.getStockKey(sku.getSkuId());
- // 检查Redis中是否已经包含了这个key
- if(redisTemplate.hasKey(skuStockKey)){
- // 如果key已经存在,证明之前已经缓存过了,直接跳过即可
- log.info("{}号sku商品已经缓存过了",sku.getSkuId());
- }else{
- // 如果key不在Redis中,就要将sku的库存数保存到Redis
- // 这里要将库存数的字符串格式保存,以便后续直接在Redis中减少库存的操作
- // 设置过期时间,应该是秒杀活动时间,加5分钟,最好再加个随机数防雪崩
- stringRedisTemplate.boundValueOps(skuStockKey)
- .set(sku.getSeckillStock()+"",
- 125*60*1000+ RandomUtils.nextInt(10000),
- TimeUnit.MILLISECONDS);
- log.info("成功为{}号sku商品预热缓存",sku.getSkuId());
- }
- }
- // 上面sku库存数预热完成
- // 下面开始预热每个spu的随机码
- // 随机码的作用简单来说就是给访问spu设置一个随机的路径
- // 如果不知道这个随机的路径是无法访问我们spu信息的
- // 能够减少服务器的压力
- // 在缓存预热时我们的操作就是生成随机码并保存到Redis,以便在后续业务中获取
- // randCodeKey=mall:seckill:spu:url:rand:code:2
- String randCodeKey=SeckillCacheUtils.getRandCodeKey(spu.getS