欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Symfony2框架學(xué)習(xí)筆記之HTTP Cache用法詳解

 更新時(shí)間:2016年03月18日 09:02:54   作者:Seekr  
這篇文章主要介紹了Symfony2框架HTTP Cache用法,結(jié)合實(shí)例形式分析了Symfony框架HTTP緩存的相關(guān)使用技巧,需要的朋友可以參考下

本文實(shí)例講述了Symfony2框架HTTP Cache用法。分享給大家供大家參考,具體如下:

富web應(yīng)用程序的本質(zhì)意味著它們的動(dòng)態(tài)。無(wú)論你的應(yīng)用程序多么有效率,每個(gè)請(qǐng)求比起靜態(tài)文件來(lái)說(shuō)總會(huì)存在很多的耗費(fèi)。對(duì)于大多數(shù)web程序來(lái)說(shuō),這沒(méi)什么。 Symfony2非常的輕快,無(wú)論你做些嚴(yán)重超載的請(qǐng)求,每個(gè)請(qǐng)求將會(huì)得到很快的回復(fù),而不會(huì)對(duì)你的服務(wù)器造成壓力。但是隨著你站點(diǎn)的成長(zhǎng),負(fù)載將成為一個(gè)嚴(yán)重的問(wèn)題。對(duì)每個(gè)請(qǐng)求處理應(yīng)該只被正常執(zhí)行一次。這就是緩存真正要達(dá)成的目標(biāo)。

站在巨人肩膀上的緩存:

提高一個(gè)應(yīng)用程序執(zhí)行效率的最有效方法是緩存一個(gè)頁(yè)面的所有輸出然后讓后續(xù)的請(qǐng)求繞開(kāi)整個(gè)應(yīng)用程序。當(dāng)然,這對(duì)于高動(dòng)態(tài)性的站點(diǎn)來(lái)說(shuō)并不是總是可能的。Symfony2 緩存系統(tǒng)是比較特別的,因?yàn)樗蕾囉谠贖TTP規(guī)范中定義的簡(jiǎn)單強(qiáng)大的HTTP cache。沒(méi)有重新發(fā)明新的緩存方法,Symfony2 擁抱在web上定義基礎(chǔ)交流的標(biāo)準(zhǔn)。一旦你理解了基礎(chǔ)的HTTP校驗(yàn)和過(guò)期緩存模式,你就會(huì)完全掌握了Symfony2的緩存系統(tǒng)。

第一步:一個(gè)網(wǎng)關(guān)緩存(gateway cache),或者反向代理。是一個(gè)坐在你應(yīng)用程序前面的對(duì)立的層。反向代理緩存來(lái)自于你應(yīng)用程序的響應(yīng)并使用這些緩存響應(yīng)在某些請(qǐng)求到達(dá)你應(yīng)用程序之前來(lái)回復(fù)它們。 Symfony2提供了自己的反向代理,也可以使用其它任何的反向代理。

第二步:HTTP緩存 (HTTP cache)頭用于和網(wǎng)關(guān)緩存以及任何其位于客戶和你的應(yīng)用程序之間的其它緩存交流。Symfony2 提供了和緩存頭交互的預(yù)設(shè)行為和強(qiáng)大接口。

第三步:HTTP 超時(shí)和校驗(yàn)時(shí)用于決定一個(gè)緩存內(nèi)容是否新鮮和陳舊的兩種模式。

第四步:ESI(Edge Side Includes)允許HTTP緩存被用于獨(dú)立緩存頁(yè)面片段(甚至是嵌套片段)。使用ESI,你甚至可以緩存一個(gè)完整的頁(yè)面60分鐘。但一個(gè)嵌入式邊欄緩存只有5分鐘。

使用網(wǎng)關(guān)緩存

當(dāng)使用HTTP緩存時(shí),緩存是跟你的應(yīng)用程序完全分離的,它位于你的請(qǐng)求客戶端和應(yīng)用程序之間。該緩存的工作就是從客戶端接收請(qǐng)求并把它們傳遞回你的應(yīng)用程序。同時(shí)它也將接收從你的應(yīng)用程序返回的響應(yīng)并把它轉(zhuǎn)給客戶端。可以說(shuō)它是你的應(yīng)用程序和請(qǐng)求客戶端之間請(qǐng)求-響應(yīng)交互的中間人。

按照這個(gè)思路,緩存會(huì)保存被認(rèn)為是“可緩存的”每一個(gè)響應(yīng)回復(fù)。當(dāng)同樣的請(qǐng)求再次傳來(lái)時(shí),該緩存會(huì)把自己緩存的響應(yīng)直接回復(fù)給請(qǐng)求客戶端,而完全忽略你的應(yīng)用程序。這種類(lèi)型的緩存就是HTTP網(wǎng)關(guān)緩存。目前有很多這類(lèi)緩存,比如Varnish,Squid in reverse proxy mode和Symfony2 反向代理等。

