Directory的实现
整个容错过程中首先会使用Directory#list
来获取所有的Invoker列表。Directory也有多种实现子类,既可以提供静态的Invoker列表,也可以提供动态的Invoker列表。静态的列表是用户自己设置的Invoker列表;动态列表根据注册中心的数据动态变化,动态更新Invoker列表的数据,整个过程对上层透明。
总体实现
Directory是顶层接口。AbstractDirectory封装了通用的实现逻辑。抽象类包含RegistryDirectory和StaticDirectory两个子类。
AbstractDirectory:
封装了通用逻辑,主要实现了四个方法:检测Invoker是否可用,销毁所有Invoker,list方法,还留了一个抽象的doList方法给子类自行实现。list方法是最主要的方法,用于放回所有可用的list,逻辑分为两步:
- 调用抽象方法doList获取所有Invoker列表,不同子类有不同的实现;
- 遍历所有的router,进行Invoker过滤,最后返回过滤好的Invoker列表。
doList抽象方法则是返回所有的Invoker列表,由于是抽象方法,子类继承后必须要有自己的实现。
RegistryDirectory:
属于Directory的动态列表实现,会自动从注册中心更新Invoker列表、配置信息、路由列表。
StaticDirectory:
Directory的静态列表实现,即将出阿奴的Invoker列表封装成静态的Directory对象,里面的列表不会改变。因为Cluster#join(Directory<T> directory)
方法需要传入Directory对象,因此该实现主要使用一些上层已经知道自己要调用哪些Invoker,只需要包装一个Direcotry对象返回即可的场景。在ReferenceConfig#createProxy
和RegistryDirectory#toMergeMethodInvokerMap
中使用了Cluster#join方法。StaticDirectory的逻辑非常简单,在构造方法中需要传入Invoker列表,doList方法则直接返回初始化传入的列表。
RegistryDirectory的实现
RegistryDirectory中有两条比较重要的逻辑线,第一条:框架与注册中心的订阅,并动态更新本地Invoker列表、路由列表、配置信息的逻辑;第二条:子类实现父类的doList方法。
1. 订阅与动态更新:
这个逻辑主要涉及subscribe、notify、refreshInvoker三个方法,其余是一些数据转换的辅助类方法,如toConfigurators、toRouters。
subscribe是订阅某个URL的更新信息。Dubbo在引用每个需要RPC调用的Bean的时候,会调用directory.subscribe
来订阅这个Bean的各种URL的变化(Bean的 配置在配置中心都是以URL的形式存放的)。这个方法比较简单,只有两行代码,仅仅使用registry.suscribe订阅。
notify就是监听到配置中心对应的URL的变化,然后更新本地的配置参数。监听的URL分为三类:配置configurators、路由规则router、Invoker列表
。工作流程如下:
- 新建三个List,分别用于保存更新三类变化。遍历监听返回的所有URL,分类后放入三个List中
- 解析并更新配置参数:
- 对于router类型的参数,首先遍历所有router类型的URL,然后通过Router工厂把每个URL包装成路由规则,最后更新本地的路由信息。这个过程会忽略以empty开始的URL。
- 对于Configurator类的参数,管理员可以在dubbo-admin动态配置功能上修改生产者的参数,这些参数会保存在配置中心的configurators类目下。notify监听到URL配置参数的变化,会解析并更新本地的Configurator配置。
- 对于Invoker类型的参数,如果是empty协议的URL,则会禁用该服务,并销毁本地缓存的Invoker;如果监听到的Invoker类型URL都是空的,则说明没有更新,直接使用本地缓存;如果监听到的Invoker类型URL不为空,则把新的URL和本地老的URL合并,创建新的Invoker,找出差异的老Invoker并销毁。
dubbo-admin上更新路由规则或参数是通过"override://"
协议实现的,dubbo-admin的使用方法可以查看官方文档。override协议的URL会覆盖更新本地URL中对应的参数。如果是"empty://"
协议的URL,则会情况本地的配置,这里会调用Configurator接口来实现该功能。
override参数示例:
overrie://10.20.155.1/com.test.DemoServer?category=configurators&dynamic=false&enabled=true&application=test&timeout=1000
overrie:// override协议
10.20.155.1 如果是0.0.0.0,则表示对所有服务生效。具体IP则表示只覆盖具体IP的。必填
com.test.DemoServer 表示只对指定的服务生效,必填
category=configurators 表示这个参数是动态配置的,必填
dynamic=false 是否持久化,false为持久化数据。注册方退出,数据依然会保留在注册中心,必填
enabled=true 覆盖规则是否生效,默认生效,可以不填
application=test 针对某个应用生效,不填则对所有应用生效,可以不填
timeout=1000 表示将满足前面的条件的timeout参数覆盖为1000
2. doList的实现:
notify中更新的Invoker列表最终会转化为一个字典Map<String, List<Invoker<T>>> mthodInvokerMap
。key是对应的方法名称,value是整个Invoker列表。doList的最终目的就是在字典里匹配出可以调用的Invoekr列表,并返回给上层。其主要步骤如下:
- 检查服务是否被禁用。如果配置中心禁用了某个服务,则该服务无法被调用。如果服务被禁用则会抛出异常。
- 根据方法名和首参数匹配Invoekr。这是一个比较奇怪的特性。根据方法名和首参数查找对应的Invoker列表,暂时没看到相关的应用场景。
//首参数匹配Invoker使用示例
void test(String arg); //假设有test方法
test("123); //调用
test.123 //用test.123在methodInvokerMap中匹配对应的Invoker列表
如果在这一步没有匹配到Invoker列表,则进入第三步。
- 根据方法名匹配Invoker。以方法名为key去methodInvokerMap中匹配Invoker列表,如果还是没有匹配到,则进入第四步。
- 根据
"*"
匹配Invoker。用星号去匹配Invoker列表,如果还没有匹配到,则进入最后一步兜底操作。 - 遍历methodInvokerMap,找到第一个Invoker列表返回。如果还没有,则返回一个空列表。