java商品秒杀逻辑_如何通过SpringBoot实现商城秒杀系统
这篇文章主要介绍了如何通过SpringBoot实现商城秒杀系统,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
学习自:地址
1.主要流程
1.1数据库:
1.2 环境
window下:Zookeeper,Redis,rabbitmq-server。jdk1.8以上。
1.3 介绍
这里只做秒杀部分功能,其他功能不会涉及。项目运行后可访问秒杀商品页面
当用户没登陆,点击详情会跳转到登陆页面。
用户登陆后可以查看商品的详情并进行抢购。
注意,用户对于一件商品只能抢购一次,进行第二次抢购时会被拒绝。当用户抢购成功时会异步发送一封邮件给用户。
主要逻辑就是以上。接下来看代码
1.4 项目结构,api封装一些枚举和返回值,model主要是实体类和sql映射文件,service实现业务逻辑代码。
1.5 显示秒杀商品到页面以及用户的操作使用的还是MVC模式,不细讲。主要看如实现高并发下的秒杀。
要细述的话,东西太多,如果想深入了解,可点击上面的链接。
基本的秒杀逻辑如下,判断用户是否已经抢购过该商品,如果没有则查询待秒杀商品详情,判断该商品是否可以别秒杀,判断依据为库存是否足够
如果符合条件,则该商品库存减1,接着,再一次判断扣减是否成功,如果扣减成功则生成秒杀成功的订单,同时通知用户秒杀成功的信息。
public Boolean killItem(Integer killId, Integer userId) throws Exception {
Boolean result=false;
//TODO:判断当前用户是否已经抢购过当前商品
if (itemKillSuccessMapper.countByKillUserId(killId,userId) <= 0){
//TODO:查询待秒杀商品详情
ItemKill itemKill=itemKillMapper.selectById(killId);
//TODO:判断是否可以被秒杀canKill=1?
if (itemKill!=null && 1==itemKill.getCanKill() ){
//TODO:扣减库存-减一
int res=itemKillMapper.updateKillItem(killId);
//TODO:扣减是否成功?是-生成秒杀成功的订单,同时通知用户秒杀成功的消息
if (res>0){
commonRecordKillSuccessInfo(itemKill,userId);
result=true;
}
}
}else{
throw new Exception("您已经抢购过该商品了!");
}
return result;
}
代码优化1:使用redis的分布式锁,使用当前秒杀商品的id和当前用户的id组成一个key,使用StringBuffer拼接,使用雪花算法生成一个value,存进redis中。
@Autowired
private StringRedisTemplate stringRedisTemplate;
/**
* 商品秒杀核心业务逻辑的处理-redis的分布式锁
* @param killId
* @param userId
* @return
* @throws Exception
*/
@Override
public Boolean killItemV3(Integer killId, Integer userId) throws Exception {
Boolean result=false;
if (itemKillSuccessMapper.countByKillUserId(killId,userId) <= 0){
//TODO:借助Redis的原子操作实现分布式锁-对共享操作-资源进行控制
ValueOperations valueOperations=stringRedisTemplate.opsForValue();
final String key=new StringBuffer().append(killId).append(userId).append("-RedisLock").toString();
final String value=RandomUtil.generateOrderCode();
Boolean cacheRes=valueOperations.setIfAbsent(key,value); //luna脚本提供“分布式锁服务”,就可以写在一起
//TOOD:redis部署节点宕机了
if (cacheRes){
stringRedisTemplate.expire(key,30, TimeUnit.SECONDS);
try {
ItemKill itemKill=itemKillMapper.selectByIdV2(killId);
if (itemKill!=null && 1==itemKill.getCanKill() && itemKill.getTotal()>0){
int res=itemKillMapper.updateKillItemV2(killId);
if (res>0){
commonRecordKillSuccessInfo(itemKill,userId);
result=true;
}
}
}catch (Exception e){
throw new Exception("还没到抢购日期、已过了抢购时间或已被抢购完毕!");
}finally {
if (value.equals(valueOperations.get(key).toString())){
stringRedisTemplate.delete(key);
}
}
}
}else{
throw new Exception("Redis-您已经抢购过该商品了!");
}
return result;
}
代码优化2:将 Boolean cacheRes=lock.tryLock(30,10,TimeUnit.SECONDS); 每隔30秒判断当前用户是否超时写在了锁外面,不会因为一次卡顿而影响整个程序。
@Autowired
private RedissonClient redissonClient;
/**
* 商品秒杀核心业务逻辑的处理-redisson的分布式锁
* @param killId
* @param userId
* @return
* @throws Exception
*/
@Override
public Boolean killItemV4(Integer killId, Integer userId) throws Exception {
Boolean result=false;
final String lockKey=new StringBuffer().append(killId).append(userId).append("-RedissonLock").toString();
RLock lock=redissonClient.getLock(lockKey);
try {
Boolean cacheRes=lock.tryLock(30,10,TimeUnit.SECONDS);
if (cacheRes){
//TODO:核心业务逻辑的处理
if (itemKillSuccessMapper.countByKillUserId(killId,userId) <= 0){
ItemKill itemKill=itemKillMapper.selectByIdV2(killId);
if (itemKill!=null && 1==itemKill.getCanKill() && itemKill.getTotal()>0){
int res=itemKillMapper.updateKillItemV2(killId);
if (res>0){
commonRecordKillSuccessInfo(itemKill,userId);
result=true;
}
}
}else{
throw new Exception("redisson-您已经抢购过该商品了!");
}
}
}finally {
lock.unlock();
//lock.forceUnlock();
}
return result;
}
代码优化3:
@Autowired
private CuratorFramework curatorFramework;
private static final String pathPrefix="/kill/zkLock/";
/**
* 商品秒杀核心业务逻辑的处理-基于ZooKeeper的分布式锁
* @param killId
* @param userId
* @return
* @throws Exception
*/
@Override
public Boolean killItemV5(Integer killId, Integer userId) throws Exception {
Boolean result=false;
InterProcessMutex mutex=new InterProcessMutex(curatorFramework,pathPrefix+killId+userId+"-lock");
try {
if (mutex.acquire(10L,TimeUnit.SECONDS)){
//TODO:核心业务逻辑
if (itemKillSuccessMapper.countByKillUserId(killId,userId) <= 0){
ItemKill itemKill=itemKillMapper.selectByIdV2(killId);
if (itemKill!=null && 1==itemKill.getCanKill() && itemKill.getTotal()>0){
int res=itemKillMapper.updateKillItemV2(killId);
if (res>0){
commonRecordKillSuccessInfo(itemKill,userId);
result=true;
}
}
}else{
throw new Exception("zookeeper-您已经抢购过该商品了!");
}
}
}catch (Exception e){
throw new Exception("还没到抢购日期、已过了抢购时间或已被抢购完毕!");
}finally {
if (mutex!=null){
mutex.release();
}
}
return result;
}
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。
java商品秒杀逻辑_如何通过SpringBoot实现商城秒杀系统相关推荐
- java商品搜索功能_利用solr实现商品的搜索功能(实例讲解)
后期补充: 为什么要用solr服务,为什么要用luncence? 问题提出:当我们访问购物网站的时候,我们可以根据我们随意所想的内容输入关键字就可以查询出相关的内容,这是怎么做到呢?这些随意的数据不可 ...
- java动态执行逻辑_动态执行代码逻辑
动态执行逻辑的方法据我所知有一下两种方式 QLExpress Groovy QLExpress QLExpress是阿里开源的动态脚本执行的项目. 由阿里的电商业务规则.表达式(布尔组合).特殊数学公 ...
- JAVA的思维逻辑_[Java教程]计算机程序的思维逻辑
[Java教程]计算机程序的思维逻辑 0 2016-04-15 20:00:04 程序大概是怎么回事 计算机就是个机器,这个机器主要由CPU.内存.硬盘和输入输出设备组成.计算机上跑着操作系统,如Wi ...
- java商品列表展示_前台开发-----开发商品的列表显示
创建分页的数据模型 在model层创建一个分页的数据模型,命名为:Page.java,为了实现点击某一个系列进入到商品的列表,因此需要进行分页处理: 采用了向上取整的方式进行分页: 1 private ...
- java h5模板引擎_详解SpringBoot+Thymeleaf 基于HTML5的现代模板引擎
序言: Thymeleaf 是Java服务端的模板引擎,与传统的JSP不同,前者可以使用浏览器直接打开,因为可以忽略掉拓展属性,相当于打开原生页面,给前端人员也带来一定的便利.如果你已经厌倦了JSP+ ...
- java xml快捷注释_详解SpringBoot 快速整合Mybatis(去XML化+注解进阶)
序言:使用MyBatis3提供的注解可以逐步取代XML,例如使用@Select注解直接编写SQL完成数据查询,使用@SelectProvider高级注解还可以编写动态SQL,以应对复杂的业务需求. 一 ...
- java 定时任务怎么关闭_浅谈springboot项目中定时任务如何优雅退出
在一个springboot项目中需要跑定时任务处理批数据时,突然有个Kill命令或者一个Ctrl+C的命令,此时我们需要当批数据处理完毕后才允许定时任务关闭,也就是当定时任务结束时才允许Kill命令生 ...
- java websocket 微服务_微服务-springboot+websocket在线聊天室
一.引入依赖 org.springframework.boot spring-boot-starter-websocket 二.注入ServerEndpointExporter 编写一个WebSock ...
- java在线教学平台_开源项目SpringBoot在线教育平台
作者:java1234_小锋 本项目核心技术采用SpringBoot+mybatis; 前端是layui: 开发工具idea: 数据库mysql5.7: 模版引擎采用的是thymeleaf: 安全框架 ...
最新文章
- firefox html5 canvas,html5 Canvas
- python直方图均衡函数_Python中的自适应直方图均衡
- mysql全量备份、增量备份实现方法
- linux禁止其他主机ping自己
- Java黑皮书课后题第8章:*8.32(几何:三角形面积)编写一个方法,使用下面的方法头,返回一个三角形的面积。编写一个程序,提示用户输入三角形的三个点,然后显示三角形的面积
- Exchange队列优先级介绍和配置
- zabbix通过钉钉报警
- Google分析language垃圾信息
- delphi 2007 精简版
- 在ubuntu上定时锁屏
- KVM虚拟化技术浅析
- 崩溃中!我终于看明白了,什么是财富自由的底层逻辑!思维导图+笔记精华
- 明尼苏达双城计算机科学硕士申请截止日期,明尼苏达大学双城分校
- 电脑技术员联盟 GhostXp Sp3 装机版V5.1
- Python生成图文并茂PDF报告
- 随手查_python
- 高级程序员必会的程序设计原则 —— 墨菲定律及防呆设计
- 读什么,才能让你的心沉静下来
- opencv(python)使用ann神经网络识别手写数字
- php sleep usleep,php sleep()函数, usleep()函数的用法
热门文章
- Ansible(十七)-- ansible 中的循环(二) with_items with_list with_flattened with_together之间的区别和联系
- 授人玫瑰 手留余香 --纪念python3.2.3官方文档翻译结束
- CentOS 上的默认网关
- HashMap转LinkedHashMap
- 王者为什么服务器在维护8月4日,问道手游iOS服务器8月4日6时维护公告
- 我佛了……O2O模式的刮刮乐看的我一愣一愣的
- Appium-Open Notifications(打开通知)
- 电脑中使用git命令时出现英文警告提示怎么办
- SQLi-Labs 学习笔记(Less 31-40)
- c语言pdb文件,VISUAL c+中的pdb文件及其作用