Symfony2學習筆記之HTTP Cache


 

富web應用程序的本質意味着它們的動態。不管你的應用程序多麼有效率,每一個請求比起靜態文件來講總會存在不少的耗費。對於大多數web程序來講,這沒什麼。 Symfony2很是的輕快,不管你作些嚴重超載的請求,每一個請求將會獲得很快的回覆,而不會對你的服務器形成壓力。可是隨着你站點的成長,負載將成爲一個嚴重的問題。對每一個請求處理應該只被正常執行一次。這就是緩存真正要達成的目標。php


站在巨人肩膀上的緩存:
提升一個應用程序執行效率的最有效方法是緩存一個頁面的全部輸出而後讓後續的請求繞開整個應用程序。固然,這對於高動態性的站點來講並非老是可能的。Symfony2 緩存系統是比較特別的,由於它依賴於在HTTP規範中定義的簡單強大的HTTP cache。沒有從新發明新的緩存方法,Symfony2 擁抱在web上定義基礎交流的標準。一旦你理解了基礎的HTTP校驗和過時緩存模式,你就會徹底掌握了Symfony2的緩存系統。html

第一步:一個網關緩存(gateway cache),或者反向代理。是一個坐在你應用程序前面的對立的層。反向代理緩存來自於你應用程序的響應並使用這些緩存響應在某些請求到達你應用程序以前來回復它們。 Symfony2提供了本身的反向代理,也可使用其它任何的反向代理。前端

第二步:HTTP緩存 (HTTP cache)頭用於和網關緩存以及任何其位於客戶和你的應用程序之間的其它緩存交流。Symfony2 提供了和緩存頭交互的預設行爲和強大接口。web

第三步:HTTP 超時和校驗時用於決定一個緩存內容是否新鮮和陳舊的兩種模式。算法

第四步:ESI(Edge Side Includes)容許HTTP緩存被用於獨立緩存頁面片斷(甚至是嵌套片斷)。使用ESI,你甚至能夠緩存一個完整的頁面60分鐘。但一個嵌入式邊欄緩存只有5分鐘。數據庫

 

使用網關緩存
當使用HTTP緩存時,緩存是跟你的應用程序徹底分離的,它位於你的請求客戶端和應用程序之間。該緩存的工做就是從客戶端接收請求並把它們傳遞迴你的應用程序。同時它也將接收從你的應用程序返回的響應並把它轉給客戶端。能夠說它是你的應用程序和請求客戶端之間請求-響應交互的中間人。bootstrap

按照這個思路,緩存會保存被認爲是「可緩存的」每個響應回覆。當一樣的請求再次傳來時,該緩存會把本身緩存的響應直接回復給請求客戶端,而徹底忽略你的應用程序。這種類型的緩存就是HTTP網關緩存。目前有不少這類緩存,好比Varnish,Squid in reverse proxy mode和Symfony2 反向代理等。數組


緩存類型
一個網關緩存不是緩存的惟一類型。事實上,有三種不一樣類型的緩存會截獲並使用你的應用程序發出的HTTP緩存頭。它們是:
    瀏覽器緩存(Browser caches):瀏覽器擁有本身的本地緩存,這對你單擊"前一步"或者查看圖片和其它網絡資產時起到了主要做用。
    代理緩存(Proxy caches):一個代理緩存是一個多人位於一人以後的共享的緩存。它們大可能是一些大公司或者ISP安裝用來減小延遲和網絡阻塞的。
    網關緩存(Gateway caches):像一個代理,也是一個共享緩存可是是位於服務器端的。通常是網絡管理員安裝它們,它使得網站更具可伸縮性,可靠性和高效性。網關緩存有時候被稱爲反向代理緩存,代理緩存,更或者是HTTP加速器。瀏覽器


Symfony2 反向代理
Symfony2擁有一個用PHP編寫的反向代理(也叫作網關緩存)。開啓它後,來自你應用程序的可緩存的響應回覆將會開始被馬上緩存。安裝它相也當容易。每個新的Symfony2應用程序都有一個預配置緩存內核(AppCache)包含了一個默認的AppKernel。該緩存內核就是個反向代理。要開啓緩存,修改前端控制器代碼使用緩存內核:緩存

複製代碼
// 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();
//使用AppCache包裹默認的AppKernel
$kernel = new AppCache($kernel);
$kernel->handle(Request::createFromGlobale())->send();
複製代碼

緩存內核會馬上扮演一個反向代理的角色,緩存來自你應用程序的回覆把它們發回給請求客戶端。

