綜述
Rmi自從JDK1.1就已經(jīng)出現(xiàn)了。而對(duì)于為什么在JAVA的世界里需要一個(gè)這樣 思想理念就需要看下:RMI問世由來。其實(shí)真正在國(guó)內(nèi)使用到它的比較少,不過在前些年比較火的EJB就是在它的基礎(chǔ)上進(jìn)一步深化的。從本質(zhì)上來講RMI的興起正是為了設(shè)計(jì)分布式的客戶、服務(wù)器結(jié)構(gòu)需求而應(yīng)運(yùn)而生的,而它的這種B/S結(jié)構(gòu)思想能否和我們通常的JAVA編程更加貼切呢?言外之意就是能否讓這種分布式的狀態(tài)做到更加透明,作為開發(fā)人員只需要按照往常一樣開發(fā)JAVA應(yīng)用程序一樣來開發(fā)分布式的結(jié)構(gòu)。那現(xiàn)在的問題是如何來劃平這個(gè)鴻溝呢?首先我們來分析下在JAVA世界里它的一些特點(diǎn)因素:
l JAVA使用垃圾收集確定對(duì)象的生命周期。
l JAVA使用異常處理來報(bào)告運(yùn)行期間的錯(cuò)誤。這里就要和我們網(wǎng)絡(luò)通訊中的異常相聯(lián)系起來了。在B/S結(jié)構(gòu)的網(wǎng)絡(luò)體系中我們的這種錯(cuò)誤性是非常常見的。
l JAVA編寫的對(duì)象通過調(diào)用方法來調(diào)用。由于網(wǎng)絡(luò)通訊把我們的客戶與服務(wù)器之間阻隔開了。但是代理的一種方式可以很好的提供一種這樣的假象,讓開發(fā)人員或者使用者都感覺是在本地調(diào)用。
l JAVA允許一種高級(jí)的使用類加載器(CLassLoader)機(jī)制提供系統(tǒng)類路徑中沒有的類。這話什么意思?
主要特點(diǎn)
上面說到了分布式的方式和我們的JAVA中如何更好的劃平這個(gè)鴻溝,需要具備的特質(zhì)。
那這里我們來看看我們所謂的RMI到底跟我們普通的JAVA(或者說JavaBean)存在一些什么樣的差異:
l RMI遠(yuǎn)程異常(Remote Exception):在上面我們也提到了一個(gè)網(wǎng)絡(luò)通訊難免有一些無(wú)論是軟件級(jí)別的還是硬件級(jí)別的異?,F(xiàn)象,有時(shí)候這些異常或許是一種無(wú)法預(yù)知的結(jié)果。讓我們開發(fā)人緣如何來回溯這種異常信息,這個(gè)是我們開發(fā)人員要關(guān)心的。因此在調(diào)用遠(yuǎn)程對(duì)象的方法中我們必須在遠(yuǎn)程接口中(接口是一種規(guī)范的標(biāo)準(zhǔn)行為)所以在調(diào)用的這個(gè)方法體上需要簽名注明:java.rmi,RemoteException.。這也就注明了此方法是需要調(diào)用遠(yuǎn)程對(duì)象的。
l 值傳遞 :當(dāng)把對(duì)象作為參數(shù)傳遞給一個(gè)普通的JAVA對(duì)象方法調(diào)用時(shí),只是傳遞該對(duì)象的引用。請(qǐng)注意這里談到的是對(duì)象的“引用”一詞,如果在修改該參數(shù)的時(shí)候,是直接修改原始對(duì)象。它并不是所謂的一個(gè)對(duì)象的備份或者說拷貝(說白了就是在本JVM內(nèi)存中的對(duì)象)。但是如果說使用的是RMI對(duì)象,則完全是拷貝的。這與普通對(duì)象有著鮮明的對(duì)比。也正是由于這種拷貝的資源消耗造就了下面要說到的性能缺失了。
l 調(diào)用開銷:凡是經(jīng)過網(wǎng)絡(luò)通訊理論上來說都是一種資源的消耗。它需要通過編組與反編組方式不斷解析類對(duì)象。而且RMI本身也是一種需要返回值的一個(gè)過程定義。
l 安全性:一談到網(wǎng)絡(luò)通訊勢(shì)必會(huì)說到如何保證安全的進(jìn)行。
概念定義
在開始進(jìn)行原理梳理之前我們需要定義清楚幾個(gè)名詞。對(duì)于這些名詞的理解影響到后的深入進(jìn)行。
1. Stub(存根,有些書上也翻譯成:樁基在EJB的相關(guān)書籍中尤為體現(xiàn)這個(gè)意思):
這里舉例說明這個(gè)概念起(或許不夠恰當(dāng))。例如大家因公出差后,都有存在一些報(bào)銷的發(fā)票或者說小票。對(duì)于你當(dāng)前手頭所拿到的發(fā)票并不是一個(gè)唯一的,它同時(shí)還在你發(fā)生消費(fèi)的地點(diǎn)有一個(gè)復(fù)印件,而這個(gè)復(fù)印件就是所謂的存根。但是這個(gè)存根上并沒有很多明細(xì)的描述,只是有一個(gè)大概的金額定義。它把很多的細(xì)節(jié)費(fèi)用都忽略了。所以這個(gè)也是我們說的存根定義。而在我們RMI的存根定義就是使用了這樣一個(gè)理解:在與遠(yuǎn)程發(fā)生通訊調(diào)用時(shí),把通訊調(diào)用的所有細(xì)節(jié)都通過對(duì)象的封裝形式給隱藏在后端。這本身就符合OOAD的意思理念。而暴露出來的就是我們的接口方式,而這種接口方式又和服務(wù)器的對(duì)象具有相同的接口(這里就和我們前面舉例說的報(bào)銷單據(jù)聯(lián)系上了,報(bào)銷單據(jù)的存根不知道會(huì)有一個(gè)什么形式發(fā)生具體問題,而你手執(zhí)的發(fā)票具體就需要到貴公司去報(bào)銷費(fèi)用,而這里的公司財(cái)務(wù)處就是所謂的服務(wù)器端,它才是真正干實(shí)質(zhì)性問題的。)因此作為開發(fā)人員只需要把精力集中在業(yè)務(wù)問題的解決上,而不需要考慮復(fù)雜的分布式計(jì)算。所有這些問題都交給RMI去一一處理。
2. Skeleton(一些書翻譯叫骨架,也叫結(jié)構(gòu)體):它的內(nèi)部就是真正封裝了一個(gè)類的形成調(diào)用體現(xiàn)機(jī)制。包括我們熟知的ServerSocket創(chuàng)建、接受、監(jiān)聽、處理等。
3. Mashalling(編組):在內(nèi)存中的對(duì)象轉(zhuǎn)換成字節(jié)流,以便能夠通過網(wǎng)絡(luò)連接傳輸。
4. Unmashalling(反編組):在內(nèi)存中把字節(jié)流轉(zhuǎn)換成對(duì)象,以便本地化調(diào)用。
5. Serialization(序列化):編組中使用到的技術(shù)叫序列化。
6. Deserializationg(反序列化):反編組中使用到的技術(shù)叫反序列化。
客戶端
既然我們知道stub主要是以接口的方式來暴露體現(xiàn),而stub主要 也是以代理的方式來具體實(shí)施。那在RMI中的這種接口有哪些特性呢?(Remote Interface)
1) 必須擴(kuò)展(extends)java.rmi.Remote接口,因?yàn)檫h(yuǎn)程接口并不包含任何一個(gè)方法,而是作為一個(gè)標(biāo)記出現(xiàn),它就是需要告訴JVM在RunTime的時(shí)候哪些是常規(guī)對(duì)象,哪些屬于遠(yuǎn)程對(duì)象。通過這種標(biāo)識(shí)的定義能讓JVM了解類中哪些方法需要編組,通過了編組的方式才能通過網(wǎng)絡(luò)序列化的調(diào)用;
2) 接口必須為public(公共),它的好處不言而喻的——能夠方便的讓所有人員來調(diào)用。
3) 接口方法還需要以異常拋出(例如:RemoteException),至于它的用處我們?cè)谇懊嬉蔡岬竭@里就不再?gòu)?fù)述;
4) 在調(diào)用一個(gè)遠(yuǎn)程對(duì)象期間(運(yùn)行期間),方法的參數(shù)和返回值都要必須是可序列化的。至于為什么需要這么做?這里的緣由不用多說大家也應(yīng)該清楚了解。
服務(wù)端
既然我們知道stub所做的事情是一個(gè)簡(jiǎn)單的代理轉(zhuǎn)發(fā)動(dòng)作,那我們真正要做的對(duì)象就在服務(wù)端來做了。對(duì)于使用簡(jiǎn)單的RMI我們直接去指定,但是往往一旦使用了RMI對(duì)象就存在非常多的遠(yuǎn)程方法調(diào)用,這個(gè)時(shí)候服務(wù)器端對(duì)于這么多的調(diào)用如何來判別或者說識(shí)別呢?這里就要說到的是對(duì)于RMI實(shí)現(xiàn)它會(huì)創(chuàng)建一個(gè)標(biāo)識(shí)符,以便以后的stub可以調(diào)用轉(zhuǎn)發(fā)給服務(wù)器對(duì)象使用了,而這種方式我們通常叫服務(wù)器RMI的注冊(cè)機(jī)制。言外之意就是讓服務(wù)器端的對(duì)象注冊(cè)在RMI機(jī)制中,然后可以導(dǎo)出讓今后的stub按需來調(diào)用。那它又是如何做到這種方式的呢?對(duì)于RMI來說有兩種方式可以達(dá)到這種效果:
a) 直接使用UnicastRemoteObject的靜態(tài)方法:exportObject;
b) 繼承UnicastRemoteObject類則缺省的構(gòu)造函數(shù)exportObject。
現(xiàn)在大家又會(huì)問他們之間又有什么區(qū)別呢?我該使用哪種方式來做呢,這不是很難做抉擇嗎?從一般應(yīng)用場(chǎng)景來說區(qū)別并不是很大,但是,這里說了“但是”哦,呵呵。大家知道繼承的方式是把父類所具備的所有特質(zhì)都可以完好無(wú)損的繼承到子類中而對(duì)于類的總老大:Object來說里面有:equals()、hashCode()、toString()等方法。這是個(gè)什么概念呢?意思就是說如果對(duì)于本地化的調(diào)用,他們兩個(gè)的方法(a,b)基本區(qū)別不是很大。但是我們這里強(qiáng)調(diào)的RMI如果是一種分布式的特定場(chǎng)景,具備使用哈希表這種特性就顯得尤為重要了。
剛才說了服務(wù)端采用什么方法行為導(dǎo)出對(duì)象的。那現(xiàn)在導(dǎo)出后的對(duì)象又對(duì)應(yīng)會(huì)發(fā)生什么情況呢?
首先被導(dǎo)出的對(duì)象被分配一個(gè)標(biāo)識(shí)符,這個(gè)標(biāo)識(shí)符被保存為:java.rmi.server.ObjID對(duì)象中并被放到一個(gè)對(duì)象列表中或者一個(gè)映射中。而這里的ID是一個(gè)關(guān)鍵字,而遠(yuǎn)程對(duì)象則是它的一個(gè)值(說到這大家有沒有覺得它原理非常像HashMap的特質(zhì)呢?沒錯(cuò),其實(shí)就是使用了它的特性),這樣它就可以很好的和前面創(chuàng)建的stub溝通。如果調(diào)用了靜態(tài)方法UnicastRemoteObject.export(Remote …),RMI就會(huì)選擇任意一個(gè)端口號(hào),但這只是第一調(diào)用發(fā)生在隨后的exportObject每次調(diào)用都把遠(yuǎn)程對(duì)象導(dǎo)出到該遠(yuǎn)程對(duì)象第一被導(dǎo)出時(shí)使用的端口號(hào)。這樣就不會(huì)產(chǎn)生混亂,會(huì)把先前一一導(dǎo)出的對(duì)象全部放入到列表中。當(dāng)然如果采用的是指定端口的,則按照對(duì)應(yīng)顯示的調(diào)用方式使用。這里稍作強(qiáng)調(diào)的是一個(gè)端口可以導(dǎo)出任意數(shù)目的對(duì)象。
(待續(xù)……)
posted on 2009-02-02 12:04
葉澍成 閱讀(3402)
評(píng)論(3) 編輯 收藏 所屬分類:
java基礎(chǔ) 、
分布式