学习使用Jmeter做压力测试(一)--压力测试基本概念

一、性能测试的概念

性能测试是通过自动化的测试工具模拟多种正常峰值及异常负载条件来对系统的各项性能指标进行测试。负载测试压力测试都属于性能测试,两者可以结合进行。

通过负载测试,确定在各种工作负载下系统的性能,目标是当负载逐渐增加时,测试系统各项性能指标的变化情况。压力测试时通过确定一个系统的瓶颈或者不能接受的

性能点,来获取系统能提供的最大服务级别的测试。性能测试主要包括负载测试、强度测试、容量测试。

二、性能测试的指标

web服务器:

Avg Rps: 平均每秒的响应次数 = 总请求数 /秒数;

Avg time to last byte per terstion(mstes): 平均每秒业务脚本的迭代次数;

Successful Rounds: 成功的请求;

Failed Rounds: 失败的请求;

Successful Hits: 成功的点击次数;

Failed Hits: 失败的点击次数;

Hits Per Second: 每秒点击次数;

Successful Hits Per Second:每秒成功的点击次数;

Failed Hits Per Second: 每秒失败的点击次数;

Attempted Connections: 尝试连接数;

Throughput: 吞吐率;

数据库服务器:

User Connections: 用户连接数,也就是数据库的连接数量;

Number of Deadlocks: 数据库死锁;

Butter Cache Hit: 数据库Cache 的命中情况;

三、性能测试的流程

1.明确性能测试需求;

2.制定性能测试方案;

2.1.测试范围

2.2.入口标准

2.3.出口标准

2.4.测试策略(测试环境指标、存量数据、业务场景、测试通过标准等)

2.5.测试风险

2.6.测试资源

3.设计性能测试用例;

4.执行性能测试用例;

5.分析性能测试结果;

6.生成性能测试报告;

四、性能测试的工具--JMeter

为什么是JMeter而不是LoadRunner呢  1.更少的投入,针对有限的测试成本;  2.开源工具的可定制性无可比拟;  3.通过社区得到最大程度的支持。

JMeter是Apache组织开发的基于Java的压力测试工具。最初被设计用于web应用的测试,后来扩展到其他测试领域。可用于测试静态和动态资源,如文件、Java服务

程序、Java对象、数据库等。JMeter能够对应用程序做功能/回归测试,通过创建带有断言的脚本来验证被测程序返回了期望的结果。而且为了保证最大限度的灵活性,

JMeter允许使用正则表达式创建断言。

五、JMeter的特性

1.支持对多种服务类型进行测试;

2.支持通过录制/回访方式获取测试脚本;

3.具备高可移植性,是纯Java 程序;

4.采用多线程框架,允许通过多个线程并发取样及通过独立的线程组对不同的功能同时取样;

5.精心设计的GUI支持高速用户操作和精确计时;

6.支持缓存和离线的方式分析/回放测试结果;

7.高扩展性;

六、JMeter常用测试元件

1.线程组

用来管理执行性能测试所需的JMeter线程。

a.可以设置线程数量

b.设置线程启动周期

c.设置执行测试脚本的循环次数

2.控制器

JMeter有两种类型的控制器:采样器和逻辑控制器。

采样器被用来向服务器发送请求。JMeter采样器包含:FTP Request、HTTP Request、JDBC Request等。

逻辑控制器用来控制JMeter的测试逻辑,特别是何时发送请求。

3.监听器

监听器提供了对JMeter在测试期间收集到的信息的访问方法。

4.定时器

JMeter线程在发送请求之间没有间歇,通过添加定时器,设定请求之间应该间隔的时间。

5.断言

可以使用断言来检查从服务器获得的响应内容。

6.配置元件

配置元件与采样器紧密关联。虽然配置元件并不发送请求,但可添加或修改请求。

7.前置处理器

会在采样器发出请求之前做一些操作。

8.后置处理器

会在采样器发出请求之后做一些操作。

JMeter执行顺序:配置元件=》前置处理器=》定时器=》采样器=》后置处理器=》断言=》监听器

七、辅助测试工具开发

下面的代码(工具:sqlexec)是一个用来向数据库(目前支持Oracle、MySQL)插入测试数据的工具。支持多线程,可插入千万级别测试数据。在后续压测中会用到该

工具,工具开发尽量简单,一个工具只完成一个任务,同时不要重复制造轮子。

[java] view plain copy

package d706;

/*

* sql处理

*/

