Appearance
Dubbo 详解:高性能 RPC 框架
Apache Dubbo 是阿里巴巴开源的高性能、轻量级 RPC 框架,提供六大核心能力:面向接口的远程方法调用、智能容错与负载均衡、服务自动注册与发现、高度可扩展的 SPI 机制、丰富的服务治理功能、以及完善的监控与运维体系。在 Spring Cloud Alibaba 生态中,Dubbo 承担着微服务之间的高性能内部 RPC 调用角色。
一、为什么需要 Dubbo?
1.1 RPC 和 HTTP 的区别
在微服务通信中,RPC 和 HTTP 是两种最常见的通信方式。理解它们的区别,是理解 Dubbo 定位的前提。
| 维度 | RPC(Remote Procedure Call) | HTTP(REST) |
|---|---|---|
| 核心思想 | 像调用本地方法一样调用远程服务 | 通过 URI 操作资源,无状态请求-响应 |
| 协议层 | 基于 TCP,自定义应用层协议 | 基于 TCP,标准 HTTP 协议 |
| 序列化 | 高效二进制序列化(Hessian、Protobuf、Kryo) | 文本序列化(JSON、XML) |
| 性能 | 高吞吐、低延迟(TCP 长连接复用) | 相对较低(每次请求建立连接,HTTP/1.1 可复用) |
| 跨语言 | 需 IDL 定义,多语言 SDK 支持 | 天然跨语言,任何语言均可调用 |
| 服务治理 | 完善(注册中心、负载均衡、集群容错) | 需借助网关/服务网格实现 |
| 调试友好 | 差(二进制协议,不可直接阅读) | 好(JSON 文本,curl 可直接调试) |
| 适用场景 | 微服务内部高性能调用 | 对外 API、跨语言调用、浏览器直接访问 |
1.2 Dubbo 在微服务中的定位:高性能内部 RPC 调用
在典型的微服务架构中,服务间通信有两种典型路径:
┌─────────────────────────────────────────────────────────────────────┐
│ 微服务通信架构 │
│ │
│ ┌──────────┐ HTTP/REST (Feign) ┌──────────┐ │
│ │ Browser │ ──────────────────────→│ Gateway │ │
│ └──────────┘ └────┬─────┘ │
│ │ │
│ ┌─────────────────────┼────────────────────┐ │
│ │ 服务间内部通信 │ │ │
│ ▼ ▼ │ │
│ ┌──────────┐ ┌──────────┐ │ │
│ │ 服务 A │←──TCP──→│ 服务 B │ │ │
│ │(Dubbo RPC)│ Dubbo │(Dubbo RPC)│ │ │
│ └──────────┘ └──────────┘ │ │
│ │ │ │ │
│ └─────────┬───────────┘ │ │
│ │ │ │
│ 对外 API 使用 HTTP/REST │ │
│ 内部服务调用使用 Dubbo RPC │ │
└─────────────────────────────────────────────────────────────────────┘核心原则:对外暴露 RESTful API(便于跨语言/跨平台),内部服务间使用 Dubbo RPC(高性能)。Dubbo 不是替代 HTTP,而是与 HTTP 互补。
1.3 Feign(HTTP/REST)vs Dubbo(TCP/RPC)
| 维度 | Feign(Spring Cloud) | Dubbo(Spring Cloud Alibaba) |
|---|---|---|
| 通信协议 | HTTP/1.1(REST) | TCP 长连接(自定义协议 / HTTP/2) |
| 序列化 | JSON(文本) | Hessian / Protobuf(二进制) |
| 性能 | 中等(HTTP 头部开销 + JSON 序列化) | 高(自定义协议头精简 + 二进制序列化) |
| 连接管理 | 每次请求建立连接(或连接池复用) | TCP 长连接复用,多路复用 |
| 服务治理 | 依赖 Ribbon/LoadBalancer + 外部组件 | 内置负载均衡、集群容错、路由 |
| 服务注册 | 依赖 Spring Cloud 注册中心 | 多注册中心支持(Nacos/ZK/Redis) |
| 接口定义 | @FeignClient + @RequestMapping | Java Interface + @DubboService/@DubboReference |
| 调试 | curl / Postman 直接测试 | 需 telnet 或 Dubbo 自带工具 |
| 学习成本 | 低(REST 风格,开发者熟悉) | 中(需理解 RPC 概念和 Dubbo 配置) |
1.4 Dubbo 的优势
- 高性能:基于 TCP 长连接 + 二进制序列化,比 HTTP/JSON 性能高一倍以上。支持 NIO 网络模型(Netty),单机可支撑数十万并发连接。
- 完善的服务治理:内置负载均衡、集群容错、路由规则、服务降级、流量控制等能力,无需额外组件。
- 多协议支持:dubbo://(默认高性能协议)、tri://(HTTP/2 + gRPC 兼容)、rest://(HTTP REST)、hessian://、gRPC:// 等,一套代码多协议暴露。
- 多注册中心:Nacos、ZooKeeper、Redis、Consul、Etcd 等,与 Spring Cloud Alibaba 生态无缝集成。
- 高度可扩展:SPI 机制允许自定义协议、序列化、负载均衡、过滤器等几乎所有组件。
- 服务治理可视化:Dubbo Admin 提供可视化控制台,支持服务查询、路由管理、动态配置。
二、核心架构
2.1 整体架构图
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Registry │ │ Monitor │ │ Dubbo Admin │
│ 注册中心 │ │ 监控中心 │ │ 管理控制台 │
│ (Nacos / ZK) │ │ (Dubbo Admin)│ │ │
└──────┬───────┘ └──────┬───────┘ └──────────────┘
│ │
│ ② 注册服务 │ ④ 上报统计
│ │
▼ ▼
┌──────────────┐ ┌──────────────┐
│ Provider │ │ Consumer │
│ 服务提供者 │ │ 服务消费者 │
│ │ │ │
│ ┌────────┐ │ │ ┌────────┐ │
│ │ 服务实现 │ │ │ │ 服务调用 │ │
│ └────────┘ │ │ └────────┘ │
│ ┌────────┐ │ │ ┌────────┐ │
│ │ Container│ │ │ │ Cluster │ │
│ │ (Spring) │ │ │ │(容错/均衡)│ │
│ └────────┘ │ │ └────────┘ │
└──────────────┘ └──────────────┘
│ │
└─────────┬──────────┘
│
│ ③ 直连调用(RPC 调用)
│ 异步 / 同步
│ TCP 长连接
│
▼
┌──────────────┐
│ Network │
│ (Netty) │
└──────────────┘2.2 五大核心角色
| 角色 | 说明 |
|---|---|
| Provider(服务提供者) | 暴露服务的服务提供方,向注册中心注册自己提供的服务。 |
| Consumer(服务消费者) | 调用远程服务的服务消费方,从注册中心订阅服务地址列表。 |
| Registry(注册中心) | 负责服务地址的注册与发现。Provider 注册地址,Consumer 订阅地址。 |
| Monitor(监控中心) | 统计服务调用次数、调用时间、成功率等,上报到监控中心。 |
| Container(服务运行容器) | 服务运行的容器环境,通常是 Spring 容器。 |
2.3 服务注册与发现流程
Provider Registry Consumer
│ │ │
│ ① register(service, IP:Port) │ │
│ ──────────────────────────────→ │ │
│ │ │
│ │ ② subscribe(service) │
│ │ ←────────────────────────────── │
│ │ │
│ │ ③ notify(provider addresses) │
│ │ ──────────────────────────────→ │
│ │ │
│ │ ④ 缓存地址列表到本地 │
│ │ │
│ ←────────── RPC 调用 ──────────────────────────────────────────→ │
│ ⑤ 直连 Provider(TCP 长连接) │
│ │ │
│ ⑥ 心跳检测(定期) │ ⑥ 心跳检测(定期) │
│ ──────────────────────────────→ │ ←────────────────────────────── │
│ │ │流程说明:
- 注册:Provider 启动时,将自身服务信息(接口名、IP、端口、方法列表)注册到注册中心。
- 订阅:Consumer 启动时,向注册中心订阅所需服务。
- 通知:注册中心将 Provider 地址列表推送给 Consumer。
- 缓存:Consumer 将地址列表缓存到本地,以防注册中心不可用时仍能调用。
- 直连调用:Consumer 通过负载均衡策略选择一个 Provider,直接发起 RPC 调用(不经过注册中心)。
- 心跳检测:Provider 和 Consumer 定期向注册中心发送心跳,保持连接活跃。
2.4 RPC 调用流程(一次完整的 Dubbo 调用)
Consumer 端 Provider 端
│ │
│ ① 调用接口方法(代理对象) │
▼ │
┌─────────┐ │
│ Proxy │ 动态代理(Javassist/JDK) │
└────┬────┘ │
│ ② 组装请求(方法名、参数、版本等) │
▼ │
┌─────────┐ │
│ Cluster │ 集群容错(Failover/Failfast 等) │
└────┬────┘ │
│ ③ 选择集群策略 │
▼ │
┌─────────┐ │
│LoadBalance│ 负载均衡(Random/RoundRobin 等) │
└────┬────┘ │
│ ④ 选择一个 Provider 地址 │
▼ │
┌─────────┐ │
│ Filter │ 过滤器链(监控、限流、鉴权等) │
└────┬────┘ │
│ ⑤ 网络传输 │
▼ │
┌─────────┐ TCP 长连接 ┌─────────┐ │
│ Protocol│ ←──────────────────────→ │ Protocol│ │
│ (编码) │ 二进制数据流 │ (解码) │ │
└────┬────┘ └────┬────┘ │
│ │ │
│ ⑥ 序列化(Hessian/Protobuf) │ ⑦ 反序列化
│ │ │
│ ▼ │
│ ┌─────────┐ │
│ │ Filter │ │
│ └────┬────┘ │
│ │ │
│ ▼ │
│ ┌─────────┐ │
│ │ Invoker │ │
│ │(反射调用)│ │
│ └────┬────┘ │
│ │ │
│ ▼ │
│ ┌─────────┐ │
│ │ Service │ │
│ │ (业务实现)│ │
│ └─────────┘ │
│ │ │
│ ←─────────── 返回结果 ────────────────────── │
│ │关键步骤:
- Proxy 代理:Consumer 通过动态代理将接口调用转换为 RPC 请求。
- Cluster 集群:选择合适的集群容错策略(如 Failover 失败重试其他节点)。
- LoadBalance:从多个 Provider 中选择一个节点进行调用。
- Filter 过滤器:在调用前后执行监控、限流、鉴权等逻辑。
- Protocol 编解码:将 Java 对象序列化为二进制数据,通过网络传输。
- 反序列化:Provider 将收到的二进制数据反序列化为 Java 对象。
- 反射调用:Provider 通过反射调用对应的业务实现方法。
三、Dubbo 3.x 新特性
Dubbo 3.x 是 Dubbo 的一个重大版本升级,从 2.7 直接跳到 3.0,标志着 Dubbo 从"微服务 RPC 框架"向"云原生微服务基础设施"的全面演进。
3.1 应用级服务发现
这是 Dubbo 3.x 最核心的架构变化。
Dubbo 2.x 接口级服务发现的问题:
注册中心中存储的粒度:接口级
┌──────────────────────────────────────────────┐
│ com.example.UserService → 192.168.1.1:20880│
│ com.example.OrderService → 192.168.1.1:20880│
│ com.example.UserService → 192.168.1.2:20880│
│ com.example.OrderService → 192.168.1.2:20880│
└──────────────────────────────────────────────┘
问题:一个应用有 100 个接口,3 个实例 → 300 条注册数据Dubbo 3.x 应用级服务发现:
注册中心中存储的粒度:应用级
┌──────────────────────────────────────────────┐
│ app-user-service → 192.168.1.1:20880 │
│ app-user-service → 192.168.1.2:20880 │
│ app-user-service → 192.168.1.3:20880 │
└──────────────────────────────────────────────┘
优势:一个应用只需注册一次,与接口数量无关| 维度 | 接口级(Dubbo 2.x) | 应用级(Dubbo 3.x) |
|---|---|---|
| 注册粒度 | 每个接口单独注册 | 整个应用统一注册 |
| 注册数据量 | 接口数 x 实例数(爆炸式增长) | 实例数(恒定) |
| 注册中心压力 | 高(接口多的应用产生大量数据) | 低(恒定数据量) |
| 与 Spring Cloud 对齐 | 不对齐(Spring Cloud 是应用级) | 完全对齐 |
| 与 Kubernetes 对齐 | 不对齐(K8s Service 是应用级) | 完全对齐 |
3.2 Triple 协议
Triple 是 Dubbo 3.x 定义的新一代 RPC 通信协议,基于 HTTP/2 构建,兼容 gRPC。
┌─────────────────────────────────────────────────────────┐
│ Triple 协议栈 │
│ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ 应用层:Dubbo Business │ │
│ ├──────────────────────────────────────────────────┤ │
│ │ 序列化层:Protobuf / Hessian / JSON │ │
│ ├──────────────────────────────────────────────────┤ │
│ │ 传输层:HTTP/2 │ │
│ │ • 多路复用(Stream Multiplexing) │ │
│ │ • 全双工通信(Bidirectional Streaming) │ │
│ │ • 头部压缩(HPACK) │ │
│ │ • 服务端推送(Server Push) │ │
│ ├──────────────────────────────────────────────────┤ │
│ │ TLS(可选) │ │
│ ├──────────────────────────────────────────────────┤ │
│ │ TCP │ │
│ └──────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘| 特性 | Triple 协议 | Dubbo 2.x 协议 |
|---|---|---|
| 底层传输 | HTTP/2 | TCP 自定义协议 |
| 多路复用 | 原生支持(HTTP/2 Stream) | 不支持 |
| 流式调用 | 支持(Client Stream / Server Stream / Bidirectional) | 不支持 |
| 穿透网关 | 天然支持(HTTP/2) | 困难(私有协议) |
| 与 gRPC 互通 | 完全兼容 gRPC 客户端 | 不兼容 |
| 浏览器调用 | 支持(HTTP/2 + JSON) | 不支持 |
| 性能 | 接近 dubbo 协议 | 略高(无 HTTP/2 头部开销) |
3.3 云原生支持
- Kubernetes 原生服务发现:直接使用 K8s API Server 作为注册中心,无需额外部署 Nacos/ZK。
- xDS 协议支持:支持 Istio/Envoy 的服务发现标准协议,可与 Service Mesh 集成。
- Proxyless Mesh:Dubbo 3.x 可直接与 Istio 控制面通信,无需部署 Envoy Sidecar,实现无代理服务网格。
3.4 Mesh 支持
传统 Service Mesh: Dubbo Proxyless Mesh:
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ App A │ │ App B │ │ App A │ │ App B │
├──────────┤ ├──────────┤ │+ Dubbo 3 │ │+ Dubbo 3 │
│ Sidecar │ │ Sidecar │ │ (xDS) │ │ (xDS) │
├──────────┤ ├──────────┤ └────┬─────┘ └────┬─────┘
│ App A │ │ App B │ │ │
└──────────┘ └──────────┘ │ xDS 协议 │
│ │ └───────┬──────┘
└───────┬───────┘ │
│ ┌───────▼──────┐
┌──────▼──────┐ │ Istio CP │
│ Istio CP │ │ (Pilot) │
│ (Pilot) │ └──────────────┘
└─────────────┘
缺点:Sidecar 额外资源开销 优势:无 Sidecar,性能更高,架构更简单四、核心概念
4.1 服务接口(Service Interface)
Dubbo 是面向接口的 RPC 框架,服务接口是 Provider 和 Consumer 之间的契约。通常将接口定义在独立的 API 模块中,供双方共同依赖。
java
// dubbo-api 模块
public interface UserService {
UserDTO getUserById(Long id);
List<UserDTO> listUsers(String keyword);
}4.2 服务分组(Group)
当一个接口有多种实现时,使用 group 区分。例如:同一个 UserService 接口,有 read 和 write 两种实现,分别部署。
yaml
# Provider 端
dubbo:
provider:
group: read # 或 write
# Consumer 端
dubbo:
consumer:
group: read # 订阅 read 分组的服务java
// 消费者代码中指定分组
@DubboReference(group = "read")
private UserService readUserService;
@DubboReference(group = "write")
private UserService writeUserService;4.3 服务版本(Version)
当一个接口有多个不兼容的版本时,使用 version 进行灰度升级。版本号通常遵循语义化版本规范。
场景:UserService 从 v1.0 升级到 v2.0
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Consumer │ │Provider v1│ │Provider v2│
│ (v1.0) │────→│ (v1.0) │ │ (v2.0) │
└──────────┘ └──────────┘ └──────────┘
│
┌──────────┐ │
│ Consumer │ │
│ (v2.0) │─────────────────────────────→│
└──────────┘java
// Provider v2.0
@DubboService(version = "2.0.0")
public class UserServiceImplV2 implements UserService { ... }
// Consumer v2.0
@DubboReference(version = "2.0.0")
private UserService userService;4.4 超时(Timeout)
超时是 Dubbo 中最基础的容错机制。默认为 1000ms(1 秒)。
| 配置位置 | 说明 | 默认值 |
|---|---|---|
| Provider 端 | 服务端方法执行超时 | 1000ms |
| Consumer 端 | 客户端调用等待超时 | 1000ms |
| 方法级 | 针对特定方法设置超时 | 继承上级 |
优先级(从高到低):Consumer 方法级 > Consumer 接口级 > Provider 方法级 > Provider 接口级 > Consumer 全局 > Provider 全局
yaml
dubbo:
provider:
timeout: 3000 # Provider 全局超时 3s
consumer:
timeout: 5000 # Consumer 全局超时 5sjava
// 方法级超时
@DubboReference(timeout = 3000, parameters = {"findById.timeout", "1000"})
private UserService userService;4.5 重试(Retries)
当 RPC 调用失败时,Dubbo 会自动重试。默认重试 2 次(加上第一次调用共 3 次)。注意:重试仅适用于幂等操作(读操作),非幂等操作(写操作)应设置 retries=0。
yaml
dubbo:
consumer:
retries: 2 # 默认重试 2 次java
// 写操作禁止重试
@DubboReference(retries = 0)
private OrderService orderService;
// 读操作允许重试
@DubboReference(retries = 3)
private UserService userService;4.6 负载均衡(Load Balance)
Dubbo 内置 4 种负载均衡策略,详见第六章。
4.7 集群容错(Cluster)
Dubbo 内置 6 种集群容错策略,详见第七章。
4.8 服务降级(Service Mock)
当服务不可用时,返回降级数据(Mock 数据),保证核心业务可用。
java
// 方式一:配置 mock 类
@DubboReference(mock = "com.example.mock.UserServiceMock")
private UserService userService;
// 方式二:配置 mock 返回值
@DubboReference(mock = "return null")
private UserService userService;
// 方式三:配置 mock 抛出异常
@DubboReference(mock = "throw com.example.BusinessException")
private UserService userService;4.9 本地存根(Stub)
在 Consumer 端执行部分逻辑(如参数验证、缓存),再发起远程调用。
java
// 存根类
public class UserServiceStub implements UserService {
private final UserService userService;
public UserServiceStub(UserService userService) {
this.userService = userService;
}
@Override
public UserDTO getUserById(Long id) {
// 参数验证在本地执行
if (id == null || id <= 0) {
throw new IllegalArgumentException("invalid id");
}
// 缓存检查
// ...
return userService.getUserById(id);
}
}
// 使用
@DubboReference(stub = "com.example.stub.UserServiceStub")
private UserService userService;五、协议详解
5.1 各协议详细说明
5.1.1 dubbo:// 协议(默认)
Dubbo 默认协议,基于 TCP 长连接 + NIO 异步通信,使用 Hessian 二进制序列化。适合小数据量大并发的服务调用。
- 连接方式:单一长连接(一个 Consumer 与一个 Provider 之间只建立一条 TCP 连接)
- 传输框架:Netty(NIO)
- 序列化:Hessian2(默认)
- 适用场景:请求量较大(> 1000),但每次请求数据量较小(< 100KB)的场景
- 不适合:传输大文件、超大字符串
5.1.2 tri:// 协议(Dubbo 3.x 推荐)
基于 HTTP/2 的新一代协议,兼容 gRPC,支持流式调用。
- 连接方式:HTTP/2 多路复用(一个连接支持多个并发 Stream)
- 传输框架:Netty(HTTP/2)
- 序列化:Protobuf(默认)、Hessian、JSON
- 适用场景:需要穿透网关、需要流式调用、需要与 gRPC 互通、需要浏览器直接访问的场景
5.1.3 rest:// 协议
基于标准的 HTTP REST 协议,使用 JAX-RS 注解。
- 连接方式:HTTP 短连接 / 连接池
- 传输框架:Servlet 容器(Tomcat/Jetty)或 Netty
- 序列化:JSON、XML
- 适用场景:需要对外暴露 RESTful API、需要跨语言调用
5.1.4 hessian:// 协议
基于 HTTP 的 Hessian 远程调用协议,用于 Dubbo 与 Hessian 服务互通。
- 连接方式:HTTP 短连接
- 传输框架:Servlet 容器
- 序列化:Hessian
- 适用场景:需要与 Hessian 原生服务互通
5.1.5 gRPC:// 协议
基于 gRPC 的协议,使用 HTTP/2 + Protobuf。
- 连接方式:HTTP/2 多路复用
- 传输框架:Netty(gRPC)
- 序列化:Protobuf
- 适用场景:需要与 gRPC 生态互通、需要跨语言 IDL 定义
5.2 协议对比表
| 协议 | 连接方式 | 序列化 | 性能 | 跨语言 | 流式调用 | 穿透网关 | 适用场景 |
|---|---|---|---|---|---|---|---|
| dubbo:// | TCP 长连接 + NIO | Hessian2 | 最高 | 差 | 不支持 | 困难 | 内部微服务高性能调用 |
| tri:// | HTTP/2 多路复用 | Protobuf/Hessian | 高 | 好 | 支持 | 天然支持 | 云原生、Mesh、流式场景 |
| rest:// | HTTP 短连接 | JSON/XML | 中 | 最好 | 不支持 | 天然支持 | 对外 API、跨语言调用 |
| hessian:// | HTTP 短连接 | Hessian | 中 | 较好 | 不支持 | 支持 | 与 Hessian 服务互通 |
| gRPC:// | HTTP/2 多路复用 | Protobuf | 高 | 好 | 支持 | 天然支持 | 与 gRPC 生态互通 |
5.3 多协议暴露
Dubbo 支持同时暴露多种协议,同一服务可以被不同协议的消费者调用。
yaml
dubbo:
protocols:
- id: dubbo
name: dubbo
port: 20880
- id: triple
name: tri
port: 50051
- id: rest
name: rest
port: 8080六、负载均衡策略
Dubbo 内置 4 种负载均衡策略,默认使用 Random(随机,带权重)。
6.1 Random(随机,带权重)
原理:根据权重随机选择 Provider。权重越大,被选中的概率越高。
假设 3 个 Provider,权重分别为 5、3、2
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Provider1│ │ Provider2│ │ Provider3│
│ weight=5│ │ weight=3│ │ weight=2│
└──────────┘ └──────────┘ └──────────┘
50% 30% 20%适用场景:通用场景,各 Provider 性能差异不大时使用。通过权重可以适配不同性能的机器。
配置:
yaml
dubbo:
consumer:
loadbalance: randomjava
@DubboReference(loadbalance = "random")
private UserService userService;6.2 RoundRobin(轮询)
原理:按顺序依次调用每个 Provider。支持权重,权重大的 Provider 每轮被调用更多次。
无权重:轮询 1 → 2 → 3 → 1 → 2 → 3 → ...
有权重(5:3:2):
每 10 次调用:Provider1 被调用 5 次,Provider2 被调用 3 次,Provider3 被调用 2 次适用场景:各 Provider 性能完全一致,需要均匀分配请求的场景。
注意:Dubbo 的 RoundRobin 存在请求累积问题(当 Provider 响应时间不一致时,慢的 Provider 会累积更多请求)。推荐使用 LeastActive 替代。
配置:
java
@DubboReference(loadbalance = "roundrobin")
private UserService userService;6.3 LeastActive(最小活跃数)
原理:选择当前活跃请求数最少的 Provider。活跃请求数 = 已发送但未收到响应的请求数。如果活跃数相同,则随机选择。
活跃请求数状态:
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Provider1│ │ Provider2│ │ Provider3│
│ active=5 │ │ active=2 │ │ active=8 │
└──────────┘ └──────────┘ └──────────┘
↑
选择 Provider2(活跃数最小)适用场景:各 Provider 处理能力不同,或者请求处理时间差异较大的场景。能自动将请求分配给处理速度更快的 Provider,实现动态负载均衡。
配置:
java
@DubboReference(loadbalance = "leastactive")
private UserService userService;6.4 ConsistentHash(一致性哈希)
原理:根据请求参数(默认第一个参数)的哈希值,将请求路由到固定的 Provider。相同参数的请求始终落到同一台机器。
哈希环:
┌──────────┐
│ Provider1│
│ hash=100│
└──────────┘
│
┌──────────┐ │ ┌──────────┐
│ Provider3│ │ │ Provider2│
│ hash=300 │────────○────────│ hash=200 │
└──────────┘ └──────────┘
│
请求 hash=150
→ 顺时针找到 Provider2适用场景:
- 需要缓存亲和性的场景(相同请求打到同一台机器,可命中本地缓存)
- 有状态服务(如 WebSocket 长连接需要固定路由到同一节点)
注意:当 Provider 增减时,只有部分请求会重新分配,不会全部打散。
配置:
java
// 使用一致性哈希,指定哈希参数
@DubboReference(loadbalance = "consistenthash")
private UserService userService;6.5 负载均衡策略对比
| 策略 | 原理 | 权重支持 | 适用场景 | 注意事项 |
|---|---|---|---|---|
| Random | 随机选择 | 是 | 通用场景,默认策略 | 调用量少时可能不均匀 |
| RoundRobin | 依次轮询 | 是 | 各 Provider 性能一致 | 响应慢的 Provider 会累积请求 |
| LeastActive | 选活跃数最小 | 否 | Provider 性能差异大 | 需要预热时间 |
| ConsistentHash | 参数哈希路由 | 否 | 有状态服务、缓存亲和 | Provider 增减时部分请求重分配 |
七、集群容错
Dubbo 内置 6 种集群容错策略,默认使用 Failover(失败自动切换)。
7.1 Failover(失败自动切换,默认)
原理:调用失败后,自动切换到其他 Provider 重试。默认重试 2 次(共 3 次调用)。
Consumer Provider 集群
│
│ ① 调用 Provider1
│ ──────────────────────────────→ Provider1 ❌ 失败
│
│ ② 重试 Provider2
│ ──────────────────────────────→ Provider2 ✅ 成功
│
│ ③ 返回结果
│ ←──────────────────────────────
│适用场景:读操作(幂等查询),对可用性要求高的场景。
注意:写操作必须谨慎使用,可能导致数据重复写入。建议写操作设置 retries=0。
配置:
yaml
dubbo:
consumer:
cluster: failover
retries: 27.2 Failfast(快速失败)
原理:调用失败后立即报错,不重试。适合非幂等写操作。
Consumer Provider 集群
│
│ ① 调用 Provider1
│ ──────────────────────────────→ Provider1 ❌ 失败
│
│ ② 立即抛出异常,不重试
│适用场景:写操作(新增、修改、删除),必须保证不重复执行。
配置:
java
@DubboReference(cluster = "failfast", retries = 0)
private OrderService orderService;7.3 Failsafe(忽略失败)
原理:调用失败后忽略异常,返回空结果。仅记录日志,不影响主流程。
适用场景:非关键功能(如日志记录、审计、通知),失败不影响核心业务。
配置:
java
@DubboReference(cluster = "failsafe")
private AuditLogService auditLogService;7.4 Failback(失败后重试)
原理:调用失败后,将请求放入失败队列,后台定时重试(默认 5 秒一次),直到成功。
Consumer
│
│ ① 调用 Provider → 失败
│
│ ② 放入失败队列
│ ┌──────────────────┐
│ │ 5s 后重试一次 │
│ │ 10s 后重试二次 │
│ │ ... │
│ │ 直到成功或超时 │
│ └──────────────────┘
│适用场景:消息通知、异步任务,必须保证最终一致性。
注意:失败队列在内存中,Consumer 重启会丢失未重试的请求。
配置:
java
@DubboReference(cluster = "failback")
private NotificationService notificationService;7.5 Forking(并行调用)
原理:同时向多个 Provider 发起调用,只要有一个成功就返回结果(取最快响应)。
Consumer Provider 集群
│
│ ① 同时调用 Provider1、Provider2、Provider3
│ ──────────────────────────────→ Provider1 ⏱ 100ms ✅
│ ──────────────────────────────→ Provider2 ⏱ 200ms (忽略)
│ ──────────────────────────────→ Provider3 ⏱ 150ms (忽略)
│
│ ② 取第一个返回的成功结果
│适用场景:对延迟要求极高的场景(如实时推荐),用资源换时间。
注意:会消耗更多 Provider 资源(每个请求被 N 个 Provider 处理),仅在资源充足时使用。
配置:
java
@DubboReference(cluster = "forking", parameters = {"forks", "3"})
private RecommendService recommendService;7.6 Broadcast(广播调用)
原理:向所有 Provider 广播调用,任何一个 Provider 报错即报错。调用所有 Provider 后才返回结果。
Consumer Provider 集群
│
│ ① 广播调用所有 Provider
│ ──────────────────────────────→ Provider1 ✅
│ ──────────────────────────────→ Provider2 ✅
│ ──────────────────────────────→ Provider3 ❌ → 整体报错
│适用场景:通知所有 Provider 刷新缓存、更新配置等全局操作。
配置:
java
@DubboReference(cluster = "broadcast")
private CacheRefreshService cacheRefreshService;7.7 集群容错策略对比
| 策略 | 失败处理 | 资源消耗 | 适用场景 | 典型业务 |
|---|---|---|---|---|
| Failover | 自动切换重试 | 中 | 幂等读操作 | 查询用户信息 |
| Failfast | 立即报错 | 低 | 非幂等写操作 | 创建订单 |
| Failsafe | 忽略异常 | 低 | 非关键功能 | 日志审计 |
| Failback | 后台定时重试 | 低 | 异步保证最终一致 | 发送通知 |
| Forking | 并行多路调用 | 高 | 极致低延迟 | 实时推荐 |
| Broadcast | 全量广播 | 高 | 全局通知 | 刷新缓存 |
八、服务治理
8.1 超时配置
超时配置是 Dubbo 中最基础也最重要的配置项。合理的超时配置可以防止服务雪崩。
配置优先级(从高到低):
Consumer 方法级 > Consumer 接口级 > Provider 方法级 > Provider 接口级 > Consumer 全局 > Provider 全局yaml
dubbo:
provider:
timeout: 3000 # Provider 全局:3 秒
consumer:
timeout: 5000 # Consumer 全局:5 秒java
// 接口级优先级更高
@DubboService(timeout = 2000)
public class UserServiceImpl implements UserService { ... }
// Consumer 方法级优先级最高
@DubboReference(timeout = 1000, parameters = {"getUserById.timeout", "500"})
private UserService userService;最佳实践:
- 数据库查询类接口:2~5 秒
- 缓存查询类接口:500ms~1 秒
- 复杂计算类接口:5~10 秒
- 永远不要设置
timeout=0(无限等待),可能导致线程耗尽
8.2 重试机制
重试策略:
第一次调用 → 失败 → 等待 → 第二次调用(重试1) → 失败 → 等待 → 第三次调用(重试2)
默认配置:retries=2,即最多调用 3 次重试配置示例:
yaml
dubbo:
consumer:
retries: 2 # 默认重试 2 次java
// 读操作:允许重试
@DubboReference(retries = 3)
private UserService userService;
// 写操作:禁止重试
@DubboReference(retries = 0)
private OrderService orderService;重试的注意事项:
- 重试仅适用于幂等操作(GET、QUERY)。
- 非幂等操作(INSERT、UPDATE、DELETE)必须设置
retries=0。 - 重试会放大下游压力(3 次调用 = 3 倍压力),需评估下游容量。
- 超时时间应大于单次调用的合理时间,否则重试不会生效。
8.3 版本灰度(Version)
通过 version 实现服务版本管理和灰度发布。
灰度发布流程:
Step 1: 初始状态
┌──────────┐ ┌──────────┐
│ Consumer │────→│Provider v1│ (100% 流量)
│(version=1│ │(version=1│
│ .0.0) │ │ .0.0) │
└──────────┘ └──────────┘
Step 2: 部署新版本 Provider v2
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Consumer │────→│Provider v1│ │Provider v2│
│(version=1│ │(version=1│ │(version=2│
│ .0.0) │ │ .0.0) │ │ .0.0) │
└──────────┘ └──────────┘ └──────────┘
↑
无流量(未被订阅)
Step 3: 切换部分 Consumer
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Consumer │────→│Provider v1│ │Provider v2│
│(version=1│ │(version=1│ │(version=2│
│ .0.0) │ │ .0.0) │ │ .0.0) │
└──────────┘ └──────────┘ └──────────┘
↑
┌──────────┐ 有流量
│ Consumer │─────────────────────────→│
│(version=2│
│ .0.0) │
└──────────┘java
// Provider v1
@DubboService(version = "1.0.0")
public class UserServiceImplV1 implements UserService { ... }
// Provider v2
@DubboService(version = "2.0.0")
public class UserServiceImplV2 implements UserService { ... }
// Consumer - 灰度切换
@DubboReference(version = "2.0.0")
private UserService userService;8.4 分组隔离(Group)
通过 group 实现同一接口的多种实现隔离。
场景:线上环境和测试环境共享注册中心,用 group 隔离
┌──────────┐ ┌──────────┐
│ Consumer │────→│Provider │
│(group= │ │(group= │
│ prod) │ │ prod) │
└──────────┘ └──────────┘
┌──────────┐ ┌──────────┐
│ Consumer │────→│Provider │
│(group= │ │(group= │
│ test) │ │ test) │
└──────────┘ └──────────┘java
// 读写分离
@DubboReference(group = "read")
private UserService readUserService;
@DubboReference(group = "write")
private UserService writeUserService;8.5 多注册中心
yaml
dubbo:
registries:
nacos:
address: nacos://localhost:8848
group: DUBBO_GROUP
zookeeper:
address: zookeeper://localhost:2181java
// 指定服务注册到特定注册中心
@DubboService(registry = {"nacos", "zookeeper"})
public class UserServiceImpl implements UserService { ... }
// 消费者从特定注册中心订阅
@DubboReference(registry = "nacos")
private UserService userService;8.6 多协议暴露
yaml
dubbo:
protocols:
dubbo:
name: dubbo
port: 20880
triple:
name: tri
port: 50051java
// 同时暴露 dubbo 和 triple 协议
@DubboService(protocol = {"dubbo", "triple"})
public class UserServiceImpl implements UserService { ... }九、核心配置
9.1 完整 YAML 配置示例
yaml
dubbo:
# ==================== 应用配置 ====================
application:
name: user-service-provider # 应用名称(必填),用于服务注册和监控
owner: team-platform # 应用负责人
organization: example # 组织名称
logger: slf4j # 日志框架
qos-enable: true # 是否开启 QOS 运维命令
qos-port: 22222 # QOS 端口
# ==================== 注册中心配置 ====================
registry:
address: nacos://localhost:8848 # 注册中心地址
protocol: nacos # 注册中心协议(nacos/zookeeper/redis/consul)
group: DUBBO_GROUP # 注册中心分组
timeout: 5000 # 注册中心连接超时
check: true # 启动时检查注册中心是否可用
register: true # 是否向注册中心注册
subscribe: true # 是否从注册中心订阅
# ==================== 协议配置 ====================
protocol:
name: dubbo # 协议名称:dubbo/tri/rest/hessian/grpc
port: 20880 # 服务端口
host: 192.168.1.100 # 绑定的主机地址(多网卡时指定)
serialization: hessian2 # 序列化方式:hessian2/fastjson2/kryo/protobuf
threads: 200 # 业务线程池大小
iothreads: 8 # IO 线程数
accepts: 1000 # 最大连接数
payload: 8388608 # 请求/响应最大字节数(默认 8MB)
# ==================== Provider 配置 ====================
provider:
timeout: 3000 # 远程调用超时(毫秒)
retries: 2 # 重试次数(不含首次调用)
version: 1.0.0 # 服务版本
group: prod # 服务分组
loadbalance: random # 负载均衡策略
cluster: failover # 集群容错策略
delay: -1 # 延迟暴露(毫秒),-1 表示 Spring 初始化完成后暴露
weight: 100 # 服务权重
connections: 1 # 每个消费者最大连接数
actives: 0 # 每个服务最大并发调用数,0 表示不限制
executes: 0 # 每个方法最大并发调用数,0 表示不限制
filter: providerFilter1,providerFilter2 # 服务提供者过滤器
token: false # 令牌验证
# ==================== Consumer 配置 ====================
consumer:
timeout: 5000 # 远程调用超时(毫秒),优先级高于 Provider
retries: 2 # 重试次数
version: 1.0.0 # 服务版本
group: prod # 服务分组
loadbalance: random # 负载均衡策略
cluster: failover # 集群容错策略
check: true # 启动时检查 Provider 是否可用
init: false # 是否在启动时初始化引用
validation: true # 参数验证
filter: consumerFilter1,consumerFilter2 # 服务消费者过滤器
lazy: false # 延迟创建连接
sticky: false # 粘滞连接
# ==================== 监控中心配置 ====================
monitor:
protocol: registry # 从注册中心发现监控中心地址
address: nacos://localhost:8848
# ==================== 元数据中心配置 ====================
metadata-report:
address: nacos://localhost:8848 # 元数据中心地址
retry-times: 30 # 重试次数
cycle-report: true # 是否循环上报9.2 核心配置项速查
| 配置项 | 位置 | 说明 | 默认值 |
|---|---|---|---|
dubbo.application.name | 全局 | 应用名称 | 必填 |
dubbo.registry.address | 全局 | 注册中心地址 | 必填 |
dubbo.protocol.name | 全局 | 协议名称 | dubbo |
dubbo.protocol.port | 全局 | 协议端口 | 20880 |
dubbo.provider.timeout | Provider | 服务端超时 | 1000ms |
dubbo.provider.retries | Provider | 服务端重试 | 2 |
dubbo.provider.version | Provider | 服务版本 | 0.0.0 |
dubbo.provider.group | Provider | 服务分组 | - |
dubbo.provider.weight | Provider | 服务权重 | 100 |
dubbo.consumer.timeout | Consumer | 客户端超时 | 1000ms |
dubbo.consumer.retries | Consumer | 客户端重试 | 2 |
dubbo.consumer.loadbalance | Consumer | 负载均衡 | random |
dubbo.consumer.cluster | Consumer | 集群容错 | failover |
dubbo.consumer.check | Consumer | 启动检查 | true |
十、Spring Cloud Alibaba + Dubbo 集成
10.1 项目结构
dubbo-demo
├── dubbo-api # API 模块(接口定义)
│ ├── pom.xml
│ └── src/main/java/com/example/api/
│ └── UserService.java
├── dubbo-provider # 服务提供者
│ ├── pom.xml
│ └── src/main/java/com/example/provider/
│ ├── ProviderApplication.java
│ └── UserServiceImpl.java
│ └── src/main/resources/
│ └── application.yml
└── dubbo-consumer # 服务消费者
├── pom.xml
└── src/main/java/com/example/consumer/
├── ConsumerApplication.java
└── UserController.java
└── src/main/resources/
└── application.yml10.2 依赖配置
父 POM(dependencyManagement):
xml
<dependencyManagement>
<dependencies>
<!-- Spring Cloud Alibaba BOM -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2023.0.1.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- Spring Boot BOM -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>3.2.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>dubbo-api 模块 pom.xml:
xml
<dependencies>
<!-- 仅需 Lombok,无 Dubbo 依赖 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>dubbo-provider 模块 pom.xml:
xml
<dependencies>
<!-- 依赖 API 模块 -->
<dependency>
<groupId>com.example</groupId>
<artifactId>dubbo-api</artifactId>
<version>1.0.0</version>
</dependency>
<!-- Spring Cloud Alibaba Dubbo Starter -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- Dubbo Spring Boot Starter -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
</dependency>
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>10.3 服务接口定义(API 模块)
java
package com.example.api;
import java.io.Serializable;
/**
* 用户服务接口
* Provider 和 Consumer 共同依赖此接口
*/
public interface UserService {
/**
* 根据 ID 查询用户
*/
UserDTO getUserById(Long id);
/**
* 创建用户
*/
Long createUser(UserDTO user);
}
/**
* 用户数据传输对象
* 必须实现 Serializable 接口
*/
@Data
public class UserDTO implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
private String username;
private String email;
private Integer age;
}10.4 服务提供者(Provider)
java
package com.example.provider;
import com.example.api.UserDTO;
import com.example.api.UserService;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.stereotype.Service;
/**
* 用户服务实现类
* @DubboService 注解将服务注册到 Dubbo 和 Nacos
*/
@DubboService(
version = "1.0.0", // 服务版本
group = "prod", // 服务分组
timeout = 3000, // 超时时间
retries = 2, // 重试次数
weight = 100 // 服务权重
)
@Service
public class UserServiceImpl implements UserService {
@Override
public UserDTO getUserById(Long id) {
// 模拟数据库查询
UserDTO user = new UserDTO();
user.setId(id);
user.setUsername("user_" + id);
user.setEmail("user_" + id + "@example.com");
user.setAge(25);
return user;
}
@Override
public Long createUser(UserDTO user) {
// 模拟数据库插入
// 注意:写操作建议设置 retries=0,这里仅为演示
user.setId(System.currentTimeMillis());
return user.getId();
}
}ProviderApplication.java:
java
package com.example.provider;
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@EnableDubbo // 启用 Dubbo
@SpringBootApplication
public class ProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class, args);
}
}Provider application.yml:
yaml
server:
port: 8081
spring:
application:
name: user-service-provider # 应用名称,会注册到 Nacos
cloud:
nacos:
discovery:
server-addr: localhost:8848 # Nacos 地址
dubbo:
application:
name: user-service-provider # Dubbo 应用名称
logger: slf4j
registry:
address: nacos://localhost:8848 # 注册中心
protocol:
name: dubbo # 使用 dubbo 协议
port: 20880 # Dubbo 服务端口
provider:
timeout: 3000
retries: 2
version: 1.0.010.5 服务消费者(Consumer)
java
package com.example.consumer;
import com.example.api.UserDTO;
import com.example.api.UserService;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/users")
public class UserController {
/**
* @DubboReference 注入远程服务代理
*/
@DubboReference(
version = "1.0.0", // 匹配 Provider 版本
group = "prod", // 匹配 Provider 分组
timeout = 5000, // 超时时间(Consumer 端优先级更高)
retries = 3, // 读操作允许重试
loadbalance = "leastactive", // 负载均衡策略
cluster = "failover", // 集群容错
check = false // 启动时不检查 Provider 是否可用
)
private UserService userService;
@GetMapping("/{id}")
public UserDTO getUser(@PathVariable Long id) {
// 像调用本地方法一样调用远程服务
return userService.getUserById(id);
}
@PostMapping
public Long createUser(@RequestBody UserDTO user) {
return userService.createUser(user);
}
}ConsumerApplication.java:
java
package com.example.consumer;
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@EnableDubbo
@SpringBootApplication
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
}Consumer application.yml:
yaml
server:
port: 8082
spring:
application:
name: user-service-consumer
cloud:
nacos:
discovery:
server-addr: localhost:8848
dubbo:
application:
name: user-service-consumer
logger: slf4j
registry:
address: nacos://localhost:8848
consumer:
timeout: 5000
retries: 2
loadbalance: random
cluster: failover
check: false # 启动时不检查 Provider10.6 关键注解说明
| 注解 | 位置 | 说明 |
|---|---|---|
@EnableDubbo | 启动类 | 启用 Dubbo 自动配置 |
@DubboService | Provider 实现类 | 标记为 Dubbo 服务,自动注册到注册中心 |
@DubboReference | Consumer 字段 | 注入远程服务代理,自动从注册中心订阅 |
@Service | Provider 实现类 | Spring 的 Service 注解,与 @DubboService 共存 |
十一、Dubbo vs Feign vs gRPC
11.1 全面对比
| 维度 | Dubbo | Feign(Spring Cloud) | gRPC |
|---|---|---|---|
| 协议 | TCP 自定义协议 / HTTP/2(Triple) | HTTP/1.1(REST) | HTTP/2 |
| 序列化 | Hessian2 / Protobuf / Kryo(二进制) | JSON(文本) | Protobuf(二进制) |
| 性能 | 高(自定义协议头部精简,长连接复用) | 中(HTTP 头部开销 + JSON 序列化) | 高(HTTP/2 + Protobuf) |
| 连接模型 | TCP 长连接 + NIO 多路复用 | HTTP 连接池(短连接/Keep-Alive) | HTTP/2 多路复用 |
| 跨语言 | 一般(需 Dubbo SDK,Triple 后改善) | 最好(任何语言可调用 HTTP REST) | 好(官方多语言 SDK) |
| 接口定义 | Java Interface | Java Interface + 注解 | .proto 文件 + 代码生成 |
| 服务治理 | 完善(内置负载均衡、容错、路由、降级) | 依赖 Spring Cloud 体系(LoadBalancer) | 依赖外部组件(Envoy/Istio) |
| 注册中心 | 多注册中心(Nacos/ZK/Redis/Consul) | Spring Cloud 注册中心(Nacos/Eureka) | 需外部服务发现(DNS/K8s/Istio) |
| 流式调用 | 支持(Triple 协议) | 不支持 | 原生支持(4 种流式模式) |
| 学习成本 | 中(需理解 RPC 概念) | 低(REST 风格,开发者熟悉) | 高(需学习 Protobuf + .proto 定义) |
| 调试 | 不直观(二进制协议) | 方便(curl/Postman 直接测试) | 不直观(需 grpcurl 等工具) |
| 社区/生态 | Apache 顶级项目,阿里主导 | Spring Cloud 官方,社区庞大 | Google 主导,云原生标准 |
| 适用场景 | 微服务内部高性能 RPC 调用 | 对外 API、跨语言调用、内部 REST 服务 | 跨语言高性能调用、流式场景 |
11.2 选型建议
开始
│
▼
┌──────────────┐
│ 是否需要 │
│ 跨语言调用? │
└──────┬───────┘
是 │ 否
┌───────┴───────┐
▼ ▼
┌──────────────┐ ┌──────────────┐
│ 是否需要 │ │ 是否需要 │
│ 流式调用? │ │ 对外暴露 API? │
└──────┬───────┘ └──────┬───────┘
是 │ 否 是 │ 否
┌──────┴──┐ ┌───────┴───┐
▼ ▼ ▼ ▼
gRPC Feign Feign Dubbo
(HTTP/2) (HTTP) (HTTP) (TCP RPC)简要总结:
- Dubbo:Java 微服务内部高性能 RPC 调用首选,服务治理能力最强。
- Feign:对外 API、跨语言调用、简单 REST 服务场景合适,学习成本最低。
- gRPC:跨语言 + 高性能 + 流式调用的场景,适合混合技术栈团队。
十二、面试要点
面试题 1:Dubbo 的工作流程是什么?
回答要点:
- 服务注册:Provider 启动时,将服务信息(接口名、IP、端口、方法列表)注册到注册中心(Nacos/ZK)。
- 服务订阅:Consumer 启动时,从注册中心订阅所需服务,获取 Provider 地址列表并缓存到本地。
- 服务调用:Consumer 通过动态代理将接口调用转为 RPC 请求,经过集群容错(Failover)、负载均衡(Random/RoundRobin)、过滤器链,最终通过 Netty 发送 TCP 请求。
- 服务处理:Provider 收到请求后,反序列化、经过过滤器链、反射调用业务实现,返回结果。
- 监控上报:调用次数、耗时、成功率等统计数据上报到监控中心(Dubbo Admin)。
- 心跳检测:Provider 和 Consumer 定期向注册中心发送心跳,注册中心剔除不健康节点。
面试题 2:Dubbo 的负载均衡策略有哪些?
回答要点:
| 策略 | 原理 | 适用场景 |
|---|---|---|
| Random(默认) | 带权重的随机选择 | 通用场景,各 Provider 性能相近 |
| RoundRobin | 带权重的轮询 | 各 Provider 性能完全一致 |
| LeastActive | 选择活跃请求数最少的 Provider | Provider 性能差异大,响应时间不一致 |
| ConsistentHash | 相同参数哈希到同一 Provider | 有状态服务、缓存亲和性 |
追问:RoundRobin 有什么问题?
答:当 Provider 响应时间不一致时,慢的 Provider 会累积更多请求(因为轮询不考虑当前负载),可能导致请求堆积。LeastActive 策略可以解决这个问题。
面试题 3:Dubbo 的集群容错策略有哪些?
回答要点:
| 策略 | 原理 | 典型场景 |
|---|---|---|
| Failover(默认) | 失败后自动切换其他 Provider 重试 | 幂等读操作 |
| Failfast | 失败立即报错,不重试 | 非幂等写操作(创建订单) |
| Failsafe | 失败忽略异常,记录日志 | 非关键功能(日志审计) |
| Failback | 失败后放入队列,后台定时重试 | 异步通知,保证最终一致性 |
| Forking | 并行调用多个 Provider,取第一个成功 | 极致低延迟(实时推荐) |
| Broadcast | 广播调用所有 Provider | 全局通知(刷新缓存) |
追问:为什么写操作不能用 Failover?
答:写操作(INSERT/UPDATE/DELETE)不是幂等的。如果 Provider 实际执行成功但 Consumer 因网络超时没收到响应,Failover 会重试到另一个 Provider,导致数据重复写入。
面试题 4:Dubbo 3.x 应用级服务发现是什么?为什么要从接口级升级?
回答要点:
Dubbo 2.x 接口级服务发现:以接口为粒度注册,一个应用有 N 个接口、M 个实例,注册中心存储 N x M 条数据。当接口数量多时,注册数据量爆炸式增长,增大注册中心压力。
Dubbo 3.x 应用级服务发现:以应用为粒度注册,注册中心只存储 M 条数据(实例数),与接口数量无关。数据量大幅减少,注册中心压力恒定。
升级原因:
- 降低注册中心存储和推送压力。
- 与 Spring Cloud(应用级)和 Kubernetes Service(应用级)对齐,更好地融入云原生生态。
- 支持 Proxyless Service Mesh,Dubbo 可直接与 Istio 控制面通信。
面试题 5:Dubbo 和 Feign 的区别?什么时候用哪个?
回答要点:
| 维度 | Dubbo | Feign |
|---|---|---|
| 协议 | TCP 长连接 RPC | HTTP/1.1 REST |
| 性能 | 高(二进制序列化 + 长连接) | 中(JSON + HTTP 开销) |
| 服务治理 | 内置完善 | 依赖 Spring Cloud 体系 |
| 跨语言 | 一般 | 最好 |
| 调试 | 不直观 | curl/Postman 直接测试 |
选型建议:
- Dubbo:Java 微服务内部高性能调用,服务治理要求高,QPS 大。
- Feign:对外 API,跨语言调用,内部服务间调用量不大,团队对 REST 更熟悉。
面试题 6:Dubbo 的 RPC 调用原理(从 Consumer 到 Provider 的完整链路)?
回答要点:
- 代理层(Proxy):Consumer 通过
@DubboReference注入的不是真正的实现类,而是 Dubbo 生成的动态代理对象(Javassist/JDK Proxy)。 - 集群层(Cluster):代理对象将调用委托给 Cluster 层,Cluster 根据配置的容错策略(Failover/Failfast 等)决定如何调用。
- 负载均衡层(LoadBalance):Cluster 通过 LoadBalance 从 Provider 列表中选择一个节点。
- 过滤器链(Filter):在调用前后执行一系列 Filter(监控、限流、鉴权、上下文传递等)。
- 协议层(Protocol):将调用请求(接口名、方法名、参数类型、参数值)序列化为二进制数据,通过 Netty 的 TCP 长连接发送。
- 网络传输:Provider 端 Netty 接收数据,Protocol 反序列化。
- 过滤器链(Filter):Provider 端执行 Filter 链。
- 反射调用:通过反射找到对应的实现类和方法,传入参数执行业务逻辑。
- 返回结果:将结果序列化后原路返回给 Consumer。
关键点:整个过程中,Consumer 完全无感知,就像调用本地方法一样。这是 Dubbo 的核心设计理念——透明化远程调用。
参考文档:
