【Distributed System】Data Flow - 远程过程调用(Remote Procedure Call, RPC)

Posted by 西维蜀黍 on 2019-01-07, Last Modified on 2022-12-10

什么是远程过程调用(Remote Procedure Call,RPC)

RPC 是**远程过程调用(Remote Procedure Call)**的缩写形式。

Birrell 和 Nelson 在 1984 发表于 ACM Transactions on Computer Systems 的论文《Implementing remote procedure calls》对 RPC 做了经典的诠释。RPC 是指计算机 A 上的进程,调用另外一台计算机 B 上的进程,其中 A 上的调用进程被挂起,而 B 上的被调用进程开始执行,当值返回给 A 时,A 进程继续执行。调用方可以通过使用参数将信息传送给被调用方,而后可以通过传回的结果得到信息。而这一过程,对于开发人员来说是透明的。

RPC 的主要好处有两个:

  • 程序员可以使用过程调用语义来调用远程函数并获取响应。
  • 简化了编写分布式应用程序的难度,因为 RPC 隐藏了所有的网络代码存根函数。应用程序不必担心一些细节,比如 socket、端口号以及数据的转换和解析。在 OSI 参考模型中,RPC 跨越了会话层和表示层。

RPC 使用的通信协议

RPC 可以灵活使用其所基于的协议,如果基于 HTTP,则与 Web Service 就没有什么区别了,如基于更底层的协议(如 TCP ),则会比 Web Service 相对更为高效。

第一代 RPC

  • ONC RPC
  • DCE RPC

ONC RPC(以前称为 Sun RPC)

Sun 公司是第一个提供商业化 RPC 库和 RPC 编译器。在1980年代中期 Sun 计算机提供 RPC,并在 Sun Network File System(NFS) 得到支持。该协议被主要以 Sun 和 AT&T 为首的 Open Network Computing (开放网络计算)作为一个标准来推动。这是一个非常轻量级 RPC 系统可用在大多数 POSIX 和类 POSIX 操作系统中使用,包括 Linux、SunOS、OS X 和各种发布版本的 BSD。这样的系统被称为 Sun RPC 或 ONC RPC。

ONC RPC 提供了一个编译器,需要一个远程过程接口的定义来生成客户机和服务器的存根函数。这个编译器叫做 rpcgen。在运行此编译器之前,程序员必须提供接口定义。包含函数声明的接口定义,通过版本号进行分组,并被一个独特的程序编码来标识。该程序编码能够让客户来确定所需的接口。版本号是非常有用的,即使客户没有更新到最新的代码仍然可以连接到一个新的服务器,只要该服务器还支持旧接口。

分布式计算环境中的 RPC(DCE RPC)

DCE(Distributed Computing Environment,分布式计算环境)是一组由OFS(Open Software Foundation,开放软件基金会)设计的组件,用来提供支持分布式应用和分布式环境。与 X/Open 合并后,这组织成为了 The Open Group (开放式开发组)。DCE 提供的组件包括一个分布式文件服务、时间服务、目录服务以及其他服务。当然,我们感兴趣的是 DCE 的远程过程调用。它非常类似于 Sun RPC。接口是由 Interface Definition Notation (IDN) 定义的。类似于 Sun RPC,接口定义就像函数原型。

Sun RPC 不足之处在于,服务器的标识是一个“独特”的 32-bit 数字。虽然这是一个比在 socket 中 16-bit 可用空间更大的空间,但仍然无法满足数字唯一性的需求。DCE RPC 考虑到了这一缺陷,它无需程序员来处理编码。在编写应用程序时的第一步是从 uuidgen 程序获得一个惟一的 ID。这个程序会生成一个包含 ID 接口的原型 IDN 文件,并保证永远不会再次使用。它是一个 128-bit 的值,其中包含一个位置代码和创建时间的编码。然后用户编辑原型文件,填写远程过程声明。

