• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

三、Dubbo注册心二---源码篇

武飞扬头像
焦虑的二狗
帮助1

Dubbo注册

zookeeper注册节点结构图

2.7.5版本前只有dubbo节点,2.7.5且之后 新增了services节点,并且之后版本会逐渐将dubbo剔除掉
这样的优势是将dubbo的结构下随着一个节点注册接口的增多,需要维护更多的节点变更,services结构是按照节点进行接口维护,减轻了zookeeper的节点压力,将计算压力放在了服务器上进行计算。
学新通

RegistryProtocol.export源码讲解

  • org.apache.dubbo.registry.integration.RegistryProtocol#export
    @Override
    //第一次:
    //originInvoker -> DelegateProviderMetaDataInvoker.class
    //invoker -> eg: interface com.jiangzheng.course.dubbo.api.service.ServiceDemo -> service-discovery-registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?REGISTRY_CLUSTER=org.apache.dubbo.config.RegistryConfig&application=provider&client=curator&dubbo=2.0.2&pid=83877&registry=zookeeper&release=3.0.0.preview&timestamp=1640763327339
    //metadata -> eg: <dubbo:service beanName="serviceDemo" />
    //第二次
    //invoker -> eg: interface com.jiangzheng.course.dubbo.api.service.ServiceDemo -> registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=provider&client=curator&dubbo=2.0.2&pid=8093&registry=zookeeper&release=3.0.4&timestamp=1640861452691
    public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
        //获取注册中心地址
        //eg1: service-discovery-registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?REGISTRY_CLUSTER=org.apache.dubbo.config.RegistryConfig&application=provider&client=curator&dubbo=2.0.2&pid=82721&registry=zookeeper&release=3.0.0.preview&timestamp=1640760597729
        //eg2: zookeeper://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?REGISTRY_CLUSTER=org.apache.dubbo.config.RegistryConfig&application=provider&client=curator&dubbo=2.0.2&pid=83877&release=3.0.0.preview&timestamp=1640763327339
        URL registryUrl = getRegistryUrl(originInvoker);
        // 获取服务要注册的接口协议信息
        //eg: dubbo://30.96.216.200:20880/com.jiangzheng.course.dubbo.api.service.ServiceDemo?anyhost=true&application=provider&bind.ip=30.96.216.200&bind.port=29014&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=com.jiangzheng.course.dubbo.api.service.ServiceDemo&metadata-type=remote&methods=getName,getSelf&pid=82721&release=3.0.0.preview&side=provider&timestamp=1640760597740
        URL providerUrl = getProviderUrl(originInvoker);

        //将dubbo协议的providerUrl转为provider协议的overrideSubscribeUrl
        //eg: provider://30.96.216.200:20880/com.jiangzheng.course.dubbo.api.service.ServiceDemo?anyhost=true&application=provider&bind.ip=30.96.216.200&bind.port=29014&category=configurators&check=false&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=com.jiangzheng.course.dubbo.api.service.ServiceDemo&metadata-type=remote&methods=getName,getSelf&pid=83877&release=3.0.0.preview&side=provider&timestamp=1640763327347
        final URL overrideSubscribeUrl = getSubscribedOverrideUrl(providerUrl);
        final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker);
        Map<URL, NotifyListener> overrideListeners = getProviderConfigurationListener(providerUrl).getOverrideListeners();
        overrideListeners.put(registryUrl, overrideSubscribeListener);
		//对providerUrl没有做处理,更像是一个预留
        //eg: dubbo://30.96.216.200:20880/com.jiangzheng.course.dubbo.api.service.ServiceDemo?anyhost=true&application=provider&bind.ip=30.96.216.200&bind.port=29014&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=com.jiangzheng.course.dubbo.api.service.ServiceDemo&metadata-type=remote&methods=getName,getSelf&pid=82721&release=3.0.0.preview&side=provider&timestamp=1640760597740
        providerUrl = overrideUrlWithConfig(providerUrl, overrideSubscribeListener);
        //暴露一个本地服务 Netty -> consumer
        final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker, providerUrl);

        // 获取到一个具体的Registry(服务发现注册的)客户端
        final Registry registry = getRegistry(registryUrl);
        //做provider url的转化
        //eg: dubbo://30.96.216.200:29014/com.jiangzheng.course.dubbo.api.service.ServiceDemo?anyhost=true&application=provider&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=com.jiangzheng.course.dubbo.api.service.ServiceDemo&metadata-type=remote&methods=getName,getSelf&pid=83877&release=3.0.0.preview&side=provider&timestamp=1640763327347
        final URL registeredProviderUrl = getUrlToRegistry(providerUrl, registryUrl);

        // 是否延迟发布
        boolean register = providerUrl.getParameter(REGISTER_KEY, true);
        if (register) {
            //服务提供者开始注册节点
            register(registry, registeredProviderUrl);
        }

        // 修改状态
        registerStatedUrl(registryUrl, registeredProviderUrl, register);

		//将ProviderUrl和SubscribeUrl在本地做一个暴露
        exporter.setRegisterUrl(registeredProviderUrl);
        exporter.setSubscribeUrl(overrideSubscribeUrl);

        if (!registry.isServiceDiscovery()) {
            // 2.6.x版本前 用来做通知
            registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);
        }
		//空的方法实现,用户可通过SPI自定义实现
        notifyExport(exporter);
        //返回一个exporter
        return new DestroyableExporter<>(exporter);
    }
