2.1.5 分裂的RPC

由于一直没有一个同时满足以上三点的“完美RPC协议”出现,所以远程服务器调用这个小小的领域,逐渐进入群雄混战、百家争鸣的战国时代,距离“统一”越来越远,并一直延续至今。现在,已经相继出现过RMI(Sun/Oracle)、Thrift(Facebook/Apache)、Dubbo(阿里巴巴/Apache)、gRPC(Google)、Motan1/2(新浪)、Finagle(Twitter)、brpc(百度/Apache)、.NET Remoting(微软)、Arvo(Hadoop)、JSON-RPC 2.0(公开规范,JSON-RPC工作组)等难以穷举的协议和框架。这些RPC功能、特点不尽相同,有的是某种语言私有,有的支持跨越多种语言,有的运行在应用层HTTP协议之上,有的直接运行于传输层TCP/UDP协议之上,但并不存在哪一款是“最完美的RPC”。今时今日,任何一款具有生命力的RPC框架,都不再去追求大而全的“完美”,而是以某个具有针对性的特点作为主要的发展方向,举例分析如下。

·朝着面向对象发展,不满足于RPC将面向过程的编码方式带到分布式,希望在分布式系统中也能够进行跨进程的面向对象编程,代表为RMI、.NET Remoting,之前的CORBA和DCOM也可以归入这类。这种方式有一个别名叫作分布式对象(Distributed Object)。

·朝着性能发展,代表为gRPC和Thrift。决定RPC性能的主要因素有两个:序列化效率和信息密度。序列化效率很好理解,序列化输出结果的容量越小,速度越快,效率自然越高;信息密度则取决于协议中有效负载(Payload)所占总传输数据的比例大小,使用传输协议的层次越高,信息密度就越低,SOAP使用XML拙劣的性能表现就是前车之鉴。gRPC和Thrift都有自己优秀的专有序列化器,而传输协议方面,gRPC是基于HTTP/2的,支持多路复用和Header压缩,Thrift则直接基于传输层的TCP协议来实现,省去了应用层协议的额外开销。

·朝着简化发展,代表为JSON-RPC,说要选功能最强、速度最快的RPC可能会很有争议,但选功能弱的、速度慢的,JSON-RPC肯定会是候选人之一。牺牲了功能和效率,换来的是协议的简单轻便,接口与格式都更为通用,尤其适合用于Web浏览器这类一般不会有额外协议支持、额外客户端支持的应用场合。

经历了RPC框架的“战国时代”,开发者们终于认可了不同的RPC框架所提供的特性或多或少是有矛盾的,很难有某一种框架能满足所有需求。若要朝着面向对象发展,就注定不会太简单,如建Stub、Skeleton就很烦了,即使由IDL生成也很麻烦;功能多起来,协议就会更复杂,效率一般也会受影响;要简单易用,那很多事情就必须遵循约定而不是自行配置;要重视效率,那就需要采用二进制的序列化器和较底层的传输协议,支持的语言范围容易受限。也正是每一种RPC框架都有不完美的地方,所以才导致不断有新的RPC轮子出现,也决定了在选择框架时,在获得一些利益的同时,要付出另外一些代价。

到了最近几年,RPC框架有明显向更高层次(不仅仅负责调用远程服务,还管理远程服务)与插件化方向发展的趋势,不再追求独立地解决RPC的全部三个问题(表示数据、传递数据、表示方法),而是将一部分功能设计成扩展点,让用户自己选择。框架聚焦于提供核心的、更高层次的能力,譬如提供负载均衡、服务注册、可观察性等方面的支持。这一类框架的代表有Facebook的Thrift与阿里的Dubbo,尤其是断更多年后重启的Dubbo表现得更为明显。Dubbo默认有自己的传输协议(Dubbo协议),同时也支持其他协议;默认采用Hessian 2作为序列化器,如果你有JSON的需求,可以替换为Fastjson,如果你对性能有更高的追求,可以替换为Kryo、FST、Protocol Buffers等效率更好的序列化器,如果你不想依赖其他组件库,也可以直接使用JDK自带的序列化器。这种设计在一定程度上缓和了RPC框架必须取舍、难以完美的缺憾。

最后,笔者提个问题,大家不妨来反思一下:开发一个分布式系统,是不是就一定要用RPC呢?RPC的三大问题源自于对本地方法调用的类比模拟,如果我们把思维从“方法调用”的约束中挣脱,那在解决参数与结果如何表示、数据如何传递、方法如何表示这些问题时都会有焕然一新的视角。但是我们写程序,真的可能不面向方法来编程吗?这就是笔者下一节准备谈的话题了。

后记

前文提及DCOM、CORBA、Web Service的失败时,可能笔者的口吻多少有一些戏谑,这只是落笔行文的方式。这些框架即使没有成功,但作为早期的探索先驱,并没有什么该去讽刺的地方。而且它们的后续发展,都称得上是知耻后勇,都值得我们赞赏。譬如说到CORBA的消亡,OMG痛定思痛之后,提出了基于RTPS协议栈的“数据分发服务”(Data Distribution Service,DDS)商业标准(就是要付费使用的意思),如今主要流行于物联网领域,能够做到微秒级延时,还能支持大规模并发通信。譬如说到DCOM的失败和Web Service的式微,微软在它们的基础上推出的.NET WCF(Windows Communication Foundation,Windows通信基础),不仅同时将REST、TCP、SOAP等不同形式的调用自动封装为完全一致的如同本地方法调用一般的程序接口,还依靠自家的“地表最强IDE”Visual Studio将工作量减少到只需要指定一个远程服务地址,就可以获取服务描述、绑定各种特性(譬如安全传输)、自动生成客户端调用代码,甚至还能选择同步或者异步之类细节的程度。尽管.NET WCF只支持.NET平台,而且与传统Web Service一样采用XML描述,但使用体验异常畅快,能挽回Web Service中得罪开发者丢掉的全部印象分。