RESTful vs RPC vs gRPC 对比
你的团队正在选型微服务通信框架。
后端同事说用 RESTful,简单直观;隔壁组说用 RPC,性能强;架构师推荐 gRPC,说这是未来趋势。
你该听谁的?
今天,我们来彻底搞清楚这三种方案的优缺点,以及如何做出正确的选择。
先理解本质:它们解决的是什么问题?
在说区别之前,我们先搞清楚它们的定位:
┌─────────────────────────────────────────────────────────┐
│ 服务通信协议分层 │
├─────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 应用层:RESTful | RPC | gRPC │ │
│ │ (解决:怎么描述接口、传输什么数据) │ │
│ └─────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 传输层:HTTP/1.1 | HTTP/2 | TCP │ │
│ │ (解决:数据怎么传输) │ │
│ └─────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 编码层:JSON | XML | ProtoBuf | Hessian │ │
│ │ (解决:数据怎么序列化) │ │
│ └─────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────┘RESTful、RPC、gRPC 不是同一个维度的东西:
- RESTful 是一种接口设计风格
- RPC 是一种调用范式
- gRPC 是 HTTP/2 + ProtoBuf + RPC 的技术组合
RESTful:资源导向的设计哲学
核心思想
REST(Representational State Transfer)的核心理念是:everything is a resource。
操作资源,而不是调用方法:
RESTful 风格:
GET /users → 获取用户列表
GET /users/{id} → 获取单个用户
POST /users → 创建用户
PUT /users/{id} → 更新用户
DELETE /users/{id} → 删除用户HTTP 方法语义
| 方法 | 语义 | 幂等 | 安全 |
|---|---|---|---|
| GET | 获取资源 | ✅ | ✅ |
| POST | 创建资源 | ❌ | ❌ |
| PUT | 更新资源(完整) | ✅ | ❌ |
| PATCH | 部分更新 | ❌ | ❌ |
| DELETE | 删除资源 | ✅ | ❌ |
RESTful 的优势
java
// 1. 简单直观,URL 即资源
// 用户接口
GET /api/users/123
POST /api/users
// 订单接口
GET /api/orders
GET /api/users/123/orders // 嵌套资源很自然java
// 2. 浏览器直接访问
// 调试时可以直接在浏览器输入 URL
https://api.example.com/users/123
// 3. 成熟的工具链
// Swagger/OpenAPI 自动生成文档
// Postman、curl 直接测试RESTful 的劣势
性能问题:
- JSON 体积大(包含字段名)
- HTTP/1.1 队头阻塞
- 每次请求都要建立连接
功能限制:
- 不原生支持双向通信
- 不原生支持流式响应
- 类型系统弱RPC:动作导向的调用模式
核心思想
RPC(Remote Procedure Call)的核心理念是:像调用本地方法一样调用远程服务。
java
// 本地调用
User user = userService.getUser(123);
// RPC 调用(看起来完全一样)
User user = userService.getUser(123); // 实际是网络调用RPC 的分类
| 类型 | 代表 | 特点 |
|---|---|---|
| Web RPC | gRPC | HTTP + 二进制序列化 |
| 私有 RPC | Dubbo | TCP + 私有协议 |
| SOA RPC | Thrift | 多语言支持 |
RPC 的优势
java
// 1. 性能高
// 二进制序列化,比 JSON 小 5-10 倍
// TCP 长连接,避免重复握手
// 2. 类型安全
// 定义接口文件,所有语言生成类型
interface UserService {
User getUser(long id);
List<User> listUsers(string department);
}RPC 的劣势
调试困难:
- 二进制不可读
- 网络调用不直观
跨语言成本:
- 需要 IDL(接口定义语言)
- 每次改接口都要重新生成代码
前端不友好:
- 浏览器无法直接调用
- 需要网关转换gRPC:HTTP/2 + ProtoBuf 的强强联合
核心优势
gRPC 解决了 REST 和传统 RPC 的痛点:
┌─────────────────────────────────────────────────────────┐
│ gRPC 解决了什么? │
├─────────────────────────────────────────────────────────┤
│ │
│ RESTful 的问题 gRPC 的解决方案 │
│ ├─ JSON 体积大 → ProtoBuf 体积小 5-10 倍 │
│ ├─ HTTP/1.1 慢 → HTTP/2 多路复用 │
│ ├─ 无流式支持 → 原生四种 RPC 模式 │
│ └─ 弱类型 → 强类型 .proto 定义 │
│ │
│ 传统 RPC 的问题 gRPC 的解决方案 │
│ ├─ 调试困难 → grpcurl 工具 + 反射 │
│ ├─ 跨语言麻烦 → 一套 .proto,多语言生成 │
│ └─ 协议私有 → HTTP/2 标准协议 │
│ │
└─────────────────────────────────────────────────────────┘ProtoBuf 的类型定义
protobuf
// 定义强类型接口
service UserService {
rpc GetUser (GetUserRequest) returns (User);
rpc ListUsers (ListUsersRequest) returns (stream User);
}
message GetUserRequest {
int64 id = 1; // 强类型,不能传字符串
}
message User {
int64 id = 1;
string name = 2;
string email = 3;
}HTTP/2 的多路复用
一次连接,并行请求:
┌─────────────────────────────────────────────────────────┐
│ HTTP/2 连接(多路复用) │
├─────────────────────────────────────────────────────────┤
│ │
│ Stream 1: GET /users/1 ────────────────────→ │
│ Stream 2: POST /users ───────→ ←────────── │
│ Stream 3: GET /orders ──→ ←── │
│ │
│ 一个连接,并行传输! │
│ │
└─────────────────────────────────────────────────────────┘选型决策树
┌─────────────────────────────────────────────────────────┐
│ 通信协议选型 │
├─────────────────────────────────────────────────────────┤
│ │
│ 需要浏览器直接访问? │
│ │ │
│ ├─── Yes ──→ RESTful │
│ │ │
│ └─── No ───→ 需要高性能微服务通信? │
│ │ │
│ ├─── Yes ──→ gRPC │
│ │ │
│ └─── No ───→ 团队技术栈偏好? │
│ │ │
│ ├─── Java 技术栈 ──→ Dubbo │
│ │ │
│ └─── 多语言 ────────→ gRPC │
│ │
└─────────────────────────────────────────────────────────┘详细选型建议
| 场景 | 推荐方案 | 原因 |
|---|---|---|
| 前后端分离 | RESTful | 浏览器友好,工具链成熟 |
| 微服务内部 | gRPC 或 Dubbo | 高性能,支持多语言 |
| 高并发低延迟 | gRPC | HTTP/2 + ProtoBuf |
| 多语言微服务 | gRPC | 一套 IDL,多语言生成 |
| 快速迭代内部系统 | RESTful | 简单,改动方便 |
| Java 同构系统 | Dubbo | Spring 生态完善 |
| 外部 API 开放平台 | RESTful | 通用,生态好 |
| 实时流处理 | gRPC | 原生支持双向流 |
| 移动端调用后端 | gRPC | 高性能,省流量 |
对比总结
┌─────────────────────────────────────────────────────────────┐
│ 三大方案全面对比 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┬──────────────┬──────────────┐ │
│ │ RESTful │ RPC │ gRPC │ │
│ ├──────────────┼──────────────┼──────────────┤ │
│ │ HTTP/1.1 │ TCP/HTTP │ HTTP/2 │ │
│ │ JSON/XML │ 二进制 │ ProtoBuf │ │
│ ├──────────────┼──────────────┼──────────────┤ │
│ │ 低 │ 高 │ 最高 │ 性能 │
│ ├──────────────┼──────────────┼──────────────┤ │
│ │ 强 │ 弱 │ 强 │ 类型安全 │
│ ├──────────────┼──────────────┼──────────────┤ │
│ │ 好 │ 差 │ 中 │ 可读性 │
│ ├──────────────┼──────────────┼──────────────┤ │
│ │ 原生 │ 需要网关 │ 需要 gRPC-Web│ 浏览器支持 │
│ ├──────────────┼──────────────┼──────────────┤ │
│ │ 无 │ 无 │ 有 │ 流式支持 │
│ ├──────────────┼──────────────┼──────────────┤ │
│ │ 大 │ 中 │ 大 │ 生态 │
│ ├──────────────┼──────────────┼──────────────┤ │
│ │ 快 │ 快 │ 陡 │ 学习曲线 │
│ └──────────────┴──────────────┴──────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘混合使用:现实中的最佳实践
实际项目中,往往会混合使用:
┌─────────────────────────────────────────────────────────────┐
│ 混合架构示例 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 用户请求 │
│ ↓ │
│ ┌───────────────┐ │
│ │ API Gateway │ │
│ │ (RESTful) │ ← 外部 API │
│ └───────┬───────┘ │
│ │ │
│ ┌─────────────┼─────────────┐ │
│ ↓ ↓ ↓ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ 用户 │ │ 订单 │ │ 商品 │ │
│ │ 服务 │ │ 服务 │ │ 服务 │ │
│ │ gRPC │ │ gRPC │ │ gRPC │ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ │ │ │ │
│ └─────────────┼─────────────┘ │
│ ↓ │
│ ┌───────────────┐ │
│ │ 数据服务 │ │
│ │ (内部 RPC) │ │
│ └───────────────┘ │
│ │
│ 架构说明: │
│ - 外部访问:RESTful(通过 API Gateway) │
│ - 内部通信:gRPC(高性能) │
│ - 协议转换:Gateway 做 REST → gRPC 转换 │
│ │
└─────────────────────────────────────────────────────────────┘Spring Cloud Gateway + gRPC
yaml
# Gateway 配置:RESTful → gRPC 转换
spring:
cloud:
gateway:
routes:
- id: user-service
uri: grpc://user-service:8080
predicates:
- Path=/api/users/**
filters:
- StripPrefix=2 # 去掉 /api 前缀面试追问方向
- 为什么 gRPC 性能比 REST 高这么多?具体在哪些层面优化了?
- gRPC 的反射机制是什么?用来做什么?
- 在微服务架构中,怎么让 gRPC 和现有 REST 服务共存?
- gRPC 的负载均衡怎么做?客户端负载均衡和代理负载均衡各有什么优缺点?
总结
没有最好的方案,只有最适合的方案:
┌─────────────────────────────────────────────────────────┐
│ 选型决策总结 │
├─────────────────────────────────────────────────────────┤
│ │
│ RESTful: │
│ ✓ 浏览器友好,工具链成熟 │
│ ✓ 学习成本低,易于调试 │
│ ✓ 适合外部 API、CRUD 操作 │
│ │
│ RPC(Dubbo): │
│ ✓ Java 生态完善 │
│ ✓ 与 Spring 深度集成 │
│ ✓ 适合 Java 同构系统 │
│ │
│ gRPC: │
│ ✓ 性能最优(HTTP/2 + ProtoBuf) │
│ ✓ 多语言支持好 │
│ ✓ 原生流式支持 │
│ ✓ 适合微服务内部通信、高性能场景 │
│ │
│ 现实建议: │
│ - 外部 API → RESTful │
│ - 内部通信 → gRPC │
│ - Java 同构 → Dubbo │
│ - 都可以 → 协议转换网关统一出口 │
│ │
└─────────────────────────────────────────────────────────┘理解每种方案的本质和适用场景,才能做出正确的技术决策。
