深入理解rmi原理

來翻譯一篇rmi原理性文章:

成文動機

寫這篇文章是出于我個人的經驗,我是最近才知道java rmi這個東西的。但是立即我對這部分內容產生了濃厚的興趣,尤其是 stubs 和skeletons。讓我特別的感興趣的是RMI的設計者設計的這個框架使得rmi客戶端感覺就好像在調用本地方法一樣,但是實際上是在遠程對象上執行的。隨著我學習的深入,我開始了解了RMIRegistry和更多的東西。對很多的問題我都很困惑,比如RMIRegistry 真正的角色是什么?他是必須存在的么?stub對象是在哪里創建的?(服務端?客戶端?注冊中心?),客戶端怎么知道服務端監聽的端口是哪一個?難道所有的服務端都是使用1099來監聽客戶端的嗎?

這篇文章試圖來回答我之前的這些疑惑。我參考了網上很多關于rmi書籍和文章,但是都不能找到這些疑惑的線索,沒有人告訴我這些事情都是怎么運轉的,每個人都在告訴我怎么寫代碼,如此而已,沒有任何關于底層的工作機制。經過很長時間的研究和實驗,我得到了我想要的答案。所以我想分享我的知識,因為還有很多人可能遇到和我同樣的疑惑。

這篇文章試圖來回答關于RMI原理的幾個問題:
1、誰創建了stubs對象,服務器?客戶端?注冊中心?
2、怎么知道服務器監聽的端口是哪一個?
3、注冊中心對于RMI系統來說是必須的嗎?
4、如果沒有注冊中心,RMI可以運行嗎?

在下面的部分我會詳細解答這些問題,如果你想快速得到這些問題的答案,可以直接去看對應的部分,這里我會一步一步的引導你理解rmi中到底發生了什么

首先,請忘記注冊中心 !!

是的,從現在開始忘掉注冊中心,假設這玩意從現在開始不存在
最開始的場景大概是這樣的:我們有一個server和一個client。server 繼承了 java.rmi.server.UnicastRemoteObject。client和server運行在不同的機器上面
現在我們的需求是這樣的:client想執行一個在遠程機器上server的一個方法。

我們如果做到這一點?java rmi 會處理這些問題,解決方案肯定會涉及到socket網絡編程,因為server運行在遠程機器上,解決這個問題的關鍵點在于
1.客戶端如何從處理網絡連接中解耦開來
2.客戶端如何能就像調用本地方法一樣來調用遠程機器上的方法,因此rmi的開發人員就引入了stub和skeleton模型。

所有與網絡相關的代碼都放在了stub和skeleton中,這樣客戶端和服務端就不需要處理網絡相關的代碼了。stub同樣也實現了和服務端同樣 java.rmi.Remote接口,這樣當client想調用server上方法的時候,就可以調用stub上的相同的方法,但是stub里面只有和網絡相關的處理邏輯,并沒有對應的業務處理邏輯,比如說server上有一個add方法,stub中同樣也有一個add方法,但是stub上的這個add方法并不包含添加的邏輯實現,他僅僅包含如何連接到遠程的skeleton、調用方法的詳細信息、參數、返回值等等。

所以,目前看來大概是這個樣子的:
Client<-->stub<-->[NETWORK]<-->skeleton<-->Server

客戶端和stub對話,stub和skeleton對話,skeleton和server對話,server執行真正的方法,然后把結果原路返回,這樣看來就有4個獨立的部分,也就是說你大概需要4個class了。

在jdk1.2之后,skeleton就被合并到server中了,所以看起來是這個樣子滴:
Client<--->stub<--->[NETWORK]<--->Server_with_skeleton

socket 層的詳細內容

現在,你可以學習在socket層通信是如何完成了的,這部分非常重要!,這部分內容開始變的有點繞,所以,請特別注意這部分內容。

1.server在遠程機器上監聽一個端口,這個端口是jvm或者os在運行時隨機選擇的一個端口。可以說server在遠程機器上在這個端口上導出自己。
2.client并不知道server在哪,以及sever監聽哪個端口,但是他有stub,stub知道所有這些東西,這樣client可以調用stub上他想調用的任何方法。
3.client調用給你stub上的方法
4.stub鏈接server監聽的端口并發送參數,詳細過程如下:
a.client連接server監聽的端口
b.server收到請求并創建一個socket來處理這個鏈接
c.server繼續監聽到來的請求
d.使用雙方協定的歇息,傳送參數和結果
e.協議可以是JRMP或者 iiop
5.方法在遠程server上執行,并發執行結果返回給stub
6.stub返回結果給client,就好像是stub執行了這個方法一樣。

