對于REST中無狀態(stateless)的一點認識
今天早上在Yahoo的郵件列表里看到一篇頗有意思的討論,標題為RESTful vs. unRESTful: Session IDs and Authentication(51CTO編者注:意為REST對非REST,Session ID與驗證)。文中讓發起討論的朋友大惑不解的是這樣一個問題:為什么在請求中傳遞SessionID被普遍認為是unRESTful的,而將用戶的credentials包含在每個請求里又是一種非常RESTful的做法。看了他接下來對于REST架構風格中"statelessness"屬性的理解后,我覺得有必要對這個經常會被人誤解詞匯以及相關概念做一個簡要的整理,希望能夠通過這篇隨筆解釋清楚什么是狀態,為什么要實現無狀態,以及REST風格架構中的兩種狀態的區別,***我會從我的理解出發來回答作者提出的這個問題。
首先,一個Web應用程序協議的“狀態”在通常指的是為兩個相互關聯的用戶交互操作保留的某種公共信息,它們常常被用來存儲工作流或用戶狀態信息等數據。這些信息可以被指定不同的作用域如page,request,session或全局作用域,而存儲他們的責任也同樣可以由Client端或Server端負責。雖然存儲狀態為企業軟件開發帶來了諸多便利,但是它也給分布式系統的其他方面帶來了許多限制,比如在負載均衡方面,在有狀態的模式下,一個用戶的請求必須被提交到保存有其相關狀態信息的服務器上,否則這些請求可能無法被理解,這也就意味著在此模式下服務器端無法對用戶請求進行自由調度。于此相關的另一個問題是容錯性,倘若保有用戶信息的服務器宕機,那么該用戶最近的所有交互操作將無法被透明地移送至備用服務器上,除非該服務器時刻與主服務器同步全部用戶的狀態信息。此外,由于HTTP本身不是一個有狀態的協議,開發人員必須通過模擬實現狀態的鈍化與激活等。于是為了克服這些不足,無狀態(Statelessness)架構風格屬性受到了廣泛關注。
無狀態指的是任意一個Web請求必須完全與其他請求隔離,當請求端提出請求時,請求本身包含了相應端為相應這一請求所需的全部信息。這一約束的出現改善了分布式系統的可見性、可靠性以及可伸縮性,具體的介紹可以參考Roy T. Fielding博士的論文,這里就不哆嗦了。這些從整個系統角度來看無狀態似乎過于抽象,那么對于用戶來說,怎么感覺的有狀態與無狀態的差別呢。簡單的方法是瀏覽器的后退按鈕,如果一個網站期望用戶以A->B->C的流程來交互,而在執行至B時回退的話,那么系統很有可能不是按照其所期望的方式運行,因為用戶的狀態可能被不可逆地修改了。反過來,搜索引擎(我指的是普通意義上的搜索引擎,而不是根據用戶搜索歷史個性化了的)是一個無狀態架構的范例。任何用戶可以在瀏覽器地址欄中輸入http://www.google.com/search?q=RESTful&start=100來獲得從***百條開始的關于RESTful的記錄,并且當Google摩洛哥服務器癱瘓時,相關用戶請求會被透明地移送至其他服務器。
一切似乎很明了,那么是什么導致了那位朋友的誤解呢,答案是RESTful架構對于state的兩個不同的解釋: 應用狀態(Application State)和資源狀態(Resource State)。應用狀態指的是與某一特定請求相關的狀態信息,而資源狀態則反映了某一存儲在服務器端資源在某一時刻的特定狀態,該狀態不會因為用戶請求而改變,任何用戶在同一時刻對該資源的請求都會獲得這一狀態的表現(Representation)。RESTful架構要求服務器端不保有任何與特定HTTP請求相關的資源,所以應用狀態必須由請求方在請求過程中提供。那么再回到那個郵件列表中的問題,為什么傳遞一個session ID是違背REST架構風格而傳遞user credentials卻不是。我想作者的疑惑源于他沒有分清什么是有狀態和無狀態的架構屬性,而認為“傳遞某種表示狀態的信息”到服務器便是“有狀態”的表現。其實有狀態和無狀態與請求本身沒有多大關聯,重要的是狀態信息是由請求方還是響應方負責保存。在Session ID可以被認為是一個用來標識某一會話狀態的Key,將其傳遞給服務器端意味著這樣一個請求:“請幫我取出這個狀態信息”,也就是說這個請求假設響應方保有著狀態信息。由于與某一特定請求相關的狀態屬于應用狀態,而RESTful架構要求任何此類狀態由請求方負責提供,所以傳遞Session ID被認為是unRESTful的做法。反過來,user credential作為一種應用狀態,是被期望由請求方提供的,所以在請求中傳遞user credentials(姑且忽略安全性問題)是符合RESTful架構規范的。
這篇隨筆或多或少散發著某種純粹主義的味道,但我覺得有些概念是值得玩味的。任何一種架構風格的出現都有其期望的,對現有方案的改進或期望克服的問題。作為REST來說,它所期望的是組件的可伸縮性,組件的獨立部署,接口統一等特性,而無狀態作為實現這組需求的一個特性,個人認為是有必要清楚了解并實際開發過程中落實的。
【編輯推薦】