咖啡館的故事:FTP, RMI , XML-RPC, SOAP, REST一網打盡
周末的咖啡館有點奇怪, 一群人圍著幾個老頭兒在聊天。
“快說說,你們那個時候沒有HTTP, 沒有JavaScript,到底是怎么讓這些機器上的程序進行'交談'的?”
ftp老頭兒滿臉滄桑,喝了一口咖啡,說道:“簡單得很,機器A通過我,就是ftp, 上傳一個文件到機器B的指定路徑,然后再讓rexec 去調用機器B上的程序,這程序是程序員寫的,可以讀取FTP目錄下的文件,執(zhí)行業(yè)務邏輯就行了。”
“什么是rexec ? ”
“就是從一臺機器上遠程執(zhí)行另外一個機器的命令嘛!” rexec老頭兒略帶怒氣地說道,自己雖然沒有ftp出名,但是不至于沒人知道吧!
rexec [remote_host] [command]
“切!騙誰呢,根本不可能,怎么會用這么笨的方式!”人群中傳來了表示不屑的聲音。
ftp和rexec相視苦笑,這些程序員不會想到,早些年真有這么做的系統(tǒng),是真實的故事。
ftp招呼telnet一起喝咖啡,不再出聲。
RPC
門口傳來一陣喧囂,CORBA和Java RMI風風火火地走了進來。
人群呼啦一下子就圍了上去,拋棄了那三個老頭兒。
只聽見Java RMI說道:“所有的程序本質上都是函數調用,函數調用在一個進程內是無比自然的事情。 如果是跨越機器、跨越進程呢? 如果一個機器上的函數,能調用另外一個機器上的函數,就像調用本地方法一樣,會是什么樣子? ”
CORBA笑著說:“哈哈, 畫面太美不敢想象。 ”
人群中有人問道:“調用遠程方法就像調用本地方法一樣?怎么可能?”
Java RMI說道:“在概念上其實極其簡單,無非就是自動生成客戶端代理和服務器端代理,這兩個代理完成了大量的臟活和累活,比如:網絡通信,參數序列化...... ”
CORBA接著說道:“魔法都在這兩個代理當中,我們稱之為Stub(客戶端代理)和Skeleton(服務器端代理)。這個Stub代理提供了和服務器一模一樣的接口,客戶端程序只要調用它,它就會把請求發(fā)到服務器端的Skeleton代理進行處理。 所以對于客戶端程序來說,網絡不可見,就像是調用本地的方法一樣。”
Java RMI說道:“對,我們把這種方式稱為RPC。”
人群中發(fā)出一片驚嘆聲:“這RPC可真好啊,Stub和Skeleton代碼能自動生成,我們拿到以后,馬上就可以動手編程了,底層什么都不用關心。 ”
Java RMI說道:“底層可以采用二進制的協(xié)議,性能不要太好哦!”
人群中又是一陣歡呼:“太好了!”
那邊的ftp老頭兒警告到:“大家要小心,要注意平臺綁定,你想用Java RMI嗎? 對不起,客戶端和服務器都得用Java,都得安裝Java虛擬機, 什么Python, C#, 沒門兒, 連想都不要想。 ”
telnet接著說:“更重要的是客戶端和服務器緊密綁定,服務器端的變化,都必須得重新生成Stub和Skeleton 。 ”
沒想到這些老家伙們目光如炬。
“什么? 這也太無理了吧!” 人群呼啦一聲又涌到了三個老頭那里。
只見ftp老頭兒在紙上寫到: 比如說有這么一個接口, 可以根據用戶ID查找用戶信息。
- public interface UserService extends Remote{
- public User findUser(int id) throws RemoteException;
- }
利用Java RMI的工具,可以生成Stub和Skeleton, 客戶端拿到Stub以后,可以開心地去編程了。
至于UserService的具體實現代碼,客戶端不用操心。
過了兩天,某個客戶端要求要給這個接口增加一個新的方法:按照名稱來查找用戶。
- public User findUser(String name) throws RemoteException;
那對不起了,需要重新生成新的Stub和Skeleton, 所有的客戶端都會受到影響,即使你根本不需要新的方法。
大家紛紛唉聲嘆氣,這RPC實在是太煩人了!
有沒有一種辦法,讓服務器端獨立變化,而不影響客戶端,或者說盡量不影響客戶端呢?
XML-RPC
后面有個小伙字若有所思,他剛學會了XML, 他覺得既然XML的描述能力這么強,能不能用XML來描述一個方法調用和參數呢?
比如服務器端有個接口是getUser,需要提供的參數是用戶ID, 可以這么描述:
然后通過HTTP Post把這個XML發(fā)送到服務器端,服務器端進行解析,獲取方法名稱和參數的值,調用真正的方法,把結果也以XML形式返回, 客戶端收到以后再解析就可以得到結果了。
想到此處,他大聲叫道:“別生成什么Stub和Skeleton代理了,直接用HTTP和XML該多好啊。”
人群被他的奇異想法所吸引,呼啦一下又圍了過來。
小伙子畫了一張圖, 展示了這個處理的過程:
有人問道:“返回的數據格式可能很復雜, 怎么表示啊。”
小伙子說:“這正是XML的強項啊,圖中展示的是int型,還可以有double ,boolean ,string 等各種類型,甚至可以定義結構體。”
對XML來說,這樣的結構體就是小菜一碟。
“這樣客戶端和服務器端就變成松耦合的了,如果服務器端想添加一個新的接口,客戶端就不用做變化了。我打算把他叫做XML-RPC” 小伙子說道。
“這種辦法真好!” 人群中開始躁動起來,“我們都用XML-RPC吧!”
SOAP
“小伙子,你叫什么名字?” 狂熱的人群中有個人冷靜地問道。
“Dave Winer, 怎么了? ”
“Winner? 嗯,你的名字真不錯,天生贏家啊, 有沒有興趣和我們微軟一起制定一個新的RPC標準?”
“新標準? 我的XML-RPC已經很完善了,又簡單又好用。”
“No,No, 還欠缺不少東西,最要命的就是客戶端和服務器端沒有正式的協(xié)議約定,都是口頭約定,或者文檔約定,對吧?”
Dave Winer點點頭。
“你想想,如果我們把一個服務器對外提供的接口也用XML精確地描述下來,任何程序,只要讀取這個XML文件,就知道接口的方法名,參數名,該有多好?”
Dave Winer又點點頭。
“還有啊,你的XML-RPC只支持HTTP, 我們的新標準可以支持任意協(xié)議啊, HTTP, SMTP,TCP,UDP......都可以。”
“我還是覺得HTTP***!”
“想想看,如果我們的新協(xié)議搞成了,所有的B2B的電子商務系統(tǒng)都可以用這一套協(xié)議來自動通信,多么***的世界! 你仔細想想,你是想在這個破咖啡館喝一輩子咖啡,還是想和我們微軟一起改變世界?”
一年以后,Dave Winer 新的協(xié)議問世了,不,這其實是一套協(xié)議:
WSDL :用于描述一個服務的接口,參數......
UDDI : 實現服務的注冊和發(fā)現
SOAP : 和XML-RPC很像,但是更加規(guī)范,更加正式,更加復雜......
他們之間的關系如圖所示:
微軟的.NET戰(zhàn)略適時啟動,Web Service的宣傳鋪天蓋地:你只要用WSDL定義了接口,就可以選擇任何語言來實現! Java , Python, 甚至C語言都可以,當然,我們的Visual Studo, C#和它結合得更好,歡迎使用。
人們趨之若鶩。
幾年以后
Dave Winer又一次來到了咖啡館,這一次他選擇了一個角落坐下,要了一杯咖啡,靜靜地聽大家聊天。
“你們知道嗎,微軟太坑爹了,那個SOAP實在是太難用了!”
“沒錯沒錯,羅嗦,羅嗦,太羅嗦了。你看看,我每次發(fā)個SOAP請求得多麻煩:”
- <?xml version="1.0"?>
- <soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:m="http://www.example.org">
- <soap:Header>
- </soap:Header>
- <soap:Body>
- <m:GetUser>
- <m:UserID>1001</m:UserID>
- </m:GetUser>
- </soap:Body>
- </soap:Envelope>
“這算什么,返回值也是同樣羅嗦的XML,解析起來實在是累死了。”
“是啊,如果沒有可視化工具的輔助,簡直是無法使用。”
Dave Winer一邊喝咖啡一邊想,沒辦法,這XML就是這樣,不過我們的SOAP搞得是不是有點過分了?
“我覺得這就是那些大廠商們?yōu)榱速嶅X而搞出的東西,都是為了賣他們的軟件,一點都不實用!”
“我們還是回歸最簡單的HTTP調用吧!” 有人提議。
比如想獲取一個用戶的信息,可以調用這樣的API http://xxx.com/getUser?id=1001
“服務器端還要返回又臭又長的XML嗎? ”
“不,我們可以用JSON這種數據格式,簡潔緊湊,對JavaScript非常友好,處理起來非常方便。”
大家都表示同意。
“大家別激動,如果用這種方式,和原來的XML-RPC本質上是一樣的,都是把服務器端看做是一堆函數的集合,然后客戶端去調用他們。Java RMI是通過Stub/Skeleton代理的方式,XML-RPC是通過XML的方式。” 一個叫做羅伊的小伙提醒道。
“那可不咋地,服務器端不就是一堆函數嗎?” 有人說道。
“大家轉換一下思路,別把他們當成函數,當成資源(Resource), 從動詞轉換成名詞試試。”
聽到羅伊這新奇的想法,一群人又圍了上來。
“名詞? 資源? ”
“是啊,比如說用戶,學生,訂單等等。他們天然可以用uri來表示。”
“有點意思, 那對這些資源怎么操作?”
“HTTP的方法GET,POST, DELETE,PUT,HEAD...... 可以充當動詞啊。”羅伊說道。
“我的媽啊,你竟然把HTTP的方法當成增刪改查了。”
話雖這么說,可是大家都覺得這種方式挺簡單的,充分利用了HTTP的特性,只要腦子里不要把服務器端看成函數,而是當作一堆名詞資源就可以了。
“這種方式叫什么名字?”
“RESTful API !”
“這RESTful看起來不錯啊,要不我們試試?”
“試試去!不行的話就找這個羅伊算賬!”
【本文為51CTO專欄作者“劉欣”的原創(chuàng)稿件,轉載請通過作者微信公眾號coderising獲取授權】