注意,該緩存內核有一個特別的getLog()方法返回一個可以表示在緩存層發生了什麼的字符串。
能夠在開發環境中來調試和校驗你的緩存策略。

error_log($kernel->getLog());

AppCache 對象是一個合理的默認配置,固然你也能夠經過重寫getOptions()方法來設置可選項對它進行調優。

複製代碼
// 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,
        );
    }
}
複製代碼

注意,這裏不管怎麼重寫getOptions()方法,其中debug選項將被包裹的AppKernel的debug值自動設置。

下面是一些重要的可選項:
    default_ttl: 當沒有顯式的刷新信息在回覆中提供時,一個緩衝實體應該被認爲是新鮮的時間秒數。顯式的設置Cache-Control 或者 Expires 頭會覆蓋這個參數值。默認值爲0。
    private_headers:請求頭組,它在回覆上觸發"private" Cache-Control 行爲,不管回覆是經過Cache-Control 指令顯式的聲明是public仍是private 。默認爲Authorization和Cookie。
    allow_reload: 指定是否容許客戶端經過在請求中指定Cache-Control的"no-cache"指令來強迫緩存從新加載。設置它爲true時符合RFC2616規範。默認值爲false。
    allow_revalidate:指定是否容許客戶端經過在請求中指定Cache-Control的"max-age=0"指令來強迫緩存從新校驗。設置它爲true時符合RFC2616規範。默認值爲false。
    stale_while_revalidate:用於指定一個默認秒數(間隔是秒由於回覆TTL精度是1秒),在期間緩存還在後臺進行從新校驗時能夠馬上返回一個陳舊的回覆(默認是2);該設置會被stale-while-revalidate HTTP Cache-Control擴展重寫(RFC 5861)。 
          stale_if_error: 指定一個默認秒數(間隔是秒)在這期間緩存能夠提供一個陳舊的回覆當遇到一個錯誤時。默認值爲60。該設置會被stale-if-error HTTP Cache-Contorl 擴展重寫(RFC5861)

若是debug設置爲true,Symfony2 會自動添加一個X-Symfony-Cache 頭到回覆保存着關於緩存點擊和丟失的信息。


從一個反向代理到另外一個的轉換:
Symfony2反向代理是一個在開發你的站點或者部署你的站點到一個共享主機而你沒法安裝任何除PHP代碼之外的東西時的很是有用的工具。可是由於使用PHP編寫,它不能跟用C寫成的反向代理那樣快速。這就是爲何咱們推薦使用Varnish或者Squid到你的運營服務器上的緣由。好消息是從一個代理服務器到另一個替換很容易,簡單的不用你修改任何程序代碼。你能夠開始時使用Symfony2的反向代理等到了阻塞增長時升級到Varnish。

注意:Symfony2 反向代理執行效率獨立於應用程序的複雜性。由於應用程序核心僅僅在請求須要被轉發到它時才被啓動。

 

HTTP緩存說明:
爲了發揮可用緩存層的優點,你的應用程序必須能傳達它的哪一個回覆能夠被緩存,何時/怎樣 緩存會變成陳舊的規則。這些是經過在回覆(response)上設置HTTP 緩存頭來實現的。

記住,"HTTP"只不過是一種web客戶端和服務器之間交流的簡單文本語言。當咱們說HTTP 緩存時,咱們說的是它容許客戶端和服務器交換信息相關的緩存。

HTTP指定了4個Response緩存頭,咱們須要關注一下:
   Cache-Control
   Expires
   ETag
   Last-Modified
其中最重要的也是萬能的頭是Cache-Control頭,它實際上是一個各類緩存信息的集合。

Cache-Control Header
Cache-Control頭是惟一的一個其內部包含了各類各樣的關於一個response是否能夠被緩存的信息。每條信息之間用逗號隔開。

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

Symfony 提供一個Cache-Control頭的抽象,使它的建立更加可控。

複製代碼
$response = new Response();

// 標記response爲public仍是private
$response->setPublic();
$response->setPrivate();

// 設置private或者shared 的最大年齡 age
$response->setMaxAge(600);
$response->setSharedMaxAge(600);

// 設置一個自定義的Cache-Control 指令
$response->headers->addCacheControlDirective('must-revalidate', true);
複製代碼

 

 

