dubbo's service remote exposure

Write in front

stay Local exposure of dubbo services In this article, we analyze the local service registration using the injvm protocol when scope="local". Together, we look at the most commonly used method in actual business, that is, remote service exposure. Compared with local exposure, we mainly do the following things:

1: Start the communication server, bind the service port, and prepare to receive the remote request from the service consumer.
2: Register the service port information to the Registration Center for service consumers to obtain service call information.

Let's start now!

stay Local exposure of dubbo services On the basis of this article, it starts from doExportUrlsFor1Protocol. For details, refer to 1.1: doExportUrlsFor1Protocol.

1: Remote burst

The sequence diagram is as follows:

1.1: doExportUrlsFor1Protocol

The source code is as follows:

// com.alibaba.dubbo.config.ServiceConfig.doExportUrlsFor1Protocol
class FakeCls {
    private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
        // ***Construct the url that needs to be exposed, such as: dubbo://192.168.2.107:20826/dongshi.daddy.service.scoperemote.ScopeRemoteService?anyhost=true&application=dongshidaddy -provider&bean. name=dongshi. daddy. service. scoperemote. ScopeRemoteService&bind. ip=192.168.2.107&bind. port=20826&dubbo=2.0.2&generic=false&interface=dongshi. daddy. service. scoperemote. ScopeRemoteService&methods=sayHi&owner=dongshidaddy&pid=63200&side=provider&timestamp=1643363026148*** //
        String scope = url.getParameter(Constants.SCOPE_KEY);
        // The following only deals with non scope="none"
        if (!Constants.SCOPE_NONE.toString().equalsIgnoreCase(scope)) {
            // scope="local", or scope is not configured. In this case, scope=null, and here it will be true
            if (!Constants.SCOPE_REMOTE.toString().equalsIgnoreCase(scope)) {
                // Omit local burst logic
            }
            // scope="remote", or scope is not configured. In this case, scope=null, and here it will be true
            if (!Constants.SCOPE_LOCAL.toString().equalsIgnoreCase(scope)) {
                if (logger.isInfoEnabled()) {
                    logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
                }
                if (registryURLs != null && !registryURLs.isEmpty()) {
                    // For example: registry://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?application=dongshidaddy -provider&dubbo=2.0.2&owner=dongshidaddy&pid=63200&registry=zookeeper&timestamp=1643362790535
                    // Expose the url of the service that needs to be exposed to all registry addresses
                    for (URL registryURL : registryURLs) {
                        // If you don't know what to do, ignore it first!
                        url = url.addParameterIfAbsent(Constants.DYNAMIC_KEY, registryURL.getParameter(Constants.DYNAMIC_KEY));
                        // Get the url of the monitoring center. The monitoring center is also a service provider, which is used to count the service call information, times, time-consuming, etc
                        URL monitorUrl = loadMonitor(registryURL);
                        if (monitorUrl != null) {
                            // Monitor, adding the address of the monitor monitoring center, is actually a service provider
                            url = url.addParameterAndEncoded(Constants.MONITOR_KEY, monitorUrl.toFullString());
                        }
                        if (logger.isInfoEnabled()) {
                            logger.info("Register dubbo service " + interfaceClass.getName() + " url " + url + " to registry " + registryURL);
                        }
                        // Used by service providers to enable the generation of invoker s using custom proxy classes
                        String proxy = url.getParameter(Constants.PROXY_KEY);
                        if (StringUtils.isNotEmpty(proxy)) {
                            registryURL = registryURL.addParameter(Constants.PROXY_KEY, proxy);
                        }
                        // 2022-01-31 17:39:34
                        // Generate Invoker through ProxyFactory
                        // registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()): add the service address to be exposed to the registry address
                        // , key: after adding export: registry://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?application=dongshidaddy -provider&dubbo=2.0.2&export= dubbo://192.168.2.107:20826/dongshi.daddy.service.monitor.DubboMonitorService...
                        // When invoking the invoke method of the returned invoker, the corresponding service implementation class, that is, the specific method of ref, will be called internally
                        // Parameter ref: the specific service implementation Class, that is, the Class finally called. interfaceClass: service interface Class. registryURL: Registry address
                        Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));
                        // Create DelegateProviderMetaDataInvoker objects that encapsulate Invoker and ServiceConfig
                        DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
                        // 2022-01-31 15:01:03
                        Exporter<?> exporter = protocol.export(wrapperInvoker);
                        // private final List<Exporter<?>> exporters = new ArrayList<Exporter<?>>();
                        // Added to the exporters collection. The Exporter is used to obtain the invoker and cancel the disclosure
                        exporters.add(exporter);
                    }
                } else {
                    // The registered address is N/A. at this time, because the registered center address is not configured, it will not register with the registered center, but still listen to the port and leak the service. It is generally used in the scenario of direct connection of the consumer end
                    // If the service consumer wants to connect directly, it needs to set the url attribute, which can be configured as follows:
                    /*
                    <dubbo:reference id="providerService"
                                     interface="dongshi.daddy.service.ProviderService"
                                     url="dubbo://192.168.10.119:20880/dongshi.daddy.service.ProviderService"/>
                    */
                    // The fourth parameter is different from the normal way of registering with the registry. null is passed in here, and the url address of the registry is passed in when registering
                    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);
    }
}

At 15:01:03 on January 31, 2022, there was a sudden leak after the service was completed Local exposure of dubbo services In the article, we know that Protocol is used Wrapper of Adaptive extension class Therefore, before calling the real Protocol implementation class, the automatically generated Adaptive class and all Wrapper classes will be called first, as shown in the following debug diagram:

The reason for the above calling sequence is to ensure that the service startup information is registered with the registry after the service is started, as shown in the following pseudo code:

class RegistryProtocol {
    void export() {
        // Use the DubboProtocol burst service, that is, start netty and listen to the service port
        new DubboProtocol().export();
        // After the service is started, register the service information in the registry
        registerServerInfo();
    }
}

Let's take a look at the details in 2: Protocol.

2: Protocol

2.1:ProtocolFilterWrapper

The source code is as follows:

class FakeCls {
    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
        // It is true when the address of the registry is exposed, that is, registry: / / is true, otherwise it is false. At this time, it is a normal service exposure, and a Filter chain needs to be added
        if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
            // 2022-01-31 16:30:07
            return protocol.export(invoker);
        }
        // Add a new Filter chain, and then generate a new Filter
        return protocol.export(buildInvokerChain(invoker, Constants.SERVICE_FILTER_KEY, Constants.PROVIDER));
    }

}

At 16:30:07 on January 31, 2022, the registry protocol burst service is used. Therefore, this method mainly does the following two things:

1: Register service to registry
    Start local receive remote call service->Register service information to the registration center
2: Burst service

Refer to 2.2:RegistryProtocol for details.

2.2:RegistryProtocol

The source code is as follows:

class FakeCls {
    public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
        // 2022-01-31 16:58:42
        // Burst service, that is, start the corresponding local service port and prepare to receive the request call from the network. Note that the service has not been registered in the registration center at this time
        final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker);
        // 2022-02-06 16:46:37 
        URL registryUrl = getRegistryUrl(originInvoker);
        // Obtain the registration center object, which encapsulates the relevant operations of registration and deregistration. For example, zk is to establish relevant child nodes at the specified node
        final Registry registry = getRegistry(originInvoker);
        // Get service provider address, such as dubbo://192.168.64.1:20826/dongshi.daddy.service.monitor.DubboMonitorService?...
        final URL registeredProviderUrl = getRegisteredProviderUrl(originInvoker);
        // Whether to register. It is true normally
        boolean register = registeredProviderUrl.getParameter("register", true);
        // 2022-02-07 11:16:00
        ProviderConsumerRegTable.registerProvider(originInvoker, registryUrl, registeredProviderUrl);
        // Register service address to registration center
        if (register) {
            // 2022-02-07 13:05:06
            // Register service address to registration center
            register(registryUrl, registeredProviderUrl);
            // Settings already registered
            ProviderConsumerRegTable.getProviderWrapper(originInvoker).setReg(true);
        }
        // Fault tolerant related configuration
        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 is returned, which will be used when canceling the exposure
        return new DestroyableExporter<T>(exporter, originInvoker, overrideSubscribeUrl, registeredProviderUrl);
    }
}

