Sunday, 23 August 2009

The Missing Grinder 3 Getting Started Guide

I have been looking at The Grinder to see what it is like at performance testing. Though I found the getting started section to cover the tool but it didn't really cover the the I want to click here, here and here and have it just request a page from my web server, so hopefully this will get you past that. I have always found the first five minutes with a tool the complicated ones and need enough to get going and over that first hurdle.



Pre Reqs:
  • A web server that you have permission to test with. For me it is localhost and running on port 80.
  • I am doing this on Ubuntu doing something similar on Windows should do the trick.

The steps:
  1. Download The Grinder 3
  2. Unzip it. I unzipped it to /home/test/Desktop/grinder-3.2
  3. Create a new directory /home/test/Desktop/grinder-3.2/demo. All files created, edited etc should be in here.
  4. Create a new file in demo called grinder.properties I copied the below from the examples directory and changed the grinder.script to simple.py
    • grinder.properties:
      #
      # Sample grinder.properties
      #
      #
      # The properties can be specified in three ways.
      #  - In the console. A properties file in the distribution directory
      #    can be selected in the console.
      #  - As a Java system property in the command line used to start the
      #    agent. (e.g. java -Dgrinder.threads=20 net.grinder.Grinder).
      #  - In an agent properties file. A local properties file named
      #    "grinder.properties" can be placed in the working directory of
      #    each agent, or a different file name passed as an agent command
      #    line argument.
      #
      # Properties present in a console selected file take precedence over
      # agent command line properties, which in turn override those in
      # an agent properties file.
      #
      # Any line which starts with a ; (semi-colon) or a # (hash) is a
      # comment and is ignored. In this example we will use a # for
      # commentary and a ; for parts of the config file that you may wish to
      # enable
      #
      # Please refer to
      # http://net.grinder.sourceforge.net/g3/properties.html for further
      # documentation.
      
      
      #
      # Commonly used properties
      #
      
      # The file name of the script to run.
      #
      # Relative paths are evaluated from the directory containing the
      # properties file. The default is "grinder.py".
      grinder.script = simple.py
      
      # The number of worker processes each agent should start. The default
      # is 1.
      grinder.processes = 1
      
      # The number of worker threads each worker process should start. The
      # default is 1.
      grinder.threads = 1
      
      # The number of runs each worker process will perform. When using the
      # console this is usually set to 0, meaning "run until the console
      # sneds a stop or reset signal". The default is 1.
      grinder.runs = 0
      
      # The IP address or host name that the agent and worker processes use
      # to contact the console. The default is all the network interfaces
      # of the local machine.
      ; grinder.consoleHost = consolehost
      
      # The IP port that the agent and worker processes use to contact the
      # console. Defaults to 6372.
      ; grinder.consolePort
      
      
      
      #
      # Less frequently used properties
      #
      
      
      ### Logging ###
      
      # The directory in which worker process logs should be created. If not
      # specified, the agent's working directory is used.
      grinder.logDirectory = log
      
      # The number of archived logs from previous runs that should be kept.
      # The default is 1.
      grinder.numberOfOldLogs = 2
      
      # Overrides the "host" string used in log filenames and logs. The
      # default is the host name of the machine running the agent.
      ; grinder.hostID = myagent
      
      # Set to false to disable the logging of output and error steams for
      # worker processes. You might want to use this to reduce the overhead
      # of running a client thread. The default is true.
      ; grinder.logProcessStreams = false
      
      
      ### Script sleep time ####
      
      # The maximum time in milliseconds that each thread waits before
      # starting. Unlike the sleep times specified in scripts, this is
      # varied according to a flat random distribution. The actual sleep
      # time will be a random value between 0 and the specified value.
      # Affected by grinder.sleepTimeFactor, but not
      # grinder.sleepTimeVariation. The default is 0ms.
      ; grinder.initialSleepTime=500
      
      # Apply a factor to all the sleep times you've specified, either
      # through a property of in a script. Setting this to 0.1 would run the
      # script ten times as fast. The default is 1.
      ; grinder.sleepTimeFactor=0.01
      
      # The Grinder varies the sleep times specified in scripts according to
      # a Normal distribution. This property specifies a fractional range
      # within which nearly all (99.75%) of the times will lie. E.g., if the
      # sleep time is specified as 1000 and the sleepTimeVariation is set to
      # 0.1, then 99.75% of the actual sleep times will be between 900 and
      # 1100 milliseconds. The default is 0.2.
      ; grinder.sleepTimeVariation=0.005
      
      
      ### Worker process control ###
      
      # If set, the agent will ramp up the number of worker processes,
      # starting the number specified every
      # grinder.processesIncrementInterval milliseconds. The upper limit is
      # set by grinder.processes. The default is to start all worker
      # processes together.
      ; grinder.processIncrement = 1
      
      # Used in conjunction with grinder.processIncrement, this property
      # sets the interval in milliseconds at which the agent starts new
      # worker processes. The value is in milliseconds. The default is 60000
      # ms.
      ; grinder.processIncrementInterval = 10000
      
      # Used in conjunction with grinder.processIncrement, this property
      # sets the initial number of worker processes to start. The default is
      # the value of grinder.processIncrement.
      ; process.initialProcesses = 1
      
      # The maximum length of time in milliseconds that each worker process
      # should run for. grinder.duration can be specified in conjunction
      # with grinder.runs, in which case the worker processes will terminate
      # if either the duration time or the number of runs is exceeded. The
      # default is to run forever.
      ; grinder.duration = 60000
      
      # If set to true, the agent process spawns engines in threads rather
      # than processes, using special class loaders to isolate the engines.
      # This allows the engine to be easily run in a debugger. This is
      # primarily a tool for debugging The Grinder engine, but it might also
      # be useful to advanced users. The default is false.
      ; grinder.debug.singleprocess = true
      
      
      ### Java ###
      
      # Use an alternate JVM for worker processes. The default is "java" so
      # you do not need to specify this if java is in your PATH.
      ; grinder.jvm = /opt/jrockit/jrockit-R27.5.0-jdk1.5.0_14/bin/java
      
      # Use to adjust the classpath used for the worker process JVMs.
      # Anything specified here will be prepended to the classpath used to
      # start the Grinder processes.
      ; grinder.jvm.classpath = /tmp/myjar.jar
      
      # Additional arguments to worker process JVMs.
      ; grinder.jvm.arguments = -Dpython.cachedir=/tmp
      
      
      ### Console communications ###
      
      # (See above for console address properties).
      
      # If you are not using the console, and don't want the agent to try to
      # contact it, set grinder.useConsole = false. The default is true.
      ; grinder.useConsole = false
      
      # The period at which each process sends updates to the console. This
      # also controls the frequency at which the data files are flushed.
      # The default is 500 ms.
      ; grinder.reportToConsole.interval = 100
      
      
      ### Statistics ###
      
      # Set to false to disable reporting of timing information to the
      # console; other statistics are still reported. See
      # http://grinder.sourceforge.net/faq.html#timing for why you might
      # want to do this. The default is true.
      ; grinder.reportTimesToConsole = false
      
      # If set to true, System.nanoTime() is used for measuring time instead
      # of System.currentTimeMills(). The Grinder will still report times in
      # milliseconds. The precision of these methods depends on the JVM
      # implementation and the operating system. Setting to true requires
      # J2SE 5 or later. The default is false.
      ; grinder.useNanoTime = true
      
  5. Following the bit at the very end of getting started guide setup the three .sh scripts (setGrinderEnv.sh, startAgent.sh, startConsole.sh) as outlined and place them in the demo directory. I changed them to run to use bash instead of ksh as have bash installed not ksh.
    • setGrinderEnv.sh:
      #!/bin/bash
      GRINDERPATH=/home/test/Desktop/grinder-3.2/
      GRINDERPROPERTIES=/home/test/Desktop/grinder-3.2/demo/grinder.properties
      CLASSPATH=$GRINDERPATH/lib/grinder.jar:$CLASSPATH
      #How I found it
      #ls -l `which java`
      #The above gave me: /usr/bin/java -> /etc/alternatives/java
      #ls -l /etc/alternatives/java
      #Which gave me: /etc/alternatives/java -> /usr/lib/jvm/java-6-sun/jre/bin/java
      JAVA_HOME=/usr/lib/jvm/java-6-sun/jre/
      PATH=$JAVA_HOME/bin:$PATH
      export CLASSPATH PATH GRINDERPROPERTIES
      
    • startConsole.sh:
      #!/bin/bash
      . ./setGrinderEnv.sh
      java -cp $CLASSPATH net.grinder.Console
      
    • startAgent.sh:
      #!/bin/bash
      . ./setGrinderEnv.sh
      java -cp $CLASSPATH net.grinder.Grinder $GRINDERPROPERTIES
      
  6. Make the four .sh scripts executable chmod +x *.sh
  7. Run ./startConsole.sh
  8. Run ./startAgent.sh
  9. In the Grinder Console under the Processes tab it shows that the agent has connected to the console

  10. Create a new file called simple.py and place the following in it:
    • simple.py:
      from net.grinder.script.Grinder import grinder
      from net.grinder.script import Test
      from net.grinder.plugin.http import HTTPRequest
      
      test1 = Test(1, "Request resource")
      request1 = test1.wrap(HTTPRequest())
      
      class TestRunner:
          def __call__(self):
              result = request1.GET("http://localhost:80/")
      
  11. In the script tab select simple.py and click the send changed file to worker processes button
  12. I found that the console could not upload the files to the agent because one of the threads died with a "Java.lang.OutOfMemoryError : Java heap space" error. To get around this in startConsole.sh I added -Xmx1024m to the java command like the below
    • startConsole.sh:
      #!/bin/bash
      . ./setGrinderEnv.sh
      java -Xmx1024m -cp $CLASSPATH net.grinder.Console
      
  13. So turn the agent and console off. And then start the console and the agent
  14. Click the start processes button
     
  15. Yay you are now testing against your web server
      
  16. When you are done click the stop button and you can have a look at the results
     
  17. So there you go the Missing Grinder Getting Started Guide.

