HTTP cache


DiffThis article is derived from BOOK and is different from the official existing documents. This article explains it in more depth and detail in some places. Therefore, we did not force synchronization with the official.

The natural attribute of rich web applications is that they are dynamic. No matter how efficient your program is, each request will always bear a far greater overhead than static files.

And more web programs have not been greatly affected. Symfony is lightning fast, and unless you're doing something super heavy-duty, every request is restored quickly without putting too much stress on the server.

But your site is growing, and overload may become a problem. Processing of typical requests should only be done once. And that's exactly the goal of cache locking.

Caching on the Shoulders of Giants

The most effective way to improve the performance of a program is to cache the entire output of the page and then ignore the entire subsequent request. Of course, for highly dynamic websites, this may not always be the case. In this chapter, you'll learn how Symfony's caching system works and why it's the best solution.

The Symfony caching system is different because it relies on the simplicity and power of the HTTP cache defined by HTTP specification. Rather than reinventing a caching approach, Symfony emphasizes standards that define basic communications on the web. Once you master the basics of "HTTP validation" and "expiration of cached models", you can already master Symfony's caching system.

The process of learning Symfony caching can be divided into four steps:

  1. gateway cache (gateway cache) , or reverse proxy (reverse proxy), which is a separate layer in front of your program. A reverse proxy caches responses as they are returned by your program; it can also respond to requests with cached responses before the request reaches your program. Symfony provides its own reverse proxy, but any reverse proxy will work.

  2. HTTP cacheHTTP cache headers are used between your program and the client to communicate with gateway caches or other caches. Symfony provides reasonable default configuration and a powerful interface for interacting with cache headers.

  3. HTTPExpiration and validation (expiration and validation), these two models are used to determine whether the cached content is fresh/fresh( Can be reused from cache), or whether stale/stale (should be regenerated by the program)

  4. Edge Side Includes (ESI) , edge-side inclusion allows HTTP cache to be used for independent caching of parts of the page (even nested fragments). With the help of ESI, you can even "cache the entire page for 60 minutes, but the sidebar for only 5 minutes."

Since HTTP cache is not exclusive to Symfony, there are many related articles. If you are not familiar with HTTP caching, I highly recommend reading Things Caches Do by Ryan Tomayko. Another good in-depth article is Mark Nottingham's Cache Tutorial.

Using Gateway Cache

When using HTTP caching, cache is completely separate from your program. It resides between your program and the server that initiates the request. between clients.

The task of caching is to receive client requests, then pass them back to your program, and then push them back to the client. The cache here is the "middleman" in the "request-response" communication process between the program and the browser.

Over time, these caches will store every response that is considered "cacheable" (see HTTP Caching Introduction). If the same resource is requested again, the cache will send the cached response to the client, completely ignoring your application.

This type of cache is the HTTP gateway cache, which exists in programs such as Varnish, Squid and Symfony's reverse proxy mode among agents.

Cache Type

But Gateway cache is not the only cache type. In fact, the HTTP cache headers sent by your program are assumed to be interpreted by up to three ways of caching:

  • Browser caches : Each browser has its own local cache built in, used when you click "Back", or for images and other assets. The browser cache is a private (private) cache, because the cached resources cannot be used by others;

  • Proxy caches : Proxy refers to a shared (shared) cache because many people can follow (to use) one person. Usually used by large companies or ISPs to reduce access latency and network traffic.

  • Gateway caches: Similar to a proxy, it is also a shared cache, but on the server side. Often used by network administrators to make websites easier to upgrade, more reliable, and more performant.

Gateway caches are sometimes referred to as reverse proxy caches, surrogate caches (proxy caches), and even HTTP accelerators.

#When the situation where the cached response contains content for a specific user (such as account information) is discussed, private (private) Caching and Sharing (shared) The importance of caching is increasing day by day.

Every response of the program will experience one or both of the first two cache types. These caches are outside of your (program) control, but obey the HTTP caching instructions set in the response.

Symfony Reverse Proxy

Symfony has a built-in reverse proxy (also called gateway cache) written in PHP. It is not a full-featured reverse proxy cache like Varnish, but it is a good start.

For more details on setting up Varnish, see How to Speed ​​Up My Website with Varnish.

It is easy to turn on the proxy: Symfony programs all have a pre-built cache kernel cache core (AppCache), which uses the default core (AppKernel) Pack it. This cache core is a reverse proxy.

Turning on caching is easy, just modify your front controller code. You can also make these changes in app_dev.php to add a cache for the dev environment:

// web/app.phpuse Symfony\Component\HttpFoundation\Request;
// ...$kernel = new AppKernel('prod', false);$kernel->loadClassCache(); 
// add (or uncomment) this new line! / 添加下面新行!
// wrap the default AppKernel with the AppCache one
// 用AppCache打包默认的AppKernel$kernel = new AppCache($kernel); $request = Request::createFromGlobals(); $response = $kernel->handle($request);$response->send(); $kernel->terminate($request, $response);

The cache core above will immediately serve as a reverse A proxy works by caching responses from your application and returning them to the client.

If you are using the framework.http_method_override option to read the HTTP method from the _method parameter, refer to the link above to adjust it for you degree of need.

The cache core has a special getLog() method, which returns a string to indicate what happened in the cache layer . In a development environment, you can use it to debug or verify your caching strategy.

The

AppCache object has a suitable default configuration, but by overriding the getOptions() method To set a set of options, the object can be fine-tuned.

// app/AppCache.phpuse 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,
        );
    }}


Unless overridden in the getOptions() method, the debub option will be automatically set to The debug value in "stripped AppKernel".


Here are some of the main options:

default_ttl

The value is in seconds, expressing When no explicit freshness information is provided in the response, a cache entry is considered fresh for how long. This value can be overridden by explicitly specifying the Cache-Control or Expires header (default is 0).


private_headers

A set of request headers, used without "pass the Cache-Control directive (default is Authorization and Cookie) trigger the "private" Cache in a response that explicitly states whether the current response is public or private status" -Control behavior.


allow_reload

Specifies whether the client can include a Cache-Control "no" in the request -cache" directive to force a cache reload. Set to true to comply with RFC2616 (default is false).


allow_revalidate

Specifies whether the client can include a " to Cache-Control in the request max-age=0" to force revalidation. Set to true to comply with RFC2616 (default is false).


stale_while_revalidate

The default number of seconds specified (the interval is in seconds because the TTL accuracy of Response is seconds), here During this time, although the cache is re-validating the response in the background, it can immediately return a stale response (default value is 2); this setting can be changed by HTTPCache-Control Extended stale-while-revalidate override (see RFC 5861).


stale_if_error

Specifies the default number of seconds (interval in seconds) during which the cache can Response to provide service (default is 60). This setting can be overridden by the stale-if-error of the HTTPCache-Control extension (see RFC 5861).


If debug is set to true, Symfony will automatically add a X-Symfony-Cache header to the response, inside There is useful information about cache hits and misses.

Switching from one reverse proxy to another

When developing a website, or during the process of deploying a website to a shared hosting where "nothing but php code can be installed" , Symfony's reverse proxy is an extremely useful tool. But since it is written in PHP, it is not as fast as the proxy written in C. This is why it is highly recommended that you use Vanish or Squid on production servers whenever possible. The good news is that switching from one proxy server to another is easy and transparent because there is no code in your program to modify. You can use Symfony reverse proxy with peace of mind, and you can upgrade to Varnish at any time when your traffic increases in the future.

The performance of Symfony reverse proxy is independent of the complexity of the program. This is because the program kernel is only started when a request needs to be sent to it.

Make your response HTTP cache

To take advantage of the available caching layers, your program should communicate with the following information: 1 , which responses can be cached. 2. Rules that determine when/how the cache becomes stale.

Remember, "HTTP" is just a language (simple text) used by clients and servers to communicate with each other. HTTP caching is part of this language, allowing clients and servers to exchange information about caches.

