在正式开始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);
}
}
依次运行FirstRmiAppServer
,FirstRmiAppClient
,控制台将打印: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());
}
}
依次运行:DogSkeleton
、DogClient
,控制台打印:
dog's weight is: 100
I'm watching!
RMI依靠Socket,然后使用Stub和Skeleton对象做通信代理,完成远程调用;