緩存類(lèi)型

一個(gè)網(wǎng)關(guān)緩存不是緩存的唯一類(lèi)型。事實(shí)上,有三種不同類(lèi)型的緩存會(huì)截獲并使用你的應(yīng)用程序發(fā)出的HTTP緩存頭。它們是:

瀏覽器緩存(Browser caches):瀏覽器擁有自己的本地緩存,這對(duì)你單擊"前一步"或者查看圖片和其它網(wǎng)絡(luò)資產(chǎn)時(shí)起到了主要作用。

代理緩存(Proxy caches):一個(gè)代理緩存是一個(gè)多人位于一人之后的共享的緩存。它們大多是一些大公司或者ISP安裝用來(lái)減少延遲和網(wǎng)絡(luò)阻塞的。

網(wǎng)關(guān)緩存(Gateway caches):像一個(gè)代理,也是一個(gè)共享緩存但是是位于服務(wù)器端的。一般是網(wǎng)絡(luò)管理員安裝它們,它使得網(wǎng)站更具可伸縮性,可靠性和高效性。網(wǎng)關(guān)緩存有時(shí)候被稱(chēng)為反向代理緩存,代理緩存,更或者是HTTP加速器。

Symfony2 反向代理

Symfony2擁有一個(gè)用PHP編寫(xiě)的反向代理(也叫做網(wǎng)關(guān)緩存)。開(kāi)啟它后,來(lái)自你應(yīng)用程序的可緩存的響應(yīng)回復(fù)將會(huì)開(kāi)始被立刻緩存。安裝它相也當(dāng)容易。每一個(gè)新的Symfony2應(yīng)用程序都有一個(gè)預(yù)配置緩存內(nèi)核(AppCache)包含了一個(gè)默認(rèn)的AppKernel。該緩存內(nèi)核就是個(gè)反向代理。要開(kāi)啟緩存,修改前端控制器代碼使用緩存內(nèi)核:

// 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包裹默認(rèn)的AppKernel
$kernel = new AppCache($kernel);
$kernel->handle(Request::createFromGlobale())->send();

緩存內(nèi)核會(huì)立刻扮演一個(gè)反向代理的角色,緩存來(lái)自你應(yīng)用程序的回復(fù)把它們發(fā)回給請(qǐng)求客戶端。

注意,該緩存內(nèi)核有一個(gè)特別的getLog()方法返回一個(gè)能夠表示在緩存層發(fā)生了什么的字符串。

可以在開(kāi)發(fā)環(huán)境中來(lái)調(diào)試和校驗(yàn)?zāi)愕木彺娌呗浴?/p>

error_log($kernel->getLog());

AppCache 對(duì)象是一個(gè)合理的默認(rèn)配置,當(dāng)然你也可以通過(guò)重寫(xiě)getOptions()方法來(lái)設(shè)置可選項(xiàng)對(duì)它進(jìn)行調(diào)優(yōu)。

// 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,
    );
  }
}

注意,這里無(wú)論怎么重寫(xiě)getOptions()方法,其中debug選項(xiàng)將被包裹的AppKernel的debug值自動(dòng)設(shè)置。

下面是一些重要的可選項(xiàng):

default_ttl: 當(dāng)沒(méi)有顯式的刷新信息在回復(fù)中提供時(shí),一個(gè)緩沖實(shí)體應(yīng)該被認(rèn)為是新鮮的時(shí)間秒數(shù)。顯式的設(shè)置Cache-Control 或者 Expires 頭會(huì)覆蓋這個(gè)參數(shù)值。默認(rèn)值為0。
private_headers:請(qǐng)求頭組,它在回復(fù)上觸發(fā)"private" Cache-Control 行為,無(wú)論回復(fù)是通過(guò)Cache-Control 指令顯式的聲明是public還是private 。默認(rèn)為Authorization和Cookie。
allow_reload: 指定是否允許客戶端通過(guò)在請(qǐng)求中指定Cache-Control的"no-cache"指令來(lái)強(qiáng)迫緩存重新加載。設(shè)置它為true時(shí)符合RFC2616規(guī)范。默認(rèn)值為false。
allow_revalidate:指定是否允許客戶端通過(guò)在請(qǐng)求中指定Cache-Control的"max-age=0"指令來(lái)強(qiáng)迫緩存重新校驗(yàn)。設(shè)置它為true時(shí)符合RFC2616規(guī)范。默認(rèn)值為false。
stale_while_revalidate:用于指定一個(gè)默認(rèn)秒數(shù)(間隔是秒因?yàn)榛貜?fù)TTL精度是1秒),在期間緩存還在后臺(tái)進(jìn)行重新校驗(yàn)時(shí)可以立刻返回一個(gè)陳舊的回復(fù)(默認(rèn)是2);該設(shè)置會(huì)被stale-while-revalidate HTTP Cache-Control擴(kuò)展重寫(xiě)(RFC 5861)。
stale_if_error: 指定一個(gè)默認(rèn)秒數(shù)(間隔是秒)在這期間緩存可以提供一個(gè)陳舊的回復(fù)當(dāng)遇到一個(gè)錯(cuò)誤時(shí)。默認(rèn)值為60。該設(shè)置會(huì)被stale-if-error HTTP Cache-Contorl 擴(kuò)展重寫(xiě)(RFC5861)

