
RPC(Remote Procedure Call)和 RESTful API 是两种主流的分布式系统间通信架构风格,它们在设计理念、实现方式和使用场景上有显著区别:
核心区别简述:
•RPC: 核心思想是 像调用本地函数一样调用远程服务。它关注动作和过程(做什么操作)。•RESTful: 核心思想是将远程资源当作网络上的实体来操作。它关注资源(操作什么)和标准化的操作方式(如何操作)。
特性 | RPC (Remote Procedure Call) | RESTful API (Representational State Transfer) |
|---|---|---|
核心抽象 | 方法/函数调用 (getUser, createOrder) | 资源 (/users/{id}, /orders) |
关注点 | 动作/行为 (做什么操作) | 资源 (操作什么对象) |
设计哲学 | 面向过程/面向对象 (强调远程调用) | 面向资源 (强调资源的表述和状态转移) |
协议基础 | 不限定底层协议 (常用 TCP, HTTP, 或自定义协议) | 强依赖 HTTP 协议 (利用其语义和特性) |
操作方式 | 自定义方法名 (每个服务定义自己的函数接口) | 标准 HTTP 方法 (GET, POST, PUT, DELETE, PATCH 等) |
接口定义 | 强契约 (需要 IDL, 如 .proto, .thrift) | 弱契约 (主要依赖 URI 模板和 HTTP 动词约定) |
数据格式 | 多种选择 (Protocol Buffers, Thrift, JSON, XML 等),常用高效二进制格式 | 常用文本格式 (JSON, XML, HTML),JSON 最流行 |
状态管理 | 通常无状态 (但实现上也可以有状态) | 严格无状态 (每次请求包含所有必要信息) |
可发现性 | 低 (需要外部文档或服务发现机制) | 较高 (通过 HATEOAS 原则,资源链接嵌入响应中) |
缓存支持 | 弱 (需要显式实现) | 强 (天然支持 HTTP 缓存机制) |
性能 | 通常更高 (二进制协议紧凑,连接复用优化好) | 通常稍低 (文本协议开销稍大,但 HTTP/2 改善很多) |
灵活性 | 较低 (接口变更需更新客户端/服务端 Stub) | 较高 (客户端只需知道资源 URI 和 HTTP 方法) |
通用性 | 较低 (客户端通常需特定库/Stub) | 非常高 (任何支持 HTTP 的客户端都能调用) |
典型代表 | gRPC, Apache Thrift, Dubbo, .NET Remoting | 基于 HTTP+JSON/XML 设计的 Web API |
主要优势 | 性能高、开发体验统一(强类型)、适合内部服务 | 简单、通用、可缓存、可发现、适合公开 API 和 Web |
主要劣势 | 灵活性较低、通用性较差、调试稍复杂 | 性能略低于二进制 RPC、无强类型约束(易出错) |
虽然现代 HTTP 通过 Keep-Alive 和 HTTP/2 大大改善了连接效率,但与为性能极致优化的 RPC 框架相比,在纯通信效率上通常仍有差距,原因包括:
特性 | 典型 RPC 框架 (如 gRPC) | 典型 RESTful API (HTTP/JSON) | 对性能的影响 |
|---|---|---|---|
底层传输协议 | 优化设计的协议 (如 gRPC 基于 HTTP/2) 或 直接高效二进制协议 (Thrift) | HTTP (通常为 1.1 或 2) | RPC 协议栈通常更精简,更贴近高效通信需求 |
序列化格式 | 高效二进制格式 (Protocol Buffers, Thrift Binary, 等) | 文本格式 (JSON, XML 为主) | 二进制编码体积小、解析快,远超 JSON/XML |
连接管理 | 默认强依赖 TCP 长连接 + 多路复用 | 依赖 HTTP 层 Keep-Alive/HTTP2 复用 | 两者都可长连接复用,但 RPC 框架集成更紧密 |
接口契约/编解码 | 强类型 IDL + 代码生成 | 弱类型 (JSON Schema/Swagger 非强制) | 强类型减少运行时检查/转换开销,提升效率与安全 |
设计目标侧重 | 极致性能、低延迟、高吞吐 (内部服务) | 通用性、互操作性、可读性、可缓存性 (Web/公开 API) | RPC 牺牲通用性换取性能;REST 牺牲部分性能换取通用性 |
•误区纠正: “RPC 是长连接,HTTP (RESTful) 每次都需要三次握手” 是不准确的。
•RPC 常用长连接,但非本质要求。•HTTP/1.1+ 和 HTTP/2 普遍支持连接复用,避免每次握手。
•核心差异:
•RPC 框架(尤其是 gRPC, Thrift)通常选择或设计更底层、更高效的协议栈(常基于 TCP 长连接)并搭配高效的二进制序列化,这是其追求极致内部通信性能的自然结果。•RESTful API 基于 HTTP 协议,HTTP 的设计目标(通用性、可读性)和传统文本序列化(JSON/XML)在纯效率上通常不如优化的 RPC 方案。但 HTTP/2 极大缩小了这一差距。
•现代实践:
•高性能 RPC: gRPC (基于 HTTP/2 + Protobuf) 是典范,它利用 HTTP/2 的长连接和多路复用,同时获得 HTTP 生态兼容性和 RPC 的高效性。•高性能 RESTful: 使用 HTTP/2 + 高效 JSON 库(或 Protocol Buffers over HTTP,但此时更像 RPC 风格)也能获得非常好的性能,尤其适合需要通用性的场景。
•选型考量:
•需要极致性能、强类型、内部服务通信? -> 优先考虑 gRPC 等现代 RPC。•需要最大通用性、浏览器友好、公开 API、利用 HTTP 生态(缓存等)? -> RESTful API (HTTP/2 + JSON) 仍是主流。
1.
抽象层面:
•RPC: 抽象为远程方法调用。开发者定义接口(类似本地接口),客户端调用这些接口,底层框架处理网络通信、序列化等细节。调用 client.GetUser(123) 感觉就像在调用本地函数。•RESTful: 抽象为对资源的操作。服务器暴露的是资源的 URI(如 /users/123)。客户端通过标准的 HTTP 方法(GET, POST, PUT, DELETE)来操作这些资源,表达意图(获取、创建、更新、删除)。操作的是 /users/123 这个资源,使用 GET 方法。
2.
操作语义:
•RPC: 操作语义由自定义的方法名决定。方法名千差万别 (FetchUser, RetrieveCustomer, get_user_info),需要文档说明每个方法是干什么的。•RESTful: 操作语义由标准的 HTTP 方法 (动词) 和 URI (名词) 共同定义。
•GET /users/123 -> 获取 ID 为 123 的用户•POST /users -> 创建一个新用户•PUT /users/123 -> 替换(更新)ID 为 123 的用户•DELETE /users/123 -> 删除 ID 为 123 的用户•PATCH /users/123 -> 部分更新 ID 为 123 的用户 这种标准化使得 API 更易理解和预测。
3.
契约与接口定义:
•RPC: 通常需要强契约,使用 Interface Definition Language (IDL) 如 Protocol Buffers (.proto) 或 Apache Thrift IDL 来严格定义服务接口、方法、参数和返回值的数据结构。工具根据 IDL 生成客户端和服务端代码(Stub/Skeleton),确保类型安全。•RESTful: 没有严格的 IDL 强制要求。契约主要基于:
•URI 模板 (约定资源路径格式,如 /users/{id})•HTTP 方法 (GET/POST/PUT/DELETE/PATCH)•请求/响应格式 (常用 JSON Schema 或 OpenAPI/Swagger 规范来描述,但不是强制的运行时约束) 更依赖约定和文档(如 OpenAPI/Swagger)。
4.
状态管理:
•RPC: 协议本身不强制要求无状态。实现上可以是无状态的(每次调用独立),也可以是有状态的(服务端维护会话状态)。框架或开发者需要自行管理状态。•RESTful: 严格遵循无状态原则。服务器不保存客户端会话状态。客户端发起的每个请求都必须包含服务器处理该请求所需的所有信息(身份认证 Token、上下文等)。状态保存在客户端或资源本身。这使得服务器易于扩展和容错。
5.
可发现性 (Discoverability):
•RPC: 通常可发现性较差。客户端需要预先知道服务端提供的具体方法签名和地址(通常通过服务发现或配置文件)。方法之间缺乏显式的关联关系描述。•RESTful: 通过遵循 HATEOAS (Hypermedia as the Engine of Application State) 原则可以实现高可发现性。服务器在响应中不仅返回请求的资源数据,还返回与该资源相关的、客户端接下来可能执行的操作的链接(Hypermedia Controls)。客户端通过解析响应中的链接来“发现”和导航 API,无需预先知道所有 URI 结构。虽然实践中 HATEOAS 的应用程度不一,但这是 REST 的重要特征。
6.
缓存:
•RPC: 缓存通常不是协议内置功能。需要开发者显式实现缓存逻辑(可能在客户端、服务端或中间件),或者依赖底层传输协议(如 HTTP)的有限缓存支持。•RESTful: 天然支持 HTTP 缓存机制。可以利用 HTTP 头(如 Cache-Control, ETag, Last-Modified, Expires)来指示响应是否可缓存、缓存多长时间、如何进行条件请求验证缓存有效性。这对提升 Web 性能和可伸缩性至关重要。
•
选择 RPC (如 gRPC, Thrift) 当:
•需要极致性能(内部微服务间通信、数据中心内部)。•需要强类型接口和代码生成带来的开发效率、类型安全和一致性。•通信双方环境可控(使用相同语言/框架或能方便生成 Stub)。•操作更偏向动作/命令(如执行一个复杂计算、触发一个任务),而不仅仅是 CRUD。•对通用性和浏览器直接调用要求不高。
•
选择 RESTful API 当:
•构建公开 API 或 Web 应用接口,需要最大程度的通用性和互操作性(任何语言、任何设备都能通过 HTTP 调用)。•可缓存性是关键需求(如内容分发)。•资源模型清晰,操作主要是 CRUD。•希望利用 HTTP 基础设施(代理、缓存、防火墙、监控)。•可发现性和自描述性很重要(尤其是在结合 OpenAPI 和 HATEOAS 时)。•对强类型和极致性能要求相对宽松。
现代趋势:
•gRPC 的崛起: gRPC (基于 HTTP/2 和 Protobuf) 结合了 RPC 的性能优势和 HTTP 的某些通用性(如基于 HTTP/2),特别适合内部微服务通信,也越来越多地用于移动客户端和后端、浏览器(通过 gRPC-Web)。•REST 的普及: RESTful 因其简单性和通用性,仍然是构建公开 Web API 的事实标准。•混合使用: 一个系统内部可以同时使用 RPC 和 RESTful。例如,内部微服务间用高性能 gRPC 通信,对外公开的 API 则提供 RESTful 接口。
理解它们之间的本质区别,有助于你根据具体的应用场景(性能需求、接口性质、客户端类型、开发运维成本等)做出最合适的技术选型。