JMeter-SSE响应数据自动化2.0
结构图
背景
对之前的脚本进行优化
主要是做一些兼容性处理,降低耦合度
优化内容
1.csv文件变量
增加了白名单变量,目标值变量,必须参数变量
2.响应信息获取方式更新
之前是用正则来捕获简单的json格式的响应,响应的格式是固定的。此次更新后使用的是String的spilt()方法来进行信息截取,易读性相较于正则表达式稍好一些
3.白名单变量初始化
只在线程组的第一次HTTP请求的预处理程序中更新
4.降低JSR223后置处理脚本的耦合度
便于后续维护或者更新,兼容性更好
实现思路
只写了一些更新的结构
1.While控制器
来控制从csv数据中读取数据
2.csv数据文件设置
更新了一些新添加的变量
3.JSR223预处理程序
根据csv文件的数据来更新一些HTTP请求中的参数,比如文件、入参等…
import org.json.JSONObject;
import org.json.JSONArray;
import java.util.ArrayList;//判断是否需要 files 参数
private Boolean needFiles(String str) {if("1".equals(str)) {return true;}else {return false;}
}//设置 files 参数
private void setFiles(String fileParams) {try{String[] fileList = fileParams.split(";");// 创建 JSON 对象数组(List<Map> 格式)JSONArray fileParamsArray = new JSONArray();for(String s : fileList) {fileParamsArray.put(setOneFileParam(s));}// 存入 vars(JSON 字符串)vars.put("fileParam", fileParamsArray.toString());} catch (Exception e) {log.error("设置 files 参数 失败!", e);prev.setSuccessful(false);}
}//设置 一个 files 参数
private JSONObject setOneFileParam(String fileParam) {try {//将fileParams转为json格式JSONObject jsonResponse = new JSONObject(fileParam);// 提取各个参数String filexxx= jsonResponse.optString("filexxx", "");String filexxx= jsonResponse.optString("filexxx", "");Integer filexxx= jsonResponse.optInt("filexxx", 0); // Integer类型String filexxx= jsonResponse.optString("filexxx", "");String filexxx= jsonResponse.optString("filexxx", "");JSONObject fileObj = new JSONObject();fileObj.put("filexxx", filename);fileObj.put("filexxx", fileHash);fileObj.put("filexxx", filesize);fileObj.put("filexxx", extension);fileObj.put("filexxx", mimeType);return fileObj;}catch (Exception e) {log.error("setOneFileParam函数设置单个 file 参数 失败!", e);prev.setSuccessful(false);}}//初始化白名单
private void initWhite() {try {String white = vars.get("white");if(white != null && !"".equals(vars.get("white"))) {String list = vars.get("white");String[] ret = list.split(";");Set<String> whiteSet = new HashSet<String>();for(String x:ret) {whiteSet.add(x);}vars.putObject("whiteSet",whiteSet);}// 使用后重置白名单变量,避免影响后续vars.put("white","");}catch (Exception e) {log.error("初始化白名单失败!");}
}try {String str = vars.get("needFiles"); String fileParams = vars.get("file");if(needFiles(str)){//需要文件参数setFiles(fileParams);}else{//不需要文件参数,设置为空JSONArray fileParamsArray = new JSONArray();vars.put("fileParam",fileParamsArray.toString()); }//初始化白名单initWhite();} catch (Exception e) {log.error("判断是否需要文件参数 失败!", e);prev.setSuccessful(false);
}
4.JSR223后置处理脚本
主要处理响应的信息来进行断言操作
import org.apache.jmeter.samplers.SampleResult;
import org.json.JSONObject;
import org.json.JSONException;// 每次脚本执行前需要重置的变量
private void init(){// 添加APPID信息vars.put("APPID",vars.get("appId"));// 每次重置isExist的值,避免上次结果影响本次vars.put("isExist", "true");// 每次重置断言输出信息vars.put("response_type","");vars.put("error_msg", "");vars.put("actual_msg", "");
}// 判断是否是流式响应
private Boolean isStreamingResponse(String response) {return response.contains("data: {");
}// 非流式响应处理
private void handleNonStreamingResponse(String response, SampleResult prev) throws Exception {JSONObject jsonResponse = new JSONObject(response);String msg = jsonResponse.get("msg");if ("不存在".equals(msg)) {setAssertMsg("不存在",msg);} else {setAssertMsg("非流式响应,未知错误",msg);}
}// 处理SSE响应数据,以data: 为分隔符,输出结果到String[] 中
private String[] splitResponse(String resp) {try {return resp.split("data: ");}catch(Exception e) {log.error("拆分resp为String数组失败!" + vars.get("appName"));}
}// 获取event信息
private String getEvent(String resp) {try {String ret = resp.substring(resp.indexOf("\"", 8)+1, resp.indexOf("\"", 12));return ret;}catch(Exception e) {log.error("获取event的字段内容失败!");return "Unknow";}
}// 处理error响应
private void errorResp(String resp) {try{JSONObject jsonResponse = new JSONObject(resp);String errorMsg = jsonResponse.get("message");setAssertMsg("执行失败",errorMsg);}catch(Exception e) {log.error("处理 error 响应信息失败");}
}// 判断有无特定target
private Boolean hasTarget(String resp) {String targetStr = vars.get("target");// 每次执行后重置target,避免影响下次target的值vars.put("target", "");return resp.contains(targetStr);
}// 断言参数设置
private void setAssertMsg(String response_type,String error_msg){vars.put("response_type",response_type);vars.put("error_msg", error_msg);vars.put("isExist", "false");
}
private void setAssertMsg(String resp){//获取answer中的信息,实际msgJSONObject jsonResp = new JSONObject(resp);String answer = jsonResp.get("xxx").get("xxx").optString("具体信息");vars.put("response_type","无目标值");vars.put("actual_msg", answer);vars.put("isExist", "false");
}// 是否需要判断 特定target
private Boolean needCheckTarget(String resp) {if(vars.get("target") != null || !"".equals(vars.get("target"))) {// 有target,需要判断特定targetreturn hasTarget(resp);}else {// 无需判断特定targetreturn true;}
}// 过滤器,过滤白名单
private void filter(String resp) {try {Set<String> whiteSet = vars.getObject("whiteSet");String appId = vars.get("APPID");if(whiteSet.contains(appId)) {vars.put("isExist", "true");}else {// 非白名单errorResp(resp);}} catch (Exception e) {setAssertMsg("过滤器失效!","过滤器失效!");log.warn("过滤白名单失败!");}
}/**每次执行后,处理变量避免下次空变量的值 受上次的影响变量:xxx
*/
private void afterHandle(){vars.put("xxx","");
}SampleResult prev = ctx.getPreviousResult();String response = prev.getResponseDataAsString();// 执行前重置变量
init();// 判断是不是SSE响应
try {if (!isStreamingResponse(response)) {// 非SSE响应handleNonStreamingResponse(response, prev);} else {// SSE响应// 拆分String[] respArray = splitResponse(response);// 获取event,进行判断String event = getEvent(respArray[respArray.length - 1]);if("message_end".equals(event)) {if(needCheckTarget(respArray[respArray.length - 2])){vars.put("isExist", "true");}else {// 无目标字段,更新断言信息setAssertMsg(respArray[respArray.length - 2]);}}else if("error".equals(event)) {// 有报错,进入过滤器,过滤白名单filter(respArray[respArray.length - 1]);}else if("Unknow".equals(event)) {// 获取event的字段内容失败!setAssertMsg("响应异常!","获取event的字段内容失败!");}else {// 未知错误setAssertMsg("处理SSE响应异常!!!","处理SSE响应异常!!!");}}
} catch (Exception e) {log.error("处理响应失败!", e);setAssertMsg("处理响应异常!","处理响应异常!");prev.setSuccessful(false);
}finally {afterHandle();
}