nGrinder对监控机器收集自定义数据及源码分析

转载:https://blog.csdn.net/neven7/article/details/50782451

0.背景

性能测试工具nGrinder支持在无需修改源码的情况下,对目标服务器收集自定义数据,最多支持5类;

在性能测试详细报告页,目标服务器->你的机器ip便签页下,默认只收集CPU, Memory, Received Byte/s, Sent Byte Per Secode/s等4类数据;

可能你还需要监控其它的性能统计数据,用于分析(比如load, Full Gc);本文先介绍实现方法;再分析nGrinder源码,看它是怎么实现的。

1.实现

1-1. 安装monitor

在你的nGrinder系统下,下载监控

这里写图片描述

安装到你测试服务所在的机器,解压tar包,执行sh run_monitor_bg.sh;

其实脚本是启了个java服务,以monitor模式启动;

之前介绍过Agent有2种模式:

gent mode: 运行进程和线程,压测目标服务;

monitor mode: 监控目标系统性能(cpu/memory)。

[root@10 ngrinder-monitor]# cat run_monitor.sh
#!/bin/sh
curpath=`dirname $0`
cd ${curpath} java -server -cp "lib/*" org.ngrinder.NGrinderAgentStarter --mode monitor --command run $@ 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

Agent的home路径为/root/.ngrinder_agent,你在执行sh run_monitor_bg.sh默认获取的配置信息为/root/.ngrinder_agent/agent.conf; 如果加上-o,sh run_monitor_bg.sh 读取你安装monitor目录下的__agent.conf, 该配置文件定义了Agent的模式,ip, 端口。

[root@10 .ngrinder_agent]# cat agent.conf 
common.start_mode=monitor
#If you want to monitor bind to the different local ip not automatically selected ip. Specify below field. #monitor.binding_port=hostname_or_ip monitor.binding_port=13243 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

自定义数据需放在/root/.ngrinder_agent/monitor/custom.data文件里,格式如下:

类型1数据,类型2数据,类型3数据,类型4数据,类型5数据
  • 1

最多支持5类,每类数据用“,”分隔,注意的是: 数据是实时的写文件,不是累积数据到文件中(类似shell中的>, 不是>>),即同一时刻,只有一行数据。

1-2. 定制收集脚本

以收集load和full GC为例:

[root@10 bin]# cat updateCustomData.sh 
#!/bin/sh
#@author hugang

customDataRoot=/root/.ngrinder_agent/monitor/custom.data;
# 获取load信息 load=`/bin/cat /proc/loadavg | awk '{print $1}'`; # 获取full gc count if [[ $1 -gt 0 ]]; then fgc=`jstat -gcutil $1 | tail -1 | awk '{print $8}'`; echo $load,$fgc > $customDataRoot; else echo $load > $customDataRoot; fi; 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

开始性能测试时,每秒去执行该脚本,收集数据到custom.data中:

 watch -n 1 sh updateCustomData.sh 5528
  • 1

5528为需监控java服务进程pid;

当你性能测试结束后,monitor收集的数据会放到/root/.ngrinder/perftest/0_999/{test_id}/report/monitor_system_{test_id}/report/monitor_system_{ip}.data文件中:

[root@10 report]# cat monitor_system_10.13.1.139.data 
ip,system,collectTime,freeMemory,totalMemory,cpuUsedPercentage,receivedPerSec,sentPerSec,customValues
10.13.1.139,LINUX,20160302151441,97102768,132112072,26.895683,32954,27897,4.93,49 10.13.1.139,LINUX,20160302151443,97075896,132112072,30.513468,45702,32306,4.93,49 10.13.1.139,LINUX,20160302151445,97034772,132112072,30.411074,110306,65391,5.02,49 10.13.1.139,LINUX,20160302151447,96972504,132112072,22.073017,84813,57503,5.02,49 ... 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

1-3.结果展示

这里写图片描述

2.源码分析

