Dubbo注册Provider

一、与Spring的结合

首先spring注册实例让IOC容器管理实例的方式有注解方式,xml的方式。Dubbo中使用另一种方式BeanDefinitionRegistryPostProcessor提供了让我们对BeanDefinition进行自定义注册的方法,如果Spring中的默认配置方式不能满足你的要求,就可以通过实现BeanDefinitionRegistryPostProcessor接口来进行扩展(http://rason.me/2016/12/16/BeanDefinitionRegistryPostProcessor/)

它是以ServiceBean为根对象的方式来注册的,那么实例化的过程必将触发ServiceBean的初始化的方法。

然后再来看看ServiceBean,它是继承了ServiceConfig,所以从现在开始就离开与Spring结合的地方,进入Dubbo了。

这里实现了ApplicationListener,如果在上下文中部署一个实现了ApplicationListener接口的bean,那么每当在一个ApplicationEvent发布到ApplicationContext时,这个bean得到通知。其实这就是标准的Oberver设计模式。(https://www.cnblogs.com/duanxz/p/3772654.html)所以这里其实就是类似于初始化的方法。当触发ApplicationEvent的事件时,这个实现的方法就会被调用。

public class ServiceBean extends ServiceConfig implements InitializingBean, DisposableBean, ApplicationContextAware, ApplicationListener, BeanNameAware {

   private static final long serialVersionUID = 213195494150089726L;

   private final transient Service service;

   private transient ApplicationContext applicationContext;

   private transient String beanName;

   private transient boolean supportedApplicationListener;

   public ServiceBean() {
       super();
       this.service = null;
   }

   public ServiceBean(Service service) {
       super(service);
       this.service = service;
   }
}
  @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        if (isDelay() && !isExported() && !isUnexported()) {
            if (logger.isInfoEnabled()) {
                logger.info("The service ready on spring started. service: " + getInterface());
            }
            export();
        }
    }

还在ServiceBean中看看这个实现方法做了什么,export()。这方法来至父类ServiceConfig中。这就是Dubbo严格意义上的入口。前面都是在利用Spring在实例化对象,收集配置。

二、注册Service

进入ServiceConfig的export()方法。

顺着方法一直走到上面这个方法,查看最底,找到注册registryUrls的代码。

            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.addParameterAndEncoded(Constants.MONITOR_KEY, monitorUrl.toFullString());
                       }
                       if (logger.isInfoEnabled()) {
                           logger.info("Register dubbo service " + interfaceClass.getName() + " url " + url + " to registry " + registryURL);
                       }

                       // For providers, this is used to enable custom proxy to generate invoker
                       String proxy = url.getParameter(Constants.PROXY_KEY);
                       if (StringUtils.isNotEmpty(proxy)) {
                           registryURL = registryURL.addParameter(Constants.PROXY_KEY, proxy);
                       }
                         //包装Invoker
                       Invoker invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));
                       DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
                       //导出服务,注册服务到注册中心
                       Exporter exporter = protocol.export(wrapperInvoker);
                       exporters.add(exporter);
                   }
               } else {
                   //包装Invoker
                   Invoker invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url);
                   DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
                   //导出服务,注册服务到注册中心
                   Exporter exporter = protocol.export(wrapperInvoker);
                   exporters.add(exporter);
               }

