jmeter分布式压力测试
一。 压力测试简介
软件测试(英语:software testing),描述一种用来促进鉴定软件的正确性、完整性、安全性和质量的过程。换句话说,软件测试是一种实际输出与预期输出间的审核或者比较过程。软件测试的经典定义是:在规定的条件下对程序进行操作,以发现程序错误,衡量软件质量,并对其是否能满足设计要求进行评估的过程。
性能测试是通过自动化的测试工具模拟多种正常、峰值以及异常负载条件来对系统的各项性能指标进行测试。负载测试和压力测试都属于性能测试,两者可以结合进行。通过负载测试,确定在各种工作负载下系统的性能,目标是测试当负载逐渐增加时,系统各项性能指标的变化情况。压力测试是通过确定一个系统的瓶颈或者不能接受的性能点,来获得系统能提供的最大服务级别的测试。
- 负载测试:模拟实际软件系统所承受的负载条件的系统负荷,通过不断加载(如逐渐增加模拟用户的数量)或其它加载方式来观察不同负载下系统的响应时间和数据吞吐量、系统占用的资源(如CPU、内存)等,以检验系统的行为和特性,以发现系统可能存在的性能瓶颈、内存泄漏、不能实时同步等问题。负载测试更多地体现了一种方法或一种技术。
- 压力测试:在强负载(大数据量、大量并发用户等)下的测试,查看应用系统在峰值使用情况下操作行为,从而有效地发现系统的某项功能隐患、系统是否具有良好的容错能力和可恢复能力。压力测试分为高负载下的长时间(如24小时以上)的稳定性压力测试和极限负载情况下导致系统崩溃的破坏性压力测试。
性能测试指标
- RPS(Request Per Second):每秒处理请求数(处理开始到处理结束 中间时间成为响应时间) 可以通过测试工具 模拟多线程访问某个http计算 jmeter中是通过聚合报告的throughput(吞吐量)查看 不同的系统由于cpu 内存 磁盘 网络的限制 rps(吞吐量重要指标)也就不同
- QPS/TPS(Query/Transcation Per Second) :每秒查询或者事务数 每个请求可能是基于查询或者修改目的 请求被分为查询请求和事务请求 一般互联网系统QPS>TPS 可以说RPS是QPS和TPS的总和
- PV/UV:网站流量是指网站的访问量,用来描述访问网站的用户数量以及用户所浏览的网页数量等指标,常用的统计指标包括网站的独立用户数量、总用户数量(含重复访问者)、网页浏览数量、每个用户的页面浏览数量、用户在网站的平均停留时间等。网站访问量的常用衡量标准:独立访客(UV) 和 综合浏览量(PV),一般以日为单位来衡量和计算。
独立访客(UV):指一定时间范围内相同访客多次访问网站,只计算为1个独立访客。
综合浏览量(PV):指一定时间范围内页面浏览量或点击量,用户每次刷新即被计算一次。 - 并发量:就是同一时刻服务器在同时处理多少个请求 QPS=(同一时刻处理的请求 也就是并发量)/响应的时间,一个系统吞吐量通常由QPS(TPS)、并发数两个因素决定,每套系统这两个值都有一个相对极限值,在应用场景访问压力下,只要某一项达到系统最高值,系统的吞吐量就上不去了,如果压力继续增大,系统的吞吐量反而会下降,原因是系统超负荷工作,上下文切换、内存等等其它消耗导致系统性能下降。
性能指标推算web资源公式
假设目前需求确定系统的平均日PV(多天pv总和/天数) 通过日PV可以推算:
- 网络带宽=平均日pv/24*60*60*页面的平均大小*(峰值和平均流量倍数 一般是5)
假设平均日pv 100w 100w/(24-8)*60*60 【一天的总描述 除掉睡觉8个小时】 等于1s的pv =18
峰值流程可能是平均流程 5倍 秒pv(rps/qps/tps)=18 *5 =90 最后乘 平均每个页面大小 假设是 50KB
最终带宽 =90*50KB=5M带宽
- 并发数= PV / 统计时间 * 页面衍生连接次数 * http响应时间 * 因数 / web服务器数量;
假设平均日pv 100w 100w、的秒pv(rps/qps/tps) 100w/(24-8)*60*60*峰值倍数5 =18 *5 =90
qps*平均响应时间 假设2s =90*2=180 假设一个访问派生的js /css /图片是 10个 就是 180*10=1800并发数
- 机器数=并发数/每 台机器能处理并发数
假设上面算出了日pv 100w需要1800并发数 单机能处理1000个并发 需要 1800/1000=2台机器
- QPS=( 总PV数 ) / ( 每天秒数 【除掉8个小时睡觉时间】 ) = 每秒请求数(QPS)
- 峰值QPS=( 总PV数 * 80% ) / ( 每天秒数 * 20% ) = 峰值时间每秒请求数(QPS)
原理:每天80%的访问集中在20%的时间里,这20%时间叫做峰值时间
100w 总pv 峰值qps= (100w*0.8)/(24*60*60*0.2)=80w/17,280=47qps
使用qps计算机器:峰值时间每秒QPS / 单台机器的QPS = 需要的机器
二。jmeter用法
1》jmeter安装
下载jmeter4.0(http://jmeter.apache.org/download_jmeter.cgi) 前提安装jdk1.8
2》解压并运行
解压后目录结构如下
printable_docs 用户手册和开始文档 访问index.html
docs\api javaapi目录 访问index.html
bin目录 所有可执行文件目录
windows下点击bin目录下 jmeter.bat linux下运行 jmeter.sh运行
window下运行meter.bat会打开gui运行
打开界面风格
3》jmeter组件概念
- Test Plan也就是测试计划,概念有点类似eclipse里面的project(项目、工程)。 一个JMeter测试计划有很多种测试元素组成。一般至少包含一个Thread Group(线程组),在每个Thread Group里面又可以包含Controller,Listener,Timer等等。
- Thread Group就是线程组,,可以看做一个虚拟用户组,线程组中的每个线程都可以理解为一个虚拟用户。线程组中包含的线程数量在测试执行过程中是不会发生改变的。
- JMeter主要有两种类型的Controllers,Samplers和Logic Controllers。
Samplers允许JMeter向server发送一系列的请求。她模拟了用户从页面想终端server发送请求的行为,比如新建一个HTTP的Sampler,可执行POST、GET、DELETE等请求。
Logic Controllers其实最主要的作用就是控制执行顺序。她可以用来控制sampler在一个线程中的执行顺序,也可以改变来自其他elements请求的顺序。典型的代表就是,IF Controller、While Controller等。
一般控制器都添加在线程组下 - Listeners 看到samplers测试执行的结果,形式多样,包括表格、图标、树状或者是日志文件中的简单文本。在每次smaplers执行的时候,结果数据都会被收集在多样的Listeners中。 Listeners可以加在test plan下的任何地方
比如 View Results Tree 可以查看所有模拟线程的执行结果
Summary Report/Aggregate Report 可以查看吞吐量(qps)
- Timers 默认情况下,一个JMeter线程在sampler之间发送请求是不会暂停的。有时候这可能不是你想要的。这时候你就可以加一个timer元素,你可以用她来在每个请求之间定义一个时间间隔。
- Assertions,有点类似JUNIT里面的断言,她可以对请求中返回的结果做一些验证性的处理。使用Assertions可以定制请求正确时返回的成功信息,也可以高亮返回的错误信息。
linux下shell下进行测试 可以先使用window客户端创建测试计划 生成jmx文件拷贝到linux使用命令(http://jmeter.apache.org/usermanual/get-started.html#non_gui)
jmeter -n -t my_test.jmx -l log.jtl
生成的jtl 拷贝到window客户端 打开Aggregate Report 可以打开jtl文件表格预览结果4》jmeter简单测试案例
使用springboot开发几个简单的控制层案例 使用jmeter进行测试 使用jpa来操作数据库
maven依赖
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.et</groupId>
<artifactId>springfox</artifactId>
<version>0.0.1-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.8.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.24</version>
</dependency>
<dependency>
<groupId>io.springfox.ui</groupId>
<artifactId>springfox-swagger-ui-rfc6570</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
</project>
springboot配置文件application.propertiesspring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=test
spring.datasource.password=test
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
控制层代码package test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@EnableAutoConfiguration
public class TestController {
@Autowired
private TestDao testDao;
//普通输出
@RequestMapping(value="/pv",method=RequestMethod.GET)
@ResponseBody
String pv() {
return "Hello World!";
}
//查询数据库
@RequestMapping(value="/qps",method=RequestMethod.GET)
@ResponseBody
String qps() {
Iterable<Shop> findAll = testDao.findAll();
return "Hello World!"+findAll.iterator().next().getAddress();
}
//插入数据库
@RequestMapping(value="/tps",method=RequestMethod.GET)
@ResponseBody
String tps(Shop shop) {
testDao.save(shop);
return "1";
}
public static void main(String[] args) throws Exception {
SpringApplication.run(TestController.class, args);
}
}
java实体类package test;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "shop")
public class Shop {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
@Column(nullable = false)
private String name;
@Column(nullable = true)
private String address;
}
测试的dao
package test;
import org.springframework.data.repository.CrudRepository;
public interface TestDao extends CrudRepository<Shop, Integer>{
}
数据库CREATE TABLE `shop` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(29) DEFAULT NULL,
`address` varchar(200) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=10001 DEFAULT CHARSET=latin1;
/*Data for the table `shop` */
insert into `shop`(`id`,`name`,`address`) values (1,'9','aaaaa'),(2,'100','aaaaa'),(3,'94','aaaaa'),(4,'15','aaaaa'),(5,'114','aaaaa');
使用jmeter测试的地址为 :
http://localhost:8080/pv 直接测试输出内容
http://localhost:8080/qps 直接查询数据库
http://localhost:8080/tps 直接测试操作数据库数据
pv和qps都没有参数传递 操作过程参考
在测试计划 添加线程组
Numbers of Users 设置为 1000 表示同时启动1000个线程并发
Loop Count设置为10 每个线程会循环10次
添加 sampler 的 Http Request 输入ip和端口 请求的path 保存为jmx文件
添加listener 的 Summary Report 用于监听并发量
这里测试结果查看Summary Report的Throughout(吞吐量qps)我的结果是
/pv 3000左右qps
/qps 1500不到qps
tps有参数传递 操作过程参考
tps模拟三千个并发 每个都到数据库插入数据 必须是不一样的数据 此时我们可以添加一个计数器 让他自增
值的设置参考
在http request的页面需要添加两个参数 name和address
在添加一个listerner View Results Tree 可以看实际执行被替换的值 点击运行开始测试 (启动springboot程序)
查看吞吐量 1000左右
查看实际运行参数
当然动态值可以使用csv维护 或者随机值 都可以
三。jmeter分布式压测
上面的测试是负载测试 没有达到系统极限 只是为了获取单机的吞吐量
如果系统需要做压力测试 必须测试出系统的极限 也就是出现错误率 但是单机的线程数有限 比如设置线程数 10000时系统就已经死机 无法进行测试 可以将10000个线程分发到多个系统中进行测试 然后将多机结果合并到一个客户端进行统计 这就是分布式压测 原理图如下
注意点:
- You have setup SSL for RMI or disabled it. 文章中描述 必须要安装sslforrmi或者禁用他
jmeter4.0后 默认远程测试 必须使用ssl for rmi来进行资源分配 可以禁用他 找到 bin/jmeter.properties(所有客户控制机和
agent代理机)都需要修改
server.rmi.ssl.disable=true
如果不想禁用他 可以在任意一台机器生成keystore文件 参考http://jmeter.apache.org/usermanual/remote-test.html#setup_ssl执行bin\create-rmi-keystore.sh
输入别名rmi 其他的随便输入 密钥对密码直接回车默认密码时changeit最后输入yes 会在bin目录下生成rmi_keystore.jks
如果按照上面规则生成的就不需要修改任何配置 拷贝到所有其他机器的jmeter/bin目录 如果别名不是rmi密码也自己设置了
可以修改 jmeter.properties文件 如下相关项
#
# Configuration of Secure RMI connection
#
# Type of keystore : JKS
#server.rmi.ssl.keystore.type=JKS
#
# Keystore file that contains private key
#server.rmi.ssl.keystore.file=rmi_keystore.jks
#
# Password of Keystore
#server.rmi.ssl.keystore.password=changeit
#
# Key alias
#server.rmi.ssl.keystore.alias=rmi
#
# Type of truststore : JKS
#server.rmi.ssl.truststore.type=JKS
#
# Keystore file that contains certificate
#server.rmi.ssl.truststore.file=rmi_keystore.jks
#
# Password of Trust store
#server.rmi.ssl.truststore.password=changeit
#
# Set this if you don't want to use SSL for RMI
#server.rmi.ssl.disable=false
修改windows客户机agent的地址remote_hosts=192.168.58.149,192.168.58.150
两个agent机器上 分别启动
./jmeter-server
出现以下表示成功[root@node2 bin]# ./jmeter-server
Created remote object: UnicastServerRef2 [liveRef: [endpoint:[192.168.58.149:58159,SSLRMIServerSocketFactory(host=node2/192.168.58.149, keyStoreLocation=rmi_keystore.jks, type=JKS, trustStoreLocation=rmi_keystore.jks, type=JKS, alias=rmi),SSLRMIClientSocketFactory(keyStoreLocation=rmi_keystore.jks, type=JKS, trustStoreLocation=rmi_keystore.jks, type=JKS, alias=rmi)](local),objID:[3e87da73:162ad2d59e0:-7fff, -8236211744330230598]]]
window机器 点击jmeter.bat启动界面 添加测试计划 点击run - startall 就可以启动测试在agent机器的linux命令行 可以看到 输出
Starting the test on host 192.168.58.149 @ Mon Apr 09 18:37:57 PDT 2018 (1523324277629)
Finished the test on host 192.168.58.149 @ Mon Apr 09 18:38:33 PDT 2018 (1523324313314)
表示 开始成功测试 检测多个agent是否同时有输出