在很长的一段时间,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 | { |
等效的序列化 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/