常用壓力測試工具對比
?? ??? ?1、loadrunner
?? ??? ??? ?性能穩定,壓測結果及細粒度大,可以自定義腳本進行壓測,但是太過于重大,功能比較繁多
?? ??? ?2、apache ab(單接口壓測最方便)
?? ??? ??? ?模擬多線程并發請求,ab命令對發出負載的計算機要求很低,既不會占用很多CPU,也不會占用太多的內存,但卻會給目標服務器造成巨大的負載, 簡單DDOS攻擊等
?? ??? ?3、webbench
?? ??? ??? ?webbench首先fork出多個子進程,每個子進程都循環做web訪問測試。子進程把訪問的結果通過pipe告訴父進程,父進程做最終的統計結果。
Jmeter基本介紹和使用場景
?? ?簡介
?? ??? ?1、壓測不同的協議和應用
?? ??? ??? ?1) Web - HTTP, HTTPS (Java, NodeJS, PHP, ASP.NET, …)
?? ??? ??? ?2) SOAP / REST Webservices
?? ??? ??? ?3) FTP
?? ??? ??? ?4) Database via JDBC
?? ??? ??? ?5) LDAP ?輕量目錄訪問協議
?? ??? ??? ?6) Message-oriented middleware (MOM) via JMS
?? ??? ??? ?7) Mail - SMTP(S), POP3(S) and IMAP(S)
?? ??? ??? ?8) TCP等等
?? ??? ?2、使用場景及優點
?? ??? ??? ?1)功能測試
?? ??? ??? ?2)壓力測試
?? ??? ??? ?3)分布式壓力測試
?? ??? ??? ?4)純java開發
?? ??? ??? ?5)上手容易,高性能
?? ??? ??? ?4)提供測試數據分析
?? ??? ??? ?5)各種報表數據圖形展示
本地快速安裝Jmeter4.x
apache jmeter是100%的java桌面應用程序,它被設計用來加載被測試軟件功能特性、度量被測試軟件的性能。設計jmeter的初衷是測試web應用,后來又擴充了其它的功能。jmeter可以完成針對靜態資源和動態資源(servlets, perl腳本, java對象, 數據查詢s, ftp服務等)的性能測試。 jmeter可以模擬大量的服務器負載、網絡負載、軟件對象負載,通過不同的加載類型全面測試軟件的性能。并且jmeter提供圖形化的性能分析。
?? ? 1、需要安裝JDK8。或者JDK9,JDK10
?? ? 2、快速下載
?? ? ?? ?windows: http://mirrors.tuna.tsinghua.edu.cn/apache//jmeter/binaries/apache-jmeter-4.0.zip
?? ? ?? ?mac或者linux:http://mirrors.tuna.tsinghua.edu.cn/apache//jmeter/binaries/apache-jmeter-4.0.tgz
?? ? 3、文檔地址:http://jmeter.apache.org/usermanual/get-started.html
?? ? 4、建議安裝JDK環境,雖然JRE也可以,但是壓測https需要JDK里面的 keytool工具
?? ?
Jmeter目錄文件講解
?? ?1、目錄
?? ??? ?bin:核心可執行文件,包含配置
?? ??? ??? ?jmeter.bat: windows啟動文件:
?? ??? ??? ?jmeter: mac或者linux啟動文件:
?? ??? ??? ?jmeter-server:mac或者Liunx分布式壓測使用的啟動文件
?? ??? ??? ?jmeter-server.bat:mac或者Liunx分布式壓測使用的啟動文件
?? ??? ??? ?jmeter.properties: 核心配置文件
?? ??? ??? ?
?? ??? ?extras:插件拓展的包
?? ??? ?lib:核心的依賴包
?? ??? ??? ?ext:核心包
?? ??? ??? ?junit:單元測試包
Jmeter語言版本中英文切換
?? ?簡介:
?? ??? ?講解怎么改變jmeter的GUI界面語言版本
?? ??? ?1、控制臺修改
?? ??? ??? ?menu -> options -> choose language
?? ??? ?2、配置文件修改?? ?
?? ??? ??? ?bin目錄 -> jmeter.properties
?? ??? ??? ??? ?默認 #language=en
?? ??? ??? ??? ?改為 language=zh_CN?
使用SpringBoot 2.0快速編寫API測試接口
我們在這里面編寫具體方法:
@Controller
@RequestMapping("/alpha")
public class AlphaController{...具體方法...}
1、模擬GET請求
//get// /students?current=1&limit=20@RequestMapping(path="/students",method= RequestMethod.GET)@ResponseBodypublic String getStudents(@RequestParam(name="current",required = false,defaultValue = "1") int current,@RequestParam(name="limit",required = false,defaultValue = "10") int limit){System.out.println(current);System.out.println(limit);return "some students";}
?2、模擬POST請求
//post@RequestMapping(path = "/student",method = RequestMethod.POST)@ResponseBodypublic String saveStudent(String name,int age){System.out.println(name);System.out.println(age);return "success";}
我們的訪問路徑就是localhost:8080/alpha/...
當然,為了大家測試方便,我已經部署到服務器上,地址是:
http://139.9.83.25/alpha/方法的path
我把整個alpha的接口都放來,你們可以照著寫或者按照路徑直接訪問服務器上的進行測試。
package com.now.community.community.controller;/*
*
* http
* get請求
* post請求
* 響應ModelAndView
* 響應(簡單)
* json
*
* */
import ch.qos.logback.core.net.SyslogOutputStream;
import com.now.community.community.service.AlphaService;
import com.now.community.community.util.CommunityUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.*;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.*;@Controller
@RequestMapping("/alpha")
public class AlphaController {@Autowiredprivate AlphaService AlphaService;@RequestMapping("/hello")@ResponseBodypublic String sayHello(){return "sfadsfafafadf";}@RequestMapping("/data")@ResponseBodypublic String getData(){return AlphaService.find();}@RequestMapping("/http")public void http(HttpServletRequest request, HttpServletResponse response){System.out.println(request.getMethod());System.out.println(request.getServletPath());Enumeration<String> enumeration=request.getHeaderNames();while(enumeration.hasMoreElements()){String name=enumeration.nextElement();String value=request.getHeader(name);System.out.println(name+":"+value);}System.out.println(request.getParameter("code"));response.setContentType("text/html;charset=utf-8");try(PrintWriter writer = response.getWriter()){writer.write("<h1>橙白站</h1>");} catch (IOException e) {e.printStackTrace();}}//get// /students?current=1&limit=20@RequestMapping(path="/students",method= RequestMethod.GET)@ResponseBodypublic String getStudents(@RequestParam(name="current",required = false,defaultValue = "1") int current,@RequestParam(name="limit",required = false,defaultValue = "10") int limit){System.out.println(current);System.out.println(limit);return "some students";}// /student/123@RequestMapping(path="/student/{id}",method= RequestMethod.GET)@ResponseBodypublic String getStudent(@PathVariable("id") int id){System.out.println(id);return "a student";}//post@RequestMapping(path = "/student",method = RequestMethod.POST)@ResponseBodypublic String saveStudent(String name,int age){System.out.println(name);System.out.println(age);return "success";}//響應html數據@RequestMapping(path = "/teacher",method = RequestMethod.GET)public ModelAndView getTeacher(){ModelAndView mav=new ModelAndView();mav.addObject("name","張三");mav.addObject("age",30);mav.setViewName("/demo/view");return mav;}//返回模板的地址@RequestMapping(path = "/school",method = RequestMethod.GET)public String getSchool(Model model){model.addAttribute("name","北大");model.addAttribute("age","100");return "/demo/view";}//響應json數據(異步請求中)//java對象->json字符串-> js對象@RequestMapping(path = "/emp",method = RequestMethod.GET)@ResponseBodypublic Map<String,Object> getEmp(){Map<String,Object> emp=new HashMap<>();emp.put("name","張三");emp.put("age",23);emp.put("salary",8000.00);return emp;}@RequestMapping(path = "/emps",method = RequestMethod.GET)@ResponseBodypublic List<Map<String,Object>> getEmps(){List<Map<String,Object>> list=new ArrayList<>();Map<String,Object>emp=new HashMap<>();emp.put("name","張三");emp.put("age",23);emp.put("salary",8000.00);list.add(emp);emp=new HashMap<>();emp.put("name","李四");emp.put("age",24);emp.put("salary",9000.00);list.add(emp);return list;}@RequestMapping(path = "/cookie/set",method = RequestMethod.GET)@ResponseBodypublic String setCookie(HttpServletResponse httpServletResponse){Cookie cookie=new Cookie("code", CommunityUtil.generateUUID());cookie.setPath("/community/alpha");cookie.setMaxAge(60*10);httpServletResponse.addCookie(cookie);return "set cookie";}@RequestMapping(path = "/cookie/get",method = RequestMethod.GET)@ResponseBodypublic String getCookie(@CookieValue("code") String code){System.out.println(code);return "get cookie";}@RequestMapping(path = "/session/set", method = RequestMethod.GET)@ResponseBodypublic String setSession(HttpSession session){session.setAttribute("id",1);session.setAttribute("name","Test");return "set session";}@RequestMapping(path = "/session/get", method = RequestMethod.GET)@ResponseBodypublic String getSession(HttpSession session) {System.out.println(session.getAttribute("id"));System.out.println(session.getAttribute("name"));return "get session";}@RequestMapping(path = "/ajax",method = RequestMethod.POST)@ResponseBodypublic String testAjax(String name,int age){System.out.println(name);System.out.println(age);return CommunityUtil.getJSONString(0,"操作成功");}
}
?
創建Jmeter測試計劃,快速壓測一個接口
?
我們首先創建一個測試計劃
設置測試計劃
我們可以創建一個http請求
設置參數
解釋一下:
協議要寫
服務器名稱或者ip要寫,你可以訪問我的服務器139.9.83.25,也可以訪問本地localhost前提是你自己啟動了項目。
端口號本地tomcat默認8080,如果訪問我的服務器不用寫,我設置了。
方法:測什么類型就選什么類型。
路徑:具體接口的路徑
其他的以后再解釋。
?
好,我們run就算測完了,但是看不到結果,所以繼續操作
添加結果樹
查看結果樹
這是我上面的接口里寫的
Jmeter基礎功能組件介紹線程組和Sampler
?? ??? ?
?? ??? ?1、添加->threads->線程組(控制總體并發)
?? ??? ??? ?線程數:虛擬用戶數。一個虛擬用戶占用一個進程或線程
?? ??? ??? ?
?? ??? ??? ?準備時長(Ramp-Up Period(in seconds)):全部線程啟動的時長,比如100個線程,20秒,則表示20秒內100個線程都要啟動完成,每秒啟動5個線程
?? ??? ??? ?
?? ??? ??? ?循環次數:每個線程發送的次數,假如值為5,100個線程,則會發送500次請求,可以勾選永遠循環
? ? ? ? ? ? 調度器:循環次數永遠之后,可以配置多長時間結束;也可以配置啟動延遲
?? ??? ??? ?
?? ??? ?2、線程組->添加-> Sampler(采樣器) -> Http (一個線程組下面可以增加幾個Sampler)
?? ??? ??? ?名稱:采樣器名稱
?? ??? ??? ?注釋:對這個采樣器的描述
?? ??? ??? ?web服務器:
?? ??? ??? ??? ?默認協議是http
?? ??? ??? ??? ?默認端口是80
?? ??? ??? ??? ?服務器名稱或IP :請求的目標服務器名稱或IP地址
?? ??? ??? ?路徑:服務器URL
?? ??? ??? ?Use multipart/from-data for HTTP POST :當發送POST請求時,使用Use multipart/from-data方法發送,默認不選中。
?? ??? ?3、查看測試結果
?? ??? ??? ?線程組->添加->監聽器->察看結果樹
Jmeter的斷言基本使用
?
?? ??? ?1、增加斷言: 線程組 -> 添加 -> 斷言 -> 響應斷言 ?
?? ??? ??? ?apply to(應用范圍):
?? ??? ??? ??? ?Main sample only: 僅當前父取樣器 進行斷言,一般一個請求,如果發一個請求會觸發多個,則就有sub sample(比較少用)
?? ??? ??? ?要測試的響應字段:
?? ??? ??? ??? ?響應文本:即響應的數據,比如json等文本
?? ??? ??? ??? ?響應代碼:http的響應狀態碼,比如200,302,404這些
?? ??? ??? ??? ?響應信息:http響應代碼對應的響應信息,例如:OK, Found
?? ??? ??? ??? ?Response Header: 響應頭
?? ??? ??? ?模式匹配規則:
?? ??? ??? ??? ?包括:包含在里面就成功
?? ??? ??? ??? ?匹配:響應內容完全匹配,不區分大小寫
?? ??? ??? ??? ?equals:完全匹配,區分大小寫
舉個例子:
測這個地址:http://139.9.83.25/alpha/emps大家可以先去看一下結果
我們設置一下斷言
run后去看結果樹,肯定是成功的。
但是你改成“相等”,肯定是失敗的
?
?? ??? ??? ??? ?
?? ??? ?2、斷言結果監聽器: 線程組-> 添加 -> 監聽器 -> 斷言結果
?? ??? ??? ?里面的內容是sampler采樣器的名稱
?? ??? ??? ?斷言失敗,查看結果樹任務結果顏色標紅(通過結果數里面雙擊不通過的記錄,可以看到錯誤信息)
點開這個請求可以查看斷言詳情
?? ??? ?3、每個sample下面可以加單獨的結果樹,然后同時加多個斷言,最外層可以加個結果樹進行匯總
壓測結果聚合報告分析
?? ?新增聚合報告:線程組->添加->監聽器->聚合報告(Aggregate Report)
?? ??? ??? ?lable: sampler的名稱
?? ??? ??? ?Samples: 一共發出去多少請求,例如10個用戶,循環10次,則是 100
?? ??? ??? ?Average: 平均響應時間
?? ??? ??? ?Median: 中位數,也就是 50% 用戶的響應時間
?? ??? ??? ?90% Line : 90% 用戶的響應不會超過該時間 (90% of the samples took no more than this time. The remaining samples at least as long as this)
?? ??? ??? ?95% Line : 95% 用戶的響應不會超過該時間
?? ??? ??? ?99% Line : 99% 用戶的響應不會超過該時間
?? ??? ??? ?min : 最小響應時間
?? ??? ??? ?max : 最大響應時間
?? ??? ??? ?Error%:錯誤的請求的數量/請求的總數
?? ??? ??? ?Throughput: 吞吐量——默認情況下表示每秒完成的請求數(Request per Second) 可類比為qps
?? ??? ??? ?KB/Sec: 每秒接收數據量
實戰:你可以把路徑換成/,我設置了弄到主頁,主頁是這個樣子:
這時,有些參數就可以設置了:
線程數請不要超過100,要不然就把我的小服務器搞炸了。。。
然后run之后可以查看結果了:
你可以多測幾次,因為可能恰巧系統在垃圾回收,或者在清緩存等等,多測幾次比較準。
?
Jmeter用戶自定義變量實戰
?
?? ??? ?為什么使用:很多變量在全局中都有使用,或者測試數據更改,可以在一處定義,四處使用
?? ??? ?比如服務器地址
?? ??? ?1、線程組->add -> Config Element(配置原件)-> User Definde Variable(用戶定義的變量)
?? ??? ?
?? ??? ?2、引用方式${XXX},在接口中變量中使用
建議:每個線程組定義自己的。測試計劃定義總的參數,比如網址。
?? ??? ?3、原始查看結果樹和非原生查看(基礎按鈕)
實戰之CSV可變參數壓測
?? ??? ?實戰操作jmeter讀取CSV和Txt文本文件里面的參數進行壓測
?? ??? ?1、線程組->add -> Config Element(配置原件)-> CSV data set config (CSV數據文件設置)
參數太容易理解,不解釋了。
我們可以讀取文件的數據進行壓測,比如:
一行一個請求,很容易理解吧。
你會發現user是變化的。
介紹多參數分隔符:
你可以設置分隔符:
然后就可以使用了:
唉。。。截圖截到現在。。實在是不i想截了,數據庫我就不截圖了。。。只有步驟和參數解釋
JDBC request壓測Mysql
講解jdbc壓測mysql相關準備工作,jar包添加,配置講解
?? ??? ?1、Thread Group -> add -> sampler -> jdbc request
?? ??? ?2、jar包添加 ?mysql-connector-java-5.1.30.jar?
?? ??? ?3、JDBC connection Configuration 配置
?? ??? ??? ?1、JDBC request->add -> config element -> JDBC connection configuration
?? ??? ??? ??? ?核心配置
?? ??? ??? ??? ??? ?Max Number of connections : 最大連接數
?? ??? ??? ??? ??? ?MAX wait :最大等待時間
?? ??? ??? ??? ??? ?Auto Commit: 是否自動提交事務
?? ??? ??? ??? ??? ?DataBase URL : 數據庫連接地址 jdbc:mysql://127.0.0.1:3306/blog
?? ??? ??? ??? ??? ?JDBC Driver Class : 數據庫驅動,選擇對應的mysql
?? ??? ??? ??? ??? ?username:數據庫用戶名
?? ??? ??? ??? ??? ?password:數據庫密碼
JDBC request壓測Mysql, select
?? ?使用jmeter壓測mysql,select,insert語句
?? ??? ?1、Debug Sampler使用(結果樹中查看)
?? ??? ??? ?Thread Group -> add -> sampler -> debug sampler
?? ??? ??? ?
?? ??? ?2、參數講解:(sql結尾不要加";")
?? ??? ??? ?1、variable name of pool declared in JDBC connection configuration(和配置文件同名)
?? ??? ??? ?2、Query Type 查詢類型
?? ??? ??? ?3、parameter values 參數值
?? ??? ??? ?4、parameter types ?參數類型
?? ??? ??? ?5、variable names ?sql執行結果變量名
?? ??? ??? ?6、result variable names 所有結果當做一個對象存儲
?? ??? ??? ?7、query timeouts ?查詢超時時間?
?? ??? ??? ?8、 handle results ?處理結果集
定時器
定時器幫助我們模擬更真實的場景
隨機多長時間,這也比較符合我們真實的場景,肯定是有不確定間隔的請求過來。
其他的定時器做到見文知意。
執行順序
如果在同一個作用域范圍內有多個同一類型的元件,則這些元件按照它們在測試計劃中的上下順序依次執行
作用域
?固定定時器:http1
?循環控制器:http2、http3、圖形結果、隨機控制器
?圖形結果:http2、http3
?響應斷言:jdbc
?聚合報告:所有
?
for-each控制器
目的:遍歷變量值
一般和用戶自定義變量(User Defined Variables)一起使用,其在用戶自定義變量中讀取一系列相關的變量。每一個線程下執行時該控制器下的采樣器或控制器都會被執行一次或多次(次數和用戶變量有關)。輸入應包括的幾個變量,每個變量由變量名、下劃線、和數字組成,每個變量必須有一個值
switch控制器
該控制器可以控制里面的請求根據某些規則,在不同數字時選擇控制器下的對應數字位的不同sampler。一般可以用在模擬多線程同時操作不同請求的測試場景。
Switch Value:設置你要訪問的請求索引(從0開始的)或者直接設置訪問的請求名稱。數字對應的就是每個請求的順序(注意點:第一個是0),這個num可以通過計數器,counter函數,random函數來自動生成(一般常用random函數)。
if控制器
目的:判斷條件,可以引用變量。當為 true 時,執行相應的操作
Interpret Condition as Variable Expression?
如果選擇了此項,則條件必須是一個表達式,該表達式計算為“true”。
Evaluate for all children
注意事項:在if邏輯控制器的Expression中不能直接填寫條件表達式,需要借助函數將條件表達式計算為true/false,可以借助的函數有__jexl3和__groovy函數
如果是字符串的比較,需要加””
"${url}"=="baidu"
表達式支持:
循環控制器
目的:循環該控制器下面字節點的次數。
線程組里循環次數設置了n次,循環控制器下的循環次數也設置了m次,則該控制器下的請求運行的次數是(n*m)次。
分布式壓測介紹
?? ??? ?
?? ??? ? 普通壓測:單臺機可以對目標機器產生的壓力比較小,受限因素包括CPU,網絡,IO等
?? ??? ? 分布式壓測:利用多臺機器向目標機器產生壓力,模擬幾萬用戶并發訪問
分布式壓測原理
?? ??? ?1、總控機器的節點master,其他產生壓力的機器叫“肉雞” server
?? ??? ?2、master會把壓測腳本發送到 server上面
? ? ? ? 3、執行的時候,server上只需要把jmeter-server打開就可以了,不用啟動jmeter
?? ??? ?4、結束后,server會把壓測數據回傳給master,然后master匯總輸出報告
?? ??? ?5、配置詳情
實戰下次再更