公共vs私有 Response
網關緩存和代理緩存都被認爲是「共享」緩存,由於它們緩存的內容是被多用戶共享的。若是一個特定用戶的回覆曾被錯誤的存儲到共享緩存中,它之後可能被返回給無數的不用用戶。想象一下,若是你的帳戶信息被緩存而後返回給每個後來請求他們本身帳戶頁面的用戶。要處理這種狀況,每一個回覆可能都要設置時public仍是private。
     public 說明給回覆可能被private和共享的緩存保存。
     private 說明全部的或者部分的回覆信息時給一個單獨用戶的,因此不能緩存到共享緩存中。
Symfony 謹慎地默認每一個回覆爲private。 要使用共享緩存的優勢(好比Symfony2反向代理),回覆必須被顯式的設置爲public。

 

安全方法
  HTTP緩存僅僅爲安全方法工做(好比GET和HEAD)。要安全意味着當它爲某個請求服務時歷來不會改變服務器上應用程序的狀態。(固然你能夠寫日誌信息,緩存數據等)。這裏有兩個很合理的後果(consequences):
當你的應用程序回覆一個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,意味着該response將不會被緩存。
   若是Cache-Control 爲空(可是有另外一個緩存頭存在),它的值被設置爲private,must-revalidate;
   若是至少一個Cache-Control指令被設置,而且沒有'public'或者‘private'指令被顯式的添加,Symfony2 會自動添加一個private指令(除去s-maxage 被設置的狀況)。

 

HTTP過時和校驗
HTTP規範定義了兩個緩存模型:
過時模型,你只須要經過包含一個Cache-Control和/或者一個Expires頭來指定一個Response應該多長時間被考慮「新鮮」問題。緩存理解過時將再也不讓相同的請求回覆,直到緩存的版本達到它過時時間成爲「stale"陳舊。

校驗模型,當頁面時真正的動態頁面時(他們的展示常常變化),校驗模型就常常須要了。這種模型,緩存存儲response,可是要求服務對每一個請求是否緩存response依然進行校驗。
           應用程序使用惟一的response 標示符(ETag 頭) 和/或者 時間戳(Last-Modified 頭)來檢查頁面自從被緩存後是否放生了變化。

這兩個模型的目標是經過依靠一個緩存存儲並返回"新鮮" response,使得應用程序從不生成相同的response兩次。


過時
過時模型是在這兩個模型中是更加有效和簡單明確的模型,它應該在任什麼時候候都有被使用的可能。當一個response使用一過時方式被緩存,緩存將存儲response併爲請求直接返回它而不去訪問應用程序,直到它過時。

過時模型能夠被熟練的使用一兩個,幾乎相同的,HTTP頭:好比 Expires或cache - control。


過時和Expires 頭
根據HTTP規範,Expires頭字段提供一個日期/時間,過了這個日期或時間後它的response就被認爲是陳舊的了。Expires頭能夠被Response的setExpires()方法設置。它要求一個DateTime實例做爲輸入參數。

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

$response->setExpires($date);

生成的HTTP頭的結果以下:

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

注意,由於規範的須要setExprise()方法會自動把日期轉換爲GMT時區。
咱們注意到在HTTP規範1.1版以前,源服務不須要發送一個Date頭。 所以緩存(好比瀏覽器)可能須要依靠它本地的始終來評估Expires頭,形成計算生命週期時時鐘誤差。 Expires頭的另外一個限制是規範規定:"HTTP/1.1服務不該該發送Expires日期將來超過一年。"


過時和Cache-Control 頭
由於Expires頭的限制,大多時候,你應該採用Cache-Control頭來替代它。回想一下,Cache-Control頭是用來指定多個不一樣緩存指令的。對於過時來講,有兩個指令,max-age 和 s-maxage。第一個被全部的緩存使用,然而第二個僅僅被用於共享緩存。

// 設置一個秒數,過了這個秒數後response就被認爲是陳舊的了。
$response->setMaxAge(600);

// 同上,可是隻用於共享緩存。
$response->setSharedMaxAge(600);

Cache-Control頭將使用以下格式(它可能還有其它指令):

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

 


校驗
一旦底層數據發生變化須要馬上對緩存資源進行更新時,過時模型就顯得力不從心了。在過時模型下,應用程序不會被要求返回更新的response直到緩存最後過時變爲陳舊內容之後。

校驗模型解決了這個問題。在校驗模型下,緩存持續保存response。不一樣的是,對每個請求request,緩存都詢問應用程序緩存的response是否依然有效。若是緩存仍然有效,你的應用程序應該返回一個304狀態碼和一個空內容。這告訴緩存它能夠爲請求用戶返回它緩存的response。

