Skip to content

Dubbo服务框架面试题库 ​

生成时间:2026-05-21 23:27 题目数量:10道


本轮 Dubbo服务框架面试题库(带答案) ​

【面试题】1. Dubbo SPI 和 Java SPI 有什么区别?为什么 Dubbo 要自己实现一套 SPI 机制? ​

【标准答案】核心区别有三点:

第一,加载时机不同。 Java SPI 是一次性加载所有实现类,不管用不用全加载进来,启动慢、浪费资源。Dubbo SPI 是按需加载,配置了哪个扩展点才加载哪个,启动快、省内存。

第二,实现方式不同。 Java SPI 配置文件在 META-INF/services/,文件名是接口全限定名,内容是实现类全限定名。Dubbo SPI 配置文件在 META-INF/dubbo/,文件名是接口全限定名,内容是 key=value 形式,比如 dubbo=org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol,通过 key 灵活获取实现。

第三,Dubbo SPI 有高级特性。 Java SPI 只是简单加载,Dubbo SPI 支持 @Adaptive 自适应扩展(根据 URL 参数动态选择实现)、@Activate 自动激活(条件匹配自动加载)、Wrapper 自动包装(AOP 增强)。这些 Java SPI 都没有。

为什么 Dubbo 要自己实现? 因为 Dubbo 是微内核架构,所有功能都通过扩展点实现。需要灵活切换实现(比如负载均衡策略、序列化方式),需要运行时动态选择,需要 AOP 增强。Java SPI 太简陋,满足不了 Dubbo 的扩展需求。


【面试题】2. Dubbo 服务暴露的完整流程是怎样的?请结合源码说明。 ​

【标准答案】服务暴露分本地暴露和远程暴露两个阶段:

第一阶段:服务暴露入口。 Spring 容器启动,DubboServiceAnnotationBeanPostProcessor 扫描 @DubboService 注解,调用 ServiceBean.export()。export() 内部调用 doExport(),再调用 doExportUrls()。

第二阶段:创建 Invoker。 doExportUrls() 调用 proxyFactory.getInvoker(ref, interfaceClass, url),用动态代理(Javassist 或 JDK)把服务实现类包装成 Invoker 对象。Invoker 是 Dubbo 的核心抽象,代表一个可执行体,对外提供 invoke() 方法。

第三阶段:协议导出。 遍历所有注册中心 URL,对每个协议调用 protocol.export(invoker)。Protocol 是扩展点,默认是 DubboProtocol。export() 内部:

  • 创建 DubboExporter,把 Invoker 缓存到 exporterMap
  • 开启 Netty Server,绑定端口,监听请求
  • 注册服务到注册中心(Zookeeper 临时节点)

第四阶段:本地暴露。 如果配置了 scope=local 或 scope=remote,会分别处理。本地暴露用 InjvmProtocol,不走网络,同一 JVM 内直接调用。

关键源码路径: ServiceBean.export() → doExportUrls() → proxyFactory.getInvoker() → protocol.export() → DubboProtocol.export() → openServer() → createServer() → NettyServer


【面试题】3. Dubbo3 的 Triple 协议是什么?和原来的 Dubbo 协议有什么区别? ​

【标准答案】Triple 协议是 Dubbo3 的默认协议,基于 HTTP/2 + Protobuf,完全兼容 gRPC。

和 Dubbo 协议的核心区别:

第一,传输层不同。 Dubbo 协议是单一长连接 + NIO,自定义传输协议。Triple 基于 HTTP/2,天然支持多路复用、流式传输、头部压缩,一个连接可以承载多个请求响应。

第二,序列化方式不同。 Dubbo 协议默认 Hessian2 序列化。Triple 默认 Protobuf,跨语言友好,性能高,有 IDL 定义,强类型约束。

第三,协议兼容性不同。 Dubbo 协议是 Dubbo 私有协议,只能 Dubbo 框架间调用。Triple 完全兼容 gRPC,Dubbo 服务可以直接被 gRPC 客户端调用,跨语言、跨框架调用更方便。

第四,服务治理能力不同。 Triple 支持 Service Mesh,可以通过 Sidecar 代理接管流量,配合 Istio、Envoy 实现服务治理。Dubbo 协议需要自己实现治理逻辑。

什么时候用 Triple?

  • 需要跨语言调用(Go、Python 客户端调用 Dubbo 服务)
  • 需要和 gRPC 生态打通
  • 需要支持 Service Mesh 架构
  • 需要流式传输(大文件、实时数据推送)

什么时候用 Dubbo 协议?

  • 纯 Dubbo 生态,追求极致性能
  • 对 HTTP/2、Protobuf 有顾虑(老系统改造难度大)

【面试题】4. Dubbo 有哪些负载均衡策略?各适合什么场景? ​

