做性能测试时,统计性能数据分为被压系统的数据和被压系统所在机器的数据,被压系统所在机器的数据主要包括 CPU 利用率、内存使用率、网络 IO、磁盘 IO 和负载 load;nGrinder 默认只收集 CPU, Memory, Received Byte/s, Sent Byte Per Secode/s,同时支持自定义数据收集,之前介绍过相关内容:nGrinder 对监控机器收集自定义数据及源码分析。
展示自定义数据需定时的写文件,比较麻烦;本文将介绍如何改动源码,直接展示 CPU 利用率、内存使用率、网络 IO、磁盘 IO 和负载 load 这 5 类数据。
nGrinder 是通过 sigar api 统计机器性能数据,sigar 项目主页:https://support.hyperic.com/display/SIGAR/Home, sigar api :http://cpansearch.perl.org/src/DOUGM/hyperic-sigar-1.6.3-src/docs/javadoc/org/hyperic/sigar/package-summary.html。
ngrinder-core 中 package:org.ngrinder.monitor.share.domain 下 SystemInfo 类定义了收集的数据类型,新增 load、read、write 和 memUsedPercentage 数据字段:
/**
* modify by hugang
*/
public class SystemInfo extends MonitorInfo implements Serializable {
private static final long serialVersionUID = -2995334644975166549L;
/**
* Header field of monitor status fields.
*/
// 新增load、磁盘IO和内存使用率
public static final String HEADER = "ip,system,collectTime,freeMemory,"
+ "totalMemory,cpuUsedPercentage,receivedPerSec,sentPerSec,load,read,write,memUsedPercentage";
public boolean isParsed() {
return true;
}
/**
* Enum for the system type, linux or windows.
*/
public enum System {
LINUX, WINDOW
}
private System system;
// 网络IO
protected BandWidth bandWidth;
private long totalCpuValue;
private long idleCpuValue;
private long freeMemory;
private long totalMemory;
private float cpuUsedPercentage;
// load
private double load;
// 磁盘读
private long read;
// 磁盘写
private long write;
// 内存使用率
private double memUsedPercentage;
...
set()、get()方法
public void parse(CompositeData cd) {
// 新增
this.load = getDouble(cd, "load");
this.write = getLong(cd, "write");
this.read = getLong(cd, "read");
this.memUsedPercentage = getDouble(cd, "memUsedPercentage");
}
public String toRecordString() {
StringBuilder sb = new StringBuilder();
sb.append(ip).append(",").append(system).append(",");
sb.append(DateUtils.getCollectTimeInLong(new Date(getCollectTime()))).append(",").append(freeMemory).append(",");
sb.append(totalMemory).append(",").append(cpuUsedPercentage);
if (bandWidth != null) {
sb.append(",").append(bandWidth.getReceivedPerSec()).append(",").append(bandWidth.getSentPerSec());
}
// 新类型load、read、write、memUsedPercentage数据拼接
sb.append(",").append(load).append(",").append(read).append(",").append(write).append(",").append(memUsedPercentage);
if (customValues != null) {
sb.append(",").append(customValues);
}
return sb.toString();
}
public static class NullSystemInfo extends SystemInfo {
private static final NullSystemInfo instance = new NullSystemInfo();
public static SystemInfo getNullSystemInfo() {
return instance;
}
/**
* Return the empty record string.
*
* @return null filled record string.
* @see #toRecordString()
*/
@Override
public String toRecordString() {
StringBuilder sb = new StringBuilder();
sb.append("null").append(",").append("null").append(",");
sb.append("null").append(",").append("null").append(",");
sb.append("null").append(",").append("null");
if (bandWidth != null) {
sb.append(",").append("null").append(",").append("null");
}
// 拼接新类型数据为"null"
sb.append(",").append("null").append(",").append("null").append(",").append("null");
if (customValues != null) {
int valueCount = StringUtils.countMatches(customValues, ",") + 1;
for (int i = 0; i < valueCount; i++) {
sb.append(",").append("null");
}
}
return sb.toString();
}
public boolean isParsed() {
return false;
}
}
}
ngrinder-core 中 package:org.ngrinder.monitor.collector 下 SystemDataCollector 类中 execute() 方法实现了系统数据的收集:
/**
* modify by hugang
*/
public synchronized SystemInfo execute() {
SystemInfo systemInfo = new SystemInfo();
systemInfo.setCollectTime(System.currentTimeMillis());
try {
BandWidth networkUsage = getNetworkUsage();
BandWidth bandWidth = networkUsage.adjust(prev.getBandWidth());
systemInfo.setBandWidth(bandWidth);
// getCombined()代表 Sum of User + Sys + Nice + Wait
systemInfo.setCPUUsedPercentage((float) sigar.getCpuPerc().getCombined() * 100);
Cpu cpu = sigar.getCpu();
systemInfo.setTotalCpuValue(cpu.getTotal());
systemInfo.setIdleCpuValue(cpu.getIdle());
Mem mem = sigar.getMem();
systemInfo.setTotalMemory(mem.getTotal() / 1024L);
systemInfo.setFreeMemory(mem.getActualFree() / 1024L);
// 新增load、磁盘IO和内存使用率
// The system load averages for the past 1, 5, and 15 minutes.
double load = 0;
long read = 0l;
long write = 0l;
load = sigar.getLoadAverage()[0];
//LOGGER.info("monitor system load:{}", load);
// 所有的文件系统
FileSystem[] fileSystems = sigar.getFileSystemList();
// 获取本地文件系统
List<String> localDevNames = new ArrayList<String>();
for(FileSystem fileSystem : fileSystems) {
if(fileSystem.getType() == FileSystem.TYPE_LOCAL_DISK) {
localDevNames.add(fileSystem.getDevName());
}
}
for(String localDevName : localDevNames) {
read += sigar.getDiskUsage(localDevName).getReadBytes();
write += sigar.getDiskUsage(localDevName).getWriteBytes();
}
//LOGGER.info("monitor system read:{}, write:{}", read, write);
systemInfo.setLoad(load);
systemInfo.setRead(read / 1024L);
systemInfo.setWrite(write / 1024L);
systemInfo.setMemUsedPercentage(mem.getUsedPercent());
systemInfo.setSystem(OperatingSystem.IS_WIN32 ? SystemInfo.System.WINDOW : SystemInfo.System.LINUX);
systemInfo.setCustomValues(getCustomMonitorData());
} catch (Throwable e) {
LOGGER.error("Error while getting system perf data:{}", e.getMessage());
LOGGER.debug("Error trace is ", e);
}
prev = systemInfo;
return systemInfo;
}
ngrinder-controller 下 src/main/webapp/WEB-INF/ftl/perftest/detail_report/monitor.ftl 定义了系统数据的可视化,修改如下:
<#setting number_format="computer">
<#import "../../common/spring.ftl" as spring/>
<div class="page-header page-header">
<h4>Monitor</h4>
</div>
<h6 id="cpu_usage_chart_header">CPU利用率(User + Sys + Nice + Wait),建议值:小于75%</h6>
<div class="chart" id="cpu_usage_chart"></div>
<h6 id="mem_usage_chart_header">Memory使用率,建议值:小于80%</h6>
<div class="chart" id="mem_usage_chart"></div>
<h6 id="received_byte_per_sec_chart_header">Received Byte Per Second</h6>
<div class="chart" id="received_byte_per_sec_chart"></div>
<h6 id="sent_byte_per_sec_chart_header">Sent Byte Per Second</h6>
<div class="chart" id="sent_byte_per_sec_chart"></div>
<h6 id="custom_monitor_chart_1_header">Load-average(one-minute)</h6>
<div class="chart" id="custom_monitor_chart_1"></div>
<h6 id="custom_monitor_chart_2_header">Physical Disk readBytes(total number of physical disk reads)</h6>
<div class="chart" id="custom_monitor_chart_2"></div>
<h6 id="custom_monitor_chart_3_header">Physical Disk writesBytes(total number of physical disk writes)</h6>
<div class="chart" id="custom_monitor_chart_3"></div>
<script>
//@ sourceURL=/perftest/detail_report/monitor
function getMonitorDataAndDraw(testId, targetIP) {
var ajaxObj = new AjaxObj("/perftest/api/" + testId + "/monitor");
ajaxObj.params = {
targetIP: targetIP,
imgWidth: parseInt($("#cpu_usage_chart").width())
};
ajaxObj.success = function (data) {
var interval = data.chartInterval;
// modify by hugang
// data.customData1为load, data.customData2为磁盘读, data.customData3为磁盘写
drawChart('cpu_usage_chart', [data.cpu], formatPercentage, interval);
// 调整为内存使用率
drawChart('mem_usage_chart', [data.customData4], formatPercentage, interval);
// load
drawChart("custom_monitor_chart_1", [data.customData1], formatDouble, interval);
drawChart("received_byte_per_sec_chart", [data.received], formatNetwork, interval);
drawChart("sent_byte_per_sec_chart", [data.sent], formatNetwork, interval);
// 磁盘读
drawChart("custom_monitor_chart_2", [data.customData2], formatMemory, interval);
// 磁盘写
drawChart("custom_monitor_chart_3", [data.customData3], formatMemory, interval);
createChartExportButton("<@spring.message "perfTest.report.exportImg.button"/>", "<@spring.message "perfTest.report.exportImg.title"/>");
};
ajaxObj.call();
}
function drawChart(id, data, yFormat, interval) {
return new Chart(id, data, interval, {yAxisFormatter: yFormat}).plot();
}
function drawOptionalChart(id, data, interval, labels) {
var result = drawChart(id, data, interval, labels);
if (result.isEmpty()) {
$("#" + id).hide();
$("#" + id + "_header").hide();
}
}
getMonitorDataAndDraw(${id}, "${targetIP}");
</script>
对工程进行打包:
hugangdeMacBook-Pro:CPC hugang$ mvn clean -Dmaven.test.skip=true package
部署新 war 包,服务正常启动后;需重新下载监控包,因为依赖的 ngrinder-core.jar 包有改动,重新启动监控服务后,执行性能测试任务。
系统监控数据文件:/root/.ngrinder/perftest/0_999/${任务id}/report
下 monitor_system_${ip}.data
新增 load、read、write 和 memUsedPercentage。