Dubbo服务框架面试题库
生成时间:2026-05-22 23:04 (Asia/Shanghai)
【面试题】1. Dubbo的URL总线设计是什么?为什么说URL是Dubbo的统一数据模型?
【标准答案】Dubbo把所有配置信息、运行时参数都抽象成一个URL对象,格式是 protocol://username:password@host:port/path?key=value&key=value。这个设计非常关键——Provider暴露服务时把元数据封装成URL注册到注册中心,Consumer从注册中心拿到URL后据此创建Invoker发起调用,Filter、Router、LoadBalance等扩展点也都从URL读取参数来做决策。本质上URL就是Dubbo的"配置总线",任何组件需要参数都从URL取,避免了到处传参和配置散落的问题。面试时你可以类比Spring的Environment——Spring用Environment统一管理配置,Dubbo用URL统一承载元数据。这也是微内核+插件架构能跑起来的基础:扩展点不需要知道彼此的存在,只需要读URL就能拿到自己要的东西。
【面试题】2. Dubbo SPI和Java SPI的核心区别是什么?@Adaptive注解解决了什么问题?
【标准答案】三个核心区别:第一,Java SPI会一次性加载所有实现类,Dubbo SPI按需加载,通过key=value配置文件指定,用到哪个加载哪个,节省资源;第二,Dubbo SPI支持依赖注入和自动包装(Wrapper),实现类可以自动注入其他扩展点,Wrapper可以做AOP式的功能增强;第三,Dubbo SPI支持自适应扩展——这就是@Adaptive的作用。@Adaptive标注在接口方法上或实现类上,运行时Dubbo会动态生成一个代理类,根据URL中的参数决定用哪个实现。比如Protocol接口的@Adaptive方法会读URL的protocol字段,自动选择dubbo还是triple还是rest协议。这解决了"编译期不知道用哪个实现,运行期才能决定"的问题,是Dubbo插件化的核心机制。
【面试题】3. Dubbo服务暴露的完整流程是怎样的?本地暴露和远程暴露有什么区别?
【标准答案】服务暴露分两步:先本地暴露,再远程暴露。本地暴露走injvm协议,同一个JVM内的服务调用不走网络,直接走本地引用,性能极高;远程暴露走配置的协议(如dubbo/triple),需要启动Netty Server监听端口,然后把服务元数据注册到注册中心。源码流程:ServiceConfig.doExport() → doExportUrls() → 遍历所有协议分别调用export() → Protocol.export(invoker) → 生成Exporter并启动Server。其中Invoker是Dubbo最核心的抽象,Provider端Invoker包装了服务实现类,调用invoke()就执行具体方法。面试亮点:强调本地暴露是Dubbo的优化——同JVM调用走本地,省掉网络开销,这是很多面试官不知道的细节。
【面试题】4. Dubbo的集群容错策略有哪些?在实际项目中怎么选?
【标准答案】六种策略:Failover(默认)失败自动切换其他Provider重试,适合读操作;Failfast快速失败,只调一次,适合非幂等写操作比如下单;Failsafe失败安全,出异常直接忽略,适合日志写入等不影响主流程的操作;Failback失败自动恢复,失败后记录请求后台定时重试,适合消息通知等最终一致场景;Forking并行调用多个Provider,取最快的返回,适合实时性要求高的读;Broadcast逐个调用所有Provider,任意一个报错则报错,适合通知所有节点更新缓存。选型口诀:读用Failover,写用Failfast,边缘操作用Failsafe,异步通知用Failback,追求极致快用Forking。实际项目中最常用的是Failover+Failfast组合:查询接口配Failover retries=2,写操作配Failfast。
【面试题】5. Dubbo的服务超时配置优先级是怎样的?超时后在Consumer和Provider端分别会怎样?
【标准答案】优先级从高到低:Consumer方法级 > Provider方法级 > Consumer接口级 > Provider接口级 > Consumer全局 > Provider全局。核心原则:Consumer优先,方法级优先。超时后的行为:Consumer端在发起调用时启动定时器,超时后直接抛出TimeoutException,不再等待返回;但Provider端不会因为Consumer超时就中断执行,请求还在跑——所以可能出现Consumer已经超时返回,Provider还在处理的情况,这就是超时不是熔断,调用链没断。面试加分点:强调超时配置一定要设在Consumer端才有意义,因为只有调用方知道自己能等多久;另外要配合合理设置retries,否则超时+重试可能导致Provider雪崩。
【面试题】6. Dubbo3的应用级服务发现和原来的接口级服务发现有什么区别?为什么要做这个升级?
【标准答案】接口级服务发现是Dubbo2的做法:每个接口每个Provider注册一条数据到注册中心,比如一个应用有50个接口、10个实例,就注册500条数据。问题很明显——规模大了以后注册中心压力巨大,推送数据量爆炸,Consumer端内存也扛不住。Dubbo3的应用级服务发现改为:每个应用实例只注册一条数据,包含IP、端口和该实例提供的所有接口列表。从500条变成10条,注册中心数据量缩减几十倍。Consumer先通过应用级发现找到Provider实例列表,再通过元数据中心获取具体的接口方法信息。这个升级的收益:注册中心存储和推送压力大幅降低、Consumer端内存占用减少、和Spring Cloud/K8s等服务发现模型对齐,便于Service Mesh融合。迁移上Dubbo3兼容两种模式,可以双注册平滑过渡。
【面试题】7. Dubbo的Filter链是怎么工作的?怎么实现一个自定义Filter来记录接口耗时?
【标准答案】Filter是Dubbo的AOP机制,基于Dubbo SPI扩展。调用链:Consumer发起调用 → 经Consumer端Filter链(如ConsumerContextFilter → FutureFilter → MonitorFilter → 自定义Filter) → 网络传输 → Provider端Filter链(如ContextFilter → ExecuteLimitFilter → 自定义Filter → ExceptionFilter) → Invoker执行。每个Filter包装Invoker,形成责任链,在invoke前后插入逻辑。自定义Filter三步走:第一步,实现Filter接口,在invoke方法里写逻辑,比如long start = System.currentTimeMillis(); Result result = invoker.invoke(invocation); log.info("耗时:{}ms", System.currentTimeMillis()-start); return result;;第二步,在META-INF/dubbo/org.apache.dubbo.rpc.Filter文件里配置key=实现类全限定名;第三步,加@Activate注解指定生效条件,如@Activate(group = {CommonConstants.PROVIDER, CommonConstants.CONSUMER})。注意Filter顺序由@Activate的order属性控制,数值越小越先执行。
【面试题】8. Dubbo的泛化调用是什么?什么场景下需要用到?
【标准答案】泛化调用就是Consumer端不需要依赖Provider的API接口jar包,直接通过GenericService接口发起调用,参数用Map传递。写法:GenericService service = (GenericService) reference.get(); Object result = service.$invoke("方法名", new String[]{"参数类型"}, new Object[]{参数Map});。典型场景有三个:第一,API网关——网关要转发各种Dubbo服务调用,不可能依赖所有业务jar包,只能用泛化调用;第二,测试平台——通用的接口测试工具,动态输入方法名和参数就能调;第三,跨语言调用——非Java客户端没法依赖Java接口,通过泛化调用+Triple协议实现跨语言。泛化调用的代价是失去编译期类型检查,参数构造容易出错,所以只在基础框架层使用,业务代码不要用。
【面试题】9. Dubbo的线程模型有哪些?IO线程和业务线程是怎么分离的?线程池满了会怎样?
【标准答案】Dubbo的Dispatcher策略决定了IO线程和业务线程的分工:AllDispatcher(默认)——所有请求处理和响应都走业务线程池,IO线程只负责网络读写;DirectDispatcher——全部在IO线程执行,不创建业务线程池,适合计算极轻的场景;MessageOnlyDispatcher——只有请求处理走业务线程,连接建立和断开走IO线程;ExecutionDispatcher——只有请求走业务线程,响应走IO线程;ConnectionOrderedDispatcher——连接建立断开排队走独立线程池,请求走业务线程。默认的AllDispatcher最稳妥,IO线程不会被业务逻辑阻塞。线程池满后的行为:Provider端会抛出RejectedExecutionException,Consumer端收到后封装成RpcException,提示"Thread pool is exhausted"。生产上要配合executes参数限流,并且监控线程池使用率,提前扩容或降级。
【面试题】10. 说说你在生产环境中遇到过的Dubbo故障排查经验,比如超时、线程池满或连接耗尽怎么处理?
【标准答案】超时排查四步法:第一步看Consumer日志确认是哪个接口超时、耗时多少;第二步看Provider端监控确认该接口P99耗時是否真的高;第三步如果Provider本身慢,排查是DB慢查询、外部依赖慢还是GC导致;第四步如果Provider不慢但Consumer超时,排查网络抖动或注册中心推送延迟。线程池满的排查:先看Dubbo Admin或监控的线程池指标,确认活跃线程数和队列大小;如果确实满载,看是流量突增还是某些请求阻塞——用jstack抓线程dump,找BLOCKED或WAITING的线程,定位卡在哪行代码。临时方案调大threads参数或开启executes限流防止雪崩,长期方案优化慢接口或拆分独立线程池。连接耗尽比较少见,一般出现在Consumer端大量短连接场景,排查连接泄漏用netstat看TIME_WAIT和CLOSE_WAIT数量,解决方向是确认是否误配了短连接、是否没有正确关闭引用。核心经验:Dubbo故障80%不是Dubbo本身的问题,是业务代码或基础设施的问题,排查时先排除业务层再深入框架层。