Yahoo的前端優化實踐中有一條是先把html裡的<head>部分先輸出(Flush the Buffer Early),這樣做浏覽器得到head後能先下載head裡的css/js文件,而不用等到整個html下載完了才去下載head裡的css/js,從而提高網頁打開的速度。
http1.1裡增加了一個Transfer-Encoding: chunked報頭,這個報頭的作用可以把報文分成多塊輸出。
報文的格式如下:
Java代碼
Chunked-Body = *chunk
"0" CRLF
footer
CRLF
chunk = chunk-size [ chunk-ext ] CRLF
chunk-data CRLF
hex-no-zero = <HEX excluding "0">
chunk-size = hex-no-zero *HEX
chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-value ] )
chunk-ext-name = token
chunk-ext-val = token | quoted-string
chunk-data = chunk-size(OCTET)
footer = *entity-header
Chunked-Body = *chunk
"0" CRLF
footer
CRLF
chunk = chunk-size [ chunk-ext ] CRLF
chunk-data CRLF
hex-no-zero = <HEX excluding "0">
chunk-size = hex-no-zero *HEX
chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-value ] )
chunk-ext-name = token
chunk-ext-val = token | quoted-string
chunk-data = chunk-size(OCTET)
footer = *entity-header
CRLE:回車換行(\r\n)
例如:
Java代碼
... #很多報頭
Transfer-Encoding: chunked #報頭2個CRLE後開始報文
1 #chunk的大小,十六進制,然後加個CRLE
a #chunk數據,然後加個CRLE
4 #chunk的大小,十六進制,然後加個CRLE
test #chunk數據,可以不斷循環分塊輸出,然後加個CRLE
0 #chunk結束,0 + 2個CRLE
... #很多報頭
Transfer-Encoding: chunked #報頭2個CRLE後開始報文
1 #chunk的大小,十六進制,然後加個CRLE
a #chunk數據,然後加個CRLE
4 #chunk的大小,十六進制,然後加個CRLE
test #chunk數據,可以不斷循環分塊輸出,然後加個CRLE
0 #chunk結束,0 + 2個CRLE
在php裡使用ob_flush後,將自動加上Transfer-Encoding: chunked報頭實現分塊輸出,但是在使用過程中經常達不到效果。不得不考慮一些問題
一、php的緩沖區
如果你的php是以apache模塊運行,那請使用flush函數來通知php輸出。如果是以fastcgi模式運行則使用ob_flush通知php。這時gzip將失效,Chunked方式不支持每塊都獨立壓縮。只能全部輸出壓縮後,將壓縮包分塊輸出。為了保證兼容性,先調用ob_flush,再調用flush。
二、浏覽器的緩沖區
當遇到Transfer-Encoding: chunked報頭後,浏覽器做什麼反應,這個還是要看浏覽器的實現了。在我的實驗中,firefox不管chunk數據大小都會做實時顯示,而ie8和chrome則需要一定長度後才顯示。所以,需要先輸出一定的大小後某些浏覽器才有效果。
三、反向代理服務器
你使用的反向代理服務器支持http1.1協議嗎?它是怎麼處理後端是chunked方式的?proxy緩沖沒滿之前遇到chunked會按照後端來輸出嗎?
nginx的proxy功能只支持http1.0,並且它只有proxy buffer滿了才會輸出。
四、FastCGI緩沖
如果以FastCGI模式運行,可能Web Server有自己的fastcgi緩沖,等待緩沖滿了才輸出(nginx就這樣)。flush函數只能通知php的output緩沖輸出
Chunked transfer encoding
Hypertext Transfer Protocol -- HTTP/1.1 Chunked transfer encoding
深入理解ob_flush和flush的區別