Java分佈式處理技術(RMI,JDNI)

http://hedaoyuan.blog.51cto.com/4639772/813702java

1.1 RMI的基本概念

1.1.1 什麼是RMI

RMI(Remote Method Invocation)遠程方法調用是一種計算機之間對象互相調用對方函數,啓動對方進程的一種機制,使用這種機制,某一臺計算機上的對象在調用另一臺計算機上的方法時,使用的程序語法規則和在本地機上對象間的方法調用的語法規則同樣。

1.1.2 RMI的用途

一、  分佈式體系結構
咱們爲何要使用分佈式計算呢?
Ø 當咱們想與多個用戶或客戶機共享一箇中央資源(如一個數據庫)時,就會使用分佈式計算。
Ø 分佈式計算用來利用多個系統的組合計算能力,以便比在單個系統上更有效或更快地解決問題。
能夠用多種方法配置多個計算機系統以共享處理,包括共享內存、共享磁盤或只是共享一條公共通訊通道。最新的技術容許物理上相隔很遠的系統可以在處理計算問題時協同工做。
關於利用計算能力這一主題,因特網及伴隨的通訊協議 TCP/IP 的出現已使無數的計算機系統前所未有地鏈接起來。對一些應用程序來講,可以利用如此多的計算功能來解決問題是使人滿意的。甚至更吸引人的是,大多數計算機系統都有充足的空閒時間,能夠幫助解決其它問題。未來,網格計算會利用分佈式計算能力進行出售,這與電力行業出售電能很是類似。
二、  Java 分佈式對象編程技術
RMI是Enterprise JavaBeans的支柱,是創建分佈式Java應用程序的方便途徑。只要按照RMI規則設計程序,能夠沒必要再過問在RMI之下的網絡細節了,如:TCP和Socket等等。任意兩臺計算機之間的通信徹底由RMI負責。調用遠程計算機上的對象就像本地對象同樣方便。

1.1.3 RMI應用程序分類

依據RMI應用程序各部分職責,可對應用程序進行以下分類:
Ø 服務器程序:服務器程序將建立多個遠程對象,並使每一個對象可以被引用。等待客戶端調用建立好的遠程對象上的方法。
Ø 客戶端程序:從服務端程序中獲得一個或多個遠程對象的引用。客戶端能用此引用調用遠程對象上的方法。
Ø 對等計算程序:雙方地位相等,互爲對方的服務器和客戶端。

1.2 建立RMI應用程序步驟

一、  定義遠程接口
在 Java 中,遠程對象是實現遠程接口的類的實例, 遠程接口聲明每一個要遠程調用的方法。在須要建立一個遠程對象的時候,咱們經過傳遞一個接口來隱藏基層的實施細節,客戶經過接口句柄發送消息便可。遠程接口具備以下特色:
Ø 遠程接口必須爲public屬性。若是不這樣,除非客戶端與遠程接口在同一個包內,不然當試圖裝入實現該遠程接口的遠程對象時,調用會獲得錯誤結果。
Ø 遠程接口必須擴展接口java.rmi.Remote。
Ø 除與應用程序自己特定的例外以外,遠程接口中的每一個方法都必須在本身的throws從句中聲明java.rmi.RemoteException。(或 RemoteException 的父類)。
代碼範例1
package com.itjob;
import java.rmi.*;
 
public interface RmiSample extends Remote{
public int sum(int a,int b) throws RemoteException;
 
}
 
二、  實現遠程接口
遠程對象實現類必須擴展遠程對象java.rmi.UnicastRemoteObject類,並實現所定義的遠程接口。遠程對象的實現類中包含實現每一個遠程接口所指定的遠程方法的代碼。這個類也能夠含有附加的方法,但客戶只能使用遠程接口中的方法。由於客戶是指向接口的一個句柄,而不是它的哪一個類。必須爲遠程對象定義構造函數,即便只准備定義一個默認構造函數,用它調用基礎類構造函數。由於基礎類構造函數可能會拋出java.rmi.RemoteException,因此即便別無它用必須拋出java.rmi.RemoteException例外。
代碼範例2
package com.itjob.rmi;
import java.rmi.*;
import java.rmi.server.*;
import com.itjob.RmiSample ;
/**
遠程接口實現類,繼承了UnicastRemoteObject並實現了RmiSample遠程接口
*/
public class RmiSampleImpl extends UnicastRemoteObject implements RmiSample{
//覆蓋默認構造函數並拋出RemoteException
public RmiSampleImpl() throws RemoteException{
super();
}
//全部遠程實現方法必須拋出RemoteException
public int sum(int a,int b) throws RemoteException{
return a+b;
}
}
 
