一、性能測試的概念
性能測試是通過自動化的測試工具模擬多種正常峰值及異常負(fù)載條件來對系統(tǒng)的各項性能指標(biāo)進(jìn)行測試。負(fù)載測試和壓力測試都屬于性能測試,兩者可以結(jié)合進(jìn)行。
通過負(fù)載測試,確定在各種工作負(fù)載下系統(tǒng)的性能,目標(biāo)是當(dāng)負(fù)載逐漸增加時,測試系統(tǒng)各項性能指標(biāo)的變化情況。壓力測試時通過確定一個系統(tǒng)的瓶頸或者不能接受的
性能點,來獲取系統(tǒng)能提供的大服務(wù)級別的測試。性能測試主要包括負(fù)載測試、強度測試、容量測試。
二、性能測試的指標(biāo)
web服務(wù)器:
Avg Rps: 平均每秒的響應(yīng)次數(shù) = 總請求數(shù) /秒數(shù);
Avg time to last byte per terstion(mstes): 平均每秒業(yè)務(wù)腳本的迭代次數(shù);
Successful Rounds: 成功的請求;
Failed Rounds: 失敗的請求;
Successful Hits: 成功的點擊次數(shù);
Failed Hits: 失敗的點擊次數(shù);
Hits Per Second: 每秒點擊次數(shù);
Successful Hits Per Second:每秒成功的點擊次數(shù);
Failed Hits Per Second: 每秒失敗的點擊次數(shù);
Attempted Connections: 嘗試連接數(shù);
Throughput: 吞吐率;
數(shù)據(jù)庫服務(wù)器:
User Connections: 用戶連接數(shù),也是數(shù)據(jù)庫的連接數(shù)量;
Number of Deadlocks: 數(shù)據(jù)庫死鎖;
Butter Cache Hit: 數(shù)據(jù)庫Cache 的命中情況;
三、性能測試的流程
1.明確性能測試需求;
2.制定性能測試方案;
2.1.測試范圍
2.2.入口標(biāo)準(zhǔn)
2.3.出口標(biāo)準(zhǔn)
2.4.測試策略(測試環(huán)境指標(biāo)、存量數(shù)據(jù)、業(yè)務(wù)場景、測試通過標(biāo)準(zhǔn)等)
2.5.測試風(fēng)險
2.6.測試資源
3.設(shè)計性能測試用例;
4.執(zhí)行性能測試用例;
5.分析性能測試結(jié)果;
6.生成性能測試報告;
四、性能測試的工具--JMeter
為什么是JMeter而不是LoadRunner呢 1.更少的投入,針對有限的測試成本; 2.開源工具的可定制性無可比擬; 3.通過社區(qū)得到大程度的支持。
JMeter是Apache組織開發(fā)的基于Java的壓力測試工具。初被設(shè)計用于web應(yīng)用的測試,后來擴(kuò)展到其他測試領(lǐng)域?捎糜跍y試靜態(tài)和動態(tài)資源,如文件、Java服務(wù)
程序、Java對象、數(shù)據(jù)庫等。JMeter能夠?qū)?yīng)用程序做功能/回歸測試,通過創(chuàng)建帶有斷言的腳本來驗證被測程序返回了期望的結(jié)果。而且為了保證大限度的靈活性,
JMeter允許使用正則表達(dá)式創(chuàng)建斷言。
五、JMeter的特性
1.支持對多種服務(wù)類型進(jìn)行測試;
2.支持通過錄制/回訪方式獲取測試腳本;
3.具備高可移植性,是純Java 程序;
4.采用多線程框架,允許通過多個線程并發(fā)取樣及通過獨立的線程組對不同的功能同時取樣;
5.精心設(shè)計的GUI支持高速用戶操作和精確計時;
6.支持緩存和離線的方式分析/回放測試結(jié)果;
7.高擴(kuò)展性;
六、JMeter常用測試元件
1.線程組
用來管理執(zhí)行性能測試所需的JMeter線程。
a.可以設(shè)置線程數(shù)量
b.設(shè)置線程啟動周期
c.設(shè)置執(zhí)行測試腳本的循環(huán)次數(shù)
2.控制器
JMeter有兩種類型的控制器:采樣器和邏輯控制器。
采樣器被用來向服務(wù)器發(fā)送請求。JMeter采樣器包含:FTP Request、HTTP Request、JDBC Request等。
邏輯控制器用來控制JMeter的測試邏輯,特別是何時發(fā)送請求。
3.監(jiān)聽器
監(jiān)聽器提供了對JMeter在測試期間收集到的信息的訪問方法。
4.定時器
JMeter線程在發(fā)送請求之間沒有間歇,通過添加定時器,設(shè)定請求之間應(yīng)該間隔的時間。
5.斷言
可以使用斷言來檢查從服務(wù)器獲得的響應(yīng)內(nèi)容。
6.配置元件
配置元件與采樣器緊密關(guān)聯(lián)。雖然配置元件并不發(fā)送請求,但可添加或修改請求。
7.前置處理器
會在采樣器發(fā)出請求之前做一些操作。
8.后置處理器
會在采樣器發(fā)出請求之后做一些操作。
JMeter執(zhí)行順序:配置元件=》前置處理器=》定時器=》采樣器=》后置處理器=》斷言=》監(jiān)聽器
七、輔助測試工具開發(fā)
下面的代碼(工具:sqlexec)是一個用來向數(shù)據(jù)庫(目前支持Oracle、MySQL)插入測試數(shù)據(jù)的工具。支持多線程,可插入千萬級別測試數(shù)據(jù)。在后續(xù)壓測中會用到該
工具,工具開發(fā)盡量簡單,一個工具只完成一個任務(wù),同時不要重復(fù)制造輪子。
[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(); // 讀取數(shù)據(jù)庫配置文件
private String databaseType = null; // 數(shù)據(jù)庫連接類型
private String driver = null; // 數(shù)據(jù)庫驅(qū)動
private String url = null; // 數(shù)據(jù)庫連接
private String uName = null; // 數(shù)據(jù)庫登錄用戶名
private String pwd = null; // 數(shù)據(jù)庫登錄用戶密碼
private int numOfTestRecords; // 插入數(shù)據(jù)條數(shù)
private Connection con = null; // 連接數(shù)據(jù)庫
private PreparedStatement statement = null; // 獲取數(shù)據(jù)庫操作對象
public Test_DB_Insert(String sql){
SQLTEXT = sql; // sql語句以參數(shù)的形式,在構(gòu)造實例的時候傳入
}
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"); // 獲取配置文件中設(shè)置的連接數(shù)據(jù)庫類型
if(databaseType.toUpperCase().equals("MYSQL")){ // 判斷連接數(shù)據(jù)庫類型
driver = property.getProperty("driver_mysql");
url = property.getProperty("url_mysql");
uName = property.getProperty("db_userName_mysql"); // 連接數(shù)據(jù)庫的用戶信息;
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 ); // 注冊驅(qū)動;
}catch(ClassNotFoundException cf){
cf.printStackTrace();
}
con = DriverManager.getConnection(url,uName, pwd); // 獲取數(shù)據(jù)庫連接
con.setAutoCommit(false); // 關(guān)閉事務(wù)自動提交
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss:SS"); // 記錄執(zhí)行時間
TimeZone t = sdf.getTimeZone();
t.setRawOffset(0);
sdf.setTimeZone(t);
Long startTime = System.currentTimeMillis();
System.out.println("插入數(shù)據(jù)操作開始...");
statement = con.prepareStatement(SQLTEXT); //創(chuàng)建數(shù)據(jù)庫操作對象
/*
* "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; //插入的測試數(shù)據(jù)量;
for(int i = 0; i<numOfTestRecords; i++) { //循環(huán)
statement.setString(i + 1, "DBTest-" + i);
//statement.setString(2, "" + i%2); //0表示男 1表示女
statement.addBatch(); // 把一個SQL命令加入命令列表
//statement.executeUpdate(); //執(zhí)行SQL;
}
statement.executeBatch(); //執(zhí)行批量更新
con.commit();//語句執(zhí)行完畢,提交事務(wù)
//int[] ref = statement.executeBatch();
//if(ref[numOfTestRecords-1] == 0){System.out.println("插入數(shù)據(jù)操作完成");} //
System.out.println("插入數(shù)據(jù)操作完成");
Long endTime = System.currentTimeMillis();
System.out.println("插入"+numOfTestRecords+"條數(shù)據(jù),"+"用時(時:分:秒:毫秒)" +
sdf.format(new Date(endTime - startTime))); //
}catch(Exception e) {
System.out.println("異常: " + e.toString());
e.printStackTrace();
}finally{
if(statement != null){ // 關(guān)閉數(shù)據(jù)庫操作對象
try{
statement.close();
}catch(SQLException se){
se.printStackTrace();
}
}
if(con != null){ // 關(guān)閉數(shù)據(jù)庫連接
try{
if(con.isClosed()){con.close();}
}catch(SQLException se){
se.printStackTrace();
}
}
}
}
@Override
public void run() { // 類外調(diào)用
Test_DB_Insert ti = new Test_DB_Insert(SQLTEXT); // 構(gòu)造實例
ti.init(); // 初始化
ti.Insert_DB(); // 執(zhí)行插入數(shù)據(jù)
}
// public static void main(String[] args){
//
// Test_DB_Insert ti = new Test_DB_Insert(SQLTEXT);
// ti.init(); //初始化
// ti.Insert_DB(); //執(zhí)行插入數(shù)據(jù)
// }
}
// 針對增刪查改,可放到一個SQL處理類(Test_DB_crud)中,判斷傳入的SQL字符串,然后交給對應(yīng)的方法去執(zhí)行并在控制臺輸出結(jié)果。 在Test_DB_Control類