Dubbo相关

Dubbo相关

Dubbo相关

📚【面试题目1:Dubbo远程调用的核心原理是什么?如何实现?如何自定义加入鉴权?】

回答要点关键字

(1) 核心原理:服务注册发现、远程通信(RPC)、序列化/反序列化、过滤器链
(2) 实现流程:服务暴露→注册→订阅→调用→响应,依赖 Netty 通信、SPI 扩展
(3) 自定义鉴权:基于 Filter 扩展机制、请求头携带鉴权信息、服务端校验
(4) 关键组件:注册中心(Zookeeper)、服务提供者/消费者、协议层(Dubbo协议)、序列化器

打开详情

🍺基础回答:

Dubbo 是 RPC 框架,核心就是让远程服务像本地调用一样。流程很简单:服务提供者启动后把服务信息注册到注册中心(比如 Zookeeper),消费者启动后去注册中心订阅服务,拿到提供者的地址列表,然后通过 Netty 建立连接,把调用参数序列化后发过去,提供者收到后反序列化、执行方法,再把结果序列化返回。自定义鉴权的话,就用 Dubbo 的 Filter 机制,在调用前加个过滤器,消费者往请求头里塞 token,服务端过滤器校验 token 合法才放行。

🎉高级扩展版:

远程调用全流程拆解:1. 服务暴露:提供者通过 ServiceConfig 暴露服务,生成代理类,将服务元数据(接口名、方法名、地址)注册到注册中心;2. 服务订阅:消费者通过 ReferenceConfig 订阅服务,从注册中心获取提供者地址列表,缓存本地并监听变更;3. 远程通信:消费者通过代理类发起调用,参数经序列化(如 Hessian2、Protobuf)后,通过 Dubbo 协议(默认)或 HTTP/2 协议,由 Netty 发送到提供者;4. 服务端处理:提供者 Netty 接收请求,反序列化参数,通过过滤器链,找到目标服务实现类执行方法,结果序列化后返回。自定义鉴权实现:1. 实现 Filter 接口,重写 invoke 方法,服务端过滤器从 RpcContext 中获取请求头的鉴权信息(如 token);2. 校验逻辑:查询数据库或缓存验证 token 有效性,无效则抛异常;3. 配置生效:通过 META-INF/dubbo/com.alibaba.dubbo.rpc.Filter 文件注册过滤器,消费者端在调用前通过 RpcContext 设置鉴权头。

📌 加分项:

能区分 Dubbo 协议与 HTTP 协议的差异(Dubbo 协议更紧凑、性能更高,基于 TCP 长连接);提到负载均衡在调用时的作用(消费者从地址列表中选一个提供者);鉴权可结合 SPI 实现多种策略(如 token 鉴权、签名鉴权);提到 RpcContext 的作用(存储上下文信息,跨线程传递);结合实际场景,比如鉴权失败返回统一错误码,或集成 Spring Security 做权限联动。

⚠️注意事项:

鉴权信息要加密传输(如 HTTPS 或自定义加密),避免明文泄露;过滤器要控制执行耗时,避免影响接口性能;注册过滤器时要指定作用范围(提供者/消费者),避免无效执行;序列化器选择要兼顾性能和兼容性,避免跨语言调用时序列化失败;鉴权逻辑要考虑分布式场景,token 校验需支持集群共享(如基于 Redis 存储 token)。

📚【面试题目2:Dubbo的SPI机制是什么?与Java原生SPI有何区别?核心应用场景?】

回答要点关键字

