You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dubbo.apache.org by al...@apache.org on 2023/07/31 05:44:52 UTC

[dubbo] branch 3.2 updated: Support multi registries metrics key (#12582)

This is an automated email from the ASF dual-hosted git repository.

albumenj pushed a commit to branch 3.2
in repository https://gitbox.apache.org/repos/asf/dubbo.git


The following commit(s) were added to refs/heads/3.2 by this push:
     new 0add70e3f5 Support multi registries metrics key (#12582)
0add70e3f5 is described below

commit 0add70e3f5a530766bfacf65392190afe3fd2a85
Author: wxbty <38...@users.noreply.github.com>
AuthorDate: Mon Jul 31 13:44:46 2023 +0800

    Support multi registries metrics key (#12582)
    
    * init
    
    * fix
    
    * fix
    
    * fix
    
    * fix
    
    * add licence
    
    * fix testcase
    
    * fix testcase
    
    * lowcase
    
    * remove unuse
    
    * opt notify&directory
    
    * fix post&finish
    
    * revert name
    
    * move registerMetadataAndInstance
    
    * move metrics from
    
    * remove some unuse
    
    * remove unuse
    
    * fix style
    
    * fix style
    
    * remove unuse
    
    * remove unuse
    
    * fix default&& move post
    
    * add multi registry lables for subscribe
    
    * fix
    
    * fix url
    
    * fix url
    
    * Fix uts
    
    * add registry key for directory
    
    * Compatible with empty registry keys
    
    * remove unus
    
    * Support registry type
    
    * Fix attachment
    
    * Code enhancement
    
    * Fix registry key
    
    * Fix service discovery notify
    
    * fix
    
    ---------
    
    Co-authored-by: songxiaosheng <so...@elastic.link>
    Co-authored-by: Albumen Kevin <jh...@gmail.com>
---
 .../rpc/cluster/directory/AbstractDirectory.java   |  31 ++---
 .../rpc/cluster/directory/StaticDirectory.java     |   7 +
 .../dubbo/common/constants/CommonConstants.java    |   2 +
 .../org/apache/dubbo/config/ServiceConfig.java     |  26 ++--
 .../config/deploy/DefaultApplicationDeployer.java  |   9 +-
 .../metrics/collector/CombMetricsCollector.java    |   6 +-
 .../dubbo/metrics/data/BaseStatComposite.java      |  15 +-
 .../apache/dubbo/metrics/data/RtStatComposite.java |  16 +--
 .../dubbo/metrics/data/ServiceStatComposite.java   |  22 ++-
 .../apache/dubbo/metrics/event/MetricsEvent.java   |  15 +-
 .../listener/AbstractMetricsKeyListener.java       |   1 +
 .../metrics/listener/AbstractMetricsListener.java  |   2 +-
 .../listener/MetricsApplicationListener.java       |  29 +++-
 .../metrics/listener/MetricsServiceListener.java   |   5 +
 .../dubbo/metrics/model/ApplicationMetric.java     |  46 +++---
 .../apache/dubbo/metrics/model/MetricsSupport.java |  26 ++--
 .../dubbo/metrics/model/ServiceKeyMetric.java      |  13 +-
 .../apache/dubbo/metrics/model/key/MetricsCat.java |   2 +-
 .../dubbo/metrics/model/key/MetricsKeyWrapper.java |  16 ---
 .../dubbo/metrics/model/key/MetricsLevel.java      |   2 +-
 .../metrics/registry/RegistryMetricsConstants.java |  15 +-
 .../collector/RegistryMetricsCollector.java        |  47 +++++++
 .../registry/collector/RegistryStatComposite.java  |  81 +++++++++++
 .../metrics/registry/event/RegistryEvent.java      |  31 ++++-
 .../registry/event/RegistrySpecListener.java       | 154 +++++++++++++++++++++
 .../registry/event/RegistrySubDispatcher.java      |  59 +++-----
 .../collector/RegistryMetricsCollectorTest.java    |  28 ++--
 .../metrics/collector/RegistryMetricsTest.java     |   9 +-
 .../collector/RegistryStatCompositeTest.java       |  12 +-
 .../registry/client/ServiceDiscoveryRegistry.java  |   6 +-
 .../client/ServiceDiscoveryRegistryDirectory.java  |  18 +++
 .../metadata/ServiceInstanceMetadataUtils.java     |  22 ++-
 .../registry/integration/RegistryDirectory.java    |  20 ++-
 .../registry/integration/RegistryProtocol.java     |  16 ++-
 34 files changed, 594 insertions(+), 215 deletions(-)

diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/directory/AbstractDirectory.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/directory/AbstractDirectory.java
index 3583b15f1c..4ffe016d12 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/directory/AbstractDirectory.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/directory/AbstractDirectory.java
@@ -352,7 +352,7 @@ public abstract class AbstractDirectory<T> implements Directory<T> {
                 }
             }, reconnectTaskPeriod, TimeUnit.MILLISECONDS);
         }
