01.RPC基础

RPC定义

远程过程调用范例可以在较高级别上定义为一组两个通过网络连接的通信端点,其中一个端点发送请求,另一个端点根据该请求生成响应。简单来说,这是一个请求-响应范例,其中两个端点/主机具有不同的地址空间。请求远程过程的端点可以称为调用方,对此作出响应的端点可以称为被调用方。RPC中的端点可以是客户端和服务器,对等网络中的两个节点,网格计算系统中的两个主机,甚至两个微服务。RPC通信不仅限于两个主机,还可以涉及多个主机或端点(BergstromPandey,2007

Remote Procedure Call.

最简单的RPC实现如上图所示。在这种情况下,客户端(或调用者)和服务器(或被调用者)由物理网络分开。系统的主要组件是客户端例程/程序,客户端存根,服务器例程/程序,服务器存根和网络例程。存根是一个小程序,通常用作较大程序的替代品(或接口(Rouse,n.d。客户端存根向客户端例程公开服务器例程提供的功能,而服务器存根向服务器例程提供类似客户端的程序(Taing,n.d。客户端存根从客户端程序获取输入参数并返回结果,而服务器存根向服务器程序提供输入参数并获取结果。客户端程序只能与提供远程服务器到客户端接口的客户端存根进行交互。此存根还会序列化由客户端例程发送到存根的输入参数。同样,服务器存根提供到服务器例程的客户端接口,以及处理发送到客户端的数据的序列化。

客户端例程执行远程过程时,它将调用客户端存根,该存根将输入参数序列化。使用OS网络例程(TCP / IP(Taing,n.d.,将此序列化的数据发送到服务器。然后,数据由服务器存根反序列化,并使用给定的参数提供给服务器例程。服务器例程的返回值将再次进行序列化,并通过网络发送回客户端,在此由客户端存根反序列化,并提供给客户端例程。通常,此远程过程对于客户端例程是隐藏的,对于客户端而言它是本地过程。RPC服务还需要发现服务/主机解析机制来引导客户端和服务器之间的通信。RPC的一个重要功能是所有端点的地址空间不同(Birrell&Nelson,1984,但是,将位置传递到全局存储(Amazon S3,Microsoft Azure,Google Cloud Store)并不是不可能的。在RPC中,所有主机都有单独的地址空间。他们无法共享指向一台主机中某个内存位置的指针或引用。这种地址空间隔离意味着所有信息都在主机之间的消息中传递,这些主机以值(对象或变量)的形式进行通信,而不是通过引用进行通信。由于RPC是远程过程调用,因此发送到远程主机的值不能是指向本地内存的指针或引用。但是,将链接传递到全局共享内存位置并不是不可能的,而是取决于系统的类型。

最初,RPC是作为一种同步请求-响应机制开发的,它与特定的编程语言实现绑定在一起,并具有用于将计算外包的自定义网络协议(Birrell&Nelson,1984。它有一个注册系统来注册所有服务器。最早的基于RPC的系统之一(Birrell&Nelson,1984)是在1980年代初期以Cedar编程语言实现的。该系统的目标是为本地过程调用提供类似的编程语义。它是为具有低效率网络协议和用于使用所述网络协议传输信息的串行化方案的LAN网络而开发的,该系统旨在在远程地址空间中执行过程(也称为方法或功能。单线程同步客户端和服务器随附一个注册表系统,服务器使用该注册表系统来绑定(或注册)其过程。客户端使用此注册表系统来查找要在其上执行其远程过程的特定服务器。这个RPC的实现(Birrell&Nelson,1984)有一个非常特定的用例。它是专门为在“施乐研究互联网络”之间进行外包计算而构建的“施乐研究互联网络”是一个小型的封闭的以太网网络,具有16位地址(Birrell&Nelson,1984

现代的RPC系统是与语言无关的,异步的,负载均衡的系统。已根据需要添加了对这些系统的身份验证和授权以及其他安全功能。这些系统中的大多数都将故障处理作为模块内置在其中,并且这些系统通常遍布整个InternetRPC程序跨网络(或通信通道)运行,因此它们需要处理远程错误并能够成功传递信息。错误处理通常会有所不同,可分为远程主机或网络故障处理。根据系统的类型和错误,调用者(或被调用者)将返回错误,并且可以相应地处理这些错误。对于异步RPC调用,可以指定事件以确保进度。

RPC实现在基础通信协议(传统上是基于IPTCP)之上使用序列化(也称为编组或酸洗)方案。这些序列化方案使呼叫者和被叫者都变得与语言无关,从而可以并行开发这两个系统而没有任何语言限制。序列化方案的一些示例是JSONXML或协议缓冲区。现代RPC系统允许彼此独立开发大型系统的不同组件。语言不可知的本质与系统某些部分的解耦相结合,使这两个组件(主叫方和被叫方)可以分别扩展并添加新功能。系统的这种独立扩展可能会导致相互促进的相互连接的RPC服务的网格。

无法定义的RPC

实际上,我们现在很难去定义或者区分RPC与非RPC的边界。在分布式系统世界中,系统的每个单独组件(无论是硬盘,多核处理器还是微服务)都是RPC的扩展,因此很难对RPC范式进行具体定义 。因此,任何与请求-响应机制松散关联的东西都可以视为RPCRPC范例在请求-响应机制中表现最为突出。Futures & Promises也出现在RPC的新品种中。这就引发了一个疑问,即每个请求-响应系统是否都是RPC范例的修改实现,还是实际上给表带来了新的东西?这些现代的通信协议,例如HTTPREST,可能只是RPC的另一种形式。在HTTP中,客户端请求网页(或其他内容,然后服务器以所需的内容进行响应。这种通信的动态可能与传统的RPC稍有不同,但是,HTTP无状态服务器遵循RPC范例背后的大多数概念。同样,请考虑向您喜欢的Google API发送请求。假设您要使用其反向地理编码API将纬度/经度转换为地址,或者想使用其Places API在附近找到一家不错的餐厅,您将向其服务器发送请求以执行以下操作:将使用一些输入参数(例如坐标)并返回结果。尽管这些API遵循RESTful设计,但它似乎是RPC范例的扩展。

RPC范式随着时间的流逝而发展。目前,将RPC与非RPC进行区分变得非常困难。随着时间的流逝,RPC的局限性和局限性不断发展。当前的RPC实现甚至还支持服务器从客户端请求信息以响应这些请求,反之亦然(双向性RPC的这种双向特性已经使RPC从简单的客户端-服务器模型过渡到了一组相互通信的端点。

在过去的四十年中,研究人员和行业领导者一直在努力提出他们对RPC的定义。RPC范式的支持者将每次请求-响应通信都视为RPC范式的一种实现,而反对RPC的人则试图明确列举RPC的局限性。但是,随着时间的推移,随着新的RPC模型的引入,这些限制似乎逐渐消失。RPC支持者将其视为分布式系统的圣杯。他们将其视为现代分布式通信的基础。从Apache ThriftONCHTTPREST,他们都将其倡导为RPC,而REST开发人员强烈反对RPC

而且,利用现代的全局存储机制,对RPC系统具有单独的地址空间的需求似乎正在慢慢消失并逐渐消失。因此,问题仍然是什么是RPC,什么不是RPC?这是一个开放式的问题。关于RPC的外观并没有达成一致的共识,只是它在两个端点之间具有通信。

RPC案例

在现代系统中,RPC已变得非常重要,Google甚至每秒执行10^10RPC调用的命令,每秒有数十万亿次RPC调用。在最简单的RPC系统中,客户端通过网络连接连接到服务器并执行过程。该过程可以很简单,就像用您喜欢的编程语言返回“ Hello World”一样。但是,此远程过程的复杂性没有上限。

from xmlrpc.server import SimpleXMLRPCServer

# a simple RPC function that returns "Hello World!"
def remote_procedure():
    return "Hello World!"

server = SimpleXMLRPCServer(("localhost", 8080))
print("RPC Server listening on port 8080...")
server.register_function(remote_procedure, "remote_procedure")
server.serve_forever()

下面是用Python 3编写的上述服务器的简单RPC客户端的代码。

import xmlrpc.client

with xmlrpc.client.ServerProxy("http://localhost:8080/") as proxy:
    print(proxy.remote_procedure())

在上面的示例中,我们创建了一个名为remote_procedure的简单函数,并将其绑定到localhost上的端口8080。然后,RPC客户端连接到服务器,并在没有输入参数的情况下请求remote_procedure。然后,服务器以执行remote_procedure返回的返回值作为响应。甚至可以将三向握手视为RPC范例的一个示例。三向握手最常用于建立TCP连接。在这里,服务器端应用程序绑定到服务器上的端口,并向DNS服务器添加了主机名解析条目(可以看作是RPC中的注册表。现在,当客户端必须连接到服务器时,它请求DNS服务器将主机名解析为IP地址,然后客户端发送SYN数据包。该SYN数据包可以看作是对另一个地址空间的请求。服务器收到此消息后,将返回SYN-ACK数据包。来自服务器的此SYN-ACK数据包可被视为来自服务器的响应以及建立连接的请求。然后,客户端以ACK数据包响应。