在這個模型下,你主要節省了帶寬由於描述不會發送兩次到相同的客戶端(而是發送一個304回覆代替)。可是,若是你仔細設計你的應用程序,你可能能忍受304 response須要的最小數據並節省CPU。

304狀態碼意味着沒有修改。它很重要由於它沒有包含整整的被請求內容,而只是一個輕量級的導向集,它告訴緩存它應該使用它如今保存的版本回復請求。跟過時相似,也有兩個不一樣的HTTP頭能夠被用來實現校驗模型: ETag和Last-Modifed


校驗和ETag頭
ETag頭是一個字符串(也叫"entity-tag")它是目標資源一個表現的惟一標識。它徹底由你的應用程序來生成和設置。 好比,若是 /about 資源被緩存保存時取決於日期和你應用程序的返回內容。一個ETag像一個手印,被用來快速的比較一個資源的兩個不一樣版本是否等效。

像手印,同一個資源的全部表示形式中每一個ETag必須是惟一的。讓咱們來簡單實現一個生成ETag使用md5加密的回覆內容做爲內容:

複製代碼
public function indexAction()
{
   $response = $this->render('MyBundle:Main:index.html.twig');
   $response->setETag(md5($response->getContent()));
   $response->isNotModified($this->getRequest());

   return $response;
}
複製代碼

Response::isNotModified()方法把和Request一塊兒發送的ETag與Response上的ETag進行比較。若是兩個匹配,方法自動設置Response狀態碼爲304。

這個算法很是簡單也很是通用,可是你須要在能計算ETag以前建立一個完整的Response,校驗模型是次優選擇。換句話說,它節省了帶寬,單沒有節省CPU利用。   

Symfony2還經過向setETag()方法傳入true做爲第二個參數,來支持弱ETag。


校驗和Last-Modified 頭
Last-Modified頭是校驗模型的第二種形式。根據HTTP規範,」Last-Modified 頭字段指定日期和時間,在這個時間源服務器相信該表現是最後被修改版。「
換句話說,應用程序決定基於自動緩存內容被緩存後是否被更新過來判斷緩存的內容是否要被更新過。舉個例子,你可使用最新更新日期爲全部須要計算資源表現的對象做爲Last-Modified頭的值:

複製代碼
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;
}
複製代碼

Response::isNotModified() 方法比較請求Request中的If-Modified-Since頭和Response中的Last-Modified 頭。若是他們相等,Response會被設置一個304狀態碼。

注意,If-Modified-since 請求頭等於最終發送到客戶端特定資源Last-Modified頭。這就是如何客戶端和服務端相互交流決定資源自從它被緩存後是否被更新。

 

使用校驗優化你的代碼
任何緩存策略的主要目的都是減輕應用程序的加載。換句話說,你的應用程序作的越少來返回304 response,越好。Response::isNotModified()方法經過暴露一個簡單有效的模式作到了。

複製代碼
public funcation showAction($articleSlug)
{
   //獲取最小信息來計算ETag或者Last-Modified值(基於Request,數據是從數據庫或者一個鍵值對存儲實例中獲取。
   $article = //...
  
   //建立一個Response帶有一個ETag 和/或者 一個Last-Modified 頭
   $response = new Response();
   $response->setETag($article->computeETag());
   $response->setLastModified($article->getPublishedAt());

   //爲給定的Request檢查Response沒有被修改
   if($response->isNotModified($this->getRequest())){
       //馬上返回304 Response
       return $response;
   }else{
       //作一些更多的工做-好比獲取更多的數據
       $comment=//...
       //或者用你已經開啓的$response渲染一個模版
       return $this->render('MyBundle:MyController:article.html.twig',
                array('article'=>$article, 'comments' =>$comments),
                $response
       );
   }
}
複製代碼

當Response沒有被修改後,isNotModified()自動設置response的狀態碼爲304,移除response的內容,移除一些不須要爲304存在的頭。


不一樣的回覆響應:
到目前爲止,咱們已經假設了每一個URI只有一個目標資源的表示。默認狀況下,HTTP緩存經過使用URI的資源做爲緩存鍵被執行。若是兩我的請求同一個可緩存資源的URI,第二個用戶將獲取緩存版本。有時候這些不夠,不一樣版本的用一個URI須要被按照一個或者多個請求頭的值來被緩存。舉個例子,若是當客戶端支持你壓縮頁面時,任何給定的URI都有兩種表示:一個是客戶端支持壓縮時,一個是不支持時的表示。這時候請求頭的Accept-Encoding值將決定使用哪一個。

