假如我是一個函數(shù)
這兩天有人問我RPC相關(guān)的問題,這其實是個非常“古老”的概念了,沒有經(jīng)歷過DCOM和RMI開發(fā)時代的同學可能對RPC的來龍去脈沒有直觀的認識,之前我寫過一篇文章做過講述,今天再發(fā)一次,希望能解決一部分人的疑惑吧!
我是一個函數(shù), 生活在內(nèi)存當中,我的家--用你們的“黑話”來說--就是進程的地址空間, 我的鄰居也是一個函數(shù),其中有一段很有趣的代碼。
我經(jīng)常去拜訪他,去的時候當然不能空著手,我會攜帶四個數(shù)字作為禮物送給他計算, 耐心等待他在CPU中忙活半天,最后,作為回贈的禮物,他告訴我一個地址,讓我去那里取結(jié)果。
拜訪的次數(shù)多了,我慢慢地琢磨出了我這個鄰居做的事情:房貸計算。
我給他發(fā)的四個參數(shù)分別是房貸總額,利率,貸款年限,還款方式(等額本息是1, 等額本金是2) 他告訴我的是一個地址,其實就是一個列表,存放著每個月應(yīng)還的月供、本金和利息。
用你們的“黑話”來說就是這樣:
List calculateHouseLoan(float total, float intrestRate, int years ,int loanType)
所有的調(diào)用都發(fā)生在本機內(nèi)的一個進程中, 大家把這種方式稱為本地過程調(diào)用。
這種調(diào)用方式速度飛快,眨眼間就可完成。
有時候,房貸計算鄰居會驚呼道:我賽,你給我發(fā)了一個多大的數(shù)啊, 800萬的貸款總額!
我就知道,帝都的房價又漲了!
日子過了一天又一天, 房價也漲了一天又一天。
一天早上, 我睡了一覺醒來感覺不太對勁,頭暈暈的,一般情況下這就表示昨天夜里系統(tǒng)重啟了。
還沒等我清醒過來, 我就接到上司(調(diào)用我的函數(shù))的命令,又要計算房貸了,我忍著頭暈趕緊去找鄰居,可是這一次卻換成了陌生人, 他笑瞇瞇的說:“是不是要找你的鄰居房貸計算啊”
“是啊”
“他已經(jīng)搬走了!”
“啊?他搬到哪兒去了?我怎么計算房貸?”
“ 他搬到另外一臺機器去住了,具體位置我也不清楚,不過從IP看應(yīng)該是在同一個機房吧”
說實話這個消息讓我吃驚不小,我聽人說過,想和網(wǎng)絡(luò)上的機器通信,那可比本機的同一進程內(nèi)的通信麻煩太多了。
之前我們生活在同一個進程中,每個函數(shù)的住處(地址)對大家來說都是可見的,想要調(diào)用了,直接去函數(shù)的住處去執(zhí)行代碼即可。
現(xiàn)在這個函數(shù)都搬走了,新的地址我也不知道,就是知道了,跨域網(wǎng)絡(luò)的調(diào)用,據(jù)說得使用什么socket,建立連接,在連接上按雙方商量好格式、次序來發(fā)送數(shù)據(jù), 接收數(shù)據(jù),聽著就頭大, 打死我也搞不定。
(碼農(nóng)翻身老劉注:socket的故事參見《張大胖和socket》)
陌生人看出了我的擔心, 笑著說:“放心吧, 我是他的客戶端代理,你盡管把那四個參數(shù)交給我,我來幫你搞定。”
這家伙自稱為客戶端代理的家伙竟然知道那個四個參數(shù)!也許能行,對我來說反正調(diào)用方式?jīng)]什么變化, 于是我將信將疑地像以前把4個參數(shù)傳遞過去, 他馬上就忙活起來,建立連接,發(fā)送數(shù)據(jù),接收數(shù)據(jù),過了很久(我感覺比平時要慢了100倍)他才說房貸已經(jīng)計算好了,數(shù)據(jù)在地址XXXX處, 你去拿吧。
我去那個地方一查,和往常一樣,每月的還款結(jié)果已經(jīng)整整齊齊的擺在那里了。
“你這個房貸計算的客戶端代理還真不含糊啊, 既然你是客戶端代理, 難道還有服務(wù)器端代理人? ”
“沒錯, 我還有個好基友,在服務(wù)器端忙活, 我和他約定好了消息的格式, 你交給我的數(shù)據(jù)其實我都通過socket發(fā)給他了,由他來調(diào)用真正的房貸計算, 然后再把結(jié)果發(fā)回來。”
“難道這就是傳說中的遠程過程調(diào)用(RPC) ?” 我問道
“是的, 我們這兩個代理人把臟活累活都幫著你們做了,把那些復(fù)雜的網(wǎng)絡(luò)細節(jié)都給隱藏起來了, 在你們看來和本地調(diào)用一樣。對了,有人會把我稱為Stub, 把我的好基友稱為 Skeleton, 我和他之間的交互是通過socket進行的, 有些RPC的代理人可能不用這么底層的東西,直接用HTTP, 不過沒關(guān)系,只要兩端的代理人約定好就行了, 關(guān)鍵是要給你們提供一個舒適的體驗。”
“我想到一個問題, 如果我傳遞給你的不是簡單的float, int型的參數(shù), 而是內(nèi)存中的對象, 怎么處理?”
“當然要做序列化了, 要不然怎么通過網(wǎng)絡(luò)發(fā)送啊, 其實float,int也得做序列化, 把內(nèi)存中的值和對象變成二進制流,這樣才能發(fā)送出去。到了我的好基友那邊,他還得做反序列化,把而二進制流再轉(zhuǎn)化為對象, 然后才能調(diào)用真正的函數(shù), 唉,這工作實在是麻煩啊。”
我對他表示了深切的同情和敬意, 為了我們能做透明的遠程調(diào)用,這些代理們真不容易。
“我聽說還能用XML和JSON?” 我問道
“你知道的不少嘛 !有些人在使用HTTP作為通信協(xié)議的時候, 喜歡把對象變成文本,例如XML/JSON,可讀性比較好,但是你要知道,雖然應(yīng)用層的HTTP中看起來時文本, 但是到了底層通道例如TCP發(fā)送出去的時候,那還得變成二進制流, 到了目的地再把他們轉(zhuǎn)化成文本。”
(碼農(nóng)翻身老劉注:參加《序列化:一個老家伙的咸魚翻身》)
聊了半天,我們越來越熟, 我無意間談起了他的身世, 他說 :“我們這些代理人啊,出生的方式主要有兩種, 一種就是程序員們一行行代碼的把我們給敲出來、費心而費力, 另外一種就是自動生成。”
“自動生成,具體怎么做?”
“拿Java來舉個例子, 你可以先定義一個接口(interface), 讓這個接口擴展自java.rmi.Remote, 然后寫個實現(xiàn)類, 最后用一個工具rmic就可以自動生成客戶端和服務(wù)器端的代理人了 , 是不是很簡單? ”
(碼農(nóng)翻身老劉注:從JDK5.0開始, 連這個rmic這一步都可以省略, 完全由JVM自動生成,運行時可以把客戶端代理人下載到客戶端。)
網(wǎng)絡(luò)的世界遠比單機精彩, 不知不覺我已經(jīng)和這個代理人聊了將近800毫秒, 我的上司已經(jīng)等不及了,他抱怨地說:“這次怎么這么慢?難道人類在調(diào)試,在你這里加了斷點?”
我說 :“沒有調(diào)試, 原來是本地過程調(diào)用,現(xiàn)在變成遠程過程調(diào)用了!”
【本文為51CTO專欄作者“劉欣”的原創(chuàng)稿件,轉(zhuǎn)載請通過作者微信公眾號coderising獲取授權(quán)】