【业务功能篇105】 微服务-springcloud-springboot-电商订单模块--秒杀服务-定时任务【上篇】

秒杀服务

一、商品上架

秒杀活动的结构图

image.png

通过定时任务触发:

  • 定时任务由spring提供,需要通过注解开启,这里通过定义一个配置类,注入spring,对其配置类进行相应的注解,当然也可以注解放在我们的服务启动类上
  • cron表达式定时示例

0 * * * * ? 每1分钟触发一次
0 0 * * * ? 每天每1小时触发一次
0 0 10 * * ? 每天10点触发一次
0 * 14 * * ? 在每天下午2点到下午2:59期间的每1分钟触发
0 30 9 1 * ? 每月1号上午9点半
0 15 10 15 * ? 每月15日上午10:15触发
*/5 * * * * ? 每隔5秒执行一次
0 */1 * * * ? 每隔1分钟执行一次
0 0 5-15 * * ? 每天5-15点整点触发
0 0/3 * * * ? 每三分钟触发一次
0 0 0 1 * ? 每月1号凌晨执行一次

对照上面的字段含义写自定义的cron时间表达式基本就ok了,写完后可以到 http://cron.qqe2.com/ 验证下。

注意:"0 0/50 * * * ?"这个表达式很多人会认为是每隔50分钟执行,实际不是,会每个小时的50分、60分钟跑一次,例如1:50,2:00,2:50,3:00……

