The Apache JServ Protocol (AJP) , is a binary protocol that can proxy inbound requests from a webserver, such as Apache HTTPD, to an application server like Apache Tomcat. Typically used in load balanced web applications where the web server has to pass requests to multiple application servers, using modules like mod_proxy_ajp help improve the speed of transactions and add support for SSL. This week’s update of Apache Tomcat 7.0.16, introduces a NIO implementation of the built-in AJP connector.
New I/O, usually shortened to NIO, is a set of Java APIs that allow for more scaleable I/O operations. Among other things, NIO provides support for non-blocking of data connections which ensures a response from the application server. Without NIO, admins must configure their web servers and application servers to match the number of threads between the web server and application server. Depending on configuration, application behavior and the number of concurrent sessions, there is a constant risk of running out of threads and having users get a HTTP 500 Internal Server error. NIO eliminates this risk by providing a more efficient usage of these threads.
In simple deployments, users will have one HTTPD instance and one Tomcat server to host their web application. Configure both to use 1000 threads, and the web server instance and the application server instance should run fine. Where it gets complicated is when you employ multiple HTTPDs and multiple instances of Tomcat, and those instances are not using a 1 to 1 mapping— i.e. situations where any HTTPD instance can talk to any Tomcat instance.
Let’s say that you have 2 HTTPD instances and 2 Tomcat instances. Each HTTPD is configured for 1000 threads. Each Tomcat will need to be available to process a connection from each of the threads from all of the instances of HTTPD. So each Tomcat will need 2000 threads. As we add more, this quickly does not scale. As the number of HTTPD instances go up, you need more and more threads on the Tomcat side and eventually this becomes unsustainable.
To avoid running out of threads, admins can work to time out threads, or deploy the Apache MPM Worker to use a pool of threads to reduce the number of connections you will need between HTTPD and Tomcat. Both of these options require significant time and effort to tune the application so it performs well and does not create long running requests or worse, serve up the dreaded 500 Internal Server Error.
The NIO implementation of the AJP connector uses non-blocking I/O on the Tomcat side. So, rather than having one thread per connection, there is a small number of poller threads (normally one or two, and usually under ten) that maintain the connections between Apache HTTPD and Tomcat. Each poller thread can maintain many connections. These poller threads watch for incoming requests from HTTPD and when a request is received , the poller thread passes the request to a worker thread. Once the worker thread completes its task, the worker thread is free to handle another request while the poller thread maintains the connection to httpd. In this scenario, rather than Tomcat having one thread per httpd connection, you can have one thread per concurrent request processed by Tomcat (plus a safety margin) which is often several orders of magnitude lower.
Prior to the NIO implementation of the AJP connector, which is pure Java, there was another option for a non-blocking connector through the APR/Native connector. The challenge with the APR/Native implementation is that the code needs to be compiled for every platform. Users need to compile the code for every platform they have throughout their infrastructure. Depending on the size of your architecture, this can get to be an enormous task. Since the NIO implementation is pure Java, there is no need to compile, it just works.
For some deployments, the APR/Native connector may still be worth the extra work of compiling as it does provide better performance for SSL.
In recent years, a reasonable number of SpringSource’s support calls come in about configuring Tomcat and HTTPD to ensure that there are always threads available to serve any given request. This new NIO implementation of AJP provides users with a new option that will simplify configuration, tuning and improve performance for deployments where there are multiple instances of HTTPD as well as multiple instances of Tomcat. This will be particularly true where there are multiple platform types involved as it will simplify the setup of this type of non-blocking connector.