Performing preliminary load testing on web applications does not require the use of complex tools such as Load Runner. An open-source tool called The Grinder provides a simple way for developers to perform load testing before handing out the application to Quality Assurance. With The Grinder, the load tests are defined using Jython scripts, and then are run by agents which could be distributed over the network. A property file defines which load tests should be run, how many times, and on how many threads. A central application referred to as the console starts the agents, collects the responses, and monitors the number of requests per second (RPS). The authors of this tool provide a short but useful overview of the architecture.

Installing The Grinder is a matter of unzipping the archive since the tool is written in Java. However, making the tool work as indented requires to dig into the documentation a little bit. This article is intended to assist people interested in getting up and running the tool on a Widows environment as quickly as possible.

Configuration

On my desktop, I have created a folder called grinder. The archive downloaded from Sourceforge was extracted in this folder as grinder-3.1. A couple of batch files will need to be created to conveniently start up the agents and the console. The first batch file setGrinderEnv.bat defines environment variables which will be reused:

set GRINDERPATH="C:\Documents and Settings\pierre rebours\Desktop\grinder\grinder-3.1"
set GRINDERPROPERTIES="C:\Documents and Settings\pierre rebours\Desktop\grinder\grinder.properties"
set CLASSPATH=%GRINDERPATH%\lib\grinder.jar;%CLASSPATH%

Since The Grinder is a Java-based tool, the infamous environment variable CLASS_PATH needs to be defined. The environment variable GRINDERPROPERTIES refers to the path of the property file. More about this file in a moment.

Starting the console is done using startConsole.bat:

call "C:\Documents and Settings\pierre rebours\Desktop\grinder\setGrinderEnv.cmd"
java -cp %CLASSPATH% net.grinder.Console 

Similarly, startAgent.bat starts the agent(s)

call "C:\Documents and Settings\pierre rebours\Desktop\grinder\setGrinderEnv.cmd"
java -cp %CLASSPATH% net.grinder.Grinder %GRINDERPROPERTIES%

Property File

The load test is defined using the property file. The configuration entry grinder.script = http.py indicates the path of the Jython script which will be run. Other interesting configurations include the number of processes created by each agent (grinder.processes) and the number of threads for each process (grinder.threads).

I would also recommend to explicitly specify the log and the temporary directories:

# The directory in which worker process logs should be created. 
grinder.logDirectory = log

# Additional arguments to worker process JVMs.
grinder.jvm.arguments = -Dpython.cachedir="C:\\Documents and Settings\\pierre rebours\\Desktop\\grinder\\tmp"

Load Test Specification

Simulating a request to a web server could be defined as follows:

from net.grinder.script import Test
from net.grinder.script.Grinder import grinder
from net.grinder.plugin.http import HTTPPluginControl, HTTPRequest
from HTTPClient import NVPair

test1 = Test(1, "WebService")
request1 = test1.wrap(HTTPRequest())

class TestRunner:
	def __call__(self):		
		request1.GET("http://localhost/MyApplication/PresentationService.ashx")

A custom proxy server can also be defined. In the following case, the proxy is defined as localhost on port 8888 which is default the port Fiddler is listening to. The full name of the computer needs to be provided since Fiddler will bypass localhost.

# Import Directives
# ....

control = HTTPPluginControl.getConnectionDefaults()
control.setProxyServer("localhost", 8888)
control.setFollowRedirects(0)

test1 = Test(1, "WebService")
request1 = test1.wrap(HTTPRequest())

class TestRunner:
	def __call__(self):		
		request1.GET("http://MyComputer/MyApplication/PresentationService.ashx")

Adding a new request is a matter of creating a new test and wrap it as a HTTP request. The following script simulates two requests:

# Import Directives
# ....

test1 = Test(1, "WebService")
request1 = test1.wrap(HTTPRequest())

test2 = Test(1, "CSSFile")
request2 = test1.wrap(HTTPRequest())

class TestRunner:
	def __call__(self):		
		request1.GET("http://localhost/MyApplication/PresentationService.ashx")
                request2.GET("http://localhost/MyApplication/Style.css")

The load test can be written manually or can be generated through the TCP proxy provided with The Grinder. Starting the TCP proxy to generate the load case could be done through yet another batch file:

call "C:\Documents and Settings\pierre rebours\Desktop\grinder\setGrinderEnv.cmd"
java -cp %CLASSPATH% net.grinder.TCPProxy -console -http > proxy-output.py

Conclusion

The Grinder remains a simple tool to set up and start testing web applications under heavy load. Running Windows Performance Monitor in parallel allows correlating the number of requests per second (RPS) with underlying performance counters. I would recommend The Grinder to developers who would like to ensure that their web applications respond well under stress before handing them to more experimented testers knowing the in-and-outs of more sophisticated testing tools.

Download icon The source code for this post is available from github.