Communication
RPC 与 REST
RPC 与 REST 的设计与对比
Source: Crack the system design interview
在 RPC 中,客户端会去调用另一个地址空间(通常是一个远程服务器)里的方法。调用代码看起来就像是调用的是一个本地方法,客户端和服务器交互的具体过程被抽象。远程调用相对于本地调用一般较慢而且可靠性更差,因此区分两者是有帮助的。热门的 RPC 框架包括 Protobuf、Thrift 和 Avro。
RPC 是一个“请求-响应”协议:
- 客户端程序 ── 调用客户端存根程序。就像调用本地方法一样,参数会被压入栈中。
- 客户端 stub 程序 ── 将请求过程的 id 和参数打包进请求信息中。
- 客户端通信模块 ── 将信息从客户端发送至服务端。
- 服务端通信模块 ── 将接受的包传给服务端存根程序。
- 服务端 stub 程序 ── 将结果解包,依据过程 id 调用服务端方法并将参数传递过去。
RPC 调用示例:
GET /someoperation?data=anId
POST /anotheroperation
{
"data":"anId";
"anotherdata": "another value"
}
RPC 专注于暴露方法。RPC 通常用于处理内部通讯的 performance 问题,这样你可以手动处理本地调用以更好的适应你的情况。
当以下情况时选择本地库(也就是 SDK):
- 你知道你的目标平台。
- 你想控制如何访问你的“逻辑”。
- 你想对发生在你的库中的错误进行控制。
- performance 和终端用户体验是你最关心的事。
遵循 REST 的 HTTP API 往往更适用于公共 API。
缺点:RPC
- RPC 客户端与服务实现捆绑地很紧密。
- 一个新的 API 必须在每一个操作或者用例中定义。
- RPC 很难调试。
- 你可能没办法很方便的去修改现有的技术。举个例子,如果你希望在 Squid 这样的 cache 服务器上确保 RPC 被正确 cache的话可能需要一些额外的努力了。
表述性状态转移(REST)
REST 是一种强制的客户端/服务端架构设计模型,客户端基于服务端管理的一系列资源操作。服务端提供修改或获取资源的接口。所有的通信必须是无状态和可 cache 的。
RESTful 接口有四条规则:
- 标志资源(HTTP 里的 URI) ── 无论什么操作都使用同一个 URI。
- 表示的改变(HTTP 的动作) ── 使用动作, headers 和 body。
- 可自我描述的错误信息(HTTP 中的 status code) ── 使用状态码,不要重新造轮子。
- HATEOAS(HTTP 中的 HTML 接口) ── 你的 web 服务器应该能够通过浏览器访问。
REST 请求的例子:
GET /someresources/anId
PUT /someresources/anId
{"anotherdata": "another value"}
REST 关注于暴露数据。它减少了客户端/服务端的耦合程度,经常用于公共 HTTP API 接口设计。REST 使用更通常与规范化的方法来通过 URI 暴露资源,通过 header 来表述并通过 GET、POST、PUT、DELETE 和 PATCH 这些动作来进行操作。因为无状态的特性,REST 易于横向扩展和隔离。
缺点:REST
- 由于 REST 将重点放在暴露数据,所以当资源不是自然组织的或者结构复杂的时候它可能无法很好的适应。举个例子,返回过去一小时中与特定事件集匹配的更新记录这种操作就很难表示为路径。使用 REST,可能会使用 URI 路径,查询参数和可能的请求体来实现。
- REST 一般依赖几个动作(GET、POST、PUT、DELETE 和 PATCH),但有时候仅仅这些没法满足你的需要。举个例子,将过期的文档移动到归档文件夹里去,这样的操作可能没法简单的用上面这几个 verbs 表达。
- 为了渲染单个页面,获取被嵌套在层级结构中的复杂资源需要客户端,服务器之间多次往返通信。例如,获取博客内容及其关联评论。对于使用不确定网络环境的移动应用来说,这些多次往返通信是非常麻烦的。
- 随着时间的推移,更多的字段可能会被添加到 API 响应中,较旧的客户端将会接收到所有新的数据字段,即使是那些它们不需要的字段,结果它会增加负载大小并引起更大的 latency。
RPC 与 REST 比较
| 操作 | RPC | REST |
|---|---|---|
| 注册 | POST /signup | POST /persons |
| 注销 | POST /resign { "personid": "1234" } | DELETE /persons/1234 |
| 读取用户信息 | GET /readPerson?personid=1234 | GET /persons/1234 |
| 读取用户物品列表 | GET /readUsersItemsList?personid=1234 | GET /persons/1234/items |
| 向用户物品列表添加一项 | POST /addItemToUsersItemsList { "personid": "1234"; "itemid": "456" } | POST /persons/1234/items { "itemid": "456" } |
| 更新一个物品 | POST /modifyItem { "itemid": "456"; "key": "value" } | PUT /items/456 { "key": "value" } |
| 删除一个物品 | POST /removeItem { "itemid": "456" } | DELETE /items/456 |
资料来源:你真的知道你为什么更喜欢 REST 而不是 RPC 吗