-        MetricsEventBus.publish(RegistryEvent.refreshDirectoryEvent(applicationModel, getSummary()));
+        MetricsEventBus.publish(RegistryEvent.refreshDirectoryEvent(applicationModel, getSummary(), getDirectoryMeta()));
     }
 
     /**
@@ -366,7 +366,11 @@ public abstract class AbstractDirectory<T> implements Directory<T> {
         if (invokersInitialized) {
             refreshInvokerInternal();
         }
-        MetricsEventBus.publish(RegistryEvent.refreshDirectoryEvent(applicationModel, getSummary()));
+        MetricsEventBus.publish(RegistryEvent.refreshDirectoryEvent(applicationModel, getSummary(), getDirectoryMeta()));
+    }
+
+    protected Map<String, String> getDirectoryMeta() {
+        return Collections.emptyMap();
     }
 
     private synchronized void refreshInvokerInternal() {
@@ -395,7 +399,7 @@ public abstract class AbstractDirectory<T> implements Directory<T> {
             removeValidInvoker(invoker);
             logger.info("Disable service address: " + invoker.getUrl() + ".");
         }
-        MetricsEventBus.publish(RegistryEvent.refreshDirectoryEvent(applicationModel, getSummary()));
+        MetricsEventBus.publish(RegistryEvent.refreshDirectoryEvent(applicationModel, getSummary(), getDirectoryMeta()));
     }
 
     @Override
@@ -408,7 +412,7 @@ public abstract class AbstractDirectory<T> implements Directory<T> {
 
             }
         }
-        MetricsEventBus.publish(RegistryEvent.refreshDirectoryEvent(applicationModel, getSummary()));
+        MetricsEventBus.publish(RegistryEvent.refreshDirectoryEvent(applicationModel, getSummary(), getDirectoryMeta()));
     }
 
     protected final void refreshRouter(BitList<Invoker<T>> newlyInvokers, Runnable switchAction) {
@@ -465,7 +469,7 @@ public abstract class AbstractDirectory<T> implements Directory<T> {
         refreshInvokerInternal();
         this.invokersInitialized = true;
 
-        MetricsEventBus.publish(RegistryEvent.refreshDirectoryEvent(applicationModel, getSummary()));
+        MetricsEventBus.publish(RegistryEvent.refreshDirectoryEvent(applicationModel, getSummary(), getDirectoryMeta()));
     }
 
     protected void destroyInvokers() {
@@ -480,7 +484,7 @@ public abstract class AbstractDirectory<T> implements Directory<T> {
         synchronized (this.validInvokers) {
             result = this.validInvokers.add(invoker);
         }
-        MetricsEventBus.publish(RegistryEvent.refreshDirectoryEvent(applicationModel, getSummary()));
+        MetricsEventBus.publish(RegistryEvent.refreshDirectoryEvent(applicationModel, getSummary(), getDirectoryMeta()));
         return result;
     }
 
@@ -489,7 +493,7 @@ public abstract class AbstractDirectory<T> implements Directory<T> {
         synchronized (this.validInvokers) {
             result = this.validInvokers.remove(invoker);
         }
-        MetricsEventBus.publish(RegistryEvent.refreshDirectoryEvent(applicationModel, getSummary()));
+        MetricsEventBus.publish(RegistryEvent.refreshDirectoryEvent(applicationModel, getSummary(), getDirectoryMeta()));
         return result;
     }
 
@@ -519,18 +523,7 @@ public abstract class AbstractDirectory<T> implements Directory<T> {
     }
 
     private Map<String, Integer> groupByServiceKey(Collection<Invoker<T>> invokers) {
-
-        Map<String, Integer> serviceNumMap = new HashMap<>();
-        for (Invoker<T> invoker : invokers) {
-            if (invoker.getClass().getSimpleName().contains("Mockito")) {
-                return serviceNumMap;
-            }
-        }
-        if (invokers.size() > 0) {
-            serviceNumMap = invokers.stream().filter(invoker -> invoker.getInterface() != null).collect(Collectors.groupingBy(invoker -> invoker.getInterface().getName(), Collectors.reducing(0, e -> 1, Integer::sum)));
-        }
-
-        return serviceNumMap;
+        return Collections.singletonMap(getConsumerUrl().getServiceKey(), invokers.size());
     }
 
     @Override
diff --git a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/directory/StaticDirectory.java b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/directory/StaticDirectory.java
index 36e3d3eb59..d5ec1e512b 100644
--- a/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/directory/StaticDirectory.java
+++ b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/directory/StaticDirectory.java
@@ -27,9 +27,12 @@ import org.apache.dubbo.rpc.cluster.RouterChain;
 import org.apache.dubbo.rpc.cluster.SingleRouterChain;
 import org.apache.dubbo.rpc.cluster.router.state.BitList;
 
+import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 
 import static org.apache.dubbo.common.constants.LoggerCodeConstants.CLUSTER_FAILED_SITE_SELECTION;
+import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_KEY;
 
 /**
  * StaticDirectory
@@ -125,4 +128,8 @@ public class StaticDirectory<T> extends AbstractDirectory<T> {
         return invokers;
     }
 
+    @Override
+    protected Map<String, String> getDirectoryMeta() {
+        return Collections.singletonMap(REGISTRY_KEY, "static");
+    }
 }
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java
index b59ffd4654..88d687c504 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java
@@ -241,6 +241,8 @@ public interface CommonConstants {
 
     String INTERFACE_REGISTER_MODE = "interface";
 
+    String INSTANCE_REGISTER_MODE = "instance";
+
     String DEFAULT_REGISTER_MODE = "all";
 
     String GENERIC_KEY = "generic";
diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ServiceConfig.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ServiceConfig.java
index 0c2161ba0e..697c3eee51 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ServiceConfig.java
+++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ServiceConfig.java
@@ -38,8 +38,6 @@ import org.apache.dubbo.config.invoker.DelegateProviderMetaDataInvoker;
 import org.apache.dubbo.config.support.Parameter;
 import org.apache.dubbo.config.utils.ConfigValidationUtils;
 import org.apache.dubbo.metadata.ServiceNameMapping;
-import org.apache.dubbo.metrics.event.MetricsEventBus;
-import org.apache.dubbo.metrics.registry.event.RegistryEvent;
 import org.apache.dubbo.registry.client.metadata.MetadataUtils;
 import org.apache.dubbo.rpc.Exporter;
 import org.apache.dubbo.rpc.Invoker;
@@ -516,21 +514,17 @@ public class ServiceConfig<T> extends ServiceConfigBase<T> {
 
         List<URL> registryURLs = ConfigValidationUtils.loadRegistries(this, true);
 
-        MetricsEventBus.post(RegistryEvent.toRsEvent(getApplicationModel(), getUniqueServiceName(), protocols.size() * registryURLs.size()),            () -> {
-                for (ProtocolConfig protocolConfig : protocols) {
-                    String pathKey = URL.buildKey(getContextPath(protocolConfig)
-                        .map(p -> p + "/" + path)
-                        .orElse(path), group, version);
-                    // stub service will use generated service name
-                    if (!serverService) {
-                        // In case user specified path, registerImmediately service one more time to map it to path.
-                        repository.registerService(pathKey, interfaceClass);
-                    }
-                    doExportUrlsFor1Protocol(protocolConfig, registryURLs, registerType);
-                }
-                return null;
+        for (ProtocolConfig protocolConfig : protocols) {
+            String pathKey = URL.buildKey(getContextPath(protocolConfig)
+                .map(p -> p + "/" + path)
+                .orElse(path), group, version);
+            // stub service will use generated service name
+            if (!serverService) {
+                // In case user specified path, register service one more time to map it to path.
+                repository.registerService(pathKey, interfaceClass);
             }
-        );
+            doExportUrlsFor1Protocol(protocolConfig, registryURLs, registerType);
+        }
 
         providerModel.setServiceUrls(urls);
     }
diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/deploy/DefaultApplicationDeployer.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/deploy/DefaultApplicationDeployer.java
index ac012ee3d5..b075acaa44 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/deploy/DefaultApplicationDeployer.java
+++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/deploy/DefaultApplicationDeployer.java
@@ -55,7 +55,6 @@ import org.apache.dubbo.metadata.report.MetadataReportInstance;
 import org.apache.dubbo.metrics.collector.DefaultMetricsCollector;
 import org.apache.dubbo.metrics.config.event.ConfigCenterEvent;
 import org.apache.dubbo.metrics.event.MetricsEventBus;
-import org.apache.dubbo.metrics.registry.event.RegistryEvent;
 import org.apache.dubbo.metrics.report.DefaultMetricsReporterFactory;
 import org.apache.dubbo.metrics.report.MetricsReporter;
 import org.apache.dubbo.metrics.report.MetricsReporterFactory;
@@ -899,16 +898,10 @@ public class DefaultApplicationDeployer extends AbstractDeployer<ApplicationMode
     private void registerServiceInstance() {
         try {
             registered = true;
-            MetricsEventBus.post(RegistryEvent.toRegisterEvent(applicationModel),
-                () -> {
-                    ServiceInstanceMetadataUtils.registerMetadataAndInstance(applicationModel);
-                    return null;
-                }
-            );
+            ServiceInstanceMetadataUtils.registerMetadataAndInstance(applicationModel);
         } catch (Exception e) {
             logger.error(CONFIG_REGISTER_INSTANCE_ERROR, "configuration server disconnected", "", "Register instance error.", e);
         }
-
         if (registered) {
             // scheduled task for updating Metadata and ServiceInstance
             asyncMetadataFuture = frameworkExecutorRepository.getSharedScheduledExecutor().scheduleWithFixedDelay(() -> {
diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/collector/CombMetricsCollector.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/collector/CombMetricsCollector.java
index 81ae03eb40..66fb5f2aee 100644
--- a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/collector/CombMetricsCollector.java
+++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/collector/CombMetricsCollector.java
@@ -34,7 +34,7 @@ import static org.apache.dubbo.metrics.MetricsConstants.SELF_INCREMENT_SIZE;
 
 public abstract class CombMetricsCollector<E extends TimeCounterEvent> extends AbstractMetricsListener<E> implements ApplicationMetricsCollector<E>, ServiceMetricsCollector<E>, MethodMetricsCollector<E> {
 
-    private final BaseStatComposite stats;
+    protected final BaseStatComposite stats;
     private MetricsEventMulticaster eventMulticaster;
 
 
@@ -108,5 +108,9 @@ public abstract class CombMetricsCollector<E extends TimeCounterEvent> extends A
     public void onEventError(TimeCounterEvent event) {
         eventMulticaster.publishErrorEvent(event);
     }
+
+    protected BaseStatComposite getStats() {
+        return stats;
+    }
 }
 
diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/data/BaseStatComposite.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/data/BaseStatComposite.java
index c7ddd9dee0..ef987e7e15 100644
--- a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/data/BaseStatComposite.java
+++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/data/BaseStatComposite.java
@@ -18,8 +18,10 @@
 package org.apache.dubbo.metrics.data;
 
 import org.apache.dubbo.metrics.collector.MetricsCollector;
+import org.apache.dubbo.metrics.model.ApplicationMetric;
 import org.apache.dubbo.metrics.model.MethodMetric;
 import org.apache.dubbo.metrics.model.MetricsCategory;
+import org.apache.dubbo.metrics.model.ServiceKeyMetric;
 import org.apache.dubbo.metrics.model.key.MetricsKey;
 import org.apache.dubbo.metrics.model.key.MetricsKeyWrapper;
 import org.apache.dubbo.metrics.model.sample.MetricSample;
@@ -29,6 +31,7 @@ import org.apache.dubbo.rpc.model.ApplicationModel;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 
 
 /**
@@ -70,11 +73,11 @@ public abstract class BaseStatComposite implements MetricsExport {
     }
 
     public void calcApplicationRt(String registryOpType, Long responseTime) {
-        rtStatComposite.calcApplicationRt(registryOpType, responseTime);
+        rtStatComposite.calcServiceKeyRt(registryOpType, responseTime, new ApplicationMetric(rtStatComposite.getApplicationModel()));
     }
 
     public void calcServiceKeyRt(String serviceKey, String registryOpType, Long responseTime) {
-        rtStatComposite.calcServiceKeyRt(serviceKey, registryOpType, responseTime);
+        rtStatComposite.calcServiceKeyRt(registryOpType, responseTime, new ServiceKeyMetric(rtStatComposite.getApplicationModel(), serviceKey));
     }
 
     public void calcServiceKeyRt(Invocation invocation, String registryOpType, Long responseTime) {
@@ -89,6 +92,10 @@ public abstract class BaseStatComposite implements MetricsExport {
         serviceStatComposite.setServiceKey(metricsKey, serviceKey, num);
     }
 
+    public void setServiceKey(MetricsKeyWrapper metricsKey, String serviceKey, int num, Map<String, String> extra) {
+        serviceStatComposite.setExtraServiceKey(metricsKey, serviceKey, num, extra);
+    }
+
     public void incrementApp(MetricsKey metricsKey, int size) {
         applicationStatComposite.incrementSize(metricsKey, size);
     }
@@ -97,6 +104,10 @@ public abstract class BaseStatComposite implements MetricsExport {
         serviceStatComposite.incrementServiceKey(metricsKeyWrapper, attServiceKey, size);
     }
 
+    public void incrementServiceKey(MetricsKeyWrapper metricsKeyWrapper, String attServiceKey, Map<String, String> extra, int size) {
+        serviceStatComposite.incrementExtraServiceKey(metricsKeyWrapper, attServiceKey, extra, size);
+    }
+
     public void incrementMethodKey(MetricsKeyWrapper metricsKeyWrapper, MethodMetric methodMetric, int size) {
         methodStatComposite.incrementMethodKey(metricsKeyWrapper, methodMetric, size);
     }
diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/data/RtStatComposite.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/data/RtStatComposite.java
index 5a3d96c9b3..e6d0bae6e7 100644
--- a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/data/RtStatComposite.java
+++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/data/RtStatComposite.java
@@ -17,7 +17,6 @@
 
 package org.apache.dubbo.metrics.data;
 
-import org.apache.dubbo.metrics.model.ApplicationMetric;
 import org.apache.dubbo.metrics.model.MethodMetric;
 import org.apache.dubbo.metrics.model.Metric;
 import org.apache.dubbo.metrics.model.MetricsCategory;
@@ -89,20 +88,7 @@ public class RtStatComposite extends AbstractMetricsExport {
         return singleRtStats;
     }
 
-    public void calcApplicationRt(String registryOpType, Long responseTime) {
-        ApplicationMetric key = new ApplicationMetric(getApplicationModel());
-        for (LongContainer container : rtStats.get(registryOpType)) {
-            Number current = (Number) container.get(key);
-            if (current == null) {
-                container.putIfAbsent(key, container.getInitFunc().apply(key));
-                current = (Number) container.get(key);
-            }
-            container.getConsumerFunc().accept(responseTime, current);
-        }
-    }
-
-    public void calcServiceKeyRt(String serviceKey, String registryOpType, Long responseTime) {
-        ServiceKeyMetric key = new ServiceKeyMetric(getApplicationModel(), serviceKey);
+    public void calcServiceKeyRt(String registryOpType, Long responseTime, Metric key) {
         for (LongContainer container : rtStats.get(registryOpType)) {
             Number current = (Number) container.get(key);
             if (current == null) {
diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/data/ServiceStatComposite.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/data/ServiceStatComposite.java
index 3399541c42..afedd71641 100644
--- a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/data/ServiceStatComposite.java
+++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/data/ServiceStatComposite.java
@@ -53,21 +53,37 @@ public class ServiceStatComposite extends AbstractMetricsExport {
     }
 
     public void incrementServiceKey(MetricsKeyWrapper wrapper, String serviceKey, int size) {
+        incrementExtraServiceKey(wrapper, serviceKey, null, size);
+    }
+
+    public void incrementExtraServiceKey(MetricsKeyWrapper wrapper, String serviceKey, Map<String, String> extra, int size) {
         if (!serviceWrapperNumStats.containsKey(wrapper)) {
             return;
         }
-        serviceWrapperNumStats.get(wrapper).computeIfAbsent(new ServiceKeyMetric(getApplicationModel(), serviceKey), k -> new AtomicLong(0L)).getAndAdd(size);
+        ServiceKeyMetric serviceKeyMetric = new ServiceKeyMetric(getApplicationModel(), serviceKey);
+        if (extra != null) {
+            serviceKeyMetric.setExtraInfo(extra);
+        }
+        serviceWrapperNumStats.get(wrapper).computeIfAbsent(serviceKeyMetric, k -> new AtomicLong(0L)).getAndAdd(size);
 //        MetricsSupport.fillZero(serviceWrapperNumStats);
     }
 
     public void setServiceKey(MetricsKeyWrapper wrapper, String serviceKey, int num) {
+        setExtraServiceKey(wrapper, serviceKey, num, null);
+    }
+
+    public void setExtraServiceKey(MetricsKeyWrapper wrapper, String serviceKey, int num, Map<String, String> extra) {
         if (!serviceWrapperNumStats.containsKey(wrapper)) {
             return;
         }
-        serviceWrapperNumStats.get(wrapper).computeIfAbsent(new ServiceKeyMetric(getApplicationModel(), serviceKey), k -> new AtomicLong(0L)).set(num);
-//        MetricsSupport.fillZero(serviceWrapperNumStats);
+        ServiceKeyMetric serviceKeyMetric = new ServiceKeyMetric(getApplicationModel(), serviceKey);
+        if (extra != null) {
+            serviceKeyMetric.setExtraInfo(extra);
+        }
+        serviceWrapperNumStats.get(wrapper).computeIfAbsent(serviceKeyMetric, k -> new AtomicLong(0L)).set(num);
     }
 
+    @Override
     public List<MetricSample> export(MetricsCategory category) {
         List<MetricSample> list = new ArrayList<>();
         for (MetricsKeyWrapper wrapper : serviceWrapperNumStats.keySet()) {
diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/event/MetricsEvent.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/event/MetricsEvent.java
index 4203d81a63..3b22dfa818 100644
--- a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/event/MetricsEvent.java
+++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/event/MetricsEvent.java
@@ -23,6 +23,7 @@ import org.apache.dubbo.metrics.model.MethodMetric;
 import org.apache.dubbo.metrics.model.key.TypeWrapper;
 import org.apache.dubbo.rpc.model.ApplicationModel;
 
+import java.util.Collections;
 import java.util.IdentityHashMap;
 import java.util.Map;
 
@@ -40,7 +41,7 @@ public abstract class MetricsEvent {
     private final String appName;
     private final MetricsDispatcher metricsDispatcher;
 
-    private final Map<String, Object> attachment = new IdentityHashMap<>(8);
+    private final Map<String, Object> attachments = new IdentityHashMap<>(8);
 
     public MetricsEvent(ApplicationModel source, TypeWrapper typeWrapper) {
         this(source, null, null, typeWrapper);
@@ -82,11 +83,19 @@ public abstract class MetricsEvent {
         if (key == null) {
             throw new MetricsNeverHappenException("Attachment key is null");
         }
-        return (T) attachment.get(key);
+        return (T) attachments.get(key);
+    }
+
+    public Map<String, Object> getAttachments() {
+        return Collections.unmodifiableMap(attachments);
     }
 
     public void putAttachment(String key, Object value) {
-        attachment.put(key, value);
+        attachments.put(key, value);
+    }
+
+    public void putAttachments(Map<String, String> attachments) {
+        this.attachments.putAll(attachments);
     }
 
     public void setAvailable(boolean available) {
diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/listener/AbstractMetricsKeyListener.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/listener/AbstractMetricsKeyListener.java
index 04c18f5263..2ba791e0e1 100644
--- a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/listener/AbstractMetricsKeyListener.java
+++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/listener/AbstractMetricsKeyListener.java
@@ -27,6 +27,7 @@ import java.util.function.Consumer;
 /**
  * According to the event template of {@link MetricsEventBus},
  * build a consistent static method for general and custom monitoring consume methods
+ *
  */
 public abstract class AbstractMetricsKeyListener extends AbstractMetricsListener<TimeCounterEvent> implements MetricsLifeListener<TimeCounterEvent> {
 
diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/listener/AbstractMetricsListener.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/listener/AbstractMetricsListener.java
index 63ac20b877..0a3b9e65f9 100644
--- a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/listener/AbstractMetricsListener.java
+++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/listener/AbstractMetricsListener.java
@@ -28,7 +28,7 @@ public abstract class AbstractMetricsListener<E extends MetricsEvent> implements
     private final Map<Integer, Boolean> eventMatchCache = new ConcurrentHashMap<>();
 
     /**
-     * Whether to support the general determination of event points depends on the event type
+     * Only interested in events of the current listener's generic parameter type
      */
     public boolean isSupport(MetricsEvent event) {
         Boolean eventMatch = eventMatchCache.get(System.identityHashCode(event.getClass()));
diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/listener/MetricsApplicationListener.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/listener/MetricsApplicationListener.java
index 57d52ccb29..96891e6bb0 100644
--- a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/listener/MetricsApplicationListener.java
+++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/listener/MetricsApplicationListener.java
@@ -21,19 +21,37 @@ import org.apache.dubbo.metrics.collector.CombMetricsCollector;
 import org.apache.dubbo.metrics.model.key.MetricsKey;
 import org.apache.dubbo.metrics.model.key.MetricsPlaceValue;
 
+/**
+ * App-level listener type, in most cases, can use the static method
+ * to produce an anonymous listener for general monitoring
+ */
 public class MetricsApplicationListener extends AbstractMetricsKeyListener {
 
     public MetricsApplicationListener(MetricsKey metricsKey) {
         super(metricsKey);
     }
 
-    public static AbstractMetricsKeyListener onPostEventBuild(MetricsKey metricsKey, CombMetricsCollector collector) {
+    /**
+     * Perform auto-increment on the monitored key,
+     * Can use a custom listener instead of this generic operation
+     *
+     * @param metricsKey Monitor key
+     * @param collector  Corresponding collector
+     */
+    public static AbstractMetricsKeyListener onPostEventBuild(MetricsKey metricsKey, CombMetricsCollector<?> collector) {
         return AbstractMetricsKeyListener.onEvent(metricsKey,
-                event -> collector.increment(metricsKey)
+            event -> collector.increment(metricsKey)
         );
     }
 
-    public static AbstractMetricsKeyListener onFinishEventBuild(MetricsKey metricsKey, MetricsPlaceValue placeType, CombMetricsCollector collector) {
+    /**
+     * To end the monitoring normally, in addition to increasing the number of corresponding indicators,
+     * use the introspection method to calculate the relevant rt indicators
+     *
+     * @param metricsKey Monitor key
+     * @param collector  Corresponding collector
+     */
+    public static AbstractMetricsKeyListener onFinishEventBuild(MetricsKey metricsKey, MetricsPlaceValue placeType, CombMetricsCollector<?> collector) {
         return AbstractMetricsKeyListener.onFinish(metricsKey,
                 event -> {
                     collector.increment(metricsKey);
@@ -42,7 +60,10 @@ public class MetricsApplicationListener extends AbstractMetricsKeyListener {
         );
     }
 
-    public static AbstractMetricsKeyListener onErrorEventBuild(MetricsKey metricsKey, MetricsPlaceValue placeType, CombMetricsCollector collector) {
+    /**
+     * Similar to onFinishEventBuild
+     */
+    public static AbstractMetricsKeyListener onErrorEventBuild(MetricsKey metricsKey, MetricsPlaceValue placeType, CombMetricsCollector<?> collector) {
         return AbstractMetricsKeyListener.onError(metricsKey,
                 event -> {
                     collector.increment(metricsKey);
diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/listener/MetricsServiceListener.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/listener/MetricsServiceListener.java
index 18989d78b4..40f27b18ca 100644
--- a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/listener/MetricsServiceListener.java
+++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/listener/MetricsServiceListener.java
@@ -23,6 +23,11 @@ import org.apache.dubbo.metrics.model.MetricsSupport;
 import org.apache.dubbo.metrics.model.key.MetricsKey;
 import org.apache.dubbo.metrics.model.key.MetricsPlaceValue;
 
+/**
+ * Service-level listener type, in most cases, can use the static method
+ * to produce an anonymous listener for general monitoring.
+ * Similar to App-level
+ */
 public class MetricsServiceListener extends AbstractMetricsKeyListener {
 
     public MetricsServiceListener(MetricsKey metricsKey) {
diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/ApplicationMetric.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/ApplicationMetric.java
index 6752139e89..20d03ca523 100644
--- a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/ApplicationMetric.java
+++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/ApplicationMetric.java
@@ -17,25 +17,14 @@
 
 package org.apache.dubbo.metrics.model;
 
-import org.apache.dubbo.common.Version;
-import org.apache.dubbo.metrics.model.key.MetricsKey;
 import org.apache.dubbo.rpc.model.ApplicationModel;
 
-import java.util.HashMap;
 import java.util.Map;
 import java.util.Objects;
 
-import static org.apache.dubbo.common.constants.MetricsConstants.TAG_APPLICATION_NAME;
-import static org.apache.dubbo.common.constants.MetricsConstants.TAG_APPLICATION_VERSION_KEY;
-import static org.apache.dubbo.common.constants.MetricsConstants.TAG_HOSTNAME;
-import static org.apache.dubbo.common.constants.MetricsConstants.TAG_IP;
-import static org.apache.dubbo.common.utils.NetUtils.getLocalHost;
-import static org.apache.dubbo.common.utils.NetUtils.getLocalHostName;
-
 public class ApplicationMetric implements Metric {
     private final ApplicationModel applicationModel;
-    private static final String version = Version.getVersion();
-    private static final String commitId = Version.getLastCommitId();
+    protected Map<String, String> extraInfo;
 
     public ApplicationMetric(ApplicationModel applicationModel) {
         this.applicationModel = applicationModel;
@@ -49,27 +38,29 @@ public class ApplicationMetric implements Metric {
         return getApplicationModel().getApplicationName();
     }
 
-    public String getData() {
-        return version;
-    }
-
     @Override
     public Map<String, String> getTags() {
-        Map<String, String> tags = new HashMap<>();
-        tags.put(TAG_IP, getLocalHost());
-        tags.put(TAG_HOSTNAME, getLocalHostName());
-        tags.put(TAG_APPLICATION_NAME, getApplicationName());
-        tags.put(TAG_APPLICATION_VERSION_KEY, version);
-        tags.put(MetricsKey.METADATA_GIT_COMMITID_METRIC.getName(), commitId);
-        return tags;
+        return MetricsSupport.applicationTags(applicationModel, getExtraInfo());
+    }
+
+    public Map<String, String> getExtraInfo() {
+        return extraInfo;
+    }
+
+    public void setExtraInfo(Map<String, String> extraInfo) {
+        this.extraInfo = extraInfo;
     }
 
     @Override
     public boolean equals(Object o) {
-        if (this == o) return true;
-        if (!(o instanceof ApplicationMetric)) return false;
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
         ApplicationMetric that = (ApplicationMetric) o;
-        return Objects.equals(getApplicationName(), that.applicationModel.getApplicationName());
+        return getApplicationName().equals(that.applicationModel.getApplicationName()) && Objects.equals(extraInfo, that.extraInfo);
     }
 
     private volatile int hashCode;
@@ -77,8 +68,9 @@ public class ApplicationMetric implements Metric {
     @Override
     public int hashCode() {
         if (hashCode == 0) {
-            hashCode = Objects.hash(getApplicationName());
+            hashCode = Objects.hash(getApplicationName(), extraInfo);
         }
         return hashCode;
     }
+
 }
diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/MetricsSupport.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/MetricsSupport.java
index 034bcbea02..404018dfb5 100644
--- a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/MetricsSupport.java
+++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/MetricsSupport.java
@@ -18,12 +18,12 @@
 package org.apache.dubbo.metrics.model;
 
 import org.apache.dubbo.common.Version;
+import org.apache.dubbo.common.lang.Nullable;
 import org.apache.dubbo.common.utils.CollectionUtils;
 import org.apache.dubbo.metrics.collector.MethodMetricsCollector;
 import org.apache.dubbo.metrics.collector.ServiceMetricsCollector;
 import org.apache.dubbo.metrics.event.MetricsEvent;
 import org.apache.dubbo.metrics.event.TimeCounterEvent;
-import org.apache.dubbo.metrics.exception.MetricsNeverHappenException;
 import org.apache.dubbo.metrics.model.key.MetricsKey;
 import org.apache.dubbo.metrics.model.key.MetricsKeyWrapper;
 import org.apache.dubbo.metrics.model.key.MetricsPlaceValue;
@@ -62,6 +62,10 @@ public class MetricsSupport {
     private static final String commitId = Version.getLastCommitId();
 
     public static Map<String, String> applicationTags(ApplicationModel applicationModel) {
+        return applicationTags(applicationModel, null);
+    }
+
+    public static Map<String, String> applicationTags(ApplicationModel applicationModel, @Nullable Map<String, String> extraInfo) {
         Map<String, String> tags = new HashMap<>();
         tags.put(TAG_IP, getLocalHost());
         tags.put(TAG_HOSTNAME, getLocalHostName());
@@ -69,23 +73,18 @@ public class MetricsSupport {
         tags.put(TAG_APPLICATION_MODULE, applicationModel.getInternalId());
         tags.put(TAG_APPLICATION_VERSION_KEY, version);
         tags.put(MetricsKey.METADATA_GIT_COMMITID_METRIC.getName(), commitId);
+        if (CollectionUtils.isNotEmptyMap(extraInfo)) {
+            tags.putAll(extraInfo);
+        }
         return tags;
     }
 
-    public static Map<String, String> serviceTags(ApplicationModel applicationModel, String serviceKey) {
-        Map<String, String> tags = applicationTags(applicationModel);
+    public static Map<String, String> serviceTags(ApplicationModel applicationModel, String serviceKey, Map<String, String> extraInfo) {
+        Map<String, String> tags = applicationTags(applicationModel, extraInfo);
         tags.put(TAG_INTERFACE_KEY, serviceKey);
         return tags;
     }
 
-    public static Map<String, String> methodTags(ApplicationModel applicationModel, String names) {
-        String[] keys = names.split("_");
-        if (keys.length != 2) {
-            throw new MetricsNeverHappenException("Error names: " + names);
-        }
-        return methodTags(applicationModel, keys[0], keys[1]);
-    }
-
     public static Map<String, String> methodTags(ApplicationModel applicationModel, String serviceKey, String methodName) {
         Map<String, String> tags = applicationTags(applicationModel);
         tags.put(TAG_INTERFACE_KEY, serviceKey);
@@ -211,7 +210,6 @@ public class MetricsSupport {
         Invocation invocation = event.getAttachmentValue(INVOCATION);
         if (invocation != null) {
             collector.addServiceRt(invocation, placeType.getType(), event.getTimePair().calc());
-            return;
         } else {
             collector.addServiceRt((String) event.getAttachmentValue(ATTACHMENT_KEY_SERVICE), placeType.getType(), event.getTimePair().calc());
         }
@@ -240,9 +238,9 @@ public class MetricsSupport {
     }
 
     /**
-     *  Generate a complete indicator item for an interface/method
+     * Generate a complete indicator item for an interface/method
      */
-    public static <T> void fillZero(Map<MetricsKeyWrapper, Map<T, AtomicLong>> data) {
+    public static <T> void fillZero(Map<?, Map<T, AtomicLong>> data) {
         if (CollectionUtils.isEmptyMap(data)) {
             return;
         }
diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/ServiceKeyMetric.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/ServiceKeyMetric.java
index 1b1d220cea..361fd9f06e 100644
--- a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/ServiceKeyMetric.java
+++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/ServiceKeyMetric.java
@@ -35,7 +35,7 @@ public class ServiceKeyMetric extends ApplicationMetric {
 
     @Override
     public Map<String, String> getTags() {
-        return MetricsSupport.serviceTags(getApplicationModel(), serviceKey);
+        return MetricsSupport.serviceTags(getApplicationModel(), serviceKey, getExtraInfo());
     }
 
     public String getServiceKey() {
@@ -47,16 +47,11 @@ public class ServiceKeyMetric extends ApplicationMetric {
         if (this == o) {
             return true;
         }
-        if (o == null || getClass() != o.getClass()) {
+        if (!(o instanceof ServiceKeyMetric)) {
             return false;
         }
-
         ServiceKeyMetric that = (ServiceKeyMetric) o;
-
-        if (!getApplicationName().equals(that.getApplicationName())) {
-            return false;
-        }
-        return serviceKey.equals(that.serviceKey);
+        return serviceKey.equals(that.serviceKey) && Objects.equals(extraInfo, that.extraInfo);
     }
 
 
@@ -65,7 +60,7 @@ public class ServiceKeyMetric extends ApplicationMetric {
     @Override
     public int hashCode() {
         if (hashCode == 0) {
-            hashCode = Objects.hash(getApplicationName(), serviceKey);
+            hashCode = Objects.hash(getApplicationName(), serviceKey, extraInfo);
         }
         return hashCode;
     }
diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/key/MetricsCat.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/key/MetricsCat.java
index cf1c813419..d085aa7ee9 100644
--- a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/key/MetricsCat.java
+++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/key/MetricsCat.java
@@ -43,7 +43,7 @@ public class MetricsCat {
     }
 
     /**
-     * @param tpFunc   Ternary function, corresponding to finish and error events, because an additional record rt is required, and the type type of metricsKey is required
+     * @param tpFunc   Ternary function, corresponding to finish and error events, because an additional record rt is required, and the type of metricsKey is required
      */
     public MetricsCat(MetricsKey metricsKey, TpFunction<MetricsKey, MetricsPlaceValue, CombMetricsCollector, AbstractMetricsKeyListener> tpFunc) {
         this.eventFunc = collector -> tpFunc.apply(metricsKey, placeType, collector);
diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/key/MetricsKeyWrapper.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/key/MetricsKeyWrapper.java
index 21bb7648e9..095cc5f305 100644
--- a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/key/MetricsKeyWrapper.java
+++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/key/MetricsKeyWrapper.java
@@ -18,11 +18,8 @@
 package org.apache.dubbo.metrics.model.key;
 
 import io.micrometer.common.lang.Nullable;
-import org.apache.dubbo.metrics.model.MetricsSupport;
 import org.apache.dubbo.metrics.model.sample.MetricSample;
-import org.apache.dubbo.rpc.model.ApplicationModel;
 
-import java.util.Map;
 import java.util.Objects;
 
 /**
@@ -105,19 +102,6 @@ public class MetricsKeyWrapper {
         }
     }
 
-    public Map<String, String> tagName(ApplicationModel applicationModel, String key) {
-        MetricsLevel level = getLevel();
-        switch (level) {
-            case APP:
-                return MetricsSupport.applicationTags(applicationModel);
-            case SERVICE:
-                return MetricsSupport.serviceTags(applicationModel, key);
-            case METHOD:
-                return MetricsSupport.methodTags(applicationModel, key);
-        }
-        return MetricsSupport.applicationTags(applicationModel);
-    }
-
     public static MetricsKeyWrapper wrapper(MetricsKey metricsKey) {
         return new MetricsKeyWrapper(metricsKey, null);
     }
diff --git a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/key/MetricsLevel.java b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/key/MetricsLevel.java
index 783de99948..9a81e17ef8 100644
--- a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/key/MetricsLevel.java
+++ b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/key/MetricsLevel.java
@@ -18,5 +18,5 @@
 package org.apache.dubbo.metrics.model.key;
 
 public enum MetricsLevel {
-    APP, SERVICE, METHOD, CONFIG
+    APP, SERVICE, METHOD, CONFIG, REGISTRY
 }
diff --git a/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/RegistryMetricsConstants.java b/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/RegistryMetricsConstants.java
index e7adbc9622..18dac35bc6 100644
--- a/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/RegistryMetricsConstants.java
+++ b/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/RegistryMetricsConstants.java
@@ -23,6 +23,7 @@ import org.apache.dubbo.metrics.model.key.MetricsLevel;
 import org.apache.dubbo.metrics.model.key.MetricsPlaceValue;
 
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 
 import static org.apache.dubbo.metrics.model.key.MetricsKey.DIRECTORY_METRIC_NUM_ALL;
@@ -46,17 +47,23 @@ import static org.apache.dubbo.metrics.model.key.MetricsKey.SUBSCRIBE_METRIC_NUM
 
 public interface RegistryMetricsConstants {
 
+    String ATTACHMENT_REGISTRY_KEY = "registryKey";
+    String ATTACHMENT_REGISTRY_SINGLE_KEY = "registrySingleKey";
+
     MetricsPlaceValue OP_TYPE_REGISTER = MetricsPlaceValue.of("register", MetricsLevel.APP);
     MetricsPlaceValue OP_TYPE_SUBSCRIBE = MetricsPlaceValue.of("subscribe", MetricsLevel.APP);
     MetricsPlaceValue OP_TYPE_NOTIFY = MetricsPlaceValue.of("notify", MetricsLevel.APP);
     MetricsPlaceValue OP_TYPE_DIRECTORY = MetricsPlaceValue.of("directory", MetricsLevel.APP);
-    MetricsPlaceValue OP_TYPE_REGISTER_SERVICE = MetricsPlaceValue.of("register.service", MetricsLevel.SERVICE);
+    MetricsPlaceValue OP_TYPE_REGISTER_SERVICE = MetricsPlaceValue.of("register.service", MetricsLevel.REGISTRY);
     MetricsPlaceValue OP_TYPE_SUBSCRIBE_SERVICE = MetricsPlaceValue.of("subscribe.service", MetricsLevel.SERVICE);
 
     // App-level
-    List<MetricsKey> APP_LEVEL_KEYS = Arrays.asList(REGISTER_METRIC_REQUESTS, REGISTER_METRIC_REQUESTS_SUCCEED, REGISTER_METRIC_REQUESTS_FAILED,
-        SUBSCRIBE_METRIC_NUM, SUBSCRIBE_METRIC_NUM_SUCCEED, SUBSCRIBE_METRIC_NUM_FAILED,
-        NOTIFY_METRIC_REQUESTS);
+    List<MetricsKey> APP_LEVEL_KEYS = Collections.singletonList(NOTIFY_METRIC_REQUESTS);
+
+    // Registry-level
+    List<MetricsKey> REGISTER_LEVEL_KEYS = Arrays.asList(REGISTER_METRIC_REQUESTS, REGISTER_METRIC_REQUESTS_SUCCEED, REGISTER_METRIC_REQUESTS_FAILED,
+        SUBSCRIBE_METRIC_NUM, SUBSCRIBE_METRIC_NUM_SUCCEED, SUBSCRIBE_METRIC_NUM_FAILED
+    );
 
     // Service-level
     List<MetricsKeyWrapper> SERVICE_LEVEL_KEYS = Arrays.asList(
diff --git a/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/collector/RegistryMetricsCollector.java b/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/collector/RegistryMetricsCollector.java
index b18ed313f7..b510ed541b 100644
--- a/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/collector/RegistryMetricsCollector.java
+++ b/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/collector/RegistryMetricsCollector.java
@@ -17,6 +17,7 @@
 
 package org.apache.dubbo.metrics.registry.collector;
 
+import org.apache.dubbo.common.constants.RegistryConstants;
 import org.apache.dubbo.common.extension.Activate;
 import org.apache.dubbo.config.context.ConfigManager;
 import org.apache.dubbo.metrics.collector.CombMetricsCollector;
@@ -25,7 +26,11 @@ import org.apache.dubbo.metrics.data.ApplicationStatComposite;
 import org.apache.dubbo.metrics.data.BaseStatComposite;
 import org.apache.dubbo.metrics.data.RtStatComposite;
 import org.apache.dubbo.metrics.data.ServiceStatComposite;
+import org.apache.dubbo.metrics.model.ApplicationMetric;
 import org.apache.dubbo.metrics.model.MetricsCategory;
+import org.apache.dubbo.metrics.model.ServiceKeyMetric;
+import org.apache.dubbo.metrics.model.key.MetricsKey;
+import org.apache.dubbo.metrics.model.key.MetricsKeyWrapper;
 import org.apache.dubbo.metrics.model.sample.MetricSample;
 import org.apache.dubbo.metrics.registry.RegistryMetricsConstants;
 import org.apache.dubbo.metrics.registry.event.RegistryEvent;
@@ -33,7 +38,9 @@ import org.apache.dubbo.metrics.registry.event.RegistrySubDispatcher;
 import org.apache.dubbo.rpc.model.ApplicationModel;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 import java.util.Optional;
 
 import static org.apache.dubbo.metrics.registry.RegistryMetricsConstants.OP_TYPE_NOTIFY;
@@ -51,6 +58,7 @@ public class RegistryMetricsCollector extends CombMetricsCollector<RegistryEvent
 
     private Boolean collectEnabled = null;
     private final ApplicationModel applicationModel;
+    private final RegistryStatComposite internalStat;
 
     public RegistryMetricsCollector(ApplicationModel applicationModel) {
         super(new BaseStatComposite(applicationModel) {
@@ -73,6 +81,7 @@ public class RegistryMetricsCollector extends CombMetricsCollector<RegistryEvent
             }
         });
         super.setEventMulticaster(new RegistrySubDispatcher(this));
+        internalStat = new RegistryStatComposite(applicationModel);
         this.applicationModel = applicationModel;
     }
 
@@ -99,7 +108,45 @@ public class RegistryMetricsCollector extends CombMetricsCollector<RegistryEvent
             return list;
         }
         list.addAll(super.export(MetricsCategory.REGISTRY));
+        list.addAll(internalStat.export(MetricsCategory.REGISTRY));
         return list;
     }
 
+    public void incrMetricsNum(MetricsKey metricsKey, List<String> registryClusterNames) {
+        registryClusterNames.forEach(name -> internalStat.incrMetricsNum(metricsKey, name));
+    }
+
+    public void incrRegisterFinishNum(MetricsKey metricsKey, String registryOpType, List<String> registryClusterNames, Long responseTime) {
+        registryClusterNames.forEach(name ->
+        {
+            ApplicationMetric applicationMetric = new ApplicationMetric(applicationModel);
+            applicationMetric.setExtraInfo(Collections.singletonMap(RegistryConstants.REGISTRY_CLUSTER_KEY.toLowerCase(), name));
+            internalStat.incrMetricsNum(metricsKey, name);
+            getStats().getRtStatComposite().calcServiceKeyRt(registryOpType, responseTime, applicationMetric);
+        });
+
+    }
+
+    public void incrServiceRegisterNum(MetricsKeyWrapper wrapper, String serviceKey, List<String> registryClusterNames, int size) {
+        registryClusterNames.forEach(name ->
+            stats.incrementServiceKey(wrapper, serviceKey, Collections.singletonMap(RegistryConstants.REGISTRY_CLUSTER_KEY.toLowerCase(), name), size)
+        );
+    }
+
+    public void incrServiceRegisterFinishNum(MetricsKeyWrapper wrapper, String serviceKey, List<String> registryClusterNames, int size, Long responseTime) {
+        registryClusterNames.forEach(name ->
+            {
+                Map<String, String> extraInfo = Collections.singletonMap(RegistryConstants.REGISTRY_CLUSTER_KEY.toLowerCase(), name);
+                ServiceKeyMetric serviceKeyMetric = new ServiceKeyMetric(applicationModel, serviceKey);
+                serviceKeyMetric.setExtraInfo(extraInfo);
+                stats.incrementServiceKey(wrapper, serviceKey, extraInfo, size);
+                getStats().getRtStatComposite().calcServiceKeyRt(wrapper.getType(), responseTime, serviceKeyMetric);
+            }
+        );
+    }
+
+    public void setNum(MetricsKeyWrapper metricsKey, String serviceKey, int num, Map<String, String> attachments) {
+        this.stats.setServiceKey(metricsKey, serviceKey, num, attachments);
+    }
+
 }
diff --git a/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/collector/RegistryStatComposite.java b/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/collector/RegistryStatComposite.java
new file mode 100644
index 0000000000..a60c5c4b49
--- /dev/null
+++ b/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/collector/RegistryStatComposite.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metrics.registry.collector;
+
+import org.apache.dubbo.common.constants.RegistryConstants;
+import org.apache.dubbo.common.utils.CollectionUtils;
+import org.apache.dubbo.metrics.model.ApplicationMetric;
+import org.apache.dubbo.metrics.model.MetricsCategory;
+import org.apache.dubbo.metrics.model.MetricsSupport;
+import org.apache.dubbo.metrics.model.key.MetricsKey;
+import org.apache.dubbo.metrics.model.sample.GaugeMetricSample;
+import org.apache.dubbo.metrics.model.sample.MetricSample;
+import org.apache.dubbo.metrics.registry.RegistryMetricsConstants;
+import org.apache.dubbo.metrics.report.AbstractMetricsExport;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+
+import static org.apache.dubbo.metrics.MetricsConstants.SELF_INCREMENT_SIZE;
+
+public class RegistryStatComposite extends AbstractMetricsExport {
+
+    private final Map<MetricsKey, Map<ApplicationMetric, AtomicLong>> appStats = new ConcurrentHashMap<>();
+
+    public RegistryStatComposite(ApplicationModel applicationModel) {
+        super(applicationModel);
+        init(RegistryMetricsConstants.REGISTER_LEVEL_KEYS);
+    }
+
+    public void init(List<MetricsKey> appKeys) {
+        if (CollectionUtils.isEmpty(appKeys)) {
+            return;
+        }
+        appKeys.forEach(appKey -> appStats.put(appKey, new ConcurrentHashMap<>()));
+    }
+
+    @Override
+    public List<MetricSample> export(MetricsCategory category) {
+        List<MetricSample> list = new ArrayList<>();
+        for (MetricsKey metricsKey : appStats.keySet()) {
+            Map<ApplicationMetric, AtomicLong> stringAtomicLongMap = appStats.get(metricsKey);
+            for (ApplicationMetric registerKeyMetric : stringAtomicLongMap.keySet()) {
+                list.add(new GaugeMetricSample<>(metricsKey, registerKeyMetric.getTags(), category, stringAtomicLongMap, value -> value.get(registerKeyMetric).get()));
+            }
+        }
+        return list;
+    }
+
+    public void incrMetricsNum(MetricsKey metricsKey, String name) {
+        if (!appStats.containsKey(metricsKey)) {
+            return;
+        }
+        ApplicationMetric applicationMetric = new ApplicationMetric(getApplicationModel());
+        applicationMetric.setExtraInfo(Collections.singletonMap(RegistryConstants.REGISTRY_CLUSTER_KEY.toLowerCase(), name));
+        appStats.get(metricsKey).computeIfAbsent(applicationMetric, k -> new AtomicLong(0L)).getAndAdd(SELF_INCREMENT_SIZE);
+        MetricsSupport.fillZero(appStats);
+    }
+
+    public Map<MetricsKey, Map<ApplicationMetric, AtomicLong>> getAppStats() {
+        return appStats;
+    }
+}
diff --git a/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/RegistryEvent.java b/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/RegistryEvent.java
index 5ed721c695..97b9bac1ac 100644
--- a/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/RegistryEvent.java
+++ b/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/RegistryEvent.java
@@ -22,9 +22,12 @@ import org.apache.dubbo.metrics.event.TimeCounterEvent;
 import org.apache.dubbo.metrics.model.key.MetricsKey;
 import org.apache.dubbo.metrics.model.key.MetricsLevel;
 import org.apache.dubbo.metrics.model.key.TypeWrapper;
+import org.apache.dubbo.metrics.registry.RegistryMetricsConstants;
 import org.apache.dubbo.metrics.registry.collector.RegistryMetricsCollector;
 import org.apache.dubbo.rpc.model.ApplicationModel;
 
+import java.util.Collections;
+import java.util.List;
 import java.util.Map;
 
 import static org.apache.dubbo.metrics.MetricsConstants.ATTACHMENT_DIRECTORY_MAP;
@@ -48,18 +51,25 @@ public class RegistryEvent extends TimeCounterEvent {
     }
 
     private static final TypeWrapper REGISTER_EVENT = new TypeWrapper(MetricsLevel.APP, MetricsKey.REGISTER_METRIC_REQUESTS, MetricsKey.REGISTER_METRIC_REQUESTS_SUCCEED, MetricsKey.REGISTER_METRIC_REQUESTS_FAILED);
-    public static RegistryEvent toRegisterEvent(ApplicationModel applicationModel) {
-        return new RegistryEvent(applicationModel, REGISTER_EVENT);
+
+    public static RegistryEvent toRegisterEvent(ApplicationModel applicationModel, List<String> registryClusterNames) {
+        RegistryEvent registryEvent = new RegistryEvent(applicationModel, REGISTER_EVENT);
+        registryEvent.putAttachment(RegistryMetricsConstants.ATTACHMENT_REGISTRY_KEY, registryClusterNames);
+        return registryEvent;
     }
 
 
     private static final TypeWrapper SUBSCRIBE_EVENT = new TypeWrapper(MetricsLevel.APP, MetricsKey.SUBSCRIBE_METRIC_NUM, MetricsKey.SUBSCRIBE_METRIC_NUM_SUCCEED, MetricsKey.SUBSCRIBE_METRIC_NUM_FAILED);
-    public static RegistryEvent toSubscribeEvent(ApplicationModel applicationModel) {
-        return new RegistryEvent(applicationModel, SUBSCRIBE_EVENT);
+
+    public static RegistryEvent toSubscribeEvent(ApplicationModel applicationModel, String registryClusterName) {
+        RegistryEvent ddEvent = new RegistryEvent(applicationModel, SUBSCRIBE_EVENT);
+        ddEvent.putAttachment(RegistryMetricsConstants.ATTACHMENT_REGISTRY_KEY, Collections.singletonList(registryClusterName));
+        return ddEvent;
     }
 
 
     private static final TypeWrapper NOTIFY_EVENT = new TypeWrapper(MetricsLevel.APP, MetricsKey.NOTIFY_METRIC_REQUESTS, MetricsKey.NOTIFY_METRIC_NUM_LAST, (MetricsKey) null);
+
     public static RegistryEvent toNotifyEvent(ApplicationModel applicationModel) {
         return new RegistryEvent(applicationModel, NOTIFY_EVENT) {
             @Override
@@ -70,24 +80,31 @@ public class RegistryEvent extends TimeCounterEvent {
     }
 
     private static final TypeWrapper RS_EVENT = new TypeWrapper(MetricsLevel.SERVICE, MetricsKey.SERVICE_REGISTER_METRIC_REQUESTS, MetricsKey.SERVICE_REGISTER_METRIC_REQUESTS_SUCCEED, MetricsKey.SERVICE_REGISTER_METRIC_REQUESTS_FAILED);
-    public static RegistryEvent toRsEvent(ApplicationModel applicationModel, String serviceKey, int size) {
+
+    public static RegistryEvent toRsEvent(ApplicationModel applicationModel, String serviceKey, int size, List<String> serviceDiscoveryNames) {
         RegistryEvent ddEvent = new RegistryEvent(applicationModel, RS_EVENT);
         ddEvent.putAttachment(ATTACHMENT_KEY_SERVICE, serviceKey);
         ddEvent.putAttachment(ATTACHMENT_KEY_SIZE, size);
+        ddEvent.putAttachment(RegistryMetricsConstants.ATTACHMENT_REGISTRY_KEY, serviceDiscoveryNames);
         return ddEvent;
     }
 
     private static final TypeWrapper SS_EVENT = new TypeWrapper(MetricsLevel.SERVICE, MetricsKey.SERVICE_SUBSCRIBE_METRIC_NUM, MetricsKey.SERVICE_SUBSCRIBE_METRIC_NUM_SUCCEED, MetricsKey.SERVICE_SUBSCRIBE_METRIC_NUM_FAILED);
-    public static RegistryEvent toSsEvent(ApplicationModel applicationModel, String serviceKey) {
+
+    public static RegistryEvent toSsEvent(ApplicationModel applicationModel, String serviceKey, List<String> serviceDiscoveryNames) {
         RegistryEvent ddEvent = new RegistryEvent(applicationModel, SS_EVENT);
         ddEvent.putAttachment(ATTACHMENT_KEY_SERVICE, serviceKey);
+        ddEvent.putAttachment(ATTACHMENT_KEY_SIZE, 1);
+        ddEvent.putAttachment(RegistryMetricsConstants.ATTACHMENT_REGISTRY_KEY, serviceDiscoveryNames);
         return ddEvent;
     }
 
     private static final TypeWrapper DIRECTORY_EVENT = new TypeWrapper(MetricsLevel.APP, MetricsKey.DIRECTORY_METRIC_NUM_VALID, null, null);
-    public static RegistryEvent refreshDirectoryEvent(ApplicationModel applicationModel, Map<MetricsKey, Map<String, Integer>> summaryMap) {
+
+    public static RegistryEvent refreshDirectoryEvent(ApplicationModel applicationModel, Map<MetricsKey, Map<String, Integer>> summaryMap, Map<String, String> attachments) {
         RegistryEvent registryEvent = new RegistryEvent(applicationModel, DIRECTORY_EVENT);
         registryEvent.putAttachment(ATTACHMENT_DIRECTORY_MAP, summaryMap);
+        registryEvent.putAttachments(attachments);
         return registryEvent;
     }
 
diff --git a/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/RegistrySpecListener.java b/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/RegistrySpecListener.java
new file mode 100644
index 0000000000..6e44fea1b1
--- /dev/null
+++ b/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/RegistrySpecListener.java
@@ -0,0 +1,154 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dubbo.metrics.registry.event;
+
+import org.apache.dubbo.common.utils.CollectionUtils;
+import org.apache.dubbo.metrics.collector.CombMetricsCollector;
+import org.apache.dubbo.metrics.event.MetricsEvent;
+import org.apache.dubbo.metrics.listener.AbstractMetricsKeyListener;
+import org.apache.dubbo.metrics.listener.MetricsApplicationListener;
+import org.apache.dubbo.metrics.model.key.MetricsKey;
+import org.apache.dubbo.metrics.model.key.MetricsKeyWrapper;
+import org.apache.dubbo.metrics.model.key.MetricsPlaceValue;
+import org.apache.dubbo.metrics.registry.RegistryMetricsConstants;
+import org.apache.dubbo.metrics.registry.collector.RegistryMetricsCollector;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import static org.apache.dubbo.metrics.MetricsConstants.ATTACHMENT_DIRECTORY_MAP;
+import static org.apache.dubbo.metrics.MetricsConstants.ATTACHMENT_KEY_LAST_NUM_MAP;
+import static org.apache.dubbo.metrics.MetricsConstants.ATTACHMENT_KEY_SERVICE;
+import static org.apache.dubbo.metrics.MetricsConstants.ATTACHMENT_KEY_SIZE;
+import static org.apache.dubbo.metrics.registry.RegistryMetricsConstants.OP_TYPE_DIRECTORY;
+import static org.apache.dubbo.metrics.registry.RegistryMetricsConstants.OP_TYPE_NOTIFY;
+import static org.apache.dubbo.metrics.registry.RegistryMetricsConstants.OP_TYPE_REGISTER;
+
+/**
+ * Different from the general-purpose listener constructor {@link MetricsApplicationListener} ,
+ * it provides registry custom listeners
+ */
+public class RegistrySpecListener {
+
+    /**
+     * Perform auto-increment on the monitored key,
+     * Can use a custom listener instead of this generic operation
+     */
+    public static AbstractMetricsKeyListener onPost(MetricsKey metricsKey, CombMetricsCollector<?> collector) {
+        return AbstractMetricsKeyListener.onEvent(metricsKey,
+            event -> ((RegistryMetricsCollector) collector).incrMetricsNum(metricsKey, getRgs(event))
+        );
+    }
+
+    public static AbstractMetricsKeyListener onFinish(MetricsKey metricsKey, CombMetricsCollector<?> collector) {
+        return AbstractMetricsKeyListener.onFinish(metricsKey,
+            event -> ((RegistryMetricsCollector) collector).incrRegisterFinishNum(metricsKey, OP_TYPE_REGISTER.getType(), getRgs(event), event.getTimePair().calc())
+        );
+    }
+
+    public static AbstractMetricsKeyListener onError(MetricsKey metricsKey, CombMetricsCollector<?> collector) {
+        return AbstractMetricsKeyListener.onError(metricsKey,
+            event -> ((RegistryMetricsCollector) collector).incrRegisterFinishNum(metricsKey, OP_TYPE_REGISTER.getType(), getRgs(event), event.getTimePair().calc())
+        );
+    }
+
+    public static AbstractMetricsKeyListener onPostOfService(MetricsKey metricsKey, MetricsPlaceValue placeType, CombMetricsCollector<?> collector) {
+        return AbstractMetricsKeyListener.onEvent(metricsKey,
+            event -> ((RegistryMetricsCollector) collector).incrServiceRegisterNum(new MetricsKeyWrapper(metricsKey, placeType), getServiceKey(event), getRgs(event), getSize(event))
+        );
+    }
+
+    public static AbstractMetricsKeyListener onFinishOfService(MetricsKey metricsKey, MetricsPlaceValue placeType, CombMetricsCollector<?> collector) {
+        return AbstractMetricsKeyListener.onFinish(metricsKey,
+            event -> ((RegistryMetricsCollector) collector).incrServiceRegisterFinishNum(new MetricsKeyWrapper(metricsKey, placeType), getServiceKey(event), getRgs(event), getSize(event), event.getTimePair().calc())
+        );
+    }
+
+    public static AbstractMetricsKeyListener onErrorOfService(MetricsKey metricsKey, MetricsPlaceValue placeType, CombMetricsCollector<?> collector) {
+        return AbstractMetricsKeyListener.onError(metricsKey,
+            event -> ((RegistryMetricsCollector) collector).incrServiceRegisterFinishNum(new MetricsKeyWrapper(metricsKey, placeType), getServiceKey(event), getRgs(event), getSize(event), event.getTimePair().calc())
+        );
+    }
+
+    /**
+     * Every time an event is triggered, multiple serviceKey related to notify are increment
+     */
+    public static AbstractMetricsKeyListener onFinishOfNotify(MetricsKey metricsKey, MetricsPlaceValue placeType, CombMetricsCollector<?> collector) {
+        return AbstractMetricsKeyListener.onFinish(metricsKey,
+            event ->
+            {
+                collector.addServiceRt(event.appName(), placeType.getType(), event.getTimePair().calc());
+                Map<String, Integer> lastNumMap = Collections.unmodifiableMap(event.getAttachmentValue(ATTACHMENT_KEY_LAST_NUM_MAP));
+                lastNumMap.forEach(
+                    (k, v) -> collector.setNum(new MetricsKeyWrapper(metricsKey, OP_TYPE_NOTIFY), k, v));
+            }
+        );
+    }
+
+    /**
+     * Every time an event is triggered, multiple fixed key related to directory are increment, which has nothing to do with the monitored key
+     */
+    public static AbstractMetricsKeyListener onPostOfDirectory(MetricsKey metricsKey, CombMetricsCollector<?> collector) {
+        return AbstractMetricsKeyListener.onEvent(metricsKey,
+            event -> {
+                Map<MetricsKey, Map<String, Integer>> summaryMap = event.getAttachmentValue(ATTACHMENT_DIRECTORY_MAP);
+                Map<String, String> otherAttachments = new HashMap<>();
+                for (Map.Entry<String, Object> entry : event.getAttachments().entrySet()) {
+                    if (entry.getValue() instanceof String) {
+                        otherAttachments.put(entry.getKey().toLowerCase(Locale.ROOT), (String) entry.getValue());
+                    }
+                }
+                summaryMap.forEach((summaryKey, map) ->
+                    map.forEach(
+                        (k, v) ->
+                        {
+                            if (CollectionUtils.isEmptyMap(otherAttachments)) {
+                                collector.setNum(new MetricsKeyWrapper(summaryKey, OP_TYPE_DIRECTORY), k, v);
+                            } else {
+                                ((RegistryMetricsCollector) collector).setNum(new MetricsKeyWrapper(summaryKey, OP_TYPE_DIRECTORY), k, v, otherAttachments);
+                            }
+                        }
+
+                    ));
+
+            }
+        );
+    }
+
+
+    /**
+     * Get the number of multiple registries
+     */
+    public static List<String> getRgs(MetricsEvent event) {
+        return event.getAttachmentValue(RegistryMetricsConstants.ATTACHMENT_REGISTRY_KEY);
+    }
+
+    /**
+     * Get the exposed number of the protocol
+     */
+    public static int getSize(MetricsEvent event) {
+        return event.getAttachmentValue(ATTACHMENT_KEY_SIZE);
+    }
+
+    public static String getServiceKey(MetricsEvent event) {
+        return event.getAttachmentValue(ATTACHMENT_KEY_SERVICE);
+    }
+
+}
diff --git a/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/RegistrySubDispatcher.java b/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/RegistrySubDispatcher.java
index fd314443db..27cbcb4f61 100644
--- a/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/RegistrySubDispatcher.java
+++ b/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/RegistrySubDispatcher.java
@@ -18,22 +18,15 @@
 package org.apache.dubbo.metrics.registry.event;
 
 import org.apache.dubbo.metrics.event.SimpleMetricsEventMulticaster;
-import org.apache.dubbo.metrics.listener.AbstractMetricsKeyListener;
 import org.apache.dubbo.metrics.listener.MetricsApplicationListener;
-import org.apache.dubbo.metrics.listener.MetricsServiceListener;
 import org.apache.dubbo.metrics.model.key.CategoryOverall;
 import org.apache.dubbo.metrics.model.key.MetricsCat;
 import org.apache.dubbo.metrics.model.key.MetricsKey;
-import org.apache.dubbo.metrics.model.key.MetricsKeyWrapper;
 import org.apache.dubbo.metrics.registry.collector.RegistryMetricsCollector;
 
 import java.util.Arrays;
-import java.util.Collections;
 import java.util.List;
-import java.util.Map;
 
-import static org.apache.dubbo.metrics.MetricsConstants.ATTACHMENT_DIRECTORY_MAP;
-import static org.apache.dubbo.metrics.MetricsConstants.ATTACHMENT_KEY_LAST_NUM_MAP;
 import static org.apache.dubbo.metrics.registry.RegistryMetricsConstants.OP_TYPE_DIRECTORY;
 import static org.apache.dubbo.metrics.registry.RegistryMetricsConstants.OP_TYPE_NOTIFY;
 import static org.apache.dubbo.metrics.registry.RegistryMetricsConstants.OP_TYPE_REGISTER;
@@ -74,55 +67,37 @@ public final class RegistrySubDispatcher extends SimpleMetricsEventMulticaster {
 
 
     /**
-     *  {@link MetricsCat} MetricsCat collection, for better classification processing
-     *  Except for a few custom functions, most of them can build standard event listening functions through the static methods of MetricsApplicationListener
+     * {@link MetricsCat} MetricsCat collection, for better classification processing
+     * Except for a few custom functions, most of them can build standard event listening functions through the static methods of MetricsApplicationListener
      */
     interface MCat {
         // MetricsRegisterListener
-        MetricsCat APPLICATION_REGISTER_POST = new MetricsCat(MetricsKey.REGISTER_METRIC_REQUESTS, MetricsApplicationListener::onPostEventBuild);
-        MetricsCat APPLICATION_REGISTER_FINISH = new MetricsCat(MetricsKey.REGISTER_METRIC_REQUESTS_SUCCEED, MetricsApplicationListener::onFinishEventBuild);
-        MetricsCat APPLICATION_REGISTER_ERROR = new MetricsCat(MetricsKey.REGISTER_METRIC_REQUESTS_FAILED, MetricsApplicationListener::onErrorEventBuild);
+        MetricsCat APPLICATION_REGISTER_POST = new MetricsCat(MetricsKey.REGISTER_METRIC_REQUESTS, RegistrySpecListener::onPost);
+        MetricsCat APPLICATION_REGISTER_FINISH = new MetricsCat(MetricsKey.REGISTER_METRIC_REQUESTS_SUCCEED, RegistrySpecListener::onFinish);
+        MetricsCat APPLICATION_REGISTER_ERROR = new MetricsCat(MetricsKey.REGISTER_METRIC_REQUESTS_FAILED, RegistrySpecListener::onError);
 
         // MetricsSubscribeListener
-        MetricsCat APPLICATION_SUBSCRIBE_POST = new MetricsCat(MetricsKey.SUBSCRIBE_METRIC_NUM, MetricsApplicationListener::onPostEventBuild);
-        MetricsCat APPLICATION_SUBSCRIBE_FINISH = new MetricsCat(MetricsKey.SUBSCRIBE_METRIC_NUM_SUCCEED, MetricsApplicationListener::onFinishEventBuild);
-        MetricsCat APPLICATION_SUBSCRIBE_ERROR = new MetricsCat(MetricsKey.SUBSCRIBE_METRIC_NUM_FAILED, MetricsApplicationListener::onErrorEventBuild);
+        MetricsCat APPLICATION_SUBSCRIBE_POST = new MetricsCat(MetricsKey.SUBSCRIBE_METRIC_NUM, RegistrySpecListener::onPost);
+        MetricsCat APPLICATION_SUBSCRIBE_FINISH = new MetricsCat(MetricsKey.SUBSCRIBE_METRIC_NUM_SUCCEED, RegistrySpecListener::onFinish);
+        MetricsCat APPLICATION_SUBSCRIBE_ERROR = new MetricsCat(MetricsKey.SUBSCRIBE_METRIC_NUM_FAILED, RegistrySpecListener::onError);
 
         // MetricsNotifyListener
         MetricsCat APPLICATION_NOTIFY_POST = new MetricsCat(MetricsKey.NOTIFY_METRIC_REQUESTS, MetricsApplicationListener::onPostEventBuild);
-        MetricsCat APPLICATION_NOTIFY_FINISH = new MetricsCat(MetricsKey.NOTIFY_METRIC_NUM_LAST,
-            (key, placeType, collector) -> AbstractMetricsKeyListener.onFinish(key,
-                event -> {
-                    collector.addServiceRt(event.appName(), placeType.getType(), event.getTimePair().calc());
-                    Map<String, Integer> lastNumMap = Collections.unmodifiableMap(event.getAttachmentValue(ATTACHMENT_KEY_LAST_NUM_MAP));
-                    lastNumMap.forEach(
-                        (k, v) -> collector.setNum(new MetricsKeyWrapper(key, OP_TYPE_NOTIFY), k, v));
-
-                }
-            ));
-
-
-        MetricsCat APPLICATION_DIRECTORY_POST = new MetricsCat(MetricsKey.DIRECTORY_METRIC_NUM_VALID, (key, placeType, collector) -> AbstractMetricsKeyListener.onEvent(key,
-            event ->
-            {
-                Map<MetricsKey, Map<String, Integer>> summaryMap = event.getAttachmentValue(ATTACHMENT_DIRECTORY_MAP);
-                summaryMap.forEach((metricsKey, map) ->
-                    map.forEach(
-                        (k, v) -> collector.setNum(new MetricsKeyWrapper(metricsKey, OP_TYPE_DIRECTORY), k, v)));
-            }
-        ));
+        MetricsCat APPLICATION_NOTIFY_FINISH = new MetricsCat(MetricsKey.NOTIFY_METRIC_NUM_LAST, RegistrySpecListener::onFinishOfNotify);
+
+        MetricsCat APPLICATION_DIRECTORY_POST = new MetricsCat(MetricsKey.DIRECTORY_METRIC_NUM_VALID, RegistrySpecListener::onPostOfDirectory);
 
 
         // MetricsServiceRegisterListener
-        MetricsCat SERVICE_REGISTER_POST = new MetricsCat(MetricsKey.SERVICE_REGISTER_METRIC_REQUESTS, MetricsServiceListener::onPostEventBuild);
-        MetricsCat SERVICE_REGISTER_FINISH = new MetricsCat(MetricsKey.SERVICE_REGISTER_METRIC_REQUESTS_SUCCEED, MetricsServiceListener::onFinishEventBuild);
-        MetricsCat SERVICE_REGISTER_ERROR = new MetricsCat(MetricsKey.SERVICE_REGISTER_METRIC_REQUESTS_FAILED, MetricsServiceListener::onErrorEventBuild);
+        MetricsCat SERVICE_REGISTER_POST = new MetricsCat(MetricsKey.SERVICE_REGISTER_METRIC_REQUESTS, RegistrySpecListener::onPostOfService);
+        MetricsCat SERVICE_REGISTER_FINISH = new MetricsCat(MetricsKey.SERVICE_REGISTER_METRIC_REQUESTS_SUCCEED, RegistrySpecListener::onFinishOfService);
+        MetricsCat SERVICE_REGISTER_ERROR = new MetricsCat(MetricsKey.SERVICE_REGISTER_METRIC_REQUESTS_FAILED, RegistrySpecListener::onErrorOfService);
 
 
         // MetricsServiceSubscribeListener
-        MetricsCat SERVICE_SUBSCRIBE_POST = new MetricsCat(MetricsKey.SERVICE_SUBSCRIBE_METRIC_NUM, MetricsServiceListener::onPostEventBuild);
-        MetricsCat SERVICE_SUBSCRIBE_FINISH = new MetricsCat(MetricsKey.SERVICE_SUBSCRIBE_METRIC_NUM_SUCCEED, MetricsServiceListener::onFinishEventBuild);
-        MetricsCat SERVICE_SUBSCRIBE_ERROR = new MetricsCat(MetricsKey.SERVICE_SUBSCRIBE_METRIC_NUM_FAILED, MetricsServiceListener::onErrorEventBuild);
+        MetricsCat SERVICE_SUBSCRIBE_POST = new MetricsCat(MetricsKey.SERVICE_SUBSCRIBE_METRIC_NUM, RegistrySpecListener::onPostOfService);
+        MetricsCat SERVICE_SUBSCRIBE_FINISH = new MetricsCat(MetricsKey.SERVICE_SUBSCRIBE_METRIC_NUM_SUCCEED, RegistrySpecListener::onFinishOfService);
+        MetricsCat SERVICE_SUBSCRIBE_ERROR = new MetricsCat(MetricsKey.SERVICE_SUBSCRIBE_METRIC_NUM_FAILED, RegistrySpecListener::onErrorOfService);
 
 
     }
diff --git a/dubbo-metrics/dubbo-metrics-registry/src/test/java/org/apache/dubbo/metrics/registry/metrics/collector/RegistryMetricsCollectorTest.java b/dubbo-metrics/dubbo-metrics-registry/src/test/java/org/apache/dubbo/metrics/registry/metrics/collector/RegistryMetricsCollectorTest.java
index 00352ecf2a..ff289caca3 100644
--- a/dubbo-metrics/dubbo-metrics-registry/src/test/java/org/apache/dubbo/metrics/registry/metrics/collector/RegistryMetricsCollectorTest.java
+++ b/dubbo-metrics/dubbo-metrics-registry/src/test/java/org/apache/dubbo/metrics/registry/metrics/collector/RegistryMetricsCollectorTest.java
@@ -17,6 +17,7 @@
 
 package org.apache.dubbo.metrics.registry.metrics.collector;
 
+import com.google.common.collect.Lists;
 import org.apache.dubbo.config.ApplicationConfig;
 import org.apache.dubbo.metrics.event.MetricsDispatcher;
 import org.apache.dubbo.metrics.event.MetricsEventBus;
@@ -30,12 +31,12 @@ import org.apache.dubbo.metrics.registry.collector.RegistryMetricsCollector;
 import org.apache.dubbo.metrics.registry.event.RegistryEvent;
 import org.apache.dubbo.rpc.model.ApplicationModel;
 import org.apache.dubbo.rpc.model.FrameworkModel;
-
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -43,9 +44,11 @@ import java.util.Objects;
 import java.util.stream.Collectors;
 
 import static org.apache.dubbo.common.constants.MetricsConstants.TAG_APPLICATION_NAME;
+import static org.apache.dubbo.metrics.registry.RegistryMetricsConstants.APP_LEVEL_KEYS;
 import static org.apache.dubbo.metrics.registry.RegistryMetricsConstants.OP_TYPE_REGISTER;
 import static org.apache.dubbo.metrics.registry.RegistryMetricsConstants.OP_TYPE_REGISTER_SERVICE;
 import static org.apache.dubbo.metrics.registry.RegistryMetricsConstants.OP_TYPE_SUBSCRIBE_SERVICE;
+import static org.apache.dubbo.metrics.registry.RegistryMetricsConstants.REGISTER_LEVEL_KEYS;
 
 
 class RegistryMetricsCollectorTest {
@@ -74,12 +77,12 @@ class RegistryMetricsCollectorTest {
     @Test
     void testRegisterMetrics() {
 
-        RegistryEvent registryEvent = RegistryEvent.toRegisterEvent(applicationModel);
+        RegistryEvent registryEvent = RegistryEvent.toRegisterEvent(applicationModel, Lists.newArrayList("reg1"));
         MetricsEventBus.post(registryEvent,
             () -> {
                 List<MetricSample> metricSamples = collector.collect();
-                // push success +1 -> other default 0 = RegistryMetricsConstants.APP_LEVEL_KEYS.size()
-                Assertions.assertEquals(RegistryMetricsConstants.APP_LEVEL_KEYS.size(), metricSamples.size());
+                // push success +1 -> other default 0 = APP_LEVEL_KEYS.size()
+                Assertions.assertEquals(APP_LEVEL_KEYS.size() + REGISTER_LEVEL_KEYS.size(), metricSamples.size());
                 Assertions.assertTrue(metricSamples.stream().allMatch(metricSample -> metricSample instanceof GaugeMetricSample));
                 Assertions.assertTrue(metricSamples.stream().anyMatch(metricSample -> ((GaugeMetricSample) metricSample).applyAsDouble() == 1));
                 return null;
@@ -88,12 +91,12 @@ class RegistryMetricsCollectorTest {
 
         // push finish rt +1
         List<MetricSample> metricSamples = collector.collect();
-        // RegistryMetricsConstants.APP_LEVEL_KEYS.size() + rt(5) = 12
-        Assertions.assertEquals(RegistryMetricsConstants.APP_LEVEL_KEYS.size() + 5, metricSamples.size());
+        // APP_LEVEL_KEYS.size() + rt(5) = 12
+        Assertions.assertEquals(APP_LEVEL_KEYS.size() + REGISTER_LEVEL_KEYS.size() + 5, metricSamples.size());
         long c1 = registryEvent.getTimePair().calc();
 
 
-        registryEvent = RegistryEvent.toRegisterEvent(applicationModel);
+        registryEvent = RegistryEvent.toRegisterEvent(applicationModel, Lists.newArrayList("reg1"));
         TimePair lastTimePair = registryEvent.getTimePair();
         MetricsEventBus.post(registryEvent,
             () -> {
@@ -111,7 +114,7 @@ class RegistryMetricsCollectorTest {
         metricSamples = collector.collect();
 
         // num(total+success+error) + rt(5)
-        Assertions.assertEquals(RegistryMetricsConstants.APP_LEVEL_KEYS.size() + 5, metricSamples.size());
+        Assertions.assertEquals(APP_LEVEL_KEYS.size() + REGISTER_LEVEL_KEYS.size() + 5, metricSamples.size());
 
         // calc rt
         for (MetricSample sample : metricSamples) {
@@ -134,8 +137,9 @@ class RegistryMetricsCollectorTest {
     void testServicePushMetrics() {
 
         String serviceName = "demo.gameService";
+        List<String> rcNames = Lists.newArrayList("demo1");
 
-        RegistryEvent registryEvent = RegistryEvent.toRsEvent(applicationModel, serviceName, 2);
+        RegistryEvent registryEvent = RegistryEvent.toRsEvent(applicationModel, serviceName, 2, rcNames);
         MetricsEventBus.post(registryEvent,
             () -> {
                 List<MetricSample> metricSamples = collector.collect();
@@ -154,7 +158,7 @@ class RegistryMetricsCollectorTest {
         Assertions.assertEquals(RegistryMetricsConstants.APP_LEVEL_KEYS.size() + 5 + 2, metricSamples.size());
 
         long c1 = registryEvent.getTimePair().calc();
-        registryEvent = RegistryEvent.toRsEvent(applicationModel, serviceName, 2);
+        registryEvent = RegistryEvent.toRsEvent(applicationModel, serviceName, 2, rcNames);
         TimePair lastTimePair = registryEvent.getTimePair();
         MetricsEventBus.post(registryEvent,
             () -> {
@@ -196,7 +200,7 @@ class RegistryMetricsCollectorTest {
 
         String serviceName = "demo.gameService";
 
-        RegistryEvent subscribeEvent = RegistryEvent.toSsEvent(applicationModel, serviceName);
+        RegistryEvent subscribeEvent = RegistryEvent.toSsEvent(applicationModel, serviceName, Collections.singletonList("demo1"));
         MetricsEventBus.post(subscribeEvent,
             () -> {
                 List<MetricSample> metricSamples = collector.collect();
@@ -216,7 +220,7 @@ class RegistryMetricsCollectorTest {
         Assertions.assertEquals(RegistryMetricsConstants.APP_LEVEL_KEYS.size() + 5 + 2, metricSamples.size());
 
         long c1 = subscribeEvent.getTimePair().calc();
-        subscribeEvent = RegistryEvent.toSsEvent(applicationModel, serviceName);
+        subscribeEvent = RegistryEvent.toSsEvent(applicationModel, serviceName, Collections.singletonList("demo1"));
         TimePair lastTimePair = subscribeEvent.getTimePair();
         MetricsEventBus.post(subscribeEvent,
             () -> {
diff --git a/dubbo-metrics/dubbo-metrics-registry/src/test/java/org/apache/dubbo/metrics/registry/metrics/collector/RegistryMetricsTest.java b/dubbo-metrics/dubbo-metrics-registry/src/test/java/org/apache/dubbo/metrics/registry/metrics/collector/RegistryMetricsTest.java
index 0e1bf07346..5610bef232 100644
--- a/dubbo-metrics/dubbo-metrics-registry/src/test/java/org/apache/dubbo/metrics/registry/metrics/collector/RegistryMetricsTest.java
+++ b/dubbo-metrics/dubbo-metrics-registry/src/test/java/org/apache/dubbo/metrics/registry/metrics/collector/RegistryMetricsTest.java
@@ -17,6 +17,7 @@
 
 package org.apache.dubbo.metrics.registry.metrics.collector;
 
+import com.google.common.collect.Lists;
 import org.apache.dubbo.config.ApplicationConfig;
 import org.apache.dubbo.config.MetricsConfig;
 import org.apache.dubbo.config.context.ConfigManager;
@@ -28,7 +29,6 @@ import org.apache.dubbo.metrics.registry.collector.RegistryMetricsCollector;
 import org.apache.dubbo.metrics.registry.event.RegistryEvent;
 import org.apache.dubbo.rpc.model.ApplicationModel;
 import org.apache.dubbo.rpc.model.FrameworkModel;
-
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -289,19 +289,20 @@ public class RegistryMetricsTest {
     }
 
     RegistryEvent registerEvent() {
-        RegistryEvent event = RegistryEvent.toRegisterEvent(applicationModel);
+        RegistryEvent event = RegistryEvent.toRegisterEvent(applicationModel, Lists.newArrayList("reg1"));
         event.setAvailable(true);
         return event;
     }
 
     RegistryEvent rsEvent() {
-        RegistryEvent event = RegistryEvent.toRsEvent(applicationModel, "TestServiceInterface1", 1);
+        List<String> rcNames = Lists.newArrayList("demo1");
+        RegistryEvent event = RegistryEvent.toRsEvent(applicationModel, "TestServiceInterface1", 1, rcNames);
         event.setAvailable(true);
         return event;
     }
 
     RegistryEvent subscribeEvent() {
-        RegistryEvent event = RegistryEvent.toSubscribeEvent(applicationModel);
+        RegistryEvent event = RegistryEvent.toSubscribeEvent(applicationModel, "registryClusterName_test");
         event.setAvailable(true);
         return event;
     }
diff --git a/dubbo-metrics/dubbo-metrics-registry/src/test/java/org/apache/dubbo/metrics/registry/metrics/collector/RegistryStatCompositeTest.java b/dubbo-metrics/dubbo-metrics-registry/src/test/java/org/apache/dubbo/metrics/registry/metrics/collector/RegistryStatCompositeTest.java
index 5ce44d04ad..3c0d00826e 100644
--- a/dubbo-metrics/dubbo-metrics-registry/src/test/java/org/apache/dubbo/metrics/registry/metrics/collector/RegistryStatCompositeTest.java
+++ b/dubbo-metrics/dubbo-metrics-registry/src/test/java/org/apache/dubbo/metrics/registry/metrics/collector/RegistryStatCompositeTest.java
@@ -17,6 +17,7 @@
 
 package org.apache.dubbo.metrics.registry.metrics.collector;
 
+import org.apache.dubbo.common.constants.RegistryConstants;
 import org.apache.dubbo.config.ApplicationConfig;
 import org.apache.dubbo.metrics.data.ApplicationStatComposite;
 import org.apache.dubbo.metrics.data.BaseStatComposite;
@@ -29,12 +30,15 @@ import org.apache.dubbo.metrics.model.container.LongContainer;
 import org.apache.dubbo.metrics.model.sample.GaugeMetricSample;
 import org.apache.dubbo.metrics.model.sample.MetricSample;
 import org.apache.dubbo.metrics.registry.RegistryMetricsConstants;
+import org.apache.dubbo.metrics.registry.collector.RegistryStatComposite;
 import org.apache.dubbo.rpc.model.ApplicationModel;
 import org.apache.dubbo.rpc.model.FrameworkModel;
+
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
@@ -55,6 +59,7 @@ public class RegistryStatCompositeTest {
     private ApplicationModel applicationModel;
     private String applicationName;
     private BaseStatComposite statComposite;
+    private RegistryStatComposite regStatComposite;
 
     @BeforeEach
     public void setup() {
@@ -83,6 +88,7 @@ public class RegistryStatCompositeTest {
                 rtStatComposite.init(OP_TYPE_REGISTER, OP_TYPE_SUBSCRIBE, OP_TYPE_NOTIFY, OP_TYPE_REGISTER_SERVICE, OP_TYPE_SUBSCRIBE_SERVICE);
             }
         };
+        regStatComposite = new RegistryStatComposite(applicationModel);
     }
 
     @Test
@@ -102,8 +108,10 @@ public class RegistryStatCompositeTest {
 
     @Test
     void testIncrement() {
-        statComposite.incrementApp(REGISTER_METRIC_REQUESTS, 1);
-        Assertions.assertEquals(1L, statComposite.getApplicationStatComposite().getApplicationNumStats().get(REGISTER_METRIC_REQUESTS).get());
+        regStatComposite.incrMetricsNum(REGISTER_METRIC_REQUESTS, "beijing");
+        ApplicationMetric applicationMetric = new ApplicationMetric(applicationModel);
+        applicationMetric.setExtraInfo(Collections.singletonMap(RegistryConstants.REGISTRY_CLUSTER_KEY.toLowerCase(), "beijing"));
+        Assertions.assertEquals(1L, regStatComposite.getAppStats().get(REGISTER_METRIC_REQUESTS).get(applicationMetric).get());
     }
 
     @Test
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistry.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistry.java
index 9c6eb314de..95a196466e 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistry.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistry.java
@@ -17,6 +17,7 @@
 package org.apache.dubbo.registry.client;
 
 import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.constants.RegistryConstants;
 import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
 import org.apache.dubbo.common.logger.LoggerFactory;
 import org.apache.dubbo.common.utils.CollectionUtils;
@@ -33,6 +34,7 @@ import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedLi
 import org.apache.dubbo.registry.support.FailbackRegistry;
 import org.apache.dubbo.rpc.model.ApplicationModel;
 
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -328,7 +330,9 @@ public class ServiceDiscoveryRegistry extends FailbackRegistry {
                 serviceInstancesChangedListener.addListenerAndNotify(url, listener);
                 ServiceInstancesChangedListener finalServiceInstancesChangedListener = serviceInstancesChangedListener;
 
-                MetricsEventBus.post(RegistryEvent.toSsEvent(url.getApplicationModel(), serviceKey),
+                String serviceDiscoveryName = url.getParameter(RegistryConstants.REGISTRY_CLUSTER_KEY, url.getProtocol());
+
+                MetricsEventBus.post(RegistryEvent.toSsEvent(url.getApplicationModel(), serviceKey, Collections.singletonList(serviceDiscoveryName)),
                     () -> {
                         serviceDiscovery.addServiceInstancesChangedListener(finalServiceInstancesChangedListener);
                         return null;
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryDirectory.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryDirectory.java
index baec948745..d5168aaf7c 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryDirectory.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/ServiceDiscoveryRegistryDirectory.java
@@ -21,6 +21,7 @@ import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.URLBuilder;
 import org.apache.dubbo.common.config.configcenter.DynamicConfiguration;
 import org.apache.dubbo.common.constants.CommonConstants;
+import org.apache.dubbo.common.constants.RegistryConstants;
 import org.apache.dubbo.common.extension.ExtensionLoader;
 import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
 import org.apache.dubbo.common.logger.LoggerFactory;
@@ -34,6 +35,7 @@ import org.apache.dubbo.metadata.MetadataInfo;
 import org.apache.dubbo.registry.AddressListener;
 import org.apache.dubbo.registry.Constants;
 import org.apache.dubbo.registry.ProviderFirstParams;
+import org.apache.dubbo.registry.Registry;
 import org.apache.dubbo.registry.integration.AbstractConfiguratorListener;
 import org.apache.dubbo.registry.integration.DynamicDirectory;
 import org.apache.dubbo.rpc.Invocation;
@@ -58,6 +60,7 @@ import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
@@ -66,6 +69,7 @@ import java.util.stream.Collectors;
 import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.DISABLED_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.ENABLED_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.INSTANCE_REGISTER_MODE;
 import static org.apache.dubbo.common.constants.CommonConstants.PROTOCOL_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.SIDE_KEY;
 import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_FAILED_DESTROY_INVOKER;
@@ -73,6 +77,8 @@ import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_FAI
 import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_UNSUPPORTED;
 import static org.apache.dubbo.common.constants.RegistryConstants.DEFAULT_HASHMAP_LOAD_FACTOR;
 import static org.apache.dubbo.common.constants.RegistryConstants.EMPTY_PROTOCOL;
+import static org.apache.dubbo.common.constants.RegistryConstants.REGISTER_MODE_KEY;
+import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_KEY;
 import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_TYPE_KEY;
 import static org.apache.dubbo.common.constants.RegistryConstants.SERVICE_REGISTRY_TYPE;
 import static org.apache.dubbo.registry.Constants.CONFIGURATORS_SUFFIX;
@@ -498,6 +504,18 @@ public class ServiceDiscoveryRegistryDirectory<T> extends DynamicDirectory<T> {
         this.destroyInvokers();
     }
 
+    @Override
+    protected Map<String, String> getDirectoryMeta() {
+        String registryKey = Optional.ofNullable(getRegistry())
+            .map(Registry::getUrl)
+            .map(url -> url.getParameter(RegistryConstants.REGISTRY_CLUSTER_KEY, url.getParameter(RegistryConstants.REGISTRY_KEY, url.getProtocol())))
+            .orElse("unknown");
+        Map<String, String> metas = new HashMap<>();
+        metas.put(REGISTRY_KEY, registryKey);
+        metas.put(REGISTER_MODE_KEY, INSTANCE_REGISTER_MODE);
+        return metas;
+    }
+
     /**
      * Check whether the invoker in the cache needs to be destroyed
      * If set attribute of url: refer.autodestroy=false, the invokers will only increase without decreasing,there may be a refer leak
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/ServiceInstanceMetadataUtils.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/ServiceInstanceMetadataUtils.java
index 13434ceabc..fbb858aac0 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/ServiceInstanceMetadataUtils.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/ServiceInstanceMetadataUtils.java
@@ -17,6 +17,7 @@
 package org.apache.dubbo.registry.client.metadata;
 
 import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.constants.RegistryConstants;
 import org.apache.dubbo.common.extension.ExtensionLoader;
 import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
 import org.apache.dubbo.common.logger.LoggerFactory;
@@ -24,6 +25,8 @@ import org.apache.dubbo.common.utils.JsonUtils;
 import org.apache.dubbo.common.utils.StringUtils;
 import org.apache.dubbo.metadata.MetadataInfo;
 import org.apache.dubbo.metadata.MetadataService;
+import org.apache.dubbo.metrics.event.MetricsEventBus;
+import org.apache.dubbo.metrics.registry.event.RegistryEvent;
 import org.apache.dubbo.registry.client.DefaultServiceInstance;
 import org.apache.dubbo.registry.client.DefaultServiceInstance.Endpoint;
 import org.apache.dubbo.registry.client.ServiceDiscovery;
@@ -33,6 +36,7 @@ import org.apache.dubbo.registry.support.RegistryManager;
 import org.apache.dubbo.rpc.model.ApplicationModel;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
@@ -44,6 +48,7 @@ import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.PORT_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.PROTOCOL_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.TIMESTAMP_KEY;
+import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_KEY;
 import static org.apache.dubbo.common.utils.StringUtils.isBlank;
 import static org.apache.dubbo.registry.integration.InterfaceCompatibleRegistryProtocol.DEFAULT_REGISTER_PROVIDER_KEYS;
 import static org.apache.dubbo.rpc.Constants.DEPRECATED_KEY;
@@ -201,7 +206,22 @@ public class ServiceInstanceMetadataUtils {
         LOGGER.info("Start registering instance address to registry.");
         RegistryManager registryManager = applicationModel.getBeanFactory().getBean(RegistryManager.class);
         // register service instance
-        registryManager.getServiceDiscoveries().forEach(ServiceDiscovery::register);
+        List<ServiceDiscovery> serviceDiscoveries = registryManager.getServiceDiscoveries();
+        for (ServiceDiscovery serviceDiscovery : serviceDiscoveries) {
+            MetricsEventBus.post(RegistryEvent.toRegisterEvent(applicationModel,
+                    Collections.singletonList(getServiceDiscoveryName(serviceDiscovery))),
+                () -> {
+                    // register service instance
+                    serviceDiscoveries.forEach(ServiceDiscovery::register);
+                    return null;
+                }
+            );
+        }
+    }
+
+    private static String getServiceDiscoveryName(ServiceDiscovery serviceDiscovery) {
+        return serviceDiscovery.getUrl().getParameter(RegistryConstants.REGISTRY_CLUSTER_KEY,
+            serviceDiscovery.getUrl().getParameter(REGISTRY_KEY));
     }
 
     public static void refreshMetadataAndInstance(ApplicationModel applicationModel) {
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryDirectory.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryDirectory.java
index a484453322..cf33c533f1 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryDirectory.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryDirectory.java
@@ -19,6 +19,7 @@ package org.apache.dubbo.registry.integration;
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.URLBuilder;
 import org.apache.dubbo.common.config.configcenter.DynamicConfiguration;
+import org.apache.dubbo.common.constants.RegistryConstants;
 import org.apache.dubbo.common.extension.ExtensionLoader;
 import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
 import org.apache.dubbo.common.logger.LoggerFactory;
@@ -33,6 +34,7 @@ import org.apache.dubbo.common.utils.UrlUtils;
 import org.apache.dubbo.metrics.event.MetricsEventBus;
 import org.apache.dubbo.metrics.registry.event.RegistryEvent;
 import org.apache.dubbo.registry.AddressListener;
+import org.apache.dubbo.registry.Registry;
 import org.apache.dubbo.remoting.Constants;
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.Protocol;
@@ -62,6 +64,7 @@ import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.DISABLED_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_PROTOCOL;
 import static org.apache.dubbo.common.constants.CommonConstants.ENABLED_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_REGISTER_MODE;
 import static org.apache.dubbo.common.constants.CommonConstants.PROTOCOL_KEY;
 import static org.apache.dubbo.common.constants.CommonConstants.SIDE_KEY;
 import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_FAILED_INIT_SERIALIZATION_OPTIMIZER;
@@ -79,6 +82,8 @@ import static org.apache.dubbo.common.constants.RegistryConstants.DEFAULT_HASHMA
 import static org.apache.dubbo.common.constants.RegistryConstants.DYNAMIC_CONFIGURATORS_CATEGORY;
 import static org.apache.dubbo.common.constants.RegistryConstants.EMPTY_PROTOCOL;
 import static org.apache.dubbo.common.constants.RegistryConstants.PROVIDERS_CATEGORY;
+import static org.apache.dubbo.common.constants.RegistryConstants.REGISTER_MODE_KEY;
+import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_KEY;
 import static org.apache.dubbo.common.constants.RegistryConstants.ROUTERS_CATEGORY;
 import static org.apache.dubbo.common.constants.RegistryConstants.ROUTE_PROTOCOL;
 import static org.apache.dubbo.registry.Constants.CONFIGURATORS_SUFFIX;
@@ -130,7 +135,8 @@ public class RegistryDirectory<T> extends DynamicDirectory<T> {
         }
 
         ApplicationModel applicationModel = url.getApplicationModel();
-        MetricsEventBus.post(RegistryEvent.toSubscribeEvent(applicationModel), () ->
+        String registryClusterName = registry.getUrl().getParameter(RegistryConstants.REGISTRY_CLUSTER_KEY, registry.getUrl().getParameter(PROTOCOL_KEY));
+        MetricsEventBus.post(RegistryEvent.toSubscribeEvent(applicationModel,registryClusterName), () ->
             {
                 super.subscribe(url);
                 return null;
@@ -644,6 +650,18 @@ public class RegistryDirectory<T> extends DynamicDirectory<T> {
         return false;
     }
 
+    @Override
+    protected Map<String, String> getDirectoryMeta() {
+        String registryKey = Optional.ofNullable(getRegistry())
+            .map(Registry::getUrl)
+            .map(url -> url.getParameter(RegistryConstants.REGISTRY_CLUSTER_KEY, url.getProtocol()))
+            .orElse("unknown");
+        Map<String, String> metas = new HashMap<>();
+        metas.put(REGISTRY_KEY, registryKey);
+        metas.put(REGISTER_MODE_KEY, INTERFACE_REGISTER_MODE);
+        return metas;
+    }
+
     private boolean isNotCompatibleFor26x(URL url) {
         return StringUtils.isEmpty(url.getParameter(COMPATIBLE_CONFIG_KEY));
     }
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java
index b69376e784..9db1323951 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java
@@ -18,6 +18,7 @@ package org.apache.dubbo.registry.integration;
 
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.config.configcenter.DynamicConfiguration;
+import org.apache.dubbo.common.constants.RegistryConstants;
 import org.apache.dubbo.common.deploy.ApplicationDeployer;
 import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
 import org.apache.dubbo.common.logger.LoggerFactory;
@@ -29,6 +30,8 @@ import org.apache.dubbo.common.utils.ConcurrentHashSet;
 import org.apache.dubbo.common.utils.NamedThreadFactory;
 import org.apache.dubbo.common.utils.StringUtils;
 import org.apache.dubbo.common.utils.UrlUtils;
+import org.apache.dubbo.metrics.event.MetricsEventBus;
+import org.apache.dubbo.metrics.registry.event.RegistryEvent;
 import org.apache.dubbo.registry.NotifyListener;
 import org.apache.dubbo.registry.Registry;
 import org.apache.dubbo.registry.RegistryFactory;
@@ -61,9 +64,11 @@ import org.apache.dubbo.rpc.protocol.InvokerWrapper;
 import org.apache.dubbo.rpc.support.ProtocolUtils;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
@@ -224,7 +229,16 @@ public class RegistryProtocol implements Protocol, ScopeModelAware {
         ApplicationDeployer deployer = registeredProviderUrl.getOrDefaultApplicationModel().getDeployer();
         try {
             deployer.increaseServiceRefreshCount();
-            registry.register(registeredProviderUrl);
+            String registryName = Optional.ofNullable(registry.getUrl())
+                .map(u -> u.getParameter(RegistryConstants.REGISTRY_CLUSTER_KEY,
+                    UrlUtils.isServiceDiscoveryURL(u) ? u.getParameter(REGISTRY_KEY) : u.getProtocol()))
+                .filter(StringUtils::isNotEmpty)
+                .orElse("unknown");
+            MetricsEventBus.post(RegistryEvent.toRsEvent(registeredProviderUrl.getApplicationModel(), registeredProviderUrl.getServiceKey(), 1, Collections.singletonList(registryName)),
+                () -> {
+                    registry.register(registeredProviderUrl);
+                    return null;
+                });
         } finally {
             deployer.decreaseServiceRefreshCount();
         }