【Dubbo】--服务注册与发布原理分析

本文详细分析了Dubbo服务注册与发布的过程,包括配置解析、服务导出到本地和远程、服务注册到注册中心(如Zookeeper)的步骤。解析配置涉及Spring扩展和接口检查,服务导出涉及Invoker创建、NettyServer启动,注册中心创建涉及ZookeeperTransporter的自适应扩展。
摘要由CSDN通过智能技术生成


Dubbo 服务导出过程始于 Spring 容器发布刷新事件,Dubbo 在接收到事件后,会立即执行服务导出逻辑。
整个逻辑大致可分为三个部分:
第一部分是前置工作,主要用于检查配置参数,组装 URL。
第二部分是导出服务,包含导出服务到本地 (JVM),和导出服务到远程两个过程。
第三部分是向注册中心注册服务,用于服务发现;

实现的效果:

  1. Main()方法启动后:
  2. 开发了20880端口(默认端口)
  3. 发布了一个可远端调用的地址:dubbo:// 192.168.1.1:20880/interface?…… (发布到了注册中心)

------------------
这个过程都需要做哪些事情:

  • 解析配置文件–>基于spring标签进行的扩展
  • 暴露本地服务/远程服务
  • 启动netty --> 提供netty服务 暴露端口
  • 打开连接zk
  • 把地址注册到zk
  • 监听zk

1、解析配置

spring里面提供了很多扩展
dubbo-config-spring模块里面提供了配置文件的扩展
spring提供了两个可扩展的接口或类:
image.png
根据SPI扩展机制进入DubboNamespaceHandler解析配置文件,进入到serviceBean 和ServiceConfig中,开始进行导出服务的前置工作;通过onApplicationEvent 监听事件,检查服务是否发布,调用export导出服务,一下是对export的分析:

1.1 检查配置

  • 检查的interface属性是否合法
  • 检查providerConfig和ApplicationConfig等核心配置类对象是否为空,为空会从其他配置类获取对应的实例;
  • 检查并处理泛化服务和普通服务类
  • 检查本地存根配置
  • 对ApplicationConfig、RegisterConfig等配置类进行检测,为空则尝试创建,无法创建则抛异常;

1.2 多协议多注册中心导出服务

private void doExportUrls() {
   
    // 加载注册中心链接
    List<URL> registryURLs = loadRegistries(true);
    // 遍历 protocols,并在每个协议下导出服务
    for (ProtocolConfig protocolConfig : protocols) {
   
        doExportUrlsFor1Protocol(protocolConfig, registryURLs);
    }
}

1.3 组装URL

走到这一步url大概是这个样子:_ registry://192.168.22.130:2181/org.apache.dubbo.registry.RegistryService/……_
这一步是根据配置信息组装url, 而且url驱动流程的执行,会再各个模块之间一直往下传,后续会不断修改URL头或协议地址,并根据url地址中的信息做相应的处理;
doExportUrlsFor1Protocol方法中做了url的组装处理,通过反射的方式获取到版本、时间戳、方法名以及各种配置对象的字段信息,然后放入map,源码太长了,这块只贴一下伪代码帮助理解:

private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
   
    // 获取 ArgumentConfig 列表
    for (遍历 ArgumentConfig 列表) {
   
        if (type 不为 null,也不为空串) {
       // 分支1
            1. 通过反射获取 interfaceClass 的方法列表
            for (遍历方法列表) {
   
                1. 比对方法名,查找目标方法
                2. 通过反射获取目标方法的参数类型数组 argtypes
                if (index != -1) {
       // 分支2
                    1. 从 argtypes 数组中获取下标 index 处的元素 argType
                    2. 检测 argType 的名称与 ArgumentConfig 中的 type 属性是否一致
                    3. 添加 ArgumentConfig 字段信息到 map 中,或抛出异常
                } else {
       // 分支3
                    1. 遍历参数类型数组 argtypes,查找 argument.type 类型的参数
                    2. 添加 ArgumentConfig 字段信息到 map 中
                }
            }
        } else if (index != -1) {
       // 分支4
            1. 添加 ArgumentConfig 字段信息到 map 中
        }
    }
}

2. 导出Dubbo服务

准备工作做完了,接下来进行服务导出。服务导出分为导出到本地JVM和导出到远程。
代码根据 url 中的 scope 参数决定服务导出方式,分别如下:

  • scope = none,不导出服务
  • scope != remote,导出到本地
  • scope != local,导出到远程

服务发布的本质就是把export的每个服务转为一个对应的Invoker可执行体,然后把转换后的Invoker都放到一个exporterMap(key,invoker)集和中;

private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
   
    
    // 省略无关代码
    
    if (ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
            .hasExtension(url.getProtocol())) {
   
        // 加载 ConfiguratorFactory,并生成 Configurator 实例,然后通过实例配置 url
        url = ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
                .getExtension(url.getProtocol()).getConfigurator(url).configure(url);
    }

    String scope = url.getParameter(Constants.SCOPE_KEY);
    // 如果 scope = none,则什么都不做
    if (!Constants.SCOPE_NONE.toString().equalsIgnoreCase(scope)) {
   
        // scope != remote,导出到本地
        if (!Constants.SCOPE_REMOTE.toString().equalsIgnoreCase(scope)) {
   
            exportLocal(url);
        }

        // scope != local,导出到远程
        if (!Constants.SCOPE_LOCAL.toString().equalsIgnoreCase(scope)) {
   
            if (registryURLs != null && !registryURLs.isEmpty()) {
   
                for (URL registryURL : registryURLs) {
   
                    url = url.addParameterIfAbsent(Constants.DYNAMIC_KEY, registryURL.getParameter(Constants.DYNAMIC_KEY));
                    // 加载监视器链接
                    URL monitorUrl = loadMonitor(registryURL);
                    if (monitorUrl != null) {
   
                        // 将监视器链接作为参数添加到 url 中
                        url = url.addParameterAndEncoded(Constants.MONITOR_KEY, monitorUrl.toFullString());
                    }

                    String proxy = url.getParameter(Constants.PROXY_KEY);
                    if (StringUtils.isNotEmpty(proxy)) {
   
                        registryURL = registryURL.addParameter(Constants.PROXY_KEY, proxy);
                    }

                    // 为服务提供类(ref)生成 Invoker
                    Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));
                    // DelegateProviderMetaDataInvoker 用于持有 Invoker 和 ServiceConfig
                    DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);

                    // 导出服务,并生成 Exporter
                    Exporter<?> exporter = protocol.export(wrapperInvoker);
                    exporters.add(exporter);
                }
                
            // 不存在注册中心,仅导出服务
            } else {
   
                Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url);
                DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);

                Exporter<?> exporter = protocol.export(wrapperInvoker);
                exporters.add(exporter);
            }
        }
    }
    this.urls.add(url);
}

不管是导出到本地,还是远程。进行服务导出之前,均需要先创建 Invoker,这是一个很重要的步骤。因此下面先来分析 Invoker 的创建过程。

2.1 Invoker创建过程

Invoker是一个重要的模型,在服务的提供端和调用端都会出现invoker.

Invoker 是实体域,它是 Dubbo 的核心模型,其它模型都向它靠扰,或转换成它,它代表一个可执行体,可向它发起 invoke 调用,它有可能是一个本地的实现,也可能是一个远程的实现,也可能一个集群实现。

Invoker由ProxyFactory创建而来,Dubbo默认的ProxyFactory实现类是javassistProFactory。来看一下javassistProFactory类创建Invoker的过程

public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
   
	// 为目标类创建 Wrapper
    final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值