HTTP協議理解與應用總結

總結了自己在實際工作場景中遇到的與http協議相關的一些內容的理解。

Request & Response

Request格式

<request-line> 比如:GET /api/index.json HTTP/1.1
<headers> 比如:Accept: */*; User-Agent: Mozilla/4.0;……
<blank line>
[<request-body>] 比如:id=1&timestamp=xxxxxx

Response格式

<status-line> 比如:HTTP/1.1 200 OK
<headers> 比如:Content-Type: application/json;……
<blank line>
[<response-body>] 比如:{"id":1,"username":"testuser"}

Status Code

http的狀態碼有將近60個,我這裏主要記錄一些常見的非正常情況下產生的狀態碼,在平常應用中或多或少會碰到,有助於我們去理解和發現問題。

206 – 斷點下載時用到,客戶端請求了一部分內容,服務器成功把這部分內容返回給它,這時候就是用這個狀態。

301 – 永久跳轉,原地址不存在了,url被指向到另一個地址。這個主要是搜索引擎相關,影響爬蟲的檢索行爲。

302 – 臨時跳轉,服務器會返回一個新的url給客戶端,客戶端可以繼續訪問這個url來獲取內容。

304 – 資源沒有改變,客戶端可以使用本地緩存的內容,常見於靜態內容訪問。

413 – 請求實體太大。常見的情況是上傳大文件,但超出了服務器(比如nginx)限制。或者請求頭或請求體超出了後端的server(比如tomcat)的設置(比如當前域名下cookie太多,超出了請求頭限制)

416 – 跟斷點續傳有關,客戶端請求的範圍超出了服務器上文件大小。

500 – 服務器內部錯誤,不能返回正常的結果。比如最常見的應用拋出空指針異常未進行處理。

502 – 網關錯誤。常見的情況是反向代理後端的服務器(比如resin或tomcat)沒有啓動。

503 – 服務不可用。比如服務器負載太高或者服務器已經停止服務。

504 – 網關超時。比如請求時長超出了服務器的響應時間限制。

Headers

http headers分爲請求頭(Request Header)和響應頭(Response Header)兩類。下面是我們經常用到的一些header.

1.緩存控制

在互聯網站的應用中,緩存幾乎無處不在,在基於http的服務中,我們也可以對一些不常改變的內容在客戶端進行緩存,這樣在多次訪問中可以複用緩存內容,加快訪問速度,提升用戶體驗。http的協議裏規定了一些用於緩存控制的http消息頭:

Cache-Control(HTTP/1.1)/Pragma(HTTP/1.0):指示客戶端是否進行緩存以及緩存的時間是多長。默認值是private,也就是把內容緩存在用戶私有空間。比如:Cache-Control:max-age=86400,must-revalidate,這是告訴客戶端所請求的資源緩存一天(max-age單位是秒,相對時間),過期之後必須進行重新檢驗。

Expires:指定客戶端(如果不強制刷新的話)在多長時間裏可以不向服務器發請求,直接讀本地緩存。

注意:

  1. 優先級:Cache-Control > Expires;

  2. 詳細參數說明:http://condor.depaul.edu/dmumaugh/readings/handouts/SE435/HTTP/node24.html

  3. 不同瀏覽器的不同行爲(刷新,後退,地址欄回車等)在實現上可能有差異;

Last-Modified/If-Modified-Since:Last-Modified是服務器端返回給客戶端的資源最後修改時間戳,這樣,客戶端在下次請求時(比如強制刷新)會帶上If-Modified-Since參數來校驗資源是否有更新,沒有更新的話服務器就返回304狀態碼,客戶端直接取本地緩存的資源。這個時候只有請求開銷,沒有網絡傳輸開銷。注意:時間戳必須是格林威治(GMT)時間,比如:Last-Modified:Sat, 19 Oct 2013 09:20:15 GMT

ETag/If-None-Match:ETag是根據文件屬性通過一定算法生成的資源標識,也是用來確定客戶端請求的資源是否有更新。如果服務器返回了一個ETag值給客戶端,那麼下次客戶端請求時會帶上If-None-Match參數來校驗資源是否更新,沒有更新的就返回304狀態碼。(效果基本等同於Last-Modified)

注意:

  1. ETag需要計算,對於計算資源緊張的服務器來說是一種消耗,所以有些網站直接不使用ETag;

  2. 如果服務器在負載均衡後面,同一個資源的請求可能分發到不同的後端機器上,由於ETag的計算依賴於文件屬性,不同機器上內容相同的文件可能生成的ETag不同,這樣就可能使本來內容沒變的文件通過ETag校驗失敗。這裏有兩種解決方案:一是etag計算不依賴於本地機器,比如直接算文件內容的md5值;二是在負載均衡器上把相同的url請求分發到同一臺後端機器。

在我們的實際業務場景下,http的緩存具有非常大的用途,下面列舉一些:

  1. 充分利用客戶端的資源,比如一些客戶端需要頻繁訪問的靜態文件,像LOGO,廣告圖等,完全可以緩存在客戶端本地。這樣可以減少網絡請求,加快客戶端展示,還能減少服務器請求的壓力。

  2. 我們的一些靜態內容,比如新聞,博客等,在被搜索引擎爬蟲抓取的時候,通過控制緩存參數,就可以減少爬蟲的抓取頻率,減少不必要的資源浪費。

  3. 如果我們的靜態資源使用了CDN,那麼設置了http緩存就可以在CDN節點上保存一份文件,減少CDN的回源次數,減少網絡延時和源站服務器壓力。

2.斷點請求

Accept-Ranges:服務端支持斷點下載時會返回這個響應頭給客戶端,當客戶端知道這個以後就可以發送斷點請求了。

Content-Length:響應信息的長度,告訴客戶端當前請求返回了多少數據。這裏要注意一下,用head方法提交請求時不會返回具體數據,但是這個Content-Length會返回完整數據的大小。

Range/Content-Range:客戶端請求時提交名爲Range的header,告訴服務器自己要請求哪部分的數據。比如:Range: bytes=0-1023表示請求第0到1023個字節.然後服務器返回這1024個字節的內容給客戶端,響應頭中會帶上Content-Range。即:Content-Range: bytes 0-1023/4096,這個4096就是文件總大小。客戶端下次請求可以從第1024個字節處開始,Range: bytes=1024-xxxx

3.編碼

Accept-Encoding/Content-Encoding:前者是客戶端支持接收的消息編碼類型。默認是identity,可選值有gzip,compress等。後者是服務器端響應信息的內容編碼類型,常用的就是壓縮。壓縮的好處顯而易見,可以大大減少網絡傳輸的開銷,相對於服務器端壓縮產生的cpu消耗,網絡傳輸的減少顯然更實在。常見形式:Content-Encoding: gzip,deflate,compress.通常我們對html,js,css,xml,json之類的響應結果可以進行壓縮傳輸。

Transfer-Encoding:response header.響應消息的傳輸編碼類型,規定了網絡傳輸的形式。一般都是下面這種形式:Transfer-Encoding: chunked。當服務器產生動態內容,不知道響應信息的具體長度時,可以通過這個指定分塊進行傳輸,處理多少數據就返回多少數據,這樣不用等到數據都準備好了一次性返回。結合上面的內容編碼,比如gzip,可以分塊壓縮並進行傳輸。另外,請注意,在使用這種編碼傳輸時,我們是看不到Content-Length的,因爲內容還沒有完全生成。

4.其他

X-Forward-For:request header. 用來標識用戶的真實ip,特別是通過代理(正向或反向)訪問服務器或是服務器在負載均衡設備後面的情況。格式:X-forward-For: client,proxy1,proxy2,…最左邊的是最接近客戶端的ip。

User-Agent:request header.服務器用來識別客戶端基本信息的請求頭。一般這個在識別搜索爬蟲的時候有用,某些場景下也可以用這個來做一些客戶端的統計。

Referer:request header.客戶端訪問服務器時,這個Referer來指定請求來源,比如是從哪個網站鏈接過來的,我們在一些統計中會經常用到這個。另外,還有一個重要的用途就是在需要資源防盜鏈的場景中來過濾非法的請求來源(但是,這個referer是客戶端可以僞造的)。

Location:response header.在301/302狀態碼的響應頭中,都會帶上這個Location頭,來指示客戶端用新的地址去訪問需要的資源。

Connection:request/response header.在http/1.1中,客戶端和服務端默認都是保持連接的,也就是Connection: keep-alive.如果任何一方不想保持連接,都可以把這個值設置爲close.默認情況下,客戶端和服務端會保持一個長連接,這樣客戶端就可以用這個連接發送多次http請求,減少頻繁創建連接帶來的消耗。對於這個參數,在服務端可能要做更多的設置,比如連接keep-alive的時間,服務器內核的一些網絡參數設置(針對tcp)。

Session和Cookie

http請求是無狀態的請求,但是在我們的互聯網應用中,經常需要標識用戶狀態信息來完成一些交互性的操作,比如用戶認證要記錄用戶登錄狀態,購物車應用要記住用戶選擇的商品,廣告投放應用要記錄用戶的歷史瀏覽行爲等等。這裏就會用到session和cookie了。

session:是指http請求-響應的過程中客戶端與服務器端的交互狀態,這些信息被保存在服務器端,比如內存,數據庫等。每個session都有一個唯一標識,由服務器生成,這個標識也要在客戶端進行保存,這樣客戶端在下次請求時可以帶上這個標識,方便服務器判斷客戶端的狀態。

客戶端對session的支持:

  1. 通過cookie保存session id,在請求時發送給服務器。

  2. 通過url的參數攜帶session id與服務器通信。

  3. 通過表單的隱藏字段攜帶session id與服務器通信。

session共享的問題:

在分佈式應用中,我們的http server一般都架在反向代理或是負載均衡設備後面,這就會面臨一個session共享的問題。也就是同一個用戶的多個請求可能被分發到多個不同的機器,如果我們把session保存在機器本地內存中的話,就無法在多個機器間共享用戶的session。這個問題,一般來說,我們可以有兩種方式來解決:

  1. 把session存放到分佈式的內存(eg:memcached)或是集中式存儲中(eg:database)。

  2. 在反向代理或負載均衡設備上把相同用戶的請求分發到同一臺機器(這裏要處理好機器宕機後請求重新分配的問題)。

cookie:在客戶端保持狀態化信息,每個cookie內容都屬於特定的域(domain)和路徑(path),出於安全考慮,不同域或路徑下的cookie不能共享。

會話cookie:沒有指定過期時間,保存在內存,瀏覽器關閉後就失效。

持久cookie:指定了過期時間,保存在瀏覽器本地。

詳細內容可以參考:http://en.wikipedia.org/wiki/HTTP_cookie

需要注意的是cookie會存在一些安全方面的問題。

在這裏我只是總結了自己在工作中遇到的與http協議相關的一些內容的理解,http協議還有很多需要挖掘的東西,也需要不斷去探索,對http協議的理解將會給我們的開發應用帶來很大的便利。

最後,推薦兩個很NB的http調試工具:fiddler(windows)和charles(mac)有http代理功能,對於不是基於瀏覽器的http應用(比如mobile app),可以用這兩個工具來監控http請求。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章