AMBARI-13409. AMS Load Simulator updates. (Aravindan Vijayan via swagle)
Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/b2f306d9 Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/b2f306d9 Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/b2f306d9 Branch: refs/heads/branch-dev-patch-upgrade Commit: b2f306d95e41cc132a3d92a650d0faa03c44c844 Parents: 063e79f Author: Siddharth Wagle <swa...@hortonworks.com> Authored: Thu Oct 22 17:03:47 2015 -0700 Committer: Siddharth Wagle <swa...@hortonworks.com> Committed: Thu Oct 22 17:03:47 2015 -0700 ---------------------------------------------------------------------- .../ambari-metrics-timelineservice/pom.xml | 49 +- .../metrics/loadsimulator/LoadRunner.java | 4 +- .../loadsimulator/MetricsLoadSimulator.java | 17 + .../metrics/loadsimulator/data/AppID.java | 6 +- .../jmetertest/AMSJMeterLoadTest.java | 202 +++ .../loadsimulator/jmetertest/AppGetMetric.java | 57 + .../jmetertest/GetMetricRequestInfo.java | 61 + .../jmetertest/JmeterTestPlanTask.java | 269 ++++ .../loadsimulator/net/RestMetricsSender.java | 2 +- .../src/main/resources/loadsimulator/README | 65 + .../loadsimulator/ams-jmeter.properties | 56 + .../resources/loadsimulator/amsJmeterGraph.jmx | 104 ++ .../resources/loadsimulator/jmeter.properties | 1172 ++++++++++++++++++ .../loadsimulator/saveservice.properties | 381 ++++++ .../main/resources/metrics_def/AMS-HBASE.dat | 18 + .../resources/metrics_def/FLUME_HANDLER.dat | 40 + .../main/resources/metrics_def/KAFKA_BROKER.dat | 1104 +++++++++++++++++ .../src/main/resources/metrics_def/NIMBUS.dat | 7 + .../main/resources/ui_metrics_def/AMS-HBASE.dat | 26 + .../main/resources/ui_metrics_def/DATANODE.dat | 4 + .../resources/ui_metrics_def/FLUME_HANDLER.dat | 63 + .../src/main/resources/ui_metrics_def/HBASE.dat | 47 + .../src/main/resources/ui_metrics_def/HOST.dat | 79 ++ .../resources/ui_metrics_def/KAFKA_BROKER.dat | 16 + .../main/resources/ui_metrics_def/NAMENODE.dat | 30 + .../main/resources/ui_metrics_def/NIMBUS.dat | 28 + .../resources/ui_metrics_def/NODEMANAGER.dat | 33 + .../ui_metrics_def/RESOURCEMANAGER.dat | 11 + 28 files changed, 3945 insertions(+), 6 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/b2f306d9/ambari-metrics/ambari-metrics-timelineservice/pom.xml ---------------------------------------------------------------------- diff --git a/ambari-metrics/ambari-metrics-timelineservice/pom.xml b/ambari-metrics/ambari-metrics-timelineservice/pom.xml index 6e04330..67c278f 100644 --- a/ambari-metrics/ambari-metrics-timelineservice/pom.xml +++ b/ambari-metrics/ambari-metrics-timelineservice/pom.xml @@ -420,7 +420,54 @@ <artifactId>commons-logging</artifactId> <version>1.1.1</version> </dependency> - + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-math3</artifactId> + <version>3.4.1</version> + </dependency> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-pool2</artifactId> + <version>2.3</version> + </dependency> + <dependency> + <groupId>org.apache.jmeter</groupId> + <artifactId>ApacheJMeter_core</artifactId> + <version>2.13</version> + <exclusions> + <exclusion> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-nop</artifactId> + </exclusion> + <exclusion> + <groupId>commons-math3</groupId> + <artifactId>commons-math3</artifactId> + </exclusion> + <exclusion> + <groupId>commons-pool2</groupId> + <artifactId>commons-pool2</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>org.apache.jmeter</groupId> + <artifactId>ApacheJMeter_http</artifactId> + <version>2.13</version> + <exclusions> + <exclusion> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-nop</artifactId> + </exclusion> + <exclusion> + <groupId>commons-math3</groupId> + <artifactId>commons-math3</artifactId> + </exclusion> + <exclusion> + <groupId>commons-pool2</groupId> + <artifactId>commons-pool2</artifactId> + </exclusion> + </exclusions> + </dependency> <!-- 'mvn dependency:analyze' fails to detect use of this dependency --> <dependency> <groupId>org.apache.hadoop</groupId> http://git-wip-us.apache.org/repos/asf/ambari/blob/b2f306d9/ambari-metrics/ambari-metrics-timelineservice/src/main/java/org/apache/hadoop/yarn/server/applicationhistoryservice/metrics/loadsimulator/LoadRunner.java ---------------------------------------------------------------------- diff --git a/ambari-metrics/ambari-metrics-timelineservice/src/main/java/org/apache/hadoop/yarn/server/applicationhistoryservice/metrics/loadsimulator/LoadRunner.java b/ambari-metrics/ambari-metrics-timelineservice/src/main/java/org/apache/hadoop/yarn/server/applicationhistoryservice/metrics/loadsimulator/LoadRunner.java index e5da0a3..203a88b 100644 --- a/ambari-metrics/ambari-metrics-timelineservice/src/main/java/org/apache/hadoop/yarn/server/applicationhistoryservice/metrics/loadsimulator/LoadRunner.java +++ b/ambari-metrics/ambari-metrics-timelineservice/src/main/java/org/apache/hadoop/yarn/server/applicationhistoryservice/metrics/loadsimulator/LoadRunner.java @@ -82,13 +82,13 @@ public class LoadRunner { int startIndex = 0; if (createMaster) { - String simHost = hostName + ".0"; + String simHost = hostName + "0"; addMetricsWorkers(senderWorkers, simHost, metricsHost, MASTER_APPS); startIndex++; } for (int i = startIndex; i < threadCount; i++) { - String simHost = hostName + "." + i; + String simHost = hostName + i; addMetricsWorkers(senderWorkers, simHost, metricsHost, SLAVE_APPS); } http://git-wip-us.apache.org/repos/asf/ambari/blob/b2f306d9/ambari-metrics/ambari-metrics-timelineservice/src/main/java/org/apache/hadoop/yarn/server/applicationhistoryservice/metrics/loadsimulator/MetricsLoadSimulator.java ---------------------------------------------------------------------- diff --git a/ambari-metrics/ambari-metrics-timelineservice/src/main/java/org/apache/hadoop/yarn/server/applicationhistoryservice/metrics/loadsimulator/MetricsLoadSimulator.java b/ambari-metrics/ambari-metrics-timelineservice/src/main/java/org/apache/hadoop/yarn/server/applicationhistoryservice/metrics/loadsimulator/MetricsLoadSimulator.java index a0c1bd2..09db9b5 100644 --- a/ambari-metrics/ambari-metrics-timelineservice/src/main/java/org/apache/hadoop/yarn/server/applicationhistoryservice/metrics/loadsimulator/MetricsLoadSimulator.java +++ b/ambari-metrics/ambari-metrics-timelineservice/src/main/java/org/apache/hadoop/yarn/server/applicationhistoryservice/metrics/loadsimulator/MetricsLoadSimulator.java @@ -51,6 +51,23 @@ public class MetricsLoadSimulator { loadRunner.start(); } + /* + Another entry point to the test to be called from ./jmetertest/AMSJMeterLoadTest.java + */ + public static void startTest(Map<String,String> mapArgs) { + + LoadRunner loadRunner = new LoadRunner( + mapArgs.get("hostName"), + Integer.valueOf(mapArgs.get("numberOfHosts")), + mapArgs.get("metricsHostName"), + Integer.valueOf(mapArgs.get("collectInterval")), + Integer.valueOf(mapArgs.get("sendInterval")), + Boolean.valueOf(mapArgs.get("master")) + ); + + loadRunner.start(); + } + private static Map<String, String> parseArgs(String[] args) { Map<String, String> mapProps = new HashMap<String, String>(); mapProps.put("hostName", "host"); http://git-wip-us.apache.org/repos/asf/ambari/blob/b2f306d9/ambari-metrics/ambari-metrics-timelineservice/src/main/java/org/apache/hadoop/yarn/server/applicationhistoryservice/metrics/loadsimulator/data/AppID.java ---------------------------------------------------------------------- diff --git a/ambari-metrics/ambari-metrics-timelineservice/src/main/java/org/apache/hadoop/yarn/server/applicationhistoryservice/metrics/loadsimulator/data/AppID.java b/ambari-metrics/ambari-metrics-timelineservice/src/main/java/org/apache/hadoop/yarn/server/applicationhistoryservice/metrics/loadsimulator/data/AppID.java index 4f58dc5..a130171 100644 --- a/ambari-metrics/ambari-metrics-timelineservice/src/main/java/org/apache/hadoop/yarn/server/applicationhistoryservice/metrics/loadsimulator/data/AppID.java +++ b/ambari-metrics/ambari-metrics-timelineservice/src/main/java/org/apache/hadoop/yarn/server/applicationhistoryservice/metrics/loadsimulator/data/AppID.java @@ -25,9 +25,11 @@ public enum AppID { DATANODE("datanode"), NODEMANAGER("nodemanager"), MASTER_HBASE("hbase"), - SLAVE_HBASE("hbase"); + SLAVE_HBASE("hbase"), + NIMBUS("nimbus"), + KAFKA_BROKER("kafka_broker"); - public static final AppID[] MASTER_APPS = {HOST, NAMENODE, RESOURCEMANAGER, MASTER_HBASE}; + public static final AppID[] MASTER_APPS = {HOST, NAMENODE, RESOURCEMANAGER, MASTER_HBASE, KAFKA_BROKER, NIMBUS}; public static final AppID[] SLAVE_APPS = {HOST, DATANODE, NODEMANAGER, SLAVE_HBASE}; private String id; http://git-wip-us.apache.org/repos/asf/ambari/blob/b2f306d9/ambari-metrics/ambari-metrics-timelineservice/src/main/java/org/apache/hadoop/yarn/server/applicationhistoryservice/metrics/loadsimulator/jmetertest/AMSJMeterLoadTest.java ---------------------------------------------------------------------- diff --git a/ambari-metrics/ambari-metrics-timelineservice/src/main/java/org/apache/hadoop/yarn/server/applicationhistoryservice/metrics/loadsimulator/jmetertest/AMSJMeterLoadTest.java b/ambari-metrics/ambari-metrics-timelineservice/src/main/java/org/apache/hadoop/yarn/server/applicationhistoryservice/metrics/loadsimulator/jmetertest/AMSJMeterLoadTest.java new file mode 100644 index 0000000..187c3f1 --- /dev/null +++ b/ambari-metrics/ambari-metrics-timelineservice/src/main/java/org/apache/hadoop/yarn/server/applicationhistoryservice/metrics/loadsimulator/jmetertest/AMSJMeterLoadTest.java @@ -0,0 +1,202 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * <p/> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p/> + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.yarn.server.applicationhistoryservice.metrics.loadsimulator.jmetertest; + +import org.apache.commons.lang3.StringUtils; +import org.apache.hadoop.yarn.server.applicationhistoryservice.metrics.loadsimulator.MetricsLoadSimulator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.BufferedReader; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +public class AMSJMeterLoadTest { + + private final static Logger LOG = LoggerFactory.getLogger(AMSJMeterLoadTest.class); + private static String PROPERTIES_FILE = "loadsimulator/ams-jmeter.properties"; + private ScheduledExecutorService scheduledExecutorService = null; + private List<AppGetMetric> appGetMetrics; + private Properties amsJmeterProperties = null; + + public AMSJMeterLoadTest(String testType, String userDefinedPropertiesFile) { + + if (null == userDefinedPropertiesFile || userDefinedPropertiesFile.isEmpty()) { + this.amsJmeterProperties = readProperties(PROPERTIES_FILE); + } else { + this.amsJmeterProperties = readProperties(userDefinedPropertiesFile); + } + + if ("U".equals(testType)) { //GET metrics simulator + int numInstances = Integer.valueOf(amsJmeterProperties.getProperty("num-ui-instances")); + this.scheduledExecutorService = Executors.newScheduledThreadPool(numInstances); + this.appGetMetrics = initializeGetMetricsPayload(amsJmeterProperties); + this.runTest(numInstances); + } else { //PUT Metrics simulator + Map<String, String> mapArgs = new HashMap<String, String>(); + mapArgs.put("hostName", amsJmeterProperties.getProperty("host-prefix")); + mapArgs.put("numberOfHosts", amsJmeterProperties.getProperty("num-hosts")); + mapArgs.put("metricsHostName", amsJmeterProperties.getProperty("ams-host-port")); + mapArgs.put("collectInterval", amsJmeterProperties.getProperty("collection-interval")); + mapArgs.put("sendInterval", amsJmeterProperties.getProperty("send-interval")); + mapArgs.put("master", amsJmeterProperties.getProperty("create-master")); + MetricsLoadSimulator.startTest(mapArgs); + } + } + + public static Properties readProperties(String propertiesFile) { + try { + Properties properties = new Properties(); + InputStream inputStream = ClassLoader.getSystemResourceAsStream(propertiesFile); + if (inputStream == null) { + inputStream = new FileInputStream(propertiesFile); + } + properties.load(inputStream); + return properties; + } catch (IOException ioEx) { + LOG.error("Error reading properties file for jmeter"); + return null; + } + } + + private static List<GetMetricRequestInfo> readMetricsFromFile(String app) { + InputStream input = null; + List<GetMetricRequestInfo> metricList = new ArrayList<>(); + String fileName = "ui_metrics_def/" + app + ".dat"; + + try { + input = ClassLoader.getSystemResourceAsStream(fileName); + BufferedReader reader = new BufferedReader(new InputStreamReader(input)); + String line; + List<String> metrics = new ArrayList<>(); + while ((line = reader.readLine()) != null) { + + if (line.startsWith("|")) { + boolean needsTimestamps = line.contains("startTime"); + boolean needsHost = line.contains("hostname"); + metricList.add(new GetMetricRequestInfo(metrics, needsTimestamps, needsHost)); + metrics.clear(); + } else { + metrics.add(line); + } + } + return metricList; + } catch (IOException e) { + LOG.error("Cannot read file " + fileName + " for appID " + app, e); + } finally { + if (input != null) { + try { + input.close(); + } catch (IOException ex) { + } + } + } + return null; + } + + private static List<AppGetMetric> initializeGetMetricsPayload(Properties amsJmeterProperties) { + + List<AppGetMetric> appGetMetrics = new ArrayList<AppGetMetric>(); + String appsToTest = amsJmeterProperties.getProperty("apps-to-test"); + String[] apps; + + if (appsToTest != null && !appsToTest.isEmpty()) { + apps = StringUtils.split(appsToTest, ","); + } else { + apps = new String[JmeterTestPlanTask.ClientApp.values().length]; + int ctr = 0; + for (JmeterTestPlanTask.ClientApp app : JmeterTestPlanTask.ClientApp.values()) + apps[ctr++] = app.getId(); + } + + for (String app : apps) { + + int interval = Integer.valueOf(amsJmeterProperties.getProperty("get-interval")); + String intervalString = amsJmeterProperties.getProperty(app + "-get-interval"); + if (intervalString != null && !intervalString.isEmpty()) { + interval = Integer.valueOf(intervalString); + } + appGetMetrics.add(new AppGetMetric(readMetricsFromFile(app), interval, app)); + } + + return appGetMetrics; + } + + public void runTest(int numInstances) { + + int appRefreshRate = Integer.valueOf(amsJmeterProperties.getProperty("app-refresh-rate")); + for (int i = 0; i < numInstances; i++) { + ScheduledFuture future = scheduledExecutorService.scheduleAtFixedRate(new JmeterTestPlanTask(appGetMetrics, + amsJmeterProperties), 0, appRefreshRate, TimeUnit.MILLISECONDS); + } + } + + /** + * Sample Usage: + * java -cp "lib/*":ambari-metrics-timelineservice-2.1.1.0.jar org.apache.hadoop.yarn.server.applicationhistoryservice + * .metrics.loadsimulator.jmeter.AMSJMeterLoadTest + * -t UI -p ambari-metrics-timelineservice/src/main/resources/jmeter/ams-jmeter.properties + */ + public static void main(String[] args) { + Map<String, String> mapArgs = parseArgs(args); + String testType = mapArgs.get("type"); + String amsJmeterPropertiesFile = mapArgs.get("amsJmeterPropertiesFile"); + new AMSJMeterLoadTest(testType, amsJmeterPropertiesFile); + } + + private static Map<String, String> parseArgs(String[] args) { + Map<String, String> mapProps = new HashMap<String, String>(); + if (args.length == 0) { + printUsage(); + throw new RuntimeException("Unexpected argument, See usage message."); + } else { + for (int i = 0; i < args.length; i += 2) { + String arg = args[i]; + if (arg.equals("-t")) { + mapProps.put("type", args[i + 1]); + } else if (arg.equals("-p")) { + mapProps.put("amsJmeterPropertiesFile", args[i + 1]); + } else { + printUsage(); + throw new IllegalArgumentException("Unexpected argument, See usage message."); + } + } + } + return mapProps; + } + + public static void printUsage() { + System.err.println("Usage: java AMSJmeterLoadTest [OPTIONS]"); + System.err.println("Options: "); + System.err.println("[-t type (S=>Sink/U=>UI)] [-p amsJmeterPropertiesFile (Optional)]"); + } + +} + + http://git-wip-us.apache.org/repos/asf/ambari/blob/b2f306d9/ambari-metrics/ambari-metrics-timelineservice/src/main/java/org/apache/hadoop/yarn/server/applicationhistoryservice/metrics/loadsimulator/jmetertest/AppGetMetric.java ---------------------------------------------------------------------- diff --git a/ambari-metrics/ambari-metrics-timelineservice/src/main/java/org/apache/hadoop/yarn/server/applicationhistoryservice/metrics/loadsimulator/jmetertest/AppGetMetric.java b/ambari-metrics/ambari-metrics-timelineservice/src/main/java/org/apache/hadoop/yarn/server/applicationhistoryservice/metrics/loadsimulator/jmetertest/AppGetMetric.java new file mode 100644 index 0000000..727a1c7 --- /dev/null +++ b/ambari-metrics/ambari-metrics-timelineservice/src/main/java/org/apache/hadoop/yarn/server/applicationhistoryservice/metrics/loadsimulator/jmetertest/AppGetMetric.java @@ -0,0 +1,57 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * <p/> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p/> + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.yarn.server.applicationhistoryservice.metrics.loadsimulator.jmetertest; + +import java.util.List; + +public class AppGetMetric { + + private String app; + private int interval; + private List<GetMetricRequestInfo> requests; + + public AppGetMetric(List<GetMetricRequestInfo> requests, int interval, String app) { + this.setMetricRequests(requests); + this.setInterval(interval); + this.setApp(app); + } + + public List<GetMetricRequestInfo> getMetricRequests() { + return requests; + } + + public void setMetricRequests(List<GetMetricRequestInfo> requests) { + this.requests = requests; + } + + public int getInterval() { + return interval; + } + + public void setInterval(int interval) { + this.interval = interval; + } + + public String getApp() { + return app; + } + + public void setApp(String app) { + this.app = app; + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/b2f306d9/ambari-metrics/ambari-metrics-timelineservice/src/main/java/org/apache/hadoop/yarn/server/applicationhistoryservice/metrics/loadsimulator/jmetertest/GetMetricRequestInfo.java ---------------------------------------------------------------------- diff --git a/ambari-metrics/ambari-metrics-timelineservice/src/main/java/org/apache/hadoop/yarn/server/applicationhistoryservice/metrics/loadsimulator/jmetertest/GetMetricRequestInfo.java b/ambari-metrics/ambari-metrics-timelineservice/src/main/java/org/apache/hadoop/yarn/server/applicationhistoryservice/metrics/loadsimulator/jmetertest/GetMetricRequestInfo.java new file mode 100644 index 0000000..26c5025 --- /dev/null +++ b/ambari-metrics/ambari-metrics-timelineservice/src/main/java/org/apache/hadoop/yarn/server/applicationhistoryservice/metrics/loadsimulator/jmetertest/GetMetricRequestInfo.java @@ -0,0 +1,61 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.yarn.server.applicationhistoryservice.metrics.loadsimulator.jmetertest; + +import org.apache.commons.lang.StringUtils; + +import java.util.List; + + +public class GetMetricRequestInfo { + + private String metricStringPayload; + private boolean needsTimestamps; + private boolean needsHost; + + public GetMetricRequestInfo(List<String> metrics, boolean needsTimestamps, boolean needsHost) { + + this.setMetricStringPayload(StringUtils.join(metrics, ",")); + this.setNeedsTimestamps(needsTimestamps); + this.setNeedsHost(needsHost); + } + + public String getMetricStringPayload() { + return metricStringPayload; + } + + public void setMetricStringPayload(String metricStringPayload) { + this.metricStringPayload = metricStringPayload; + } + + public boolean needsTimestamps() { + return needsTimestamps; + } + + public void setNeedsTimestamps(boolean needsTimestamps) { + this.needsTimestamps = needsTimestamps; + } + + public boolean needsHost() { + return needsHost; + } + + public void setNeedsHost(boolean needsHost) { + this.needsHost = needsHost; + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/b2f306d9/ambari-metrics/ambari-metrics-timelineservice/src/main/java/org/apache/hadoop/yarn/server/applicationhistoryservice/metrics/loadsimulator/jmetertest/JmeterTestPlanTask.java ---------------------------------------------------------------------- diff --git a/ambari-metrics/ambari-metrics-timelineservice/src/main/java/org/apache/hadoop/yarn/server/applicationhistoryservice/metrics/loadsimulator/jmetertest/JmeterTestPlanTask.java b/ambari-metrics/ambari-metrics-timelineservice/src/main/java/org/apache/hadoop/yarn/server/applicationhistoryservice/metrics/loadsimulator/jmetertest/JmeterTestPlanTask.java new file mode 100644 index 0000000..f7e27b1 --- /dev/null +++ b/ambari-metrics/ambari-metrics-timelineservice/src/main/java/org/apache/hadoop/yarn/server/applicationhistoryservice/metrics/loadsimulator/jmetertest/JmeterTestPlanTask.java @@ -0,0 +1,269 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.yarn.server.applicationhistoryservice.metrics.loadsimulator.jmetertest; + +import org.apache.commons.io.IOUtils; +import org.apache.jmeter.control.LoopController; +import org.apache.jmeter.engine.StandardJMeterEngine; +import org.apache.jmeter.protocol.http.sampler.HTTPSampler; +import org.apache.jmeter.protocol.http.util.HTTPConstants; +import org.apache.jmeter.reporters.ResultCollector; +import org.apache.jmeter.reporters.Summariser; +import org.apache.jmeter.testelement.TestElement; +import org.apache.jmeter.testelement.TestPlan; +import org.apache.jmeter.threads.JMeterContextService; +import org.apache.jmeter.threads.ThreadGroup; +import org.apache.jmeter.timers.ConstantTimer; +import org.apache.jmeter.util.JMeterUtils; +import org.apache.jorphan.collections.HashTree; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.*; +import java.lang.reflect.Field; +import java.util.*; + +public class JmeterTestPlanTask implements Runnable { + + private static StandardJMeterEngine jmeterEngine = null; + private final static Logger LOG = LoggerFactory.getLogger(JmeterTestPlanTask.class); + private List<AppGetMetric> appGetMetrics; + private Properties amsJmeterProperties; + private HashTree amsTestPlanTree; + private TestPlan amsTestPlan; + private static final String JMETER_HOME = "loadsimulator"; + private static final String JMETER_PROPERTIES_FILE = JMETER_HOME + "/jmeter.properties"; + private static final String SAVESERVICE_PROPERTIES_FILE = JMETER_HOME + "/saveservice.properties"; + + public enum ClientApp { + HOST("HOST"), + NAMENODE("NAMENODE"), + HBASE("HBASE"), + NIMBUS("NIMBUS"), + KAFKA_BROKER("KAFKA_BROKER"), + FLUME_HANDLER("FLUME_HANDLER"), + AMS_HBASE("AMS-HBASE"), + NODEMANAGER("NODEMANAGER"), + RESOURCEMANAGER("RESOURCEMANAGER"), + DATANODE("DATANODE"); + + private String id; + + private ClientApp(String id) { + this.id = id; + } + + public String getId() { + return id; + } + } + + public JmeterTestPlanTask(List<AppGetMetric> appGetMetrics, Properties amsJmeterProperties) { + this.appGetMetrics = appGetMetrics; + this.amsJmeterProperties = amsJmeterProperties; + amsTestPlanTree = new HashTree(); + amsTestPlan = new TestPlan("AMS JMeter Load Test plan"); + System.out.println("Starting AMS Jmeter load testing"); + } + + public void run() { + if (jmeterEngine != null) { + + Object[] threadGroups = amsTestPlanTree.getArray(amsTestPlan); + for (Object threadGroupObj : threadGroups) { + if (threadGroupObj instanceof ThreadGroup) { + ThreadGroup threadGroup = (ThreadGroup) threadGroupObj; + threadGroup.stop(); + } + } + amsTestPlanTree.clear(); + jmeterEngine.askThreadsToStop(); + jmeterEngine.stopTest(); + JMeterContextService.endTest(); + } + + //Start the new test plan for the new app. + try { + //Initialize Jmeter essentials + jmeterEngine = new StandardJMeterEngine(); + JMeterContextService.getContext().setEngine(jmeterEngine); + + //Workaround to supply JMeterUtils with jmeter.prooperties from JAR. + JMeterUtils.setJMeterHome(""); + Field f = new JMeterUtils().getClass().getDeclaredField("appProperties"); + f.setAccessible(true); + f.set(null, AMSJMeterLoadTest.readProperties(JMETER_PROPERTIES_FILE)); + + //Copy saveservices.properties file to tmp dir for JMeter to consume. + InputStream inputStream = ClassLoader.getSystemResourceAsStream(SAVESERVICE_PROPERTIES_FILE); + if (inputStream == null) { + inputStream = new FileInputStream(SAVESERVICE_PROPERTIES_FILE); + } + String tmpDir = System.getProperty("java.io.tmpdir"); + OutputStream outputStream = new FileOutputStream(tmpDir + "/saveservice.properties"); + IOUtils.copy(inputStream, outputStream); + outputStream.close(); + JMeterUtils.setProperty("saveservice_properties", tmpDir + "/saveservice.properties"); + + //Initialize Test plan + amsTestPlan.setProperty(TestElement.TEST_CLASS, TestPlan.class.getName()); + amsTestPlanTree.add("AMS Test plan", amsTestPlan); + + //Choose a random APP to run the perform GET metrics request. + int currentAppIndex = new Random().nextInt(appGetMetrics.size()); + + //Create ThreadGroup for the App + createThreadGroupHashTree(currentAppIndex, amsJmeterProperties, amsTestPlanTree, amsTestPlan); + + //Geneates the JMX file that you can use through the GUI mode. + //SaveService.saveTree(amsTestPlanTree, new FileOutputStream(JMETER_HOME + "/" + "amsTestPlan.jmx")); + + //Summarizer output to get test progress in stdout like. + Summariser summariser = null; + String summariserName = JMeterUtils.getPropDefault("summariser.name", "summary"); + if (summariserName.length() > 0) { + summariser = new Summariser(summariserName); + } + + //Store execution results into a .jtl file + String jmeterLogFile = tmpDir + "/amsJmeterTestResults.jtl"; + ResultCollector resultCollector = new ResultCollector(summariser); + resultCollector.setFilename(jmeterLogFile); + amsTestPlanTree.add(amsTestPlanTree.getArray()[0], resultCollector); + jmeterEngine.configure(amsTestPlanTree); + jmeterEngine.run(); + + LOG.info("AMS Jmeter Test started up successfully"); + + } catch (Exception ioEx) { + amsTestPlanTree.clear(); + jmeterEngine.askThreadsToStop(); + jmeterEngine.stopTest(); + JMeterContextService.endTest(); + LOG.error("Error occurred while running AMS load test : " + ioEx.getMessage()); + ioEx.printStackTrace(); + } + } + + private ConstantTimer createConstantTimer(int delay) { + ConstantTimer timer = new ConstantTimer(); + timer.setDelay("" + delay); + return timer; + } + + private Map<String, String> getAppSpecificParameters(String app, GetMetricRequestInfo request, Properties amsJmeterProperties) { + + Map<String, String> parametersMap = new HashMap<String, String>(); + String hostPrefix = amsJmeterProperties.getProperty("host-prefix"); + String hostSuffix = amsJmeterProperties.getProperty("host-suffix"); + int minHostIndex = Integer.valueOf(amsJmeterProperties.getProperty("min-host-index")); + int numHosts = Integer.valueOf(amsJmeterProperties.getProperty("num-hosts")); + + parametersMap.put("appId", app); + + if (request.needsTimestamps()) { + long currentTime = System.currentTimeMillis(); + long oneHourBack = currentTime - 3600 * 1000; + parametersMap.put("startTime", String.valueOf(oneHourBack)); + parametersMap.put("endTime", String.valueOf(currentTime)); + } + + if (request.needsHost()) { + if (ClientApp.AMS_HBASE.getId().equals(app)) { + parametersMap.put("hostname", amsJmeterProperties.getProperty("ams-host")); + } else if (ClientApp.HOST.getId().equals(app) || ClientApp.NODEMANAGER.getId().equals(app)) { + int randomHost = minHostIndex + new Random().nextInt(numHosts); + parametersMap.put("hostname", hostPrefix + randomHost + hostSuffix); + } else { + parametersMap.put("hostname", hostPrefix + amsJmeterProperties.getProperty(app + "-host") + hostSuffix); + } + } + parametersMap.put("metricNames", request.getMetricStringPayload()); + return parametersMap; + } + + private void createThreadGroupHashTree(int appIndex, Properties amsJmeterProperties, HashTree amsTestPlanTree, TestPlan amsTestPlan) { + + AppGetMetric appGetMetric = appGetMetrics.get(appIndex); + String app = appGetMetric.getApp(); + int interval = appGetMetric.getInterval(); + + //Read and validate AMS information. + String[] amsHostPort = amsJmeterProperties.getProperty("ams-host-port").split(":"); + String amsHost = amsHostPort[0]; + String amsPath = amsJmeterProperties.getProperty("ams-path"); + int amsPort = Integer.valueOf(amsHostPort[1]); + int numLoops = Integer.valueOf(amsJmeterProperties.getProperty("num-get-calls-per-app")); + + LoopController loopController = createLoopController(app + " GET loop controller", numLoops, false); + for (GetMetricRequestInfo request : appGetMetric.getMetricRequests()) { + + ThreadGroup threadGroup = createThreadGroup(app + " GET threadGroup", 1, 0, loopController); + + HashTree threadGroupHashTree = amsTestPlanTree.add(amsTestPlan, threadGroup); + Map<String, String> parametersMap = getAppSpecificParameters(app, request, amsJmeterProperties); + + HTTPSampler sampler = createGetSampler("GET " + app + " metrics", amsHost, amsPort, amsPath, null, parametersMap); + + if (numLoops > 1) { + threadGroupHashTree.add(createConstantTimer(interval)); + } + + threadGroupHashTree.add(sampler); + } + } + + private HTTPSampler createGetSampler(String name, String domain, int port, String path, String encoding, Map<String, String> parameters) { + + HTTPSampler sampler = new HTTPSampler(); + sampler.setDomain(domain); + sampler.setPort(port); + sampler.setPath(path); + sampler.setMethod(HTTPConstants.GET); + + if (encoding != null) + sampler.setContentEncoding(encoding); + + for (Map.Entry<String, String> entry : parameters.entrySet()) { + sampler.addArgument(entry.getKey(), entry.getValue()); + } + sampler.setName(name); + return sampler; + } + + private LoopController createLoopController(String name, int numLoops, boolean continueForever) { + LoopController loopController = new LoopController(); + loopController.setLoops(numLoops); + loopController.setProperty(TestElement.TEST_CLASS, LoopController.class.getName()); + loopController.initialize(); + loopController.setContinueForever(continueForever); + loopController.setName(name); + return loopController; + } + + private ThreadGroup createThreadGroup(String name, int numThreads, int rampUp, LoopController loopController) { + ThreadGroup threadGroup = new ThreadGroup(); + threadGroup.setName(name); + threadGroup.setNumThreads(numThreads); + threadGroup.setRampUp(rampUp); + threadGroup.setSamplerController(loopController); + threadGroup.setProperty(TestElement.TEST_CLASS, ThreadGroup.class.getName()); + return threadGroup; + } + +} http://git-wip-us.apache.org/repos/asf/ambari/blob/b2f306d9/ambari-metrics/ambari-metrics-timelineservice/src/main/java/org/apache/hadoop/yarn/server/applicationhistoryservice/metrics/loadsimulator/net/RestMetricsSender.java ---------------------------------------------------------------------- diff --git a/ambari-metrics/ambari-metrics-timelineservice/src/main/java/org/apache/hadoop/yarn/server/applicationhistoryservice/metrics/loadsimulator/net/RestMetricsSender.java b/ambari-metrics/ambari-metrics-timelineservice/src/main/java/org/apache/hadoop/yarn/server/applicationhistoryservice/metrics/loadsimulator/net/RestMetricsSender.java index 8657436..c6d84c2 100644 --- a/ambari-metrics/ambari-metrics-timelineservice/src/main/java/org/apache/hadoop/yarn/server/applicationhistoryservice/metrics/loadsimulator/net/RestMetricsSender.java +++ b/ambari-metrics/ambari-metrics-timelineservice/src/main/java/org/apache/hadoop/yarn/server/applicationhistoryservice/metrics/loadsimulator/net/RestMetricsSender.java @@ -32,7 +32,7 @@ import java.net.ProtocolException; public class RestMetricsSender implements MetricsSender { private final static Logger LOG = LoggerFactory.getLogger(RestMetricsSender.class); - private final static String COLLECTOR_URL = "http://%s:6188/ws/v1/timeline/metrics"; + private final static String COLLECTOR_URL = "http://%s/ws/v1/timeline/metrics"; private final String collectorServiceAddress; /** http://git-wip-us.apache.org/repos/asf/ambari/blob/b2f306d9/ambari-metrics/ambari-metrics-timelineservice/src/main/resources/loadsimulator/README ---------------------------------------------------------------------- diff --git a/ambari-metrics/ambari-metrics-timelineservice/src/main/resources/loadsimulator/README b/ambari-metrics/ambari-metrics-timelineservice/src/main/resources/loadsimulator/README new file mode 100644 index 0000000..39e5365 --- /dev/null +++ b/ambari-metrics/ambari-metrics-timelineservice/src/main/resources/loadsimulator/README @@ -0,0 +1,65 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + WORKING OF THE AMS LOAD SIMULATOR + The AMS load simulator is designed to perform load testing on a live AMS instance by performing the role of either a + sink or an Ambari UI instance based on how it is being invoked. + + > When it acts as a host sink, it makes calls to the AMS to PUT metrics for all the services for a defined interval. + The simulator can also be used to start up "N" such host instances where each instance has a number of Sinks that PUT + metrics. + > When the load simulator is invoked as a UI instance, it makes GET metrics calls to the AMS in defined + intervals for all the services. The rate of the GET metrics call and the list of metrics requested has been designed + to closely match an actual Ambari UI instance. Apache JMeter API has been used to design the GET calls made to the + AMS. + + The load simulator uses a properties file (ams-jmeter.properties) file to configure the test run. It is part of the + JAR in the same folder as this README file. It can also be supplied as a command line argument to the test using the + "-p" option. Other properties files like jmeter.properties and saveservice.properties contain JMeter internal + properties and need not be modified. + + + INSTRUCTIONS TO RUN THE SIMULATOR + 1. Modify the ams-jmeter.properties to point to your AMS host. Change the properties "num-hosts" based on how many hosts + need to be simulated for sinks. The GET Metric section of the properties is used for fine tuning GET call interval + for every APP type. + 2. Build the ambari-metrics-timelineservice jar. + 3. Invoke the test using the command as follows. + + java -cp lib/*:ambari-metrics-timelineservice-<version>.jar org.apache.hadoop.yarn.server.applicationhistoryservice.metrics.loadsimulator.jmeter.AMSJMeterLoadTest -t <S/U> + + lib/* -> All the dependency JARs generated for the ambari-metrics-timelineservice JAR. + -t option => S-Sink simulator or U-UI simulator + You can use the -p <location of ams-jmeter.properties> option to pass in your own properties file. + + 4. Test results will be found at <TMP_DIR>/amsTestResults.jtl. + 5. Open the amsJmeterGrpah.jmx file through a JMeter GUI instance and supply the results (amsTestResults.jtl) file as + input to the Graph to be drawn. + + TESTING ON GCE + + 1. Copy the JAR, libs, optional ams-jmeter.properties file to all the machines on which the test needs to be run. + 2. Sink simulation for num-hosts = N. + Start the test with -t S on 1 machine with property "create-master=true". + Start the test with -t S on N-1 machines with property "create-master=false" + 3. UI simulation + Start the test with -t U on 1 or more machines. + 4. To stop the test after you have sufficient load testing done, the following command should be run on all machines. + ps axf | grep jmeter | grep -v grep | awk '{print "kill -9 " $1}' | sh + 5. Copy over the results file to a location with JMeter downloaded. Open the amsJmeterGraph.jmx on jmeter and browse to + open the results file as input to the graph. http://git-wip-us.apache.org/repos/asf/ambari/blob/b2f306d9/ambari-metrics/ambari-metrics-timelineservice/src/main/resources/loadsimulator/ams-jmeter.properties ---------------------------------------------------------------------- diff --git a/ambari-metrics/ambari-metrics-timelineservice/src/main/resources/loadsimulator/ams-jmeter.properties b/ambari-metrics/ambari-metrics-timelineservice/src/main/resources/loadsimulator/ams-jmeter.properties new file mode 100644 index 0000000..3353d43 --- /dev/null +++ b/ambari-metrics/ambari-metrics-timelineservice/src/main/resources/loadsimulator/ams-jmeter.properties @@ -0,0 +1,56 @@ +# Copyright 2011 The Apache Software Foundation +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +#AMS information +ams-host-port=104.196.94.27:6188 +ams-path=/ws/v1/timeline/metrics + +# Host Information, using the host-prefix, num-hosts and <MASTER>-host +# Hosts in this case will be TestHost0, TestHost1 +# For example NAMENODE, NIMBUS,HBASE MASTER,RESOURCEMANAGER host will be TestHost0 +host-prefix=TestHost +host-suffix= +min-host-index=0 +num-hosts=2 +NAMENODE-host=0 +NIMBUS-host=0 +HBASE-host=0 +RESOURCEMANAGER-host=0 + +# PUT Metric / Sinks config +collection-interval=1000 +send-interval=3000 +create-master=true + +# GET Metric / Client Apps config +num-ui-instances=1 +apps-to-test= +app-refresh-rate=8000 +num-get-calls-per-app=3 +get-interval=3000 +HOST-get-interval=3000 +NAMENODE-get-interval=2000 +HBASE-get-interval=3000 +NIMBUS-get-interval=2000 +AMS-HBASE-get-interval=2000 +FLUME_HANDLER-get-interval=2000 +NODEMANAGER-get-interval=2000 +KAFKA_BROKER-get-interval=2000 +DATANODE-get-interval=3000 +RESOURCEMANAGER-get-interval=3000 + http://git-wip-us.apache.org/repos/asf/ambari/blob/b2f306d9/ambari-metrics/ambari-metrics-timelineservice/src/main/resources/loadsimulator/amsJmeterGraph.jmx ---------------------------------------------------------------------- diff --git a/ambari-metrics/ambari-metrics-timelineservice/src/main/resources/loadsimulator/amsJmeterGraph.jmx b/ambari-metrics/ambari-metrics-timelineservice/src/main/resources/loadsimulator/amsJmeterGraph.jmx new file mode 100644 index 0000000..06d6360 --- /dev/null +++ b/ambari-metrics/ambari-metrics-timelineservice/src/main/resources/loadsimulator/amsJmeterGraph.jmx @@ -0,0 +1,104 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Licensed to the Apache Software Foundation (ASF) under one + ~ or more contributor license agreements. See the NOTICE file + ~ distributed with this work for additional information + ~ regarding copyright ownership. The ASF licenses this file + ~ to you under the Apache License, Version 2.0 (the + ~ "License"); you may not use this file except in compliance + ~ with the License. You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<jmeterTestPlan version="1.2" properties="2.8" jmeter="2.13 r1665067"> + <hashTree> + <TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="Test Plan" enabled="true"> + <stringProp name="TestPlan.comments"></stringProp> + <boolProp name="TestPlan.functional_mode">false</boolProp> + <boolProp name="TestPlan.serialize_threadgroups">false</boolProp> + <elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="TestPlan.user_define_classpath"></stringProp> + </TestPlan> + <hashTree> + <ResultCollector guiclass="GraphVisualizer" testclass="ResultCollector" testname="Latency Graph" enabled="true"> + <boolProp name="ResultCollector.error_logging">false</boolProp> + <objProp> + <name>saveConfig</name> + <value class="SampleSaveConfiguration"> + <time>false</time> + <latency>false</latency> + <timestamp>true</timestamp> + <success>true</success> + <label>true</label> + <code>true</code> + <message>true</message> + <threadName>false</threadName> + <dataType>true</dataType> + <encoding>false</encoding> + <assertions>false</assertions> + <subresults>false</subresults> + <responseData>true</responseData> + <samplerData>true</samplerData> + <xml>true</xml> + <fieldNames>true</fieldNames> + <responseHeaders>true</responseHeaders> + <requestHeaders>true</requestHeaders> + <responseDataOnError>true</responseDataOnError> + <saveAssertionResultsFailureMessage>false</saveAssertionResultsFailureMessage> + <assertionsResultsToSave>0</assertionsResultsToSave> + <url>true</url> + <hostname>true</hostname> + <sampleCount>true</sampleCount> + </value> + </objProp> + <stringProp name="filename">/tmp/amsJmeterTestResults.jtl</stringProp> + </ResultCollector> + <hashTree/> + <ResultCollector guiclass="RespTimeGraphVisualizer" testclass="ResultCollector" testname="Response Time Graph" enabled="true"> + <boolProp name="ResultCollector.error_logging">false</boolProp> + <objProp> + <name>saveConfig</name> + <value class="SampleSaveConfiguration"> + <time>false</time> + <latency>false</latency> + <timestamp>true</timestamp> + <success>true</success> + <label>true</label> + <code>true</code> + <message>true</message> + <threadName>false</threadName> + <dataType>true</dataType> + <encoding>false</encoding> + <assertions>false</assertions> + <subresults>false</subresults> + <responseData>true</responseData> + <samplerData>true</samplerData> + <xml>true</xml> + <fieldNames>true</fieldNames> + <responseHeaders>true</responseHeaders> + <requestHeaders>true</requestHeaders> + <responseDataOnError>true</responseDataOnError> + <saveAssertionResultsFailureMessage>false</saveAssertionResultsFailureMessage> + <assertionsResultsToSave>0</assertionsResultsToSave> + <url>true</url> + <hostname>true</hostname> + <sampleCount>true</sampleCount> + </value> + </objProp> + <stringProp name="filename">/tmp/amsJmeterTestResults.jtl</stringProp> + <stringProp name="RespTimeGraph.interval">15000</stringProp> + <intProp name="RespTimeGraph.linestrockwidth">2</intProp> + <intProp name="RespTimeGraph.lineshapepoint">4</intProp> + </ResultCollector> + <hashTree/> + </hashTree> + </hashTree> +</jmeterTestPlan>