开发接口时,经常会听到这样的问题:"我们用 HTTP,还是用 RPC?"
这句话在团队里通常能被理解,但严格来说,比较的双方并不在同一个层面。HTTP 是一种应用层通信协议;RPC 是 Remote Procedure Call 的缩写,中文叫"远程过程调用",描述的是调用另一台机器上某个功能的方式。 RPC 可以通过 HTTP 实现。 常见的 gRPC 就建立在 HTTP/2 之上。理解了这一点,再看它们的区别,就不容易绕进去。
一、先从一次点餐说起
假设你坐在餐厅里,想点一碗牛肉面。你不用走进后厨,只需要告诉服务员:"来一碗牛肉面,少放辣。"服务员把要求交给后厨,再把做好的面端给你。
用RPC理解:你提出一个明确的操作要求,由另一处的人完成,再把结果交回来。对应到程序里,就是调用远程服务的一个方法,比如点餐(牛肉面, 少辣)。服务员则像 RPC 框架,负责传递要求和返回结果,让你不必亲自处理这些细节。
HTTP 可以类比为餐厅统一使用的订单和回单规则:订单上如何写明要做什么、附带哪些要求,回单上如何表示"已接收"或"菜品售罄"。它让传递信息的双方知道该怎样表达和理解请求、响应。
因此,一次点餐可以同时包含这两件事:你通过服务员请后厨做面,体现了 RPC 的调用思路;服务员和后厨按照统一的订单规则沟通,类似于使用 HTTP。一个描述"怎样调用远处的功能",一个规定"请求和响应怎样表达",可以用在同一次交互中。
二、HTTP:把请求和响应说清楚
HTTP 的全称是 Hypertext Transfer Protocol,中文叫"超文本传输协议"。名字里虽然有"超文本",但它能传递的内容远不止网页,还包括 JSON、图片、文件和二进制数据。例如,查询编号为 42 的用户,可以发送下面的请求。这里用 HTTP/1.1 的文本形式展示,方便阅读:
GET /users/42 HTTP/1.1
Host: api.example.com
Accept: application/json
服务端可能返回:
HTTP/1.1 200 OK
Content-Type: application/json
{"id":42,"name":"小林"}
这里的几个部分各有用途:
GET表示请求获取资源的方式。/users/42指向要访问的资源。200 OK表示请求成功。Content-Type说明响应正文是什么格式。
HTTP 提供了方法、状态码、字段等通用语义,但不会替你决定"用户有哪些字段""订单能否取消"。这些仍然需要业务接口自己约定。也别把 HTTP 和 REST 当成同义词。REST 是一种架构风格,很多 HTTP API 会借鉴它,以资源为中心设计接口;使用了 HTTP,并不意味着接口就符合 REST。
三、RPC:让远程服务像一个可调用的方法
如果用户查询逻辑就在当前程序里,你可能这样写:
user = getUser(42)
后来,用户数据交给独立的用户服务管理。查询需要跨网络完成,RPC 框架通常会提供一个客户端,让调用代码保持相近的形状:
示意代码,具体写法取决于所用框架
user = user_client.getUser(userId=42)
这一行代码背后,实际发生了不少事情:客户端把方法名和参数编码成可传输的数据,发给远程服务;服务端解码后执行相应逻辑,再把结果编码返回。**把内存中的数据转换成可传输格式,叫"序列化";收到数据后还原,叫"反序列化",**一般像我们常用的JSON。
许多 RPC 框架还会根据接口定义生成客户端和服务端骨架。调用方不必为每个接口重复编写组装请求、解析响应的代码。各框架提供的功能不同,不能把某一种框架的能力看成所有 RPC 实现都具备的特性。
四、用同一个需求,看两种接口思路
继续以"查询用户"为例,偏资源风格的 HTTP API,通常会设计成:
GET /users/42
偏 RPC 风格的接口,则更直接地表达动作:
UserService.GetUser(userId=42)
前者让你围绕"用户这个资源"思考,后者让你围绕"查询用户这个操作"思考。RPC 也可以装进普通 HTTP 请求里,例如:
POST /rpc HTTP/1.1
Host: api.example.com
Content-Type: application/json
{"method":"UserService.GetUser","params":{"user_id":42}}
这是一个简化的自定义示例,不代表某项 RPC 标准。它说明,同一个请求可以同时具有两种属性:通信使用 HTTP,业务接口采用 RPC 风格。
五、RPC 一定比 HTTP 快吗?
不能这样下结论。RPC 可能本来就在使用 HTTP,而且性能取决于具体实现。如果拿"HTTP/1.1 + JSON"的接口和"gRPC + Protocol Buffers"的接口比较,结果里会混合很多因素:编码后的数据大小、序列化开销、连接复用方式,以及框架本身的处理成本。业务代码和数据库查询也可能占掉大部分时间。
HTTP/2 本身支持多路复用,这并不是 RPC 独有的能力。普通 HTTP API 也不必只用 JSON。把这些条件换掉,结果可能跟着变化。那么实际项目应该如何选择:
| 你的需求 | 可以优先考虑的方向 |
|---|---|
| 浏览器直接访问、外部开发者接入、方便查看请求 | 常规 HTTP/JSON API |
| 多个内部服务需要共享明确的接口定义、自动生成调用代码 | gRPC 等 RPC 框架 |
| 需要持续传递消息或双向流式通信 | 根据客户端和部署条件,评估 gRPC 等流式方案 |
| 已有系统运行稳定,没有明确的性能或维护问题 | 优先沿用现有方案,避免为了换技术而改造 |
当然,一个系统同时使用多种方案很常见:**浏览器通过 HTTP/JSON 访问业务入口,后端服务之间通过 gRPC 协作,外部合作方则使用公开的 HTTP API。**不同边界有不同需求,不必强求整套系统只用一种方式。
六、小结
HTTP 让通信双方对请求和响应有共同的理解;RPC 让调用方以"调用远程方法"的方式使用另一个服务的能力。