1. Dubbo configuration parsing

1.1 Configuration Resolution

First understand the meaning of elements in a configuration file:

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="Http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
       
    <dubbo:protocol name="dubbo"/>

    <bean id="demoService" class="org.apache.dubbo.demo.provider.DemoServiceImpl"/>
    
<beams/>
Copy the code
  1. XMLNS :dubbo: Declares a namespace prefixed with dubbo, followed by a link to give dubbo a unique name. His value with xsi: schemaLocation specified in the second value dubbo.apache.org/schema/dubb…
Caused by: org.xml.sax.SAXParseException; systemId: http://dubbo.apache.org/schema/dubbo/dubbo.xsd; lineNumber: 6; columnNumber: 68; TargetNamespace.1: Should be namespace'dubbo', but the target namespace of the schema document is'http://dubbo.apache.org/schema/dubbo'.Copy the code

In addition, also need to under the meta-inf/spring. The key corresponding handler, specify when meet this kind of namespace, spring using the specified handler to load the configuration, if inconsistent throws an exception:

Exception in thread "main" org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Unable to locate Spring NamespaceHandler for XML schema namespace [http://dubbo.apache.org/schema/dubbo]
Copy the code
  1. XMLNS: What is the default namespace that all elements without prefixes use by default
  2. Xsi :schemaLocation: This is used with a namespace. The previous dubbo.apache.org/schema/dubb… The location of the schema
  3. For the inside of the elements, such as < dubbo: protocol / > will be used to dubbo corresponding XSD – > dubbo.apache.org/schema/dubb… The element type corresponds to the value specified in the type attribute.

1.1.1 Design Analysis based on Schema

Under Dubo-config-spring, there is a dubo.xsd to constrain tags and corresponding attributes when configured with XML. When Spring parses to a namespace, it goes to the spring.handlers and spring.schemas files to find a corresponding Handler for parsing the corresponding XML configuration information. For example,

http\://dubbo.apache.org/schema/dubbo=org.apache.dubbo.config.spring.schema.DubboNamespaceHandler
http\://code.alibabatech.com/schema/dubbo=org.apache.dubbo.config.spring.schema.DubboNamespaceHandler
Copy the code

When spring dubbo.apache.org/schema/dubb…

1.1.2 XmL-based Configuration Principle Parsing

As you can see from above, the configuration of XML constraints the role of dubo.xsd, as well as its parsing Handler.

public void init() {
        registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));
        registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));
        registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));
        registerBeanDefinitionParser("config-center", new DubboBeanDefinitionParser(ConfigCenterBean.class, true));
        registerBeanDefinitionParser("metadata-report", new DubboBeanDefinitionParser(MetadataReportConfig.class, true));
        registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));
        registerBeanDefinitionParser("metrics", new DubboBeanDefinitionParser(MetricsConfig.class, true));
        registerBeanDefinitionParser("ssl", new DubboBeanDefinitionParser(SslConfig.class, true));
        registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true));
        registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true));
        registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true));
        registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
        registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
        registerBeanDefinitionParser("annotation", new AnnotationBeanDefinitionParser());
    }
Copy the code

This part is mainly the PARSING logic of Spring’S XML. It will send the corresponding element to a specified Parser for parsing and save it in a map. When it encounters the element in the future, it will use the corresponding Parser for parsing. Parsing steps

  1. Replace the placeholder information in the Id to get an Id
  2. If the ID is empty, the ID is generated by the name, and if the name is also empty, the name of the class is used
  3. Check whether there is an identical ID in the current container. If so, throw an exception. If not, register the bean information
  4. Set the value of propertyValue and add the ID information
  5. Depending on the type of the element, different configuration information is parsed
  6. Find all the parameters configured in the XML and place them in the beanDefinition property

1.1.3 Annotation-based Configuration Principle Analysis

1.1.3.1 @ EnableDubbo

The EnableDubbo annotation is used to enable the Dubbo service. When Spring starts initialization, the annotation information in the corresponding configuration header is obtained. In the header of the @enableDubbo annotation, there are two important @enableDubboConfig and @DubBoComponentScan annotations. Each of these annotations contains an @import annotation.

  • @EnableDubboConfig
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Import(DubboConfigConfigurationRegistrar.class)
public @interface EnableDubboConfig {

