Symfony2Book12:HTTP 缓存

简介:

富Web应用程序的特征就是它们是动态的。无论你的应用程序多么有效率,每个请求总是比服务静态文件有着更多的开销。

对于大多数Web应用程序而言,这是好的。Symfony2非常快,除非你做的是很重量级的事,否则每个请求都会很快被返回,这不会给你的服务器太大压力。

但当你的站点增长之后,开销就变成了一个问题。对每个请求的正常处理应该只做一次,这正是缓存的目标所在。

在巨人的肩膀上缓存

提高应用程序性能最有效的方式就是缓存整个输出页,然后完全旁路应用程序随后的请求。当然,对于高度动态的网站而言并不总是可以这样做,不是吗?在本章,我们将向你展示Symfony2缓存系统是如何工作的,并且为什么我们认为这是最好的方式。

Symfony2的缓存系统是不同的,因为它依赖简单而强大的HTTP缓存,正如它在HTTP规范中定义的那样。与重塑一个缓存方法不同,Symfony2拥抱标准,该标准定义了Web上的基本通信。一旦你理解HTTP验证和失效缓存模式的基本原理,你将做好了掌握Symfony2缓存系统的准备。

为了学会如何Symfony2缓存的用途,我们将分四步来讨论:
  • 步骤1: 一个网关缓存, 或反向代理,是个位于你应用程序之前的独立层。反向代理缓存来自你应用程序返回的响应,并且回复被缓存响应的请求, 在它们与应用程序接触之前。Symfony2提供它自己的反向代理,但任何反向代理都可以用。
  • 步骤2: HTTP缓存头被用于在应用程序和客户端之间的网关缓存和其它缓存通信。Symfony2提供合理的缺省值和强大的接口用于缓存头之间的交互。
  • 步骤3: HTTP失效和验证是用于确保缓存内容是新鲜(可以从缓存中重用)还是陈旧(应该从应用程序中生成)的两种模型。
  • 步骤4: 边缘端包含(ESI) 允许HTTP缓存用于独立缓存页面片段(甚至是嵌套的片段)。使用ESI,你甚至可以缓存整个页面60分钟,但内嵌的边栏则只缓存5分钟。

因为HTTP缓存到于Symfony2不是唯一的,所以已经有许多关于这方面的文章存在。如果你个HTTP缓存的新手,我们强烈推荐Ryan Tomayko的文章缓存做什么事。另一个有深度的文章是Mark Nottingham's的缓存教程

用网关缓存进行缓存

当与HTTP缓存时,缓存完全从你的应用程序中分离出来,并且位于你应用程序和客户端之间发送请求。

缓存的工作是从客户端接受请求,并将其送给应用程序。缓存也从应用程序中接收返回的响应并将其发送给客户端。缓存是客户端和应用程序之间请求-响应通信的中间人。

在这个过程中,缓存将保存每个被认为是“可缓存的“响应(参见HTTP缓存介绍)。如果相同的资源被再次请求,缓存将向客户端发送被缓存的响应,而完全忽视应用程序。

这种类型的缓存被认为是HTTP网关缓存,并且已经有许多的实现,如VarnishSquid的反向代理模式以及Symfony2反向代理。

This type of cache is knows as an HTTP gateway cache and many exist such as VarnishSquid in reverse proxy mode, and the Symfony2 reverse proxy.

缓存的类型

但网关缓存并不是唯一的缓存类型。实际上,应用程序发送的HTTP缓存头被消耗和解释多达三种不同的缓存类型:

  • 浏览器缓存:每个浏览器都有它自己的本地缓存,它主要用在你点击“返回”按钮或者缓存图片和其它资源。浏览器缓存对于被缓存的资源来说是个私有缓存,它不与任何人共享。
  • 代理缓存:代理是一个共享缓存,因为许多人可以共享一个缓存。它通常被大公司或ISP们安装,用来减少潜在的和网络的流量。
  • 网关缓存:就象代理一样,它也是一个共享缓存,但是在服务器端的。它通过网络管理员安装,使得网站更具可扩展性、可靠性和高性能。

