Dubbo系列1:Java RMI初见

在正式开始Dubbo系列之前,先来介绍一下Java RMI服务;

一、Java RMI的原理

Java RMI是The Java Remote Method Invocation的简写,其字面含义为:java远程方法调用;Java RMI允许一个JVM上的对象调用另一个JVM中的对象,其使得两个JVM之间可以相互通信,Dubbo则借鉴了这种理念;

RMI可用于构建分布式应用,其原理如下图:


图片来源:https://onclick786.blogspot.com/2016/08/java-remote-method-invocation-or-java.html

1、RMI的基本概念

  • Client Host,客户端
  • Server Host,服务端
  • Server Object,远程对象,存在于服务端,Server Skeleton依赖其做出业务响应;
  • Server Stub 对象,客户端本地存根,从远程获取,充当远程服务(主要为Server Object)的客户端代理;
  • Server Skeleton 对象,存在于服务端,负责将Server Stub传输过来的数据进行解包,以及调用Server Object的方法,然后封装返回值给stub对象;
  • Server Stub 和Server Skeleton 在各自的JVM中使用socket通信将远程调用伪装成本地调用,理解此至关重要,后面将有详解;
  • 通过 RMI 注册服务完成服务的注册和发现;

2、调用步骤:

  • (1). 服务端(Server Host)向注册服务注册(RMI Registry Host)自己的地址;
  • (2). 客户端(Client Host)通过 RMI 注册服务获取(Look for)目标地址;
  • (3). 注册中心返回一个本地存根(Server Stub)给客户端;
  • (4). 客户端调用本地的 Stub(即存根对象,Server Stub) 对象上的方法,将请求参数通过网络通信传输给服务端的Server Skeleton
  • (5). 服务端的 Skeleton(Server Skeleton) 对象收到网络请求之后,将调用信息解包,然后找到真正的服务对象(Server Object)发起调用,并将返回结果打包通过网络(Data Communication)发送回客户端。

二、Java RMI 使用案例

1. 创建远程对象Server Object

package com.codestack.dubbo.learn.FirstRmi.service;

import java.rmi.Remote;
import java.rmi.RemoteException;
/**
 * @author Ling
 * @create 2018-09-03-9:26
 **/
public interface Hello extends Remote{
    String sayHello(String name) throws RemoteException;
}

其中sayHello必须声明RemoteException异常;

Server Object的具体实现:

package com.codestack.dubbo.learn.FirstRmi.service.impl;

import com.codestack.dubbo.learn.FirstRmi.service.Hello;
/**
 * @author Ling
 * @create 2018-09-03-9:26
 **/
public class HelloImpl implements Hello {
    @Override
    public String sayHello(String name) {
        return "hello! " + name;
    }
    
}

2. 服务注册与绑定

package com.codestack.dubbo.learn.FirstRmi;

import com.codestack.dubbo.learn.FirstRmi.service.Hello;
import com.codestack.dubbo.learn.FirstRmi.service.impl.HelloImpl;

import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;


/**
 * @author Ling
 * @create 2018-09-03-9:26
 **/
public class FirstRmiAppServer {
    public static void main(String[] args) throws RemoteException {
        //创建Skeleton
        Hello obj = new HelloImpl(); // #1
        Hello skeleton = (Hello) UnicastRemoteObject.exportObject(obj, 0); // #2
        //创建本地注册中心
        Registry registry = LocateRegistry.createRegistry(1099); // #3
        //绑定skeleton
        registry.rebind("Hello", skeleton); // #4
    }
}

3. 客户端注册

package com.codestack.dubbo.learn.FirstRmi;

import com.codestack.dubbo.learn.FirstRmi.service.Hello;

import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

/**
 * @author Ling
 * @create 2018-09-03-9:26
 **/
public class FirstRmiAppClient {
    public static void main(String[] args) throws RemoteException, NotBoundException {
        Registry registry = LocateRegistry.getRegistry(); // #1 获取本地注册中心实例
        Hello stub = (Hello) registry.lookup("Hello"); // #2 返回一个本地存根
        String response = stub.sayHello("Chris"); // #3 调用远程方法
        System.out.println(response);
    }
}

