Tomcat is a fairly simple beast. It's very easy to get up and running and doesn't require much configuration for a beginner to start developing with. There are, however, plenty of configuration options available for businesses or organisations that depend on commercial Tomcat deployments. While Tomcat has a lot of very sensible defaults, they can't guarantee that the settings are appropriate for every business - or every production environment.
In the same way, I can't create a list of settings to tune for everyone, but what I can do is explain a little about the way Tomcat connectors are put together and what things to consider when beginning to tune for performance.
Anatomy of a Connector
Tomcat connectors are quite simple, but they aren't just a thread pool. Each connector uses an Executor to manage threads - whether a named executor is configured or not.
The executor's attributes are the same as some attributes of the connector. This is because the connector will create its own private executor if there isn't a named one configured with the connector's "executor" attribute. The only practical difference between the two options is that configuring a separate named executor will allow the same thread pool to be shared between connectors.
Another thing to bear in mind, aside from the executor, is the acceptor. By default, there is a single thread used as an acceptor, which is generally fine. It may be worth increasing the number of threads with the number of CPU cores available, but beyond a couple of threads there will be diminishing returns.
The acceptor thread(s) accept connection requests and pass them on to the executor to be processed. Once maxThreads is reached connection requests will still be accepted, up to the acceptCount and queued ready to be processed.
It's important to remember that changing values of attributes in the connector can impact other components, such as database connection pools. For example, it's generally not a good idea to have maxThreads be a lot higher than the maximum size of your DB connection pool, since a high proportion of those requests may just sit there, consuming resources, and giving your users an impression of slowness from your application. If you need to support more requests, you need to make sure that every component of your system can support the same number to avoid bottlenecks.
Choosing a Protocol
Tomcat supports both AJP and HTTP connectors. HTTP is perhaps more commonly used but AJP is, in many circumstances, the more preferable option due to the performance benefits.
AJP is a binary protocol, as opposed to HTTP which is text. Each packet is a maximum of 8K and uses only a very small amount of that 8K for headers - just 3 bytes, where byte 3 is the data length of the packet.
In comparison, the equivalent HTTP header might be something like Content-Length: 256, where each ASCII character would need a byte each.
AJP, being binary, has no secure option. This is good for performance, but if you need SSL connections direct to Tomcat, then you're left with HTTPS. This shouldn't ever be the case, really, since Tomcat should be proxied through a load balancer and should not be directly accessible from the outside world.
Beyond the choice between AJP and HTTP, there is the choice of connector type - BIO, NIO, NIO2 or APR (native). By default, there is an auto-switching mechanism which will choose what Tomcat thinks is the most appropriate type, however the most performant is almost certainly going to be the APR/native connector. This is because it uses the Apache Portable Runtime (APR), which is natively compiled, though this does require separate installation/configuration.
Shouldn't I use httpd to serve static content?
Traditionally, the advice around the Internet was to use Apache httpd to serve static web pages, since it was so much faster than Tomcat. This seems to make sense; httpd is native whereas Tomcat runs in the Java virtual machine.
This is not always the case, however.
Well-known Tomcat developer Mark Thomas explains more in his blog post from 2010, in which he talks about the reasons why speed should no longer be a deciding factor:
"Tomcat now supports the native/APR connector which uses the same native library (the Apache Portable Runtime—APR) as httpd for the low-level I/O and therefore can achieve similar performance to httpd. When serving static content there is ever so slightly more overhead when using Tomcat compared to httpd but the differences are so small they are unlikely to be noticeable in production systems."
So, the thing to bear in mind is that, yes, Tomcat can serve static content just as fast as httpd, but only if you've configured Tomcat to use the APR/native connector.
For most cases, an APR/native connector using the AJP protocol is the most performant; though it is important to make sure the number of threads available does not exceed the capability of other resources (e.g. the DB connection pool, or operating system ulimit).
You may find it useful to ask yourself some questions when configuring your connectors:
- How many requests do I need to serve in total? What's the average number and maximum number?
- How many Tomcat instances have I got to serve those requests? Does each instance have the resources it needs?
- Do I need to secure requests coming to Tomcat?
Questions like these help in getting an understanding of the needs of your particular system, before testing your tuned settings.