RMI:遠程方法調用(Remote Method Invocation)。能夠讓在某個java虛擬機上的對象像調用本地對象一樣調用另一個java 虛擬機中的對象上的方法。
RMI遠程調用步驟:
1,客戶對象調用客戶端輔助對象上的方法
2,客戶端輔助對象打包調用信息(變量,方法名),通過網絡發送給服務端輔助對象
3,服務端輔助對象將客戶端輔助對象發送來的信息解包,找出真正被調用的方法以及該方法所在對象
4,調用真正服務對象上的真正方法,並將結果返回給服務端輔助對象
5,服務端輔助對象將結果打包,發送給客戶端輔助對象
6,客戶端輔助對象將返回值解包,返回給客戶對象
7,客戶對象獲得返回值
對於客戶對象來說,步驟2-6是完全透明的
搭建一個RMI服務的過程分爲以下7步;
1,創建遠程方法接口,該接口必須繼承自Remote接口
Remote 接口是一個標識接口,用於標識所包含的方法可以從非本地虛擬機上調用的接口,Remote接口本身不包含任何方法
- package server;
- import java.rmi.Remote;
- import java.rmi.RemoteException;
- public interface Hello extends Remote {
- public String sayHello(String name) throws RemoteException;
- }
由於遠程方法調用的本質依然是網絡通信,只不過隱藏了底層實現,網絡通信是經常會出現異常的,所以接口的所有方法都必須拋出RemoteException以說明該方法是有風險的
2,創建遠程方法接口實現類:
UnicastRemoteObject類的構造函數拋出了RemoteException,故其繼承類不能使用默認構造函數,繼承類的構造函數必須也拋出RemoteException
由於方法參數與返回值最終都將在網絡上傳輸,故必須是可序列化的
- package server;
- import java.rmi.RemoteException;
- import java.rmi.server.UnicastRemoteObject;
- public class HelloImpl extends UnicastRemoteObject implements Hello {
- private static final long serialVersionUID = -271947229644133464L;
- public HelloImpl() throws RemoteException{
- super();
- }
- public String sayHello(String name) throws RemoteException {
- return "Hello,"+name;
- }
- }
3,利用java自帶rmic工具生成sutb存根類(jdk1.5.0_15/bin/rmic)
jdk1.2以後的RMI可以通過反射API可以直接將請求發送給真實類,所以不需要skeleton類了
sutb存根爲遠程方法類在本地的代理,是在服務端代碼的基礎上生成的,需要HelloImpl.class文件,由於HelloImpl繼承了Hello接口,故Hello.class文件也是不可少的
Test
- - server
- - - - Hello.class
- - - - HelloImpl.class
方式一:
- [name@name Test]$ cd /home/name/Test/
- [name@name Test]$ rmic server.HelloImpl
方式二:
- [name@name Test]$ rmic -classpath /home/name/Test server.HelloImpl
運行成功後將會生成HelloImpl_Stub.class文件
4,啓動RMI註冊服務(jdk1.5.0_15/bin/rmiregistry)
方式一:後臺啓動rmiregistry服務
- [name@name jdk]$ jdk1.5.0_15/bin/rmiregistry 12312 &
- [1] 22720
- [name@name jdk]$ ps -ef|grep rmiregistry
- name 22720 13763 0 16:43 pts/3 00:00:00 jdk1.5.0_15/bin/rmiregistry 12312
- name 22737 13763 0 16:43 pts/3 00:00:00 grep rmiregistry
如果不帶具體端口號,則默認爲1099
方式二:人工創建rmiregistry服務,需要在代碼中添加:
- LocateRegistry.createRegistry(12312);
5,編寫服務端代碼
- package server;
- import java.rmi.Naming;
- import java.rmi.registry.LocateRegistry;
- public class HelloServer {
- public static void main(String[] args) {
- try{
- Hello h = new HelloImpl();
- //創建並導出接受指定port請求的本地主機上的Registry實例。
- //LocateRegistry.createRegistry(12312);
- /** Naming 類提供在對象註冊表中存儲和獲得遠程對遠程對象引用的方法
- * Naming 類的每個方法都可將某個名稱作爲其一個參數,
- * 該名稱是使用以下形式的 URL 格式(沒有 scheme 組件)的 java.lang.String:
- * //host:port/name
- * host:註冊表所在的主機(遠程或本地),省略則默認爲本地主機
- * port:是註冊表接受調用的端口號,省略則默認爲1099,RMI註冊表registry使用的著名端口
- * name:是未經註冊表解釋的簡單字符串
- */
- //Naming.bind("//host:port/name", h);
- Naming.bind("rmi://192.168.58.164:12312/Hello", h);
- System.out.println("HelloServer啓動成功");
- }catch(Exception e){
- e.printStackTrace();
- }
- }
- }
先創建註冊表,然後才能在註冊表中存儲遠程對象信息
6,運行服務端(58.164):
Test
- - server
- - - - Hello.class
- - - - HelloImpl.class
- - - - HelloServer.class
- [name@name ~]$ java server.HelloServer
- HelloServer啓動成功
當然/home/name/Test一定要在系統CLASSPATH中,否則會報找不到相應的.class文件
7,編寫客戶端代碼
- package client;
- import java.net.MalformedURLException;
- import java.rmi.Naming;
- import java.rmi.NotBoundException;
- import java.rmi.RemoteException;
- import server.Hello;
- public class HelloClient {
- public static void main(String[] args) {
- try {
- Hello h = (Hello)Naming.lookup("rmi://192.168.58.164:12312/Hello");
- System.out.println(h.sayHello("zx"));
- } catch (MalformedURLException e) {
- System.out.println("url格式異常");
- } catch (RemoteException e) {
- System.out.println("創建對象異常");
- e.printStackTrace();
- } catch (NotBoundException e) {
- System.out.println("對象未綁定");
- }
- }
- }
8,運行客戶端(58.163):
Test
- - client
- - - - HelloClient.class
- - server
- - - - Hello.class
- - - - HelloImpl_Stub.class//服務端生成的存根文件
- [name@name client]$ java client.HelloClient
- Hello,zx
同服務器端,/home/name/Test一定要在系統CLASSPATH中
PS:
1,客戶端所在服務和服務端所在的服務器網絡一定要通(一開始浪費了很多時間,最後才發現是網絡不通)
2,所有代碼在jdk1.5.0_15,Linux服務器上調試通過
3,如果java命令運行提示找不到類文件,則爲CLASSPATH配置問題
- [name@name ~]$ vi .bash_profile
- JAVA_HOME=/home/name/jdk/jdk1.5.0_15
- export JAVA_HOME
- PATH=$JAVA_HOME/bin:$PATH
- export PATH
- CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:/home/name/Test
- export CLASSPATH
JAVA_HOME爲jdk的根目錄
PATH爲java工具類路徑(java,javac,rmic等)
CLASSPATH爲java .class文件的存放路徑,使用java命令運行.class文件時即會在該參數配置的路徑下尋找相應文件
java RMI的缺點:
1,從代碼中也可以看到,代碼依賴於ip與端口
2,RMI依賴於Java遠程消息交換協議JRMP(Java Remote Messaging Protocol),該協議爲java定製,要求服務端與客戶端都爲java編寫