如果debug設(shè)置為true,Symfony2 會(huì)自動(dòng)添加一個(gè)X-Symfony-Cache 頭到回復(fù)保存著關(guān)于緩存點(diǎn)擊和丟失的信息。

從一個(gè)反向代理到另一個(gè)的轉(zhuǎn)換:

Symfony2反向代理是一個(gè)在開(kāi)發(fā)你的站點(diǎn)或者部署你的站點(diǎn)到一個(gè)共享主機(jī)而你無(wú)法安裝任何除PHP代碼以外的東西時(shí)的非常有用的工具。但是因?yàn)槭褂肞HP編寫(xiě),它不能跟用C寫(xiě)成的反向代理那樣快速。這就是為什么我們推薦使用Varnish或者Squid到你的運(yùn)營(yíng)服務(wù)器上的原因。好消息是從一個(gè)代理服務(wù)器到另外一個(gè)替換很容易,簡(jiǎn)單的不用你修改任何程序代碼。你可以開(kāi)始時(shí)使用Symfony2的反向代理等到了阻塞增加時(shí)升級(jí)到Varnish。

注意:Symfony2 反向代理執(zhí)行效率獨(dú)立于應(yīng)用程序的復(fù)雜性。因?yàn)閼?yīng)用程序核心僅僅在請(qǐng)求需要被轉(zhuǎn)發(fā)到它時(shí)才被啟動(dòng)。

HTTP緩存說(shuō)明:

為了發(fā)揮可用緩存層的優(yōu)勢(shì),你的應(yīng)用程序必須能傳達(dá)它的哪個(gè)回復(fù)可以被緩存,什么時(shí)候/怎樣 緩存會(huì)變成陳舊的規(guī)則。這些是通過(guò)在回復(fù)(response)上設(shè)置HTTP 緩存頭來(lái)實(shí)現(xiàn)的。

記住,"HTTP"只不過(guò)是一種web客戶端和服務(wù)器之間交流的簡(jiǎn)單文本語(yǔ)言。當(dāng)我們說(shuō)HTTP 緩存時(shí),我們說(shuō)的是它允許客戶端和服務(wù)器交換信息相關(guān)的緩存。

HTTP指定了4個(gè)Response緩存頭,我們需要關(guān)注一下:

Cache-Control
Expires
ETag
Last-Modified

其中最重要的也是萬(wàn)能的頭是Cache-Control頭,它其實(shí)是一個(gè)各種緩存信息的集合。

Cache-Control Header

Cache-Control頭是唯一的一個(gè)其內(nèi)部包含了各種各樣的關(guān)于一個(gè)response是否可以被緩存的信息。每條信息之間用逗號(hào)隔開(kāi)。

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

Symfony 提供一個(gè)Cache-Control頭的抽象,使它的創(chuàng)建更加可控。

$response = new Response();
// 標(biāo)記response為public還是private
$response->setPublic();
$response->setPrivate();
// 設(shè)置private或者shared 的最大年齡 age
$response->setMaxAge(600);
$response->setSharedMaxAge(600);
// 設(shè)置一個(gè)自定義的Cache-Control 指令
$response->headers->addCacheControlDirective('must-revalidate', true);

公共vs私有 Response

網(wǎng)關(guān)緩存和代理緩存都被認(rèn)為是“共享”緩存,因?yàn)樗鼈兙彺娴膬?nèi)容是被多用戶共享的。如果一個(gè)特定用戶的回復(fù)曾被錯(cuò)誤的存儲(chǔ)到共享緩存中,它以后可能被返回給無(wú)數(shù)的不用用戶。想象一下,如果你的賬戶信息被緩存然后返回給每一個(gè)后來(lái)請(qǐng)求他們自己賬戶頁(yè)面的用戶。要處理這種情況,每個(gè)回復(fù)可能都要設(shè)置時(shí)public還是private。

public 說(shuō)明給回復(fù)可能被private和共享的緩存保存。
private 說(shuō)明所有的或者部分的回復(fù)信息時(shí)給一個(gè)單獨(dú)用戶的,所以不能緩存到共享緩存中。

Symfony 謹(jǐn)慎地默認(rèn)每個(gè)回復(fù)為private。 要使用共享緩存的優(yōu)點(diǎn)(比如Symfony2反向代理),回復(fù)必須被顯式的設(shè)置為public。

安全方法:

HTTP緩存僅僅為安全方法工作(比如GET和HEAD)。要安全意味著當(dāng)它為某個(gè)請(qǐng)求服務(wù)時(shí)從來(lái)不會(huì)改變服務(wù)器上應(yīng)用程序的狀態(tài)。(當(dāng)然你可以寫(xiě)日志信息,緩存數(shù)據(jù)等)。這里有兩個(gè)很合理的后果(consequences):

當(dāng)你的應(yīng)用程序回復(fù)一個(gè)GET或者HEAD請(qǐng)求時(shí),你絕對(duì)不會(huì)改變你應(yīng)用程序的狀態(tài)。即使你不用網(wǎng)關(guān)緩存,代理緩存的存在意味著任何GET和HEAD請(qǐng)求可能會(huì)或者可能不會(huì)真的達(dá)到你的服務(wù)器。

不要期望PUT,POST或者DELETE方法被緩存。這些方法被使用意味著你應(yīng)用程序狀態(tài)的改變。緩存它們將阻止某種請(qǐng)求訪問(wèn)或者改變你的應(yīng)用程序。

緩存規(guī)則和默認(rèn)設(shè)置

HTTP 1.1 默認(rèn)情況下允許緩存任何事情除非有一個(gè)顯式的Cache-Control頭。實(shí)踐中,大多數(shù)緩存當(dāng)請(qǐng)求有cookie,一個(gè)授權(quán)頭,使用一個(gè)非安全的方法(比如PUT,POST,DELETE)或者當(dāng)請(qǐng)求有一個(gè)重定向代碼時(shí),不會(huì)進(jìn)行任何緩存活動(dòng)。

當(dāng)開(kāi)發(fā)者沒(méi)有做任何設(shè)置時(shí),Symfony2 會(huì)自動(dòng)按照下面的規(guī)則設(shè)置一個(gè)合理的比較保守的Cache-Control頭:

如果沒(méi)有緩存頭被定義(Cache-Control,Expires,ETag 或者Last-Modified),Cache-Control被設(shè)置為no-cache,意味著該response將不會(huì)被緩存。

如果Cache-Control 為空(但是有另一個(gè)緩存頭存在),它的值被設(shè)置為private,must-revalidate;

如果至少一個(gè)Cache-Control指令被設(shè)置,并且沒(méi)有'public'或者‘private'指令被顯式的添加,Symfony2 會(huì)自動(dòng)添加一個(gè)private指令(除去s-maxage 被設(shè)置的情況)。

HTTP過(guò)期和校驗(yàn)

HTTP規(guī)范定義了兩個(gè)緩存模型:
過(guò)期模型,你只需要通過(guò)包含一個(gè)Cache-Control和/或者一個(gè)Expires頭來(lái)指定一個(gè)Response應(yīng)該多長(zhǎng)時(shí)間被考慮“新鮮”問(wèn)題。緩存理解過(guò)期將不再讓相同的請(qǐng)求回復(fù),直到緩存的版本達(dá)到它過(guò)期時(shí)間成為“stale"陳舊。

校驗(yàn)?zāi)P?,?dāng)頁(yè)面時(shí)真正的動(dòng)態(tài)頁(yè)面時(shí)(他們的展現(xiàn)經(jīng)常變化),校驗(yàn)?zāi)P途徒?jīng)常需要了。這種模型,緩存存儲(chǔ)response,但是要求服務(wù)對(duì)每個(gè)請(qǐng)求是否緩存response依然進(jìn)行校驗(yàn)。

應(yīng)用程序使用唯一的response 標(biāo)示符(ETag 頭) 和/或者 時(shí)間戳(Last-Modified 頭)來(lái)檢查頁(yè)面自從被緩存后是否放生了變化。

這兩個(gè)模型的目標(biāo)是通過(guò)依靠一個(gè)緩存存儲(chǔ)并返回"新鮮" response,使得應(yīng)用程序從不生成相同的response兩次。

過(guò)期:

過(guò)期模型是在這兩個(gè)模型中是更加有效和簡(jiǎn)單明確的模型,它應(yīng)該在任何時(shí)候都有被使用的可能。當(dāng)一個(gè)response使用一過(guò)期方式被緩存,緩存將存儲(chǔ)response并為請(qǐng)求直接返回它而不去訪問(wèn)應(yīng)用程序,直到它過(guò)期。

過(guò)期模型可以被熟練的使用一兩個(gè),幾乎相同的,HTTP頭:比如 Expires或cache - control。

過(guò)期和Expires 頭

根據(jù)HTTP規(guī)范,Expires頭字段提供一個(gè)日期/時(shí)間,過(guò)了這個(gè)日期或時(shí)間后它的response就被認(rèn)為是陳舊的了。Expires頭可以被Response的setExpires()方法設(shè)置。它要求一個(gè)DateTime實(shí)例作為輸入?yún)?shù)。

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

生成的HTTP頭的結(jié)果如下:

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

注意,因?yàn)橐?guī)范的需要setExprise()方法會(huì)自動(dòng)把日期轉(zhuǎn)換為GMT時(shí)區(qū)。

我們注意到在HTTP規(guī)范1.1版之前,源服務(wù)不需要發(fā)送一個(gè)Date頭。 因此緩存(比如瀏覽器)可能需要依靠它本地的始終來(lái)評(píng)估Expires頭,造成計(jì)算生命周期時(shí)時(shí)鐘偏差。 Expires頭的另一個(gè)限制是規(guī)范規(guī)定:"HTTP/1.1服務(wù)不應(yīng)該發(fā)送Expires日期未來(lái)超過(guò)一年。"

過(guò)期和Cache-Control 頭

因?yàn)镋xpires頭的限制,大多時(shí)候,你應(yīng)該采用Cache-Control頭來(lái)替代它?;叵胍幌?,Cache-Control頭是用來(lái)指定多個(gè)不同緩存指令的。對(duì)于過(guò)期來(lái)說(shuō),有兩個(gè)指令,max-age 和 s-maxage。第一個(gè)被所有的緩存使用,然而第二個(gè)僅僅被用于共享緩存。

// 設(shè)置一個(gè)秒數(shù),過(guò)了這個(gè)秒數(shù)后response就被認(rèn)為是陳舊的了。
$response->setMaxAge(600);
// 同上,但是只用于共享緩存。
$response->setSharedMaxAge(600);
Cache-Control頭將使用如下格式(它可能還有其它指令):
Cache-Control: max-age=600, s-maxage=600

校驗(yàn):

一旦底層數(shù)據(jù)發(fā)生變化需要立刻對(duì)緩存資源進(jìn)行更新時(shí),過(guò)期模型就顯得力不從心了。在過(guò)期模型下,應(yīng)用程序不會(huì)被要求返回更新的response直到緩存最后過(guò)期變?yōu)殛惻f內(nèi)容以后。

校驗(yàn)?zāi)P徒鉀Q了這個(gè)問(wèn)題。在校驗(yàn)?zāi)P拖?,緩存持續(xù)保存response。不同的是,對(duì)每一個(gè)請(qǐng)求request,緩存都詢問(wèn)應(yīng)用程序緩存的response是否依然有效。如果緩存仍然有效,你的應(yīng)用程序應(yīng)該返回一個(gè)304狀態(tài)碼和一個(gè)空內(nèi)容。這告訴緩存它可以為請(qǐng)求用戶返回它緩存的response。

在這個(gè)模型下,你主要節(jié)省了帶寬因?yàn)槊枋霾粫?huì)發(fā)送兩次到相同的客戶端(而是發(fā)送一個(gè)304回復(fù)代替)。但是,如果你仔細(xì)設(shè)計(jì)你的應(yīng)用程序,你可能能忍受304 response需要的最小數(shù)據(jù)并節(jié)省CPU。

304狀態(tài)碼意味著沒(méi)有修改。它很重要因?yàn)樗鼪](méi)有包含整整的被請(qǐng)求內(nèi)容,而只是一個(gè)輕量級(jí)的導(dǎo)向集,它告訴緩存它應(yīng)該使用它現(xiàn)在保存的版本回復(fù)請(qǐng)求。跟過(guò)期類(lèi)似,也有兩個(gè)不同的HTTP頭可以被用來(lái)實(shí)現(xiàn)校驗(yàn)?zāi)P停?ETag和Last-Modifed

校驗(yàn)和ETag頭

ETag頭是一個(gè)字符串(也叫"entity-tag")它是目標(biāo)資源一個(gè)表現(xiàn)的唯一標(biāo)識(shí)。它完全由你的應(yīng)用程序來(lái)生成和設(shè)置。 比如,如果 /about 資源被緩存保存時(shí)取決于日期和你應(yīng)用程序的返回內(nèi)容。一個(gè)ETag像一個(gè)手印,被用來(lái)快速的比較一個(gè)資源的兩個(gè)不同版本是否等效。
像手印,同一個(gè)資源的所有表示形式中每個(gè)ETag必須是唯一的。讓我們來(lái)簡(jiǎn)單實(shí)現(xiàn)一個(gè)生成ETag使用md5加密的回復(fù)內(nèi)容作為內(nèi)容:

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一起發(fā)送的ETag與Response上的ETag進(jìn)行比較。如果兩個(gè)匹配,方法自動(dòng)設(shè)置Response狀態(tài)碼為304。

這個(gè)算法非常簡(jiǎn)單也非常通用,但是你需要在能計(jì)算ETag之前創(chuàng)建一個(gè)完整的Response,校驗(yàn)?zāi)P褪谴蝺?yōu)選擇。換句話說(shuō),它節(jié)省了帶寬,單沒(méi)有節(jié)省CPU利用。  

Symfony2還通過(guò)向setETag()方法傳入true作為第二個(gè)參數(shù),來(lái)支持弱ETag。

校驗(yàn)和Last-Modified 頭

Last-Modified頭是校驗(yàn)?zāi)P偷牡诙N形式。根據(jù)HTTP規(guī)范,”Last-Modified 頭字段指定日期和時(shí)間,在這個(gè)時(shí)間源服務(wù)器相信該表現(xiàn)是最后被修改版?!?br /> 換句話說(shuō),應(yīng)用程序決定基于自動(dòng)緩存內(nèi)容被緩存后是否被更新過(guò)來(lái)判斷緩存的內(nèi)容是否要被更新過(guò)。舉個(gè)例子,你可以使用最新更新日期為所有需要計(jì)算資源表現(xiàn)的對(duì)象作為L(zhǎng)ast-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() 方法比較請(qǐng)求Request中的If-Modified-Since頭和Response中的Last-Modified 頭。如果他們相等,Response會(huì)被設(shè)置一個(gè)304狀態(tài)碼。

注意,If-Modified-since 請(qǐng)求頭等于最終發(fā)送到客戶端特定資源Last-Modified頭。這就是如何客戶端和服務(wù)端相互交流決定資源自從它被緩存后是否被更新。

使用校驗(yàn)優(yōu)化你的代碼:

任何緩存策略的主要目的都是減輕應(yīng)用程序的加載。換句話說(shuō),你的應(yīng)用程序做的越少來(lái)返回304 response,越好。Response::isNotModified()方法通過(guò)暴露一個(gè)簡(jiǎn)單有效的模式做到了。

public funcation showAction($articleSlug)
{
  //獲取最小信息來(lái)計(jì)算ETag或者Last-Modified值(基于Request,數(shù)據(jù)是從數(shù)據(jù)庫(kù)或者一個(gè)鍵值對(duì)存儲(chǔ)實(shí)例中獲取。
  $article = //...
  //創(chuàng)建一個(gè)Response帶有一個(gè)ETag 和/或者 一個(gè)Last-Modified 頭
  $response = new Response();
  $response->setETag($article->computeETag());
  $response->setLastModified($article->getPublishedAt());
  //為給定的Request檢查Response沒(méi)有被修改
  if($response->isNotModified($this->getRequest())){
    //立刻返回304 Response
    return $response;
  }else{
    //做一些更多的工作-比如獲取更多的數(shù)據(jù)
    $comment=//...
    //或者用你已經(jīng)開(kāi)啟的$response渲染一個(gè)模版
    return $this->render('MyBundle:MyController:article.html.twig',
        array('article'=>$article, 'comments' =>$comments),
        $response
    );
  }
}

當(dāng)Response沒(méi)有被修改后,isNotModified()自動(dòng)設(shè)置response的狀態(tài)碼為304,移除response的內(nèi)容,移除一些不需要為304存在的頭。

不同的回復(fù)響應(yīng):

到目前為止,我們已經(jīng)假設(shè)了每個(gè)URI只有一個(gè)目標(biāo)資源的表示。默認(rèn)情況下,HTTP緩存通過(guò)使用URI的資源作為緩存鍵被執(zhí)行。如果兩個(gè)人請(qǐng)求同一個(gè)可緩存資源的URI,第二個(gè)用戶將獲取緩存版本。有時(shí)候這些不夠,不同版本的用一個(gè)URI需要被按照一個(gè)或者多個(gè)請(qǐng)求頭的值來(lái)被緩存。舉個(gè)例子,如果當(dāng)客戶端支持你壓縮頁(yè)面時(shí),任何給定的URI都有兩種表示:一個(gè)是客戶端支持壓縮時(shí),一個(gè)是不支持時(shí)的表示。這時(shí)候請(qǐng)求頭的Accept-Encoding值將決定使用哪個(gè)。

在這種情況下,我們需要回復(fù)的特定URI緩存一個(gè)壓縮版本和一個(gè)非壓縮版本,基于請(qǐng)求的Accept-Encoding值返回它們。這是通過(guò)Vary Response頭,Vary是一個(gè)不同頭用逗號(hào)分隔,它的值觸發(fā)請(qǐng)求資源的不同表示。

Vary:Accept-Encoding,User-Agent

注意,這個(gè)特別的Vary頭,將基于URI和Accept-Encoding和User-Agent 請(qǐng)求頭為每個(gè)資源的不同版本進(jìn)行緩存。

Response對(duì)象提供一個(gè)干凈的接口來(lái)管理Vary 頭:

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

setVary()方法需要一個(gè)頭名字或者一個(gè)頭名字?jǐn)?shù)組對(duì)應(yīng)不同的response。

過(guò)期和校驗(yàn):

