Symfony2學習筆記之HTTP Cache

huidaoli發表於2014-08-06


 

富web應用程式的本質意味著它們的動態。無論你的應用程式多麼有效率,每個請求比起靜態檔案來說總會存在很多的耗費。對於大多數web程式來說,這沒什麼。 Symfony2非常的輕快,無論你做些嚴重超載的請求,每個請求將會得到很快的回覆,而不會對你的伺服器造成壓力。但是隨著你站點的成長,負載將成為一個嚴重的問題。對每個請求處理應該只被正常執行一次。這就是快取真正要達成的目標。


站在巨人肩膀上的快取:
提高一個應用程式執行效率的最有效方法是快取一個頁面的所有輸出然後讓後續的請求繞開整個應用程式。當然,這對於高動態性的站點來說並不是總是可能的。Symfony2 快取系統是比較特別的,因為它依賴於在HTTP規範中定義的簡單強大的HTTP cache。沒有重新發明新的快取方法,Symfony2 擁抱在web上定義基礎交流的標準。一旦你理解了基礎的HTTP校驗和過期快取模式,你就會完全掌握了Symfony2的快取系統。

第一步:一個閘道器快取(gateway cache),或者反向代理。是一個坐在你應用程式前面的對立的層。反向代理快取來自於你應用程式的響應並使用這些快取響應在某些請求到達你應用程式之前來回復它們。 Symfony2提供了自己的反向代理,也可以使用其它任何的反向代理。

第二步:HTTP快取 (HTTP cache)頭用於和閘道器快取以及任何其位於客戶和你的應用程式之間的其它快取交流。Symfony2 提供了和快取頭互動的預設行為和強大介面。

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

第四步:ESI(Edge Side Includes)允許HTTP快取被用於獨立快取頁面片段(甚至是巢狀片段)。使用ESI,你甚至可以快取一個完整的頁面60分鐘。但一個嵌入式邊欄快取只有5分鐘。

 

使用閘道器快取
當使用HTTP快取時,快取是跟你的應用程式完全分離的,它位於你的請求客戶端和應用程式之間。該快取的工作就是從客戶端接收請求並把它們傳遞迴你的應用程式。同時它也將接收從你的應用程式返回的響應並把它轉給客戶端。可以說它是你的應用程式和請求客戶端之間請求-響應互動的中間人。

按照這個思路,快取會儲存被認為是“可快取的”每一個響應回覆。當同樣的請求再次傳來時,該快取會把自己快取的響應直接回復給請求客戶端,而完全忽略你的應用程式。這種型別的快取就是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

相關文章