Java之——RMI遠端程序呼叫(插曲)

來源:互聯網
上載者:User

標籤:rmi   java   通訊   網路   分布式   

轉載請註明出處:http://blog.csdn.net/l1028386804/article/details/45970641
一、定義

         Java RMI:Java遠程方法調用,即Java RMI(Java Remote Method Invocation)是Java程式設計語言裡,一種用於實現遠端程序呼叫的API。它使客戶機上啟動並執行程式可以調用遠程伺服器上的對象。遠程方法調用特性使Java編程人員能夠在網路環境中分布操作。RMI全部的宗旨就是儘可能簡化遠程介面對象的使用。

      我們知道遠端程序呼叫(Remote Procedure Call, RPC)可以用於一個進程調用另一個進程(很可能在另一個遠程主機上)中的過程,從而提供了過程的分布能力。Java 的 RMI 則在 RPC 的基礎上向前又邁進了一步,即提供分布式對象間的通訊。

      RMI(Remote Method Invocation)為遠程方法調用,是允許運行在一個Java虛擬機器的對象調用運行在另一個Java虛擬機器上的對象的方法。

      這兩個虛擬機器可以是運行在相同電腦上的不同進程中,也可以是運行在網路上的不同電腦中。

二、工作原理

        RMI能讓一個Java程式去調用網路中另一台電腦的Java對象的方法,那麼調用的效果就像是在本機上調用一樣。通俗的講:A機器上面有一個class,通過遠程調用,B機器調用這個class 中的方法。

      RMI,遠程方法調用(Remote Method Invocation)是Enterprise JavaBeans的支柱,是建立分布式Java應用程式的方便途徑。RMI是非常容易使用的,但是它非常的強大。

      RMI的基礎是介面,RMI構架基於一個重要的原理:定義介面和定義介面的具體實現是分開的。下面我們通過具體的例子,建立一個簡單的遠程計算服務和使用它的客戶程式

三、RMI包含部分

        遠程服務的介面定義遠程服務介面的具體實現樁(Stub)和架構(Skeleton)檔案一個運行遠程服務的伺服器一個RMI命名服務,它允許用戶端去發現這個遠程服務類檔案的提供者(一個HTTP或者FTP伺服器)一個需要這個遠程服務的用戶端程式

四、用途

     RMI的用途是為分布式Java應用程式之間的遠程通訊提供服務,提供分布式服務。

     目前主要應用時封裝在各個J2EE項目架構中,例如Spring,EJB(Spring和EJB均封裝了RMI技術)

     在Spring中實現RMI:

     ①在伺服器端定義服務的介面,定義特定的類實現這些介面;

     ②在伺服器端使用org.springframework.remoting.rmi.RmiServiceExporter類來註冊服務;

     ③在用戶端使用org.springframework.remoting.rmi.RmiProxyFactoryBean來實現遠程服務的代理功能;

     ④在用戶端定義訪問與伺服器端服務介面相同的類

五、RMI的局限

        RMI目前使用Java遠程訊息交換協議JRMP(Java Remote Messaging Protocol)進行通訊。JRMP是專為Java的遠程對象制定的協議,由於JRMP是專為Java對象制定的,因此,RMI對於用非Java語言開發的應用系統的支援不足。不能與用非Java語言書寫的對象進行通訊(意思是只支援用戶端和伺服器端都是Java程式的代碼的遠程調用)。

六、RMI的使用局限

       由於客戶機和伺服器都是使用Java編寫的,二者平台相容性的要求僅僅是雙方都運行在版本相容的Java虛擬機器上。

七、調用遠程方法的參數和返回值

        當調用遠程對象上的方法時,客戶機除了可以將原始類型的資料作為參數一外,還可以將對象作為參數來傳遞,與之相對應的是返回值,可以返回原始類型或對象,這些都是通過Java的對象序列化(serialization)技術來實現的。(換而言之:參數或者返回值如果是對象的話必須實現Serializable介面)

八、RMI應用程式的基本類型

九、RMI體繫結構

     樁/架構(Stub/Skeleton)層:用戶端的樁和伺服器端的架構;

      遠端參照(remote reference)層:處理遠端參照行為

      傳送層(transport):串連的建立和管理,以及遠程對象的跟蹤

十、RMI類和介面

(一) Remote介面:

      是一個不定義方法的標記介面

      Public interface Remote{}

      在RMI中,遠程介面聲明了可以從遠程Java虛擬機器中調用的方法集。遠程介面滿足下列要求:

      1、遠程介面必須直接或間接擴充Java.rmi.Remote介面,且必須聲明為public,除非用戶端於遠程介面在同一包中

      2、在遠程介面中的方法在聲明時,除了要拋出與應用程式有關的一場之外,還必須包括RemoteException(或它的超類,IOExcepion或Exception)異常

      3、在遠程方法聲明中,作為參數或返回值聲明的遠程對象必須聲明為遠程介面,而非該介面的實作類別。

(二) RemoteObject抽象類別

        實現了Remote介面和序列化Serializable介面,它和它的子類提供RMI伺服器函數。

(三) LocateRegistry final()

        類用於獲得特定主機的引導遠程對象註冊伺服器程式的引用(即建立stub),或者建立能在特定連接埠接收調用的遠程對象註冊服務程式。

伺服器端

       向其他客戶機提供遠程物件服務

      SomeService servcie=……;//遠程物件服務

        Registry registry=LocateRegisty.getRegistry();//Registry是個介面,他繼承了Remote,此方法返回本地主機在預設註冊表連接埠 1099 上對遠程對象 Registry 的引用。getRegistry(int port) 返回本地主機在指定 port 上對遠程對象 Registry 的引用;getRegistry(String host)  返回指定 host 在預設註冊表連接埠 1099 上對遠程對象 Registry 的引用;getRegistry(String host, int port) 返回指定的 hostport 上對遠程對象 Registry 的引用registry.bind(“I serve”,service);// bind(String name,Remote obj) 綁定對此註冊表中指定 name 的遠端參照。name : 與該遠端參照相關的名稱 obj : 對遠程對象(通常是一個 stub)的引用unbind(String name)移除註冊表中指定name的綁定。rebind(String name,Remote obj)重新綁定,如果name已存在,但是Remote不一樣則替換,如果Remote一樣則丟棄現有的綁定lookup(String name) 返回註冊表中綁定到指定 name 的遠端參照,返回RemoteString[] list()   返回在此註冊表中綁定的名稱的數組。該數組將包含一個此註冊表中調用此方法時綁定的名稱快照。
客戶機端

       向伺服器提供相應的服務要求。

Registry registry=LocateRegisty.getRegistry();SomeService servcie=(SomeService)registry.lookup(“I serve”);Servcie.requestService();

(四) Naming類和Registry類類似。 用戶端:

      Naming.lookup(String url)

      url 格式如下"rmi://localhost/"+遠程對象引用

伺服器端:

     Registry registry=LocateRegistry.createRegistry(int port);
     Naming.rebind(“service”,service);

(五) RMISecurityManager類

     在RMI引用程式中,如果沒有設定安全管理器,則只能從本地類路徑載入stub和類,這可以確保應用程式不受由遠程方法調用所下載的代碼侵害

     在從遠程主機下載代碼之前必須執行以下代碼來安裝RMISecurityManager:

     System.setSecurityManager(new RMISecurityManager());

十一、代碼開發

        為了編寫一個demo,我們分為兩部分,一部分是server端的代碼,一部分是client端的代碼,client端的代碼主要是為了使用server端的代碼。當然這個代碼是非常簡單的,只是為了說明問題,現實的使用會使比較複雜的。

(一) 我們的目的

     建立一個server端的java project,包含遠程端的代碼,定義介面,定義介面實現,然後在建立一個client端的java project,通過RMI使用遠端服務中的方法。

(二) 我們的代碼結構

