远程调用
请求-应答协议
- 在TCP协议之上,通过数据报API中发送和接收操作来描述。请求-应答协议基于三个通信原语:doOperation、getRequest、sendReply,该协议可将服务器的应答消息作为客户端请求消息的确认。
- 请求-应答协议消息结构
messageType — int(0 请求 1 应答)
requestId — int 消息唯一标识符 由整数序列 发送进程IP 端口号 组成
remoteReference — 远程教育对象引用
operationId — 整数或方法
arguments — 字节数组 - 故障模型
超时、丢弃重复的请求消息、丢失应答消息、历史(包含已发送的(应答)消息记录的结构,当客户端要求重传应答消息是,服务器不需要重新执行,是直接从内存中传送历史消息) - 协议变体
请求协议、请求-应答协议、请求-应答-确认应答协议
注:http传输协议属于请求-应答协议
远程过程调用 RPC
RPC的特点
- 面向接口编程,接口和实现解偶。接口定义语言(IDL):是一种接口定义得规范,允许以不同语言实现过程以便互相调用。
- RPC调用语义
调用语义 | 重发请求消息 | 过滤重复请求 | 重新执行过程/重传应答 | 故障类型 |
---|---|---|---|---|
或许(可能执行一次或者不执行) | 否 | 不适用 | 不适用 | 遗漏故障、系统崩溃 |
至少一次 | 是 | 否 | 重新执行过程 | 由于包含远程对象服务器故障而引起的系统故障,(非幂等操作引起的)随机故障 |
至多一次 | 是 | 是 | 重传应答 | 可以通过容错避免 |
- 透明性
远程过程调用和本地过程调用在语法上没有差别,所有对编码和消息传递过程的必要调用都对调用方隐藏,对调用者而言是透明的。
远程调用的延迟比本地调用大好几个数量级,尽量减少无必要的远程调用,中止花费很长但对服务器无用的远程调用过程。
RPC不支持引用调用。
RPC的实现
客户端包含客户存根过程。该存根过程把过程标识符和参数编码成一个请求消息,并通过通信模块发送给服务器端。在收到应答消息后,该存根过程可对结果进行解码。对于客户端,该存根过程使得远程调用可以当作本地调用。
服务器端包括分发器程序、服务器存根过程和服务过程。
分发器 :根据请求消息中的过程标识符,选择一个服务器存根过程。
服务器存根过程:对请求消息的参数解码,然后调用相应的服务过程,并把返回值编码成应答消息。
服务过程:服务接口中过程的具体实现。
远程方法调用 RMI
与 RPC的区别:RMI支持对象应用作为参数传递。远程一旦接收对象引用,就能够使用远程调用方法访问该对象,而不是通过网络传输对象。
分布式对象
不同进程中的对象之间的方法调用都被认为是远程方法调用,在同一进程中的对象间的方法调用称为本地方法调用。
远程对象:能够接收远程调用的对象。上图中对象B和对象D是远程对象。
所有对象都能接收本地调用,当然调用方必须拥有被调用方的引用。
远程接口:每个远程对象上必须有远程接口。其他进程只能访问远程对象的远程进口,本地进程才能访问对象的其他方法。Java RMI中,远程对象通过扩展Remote接口,实现定义远程接口。
远程对象引用:可以用于整个分布式系统的 标识符,它指向某个唯一的远程对象。远程对象调用可以作为远程方法的结果返回,例如,对象A可以从对象B处获取到远程对象F的远程对象引用。
远程对象引用和本地对象引用的差别。
互联网地址 | 端口号 | 时间 | 对象编号 | 远程对象接口 |
---|---|---|---|---|
32bit | 32bit | 32bit | 32bit |
分布式对象系统中的动作:一个动作由方法调用启动,相关调用的对象可能处于不同进程或者计算机中。在跨域的时候 ,就要使用到RMI。
分布式对象系统中的无用单元收集(垃圾回收):有本地无用单元收集器和分布式无用单元收集模块(用于引用计数)协作完成。
RMI实现
通信模块:双方通信模块协作执行请求-应答协议,使用协议消息中 消息类型、requestId和被调用对象的远程引用。执行给定的调用语义。服务器通信模块为被调用的对象类 B 选择 分发器,从远程引用模块中传输其本地引用,为其替换 被调用对象的远程引用。
远程引用模块:远程引用模块负责在本地对象引用和远程对象引用间翻译(使用远程对象表,记录两者的对应关系),并负责创建远程对象引用。
- 服务器端远程引用模块的远程对象表包含所有的远程对象(远程对象B);
- 客户端远程引用模块的远程对象表包含本地代理(B的代理);
- 当请求/应答消息到达时,由RMI软件的组建调用远程引用模块,该模块需要提供相应的远程对象/本地引用。如果没有,就需要RMI软件创建。
伺服器:提供了远程对象主体的类的实例,存活于服务器的进程中。当远程对象B被实例化时,就会产生一个 伺服器,用来处理相应的骨架传来的远程请求。伺服器最终由无用单元被回收或删除。
RMI软件:由以上模块的软件层组成。可以实现以下功能:
- 代理:每个远程对象都有一个代理类,实现远程对象的远程接口定义方法,代理中的每个方法会将目标对象的引用、自身的methodId和参数 编码成一个请求消息,并发送到目标,等待应答后解码,将结果返回给调用者。
- 分发器:用于接收来自通信模块的请求消息,使用消息中methodId选择骨架中恰当的方法。
- 骨架:位于远程对象类中,将请求消息中的参数解码,并调用伺服器中的相应方法。调用完成后 ,将结果和异常信息编码,组成应答消息,传送回请求方。
绑定程序:在分布式系统中,绑定程序是单独的服务,该服务维护一张包含从文本名字到程序对象引用的映射的表。服务器用该表按名字注册远程对象;客户端通过该表查找远程对象。
服务器线程:为了避免不同远程调用之间相互影响,服务器会为每个远程调用的执行分配一个独立现场。
远程对象的激活器:用于启动服务器中驻留的远程对象的进程。激活是指当远程对象需要被调用时,从被动对象创建一个主动对象。其中被动对象包含对象的方法的实现和激活编码状态。激活的具体方法是创建被动对象类的新实例,并根据存储状态初始化它的实例变量。
持久对象存储:在进程两次激活之间仍能保持存活的对象被称为持久对象。持久对象存储进程在磁盘或者数据库中以编码格式存储持久对象的状态。主存中不再需要的持久对象就会被置为被动状态。
判断对象是否为持久的方法:
- 持久对象存储维护了持久根,任何可以通过持久根访问到的对象都被定义为持久的。
- 持久对象存储提供一些持久类,供持久对象继承。
对象定位:由于远程对象在其整个生命周期里会存在于不同的进程(或计算机)中。在这种情况下,远程对象引用不能当做远程对象的地址,还需要一个调用发送到的地址。定位服务使用数据库,将远程对象引用映射到远程对象当前的大概位置(存在延迟)。帮助客户根据远程对象引用定位远程对象。
分布式无用单元收集
分布式无用单元收集器和本地无用单元收集器协作,共同维护服务器中具有远程对象B代理的客户进程的集合B.holders。
- 每个服务器进程为它的每个远程对象维护拥有远程对象引用的一组客户端进程名,如远程对象B的B.holders。
- 当客户C第一次接收到远程对象B的远程引用时,它发出一个addRef(B)调用到远程对象的服务器并创建一个代理,服务器将c添加到B.holders。
- 当客户C的无用单元收集器注意到远程对象B的一个代理不可达时,会给服务器发送removeRef(B),通知服务器从B.holders中删除C,客户端本地删除代理。
- 如果不存在B的本地持有者,B.holders为空时,服务器的本地无用单元收集器将回收B的空间。