    /**
     * It indicates whether binding to multiple Spring Beans.
     *
     * @return the default value is <code>true</code>
     * @revised2.5.9 * /
    boolean multiple(a) default true;

}
Copy the code

@ Import the values in the annotations DubboConfigConfigurationRegistrar ImportBeanDefinitionRegistrar is achieved, the Spring container load at the time of loading to @ Import annotations, And executes the registerBeanDefinitions method in the class specified by the value.

private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited)
			throws IOException {

	if (visited.add(sourceClass)) {
		for (SourceClass annotation : sourceClass.getAnnotations()) {
			String annName = annotation.getMetadata().getClassName();
			if(! annName.startsWith("java") && !annName.equals(Import.class.getName())) {
				collectImports(annotation, imports, visited);
			}
		}
		imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value")); }}Copy the code

All annotations are recursively iterated, if the annotation name is not Import and does not start with Java. The registerBeanDefinitions method is then executed.

public class DubboConfigConfigurationRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

        AnnotationAttributes attributes = AnnotationAttributes.fromMap(
                importingClassMetadata.getAnnotationAttributes(EnableDubboConfig.class.getName()));

        boolean multiple = attributes.getBoolean("multiple");

        // Single Config Bindings
        registerBeans(registry, DubboConfigConfiguration.Single.class);
        // The configured value starts with dubo.xxxs, which can be judged from Multiple annotations. Its main function is annotations in Multiple
        if (multiple) { / / Since 2.6.6 at https://github.com/apache/dubbo/issues/3193
            registerBeans(registry, DubboConfigConfiguration.Multiple.class);
        }

        // Register some configuration related beans with the Spring container
        / / Since 2.7.6registerCommonBeans(registry); }}Copy the code

When set to multiple, such as dubbo applations. Name1. Test1 = test1Value will at the time of loading ApplicationConfig, name1 will be the name of the bean, injected into the container, Test1 /test1Value is saved to the attribute in BeanDefination as the keyValue value. This parameter is available when there are multiple configuration centers.

  • @DubboComponentScan

A scan of Dubbo’s annotations.

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(DubboComponentScanRegistrar.class)
public @interface DubboComponentScan {

    /**
     * Alias for the {@link #basePackages()} attribute. Allows for more concise annotation
     * declarations e.g.: {@code @DubboComponentScan("org.my.pkg")} instead of
     * {@code @DubboComponentScan(basePackages="org.my.pkg")}.
     *
     * @return the base packages to scan
     */
    String[] value() default {};

    /**
     * Base packages to scan for annotated @Service classes. {@link #value()} is an
     * alias for (and mutually exclusive with) this attribute.
     * <p>
     * Use {@link #basePackageClasses()} for a type-safe alternative to String-based
     * package names.
     *
     * @return the base packages to scan
     */
    String[] basePackages() default {};

    /**
     * Type-safe alternative to {@link #basePackages()} for specifying the packages to
     * scan for annotated @Service classes. The package of each class specified will be
     * scanned.
     *
     * @return classes from the base packages to scan
     */Class<? >[] basePackageClasses()default {};

}
Copy the code

He will also go to perform @ Import of DubboComponentScanRegistrar. RegisterBeanDefinitions under class. The main steps are divided into three parts:

  1. Get the path that the annotation writes to all scannable packages
  2. Create a ServiceAnnotationBeanPostProcessor Bean, and set his constructor parameters for all packages can scan path
  3. Inject the beans generated in 2 into the container, and let Spring do the rest.

