scribble

Xujiajun Blog

About Blog Email GitHub

03 Jun 2014
HTTP Cache [第14天]

The nature of rich web applications means that they’re dynamic. No matter how efficient your application, each request will always contain more overhead than serving a static file.

And for most Web applications, that’s fine. Symfony2 is lightning fast, and unless you’re doing some serious heavy-lifting, each request will come back quickly without putting too much stress on your server.

But as your site grows, that overhead can become a problem. The processing that’s normally performed on every request should be done only once. This is exactly what caching aims to accomplish.

Caching on the Shoulders of Giants¶

The most effective way to improve performance of an application is to cache the full output of a page and then bypass the application entirely on each subsequent request. Of course, this isn’t always possible for highly dynamic websites, or is it? In this chapter, you’ll see how the Symfony2 cache system works and why this is the best possible approach.

The Symfony2 cache system is different because it relies on the simplicity and power of the HTTP cache as defined in the HTTP specification. Instead of reinventing a caching methodology, Symfony2 embraces the standard that defines basic communication on the Web. Once you understand the fundamental HTTP validation and expiration caching models, you’ll be ready to master the Symfony2 cache system.

For the purposes of learning how to cache with Symfony2, the subject is covered in four steps:

1.A gateway cache, or reverse proxy, is an independent layer that sits in front of your application. The reverse proxy caches responses as they’re returned from your application and answers requests with cached responses before they hit your application. Symfony2 provides its own reverse proxy, but any reverse proxy can be used.

2.HTTP cache headers are used to communicate with the gateway cache and any other caches between your application and the client. Symfony2 provides sensible defaults and a powerful interface for interacting with the cache headers.

3.HTTP expiration and validation are the two models used for determining whether cached content is fresh (can be reused from the cache) or stale (should be regenerated by the application).

4.Edge Side Includes (ESI) allow HTTP cache to be used to cache page fragments (even nested fragments) independently. With ESI, you can even cache an entire page for 60 minutes, but an embedded sidebar for only 5 minutes.

Since caching with HTTP isn’t unique to Symfony, many articles already exist on the topic. If you’re new to HTTP caching, Ryan Tomayko’s article Things Caches Do is highly recommended . Another in-depth resource is Mark Nottingham’s Cache Tutorial.

Caching with a Gateway Cache¶

When caching with HTTP, the cache is separated from your application entirely and sits between your application and the client making the request.

The job of the cache is to accept requests from the client and pass them back to your application. The cache will also receive responses back from your application and forward them on to the client. The cache is the “middle-man” of the request-response communication between the client and your application.

Along the way, the cache will store each response that is deemed “cacheable” (See Introduction to HTTP Caching). If the same resource is requested again, the cache sends the cached response to the client, ignoring your application entirely.

This type of cache is known as a HTTP gateway cache and many exist such as Varnish, Squid in reverse proxy mode, and the Symfony2 reverse proxy.

Types of Caches¶

But a gateway cache isn’t the only type of cache. In fact, the HTTP cache headers sent by your application are consumed and interpreted by up to three different types of caches:

Browser caches: Every browser comes with its own local cache that is mainly useful for when you hit “back” or for images and other assets. The browser cache is a private cache as cached resources aren’t shared with anyone else;

Proxy caches: A proxy is a shared cache as many people can be behind a single one. It’s usually installed by large corporations and ISPs to reduce latency and network traffic;

Gateway caches: Like a proxy, it’s also a shared cache but on the server side. Installed by network administrators, it makes websites more scalable, reliable and performant.

Tips:

Gateway caches are sometimes referred to as reverse proxy caches, surrogate caches, or even HTTP accelerators.

The significance of private versus shared caches will become more obvious when caching responses containing content that is specific to exactly one user (e.g. account information) is discussed.

Each response from your application will likely go through one or both of the first two cache types. These caches are outside of your control but follow the HTTP cache directions set in the response.

Symfony2 Reverse Proxy¶

