Java學習筆記(十六)——Java RMI

【前面的話】html

      最近過的好舒服,天天過的感受很充實,一些生活和工做的技巧注意了就會發現,其實生活也是能夠過的如此的有滋有味,知足如今的情況,而且感受很幸福。java

      學習java RMI的緣由是最近在使用dubbo框架作一個系統,因此對這java RMI進行學習,作一些筆記,基礎性文章,選擇性閱讀。c++

【定義】程序員

       Java RMIJava遠程方法調用,即Java RMI(Java Remote Method Invocation)是Java編程語言裏,一種用於實現遠程過程調用的應用程序編程接口。它使客戶機上運行的程序能夠調用遠程服務器上的對象。遠程方法調用特性使Java編程人員可以在網絡環境中分佈操做。RMI所有的宗旨就是儘量簡化遠程接口對象的使用。spring

      咱們知道遠程過程調用(Remote Procedure Call, RPC)能夠用於一個進程調用另外一個進程(極可能在另外一個遠程主機上)中的過程,從而提供了過程的分佈能力。Java 的 RMI 則在 RPC 的基礎上向前又邁進了一步,即提供分佈式對象間的通信。編程

      RMI(Remote Method Invocation)爲遠程方法調用,是容許運行在一個Java虛擬機的對象調用運行在另外一個Java虛擬機上的對象的方法。數組

      這兩個虛擬機能夠是運行在相同計算機上的不一樣進程中,也能夠是運行在網絡上的不一樣計算機中。安全

JavaRMI服務器

1、工做原理網絡

      RMI能讓一個Java程序去調用網絡中另外一臺計算機的Java對象的方法,那麼調用的效果就像是在本機上調用同樣。通俗的講:A機器上面有一個class,經過遠程調用,B機器調用這個class 中的方法。

      RMI,遠程方法調用(Remote Method Invocation)是Enterprise JavaBeans的支柱,是創建分佈式Java應用程序的方便途徑。RMI是很是容易使用的,可是它很是的強大。

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

2、RMI包含部分:

  1. 遠程服務的接口定義
  2. 遠程服務接口的具體實現
  3. 樁(Stub)和框架(Skeleton)文件
  4. 一個運行遠程服務的服務器
  5. 一個RMI命名服務,它容許客戶端去發現這個遠程服務
  6. 類文件的提供者(一個HTTP或者FTP服務器)
  7. 一個須要這個遠程服務的客戶端程序

3、RMI的用途?

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

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

     在Spring中實現RMI:

     ①在服務器端定義服務的接口,定義特定的類實現這些接口;

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

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

     ④在客戶端定義訪問與服務器端服務接口相同的類

4、RMI的侷限?                                                                    

      RMI目前使用Java遠程消息交換協議JRMP(Java Remote Messaging Protocol)進行通訊。JRMP是專爲Java的遠程對象制定的協議,因爲JRMP是專爲Java對象制定的,所以,RMI對於用非Java語言開發的應用系統的支持不足。不能與用非Java語言書寫的對象進行通訊(意思是隻支持客戶端和服務器端都是Java程序的代碼的遠程調用)。

5、RMI的使用侷限?

      因爲客戶機和服務器都是使用Java編寫的,兩者平臺兼容性的要求僅僅是雙方都運行在版本兼容的Java虛擬機上。

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

      當調用遠程對象上的方法時,客戶機除了能夠將原始類型的數據做爲參數一外,還能夠將對象做爲參數來傳遞,與之相對應的是返回值,能夠返回原始類型或對象,這些都是經過Java的對象序列化(serialization)技術來實現的。(換而言之:參數或者返回值若是是對象的話必須實現Serializable接口)

7、 RMI應用程序的基本模型

                       

8、RMI體系結構

 

      樁/框架(Stub/Skeleton)層:客戶端的樁和服務器端的框架;

      遠程引用(remote reference)層:處理遠程引用行爲

      傳送層(transport):鏈接的創建和管理,以及遠程對象的跟蹤

9、 RMI類和接口(完成一個簡單RMI須要用到的類)。

 

 

 

