微服務(wù)架構(gòu)已經(jīng)很流行饥悴, 從概念上要解決的問題: 認(rèn)識(shí)一個(gè)系統(tǒng)蒿秦,如何將這個(gè)系統(tǒng)劃分(系統(tǒng)的分解)烤镐,識(shí)別出組件, 組件之間如何通訊棍鳖,信息如何交換炮叶,使得協(xié)同完成一件事情。 同樣還要包括渡处,隨著系統(tǒng)的演變镜悉,組件如何演化。
那么組件(模塊或服務(wù))本身的抽象医瘫,以及如何暴露侣肄,如何和外部通訊,如何數(shù)據(jù)交換醇份,以及如何交互稼锅?這些都需要面對(duì)解決的問題, 重讀Roy論文僚纷,重新認(rèn)識(shí)到Rest對(duì)于上面的問題(組件如何暴露矩距,如何通訊,如何交換數(shù)據(jù)怖竭,以及模塊的演化)給出一個(gè)答案锥债。下面談?wù)勛约簩?duì)Rest的理解。
一切皆資源(resource)
Rest是面向資源的架構(gòu)ROA( resource -oriented architecture)痊臭,理解resource這個(gè)概念非常重要哮肚,或者這個(gè)是理解Rest的核心。Rest 是一種理念趣兄,將現(xiàn)實(shí)世界一切抽象為資源绽左,通過資源將現(xiàn)實(shí)世界映射到數(shù)字世界; 就如同面向?qū)ο笸叮瑢F(xiàn)實(shí)世界一切抽象為對(duì)象,通過對(duì)象將現(xiàn)實(shí)世界映射到數(shù)字世界戏蔑。?比如電商里面的一個(gè)商品蹋凝,一個(gè)訂單,一個(gè)快遞單號(hào)总棵,甚至一條評(píng)論都可以抽象為資源鳍寂;更抽象點(diǎn)概念,比如提供在線打印的服務(wù)情龄,每一個(gè)打印的請(qǐng)求也是一個(gè)資源迄汛; 一個(gè)調(diào)度引擎捍壤,每一次調(diào)度的請(qǐng)求都一個(gè)資源。
在資源為基本的抽象單元世界里鞍爱,資源有這么些特性:
1. 每一種現(xiàn)實(shí)客觀事物都可以抽象為資源鹃觉;資源就是現(xiàn)實(shí)世界的抽象的屬性的信息實(shí)體。
2. 每一種資源都有一個(gè)標(biāo)識(shí)睹逃, 或者有多個(gè)標(biāo)識(shí)盗扇, 就是資源的名字;
3. 在給定的上下文沉填,可以通過名字查找到資源本身疗隶;( 資源本身的名字,與物理實(shí)現(xiàn)分離)
4. 計(jì)算是將資源具體化到表現(xiàn)層翼闹;
5. 資源本身具有不可變性斑鼻;
6.? 對(duì)于不同表現(xiàn)層之間的轉(zhuǎn)換時(shí)無損的,之間可以等價(jià)轉(zhuǎn)換猎荠;
7. 計(jì)算結(jié)果也是一種資源卵沉,也有其標(biāo)識(shí)符。
在web世界里面多有一一對(duì)應(yīng)法牲。 #1 是相同的理念史汗;#2 資源標(biāo)識(shí)就是URI ; #3 通過就是DNS查找到資源拒垃; #4 對(duì)應(yīng)動(dòng)態(tài)資源計(jì)算就是一個(gè)請(qǐng)求停撞,通過計(jì)算轉(zhuǎn)換為表現(xiàn)層數(shù)據(jù)給用戶; #5 資源本身邏輯上保持不變悼瓮; #6 不同表現(xiàn)層是等價(jià)的(xml戈毒,json); #7 對(duì)于計(jì)算出來的結(jié)果,也是一種資源横堡;? ?
通過客觀世界映射到數(shù)字世界埋市,那么客觀世界中的互操作,如何體現(xiàn)命贴。下面分別介紹道宅,對(duì)象如何被訪問(URI),對(duì)象如何操作(CRUD)胸蛛,以及信息數(shù)據(jù)如何交換污茵。
URI (統(tǒng)一資源識(shí)別符)
?資源如何被訪問?或者資源如何將資源暴露外部世界葬项? 這個(gè)就是URI泞当,這個(gè)天生就于Web緊密連接在一起的,被用作資源的名字民珍。?
比如對(duì)一個(gè)手機(jī)的描述 http://hostname:80/products/smartphone襟士, 清晰明了這是一個(gè)smarphone的產(chǎn)品盗飒,同時(shí)可以通過URI,找到獲取信息本身陋桂。(URI與URL往往一致)逆趣。這個(gè)是被認(rèn)為理所當(dāng)然或者已經(jīng)熟視無睹,但是理所當(dāng)然的事情都不是這么的簡(jiǎn)單的章喉。 比對(duì)一下汗贫,看看書的URI(ISBN)與URI比較, 看看哪一個(gè)可讀性好一些秸脱?
http://hostname/books/愛麗絲夢(mèng)游仙境
ISBN:7-301-04815-7?
URI是資源的名字落包,就說說名字本身。比如一個(gè)資源
http://host/products/smartphone
http://host/products 也可以理解為名字在網(wǎng)絡(luò)上的namespace摊唇。名字很顯然是一個(gè)名詞性質(zhì)的詞語(yǔ)咐蝇, 試試想想一個(gè)動(dòng)詞的名字是什么感覺:); 同樣名字與其實(shí)現(xiàn)沒有關(guān)系,下面的名字將概念和實(shí)現(xiàn)耦合在一起巷查,不是好的名字有序。
http://host/books/xxx.asp
http://host/books/xxx.php
?同樣名字要名副其實(shí),這個(gè)是程序員程序員的品味和修煉有關(guān)岛请,不在本文范圍里面旭寿,暫且不討論。
統(tǒng)一的方法(CRUD)
Rest 將對(duì)于資源的操作定義為CRUD(get/post/put/delete),是的崇败,是所有的操作盅称!或許這樣說更容易理解一些,相當(dāng)面向?qū)ο罄锩嫠械膶?duì)象的方法后室,當(dāng)且僅當(dāng)只有CRUD操作缩膝。先回顧一下這個(gè)四個(gè)操作的定義,然后再看如何統(tǒng)一所有操作.
get: 對(duì)應(yīng)讀操作岸霹,獲取資源疾层;語(yǔ)義很清晰,只讀應(yīng)該不期望改變資源本身贡避。
post: 創(chuàng)建一個(gè)資源痛黎。 因?yàn)橘Y源ID一般是服務(wù)創(chuàng)建的,服務(wù)器創(chuàng)建成果會(huì)返回新的ID贸桶,這為一個(gè)不等冪操作舅逸。
put:? 更新資源。 這個(gè)需要提交一個(gè)完整的資源皇筛,當(dāng)然包括ID,才能更新坠七。這個(gè)操作經(jīng)常會(huì)和post搞混水醋。 (post就是沒有完整信息比如ID旗笔,就只能由服務(wù)器創(chuàng)建,put是有ID可以提交更新)拄踪。 等冪操作蝇恶。
delete: 刪除資源。 等冪操作惶桐。
冪等操作(idempotentance)撮弧,簡(jiǎn)單理解一個(gè)操作(函數(shù)映射)多次,和映射一次效果一樣姚糊。?
f(f(x)) = f(x)
operation vs status
如何將所有的操作統(tǒng)一到CRUD上呢贿衍? 比如對(duì)于一個(gè)訂單,我們有增刪改查救恨,這個(gè)很好理解與CRUD一一對(duì)應(yīng)贸辈。但是對(duì)于此外的,比如取消肠槽,鎖定一個(gè)訂單如何對(duì)應(yīng)擎淤? 對(duì)于傳統(tǒng)的OO理念我們會(huì)定義一組操作:
class OrderManager{
cancelOrder( int id){...}
?lockOrder(int id) {...}
}
對(duì)于SOAP,RPC我們經(jīng)常都是這么做的秸仙,暴露出方法嘴拢。
對(duì)于Rest里面我們把操作當(dāng)成一個(gè)狀態(tài)的修改。對(duì)于上面的Cancel方法在rest里面可以這樣定義
put? http://host/books/xxx??
body: {? "status": "cancel"}
這樣就將操作轉(zhuǎn)換為狀態(tài)變化寂纪,只要提供狀態(tài)修改的方法席吴,更新操作(put), 就可以統(tǒng)一所有類似的操作弊攘。貌似一個(gè)很簡(jiǎn)單的轉(zhuǎn)化就可以將所有的操作統(tǒng)一抢腐,還是很是強(qiáng)大。
既然CURD可以完全統(tǒng)一所有對(duì)資源的操作襟交,那么很顯然CRUD可以借助于HTTP協(xié)議的動(dòng)詞來實(shí)現(xiàn)迈倍。很是完美,誰(shuí)讓Roy T. Fielding就是http協(xié)議和Rest概念的提出者捣域。
既然通過URI來訪問資源啼染,所有的操作http動(dòng)詞可以來操作。下一個(gè)問題就是信息如何交換焕梅。
Representation(表現(xiàn)層)
數(shù)據(jù)是信息的載迹鹅,而數(shù)據(jù)本身可以有不同表現(xiàn)形式 如xml,json贞言,yaml斜棚,binary。 那么服務(wù)之間如何信息的交換,就是通過表現(xiàn)層來交換弟蚀。先看看傳統(tǒng)的rpc蚤霞,數(shù)據(jù)如何傳輸?數(shù)據(jù)的格式义钉,對(duì)象的本身都在開發(fā)的時(shí)候定義好昧绣。 這樣使得客戶端和服務(wù)器都綁定在一起,沒有辦法單獨(dú)演化升級(jí)捶闸。而對(duì)于Rest定義客戶端和服務(wù)器對(duì)于數(shù)據(jù)可以根據(jù)客戶端的請(qǐng)求來返回(Content -type)夜畴,同樣服務(wù)器端可以支持不同的格式。 同樣之間還可以協(xié)商機(jī)制確定如何傳遞數(shù)據(jù)删壮。 這樣服務(wù)器與客戶端解耦贪绘,可以獨(dú)立升級(jí), 獨(dú)立演化醉锅,是軟件架構(gòu)追求一致追尋的目標(biāo)兔簇。
Stateless (無狀態(tài))
互聯(lián)網(wǎng)上資源天生就可以任何時(shí)間任何地點(diǎn)的可以訪問,其訪問彈性是不受控制的硬耍。如何做到彈性垄琐,這個(gè)一個(gè)挑戰(zhàn). 試試想想一般的程序如果要做到彈性計(jì)算,而不用去改代碼经柴,不是一般挑戰(zhàn)狸窘。 無狀態(tài)很好的適應(yīng)這樣的場(chǎng)景。
在互聯(lián)網(wǎng)上坯认,客戶端瀏覽器發(fā)送URL請(qǐng)求到服務(wù)器翻擒,服務(wù)器接受請(qǐng)求響應(yīng)返回給客戶。 如何客戶訪問量陡增牛哺,比如雙11陋气, 服務(wù)器就通過擴(kuò)展來并發(fā)處理。如果請(qǐng)求是沒有狀態(tài)的引润, 就可以被分發(fā)到不同的服務(wù)器巩趁,服務(wù)器只需要根據(jù)請(qǐng)求的本身去應(yīng)答,而不去考慮請(qǐng)求的上下文淳附。如果有狀態(tài)议慰,那么處理起來就復(fù)雜很多,服務(wù)器需要根據(jù)請(qǐng)求查找上下文奴曙,如果處理的服務(wù)器沒有上下文别凹,就需要同步上下文。更進(jìn)一步洽糟,狀態(tài)更新炉菲,如何保證上下文一致堕战。這些帶來的復(fù)雜度,往往就是bug颁督,影響到開發(fā)效率践啄,提高維護(hù)成本浇雹。 另外沉御,這樣上下文相關(guān)對(duì)于緩存也沒有存在同樣的問題。
當(dāng)然天下沒有免費(fèi)的午餐昭灵。 沒有狀態(tài)就需要對(duì)相應(yīng)每一次客戶端在請(qǐng)求的時(shí)候吠裆,需要額外的信息來標(biāo)識(shí)自己,cookie或者額外的參數(shù)烂完。 很顯然rest不是最優(yōu)的傳輸效率不是最高的试疙。 比起來帶來的可擴(kuò)展性,好處遠(yuǎn)遠(yuǎn)大于那點(diǎn)額外的帶寬占用抠蚣。
HATEOAS
(Hypermedia As The Engine of Application State)
超鏈接就是一個(gè)應(yīng)用程序狀態(tài)引擎祝旷。這個(gè)是Rest對(duì)API的約束, 讓服務(wù)器和客戶端進(jìn)一步交互上解耦嘶窄。這個(gè)和傳統(tǒng)的分布式應(yīng)用交互不太一樣怀跛。 比如rpc,預(yù)先定義一組api柄冲,然后客戶端只能根據(jù)預(yù)先定義的api來與服務(wù)器之間交互吻谋,這樣服務(wù)器和客戶端之間就產(chǎn)生依賴,二者不能獨(dú)自演化现横。
Rest API 對(duì)于每一個(gè)api返回值漓拾,不但需要告訴返回哪些值,同樣還需要告訴下一步有哪些交互戒祠,交互的信息是通過超文本骇两。這樣子從一個(gè)起始點(diǎn)api就可以根據(jù)返回相關(guān)的超鏈接,來進(jìn)行下一步操作姜盈。理想的這樣所有的流程都可以通過一個(gè)api低千,根據(jù)超文本連接完成。貌似爬蟲很喜歡:)
這里有一個(gè)例子
下面是個(gè)例子,首先是一個(gè)GET請(qǐng)求賬戶的信息
? ? GET /account/12345 HTTP/1.1
? ? Host: somebank.org
? ? Accept: application/xml
將會(huì)返回?:
HTTP/1.1 200 OK
Content-Type: application/xml
<account>
<account_number>12345<?/account_number>
<balance currency="usd">100.00 </balance>
<link rel="deposit" / link> <rel="withdraw" / link><rel="transfer" / link>? ? ? ? ? ? ? <rel="close" />
</account>
這樣用戶可以通過返回值中的URL贩据,做下一次操作栋操。 這樣交互不需預(yù)定義API。 碰到的大多數(shù)見到的rest API饱亮,這一點(diǎn)都不滿足矾芙,被稱為restful.
本文回顧一下Rest 理念(一切皆資源),以及rest api的五大原則(URI,Uniform operation近上, representation剔宪,stateless 和?HATEOAS ) 。
https://stackoverflow.com/questions/152871/isnt-resource-oriented-really-object-oriented
https://www.w3.org/People/Connolly/9703-web-apps-essay.html
http://resources.1060research.com/docs/IntroductionToResourceOrientedComputing-1.pdf
https://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm