fix(接口自动化):NODE节点获取算法调整,增加动态计算NODE可用内存大小

This commit is contained in:
fit2-zhao 2021-04-27 14:47:13 +08:00 committed by fit2-zhao
parent 03b2ee7537
commit 9d044117a6
6 changed files with 236 additions and 24 deletions

View File

@ -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;
}
}

View File

@ -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,8 +351,13 @@ public class JMeterService {
HttpEntity<MultiValueMap<String, Object>> request = new HttpEntity<>(postParameters, headers);
String result = restTemplate.postForObject(uri, request, String.class);
if (result == null || !StringUtils.equals("SUCCESS",result)) {
MSException.throwException("执行失败:"+ result);
// 删除零时压缩文件
/*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);
}
} catch (Exception e) {
e.printStackTrace();

View File

@ -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);
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 (testResource == null) {
if (jvmInfoDTO == null || jvmInfoDTO.getTestResource() == null) {
MSException.throwException("未获取到资源池,请检查配置【系统设置-系统-测试资源池】");
}
return testResource;
return jvmInfoDTO.getTestResource();
}
}

View File

@ -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());
// jMeterService.runTest(JSON.toJSONString(reportIds), hashTree, runMode, false, request.getConfig());
} catch (Exception e) {
LogUtil.error(e.getMessage());
MSException.throwException(e.getMessage());
}
return request.getId();
}

View File

@ -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