在这一步后,IDN 的编译器 dceidl(类似于 rpcgen)会生成一个头、客户机存根和服务器存根。

Sun RPC 的另一个缺陷是,客户端必须知道服务器在哪台机器上。当它要访问时,必须要询问机器上的 RPC 名称服务程序编码所对应的端口号。DCE 支持将多个机器组织成为管理实体,称为 cells。cell 目录服务器使得每台机器知道如何与另外一台负责维护 cell 信息服务机器交互。

第二代 RPC:支持对象

  • 微软 DCOM(COM+)
  • CORBA
  • Java RMI

面向对象的语言开始在1980年代末兴起,很明显,第一代 RPC(当时的 Sun ONC 和 DCE RPC 系统)都没有提供任何支持诸如从远程类实例化远程对象、跟踪对象的实例或提供支持多态性

微软 DCOM(COM+)

1992年4月,微软发布 Windows 3.1 包括一种机制称为 OLE (Object Linking and Embedding)。这允许一个程序动态链接其他库来支持的其他功能。如将一个电子表格嵌入到 Word 文档。OLE 演变成了 COM (Component Object Model)。一个 COM 对象是一个二进制文件。使用 COM 服务的程序来访问标准化接口的 COM 对象而不是其内部结构。COM 对象用全局唯一标识符(GUID)来命名,用类的 ID 来识别对象的类。几种方法来创建一个 COM 对象(例如 CoGetInstanceFromFile)。COM 库在系统注册表中查找相应的二进制代码(一个 DLL 或可执行文件),来创建对象,并给调用者返回一个接口指针。COM 的着眼点是在于同一台计算机上不同应用程序之间的通讯需求.

DCOM( Distributed Component Object Model)是 COM 的扩展,它支持不同的两台机器上的组件间的通信,而且不论它们是运行在局域网、广域网、还是 Internet 上。借助 DCOM 你的应用程序将能够进行任意空间分布。DCOM 于1996年在 Windows NT4.0 中引入的,后来更名为 COM+。由于 DCOM 是为了支持访问远程 COM 对象,需要创建一个对象的过程,此时需要提供服务器的网络名以及类 ID。微软提供了一些机制来实现这一点。最透明的方式是远程计算机的名称固定在注册表(或 DCOM 类存储)里,与特定类 ID 相关联。以此方式,应用程序不知道它正在访问一个远程对象,并且可以使用与访问本地 COM 对象相同的接口指针。另一方面,应用程序也可指定一个机器名作为参数。

由于 DCOM 是 COM 这个组件技术的无缝升级,所以你能够从你现有的有关 COM 得知识中获益,你的以前在 COM 中开发的应用程序、组件、工具都可以移入分布式的环境中。DCOM 将为你屏蔽底层网络协议的细节,你只需要集中精力于你的应用。

CORBA

虽然 DCE 修复的一些 Sun RPC 的缺点,但某些缺陷依然存在。例如,如果服务器没有运行,客户端是无法连接到远程过程进行调用的。管理员必须要确保在任何客户端试图连接到服务器之前将服务器启动。如果一个新服务或接口添加到了系统,客户端是不能发现的。最后,面向对象语言期望在函数调用中体现多态性,即不同类型的数据的函数的行为应该有所不同,而这点恰恰是传统的 RPC 所不支持的。

CORBA (Common Object Request Broker Architecture) 就是为了解决上面提到的各种问题。是由 OMG 组织制订的一种标准的面向对象应用程 序体系规范。或者说 CORBA 体系结构是对象管理组织(OMG)为解决分布式处理环境(DCE)中,硬件和软件系统的互连而提出的一种解决方案。

Java RMI

CORBA 旨在提供一组全面的服务来管理在异构环境中(不同语言、操作系统、网络)的对象。Java 在其最初只支持通过 socket 来实现分布式通信。1995年,作为 Java 的缔造者,Sun 公司开始创建一个 Java 的扩展,称为 Java RMI(Remote Method Invocation,远程方法调用)。Java RMI 允许程序员创建分布式应用程序时,可以从其他 Java 虚拟机(JVM)调用远程对象的方法。