【标准答案】Dubbo 提供五种负载均衡策略:

1. Random 随机(默认)

  • 按权重随机选择,权重越高,被选中的概率越大
  • 调用量大时,调用分布接近权重比例
  • 适用场景:大部分场景,权重配置灵活,调整方便

2. RoundRobin 轮询

  • 按权重轮询,依次选择每个提供者
  • 存在慢提供者累积问题:如果某台机器响应慢,会在它上面堆积请求
  • 适用场景:提供者性能差异不大,请求量均匀

3. LeastActive 最少活跃调用

  • 选择当前活跃调用数最少的提供者
  • 活跃调用数 = 正在处理的请求数
  • 让慢机器收到更少请求,快机器处理更多
  • 适用场景:提供者性能差异大,避免慢机器成为瓶颈

4. ConsistentHash 一致性哈希

  • 相同参数的请求总是路由到同一提供者
  • 基于虚拟节点,提供者下线时只影响部分请求
  • 适用场景:有状态服务、缓存场景(请求需要落到特定节点)

5. ShortestResponseTime 最短响应时间(Dubbo 2.7.7+)

  • 选择响应时间最短的提供者
  • 根据历史调用统计,动态调整
  • 适用场景:对延迟敏感的业务,自动把请求分发给最快的机器

实际选型:

  • 默认用 Random,权重配置灵活
  • 提供者性能不均,用 LeastActive 或 ShortestResponseTime
  • 有状态服务(如 session),用 ConsistentHash
  • 监控发现某台机器慢,临时调低权重,比改代码快

【面试题】5. Dubbo 的集群容错策略有哪些?各用在什么场景? ​

【标准答案】Dubbo 提供六种集群容错策略:

1. Failover 失败自动重试(默认)

  • 调用失败,自动重试其他提供者
  • 可配置重试次数 retries=2(不含第一次)
  • 适用场景:读操作、幂等写操作,允许重试
  • 不适用:非幂等操作(如扣款、新增),重试会重复执行

2. Failfast 快速失败

  • 调用失败,立即抛异常,不重试
  • 适用场景:非幂等写操作、高并发场景,避免重复执行
  • 实际用法:下单、支付、扣库存等关键写操作

3. Failsafe 失败安全

  • 调用失败,吞掉异常,返回空结果
  • 适用场景:非核心业务、日志记录、监控上报,失败不影响主流程
  • 实际用法:写入审计日志、发送通知

4. Failback 失败自动恢复

  • 调用失败,后台记录,定时重试
  • 适用场景:最终一致性业务,允许延迟执行
  • 实际用法:消息通知、异步任务

5. Forking 并行调用

  • 并行调用多个提供者,一个成功即返回
  • 可配置并行数 forks=2
  • 适用场景:实时性要求高,允许浪费资源
  • 实际用法:抢票、秒杀,多节点并发处理,谁快用谁

6. Broadcast 广播调用

  • 逐个调用所有提供者,任意一个失败则失败
  • 适用场景:更新缓存、刷新配置,通知所有节点
  • 实际用法:广播缓存失效通知

生产经验:

  • 读接口用 Failover,retries=2
  • 写接口用 Failfast,不重试
  • 非核心业务用 Failsafe,不影响主流程
  • 并行调用谨慎用,容易放大流量

【面试题】6. Dubbo 服务超时配置优先级是怎样的?生产环境怎么配置? ​

【标准答案】超时配置优先级从高到低:

Consumer 端方法级 > Provider 端方法级 > Consumer 端接口级 > Provider 端接口级 > Consumer 端全局 > Provider 端全局

为什么 Consumer 优先级更高? 因为超时时间主要由调用方决定。Consumer 知道自己的业务能等多久,Provider 无法预知所有调用方的超时预期。Provider 配的超时只是建议,Consumer 可以覆盖。

配置示例:

xml
<!-- Provider 端 -->
<dubbo:service timeout="5000">
    <dubbo:method name="query" timeout="1000"/>
</dubbo:service>

<!-- Consumer 端 -->
<dubbo:reference timeout="3000">
    <dubbo:method name="query" timeout="500"/>
</dubbo:reference>

生产环境配置建议:

第一,Provider 端配置接口超时。 定义接口的默认超时,作为兜底值。查询类设长一点(如 3-5 秒),写入类设短一点(如 1-2 秒)。

第二,Consumer 端按需覆盖。 不同场景对超时要求不同,Consumer 根据业务需求调整。比如同一个查询接口,H5 页面要快(500ms),后台报表可以慢(10s)。

第三,方法级精准控制。 慢方法单独配置,不要影响整体。比如批量导出接口,超时设长;单条查询接口,超时设短。

第四,超时和重试配合。

  • Failover 策略,超时时间要考虑重试次数,总时间 = timeout × (retries + 1)
  • Failfast 策略,超时就是实际等待时间

