Apache JMeter是Apache组织开发的基于Java的压力测试工具。用于对软件做压力测试,它最初被设计用于Web应用测试,但后来扩展到其他测试领域。
1)因为JMeter是使用JAVA写的,所以使用JMeter之前,先安装JAVA环境,有关JAVA环境的配置在这就不详细说了,可以去oracle官网下载JDK:https://www.oracle.com/technetwork/java/javase/downloads/index.html
1.JMeter下载地址: http://jmeter.apache.org/
2.解压下载的二进制包,使用cmd命令进入bin目录,使用jmeter.bat启动程序。(注意直接双击jmeter.bat无法启动时需要使用Window+R,输入cmd,然后进入bin目录如下)
1.创建线程组
在“测试计划”上右键 【添加】–>【Threads(Users)】–>【线程组】。
2.设置线程数和循环次数。我这里设置线程数为500,循环一次。
3.配置元件,在我们刚刚创建的线程组上右键 【添加】–>【配置元件】–>【HTTP请求默认值】。
4.配置我们需要进行测试的程序协议、地址和端口
4.添加察看结果树和聚合报告
在我们刚刚创建的线程组上右键 【添加】–>【监听器】–>【察看结果树】。添加聚合报告,右键 【添加】–>【监听器】–>【聚合报告】。
直接添加,然后点击运行按钮就可以看到结果了。
结果树分析:
测试结果:
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信息文件:
秒杀开始:新建一个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";
}
}
秒杀开始之前
秒杀订单表:
我们的秒杀在单线程的情况下,是没有问题的,但是在高并发的情况下,竟然出现了问题:这个秒杀会存在把库存减成负值了。这就在高并发情况下,我们的这个秒杀是不安全的。