如何设计一个 RPC 系统 (3)

CORBA是一个“古老”的,雄心勃勃的方案,他试图在完成远程调用的同时,还完成跨语言的通信的任务,因此其复杂程度是最高的,但是它的设计思想,也被后来更多的其他方案所学习。在通信对象的定位上,它使用URL来定义一个远程对象,这是在互联网时代非常容易接受的。其对象的内容则限定在C语言类型上,并且只能传递值,这也是非常容易理解的。为了能让不同语言的程序通信,所以就必须要在各种编程语言之外独立设计一种仅仅用于描述远程接口的语言,这就是所谓的IDL:Interface Description Language 接口描述语言。

用这个方法,你就可以先用一种超然于所有语言之外的语言来定义接口,然后使用工具自动生成各种编程语言的代码。这种方案对于编译型语言几乎是唯一选择。CORBA并没有对通信问题有任何约定,而是留给具体语言的实现者去处理,这也许是他没有广泛流行的原因之一。

实际上CORBA有一个非常著名的继承者,他就是Facebook公司的Thrift框架。Thrift也是使用一种IDL编译生成多种语言的远程调用方案,并且用C++/JAVA等多种语言完整的实现了通信承载,所以在开源框架中是特别有号召力的一个。Thrfit的通信承载还有个特点,就是能组合使用各种不同的传输协议和编码协议,比如TCP/UDP/HTTP配合JSON/BIN/PB……这让它几乎可以选择任何的网络环境。

Thrift的模型类似下图,这里有的stub表示“桩代码”,就是客户端直接使用的函数形式程序;skeleton表示“骨架代码”,是需要程序员编写具体提供远程服务功能的模板代码,一般对模版做填空或者继承(扩展)即可。这个stub-skeleton模型几乎是所有远程调用方案的标配。

img

2. JAVA RMI

JAVA RMI是JAVA虚拟机自带的一个远程调用方案。它也是可以使用URL来定位远程对象,使用JAVA自带的序列化编码协议传递参数值。在接口描述上,由于这是一个仅限于JAVA环境下的方案,所以直接用JAVA语言的Interface类型作为定义语言。用户通过实现这个接口类型来提供远程服务,同时JAVA会根据这个接口文件自动生成客户端的调用代码供调用者使用。他的底层通信实现,还是用TCP协议实现的。在这里,Interface文件就是JAVA语言的IDL,同时也是skeleton模板,供开发者来填写远程服务内容。而stub代码则由于JAVA的反射功能,由虚拟机直接包办了。

这个方案由于JAVA虚拟机的支持,使用起来非常简单,完全按照标志的JAVA编程方法就可以轻松解决问题,但是这也仅仅能在JAVA环境下运行,限制了其适用的范围。鱼与熊掌不可兼得,易用性和适用性往往是互相冲突的。这和CORBA/Thrift追求最大范围的适用性有很大的差别,也导致了两者在易用性上的不同。

3. Windows RPC

Windows中对RPC支持是比较早和比较完善的。首先它通过GUID来查询对象,然后使用C语言类型作为参数值的传递。由于Windows的API主要是C语言的,所以对于RPC功能来说,还是要用一种IDL来描述接口,最后生成.h和.c文件来生产RPC的stub和skeleton代码。而通信机制,由于是操作系统自带的,所以使用内核LPC机制承载,这一点还是对使用者来说比较方便的。但是也限制了只能用于Windows程序之间做调用。

4. WebService & REST

在互联网时代,程序需要通过互联网来互相调用。而互联网上最流行的协议是HTTP协议和WWW服务,因此使用HTTP协议的Web Service就顺理成章的成为跨系统调用的最流行方案。由于可以使用大多数互联网的基础设施,所以Web Service的开发和实现几乎是毫无难度的。一般来说,它都会使用URL来定位远程对象,而参数则通过一系列预定义的类型(主要是C语言基础类型),以及对象序列化方式来传递。接口生成方面,你可以自己直接对HTTP做解析,也可以使用诸如WSDL或者SOAP这样的规范。在REST的方案中,则限定了只有PUT/GET/DELETE/POST四种操作函数,其他都是参数。

img

总结一下上面的这些RPC方案,我们发现,针对远程调用的三个核心问题,一般业界有以下几个选择:

远程对象定位:使用URL;或者使用名字服务来查找

远程调用参数传递:使用C的基本类型定义;或者使用某种预订的序列化(反序列化)方案

接口定义:使用某种特定格式的技术,直接按预先约定一种接口定义文件;或者使用某种描述协议IDL来生成这些接口文件

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zywxpd.html