亚洲国产日韩欧美一区二区三区,精品亚洲国产成人av在线,国产99视频精品免视看7,99国产精品久久久久久久成人热,欧美日韩亚洲国产综合乱

HTTP緩存

Diff本文源自BOOK,不同于官方現(xiàn)有文檔。本文在某些地方解釋得更深入、更細(xì)致。因此我們沒有強(qiáng)行與官方同步。

富網(wǎng)絡(luò)應(yīng)用程序的天然屬性是,它們是動(dòng)態(tài)的。不管你的程序多有效率,每次請(qǐng)求始終承受著遠(yuǎn)遠(yuǎn)大過靜態(tài)文件的開銷。

而更多的web程序,并沒有受大的影響。Symfony閃電般快,除非你在做一些超級(jí)重載,每一次請(qǐng)求都會(huì)很快恢復(fù),而沒有把過多壓力留給服務(wù)器。

但你的網(wǎng)站在成長(zhǎng),過載有可能成為問題。針對(duì)通常請(qǐng)求的處理,只應(yīng)完成一次。而這正是緩存鎖定的目標(biāo)。

緩存于巨人的肩膀 ?

改善一套程序的性能,最有效的方式是緩存頁(yè)面的全部輸出,然后無視整個(gè)后續(xù)請(qǐng)求。當(dāng)然,對(duì)于高動(dòng)態(tài)網(wǎng)站而言,不可能總是這樣。本章,你將了解Symfony的緩存系統(tǒng)是如何運(yùn)作的,以及為何這是最佳方案。

Symfony緩存系統(tǒng)與眾不同,因?yàn)樗揽康氖?a href="/blog/the-symfony-glossary.html#catalog12">HTTP specification所定義的HTTP cache之簡(jiǎn)單與強(qiáng)大。不同于重新發(fā)明一套緩存方法,Symfony強(qiáng)調(diào)的是定義了web基本通信的標(biāo)準(zhǔn)。一旦你掌握了“HTTP驗(yàn)證”,以及“緩存models的過期”等基本知識(shí),你已經(jīng)可以去掌握Symfony的緩存系統(tǒng)。

學(xué)習(xí)Symfony緩存的過程,可分為四個(gè)步驟:

  1. 網(wǎng)關(guān)緩存(gateway cache),或者反向代理(reverse proxy),是位于你程序前面的獨(dú)立層。反向代理,緩存的是響應(yīng),因?yàn)樗鼈儽荒愕某绦蚍祷?;還能在請(qǐng)求到達(dá)你的程序之前,通過緩存的響應(yīng)來回應(yīng)請(qǐng)求。Symfony提供了自己的反向代理,但是任何反向代理都可以使用。

  2. HTTP cacheHTTP緩存頭,在你的程序和客戶端之間,被用于同網(wǎng)關(guān)緩存或其他緩存進(jìn)行通信。Symfony提供了合理的默認(rèn)配置,以及強(qiáng)大的接口,用于與緩存頭(cache headers)進(jìn)行互動(dòng)。

  3. HTTP過期與驗(yàn)證(expiration and validation),這兩個(gè)模型被用于決定緩存的內(nèi)容是否新鮮/fresh(可從cache中復(fù)用),或者是否陳舊/stale(應(yīng)當(dāng)被程序重新生成)

  4. Edge Side Includes(ESI),邊緣端包容允許HTTP cache被用于頁(yè)面局部(甚至嵌套片段)的獨(dú)立緩存。在ESI的幫助下,你甚至可以“緩存整個(gè)頁(yè)面60分鐘,但側(cè)邊欄只緩存5分鐘”。

由于HTTP cache并非Symfony專用,有很多相關(guān)文章。如果你對(duì)HTTP緩存不太熟,強(qiáng)烈推薦閱讀Ryan Tomayko的緩存能做什么(Things Caches Do)。另一個(gè)深度好文是Mark Nottingham的緩存教程(Cache Tutorial)。

使用Gateway Cache ?

當(dāng)用HTTP緩存時(shí),cache是完全與你的程序分開的,它居于你的程序與發(fā)動(dòng)請(qǐng)求的客戶端的之間。