三、  編寫服務器類
包含 main 方法的類能夠是實現類自身,也能夠徹底是另外一個類。下面經過RmiSampleServer來建立一個遠程對象的實例,並經過java.rmi.registry.LocateRegistry類的createRegistry 方法從指定端口號啓動註冊服務程序,也能夠經過執行 rmiregistry 命令啓動註冊服務程序,註冊服務程序的缺省運行端口爲 1099。
代碼範例3
package com.itjob.rmi;
 
import java.rmi.*;
import java.rmi.registry.*;
 
public class RmiSampleServer{
public static void main(String[] args){
/*建立和安裝一個安全管理器,令其支持RMI.做爲Java開發包的一部分
*適用於RMI惟一一個是RMISecurityManager.
*
if(System.getSecurityManager() == null) {
System.setSecurityManager(new RMISecurityManager());
}
*/
try{
LocateRegistry.createRegistry(8808);
RmiSampleImpl server=new RmiSampleImpl();
Naming.rebind("//localhost:8808/SAMPLE-SERVER",server);
System.out.println ("遠程對象註冊成功,RMI服務已經啓動,等待客戶端調用....");
}catch(java.net.MalformedURLException me){
System.out.println ("Malformed URL:"+me.toString());
}catch(RemoteException re){
System.out.println ("Remote exception:"+re.toString());
}catch(AlreadyBoundException abe){
System.out.println ("(AlreadyBound exception:"+ abe.toString());
}
}
}
 
Ø 代碼範例3中將將遠程對象名字綁定到對遠程對象的引用上:
LocateRegistry.createRegistry(8808);指定本RMI服務程序不使用默認端口1099,而是使用本身指定的端口8808。
Naming.rebind("//localhost:8808/SAMPLE-SERVER" , Server);將遠程對象在服務器上註冊並指定了將查找遠程對象引用的URL,URL格式爲//host:port/name。其中 host 是註冊表所在的主機(遠程或本地),port 是註冊表接受調用的端口號,name 是未經註冊表解釋的簡單字符串。host 和 port 二者都是可選項。若是省略了host,則主機默認爲本地主機。若是省略了 port,則端口默認爲 1099,該端口是 RMI 的註冊表 rmiregistry使用的「著名」端口。
代碼範例3的運行結果如圖所示:
 
 14.1 代碼範例 3 運行結果
 
四、  編寫使用遠程服務的客戶機類
客戶機類的主要功能有兩個,一是經過Naming.lookup方法來構造註冊服務程序 stub 程序實例,二是調用服務器遠程對象上的遠程方法。
代碼範例4
package com.itjob.rmi;
import java.rmi.*;
import java.rmi.server.*;
public class RmiSampleClient {
public static void main(String[] args)
{
try {
String url = "//localhost:8808/SAMPLE-SERVER";
RmiSample RmiObject = (RmiSample)Naming.lookup(url);
System.out.println(" 1 + 2 = " + RmiObject.sum(1,2) );
} catch (RemoteException exc) {
System.out.println("Error in lookup: " + exc.toString());
} catch (java.net.MalformedURLException exc) {
System.out.println("Malformed URL: " + exc.toString());
} catch (java.rmi.NotBoundException exc) {
System.out.println("NotBound: " + exc.toString());
}
}
}
 
五、  爲遠程對象實現建立根和幹
客戶端是經過Naming.lookup方法來構造註冊服務程序 stub 程序實例,經過該實例的引用來發起對遠程對象方法調用的,因此在運行運行客戶端應用前必須爲遠程對象實現建立根(stub)和幹(Skeleton)。要建立存根程序和骨架文件,應以包含遠程對象實現的已編譯類包全名運行 rmic 編譯器。存根(Stub)是遠程對象在客戶端的代理,它將RMI調用傳遞給服務器端的骨架(Skeleton),後者負責將該調用傳遞給實際的遠程方法。在命令行模塊下運行RMIC調用:
 
 14. 2  RMIC 命令
調用RMIC命令運行結果如圖所示:
 
14.3 RMIC 運行結果
Ø 咱們能夠看系統幫咱們生成了存根(stub)RmiSampleImpl_Stub.class但系統並無幫咱們生成骨架(Skeleton)RmiSampleImpl_ Skeleton.class。這與JDK的版本有關:
採用JDK1.4版本運行rmic com.itjob.rmi. RmiSampleImpl命令系統將會生成存根(stub)RmiSampleImpl_Stub.class和骨架(Skeleton)RmiSampleImpl_ Skeleton.class兩個類文件;
採用JDK1.5版本運行rmic com.itjob.rmi. RmiSampleImpl命令系統將只會生成存根(stub)RmiSampleImpl_Stub.class,骨架(Skeleton)RmiSampleImpl_ Skeleton.class的功能將通反射技術由系統在運行時自動實現;
六、  運行程序
依次作完上述步驟後,咱們如今來運行一下咱們的RMI應用。先運行服務端程序,運行結果如圖14.1所示。
接下來咱們運行客戶端程序(代碼範例4),運行結果如圖所示:
 
圖14.4  客戶端程序運行結果
看到上面結果說明咱們客戶端程序進行RMI遠程調用已經成功了。

1.3 RMI接口和類簡介

負責指定rmi系統遠程對象行爲的接口和類在java.rmi包中定義的,接下來咱們瞭解一下幾個核心接口和類:
一、  Java.rmi.Remote 接口
在rmi中,遠程接口聲明瞭可從遠程java虛擬機中調用的方法集,遠程接口必須知足下列條件:
Ø 遠程接口必須至少直接或間接的擴展java.rmi.Remote接口。
Ø 遠程接口中的方法申明必須知足:遠程方法申明在其throws子句中除了要包含與應用程序有關的異常以外,還必須包括RemoteException異常(或她的父類);在遠程方法申明中,做爲參數或返回值申明的遠程對象必須申明爲遠程接口,而非該接口的實現類。
二、  Java.rmi.RemoteException
RemoteException類是在遠程方法調用期間由RMI運行所拋出的異常,在使用了rmi系統的應用程序中,遠程接口中申明的遠程方法在其throws子句中必須指定RemoteException或者其超類。
Ø 當遠程方法調用因爲某種緣由失敗時,將拋出RemoteException異常。
Ø RemoteException類是一個已檢驗的異常,而不是RuntimeException。
三、  Java.rmi.server.RemoteObject
Ø RMI服務器函數由RemoteObject類及其子類RemoteServer,UnicastRemoteObject和Activatabble提供。
Ø RemoteObject爲遠程對象敏感的Object方法,hashCode,equals和toString方法提供實現。
Ø 建立遠程對象並將其導出,所需的方法由類UnicastRemoteObject和Activatable提供,子類能夠識別遠程方法。
Ø UnicastRemoteObject定義了單個調用的遠程對象,其引用只有在服務器進程運行時纔有效。
Ø 類Activatable是抽象類,它定義的activatable遠程對象在其遠程方法被調用時開始執行,並在必要時本身關閉。
四、  Java.rmi.registry.LocateRegistry
LocateRegistry類用於得到對特定主機的引導遠程對象註冊服務程序的引用(建立stub),或者建立能在特定端口接受調用的遠程對象註冊服務程序,註冊服務程序實現將遠程對象名與遠程對象引用關聯的簡單命名語法,服務器從新啓動不會記住這些名字和遠程對象之間的綁定。
LocateRegistry類中的方法:
public static Registry getRegistry() throws RemoteException
Public static Registry getRegistry(int port ) throws RemoteException
Public static Registry getRegistry(String host ) throws RemoteException
Public static Registry getRegistry(String host , int port) throws RemoteException
Public static Registry getRegistry(String host, int port ,RMIClientSocketFactory csf ) throws RemoteException
Public static Registry createRegistry(int port )throws RemoteException
Public static Registry createRegistry(int port, RMIClientSocketFactory csf, RMIServerSocketFactory ssf) throws RemoteException
五、  Java.rmi.Naming
Naming類提供了存儲和得到遠程對象註冊服務程序中的遠程對象進行引用的方法
Ø Naming類中的方法以url的形式做爲其中的一個參數,//host:port/name
Ø 當遠程對象使用rmi註冊服務程序在本地主機上進行過註冊後,遠程主機上的調用程序就能夠按名稱查詢遠程對象,得到其引用,而後在對象上調用遠程方法。
Public static Remote lookup(String name) throws NotBoundException, MalformedURException, RemoteException
Public static void bind(String name, Remote obj) throws AlreadyBoundException, MalforedURException, RemoteException
Public static void unbind (String name) throws RemoteException, NotBoundException,MalformedURLException
Public static void rebind(String name, Remote obj) throws RemoteException, MalformedURLException
六、  Java.rmi.server.UnicastRemoteObject
類UnicastRemoteObject建立並導出遠程對象,該類實現的遠程服務具備如下特色:
Ø 將這種對象的引用至多僅在建立該遠程對象的進程生命週期內有效。
Ø 經過TCP傳輸與遠程對象通訊調用,參數和結果使用流協議在客戶端和服務器之間進行通訊。
七、  Stub  和skeleton
在遠程對象的通訊過程當中,rmi使用標準機制:stub和skeleton
 