nGrinder使用Sigar工具(https://support.hyperic.com/display/SIGAR/Home)收集系统信息,该工具可以收集以下数据:

System memory, swap, cpu, load average, uptime, logins
Per-process memory, cpu, credential info, state, arguments, environment, open files File system detection and metrics Network interface detection, configuration info and metrics TCP and UDP connection tables Network route table 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

sigar工具(http://download.csdn.net/download/neven7/9450930)示例:

[root@10 testsigar]# ls
libsigar-amd64-linux.so  sigar-1.6.4.jar sigar-1.6.4.jar.zip [root@10 testsigar]# [root@10 testsigar]# java -jar ./sigar-1.6.4.jar sigar> free total used free Mem: 132112072 96855372 35256700 -/+ buffers/cache: 34855500 97256572 Swap: 8388600 264980 8123620 RAM: 129016MB sigar> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

收集系统数据的java文件为: 
ngrinder-core/src/main/java/org/ngrinder/monitor/collector/SystemDataCollector.java

继承和实现关系:

SystemDataCollector extends DataCollector 

DataCollector implements Runnable
  • 1
  • 2
  • 3
  • 4

SystemDataCollector的线程执行体:

public void run() {
        // 初始化sigar
        initSigar();
        SystemMonitoringData systemMonitoringData = (SystemMonitoringData) getMXBean(SYSTEM);
        // execute()通过sigar api获取系统信息 systemMonitoringData.setSystemInfo(execute()); } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

execute()获取系统信息SystemInfo(System info object to save date collected by monitor):

/**
     * Execute the collector to get the system info model.
     *
     * @return SystemInfo in current time
     */
    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); 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); 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; } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

其中:getCustomMonitorData()获取自定义数据,读取custom.data文件中一行数据

