http://my.oschina.net/xianggao/blog/634968
简介
RPC功能目标:让构建分布式计算(应用)更容易,在提供强大的远程调用能力时不损失本地调用的语义简洁性。
故,RPC框架需提供一种透明调用机制让使用者不必显示的区分本地调用和远程调用。
同步调用:客户方等待调用执行完成并返回结果。
异步调用:客户方调用后不用等待执行结果返回,但仍然可以通过回调通知等方式获取返回结果。若客户方不关心调用返回结果,则变成单向异步调用,单向调用不用返回结果。
异步、同步区别在于是否等待服务器端执行完成并返回结果。
RPC服务通过RpcServer【负责导出远程接口】去导出远程接口方法,而客户方通过RpcClient【负责导入远程接口的代理实现】去引入远程接口方法。客户方像调用本地方法一样去调用远程接口方法,RPC框架提供接口的代理实现,实际的调用将委托给代理RpcProxy【远程接口的代理实现】。代理封装调用信息并将调用转交给RpcInvoker【客户方实现:负责编码调用信息和发送调用请求到服务方并等待调用结果返回;服务方实现:负责调用服务端接口的具体实现并返回调用结果】去实际执行。在客户端的RpcInvoker通过连接器RpcConnector【负责维持客户方和服务方的连接通道和发送数据到服务方】去维持与服务端的通道RpcChannel【数据传输通道】,并使用RpcProtocol【负责协议编码/解码】执行协议编码并将编码后的请求消息通过通道发送给服务方。
RPC服务端接收器RcpAcceptor接收客户端的调用请求,同样使用RcpProtocal执行协议解码。解码后的调用信息传递给RcpProcessor去控制处理调用过程,再委托调用给PcpInvoker去实际执行并返回调用结果。
导入远程接口与客户端代理
导入远程接口与客户端代理
传输服务
协议编码后,自然就是需要将编码后的RPC请求消息传输到服务方,服务方执行后返回结果消息或确认信息给客户方。RPC的应用场景实质是一种可靠的请求应答消息流,和HTTP类似。因此选择长连接方式的TCP协议会更高效,与HTTP不同的是在协议层面我们定义了每个消息的唯一id,因此可以更容易的复用连接。
既然使用长连接,那么第一个问题是到底client和server之间需要多少根连接?实际上单连接和多连接在使用上没有区别,对应数据传输量较小的应用类型,单连接基本足够。单连接和多连接最大的区别在于,每根连接都有自己私有的发送和接收缓冲区,因此大数据量传输时分散在不同的连接缓冲区会得到更好的吞吐效率。所以,如果你的数据传输量不足以让单连接的缓冲区一直处于饱和状态的话,那么使用多连接并不会产生任何明显提升,反而会增加连接管理的开销。
连接是由client端发起建立并维持。如果client和server之间是直连的,那么连接一般不会中断(物理链路故障除外)。
执行调用
异常处理
如何调用他人的远程服务
怎么做到透明化远程服务调用
执行调用
client stub所做的事情仅仅是编码消息并传输给服务方, 而真正的调用过程发生在服务方。server stub我们细分了RpcProcessor 和RpcInvoker(封装了反射调用的实现细节)两个组件,一个负责控制调用过程,一个负责真正调用。
本地调用和RPC调用的一些差异:
- 本地调用一定会执行,而远程调用则不一定,调用消息可能因为网络原因并未发送到服务方;
- 本地调用只会抛出接口声明的异常,而远程调用还会抛出RPC框架运行时的其它异常;
- 本地调用和远程调用的性能可能差距很大,这取决于RPC固有消耗所占的比重。
由于各服务部署在不同机器,服务间的调用免不了网络通信过程。如果有一种方式能让我们像调用本地服务一样调用远程服务,而让调用者对网络通信这些细节透明,那么将大大提高生产力。
要让网络通信细节对使用者透明,我们自然需要对通信细节进行封装。
- 服务消费方(client)调用以本地调用方式调用服务;
- client stub接收到调用后负责将方法、参数等组装成能够进行网络传输的消息体;
- client stub找到服务地址,并将消息发送到客户端;
- server stub收到消息后进行解码;
- server stub根据解码结果调用本地服务;
- 本地服务执行并将结果返回给server stub;
- server stub将返回结果打包成消息并发送给消费方;
- client stub接收到消息,并进行解码;
- 服务消费方得到最终结果。
使用代理封装通信细节让用户像以本地调用方式调用远程服务。Java代理有两种方式:1.jdk动态代理;2.字节码生成。
RPCProxyClient代理类的invoke方法中封装了与远端服务通信的细节,消费方首先从RPCProxyClient获得服务提供方的接口,当执行helloWorldService.sayHello("test")方法时就会调用invoke方法。