//开启异步,还需要在具体的任务方法加@Async表示需要异步 ,定时任务默认是同步的,主要是为了定时可以按时跑起来,比如说我们任务是每10秒执行一次,但是任务逻辑跑完不止10秒,这样就需要等任务执行完再接着跑,这样频率就没有达到预期效果,所以我们开启异步,多线程去每隔10秒运行任务,即使任务10秒内没跑完也会开启其他线程来跑,而这里的线程池的数量,是系统默认给的,这里可以通过配置参数在yaml文件自定义
//spring.task.execution.pool.core-size=5  .max-size=20 进行设置
@EnableAsync//开启定时功能  还需要在具体扥任务方法加@Scheduled表示定时频率
@EnableScheduling
@Configuration
public class ScheduleConfig {
}
/*** 定时上架秒杀商品信息*/
@Slf4j
@Component
public class SeckillSkuSchedule {@AutowiredSeckillService seckillService;@AutowiredRedissonClient redissonClient;/****/@Async//每5s执行一次@Scheduled(cron = "*/5 * * * * *")public void uploadSeckillSku3Days(){log.info("定时上架秒杀商品执行了...." + new Date());// 分布式锁 避免多节点多集群场景下,会重复执行,先提供一个分布式锁,使各个节点通过这种获取锁方式来执行,当然拿到锁还是会去重复执行的,所以接着再业务方法里头还要通过逻辑判断去过滤处理,比如已经存储在redis的key 那么就不再执行,不存在的说明还没执行就进行插入 if(!key)RLock lock = redissonClient.getLock("seckill:upload:lock");lock.lock(10, TimeUnit.SECONDS);try {// 调用上架商品的方法seckillService.uploadSeckillSku3Days();}catch (Exception e){lock.unlock();}}}

进入到Service中处理

@Overridepublic void uploadSeckillSku3Days() {// 1. 通过OpenFegin 远程调用Coupon服务中接口来获取未来三天的秒杀活动的商品R r = couponFeignService.getLates3DaysSession();if(r.getCode() == 0){// 表示查询操作成功String json = (String) r.get("data");List<SeckillSessionEntity> seckillSessionEntities = JSON.parseArray(json,SeckillSessionEntity.class);// 2. 上架商品  Redis数据保存// 缓存商品//  2.1 缓存每日秒杀的SKU基本信息saveSessionInfos(seckillSessionEntities);// 2.2  缓存每日秒杀的商品信息saveSessionSkuInfos(seckillSessionEntities);}}/*** 保存每日活动的信息到Redis中* @param seckillSessionEntities*/private void saveSessionInfos(List<SeckillSessionEntity> seckillSessionEntities) {for (SeckillSessionEntity seckillSessionEntity : seckillSessionEntities) {// 循环缓存每一个活动  key: start_endTimelong start = seckillSessionEntity.getStartTime().getTime();long end = seckillSessionEntity.getEndTime().getTime();// 生成KeyString key = SeckillConstant.SESSION_CHACE_PREFIX+start+"_"+end;Boolean flag = redisTemplate.hasKey(key);if(!flag){// 表示这个秒杀活动在Redis中不存在,也就是还没有上架,那么需要保存// 需要存储到Redis中的这个秒杀活动涉及到的相关的商品信息的SKUIDList<String> collect = seckillSessionEntity.getRelationEntities().stream().map(item -> {// 秒杀活动存储的 VALUE是 sessionId_SkuIdreturn item.getPromotionSessionId()+"_"+item.getSkuId().toString();}).collect(Collectors.toList());redisTemplate.opsForList().leftPushAll(key,collect);}}}/*** 存储活动对应的 SKU信息* @param seckillSessionEntities*/private void saveSessionSkuInfos(List<SeckillSessionEntity> seckillSessionEntities) {seckillSessionEntities.stream().forEach(session -> {// 循环取出每个Session,然后取出对应SkuID 封装相关的信息BoundHashOperations<String, Object, Object> hashOps = redisTemplate.boundHashOps(SeckillConstant.SKU_CHACE_PREFIX);session.getRelationEntities().stream().forEach(item->{String skuKey = item.getPromotionSessionId()+"_"+item.getSkuId();Boolean flag = redisTemplate.hasKey(skuKey);if(!flag){SeckillSkuRedisDto dto = new SeckillSkuRedisDto();// 1.获取SKU的基本信息R info = productFeignService.info(item.getSkuId());if(info.getCode() == 0){// 表示查询成功String json = (String) info.get("skuInfoJSON");dto.setSkuInfoVo(JSON.parseObject(json,SkuInfoVo.class));}// 2.获取SKU的秒杀信息/*dto.setSkuId(item.getSkuId());dto.setSeckillPrice(item.getSeckillPrice());dto.setSeckillCount(item.getSeckillCount());dto.setSeckillLimit(item.getSeckillLimit());dto.setSeckillSort(item.getSeckillSort());*/BeanUtils.copyProperties(item,dto);// 3.设置当前商品的秒杀时间dto.setStartTime(session.getStartTime().getTime());dto.setEndTime(session.getEndTime().getTime());// 4. 随机码String token = UUID.randomUUID().toString().replace("-","");dto.setRandCode(token);// 分布式信号量的处理  限流的目的RSemaphore semaphore = redissonClient.getSemaphore(SeckillConstant.SKU_STOCK_SEMAPHORE + token);// 把秒杀活动的商品数量作为分布式信号量的信号量semaphore.trySetPermits(item.getSeckillCount().intValue());hashOps.put(skuKey,JSON.toJSONString(dto));}});});}

通过OpenFegin 远程调用Coupon服务中接口来获取未来三天的秒杀活动的商品
在秒杀服务中,创建fegin调用接口,去调用Coupon的接口服务

package com.msb.mall.feign;
@FeignClient("mall-coupon")
public interface CouponFeignService {@GetMapping("/coupon/seckillsession/getLates3DaysSession")public R getLates3DaysSession();
}

Coupon服务也就是会员服务中创建接口,获取会员可以调用的 秒杀活动商品信息
controller层


@RestController
@RequestMapping("coupon/seckillsession")
public class SeckillSessionController {@Autowiredprivate SeckillSessionService seckillSessionService;@GetMapping("/getLates3DaysSession")public R getLates3DaysSession(){List<SeckillSessionEntity> lates3DaysSession = seckillSessionService.getLates3DaysSession();String json = JSON.toJSONString(lates3DaysSession);return R.ok().put("data",json);}
}

service层

@Service("seckillSessionService")
public class SeckillSessionServiceImpl extends ServiceImpl<SeckillSessionDao, SeckillSessionEntity> implements SeckillSessionService {@AutowiredSeckillSkuRelationService relationService;@Overridepublic List<SeckillSessionEntity> getLates3DaysSession() {// 计算未来3天的时间List<SeckillSessionEntity> list =  this.list(new QueryWrapper<SeckillSessionEntity>().between("start_time",startTime(),endTime()));List<SeckillSessionEntity> newList = list.stream().map(session -> {// 根据对应的sessionId活动编号查询出对应的活动商品信息List<SeckillSkuRelationEntity> relationEntities = relationService.list(new QueryWrapper<SeckillSkuRelationEntity>().eq("promotion_session_id", session.getId()));session.setRelationEntities(relationEntities);return session;}).collect(Collectors.toList());return newList;}private String startTime(){LocalDate now = LocalDate.now();LocalDate startDay = now.plusDays(0);LocalTime min = LocalTime.MIN;LocalDateTime start = LocalDateTime.of(startDay, min);return start.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));}private String endTime(){LocalDate now = LocalDate.now();LocalDate endDay = now.plusDays(2);LocalTime max = LocalTime.MAX;LocalDateTime end = LocalDateTime.of(endDay, max);return end.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));}
}

启动服务,数据会被保存到Redis中

image.png

image.png

二、秒杀商品查询

  通过当前时间获取对应的秒杀活动及对应的SKU信息。

   /*** 查询出当前时间内的秒杀活动及对应的商品SKU信息* @return*/@Overridepublic List<SeckillSkuRedisDto> getCurrentSeckillSkus() {// 1.确定当前时间是属于哪个秒杀活动的long time = new Date().getTime();// 从Redis中查询所有的秒杀活动Set<String> keys = redisTemplate.keys(SeckillConstant.SESSION_CHACE_PREFIX + "*");for (String key : keys) {//seckill:sessions1656468000000_1656469800000String replace = key.replace(SeckillConstant.SESSION_CHACE_PREFIX, "");// 1656468000000_1656469800000String[] s = replace.split("_");Long start = Long.parseLong(s[0]); // 活动开始的时间Long end = Long.parseLong(s[1]); // 活动结束的时间if(time > start && time < end){// 说明的秒杀活动就是当前时间需要参与的活动// 取出来的是SKU的ID  2_9List<String> range = redisTemplate.opsForList().range(key, -100, 100);BoundHashOperations<String, String, String> ops = redisTemplate.boundHashOps(SeckillConstant.SKU_CHACE_PREFIX);List<String> list = ops.multiGet(range);if(list != null && list.size() > 0){List<SeckillSkuRedisDto> collect = list.stream().map(item -> {SeckillSkuRedisDto seckillSkuRedisDto = JSON.parseObject(item, SeckillSkuRedisDto.class);return seckillSkuRedisDto;}).collect(Collectors.toList());return collect;}}}return null;}

然后定义相关的Controller接口就可以访问了

@RestController
@RequestMapping("/seckill")
public class SeckillController {@AutowiredSeckillService seckillService;@GetMapping("/currentSeckillSessionSkus")public R getCurrentSeckillSessionSkus(){List<SeckillSkuRedisDto> currentSeckillSkus = seckillService.getCurrentSeckillSkus();return R.ok().put("data", JSON.toJSONString(currentSeckillSkus));}
}

然后对应的访问效果:

image.png

三、页面渲染

1.网关配置

首先在host中配置域名

image.png

然后在网关中配置路由信息

image.png

然后重启服务访问:

image.png

能访问到数据就表示域名配置成功

2.首页配置

  通过Ajax来访问获取秒杀的相关信息

  $.get("http://seckill.msb.com/seckill/currentSeckillSessionSkus",function(resp){if(resp.data.length > 0){// 说明有秒杀的数据console.log($.parseJSON(resp.data))$.parseJSON(resp.data).forEach(function(item){$("<li></li>").append("<img width='130px' height='130px' src='"+item.skuInfoVo.skuDefaultImg+"'/>").append("<p>"+item.skuInfoVo.skuSubtitle+"</p>").append("<span>"+item.seckillPrice+"</span>").append("<s>"+item.skuInfoVo.price+"</s>").appendTo("#seckillSessionContent");})/*<li><img src="/static/index/img/section_second_list_img1.jpg" alt=""><p>花王 (Merries) 妙而舒 纸尿裤 大号 L54片 尿不湿(9-14千克) (日本官方直采) 花王 (Merries) 妙而舒 纸尿裤 大号 L54片 尿不湿(9-14千</p><span>¥83.9</span><s>¥99.9</s></li>*/}})

展示的效果

image.png

3.商品详情

  在购买商品的时候,进入到商品详情页,如果该商品也参与了秒杀活动,那么对应的需要展示相关的信息

image.png

首先我们需要在秒杀服务中提供一个根据SKUID查询相关的秒杀活动的接口

   /*** 根据SKUID查询秒杀活动对应的信息* @param skuId* @return*/@Overridepublic SeckillSkuRedisDto getSeckillSessionBySkuId(Long skuId) {// 1.找到所有需要参与秒杀的商品的sku信息BoundHashOperations<String, String, String> ops = redisTemplate.boundHashOps(SeckillConstant.SKU_CHACE_PREFIX);Set<String> keys = ops.keys();if(keys != null && keys.size() > 0){String regx = "\\d_"+ skuId;for (String key : keys) {boolean matches = Pattern.matches(regx, key);if(matches){// 说明找到了对应的SKU的信息String json = ops.get(key);SeckillSkuRedisDto dto = JSON.parseObject(json, SeckillSkuRedisDto.class);return dto;}}}return null;}

然后在查询商品详情的时候异步查询出对应的秒杀活动信息

image.png

然后在模板页面中展示相关的信息

<div class="box-summary clear"><ul><li>京东价</li><li><span></span><span th:text="${#numbers.formatDecimal(item.info.price,3,2)}">4499.00</span></li><li style="color: red"><span th:if="${#dates.createNow().getTime() < item.seckillVO.startTime}">商品将在:[[${#dates.format(new java.util.Date(item.seckillVO.startTime),'yyyy-MM-dd HH:mm:ss')}]] 开始秒杀</span><span th:if="${#dates.createNow().getTime() > item.seckillVO.startTime&& #dates.createNow().getTime() < item.seckillVO.endTime }">秒杀价: [[${#numbers.formatDecimal(item.seckillVO.seckillPrice,1,2)}]]</span></li><li><a href="/static/item/">预约说明</a></li></ul></div>

首页调整到商品详情页

function goItem(skuId){location.href="http://item.msb.com/"+skuId+".html"}$.get("http://seckill.msb.com/seckill/currentSeckillSessionSkus",function(resp){if(resp.data.length > 0){// 说明有秒杀的数据console.log($.parseJSON(resp.data))$.parseJSON(resp.data).forEach(function(item){$("<li οnclick='goItem("+item.skuId+")'></li>").append("<img width='130px' height='130px' src='"+item.skuInfoVo.skuDefaultImg+"'/>").append("<p>"+item.skuInfoVo.skuSubtitle+"</p>").append("<span>"+item.seckillPrice+"</span>").append("<s>"+item.skuInfoVo.price+"</s>").appendTo("#seckillSessionContent");})/*<li><img src="/static/index/img/section_second_list_img1.jpg" alt=""><p>花王 (Merries) 妙而舒 纸尿裤 大号 L54片 尿不湿(9-14千克) (日本官方直采) 花王 (Merries) 妙而舒 纸尿裤 大号 L54片 尿不湿(9-14千</p><span>¥83.9</span><s>¥99.9</s></li>*/}})

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/75605.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Windows wsl2安装Ubuntu

wsl&#xff08;Windows Subsystem for Linux&#xff09;即适用于Windows的Linux子系统&#xff0c;是一个实现在Windows 10 / 11上运行原生Linux的技术。 wsl2 为其迭代版本&#xff0c;可以更好的在Windows上运行Linux子系统。 这里以 Windows 11 安装Ubuntu作为示例。 开启…

提升效率:PostgreSQL准确且快速的数据对比方法

作为一款强大而广受欢迎的开源关系型数据库管理系统&#xff0c;PostgreSQL 在数据库领域拥有显著的市场份额。其出色的可扩展性、稳定性使其成为众多企业和项目的首选数据库。而在很多场景下&#xff08;开发|生产环境同步、备份恢复验证、数据迁移、数据合并等&#xff09;&a…

“JSR303和拦截器在Java Web开发中的应用与实践“

目录 引言JSR303什么是JSR303?为什么要使用JSR303?常用注解快速入门JSR303 拦截器什么是拦截器拦截器与过滤器应用场景快速入门拦截器 总结 引言 在Java Web开发过程中&#xff0c;我们经常会遇到需要对输入数据进行验证和处理&#xff0c;同时需要对请求进行拦截与控制的需…

PyTorch深度学习实践1——线性回归和Logistic回归

PyTorch的风格 准备数据集使用类设计模型计算损失函数和优化器训练【前向、反向和更新】 线性回归 import torch# 准备数据集 # x,y是矩阵&#xff0c;3行1列 也就是说总共有3个数据&#xff0c;每个数据只有1个特征 x_data torch.tensor([[1.0], [2.0], [3.0]]) y_data to…

Modelsim仿真问题解疑二:ERROR: [USF-ModelSim-70]

现象&#xff1a;在Vivado中已配置modelsim为仿真工具后&#xff0c;运行仿真&#xff0c;报错USF-ModelSim-70和ERROR: [Vivado 12-4473] 详细报错内容如下 ERROR: [USF-ModelSim-70] compile step failed with error(s) while executing C:/Users/ZYP_PC/Desktop/verilog_t…

python趣味编程-恐龙克隆游戏

Python 中使用 Turtle 的恐龙克隆游戏免费源代码 使用 Turtle 的恐龙克隆游戏是一个用Python编程语言编码的桌面游戏应用程序。该项目包含在 Chrome 浏览器中克隆实际恐龙游戏的多种功能。该项目可以使正在修读 IT 相关课程的学生受益。这个应用程序非常有趣,可以帮助您学习创…

显示器配置信息删除

显示器配置信息删除 1 介绍2 操作参考 1 介绍 笔记本屏幕坏了&#xff0c;手头的拓展显示器都是配置成拓展显示&#xff0c;需要先找一台没配置过的显示器将系统中显示器配置信息删除&#xff0c;这样就能复制屏幕显示到拓展屏幕上了。 2 操作 Windows 的显示器配置位于注册…

算法AB实验平台进化历程和挑战

1 AB 平台简介 AB 实验平台这几年在互联网公司得到了越来越广泛的应用&#xff0c;采用 AB 实验来评估产品和技术迭代效果也成为主流的业务新功能效果评估方式&#xff0c;数据驱动的文化在这几年得到了不少公司的广泛的认同&#xff0c;通过数据和指标来说明产品效果也得到了…

大数据组件-Flink环境搭建

&#x1f947;&#x1f947;【大数据学习记录篇】-持续更新中~&#x1f947;&#x1f947; 个人主页&#xff1a;beixi 本文章收录于专栏&#xff08;点击传送&#xff09;&#xff1a;【大数据学习】 &#x1f493;&#x1f493;持续更新中&#xff0c;感谢各位前辈朋友们支持…

Nacos使用和注册部分源码介绍

Nacos简单介绍 Nacos致力于帮助您发现、配置和管理微服务。Nacos提供了一组简单易用的特性集&#xff0c;帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。Nacos帮助您更敏捷和容易地构建、交付和管理微服务平台。Nacos是构建以“服务”为中心的现代应用架构 (例…

NLP信息抽取全解析:从命名实体到事件的PyTorch实战指南

目录 引言背景和信息抽取的重要性文章的目标和结构 信息抽取概述什么是信息抽取信息抽取的应用场景信息抽取的主要挑战 实体识别什么是实体识别实体识别的应用场景PyTorch实现代码输入、输出与处理过程 关系抽取什么是关系抽取关系抽取的应用场景PyTorch实现代码输入、输出与处…

超高真空度精密控制解决方案设计中百度“文心一言”的具体应用

摘要&#xff1a;本文采用国产版本ChatGPT百度“文心一言”作为一种辅助工具&#xff0c;针对超高真空度精密控制装置的开发进行了初期的技术路线设计&#xff0c;对话调研的重点是了解可调节式微流量进气阀门和可用于连接非线性输出信号型真空计的PID控制器。总体而言&#xf…

Google Hacking搜索

Google Hacking概述 GoogleHacking基本用法逻辑或OR逻辑与And逻辑非NOT完整匹配 GoogleHacking高级用法sitefiletypeinurlintitleintext Google Hacking DataBaseGoogle Hacking概述 GoogleHacking&#xff1a;利用搜索引擎有争对性的搜索信息来对网络入侵的技术和行为。搜索引…

异步请求库的实际应用案例:爬取豆瓣经典电影

在日常爬虫过程中&#xff0c;你有没有遇到过需要爬取大量数据的情况&#xff0c;但是传统的同步请求方式让您等得焦头烂额&#xff1f; 这个问题的根源在于传统的同步请求方式。当我们使用同步请求时&#xff0c;程序会一直等待服务器的响应&#xff0c;直到数据返回后才能继续…

【juc】ReentrantReadWriteLock之缓存(仅当学习)

目录 一、说明二、代码示例2.1 pom依赖2.2 示例代码2.3 实体类 三、示例截图 一、说明 1.针对于读多写少的情况 2.先查缓存&#xff0c;没有再去查库 二、代码示例 2.1 pom依赖 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"h…

期权权利金计算方法大揭秘!详解期权权利金的计算公式

期权的权利金就是指期权合约市场当时的价格&#xff0c;权利金是会浮动的&#xff0c;跟股票的价格一样&#xff0c;随着市场的波动而波动。权利金是期权费&#xff0c;不是保证金。那么期权权利金计算方法大揭秘&#xff01;详解期权权利金的计算公式。 一、什么是期权的权利金…

031:vue子组件向父组件传递多个参数,父组件2种解析方法

第031个 查看专栏目录: VUE ------ element UI 专栏目标 在vue和element UI联合技术栈的操控下&#xff0c;本专栏提供行之有效的源代码示例和信息点介绍&#xff0c;做到灵活运用。 &#xff08;1&#xff09;提供vue2的一些基本操作&#xff1a;安装、引用&#xff0c;模板使…

C++初阶--类和对象(中)

目录 类的6个默认成员函数构造函数使用方法 析构函数使用方法 拷贝构造函数使用方法 赋值运算符重载赋值运算符重载 const成员 上篇末尾我们讲到了关于c实现栈相较于c语言在传递参数时的一些优化&#xff0c;但实际上&#xff0c;c在 初始化 清理 赋值 拷贝等方面也做了很大程…

Java虚拟机(JVM)夺命20连问

博主介绍&#xff1a;✌全网粉丝3W&#xff0c;全栈开发工程师&#xff0c;从事多年软件开发&#xff0c;在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建与毕业项目实战&#xff0c;博主也曾写过优秀论文&#xff0c;查重率极低&#xff0c;在这方面有丰富的经验…

1.创建项目(wpf视觉项目)

目录 前言本章环境创建项目启动项目可执行文件 前言 本项目主要开发为视觉应用&#xff0c;项目包含&#xff08;视觉编程halcon的应用&#xff0c;会引入handycontrol组件库&#xff0c;工具库Masuit.Tools.Net&#xff0c;数据库工具sqlSugar等应用&#xff09; 后续如果还有…