摘要:服務(wù)發(fā)現(xiàn)問題對于來講,我們已經(jīng)解決了傳輸協(xié)議的問題基于,協(xié)議約定問題基于,最后要解決的是服務(wù)發(fā)現(xiàn)問題。另外一方是服務(wù)消費(fèi)方,向獲取服務(wù)提供方的注冊信息。
????上一節(jié)我們了解了基于 XML 的 SOAP 協(xié)議,SOAP 的 S 是啥意思來著?是 Simple,但是好像一點兒都不簡單?。?/p> 傳輸協(xié)議問題
????對于 SOAP 來講,比如我創(chuàng)建一個訂單,用 POST,在 XML 里面寫明動作是 CreateOrder;刪除一個訂單,還是用 POST,在 XML 里面寫明了動作是 DeleteOrder。其實創(chuàng)建訂單完全可以使用 POST 動作,然后在 XML 里面放一個訂單的信息就可以了,而刪除用 DELETE 動作,然后在 XML 里面放一個訂單的 ID 就可以了。
????于是上面的那個 SOAP 就變成下面這個簡單的模樣。
POST /purchaseOrder HTTP/1.1 Host: www.cnblog.com Content-Type: application/xml; charset=utf-8 Content-Length: nnn2018-07-01 板栗燜雞 58
????而且 XML 的格式也可以改成另外一種簡單的文本化的對象表示格式 JSON。
????經(jīng)常寫 Web 應(yīng)用的應(yīng)該已經(jīng)發(fā)現(xiàn),這就是 RESTful 格式的 API 的樣子。
協(xié)議約定問題????然而 RESTful 可不僅僅是指 API,而是一種架構(gòu)風(fēng)格,全稱 Representational State Transfer,表述性狀態(tài)轉(zhuǎn)移,來自一篇重要的論文《架構(gòu)風(fēng)格與基于網(wǎng)絡(luò)的軟件架構(gòu)設(shè)計》(Architectural Styles and the Design of Network-based Software Architectures)。
????這篇文章從深層次,更加抽象地論證了一個互聯(lián)網(wǎng)應(yīng)用應(yīng)該有的設(shè)計要點,而這些設(shè)計要點,成為后來我們能看到的所有高并發(fā)應(yīng)用設(shè)計都必須要考慮的問題,再加上 REST API 比較簡單直接,所以后來幾乎成為互聯(lián)網(wǎng)應(yīng)用的標(biāo)準(zhǔn)接口。
????因此,和 SOAP 不一樣,REST 不是一種嚴(yán)格規(guī)定的標(biāo)準(zhǔn),它其實是一種設(shè)計風(fēng)格。如果按這種風(fēng)格進(jìn)行設(shè)計,RESTful 接口和 SOAP 接口都能做到,只不過后面的架構(gòu)是 REST 倡導(dǎo)的,而 SOAP 相對比較關(guān)注前面的接口。
????而且由于能夠通過 WSDL 生成客戶端的 Stub,因而 SOAP 常常被用于類似傳統(tǒng)的 RPC 方式,也即調(diào)用遠(yuǎn)端和調(diào)用本地是一樣的。
????然而本地調(diào)用和遠(yuǎn)程跨網(wǎng)絡(luò)調(diào)用畢竟不一樣,這里的不一樣還不僅僅是因為有網(wǎng)絡(luò)而導(dǎo)致的客戶端和服務(wù)端的分離,從而帶來的網(wǎng)絡(luò)性能問題。更重要的問題是,客戶端和服務(wù)端誰來維護(hù)狀態(tài)。所謂的狀態(tài)就是對某個數(shù)據(jù)當(dāng)前處理到什么程度了。
????這里舉幾個例子,例如,我瀏覽到哪個目錄了,我看到第幾頁了,我要買個東西,需要扣減一下庫存,這些都是狀態(tài)。本地調(diào)用其實沒有人糾結(jié)這個問題,因為數(shù)據(jù)都在本地,誰處理都一樣,而且一邊處理了,另一邊馬上就能看到。
????當(dāng)有了 RPC 之后,我們本來期望對上層透明,就像上一節(jié)說的“遠(yuǎn)在天邊,盡在眼前”。于是使用 RPC 的時候,對于狀態(tài)的問題也沒有太多的考慮。
????就像 NFS 一樣,客戶端會告訴服務(wù)端,我要進(jìn)入哪個目錄,服務(wù)端必須要為某個客戶端維護(hù)一個狀態(tài),就是當(dāng)前這個客戶端瀏覽到哪個目錄了。例如,客戶端輸入 cd hello,服務(wù)端要在某個地方記住,上次瀏覽到 /root/liuchao 了,因而客戶的這次輸入,應(yīng)該給它顯示 /root/liuchao/hello 下面的文件列表。而如果有另一個客戶端,同樣輸入 cd hello,服務(wù)端也在某個地方記住,上次瀏覽到 /var/lib,因而要給客戶顯示的是 /var/lib/hello。
????不光 NFS,如果瀏覽翻頁,我們經(jīng)常要實現(xiàn)函數(shù) next(),在一個列表中取下一頁,但是這就需要服務(wù)端記住,客戶端 A 上次瀏覽到 20~30 頁了,那它調(diào)用 next(),應(yīng)該顯示 30~40 頁,而客戶端 B 上次瀏覽到 100~110 頁了,調(diào)用 next() 應(yīng)該顯示 110~120 頁。
????上面的例子都是在 RPC 場景下,由服務(wù)端來維護(hù)狀態(tài),很多 SOAP 接口設(shè)計的時候,也常常按這種模式。這種模式原來沒有問題,是因為客戶端和服務(wù)端之間的比例沒有失衡。因為一般不會同時有太多的客戶端同時連上來,所以 NFS 還能把每個客戶端的狀態(tài)都記住。
????公司內(nèi)部使用的 ERP 系統(tǒng),如果使用 SOAP 的方式實現(xiàn),并且服務(wù)端為每個登錄的用戶維護(hù)瀏覽到報表那一頁的狀態(tài),由于一個公司內(nèi)部的人也不會太多,把 ERP 放在一個強(qiáng)大的物理機(jī)上,也能記得過來。
????但是互聯(lián)網(wǎng)場景下,客戶端和服務(wù)端就徹底失衡了。你可以想象“雙十一”,多少人同時來購物,作為服務(wù)端,它能記得過來嗎?當(dāng)然不可能,只好多個服務(wù)端同時提供服務(wù),大家分擔(dān)一下。但是這就存在一個問題,服務(wù)端怎么把自己記住的客戶端狀態(tài)告訴另一個服務(wù)端呢?或者說,你讓我給你分擔(dān)工作,你也要把工作的前因后果給我說清楚啊!
????那服務(wù)端索性就要想了,既然這么多客戶端,那大家就分分工吧。服務(wù)端就只記錄資源的狀態(tài),例如文件的狀態(tài),報表的狀態(tài),庫存的狀態(tài),而客戶端自己維護(hù)自己的狀態(tài)。比如,你訪問到哪個目錄了啊,報表的哪一頁了啊,等等。
????這樣對于 API 也有影響,也就是說,當(dāng)客戶端維護(hù)了自己的狀態(tài),就不能這樣調(diào)用服務(wù)端了。例如客戶端說,我想訪問當(dāng)前目錄下的 hello 路徑。服務(wù)端說,我怎么知道你的當(dāng)前路徑。所以客戶端要先看看自己當(dāng)前路徑是 /root/liuchao,然后告訴服務(wù)端說,我想訪問 /root/liuchao/hello 路徑。
????再比如,客戶端說我想訪問下一頁,服務(wù)端說,我怎么知道你當(dāng)前訪問到哪一頁了。所以客戶端要先看看自己訪問到了 100~110 頁,然后告訴服務(wù)器說,我想訪問 110~120 頁。
????這就是服務(wù)端的無狀態(tài)化。這樣服務(wù)端就可以橫向擴(kuò)展了,一百個人一起服務(wù),不用交接,每個人都能處理。
????所謂的無狀態(tài),其實是服務(wù)端維護(hù)資源的狀態(tài),客戶端維護(hù)會話的狀態(tài)。對于服務(wù)端來講,只有資源的狀態(tài)改變了,客戶端才調(diào)用 POST、PUT、DELETE 方法來找我;如果資源的狀態(tài)沒變,只是客戶端的狀態(tài)變了,就不用告訴我了,對于我來說都是統(tǒng)一的 GET。
????雖然這只改進(jìn)了 GET,但是已經(jīng)帶來了很大的進(jìn)步。因為對于互聯(lián)網(wǎng)應(yīng)用,大多數(shù)是讀多寫少的。而且只要服務(wù)端的資源狀態(tài)不變,就給了我們緩存的可能。例如可以將狀態(tài)緩存到接入層,甚至緩存到 CDN 的邊緣節(jié)點,這都是資源狀態(tài)不變的好處。
????按照這種思路,對于 API 的設(shè)計,就慢慢變成了以資源為核心,而非以過程為核心。也就是說,客戶端只要告訴服務(wù)端你想讓資源狀態(tài)最終變成什么樣就可以了,而不用告訴我過程,不用告訴我動作。
????還是文件目錄的例子??蛻舳藨?yīng)該訪問哪個絕對路徑,而非一個動作,我就要進(jìn)入某個路徑。再如,庫存的調(diào)用,應(yīng)該查看當(dāng)前的庫存數(shù)目,然后減去購買的數(shù)量,得到結(jié)果的庫存數(shù)。這個時候應(yīng)該設(shè)置為目標(biāo)庫存數(shù)(但是當(dāng)前庫存數(shù)要匹配),而非告知減去多少庫存。
????這種 API 的設(shè)計需要實現(xiàn)冪等,因為網(wǎng)絡(luò)不穩(wěn)定,就會經(jīng)常出錯,因而需要重試,但是一旦重試,就會存在冪等的問題,也就是同一個調(diào)用,多次調(diào)用的結(jié)果應(yīng)該一樣,不能一次支付調(diào)用,因為調(diào)用三次變成了支付三次。不能進(jìn)入 cd a,做了三次,就變成了 cd a/a/a。也不能扣減庫存,調(diào)用了三次,就扣減三次庫存。
????當(dāng)然按照這種設(shè)計模式,無論 RESTful API 還是 SOAP API 都可以將架構(gòu)實現(xiàn)成無狀態(tài)的,面向資源的、冪等的、橫向擴(kuò)展的、可緩存的。
????但是 SOAP 的 XML 正文中,是可以放任何動作的。例如 XML 里面可以寫 < ADD >,< MINUS > 等。這就方便使用 SOAP 的人,將大量的動作放在 API 里面。
????RESTful 沒這么復(fù)雜,也沒給客戶提供這么多的可能性,正文里的 JSON 基本描述的就是資源的狀態(tài),沒辦法描述動作,而且能夠出發(fā)的動作只有 CRUD,也即 POST、GET、PUT、DELETE,也就是對于狀態(tài)的改變。
????所以,從接口角度,就讓你死了這條心。當(dāng)然也有很多技巧的方法,在使用 RESTful API 的情況下,依然提供基于動作的有狀態(tài)請求,這屬于反模式了。
服務(wù)發(fā)現(xiàn)問題????對于 RESTful API 來講,我們已經(jīng)解決了傳輸協(xié)議的問題——基于 HTTP,協(xié)議約定問題——基于 JSON,最后要解決的是服務(wù)發(fā)現(xiàn)問題。
????有個著名的基于 RESTful API 的跨系統(tǒng)調(diào)用框架叫 Spring Cloud。在 Spring Cloud 中有一個組件叫 Eureka。傳說,阿基米德在洗澡時發(fā)現(xiàn)浮力原理,高興得來不及穿上褲子,跑到街上大喊:“Eureka(我找到了)!”所以 Eureka 是用來實現(xiàn)注冊中心的,負(fù)責(zé)維護(hù)注冊的服務(wù)列表。
????服務(wù)分服務(wù)提供方,它向 Eureka 做服務(wù)注冊、續(xù)約和下線等操作,注冊的主要數(shù)據(jù)包括服務(wù)名、機(jī)器 IP、端口號、域名等等。
????另外一方是服務(wù)消費(fèi)方,向 Eureka 獲取服務(wù)提供方的注冊信息。為了實現(xiàn)負(fù)載均衡和容錯,服務(wù)提供方可以注冊多個。
????當(dāng)消費(fèi)方要調(diào)用服務(wù)的時候,會從注冊中心讀出多個服務(wù)來,那怎么調(diào)用呢?當(dāng)然是 RESTful 方式了。
????Spring Cloud 提供一個 RestTemplate 工具,用于將請求對象轉(zhuǎn)換為 JSON,并發(fā)起 Rest 調(diào)用,RestTemplate 的調(diào)用也是分 POST、PUT、GET、 DELETE 的,當(dāng)結(jié)果返回的時候,根據(jù)返回的 JSON 解析成對象。
????通過這樣封裝,調(diào)用起來也很方便。
小結(jié)SOAP 過于復(fù)雜,而且設(shè)計是面向動作的,因而往往因為架構(gòu)問題導(dǎo)致并發(fā)量上不去;
RESTful 不僅僅是一個 API,而且是一種架構(gòu)模式,主要面向資源,提供無狀態(tài)服務(wù),有利于橫向擴(kuò)展應(yīng)對高并發(fā)。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/29948.html
摘要:服務(wù)發(fā)現(xiàn)問題對于來講,我們已經(jīng)解決了傳輸協(xié)議的問題基于,協(xié)議約定問題基于,最后要解決的是服務(wù)發(fā)現(xiàn)問題。另外一方是服務(wù)消費(fèi)方,向獲取服務(wù)提供方的注冊信息。 ????上一節(jié)我們了解了基于 XML 的 SOAP 協(xié)議,SOAP 的 S 是啥意思來著?是 Simple,但是好像一點兒都不簡單?。?傳輸協(xié)議問題 ????對于 SOAP 來講,比如我創(chuàng)建一個訂單,用 POST,在 XML 里面寫明...
摘要:但是只不過都是以二進(jìn)制的形式編碼的。這其實相當(dāng)于綜合了和二進(jìn)制共同優(yōu)勢的一個協(xié)議。在上面的架構(gòu)中,如果使用二進(jìn)制的方式進(jìn)行序列化,雖然不用協(xié)議文件來生成,但是對于接口的定義,以及傳的對象,還是需要共享。 ????前面我們認(rèn)識了兩個常用文本類的 RPC 協(xié)議,對于陌生人之間的溝通,用 NBA、CBA 這樣的縮略語,會使得協(xié)議約定非常不方便。 ????在講 CDN 和 DNS 的時候,我們...
摘要:但是只不過都是以二進(jìn)制的形式編碼的。這其實相當(dāng)于綜合了和二進(jìn)制共同優(yōu)勢的一個協(xié)議。在上面的架構(gòu)中,如果使用二進(jìn)制的方式進(jìn)行序列化,雖然不用協(xié)議文件來生成,但是對于接口的定義,以及傳的對象,還是需要共享。 ????前面我們認(rèn)識了兩個常用文本類的 RPC 協(xié)議,對于陌生人之間的溝通,用 NBA、CBA 這樣的縮略語,會使得協(xié)議約定非常不方便。 ????在講 CDN 和 DNS 的時候,我們...
摘要:對于與而言,則可以看做是消息傳遞技術(shù)的一種衍生或封裝。在生產(chǎn)者通知消費(fèi)者時,傳遞的往往是消息或事件,而非生產(chǎn)者自身。通過消息路由,我們可以配置路由規(guī)則指定消息傳遞的路徑,以及指定具體的消費(fèi)者消費(fèi)對應(yīng)的生產(chǎn)者。采用和來進(jìn)行遠(yuǎn)程對象的通訊。 消息模式 歸根結(jié)底,企業(yè)應(yīng)用系統(tǒng)就是對數(shù)據(jù)的處理,而對于一個擁有多個子系統(tǒng)的企業(yè)應(yīng)用系統(tǒng)而言,它的基礎(chǔ)支撐無疑就是對消息的處理。與對象不同,消息本質(zhì)上...
摘要:微軟的雖然引入了事件機(jī)制,可以在隊列收到消息時觸發(fā)事件,通知訂閱者。由微軟作為主要貢獻(xiàn)者的,則對以及做了進(jìn)一層包裝,并能夠很好地實現(xiàn)這一模式。 在分布式服務(wù)框架中,一個最基礎(chǔ)的問題就是遠(yuǎn)程服務(wù)是怎么通訊的,在Java領(lǐng)域中有很多可實現(xiàn)遠(yuǎn)程通訊的技術(shù),例如:RMI、MINA、ESB、Burlap、Hessian、SOAP、EJB和JMS等,這些名詞之間到底是些什么關(guān)系呢,它們背后到底是基...
閱讀 2590·2021-10-11 10:58
閱讀 1111·2019-08-29 13:58
閱讀 1713·2019-08-26 13:32
閱讀 882·2019-08-26 10:40
閱讀 3311·2019-08-26 10:18
閱讀 1806·2019-08-23 14:18
閱讀 1165·2019-08-23 10:54
閱讀 499·2019-08-22 18:39