学新通
  • 核心代码
    • final ExporterChangeableWrapper exporter = doLocalExport(originInvoker, providerUrl); //本地服务JMI的暴露
    • final Registry registry = getRegistry(registryUrl); //获取注册服务
    • register(registry, registeredProviderUrl); //进行注册

RegistryProtocol.register源码讲解

  • org.apache.dubbo.registry.integration.RegistryProtocol#register

在讲解前补充下上一方法export的相关内容,export会被调用两回,一回是在zookeeper下注册services节点,一回是注册dubbo节点,具体该注册哪一个节点呢,这个和registryUrl的内容有关。在示例中也有标识eg1、eg2,二者的协议不同,eg1的url是service-discovery-registry协议,eg2的url是zookeeper协议。

		//org.apache.dubbo.registry.integration.RegistryProtocol#export中调用
		//url是service-discovery-registry协议的返回registry -> ServiceDiscoveryRegistry(ZookeeperServiceDiscovery)  可查看图1了解具体结构
		//url是zookeeper协议的返回registry -> ZookeeperRegistry  可查看图2了解具体结构
		final Registry registry = getRegistry(registryUrl);
		
        if (register) {
            register(registry, registeredProviderUrl);
        }
	//org.apache.dubbo.registry.integration.RegistryProtocol#register
    private void register(Registry registry, URL registeredProviderUrl) {
        registry.register(registeredProviderUrl);
    }
	//org.apache.dubbo.registry.ListenerRegistryWrapper#register
	@Override
    public void register(URL url) {
        try {
        	//url是service-discovery-registry协议的 走ServiceDiscoveryRegistry,此处不会立即触发注册,具体下面有解释
        	//url是zookeeper协议的 走ZookeeperRegistry
            registry.register(url);
        } finally {
            if (CollectionUtils.isNotEmpty(listeners) && !UrlUtils.isConsumer(url)) {
                RuntimeException exception = null;
                for (RegistryServiceListener listener : listeners) {
                    if (listener != null) {
                        try {
                            listener.onRegister(url, registry);
                        } catch (RuntimeException t) {
                            logger.error(t.getMessage(), t);
                            exception = t;
                        }
                    }
                }
                if (exception != null) {
                    throw exception;
                }
            }
        }
    }
学新通

学新通
学新通

  • 看ServiceDiscoveryRegistry的调用 — zookeeper中注册services节点
    @Override
    public final void register(URL url) {
        if (!shouldRegister(url)) { // Should Not Register
            return;
        }
        doRegister(url);
    }

    @Override
    public void doRegister(URL url) {
        url = addRegistryClusterKey(url);
        if (writableMetadataService.exportURL(url)) {
            if (logger.isInfoEnabled()) {
                logger.info(format("The URL[%s] registered successfully.", url.toString()));
            }
        } else {
            if (logger.isWarnEnabled()) {
                logger.warn(format("The URL[%s] has been registered.", url.toString()));
            }
        }
    }
学新通

貌似没有做对应的节点注册,其实注册并没有放在此处来完成,这里有必要解释下,Dubbo的启动是由SpringBoot的事件监听来驱动的,而此处的RegistryProtocol#export方法调用是由方法exportServices()调用完成,下一步会调用registerServiceInstance()方法, 关于zk上/services节点注册就是放在registerServiceInstance()中做的,具体可查看如下方法调用流程,具体代码省略了,只列出了方法调用。(不同版本实现类有所不同,可通过方法名进行定位所在类)

3.0.0.preview版本调用链路

  1. org.springframework.context.support.AbstractApplicationContext#refresh

  2. org.springframework.context.support.AbstractApplicationContext#finishRefresh

  3. org.springframework.context.support.AbstractApplicationContext#publishEvent(org.springframework.context.ApplicationEvent)

  4. org.springframework.context.support.AbstractApplicationContext#publishEvent(java.lang.Object, org.springframework.core.ResolvableType)

  5. org.springframework.context.event.SimpleApplicationEventMulticaster#multicastEvent(org.springframework.context.ApplicationEvent, org.springframework.core.ResolvableType)

  6. org.springframework.context.event.SimpleApplicationEventMulticaster#invokeListener

  7. org.springframework.context.event.SimpleApplicationEventMulticaster#doInvokeListener

  8. org.apache.dubbo.config.spring.context.OneTimeExecutionApplicationContextEventListener#onApplicationEvent

  9. org.apache.dubbo.config.spring.context.DubboBootstrapApplicationListener#onApplicationContextEvent

  10. org.apache.dubbo.config.spring.context.DubboBootstrapApplicationListener#onContextRefreshedEvent

  11. org.apache.dubbo.config.bootstrap.DubboBootstrap#start

  12. org.apache.dubbo.config.bootstrap.DubboBootstrap#registerServiceInstance

  13. org.apache.dubbo.config.bootstrap.DubboBootstrap#doRegisterServiceInstance

  14. org.apache.dubbo.registry.client.EventPublishingServiceDiscovery#register

  15. org.apache.dubbo.registry.client.EventPublishingServiceDiscovery#executeWithEvents

  16. org.apache.dubbo.registry.zookeeper.ZookeeperServiceDiscovery#register

3.0.4版本调用链路

  1. org.springframework.context.support.AbstractApplicationContext#refresh
  2. org.springframework.context.support.AbstractApplicationContext#finishRefresh
  3. org.springframework.context.support.AbstractApplicationContext#publishEvent(org.springframework.context.ApplicationEvent)
  4. org.springframework.context.support.AbstractApplicationContext#publishEvent(java.lang.Object, org.springframework.core.ResolvableType)
  5. org.springframework.context.event.SimpleApplicationEventMulticaster#multicastEvent(org.springframework.context.ApplicationEvent, org.springframework.core.ResolvableType)
  6. org.springframework.context.event.SimpleApplicationEventMulticaster#invokeListener
  7. org.springframework.context.event.SimpleApplicationEventMulticaster#doInvokeListener
  8. org.apache.dubbo.config.spring.context.DubboDeployApplicationListener#onApplicationEvent
  9. org.apache.dubbo.config.spring.context.DubboDeployApplicationListener#onContextRefreshedEvent
  10. org.apache.dubbo.config.deploy.DefaultModuleDeployer#start
  11. org.apache.dubbo.config.deploy.DefaultApplicationDeployer#prepareApplicationInstance
  12. org.apache.dubbo.config.deploy.DefaultApplicationDeployer#registerServiceInstance
  13. org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils#registerMetadataAndInstance
  14. org.apache.dubbo.registry.client.AbstractServiceDiscovery#register
  15. org.apache.dubbo.registry.zookeeper.ZookeeperServiceDiscovery#doRegister

