Skip to content

RPC 与 REST 详解:通信范式选型指南

  RPC 和 REST 是分布式系统中最主流的两种服务通信范式。它们不是非黑即白的选择,而是适用不同场景的两套设计哲学。理解它们的本质差异,才能在微服务架构中做出正确的选型。

一、什么是 RPC?

1.1 核心思想

  RPC(Remote Procedure Call,远程过程调用)的核心思想是:让远程服务调用像调用本地方法一样简单。开发者不需要关心网络通信、序列化、路由等底层细节,只需要调用一个"看起来像本地"的方法。

┌──────────────────────────────────────────────────────────────────┐
│                      RPC 调用过程                                 │
│                                                                  │
│  调用方(Consumer)                  被调方(Provider)            │
│                                                                  │
│  orderService.createOrder(dto)        public Order createOrder   │
│       │                               (OrderDTO dto) {           │
│       │                                       │                  │
│       ▼                                       │                  │
│  ┌──────────┐                                │                  │
│  │ Stub 代理 │ ← 看起来像本地方法              │                  │
│  │ (动态代理)│                                │                  │
│  └────┬─────┘                                │                  │
│       │                                      │                  │
│       │ ① 序列化(DTO → 二进制)              │                  │
│       ▼                                      │                  │
│  ┌──────────┐        网络传输          ┌──────────┐              │
│  │ 网络层    │ ◄─────────────────────► │ 网络层    │              │
│  │ (TCP)    │                         │ (TCP)    │              │
│  └──────────┘                         └────┬─────┘              │
│                                            │                    │
│                                            │ ② 反序列化          │
│                                            ▼                    │
│                                       ┌──────────┐              │
│                                       │ 实际执行  │              │
│                                       │ 方法体   │              │
│                                       └────┬─────┘              │
│                                            │                    │
│                                            │ ③ 序列化返回值      │
│                                            ▼                    │
│  ┌──────────┐        网络传输          ┌──────────┐              │
│  │ 收到响应  │ ◄───────────────────── │ 返回结果  │              │
│  └────┬─────┘                         └──────────┘              │
│       │                                                          │
│       │ ④ 反序列化(二进制 → Order)                               │
│       ▼                                                          │
│  Order result = ...  ← 调用方拿到结果,就像本地方法返回一样        │
└──────────────────────────────────────────────────────────────────┘

1.2 RPC 框架代表

框架语言协议序列化生态
DubboJava自定义 TCP(dubbo://)Hessian2 / Kryo / ProtobufSpring Cloud Alibaba
gRPC多语言HTTP/2ProtobufGoogle 出品,CNCF
Thrift多语言自定义 TCPThrift BinaryFacebook 出品
MotanJava自定义 TCPHessian2 / FastJSON微博出品
SOFARPCJava自定义 TCPHessian2 / Protobuf蚂蚁出品

1.3 Dubbo 代码示例

java
// ===== 接口定义(共享 jar 包) =====
public interface OrderService {
    Order createOrder(OrderDTO dto);
}

// ===== 服务提供者 =====
@DubboService
public class OrderServiceImpl implements OrderService {
    @Override
    public Order createOrder(OrderDTO dto) {
        // 业务逻辑
        return new Order(dto.getId(), "创建成功");
    }
}

// ===== 服务消费者 =====
@Component
public class OrderController {
    @DubboReference  // 注入远程代理,像用本地 Bean 一样
    private OrderService orderService;

    public void create() {
        OrderDTO dto = new OrderDTO(1L, "iPhone");
        Order order = orderService.createOrder(dto);  // 像调用本地方法
    }
}

1.4 gRPC 代码示例

protobuf
// order.proto — 接口定义文件
service OrderService {
  rpc CreateOrder (OrderRequest) returns (OrderResponse);
}

message OrderRequest {
  int64 id = 1;
  string product = 2;
  int32 quantity = 3;
}

message OrderResponse {
  int64 id = 1;
  string status = 2;
}
java
// 服务端
@GrpcService
public class OrderServiceImpl extends OrderServiceGrpc.OrderServiceImplBase {
    @Override
    public void createOrder(OrderRequest req, StreamObserver<OrderResponse> resp) {
        OrderResponse response = OrderResponse.newBuilder()
            .setId(req.getId())
            .setStatus("创建成功")
            .build();
        resp.onNext(response);
        resp.onCompleted();
    }
}

// 客户端
OrderServiceGrpc.OrderServiceBlockingStub stub = OrderServiceGrpc.newBlockingStub(channel);
OrderResponse response = stub.createOrder(OrderRequest.newBuilder()
    .setId(1L)
    .setProduct("iPhone")
    .setQuantity(2)
    .build());

二、什么是 REST?

2.1 核心思想

  REST(Representational State Transfer,表述性状态转移)的核心思想是:把一切抽象为资源(Resource),通过标准的 HTTP 方法操作资源的状态。REST 不是协议,而是一种架构风格。

┌──────────────────────────────────────────────────────────────────┐
│                      REST 调用过程                                │
│                                                                  │
│  调用方(Consumer)                  被调方(Provider)            │
│                                                                  │
│  POST /api/order/create              @PostMapping("/api/order    │
│  Content-Type: application/json           /create")              │
│  Body: {"id":1,"product":"iPhone"}   public Order createOrder    │
│       │                              (@RequestBody OrderDTO dto) │
│       │                                       │                  │
│       ▼                                       │                  │
│  ┌──────────┐                                │                  │
│  │ HTTP 客户端│ ← 构造 HTTP 请求               │                  │
│  │ (HttpClient│                                │                  │
│  │  /OkHttp) │                                │                  │
│  └────┬─────┘                                │                  │
│       │                                      │                  │
│       │ ① JSON 序列化(DTO → JSON 字符串)     │                  │
│       ▼                                      │                  │
│  ┌──────────┐       HTTP/1.1 传输      ┌──────────┐              │
│  │ 网络层    │ ◄─────────────────────► │ 网络层    │              │
│  │ (TCP)    │   HTTP Method + URL     │ (TCP)    │              │
│  │          │   + Headers + Body      │          │              │
│  └──────────┘                         └────┬─────┘              │
│                                            │                    │
│                                            │ ② JSON 反序列化     │
│                                            ▼                    │
│                                       ┌──────────┐              │
│                                       │ Spring MVC│              │
│                                       │ 路由匹配   │              │
│                                       │ 参数绑定   │              │
│                                       │ 执行业务   │              │
│                                       └────┬─────┘              │
│                                            │                    │
│                                            │ ③ 序列化返回值      │
│                                            ▼                    │
│  ┌──────────┐       HTTP 响应         ┌──────────┐              │
│  │ 收到响应  │ ◄───────────────────── │ 返回 JSON │              │
│  │ Status 200│                         │ + Status │              │
│  └────┬─────┘                         └──────────┘              │
│       │                                                          │
│       │ ④ JSON 反序列化(JSON → Order)                           │
│       ▼                                                          │
│  Order order = ...  ← 调用方拿到结果                               │
└──────────────────────────────────────────────────────────────────┘

2.2 REST 的核心约束

约束说明示例
资源标识每个资源有唯一的 URI/api/orders/123
统一接口使用标准 HTTP 方法GET/POST/PUT/DELETE
无状态每个请求包含所有必要信息不依赖服务端 Session
表述资源可以有多种表述形式JSON / XML / HTML
HATEOAS超媒体驱动(可选)响应中包含下一步链接

2.3 REST 风格示例

java
// 接口定义 — 遵循 RESTful 风格
@RestController
@RequestMapping("/api/orders")
public class OrderController {

    // GET    /api/orders/123     → 查询订单
    @GetMapping("/{id}")
    public Order getOrder(@PathVariable Long id) { ... }

    // POST   /api/orders          → 创建订单
    @PostMapping
    public Order createOrder(@RequestBody OrderDTO dto) { ... }

    // PUT    /api/orders/123     → 更新订单
    @PutMapping("/{id}")
    public Order updateOrder(@PathVariable Long id, @RequestBody OrderDTO dto) { ... }

    // DELETE /api/orders/123     → 删除订单
    @DeleteMapping("/{id}")
    public void deleteOrder(@PathVariable Long id) { ... }

    // GET    /api/orders?page=1&size=20 → 分页查询
    @GetMapping
    public Page<Order> listOrders(@RequestParam int page, @RequestParam int size) { ... }
}

三、RPC vs REST 核心对比

3.1 设计哲学

RPC:面向动作(Action-Oriented)
  "我要调用你的 createOrder 方法"
  关注点:做什么操作?传什么参数?
  接口名称就是操作名:createOrder()、cancelOrder()、queryOrder()

REST:面向资源(Resource-Oriented)
  "我要操作 /api/orders/123 这个资源"
  关注点:操作哪个资源?用什么 HTTP 方法?
  URL 代表资源,HTTP 方法代表操作:GET /orders/123、POST /orders

3.2 全方位对比

维度RPCREST
通信协议自定义 TCP 协议(Dubbo)/ HTTP/2(gRPC)HTTP/1.1 或 HTTP/2
序列化格式二进制(Protobuf、Hessian2、Kryo)文本(JSON、XML)
接口定义强契约:共享接口 jar / .proto 文件弱契约:OpenAPI 文档(可选)
调用方式orderService.createOrder(dto) 像本地方法POST /api/orders HTTP 请求
耦合度强耦合(接口 jar 依赖,编译期绑定)弱耦合(仅 HTTP 端点约定,运行时绑定)
跨语言弱(Dubbo 专注 Java,gRPC 可跨语言)强(任何语言都能发 HTTP 请求)
性能高(二进制序列化、长连接、多路复用)中(JSON 文本序列化开销大,HTTP/1.1 队头阻塞)
网络开销小(二进制协议,头部极小)大(HTTP 头部 + JSON 键名重复)
调试难(二进制不可读,需专用工具)易(curl/Postman/浏览器直接查看)
缓存难(协议层面不支持)易(HTTP 缓存头、CDN、反向代理)
负载均衡需注册中心(Dubbo 多层 LB)通用(L4/L7 负载均衡器均可)
流式传输gRPC 原生支持双向流SSE / WebSocket(需额外处理)
超时控制框架内置(可精细控制)HTTP 超时(依赖客户端实现)
服务治理框架内置(Dubbo 有完整治理能力)依赖外部组件(Gateway、Sentinel)
学习成本高(需理解 RPC 框架原理)低(HTTP 是基础技能)
代表框架Dubbo、gRPC、ThriftSpring MVC、OpenFeign、WebClient

3.3 性能对比(实测数据参考)

场景:单次请求,Payload 1KB

RPC(Dubbo + Hessian2):
  TCP 包大小:~1.1KB
  TPS:~50,000/s(长连接)
  平均延迟:~2ms

REST(HTTP + JSON):
  HTTP 包大小:~1.5KB(HTTP 头 + JSON 键名)
  TPS:~20,000/s(HTTP/1.1)
  平均延迟:~5ms

RPC(gRPC + Protobuf + HTTP/2):
  HTTP/2 帧大小:~1.05KB
  TPS:~45,000/s(HTTP/2 多路复用)
  平均延迟:~2.5ms

结论:RPC 性能比 REST 高 2-10 倍,gRPC 最接近 RPC 性能。
      但 REST 性能对大多数业务场景足够了(20K TPS 远超业务需求)。

3.4 序列化效率对比

同一个对象:OrderDTO { id: 12345, product: "iPhone 15 Pro", quantity: 2, price: 8999.00 }

JSON(REST 默认):
{
  "id": 12345,
  "product": "iPhone 15 Pro",
  "quantity": 2,
  "price": 8999.00
}
→ 87 字节

Protobuf(gRPC 默认):
08 39 30 12 0D 69 50 68 6F 6E 65 20 31 35 20 50 72 6F 18 02 ...
→ 35 字节

Hessian2(Dubbo 默认):
43 4A 01 04 6E 61 6D 65 ...
→ 40 字节

结论:二进制序列化体积约为 JSON 的 40-50%,解析速度更是快 3-5 倍。

四、RPC 的深层优势

4.1 接口契约(强类型安全)

java
// RPC:编译期就发现接口不匹配
// 提供者改了方法签名 → 消费者编译报错 → 立即发现
@DubboService
public class OrderServiceImpl implements OrderService {
    @Override
    public Order createOrder(OrderDTO dto) { ... }  // 改了方法名
}

// 消费者:
@DubboReference
private OrderService orderService;
orderService.createOrder(dto);  // ← 编译就报错,不会上线才发现


// REST:运行时才发现路径不匹配
// 提供者改了路径 → 消费者 404 → 上线后才发现
@PostMapping("/api/order/new")  // 原来叫 /api/order/create
public Order createOrder(@RequestBody OrderDTO dto) { ... }

4.2 长连接与多路复用

REST(HTTP/1.1):
  请求1 ────→ 等待响应1 ────→ 收到响应1 ────→ 请求2 ────→ 等待响应2 ────→ 收到响应2
  连接数 = 并发数(每个请求一个连接或排队)

RPC(Dubbo 长连接):
  TCP 连接 ──── 请求1 ────→
            ├── 请求2 ────→    ← 一个连接承载多个请求
            ├── 请求3 ────→
            └── 请求4 ────→
  连接数 = 1(无论多少并发)

gRPC(HTTP/2 多路复用):
  TCP 连接 ──── Stream1 ────→
            ├── Stream2 ────→    ← HTTP/2 多路复用,一个连接多个 Stream
            ├── Stream3 ────→
            └── Stream4 ────→
  连接数 = 1,且每个 Stream 独立,不存在队头阻塞

4.3 服务治理能力

yaml
# Dubbo 内置服务治理配置(无需额外组件)
dubbo:
  provider:
    timeout: 3000              # 超时
    retries: 2                 # 重试
    loadbalance: roundrobin    # 负载均衡
    cluster: failover          # 集群容错
    weight: 100                # 权重
    actives: 200               # 并发限制
    version: 1.0.0             # 版本(灰度发布)
    group: dev                 # 分组

REST 实现这些需要 Gateway + Sentinel + LoadBalancer 等多个组件协同。

五、REST 的深层优势

5.1 跨语言无障碍

REST:
  Java:   restTemplate.postForObject(url, body, Order.class)
  Go:     http.Post(url, "application/json", body)
  Python: requests.post(url, json=body)
  Node:   fetch(url, { method: 'POST', body: JSON.stringify(data) })
  Shell:  curl -X POST url -d '{"id":1}'

  任何语言、任何工具,只要能发 HTTP 请求就能调用。


RPC(Dubbo):
  Java:   orderService.createOrder(dto)  ✅ 原生支持
  Go:     dubbo-go 客户端                  ⚠️ 需要专门的 Go SDK
  Python: dubbo-python 客户端              ⚠️ 需要专门的 Python SDK
  Node:   无官方支持                       ❌ 无法调用

5.2 生态兼容性

                    REST 天然穿透
                    ─────────────→
┌──────┐  ┌──────┐  ┌──────┐  ┌──────┐  ┌──────┐
│ 浏览器 │→│ CDN  │→│ 防火墙 │→│ Nginx │→│ 服务  │
└──────┘  └──────┘  └──────┘  └──────┘  └──────┘
   ✅        ✅        ✅        ✅        ✅

                     RPC 可能被拦截
                    ─────────────→
┌──────┐  ┌──────┐  ┌──────┐  ┌──────┐  ┌──────┐
│ 客户端 │→│ CDN  │→│ 防火墙 │→│ Nginx │→│ 服务  │
└──────┘  └──────┘  └──────┘  └──────┘  └──────┘
   ✅        ❌        ❌        ❌        ✅
         看不懂      可能拦截   需要TCP代理
         自定义协议   自定义协议

5.3 调试和可观测性

bash
# REST:直接 curl 验证
curl -X POST http://order-service/api/orders \
  -H "Content-Type: application/json" \
  -d '{"product":"iPhone","quantity":2}'
# 返回:{"id":12345,"status":"创建成功"}  ← 一目了然

# RPC(Dubbo):需要 telnet 或专用工具
telnet localhost 20880
dubbo> invoke com.example.OrderService.createOrder({"product":"iPhone","quantity":2})
# 返回:二进制数据,常人不可读


# REST:抓包后可读
HTTP/1.1 200 OK
Content-Type: application/json
{"id":12345,"status":"创建成功"} 任何抓包工具都能直接看到

# RPC:抓包后不可读
00 00 00 1A 0A 0E 63 6F 6D 2E 65 78 61 6D 70 6C ... 需要专门解码

5.5 REST 高并发与高可用

  REST 完全可以做到高并发和高可用。 很多人把"REST 比 RPC 慢"误解为"REST 不能高并发",这是两个完全不同的概念。REST 慢的是单次请求延迟,不是并发能力。

5.5.1 高并发:REST 如何做到?

误区:REST = 阻塞 IO = 低并发
真相:REST 是一种通信范式,与 IO 模型无关

REST 的并发能力取决于底层实现:

┌──────────────────────────────────────────────────────────────────┐
│  Servlet 容器(Tomcat)                                           │
│  线程模型:一个请求一个线程                                        │
│  最大连接数:200(默认)                                           │
│  并发能力:中等(可调大线程池)                                    │
├──────────────────────────────────────────────────────────────────┤
│  Reactor Netty(WebClient)                                       │
│  线程模型:事件驱动,少量线程处理大量请求                           │
│  并发能力:数万连接/实例                                           │
│  代表:Spring WebFlux、WebClient                                  │
├──────────────────────────────────────────────────────────────────┤
│  Undertow                                                         │
│  线程模型:NIO + 工作线程池                                        │
│  并发能力:高(Tomcat 的 1.5-2 倍)                               │
└──────────────────────────────────────────────────────────────────┘

REST 高并发的关键手段:

手段说明效果
异步非阻塞WebFlux + WebClient,事件驱动1 台机器数万并发连接
HTTP/2 多路复用一个 TCP 连接承载多个请求消除 HTTP/1.1 队头阻塞
连接池复用 HTTP 连接,避免频繁握手减少 90% 连接开销
水平扩展无状态服务,加机器即可理论上无限扩展
CDN + 缓存静态资源走 CDN,热点数据走缓存大幅减少后端请求
异步处理请求先受理,后台异步处理削峰填谷
高并发 REST 架构:

  用户请求(100K QPS)


  ┌──────────┐
  │   CDN    │ ← 静态资源直接返回,不回源
  └────┬─────┘
       │ 动态请求

  ┌──────────┐
  │  Nginx   │ ← 反向代理 + 负载均衡,10W+ 连接无压力
  └────┬─────┘


  ┌──────────────────────────────────────────┐
  │           Gateway 集群                     │
  │  ┌────────┐ ┌────────┐ ┌────────┐        │
  │  │ 实例 1  │ │ 实例 2  │ │ 实例 3  │  ...  │ ← 水平扩展
  │  └────────┘ └────────┘ └────────┘        │
  └──────────────────────────────────────────┘


  ┌──────────────────────────────────────────┐
  │         业务服务集群(无状态)              │
  │  ┌────────┐ ┌────────┐ ┌────────┐        │
  │  │ 实例 1  │ │ 实例 2  │ │ 实例 3  │  ...  │
  │  │ WebFlux│ │ WebFlux│ │ WebFlux│        │ ← 异步非阻塞
  │  └────────┘ └────────┘ └────────┘        │
  └──────────────────────────────────────────┘


  ┌──────────┐  ┌──────────┐
  │  Redis   │  │   MQ     │ ← 异步削峰
  │  缓存    │  │  队列    │
  └──────────┘  └──────────┘

5.5.2 高可用:REST 如何做到?

REST 天然支持高可用的核心原因:无状态。

REST 的无状态特性:
  每个请求独立,包含所有必要信息(Token、参数)
  任何实例都能处理任何请求
  实例挂了 → 流量自动切到其他实例 → 用户无感知

RPC 的挑战:
  Dubbo 长连接:客户端和特定实例建立了 TCP 连接
  实例挂了 → 连接断开 → 需要重新建立连接 → 短暂不可用
  虽然 Dubbo 有自动重连,但恢复过程有短暂抖动

REST 高可用架构:

                    ┌─────────────────────────────────────┐
                    │         Nginx / Gateway 集群         │
                    │   健康检查 + 自动剔除故障节点         │
                    └──────────┬──────────┬───────────────┘
                               │          │
              ┌────────────────┼──────────┼──────────────────┐
              │                │          │                  │
              ▼                ▼          ▼                  ▼
        ┌──────────┐    ┌──────────┐    ┌──────────┐    ┌──────────┐
        │ 实例 1   │    │ 实例 2   │    │ 实例 3   │    │ 实例 4   │
        │ (正常)   │    │ (正常)   │    │ (故障)   │    │ (正常)   │
        └──────────┘    └──────────┘    └──────────┘    └──────────┘
             ▲               ▲               ✗                ▲
             │               │               │                │
             └───────────────┴───────────────┴────────────────┘
                     负载均衡自动跳过故障实例

REST 高可用三板斧:
  ① 无状态服务 → 水平扩展,任意实例可替换
  ② 负载均衡 → 自动剔除故障节点,流量分发到健康实例
  ③ 熔断降级 → 下游故障时快速失败,返回兜底数据

5.5.3 REST 的真实瓶颈在哪?

瓶颈问题解决方案
JSON 序列化开销文本序列化 CPU 消耗大换 Protobuf / JSONB / 压缩
HTTP/1.1 队头阻塞单连接请求串行排队升级 HTTP/2,多路复用
HTTP 头部冗余每次请求都带完整 HeaderHTTP/2 HPACK 头部压缩
TCP 握手开销每次新建连接 3 次握手连接池复用 + Keep-Alive
数据库瓶颈最终瓶颈都在 DB读写分离、分库分表、缓存

核心结论:REST 的瓶颈不在 REST 本身,而在底层 IO 模型和序列化格式。 用 WebFlux + HTTP/2 + Protobuf,REST 可以接近 RPC 的性能。

5.5.4 REST vs RPC 高并发场景对比

场景REST 方案RPC 方案结论
秒杀(瞬时 10W QPS)Nginx 限流 + 异步队列 + 水平扩展Dubbo 长连接 + 并行调用都能扛,REST 运维成本更低
服务间高频调用(1W+ QPS)WebClient + HTTP/2 + 连接池Dubbo 长连接性能差距 2-3 倍,RPC 更优
流式数据(日志/监控)SSE / WebSocketgRPC 双向流gRPC 更优雅
对外 API(第三方调用)REST(唯一选择)不适用REST 无替代
微服务集群(100+ 服务)Gateway + 服务发现Dubbo 注册中心都能胜任,取决于技术栈

一句话:REST 完全可以高并发高可用,只是单次请求延迟比 RPC 高。对于绝大多数业务场景(QPS < 1W),REST 的性能完全够用,而且运维成本更低。

六、如何选型?

6.1 决策树

                    ┌─ 是否需要跨语言调用? ──┐
                    │ 是                      │ 否
                    ▼                         ▼
           ┌─ 需要高性能? ──┐        ┌─ 需要高性能? ──┐
           │ 是             │ 否       │ 是              │ 否
           ▼                ▼          ▼                 ▼
          gRPC            REST      Dubbo / gRPC        REST
      (HTTP/2+Protobuf)           (纯 Java 选 Dubbo,
                                   跨语言选 gRPC)

6.2 场景化推荐

场景推荐方案原因
对外开放 API(给第三方调用)REST跨语言、调试友好、文档规范
内部微服务间调用(纯 Java)Dubbo RPC高性能、内置治理、编译期安全
内部微服务间调用(多语言)gRPCHTTP/2 + Protobuf,跨语言高性能
高并发低延迟场景(<1ms 要求)Dubbo RPC二进制协议、长连接、零拷贝
流式数据传输(日志、监控)gRPC原生双向流支持
前端 ← → 后端REST浏览器原生支持 HTTP
快速迭代项目REST松耦合、无需共享接口 jar
已有 Spring Cloud 体系REST(OpenFeign)生态一致,开箱即用
已有 Spring Cloud AlibabaDubbo RPC与 Nacos 天然集成
网关层 → 服务层REST网关(Gateway)天然支持 HTTP
服务层 → 服务层(高频调用)Dubbo RPC绕过网关,直连高性能

6.3 混合使用方案(推荐)

              ┌─────────────────────────────────────────┐
              │              最佳实践:混用              │
              └─────────────────────────────────────────┘

  外部请求(浏览器 / App / 第三方)

        │ REST(HTTP + JSON)

  ┌──────────────┐
  │   Gateway     │ ← REST 统一入口(鉴权、限流、路由)
  └──────┬───────┘

         │ REST

  ┌──────────────┐
  │ order-service │
  │              │
  │  ┌──────────┐│
  │  │ REST API ││ ← 对外暴露 REST 接口
  │  └──────────┘│
  │              │
  │  ┌──────────┐│
  │  │Dubbo Ref ││ ← 调用 inventory-service 用 Dubbo(高性能)
  │  └──────────┘│
  │              │
  │  ┌──────────┐│
  │  │OpenFeign ││ ← 调用 payment-service 用 REST(跨团队)
  │  └──────────┘│
  └──────────────┘

  原则:
  - 对外 API 统一用 REST
  - 内部高频调用用 RPC(Dubbo/gRPC)
  - 跨团队调用用 REST(松耦合)
  - 同一团队内部用 RPC(高性能 + 编译期安全)

6.4 选型 Checklist

条件选 RPC选 REST
需要跨语言调用❌(选 gRPC)
追求极致性能(<1ms)
需要浏览器直接调用
需要过 CDN 缓存
需要流式传输✅(gRPC)⚠️(SSE/WebSocket)
需要编译期类型安全
需要 curl 调试
团队技术栈统一(纯 Java)
需要对外开放 API
需要服务治理内置✅(Dubbo)❌(需外部组件)
快速原型开发

七、面试高频考点

Q1:RPC 和 REST 的本质区别是什么?

  RPC 面向动作(Action-Oriented),目标是"像调用本地方法一样调用远程方法",接口名就是操作名。REST 面向资源(Resource-Oriented),目标是"操作 URL 标识的资源",HTTP 方法表示操作类型。RPC 追求性能和编译期安全,REST 追求松耦合和跨语言。

Q2:为什么 Dubbo 比 REST 性能高?

  • 二进制序列化(Hessian2/Protobuf)比 JSON 快 3-5 倍
  • 长连接复用,避免 HTTP 握手开销
  • 自定义协议头部极小,HTTP 头部有冗余
  • 零拷贝、NIO 多路复用等底层优化

Q3:gRPC 算是 RPC 还是 HTTP?

  gRPC 是 RPC 框架,但底层使用 HTTP/2 作为传输协议。它继承了 RPC 的强契约(.proto 文件)和高性能(Protobuf 序列化),同时利用了 HTTP/2 的多路复用、双向流等现代特性。可以理解为"HTTP/2 上的 RPC"。

Q4:REST 能不能实现高性能?

  可以,但需要额外工作。REST + HTTP/2 + JSONB(二进制 JSON)+ 长连接池可以接近 RPC 性能。但大部分团队不会做这些优化,这就导致了"REST 比 RPC 慢"的普遍认知。

Q5:Spring Cloud 项目中怎么混用 RPC 和 REST?

  对外 API 用 REST(Gateway + Spring MVC),内部调用用 Dubbo(Spring Cloud Alibaba),两者通过 Nacos 统一注册发现。Gateway 路由 REST 请求,Dubbo 直连处理内部调用,各取所长。

Q6:微服务架构中,接口变更怎么处理?

  RPC:共享接口 jar 版本管理,提供者保证向后兼容,消费者按需升级。REST:API 版本化(/api/v1/orders/api/v2/orders),或请求头版本号。REST 的版本管理更灵活,不需要消费者重新编译。