实际经验:

  • 查询接口 timeout=3000,retries=2
  • 写入接口 timeout=2000,retries=0(Failfast)
  • 批量操作 timeout=30000,retries=0
  • 超时配置要配合监控,经常超时的接口要优化

【面试题】7. Dubbo 如何实现异步调用?和同步调用有什么区别? ​

【标准答案】Dubbo 提供多种异步调用方式:

方式一:CompletableFuture(推荐)

接口定义返回 CompletableFuture:

java
public interface UserService {
    CompletableFuture<User> getUserAsync(Long userId);
}

Consumer 调用:

java
CompletableFuture<User> future = userService.getUserAsync(123L);
// 不阻塞,继续执行其他逻辑
User user = future.get(3, TimeUnit.SECONDS);

方式二:RpcContext 异步

java
// Consumer 端
userService.getUser(123L);
CompletableFuture<User> future = RpcContext.getContext().getCompletableFuture();

方式三:AsyncContext(Provider 端异步)

java
public void asyncMethod(AsyncContext asyncContext) {
    asyncContext.start(() -> {
        // 异步处理业务
        asyncContext.write(result);
    });
}

同步 vs 异步的区别:

维度同步调用异步调用
线程占用调用期间线程阻塞等待线程发起请求后立即释放
资源利用率高并发时线程池压力大线程利用率高,支持更高并发
编程复杂度简单,直接返回结果需要 Future 或回调处理结果
适用场景调用快、并发低调用慢、并发高、需要并行调用

异步调用适用场景:

第一,并行调用多个服务。 比如详情页,需要调用用户服务、订单服务、商品服务,三个异步并行调用,总时间 = max(各服务时间),而不是 sum。

第二,高并发场景。 线程池有限,异步调用不阻塞线程,支持更高并发。

第三,流式处理。 大文件上传下载,异步流式传输,避免内存溢出。

生产经验:

  • 能用异步就用异步,特别是聚合服务
  • 注意超时控制,异步也超时
  • Future.get() 要指定超时时间,避免无限等待
  • 异步调用的异常不会直接抛出,要在 get() 时捕获

【面试题】8. Dubbo3 应用级服务发现和接口级服务发现有什么区别?为什么要改成应用级? ​

【标准答案】Dubbo3 之前是接口级服务发现,Dubbo3 改为应用级服务发现。

接口级服务发现(Dubbo2.x):

  • 注册中心存储:接口维度,每个接口独立注册
  • 注册数据:interface → [provider1, provider2, ...]
  • 问题一:注册中心压力大。一个应用暴露 100 个接口,就注册 100 条数据。大规模集群,注册中心 ZK 节点爆炸。
  • 问题二:冗余数据。同一应用的多个接口,提供者地址相同,重复注册。
  • 问题三:和 Spring Cloud、K8s 体系不兼容。它们都是应用级,Dubbo 接口级无法复用基础设施。

应用级服务发现(Dubbo3):

  • 注册中心存储:应用维度,每个应用一条数据
  • 注册数据:application → [ip:port, metadata]
  • 元数据包含该应用暴露的所有接口
  • 优势一:注册数据量大幅减少。100 个接口只注册 1 条数据,压力降低 2 个数量级。
  • 优势二:和 Spring Cloud、K8s、Nacos 应用级发现对齐,复用现有基础设施。
  • 优势三:支持 Service Mesh。Sidecar 代理基于应用级发现,接口级无法适配。

为什么要改?

第一,规模问题。 大厂一个应用可能暴露几百个接口,集群几千个应用,接口级注册数据量是百万级,ZK 扛不住。

第二,云原生适配。 K8s、Istio、Spring Cloud 都是应用级,Dubbo 要融入云原生生态,必须对齐。

第三,运维简化。 应用级发现,运维视角清晰:应用上线、应用下线、应用扩容。接口级发现,运维要理解每个接口,复杂度高。

迁移方案: Dubbo3 支持双注册模式:同时注册接口级和应用级,平滑迁移。Consumer 先尝试应用级发现,失败降级到接口级。迁移完成后,关闭接口级注册。


【面试题】9. Dubbo 服务线程模型是怎样的?IO 线程和业务线程如何分工? ​

【标准答案】Dubbo 采用 Reactor 线程模型,IO 线程和业务线程分离。

线程模型架构:

Client请求 → Netty IO线程(读取请求) → ChannelHandler(解码) → 业务线程池(执行) → 响应

两种线程池:

1. IO 线程池(EventLoop Group)

  • 职责:网络 IO 操作,连接建立、请求读取、响应写回
  • 线程数:默认 CPU 核数 × 2
  • 特点:非阻塞,一个线程处理多个连接,高性能
  • 配置:<dubbo:protocol iothreads="4"/>