At 16:58:42 on January 31, 2022, there was a leak service. Refer to 2.2.1 dolocalexport for details. 2022-02-06 16:46:37 is to obtain the address of the registration center. Because the service needs to be registered with the registration center, we start to obtain the address of the registration center here. For details, refer to 2.2.2:getRegistryUrl. At 11:16:00 on February 7, 2022, the service is registered in the local registry. Refer to 4:ProviderConsumerRegTable for details. 2022-02-07 13:05:06 is the registration service address to the registration center. For details, please refer to 2.2.3:register.

2.2.1:doLocalExport

This method only starts the service locally and does not register the service with the registry.
The source code is as follows:

class FakeCls {
    private <T> ExporterChangeableWrapper<T> doLocalExport(final Invoker<T> originInvoker) {
        // 2022-01-31 17:33:00
        String key = getCacheKey(originInvoker);
        // private final Map<String, ExporterChangeableWrapper<?>>  bounds = new ConcurrentHashMap<String, ExporterChangeableWrapper<?>> ();  Get from the bounds cache
        ExporterChangeableWrapper<T> exporter = (ExporterChangeableWrapper<T>) bounds.get(key);
        if (exporter == null) {
            // upper 🔒
            synchronized (bounds) {
                // 2022-02-01 11:31:59
                exporter = (ExporterChangeableWrapper<T>) bounds.get(key);
                // Still not in cache
                if (exporter == null) {
                    // Encapsulating the invokerdelegate object is mainly to add the getInvoker method, as follows:
                    /*
                    public Invoker<T> getInvoker() {
                        // Recursive acquisition. The invoker attribute may be invokerdelegate
                        if (invoker instanceof InvokerDelegete) {
                            return ((InvokerDelegete<T>) invoker).getInvoker();
                        } else {
                            return invoker;
                        }
                    }
                    */
                    // getProviderUrl(originInvoker): get the service exposure address, such as dubbo://192.168.2.107:20826/dongshi.daddy.service.monitor.DubboMonitorService...
                    final Invoker<?> invokerDelegete = new InvokerDelegete<T>(originInvoker, getProviderUrl(originInvoker));
                    // 2022-02-01 17:11:26
                    exporter = new ExporterChangeableWrapper<T>((Exporter<T>) protocol.export(invokerDelegete), originInvoker);
                    bounds.put(key, exporter);
                }
            }
        }
        return exporter;
    }
}

At 17:33:00 on January 31, 2022, the cache key of bounds is obtained. Refer to 2.2.1.1:getCacheKey for details. It was executed at 11:31:59 on February 1, 2022 DCL check , prevent the conditions for entering synchronization from changing. Protocol at 17:11:26 on February 1, 2022 Export (invokerdelegate) let's analyze, where protocol is Protocol$Adaptive, which is Local exposure of dubbo services In this article, we analyze the dynamically generated code of Protocol$Adaptive as follows:

public class Protocol$Adaptive implements com.alibaba.dubbo.rpc.Protocol {
    public void destroy() {
        throw new UnsupportedOperationException("method public abstract void com.alibaba.dubbo.rpc.Protocol.destroy() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
    }

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

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

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

Note the code string extname = (URL. Getprotocol() = = null) in the export method? "dubbo" : url. getProtocol());, Obtain the target extension name according to the protocol. At this time, debug can see the results as follows:

The result is dubbo, think again Wrapper Therefore, the final calling sequence is protocoladaptive - > protocollistenerwrapper - > potocolfilterwrapper - > dubboprotocol. For the specific calling process, refer to 3:DubboProtocol.

2.2.1.1:getCacheKey

The source code is as follows:

class FakeCls {
    private String getCacheKey(final Invoker<?> originInvoker) {
        // 2022-01-31 17:38:19
        // Get service provider address
        URL providerUrl = getProviderUrl(originInvoker);
        // Delete dynamic, enabled, and then convert it into a string as the cache key. The result is as follows:
        // dubbo://192.168.2.107:20826/dongshi.daddy.service.monitor.DubboMonitorService...
        String key = providerUrl.removeParameters("dynamic", "enabled").toFullString();
        return key;
    }
}

At 17:38:19 on January 31, 2022, the URL of the service provider is obtained. The source code is as follows:

class FakeCls {
    private URL getProviderUrl(final Invoker<?> origininvoker) {
        // The value has been set at 17:39:34 on 2022-01-31, so it can be obtained directly here
        // For example: registry://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?...&export=

        // The result obtained is: Dubbo% 3A% 2F% 2f192 168.2.107%3A20826%2Fdongshi. daddy. service. monitor. DubboMonitorService
        String export = origininvoker.getUrl().getParameterAndDecoded(Constants.EXPORT_KEY);
        // There must be export here, or Java. Is thrown Lang.illegalargumentexception exception
        if (export == null || export.length() == 0) {
            throw new IllegalArgumentException("The registry export url is null! registry: " + origininvoker.getUrl());
        }
        // Turn to com alibaba. dubbo. common. URL object
        URL providerUrl = URL.valueOf(export);
        return providerUrl;
    }

}

2.2.2:getRegistryUrl

The source code is as follows:

class FakeCls {
    // com.alibaba.dubbo.registry.integration.RegistryProtocol.getRegistryUrl
    private URL getRegistryUrl(Invoker<?> originInvoker) {
        // Get the address of the registration center directly, such as: registry://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?...
        URL registryUrl = originInvoker.getUrl();
        // If it is "public static final string registry_protocol =" Registry " Protocol, i.e. registry://
        if (Constants.REGISTRY_PROTOCOL.equals(registryUrl.getProtocol())) {
            // Get the value of the registry parameter as the new protocol name. If I use zk locally, yes registry://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?...&registry=zookeeper...
            // The result is zookeeper
            String protocol = registryUrl.getParameter(Constants.REGISTRY_KEY, Constants.DEFAULT_DIRECTORY);
            // Use zookeeper as the new protocol name, registry://127.0.0.1:2181...  ->  zookeeper://127.0.0.1:2181...
            registryUrl = registryUrl.setProtocol(protocol).removeParameter(Constants.REGISTRY_KEY);
        }
        // The results are as follows: zookeeper://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?...
        return registryUrl;
    }
}

2.2.3:register

The source code is as follows:

class FakeCls {
    // com.alibaba.dubbo.registry.integration.RegistryProtocol.register
    public void register(URL registryUrl, URL registedProviderUrl) {
        // 2022-02-07 14:17:03
        Registry registry = registryFactory.getRegistry(registryUrl);
        // 2022-02-07 15:51:34
        // If you use zk as the registration center, you can get com alibaba. dubbo. registry. zookeeper. ZookeeperRegistry
        registry.register(registedProviderUrl);
    }
}

At 14:17:03 on February 7, 2022, the Registry is obtained through RegistryFactory, where RegistryFactory is a self-adaption registryFactory is an automatically generated adaptive extension class RegistryFactory$Adaptive.
The source code of RegistryFactory is as follows:

@SPI("dubbo")
public interface RegistryFactory {
    @Adaptive({"protocol"})
    Registry getRegistry(URL url);
}

The automatically generated code is as follows:

package com.alibaba.dubbo.registry;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class RegistryFactory$Adaptive implements com.alibaba.dubbo.registry.RegistryFactory {
	public com.alibaba.dubbo.registry.Registry getRegistry(com.alibaba.dubbo.common.URL arg0) {
		if (arg0 == null) throw new IllegalArgumentException("url == null");
		com.alibaba.dubbo.common.URL url = arg0;
		String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
		if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.registry.RegistryFactory) name from url(" + url.toString() + ") use keys([protocol])");
		com.alibaba.dubbo.registry.RegistryFactory extension = (com.alibaba.dubbo.registry.RegistryFactory)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.registry.RegistryFactory.class).getExtension(extName);
		return extension.getRegistry(arg0);
	}
}

