Dubbo service provider starts the process

Posted by rockstarmike on Wed, 19 Jan 2022 03:54:53 +0100

First, consider the following questions:
1. When does the service establish a connection to the registry
2. When does the service provider register the service with the registry
3. Heartbeat mechanism between service provider and registry
If you want to fully understand the above problems, let's take the problems into the startup process of the service provider to find out:
In fact, Dubbo also builds its own service framework based on the spring framework. The core entry started by the service provider is also a ServiceBean related to the spring life cycle. The interfaces implemented by the ServiceBean include initializingbean and disposablebean,
ApplicationContextAware, ApplicationListener, BeanNameAware,
ApplicationEventPublisherAware are all spring life cycle related interfaces. The afterpropertieset method of InitializingBean interface is the key. ServiceBean implements the afterpropertieset method for specific instantiation operations

Step 1: judge whether to obtain the providerConfig. If not, obtain the providerConfig collection from the spring container
Get the ProtocolConfig collection from the container when the obtained provider collection is not empty. If the obtained ProtocolConfig set is empty, the relevant ProtocolConfig is generated according to the providerConfig set, as can be seen from the source code


If ProtocolConfig is not empty, only one providerConfig can be configured by default. If there are multiple, an exception will be thrown: duplicate provider configurations

Step 2: get ApplicationConfig. If you can't get it, get ApplicationConfig from the spring container, but there can't be multiple ApplicationConfig configurations, otherwise an exception will be thrown: duplicate application configurations

Step 3: get the ModuleConfig. If you can't get it, get it from the spring container. There can't be more than one ModuleConfig configuration. Otherwise, an exception will be thrown: duplicate module configurations

Step 4: get the RegistryConfig registry configuration. Firstly, RegistryIds will be set according to the relevant information of provider and application, and then all RegistryConfig information in the container will be obtained


Then you can obtain relevant information, such as metadata reportconfig, ConfigCenterConfig, MonitorConfig, MetricsConfig and ProtocolConfig protocol information configuration
A key information point in the last step is that you need to wait until the spring container starts refreshing before starting the real service provider startup

It can be seen that the processing entry of Dubbo's service exposure is servicebean #export - > serviceconfig #export
Next, let's go to the ServiceConfig#export method for analysis:

First, check and update the relevant configuration information and check whether the service is exposed:

In addition, whether the delay mechanism is enabled. If the delay is greater than 0, it indicates the number of milliseconds after the delay to expose the service. Use the ScheduledExecutorService to delay scheduling, and then conduct doExport processing
Check and update relevant configuration information, including information check and configuration of configuration center, protocol and application

Judge whether it is a generalized implementation, and then verify whether the interface interface is consistent with the type of ref reference


Process local stub. When the stub attribute is true, the class name of the stub is: interface+stub. Stub can also be specified as a custom full class name.
The principle of dubbo's local Stub is: after a remote service, the client usually only has an interface, and the implementation is all on the server side. However, sometimes the provider wants to execute some logic on the client side, so it provides a Stub class on the service consumer side. Then, when the consumer calls the dubbo service provided by the provider, the client generates a Proxy instance, This Proxy instance is the Proxy instance that we normally call dubbo remote service to generate. Then the consumer will pass the Proxy to the consumer's Stub through the constructor, and then expose the Stub to the user. The Stub can decide whether to call the Proxy or not. It will complete the call through the Proxy class, so that in the Stub class, you can do some extra things to optimize or fault-tolerant the service call process. Attached drawings