緩存的任務(wù),就是接收客戶端請(qǐng)求,然后把它們?cè)賯骰啬愕某绦?,跟著推送回客戶端。這里的緩存是程序與瀏覽器之間的“請(qǐng)求-響應(yīng)”通信過程的“中間人”。

隨著時(shí)間推移,這些緩存將存儲(chǔ)每一次被認(rèn)為“可以緩存(cacheable)”的響應(yīng)(參考HTTP緩存介紹)。如果相同的資源被再次請(qǐng)求,cache將發(fā)送緩存了的響應(yīng)至客戶端,完全無視你的程序。

這種類型的緩存即是HTTP gateway cache(網(wǎng)關(guān)緩存),存在于諸如Varnish、反向代理模式下的Squid以及Symfony的反向代理之中。

緩存類型 ?

但是Gateway緩存并非唯一的緩存類型。實(shí)際上,你的程序發(fā)送的HTTP緩存頭,被假定于被最多三種方式的緩存所解釋:

  • 瀏覽器緩存(Browser caches):每個(gè)瀏覽器都內(nèi)置了自己的本地緩存,用于你點(diǎn)擊“回退”時(shí)使用,或者用于圖片和其他assets資源。瀏覽器緩存是私有(private)緩存,因?yàn)榫彺娴馁Y源不能被其他人使用;

  • 代理緩存(Proxy caches):代理,是指共享(shared)緩存,因?yàn)楹芏嗳丝梢愿谀硞€(gè)人的后面(來使用)。通常被大公司或ISP所使用,以減低訪問延遲和網(wǎng)絡(luò)流量。

  • 網(wǎng)關(guān)緩存(Gateway caches):類似代理,它也是共享緩存,但卻是在服務(wù)器端。常為網(wǎng)絡(luò)管理員所用,令網(wǎng)站更易升級(jí)、更可靠、性能更高。

Gateway caches有時(shí)特指反向代理緩存,surrogate caches(代理緩存),甚至HTTP加速器。

當(dāng)緩存的響應(yīng)包含了某個(gè)特定用戶的內(nèi)容(比如賬號(hào)信息)這種情況被討論時(shí),私有(private)緩存和共享(shared)緩存的重要性與日俱增。

程序的每一次響應(yīng),將會(huì)經(jīng)歷前兩種緩存類型中的一種或兩種。這些緩存是在你的(程序)控制之外,卻遵守響應(yīng)中設(shè)置好的HTTP緩存的指令。

Symfony反向代理(Reverse Proxy) ?

Symfony內(nèi)置了用PHP寫的反向代理(也被稱為gateway緩存)。它并非Varnish這種全功能的反向代理緩存,但卻是一個(gè)很好的起步。

關(guān)于Varnish設(shè)置的更多細(xì)節(jié),參考 如何使用Varnish加速我的網(wǎng)站。

開啟代理很容易:Symfony程序都預(yù)建了一個(gè)cache kernel緩存核心(AppCache),它把默認(rèn)的核心(AppKernel)給打包。這個(gè)緩存核心就是 反向代理。

開啟緩存很容易,修改你的前端控制器代碼。你也可以在app_dev.php中做出這些改變,即可為dev環(huán)境添加緩存:

// web/app.phpuse Symfony\Component\HttpFoundation\Request;
// ...$kernel = new AppKernel('prod', false);$kernel->loadClassCache(); 
// add (or uncomment) this new line! / 添加下面新行!
// wrap the default AppKernel with the AppCache one
// 用AppCache打包默認(rèn)的AppKernel$kernel = new AppCache($kernel); $request = Request::createFromGlobals(); $response = $kernel->handle($request);$response->send(); $kernel->terminate($request, $response);

上面的緩存核心,將立即作為反向代理來運(yùn)作——從你的程序中緩存響應(yīng),然后把它們返回到客戶端。

如果你正使用framework.http_method_override選項(xiàng),來從_method參數(shù)中讀取HTTP方法,參考上面鏈接來調(diào)整到你需要的程度。