在這種狀況下,咱們須要回覆的特定URI緩存一個壓縮版本和一個非壓縮版本,基於請求的Accept-Encoding值返回它們。這是經過Vary Response頭,Vary是一個不一樣頭用逗號分隔,它的值觸發請求資源的不一樣表示。

Vary:Accept-Encoding,User-Agent

注意,這個特別的Vary頭,將基於URI和Accept-Encoding和User-Agent 請求頭爲每一個資源的不一樣版本進行緩存。

Response對象提供一個乾淨的接口來管理Vary 頭:

// 設置一個vary 頭
$response->setVary('Accept-Encoding');

// 設置多個vary頭
$response->setVary(array('Accept-Encoding', 'User-Agent'));

setVary()方法須要一個頭名字或者一個頭名字數組對應不一樣的response。

 

過時和校驗
你固然能夠在同一個Response中同時使用校驗和過時。由於過時賽過校驗,你能夠輕易的從它們兩個中根據好處作出選擇。換句話說,經過同時使用過時和校驗,你能夠指示緩存服務於緩存的內容,同時後臺間隔檢查來調查內容是否依然合法。


更多Response方法
Response類提供了許多和緩存相關的方法。下面是主要的一些:

// 標誌Response過時陳舊
$response->expire();

// 強迫response返回一個適合 304 的沒有內容的response
$response->setNotModified();

另外,跟緩存最相關的HTTP頭能夠被經過一個單獨的方法setCache()設置。

複製代碼
// 經過一個調用設置緩存參數
$response->setCache(array(
    'etag'          => $etag,
    'last_modified' => $date,
    'max_age'       => 10,
    's_maxage'      => 10,
    'public'        => true,
    // 'private'    => true,
));
複製代碼

 

 


使用ESI(Edge Side Includes)
網關緩存是一個提升你網站執行效率的很好的途徑。可是它們有一個限制:只能緩存整個頁面。若是你不想緩存整個頁面或者頁面的某一部分很動態,你就沒那麼幸運了。
幸運的是,Symfony2爲這些狀況提供一個解決方案,基於ESI技術。它容許頁面指定的部分和主頁比起來有一個不一樣的緩存策略。

ESI規範描述標籤你能夠嵌入到你的頁面來和網關緩存交流。Symfony2中只實現了一個標籤,include, 由於這是惟一一個能在Akami上下文以外使用的標籤。

複製代碼
<html>
    <body>
        Some content

        <!-- 嵌入一個其它頁的內容 -->
        <esi:include src="http://..." />

        More content
    </body>
</html>
複製代碼

從這個例子中注意到每一個ESI標籤有一個全限定URL。一個ESI標籤表示能夠經過一個給定的URL獲取的一個頁面片斷。

當請求被處理時,網關緩存從它的緩存或者從背後的應用程序中請求回覆獲取整個頁面。換句話說,網關緩存既從緩存中獲取包含的頁面片斷也會再次從背後的應用程序中獲取回覆請求的頁面片斷。當全部的ESI標籤被解析後,網關緩存合併每個ESI內容到一個主頁並返回最後的內容到客戶端。全部的這一切都透明的發生在網關緩存級(在你的程序外)。你將看到,若是你選擇ESI標籤,Symfony2讓這包含它們的這一過程幾乎不費勁。


在Symfony2中使用ESI
首先,使用ESI須要確認在你的應用程序配置中已經打開。
YAML格式:

# app/config/config.yml
framework:
    # ...
    esi: { enabled: true }

XML格式:

<!-- app/config/config.xml -->
<framework:config ...>
    <!-- ... -->
    <framework:esi enabled="true" />
</framework:config>

PHP代碼格式:

// app/config/config.php
$container->loadFromExtension('framework', array(
    // ...
    'esi'    => array('enabled' => true),
));

如今假設咱們有一個頁面時相對靜態的,除了一個新聞自動收報機在內容的底部。使用ESI,咱們能夠緩存新聞自動收報機獨立於頁面其它部分。

複製代碼
public function indexAction()
{
    $response = $this->render('MyBundle:MyController:index.html.twig');
    $response->setSharedMaxAge(600);

    return $response;
}
複製代碼

在該示例中,咱們給全頁面緩存週期爲10分鐘。接下來,經過嵌入一個action讓新聞ticker包含到模板中。這是經過render幫助來實現的。由於嵌入的內容來自其它頁面,Symfony2使用一個標準的render幫助來配置ESI標籤:
Twig格式:

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

