Skip to main content

Performance Testing in the Cloud with JMeter & AWS

JMeter is a wonderful tool to stress test your website and your application architecture, however if you are trying to simulate many users (>1000) one JMeter instance (=pc) will not be sufficient. You will have to set up a JMeter cluster with multiple machines. JMeter is capable or running distributed tests, but it comes with limitations.
Since most of us don’t have multiple servers laying around somewhere, we usually go to cloud service providers like AWS, spin up a couple of EC2 instances and turn them off whenever we’re done. Here is the problem, JMeter uses Java RMI (Remote Method Invocation) to communicate to its slaves, but these connections require all machines to be on the same subnet and this is not feasible with EC2 instances.
Below, I explain how to get around this problem using a 3 node configuration in AWS to execute tests. I assume that you have a written the test already and have the .jmx file ready to go. 

The basic configuration:

The idea is that we have 1 master instance that sends the test to 2 slaves. These slaves execute the test and send the results back to the master who will collect and combine the results.
A few notes:
  • Your test will be executed on both slave machines and not divided across them -- this means that if you want to run 300 threads in your test, your target will be hit with 600 threads.
  • The master does NOT execute any tests. Gathering the results and orchestrating the tests is enough to handle for one machine.
  • The test will be sent out to the slaves from the master.  There's no need to copy the test file to all slaves.

Before we start:

It is important to understand how JMeter communicates. JMeter creates 3 connections between two machines, illustrated below.



Two connections are used for RMI (A: executing methods, B: receiving results) and the third one is for JMeter.
To get around the RMI limitations, we are going to set up a few SSH Tunnels (SSH Port Forwarding).  This will make our slaves available to our master. We are going to use the port range between 24000 and 26999 (we don’t really need that many ports, but it helps to spread the range to know which ports are going to the slave’s vs. the ports that are coming in to the master).

Preparing EC2 instances:

We are using 3 EC2 instances (medium instances usually do the job) with Ubuntu server (but any other Linux distribution will work), Java has been installed on all instances. In addition to that, I downloaded and extracted JMeter on all of my machines.
SSH keep alive
Since we are using SSH tunnels to the slaves (and we don’t know how long our coffee breaks are going to take) it is a good idea to keep the SSH alive (stale connections are the worst nightmare when it comes to debugging a JMeter clusters)
You can do that in on the master in /etc/ssh/ssh_config by adding (or changing) the lines
ServerAliveInterval 60
ServerAliveCountMax 3
This will send null packages to the respective slaves and keep the connections open.
Optional
You could give those instances static IP addresses, and configure the hosts file to use domain names. Here is a possible hosts file (/etc/hosts) on master
123.123.123.123 slave01
123.123.123.124 slave02
By doing this, you can create a central script to create the tunnels every time you start the machines.

SSH Port Forwarding

The idea is that we use the localhost address and forward certain ports. JMeter will be configured to use the localhost IP (+ port) to connect to the server.

Based on the graph above, we need to create three tunnels (2 outgoing, 1 incoming) for each slave. Luckily, we can set that up with two commands:
For slave 1:
ssh -L 24001:127.0.0.1:24001 \
-R 25000:127.0.0.1:25000 \
-L 26001:127.0.0.1:26001 -N -f USERNAME@SLAVE01
For slave 2:
ssh -L 24002:127.0.0.1:24002 \
-R 25000:127.0.0.1:25000 \
-L 26002:127.0.0.1:26002 -N -f USERNAME@SLAVE02
Wait, why is the return port the same (25000)?
The master is listening to one port only to receive results. That means that all of the slaves will be sending their results back through this port.
Jmter configurations
Next we go onto the Jmeter configurations. The slaves need to know that they should know that they are slaves and that the need to listen to local ports. We update the following lines in bin/jmeter.properties
Slave 1:
server_port=24001
server.rmi.localhostname=127.0.0.1
server.rmi.localport=26001
Slave 2:
server_port=24002
server.rmi.localhostname=127.0.0.1
server.rmi.localport=26002
The master needs to know where to send the tests to and how to receive results. This is done by changing these lines in /bin/jmeter.properties
remote_hosts=127.0.0.1:24001, 127.0.0.1:24002
client.rmi.localport=25000
mode=Statistical
The mode is important as we want to make sure not to stress test our master with returning connections from the slaves. "Statistical" will send a summary of our stresstest back to the master.
Finally, we can start our slaves and tell JMeter to use 127.0.0.1 as our RMI server in the background
nohup ./jmeter-server -Djava.rmi.server.hostname=127.0.0.1 > /dev/null 2>&1 &
This will execute JMeter in the background, so that you can close those terminals to these machines once they are running.
To start the test on our master just execute:
./jmeter -n -t jmetertesplan.jmx –r -l jmeteroutput.csv
The paramter –r tells jmeter to use all defined remote hosts. You can also connect to one server using
./jmeter -n -t jmetertesplan.jmx –R 127.0.0.1:24001 -l jmeteroutput.csv
To stop the test just execute
./stoptest.sh

