Back

TechnologyOct 13, 2009

The Grinder – Load Testing Web Applications

Eric Woods

I ended up using The Grinder load tester for work recently and found it to be remarkably lightweight and easy to use. The Grinder is a free, Java open source load testing framework that uses Jython scripts for running the load tests. Jython is interesting in itself — it’s a Java implementation of Python that is compiled to bytecode and runs on a JVM and also allows you to access the Java libraries using the Python syntax.

But for the purposes of this post, I’m going to give a quick rundown of how The Grinder works, how to set it up, and then how to use the TCPProxy to generate Jython test scripts for the purposes of load testing web applications.

the players

There are essentially 4 main components to The Grinder.

  1. Agents – You’ll typically have a single agent on each load injector machine, which will start a configured number of worker processes. If the agents can connect to the console, they’ll wait for a signal to start before passing off a local grinder.properties file to the worker processes.

  2. Workers – Workers, as you would imagine, do the work. That is, they actually execute the load test scripts. The grinder.properties file that is passed to the worker by the agent defines, among other things, the script that the worker will execute against the target, how many threads the worker will spawn, and how many times each one of those threads will execute the script.

  3. Console – This is a GUI that can be used to control the agents, and also displays the collected statistics that are reported back from the workers.

  4. TCPProxy – The TCPProxy is interposed between your browser and the target server, and can be used to generate scripts by recording the activity of your browser, which can subsequently be executed by the worker processes. This is really handy for generating load tests that simulate user interaction with a web application

installing the grinder

This part is incredibly easy.

  1. Download the Grinder at SourceForge.net. You’ll need to at least have J2SE 1.4 on your machine to run The Grinder 3.

  2. Unzip it.

  3. You might want to create a few scripts for setting the environment variables and starting The Grinder for your own convenience. These are for Windows.

setGrinderEnv.cmd [code lang=”dos”] set GRINDERPATH=C:\grinder-3.2 set GRINDERPROPERTIES=C:\grinder-3.2\examples\grinder.properties set CLASSPATH=%GRINDERPATH%\lib\grinder.jar; set JAVA_HOME=C:\Program Files\Java\j2re1.4.2_15 PATH=%JAVA_HOME%\bin;%PATH% [/code]

startAgent.cmd [code lang=”dos”] call C:\grinder-3.2\setGrinderEnv.cmd echo %CLASSPATH% java -cp %CLASSPATH% net.grinder.Grinder %GRINDERPROPERTIES% [/code]

startConsole.cmd [code lang=”dos”] call C:\grinder-3.2\setGrinderEnv.cmd java -cp %CLASSPATH% net.grinder.Console [/code]

startProxy.cmd [code lang=”dos”] call C:\grinder-3.2\setGrinderEnv.cmd java -cp %CLASSPATH% net.grinder.TCPProxy -console -http > grinder.py [/code]

recording scripts with tcpproxy

To create a test script with the TCPProxy, you’ll need to configure your browser to point to the TCPProxy so that it can record your actions.

Configure the Browser The first thing that you’ll need to do is to configure your web browser to use the TCPProxy as the HTTP proxy. The steps required to do this will be different depending on the browser that you’re using. I’m using Mozilla/Firefox 3.0, so I selected:

1.  Tools  -> Options -> Advanced  Settings. 2.  I then configured the HTTP Proxy to point to localhost:8001 (TCPProxy default listener port).

Record the Script With your browser pointed to the TCPProxy use the shortcut you created in the previous section to start the proxy. Let’s take a quick look at the options specified on startup.

[code lang=”dos”] java -cp %CLASSPATH% net.grinder.TCPProxy -console -http > grinder.py [/code]

console – displays the console below, which can be used to add comments during the recording process and to shut the TCPProxy down cleanly. http – Adds the request/response filters to produce a script readable by The Grinder’s HTTP plugin. grinder.py – The script will be output locally to a file named grinder.py. You can rename this or relocate where it will be created if you like.

Of course, there are many more startup options I won’t cover now, but which you can check out here.

Once the proxy has started, the TCPProxy console should appear.  The TCPProxy will record all of the requests, and time between requests, that your browser makes until you press the stop button. You can add ad-hoc comments throughout the process if you wish.

Here are some snippets from a script created by starting the TCPProxy and going to Credera.com.