网关缓存有时也被称为反向代理缓存、代理缓存甚至是HTTP加速器。

私有共享缓存的重要性开始变得更为明显,因为我们所谈论的缓存响应包括的内容是指向特定用户的(如帐号信息)。

每个来自你应用程序的响应可能会经过一个或前两种类型的缓存。这些缓存在你的控制之外,但遵循响应中的HTTP缓存指令集。

Symfony2反向代理

Symfony2附带一个用PHP写的反向代理(也被称为网关缓存) 。启动它之后来自你应用程序、可被缓存的响应将马上开始被缓存。安装它非常简单。每个新Symfony2应用程序都有一个被预配置的缓存内核(AppCache),它包含了缺省内核(AppKernel)。缓存内核是个反向代理:

 
  1. // web/app.php 
  2.  
  3. require_once __DIR__.'/../app/bootstrap_cache.php.cache'; 
  4. require_once __DIR__.'/../app/AppCache.php'; 
  5.  
  6. use Symfony\Component\HttpFoundation\Request; 
  7.  
  8. // wrap the default AppKernel with the AppCache one 
  9. $kernel = new AppCache(new AppKernel('prod', false)); 
  10. $kernel->handle(Request::createFromGlobals())->send(); 

缓存内核会立即生效。作为一个反向代理,它缓存来自应用程序的响应并把它们返回给客户端。

缓存内核有一个特殊的getLog()方法,它返回一个缓存层发生了什么的字符串说明。在开发环境中,使用它去调试和验证你的缓存策略:

 
  1. error_log($kernel->getLog()); 

AppCache对象有一个合理的缺省配置,但它也可以通过覆写getOptions方法设置选项的方式进行微调:

 
  1. // app/AppCache.php 
  2. class AppCache extends Cache 
  3.     protected function getOptions() 
  4.     { 
  5.         return array( 
  6.             'debug'                  => false, 
  7.             'default_ttl'            => 0, 
  8.             'private_headers'        => array('Authorization', 'Cookie'), 
  9.             'allow_reload'           => false, 
  10.             'allow_revalidate'       => false, 
  11.             'stale_while_revalidate' => 2, 
  12.             'stale_if_error'         => 60, 
  13.         ); 
  14.     } 

除非覆写getOptions(),否则调试选项将被自动设置为被包含AppKernel的debug值。

这里有一个主要选项的列表:

  • default_ttl: 在响应中没有提供明确的刷新信息时一个缓存条目被视为新鲜的秒数。直接覆盖缓存控制或失效头的值(缺省:0);
  • private_headers: 一组请求头。在通过缓存控制指令不能明确公共还是私有状态响应的情况下,触发“私有”缓存控制行为(缺省:授权和Cookie);
  • allow_reload: 通过在请求中包含一个缓存控制的"no-cache"指令来指定用户端是否可以强制进行重载缓存,根据RFC2616将其设置为true(缺省:false);
  • allow_revalidate:通过在请求中包含一个缓存控制的"max-age=0"指令来指定用户端是否可以强制进行缓存重验证。根据RFC2616将其设置为true(缺省:false);
  • stale_while_revalidate: 指定缺省秒数(和响应的TTL精度一样是秒)。在此期间缓存在后台重新验证(缺省:2)时可以返回陈旧响应;这个设置被stale-while-revalidate HTTP 缓存控制扩展覆写(参见RFC 5861);
  • stale_if_error:指定缺省的秒数(精度是秒)。在此期间当发生错误(缺省:60)时,缓存可以成为陈旧响应。该设置可以通过stale-if-error HTTP 缓存控制扩展来覆写(参见RFC 5861)。

如果debug为true,Symfony2自动向响应添加 X-Symfony-Cache头,该响应包含关于缓存点击和错失的有用信息。

 

从一个反向代理改到另一个反向代理