The key code for dealing with beans in 2 is as follows:

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {

        / / @ since 2.7.5
        registerInfrastructureBean(registry, DubboBootstrapApplicationListener.BEAN_NAME, DubboBootstrapApplicationListener.class);

        // Resolve the path of the package to be scanned. Replace placeholders that may exist in it, etc
        Set<String> resolvedPackagesToScan = resolvePackagesToScan(packagesToScan);

        if(! CollectionUtils.isEmpty(resolvedPackagesToScan)) {// Scan packet information
            registerServiceBeans(resolvedPackagesToScan, registry);
        } else {
            if (logger.isWarnEnabled()) {
                logger.warn("packagesToScan is empty , ServiceBean registry will be ignored!"); }}}/**
     * Registers Beans whose classes was annotated {@link Service}
     *
     * @param packagesToScan The base packages to scan
     * @param registry       {@link BeanDefinitionRegistry}
     */
    private void registerServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) {

        DubboClassPathBeanDefinitionScanner scanner =
                new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader);

        BeanNameGenerator beanNameGenerator = resolveBeanNameGenerator(registry);

        scanner.setBeanNameGenerator(beanNameGenerator);

        / / refactor @ since 2.7.7
        // Specify to scan annotation types under serviceAnnotationTypes
        serviceAnnotationTypes.forEach(annotationType -> {
            scanner.addIncludeFilter(new AnnotationTypeFilter(annotationType));
        });

        for (String packageToScan : packagesToScan) {

            // Registers @Service Bean first
            // It scans the package for each resource under the package, loads its Metadata information, and determines if it is an annotation marked with serviceAnnotationTypes
            // And inject into the container
            scanner.scan(packageToScan);

            // Finds all BeanDefinitionHolders of @Service whether @ComponentScan scans or not.
            // Generates a BeanDefinitionHolder
            Set<BeanDefinitionHolder> beanDefinitionHolders =
                    findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);

            if(! CollectionUtils.isEmpty(beanDefinitionHolders)) {for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
                    // Inject into the Bean container
                    registerServiceBean(beanDefinitionHolder, registry, scanner);
                }

                if (logger.isInfoEnabled()) {
                    logger.info(beanDefinitionHolders.size() + " annotated Dubbo's @Service Components { " +
                            beanDefinitionHolders +
                            " } were scanned under package[" + packageToScan + "]"); }}else {

                if (logger.isWarnEnabled()) {
                    logger.warn("No Spring Bean annotating Dubbo's @Service was found under package["
                            + packageToScan + "]"); }}}}Copy the code

2. Dubbo service exposure principle

Configuration carries initialization

Configuring priorities (from high to low)

  1. -d has the highest priority for parameters passed to the JVM
  2. Code or XML configuration takes precedence
  3. The configuration file has the lowest priority

For run-time property values (Dubbo configuration is affected by the provider), follow these rules:

  1. If the configuration rules are specified on the provider end, the configuration rules are automatically transmitted to the client transparently
  2. If the client is configured with the corresponding properties, the server is overwritten

Exposure mechanisms for remote services

At the time of the Spring container startup, will go to get all the ApplicationListener, then execute the onApplicationEvent OneTimeExecutionApplicationContextEventListener method. Will call into the DubboBootstrapApplicationListener onApplicationContextEvent method, detect is container startup event, she will go DubboBootstrap start method to carry on some of the initialization tasks, Then start exposing the service.

The overall process

According to the figure above, the exposure process is divided into six steps:

  1. According to ServiceConfig, configure the parameter information required for exposure
  2. Create a ProxyFactory using Dubbo’s SPI mechanism (using JavAssist by default)
  3. Based on the ProxyFactory created, a proxy object is created using bytecode technology
  4. Create an Invoker object with the proxy object and the parameter information generated in step 1
  5. Using the SPI mechanism, take a Protocol and convert it into an Exportor based on the specific Protocol
  6. Exposed services