在ZookeeperServiceDiscovery的doRegister或register(版本不同)中有调用build(serviceInstance)方法,build方法构建了节点中保存的json内容,并返回一个org.apache.curator.x.discovery.ServiceInstance<ZookeeperInstance>实例

  • 看ZookeeperRegistry的调用 — zookeeper中注册dubbo节点
  1. org.apache.dubbo.registry.ListenerRegistryWrapper#register
  2. org.apache.dubbo.registry.support.FailbackRegistry#register
  3. org.apache.dubbo.registry.zookeeper.ZookeeperRegistry#doRegister
    @Override
    public void doRegister(URL url) {
        try {
            checkDestroyed();
            //调用zk客户端进行创建节点
            zkClient.create(toUrlPath(url), url.getParameter(DYNAMIC_KEY, true));
        } catch (Throwable e) {
            throw new RpcException("Failed to register "   url   " to zookeeper "   getUrl()   ", cause: "   e.getMessage(), e);
        }
    }
    
    private String toUrlPath(URL url) {
        //此处会对节点名称进行encode
        return toCategoryPath(url)   PATH_SEPARATOR   URL.encode(url.toFullString());
    }
    //此处采用递归的方式进行节点创建
    @Override
    public void create(String path, boolean ephemeral) {
        if (!ephemeral) {
            if (persistentExistNodePath.contains(path)) {
                return;
            }
            if (checkExists(path)) {
                persistentExistNodePath.add(path);
                return;
            }
        }
        //获取最后一个“/”的位置
        int i = path.lastIndexOf('/');
        if (i > 0) {
            //如果path中还存在“/”字符,则继续递归调用该方法
            create(path.substring(0, i), false);
        }
        //如果不存在“/”字符了,则表示 当前获取到了根路径名称,递归创建之后的路径即可
        if (ephemeral) {
            createEphemeral(path);
        } else {
            createPersistent(path);
            persistentExistNodePath.add(path);
        }
    }
学新通

该递归创建zk节点的编程方式 还是值得我们学习一下。

值得一提的是dubbo的SPI,对于程序中具体使用实体类的指定都是依赖于SPI
学新通
例如:/org/apache/dubbo/dubbo/3.0.4/dubbo-3.0.4.jar!/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscovery的文件内容如下

file=org.apache.dubbo.registry.client.FileSystemServiceDiscovery
multicast=org.apache.dubbo.registry.multicast.MulticastServiceDiscovery
zookeeper=org.apache.dubbo.registry.zookeeper.ZookeeperServiceDiscovery
nacos=org.apache.dubbo.registry.nacos.NacosServiceDiscovery
multiple=org.apache.dubbo.registry.multiple.MultipleServiceDiscovery
kubernetes=org.apache.dubbo.registry.kubernetes.KubernetesServiceDiscovery
dns=org.apache.dubbo.registry.dns.DNSServiceDiscovery
xds=org.apache.dubbo.registry.xds.XdsServiceDiscovery


/org/apache/dubbo/dubbo/3.0.4/dubbo-3.0.4.jar!/META-INF/dubbo/internal/org.apache.dubbo.remoting.zookeeper.ZookeeperTransporter

curator=org.apache.dubbo.remoting.zookeeper.curator.CuratorZookeeperTransporter

curator5=org.apache.dubbo.remoting.zookeeper.curator5.Curator5ZookeeperTransporter


里面定义了对应的实现

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgcbjjc
系列文章
更多 icon
同类精品
更多 icon
继续加载