Dubbo系列1:Java RMI初见

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

一、Java RMI的原理

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

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

Dubbo系列1:Java RMI初见_第1张图片

图片来源: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对象做通信代理,完成远程调用;

你可能感兴趣的:(Dubbo系列1:Java RMI初见)