▶【SecKill】U4 JMeter压测
一、JMeter入门
1、JMeter下载和安装
(1)下载地址:https://jmeter.apache.org/download_jmeter.cgi
(2)解压,将 apache-jmeter-5.4.3/bin 下的 jmeter.bat 发送到桌面
(3)启动jmeter.bat
启动成功!
2、术语
QPS(Queries Per Second):每秒查询数,每秒能够响应的查询次数,对一个特定的查询服务器在规定时间内所处理流量多少的衡量标准。在因特网上,作为域名系统服务器的机器的性能经常用每秒查询率来衡量。每秒的响应请求数,也即是最大吞吐能力。
3、JMeter压测:商品列表
(1)添加 线程组
(2)添加 HTTP默认请求
(3)添加 HTTP请求
(4)添加 聚合报告
(5)启动
A. 10个线程:QPS=2.5个/s
Label:请求的名称,就是脚本中Sampler的名称。
#Samples(样本):总共发给服务器的请求数量,如果模拟10个用户,每个用户迭代10次,那么总的请求数为:10*10 =100次。
Average(平均值):默认情况下是单个Request的平均响应时间,当使用了Transaction Controller(事务控制器) 时,也可以用Transaction的时间,来显示平均响应时间 ,单位是毫秒。
Median(中位数):50%用户的响应时间小于该值。
90% Line(90% 百分位):90%用户的响应时间小于该值。
95% Line(95% 百分位):95%用户的响应时间小于该值。
99% Line(99% 百分位):99%用户的响应时间小于该值。
Min(最小值):最小的响应时间。
Maximum(最大值):最大的响应时间。
Error%(异常%):错误率=错误请求的数量/请求的总数。
Throughput(吞吐量):默认情况下表示每秒完成的请求数(Request per Second)。
Received KB/sec (接收数据):每秒从服务器端接收到的数据量。
Sent KB/sec(发送):每秒发送到服务器端的数据量。
B. 1000个线程:QPS=200个/s
4、检测吞吐量小的原因:数据库
(1)监控服务器的CPU、内存
top
(2)同时压测,设置1000*10个线程
发现占用CPU最大的是mysql
二、自定义变量模拟多用户
1、新建 com.kirin.miaosha.controller 包下 UserController.java(获取用户信息),压测带参用户
package com.kirin.miaosha.controller;
@Controller
@RequestMapping("/user")
public class UserController {
@Autowired
MiaoshaUserService userService;
@Autowired
RedisService redisService;
@RequestMapping("/info")
@ResponseBody
public Result<MiaoshaUser> info(Model model,MiaoshaUser user) {
return Result.success(user);
}
}
2、自定义变量模拟多用户:获取用户信息
(1)新建 HTTP请求
(2)运行系统,做一次商品秒杀,查看后台,找到token值=8da08ab9cff748809c2c1456e0f0c930,填入Jmeter中
(3)禁用 “商品列表”HTTP请求
(4)压测:QPS高于 “商品列表”HTTP请求
3、自定义配置文件JMeter压测
(1)添加 CSV 数据文件设置:配置文件
(2)编写配置文件
(3)读取我们自己编写的配置文件,并且标注变量名称
(4)将数据库的用户信息填入,其中配置文件信息,用英文逗号隔开
(5)
(6)压测
三、Linux环境下,JMeter命令行使用
1、打jar包
(1)删除所有打war包的插件、依赖、代码
(2)加入jar包依赖
(3)打jar包
mvn clean package
(4)打开jar包,进入META-INF目录下,打开MANIFEST.MF文件
Main-Class:SpringBoot框架的启动类,在这个类中可以跟进看源码
Start-Class:我们自己编写的启动类
2、将项目部署到Linux服务器上
(1)将jar包上传Linux的tmp文件夹下
(2)将这个进程的输出结果放进nohup.out文件中
nohup java -jar miaosha_2.jar &
(3)启动
tail -f nohup.out
访问浏览器
★3、压测(起始标准压测数据)
(1)在Windows目录下写好jmx脚本文件
① 设置压测数据
② 文件另存为goods_list.jmx,保存到桌面
③ 将jmx脚本文件上传Linux服务器的tmp文件夹下
(2)在Linux安装Jmeter
① 将Jmeter放入tmp文件夹下
② 解压
unzip apache-jmeter-5.4.3.zip
(3)运行,压测
./apache-jmeter-5.4.3/bin/jmeter.sh -n -t goods_list.jmx -l result.jtl
-n:不使用图形界面
-t:不使用config.text脚本文件
-l:保存输出结果到result.jtl
压测完,生成result.jtl,下载到本地桌面
(3)再将result.jtl导入到jmeter的聚合报告中
结果1:1.goods_list.jmx
并发10000 = 1000个线程 * 10次循环
QPS=553.2/s
结果2:2.miaosha.jmx
并发10000 = 1000个线程 * 10次循环
QPS=146.9/s
A. 添加 com.kirin.miaosha.util / UserUtil.java:生成5000个用户的方法
package com.kirin.miaosha.util;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.kirin.miaosha.domain.MiaoshaUser;
//压测:生成5000个用户的方法
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.setLong(6, user.getId());
pstmt.addBatch();
}
pstmt.executeBatch();
pstmt.close();
conn.close();
System.out.println("insert to db");
//登录,生成token
String urlString = "http://localhost:8080/login/do_login";
File file = new File("E:/tokens.txt");
if(file.exists()) {
file.delete();
}
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();
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("\r\n".getBytes());
System.out.println("write to file : " + user.getId());
}
raf.close();
System.out.println("over");
}
public static void main(String[] args)throws Exception {
createUser(5000);
}
}
B. 添加 com.kirin.miaosha.util / DBUtil.java
package com.kirin.miaosha.util;
public class DBUtil {
private static Properties props;
static {
try {
InputStream in = DBUtil.class.getClassLoader().getResourceAsStream("application.properties");
props = new Properties();
props.load(in);
in.close();
}catch(Exception e) {
e.printStackTrace();
}
}
public static Connection getConn() throws Exception{
String url = props.getProperty("spring.datasource.url");
String username = props.getProperty("spring.datasource.username");
String password = props.getProperty("spring.datasource.password");
String driver = props.getProperty("spring.datasource.driver-class-name");
Class.forName(driver);
return DriverManager.getConnection(url,username, password);
}
}
C. 修改代码
D. 先运行项目,再运行UserUtil.java
E. 新建 HTTP请求
F. 新建 CSV 数据文件设置
添加刚刚生成的token.txt
添加参数
另存为miaosha.jmx
H. 将miaosha.jmx和tokens.txt上传Linux服务器的tmp文件夹下
I. 修改配置文件中token路径
vi 2.miaosha.jmx
J. 压测
./apache-jmeter-5.4.3/bin/jmeter.sh -n -t 2.miaosha.jmx -l result2.jtl
五、Spring Boot打Jwar包
1、在pom.xml文件中,添加打包为war包的标签
<packaging>war</packaging>
2、添加spring-boot-starter-tomcat的provided编译时的依赖
<!-- 10.添加spring-boot-starter-tomcat的provided编译时的依赖:Spring Boot打Jwar包 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <scope>provided</scope><!-- provided:编译时的依赖 --> </dependency>
3、maven-war-plugin插件
<!-- 10.【Spring Boot打Jwar包】添加spring-boot-starter-tomcat的provided编译时的依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <scope>provided</scope><!-- provided:编译时的依赖 --> </dependency> <finalName>${project.artifactId}</finalName> <!-- 10.【Spring Boot打Jwar包】添加maven-war-plugin插件 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <configuration> <failOnMissingWebXml>false</failOnMissingWebXml> </configuration> </plugin> <!-- 打jar包插件 --> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin>
4、在主类 com.kirin.miaosha / MainApplication.java 中,继承SpringBootServletInitializer,重写configure()方法
package com.kirin.miaosha;
@SpringBootApplication
public class MainApplication extends SpringBootServletInitializer{
public static void main(String[] args) throws Exception {
SpringApplication.run(MainApplication.class, args);
}
//Spring Boot打Jwar包:继承SpringBootServletInitializer,重写configure()方法
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(MainApplication.class);
}
}
5、打war包
(1)找到项目目录
(2)打war包
mvn clean package
(3)将war包放到Tomcat的webapps目录下
(4)启动Tomcat:startup.bat
D:\DevTool\Java\Tomcat\apache-tomcat-8.5.5\bin\startup.bat