Skip to content

Commit

Permalink
First commit
Browse files Browse the repository at this point in the history
  • Loading branch information
oleksiyp committed Nov 4, 2011
0 parents commit a606f61
Show file tree
Hide file tree
Showing 13 changed files with 587 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitignore
@@ -0,0 +1,3 @@
.idea
target
loadtest.iml
6 changes: 6 additions & 0 deletions Example1.groovy
@@ -0,0 +1,6 @@
// run: java -jar loadtest.jar -n 10 -d 1000 Example1.groovy
try {
HTTP.get("http://google.com", "http://facebook.com");
} catch (Throwable thr) {
System.err.println(thr);
}
1 change: 1 addition & 0 deletions README
@@ -0,0 +1 @@
This file was created by IntelliJ IDEA 10.5.2 for binding GitHub repository
21 changes: 21 additions & 0 deletions README.textile
@@ -0,0 +1,21 @@
h1. Web load test tool v1.0

Tool could be used to test load your web application using different scenarios. Scripts are written in groovy using special test environment.

h2. Run

Copy one loadtest-1.0.jar and Example*.groovy from repository to one of directories on your computer. Use java to run it from commandline:

java -jar loadtest-1.0.jar Example1.groovy

h2. Environment

In groovy following classes avialable: HTTP and Variations.

h2. License

Load test tool is distributed on terms of BSD-license. Look at src/main/resources/BSD-LICENSE.txt for details.

Groovy used internally by load test tool is licensed under the Apache 2 license:

http://www.apache.org/licenses/LICENSE-2.0.html
Binary file added loadtest-1.0.jar
Binary file not shown.
69 changes: 69 additions & 0 deletions pom.xml
@@ -0,0 +1,69 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.loadtest</groupId>
<artifactId>loadtest</artifactId>
<packaging>jar</packaging>
<version>1.0</version>
<name>Web load test</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy</artifactId>
<version>1.8.3</version>
</dependency>
<dependency>
<groupId>args4j</groupId>
<artifactId>args4j</artifactId>
<version>2.0.16</version>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.3</version>
<executions>
<execution>
<id>unpack</id>
<phase>prepare-package</phase>
<goals>
<goal>unpack-dependencies</goal>
</goals>
<configuration>
<excludes>META-INF/MANIFEST.MF,META-INF/LICENSE.txt,META-INF/maven/**</excludes>
<outputDirectory>${project.build.outputDirectory}</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.3.1</version>
<configuration>
<archive>
<index>true</index>
<manifest>
<mainClass>org.loadtest.LoadTest</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>versions-maven-plugin</artifactId>
<version>1.2</version>
<configuration>
<newVersion>${parsedVersion.majorVersion}.${parsedVersion.minorVersion}</newVersion>
</configuration>
</plugin>
</plugins>
</build>
</project>
28 changes: 28 additions & 0 deletions src/main/java/org/loadtest/Globals.java
@@ -0,0 +1,28 @@
package org.loadtest;

import java.util.HashMap;
import java.util.Map;

/**
* Object used by scripts to interchange values
*/
public class Globals {
private Map values = new HashMap();

public synchronized void put(String name, Object value) {
values.put(name, value);
}

public synchronized Object get(String name) {
return values.get(name);
}
public synchronized long increment(String name) {
Number num = (Number)values.get(name);
if (num == null) {
num = 0L;
}
long res = num.longValue();
values.put(name, res + 1);
return res;
}
}
141 changes: 141 additions & 0 deletions src/main/java/org/loadtest/LoadTest.java
@@ -0,0 +1,141 @@
package org.loadtest;

import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.args4j.CmdLineParser;
import org.kohsuke.args4j.Option;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
* Tool to run test load specified by script using several threads.
* Main entry point.
*/
public class LoadTest {
private Options options;
private ScheduledExecutorService executor;
private ScheduledExecutorService printExecutor;
private ScriptRunner scriptRunner;

public LoadTest(int threads, long delay, File script) {
this.options = new Options(threads, delay, script);
}
public LoadTest(Options options) {
this.options = options;
}

public synchronized void start() {
if (executor != null) {
return;
}
scriptRunner = new ScriptRunner(this, options.scripts);
executor = Executors.newScheduledThreadPool(options.threads);
for (int i = 0; i < options.threads; i++) {
executor.scheduleWithFixedDelay(scriptRunner,
options.delay * i / options.threads,
options.delay,
TimeUnit.MILLISECONDS);
}

printExecutor = Executors.newSingleThreadScheduledExecutor();
printExecutor.scheduleWithFixedDelay(new StatsPrinter(),
500L,
1000L,
TimeUnit.MILLISECONDS);
}

public synchronized void stop() {
if (executor == null) { return; }
executor.shutdownNow();
executor = null;
printExecutor.shutdownNow();
printExecutor = null;
scriptRunner = null;
}

public boolean isStopped() {
return executor == null;
}

public static void main(String[] args) throws IOException {
LoadTest testload = new LoadTest(new Options().parse(args));
Scanner scanner = new Scanner(System.in);
try {
do {
System.out.println("Starting(press q[ENTER] - to quit, [ENTER] - to stop)");
testload.start();
if (scanner.nextLine().equals("q")) {
break;
}
System.out.println("Stopping(press q[ENTER] - to quit, [ENTER] - to start)");
testload.stop();
} while (!scanner.nextLine().equals("q"));
} catch (Throwable thr) {
System.out.println("Breaked");
}
}

public synchronized ScriptRunner getScriptRunner() {
return scriptRunner;
}

private static class Options {
@Option(name="-n", usage="number of threads")
private int threads = 1;

@Option(name="-d", usage="delay between requests")
private long delay = 1;

@Option(name="-s", usage="number of the slowest queries to show in statistics")
private int slowQueriesToShow = 24;

@Argument
private List<File> scripts = new ArrayList();

public Options(int threads, long delay, File script) {
this.threads = threads;
this.delay = delay;
this.scripts.add(script);
}

public Options() {
}

public Options parse(String[] args) {
CmdLineParser parser = new CmdLineParser(this);
try {
parser.parseArgument(args);
if (scripts.isEmpty()) {
usage(parser, new CmdLineException(parser, "specify one or more scripts"));
}
} catch (CmdLineException e) {
usage(parser, e);
}
return this;
}

private void usage(CmdLineParser parser, CmdLineException e) {
System.err.println(e.getMessage());
System.err.println("java -jar loadtest.jar [options...] scripts...");
parser.printUsage(System.err);
System.exit(1);
}
}

private class StatsPrinter implements Runnable {
public void run() {
ScriptRunner runner = getScriptRunner();
if (runner != null) {
runner.getStats().report(options.slowQueriesToShow);
}
}
}

}
74 changes: 74 additions & 0 deletions src/main/java/org/loadtest/ScriptRunner.java
@@ -0,0 +1,74 @@
package org.loadtest;

import groovy.lang.Binding;
import groovy.lang.GroovyCodeSource;
import groovy.lang.GroovyShell;
import org.codehaus.groovy.control.CompilationFailedException;

import java.io.File;
import java.util.List;
import java.util.Random;

/**
* Runs scripts in environment produced by "Classes.groovy" and three bindings:
* GLOBAL - global variables interchanger,
* STATS - statistics object,
* RANDOM - random object
*/
public class ScriptRunner implements Runnable {
private LoadTest load;
private List scripts;
private Stats stats = new Stats();
private Globals globals = new Globals();
private Random random = new Random();
private static ThreadLocal local = new ThreadLocal();

public ScriptRunner(LoadTest load, List scripts) {
this.load = load;
this.scripts = scripts;
}

protected Binding createNewBinding() {
Binding binding = new Binding();
binding.setVariable("STATS", stats);
binding.setVariable("GLOBALS", globals);
binding.setVariable("RANDOM", random);
return binding;
}

public void run() {
Binding binding = createNewBinding();
GroovyShell shell = new GroovyShell(binding);
stats.addRun();
try {
local.set(shell);
GroovyCodeSource functions = new GroovyCodeSource(ScriptRunner.class.getResource("Classes.groovy"));
shell.evaluate(functions);
for (Object script : scripts) {
shell.evaluate((File)script);
}
} catch (CompilationFailedException e) {
stats.addError();
errorCase(e);
} catch (Throwable e) {
stats.addError();
} finally {
local.remove();
}
}

private void errorCase(Exception e) {
if (!load.isStopped()) {
e.printStackTrace();
load.stop();
}
}

public Stats getStats() {
return stats;
}

public static GroovyShell getGroovyShell() {
return (GroovyShell) local.get();
}
}

0 comments on commit a606f61

Please sign in to comment.