1.定时任务

1、spring中6位组成,不允许第七位的年,即秒、分、时、日、月、周

2、在周几的位置,1-7代表周一到周日,MON-SUN

3、定时任务不应该是阻塞的,默认是阻塞的。

(1)可以让业务以异步的方式运行,自己提交给线程池

(2)支持定时任务线程池,设置TaskShedulingProperties

​ spring.task.scheduling.pool.size=5 #默认size是1,也就是阻塞

(3)让定时任务异步执行,自动配置类 TaskExecutionAutoConfiguration

spring:task:execution:pool:core-size: 5 #默认是8max-size: 50 #默认最大是Integer.MAX_VALUE

定时任务不阻塞最终解决方案:异步+定时任务

@Async
@Scheduled(cron = "*/5 * * ? * 1")
public void hello(){log.info("定时任务...");try {Thread.sleep(3000);} catch (InterruptedException e) { }
}

基本注解:

1、定时任务

(1)@EnableScheduling:开启定时任务

(2)@Scheduled:开启一个定时任务

2、异步任务

(1)@EnableAsync:开启异步任务功能

(2)@Async:给希望异步执行的方法上标注

在配置类里面标注注解,其他地方就不需要标了

@EnableAsync
@Configuration
@EnableScheduling
public class ScheduledConfig {}

2. 秒杀商品上架

秒杀商品上架的流程图

gulimall-coupn服务:定时扫描数据,获取最近3天的秒杀活动

@Override // gulimall-coupon  SeckillSessionServiceImpl
public List<SeckillSessionEntity> getLate3DaySession() {// 获取最近3天的秒杀活动List<SeckillSessionEntity> list = this.list(new QueryWrapper<SeckillSessionEntity>().between("start_time", startTime(), endTime()));// 设置秒杀活动里的秒杀商品if(list != null && list.size() > 0){return list.stream().map(session -> {// 给每一个活动写入他们的秒杀项Long id = session.getId();// 根据活动id获取每个sku项List<SeckillSkuRelationEntity> entities =skuRelationService.list(new QueryWrapper<SeckillSkuRelationEntity>().eq("promotion_session_id", id));session.setRelationSkus(entities);return session;}).collect(Collectors.toList());}return null;
}
// LocalDateTime.of把时间合在一起组合:startTime=now+LocalTime.MIN:00:00
// 最小时间LocalTime.MIN:00:00
// 最大时间LocalTime.MAX:23:59:59:99999999
private String startTime(){LocalDateTime start = LocalDateTime.of(LocalDate.now(), LocalTime.MIN);return start.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
}
private String endTime(){LocalDate acquired = LocalDate.now().plusDays(2);//加两天return LocalDateTime.of(acquired, LocalTime.MAX).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
}

gulimall-seckill服务:

远程从gulimall-coupon中获取最近三天要参加秒杀的商品,写入到redis缓存中(活动信息+活动的关联的商品信息)

private final String SESSION_CACHE_PREFIX = "seckill:sessions:";
private final String SKUKILL_CACHE_PREFIX = "seckill:skus:";
private final String SKUSTOCK_SEMAPHONE = "seckill:stock:"; // +商品随机码
@Override // SeckillServiceImpl
public void uploadSeckillSkuLatest3Day() {// 1.扫描最近三天要参加秒杀的商品R r = couponFeignService.getLate3DaySession();if(r.getCode() == 0){List<SeckillSessionsWithSkus> sessions = r.getData(new TypeReference<List<SeckillSessionsWithSkus>>() {});// 2.缓存活动信息saveSessionInfo(sessions);// 3.缓存活动的关联的商品信息saveSessionSkuInfo(sessions);}
}

缓存活动信息

key=seckill:sessions:开始时间_结束时间

value=List 格式是sessionid-skuId

