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.commons.constants.ApiRunMode;
|
||||
import io.metersphere.commons.exception.MSException;
|
||||
import io.metersphere.commons.utils.CommonBeanFactory;
|
||||
import io.metersphere.commons.utils.LogUtil;
|
||||
import io.metersphere.commons.utils.SessionUtils;
|
||||
import io.metersphere.commons.utils.UrlTestUtils;
|
||||
import io.metersphere.commons.utils.*;
|
||||
import io.metersphere.config.JmeterProperties;
|
||||
import io.metersphere.dto.BaseSystemConfigDTO;
|
||||
import io.metersphere.dto.NodeDTO;
|
||||
|
@ -47,10 +44,7 @@ import java.io.File;
|
|||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.*;
|
||||
|
||||
@Service
|
||||
public class JMeterService {
|
||||
|
@ -216,6 +210,42 @@ public class JMeterService {
|
|||
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() {
|
||||
List<Object> jarFiles = new LinkedList<>();
|
||||
// jar 包
|
||||
|
@ -321,6 +351,11 @@ public class JMeterService {
|
|||
HttpEntity<MultiValueMap<String, Object>> request = new HttpEntity<>(postParameters, headers);
|
||||
|
||||
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)) {
|
||||
MSException.throwException("执行失败:" + result);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
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.TestResourceExample;
|
||||
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.TestResourcePoolMapper;
|
||||
import io.metersphere.commons.exception.MSException;
|
||||
import io.metersphere.dto.NodeDTO;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
@ -20,25 +24,50 @@ public class ResourcePoolCalculation {
|
|||
TestResourcePoolMapper testResourcePoolMapper;
|
||||
@Resource
|
||||
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() {
|
||||
// 获取可以执行的资源池
|
||||
TestResourcePoolExample example = new TestResourcePoolExample();
|
||||
example.createCriteria().andStatusEqualTo("VALID").andTypeEqualTo("NODE");
|
||||
example.createCriteria().andStatusEqualTo("VALID").andTypeEqualTo("NODE").andNameEqualTo("赵勇资源池");
|
||||
List<TestResourcePool> pools = testResourcePoolMapper.selectByExample(example);
|
||||
// 暂时随机获取一个正常状态NODE
|
||||
TestResource testResource = null;
|
||||
// 按照NODE节点的可用内存空间大小排序
|
||||
JvmInfoDTO jvmInfoDTO = null;
|
||||
if (CollectionUtils.isNotEmpty(pools)) {
|
||||
List<String> poolIds = pools.stream().map(pool -> pool.getId()).collect(Collectors.toList());
|
||||
TestResourceExample resourceExample = new TestResourceExample();
|
||||
resourceExample.createCriteria().andTestResourcePoolIdIn(poolIds);
|
||||
List<TestResource> testResources = testResourceMapper.selectByExampleWithBLOBs(resourceExample);
|
||||
int index = (int) (Math.random() * testResources.size());
|
||||
testResource = testResources.get(index);
|
||||
}
|
||||
if (testResource == null) {
|
||||
MSException.throwException("未获取到资源池,请检查配置【系统设置-系统-测试资源池】");
|
||||
for (TestResource testResource : testResources) {
|
||||
String configuration = testResource.getConfiguration();
|
||||
NodeDTO node = JSON.parseObject(configuration, NodeDTO.class);
|
||||
String nodeIp = node.getIp();
|
||||
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;
|
||||
}
|
||||
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 {
|
||||
HashTree hashTree = generateHashTree(apiScenarios, request, reportIds);
|
||||
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());
|
||||
|
||||
} catch (Exception e) {
|
||||
LogUtil.error(e.getMessage());
|
||||
MSException.throwException(e.getMessage());
|
||||
}
|
||||
return request.getId();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package io.metersphere.commons.utils;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.*;
|
||||
import java.util.List;
|
||||
import java.util.zip.*;
|
||||
|
||||
public class CompressUtils {
|
||||
|
@ -37,6 +37,115 @@ public class CompressUtils {
|
|||
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解压
|
||||
*
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit e50f0463826ac4d7837ea3a237333827774a1b19
|
||||
Subproject commit e7709b9a340394e78610b91105b2cec0f1b8289d
|
Loading…
Reference in New Issue