JAVA基礎--分佈式對象

一、概念

  1. 當客戶代碼要在遠程對象上調用一個遠程方法時,實際上調用的是代理對象上的一個普通方法,我們稱此代理對象爲存根。存根在客戶端上,而不是在服務器上。存根構造了一個信息塊:遠程對象的標識符、方法的描述、編組後的參數。

  2. 參數編組:把遠程方法所需的參數打包成一組字節。目的是將參數轉換成合適在虛擬機之間進行傳遞的格式。在RMI中,對象是使用序列化機制進行編碼的。

  3. 客戶端存根對來自服務端的返回值或者異常進行反編組,就成了調用存根的返回值。


二、RMI

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;  
}

注意:1)遠程對象必須實現Remote接口。

    2)接口中的所有方法必須拋出RemoteException。


2.創建遠程方法實現類

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;  
    }  
}

注意:1)UnicastRemoteObject類的構造函數拋出了RemoteException,這個構造器使得它的對象可供遠程訪問,因爲其超類構造器也會拋出這個異常,故其繼承類不能使用默認構造函數,繼承類的構造函數必須也拋出RemoteException。

    2)由於方法參數與返回值最終都將在網絡上傳輸,故必須是可序列化的


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

方式一: view plain copy

  1. [name@name Test]$ cd /home/name/Test/  

  2. [name@name Test]$ rmic server.HelloImpl  

方式二:view plain copy

  1. [name@name Test]$ rmic -classpath /home/name/Test server.HelloImpl  

 運行成功後將會生成HelloImpl_Stub.class文件


4.啓動RMI註冊服務(jdk1.5.0_15/bin/rmiregistry)

方式一:後臺啓動rmiregistry服務view plain copy

  1. [name@name jdk]$ jdk1.5.0_15/bin/rmiregistry 12312 &  

  2. [1] 22720  

  3. [name@name jdk]$ ps -ef|grep rmiregistry  

  4. name    22720 13763  0 16:43 pts/3    00:00:00 jdk1.5.0_15/bin/rmiregistry 12312  

  5. name    22737 13763  0 16:43 pts/3    00:00:00 grep rmiregistry  

如果不帶具體端口號,則默認爲1099


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 view plain copy

  1. [name@name ~]$ java server.HelloServer  

  2. 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//服務端生成的存根文件 copy

  1. [name@name client]$ java client.HelloClient  

  2. Hello,zx  

同服務器端,/home/name/Test一定要在系統CLASSPATH中




spring中的RMI

Spring RMI中,主要有兩個類:org.springframework.remoting.rmi.RmiServiceExporterorg.springframework.remoting.rmi.RmiProxyFactoryBean

服務端使用RmiServiceExporter暴露RMI遠程方法,客戶端用RmiProxyFactoryBean間接調用遠程方法。

 

       首先,也是兩個工程,服務端用Web工程,因爲使用Spring,我們依託Web容器來完成。

       1、在該服務端Web工程中添加接口,普通接口,無需繼承其他view plain copy

  1. package rmi.test;  

  2.   

  3. public interface HelloRMIService {  

  4.       

  5.     public int  getAdd(int a, int b);  

  6.        

  7.   

  8. }  


 

        2、接口的實現類view plain copy

  1. package rmi.test;  

  2.   

  3. public class HelloRMIServiceImpl implements HelloRMIService {  

  4.   

  5.     @Override  

  6.     public int getAdd(int a, int b) {  

  7.           

  8.         return a+b;  

  9.     }  

  10.   

  11. }  


 

       3、在該服務端Web工程中添加Spring的bean配置文件,比如命名爲rmiServer.xml,內容如下: view plain copy

  1. <?xml version="1.0" encoding="UTF-8" standalone="no"?>  

  2. <beans xmlns="http://www.springframework.org/schema/beans"  

  3.        xmlns:aop="http://www.springframework.org/schema/aop"  

  4.        xmlns:context="http://www.springframework.org/schema/context"  

  5.        xmlns:jee="http://www.springframework.org/schema/jee"   

  6.        xmlns:tx="http://www.springframework.org/schema/tx"  

  7.        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  

  8.        xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd     

  9.        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd     

  10.        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd     

  11.        http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd     

  12.        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">  

  13.   

  14.     <bean id="helloRMIServiceImpl" class="rmi.test.HelloRMIServiceImpl"> </bean>  

  15.     <!-- 將一個類發佈爲一個RMI服務 -->  

  16.     <bean id="myRMIServer"  class="org.springframework.remoting.rmi.RmiServiceExporter">  

  17.       <property name="serviceName" value="helloRMI"></property>  

  18.       <property name="service" ref="helloRMIServiceImpl"></property>  

  19.       <property name="serviceInterface" value="rmi.test.HelloRMIService"></property>  

  20.       <property name="registryPort" value="9999"></property>  

  21.     </bean>  

  22.       

  23.     

  24.       

  25. </beans>  


       說明:這裏不詳細的說明了,主要配置了真實實現類,用RmiServiceExporter暴露時,配置property要注意的有service,serviceName,serviceInterface,端口registryPort。

       啓動Web工程的服務器,該配置文件應該被Spring的監聽器監聽,並加載,啓動成功後,服務端就算建好了。如果服務器是在localhost啓動的,那麼暴露的RMIIP也是localhost,如果需要使用其他IP,需要讓服務器在其他的IP啓動。

    這裏沒有web服務器,使用main模擬啓動,如下:

[html] view plain copy

  1. package rmi.test;  

  2.   

  3. import org.springframework.context.support.ClassPathXmlApplicationContext;  

  4.   

  5. public class RMIServiceTest {  

  6.       

  7.     public static void main(String[] args) {  

  8.           

  9.         new ClassPathXmlApplicationContext("rmiServer.xml");  

  10.           

  11.     }  

  12.   

  13. }  


     客戶端調用:爲了方便也只新建一個簡單的Java Project,使用靜態的java代碼來調用了。

     1、 在源文件src下建立一個rmiClient.xml view plain copy

  1. <?xml version="1.0" encoding="UTF-8" standalone="no"?>  

  2. <beans xmlns="http://www.springframework.org/schema/beans"  

  3.        xmlns:aop="http://www.springframework.org/schema/aop"  

  4.        xmlns:context="http://www.springframework.org/schema/context"  

  5.        xmlns:jee="http://www.springframework.org/schema/jee"   

  6.        xmlns:tx="http://www.springframework.org/schema/tx"  

  7.        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  

  8.        xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd     

  9.        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd     

  10.        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd     

  11.        http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd     

  12.        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">  

  13.   

  14.     <!-- 客戶端 -->  

  15.     <bean id="myRMIClient"  class="org.springframework.remoting.rmi.RmiProxyFactoryBean">  

  16.       <property name="serviceInterface" value="rmi.test.HelloRMIService"></property>  

  17.       <property name="serviceUrl" value="rmi://127.0.0.1:9999/helloRMI"></property>  

  18.     </bean>  

  19.       

  20.          

  21. </beans>  

 

       這裏注意到RmiProxyFactoryBean的兩個重要的property:serviceUrlserviceInterface,HelloRMIService接口可以從服務端的接口打成jar包來提供。

        2、 新建java類 view plain copy

  1. package rmi.test;  

  2.   

  3. import org.springframework.context.ApplicationContext;  

  4. import org.springframework.context.support.ClassPathXmlApplicationContext;  

  5.   

  6. public class RMIClient {  

  7.       

  8.     public static void main(String[] args) {  

  9.         ApplicationContext applicationContext = new ClassPathXmlApplica                    tionContext("rmi/test/rmiClient.xml");  

  10.         HelloRMIService helloRMIService =  applicationContext.getBean("                myRMIClient",HelloRMIService.class);  

  11.         System.out.println(helloRMIService.getAdd(3, 4));  

  12.     }  

  13.   

  14. }  


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章