當前位置: 華文星空 > 知識

HTTP是一個無狀態的協定。這句話裏的無狀態是什麽意思?

2014-03-29知識

驚訝的發現這個問題已經有好幾個大V回答過了,但其實都不太準確。HTTP所謂的「無狀態協定」,其實跟Cookies、Session這些都沒有什麽大的聯系,它描述的主要是通訊協定層面的問題。

常見的許多七層協定實際上是有狀態的,例如SMTP協定,它的第一條訊息必須是HELO,用來握手,在HELO發送之前其他任何命令都是不能發送的;接下來一般要進行AUTH階段,用來驗證使用者名稱和密碼;接下來可以發送信件數據;最後,透過QUIT命令結束。可以看到,在整個傳輸層上,通訊的雙方是必須要時刻記住當前連線的狀態的,因為不同的狀態下能接受的命令是不同的;另外,之前的命令傳輸的某些數據也必須要記住,可能會對後面的命令產生影響。這種就叫做有狀態的協定。

相反,為什麽說HTTP是無狀態的協定呢?因為它的每個請求都是完全獨立的,每個請求包含了處理這個請求所需的完整的數據,發送請求不涉及到狀態變更。即使在HTTP/1.1上,同一個連線允許傳輸多個HTTP請求的情況下,如果第一個請求出錯了,後面的請求一般也能夠繼續處理(當然,如果導致協定解析失敗、訊息分片錯誤之類的自然是要除外的)可以看出,這種協定的結構是要比有狀態的協定更簡單的,一般來說實作起來也更簡單,不需要使用狀態機,一個迴圈就行了——不過,HTTP本身很復雜,這是另一個故事了,這裏暫時不提。

無狀態的協定有一些優點,也有一些缺點。

和許多人想象的不同,會話(Session)支持其實並不是一個缺點,反而是無狀態協定的優點,因為對於有狀態協定來說,如果將會話狀態與連線繫結在一起,那麽如果連線意外斷開,整個會話就會遺失,重新連線之後一般需要從頭開始(當然這也可以透過吸收無狀態協定的某些特點進行改進);而HTTP這樣的無狀態協定,使用後設資料(如Cookies頭)來維護會話,使得會話與連線本身獨立起來,這樣即使連線斷開了,會話狀態也不會受到嚴重傷害,保持會話也不需要保持連線本身。另外,無狀態的優點還在於對中介軟體友好,中介軟體無需完全理解通訊雙方的互動過程,只需要能正確分片訊息即可,而且中介軟體可以很方便地將訊息在不同的連線上傳輸而不影響正確性,這就方便了負載均衡等元件的設計。

無狀態協定的主要缺點在於,單個請求需要的所有資訊都必須要包含在請求中一次發送到伺服端,這導致單個訊息的結構需要比較復雜,必須能夠支持大量後設資料,因此HTTP訊息的解析要比其他許多協定都要復雜得多。同時,這也導致了相同的數據在多個請求上往往需要反復傳輸,例如同一個連線上的每個請求都需要傳輸Host、Authentication、Cookies、Server等往往是完全重復的後設資料,一定程度上降低了協定的效率。

話說回來,HTTP協定是無狀態協定,這句話本身到底對不對?

實際上,並不全對。HTTP/1.1中有一個Expect: 100-Continue的功能,它是這麽工作的:

  1. 在發送大量數據的時候,考慮到伺服端有可能直接拒收數據,客戶端發出請求頭並附帶Expect: 100-Continue的HTTP頭,不發送請求體,先等待伺服器響應
  2. 伺服器收到Expect: 100-Continue的請求,如果允許上傳,發送100 Continue的HTTP響應(同一個請求可以有任意個1xx的響應,均不是最後的Response,只起到提示性作用);如果不允許,例如不允許上傳數據,或者數據大小超出限制,直接返回4xx/5xx的錯誤
  3. 客戶端收到100 Continue的響應之後,繼續上傳數據

可以看出,這實際上很明顯是一個有狀態協定的套路,它需要先進行一次握手,然後再真正發送數據。不過,HTTP協定也規定,如果伺服端不進行100 Continue的響應,建議客戶端在等待較短的時間之後仍然上傳數據,以達成與不支持Expect: 100-Continue功能的伺服器的相容,這樣可以算是「能有狀態則有狀態,否則回到無狀態的路上」,這樣說HTTP 1.x是無狀態的協定也是沒錯的。

至於HTTP/2,它應該算是一個有狀態的協定了(有握手和GOAWAY訊息,有類似於TCP的流控),所以以後說「HTTP是無狀態的協定」就不太對了,最好說「HTTP 1.x是無狀態的協定」

===================================================

關於Cookies和有/無狀態的關系,如果把有狀態、無狀態理解成相同的請求是否返回相同的結果,那麽要讓某個HTTP伺服器有狀態,真的需要Cookies嗎?不需要對不對?只需要大家都共享一個全域狀態就行了,比如說首頁上有一個存取計數器「您是第XXXX個存取本頁的使用者」,每存取一次這個計數器就加1,誰存取都加1,這從剛才的理解來說也是「有狀態」,對不對?Cookies/Session的作用是 建立和維護多組獨立的狀態 ,而不是 有狀態 。這個狀態指的是後端服務的狀態,而非HTTP協定本身的狀態。所以說,HTTP協定是無狀態的協定,這個其實跟服務的狀態是無關的。一個服務不管使用何種協定,都可以在服務層面上是有狀態的,因為這和通訊協定無關,只需要它在響應請求時改變自己的狀態即可,例如有一個Shutdown命令可以直接關掉整個伺服器不再接受響應,那麽它無論如何都是有狀態的對吧。所以說,服務本身有沒有狀態、支不支持會話,其實跟HTTP協定是否有狀態是無關的。