审校 | 重楼
本文对gRPC和REST的特征和区别进行了介绍,这可能是当今创建API最常用的两种方法。
以下将从这两种工具的特征开始,也就是它们是什么以及提供什么功能。然后,将根据七个方面对它们进行比较,这对现代系统来说是最重要的7个类别。
其类别如下:
(1)底层HTTP协议
(2)支持的数据格式
(3)数据大小
(4)吞吐量
(5)定义
(6)易于采用
(7)工具支持
gRPC的介绍
当人们听到API时,可能马上想到REST API。然而,REST是构建API的众多方法之一。它并不是适用于所有用例的灵丹妙药。还有其他方法,远程过程调用 (RPC)只是其中之一,而gRPC可能是使用RPC最成功的框架。
尽管gRPC是一种相当成熟和高效的技术,但它仍然被视为一种新技术。因此,尽管在某些用例中非常方便,但它没有REST被广泛采用。
本文主要介绍gRPC,并指出它可以发挥作用的用例。
什么是REST
表述性状态转移(REST)可能是创建公开任何类型API的应用程序的最常用方法。它使用HTTP作为底层通信媒介。正因为如此,它可以从HTTP的所有优点中获益,例如缓存。
- 客户端-服务器通信
- 无状态通信
- 缓存
- 统一接口
- 分层系统
- 按需编码
REST的两个关键概念是:
(1)端点:表示特定资源的唯一统一资源定位符(URL);可以看作是通过互联网访问特定操作或数据元素的一种方式。
(2)资源:在特定URL下可用的特定数据块。
此外,还有一个叫做Richardson成熟模型的描述——这是一个描述REST API中“专业性”程度的模型。它根据特定API具有的特征集将REST API划分为3个级别(或4个级别,取决于是否将级别0计算在内)。
其中一个特征是REST端点应该在URL中使用名词,并使用正确的HTTP请求方法来管理其资源。
- 示例:用DELETE user/1代替GET user/deleteById/1
至于HTTP方法及其相关操作,如下所示:
- GET—检索特定的资源或资源集合。
- POST—创建一个新的资源。
- PUT—修改整个资源。
- PATCH -特定资源的部分更新。
- DELETE—删除指定id的资源。
成熟度模型规定的远不止这些。例如称为HyperMedi的概念。HyperMedia将数据的呈现和对客户端可以执行的操作的控制结合在一起。
对成熟度模型的完整描述超出了本文的范围。
什么是gRPC?
gRPC是远程过程调用(RPC)这个相对古老的概念的另一种实现。谷歌的开发人员创建了它,这就是为什么它的名称里有“g”的原因。它可能是处理RPC最现代和最有效的工具,也是CNCF孵化项目。
gRRC使用谷歌公司的协议缓冲区作为序列化格式,同时利用HTTP/2作为传输介质数据,尽管gRPC也可以使用JSON作为数据层。
- 方法:gRPC的基本构建块,每个方法都是一个远程过程调用,它接受一些输入并返回输出。它执行用所选编程语言进一步实现的单个操作。目前,gRPC支持4种方法:
①Unary:经典的请求-响应模型,该方法接受输入并返回输出。
②服务器流:该方法接受消息作为输入,同时返回消息流作为输出。gRPC保证了单个RPC调用中的消息排序。
③客户端流:该方法将消息流作为输入,处理它们直到没有消息为止,然后返回一条消息作为输出。与上面类似,gRPC保证在单个RPC调用中进行消息排序。
④双向流:该方法将消息流作为输入,并将消息流作为输出返回,有效地使用了读和写两个消息流。两个流都独立运行,并且将消息顺序保留在流级别上。
- 服务:表示一组方法,每个方法在服务中必须有其唯一的名称。服务还描述了安全性、超时或重试等特性。
- 消息:表示方法的输入或输出的对象。
gRPC API定义以.proto文件的形式编写,其中包含上述所有三个基本构建块。此外,gRPC提供了一个协议缓冲区编译器,它从.proto文件生成客户端和服务代码。
用户可以随心所欲地实现服务器端方法,必须坚持使用API的输入输出契约。
在客户端,有一个叫做客户端(或stub)的对象——类似于HTTP客户端。它知道来自服务器的所有方法,只处理调用远程过程并返回它们的响应。
gRPC和REST的比较
(1)底层HTTP协议
这是第一类,可能也是最重要的一类,因为它的影响也可以在其他方面看到。
一般来说,REST是基于请求-响应的,并使用HTTP/1.1作为传输媒介。这里必须使用不同的协议,例如WebSocket或任何类型的流或更持久的连接。
还可以实现一些简单的代码,使REST看起来像消息流。更重要的是,使用HTTP/1.1 REST需要每个请求-响应交换一个连接。对于长时间运行的请求,或者当网络容量有限时,这种方法可能会有问题。
当然,可以使用HTTP/2来构建类似REST的API;然而,并不是所有的服务器和库都支持HTTP/2。因此,其他地方可能会出现问题。
另一方面,gRPC只使用HTTP/2。它允许通过单个TCP连接发送多个请求-响应对。这种方法可以显著地提高应用程序的性能。
- 结果:gRPC略有获胜
(2)支持的数据格式
假设REST API使用HTTP/1.1的默认情况,那么它可以支持多种格式。
REST通常不对消息格式和样式施加任何限制。基本上,任何一种可以序列化为普通旧文本的格式都是有效的。用户可以在特定场景中使用最适合自己的任何格式。
在REST应用程序中发送数据的最流行格式无疑是JSON。XML排在第二位,因为有大量的较旧/遗留应用程序。
然而,当使用REST和HTTP/2时,只支持二进制交换格式。在这种情况下,可以使用Protobuf或Avro。当然,这种方法也有它的缺点,但在以下几点中会详细说明这一点。
同时,gRPC只支持两种数据交换格式:
①Protobuf —默认情况下。
②JSON—当需要与原有API集成时。
如果用户尝试使用JSON,那么gRPC将使用JSON作为消息的编码格式,而使用GSON作为消息格式。此外,使用JSON需要做更多的配置。
- 结果:REST获胜,因为它支持更多格式。
(3)数据大小
默认情况下,gRPC使用二进制数据交换格式,这显著地减少了通过网络发送的消息的大小:研究表明,以字节计算,大约减少了40%~50%,而从以前的一个项目中获得的经验表明,甚至减少了50%~70%。
以上提供了JSON和Protobuff之间相对深入的大小比较。本文作者还提供了一个生成JSON和二进制文件的工具。这样就可以重新运行他的实验并比较结果。
本文中的对象相当简单。不过,一般的规则是——嵌入的对象越多,JSON的结构越复杂,它就会比Protobuf更重。50%的大小差异有利于Protobuf 是一个很好的基线。
在使用REST的二进制交换格式时,可以最大限度地减少或消除这种差异。然而,这不是最常见的,也不是最受支持的RESTful API方式,因此可能会出现其他问题。
- 结果:默认情况下,gRPC获胜;在两者都使用二进制数据格式的情况下,双方为平局。
(4)吞吐量
同样,在REST的情况下,一切都取决于底层HTTP协议和服务器。
在默认情况下,基于HTTP/1.1的REST,即使是最高性能的服务器也无法击败gRPC的性能,特别是当使用JSON时添加序列化和反序列化开销时。虽然当切换到HTTP/2时,差异似乎有所减少。
至于最大吞吐量,在这两种情况下,HTTP都是一种传输媒介,因此它具有无限扩展的潜力。因此,一切都取决于正在使用的工具以及对应用程序的精确操作,因为设计没有限制。
- 结果:默认情况下为gRPC;在同时使用二进制数据和HTTP/2的情况下,gRPC略胜一筹。
(5)定义
在这一部分中,将描述如何在这两种方法中定义消息和服务。
在大多数REST应用程序中,只是将请求和响应声明为类、对象或特定语言支持的任何结构。然后,依靠提供的库来序列化和反序列化JSON/XML/YAML或任何需要的格式。
此外,目前正在努力创建能够根据Swagger的REST API定义用所选编程语言生成代码的工具。然而,它们似乎是在alpha版本中,所以仍然可能会出现一些错误和小问题,这将使它们难以使用。
REST应用程序的二进制格式和非二进制格式之间几乎没有区别,因为这两种情况下的规则大致相同。对于二进制格式,只是以特定格式所需的方式定义所有内容。
此外,可以通过来自底层库或框架的方法或注释来定义REST服务。该工具还负责将其与其他配置一起向外部世界公开。
在gRPC的情况下,将Protobuf作为默认的并且事实上是编写定义的唯一方式。必须在.proto文件中声明所有内容:消息、服务和方法,所以事情非常简单。
然后使用gRPC提供的工具生成代码,只需要实现自己的方法。在那之后,一切都应该按预期工作。
此外,Protobuf支持导入,因此能够以一种相当简单的方式将设置扩展到多个文件。
- 结果:在这里没有赢家,给出的描述和建议是,选择最适合自己的方法。
(6)易于采用
在这一部分中,将比较现代编程语言中对每种方法的库/框架支持。
本文作者表示,在他作为软件工程师的职业生涯中,遇到的每种编程语言(Java、Scala、Python)都至少有3个主要的库/框架用于创建类REST应用程序,更不用说将JSON解析为对象/类的库了。
此外,由于REST默认使用人类可读的格式,因此对于新手来说,它更容易调试和使用。这也会影响交付新特性的速度,并帮助用户应对代码中出现的错误。
长话短说,对REST风格应用程序的支持至少非常好。
在Scala中,甚至有一个叫做Tapir的工具。Tapir允许用户抽象HTTP服务器,并编写可用于多个服务器的端点。
gRPC本身为超过8种流行的编程语言提供了客户端库。这通常就足够了,因为这些库包含了制作gRPC API所需的所有内容。此外,一些库为Java(通过Spring Boot Starter)和Scala提供了更高的抽象。
另一件事是,REST如今被认为是一个世界性的标准和构建服务的切入点,而RPC和gRPC,特别是,尽管在这一点上有点过时,但仍然被视为一种新奇事物。
- 结果:REST被更广泛地采用,并且有更多的库和框架。
(7)工具支持
以上已经介绍了库、框架和一般市场份额,所以在这一部分中介绍围绕这两种风格的工具。它意味着用于测试、性能/压力测试和文档的工具。
①自动化测试
首先,在REST的情况下,用于构建自动化测试的工具被构建到不同的库和框架中,或者是为了这个唯一目的而构建的独立工具,比如REST-assured。
在gRPC的情况下,可以生成一个stub并将其用于测试。如果想要更严格,可以将生成的客户端作为一个单独的应用程序使用,并将其用作对实际服务进行测试的基础。
关于gRPC的外部工具支持,需要知道:
- Postman程序对gRPC的支持。
- 在IDE中使用的JetBrains HTTP客户端也可以通过一些最低配置来支持gRPC。
- 结果一:REST获得胜利。然而,gRPC的情况似乎有所改善。
②性能测试
在这里,REST具有显著的优势,因为像JMeter或Gatling这样的工具使REST API的压力测试变得相当容易。
不幸的是,gRPC没有这样的支持。在当前的Gatling版本中包含了gRPC插件,所以情况似乎正在好转。
然而到目前为止,只有一个名为ghz的非官方插件和库。这些都很好;它只是与REST的支持级别不同。
- 结果二:REST的胜利;然而,gRPC的情况似乎有所改善。
③文档
在API文档方面,随着OpenAPI和Swagger在整个行业被广泛采用并成为事实上的标准,REST再次取得了胜利。几乎所有用于REST的库都能够以最小的努力或开箱即用的方式公开Swagger文档。
不幸的是,gRPC没有这样的文档。
然而,问题是gRPC是否需要这样的工具。gRPC在设计上比REST更具描述性,因此可能需要其他文档工具。
一般来说,带有API描述的.proto文件比负责编写REST API代码的代码更具声明性和紧凑性,因此可能不需要gRPC提供更多文档。
- 结果三:REST的胜利。然而,关于gRPC文档的问题是开放的。
④总体结果
这是REST的一次重大胜利。
总结
以下是gRPC和REST比较的最终得分表。
得出的结论是:gRPC和REST的比较结果平分秋色,各有三场胜利,没有明显的赢家。
因此没有什么灵丹妙药,用户只需要考虑哪些类别可能对应用程序最重要,然后选择在大多数类别中获胜的方法。
本文作者表示,如果可以的话,他会尝试使用gRPC,因为gRPC在他的上一个项目中运行得非常好。它可能是比原有的REST更好的选择。
原文gRPC vs REST:Comparing Approaches for Making APIs,作者:Bartłomiej Żyliński