此段代码就是在使用Invoker包装Service的一些信息,比如接口,注册的url.这个url包含了注册中心的url及dubbo加入的一些自定义的参数。所以并不是简单只有注册中心的地址。至于发现protocol与proxyFactory,是怎么来的这是dubbo的扩展工厂类ExtensionLoader获取的,它会根据配置文件和注解获取最适合的实例返回。这样就可以实现SPI详情移步(https://blog.csdn.net/xiaoxufox/article/details/75117992)然后进入proctcol的export方法看看。

那扩展工厂类会返回RegistryProtocol,来看看它的export方法。

@Override
   public  Exporter export(final Invoker originInvoker) throws RpcException {
       //export invoker
       final ExporterChangeableWrapper exporter = doLocalExport(originInvoker);

       URL registryUrl = getRegistryUrl(originInvoker);

       //registry provider
       final Registry registry = getRegistry(originInvoker);
       final URL registeredProviderUrl = getRegisteredProviderUrl(originInvoker);

       //to judge to delay publish whether or not
       boolean register = registeredProviderUrl.getParameter("register", true);

       ProviderConsumerRegTable.registerProvider(originInvoker, registryUrl, registeredProviderUrl);

       if (register) {
         //注册服务
           register(registryUrl, registeredProviderUrl);
           ProviderConsumerRegTable.getProviderWrapper(originInvoker).setReg(true);
       }

       // Subscribe the override data
       // FIXME When the provider subscribes, it will affect the scene : a certain JVM exposes the service and call the same service. Because the subscribed is cached key with the name of the service, it causes the subscription information to cover.
       final URL overrideSubscribeUrl = getSubscribedOverrideUrl(registeredProviderUrl);
       final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker);
       overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);
       registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);
       //Ensure that a new exporter instance is returned every time export
       return new DestroyableExporter(exporter, originInvoker, overrideSubscribeUrl, registeredProviderUrl);
   }

这里从前面传递的Invoker中获取注册的url,并且订阅符合要求的consumer。在register方法中获取实际的注册类,根据你的配置选择需要的注册类。

这里如果你注册中心的配置是zookeeper,那么ExtensionLoader返回的Registry就是ZookeeperRegistry。看看ZookeeperRegistry中怎么注册的。
进入ZookeeperRegistrydoRegister的方法,它是被父类的register方法调用的。

@Override
   protected void doRegister(URL url) {
       try {
           zkClient.create(toUrlPath(url), url.getParameter(Constants.DYNAMIC_KEY, true));
       } catch (Throwable e) {
           throw new RpcException("Failed to register " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
       }
   }

其实dubbo拼接url然后一步一步传递下去,这些参数是做啥的也蛮重要的对理解这个过程。Dubbo是URL总线模式,其中的很多可变化的地方就是通过url的值变化来处理的

再来看看注册的接口

 /**
     * Register data, such as : provider service, consumer address, route rule, override rule and other data.
     * 

* Registering is required to support the contract:
* 1. When the URL sets the check=false parameter. When the registration fails, the exception is not thrown and retried in the background. Otherwise, the exception will be thrown.
* 2. When URL sets the dynamic=false parameter, it needs to be stored persistently, otherwise, it should be deleted automatically when the registrant has an abnormal exit.
* 3. When the URL sets category=routers, it means classified storage, the default category is providers, and the data can be notified by the classified section.
* 4. When the registry is restarted, network jitter, data can not be lost, including automatically deleting data from the broken line.
* 5. Allow URLs which have the same URL but different parameters to coexist,they can't cover each other.
* * @param url Registration information , is not allowed to be empty, e.g: dubbo://10.20.153.10/org.apache.dubbo.foo.BarService?version=1.0.0&application=kylin */ void register(URL url); /** * Subscribe to eligible registered data and automatically push when the registered data is changed. *

* Subscribing need to support contracts:
* 1. When the URL sets the check=false parameter. When the registration fails, the exception is not thrown and retried in the background.
* 2. When URL sets category=routers, it only notifies the specified classification data. Multiple classifications are separated by commas, and allows asterisk to match, which indicates that all categorical data are subscribed.
* 3. Allow interface, group, version, and classifier as a conditional query, e.g.: interface=org.apache.dubbo.foo.BarService&version=1.0.0
* 4. And the query conditions allow the asterisk to be matched, subscribe to all versions of all the packets of all interfaces, e.g. :interface=*&group=*&version=*&classifier=*
* 5. When the registry is restarted and network jitter, it is necessary to automatically restore the subscription request.
* 6. Allow URLs which have the same URL but different parameters to coexist,they can't cover each other.
* 7. The subscription process must be blocked, when the first notice is finished and then returned.
* * @param url Subscription condition, not allowed to be empty, e.g. consumer://10.20.153.10/org.apache.dubbo.foo.BarService?version=1.0.0&application=kylin * @param listener A listener of the change event, not allowed to be empty */

上图中给出了很多参数的注解和注册的url的模板。

你可能感兴趣的:(Dubbo注册Provider)