1
包頭header中的,Transfer-Encoding: chunked 表示輸出的內(nèi)容長(zhǎng)度不能確定,普通的靜態(tài)頁(yè)面、圖片之類的基本上都用不到這個(gè)。
但動(dòng)態(tài)頁(yè)面就有可能會(huì)用到,但我也注意到大部分asp,php,http://asp.net**動(dòng)態(tài)頁(yè)面輸出的時(shí)候大部分還是使用Content-Length,沒(méi)有使用Transfer-Encoding: chunked。
不過(guò)如果結(jié)合:Content-Encoding: gzip 使用的時(shí)候,Transfer-Encoding: chunked還是比較有用的。
2
著名的 Yahoo Web 開(kāi)發(fā) 14 條軍規(guī)的第一條就是“減少你的 Http 請(qǐng)求”。當(dāng)然這只是一個(gè)籠統(tǒng)的說(shuō)法,或者是給我們指明一條優(yōu)化的總則。畢竟我們要在“頁(yè)面信息規(guī)?!焙汀癏ttp 請(qǐng)求數(shù)”上做到一個(gè)取舍。不能因?yàn)椤耙獪p少 Http 請(qǐng)求數(shù)”而犧牲了用戶體驗(yàn)。終歸我們是要將信息通過(guò) HTML 頁(yè)面?zhèn)鬟_(dá)給用戶的。 簡(jiǎn)單的說(shuō),如果我們只想簡(jiǎn)單的達(dá)到把信息傳遞給用戶的目的。那我們可以將信息直接輸出到瀏覽器上,可能一個(gè) Http 請(qǐng)求就解決了問(wèn)題。但這樣的信息即便到達(dá)了用戶的瀏覽器,用戶也很難接受。白底黑子,亂呼呼的一堆,你想誰(shuí)會(huì)喜歡看?所以我們?cè)跒橛脩魝鬟_(dá)信息的同時(shí),我們需要加載 CSS、圖片、js腳本等,通過(guò)各種復(fù)雜而生動(dòng)的效果,讓我們想要傳達(dá)的信息更容易的為用戶所接受。而我們要為此付出的代價(jià)就是“增加我們的 Http 請(qǐng)求數(shù)”。 我們需要更好的用戶體驗(yàn),讓用戶看到他們更容易接受的頁(yè)面,這使得我們不斷的增加了我們的 Http 請(qǐng)求數(shù),這與我們的“軍規(guī)”背道而馳,難道就沒(méi)有一個(gè)“兩全其美”的辦法嗎?當(dāng)然是有的。 首先,我們可以充分利用瀏覽器緩存。把 CSS、js腳本和圖片等靜態(tài)文件緩存起來(lái)。當(dāng)瀏覽器再次加載這些資源的時(shí)候,我們可以方便的從瀏覽器緩存中取得這些內(nèi)容。從而大大減少我們發(fā)起的 Http 請(qǐng)求。這是一個(gè)普遍為大家所熟知而又簡(jiǎn)單有效的辦法。但是請(qǐng)注意,瀏覽器緩存只是在我們?cè)俅握?qǐng)求同樣的資源時(shí)才生效。我們暫且稱之為“第二次請(qǐng)求優(yōu)化”,而這無(wú)法解決“首次加載”時(shí)的問(wèn)題。因?yàn)槲覀兊谝淮稳フ?qǐng)求這些原本瀏覽器沒(méi)有緩存的資源時(shí),我們還是要老老實(shí)實(shí)的發(fā)送“Http 請(qǐng)求”的。 針對(duì)我們要研究的“首次加載優(yōu)化”問(wèn)題。我們?cè)谶@里先做一個(gè)假設(shè)。假設(shè)我們要提供一篇文章給用戶。這篇文章分為題目、文章簡(jiǎn)介、章節(jié)索引、章節(jié)內(nèi)容四部分。當(dāng)然我們?yōu)榱宋恼碌拿烙^少不了要寫(xiě) CSS 樣式、準(zhǔn)備一些插圖和通過(guò)一些 JS 腳本控制我們的文章結(jié)構(gòu)。那么當(dāng)用戶發(fā)起請(qǐng)求,向我們“索要”這篇文章時(shí),我們?nèi)绾巫龅娇焖夙憫?yīng),以避免用戶等得不耐煩呢? 我們要展現(xiàn)給用戶的文章是固定的。有題目、文章簡(jiǎn)介、章節(jié)索引、章節(jié)內(nèi)容四部分。從用戶發(fā)起請(qǐng)求想要閱讀這篇文章,到我們提供文章給用戶,為了更為直觀的理解這個(gè)過(guò)程,我們將他們以慢動(dòng)作的形式來(lái)展現(xiàn)。假設(shè)用戶發(fā)起的想要閱讀這篇文章的請(qǐng)求經(jīng)過(guò) 1 秒鐘到達(dá)我們的服務(wù)器。我們的服務(wù)器接收到這個(gè)請(qǐng)求,從數(shù)據(jù)庫(kù)或是其他存儲(chǔ)器中取得這篇文章,并按照題目、文章簡(jiǎn)介、章節(jié)索引、章節(jié)內(nèi)容的結(jié)構(gòu)組裝好,這個(gè)過(guò)程花費(fèi)了 4 秒鐘,然后我們將這篇文章發(fā)送給用戶用了 2 秒鐘。用戶從想要看這篇文章到看到了這篇文章,一共花費(fèi)了 7 秒鐘(當(dāng)然沒(méi)那么夸張,因?yàn)槲覀兊姆?wù)器響應(yīng)和網(wǎng)絡(luò)傳輸基本都在毫秒級(jí),我們只是將這個(gè)過(guò)程以“慢鏡頭”的形式在播放)。 這 7 秒鐘意味著什么?意味著用戶在點(diǎn)擊鼠標(biāo)表達(dá)想要閱讀這篇文章到用戶讀到這篇文章前,他要呆呆的面對(duì)一張白花花的屏幕 7 秒鐘。會(huì)有一部分用戶因?yàn)椴荒蜔┒P(guān)閉頁(yè)面,因?yàn)闆](méi)有幾個(gè)人喜歡對(duì)著白屏發(fā)呆。 而如何在不犧牲用戶體驗(yàn)(該加載的 CSS 樣式、文章插圖和 JS 腳本文件一個(gè)都不能少)的前提下減少用戶的等待時(shí)間,就是我們需要解決的問(wèn)題關(guān)鍵。解決這個(gè)問(wèn)題比較通用的做法就是“分部加載”。就是在用戶發(fā)出閱讀請(qǐng)求的時(shí)候,并不等文章整體加載完成后再呈現(xiàn)給用戶,而是將他們“打碎”。比如我們可以現(xiàn)將文章的題目呈現(xiàn)給用戶,接著是文章簡(jiǎn)介,然后是章節(jié)索引,最后呈現(xiàn)章節(jié)內(nèi)容。通過(guò)這樣“打碎”文章結(jié)構(gòu),將文章一部分一部分的呈現(xiàn)給用戶,我們也就“打碎”了用戶的等待時(shí)間(用戶不再等待 7 秒鐘去看整篇文章,而是等上 1 秒鐘就會(huì)看到文章題目,再等上 1 秒鐘看到文章簡(jiǎn)介......通過(guò)“打碎”等待時(shí)間讓用戶有一種“很快響應(yīng)”的錯(cuò)覺(jué))。 寫(xiě)到這,熟悉 web 開(kāi)發(fā)的讀者可能會(huì)想到“用 Ajax 不就可以了嗎!”。沒(méi)錯(cuò),Ajax 技術(shù)完全可以實(shí)現(xiàn)這種“分部加載”以提升用戶體驗(yàn),“減少”(并不是真正意義上的減少)響應(yīng)延遲時(shí)間。我們可以在用戶請(qǐng)求閱讀這篇文章的時(shí)候,快速的加載一個(gè)體積較小的頁(yè)面,頁(yè)面中含有一些 JavaScript 語(yǔ)句,通過(guò)這些語(yǔ)句分別發(fā)送一些 Ajax 請(qǐng)求,這些請(qǐng)求有的去取文章題目,有的去取文章簡(jiǎn)介......并將取到的內(nèi)容呈現(xiàn)在頁(yè)面上,以減少用戶一次性等待響應(yīng)的時(shí)間。Ajax 是個(gè)不錯(cuò)的辦法,但是想想我們的“軍規(guī)”,我們要“減少 Http 請(qǐng)求數(shù)”!,而 Ajax 在提升用戶體驗(yàn),減少用戶等待的同時(shí)最大的弊病就是增加了大量的“Http 請(qǐng)求次數(shù)”。 除了 Ajax 技術(shù),還有別的辦法嗎?答案是肯定的。接下來(lái)就該是我們的 “Chunk 技術(shù)” 出場(chǎng)的時(shí)間了。Chunk 并不是什么新技術(shù),而是 Http 協(xié)議的一個(gè)編碼方式。簡(jiǎn)單的解釋就是“服務(wù)器生成HTTP回應(yīng)是無(wú)法確定消息大小的,這時(shí)用Content-Length就無(wú)法事先寫(xiě)入長(zhǎng)度,而需要實(shí)時(shí)生成消息長(zhǎng)度,這時(shí)服務(wù)器一般采用Chunked編碼。 在進(jìn)行Chunked編碼傳輸時(shí),在回復(fù)消息的頭部有transfer-coding并定為Chunked,表示將用Chunked編碼傳輸內(nèi)容?!蔽覀兛梢酝ㄟ^(guò) FireBug 等調(diào)試工具查看服務(wù)器的 Http 響應(yīng)頭,看到有 transfer-coding 并且值為 Chunked 的,就表明采用了 Chunk 編碼。 那么 Chunk 和我們所討論的“分部加載”有什么關(guān)系呢?細(xì)心的讀者可能已經(jīng)看出來(lái)了,Http 的 Chunk 編碼實(shí)際上就是一種將 Http 響應(yīng)分部傳送的方法。一個(gè)Http響應(yīng)可以有若干個(gè)Chunk組成,由一個(gè)標(biāo)明長(zhǎng)度為0的chunk結(jié)束,每個(gè)Chunk有兩部分組成,第一部分是該Chunk的長(zhǎng)度和長(zhǎng)度單位(一般不 寫(xiě)),第二部分就是指定長(zhǎng)度的內(nèi)容。而當(dāng)瀏覽器接收到服務(wù)器的響應(yīng)并判定響應(yīng)是 Chunk 編碼的,則瀏覽器可以在每接收一個(gè) Chunk 段完畢時(shí)先行渲染這個(gè) Chunk 段所攜帶的內(nèi)容。這樣就實(shí)現(xiàn)了“分部加載”的目的。 如何應(yīng)用 Chunk 技術(shù)來(lái)進(jìn)行“分部加載”呢?我們以一段 PHP 代碼為例。 <?php $content = array("我是文章題目", "我是文章簡(jiǎn)介", "我是章節(jié)索引", "我是章節(jié)內(nèi)容",); foreach ($content as $c) { echo "<p>$c</p>"; sleep(1); //我們這里故意放慢節(jié)奏,等待一秒 } ?> 假設(shè)我們想服務(wù)器請(qǐng)求一篇文章的時(shí)候由上述 PHP 腳本來(lái)響應(yīng)這次請(qǐng)求。你會(huì)發(fā)現(xiàn),你大概需要等待 4 秒鐘后才會(huì)看到文章的全部?jī)?nèi)容(文章題目、文章簡(jiǎn)介、章節(jié)索引和章節(jié)內(nèi)容)。我們?cè)賮?lái)看下面的這種通過(guò) Chunk 來(lái)“分部加載”的代碼實(shí)現(xiàn): <?php $content = array("我是文章題目", "我是文章簡(jiǎn)介", "我是章節(jié)索引", "我是章節(jié)內(nèi)容",); $buffer_size = 4096; foreach ($content as $c) { echo str_pad( "<p>$c</p>", $buffer_size); ob_flush(); flush(); sleep(1); //我們這里故意放慢節(jié)奏,等待一秒 } ?> 這次我們?cè)賮?lái)試試這段 PHP 代碼作為響應(yīng)文章請(qǐng)求時(shí)的效果。你會(huì)發(fā)現(xiàn),你先看到了文章題目,1 秒鐘后瀏覽器為你呈現(xiàn)了文章簡(jiǎn)介,又 1 秒鐘后是章節(jié)索引,再 1 秒鐘后章節(jié)內(nèi)容也出來(lái)了?!胺植考虞d”的效果出來(lái)了,你再也不同等待 4 秒鐘了! 簡(jiǎn)單的解釋一些上面的代碼。PHP 本身是支持這種通過(guò) Http Chunk 編碼來(lái)響應(yīng)請(qǐng)求的。我們每 echo 一段要輸出的內(nèi)容后,調(diào)用 ob_flush 和 flush 兩個(gè)方法來(lái)清空緩存,這樣 echo 的內(nèi)容會(huì)直接發(fā)送給瀏覽器,達(dá)到“分部”完成響應(yīng)的目的。 最后在說(shuō)明一下“分部加載”時(shí)的 echo 為什么使用了 str_pad 方法。由于 PHP 本身緩存大?。╬hp.ini 中的 ob_buffering),不同 web 服務(wù)器(Nginx、Apache)和瀏覽器不同的關(guān)系,當(dāng)我們 echo 的內(nèi)容過(guò)小時(shí),即使調(diào)用了 ob_flush 和 flush 方法清空輸出緩存,echo 的內(nèi)容也未必會(huì)即時(shí)發(fā)送,這是我們就需要調(diào)用 str_pad 方法來(lái)讓我們 echo 的內(nèi)容“增肥”以達(dá)到占滿緩存的目的。 對(duì)于實(shí)現(xiàn)“分部加載”,Ajax 和 Chunk 都可以達(dá)到同樣的效果。但是由于 Chunk 不會(huì)產(chǎn)生額外的 Http 請(qǐng)求,所以更為輕盈(畢竟發(fā)起 Http 請(qǐng)求也是時(shí)分昂貴的?。?。 Facebook 為我們大家所熟知(雖然我們無(wú)法使用),其用戶個(gè)人主頁(yè)信息量之大,結(jié)構(gòu)之復(fù)雜也可見(jiàn)一斑。Facebook為了減少頁(yè)面的響應(yīng)時(shí)間,發(fā)明了一種他們稱之為“BigPipe”的技術(shù),這個(gè)技術(shù)的核心原理就是 Http 的 Chunk 編碼。據(jù)Facebook的測(cè)試表明“BigPipe”技術(shù)使其頁(yè)面響應(yīng)時(shí)間減少了約 50% (FireFox3.6除外,用 FireFox3.6的響應(yīng)時(shí)間減少約22%),可見(jiàn)很好的利用 Http Chunk 技術(shù),可以大大提高我們 Http 的響應(yīng)速度.
放上 Chunk 技術(shù)和 BigPipe 技術(shù)的相關(guān)文章,剩下的google
Chunk 技術(shù):《flush讓頁(yè)面分塊,逐步呈現(xiàn)》**
Facebook BigPipe 技術(shù) : 《名站技術(shù)分析 — facebook奇特的頁(yè)面加載技術(shù)》**《Facebook創(chuàng)新之BigPipe:優(yōu)化頁(yè)面加載時(shí)間》**《BigPipe: Pipelining web pages for high performance》**