private void saveSessionInfo(List<SeckillSessionsWithSkus> sessions){if(sessions != null){// 遍历sessionsessions.stream().forEach(session -> {//时间比较的话Long值比较方便long startTime = session.getStartTime().getTime();long endTime = session.getEndTime().getTime();String key = SESSION_CACHE_PREFIX + startTime + "_" + endTime; // "seckill:sessions:";Boolean hasKey = stringRedisTemplate.hasKey(key);// 防止重复添加活动到redis中if(!hasKey){// 获取所有商品id // 格式:活动id-skuId// 遍历skuList<String> skus = session.getRelationSkus().stream().map(item -> item.getPromotionSessionId() + "-" + item.getSkuId()).collect(Collectors.toList());// 缓存活动信息,leftPushAll从左边往里面放一整个集合stringRedisTemplate.opsForList().leftPushAll(key, skus);}});}
}

缓存活动的关联的商品信息

创建SeckillSkuRedisTo来表示给缓存中写的数据传输内容

内容包括:活动场次id+sku详细信息(SkuInfoVo)+sku秒杀信息(秒杀价格、总量、开始结束时间)+秒杀随机码

此处添加秒杀随机码的目的:

  • 避免恶意争抢,确保是公平的秒杀,只有在秒杀开始时才可以秒杀,如果不加随机码,直接带skuId的话就会有被脚本恶意攻击的可能
  • 分布式信号量:seckill:stock:#(商品随机码),在使用库存作为分布式信号量时也是使用的这个随机码,避免被伪造的恶意请求把信号量给减去了

比如:seckill?skuId=1&key=skjdncsk

@Data
public class SeckillSkuRedisTo {private Long promotionId;/*** 活动场次id*/private Long promotionSessionId;/*** 商品id*/private Long skuId;/*** 商品的秒杀随机码*/private String randomCode;/*** 秒杀价格*/private BigDecimal seckillPrice;/*** 秒杀总量*/private BigDecimal seckillCount;/*** 每人限购数量*/private BigDecimal seckillLimit;/*** 排序*/private Integer seckillSort;/***  sku的详细信息*/private SkuInfoVo skuInfoVo;/***  商品秒杀的开始时间*  Long类型进行比较时很快,所以使用Long类型*/private Long startTime;/***  商品秒杀的结束时间*/private Long endTime;
}

缓存活动的关联商品信息

(1)seckill:skus:Hash结构,设置SeckillSkuRedisTo的内容,绑定hash操作,key:sessionid-skuId

(2)设置库存为分布式信号量semaphore,

/*** 活动redis* seckill:sessions:开始时间_结束时间* skuIds[]* ====================* 商品redis* Map【seckill:skus:】* <session-skuId,skuId的图片、随机码等详细信息>* ========================* 信号量* <seckill:stock:随机码,* 信号量>* */
private void saveSessionSkuInfo(List<SeckillSessionsWithSkus> sessions){if(sessions != null){// 遍历sessionsessions.stream().forEach(session -> {// sku信息redis,准备往里放内容BoundHashOperations<String, Object, Object> ops =stringRedisTemplate.boundHashOps(SKUKILL_CACHE_PREFIX); // "seckill:skus:";// 遍历sku往上面redis里放内容session.getRelationSkus().stream().forEach(seckillSkuVo -> {// 1.商品的随机码String randomCode = UUID.randomUUID().toString().replace("-", "");// 缓存中没有再添加 // key:sessionid-skuIdif(!ops.hasKey(seckillSkuVo.getPromotionSessionId() + "-" + seckillSkuVo.getSkuId())){// 2.缓存商品SeckillSkuRedisTo redisTo = new SeckillSkuRedisTo();BeanUtils.copyProperties(seckillSkuVo, redisTo);// 3.sku的基本数据 sku的秒杀信息R info = productFeignService.skuInfo(seckillSkuVo.getSkuId());if(info.getCode() == 0){SkuInfoVo skuInfo = info.getData("skuInfo", new TypeReference<SkuInfoVo>() {});redisTo.setSkuInfoVo(skuInfo);}// 4.设置当前商品的秒杀信息// 设置时间redisTo.setStartTime(session.getStartTime().getTime());redisTo.setEndTime(session.getEndTime().getTime());// 设置随机码redisTo.setRandomCode(randomCode);// sku信息放到第二个redis里 // key:活动id-skuIDops.put(seckillSkuVo.getPromotionSessionId() + "-" + seckillSkuVo.getSkuId(),JSON.toJSONString(redisTo));// 5.使用库存作为分布式信号量  限流,得到信号量才去改dbRSemaphore semaphore =redissonClient.getSemaphore(SKUSTOCK_SEMAPHONE + randomCode);//"seckill:stock:";// 在信号量中设置秒杀数量semaphore.trySetPermits(seckillSkuVo.getSeckillCount().intValue());}});});}
}

秒杀上架的幂等性问题

(1)使用Redis的分布式锁,锁的名称为seckill:upload:lock

(2)在代码内部再判断一次,如果key不存在才会进行上架,缓存中没有才添加

private final String upload_lock = "seckill:upload:lock";
/*** 这里应该是幂等的*  三秒执行一次:* /3 * * * * ?*  8小时执行一次:0 0 0-8 * * ?*/
@Scheduled(cron = "0 0 0-8 * * ?")
public void uploadSeckillSkuLatest3Day(){log.info("\n上架秒杀商品的信息");// 1.重复上架无需处理 加上分布式锁 状态已经更新 释放锁以后其他人才获取到最新状态RLock lock = redissonClient.getLock(upload_lock);// "seckill:upload:lock";lock.lock(10, TimeUnit.SECONDS);//锁的释放时间try {seckillService.uploadSeckillSkuLatest3Day();} finally {lock.unlock();}
}

文章完!!!

谷粒商城:秒杀商品定时上架相关推荐

  1. 【谷粒商城 -秒杀服务】

    谷粒商城–秒杀服务–高级篇笔记十二 1.后台添加秒杀商品 未配置秒杀服务相关网关 1.1 配置网关 - id: coupon_routeuri: lb://gulimall-couponpredica ...

  2. 谷粒商城--秒杀服务--高级篇笔记十二

    谷粒商城–秒杀服务–高级篇笔记十二 1.后台添加秒杀商品 未配置秒杀服务相关网关 1.1 配置网关 - id: coupon_routeuri: lb://gulimall-couponpredica ...

  3. 【谷粒商城】ElasticSearch、上架与检索

    笔记-基础篇-1(P1-P28):https://blog.csdn.net/hancoder/article/details/106922139 笔记-基础篇-2(P28-P100):https:/ ...

  4. 尚硅谷2020微服务分布式电商项目《谷粒商城》-商品搜索

    关注公众号:java星星 获取全套课件资料 1. 导入商品数据 1.1. 搭建搜索工程 pom.xml内容如下: <?xml version="1.0" encoding=& ...

  5. 记录谷粒商城P90 商品发布关于[500] during [POST](null空指针)的坑

    代码小白,记录自学制作谷粒商城遇到的坑 如有错误请轻喷 1.问题的出现:关于[500] during [POST]以及null空指针 在做到发布商品的时候突然报了一个post500的错误,这比较诡异啊 ...

  6. 谷粒商城六商品服务三级分类

    递归-树形结构数据获取 sql文件 sql文件太大了,这个博主写的非常厉害,看他的就ok了 CategoryController package com.atguigu.gulistore.produ ...

  7. 谷粒商城之商品服务-平台属性-属性组管理

    目录 什么是SPU? 什么是SKU? 规格参数 ​ 销售属性 三级分类-属性组-属性的关联关系 SPU-属性&SKU-属性的关联关系 预期效果: 属性分组之前端组件抽取&父子组件交互 ...

  8. 谷粒商城(新增商品、商品管理、仓库管理)思路详解

    谷粒商城基础分布式总结 1.新增商品 1.调试member服务 2.查询分类下的所有品牌 2.获取分类下面带属性的属性分组. 3.保存设定好的spu和sku信息 2.商品管理 1.spu展示查询(sp ...

  9. 谷粒商城七商品服务品牌管理之oss文件存储

    使用renren-generator生成crud页面 todo谷粒商城二本地虚拟机环境搭建及项目初始化在逆向工程的时候,resources下有一个view文件夹,下面都是可以直接使用的vue文件,我们 ...

最新文章

  1. php左右值实现无限极分类,基于ThinkPHP的二叉树左右值无限极分类实现
  2. 酷冷至尊官方psu计算工具_静静的挺你10年:酷冷至尊V650 GOLD全模组电源体验
  3. Kotlin学习 PART 1:kotlin定义和目的
  4. CSS学习04之层次选择器
  5. nrf51822-提高nordic ble数据发送速率
  6. response生成图片验证码
  7. mysql安装被打断_MySQL安装未响应解决方法
  8. layui添加复选框_对layui初始化列表的CheckBox属性详解,初始化属性
  9. java调用ole ie_SWT中通过Automatioin的方式访问IE(升级版)
  10. c++ std::async的注意事项
  11. python之Character string
  12. 华为手机如何换成鸿蒙,如何将自己的华为手机升级成鸿蒙系统
  13. 微信平台商户开通证书相关问题盘点
  14. 如何入门多视角人脸正面化生成?不得不看的超详细最新综述!
  15. HDU6069(数学)
  16. 命运多舛。德体:多特蒙德队长罗伊斯因伤无缘卡塔尔世界杯
  17. 学习笔记 | 高效能团队打造
  18. 一个精壮的代购骗子被我用Python彻底征服了
  19. linux 云计算 python 零基础 开机流程
  20. uni-app设置标题名字

热门文章

  1. 有关<a>标签的target属性决定了是否展示新打开的网页
  2. php英语过几级,英语一共有几级?等级表是怎么划分的?
  3. mask-rcnn报错: IndexError: boolean index did not match indexed array along dimension 0;...
  4. 平面设计:创建精美的书虫图标
  5. 一种导致不明确引用的原因
  6. win32 API函数( InvalidateRect)
  7. 哈夫曼(Huffman)编码在word2vec中的应用
  8. Instagram注册用户超1亿 平均每月新增1000万
  9. 熊啸锋:什么是大数据网络营销,它的好处和原理以及实战运用
  10. 外汇三角对冲套利策略原理全解析