Performance testing has proved itself to be crucial for the success of a business. Not only does a poor performing site face financial losses, it can also lead to legal repercussions at times.
No one wants to put up with a slow performing, unreliable site in important online interactions such as purchasing, online test taking, bill payment, etc. With the Internet being so widely available, the range of alternatives is immense. It is easier to lose clientele than gain them and performance is a key game changer.
If we can understand what is the need for a load testing tool, it will give us the reason and motivation to use it. Some famous business sites have suffered serious downtimes when they get large number of visitors. E-commerce websites invest heavily in advertising campaigns, but not in Load Testing. Therefore, they fail to ensure optimal system performance, when that marketing brings in traffic.
Another familiar example of ignoring load testing is that of “error establishing connection” in WordPress websites. Therefore, it is a good idea to load test a website or application before its deployment in production. It is nice to quickly establish a best-case scenario for a project before running more detailed tests down the road.
Apache Bench (ab) is a tool from the Apache organization for benchmarking a Hypertext Transfer Protocol (HTTP) web server. Although it is designed to measure the performance of Apache web server, yet it can also be used to test any other web server that is equally good. With this tool, you can quickly know how many requests per second your web server is capable of serving.
Let us see the important features and limitations of Apache Bench. The features and limitations are listed below −
Being an open source software, it is freely available.
It is a simple command line computer program.
It is a platform-independent tool. It means that it can be invoked on Linux/Unix or on Windows server equally well.
It can conduct load and performance test for only web server - HTTP or HTTPS.
It is not extensible.
Apache Bench uses only one operating system thread irrespective of the concurrency level (specified by the -c flag). Therefore, when benchmarking high-capacity servers, a single instance of Apache Bench can itself be a bottleneck. To completely saturate the target URL, it is better to use additional instances of Apache Bench in parallel, if your server has multiple processor cores.
You need to be aware that there is no directive in the Apache Bench to increase concurrency in particular intervals while running tests. Therefore, running load tests using ab is equivalent to a denial-of-service (DOS) attack. It is recommended that you inform and take prior permission from your VPS service provider if you are going to do heavy load testing for a long period of time. They will allot you an appropriate time interval or shift your node for the load testing task.
Second, if you are testing a third person’s website continuously and for a long time just for learning Apache Bench from your VPS (which becomes the testing node), there is a remote possibility that your VPS public IP can be blocked by the third person’s website permanently. In that case, you will not be able to connect to that website with the same IP. But if you really want to connect to the website in future, the only solution will be to talk to the system administrator of the target website, or create a new instance of the server with a different IP with the help of your VPS service provider.
Having warned you, let me assure you that all tests in this tutorial are safe enough and out of what system administrators generally call "system abuse" practices.
In this chapter, we will guide you how to set up your environment for Apache Bench on your VPS.
Memory − 128 MB
Disk Space − No minimum requirement
Operating System − No minimum requirement
Apache Bench is a stand-alone application, and has no dependencies on the Apache web server installation. The following is a two-step process to install Apache Bench.
Step 1 − Update package database.
# apt-get update
Please note that symbol # before a terminal command means that root user is issuing that command.
Step 2 − Install apache2 utils package to get access to Apache Bench.
# apt-get install apache2-utils
Apache Bench is now installed. If you want to test a web application hosted on the same VPS, then it is enough to install the Apache web server only −
# apt-get install apache2
Being an Apache utility, Apache Bench is automatically installed on installation of the Apache web server.
Let us now see how to verify Apache Bench Installation. The following code will help verify the installation −
# ab -V
Output
This is ApacheBench, Version 2.3 <$Revision: 1604373 $> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Licensed to The Apache Software Foundation, http://www.apache.org/
When you see the above terminal output, it means you have successfully installed Apache Bench.
From the safety point of view, it is considered a good practice for system administrator to create a sudo user instead of working as root. We will create a test user, named test, for the purpose −
# useradd -m -d /home/test -g sudo test
Let us set the password for the new user −
# passwd test
System will prompt for a new password for the user test. You can enter a simple password as we are just testing, and not deploying to the production server. Usually the sudo command will prompt you to provide the sudo user password; it is recommended not to use complicated password as the process becomes cumbersome.
Output
Enter new UNIX password: Retype new UNIX password: passwd: password updated successfully
In this section, we will test the Apache.org Website. Let us first switch to the sudo user test −
# su test
To begin with, we will test the website of Apache organization, https://www.apache.org/. We will first run the command, and then understand the output −
$ ab -n 100 -c 10 https://www.apache.org/
Here -n is the number of requests to perform for the benchmarking session. The default is to just perform a single request which usually leads to non-representative benchmarking results.
And -c is the concurrency and denotes the number of multiple requests to perform at a time. Default is one request at a time.
So in this test, Apache Bench will make 100 requests with concurrency 10 to the Apache organization server.
Output
This is ApacheBench, Version 2.3 <$Revision: 1604373 $> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Licensed to The Apache Software Foundation, http://www.apache.org/ Benchmarking www.apache.org (be patient).....done Server Software: Apache/2.4.7 Server Hostname: www.apache.org Server Port: 443 SSL/TLS Protocol: TLSv1.2,ECDHE-RSA-AES256-GCM-SHA384,2048,256 Document Path: / Document Length: 58769 bytes Concurrency Level: 10 Time taken for tests: 1.004 seconds Complete requests: 100 Failed requests: 0 Total transferred: 5911100 bytes HTML transferred: 5876900 bytes Requests per second: 99.56 [#/sec] (mean) Time per request: 100.444 [ms] (mean) Time per request: 10.044 [ms] (mean, across all concurrent requests) Transfer rate: 5747.06 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 39 46 30.9 41 263 Processing: 37 40 21.7 38 255 Waiting: 12 15 21.7 13 230 Total: 77 86 37.5 79 301 Percentage of the requests served within a certain time (ms) 50% 79 66% 79 75% 80 80% 80 90% 82 95% 84 98% 296 99% 301 100% 301 (longest request)
Having run our first test, it will be easy to recognize the pattern of use for this command which is as follows −
# ab [options .....] URL
where,
ab − Apache Bench command
options − flags for particular task we want to perform
URL − path url we want to test
We need to understand the different metrics to understand the various output values returned by ab. Here goes the list −
Server Software − It is the name of the web server returned in the HTTP header of the first successful return.
Server Hostname − It is the DNS or IP address given on the command line.
Server Port − It is the port to which ab is connecting. If no port is given on the command line, this will default to 80 for http and 443 for https.
SSL/TLS Protocol − This is the protocol parameter negotiated between the client and server. This will only be printed if SSL is used.
Document Path − This is the request URI parsed from the command line string.
Document Length − It is the size in bytes of the first successfully returned document. If the document length changes during testing, the response is considered an error.
Concurrency Level − This is the number of concurrent clients (equivalent to web browsers) used during the test.
Time Taken for Tests − This is the time taken from the moment the first socket connection is created to the moment the last response is received.
Complete Requests − The number of successful responses received.
Failed Requests − The number of requests that were considered a failure. If the number is greater than zero, another line will be printed showing the number of requests that failed due to connecting, reading, incorrect content length, or exceptions.
Total Transferred − The total number of bytes received from the server. This number is essentially the number of bytes sent over the wire.
HTML Transferred − The total number of document bytes received from the server. This number excludes bytes received in HTTP headers
Requests per second − This is the number of requests per second. This value is the result of dividing the number of requests by the total time taken.
Time per request − The average time spent per request. The first value is calculated with the formula concurrency * timetaken * 1000 / done while the second value is calculated with the formula timetaken * 1000 / done
Transfer rate − The rate of transfer as calculated by the formula totalread / 1024 / timetaken.
Having learned about the headings of the output values from the ab command, let us try to analyze and understand the output values for our initial test −
Apache organisation is using their own web Server Software − Apache (version 2.4.7)
Server is listening on Port 443 because of https. Had it been http, it would have been 80 (default).
Total data transferred is 58769 bytes for 100 requests.
Test completed in 1.004 seconds. There are no failed requests.
Requests per seconds − 99.56. This is considered a pretty good number.
Time per request − 100.444 ms (for 10 concurrent requests). So across all requests, it is 100.444 ms/10 = 10.044 ms.
Transfer rate − 1338.39 [Kbytes/sec] received.
In connection time statistics, you can observe that many requests had to wait for few seconds. This may be due to apache web server putting requests in wait queue.
In our first test, we had tested an application (i.e., www.apache.org) hosted on a different server. In the later part of the tutorial, we will be testing our sample web-applications hosted on the same server from which we will be running the ab tests. This is for the ease of learning and demonstration purpose. Ideally, the host node and testing node should be different for accurate measurement.
To better learn ab, you should compare and observe how the output values vary for different cases as we move forward in this tutorial.
Here we will plot the relevant outcome to see how much time the server takes as the number of requests increases. For that, we will add the -g option in the previous command followed by the file name (here out.data) in which the ab output data will be saved −
$ ab -n 100 -c 10 -g out.data https://www.apache.org/
Let us now see the out.data before we create a plot −
$ less out.data
Output
starttime seconds ctime dtime ttime wait Tue May 30 12:11:37 2017 1496160697 40 38 77 13 Tue May 30 12:11:37 2017 1496160697 42 38 79 13 Tue May 30 12:11:37 2017 1496160697 41 38 80 13 ...
Let us now understand the column headers in the out.data file −
starttime − This is the date and time at which the call started.
seconds − Same as starttime but in the Unix timestamp format (date -d @1496160697 returns starttime output).
ctime − This is the Connection Time.
dtime − This is the Processing Time.
ttime − This is the Total Time (it is the sum of ctime and dtime, mathematically ttime = ctime + dtime).
wait − This is the Waiting Time.
For a pictorial visualization of how these multiple items are related to each other, take a look at the following image −
If we are working over terminal or where graphics are not available, gnuplot is a great option. We will quickly understand it by going through the following steps.
Let us install and launch gnuplot −
$ sudo apt-get install gnuplot $ gnuplot
Output
G N U P L O T Version 4.6 patchlevel 6 last modified September 2014 Build System: Linux x86_64 Copyright (C) 1986-1993, 1998, 2004, 2007-2014 Thomas Williams, Colin Kelley and many others gnuplot home: http://www.gnuplot.info faq, bugs, etc: type "help FAQ" immediate help: type "help" (plot window: hit 'h') Terminal type set to 'qt' gnuplot>
As we are working over terminal and supposing that graphics are not available, we can choose the dumb terminal which will give output in ASCII over the terminal itself. This helps us get an idea what our plot looks like with this quick tool. Let us now prepare the terminal for ASCII plot.
gnuplot> set terminal dumb
Output
Terminal type set to 'dumb' Options are 'feed size 79, 24'
As, our gnuplot terminal is now ready for ASCII plot, let us plot the data from the out.data file −
gnuplot> plot "out.data" using 9 w l
Output
1400 ++-----+------+-----+------+------+------+------+-----+------+-----++ + + + + + + +"out.data" using 9 ****** + | | 1200 ++ ******************************************** | ******************* | 1000 ++ * ++ | * | | * | 800 ++ * ++ | * | | * | 600 ++ * ++ | * | | * | 400 ++ * ++ | * | 200 ++ * ++ | * | +**** + + + + + + + + + + 0 ++-----+------+-----+------+------+------+------+-----+------+-----++ 0 10 20 30 40 50 60 70 80 90 100
We have plotted the ttime, total time (in ms) from column 9, with respect to the number of requests. We can notice that for the initial ten requests, the total time was in the nearly 100 ms, for next 30 requests (from 10th to 40th), it increased to 1100 ms, and so on. Your plot must be different depending on your out.data.
In the previous chapter, we understood the basic use of the Apache Bench to test a third party website. In this section, we will use this tool to test a web application on our own server. To keep the tutorial self-contained to the extent possible, we have chosen to install a python application for the demonstration purpose; you can choose any other language like PHP or Ruby depending on your expertise level.
Generally, Python is installed by default on Linux servers.
Bottle is a micro-framework written in python for creating web applications, and pip is a python package manager. Type the following command in terminal to install Bottle −
$ sudo apt-get install python-pip $ sudo pip install bottle
Let us now create a small Bottle application. For that, create a directory and move inside it −
$ mkdir webapp $ cd webapp
We will create a new python script, app.py, inside the webapp directory −
$ vim app.py
Now, write the following code in the app.py file −
from bottle import Bottle, run app = Bottle() @app.route('/') @app.route('/hello') def hello(): return "Hello World!" run(app, host = 'localhost', port = 8080)
When you have added the above lines, save and close the file. Having saved the file, we can run the python script to launch the application −
$ python app.py
Output
Bottle v0.12.7 server starting up (using WSGIRefServer())... Listening on http://localhost:8080/ Hit Ctrl-C to quit.
This output shows that our application is running on the local machine at the host http://localhost and listening on the port 8080.
Let us check if our app is responding properly to the HTTP requests. As this terminal cannot take any input without quitting serving the Bottle application, we need to login to our VPS with another terminal. After logging into the VPS with another terminal, you can navigate to your application by typing the following code in the new terminal.
$ lynx http://localhost:8080/
Lynx is a command line browser and is usually installed by default in various Linux distributions like Debian and Ubuntu. If you see the following output, it means your app is working fine.
Output
If you see the above output, that means our application is live and ready for testing.
Please note that there is a bug in ab, and it is not able to test the application on the localhost. So we will change the host from localhost to 127.0.0.1 in the app.py file. So the file will change to the following −
from bottle import Bottle, run app = Bottle() @app.route('/') @app.route('/hello') def hello(): return "Hello World!" run(app, host = '127.0.0.1', port = 8080)
Let us now test our app by typing the following command on the same terminal on which ran the lynx command −
$ ab -n 100 -c 10 http://127.0.0.1:8080/hello
Output
This is ApacheBench, Version 2.3 <$Revision: 1604373 $> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Licensed to The Apache Software Foundation, http://www.apache.org/ Benchmarking 127.0.0.1 (be patient).....done Server Software: WSGIServer/0.1 Server Hostname: 127.0.0.1 Server Port: 8080 Document Path: /hello Document Length: 12 bytes Concurrency Level: 10 Time taken for tests: 0.203 seconds Complete requests: 100 Failed requests: 0 Total transferred: 16500 bytes HTML transferred: 1200 bytes Requests per second: 493.78 [#/sec] (mean) Time per request: 20.252 [ms] (mean) Time per request: 2.025 [ms] (mean, across all concurrent requests) Transfer rate: 79.56 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 0 0.1 0 0 Processing: 1 6 28.2 2 202 Waiting: 1 6 28.2 2 202 Total: 1 6 28.2 2 202 Percentage of the requests served within a certain time (ms) 50% 2 66% 2 75% 2 80% 2 90% 2 95% 2 98% 202 99% 202 100% 202 (longest request)
While the output on the first terminal will be (100 times) as follows −
... 127.0.0.1 - - [10/Jun/2017 04:30:26] "GET /hello HTTP/1.0" 200 12 127.0.0.1 - - [10/Jun/2017 04:30:26] "GET /hello HTTP/1.0" 200 12 127.0.0.1 - - [10/Jun/2017 04:30:26] "GET /hello HTTP/1.0" 200 12 ...
You can observe how the various values of the ab outcome have changed as compared to the initial test.
In the previous tests of ab, we have used the default web server bundled in the Bottle framework.
Now we will change the single-threaded default web server with a multi-threaded one. Therefore, let us install a multi-threaded web server library like cherrypy or gunicorn and tell Bottle to use it. We have chosen gunicorn for the demonstration purpose here (you can choose some other one too) −
$ sudo apt-get install gunicorn
And modify the file, that is change from the default web server to gunicorn −
... run(server = 'gunicorn'...) ...
Let us test the app in the second terminal.
$ ab -n 100 -c 10 http://127.0.0.1:8080/hello
Output
This is ApacheBench, Version 2.3 <$Revision: 1604373 $> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Licensed to The Apache Software Foundation, http://www.apache.org/ Benchmarking 127.0.0.1 (be patient).....done Server Software: gunicorn/19.0.0 Server Hostname: 127.0.0.1 Server Port: 8080 Document Path: /hello Document Length: 12 bytes Concurrency Level: 10 Time taken for tests: 0.031 seconds Complete requests: 100 Failed requests: 0 Total transferred: 17200 bytes HTML transferred: 1200 bytes Requests per second: 3252.77 [#/sec] (mean) Time per request: 3.074 [ms] (mean) Time per request: 0.307 [ms] (mean, across all concurrent requests) Transfer rate: 546.36 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 1 0.9 0 4 Processing: 1 2 0.7 3 4 Waiting: 0 2 0.8 2 3 Total: 2 3 0.6 3 5 WARNING: The median and mean for the initial connection time are not within a normal deviation These results are probably not that reliable. WARNING: The median and mean for the processing time are not within a normal deviation These results are probably not that reliable. Percentage of the requests served within a certain time (ms) 50% 3 66% 3 75% 3 80% 3 90% 4 95% 5 98% 5 99% 5 100% 5 (longest request)
Observe how the Requests per second increased from 493 to 3252. It means gunicorn is suitable as a production server for python apps.
In this chapter, we will learn how to test multiple URLs concurrently. For that, we will need to edit our application file, app.py to include two URLs −
from bottle import Bottle, run app = Bottle() @app.route('/') @app.route('/hello1') def hello(): return "Hello World! It is first URL." @app.route('/hello2') def hello(): return "Hello World! It is second URL." run(app,server = 'gunicorn',host = '127.0.0.1', port = 8080)
You can do this by creating a shell script, with multiple ab calls. Create a file test.sh and add the following lines to it −
ab -n 100 -c 10 http://127.0.0.1:8080/hello1 ab -n 100 -c 10 http://127.0.0.1:8080/hello2
When you have added the above lines, Save and Close the file. Make the file executable −
chmod u+x test.sh
Let us now run the script −
./test.sh
To avoid repetition and purpose of clarity, we will show only the relevant of the ab output, indicating by dots what portion has been omitted, as in the following.
. . . Document Path: /hello1 Document Length: 732 bytes Concurrency Level: 10 Time taken for tests: 0.040 seconds Complete requests: 100 Failed requests: 0 Non-2xx responses: 100 Total transferred: 90000 bytes HTML transferred: 73200 bytes Requests per second: 2496.13 [#/sec] (mean) Time per request: 4.006 [ms] (mean) Time per request: 0.401 [ms] (mean, across all concurrent requests) Transfer rate: 2193.87 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 0 0.8 0 3 Processing: 1 3 1.0 4 5 Waiting: 0 3 1.2 4 4 Total: 1 4 0.6 4 5 WARNING: The median and mean for the processing time are not within a normal deviation These results are probably not that reliable. . . .
You can save the Apache Bench Output to file by creating a shell script, with multiple ab calls. At the end of each line, place an &; this makes the command run in the background, and lets the next command start its execution. You will also want to redirect the output to a file for each url using <filename>. For example, our file test.sh will look like the following after modification −
$ ab -n 100 -c 10 http://127.0.0.1:8080/hello1 > test1.txt & $ ab -n 100 -c 10 http://127.0.0.1:8080/hello2 > test2.txt &
Here, test1.txt and test2.txt are the files to save the output data.
You can check that the above script has created two files, test1.txt and test2.txt which contains the ab output for the respective URLs −
$ ls -l
... -rw-r--r-- 1 root root 5225 May 30 12:11 out.data -rwxr--r-- 1 root root 118 Jun 10 12:24 test.sh -rw-r--r-- 1 root root 1291 Jun 10 12:31 test1.txt -rwxr--r-- 1 root root 91 Jun 10 13:22 test2.sh -rw-r--r-- 1 root root 1291 Jun 10 12:31 test2.txt ...
While using ab, you should be alert to the failed test without warning. For example, if you check a wrong URL, you may get something similar to the following (we have deliberately changed the port here).
$ ab -l -r -n 100 -c 10 -k -H "Accept-Encoding: gzip, deflate" http://127.0.0.1:805/
This is ApacheBench, Version 2.3 <$Revision: 1604373 $> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Licensed to The Apache Software Foundation, http://www.apache.org/ Benchmarking 127.0.0.1 (be patient).....done Server Software: Server Hostname: 127.0.0.1 Server Port: 805 Document Path: / Document Length: Variable Concurrency Level: 10 Time taken for tests: 0.002 seconds Complete requests: 100 Failed requests: 150 (Connect: 0, Receive: 100, Length: 0, Exceptions: 50) Keep-Alive requests: 0 Total transferred: 0 bytes HTML transferred: 0 bytes Requests per second: 44984.26 [#/sec] (mean) Time per request: 0.222 [ms] (mean) Time per request: 0.022 [ms] (mean, across all concurrent requests) Transfer rate: 0.00 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 0 0.0 0 0 Processing: 0 0 0.2 0 0 Waiting: 0 0 0.0 0 0 Total: 0 0 0.2 0 0 Percentage of the requests served within a certain time (ms) 50% 0 66% 0 75% 0 80% 0 90% 0 95% 0 98% 0 99% 0 100% 0 (longest request)
In this chapter, we will understand the preparation required for testing dynamic pages. A server-side dynamic web page is a web page the construction of which is controlled by an application server processing server-side scripts. The apache bench can only load test the server-side dynamic web page.
Concurrency level should be lower than the total number of requests.
$ ab -l -r -n 30 -c 80 -k -H "Accept-Encoding: gzip, deflate" http://127.0.0.1:8000/
Output
ab: Cannot use concurrency level greater than total number of requests Usage: ab [options] [http[s]://]hostname[:port]/path
In this section, we will describe the use of some important flags with the ab command. We will use the terms, options and flags, interchangeably.
The verbose option can be used to analyze and debug if there exist multiple number of failed requests. A common indication of failure of the load test is that the test finishes very fast and it gives a good number for request per second value. But it will be a wrong benchmark. To identify the success or failure, you can use the -v 2 option which will dump each response’s body and header to the terminal output. Following command depicts a use case −
$ ab -n 1 -v 2 http://www.generic-example-URL.com/
Output
LOG: header received: HTTP/1.0 200 OK … Content-Length: 2548687
Of course, if you are testing variable responses or returning non-200 HTTP codes in the event of any error, you should simply ignore length checking with the -l option. We will soon see non-200 HTTP when we will launch a web2py application in the subsequent chapters.
When the client sends HTTP request, the connection is made to the server, the server sends the response, and the connection is closed after it has sent the request. This cycle continues with each request. However, with the keep-alive setting (also known as persistent connections), the client maintains an underlying TCP connection open to facilitate multiple requests and response; this eliminates the slow and costly connection initialization time that would otherwise be present.
If the web page is of variable length, then you should make use of the option -l. Apache Bench does not report errors if the length of the responses is not constant. This can be useful for dynamic pages.
How to force ab not to exit on receiving errors? You should use the option -r. Without this option, your test may break as soon as any request hits the socket error. However, with this option, errors will be reported in the failed errors heading, but the test will continue till the end.
This option is used to add arbitrary header line. The argument is typically in the form of a valid header line, containing a colon-separated field-value pair (i.e., “Accept-Encoding: zip/zop;8bit”).
In the following section, we will learn in detail how to use the above options in combination with the option to use the cookie value, i.e., the -C option. The -C option is typically in the form of a name = value pair. This field can be repeated.
To understand how to use the cookie with Apache Bench, we need a web page that tries to set a cookie. A very good example is the web2py application which is a python web framework.
We are going to quickly install another python app web2py. You can read more on how to use it on Web2py Framework Overview.
Python is generally installed by default on the Ubuntu and Debian server. Therefore, one requirement is already met to run web2py successfully.
However, we need to install the unzip package to extract the source files of web2py from the zip file which we will be downloading −
$ sudo apt-get update $ sudo apt-get install unzip
Let us get the web2py framework from the project’s website. We will download this to our home folder −
$cd ~ $ wget http://www.web2py.com/examples/static/web2py_src.zip
Now, we can unzip the file we just downloaded and move inside −
$ unzip web2py_src.zip $ cd web2py
To run the web2py, you do not need to install it. Once you are inside the web2py directory, you can run it by typing the following command −
$python web2py.py
If everything is successful, you will see the following output where you will be asked to choose a password for the administrative UI −
web2py Web Framework Created by Massimo Di Pierro, Copyright 2007-2017 Version 2.14.6-stable+timestamp.2016.05.10.00.21.47 Database drivers available: sqlite3, imaplib, pymysql, pg8000 WARNING:web2py:GUI not available because Tk library is not installed choose a password: please visit: http://127.0.0.1:8000/ use "kill -SIGTERM 23904" to shutdown the web2py server
However, you need to be aware of the fact that the launched web interface is accessible on the local machine only.
From the output, you can understand that to stop the web server, you will have to type “CTRL-C” in the instant terminal. On the other hand, to stop the web2py server on the other terminal related to the same VPS, you can insert the command kill -SIGTERM <PID>, where <PID> is the process ID for the web2py server, which in this case is 23904.
If a page is only accessible by a logged in user, not directly accessible from the login page, in that case you can use the -C flag. This flag defines a cookie for the ab command. But you have to get the value of the session identifier cookie from a valid session. How to get that? Various online tutorials will guide you towards Chrome (or Mozilla) browser developer tools. But in our test case, as the application is available only on the command line, we will use the lynx browser to obtain the value.
Let us get the cookie value of a session first. Open another terminal and type the following command −
$ lynx http://127.0.0.1:8000/
In response to the above command, lynx will ask your permission to accept the cookie from the web2py server as shown in the image below.
Note down the cookie value before typing y to accept the cookie. Now the terminal will look similar to the following image – website on the terminal!
Having obtained the cookie value, we will now run the ab test. For that, we will have to open the third terminal (see the image below) −
Now, let us use the -C flag in the third terminal −
$ ab -n 100 -c 10 -C session_name = 127.0.0.1-643dad04-3c34 http://127.0.0.1:8000/
This is ApacheBench, Version 2.3 <$Revision: 1604373 $> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Licensed to The Apache Software Foundation, http://www.apache.org/ Benchmarking 127.0.0.1 (be patient).....done Server Software: Rocket Server Hostname: 127.0.0.1 Server Port: 8000 Document Path: / Document Length: 66 bytes Concurrency Level: 10 Time taken for tests: 0.051 seconds Complete requests: 100 Failed requests: 0 Non-2xx responses: 100 Total transferred: 27700 bytes HTML transferred: 6600 bytes Requests per second: 1968.12 [#/sec] (mean) Time per request: 5.081 [ms] (mean) Time per request: 0.508 [ms] (mean, across all concurrent requests) Transfer rate: 532.39 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 1 2 0.9 2 4 Processing: 0 3 0.9 3 5 Waiting: 0 2 1.1 2 4 Total: 4 5 0.7 5 7 Percentage of the requests served within a certain time (ms) 50% 5 66% 5 75% 5 80% 6 90% 6 95% 6 98% 7 99% 7 100% 7 (longest request)
From the output above, we note several points. First, web2py uses Rocket web server. We also note that we are getting ‘Non-2xx responses’ in addition to previously discussed headings in the output. In general, Http protocol responds to a request using a response code, and anything within the 200s range means ‘okay’, and the rest corresponds to some problem. For example, 400s are resource related errors such as 404 File Not Found. 500s correspond to server errors. In our instant case, there is no error anywhere except when we are using the -C option. It can be suppressed using the -l option as already described.
In this section, we will understand how to check the admin page. For the purpose of comparison, let us test another URL of the web2py application −
$ ab -n 100 -c 10 session_name = 127.0.0.1-643dad04-3c34 http://127.0.0.1:8000/admin
This is ApacheBench, Version 2.3 <$Revision: 1604373 $> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Licensed to The Apache Software Foundation, http://www.apache.org/ Benchmarking 127.0.0.1 (be patient).....done Server Software: Rocket Server Hostname: 127.0.0.1 Server Port: 8000 Document Path: /admin Document Length: 8840 bytes Concurrency Level: 10 Time taken for tests: 2.077 seconds Complete requests: 100 Failed requests: 0 Total transferred: 926700 bytes HTML transferred: 884000 bytes Requests per second: 48.14 [#/sec] (mean) Time per request: 207.749 [ms] (mean) Time per request: 20.775 [ms] (mean, across all concurrent requests) Transfer rate: 435.61 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 1 3.2 0 12 Processing: 62 204 52.2 199 400 Waiting: 61 203 52.0 199 400 Total: 62 205 54.3 199 411 Percentage of the requests served within a certain time (ms) 50% 199 66% 211 75% 220 80% 226 90% 264 95% 349 98% 381 99% 411 100% 411 (longest request)
You should in particular note the respective statistics in section “Connection Times” and “Percentage of the requests served …” of http://127.0.0.1:8000/ and http://127.0.0.1:8000/admin. There is a huge difference.
Generally, Timelimit option is a tricky one. Let us understand this from the manual of ab, which is quite explanatory −
-t timelimit Maximum number of seconds to spend for benchmarking. This implies a -n 50000 internally. Use this to benchmark the server within a fixed total amount of time. Per default there is no timelimit.
Let us run a test with this option. We will note our observations after the going through the output −
$ ab -n 100 -c 10 -t 60 http://127.0.0.1:8000/
This is ApacheBench, Version 2.3 <$Revision: 1604373 $> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Licensed to The Apache Software Foundation, http://www.apache.org/ Benchmarking 127.0.0.1 (be patient) Completed 5000 requests Completed 10000 requests Completed 15000 requests Completed 20000 requests Completed 25000 requests Completed 30000 requests Completed 35000 requests Completed 40000 requests Completed 45000 requests Completed 50000 requests Finished 50000 requests Server Software: Rocket Server Hostname: 127.0.0.1 Server Port: 8000 Document Path: / Document Length: 66 bytes Concurrency Level: 10 Time taken for tests: 22.547 seconds Complete requests: 50000 Failed requests: 0 Non-2xx responses: 50000 Total transferred: 13850000 bytes HTML transferred: 3300000 bytes Requests per second: 2217.61 [#/sec] (mean) Time per request: 4.509 [ms] (mean) Time per request: 0.451 [ms] (mean, across all concurrent requests) Transfer rate: 599.88 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 2 0.8 2 8 Processing: 0 2 3.2 2 218 Waiting: 0 2 3.2 2 218 Total: 2 4 3.1 4 220 Percentage of the requests served within a certain time (ms) 50% 4 66% 4 75% 4 80% 5 90% 5 95% 5 98% 7 99% 8 100% 220 (longest request)
Notice that the output shows this option overrides the number of requests specified by the -n option and continues upto the 50K requests. However, as the requests were handled very fast, ab has terminated as soon as 50k mark was achieved – within 22 seconds (see heading Time taken for tests) in the instant case.
You can test the same command replacing http://127.0.0.1:8000/ with http://127.0.0.1:8000/admin (assuming it is our web2py application) or a third party website like https://www.apache.org/, notice the difference in statistics.
There are a few checks which will help you successfully run the test, and measure the performance accurately. Consider the following conditions before performing the load test −
Ensure that no extra python module is loaded.
To avoid TCP/IP Port Exhaustion, you should typically wait 2-3 minutes before you move to another ab test.
Ensure that the number of concurrent connections are lower than Apache Worker Threads.
You should reboot the server before performing another test, if Apache or python crashes.
In this chapter, we will describe the various combinations of -n and -c with the important flags to gradually increase the load on your webserver.
You should mainly focus on how the following metrics change as you increase the load −
You should also notice for the threshold value when server starts getting stuck and you start getting failed requests.
Let us do 100 sequential page-loads by a single user −
$ ab -l -r -n 100 -c 1 -k -H "Accept-Encoding: gzip, deflate" http://127.0.0.1:8000/
This is ApacheBench, Version 2.3 <$Revision: 1604373 $> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Licensed to The Apache Software Foundation, http://www.apache.org/ Benchmarking 127.0.0.1 (be patient).....done Server Software: Rocket Server Hostname: 127.0.0.1 Server Port: 8000 Document Path: / Document Length: Variable Concurrency Level: 1 Time taken for tests: 0.045 seconds Complete requests: 100 Failed requests: 0 Non-2xx responses: 100 Keep-Alive requests: 0 Total transferred: 27700 bytes HTML transferred: 6600 bytes Requests per second: 2206.24 [#/sec] (mean) Time per request: 0.453 [ms] (mean) Time per request: 0.453 [ms] (mean, across all concurrent requests) Transfer rate: 596.80 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 0 0.0 0 0 Processing: 0 0 0.0 0 0 Waiting: 0 0 0.0 0 0 Total: 0 0 0.0 0 1 Percentage of the requests served within a certain time (ms) 50% 0 66% 0 75% 0 80% 0 90% 1 95% 1 98% 1 99% 1 100% 1 (longest request)
This case corresponds to a peak load on a website that gets around 50,000+ hits a month.
$ ab -l -r -n 10 -c 5 -k -H "Accept-Encoding: gzip, deflate" http://127.0.0.1:8000/
In the following subsequent outputs, we will be omitting the common header for clarity purpose.
... Requests per second: 2009.24 [#/sec] (mean) Time per request: 2.488 [ms] (mean) Time per request: 0.498 [ms] (mean, across all concurrent requests) Transfer rate: 543.52 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 1 0.5 1 2 Processing: 0 1 0.5 1 2 Waiting: 0 1 0.5 1 1 Total: 2 2 0.4 3 3 ERROR: The median and mean for the total time are more than twice the standard deviation apart. These results are NOT reliable. Percentage of the requests served within a certain time (ms) 50% 3 66% 3 75% 3 80% 3 90% 3 95% 3 98% 3 99% 3 100% 3 (longest request)
This test corresponds to 100 page loads by 10 different concurrent users, each user is doing 10 sequential pages loads.
$ ab -r -n 10 -c 10 -k -H "Accept-Encoding: gzip, deflate" http://127.0.0.1:8000/
... Requests per second: 2225.68 [#/sec] (mean) Time per request: 4.493 [ms] (mean) Time per request: 0.449 [ms] (mean, across all concurrent requests) Transfer rate: 602.07 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 1 2 0.7 2 3 Processing: 0 2 1.0 2 3 Waiting: 0 1 1.0 2 3 Total: 4 4 0.3 4 4 WARNING: The median and mean for the waiting time are not within a normal deviation These results are probably not that reliable. Percentage of the requests served within a certain time (ms) 50% 4 66% 4 75% 4 80% 4 90% 4 95% 4 98% 4 99% 4 100% 4 (longest request)
This test corresponds to 400 page loads by 20 different concurrent users, each user is doing 20 sequential pages loads.
$ ab -r -n 20 -c 20 -k -H “Accept-Encoding: gzip, deflate” http://127.0.0.1:8000/
... Requests per second: 1619.96 [#/sec] (mean) Time per request: 12.346 [ms] (mean) Time per request: 0.617 [ms] (mean, across all concurrent requests) Transfer rate: 438.21 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 2 6 2.3 6 10 Processing: 1 5 2.9 5 10 Waiting: 0 5 2.9 5 9 Total: 10 11 0.6 11 12 Percentage of the requests served within a certain time (ms) 50% 11 66% 11 75% 12 80% 12 90% 12 95% 12 98% 12 99% 12 100% 12 (longest request)
This test corresponds to 900 page loads by 30 different concurrent users, each user is doing 30 sequential pages loads.
$ ab -r -n 30 -c 30 -k -H "Accept-Encoding: gzip, deflate" http://127.0.0.1:8000/
... Requests per second: 2283.45 [#/sec] (mean) Time per request: 13.138 [ms] (mean) Time per request: 0.438 [ms] (mean, across all concurrent requests) Transfer rate: 617.69 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 2 6 2.7 6 11 Processing: 1 6 3.1 6 11 Waiting: 0 5 3.2 5 10 Total: 11 12 0.5 12 13 Percentage of the requests served within a certain time (ms) 50% 12 66% 12 75% 12 80% 12 90% 13 95% 13 98% 13 99% 13 100% 13 (longest request)
We have now learned how to increase the load gradually on the website and test its performance.
In this chapter, we will compare the outputs with and without flags. Let us see how the use of appropriate flags can increase the performance of your web application. Before that, we need to understand how if your application is simple then you may not notice the difference. As is the case with our simple application, with flags and without flags. Then we will perform the same test with https://www.apache.org/ URL, and see the difference.
In this section, we will understand how to test our application without flags.
$ ab -n 100 -c 10 http://127.0.0.1:8000/
This is ApacheBench, Version 2.3 <$Revision: 1604373 $> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Licensed to The Apache Software Foundation, http://www.apache.org/ Benchmarking 127.0.0.1 (be patient).....done Server Software: Rocket Server Hostname: 127.0.0.1 Server Port: 8000 Document Path: / Document Length: Variable Concurrency Level: 10 Time taken for tests: 0.244 seconds Complete requests: 100 Failed requests: 0 Non-2xx responses: 100 Keep-Alive requests: 0 Total transferred: 27700 bytes HTML transferred: 6600 bytes Requests per second: 2208.77 [#/sec] (mean) Time per request: 4.527 [ms] (mean) Time per request: 0.453 [ms] (mean, across all concurrent requests) Transfer rate: 597.49 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 1 2 0.7 2 3 Processing: 0 2 0.7 2 4 Waiting: 0 2 1.0 2 3 Total: 4 4 0.3 4 5 Percentage of the requests served within a certain time (ms) 50% 4 66% 4 75% 5 80% 5 90% 5 95% 5 98% 5 99% 5 100% 5 (longest request)
In this section, we will understand how to test our application with flags.
$ ab -l -r -n 100 -c 10 -k -H "Accept-Encoding: gzip, deflate" http://127.0.0.1:8000/
... Requests per second: 2277.07 [#/sec] (mean) Time per request: 4.392 [ms] (mean) Time per request: 0.439 [ms] (mean, across all concurrent requests) Transfer rate: 615.97 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 1 2 0.7 2 3 Processing: 0 2 0.7 2 4 Waiting: 0 2 1.0 2 3 Total: 4 4 0.2 4 5 Percentage of the requests served within a certain time (ms) 50% 4 66% 4 75% 4 80% 4 90% 5 95% 5 98% 5 99% 5 100% 5 (longest request)
We can simply note that there is not much difference between the output statistics.
Let us now see how to test the Apache Organisation Website without flags.
$ ab -n 100 -c 10 http://www.apache.org/
This is ApacheBench, Version 2.3 <$Revision: 1604373 $> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Licensed to The Apache Software Foundation, http://www.apache.org/ Benchmarking www.apache.org (be patient).....done Server Software: Apache/2.4.7 Server Hostname: www.apache.org Server Port: 80 Document Path: / Document Length: 58433 bytes Concurrency Level: 10 Time taken for tests: 1.498 seconds Complete requests: 100 Failed requests: 0 Total transferred: 5877500 bytes HTML transferred: 5843300 bytes Requests per second: 66.74 [#/sec] (mean) Time per request: 149.840 [ms] (mean) Time per request: 14.984 [ms] (mean, across all concurrent requests) Transfer rate: 3830.58 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 12 110 295.2 12 1012 Processing: 37 38 0.5 38 39 Waiting: 12 13 0.3 13 15 Total: 49 147 295.4 50 1051 Percentage of the requests served within a certain time (ms) 50% 50 66% 50 75% 50 80% 50 90% 816 95% 1050 98% 1051 99% 1051 100% 1051 (longest request)
Let us now test the Apache Organisation Website with Flags.
$ ab -l -r -n 100 -c 10 -k -H "Accept-Encoding: gzip, deflate" http://www.apache.org/
... Document Length: Variable Concurrency Level: 10 Time taken for tests: 0.357 seconds Complete requests: 100 Failed requests: 0 Keep-Alive requests: 100 Total transferred: 1358510 bytes HTML transferred: 1317700 bytes Requests per second: 280.28 [#/sec] (mean) Time per request: 35.678 [ms] (mean) Time per request: 3.568 [ms] (mean, across all concurrent requests) Transfer rate: 3718.41 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 1 3.7 0 12 Processing: 14 17 21.3 15 227 Waiting: 14 17 21.3 14 227 Total: 14 18 21.5 15 227 Percentage of the requests served within a certain time (ms) 50% 15 66% 15 75% 15 80% 15 90% 27 95% 28 98% 29 99% 227 100% 227 (longest request)
You can simply note how the request per second increased with the use of flags. In the instant case, it is particularly due to use of -H "Accept-Encoding: gzip, deflate because this flag tells the Apache server to serve requests in gzipped format.
A few important points need to be considered when it comes to the Apache Bench results. This will help us design our overall strategy to remove the bottlenecks in our application and improve its performance.
We need to Requests Per Second. This gives us an idea of how well our web server set-up is working; the larger the number, the better the performance. Then comes the Connection Times (ms) and the Percentage of the requests served. You may have to tweak the settings of your web server to change these metrics to your desired performance.
Check if there are errors in the Apache’s or the used web server error logs or (general) logs. As you will increase your load, things will start to choke: memory issues will start coming up. A lot of python scripts will begin to crash if they are not written with concurrency in mind.
You need to find out what is the critical concurrency value above which your web server crashes and/or times-out? Normally this should happen at a fairly high concurrency level. If this value is low, something is wrong and you need to adjust these settings lower/higher.
In this tutorial we learned how Apache Bench can be used to load test any web site or web application. Apache Bench can be a very valuable tool for determining how your web application server setup should be improved, to reduce bottlenecks and increase performance. Now that you are familiar with the basic usage of Apache Bench, you can start by creating new test plans to measure the performance of your applications in various scenarios.