Dubbo服务框架面试题库
生成时间:2026-05-23 16:01 | 题目数量:10道
【面试题】1. Dubbo的URL总线设计是怎么回事?为什么说URL是Dubbo的配置总线?
【标准答案】Dubbo里所有配置信息最终都会被封装成URL对象,它就像一条总线把整个框架串联起来。URL的全称是dubbo://host:port/path?key=value&key=value这种格式,参数部分承载了超时、重试、负载均衡、序列化等所有配置。为什么这么设计?因为Dubbo采用微内核+插件架构,各个扩展点(Protocol、Filter、LoadBalance等)之间需要传递上下文信息,如果每个扩展点都定义自己的参数对象,耦合度就炸了。用URL作为统一的数据载体,每个扩展点只从URL里取自己关心的参数,做到松耦合。比如Protocol从URL拿host/port做连接,LoadBalance从URL拿weight做权重,Filter从URL拿timeout做超时控制,各取所需互不干扰。这也是Dubbo架构设计的精髓之一——用统一的契约消除组件间的依赖。
【面试题】2. Dubbo SPI和Java SPI有什么区别?为什么Dubbo要自己搞一套?
【标准答案】三个核心区别。第一,Java SPI会一次性加载所有实现类,不管你用不用全给你实例化了,浪费资源;Dubbo SPI是按需加载,配置文件里声明了但只有真正用到时才实例化。第二,Dubbo SPI支持AOP特性——自动包装(Wrapper),如果你的扩展类有一个构造函数参数是扩展点接口类型,Dubbo会自动把它识别为Wrapper,像套洋葱一样一层层包上去,这就是Filter链的实现原理。第三,Dubbo SPI支持自适应扩展(@Adaptive),运行时根据URL参数动态决定用哪个实现,这是实现SPI灵活切换的关键。配置文件格式也不一样,Java SPI是每行一个全限定类名,Dubbo SPI是key=value格式,key就是扩展名,可以通过@SPI("key")指定默认实现。说白了,Java SPI太简陋了,Dubbo SPI在它的基础上做了延迟加载、依赖注入、AOP包装、自适应选择,是Dubbo插件化架构的基石。
【面试题】3. 说说Dubbo服务暴露的完整流程,从源码角度讲讲。
【标准答案】服务暴露分两大步:本地暴露和远程暴露。入口是ServiceBean的afterPropertiesSet(),最终调到ServiceConfig.doExport()。首先把接口配置组装成URL,然后判断是否需要本地暴露(scope不为remote时),本地暴露走InjvmProtocol,直接把服务暴露在同一个JVM内,走本地调用不走网络。远程暴露走的是核心流程:第一步,通过ProxyFactory(默认JavassistProxyFactory)把服务实现类包装成Invoker,Invoker是Dubbo的核心抽象模型,代表一个可执行体;第二步,通过Protocol(默认DubboProtocol)把Invoker转成Exporter,这一步会创建Server绑定端口、注册到注册中心;第三步,DubboProtocol.openServer()开启Netty Server监听,RegistryProtocol.register()把URL注册到ZK的/dubbo/com.xxx.Service/providers/目录下。延迟暴露delay参数会让ServiceConfig起一个定时任务延迟执行暴露逻辑。整个流程就是:实现类→ProxyFactory→Invoker→Protocol→Exporter+Server+Registry。
【面试题】4. Dubbo的集群容错策略有哪些?分别用在什么场景?
【标准答案】六大策略。Failover(默认):调用失败自动切换其他Provider重试,默认重试2次共3次调用,适合读操作,写操作可能产生重复要慎用。Failfast:一次调用失败立即报错,不重试,适合非幂等写操作比如下单,重试可能导致重复扣款。Failsafe:调用失败直接忽略,吃掉异常返回空结果,适合日志记录、监控上报这类可容忍丢失的旁路操作。Failback:调用失败记录到失败队列,后台定时重试,适合消息通知这类最终一致性场景。Forking:并行调用多个Provider,取最快的返回,适合实时性要求高的读操作,但浪费服务器资源。Broadcast:逐个调用所有Provider,任意一个报错就报错,适合通知所有节点更新缓存或日志这种场景。实际项目中90%用Failover+Failfast组合:查询用Failover,写入用Failfast。另外Mock是降级策略不是容错策略,当触发Mock条件时返回预设的兜底数据。
【面试题】5. Dubbo服务超时配置的优先级是怎样的?生产上怎么配最合理?
【标准答案】优先级从高到低:Consumer方法级 > Provider方法级 > Consumer接口级 > Provider接口级 > Consumer全局 > Provider全局。一句话:Consumer端配置优先于Provider端,方法级优先于接口级。这个设计的核心思想是Consumer更了解自己的调用场景和超时需求。生产配置建议:Provider端设一个合理的全局默认值(比如3秒),在关键方法上通过方法级配置覆盖(比如支付接口给5秒,查询接口给1秒)。Consumer端一般不重复配置全局值,只在有特殊超时需求的调用方做方法级覆盖。踩坑点:如果你Provider设了3秒超时但Consumer设了1秒,Consumer会先超时触发重试,而Provider那边还在执行,造成资源浪费。所以最好Consumer超时 >= Provider超时,或者干脆Consumer不配让Provider端控制。重试次数同理,默认2次对写操作很危险,写接口一定要配retries="0"用Failfast。
【面试题】6. Dubbo3的应用级服务发现和之前的接口级服务发现有什么区别?为什么要改?
【标准答案】接口级服务发现是Dubbo2的做法:每个接口在注册中心注册一条数据,/dubbo/com.xxx.UserService/providers/下面存每个Provider的URL。问题是什么?一个应用如果有50个接口,100个实例,注册中心就要存50×100=5000条数据,推送量也是5000条。当服务规模到几万实例几万接口时,注册中心扛不住,网络带宽也被撑爆。应用级服务发现是Dubbo3的新方案:每个应用实例只注册一条数据,类似Spring Cloud和K8s Service的做法,注册中心只存/services/app-name/instance-id,元数据(有哪些接口、方法)通过元数据中心或服务自省获取。优势很明确:数据量从接口数×实例数降到实例数,规模效应下注册中心压力骤降。迁移方案Dubbo3也给了双注册模式,同时注册接口级和应用级,Consumer端可以逐步切到应用级发现,实现平滑迁移。这是Dubbo3走向大规模微服务和Service Mesh的基础。
【面试题】7. Dubbo的Filter链是怎么工作的?如何自定义一个Filter?
【标准答案】Filter基于Dubbo SPI的Wrapper机制实现。Protocol在暴露和引用时,会自动加载激活的Filter,按@Activate的order排序,把Invoker一层层包起来,形成责任链。调用时从外到内依次执行Filter.invoke(),每个Filter在调用前后可以加逻辑,最后调next.invoke()传给下一个Filter。内置Filter包括:AccessLogFilter记录访问日志、ActiveLimitFilter控制Consumer并发数、ExecuteLimitFilter控制Provider并发数、TimeoutFilter在Provider端检查超时、TpsLimitFilter做限流、TokenFilter做令牌验证等。自定义Filter三步走:第一步写类实现Filter接口的invoke方法;第二步在META-INF/dubbo/org.apache.dubbo.rpc.Filter文件里配myFilter=com.xxx.MyFilter;第三步加@Activate注解指定激活条件,比如@Activate(group = CommonConstants.PROVIDER)表示Provider端自动激活。注意Consumer端Filter和Provider端Filter是通过group区分的,Consumer端做调用拦截(如日志、限流),Provider端做服务治理(如权限校验、限流)。
【面试题】8. Dubbo的泛化调用是什么?什么场景下会用?
【标准答案】泛化调用就是Consumer端不需要依赖Provider的接口API jar包,也能调用服务。正常调用你得在Consumer的pom里引入Provider的接口定义,但有些场景做不到或者不想做。泛化调用的方式是:Consumer端把GenericService接口当作服务引用,调用时传方法名、参数类型数组、参数值数组,底层通过GenericImplFilter把调用信息打包成通用格式发给Provider,Provider端通过GenericFilter解析后反射调用真实方法再返回。典型场景有三个:第一,API网关,网关要转发所有服务的调用,不可能依赖每个服务的jar包,用泛化调用统一处理;第二,测试平台,动态传入接口名和方法名做线上服务测试;第三,跨语言调用,非Java客户端通过HTTP等方式传入方法名和参数即可调用。泛化调用因为走了反射,性能比原生调用差一些,但灵活性极高。
【面试题】9. Dubbo的线程模型有哪几种?生产上怎么选?
【标准答案】Dubbo有五种Dispatcher线程模型,决定了消息在IO线程还是业务线程处理。AllDispatcher(默认):所有消息都派发到业务线程池,包括请求、响应、连接事件、断开事件,最安全但线程池压力大。DirectDispatcher:所有消息都在IO线程上直接处理,不走业务线程池,性能最高但IO线程可能被阻塞,导致连接处理变慢。MessageOnlyDispatcher:只有请求和响应消息派发到业务线程池,连接事件在IO线程处理,折中方案。ExecutionDispatcher:只有请求消息派发到业务线程,响应和连接事件在IO线程处理,适合请求处理耗时但响应快的场景。ConnectionOrderedDispatcher:请求消息走业务线程池,连接/断开事件走单独的连接线程池(保证有序),适合连接频繁但请求不频繁的场景。生产上90%用默认AllDispatcher就行,除非你明确知道IO线程不会成为瓶颈且需要极致性能,才考虑MessageOnly或Execution。关键调优点是业务线程池的大小,默认固定200线程,CPU密集型调小、IO密集型调大,配合executes参数做Provider端限流保护。
【面试题】10. 生产环境Dubbo服务超时频繁,怎么排查和解决?
【标准答案】分四步排查。第一步,确认超时是偶尔还是持续。偶尔超时看是不是GC导致STW,用jstat -gcutil看GC情况,或-XX:+PrintGCDetails打GC日志;持续超时说明是性能瓶颈。第二步,定位慢在哪里。Consumer端看RpcContext的耗时,Provider端打方法入口出口时间戳,或用Arthas的trace命令追踪方法调用链路,精准定位哪个方法慢。第三步,分析根因。常见几个:业务逻辑本身慢(SQL没索引、外部接口超时),用慢查询日志和链路追踪定位;线程池满了导致排队,看DubboAdmin的线程池监控或jstack看线程状态;连接数不够,默认一个Consumer对每个Provider只有一个长连接,大并发下连接成为瓶颈;网络问题,跨机房调用延迟高。第四步,针对性解决。SQL慢就加索引或拆分;外部依赖超时就加timeout和fallback;线程池满就调大threads参数或加executes限流保护;连接瓶颈就增加connections数;跨机房就考虑同机房优先路由。最后别忘了把超时时间调到合理值,别一上来就改大timeout,那只是掩盖问题不是解决问题。