(1) 核心定义:服务发现机制,通过配置文件加载接口实现类,支持按需加载、动态扩展
(2) 与原生SPI区别:加载方式(懒加载vs全加载)、扩展能力(自适应扩展、自动包装)、性能
(3) 核心应用:协议扩展、序列化器、负载均衡、过滤器、注册中心适配
(4) 实现原理:ExtensionLoader 加载、@SPI 注解标识、自适应代理(@Adaptive

打开详情

🍺基础回答:

SPI 就是服务提供者接口,Dubbo 用它来实现插件化扩展,比如想换个序列化方式、加个过滤器,不用改核心代码,只需要加个实现类再配个配置文件就行。Java 原生 SPI 是加载接口所有实现类,不管用不用都加载,浪费资源;Dubbo 改进了,是懒加载,用到的时候才加载,还支持自适应扩展,能根据配置自动选实现类,比如根据 URL 里的协议参数选对应的协议实现。

🎉高级扩展版:

Dubbo SPI 核心是 ExtensionLoader 类,完整流程:1. 接口标识:用 @SPI 注解标记接口(如 ProtocolFilter),指定默认实现;2. 配置文件:在 META-INF/dubbo/ 下创建以接口全类名为名的文件,内容为“扩展名=实现类全类名”;3. 加载机制:ExtensionLoader.getExtensionLoader(接口类) 获取加载器,getExtension(扩展名) 加载实现类,支持单例模式;4. 核心特性:- 自适应扩展:用 @Adaptive 注解标记方法,动态生成代理类,根据 URL 参数(如 protocol=dubbo)自动选择实现类;- 自动包装:实现类若有含接口类型参数的构造函数,会自动包装其他扩展(如 Filter 链式调用);- 依赖注入:自动注入其他扩展(通过 setter 方法)。与 Java 原生 SPI 区别:1. 加载方式:原生 SPI 用 ServiceLoader 加载所有实现类(全加载),Dubbo 懒加载(按需加载);2. 扩展能力:原生无自适应、包装、依赖注入,Dubbo 支持;3. 性能:原生全加载开销大,Dubbo 懒加载更高效;4. 容错:Dubbo 支持扩展点包装和降级,原生无。

📌 加分项:

举例说明核心扩展场景,如 Protocol 接口的 DubboProtocol、HttpProtocol 实现,LoadBalance 接口的 RandomLoadBalance、RoundRobinLoadBalance 实现;提到 @Adaptive 的两种用法(标记方法动态生成代理,标记类直接作为自适应实现);解释 Dubbo SPI 如何解决原生 SPI 的类加载问题(避免类冲突,支持自定义类加载器);对比 Spring 的 SPI(Spring.factories),说明 Dubbo SPI 更侧重 RPC 场景的扩展灵活性。

⚠️注意事项:

扩展实现类必须有无参构造函数(或符合自动包装的构造函数),否则加载失败;配置文件路径和文件名必须严格规范,否则加载不到;避免扩展名重复,不同扩展模块的扩展名需唯一;自适应扩展的 URL 参数要与配置文件中的扩展名一致,否则无法匹配;自定义扩展时要注意线程安全,单例模式下避免共享可变状态。

📚【面试题目3:Dubbo的负载均衡机制有哪些?核心实现原理是什么?如何自定义负载均衡策略?】

回答要点关键字

(1) 内置策略:随机(Random)、轮询(RoundRobin)、最少活跃调用数(LeastActive)、一致性哈希(ConsistentHash)
(2) 核心原理:消费者获取提供者地址列表后,按策略选择一个地址发起调用,基于 SPI 扩展
(3) 自定义实现:实现 LoadBalance 接口、重写 select 方法、配置文件注册
(4) 适用场景:随机(默认,通用)、轮询(均匀分配)、最少活跃(避免慢服务)、一致性哈希(会话粘滞)

打开详情

🍺基础回答:

Dubbo 有四种默认负载均衡策略,最常用的是随机策略(默认),就是从提供者列表里随机选一个;轮询是按顺序一个个来,适合每个提供者性能差不多的情况;最少活跃调用数是选当前正在处理的请求最少的提供者,能避免把请求发给慢服务;一致性哈希是根据请求参数的哈希值固定选一个提供者,适合需要会话粘滞的场景。自定义的话,就实现 LoadBalance 接口,重写 select 方法(里面写自己的选择逻辑),再配个 SPI 配置文件,调用时指定用这个自定义策略就行。

🎉高级扩展版:

内置负载均衡核心实现:1. 随机(RandomLoadBalance):默认加权随机,根据提供者权重(默认 1)分配概率,权重越高被选中概率越大,通过 ThreadLocalRandom 生成随机数选择;2. 轮询(RoundRobinLoadBalance):加权轮询,维护计数器,按权重分配轮询次数(如权重 2 的提供者占 2 次轮询),避免普通轮询的负载不均;3. 最少活跃调用数(LeastActiveLoadBalance):统计每个提供者的活跃调用数(正在处理的请求数),优先选活跃数最少的,活跃数相同时按权重随机,能动态避开慢服务;4. 一致性哈希(ConsistentHashLoadBalance):对请求参数(如 ID)和提供者地址做哈希,映射到哈希环上,按顺时针找最近的提供者,支持虚拟节点(默认 160 个),减少节点上下线导致的哈希抖动。自定义负载均衡流程:1. 实现 com.alibaba.dubbo.rpc.cluster.LoadBalance 接口,重写 select(List<Invoker<T>> invokers, URL url, Invocation invocation) 方法,在方法中实现自定义选择逻辑(如基于 IP 哈希、基于服务健康状态);2. 编写 SPI 配置文件:在 META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.LoadBalance 中添加“自定义名=实现类全类名”;3. 配置生效:消费者端通过 @Reference(loadbalance="自定义名") 或 XML 配置指定使用自定义策略。

📌 加分项:

能解释权重的作用和配置方式(服务提供者配置 weight 参数,如 <dubbo:service weight="2"/>);提到最少活跃调用数的活跃数统计原理(通过 RpcStatus 记录每个 Invoker 的活跃数);一致性哈希的虚拟节点作用(解决哈希环上节点分布不均的问题);对比不同策略的性能,如随机策略开销最小,一致性哈希因计算哈希开销略大;结合实际场景,如高并发场景选随机或最少活跃,有状态服务选一致性哈希。

⚠️注意事项:

自定义策略要保证线程安全,select 方法可能被多线程并发调用;权重配置要合理,避免单个提供者权重过高导致负载集中;一致性哈希策略不适合提供者频繁上下线的场景(仍会有少量抖动);最少活跃调用数依赖活跃数统计的准确性,需避免统计逻辑出错;负载均衡策略选择要结合业务场景,如无特殊需求优先用默认的随机策略(性能最优)。

📚【面试题目4:Dubbo的服务注册与发现机制是什么?注册中心挂了会影响已运行的服务吗?】

回答要点关键字

(1) 核心流程:服务暴露注册→注册中心存储→消费者订阅→地址推送/拉取→缓存更新
(2) 注册中心作用:存储服务元数据、服务地址分发、服务上下线通知
(3) 注册中心挂了影响:不影响已运行服务(本地缓存),影响新服务注册/订阅、地址更新
(4) 关键组件:提供者 ServiceConfig、消费者 ReferenceConfig、注册中心客户端(ZookeeperClient)

打开详情

🍺基础回答:

服务注册发现就是提供者把服务信息告诉注册中心,消费者从注册中心拿提供者地址。流程是:提供者启动后,将接口名、地址、端口这些信息注册到 Zookeeper(默认注册中心),注册中心会存储这些数据;消费者启动后,去注册中心订阅自己需要的服务,注册中心把对应的提供者地址列表推给消费者,消费者缓存到本地。注册中心挂了的话,已经运行的服务不受影响,因为消费者本地有地址缓存,还能继续调用提供者;但新的服务没法注册,新的消费者没法订阅,而且如果提供者上下线,消费者也没法收到通知,可能会调用到已经下线的服务。

🎉高级扩展版:

服务注册发现全流程:1. 服务注册:- 提供者通过 ServiceConfig.export() 暴露服务,生成代理类和 Invoker;- 调用注册中心客户端(如 ZookeeperClient),在 Zookeeper 上创建临时节点(/dubbo/接口全类名/providers/ 下),节点数据为服务元数据(URL 格式,如 dubbo://ip:port/接口名?version=1.0);- 临时节点依赖会话心跳,提供者下线(或网络中断)时节点自动删除。2. 服务发现:- 消费者通过 ReferenceConfig.get() 订阅服务,注册中心客户端在 Zookeeper 上为该服务创建临时订阅节点(/dubbo/接口全类名/consumers/ 下);- 注册中心通过Watcher机制监听提供者节点变化,当提供者上下线时,推送最新地址列表给消费者;- 消费者将地址列表缓存到本地(RegistryDirectory 中),并定期拉取更新(容错)。3. 注册中心挂了的影响:- 已运行服务:消费者本地缓存了提供者地址,仍能正常发起调用,不影响服务可用性;- 负面影响:新服务提供者无法注册,新消费者无法订阅获取地址;提供者上下线时,消费者无法收到通知,可能出现调用失败(需依赖 Dubbo 的重试和容错机制);注册中心恢复后,消费者会重新拉取最新地址列表。

📌 加分项:

提到 Zookeeper 上的节点结构(/dubbo/接口名 下有 providers、consumers、configurators、routers 子节点);解释临时节点的作用(自动清理下线服务,避免无效地址);对比不同注册中心(Zookeeper 支持高可用,Nacos 支持动态配置和服务发现一体化);提到 Dubbo 的地址刷新机制(消费者定期从注册中心拉取地址,默认 60 秒一次);结合实际场景,如生产环境需部署注册中心集群(Zookeeper 集群),避免单点故障。

⚠️注意事项:

注册中心需部署集群保证高可用,避免单点挂掉影响服务扩容;消费者本地缓存的地址可能存在过期(提供者已下线但缓存未更新),需配合 Dubbo 的重试(retries)和容错(cluster)机制;提供者注册时要确保 Zookeeper 会话超时时间合理(默认 60 秒),避免网络抖动导致节点误删除;服务接口名和版本号要一致,否则消费者订阅不到对应服务。

📚【面试题目5:Dubbo的容错机制和重试机制是什么?如何配置?核心应用场景?】

回答要点关键字

(1) 容错机制:失败自动切换、集群容错策略(Failover、Failfast、Failsafe 等)
(2) 重试机制:消费者调用失败后自动重试,支持重试次数、重试间隔配置
(3) 配置方式:注解(@Reference)、XML、API 配置,指定 cluster(容错)和 retries(重试)
(4) 适用场景:Failover(普通服务,允许重试)、Failfast(写操作,避免重复执行)、Failsafe(非核心读操作)

打开详情

🍺基础回答:

Dubbo 的容错机制就是调用服务失败后,自动用备用策略处理,比如换个提供者重试。默认的容错策略是 Failover(失败切换),配合重试机制,调用失败会重试其他提供者,默认重试 2 次(总共调用 3 次)。还有其他策略,比如 Failfast 是快速失败,失败了就抛异常,不重试,适合写操作(比如插入数据库,避免重复插入);Failsafe 是失败安全,忽略异常,返回默认值,适合非核心的读操作(比如查询缓存,失败了就返回空)。配置的话,在 @Reference 里加 cluster="failover"retries=3 就行。

🎉高级扩展版:

核心容错策略详解:1. Failover Cluster(默认):失败自动切换,调用失败后,按负载均衡策略重试其他提供者,支持配置重试次数(retries),默认 2 次(不含首次调用),适合读操作或幂等写操作(如查询、更新缓存);2. Failfast Cluster:快速失败,调用一次失败后直接抛异常,不重试,适合非幂等写操作(如插入数据、创建订单),避免重复执行;3. Failsafe Cluster:失败安全,调用失败后忽略异常,返回空结果或默认值,适合非核心服务(如日志采集、统计上报);4. Failback Cluster:失败自动恢复,调用失败后后台异步重试,返回默认值,适合异步通知类服务(如短信发送);5. Forking Cluster:并行调用多个提供者,只要有一个成功就返回结果,适合对响应时间要求高的场景(如实时查询),支持配置并行数(forks);6. Broadcast Cluster:广播调用所有提供者,要求所有提供者都成功,适合全局通知(如更新所有节点的缓存)。重试机制细节:1. 触发条件:仅当调用抛出非业务异常(如网络超时、服务不可用)时重试,业务异常(如参数错误)不重试;2. 配置方式:- 注解:@Reference(retries=3, cluster="failover");- XML:<dubbo:reference retries="3" cluster="failover"/>;- 全局配置:<dubbo:consumer retries="3"/>(所有消费者生效);3. 重试间隔:Dubbo 2.7+ 支持配置重试间隔(retry.sleep),默认无间隔,避免短时间内频繁重试给服务端压力。

📌 加分项:

能解释幂等性的重要性(重试机制要求服务方法幂等,否则可能出现重复数据);提到容错策略的扩展(通过 SPI 实现自定义容错策略,实现 Cluster 接口);对比不同策略的性能,如 Forking 并行调用开销大,Failover 重试过多可能导致服务端压力;结合实际场景,如核心读服务用 Failover+3 次重试,订单创建用 Failfast+0 重试,日志服务用 Failsafe;提到 Dubbo 的熔断机制(整合 Sentinel 或 Hystrix),与容错重试配合提升服务稳定性。

⚠️注意事项:

重试次数不宜过多(默认 2 次即可),过多重试会加剧服务端压力和网络开销;非幂等写操作严禁使用 Failover 策略和重试,否则会导致数据重复(如重复下单);重试机制仅能解决临时故障(如网络抖动),无法解决服务端持续异常,需配合熔断和降级;配置全局重试时要注意,避免对所有服务统一配置高重试次数,应按服务类型差异化配置。