(一) Remote接口:是一個不定義方法的標記接口

      Public interface Remote{}

      在RMI中,遠程接口聲明瞭能夠從遠程Java虛擬機中調用的方法集。遠程接口知足下列要求:

      一、遠程接口必須直接或間接擴展Java.rmi.Remote接口,且必須聲明爲public,除非客戶端於遠程接口在同一包中

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

      三、在遠程方法聲明中,做爲參數或返回值聲明的遠程對象必須聲明爲遠程接口,而非該接口的實現類。

(二) RemoteObject抽象類實現了Remote接口和序列化Serializable接口,它和它的子類提供RMI服務器函數。

(三) LocateRegistry final()用於得到特定主機的引導遠程對象註冊服務器程序的引用(即建立stub),或者建立能在特定端口接收調用的遠程對象註冊服務程序。

服務器端向其餘客戶機提供遠程對象服務

      SomeService servcie=……;//遠程對象服務

  1. Registry registry=LocateRegisty.getRegistry();//Registry是個接口,他繼承了Remote,此方法返回本地主機在默認註冊表端口 1099 上對遠程對象 Registry 的引用。
  2. getRegistry(int port) 返回本地主機在指定 port 上對遠程對象 Registry 的引用;
  3. getRegistry(String host)  返回指定 host 在默認註冊表端口 1099 上對遠程對象 Registry 的引用;
  4. getRegistry(String host, int port) 返回指定的 hostport 上對遠程對象 Registry 的引用
  5. registry.bind(「I serve」,service);// bind(String name,Remote obj) 綁定對此註冊表中指定 name 的遠程引用。name : 與該遠程引用相關的名稱 obj : 對遠程對象(一般是一個 stub)的引用
  6. unbind(String name)移除註冊表中指定name的綁定。
  7. rebind(String name,Remote obj)從新綁定,若是name已存在,可是Remote不同則替換,若是Remote同樣則丟棄現有的綁定
  8. lookup(String name) 返回註冊表中綁定到指定 name 的遠程引用,返回Remote
  9. String[] 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());

10、demo開發

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

(一) 咱們的目的

     創建一個server端的java project,包含遠程端的代碼,定義接口,定義接口實現,而後在創建一個client端的java project,經過RMI使用遠端服務中的方法。

(二) 咱們的代碼結構

 

(三) 遠程服務代碼

      1. 遠程服務的接口定義

       第一步就是創建和編譯服務接口的Java代碼。這個接口定義了全部的提供遠程服務的功能,下面是源程序:

       UserManagerInterface.java

 1 package cn.com.tt.rmiserver.stub;
 2 
 3 import java.rmi.Remote;
 4 import java.rmi.RemoteException;
 5 
 6 import cn.com.tt.rmiserver.bean.Account;
 7 
 8 public interface UserManagerInterface extends Remote{
 9     public String getUserName() throws RemoteException;
10     public Account getAdminAccount() throws RemoteException;
11 }

     接口必須繼承Remote類,每個定義地方法都要拋出RemoteException異常對象。

    2. 接口的具體實現

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

    UserManagerImp.java

 1 package cn.com.tt.rmiserver;
 2 
 3 import java.rmi.RemoteException;
 4 
 5 import cn.com.tt.rmiserver.stub.UserManagerInterface;
 6 import cn.com.tt.rmiserver.bean.Account;
 7 
 8 public class UserManagerImp implements UserManagerInterface {
 9     public UserManagerImp() throws RemoteException {
10 
11     }
12     private static final long serialVersionUID = -3111492742628447261L;
13 
14     public String getUserName() throws RemoteException{
15         return "TT";
16     }
17     public Account getAdminAccount() throws RemoteException{
18         Account account=new Account();
19         account.setUsername("TT");
20         account.setPassword("123456");
21         return account;
22     }
23 }

      3. 定義一個bean,實現implements Serializable序列化接口。也就是能夠在client和server端進行傳輸的可序列化對象。

      Account.java

 1 package cn.com.tt.rmiserver.bean;
 2 
 3 import java.io.Serializable;
 4 
 5 public class Account implements Serializable,Cloneable{
 6     private static final long serialVersionUID = -1858518369668584532L;
 7     private String username;
 8     private String password;
 9     
10     public String getUsername() {
11         return username;
12     }
13     public void setUsername(String username) {
14         this.username = username;
15     }
16     public String getPassword() {
17         return password;
18     }
19     public void setPassword(String password) {
20         this.password = password;
21     }
22 }

      4. 定義server端的主程序入口。

          Entry.java

 1 package cn.com.tt.rmiserver.entry;
 2 
 3 import java.rmi.AlreadyBoundException;
 4 import java.rmi.RemoteException;
 5 import java.rmi.registry.LocateRegistry;
 6 import java.rmi.registry.Registry;
 7 import java.rmi.server.UnicastRemoteObject;
 8 
 9 import cn.com.tt.rmiserver.UserManagerImp;
