简单实现Java的RMI——远程方法调用
一、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——远程方法调用的更多相关文章
- RMI远程方法调用
RMI远程方法调用:适用于 客户端 调用 服务器 内的方法:(Kotlin 语言编写) 如果业务为二个服务器之间的通信,还是得用消息队列的形式,因为RMI 不适合 双向 调用 下面介绍RMI 的使用方 ...
- Java RMI远程方法调用
RMI(远程接口调用) 1. RMI的原理: RMI系统结构,在客户端和服务器端都有几层结构. 方法调用从客户对象经占位程序(Stub).远程引用层(Remote Reference Layer)和传 ...
- Java APi 之 RMI远程方法调用
一.什么是RPC RPC全称是remote procedure call,即远程过程调用.它是一种协议,用于从远程计算机上请求服务. 例如有两台服务器A和B,A上的应用想要调用B上应用的方法,但是他们 ...
- Java RMI 远程方法调用
Java RMI 指的是远程方法调用 (Remote Method Invocation).它是一种机制,能够让在某个 Java 虚拟机上的对象调用另一个 Java 虚拟机中的对象上的方法.可以用此方 ...
- 【Java Web开发学习】远程方法调用RMI
Java RMI 远程方法调用Remote Method Invocation 转载:http://www.cnblogs.com/yangchongxing/p/9078061.html 1.创建远 ...
- [转]Java远程方法调用
Java远程方法调用,即Java RMI(Java Remote Method Invocation)是Java编程语言里,一种用于实现远程过程调用的应用程序编程接口.它使客户机上运行的程序可以调用远 ...
- SpringBoot里使用RMI进行远程方法调用
一.Java RMI定义 Java RMI:Java远程方法调用,即Java RMI(Java Remote Method Invocation)是Java编程语言里,一种用于实现远程过程调用的应用程 ...
- Java中RMI远程调用demo
Java远程方法调用,即Java RMI(Java Remote Method Invocation),一种用于实现远程过程调用的应用程序编程接口.它使客户机上运行的程序可以调用远程服务器上的对象.远 ...
- RMI远端方法调用
一.RMI介绍 RMI(Remote Method Invocation),RMI是分布式对象软件包,它简化了在多台计算机上的JAVA应用之间的通信.必须在jdk1.1以上,RMI用到的类:java. ...
随机推荐
- mysql 5.7多源复制(用于生产库多主库合并到一个查询从库)
目前我们使用的是主从+分库分表的系统架构,主库有N个分库,从库为多个slave做负载均衡,所以数据库端的架构是下面这样的: 因为差不多有一年半没有专门搞技术为主了,顺带回顾下. 这就涉及到多个主库数据 ...
- Net SMTP QQ 发送邮件
调用DEMO var currUser = new List<string> { "123@qq.com" , "123@qq.com" , &qu ...
- Examples of Scikit-learn Usages
Examples of Scikit-learn Usages KFold K-折交叉验证 >>> import numpy as np >>> from skle ...
- php 操作 mysql 数据库常用方法集合
参考: https://www.runoob.com/php/php-pdo.html https://www.cnblogs.com/feng18/p/6523646.html https://bl ...
- SSM集成activiti6.0错误集锦(一)
项目环境 Maven构建 数据库:Orcle12c 服务器:Tomcat9 <java.version>1.8</java.version> <activiti.vers ...
- Bootstrap3基础 btn-xs/sm... 按钮的四种大小
内容 参数 OS Windows 10 x64 browser Firefox 65.0.2 framework Bootstrap 3.3.7 editor ...
- Django框架(六) Django之模板继承
模版导入和继承 模版导入 一个页面只能继承一个模板,如何解决了?如何使用多个模板,或者引入其他页面 <% include "a.html" %> 可以引用多次 模板,i ...
- 彻底地/ 终于地, 解决 关于apache 权限的问题了:: 修改 DocumentRoot后的 403错误: have no permission to access / on this server
目录的权限都 应该设置 为 drwxr_xr_x, 即755, 而html下的文件的权限设置为; 644 即可! -x 只有目标文件对某些用户是可执行的或该目标文件是目录时才追加x 属性. -w权限, ...
- 牛客竞赛&&mjt的毒瘤赛
题目链接 https://ac.nowcoder.com/acm/contest/368/F 思路 询问可以离线. 然后每个节点上建32个权值线段树(权值不大,其实只要20颗) 记录每一位权值为x(如 ...
- LOJ#2444. 「NOI2011」阿狸的打字机
题目描述 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有 \(28\) 个按键,分别印有 \(26\) 个小写英文字母和 B . P 两个字母. 经阿狸研究发现,这个打字机是 ...