一、RMI简介:

说到RMI就不得不说RPC了。

RPC:(Remote Procedure Call),远程过程调用。

RMI(Remote Method Invocation),远程方法调用。

RPC和RMI是有区别的,RPC中是通过网络服务协议向远程主机发送请求,RPC远程主机就去搜索与之相匹配的类和方法,找到后就执行方法并把结果编码,通过网络协议发回。

而RMI是通过客户端的对象作为远程接口进行远程方法的调用。RMI只适用于Java语言。

二、RMI的运行机理:

涉及两个网络端。其核心思想是,一个端可以通过调用另一个端的方法,实现相关功能。
一个端“执行”一个方法,而这个方法的实际执行是在另一端进行的!

当然,两个端都应该有相同的类,自然会拥有相同的方法。
一个端所谓的执行这个方法,其实是通过调用这个类的代理对象的方法,在其中拦截这个方法,在这个方法中
实际上是将执行这个方法的参数和类名称、方法名称,通过网络通讯传输给另一端;另一端根据得到的方法名称、
类名称和参数,实际执行那个方法,再将方法执行结果回传给对端。
要注意的问题:
1、实际执行方法的一端,我们可以认为是RMI服务器端,伪执行一端,自然是RMI客户端;
2、伪执行端不应该自己完成参数、方法名称和类名称的传递工作;也就是说,对于RMI客户端用户而言,他只面对一个类的一个方法,
    直接执行就好;
3、RMI服务器端可能接收多个RMI客户端有关这个方法的执行请求,每个客户端的执行当然应该是独立的,应该用线程实现;
4、RMI服务器端在执行了相关方法,并回传方法执行结果后,应该断开与RMI客户端的连接。

下面是我要实现它的一个思路。

上图由于截图原因,给点补充说明:RpcClientExecutor的作用是建立和服务器的连接,并接受消息和发送消息,具体是发送方法的序列号和参数类型。

