分布式系统远程调用:HTTP+RESTful 和 RPC 深度解剖
摘要: 远程调用是分布式系统实现服务间通信的基础。当前主流的两种远程调用是HTTP+RESTful 和 RPC(远程过程调用)。这篇文章从原理、工作机制、核心特点、优缺点以及适用场景等维度进行详细阐述和对比。
一、引言
分布式系统把一个庞大的应用拆解成多个独立部署、独立运行的服务单元,这些服务单元通过网络协同工作,共同完成复杂的业务逻辑。这种架构模式,微服务、SOA(Service-Oriented Architecture)、云原生应用都把服务间通信作为核心。
分布式环境的服务不运行在同一个内存空间,而是部署在不同的服务器、不同的数据中心,由不同的团队开发和维护。所以,就需要远程调用技术,让一个服务(客户端)调用另一个远程服务(服务器)上的功能,不关心底层的网络通信细节,简化分布式应用的开发复杂性。远程调用的效率、可靠性和易用性,直接决定整个分布式系统的性能、稳定性和可维护性。
HTTP+RESTful 远程调用和 RPC(Remote Procedure Call,远程过程调用)是目前使用最广的两大主流架构。
-
HTTP+RESTful 远程调用:以 HTTP 协议为基础,遵循 REST(Representational State Transfer,表述性状态转移)架构风格。把系统的实体抽象为资源,通过统一资源标识符(URI)进行定位,用 HTTP 协议的标准化方法对资源进行操作。RESTful API 因为无状态、容易理解、跨平台兼容性强、跟 Web 生态天然融合,是构建开放 API 和前后端分离应用的首选。
-
RPC(远程过程调用):RPC 像调用本地函数一样调用远程服务。用一套机制隐藏网络通信的复杂性,客户端直接调用远程服务器上的某个函数或方法。RPC 框架用更高效的二进制协议和序列化方式,追求极致的性能和低延迟,所以,在内部服务通信、微服务架构以及对性能要求极高的场景非常合适。
这篇文章对 HTTP+RESTful 远程调用和 RPC 进行深度解剖。从各自的起源、核心原理、工作机制、关键特性入手,了解内部运作方式。
二、RPC(远程过程调用)
RPC,全称 Remote Procedure Call,即远程过程调用。是一种进程间通信(IPC)方式,调用另一个地址空间(另一台机器上)的过程或函数,不用显式编码这个远程调用的细节。核心思想是透明性:让调用远程服务就像调用本地函数一样简单、直观和透明。
分布式系统的一个服务需要另一个服务提供的功能时,RPC 提供一种抽象层,把网络通信、数据序列化/反序列化等复杂性隐藏起来。调用远程服务提供的接口方法,就能实现跨进程、跨机器的功能调用,极大简化分布式应用的开发。
RPC 的工作原理(从客户端到服务器的完整流程):
-
客户端存根 (Client Stub):调用一个远程方法实际上并不是直接调用远程服务器上的方法,而是调用本地的“客户端存根”(或称为代理)。客户端存根是远程方法在本地的一个代理对象,负责拦截这个调用。
- 参数编码(序列化):客户端存根把方法名、参数类型、参数值等信息进行编码(序列化),转换为字节流。这个过程把内存中的数据结构转换成可传输的格式。
- 打包:序列化后的数据和其他必要的元数据打包成一个请求消息。
-
网络传输:客户端存根打包好的请求消息通过底层网络传输协议发送到远程服务器。RPC 支持多种传输协议,有 TCP/IP、UDP,HTTP/2 等。RPC 框架负责建立和维护跟服务器的连接。
-
服务器存根 (Server Stub): 服务器接收到客户端发送的请求消息,首先是服务器端的“服务器存根”(或称为骨架)接收。服务器存根对接收到的字节流进行解包,提取出方法名、参数等信息。然后把字节流反序列化成服务器端能理解的数据结构和参数对象。
-
服务执行:服务器存根根据解析出的方法名和参数,找到服务器上对应的实际服务实现(一个业务逻辑处理类),调用该方法执行业务逻辑。
-
结果返回:实际服务方法执行完毕就把结果返回给服务器存根。
- 结果编码(序列化):服务器存根把执行结果进行编码(序列化),转换为字节流。
- 打包:序列化的结果和请求 ID 等信息打包成一个响应消息。
- 网络传输:响应消息通过网络传输回客户端。
- 客户端存根处理:客户端存根接收到响应开始进行解包和反序列化,最终把结果返回给客户端程序,完成整个远程调用过程。
整个过程中,客户端和服务器的业务逻辑代码都不关心底层的 Socket 编程、数据转换、网络异常处理等复杂细节,实现底层通信的完全透明。
RPC 的核心特点:
- 面向过程/行为:RPC 关注的是调用远程服务器上的特定函数或方法来执行某个操作。核心抽象是“方法”或“过程”,强调的是执行一个动作,而不是操作一个资源。
- 紧耦合:客户端要知道服务器上方法的具体名称、参数类型和返回类型。也就是共享一个接口定义(Interface Definition Language, IDL),任何接口的变更都要客户端和服务端同步更新。
- RPC 基于多种底层传输协议实现,不限于 HTTP。常见的有基于 TCP 的私有协议,或用 HTTP/2 更现代的协议。
- RPC 框架支持多种高效的数据序列化协议,有:Protobuf (Protocol Buffers) 、Hessian、Kryo。比 JSON 或 XML 等文本协议在序列化/反序列化速度和数据包大小上更有优势。
- 用自定义的二进制协议、更底层的传输、优化的序列化/反序列化机制,RPC 框架在性能和效率上优于基于文本的 HTTP 协议。非常适合对延迟和吞吐量有严格要求的内部服务间通信。
- RPC 调用是同步的,即客户端在发出请求后会阻塞等待服务器的响应。这是模拟本地函数调用的行为。不过,现代 RPC 框架也提供了异步调用、回调或基于 Future/Promise 的机制,支持非阻塞的远程调用。
目前,有很多成熟的 RPC 框架,提供完整的客户端/服务器端通信机制、服务发现、负载均衡、容错等功能。
gRPC:Google 开发,基于 HTTP/2 协议和 Protocol Buffers 作为接口定义语言(IDL)和序列化机制。支持多种语言,有高性能、低延迟的特点,是目前最流行的 RPC 框架之一。
Apache Dubbo:一个高性能、轻量级的开源 Java RPC 框架,是阿里巴巴开发然后贡献给 Apache 基金会。提供服务发现、负载均衡、容错等丰富的功能,在 Java 生态中有很广的应用。
Apache Thrift:Facebook 开发,支持多种语言的 RPC 框架。通过 IDL 定义服务接口和数据结构,然后自动生成跨语言的客户端和服务器端代码。
三、HTTP + RESTful 远程调用
REST (Representational State Transfer,表述性状态转移) 是一种架构风格,不是协议或标准。是Roy Fielding 在 2000 年的博士论文《Architectural Styles and the Design of Network-based Software Architectures》中提出的,为分布式超媒体系统(特别是万维网,World Wide Web)提供一套设计原则和约束。REST 的核心目标是提高分布式系统的可伸缩性、可扩展性、可靠性和可维护性,能更适应 Web 环境的特点。
RESTful 是指遵循 REST 架构风格的 Web 服务或 API。把系统的一切都看成是资源,并用统一的接口对这些资源进行操作,实现客户端和服务器之间的松耦合。
REST 架构风格最核心的六大约束:
-
客户端-服务器分离: 客户端和服务器是独立的实体。客户端负责用户界面和用户体验,服务器负责数据存储、业务逻辑和资源管理。客户端不关心数据如何存储,服务器也不关心数据如何呈现给用户。
-
无状态: 服务器不存储任何客户端会话信息。每个客户端的请求都必须包含处理该请求所需的所有信息。服务器不用在请求之间记住客户端的状态。任何服务器都可以处理任何请求,并且故障恢复更容易。
-
可缓存: 客户端可以缓存服务器的响应。服务器的响应必须明确或隐式声明是否可缓存。如果响应是可缓存的,客户端可以在未来直接用缓存的响应,不用再次向服务器发送请求,减少网络流量,提高性能和用户体验。HTTP 协议提供丰富的缓存机制,RESTful API 充分利用了这些机制。
-
统一接口: 这是 RESTful 架构风格的核心特征,也是跟其他分布式系统架构最显著的区别。统一接口可以简化整个系统的架构,提高交互的可见性,让服务更容易被发现和使用。主要有四个子约束:
- 资源识别: 系统的所有信息都抽象为资源,用唯一的 URI (Uniform Resource Identifier) 进行标识。
- 通过表述操作资源:客户端通过资源的表述来操作资源。客户端获取资源的表述,修改它,然后把修改后的表述发送回服务器,服务器再根据表述进行资源状态的更新。
- 自描述消息:每个消息都包含足够的信息来描述如何处理它。
- 超媒体作为应用状态引擎:服务器通过超链接告知客户端下一步的操作。客户端不用预先知道所有可能的 URI,而是通过解析服务器响应中的链接来发现和导航应用的状态。这是 REST 最成熟的级别,但很难完全实现。
-
分层系统: 客户端无法区分是直接连接到最终服务器还是中间层(比如代理服务器、负载均衡器、API 网关等)。这种分层架构提高系统的可伸缩性、安全性和灵活性,因为可以在不影响客户端和服务器的前提下增加或修改中间层。
-
按需代码 (可选): 服务器可以临时扩展或定制客户端功能。这是一个可选的约束,现代 Web 应用的 客户端通过下载 JavaScript 代码来增强功能。
基于 HTTP + RESTful 规范 + 序列化和反序列化 构成的远程调用流程图:
RESTful API 基于 HTTP 协议实现,充分利用 HTTP 协议的特性来满足上述 REST 约束。
-
RESTful API 把所有可操作的实体都视为资源。每个资源都有一个唯一的 URI。
-
HTTP 方法:用 HTTP 协议定义的标准方法(也称为动词)来表示对资源的不同操作,不是像 RPC 那样把操作封装在方法名中。通过这种方式,HTTP 方法的语义跟 CRUD (Create, Read, Update, Delete) 操作直接对应。
GET:从服务器获取(读取)资源或资源集合。是幂等和安全的。POST:在服务器上创建新资源。主要是提交数据。PUT:完全更新或替换现有资源。如果资源不存在,会创建新资源。是幂等的。PATCH:部分更新现有资源。DELETE:删除指定资源。是幂等的。
-
无状态通信:HTTP 协议本身就是无状态的,每个请求都是独立的,不依赖于之前的请求。这跟 REST 的无状态约束天然契合。服务器不用保存客户端的会话信息,所有的状态信息都由客户端在每个请求中携带。
-
数据格式:用 JSON (JavaScript Object Notation) 或 XML (Extensible Markup Language) 作为数据交换格式。客户端和服务器通过 HTTP 头部中的
Content-Type(请求体类型) 和Accept(期望响应类型) 来协商数据格式。
RESTful API 的实现依赖于 Web 框架提供的能力,这些框架封装 HTTP 请求/响应的处理、路由、序列化/反序列化等功能。
- Web 框架支持:几乎所有的主流后端 Web 框架都提供构建 RESTful API 的强大支持,Java 的 Spring Boot (Spring WebFlux/MVC)、Node.js 的 Express/Koa、Python 的 Django REST Framework/Flask、Go 的 Gin/Echo 等。提供路由、控制器、模型序列化等机制。
- 客户端工具:客户端用各种 HTTP 客户端库(Java 的 RestTemplate/WebClient、Python 的 requests、JavaScript 的 fetch/axios)或命令行工具来跟 RESTful API 进行交互。
- wagger/OpenAPI 等工具可以根据代码或配置文件自动生成 RESTful API 文档。
四、HTTP+RESTful 和 RPC 的比较
| 特性维度 | HTTP + RESTful | RPC (远程过程调用) |
|---|---|---|
| 设计理念 | 面向资源:一切皆资源,通过 URI 标识并操作资源状态。 | 面向行为/过程:调用远程服务器上的特定函数或方法来执行某个操作。 |
| 通信模型 | 基于 HTTP 请求-响应模型,用 HTTP 动词操作资源。 | 模拟本地方法调用,客户端直接调用远程方法。 |
| HTTP 方法 | 使用 HTTP 标准方法 (GET, POST, PUT, DELETE, PATCH) 的语义表示 CRUD 操作。 | 用 GET/POST (或自定义动词)。 |
| URL 设计 | 资源路径。 | 方法路径。 |
| 耦合度 | 松耦合:客户端和服务器统一接口交互,不依赖具体实现。 | 紧耦合:客户端依赖服务接口定义(IDL),接口变更要双方同步。 |
| 可发现性 | 高可发现性:通过 URI 和 HATEOAS原则,客户端可以发现和导航资源。 | 低可发现性:客户端要预先知道服务接口和方法签名。 |
| 数据格式 | 文本格式:用 JSON、XML。 | 二进制格式:用高效二进制序列化,追求性能。 |
| 状态管理 | 无状态:服务器不存储客户端会话信息,每个请求独立。 | 部分 RPC 框架或实现支持有状态会话,但也推荐无状态设计。 |
| 性能开销 | HTTP 头部较大,JSON/XML 解析开销相对较高,HTTP/1.1 存在队头阻塞。 | 二进制协议数据包小,序列化/反序列化快,基于 TCP/HTTP/2 优化。 |
| 易用性/标准化 | 高:基于 HTTP 协议和 Web 标准,浏览器原生支持,工具链丰富。 | 框架特定:依赖特定的 RPC 框架和 IDL。 |
| 安全性 | 用 HTTPS、OAuth2 等 Web 标准安全机制。 | 用 TLS/SSL,框架提供认证授权机制。 |
性能对比:
-
序列化/反序列化效率:HTTP + RESTful (JSON/XML) 用文本格式,解析和生成涉及到字符串操作和大量字符编码/解码效率较低。RPC (二进制协议) 用Protobuf 等二进制序列化协议设计紧凑,数据包小,序列化和反序列化速度极快。
-
传输协议效率:
- HTTP/1.1 (RESTful 常用):HTTP/1.1 存在队头阻塞问题,而且 HTTP/1.1 的头部信息相对冗余,增加传输开销。
- HTTP/2 (gRPC 和现代 RESTful):HTTP/2 引入多路复用机制,同一个 TCP 连接上同时发送多个请求和响应,解决了队头阻塞问题。还支持头部压缩,大大减少头部开销。gRPC 就是基于 HTTP/2 构建的,充分利用了这些优势。现代 RESTful API 也可以通过 HTTP/2 部署来提升性能。
- TCP:很多 RPC 框架直接基于 TCP 协议进行通信,更灵活控制连接和数据流,避免 HTTP 协议的一些额外开销,实现更高的性能。
-
数据包大小: JSON/XML是文本格式,包含字段名等元数据,比二进制格式大。Protobuf 等二进制 通过紧凑的编码方式极大减小了数据包大小。
协议优化:
- HTTP/2 对 RESTful 的优化:RESTful API 跟 HTTP/1.1 紧密结合,但部署在 HTTP/2 之上可以提升其性能。HTTP/2 的多路复用、头部压缩、服务器推送等特性,有效缓解 HTTP/1.1 的一些性能瓶颈。所以,基于 JSON 的 RESTful API,在 HTTP/2 环境下也能获得更好的并发处理能力和更低的延迟。
- gRPC 对 HTTP/2 的利用:gRPC 是专门为 HTTP/2 设计的,充分利用了 HTTP/2 的所有高级特性,包括双向流、多路复用和头部压缩,结合 Protobuf 的高效序列化,让 gRPC 在性能上达到了新的高度,成为高性能微服务通信的首选。
现代的 RESTful API 结合 HTTP/2 也能提供相当不错的性能,但在极限性能场景下,gRPC 基于二进制协议和 HTTP/2 的 RPC 框架仍更胜一筹。
五、如何选择?
RPC 的适用场景:
-
内部微服务通信:微服务架构的服务之间部署在同一个局域网或数据中心内,对通信的效率和速度有较高要求。RPC 框架用二进制协议、多路复用、长连接等技术,能提供比 RESTful 更高的吞吐量和更低的延迟,是内部服务间通信的首选。
-
实时数据处理、在线游戏后端、金融交易系统、物联网数据采集等对响应时间有毫秒级甚至微秒级要求的应用。在这些场景下,RPC 能减少网络传输和序列化/反序列化带来的开销。
-
RPC 框架(特别是 gRPC 和 Thrift)通过接口定义语言(IDL)和代码生成工具,实现跨语言的服务调用。
-
gRPC 原生支持四种流式传输模式(单向流、服务器端流、客户端流、双向流),非常适合要长时间连接、实时数据推送或大量数据分块传输的场景。
-
很多成熟的 RPC 框架内置丰富的服务治理功能,包括服务注册与发现、负载均衡、熔断降级、流量控制、链路追踪等。
HTTP + RESTful 的适用场景:
-
对外开放 API:RESTful API 基于 HTTP 协议,是 Web 的天然组成部分。不用特定的客户端库或框架,任何支持 HTTP 的客户端都可以调用,是构建开放平台和生态系统的首选。
-
前后端分离架构:Web 浏览器和移动应用通过 HTTP 请求和后端服务进行通信。RESTful API 能很好支持这种模式,前端通过 AJAX 或 Fetch API 调用 RESTful 接口获取数据,后端提供数据和业务逻辑。JSON 作为主要数据格式,跟 JavaScript 的兼容性非常好。
-
要跟外部系统进行数据交换或功能协作,RESTful API 是最通用和标准化的选择。大多数企业级应用和云服务都提供 RESTful API 接口。
-
RESTful API 的 URI 结构清晰,HTTP 方法语义明确,JSON 数据格式直观易读,不用特殊的工具或协议知识即可快速上手。
-
对性能要求不极致,但要求通用性和扩展性。
六、结语
RPC (Remote Procedure Call) 像调用本地函数一样调用远程服务。HTTP + RESTful 把系统的一切抽象为资源。
RPC 和 RESTful 的差异:
- RPC 面向行为/过程,执行远程函数;RESTful 面向资源,对资源状态增删改查。
- RPC 是紧耦合的(依赖 IDL),RESTful 是 松耦合。
没有绝对“最好”的远程调用方式,只有“最适合”的。大型分布式系统会 混合使用 RPC 和 RESTful。

643

被折叠的 条评论
为什么被折叠?



