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 c041fa1f93 Observability task: registration center (#11493)
c041fa1f93 is described below

commit c041fa1f9331a96f65f5fe006f035d4679b41c04
Author: wxbty <[email protected]>
AuthorDate: Sat Feb 25 17:22:44 2023 +0800

    Observability task: registration center (#11493)
---
 dubbo-cluster/pom.xml                              |   6 +
 .../rpc/cluster/directory/AbstractDirectory.java   |  47 ++--
 .../rpc/cluster/directory/StaticDirectoryTest.java |   3 +
 .../support/AbstractClusterInvokerTest.java        |  19 +-
 .../support/ConnectivityValidationTest.java        |   3 +
 .../support/FailoverClusterInvokerTest.java        |  12 +-
 .../support/wrapper/MockClusterInvokerTest.java    |   3 +
 .../wrapper/MockProviderRpcExceptionTest.java      |   3 +
 .../org/apache/dubbo/config/MetricsConfig.java     |  15 +-
 dubbo-config/dubbo-config-api/pom.xml              |  12 ++
 .../config/deploy/DefaultApplicationDeployer.java  |   8 +
 dubbo-distribution/dubbo-all/pom.xml               |   8 +
 dubbo-distribution/dubbo-bom/pom.xml               |   5 +
 dubbo-metrics/dubbo-metrics-api/pom.xml            |   5 -
 .../event/SimpleMetricsEventMulticaster.java       |   2 +-
 .../org/apache/dubbo/metrics/model/MetricsKey.java |  23 +-
 .../org/apache/dubbo/metrics/model/TimePair.java   |   5 +
 dubbo-metrics/dubbo-metrics-default/pom.xml        |   6 +
 .../pom.xml                                        |  12 +-
 .../collector/RegistryMetricsCollector.java        | 123 +++++++++++
 .../collector/stat/RegistryStatComposite.java      | 240 +++++++++++++++++++++
 .../registry/collector/stat/ServiceKeyMetric.java  |  83 +++++++
 .../registry/event/MetricsDirectoryListener.java   |  43 ++++
 .../registry/event/MetricsNotifyListener.java      |  54 +++++
 .../registry/event/MetricsRegisterListener.java    |  52 +++++
 .../registry/event/MetricsSubscribeListener.java   |  52 +++++
 .../metrics/registry/event/RegistryEvent.java      | 156 ++++++++++++++
 .../event/RegistryMetricsEventMulticaster.java}    |  25 +--
 ...apache.dubbo.metrics.collector.MetricsCollector |   1 +
 .../collector/RegistryMetricsCollectorTest.java    | 115 ++++++++++
 .../collector/RegistryMetricsSampleTest.java       |  97 +++++++++
 .../registry/metrics/model/MethodMetricTest.java   |  79 +++++++
 .../model/sample/GaugeMetricSampleTest.java        |  63 ++++++
 dubbo-metrics/pom.xml                              |   1 +
 dubbo-registry/dubbo-registry-api/pom.xml          |   7 +
 .../listener/ServiceInstancesChangedListener.java  |  48 +++--
 .../registry/integration/RegistryDirectory.java    |  39 ++--
 dubbo-test/dubbo-dependencies-all/pom.xml          |   5 +
 38 files changed, 1385 insertions(+), 95 deletions(-)

diff --git a/dubbo-cluster/pom.xml b/dubbo-cluster/pom.xml
index f7235cf7d6..744d0f9f11 100644
--- a/dubbo-cluster/pom.xml
+++ b/dubbo-cluster/pom.xml
@@ -68,5 +68,11 @@
             <version>${project.parent.version}</version>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-metrics-registry</artifactId>
+            <version>${project.parent.version}</version>
+            <scope>compile</scope>
+        </dependency>
     </dependencies>
 </project>
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 eb69309f24..6af8bdb98d 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
@@ -16,21 +16,6 @@
  */
 package org.apache.dubbo.rpc.cluster.directory;
 
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.ScheduledFuture;
-import java.util.concurrent.Semaphore;
-import java.util.concurrent.ThreadLocalRandom;
-import java.util.concurrent.TimeUnit;
-import java.util.stream.Collectors;
-
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.Version;
 import org.apache.dubbo.common.config.Configuration;
@@ -42,6 +27,8 @@ import 
org.apache.dubbo.common.threadpool.manager.FrameworkExecutorRepository;
 import org.apache.dubbo.common.utils.ConcurrentHashSet;
 import org.apache.dubbo.common.utils.NetUtils;
 import org.apache.dubbo.common.utils.StringUtils;
+import org.apache.dubbo.metrics.event.GlobalMetricsEventMulticaster;
+import org.apache.dubbo.metrics.registry.event.RegistryEvent;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.RpcContext;
@@ -54,6 +41,21 @@ import org.apache.dubbo.rpc.cluster.router.state.BitList;
 import org.apache.dubbo.rpc.cluster.support.ClusterUtils;
 import org.apache.dubbo.rpc.model.ApplicationModel;
 
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
 import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER;
 import static 
org.apache.dubbo.common.constants.CommonConstants.DEFAULT_RECONNECT_TASK_PERIOD;
 import static 
org.apache.dubbo.common.constants.CommonConstants.DEFAULT_RECONNECT_TASK_TRY_COUNT;
@@ -128,6 +130,10 @@ public abstract class AbstractDirectory<T> implements 
Directory<T> {
      */
     private final int reconnectTaskPeriod;
 
+    private final GlobalMetricsEventMulticaster eventMulticaster;
+
+    private ApplicationModel applicationModel;
+
     public AbstractDirectory(URL url) {
         this(url, null, false);
     }
@@ -153,7 +159,7 @@ public abstract class AbstractDirectory<T> implements 
Directory<T> {
         }
 
         // remove some local only parameters
-        ApplicationModel applicationModel = url.getOrDefaultApplicationModel();
+        applicationModel = url.getOrDefaultApplicationModel();
         this.queryMap = 
applicationModel.getBeanFactory().getBean(ClusterUtils.class).mergeLocalParams(queryMap);
 
         if (consumerUrl == null) {
@@ -179,12 +185,14 @@ public abstract class AbstractDirectory<T> implements 
Directory<T> {
         this.reconnectTaskTryCount = 
configuration.getInt(RECONNECT_TASK_TRY_COUNT, 
DEFAULT_RECONNECT_TASK_TRY_COUNT);
         this.reconnectTaskPeriod = configuration.getInt(RECONNECT_TASK_PERIOD, 
DEFAULT_RECONNECT_TASK_PERIOD);
         setRouterChain(routerChain);
+
+        eventMulticaster = 
applicationModel.getBeanFactory().getBean(GlobalMetricsEventMulticaster.class);
     }
 
     @Override
     public List<Invoker<T>> list(Invocation invocation) throws RpcException {
         if (destroyed) {
-            throw new RpcException("Directory of type " + 
this.getClass().getSimpleName() +  " already destroyed for service " + 
getConsumerUrl().getServiceKey() + " from registry " + getUrl());
+            throw new RpcException("Directory of type " + 
this.getClass().getSimpleName() + " already destroyed for service " + 
getConsumerUrl().getServiceKey() + " from registry " + getUrl());
         }
 
         BitList<Invoker<T>> availableInvokers;
@@ -381,6 +389,7 @@ public abstract class AbstractDirectory<T> implements 
Directory<T> {
 
     @Override
     public void addDisabledInvoker(Invoker<T> invoker) {
+        eventMulticaster.publishEvent(new 
RegistryEvent.MetricsDirectoryEvent(applicationModel, 
RegistryEvent.Type.D_DISABLE));
         if (invokers.contains(invoker)) {
             disabledInvokers.add(invoker);
             removeValidInvoker(invoker);
@@ -390,6 +399,7 @@ public abstract class AbstractDirectory<T> implements 
Directory<T> {
 
     @Override
     public void recoverDisabledInvoker(Invoker<T> invoker) {
+        eventMulticaster.publishEvent(new 
RegistryEvent.MetricsDirectoryEvent(applicationModel, 
RegistryEvent.Type.D_RECOVER_DISABLE));
         if (disabledInvokers.remove(invoker)) {
             try {
                 addValidInvoker(invoker);
@@ -453,6 +463,7 @@ public abstract class AbstractDirectory<T> implements 
Directory<T> {
         this.invokers = invokers;
         refreshInvokerInternal();
         this.invokersInitialized = true;
+        eventMulticaster.publishEvent(new 
RegistryEvent.MetricsDirectoryEvent(applicationModel, 
RegistryEvent.Type.D_CURRENT, invokers.size()));
     }
 
     protected void destroyInvokers() {
@@ -463,12 +474,14 @@ public abstract class AbstractDirectory<T> implements 
Directory<T> {
     }
 
     private boolean addValidInvoker(Invoker<T> invoker) {
+        eventMulticaster.publishEvent(new 
RegistryEvent.MetricsDirectoryEvent(applicationModel, 
RegistryEvent.Type.D_VALID));
         synchronized (this.validInvokers) {
             return this.validInvokers.add(invoker);
         }
     }
 
     private boolean removeValidInvoker(Invoker<T> invoker) {
+        eventMulticaster.publishEvent(new 
RegistryEvent.MetricsDirectoryEvent(applicationModel, 
RegistryEvent.Type.D_UN_VALID));
         synchronized (this.validInvokers) {
             return this.validInvokers.remove(invoker);
         }
diff --git 
a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/directory/StaticDirectoryTest.java
 
b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/directory/StaticDirectoryTest.java
index 379d50b1c1..62787ccd2b 100644
--- 
a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/directory/StaticDirectoryTest.java
+++ 
b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/directory/StaticDirectoryTest.java
@@ -19,6 +19,7 @@ package org.apache.dubbo.rpc.cluster.directory;
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.utils.Holder;
 import org.apache.dubbo.common.utils.NetUtils;
+import org.apache.dubbo.metrics.event.GlobalMetricsEventMulticaster;
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.RpcInvocation;
 import org.apache.dubbo.rpc.cluster.router.MockInvoker;
@@ -26,6 +27,7 @@ import 
org.apache.dubbo.rpc.cluster.router.condition.ConditionStateRouterFactory
 import org.apache.dubbo.rpc.cluster.router.state.BitList;
 import org.apache.dubbo.rpc.cluster.router.state.StateRouter;
 
+import org.apache.dubbo.rpc.model.ApplicationModel;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 
@@ -60,6 +62,7 @@ class StaticDirectoryTest {
 
 
         List<Invoker<String>> filteredInvokers = 
router.route(invokers.clone(), URL.valueOf("consumer://" + 
NetUtils.getLocalHost() + "/com.foo.BarService"), new RpcInvocation(), false, 
new Holder<>());
+        
ApplicationModel.defaultModel().getBeanFactory().registerBean(GlobalMetricsEventMulticaster.class);
         StaticDirectory<String> staticDirectory = new 
StaticDirectory<>(filteredInvokers);
         boolean isAvailable = staticDirectory.isAvailable();
         Assertions.assertTrue(!isAvailable);
diff --git 
a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/AbstractClusterInvokerTest.java
 
b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/AbstractClusterInvokerTest.java
index 665a9ef65d..9028114aab 100644
--- 
a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/AbstractClusterInvokerTest.java
+++ 
b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/AbstractClusterInvokerTest.java
@@ -16,18 +16,11 @@
  */
 package org.apache.dubbo.rpc.cluster.support;
 
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.atomic.AtomicLong;
-
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.extension.ExtensionLoader;
 import org.apache.dubbo.common.utils.NetUtils;
 import org.apache.dubbo.common.utils.StringUtils;
+import org.apache.dubbo.metrics.event.GlobalMetricsEventMulticaster;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.Result;
@@ -41,6 +34,7 @@ import org.apache.dubbo.rpc.cluster.filter.DemoService;
 import org.apache.dubbo.rpc.cluster.loadbalance.LeastActiveLoadBalance;
 import org.apache.dubbo.rpc.cluster.loadbalance.RandomLoadBalance;
 import org.apache.dubbo.rpc.cluster.loadbalance.RoundRobinLoadBalance;
+import org.apache.dubbo.rpc.model.ApplicationModel;
 import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.Assertions;
@@ -50,6 +44,14 @@ import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
 import org.mockito.Mockito;
 
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+
 import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER;
 import static 
org.apache.dubbo.common.constants.CommonConstants.ENABLE_CONNECTIVITY_VALIDATION;
 import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY;
@@ -104,6 +106,7 @@ class AbstractClusterInvokerTest {
     @SuppressWarnings({"unchecked"})
     @BeforeEach
     public void setUp() throws Exception {
+        
ApplicationModel.defaultModel().getBeanFactory().registerBean(GlobalMetricsEventMulticaster.class);
         Map<String, Object> attributes = new HashMap<>();
         attributes.put("application", "abstractClusterInvokerTest");
         url = url.putAttribute(REFER_KEY, attributes);
diff --git 
a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/ConnectivityValidationTest.java
 
b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/ConnectivityValidationTest.java
index 0ccb77b8d5..59fc9cfd10 100644
--- 
a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/ConnectivityValidationTest.java
+++ 
b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/ConnectivityValidationTest.java
@@ -19,6 +19,7 @@ package org.apache.dubbo.rpc.cluster.support;
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.constants.CommonConstants;
 import org.apache.dubbo.common.utils.CollectionUtils;
+import org.apache.dubbo.metrics.event.GlobalMetricsEventMulticaster;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.Result;
@@ -28,6 +29,7 @@ import org.apache.dubbo.rpc.cluster.Directory;
 import org.apache.dubbo.rpc.cluster.LoadBalance;
 import org.apache.dubbo.rpc.cluster.directory.StaticDirectory;
 
+import org.apache.dubbo.rpc.model.ApplicationModel;
 import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.Assertions;
@@ -70,6 +72,7 @@ class ConnectivityValidationTest {
 
     @BeforeEach
     public void setup() {
+        
ApplicationModel.defaultModel().getBeanFactory().registerBean(GlobalMetricsEventMulticaster.class);
         invoker1 = Mockito.mock(Invoker.class);
         invoker2 = Mockito.mock(Invoker.class);
         invoker3 = Mockito.mock(Invoker.class);
diff --git 
a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/FailoverClusterInvokerTest.java
 
b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/FailoverClusterInvokerTest.java
index 736d5a71d2..de20e2d262 100644
--- 
a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/FailoverClusterInvokerTest.java
+++ 
b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/FailoverClusterInvokerTest.java
@@ -16,11 +16,8 @@
  */
 package org.apache.dubbo.rpc.cluster.support;
 
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.Callable;
-
 import org.apache.dubbo.common.URL;
+import org.apache.dubbo.metrics.event.GlobalMetricsEventMulticaster;
 import org.apache.dubbo.rpc.AppResponse;
 import org.apache.dubbo.rpc.AsyncRpcResult;
 import org.apache.dubbo.rpc.Invocation;
@@ -33,10 +30,15 @@ import org.apache.dubbo.rpc.cluster.Directory;
 import org.apache.dubbo.rpc.cluster.SingleRouterChain;
 import org.apache.dubbo.rpc.cluster.directory.StaticDirectory;
 import org.apache.dubbo.rpc.cluster.router.state.BitList;
+import org.apache.dubbo.rpc.model.ApplicationModel;
 import org.apache.dubbo.rpc.protocol.AbstractInvoker;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Callable;
+
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertSame;
@@ -65,7 +67,7 @@ class FailoverClusterInvokerTest {
 
     @BeforeEach
     public void setUp() throws Exception {
-
+        
ApplicationModel.defaultModel().getBeanFactory().registerBean(GlobalMetricsEventMulticaster.class);
         dic = mock(Directory.class);
 
         given(dic.getUrl()).willReturn(url);
diff --git 
a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/wrapper/MockClusterInvokerTest.java
 
b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/wrapper/MockClusterInvokerTest.java
index 62646cb201..7db7129e81 100644
--- 
a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/wrapper/MockClusterInvokerTest.java
+++ 
b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/wrapper/MockClusterInvokerTest.java
@@ -18,6 +18,7 @@ package org.apache.dubbo.rpc.cluster.support.wrapper;
 
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.extension.ExtensionLoader;
+import org.apache.dubbo.metrics.event.GlobalMetricsEventMulticaster;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.Protocol;
@@ -28,6 +29,7 @@ import org.apache.dubbo.rpc.RpcInvocation;
 import org.apache.dubbo.rpc.cluster.LoadBalance;
 import org.apache.dubbo.rpc.cluster.directory.StaticDirectory;
 import org.apache.dubbo.rpc.cluster.support.AbstractClusterInvoker;
+import org.apache.dubbo.rpc.model.ApplicationModel;
 import org.apache.dubbo.rpc.support.MockProtocol;
 
 import org.junit.jupiter.api.Assertions;
@@ -49,6 +51,7 @@ class MockClusterInvokerTest {
 
     @BeforeEach
     public void beforeMethod() {
+        
ApplicationModel.defaultModel().getBeanFactory().registerBean(GlobalMetricsEventMulticaster.class);
         invokers.clear();
     }
 
diff --git 
a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/wrapper/MockProviderRpcExceptionTest.java
 
b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/wrapper/MockProviderRpcExceptionTest.java
index 89917f1de9..c0a737280f 100644
--- 
a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/wrapper/MockProviderRpcExceptionTest.java
+++ 
b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/wrapper/MockProviderRpcExceptionTest.java
@@ -18,6 +18,7 @@ package org.apache.dubbo.rpc.cluster.support.wrapper;
 
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.extension.ExtensionLoader;
+import org.apache.dubbo.metrics.event.GlobalMetricsEventMulticaster;
 import org.apache.dubbo.rpc.Invocation;
 import org.apache.dubbo.rpc.Invoker;
 import org.apache.dubbo.rpc.ProxyFactory;
@@ -28,6 +29,7 @@ import org.apache.dubbo.rpc.cluster.LoadBalance;
 import org.apache.dubbo.rpc.cluster.directory.StaticDirectory;
 import org.apache.dubbo.rpc.cluster.support.AbstractClusterInvoker;
 
+import org.apache.dubbo.rpc.model.ApplicationModel;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -46,6 +48,7 @@ class MockProviderRpcExceptionTest {
 
     @BeforeEach
     public void beforeMethod() {
+        
ApplicationModel.defaultModel().getBeanFactory().registerBean(GlobalMetricsEventMulticaster.class);
         invokers.clear();
     }
 
diff --git 
a/dubbo-common/src/main/java/org/apache/dubbo/config/MetricsConfig.java 
b/dubbo-common/src/main/java/org/apache/dubbo/config/MetricsConfig.java
index 1b8340138b..07a1e5f425 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/MetricsConfig.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/config/MetricsConfig.java
@@ -41,7 +41,12 @@ public class MetricsConfig extends AbstractConfig {
     private Boolean enableJvmMetrics;
 
     /**
-     * Enable jvm metrics when collecting.
+     * Enable registry metrics.
+     */
+    private Boolean enableRegistryMetrics;
+
+    /**
+     * Enable metadata metrics.
      */
     private Boolean enableMetadataMetrics;
 
@@ -103,6 +108,14 @@ public class MetricsConfig extends AbstractConfig {
         this.enableJvmMetrics = enableJvmMetrics;
     }
 
+    public Boolean getEnableRegistryMetrics() {
+        return enableRegistryMetrics;
+    }
+
+    public void setEnableRegistryMetrics(Boolean enableRegistryMetrics) {
+        this.enableRegistryMetrics = enableRegistryMetrics;
+    }
+
     public String getPort() {
         return port;
     }
diff --git a/dubbo-config/dubbo-config-api/pom.xml 
b/dubbo-config/dubbo-config-api/pom.xml
index 2ba42d26a3..42ee10a4e0 100644
--- a/dubbo-config/dubbo-config-api/pom.xml
+++ b/dubbo-config/dubbo-config-api/pom.xml
@@ -42,12 +42,24 @@
             <version>${project.parent.version}</version>
         </dependency>
 
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-metrics-api</artifactId>
+            <version>${project.parent.version}</version>
+        </dependency>
+
         <dependency>
             <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo-metrics-default</artifactId>
             <version>${project.parent.version}</version>
         </dependency>
 
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-metrics-registry</artifactId>
+            <version>${project.parent.version}</version>
+        </dependency>
+
         <dependency>
             <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo-metrics-metadata</artifactId>
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 c526864ab4..f605c6d09e 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
@@ -51,6 +51,9 @@ import org.apache.dubbo.config.utils.ConfigValidationUtils;
 import org.apache.dubbo.metadata.report.MetadataReportFactory;
 import org.apache.dubbo.metadata.report.MetadataReportInstance;
 import org.apache.dubbo.metrics.collector.DefaultMetricsCollector;
+import org.apache.dubbo.metrics.event.GlobalMetricsEventMulticaster;
+import org.apache.dubbo.metrics.model.TimePair;
+import org.apache.dubbo.metrics.registry.event.RegistryEvent;
 import org.apache.dubbo.metrics.report.MetricsReporter;
 import org.apache.dubbo.metrics.report.MetricsReporterFactory;
 import org.apache.dubbo.metrics.service.MetricsServiceExporter;
@@ -825,10 +828,15 @@ public class DefaultApplicationDeployer extends 
AbstractDeployer<ApplicationMode
     private final AtomicInteger serviceRefreshState = new AtomicInteger(0);
 
     private void registerServiceInstance() {
+        TimePair timePair = TimePair.start();
+        GlobalMetricsEventMulticaster eventMulticaster = 
applicationModel.getBeanFactory().getBean(GlobalMetricsEventMulticaster.class);
+        eventMulticaster.publishEvent(new 
RegistryEvent.MetricsRegisterEvent(applicationModel, timePair));
         try {
             registered = true;
             
ServiceInstanceMetadataUtils.registerMetadataAndInstance(applicationModel);
+            eventMulticaster.publishFinishEvent(new 
RegistryEvent.MetricsRegisterEvent(applicationModel, timePair));
         } catch (Exception e) {
+            eventMulticaster.publishErrorEvent(new 
RegistryEvent.MetricsRegisterEvent(applicationModel, timePair));
             logger.error(CONFIG_REGISTER_INSTANCE_ERROR, "configuration server 
disconnected", "", "Register instance error.", e);
         }
         if (registered) {
diff --git a/dubbo-distribution/dubbo-all/pom.xml 
b/dubbo-distribution/dubbo-all/pom.xml
index 49a224b69f..5e47b22074 100644
--- a/dubbo-distribution/dubbo-all/pom.xml
+++ b/dubbo-distribution/dubbo-all/pom.xml
@@ -190,6 +190,13 @@
             <scope>compile</scope>
             <optional>true</optional>
         </dependency>
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-metrics-registry</artifactId>
+            <version>${project.version}</version>
+            <scope>compile</scope>
+            <optional>true</optional>
+        </dependency>
         <dependency>
             <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo-metrics-prometheus</artifactId>
@@ -502,6 +509,7 @@
                                     
<include>org.apache.dubbo:dubbo-metadata-report-zookeeper</include>
                                     
<include>org.apache.dubbo:dubbo-metrics-api</include>
                                     
<include>org.apache.dubbo:dubbo-metrics-default</include>
+                                    
<include>org.apache.dubbo:dubbo-metrics-registry</include>
                                     
<include>org.apache.dubbo:dubbo-metrics-metadata</include>
                                     
<include>org.apache.dubbo:dubbo-metrics-prometheus</include>
                                     
<include>org.apache.dubbo:dubbo-monitor-api</include>
diff --git a/dubbo-distribution/dubbo-bom/pom.xml 
b/dubbo-distribution/dubbo-bom/pom.xml
index 42b26038cd..4b5b5fcd51 100644
--- a/dubbo-distribution/dubbo-bom/pom.xml
+++ b/dubbo-distribution/dubbo-bom/pom.xml
@@ -235,6 +235,11 @@
                 <artifactId>dubbo-metrics-default</artifactId>
                 <version>${project.version}</version>
             </dependency>
+            <dependency>
+                <groupId>org.apache.dubbo</groupId>
+                <artifactId>dubbo-metrics-registry</artifactId>
+                <version>${project.version}</version>
+            </dependency>
             <dependency>
                 <groupId>org.apache.dubbo</groupId>
                 <artifactId>dubbo-metrics-prometheus</artifactId>
diff --git a/dubbo-metrics/dubbo-metrics-api/pom.xml 
b/dubbo-metrics/dubbo-metrics-api/pom.xml
index 7ee8df194d..3c31ae389c 100644
--- a/dubbo-metrics/dubbo-metrics-api/pom.xml
+++ b/dubbo-metrics/dubbo-metrics-api/pom.xml
@@ -36,11 +36,6 @@
             <artifactId>dubbo-common</artifactId>
             <version>${project.parent.version}</version>
         </dependency>
-        <dependency>
-            <groupId>org.apache.dubbo</groupId>
-            <artifactId>dubbo-cluster</artifactId>
-            <version>${project.parent.version}</version>
-        </dependency>
         <dependency>
             <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo-rpc-api</artifactId>
diff --git 
a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/event/SimpleMetricsEventMulticaster.java
 
b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/event/SimpleMetricsEventMulticaster.java
index 6f5df6660b..c3f187c82e 100644
--- 
a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/event/SimpleMetricsEventMulticaster.java
+++ 
b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/event/SimpleMetricsEventMulticaster.java
@@ -71,7 +71,7 @@ public class SimpleMetricsEventMulticaster implements 
MetricsEventMulticaster {
         publishTimeEvent(event, metricsLifeListener -> 
metricsLifeListener.onEventError(event));
     }
 
-    @SuppressWarnings({"rawtypes", "unchecked"})
+    @SuppressWarnings({"rawtypes"})
     private void publishTimeEvent(MetricsEvent event, 
Consumer<MetricsLifeListener> consumer) {
         if (event instanceof EmptyEvent) {
             return;
diff --git 
a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/MetricsKey.java
 
b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/MetricsKey.java
index 96c879c8dd..39cecbfe49 100644
--- 
a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/MetricsKey.java
+++ 
b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/MetricsKey.java
@@ -23,7 +23,7 @@ public enum MetricsKey {
     // provider metrics key
     METRIC_REQUESTS("dubbo.%s.requests.total", "Total Requests"),
     METRIC_REQUESTS_SUCCEED("dubbo.%s.requests.succeed.total", "Succeed 
Requests"),
-    
METRIC_REQUEST_BUSINESS_FAILED("dubbo.%s.requests.business.failed.total","Failed
 Business Requests"),
+    METRIC_REQUEST_BUSINESS_FAILED("dubbo.%s.requests.business.failed.total", 
"Failed Business Requests"),
     METRIC_REQUESTS_PROCESSING("dubbo.%s.requests.processing", "Processing 
Requests"),
     METRIC_REQUESTS_TIMEOUT("dubbo.%s.requests.timeout.total", "Total Timeout 
Failed Requests"),
     METRIC_REQUESTS_LIMIT("dubbo.%s.requests.limit.total", "Total Limit Failed 
Requests"),
@@ -59,6 +59,26 @@ public enum MetricsKey {
     GENERIC_METRIC_RT_P99("dubbo.%s.rt.seconds.p99", "Response Time P99"),
     GENERIC_METRIC_RT_P95("dubbo.%s.rt.seconds.p95", "Response Time P95"),
 
+    // register metrics key
+    REGISTER_METRIC_REQUESTS("dubbo.registry.register.requests.total", "Total 
Register Requests"),
+    
REGISTER_METRIC_REQUESTS_SUCCEED("dubbo.registry.register.requests.succeed.total",
 "Succeed Register Requests"),
+    
REGISTER_METRIC_REQUESTS_FAILED("dubbo.registry.register.requests.failed.total",
 "Failed Register Requests"),
+
+    // subscribe metrics key
+    SUBSCRIBE_METRIC_NUM("dubbo.registry.subscribe.num.total", "Total 
Subscribe Num"),
+    SUBSCRIBE_METRIC_NUM_SUCCEED("dubbo.registry.subscribe.num.succeed.total", 
"Succeed Subscribe Num"),
+    SUBSCRIBE_METRIC_NUM_FAILED("dubbo.registry.subscribe.num.failed.total", 
"Failed Subscribe Num"),
+
+    // directory metrics key
+    DIRECTORY_METRIC_NUM_CURRENT("dubbo.registry.directory.num.current", 
"Current Directory Urls"),
+    DIRECTORY_METRIC_NUM_VALID("dubbo.registry.directory.num.valid.total", 
"Valid Directory Urls"),
+    
DIRECTORY_METRIC_NUM_UN_VALID("dubbo.registry.directory.num.un_valid.total", 
"UnValid Directory Urls"),
+    DIRECTORY_METRIC_NUM_DISABLE("dubbo.registry.directory.num.disable.total", 
"Disable Directory Urls"),
+    
DIRECTORY_METRIC_NUM_RECOVER_DISABLE("dubbo.registry.directory.num.recover.disable.total",
 "Recover Disable Directory Urls"),
+
+    NOTIFY_METRIC_REQUESTS("dubbo.registry.notify.requests.total", "Total 
Notify Requests"),
+    NOTIFY_METRIC_NUM_LAST("dubbo.registry.notify.num.last", "Last Notify 
Nums"),
+
     THREAD_POOL_CORE_SIZE("dubbo.thread.pool.core.size", "Thread Pool Core 
Size"),
     THREAD_POOL_LARGEST_SIZE("dubbo.thread.pool.largest.size", "Thread Pool 
Largest Size"),
     THREAD_POOL_MAX_SIZE("dubbo.thread.pool.max.size", "Thread Pool Max Size"),
@@ -85,6 +105,7 @@ public enum MetricsKey {
     public final String getName() {
         return this.name;
     }
+
     public final String getNameByType(String type) {
         return String.format(name, type);
     }
diff --git 
a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/TimePair.java
 
b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/TimePair.java
index 3994fe9dd2..70e0aba1d6 100644
--- 
a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/TimePair.java
+++ 
b/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/TimePair.java
@@ -21,6 +21,7 @@ public class TimePair {
 
     private final long begin;
     private long end;
+    private static final TimePair empty = new TimePair(0L);
 
     public TimePair(long currentTimeMillis) {
         this.begin = currentTimeMillis;
@@ -37,4 +38,8 @@ public class TimePair {
     public long calc() {
         return end - begin;
     }
+
+    public static TimePair empty() {
+        return empty;
+    }
 }
diff --git a/dubbo-metrics/dubbo-metrics-default/pom.xml 
b/dubbo-metrics/dubbo-metrics-default/pom.xml
index 71be413c6a..bc4165d765 100644
--- a/dubbo-metrics/dubbo-metrics-default/pom.xml
+++ b/dubbo-metrics/dubbo-metrics-default/pom.xml
@@ -46,5 +46,11 @@
             <artifactId>micrometer-tracing-integration-test</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-cluster</artifactId>
+            <version>${project.parent.version}</version>
+            <scope>compile</scope>
+        </dependency>
     </dependencies>
 </project>
diff --git a/dubbo-metrics/dubbo-metrics-default/pom.xml 
b/dubbo-metrics/dubbo-metrics-registry/pom.xml
similarity index 80%
copy from dubbo-metrics/dubbo-metrics-default/pom.xml
copy to dubbo-metrics/dubbo-metrics-registry/pom.xml
index 71be413c6a..86f190ff76 100644
--- a/dubbo-metrics/dubbo-metrics-default/pom.xml
+++ b/dubbo-metrics/dubbo-metrics-registry/pom.xml
@@ -23,7 +23,7 @@
         <version>${revision}</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
-    <artifactId>dubbo-metrics-default</artifactId>
+    <artifactId>dubbo-metrics-registry</artifactId>
     <packaging>jar</packaging>
     <name>${project.artifactId}</name>
     <description>The metrics module of dubbo project</description>
@@ -36,15 +36,5 @@
             <artifactId>dubbo-metrics-api</artifactId>
             <version>${project.parent.version}</version>
         </dependency>
-        <dependency>
-            <groupId>io.micrometer</groupId>
-            <artifactId>micrometer-test</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>io.micrometer</groupId>
-            <artifactId>micrometer-tracing-integration-test</artifactId>
-            <scope>test</scope>
-        </dependency>
     </dependencies>
 </project>
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
new file mode 100644
index 0000000000..c30b7c0f11
--- /dev/null
+++ 
b/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/collector/RegistryMetricsCollector.java
@@ -0,0 +1,123 @@
+/*
+ * 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.extension.Activate;
+import org.apache.dubbo.config.context.ConfigManager;
+import org.apache.dubbo.metrics.collector.ApplicationMetricsCollector;
+import org.apache.dubbo.metrics.collector.MetricsCollector;
+import org.apache.dubbo.metrics.event.MetricsEvent;
+import org.apache.dubbo.metrics.event.MetricsEventMulticaster;
+import org.apache.dubbo.metrics.model.sample.MetricSample;
+import org.apache.dubbo.metrics.registry.collector.stat.RegistryStatComposite;
+import org.apache.dubbo.metrics.registry.event.RegistryEvent;
+import org.apache.dubbo.metrics.registry.event.RegistryMetricsEventMulticaster;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+
+/**
+ * Registry implementation of {@link MetricsCollector}
+ */
+@Activate
+public class RegistryMetricsCollector implements 
ApplicationMetricsCollector<RegistryEvent.Type, RegistryEvent> {
+
+    private Boolean collectEnabled = null;
+    private final RegistryStatComposite stats;
+    private final MetricsEventMulticaster registryMulticaster;
+    private final ApplicationModel applicationModel;
+
+    public RegistryMetricsCollector(ApplicationModel applicationModel) {
+        this.stats = new RegistryStatComposite();
+        this.registryMulticaster = new RegistryMetricsEventMulticaster();
+        this.applicationModel = applicationModel;
+    }
+
+    public void setCollectEnabled(Boolean collectEnabled) {
+        if (collectEnabled != null) {
+            this.collectEnabled = collectEnabled;
+        }
+    }
+
+    @Override
+    public boolean isCollectEnabled() {
+        if (collectEnabled == null) {
+            ConfigManager configManager = 
applicationModel.getApplicationConfigManager();
+            configManager.getMetrics().ifPresent(metricsConfig -> 
setCollectEnabled(metricsConfig.getEnableRegistryMetrics()));
+        }
+        return Optional.ofNullable(collectEnabled).orElse(false);
+    }
+
+    public void setNum(RegistryEvent.Type registryType, String 
applicationName, Map<String, Integer> lastNumMap) {
+        lastNumMap.forEach((serviceKey, num) ->
+            this.stats.setServiceKey(registryType, applicationName, 
serviceKey, num));
+    }
+
+    public void setNum(RegistryEvent.Type registryType, String 
applicationName, Integer num) {
+        this.stats.setApplicationKey(registryType, applicationName, num);
+    }
+
+
+    @Override
+    public void increment(String applicationName, RegistryEvent.Type 
registryType) {
+        this.stats.increment(registryType, applicationName);
+    }
+
+    @Override
+    public void addRT(String applicationName, String registryOpType, Long 
responseTime) {
+        stats.calcRt(applicationName, registryOpType, responseTime);
+    }
+
+    @Override
+    public List<MetricSample> collect() {
+        if (!isCollectEnabled()) {
+            new ArrayList<>();
+        }
+        List<MetricSample> list = new ArrayList<>();
+        list.addAll(stats.exportNumMetrics());
+        list.addAll(stats.exportRtMetrics());
+        list.addAll(stats.exportSkMetrics());
+
+        return list;
+    }
+
+    @Override
+    public boolean isSupport(MetricsEvent event) {
+        return event instanceof RegistryEvent;
+    }
+
+    @Override
+    public void onEvent(RegistryEvent event) {
+        registryMulticaster.publishEvent(event);
+    }
+
+
+    @Override
+    public void onEventFinish(RegistryEvent event) {
+        registryMulticaster.publishFinishEvent(event);
+    }
+
+    @Override
+    public void onEventError(RegistryEvent event) {
+        registryMulticaster.publishErrorEvent(event);
+    }
+}
diff --git 
a/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/collector/stat/RegistryStatComposite.java
 
b/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/collector/stat/RegistryStatComposite.java
new file mode 100644
index 0000000000..1d955cd7a7
--- /dev/null
+++ 
b/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/collector/stat/RegistryStatComposite.java
@@ -0,0 +1,240 @@
+/*
+ * 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.stat;
+
+import org.apache.dubbo.common.utils.ConcurrentHashMapUtils;
+import org.apache.dubbo.metrics.collector.MetricsCollector;
+import org.apache.dubbo.metrics.model.ApplicationMetric;
+import org.apache.dubbo.metrics.model.MetricsCategory;
+import org.apache.dubbo.metrics.model.MetricsKey;
+import org.apache.dubbo.metrics.model.MetricsKeyWrapper;
+import org.apache.dubbo.metrics.model.sample.GaugeMetricSample;
+import org.apache.dubbo.metrics.registry.event.RegistryEvent;
+import org.apache.dubbo.metrics.report.MetricsExport;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.LongAccumulator;
+import java.util.function.BiConsumer;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+
+/**
+ * As a data aggregator, use internal data containers calculates and classifies
+ * the registry data collected by {@link MetricsCollector MetricsCollector}, 
and
+ * provides an {@link MetricsExport MetricsExport} interface for exporting 
standard output formats.
+ */
+public class RegistryStatComposite implements MetricsExport {
+
+
+    public Map<RegistryEvent.Type, Map<String, AtomicLong>> numStats = new 
ConcurrentHashMap<>();
+    public Map<RegistryEvent.Type, Map<ServiceKeyMetric, AtomicLong>> skStats 
= new ConcurrentHashMap<>();
+    public List<LongContainer<? extends Number>> rtStats = new ArrayList<>();
+    public static String OP_TYPE_REGISTER = "register";
+    public static String OP_TYPE_SUBSCRIBE = "subscribe";
+    public static String OP_TYPE_NOTIFY = "notify";
+
+    public RegistryStatComposite() {
+        for (RegistryEvent.Type type : RegistryEvent.Type.values()) {
+            numStats.put(type, new ConcurrentHashMap<>());
+        }
+
+        rtStats.addAll(initStats(OP_TYPE_REGISTER));
+        rtStats.addAll(initStats(OP_TYPE_SUBSCRIBE));
+        rtStats.addAll(initStats(OP_TYPE_NOTIFY));
+    }
+
+    private List<LongContainer<? extends Number>> initStats(String 
registryOpType) {
+        List<LongContainer<? extends Number>> singleRtStats = new 
ArrayList<>();
+        singleRtStats.add(new AtomicLongContainer(new 
MetricsKeyWrapper(registryOpType, MetricsKey.GENERIC_METRIC_RT_LAST)));
+        singleRtStats.add(new LongAccumulatorContainer(new 
MetricsKeyWrapper(registryOpType, MetricsKey.GENERIC_METRIC_RT_MIN), new 
LongAccumulator(Long::min, Long.MAX_VALUE)));
+        singleRtStats.add(new LongAccumulatorContainer(new 
MetricsKeyWrapper(registryOpType, MetricsKey.GENERIC_METRIC_RT_MAX), new 
LongAccumulator(Long::max, Long.MIN_VALUE)));
+        singleRtStats.add(new AtomicLongContainer(new 
MetricsKeyWrapper(registryOpType, MetricsKey.GENERIC_METRIC_RT_SUM), 
(responseTime, longAccumulator) -> longAccumulator.addAndGet(responseTime)));
+        // AvgContainer is a special counter that stores the number of times 
but outputs function of sum/times
+        AtomicLongContainer avgContainer = new AtomicLongContainer(new 
MetricsKeyWrapper(registryOpType, MetricsKey.GENERIC_METRIC_RT_AVG), (k, v) -> 
v.incrementAndGet());
+        avgContainer.setValueSupplier(applicationName -> {
+            LongContainer<? extends Number> totalContainer = 
rtStats.stream().filter(longContainer -> 
longContainer.isKeyWrapper(MetricsKey.GENERIC_METRIC_RT_SUM, 
registryOpType)).findFirst().get();
+            AtomicLong totalRtTimes = avgContainer.get(applicationName);
+            AtomicLong totalRtSum = (AtomicLong) 
totalContainer.get(applicationName);
+            return totalRtSum.get() / totalRtTimes.get();
+        });
+        singleRtStats.add(avgContainer);
+        return singleRtStats;
+    }
+
+    public void setApplicationKey(RegistryEvent.Type type, String 
applicationName, int num) {
+        if (!numStats.containsKey(type)) {
+            return;
+        }
+        numStats.get(type).computeIfAbsent(applicationName, k -> new 
AtomicLong(0L)).set(num);
+    }
+
+    public void setServiceKey(RegistryEvent.Type type, String applicationName, 
String serviceKey, int num) {
+        if (!skStats.containsKey(type)) {
+            return;
+        }
+        skStats.get(type).computeIfAbsent(new 
ServiceKeyMetric(applicationName, serviceKey), k -> new 
AtomicLong(0L)).set(num);
+    }
+
+    public void increment(RegistryEvent.Type type, String applicationName) {
+        if (!numStats.containsKey(type)) {
+            return;
+        }
+        numStats.get(type).computeIfAbsent(applicationName, k -> new 
AtomicLong(0L)).incrementAndGet();
+    }
+
+    @SuppressWarnings({"rawtypes", "unchecked"})
+    public void calcRt(String applicationName, String registryOpType, Long 
responseTime) {
+        for (LongContainer container : rtStats.stream().filter(longContainer 
-> longContainer.specifyType(registryOpType)).collect(Collectors.toList())) {
+            Number current = (Number) 
ConcurrentHashMapUtils.computeIfAbsent(container, applicationName, 
container.getInitFunc());
+            container.getConsumerFunc().accept(responseTime, current);
+        }
+    }
+
+    @Override
+    public List<GaugeMetricSample> exportNumMetrics() {
+        List<GaugeMetricSample> list = new ArrayList<>();
+        for (RegistryEvent.Type type : numStats.keySet()) {
+            Map<String, AtomicLong> stringAtomicLongMap = numStats.get(type);
+            for (String applicationName : stringAtomicLongMap.keySet()) {
+                list.add(convertToSample(applicationName, type, 
MetricsCategory.REGISTRY, stringAtomicLongMap.get(applicationName)));
+            }
+        }
+        return list;
+    }
+
+    @Override
+    public List<GaugeMetricSample> exportRtMetrics() {
+        List<GaugeMetricSample> list = new ArrayList<>();
+        for (LongContainer<? extends Number> rtContainer : rtStats) {
+            MetricsKeyWrapper metricsKeyWrapper = 
rtContainer.getMetricsKeyWrapper();
+            for (Map.Entry<String, ? extends Number> entry : 
rtContainer.entrySet()) {
+                list.add(new GaugeMetricSample(metricsKeyWrapper.targetKey(), 
metricsKeyWrapper.targetDesc(), 
ApplicationMetric.getTagsByName(entry.getKey()), MetricsCategory.RT, () -> 
rtContainer.getValueSupplier().apply(entry.getKey())));
+            }
+        }
+        return list;
+    }
+
+    public List<GaugeMetricSample> exportSkMetrics() {
+        List<GaugeMetricSample> list = new ArrayList<>();
+        for (RegistryEvent.Type type : skStats.keySet()) {
+            Map<ServiceKeyMetric, AtomicLong> stringAtomicLongMap = 
skStats.get(type);
+            for (ServiceKeyMetric serviceKeyMetric : 
stringAtomicLongMap.keySet()) {
+                list.add(new GaugeMetricSample(type.getMetricsKey(), 
serviceKeyMetric.getTags(), MetricsCategory.REGISTRY, 
stringAtomicLongMap.get(serviceKeyMetric)::get));
+            }
+        }
+        return list;
+    }
+
+    public GaugeMetricSample convertToSample(String applicationName, 
RegistryEvent.Type type, MetricsCategory category, AtomicLong targetNumber) {
+        return new GaugeMetricSample(type.getMetricsKey(), 
ApplicationMetric.getTagsByName(applicationName), category, targetNumber::get);
+    }
+
+
+    /**
+     * Collect Number type data
+     *
+     * @param <NUMBER>
+     */
+    public static class LongContainer<NUMBER extends Number> extends 
ConcurrentHashMap<String, NUMBER> {
+
+        /**
+         * Provide the metric type name
+         */
+        private final MetricsKeyWrapper metricsKeyWrapper;
+        /**
+         * The initial value corresponding to the key is generally 0 of 
different data types
+         */
+        private final Function<String, NUMBER> initFunc;
+        /**
+         * Statistical data calculation function, which can be self-increment, 
self-decrement, or more complex avg function
+         */
+        private final BiConsumer<Long, NUMBER> consumerFunc;
+        /**
+         * Data output function required by  {@link GaugeMetricSample 
GaugeMetricSample}
+         */
+        private Function<String, Long> valueSupplier;
+
+
+        public LongContainer(MetricsKeyWrapper metricsKeyWrapper, 
Supplier<NUMBER> initFunc, BiConsumer<Long, NUMBER> consumerFunc) {
+            this.metricsKeyWrapper = metricsKeyWrapper;
+            this.initFunc = s -> initFunc.get();
+            this.consumerFunc = consumerFunc;
+            this.valueSupplier = k -> this.get(k).longValue();
+        }
+
+        public boolean specifyType(String type) {
+            return type.equals(getMetricsKeyWrapper().getType());
+        }
+
+        public MetricsKeyWrapper getMetricsKeyWrapper() {
+            return metricsKeyWrapper;
+        }
+
+        public boolean isKeyWrapper(MetricsKey metricsKey, String 
registryOpType) {
+            return metricsKeyWrapper.isKey(metricsKey,registryOpType);
+        }
+
+        public Function<String, NUMBER> getInitFunc() {
+            return initFunc;
+        }
+
+        public BiConsumer<Long, NUMBER> getConsumerFunc() {
+            return consumerFunc;
+        }
+
+        public Function<String, Long> getValueSupplier() {
+            return valueSupplier;
+        }
+
+        public void setValueSupplier(Function<String, Long> valueSupplier) {
+            this.valueSupplier = valueSupplier;
+        }
+
+        @Override
+        public String toString() {
+            return "LongContainer{" +
+                "metricsKeyWrapper=" + metricsKeyWrapper +
+                '}';
+        }
+    }
+
+    public static class AtomicLongContainer extends LongContainer<AtomicLong> {
+
+        public AtomicLongContainer(MetricsKeyWrapper metricsKeyWrapper) {
+            super(metricsKeyWrapper, AtomicLong::new, (responseTime, 
longAccumulator) -> longAccumulator.set(responseTime));
+        }
+
+        public AtomicLongContainer(MetricsKeyWrapper metricsKeyWrapper, 
BiConsumer<Long, AtomicLong> consumerFunc) {
+            super(metricsKeyWrapper, AtomicLong::new, consumerFunc);
+        }
+
+    }
+
+    public static class LongAccumulatorContainer extends 
LongContainer<LongAccumulator> {
+        public LongAccumulatorContainer(MetricsKeyWrapper metricsKeyWrapper, 
LongAccumulator accumulator) {
+            super(metricsKeyWrapper, () -> accumulator, (responseTime, 
longAccumulator) -> longAccumulator.accumulate(responseTime));
+        }
+    }
+
+
+}
diff --git 
a/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/collector/stat/ServiceKeyMetric.java
 
b/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/collector/stat/ServiceKeyMetric.java
new file mode 100644
index 0000000000..0b39ff8376
--- /dev/null
+++ 
b/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/collector/stat/ServiceKeyMetric.java
@@ -0,0 +1,83 @@
+/*
+ * 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.stat;
+
+import org.apache.dubbo.metrics.model.Metric;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static 
org.apache.dubbo.common.constants.MetricsConstants.TAG_APPLICATION_NAME;
+import static org.apache.dubbo.common.constants.MetricsConstants.TAG_HOSTNAME;
+import static 
org.apache.dubbo.common.constants.MetricsConstants.TAG_INTERFACE_KEY;
+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;
+
+/**
+ * Metric class for service.
+ */
+public class ServiceKeyMetric implements Metric {
+    private final String applicationName;
+    private final String serviceKey;
+
+    public ServiceKeyMetric(String applicationName, String serviceKey) {
+        this.applicationName = applicationName;
+        this.serviceKey = serviceKey;
+    }
+
+    public String getServiceKey() {
+        return serviceKey;
+    }
+
+    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, applicationName);
+
+        tags.put(TAG_INTERFACE_KEY, serviceKey);
+        return tags;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        ServiceKeyMetric that = (ServiceKeyMetric) o;
+
+        if (!applicationName.equals(that.applicationName)) return false;
+        return serviceKey.equals(that.serviceKey);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = applicationName.hashCode();
+        result = 31 * result + serviceKey.hashCode();
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return "ServiceKeyMetric{" +
+            "applicationName='" + applicationName + '\'' +
+            ", serviceKey='" + serviceKey + '\'' +
+            '}';
+    }
+}
diff --git 
a/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/MetricsDirectoryListener.java
 
b/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/MetricsDirectoryListener.java
new file mode 100644
index 0000000000..27648f962d
--- /dev/null
+++ 
b/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/MetricsDirectoryListener.java
@@ -0,0 +1,43 @@
+/*
+ * 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.metrics.event.MetricsEvent;
+import org.apache.dubbo.metrics.listener.MetricsListener;
+
+public class MetricsDirectoryListener implements 
MetricsListener<RegistryEvent.MetricsDirectoryEvent> {
+
+    @Override
+    public boolean isSupport(MetricsEvent event) {
+        return event instanceof RegistryEvent.MetricsDirectoryEvent;
+    }
+
+    @Override
+    public void onEvent(RegistryEvent.MetricsDirectoryEvent event) {
+        if (!event.isAvailable()) {
+            return;
+        }
+        if (event.getType().isIncrement()) {
+            
event.getCollector().increment(event.getSource().getApplicationName(), 
event.getType());
+        } else {
+            event.getCollector().setNum(event.getType(), 
event.getSource().getApplicationName(), event.getSize());
+        }
+    }
+
+
+}
diff --git 
a/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/MetricsNotifyListener.java
 
b/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/MetricsNotifyListener.java
new file mode 100644
index 0000000000..3b5561cb1f
--- /dev/null
+++ 
b/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/MetricsNotifyListener.java
@@ -0,0 +1,54 @@
+/*
+ * 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.logger.ErrorTypeAwareLogger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.metrics.event.MetricsEvent;
+import org.apache.dubbo.metrics.listener.MetricsLifeListener;
+
+import static 
org.apache.dubbo.metrics.registry.collector.stat.RegistryStatComposite.OP_TYPE_NOTIFY;
+
+/**
+ * The registration center actively pushes Listener, no failure and rt 
statistics
+ */
+public class MetricsNotifyListener implements 
MetricsLifeListener<RegistryEvent.MetricsNotifyEvent> {
+
+    protected final ErrorTypeAwareLogger logger = 
LoggerFactory.getErrorTypeAwareLogger(getClass());
+
+    @Override
+    public boolean isSupport(MetricsEvent event) {
+        return event instanceof RegistryEvent.MetricsNotifyEvent;
+    }
+
+    @Override
+    public void onEvent(RegistryEvent.MetricsNotifyEvent event) {
+        event.getCollector().increment(event.getSource().getApplicationName(), 
RegistryEvent.Type.N_TOTAL);
+    }
+
+    @Override
+    public void onEventFinish(RegistryEvent.MetricsNotifyEvent event) {
+        event.getCollector().setNum(RegistryEvent.Type.N_LAST_NUM, 
event.getSource().getApplicationName(), event.getLastNotifyNum());
+        event.getCollector().addRT(event.getSource().getApplicationName(), 
OP_TYPE_NOTIFY, event.getTimePair().calc());
+    }
+
+    @Override
+    public void onEventError(RegistryEvent.MetricsNotifyEvent event) {
+    }
+
+}
diff --git 
a/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/MetricsRegisterListener.java
 
b/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/MetricsRegisterListener.java
new file mode 100644
index 0000000000..f3c6352fcf
--- /dev/null
+++ 
b/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/MetricsRegisterListener.java
@@ -0,0 +1,52 @@
+/*
+ * 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.metrics.event.MetricsEvent;
+import org.apache.dubbo.metrics.listener.MetricsLifeListener;
+
+import static 
org.apache.dubbo.metrics.registry.collector.stat.RegistryStatComposite.OP_TYPE_REGISTER;
+
+public class MetricsRegisterListener implements 
MetricsLifeListener<RegistryEvent.MetricsRegisterEvent> {
+
+
+    @Override
+    public boolean isSupport(MetricsEvent event) {
+        return event instanceof RegistryEvent.MetricsRegisterEvent;
+    }
+
+    @Override
+    public void onEvent(RegistryEvent.MetricsRegisterEvent event) {
+        if (!event.isAvailable()) {
+            return;
+        }
+        event.getCollector().increment(event.getSource().getApplicationName(), 
RegistryEvent.Type.R_TOTAL);
+    }
+
+    @Override
+    public void onEventFinish(RegistryEvent.MetricsRegisterEvent event) {
+        event.getCollector().increment(event.getSource().getApplicationName(), 
RegistryEvent.Type.R_SUCCEED);
+        event.getCollector().addRT(event.getSource().getApplicationName(), 
OP_TYPE_REGISTER, event.getTimePair().calc());
+    }
+
+    @Override
+    public void onEventError(RegistryEvent.MetricsRegisterEvent event) {
+        event.getCollector().increment(event.getSource().getApplicationName(), 
RegistryEvent.Type.R_FAILED);
+        event.getCollector().addRT(event.getSource().getApplicationName(), 
OP_TYPE_REGISTER, event.getTimePair().calc());
+    }
+}
diff --git 
a/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/MetricsSubscribeListener.java
 
b/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/MetricsSubscribeListener.java
new file mode 100644
index 0000000000..abfc5a0624
--- /dev/null
+++ 
b/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/MetricsSubscribeListener.java
@@ -0,0 +1,52 @@
+/*
+ * 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.metrics.event.MetricsEvent;
+import org.apache.dubbo.metrics.listener.MetricsLifeListener;
+
+import static 
org.apache.dubbo.metrics.registry.collector.stat.RegistryStatComposite.OP_TYPE_SUBSCRIBE;
+
+public class MetricsSubscribeListener implements 
MetricsLifeListener<RegistryEvent.MetricsSubscribeEvent> {
+
+    @Override
+    public boolean isSupport(MetricsEvent event) {
+        return event instanceof RegistryEvent.MetricsSubscribeEvent;
+    }
+
+    @Override
+    public void onEvent(RegistryEvent.MetricsSubscribeEvent event) {
+        if (!event.isAvailable()) {
+            return;
+        }
+        event.getCollector().increment(event.getSource().getApplicationName(), 
RegistryEvent.Type.S_TOTAL);
+    }
+
+    @Override
+    public void onEventFinish(RegistryEvent.MetricsSubscribeEvent event) {
+        event.getCollector().increment(event.getSource().getApplicationName(), 
RegistryEvent.Type.S_SUCCEED);
+        event.getCollector().addRT(event.getSource().getApplicationName(), 
OP_TYPE_SUBSCRIBE, event.getTimePair().calc());
+    }
+
+    @Override
+    public void onEventError(RegistryEvent.MetricsSubscribeEvent event) {
+        event.getCollector().increment(event.getSource().getApplicationName(), 
RegistryEvent.Type.S_FAILED);
+        event.getCollector().addRT(event.getSource().getApplicationName(), 
OP_TYPE_SUBSCRIBE, event.getTimePair().calc());
+    }
+
+}
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
new file mode 100644
index 0000000000..c13eb2310d
--- /dev/null
+++ 
b/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/RegistryEvent.java
@@ -0,0 +1,156 @@
+/*
+ * 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.metrics.event.MetricsEvent;
+import org.apache.dubbo.metrics.event.TimeCounter;
+import org.apache.dubbo.metrics.model.MetricsKey;
+import org.apache.dubbo.metrics.model.TimePair;
+import org.apache.dubbo.metrics.registry.collector.RegistryMetricsCollector;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+
+import java.util.Map;
+
+/**
+ * Registry related events
+ */
+public class RegistryEvent extends MetricsEvent implements TimeCounter {
+    private final TimePair timePair;
+    private final RegistryMetricsCollector collector;
+    private final boolean available;
+
+    public RegistryEvent(ApplicationModel applicationModel, TimePair timePair) 
{
+        super(applicationModel);
+        this.timePair = timePair;
+        this.collector = 
applicationModel.getBeanFactory().getBean(RegistryMetricsCollector.class);
+        this.available = this.collector != null && 
collector.isCollectEnabled();
+    }
+
+    public ApplicationModel getSource() {
+        return (ApplicationModel) source;
+    }
+
+    public RegistryMetricsCollector getCollector() {
+        return collector;
+    }
+
+    public boolean isAvailable() {
+        return available;
+    }
+
+    @Override
+    public TimePair getTimePair() {
+        return timePair;
+    }
+
+    public enum Type {
+        R_TOTAL(MetricsKey.REGISTER_METRIC_REQUESTS),
+        R_SUCCEED(MetricsKey.REGISTER_METRIC_REQUESTS_SUCCEED),
+        R_FAILED(MetricsKey.REGISTER_METRIC_REQUESTS_FAILED),
+
+        S_TOTAL(MetricsKey.SUBSCRIBE_METRIC_NUM),
+        S_SUCCEED(MetricsKey.SUBSCRIBE_METRIC_NUM_SUCCEED),
+        S_FAILED(MetricsKey.SUBSCRIBE_METRIC_NUM_FAILED),
+
+        D_VALID(MetricsKey.DIRECTORY_METRIC_NUM_VALID),
+        D_UN_VALID(MetricsKey.DIRECTORY_METRIC_NUM_UN_VALID),
+        D_DISABLE(MetricsKey.DIRECTORY_METRIC_NUM_DISABLE),
+        D_CURRENT(MetricsKey.DIRECTORY_METRIC_NUM_CURRENT, false),
+        D_RECOVER_DISABLE(MetricsKey.DIRECTORY_METRIC_NUM_RECOVER_DISABLE),
+
+        N_TOTAL(MetricsKey.NOTIFY_METRIC_REQUESTS),
+        N_LAST_NUM(MetricsKey.NOTIFY_METRIC_NUM_LAST),
+        ;
+
+
+        private final MetricsKey metricsKey;
+        private final boolean isIncrement;
+
+
+        Type(MetricsKey metricsKey) {
+            this(metricsKey, true);
+        }
+
+        Type(MetricsKey metricsKey, boolean isIncrement) {
+            this.metricsKey = metricsKey;
+            this.isIncrement = isIncrement;
+        }
+
+        public MetricsKey getMetricsKey() {
+            return metricsKey;
+        }
+
+        public boolean isIncrement() {
+            return isIncrement;
+        }
+    }
+
+    public static class MetricsRegisterEvent extends RegistryEvent {
+
+        public MetricsRegisterEvent(ApplicationModel applicationModel, 
TimePair timePair) {
+            super(applicationModel, timePair);
+        }
+
+    }
+
+    public static class MetricsSubscribeEvent extends RegistryEvent {
+
+        public MetricsSubscribeEvent(ApplicationModel applicationModel, 
TimePair timePair) {
+            super(applicationModel, timePair);
+        }
+
+    }
+
+    public static class MetricsNotifyEvent extends RegistryEvent {
+
+        private final Map<String, Integer> lastNumMap;
+
+        public MetricsNotifyEvent(ApplicationModel applicationModel, TimePair 
timePair, Map<String, Integer> lastNumMap) {
+            super(applicationModel, timePair);
+            this.lastNumMap = lastNumMap;
+        }
+
+        public Map<String, Integer> getLastNotifyNum() {
+            return lastNumMap;
+        }
+    }
+
+    public static class MetricsDirectoryEvent extends RegistryEvent {
+
+        private final RegistryEvent.Type type;
+        private final int size;
+
+        public MetricsDirectoryEvent(ApplicationModel applicationModel, 
RegistryEvent.Type type) {
+            this(applicationModel, type, 1);
+        }
+
+        public MetricsDirectoryEvent(ApplicationModel applicationModel, 
RegistryEvent.Type type, int size) {
+            super(applicationModel, TimePair.empty());
+            this.type = type;
+            this.size = size;
+        }
+
+        public RegistryEvent.Type getType() {
+            return type;
+        }
+
+        public int getSize() {
+            return size;
+        }
+    }
+}
diff --git 
a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/TimePair.java
 
b/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/RegistryMetricsEventMulticaster.java
similarity index 61%
copy from 
dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/TimePair.java
copy to 
dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/RegistryMetricsEventMulticaster.java
index 3994fe9dd2..566607efdb 100644
--- 
a/dubbo-metrics/dubbo-metrics-api/src/main/java/org/apache/dubbo/metrics/model/TimePair.java
+++ 
b/dubbo-metrics/dubbo-metrics-registry/src/main/java/org/apache/dubbo/metrics/registry/event/RegistryMetricsEventMulticaster.java
@@ -15,26 +15,19 @@
  * limitations under the License.
  */
 
-package org.apache.dubbo.metrics.model;
+package org.apache.dubbo.metrics.registry.event;
 
-public class TimePair {
+import org.apache.dubbo.metrics.event.SimpleMetricsEventMulticaster;
 
-    private final long begin;
-    private long end;
+public final class RegistryMetricsEventMulticaster extends 
SimpleMetricsEventMulticaster {
 
-    public TimePair(long currentTimeMillis) {
-        this.begin = currentTimeMillis;
-    }
-
-    public static TimePair start() {
-        return new TimePair(System.currentTimeMillis());
-    }
+    public RegistryMetricsEventMulticaster() {
+        super.addListener(new MetricsRegisterListener());
+        super.addListener(new MetricsSubscribeListener());
+        super.addListener(new MetricsNotifyListener());
+        super.addListener(new MetricsDirectoryListener());
 
-    public void end() {
-        this.end = System.currentTimeMillis();
+        setAvailable();
     }
 
-    public long calc() {
-        return end - begin;
-    }
 }
diff --git 
a/dubbo-metrics/dubbo-metrics-registry/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metrics.collector.MetricsCollector
 
b/dubbo-metrics/dubbo-metrics-registry/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metrics.collector.MetricsCollector
new file mode 100644
index 0000000000..3b14cf6c7b
--- /dev/null
+++ 
b/dubbo-metrics/dubbo-metrics-registry/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.metrics.collector.MetricsCollector
@@ -0,0 +1 @@
+registry-collector=org.apache.dubbo.metrics.registry.collector.RegistryMetricsCollector
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
new file mode 100644
index 0000000000..d4e8f34cec
--- /dev/null
+++ 
b/dubbo-metrics/dubbo-metrics-registry/src/test/java/org/apache/dubbo/metrics/registry/metrics/collector/RegistryMetricsCollectorTest.java
@@ -0,0 +1,115 @@
+/*
+ * 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.metrics.collector;
+
+import org.apache.dubbo.config.ApplicationConfig;
+import org.apache.dubbo.metrics.event.GlobalMetricsEventMulticaster;
+import org.apache.dubbo.metrics.model.MetricsKey;
+import org.apache.dubbo.metrics.model.MetricsKeyWrapper;
+import org.apache.dubbo.metrics.model.TimePair;
+import org.apache.dubbo.metrics.model.sample.GaugeMetricSample;
+import org.apache.dubbo.metrics.model.sample.MetricSample;
+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.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import static 
org.apache.dubbo.common.constants.MetricsConstants.TAG_APPLICATION_NAME;
+import static 
org.apache.dubbo.metrics.registry.collector.stat.RegistryStatComposite.OP_TYPE_REGISTER;
+
+
+class RegistryMetricsCollectorTest {
+
+    private ApplicationModel applicationModel;
+
+    @BeforeEach
+    public void setup() {
+        FrameworkModel frameworkModel = FrameworkModel.defaultModel();
+        applicationModel = frameworkModel.newApplication();
+        ApplicationConfig config = new ApplicationConfig();
+        config.setName("MockMetrics");
+
+        applicationModel.getApplicationConfigManager().setApplication(config);
+
+    }
+
+    @AfterEach
+    public void teardown() {
+        applicationModel.destroy();
+    }
+
+    @Test
+    void testPushMetrics() throws InterruptedException {
+
+        TimePair timePair = TimePair.start();
+        GlobalMetricsEventMulticaster eventMulticaster = 
applicationModel.getBeanFactory().getOrRegisterBean(GlobalMetricsEventMulticaster.class);
+        RegistryMetricsCollector collector = 
applicationModel.getBeanFactory().getOrRegisterBean(RegistryMetricsCollector.class);
+        collector.setCollectEnabled(true);
+
+        eventMulticaster.publishEvent(new 
RegistryEvent.MetricsRegisterEvent(applicationModel, timePair));
+        List<MetricSample> metricSamples = collector.collect();
+
+        // push success +1
+        Assertions.assertEquals(metricSamples.size(), 1);
+        Assertions.assertTrue(metricSamples.get(0) instanceof 
GaugeMetricSample);
+
+        eventMulticaster.publishFinishEvent(new 
RegistryEvent.MetricsRegisterEvent(applicationModel, timePair));
+        // push finish rt +1
+        metricSamples = collector.collect();
+        //num(total+success) + rt(5) = 7
+        Assertions.assertEquals(metricSamples.size(), 7);
+        long c1 = timePair.calc();
+        TimePair lastTimePair = TimePair.start();
+        eventMulticaster.publishEvent(new 
RegistryEvent.MetricsRegisterEvent(applicationModel, lastTimePair));
+        Thread.sleep(50);
+        // push error rt +1
+        eventMulticaster.publishErrorEvent(new 
RegistryEvent.MetricsRegisterEvent(applicationModel, lastTimePair));
+        long c2 = lastTimePair.calc();
+        metricSamples = collector.collect();
+
+        // num(total+success+error) + rt(5)
+        Assertions.assertEquals(metricSamples.size(), 8);
+
+        // calc rt
+        for (MetricSample sample : metricSamples) {
+            Map<String, String> tags = sample.getTags();
+            Assertions.assertEquals(tags.get(TAG_APPLICATION_NAME), 
applicationModel.getApplicationName());
+        }
+        Map<String, Long> sampleMap = 
metricSamples.stream().collect(Collectors.toMap(MetricSample::getName, k -> {
+            Number number = ((GaugeMetricSample) k).getSupplier().get();
+            return number.longValue();
+        }));
+
+        Assertions.assertEquals(sampleMap.get(new 
MetricsKeyWrapper(OP_TYPE_REGISTER, 
MetricsKey.GENERIC_METRIC_RT_LAST).targetKey()), lastTimePair.calc());
+        Assertions.assertEquals(sampleMap.get(new 
MetricsKeyWrapper(OP_TYPE_REGISTER, 
MetricsKey.GENERIC_METRIC_RT_MIN).targetKey()), Math.min(c1, c2));
+        Assertions.assertEquals(sampleMap.get(new 
MetricsKeyWrapper(OP_TYPE_REGISTER, 
MetricsKey.GENERIC_METRIC_RT_MAX).targetKey()), Math.max(c1, c2));
+        Assertions.assertEquals(sampleMap.get(new 
MetricsKeyWrapper(OP_TYPE_REGISTER, 
MetricsKey.GENERIC_METRIC_RT_AVG).targetKey()), (c1 + c2) / 2);
+        Assertions.assertEquals(sampleMap.get(new 
MetricsKeyWrapper(OP_TYPE_REGISTER, 
MetricsKey.GENERIC_METRIC_RT_SUM).targetKey()), c1 + c2);
+    }
+
+
+
+}
diff --git 
a/dubbo-metrics/dubbo-metrics-registry/src/test/java/org/apache/dubbo/metrics/registry/metrics/collector/RegistryMetricsSampleTest.java
 
b/dubbo-metrics/dubbo-metrics-registry/src/test/java/org/apache/dubbo/metrics/registry/metrics/collector/RegistryMetricsSampleTest.java
new file mode 100644
index 0000000000..92025d71c9
--- /dev/null
+++ 
b/dubbo-metrics/dubbo-metrics-registry/src/test/java/org/apache/dubbo/metrics/registry/metrics/collector/RegistryMetricsSampleTest.java
@@ -0,0 +1,97 @@
+/*
+ * 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.metrics.collector;
+
+import org.apache.dubbo.config.ApplicationConfig;
+import org.apache.dubbo.metrics.model.MetricsKey;
+import org.apache.dubbo.metrics.model.MetricsKeyWrapper;
+import org.apache.dubbo.metrics.model.sample.GaugeMetricSample;
+import org.apache.dubbo.metrics.model.sample.MetricSample;
+import org.apache.dubbo.metrics.registry.collector.RegistryMetricsCollector;
+import org.apache.dubbo.metrics.registry.collector.stat.RegistryStatComposite;
+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.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import static 
org.apache.dubbo.common.constants.MetricsConstants.TAG_APPLICATION_NAME;
+
+class RegistryMetricsSampleTest {
+
+    private ApplicationModel applicationModel;
+
+    @BeforeEach
+    public void setup() {
+        FrameworkModel frameworkModel = FrameworkModel.defaultModel();
+        applicationModel = frameworkModel.newApplication();
+        ApplicationConfig config = new ApplicationConfig();
+        config.setName("MockMetrics");
+        applicationModel.getApplicationConfigManager().setApplication(config);
+    }
+
+    @AfterEach
+    public void teardown() {
+        applicationModel.destroy();
+    }
+
+    @Test
+    void testRegisterMetrics() {
+    }
+
+    @Test
+    void testRTMetrics() {
+        RegistryMetricsCollector collector = new 
RegistryMetricsCollector(applicationModel);
+        collector.setCollectEnabled(true);
+        String applicationName = applicationModel.getApplicationName();
+        collector.addRT(applicationName, 
RegistryStatComposite.OP_TYPE_REGISTER, 10L);
+        collector.addRT(applicationName, 
RegistryStatComposite.OP_TYPE_REGISTER, 0L);
+
+        List<MetricSample> samples = collector.collect();
+        for (MetricSample sample : samples) {
+            Map<String, String> tags = sample.getTags();
+            Assertions.assertEquals(tags.get(TAG_APPLICATION_NAME), 
applicationName);
+        }
+
+        Map<String, Long> sampleMap = 
samples.stream().collect(Collectors.toMap(MetricSample::getName, k -> {
+            Number number = ((GaugeMetricSample) k).getSupplier().get();
+            return number.longValue();
+        }));
+
+        Assertions.assertEquals(sampleMap.get(new 
MetricsKeyWrapper(RegistryStatComposite.OP_TYPE_REGISTER, 
MetricsKey.GENERIC_METRIC_RT_LAST).targetKey()), 0L);
+        Assertions.assertEquals(sampleMap.get(new 
MetricsKeyWrapper(RegistryStatComposite.OP_TYPE_REGISTER, 
MetricsKey.GENERIC_METRIC_RT_MIN).targetKey()), 0L);
+        Assertions.assertEquals(sampleMap.get(new 
MetricsKeyWrapper(RegistryStatComposite.OP_TYPE_REGISTER, 
MetricsKey.GENERIC_METRIC_RT_MAX).targetKey()), 10L);
+        Assertions.assertEquals(sampleMap.get(new 
MetricsKeyWrapper(RegistryStatComposite.OP_TYPE_REGISTER, 
MetricsKey.GENERIC_METRIC_RT_AVG).targetKey()), 5L);
+        Assertions.assertEquals(sampleMap.get(new 
MetricsKeyWrapper(RegistryStatComposite.OP_TYPE_REGISTER, 
MetricsKey.GENERIC_METRIC_RT_SUM).targetKey()), 10L);
+    }
+
+    @Test
+    void testListener() {
+        RegistryMetricsCollector collector = new 
RegistryMetricsCollector(applicationModel);
+        collector.setCollectEnabled(true);
+        String applicationName = applicationModel.getApplicationName();
+        collector.increment(applicationName,RegistryEvent.Type.R_TOTAL);
+    }
+
+}
diff --git 
a/dubbo-metrics/dubbo-metrics-registry/src/test/java/org/apache/dubbo/metrics/registry/metrics/model/MethodMetricTest.java
 
b/dubbo-metrics/dubbo-metrics-registry/src/test/java/org/apache/dubbo/metrics/registry/metrics/model/MethodMetricTest.java
new file mode 100644
index 0000000000..44beb3a608
--- /dev/null
+++ 
b/dubbo-metrics/dubbo-metrics-registry/src/test/java/org/apache/dubbo/metrics/registry/metrics/model/MethodMetricTest.java
@@ -0,0 +1,79 @@
+/*
+ * 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.metrics.model;
+
+import org.apache.dubbo.metrics.model.MethodMetric;
+import org.apache.dubbo.rpc.RpcInvocation;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import java.util.Map;
+
+import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY;
+import static 
org.apache.dubbo.common.constants.MetricsConstants.TAG_APPLICATION_NAME;
+import static org.apache.dubbo.common.constants.MetricsConstants.TAG_GROUP_KEY;
+import static org.apache.dubbo.common.constants.MetricsConstants.TAG_HOSTNAME;
+import static 
org.apache.dubbo.common.constants.MetricsConstants.TAG_INTERFACE_KEY;
+import static org.apache.dubbo.common.constants.MetricsConstants.TAG_IP;
+import static 
org.apache.dubbo.common.constants.MetricsConstants.TAG_METHOD_KEY;
+import static 
org.apache.dubbo.common.constants.MetricsConstants.TAG_VERSION_KEY;
+import static org.apache.dubbo.common.utils.NetUtils.getLocalHost;
+import static org.apache.dubbo.common.utils.NetUtils.getLocalHostName;
+
+class MethodMetricTest {
+
+    private static final String applicationName = null;
+    private static String interfaceName;
+    private static String methodName;
+    private static String group;
+    private static String version;
+    private static RpcInvocation invocation;
+
+    @BeforeAll
+    public static void setup() {
+        interfaceName = "org.apache.dubbo.MockInterface";
+        methodName = "mockMethod";
+        group = "mockGroup";
+        version = "1.0.0";
+        invocation = new RpcInvocation(methodName, interfaceName, 
"serviceKey", null, null);
+        invocation.setTargetServiceUniqueName(group + "/" + interfaceName + 
":" + version);
+        invocation.setAttachment(GROUP_KEY, group);
+        invocation.setAttachment(VERSION_KEY, version);
+    }
+
+    @Test
+    void test() {
+        MethodMetric metric = new MethodMetric(applicationName, invocation);
+        Assertions.assertEquals(metric.getInterfaceName(), interfaceName);
+        Assertions.assertEquals(metric.getMethodName(), methodName);
+        Assertions.assertEquals(metric.getGroup(), group);
+        Assertions.assertEquals(metric.getVersion(), version);
+
+        Map<String, String> tags = metric.getTags();
+        Assertions.assertEquals(tags.get(TAG_IP), getLocalHost());
+        Assertions.assertEquals(tags.get(TAG_HOSTNAME), getLocalHostName());
+        Assertions.assertEquals(tags.get(TAG_APPLICATION_NAME), 
applicationName);
+
+        Assertions.assertEquals(tags.get(TAG_INTERFACE_KEY), interfaceName);
+        Assertions.assertEquals(tags.get(TAG_METHOD_KEY), methodName);
+        Assertions.assertEquals(tags.get(TAG_GROUP_KEY), group);
+        Assertions.assertEquals(tags.get(TAG_VERSION_KEY), version);
+    }
+}
diff --git 
a/dubbo-metrics/dubbo-metrics-registry/src/test/java/org/apache/dubbo/metrics/registry/metrics/model/sample/GaugeMetricSampleTest.java
 
b/dubbo-metrics/dubbo-metrics-registry/src/test/java/org/apache/dubbo/metrics/registry/metrics/model/sample/GaugeMetricSampleTest.java
new file mode 100644
index 0000000000..bfa6d2e176
--- /dev/null
+++ 
b/dubbo-metrics/dubbo-metrics-registry/src/test/java/org/apache/dubbo/metrics/registry/metrics/model/sample/GaugeMetricSampleTest.java
@@ -0,0 +1,63 @@
+/*
+ * 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.metrics.model.sample;
+
+import org.apache.dubbo.metrics.model.MetricsCategory;
+import org.apache.dubbo.metrics.model.sample.GaugeMetricSample;
+import org.apache.dubbo.metrics.model.sample.MetricSample;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Supplier;
+
+class GaugeMetricSampleTest {
+
+    private static String name;
+    private static String description;
+    private static Map<String, String> tags;
+    private static MetricsCategory category;
+    private static String baseUnit;
+    private static Supplier<Number> supplier;
+
+    @BeforeAll
+    public static void setup() {
+        name = "test";
+        description = "test";
+        tags = new HashMap<>();
+        category = MetricsCategory.REQUESTS;
+        baseUnit = "byte";
+        supplier = () -> 1;
+    }
+
+    @Test
+    void test() {
+        GaugeMetricSample sample = new GaugeMetricSample(name, description, 
tags, category, baseUnit, supplier);
+        Assertions.assertEquals(sample.getName(), name);
+        Assertions.assertEquals(sample.getDescription(), description);
+        Assertions.assertEquals(sample.getTags(), tags);
+        Assertions.assertEquals(sample.getType(), MetricSample.Type.GAUGE);
+        Assertions.assertEquals(sample.getCategory(), category);
+        Assertions.assertEquals(sample.getBaseUnit(), baseUnit);
+        Assertions.assertEquals(sample.getSupplier().get(), 1);
+        sample.setSupplier(() -> 2);
+        Assertions.assertEquals(sample.getSupplier().get(), 2);
+    }
+}
diff --git a/dubbo-metrics/pom.xml b/dubbo-metrics/pom.xml
index 835c2b1b44..d5527ecf7c 100644
--- a/dubbo-metrics/pom.xml
+++ b/dubbo-metrics/pom.xml
@@ -20,6 +20,7 @@
     <modules>
         <module>dubbo-metrics-api</module>
         <module>dubbo-metrics-default</module>
+        <module>dubbo-metrics-registry</module>
         <module>dubbo-metrics-metadata</module>
         <module>dubbo-metrics-prometheus</module>
     </modules>
diff --git a/dubbo-registry/dubbo-registry-api/pom.xml 
b/dubbo-registry/dubbo-registry-api/pom.xml
index f9c580f8c9..4c3a15f353 100644
--- a/dubbo-registry/dubbo-registry-api/pom.xml
+++ b/dubbo-registry/dubbo-registry-api/pom.xml
@@ -98,5 +98,12 @@
             <version>${project.parent.version}</version>
             <scope>compile</scope>
         </dependency>
+
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-metrics-registry</artifactId>
+            <version>${project.parent.version}</version>
+            <scope>compile</scope>
+        </dependency>
     </dependencies>
 </project>
diff --git 
a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListener.java
 
b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListener.java
index c6eb7a83ec..0859c1e9e2 100644
--- 
a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListener.java
+++ 
b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/event/listener/ServiceInstancesChangedListener.java
@@ -16,25 +16,10 @@
  */
 package org.apache.dubbo.registry.client.event.listener;
 
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.TreeSet;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.ScheduledFuture;
-import java.util.concurrent.Semaphore;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
-
 import org.apache.dubbo.common.ProtocolServiceKey;
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.URLBuilder;
+import org.apache.dubbo.common.beans.factory.ScopeBeanFactory;
 import org.apache.dubbo.common.constants.CommonConstants;
 import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
 import org.apache.dubbo.common.logger.LoggerFactory;
@@ -43,6 +28,9 @@ import org.apache.dubbo.common.utils.CollectionUtils;
 import org.apache.dubbo.common.utils.ConcurrentHashSet;
 import org.apache.dubbo.metadata.MetadataInfo;
 import org.apache.dubbo.metadata.MetadataInfo.ServiceInfo;
+import org.apache.dubbo.metrics.event.SimpleMetricsEventMulticaster;
+import org.apache.dubbo.metrics.model.TimePair;
+import org.apache.dubbo.metrics.registry.event.RegistryEvent;
 import org.apache.dubbo.registry.NotifyListener;
 import org.apache.dubbo.registry.client.DefaultServiceInstance;
 import org.apache.dubbo.registry.client.ServiceDiscovery;
@@ -54,6 +42,22 @@ import 
org.apache.dubbo.registry.client.metadata.ServiceInstanceNotificationCust
 import org.apache.dubbo.rpc.model.ApplicationModel;
 import org.apache.dubbo.rpc.model.ScopeModelUtil;
 
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
 import static org.apache.dubbo.common.constants.CommonConstants.PROTOCOL_KEY;
 import static 
org.apache.dubbo.common.constants.LoggerCodeConstants.INTERNAL_ERROR;
 import static 
org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_FAILED_REFRESH_ADDRESS;
@@ -87,6 +91,7 @@ public class ServiceInstancesChangedListener {
     private final ScheduledExecutorService scheduler;
     private volatile boolean hasEmptyMetadata;
     private final Set<ServiceInstanceNotificationCustomizer> 
serviceInstanceNotificationCustomizers;
+    private final ApplicationModel applicationModel;
 
 
     public ServiceInstancesChangedListener(Set<String> serviceNames, 
ServiceDiscovery serviceDiscovery) {
@@ -99,6 +104,7 @@ public class ServiceInstancesChangedListener {
         ApplicationModel applicationModel = 
ScopeModelUtil.getApplicationModel(serviceDiscovery == null || 
serviceDiscovery.getUrl() == null ? null : 
serviceDiscovery.getUrl().getScopeModel());
         this.scheduler = 
applicationModel.getBeanFactory().getBean(FrameworkExecutorRepository.class).getMetadataRetryExecutor();
         this.serviceInstanceNotificationCustomizers = 
applicationModel.getExtensionLoader(ServiceInstanceNotificationCustomizer.class).getSupportedExtensionInstances();
+        this.applicationModel = applicationModel;
     }
 
     /**
@@ -398,6 +404,13 @@ public class ServiceInstancesChangedListener {
      * race condition is protected by onEvent/doOnEvent
      */
     protected void notifyAddressChanged() {
+
+        ScopeBeanFactory beanFactory = 
applicationModel.getFrameworkModel().getBeanFactory();
+        SimpleMetricsEventMulticaster eventMulticaster = 
beanFactory.getOrRegisterBean(SimpleMetricsEventMulticaster.class);
+
+        TimePair timePair = TimePair.start();
+        eventMulticaster.publishEvent(new 
RegistryEvent.MetricsNotifyEvent(applicationModel, timePair, null));
+        Map<String, Integer> lastNumMap = new HashMap<>();
         // 1 different services
         listeners.forEach((serviceKey, listenerSet) -> {
             // 2 multiple subscription listener of the same service
@@ -407,8 +420,11 @@ public class ServiceInstancesChangedListener {
                 List<URL> urls = 
toUrlsWithEmpty(getAddresses(listenerWithKey.getProtocolServiceKey(), 
notifyListener.getConsumerUrl()));
                 logger.info("Notify service " + 
listenerWithKey.getProtocolServiceKey() + " with urls " + urls.size());
                 notifyListener.notify(urls);
+                lastNumMap.put(serviceKey, urls.size());
             }
         });
+        eventMulticaster.publishFinishEvent(new 
RegistryEvent.MetricsNotifyEvent(applicationModel, timePair, lastNumMap));
+
     }
 
     protected List<URL> toUrlsWithEmpty(List<URL> urls) {
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 a994f1f1c7..65c8ef3190 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
@@ -16,19 +16,6 @@
  */
 package org.apache.dubbo.registry.integration;
 
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-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.stream.Collectors;
-
 import org.apache.dubbo.common.URL;
 import org.apache.dubbo.common.URLBuilder;
 import org.apache.dubbo.common.config.configcenter.DynamicConfiguration;
@@ -43,6 +30,9 @@ import org.apache.dubbo.common.utils.CollectionUtils;
 import org.apache.dubbo.common.utils.NetUtils;
 import org.apache.dubbo.common.utils.StringUtils;
 import org.apache.dubbo.common.utils.UrlUtils;
+import org.apache.dubbo.metrics.event.GlobalMetricsEventMulticaster;
+import org.apache.dubbo.metrics.model.TimePair;
+import org.apache.dubbo.metrics.registry.event.RegistryEvent;
 import org.apache.dubbo.registry.AddressListener;
 import org.apache.dubbo.remoting.Constants;
 import org.apache.dubbo.rpc.Invoker;
@@ -53,8 +43,22 @@ import org.apache.dubbo.rpc.cluster.Router;
 import org.apache.dubbo.rpc.cluster.directory.StaticDirectory;
 import org.apache.dubbo.rpc.cluster.router.state.BitList;
 import org.apache.dubbo.rpc.cluster.support.ClusterUtils;
+import org.apache.dubbo.rpc.model.ApplicationModel;
 import org.apache.dubbo.rpc.model.ModuleModel;
 
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+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.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.DUBBO_PROTOCOL;
@@ -110,11 +114,18 @@ public class RegistryDirectory<T> extends 
DynamicDirectory<T> {
         super(serviceType, url);
         moduleModel = getModuleModel(url.getScopeModel());
         consumerConfigurationListener = 
getConsumerConfigurationListener(moduleModel);
+
     }
 
     @Override
     public void subscribe(URL url) {
+        ApplicationModel applicationModel = url.getApplicationModel();
+        GlobalMetricsEventMulticaster eventMulticaster = 
applicationModel.getBeanFactory().getBean(GlobalMetricsEventMulticaster.class);
+        TimePair timePair = TimePair.start();
+
+        eventMulticaster.publishEvent(new 
RegistryEvent.MetricsSubscribeEvent(applicationModel, timePair));
         super.subscribe(url);
+        eventMulticaster.publishFinishEvent(new 
RegistryEvent.MetricsSubscribeEvent(applicationModel, timePair));
         if 
(moduleModel.getModelEnvironment().getConfiguration().convert(Boolean.class, 
org.apache.dubbo.registry.Constants.ENABLE_CONFIGURATION_LISTEN, true)) {
             consumerConfigurationListener.addNotifyListener(this);
             referenceConfigurationListener = new 
ReferenceConfigurationListener(moduleModel, this, url);
@@ -233,7 +244,7 @@ public class RegistryDirectory<T> extends 
DynamicDirectory<T> {
             // use local reference to avoid NPE as this.cachedInvokerUrls will 
be set null by destroyAllInvokers().
             Set<URL> localCachedInvokerUrls = this.cachedInvokerUrls;
             if (invokerUrls.isEmpty()) {
-                if(CollectionUtils.isNotEmpty(localCachedInvokerUrls)){
+                if (CollectionUtils.isNotEmpty(localCachedInvokerUrls)) {
                     // 1-4 Empty address.
                     logger.warn(REGISTRY_EMPTY_ADDRESS, "configuration ", "",
                         "Service" + serviceKey + " received empty address list 
with no EMPTY protocol set, trigger empty protection.");
diff --git a/dubbo-test/dubbo-dependencies-all/pom.xml 
b/dubbo-test/dubbo-dependencies-all/pom.xml
index 66f84b58f6..8922e63208 100644
--- a/dubbo-test/dubbo-dependencies-all/pom.xml
+++ b/dubbo-test/dubbo-dependencies-all/pom.xml
@@ -148,6 +148,11 @@
             <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo-metrics-default</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.apache.dubbo</groupId>
+            <artifactId>dubbo-metrics-registry</artifactId>
+        </dependency>
+
         <dependency>
             <groupId>org.apache.dubbo</groupId>
             <artifactId>dubbo-metrics-metadata</artifactId>

Reply via email to