1. 程式人生 > >1.5分散式通訊框架-RMI

1.5分散式通訊框架-RMI

分散式通訊框架-RMI講解

什麼是RPC

Remote procedure call protocal

RPC協議其實是一個規範。常用PRC框架:Dubbo、Thrif、RMI、Webservice、Hessain

網路協議和網路IO對於呼叫端和服務端來說是透明; 可以簡單的理解為使用者呼叫遠端方法。不必在乎這個遠端方法是怎麼出現的,就跟呼叫本地方法一樣。RPC框架做的事就是讓我們像使用本地方法一樣的去使用遠端方法,但是其實RPC框架的底層有自己寫好協議以便能遠端通訊。

一個RPC框架包含的要素

RPC的框架自然少不了 RPC的伺服器和要使用PRC框架的客戶端。

自底向上分析:

1.一個通訊協議最基本的就是通訊,常見的通訊協議有TCP和UDP,這屬於傳輸層 。

2.其次就是資料鏈路層,也可以認為是訊息層(畢竟都是資料).

3.而RPC通訊框架在客戶端連線時會返回給客戶端一個代理物件來執行想要遠端呼叫的方法。

伺服器端則是通過RPC框架獲取一個處理器。

4.再接著就是伺服器端和客戶端

這裡只是先闡述一個概念,當然這個圖並不完整,清接著往下看。

RMI的概述

RMI(remote method invocation) , 可以認為是RPC的java版本

RMI使用的是JRMP(Java Remote Messageing Protocol), JRMP是專門為java定製的通訊協議,所以踏實純java的分散式解決方案

如何實現一個RMI程式

1. 建立遠端介面, 並且繼承java.rmi.Remote介面

2. 實現遠端介面,並且繼承:UnicastRemoteObject

3. 建立伺服器程式: createRegistry方法註冊遠端物件

4. 建立客戶端程式

程式碼如下:

介面:模擬PRC的遠端方法

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

/**
 * @Auther: Anthony
 * @Date: 2018/10/16 22:14
 * @Description:介面 模擬PRC的遠端方法
 
*/ public interface ISayHello extends Remote{ public String sayHello(String name) throws RemoteException; }

具體實現:真正執行方法(繼承UnicastRemoteObject表明是一個遠端服務)

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

/**
 * @Auther: Anthony
 * @Date: 2018/10/16 22:15
 * @Description:
 */
public class SayHelloImpl extends UnicastRemoteObject implements ISayHello{

    protected SayHelloImpl() throws RemoteException {
    }

    @Override
    public String sayHello(String name) throws RemoteException {
        return "hello->"+name;
    }
}

建立server端

import java.net.MalformedURLException;
import java.rmi.AlreadyBoundException;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;

/**
 * @Auther: Anthony
 * @Date: 2018/10/16 22:19
 * @Description:
 */
public class HelloServer {

    public static void main(String[] args) {
        try {
            ISayHello hello = new SayHelloImpl();
            LocateRegistry.createRegistry(8888);//註冊服務
            Naming.bind("rmi://localhost:8888/sayHello",hello);//繫結服務
            System.out.println("server start success");

        } catch (RemoteException e) {
            e.printStackTrace();
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (AlreadyBoundException e) {
            e.printStackTrace();
        }
    }
}

建立Client端

import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;

/**
 * @Auther: Anthony
 * @Date: 2018/10/16 22:22
 * @Description:
 */
public class HelloClient {
    public static void main(String[] args) {
        try {
            ISayHello hello = (ISayHello) Naming.lookup("rmi://localhost:8888/sayHello");
            System.out.println(hello);
            System.out.println(hello.sayHello("wangqian"));
        } catch (NotBoundException e) {
            e.printStackTrace();
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }
}

 

這裡我們需要做的就是Server端註冊服務,Client端找到服務並呼叫方法

如果自己要去實現一個RMI

1. 編寫伺服器程式,暴露一個監聽, 可以使用socket

2. 編寫客戶端程式,通過ip和埠連線到指定的伺服器,並且將資料做封裝(序列化)

3. 伺服器端收到請求,先反序列化。再進行業務邏輯處理。把返回結果序列化返回

RMI給客戶端返回的代理為(呼叫方法名+Stub),這裡統一稱為Stub。

伺服器使用處理器是為Skeleton

具體RMI框架是怎麼操作的呢?

 

1.server端在註冊服務的時候,registry(可以理解為Dubbo裡的註冊中心 = =)就建立了一個代理物件Stub,

2.同時registry裡也同時生成了skeleton物件

3.客戶端訪問遠端服務,就返回建立好的Stub物件

4.Stub遠端呼叫(這裡其實就是底層通訊了),Skeleton(這裡相當於是一個不斷監聽的socket)發現有請求過來就指定相應的服務,返回服務給Client。

這裡就不貼程式碼了(知乎寫部落格是真的難受....)

給個地址:

https://github.com/MelonAnthony/BaseStudy/tree/master/qiansion-day1/src/main/java/per/wq/mvp/qiansion/demo​github.com

MelonAnthony/BaseStudy

原始碼分析

就拿上面的一段使用RMI框架的程式碼來說吧

ISayHello hello = new SayHelloImpl(); LocateRegistry.createRegistry(8888);//註冊服務 Naming.bind("rmi://localhost:8888/sayHello",hello);//繫結服務 System.out.println("server start success"); 

先看這個createRegistry

if分支是判斷安全性的,而我們傳的埠是8888,所以走的是else分支

setup裡的主要方法就是這個exportObject,可以認為是暴露服務。

先看第一個紅框-建立了一個代理這裡var4 就是RegistryImpl也就是一個Registry。由於var2是true,這裡建立的就是一個Stub物件(不信的同學可以去這個createProxy裡驗證一下)。算了,我還是貼出來吧:邏輯或,這我就不解釋了吧

 

然後是setSkeleton,詳見下圖:

同樣是建立一個Skeleton。

這樣我們上面說的時序圖的1,2步就能說得通了。

至於3,4步,希望大家自己看一看原始碼,今天太晚了,我就不補充了。下次再來吧。

以上為本人自己的分析,有錯誤希望各路高人指點江山,指正一下。