前言:
繼上一篇,使用java生成jmx文件,並運行,但是我們發現,傳遞的數據只有一個請求體,也就是當不設置循環調用的時候,該接口之調用一次那么有沒有什么辦法,我們根據請求體傳遞的參數不同來調用多次?
當然有了,我們使用jmeter自帶的導入csv文件功能,我們將每次請求的不同的請求體放入到csv文件中
創建csv文件
文件內容如下圖所示:

我們使用java代碼生成。
package com.yiyang.myfirstspringdemo.utils;
import com.yiyang.myfirstspringdemo.model.Passenger;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.io.*;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import static com.yiyang.myfirstspringdemo.service.JemterTest.JMETER_ENCODING;
import static com.yiyang.myfirstspringdemo.service.JemterTest.NUMBER_THREADS;
/**
* @Author 劉翊揚
* @Date 2020/10/11 3:12 下午
* @Version 1.0
*/
@Slf4j
@Component
public class CVSUtils {
static String format = "\"%s\"";
/**
* 生成為CVS文件
*
* @param exportData 源數據List
* @param outPutPath 文件路徑
* @param fileName 文件名稱
* @return
*/
private static File createCSVFile(List<List<String>> exportData, String outPutPath, String fileName) {
File csvFile = null;
BufferedWriter csvFileOutputStream = null;
try {
File file = new File(outPutPath);
if (!file.exists()) {
if (file.mkdirs()) {
log.info("創建cvs文件成功");
} else {
log.error("創建cvs文件失敗");
}
}
// 定義文件名格式並創建
csvFile = File.createTempFile(fileName, ".csv", new File(outPutPath));
csvFileOutputStream = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(csvFile)));
for (List<String> exportDatum : exportData) {
writeRow(exportDatum, csvFileOutputStream);
csvFileOutputStream.newLine();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (csvFileOutputStream != null) {
try {
csvFileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
// 如果臨時文件,不需要在使用完成之后,記得刪除。我這里就先不刪除了。
// csvFile.delete();
// csvFile.deleteOnExit();
}
return csvFile;
}
/**
* 寫一行數據
* @param row 數據列表
* @param csvWriter
* @throws IOException
*/
private static void writeRow(List<String> row, BufferedWriter csvWriter) throws IOException {
for (String data : row) {
csvWriter.write(new String(data.getBytes(JMETER_ENCODING), JMETER_ENCODING));
// csvWriter.write(data);
}
}
/***
* 將json弄成csv文件可以識別的,保證這個請求體(json格式)在同一列
* @param object
* @return
* @throws IllegalAccessException
*/
private static String cvsFormat(Object object) throws IllegalAccessException {
StringBuilder sb = new StringBuilder();
sb.append(String.format(format, "{"));
Class<?> aClass = object.getClass();
Field[] declaredFields = aClass.getDeclaredFields();
int i = 0;
for(Field field : declaredFields) {
field.setAccessible(true);
Object value = field.get(object);
if (value == null) {
continue;
}
String name = field.getName();
sb.append(String.format(format, name));
sb.append(String.format(format, ":"));
sb.append(String.format(format, value));
if (i < declaredFields.length - 1) {
sb.append(String.format(format, ","));
}
i++;
}
sb.append(String.format(format, "}"));
return sb.toString();
}
public static File getCsvPath () {
String outFile = "/Users/liufei/Downloads/jmter";
String filename = "my_replay_data";
List<List<String>> listList = new ArrayList<List<String>>();
List<String> list = null;
list = new ArrayList<>();//一個List為一行
// 標題
list.add("body");
listList.add(list);
// 內容
int count = NUMBER_THREADS;
for (int i = 0; i < count; i++){
List<String> colList = new ArrayList<>();
Passenger passenger = new Passenger();
String uuId = UUID.randomUUID().toString().substring(0, 8);
passenger.setName("劉翊揚_" + uuId);
passenger.setPassword("123456_" + uuId);
String json = null;
try {
json = cvsFormat(passenger);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
colList.add(json);
listList.add(colList);
}
return createCSVFile(listList, outFile, filename);
}
/***
* 獲取文件的行數
* @param file
* @return
* @throws IOException
*/
public static int getTotalLines(File file) throws IOException {
long startTime = System.currentTimeMillis();
FileReader in = new FileReader(file);
LineNumberReader reader = new LineNumberReader(in);
reader.skip(Long.MAX_VALUE);
int lines = reader.getLineNumber();
reader.close();
long endTime = System.currentTimeMillis();
System.out.println("統計文件行數運行時間: " + (endTime - startTime) + "ms");
return lines;
}
}
注意:因為請求體中含有中文,所以為了避免亂碼:需要保持編碼一樣(樓主使用的是UTF-8)
- 首先在寫入csv文件設置編碼。
- jemter解析csv文件時,需要設置編碼
- http發送請求時,需要設置內容編碼
以上三個保持一致就不會出現亂碼了。
詳細內容:請查看這篇文章
生成的csv,在jmx中指定需要導入csv文件的路徑
我們修改JmterTest.java類中的run方法。(在上一篇文章中)
private static void run() {
String url = "localhost";
String port = "8088";
String api = "/mongo/insert";
/** 由於csv文件的請求體列中標題是body */
String request = "${body}";
String jemterHome = "/Users/liufei/Downloads/apache-jmeter-5.3";
JMeterUtils.setJMeterHome(jemterHome);
JMeterUtils.loadJMeterProperties(JMeterUtils.getJMeterBinDir() + "/jmeter.properties");
//JMeterUtils.initLocale();
// 獲取TestPlan
TestPlan testPlan = getTestPlan();
// 獲取設置循環控制器
LoopController loopController = getLoopController();
// 獲取Http請求信息
HTTPSamplerProxy httpSamplerProxy = getHttpSamplerProxy(url, port, api, request);
// 獲取結果:如匯總報告、察看結果樹
List<ResultCollector> resultCollector = getResultCollector(replayLogPath);
// 獲取CVSData設置
/** === 主要是這里的變化 ====== */
File cvsPath = CVSUtils.getCsvPath();
int number = 0;
try {
// 去掉body標題列
number = CVSUtils.getTotalLines(cvsPath) - 1;
} catch (IOException e) {
e.printStackTrace();
}
CSVDataSet csvDataSet = getCSVDataSet(cvsPath.getAbsolutePath());
// 獲取線程組
ThreadGroup threadGroup = getThreadGroup(loopController, number);
// 獲取設置吞吐量
ConstantThroughputTimer constantThroughputTimer = getConstantThroughputTimer(20);
// 獲取請求頭信息
HeaderManager headerManager = getHeaderManager();
HashTree fourHashTree = new HashTree();
resultCollector.stream().forEach(item -> fourHashTree.add(item));
fourHashTree.add(headerManager);
/** 將csvDataSet添加進去 */
fourHashTree.add(csvDataSet);
HashTree thirdHashTree = new HashTree();
// 注意:設置吞吐量需要和Http請求同一級,否則無效
thirdHashTree.add(constantThroughputTimer);
thirdHashTree.add(httpSamplerProxy, fourHashTree);
HashTree secondHashTree = new HashTree();
secondHashTree.add(threadGroup, thirdHashTree);
HashTree firstTreeTestPlan = new HashTree();
firstTreeTestPlan.add(testPlan, secondHashTree);
try {
SaveService.saveTree(firstTreeTestPlan, new FileOutputStream(jmxPath));
} catch (IOException e) {
e.printStackTrace();
}
// 第一種方式:運行
StandardJMeterEngine jMeterEngine = new StandardJMeterEngine();
jMeterEngine.configure(firstTreeTestPlan);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
jMeterEngine.run();
System.out.println("運行成功!!!");
// 使用命令
/* String command = JMeterUtils.getJMeterBinDir() + "/jmeter -n -t " + jmxPath + " -l /Users/liufei/Downloads/jmter/replay_result.jtl";
Runtime.getRuntime().exec(command);
System.out.println(command);*/
}
主要修改的有三個地方:
第7行和、第29 ~ 38行、52行