10 import cn.com.tt.rmiserver.stub.UserManagerInterface;
11 
12 public class Entry {
13     public static void main(String []args) throws AlreadyBoundException, RemoteException{
14         UserManagerImp userManager=new UserManagerImp();
15         UserManagerInterface userManagerI=(UserManagerInterface)UnicastRemoteObject.exportObject(userManager,0);
16         // Bind the remote object's stub in the registry
17         Registry registry = LocateRegistry.createRegistry(2002);
18        
19         registry.rebind("userManager", userManagerI);
20         System.out.println("server is ready");
21         }
22 }

(四) client端代碼

  1. 把Server端的Account類和接口UserManagerInterface 導出Export成jar包,命名爲:RmiServerInterface.jar。導入到client中。
  2. 項目——右鍵——Export——java——jar file——next——選擇Account類和接口UserManagerInterface——命名爲:RmiServerInterface.jar以下圖:

 

     3. 新建一個java Project,導入jar包,編寫客戶端代碼。

    4. 代碼

         ClientEntry.java

 1 package weiblog.rmi;
 2 
 3 import java.rmi.NotBoundException;
 4 import java.rmi.RemoteException;
 5 import java.rmi.registry.LocateRegistry;
 6 import java.rmi.registry.Registry;
 7 
 8 import cn.com.tt.rmiserver.stub.UserManagerInterface;
 9 
10 public class ClientEntry {
11     
12     public static void main(String []args){
13         
14         try {
15             Registry registry = LocateRegistry.getRegistry("localhost",2004);
16             UserManagerInterface userManager = (UserManagerInterface)registry.lookup("userManager");
17             System.out.println("用戶名是"+userManager.getAdminAccount().getUsername()
18                     +"密碼"+userManager.getAdminAccount().getPassword());
19         } catch (RemoteException e) {
20             // TODO Auto-generated catch block
21             e.printStackTrace();
22         } catch (NotBoundException e) {
23             // TODO Auto-generated catch block
24             e.printStackTrace();
25         }
26         
27     }
28 
29 }

     5. 先運行服務器端代碼, 而後運行客戶端代碼,就會顯示運行結果,客戶端能夠運行屢次,每次均可以取得服務器端的對象。若是要再次運行客戶端代碼就須要更改端口號,若是不更改就會顯示端口號被佔用。

【參考資料】

     1. demo參考的下面文章:

         RMI網絡編程開發之二 如何搭建基於JDK1.5的分佈式JAVA RMI 程序

     2. 理論知識參考的下面文章:

       RMI入門教程 

       java_RMI技術講解

【後面的話】

      快要放假了多麼值得高興。

      時至今日都是我咎由自取,學java那是自找,與任何人無關。大學生活過的平順,造就了我輕信java,編寫衆多bug的脾氣,致使今日朝不保夕的地步,我今天願意承擔一切後果。其實,我很感激大家讓我在測試的時候發現bug,而不是到了生產環境,我必須從新梳理我所形成的bug,坦然面對每一個bug。我,在學java的路上編寫了大量的bug。我辜負了cc++,辜負了C#,辜負了程序員和碼農的稱呼,辜負了全部覺得我能夠寫出漂亮代碼的人。對不起,請接收我發自心裏的歉意和懺悔。晚上和週末原本有一個溫暖和美的生活,但是這一切被我學習java給打破了,我學java的行爲不配獲得原諒,我形成的無休止的bug現狀也難以修改,但我仍是想修改,我必須修改,這是我今日以後的生活。至於我本身,已咎由自取,願往後不在產生bugjava程序員。——我真是愈來愈喜歡自黑了。哈哈,讓我笑一會。

——TT

相關文章
相關標籤/搜索