一、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:
1 package com.xupt.rpc.core;
2
3 import java.lang.reflect.Method;
4
5 public class RpcBeanDefination { //将类,类方法,类对象封装在Definition中。
6
7 private Class<?> klass;
8 private Method method;
9 private Object object;
10
11 RpcBeanDefination() {
12 }
给该类所有的成员都有getter和setter方法就不需要说了,这个类,将执行的哪个类的哪个方法和类的对象封装起来,以后这个类将形成Map
的值,下面来介绍的RpcBeanFactory会重点介绍。
2.RpcBeanFactory
1 package com.xupt.rpc.core;
2
3 import java.util.HashMap;
4 import java.util.Map;
5
6 public class RpcBeanFactory {
7
8 private final Map<String, RpcBeanDefination> beanMap;
9
10 RpcBeanFactory() {
11 beanMap = new HashMap<>();
12 }
13
14 void rpcBeanRegistry(String beanId,RpcBeanDefination defination) {
15 RpcBeanDefination rbd = beanMap.get(beanId);
16 if(rbd != null) {
17 return;
18 }
19 beanMap.put(beanId, defination);
20 }
21
22 RpcBeanDefination getBean(String beanId) {
23 return beanMap.get(beanId);
24 }
25 }
此类是将序列号作为Map中的键,RpcBeanDeifintion作为值放入Map中,用BeanId来找对应客户端那边序列号相同的方法。
3.下来是RpcBeanRegistry:
1 package com.xupt.rpc.core;
2
3 import java.lang.reflect.Method;
4
5 public class RpcBeanRegistry {
6
7 RpcBeanRegistry() {
8 }
9
10 //给客户端提供
11 static void registryInterface(RpcBeanFactory rpcBeanFactory,Class<?> interfaces) {
12 doregistry(rpcBeanFactory,interfaces,null);
13 }
14
15 //内部使用,注册
16 private static void doregistry(RpcBeanFactory rpcBeanFactory , Class<?> interfaces ,Object object) {
17 //得到接口中的所有的方法,行程方法的数组
18 Method[] methods = interfaces.getDeclaredMethods();
19 for(Method method : methods) { //遍历这些方法
20 String beanId = String.valueOf(method.toString().hashCode());//将方法序列化。
21 RpcBeanDefination rpcBeanDefination = new RpcBeanDefination();
22
23 //将得到的实现接口的那个类和它的方法以及对象放进RpcBeanDefination()中。
24 rpcBeanDefination.setKlass(interfaces);
25 rpcBeanDefination.setMethod(method);
26 rpcBeanDefination.setObject(object);
27
28 rpcBeanFactory.rpcBeanRegistry(beanId, rpcBeanDefination);
29 }
30 }
31
32 //服务端使用,知道实现类的对象。
33 static void registryInterface(RpcBeanFactory rpcBeanFactory,Class<?> interfaces,Object object) {
34 //判断此类是否实现了这个接口。
35 if(!interfaces.isAssignableFrom(object.getClass())){
36 return;
37 }
38 doregistry(rpcBeanFactory,interfaces,object);
39 }
40
41 //服务器端使用,知道类,创建一个对象。
42 static void registryInterface(RpcBeanFactory rpcBeanFactory,Class<?> interfaces,Class<?> klass) {
43 //判断该类是否实现了接口。
44 if(!interfaces.isAssignableFrom(klass)){
45 return;
46 }
47 try {
48 doregistry(rpcBeanFactory, interfaces, klass.newInstance());
49 } catch (Exception e) {
50 e.printStackTrace();
51 }
52 }
53 }
这个类是同时给客户端和服务器使用的,所以有一个私有方法来完成类方法的获得和序列号的产生(method.toString().hashcode())。然后将类、方法、对象放进RpcBeanDefinition中,将得到的序列号作为键和rpcBeanDeifinyion作为值放进Map中,形成键值对,方便客户端和服务器的调用。
4.下来是RpcServer:
1 package com.xupt.rpc.core;
2
3 import java.io.IOException;
4 import java.net.ServerSocket;
5 import java.net.Socket;
6
7 public class RpcServer implements Runnable {
8
9 private ServerSocket server;
10 private int port;
11 private boolean goon;
12 private final RpcBeanFactory rpcBeanFactory;
13 private static long executorId;
14
15 public RpcServer() {
16 rpcBeanFactory = new RpcBeanFactory();
17 this.goon = false;
18 }
19
20 public void setPort(int port) {
21 this.port = port;
22 }
23
24 public void startRpcServer() throws Exception {
25 if(this.port == 0) {
26 return;
27 }
28 server = new ServerSocket(port);
29 this.goon = true;
30 new Thread(this,"Rpc_Server").start();//启动线程
31 }
32
33 public void stopRpcServer() { //关闭服务器
34 if (this.server != null && !this.server.isClosed()) {
35 try {
36 this.server.close();
37 } catch (IOException e) {
38 e.printStackTrace();
39 } finally {
40 this.server = null;
41 }
42 }
43 }
44
45 RpcBeanFactory getRpcBeanFactory() {
46 return rpcBeanFactory;
47 }
48
49 //用注册的方法知道类实现的接口,类的对象,通过对象执行类的方法。
50 public void rpcRegistry(Class<?> interfaces,Object object) {
51 RpcBeanRegistry.registryInterface(rpcBeanFactory, interfaces, object);
52 }
53
54 public void rpcRegistry(Class<?> interfaces, Class<?> imClass) {
55 RpcBeanRegistry.registryInterface(rpcBeanFactory, interfaces,imClass);
56 }
57
58 @Override
59 public void run() {
60 while(goon) {
61 try {
62 Socket socket = server.accept();//不断的侦听
63
64 new RpcServerExecutor(socket, this,++executorId);
65
66 } catch (Exception e) {
67 goon = false;
68 e.printStackTrace();
69 }
70 }
71 stopRpcServer();
72 }
73 }
上述服务器端的基本任务已经完成了。
下来我们来看看客户端:
首先还是: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:这个类是产生代理,用代理对象伪执行相关的方法,然后在真正的来连接服务器。
1 package com.xupt.rpc.core;
2
3 import java.lang.reflect.InvocationHandler;
4 import java.lang.reflect.Method;
5 import java.lang.reflect.Proxy;
6
7 public class RpcClient {
8
9 private RpcClientExecutor rpcClientExecutor;
10
11 public RpcClient(String rpcServerIp,int rpcServerport) {
12 this.rpcClientExecutor = new RpcClientExecutor(rpcServerIp, rpcServerport);
13 }
14
15 @SuppressWarnings("unchecked")
16 public <T> T getProxy(Class<?> klass) {
17 return (T) Proxy.newProxyInstance(
18 klass.getClassLoader(),
19 new Class[] {klass},
20 new InvocationHandler() {
21
22 @Override
23 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
24 String BeanId = String.valueOf(method.toString().hashCode());
25 Object result = rpcClientExecutor.rpExecutor(BeanId, args);
26
27 return result;
28 }
29 }
30 );
31 }
32 }
上述方法产生代理就不多做解释,不明白请看上一篇的代理机制之初见吧。。。。
以上就是我的RMI的简单实现,入如果有错误,请指正!也希望它对你有所帮助。