When performing service exposures, be aware that the first exposed entry is in the ProtocolFilterWrapper exporter. The specific logic is as follows:

  1. When the SPI mechanism is used to obtain the Protocol, the method is adaptive extension point, which is selected according to the Protocol line in the url (adaptive generated code is as follows 🙂
package org.apache.dubbo.rpc;

import org.apache.dubbo.common.extension.ExtensionLoader;

public class Protocol$Adaptive implements org.apache.dubbo.rpc.Protocol {
  public org.apache.dubbo.rpc.Exporter export(org.apache.dubbo.rpc.Invoker arg0)
      throws org.apache.dubbo.rpc.RpcException {
    if (arg0 == null)
      throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument == null");
    if (arg0.getUrl() == null)
      throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument getUrl() == null");
    org.apache.dubbo.common.URL url = arg0.getUrl();
    String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
    if (extName == null)
      throw new IllegalStateException(
          "Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url ("
              + url.toString()
              + ") use keys([protocol])");
    org.apache.dubbo.rpc.Protocol extension =
        (org.apache.dubbo.rpc.Protocol)
            ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class)
                .getExtension(extName);
    return extension.export(arg0);
  }

  public void destroy(a) {
    throw new UnsupportedOperationException(
        "The method public abstract void org.apache.dubbo.rpc.Protocol.destroy() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");
  }

  public int getDefaultPort(a) {
    throw new UnsupportedOperationException(
        "The method public abstract int org.apache.dubbo.rpc.Protocol.getDefaultPort() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");
  }

  public org.apache.dubbo.rpc.Invoker refer(java.lang.Class arg0, org.apache.dubbo.common.URL arg1)
      throws org.apache.dubbo.rpc.RpcException {
    if (arg1 == null) throw new IllegalArgumentException("url == null");
    org.apache.dubbo.common.URL url = arg1;
    String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
    if (extName == null)
      throw new IllegalStateException(
          "Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url ("
              + url.toString()
              + ") use keys([protocol])");
    org.apache.dubbo.rpc.Protocol extension =
        (org.apache.dubbo.rpc.Protocol)
            ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class)
                .getExtension(extName);
    return extension.refer(arg0, arg1);
  }

  public java.util.List getServers(a) {
    throw new UnsupportedOperationException(
        "The method public default java.util.List org.apache.dubbo.rpc.Protocol.getServers() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!"); }}Copy the code

The key is to be a friend

String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
    if (extName == null)
      throw new IllegalStateException(
          "Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url ("
              + url.toString()
              + ") use keys([protocol])");
    org.apache.dubbo.rpc.Protocol extension =
        (org.apache.dubbo.rpc.Protocol)
            ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class)
                .getExtension(extName);
    return extension.export(arg0);
Copy the code

Therefore, the extension point with the name “Registry” is eventually selected as follows

So will use the org. Apache. Dubbo. Registry. Integration. RegistryProtocol. But since Protocol has two wrapper classes, the configuration is as follows,

filter=org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper
listener=org.apache.dubbo.rpc.protocol.ProtocolListenerWrapper
mock=org.apache.dubbo.rpc.support.MockProtocol
Copy the code

So when you retrieve an extension point, you end up returning information about the wrapper class. The following methods are in ExtensionLoader

 // Determine if it is a wrapper class. If so, initialize the wrapper class and inject the instance
      if(wrap) { List<Class<? >> wrapperClassesList =new ArrayList<>();
        // Sort the wrapper classes first
        if(cachedWrapperClasses ! =null) {
          wrapperClassesList.addAll(cachedWrapperClasses);
          wrapperClassesList.sort(WrapperComparator.COMPARATOR);
          Collections.reverse(wrapperClassesList);
        }

        if (CollectionUtils.isNotEmpty(wrapperClassesList)) {
          for(Class<? > wrapperClass : wrapperClassesList) { Wrapper wrapper = wrapperClass.getAnnotation(Wrapper.class);if (wrapper == null|| (ArrayUtils.contains(wrapper.matches(), name) && ! ArrayUtils.contains(wrapper.mismatches(), name))) { instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)); }}}}Copy the code
  1. Export is called once inside the wrapper class until the export method of the RegistryProtocol class is called
  2. Exposed services