Symfony2 comes with a reverse proxy (also called a gateway cache) written in PHP. Enable it and cacheable responses from your application will start to be cached right away. Installing it is just as easy. Each new Symfony2 application comes with a pre-configured caching kernel (AppCache) that wraps the default one (AppKernel). The caching Kernel is the reverse proxy.

To enable caching, modify the code of a front controller to use the caching kernel:

<?php 
// web/app.php
require_once __DIR__.'/../app/bootstrap.php.cache';
require_once __DIR__.'/../app/AppKernel.php';
require_once __DIR__.'/../app/AppCache.php';

use Symfony\Component\HttpFoundation\Request;

$kernel = new AppKernel('prod', false);
$kernel->loadClassCache();
// wrap the default AppKernel with the AppCache one
$kernel = new AppCache($kernel);
$request = Request::createFromGlobals();
$response = $kernel->handle($request);
$response->send();
$kernel->terminate($request, $response);
?>

The caching kernel will immediately act as a reverse proxy - caching responses from your application and returning them to the client.

Tips:

The cache kernel has a special getLog() method that returns a string representation of what happened in the cache layer. In the development environment, use it to debug and validate your cache strategy:

error_log($kernel->getLog());

The AppCache object has a sensible default configuration, but it can be finely tuned via a set of options you can set by overriding the getOptions() method:

<?php
	// app/AppCache.php
	use Symfony\Bundle\FrameworkBundle\HttpCache\HttpCache;

	class AppCache extends HttpCache
	{
	    protected function getOptions()
	    {
	        return array(
	            'debug'                  => false,
	            'default_ttl'            => 0,
	            'private_headers'        => array('Authorization', 'Cookie'),
	            'allow_reload'           => false,
	            'allow_revalidate'       => false,
	            'stale_while_revalidate' => 2,
	            'stale_if_error'         => 60,
	        );
	    }
	}
?>

Tips:

Unless overridden in getOptions(), the debug option will be set to automatically be the debug value of the wrapped AppKernel.

Here is a list of the main options:

default_ttl: The number of seconds that a cache entry should be considered fresh when no explicit freshness information is provided in a response. Explicit Cache-Control or Expires headers override this value (default: 0);

private_headers: Set of request headers that trigger “private” Cache-Control behavior on responses that don’t explicitly state whether the response is public or private via a Cache-Control directive. (default: Authorization and Cookie); allow_reload: Specifies whether the client can force a cache reload by including a Cache-Control “no-cache” directive in the request. Set it to true for compliance with RFC 2616 (default: false);

allow_revalidate: Specifies whether the client can force a cache revalidate by including a Cache-Control “max-age=0” directive in the request. Set it to true for compliance with RFC 2616 (default: false);

stale_while_revalidate: Specifies the default number of seconds (the granularity is the second as the Response TTL precision is a second) during which the cache can immediately return a stale response while it revalidates it in the background (default: 2); this setting is overridden by the stale-while-revalidate HTTP Cache-Control extension (see RFC 5861);

stale_if_error: Specifies the default number of seconds (the granularity is the second) during which the cache can serve a stale response when an error is encountered (default: 60). This setting is overridden by the stale-if-error HTTP Cache-Control extension (see RFC 5861).

If debug is true, Symfony2 automatically adds a X-Symfony-Cache header to the response containing useful information about cache hits and misses.

plus:

Changing from one Reverse Proxy to another The Symfony2 reverse proxy is a great tool to use when developing your website or when you deploy your website to a shared host where you cannot install anything beyond PHP code. But being written in PHP, it cannot be as fast as a proxy written in C. That’s why it is highly recommended you use Varnish or Squid on your production servers if possible. The good news is that the switch from one proxy server to another is easy and transparent as no code modification is needed in your application. Start easy with the Symfony2 reverse proxy and upgrade later to Varnish when your traffic increases. For more information on using Varnish with Symfony2, see the How to use Varnish cookbook chapter.

Tips:

The performance of the Symfony2 reverse proxy is independent of the complexity of the application. That’s because the application kernel is only booted when the request needs to be forwarded to it.


Til next time,
Xujiajun at 08:36

scribble

About Blog Email GitHub