远去的尘埃
上个世纪50年代随着计算机的诞生,人类社会进入飞速发展阶段,在血雨腥风间多少架构与模式已被人们遗忘,从最简单的单片机开始,当时人们只会汇编,C之类最原始的编码方式,面向过程的编程的方式复杂而低效,随着面向对象的语言和互联网的兴起,传统的单机程序已成历史往事,逐渐为分布式系统所替代。
下面列举几种常见的分布式调用范型:
- 请求-应答协议描述了一个基于消息传递的范型,该协议支持在客户/服务器计算中遇到的消息双向传输。尤其是此类协议为远程操作的执行请求提供了相关的底层支持,同时提供了对RPC和RMI的直接支持。
- 最早的、可能也是最为人们所熟知的程序员友好模型示例就是将传统的过程调用模型扩展到分布式系统(远程过程调用,或RPC,模型- - 远程过程调用模型允许客户程序透明地调用在服务器程序中的过程,而这些服务器程序运行在不同的进程中,通常位于不同于客户端的计算机中。
- 20世纪90年代,基于对象的编程模型被扩展,允许不同进程运行的对象通过远程方法调用(Remote Method Invocation,RMI)彼此通信。RMI是对本地方法调用的扩展,它允许一个进程对象调用另外一个进程对象的方法。
请求-应答协议
这种通信设计用于支持典型客户/服务器交互中角色和信息的转换。通常情况下,请求-应答通信是同步的,在来自服务器端的应答到达之前客户端进程是阻塞的。它也是可靠的,因为从服务器端的应答是对客户端进程的一个有效的确认,因此也是可靠的。异步的请求一应答通信是可选择的,这种方式可能在客户允许检索应答延迟的情况下是有用的。该协议基于三个通信原语:doOperation、getRequest、aendReply
在设计请求-应答协议时需要考虑如下几个问题:
消息标识符:
如果需要提供类似于可靠消息传递或请求-应答通信等额外特性,那么任何消息管理方案都会要求每一个消息必须有唯一的消息标识符。通过消息标识符才可以引用消息。消息标识符由两部分组成:
1)requeatld,发送进程从一个长度不断增加的整数序列获取requestld;
2)发送若进程的标识符,如它的端口号和互联网地址。
超时
超时发生时,doOpernution方法有多种选择。最简单的选择就是立即返回给客户一个doOper ation发生故障的标示。这不是通常的方法。超时的原因可能是请求或应答消息丢失,对于后者该操作将被执行。为了避免消息丢失的可能性,doOperation方法会重复地发送请求消息直到它收到应答,或已有理由相信延迟是因为服务器未作应答而不是丢失了请求消息。最终,当doOperation方法返回时,会以未接收到结果的异常告诉客户。
丢弃重复的请求消息
当请求消息重复传输时,服务器可能不止一次地接收到该消息。例如,服务器可能接收第一个请求消息但是却花了比客户的超时时限更长的时间执行命令和返回结果。这就导致服务器为同样的请求而不止一次地执行某个操作。为了避免这种情况,该协议设计能识别带有相同请求标识符的连续消息(来自同一客户),并过滤掉重发的消息。如果服务器还没有发送应答消息,它就无须采取特殊行动,在执行完这个操作时传输该应答。
丢失应答消息
如果当服务器收到一个重复的请求消息时已经发送了应答消息,那么除非它保存了原先执行的结果,否则它需要再次执行这个操作来获得该结果。一些服务器会不止一次地执行它们的操作并每次都获得相同的结果。一个系等操作(idempotent opermtion)指的是,它重复执行的效果与它好像仅执行一次的效果相同。例如,向集合中添加一个元素的操作是幕等操作,因为它每次的执行对于集合的效果是一样的。然而,给一个序列添加一个项就不是幂等操作,因为每次它执行都扩展了这个序列。如果一个服务器上的操作都是幂等操作,那么就没有必要去采取特殊措施避免操作的多次执行。
历史
对于要求重新传输应答而不需要重新执行操作的服务器来说,可以使用历史。术语“历史”通常指的是包含已发送的(应答)消息记录的结构。历史的内容包含请求标识符、消息和消息被发送到的客户的标识符。其目的是当客户进程请求服务器时让服务器重新传输应答消息。和历史的使用相关的问题是它的内存开销。如果服务器不能确定何时不再需要重新传输消息,那么历史的内存开销将会变得很大。
交互协议的类型
为了实现多种类型的请求行为,可以使用三种协议,能够在出现通信故障时产生不同的行为。Spector[1982]首先确认了三种协议:
- 请求(R)协议;
- 请求-应答(RR)协议;
- 请求-应答-确认应答(RRA)协议。
远程过程调用
远程过程调用(RPC)的概念代表着分布式计算的重大突破,同时也使分布式编程和传统编程相似,即实现了高级的分布透明性。这种相似性通过将传统的过程调用模型扩展到分布式环境方式实现。尤其是,在RPC中调用远程机器上的程序就像这些程序在本地的地址空间中一样。那么底层RPC系统就隐藏了分布式环境重要的部分,包括对参数和结果的编码和解码、消息传递以及保留过程调用要求的语义。该概念由Birmel和Nelson在1984年首次提出,为许多分布式系统的编程铺平了道路,一直到现在。
RPC的几个特性:
接口编程
大多数现代编程语言提供了把一个程序组织成一系列能被此通信的模块的方法。模块之间的通信可以依靠模块间的过程调用,或者直接访问另外一个模块中的变量来实现。为了控制模块之间可能的交互,必须为每一个模块定义显式的接口,模块接口指定可供其他模块访问的过程和变量。实现后的模块就隐藏了除接口以外的所有信息。只要模块的接口保持相同,模块的实现就可以随意改变面不影响到模块的使用者。
透明性
RPC的创始人Birrell和Nelson[1984]致力于使远程过程调用与本地过程调用尽可能相似,使得本地过程调用和远程过程调用在语法上没有差别。所有对编码和消息传递过程的必要调用都对编写调用的程序员面隐藏起来。尽管请求消息在超时后重新发送,但这对调用者而言也是透明的一一使远程过程调用的语义与本地过程洞用的语义相似。
RPC的实现
- 接口定义语言选择
接口定义语言需要提供用于定义常量、类型预定义(typodef)、结构、枚举类型、联合和程序的表示法。 - 绑定
对于生产者来说需要在众所周知的服务器和端口上提供服务,所以对于生产者来说需要实现服务的发布功能。 - 序列化与反序列化
由于要经过网络对消息进行传输,所以对对象进行序列化,客户端在收到消息之后要进行解码和反序列化成对象对能使用。
同步和异步 - 同步指发出一个请求后是否阻塞并-一直等待结果返回,而异步可以在发送请求后先去执行其他任务,在一段时间后再获取结果或通过注册监听器设置回调。
远程方法调用
远程方法调用(Remote Method Invocation,RMI)和RPC有紧密的联系,只是RMI被扩展到了分布式对象的范畴。在RMl中,访问对象能够调用位于潜在的远程对象上的方法。至于RPC,它底层的细节被隐藏起来不为使用者所知。RMI和RPC的共性如下:
- 它们都支持接口编程,同时能带来使用这种方法的好处。
- 它们都是典型的基于请求-应答协议构造的,并能提供一系列如最少一次、最多一次调用语义。·它们都提供相似程度的透明性——也就是说,本地调用和远程调用采用相同的语法,但远程接口通常暴露了底层调用的分布式本质,例如通过支持远程异常。
下面的不同会在复杂的分布式应用和服务的编程中带来额外的功能。 - 程序员能够在分布式系统软件开发中使用所有的面向对象编程的功能,包括对象、类、继承的使用,以及相关面向对象的设计方法和相关的工具的使用。
- 基于面向对象系统中对象标识的概念,在基于RMl系统中的所有对象都有唯一的对象引用(无论对象是本地还是远程的)。对象引用可以当做参数进行传递,因此RMI比RPC提供了更为丰富的参数传递语义。
在分布式系统中,参数传递的问题尤其重要。RMT使得程序员不仅能够通过值进行输入或输出参数传递,而且还能通过对象引用进行传递。如果下层的参数比较大或比较复杂,那么传递引用是特别有用的。远程一端一且接收到对象引用就能够使用远程方法调用访问该对象,而不是通过网络传输对象值。
参考
- <<分布式系统概念与设计>>
- https://liuzhengyang.github.io/2016/12/16/rpc-principle/