@Override
    public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
        // Get the parameter of registry in the URL, and set the protocol of the current URL to the value of this parameter, if the parameter does not exist, return directly. Replace it with the registration protocol, for example, ZooKeeper
        URL registryUrl = getRegistryUrl(originInvoker);
        // url to export locally, grabs the export parameter in the URL. If it is null, an exception will be raised, otherwise a new URL will be created
        URL providerUrl = getProviderUrl(originInvoker);

        // 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.
        // TODO service override for the console. Save it for now, and continue later
        final URL overrideSubscribeUrl = getSubscribedOverrideUrl(providerUrl);
        final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker);
        overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);

        providerUrl = overrideUrlWithConfig(providerUrl, overrideSubscribeListener);
        //export Invoker creates a Netty service that listens for addresses and ports
        final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker, providerUrl);

        // url to registry
        // The SPI mechanism is used to obtain a Registry. The default is dubbo, which is selected according to the protocol parameter in the URL
        final Registry registry = getRegistry(originInvoker);
        final URL registeredProviderUrl = getUrlToRegistry(providerUrl, registryUrl);

        // decide if we need to delay publish
        boolean register = providerUrl.getParameter(REGISTER_KEY, true);
        if (register) {
            // Write the service to ZooKeeper
            register(registryUrl, registeredProviderUrl);
        }

        // register stated url on provider model
        // Is written to providers in the ServiceRepository to record information about the URL of the service provider and the exposed state
        registerStatedUrl(registryUrl, registeredProviderUrl, register);


        exporter.setRegisterUrl(registeredProviderUrl);
        exporter.setSubscribeUrl(overrideSubscribeUrl);

        // Deprecated! Subscribe to override rules in 2.6.x or before.
        registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);

        // A user-definable listener
        notifyExport(exporter);
        //Ensure that a new exporter instance is returned every time export
        return new DestroyableExporter<>(exporter);
    }
Copy the code

Inside is a final ExporterChangeableWrapper exporter = doLocalExport (originInvoker providerUrl); A DubboExporter(if you are using the Dubbo protocol) is first created and exporterMap is exported. Key is a exporter consisting of Group, Version, port, and Path, and value is a exporter that contains invoker. It then goes and opens a network server (netty by default) and listens on port 5. Write the registeredProviderUrl to the corresponding service (zooKeeper is recommended) according to registryUrl. 6. Modify the state and return to finish exposing the service

Creating a server (default netty)

