總結了自己在實際工作場景中遇到的與http協議相關的一些內容的理解。
Request & Response
Request格式
#
#
[
Response格式
#
#
[
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:指定客戶端(如果不強制刷新的話)在多久裡可以不向伺服器發送請求,直接讀取本地快取。
注意:
優先權:Cache-Control > Expires;
詳細參數說明:http://condor.depaul.edu/dmumaugh/readings/handouts/ SE435/HTTP/node24.html
不同瀏覽器的不同行為(刷新,後退,網址列回車等)在實作上可能有差異;
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)
注意:
ETag需要計算,對於計算資源緊張的伺服器來說是一種消耗,所以有些網站直接不使用ETag;
如果伺服器在負載平衡後面,同一個資源的請求可能會分發到不同的後端機器上,由於ETag的計算依賴於檔案屬性,不同機器上內容相同的檔案可能產生的ETag不同,這樣就可能讓本來內容沒變的檔案透過ETag校驗失敗。這裡有兩種解決方案:一是etag運算不依賴本機,例如直接算檔內容的md5值;二是在負載平衡器上把相同的url請求分發到同一台後端機器。
在我們的實際業務場景下,http的快取具有非常大的用途,以下列舉一些:
充分利用客戶端的資源,例如一些客戶端需要頻繁存取的靜態文件,像LOGO,廣告圖等,完全可以快取在客戶端本地。這樣可以減少網路請求,加快客戶端展示,還能減少伺服器請求的壓力。
我們的一些靜態內容,例如新聞,部落格等,在被搜尋引擎爬蟲抓取的時候,透過控制快取參數,就可以減少爬蟲的抓取頻率,減少不必要的資源浪費。
如果我們的靜態資源使用了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的支援:
透過cookie儲存session id,在請求時傳送給伺服器。
透過url的參數攜帶session id與伺服器通訊。
透過表單的隱藏欄位攜帶session id與伺服器通訊。
session共享的問題:
在分散式應用程式中,我們的http server一般都架在反向代理或是負載平衡設備後面,這就會面臨一個session共享的問題。也就是同一個使用者的多個請求可能被分發到多個不同的機器,如果我們把session保存在機器本地記憶體中的話,就無法在多個機器間共享用戶的session。這個問題,一般來說,我們可以有兩種方式來解決:
把session存放到分佈式的記憶體(eg:memcached)或集中式儲存(eg:database)。
在反向代理或負載平衡設備上把相同使用者的請求分發到同一台機器(這裡要處理好機器宕機後請求重新分配的問題)。
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請求。
以上是HTTP協定相關介紹及深度理解的詳細內容。更多資訊請關注PHP中文網其他相關文章!