JMeter官网:http://jmeter.apache.org/download_jmeter.cgi
工具的使用:
2.设置1000个线程数,循环10次(这个可以根据实际情况来进行设置):
线程数:就是可以有几个线程同时可以访问。
Ramp_Up Period(in seconds):表示几个线程在多长时间内要启动起来。
3.添加Http请求默认值:
(这里配了以后呢,其他的请求就不需要重复的配置)
4.新建一个Http请求:
下面配一个http请求(是一个商品列表的),这里就不需要配http请求默认值了,因为上一步已经配了。(也就是10个线程访问通过这个路(/goods/to_list)来访问商品列表这个接口)
怎样查看结果呢?是在聚合报告中,就会把这个压测的情况给展示出来,下图是一个比较概括的展示:如下:
如果想看比较详细的结果,我们可以通过“图形结果、察看结果树、等”如下:
如点击运行按钮,结果就会出来了:如下:
从图中分析可以得出,(QPS这时候为84.9)。
下面查看数据库,通过top命令(top命令就是监控我们服务器的CPU呀,等信息关于系统的负载信息。)如下:
通过下图,我们可以看到,mysql是我们的负载,是一个瓶颈。如下:
以上为简单的进行了一个接口的测试。
下面来一个比较详细的压测:
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);
}
同时因为用户登录过,所以缓存里面就拥有用户信息:
数据库里面已经生成的用户:
5,秒杀开始之前:
新建一个CVS Data:
配置相应的信息(将tokens.txt文件导入):
使用工具压测接口:
压测接口:
/**
* 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,秒杀结果:
秒杀商品表:
秒杀订单表:
订单详情表:
则以上10个用户秒杀成功了。
小结论:我们的秒杀在单线程的情况下,是没有问题的,但是在高并发的情况下,竟然出现了问题:这个秒杀会存在把库存减成负值了。这就在高并发情况下,我们的这个秒杀是不安全的。所以在接下来的学习中,要修改。
感谢:
参考:https://blog.csdn.net/Brad_PiTt7/article/details/90646389