diff --git a/backend/src/main/java/io/metersphere/performance/parse/xml/reader/jmx/JmeterDocumentParser.java b/backend/src/main/java/io/metersphere/performance/parse/xml/reader/jmx/JmeterDocumentParser.java index 308e68e3ff23cb7d3721fa3fb4d0714e4db794b3..ba015e589ca2289f8ed256da5279436628acc9ed 100644 --- a/backend/src/main/java/io/metersphere/performance/parse/xml/reader/jmx/JmeterDocumentParser.java +++ b/backend/src/main/java/io/metersphere/performance/parse/xml/reader/jmx/JmeterDocumentParser.java @@ -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) { diff --git a/frontend/src/business/components/performance/test/EditPerformanceTest.vue b/frontend/src/business/components/performance/test/EditPerformanceTest.vue index 27338c86b59d0d5b64770a64a77dc108ab37a168..c87f886cc82ddd5800ccc2f67b7717787d170fd5 100644 --- a/frontend/src/business/components/performance/test/EditPerformanceTest.vue +++ b/frontend/src/business/components/performance/test/EditPerformanceTest.vue @@ -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'}) }, diff --git a/frontend/src/business/components/performance/test/components/PerformancePressureConfig.vue b/frontend/src/business/components/performance/test/components/PerformancePressureConfig.vue index e84de75a1ac8c5938012b926ed489c0e27098ec7..f23bba6ade663025334dd78f187f5f1316d5f635 100644 --- a/frontend/src/business/components/performance/test/components/PerformancePressureConfig.vue +++ b/frontend/src/business/components/performance/test/components/PerformancePressureConfig.vue @@ -13,6 +13,9 @@ + + + @@ -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() { /// todo:下面4个属性是jmeter 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; } } diff --git a/frontend/src/i18n/en-US.js b/frontend/src/i18n/en-US.js index 70bfea7b88879d8fb7f33170b489ab7e33795154..d9d718645d41e3d81bcdd450ce17017eb77c7608 100644 --- a/frontend/src/i18n/en-US.js +++ b/frontend/src/i18n/en-US.js @@ -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', diff --git a/frontend/src/i18n/zh-CN.js b/frontend/src/i18n/zh-CN.js index b18147c150c2da5f9c66892410dfa1d118b5b838..e7f549effe724b01055140181987088764eff92a 100644 --- a/frontend/src/i18n/zh-CN.js +++ b/frontend/src/i18n/zh-CN.js @@ -523,6 +523,7 @@ export default { create: '创建测试', run: '一键运行', select_resource_pool: '请选择资源池', + serialize_threadgroups: '场景是否顺序执行', resource_pool_is_null: '资源池为空', download_log_file: '下载完整日志文件', pressure_prediction_chart: '压力预估图', diff --git a/frontend/src/i18n/zh-TW.js b/frontend/src/i18n/zh-TW.js index b133f206e15c1f38f49bc79f7e2a5dad5a537619..305cd050615ac95e3bbc7226579ea67957d64166 100644 --- a/frontend/src/i18n/zh-TW.js +++ b/frontend/src/i18n/zh-TW.js @@ -523,6 +523,7 @@ export default { create: '創建測試', run: '壹鍵運行', select_resource_pool: '請選擇資源池', + serialize_threadgroups: '場景是否順序執行', resource_pool_is_null: '資源池為空', download_log_file: '下載完整日誌文件', pressure_prediction_chart: '壓力預估圖',