Dubbo——Directory的实现原理

Directory的实现

整个容错过程中首先会使用Directory#list来获取所有的Invoker列表。Directory也有多种实现子类,既可以提供静态的Invoker列表,也可以提供动态的Invoker列表。静态的列表是用户自己设置的Invoker列表;动态列表根据注册中心的数据动态变化,动态更新Invoker列表的数据,整个过程对上层透明。

总体实现

在这里插入图片描述

Directory是顶层接口。AbstractDirectory封装了通用的实现逻辑。抽象类包含RegistryDirectory和StaticDirectory两个子类。

AbstractDirectory:
封装了通用逻辑,主要实现了四个方法:检测Invoker是否可用,销毁所有Invoker,list方法,还留了一个抽象的doList方法给子类自行实现。list方法是最主要的方法,用于放回所有可用的list,逻辑分为两步:

  1. 调用抽象方法doList获取所有Invoker列表,不同子类有不同的实现;
  2. 遍历所有的router,进行Invoker过滤,最后返回过滤好的Invoker列表。

doList抽象方法则是返回所有的Invoker列表,由于是抽象方法,子类继承后必须要有自己的实现。

RegistryDirectory:
属于Directory的动态列表实现,会自动从注册中心更新Invoker列表、配置信息、路由列表。

StaticDirectory:
Directory的静态列表实现,即将出阿奴的Invoker列表封装成静态的Directory对象,里面的列表不会改变。因为Cluster#join(Directory<T> directory)方法需要传入Directory对象,因此该实现主要使用一些上层已经知道自己要调用哪些Invoker,只需要包装一个Direcotry对象返回即可的场景。在ReferenceConfig#createProxyRegistryDirectory#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中
  • 解析并更新配置参数:
    1. 对于router类型的参数,首先遍历所有router类型的URL,然后通过Router工厂把每个URL包装成路由规则,最后更新本地的路由信息。这个过程会忽略以empty开始的URL。
    2. 对于Configurator类的参数,管理员可以在dubbo-admin动态配置功能上修改生产者的参数,这些参数会保存在配置中心的configurators类目下。notify监听到URL配置参数的变化,会解析并更新本地的Configurator配置。
    3. 对于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列表,并返回给上层。其主要步骤如下:

  1. 检查服务是否被禁用。如果配置中心禁用了某个服务,则该服务无法被调用。如果服务被禁用则会抛出异常。
  2. 根据方法名和首参数匹配Invoekr。这是一个比较奇怪的特性。根据方法名和首参数查找对应的Invoker列表,暂时没看到相关的应用场景。
//首参数匹配Invoker使用示例
void test(String arg); //假设有test方法
test("123); //调用
test.123  //用test.123在methodInvokerMap中匹配对应的Invoker列表

如果在这一步没有匹配到Invoker列表,则进入第三步。

  1. 根据方法名匹配Invoker。以方法名为key去methodInvokerMap中匹配Invoker列表,如果还是没有匹配到,则进入第四步。
  2. 根据"*"匹配Invoker。用星号去匹配Invoker列表,如果还没有匹配到,则进入最后一步兜底操作。
  3. 遍历methodInvokerMap,找到第一个Invoker列表返回。如果还没有,则返回一个空列表。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章