fix(接口自动化):NODE节点获取算法调整,增加动态计算NODE可用内存大小
This commit is contained in:
parent
03b2ee7537
commit
9d044117a6
|
@ -0,0 +1,38 @@
|
||||||
|
package io.metersphere.api.dto;
|
||||||
|
|
||||||
|
import io.metersphere.base.domain.TestResource;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class JvmInfoDTO {
|
||||||
|
/**
|
||||||
|
* JVM内存的空闲空间为
|
||||||
|
*/
|
||||||
|
private long vmFree;
|
||||||
|
/**
|
||||||
|
* JVM内存已用的空间为
|
||||||
|
*/
|
||||||
|
private long vmUse;
|
||||||
|
|
||||||
|
private long vmTotal;
|
||||||
|
/**
|
||||||
|
* JVM总内存空间为
|
||||||
|
*/
|
||||||
|
private long vmMax;
|
||||||
|
|
||||||
|
private int totalThread;
|
||||||
|
|
||||||
|
private TestResource testResource;
|
||||||
|
|
||||||
|
public JvmInfoDTO() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public JvmInfoDTO(long vmTotal, long vmFree, long vmMax, long vmUse, int totalThread) {
|
||||||
|
this.vmFree = vmFree;
|
||||||
|
this.vmTotal = vmTotal;
|
||||||
|
this.vmMax = vmMax;
|
||||||
|
this.vmUse = vmUse;
|
||||||
|
this.totalThread = totalThread;
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,10 +9,7 @@ import io.metersphere.base.domain.JarConfig;
|
||||||
import io.metersphere.base.domain.TestResource;
|
import io.metersphere.base.domain.TestResource;
|
||||||
import io.metersphere.commons.constants.ApiRunMode;
|
import io.metersphere.commons.constants.ApiRunMode;
|
||||||
import io.metersphere.commons.exception.MSException;
|
import io.metersphere.commons.exception.MSException;
|
||||||
import io.metersphere.commons.utils.CommonBeanFactory;
|
import io.metersphere.commons.utils.*;
|
||||||
import io.metersphere.commons.utils.LogUtil;
|
|
||||||
import io.metersphere.commons.utils.SessionUtils;
|
|
||||||
import io.metersphere.commons.utils.UrlTestUtils;
|
|
||||||
import io.metersphere.config.JmeterProperties;
|
import io.metersphere.config.JmeterProperties;
|
||||||
import io.metersphere.dto.BaseSystemConfigDTO;
|
import io.metersphere.dto.BaseSystemConfigDTO;
|
||||||
import io.metersphere.dto.NodeDTO;
|
import io.metersphere.dto.NodeDTO;
|
||||||
|
@ -47,10 +44,7 @@ import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.util.Arrays;
|
import java.util.*;
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class JMeterService {
|
public class JMeterService {
|
||||||
|
@ -216,6 +210,42 @@ public class JMeterService {
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<Object> getZipJar() {
|
||||||
|
List<Object> jarFiles = new LinkedList<>();
|
||||||
|
// jar 包
|
||||||
|
JarConfigService jarConfigService = CommonBeanFactory.getBean(JarConfigService.class);
|
||||||
|
List<JarConfig> jars = jarConfigService.list();
|
||||||
|
List<File> files = new ArrayList<>();
|
||||||
|
|
||||||
|
jars.forEach(jarConfig -> {
|
||||||
|
String path = jarConfig.getPath();
|
||||||
|
File file = new File(path);
|
||||||
|
if (file.isDirectory() && !path.endsWith("/")) {
|
||||||
|
file = new File(path + "/");
|
||||||
|
}
|
||||||
|
files.add(file);
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
File file = CompressUtils.zipFiles(UUID.randomUUID().toString() + ".zip", files);
|
||||||
|
FileSystemResource resource = new FileSystemResource(file);
|
||||||
|
byte[] fileByte = this.fileToByte(file);
|
||||||
|
if (fileByte != null) {
|
||||||
|
ByteArrayResource byteArrayResource = new ByteArrayResource(fileByte) {
|
||||||
|
@Override
|
||||||
|
public String getFilename() throws IllegalStateException {
|
||||||
|
return resource.getFilename();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
jarFiles.add(byteArrayResource);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
return jarFiles;
|
||||||
|
}
|
||||||
|
|
||||||
private List<Object> getJar() {
|
private List<Object> getJar() {
|
||||||
List<Object> jarFiles = new LinkedList<>();
|
List<Object> jarFiles = new LinkedList<>();
|
||||||
// jar 包
|
// jar 包
|
||||||
|
@ -321,6 +351,11 @@ public class JMeterService {
|
||||||
HttpEntity<MultiValueMap<String, Object>> request = new HttpEntity<>(postParameters, headers);
|
HttpEntity<MultiValueMap<String, Object>> request = new HttpEntity<>(postParameters, headers);
|
||||||
|
|
||||||
String result = restTemplate.postForObject(uri, request, String.class);
|
String result = restTemplate.postForObject(uri, request, String.class);
|
||||||
|
// 删除零时压缩文件
|
||||||
|
/*if (CollectionUtils.isNotEmpty(jarFiles)) {
|
||||||
|
ByteArrayResource resource = (ByteArrayResource) jarFiles.get(0);
|
||||||
|
CompressUtils.deleteFile(resource.getFile().getPath());
|
||||||
|
}*/
|
||||||
if (result == null || !StringUtils.equals("SUCCESS", result)) {
|
if (result == null || !StringUtils.equals("SUCCESS", result)) {
|
||||||
MSException.throwException("执行失败:" + result);
|
MSException.throwException("执行失败:" + result);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package io.metersphere.api.jmeter;
|
package io.metersphere.api.jmeter;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
|
import io.metersphere.api.dto.JvmInfoDTO;
|
||||||
import io.metersphere.base.domain.TestResource;
|
import io.metersphere.base.domain.TestResource;
|
||||||
import io.metersphere.base.domain.TestResourceExample;
|
import io.metersphere.base.domain.TestResourceExample;
|
||||||
import io.metersphere.base.domain.TestResourcePool;
|
import io.metersphere.base.domain.TestResourcePool;
|
||||||
|
@ -7,8 +9,10 @@ import io.metersphere.base.domain.TestResourcePoolExample;
|
||||||
import io.metersphere.base.mapper.TestResourceMapper;
|
import io.metersphere.base.mapper.TestResourceMapper;
|
||||||
import io.metersphere.base.mapper.TestResourcePoolMapper;
|
import io.metersphere.base.mapper.TestResourcePoolMapper;
|
||||||
import io.metersphere.commons.exception.MSException;
|
import io.metersphere.commons.exception.MSException;
|
||||||
|
import io.metersphere.dto.NodeDTO;
|
||||||
import org.apache.commons.collections.CollectionUtils;
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -20,25 +24,50 @@ public class ResourcePoolCalculation {
|
||||||
TestResourcePoolMapper testResourcePoolMapper;
|
TestResourcePoolMapper testResourcePoolMapper;
|
||||||
@Resource
|
@Resource
|
||||||
TestResourceMapper testResourceMapper;
|
TestResourceMapper testResourceMapper;
|
||||||
|
@Resource
|
||||||
|
private RestTemplate restTemplate;
|
||||||
|
|
||||||
|
private static final String BASE_URL = "http://%s:%d";
|
||||||
|
|
||||||
|
private JvmInfoDTO getNodeJvmInfo(String uri) {
|
||||||
|
return restTemplate.getForObject(uri, JvmInfoDTO.class);
|
||||||
|
}
|
||||||
|
|
||||||
public TestResource getPool() {
|
public TestResource getPool() {
|
||||||
// 获取可以执行的资源池
|
// 获取可以执行的资源池
|
||||||
TestResourcePoolExample example = new TestResourcePoolExample();
|
TestResourcePoolExample example = new TestResourcePoolExample();
|
||||||
example.createCriteria().andStatusEqualTo("VALID").andTypeEqualTo("NODE");
|
example.createCriteria().andStatusEqualTo("VALID").andTypeEqualTo("NODE").andNameEqualTo("赵勇资源池");
|
||||||
List<TestResourcePool> pools = testResourcePoolMapper.selectByExample(example);
|
List<TestResourcePool> pools = testResourcePoolMapper.selectByExample(example);
|
||||||
// 暂时随机获取一个正常状态NODE
|
// 按照NODE节点的可用内存空间大小排序
|
||||||
TestResource testResource = null;
|
JvmInfoDTO jvmInfoDTO = null;
|
||||||
if (CollectionUtils.isNotEmpty(pools)) {
|
if (CollectionUtils.isNotEmpty(pools)) {
|
||||||
List<String> poolIds = pools.stream().map(pool -> pool.getId()).collect(Collectors.toList());
|
List<String> poolIds = pools.stream().map(pool -> pool.getId()).collect(Collectors.toList());
|
||||||
TestResourceExample resourceExample = new TestResourceExample();
|
TestResourceExample resourceExample = new TestResourceExample();
|
||||||
resourceExample.createCriteria().andTestResourcePoolIdIn(poolIds);
|
resourceExample.createCriteria().andTestResourcePoolIdIn(poolIds);
|
||||||
List<TestResource> testResources = testResourceMapper.selectByExampleWithBLOBs(resourceExample);
|
List<TestResource> testResources = testResourceMapper.selectByExampleWithBLOBs(resourceExample);
|
||||||
int index = (int) (Math.random() * testResources.size());
|
for (TestResource testResource : testResources) {
|
||||||
testResource = testResources.get(index);
|
String configuration = testResource.getConfiguration();
|
||||||
}
|
NodeDTO node = JSON.parseObject(configuration, NodeDTO.class);
|
||||||
if (testResource == null) {
|
String nodeIp = node.getIp();
|
||||||
MSException.throwException("未获取到资源池,请检查配置【系统设置-系统-测试资源池】");
|
Integer port = node.getPort();
|
||||||
|
String uri = String.format(BASE_URL + "/jmeter/getJvmInfo", nodeIp, port);
|
||||||
|
JvmInfoDTO nodeJvm = this.getNodeJvmInfo(uri);
|
||||||
|
if (nodeJvm == null) {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
// 优先取资源充足的节点,如果当前节点资源超过1G就不需要在排序了
|
||||||
|
if (nodeJvm.getVmFree() > 1024) {
|
||||||
return testResource;
|
return testResource;
|
||||||
}
|
}
|
||||||
|
if (jvmInfoDTO == null || jvmInfoDTO.getVmFree() < nodeJvm.getVmFree()) {
|
||||||
|
jvmInfoDTO = nodeJvm;
|
||||||
|
jvmInfoDTO.setTestResource(testResource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (jvmInfoDTO == null || jvmInfoDTO.getTestResource() == null) {
|
||||||
|
MSException.throwException("未获取到资源池,请检查配置【系统设置-系统-测试资源池】");
|
||||||
|
}
|
||||||
|
return jvmInfoDTO.getTestResource();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -969,11 +969,12 @@ public class ApiAutomationService {
|
||||||
try {
|
try {
|
||||||
HashTree hashTree = generateHashTree(apiScenarios, request, reportIds);
|
HashTree hashTree = generateHashTree(apiScenarios, request, reportIds);
|
||||||
jMeterService.runSerial(JSON.toJSONString(reportIds), hashTree, request.getReportId(), runMode, request.getConfig());
|
jMeterService.runSerial(JSON.toJSONString(reportIds), hashTree, request.getReportId(), runMode, request.getConfig());
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
// jMeterService.runTest(JSON.toJSONString(reportIds), hashTree, runMode, false, request.getConfig());
|
// jMeterService.runTest(JSON.toJSONString(reportIds), hashTree, runMode, false, request.getConfig());
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
LogUtil.error(e.getMessage());
|
||||||
|
MSException.throwException(e.getMessage());
|
||||||
|
}
|
||||||
return request.getId();
|
return request.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package io.metersphere.commons.utils;
|
package io.metersphere.commons.utils;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.*;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.util.List;
|
||||||
import java.util.zip.*;
|
import java.util.zip.*;
|
||||||
|
|
||||||
public class CompressUtils {
|
public class CompressUtils {
|
||||||
|
@ -37,6 +37,115 @@ public class CompressUtils {
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final static String ZIP_PATH = "/opt/metersphere/data/tmp/";
|
||||||
|
|
||||||
|
public static File getFile(String fileName) throws IOException {
|
||||||
|
// 创建文件对象
|
||||||
|
File file;
|
||||||
|
if (ZIP_PATH != null && !ZIP_PATH.equals("")) {
|
||||||
|
file = new File(ZIP_PATH, fileName);
|
||||||
|
} else {
|
||||||
|
file = new File(fileName);
|
||||||
|
}
|
||||||
|
if (!file.exists()) {
|
||||||
|
file.createNewFile();
|
||||||
|
}
|
||||||
|
// 返回文件
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FileOutputStream getFileStream(File file) throws FileNotFoundException {
|
||||||
|
return new FileOutputStream(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void zipFile(File file, ZipOutputStream zipOutputStream) throws IOException {
|
||||||
|
if (file.exists()) {
|
||||||
|
if (file.isFile()) {
|
||||||
|
FileInputStream fis = new FileInputStream(file);
|
||||||
|
BufferedInputStream bis = new BufferedInputStream(fis);
|
||||||
|
ZipEntry entry = new ZipEntry(file.getName());
|
||||||
|
zipOutputStream.putNextEntry(entry);
|
||||||
|
|
||||||
|
final int MAX_BYTE = 10 * 1024 * 1024; // 最大流为10MB
|
||||||
|
long streamTotal = 0; // 接收流的容量
|
||||||
|
int streamNum = 0; // 需要分开的流数目
|
||||||
|
int leaveByte = 0; // 文件剩下的字符数
|
||||||
|
byte[] buffer; // byte数据接受文件的数据
|
||||||
|
|
||||||
|
streamTotal = bis.available(); // 获取流的最大字符数
|
||||||
|
streamNum = (int) Math.floor(streamTotal / MAX_BYTE);
|
||||||
|
leaveByte = (int) (streamTotal % MAX_BYTE);
|
||||||
|
|
||||||
|
if (streamNum > 0) {
|
||||||
|
for (int i = 0; i < streamNum; i++) {
|
||||||
|
buffer = new byte[MAX_BYTE];
|
||||||
|
bis.read(buffer, 0, MAX_BYTE);
|
||||||
|
zipOutputStream.write(buffer, 0, MAX_BYTE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 写入剩下的流数据
|
||||||
|
buffer = new byte[leaveByte];
|
||||||
|
bis.read(buffer, 0, leaveByte); // 读入流
|
||||||
|
zipOutputStream.write(buffer, 0, leaveByte); // 写入流
|
||||||
|
zipOutputStream.closeEntry(); // 关闭当前的zip entry
|
||||||
|
|
||||||
|
// 关闭输入流
|
||||||
|
bis.close();
|
||||||
|
fis.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将多个文件压缩
|
||||||
|
*
|
||||||
|
* @param fileList 待压缩的文件列表
|
||||||
|
* @param zipFileName 压缩文件名
|
||||||
|
* @return 返回压缩好的文件
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public static File zipFiles(String zipFileName, List<File> fileList) throws IOException {
|
||||||
|
File zipFile = getFile(zipFileName);
|
||||||
|
// 文件输出流
|
||||||
|
FileOutputStream outputStream = getFileStream(zipFile);
|
||||||
|
// 压缩流
|
||||||
|
ZipOutputStream zipOutputStream = new ZipOutputStream(outputStream);
|
||||||
|
|
||||||
|
int size = fileList.size();
|
||||||
|
// 压缩列表中的文件
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
File file = fileList.get(i);
|
||||||
|
zipFile(file, zipOutputStream);
|
||||||
|
}
|
||||||
|
// 关闭压缩流、文件流
|
||||||
|
zipOutputStream.close();
|
||||||
|
outputStream.close();
|
||||||
|
return zipFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void deleteFile(String delPath) {
|
||||||
|
try {
|
||||||
|
File file = new File(delPath);
|
||||||
|
if (!file.isDirectory()) {
|
||||||
|
file.delete();
|
||||||
|
} else if (file.isDirectory()) {
|
||||||
|
String[] fileList = file.list();
|
||||||
|
for (int i = 0; i < fileList.length; i++) {
|
||||||
|
File delfile = new File(delPath + File.separator + fileList[i]);
|
||||||
|
if (!delfile.isDirectory()) {
|
||||||
|
delfile.delete();
|
||||||
|
} else if (delfile.isDirectory()) {
|
||||||
|
deleteFile(delPath + File.separator + fileList[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file.delete();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* Zip解压
|
* Zip解压
|
||||||
*
|
*
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit e50f0463826ac4d7837ea3a237333827774a1b19
|
Subproject commit e7709b9a340394e78610b91105b2cec0f1b8289d
|
Loading…
Reference in New Issue