在教主的带领下,完成了一个很简单的RMI工具。就当是练习网络编程吧。
RMI(Remote Method Invocation),远程方法调用。
涉及两个网络端。其核心思想是,一个端可以通过调用另一个端的方法,实现相关功能。
一个端“执行”一个方法,而这个方法的实际执行是在另一端进行的!
要进行远程方法调用,那么,两个端都应该有相同的类,相同的方法。一个端执行一个方法,其实本质是通过调用这个类的代理对象的方法,在其中拦截这个方法,将这个方法的哈希值和参数,通过网络通讯传输给另一端;另一端根据哈希值和参数,能唯一的确定到那个方法,执行完成后将结果返回给对端。
我采用的是短连接,jdk代理。
大致的处理流程:
我的思路:
建立RPC服务器端:
主要有两个大的步骤:
1、需要给RpcBeanFactory注入方法的哈希值和方法参数(这两个数据相同,就能保证客户机准确的调用远程方法)。
注入的具体分析:
static void doRegist(RpcBeanFactory rpcBeanFactory, Class<?> interfaces, Object object) {
Method[] methods = interfaces.getDeclaredMethods();
for (Method method : methods) {
String rpcBeanId = String.valueOf(method.toString().hashCode());
RpcBeanDefination rpcBeanDefination = new RpcBeanDefination();
rpcBeanDefination.setKlass(interfaces);
rpcBeanDefination.setMethod(method);
rpcBeanDefination.setObject(object);
rpcBeanFactory.AddRpcBean(rpcBeanId, rpcBeanDefination);
}
}
通过传过来的接口,得到这个接口中的所有方法,遍历,把每个方法的信息都注入到RpcBeanFactory的Map中。
2、RpcServer需要一直侦听客户机的请求,如果有请求,那么RpcServerExecutor进行具体的处理。
分析:
@Override
public void run() {
try {
// 接收Rpc客户端传递的rpcBeanId和参数
String rpcBeanId = ois.readUTF();
Object[] parameters = (Object[]) ois.readObject();
showParameters(parameters);
// 定位相关类,对象和方法
RpcBeanDefination rpcBeanDefination;
rpcBeanDefination = rpcServer.getRpcBeanFactory().getRpcBean(rpcBeanId);
// 执行Rpc客户端要求执行的方法
Method method = rpcBeanDefination.getMethod();
Object object = rpcBeanDefination.getObject();
Object result = method.invoke(object, parameters);
// 向客户端返回执行结果
oos.writeObject(result);
} catch (IOException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
closeSocket();
}
}
这是RpcServerExecutor中的处理,接收->定位->执行->返回结果。
建立RPC客户机端:
1、在代理对象的方法中,调用RpcClientExecutor。
/*jdk代理*/
public Object getProxy(Class<?> klass) {
// 判断klass是否是接口,若不是,则应该采用cglib代理模式
return Proxy.newProxyInstance(klass.getClassLoader(), new Class<?>[] { klass }, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String rpcBeanId = String.valueOf(method.toString().hashCode());
Object result = rpcClientExecutor.rpcExecutor(rpcBeanId, args);
return result;
}
});
}
给方法生成哈希值,调用rpcExecutor。
2、执行RpcClientExecutor。
<T> T rpcExecutor(String rpcBeanId, Object[] para) throws IOException, ClassNotFoundException {
Socket socket = new Socket(rpcServerIp, rpcServerPort);
ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
// 给RPC服务器发送rpcBeanId和参数
oos.writeUTF(rpcBeanId);
oos.writeObject(para);
// 接收RPC服务器执行的结果,并返回
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
Object result = ois.readObject();
closeSocket(ois, oos, socket);
return (T) result;
}
客户端处理很简单:发送信息->接收信息。
源码链接:
https://github.com/yangchaoy259189888/A-simple-implementation-of-RMI/