From The Atlassian Developer Blog:
You appear to be running Tomcat in development mode.
(http://tomcat.apache.org/tomcat-5.5-doc/jasper-howto.html)
This means that source of every JSP is held in memory to provide
detailed messages in the event of an error. If you have large JSPs
this will hurt. It looks like this accounts for 50MB+
From Tomcat's Production Configuration documentation:
The main JSP optimization which can be done is precompilation of JSPs. However,
this might not be possible (for example, when using the jsp-property-group feature)
or practical, in which case the configuration of the Jasper servlet becomes critical.
When using Jasper 2 in a production Tomcat server you should consider
making the following changes from the default configuration.
- development - To disable on access checks for JSP
pages compilation set this to
false
.
- genStringAsCharArray - To generate slightly more efficient
char arrays, set this to
true
.
- modificationTestInterval - If development has to be set to
true
for any reason (such as dynamic generation of JSPs), setting
this to a high value will improve performance a lot.
- trimSpaces - To remove useless bytes from the response,
set this to
true
.
Seems like good information to know.
I'm trying to configure Apache and Tomcat to work with a desired architecture for doing A/B Testing on my current project. Our basic idea is that we'll deploy entirely new WAR files when we have a test, then use the magic of Apache's mod_rewrite, mod_jk and possible the UrlRewriteFilter to keep the URLs somewhat consistent between version A and version B. Here's some questions I have for those folks who might've done this before:
- Is it possible to use Apache's mod_rewrite to map http://www.domain.com/?v=1 to http://www.domain.com/1 (this allows us to have two different applications/wars served up to the same domain).
- If #1 is possible, what's the RewriteRule for allowing the parameter to be anywhere in the query string, but still allowing the target to use it as the context name?
- Is it possible to use something in the WAR (likely the UrlRewriteFilter) to produce HTML that has rewritten links (i.e. http://www.domain.com/?id=1 in the WAR whose context is 1)?
In other words, can Apache forward to the correct app going in, and can that app rewrite its URLs so those same URLs are used when going out?
I believe this is all possible. However, I am having difficulty getting mod_jk to allow mod_rewrite to be processed first. If I have the following in httpd.conf, it seems like htdocs/.htaccess gets bypassed.
JkMount /* loadbalancer
Is it possible to configure Apache/mod_jk so WARs can hang off the root, but still use mod_rewrite? If not, the only solution I can think of is to use UrlRewriteFilter in the WAR to forward to another context when a "v" parameter is in the URL. Currently, the UrlRewriteFilter doesn't allow forwarding to another context. The good news is the Servlet API allows it. I got it working in Tomcat (with crossContext enabled) and wrote a patch for the UrlRewriteFilter.
Anyone out there have experience doing A/B Testing in a Java webapp? If so, did you try to disguise the URLs for the different versions?
Update:
I've got a bit of this working. The magic formula seems to be don't try to hang things off the root - use mod_rewrite to make things appear to hang off the root.
First of all, I posted a message similar to this post to the tomcat-user mailing list. Before I did so, I discovered mod_proxy_ajp, which happens to look like the successor to mod_jk. AFAICT, it doesn't allow fine-grained rules (i.e. only serve up *.jsp and *.do from Tomcat), so I'll stick with mod_jk for now.
Rather than proxying all root-level requests to Tomcat, I changed my JkMount to expect all Tomcat applications to have a common prefix. For example, "app".
JkMount /app* loadbalancer
This allows me to create RewriteRules in htdocs/.htaccess to detect the "v" parameter and forward to Tomcat.
RewriteEngine On
RewriteCond %{QUERY_STRING} ^v=(.*)$
RewriteRule ^(.*)$ /app%1/ [L]
This isn't that robust as adding another parameter causes the forward to fail. However, it does successfully forward http://localhost/?v=1 to /app1 on Tomcat and http://localhost/?v=2 to /app2 on Tomcat.
What about when ?v=3 is passed in? There's no /app3 installed on Tomcat, so Tomcat's ROOT application will be hit. Using the UrlRewriteFilter, I installed a root application (which we'll likely need anyway) with the following rule:
<rule>
<from>^/app(.*)$</from>
<to type="forward">/</to>
</rule>
So I've solved problem #1: Using URL parameters to serve up different web applications. To solve the second issue (webapps should rewrite their URLs to delete their context path), I found two solutions:
- Use mod_proxy_html. Sounds reasonable, but requires the use of mod_proxy.
- Use the UrlRewriteFilter and outbound-rules.
Since I'm using mod_jk, #2 is the reasonable choice. I added the following link in my /app1/index.jsp:
<a href="<c:url value="/products.jsp"/>">link to products</a>
By default, this gets written out as http://localhost/app1/products.jsp. To change it to http://localhost/products.jsp?v=1, I added the following to urlrewrite.xml:
<outbound-rule>
<from>^/app1/(.*)$</from>
<to>/$1?v=1</to>
</outbound-rule>
This produces the desired effect, except that when I click on the link, a new session is created every time. AFAICT, I probably need to do something with cookies so the jsessionid cookie is set for the proper path.
Not bad for a day's work. Only 2 questions remain:
- What's a more robust RewriteRule that doesn't care about other parameters being passed in?
- What do I need to do so new sessions aren't created when using an outbound-rule?
It's entirely possible that mod_proxy_ajp with mod_rewrite_html is best tool for this. Can mod_proxy handle wildcards like JkMount can? I've heard it's faster than mod_jk, so it probably warrants further investigation.
Update 2: I achieved the desired result using mod_rewrite, mod_jk and the UrlRewriteFilter (for outgoing links). Here's what I put in htdocs/.htaccess (on Apache):
RewriteEngine On
# http://domain/?v=1 --> http://domain/app1/?v=1
RewriteCond %{QUERY_STRING} v=([^&]+)
RewriteRule ^(.*)$ /app%1/$1 [L]
# http://domain --> http://domain/app (default ROOT in Tomcat)
RewriteRule ^$ /app/ [L]
And in the urlrewrite.xml of each webapp:
<outbound-rule>
<from>^/app([0-9])/([A-Za-z0-9]+)\.([A-Za-z0-9]+)$</from>
<to>/$2.$3?v=$1</to>
</outbound-rule>
<outbound-rule>
<from>^/app([0-9])/([A-Za-z0-9]+)\.([A-Za-z0-9]+)\?(.*)$</from>
<to>/$2.$3?$4&v=$1</to>
</outbound-rule>
Next I'll try to see if I can get it all working with mod_proxy_ajp and mod_proxy_html. Anyone know the equivalent of "JkMount /app*" when using LocationMatch with mod_proxy?