public class Test_DB_Insert extends Thread{

public static String SQLTEXT = null;  // 待处理的sql语句

private InputStream ins = null;       // 用于读取配置文件

private Properties property = new Properties(); // 读取数据库配置文件

private String databaseType = null; // 数据库连接类型

private String driver = null;       // 数据库驱动

private String url = null;          // 数据库连接

private String uName = null;        // 数据库登录用户名

private String pwd = null;          // 数据库登录用户密码

private  int numOfTestRecords;      // 插入数据条数

private  Connection con = null;     // 连接数据库

private  PreparedStatement statement = null;  // 获取数据库操作对象

public  Test_DB_Insert(String sql){

SQLTEXT = sql;                  // sql语句以参数的形式,在构造实例的时候传入

}

private void init(){   // 初始化配置文件

try{

ins = new FileInputStream("./d706/dbconf.properties");

}catch(FileNotFoundException ffe){

ffe.printStackTrace();

}

try{

property.load(ins);   //

}catch(IOException ie){

ie.printStackTrace();

}

databaseType = property.getProperty("databasetype"); // 获取配置文件中设置的连接数据库类型

if(databaseType.toUpperCase().equals("MYSQL")){      // 判断连接数据库类型

driver = property.getProperty("driver_mysql");

url = property.getProperty("url_mysql");

uName = property.getProperty("db_userName_mysql"); // 连接数据库的用户信息;

pwd = property.getProperty("db_pwd_mysql");

}else if(databaseType.toLowerCase().equals("oracle")){ //

driver = property.getProperty("driver_oracle");

url = property.getProperty("url_oracle");

uName = property.getProperty("db_userName_oracle");

pwd = property.getProperty("db_pwd_oracle");

}

}

private  synchronized  void Insert_DB(){

try {

try {

Class.forName( driver );  // 注册驱动;

}catch(ClassNotFoundException cf){

cf.printStackTrace();

}

con = DriverManager.getConnection(url,uName, pwd); // 获取数据库连接

con.setAutoCommit(false);  // 关闭事务自动提交

SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss:SS");  // 记录执行时间

TimeZone t = sdf.getTimeZone();

t.setRawOffset(0);

sdf.setTimeZone(t);

Long startTime = System.currentTimeMillis();

System.out.println("插入数据操作开始...");

statement = con.prepareStatement(SQLTEXT); //创建数据库操作对象

/*

* "INSERT INTO TEST_DB(name,sex,nickname,test1,test2,test3,test4," +

"test5,test6,test7,test8,test9,test10,test11,test12,test13,test14," +

"test15,test16,test17,test18,test19,test20,test21,test22,test23," +

"test24,test25,test26,test27,test28,test29,test30,test31,test32," +

"test33,test34,test35,test36,test37,test38,test39,test40,test41," +

"test42) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?," +

"?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"

*/

numOfTestRecords = 1000;                               //插入的测试数据量;

for(int i = 0; i<numOfTestRecords; i++) {              //循环

statement.setString(i + 1, "DBTest-" + i);

//statement.setString(2, "" + i%2);          //0表示男 1表示女

statement.addBatch();  // 把一个SQL命令加入命令列表

//statement.executeUpdate(); //执行SQL;

}

statement.executeBatch(); //执行批量更新

con.commit();//语句执行完毕,提交事务

//int[] ref = statement.executeBatch();

//if(ref[numOfTestRecords-1] == 0){System.out.println("插入数据操作完成");} //

System.out.println("插入数据操作完成");

Long endTime = System.currentTimeMillis();

System.out.println("插入"+numOfTestRecords+"条数据,"+"用时(时:分:秒:毫秒)" +

sdf.format(new Date(endTime - startTime))); //

}catch(Exception e) {

System.out.println("异常: " + e.toString());

e.printStackTrace();

}finally{

if(statement != null){   // 关闭数据库操作对象

try{

statement.close();

}catch(SQLException se){

se.printStackTrace();

}

}

if(con != null){         // 关闭数据库连接

try{

if(con.isClosed()){con.close();}

}catch(SQLException se){

se.printStackTrace();

}

}

}

}

@Override

public void run() {    // 类外调用

Test_DB_Insert ti = new Test_DB_Insert(SQLTEXT); // 构造实例

ti.init();      // 初始化

ti.Insert_DB(); // 执行插入数据

}

//  public static void main(String[] args){

//

//          Test_DB_Insert ti = new Test_DB_Insert(SQLTEXT);

//          ti.init();  //初始化

//          ti.Insert_DB();  //执行插入数据

//  }

}

// 针对增删查改,可放到一个SQL处理类(Test_DB_crud)中,判断传入的SQL字符串,然后交给对应的方法去执行并在控制台输出结果。 在Test_DB_Control类

中只是new一个Test_DB_crud类,形成了Test_DB_crud对Test_DB_Control的依赖关系。

[java] view plain copy

package d706;

/*

*程序界面

*按钮事件

*/

public class Test_DB_gui extends JFrame implements ActionListener{

private static final long serialVersionUID = 1L;

public static String SQLTEXT = null; // 界面输入的sql文本

private JScrollPane js = null;

private JPanel jp1 = null;

private JTextArea ta = null;

private JButton jb = new JButton();

public Test_DB_gui(){

this.setTitle("sqlExecV1.0");

ta = new JTextArea();

ta.setText("");

js = new JScrollPane(ta);

jp1 = new JPanel();

jp1.setLayout(new BorderLayout());

jp1.add(js,BorderLayout.CENTER);

jb = new JButton();

jb.setText("执行");

jb.addActionListener(this); // 添加监听

jp1.add(jb,BorderLayout.SOUTH);

this.getContentPane().add(jp1);

this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

this.setBounds(400,200,700,500);

this.setVisible(true);

jb.addActionListener(new ActionListener(){  // 按钮事件

public void  actionPerformed(ActionEvent e) {  // 匿名内部类的形式实现按钮事件

SQLTEXT = ta.getText(); // 将要执行的SQL设置成静态的

Multi_process mp = new Multi_process(SQLTEXT); // 在构造实例时,传入sql

mp.run(); // 运行程序

//System.out.println( mp.SQLTEXT +"***"); // 控制台输出

}

});

}

public void  actionPerformed(ActionEvent e) {

// 实现 ActionListener 接口,需要实现的方法

}

public void run() {  // 运行方法

Test_DB_gui np =  new Test_DB_gui();

System.out.println( np.getTitle() );

}

}

[java] view plain copy

public class Multi_process {

/*

*多线程

*/

public static String SQLTEXT = null;  // 待处理的sql语句

public Multi_process(String sql){

SQLTEXT = sql;                    // sql语句以参数的形式,在构造实例的时候传入

}

public  void run(){

Test_DB_Insert  td1 = new Test_DB_Insert(SQLTEXT); // 创建实例

//     Test_DB_Insert  td2 = new Test_DB_Insert(SQLTEXT);

Thread t1 = td1;  // 创建线程

//         Thread t2 = td2;

t1.start(); // 启动线程

//         t2.start();

//     Test_DB_Insert t3 = new Test_DB_Insert();

//     Test_DB_Insert t4 = new Test_DB_Insert();

//     t3.run();

//     t4.run();

}

}

[java] view plain copy

public class Test_Exec {

/*

* 程序入口

*/

public static void main(String[] args){

Test_DB_gui np = new Test_DB_gui();

np.run();

}

}

在Linux下执行,需打成jar包,通过shell脚本执行。以下是sqlexec的startup.sh执行脚本.

[plain] view plain copy

#!/bin/sh

#

#Author: bruce

#Version: sqlExecv1.0

#Date:2013-11-20

#

read -p "please input jar file path:" jarpath

java -jar "$jarpath"

echo "running sqlExec."

注:

今天突然想到测试数据的一个问题。即造出来的数据应该更接近真实,而不是都一样。所以想利用配置文件的方式,在配置文件中写好SQL,程序读取执行配置文件并动态生成SQL,完成插入测试数据。这样就解决了测试数据不够真实,可能影响测试结果的问题。

时间: 2024-10-10 08:22:14

学习使用Jmeter做压力测试(一)--压力测试基本概念的相关文章

学习总结——JMeter做http接口压力测试

JMeter做http接口压力测试 测前准备 用JMeter做接口的压测非常方便,在压测之前我们需要考虑这几个方面: 场景设定 场景分单场景和混合场景.针对一个接口做压力测试就是单场景,针对一个流程做压力测试的时候就是混合场景,需要多个接口共同作用. 压测时间设定 通常时间设为10 – 15 分钟,如果涉及疲劳测试的话时间可根据实际情况设定,1周,一个月不等. 测试数据准备 如果需要测试的数据量很大的话,需要造数据,造数据可以JMeter操作数据库来完成,也可以用Python造数据. 结果查看

学习总结——JMeter做http接口功能测试

