diff --git a/README.md b/README.md index 107d0bec..5ed91b92 100644 --- a/README.md +++ b/README.md @@ -104,13 +104,13 @@ With all listeners we have, we can take measure in different places 8. response body start received ( ContentListener#onContent() ) 9. response body completed ( CompleteListener#onComplete() ) -#### Response Time +#### Latency Time -The responseTime is the time taken just before 1. and 9. +The latencyTime is the time taken just before 2. and 6. (time to get the first byte of the response) -#### Latency Time +#### Response Time -The latencyTime is the time taken just before 2. and 6. +The responseTime is the time taken just before 1. and 9. (time to get the last byte of the response) ### Using uber jar diff --git a/jetty-load-generator-client/pom.xml b/jetty-load-generator-client/pom.xml index 03e4db6d..ec09d886 100644 --- a/jetty-load-generator-client/pom.xml +++ b/jetty-load-generator-client/pom.xml @@ -129,4 +129,23 @@ + + + debug + + + + org.apache.maven.plugins + maven-surefire-plugin + + + DEBUG + + + + + + + + diff --git a/jetty-load-generator-client/src/main/java/org/mortbay/jetty/load/generator/LoadGenerator.java b/jetty-load-generator-client/src/main/java/org/mortbay/jetty/load/generator/LoadGenerator.java index eea4c894..15870ab9 100644 --- a/jetty-load-generator-client/src/main/java/org/mortbay/jetty/load/generator/LoadGenerator.java +++ b/jetty-load-generator-client/src/main/java/org/mortbay/jetty/load/generator/LoadGenerator.java @@ -294,6 +294,7 @@ public void failed(Throwable x) { logger.debug("failed tree for {}", resource); } callback.failed(x); + LoadGenerator.this.interrupt(); } }, nodes); Sender sender = new Sender(client, warmup, treeCallback); diff --git a/jetty-load-generator-client/src/test/java/org/mortbay/jetty/load/generator/FailFastTest.java b/jetty-load-generator-client/src/test/java/org/mortbay/jetty/load/generator/FailFastTest.java new file mode 100644 index 00000000..4b4f80bb --- /dev/null +++ b/jetty-load-generator-client/src/test/java/org/mortbay/jetty/load/generator/FailFastTest.java @@ -0,0 +1,162 @@ +// +// ======================================================================== +// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.mortbay.jetty.load.generator; + +import org.eclipse.jetty.client.api.Request; +import org.eclipse.jetty.server.HttpConfiguration; +import org.eclipse.jetty.server.HttpConnectionFactory; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.server.handler.StatisticsHandler; +import org.eclipse.jetty.server.session.SessionHandler; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.servlet.StatisticsServlet; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; +import org.eclipse.jetty.util.thread.QueuedThreadPool; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.atomic.AtomicInteger; + +public class FailFastTest +{ + + private static final Logger LOGGER = Log.getLogger( FailFastTest.class ); + protected Resource resource; + protected Server server; + protected ServerConnector connector; + TestHandler testHandler; + + + @Before + public void startJetty() + throws Exception + { + StatisticsHandler statisticsHandler = new StatisticsHandler(); + QueuedThreadPool serverThreads = new QueuedThreadPool(); + serverThreads.setName( "server" ); + server = new Server( serverThreads ); + connector = new ServerConnector( server, new HttpConnectionFactory( new HttpConfiguration() ) ); + server.addConnector( connector ); + server.setHandler( statisticsHandler ); + ServletContextHandler statsContext = new ServletContextHandler( statisticsHandler, "/" ); + statsContext.addServlet( new ServletHolder( new StatisticsServlet() ), "/stats" ); + testHandler = new TestHandler(); + testHandler.server = server; + statsContext.addServlet( new ServletHolder( testHandler ), "/" ); + statsContext.setSessionHandler( new SessionHandler() ); + server.start(); + } + + @After + public void stopJetty() + throws Exception + { + if ( server.isRunning() ) + { + server.stop(); + } + } + + @Test + public void should_fail_fast() + throws Exception + { + AtomicInteger onFailure = new AtomicInteger( 0 ), onCommit = new AtomicInteger( 0 ); + LoadGenerator.Builder builder = // + new LoadGenerator.Builder() // + .host( "localhost" ) // + .port( connector.getLocalPort() ) // + .resource( new Resource( "/index.html?fail=5" ) ) // + .warmupIterationsPerThread( 1 ) // + .usersPerThread( 1 ) // + .threads( 1 ) // + .resourceRate( 5 ) + .iterationsPerThread( 25 ) // + //.runFor( 10, TimeUnit.SECONDS ) // + .requestListener( new Request.Listener.Adapter() { + @Override + public void onFailure( Request request, Throwable failure ) + { + LOGGER.info( "fail: {}", onFailure.incrementAndGet() ); + } + + @Override + public void onCommit( Request request ) + { + LOGGER.info( "onCommit: {}", onCommit.incrementAndGet() ); + } + } ); + boolean exception = false; + try + { + builder.build().begin().get(); + } + catch ( Exception e ) + { + exception = true; + } + Assert.assertTrue( exception ); + LOGGER.info( "onFailure: {}, onCommit: {}", onFailure, onCommit); + int onFailureCall = onFailure.get(); + Assert.assertTrue("onFailureCall is " + onFailureCall, onFailureCall < 5); + } + + + static class TestHandler + extends HttpServlet + { + + AtomicInteger getNumber = new AtomicInteger( 0 ); + Server server; + + @Override + protected void service( HttpServletRequest request, HttpServletResponse response ) + throws ServletException, IOException + { + String fail = request.getParameter( "fail" ); + if ( getNumber.get() >= Integer.parseInt( fail ) ) + { + try + { + server.stop(); + } + catch ( Exception e ) + { + throw new RuntimeException( e.getMessage(), e ); + } + } + response.getOutputStream().write( "Jetty rocks!!".getBytes() ); + response.flushBuffer(); + getNumber.addAndGet( 1 ); + } + } + +} diff --git a/jetty-load-generator-starter/pom.xml b/jetty-load-generator-starter/pom.xml index 641c613f..bab8bbf8 100644 --- a/jetty-load-generator-starter/pom.xml +++ b/jetty-load-generator-starter/pom.xml @@ -106,16 +106,27 @@ - - org.apache.maven.plugins - maven-surefire-plugin - - - DEBUG - - - + + + debug + + + + org.apache.maven.plugins + maven-surefire-plugin + + + DEBUG + DEBUG + + + + + + + + diff --git a/jetty-load-generator-starter/simple_profile.groovy b/jetty-load-generator-starter/simple_profile.groovy deleted file mode 100644 index e4cb9fda..00000000 --- a/jetty-load-generator-starter/simple_profile.groovy +++ /dev/null @@ -1,3 +0,0 @@ -import org.mortbay.jetty.load.generator.Resource - -return new Resource(new Resource("index.html"), new Resource("hello")) diff --git a/jetty-load-generator-starter/src/main/java/org/mortbay/jetty/load/generator/starter/AbstractLoadGeneratorStarter.java b/jetty-load-generator-starter/src/main/java/org/mortbay/jetty/load/generator/starter/AbstractLoadGeneratorStarter.java index e7e0c23e..eae035b9 100644 --- a/jetty-load-generator-starter/src/main/java/org/mortbay/jetty/load/generator/starter/AbstractLoadGeneratorStarter.java +++ b/jetty-load-generator-starter/src/main/java/org/mortbay/jetty/load/generator/starter/AbstractLoadGeneratorStarter.java @@ -60,6 +60,8 @@ public abstract class AbstractLoadGeneratorStarter private Resource resource; + private Request.Listener[] listeners; + public AbstractLoadGeneratorStarter( LoadGeneratorStarterArgs runnerArgs ) { this.starterArgs = runnerArgs; @@ -83,6 +85,11 @@ public void run() .warmupIterationsPerThread( starterArgs.getWarmupNumber() ) // .scheme( starterArgs.getScheme() ); // + if (starterArgs.getThreads() > 0) + { + loadGeneratorBuilder.threads( starterArgs.getThreads() ); + } + if ( starterArgs.getMaxRequestsQueued() > 0 ) { loadGeneratorBuilder.maxRequestsQueued( starterArgs.getMaxRequestsQueued() ); @@ -159,7 +166,12 @@ protected Resource.Listener[] getResourceListeners() protected Request.Listener[] getListeners() { - return new Request.Listener[0]; + return listeners == null ? new Request.Listener[0] : this.listeners; + } + + protected void setListeners(Request.Listener[] listeners) + { + this.listeners = listeners; } public ExecutorService getExecutorService() diff --git a/jetty-load-generator-starter/src/main/java/org/mortbay/jetty/load/generator/starter/LoadGeneratorStarter.java b/jetty-load-generator-starter/src/main/java/org/mortbay/jetty/load/generator/starter/LoadGeneratorStarter.java index 9248dacf..939a6da2 100644 --- a/jetty-load-generator-starter/src/main/java/org/mortbay/jetty/load/generator/starter/LoadGeneratorStarter.java +++ b/jetty-load-generator-starter/src/main/java/org/mortbay/jetty/load/generator/starter/LoadGeneratorStarter.java @@ -19,6 +19,8 @@ package org.mortbay.jetty.load.generator.starter; import com.beust.jcommander.JCommander; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; /** * @@ -27,6 +29,8 @@ public class LoadGeneratorStarter extends AbstractLoadGeneratorStarter { + private static final Logger LOGGER = Log.getLogger( LoadGeneratorStarter.class); + public LoadGeneratorStarter( LoadGeneratorStarterArgs runnerArgs ) { super( runnerArgs ); @@ -67,9 +71,8 @@ public static void main( String[] args ) } catch ( Exception e ) { - e.printStackTrace(); + LOGGER.info( "error happened", e); new JCommander( runnerArgs ).usage(); - System.exit( 1 ); } } diff --git a/jetty-load-generator-starter/src/main/java/org/mortbay/jetty/load/generator/starter/LoadGeneratorStarterArgs.java b/jetty-load-generator-starter/src/main/java/org/mortbay/jetty/load/generator/starter/LoadGeneratorStarterArgs.java index 4f3e2d61..eb2e1f0e 100644 --- a/jetty-load-generator-starter/src/main/java/org/mortbay/jetty/load/generator/starter/LoadGeneratorStarterArgs.java +++ b/jetty-load-generator-starter/src/main/java/org/mortbay/jetty/load/generator/starter/LoadGeneratorStarterArgs.java @@ -49,6 +49,9 @@ public class LoadGeneratorStarterArgs @Parameter( names = { "--users", "-u" }, description = "Simulated users number" ) private int users = 1; + @Parameter( names = { "--threads" }, description = "Threads number" ) + private int threads = 0; + @Parameter( names = { "--transaction-rate", "-tr" }, description = "Transaction rate / second" ) private int transactionRate = 1; @@ -358,18 +361,28 @@ public void setMaxRequestsQueued( int maxRequestsQueued ) this.maxRequestsQueued = maxRequestsQueued; } + public int getThreads() + { + return threads; + } + + public void setThreads( int threads ) + { + this.threads = threads; + } + @Override public String toString() { return "LoadGeneratorStarterArgs{" + "profileXmlPath='" + profileXmlPath + '\'' + ", profileJsonPath='" + profileJsonPath + '\'' + ", profileGroovyPath='" + profileGroovyPath + '\'' + ", host='" + host + '\'' - + ", port=" + port + ", users=" + users + ", transactionRate=" + transactionRate + ", transport='" - + transport + '\'' + ", selectors=" + selectors + ", runningTime=" + runningTime + ", runningTimeUnit='" - + runningTimeUnit + '\'' + ", runIteration=" + runIteration + ", reportHost='" + reportHost + '\'' - + ", scheme='" + scheme + '\'' + ", reportPort=" + reportPort + ", notInterrupt=" + notInterrupt - + ", statsFile='" + statsFile + '\'' + ", params=" + params + ", help=" + help + ", displayStatsAtEnd=" - + displayStatsAtEnd + ", collectServerStats=" + collectServerStats + ", warmupNumber=" + warmupNumber - + ", maxRequestsQueued=" + maxRequestsQueued + '}'; + + ", port=" + port + ", users=" + users + ", threads=" + threads + ", transactionRate=" + transactionRate + + ", transport='" + transport + '\'' + ", selectors=" + selectors + ", runningTime=" + runningTime + + ", runningTimeUnit='" + runningTimeUnit + '\'' + ", runIteration=" + runIteration + ", reportHost='" + + reportHost + '\'' + ", scheme='" + scheme + '\'' + ", reportPort=" + reportPort + ", notInterrupt=" + + notInterrupt + ", statsFile='" + statsFile + '\'' + ", params=" + params + ", help=" + help + + ", displayStatsAtEnd=" + displayStatsAtEnd + ", collectServerStats=" + collectServerStats + + ", warmupNumber=" + warmupNumber + ", maxRequestsQueued=" + maxRequestsQueued + '}'; } public enum Transport { diff --git a/jetty-load-generator-starter/src/test/java/org/mortbay/jetty/load/generator/starter/LoadGeneratorStarterTest.java b/jetty-load-generator-starter/src/test/java/org/mortbay/jetty/load/generator/starter/LoadGeneratorStarterTest.java index 25620e1b..587eb417 100644 --- a/jetty-load-generator-starter/src/test/java/org/mortbay/jetty/load/generator/starter/LoadGeneratorStarterTest.java +++ b/jetty-load-generator-starter/src/test/java/org/mortbay/jetty/load/generator/starter/LoadGeneratorStarterTest.java @@ -16,7 +16,8 @@ package org.mortbay.jetty.load.generator.starter; -import com.fasterxml.jackson.databind.ObjectMapper; +import com.beust.jcommander.JCommander; +import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.HttpConnectionFactory; import org.eclipse.jetty.server.Server; @@ -27,6 +28,8 @@ import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.servlet.StatisticsServlet; import org.eclipse.jetty.util.IO; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.thread.QueuedThreadPool; import org.junit.After; import org.junit.Assert; @@ -42,7 +45,6 @@ import java.io.IOException; import java.io.Reader; import java.nio.file.Files; -import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; @@ -54,6 +56,7 @@ */ public class LoadGeneratorStarterTest { + private static final Logger LOGGER = Log.getLogger( LoadGeneratorStarterTest.class); Server server; @@ -80,19 +83,24 @@ public void startJetty() statsContext.addServlet( new ServletHolder( new StatisticsServlet() ), "/stats" ); testHandler = new TestHandler(); + testHandler.server = server; statsContext.addServlet( new ServletHolder( testHandler ), "/" ); statsContext.setSessionHandler( new SessionHandler() ); server.start(); + } @After public void stopJetty() throws Exception { - server.stop(); + if (server.isRunning()) + { + server.stop(); + } } @Test @@ -119,15 +127,75 @@ public void simpletest() args.add( "3" ); args.add( "--profile-groovy-path" ); args.add( "src/test/resources/loadgenerator_profile.groovy" ); - LoadGeneratorStarter.main( args.toArray( new String[args.size()] ) ); - int getNumber = testHandler.getNumber.get(); - + LOGGER.debug( "received get: {}", getNumber ); Assert.assertTrue( "getNumber return: " + getNumber, getNumber > 10 ); } + @Test + public void fail_fast() + throws Exception + { + + List args = new ArrayList<>(); + args.add( "--warmup-number" ); + args.add( "10" ); + args.add( "-h" ); + args.add( "localhost" ); + args.add( "--port" ); + args.add( Integer.toString( connector.getLocalPort() ) ); + args.add( "--running-time" ); + args.add( "10" ); + args.add( "--running-time-unit" ); + args.add( "s" ); + args.add( "--transaction-rate" ); + args.add( "3" ); + args.add( "--transport" ); + args.add( "http" ); + args.add( "--users" ); + args.add( "1" ); + args.add( "--profile-groovy-path" ); + args.add( "src/test/resources/single_resource.groovy" ); + + LoadGeneratorStarterArgs runnerArgs = new LoadGeneratorStarterArgs(); + new JCommander( runnerArgs, args.toArray( new String[args.size()] ) ); + + AtomicInteger onFailure = new AtomicInteger( 0 ), onCommit = new AtomicInteger( 0 ); + Request.Listener.Adapter adapter = new Request.Listener.Adapter() { + @Override + public void onFailure( Request request, Throwable failure ) + { + LOGGER.info( "fail: {}", onFailure.incrementAndGet() ); + } + + @Override + public void onCommit( Request request ) + { + LOGGER.info( "onCommit: {}", onCommit.incrementAndGet() ); + } + }; + + LoadGeneratorStarter runner = new LoadGeneratorStarter( runnerArgs ); + runner.setListeners( new Request.Listener[] { adapter } ); + boolean exception = false; + try + { + runner.run(); + } + catch ( Exception e ) + { + exception = true; + } + LOGGER.info( "onFailure: {}, onCommit: {}", onFailure, onCommit); + Assert.assertTrue("not in exception", exception ); + int getNumber = testHandler.getNumber.get(); + LOGGER.debug( "received get: {}", getNumber ); + Assert.assertTrue( "getNumber return: " + getNumber, getNumber == 5 ); + Assert.assertTrue( onFailure.get() < 10 ); + } + @Test public void json_serial_deserial_from_groovy() throws Exception @@ -147,6 +215,8 @@ static class TestHandler AtomicInteger getNumber = new AtomicInteger( 0 ), postNumber = new AtomicInteger( 0 ); + Server server; + @Override protected void service( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException @@ -160,6 +230,21 @@ protected void service( HttpServletRequest request, HttpServletResponse response { case "GET": { + String fail = request.getParameter( "fail" ); + if (fail != null) + { + if ( getNumber.get() >= Integer.parseInt( fail ) ) + { + try + { + server.stop(); + } + catch ( Exception e ) + { + throw new RuntimeException( e.getMessage(), e ); + } + } + } response.getOutputStream().write( "Jetty rocks!!".getBytes() ); response.flushBuffer(); getNumber.addAndGet( 1 ); diff --git a/jetty-load-generator-starter/src/test/resources/single_resource.groovy b/jetty-load-generator-starter/src/test/resources/single_resource.groovy new file mode 100644 index 00000000..5f8a1533 --- /dev/null +++ b/jetty-load-generator-starter/src/test/resources/single_resource.groovy @@ -0,0 +1,3 @@ +import org.mortbay.jetty.load.generator.Resource + +return new Resource("/index.html?fail=5")