Recipe 12.3. Improving Performance by Caching Static Pages


Problem

You want to improve application performance by cashing entire pages that are static or that containing changes that don't need to be shown in real time.

Solution

You can instruct Rails to cache entire pages using the caches_page class method of Action Controller. You call caches_page in your controllers and pass it a list of actions whose rendered output is to be cached; for example:

app/controllers/articles_controller.rb:

class ArticlesController < ApplicationController   caches_page :show   def show     @article = Article.find(params[:id])   end   # ... end

Now, start your server in production mode, visit the site, and invoke the show action of the Articles Controller in a browser with:

http://tupleshop.com/articles/show/2

In addition to displaying the second article (id = 2), page caching writes the show action's output to a cache directory as a static HTML file. The following is the HTML file that's created under your application's public directory:

public/articles/show/2.html:

<html> <head>   <title>Articles: show</title>   <link href="/stylesheets/scaffold.css?1156567340" media="screen" rel="Stylesheet"     type="text/css" /> </head> <body> <p style="color: green"></p> <p>   <b>Title:</b> Article Number Two </p> <p>   <b>Body:</b> This would be the body of the second article... </p> <a href="/articles/edit/2">Edit</a> | <a href="/articles/list">Back</a> </body> </html>

The file is the result of what was rendered by the show action along with the following articles.rhtml layout file:

app/views/layouts/articles.rhtml:

<html> <head>   <title>Articles: <%= controller.action_name %></title>   <%= stylesheet_link_tag 'scaffold' %> </head> <body> <p style="color: green"><%= flash[:notice] %></p> <%= yield  %> </body> </html>

Discussion

Using caches_page :show in your controller class definition instructs Rails to cache all pages rendered by the show action by writing the output to disk the first time a specific URL is requested. The files in the cache directory are named after the components of the requested URL. The cache directory in the solution is called articles (named after the controller) and contains a subdirectory named show, (named after the action). Each file in the cache is named using the id from the request and a .html file extension.

On subsequent requests, these cached HTML pages are served straight from disk by your web server, and Rails is bypassed entirely. This produces tremendous performance gains.

As the solution demonstrates, enabling Rails page caching is relatively simple. What is more complex is getting your web server to recognize that there are static HTML pages it should render instead of invoking the Rails framework. The following VirtualHost definition demonstrates how this can be set up in Apache, using the mod_rewrite module:

apache2.2.3/conf/httpd.conf:

<Proxy balancer://blogcluster>     # cluster member(s)     BalancerMember http://127.0.0.1:7171  </Proxy> <VirtualHost *:81>     ServerName blog     DocumentRoot /var/www/cache/public     <Directory /var/www/cache/public>         Options Indexes FollowSymLinks MultiViews         AllowOverride None         Order allow,deny         allow from all     </Directory>     RewriteEngine On     RewriteRule ^$ index.html [QSA]     RewriteRule ^([^.]+)$ $1.html [QSA]     RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f      RewriteRule ^/(.*)$ balancer://blogcluster%{REQUEST_URI} [P,QSA,L] </VirtualHost>

After turning the rewrite engine on, two rewrite rules are defined. These rules translate both requests for the application root and requests in the typical Rails format (controller/action/ID) into requests for HTML files that may exist in a cache directory. The rewrite condition builds a system file path out of the request string and checks whether the HTML file actually exists. If an HTML file corresponds to the incoming request, it's served directly by the web server, bypassing Rails. If no HTML file is found in the cache (i.e., the rewrite condition passes), a rewrite rule passes the request on to mod_proxy_balancer, which has Rails handle the page via a Mongrel process.

It may seem like things could get complicated with Rails creating subdirectories in your application's public directory that you may not have anticipated, possibly conflicting with a directory that already exists. This situation is easily avoided by changing the default base directory for the cache store. To do this, add the following line to your config/environment.rb:

config.action_controller.page_cache_directory = \                                           RAILS_ROOT+"/public/cache/"

With the cache directory changed, you'll have to modify the rewrite rules accordingly. Replace them with the following:

RewriteRule ^$ cache/index.html [QSA] RewriteRule ^([^.]+)$ cache/$1.html [QSA]

mod_rewrite is a powerful and complex module. If you get into trouble and need to see more of what's happening behind the scenes, enable debugging by adding the following to your virtual host definition:

RewriteLog logs/myapp_rewrite_log RewriteLogLevel 9

See the Apache documentation for more information.

See Also

  • Section 12.4"

  • Section 12.5"

  • Section 12.7"

  • Section 12.8"




Rails Cookbook
Rails Cookbook (Cookbooks (OReilly))
ISBN: 0596527314
EAN: 2147483647
Year: 2007
Pages: 250
Authors: Rob Orsini

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net