This article mainly introduces the use of HTTPCache in the Symfony2 framework, and analyzes the use skills related to the HTTP Cache of the Symfony framework in combination with the instance form. If you need it, refer to the example in this article to describe the HTTP Cache Usage of the Symfony2 framework. We will share this with you for your reference. The details are as follows:
The essence of rich web applications means their dynamics. No matter how efficient your application is, each request will be much more expensive than a static file. For most web programs, this is nothing. Symfony2 is very brisk. No matter you make some serious overload requests, each request will get a quick response without putting pressure on your server. However, as your site grows, load will become a serious problem. Each request should be processed only once. This is what the cache really wants to achieve.
Standing on the shoulders of giants:
The most effective way to improve the execution efficiency of an application is to cache all the output of a page and then let subsequent requests bypass the entire application. Of course, this is not always possible for highly dynamic sites. The Symfony2 cache system is special because it relies on simple and powerful HTTP cache defined in the HTTP specification. Without re-inventing the cache method, Symfony2 embraces the standards for defining basic communication on the web. Once you understand the basic HTTP checksum and expiration cache modes, you will have full control over the Symfony2 cache system.
Step 1: A gateway cache or reverse proxy. It is an opposite layer sitting in front of your application. The reverse proxy cache comes from the response of your application and uses these cache responses to reply to some requests before they reach your application. Symfony2 provides its own reverse proxy, or you can use any other reverse proxy.
Step 2: the HTTP cache header is used to communicate with the gateway cache and any other cache communication between the client and your application. Symfony2 provides a preset behavior and powerful interface for interacting with the cache header.
Step 3: HTTP timeout and verification are used to determine whether the cache content is fresh and old.
Step 4: ESI allows the HTTP cache to be used to independently cache page segments (or even nested segments ). With ESI, you can even cache a complete page for 60 minutes. However, an embedded sidebar is only cached for 5 minutes.
Use gateway Cache
When HTTP cache is used, the cache is completely separated from your application. It is located between your request client and the application. The Caching job is to receive requests from the client and pass them back to your application. At the same time, it will receive the response from your application and forward it to the client. It can be said that it is the intermediary between your application and the request client for request-response interaction.
In this way, the cache will save every response that is considered "cacheable. When the same request is sent again, the cache will directly return its cached response to the request client, ignoring your application. This type of cache is the HTTP gateway cache. There are many such caches, such as Varnish, Squid in reverse proxy mode, and Symfony2 reverse proxy.
Cache type
A gateway cache is not the only cache type. In fact, there are three different types of cache that will intercept and use the HTTP cache headers sent by your application. They are:
Browser caches: the Browser has its own local cache, which plays a major role when you click "previous step" or view images and other network assets.
Proxy caches: A Proxy cache is a shared cache with multiple people located after one person. Most of them are installed by large companies or ISPs to reduce latency and network congestion.
Gateway caches: Like a proxy, it is also a shared cache but is located on the server side. They are usually installed by network administrators, which makes the website more scalable, reliable, and efficient. Gateway caches are sometimes called reverse proxy caches, proxy caches, or HTTP accelerators.
Symfony2 reverse proxy
Symfony2 has a reverse proxy (also called Gateway cache) written in PHP ). When it is enabled, the buffered response from your application will begin to be cached immediately. It is also easy to install. Each new Symfony2 application has a pre-configured cache kernel (AppCache) that contains a default AppKernel. The cache kernel is a reverse proxy. To enable caching, modify the front-end controller code to use the cache kernel:
// Web/app. phprequire_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 (); // use AppCache to wrap the default AppKernel $ kernel = new AppCache ($ kernel); $ kernel-> handle (Request: createFromGlobale ()-> send ();
The cache kernel immediately plays the role of a reverse proxy, caching replies from your application to send them back to the request client.
Note that the cache kernel has a special getLog () method that returns a string that can indicate what happened on the cache layer.
You can debug and verify your cache policies in the development environment.
error_log($kernel->getLog());
The AppCache object is a reasonable default configuration. You can also set the option to optimize it by rewriting the getOptions () method.
// 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, ); }}
Note: No matter how you override the getOptions () method, the debug option is automatically set for the debug value of the wrapped AppKernel.
Below are some important options:
Default_ttl: when no explicit refresh information is provided in the reply, a buffer object should be considered as a fresh time in seconds. Explicitly set the Cache-Control or Expires header to overwrite this parameter value. The default value is 0.
Private_headers: The request header group that triggers the "private" Cache-Control action on the response, whether the response is explicitly declared as public or private through the Cache-Control command. The default values are Authorization and Cookie.
Allow_reload: Specifies whether to allow the client to force the Cache to be reloaded by specifying the "no-cache" command of Cache-Control in the request. When it is set to true, the RFC2616 specification is met. The default value is false.
Allow_revalidate: Specifies whether to allow the client to force the Cache to re-verify by specifying the "max-age = 0" command of Cache-Control in the request. When it is set to true, the RFC2616 specification is met. The default value is false.
Stale_while_revalidate: used to specify a default number of seconds (the interval is seconds because the response TTL precision is 1 second ), during this period, when the cache is re-verified in the background, an old response can be immediately returned (2 by default ); this setting will be overwritten by the stale-while-revalidate HTTP Cache-Control extension (RFC 5861 ).
Stale_if_error: Specifies a default number of seconds (interval is second) during which cache can provide an old response when an error occurs. The default value is 60. This setting will be overwritten by stale-if-error HTTP Cache-Contorl extension (RFC5861)
If debug is set to true, Symfony2 automatically adds an X-Symfony-Cache header to the reply, saving information about Cache clicks and loss.
Conversion from one reverse proxy to another:
Symfony2 reverse proxy is a very useful tool when developing your site or deploying your site to a shared host and you cannot install anything other than PHP code. However, because PHP is used for writing, it cannot be as fast as the reverse proxy written in C. This is why we recommend Varnish or Squid to your operating server. The good news is that it is easy to replace a proxy server with another one. You do not need to modify any program code. You can use the reverse proxy of Symfony2 at the beginning to upgrade to Varnish when blocking increases.
Note: The execution efficiency of Symfony2 reverse proxy is independent of the complexity of the application. Because the application core is started only when the request needs to be forwarded to it.
HTTP cache description:
To take advantage of the available cache layer, your application must be able to express which response can be cached, and when/When the cache will become obsolete rules. These are implemented by setting the HTTP cache header on the response.
Remember, "HTTP" is just a simple text language for communication between web clients and servers. When we talk about the HTTP cache, we talk about the cache that allows the client to exchange information with the server.
HTTP specifies four Response cache headers. we need to pay attention to the following:
Cache-Control
Expires
ETag
Last-Modified
The most important and omnipotent header is the Cache-Control header, which is actually a collection of various Cache information.
Cache-Control Header
The Cache-Control header is the only one that contains a variety of information about whether a response can be cached. Each piece of information is separated by commas.
Cache-Control:private,max-age=0,must-revalidateCache-Control:max-age=3600,must-revalidate
Symfony provides a Cache-Control header abstraction to make its creation more controllable.
$ Response = new Response (); // mark whether response is public or private $ response-> setPublic (); $ response-> setPrivate (); // set the maximum age of private or shared resources $ response-> setMaxAge (600); $ response-> setSharedMaxAge (600 ); // set a custom Cache-Control command $ response-> headers-> addCacheControlDirective ('must-revalidate', true );
Public vs private Response
Both the gateway cache and the proxy cache are considered as "shared" caches because the cached content is shared by multiple users. If a user's reply has been mistakenly stored in the shared cache, it may be returned to countless users in the future. Imagine if your account information is cached and then returned to every user who subsequently requests their own account page. To handle this situation, each reply may be set to public or private.
Public indicates that the reply may be saved in private and shared cache.
Private indicates that all or part of the reply information is sent to an individual user, so it cannot be cached in the shared cache.
Symfony cautiously defaults each reply to private. To use the advantages of shared cache (such as Symfony2 reverse proxy), the reply must be explicitly set to public.
Security method:
The HTTP cache only works for secure methods (such as GET and HEAD ). Security means that when it serves a request, it never changes the status of the application on the server. (Of course, you can write log information, cache data, and so on ). There are two reasonable consequences (consequences ):
When your application replies with a GET or HEAD request, you will never change the status of your application. Even if you do not use the gateway cache, the existence of the proxy cache means that any GET and HEAD requests may or may not actually reach your server.
Do not expect the PUT, POST, or DELETE methods to be cached. These methods are used to change the state of your application. Caching them will block certain requests from accessing or changing your applications.
Cache rules and default settings
HTTP 1.1 allows caching of everything by default unless there is an explicit Cache-Control header. In practice, most caches use an insecure method (such as PUT, POST, or DELETE) When a request has a cookie or Authorization header, or when a request has a redirection code, no cache activity is performed.
When the developer does not make any settings, Symfony2 automatically sets a reasonable and conservative Cache-Control header according to the following rules:
If no Cache header is defined (Cache-Control, Expires, ETag or Last-Modified), the cache-Control is set to no-Cache, meaning the response will not be cached.
If Cache-Control is empty (but another Cache header exists), its value is set to private, must-revalidate;
If at least one Cache-Control command is set and no 'public' or 'private' command is explicitly added, symfony2 automatically adds a private command (except when s-maxage is set ).
HTTP expiration and Verification
The HTTP specification defines two cache models:
Expiration model. You only need to specify how long a Response should be considered by including a Cache-Control and/or an Expires header. The cache understands that expiration will no longer allow the same request to reply until the cached version expires and becomes "stale" old.
Verification Model: When pages are real dynamic pages (their presentation changes frequently), verification models are often needed. This model caches response, but requires the service to verify whether the response is cached for each request.
The application uses the unique response identifier (ETag header) and/or timestamp (Last-Modified header) to check whether the page has changed since it was cached.
The goal of these two models is to rely on a cache to store and return "fresh" response, so that the application never generates the same response twice.
Expiration:
The expiration model is a more effective and simple model among the two models. It should be used at any time. When a response is cached with an expiration method, the cache will store the response and directly return it for the request without accessing the application until it expires.
The expiration model can be skillfully used with one or two identical HTTP headers, such as Expires or cache-control.
Expired and Expires Headers
According to the HTTP specification, the Expires header field provides a date/time. After the date or time Expires, its response is regarded as obsolete. The Expires header can be set by the setExpires () method of Response. It requires a DateTime instance as the input parameter.
$date = new DateTime();$date->modify('+600 seconds');$response->setExpires($date);
The result of the generated HTTP header is as follows:
Expires: Thu, 01 Mar 2011 16:00:00 GMT
Note that the setExprise () method will automatically convert the date to the GMT time zone as required by the standard.
Before HTTP standard version 1.1, the source service does not need to send a Date header. Therefore, the cache (such as the browser) may need to always evaluate the Expires header locally, resulting in Clock Deviation during the life cycle calculation. Another restriction of the Expires header is the specification: "HTTP/1.1 Service should not send the Expires date more than one year in the future. "
Expires and Cache-Control headers
Because of the limitations of the Expires header, you should use the Cache-Control header to replace it most of the time. Recall that the Cache-Control header is used to specify multiple different Cache commands. For expiration, there are two Commands: max-age and s-maxage. The first one is used by all the caches, but the second one is only used for sharing the cache.
// Set a number of seconds. After this number of seconds, the response is regarded as obsolete. $ Response-> setMaxAge (600); // same as above, but only used for shared cache. $ Response-> setSharedMaxAge (600); the Cache-Control header uses the following format (it may have other commands): Cache-Control: max-age = 600, s-maxage = 600
Verification:
Once the underlying data changes, the cache resources need to be updated immediately, and the expiration model is insufficient. In the expiration model, the application is not required to return the updated response until the last expiration of the cache changes to obsolete content.
The validation model solves this problem. In the validation model, the cache keeps storing response. The difference is that, for each request, the cache will ask the application to check whether the cached response is still valid. If the cache is still valid, your application should return a 304 status code and an empty content. This tells the cache that it can return the cached response for the requesting user.
In this model, you mainly Save the bandwidth because the description will not send twice to the same client (Instead, send a 304 reply ). However, if you carefully design your application, you may be able to endure the minimum data required by 304 response and save the CPU.
The 304 Status Code indicates that it has not been modified. It is very important because it does not contain the whole requested content, but is just a lightweight guiding set, it tells the cache that it should use the version it saves to reply to the request. Similar to expiration, two different HTTP headers can be used to implement verification models: ETag and Last-Modifed.
Checksum ETag Header
The ETag header is a string (also called "entity-tag") that uniquely identifies a target resource. It is completely generated and set by your application. For example, if the/about resource is cached and saved, it depends on the date and the returned content of your application. An ETag is like a fingerprint and is used to quickly compare whether two different versions of a resource are equivalent.
Like a fingerprint, each ETag must be unique in all representations of the same resource. Let's simply implement a message that generates ETag and uses md5 encryption as the content:
public function indexAction(){ $response = $this->render('MyBundle:Main:index.html.twig'); $response->setETag(md5($response->getContent())); $response->isNotModified($this->getRequest()); return $response;}
The Response: isNotModified () method compares the ETag sent along with the Request with the ETag on the Response. If the two match, the method automatically sets the Response status code to 304.
This algorithm is simple and common, but you need to create a complete Response before being able to calculate ETag. The validation model is a sub-optimal choice. In other words, it saves bandwidth and does not save CPU usage.
Symfony2 also supports weak ETag by passing true to the setETag () method as the second parameter.
Checksum and Last-Modified Header
The Last-Modified header is the second form of validation model. According to the HTTP specification, the "Last-Modified header field specifies the date and time. At this time, the source server believes that this is the Last Modified version. "
In other words, the application determines whether the cached content is updated based on the automatic cache content. For example, you can use the latest update date as the value of the Last-Modified header for all objects that need to calculate resource performance:
public function showAction($articleSlug){ //... $articleDate = new \DateTime($article->getUdateAt()); $authorDate = new \DateTime($author->getUpdateAt());\ $date = $authorDate>$articleDate ? $authorDate : $articleDate; $response->setLastModified($date); $response->isNotModified($this->getRequest()); return $response;}
The Response: isNotModified () method compares the If-Modified-Since Header in the Request with the Last-Modified header in the Response. If they are equal, Response will be set with a 304 status code.
Note that the If-Modified-since request header is the Last-Modified header that is finally sent to a specific client resource. This is how the client communicates with the server to determine whether the resource has been updated since it is cached.
Use verification to optimize your code:
The main purpose of any cache policy is to reduce application loading. In other words, the less your application does, the better it returns 304 response. The Response: isNotModified () method is implemented by exposing a simple and effective mode.
Public funcation showAction ($ articleSlug) {// obtain the minimum information to calculate the ETag or Last-Modified value (based on the Request, the data is obtained from the database or a key-Value Pair storage instance. $ Article = //... // create a Response with an ETag and/or a Last-Modified header $ response = new Response (); $ response-> setETag ($ article-> computeETag ()); $ response-> setLastModified ($ article-> getPublishedAt ()); // check if Response is not modified for the given Request if ($ response-> isNotModified ($ this-> getRequest () {// immediately return 304 Response return $ response ;} else {// do more work-for example, get more data $ comment = //... // use the $ response you have enabled to render a template return $ this-> render ('mybundle: MyController: article.html. twig ', array ('Article' => $ article, 'comments' => $ comments), $ response );}}
After the Response is not modified, isNotModified () automatically sets the response status code to 304 to remove the response content and remove some headers that do not need to be 304.
Different responses:
So far, we have assumed that each URI has only one representation of the target resource. By default, HTTP cache is executed by using URI resources as the cache key. If two people request the same cache resource URI, the second user will obtain the Cache version. Sometimes these are not enough. Different versions of a URI must be cached according to the value of one or more request headers. For example, if the client supports page compression, any given URI has two types: one is that the client supports compression, and the other is not. The Accept-Encoding value of the Request Header determines which one to use.
In this case, a compressed version and a non-compressed version are cached for the specific URI to be replied, and they are returned based on the request's Accept-Encoding value. This is through the Vary Response Header, Vary is a different header separated by commas, and its value triggers different representations of the Request resource.
Vary:Accept-Encoding,User-Agent
Note that this special Vary header will be cached for different versions of each resource based on the URI, Accept-Encoding, and User-Agent request headers.
The Response object provides a clean interface to manage the Vary header:
// Set a vary header $ response-> setVary ('Accept-Encoding '); // set multiple vary headers $ response-> setVary (array ('Accept-encoding ', 'User-agent '));
The setVary () method requires a header name or an array of header names to correspond to different response.
Expiration and verification:
Of course, you can also use the checksum and expiration in the same Response. Because expiration is better than verification, you can easily choose between them based on the benefits. In other words, by using both expiration and verification, You can instruct the cached content and check the background interval to check whether the content is still valid.
More Response methods:
The Response class provides many caching-related methods. Below are some of the main features:
// Indicates that the Response expires. $ response-> expire (); // force the response to return a response suitable for 304 with no content $ response-> setNotModified ();
In addition, the HTTP header most relevant to the cache can be set through a separate method setCache.
// Set the cache parameter $ response-> setCache (array ('etag' => $ etag, 'last _ modified' => $ date, 'max _ age' => 10,'s _ maxage' => 10, 'public' => true, // 'private' => true ,));
Use ESI (Edge Side encryption DES)
Gateway cache is a good way to improve the efficiency of your website. However, they have a limit that only the entire page can be cached. If you don't want to cache the entire page or a part of the page that is dynamic, you're not so lucky.
Fortunately, Symfony2 provides a solution for these situations based on ESI technology. It allows the specified part of the page to have a different cache policy than the home page.
You can embed the ESI Specification Description label on your page to communicate with the gateway cache. Symfony2 only implements one label, including, because this is the only tag that can be used outside the Akami context.
Some content
More content
In this example, we noticed that each ESI tag has a fully qualified URL. An ESI tag indicates a page segment that can be obtained through a given URL.
When a request is processed, the gateway cache retrieves the entire page from its cache or from the response of the application. In other words, the gateway cache obtains the contained page fragments from both the cache and the Response Request page fragments from the application. After all the ESI labels are parsed, the gateway cache merges each ESI content to a home page and returns the final content to the client. All of this happens transparently at the gateway cache level (outside your program ). You will see that if you choose the ESI tag, Symfony2 makes the process containing them almost effortless.
Use ESI in Symfony2
First, you must confirm that your application configuration has been enabled when you use ESI.
YAML format:
# app/config/config.ymlframework: # ... esi: { enabled: true }
XML format:
PHP code format:
// app/config/config.php$container->loadFromExtension('framework', array( // ... 'esi' => array('enabled' => true),));
Now let's assume that we have a relatively static page, except for an automatic news reader at the bottom of the content. With ESI, We Can cache the automatic news receiving machine independent of other parts of the page.
public function indexAction(){ $response = $this->render('MyBundle:MyController:index.html.twig'); $response->setSharedMaxAge(600); return $response;}
In this example, the cache period for the entire page is 10 minutes. Next, embed an action to include the news ticker into the template. This is implemented through the help of render. Because the embedded content comes from other pages, Symfony2 uses a standard render help to configure the ESI Tag:
Twig format:
{% render '...:news' with {}, {'standalone': true} %}
PHP format:
<?php echo $view['actions']->render('...:news', array(), array('standalone' => true)) ?>
Set standalone to true to indicate that the action Symfony2 should be rendered as an ESI tag.
You may want to know why to use a helper method instead of writing the ESI tag directly. This is because using helper allows your application to work even if no gateway cache is installed. Let's see how it works.
When standalone is false (which is also the default value), Symfony2 merges the page content contained in the response to the client to a home page.
However, if standalone is true and Symfony2 finds that it is talking to the gateway cache that supports ESI, it generates an ESI include tag.
If no gateway cache is available or the gateway cache does not support ESI, Symfony2 merges only the contained tag page content to a major one, as it does when standalone is set to false.
Embedded actions can now specify their own cache rules, completely independent of the home page.
public function newsAction(){ //... $response->setShareMaxAge(60);}
With ESI, the entire page cache will be valid for 600 seconds, but the news build cache will only last 60 seconds.
An essential condition for ESI is that an embedded action can be accessed through a URL so that the gateway cache can be obtained independently from other parts of the page. Of course, an action cannot be accessed through a URL unless a route points to it. Symfony2 is responsible for this through a common route and controller.
In order for ESI to contain labels to work normally, you must define the _ internal route:
YAML format:
# app/config/routing.yml_internal: resource: "@FrameworkBundle/Resources/config/routing/internal.xml" prefix: /_internal
XML format:
<?xml version="1.0" encoding="UTF-8" ?>
PHP code format:
// app/config/routing.phpuse Symfony\Component\Routing\RouteCollection;use Symfony\Component\Routing\Route;$collection->addCollection($loader->import('@FrameworkBundle/Resources/config/routing/internal.xml', '/_internal'));return $collection;
Because the route allows all actions to be accessed through a URL, you can use the Symfony2 firewall (allow access to the IP range of your reverse proxy) to protect it.
One of the major advantages of the cache policy is that you can allow your applications to minimize the impact on the Application Based on Dynamic needs.
Once you start using ESI, remember to use the s-maxage command instead of max-age. Because the browser only accepts aggregated resources and does not know sub-components, it caches the entire page according to the max-age command. This is what you don't want it to do.
Two useful options supported by render helper:
Alt: Used as the alt attribute of the ESI tag. When src cannot be found, it allows you to specify an alternative URL.
Ignore_errors: if it is set to true, an onerror attribute will be added to ESI and the attribute value is set to continue. In a failure event, the gateway cache will only silently remove the ESI tag.
Cache failure:
"There are two major challenges in Computer Science: cache invalidation and naming things" --- Phil Karlton
You never need to cache invalid data, because it has long been taken into account in the HTTP cache model. If you use verification, you never need to validate anything through definition. If you use expiration and expire a resource, it means you set a future expiration date. Failure in any type of reverse proxy is a top-level specification. If you do not worry about failure, you can switch between reverse proxies without changing any application code.
In fact, all reverse proxies provide a way to clear cache data, but you need to avoid using them as much as possible. The most standard way to clear the cache of a given URL is to specify the HTTP Method of the request as PURGE.
The following describes how to configure the reverse proxy of Symfony2 to support the purge http method:
// app/AppCache.phpuse Symfony\Bundle\FrameworkBundle\HttpCache\HttpCache;class AppCache extends HttpCache{ protected function invalidate(Request $request) { if ('PURGE' !== $request->getMethod()) { return parent::invalidate($request); } $response = new Response(); if (!$this->getStore()->purge($request->getUri())) { $response->setStatusCode(404, 'Not purged'); } else { $response->setStatusCode(200, 'Purged'); } return $response; }}
Note that you must protect your purge http method to prevent anyone from using some methods to clear your cached data.
Summary:
Symfony2 is designed to follow a proven road rule: HTTP. The cache is no exception. Understanding the Symfony2 cache system means you are familiar with the HTTP cache mode and use them effectively.
This means that you cannot rely solely on symfony2 documents and code examples. You must have a broader understanding of HTTP cache and gateway cache, such as Varnish.
I hope this article will help you design PHP programs based on the Symfony framework.