总体架构
- rpc-api:客户端和服务端的公共调用接口
- roc-common: 项目中通用的一些枚举类和工具类
- rpc-core: 框架的核心实现
- test-client: 测试用的客户端项目
- test-server: 测试用的服务端项目
一个简单的实现版
RPC框架的原理:
服务端和客户端都可以访问到通用的接口,只有服务端中有这个接口的实现类,客户端通过网络传输的方式调用这个接口,告诉服务端我需要这个接口,服务端收到请求后找到这个实现类并执行,将生成的结果给客户端调用的接口的返回值。
需要思考的问题:
- 客户端怎么知道服务端的地址
- 客户端怎么告诉服务端要调用的接口
- 客户端怎么传递参数
- 客户端只有接口怎么生成实现类
假设客户端已知服务端的地址,完成一个最简单的实现
通用接口
hello方法需要传递一个对象HelloObject
定义如下
注意,Hello Object这个对象要实现Serializable这个序列化接口, ,因为调用的过程中要从客户端发送给服务端
在服务端实现这个接口的实现类
简单的实现,返回一个message字符串
传输格式
服务端需要哪些信息,才能唯一确定服务端要调用的接口的方法呢
- 接口名字
- 方法名字
- 重载原因:需要方法的所有参数类型
- 客户端调用方法时的实际参数值
当服务端知道这4个条件,就可以找到方法并进行调用了
我们将这4个条件写进一个对象RpcRequest中,到时候传输时传输这个对象就够了
当服务端调用发放成功后,需要给客户端返回哪些信息
- 调用成功:需要返回值
- 调用失败: 失败的信息
客户端的实现:动态代理
因为客户端并没有接口的具体实现类,没有办法直接生成实例对象。可以动态代理的方式生成实例,并生成RpcRequest对象发送给服务端
使用JDK动态代理,需要实现 InvocationHandler 接口
传递的host和post指明服务端的位置,getProxy方法生成代理对象
实现InvocationHandler需要实现invoke()方法,指明代理对象方法被调用时的动作,这里我们应该生成RpcRequest对象并发送出去,然后返回从服务端接收的结果即可
RpcClient对象作用:将一个对象发送出去,并接收返回的对象
使用java序列化,通过socket进行传输。创建一个socket获取Object OutputStream 对象,然后将要发送的对象发送出去即可,接收时获取ObjectInputStream对象.readObject方法可以获得一个对象
服务端的实现:反射调用
使用ServerSocket监听某个端口,循环接收连接请求,如果新来一个来连接就新建一个线程处理调用,创建线程采用线程池方法
其中,通过class.getMethod方法,传入方法名和方法参数类型即可获得Method对象。如果你上面RpcRequest中使用String数组来存储方法参数类型的话,这里你就需要通过反射生成对应的Class数组了。通过method.invoke方法,传入对象实例和参数,即可调用并且获得返回值。