HTTP specifies the following four caching headers for responses:

  • ##Cache-Control

  • Expires

  • ETag

  • Last -Modified

The most important and powerful one is the

Cache-Control header, which can be said to be a collection of various cache information.

Each header is explained in detail in the

HTTP Expiration, Validation and Invalidation sections.

Cache-Control header

Cache-ControlThe header is special, it contains more than one, but A lot of information related to the cacheability of the response. Each piece of information is separated by commas:

Cache-Control: private, max-age=0, must-revalidate
 
Cache-Control: max-age=3600, must-revalidate

Symfony provides an abstraction layer over the

Cache-Control header to make its creation more manageable:

// ... use Symfony\Component\HttpFoundation\Response; $response = new Response();
// mark the response as either public or private 标记响应是公有还是私有$response->setPublic();$response->setPrivate();
// set the private or shared max age 设置私有或公有的最大周期$response->setMaxAge(600);$response->setSharedMaxAge(600); 
// set a custom Cache-Control directive 设置一个自定义Cache-Control命令$response->headers->addCacheControlDirective('must-revalidate', true)

If you want to set cache headers for different actions in the controller, you may want to take a look at

FOSHttpCacheBundle. It provides a way to define cache headers based on URL pattern matching and other request attributes.

Public response and Private response

Whether it is gateway or proxy cache, it is considered a "shared" shared cache, because the cache content is shared by more users. If a "user-specific" response is mistakenly placed in a shared cache, it may be returned to multiple different users at a later time. Just imagine what it would be like if your account information was cached and then sent to all subsequent users who requested their account page!

To deal with this situation, each response should be set to public or private:

public

indicates the response Should be cached as both public and private caches.


private

Indicates that all or part of the response information is only for a certain user, so the cache is prohibited from being public cached.


Symfony conservatively sets each response to private. In order to take advantage of a shared cache (such as a Symfony reverse proxy), the response must be explicitly set to public.

Safe Method

HTTP caching only works under "safe" HTTP methods (such as GET or HEAD). The so-called security means that you can never change the program state on the server when providing services to requests (such as logging, processing cache information, etc.). This leads to two extremely persuasive and important conclusions:

  • You should never change program state in response to a GET or HEAD request. Even if you don't use gateway cache, the essence of proxy caching is that any GET or HEAD request may or may not actually hit your server;

  • Do not expect any requests to PUT, POST or DELETE method for caching. These methods are meant to be used when the state of your application changes (such as deleting a blog post). Caching them will prevent specific requests from hitting or changing your program.

Cache rules and default settings

HTTP1.1 allows any content to be cached by default unless Cache-Control is explicitly specified head. In practice, most caches do nothing when a request contains a cookie, when an authorization header is included, when a non-safe method is used (such as PUT, POST, or DELETE), or when the response has a redirect status code.

When the developer sets nothing in the response header, Symfony automatically sets a meaningful and conservative Cache-Header header according to the following rules.

  • If no cache header is defined (Cache-Control, Expires, ETag or Last -Modified), Cache-Control will be set to no-cache, which means the response will not be cached;

  • if Cache-Control is empty (but another cache header is set), its value will be set to private, must-revalidate;

  • But if at least one Cache-Control directive is set and no public or private directive is explicitly added , Symfony will automatically add private directives (except when s-maxage is set)

HTTP Expiration, Validation and Invalidation

HTTP protocol defines two caching models:

  • Utilizing expiration model (expiration model), through inclusion The Cache-Control header and/or the Expires header can directly specify how long a response should be considered "fresh". The cache understands the expiration time and no longer makes the same request until the cached version reaches the expiration time and becomes "stale".

  • When the page is truly dynamic (the presentation layer changes frequently), the use of validation model is very necessary. With this model, the cache stores the response but "questions" the server on each request - is the cached response still valid? The program uses an independent response identifier (i.e., Etag header) and/or a timestamp (i.e., Last-Modified header) to check whether the current page has changed since it was cached. Changes have occurred.

Understanding the HTTP Specification