一旦应用程序(客户端)引用了远程对象,就可以进行远程调用了。这是通过 RMI 提供的命名服务(RMI 注册中心)来查找远程对象,来接收作为返回值的引用。Java RMI 在概念上类似于 RPC,但能在不同地址空间支持对象调用的语义。

与大多数其他诸如 CORBA 的 RPC 系统不同,RMI 只支持基于 Java 来构建,但也正是这个原因, RMI 对于语言来说更加整洁,无需做额外的数据序列化工作。

Java RMI采用 TCP/IP协议,客户端直接调用服务端上的一些方法。优点是强类型,编译期可检查错误,缺点是只能基于 Java 语言,客户机与服务器紧耦合。

第三代 RPC 以及 Web Services

Background

The RPC model tries to make a request to a remote network service look the same as calling a function or method in your programming lan‐guage, within the same process (this abstraction is called location transparency). Although RPC seems convenient at first, the approach is fundamentally flawed. A network request is very different from a local function call:

A local function call is predictable and either succeeds or fails, depending only on parameters that are under your control. A network request is unpredictable: the request or response may be lost due to a network problem, or the remote machine may be slow or unavailable, and such problems are entirely outside of your control. Network problems are common, so you have to anticipate them, for example by retrying a failed request.

  • A local function call either returns a result, or throws an exception, or never returns (because it goes into an infinite loop or the process crashes). A network request has another possible outcome: it may return without a result, due to a timeout. In that case, you simply don’t know what happened: if you don’t get a response from the remote service, you have no way of knowing whether the request got through or not.

  • If you retry a failed network request, it could happen that the requests are actually getting through, and only the responses are getting lost. In that case, retrying will cause the action to be performed multiple times, unless you build a mechanism for deduplication (idempotence) into the protocol. Local function calls don’t have this problem.

  • Every time you call a local function, it normally takes about the same time to exe‐cute. A network request is much slower than a function call, and its latency is also wildly variable: at good times it may complete in less than a millisecond, but when the network is congested or the remote service is overloaded it may take many seconds to do exactly the same thing.

  • When you call a local function, you can efficiently pass it references (pointers) to objects in local memory. When you make a network request, all those parameters

  • need to be encoded into a sequence of bytes that can be sent over the network. That’s okay if the parameters are primitives like numbers or strings, but quickly becomes problematic with larger objects.

    • The client and the service may be implemented in different programming lan‐guages, so the RPC framework must translate datatypes from one language into another. This can end up ugly, since not all languages have the same types—recall JavaScript’s problems with numbers greater than 2 53 , for example. This problem doesn’t exist in a single process written in a single language.

第三代 RPC 以及 Web Services

  • JSON-RPC
  • XML-RPC
  • SOAP
  • Microsoft .NET Remoting

由于互联网的兴起,Web 浏览器成为占主导地位的用于访问信息的模型。现在的应用设计的首要任务大多数是提供用户通过浏览器来访问,而不是编程访问或操作数据。

网页设计关注的是内容。解析展现方面往往是繁琐的。传统 RPC 解决方案可以工作在互联网上,但问题是,他们通常严重依赖于动态端口分配,往往要进行额外的防火墙配置。

Web Services 成为一组协议,允许服务被发布、发现,并用于技术无关的形式。即服务不应该依赖于客户的语言、操作系统或机器架构。

Web Services 的实现一般是使用 Web 服务器作为服务请求的管道。客户端访问该服务,首先是通过一个 HTTP 协议发送请求到服务器上的 Web 服务器。Web 服务器配置识别 URL 的一部分路径名或文件名后缀并将请求传递给特定的浏览器插件模块。这个模块可以除去头、解析数据(如果需要),并根据需要调用其他函数或模块。对于这个实现流,一个常见的例子是浏览器对于 Java Servlet 的支持。HTTP 请求会被转发到 JVM 运行的服务端代码来执行处理。


XML-RPC 、JSON-RPC,常见的语境是利用 HTTP 协议作为调用控制协议。并将 XML 和 JSON 作为对象序列化之后的格式。

JSON-RPC

JSON-RPC is a remote procedure call protocol encoded in JSON. It is similar to the XML-RPC protocol, defining only a few data types and commands.

  • 可以选择基于 TCP 或 HTTP
  • JSON-based
  • Java语言中较好的 JSON-RPC 实现框架有jsonrpc4j、jpoxy、json-rpc

XML-RPC

XML-RPC is a remote procedure call (RPC) protocol which uses XML to encode its calls and HTTP as a transport mechanism.

  • 基于 HTTP
  • 请求被编码为 XML
  • 服务端处理过程
    • 读取并解析 XML
    • 执行调用方法
    • 将执行结果存入 XML
    • 返回XML结果给客户端

SOAP (Simple Object Access Protocol)

SOAP (formerly an acronym for Simple Object Access Protocol) is a messaging protocol specification for exchanging structured information in the implementation of web services in computer networks.

  • 通常基于 HTTP POST
  • SOAP uses XML Information Set for its message format
  • SOAP 只是一种消息格式,并未定义垃圾回收、对象引用、存根生成和传输协议。

SOAP(Simple Object Access Protocol,简单对象访问协议),是以 XML-RPC 规范作为创建 SOAP 的依据,成立于1998年,获得微软和 IBM 的大力支持。该协议在创建初期只作为一种对象访问协议,但由于 SOAP 的发展,其协议已经不单只是用于简单的访问对象,所以这种 SOAP 缩写已经在标准的1.2版后被废止了。1.2版在2003年6月24日成为 W3C 的推荐版本。SOAP 指定 XML 作为无状态的消息交换格式,包括了 RPC 式的过程调用。

SOAP是在XML-RPC基础上,使用标准的XML描述了RPC的请求信息(URI/类/方法/参数/返回值)。无论Jave RMI还是DCOM,他们都耦合于特定的编程语言。SOAP希望能够推出一种RPC通信不依赖于任何操作系统、编程语言,最终实现可相互操作性(Interoperability)。

SOAP可支持任何传输协议,从HTTP/HTTPS到SMTP(Simple Mail Transfer Protocol,简单邮件传送协议),甚至JMS(Java Messaging Service,Java消息传递服务)。不过,由于XML较为冗长且解析费时,因此采用XML也成为一个弊端。

参考:

Microsoft .NET Remoting

从微软的产品角度来看,可以说 .NET Remoting 就是 DCOM 的一种升级,它改善了很多功能,并极好的融合到 .NET 平台下。Microsoft .NET Remoting 提供了一种允许对象通过应用程序域与另一对象进行交互的框架。

.NET Remoting 提供了一种允许对象通过应用程序域与另一对象进行交互的框架。这种框架提供了多种服务,包括激活和生存期支持,以及负责与远程应用程序进行消息传输的通讯通道。格式化程序用于在消息通过通道传输之前,对其进行编码和解码。应用程序可以在注重性能的场合使用二进制编码,在需要与其他远程处理框架进行交互的场合使用 XML 编码。在从一个应用程序域向另一个应用程序域传输消息时,所有的 XML 编码都使用 SOAP 协议。出于安全性方面的考虑,远程处理提供了大量挂钩,使得在消息流通过通道进行传输之前,安全接收器能够访问消息和序列化流

Java 对 Web Services的支持

Java RMI 与远程对象进行交互,其实现是需要基于 Java 的模型。此外,它没有使用 Web Services 和基于 HTTP 的消息传递。

