RMI(Remote Method)
Invocation):遠程方法調用,即在RPC的基礎上有向前邁進了一步,提供分布式對象間的通訊。允許運行在一個java虛擬機的對象調用運行在另一個java虛擬機上對象的方法。這兩個虛擬機可以是運行在相同計算機上的不同進程中,也可以是運行在網絡上的不同計算機中。
Java RMI 威力強大
Java RMI在JDK1.1中實現的,其威力就體現在它強大的開發分布式網絡應用的能力上,是純Java的網絡分布式應用系統的核心解決方案之一。其實它可以被看作是RPC的Java版本。但是傳統RPC并不能很好地應用于分布式對象系統。而Java RMI則支持存儲于不同地址空間的程序級對象之間彼此進行通信,實現遠程對象之間的無縫遠程調用。
Java遠程消息交換協議JRMP
RMI目前使用Java遠程消息交換協議JRMP(Java Remote Messaging Protocol)進行通信。由于JRMP是專為Java對象制定的,Java RMI具有Java的"Write Once,Run Anywhere"的優點,是分布式應用系統的百分之百純Java解決方案。用Java RMI開發的應用系統可以部署在任何支持JRE(Java Run Environment Java,運行環境)的平臺上。但由于JRMP是專為Java對象制定的,因此,RMI對于用非Java語言開發的應用系統的支持不足。不能與用非Java語言書寫的對象進行通信。
使用RMI優點
RMI大大增強了java開發分布式應用的能力,例如可以將計算方法復雜的程序放在其他的服務器上,主服務器只需要去調用,而真正的運算是在其他服務器上進行,最后將運算結果返回給主服務器,這樣就減輕了主服務器的負擔,提高了效率(但是也有其他的開銷)。
RMI網絡模型
在客戶端為遠程對象安裝一個代理。代理是位于客戶端虛擬機中的一個對象,它對于客戶端程序來說,就像是要訪問的遠程對象一樣。客戶端調用此代理時,只需進行常規的方法調用。而客戶端代理則負責使用網絡協議與服務器進行聯系。
網絡模型
RMI的工作原理
一、術語介紹
1、存根:當客戶端要調用遠程對象的一個方法時,實際上調用的是代理對象上的一個普通方法,我們稱此代理對象為存根(stub)。存根位于客戶端機器上,而非服務器上。
2、參數編組:存根會將遠程方法所需的參數打包成一組字節,對參數編碼的過程就稱為參數編組。參數編組的目的是將參數轉換成適合在虛擬機之間進行傳遞的格式,在RMI協議中,對象是使用序列化機制進行編碼的。
RMI遠程調用步驟:
1,客戶對象調用客戶端輔助對象上的方法
2,客戶端輔助對象打包調用信息(變量,方法名),通過網絡發送給服務端輔助對象
3,服務端輔助對象將客戶端輔助對象發送來的信息解包,找出真正被調用的方法以及該方法所在對象
4,調用真正服務對象上的真正方法,并將結果返回給服務端輔助對象
5,服務端輔助對象將結果打包,發送給客戶端輔助對象
6,客戶端輔助對象將返回值解包,返回給客戶對象
7,客戶對象獲得返回值
對于客戶對象來說,步驟2-6是完全透明的
編碼實現java RMI hello world的例子
定義一個接口
/**
* Created by junyi.pc on 2017/1/23.
*/
importjava.rmi.RemoteException;
importjava.rmi.*;
public interface IHello extends Remote {
/**
* 簡單的返回“Hello World!"字樣
*
*@return返回“Hello World!"字樣
*@throwsjava.rmi.RemoteException
*/
public default String helloWorld()throwsRemote Exception {
return null;
}
/**
* 一個簡單的業務方法,根據傳入的人名返回相應的問候語
*@paramsomeBodyName人名
*@return返回相應的問候語
*@throwsjava.rmi.RemoteException
*/
publicString sayHelloToSomeBody(String someBodyName)throwsRemoteException;
}
接口實現
/**
* Created by junyi.pc on 2017/1/23.
*/
importjava.rmi.RemoteException;
importjava.rmi.*;
importjava.rmi.server.UnicastRemoteObject;
public class HelloImpl extends UnicastRemoteObject implements IHello {
/**
* 因為UnicastRemoteObject的構造方法拋出了RemoteException異常,因此這里默認的構造方法必須寫,必須聲明拋出RemoteException異常
*
*@throwsRemoteException
*/
public HelloImpl()throws RemoteException {
}
/**
* 簡單的返回“Hello World!"字樣
*
*@return返回“Hello World!"字樣
*@throwsjava.rmi.RemoteException
*/
publicString helloWorld()throwsRemoteException {
return"Hello World!";
}
/**
* 一個簡單的業務方法,根據傳入的人名返回相應的問候語
*
*@paramsomeBodyName人名
*@return返回相應的問候語
*@throwsjava.rmi.RemoteException
*/
publicString sayHelloToSomeBody(String someBodyName)throwsRemoteException {
return"你好,"+ someBodyName +"!";
}
}
server實現
/**
* Created by junyi.pc on 2017/1/23.
*/
importjava.net.MalformedURLException;
importjava.rmi.AlreadyBoundException;
importjava.rmi.Naming;
importjava.rmi.RemoteException;
importjava.rmi.registry.LocateRegistry;
public classHelloServer {
public static voidmain(String args[]) {
try{
//創建一個遠程對象
IHello rhello =new HelloImpl();
//本地主機上的遠程對象注冊表Registry的實例,并指定端口為8888,這一步必不可少(Java默認端口是1099),必不可缺的一步,缺少注冊表創建,則無法綁定對象到遠程注冊表上
LocateRegistry.createRegistry(8888);
//把遠程對象注冊到RMI注冊服務器上,并命名為RHello
//綁定的URL標準格式為:rmi://host:port/name(其中協議名可以省略,下面兩種寫法都是正確的)
Naming.bind("rmi://localhost:8888/RHello",rhello);
// Naming.bind("http://localhost:8888/RHello",rhello);
System.out.println(">>>>>INFO:遠程IHello對象綁定成功!");
}catch(RemoteException e) {
System.out.println("創建遠程對象發生異常!");
e.printStackTrace();
}catch(AlreadyBoundException e) {
System.out.println("發生重復綁定對象異常!");
e.printStackTrace();
}catch(MalformedURLException e) {
System.out.println("發生URL畸形異常!");
e.printStackTrace();
}
}
}
客戶端測試程序
/**
* Created by junyi.pc on 2017/1/23.
*/
importjava.net.MalformedURLException;
importjava.rmi.Naming;
importjava.rmi.NotBoundException;
importjava.rmi.RemoteException;
/**
* Created by junyi.pc on 2017/1/23.
*/
public class client {
public static voidmain(String args[]){
try{
//在RMI服務注冊表中查找名稱為RHello的對象,并調用其上的方法
IHello rhello =(IHello) Naming.lookup("rmi://localhost:8888/RHello");
System.out.println(rhello.helloWorld());
System.out.println(rhello.sayHelloToSomeBody("張俊怡"));
}catch(NotBoundException e) {
}catch(MalformedURLException e) {
e.printStackTrace();
e.printStackTrace();
}catch(RemoteException e) {
e.printStackTrace();
}
}
}