Symfony2反向代理是一个伟大的工具。它用于开发网站,或者在除PHP之外不能安装其它代码的共享主机中布署网站。但是因为是用PHP写的,所以它不如用C写的代理那么快。这就是为什么只要可能,我们都会高度推荐你在你的生产服务上使用Varnish或Squid。好消息是从一个代理服务切换到另一个是方便和透明的,不需要在你的应用程序上做任何修改。开始时使用方便的Symfony2反向代理,然后在你流量提升之后更新到Varnish。

关于Symfony2使用Varnish的更多信息,参见食谱(cookbook)中的如何使用Varnish一章

Symfony2反向代理的性能有赖于应用程序的复杂度。那是因为应用程序内核只在请求需要转发给它时才启动。

HTTP缓存的介绍

要使用缓存层,你的应用程序必须要能够在可缓存响应和何时/如何缓存变陈旧的规则之间通信。这一切可以通过在响应中设置HTTP缓存头来实现。

记住,"HTTP"无非是一种语言(一个简单文本语言),Web客户端(如浏览器)和Web服务器使用它来相互通信。当我们谈论HTTP缓存时,我们也正在谈论这个语言允许客户端和服务端交换与缓存有关的信息。

HTTP指定我们关注的4种响应头

  • Cache-Control(缓存控制)
  • Expires(过程)
  • ETag(被请求变量的实体值)
  • Last-Modified(最后修改时间)

最重要和最多模式的是Cache-Control头,它其实上是不同缓存信息的集合。

头中每一部分的细节都在HTTP失效和验证一节中进行说明。

The Cache-Control Header

Cache-Control头是唯一的,但它所包含的信息部分不是一个,而是多个。信息的每一部分都由冒号分开:

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

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

Symfony2提供一个Cache-Control头的抽象, 使之更易管理:

 
  1. $response = new Response(); 
  2.  
  3. // mark the response as either public or private 
  4. $response->setPublic(); 
  5. $response->setPrivate(); 
  6.  
  7. // set the private or shared max age 
  8. $response->setMaxAge(600); 
  9. $response->setSharedMaxAge(600); 
  10.  
  11. // set a custom Cache-Control directive 
  12. $response->headers->addCacheControlDirective('must-revalidate', true); 

公有与私有响应

网关和代理这两个缓存被认为是“共享的”缓存,因为缓存内容被超过一个用户共享。如果特定用户的响应错误地被共享缓存保存,那么它随后可能会返回给不同的用户。想像一下,如果你的帐号信息被缓存,然后返回给每个询问其帐号页的用户时的情景!

要解决这个问题 ,每个响应都要设置成公共或是私有:

  • public: 表示该响应可能被私有或共享缓存进行缓存;
  • private: 表示响应的部分或全部消息是针对单个用户的,不允许被共享缓存来进行缓存。

Symfony2保守地默认每个响应是私有的。要利用共享缓存(如Symfony2反向代理),响应必须明确设为public。

安全方法

HTTP缓存只为“安全”的HTTP方法工作(如GET和HEAD)。安全的意思是当提交请求时你永远不会改变服务器上应用程序的状态(你当然可以记录信息、缓存数据等)。这样有两个非常合理的结果:

  • 当响应GET或HEAD请求时,你应该不会改变应用程序的状态。甚至如果你没有使用网关缓存,代理缓存的表现也意味着任何GET或HEAD请求也许或者不会实际去请求服务器。
  • 不能希望缓存PUT、POST或DELETE方法。这些方法只有当应用程序状态变化时才会被使用(如删除一条博文)。缓存它们将会阻止一些来自点击或改变你应用程序的请求。

缓存规则和缺省

HTTP 1.1 允许缺省缓存任何东西,除非有一个明显的Cache-Control头。在实际中,当请求有一个cookie、授权头、使用非安全方法(如PUT、POST和DELETE)或者当响应有一个重定向状态码时,大多数缓存什么也不做。

