Tag Archives: performance

AWS EC2 Nginx Reverse Proxy And localhost Slowness

This is something really odd I yet to fully understand, but for time being I’ve decided using localhost on AWS ec2 is bad. (at least on Windows Server 2008 R2)

I think it might have something to do with internal DNS or address routing, but my nginx-reverse-proxied tomcat is 4-5x slower when I bind it into localhost as opposed of the local IP. We’re talking 1 minute to load a 300kb css file!

Open a command prompt and run ipconfig /all and your local ip should be under IPv4 Address under Ethernet adapter Local Area Connection 2:

ec2-local-ip

On tomcat, edit your server.xml, find your element and add address attribute:


And finally update nginx.conf to point the reverse proxy backend to above IP.

After doing this now my reverse proxy is much faster, only few seconds to load 300kb css file.

Benchmarking Web Page Load Time Using Apache AB Tool

Any apache httpd installation comes with ab tool on the bin folder. This handy tool can be used to perform benchmark testing:

ab -n 10 -c 2 http://www.mycoolwebsite.com/
  • -n 10: Make a total of 10 request to http://www.mycoolwebsite.com/
  • -c 2: Allow maximum simultaneously 2 concurrent request (2 threads)

The output you get is quite self-describing:

This is ApacheBench, Version 2.3 
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking www.vantagefx.com (be patient).....done


Server Software:        Apache
Server Hostname:        www.mycoolwebsite.com
Server Port:            80

Document Path:          /
Document Length:        29320 bytes

Concurrency Level:      2
Time taken for tests:   4.524 seconds
Complete requests:      10
Failed requests:        0
Write errors:           0
Total transferred:      297360 bytes
HTML transferred:       293200 bytes
Requests per second:    2.21 [#/sec] (mean)
Time per request:       904.890 [ms] (mean)
Time per request:       452.445 [ms] (mean, across all concurrent requests)
Transfer rate:          64.18 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    1   1.3      0       4
Processing:   806  887  90.4    852    1063
Waiting:      804  885  90.4    850    1061
Total:        806  887  91.3    852    1067

Percentage of the requests served within a certain time (ms)
  50%    852
  66%    888
  75%    918
  80%   1027
  90%   1067
  95%   1067
  98%   1067
  99%   1067
 100%   1067 (longest request)

Note that however I believe this tool will only request specified html page, not external resources associated with the page (no external images, javascript, css, etc.).

If you want to test https (SSL) page, make sure you have a version of Apache httpd with ssl support, and use abs instead.

Handling Heavy-lifting Job Using Thread Pooling

Your java app server such as Tomcat, JBoss, Websphere and Glassfish are multithreaded — meaning if a user opens a web page (HTTP request submitted), the app server will try to find a free unused thread, and allocate the job of serving the request to it.

One common problem which I recently come accross is when the user performs a long-running task such as reporting or data transformation. If you just simply allow a user to execute a long-running task, your app server is vulnerable to thread starvation — that is genuine user load, or even hackers can issue a massive amount of request causing your server to run out of free threads — hence denying application availability.

One approach is to implement a resource bounding pattern such as thread pooling. The analogy of this pattern is similar like a chef in a restaurant. Say if there is 2 chefs on a restaurant, and each one of them needs 10 minutes to cook an order. Each next free chef will take the order in the order that it arrives, and if all two of them are busy, subsequent order will be queued .

To illustrate this with an example, following is a class that represent a long-running task. Here the task is simply performing 10 seconds delay to simulate the long-run. A log message with task id and the name of the thread running the task is printed when the task begins and ends.

[sourcecode language="java"]
public class HeavyTask implements Runnable {
private static Logger logger = LoggerFactory.getLogger(HeavyTask.class);
private int taskId;

public HeavyTask(int taskId) {
this.taskId = taskId;
}

public void run() {
logger.info(String.format("Task #%s is starting on thread %s…", taskId, Thread.currentThread().getName()));

try {
Thread.sleep(10 * 1000);
} catch (InterruptedException e) {
logger.error(String.format("Task #%s running on thread %s is interrupted", taskId, Thread.currentThread().getName()), e);
}

logger.info(String.format("Task #%s on thread %s finished", taskId, Thread.currentThread().getName()));
}
}
[/sourcecode]

Secondly we have the HeavyTaskRunner class which is a service class that implements business functionality. This class also instantiates the thread pool using Java ExecutorService API with 2 maximum threads by using Executors.newFixedThreadPools() factory method.

[sourcecode language="java"]

public class HeavyTaskRunner {
private ExecutorService executorService;
private static final int NUM_THREADS = 2;
private int taskCounter = 0;

public HeavyTaskRunner() {
executorService = Executors.newFixedThreadPool(NUM_THREADS);
}

/**
* Create a new { HeavyTask} and submit it to thread pool for execution
*/
public int runTask() {
int nextTaskId;
synchronized(this) {
nextTaskId = taskCounter++;
}
executorService.submit(new HeavyTask(nextTaskId));
return nextTaskId;
}
}
[/sourcecode]

And finally I wrapped this in a nice simple Spring MVC web application. You can download the source code from my google code site and run embedded tomcat container using mvn tomcat:run command.

If you look at the server log, this is what it prints after 4 tasks were run in less than 10 seconds. Notice that not more than 2 tasks ever run simultaneously. If all threads are busy the task will be queued until one become free:

This techniques is ofcourse not without downside. Although we have capped the thread (an memory) resource into certain limit, the queue can still grow infinitely, hence our application is still vulnerable to denial of service attack (if a hacker submitted long running tasks for a thousand time, the queue will grow very big causing service unavailability to genuine users)

The concurrency chapter of Java SE tutorial is a good reference if you want to dig more into other techniques of managing threads and long running tasks.

Demo Application Source Code

Source code for the demo application above is available for browsing / checkout via Google Code hosting:

  • Browse: 
  • Checkout:

Ensure you have latest version of JDK and Maven installed. You can run the application using embedded tomcat with mvn tomcat:run command from your shell.