贫瘠之地

华北无浪漫,死海扬起帆
多少个夜晚,独自望着天

0%

REST VS gRPC

在很长的一段时间,REST 都是一种且唯一一种构建 API 的 “标准”;它在某种程度上取代了 SOAP,后者是一个 “太多 XML” 的丑陋烂摊子

但是近些年新的选择出现了,2015 年,Facebook 向公众发布了 GraphQL,2016 年谷歌紧随其后发布了 gRPC

在本文中,我们将关注仍然被广泛使用的后者,并将其与 REST 进行比较

概述

下表将概述所讨论的要点,并显示 REST 和 gRPC 的亮点

Toic REST gRPC
Standardization 无标准 定义完善
Paradigm 基于资源 RPC
Service modes 只有 unary Unary, client streaming, server streaming 和 bidirectional streaming
Requirements 各种 HTTP 版本,JSON 解析 HTTP/2, 依赖语言实现
API design 代码第一 设计第一
Default data format JSON Protobuf
Web browser support 原生 gRPC web, via workarounds
Tools 更成熟 语言支持各不相同,有些具有出色的实现

标准化

REST 其中之一的缺点就是缺乏标准化,因为 REST 与其说是 API 标准(standard),不如说是一种范式(paradigm),许多人在谈论它时都有不同的含义

大多数情况下,所谓的 “REST API” 更倾向于基于 HTTP 协议和 JSON 格式的 API,但是使用 XML 代替 JSON 仍然会使 API 符合 RESTful 规范,尽管这种方式没有被广泛使用;REST 这个术语甚至与 HTTP 无关,在使用 REST API 时,这可能会导致很多混乱,例如使用者可能会自动期望某些 REST API 端点的幂等性或缓存性,即使没有明确定义

相比之下,gRPC 定义完善;例如, gRPC implementation over HTTP/2 非常详细

基本区别

REST 和 gRPC 的规范是不同的

REST 中一切都以资源为中心,这些资源可以被检索和操作;如果我们以一本书作为示例资源,REST API 通常会提供以下接口:

  • GET /books 获取所有书籍,可能带有用于筛选项和分页相关参数
  • GET /books/{id} 获取特定的书
  • POST /books 创建一本书
  • DELETE /books/{id} 删除一本书

大多数基于 HTTP 的 REST API 都遵循这种模式,虽然效果不错,但是依然存在难以用这样的规范表达的情况

举一个例子,如果我想创建多本书,而不想为每本书重复调用 POST/books(出于性能、幂等性或其他原因),该如何处理;我是否应该定义 POST/books/batch 接口,这仍然符合 RESTful 吗?虽然在技术上很容易解决,但开发人员之间经常会因为存在争议而进行讨论

而 gRPC 是一个RPC框架。它以服务方法为中心;如果我们以图书 API 为例,使用 gRPC,我们将使用以下方法创建 BookService

  • GetBooks()
  • GetBook()
  • CreateBook()
  • DeleteBook()

可以随心所欲地命名这些方法,并需要我们需要的任何参数;如果我们现在想添加一个方法来创建多本书,没有什么能阻止我们添加 CreateBooks() 方法

gRPC 在设计 API 时提供了更多的 “自由”,因为有更少的(自我强加的)限制

服务模式

gRPC 支持四种服务方式:

  • Unary:发送一个请求,接受一个响应
  • Server steaming:发送一个请求,接收多个响应
  • Client streaming:发送多个请求,接收一个响应
  • Bidirectional streaming:发送多个请求,接收多个响应

这是 gRPC 相比只支持 Unary 方式的 REST 非常好的优势,在 REST API 中支持其他服务模式需要使用不同的协议,例如 server-sent events 或 websocket,这并不是非常 “RESTful”

依赖

REST API 经常只工作在各种 HTTP 版本之上,只要编程语言有 HTTP 客户端和 JSON 解析器,处理 REST 的 API 是轻而易举的

gRPC 明确需要 HTTP/2 协议的支持;近些年这种问题越来越少,因为大多数代理和框架都增加了对 HTTP/2 的支持;不过需要注意,由于 gRPC 需要生成代码(用于创建客户端或服务器存根),因此并不是所有编程语言都支持

API 设计

REST API 通常是其实现的结果,称为 “代码优先(code-first)”;虽然可以先用 OpenAPI 进行设计,然后生成服务器存根,但这并不是许多开发人员采用的方法;如果有一个 OpenAPI 定义,那么 OpenAPI 定义很可能是从 API 实现生成的;因此 API 定义与实现紧密耦合,错误地更改模型可能会导致无意中破坏 API

