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