你當(dāng)然可以在同一個(gè)Response中同時(shí)使用校驗(yàn)和過(guò)期。因?yàn)檫^(guò)期勝過(guò)校驗(yàn),你可以輕易的從它們兩個(gè)中根據(jù)好處做出選擇。換句話說(shuō),通過(guò)同時(shí)使用過(guò)期和校驗(yàn),你可以指示緩存服務(wù)于緩存的內(nèi)容,同時(shí)后臺(tái)間隔檢查來(lái)調(diào)查內(nèi)容是否依然合法。

更多Response方法:

Response類(lèi)提供了許多和緩存相關(guān)的方法。下面是主要的一些:

// 標(biāo)志Response過(guò)期陳舊
$response->expire();
// 強(qiáng)迫response返回一個(gè)適合 304 的沒(méi)有內(nèi)容的response
$response->setNotModified();

另外,跟緩存最相關(guān)的HTTP頭可以被通過(guò)一個(gè)單獨(dú)的方法setCache()設(shè)置。

// 通過(guò)一個(gè)調(diào)用設(shè)置緩存參數(shù)
$response->setCache(array(
  'etag'     => $etag,
  'last_modified' => $date,
  'max_age'    => 10,
  's_maxage'   => 10,
  'public'    => true,
  // 'private'  => true,
));

使用ESI(Edge Side Includes)

網(wǎng)關(guān)緩存是一個(gè)提高你網(wǎng)站執(zhí)行效率的很好的途徑。但是它們有一個(gè)限制:只能緩存整個(gè)頁(yè)面。如果你不想緩存整個(gè)頁(yè)面或者頁(yè)面的某一部分很動(dòng)態(tài),你就沒(méi)那么幸運(yùn)了。

幸運(yùn)的是,Symfony2為這些情況提供一個(gè)解決方案,基于ESI技術(shù)。它允許頁(yè)面指定的部分和主頁(yè)比起來(lái)有一個(gè)不同的緩存策略。

ESI規(guī)范描述標(biāo)簽?zāi)憧梢郧度氲侥愕捻?yè)面來(lái)和網(wǎng)關(guān)緩存交流。Symfony2中只實(shí)現(xiàn)了一個(gè)標(biāo)簽,include, 因?yàn)檫@是唯一一個(gè)能在Akami上下文之外使用的標(biāo)簽。

<html>
  <body>
    Some content
    <!-- 嵌入一個(gè)其它頁(yè)的內(nèi)容 -->
    <esi:include src="http://..." />
    More content
  </body>
</html>

從這個(gè)例子中注意到每個(gè)ESI標(biāo)簽有一個(gè)全限定URL。一個(gè)ESI標(biāo)簽表示可以通過(guò)一個(gè)給定的URL獲取的一個(gè)頁(yè)面片段。

當(dāng)請(qǐng)求被處理時(shí),網(wǎng)關(guān)緩存從它的緩存或者從背后的應(yīng)用程序中請(qǐng)求回復(fù)獲取整個(gè)頁(yè)面。換句話說(shuō),網(wǎng)關(guān)緩存既從緩存中獲取包含的頁(yè)面片段也會(huì)再次從背后的應(yīng)用程序中獲取回復(fù)請(qǐng)求的頁(yè)面片段。當(dāng)所有的ESI標(biāo)簽被解析后,網(wǎng)關(guān)緩存合并每一個(gè)ESI內(nèi)容到一個(gè)主頁(yè)并返回最后的內(nèi)容到客戶端。所有的這一切都透明的發(fā)生在網(wǎng)關(guān)緩存級(jí)(在你的程序外)。你將看到,如果你選擇ESI標(biāo)簽,Symfony2讓這包含它們的這一過(guò)程幾乎不費(fèi)勁。

在Symfony2中使用ESI

首先,使用ESI需要確認(rèn)在你的應(yīng)用程序配置中已經(jīng)打開(kāi)。

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),
));

現(xiàn)在假設(shè)我們有一個(gè)頁(yè)面時(shí)相對(duì)靜態(tài)的,除了一個(gè)新聞自動(dòng)收?qǐng)?bào)機(jī)在內(nèi)容的底部。使用ESI,我們可以緩存新聞自動(dòng)收?qǐng)?bào)機(jī)獨(dú)立于頁(yè)面其它部分。

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

在該示例中,我們給全頁(yè)面緩存周期為10分鐘。接下來(lái),通過(guò)嵌入一個(gè)action讓新聞ticker包含到模板中。這是通過(guò)render幫助來(lái)實(shí)現(xiàn)的。因?yàn)榍度氲膬?nèi)容來(lái)自其它頁(yè)面,Symfony2使用一個(gè)標(biāo)準(zhǔn)的render幫助來(lái)配置ESI標(biāo)簽:

Twig格式:

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

PHP格式:

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

通過(guò)把standalone設(shè)置為true,告訴Symfony2這個(gè)action應(yīng)該被渲染為一個(gè)ESI標(biāo)簽。

你可能想知道為什么要使用一個(gè)helper方法來(lái)代替直接寫(xiě)ESI標(biāo)簽。這是因?yàn)槭褂胔elper讓你的應(yīng)用程序工作即使沒(méi)有網(wǎng)關(guān)緩存被安裝。讓我們來(lái)看看它是怎樣工作的。