The HTTP specification defines a simple but powerful language that enables clients to Communicate with the server. As a web developer, the request-response model owned by the HTTP specification will govern your caching efforts. Unfortunately, the actual documentation for the HTTP protocol, RFC2616, is difficult to read.

But there is an ongoing HTTP Bis to overwrite RFC 2616. It does not describe the new version of HTTP, but more of a cleanup of the original HTTP protocol. The organization of the document has also improved with the HTTP protocol being divided into seven parts; each part of HTTP caching can be found in two separate chapters (P4 - Conditional Requests and P6 - Caching: Browser and intermediary caches).

As a web developer, you are strongly urged by our official Symfony team to read about the HTTP protocol. It's so clear and powerful that - even 10 years after it was created - the HTTP Specification is priceless. We would like to remind you not to take the appearance of these agreements lightly - their contents are hundreds of thousands of times more beautiful than their covers.

Expiration (Expiration)

expiration model is the more efficient and direct one of the two caching models, so it should be used as much as possible. When a response is cached via expiration, the cache will save the response and return it directly before expiration without hitting the program.

The expiration model can be implemented through one of the following two almost identical HTTP headers: Expires or Cache-Control.

Use the Expires header to control expiration

According to the HTTP specification, the "Expires header field will give the date/after the response is considered stale" time." The Expires header here can be set to the Response method: setExpires(). It uses a DateTime instance as a parameter:

$date = new DateTime();
$date->modify('+600 seconds'); 
$response->setExpires($date);

The HTTP header information of the response is similar to this:

Expires: Thu, 01 Mar 2011 16:00:00 GMT

setExpires()The method will automatically convert The date is in GMT time zone as this is a requirement of the HTTP specification.

Note that prior to HTTP version 1.1, the origin server was not required to send the Date header. Therefore, caches (such as browsers) require a local clock to evaluate the Expires header, making cache period calculations vulnerable to time skew. Another Expires header restriction is that, as described in the HTTP protocol, "HTTP/1.1 MUST NOT send Expires with a date more than one year old."

Using Cache- Control header controls expiration

Because of the limitations of the Expires header, in most cases, you should use the Cache-Control header instead. Remember, the Cache-Control header is used for a variety of different caching directives. For example, max-age and s-maxage. The first one is used for all caches, while the second one is only used when the cache is shared.

// Sets the number of seconds after which the response
// should no longer be considered fresh// 设置“响应过期”的秒数$response->setMaxAge(600);
 // Same as above but only for shared caches
// 同上,但仅用于共享缓存$response->setSharedMaxAge(600);

Cache-ControlThe header is generally in the following format (but sometimes there are other instructions):

##
1
error_log($kernel->getLog());
1
Cache-Control: max-age=600, s-maxage=600

Expiration and Validation (Expiration and Validation)

Of course you can use validation and expiration at the same time for the same Response. Because the advantages of expiration outweigh validation, you can easily benefit from the best of both worlds. That is, using expiration and validation together, you can instruct the cache to serve the cached content, while also checking backwards at certain intervals (expiration) to confirm that the cached content is still valid.

You can also define HTTP cache headers for expiration and validation through annotation. Refer to the FrameworkExtraBundle documentation.

More Response methods

The Response class provides many methods to deal with caching. Here are a few that are particularly useful:

// Marks the Response stale 标记响应过期$response->expire(); 
// Force the response to return a proper 304 response with no content
// 强制响应返回一个没有内容的恰当的304响应$response->setNotModified();

In addition, most cache-related HTTP headers can be set individually using the setCache() method:

// Set cache settings in one call$response->setCache(array(
    'etag'          => $etag,
    'last_modified' => $date,
    'max_age'       => 10,
    's_maxage'      => 10,
    'public'        => true,
    // 'private'    => true,
    ));

Summary

The design idea of ​​Symfony is to follow the industry-recognized standard: HTTP. Caching functionality is no exception. Mastering Symfony's caching system means that you are familiar with the HTTP cache model and can use it efficiently. In other words, you can explore the world of HTTP caching and gateway caches represented by Varnish without relying on Symfony documentation and routines.