Register the service provider information into the ApplicationModel instance. First, traverse the ServiceBean's list < registryconfig < > > registers (configuration centers of all registries), then encapsulate the address into a URL object, and finally convert all configuration attributes about the registry into URL attributes (? Attribute name = attribute value)
The meaning of the loadRegistries (true) parameter: true represents a service provider and false represents a service consumer. If it is a service provider, the configuration of the registry will be detected. If register = "false" is configured, the address will be ignored. If it is a service consumer and subscribe="false" is configured, it means that the service will not be subscribed from the registry, so it will not be returned, An example of a registry URL:
registry://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.0.0&pid=7072&qos.port=22222&registry=zookeeper&timestamp=1527308268041
Code @2: then traverse all configured protocols and expose services to the registry according to each protocol. Next, focus on the implementation details of the doExportUrlsFor1Protocol method.
Source code analysis doExportUrlsFor1Protocol
Call chain: servicebean #afterpropertiesset ------ > serviceconfig #export ------ > serviceconfig #doexport ------ > serviceconfig #doexporturlsfor1protocol
   ServiceConfig#doExportUrlsFor1Protocol
Use Map to store all configuration parameters of the protocol, including protocol name, dubbo version, current system timestamp, process ID, application configuration, module configuration, default service provider parameter (ProviderConfig), protocol configuration and service provider properties.
When the method name is not empty, the configuration properties of dubbo:method and its sub tags are stored in the Map, and the property name is prefixed with the corresponding method name. dubbo:method's sub - label dubbo:argument, whose key is the method name Parameter serial number

     String name = protocolConfig.getName();
    if (StringUtils.isEmpty(name)) {
        name = DUBBO;
    }
    Map<String, String> map = new HashMap<String, String>();
    map.put(SIDE_KEY, PROVIDER_SIDE);

    appendRuntimeParameters(map);
    appendParameters(map, metrics);
    appendParameters(map, application);
    appendParameters(map, module);
    // remove 'default.' prefix for configs from ProviderConfig
    // appendParameters(map, provider, Constants.DEFAULT_KEY);
    appendParameters(map, provider);
    appendParameters(map, protocolConfig);
    appendParameters(map, this);
    if (CollectionUtils.isNotEmpty(methods)) {
        for (MethodConfig method : methods) {
            appendParameters(map, method, method.getName());
            String retryKey = method.getName() + ".retry";
            if (map.containsKey(retryKey)) {
                String retryValue = map.remove(retryKey);
                if ("false".equals(retryValue)) {
                    map.put(method.getName() + ".retries", "0");
                }
            }
            List<ArgumentConfig> arguments = method.getArguments();
            if (CollectionUtils.isNotEmpty(arguments)) {
                for (ArgumentConfig argument : arguments) {
                    // convert argument type
                    if (argument.getType() != null && argument.getType().length() > 0) {
                        Method[] methods = interfaceClass.getMethods();
                        // visit all methods
                        if (methods != null && methods.length > 0) {
                            for (int i = 0; i < methods.length; i++) {
                                String methodName = methods[i].getName();
                                // target the method, and get its signature
                                if (methodName.equals(method.getName())) {
                                    Class<?>[] argtypes = methods[i].getParameterTypes();
                                    // one callback in the method
                                    if (argument.getIndex() != -1) {
                                        if (argtypes[argument.getIndex()].getName().equals(argument.getType())) {
                                            appendParameters(map, argument, method.getName() + "." + argument.getIndex());
                                        } else {
                                            throw new IllegalArgumentException("Argument config error : the index attribute and type attribute not match :index :" + argument.getIndex() + ", type:" + argument.getType());
                                        }
                                    } else {
                                        // multiple callbacks in the method
                                        for (int j = 0; j < argtypes.length; j++) {
                                            Class<?> argclazz = argtypes[j];
                                            if (argclazz.getName().equals(argument.getType())) {
                                                appendParameters(map, argument, method.getName() + "." + j);
                                                if (argument.getIndex() != -1 && argument.getIndex() != j) {
                                                    throw new IllegalArgumentException("Argument config error : the index attribute and type attribute not match :index :" + argument.getIndex() + ", type:" + argument.getType());
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    } else if (argument.getIndex() != -1) {
                        appendParameters(map, argument, method.getName() + "." + argument.getIndex());
                    } else {
                        throw new IllegalArgumentException("Argument config must set index or type attribute.eg: <dubbo:argument index='0' .../> or <dubbo:argument type=xxx .../>");
                    }

                }
            }
        } // end of methods for
    }

Add the methods key value pair to store all the method names of dubbo:service. Multiple method names are separated by. If it is a generalized implementation, fill in generic = true and methods is "*";

        if (ProtocolUtils.isGeneric(generic)) {
        map.put(GENERIC_KEY, generic);
        map.put(METHODS_KEY, ANY_VALUE);
    } else {
        String revision = Version.getVersion(interfaceClass, version);
        if (revision != null && revision.length() > 0) {
            map.put(REVISION_KEY, revision);
        }
        String[] methods = Wrapper.getWrapper(interfaceClass).getMethodNames();
        if (methods.length == 0) {
            logger.warn("No method found in service interface " + interfaceClass.getName());
            map.put(METHODS_KEY, ANY_VALUE);
        } else {
            map.put(METHODS_KEY, StringUtils.join(new HashSet<String>(Arrays.asList(methods)), ","));
        }
    }        

It depends on whether the token mechanism is enabled. If enabled, set the token key. The value is a static value or uuid

if (!ConfigUtils.isEmpty(token)) {
if (ConfigUtils.isDefault(token)) {
map.put(TOKEN_KEY, UUID.randomUUID().toString());
} else {
map.put(TOKEN_KEY, token);
}
}

Resolve the IP address and port of the service provider.
Service IP address resolution order: (the smaller the sequence number, the more priority)

System environment variable, variable name: DUBBO_DUBBO_IP_TO_BIND
System attribute, variable name: DUBBO_DUBBO_IP_TO_BIND
System environment variable, variable name: DUBBO_IP_TO_BIND
System attribute, variable name: DUBBO_IP_TO_BIND
Host attribute of dubbo:protocol tag -- host attribute of dubbo:provider tag
Default network card IP address, via InetAddress getLocalHost(). Get gethostaddress(). If the IP address does not meet the requirements, continue the next matching

   // export service
    String host = this.findConfigedHosts(protocolConfig, registryURLs, map);
    Integer port = this.findConfigedPorts(protocolConfig, name, map);
    URL url = new URL(name, host, port, getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), map);

Build the service provider URI according to the protocol name, protocol host, protocol port, contextPath and related configuration properties (application, module, provider, protocolConfig, service and its sub tags).
URL running rendering
   
Taking Dubbo protocol as an example, the URL information of the final service provider is shown as follows: dubbo://192.168.56.1:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo -provider&bind. ip=192.168.56.1&bind. port=20880&dubbo=2.0.0&generic=false&interface=com. alibaba. dubbo. demo. DemoService&methods=sayHello&pid=5916&qos. port=22222&side=provider&timestamp=1527168070857
Get the scope attribute of dubbo:service tag. Its optional values are none (not exposed), local (local) and remote (remote). If it is configured as none, it will not be exposed. The default is local

String scope = url.getParameter(SCOPE_KEY);
// don't export when none is configured
if (!SCOPE_NONE.equalsIgnoreCase(scope)) {
// export to local if the config is not remote (export to remote only when config is remote)
if (!SCOPE_REMOTE.equalsIgnoreCase(scope)) {
exportLocal(url);
}
// export to remote if the config is not local (export to local only when config is local)
if (!SCOPE_LOCAL.equalsIgnoreCase(scope)) {
if (!isOnlyInJvm() && logger.isInfoEnabled()) {
logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
}
if (CollectionUtils.isNotEmpty(registryURLs)) {
for (URL registryURL : registryURLs) {
//if protocol is only injvm ,not register
if (LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {
continue;
}
url = url.addParameterIfAbsent(DYNAMIC_KEY, registryURL.getParameter(DYNAMIC_KEY));
URL monitorUrl = loadMonitor(registryURL);
if (monitorUrl != null) {
url = url.addParameterAndEncoded(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(PROXY_KEY);
                    if (StringUtils.isNotEmpty(proxy)) {
                        registryURL = registryURL.addParameter(PROXY_KEY, proxy);
                    }

                    Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString()));
                    DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);

                    Exporter<?> exporter = protocol.export(wrapperInvoker);
                    exporters.add(exporter);
                }
            } else {
                Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, url);
                DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);

                Exporter<?> exporter = protocol.export(wrapperInvoker);
                exporters.add(exporter);
            }
            /**
             * @since 2.7.0
             * ServiceData Store
             */
            MetadataReportService metadataReportService = null;
            if ((metadataReportService = getMetadataReportService()) != null) {
                metadataReportService.publishProvider(url);
            }
        }
    }    

Expose services according to the scope. If the scope is not configured, both local and remote services will be exposed by default. If it is configured as local or remote, it can only be one of two.
Code @1: if the scope is not remote, it will be exposed locally (injvm) first. The specific implementation of the service will be exposed, which will be analyzed in detail in the remote mode.
Code @2: if the scope is not local, expose the service to the remote.
Code @ 3: remote mode: detect all the currently configured registries. If the registry is not empty, traverse the registry and register the services in different registries in turn.
Code @4: if the dynamic attribute of dubbo:service is not configured, try to get the dynamic attribute of dubbo:registry. The function of this attribute is to enable dynamic registration. If it is set to false, the status of the service after registration is displayed as disable, which needs to be enabled manually. When the service is unavailable, it will not be removed automatically. It also needs manual processing, This property should not be configured on a production environment.
Code @5: build the URL of the monitoring center according to the registration center URL (Registration Center url). If the monitoring center URL is not empty, add monitor on the service provider URL, and its value is the monitoring center URL (encoded).

If the monitoring center (dubbo:monitor) is not configured in the dubbo spring xml configuration file, if from the system attribute - ddubbo monitor. address,-Ddubbo.monitor.protocol builds the MonitorConfig object. Otherwise, look for these two parameters from dubbo's properties configuration file. If there is no configuration, null will be returned.
If it is configured, relevant parameters will be added. The dubbo:monitor tag has only two attributes: address and protocol, followed by interface(MonitorService) and protocol.
Code @6: create Invoker and dubbo's remote call implementation class through dynamic proxy mechanism

How to build a Dubbo remote caller is not detailed here. The url of WrapperInvoker is: registry://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?application=demo -provider&dubbo=2.0.0&export=dubbo%3A%2F%2F192. 168.56.1%3A20880%2Fcom. alibaba. dubbo. demo. DemoService%3Fanyhost%3Dtrue%26application%3Ddemo-provider%26bind. ip%3D192. 168.56.1%26bind. port%3D20880%26dubbo%3D2. 0.0%26generic%3Dfalse%26interface%3Dcom. alibaba. dubbo. demo. DemoService%26methods%3DsayHello%26pid%3D6328%26qos. port%3D22222%26side%3Dprovider%26timestamp%3D1527255510215&pid=6328&qos. Port = 22222 & registry = zookeeper & timestamp = 1527255510202, there are two key points worth paying attention to:

path attribute: com alibaba. dubbo. registry. Registryservice, a registry is also similar to a service provider.
export attribute: the value is the URL of the service provider. Why should we pay attention to this URL? Please look at the code @7. The protocol attribute is Protocol$Adaptive. Dubbo uses SPI (plug-in mechanism, which will be analyzed in the subsequent articles of this topic) when loading the component implementation class. Here, we only need to know that the corresponding method will be called according to the protocol name before the URL colon
Its mapping relationship (list the protocol implementation classes related to service startup):
dubbo=com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol / / the file is located at Dubbo RPC Dubbo / SRC / main / resources / meta-inf / Dubbo / internal / com alibaba. dubbo. rpc. Protocol
registry=com.alibaba.dubbo.registry.integration.RegistryProtocol / / the file is located at Dubbo registry API / SRC / main / resources / meta-inf / Dubbo / internal / com alibaba. dubbo. rpc. Protocol
Code @ 7: according to the analysis of code @ 6, the RegistryProtocol#export method will be called
public Exporter export(final Invoker originInvoker) throws RpcException {
URL registryUrl = getRegistryUrl(originInvoker);
// url to export locally
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.
    final URL overrideSubscribeUrl = getSubscribedOverrideUrl(providerUrl);
    final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker);
    overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);

    providerUrl = overrideUrlWithConfig(providerUrl, overrideSubscribeListener);
    //export invoker
    final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker, providerUrl);

    // url to registry
    final Registry registry = getRegistry(originInvoker);
    final URL registeredProviderUrl = getRegisteredProviderUrl(providerUrl, registryUrl);
    ProviderInvokerWrapper<T> providerInvokerWrapper = ProviderConsumerRegTable.registerProvider(originInvoker,
            registryUrl, registeredProviderUrl);
    //to judge if we need to delay publish
    boolean register = registeredProviderUrl.getParameter("register", true);
    if (register) {
        register(registryUrl, registeredProviderUrl);
        providerInvokerWrapper.setReg(true);
    }

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

    exporter.setRegisterUrl(registeredProviderUrl);
    exporter.setSubscribeUrl(overrideSubscribeUrl);
    //Ensure that a new exporter instance is returned every time export
    return new DestroyableExporter<>(exporter);
}

Code @ 1: start the service provider service, listen to the specified port, and prepare the service consumer's request. In fact, this is to extract the export attribute from the url (registry url) in WrapperInvoker, describe the url of the service provider, and then start the service provider.

It can be seen from the above figure that DubboProtocol#export will be called to complete the startup of dubbo service, and netty will be used to build a micro server, listen to the port, and prepare to accept the network requests of service consumers. This section aims to sort out the startup process, and the specific implementation details will be explained in the subsequent chapters. Here we just need to know, < dubbo: protocol name = "dubbo" port = "20880" / >, it will listen to the port again this time, and then add the service handler of dubbo:service to the command processor. When a message consumer connects to the port, unpack it through the network, parse and process the information such as the service and parameters to be called, and transfer it to the corresponding service implementation class for processing.
Code @ 2: get the URL of the real registry, for example, the URL of zookeeper registry: zookeeper://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?application=demo -provider&dubbo=2.0.0&export=dubbo%3A%2F%2F192. 168.56.1%3A20880%2Fcom. alibaba. dubbo. demo. DemoService%3Fanyhost%3Dtrue%26application%3Ddemo-provider%26bind. ip%3D192. 168.56.1%26bind. port%3D20880%26dubbo%3D2. 0.0%26generic%3Dfalse%26interface%3Dcom. alibaba. dubbo. demo. DemoService%26methods%3DsayHello%26pid%3D10252%26qos. port%3D22222%26side%3Dprovider%26timestamp%3D1527263060882&pid=10252&qos. port=22222&timestamp=1527263060867
Code @3: obtain the specified registry implementation class from the registry factory according to the registry URL: zookeeper. The implementation class of the registry is ZookeeperRegistry
Code @4: get the register attribute in the service provider URL. If it is true, call the ZookeeperRegistry#register method of the registry to register the service with the registry (actually implemented by its parent class FailbackRegistry).
Code @5: the service provider subscribes to the registry to re expose the service after the service provider URL changes. Of course, the check attribute of dubbo:reference will be set to false.

Here is an answer to question 1 and question 2 mentioned at the beginning of the article. Its relationship with the heartbeat mechanism of the registry will be analyzed in detail in the following chapters.

The text may not be very intuitive. Now let's sort out the startup flow chart of Dubbo service provider as follows

Reference original address: https://blog.csdn.net/prestigeding/article/details/80536385

Topics: Java Eclipse IDE