HTTP 和 RPC 并非“二选一”的竞争关系,而是不同抽象层次的概念。理解这一点,是掌握现代后端架构设计的重要一步。
一、概念澄清:它们不在同一维度
| 维度 | HTTP | RPC |
|---|---|---|
| 定位 | 传输协议(应用层协议,定义如何传输数据) | 通信范式(应用层协议,远程过程调用,定义如何组织调用逻辑) |
| 类比 | 像“快递公司”(负责把包裹从A送到B) | 像“点餐系统”(你点菜 → 厨房做 → 送回给你,你无需关心怎么做) |
| 关系 | 可作为 RPC 的传输载体(如 gRPC 基于 HTTP/2,JSON-RPC 基于 HTTP/1.1) | 可基于 HTTP、TCP、UDP 等多种协议实现 |
✅ 核心结论:
- 不存在“有了 HTTP 就不需要 RPC”的逻辑。
- 正确说法是:在内部服务通信场景中,为何不直接用 HTTP/REST,而要引入 RPC 框架?
二、为何内部服务通信倾向用 RPC?五大核心动机
虽然 HTTP/REST 简单通用,但在高性能、强类型、大规模微服务场景下,传统 REST 存在明显短板。RPC 框架正是为解决这些问题而生:
1️⃣ 开发体验:像调本地函数一样调远程服务
// REST 风格(手动拼 URL、解析 JSON)
HttpResponse resp = httpClient.get("/users/123?fields=name,email");
User user = JSON.parse(resp.body(), User.class);
// RPC 风格(自动生成客户端 stub)
User user = userService.getUser(123); // ← 看起来就是本地方法调用!
- IDL(接口定义语言)驱动:用
.proto(Protobuf)、.thrift等定义接口,自动生成客户端/服务端代码。 - 编译期类型检查:参数/返回值类型错误在编译时报出,而非运行时 JSON 反序列化失败。
2️⃣ 性能与效率:二进制协议 + 多路复用
| 特性 | HTTP/1.1 + JSON | gRPC (HTTP/2 + Protobuf) | Thrift (自定义二进制) |
|---|---|---|---|
| 序列化 | 文本(冗长,需解析) | 二进制(体积小 3~5 倍) | 二进制(高效) |
| 多路复用 | ❌ 队头阻塞 | ✅ 单连接并发多请求 | ✅ |
| 连接复用 | 有限(Keep-Alive) | 原生支持 | 原生支持 |
| 典型场景 | 浏览器 → 后端 | 微服务间通信 | 高性能内部服务 |
💡 实测数据:Protobuf 序列化比 JSON 快 3
10 倍,体积小 35 倍(尤其对数字/布尔值优势明显)。 > > HTTP连接需要依靠Keep-ALive保持这个连接,而RPC底层有一个连接池,在请求量大的时候,会建立多条连接放在池子里,需要用的时候直接取出,用完放回去,方便下次复用
3️⃣ 服务发现
- HTTP:通过DNS服务器去解析域名背后的IP地址,再注册服务
- RPC:通过自行配置中间服务(如Redis)去获取IP地址和端口信息
4️⃣ 协议演进友好:向后兼容的版本管理
- Protobuf/Thrift 支持字段增删(旧客户端忽略新字段,新客户端兼容旧数据)。
- REST 的 JSON 结构变更易导致客户端解析失败,需谨慎设计版本(如
/v1/users→/v2/users)。
5️⃣ 流式通信支持
-
gRPC 原生支持:
- 服务端流(Server Streaming):如实时推送日志
- 客户端流(Client Streaming):如上传大文件分片
- 双向流(Bidirectional Streaming):如聊天、IoT 设备通信
-
HTTP/1.1 实现流需用 Chunked Encoding,复杂且浏览器兼容差;HTTP/2 支持但生态工具少。
三、HTTP/REST 依然不可替代:适用场景对比
| 场景 | 推荐方案 | 原因 |
|---|---|---|
| 浏览器 ↔ 后端 | HTTP/REST + JSON | 浏览器原生支持,无需额外库 |
| 对外开放 API | HTTP/REST + OpenAPI | 易调试(curl/postman)、易文档化、易被 CDN/网关代理 |
| 异构系统集成 | HTTP/REST | 语言/平台无关性最强 |
| 内部微服务通信 | RPC(gRPC/Dubbo) | 性能、类型安全、服务治理优势明显 |
| 需要缓存/CDN | HTTP/REST | 利用 Cache-Control、ETag 等标准头 |
| 需要 SEO/可链接性 | HTTP/REST | URL 可被搜索引擎索引、可直接分享 |
✅ 现代架构趋势:混合使用
- 对外:RESTful API(供 Web/App 调用)
- 对内:gRPC(服务间高性能通信)
- 网关层:将 REST 转为 gRPC(如 Envoy、Kratos 网关)
四、常见误区澄清
| 误区 | 正解 |
|---|---|
| “RPC 比 HTTP 快” | 不准确。gRPC 快是因为 HTTP/2 + Protobuf,而非“RPC”本身。用 HTTP/2 + Protobuf 实现 REST 同样高效。 |
| “RPC 不安全” | 安全性取决于传输层(TLS)和认证机制,与是否 RPC 无关。gRPC 原生支持 TLS。 |
| “REST 就是 HTTP” | REST 是架构风格(基于资源、无状态、统一接口),HTTP 是实现它的常用协议。 |
| “RPC 不能跨语言” | 主流 RPC 框架(gRPC/Thrift)均支持多语言代码生成。 |
总结:一张图看懂关系
┌─────────────────────────────────────────────┐
│ 应用层通信需求 │
└──────────────┬──────────────────────────────┘
│
┌───────┴────────┐
│ │
┌───▼─────┐ ┌───▼─────┐
│ REST │ │ RPC │ ← 通信范式(如何组织调用)
│ (资源) │ │ (函数) │
└───┬─────┘ └───┬─────┘
│ │
└───────┬────────┘
│
┌───────▼────────┐
│ 传输协议层 │
├────────────────┤
│ HTTP/1.1 │
│ HTTP/2 (gRPC) │ ← RPC 可基于 HTTP/2
│ TCP (Thrift) │
└────────────────┘
💡 记住一句话:
HTTP 是“路”,RPC 是“车”。你可以用 HTTP 这条路跑 REST 这辆车,也可以跑 gRPC 这辆车。选车的标准是:目的地(场景)和载重需求(性能/类型安全)。
扩展:那WebSocket协议呢?
概念澄清:它们解决的是不同问题
| 维度 | HTTP | WebSocket |
|---|---|---|
| 通信模式 | 请求-响应(Request-Response) 客户端发起 → 服务器响应 → 连接关闭 | 全双工双向通信(Full-duplex) 建立连接后,客户端和服务器可随时主动发送消息 |
| 连接生命周期 | 短连接(HTTP/1.1 Keep-Alive 可复用,但仍是请求驱动) | 长连接(一次握手,持续通信,直到主动关闭) |
| 类比 | 对讲机:你说一句 → 对方回一句 → 等待下一轮 | 电话通话:建立连接后,双方可随时插话、实时交流 |
| 协议标识 | http:// 或 https:// | ws:// 或 wss://(wss = WebSocket Secure) |
✅ 核心结论:
WebSocket 不是 HTTP 的替代品,而是为了解决 实时双向通信 场景而诞生的补充协议。
二、HTTP 的局限性:为什么它不适合实时场景?
问题 1:请求必须由客户端发起
客户端:我能问问题吗? → 服务器:可以
客户端:现在有新消息吗? → 服务器:没有
客户端:现在有新消息吗? → 服务器:没有
客户端:现在有新消息吗? → 服务器:有!"Hello"
- 服务器有新数据时,无法主动推送给客户端
- 客户端只能不断“轮询”(Polling),浪费资源
问题 2:轮询方案的缺陷
| 方案 | 描述 | 缺点 |
|---|---|---|
| 短轮询(Short Polling) | 客户端定时发起 HTTP 请求 | 大量无效请求,延迟高(取决于轮询间隔) |
| 长轮询(Long Polling) | 客户端发起请求,服务器挂起直到有数据 | 仍基于请求-响应,连接频繁建立/断开,服务器压力大 |
| SSE(Server-Sent Events) | 服务器可单向推送(基于 HTTP) | 仅支持服务器 → 客户端,客户端无法主动发消息 |
📊 数据对比(假设每秒检查一次新消息):
- 短轮询:每分钟 60 次 HTTP 请求(大部分是空响应)
- WebSocket:1 次握手 + 仅在有数据时传输 → 节省 90%+ 带宽
三、WebSocket 的核心优势
1️⃣ 真正的双向实时通信
握手阶段(基于 HTTP):
客户端:GET /chat HTTP/1.1 + Upgrade: websocket
服务器:101 Switching Protocols(协议升级)
通信阶段(切换到 WebSocket 协议):
服务器:{"type": "message", "text": "有人加入了房间"}
客户端:{"type": "message", "text": "Hello everyone!"}
服务器:{"type": "typing", "user": "张三"} ← 服务器主动推送
客户端:{"type": "reaction", "emoji": "👍"}
- 任意一方可随时发送消息,无需等待对方请求
- 消息延迟 ≈ 网络传输时间(毫秒级)
2️⃣ 协议开销极小
| 协议 | 消息头开销 | 说明 |
|---|---|---|
| HTTP | ~200-800 字节 | 每次请求都带完整 Header(User-Agent、Cookie 等) |
| WebSocket | 2-14 字节 | 帧头极简,适合高频小消息 |
💡 举例:发送 10 字节消息
- HTTP:10 + 500(Header)= 510 字节
- WebSocket:10 + 2(帧头)= 12 字节 → 节省 97% 带宽
3️⃣ 连接复用,资源消耗低
- 一次握手建立连接,长期保持(心跳保活)
- 无需频繁建立 TCP 连接(3 次握手 + TLS 握手)
- 适合海量并发连接场景(如聊天室、在线游戏)
四、WebSocket 与 HTTP 的关系:握手阶段
WebSocket 握手阶段基于 HTTP,之后升级为独立协议:
// 客户端发起握手(HTTP GET)
GET /ws HTTP/1.1
Host: example.com
Upgrade: websocket ← 告诉服务器:我想升级协议
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
// 服务器响应(101 状态码:协议切换)
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
🔑 关键点:
- 使用
Upgrade: websocket头告知服务器意图- 服务器返回 101 Switching Protocols 状态码
- 之后通信不再使用 HTTP 协议,而是 WebSocket 帧协议
五、典型应用场景对比
| 场景 | 推荐协议 | 原因 |
|---|---|---|
| 网页聊天 / 即时通讯 | WebSocket | 消息需实时双向推送,延迟要求低 |
| 在线游戏(如 MMO) | WebSocket | 高频状态同步(玩家位置、技能释放) |
| 股票/行情推送 | WebSocket / SSE | 服务器持续推送价格变动 |
| 协同编辑(如在线文档) | WebSocket | 多人实时同步光标、内容变更 |
| 直播弹幕 / 互动 | WebSocket | 用户发送弹幕 + 服务器广播给所有人 |
| IoT 设备监控 | WebSocket / MQTT | 设备状态实时上报 + 远程控制指令下发 |
| 普通网页浏览 | HTTP | 请求-响应模型完全够用 |
| RESTful API | HTTP | 资源操作(CRUD),无需长连接 |
| 文件上传/下载 | HTTP | 大文件传输,HTTP 工具链成熟 |