(三) 遠程服務代碼      1. 遠程服務的介面定義

       第一步就是建立和編譯服務介面的Java代碼。這個介面定義了所有的提供遠程服務的功能,下面是來源程式:

       UserManagerInterface.java

  package cn.com.tt.rmiserver.stub;  import java.rmi.Remote;  import java.rmi.RemoteException;  import cn.com.tt.rmiserver.bean.Account;  /**   *  author liuyazhuang   */  public interface UserManagerInterface extends Remote{      public String getUserName() throws RemoteException;     public Account getAdminAccount() throws RemoteException; }
  介面必須繼承Remote類,每一個定義地方法都要拋出RemoteException異常對象。    2. 介面的具體實現

    第二步就是對於上面的介面進行實現:

    UserManagerImp.java

  package cn.com.tt.rmiserver;    import java.rmi.RemoteException;    import cn.com.tt.rmiserver.stub.UserManagerInterface;  import cn.com.tt.rmiserver.bean.Account;  /**   *  author liuyazhuang   */  public class UserManagerImp implements UserManagerInterface {      public UserManagerImp() throws RemoteException {      }     private static final long serialVersionUID = -3111492742628447261L;      public String getUserName() throws RemoteException{         return "TT";     }     public Account getAdminAccount() throws RemoteException{         Account account=new Account();         account.setUsername("TT");         account.setPass<a href="http://www.it165.net/edu/ebg/" target="_blank" class="keylink">word</a>("123456");         return account;     } }

  3. 定義一個bean

   實現implements Serializable序列化介面。也就是可以在client和server端進行傳輸的可序列化對象。

      Account.java

  package cn.com.tt.rmiserver.bean;    import java.io.Serializable;  /**   *  @author liuyazhuang   */  public class Account implements Serializable,Cloneable{      private static final long serialVersionUID = -1858518369668584532L;      private String username;      private String pass<a href="http://www.it165.net/edu/ebg/" target="_blank" class="keylink">word</a>;           public String getUsername() {         return username;     }     public void setUsername(String username) {         this.username = username;     }     public String getPassword() {         return password;     }     public void setPassword(String password) {         this.password = password;     } }
  4. 定義server端的主程式入口。

          Entry.java

  package cn.com.tt.rmiserver.entry;    import java.rmi.AlreadyBoundException;  import java.rmi.RemoteException;  import java.rmi.registry.LocateRegistry;  import java.rmi.registry.Registry;  import java.rmi.server.UnicastRemoteObject;    import cn.com.tt.rmiserver.UserManagerImp;  import cn.com.tt.rmiserver.stub.UserManagerInterface;  /**   * @author liuyazhuang   */  public class Entry {     public static void main(String []args) throws AlreadyBoundException, RemoteException{         UserManagerImp userManager=new UserManagerImp();         UserManagerInterface userManagerI=(UserManagerInterface)UnicastRemoteObject.exportObject(userManager,0);         // Bind the remote object's stub in the registry         Registry registry = LocateRegistry.createRegistry(2002);                 registry.rebind("userManager", userManagerI);         System.out.println("server is ready");         } }

(四) client端代碼        把Server端的Account類和介面UserManagerInterface 匯出Export成jar包,命名為:RmiServerInterface.jar。匯入到client中。項目——右鍵——Export——java——jar file——next——選擇Account類和介面UserManagerInterface——命名為:RmiServerInterface.jar如:

    5. 建立一個java Project,匯入jar包,編寫用戶端代碼。

    6. 代碼     ClientEntry.java

  package weiblog.rmi;    import java.rmi.NotBoundException;  import java.rmi.RemoteException;  import java.rmi.registry.LocateRegistry;  import java.rmi.registry.Registry;    import cn.com.tt.rmiserver.stub.UserManagerInterface;  /**   *  @author liuyazhuang   */  public class ClientEntry {          public static void main(String []args){                  try {             Registry registry = LocateRegistry.getRegistry("localhost",2004);             UserManagerInterface userManager = (UserManagerInterface)registry.lookup("userManager");             System.out.println("使用者名稱是"+userManager.getAdminAccount().getUsername()                     +"密碼"+userManager.getAdminAccount().getPassword());         } catch (RemoteException e) {             // TODO Auto-generated catch block             e.printStackTrace();         } catch (NotBoundException e) {             // TODO Auto-generated catch block             e.printStackTrace();         }              }  }

     7. 先運行伺服器端代碼, 然後運行用戶端代碼

    顯示運行結果,用戶端可以運行多次,每次都可以取得伺服器端的對象。如果要再次運行用戶端代碼就需要更改連接埠號碼,如果不更改就會顯示連接埠號碼被佔用。





Java之——RMI遠端程序呼叫(插曲)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.