RPC是什么,先摘取一段解释:
RPC全称为远程过程调用(Remote Procedure Call),它是一种计算机通信协议,允许一个计算机程序调用另一个计算机上的子程序,而无需了解底层网络细节。通过RPC,一个计算机程序可以像调用本地程序一样调用远程程序,使得分布式应用程序的开发更加简单和高效。
根据对应的接口定义去找到对应实际提供服务的方法去调用,需要的必要信息,要调用的服务的地址,要调用的类,方法,方法参数类型,参数值,怎么接收返回值,多个服务提供者的话需要负载均衡
RPC 框架很多,找一个比较纯粹的框架来分析下是怎么实现的,自己实现的话,也可以照葫芦画瓢,有哪些是必要的,当然同样的效果可以有不同的实现,自己也可以针对一些特性进行替换。
xxl-rpc实现原理解析
怎么对外提供服务,考虑到性能原因,采用的netty来实现的服务
要调用的地址,这里可以直接指定,可以通过本地或者注册中心获取到提供的服务列表进行负载
类,方法,方法参数类型,参数值,为什么需要这些,因为这些才可以唯一确定一个调用的方法,具体是调用的哪个
返回值,是否需要关心返回值,是否需要异步,异步的话是通过回调还是future,怎么进行实现的
xxl-rpc对于代理的生成
针对需要依赖的接口生成对应代理的,代理是什么时候生成的,这里还是先看spring环境中的情况吧
通过spring的生命周期管理,实现了InstantiationAwareBeanPostProcessor来对对象初始化之后对对象的属性进行处理,看是不是有依赖rpc的接口,有依赖的对其进行属性注入对应生成的代理类
public class XxlRpcSpringInvokerFactory implements InitializingBean, DisposableBean, BeanFactoryAware, InstantiationAwareBeanPostProcessor {
}
�怎么生成对应的代理类,包装成了什么样子的数据
采用的JDK的动态代理,有对应的版本号区分,然后类名,方法,方法参数类型,参数值,地址可以支持负载,那么要传递给提供服务方的数据是什么样子呢,就是这样子的
public class XxlRpcRequest implements Serializable{
private static final long serialVersionUID = 42L;
private String requestId;
private long createMillisTime;
private String accessToken;
private String className;
private String methodName;
private Class<?>[] parameterTypes;
private Object[] parameters;
private String version;
}
只要有这些参数就可以确定服务提供方需要执行的方法,进行执行,
是否关心返回值,xxl-rpc提供了四种选择,同步调用,异步调用返回future,异步设置callback,oneway不关心返回值
底层实现这几种的方式,首先oneway不关心返回值,不需要多考虑,rpc内部都是直接异步发送,那么怎么接受返回值呢,采用的回调的方法,就是clent自己也起一个服务,然后当服务端执行完成之后,进行远程调用回来结果,怎么确定是哪次调用的呢,上面封装参数的requestId就派上用场了,采用uuid生成的方式,唯一标志,回调之后有等待结果的,就对其进行唤醒,需要执行回调方法的就进行执行回调
具体实现上,通过XxlRpcFutureResponse 来封装返回值,里面记录了request,然后根据requestId,把对应的resonse进行存放到一个公共集合,这里用的一个concurrentHashMap里面,然后回调的时候从里面通过对应的requestId取对应的resposne对象,把返回值记录上,状态修改,
XxlRpcInvokeFuture invokeFuture = new XxlRpcInvokeFuture(futureResponse);
// future存入到了threadLocal里面
XxlRpcInvokeFuture.setFuture(invokeFuture);
�返回的对象也通过工具类直接放到对应的ThreadLocal里面,可以获取到对应的futureResponse,然后可以get等待返回状态的改变,未返回的时候加锁等待,等返回之后通过唤醒锁,可以设置超时时间,这样一个基础的rpc功能就齐了
总结
提供服务可以采用netty或者别的也可以,然后服务暴露就需要自己完成去暴露了,这里没主动去解析,主要考虑的调用端,调用时候根据对应的注解,去生成代理对象进行注入,地址可以提供负载,然后进行调用,返回值通过回调的方式,回调之后通过改变对应的对象的状态,对其进行唤醒,就可以取到对应的返回值,一次rpc调用完成