所以整個過程就結束了,但是等一下!回頭再看第2點,說stub知道server在哪,他監聽的端口,這怎么么可能?如果client不知道server的host和port,他怎么能創建一個知道所有這一切的stub對象呢?更何況是在服務端端口是隨機選擇的

啟動的窘境

這是在很多現實生活中最大的問題之一。解決方案在于我們如何通知client這些server端的細節。RMI的設計者們有應對啟動問題的應急措施,那就是RMIRegistry 存在的必要了。RMIRegistry 可以認為是一個服務,它提供了一個hashmap,里面是 public_name, Stub_object 名值對。比如我有一個遠程服務對象叫做 Scientific_Calculator,然后我想把這個服務對外公布為 calc,這樣會在server上創建一個stub對象,燃火把他注冊到RMIRegistry ,這樣client就可以從RMIRegistry 中得到這個stub對象了,你可以使用一個工具類 java.rmi.Naming 來方便的操作注冊和操作。

用一個栗子來說明整個過程

考慮一個計算的應用,它有一個add的方法,你想把這個方法對外發布成一個遠程對象,遠程接口叫做Calc

Paste_Image.png
Paste_Image.png

這就是所有的類,編譯這些類得到class,使用RMI 編譯器生成stub的class,使用.keep選項得到源代碼。

Paste_Image.png

Source code for CalcImpl_Stub.java:

Paste_Image.png

現在看一個這個生成的stub源代碼,注意到他的構造參數需要個RemoteRef類型的對象,這個對象從哪里獲取呢?繼續下面看。
下面先運行程序:
開2個console

Paste_Image.png

然后就可以看到結果:7!!!
這里到底是怎么搞得?

我會給你看這個背后到底發生了什么
1.首先RMIRegistry 運行在server端,RMIRegistry 自身也是一個遠程對象。有一點需要注意的是:所有的遠程對象(繼承了UnicastRemoteObject對象)都會在sever上任意的端口導出自己,因為RMIRegistry 也是一個遠程對象,他也在server上導出自己,只是這個端口是廣為人知的1099,“廣為人知”的意思是所有的client都知道這個端口

2.服務端運行在server上,在UnicastRemoteObject構造函數里面,他把自己導出在server上一個任意端口上,這個端口client是不知道的

3.當你調用Naming.rebind()的時候,會傳入一個CalcImpl 的引用(這里叫做 c)作為第2個參數,Naming 就會構造一個stub對象,詳細如下:
a.Naming會使用getClass來獲取類的名字,這里就是CalcImpl
b.加上后綴_Stub 變成了 CalcImpl _Stub
c.加載CalcImpl_Stub.class到虛擬機中
d.從c中獲取RemoteRef 對象

Paste_Image.png

e.就是這個ref對象中封裝了服務端細節,包括服務端的hostname、port
f.獲取了RemoteRef 對象之后,就可以構造stub對象了。
g.傳遞stud對象到RMIRegistry中進行綁定,即(publicname,stub)
f.RMIRegistry 中內部使用一個hashmap來存儲(publicname,stub)

4.當客戶端使用 Naming.lookup()的時候,會傳入public name 作為參數,RMIRegistry 就會返回stub給客戶端調用

RMIRegistry 是必須的嗎?

No,RMIRegistry 起到的作用只是為了方便client獲取到stub對象,如果還有其他的方法讓client拿到stub就不需要RMIRegistry 了,因為client一旦拿到了stub就不需要RMIRegistry 了

直接在client new一個stub對象不就可以了?

No,stub構造函數中需要一個RemoteRef 對象,這個對象只能在server端獲取。

阿里云服務器限時打折!點擊獲取

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,106評論 6 542
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,441評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,211評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,736評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,475評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,834評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,829評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,009評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,559評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,306評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,516評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,038評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,728評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,132評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,443評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,249評論 3 399
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,484評論 2 379

推薦閱讀更多精彩內容

  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,826評論 18 139
  • 代理模式是什么 如上圖所示,代理代表著另一終端中的某個真實服務對象,Client 調用代理(Client help...
    野生西瓜閱讀 2,335評論 2 14
  • 1. Java基礎部分 基礎部分的順序:基本語法,類相關的語法,內部類的語法,繼承相關的語法,異常的語法,線程的語...
    子非魚_t_閱讀 31,733評論 18 399
  • Java是一個支持并發、基于類和面向對象的計算機編程語言。下面列出了面向對象軟件開發的優點: 代碼開發模塊化,更易...
    安安靜靜寫代碼閱讀 1,101評論 0 8
  • “酸甜苦辣”化勞動 “酸” 心中苦苦“心塞” “甜” 開朗,樂觀,快樂 “苦” 默默的哭 ...
    小小小朱閱讀 513評論 0 0