Execute code string extname = (URL. Getprotocol() = = null? "dubbo" : url. getProtocol() );, The result obtained is extName=zookeeper. Which extension class does it correspond to? You can get it from meta-inf \ Dubbo \ internal \ com alibaba. dubbo. registry. Find the answer in the registryfactory file, as follows:

dubbo=com.alibaba.dubbo.registry.dubbo.DubboRegistryFactory
multicast=com.alibaba.dubbo.registry.multicast.MulticastRegistryFactory
zookeeper=com.alibaba.dubbo.registry.zookeeper.ZookeeperRegistryFactory
redis=com.alibaba.dubbo.registry.redis.RedisRegistryFactory

The final obtained extension class is ZookeeperRegistryFactory, and its getRegistry will be called. Because this method is defined in its parent class AbstractRegistryFactory, the final called method is as follows:

class FakeCls {
    // com.alibaba.dubbo.registry.support.AbstractRegistryFactory.getRegistry
    public Registry getRegistry(URL url) {
        // 1: Set the path to com alibaba. dubbo. registry. RegistryService
        // 2: Set the parameter interface = com alibaba. dubbo. registry. RegistryService
        // 3: Delete parameter export
        url = url.setPath(RegistryService.class.getName())
                .addParameter(Constants.INTERFACE_KEY, RegistryService.class.getName())
                .removeParameters(Constants.EXPORT_KEY, Constants.REFER_KEY);
        // Cache key, such as zookeeper://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService
        String key = url.toServiceString();
        LOCK.lock();
        try {
            // Cache fetch
            Registry registry = REGISTRIES.get(key);
            // If there is a cache, return directly
            if (registry != null) {
                return registry;
            }
            // 2022-02-07 14:58:54
            registry = createRegistry(url);
            // You can't get it
            if (registry == null) {
                throw new IllegalStateException("Can not create registry " + url);
            }
            // Put in cache
            REGISTRIES.put(key, registry);
            return registry;
        } finally {
            LOCK.unlock();
        }
    }
}

At 14:58:54 on February 7, 2022, a Registry is created. For details, refer to 2.2.4:createRegistry. 2022-02-07 15:51:34 is the url of the service provider registered through the Registry of the corresponding registration center. For details, refer to 2.2.5: registering the service address to the registration center.

2.2.4:createRegistry

The source code is as follows:

class FakeCls {
    // com.alibaba.dubbo.registry.support.AbstractRegistryFactory.createRegistry
    protected abstract Registry createRegistry(URL url);
}

We found that this is an abstract template method, specifically calling com alibaba. dubbo. registry. zookeeper. ZookeeperRegistryFactory. Createregistry. The source code is as follows:

class FakeCls {
    // com.alibaba.dubbo.registry.zookeeper.ZookeeperRegistryFactory.createRegistry
    public Registry createRegistry(URL url) {
        // Return directly from new ZookeeperRegistry
        return new ZookeeperRegistry(url, zookeeperTransporter);
    }
}

The ZookeeperRegistry constructor is as follows:

class FakeCls {
    // com.alibaba.dubbo.registry.zookeeper.ZookeeperRegistry.ZookeeperRegistry
    public ZookeeperRegistry(URL url, ZookeeperTransporter zookeeperTransporter) {
        super(url);
        if (url.isAnyHost()) {
            throw new IllegalStateException("registry address == null");
        }
        // dubbo
        String group = url.getParameter(Constants.GROUP_KEY, DEFAULT_ROOT);
        // /dubbo
        if (!group.startsWith(Constants.PATH_SEPARATOR)) {
            group = Constants.PATH_SEPARATOR + group;
        }
        this.root = group;
        // Gets the client object of the operation zk
        zkClient = zookeeperTransporter.connect(url);
        // Add state change listener
        zkClient.addStateListener(new StateListener() {
            @Override
            public void stateChanged(int state) {
                if (state == RECONNECTED) {
                    try {
                        recover();
                    } catch (Exception e) {
                        logger.error(e.getMessage(), e);
                    }
                }
            }
        });
    }
}

2.2.5: register the service address to the registration center

Let's take ZookeeperRegistry as an example. The source code is as follows:

class FakeCls {
    // com.alibaba.dubbo.registry.zookeeper.ZookeeperRegistry.doRegister
    protected void doRegister(URL url) {
        try {
            // toUrlPath(url): the zk path to create, such as / Dubbo / Dongshi daddy. service. monitor. DubboMonitorService/providers/dubbo... version%3D0. zero point one one
            // After execution, the corresponding path is established in zk, the service information is registered in the registration center, and consumers can obtain the information of service providers through the registration center
            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);
        }
    }
}

3:DubboProtocol

The source code is as follows:

class FakeCls {
    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
        // Get the url object, such as dubbo://192.168.2.107:20826/dongshi.daddy.service.monitor.DubboMonitorService...
        URL url = invoker.getUrl();
        // 2022-02-01 18:58:33
        String key = serviceKey(url);
        // exporterMap->new ConcurrentHashMap<String, Exporter<?>> (); A service that stores a burst (local startup service)
        DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);
        //zs into map
        exporterMap.put(key, exporter);
        /*** Ignore start for now***/
        Boolean isStubSupportEvent = url.getParameter(Constants.STUB_EVENT_KEY, Constants.DEFAULT_STUB_EVENT);
        Boolean isCallbackservice = url.getParameter(Constants.IS_CALLBACK_SERVICE, false);
        if (isStubSupportEvent && !isCallbackservice) {
            String stubServiceMethods = url.getParameter(Constants.STUB_EVENT_METHODS_KEY);
            if (stubServiceMethods == null || stubServiceMethods.length() == 0) {
                if (logger.isWarnEnabled()) {
                    logger.warn(new IllegalStateException("consumer [" + url.getParameter(Constants.INTERFACE_KEY) +
                            "], has set stubproxy support event ,but no stub methods founded."));
                }
            } else {
                stubServiceMethodsMap.put(url.getServiceKey(), stubServiceMethods);
            }
        }
        /*** Ignore end for now***/
        // 2022-02-02 10:10:53
        openServer(url);
        // Initialization sequencer
        optimizeSerialization(url);
        return exporter;
    }
}

2022-02-01 18:58:33 is the unique identifier of the generated service. For details, refer to 3.1:serviceKey. 2022-02-02 10:10:53 is the start server. For details, refer to 3.2:openServer.

3.1:serviceKey

The source code is as follows:

class FakeCls {
    protected static String serviceKey(URL url) {
        // Get the port to bind, public static final String BIND_PORT_KEY = "bind.port";
        // dubbo://192.168.2.107:20826/dongshi.daddy.service.monitor.DubboMonitorService , the result is 20826
        int port = url.getParameter(Constants.BIND_PORT_KEY, url.getPort());
        // port: 20826,
        // url.getPath(): dongshi.daddy.service.monitor.DubboMonitorService
        // url.getParameter(Constants.VERSION_KEY): such as < Dubbo: Service Version = "0.0.11" / >, value is 0.0.11
        // url.getParameter(Constants.GROUP_KEY): such as < Dubbo: Service Group = "xxxx" / >, the value is xxxx
        // 2022-02-01 19:51:29
        return serviceKey(port, url.getPath(), url.getParameter(Constants.VERSION_KEY),
                url.getParameter(Constants.GROUP_KEY));
    }
}

At 19:51:29 on February 1, 2022, the method com was finally called alibaba. dubbo. rpc. support. ProtocolUtils. The source code of servicekey (int, java.lang.string, java.lang.string, Java. Lang.string) is as follows:

class FakeCls {
    // com.alibaba.dubbo.rpc.support.ProtocolUtils.serviceKey(int, java.lang.String, java.lang.String, java.lang.String)
    public static String serviceKey(int port, String serviceName, String serviceVersion, String serviceGroup) {
        StringBuilder buf = new StringBuilder();
        // Group name/
        if (serviceGroup != null && serviceGroup.length() > 0) {
            buf.append(serviceGroup);
            buf.append("/");
        }
        // Group name / service name
        buf.append(serviceName);
        // Group name / Service Name: version number
        if (serviceVersion != null && serviceVersion.length() > 0 && !"0.0.0".equals(serviceVersion)) {
            buf.append(":");
            buf.append(serviceVersion);
        }
        // Group name / Service Name: version number: port number
        buf.append(":");
        buf.append(port);
        // The result is as follows: XXXX / Dongshi daddy. service. monitor. DubboMonitorService:0.0.11:20826
        return buf.toString();
    }
}

3.2:openServer

The source code is as follows:

class FakeCls {
    private void openServer(URL url) {
        // Use the address as the unique identifier, such as 192.168.2.107:20826
        String key = url.getAddress();
        // The default value is "true", so the default value is "None"
        boolean isServer = url.getParameter(Constants.IS_SERVER_KEY, true);
        if (isServer) {
            // Exchange Server represents the Server and is a sub interface of the Server interface
            // Map<String, ExchangeServer> serverMap = new ConcurrentHashMap<String, ExchangeServer>();
            // key;host:port value: ExchangeServer
            ExchangeServer server = serverMap.get(key);
            if (server == null) {
                // 2022-02-05 20:26:00
                serverMap.put(key, createServer(url));
            // When there are multiple service s, it will not be empty
            } else {
                // Because it indirectly inherited com alibaba. dubbo. common. Resettable interface, so relevant properties can be reset here
                server.reset(url);
            }
        }
    }
}

2022-02-05 at 20:26:00 is to create a Server using the URL and add it to the serverMap. For details, refer to 3.3:createServer.

3.3:createServer

The source code is as follows:

class FakeCls {
    private ExchangeServer createServer(URL url) {
        // public static final String CHANNEL_READONLYEVENT_SENT_KEY = "channel.readonly.sent";
        // Send READONLY event when the server is shut down 
        // dubbo://192.168.2.107:20826/dongshi.daddy.service.monitor.DubboMonitorService?...&channel.readonly.sent=true&...
        url = url.addParameterIfAbsent(Constants.CHANNEL_READONLYEVENT_SENT_KEY, Boolean.TRUE.toString());
        // public static final int DEFAULT_HEARTBEAT = 60 * 1000;
        // public static final String HEARTBEAT_KEY = "heartbeat";
        // Enable heartbeat. The default heartbeat duration is one minute
        // dubbo://192.168.2.107:20826/dongshi.daddy.service.monitor.DubboMonitorService?...&heartbeat=60000&...
        url = url.addParameterIfAbsent(Constants.HEARTBEAT_KEY, String.valueOf(Constants.DEFAULT_HEARTBEAT));
        // public static final String SERVER_KEY = "server";
        // public static final String DEFAULT_REMOTING_SERVER = "netty";
        // Check whether there is a corresponding named Transporter Dubbo SPI extension class. Transporter is an interface used to operate netty, mina and other frameworks
        String str = url.getParameter(Constants.SERVER_KEY, Constants.DEFAULT_REMOTING_SERVER);
        // Ensure that the corresponding extension class exists
        if (str != null && str.length() > 0 && !ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(str))
            throw new RpcException("Unsupported server type: " + str + ", url: " + url);
        // dubbo codec is used for encoding and decoding, i.e. dubbocount codec
        url = url.addParameter(Constants.CODEC_KEY, DubboCodec.NAME);
        ExchangeServer server;
        try {
            // 2022-02-06 12:45:27
            server = Exchangers.bind(url, requestHandler);
        } catch (RemotingException e) {
            throw new RpcException("Fail to start server(url: " + url + ") " + e.getMessage(), e);
        }
        // Verify that the client extension class must exist
        str = url.getParameter(Constants.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 server;
    }
}

At 12:45:27 on February 6, 2022, the exchange is the owner of the exchange Facade class , which is used to encapsulate the details of the underlying system and simplify the use of users. Finally, exchange Server is returned, which is a sub interface of Server. The definition is as follows:

public interface ExchangeServer extends Server {
    Collection<ExchangeChannel> getExchangeChannels();
    ExchangeChannel getExchangeChannel(InetSocketAddress remoteAddress);
}

The log of bind method execution output is as follows:

[2022-02-0613:06:28][INFO ][jpm-AbstractServer-<init>(65)]- [DUBBO] Start NettyServer bind /0.0.0.0:20826, export /192.168.2.107:20826, dubbo version: 2.6.6, current host: 192.168.2.107

It can be seen that the netty service is started and the service port is exposed.

4:ProviderConsumerRegTable

This class is a support class (tool class) for the registration of local service consumers and service providers. In order to the Invoker of service providers and service consumers, it is mainly used for QOS(quality of service), such as offline, statistics, etc. the relevant codes are as follows:

public class ProviderConsumerRegTable {
    /**
     * Service provider Invoker collection
     * key: Service provider URL
     */
    public static ConcurrentHashMap<String, Set<ProviderInvokerWrapper>> providerInvokers = new ConcurrentHashMap<String, Set<ProviderInvokerWrapper>>();
    /**
     * Service consumer Invoker collection
     * key: Service consumer URL
     */
    public static ConcurrentHashMap<String, Set<ConsumerInvokerWrapper>> consumerInvokers = new ConcurrentHashMap<String, Set<ConsumerInvokerWrapper>>();
    // ....  Omitting method
}

4.1:ProviderInvokerWrapper

The wrapper class of provider invoker mainly adds some additional auxiliary operation and maintenance attributes, such as the attribute isReg marking whether to register. After offline operation, it can be modified to false, indicating that it has been offline. The main attributes are as follows:

class FakeCls {
    /**
     * Invoker object
     */
    private Invoker<T> invoker;
    /**
     * Original URL
     */
    private URL originUrl;
    /**
     * Registry URL
     */
    private URL registryUrl;
    /**
     * Service provider URL
     */
    private URL providerUrl;
    /**
     * Register
     */
    private volatile boolean isReg;
}

4.2:ConsumerInvokerWrapper

The wrapper class of consumer invoker mainly adds some additional attributes, as follows:

class FakeCls {
    /**
     * Invoker object
     */
    private Invoker<T> invoker;
    /**
     * Original URL
     */
    private URL originUrl;
    /**
     * Registry URL
     */
    private URL registryUrl;
    /**
     * Consumer URL
     */
    private URL consumerUrl;
    /**
     * Registry Directory
     */
    private RegistryDirectory registryDirectory;
}

Keywords: Dubbo Zookeeper

Added by jc_ply on Mon, 07 Feb 2022 10:46:25 +0200