2. 业务线程池(ThreadPool)

  • 职责:执行业务逻辑,调用服务实现类
  • 线程数:默认固定大小 200,可配置
  • 特点:阻塞式,线程执行业务逻辑,可能慢
  • 配置:<dubbo:protocol threads="500"/>

Dispatcher 线程派发策略:

Dubbo 提供 5 种 Dispatcher,决定请求在哪个线程处理:

Dispatcher说明适用场景
AllDispatcher(默认)所有消息都派发到业务线程池通用场景
DirectDispatcher直接在 IO 线程执行极简场景,业务逻辑快
MessageOnlyDispatcher仅请求消息派发到业务线程减少线程切换
ExecutionDispatcher仅请求消息派发,不处理响应高吞吐场景
ConnectionOrderedDispatcher连接事件单独线程池处理连接管理场景

IO 线程和业务线程分工:

  • IO 线程:负责网络传输,解码请求、编码响应,不做业务逻辑
  • 业务线程:负责业务逻辑,调用服务实现、执行 Filter 链

为什么要分离?

  • IO 操作快,不能被慢业务逻辑阻塞
  • 业务逻辑可能慢(查数据库、调外部服务),不能阻塞网络层
  • 分离后,IO 线程专注网络,吞吐量稳定;业务线程池可独立扩容

生产配置经验:

  • 默认 AllDispatcher,大部分场景够用
  • 业务逻辑极快(纯计算、缓存命中),可用 DirectDispatcher,省去线程切换开销
  • 业务线程池大小:CPU 密集型设 CPU 核数 × 2,IO 密集型设 200-500
  • 监控线程池队列长度,队列满说明线程数不够,要扩容或优化业务逻辑

【面试题】10. 生产环境 Dubbo 服务超时、线程池满了怎么排查? ​

【标准答案】分三步排查:

第一步:确认问题现象

超时问题:

  • 日志报错:Invoke remote method timeout
  • 监控显示:调用耗时超过配置的 timeout
  • 用户反馈:接口响应慢,部分请求失败

线程池满问题:

  • 日志报错:Thread pool exhausted 或 RejectedExecutionException
  • 监控显示:线程池活跃线程数达到上限,队列满
  • 用户反馈:大量请求失败,服务不可用

第二步:定位根因

超时问题排查:

  1. Provider 端慢。 检查提供者日志,看具体耗时在哪:

    • 数据库慢查询 → 查慢 SQL,加索引,优化 SQL
    • 外部服务调用慢 → 检查下游服务状态,调整超时配置
    • 业务逻辑慢 → 代码性能分析,优化算法
  2. 网络问题。 检查网络延迟、带宽、丢包:

    • Provider 机器负载高 → CPU、内存、IO 监控
    • 网络抖动 → 跨机房调用、运营商问题
    • 连接数不够 → 配置 connections 增加连接数
  3. 超时配置不合理。 检查配置:

    • timeout 设置太小 → 根据实际耗时调整
    • retries 配置导致总超时过长 → 调整重试次数

线程池满排查:

  1. 业务逻辑慢。 线程执行时间长,无法释放:

    • 查日志看哪些接口慢
    • 优化慢接口,或拆分接口
    • 降低超时时间,让慢请求快速失败
  2. 流量突增。 并发请求超过线程池容量:

    • 查监控看 QPS 变化
    • 增加线程数:threads="500"
    • 增加队列长度:queues="1000"(不推荐,可能拖垮系统)
    • 限流:配置 executes 限制接口并发数
  3. 线程池泄漏。 线程创建后未释放:

    • 检查是否有阻塞操作(死锁、无限等待)
    • 检查是否有异步任务未正确管理

第三步:应急处理

超时问题:

  • 临时调大 timeout,先让业务可用
  • 降级服务:开启 mock 返回默认值
  • 熔断:配置熔断策略,避免雪崩

线程池满:

  • 扩容提供者:增加节点,分摊流量
  • 限流:限制接口并发数,保护系统
  • 快速失败:设置超时时间,让慢请求失败

预防措施:

  1. 监控告警。

    • 监控线程池使用率、队列长度、拒绝任务数
    • 监控接口平均耗时、超时率
    • 设置告警阈值,提前发现问题
  2. 容量规划。

    • 压测确定系统容量
    • 预留 50% 余量应对突发流量
    • 自动扩缩容配置
  3. 超时配置。

    • 根据压测结果设置合理超时
    • 关键接口单独配置,不依赖全局默认值
  4. 限流熔断。

    • 配置 executes 限制并发
    • 开启 Sentinel/Hystrix 熔断保护

本轮题目覆盖:Dubbo SPI 机制、服务暴露源码、Triple 协议、负载均衡策略、集群容错、超时配置、异步调用、应用级服务发现、线程模型、生产故障排查