在开发者和下列规则没有设置时,Symfony2会自动设置一个合理保守的Cache-Control头:

  • 如果没有定义缓存头(Cache-Control、Expires、ETag或 Last-Modified)时,Cache-Control被设置成no-cache,意思是响应将不会被缓存;
  • 如果Cache-Control为空(但其它缓存头之一被递交),它的值被设置为private, must-revalidate;
  • 但如果至少有一个Cache-Control指令被设置,并且非public或private被明确添加,Symfony2将自动添加private指令(除了s-maxage被设置)。

HTTP失效和验证

HTTP规范定义了两个缓存模型:

  • 失效模式中,通过包含Cache-Control和/或失效头,你只需简单指定响应多长时间内会被认为是“新鲜”。缓存理解失效,它将不会生成制造的请求,直到缓存的版本到了它失效的时间,并变得“陈旧”。
  • 当页面是真正地动态时(如它们的表现经常改变),验证模型通常是必须的。这种模式中缓存保存响应,但无论缓存的响应是否有效,每次请求都会询问服务器。应用程序使用一个唯一的响应标识(Etage头)和/或一个时间戳(Last-Modified头)去检查在缓存之后页面是否被更改。

两种模式的目标是永远不会两次生成同一个响应,它们依赖缓存去保存和返回“新鲜”的响应。

读HTTP规范

HTTP规范定义一个简单而强大的语言,使用该语言客户端和服务器可以通信。作为Web开发者,规范的请求-响应模型主导了我们的工作。不幸的是,实际规范文档,RFC2616,十分难读。

