微服务项目【秒杀商品展示及商品秒杀】
登录方式调整
第1步:从zmall-common的pom.xml中移除spring-session-data-redis依赖
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EGgFVLjU-1676371517002)(images\20220817224735.jpg)]](https://img-blog.csdnimg.cn/b39f7918964b4c6787d7bca78db27970.jpeg)
注意:
1)本次不采用spring-session方式,改用redis直接存储用户登录信息,主要是为了方便之后的jmeter压测;
2)这里只注释调用spring-session的依赖,保留redis的依赖;
第2步:在zmall-common公共模块中定义RedisConfig配置类
@Configuration
public class RedisConfig {@Beanpublic RedisTemplate<String,Object> restTemplate(RedisConnectionFactory redisConnectionFactory){RedisTemplate<String,Object> redisTemplate=new RedisTemplate<>();//String类型Key序列化redisTemplate.setKeySerializer(new StringRedisSerializer());//String类型Value序列化redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());//Hash类型Key序列化redisTemplate.setHashKeySerializer(new StringRedisSerializer());//Hash类型Value序列化redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());redisTemplate.setConnectionFactory(redisConnectionFactory);return redisTemplate;}
}
第3步:在zmall-common公共模块中配置redis相关服务
IRedisServcie
public interface IRedisService {/*** 将登陆用户对象保存到Redis中,并以token来命名* @param token* @param user*/void setUserToRedis(String token, User user);/*** 根据token令牌从Redis中获取User对象* @param token* @return*/User getUserByToken(String token);
}
RedisServcieImple
@Service
public class RedisServiceImpl implements IRedisService {@Autowiredprivate RedisTemplate<String,Object> redisTemplate;@Overridepublic void setUserToRedis(String token, User user) {String key="user:"+token;redisTemplate.boundValueOps(key).set(user,7200,TimeUnit.SECONDS);}@Overridepublic User getUserByToken(String token) {return (User) redisTemplate.opsForValue().get("user:"+token);}
}
用户登录成功后,将用户对象保存到Redis中,并设置超时时间7200秒。
第4步:配置自定义参数解析UserArgumentResolver、WebConfig
UserArgumentResolver
/*** 自定义用户参数类*/
@Component
public class UserArgumentResolver implements HandlerMethodArgumentResolver {@Autowiredprivate IRedisService redisService;/*** 只有supportsParameter方法执行返回true,才能执行下面的resolveArgument方法* @param methodParameter* @return*/@Overridepublic boolean supportsParameter(MethodParameter methodParameter) {Class<?> type = methodParameter.getParameterType();return type== User.class;}@Overridepublic Object resolveArgument(MethodParameter methodParameter,ModelAndViewContainer modelAndViewContainer,NativeWebRequest nativeWebRequest,WebDataBinderFactory webDataBinderFactory) throws Exception {HttpServletRequest req= (HttpServletRequest) nativeWebRequest.getNativeRequest();//从cookie获取token令牌String token = CookieUtils.getCookieValue(req, "token");//判断cookie中的token令牌是否为空if(StringUtils.isEmpty(token))throw new BusinessException(JsonResponseStatus.TOKEN_ERROR);//根据token令牌获取redis中存储的user对象,方便jmeter测试User user = redisService.getUserByToken(token);if(null==user)throw new BusinessException(JsonResponseStatus.TOKEN_ERROR);return user;}
}
WebConfig
@Component
public class WebConfig implements WebMvcConfigurer {@Autowiredprivate UserArgumentResolver userArgumentResolver;@Overridepublic void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {resolvers.add(userArgumentResolver);}@Overridepublic void addResourceHandlers(ResourceHandlerRegistry registry) {//添加静态资源访问映射//registry.addResourceHandler("/static/**")// .addResourceLocations("classpath:/static/");}
}
第5步:用户登录业务调整,将spring-session方式更改为redis方式存储登录用户信息。
//5.通过UUID生成token令牌并保存到cookie中
String token= UUID.randomUUID().toString().replace("-","");
//将随机生成的Token令牌保存到Cookie中,并设置1800秒超时时间
CookieUtils.setCookie(req,resp,"token",token,7200);
//6.将token令牌与spring session进行绑定并存入redis中
//HttpSession session = req.getSession();
//session.setAttribute(token,us);
//将token令牌与user绑定后存储到redis中,方便jmeter测试
redisService.setUserToRedis(token,us);
这里采用Redis方式直接存储登录用户信息,只为后续使用Jmeter压测时提供便利。正常运行使用项目还是可以使用spring-session方式。
第6步:修改商品服务zmall-product模块中的index方法,将之前从HttpSession中获取登录用户信息改换成User对象参数方式
@RequestMapping("/index.html")
public String index(Model model, User user){System.out.println(user);
}
在调用index方法之前,先由自定义的参数解析器进行参数解析并返回解析结果User,所以在这里可直接在方法参数中获取的User对象。
第7步:重启zmall-user和zmall-product模块,完成用户登录后,直接在浏览器地址栏输入:http://zmall.com/product-serv/index.html,查看zmall-product模块中的控制台是否已经获取到登录用户对象信息。
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nf3XjJz0-1676371517006)(images\20220817233131.jpg)]](https://img-blog.csdnimg.cn/a8c7b9183f8741eb9dda7429ecedd4fb.jpeg)
生成秒杀订单
绑定秒杀商品
添加sellDetail.html页面到zmall-product模块中;实现首页秒杀商品展示,必须保证秒杀商品状态为已激活、且秒杀商品的活动时间为有效时间范围之内。
<#if kills??><#list kills as g><div class="sell_${g_index?if_exists+1}"><div class="sb_img"><a href="${ctx}/sellDetail.html?pid=${g.id}"><img src="${g.fileName}" width="242" height="356" /></a></div><div class="s_price">¥<span>${g.price}</span></div><div class="s_name"><h2><a href="${ctx}/sellDetail.html?pid=${g.id}">${g.name}</a></h2>倒计时:<span>1200</span> 时 <span>30</span> 分 <span>28</span> 秒</div></div></#list></#if>
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A1zL4Qix-1676371517008)(images\2022-08-18_164403.png)]](https://img-blog.csdnimg.cn/97de68cab1bd4b97b4f66748705b49c8.png)
查看秒杀商品
点击限时秒杀中的秒杀商品,根据秒杀商品ID查询秒杀商品详情信息并跳转到sellDetail.html页面展示秒杀商品信息。
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NWNimZH4-1676371517017)(images\2022-08-18_164751.png)]](https://img-blog.csdnimg.cn/7fac527119d84216a4a7c8bc62510e03.png)
订单秒杀
移除seata相关
第1步:先注释掉zmall-order和zmall-product模块中的seata依赖
第2步:分别删掉zmall-order和zmall-product模块中resources目录下的bootstrap.xml和register.conf文件
第3步:移除zmall-order中分布式事务案例中的@GlobalTransactional注解
第4步:删除DataSourceProxyConfig该类
第5步:移除zmall-order中启动类上的注解参数(exclude = DataSourceAutoConfiguration.class)
//更改前:
//@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
//更改后:
@SpringBootApplication
@EnableDiscoveryClient
@MapperScan({"com.zking.zmall.mapper"})
@EnableFeignClients
public class ZmallOrderApplication {public static void main(String[] args) {SpringApplication.run(ZmallOrderApplication.class, args);}}
第6步:更换zmall-order和zmall-product中的application.yml配置中的数据库连接池
datasource:#type连接池类型 DBCP,C3P0,Hikari,Druid,默认为Hikari#更改前:#type: com.alibaba.druid.pool.DruidDataSource#更改后:type: com.zaxxer.hikari.HikariDataSource
生成秒杀订单
将SnowFlake雪花ID生成工具类导入到zmall-common模块中utils,然后再生成秒杀订单时使用雪花ID来充当秒杀订单编号;在zmall-order模块中完成秒杀订单生成工作。
IOrderService
public interface IOrderService extends IService<Order> {JsonResponseBody<?> createKillOrder(User user, Integer pid);
}
OrderServiceImpl
@Transactional
@Override
public JsonResponseBody<?> createKillOrder(User user, Integer pid) {//1.判断用户是否登录if(null==user)throw new BusinessException(JsonResponseStatus.TOKEN_ERROR);//根据用户ID和秒杀商品Id判断。。。。//2.根据秒杀商品编号获取秒杀商品库存是否为空Kill kill = killService.getOne(new QueryWrapper<Kill>().eq("item_id",pid));if(kill.getTotal()<1)throw new BusinessException(JsonResponseStatus.STOCK_EMPTY);//3.根据商品ID获取商品Product product = productService.getProductById(pid);//4.秒杀商品库存减一killService.updateKillStockById(pid);//5.生成秒杀订单及订单项SnowFlake snowFlake=new SnowFlake(2,3);Long orderId=snowFlake.nextId();//创建订单Order order=new Order();order.setUserId(user.getId());order.setLoginName(user.getLoginName());order.setCost(product.getPrice());order.setSerialNumber(orderId);this.save(order);//创建订单项OrderDetail orderDetail=new OrderDetail();orderDetail.setOrderId(orderId);orderDetail.setProductId(product.getId());orderDetail.setQuantity(1);orderDetail.setCost(product.getPrice());orderDetailService.save(orderDetail);return new JsonResponseBody();
}
前端页面秒杀测试
在sellDetail.html页面中添加订单秒杀JS方法。
<script>$(function(){$('.ch_a').click(function(){let pid=$(this).attr('alt');console.log(pid);$.post('http://zmall.com/order-serv/createKillOrder',{pid:pid},function(rs){console.log(rs);if(rs.code===200)alert('秒杀成功');elsealert(rs.msg);},'json');});});
</script>
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LM7Ibg8f-1676371517019)(images\2022-08-18_171847.png)]](https://img-blog.csdnimg.cn/fed67114ebc74a70936a5796e7f02862.png)
这里虽然已经能正常展示秒杀效果,但是还是存在很多问题,比如:重复抢购问题等等问题。
utils:https://pan.baidu.com/s/1ExaC4GgEg_ofKsARkYhHXw
提取码:kq20
相关文章:
微服务项目【秒杀商品展示及商品秒杀】
登录方式调整 第1步:从zmall-common的pom.xml中移除spring-session-data-redis依赖 注意: 1)本次不采用spring-session方式,改用redis直接存储用户登录信息,主要是为了方便之后的jmeter压测; 2)…...
DIDL3_模型选择、复杂度、过欠拟合的相关概念
模型选择、复杂度、过欠拟合的概念模型选择训练误差和泛化误差验证数据集和测试数据集K-则交叉验证(没有足够多数据时使用)过拟合和欠拟合模型容量模型容量的影响估计模型容量控制模型容量数据复杂度处理过拟合的方法(1)ÿ…...
Android 9.0 去除锁屏界面及SystemUI无sim卡拨打紧急电话控件显示功能实现
1.1概述 在9.0的系统rom定制化开发中,关于SystemUI的定制化功能也是比较多的,在SystemUI的锁屏页面和状态栏提示无sim卡拨打紧急电话控件显示等相关提示 的功能中,在有些systemui的定制中是不需要这些功能的,所以需要从systemui中去掉这些功能提示的,这就需要从systemui中…...
AntDB-M设计之内存结构
亚信科技专注通信行业多年,AntDB数据库从诞生开始,就面对通信级的大数据量应用场景挑战,在性能、稳定性、规模化等方面获得了超过10年的通信核心业务系统验证,性能峰值达到每秒百万的通信核心交易量。AntDB-M(AntDB内存…...
互联网舆情监测公司监测哪些内容,TOOM北京舆情监测公司
互联网舆情监测公司是一种提供舆情监测、分析和管理服务的公司,其业务主要涉及互联网舆情监测、数据分析、报告撰写、危机处理等方面。这些公司通过使用各种技术和工具,帮助客户监测他们在互联网上的声誉和品牌形象,并提供相应的建议和解决方…...
一篇文章带你熟练使用Ansible中的playbook
目录 一、Playbook的功能 二、YAML 1、简介 2、特点 3、语法简介 4、YAML 列表 5、YAML的字典 三、playbook执行命令 四、 Playbook的核心组件 五、vim 设定技巧 练习 一、Playbook的功能 playbook 是由一个或多个play组成的列表 Playboot 文件使用YAML来写的 二、…...
HashedWheelTimer
序言这种算法是一种轮询算法的优化升级,能够以只有一个Timer的情况下处理大量的定时任务.Begin结合HashedWheelTimer的思想根据自然时间1分钟为例,来做大批量的定时任务触发首先定一个长度为60的数组,数组中存放的是Set集合,集合里面是任务详情.当有定时任务刚来的时候判断是否…...
OPenCV库移植到ARM开发板子上面配置过程
步骤一 1,环境准备去下载opencv官方的源码。 我这里用的是opencv-4.5.5版本的 2,还需要交叉编译工具一般,你交叉编译的工具板子厂家会提供工具,最好还是用板子厂家提供的交叉编译工具,因为我之前编译试过其他的交叉…...
Jenkins实现CI/CD
Jenkins是一个开源的持续集成和持续交付(CI/CD)解决方案,它可以自动执行构建、测试和部署等任务,从而简化了开发工作流程。本文将详细介绍如何使用Jenkins实现CI/CD。 首先,您需要安装Jenkins并启动它。您可以通过以下…...
如何给img标签里的请求添加自定义header
是这样的需求,有一个web页面,里面图片的上传和预览来自于一个独立的文件服务器,对http的请求需要进行访问权限的设置,就是在请求的header里加一个Authorization的字段。上传好说我用的Axios直接添加一个header就行了,但…...
Linux系统基本概念操作,用户和文件权限管理
常用快捷键和通配符常用快捷键按键作用Ctrld键盘输入结束或退出终端Ctrls暂停当前程序,暂停后按下任意键恢复运行Ctrlz将当前程序放到后台运行,恢复到前台为命令fgCtrla将光标移至输入行头,相当于Home键Ctrle将光标移至输入行末,相…...
数据库中的单表查询和多表查询
一、单表查询素材: 表名:worker-- 表中字段均为中文,比如 部门号 工资 职工号 参加工作 等 CREATE TABLE worker (部门号 int(11) NOT NULL,职工号 int(11) NOT NULL,工作时间 date NOT NULL,工资 float(8,2) NOT NULL,政治面貌 varchar(10) …...
全网详解MyBatis-Plus LambdaQueryWrapper的使用说明以及LambdaQueryWrapper和QueryWapper的区别
文章目录1. 文章引言2. 代码演示3. 分析LambdaQueryWrapper3.1 引入LambdaQueryWrapper的原因3.2 LambdaQueryWrapper和QueryWapper的区别4. 重要总结1. 文章引言 今天在公司写代码时,发现同事使用LambdaQueryWrapper来查询数据,而我一直习惯使用QueryW…...
暴力破解(new)
数据来源 本文仅用于信息安全的学习,请遵守相关法律法规,严禁用于非法途径。若观众因此作出任何危害网络安全的行为,后果自负,与本人无关。 01 暴力破解介绍及应用场景 》暴力破解介绍 》暴力破解字典 GitHub - k8gege/Passwor…...
Android12之apex调试
1.问题在调试libtinyalsa.so中添加log后,但是发现push so后,却没有log打印,why?2.分析以下为libtinyalsa.so的位置/system/lib64/libtinyalsa.so /system/lib/libtinyalsa.so /apex/com.android.vndk.v31/lib64/libtinyalsa.so /a…...
Python - 数字(Number)数据类型常用操作
目录数字运算类型转换数学函数数学库math、cmathmath 模块常量math 模块方法随机函数库 randomrandom 模块方法保留小数到指定位数三角函数数字运算 :用于给变量赋值type(x):查看数据所属类型isinstance(x, A_tuple):判断数据是否为预期类型…...
QT(51)-动态链接库-windows
1.qt- 调用win32 DLL 2.qt- 调用MFC DLL 0概述: 01.扩展DLL: 必须有一个DllMain()函数,且调用AfxInitExtensionModule()函数。 CRuntimeClass类-初始化函数CDynLinkLibrary。02.windows定位DLL文件: 1)…...
[Vivado那些事儿]将自定义 IP (HDL)添加到 Vivado 模块设计(Block Design)
绪论使用Vivado Block Design设计解决了项目继承性问题,但是还有个问题,不知道大家有没有遇到,就是新设计的自定义 RTL 文件无法快速的添加到Block Design中,一种方式是通过自定义IP,但是一旦设计的文件有问题就需要重…...
开学必备数码清单,大学生开学必备数码好物分享
还有很多小伙伴不知道开学应该准备什么,在学校当中需要用到的数码产品,在宿舍娱乐使用的音箱、学习当中使用到的笔记本,这些都是我们可以准备的,还有一个小众的好物,能够让我们在学校当中提升生活的幸福感,…...
【面试题】常见前端基础面试题(HTML,CSS,JS)
大厂面试题分享 面试题库后端面试题库 (面试必备) 推荐:★★★★★地址:前端面试题库html语义化的理解代码结构: 使页面在没有css的情况下,也能够呈现出好的内容结构有利于SEO: 爬虫根据标签来分配关键字的权重,因此可以和搜索引擎…...
变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析
一、变量声明设计:let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性,这种设计体现了语言的核心哲学。以下是深度解析: 1.1 设计理念剖析 安全优先原则:默认不可变强制开发者明确声明意图 let x 5; …...
Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例
使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件,常用于在两个集合之间进行数据转移,如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model:绑定右侧列表的值&…...
渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止
<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet: https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...
电脑插入多块移动硬盘后经常出现卡顿和蓝屏
当电脑在插入多块移动硬盘后频繁出现卡顿和蓝屏问题时,可能涉及硬件资源冲突、驱动兼容性、供电不足或系统设置等多方面原因。以下是逐步排查和解决方案: 1. 检查电源供电问题 问题原因:多块移动硬盘同时运行可能导致USB接口供电不足&#x…...
定时器任务——若依源码分析
分析util包下面的工具类schedule utils: ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类,封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz,先构建任务的 JobD…...
第25节 Node.js 断言测试
Node.js的assert模块主要用于编写程序的单元测试时使用,通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试,通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...
RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程
本文较长,建议点赞收藏,以免遗失。更多AI大模型应用开发学习视频及资料,尽在聚客AI学院。 本文全面剖析RNN核心原理,深入讲解梯度消失/爆炸问题,并通过LSTM/GRU结构实现解决方案,提供时间序列预测和文本生成…...
基于Java Swing的电子通讯录设计与实现:附系统托盘功能代码详解
JAVASQL电子通讯录带系统托盘 一、系统概述 本电子通讯录系统采用Java Swing开发桌面应用,结合SQLite数据库实现联系人管理功能,并集成系统托盘功能提升用户体验。系统支持联系人的增删改查、分组管理、搜索过滤等功能,同时可以最小化到系统…...
JVM虚拟机:内存结构、垃圾回收、性能优化
1、JVM虚拟机的简介 Java 虚拟机(Java Virtual Machine 简称:JVM)是运行所有 Java 程序的抽象计算机,是 Java 语言的运行环境,实现了 Java 程序的跨平台特性。JVM 屏蔽了与具体操作系统平台相关的信息,使得 Java 程序只需生成在 JVM 上运行的目标代码(字节码),就可以…...
【Redis】笔记|第8节|大厂高并发缓存架构实战与优化
缓存架构 代码结构 代码详情 功能点: 多级缓存,先查本地缓存,再查Redis,最后才查数据库热点数据重建逻辑使用分布式锁,二次查询更新缓存采用读写锁提升性能采用Redis的发布订阅机制通知所有实例更新本地缓存适用读多…...