Troubleshooting

This section could fill another blog post, however I do want to share some of my immediate trouble shooting tips.
1. Server reports: Can't connect to server/Connection Timoute
It seems that sometimes JVM doesn't know about localhost and setting the localhost variable might be neccessary. Executing
export JVM_ARGS="-Djava.rmi.server.hostname=localhost"
before running the test helped me. (If someone knows exactly what the problem is, please enlighten me!)

2. OutOfMemoryError: unable to create new native thread (too many threads, each thread has a large stack)Decrease the XSS value in your jmeter file on your master machine

3. StackOverflowError (the stack size is greater than the limit)Increase XSS value in your jmeter file on your master machine

4. GC Overhead limit exceeds...Increase/decrease heap size (-Xms2048m -Xmx2048m) in 512mb increments in your jmeter file
When changing the heapsize adjust the following line as well:
NEW="-XX:NewSize=683m -XX:MaxNewSize=683m"
(NewSize = round(Xms / 3) ) 

Sources


What is your experience with JMeter and distributed testing? Anything to add to this? Please let me know in the comments section.

Comments

Popular posts from this blog

JMeter Exceeded Maximum Number of Redirects Error Solution

While running performance test, JMeter allows maximum 5 redirects by default. However, if your system demands more than 5 redirects, it may result in JMeter exceeded maximum number of redirects error. In this post, we have listed down steps to overcome this error. Actual error in JMeter: Response code: “Non HTTP response code: java.io.IOException” Response message: “Non HTTP response message: Exceeded maximum number of redirects: 5” This error is noticed because  JMeter  allows maximum 5 redirects by default and your system may be using more than 5 redirects. You need to increase this count to more than 5 in jmeter.properties file. Follow below steps to achieve this. Navigate to /bin directory of your JMeter installation. Locate jmeter.properties file and open it in any editor. Search for “httpsampler.max_redirects” property in opened file. Uncomment the above property by removing # before it. Change to value to more than 5 Eg. 20. Save the file and restart JMeter. If

SSO with SAML login scenario in JMeter

SAML(Security Assertion Markup Language) is increasingly being used to perform single sign-on(SSO) operations. As WikiPedia puts it, SAML is an XML-based open standard data format for exchanging authentication and authorization data between parties, in particular, between an identity provider and a service provider. With the rise in use of SAML in web applications, we may need to handle this in JMeter. This step-by-step tutorial shows SAML JMeter scenario to perform login operation. First request from JMeter is a GET request to fetch Login page. We need to fetch two values ‘SAMLRequest’ and ‘RelayState’ from the Login page response data. We can do this by using  Regular Expression Extractor . These two values need to be sent in POST request to service provider. Refer below image to see how to do this. We will get an HTML login page as a response to the request sent in 1st step. We need to fetch values of some hidden elements to pass it in the next request. We can do this b

A Tutorial to Send Email using JMeter

Sending email is a mundane activity in any professional’s life. It’s a common medium for communication nowadays. Therefore performance testing of email server is not only important but necessary for an organization. JMeter can be helpful to perform load testing in such scenarios. In this tutorial, we will see how JMeter can be used to send email. We will use SMTP Sampler of JMeter to send an email. JavaMail API is needed to enable email functionality in JMeter. Download it from  here  and paste the jar in JMeter’s lib folder. Now, perform below steps to configure SMTP Sampler. Add a new Thread Group under Test Plan. Right click on Thread Group and select Add–>Sampler–>SMTP Sampler. We need to populate SMTP server’s details in this sampler. We will use GMail for sending an email. For this, enter these values in SMTP Sampler fields. Server: smtp.googlemail.com, Port: 587. Provide values in Email Address From and To fields of Mail Settings section to specify sender and reci