當(dāng)standalone為false時(shí)(也是默認(rèn)值),Symfony2在發(fā)送response到客戶端之前合并包含的頁(yè)面內(nèi)容到一個(gè)主頁(yè)。

但是當(dāng)standalone為true時(shí),并且如果Symfony2發(fā)現(xiàn)它跟支持ESI的網(wǎng)關(guān)緩存對(duì)話時(shí),它生成一個(gè)ESI include標(biāo)簽。

如果沒(méi)有網(wǎng)關(guān)緩存或者網(wǎng)關(guān)緩存不支持ESI,Symfony2將只合并包含的標(biāo)簽頁(yè)面內(nèi)容到一個(gè)主要的像它在standalone為false時(shí)所做的一樣。

嵌入的action現(xiàn)在可以指定自己的緩存規(guī)則了,完全獨(dú)立于主頁(yè)。

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

使用ESI,整個(gè)頁(yè)面緩存將被保持600秒有效,但是新聞組建緩存將只持續(xù)60秒。

ESI的一個(gè)必備條件是嵌入的action可以通過(guò)一個(gè)URL被訪問(wèn),這樣網(wǎng)關(guān)緩存才可以獨(dú)立于頁(yè)面其它部分獲取它。當(dāng)然,一個(gè)action不能被通過(guò)一個(gè)URL訪問(wèn)除非有一個(gè)路由指向它。Symfony2 通過(guò)一個(gè)通用的路由和controller負(fù)責(zé)這個(gè)。

為了ESI包含標(biāo)簽?zāi)苷5墓ぷ?,你必須定義_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;

因?yàn)槁酚稍试S所有的action通過(guò)一個(gè)URL被訪問(wèn),你可以通過(guò)使用Symfony2防火墻(允許訪問(wèn)你的反向代理的IP范圍)內(nèi)容保護(hù)它。

緩存策略的一大優(yōu)勢(shì)是你可以讓你的應(yīng)用程序根據(jù)動(dòng)態(tài)的需要同時(shí)又盡量的減少觸及應(yīng)用程序。

一旦你開(kāi)始使用ESI,請(qǐng)記住一定使用s-maxage指令代替max-age。因?yàn)闉g覽器只接受聚合的資源,它不知道子組件,所以它會(huì)按照max-age指令緩存整個(gè)頁(yè)面。這是你不希望它做的。

render helper支持的兩外兩個(gè)有用選項(xiàng):
alt:用作ESI標(biāo)簽的alt屬性,當(dāng)src找不到時(shí),它允許你指定一個(gè)替代URL。
ignore_errors:如果設(shè)置為true,一個(gè)onerror屬性將被添加到ESI,并且屬性值設(shè)置為continue,在一個(gè)失敗事件中,網(wǎng)關(guān)緩存將只默默的移除ESI標(biāo)簽。

緩存失效:

“計(jì)算機(jī)科學(xué)中有兩大難題:緩存失效和命名事物”---Phil Karlton

你永遠(yuǎn)都不需要失效緩存數(shù)據(jù),因?yàn)槭г缫言贖TTP緩存模型中被考慮到了。如果你使用校驗(yàn),你永遠(yuǎn)都不需要通過(guò)定義校驗(yàn)任何事情;如果你使用過(guò)期并失效某個(gè)資源,它意味著你設(shè)置一個(gè)未來(lái)的過(guò)期日期。因?yàn)樵谌魏晤?lèi)型的反向代理中失效都是一個(gè)頂級(jí)規(guī)范,如果你不擔(dān)心失效,你可以在不改變?nèi)魏螒?yīng)用程序代碼的情況下在反向代理間切換。

其實(shí),所有的反向代理都提供了清除緩存數(shù)據(jù)的方式,但是你需要盡量的避免使用它們。最標(biāo)準(zhǔn)的清除給定URL的緩存的方式是通過(guò)指定請(qǐng)求的HTTP方法為PURGE 來(lái)進(jìn)行。

下面是如何配置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;
  }
}

注意,你必須保護(hù)你的PURGE HTTP方法以避免隨便一個(gè)人使用某些方法清除你的緩存數(shù)據(jù)。

總結(jié):

Symfony2旨在遵循一條被證明了的道路規(guī)則:HTTP。 緩存也不例外。掌握Symfony2緩存系統(tǒng)意味著熟悉HTTP緩存模式和有效的使用它們。

這就意味著,你不能只依賴于symfony2文檔和代碼示例,你必須了解有關(guān)HTTP緩存和網(wǎng)關(guān)緩存的更寬闊的知識(shí),比如Varnish。

希望本文所述對(duì)大家基于Symfony框架的PHP程序設(shè)計(jì)有所幫助。

相關(guān)文章

最新評(píng)論