private void openServer(URL url) {
        // find server.
        String key = url.getAddress();
        //client can export a service which's only for server to invoke
        boolean isServer = url.getParameter(IS_SERVER_KEY, true);
        if (isServer) {
            // Get a server according to Address
            ProtocolServer server = serverMap.get(key);
            if (server == null) {
                synchronized (this) {
                    server = serverMap.get(key);
                    // Create a servce if it is not found
                    if (server == null) {
                        // Address is used as the key to ensure that each address has only one nettyserverMap.put(key, createServer(url)); }}}else {
                // server supports reset, use together with overrideserver.reset(url); }}}Copy the code

If there is an Address, it will not create a server. If there is no Address, it will create a server and put it in the cache.

private ProtocolServer createServer(URL url) {
        // Add some server parameters
        url = URLBuilder.from(url)
                // send readonly event when server closes, it's enabled by default
                .addParameterIfAbsent(CHANNEL_READONLYEVENT_SENT_KEY, Boolean.TRUE.toString())
                // enable heartbeat by default
                .addParameterIfAbsent(HEARTBEAT_KEY, String.valueOf(DEFAULT_HEARTBEAT))
                // Set the codec, default is dubbo
                .addParameter(CODEC_KEY, DubboCodec.NAME)
                .build();
        String str = url.getParameter(SERVER_KEY, DEFAULT_REMOTING_SERVER);

        // Check whether there is an extension point for the network server. The default is netty
        if(str ! =null && str.length() > 0 && !ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(str)) {
            throw new RpcException("Unsupported server type: " + str + ", url: " + url);
        }

        ExchangeServer server;
        try {
            // Create a Netty service and bind and listen to host
            server = Exchangers.bind(url, requestHandler);
        } catch (RemotingException e) {
            throw new RpcException("Fail to start server(url: " + url + ")" + e.getMessage(), e);
        }

        str = url.getParameter(CLIENT_KEY);
        if(str ! =null && str.length() > 0) {
            Set<String> supportedTypes = ExtensionLoader.getExtensionLoader(Transporter.class).getSupportedExtensions();
            if(! supportedTypes.contains(str)) {throw new RpcException("Unsupported client type: "+ str); }}return new DubboProtocolServer(server);
    }
Copy the code

The default parameters for the network server will be set first. Codec is the codec for web services, which defaults to Dubbo. The default HEADER (SPI mechanism) is used. If the user has configured the HEADER in the ProviderConfig, the specified RECOVERY is used. The next step is to use the Transporter to bind the address to the server. During this process, the ProtocolConfig handler is wrapped as follows:Then, a Netty server is created based on the prepared data and listens for the address.

Write to registry

  1. Start by getting a registry creation factory
  2. Using the factory, get an instance of the registry
  3. Calling the Registry method
private void register(URL registryUrl, URL registeredProviderUrl) {
        // registryFactory creates a factory using adaptive
        // getRegistry will get a Registry based on protocol first, if not, use dubbo
        Registry registry = registryFactory.getRegistry(registryUrl);
        registry.register(registeredProviderUrl);
}
Copy the code

RegistryFactory is the value injected using the set method when instantiating the RegistryProtocol to get the extension point. RegistryFactory has a wrapper class org. Apache. Dubbo. Registry. RegistryFactoryWrapper, the instantiation is returns the wrapper classes. In the call registryFactory. GetRegistry (registryUrl), will be called to getRegistry methods of wrapper classes, will use registryFactory according to the URL to get a Registry instance, It is then wrapped as ListenerRegistryWrapper and returned, so it is ListenerRegistryWrapper that ends up calling the Registry method. RegistryFactory When instantiating the RegistryProtocol, the Set method is used to inject the RegistryFactory, and when fetching the extension point, the implementation class tagged @Adaptive under the RegistryFactory is fetched. Since the implementation class could not be found, javassist technology was used to generate and load an Adaptive class. The @adaptive method marked under the interface would be implemented and the extension point would be obtained according to protocol in the URL. If protocol is empty, The default DubboProtocolFactory is used. This is followed by the Registry method called above, which will eventually call ZookeeperRegister for service exposure because it contains ZookeeperRegistry inside ListenerRegistryWrapper. Let’s do another pictureZookeeperRefistry inherits from FailbackRegistry, which is invoked because it has not been overridden.

if(! acceptable(url)) { logger.info("URL " + url + " will not be registered to Registry. Registry " + url + " does not accept service of this protocol type.");
            return;
        }
        // Call the parent class's method to write the URL to the cache of the registered URL
        super.register(url);
        // If the URL fails to register, it is removed from the failed queue first
        removeFailedRegistered(url);
        // If the URL is unregistered, the unregistered URL is removed from the queue
        removeFailedUnregistered(url);
        try {
            // Sending a registration request to the server side
            // Call a concrete instance to write data. Then there's zK
            doRegister(url);
        } catch (Exception e) {
            Throwable t = e;

            // If the startup detection is opened, the Exception is thrown directly.
            boolean check = getUrl().getParameter(Constants.CHECK_KEY, true)
                    && url.getParameter(Constants.CHECK_KEY, true)
                    && !CONSUMER_PROTOCOL.equals(url.getProtocol());
            boolean skipFailback = t instanceof SkipFailbackWrapperException;
            if (check || skipFailback) {
                if (skipFailback) {
                    t = t.getCause();
                }
                throw new IllegalStateException("Failed to register " + url + " to registry " + getUrl().getAddress() + ", cause: " + t.getMessage(), t);
            } else {
                logger.error("Failed to register " + url + ", waiting for retry, cause: " + t.getMessage(), t);
            }

            // Record a failed registration request to a failed list, retry regularly
            addFailedRegistered(url);
        }
Copy the code

This remote service exposure is complete

Exposure mechanisms for local services

Local service exposure is much simpler than remote service exposure, mainly divided into two steps:

  1. Create a proxy object invoker using the proxy project.
  2. Create an InjvmExporter object and place invoker into the locally exposed map to complete the exposure
 private void exportLocal(URL url) {
        // Set protocol to injvm, which will be used later to obtain extension points
        URL local = URLBuilder.from(url)
                .setProtocol(LOCAL_PROTOCOL)
                .setHost(LOCALHOST_VALUE)
                .setPort(0)
                .build();
        // getInvoker creates a proxy object
        // Export creates an InjvmExporter and puts invoker into the cache map to expose itExporter<? > exporter = PROTOCOL.export( PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, local)); exporters.add(exporter); logger.info("Export dubbo service " + interfaceClass.getName() + " to local registry url : " + local);
    }
Copy the code