圖14.5 Stub 和Skeleton
Ø Stub的功能
初始化與包含遠程對象的遠程機器的鏈接。
對遠程機器參數進行編組(寫入並傳輸)。
等待方法調用結果。
讀取返回值或返回的異常。
將值返回給調用程序。
Ø Skeleton的功能
在遠程機器中,每一個遠程對象均可以有相應的skeleton,skeleton負責將調用分配給實際的遠程對象實現,他的主要功能以下:
讀取遠程方法的參數。
調用實際遠程對象實現上的方法。
將結果(返回值或異常)編組(寫入並傳輸)給調用程序。

1.4 JNDI基本概念

JNDI誕生的理由彷佛很簡單。隨着分佈式應用的發展,遠程訪問對象訪問成爲經常使用的方法。雖說經過Socket等編程手段仍然可實現遠程通訊,但按照模式的理論來講,還是有其侷限性的。RMI技術,RMI-IIOP技術的產生,使遠程對象的查找成爲了技術焦點。JNDI技術就應運而生。JNDI技術產生後,就可方便的查找遠程或是本地對象。
一、  JNDI  是什麼?
JNDI(The Java Naming and Directory Interface,Java 命名和目錄接口) 是一組在Java 應用中訪問命名和目錄服務的API。爲開發人員提供了查找和訪問各類命名和目錄服務的通用、統一的方式。藉助於JNDI 提供的接口,可以經過名字定位用戶、機器、網絡、對象服務等。
Ø 命名服務:就像DNS 同樣,經過命名服務器提供服務,大部分的J2EE 服務器都含有命名服務器。
Ø 目錄服務:一種簡化的RDBMS 系統,經過目錄具備的屬性保存一些簡單的信息。目錄服務經過目錄服務器實現,好比微軟ACTIVE DIRECTORY 等。
二、  JNDI  的好處:
Ø 包含大量命名和目錄服務,可使用相同API 調用訪問任何命名或目錄服務。
Ø 能夠同時鏈接多個命名和目錄服務。
Ø 容許把名稱同JAVA 對象或資源關聯起來,沒必要知道對象或資源的物理ID。
Ø 使用通用接口訪問不一樣種類的目錄服務
Ø 使得開發人員可以集中使用和實現一種類型的命名或目錄服務客戶API 上。

1.5 JNDI應用程序結構

JNDI的結構由一個API和一個SPI組成,Java應用程序實用JNDI API訪問各類各樣的命名和目錄服務。
 