1.首先:应该是RpcBeanDefinition:

 package com.xupt.rpc.core;

 import java.lang.reflect.Method;

 public class RpcBeanDefination {   //将类,类方法,类对象封装在Definition中。

     private Class<?> klass;
private Method method;
private Object object; RpcBeanDefination() {
}

给该类所有的成员都有getter和setter方法就不需要说了,这个类,将执行的哪个类的哪个方法和类的对象封装起来,以后这个类将形成Map

的值,下面来介绍的RpcBeanFactory会重点介绍。

2.RpcBeanFactory

 package com.xupt.rpc.core;

 import java.util.HashMap;
import java.util.Map; public class RpcBeanFactory { private final Map<String, RpcBeanDefination> beanMap; RpcBeanFactory() {
beanMap = new HashMap<>();
} void rpcBeanRegistry(String beanId,RpcBeanDefination defination) {
RpcBeanDefination rbd = beanMap.get(beanId);
if(rbd != null) {
return;
}
beanMap.put(beanId, defination);
} RpcBeanDefination getBean(String beanId) {
return beanMap.get(beanId);
}
}

此类是将序列号作为Map中的键,RpcBeanDeifintion作为值放入Map中,用BeanId来找对应客户端那边序列号相同的方法。

3.下来是RpcBeanRegistry:

 package com.xupt.rpc.core;

 import java.lang.reflect.Method;

 public class RpcBeanRegistry {

     RpcBeanRegistry() {
} //给客户端提供
static void registryInterface(RpcBeanFactory rpcBeanFactory,Class<?> interfaces) {
doregistry(rpcBeanFactory,interfaces,null);
} //内部使用,注册
private static void doregistry(RpcBeanFactory rpcBeanFactory , Class<?> interfaces ,Object object) {
//得到接口中的所有的方法,行程方法的数组
Method[] methods = interfaces.getDeclaredMethods();
for(Method method : methods) { //遍历这些方法
String beanId = String.valueOf(method.toString().hashCode());//将方法序列化。
RpcBeanDefination rpcBeanDefination = new RpcBeanDefination(); //将得到的实现接口的那个类和它的方法以及对象放进RpcBeanDefination()中。
rpcBeanDefination.setKlass(interfaces);
rpcBeanDefination.setMethod(method);
rpcBeanDefination.setObject(object); rpcBeanFactory.rpcBeanRegistry(beanId, rpcBeanDefination);
}
} //服务端使用,知道实现类的对象。
static void registryInterface(RpcBeanFactory rpcBeanFactory,Class<?> interfaces,Object object) {
//判断此类是否实现了这个接口。
if(!interfaces.isAssignableFrom(object.getClass())){
return;
}
doregistry(rpcBeanFactory,interfaces,object);
} //服务器端使用,知道类,创建一个对象。
static void registryInterface(RpcBeanFactory rpcBeanFactory,Class<?> interfaces,Class<?> klass) {
//判断该类是否实现了接口。
if(!interfaces.isAssignableFrom(klass)){
return;
}
try {
doregistry(rpcBeanFactory, interfaces, klass.newInstance());
} catch (Exception e) {
e.printStackTrace();
}
}
}

这个类是同时给客户端和服务器使用的,所以有一个私有方法来完成类方法的获得和序列号的产生(method.toString().hashcode())。然后将类、方法、对象放进RpcBeanDefinition中,将得到的序列号作为键和rpcBeanDeifinyion作为值放进Map中,形成键值对,方便客户端和服务器的调用。

4.下来是RpcServer:

package com.xupt.rpc.core;

 import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket; public class RpcServer implements Runnable { private ServerSocket server;
private int port;
private boolean goon;
private final RpcBeanFactory rpcBeanFactory;
private static long executorId; public RpcServer() {
rpcBeanFactory = new RpcBeanFactory();
this.goon = false;
} public void setPort(int port) {
this.port = port;
} public void startRpcServer() throws Exception {
if(this.port == 0) {
return;
}
server = new ServerSocket(port);
this.goon = true;
new Thread(this,"Rpc_Server").start();//启动线程
} public void stopRpcServer() { //关闭服务器
if (this.server != null && !this.server.isClosed()) {
try {
this.server.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
this.server = null;
}
}
} RpcBeanFactory getRpcBeanFactory() {
return rpcBeanFactory;
} //用注册的方法知道类实现的接口,类的对象,通过对象执行类的方法。
public void rpcRegistry(Class<?> interfaces,Object object) {
RpcBeanRegistry.registryInterface(rpcBeanFactory, interfaces, object);
} public void rpcRegistry(Class<?> interfaces, Class<?> imClass) {
RpcBeanRegistry.registryInterface(rpcBeanFactory, interfaces,imClass);
} @Override
public void run() {
while(goon) {
try {
Socket socket = server.accept();//不断的侦听 new RpcServerExecutor(socket, this,++executorId); } catch (Exception e) {
goon = false;
e.printStackTrace();
}
}
stopRpcServer();
}
}

上述服务器端的基本任务已经完成了。

下来我们来看看客户端:

首先还是:RpcClientExecutor,它是建立与服务器端的连接,以及接收,发送消息,具体发送的是方法的序列号和参数类型。

package com.xupt.rpc.core;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket; public class RpcClientExecutor { private String rpcServerIp; //ip地址
private int rpcServerPort; //端口号 RpcClientExecutor() {
} RpcClientExecutor(String rpcServerIp, int rpcServerPort) {
this.rpcServerIp = rpcServerIp;
this.rpcServerPort = rpcServerPort;
} String getRpcServerIp() {
return rpcServerIp;
} void setRpcServerIp(String rpcServerIp) {
this.rpcServerIp = rpcServerIp;
} int getRpcServerPort() {
return rpcServerPort;
} void setRpcServerPort(int rpcServerPort) {
this.rpcServerPort = rpcServerPort;
} //关闭客户端
private void closeSocket(ObjectInputStream ois, ObjectOutputStream oos, Socket socket) {
try {
if (ois != null) {
ois.close();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
ois = null;
}
try {
if (oos != null) {
oos.close();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
oos = null;
}
try {
if (socket != null && !socket.isClosed()) {
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
socket = null;
}
} @SuppressWarnings("unchecked")
<T> T rpExecutor(String beanId,Object[] params) throws Exception {
//连接服务器端
Socket socket = new Socket(rpcServerIp, rpcServerPort);
//发送方法的序列号和参数类型。
ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
oos.writeUTF(beanId);
oos.writeObject(params); //必须将这句放在前面三句的后面。
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
//接收服务器端返回的结果。
Object result = ois.readObject(); closeSocket(ois,oos,socket); return (T) result; }
}

下来是:RpcClient:这个类是产生代理,用代理对象伪执行相关的方法,然后在真正的来连接服务器。

 package com.xupt.rpc.core;

 import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy; public class RpcClient { private RpcClientExecutor rpcClientExecutor; public RpcClient(String rpcServerIp,int rpcServerport) {
this.rpcClientExecutor = new RpcClientExecutor(rpcServerIp, rpcServerport);
} @SuppressWarnings("unchecked")
public <T> T getProxy(Class<?> klass) {
return (T) Proxy.newProxyInstance(
klass.getClassLoader(),
new Class[] {klass},
new InvocationHandler() { @Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String BeanId = String.valueOf(method.toString().hashCode());
Object result = rpcClientExecutor.rpExecutor(BeanId, args); return result;
}
}
);
}
}

上述方法产生代理就不多做解释,不明白请看上一篇的代理机制之初见吧。。。。

以上就是我的RMI的简单实现,入如果有错误,请指正!也希望它对你有所帮助。

简单实现Java的RMI——远程方法调用的更多相关文章

  1. RMI远程方法调用

    RMI远程方法调用:适用于 客户端 调用 服务器 内的方法:(Kotlin 语言编写) 如果业务为二个服务器之间的通信,还是得用消息队列的形式,因为RMI 不适合 双向 调用 下面介绍RMI 的使用方 ...

  2. Java RMI远程方法调用

    RMI(远程接口调用) 1. RMI的原理: RMI系统结构,在客户端和服务器端都有几层结构. 方法调用从客户对象经占位程序(Stub).远程引用层(Remote Reference Layer)和传 ...

  3. Java APi 之 RMI远程方法调用

    一.什么是RPC RPC全称是remote procedure call,即远程过程调用.它是一种协议,用于从远程计算机上请求服务. 例如有两台服务器A和B,A上的应用想要调用B上应用的方法,但是他们 ...

  4. Java RMI 远程方法调用

    Java RMI 指的是远程方法调用 (Remote Method Invocation).它是一种机制,能够让在某个 Java 虚拟机上的对象调用另一个 Java 虚拟机中的对象上的方法.可以用此方 ...

  5. 【Java Web开发学习】远程方法调用RMI

    Java RMI 远程方法调用Remote Method Invocation 转载:http://www.cnblogs.com/yangchongxing/p/9078061.html 1.创建远 ...

  6. [转]Java远程方法调用

    Java远程方法调用,即Java RMI(Java Remote Method Invocation)是Java编程语言里,一种用于实现远程过程调用的应用程序编程接口.它使客户机上运行的程序可以调用远 ...

  7. SpringBoot里使用RMI进行远程方法调用

    一.Java RMI定义 Java RMI:Java远程方法调用,即Java RMI(Java Remote Method Invocation)是Java编程语言里,一种用于实现远程过程调用的应用程 ...

  8. Java中RMI远程调用demo

    Java远程方法调用,即Java RMI(Java Remote Method Invocation),一种用于实现远程过程调用的应用程序编程接口.它使客户机上运行的程序可以调用远程服务器上的对象.远 ...

  9. RMI远端方法调用

    一.RMI介绍 RMI(Remote Method Invocation),RMI是分布式对象软件包,它简化了在多台计算机上的JAVA应用之间的通信.必须在jdk1.1以上,RMI用到的类:java. ...

随机推荐

  1. Golang错误处理函数defer、panic、recover、errors.New介绍

    在默认情况下,当发生错误(panic)后,程序就会终止运行 如果发生错误后,可以捕获错误,并通知管理人员(邮件或者短信),程序还可以继续运行,这当然无可厚非 errors.New("错误信息 ...

  2. Linux 流量监控工具 iftop

    Linux 流量监控工具 iftop Iftop工具主要用来显示本机网络流量情况及各相互通信的流量集合,如单独同哪台机器间的流量大小,非常适合于代理服务器和iptables服务器使用,这样可以方便的查 ...

  3. neutron 的 quota design

    发现, cinder, nova 制实现了, CountableResource. 只有nuetron实现了 TrackedResource 和 CountableResource. I read u ...

  4. linux homebrew skill 技巧

    $ cat /usr/bin/gitdiffH0#!/bin/bashgit diff HEAD $(git ls-files| grep '\.py$\|\.py\.in$') | osflake8 ...

  5. 【专家坐堂Q&A】在 petalinux-config 中选择外部来源时,可将符号链路添加内核来源目录树

    问题描述 作为 petalinux-config 菜单的一部分,现在可以将 Linux 内核指定为外部来源. 如果选择了该选项,可为内核来源目录树添加两个符号链路. 这会带来两个问题: 1. 符号链路 ...

  6. CEF3开发者系列之Cookies管理和共享

    涉及网页登录相关的技术,Cookies肯定是忽略不了的.由于项目的需要,要做一个双核的产品.双核间切换会涉及到登录状态的问题,共享Cookies是一个很好的方案.既然涉及到共享cookies,那么读取 ...

  7. STM32的FLASH ID加密

    #define FLASH_ID_OFFSET 30000    //任意定义一个数 //把地址直接减去或者加上一个数是不要程序中直接出现这个地址 volatile u32 Flash_ID_addr ...

  8. 安装jumpserver

    Centos7.5 安装jumpserver 同步服务器时间 #下载 [root@jumpserver ~]# yum install ntpdate -y #同步时间 [root@jumpserve ...

  9. ODAC(V9.5.15) 学习笔记(十四)TCRBatchMove

    名称 类型 说明 AbortOnKeyViol Boolean 在处理数据时,如果发生主键冲突或主键校验失败时,是否中断 AbortOnProblem Boolean 发生问题时是否中断 Change ...

  10. bzoj4709 柠檬 单调栈,DP,斜率优化

    目录 前言吐槽 思路 错误 代码 /* 前言吐槽 我真的不知道是咋做的 不过大约就是栈的斜率优化 哪位大佬见识广,给看看吧(乞讨) 思路 s是值等于a[i]的前缀和 转移方程$f[i]=max(f[i ...