[code lang=”python”] connectionDefaults.defaultHeaders = \ ( NVPair(‘User-Agent’, ‘Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.12) Gecko/2009070611 Firefox/3.0.12’), NVPair(‘Accept-Encoding’, ‘gzip,deflate’), NVPair(‘Accept-Language’, ‘en-us,en;q=0.5’), NVPair(‘Accept-Charset’, ‘ISO-8859-1,utf-8;q=0.7,*;q=0.7’), )

headers0= \ ( NVPair(‘Accept’, ‘text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8’), )

headers1= \ ( NVPair(‘Accept’, ‘text/css,*/*;q=0.1’), NVPair(‘Referer’, ‘http://www.credera.com/default.html’), ) … … … url0 = ‘http://www.credera.com:80’ url1 = ‘http://www.google-analytics.com:80’ [/code]

Headers and urls are defined once at the top of the script.

[code lang=”python”] request101 = HTTPRequest(url=url0, headers=headers0) request101 = Test(101, ‘GET /’).wrap(request101)

request102 = HTTPRequest(url=url0, headers=headers0) request102 = Test(102, ‘GET Default.aspx’).wrap(request102)

request103 = HTTPRequest(url=url0, headers=headers0) request103 = Test(103, ‘GET default.html’).wrap(request103) … … … [/code]

An HTTPRequest is created for each request and wrapped in a Test object.

[code lang=”python”] class TestRunner: “””A TestRunner instance is created for each worker thread.””” … … … def __call__(self): “””This method is called for every run performed by the worker thread.””” self.page1() # GET / (requests 101-112)

grinder.sleep(234) self.page2() # GET WebResource.axd (request 201)

grinder.sleep(31) self.page3() # GET WebResource.axd (requests 301-303)

grinder.sleep(63) self.page4() # GET ga.js (request 401)

grinder.sleep(47) self.page5() # GET CrederaButterfly_v4.swf (request 501)

grinder.sleep(109) self.page6() # GET __utm.gif (request 601) [/code]

TestRunner instance is created for each worker process thread and executes the _call_ method a predetermined amount of times, or indefinitely, depending on the configuration (set in grinder.properties).

A few things of note:

  • You can see that the sleep method is incorporated into the script to simulate user “think time”.

  • The value passed to sleep is the mean value of a pseudo normal distribution. So actual sleep times will vary slightly when the script is played back.

  • Requests are executed serially by a single worker thread. This differs slightly from how a browser will function in that the browser can spawn multiple parallel threads to make simultaneous requests.

configuring script playback

Before executing your generated test script, you’ll want to configure a few properties on the grinder.properties file located at <installation directory>/examples/ by default. We’ll just look at the most basic properties to get you started. You’ll have a properties file for each agent process.

grinder.script – This is the name of the script you will execute (e.g. grinder.py). The value is relative to the directory in which the property file resides. grinder.processes – This is the number of workers that the agent will start grinder.threads – This is the number of threads each worker will spawn. Each thread will execute the script. grinder.runs – This is the number of times each worker thread will run the script. If this is set to 0, the script will run indefinitely until stopped by the console.

So you might have something like the following in your grinder.properties file: [code lang=”dos”] grinder.script = grinder.py grinder.processes = 1 grinder.threads = 10 grinder.runs = 50 [/code] If you’re running your agent on a different machine than the console, you’ll also need to configure those properties that allow the agent to contact the console – grinder.consoleHost and grinder.consolePort. The default port for the console is 6372.

script playback

Put your grinder.py script in a location where the properties file can read it. We defined grinder.py above as being in the same directory as the grinder.properties file, so put your script in the <installation directory>/examples/ directory. Then:

1. Start the Grinder console using startConsole.cmd. 2. Start the agents using startAgent.bat.

If you click on the Processes tab of the console, you should see your agent(s) have connected.  After detecting the console, the agents will wait for a signal to start. Just click the start button in the upper left corner of the console to have the agents kick the worker processes off.

While the workers are running the test scripts, you can click on the Results tab to see the response times for the various requests. These statistics are also presented in graphical form — as you would probably guess — on the Graphs tab. You can later export these statistics to an Excel file for analysis if need be.

That, in a nutshell, is how you can use The Grinder’s TCPProxy to very easily generate load test scripts to simulate user interaction with your web application.