fix(性能测试): 压力配置上可以选择场景是否顺序执行

This commit is contained in:
Captain.B 2021-04-02 16:20:37 +08:00 committed by 刘瑞斌
parent c7c55541f0
commit b4d8c693ba
6 changed files with 78 additions and 76 deletions

View File

@ -41,6 +41,7 @@ public class JmeterDocumentParser implements DocumentParser {
private final static String RESPONSE_ASSERTION = "ResponseAssertion";
private final static String CSV_DATA_SET = "CSVDataSet";
private EngineContext context;
private boolean containsIterationThread = false;
@Override
public String parse(EngineContext context, Document document) throws Exception {
@ -96,10 +97,10 @@ public class JmeterDocumentParser implements DocumentParser {
processCheckoutArguments(ele);
processCheckoutResponseAssertion(ele);
processCheckoutSerializeThreadgroups(ele);
processCheckoutBackendListener(ele);
} else if (nodeNameEquals(ele, CONCURRENCY_THREAD_GROUP)) {
processThreadGroupName(ele);
processCheckoutTimer(ele);
processCheckoutBackendListener(ele);
} else if (nodeNameEquals(ele, VARIABLE_THROUGHPUT_TIMER)) {
processVariableThroughputTimer(ele);
} else if (nodeNameEquals(ele, THREAD_GROUP)) {
@ -112,6 +113,7 @@ public class JmeterDocumentParser implements DocumentParser {
}
if ("ITERATION".equals(o)) {
processIterationThreadGroup(ele);
this.containsIterationThread = true; // 包括按照迭代次数的线程组
}
} else {
processThreadGroup(ele);
@ -119,7 +121,6 @@ public class JmeterDocumentParser implements DocumentParser {
processThreadGroupName(ele);
processCheckoutTimer(ele);
processCheckoutBackendListener(ele);
} else if (nodeNameEquals(ele, BACKEND_LISTENER)) {
processBackendListener(ele);
} else if (nodeNameEquals(ele, CONFIG_TEST_ELEMENT)) {
@ -144,14 +145,19 @@ public class JmeterDocumentParser implements DocumentParser {
}
private void processCheckoutSerializeThreadgroups(Element element) {
Object serializeThreadGroups = context.getProperty("serializeThreadGroups");
String serializeThreadGroup = "false";
if (serializeThreadGroups instanceof List) {
Object o = ((List<?>) serializeThreadGroups).get(0);
serializeThreadGroup = o.toString();
}
NodeList childNodes = element.getChildNodes();
for (int i = 0; i < childNodes.getLength(); i++) {
Node item = childNodes.item(i);
if (nodeNameEquals(item, BOOL_PROP)) {
String serializeName = ((Element) item).getAttribute("name");
if (StringUtils.equals(serializeName, "TestPlan.serialize_threadgroups")) {
// 保存线程组是否是顺序执行
context.addProperty("serialize_threadgroups", item.getTextContent());
item.setTextContent(serializeThreadGroup);
break;
}
}
@ -542,6 +548,15 @@ public class JmeterDocumentParser implements DocumentParser {
}
private void processBackendListener(Element backendListener) {
String duration = "0";
Object expectedDurations = context.getProperty("expectedDuration");
if (expectedDurations instanceof List) {
Object o = ((List<?>) expectedDurations).get(0);// 预计执行时间已经计算好
duration = o.toString() + "000"; // 转成 ms
}
if (this.containsIterationThread) {
duration = Integer.MAX_VALUE + ""; // 如果包含了按照迭代次数的线程组预计执行时间很长
}
KafkaProperties kafkaProperties = CommonBeanFactory.getBean(KafkaProperties.class);
Document document = backendListener.getOwnerDocument();
// 清空child
@ -586,7 +601,7 @@ public class JmeterDocumentParser implements DocumentParser {
collectionProp.appendChild(createKafkaProp(document, "test.name", context.getTestName()));
collectionProp.appendChild(createKafkaProp(document, "test.startTime", context.getStartTime().toString()));
collectionProp.appendChild(createKafkaProp(document, "test.reportId", context.getReportId()));
collectionProp.appendChild(createKafkaProp(document, "test.expectedDuration", (String) context.getProperty("expectedDuration")));
collectionProp.appendChild(createKafkaProp(document, "test.expectedDuration", duration));// ms
collectionProp.appendChild(createKafkaProp(document, "test.expectedDelayEndTime", kafkaProperties.getExpectedDelayEndTime())); // 30s
elementProp.appendChild(collectionProp);
@ -612,15 +627,6 @@ public class JmeterDocumentParser implements DocumentParser {
listenerParent = listenerParent.getNextSibling();
}
NodeList childNodes = listenerParent.getChildNodes();
for (int i = 0, l = childNodes.getLength(); i < l; i++) {
Node item = childNodes.item(i);
if (nodeNameEquals(item, BACKEND_LISTENER)) {
// 如果已经存在不再添加
return;
}
}
// add class name
Element backendListener = document.createElement(BACKEND_LISTENER);
backendListener.setAttribute("guiclass", "BackendListenerGui");
@ -729,8 +735,6 @@ public class JmeterDocumentParser implements DocumentParser {
default:
break;
}
// 处理预计结束时间
processExpectedDuration(duration);
threadGroup.setAttribute("enabled", enabled);
if (BooleanUtils.toBoolean(deleted)) {
@ -824,30 +828,18 @@ public class JmeterDocumentParser implements DocumentParser {
enabled = o.toString();
}
Object durations = context.getProperty("duration");
String duration = "2";
if (durations instanceof List) {
Object o = ((List<?>) durations).get(0);
((List<?>) durations).remove(0);
duration = o.toString();
}
switch (unit) {
case "M":
duration = String.valueOf(Long.parseLong(duration) * 60);
hold = String.valueOf(Long.parseLong(hold) * 60);
rampUp = String.valueOf(Long.parseLong(rampUp) * 60);
break;
case "H":
duration = String.valueOf(Long.parseLong(duration) * 60 * 60);
hold = String.valueOf(Long.parseLong(hold) * 60 * 60);
rampUp = String.valueOf(Long.parseLong(rampUp) * 60 * 60);
break;
default:
break;
}
// 处理预计结束时间
processExpectedDuration(duration);
threadGroup.setAttribute("enabled", enabled);
if (BooleanUtils.toBoolean(deleted)) {
@ -868,26 +860,6 @@ public class JmeterDocumentParser implements DocumentParser {
threadGroup.appendChild(createStringProp(document, "Unit", "S"));
}
private void processExpectedDuration(String duration) {
Long d = Long.parseLong(duration);
Object serialize = context.getProperty("serialize_threadgroups");
String expectedDuration = (String) context.getProperty("expectedDuration");
if (StringUtils.isBlank(expectedDuration)) {
expectedDuration = "0";
}
long durationTime = Long.parseLong(expectedDuration);
if (BooleanUtils.toBoolean((String) serialize)) {
// 顺序执行线程组
context.addProperty("expectedDuration", String.valueOf(durationTime + d * 1000));
} else {
// 同时执行线程组
if (durationTime < d * 1000) {
context.addProperty("expectedDuration", String.valueOf(d * 1000));
}
}
}
private void processIterationThreadGroup(Element threadGroup) {
// 检查 threadgroup 后面的hashtree是否为空
Node hashTree = threadGroup.getNextSibling();
@ -956,10 +928,6 @@ public class JmeterDocumentParser implements DocumentParser {
threadGroup.appendChild(createStringProp(document, "ThreadGroup.duration", "10"));
threadGroup.appendChild(createStringProp(document, "ThreadGroup.delay", ""));
threadGroup.appendChild(createBoolProp(document, "ThreadGroup.same_user_on_next_iteration", true));
// 处理预计结束时间 (按照迭代次数 * 线程数)s
String duration = String.valueOf(Long.parseLong(loops) * Long.parseLong(threads));
processExpectedDuration(duration);
}
private void processCheckoutTimer(Element element) {

View File

@ -229,30 +229,6 @@ export default {
}
};
},
stringToByte(str) {
var bytes = new Array();
var len, c;
len = str.length;
for (var i = 0; i < len; i++) {
c = str.charCodeAt(i);
if (c >= 0x010000 && c <= 0x10FFFF) {
bytes.push(((c >> 18) & 0x07) | 0xF0);
bytes.push(((c >> 12) & 0x3F) | 0x80);
bytes.push(((c >> 6) & 0x3F) | 0x80);
bytes.push((c & 0x3F) | 0x80);
} else if (c >= 0x000800 && c <= 0x00FFFF) {
bytes.push(((c >> 12) & 0x0F) | 0xE0);
bytes.push(((c >> 6) & 0x3F) | 0x80);
bytes.push((c & 0x3F) | 0x80);
} else if (c >= 0x000080 && c <= 0x0007FF) {
bytes.push(((c >> 6) & 0x1F) | 0xC0);
bytes.push((c & 0x3F) | 0x80);
} else {
bytes.push(c & 0xFF);
}
}
return bytes;
},
cancel() {
this.$router.push({path: '/performance/test/all'})
},

View File

@ -13,6 +13,9 @@
</el-option>
</el-select>
</el-form-item>
<el-form-item :label="$t('load_test.serialize_threadgroups')">
<el-switch v-model="serializeThreadGroups"/>
</el-form-item>
</el-form>
</el-col>
</el-row>
@ -177,6 +180,8 @@ import {findThreadGroup} from "@/business/components/performance/test/model/Thre
const HANDLER = "handler";
const THREAD_GROUP_TYPE = "tgType";
const EXPECTED_DURATION = "expectedDuration";
const SERIALIZE_THREAD_GROUPS = "serializeThreadGroups";
const TARGET_LEVEL = "TargetLevel";
const RAMP_UP = "RampUp";
const ITERATE_RAMP_UP = "iterateRampUpTime";
@ -231,6 +236,7 @@ export default {
threadGroups: [],
resourcePoolResourceLength: 1,
maxThreadNumbers: 5000,
serializeThreadGroups: false,
}
},
mounted() {
@ -317,6 +323,9 @@ export default {
case THREAD_GROUP_TYPE:
this.threadGroups[i].tgType = item.value;
break;
case SERIALIZE_THREAD_GROUPS:
this.serializeThreadGroups = item.value;// 线
break;
default:
break;
}
@ -639,9 +648,52 @@ export default {
}
return this.$t('schedule.cron.seconds');
},
calculateDuration() {
let expectedDuration = 0;
for (let i = 0; i < this.threadGroups.length; i++) {
if (this.serializeThreadGroups) {
switch (this.threadGroups[i].unit) {
case "S":
expectedDuration += this.threadGroups[i].duration;
break;
case "M":
expectedDuration += this.threadGroups[i].duration * 60;
break;
case "H":
expectedDuration += this.threadGroups[i].duration * 60 * 60;
break;
default:
break;
}
} else {
let tmp = 0;
switch (this.threadGroups[i].unit) {
case "S":
tmp = this.threadGroups[i].duration;
break;
case "M":
tmp = this.threadGroups[i].duration * 60;
break;
case "H":
tmp = this.threadGroups[i].duration * 60 * 60;
break;
default:
break;
}
if (expectedDuration < tmp) {
expectedDuration = tmp;
}
}
}
return expectedDuration;
},
convertProperty() {
/// todo4jmeter ConcurrencyThreadGroup plugin
let result = [];
//
let expectedDuration = this.calculateDuration();
//
for (let i = 0; i < this.threadGroups.length; i++) {
result.push([
{key: HANDLER, value: this.threadGroups[i].handler},
@ -659,8 +711,11 @@ export default {
{key: ENABLED, value: this.threadGroups[i].enabled},
{key: DELETED, value: this.threadGroups[i].deleted},
{key: THREAD_GROUP_TYPE, value: this.threadGroups[i].tgType},
{key: EXPECTED_DURATION, value: expectedDuration},
{key: SERIALIZE_THREAD_GROUPS, value: this.serializeThreadGroups},
]);
}
return result;
}
}

View File

@ -523,6 +523,7 @@ export default {
response_timeout: 'Timeout to response',
custom_http_code: 'Custom HTTP response success status code',
separated_by_commas: 'Separated by commas',
serialize_threadgroups:'Whether the scene is executed sequentially',
create: 'Create Test',
select_resource_pool: 'Please Select Resource Pool',
resource_pool_is_null: 'Resource Pool is empty',

View File

@ -523,6 +523,7 @@ export default {
create: '创建测试',
run: '一键运行',
select_resource_pool: '请选择资源池',
serialize_threadgroups: '场景是否顺序执行',
resource_pool_is_null: '资源池为空',
download_log_file: '下载完整日志文件',
pressure_prediction_chart: '压力预估图',

View File

@ -523,6 +523,7 @@ export default {
create: '創建測試',
run: '壹鍵運行',
select_resource_pool: '請選擇資源池',
serialize_threadgroups: '場景是否順序執行',
resource_pool_is_null: '資源池為空',
download_log_file: '下載完整日誌文件',
pressure_prediction_chart: '壓力預估圖',