简单实现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. ...
随机推荐
- Linux centos7下php安装cphalcon扩展的方法
说明: 操作系统:CentOS7 php安装目录:/usr/local/php php.ini配置文件路径:/usr/local/php/etc/php.ini 运行环境:LNMP ,PHP7 .安装 ...
- DevExpress GridControl使用方法总结2
一.如何解决单击记录整行选中的问题 View->OptionsBehavior->EditorShowMode 设置为:Click 二.如何新增一条记录 (1).gridView.AddN ...
- 06: dorpzone上传下载
1.1 dropzone上传下载使用 参考博客:https://www.cnblogs.com/fu-yong/p/9053515.html 1.使用dropzone需要引入文件 <link r ...
- k8s tensorflow
Online learning github source Kubeflow实战系列 Prepare 了解ksonnet初探Google之Kubeflow (采用的是0.8.0)install dep ...
- py4CV例子2汽车检测和svm算法
1.什么是汽车检测数据集: ) pos, neg = , ) matcher = cv2.FlannBasedMatcher(flann_params, {}) bow_kmeans_trainer ...
- JS深拷贝/深克隆(面试用)
晒下我的比较浅显的深拷贝,没有考虑原型和循环引用,可以拷贝一些js原生类型,用于面试用. function deepClone(obj){ if(Object.prototype.toString.c ...
- Lucas定理学习笔记
从这里开始 一个有趣的问题 扩展Lucas算法 一个有趣的问题 题目大意 给定$n, m, p$,求$C_{n}^{m}$除以$p$后的余数. Subtask#1 $0\leqslant m\leq ...
- bzoj 1614 Telephone Lines架设电话线 - 二分答案 - 最短路
Description Farmer John打算将电话线引到自己的农场,但电信公司并不打算为他提供免费服务.于是,FJ必须为此向电信公司支付一定的费用. FJ的农场周围分布着N(1 <= N ...
- Codeforces Round #425 (Div. 2) Problem C Strange Radiation (Codeforces 832C) - 二分答案 - 数论
n people are standing on a coordinate axis in points with positive integer coordinates strictly less ...
- tomcat8.5之后版本,远程无法登录管理页面
转载自http://jingyan.baidu.com/article/1612d500b56fa1e20e1eeed2.html 服务器采用的是linux系统. 安装tomcat在服务器上后,客户端 ...