緩存核心有一個(gè)特殊的getLog()方法,返回一個(gè)字符串,用以表明緩存層中到底發(fā)生了什么。在開發(fā)環(huán)境下,可以使用它來除錯(cuò),或者驗(yàn)證你的緩存戰(zhàn)略。

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

AppCache對(duì)象有一個(gè)合適的默認(rèn)配置,但是通過覆寫getOptions()方法來設(shè)置一組選項(xiàng),該對(duì)象即可被精細(xì)調(diào)整。

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


除非在getOptions()方法中進(jìn)行覆寫,否則debub選項(xiàng)將被自動(dòng)設(shè)成“被剝離出來的AppKernel”中的debug值。


下面是一些主要選項(xiàng):

default_ttl

數(shù)值是秒,表達(dá)的是當(dāng)響應(yīng)中沒有提供明確的新鮮度信息時(shí),一個(gè)緩存入口被認(rèn)為是fresh的時(shí)長(zhǎng)。顯式指定Cache-ControlExpires頭,可以覆寫這個(gè)值(默認(rèn)是0)。


private_headers

一組請(qǐng)求頭,在沒有“通過Cache-Control指令(默認(rèn)是AuthorizationCookie)明確聲明當(dāng)前響應(yīng)是public還是private狀態(tài)”的響應(yīng)中,觸發(fā)“private”Cache-Control行為。


allow_reload

指定客戶端是否可以在請(qǐng)求中包容一個(gè)Cache-Control的“no-cache”指令來強(qiáng)制重新加載緩存。設(shè)為true即可遵守RFC2616(默認(rèn)是false)。


allow_revalidate

指定客戶端是否可以在請(qǐng)求中包容一個(gè)來Cache-Control的“max-age=0”來強(qiáng)制重新驗(yàn)證。設(shè)為true即可遵守RFC2616(默認(rèn)是false)。


stale_while_revalidate

指定的默認(rèn)秒數(shù)(以秒為間隔是因?yàn)镽esponse的TTL精度是秒),在此期間,盡管緩存在后臺(tái)對(duì)響應(yīng)正進(jìn)行重新驗(yàn)證,但它能夠立即返回一個(gè)不新鮮的響應(yīng)(默認(rèn)值是2);本設(shè)置可被HTTPCache-Control擴(kuò)展的stale-while-revalidate覆寫(參考RFC 5861)。


stale_if_error

指定的默認(rèn)秒數(shù)(間隔是秒),在此期間,緩存可以對(duì)遇到錯(cuò)誤的響應(yīng)提供服務(wù)(默認(rèn)值是60)。本設(shè)置可被HTTPCache-Control擴(kuò)展的stale-if-error覆寫(參考RFC 5861)。


如果debug被設(shè)為true,Symfony將自動(dòng)添加一個(gè)X-Symfony-Cache頭到響應(yīng)中,里面有關(guān)于緩存命中和丟失的有用信息。

從一個(gè)反向代理切換到另一個(gè)

在開發(fā)網(wǎng)站時(shí),或者在部署網(wǎng)站到“除了php代碼什么都不能安裝”的共享主機(jī)過程中,Symfony的反向代理是個(gè)極為好用的工具。但由于是用PHP寫成,它不如用C寫的代理快。這也是為什么強(qiáng)烈推薦你在生產(chǎn)環(huán)境的服務(wù)器上盡可能地使用Vanish或Squid。好消息是,從一個(gè)代理服務(wù)器切換到另一個(gè)是很容易的,而且過程透明,因?yàn)槟愕某绦蛑胁]有代碼要修改。你可以安心使用Symfony反向代理,日后訪問量增長(zhǎng)時(shí)可以隨時(shí)升級(jí)到Varnish。

Symfony反向代理的性能是獨(dú)立于程序復(fù)雜程度之外的。這是因?yàn)槌绦騼?nèi)核只在“request需要被發(fā)送給它”時(shí)才會(huì)啟動(dòng)。

令你的響應(yīng)成為HTTP緩存 ?

