refactor(性能测试): 性能测试重构

This commit is contained in:
Captain.B 2021-07-30 21:40:41 +08:00 committed by 刘瑞斌
parent 2908846e90
commit 29efd7f60e
16 changed files with 176 additions and 90 deletions

View File

@ -1,7 +1,6 @@
package io.metersphere.base.domain;
import java.io.Serializable;
import lombok.Data;
@Data
@ -32,5 +31,7 @@ public class TestResourcePool implements Serializable {
private Boolean performance;
private Boolean backendListener;
private static final long serialVersionUID = 1L;
}

View File

@ -973,6 +973,66 @@ public class TestResourcePoolExample {
addCriterion("performance not between", value1, value2, "performance");
return (Criteria) this;
}
public Criteria andBackendListenerIsNull() {
addCriterion("backend_listener is null");
return (Criteria) this;
}
public Criteria andBackendListenerIsNotNull() {
addCriterion("backend_listener is not null");
return (Criteria) this;
}
public Criteria andBackendListenerEqualTo(Boolean value) {
addCriterion("backend_listener =", value, "backendListener");
return (Criteria) this;
}
public Criteria andBackendListenerNotEqualTo(Boolean value) {
addCriterion("backend_listener <>", value, "backendListener");
return (Criteria) this;
}
public Criteria andBackendListenerGreaterThan(Boolean value) {
addCriterion("backend_listener >", value, "backendListener");
return (Criteria) this;
}
public Criteria andBackendListenerGreaterThanOrEqualTo(Boolean value) {
addCriterion("backend_listener >=", value, "backendListener");
return (Criteria) this;
}
public Criteria andBackendListenerLessThan(Boolean value) {
addCriterion("backend_listener <", value, "backendListener");
return (Criteria) this;
}
public Criteria andBackendListenerLessThanOrEqualTo(Boolean value) {
addCriterion("backend_listener <=", value, "backendListener");
return (Criteria) this;
}
public Criteria andBackendListenerIn(List<Boolean> values) {
addCriterion("backend_listener in", values, "backendListener");
return (Criteria) this;
}
public Criteria andBackendListenerNotIn(List<Boolean> values) {
addCriterion("backend_listener not in", values, "backendListener");
return (Criteria) this;
}
public Criteria andBackendListenerBetween(Boolean value1, Boolean value2) {
addCriterion("backend_listener between", value1, value2, "backendListener");
return (Criteria) this;
}
public Criteria andBackendListenerNotBetween(Boolean value1, Boolean value2) {
addCriterion("backend_listener not between", value1, value2, "backendListener");
return (Criteria) this;
}
}
public static class Criteria extends GeneratedCriteria {

View File

@ -15,6 +15,7 @@
<result column="create_user" jdbcType="VARCHAR" property="createUser" />
<result column="api" jdbcType="BIT" property="api" />
<result column="performance" jdbcType="BIT" property="performance" />
<result column="backend_listener" jdbcType="BIT" property="backendListener" />
</resultMap>
<sql id="Example_Where_Clause">
<where>
@ -76,7 +77,7 @@
</sql>
<sql id="Base_Column_List">
id, `name`, `type`, description, `status`, create_time, update_time, image, `heap`,
gc_algo, create_user, api, performance
gc_algo, create_user, api, performance, backend_listener
</sql>
<select id="selectByExample" parameterType="io.metersphere.base.domain.TestResourcePoolExample" resultMap="BaseResultMap">
select
@ -113,12 +114,12 @@
description, `status`, create_time,
update_time, image, `heap`,
gc_algo, create_user, api,
performance)
performance, backend_listener)
values (#{id,jdbcType=VARCHAR}, #{name,jdbcType=VARCHAR}, #{type,jdbcType=VARCHAR},
#{description,jdbcType=VARCHAR}, #{status,jdbcType=VARCHAR}, #{createTime,jdbcType=BIGINT},
#{updateTime,jdbcType=BIGINT}, #{image,jdbcType=VARCHAR}, #{heap,jdbcType=VARCHAR},
#{gcAlgo,jdbcType=VARCHAR}, #{createUser,jdbcType=VARCHAR}, #{api,jdbcType=BIT},
#{performance,jdbcType=BIT})
#{performance,jdbcType=BIT}, #{backendListener,jdbcType=BIT})
</insert>
<insert id="insertSelective" parameterType="io.metersphere.base.domain.TestResourcePool">
insert into test_resource_pool
@ -162,6 +163,9 @@
<if test="performance != null">
performance,
</if>
<if test="backendListener != null">
backend_listener,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null">
@ -203,6 +207,9 @@
<if test="performance != null">
#{performance,jdbcType=BIT},
</if>
<if test="backendListener != null">
#{backendListener,jdbcType=BIT},
</if>
</trim>
</insert>
<select id="countByExample" parameterType="io.metersphere.base.domain.TestResourcePoolExample" resultType="java.lang.Long">
@ -253,6 +260,9 @@
<if test="record.performance != null">
performance = #{record.performance,jdbcType=BIT},
</if>
<if test="record.backendListener != null">
backend_listener = #{record.backendListener,jdbcType=BIT},
</if>
</set>
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
@ -272,7 +282,8 @@
gc_algo = #{record.gcAlgo,jdbcType=VARCHAR},
create_user = #{record.createUser,jdbcType=VARCHAR},
api = #{record.api,jdbcType=BIT},
performance = #{record.performance,jdbcType=BIT}
performance = #{record.performance,jdbcType=BIT},
backend_listener = #{record.backendListener,jdbcType=BIT}
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
</if>
@ -316,6 +327,9 @@
<if test="performance != null">
performance = #{performance,jdbcType=BIT},
</if>
<if test="backendListener != null">
backend_listener = #{backendListener,jdbcType=BIT},
</if>
</set>
where id = #{id,jdbcType=VARCHAR}
</update>
@ -332,7 +346,8 @@
gc_algo = #{gcAlgo,jdbcType=VARCHAR},
create_user = #{createUser,jdbcType=VARCHAR},
api = #{api,jdbcType=BIT},
performance = #{performance,jdbcType=BIT}
performance = #{performance,jdbcType=BIT},
backend_listener = #{backendListener,jdbcType=BIT}
where id = #{id,jdbcType=VARCHAR}
</update>
</mapper>

View File

@ -33,6 +33,7 @@ public abstract class AbstractEngine implements Engine {
protected PerformanceTestService performanceTestService;
protected Integer threadNum;
protected List<TestResource> resourceList;
protected TestResourcePool resourcePool;
private final TestResourcePoolService testResourcePoolService;
private final TestResourceService testResourceService;
@ -45,9 +46,10 @@ public abstract class AbstractEngine implements Engine {
this.startTime = System.currentTimeMillis();
this.reportId = UUID.randomUUID().toString();
}
protected void initApiConfig(RunRequest runRequest) {
String resourcePoolId = runRequest.getPoolId();
TestResourcePool resourcePool = testResourcePoolService.getResourcePool(resourcePoolId);
resourcePool = testResourcePoolService.getResourcePool(resourcePoolId);
if (resourcePool == null || StringUtils.equals(resourcePool.getStatus(), ResourceStatusEnum.DELETE.name())) {
MSException.throwException("Resource Pool is empty");
}
@ -78,6 +80,7 @@ public abstract class AbstractEngine implements Engine {
MSException.throwException("Test Resource is empty");
}
}
protected void init(LoadTestWithBLOBs loadTest) {
if (loadTest == null) {
MSException.throwException("LoadTest is null.");
@ -91,7 +94,7 @@ public abstract class AbstractEngine implements Engine {
if (StringUtils.isBlank(resourcePoolId)) {
MSException.throwException("Resource Pool ID is empty");
}
TestResourcePool resourcePool = testResourcePoolService.getResourcePool(resourcePoolId);
resourcePool = testResourcePoolService.getResourcePool(resourcePoolId);
if (resourcePool == null || StringUtils.equals(resourcePool.getStatus(), ResourceStatusEnum.DELETE.name())) {
MSException.throwException("Resource Pool is empty");
}

View File

@ -96,6 +96,7 @@ public class DockerTestEngine extends AbstractEngine {
env.put("HEAP", HEAP);
env.put("GC_ALGO", GC_ALGO);
env.put("GRANULARITY", performanceTestService.getGranularity(this.getReportId()).toString());
env.put("BACKEND_LISTENER", resourcePool.getBackendListener().toString());
StartTestRequest startTestRequest = new StartTestRequest();

View File

@ -2,6 +2,7 @@ package io.metersphere.performance.parse.xml.reader.jmx;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import io.metersphere.base.domain.TestResourcePool;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.CommonBeanFactory;
import io.metersphere.config.KafkaProperties;
@ -9,6 +10,7 @@ import io.metersphere.i18n.Translator;
import io.metersphere.jmeter.utils.ScriptEngineUtils;
import io.metersphere.performance.engine.EngineContext;
import io.metersphere.performance.parse.xml.reader.DocumentParser;
import io.metersphere.service.TestResourcePoolService;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.w3c.dom.Document;
@ -587,6 +589,11 @@ public class JmeterDocumentParser implements DocumentParser {
}
private void processBackendListener(Element backendListener) {
String resourcePoolId = context.getResourcePoolId();
TestResourcePool resourcePool = CommonBeanFactory.getBean(TestResourcePoolService.class).getResourcePool(resourcePoolId);
if (!BooleanUtils.toBoolean(resourcePool.getBackendListener())) {
return;
}
KafkaProperties kafkaProperties = CommonBeanFactory.getBean(KafkaProperties.class);
Document document = backendListener.getOwnerDocument();
// 清空child
@ -647,6 +654,12 @@ public class JmeterDocumentParser implements DocumentParser {
}
private void processCheckoutBackendListener(Element element) {
String resourcePoolId = context.getResourcePoolId();
TestResourcePool resourcePool = CommonBeanFactory.getBean(TestResourcePoolService.class).getResourcePool(resourcePoolId);
if (!BooleanUtils.toBoolean(resourcePool.getBackendListener())) {
return;
}
Document document = element.getOwnerDocument();
Node listenerParent = element.getNextSibling();

View File

@ -2,7 +2,6 @@ package io.metersphere.performance.service;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import io.metersphere.api.dto.JmxInfoDTO;
import io.metersphere.api.dto.automation.ApiScenarioBatchRequest;
import io.metersphere.api.dto.automation.ApiScenrioExportJmx;
@ -16,7 +15,6 @@ import io.metersphere.base.mapper.ext.ExtLoadTestReportMapper;
import io.metersphere.commons.constants.*;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.*;
import io.metersphere.config.JmeterProperties;
import io.metersphere.config.KafkaProperties;
import io.metersphere.controller.request.OrderRequest;
import io.metersphere.controller.request.QueryScheduleRequest;
@ -30,7 +28,6 @@ import io.metersphere.log.utils.ReflexObjectUtil;
import io.metersphere.log.vo.DetailColumn;
import io.metersphere.log.vo.OperatingLogDetails;
import io.metersphere.log.vo.performance.PerformanceReference;
import io.metersphere.performance.base.GranularityData;
import io.metersphere.performance.dto.LoadTestExportJmx;
import io.metersphere.performance.engine.Engine;
import io.metersphere.performance.engine.EngineFactory;
@ -60,7 +57,6 @@ import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
@Service
@ -356,11 +352,7 @@ public class PerformanceTestService {
}
// 启动测试
try {
engine.start();
// 启动正常修改状态 starting
loadTest.setStatus(PerformanceTestStatus.Starting.name());
loadTestMapper.updateByPrimaryKeySelective(loadTest);
// 启动正常插入 report
// 启动插入 report
testReport.setLoadConfiguration(loadTest.getLoadConfiguration());
testReport.setAdvancedConfiguration(loadTest.getAdvancedConfiguration());
testReport.setStatus(PerformanceTestStatus.Starting.name());
@ -369,6 +361,11 @@ public class PerformanceTestService {
testReport.setTestName(loadTest.getName());
loadTestReportMapper.insertSelective(testReport);
engine.start();
// 启动正常修改状态 starting
loadTest.setStatus(PerformanceTestStatus.Starting.name());
loadTestMapper.updateByPrimaryKeySelective(loadTest);
LoadTestReportDetail reportDetail = new LoadTestReportDetail();
reportDetail.setContent(HEADERS);
reportDetail.setReportId(testReport.getId());
@ -788,42 +785,16 @@ public class PerformanceTestService {
}
public Integer getGranularity(String reportId) {
Integer granularity = CommonBeanFactory.getBean(JmeterProperties.class).getReport().getGranularity();
try {
LoadTestReportWithBLOBs report = loadTestReportMapper.selectByPrimaryKey(reportId);
LoadTestWithBLOBs loadTest = loadTestMapper.selectByPrimaryKey(report.getTestId());
JSONObject advancedConfig = JSON.parseObject(loadTest.getAdvancedConfiguration());
if (advancedConfig.getInteger("granularity") != null) {
granularity = advancedConfig.getInteger("granularity");
return granularity * 1000; // 单位是ms
return advancedConfig.getInteger("granularity") * 1000;// 单位是ms
}
AtomicReference<Integer> maxDuration = new AtomicReference<>(0);
List<List<JSONObject>> pressureConfigLists = JSON.parseObject(loadTest.getLoadConfiguration(), new TypeReference<List<List<JSONObject>>>() {
});
// 按照最长的执行时间来确定
pressureConfigLists.forEach(pcList -> {
Optional<Integer> maxOp = pcList.stream()
.filter(pressureConfig -> StringUtils.equalsAnyIgnoreCase(pressureConfig.getString("key"), "hold", "duration"))
.map(pressureConfig -> pressureConfig.getInteger("value"))
.max(Comparator.naturalOrder());
Integer max = maxOp.orElse(0);
if (maxDuration.get() < max) {
maxDuration.set(max);
}
});
Optional<GranularityData.Data> dataOptional = GranularityData.dataList.stream()
.filter(data -> maxDuration.get() >= data.getStart() && maxDuration.get() <= data.getEnd())
.findFirst();
if (dataOptional.isPresent()) {
GranularityData.Data data = dataOptional.get();
granularity = data.getGranularity();
}
} catch (Exception e) {
LogUtil.error(e);
}
return granularity;
return -1; // 表示计算报告自己决定
}
}

@ -1 +1 @@
Subproject commit 5b06224daf6b4232613a96c58d03a7f6831f4789
Subproject commit 7acb44cc610bbaf08b510c35849c04e4871e223a

View File

@ -1,4 +1,3 @@
-- 接口定义增加全局运行环境选择
CREATE TABLE IF NOT EXISTS `api_definition_env` (
`id` varchar(50) NOT NULL COMMENT 'ID',
@ -8,3 +7,19 @@ CREATE TABLE IF NOT EXISTS `api_definition_env` (
update_time bigint(13) null,
PRIMARY KEY (`id`)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE utf8mb4_general_ci;
-- load_test_report_result_part
CREATE TABLE `load_test_report_result_part`
(
`report_id` VARCHAR(50) NOT NULL,
`report_key` VARCHAR(64) NOT NULL,
`resource_index` INT NOT NULL,
`report_value` LONGTEXT,
PRIMARY KEY `load_test_report_result_report_id_report_key_index` (`report_id`, `report_key`, `resource_index`)
)
ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE utf8mb4_general_ci;
ALTER TABLE test_resource_pool
ADD backend_listener TINYINT(1) DEFAULT 1;

View File

@ -146,7 +146,7 @@ export default {
}
.infinite-list {
height: calc(100vh - 390px);
height: calc(100vh - 295px);
padding: 0;
margin: 0;
list-style: none;

View File

@ -4,6 +4,7 @@
:data="tableData"
stripe
border
height="calc(100vh - 235px)"
style="width: 100%"
>
<el-table-column label="Requests" min-width="150" align="center">

View File

@ -715,7 +715,7 @@ export default {
}
.test-detail {
height: calc(100vh - 345px);
height: calc(100vh - 285px);
overflow: auto;
}

View File

@ -53,13 +53,13 @@
</el-card>
<el-dialog
:close-on-click-modal="false"
:title="form.id ? $t('test_resource_pool.update_resource_pool') : $t('test_resource_pool.create_resource_pool')"
:visible.sync="dialogVisible" width="80%"
top="5%"
@closed="closeFunc"
:destroy-on-close="true"
v-loading="result.loading"
:close-on-click-modal="false"
:title="form.id ? $t('test_resource_pool.update_resource_pool') : $t('test_resource_pool.create_resource_pool')"
:visible.sync="dialogVisible" width="80%"
top="5%"
@closed="closeFunc"
:destroy-on-close="true"
v-loading="result.loading"
>
<div style="height: 60vh;overflow: auto;">
<el-form :model="form" label-position="right" label-width="140px" size="small" :rules="rule"
@ -73,7 +73,10 @@
<el-form-item :label="$t('commons.image')" prop="image">
<el-input v-model="form.image"/>
</el-form-item>
<el-form-item :label="$t('test_resource_pool.usage')" prop="image">
<el-form-item :label="$t('test_resource_pool.backend_listener')" prop="backendListener">
<el-switch v-model="form.backendListener"/>
</el-form-item>
<el-form-item :label="$t('test_resource_pool.usage')" prop="usage">
<el-checkbox :label="$t('commons.api')" v-model="form.api"></el-checkbox>
<el-checkbox :label="$t('commons.performance')" v-model="form.performance"></el-checkbox>
</el-form-item>
@ -161,33 +164,33 @@
</el-row>
<el-table :data="infoList" class="tb-edit" align="center" border highlight-current-row>
<el-table-column
align="center"
prop="ip"
label="IP">
align="center"
prop="ip"
label="IP">
<template v-slot:default="{row}">
<el-input size="small" v-model="row.ip" autocomplete="off"/>
</template>
</el-table-column>
<el-table-column
align="center"
prop="port"
label="Port">
align="center"
prop="port"
label="Port">
<template v-slot:default="{row}">
<el-input-number size="small" v-model="row.port" :min="1" :max="65535"></el-input-number>
</template>
</el-table-column>
<el-table-column
align="center"
prop="monitorPort"
label="Monitor">
align="center"
prop="monitorPort"
label="Monitor">
<template v-slot:default="{row}">
<el-input-number size="small" v-model="row.monitorPort" :min="1" :max="65535"></el-input-number>
</template>
</el-table-column>
<el-table-column
align="center"
prop="maxConcurrency"
:label="$t('test_resource_pool.max_threads')">
align="center"
prop="maxConcurrency"
:label="$t('test_resource_pool.max_threads')">
<template v-slot:default="{row}">
<el-input-number size="small" v-model="row.maxConcurrency" :min="1"
:max="1000000000"></el-input-number>
@ -209,13 +212,13 @@
</div>
<template v-slot:footer>
<ms-dialog-footer
v-if="form.id"
@cancel="dialogVisible = false"
@confirm="updateTestResourcePool()"/>
v-if="form.id"
@cancel="dialogVisible = false"
@confirm="updateTestResourcePool()"/>
<ms-dialog-footer
v-else
@cancel="dialogVisible = false"
@confirm="createTestResourcePool()"/>
v-else
@cancel="dialogVisible = false"
@confirm="createTestResourcePool()"/>
</template>
</el-dialog>
</div>
@ -393,11 +396,11 @@ export default {
this.convertSubmitResources();
this.result = this.$post("/testresourcepool/add", this.form, () => {
this.$message({
type: 'success',
message: this.$t('commons.save_success')
},
this.dialogVisible = false,
this.initTableData());
type: 'success',
message: this.$t('commons.save_success')
},
this.dialogVisible = false,
this.initTableData());
});
} else {
this.$warning(vri.msg);
@ -430,12 +433,12 @@ export default {
this.convertSubmitResources();
this.result = this.$post("/testresourcepool/update", this.form, () => {
this.$message({
type: 'success',
message: this.$t('commons.modify_success')
},
this.dialogVisible = false,
this.initTableData(),
self.loading = false);
type: 'success',
message: this.$t('commons.modify_success')
},
this.dialogVisible = false,
this.initTableData(),
self.loading = false);
});
} else {
this.$warning(vri.msg);
@ -489,10 +492,10 @@ export default {
},
updatePoolStatus(row) {
this.$get('/testresourcepool/update/' + row.id + '/' + row.status)
.then(() => {
this.$success(this.$t('test_resource_pool.status_change_success'));
this.result.loading = false;
}).catch(() => {
.then(() => {
this.$success(this.$t('test_resource_pool.status_change_success'));
this.result.loading = false;
}).catch(() => {
this.$error(this.$t('test_resource_pool.status_change_failed'));
row.status = 'INVALID';
this.result.loading = false;

View File

@ -1736,6 +1736,7 @@ export default {
node_selector_invalid: 'nodeSelector must be JSON',
pod_thread_limit: 'Maximum number of threads per POD',
usage: 'Usage',
backend_listener: 'Backend Listener',
},
system_parameter_setting: {
mailbox_service_settings: 'Mailbox Settings',

View File

@ -1744,6 +1744,7 @@ export default {
node_selector_invalid: 'nodeSelector 必须是有效的JSON',
pod_thread_limit: '单POD最大线程数',
usage: '用途',
backend_listener: '后置监听器',
},
system_parameter_setting: {
mailbox_service_settings: '邮件设置',

View File

@ -1744,6 +1744,7 @@ export default {
node_selector_invalid: 'nodeSelector 必須是有效的JSON',
pod_thread_limit: '單POD最大線程數',
usage: '用途',
backend_listener: '後置監聽器',
},
system_parameter_setting: {
mailbox_service_settings: '郵件設置',