JAX-WS是JDK自带的Web服务的API,它可以用于提供REST式或基于SOAP的服务。

现在,已经出现了大量的软件来支持基于 Java 的 Web Services。JAX-WS (Java API for XML Web Services) 就是作为 Web Services 消息和远程过程调用的规范。

注意,JAX-WS只是一个Java通过 Web Services 实现RPC的一个规范,而遵循这个规范对应的实现由很多种,比如

  • Jersey
  • Apache CXF
  • Apache Axis 2
  • xfire

JAX-WS 的一个目标是平台互操作性。其 API 使用 SOAP 和WSDL。双方不需要 Java 环境。

第四代RPC

  • REST
  • Apache Thrift
  • gRPC

SOAP 虽然仍然是广泛部署应用,但在许多环境中很多厂商已经抛弃了 SOAP,转而使用其他更轻量、更容易理解、或者与 Web 交互模型更干净的机制。例如,Google 的 API 在2006年后就不再支持 SOAP 接口,而是使用AJAX、XML-RPC 和 REST 作为替代。一个匿名的微软员工批评 SOAP 过于复杂,因为“我们希望我们的工具来阅读它,而不是人”。不管上述言论是否准确,有一点是可以肯定的,SOAP 显然是一个复杂和高度冗长的格式。

REST(REpresentational State Transfer)

  • 基于HTTP
    • SOAP 在创建自己的消息传递协议时是基于HTTP,但实际上 REST (REpresentational State Transfer)的方式才是保持 Web 的原理和使用 HTTP 协议的核心部分。

原始的 HTTP 协议已经定义了四个命令,清晰地映射到各种数据(定义为“资源”)操作:

  • PUT(更新)
  • GET(选择)
  • POST(插入)
  • DELETE(删除)

Refer to https://swsmile.info/post/httprestful/

Apache Thrift

  • 跟一些替代选择,比如 SOAP 相比,跨语言序列化的代价更低,因为它使用二进制格式。
  • 可基于 HTTP 或者 TCP
  • 支持 JSON 或二进制消息格式

Thrift 是 Facebook 于2007年开发的跨语言的 RPC 服务框架,提供多语言的编译功能,并提供多种服务器工作模式;用户通过Thrift的IDL(Interface Description Language,接口定义语言)来描述接口函数及数据类型,然后通过Thrift的编译环境生成各种语言类型的接口文件,用户可以根据自己的需要采用不同的语言开发客户端代码和服务器端代码。

Apache Thrift可作为REST的替代品,它用来编写可跨平台的RPC客户端/服务端,提供C语言式的风格以IDL的形式来定义你的API。

换句话说,Thrift提供跨语言的服务框架,这种跨语言主要体现在它对多种语言的编译功能的支持,用户只需要使用IDL描述好接口函数,只需要一条简单的命令,Thrift就能够把按照IDL格式描述的接口文件翻译成各种语言版本。

Thrift是为了解决facebook系统中各系统间大数据量的传 输通信以及系统之间语言环境不同需要跨平台的特性。所以thrift可以支持多种程序语言,例如: C++, C#, Cocoa, Erlang, Haskell, Java, Ocami, Perl, PHP, Python, Ruby, Smalltalk. 在多种不同的语言之间通信thrift可以作为二进制的高性能的通讯中间件,支持数据(对象)序列化和多种类型的RPC服务。

gRPC

gRPC (gRPC Remote Procedure Calls) is an open source remote procedure call (RPC) system initially developed at Google in 2015 as the next generation of the RPC infrastructure Stubby.

It uses HTTP/2 for transport, Protocol Buffers as the interface description language, and provides features such as authentication, bidirectional streaming and flow control, blocking or nonblocking bindings, and cancellation and timeouts. It generates cross-platform client and server bindings for many languages.

  • Encoding: gRPC uses Protocol Buffers to encode data. Contrary to HTTP APIs with JSON, they have a more strict specification.

Reference