很多人我相信在准备Dubbo相关的面试题时时常认为只要准备几题常用的面试题即可,比如:面试官经常会问Dubbo中有几种负载均衡机制以及如何自定义实现Dubbo负载均衡机制(还不懂Dubbo负载均衡?看完让你秒杀面试官)、Dubbo中默认有几种集群容错策略(集群容错?看看Dubbo是如何实现的)、Dubbo的SPI机制(用最清晰明了的方式讲述Dubbo的SPI机制)是如何实现、Dubbo是如何进行服务暴露和调用(之后有时间给大家详细说明)等等。但是有些有心机的面试官通常为了考察大家的高阶水平通常会问几题重要但是略微偏门的问题来考察大家对Dubbo的熟悉程度!
下面我就整理了五题非主流程但非常加分的面试题!
请简单说说Dubbo中的层级结构?
这个问题非常考验大家对Dubbo整体框架结构的熟悉程度!下面这种图非常的经典,但是也非常明确的反应出Dubbo的整体层级架构。
config 配置层:对外配置接口,以 ServiceConfig, ReferenceConfig 为中心,可以直接初始化配置类,也可以通过 spring 解析配置生成配置类
proxy 服务代理层:服务接口透明代理,生成服务的客户端 Stub 和服务器端 Skeleton, 以 ServiceProxy 为中心,扩展接口为 ProxyFactory
registry 注册中心层:封装服务地址的注册与发现,以服务 URL 为中心,扩展接口为 RegistryFactory, Registry, RegistryService
cluster 路由层:封装多个提供者的路由及负载均衡,并桥接注册中心,以 Invoker 为中心,扩展接口为 Cluster, Directory, Router, LoadBalance
monitor 监控层:RPC 调用次数和调用时间监控,以 Statistics 为中心,扩展接口为 MonitorFactory, Monitor, MonitorService
protocol 远程调用层:封装 RPC 调用,以 Invocation, Result 为中心,扩展接口为 Protocol, Invoker, Exporter
exchange 信息交换层:封装请求响应模式,同步转异步,以 Request, Response 为中心,扩展接口为 Exchanger, ExchangeChannel, ExchangeClient, ExchangeServer
transport 网络传输层:抽象 mina 和 netty 为统一接口,以 Message 为中心,扩展接口为 Channel, Transporter, Client, Server, Codec
serialize 数据序列化层:可复用的一些工具,扩展接口为 Serialization, ObjectInput, ObjectOutput, ThreadPool
对于这么多层级结构其实我们重点关注我们常见的就可以了,比如:config 配置层、proxy 服务代理层、registry 注册中心层、cluster 路由层、protocol 远程调用层这种,能知道这么多层的作用已经非常不错了。
Dubbo如何实现RPC调用的统一异常处理?
首先我们需要知道为什么需要异常处理:对于Dubbo的RPC调用都有可能会出现返回报错的情况,这个时候我们就需要对Dubbo调用进行try-catch的异常捕获以防止错误日志的丢失,导致无法定位到原始错误日志。
再就是解决方案了,一般解决方案就是两种:1.通过Dubbo的Filter全局处理;2.通过Spring AOP切面进行处理(中间还需要处理事务问题);
我这边就简单举一下第一种方案:
@Activate(group = Constants.PROVIDER) public class ExceptionFilter implements Filter {// ...非关键代码...@Override public Result invoke(Invoker> invoker, Invocation invocation) throws RpcException {Result result = invoker.invoke(invocation); if (result.hasException() && GenericService.class != invoker.getInterface()) { // ...异常处理... }}}
Dubbo中隐式传参是如何实现的?
要实现隐式传参的功能,我首需要知道Dubbo中有一个比较重要的对象RpcContext。
RpcContext 是一个ThreadLocal的临时状态记录器,当接收到 RPC 请求,或发起 RPC 请求时,RpcContext 的状态都会变化。比如:A 调 B,B 再调 C,则 B 机器上,在 B 调 C 之前,RpcContext 记录的是 A 调 B 的信息,在 B 调 C 之后,RpcContext 记录的是 B 调 C 的信息。
所以可以通过 RpcContext 上的 setAttachment 和 getAttachment 在服务消费方和提供方之间进行参数的隐式传递。
再有就是Filter(过滤器)了,这个主要是为了做统一的处理,使用时需要定义一个ConsumerFilter和ProviderFilter。ConsumerFilter用于setAttachment(key, value),而ProviderFilter用于getAttachment(key),这样就能实现信息链路传递。
Dubbo优雅停机整体流程是怎么样的?
Dubbo 是通过 JDK 的 ShutdownHook 来完成优雅停机的,所以如果用户使用 kill -9 PID 等强制关闭指令,是不会执行优雅停机的,只有通过 kill PID 时,才会执行。
Dubbo优雅停机主要的两个配置:
dubbo.shutdown.hook=true # 是否开启优雅停机dubbo.service.shutdown.wait=20000 #优雅停机等待的时间
Provider在接收到停机指令后
- 1.从注册中心上注销所有服务
- 2.从配置中心取消监听动态配置
- 3.向所有连接的客户端发送只读事件,停止接收新请求
- 4.等待一段时间以处理已到达的请求,然后关闭请求处理线程池
- 5.断开所有客户端连接
Consumer在接收到停机指令后
- 1.拒绝发出新的请求,直接返回调用异常
- 2.等待当前已发送请求执行完毕,如果响应超时则强制关闭连接。
在使用Dubbo过程中有没有遇到那些奇怪的问题?你是如何解决这些问题的?
这是一个开放性问题,主要是考察大家使用Dubbo的经验问题以及如何去处理问题。例如下面几个例子:
- Dubbo生产者注册中心注册时读取网卡错误(多网卡时),导致消费者无法调用生产者
- 如果注册中心是nacos且使用域名的方式对外提供,Dubbo会连接不上注册中心
- Dubbo本地缓存文件被占用(.dubbo/dubbo-registry-xxxxxx.cache)
- 出现调用超时com.alibaba.dubbo.remoting.TimeoutException异常