Where to from here? Well you can have multiple agents, talk more than just HTTP (look in the examples directory for JDBC, JMS, SMTP, etc) and record TCP streams via a proxy.

Something I found out later. The reason I was getting the heap of space was that the log directory was one of the directories which was trying to be synced up the agents and this was too large so it crashed console. I will have to figure out how to make it save the logs somewhere else.

5 comments:

Kevin said...

Cheers mate. Just what I needed before tackling it in earnest. Works great on a Mac too.

Salman Jamali said...

Thanks man, this was helpful.

Borislav Andruschuk said...

The Grinder is the best tool for performance testing. I've working on IDE for Grinder scripts - GrinderStone - which allows debug scripts using Eclipse and provides some interesting features for development like modularity and pretty useful logging in debug mode. That project you can download from official project site:

http://code.google.com/p/grinderstone

We also have Eclipse Update site for simple plugin installation into Eclipse platform. All details you can obtain on our site and support group. Look thru GrinderStone and you'll like it, it gives you more power to develop Grinder scripts.

Borislav Andruschuk said...

The Grinder is the best tool for performance testing. I've working on IDE for Grinder scripts - GrinderStone - which allows debug scripts using Eclipse and provides some interesting features for development like modularity and pretty useful logging in debug mode. That project you can download from official project site:

http://code.google.com/p/grinderstone

We also have Eclipse Update site for simple plugin installation into Eclipse platform. All details you can obtain on our site and support group. Look thru GrinderStone aand you'll like it, it gives you more power to develop Grinder scripts.

Anonymous said...

Great post! I'll take a look to the plugin. tks