Improving the performance of the JSPs that serve your HTML pages is the first step in improving the overall site performance. The user’s browser can not start rendering the page or requesting the secondary media. Also the faster the page request is completed, the sooner you have a thread free to handle the next request.
There are two parts to this: first, the time it takes the JSP servlet to generate the HTML response, and secondly the time it takes to transmit that HTML response back to the user’s browser.
Caching content sections
The easiest way to reduce the time it takes for the JSP servlet to generate the response is by reducing the amount of dynamic content on the page. Or more precisely by reducing the amount of real-time or unique individual content on the page.
The Cache droplet is THE most under-utilized ATG droplet.
The Cache droplet caches the rendered output of the contents of the oparam based on a content key (such as category, user gender, logged in/logged out state, etc..) for a configured period of time. This can be very useful for things like navigation menus dynamically built based on the catalog. The catalog won’t change too often, so this dynamically generated menu can be safely cached for hours. Or for some or all of a category or product page, when you set the key to the category id or product id.
Look at your pages and evaluate what parts of the page don’t change that frequently. Even if you can only cache the page or block for five minutes, that can be a huge performance win.
Read on for more….
I’ll talk about things like repository caching and other related topics when I dive into the server side of performance tuning your ATG application in a future post.
Reduce CPU utilization in page rendering
You’re also going to want to look at potential CPU draining elements of your pages. Think about what has to happen to render the page. Don’t use RQLQueryForEach if you have the Repository Id you want, just use the RepositoryLookup droplet. Don’t loop through collections if you don’t have to. Think through it. If you have to do something computationally expensive, don’t do it twice. If you need the same data later just store the first result in a param or JSP variable.
Reduce the size of the HTML
Now that we’ve reduced the CPU time need to render the JSP, we need to reduce the amount of time it takes to transmit to the end user. First, we want to shrink the generated HTML. The smaller the output HTML is, the less time it will take to transfer across the wire to the end user. Also, you want to keep the HTML output under 100k, as Google does not index page content over the first 100k (that’s 100k ungzipped). Also, the smaller the HTML is, the quicker the browser can parse it out into the DOM tree.
Simplify the html structure as much as you can. Don’t use more tags than you need. I’ve seen an amazing amount of useless nested divs on pages.
Move comments from HTML comments to JSP comments (if you can). Most comments shouldn’t be seen by end users anyhow.
Reduce spaces. This is huge. JSPs (especially ones heavy in ATG droplets) tend to generate a huge amount of extraneous spaces. Part of it is that you generally want to format the JSP files using spaces and tabs to make it easy to see the tag hierarchy structure. It makes development and maintenance much easier. Unfortunately these linefeeds, tabs, and spaces get multiplied by iterative or recursive droplets, and other outputs.
If you’re using JBoss the Tomcat engine has an option to strip out extraneous whitespace. It doesn’t remove all of it, but it helps with multiple blank lines very well. In the tomcat55.sar/conf/web.xml file add the following init param to the <servlet-name>jsp</servlet-name> section:
<init-param> <param-name>trimSpaces</param-name> <param-value>true</param-value> </init-param>
This, and other helpful performance configurations are listed in the Tuning Site Performance section of the Installation and Configuration Guide for JBoss Application Server ATG documentation. You should really read through the whole thing, and generally follow it.
Compressing the HTML response using gzip
Now we’ve reduced the size of the HTML the JSP is outputting. We can further shrink the data we need to send from our server cluster to the end user’s browser, hence making the HTML transfer faster, by compressing the HTML using gzip. Most modern browsers understand gzipped content and decompress it on the fly. Some don’t, and we’ll work around those in a moment.
Assuming you’re using JBoss for your application server and Apache for your web server, you actually have two options for gzipping the content: You can compress it from within JBoss Tomcat, or you can compress it as it passes through the Apache web server.
If you want to compress it within JBoss you can configure Tomcat to do that by editing the tomcat55.sar/conf/server.xml file. Find the connector you are using (the one with port=”8080″ for HTTP, or port=”8009″ if you’re using AJP) and add the following properties to that section:
compression="on" compressionMinSize="2048" noCompressionUserAgents="gozilla, traviata" compressableMimeType="text/html,text/xml"
That should use gzip compression for output that is html or xml, and is greater than 2k in size.
I prefer to perform the gzip compression in Apache instead of JBoss/Tomcat. Firstly the Apache deflate module is more flexible than the Tomcat option, allowing greater control of compression based on path, mimetype, extension, browser, and more. Second in a three-tier infrastructure, the web servers are typically acting as pass-through proxies, plus handling some caching perhaps (more on this later), but are generally light on CPU utilization, but higher on thread and bandwidth usage. App servers on the other hand are using this CPUs a great deal to actually handle the dynamic application. Gzipping the outgoing content does use some CPU power, so I prefer to have that load be on the web servers who can handle it easily, versus having it impact the application performance.
To make Apache gzip compress the output, you need to enable mod_deflate.
You then need to configure the module using settings similar to this:
# Insert filter SetOutputFilter DEFLATE # Netscape 4.x has some problems... BrowserMatch ^Mozilla/4 gzip-only-text/html # Netscape 4.06-4.08 have some more problems BrowserMatch ^Mozilla/4\.0 no-gzip # MSIE masquerades as Netscape, but it is fine BrowserMatch \bMSIE no-gzip # NOTE: Due to a bug in mod_setenvif up to Apache 2.0.48 # the above regex won't work. You can use the following # workaround to get the desired effect: BrowserMatch \bMSIE\s7 !no-gzip !gzip-only-text/html # Don't compress images SetEnvIfNoCase Request_URI \ \.(?:gif|jpe?g|png|swf|flv)$ no-gzip dont-vary # Make sure proxies don't deliver the wrong content Header append Vary User-Agent env=!dont-vary
You can read more about this configuration, plus some issues that arise when using mod_disk_cache and mod_deflate together here. You can get around this by only gzipping html output from your JSPs, which you won’t be caching anyhow, however doing that in a clean fashion appears to be tricky. The AddOutputFilterByType directive doesn’t work with proxied content, and the mod_filter based approach for that is only built into Apache 2.1 and 2.2, so if you’re on 2.0 it’s no good. When I have a good solution for Apache 2.0 + ATG, I’ll post about it.
By gzipping the HTML we can reduce the BestBuy page further, from 80k to 14k, an additional 80% reduction. We’ve shrunk the data that has to transfer to the end user’s computer by a total of 85% at this point. This means the end user’s browser can parse the HTML dom tree, display the page, and start loading the secondary media (images, css, js, etc…) 85% faster. It also means your request handling thread is now free that much faster (after doing the actual processing of the JSP servlet) and is available to handle a new request.
So at this point we’ve made the JSP as efficient as possible, used the Cache droplet wherever we could, reduced the size of the output HTML, and gzipped the HTML for faster transmission to the end user. We’ve taken a 98k page and ended up only having to move 14k across the wire.