When running Apache HTTP Server with Tomcat through mod_jk or mod_proxy, we often encounter a critical architectural challenge: Tomcat's performance issues can cripple the entire web server, even for static content that should be completely independent. This happens because:
- Apache worker threads get tied up waiting for Tomcat responses
- Connection pools get exhausted
- Queue backpressure affects the entire request handling pipeline
Looking at the provided configurations, several areas need optimization:
// Current httpd.conf settings
Timeout 120 // Too high for static content
KeepAlive Off // Good for this scenario
MaxClients 512 // May need adjustment based on server resources
We need to implement a two-layer approach that separates static and dynamic content handling:
1. Apache Configuration Modifications
# In httpd.conf
<IfModule prefork.c>
StartServers 16
MinSpareServers 10
MaxSpareServers 40
ServerLimit 512
MaxClients 512
MaxRequestsPerChild 4000
</IfModule>
# Separate timeouts for static vs dynamic content
<Location "/static/">
Timeout 5
</Location>
<Location "/ajax/">
Timeout 120
</Location>
2. Tomcat Connector Optimization
<!-- In server.xml -->
<Connector port="8888" protocol="AJP/1.3"
maxThreads="400"
minSpareThreads="100"
acceptCount="100" <!-- Queue size -->
connectionTimeout="20000"
executor="tomcatThreadPool"
socketBuffer="10000"/>
<Executor name="tomcatThreadPool"
namePrefix="ajp-exec-"
maxThreads="400"
minSpareThreads="50"/>
For critical systems, we can implement circuit breakers:
# In workers.properties
worker.worker1.retries=2
worker.worker1.recovery_options=3
worker.worker1.socket_timeout=10000
worker.worker1.connect_timeout=5000
worker.worker1.reply_timeout=30000
worker.worker1.lbfactor=1
worker.worker1.sticky_session=0
worker.worker1.redirect=worker2
Implement health checks and static fallbacks:
<LocationMatch "^/ajax/">
JKMount worker1
Header Set X-Application-Status "active"
ErrorDocument 503 /static/fallback.html
</LocationMatch>
- Separate timeouts for static/dynamic content
- Implement proper thread pooling in Tomcat
- Configure appropriate queue sizes
- Set up monitoring for AJP connections
- Create fallback mechanisms for AJAX failures
When integrating Apache HTTPD with Tomcat through mod_jk or AJP connectors, a common performance pitfall occurs when slow Tomcat responses start consuming Apache worker threads. Here's what happens:
- Apache worker threads get blocked waiting for Tomcat responses
- Thread pool exhaustion prevents Apache from serving static content
- The entire site becomes unresponsive during backend issues
Here are the essential adjustments to isolate static content performance:
# httpd.conf modifications
Timeout 30 # Reduced from 120
KeepAlive On
KeepAliveTimeout 2
MaxKeepAliveRequests 100
<IfModule prefork.c>
MaxClients 400 # Reduced from 512
MaxSpareServers 30 # Reduced from 40
</IfModule>
# workers.properties additions
worker.worker1.connection_pool_timeout=30
worker.worker1.retries=2
worker.worker1.recovery_options=7
The most critical setting is properly configuring timeouts at multiple levels:
# server.xml connector configuration
<Connector port="8888" protocol="AJP/1.3"
connectionTimeout="10000"
keepAliveTimeout="5000"
socketTimeout="15000"
maxThreads="300"
acceptorThreadCount="4" />
For more robust isolation, consider separating traffic at the Apache level:
<VirtualHost *:80>
# Static content handler
DocumentRoot /var/www/html
<Location /ajax/>
JkMount worker1
# Special timeout for AJAX requests
ProxyTimeout 20
</Location>
<Location />
SetHandler default-handler
Options -ExecCGI
</Location>
</VirtualHost>
Implement health checks and failover mechanisms:
# workers.properties health check
worker.worker1.connection_pool_minsize=10
worker.worker1.connection_pool_ttl=60000
worker.worker1.lbfactor=1
worker.worker1.ping_mode=A
worker.worker1.ping_timeout=10000
Here's how to implement a fallback mechanism in your frontend code:
// JavaScript AJAX wrapper with timeout
function safeAjaxRequest(url, data, success, error) {
const timeout = 5000; // 5 second timeout
const timer = setTimeout(() => {
if (error) error({timeout: true});
}, timeout);
$.ajax({
url: url,
data: data,
success: (response) => {
clearTimeout(timer);
success(response);
},
error: (xhr) => {
clearTimeout(timer);
error(xhr);
}
});
}