介绍
在集群容错时,dubbo处理的流程如下图所示:

下面是来自官网的介绍,各节点关系如下:
- 这里的
Invoker
是Provider
的一个可调用Service
的抽象,Invoker
封装了Provider
地址及Service
接口信息 Directory
代表多个Invoker
,可以把它看成List<Invoker>
,但与List
不同的是,它的值可能是动态变化的,比如注册中心推送变更Cluster
将Directory
中的多个Invoker
伪装成一个Invoker
,对上层透明,伪装过程包含了容错逻辑,调用失败后,重试另一个Router
负责从多个Invoker
中按路由规则选出子集,比如读写分离,应用隔离等LoadBalance
负责从多个Invoker
中选出具体的一个用于本次调用,选的过程包含了负载均衡算法,调用失败后,需要重选
本篇将介绍这里的 Directory
。
Directory的定义
看下 Directory
的类图:

这里有两种 Directory
:RegistryDirectory
和 StaticDirectory
。看名字就知道是什么意思了,第一种是通过注册中心获取,它的值可能是动态变化的;第二种是不会动态变化的,可以查看其构造方法:
|
|
这里的 invokers
只在构造时被设置了一次。本文主要介绍的是 RegistryDirectory
。
看下 Directory
接口的定义:
|
|
主要是 list
方法,该方法会列出所有的服务提供者。
代码分析
下面从官网给出的消费者的实例来进行分析:
|
|
在调用 sayHello
方法时,会进入 MockClusterInvoker
中的 invoke
方法:
|
|
MockClusterInvoker
主要是进行本地伪装,这个在以后将 Cluster
的时候再详细介绍。
这里的第7行进入 AbstractClusterInvoker
中的 invoke
方法:
|
|
这里可以看到,在 list
方法中调用了 directory
的 list
方法,这里的 directory
对象就是 RegistryDirectory
类型的对象,这里首先会调用 AbstractDirectory
中的 list
方法,然后再调用子类自己实现的 doList
方法:
AbstractDirectory
中的 list
方法:
|
|
该方法主要有两个工作:
- 调用具体的
Directory
列出可用的服务提供者; - 根据路由规则过滤。
这里先不考虑路由,看下 RegistryDirectory
中的 doList
方法:
|
|
这里的核心是 this.methodInvokerMap
,Directory
获取 invoker
就是根据这个值,那么该值是在哪里被更新的呢?
由于是使用的 zookeeper
注册中心,看下上面的类图可以知道,是通过 ZookeeperRegistry
来进行更新的,ZookeeperRegistry
继承自 FailbackRegistry
,在 FailbackRegistry
中的 subscribe
方法会调用 ZookeeperRegistry
实现的 doSubscribe
方法进行更新:
|
|
从上面的类图可以看到,RegistryDirectory
实现了 NotifyListener
接口,该接口定义了一个 notify
方法,doSubscribe
方法执行的最终会调用 RegistryDirectory
中的 notify
方法。
看下 notify
方法的实现,这里简化了一下代码:
|
|
refreshInvoker
进行 methodInvokerMap
的更新:
|
|
我们回到 AbstractClusterInvoker
中的 invoke
方法,再来看一下该方法:
|
|
通过上面的分析可知,在 list
方法中,已经经过了 Directory
和 Router
,按照官网给出的集群容错的图,可以知道,下一步应该是 LoadBalance
,从代码中也可以看到通过 SPI
来获取具体的 LoadBalance
,然后执行具体的调用。
实例分析
下面来具体实践一下。启动两个服务,名字分别为 demo-provider-1
和 demo-provider-2
:

启动一个消费者,名字为 demo-consumer-2
:

在执行到 invoke
方法时,查看获取的 list
:

查看消费者的通知信息:

至此, Directory
的分析就结束了,下篇文章会分析一下 Router
的实现。