秒杀JMeter压力测试

1. JMeter介绍

Apache JMeter是Apache组织开发的基于Java的压力测试工具。用于对软件做压力测试,它最初被设计用于Web应用测试,但后来扩展到其他测试领域。

2.JMeter安装与配置

1)因为JMeter是使用JAVA写的,所以使用JMeter之前,先安装JAVA环境,有关JAVA环境的配置在这就不详细说了,可以去oracle官网下载JDK:https://www.oracle.com/technetwork/java/javase/downloads/index.html

3.JMeter下载与使用

1.JMeter下载地址: http://jmeter.apache.org/
秒杀JMeter压力测试_第1张图片
2.解压下载的二进制包,使用cmd命令进入bin目录,使用jmeter.bat启动程序。(注意直接双击jmeter.bat无法启动时需要使用Window+R,输入cmd,然后进入bin目录如下)
在这里插入图片描述

4.创建测试

1.创建线程组
在“测试计划”上右键 【添加】–>【Threads(Users)】–>【线程组】。
秒杀JMeter压力测试_第2张图片
2.设置线程数和循环次数。我这里设置线程数为500,循环一次。
秒杀JMeter压力测试_第3张图片
3.配置元件,在我们刚刚创建的线程组上右键 【添加】–>【配置元件】–>【HTTP请求默认值】。
秒杀JMeter压力测试_第4张图片
4.配置我们需要进行测试的程序协议、地址和端口
秒杀JMeter压力测试_第5张图片
4.添加察看结果树和聚合报告
在我们刚刚创建的线程组上右键 【添加】–>【监听器】–>【察看结果树】。添加聚合报告,右键 【添加】–>【监听器】–>【聚合报告】。

直接添加,然后点击运行按钮就可以看到结果了。

结果树分析:
秒杀JMeter压力测试_第6张图片
测试结果:
1)50个用户同时访问企业用户会议室预定页面,平均响应时间是0.146秒,最大的响应时间0.387秒,最小的响应时间是0.096秒,错误率为0。
在这里插入图片描述
2)100个用户同时访问企业用户会议室预定页面,平均响应时间是2.295秒,最大的响应时间8.132秒,最小的响应时间是0.425秒,错误率为0。
在这里插入图片描述
下面做一个详细的压力测试
1,启动系统,先生成1000个用户并且存储至数据库:

public class UserUtil {
 
