Summary: With the advent of cloud native, an important goal of Dubbo 3.0 is to fully embrace cloud native. For this reason, Dubbo 3.0 has evolved from the interface level service discovery mechanism to the application level service discovery mechanism in order to better adapt to cloud native.

The authors introduce

Ping Xiong, Github account Pinxiong, Apache Dubbo contributor, focuses on RPC, Service Mesh and cloud native. Now I am working in the R&D team of Ctrip International Division, responsible for marketing, cloud native and other related work.

background

With the advent of cloud native, an important goal of Dubbo 3.0 is to fully embrace cloud native. For this reason, Dubbo 3.0 has evolved from the interface level service discovery mechanism to the application level service discovery mechanism in order to better adapt to cloud native.

Based on the application-level service discovery mechanism, Dubbo 3.0 can significantly reduce the additional resource consumption brought by the framework and greatly improve resource utilization, mainly in the following aspects:

  • Single-machine resident memory decreased by 75%
  • Can support cluster instance sizes of millions of clusters
  • The total volume of data in the registry dropped by more than 90%

There are many technical articles about the Dubbo server exposure process, but they are interpreted based on the Dubbo interface level service discovery mechanism. Under the application level service discovery mechanism of Dubbo 3.0, the process of server exposure has changed a lot. This article hopes to analyze the whole process of server exposure by understanding the source code of Dubbo 3.0.

What is application-level service discovery

To put it simply, Dubbo used to register all the interface information in the registry, but an application instance usually has multiple interfaces, so the amount of registered data is much larger, and there is redundancy. The mechanism of application-level service discovery is that an application instance registers only one data in the registry. This mechanism mainly solves the following problems:

  • Align with mainstream microservice models such as Spring Cloud
  • Supports Kubernetes Native Service. The maintenance and scheduling services in Kubernetes are based on the application instance level, not the interface level
  • Reduced registry data storage capacity and reduced address change push pressure

Assume that three instances (Instance1, Instance2, and instance3) of dubbo-Application are deployed and three interfaces (sayHello, Echo, and getVersion) are provided with different timeout periods. Under the interface level and application level service discovery mechanisms, the data registered to the registry is quite different. As shown below:

  • Data in the registry under the interface level service discovery mechanism

    “sayHello”: [{” application “:” dubbo – application “, “name” : “instance1”, “IP” : “127.0.0.1”, “metadata” : {” timeout “: 1000}}, {” application “, “dubbo – application”, “name” : “instance2”, “IP” : “127.0.0.2”, “metadata” : {” timeout “: 2000}}, {” application “, “dubbo – application”, “name” : “instance3”, “IP” : “127.0.0.3”, “metadata” : {” timeout “: 3000}},],” echo “: [{” application “:” dubbo – application “, “name” : “instance1”, “IP” : “127.0.0.1”, “metadata” : {” timeout “: 1000}}, {” application “, “dubbo – application”, “name” : “instance2”, “IP” : “127.0.0.2”, “metadata” : {” timeout “: 2000}}, {” application “, “dubbo – application”, “name” : “instance3”, “IP” : “127.0.0.3”, “metadata” : {” timeout “: 3000}},],” getVersion “: [{” application “:” dubbo – application “, “name” : “instance1”, “IP” : “127.0.0.1”, “metadata” : {” timeout “: 1000}}, {” application “, “dubbo – application”, “name” : “instance2”, “IP” : “127.0.0.2”, “metadata” : {” timeout “: 2000}}, {” application “, “dubbo – application”, “name” : “instance3”, “IP” : “127.0.0.3”, “metadata” : {” timeout “: 3000}}]

  • Data in the registry under the application-level service discovery mechanism

    “dubbo-application”: [{” name “:” instance1 “, “IP” : “127.0.0.1”, “metadata” : {” timeout “: 1000}}, {” name” : “instance2”, “IP” : “127.0.0.2.” “Metadata” : {” timeout “: 2000}}, {” name” : “instance3”, “IP” : “127.0.0.3”, “metadata” : {” timeout “: 3000}}]

By comparison, the adoption of application-level service discovery does reduce the amount of data in the registry, where the original interface-level data is stored in the metadata center.

The server exposes the whole process

With the introduction of application-level service discovery, the whole process of server exposure on Dubbo 3.0 is very different. DubboBootstrap#doStart

