You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jmeter.apache.org by pm...@apache.org on 2019/09/30 20:02:36 UTC
[jmeter] branch master updated: This commit fixes bugs 63614 and
63723 Bug 63614 - Distributed testing: Unable to generate Dashboard report
at end of load test
This is an automated email from the ASF dual-hosted git repository.
pmouawad pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/jmeter.git
The following commit(s) were added to refs/heads/master by this push:
new 105a399 This commit fixes bugs 63614 and 63723 Bug 63614 - Distributed testing: Unable to generate Dashboard report at end of load test
105a399 is described below
commit 105a3999f44c73200fb08c249e120937840ee799
Author: pmouawad <p....@ubik-ingenierie.com>
AuthorDate: Mon Sep 30 22:02:20 2019 +0200
This commit fixes bugs 63614 and 63723
Bug 63614 - Distributed testing: Unable to generate Dashboard report at
end of load test
Bug 63723 - JMeter master ends distributed test though some threads
still are active
---
.../src/main/java/org/apache/jmeter/JMeter.java | 136 +++++++++++----------
.../apache/jmeter/engine/ClientJMeterEngine.java | 5 +
.../apache/jmeter/engine/DistributedRunner.java | 27 +++-
xdocs/changes.xml | 2 +
4 files changed, 99 insertions(+), 71 deletions(-)
diff --git a/src/core/src/main/java/org/apache/jmeter/JMeter.java b/src/core/src/main/java/org/apache/jmeter/JMeter.java
index 9526629..6113836 100644
--- a/src/core/src/main/java/org/apache/jmeter/JMeter.java
+++ b/src/core/src/main/java/org/apache/jmeter/JMeter.java
@@ -43,6 +43,7 @@ import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.StringTokenizer;
+import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
@@ -85,7 +86,6 @@ import org.apache.jmeter.gui.util.FocusRequester;
import org.apache.jmeter.plugin.JMeterPlugin;
import org.apache.jmeter.plugin.PluginManager;
import org.apache.jmeter.report.config.ConfigurationException;
-import org.apache.jmeter.report.dashboard.GenerationException;
import org.apache.jmeter.report.dashboard.ReportGenerator;
import org.apache.jmeter.reporters.ResultCollector;
import org.apache.jmeter.reporters.Summariser;
@@ -547,9 +547,9 @@ public class JMeter implements JMeterPlugin {
} else { // NON-GUI must be true
extractAndSetReportOutputFolder(parser, deleteResultFile);
- CLOption rem = parser.getArgumentById(REMOTE_OPT_PARAM);
- if (rem == null) {
- rem = parser.getArgumentById(REMOTE_OPT);
+ CLOption remoteTest = parser.getArgumentById(REMOTE_OPT_PARAM);
+ if (remoteTest == null) {
+ remoteTest = parser.getArgumentById(REMOTE_OPT);
}
CLOption jtl = parser.getArgumentById(LOGFILE_OPT);
String jtlFile = null;
@@ -561,7 +561,7 @@ public class JMeter implements JMeterPlugin {
throw new IllegalUserActionException(
"Option -"+ ((char)REPORT_AT_END_OPT)+" requires -"+((char)LOGFILE_OPT )+ " option");
}
- startNonGui(testFile, jtlFile, rem, reportAtEndOpt != null);
+ startNonGui(testFile, jtlFile, remoteTest, reportAtEndOpt != null);
startOptionalServers();
}
}
@@ -1066,27 +1066,31 @@ public class JMeter implements JMeterPlugin {
clonedTree.add(clonedTree.getArray()[0], new RemoteThreadsListenerTestElement());
List<JMeterEngine> engines = new LinkedList<>();
- clonedTree.add(clonedTree.getArray()[0], new ListenToTest(remoteStart && remoteStop ? engines : null, reportGenerator));
println("Created the tree successfully using "+testFile);
if (!remoteStart) {
JMeterEngine engine = new StandardJMeterEngine();
+ clonedTree.add(clonedTree.getArray()[0], new ListenToTest(
+ org.apache.jmeter.JMeter.ListenToTest.RunMode.LOCAL, false, reportGenerator));
engine.configure(clonedTree);
long now=System.currentTimeMillis();
- println("Starting the test @ "+new Date(now)+" ("+now+")");
- engine.runTest();
+ println("Starting standalone test @ "+new Date(now)+" ("+now+")");
engines.add(engine);
+ engine.runTest();
} else {
- java.util.StringTokenizer st = new java.util.StringTokenizer(remoteHostsString, ",");//$NON-NLS-1$
+ java.util.StringTokenizer st = new java.util.StringTokenizer(remoteHostsString.trim(), ",");//$NON-NLS-1$
List<String> hosts = new LinkedList<>();
while (st.hasMoreElements()) {
- hosts.add((String) st.nextElement());
+ hosts.add(((String) st.nextElement()).trim());
}
-
+ ListenToTest testListener = new ListenToTest(
+ org.apache.jmeter.JMeter.ListenToTest.RunMode.REMOTE, remoteStop, reportGenerator);
+ clonedTree.add(clonedTree.getArray()[0], testListener);
DistributedRunner distributedRunner=new DistributedRunner(this.remoteProps);
distributedRunner.setStdout(System.out); // NOSONAR
distributedRunner.setStdErr(System.err); // NOSONAR
distributedRunner.init(hosts, clonedTree);
engines.addAll(distributedRunner.getEngines());
+ testListener.setStartedRemoteEngines(engines);
distributedRunner.start();
}
startUdpDdaemon(engines);
@@ -1225,30 +1229,51 @@ public class JMeter implements JMeterPlugin {
* If running a remote test, then after waiting a few seconds for listeners to finish files,
* it calls ClientJMeterEngine.tidyRMI() to deal with the Naming Timer Thread.
*/
- private static class ListenToTest implements TestStateListener, Runnable, Remoteable {
- private AtomicInteger startedEngines; // keep track of remote tests
+ private static class ListenToTest implements TestStateListener, Remoteable {
+ enum RunMode {
+ LOCAL,
+ REMOTE
+ }
+
+ private AtomicInteger startedRemoteEngines = new AtomicInteger(0);
- private final List<JMeterEngine> engines;
+ private ConcurrentLinkedQueue<JMeterEngine> remoteEngines = new ConcurrentLinkedQueue<>();
private final ReportGenerator reportGenerator;
+ private RunMode runMode;
+
+ private boolean remoteStop;
+
/**
- * @param engines List<JMeterEngine>
+ * Listener for remote test
+ * @param runMode RunMode
+ * @param remoteStop
* @param reportGenerator {@link ReportGenerator}
*/
- public ListenToTest(List<JMeterEngine> engines, ReportGenerator reportGenerator) {
- this.engines=engines;
- this.startedEngines = new AtomicInteger(engines == null ? 0 : engines.size());
+ public ListenToTest(RunMode runMode, boolean remoteStop, ReportGenerator reportGenerator) {
+ this.runMode = runMode;
+ this.remoteStop = remoteStop;
this.reportGenerator = reportGenerator;
}
+ public void setStartedRemoteEngines(List<JMeterEngine> engines) {
+ if (runMode != RunMode.REMOTE) {
+ throw new IllegalArgumentException("This method should only be called in RunMode.REMOTE");
+ }
+ this.remoteEngines.clear();
+ this.remoteEngines.addAll(engines);
+ this.startedRemoteEngines = new AtomicInteger(remoteEngines.size());
+ }
+
@Override
// N.B. this is called by a daemon RMI thread from the remote host
public void testEnded(String host) {
final long now=System.currentTimeMillis();
log.info("Finished remote host: {} ({})", host, now);
- if (startedEngines.decrementAndGet() <= 0) {
- Thread stopSoon = new Thread(this);
+ if (startedRemoteEngines.decrementAndGet() <= 0) {
+ log.info("All remote engines have ended test, starting RemoteTestStopper thread");
+ Thread stopSoon = new Thread(() -> endTest(true), "RemoteTestStopper");
// the calling thread is a daemon; this thread must not be
// see Bug 59391
stopSoon.setDaemon(false);
@@ -1258,16 +1283,7 @@ public class JMeter implements JMeterPlugin {
@Override
public void testEnded() {
- long now = System.currentTimeMillis();
- println("Tidying up ... @ "+new Date(now)+" ("+now+")");
- try {
- generateReport();
- } catch (Exception e) {
- System.err.println("Error generating the report: "+e);//NOSONAR
- log.error("Error generating the report",e);
- }
- checkForRemainingThreads();
- println("... end of run");
+ endTest(false);
}
@Override
@@ -1284,52 +1300,42 @@ public class JMeter implements JMeterPlugin {
}
}
- /**
- * This is a hack to allow listeners a chance to close their files. Must
- * implement a queue for sample responses tied to the engine, and the
- * engine won't deliver testEnded signal till all sample responses have
- * been delivered. Should also improve performance of remote JMeter
- * testing.
- */
- @Override
- public void run() {
+ private void endTest(boolean isDistributed) {
long now = System.currentTimeMillis();
- println("Tidying up remote @ "+new Date(now)+" ("+now+")");
- if (engines!=null){ // it will be null unless remoteStop = true
- println("Exiting remote servers");
- for (JMeterEngine e : engines){
- e.exit();
- }
- }
- try {
- TimeUnit.SECONDS.sleep(5); // Allow listeners to close files
- } catch (InterruptedException ignored) {
- Thread.currentThread().interrupt();
+ if (isDistributed) {
+ println("Tidying up remote @ "+new Date(now)+" ("+now+")");
+ } else {
+ println("Tidying up ... @ "+new Date(now)+" ("+now+")");
}
- ClientJMeterEngine.tidyRMI(log);
- try {
- generateReport();
- } catch (Exception e) {
- System.err.println("Error generating the report: "+e);//NOSONAR
- log.error("Error generating the report",e);
+
+ if (isDistributed) {
+ if (remoteStop) {
+ println("Exiting remote servers:"+remoteEngines);
+ for (JMeterEngine engine : remoteEngines){
+ println("Exiting remote server:"+engine);
+ engine.exit();
+ }
+ }
+ try {
+ TimeUnit.SECONDS.sleep(5); // Allow listeners to close files
+ } catch (InterruptedException ignored) {
+ Thread.currentThread().interrupt();
+ }
+ ClientJMeterEngine.tidyRMI(log);
}
- checkForRemainingThreads();
- println("... end of run");
- }
- /**
- * Generate report
- */
- private void generateReport() {
if(reportGenerator != null) {
try {
log.info("Generating Dashboard");
reportGenerator.generate();
log.info("Dashboard generated");
- } catch (GenerationException ex) {
- log.error("Error generating dashboard: {}", ex, ex);
+ } catch (Exception ex) {
+ System.err.println("Error generating the report: "+ex);//NOSONAR
+ log.error("Error generating the report: {}", ex.getMessage(), ex);
}
}
+ checkForRemainingThreads();
+ println("... end of run");
}
/**
diff --git a/src/core/src/main/java/org/apache/jmeter/engine/ClientJMeterEngine.java b/src/core/src/main/java/org/apache/jmeter/engine/ClientJMeterEngine.java
index 5574655..8e4c001 100644
--- a/src/core/src/main/java/org/apache/jmeter/engine/ClientJMeterEngine.java
+++ b/src/core/src/main/java/org/apache/jmeter/engine/ClientJMeterEngine.java
@@ -232,4 +232,9 @@ public class ClientJMeterEngine implements JMeterEngine {
public String getHost() {
return hostAndPort;
}
+
+ @Override
+ public String toString() {
+ return "ClientJMeterEngine [hostAndPort=" + hostAndPort + "]";
+ }
}
diff --git a/src/core/src/main/java/org/apache/jmeter/engine/DistributedRunner.java b/src/core/src/main/java/org/apache/jmeter/engine/DistributedRunner.java
index b08f1cc..b1b414b 100644
--- a/src/core/src/main/java/org/apache/jmeter/engine/DistributedRunner.java
+++ b/src/core/src/main/java/org/apache/jmeter/engine/DistributedRunner.java
@@ -23,6 +23,7 @@ import java.io.OutputStream;
import java.io.PrintStream;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
@@ -123,21 +124,29 @@ public class DistributedRunner {
* @param addresses list of the DNS names or IP addresses of the remote testing engines
*/
public void start(List<String> addresses) {
- println("Starting remote engines");
long now = System.currentTimeMillis();
- println("Starting the test @ " + new Date(now) + " (" + now + ")");
+ println("Starting distributed test with remote engines:" + addresses +" @ " + new Date(now) + " (" + now + ")");
+ List<JMeterEngine> startedEngines = new ArrayList<>(addresses.size());
+ List<JMeterEngine> failedEngines = new ArrayList<>(addresses.size());
for (String address : addresses) {
+ JMeterEngine engine = engines.get(address);
try {
- if (engines.containsKey(address)) {
- engines.get(address).runTest();
+ if (engine != null) {
+ engine.runTest();
+ startedEngines.add(engine);
} else {
log.warn(HOST_NOT_FOUND_MESSAGE, address);
+ failedEngines.add(engine);
}
} catch (IllegalStateException | JMeterEngineException e) { // NOSONAR already reported to user
+ failedEngines.add(engine);
JMeterUtils.reportErrorToUser(e.getMessage(), JMeterUtils.getResString("remote_error_starting")); // $NON-NLS-1$
}
}
- println("Remote engines have been started");
+ println("Remote engines have been started:"+engines);
+ if (!failedEngines.isEmpty()) {
+ errln("The following remote engines have not started:"+failedEngines);
+ }
}
/**
@@ -153,7 +162,8 @@ public class DistributedRunner {
println("Stopping remote engines");
for (String address : addresses) {
try {
- if (engines.containsKey(address)) {
+ JMeterEngine engine = engines.get(address);
+ if (engine != null) {
engines.get(address).stopTest(true);
} else {
log.warn(HOST_NOT_FOUND_MESSAGE, address);
@@ -241,6 +251,11 @@ public class DistributedRunner {
stdout.println(s);
}
+ private void errln(String s) {
+ log.error(s);
+ stderr.println(s);
+ }
+
private void errln(String s, Exception e) {
log.error(s, e);
stderr.println(s + ": ");
diff --git a/xdocs/changes.xml b/xdocs/changes.xml
index cf18e24..86fda4e 100644
--- a/xdocs/changes.xml
+++ b/xdocs/changes.xml
@@ -232,6 +232,8 @@ to view the last release notes of version 5.1.1.
<li><bug>63490</bug>At end of scheduler duration lots of Samplers gets executed at the same time</li>
<li><pr>480</pr><pr>482</pr>Fix a few typos in comments and log messages. Based on patch by Anass Benomar (anassbenomar at gmail.com)</li>
<li><bug>63751</bug>Correct a typo in Chinese translations. Reported by Jinliang Wang (wjl31802 at 126.com)</li>
+ <li><bug>63723</bug>Distributed testing: JMeter master ends distributed test though some threads still are active</li>
+ <li><bug>63614</bug>Distributed testing: Unable to generate Dashboard report at end of load test</li>
</ul>
<!-- =================== Thanks =================== -->