Dubbo源码|二十二、Dubbo服务引入——服务发现

1,117 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第33天,点击查看活动详情

开篇

本文介绍Dubbo服务引入部分的源码分析——服务发现。

Dubbo服务引入(一)

Dubbo服务引入(二)

Dubbo服务引入(三)

服务发现

Dubbo的服务发现是在RegistryProtocol#refer方法中完成的,关于这个方法的工作流程上一篇已经介绍过了,这里不再赘述,本篇从RegistryProtocol#interceptInvoker方法开始。

protected <T> Invoker<T> interceptInvoker(ClusterInvoker<T> invoker, URL url, URL consumerUrl) {
    List<RegistryProtocolListener> listeners = findRegistryProtocolListeners(url);
    if (CollectionUtils.isEmpty(listeners)) {
        return invoker;
    }
    for (RegistryProtocolListener listener : listeners) {
        listener.onRefer(this, invoker, consumerUrl);
    }
    return invoker;
}

该方法首先通过Dubbo SPI获取RegistryProtocolListener接口的实现类,然后遍历其所有的实现类,调用实现类的onRefer方法。RegistryProtocolListener接口的实现类为MigrationRuleListener。在onRefer方法中,处理逻辑也很简单调用MigrationRuleHandler#doMigrate方法。

doMigrate方法为迁移规则执行的,该方法有个参数rawRule,这个参数封装了迁移的配置规则的信息,默认人INIT,这个配置信息会被解析为MigrationRule类型。根据MigrationRule实例的信息,会调用MigrationInvoker#refreshInterfaceInvoker方法。

刷新接口级服务发现Invoker

public synchronized void refreshInterfaceInvoker() {
    clearListener(invoker);
    if (needRefresh(invoker)) {
        invoker = registryProtocol.getInvoker(cluster, registry, type, url);
        if (migrationMultiRegistry) {
            setListener(serviceDiscoveryInvoker, () -> {
                this.setAddressChanged();
            });
        }
    }
}

该方法处理逻辑如下:

  1. 清空invoker中的服务变更监听器,该监听器是存储在服务目录中的。
  2. 判断invoker对象是否为空或者是否被销毁,如果是的话就需要刷新。
  3. 调用getInvoker方法重新获得invoker
  4. 设置服务监听。

获取invoker

调用registryProtocol.getInvoker方法重新获取invoker,还记得registryProtocol这个对象对应的是什么类吗?对应的是InterfaceCompatibleRegistryProtocol,这个类是通过DubboSPI加载进来的。

registry=org.apache.dubbo.registry.integration.InterfaceCompatibleRegistryProtocol
service-discovery-registry=org.apache.dubbo.registry.integration.RegistryProtocol

继续看getInvoker方法,该方法会先初始化一个动态服务目录DynamicDirectory,然后调用doCreateInvoker方法来创建invoker对象。

public <T> ClusterInvoker<T> getInvoker(Cluster cluster, Registry registry, Class<T> type, URL url) {
    DynamicDirectory<T> directory = new RegistryDirectory<>(type, url);
    return doCreateInvoker(directory, cluster, registry, type);
}

创建invoker

protected <T> ClusterInvoker<T> doCreateInvoker(DynamicDirectory<T> directory, Cluster cluster, Registry registry, Class<T> type) {
    directory.setRegistry(registry);
    directory.setProtocol(protocol);
    Map<String, String> parameters = new HashMap<String, String>(directory.getConsumerUrl().getParameters());
    URL urlToRegistry = new URL(CONSUMER_PROTOCOL, parameters.remove(REGISTER_IP_KEY), 0, type.getName(), parameters);
    if (directory.isShouldRegister()) {
        directory.setRegisteredConsumerUrl(urlToRegistry);
        registry.register(directory.getRegisteredConsumerUrl());
    }
    directory.buildRouterChain(urlToRegistry);
    directory.subscribe(toSubscribeUrl(urlToRegistry));

    return (ClusterInvoker<T>) cluster.join(directory);
}

该方法的工作流程如下:

  1. 将注册中心、协议等信息保存到服务目录。
  2. 从服务目录里获取服务url对应的参数,并去除无用的参数,例如register.ip
  3. 生成要注册的url,此时协议为consumer
  4. 判断是否需要注册,如果服务接口不为*并且register值为true,就需要注册。
  5. 将消费者url注册到注册中心。
  6. 根据注册url构造路由链。
  7. 根据注册url订阅服务。
  8. 生成invoker并返回。

构造路由链

public void buildRouterChain(URL url) {
    this.setRouterChain(RouterChain.buildChain(url));
}
public static <T> RouterChain<T> buildChain(URL url) {
    return new RouterChain<>(url);
}
private RouterChain(URL url) {
    List<RouterFactory> extensionFactories = ExtensionLoader.getExtensionLoader(RouterFactory.class)
            .getActivateExtension(url, ROUTER_KEY);
    List<Router> routers = extensionFactories.stream()
            .map(factory -> factory.getRouter(url))
            .collect(Collectors.toList());

    initWithRouters(routers);
}

路由就是我们可以设置一些规则,像网关一样,设置哪些服务提供者可以被哪些消费者调用,也可以对服务的调用进行限制等。

  1. 通过DubboSPI机制获取RouterFactory接口的实现类,其实现类主要有四个:MockRouterFactoryTagRouterFactoryAppRouterFactoryServiceRouterFactory
  2. 遍历RouterFactory实现类,调用getRouter方法创建Router实例;
  3. routers进行排序。

应用路由

AppRouter是在AppRouterFactory类中创建出来的,主要是监听应用级别的配置,例如:dubbo-demo-annotation-consumer.condition-router

服务路由

ServiceRouter是在ServiceRouterFactory类中创建出来的,主要是监听服务级别的配置,路由key的格式格式为:{interfaceName}:[version]:[group],例如:org.apache.dubbo.demo.DemoService:1.0.0:test.condition-router

标签路由

TagRouter是在TagRouterFactory类中创建出来的,主要是监听服务端的配置,例如:dubbo-demo-annotation-provider.tag-router

dubbo-admin中可以对这些路由进行配置,通过这些配置可以达到服务分流、分组或者其他个性化的定制操作。

后记

下一篇将介绍Dubbo服务引入的服务目录的监听配置,对服务监听做详细介绍。