HTTP Bis不断努力去重写RFC2616,它并不描述HTTP的新版本,而是主要阐述原始的HTTP规范。而且还改善组织结构,将规范分为七个部分:每个与HTTP缓存相关的都可以在两个专用部分找到(P4 - Conditional RequestsP6 - Caching: Browser and intermediary caches

作为一名Web开发者,我们强烈督促你去读这个规范。它的清晰和强大是无价的,距创建之日已经超过了十年。不要因为它的外观而离去,它的内容远比它的封面美丽。

失效

失效模型是两种缓存模型中更有效也更直接的,无论何时只要可能就应该使用。当有着失效期限的响应被缓存时,缓存将保存响应并无须理会应用程序而直接返回该响应,直到该响应失效。

失效模型可以使用HTTP头Expries或Cache-Control两者中的一个来实现,两者几乎相同。

带Expires头的失效

根据HTTP规范,“Expires头字段给出日期/时间,之后响应被认为陈旧",Expires头可以通过setExpires()响应方法来设置。它使用DateTime实例作为参数:

 
  1. $date = new DateTime(); 
  2. $date->modify('+600 seconds'); 
  3.  
  4. $response->setExpires($date); 

HTTP头最后看上去象这样:

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

正如规范所要求的那样,setExpires()方法会自动将日期转换到GMT时区。

Expires头有两个限制。首先,Web服务器和缓存(如:浏览器)上的时钟必须同步。然后,规范指出"HTTP/1.1服务不能发送超过一年的失效日期。"

带Cache-Control头的失效

因为Expires头的限制,大多数情况下,你应该使用Cache-Control头来代替。回想一下,Cache-Control头被用于指定许多不同缓存指令。对于失效,有两个指令max-age和s-maxage。第一个用于所有缓存,而第二个只考虑共享缓存:

 
  1. // Sets the number of seconds after which the response 
  2. // should no longer be considered fresh 
  3. $response->setMaxAge(600); 
  4.  
  5. // Same as above but only for shared caches 
  6. $response->setSharedMaxAge(600); 

Cache-Control头将使用以下格式(它也许还有附加指令):

 
  1. Cache-Control: max-age=600, s-maxage=600 

验证

当底层数据一旦改变时资源就需要更新时,失效模型是不足的。使用失效模型,应用程序不会被要求返回更新的响应,直到缓存最终变成陈旧。

验证模型解决了这个问题。在这种模型下,缓存仍然保存响应。不同在于,对于每个请求,缓存都会询问应用程序被缓存的响应是否有效。如果缓存仍然有效,你的应用程序将返回304状态码,而没有具体内容。这样就告诉缓存它是OK的,以便返回被缓存的内容。

在这种模型下,你主要节省了带宽,因为无须向同一客户端发送两次(用304响应代替)。但如果你应用程序设计仔细的话,你也可以通过发送304响应得到最低限度的数据,也节省CPU(参见下面示例的实现)。

304状态码意味着“不用修改”。这是重要的,因为这个状态码没有包含实际被请求的内容。相反,响应只是简单一组轻量级的指令,告诉缓存它应该使用它保存的版本。

就象失效一样,有两个HTTP头可以实现验证模型:ETag和Last-Modified

带ETag头的验证

ETag头是一个字符串头(被称为“实体标签”),它完全被应用程序生成和设置,以便你可以看出它是唯一标识代表目的资源的。举个例子,被缓存保存的/about资源是根据应用程序的返回进行更新。ETag就象是一个指纹,并用来快速比较资源的两个不同版本是否相等。象指纹一样,每个ETag必须是唯一代表同一资源的。

让我们看看做为内容的MD5加密来生成ETag的简单实现。

 
  1. public function indexAction() 
  2.     $response = $this->renderView('MyBundle:Main:index.html.twig'); 
  3.     $response->setETag(md5($response->getContent())); 
  4.     $response->isNotModified($this->get('request')); 
  5.  
  6.     return $response; 

Response::isNotModified()方法比较请求发送的和在响应上设置的ETag,如果两者匹配,方法将自动设置响应状态码为304。

算法足够简单也非常通用,但你需要在能计算ETag之前创建整个响应。这是次优的,换句话说,它节省带宽,而不是CPU。

 

在根据验证优化你的代码一节中,我们将展示验证是如何智能地用于决定缓存验证,而无须做大量的工作。

Symfony2也支持通过向setETag()方法的第二个参数发送true来调整ETag。

带Last-Modified头的验证

Last-Modified是第二个验证的方式。根据HTTP规范,“Last-Modified头表示的日期和时间,使源服务器相信它表示最后修改的日期和时间"。换句话说,应用程序决定是否更新缓存内容是基于响应被缓存后,该响应是否被更新。

例如,你可以为所有需要计算资源表现的对象使用最后更新的日期做为Last-Modified头的值:

 
  1. public function showAction($articleSlug) 
  2.     // ... 
  3.  
  4.     $articleDate = new \DateTime($article->getUpdatedAt()); 
  5.     $authorDate = new \DateTime($author->getUpdatedAt()); 
  6.  
  7.     $date = $authorDate > $articleDate ? $authorDate : $articleDate; 
  8.  
  9.     $response->setLastModified($date); 
  10.     $response->isNotModified($this->get('request')); 
  11.  
  12.     return $response; 

Response::isNotModified()方法比较请求发送的If-Modified-Since头和在响应上设置的Last-Modified头,如果两者相等,响应将被设置304的状态码。

If-Modified-Since请求头等于为个别资源发送给客户端的最后响应的Last-Modified头。这就是客户端和服务端相互通信并决定资源被缓存后是否被更新。

根据验证优化你的代码

缓存策略的主目标是减轻应用程序的负载。换句话说,在应用程序返回304响应中你做得越少就越好。Response::isNotModified()通过暴露一个简单而有效的模式来实现这一点:

 
  1. public function showAction($articleSlug) 
  2.     // Get the minimum information to compute 
  3.     // the ETag or the Last-Modified value 
  4.     // (based on the Request, data are retrieved from 
  5.     // a database or a key-value store for instance) 
  6.     $article = // ... 
  7.  
  8.     // create a Response with a ETag and/or a Last-Modified header 
  9.     $response = new Response(); 
  10.     $response->setETag($article->computeETag()); 
  11.     $response->setLastModified($article->getPublishedAt()); 
  12.  
  13.     // Check that the Response is not modified for the given Request 
  14.     if ($response->isNotModified($this->get('request'))) { 
  15.         // return the 304 Response immediately 
  16.         return $response; 
  17.     } else { 
  18.         // do more work here - like retrieving more data 
  19.         $comments = // ... 
  20.  
  21.         // or render a template with the $response you've already started 
  22.         return $this->render( 
  23.             'MyBundle:MyController:article.html.twig', 
  24.             array('article' => $article, 'comments' => $comments), 
  25.             $response 
  26.         ); 
  27.     } 

当响应没有被修改时,isNotModified()会自动将响应状态码设为304,删除内容和一些304响应不需要递交的头(参见setNotModified())。

不同响应

到目前为止,我们已经假设每个URI正好表示一个目的资源。缺省状况下,HTTP缓存通过使用资源的URI做为缓存关键词来实现。如果两个用户请求同一可缓存资源的URI,那么第二个用户将得到被缓存的版本。

有时这并不够,相同URI的不同缓存版本需要基于一个或更多请求头的值。例如,当客户端支持压缩页面时,而你又这样做了,那么任何给点URL都有两种形式:一种是客户端支持压缩,一种是客户端不支持压缩。这需要通过Accept-Encoding请求头的值来决定。

在本例中,你需要缓存为特定的URL保存响应的压缩和没压缩的两个版本,并且基于请求的Accept-Encoding值来返回它们。这是通过使用Vary响应头来实现的,该响应头使用逗号分隔的头列表,这些值引发被请求资源的不同表现:

 
  1. Vary: Accept-Encoding, User-Agent 

这个特殊的Vary头将缓存每个基于URL资源的不同版本、Accept-Encoding值和User-Agent请求头。

响应对象提供完整的接口去管理Vary头

 
  1. // set one vary header 
  2. $response->setVary('Accept-Encoding'); 
  3.  
  4. // set multiple vary headers 
  5. $response->setVary(array('Accept-Encoding', 'User-Agent')); 

setVary()方法为响应的Vary头提供头名或头名数据。

失效和验证

你当然可以在同一响应中使用失效和验证。因为失效高于验证,所以你可以很轻易地两全其美。换句话说,通过使用失效和验证,你可以指示缓存将被缓存的内容送到服务器,该内容在一定间隔(失效)后检查,验证内容是否仍然有效。

更多的响应方法

Response类提供了更多关于缓存的方法。这里是最有用的一些:

 
  1. // Marks the Response stale 
  2. $response->expire(); 
  3.  
  4. // Force the response to return a proper 304 response with no content 
  5. $response->setNotModified(); 

另外,大多数缓存相关的HTTP头可以通过单个setCache()方法设置:

 
  1. // Set cache settings in one call 
  2. $response->setCache(array( 
  3.     'etag'          => $etag, 
  4.     'last_modified' => $date, 
  5.     'max_age'       => 10, 
  6.     's_maxage'      => 10, 
  7.     'public'        => true, 
  8.     // 'private'    => true, 
  9. )); 

使用边缘端包含

网关缓存是让你网站性能更高的方式。但它们有一个限制:它们只能缓存整个页面。如果你不需要缓存整个页面或者如果页面的部分拥有“更”动态的内容,你就郁闷了。幸运的是,Symfony2为这些情况提供了解决方案,该方案基于一种ESI或被称为边缘端包含的技术。Akamaï大约在10年前写了该技术的规范,它允许页面的指定部分有着与整个页面不同的缓存策略。

ESI规范描述你可以内嵌到你页面的标签,以便与网关缓存通信。在Symfony2中只实现了一个标签,include,因为这是在Akamaï上下文之外唯一有用的一个:

 
  1. <html> 
  2.     <body> 
  3.         Some content 
  4.  
  5.         <!-- Embed the content of another page here --> 
  6.         <esi:include src="http://..." /> 
  7.  
  8.         More content 
  9.     </body> 
  10. </html> 

注意例子中的每个ESI标签都有一个完全合格的URL。一个ESI标签代表一个页面片段,可以通过给定URL引入。

当请求被处理时,网关缓存引入缓存或后端应用程序请求整个页面。如果响应包含一个或多个ESI标记,它们都是以相同的方式处理换句话说,网关缓存可以从缓存中检索包含的页面片段,也可以后端应用程序中再次请求页面片段当所有的ESI标记已经解析,网关缓存合并成整个页面,并将其最终内容发送给客户端。

所有发生在网关缓存层的一切都是透明的(如应用程序无关)。如你所见,如果你选择使用ESI标签,Symfony2可以使包含它们的过程毫不费力。

在Symfony2中使用ESI

首先,要使用ESI,需要确保在你的应用程序配置中启动它:

 
  1. # app/config/config.yml 
  2. framework: 
  3.     # ... 
  4.     esi: { enabled: true } 

现在,假设我们有一个相对静态的页面,除了内容底部的新闻滚动条。通过ESI,我们可以缓存除新闻滚动条之外的页面其它部分。

 
  1. public function indexAction() 
  2.     $response = $this->renderView('MyBundle:MyController:index.html.twig'); 
  3.     $response->setSharedMaxAge(600); 
  4.  
  5.     return $response; 

在本例中,我们全页面缓存十分钟的生命周期。接下来,让我们在模板中通过内嵌一个动作包含新闻滚动条。它可以通过render助手函数实现(参见templating-embedding-controller 以得到更多细节)。

因为内嵌内容来自其它页(或控制器),Symfony2使用标准的render助手函数来配置ESI标签:

 
  1. {% render '...:news' with {}, {'standalone': true} %} 

通过将standalone设置为true,你告诉Symfony2动作应该作为ESI标签渲染。你也许疑惑为什么你想使用助手函数代替ESI标签?那是因为使用助手函数可以使你的应用程序即使在没安装网关缓存的情况下正常运行。让我们看看它是如何工作的。

当standalone为false时(缺省值),Symfony2在发送响应到客户端之前合并被包含的页面内容到主页面。但当standalone为真是,如果Symfony2检测到它正在与一个支持ESI的网关缓存会话时,它会生成一个ESI的include标签。如果没有网关缓存或该缓存不支持ESI时,Symfony2将只是把被包含的页面内容合并到主页面中,就象standalone被设置成false一样。

Symfony2检测网关缓存是否通过另一个Akamaï规范支持ESI,该规范通过Symfony2反向代理的开箱支持。

被内嵌的动作现在可以指定它自己的缓存规则,完全独立于主页面。

 
  1. public function newsAction() 
  2.   // ... 
  3.  
  4.   $response->setSharedMaxAge(60); 

通过ESI,整个页面缓存的有效时间是600秒,而新闻组件缓存仅为60秒。

然而,ESI的要求是内嵌动作可以通过URL访问,因此网关代理可以将它从页面的其它部分中独立出来。当然一个动作不能通过URL来访问,除非有路由指向它。Symfony2通过路由和控制器可以实现。为了要让ESI的include标签正确工作,你必须定义_internal路由:

 
  1. # app/config/routing.yml 
  2. _internal: 
  3.     resource: "@FrameworkBundle/Resources/config/routing/internal.xml" 
  4.     prefix:   /_internal 

因为这条路由允许所有动作可以通过URL访问,所以你也许想要通过使用Symfony2防火墙功能(通过允许访问你反向代理的IP地址范围)来保护它。

这种缓存策略最大的好处在于你可以使你的应用程序根据需要在同一时间里尽可能的动态,命中最可能的少。

一旦开始使用ESI,记住总是要用s-maxage指令去替代max-age。因为浏览器只接受汇总的资源,它并不知道子组件,所以它总是听从max-age指令并缓存整个页面。而你并不想那样。

render助手函数支持其它两个有用的选项:

  • alt: 作为ESI标签中的alt属性,允许你指定另一个URL,在src没有找到时替代;
  • ignore_errors: 如果设为真,带着表明继续值的onerror属性会被添加到ESI中。当发生故障时,网关缓存将默默地将ESI标签删除。

缓存无效

"在计算机学科只有两个难题:缓存无效和命名事物" --菲尔 卡尔顿

你永远不需要去处理无效的缓存数据,因为无效已经被考虑到HTTP缓存模型中。如果你使用验证模型,你永远不需要任何被定义无效的事物;如果你使用失效模型,资源无效,这就意味着你设置的失效时间太长了。

又因为没有无效机制,你可以使用任何反向代理,而无须改动你的应用程序代码。

实际上,所有反向代理都提供删除缓存数据的方式,但你应该尽可能地避免使用它们。最标准的做法是通过请求指定的URL来删除缓存,该请求带有特殊PURGE的HTTP方法。

这里是如何配置Symfony2反向代理支持PURGE的HTTP方法:

 
  1. // app/AppCache.php 
  2. class AppCache extends Cache 
  3.     protected function invalidate(Request $request) 
  4.     { 
  5.         if ('PURGE' !== $request->getMethod()) { 
  6.             return parent::invalidate($request); 
  7.         } 
  8.  
  9.         $response = new Response(); 
  10.         if (!$this->store->purge($request->getUri())) { 
  11.             $response->setStatusCode(404, 'Not purged'); 
  12.         } else { 
  13.             $response->setStatusCode(200, 'Purged'); 
  14.         } 
  15.  
  16.         return $response; 
  17.     } 

无论如何你都必须保护PURGE的HTTP方法,以避免随机用户删除你的缓存数据。

总结

Symfony2被设计用来遵循被验证的规则:HTTP。缓存也不例外。掌握Symfon2缓存系统意味着更加熟悉HTTP缓存模型并加以有效利用。这也意味着你已经有机会接近HTTP缓存和网关缓存(如Varnish)相关知识的世界,而不仅仅只是Symfony2文档和代码示例。


本文转自 firehare 51CTO博客,原文链接:http://blog.51cto.com/firehare/584557,如需转载请自行联系原作者

相关文章
|
3月前
|
存储 缓存 前端开发
HTTP的缓存机制是什么?
HTTP的缓存机制是什么?
28 1
|
4月前
|
缓存 算法
HTTP 缓存技术 - 协商缓存
HTTP 缓存技术 - 协商缓存
44 1
HTTP 缓存技术 - 协商缓存
|
4月前
|
存储 缓存 算法
HTTP 缓存技术 - 强制缓存
HTTP 缓存技术 - 强制缓存
48 0
HTTP 缓存技术 - 强制缓存
|
4月前
|
存储 缓存 前端开发
HTTP 缓存技术 - 认识缓存
HTTP 缓存技术 - 认识缓存
37 0
|
4月前
|
缓存 前端开发
前端知识笔记(三十六)———HTTP 缓存机制
前端知识笔记(三十六)———HTTP 缓存机制
25 0
|
5月前
|
缓存 Ubuntu Linux
百度搜索:蓝易云【Varnish开源HTTP反向代理缓存服务器、部署安装、测试】
通过按照上述步骤部署和配置Varnish,您可以将其作为反向代理缓存服务器来提高Web应用程序的性能和响应速度。记住,在实际部署中,您可能需要进一步调整Varnish的配置以满足您的具体需求。
46 2
|
5月前
|
缓存 Linux 开发者
百度搜索:蓝易云【Varnish开源HTTP反向代理缓存服务器、部署安装、测试。】
通过以上步骤,你可以成功部署和安装Varnish,并对Web应用程序进行测试。请根据具体需求进行适当的配置和调整,以确保Varnish能够按预期工作并提升Web应用程序的性能。
33 0
|
6月前
|
缓存
72 # http 缓存策略
72 # http 缓存策略
24 0
|
6月前
|
缓存 前端开发 JavaScript
HTTP协议的缓存策略有哪些?
HTTP协议的缓存策略有哪些?
|
1月前
|
缓存 NoSQL 安全
【Redis】缓存穿透
【Redis】缓存穿透
30 0

热门文章

最新文章