private String getCustomMonitorData() {
        if (customDataFile != null && customDataFile.exists()) {
            BufferedReader customDataFileReader = null; try { customDataFileReader = new BufferedReader(new FileReader(customDataFile)); return customDataFileReader.readLine(); // these data will be parsed at // monitor client side. } catch (IOException e) { // Error here is very natural LOGGER.debug("Error to read custom monitor data", e); } finally { IOUtils.closeQuietly(customDataFileReader); } } return prev.getCustomValues(); } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

综上:类SystemDataCollector作用就是作为线程执行体,线程每次执行通过sigar获取系统信息:SystemInfo,赋值给SystemMonitoringData成员变量SystemInfo。

前面介绍启动monitor时,其实是执行了org.ngrinder.NGrinderAgentStarter类,我们再分析下该文件,ngrinder-core/src/main/java/org/ngrinder/NGrinderAgentStarter.java

/**
     * Agent starter.
     *
     * @param args arguments
     */
    public static void main(String[] args) { NGrinderAgentStarter starter = new NGrinderAgentStarter(); final NGrinderAgentStarterParam param = new NGrinderAgentStarterParam(); checkJavaVersion(); JCommander commander = new JCommander(param); commander.setProgramName("ngrinder-agent"); commander.setAcceptUnknownOptions(true); try { commander.parse(args); } catch (Exception e) { LOG.error(e.getMessage()); return; } final List<String> unknownOptions = commander.getUnknownOptions(); modeParam = param.getModeParam(); modeParam.parse(unknownOptions.toArray(new String[unknownOptions.size()])); if (modeParam.version != null) { System.out.println("nGrinder v" + getStaticVersion()); return; } if (modeParam.help != null) { modeParam.usage(); return; } System.getProperties().putAll(modeParam.params); starter.init(); final String startMode = modeParam.name(); if ("stop".equalsIgnoreCase(param.command)) { starter.stopProcess(startMode); System.out.println("Stop the " + startMode); return; } starter.checkDuplicatedRun(startMode); if (startMode.equalsIgnoreCase("agent")) { starter.startAgent(); } else if (startMode.equalsIgnoreCase("monitor")) { starter.startMonitor(); } else { staticPrintHelpAndExit("Invalid agent.conf, '--mode' must be set as 'monitor' or 'agent'."); } } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51

monitor模式执行该方法:starter.startMonitor()


     /**
     * Start the performance monitor.
     */
    public void startMonitor() {
        printLog("***************************************************"); printLog("* Start nGrinder Monitor... "); printLog("***************************************************"); try { MonitorServer.getInstance().init(agentConfig); MonitorServer.getInstance().start(); } catch (Exception e) { LOG.error("ERROR: {}", e.getMessage()); printHelpAndExit("Error while starting Monitor", e); } } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

MonitorServer.getInstance().start():

    /**
     * Start monitoring.
     *
     * @throws IOException exception
     */
    public void start() throws IOException { if (!isRunning()) { jmxServer.start(); DataCollectManager.getInstance().init(agentConfig); DataCollectManager.getInstance().start(); isRunning = true; } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

DataCollectManager.getInstance().start();

    /**
     * start a scheduler for the data collector jobs.
     */
    public void start() {
        int collectorCount = MXBeanStorage.getInstance().getSize(); scheduler = Executors.newScheduledThreadPool(collectorCount); if (!isRunning()) { Collection<MXBean> mxBeans = MXBeanStorage.getInstance().getMXBeans(); for (MXBean mxBean : mxBeans) { DataCollector collector = mxBean.gainDataCollector(agentConfig.getHome().getDirectory()); scheduler.scheduleWithFixedDelay(collector, 0L, getInterval(), TimeUnit.SECONDS); LOG.info("{} started.", collector.getClass().getSimpleName()); } LOG.info("Collection interval : {}s).", getInterval()); isRunning = true; } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

scheduler.scheduleWithFixedDelay(collector, 0L, getInterval(), TimeUnit.SECONDS);

线程池周期地执行SystemDataCollector中run()去获取系统数据。

    @Override
    public void run() {
        initSigar();
        SystemMonitoringData systemMonitoringData = (SystemMonitoringData) getMXBean(SYSTEM);
        systemMonitoringData.setSystemInfo(execute());
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

3.总结:

后台启动的monitor, 运行的是一个java服务:

java -server -cp lib/* org.ngrinder.NGrinderAgentStarter --mode monitor --command run
  • 1

通过线程池周期获取系统性信息(sigar工具获取),存放在SystemInfo;

ngrinder-controller/src/main/java/org/ngrinder/perftest/service/samplinglistener/MonitorCollectorPlugin.java中startSampling():

@Override
    public void startSampling(final ISingleConsole singleConsole, PerfTest perfTest, IPerfTestService perfTestService) { final List<String> targetHostIP = perfTest.getTargetHostIP(); final Integer samplingInterval = perfTest.getSamplingInterval(); for (final String target : targetHostIP) { scheduledTaskService.runAsync(new Runnable() { @Override public void run() { LOGGER.info("Start JVM monitoring for IP:{}", target); MonitorClientService client = new MonitorClientService(target, MonitorCollectorPlugin.this.port); client.init(); if (client.isConnected()) { File testReportDir = singleConsole.getReportPath(); File dataFile = null; try { dataFile = new File(testReportDir, MONITOR_FILE_PREFIX + target + ".data"); FileWriter fileWriter = new FileWriter(dataFile, false); BufferedWriter bw = new BufferedWriter(fileWriter); // write header info bw.write(SystemInfo.HEADER); bw.newLine(); bw.flush(); clientMap.put(client, bw); } catch (IOException e) { LOGGER.error("Error to write to file:{}, Error:{}", dataFile.getPath(), e.getMessage()); } } } }); } assignScheduledTask(samplingInterval); } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

根据SystemInfo写到/root/.ngrinder/perftest/0_999/{test_id}/report/monitor_system_{test_id}/report/monitor_system_{ip}.data文件中;

ngrinder-controller/src/main/java/org/ngrinder/perftest/PerfTestService.java中getMonitorGraph()根据/root/.ngrinder/perftest/0_999/{test_id}/report/monitor_system_{test_id}/report/monitor_system_{ip}.data获取系统信息数据

     /**
     * Get system monitor data and wrap the data as a string value like "[22,11,12,34,....]", which can be used directly
     * in JS as a vector.
     *
     * @param testId       test id
     * @param targetIP     ip address of the monitor target
     * @param dataInterval interval value to get data. Interval value "2" means, get one record for every "2" records.
     * @return return the data in map */ public Map<String, String> getMonitorGraph(long testId, String targetIP, int dataInterval) { Map<String, String> returnMap = Maps.newHashMap(); File monitorDataFile = new File(config.getHome().getPerfTestReportDirectory(String.valueOf(testId)), MONITOR_FILE_PREFIX + targetIP + ".data"); BufferedReader br = null; try { StringBuilder sbUsedMem = new StringBuilder("["); StringBuilder sbCPUUsed = new StringBuilder("["); StringBuilder sbNetReceived = new StringBuilder("["); StringBuilder sbNetSent = new StringBuilder("["); StringBuilder customData1 = new StringBuilder("["); StringBuilder customData2 = new StringBuilder("["); StringBuilder customData3 = new StringBuilder("["); StringBuilder customData4 = new StringBuilder("["); StringBuilder customData5 = new StringBuilder("["); br = new BufferedReader(new FileReader(monitorDataFile)); br.readLine(); // skip the header. // "ip,system,collectTime,freeMemory,totalMemory,cpuUsedPercentage,receivedPerSec,sentPerSec" String line = br.readLine(); int skipCount = dataInterval; // to be compatible with previous version, check the length before // adding while (StringUtils.isNotBlank(line)) { if (skipCount < dataInterval) { skipCount++; } else { skipCount = 1; String[] datalist = StringUtils.split(line, ","); if ("null".equals(datalist[4]) || "undefined".equals(datalist[4])) { sbUsedMem.append("null").append(","); } else { sbUsedMem.append(Long.valueOf(datalist[4]) - Long.valueOf(datalist[3])).append(","); } addCustomData(sbCPUUsed, 5, datalist); addCustomData(sbNetReceived, 6, datalist); addCustomData(sbNetSent, 7, datalist); addCustomData(customData1, 8, datalist); addCustomData(customData2, 9, datalist); addCustomData(customData3, 10, datalist); addCustomData(customData4, 11, datalist); addCustomData(customData5, 12, datalist); line = br.readLine(); } } completeCustomData(returnMap, "cpu", sbCPUUsed); completeCustomData(returnMap, "memory", sbUsedMem); completeCustomData(returnMap, "received", sbNetReceived); completeCustomData(returnMap, "sent", sbNetSent); completeCustomData(returnMap, "customData1", customData1); completeCustomData(returnMap, "customData2", customData2); completeCustomData(returnMap, "customData3", customData3); completeCustomData(returnMap, "customData4", customData4); completeCustomData(returnMap, "customData5", customData5); } catch (IOException e) { LOGGER.info("Error while getting monitor {} data file at {}", targetIP, monitorDataFile); } finally { IOUtils.closeQuietly(br); } return returnMap; } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72

数据提供给Controller端: 
ngrinder-controller/src/man/java/org/ngrinder/perftest/controller/PerfTestController.java

    private Map<String, String> getMonitorGraphData(long id, String targetIP, int imgWidth) {
        int interval = perfTestService.getMonitorGraphInterval(id, targetIP, imgWidth); Map<String, String> sysMonitorMap = perfTestService.getMonitorGraph(id, targetIP, interval); PerfTest perfTest = perfTestService.getOne(id); sysMonitorMap.put("interval", String.valueOf(interval * (perfTest != null ? perfTest.getSamplingInterval() : 1))); return sysMonitorMap; } /** * Get the monitor data of the target having the given IP. * * @param id test Id * @param targetIP targetIP * @param imgWidth image width * @return json message */ @RestAPI @RequestMapping("/api/{id}/monitor") public HttpEntity<String> getMonitorGraph(@PathVariable("id") long id, @RequestParam("targetIP") String targetIP, @RequestParam int imgWidth) { return toJsonHttpEntity(getMonitorGraphData(id, targetIP, imgWidth)); }

猜你喜欢

转载自www.cnblogs.com/ceshi2016/p/9008775.html