圖14.6 JNDI 應用程序結構
一、  JNDI  上下文
前面提到命名服務是將名稱與對象相關聯。這種關聯被稱爲綁定。一組這樣的綁定被稱爲上下文,Jndi上下文能夠用來查找,捆綁/解除捆綁,建立或者破壞綁定名稱操做在JNDI中,上下文是使用javax.naming.Context 接口來表示的,而這個接口也正是與命名服務進行交互的主要接口。
Context 接口中的每一個命名方法都有兩種重載的形式:
lookup(String name): 接受一個字符串名稱參數,查找綁定遠程對象。
lookup(javax.naming.Name): 接受一個結構化的名稱,查找綁定遠程對象。
二、  初始化上下文
InitialContext 是一個實現了 Context接口的類。使用這個類做爲您到命名服務的入口點 。建立一個InitialContext 對象構造器須要採用一組屬性,形式爲java.util.Hashtable 或其子類之一,好比:
代碼範例5
Properties props = new Properties();
props.setProperty("java.naming.factory.initial", "org.jnp.interfaces.NamingContextFactory");
props.setProperty("java.naming.provider.url", "localhost:1099");
InitialContext = new InitialContext(props);
要經過JNDI 進行資源訪問,咱們必須設置初始化上下文的參數,主要是設置JNDI 驅動的類名(java.naming.factory.initial)和提供命名服務的URL(java.naming.provider.url)。由於Jndi 的實現產品有不少。因此java.naming.factory.initial 的值因提供JNDI 服務器的不一樣而不一樣,java.naming.provider.url 的值包括提供命名服務的主機地址和端口號。
下表列出了用於所支持的服務提供程序的工廠類。
表 : Context.INITIAL_CONTEXT_FACTORY的值
名稱
服務提供程序工廠
文件系統
com.sun.jndi.fscontext.RefFSContextFactory
LDAP
com.sun.jndi.ldap.LdapCtxFactory
RMI
com.sun.jndi.rmi.registry. RegistryContextFactory
CORBA
com.sun.jndi.cosnaming.CNCtxFactory
DNS
com.sun.jndi.dns.DnsContextFactory
 

1.6 RMI與JNDI集成

經過上面對JNDI的瞭解咱們能夠利用JNDI來管理RMI遠程對象的註冊服務,咱們將代碼範例3進行以下改寫:
代碼範例6
package com.itjob.rmi;
 
import java.rmi.*;
import java.rmi.registry.*;
import javax.naming.*;
 
public class RmiSampleServerJndi{
public static void main(String[] args) throws Exception{
 
LocateRegistry.createRegistry(8808);
RmiSampleImpl server=new RmiSampleImpl();System.setProperty(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.rmi.registry.RegistryContextFactory");
System.setProperty(Context.PROVIDER_URL,"rmi://localhost:8808");
InitialContext ctx=new InitialContext();
ctx.bind("java:comp/env/SampleDemo",server);
ctx.close();
 
}
}
啓動服務端程序如圖所示:
 
圖14.7 JNDI 服務程序啓動
表示服務端程序已經將遠程對象在JNDI是進行了註冊,等待客戶端進行調用。
接下來咱們改寫客戶端程序採用JNDI方式來調用遠程對象
代碼範例7
package com.itjob.rmi;
import java.rmi.*;
import java.rmi.server.*;
import javax.naming.*;
public class RmiSampleClientJndi {
public static void main(String[] args) throws Exception
{
System.setProperty(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.rmi.registry.RegistryContextFactory");
System.setProperty(Context.PROVIDER_URL,"rmi://localhost:8808");
InitialContext ctx=new InitialContext();
 
String url = "java:comp/env/SampleDemo";
RmiSample RmiObject = (RmiSample)ctx.lookup(url);
System.out.println(" 1 + 2 = " + RmiObject.sum(1,2) );
 
}
}
運行客戶端程序如圖所示:
 
圖14.8  客戶端JNDI 程序運行結果
表示客戶端已經經過JNDI調用服務端遠程對象成功。

1.7 學習總結

Ø RMI運行在一個java虛擬機上的對象調用運行在另外一個java虛擬機上的對象的方法.。RMI的用途就是爲java程序之間的遠程通訊提供服務。
Ø RMI的編程思想
n 對客戶端:須要一些特定的代碼來引用遠程對象,一旦客戶端的代碼擁有對遠程對象的引用,對遠程對象上的調用與對本地對象方法的調用除了速度之外沒什麼區別。
n 對服務端:必須定義類並實例化類的遠程對象,服務器的代碼必須可以登記對象並向客戶端導出它們的方法,這樣,這些方法就可以被遠程調用了。
n 客戶端和服務端的代碼都必須定義或可以訪問一個接口,該接口中申明瞭能夠遠程調用的方法,而且二者還能夠設置一個安全管理器。
n 當調用遠程對象上的方法時,客戶端能夠將對象做爲參數來傳遞,而且,遠程對象上的方法能夠返回對象,這些是經過序列化來實現的。
Ø JNDI爲開發人員提供了查找和訪問各類命名和目錄服務的通用、統一的方式。
Ø JNDI的結構由一個API和一個SPI組成,Java應用程序實用JNDI API訪問各類各樣的命名和目錄服務。
相關文章
相關標籤/搜索