PHP格式:

<?php echo $view['actions']->render('...:news', array(), array('standalone' => true)) ?>

經過把standalone設置爲true,告訴Symfony2這個action應該被渲染爲一個ESI標籤。
你可能想知道爲何要使用一個helper方法來代替直接寫ESI標籤。這是由於使用helper讓你的應用程序工做即便沒有網關緩存被安裝。讓咱們來看看它是怎樣工做的。

當standalone爲false時(也是默認值),Symfony2在發送response到客戶端以前合併包含的頁面內容到一個主頁。
可是當standalone爲true時,而且若是Symfony2發現它跟支持ESI的網關緩存對話時,它生成一個ESI include標籤。
若是沒有網關緩存或者網關緩存不支持ESI,Symfony2將只合幷包含的標籤頁面內容到一個主要的像它在standalone爲false時所作的同樣。

嵌入的action如今能夠指定本身的緩存規則了,徹底獨立於主頁。

public function newsAction()
{
   //...
   $response->setShareMaxAge(60);
}

使用ESI,整個頁面緩存將被保持600秒有效,可是新聞組建緩存將只持續60秒。

ESI的一個必備條件是嵌入的action能夠經過一個URL被訪問,這樣網關緩存才能夠獨立於頁面其它部分獲取它。固然,一個action不能被經過一個URL訪問除非有一個路由指向它。Symfony2 經過一個通用的路由和controller負責這個。
爲了ESI包含標籤能正常的工做,你必須定義_internal 路由:
YAML格式:

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

XML格式:

複製代碼
<!-- app/config/routing.xml -->
<?xml version="1.0" encoding="UTF-8" ?>

<routes xmlns="http://symfony.com/schema/routing"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">

    <import resource="@FrameworkBundle/Resources/config/routing/internal.xml" prefix="/_internal" />
</routes>
複製代碼

PHP代碼格式:

複製代碼
// app/config/routing.php
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;

$collection->addCollection($loader->import('@FrameworkBundle/Resources/config/routing/internal.xml', '/_internal'));

return $collection;
複製代碼

由於路由容許全部的action經過一個URL被訪問,你能夠經過使用Symfony2防火牆(容許訪問你的反向代理的IP範圍)內容保護它。
緩存策略的一大優點是你可讓你的應用程序根據動態的須要同時又儘可能的減小觸及應用程序。

一旦你開始使用ESI,請記住必定使用s-maxage指令代替max-age。由於瀏覽器只接受聚合的資源,它不知道子組件,因此它會按照max-age指令緩存整個頁面。這是你不但願它作的。

render helper支持的兩外兩個有用選項:
   alt:用做ESI標籤的alt屬性,當src找不到時,它容許你指定一個替代URL。
   ignore_errors:若是設置爲true,一個onerror屬性將被添加到ESI,而且屬性值設置爲continue,在一個失敗事件中,網關緩存將只默默的移除ESI標籤。


緩存失效:
「計算機科學中有兩大難題:緩存失效和命名事物」---Phil Karlton

你永遠都不須要失效緩存數據,由於失效早已在HTTP緩存模型中被考慮到了。若是你使用校驗,你永遠都不須要經過定義校驗任何事情;若是你使用過時並失效某個資源,它意味着你設置一個將來的過時日期。由於在任何類型的反向代理中失效都是一個頂級規範,若是你不擔憂失效,你能夠在不改變任何應用程序代碼的狀況下在反向代理間切換。

其實,全部的反向代理都提供了清除緩存數據的方式,可是你須要儘可能的避免使用它們。最標準的清除給定URL的緩存的方式是經過指定請求的HTTP方法爲PURGE 來進行。

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

複製代碼
// app/AppCache.php

use 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;
    }
}
複製代碼

注意,你必須保護你的PURGE HTTP方法以免隨便一我的使用某些方法清除你的緩存數據。

 

總結
   Symfony2旨在遵循一條被證實了的道路規則:HTTP。 緩存也不例外。掌握Symfony2緩存系統意味着熟悉HTTP緩存模式和有效的使用它們。
這就意味着,你不能只依賴於symfony2文檔和代碼示例,你必須瞭解有關HTTP緩存和網關緩存的更寬闊的知識,好比Varnish。

 

參考URL:http://symfony.com/doc/current/book/http_cache.html

相關文章
相關標籤/搜索