RPC可以有很多种,比较流行的是Alibaba贡献的Apache Dubbo、Facebook贡献的Apache Thrift和Google的gRPC。实际上,不同RPC框架的底层协议和实现,会有一定的差异,但是也是类同的。为了进一步讨论RPC。
Thrift是一种接口描述语言和二进制通信协议,用于定义和创建跨语言的服务。它被当作一个远程过程调用(RPC)框架来使用,是Facebook为“大规模跨语言服务”开发的。Thrift服务在实际应用中分为4层,分别是服务器层(server)、处理器层(processor)、数据协议层(protocal)和传输层(transport),它们的作用如下。
- 服务器层:它提供一个关于Thrift的服务器,客户端可以连接它,它会实现一个线程池,来管理这些客户端连接。
- 处理器层:处理器是由开发者根据自己业务的需要实现的业务逻辑代码。
- 数据协议层:指定数据采用何种编码协议进行传输,然后客户端也可以按照该协议进行解码解析,还原数据。在Thrift中可以使用二进制,这样传输的数据就会大大减少,从而提高传输的速度。当然也可以使用JSON类型的数据协议,这样会使得开发者更容易理解。
- 传输层:可以用Socket、按帧、压缩(zlib)等协议进行传输。
其中服务层、数据协议层和传输层,Thrift都已经提供了良好的封装类,我们只需要告诉Thrift服务器选择便可以了。而处理器层则是开发所需的业务逻辑代码,需要自行开发。Thrift的服务调用并不是很复杂。
首先,服务提供者按照Thrift的要求提供4层结构,然后暴露对应的服务端口。其次,服务消费者会连接服务提供的Thrift服务器,获取它暴露的接口的存根(stub),然后通过存根执行远程调用。这里谈到的存根是一个接口,它会屏蔽内部实现的细节,从而提高代码可读性,降低开发者使用的困难。
从Thrift来看,可以说,RPC和REST风格服务调用在性能上相差是很大的,究其原因主要是以下3点:
- RPC不单可以使用HTTP协议,也可以使用其他协议,如TCP、UDP等,而REST风格只能使用HTTP协议。
- RPC需要传递的净荷(payload)小,一般是REST风格的20%左右。而REST传输的内容越多,需要的带宽越大、时间也越长,对系统性能不利,因此RPC的性能更优。
- RPC一般层级较少,而REST风格则需要更多的层结构,这意味着,在转换和传输的性能上,RPC将大大超过REST风格的调用。
注意,以上3点只是针对性能来说的。但在分布式(微服务)中,追求的不仅仅是性能,在大部分的情况下,如果不会出现较为严重的性能瓶颈,还是推荐使用REST风格,因为REST风格比起RPC,至少有以下3种优势:
- 平台无关性:在RPC中,可用协议很多,传输的数据也可以使用不同的规则(如Java的序列化数据流),这样就使得它只能适应某些平台。而REST风格使用HTTP协议,只要约定采用某种数据格式,如JSON,就具备平台无关性的特点。
- 安全性:REST风格遵循的协议多,可以通过防火墙、网关等进行拦截,这样可以降低恶意攻击的可能性。而RPC则不具备这样的功能。
- 独立性:采用REST风格后,当前的服务是一个相对独立的服务,对于服务消费者来说,不依赖服务提供者的接口和类。而使用RPC,则需要服务提供者暴露对应的接口服务进行调用,因此耦合性较高,独立性较差。
从上面可以看出,使用RPC可以得到很轻的载荷、传输较轻、速度快、协议层少、转换快,但是会产生依赖性,做不到平台无关性,在安全性上较差。使用REST风格,则具备平台无关性、高安全性和独立性。从程序开发的角度来说,使用REST风格时,可以把服务提供者看成一个独立的产品,它更容易使用和扩展,依赖性更低,这有利于应用的复用和扩展,可读性也高。如果性能不会遇到严重瓶颈,那么作为开发者,应该先考虑可读性,这就是为什么微服务推荐使用REST风格调用,而非RPC的原因。对于那些需要高性能、高并发的服务,在某些情况下,也可以考虑牺牲REST风格的优点去使用RPC,以满足性能的需要,所以在使用RPC的时候,需要考虑其适用的场景。