文档章节

Java秒杀系统实战系列~JMeter压力测试重现秒杀场景中超卖等问题

稳杰
 稳杰
发布于 2019/08/10 10:10
字数 1728
阅读 65
收藏 0

摘要:

本篇博文是“Java秒杀系统实战系列文章”的第十二篇,本篇博文我们将借助压力测试工具Jmeter重现秒杀场景(高并发场景)下出现的各种典型的问题,其中最为经典的当属“商品库存超卖”的问题,在本文我们重现这种问题,并对问题进行分析!

内容:

一个正规的、声称能承受高并发请求的系统的背后应该经历了一些不为人知的经历,这个秒杀系统也是如此,一般而言,这些经历都是比较残酷的,在本文中我们将重现出这样的经历!即采用压力测试工具Jmeter压测这个秒杀系统的“秒杀接口”!

在进入秒杀压测环节前,我们将之前的“接收前端用户的秒杀请求对应的控制器方法”复制一份,用于给JMeter压测使用,即在KillController中复制出一个新的“执行秒杀请求”的方法,其代码如下所示:

//商品秒杀核心业务逻辑-用于压力测试
@RequestMapping(value = prefix+"/execute/lock",method = RequestMethod.POST,consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
@ResponseBody
public BaseResponse executeLock(@RequestBody @Validated KillDto dto, BindingResult result){
  if (result.hasErrors() || dto.getKillId()<=0){
      return new BaseResponse(StatusCode.InvalidParams);
  }
  BaseResponse response=new BaseResponse(StatusCode.Success);
  try {
      //不加分布式锁的前提
      Boolean res=killService.killItem(dto.getKillId(),dto.getUserId());
      if (!res){
          return new BaseResponse(StatusCode.Fail.getCode(),"不加分布式锁-哈哈~商品已抢购完毕或者不在抢购时间段哦!");
      }
  }catch (Exception e){
      response=new BaseResponse(StatusCode.Fail.getCode(),e.getMessage());
  }
  return response;
}

之后,我们便可以开心的进入玩耍环节。

(1)双击JMeter的启动脚本jmeter.sh,进入JMeter的主界面,新建一个测试计划,然后在该测试计划下新建一个线程组(设定1秒并发1000个线程,后续还可以调整线程数),紧接着是新建HTTP请求项以及CSV数据文件的读取配置等等,如下图所示:

 

其中,userId参数用于模拟参与秒杀~抢购的用户,其取值将来源于上图中的“CSV数据文件设置”选项的文件,在这里Debug设定了10个用户,如下图所示:

值得一提的,“HTTP消息头管理器”选项是必需的,用于指定提交的数据的数据格式,即Content-Type的取值为application/json(因为我们的后端接口设置的就是 consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)。

在开始之前,我们设定了killId=3的商品作为秒杀~抢购的对象,并在数据表中设定其“可抢购数量/库存”的值total为6,如下图所示:

(2)万事俱备只欠东风,下面我们点击JMeter主界面的启动,即可发起“1秒内并发1000个线程”的请求,而这1000个线程对应的用户的Id,即userId将随机从上述的CSV文件中读取。在出现结果之前,我们先从理论的角度上进行分析:10个用户抢购库存只有6个的书籍,那么理论上结果应该是“库存变为0,被抢购完毕,然后在item_kill_success表中会有6条,而且也应该仅有6条秒杀成功的订单记录”! 然而,理论归理论,现实还是很残酷的!

(3)点击JMeter的启动按钮,此时可以观察控制台的输出信息以及数据库表item_kill和item_kill_success,会发现一连串“惨不忍睹”的数据记录,如下图所示:

对于初次接触“高并发秒杀业务场景”的童鞋可能会感觉到惊讶,“明明经过Postman测试过了呀,为啥还会出现这种情况!”,有点百思不得其解!

然鹅呢,Debug想说的是“事出必有因”,而出现这种情况,单单抱怨是屁用都木有的,还得去源头进行分析,即从代码的层次进行分析!

(4)我们再次来回顾一下所写的“秒杀接口”的核心逻辑,如下图所示:

 

(1)当用户在前端界面疯狂的点击“抢购”按钮时,我们上面接口将会接收到“汹涌潮水般”的用户秒杀请求,首次秒杀,很多用户都是第一次秒杀该商品,故而A流程大部分用户都将通过考核;

(2)同时,由于B流程的逻辑是判断是否可抢,而很明显,大家都是第一次来抢的,这个商品也没那么快被抢完,故而B流程大家也将通过考核;

(3)到了C流程,就需要扣减库存了,由于库存的扣减在这里只是单纯的“减一”的操作,故而在C这个流程,很多人将可以成功减一;

(4)最后大家势如破竹,赶紧到了D流程,D流程是用于“生成秒杀成功的订单”,记录用户秒杀过的商品的痕迹,同时也是为了服务于A流程;这个时候的D已经不做什么判断了(大家可以看到核心的判断其实在于A流程,这也就是问题出现的致命根源),大家就直接插入一条成功的记录了。

因此,最终就出现了“库存超卖”、“同一个用户可以抢到多次”等各种莫名其妙的问题;

通过上面的分析,其实Debug已经指出来了,问题产生的根源在于高并发的情况下D流程的处理并没有为A流程的处理赢得足够的时间,即“生成一条秒杀成功后的订单记录” 并没有及时的为 “判断用户是否已经秒杀过了~是否已经有对应的订单记录了” 的流程很好的服务!

那么在下面的篇章中,我们将从各个角度进行优化,包括数据库级别Sql的优化、代码逻辑的优化、分布式锁的引入等等(当然这些是从开发的层面来讲的,其实还有运维的层面也可以优化,比如Nginx的负载均衡、中间件的集群部署提高高可用等等)!

补充:

1、目前,这一秒杀系统的整体构建与代码实战已经全部完成了,完整的源代码数据库地址可以来这里下载:https://gitee.com/steadyjack/SpringBoot-SecondKill 记得Fork跟Star啊!!

2、最后,不要忘记了关注一下Debug的技术微信公众号:

© 著作权归作者所有

稳杰
粉丝 4
博文 18
码字总数 31856
作品 0
广州
高级程序员
私信 提问
加载中

评论(0)

JMeter学习-001-JMeter初识

Apache JMeter是Apache组织开发的基于Java的开源压力测试工具(个人发现也可用于接口测试)。用于对软件做压力测试,它最初被设计用于Web应用测试但后来扩展到其他测试领域。 它可以用于测试...

拎壶冲冲冲
2018/05/29
0
0
JavaWeb秒杀业务场景设计

秒杀业务场景设计问题经常被面试的时候被问到,在实际业务中,也常常需要实现,下面我们来看看如何实现秒杀业务. 秒杀业务,是典型的短时大量突发访问类问题 特点: 秒杀时网站的访问量大增; 秒杀...

小红牛
2018/06/11
0
0
开发人员学Linux(4):使用JMeter对网站和数据库进行压力测试

前言 表面看来,JMeter与本系列课程似乎关系不大,但实际上在后面的很多场景中起着重要作用:如何获知修改了某些代码或者设置之后系统性能是提升了还是下降了呢?商业的压力测试工具LoadRun...

周金桥
2018/06/29
0
0
开发人员学Linux(4):使用JMeter对网站和数据库进行压力测试

前言 表面看来,JMeter与本系列课程似乎关系不大,但实际上在后面的很多场景中起着重要作用:如何获知修改了某些代码或者设置之后系统性能是提升了还是下降了呢?商业的压力测试工具LoadRun...

周金桥
2017/11/06
117
0
jmeter在linux上分布式搭建

1、安装jdk # mkdir -p /usr/lib/jvm #建立安装目录 # tar -zxvf jdk1.8.0161.tar.gz # mv /usr/lib/jvm/jdk1.8.0161 /usr/lib/jvm/java8 #移动并重命名 2、Linux服务器jmeter压力环境配置 ......

xiaomin0322
2019/06/15
149
0

没有更多内容

加载失败,请刷新页面

加载更多

OSChina 周三乱弹 —— 提高不了工作效率和脸有关系

Osc乱弹歌单(2020)请戳(这里) 【今日歌曲】 @薛定谔的兄弟 :分享洛神有语创建的歌单「我喜欢的音乐」: 1 《夏令时记录(piano.ver)》- ゆめこ 手机党少年们想听歌,请使劲儿戳(这里) ...

小小编辑
今天
67
2
List的一波操作

public static void main(String[] args) { List<Entity> list = new ArrayList<>(); list.add(new Entity(1)); list.add(new Entity(2)); list.add(new Entity(3)); ......

那个猩猩很亮
今天
75
0
Spring基础

主要用于service层; 轻量级java开发框架; 各层 web层:struts,spring-MVC service层:spring dao层:hibernate,mybatis , jdbcTemplate --> spring-data Spring核心:控制反转IOC 切面编...

七宝1
今天
30
0
解决overflow+border-radius+transform圆角问题

网上还有其他版本,但是对我来说都不好使,下面是我在Chrome上的代码。overflow:hidden依然是不能正常使用,换成unset就可以,读者如果有更好的解决方案,请留言,谢谢。 <figure> <img...

hi懒喵
今天
53
0
《C语言》—— 数组

书籍使我变成了一个幸福的人,使我的生活变成轻松而舒适的诗。——高尔基 本文已经收录至我的GitHub,欢迎大家踊跃star 和 issues。 https://github.com/midou-tech/articles 点关注,不迷路!...

龙跃十二
今天
84
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部