结构图

背景:
需要写一个JMeter脚本来进行自动化测试,主要是通过接口调用一些东西,同时要对响应的数据进行处理,包括不限于错误信息的输出。
1.SSE(摘录)
SSE(Server-Sent Events)是一种基于HTTP协议、允许服务器主动向客户端推送实时更新的技术。它特别适用于单向数据流的实时场景,例如消息通知、AI对话流式响应等,通过保持长连接实现持续数据传输。
2.实现思路
2.1 用户自定义变量组件
主要是用来统一更换和维护环境变量的,比如线上、线下环境host的切换
2.2 HTTP信息头管理
设置https请求的信息头,比如token,数据格式等等
2.3 CSV 数据文件设置
参数如下图设置
变量名称:appId,query,appName,needFiles,file
对应vars中的变量,变量值是根据分隔带JMeter自动处理的,变量名称 数量和分隔后的变量值 数量不对应也没影响,两者缺少的值会忽略或者设置为空值
2.4 HTTP请求组件
需要设置消息体数据,请求url等
下面三个插件,归类到HTTP请求的子目录下 :如图
2.4.1 JSR223 预处理程序
主要来处理一下请求中消息体数据中的一个参数,fileParam
根据csv文件中的标记来确定fileParam的具体值,如下代码
java
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{
//将fileParams转为json格式
JSONObject jsonResponse = new JSONObject(fileParams);
// 提取各个参数
String filename = jsonResponse.optString("xxx", "");
String fileHash = jsonResponse.optString("xxx", "");
Integer filesize = jsonResponse.optInt("xxx", 0); // Integer类型
String extension = jsonResponse.optString("xxx", "");
String mimeType = jsonResponse.optString("xxx", "");
// 创建 JSON 对象数组(List<Map> 格式)
JSONArray fileParamsArray = new JSONArray();
JSONObject fileObj = new JSONObject();
fileObj.put("xxx", xxx);
fileObj.put("xxx", xxx);
fileObj.put("xxx", xxx);
fileObj.put("xxx", xxx);
fileObj.put("xxx", xxx);
fileParamsArray.put(fileObj);
// 存入 vars(JSON 字符串)
vars.put("fileParam", fileParamsArray.toString());
} catch (Exception e) {
log.error("设置 files 参数 失败!", e);
prev.setSuccessful(false);
}
}
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());
}
} catch (Exception e) {
log.error("判断是否需要文件参数 失败!", e);
prev.setSuccessful(false);
}
2.4.2 JSR223 后置处理程序
对SSE响应的数据进行处理和判断,确定好哪个数据是一次请求结束的标记
下面代码是根据 event 含有 message_end 字段来做执行成功的标记
error字段来错失败的标记,同时进入断言
下面的代码是逐行匹配、逐个处理 SSE 事件 ,适合实时响应场景
java
import org.apache.jmeter.samplers.SampleResult;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import org.json.JSONObject;
import org.json.JSONException;
// 判断是否是流式响应
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)) {
vars.put("response_type", "智能体不存在");
vars.put("error_msg", msg);
vars.put("isExist", "false");
} else {
vars.put("response_type", "非流式响应,未知错误");
vars.put("error_msg", msg);
vars.put("isExist", "false");
}
}
// 流式响应处理
private void handleStreamingResponse(String response, SampleResult prev) throws Exception {
vars.put("response_type", "streaming");
// 正则表达式,确保匹配完整JSON
Pattern pattern = Pattern.compile("data:\\s*(\\{[^{}]*\\})", Pattern.DOTALL)
Matcher matcher = pattern.matcher(response);
while (matcher.find()) {
try {
String eventJson = matcher.group(1).trim();
// 处理可能的UTF-8 BOM头
if (eventJson.startsWith("\uFEFF")) {
eventJson = eventJson.substring(1);
}
JSONObject jsonResponse = new JSONObject(eventJson);
String eventType = jsonResponse.optString("event");
if ("message_end".equals(eventType)) {
vars.put("response_type", "智能体执行成功");
vars.put("error_msg", "智能体执行成功");
vars.put("isExist", "true");
} else if ("error".equals(eventType)) {
vars.put("response_type", "智能体执行失败");
String errorMsg = jsonResponse.get("message");
vars.put("error_msg", errorMsg);
vars.put("isExist", "false");
}
} catch (JSONException e) {
log.warn("SSE事件JSON解析失败,跳过该事件: " + vars.get("appName"));
}
}
}
SampleResult prev = ctx.getPreviousResult();
String response = prev.getResponseDataAsString();
//添加APPID信息
vars.put("APPID",vars.get("appId"));
//每次重置isExist的值,避免上次结果影响本次
vars.put("isExist", "true");
try {
if (!isStreamingResponse(response)) {
handleNonStreamingResponse(response, prev);
} else {
handleStreamingResponse(response, prev);
}
} catch (Exception e) {
log.error("处理响应失败!", e);
prev.setSuccessful(false);
}
handleStreamingResponse 方法:
对每次符合要求的数据进行处理和判断
// 正则表达式,确保匹配完整JSON
Pattern pattern = Pattern.compile("data:\s*(\{[^{}]*\})", Pattern.DOTALL)
Matcher matcher = pattern.matcher(response);
data:\s *匹配字符串 "data:",后面跟 0个或多个空白字符(\s* 包括空格、换行符等)。
(\{[^{}]*\})
\{ 匹配左花括号 {({ 需要转义)。
\^{}\]\* 匹配 任意字符(除了 { 和 })0次或多次,确保匹配的是 单层花括号 的内容。 \\} 匹配右花括号 }。 () 表示捕获组,最终提取的是花括号内的内容。 **Pattern.DOTALL** 让 . 匹配 包括换行符在内的所有字符,确保多行文本也能被正确匹配。 . **Matcher matcher = pattern.matcher(response);** 用编译好的正则模式 pattern 去匹配输入的字符串 response。 matcher 对象可以用于查找、提取符合正则规则的部分。 while (matcher.find()) { try { String eventJson = matcher.group(1).trim(); > matcher.find() 每次找到一个匹配项后,会移动内部指针,直到所有匹配项被遍历完。 > matcher.group(1) 提取正则中 第一个捕获组(即 ({\[\^{}\]\*}) 匹配的 {...} 部分)。
2.4.3 JSR JSR223 Assertion
进行断言处理,处理需要输出的信息
java
// 断言
if ("false".equals(vars.get("isExist"))) {
// 获取智能体名称
String appName = vars.get("appName");
// 获取具体失败类型
String respone_type = vars.get("response_type");
// 获取error_message
String error_message = vars.get("error_msg");
// 获取APPID
String appId = vars.get("APPID");
//执行失败
AssertionResult.setFailure(true); // 标记断言失败
AssertionResult.setFailureMessage(appName + "\n\t" + " 智能体ID:" + appId + "\n" + "\t 错误原因:"
+ respone_type + "\n" + "\t error_message:" + error_message);
// 添加到标签列
//prev.setSampleLabel(vars.get("respone_type"))
// 修改响应消息为message变量的内容
prev.setResponseMessage(respone_type);
// 添加调试信息
log.info("智能体名称:", appName);
log.info("智能体ID:", appId);
log.info("错误原因:", respone_type);
log.info("error_message:", error_message);
}