RPC 协议个人学习总结

三葉Leaves Author

参考文献:操作系统中的远程过程调用 (RPC) - GeeksforGeeks

RPC 协议,全称 Remote Procedure Call ,使程序能在网络中运行另一个计算机中的函数,就像在本地运行一样。RPC 隐藏了网络细节,因此开发人员可以像处理普通函数调用一样思考,而无需考虑复杂的网络操作。

其原理如图所示:

RPC 工作原理
RPC 工作原理

其中的 stub 是一种辅助型代码,个人认为它的关键作用是序列化 / 反序列化

在客户端,stub 把你传入的参数(对象/结构体)打包成可以通过网络传输的消息(比如 Protobuf 二进制或 JSON 字符串)。在服务端,则把网络消息还原成参数对象。

RPC vs REST

RPC 和 REST 是两种让计算机程序通过互联网相互通信的方式。它们各有不同,但都很有用。

什么时候用 REST?

REST API 优点在于十分通用,例如一个第三方 API 服务(比如解析 PDF 这种)仅需要提供调用地址即可,但是如果是 RPC,它可能就需要提供 SDK 包等等。

另一个优点是,浏览器完全原生支持 REST API。而对于 RPC 则是受限的,这就像一个工作在笔记本电脑里的高端显卡,会因为散热之类的问题很受限(下文会详述)。

什么时候用 RPC?

微服务之间几乎都是通过 RPC 通信的。设想这样一个场景:

python 生态里有很多强大的 OCR 和 PDF 解析库,于是你用 python 写了一个用于解析 PDF 文件并返回指定字段的服务;

然而你的后端是 go 写的,那么 go 和 python 之间怎么通信最好?这种场景下,我们再对比 RPC 和 REST:

  1. 开发体验

RPC 会利用 IDL (Interface Definition Language,接口定义语言) 定义一个契约(例如一个 .proto 文件),这就像不同语言之间的约定;之后如果 python 那边改了字段,.proto 变了,go 这边会立即报错,把问题暴露在编译阶段,这就不用一直查 swagger 或者 open api 文档。

同时,我们也不用再纠结“这个操作应该是 PUT 还是 PATCH?”、“URL 应该是 /users/1/posts 还是 /posts?user_id=1?”

RPC 是面向动作(Action) 的:createUser、publishPost、banUser。简单直接。

  1. 高效的二进制传输

REST 层是直接使用 JSON 传数据,如果数据量大了,解析 JSON 上就需要消耗算力。而 RPC 使用二进制,能带来显著的性能提升和更小的网络包体积。

对于海量高并发(如每秒百万级请求)或带宽敏感(如IoT设备)的内部微服务通信,RPC 优势巨大。

主流 RPC 方案

1. gRPC (Google Remote Procedure Call)

这是真正的满血 RPC 协议,享受了 RPC 带来的所有优良特性,也是业界标准,尤其是微服务后端。

它是语言无关的。你可以用 Go 写服务端,用 Python 写客户端,用 Java 写另一个微服务。只要大家共享 .proto 文件,就能生成各自语言的代码并完美通信。

其遵循谷歌自己的 Protocol Buffers (Protobuf) 协议。

  • 缺点:

因为浏览器对 HTTP/2 的底层控制能力有限,不能直接由浏览器发起标准的 gRPC 请求,需要  gRPC-Web 之类的代理转换。

  • 适用:

大型企业级应用、跨语言调用(后端 Java/Go)。

2. tRPC (TypeScript RPC)

它不需要 Protobuf,也不需要代码生成。它利用 TypeScript 的类型推断,让前后端共享类型定义

这主要是给全栈 TypeScript 开发者用的,最大的用处就是提升开发体验。

然而,它并非满血 RPC 。因为浏览器环境、fetch API 以及 设计哲学上的差异,很多好的特性它享受不到,例如:

1. 原生 HTTP/2 双向流 (Native Bidirectional Streaming)

  • 客户端可以不断发数据,服务端也可以同时不断回数据(比如实时语音识别、即时游戏同步)。

  • 这种流是在同一个 HTTP 连接上完成的,非常高效。

这是因为浏览器端的 fetch API 和 XHR 并不支持访问 HTTP/2 的底层帧(Frames)和流控制,也不支持在请求体(Request Body)中进行流式发送(Fetch Streams API 正在推进,但兼容性和用法仍有限制)

tRPC 要实现类似功能(Subscription),通常需要降级使用 WebSockets 或者 Server-Sent Events (SSE)。这不仅仅是 API 的限制,更是因为 tRPC 默认是基于标准 HTTP Request/Response (GET/POST) 模型的,而不是基于长连接流模型的。

2. 极高的序列化性能与小体积 (Protobuf vs JSON)

gRPC: 默认使用 Protocol Buffers (Protobuf),这是二进制流,解析速度快、体积小。
而 tRPC 则使用 json,序列化/反序列化(Stringify/Parse)的开销比二进制大得多。

3. HTTP Trailers (尾部 Headers)

gRPC: 严重依赖 HTTP/2 的 Trailers(在响应体发送完毕后发送的 Headers)来传输状态码(gRPC status)和错误信息。

浏览器的 fetch API 长期以来无法读取 HTTP Trailers。这也是为什么原生的 gRPC 无法直接在浏览器运行,必须依靠 gRPC-Web 这样的代理将 Trailers 编码进 Response Body 里,或者依靠 Envoy 代理转译。

总结

如果你在写 TypeScript 全栈单体仓库(Next.js/Remix + Node.js),选 tRPC,开发体验秒杀 gRPC。

如果你在写 微服务架构(多语言、服务间高频调用),或者需要跟移动端/IoT交互,选 gRPC

相关阅读

  • 标题: RPC 协议个人学习总结
  • 作者: 三葉Leaves
  • 创建于 : 2025-12-10 00:00:00
  • 更新于 : 2026-03-16 12:05:05
  • 链接: https://blog.oksanye.com/32024e47e334/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论