private void doStart() { // 1. ExportServices (); // If register consumer instance or has exported services if (isRegisterConsumerInstance() || hasExportedServices()) { / / 2. Exposed metadata service exportMetadataService(); // 3. Periodically update and report metadata registerServiceInstance(); . }... }Copy the code

Assuming that Zookeeper is used as a service that exposes Triple protocol during registration, the sequence diagram of the whole process exposed on the server side is as follows:

We can see that the whole exposure process is quite complicated, which can be divided into four parts:

  • Service that exposes injVM protocol
  • Register the service-discovery-Registry protocol
  • Expose the Triple protocol service and register the Registry protocol
  • Expose the MetadataService service

The entire process of service exposure will be explained in detail from these four parts.

1. Expose injVM protocol services

Injvm services are locally exposed because an application usually has both Service (exposed Service) and Reference (Service Reference), and the Service referenced is the Service exposed in the application. To support this usage scenario, Dubbo provides the InjVM protocol to expose the Service locally so that Reference can invoke the Service locally without going over the network.

Overall sequence diagram

Since this is similar to the previous interface level service discovery mechanism, the core code is not covered here.

2. Register the service-discovery-Registry protocol

Registration service discovery – the core of the registry agreement in order to register the metadata associated with the service, by default metadata through InMemoryWritableMetadataService local memory and store the data in the local file.

Overall sequence diagram

The core code is in ServiceConfig#exportRemote as follows:

  • An entry to register the service-discovery-Registry protocol

    Private URL exportRemote (URL URL, List registryURLs) {if (CollectionUtils. IsNotEmpty (registryURLs)) {/ / if it is a multiple registries, Register each registry through a loop for (URL registryURL: RegistryURLs) {// Check if it is service-discovery-registry // set the service-name-mapping parameter to true if (SERVICE_REGISTRY_PROTOCOL.equals(registryURL.getProtocol())) { url = url.addParameterIfAbsent(SERVICE_NAME_MAPPING_KEY, “true”); }… DoExportUrl (registryurl.putattribute (EXPORT_KEY, url), true); }… return url; }

  • Wrap Metadata in invoker

The core code is in ServiceConfig#doExportUrl, detailed as follows:

private void doExportUrl(URL url, boolean withMetaData) { Invoker<? > invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, url); / / the withMetaData value is true / / invoker packing into DelegateProviderMetaDataInvoker if (withMetaData) {invoker = new DelegateProviderMetaDataInvoker(invoker, this); } Exporter<? > exporter = PROTOCOL.export(invoker); exporters.add(exporter); }Copy the code
  • Convert Invoker to Exporter through RegistryProtocol

The core code is in ProtocolListenerWrapper#export, as follows:

Public <T> Exporter<T> export(Invoker<T> Invoker) throws RpcException {// the protocol is at least the RegistryProtocol type if (UrlUtils.isRegistry(invoker.getUrl())) { return protocol.export(invoker); }... }Copy the code
  • RegistryProtocol converts Invoker into a core export process

The core code is listed in RegistryProtocol#export as follows:

public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException { URL registryUrl = getRegistryUrl(originInvoker); URL providerUrl = getProviderUrl(originInvoker); . / / once again exposed Triple protocol service final ExporterChangeableWrapper < T > exporter = doLocalExport (originInvoker providerUrl); // The registryUrl contains the service-discovery-Registry protocol. // Create a ServiceDiscoveryRegistry object with this protocol. // Then combine the RegistryServiceListener listener. Final Registry Registry = getRegistry(registryUrl); final URL registeredProviderUrl = getUrlToRegistry(providerUrl, registryUrl); boolean register = providerUrl.getParameter(REGISTER_KEY, true); If (register) {// register the service-discovery-registry protocol // trigger the RegistryServiceListener onRegister event register(registry, registeredProviderUrl); }... // Trigger RegistryServiceListener onRegister event notifyExport(EXPORTER); return new DestroyableExporter<>(exporter); }Copy the code
  • Services that expose the Triple protocol

The core code is in RegistryProtocol#doLocalExport as follows:

private <T> ExporterChangeableWrapper<T> doLocalExport(final Invoker<T> originInvoker, URL providerUrl) { String key = getCacheKey(originInvoker); / / this protocol for Triple protocol proxy class / / and exposure injvm agreement protocol return same (ExporterChangeableWrapper < T >) bounds.com puteIfAbsent (key, s -> { Invoker<?> invokerDelegate = new InvokerDelegate<>(originInvoker, providerUrl); return new ExporterChangeableWrapper<>((Exporter<T>) protocol.export(invokerDelegate), originInvoker); }); }Copy the code
  • Register the service-discovery-Registry protocol

The core code is in ServiceDiscoveryRegistry#register and ServiceDiscoveryRegistry#doRegister as follows:

1, ServiceDiscoveryRegistry# register

Public final void register(URL URL) {// Only providers need to register if (! shouldRegister(url)) { return; } // Register the service discovery-registry protocol doRegister(url); }Copy the code

2, ServiceDiscoveryRegistry# doRegister

public void doRegister(URL url) { url = addRegistryClusterKey(url); / / register metadata if (writableMetadataService exportURL (url)) {if (logger. IsInfoEnabled ()) {logger. The info (The format (" The url [% s] registered successfully.", url.toString())); } } else { if (logger.isWarnEnabled()) { logger.warn(format("The URL[%s] has been registered.", url.toString())); }}}Copy the code
  • Registration metadata

The core code in InMemoryWritableMetadataService# exportURL, specific as follows:

Public Boolean exportURL(URL URL) {// exporadataservice, If not registered metadata (MetadataService. Class. GetName (). The equals (url) getServiceInterface ())) {enclosing metadataServiceURL = url; return true; } updateLock.readLock().lock(); try { String[] clusters = getRegistryCluster(url).split(","); for (String cluster : clusters) { MetadataInfo metadataInfo = metadataInfos.computeIfAbsent(cluster, k -> new MetadataInfo(ApplicationModel.getName())); // Register ServiceInfo with MetadataInfo metadatainfo.addService (new ServiceInfo(URL)); } metadataSemaphore.release(); return addURL(exportedServiceURLs, url); } finally { updateLock.readLock().unlock(); }}Copy the code
  • An onRegister event is published

The core code is listed in ListenerRegistryWrapper#register as follows:

Public void register(URL URL) {try {// registry is the ServiceDiscoveryRegistry object // The ServiceDiscoveryRegistry#registry method registry.register(URL) has been called; } finally { if (CollectionUtils.isNotEmpty(listeners) && ! UrlUtils.isConsumer(url)) { RuntimeException exception = null; for (RegistryServiceListener listener : listeners) { if (listener ! = null) {try {// The onRegister event listener.onRegister(url, registry) is published after the service-discovery-Registry protocol is registered. } catch (RuntimeException t) { logger.error(t.getMessage(), t); exception = t; } } } if (exception ! = null) { throw exception; }}}}Copy the code
  • Publish service registration events

The core code is in RegistryProtocol#notifyExport as follows:

private <T> void notifyExport(ExporterChangeableWrapper<T> exporter) { List<RegistryProtocolListener> listeners = ExtensionLoader.getExtensionLoader(RegistryProtocolListener.class) .getActivateExtension(exporter.getOriginInvoker().getUrl(), "registry.protocol.listener"); if (CollectionUtils.isNotEmpty(listeners)) { for (RegistryProtocolListener listener : OnExport event Listener. OnExport (this, FRIEND); }}}Copy the code

We can see that the core purpose of registering the service-discovery-Registry protocol is to store information about the service’s interface in memory. For compatibility and smooth migration, the community has adopted the exposed process approach of reusing ServiceConfig for implementation.

Expose the Triple protocol service and register the Registry protocol

Exposing the Triple protocol service and registering the Registry protocol is the core process of Dubbo 3.0 service exposure, which is divided into two parts:

  • Services that expose the Triple protocol
  • Registry protocol

Since the process of exposing Triple protocol service is the same as that of exposing Injvm protocol service, it will not be described here. The process of registering the Registry protocol only registers the information related to the application instance, which is the application level service discovery mechanism mentioned earlier.

Overall sequence diagram

  • Through the InterfaceCompatibleRegistryProtocol Invoker into Exporter

The core code is in ProtocolListenerWrapper#export, as follows:

public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException { // At this time of the protocol for InterfaceCompatibleRegistryProtocol type (inherited RegistryProtocol) / / note: If (urlutils.isregistry (invoker.geturl ())) {return. If (urlutils.isregistry (invoker.geturl ())) {return protocol.export(invoker); }... }Copy the code
  • RegistryProtocol converts Invoker into a core export process

The core code is listed in RegistryProtocol#export as follows:

public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException { URL registryUrl = getRegistryUrl(originInvoker); URL providerUrl = getProviderUrl(originInvoker); . / / once again exposed Triple protocol service final ExporterChangeableWrapper < T > exporter = doLocalExport (originInvoker providerUrl); // Create the ZookeeperRegistry object with the registry protocol // then combine the RegistryServiceListener listener, // Finally wrapped as ListenerRegistryWrapper object // note: The service discovery-Registry protocol corresponds to ServiceDiscoveryRegistry // the registry protocol corresponds to ZookeeperRegistry final Registry registry = getRegistry(registryUrl); final URL registeredProviderUrl = getUrlToRegistry(providerUrl, registryUrl); boolean register = providerUrl.getParameter(REGISTER_KEY, true); If (register) {// Register the onRegister event register(registry, registeredProviderUrl) of the RegistryServiceListener; }... // Publish the onExport event notifyExport(EXPORTER) to RegistryProtocolListener; return new DestroyableExporter<>(exporter); }Copy the code
  • Registry protocol

The core code is in FailbackRegistry#register and ServiceDiscoveryRegistry#doRegister (ZookeeperRegistry inherits FailbackRegistry) as follows:

1, FailbackRegistry# register

public void register(URL url) { if (! acceptable(url)) { ...... Try {// Register the registry protocol doRegister(url); } catch (Exception e) { ...... }}}Copy the code

2, ServiceDiscoveryRegistry# doRegister

Public void doRegister (URL URL) {try {/ / register the Provider / / on the zookeeper directory: / dubbo/xxxService/will / * * * / / data: Dubbo: / / 192.168.31.167:20800 / xxxService? Anyhost = true & / / application = application - name&async = false&deprecated = false&dubbo = 2.0.2 & / / dynamic=true&file.cache=false&generic=false&interface=xxxService& // metadata-type=remote&methods=hello&pid=82470&release=& // service-name-mapping=true&side=provider&timestamp=1629588251493 zkClient.create(toUrlPath(url), url.getParameter(DYNAMIC_KEY, true)); } catch (Throwable e) { throw new RpcException("Failed to register " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e); }}Copy the code
  • Subscription address change

Failbackregistration #subscribe and zookeeperregistration #doSubscribe

1, FailbackRegistry# subscribe

public void subscribe(URL url, NotifyListener listener) { ...... Try {// call ZookeeperRegistry#doSubscribe doSubscribe(url, listener); } catch (Exception e) { ...... }Copy the code

2, ZookeeperRegistry# doSubscribe

public void doSubscribe(final URL url, final NotifyListener listener) { try { if (ANY_VALUE.equals(url.getServiceInterface())) { ...... } else { ...... for (String path : toCategoriesPath(url)) { ConcurrentMap<NotifyListener, ChildListener> listeners = zkListeners.computeIfAbsent(url, k -> new ConcurrentHashMap<>()); ChildListener zkListener = listeners.computeIfAbsent(listener, k -> new RegistryChildListenerImpl(url, path, k, latch)); if (zkListener instanceof RegistryChildListenerImpl) { ((RegistryChildListenerImpl) zkListener).setLatch(latch); Data used to store configurators} / / create a temporary node / / directory: / dubbo/xxxService configurators / / data: The application configuration information can be modified in dubo-admin. By default, zkclient.create (path, false) is empty. / / add a listener to listen to changes in the configurators a List < String >. Children = zkClient addChildListener (path, zkListener); if (children ! = null) { urls.addAll(toUrlsWithEmpty(url, path, children)); }}... } } catch (Throwable e) { ...... }}Copy the code
  • Establish connections between exposed Triple protocol services and Metadata

Core code in ServiceConfig# exportUrl, MetadataUtils# publishServiceDefinition, InMemoryWritableMetadataService# publishServiceDefinition, RemoteMetadataServiceImpl# publishServiceDefinition and MetadataReport# storeProviderMetadata, specific as follows:

1, ServiceConfig# exportUrl

private void exportUrl(URL url, List<URL> registryURLs) { ...... if (! SCOPE_NONE.equalsIgnoreCase(scope)) { ...... if (! SCOPE_LOCAL.equalsIgnoreCase(scope)) { url = exportRemote(url, registryURLs); / / publishing events, update service interface data related MetadataUtils. PublishServiceDefinition (url); }}... }Copy the code

2, MetadataUtils# publishServiceDefinition

Public static void publishServiceDefinition URL (URL) {/ / the service interface related data exist to InMemoryWritableMetadataService WritableMetadataService.getDefaultExtension().publishServiceDefinition(url); If (remote_metadatA_storage_type.equalsignorecase (url.getParameter(METADATA_KEY))) {REMOTE_METADATA_STORAGE_TYPE. getRemoteMetadataService().publishServiceDefinition(url); }}Copy the code

3, InMemoryWritableMetadataService# publishServiceDefinition

public void publishServiceDefinition(URL url) { try { String interfaceName = url.getServiceInterface(); if (StringUtils.isNotEmpty(interfaceName) && ! ProtocolUtils.isGeneric(url.getParameter(GENERIC_KEY))) { Class interfaceClass = Class.forName(interfaceName); ServiceDefinition serviceDefinition = ServiceDefinitionBuilder.build(interfaceClass); Gson gson = new Gson(); String data = gson.toJson(serviceDefinition); {// "canonicalName": "xxxService", // "codeSource": "file:/Users/ XXXX ", // "methods": [{ // "name": "hello", // "parameterTypes": ["java.lang.String"], // "returnType": "java.lang.String", // "annotations": [] // }], // "types": [{ // "type": "java.lang.String" // }], // "annotations": [] // } serviceDefinitions.put(url.getServiceKey(), data); return; } else if (CONSUMER_SIDE.equalsIgnoreCase(url.getParameter(SIDE_KEY))) { ...... }... } catch (Throwable e) { ...... }}Copy the code

4, RemoteMetadataServiceImpl# publishServiceDefinition

public void publishServiceDefinition(URL url) { checkRemoteConfigured(); String side = url.getSide(); If (provider_side.equalsignorecase (side)) {// Publish the service interface information of the server (Provider) to the metadata center publishProvider(URL); } else { ...... } } RemoteMetadataServiceImpl#publishProvider private void publishProvider(URL providerUrl) throws RpcException { ......  try { String interfaceName = providerUrl.getServiceInterface(); if (StringUtils.isNotEmpty(interfaceName)) { ...... for (Map.Entry<String, MetadataReport> entry : GetMetadataReports ().entrySet()) {// Get the MetadataReport service, which is used to access the metadata center MetadataReport MetadataReport = entry.getValue(); / / the service interface information stored in the metadata center metadataReport. StoreProviderMetadata (new MetadataIdentifier (providerUrl. GetServiceInterface (), providerUrl.getVersion(), providerUrl.getGroup(), PROVIDER_SIDE, providerUrl.getApplication()), fullServiceDefinition); } return; }... } catch (ClassNotFoundException e) { ...... }}Copy the code

5, AbstractMetadataReport# storeProviderMetadata

public void storeProviderMetadata(MetadataIdentifier providerMetadataIdentifier, ServiceDefinition serviceDefinition){ if (syncReport) { storeProviderMetadataTask(providerMetadataIdentifier, serviceDefinition); } else {/ / asynchronous storage into the center of the metadata reportCacheExecutor execute (() - > storeProviderMetadataTask (providerMetadataIdentifier, serviceDefinition)); } } private void storeProviderMetadataTask(MetadataIdentifier providerMetadataIdentifier, ServiceDefinition serviceDefinition) { try { ...... allMetadataReports.put(providerMetadataIdentifier, serviceDefinition); failedReports.remove(providerMetadataIdentifier); Gson gson = new Gson(); // "parameters": {// "side": "provider", // "interface": "xxxService", // "metadata-type": "remote", // "service-name-mapping": "true", // }, // "canonicalName": "xxxService", // "codeSource": "file:/Users/xxxx", // "methods": [{ // "name": "hello", // "parameterTypes": ["java.lang.String"], // "returnType": "java.lang.String", // "annotations": [] // }], // "types": [{ // "type": "java.lang.String" // }], // "annotations": [] // } String data = gson.toJson(serviceDefinition); // The metadata center in this example is ZookeeperMetadataReport. Meta data center Metadata - report/dubbo/Metadata/xxxService/provider / ${application - the name} node doStoreProviderMetadata(providerMetadataIdentifier, data); / / stored in the local file: / / path xxxService: : : the provider: ${application - the name} saveProperties (providerMetadataIdentifier, data, true! syncReport); } catch (Exception e) { ...... }}Copy the code
  • Establish the relationship between Triple protocol service and MetadataReport service

The core code in ServiceConfig# exported, MetadataServiceNameMapping# map and ZookeeperMetadataReport# registerServiceAppMapping, specific as follows:

1, ServiceConfig# exported

protected void exported() { exported = true; List<URL> exportedURLs = this.getExportedUrls(); Exportedurls. forEach(URL -> {// Determine whether service-name-mapping fields are marked in the URL // Services with this field are to be associated with metadata centers // If (url.getParameters().containsKey(SERVICE_NAME_MAPPING_KEY)) {if (url.getParameters().containsKey) { ServiceNameMapping serviceNameMapping = ServiceNameMapping.getDefaultExtension(); // Establish the relationship serviceNameMapping. Map (url); }}); onExported(); }Copy the code

2, MetadataServiceNameMapping# map

public void map(URL url) { execute(() -> { String registryCluster = getRegistryCluster(url); // Get MetadataReport, Which is the center of the metadata access path MetadataReport MetadataReport = MetadataReportInstance. GetMetadataReport (registryCluster); . int currentRetryTimes = 1; boolean success; String newConfigContent = getName(); Do {/ / center for metadata stored in the application of the version information ConfigItem ConfigItem = metadataReport. GetConfigItem (serviceInterface, DEFAULT_MAPPING_GROUP); String oldConfigContent = configItem.getContent(); if (StringUtils.isNotEmpty(oldConfigContent)) { boolean contains = StringUtils.isContains(oldConfigContent, getName()); if (contains) { break; } newConfigContent = oldConfigContent + COMMA_SEPARATOR + getName(); } / / to create the mapping node in the center of the metadata, and will expose the service data into the center of metadata, metadata center here with a zookeeper implementation / / directory: / dubbo/mapping/xxxService / / data: ConfigItem. The content is ${application - the name}, ConfigItem. Ticket for version good success = metadataReport. RegisterServiceAppMapping (serviceInterface DEFAULT_MAPPING_GROUP, newConfigContent, configItem.getTicket()); } while (! success && currentRetryTimes++ <= CAS_RETRY_TIMES); }); }Copy the code

3, ZookeeperMetadataReport# registerServiceAppMapping

public boolean registerServiceAppMapping(String key, String group, String content, Object ticket) { try { if (ticket ! = null && ! (ticket instanceof Stat)) { throw new IllegalArgumentException("zookeeper publishConfigCas requires stat type ticket"); } String pathKey = buildPathKey(group, key); / / 1. Create/dubbo/mapping/xxxService directory, the data stored for configItem / / 2. CreateOrUpdate (pathKey, Content, false, ticket == null? 0 : ((Stat) ticket).getVersion()); return true; } catch (Exception e) { logger.warn("zookeeper publishConfigCas failed.", e); return false; }}Copy the code

At this point, the process of exposing the Triple protocol service and registering the Registry protocol ends. In the previous interface level service discovery mechanism, the data registered in the registry (application instance data + service interface data) was split out. The registry protocol part registers the application instance data to the registry and registers the service interface data to the metadata center by calling MetadataUtils#publishServiceDefinition after the Exporter is exposed.

4. Expose MetadataService

MetadataService mainly provides an API for the Consumer side to obtain metadata, and the exposure process is the service exposure process using Triple protocol

Overall sequence diagram

  • The entrance to expose MetadataService

DubboBootstrap#exportMetadataService

Private void exportMetadataService () {/ / exposed MetadataServer metadataServiceExporter. The export (); }Copy the code
  • Exposed MetadataService

The core code in ConfigurableMetadataServiceExporter# export, specific as follows:

public ConfigurableMetadataServiceExporter export() { if (! IsExported ()) {// Define ServiceConfig<MetadataService> ServiceConfig = new ServiceConfig<>();  serviceConfig.setApplication(getApplicationConfig()); / / not registered with the registry serviceConfig. SetRegistry (new RegistryConfig (" N/A ")); serviceConfig.setProtocol(generateMetadataProtocol()); serviceConfig.setInterface(MetadataService.class); serviceConfig.setDelay(0); serviceConfig.setRef(metadataService); serviceConfig.setGroup(getApplicationConfig().getName()); serviceConfig.setVersion(metadataService.version()); serviceConfig.setMethods(generateMethodConfig()); // Use the Dubbo protocol serviceConfig. Export (); this.serviceConfig = serviceConfig; } return this; }Copy the code

Since the process of exposing MetadataService is to reuse the previously mentioned process of exposing the Triple protocol service, the whole process will be slightly different, and these differences are indicated in the above code, so we will not repeat them.

  • Register a ServiceInstance instance

The purpose of registering ServiceInstance is to update Metadata periodically. When Metadata is updated, the MetadataReport updates the version number to let the Consumer know.

The core code in DubboBootstrap# registerServiceInstance and DubboBootstrap# doRegisterServiceInstance, specific as follows:

private void registerServiceInstance() { .... // Create ServiceInstance // ServiceInstance contains the following fields // 1. serviceName: ${application-name} // 2. host: 192.168.31.167 // 3. port: 2080 // 4. metadata: Service interface-level data, such as: // Add the fields in ServiceInstance data, Invoke the following four ServiceInstanceCustomizer instance ServiceInstanceMetadataCustomizer / / / / 1) 2) MetadataServiceURLParamsMetadataCustomizer ServiceInstanceHostPortCustomizer ProtocolPortsMetadataCustomizer / / / / 3) 4)  ServiceInstance serviceInstance = createServiceInstance(serviceName); boolean registered = true; Try {/ / registered ServiceInstance doRegisterServiceInstance (ServiceInstance); } catch (Exception e) { registered = false; logger.error("Register instance error", e); } // If the registration is successful, periodically update Metadata, Updated once if not 10 s (registered) {executorRepository. NextScheduledExecutor () scheduleAtFixedRate (() - > {... Try {/ / refresh the Metadata and the ServiceInstance ServiceInstanceMetadataUtils. RefreshMetadataAndInstance (ServiceInstance); } catch (Exception e) { ...... } finally { ...... } }, 0, ConfigurationUtils.get(METADATA_PUBLISH_DELAY_KEY, DEFAULT_METADATA_PUBLISH_DELAY), TimeUnit.MILLISECONDS); }}Copy the code

DubboBootstrap#doRegisterServiceInstance

private void doRegisterServiceInstance(ServiceInstance serviceInstance) { if (serviceInstance.getPort() > 0) { // Release the Metadata data to the remote center store Metadata / / call RemoteMetadataServiceImpl# publishMetadata, MetadataReport# publishAppMetadata publishMetadataToRemote(serviceInstance); logger.info("Start registering instance address to registry."); getServiceDiscoveries().forEach(serviceDiscovery ->{ ServiceInstance serviceInstanceForRegistry = new DefaultServiceInstance((DefaultServiceInstance) serviceInstance); calInstanceRevision(serviceDiscovery, serviceInstanceForRegistry); . / / call ZookeeperServiceDiscovery# doRegister registered serviceInstance instance / / the application service information to the registry / / directory: // ${application-name}/192.168.31.167:20800 The serviceInstance serialized byte array serviceDiscovery. Register (serviceInstanceForRegistry); }); }}Copy the code

From the above analysis, we can easily know

  • ServiceInstance contains Metadata in
  • The Metadata is stored in the InMemoryWritableMetadataService Metadata, occupy the local memory space
  • InMemoryWritableMetadataService used to update the Metadata
  • ServiceInstance is a data structure stored in a remote metadata registry
  • RemoteMetadataServiceImpl invokes metadataReport ServiceInstance data update to the distal metadata registry

conclusion

The analysis of the whole process of Dubbo 3.0 server exposure shows that although the implementation of application-level service discovery mechanism is much more complex, Dubbo 3.0 is compatible with version 2.7.x in order to smooth the migration of users, so in many places in the design of the previous process as much as possible.

Dubbo 3.0’s recent release of Benchmark data shows a significant improvement in performance and resource utilization. Dubbo 3.0 still has a long way to go to embrace cloud native. The community is working on the core process of Dubbo 3.0, and plans to support multi-instance application deployment. We hope that those who are interested in witnessing the cloud native path can actively participate in the community contribution!

The original link

This article is the original content of Aliyun and shall not be reproduced without permission.