依次运行FirstRmiAppServerFirstRmiAppClient,控制台将打印:hello! Chris

三、RMI的本质:Stub 和 Skeleton剖析

1. 定义服务端ServerObject

package com.codestack.dubbo.learn.FirstRmi.RmiTheory;

public interface Dog {
    public int weight() throws Throwable;
    public String watch() throws Throwable;
}

实现Dog接口:

package com.codestack.dubbo.learn.FirstRmi.RmiTheory;

public class DogServer implements Dog{
    private int weight;
    @Override
    public int weight() throws Throwable {
        return 100;
    }

    @Override
    public String watch() throws Throwable {
        return "I'm watching!";
    }
}

2. 定义服务端Skeleton

package com.codestack.dubbo.learn.FirstRmi.RmiTheory;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class DogSkeleton extends Thread{
    private DogServer dogServer;
    public DogSkeleton(DogServer server){
        this.dogServer = server;
    }

    public void run() {
        ServerSocket serverSocket = null;
        try {
            serverSocket = new ServerSocket(8000);
            Socket socket = serverSocket.accept();

            while (!socket.isClosed()) {
                ObjectInputStream inStream =
                        new ObjectInputStream(socket.getInputStream());
                //获取客户端Stub的请求方法
                String method = (String) inStream.readObject();
                if  (method.equals( "weight" )) {
                    //调用ServerObject的真实方法
                    int  age = dogServer.weight();
                    ObjectOutputStream outStream =
                            new  ObjectOutputStream(socket.getOutputStream());
                    //返回结果给客户端Stub
                    outStream.writeInt(age);
                    outStream.flush();
                }
                if (method.equals( "watch" )) {
                    //调用ServerObject的真实方法
                    String name = dogServer.watch();
                    ObjectOutputStream outStream =
                            new  ObjectOutputStream(socket.getOutputStream());
                    // return result to stub
                    outStream.writeObject(name);
                    outStream.flush();
                    socket.close();
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }

    }

    public static void main(String[] args){
        DogServer dogServer = new DogServer();
        DogSkeleton dogSkeleton = new DogSkeleton(dogServer);
        dogSkeleton.start();
    }
}

将DogSkeleton作为外部代理,创建一个socket,等待客户端的连接,根据客户端传入的不通参数,实际调用DogServer来做相应的处理;

3. 定义客户端Stub

package com.codestack.dubbo.learn.FirstRmi.RmiTheory;

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;

public class DogStub implements Dog{

    private Socket socket;

    public DogStub() throws Throwable {
        socket =  new  Socket( "localhost" ,  8000 );
    }

    @Override
    public int weight() throws Throwable {
        ObjectOutputStream outStream =
                new  ObjectOutputStream(socket.getOutputStream());
        outStream.writeObject( "weight" );
        outStream.flush();
        ObjectInputStream inStream =
                new  ObjectInputStream(socket.getInputStream());
        return  inStream.readInt();
    }

    @Override
    public String watch() throws Throwable {
        ObjectOutputStream outStream =
                new  ObjectOutputStream(socket.getOutputStream());
        outStream.writeObject( "watch" );
        outStream.flush();
        ObjectInputStream inStream =
                new  ObjectInputStream(socket.getInputStream());
        return  (String)inStream.readObject();
    }
}

构造DogStub的时候创建一个socket,然后将请求参数写入到ObjectInputStream中,传递给服务端。

4. 测试Demo


package com.codestack.dubbo.learn.FirstRmi.RmiTheory;

public class DogClient {

    public static void main(String[] args) throws Throwable {
        Dog dogStub = new DogStub();
        System.out.println("dog's weight is: "+dogStub.weight());
        System.out.println(dogStub.watch());

    }
}

依次运行:DogSkeletonDogClient,控制台打印:

dog's weight is: 100
I'm watching!

RMI依靠Socket,然后使用Stub和Skeleton对象做通信代理,完成远程调用;

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容