為了利用可用的緩存層,你的程序應(yīng)該與以下信息進(jìn)行通信:1、哪些響應(yīng)可被緩存。2、能夠決定緩存“何時(shí)/如何變成不新鮮”的規(guī)則。

記得,“HTTP”就是一種語(yǔ)言(簡(jiǎn)單文本)而已,被客戶端和和服務(wù)器用來進(jìn)行相互通信之用。而HTTP緩存就是這種語(yǔ)言的一部分,允許客戶端和服務(wù)器交換關(guān)于緩存的信息。

HTTP指定了以下四種用于響應(yīng)的緩存頭:

  • Cache-Control

  • Expires

  • ETag

  • Last-Modified

其中最為重要和功能最強(qiáng)的當(dāng)屬Cache-Control頭,它可說是多種緩存信息的集合。

每種頭都在HTTP Expiration,Validation和Invalidation小節(jié)中進(jìn)行了詳解。

Cache-Control頭 ?

Cache-Control頭是特殊的,它包含不止一條,而是很多條和響應(yīng)的緩存能力相關(guān)的信息。每種信息被以英文逗號(hào)分隔開來:

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

Symfony提供了一個(gè)關(guān)于Cache-Control頭的抽象層,以便令它的創(chuàng)建更加易于管理:

// ... use Symfony\Component\HttpFoundation\Response; $response = new Response();
// mark the response as either public or private 標(biāo)記響應(yīng)是公有還是私有$response->setPublic();$response->setPrivate();
// set the private or shared max age 設(shè)置私有或公有的最大周期$response->setMaxAge(600);$response->setSharedMaxAge(600); 
// set a custom Cache-Control directive 設(shè)置一個(gè)自定義Cache-Control命令$response->headers->addCacheControlDirective('must-revalidate', true)

如果你要為控制器中不同的action設(shè)置緩存頭,你也許需要看看FOSHttpCacheBundle。它提供了一種基于URL模式匹配和其他請(qǐng)求屬性的方式來定義緩存頭。

Public響應(yīng)和Private響應(yīng) ?

不管是gateway還是proxy緩存,都被認(rèn)為是“shared”共享緩存,因?yàn)榫彺鎯?nèi)容被更多用戶分享。如果一個(gè)“特定用戶專有”響應(yīng)被錯(cuò)誤地置于共享緩存中,它可能在后面的時(shí)間里被返回給多位不同用戶。試想你的賬號(hào)信息被緩存,然后發(fā)送給所有后續(xù)請(qǐng)求了自己賬號(hào)頁(yè)面的用戶是個(gè)什么場(chǎng)面!

為應(yīng)對(duì)這種情形,每一個(gè)響應(yīng)應(yīng)當(dāng)被設(shè)為public或private:

public

指示響應(yīng)應(yīng)該被同時(shí)緩存為public和private緩存。


private

指示所有或部分響應(yīng)信息僅針對(duì)某一用戶,因此禁止緩存為public緩存。


Symfony保守的設(shè)置每一次響應(yīng)為private。為了利用好共享緩存(比如Symfony反向代理),響應(yīng)必須顯式設(shè)定為public。

安全方法(Safe Method) ?

HTTP緩存只工作在“安全”HTTP方法下(比如GET或HEAD)。所謂安全,是指你在對(duì)請(qǐng)求提供服務(wù)時(shí)(諸如記錄日志,處理緩存信息等)永遠(yuǎn)不能改變服務(wù)器上的程序狀態(tài)。這就產(chǎn)生兩個(gè)極為有說服力的重要結(jié)論:

  • 你永遠(yuǎn)不應(yīng)該在GET或HEAD請(qǐng)求的響應(yīng)中改變程序狀態(tài)。就算你不使用gateway cache,然而代理緩存的本質(zhì)是,任何GET或HEAD請(qǐng)求,可能或并沒有真正hit到你的服務(wù)器;

  • 不要預(yù)期對(duì)PUT、POST或DELETE方法進(jìn)行緩存。這些方法意味著被用于你的程序狀態(tài)發(fā)生改變時(shí)(比如刪除一篇博客)。緩存它們將阻止特定的請(qǐng)求命中或改變你的程序。

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

HTTP1.1允許默認(rèn)緩存任何內(nèi)容,除非顯式指定了Cache-Control頭。實(shí)踐中,多數(shù)緩存在請(qǐng)求中包含cookie時(shí)、包含authorization頭時(shí)、使用了一個(gè)非安全方法時(shí)(比如PUT、POST或DELETE)或當(dāng)響應(yīng)有一個(gè)重定向狀態(tài)碼時(shí),什么也不做。

當(dāng)開發(fā)者在響應(yīng)頭中什么也沒設(shè)置時(shí),Symfony依據(jù)以下規(guī)則,自動(dòng)設(shè)置了有意義的而且是偏保守的Cache-Header頭。

  • 如果沒有緩存頭信息被定義(Cache-ControlExpires、ETagLast-Modified),Cache-Control將被設(shè)為no-cache,代表響應(yīng)將不被緩存;

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

  • 但是如果至少有一個(gè)Cache-Control指令被設(shè)置,而且沒有publicprivate指令被顯式添加的話,Symfony會(huì)自動(dòng)添加private指令(除了當(dāng)s-maxage被設(shè)置時(shí))

HTTP Expiration,Validation和Invalidation ?

HTTP協(xié)議定義了兩種緩存模型:

  • 利用expiration model(過期模型),通過包容Cache-Control頭和/或Expires頭,即可直接指定一個(gè)響應(yīng)應(yīng)該被認(rèn)為“新鮮”的時(shí)長(zhǎng)。緩存能夠理解過期時(shí)間,不再制造相同請(qǐng)求,直到該緩存版本抵達(dá)過期時(shí)間,而且變得“不新鮮(stale)”。

  • 當(dāng)頁(yè)面是真動(dòng)態(tài)時(shí)(展現(xiàn)層經(jīng)常改變),則validation model(驗(yàn)證模型)的使用就十分有必要。利用這個(gè)模型,緩存把響應(yīng)存儲(chǔ)起來,但會(huì)在每次請(qǐng)求時(shí)向服務(wù)器“提問”——是否緩存了的響應(yīng)仍然有效?程序使用了一個(gè)獨(dú)立的響應(yīng)識(shí)別器(即Etag頭)和/或一個(gè)時(shí)間戳(即Last-Modified頭),來檢查當(dāng)前頁(yè)面自被緩存之后,是否發(fā)生了改變。

理解HTTP Specification

HTTP specification定義了一種簡(jiǎn)單但卻強(qiáng)大的語(yǔ)言,可以令客戶端和服務(wù)器進(jìn)行通信。作為web開發(fā)者,HTTP specification所擁有的request-response model(“請(qǐng)求-響應(yīng)”模型)將支配你的緩存工作。不幸的是,HTTP協(xié)議的真正文檔:RFC2616,是難以讀懂的。

不過有一個(gè)正在進(jìn)行中的HTTP Bis要覆寫RFC 2616。它并不描述新版本的HTTP,更多的是對(duì)原有HTTP協(xié)議進(jìn)行疏理。文檔的組織結(jié)構(gòu)也隨著HTTP協(xié)議被分為七個(gè)部分而有所改進(jìn);關(guān)于HTTP caching的每一部分,都可以在以下兩個(gè)獨(dú)立章節(jié)找到(P4 - Conditional RequestsP6 - Caching: Browser and intermediary caches)。

作為一名web開發(fā)者,你被我們Symfony官方團(tuán)隊(duì) 最強(qiáng)烈力勸 來閱讀HTTP協(xié)議相關(guān)內(nèi)容。它是如此明晰與強(qiáng)大——哪怕是在它被創(chuàng)造出來的10年之后——Http Specification是無價(jià)的。我們特別提醒您,千萬(wàn)別對(duì)這些協(xié)議的表象敷衍了事——其內(nèi)容之美麗,百千萬(wàn)億倍于其封面。

Expiration(過期) ?

