目录
概述
- RPC(Remote Procedure Call Protocol)远程过程调用协议,通过网络从远程计算机上请求调用某种服务。它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。
- RMI(Remote Method Invocation)远程方法调用。能够让在客户端Java虚拟机上的对象像调用本地对象一样调用服务端Java虚拟机中的对象上的方法。
它们的区别是,RPC是一种网络传输协议,不支持对象传输;而RMI基于java,支持对象传输,RMI可以看作是java版的RPC实现。
从上述来看,RMI与RPC的设计思想基本一致,都是远程连接服务器去请求某种服务。下面博主将对RMI框架的实现进行详细的讲解。
RMI框架
网上有很多的RMI框架都是基于对象序列化的,而博主将通过JSON技术来实现RMI框架;在客户端只用保存调用方法的接口,而不保存接口实现类,通过代理机制得到接口的实现类对象,在方法拦截中进行远程服务器的连接、方法及参数的传递和返回结果的接收,并在结果接收后关闭与服务器的连接。在服务器端保存接口和接口实现类,并通过注解将实现类对象和方法注册到容器中,当客户端调用方法时,找到目标方法后将方法执行的结果发送回客户端。
注册
服务端接口实现类和方法的注册是实现RMI的前提条件,注册的方式可以是注解或通过文件配置,本框架采用注解的方式。在服务端开启前要先通过包扫描(可见包扫描工具)找到带有指定注解的类,注解中是接口类型数组,将数组中每个接口含有的方法依次进行扫描并找到实现类对应的方法,形成一个MethodDefinition,再以接口完整方法名的hashCode作为键形成key-value保存在map中,以供RPCServer使用。
由于本框架在客户端保存的仅仅是含有方法的接口,因此当客户端将接口方法发送给服务端后,服务端要通过发送来的方法的方法名、参数个数、参数类型等来找到保存在服务端的接口实现类的方法,这个过程是非常麻烦且耗时的;博主的解决办法是事先通过包扫描将接口中的方法和接口实现类中的方法相互映射,形成map,这样客户端只用将接口方法的哈希值发送过来,就可以通过哈希值直接找到对应的方法,为远程调用节省了时间。
MethodFactory
package com.dl.server.core;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import com.parser_reflect.util.PackageScanner;
/**
* RPC方法扫描类<br>
* 1、本类是RPC的核心类,对指定的类中的指定接口进行扫描;<br>
* 2、将扫描到的方法缓存到methodPool中;<br>
* 3、map以接口方法的哈希值为键,以MethodDefinition为值;<br>
* 4、提供手动注册的方法,支持通过哈希值获取Definition的手段;
*
* @author dl
*
*/
public class MethodFactory {
private static final Map<Integer, MethodDefinition> methodPool;
static {
methodPool = new HashMap<>();
}
public MethodFactory() {
}
/**
* 包扫描,找到指定注解的类;
*
* @param path
*/
public static void scanPackage(String path) {
new PackageScanner() {
@Override
public void dealClass(Class<?> klass) {
if (klass.getAnnotation(ProxyAnntotation.class) == null) {
return;
}
priRegistry(klass);
}
}.packageScanner(path);
}
/**
* 遍历类中指定接口的方法,形成map。
*
* @param klass
*/
private static void priRegistry(Class<?> klass) {
Object object = null;
try {
object = klass.newInstance();
} catch (InstantiationException e1) {
e1.printStackTrace();
} catch (IllegalAccessException e1) {
e1.printStackTrace();
}
ProxyAnntotation proxy = klass.getAnnotation(ProxyAnntotation.class);
Class<?>[] interfaces = proxy.interfaces();
// 遍历注解中的接口数组
for (Class<?> clazz : interfaces) {
Method[] methods = clazz.getMethods();
for (Method method : methods) {
try {
// method.getParameterTypes() 可以得到泛型参数类型
Method meth = klass.getDeclaredMethod(method.getName(), method.getParameterTypes());
if (meth != null) {
MethodDefinition definition = new MethodDefinition(klass, object, meth);
// 形成以hashCode为键, MethodDefinition为值的key-value
methodPool.put(method.toString().hashCode(), definition);
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
}
}
}
}
/**
* 提供显示的手动注册手段;
*
* @param klass
*/
public void registry(Class<?> klass) {
if (klass.getAnnotation(ProxyAnntotation.class) == null) {
return;
}
priRegistry(klass);
}
/**
* 通过接口方法的hashCode来获取相应的MethodDefinition;
*
* @param rpcMethodId
* @return
*/
public static MethodDefinition getMethodDefinition(int rpcMethodId) {
return methodPool.get(rpcMethodId);
}
}
MethodDefinition
package com.dl.server.core;
import java.lang.reflect.Method;
/**
* 保存方法信息的类<br>
* 将扫描到的方法和对应的类和对象保存;
*
* @author dl
*
*/
public class MethodDefinition {
private Class<?> klass;
private Object object;
private Method method;
public MethodDefinition() {
}
public MethodDefinition(Class<?> klass, Object object, Method method) {
this.klass = klass;
this.object = object;
this.method = method;
}
public Class<?> getKlass() {
return klass;
}
public void setKlass(Cla