ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Dubbo面试题深度解析:从RPC原理到服务治理实战

Dubbo面试题深度解析:从RPC原理到服务治理实战 你不是来背答案的你是来搞清楚Dubbo到底怎么回事的。Dubbo面试题这个关键词网上随便一搜就是大几十篇但我是真的失望——大部分都是把文档抄一遍、把概念堆一堆看着40道题很齐全真去面试现场一追问立马露馅。我这些年面过的候选人里能把Dubbo讲明白的人基本都有一个共同点不是为了面试才学的是真的在项目里踩过坑、看过源码、想过那个“为什么”。所以这篇Dubbo面试题总结我不打算只给你一份“背了就忘”的答案清单。每一道高频题我会先讲面试官到底想从这道题里听到什么再给到一套能说出口、经得起追问的回答思路最后在文末把完整40道题的考点速查表放出来方便你出门前5分钟扫一遍。无论你是准备跳槽的Java后端还是项目中正在用Dubbo但不清楚内部机制的人这篇都值得你花十几分钟认真读完。1. Dubbo面试到底在考什么1.1 为什么这份总结值得你花时间看完先说说市面上的答案为什么没用。你去看那些所谓的“Dubbo面试题40道”十有八九是这种风格问“Dubbo是什么”答“Dubbo是一个高性能的Java RPC框架”问“Dubbo有哪些协议”答“dubbo、rmi、hessian、http、webservice”。这话对吗对。但有用吗基本没用。因为面试官问“Dubbo是什么”他真正想听的其实是你对Dubbo解决什么问题有没有体感。一个框架存在的意义不是因为它“高性能”“分布式”而是因为它解决了一个具体的痛点——在微服务架构下服务之间如何高效、稳定、可治理地完成远程调用。我见过太多候选人把概念背得滚瓜烂熟一上来就讲“服务注册发现”“负载均衡”“容错机制”但当我问“你项目里注册中心用的什么为什么这么选”他就开始支支吾吾。这就是典型的知识没有锚点。这篇总结里我会尽量把每道题都和真实的业务场景挂钩让你知道答案背后的来龙去脉。1.2 面试官考察Dubbo的四个层次这几年我做面试官发现考察Dubbo基本逃不出四个层次。你可以拿来自测看自己目前处于哪一层。第一层概念层能说清楚Dubbo是什么、能干什么、和Spring Cloud有什么区别。这是最基础的但很多人死在这一层因为他只会说“Dubbo是RPC框架”但说不清RPC和HTTP调用的本质区别。第二层原理层能讲明白一次远程调用背后发生了什么。从服务暴露、注册、订阅到负载均衡、集群容错、Netty通信、序列化协议这一整条链路有没有真正串起来。大多数候选人会挂在“服务引用拿到的到底是个什么对象”这种细节上。第三层实践层在真实项目中用过踩过坑。注册中心挂了怎么办、服务超时怎么排查、为什么扩容了流量还是压在老机器上这些不是看书能看出来的。第四层源码层能聊SPI机制、自适应扩展点、协议编码细节。到了这一层基本就是offer收割机了但说实话大部分面试不会问到这么深能讲好第三层已经超过很多人。这篇文章的主体结构我就是按照这四个层次来组织的。2. 核心概念篇三道送分题别让它变成送命题2.1 什么是Dubbo它到底解决了什么问题这道题基本必考但回答质量天差地别。初级答法Dubbo是一个高性能的Java RPC框架。这个回答没毛病但很干瘪。我建议你换个讲法Dubbo是一个分布式服务治理框架它解决的核心问题是让一个应用能像调用本地方法一样调用远程方法同时把服务注册发现、负载均衡、熔断降级、流量路由这些微服务场景下的基础能力都内置好了。然后补一句关键的在没有Dubbo这种RPC框架之前服务之间调用通常是走HTTP接口你得自己封装HTTP客户端、自己维护服务地址列表、自己处理超时重试服务一多这些重复劳动和稳定性问题就被无限放大。Dubbo把这些能力统一收敛到框架层让业务开发只需要关注接口定义和业务逻辑。这样回答的好处是你展示的不只是“知道一个框架”而是“知道框架被发明出来的理由”。有理由的知识才能对抗追问。2.2 RPC和HTTP调用本质区别在哪里这道题是前面那道题的延伸也是面试官非常爱追问的点。你想从根本上理解这个问题可以打个比方HTTP调用就像你去餐厅堂食每次去都要先排队拿号建立连接、点菜发送请求、等菜等待响应吃完走人下次再去重新排队。而RPC调用更像你雇了一个专属管家你和管家之间有长期固定的沟通渠道有什么需求随时说管家甚至能提前帮你把餐具摆好。落到技术上区别主要有四点。一是通信协议层面HTTP是基于文本协议的带着一堆请求头、状态码等额外信息而Dubbo默认的dubbo协议是自定义的二进制协议消息头固定16字节有效载荷占比更高网络开销更小。二是连接模型层面HTTP/1.1时代每次请求基本都要建立连接哪怕有keep-alive也不是长连接池的概念而dubbo协议底层走Netty长连接支持连接复用消费者和提供者之间维持一条长期存活的TCP连接省去了反复握手的时间。三是序列化层面HTTP接口大多用JSON可读性好但体积大、解析慢Dubbo默认走Hessian2二进制序列化性能高出一个量级。四是服务治理层面这是最核心的区别。HTTP接口你需要自己搞一套注册中心、负载均衡、熔断降级的方案而Dubbo这些能力全部内建配置即用。2.3 Dubbo和Spring Cloud选型之争背后的考察点面到“Dubbo和Spring Cloud怎么选”面试官不是真的想让你踩一捧一他是想看你有没有自己的技术判断。我的回答框架是这样的两者定位不同。Dubbo更专注于服务间的高性能RPC调用和服务治理通信效率高适合内部服务之间调用量大、对延迟敏感的场景。Spring Cloud是一套微服务全家桶它围绕着HTTP REST接口构建生态非常完整从配置中心、网关、熔断到链路追踪都有成熟组件开发体验统一但性能上不如Dubbo这种二进制RPC。如果非要给个选型建议我会这么说如果团队已经重度依赖Spring生态而且服务间调用频次不算极致敏感Spring Cloud全家桶省心很多如果核心链路对性能和稳定性要求高或者已经有现成的Dubbo经验沉淀选Dubbo更稳。一个很加分的加分项是提到这两者其实不是非此即彼的关系。Dubbo可以通过配置支持REST协议Spring Cloud里面也能集成Dubbo作为RPC框架现在很多团队是Spring Cloud体系做外围、Dubbo做核心调用链路。这个视角会显得你的架构视野比较完整。3. 架构与调用链路篇把一次RPC调用讲清楚3.1 一次Dubbo调用背后发生了什么这道题是Dubbo面试里的“八股文之王”但恰恰最容易被低估。你可以从一条链路开始说起之后多深问一步。先说链路。一次完整的Dubbo调用消费者侧的调用链大致是业务代码拿到的是一个代理对象Proxy这个代理对象内部会经过一个叫Invoker的调用链依次经过Cluster集群容错、Directory服务目录获取可用服务列表、Router路由规则过滤、LoadBalance负载均衡选一个节点然后通过Protocol层把请求交给Netty客户端经过编码、序列化后发送到提供者。提供者这一侧Netty服务端解码请求后经过线程池分发到业务处理链再通过Invoker找到对应的接口实现类反射调用真实方法再把结果序列化返回。讲完这条链路之后你一定要主动追问自己一句消费者手里那个代理对象到底是怎么来的这就引出了服务引用的概念。消费者在启动时ReferenceConfig会创建一个代理对象这个代理对象内部绑定了一个集群版的Invoker。这个Invoker并不关心具体调用哪台机器它只负责把请求交给DirectoryDirectory会从注册中心拿到服务提供者列表并且实时监听变化。所以每次调用时Invoker都是动态地从最新的服务列表里选一台去调。3.2 服务暴露与服务引用两个经常被忽略的细节面试官如果追问“提供者启动时发生了什么”你要能把服务暴露的过程讲清楚。服务暴露简单说就是把接口实现类封装成一个Invoker再通过Protocol把Invoker导出Export成一个网络服务端口然后把这个服务的元数据接口名、版本、地址、端口注册到注册中心。这里有个容易忽略的细节Dubbo的配置里有接口级配置和参数配置这部分配置会先组装成一个ServiceConfig对象。ServiceConfig在Spring容器刷新完成之后会触发export逻辑。源码里有个著名的判断——如果没有配置注册中心它依然会暴露本地服务这是为了支持点对点直连调试。很多人在本地开发调试Dubbo服务时发现配置了注册中心但调不通其实可以绕开注册中心直接直连后面我会在排查章节细说。服务引用则正好反过来消费者启动时ReferenceConfig会根据接口名去注册中心订阅服务列表拿到列表后创建一个远程调用代理。这里同样有一个很多人不知道的细节服务引用分饿汉式和懒汉式默认是饿汉式也就是Spring启动时就创建好代理并完成订阅如果服务不可用启动阶段就可能报错。有些场景为了不阻塞启动可以设置为懒加载等第一次真正调用时再去创建。3.3 协议怎么选Dubbo协议、gRPC、RESTDubbo支持的协议一堆面试高频是让候选人说说默认协议和如何选型。默认协议就是dubbo协议它是基于TCP的二进制私有协议采用Netty做底层通信特点是连接数少、数据包小、传输快。它内部有非常精巧的设计协议头固定16字节包含魔数、序列化ID、消息类型、状态、请求ID和数据长度——这个固定头非常关键它让接收方能精准地切分出一个个完整请求解决TCP粘包拆包问题。但dubbo协议的问题也很明显它是私有协议跨语言能力弱只适合Java与Java之间的通信。如果你们公司有Python、Go的服务想接入就没法直接用这个协议。这时候需要考虑Triple协议。Triple是基于HTTP/2的协议从Dubbo 3.0开始作为主推协议之一。它最大的优势是支持HTTP/2的流式通信、支持跨语言并且能和gRPC互通。如果你准备走云原生路线或者有多语言接入需求Triple基本是默认选择。REST协议则适合对外提供HTTP接口、需要和其他异构系统通过标准HTTP方式交互的场景。但说实话如果纯粹是Java服务内部调用REST协议的效率远不如dubbo协议没必要自己也封装一套。3.4 序列化选型Hessian2为什么是默认选项序列化题在面试里经常和协议题一起出现。你可以从“为什么不用JSON”切入。JSON是人类可读的文本格式开发和调试确实方便但它的空间利用率和解析效率在大量RPC调用场景下是不够看的。Dubbo默认选择Hessian2一是因为它是二进制格式体积小、速度快二是它支持动态类型不需要提前定义严格的Schema对Java这种语言比较友好三是它天然跨语言很多语言都有Hessian的实现。不过Hessian2也有坑。它对某些Java类型的兼容不太理想比如常见的LinkedHashMap、HashSet在某些版本下反序列化可能出问题复杂嵌套泛型也可能解析异常。我在实际项目中就遇到过服务A传了一个带泛型的对象给服务B结果B拿到的对象类型不对排查半天发现是Hessian2泛型擦除的锅。真要追求极致性能可以考虑Protobuf。Protobuf的体积是最小的、解析速度是最快的但代价是必须维护proto文件每改一个字段都需要重新生成代码。对于接口频繁迭代的业务团队来说Hessian2这种免Schema的设计省心很多这也是它成为大众选择的原因。4. 注册中心篇为什么先聊Nacos再聊ZooKeeper4.1 注册中心在Dubbo中扮演什么角色注册中心是Dubbo绕不开的话题而且这两年越来越多人用Nacos所以面试官大概率会问你用过ZK吗Nacos和ZK怎么选。先讲清楚注册中心的角色。没有注册中心的时候消费者要调用提供者必须硬编码对方地址。服务多了之后这个方式必然崩。注册中心本质上是一个“服务通讯录”提供者启动后把自己登记上去下线时自动移除消费者启动后订阅自己关心的服务并且实时感知提供者列表的变化。这个“实时感知变化”很关键。比如某台提供者机器负载过高你把它下线了如果消费者感知不到还是会继续往这台机器发请求就会导致大量报错。注册中心的职责就是把这个状态变化以最快的速度推送给所有消费者。4.2 ZooKeeper临时节点为什么能感知服务上下线如果聊ZooKeeper你要能说出临时节点Ephemeral Node和Watch机制这两个核心概念。提供者启动时会在ZooKeeper的指定路径下创建一个临时节点节点内容就是服务地址。临时节点的特性是创建它的客户端会话一断节点自动消失。这就很好地解决了“服务宕机了注册中心怎么知道”的问题——不需要提供者主动上报下线ZooKeeper通过维护会话心跳就能感知客户端失联然后自动清理临时节点。消费者则在这个路径上设置Watch。一旦这个路径下有节点新增、减少或者内容变化ZooKeeper就会主动通知所有设置了Watch的消费者消费者收到通知后刷新本地缓存的服务列表。这里有个经典追问临时节点和持久节点有什么区别临时节点生命周期绑定了会话会话没了节点就没了适合做服务实例的上下线管理持久节点即使客户端断开也还在适合保存一些需要长期存在的数据比如配置信息。Dubbo里还用到持久节点来记录一些服务元数据这也是你需要了解的。4.3 Nacos与ZooKeeper的对比面试官最想听的点Nacos这几年势头很猛面试问注册中心选型的时候你必须能说出一二三来。最核心的区别在一致性模型上。ZooKeeper是CP模型它保证数据强一致但代价是当发生Leader选举时整个集群短暂不可写这段时间内服务注册和发现都可能受到影响。Nacos默认是AP模型优先保证可用性各节点之间数据可能短暂不一致但服务基本不会被中断。在Dubbo的实际场景里服务发现这个功能真的很需要强一致吗其实未必。注册中心上存的是服务列表的元数据短暂的不一致带来的影响有限但是注册中心不可用导致服务无法上下线影响就大了。所以很多团队从ZK迁移到Nacos核心原因就是看中了它的高可用性。另外两个点也值得提。第一Nacos除了注册中心还内置了配置中心功能一个组件解决两件事对中小团队非常友好。第二Nacos的运维比ZK简单不用单独维护一套集群控制台界面也好用能直观看到服务健康状况、上下线状态、权重配置等。我把关键差异列在下面这张表里。对比维度ZooKeeperNacos一致性模型CP强一致默认AP可切换CP模式服务健康检查基于会话心跳临时实例心跳、非临时实例主动探测配置中心能力不内置内置支持动态配置管理控制台较弱功能完善可视化程度高部署维护成本较高较低Dubbo适配经典适配方案官方支持较好使用广泛4.4 注册中心挂了服务还能调吗这个问题出现频率极高而且最能体现候选人是不是真的线上处理过问题。答案是可以但有条件。消费者在启动时会从注册中心拉取一次全量服务列表缓存在本地内存里并且对服务提供者的地址做了持久化缓存。即使后续注册中心挂掉了消费者依然可以基于本地缓存的地址列表继续发起调用。这就是为什么很多线上故障里注册中心整个集群都挂了线上业务却还在正常跑。但这里有两个隐患你要说清楚。第一新增的提供者不会被发现。你这个时候扩容了新机器或者新服务消费者完全感知不到流量还是全部打向老机器。第二下线的服务也不会被感知。假设提供者发生故障宕机但注册中心挂了没法通知消费者消费者就会一直向这台故障机器发请求直到调用超时触发容错切换。有的团队为了应对这种情况会配置直连模式也就是绕过注册中心在消费者端直接指定提供者地址作为故障期的逃生通道。Dubbo官方也支持这种dubbo://ip:port方式的直连配置。线上预案里把这个作为一种兜底手段是很有必要的。5. 集群、负载均衡与容错篇分布式下的稳定性5.1 负载均衡策略四种策略的原理与场景Dubbo内置的负载均衡策略是送分题但面试官一般会追问这些策略各有什么适应场景你项目里选的哪个为什么。默认是随机负载均衡Random它会基于权重做随机选取权重越高被选中的概率越大。这是最简单也最通用的方案适合大多数场景。加权轮询RoundRobin则是按权重轮流分配请求分布比较均匀。但这里有个容易被忽略的坑如果某个节点响应慢轮询策略依然会把请求分配过去容易导致请求堆积。所以它更适合所有节点处理能力比较均衡的场景。最小活跃数LeastActive是Dubbo里我个人比较推荐的一种。它会记录每个节点当前正在处理的请求数每次优先把请求分发给活跃数最少的节点也就是最“闲”的那台。这实际上是一种动态的负载均衡能做到“能者多劳”应对机器性能差异大的场景很有效。一致性哈希ConsistentHash的原理是基于参数的哈希值来路由相同参数的请求会始终落到同一台机器。这个策略特别适合有状态服务的场景比如基于用户ID分片缓存或者需要把同一个用户的请求稳定路由到同一台后端处理。这里分享一个体感大多数业务团队性能比较稳定时用默认的随机就够了如果机器配置参差不齐建议改成最小活跃数。一致性哈希不要滥用它会牺牲一定的负载均衡性——一旦某类参数特别集中流量也会跟着集中到少数节点上。5.2 集群容错模式从Failover到Forking负载均衡选出的是“调用哪一台”集群容错处理的是“调用失败了怎么办”。Dubbo提供了六种容错模式面试常考的是前五种。默认模式是Failover失败自动切换。当一次调用失败它会自动换一台机器重试。这个模式好用但有个大坑如果接口不是幂等的比如下单、扣款操作重试就可能导致脏数据或者重复扣款。所以如果接口涉及写操作且没法保证幂等一定要设置retries0。Failfast是快速失败只发起一次调用失败立即报错。它适合非幂等写操作或者对延迟极其敏感的接口宁可直接报错也不愿等待重试。Failsafe是失败安全调用出错就静默吞掉异常返回一个空结果。这个模式适合旁路操作比如记录日志、上报监控数据这类可有可无的调用。Failback是失败自动恢复失败后会记录这次失败请求后台定时重新发送。它适合真正需要保证送达的异步操作比如发送短信。Forking是并行调用多个节点只要有一个成功返回就算成功。它会同时给多个机器发请求所以能显著降低单点故障的影响但会放大流量对下游压力很大。通常只在高可用要求极高的场景使用而且并行数要严格控制。5.3 超时、重试与幂等一个连环追击组合面试官特别喜欢把这个话题做成连环炮你超时时间设了多少谁的超时时间生效超时后重试吗重试会不会重复被重复了怎么办先说超时时间的设置。消费者和提供者都可以配timeout但生效的是消费端的配置提供端配置的timeout属于兜底值。原因很好理解接口调用的超时时间理应由调用方来决定因为只有调用方才清楚自己对延迟的容忍度。然后是重试。Dubbo的Failover默认重试次数是2加上首次调用相当于总共最多执行3次。这3次之间通常会有短暂间隔吗不会它是立即重试。所以在高并发、接口偶发抖动的场景下重试可能导致请求量瞬间翻倍。幂等问题的解法主要有三个思路。一是接口设计层面保证幂等比如基于唯一请求号做去重表二是把重试次数关掉宁可让调用失败快速返回由上层业务自己做补偿三是利用Dubbo的幂等方案或者引入分布式事务中间件但这个成本高适合关键交易链路。我在实际项目里的做法是读接口放心用默认重试写接口全部显式设置retries0然后在业务层做失败重试机制。这样既保证了读操作的容错又避免了写操作的重复风险。5.4 服务降级与Mock怎么在设计上兜底服务降级是考察候选人对“高可用设计”理解深度的一道题。Dubbo提供了mock配置它本质上是一个失败兜底。你可以在消费者端配置当远程调用失败、超时或者被拒绝时返回一个本地的兜底结果而不是直接抛异常。比如商城的商品详情页如果推荐服务挂了可以返回一个默认的推荐空列表用户依然能正常看商品只是推荐位空着这对用户体验的影响小很多。降级有两个层面要分清。第一层是服务消费者端的mock兜底这相当于“我自己先扛住”第二层是注册中心侧的路由降级比如动态把某个服务的所有流量摘掉屏蔽对故障服务的访问避免故障扩散。更进阶一点的答法是提到mock和stub的区别。stub是本地代理会在远程调用之前和之后植入逻辑比如参数校验、结果缓存mock则是在调用失败后的兜底实现。两者可以叠加使用先走stub做一些前置处理再走远程调用失败了再走到mock兜底。6. SPI机制与扩展性篇Dubbo的灵魂6.1 JDK SPI和Dubbo SPI的差距SPIService Provider Interface题目能区分出候选人到底看不看源码。如果聊到Dubbo的扩展机制你一定要能对比JDK SPI和Dubbo SPI。JDK SPI的做法是通过ServiceLoader加载META-INF/services/目录下的配置文件把接口的所有实现类一次性实例化。它的最大问题是“一锅端”你只想用其中一个实现它也会把所有实现类全部加载并实例化这会导致资源浪费而且它没法按需获取指定名字的实现。Dubbo SPI把这件事做得更精细。它的配置文件放在META-INF/dubbo/目录下格式是keyvalue。比如你配置了dubboorg.apache.dubbo.rpc.protocol.dubbo.DubboProtocol那么只有当需要名为dubbo的协议时才会去加载这个实现类。这就是按需加载性能和灵活性都好很多。另外Dubbo SPI还内置了IOC和AOP的能力。IOC是指扩展实现类里面如果有setter方法Dubbo可以自动注入其他扩展点AOP是指如果扩展点有Wrapper包装类它会负责把核心实现包起来这种设计让扩展的编排非常灵活。6.2 Adaptive与Activate两个扩展注解的理解Adaptive和Activate是Dubbo SPI机制里两个高频考点但很多人分不清。Adaptive注解是自适应扩展点。它有两种用法一种修饰在实现类上表示这是一个固定的适配类另一种修饰在接口方法上表示Dubbo会在运行期动态生成一个适配类。动态适配类的作用是根据调用时的URL参数决定具体使用哪个实现。举个例子Dubbo的Protocol接口有dubbo、tri、rest等多个实现但消费者调用的时候并不需要在代码里写死用哪个协议而是根据URL里的protocol参数自动选择。这个动态路由的能力就是Adaptive提供的。它特别适合多实现并存、运行时动态切换的场景。Activate注解则是一个自动激活条件标记。它通常用来修饰Filter、Listener这类需要被自动加载的扩展点。Dubbo会根据当前请求的URL参数、分组等条件自动激活匹配的扩展实现。比如你需要在所有调用链路里加一个日志Filter就可以实现Filter接口并用Activate注解声明生效条件Dubbo会自动把它挂到调用链上不需要手动逐个注册。可以这样理解两者的区别Adaptive负责“动态选择用哪个实现”Activate负责“满足条件就把实现自动挂载到调用链”。6.3 自定义一个协议要几步这道题如果面到基本是考察源码功底了。但其实实现起来并不复杂关键是走通流程。自定义一个协议核心是两件事对外暴露服务对内发起调用。你需要实现Protocol接口和相关的Exporter。第一步新建一个类实现Protocol接口。这个接口定义了export和refer两个核心方法。export方法负责把Invoker导出成一个网络服务refer方法负责创建远程调用客户端。第二步写配置文件。在META-INF/dubbo/目录下新建一个以接口全限定名为文件名的文件里面写一行myprotocolcom.example.MyProtocol。第三步在使用处指定协议名。在ServiceConfig或者ReferenceConfig里配置protocol为myprotocol或者在URL参数里带上。如果只是面试口头回答你不需要把每一步代码都背下来但要能说清楚SPI机制决定了扩展点可以动态加载Protocol接口是扩展的入口配置文件和Adaptive机制保证了Dubbo能在运行时根据协议名找到你的实现。这几句话足够证明你读过源码。7. 服务治理与细节题体现经验的地方7.1 优雅停机为什么重要一说到服务治理很多人只会谈服务注册发现和负载均衡但“优雅停机”这个细节才最见功底。为什么需要优雅停机你可以想象一个场景提供者正在处理一个耗时3秒的请求这时候运维执行了发布操作进程直接被杀掉那这些正在处理的请求全部会失败。消费者的Failover重试虽然能切换机器但依然会造成大量调用异常和业务报错。优雅停机的核心逻辑是进程在退出前先把自己从注册中心下线让消费者不再往自己这边分发新请求然后等待正在处理的请求完成或者等待一个配置的超时时间最后再真正关闭网络端口和进程。Dubbo在实现上做了不少细节。比如开启Spring容器关闭钩子后收到关闭信号时会先注销服务、再关闭Netty端口、最后销毁线程池。但是要注意配置了优雅停机不代表一定能等完所有请求如果超时时间设置太短依然会杀掉还在处理的请求。另外Kubernetes场景下要特别小心容器被强制杀死的时机Pod的terminationGracePeriodSeconds要大于Dubbo的优雅停机时间否则优雅停机就是空谈。7.2 多版本、灰度、路由规则服务版本管理在面试里经常以“怎么做灰度发布”的形式出现。Dubbo支持在Service或者Reference里指定version不同版本的服务可以在注册中心共存。消费者通过version指定要调用的版本号或者配置version*来订阅所有版本。这就可以实现最简单的灰度先部署一个V2版本让一部分消费者切到V2验证没问题再全量切换。更精细的路由控制可以通过Router实现。Dubbo支持条件路由、标签路由等。标签路由是非常常用的一种方式给不同的机器打不同的标签比如versiongray或envcanary然后在路由规则里指定哪些消费者只能访问哪些标签的提供者这就是典型的金丝雀发布方案。这里提醒一句多版本灰度和标签路由是两套机制V1和V2版本切换属于版本路由标签路由则是更偏环境和机器维度的控制。面试时如果能区分清楚会显得理解层次更高。7.3 隐式传参和泛化调用什么时候用这两个都是Dubbo的进阶特性属于可以拉开差距的加分项但面试中不少候选人完全不知道。隐式传参简单说就是不需要修改接口定义就能在RPC调用中传递一些附加信息。它的实现方式是借助RpcContext在调用前设置一个attachment这个attachment会随着请求一起发送到提供者那边提供者也能通过RpcContext取出来。比较典型的用途是传递链路追踪ID、用户身份令牌、客户端IP等横切信息避免了在每个接口参数里都加一个context字段。隐式传参有一个大坑它默认是线程绑定的。也就是说在异步调用场景下子线程里可能拿不到主线程设置的attachment导致参数丢失。如果你用异步编程记得要用Dubbo提供的异步上下文传递能力或者干脆把必要参数往正式参数里放。泛化调用则是指消费者在没有接口定义的情况下通过GenericService去调用远程服务。它的调用方式一般是传方法名和参数类型数组、参数值Dubbo在收到之后通过泛化实现把它转化成真实的接口调用。这个特性对网关类系统、测试平台、接口调试工具特别有用因为你不可能让网关把所有业务接口的jar包都依赖一遍泛化调用就很好地解决了这个问题。7.4 线程模型IO线程与业务线程线程模型这道题是很多候选人容易翻车的点因为能透露出你对网络编程底层理解的深度。Dubbo底层通信框架是NettyNetty本身是多线程模型有Boss线程处理连接建立有Worker IO线程处理读写事件。Dubbo又单独有业务线程池来处理具体的服务调用逻辑。这意味着一个请求进来后是先从Netty的IO线程读取数据、解码再丢给Dubbo业务线程池执行真正的业务方法IO线程可以立刻返回去处理下一个连接的数据不会因为某个业务处理慢而阻塞整个事件循环。需要关注的是Dispatcher配置它决定了IO线程和业务线程之间如何协作。默认的all模式是所有消息都派发到业务线程池处理包括请求、响应、连接事件等还有其他模式比如message、execution等只在特定场景下才需要调整。如果业务方法耗时很低、逻辑足够简单可以把线程池调小甚至用IO线程执行部分逻辑但绝大多数情况下不建议新手去乱调这个配置保持默认就好。另一个容易被问到的点是连接数量。dubbo协议默认是长连接消费者和提供者之间会建立连接池connections默认配置有讲究——同一个JVM里同一个服务默认是单连接复用。为什么单连接也能扛高并发因为TCP单连接上的报文是串行发送的但Dubbo通过请求ID把多个请求并行复用同一条连接互不阻塞所以单连接也能承载很高的吞吐。不过如果有特别极端的性能需求可以通过connections参数增加连接数但连接数增加会带来内存和文件描述符开销不是越大越好。8. 高频情景题与线上排查题8.1 “注册中心挂了能继续调用吗”的标准答法前面在注册中心章节其实已经讲到了这个问题但这里我想单独说一说标准答题结构因为这道题追问率实在太高了。建议分三层回答。第一层先给结论能但感知不了变化。第二层讲缓存机制消费者会在本地缓存服务列表调用时优先走本地缓存。第三层讲风险新增服务感知不到、下线服务也感知不到所以注册中心挂掉后要做两手准备——一是快速恢复注册中心二是临时改用直连配置来对关键链路兜底。这套答法有几个好处结论清晰、有原理、有风险意识、有解决方案。比起很多人只回答“能”或者“不能”深度完全是两个级别。8.2 服务调用超时怎么排查线上排查能力面试官会很看重尤其是Dubbo调用超时这种经典问题。我会按这个顺序排查。第一步确认是消费者超时还是提供者处理慢。看提供者的监控或者日志如果方法执行时间本身很长说明是服务端业务逻辑慢比如数据库慢查询、外部接口阻塞如果提供者日志显示方法几十毫秒就返回了但消费者还是超时那问题可能出在网络或者线程池排队上。第二步查线程池。Dubbo的线程池一旦被打满新请求就会进入排队等待等待时间超过timeout后消费者就开始报超时。提供者端可以通过Dubbo的qos或者JMX监控当前活跃线程数、队列大小如果线程数长期接近最大值说明是服务容量不足需要扩容。第三步查网络。包括网络延迟、带宽占用、防火墙丢包等。一个很典型的场景是跨机房调用如果机房之间网络抖动消费者看到的延迟会显著升高。这种问题通过日志往往看不出端倪需要借助链路追踪系统或者抓包来定位。第四步如果是偶发性超时很可能是GC导致的。Full GC或者Young GC停顿几十毫秒到几百毫秒恰好撞上请求处理窗口就会产生超时。这时候需要把GC日志拉出来看看尤其是老年代是否频繁回收。8.3 扩容了但流量没过去是怎么回事这个问题相当实战。很多人遇到过服务快扛不住了紧急加了几台机器但流量还是压在老机器上新机器负载很低。这很可能不是负载均衡的问题而是服务发现延迟在作祟。背后有几个原因。一是服务注册延迟提供者启动后注册到注册中心需要时间而且Dubbo默认有warmup机制新启动的机器权重会从低逐渐升满这是为了防止刚启动的机器CPU缓存还没热、JIT还没生效就被打满流量所以它会“故意”让新机器的流量缓慢增长。这个机制默认需要比较长的时间如果线上情况紧急可以调小或者关掉这个预热。二是消费者缓存刷新延迟消费者本地缓存了服务列表虽然注册中心推送了新节点但推送和刷新毕竟有延迟。三是路由规则限制如果配置了标签路由或者权重路由新机器没有匹配的标签或者权重配错就会导致几乎没有流量分给它。排查思路也很简单去注册中心控制台看新节点是否已经注册上如果注册上了再去看路由规则和权重配置如果都正常那就是预热期还没结束耐心等一会儿或者手动调大权重。8.4 异步调用是默认的吗注意别掉坑里面试里很多人会答“Dubbo支持同步和异步默认是同步”这个答案不能算全对。Dubbo从2.7之后对异步模型做了一次比较大的重构现在叫异步化但对外暴露的API仍然是同步的准确说是同步转异步。具体来说业务代码调一个接口方法拿到返回值看起来是同步的。实际上内部是发起了一次异步请求然后当前线程阻塞等待Future结果返回。这种设计的好处是API简洁又保留了异步的能力——如果你不想阻塞等待可以直接获取到CompletableFuture然后自己在未来某个时间点做回调处理。这里面有个经典的坑如果在Provider端想要异步执行一个耗时很长的任务不能简单地把方法声明为CompletableFuture返回值就完事必须配合CompletableFuture.supplyAsync把任务丢到自定义线程池否则框架还是会在请求线程里同步等待Future完成后才返回值异步效果等于没有。还有一个更隐蔽的坑消费端获取Future对象时如果服务提供方也进行了异步处理上下文里的隐式传参可能丢失这在前面隐式传参小节的坑是一脉相承的。9. 40道题考点速查清单集中过完前面这些高频题之后你可能会好奇剩下的题是什么。其实Dubbo的40道面试题真正核心的考点并没有超出我上面拆解的这些范围。下面这张表就是完整的40道题面试速查版每道题我只保留一句话考点详细解析在前面的章节里都能找到对应内容。面试出发前扫一遍这张表足够帮你把框架重新撑起来。序号高频面试题一句话考点速记1什么是Dubbo高性能Java RPC框架解决远程调用与服务治理问题2Dubbo核心组件有哪些Provider、Consumer、Registry、Monitor、Container3RPC和HTTP调用的区别二进制协议、长连接、序列化效率、服务治理能力不同4Dubbo和Spring Cloud怎么选性能与治理侧重点不同可互补组合5一次Dubbo调用链路Proxy、Cluster、Directory、Router、LoadBalance、Netty、序列化6服务暴露过程封装Invoker、导出服务、注册到注册中心7服务引用过程创建代理、订阅注册中心、拿到服务列表8Dubbo支持哪些注册中心ZooKeeper、Nacos、Redis、Simple等9ZooKeeper怎么实现服务发现临时节点存储地址Watch机制感知变化10临时节点和持久节点的区别临时节点随会话消失持久节点不随会话消失11Nacos和ZooKeeper选型AP与CP模型差异、是否内置配置中心、运维成本12注册中心挂了还能调用吗能基于本地缓存但感知不了新节点和下线节点13什么是服务直连绕过注册中心直接指定提供者地址调用14Dubbo默认协议是什么dubbo协议TCP二进制长连接Netty通信15Dubbo有哪些常用协议dubbo、tri、rest、hessian、rmi、grpc16Triple协议的优势HTTP/2、跨语言、流式、可与gRPC互通17Dubbo为什么用Netty高性能NIO框架、连接复用、适合高并发长连接18TCP粘包拆包怎么解决协议头固定长度Body长度字段解码时按长度切包19Dubbo默认序列化方式Hessian2二进制、体积小、跨语言、免Schema20Hessian2和JSON相比优缺点性能高但调试不便JSON可读但体积大21为什么不用JDK序列化性能差、安全性问题、跨语言能力弱22负载均衡策略有哪些随机、轮询、最小活跃数、一致性哈希23一致性哈希适合什么场景相同参数稳定路由到同一节点适合有状态服务24集群容错模式有哪些Failover、Failfast、Failsafe、Failback、Forking、Broadcast25Dubbo默认容错模式和重试次数Failover默认重试2次26重试会导致什么问题非幂等接口重复执行写接口要设retries027超时时间配置原则消费端优先写接口要谨慎设置重试28怎么保证接口幂等唯一请求号去重、关闭重试、业务层补偿29服务降级怎么实现mock配置返回兜底结果注册中心路由降级30mock和stub的区别stub是本地调用的前置/后置逻辑mock是失败兜底31JDK SPI有什么缺陷全部加载、无法按需获取、不支持IOC和AOP32Dubbo SPI做了什么改进按需加载、支持IOC/AOP、自适应扩展33Adaptive注解作用运行时根据参数动态选择具体扩展实现34Activate注解作用满足条件自动激活扩展常用于Filter自动挂载35如何自定义扩展点实现接口、写SPI配置文件、按名称激活36优雅停机怎么做先下注册中心、等请求处理完、再关端口线程池37多版本灰度怎么实现version区分消费者指定版本号38隐式传参是什么RpcContext传递附件信息注意异步场景丢失问题39泛化调用是什么不需要接口定义通过GenericService远程调用40Dubbo线程模型Netty IO线程业务线程池理解Dispatcher配置这40道题并不是需要你全部背下来的它们更像是一张知识地图。你在面试之前顺着这张地图把每一个考点过一遍能用自己的话讲清楚“是什么、为什么、什么时候用”那你的Dubbo关卡就基本稳过了。10. 一点个人体会最后说点跟技术无关但很重要的东西。我经常遇到候选人问我面试题这么多怎么记得住我的答案一直没变过别背题去用去踩坑。Dubbo这套东西你只有真的在线上遇到过线程池被打满、遇到过一次超时排查到深夜、遇到过新扩容的机器没流量你对外讲的时候才会有那种“这个坑我踩过你别踩”的分量这种分量是面试官一眼就能看出来的。如果你现在正准备面试我建议你拿着这份40道题清单把前面核心章节里的原理再过一遍然后挑几个自己工作里实际用过的场景提前组织好语言。不用贪多能讲深两三个点比泛泛而谈几十个点要有用得多。祝面试顺利。
返回列表