Nginx / Memcached Direct Page Caching
There are a few different approaches to caching; data caching, fragment caching, and full-page caching. But first, here's the typical model of a web application. User requests a page, and it works through the web server, through several layers, and finally to the database.

Data caching takes the place of a database call and can be effective if there are a lot of database requests. You would typically want to do this if you know that a piece of data is very common and unlikely to change. This saves time on one step of the process.

Fragment caching is a little more complicated. Instead of caching the data that you would input into another process to build a layout (for example), you just cache that layout directly. This is great for a "featured news" section that changes only once a day and is viewed often. It would be silly to cache that data and calculate the layout for every view, so instead we would just cache the layout itself. Saves a bunch of work.
Moving on, we have page caching. Which is the topic which I really want to go over. I have a warning though, page caching is dumb. REAL dumb. But, in that magnificent dumness is speed. Page caching is the most efficient of the three. It takes the gains we get from fragment caching and goes even further. Think about it, we hardly need to do anything—and therein lies the problem, we lose ALL dynamic ability of the page. This is not an issue for a lot of sites; news pages, catalogs, and blogs can all benefit from this method of caching.

. . .
The method I describe here I used allowed a real site to receive 250,000 real page-views in 40 min (~104 requests/sec). I expect you can do much more. (More number towards the end.)
What I haven't mentioned yet is that I'm using PHP (specifically Joomla). PHP is certainly not the fastest language around, but considering the whole spectrum of issues facing a web developer, script performance is minimal. And since I'm on this rabbit trail, I'll mention that Javascript/CSS is the first thing to fix, then caching. I don't need to explain more about this, there are volumes about it already.
. . .
Moving on, the method described for page caching is based on a simple fallback plan. Here is the psudo-code.
If the page requested is in cache
serve cached version
otherwise
serve it dynamically
add result to memcached
. . .
Joomla Plugin
With Joomla we can actually do this very easily. We just need to hook into the onAfterRender event and grab the page data. You might notice we're using the database name as the prefix to the URL in the key name. This is just to be friendly to any other sites hosted on the same server (or copies of the same site using a different database).
You also might notice that we're grabbing the gzipped version of the page. This is just to trim a little off what the user needs to download.
// no direct access
defined( '_JEXEC' ) or die( 'Restricted access' );
jimport( 'joomla.plugin.plugin' );
class plgSystemFullPageMemcache extends JPlugin
{
function plgSystemFullPageMemcache(& $subject, $config) {
parent::__construct($subject, $config);
}
function onAfterRender() {
global $mainframe;
$config = new JConfig();
$cache_id= $config->db . ":" . $_SERVER['REQUEST_URI'];
$data = JResponse::toString($mainframe->getCfg('gzip'));
$db = new Memcache;
$db->addServer('127.0.0.1', '11211', true);
$db->set($cache_id, $data, 1, 3600);
}
}
Nginx configuration
server {
listen 80;
server_name yourdomain.com www.yourdomain.com;
# ...
location ~ \.php$ {
set $memcached_key "yourdomain:$request_uri";
memcached_pass 127.0.0.1:11211;
default_type text/html;
error_page 404 405 502 = @cache_miss;
}
location @cache_miss {
fastcgi_pass 127.0.0.1:9002;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /var/www/yoursite/htdocs$fastcgi_script_name;
include /etc/nginx/fastcgi_params;
}
# ...
}
. . .
Here you can see @cache_miss will get called when nginx can't find anything in the memcache for the given key (the url). This triggers our fallback plan where the cache will fill itself with any missing data. When we do hit the cache, we actually bypass PHP altogether. No initialization wait, no slowness, and no database calls. By educated guess, nginx and memcached in this setup can start serving content in under 15 milliseconds. Compare this with the 50-100 milliseconds we commonly see.
Theoretically you could serve content faster using this system than just using files due to time spent waiting on disk access. In my proof of concept, I was able to serve about 2500 requests/second but I have reason to believe that limit was imposed by the network card. The CPU was at 10% the whole time and I'm sure it could handle more.
Happy caching!
(Downloadable Plugin Coming Soon)