因爲HTTP 1自身的侷限性,它不能很好的爲用戶提供性能良好的WEB服務。於1999年6月正式發佈了HTTP1.1標準REC2616,它釐清了以前版本中不少有歧義的地方,並且還新增了不少重要的優化,如持久鏈接、分塊編碼傳輸、狀態碼擴充、加強的緩存機制、傳輸編碼及請求管道等。本文是我的在學習《WEB性能權威指南》後,又查閱了一些文檔資料寫的一篇隨筆,僅供參考和我的之後查閱。下面將對比http1.0講述一些在新版本中的重要改進。(本文最初發佈於公司內網,外網原文地址:騰雲閣--HTTP 1.1學習筆記)html
每一個TCP鏈接在創建初期都須要進行三次握手,須要經歷一次客戶端與服務器間的完整往返,若是進行數據傳輸的話,至少還須要引起另一次往返。再加上服務器端的處理請求的時間,就是能夠獲得每次請求的總時間。若是每次發送請求都是由新建的TCP鏈接發送的話,它至少須要兩次完整的網絡往返時間,由於每一個TCP鏈接的創建都須要從新進行三次握手。數據庫
那麼,能不能對TCP鏈接進行重用呢?瀏覽器
答案無疑是確定的,HTTP1.1裏添加了持久鏈接的支持,再次發送請求時,能夠直接使用上次已經創建完成的TCP鏈接,這樣就避免了第二次TCP鏈接時的三次握手、消除另外一次TCP慢啓動的往返,極大了減少了網絡延時。若是重用CTP鏈接發送HTTP請求的次數越多,帶來的性能提高越可觀,由於在第一次通過三次握手創建鏈接以後,無需再花費多餘的時間再創建鏈接。緩存
注:目前,全部現代瀏覽器都嘗試持久化HTTP鏈接,若是服務器支持的話。使用HTTP1.1的話,默認的就是持久鏈接;若是使用HTTP 1.0,能夠明確使用connection:keep-alive首部聲明使用持久鏈接;還應注意一些HTTP庫和框架的默認行爲,它們有時候並非默認使用持久鏈接的。安全
持久HTTP可讓咱們重用已有的鏈接來完成屢次請求,但這些請求須要知足先進先出的隊列順序:發送請求--等待響應,再發送下一個請求。HTTP/1.1容許多個http請求經過一個套接字同時被輸出 ,而不用等待相應的響應。而後請求者就會等待各自的響應,這些響應是按照以前請求的順序依次到達。(全部請求保持一個FIFO的隊列,一個請求發送完以後,沒必要等待這個請求的響應被接受到,下一個請求就能夠被再次發出;同時,服務器端返回這些請求的響應時也是按照FIFO的順序)。性能優化
在高延遲和多請求的場景下,經過HTTP管道進行數據傳輸會有更大的性能提高,會節省更多的時間。通過仔細的觀察,能夠發如今HTTP1.1中存在一些侷限,它嚴格串行的返回響應,它不容許多個數據交錯到達(多路複用),只能等待一個響應徹底返回後,下一個響應才能發送,不管下一個響應是否早於前一個響應完成處理,這也叫作隊首阻塞。這就帶來一個很是糟糕的體驗,若是第一個請求須要處理的時間很是長,那麼後續的請求即便被服務器已經處理完成,響應也不能當即返回,而是存儲在服務端的緩存區中,等待第一個響應的完成,才能按照FIFO順序返回。服務器
因爲TCP嚴格按照按順序交付,丟失一個TCP分組就會阻塞全部高序號的分組,除非重傳丟失的分組,這也會帶來額外的延遲。因爲HTTP1.1中不容許多路複用,HTTP管道也會帶來一些不容忽視的問題:cookie
一個慢響應會阻塞全部後續請求;網絡
並行處理請求時,服務器須要緩存處理結果,會佔用服務器資源,若是某個響應很大,很容易造成服務器的受攻擊面;併發
響應失敗可能終止TCP鏈接,會強迫客戶端從新發送對後續資源的請求,致使重複處理;
網絡環境中存在中間代理,檢測管道兼容性十分必要;
若是中間代理不支持管道,那它可能中斷鏈接,也可能把全部請求串聯起來。
正是因爲存在這樣或那樣的問題,HTTP管道技術的應用比較有限,並無大面積推廣開來,即便一些支持它的瀏覽器也僅僅把它做爲一個高級選項。若是你對客戶端和服務端都有很強的控制力,依然可使用它,會帶來不錯的性能提高。若是要在採用這種技術,你須要注意如下事項:
確保HTTP客戶端支持管道;
確保HTTP服務器支持管道;
應用必須處理中斷的鏈接並恢復;
應用必須處理中斷的請求的冪等問題;
應用必須保持自身不受出問題的代理的影響。
實踐中部署HTTP管道的最佳途徑,就是在客戶端和服務端使用HTTPS,這樣就能夠免除不支持管道的中間代理的干擾。
在HTTP/1.0中,使用Expire頭域來判斷資源的fresh或stale,並使用條件請求(conditional request)來判斷資源是否仍有效。
相關字段:
Date: 服務器響應的時間;
Expires: 資源過時時間;
Last-Modified: 資源最後修改時間;
If-Modified-Since: 用來驗證資源是否過時;
大體策略爲:
若是Expires設置的時間在Date以後,則瀏覽器在Expires標記的時間以前都不會訪問服務器了,而是使用瀏覽器緩存;若是Expires設置的時間在Date以前,或者瀏覽器時間已經在Expires以後,那麼再次訪問資源時, 瀏覽器就要向服務器發送請求,但不是從新拉取數據,而是詢問服務器該資源是否過時,方法時,把上次response中Last-Modified的時間做爲If-Modified-Since的時間,發送請求,服務器對比該時間和資源目前的更改時間,若是未更改,則返回304,不然傳輸新文件。此外,HTTP/1.0中還定義了Pragma:no-cache頭域,客戶端使用該頭域說明請求資源不能從cache中獲取,而必須回源獲取。
HTTP/1.0中,If-Modified-Since頭域使用的是絕對時間戳,精確到秒,但使用絕對時間會帶來不一樣機器上的時鐘同步問題,文檔的 更新週期小於1s, 都會出現問題。
爲了提供更好的緩存機制,HTTP1.1對以往的機制進行了一個漸進性的改進。HTTP/1.1提倡的緩存機制是,對比文檔的hash值,文檔內容變,則hash變,用相對時間代替絕對時間,這樣就能夠解決使用絕對時間戳帶來一些問題。
HTTP/1.1 繼承 HTTP/1.0 因此HTTP/1.0的相關字段仍然有效,保留的這些字段就是爲了兼容那些僅支持HTTP/1.0的客戶端。 HTTP/1.1服務器不該該設置與1.0矛盾的過時策略, 1.1的服務器在沒有文檔hash值時,也可使用If-Modified-Since進行判斷文檔過時。
HTTP1.1中新增瞭如下字段:
Cache-Control: 用來控制瀏覽器的緩存行爲;
ETag: 文檔的Hash值;
If-None-Match: 用來驗證資源是否過時,即文檔Hash值是否變化。
而HTTP/1.1中引入了一個ETag頭域用於重激活機制,它的值ETag能夠用來惟一的描述一個資源。請求消息中可使用If-None-Match頭域來匹配資源的entitytag是否有變化。
爲了使caching機制更加靈活,HTTP/1.1增長了Cache-Control頭域(請求消息和響應消息均可使用),它支持一個可擴展的指令子集:例如max-age指令支持相對時間戳;private和no-store指令禁止對象被緩存;no-transform阻止Proxy進行任何改變響應的行爲;no-cache瀏覽器緩存,可是認爲是過時緩存;no-store瀏覽器不緩存;max-age:緩存有效時間段等等。
若是想要瀏覽器每次發送請求,還啓用緩存,那就使用Cache-Control: no-cache, 每次訪問圖片,瀏覽器都會去驗證Etag。
Cache使用關鍵字索引在磁盤中緩存的對象,在HTTP/1.0中使用資源的URL做爲關鍵字。但可能存在不一樣的資源基於同一個URL的狀況,要區別它們還須要客戶端提供更多的信息,如Accept-Language和Accept-Charset頭域。爲了支持這種內容協商機制(content negotiation mechanism),HTTP/1.1在響應消息中引入了Vary頭域,該頭域列出了請求消息中須要包含哪些頭域用於內容協商。
HTTP消息中能夠包含任意長度的實體,一般它們使用Content-Length來給出消息結束標誌。可是,對於不少動態產生的響應,只能經過緩衝完整的消息來判斷消息的大小,但這樣作會加大延遲。若是不使用長鏈接,還能夠經過鏈接關閉的信號來斷定一個消息的結束。
HTTP/1.1中引入了Chunke dtransfer-coding來解決上面這個問題,發送方將消息分割成若干個任意大小的數據塊,每一個數據塊在發送時都會附上塊的長度,最後用一個零長度的塊做爲消息結束的標誌。這種方法容許發送方只緩衝消息的一個片斷,避免緩衝整個消息帶來的過載。
在HTTP/1.0中,有一個Content-MD5的頭域,要計算這個頭域須要發送方緩衝完整個消息後才能進行。而HTTP/1.1中,採用chunked分塊傳遞的消息在最後一個塊(零長度)結束以後會再傳遞一個拖尾(trailer),它包含一個或多個頭域,這些頭域是發送方在傳遞完全部塊以後再計算出值的。發送方會在消息中包含一個Trailer頭域告訴接收方這個拖尾的存在。
狀態碼是試圖理解和知足請求的三位數字的整數碼。HTTP/1.0中只定義了16個狀態響應碼,對錯誤或警告的提示不夠具體。HTTP/1.1引入了一個Warning頭域,增長對錯誤或警告信息的描述。
截止到HTTP1.1中包含的狀態碼大體以下:
狀態代碼 狀態信息 含義
100 Continue 初始的請求已經接受,客戶應當繼續發送請求的其他部分。(HTTP 1.1新)
101 Switching Protocols 服務器將聽從客戶的請求轉換到另一種協議(HTTP 1.1新)
200 OK 一切正常,對GET和POST請求的應答文檔跟在後面。
201 Created 服務器已經建立了文檔,Location頭給出了它的URL。
202 Accepted 已經接受請求,但處理還沒有完成。
203 Non-Authoritative Information 文檔已經正常地返回,但一些應答頭可能不正確,由於使用的是文檔的拷貝(HTTP 1.1新)。
204 No Content 沒有新文檔,瀏覽器應該繼續顯示原來的文檔。若是用戶按期地刷新頁面,而Servlet能夠肯定用戶文檔足夠新,這個狀態代碼是頗有用的。
205 Reset Content 沒有新的內容,但瀏覽器應該重置它所顯示的內容。用來強制瀏覽器清除表單輸入內容(HTTP 1.1新)。
206 Partial Content 客戶發送了一個帶有Range頭的GET請求,服務器完成了它(HTTP 1.1新)。
300 Multiple Choices 客戶請求的文檔能夠在多個位置找到,這些位置已經在返回的文檔內列出。若是服務器要提出優先選擇,則應該在Location應答頭指明。
301 Moved Permanently 客戶請求的文檔在其餘地方,新的URL在Location頭中給出,瀏覽器應該自動地訪問新的URL。
302 Found 相似於301,但新的URL應該被視爲臨時性的替代,而不是永久性的。注意,在HTTP1.0中對應的狀態信息是「Moved Temporatily」。
出現該狀態代碼時,瀏覽器可以自動訪問新的URL,所以它是一個頗有用的狀態代碼。
注意這個狀態代碼有時候能夠和301替換使用。例如,若是瀏覽器錯誤地請求 http://host/~user (缺乏了後面的斜槓), 有的服務器返回301,有的則返回302。
嚴格地說,咱們只能假定只有當原來的請求是GET時瀏覽器纔會自動重定向。請參見307。
303 See Other 相似於301/302,不一樣之處在於,若是原來的請求是POST,Location頭指定的重定向目標文檔應該經過GET提取(HTTP 1.1新)。
304 Not Modified 客戶端有緩衝的文檔併發出了一個條件性的請求(通常是提供If-Modified-Since頭表示客戶只想比指定日期更新的文檔)。服務器告訴客戶,原來緩衝的文檔還能夠繼續使用。
305 Use Proxy 客戶請求的文檔應該經過Location頭所指明的代理服務器提取(HTTP 1.1新)。
307 Temporary Redirect 和302(Found)相同。許多瀏覽器會錯誤地響應302應答進行重定向,即便原來的請求是POST,即便它實際上只能在POST請求的應答是303時 才能重定向。因爲這個緣由,HTTP 1.1新增了307,以便更加清除地區分幾個狀態代碼:當出現303應答時,瀏覽器能夠跟隨重定向的GET和POST請求;若是是307應答,則瀏覽器只能跟隨對GET請求的重定向。(HTTP 1.1新)
400 Bad Request 請求出現語法錯誤。
401 Unauthorized 客戶試圖未經受權訪問受密碼保護的頁面。應答中會包含一個WWW-Authenticate頭,瀏覽器據此顯示用戶名字/密碼對話框,而後在填寫合適的Authorization頭後再次發出請求。
403 Forbidden 資源不可用。服務器理解客戶的請求,但拒絕處理它。一般因爲服務器上文件或目錄的權限設置致使。
404 Not Found 沒法找到指定位置的資源。這也是一個經常使用的應答。
405 Method Not Allowed 請求方法(GET、POST、HEAD、DELETE、PUT、TRACE等)對指定的資源不適用。(HTTP 1.1新)
406 Not Acceptable 指定的資源已經找到,但它的MIME類型和客戶在Accpet頭中所指定的不兼容(HTTP 1.1新)。
407 Proxy Authentication Required 相似於401,表示客戶必須先通過代理服務器的受權。(HTTP 1.1新)
408 Request Timeout 在服務器許可的等待時間內,客戶一直沒有發出任何請求。客戶能夠在之後重複同一請求。(HTTP 1.1新)
409 Conflict 一般和PUT請求有關。因爲請求和資源的當前狀態相沖突,所以請求不能成功。(HTTP 1.1新)
410 Gone 所請求的文檔已經再也不可用,並且服務器不知道應該重定向到哪個地址。它和404的不一樣在於,返回407表示文檔永久地離開了指定的位置,而404表示因爲未知的緣由文檔不可用。(HTTP 1.1新)
411 Length Required 服務器不能處理請求,除非客戶發送一個Content-Length頭。(HTTP 1.1新)
412 Precondition Failed 請求頭中指定的一些前提條件失敗(HTTP 1.1新)。
413 Request Entity Too Large 目標文檔的大小超過服務器當前願意處理的大小。若是服務器認爲本身可以稍後再處理該請求,則應該提供一個Retry-After頭(HTTP 1.1新)。
414 Request URI Too Long URI太長(HTTP 1.1新)。
416 Requested Range Not Satisfiable 服務器不能知足客戶在請求中指定的Range頭。(HTTP 1.1新)
500 Internal Server Error 服務器遇到了意料不到的狀況,不能完成客戶的請求。
501 Not Implemented 服務器不支持實現請求所須要的功能。例如,客戶發出了一個服務器不支持的PUT請求。
502 Bad Gateway 服務器做爲網關或者代理時,爲了完成請求訪問下一個服務器,但該服務器返回了非法的應答。
503 Service Unavailable 服務器因爲維護或者負載太重未能應答。例如,Servlet可能在數據庫鏈接池已滿的狀況下返回503。服務器返回503時能夠提供一個Retry-After頭。
504 Gateway Timeout 由做爲代理或網關的服務器使用,表示不能及時地從遠程服務器得到應答。(HTTP 1.1新)
505 HTTP Version Not Supported 服務器不支持請求中所指明的HTTP版本。(HTTP 1.1新)
在HTTP1.0中認爲每臺服務器都綁定一個惟一的IP地址,所以,請求消息中的URL並無傳遞主機名(hostname)。但隨着虛擬主機技術的發展,在一臺物理服務器上能夠存在多個虛擬主機(Multi-homed Web Servers),而且它們共享一個IP地址。因爲HTTP 1.0不支持Host請求頭字段,WEB瀏覽器沒法使用主機頭名來明確表示要訪問服務器上的哪一個WEB站點,這樣就沒法使用WEB服務器在同一個IP地址和端口號上配置多個虛擬WEB站點。在HTTP 1.1中增長Host請求頭字段後,WEB瀏覽器可使用主機頭名來明確表示要訪問服務器上的哪一個WEB站點,這才實現了在一臺WEB服務器上能夠在同一個IP地址和端口號上使用不一樣的主機名來建立多個虛擬WEB站點。
客戶程序向服務器發送的請求能夠有不一樣的類型,這樣服務器能夠根據不一樣的請求類型進行不一樣的處理。在HTTP1.0中,定義了三種最基本的請求類 型,GET、POST和HEAD,當使用GET和POST方法時,服務器最後都將結果文檔返回給客戶程序,瀏覽器將刷新顯示;而HEAD請求則不一樣,HEAD請求在客戶程序和服務器之間進行交流,而不會返回具體的文檔,它僅僅交流一些內部數據,這些數據不會影響瀏覽的過程。所以HEAD方法一般不單獨使用,而是和其餘的請求方法一塊兒起到輔助做用。一些搜尋引擎使用的自動搜索機器人使用這個方法來得到網頁的標誌信息,或者進行安全認證時,使用這個方法來傳遞認證信息。
除了上述三種最多見的訪問方法以外,在HTTP1.1中還定義了更多的訪問方法類型,具體以下:
PUT:向指定資源位置上傳其最新內容;
DELETE:請求服務器刪除Request-URI所標識的資源;
OPTIONS:返回服務器針對特定資源所支持的HTTP請求方法。也能夠利用向Web服務器發送'*'的請求來測試服務器的功能性;
TRACE:回顯服務器收到的請求,主要用於測試或診斷;
CONTENT:HTTP/1.1協議中預留給可以將鏈接改成管道方式的代理服務器。
這些方法並不經常使用,於是大部分Web服務器軟件並無實現他們。然而對於特定場合他們仍是很是有用的,若是服務器不支持客戶發送的請求方法,服務器將返回錯誤並當即關閉鏈接。
HTTP/1.0中,存在一些浪費帶寬的現象,例如客戶端只是須要某個對象的一部分,而服務器卻將整個對象送過來了,又好比下載大文件時須要支持斷點續傳功能,而不是在發生斷連後不得不從新下載完整的包。HTTP/1.1中在請求消息中引入了range頭域,它容許只請求資源的某個部分。在響應消息中Content-Range頭域聲明瞭返回的這部分對象的偏移值和長度。若是服務器相應地返回了對象所請求範圍的內容,則響應碼爲206(Partial Content),它能夠防止Cache將響應誤覺得是完整的一個對象。
另一種狀況是請求消息中若是包含比較大的實體內容,但不肯定服務器是否可以接收該請求(如是否有權限),此時若貿然發出帶實體的請求,若是被拒絕也會浪費帶寬。HTTP/1.1加入了一個新的狀態碼100(Continue)。客戶端事先發送一個只帶頭域的請求,若是服務器由於權限拒絕了請求,就回送響應碼401(Unauthorized);若是服務器接收此請求就回送響應碼100,客戶端就能夠繼續發送帶實體的完整請求了。注意,HTTP/1.0的客戶端不支持100響應碼。但可讓客戶端在請求消息中加入Expect頭域,並將它的值設置爲100-continue。
節省帶寬資源的一個很是有效的作法就是壓縮要傳送的數據。Content-Encoding是對消息進行端到端(end-to-end)的編碼,它多是資源在服務器上保存的固有格式(如jpeg圖片格式);在請求消息中加入Accept-Encoding頭域,它能夠告訴服務器客戶端可以解碼的編碼方式。而Transfer-Encoding是逐段式(hop-by-hop)的編碼,如Chunked編碼。在請求消息中加入TE頭域用來告訴服務器可以接收的transfer-coding方式。
上文已經說明,HTTP1.X並不支持多路複用,請求須要在客戶端排隊等待發送,並且容易遇到隊首阻塞的問題,並不能很好的提升數據傳輸速率。那麼,既然不能對單一鏈接進行多路複用,那是否是能夠同時打開多個鏈接進行數據傳輸呢?答案是確定的,瀏覽器開發商爲了解決這個問題,使瀏覽器支持客戶端最多打開六個鏈接,這樣咱們就能夠更快速的進行通訊。任何事情都兩面性,同時打開多個鏈接勢必帶來一些優化和問題,具體以下:
優勢:
客戶端能夠並行發起多個請求;
服務器能夠並行處理多個請求;
第一次往返能夠發送的累計分組數量是原來的6倍;
缺點:
更多的套接字會佔用更多的資源;
並行TCP流之間競爭共享的帶寬;
處理多個套接字,實現更爲複雜;
即便並行TCP流,應用的併發能力也受限制。
這種打開多個鏈接的方式,也帶來了一些壞處,那爲何如今還使用的如此普遍呢?主要由如下三個緣由:
做爲繞過HTTP限制的一個權宜之計;
做爲繞過TCP中低起始擁塞窗口的一個權宜之計;
做爲客戶端繞過不能使用TCP窗口縮放的一個權益之計。
因爲HTTP1.1協議不支持多路複用,迫使瀏覽器開發商爲了提升通訊效率,引入並維護着鏈接池,每一個主機能夠有6個TCP流。根據HTTP Archive統計,目前平均每一個頁面要包含90個左右的資源,若是這些資源都來自於同一個主機,即便能夠同時打開6個TCP流,依然會致使明顯的排隊情形。咱們並不須要把全部的資源都放在同一個主機上,能夠分開放置到不一樣的域名下,這樣就能夠增長能夠同時打開的TCP流總數,能夠突破瀏覽器的鏈接限制,實現更高的併發能力。
理論上來講,使用的域名越多,並行能力也就越強。可是,在發送請求以前都須要進行DNS解析,不一樣的域名須要分別進行解析,都須要進行額外的DNS查詢,若是域名數量過多,會致使大量的額外解析;在TCP鏈接中存在的慢啓動機制,有時候也會下降性能;並且每多一個套接字都須要客戶端和服務端消耗資源進行維護;更糟糕的是,開發者須要手動的把這些資源進行分區,部署到不一樣的域名下。域名分區的數量太大或過小都會影響性能,但如何肯定最優的分區數量並是個很好回答的問題,由於頁面中資源的數量、客戶端鏈接的可用帶寬及延遲等都會影響分區數量的合理性。
要想肯定合適的域名分區數量,只能用最原始的方式從最小分區開始不斷的測試,觀察不一樣分區數目對應用的影響,而後選擇最優的一個值做爲固定分區數目。
最快的請求就是不用請求,無論什麼協議或什麼類型的應用,減小請求次數老是最好的優化手段。若是每一個資源都是必不可少的,那你能夠考慮把這些資源打包到一塊,經過一次網絡請求獲取。鏈接和拼合技術屬於之內容爲中心的應用層優化,經過減小網絡往返開銷,能夠明顯的提高性能。但是這些技術也須要額外的處理、部署和編碼,也會帶來額外的複雜性。把多個資源打包到一塊兒,也可能給緩存帶來負擔,一些細微的更新都須要從新請求資源並緩存,有時候當前頁面並不須要文件中的其餘一些資源,這都會影響頁面的執行速度。
在採用鏈接與合併技術時,雖然會減小網絡往返開銷和提高性能,但也會增長應用的複雜度,以至緩存、更新、執行速度、渲染頁面等問題。因此,採用這種優化時,應綜合考慮,尋求一種最佳的文件打包粒度。
嵌入資源也是一種很常見的優化方法,把資源嵌入文檔能夠減小請求的次數。嵌入頁面的資源適合特別小的,使用次數不多,最好是一次的資源。實踐中,一個經驗規則是隻考慮嵌入1-2KB如下的資源,由於小於這個標準的資源會致使比它自身更高的HTTP開銷。然而,若是嵌入資源頻繁變動,也會致使宿主文檔的無效緩存率升高。若是應用中要使用很小的、個別的文件,在考慮是否嵌入時,能夠參考如下建議:
若是文件很小,並且只有個別頁面使用,能夠考慮嵌入;
若是文件很小,但須要在多個頁面中使用,應該考慮集中打包;
若是文件常常須要更新,就不要嵌入了;
經過減小HTTP cookie的大小將協議開銷最小化。
參考資料:
《WEB性能權威指南》
《HTTP/1.1與HTTP/1.0的區別》
《HTTP 1.1與HTTP 1.0的比較》
http://www.faqs.org/rfcs/rfc1945.html
http://www.faqs.org/rfcs/rfc2616.html