expiration model,是兩個(gè)緩存模型里效率更高、更直接的一個(gè),因此應(yīng)該被盡可能多地使用。當(dāng)一個(gè)響應(yīng)通過expiration被緩存時(shí),緩存將保存響應(yīng),并且在過期之前直接返回它,而毋須命中程序。

過期模型,可以通過以下幾乎一樣的兩種HTTP頭之一來實(shí)現(xiàn):ExpiresCache-Control。

使用Expires頭控制過期 ?

根據(jù)HTTP specification,“Expires頭字段將在response被認(rèn)為是stale之后給出date/time?!薄_@里的Expires頭可以被設(shè)為Response方法:setExpires()。它使用DateTime實(shí)例作為參數(shù):

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

該響應(yīng)的HTTP頭信息類似這種:

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

setExpires()方法將自動(dòng)轉(zhuǎn)換日期為GMT時(shí)區(qū),因?yàn)檫@是HTTP specification的要求。

注意,在HTTP 1.1版之前,并不需要原始服務(wù)器來發(fā)送Date頭。因此,緩存(比如瀏覽器的)就需要本地時(shí)鐘來評(píng)估Expires頭,進(jìn)而令緩存周期的計(jì)算因時(shí)間傾斜而變得脆弱不堪。另外一個(gè)Expires頭限制是,正如HTTP協(xié)議中所描述的,“HTTP/1.1 不得發(fā)送Expires的日期超過一年?!?/p>

使用Cache-Control頭控制過期 ?

因?yàn)?code>Expires頭的限制,多數(shù)情況下,你應(yīng)該使用Cache-Control頭來替代。記得,Cache-Control頭被用于多種不同的緩存指令。例如,max-ages-maxage。第一個(gè)用于全部緩存,而第二個(gè)僅在共享緩存時(shí)用到。

// Sets the number of seconds after which the response
// should no longer be considered fresh// 設(shè)置“響應(yīng)過期”的秒數(shù)$response->setMaxAge(600);
 // Same as above but only for shared caches
// 同上,但僅用于共享緩存$response->setSharedMaxAge(600);

Cache-Control頭一般是下述格式(但有時(shí)也會(huì)有其他指令):

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

過期和驗(yàn)證(Expiration and Validation) ?

你當(dāng)然可以對(duì)同一個(gè)Response同時(shí)使用validation和expiration。因?yàn)閑xpiration的優(yōu)勢(shì)大過validation,你能很容易地從兩個(gè)世界中好的一面受益。也就是說,同時(shí)使用過期和驗(yàn)證,你可以命令緩存來服務(wù)于已緩存的內(nèi)容,同時(shí)還能在某些區(qū)間(expiration)向后檢查以確認(rèn)緩存內(nèi)容仍然有效。

你也可以通過annotation來為expiration和validation去定義HTTP緩存頭。參考FrameworkExtraBundle文檔。

更多Response方法 ?

Response類提供了很多方法以應(yīng)對(duì)緩存。下面是幾個(gè)特別有用的:

// Marks the Response stale 標(biāo)記響應(yīng)過期$response->expire(); 
// Force the response to return a proper 304 response with no content
// 強(qiáng)制響應(yīng)返回一個(gè)沒有內(nèi)容的恰當(dāng)?shù)?04響應(yīng)$response->setNotModified();

另外,多數(shù)與緩存相關(guān)的HTTP頭可以單獨(dú)使用setCache()方法來完成設(shè)置:

// Set cache settings in one call$response->setCache(array(
    'etag'          => $etag,
    'last_modified' => $date,
    'max_age'       => 10,
    's_maxage'      => 10,
    'public'        => true,
    // 'private'    => true,
    ));

總結(jié) ?

Symfony的設(shè)計(jì)思想即是遵循業(yè)界公認(rèn)標(biāo)準(zhǔn):HTTP。緩存功能也不例外。掌握Symfony的緩存系統(tǒng)意味著你已然熟悉了HTTP cache模型并且能夠高效地使用它。換句話說,毋須依賴Symfony文檔和例程,你可以馳騁于HTTP caching和以Varnish為代表的gateway caches的世界。