简单实现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. ...
随机推荐
- django模板常用过滤器—add、cut、date
语法格式:{{ obj | filter:para }} add过滤器:将两个数相加或字符串.列表等进行拼接 views.py def add(request): context={'l1':[1 ...
- 使用 select 实现 goroutine 超时
虽然携程是Go语言中一个新的概念,嗯,但它本质上依然是属于多线程.超时机制是多线程中是一个非常重要的保障程序的鲁棒性的一个措施:错误是很难预估的,在多线程中更为显著,更容易出现难以预料的错误. 一个异 ...
- 复制MIFARE Classic卡
Mifare Classic 1K智能卡介绍及nfc-tools的使用 [原创]RFID安全之——ACR122U菜鸟初体验-『智能设备』-看雪安全论坛 复制MIFARE Classic小区门禁卡记录 ...
- 关于centos7字体缺失导致项目验证码丢失报错500问题
这个问题是这样的,迁移架构的时候项目验证码刷不出来, 页面报错500, 就像下面那样. tomcat报错是数组越界, 看下面 最诡异的是, 开发那边再三确定代码里面没有问题, 于是我试了一下把war包 ...
- oracle基础——内存管理、优化
内存图解: 自动管理:11g:AMM 10g:ASMM SGA(system global area):由所有服务进程和后台进程共享 PGA(program global area): 由每个服务 ...
- MFC中的CString类使用方法指南
MFC中的CString类使用方法指南 原文出处:codeproject:CString Management [禾路:这是一篇比较老的资料了,但是对于MFC的程序设计很有帮助.我们在MFC中使用字符 ...
- 20145212 罗天晨 《网络对抗》Exp3 Advanced 恶意代码伪装技术实践
恶意代码伪装技术实践 木马化正常软件. 啊哈--原本以为很复杂--然后我看了一下蔡野同学的博客,发现原理竟然如此简单-- 对原先生成病毒的代码稍作修改: 于是--把生成的后门软件改成骗人的名字:这里改 ...
- 你不知道的JavaScript(1)LHS查询和RHS查询
打算把<你不知道的JavaScript>中的知识点整理下,写点自己的心得,同时也敦促自己看书. 先做个整体的介绍,最后会再给个综合的例子. RHS 查询与简单地查找某个变量的值别无二致,而 ...
- bzoj 2434 阿狸的打字机 - Aho-Corasick自动机 - 树状数组
题目传送门 传送站I 传送站II 题目大意 阿狸有一个打字机,它有3种键: 向缓冲区追加小写字母 P:打印当前缓冲区(缓冲区不变) B:删除缓冲区中最后一个字符 然后多次询问第$x$个被打印出来的串在 ...
- Codeforces 837E Vasya's Function - 数论
Vasya is studying number theory. He has denoted a function f(a, b) such that: f(a, 0) = 0; f(a, b) = ...