远程服务调用PRC发展史
作者:互联网
本文是《凤凰架构》一书的读书笔记。
RPC 调用简介
RPC(远程服务调用)是指位于互不重合的内存地址空间中(可以是一台机器也可以是不同网络分区的不同机器)的两个程序,在语言层面上,以同步的方式使用带宽有限的信道来传输程序控制信息。
通过网络进行分布式运算的八宗罪(8 Fallaciesof Distributed Computing)
RPC协议要解决的三个问题
1. 如何表示数据(如何进行序列化)
每种RPC协议都应该要有对应的序列化协议。
- CORBA的通用数据表示(Common Data Representation,CDR)
- Java RMI的Java对象序列化流协议(Java Object Serialization StreamProtocol)
- gRPC的Protocol Buffers
- Web Service的XML序列化
- 众多轻量级RPC支持的JSON序列化
2. 如何传递数据(使用什么协议来传递数据)
如何传递数据,准确地说,是指如何通过网络,在两个服务的Endpoint之间相互操作、交换数据。
两个服务交互数据不是只扔个序列化数据流来表示参数和结果就行,许多在此之外的信息,譬如异常、超时、安全、认证、授权、事务等,都可能产生双方需要交换信息的需求。在计算机科学中,专门有一个名词“Wire Protocol”来表示这种两个Endpoint之间交换这类数据的行为,常见的Wire Protocol如下。
- Java RMI的Java远程消息交换协议(Java Remote Message Protocol,JRMP,也支持RMI-IIOP)
- CORBA的互联网ORB间协议(Internet Inter ORB Protocol,IIOP,是GIOP协议在IP协议上的实现版本)
- Web Service的简单对象访问协议(Simple Object Access Protocol,SOAP)
- 如果要求足够简单,双方都是HTTP Endpoint,直接使用HTTP协议也是可以的(如JSON-RPC)
3. 如何表示方法
确定表示方法在本地方法调用中并不是太大的问题,编译器或者解释器会根据语言规范,将调用的方法签名转换为进程空间中子过程入口位置的指针。不过一旦要考虑不同语言,事情又立刻麻烦起来,每种语言的方法签名都可能有差别,所以“如何表示同一个方法”“如何找到对应的方法”还是需要一个统一的跨语言的标准才行。
这个标准可以非常简单,譬如直接给程序的每个方法都规定一个唯一的、在任何机器上都绝不重复的编号,调用时压根不管它是什么方法、签名是如何定义的,直接传这个编号就能找到对应的方法。
类似地,用于表示方法的协议还有:
- CORBA的OMG接口定义语言(OMG Interface Definition Language,OMGIDL)
- Web Service的Web服务描述语言(Web Service Description Language,WSDL)
- JSON-RPC的JSON Web服务协议(JSON Web Service Protocol,JSON-WSP)
没有一个简单、通用、高性能的协议能同时解决上面的问题
面向透明的、简单的RPC协议,如DCE/RPC、DCOM、Java RMI,要么依赖于操作系统,要么依赖于特定语言,总有一些先天约束;
那些面向通用的、普适的RPC协议,如CORBA,就无法逃过使用复杂性的困扰,CORBA烦琐的OMGIDL、ORB都是很好的佐证;
而那些意图通过技术手段来屏蔽复杂性的RPC协议,如Web Service,又不免受到性能问题的束缚。
简单、普适、高性能这三点,似乎真的很难同时满足。
各有侧重点的RPC
由于一直没有一个同时满足以上三点的“完美RPC协议”出现,所以远程服务器调用这个小小的领域,逐渐进入群雄混战、百家争鸣的战国时代,距离“统一”越来越远,并一直延续至今。现在,已经相继出现过RMI(Sun/Oracle)、Thrift(Facebook/Apache)、Dubbo(阿里巴巴/Apache)、gRPC(Google)、Motan1/2(新浪)、Finagle(Twitter)、brpc(百度/Apache)、.NETRemoting(微软)、Arvo(Hadoop)、JSON-RPC 2.0(公开规范,JSON-RPC工作组)等难以穷举的协议和框架。这些RPC功能、特点不尽相同,有的是某种语言私有,有的支持跨越多种语言,有的运行在应用层HTTP协议之上,有的直接运行于传输层TCP/UDP协议之上,但并不存在哪一款是“最完美的RPC”。今时今日,任何一款具有生命力的RPC框架,都不再去追求大而全的“完美”,而是以某个具有针对性的特点作为主要的发展方向。
- 朝着性能发展,代表为gRPC和Thrift。决定RPC性能的主要因素有两个:序列化效率和信息密度。序列化效率很好理解,序列化输出结果的容量越小,速度越快,效率自然越高;信息密度则取决于协议中有效负载(Payload)所占总传输数据的比例大小,使用传输协议的层次越高,信息密度就越低,SOAP使用XML拙劣的性能表现就是前车之鉴。gRPC和Thrift都有自己优秀的专有序列化器,而传输协议方面,gRPC是基于HTTP/2的,支持多路复用和Header压缩,Thrift则直接基于传输层的TCP协议来实现,省去了应用层协议的额外开销。
- 朝着简化发展,代表为JSON-RPC,说要选功能最强、速度最快的RPC可能会很有争议,但选功能弱的、速度慢的,JSON-RPC肯定会是候选人之一。牺牲了功能和效率,换来的是协议的简单轻便,接口与格式都更为通用,尤其适合用于Web浏览器这类一般不会有额外协议支持、额外客户端支持的应用场合。
到了最近几年,RPC框架有明显向更高层次(不仅仅负责调用远程服务,还管理远程服务)与插件化方向发展的趋势,不再追求独立地解决RPC的全部三个问题(表示数据、传递数据、表示方法),而是将一部分功能设计成扩展点,让用户自己选择。框架聚焦于提供核心的、更高层次的能力,譬如提供负载均衡、服务注册、可观察性等方面的支持。这一类框架的代表有Facebook的Thrift与阿里的Dubbo
Dubbo默认有自己的传输协议(Dubbo协议),同时也支持其他协议;默认采用Hessian 2作为序列化器,如果你有JSON的需求,可以替换为Fastjson,如果你对性能有更高的追求,可以替换为Kryo、FST、Protocol Buffers等效率更好的序列化器,如果你不想依赖其他组件库,也可以直接使用JDK自带的序列化器。这种设计在一定程度上缓和了RPC框架必须取舍、难以完美的缺憾。
标签:协议,发展史,Protocol,PRC,Web,JSON,RPC,调用,序列化 来源: https://www.cnblogs.com/54chensongxia/p/15067341.html