 private static void createUser(int count) throws Exception{
  List<MiaoshaUser> users = new ArrayList<MiaoshaUser>(count);
  //生成用户
  for(int i=0;i<count;i++) {
   MiaoshaUser user = new MiaoshaUser();
   user.setId(13000000000L+i);
   user.setLoginCount(1);
   user.setNickname("user"+i);
   user.setRegisterDate(new Date());
   user.setSalt("1a2b3c");
   user.setPassword(MD5Util.inputPassToDbPass("123456", user.getSalt()));
   users.add(user);
  }
  System.out.println("create user");
  //插入数据库
  Connection conn = DBUtil.getConn();
  String sql = "insert into miaosha_user(login_count, nickname, register_date, salt, password, id)values(?,?,?,?,?,?)";
  PreparedStatement pstmt = conn.prepareStatement(sql);
        //生成用户
  for(int i=0;i<users.size();i++) {
   MiaoshaUser user = users.get(i);
   pstmt.setInt(1, user.getLoginCount());
   pstmt.setString(2, user.getNickname());
   pstmt.setTimestamp(3, new Timestamp(user.getRegisterDate().getTime()));
   pstmt.setString(4, user.getSalt());
   pstmt.setString(5, user.getPassword());
   pstmt.setLong(6, user.getId());
   pstmt.addBatch();
  }
  pstmt.executeBatch();
  pstmt.close();
  conn.close();

2.让每个用户登录到系统,并且生成与其对应token信息,将用户与token信息存储到文件里面去:

 //登录,生成token
  String urlString = "http://localhost:8080/login/do_login";
  File file = new File("D:/tokens.txt");
  if(file.exists()) {
   file.delete();
  }

3.模拟登录获取token信息:

RandomAccessFile raf = new RandomAccessFile(file, "rw");
  file.createNewFile();
  raf.seek(0);
  for(int i=0;i<users.size();i++) {
   MiaoshaUser user = users.get(i);
   URL url = new URL(urlString);
   HttpURLConnection co = (HttpURLConnection)url.openConnection();
   co.setRequestMethod("POST");
   co.setDoOutput(true);
   OutputStream out = co.getOutputStream();
   String params = "mobile="+user.getId()+"&password="+MD5Util.inputPassToFormPass("123456");
   out.write(params.getBytes());
   out.flush();
   InputStream inputStream = co.getInputStream();
   ByteArrayOutputStream bout = new ByteArrayOutputStream();
   byte buff[] = new byte[1024];
   int len = 0;
   while((len = inputStream.read(buff)) >= 0) {
    bout.write(buff, 0 ,len);
   }
   inputStream.close();
   bout.close();
   String response = new String(bout.toByteArray());
   JSONObject jo = JSON.parseObject(response);
   String token = jo.getString("data");
   System.out.println("create token : " + user.getId());
   
   String row = user.getId()+","+token;
   raf.seek(raf.length());
   raf.write(row.getBytes());
   raf.write("\r\n".getBytes());
   System.out.println("write to file : " + user.getId());
  }
  raf.close();
  
  System.out.println("over");
 }

4.再启动main方法,开始执行:

public static void main(String[] args)throws Exception {
		createUser(5000);
	}

用户与对应的token信息文件:
秒杀JMeter压力测试_第7张图片
秒杀开始:新建一个CVS Data:配置相应的信息(将tokens.txt文件导入):
压测接口:

@RequestMapping("/do_miaosha")//传入user对象,就可以取usser的值 ${user.nickname}
    public String list(Model model,MiaoshaUser user,
      @RequestParam("goodsId")long goodsId) {
     model.addAttribute("user", user);
       //如果用户为空,则返回至登录页面
     if(user == null) {
      return "login";
     }
     //判断库存,只有当库存>0时,才进行操作,多线程下会出错
     GoodsVo goods = goodsService.getGoodsVoByGoodsId(goodsId);
     int stock = goods.getStockCount();
     if(stock <= 0) {//失败  库存至临界值0的时候,此时刚好加入了10个线程,那么库存就会-10
      model.addAttribute("errmsg", CodeMsg.MIAO_SHA_OVER.getMsg());
      return "miaosha_fail";
     }
     //判断是否已经秒杀到了。判断这个秒杀订单形成没有,判断是否已经秒杀到了,避免一个账户秒杀多个商品
     MiaoshaOrder order = orderService.getMiaoshaOrderByUserIdGoodsId(user.getId(), goodsId);
     if(order != null) {//重复下单
      model.addAttribute("errmsg", CodeMsg.REPEATE_MIAOSHA.getMsg());
      return "miaosha_fail";
     }
     //减库存 下订单 写入秒杀订单
     //可以秒杀,原子操作:1,库存-1  2,下订单   3,写入秒杀订单------->是一个事务
     OrderInfo orderInfo = miaoshaService.miaosha(user, goods);
     //如果秒杀成功,则直接跳转到订单详情页中去
     model.addAttribute("orderInfo", orderInfo);
     model.addAttribute("goods", goods);
        return "order_detail";
    }
}

秒杀开始之前
秒杀订单表:

秒杀JMeter压力测试_第8张图片
秒杀结果
秒杀订单表:
秒杀JMeter压力测试_第9张图片

总结

我们的秒杀在单线程的情况下,是没有问题的,但是在高并发的情况下,竟然出现了问题:这个秒杀会存在把库存减成负值了。这就在高并发情况下,我们的这个秒杀是不安全的。

你可能感兴趣的:(秒杀JMeter压力测试)