44 Metadata Solution In-Depth Analysis How to Avoid Registration Center Data Bloat #
In the previous lesson, we discussed in detail the challenges faced by the traditional architecture of Dubbo and how the service introspection scheme introduced in Dubbo 2.7.5 addresses these challenges.
In this lesson, we will start by introducing the infrastructure of the service introspection scheme and its specific implementation. We will first introduce the definition of the basic classes related to metadata, and then discuss metadata reporting and related content of the metadata service. We will also explain how the Service ID is mapped to the Service Name.
ServiceInstance #
The Service Instance uniquely identifies a service instance and corresponds to the ServiceInstance interface in Dubbo’s source code. The specific definition of this interface is as follows:
public interface ServiceInstance extends Serializable {
// Unique identifier
String getId();
// Get the Service Name that the current ServiceInstance belongs to
String getServiceName();
// Get the host of the current ServiceInstance
String getHost();
// Get the port of the current ServiceInstance
Integer getPort();
// Current ServiceInstance's status
default boolean isEnabled() {
return true;
}
// Check the status of the current ServiceInstance
default boolean isHealthy() {
return true;
}
// Get the metadata associated with the current ServiceInstance, which is stored in key-value format
Map<String, String> getMetadata();
// Calculate the hashCode value of the current ServiceInstance object
int hashCode();
// Compare two ServiceInstance objects
boolean equals(Object another);
}
The DefaultServiceInstance is the only implementation of ServiceInstance. DefaultServiceInstance is a plain old Java object (POJO) class, with the following key fields:
- id (String type): Unique identifier of the ServiceInstance.
- serviceName (String type): Service Name associated with the ServiceInstance.
- host (String type): host of the ServiceInstance.
- port (Integer type): port of the ServiceInstance.
- enabled (boolean type): status of whether the ServiceInstance is available.
- healthy (boolean type): health status of the ServiceInstance.
- metadata (Map type): metadata associated with the ServiceInstance.
ServiceDefinition #
Dubbo’s metadata service is no different from the Dubbo services we publish in our business. The Consumer can call a service instance’s metadata service to retrieve the metadata of all the services it publishes.
When it comes to metadata, we have to mention the ServiceDefinition class, which is used to describe the definition of a service interface. Its key fields are as follows:
- canonicalName (String type): Fully qualified name of the interface.
- codeSource (String type): Complete path where the service interface is located.
- methods (List type): Detailed information about all the methods defined in the interface. MethodDefinition records the name of the method, parameter types, return value type, and all TypeDefinition involved in the method’s parameters.
- types (List type): Detailed information about all the types involved in the interface definition, including parameters and fields of methods. If a complex type is encountered, TypeDefinition will recursively retrieve the fields inside the complex type. The dubbo-metadata-api module provides various TypeBuilder implementations corresponding to different types to create the corresponding TypeDefinition. For types without a specific TypeBuilder implementation, DefaultTypeBuilder is used.
Diagram of TypeBuilder interface implementation relationships
When a service is published, some of the data in the service URL is encapsulated into a FullServiceDefinition object and stored as metadata. FullServiceDefinition extends ServiceDefinition and extends the basic fields of ServiceDefinition with a params collection (Map type) for storing parameters from the URL.
MetadataService #
Next, let’s look at the MetadataService interface. As mentioned in the previous lesson, each ServiceInstance in Dubbo publishes a MetadataService interface for Consumer to query metadata. The following diagram shows the inheritance relationships of the MetadataService interface:
Diagram of MetadataService interface inheritance relationships
The MetadataService interface defines methods for querying the metadata published by the current ServiceInstance, as shown below:
public interface MetadataService {
String serviceName(); // Get the name of the service to which the current ServiceInstance belongs
default String version() {
return VERSION; // Get the version of the current MetadataService interface
}
// Get all subscribed URLs of the current ServiceInstance
default SortedSet<String> getSubscribedURLs(){
throw new UnsupportedOperationException("This operation is not supported for consumer.");
}
// Get all exported URLs of the current ServiceInstance
default SortedSet<String> getExportedURLs() {
return getExportedURLs(ALL_SERVICE_INTERFACES);
}
// Get all exported URLs of the current ServiceInstance based on the service interface
default SortedSet<String> getExportedURLs(String serviceInterface) {
return getExportedURLs(serviceInterface, null);
public void publishServiceDefinition(URL providerUrl) {
// Get the service interface
String interfaceName = providerUrl.getParameter(INTERFACE_KEY);
if (StringUtils.isNotEmpty(interfaceName)
&& !ProtocolUtils.isGeneric(providerUrl.getParameter(GENERIC_KEY))) {
Class interfaceClass = Class.forName(interfaceName);
// Create a ServiceDefinition object for the service interface
ServiceDefinition serviceDefinition = ServiceDefinitionBuilder.build(interfaceClass);
Gson gson = new Gson();
// Serialize the ServiceDefinition object to a JSON string
String data = gson.toJson(serviceDefinition);
// Record the serialized ServiceDefinition JSON string to the serviceDefinitions collection
serviceDefinitions.put(providerUrl.getServiceKey(), data);
}
}
return;
}
}
In the implementation of RemoteWritableMetadataService, an InMemoryWritableMetadataService object is encapsulated and the publishServiceDefinition() method is overridden, the specific implementation is as follows:
public void publishServiceDefinition(URL url) {
// Get the value of the 'side' parameter in the URL, which determines whether to call the publishProvider() or publishConsumer() method
String side = url.getParameter(SIDE_KEY);
if (PROVIDER_SIDE.equalsIgnoreCase(side)) {
publishProvider(url);
} else {
publishConsumer(url);
}
}
In the publishProvider() method, first, a FullServiceDefinition object is created based on the Provider URL, and then it is reported through the MetadataReport. The specific implementation is as follows:
private void publishProvider(URL providerUrl) throws RpcException {
// Remove parameters such as pid, timestamp, bind.ip, bind.port, etc.
providerUrl = providerUrl.removeParameters(PID_KEY, TIMESTAMP_KEY, Constants.BIND_IP_KEY, Constants.BIND_PORT_KEY, TIMESTAMP_KEY);
// Get the service interface name
String interfaceName = providerUrl.getParameter(INTERFACE_KEY);
if (StringUtils.isNotEmpty(interfaceName)) {
Class interfaceClass = Class.forName(interfaceName); // Reflection
// Create a FullServiceDefinition object corresponding to the service interface, the parameters in the URL will be recorded in the params collection of FullServiceDefinition
FullServiceDefinition fullServiceDefinition = ServiceDefinitionBuilder.buildFullDefinition(interfaceClass, providerUrl.getParameters());
// Get MetadataReport and report FullServiceDefinition
getMetadataReport().storeProviderMetadata(new MetadataIdentifier(providerUrl.getServiceInterface(), providerUrl.getParameter(VERSION_KEY), providerUrl.getParameter(GROUP_KEY), PROVIDER_SIDE, providerUrl.getParameter(APPLICATION_KEY)), fullServiceDefinition);
return;
}
}
The publishConsumer() method is relatively simple: it first cleans up parameters such as pid, timestamp in the Consumer URL, and then reports the parameter collection in the Consumer URL.
However, methods such as exportURL(), subscribeURL(), getExportedURLs(), getServiceDefinition() in RemoteWritableMetadataService are empty implementations. So why is that? In fact, we can find the answer from RemoteWritableMetadataServiceDelegate, Note that RemoteWritableMetadataServiceDelegate is the remote extension implementation of the MetadataService interface.
In RemoteWritableMetadataServiceDelegate, both an InMemoryWritableMetadataService object and a RemoteWritableMetadataService object are maintained. Methods related to publish and subscribe, such as exportURL() and subscribeURL(), are delegated to both of these MetadataService objects, while methods for querying, such as getExportedURLs() and getServiceDefinition(), only call the InMemoryWritableMetadataService object for querying. Let’s take exportURL() method as an example to explain:
public boolean exportURL(URL url) {
return doFunction(WritableMetadataService::exportURL, url);
}
private boolean doFunction(BiFunction<WritableMetadataService, URL, Boolean> func, URL url) {
// Call the exportURL() method of both InMemoryWritableMetadataService and RemoteWritableMetadataService objects at the same time
return func.apply(defaultWritableMetadataService, url) && func.apply(remoteWritableMetadataService, url);
}
MetadataReport #
The metadata center is an optimization introduced in Dubbo 2.7.0 version. Its main purpose is to store part of the content in the URL to the metadata center, reducing the pressure on the registry.
The data in the metadata center is only used locally and does not need to be notified to the remote end when changed. For example, when the Provider modifies the metadata, there is no need to notify the Consumer in real time. In this way, while reducing the amount of data stored in the registry, it also reduces the occurrence of frequent notification to listeners due to configuration changes, effectively reducing the pressure on the registry.
The MetadataReport interface is the bridge for interaction between Dubbo nodes and the metadata center. Here is the inheritance relationship of MetadataReport:
MetadataReport inheritance relationship diagram
Let’s first take a look at the core definition of the MetadataReport interface:
public interface MetadataReport {
// Store Provider metadata
void storeProviderMetadata(MetadataIdentifier providerMetadataIdentifier, ServiceDefinition serviceDefinition);
// Store Consumer metadata
void storeConsumerMetadata(MetadataIdentifier consumerMetadataIdentifier, Map<String, String> serviceParameterMap);
// Store and remove Service metadata
void saveServiceMetadata(ServiceMetadataIdentifier metadataIdentifier, URL url);
void removeServiceMetadata(ServiceMetadataIdentifier metadataIdentifier);
// Query exported URLs
List<String> getExportedURLs(ServiceMetadataIdentifier metadataIdentifier);
// Query subscribed data
void saveSubscribedData(SubscriberMetadataIdentifier subscriberMetadataIdentifier, Set<String> urls);
List<String> getSubscribedURLs(SubscriberMetadataIdentifier subscriberMetadataIdentifier);
// Query ServiceDefinition
String getServiceDefinition(MetadataIdentifier metadataIdentifier);
}
After understanding the core behavior defined in the MetadataReport interface, next we will introduce the implementation in the order of its implementation: first analyze the common implementation provided by the AbstractMetadataReport abstract class, and then take the specific implementation of ZookeeperMetadataReport as an example to introduce how MetadataReport cooperates with ZooKeeper to achieve metadata reporting.
1. AbstractMetadataReport #
AbstractMetadataReport provides the common implementation for all MetadataReports, and its core fields are as follows:
private URL reportURL; // URL of the metadata center, which includes the address of the metadata center
// Local disk cache used to store the reported metadata
File file;
final Properties properties = new Properties();
// In-memory cache
final Map<MetadataIdentifier, Object> allMetadataReports = new ConcurrentHashMap<>(4);
// This thread pool is used for synchronizing local in-memory cache with file cache, and also for asynchronous reporting
private final ExecutorService reportCacheExecutor = Executors.newFixedThreadPool(1, new NamedThreadFactory("DubboSaveMetadataReport", true));
// Used to temporarily store the failed reports, which will be retried later
final Map<MetadataIdentifier, Object> failedReports = new ConcurrentHashMap<>(4);
boolean syncReport; // Whether to report metadata synchronously
// Records the version of the most recent metadata report, increasing monotonically
private final AtomicLong lastCacheChanged = new AtomicLong();
// Retry task for retrying failed reports
public MetadataReportRetry metadataReportRetry;
// Whether the current MetadataReport instance has been initialized
private AtomicBoolean initialized = new AtomicBoolean(false);
In the constructor of AbstractMetadataReport, it first initializes the local file cache, creates a MetadataReportRetry retry task, and starts a periodic refresh scheduled task. The specific implementation is as follows:
public AbstractMetadataReport(URL reportServerURL) {
setUrl(reportServerURL);
// Default local file cache
String defaultFilename = System.getProperty("user.home") + "/.dubbo/dubbo-metadata-" + reportServerURL.getParameter(APPLICATION_KEY) + "-" + reportServerURL.getAddress().replaceAll(":", "-") + ".cache";
String filename = reportServerURL.getParameter(FILE_KEY, defaultFilename);
File file = null;
if (ConfigUtils.isNotEmpty(filename)) {
file = new File(filename);
if (!file.exists() && file.getParentFile() != null && !file.getParentFile().exists()) {
if (!file.getParentFile().mkdirs()) {
throw new IllegalArgumentException("...");
}
}
// Delete the file if it exists and hasn't been initialized
if (!initialized.getAndSet(true) && file.exists()) {
file.delete();
}
}
this.file = file;
// Load the contents of the file into the properties field
loadProperties();
// Whether to report metadata synchronously
syncReport = reportServerURL.getParameter(SYNC_REPORT_KEY, false);
// Create the retry task
metadataReportRetry = new MetadataReportRetry(reportServerURL.getParameter(RETRY_TIMES_KEY, DEFAULT_METADATA_REPORT_RETRY_TIMES),
reportServerURL.getParameter(RETRY_PERIOD_KEY, DEFAULT_METADATA_REPORT_RETRY_PERIOD));
// Whether to report metadata periodically
if (reportServerURL.getParameter(CYCLE_REPORT_KEY, DEFAULT_METADATA_REPORT_CYCLE_REPORT)) {
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("DubboMetadataReportTimer", true));
// By default, all local metadata will be refreshed to the metadata center every day
scheduler.scheduleAtFixedRate(this::publishAll, calculateStartTime(), ONE_DAY_IN_MILLISECONDS, TimeUnit.MILLISECONDS);
}
}
In the AbstractMetadataReport.storeProviderMetadata() method, it first determines whether to report synchronously or asynchronously based on the value of the syncReport field: if it is synchronous, the report operation will be executed in the current thread; if it is asynchronous, the report operation will be executed in the reportCacheExecutor thread pool. The specific report operation is completed in the storeProviderMetadataTask() method:
private void storeProviderMetadataTask(MetadataIdentifier providerMetadataIdentifier, ServiceDefinition serviceDefinition) {
try {
// Store the metadata in the allMetadataReports collection
allMetadataReports.put(providerMetadataIdentifier, serviceDefinition);
// If it was reported failed before, there will be a record in the failedReports collection,
// and it will be removed after a successful report
failedReports.remove(providerMetadataIdentifier);
// Serialize the metadata into a JSON string
Gson gson = new Gson();
String data = gson.toJson(serviceDefinition);
// Report the serialized metadata doStoreProviderMetadata(providerMetadataIdentifier, data);
// Save the serialized metadata to the local file cache saveProperties(providerMetadataIdentifier, data, true, !syncReport);
} catch (Exception e) {
// If the report fails, record it in the failedReports collection and retry it in the metadataReportRetry task failedReports.put(providerMetadataIdentifier, serviceDefinition);
metadataReportRetry.startRetryTask();
}
We can see that the doStoreProviderMetadata() method and saveProperties() method are called here. The doStoreProviderMetadata() method is an abstract method with different implementations for different metadata center implementations. The specific implementation of this method will be analyzed later. In the saveProperties() method, the properties field is updated, the version of the local cache file is incremented, and the SaveProperties task is (synchronously/asynchronously) executed to update the content of the local cache file. The specific implementation is as follows:
private void saveProperties(MetadataIdentifier metadataIdentifier, String value, boolean add, boolean sync) {
if (file == null) {
return;
}
if (add) { // Update the metadata in the properties
properties.setProperty(metadataIdentifier.getUniqueKey(KeyTypeEnum.UNIQUE_KEY), value);
} else {
properties.remove(metadataIdentifier.getUniqueKey(KeyTypeEnum.UNIQUE_KEY));
}
// Increment the version
long version = lastCacheChanged.incrementAndGet();
if (sync) { // Synchronously update the local cache file
new SaveProperties(version).run();
} else { // Asynchronously update the local cache file
reportCacheExecutor.execute(new SaveProperties(version));
}
}
Now let’s take a look at the core method of the SaveProperties task, the doSaveProperties() method, which implements all operations to refresh the local cache file.
private void doSaveProperties(long version) {
if (version < lastCacheChanged.get()) { // Compare the current version number and the version number of this SaveProperties task
return;
}
if (file == null) { // Check if the local cache file exists
return;
}
try {
// Create a lock file
File lockfile = new File(file.getAbsolutePath() + “.lock”);
if (!lockfile.exists()) {
lockfile.createNewFile();
}
try (RandomAccessFile raf = new RandomAccessFile(lockfile, “rw”);
FileChannel channel = raf.getChannel()) {
FileLock lock = channel.tryLock(); // Lock the lock file
if (lock == null) {
throw new IOException(“Can not lock the metadataReport cache file " + file.getAbsolutePath() + “, ignore and retry later, maybe multi java process use the file, please config: dubbo.metadata.file=xxx.properties”);
}
try {
if (!file.exists()) { // Ensure that the local cache file exists
file.createNewFile();
}
// Save the metadata in the properties to the local cache file
try (FileOutputStream outputFile = new FileOutputStream(file)) {
properties.store(outputFile, “Dubbo metadataReport Cache”);
}
} finally {
lock.release(); // Release the lock on the lock file
}
}
} catch (Throwable e) {
if (version < lastCacheChanged.get()) { // Compare the version number
return;
} else { // If writing the file fails, resubmit the SaveProperties task and try again reportCacheExecutor.execute(new SaveProperties(lastCacheChanged.incrementAndGet()));
}
}
}
After understanding the core logic of refreshing the local cache file, let’s take a look at the logic of retrying on failure in AbstractMetadataReport. MetadataReportRetry maintains the following core fields:
// Thread pool for executing retry tasks
ScheduledExecutorService retryExecutor = Executors.newScheduledThreadPool(0, new NamedThreadFactory("DubboMetadataReportRetryTimer", true));
// Future object associated with the retry task
volatile ScheduledFuture retryScheduledFuture;
// Counter for tracking the number of retry tasks
final AtomicInteger retryCounter = new AtomicInteger(0);
// Interval between retry tasks
long retryPeriod;
// After no failed metadata report, the retry task will be executed 600 times before being destroyed
int retryTimesIfNonFail = 600;
// Maximum number of retry attempts, defaults to 100, i.e. give up after retrying 100 times
int retryLimit;
In the startRetryTask() method, MetadataReportRetry creates a retry task and submits it to the retryExecutor thread pool for execution (if a retry task already exists, a new task will not be created). The retry task will call the AbstractMetadataReport.retry() method to complete the re-reporting. It also checks the execution conditions such as retryLimit. The implementation is relatively simple, so I won’t show it here. If you are interested, you can refer to the source code for learning.
The specific implementation of the retry() method in AbstractMetadataReport is as follows:
public boolean retry() {
return doHandleMetadataCollection(failedReports);
}
private boolean doHandleMetadataCollection(Map<MetadataIdentifier, Object> metadataMap) {
if (metadataMap.isEmpty()) { // No failed metadata reports
return true;
}
// Loop through the failedReports map and call storeProviderMetadata() method or storeConsumerMetadata() method to re-report metadata one by one
Iterator<Map.Entry<MetadataIdentifier, Object>> iterable = metadataMap.entrySet().iterator();
while (iterable.hasNext()) {
Map.Entry<MetadataIdentifier, Object> item = iterable.next();
if (PROVIDER_SIDE.equals(item.getKey().getSide())) {
this.storeProviderMetadata(item.getKey(), (FullServiceDefinition) item.getValue());
} else if (CONSUMER_SIDE.equals(item.getKey().getSide())) {
this.storeConsumerMetadata(item.getKey(), (Map) item.getValue());
}
}
return false;
}
In the constructor of AbstractMetadataReport, a “daily” level scheduled task is started based on the reportServerURL (which is the metadataReportURL later), which executes the publishAll() method. In this method, all metadata in the allMetadataReports collection is re-reported through the doHandleMetadataCollection() method. By default, this scheduled task starts at 02:00~06:00 in the morning and is executed once a day.
That’s all for the common capabilities implemented by AbstractMetadataReport for its subclasses. Other methods are delegated to the corresponding do_() methods, which are implemented in the AbstractMetadataReport subclasses.
2. BaseMetadataIdentifier #
When reporting metadata on AbstractMetadataReport, the key for the metadata is an object of type BaseMetadataIdentifier. The inheritance relationship is shown in the following diagram:
- MetadataIdentifier contains five core fields: service interface, version, group, side, and application.
- ServiceMetadataIdentifier contains six core fields: service interface, version, group, side, revision, and protocol.
- SubscriberMetadataIdentifier contains five core fields: service interface, version, group, side, and revision.
3. MetadataReportFactory & MetadataReportInstance #
MetadataReportFactory is a factory for creating instances of MetadataReport. The definition is as follows:
@SPI("redis")
public interface MetadataReportFactory {
@Adaptive({"protocol"})
MetadataReport getMetadataReport(URL url);
}
MetadataReportFactory is an extension interface, and from the default value of the @SPI annotation, it can be seen that Dubbo uses Redis as the default implementation of the metadata center. Dubbo provides MetadataReportFactory implementations for ZooKeeper, Redis, Consul, and other metadata centers, as shown in the following diagram:
These MetadataReportFactory implementations all inherit from AbstractMetadataReportFactory. In AbstractMetadataReportFactory, it provides the ability to cache MetadataReport implementations and defines an abstract method createMetadataReport() for subclasses to implement. In addition, AbstractMetadataReportFactory implements the getMetadataReport() method of the MetadataReportFactory interface. Let’s briefly look at the implementation of this method:
public MetadataReport getMetadataReport(URL url) {
// Remove export and refer parameters
url = url.setPath(MetadataReport.class.getName())
.removeParameters(EXPORT_KEY, REFER_KEY);
ServiceConfig<MetadataService> serviceConfig = new ServiceConfig<>();
// 设置协议,这里默认使用dubbo协议
serviceConfig.setProtocol(metadataProtocol);
// 设置接口
serviceConfig.setInterface(MetadataService.class);
// 设置实现类
serviceConfig.setRef(metadataService);
// 设置服务提供者信息
serviceConfig.setApplication(applicationModel);
serviceConfig.setRegistry(applicationModel.getApplicationConfig().getRegistry());
// 暴露服务
serviceConfig.export();
// 将已经发布的URL添加到已导出的URL列表中
exportedURLs.add(serviceConfig.getExportedUrls().get(0));
}
return this;
ServiceConfig<MetadataService> serviceConfig = new ServiceConfig<>();
serviceConfig.setApplication(getApplicationConfig());
serviceConfig.setRegistries(getRegistries());
serviceConfig.setProtocol(generateMetadataProtocol()); // Set the Protocol (default is Dubbo)
serviceConfig.setInterface(MetadataService.class); // Set the service interface
serviceConfig.setRef(metadataService); // Set the MetadataService object
serviceConfig.setGroup(getApplicationConfig().getName()); // Set group
serviceConfig.setVersion(metadataService.version()); // Set version
// Publish the MetadataService service, the process of publishing a service by ServiceConfig has been analyzed in detail earlier, so it is not repeated here
serviceConfig.export();
this.serviceConfig = serviceConfig;
} else {
... // Log output
}
return this;
}
ServiceNameMapping #
The ServiceNameMapping interface is mainly used to implement the conversion between Service ID and Service Name. It depends on the configuration center to store and query data. The definition of the ServiceNameMapping interface is as follows:
@SPI("default")
public interface ServiceNameMapping {
// The Service ID is composed of the service interface, group, version, and protocol,
// and it is mapped to the current Service Name and recorded in the configuration center
void map(String serviceInterface, String group, String version, String protocol);
// Query the corresponding Service Name based on the Service ID composed of the service interface, group, version, and protocol
Set<String> get(String serviceInterface, String group, String version, String protocol);
// Get the default extension implementation of the ServiceNameMapping interface
static ServiceNameMapping getDefaultExtension() {
return getExtensionLoader(ServiceNameMapping.class).getDefaultExtension();
}
}
DynamicConfigurationServiceNameMapping is the default implementation of ServiceNameMapping, and it is also the only implementation. It depends on DynamicConfiguration to read and write the configuration center to complete the mapping between Service ID and Service Name. First, let’s take a look at the map() method of DynamicConfigurationServiceNameMapping:
public void map(String serviceInterface, String group, String version, String protocol) {
// Skip the processing of the MetadataService interface
if (IGNORED_SERVICE_INTERFACES.contains(serviceInterface)) {
return;
}
// Get the DynamicConfiguration object
DynamicConfiguration dynamicConfiguration = DynamicConfiguration.getDynamicConfiguration();
// Get the Service Name from the ApplicationModel
String key = getName();
String content = valueOf(System.currentTimeMillis());
execute(() -> {
// Create the mapping relationship in the configuration center
// Although the buildGroup() method accepts four parameters, only the serviceInterface is used here,
// so it creates a mapping from the service interface to the Service Name.
// You can temporarily understand the configuration center as a KV storage. The key is composed of the return value of the buildGroup() method and the Service Name, and the value is the content (that is, the timestamp)
dynamicConfiguration.publishConfig(key, buildGroup(serviceInterface, group, version, protocol), content);
});
}
In the DynamicConfigurationServiceNameMapping.get() method, it will search for the corresponding Service Name based on the service interface, group, version, and protocol, which form the Service ID. The code is shown below:
public Set<String> get(String serviceInterface, String group, String version, String protocol) {
// Get the DynamicConfiguration object
DynamicConfiguration dynamicConfiguration = DynamicConfiguration.getDynamicConfiguration();
// Query the Service Name from the configuration based on the Service ID
Set<String> serviceNames = new LinkedHashSet<>();
execute(() -> {
Set<String> keys = dynamicConfiguration.getConfigKeys(buildGroup(serviceInterface, group, version, protocol));
serviceNames.addAll(keys);
});
// Return all the Service Names found
return Collections.unmodifiableSet(serviceNames);
}
Summary #
In this lesson, we have focused on the implementation of metadata in the service introspection architecture.
- First, we introduced how ServiceInstance and ServiceDefinition abstract a service instance and service definition.
- Then we explained the definition of the metadata service interface, which is MetadataService interface and its core extension implementation.
- Next, we analyzed the implementation of the MetadataReport interface and how it works with the metadata center to achieve metadata reporting.
- Then we explained the implementation of the MetadataServiceExporter and the core principles of publishing metadata services.
- Finally, we introduced the ServiceNameMapping interface and its default implementation. It realizes the mapping between Service ID and Service Name, which is an indispensable part of the service introspection architecture.