JMeter对各种类型接口的测试 默认做接口测试前,已经给出明确的接口文档(如,http://test.nnzhp.cn/wiki/index.php?doc-view-59):本地配好了JMeter 3.x的运行环境: 打开JMeter,添加一个线程组和该线程组的查看结果树.以下的几种接口请求我们都在这个线程组中添加和运行. 1. 普通的以key-value传参的get请求 e.g. 获取用户信息 添加http请求:填写服务器域名或IP:方法选GET:填写路径:添加参数:运行并查看结果. 2.

(转)学习使用Jmeter做压力测试(三)--数据库测试

数据库测试 JMeter可以做为Web服务器与浏览器之间的代理网关,以捕获浏览器的请求和Web服务器的响应,这样就可很容易的生成性能测试脚本. 根据脚本,JMeter可通过线程组来模拟真实用户对Web服务器做压力测试.本文描述使用JMeter进行数据库测试的过程.创建测试计划,模拟 并发用户发送SQL请求到数据库.测试数据库. 性能测试的目标是找到系统的性能瓶颈.本文将通过构造测试场景,完成对数据库的测试. 场景: 1.单用户: a.SQL语句优化:    b.数据库约束检查:   c.分页查询

【转】学习使用Jmeter做压力测试(三)--数据库测试

JMeter可以做为Web服务器与浏览器之间的代理网关,以捕获浏览器的请求和Web服务器的响应,这样就可很容易的生成性能测试脚本.根据脚本,JMeter可通过线程组来模拟真实用户对Web服务器做压力测试.本文描述使用JMeter进行数据库测试的过程.创建测试计划,模拟并发用户发送SQL请求到数据库.测试数据库. 性能测试的目标是找到系统的性能瓶颈.本文将通过构造测试场景,完成对数据库的测试. 场景: 1.单用户: a.SQL语句优化:    b.数据库约束检查:   c.分页查询: 2.并发用户

学习总结——JMeter做WebService接口功能测试

用JMeter作WebService接口功能测试(可以借助SoapUI来完成) SoapUI里面的操作: Wsdl文件或链接导入或添加到SoapUI打开待测请求:运行请求:取URL  SOAPAction .报文. JMeter里面的操作: 为线程组添加SOAP/XML-RPC Rquest ,右击线程组添加->Sampler->SOAP/XML-RPC Rquest: 把从SoapUI获取的信息粘贴到相应位置: 发送请求并查看结果.

如何用Jmeter做压力测试 --- 转

Jmeter是一个性能测试工具,同loadrunner类似,他功能较多,我们常用的功能是用jmeter模拟多浏览器对网站做压力测试.    下载jmeter地址 :http://jakarta.apache.org/我们一般的网站,在进入业务功能前先需登录,然后才能访问业务功能.下面介绍如何用jmeter登录系统再对主业务做压力测试.1 运行jmeter    2 左边树将出现测试计划.工作台两根节点.3 选择测试计划,按右键->添加->threads(users)线程组    线程组能设置以

压力测试与负载测试的区别

性能测试中负载测试,压力测试有什么区别 对于性能测试,负载测试,压力测试的区别,之前总自认为是清楚的,后来被人问住了,才发现还差的远.这儿网上摘了一些内容,加上自己的理解,算是弄清楚了吧.特此记下,避免忘了.如有错误之处,还望指正. 性能测试(或称多用户并发性能测试).负载测试.强度测试.容量测试是性能测试领域里的几个方面,但是概念很容易混淆. 下面将几个概念进行介绍. 性能测试(Performance Test):通常收集所有和测试有关的所有性能,被不同人在不同场合下进行使用. 关注点:how

性能测试,负载测试,压力测试有什么区别

转自:http://www.cnblogs.com/bugua/archive/2012/04/06/2434940.html,感谢分享! 性能测试(或称多用户并发性能测试).负载测试.强度测试.容量测试是性能测试领域里的几个方面,但是概念很容易混淆.下面将几个概念进行介绍. 性能测试(Performance Test):通常收集所有和测试有关的所有性能,通常被不同人在不同场合下进行使用. 关注点:how much和how fast 负载测试(Load Test):负载测试是一种性能测试,指数据

我所理解的性能测试中负载测试与压力测试的区别

性能测试中负载测试,压力测试有什么区别 对于性能测试,负载测试,压力测试的区别,之前总自认为是清楚的,后来被人问住了,才发现还差的远.这儿网上摘了一些内容,加上自己的理解,算是弄清楚了吧.特此记下,避免忘了.如有错误之处,还望指正. 性能测试(或称多用户并发性能测试).负载测试.强度测试.容量测试是性能测试领域里的几个方面,但是概念很容易混淆. 下面将几个概念进行介绍. 性能测试(Performance Test):通常收集所有和测试有关的所有性能,被不同人在不同场合下进行使用. 关注点:how