Rmi學習筆記

科技小能手發表於2017-11-08
RMI和RPC之間最主要的區別在於方法是如何別呼叫的。在RMI中,遠端介面使每個遠端方法都具有方法簽名。如果一個方法在伺服器上執行,但是沒有相匹配的簽名被新增到這個遠端介面上,那麼這個新方法就不能被RMI客戶方所呼叫。在RPC中,當一個請求到達RPC伺服器時,這個請求就包含了一個引數集和一個文字值,通常形成“classname.methodname”的形式。這就向RPC伺服器表明,被請求的方法在為“classname”的類中,名叫“methodname”。然後RPC伺服器就去搜尋與之相匹配的類和方法,並把它作為那種方法引數型別的輸入。這裡的引數型別是與RPC請求中的型別是匹配的。一旦匹配成功,這個方法就被呼叫了,其結果被編碼後返回客戶方。
附英語描述(Difference between RMI and RPC):
RMI or Remote Method Invokation is very similar to RPC or Remote Proceedure call in that the client both send proxy objects (or stubs) to the server however the subtle difference is that client side RPC invokes FUNCTIONS through the proxy function and RMI invokes METHODS through the proxy function. RMI is considered slightly superior as it is an object-oriented version of RPC.
 
RMI 即遠端方法呼叫(Remote Method Invocation)是Enterprise JavaBeans的支柱,是建立分散式Java應用程式的方便途徑。
建立RMI應用程式分四步:
(1)定義和實現遠端介面中的引數
(2) 定義和實現遠端介面
(3) 編寫服務端程式碼
(4) 編寫客戶端程式碼
(5) 啟動rmiregistry , 並將服務註冊到rmiregistry.
JDK1.5之後不再需要建立存根與基幹,可以看到JAVA每天都在進步。
(1)定義和實現遠端介面中的引數,這裡定義一個學生實體,該類實現Serializable介面,因為引數可能需要網路傳輸.

package rmi; 

import java.io.Serializable; 

public class Student implements Serializable { 

  private static final long serialVersionUID = 1L; 

  private String name; 

  private int age; 

    

  public Student(String name, int age) { 

    super(); 

    this.name = name; 

    this.age = age; 

  } 


  public String getName() { 

    return name; 

  } 


  public void setName(String name) { 

    this.name = name; 

  } 


  public int getAge() { 

    return age; 

  } 


  public void setAge(int age) { 

    this.age = age; 

  } 


(2)定義和實現遠端介面
遠端介面的定義如下,介面須從java.rmi.Remote繼承;遠端介面中的方法如果要throws異常,這個異常必須是java.rmi.RemoteException(或java.rmi.RemoteException的子類),否則,這個異常就不能被返回到客戶端。


package rmi; 

import java.rmi.Remote; 
import java.rmi.RemoteException; 

public interface InfoConsult extends Remote{ 

        public int getAge(String name) throws RemoteException;    

        public Student getStudent(String name) throws RemoteException;    

}
實現遠端介面

package rmi; 
import java.rmi.RemoteException; 
import java.rmi.server.UnicastRemoteObject; 
import java.util.List; 

public class InfoConsultImpl extends UnicastRemoteObject implements InfoConsult{ 

        private List<Student> students; 

        protected InfoConsultImpl(List<Student> students) throws RemoteException { 

             super(); 

                

             this.students = students; 


        } 

        public int getAge(String name) throws RemoteException{ 


             for(Student stu:students){ 


                     if(stu.getName().equals(name)){ 


                            return stu.getAge(); 

                     } 

             } 

             return -1; 

        } 


        public Student getStudent(String name) throws RemoteException{ 


                for(Student stu:students){ 


                        if(stu.getName().equals(name)){ 


                             return stu; 


                        } 

                } 

                return new Student(null,-1); 

         } 


(3)服務端程式碼,伺服器端主要負責實現了一個遠端的物件以提供服務。繫結名稱給提供給遠端的物件,並讓執行中的系統可能開始了一個新的服務套接字或是共享一個服務套接字,監控遠端方法呼叫的呼叫.

package rmi; 

import java.net.MalformedURLException; 
import java.rmi.Naming; 
import java.rmi.RemoteException; 
import java.rmi.registry.LocateRegistry; 
import java.rmi.server.UnicastRemoteObject; 
import java.util.ArrayList; 
import java.util.List; 

import javax.naming.Context; 
import javax.naming.InitialContext; 
import javax.naming.NamingException; 

public class InfoServer { 


  public static void main(String args[]) throws RemoteException, 

      MalformedURLException, NamingException { 

                //使用程式建立RIM註冊服務 

    //LocateRegistry.createRegistry(8888); 

    InfoServer server = new InfoServer(); 

    InfoConsult consult = new InfoConsultImpl(server.getMockData()); 

    //consult= (InfoConsult)UnicastRemoteObject.exportObject(consult, 0); 

    Naming.rebind(“rmi://localhost:8888/InfoConsult”, consult); 

     

  } 


  public List<Student> getMockData() { 


    List<Student> lst = new ArrayList<Student>(); 


    lst.add(new Student(“guo”, 26)); 


    lst.add(new Student(“zhang”, 24)); 


    lst.add(new Student(“baby”, 1)); 


    return lst; 


  } 


}
(4)客戶端程式碼


package rmi; 

import java.net.MalformedURLException; 

import java.rmi.Naming; 

import java.rmi.NotBoundException; 

import java.rmi.RemoteException; 

public class School { 

  public static void main(String args[]) { 


    try { 


      InfoConsult consult = (InfoConsult) Naming 

          .lookup(“rmi://localhost:8888/InfoConsult”); 

      System.out.println(consult.getStudent(“baby”).getAge()); 


    } catch (MalformedURLException e) { 


      e.printStackTrace(); 


    } catch (RemoteException e) { 


      e.printStackTrace(); 


    } catch (NotBoundException e) { 


      e.printStackTrace(); 


    } 

  } 


}
lookup獲得遠端對像,其實是代理. 呼叫遠端方法.
(5)要啟動服務端,務必先啟RMI執行時. 用法很簡單 RmiRegistry 8888.如果省去8888,預設1089.Java RMI 登錄檔只是一個允許客戶端獲得遠端物件樁引用的簡單名字服務.
有時執行這一步之後,啟動服務端報以下錯誤:
1.java.rmi.ServerException: RemoteException occurred in server thread; nested exception is: 
java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is:   
3…..
沒有找到存根類.InfoConsultImpl與其存根在同一目錄下,為什麼沒有載入成功呢. 這是
因為存根類是由RmiRegistry 載入.而啟動RmiRegistry 與啟動服務端的classpath並不相同.所以就出現該異常.解決方法是先設定classpath環境變數這服務端工作目錄,再啟動服務端即可。
補充: 這一步也可以在程式裡完成, JAVA提供了LocateRegistry.createRegistry(8888)方法建立服務.
(6)最後一步 啟動服務端,啟動客戶端.顯示結果1.

本文轉自 anranran 51CTO部落格,原文連結:http://blog.51cto.com/guojuanjun/269346


相關文章