RMI 使用筆記


Java 遠程方法調用,即 Java RMI( Java Remote Method Invocation ) 。顧名思義,可以使客戶機上運行的程序能夠調用遠程服務器上的對象(方法)。

下面主要介紹一下使用步驟:

1.定義遠程接口(服務端)

遠程接口定義出可以讓客戶遠程調用的方法。

此接口必須實現 java.rmi.Remote 接口,來表示其支持遠程調用;同時其中聲明的所有方法,需要拋出RemoteException異常,因為遠程調用的不穩定性(如網絡原因等),這樣可以讓客戶端在調用失敗時進行相應的處理。

public interface DemoService extends Remote {
    String sayHello() throws RemoteException;
}

 

2.實現遠程接口(服務端)

遠程接口的實現類如果想要被遠程訪問,可以有如下實現方式:

繼承java.rmi.server.UnicastRemoteObject

public class DemoServerImpl extends UnicastRemoteObject implements DemoService{
​
    public DemoServerImpl() throws RemoteException {
        // 因為 UnicastRemoteObject 構造器拋出 RemoteException
        // 所以此處只能聲明一個構造器並拋出對應異常
    }
​
    @Override
    public String sayHello() throws RemoteException {
        return "Hello World";
    }
}

如果不想繼承UnicastRemoteObject類,則需要使用 UnicastRemoteObject類的靜態方法exportObject(Remote obj, int port)將對象導出

其中如果端口設為 0 的話,則表示任何合適的端口都可用來監聽客戶連接

public class DemoServerImpl implements DemoService{
​
    public DemoServerImpl() throws RemoteException {
        UnicastRemoteObject.exportObject(this, 0);
    }
​
    @Override
    public String sayHello() throws RemoteException {
        return "Hello World";
    }
}

這兩者方法本質上是一樣的,在UnicaseRemoteObject類的構造方法中,其實也是調用了exportObject方法

// UnicaseRemoteObject中的部分源碼
protected UnicastRemoteObject() throws RemoteException
{
    this(0);
}
// if port is zero, an anonymous port is chosen
protected UnicastRemoteObject(int port) throws RemoteException
{
    this.port = port;
    exportObject((Remote) this, port);
}

 

3.啟動 RMI 注冊表

注冊表就像一個電話簿,啟動后即可將提供的服務注冊到其中,客戶可以通過它查詢到服務來進行調用

啟動注冊表有兩種方法,一種是通過命令行rmiregistry來啟動,另一種方式是通過LocateRegistry.createRegistry(int port)方法。

4.注冊開啟遠程服務

注冊服務共有三種方式:

  1. LocateRegistry 類的對象的 rebind() 和 lookup() 來實現綁定注冊和查找遠程對象的

  2. 利用命名服務 java.rmi.Naming 類的 rebind() 和 lookup() 來實現綁定注冊和查找遠程對象的

  3. 利用JNDI(Java Naming and Directory Interface,Java命名和目錄接口) java.naming.InitialContext 類來 rebind() 和 lookup() 來實現綁定注冊和查找遠程對象的

其中第二種方式實際是對第一種方式的簡單封裝,在內部仍是調用Registry類的bind方法

// Naming 類的部分源碼 (為了節省篇幅,去除了拋出異常部分)
public static void bind(String name, Remote obj) throws ...
{
    ParsedNamingURL parsed = parseURL(name);
    Registry registry = getRegistry(parsed);
​
    if (obj == null)
        throw new NullPointerException("cannot bind to null");
​
    registry.bind(parsed.name, obj);
}

 

服務測試類:

public class ServerTest {
    public static void main(String[] args) throws Exception{
        String name = "rmi.service.DemoService";
        // 創建服務
        DemoService service = new DemoServerImpl();
        // 創建本機 1099 端口上的 RMI 注冊表
        Registry registry1 = LocateRegistry.createRegistry(1099);
      
        /***************** 以下為注冊方法一 ************/
        // 將服務綁定到注冊表中
        registry1.bind(name, service);
      
        /***************** 以下為注冊方法二 ************/
        // Naming.bind(name, service);
      
        /***************** 以下為注冊方法三 ************/
        //Context namingContext = new InitialContext();
        //namingContext.bind("rmi:" + name, service); // 此方式 name 需要以 rmi: 開頭
      
    }
}

 

客戶端測試類:

public class ClientTest {
    public static void main(String[] args) throws Exception {
        String name = "rmi.service.DemoService";
        /***************** 以下為查找服務方法一 ************/
        // 獲取注冊表
        Registry registry = LocateRegistry.getRegistry("localhost", 1099);
        // 查找對應的服務
        DemoService service = (DemoService) registry.lookup(name);
      
        /***************** 以下為查找服務方法二 ************/
        // DemoService service = (DemoService) Naming.lookup(name);
      
        /***************** 以下為查找服務方法三 ************/
        //Context namingContext = new InitialContext();
        //DemoService service = (DemoService) namingContext.lookup("rmi:" + name);
      
        // 調用服務
        System.out.println(service.sayHello());
    }
}

 

參考文章:https://segmentfault.com/a/1190000004494341

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM