秒杀16--秒杀压测--JMeter压力测试

秒杀16–秒杀压测–JMeter压力测试

JMeter官网:http://jmeter.apache.org/download_jmeter.cgi

安装好以后,如下图所示:
秒杀16--秒杀压测--JMeter压力测试_第1张图片

工具的使用:

1.添加一个线程组:
秒杀16--秒杀压测--JMeter压力测试_第2张图片

2.设置1000个线程数,循环10次(这个可以根据实际情况来进行设置):
秒杀16--秒杀压测--JMeter压力测试_第3张图片
线程数:就是可以有几个线程同时可以访问。
Ramp_Up Period(in seconds):表示几个线程在多长时间内要启动起来。

3.添加Http请求默认值:
(这里配了以后呢,其他的请求就不需要重复的配置)
秒杀16--秒杀压测--JMeter压力测试_第4张图片
秒杀16--秒杀压测--JMeter压力测试_第5张图片

4.新建一个Http请求:

下面配一个http请求(是一个商品列表的),这里就不需要配http请求默认值了,因为上一步已经配了。(也就是10个线程访问通过这个路(/goods/to_list)来访问商品列表这个接口
秒杀16--秒杀压测--JMeter压力测试_第6张图片

秒杀16--秒杀压测--JMeter压力测试_第7张图片

5.新建一个聚合报告:
秒杀16--秒杀压测--JMeter压力测试_第8张图片
6.启动项目:

怎样查看结果呢?是在聚合报告中,就会把这个压测的情况给展示出来,下图是一个比较概括的展示:如下:
秒杀16--秒杀压测--JMeter压力测试_第9张图片

如果想看比较详细的结果,我们可以通过“图形结果、察看结果树、等”如下:
秒杀16--秒杀压测--JMeter压力测试_第10张图片

如点击运行按钮,结果就会出来了:如下:
秒杀16--秒杀压测--JMeter压力测试_第11张图片
从图中分析可以得出,(QPS这时候为84.9)。

下面查看数据库,通过top命令(top命令就是监控我们服务器的CPU呀,等信息关于系统的负载信息。)如下:

秒杀16--秒杀压测--JMeter压力测试_第12张图片
通过下图,我们可以看到,mysql是我们的负载,是一个瓶颈。如下:
秒杀16--秒杀压测--JMeter压力测试_第13张图片
以上为简单的进行了一个接口的测试。
下面来一个比较详细的压测:

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信息:
HttpURLConnection co = (HttpURLConnection)url.openConnection();

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信息文件:
秒杀16--秒杀压测--JMeter压力测试_第14张图片

同时因为用户登录过,所以缓存里面就拥有用户信息:
秒杀16--秒杀压测--JMeter压力测试_第15张图片
数据库里面已经生成的用户:
秒杀16--秒杀压测--JMeter压力测试_第16张图片
5,秒杀开始之前:

秒杀订单表:
秒杀16--秒杀压测--JMeter压力测试_第17张图片
秒杀商品表:
秒杀16--秒杀压测--JMeter压力测试_第18张图片
详细订单表:
秒杀16--秒杀压测--JMeter压力测试_第19张图片
6,秒杀开始:

新建一个CVS Data:
秒杀16--秒杀压测--JMeter压力测试_第20张图片
配置相应的信息(将tokens.txt文件导入):
秒杀16--秒杀压测--JMeter压力测试_第21张图片
使用工具压测接口:
秒杀16--秒杀压测--JMeter压力测试_第22张图片
压测接口:

/**
	 * QPS:1306
	 * 5000 * 10
	 * */
    @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";
    }
}

7,秒杀结果:

秒杀商品表:
秒杀16--秒杀压测--JMeter压力测试_第23张图片
秒杀订单表:
秒杀16--秒杀压测--JMeter压力测试_第24张图片
订单详情表:
秒杀16--秒杀压测--JMeter压力测试_第25张图片
则以上10个用户秒杀成功了。

小结论:我们的秒杀在单线程的情况下,是没有问题的,但是在高并发的情况下,竟然出现了问题:这个秒杀会存在把库存减成负值了。这就在高并发情况下,我们的这个秒杀是不安全的。所以在接下来的学习中,要修改。

感谢:
参考:https://blog.csdn.net/Brad_PiTt7/article/details/90646389

你可能感兴趣的:(秒杀)