gRPC 使用不同的方法,在实现 API 之前必须定义 API,称为 “设计优先(design-first)”,然后根据这个 API 定义生成客户端和服务器存根,这需要提前进行,因为不能直接实现 API

这两种方法各有利弊。通常的 REST API 方法允许更快的迭代,因为服务器始终是真实接口的来源;而使用 gRPC,需要在调整实现之前首先更改 API 的定义可能会比较烦人,但是它通过显式定义 API 带来了一些安全优点

数据格式

REST 和 gRPC 都会使用不同的格式来进行数据传输;大部分 REST API 使用 JSON,gRPC 则会默认使用 Protocol Buffers(Protobuf),让我们来比较这两者

JSON 对数据类型的支持有限,也有一些怪癖(例如大数字需要表示为字符串);它是一种文本格式,便于阅读;字段名是序列化的,这会占用一些空间,在一些编程语言中需要使用反射来反序列化 JSON 格式的数据,会比较慢

如上所述,gRPC API 的消息类型首先被定义为 Protocol Buffers;对于受支持的编程语言,可以自动生成(反)序列化消息的代码;并且由于它是一种二进制格式,不需要序列化字段名,因此它使用的空间比等效的 JSON 消息更少;当然缺点是不再具备人类可读性,需要 Protobuf 定义来反序列化消息,可能会对开发人员造成一些阻碍

下面的 JSON 示例将占用大约 66 个字节(去掉空格)

1
2
3
4
5
6
7
8
9
10
11
12
{
"persons": [
{
"name": "Max",
"age": 23
},
{
"name": "Mike",
"age": 52
}
]
}

等效的序列化 protobuf 信息仅使用 19 个字节

1
0x0A070A034D617810170A080A0448616E731034

大型数据

Protobuf 设计用于序列化和反序列化内存中的数据,因此不建议使用 Protobuf / gRPC 传输大型数据;大多数 gRPC 实现对单个消息的默认限制为 4MB

使用 REST API 处理大数据量(例如文件上传)是相当直接的,接收到的文件可以被视为流,只需使用很少的内存

这在 gRPC 中并非不可能,但需要更多的手动操作:文件必须在发送方分为几个部分,然后每个块将作为单独的数据通过客户端流传输方法发送到服务器,服务器接收每个块,并可以从中构造数据流,从而产生与 REST API 类似的行为,尽管需要付出更多努力

浏览器兼容性

这就是 REST 真正的闪光点,它由 Web 浏览器天然支持,因此可以轻松地使用 Web 应用程序中的 REST API

gRPC 不直接由浏览器支持,因为它需要明确的 HTTP/2 支持和访问某些 HTTP/2 功能,而 web 浏览器不提供这些功能

gRPC Web 可以作为一种变通方式;这是 gRPC 协议的一个微小变化,使其可供 web 浏览器使用

对于某些编程语言,框架中已经包含了 gRPC Web 支持;而对于不支持的语言,需要一个代理来将 gRPC 请求转换为 gRPC Web 请求,反之亦然

与不需要特殊依赖的 REST API 相比,gRPC API 在从 Web 上使用时更麻烦

工具

gRPC 和 REST 工具在编程语言和框架之间差异很大,在某些情况下,gRPC 感觉更 “原生(native)”,而在另一些情况下,REST 工具则更高级

对 gRPC 而言适当语言支持更为重要,因为它需要工具来生成客户端和服务器存根(stubs);对于不受支持的编程语言则就不那么幸运了

REST API 的客户端总是可以手动创建的,虽然可能需要一些努力;存在从 Open API 定义创建 REST 客户端的工具,但与 gRPC 相比,对开发人员的经验要求更少

由于 REST API 存在的时间要长得多,因此存在更多有助于构建、测试和部署 REST API 的工具;它们的功能通常比 gRPC 工具更高级

这也是我们构建 Kreya 的主要原因之一,它试图成为最好的 gRPC GUI 客户端(同时也支持 REST)

总结

REST 和 gRPC 都有各自的优点和缺点

从 Web 应用中处理 REST API 通常来说更简单;此外 REST API 使用更广泛,这使得一些开发人员使用它更容易,因为他们可能不了解 gRPC

在我看来,gRPC 在服务器到服务器的通信(例如微服务之间)方面无疑具有优势,能够共享准确的 API 定义并用多种编程语言创建 API 客户端,这是一个巨大的胜利

对于 “我应该使用 REST 还是 gRPC” 的问题没有标准答案,有些 API 可能具有独特的情况,gRPC 或 REST 其中一个可能更适合,或者程序员对其中一者的使用更顺手、更熟练

所有这些都是理由,所以每个人都应该自己决定使用哪种技术

参考

https://kreya.app/blog/rest-vs-grpc/