Dubbo-Directory目录源码解析

还是先从整体架构入手,先看下Dubbo官方的架构设计图,调用方从集群中选择一个提供者时,首先得调用Directory获取Invoker列表,再根据配置的路由规则过滤掉不符合的提供者
在这里插入图片描述

Directory 的用途是保存 Invoker,可简单类比为 List。其实现类 RegistryDirectory 是一个动态服务目录,可感知注册中心配置的变化,它所持有的 Invoker 列表会随着注册中心内容的变化而变化。每次变化后,RegistryDirectory 会动态增删 Invoker,并调用 Router 的 route 方法进行路由,过滤掉不符合路由规则的 Invoker

官网的介绍中明确到,Directory中的Invoker列表会随着注册中心的内容的变化而变化,下面就从源码入手,分析Directory中的Invoker列表是如何变化的

Directory接口

先看下接口定义

public interface Directory<T> extends Node {

    //返回接口Class
    Class<T> getInterface();

    //获取提供者对应的Invoker列表
    List<Invoker<T>> list(Invocation invocation) throws RpcException;

}

有几种实现呢?
在这里插入图片描述

可以看到底层有RegistryDirectoryStaticDirectory两种实现

  • RegistryDirectory实现了NotifyListener接口,而NotifyListener接口的notify回调方法会在注册中心中提供者发生变化时被调用,从而达到通知RegistryDirectory更新Invoker列表的效果,它是能根据注册中心动态变化的核心所在
  • StaticDirectory没有实现NotifyListener接口,因此其中Invoker列表不会改变,所以呢,要明白:Directory中的Invoker列表会随着注册中心的内容的变化而变化这句话在这里不适用。

既然我们分析的主要目标是Directory中Invoker的动态变化过程,自然需要分析RegistryDirectory的实现

AbstractDirectory

看Dubbo源码如果看到Abstract开头的抽象类,就应该联想到模板方法,什么意思?看下
AbstractDirectory的实现,其list方法做了一些公共的实现逻辑,再把具体的获取Invoker列表的方法通过调用doList交由子类实现,如果子类有很多实现,均只需按doList的抽象方法模板定制化自己的实现

public abstract class AbstractDirectory<T> implements Directory<T> {
	...忽略其他代码
	@Override
    public List<Invoker<T>> list(Invocation invocation) throws RpcException {
        if (destroyed) {
            throw new RpcException("Directory already destroyed .url: " + getUrl());
        }

        return doList(invocation);
    }
    //抽象方法 由子类实现
	protected abstract List<Invoker<T>> doList(Invocation invocation) throws RpcException;
}

抽象类中做公共实现,子类做特色实现,这是抽象类、抽象方法的基本思路

RegistryDirectory#doList

再跟到子类RegistryDirectory#doList方法中看下Invoker列表是如何获取的
在这里插入图片描述
可以看到最终调用的是routerChain.route来获取符合规则的Invoker列表并返回的,RouterChain就是文章开头提到的Route路由匹配规则,和架构图中调用Router过滤Invoker相吻合

看下其中route方法

public class RouterChain<T> {
	//提供者列表
    private List<Invoker<T>> invokers = Collections.emptyList();

    //路由列表
    private volatile List<Router> routers = Collections.emptyList();
	
	//遍历每个route,针对每个路由规则,分别对Invoker列表进行过滤
	public List<Invoker<T>> route(URL url, Invocation invocation) {
        List<Invoker<T>> finalInvokers = invokers;
        for (Router router : routers) {
            finalInvokers = router.route(finalInvokers, url, invocation);
        }
        return finalInvokers;
    }
}

看到这里,都是读和过滤Invoker的操作,从RegistryDirectory中获取Invoker列表的过程想必已经清楚了,那么还有最重要的一步,怎么动态更新RegistryDirectory中的Invoker列表呢?

RegistryDirectory#notify

RegistryDirectory既然实现了notify接口,就能在注册中心中内容变化时,监听到对应变化
在这里插入图片描述

接收到Url变化通知时,首先按服务类型做了分组,在注册中心中,有如下几种节点信息
在这里插入图片描述

  • /dubbo/serviceInterface/providers 服务提供者注册信息,包含多个服务者 URL 元数据信息。eg: dubbo://192.168.139.101:20880/com.dubbo.DemoService1?key=value&…
  • /dubbo/serviceInterface/consumers 服务消费者注册信息,包含多个消费者 URL 元数据信息。eg: dubbo://192.168.139.101:8888/com.dubbo.DemoService1?key=value&…
  • /dubbo/serviceInterface/router 路由配置信息,包含消费者路由策略 URL 元数据信息。eg: condition://0.0.0.0/com.dubbo.DemoService1?category=routers&key=value&…
  • /dubbo/serviceInterface/configurators 外部化配置信息,包含服务者动态配置 URL 元数据信息。eg: override://0.0.0.0/com.dubbo.DemoService1?category=configurators&key=value&…

notify方法的最后,针对提供者类型的Url,调用refreshInvoker方法更新Invoker列表

RegistryDirectory#refreshInvoker

refreshInvoker代码如下
在这里插入图片描述

  1. 首先判断URL列表的大小是否为1,并且协议是否为EMPTY_PROTOCOL空协议,为什么这么判断?那肯定是哪里塞了一个空协议进去咯

ZookeeperRegistry#toUrlsWithEmpty中有如下代码实现

private List<URL> toUrlsWithEmpty(URL consumer, String path, List<String> providers) {
        List<URL> urls = toUrlsWithoutEmpty(consumer, providers);
        if (urls == null || urls.isEmpty()) {
            int i = path.lastIndexOf(PATH_SEPARATOR);
            String category = i < 0 ? path : path.substring(i + 1);
            URL empty = URLBuilder.from(consumer)
                    .setProtocol(EMPTY_PROTOCOL)
                    .addParameter(CATEGORY_KEY, category)
                    .build();
            urls.add(empty);
        }
        return urls;
    }

toUrlsWithEmpty()会针对providers为空的情况生成empty://协议的URL对象来通知消费者

  1. 如果URL列表不为空,设置服务状态为可用,更新RouterChain中Invoker列表,这也就是Directory中Invoker列表是动态可变的原因
发布了43 篇原创文章 · 获赞 134 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/hosaos/article/details/103476696