基于Java的企業(yè)分布式應用
發(fā)表時間:2024-05-19 來源:明輝站整理相關軟件相關文章人氣:
[摘要]隨著電力企業(yè)信息化建設的不斷深入和發(fā)展,企業(yè)內部和企業(yè)與企業(yè)之間對信息、對數據的交換量大大增加,這些信息與數據越來越需要在不同的計算機網絡間傳送和交流。同時,由于各單位、各部門之間的現(xiàn)存的計算機網絡硬件設備與操作系統(tǒng)千差萬別,應用水平也參差不齊,因此,開發(fā)出跨平臺、可移植、高效安全的網絡分布式應用...
隨著電力企業(yè)信息化建設的不斷深入和發(fā)展,企業(yè)內部和企業(yè)與企業(yè)之間對信息、對數據的交換量大大增加,這些信息與數據越來越需要在不同的計算機網絡間傳送和交流。同時,由于各單位、各部門之間的現(xiàn)存的計算機網絡硬件設備與操作系統(tǒng)千差萬別,應用水平也參差不齊,因此,開發(fā)出跨平臺、可移植、高效安全的網絡分布式應用來服務于電力企業(yè),就顯得尤為重要。
在當今的編程術語里,分布式計算已經成為很常見的詞,它將企業(yè)的業(yè)務數據和程序分布在網絡的不同物理位置上,通過調動網絡上多臺計算機的處理能力,發(fā)揮遠程調用數據的功能。
遠程方法調用(Remote Method Invocation ,RMI),可以在不同的Java虛擬機(JVM)之間實現(xiàn)對象與對象的通信。JVM可以位于相同或不同計算機上,在多個JVM中,一個JVM可以調用存儲在其它JVM的對象的方法。
本文主要介紹RMI的特點,分析應用RMI進行企業(yè)分布式計算的原理,以及利用RMI實現(xiàn)基于Java的企業(yè)分布式應用的具體步驟。
遠程方法調用(RMI)的特點
1、TCP編程的缺點
由于Java編程語言設計之初就是面向對象和支持網絡的,因此,基于對象的RMI機制已經內置在Java平臺中。
我們經常會在網絡開發(fā)中使用TCP/IP編程,這樣,自然而然地就會涉及到Socket(套接字)編程。但是,使用Socket編程需要大量重復編碼,在復雜分布式操作時顯得非常麻煩,而且易于出錯。因此,如何快速、高效、安全、可擴展地進行網絡分布式計算,是開發(fā)者們一貫追求和倡導的主題。直到RMI的出現(xiàn),這種繁雜、低效的開發(fā)情況才有很大改觀。
2、RMI編程的特點
當我們利用對象序列化在網絡上分配對象時,RMI提供了非Java平臺無法匹敵的獨特而強大的分布式計算模型,RMI主要有以下特點:
客戶機可以向本地方法一樣調用遠程服務器上的方法;
可以根據接口指定客戶機/服務器編程合約;
可以從服務器對象缺省二進制類文件,自動生成調動/反調動代碼;
將Java編程模型擴展到機器邊界(和Java虛擬機(JVM)邊界之外),不需要任何特殊語法;
還可以和一個遠程方法調用中的數據同時傳輸行為(代碼)。
盡管RMI不是唯一的企業(yè)級遠程對象訪問方案,但它卻是最容易實現(xiàn)的。
3、RMI與CORBA
作為分布式應用程序框架的規(guī)范,COBRA首當其沖,它是由對象管理組織(OMG)開發(fā)的。與CORBA不同的是,CORBA能夠利用不同編程語言(例如C/C++、Basic等)開發(fā)實現(xiàn)分布式應用,而RMI是一種純Java解決方案。在RMI中,程序的所有部分都由Java語言編寫,這樣,開發(fā)出來的程序完全符合Java規(guī)范,便于實現(xiàn)跨平臺訪問、擴展和移植。按照筆者所在西北電力建設集團公司的情況看,服務器操作系統(tǒng)主要有Linux和Windows2000 Server,分別存在于公司和部門當中,它們是不同的系統(tǒng)平臺;同時,公司下屬各個工程項目部又距離很遠,近的幾十公里,遠則達到上千公里甚至位于國外,因此跨平臺和遠程訪問這兩大功能在開發(fā)企業(yè)應用系統(tǒng)時就必須考慮,而RMI恰恰能夠用它的自身特點來滿足編程需要。
RMI基本體系結構簡介
RMI通過TCP/IP在內部使用Socket,象其名稱暗示的那樣,它能夠幫助我們查找并執(zhí)行遠程對象的方法。RMI的目的是讓位于不同JVM中的對象,在外觀及行為上都像是本地的對象。
通常,我們把調用這種遠程對象的JVM,稱為客戶機;而把包括這種遠程對象的JVM,稱為服務器。
盡管對一個遠程對象的引用和獲得對本地對象的引用有所不同,但我們可以把遠程對象像本地對象一樣使用。應用程序并不知道一個對象是遠程的還是本地的。實際上,遠程對象上被調用的方法與本地對象上調用的方法,具有相同的語法結構。
作為RMI的底層(會包含復雜的Socket操作),它會自動截獲方法調用,找到遠程對象,然后處理遠程請求。筆者認為,RMI設計的重要之處,就在于不但在設計上實現(xiàn)了遠程訪問功能,而且實現(xiàn)了設計的透明性。
RMI的基本體系結構,概括起來說,由三個抽象層組成:
1、存根/框架層(Stubs/Skeletons Layer)
RMI為我們引入了兩種特殊類型的對象,稱為存根(Stub)和框架(Skeleton),它們組成了RMI的第一層。
在遠程通信的時候,要利用TCP/IP協(xié)議,做很多底層數據的打包傳輸。運用Java技術,我們先要把數據或者對象轉換成字節(jié)流(byte stream),便于網絡傳輸,這個過程叫匯集(marshaling);當收到遠程傳來的字節(jié)流后,我們要把流信息轉換成對象或者數據,這個過程叫解讀(unmarshaling),它與匯集剛好相反。
Stub和Skeleton層位于實際應用程序之下,建立在Proxy(代理)設計方案之上。Stub類的作用是遠程服務器實現(xiàn)的代理的角色,Stub是客戶方對象;Skeleton類用于幫助對象通過RMI鏈接與Stub通信,它從鏈路中讀取方法調用的參數,向遠程服務實現(xiàn)對象進行調用,接受返回值,然后再把返回值寫回到Stub。
2、遠程引用層(Remote Reference Layer)
遠程引用層定義和支持著RMI連接的調用語義(semantics)。
RMI進行遠程訪問要用到JRMP(Java Remote Method Protocol,即Java遠程方法協(xié)議),這一層提供專用于JRMP的RemoteRef對象,它位于java.rmi.server包內,代表著遠程對象的一個句柄。RemoteRef使用遠程引用來執(zhí)行遠程對象的一個遠程方法調用。
3、傳輸層(Transport Layer)
傳輸層在JVM之間建立基于流的網絡連接,并且負責設置和管理這些連接。這時候,RMI使用一種線級(wire-level)協(xié)議進行基于TCP/IP的連接,該協(xié)議就是Java遠程方法協(xié)議(JRMP,即Java Remote Method Protocol)。
在JDK版本1.2開始,JRMP不再需要Skeleton,而是使用reflection來建立與遠程服務的連接。為了生成Stub,我們須用rmic。
當前的RMI實現(xiàn)中,傳輸層建立在TCP/IP基礎上,設計用于在客戶和服務器之間建立一條連接(即使聯(lián)網有障礙)。
開發(fā)的基本步驟
我們使用RMI編寫Client/Server模式(客戶/服務器)應用程序,包括6個基本步驟:
1) 定義遠程接口
2) 實現(xiàn)遠程接口
3) 準備遠程調用的服務器對象
4) 生成殘根Stub(客戶代理)和框架Skeleton(服務器實體)
5) 用rmiregistry找到遠程對象
6) 運行測試RMI分布式應用
開發(fā)企業(yè)信息發(fā)布系統(tǒng)實例
在開發(fā)RMI進行分布式訪問之前,需要將各項功能模塊化,即把實際應用抽象成符合Java規(guī)范的類和接口模型,使這些類和接口之間互相協(xié)作,能實現(xiàn)各自獨立的功能,最后,可以把它們組合成統(tǒng)一的網絡分布式系統(tǒng)。
現(xiàn)在,我們就以開發(fā)公司信息發(fā)布系統(tǒng)為例,把主模塊(主要的類文件)的名稱暫定為InfoDistributeService(信息發(fā)布服務),為了保持應用開發(fā)的數據一致性和清晰度,接下來涉及的其它模塊命名也將以這個模塊命名為基準。
1、定義遠程接口
Java RMI運行環(huán)境要求任何可以遠程調用的方法必須放在遠程接口中。
該遠程接口用來擴展java.rmi.Remote接口,在Java API中,可以發(fā)現(xiàn)它沒有任何方法,只是個標志性接口,這樣,可以讓Java運行環(huán)境(JRE)認識每個接口的特殊屬性,以便能夠遠程訪問。
因此,按照信息發(fā)布服務的命名(InfoDistributeService),首先須將InfoDistributeRemote定義為遠程接口,同時僅放入一個供測試的方法 getRemoteInfo()來實現(xiàn)編碼,將所有模塊至于新建的enterprise.distribute包中,代碼如下:
// -----------InfoDistributeRemote.java-------------------
package enterprise. distribute;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface InfoDistributeRemote extends Remote{
public String getRemoteInfo() throws RemoteException;
}
2、實現(xiàn)遠程接口
這是一個實現(xiàn)遠程對象的類。如果實現(xiàn)了遠程接口,就能夠覆蓋(override)該對象中的所有方法,因此,遠程對象的實現(xiàn)類將真正包含我們希望導出的方法的代碼。
在遠程信息發(fā)布系統(tǒng)中,我們至少實現(xiàn)一個遠程接口的對象,它就是遠程可訪問的對象。這里,InfoDistributeService類可以為我們生成遠程可訪問對象的實例:
// -----------InfoDistributeService.java------------------
package enterprise. distribute;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class InfoDistributeService
extends UnicastRemoteObject implements InfoDistributeRemote{
public InfoDistributeService() throws RemoteException{
super();
}
// The return value of the method only for testing...
public String getRemoteInfo(){
return "Hello! I am a remote object.";
}
}
InfoDistributeService類實現(xiàn)遠程接口InfoDistributeRemote,并繼承java.rmi.server.UnicastRemoteObject。由于符合InfoDistributeRemote接口,因此該類除構造方法之外,還應有getRemoteInfo()方法,而且必須將該方法實現(xiàn)。
同時我們注意到,getRemoteInfo()方法拋出了java.rmi.RemoteException異常。由于遠程方法調用過程中,要進行很多的低級網絡操作,因此網絡錯誤可能在調用過程中隨時發(fā)生,這樣,遠程接口中的每個方法(盡管這里只有一個getRemoteInfo()方法)都必須拋出RemoteException異常,而且,java.rmi.RemoteException都要在代碼中顯式處理,即將所有RMI活動涉及的代碼都要放在try-catch塊中。
3、準備供遠程調用的服務器對象
這是一個作為服務器使用的類,它是相對于要訪問遠程方法的客戶端而言的。它存儲著綁定的字符串和對象。
將遠程對象設置成接受遠程調用就像啟動了偵聽ServerSocket對象的socket服務器。事實上在使用RMI時,TCP/IP協(xié)議的傳輸方式,在幕后發(fā)生了很多底層網絡操作,但是此刻,用戶不需要知道這些細節(jié),只需要逐步導出遠程對象:
在Java開發(fā)工具的較新版本JDK 1.4中,可以選擇java.rmi.UnicastRemoteObject或者java.rmi.Activation.Activatable。其中,更多用到的是UnicastRemoteObject,因為它顧名思義,就是在客戶機與服務器對象實例之間建立一對一連接。
4、生成Stub和Skeleton
由RMI生成的調動與反調動的遠程調動代碼也和Java的其它代碼一樣,都包含在后綴是.class的文件中。
運用RMI之后,在客戶機上生成的調動參數和反調動返回值的代碼稱為殘根(Stub),服務器上生成的反調動參數和進行實際方法調用調動返回值的代碼稱為框架(Skeleton)。
可以使用RMI自帶的命令行工具rmic(即RMI Compiler),先掃描遠程對象的.class文件,隨之生成殘根與框架代碼。工具rmic的原理
5、遠程客戶端:這是一個幫助我們訪問遠程方法提供幫助的類,它也是最終用戶。我們將使用查找和調用遠程方法的方法在該類中調用遠程方法。
典型的rmic調用如下(在當前目錄): 我們有了遠程接口和實現(xiàn),就可以用rmic對類進行編譯,生成Stub和Skeleton,在命令行窗口中使用以下代碼行:
// compile all java source files
javac enterprisedistribute*.java
// make stub and skeleton code
rmic enterprise.distribute.InfoDistributeService
運行完畢后,在當前目錄生成下列文件(即調動代碼):
InfoDistributeService_Stub.class
InfoDistributeService_Skel.class
5、用rmiregistry找到遠程對象
導出服務器方的對象之后,就可以遠程訪問,但客戶機還要設法與這些遠程對象取得聯(lián)系。
由于分布式應用程序可能涉及許多不同機器,因此通信的所有機器對之間需要建立初始連接,換句話說,就是要設法找到初始遠程對象,這項工作通過rmiregistry命令執(zhí)行。
JDK開發(fā)工具提供了實用程序RMI Registry,用來維護文本名和遠程對象之間的映射,可以進行遠程訪問。
在客戶機方,RMI注冊表可以通過同一java.rmi.Naming類的lookup()靜態(tài)方法用程序訪問。例如,遠程主機為iServer,遠程對象實例為InfoDistributeService實例,遠程端口號為5678,則客戶機如果查找遠程主機iServer中的遠程InfoDistributeService實例,那么引用實例時須使用遠程接口InfoDistributeRemote,代碼如下:
InfoDistributeRemote iServer=( InfoDistributeRemote)Naming.lookup("rmi://iServer:5678/InfoDistributeService");
在服務器方,導出的遠程對象可以通過java.rmi.Naming類在本地注冊rmiregistry的運行實例。例如,用rebind()方法將iService中的InfoDistributeService的實例與名稱InfoDistributeService相關聯(lián)。
InfoDistributeService iService=new InfoDistributeService();
Naming.rebind("/ InfoDistributeService",iService);
綜上所述,InfoDistributeRemote是個Java接口,因此iServer實際上是實現(xiàn)InfoDistributeRemote接口的本地對象實例,它不在遠程,而是遠程InfoDistributeService的本地表示。也就是說,它是由前述rmic工具自動生成的殘根碼InfoDistributeService_Stub.class的本地實例。
客戶機實現(xiàn)通過iServer變量維護遠程對象的調用。事實上,iServer引用變量是實現(xiàn)InfoDistribute接口的對象的本地引用,該對象是InfoDistributeService殘根實現(xiàn)。這個殘根和服務器方的框架一起通過InfoDistributeRemote接口調動/反調動所有遠程調用。
當我們把InfoDistributeService看成是實現(xiàn)InfoDistributeRemote接口的本地實例的時候,殘根代碼只在幕后進行工作,而實現(xiàn)這一切則變得非常透明。
由上述原理,可以進一步設計完善客戶機和服務器代碼,進而編寫出完整的應用程序。
6、運行測試RMI分布式應用
在確認已經設計好必須的幾大類模塊后,我們開始按以下步驟,運行并測試該信息發(fā)布系統(tǒng)的基本功能(即僅僅實現(xiàn)遠程接口中聲明的getRemoteInfo()方法)。
在前面定義的包enterprise.distribute中,執(zhí)行javac命令編譯后綴為.java的源文件。
javac enterprise/distribute/*.java
接著,用RMIC工具生成殘根與框架。
rmic enterprise.distribute.InfoDistributeService
編譯之后,需要確定客戶機與服務器發(fā)行版本的內容。因此,需用jar命令,將客戶機與服務器發(fā)行版本包裝成 .jar文件。 其中,包裝服務器的文件命令如下:
jar cvf InfoDistributeService.jar
enterprisedistributeInfoDistributeService.class
enterprisedistributeInfoDistributeRemote.class
enterprisedistributeInfoDistributeService_Stub.class
enterprisedistributeInfoDistributeService_Skel.class
同樣,包裝客戶機的類似于以上命令。
運行RMI應用程序
完成了第一階段的所有RMI試驗,然后運行信息發(fā)布應用程序。按照Java規(guī)范,需要依次啟動下列項目:
啟動RMIRegistry
在代碼目錄中用以下命令啟動rmiregistry實例,使之在控制臺開始運行。
start rmiregistry
啟動服務器
直接啟動服務器,生成InfoDistributeService遠程對象的實例,并向注冊表注冊。
start java -classpath InfoDistributeService.jar
enterprise.distribute.InfoDistributeService
啟動客戶機
最后,用java -classpath RemoteClient.jar 命令啟動客戶機,然后通過rmiregistry找到遠程信息發(fā)布服務,再通過遠程調用得到所需要的遠程信息。
結論
本文簡要闡述了Java RMI的特點,以及用RMI開發(fā)企業(yè)分布式應用的主要步驟。以遠程信息發(fā)布系統(tǒng)為例,簡要地說明了遠程對象訪問、遠程方法調用在信息發(fā)布時的原理和實現(xiàn)過程。
為了開發(fā)出更符合實際的企業(yè)分布式應用,RMI還可以結合對象序列化實現(xiàn)更加強大的功能,為我們開發(fā)更加靈活、高效的網絡分布式應用系統(tǒng)提供方便。