为什么 RPC 要比 HTTP 更快?
结论先行:快慢不取决于“叫 RPC 还是叫 HTTP”,而取决于四个具体开销——报文体积、序列化成本、连接复用方式、协议栈层次。二进制 RPC 相对“HTTP/1.1 + JSON”确实更快;但换成 HTTP/2 + Protobuf 的 gRPC,差距会缩小到很小,此时“RPC 更快”就不再成立。
一、四种开销逐项拆
| 开销项 | HTTP/1.1 + JSON | 二进制 RPC(如 Dubbo 协议) |
|---|---|---|
| 报文头 | 文本头,常见 200~800 字节,每次请求都带 | 16 字节定长头(魔数、标志、状态、请求 ID、长度) |
| 序列化 | JSON 文本,字段名重复,体积大,解析要建对象树 | Hessian2/Protobuf 二进制,按字段序号编码,体积小 |
| 连接 | 短连接或连接池,受 max connections 限制 | 单一长连接多路复用,按请求 ID 匹配响应 |
| 层次 | 应用层文本解析 + 完整 HTTP 语义 | 自定义协议,跳过通用 Web 语义 |
一次 100 字节的业务载荷,JSON 版本实际传输可能 400 字节以上,二进制版本可能 120 字节,网络与 GC 压力相差数倍。这就是高 QPS 下差距被放大的原因。
HTTP/1.1 + JSON
┌──────────────────────────────┬───────────────────────┐
│ 请求行 + 十余个文本头(~500B) │ {"id":1,"name":"张三"}│
└──────────────────────────────┴───────────────────────┘
Dubbo 协议 + Hessian2
┌────┬────┬────┬──────────┬─────────┬──────────────┐
│magic│flag│status│ requestId │ length │ body(二进制) │
│ 2B │ 1B │ 1B │ 8B │ 4B │ ~120B │
└────┴────┴────┴──────────┴─────────┴──────────────┘
二、长连接多路复用为什么关键
HTTP/1.1 的管线化(pipelining)在浏览器侧基本被废弃,因为队头阻塞(Head-of-Line Blocking)严重;实际靠连接池并发,连接数是瓶颈。Dubbo 协议在一条 TCP 连接上并发发多个请求,用 requestId 把响应配回对应的 Future:
连接 A ──┬── req#1 ──┐
├── req#2 ──┤ Provider 处理完谁先回谁
└── req#3 ──┘
◄── resp#2 ── 按 ID 唤醒 Future#2
连接数从“并发数”降到“服务实例数”,TCP 握手、慢启动、TIME_WAIT 全部省掉。
三、什么时候 RPC 并不更快
- 跨语言、跨团队、对外暴露的接口,gRPC(HTTP/2 + Protobuf)与 Dubbo 性能同量级,选型更应看生态。
- 大报文场景(文件、批量数据),带宽是瓶颈,序列化优势被摊薄。
- 服务端处理耗时远大于网络耗时(如 200ms 的数据库查询),协议差异可以忽略。
# Dubbo 侧启用长连接与连接数控制
dubbo:
protocol:
name: dubbo
port: 20880
threads: 200 # 业务线程池
consumer:
connections: 1 # 每提供者一条共享长连接
lazy: true # 首次调用才建连
# 对照:HTTP 客户端连接池
http:
client:
max-connections: 200
max-per-route: 50
keep-alive: true
常见追问
- 追问:那为什么很多公司还是用 HTTP?通用、可读、易调试、网关与监控生态成熟,性能瓶颈往往不在协议。
- 追问:HTTP/2 的多路复用和 Dubbo 的长连接有何不同?HTTP/2 是标准化的流(stream)与帧(frame)机制,天然支持头部压缩 HPACK;Dubbo 协议是私有的定长头 + 请求 ID,更简单,但需要自家客户端。
- 追问:序列化对性能影响有多大?通常比协议头更大。文本 JSON 的体积与解析开销在高频小对象场景下往往是主要成本。
小结
- 差异来自报文体积、序列化、连接复用、协议层次,而非“RPC”这个名字。
- 长连接 + 请求 ID 多路复用省掉连接管理成本,是高 QPS 下的主要收益。
- gRPC 这类 HTTP/2 方案已经把差距抹平,选型要看跨语言与生态。
- 服务端处理耗时占大头时,优化协议几乎没有意义。