07-03-A-RPC与通信详解
️ 关键词:RPC · Dubbo · SPI · 序列化 · Protobuf · gRPC · HTTP vs RPC · 服务治理 · 零拷贝
📌 导读:微服务之间怎么通信?两条路:HTTP(REST)和 RPC(Dubbo/gRPC)。HTTP 通用但慢,RPC 快但重——选哪个?这篇用大白话讲透:RPC 的核心原理(动态代理+序列化+网络传输+服务发现)、Dubbo 的架构(Provider/Consumer/Registry/Monitor)与 SPI 扩展机制、序列化协议对比(JSON/Hessian/Protobuf/Kryo)、HTTP vs RPC 的选型决策、gRPC 为什么快(HTTP/2+Protobuf+零拷贝)。看完你能做到:被问"RPC 原理"能画出调用链路,被问"Dubbo SPI 和 JDK SPI 区别"能讲出按需加载,被问"HTTP 和 RPC 怎么选"能给出决策框架。
前置知识:动态代理(27-A 篇 AOP)、网络 IO(NIO/零拷贝,03-A 篇堆外内存)、Spring Cloud(28-A 篇)。
📑 目录
📖 术语速查表(每个词都用人话解释)
** RPC 核心类**
| 术语 | 一句话白话解释 |
|---|---|
| RPC(Remote Procedure Call) | 远程过程调用——像调本地方法一样调远程服务,底层帮你做网络通信 |
| 动态代理 | RPC 的客户端核心——为远程接口生成代理对象,方法调用转成网络请求 |
| 序列化 | 把对象转成字节流(网络传输)——JSON/Hessian/Protobuf/Kryo |
| 反序列化 | 把字节流转回对象——接收方还原对象 |
| 服务发现 | 从注册中心查远程服务的地址——和 Spring Cloud 注册中心同款 |
| 负载均衡 | 多个 Provider 实例时选一个——轮询/随机/最少活跃 |
️ Dubbo 架构类
| 术语 | 一句话白话解释 |
|---|---|
| Provider | 服务提供者——暴露服务,注册到注册中心 |
| Consumer | 服务消费者——从注册中心查 Provider,发起 RPC 调用 |
| Registry | 注册中心——ZooKeeper/Nacos,服务注册与发现 |
| Monitor | 监控中心——统计调用次数/耗时/成功率 |
| Dubbo SPI | Dubbo 的扩展机制——按需加载扩展点,比 JDK SPI 灵活 |
| 扩展点(Extension) | 可替换的组件——协议/序列化/负载均衡/集群容错都可扩展 |
** 序列化类**
| 术语 | 一句话白话解释 |
|---|---|
| JSON | 文本格式——可读性好、跨语言,但体积大、速度慢 |
| Hessian | 二进制格式——Dubbo 默认,体积小、速度快,Java 生态 |
| Protobuf | Google 的二进制格式——体积最小、速度最快,需 .proto 定义+代码生成 |
| Kryo | Java 二进制序列化——速度极快,但跨语言差 |
| IDL(接口定义语言) | 定义服务接口的语言——.proto(gRPC)/.thrift(Thrift),代码生成 |
** gRPC 类**
| 术语 | 一句话白话解释 |
|---|---|
| gRPC | Google 的 RPC 框架——HTTP/2+Protobuf+零拷贝,跨语言高性能 |
| HTTP/2 | HTTP 的新版本——多路复用(一个连接多个请求)、头部压缩、服务端推送 |
| 多路复用 | 一个 TCP 连接上并行多个请求/响应——不用为每个请求建连接 |
| .proto 文件 | Protobuf 的接口定义文件——定义消息结构和服务接口,生成代码 |
一、RPC 是什么:远程过程调用
1.1 RPC 的目标
// RPC 的目标:像调本地方法一样调远程服务
// 本地调用
Order order = orderService.getOrder(123L);
// RPC 调用(看起来一样,实际跨网络)
Order order = orderService.getOrder(123L); // orderService 是远程服务的代理
1.2 RPC 调用链路
RPC 核心四件事:① 动态代理(方法调用转网络请求);② 序列化(对象转字节流);③ 网络传输(TCP/HTTP);④ 服务发现+负载均衡(找到 Provider 实例)。
一句话:RPC = 动态代理+序列化+网络传输+服务发现——把"跨网络调用"包装成"本地方法调用"。
二、Dubbo 架构
2.1 四大角色
2.2 调用流程 10 步
| 步骤 | 角色 | 动作 |
|---|---|---|
| 1 | Provider | 启动,暴露服务,注册到 Registry |
| 2 | Consumer | 启动,从 Registry 订阅服务 |
| 3 | Registry | 推送 Provider 列表给 Consumer(变更时实时推送) |
| 4 | Consumer | 基于 Provider 列表,负载均衡选一个 |
| 5 | Consumer | 发起 RPC 调用(动态代理+序列化+网络) |
| 6 | Provider | 接收请求,反序列化,反射调用真实方法 |
| 7 | Provider | 返回结果(序列化+网络) |
| 8 | Consumer | 反序列化结果,返回给调用方 |
| 9 | Consumer/Provider | 异步上报调用统计到 Monitor |
| 10 | Monitor | 统计 QPS/RT/成功率 |
2.3 Dubbo 的核心能力
| 能力 | 说明 |
|---|---|
| 透明化远程调用 | 像调本地方法一样调远程——动态代理 |
| 软负载均衡 | 客户端负载均衡(轮询/随机/最少活跃/一致性哈希) |
| 集群容错 | Failover(重试)/Failfast(快速失败)/Failsafe(安全失败)/Failback(自动恢复) |
| 服务治理 | 路由规则/权重调整/黑白名单/降级——运行时动态调整 |
三、Dubbo SPI:扩展机制
3.1 JDK SPI vs Dubbo SPI
| 维度 | JDK SPI | Dubbo SPI |
|---|---|---|
| 加载方式 | 一次性加载所有实现 | 按需加载指定实现 |
| 配置 | META-INF/services/接口全名 | META-INF/dubbo/接口全名(key=value) |
| 扩展 | 不支持 | 支持 IoC(注入依赖)+ AOP(Wrapper 包装) |
| 性能 | 浪费(加载不用的实现) | 按需加载,性能好 |
3.2 Dubbo SPI 示例
dubbo=org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol
http=org.apache.dubbo.rpc.protocol.http.HttpProtocol
grpc=org.apache.dubbo.rpc.protocol.grpc.GrpcProtocol
// 按需加载指定实现
Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class)
.getExtension("dubbo"); // 只加载 DubboProtocol,不加载 http/grpc
一句话:JDK SPI 一次性加载所有实现(浪费),Dubbo SPI 按需加载指定实现(key=value 配置)+ 支持 IoC/AOP——Dubbo 的协议/序列化/负载均衡/集群容错都是 SPI 扩展点。
四、序列化协议对比
4.1 四大协议对比
| 维度 | JSON | Hessian | Protobuf | Kryo |
|---|---|---|---|---|
| 格式 | 文本 | 二进制 | 二进制 | 二进制 |
| 可读性 | 好 | 差 | 差 | 差 |
| 体积 | 大 | 中 | 最小 | 小 |
| 速度 | 慢 | 快 | 最快 | 极快 |
| 跨语言 | 好 | 中 | 好 | 差(Java) |
| 使用成本 | 低(无需定义) | 低 | 高(.proto+代码生成) | 低 |
| Dubbo 默认 | — | ✅ 默认 | 可选 | 可选 |
| 适用 | REST API/调试 | Dubbo 默认 | gRPC/高性能 | Java 内部 |
4.2 选型建议
| 场景 | 推荐 | 理由 |
|---|---|---|
| REST API/跨语言/调试 | JSON | 可读性好、跨语言、生态广 |
| Dubbo 默认 | Hessian | 体积小、速度快、Java 生态 |
| 高性能/跨语言/gRPC | Protobuf | 体积最小、速度最快 |
| Java 内部极致性能 | Kryo | 速度极快(但跨语言差) |
一句话:JSON 可读但慢,Protobuf 快但要用 .proto 定义,Hessian 是 Dubbo 的平衡选择——按"可读性 vs 性能 vs 跨语言"三角选型。
五、HTTP vs RPC:选型
5.1 对比
| 维度 | HTTP(REST) | RPC(Dubbo/gRPC) |
|---|---|---|
| 协议 | HTTP/1.1(文本) | 自定义 TCP/HTTP2(二进制) |
| 序列化 | JSON(文本) | Protobuf/Hessian(二进制) |
| 性能 | 慢(文本解析+头部开销) | 快(二进制+长连接+多路复用) |
| 跨语言 | 好(HTTP 通用) | 中(需 IDL+代码生成) |
| 调试 | 好(curl/Postman 直接调) | 差(需专用工具) |
| 服务治理 | 弱(靠网关/SDK) | 强(内置负载均衡/容错/降级) |
| 生态 | 广(所有语言/工具) | Dubbo Java 为主,gRPC 跨语言 |
| 适用 | 对外 API/跨团队/跨语言 | 内部微服务/高性能 |
5.2 选型决策框架
一句话:对外 API 用 HTTP(通用易调试),内部微服务用 RPC(高性能+服务治理)——Java 内部选 Dubbo,多语言选 gRPC。
六、gRPC:为什么快
6.1 gRPC 三大快因
6.2 gRPC 工作流
6.3 gRPC vs Dubbo
| 维度 | gRPC | Dubbo |
|---|---|---|
| 协议 | HTTP/2 | 自定义 TCP(可配 HTTP) |
| 序列化 | Protobuf(固定) | Hessian/Protobuf/JSON(可选) |
| 跨语言 | 强(Google 生态) | 弱(Java 为主) |
| 服务治理 | 弱(靠 Istio/自研) | 强(内置负载均衡/容错/降级) |
| 适用 | 跨语言/云原生/K8s | Java 微服务/阿里生态 |
七、总结
7.1 一张图回顾全文
7.2 核心要点浓缩(十二条)
- RPC = 动态代理+序列化+网络传输+服务发现——把跨网络调用包装成本地方法调用。
- RPC 调用链路:代理拦截→序列化→网络传输→反序列化→反射调用→结果返回——六步。
- Dubbo 四角色:Provider(暴露服务)、Consumer(调用服务)、Registry(注册中心)、Monitor(监控)。
- Dubbo SPI vs JDK SPI:JDK 一次性加载所有实现(浪费),Dubbo 按需加载(key=value)+支持 IoC/AOP——协议/序列化/负载均衡都是 SPI 扩展点。
- 序列化四协议:JSON 可读但慢、Hessian Dubbo 默认、Protobuf 最快但要 .proto、Kryo Java 极致——按可读性/性能/跨语言三角选型。
- HTTP vs RPC:对外 API 用 HTTP(通用易调试),内部微服务用 RPC(高性能+服务治理)。
- Java 内部选 Dubbo,多语言选 gRPC——Dubbo 服务治理强,gRPC 跨语言强。
- gRPC 三大快因:HTTP/2 多路复用(一个连接多请求)、Protobuf(体积最小速度最快)、头部压缩+零拷贝。
- Dubbo 集群容错:Failover(重试,默认)、Failfast(快速失败)、Failsafe(安全失败)、Failback(自动恢复)。
- gRPC 工作流:.proto 定义→代码生成→服务端实现→客户端 Stub 调用——IDL 驱动,跨语言。
- HTTP/2 多路复用:一个 TCP 连接并行多个请求/响应——不用为每个请求建连接,省握手开销。
- 选型口诀:对外 HTTP、内部 RPC、Java Dubbo、多语言 gRPC、极致性能 Protobuf——没有最好,只有最匹配场景。
📌 最后一句话:RPC 的本质是"把网络通信的复杂性藏起来"——动态代理藏调用、序列化藏格式、服务发现藏地址、负载均衡藏实例。HTTP 和 RPC 不是"谁更好",是"谁更匹配场景"——对外要通用选 HTTP,内部要性能选 RPC,跨语言选 gRPC,Java 生态选 Dubbo。
📌 配套阅读:
上一篇:《07-02-A-SpringCloud全家桶详解.md》
如果这篇文章对你有帮助,欢迎点赞、收藏、关注!
2513



被折叠的 条评论
为什么被折叠?



