You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@servicecomb.apache.org by li...@apache.org on 2023/12/01 08:22:24 UTC

(servicecomb-java-chassis) 06/11: [SCB-2838]using micrometer to replace spectator part4: remove spectator and fix UT

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

liubao pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/servicecomb-java-chassis.git

commit 82d4432434f9facf29e6ce4cb3be2e341fcdeedf
Author: liubao <bi...@qq.com>
AuthorDate: Wed Nov 29 17:53:59 2023 +0800

    [SCB-2838]using micrometer to replace spectator part4: remove spectator and fix UT
---
 .../metrics/meter/LatencyDistributionConfig.java   |   4 +-
 .../metrics/publish/spectator/MeasurementNode.java |   2 +-
 .../metrics/publish/spectator/MeasurementTree.java |  33 +-
 foundations/foundation-test-scaffolding/pom.xml    |   4 +-
 .../metric/DefaultClientEndpointMetricManager.java |  22 --
 .../monitor/MetricsMonitorDataProvider.java        |   3 +-
 .../metrics/core/MetricsBootListener.java          |  11 +-
 .../metrics/core/ThreadPoolMetersInitializer.java  |   2 +-
 .../metrics/core/VertxMetersInitializer.java       |  17 +-
 .../ThreadPoolMonitorPublishModelFactory.java      |   2 +-
 .../meter/invocation/AbstractInvocationMeter.java  |  11 +-
 .../meter/invocation/MeterInvocationConst.java     |   4 +-
 .../metrics/core/meter/os/CpuMeter.java            |   9 +-
 .../metrics/core/meter/os/NetMeter.java            |   1 +
 .../metrics/core/meter/os/net/InterfaceUsage.java  |  30 +-
 .../metrics/core/meter/vertx/EndpointMeter.java    | 101 +++--
 .../core/meter/vertx/HttpClientEndpointMeter.java  |  13 +-
 .../core/meter/vertx/HttpClientEndpointsMeter.java |   1 -
 .../core/meter/vertx/ServerEndpointMeter.java      |  20 +-
 .../core/meter/vertx/VertxEndpointsMeter.java      |  15 +-
 .../metrics/core/publish/DefaultLogPublisher.java  |   2 +-
 .../metrics/core/publish/PublishModelFactory.java  |  11 +-
 .../metrics/core/publish/PublishUtils.java         |  10 +-
 .../servicecomb/metrics/core/AssertUtil.java       |  12 +-
 .../core/TestInvocationMetersInitializer.java      | 419 +++++++++++++--------
 .../metrics/core/TestOsMeterInitializer.java       |  42 +--
 .../core/TestThreadPoolMetersInitializer.java      |  60 ++-
 .../metrics/core/TestVertxMetersInitializer.java   |  42 +--
 .../metrics/core/meter/os/TestCpuMeter.java        |  42 ++-
 .../metrics/core/meter/os/TestNetMeter.java        | 214 ++++++-----
 .../metrics/core/meter/os/TestOsMeter.java         |  31 +-
 .../core/publish/TestDefaultLogPublisher.java      |  44 ++-
 .../publish/TestInvocationPublishModelFactory.java | 388 ++++++++++---------
 .../metrics/core/publish/TestPublishUtils.java     |   2 +-
 .../publish/TestThreadPoolPublishModelFactory.java |  28 +-
 .../core/publish/model/invocation/Utils.java       |  43 +--
 .../prometheus/TestPrometheusPublisher.java        |  23 +-
 .../basic/integration/MetricsEndpointImpl.java     |  16 +-
 .../basic/integration/TestMetricsEndpointImpl.java |  49 +--
 39 files changed, 989 insertions(+), 794 deletions(-)

diff --git a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/meter/LatencyDistributionConfig.java b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/meter/LatencyDistributionConfig.java
index 2a341cfa1..d46469702 100644
--- a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/meter/LatencyDistributionConfig.java
+++ b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/meter/LatencyDistributionConfig.java
@@ -24,6 +24,8 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 public class LatencyDistributionConfig {
+  public static final Long MAX_LATENCY = 60 * 60 * 1000L;
+
   private static final Logger LOGGER = LoggerFactory.getLogger(LatencyDistributionConfig.class);
 
   private final List<LatencyScopeConfig> scopeConfigs = new ArrayList<>();
@@ -36,7 +38,7 @@ public class LatencyDistributionConfig {
     if (StringUtils.isEmpty(config)) {
       return;
     }
-    config = config.trim() + "," + Long.MAX_VALUE;
+    config = config.trim() + "," + MAX_LATENCY;
     String[] array = config.split("\\s*,+\\s*");
     try {
       for (int idx = 0; idx < array.length - 1; idx++) {
diff --git a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/spectator/MeasurementNode.java b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/spectator/MeasurementNode.java
index 6cace285b..b357f758e 100644
--- a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/spectator/MeasurementNode.java
+++ b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/spectator/MeasurementNode.java
@@ -40,7 +40,7 @@ public class MeasurementNode implements Comparable<MeasurementNode> {
   }
 
   public String getName() {
-    return id.getName();
+    return this.name;
   }
 
   public Id getId() {
diff --git a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/spectator/MeasurementTree.java b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/spectator/MeasurementTree.java
index a165ca935..c372a3304 100644
--- a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/spectator/MeasurementTree.java
+++ b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/spectator/MeasurementTree.java
@@ -16,17 +16,26 @@
  */
 package org.apache.servicecomb.foundation.metrics.publish.spectator;
 
+import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
 
 import io.micrometer.core.instrument.Measurement;
 import io.micrometer.core.instrument.Meter;
 import io.micrometer.core.instrument.Meter.Id;
+import io.micrometer.core.instrument.Statistic;
 import io.micrometer.core.instrument.Tag;
+import io.micrometer.core.instrument.Timer;
+import io.micrometer.core.instrument.distribution.CountAtBucket;
+import io.micrometer.core.instrument.distribution.HistogramSnapshot;
 
 // like select * from meters group by ......
 // but output a tree not a table
 public class MeasurementTree extends MeasurementNode {
+  public static final String TAG_LATENCY_DISTRIBUTION = "latencyDistribution";
+
+  public static final String TAG_TYPE = "type";
+
   public MeasurementTree() {
     super(null, null, null);
   }
@@ -39,12 +48,32 @@ public class MeasurementTree extends MeasurementNode {
     meters.forEachRemaining(meter -> {
       Iterable<Measurement> measurements = meter.measure();
       from(meter.getId(), measurements, groupConfig);
+
+      // This code snip is not very good design. But timer is quite special.
+      if (meter instanceof Timer) {
+        HistogramSnapshot snapshot = ((Timer) meter).takeSnapshot();
+        CountAtBucket[] countAtBuckets = snapshot.histogramCounts();
+        if (countAtBuckets.length > 2) {
+          List<Measurement> latency = new ArrayList<>(countAtBuckets.length);
+          for (int i = 0; i < countAtBuckets.length; i++) {
+            final int index = i;
+            if (index == 0) {
+              latency.add(new Measurement(() -> countAtBuckets[index].count(),
+                  Statistic.VALUE));
+              continue;
+            }
+            latency.add(new Measurement(() -> countAtBuckets[index].count() - countAtBuckets[index - 1].count(),
+                Statistic.VALUE));
+          }
+          from(meter.getId().withTag(Tag.of(TAG_TYPE, TAG_LATENCY_DISTRIBUTION)), latency, groupConfig);
+        }
+      }
     });
   }
 
   public void from(Id id, Iterable<Measurement> measurements, MeasurementGroupConfig groupConfig) {
     for (Measurement measurement : measurements) {
-      MeasurementNode node = addChild(id.getName(), id, null);
+      MeasurementNode node = addChild(id.getName(), id, measurement);
 
       List<TagFinder> tagFinders = groupConfig.findTagFinders(id.getName());
       if (tagFinders == null) {
@@ -63,7 +92,7 @@ public class MeasurementTree extends MeasurementNode {
                   id));
         }
 
-        node = node.addChild(tag.getValue(), id, null);
+        node = node.addChild(tag.getValue(), id, measurement);
       }
 
       node.addChild(measurement.getStatistic().name(), id, measurement);
diff --git a/foundations/foundation-test-scaffolding/pom.xml b/foundations/foundation-test-scaffolding/pom.xml
index a149ef9de..b3cc9f897 100644
--- a/foundations/foundation-test-scaffolding/pom.xml
+++ b/foundations/foundation-test-scaffolding/pom.xml
@@ -41,8 +41,8 @@
       <scope>provided</scope>
     </dependency>
     <dependency>
-      <groupId>com.netflix.spectator</groupId>
-      <artifactId>spectator-reg-servo</artifactId>
+      <groupId>com.google.guava</groupId>
+      <artifactId>guava</artifactId>
     </dependency>
     <dependency>
       <groupId>org.apache.logging.log4j</groupId>
diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/metric/DefaultClientEndpointMetricManager.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/metric/DefaultClientEndpointMetricManager.java
index a6ece5cac..17977a468 100644
--- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/metric/DefaultClientEndpointMetricManager.java
+++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/metric/DefaultClientEndpointMetricManager.java
@@ -16,8 +16,6 @@
  */
 package org.apache.servicecomb.foundation.vertx.metrics.metric;
 
-import java.util.ArrayList;
-import java.util.List;
 import java.util.Map;
 import java.util.concurrent.locks.ReadWriteLock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
@@ -30,10 +28,6 @@ import com.google.common.annotations.VisibleForTesting;
 import io.vertx.core.Vertx;
 
 public class DefaultClientEndpointMetricManager {
-  public interface ChangeListener {
-    void endpointsChanged();
-  }
-
   private final MetricsOptionsEx metricsOptionsEx;
 
   // to avoid save too many endpoint that not exist any more
@@ -49,22 +43,15 @@ public class DefaultClientEndpointMetricManager {
   // so must lock the logic
   private final ReadWriteLock rwlock = new ReentrantReadWriteLock();
 
-  private final List<ChangeListener> changeListeners = new ArrayList<>();
-
   public DefaultClientEndpointMetricManager(MetricsOptionsEx metricsOptionsEx) {
     this.metricsOptionsEx = metricsOptionsEx;
   }
 
-  public void addChangeListener(ChangeListener listener) {
-    this.changeListeners.add(listener);
-  }
-
   public DefaultClientEndpointMetric getOrCreateEndpointMetric(String address) {
     rwlock.readLock().lock();
     try {
       if (clientEndpointMetricMap.get(address) == null) {
         clientEndpointMetricMap.put(address, new DefaultClientEndpointMetric(address));
-        onChanged();
       }
       return clientEndpointMetricMap.get(address);
     } finally {
@@ -83,31 +70,22 @@ public class DefaultClientEndpointMetricManager {
 
   @VisibleForTesting
   public void onCheckClientEndpointMetricExpired(long periodic) {
-    boolean changed = false;
     for (DefaultClientEndpointMetric metric : clientEndpointMetricMap.values()) {
       if (metric.isExpired(metricsOptionsEx.getCheckClientEndpointMetricExpiredInNano())) {
         rwlock.writeLock().lock();
         try {
           if (metric.isExpired(metricsOptionsEx.getCheckClientEndpointMetricExpiredInNano())) {
             clientEndpointMetricMap.remove(metric.getAddress());
-            changed = true;
           }
         } finally {
           rwlock.writeLock().unlock();
         }
       }
     }
-    if (changed) {
-      onChanged();
-    }
   }
 
   public void setVertx(Vertx vertx) {
     vertx.setPeriodic(metricsOptionsEx.getCheckClientEndpointMetricIntervalInMilliseconds(),
         this::onCheckClientEndpointMetricExpired);
   }
-
-  private void onChanged() {
-    this.changeListeners.forEach(ChangeListener::endpointsChanged);
-  }
 }
diff --git a/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/MetricsMonitorDataProvider.java b/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/MetricsMonitorDataProvider.java
index b56c5c2a2..4829fb22a 100644
--- a/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/MetricsMonitorDataProvider.java
+++ b/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/MetricsMonitorDataProvider.java
@@ -38,7 +38,8 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.core.env.Environment;
 
 import com.google.common.eventbus.Subscribe;
-import com.netflix.spectator.api.Meter;
+
+import io.micrometer.core.instrument.Meter;
 
 /**
  * Monitor data based on metrics-core module.
diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/MetricsBootListener.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/MetricsBootListener.java
index 540b10ed9..eb9e43475 100644
--- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/MetricsBootListener.java
+++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/MetricsBootListener.java
@@ -19,15 +19,10 @@ package org.apache.servicecomb.metrics.core;
 import org.apache.servicecomb.core.BootListener;
 import org.apache.servicecomb.foundation.common.event.EventManager;
 import org.apache.servicecomb.foundation.metrics.MetricsBootstrap;
-import org.apache.servicecomb.foundation.metrics.registry.GlobalRegistry;
 
 public class MetricsBootListener implements BootListener {
   private final MetricsBootstrap metricsBootstrap;
 
-  public MetricsBootstrap getMetricsBootstrap() {
-    return metricsBootstrap;
-  }
-
   public MetricsBootListener(MetricsBootstrap metricsBootstrap) {
     this.metricsBootstrap = metricsBootstrap;
   }
@@ -37,9 +32,13 @@ public class MetricsBootListener implements BootListener {
 
   }
 
+  public MetricsBootstrap getMetricsBootstrap() {
+    return metricsBootstrap;
+  }
+
   @Override
   public void onAfterRegistry(BootEvent event) {
-    metricsBootstrap.start(new GlobalRegistry(), EventManager.getEventBus());
+    metricsBootstrap.start(EventManager.getEventBus());
   }
 
   @Override
diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/ThreadPoolMetersInitializer.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/ThreadPoolMetersInitializer.java
index ebe0c019a..a2a307b4a 100644
--- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/ThreadPoolMetersInitializer.java
+++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/ThreadPoolMetersInitializer.java
@@ -101,7 +101,7 @@ public class ThreadPoolMetersInitializer implements MetricsInitializer {
     }
   }
 
-  protected void createThreadPoolMeters(String threadPoolName, Executor executor) {
+  public void createThreadPoolMeters(String threadPoolName, Executor executor) {
     if (!(executor instanceof ThreadPoolExecutor threadPoolExecutor)) {
       return;
     }
diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/VertxMetersInitializer.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/VertxMetersInitializer.java
index dbc093ab1..8240fabd4 100644
--- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/VertxMetersInitializer.java
+++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/VertxMetersInitializer.java
@@ -18,6 +18,7 @@ package org.apache.servicecomb.metrics.core;
 
 import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig;
 import org.apache.servicecomb.foundation.metrics.MetricsInitializer;
+import org.apache.servicecomb.foundation.metrics.meter.PeriodMeter;
 import org.apache.servicecomb.foundation.vertx.SharedVertxFactory;
 import org.apache.servicecomb.metrics.core.meter.vertx.HttpClientEndpointsMeter;
 import org.apache.servicecomb.metrics.core.meter.vertx.ServerEndpointsMeter;
@@ -27,7 +28,7 @@ import com.google.common.eventbus.EventBus;
 import io.micrometer.core.instrument.MeterRegistry;
 import io.micrometer.core.instrument.Tags;
 
-public class VertxMetersInitializer implements MetricsInitializer {
+public class VertxMetersInitializer implements MetricsInitializer, PeriodMeter {
   public static final String VERTX_ENDPOINTS = "servicecomb.vertx.endpoints";
 
   public static final String ENDPOINTS_TYPE = "type";
@@ -36,18 +37,28 @@ public class VertxMetersInitializer implements MetricsInitializer {
 
   public static final String ENDPOINTS_SERVER = "server";
 
+  private HttpClientEndpointsMeter httpClientEndpointsMeter;
+
+  private ServerEndpointsMeter serverEndpointsMeter;
+
   @Override
   public void init(MeterRegistry meterRegistry, EventBus eventBus, MetricsBootstrapConfig config) {
-    new HttpClientEndpointsMeter(meterRegistry, VERTX_ENDPOINTS,
+    httpClientEndpointsMeter = new HttpClientEndpointsMeter(meterRegistry, VERTX_ENDPOINTS,
         Tags.of(ENDPOINTS_TYPE, ENDPOINTS_CLIENT),
         SharedVertxFactory.getMetricsFactory(config.getEnvironment())
             .getVertxMetrics()
             .getClientEndpointMetricManager());
 
-    new ServerEndpointsMeter(meterRegistry, VERTX_ENDPOINTS,
+    serverEndpointsMeter = new ServerEndpointsMeter(meterRegistry, VERTX_ENDPOINTS,
         Tags.of(ENDPOINTS_TYPE, ENDPOINTS_SERVER),
         SharedVertxFactory.getMetricsFactory(config.getEnvironment())
             .getVertxMetrics()
             .getServerEndpointMetricMap());
   }
+
+  @Override
+  public void poll(long msNow, long secondInterval) {
+    httpClientEndpointsMeter.poll(msNow, secondInterval);
+    serverEndpointsMeter.poll(msNow, secondInterval);
+  }
 }
diff --git a/metrics/metrics-core/src/main/java/com/netflix/spectator/api/patterns/ThreadPoolMonitorPublishModelFactory.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/ThreadPoolMonitorPublishModelFactory.java
similarity index 98%
rename from metrics/metrics-core/src/main/java/com/netflix/spectator/api/patterns/ThreadPoolMonitorPublishModelFactory.java
rename to metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/ThreadPoolMonitorPublishModelFactory.java
index c631defb7..d6b7fb7bf 100644
--- a/metrics/metrics-core/src/main/java/com/netflix/spectator/api/patterns/ThreadPoolMonitorPublishModelFactory.java
+++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/ThreadPoolMonitorPublishModelFactory.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.netflix.spectator.api.patterns;
+package org.apache.servicecomb.metrics.core.meter;
 
 import java.util.Map;
 
diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/AbstractInvocationMeter.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/AbstractInvocationMeter.java
index 34ef22121..bf7bb0429 100644
--- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/AbstractInvocationMeter.java
+++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/AbstractInvocationMeter.java
@@ -23,6 +23,7 @@ import org.apache.commons.lang3.StringUtils;
 import org.apache.servicecomb.core.event.InvocationFinishEvent;
 import org.apache.servicecomb.core.invocation.InvocationStageTrace;
 import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig;
+import org.apache.servicecomb.foundation.metrics.meter.LatencyDistributionConfig;
 
 import io.micrometer.core.instrument.MeterRegistry;
 import io.micrometer.core.instrument.Tags;
@@ -53,7 +54,7 @@ public abstract class AbstractInvocationMeter {
   }
 
   protected static Duration[] toDuration(String config) {
-    config = config.trim() + "," + Long.MAX_VALUE;
+    config = config.trim() + "," + LatencyDistributionConfig.MAX_LATENCY;
     String[] array = config.split("\\s*,+\\s*");
     Duration[] result = new Duration[array.length];
 
@@ -67,7 +68,13 @@ public abstract class AbstractInvocationMeter {
 
       result[idx] = Duration.ofMillis(msMin);
     }
-    result[array.length - 1] = Duration.ofMillis(Long.MAX_VALUE);
+    result[array.length - 1] = Duration.ofMillis(LatencyDistributionConfig.MAX_LATENCY);
+
+    if (result[0].toMillis() == 0) {
+      Duration[] target = new Duration[result.length - 1];
+      System.arraycopy(result, 1, target, 0, target.length);
+      return target;
+    }
 
     return result;
   }
diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/MeterInvocationConst.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/MeterInvocationConst.java
index ab0ab0b34..fd5d53f9e 100644
--- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/MeterInvocationConst.java
+++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/MeterInvocationConst.java
@@ -16,7 +16,7 @@
  */
 package org.apache.servicecomb.metrics.core.meter.invocation;
 
-import com.netflix.spectator.api.Statistic;
+import io.micrometer.core.instrument.Statistic;
 
 public interface MeterInvocationConst {
   String INVOCATION_NAME = "servicecomb.invocation";
@@ -36,7 +36,7 @@ public interface MeterInvocationConst {
 
   String TAG_STATUS = "status";
 
-  String TAG_STATISTIC = Statistic.count.key();
+  String TAG_STATISTIC = Statistic.COUNT.name();
 
   String EDGE_INVOCATION_NAME = "EDGE";
 }
diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/CpuMeter.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/CpuMeter.java
index f7d7f83b2..388a24d61 100644
--- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/CpuMeter.java
+++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/CpuMeter.java
@@ -16,8 +16,6 @@
  */
 package org.apache.servicecomb.metrics.core.meter.os;
 
-import java.io.IOException;
-
 import org.apache.servicecomb.foundation.metrics.meter.PeriodMeter;
 import org.apache.servicecomb.metrics.core.meter.os.cpu.OsCpuUsage;
 import org.apache.servicecomb.metrics.core.meter.os.cpu.ProcessCpuUsage;
@@ -48,6 +46,11 @@ public class CpuMeter implements PeriodMeter {
 
     Gauge.builder(name, processCpuUsage::getUsage).tags(Tags.of(OsMeter.OS_TYPE, OsMeter.OS_TYPE_PROCESS_CPU))
         .register(meterRegistry);
+
+    // read initial data, and cannot calculate usage, set to 0 first.
+    poll(0, 0);
+    allCpuUsage.setUsage(0);
+    processCpuUsage.setUsage(0);
   }
 
   @Override
@@ -55,7 +58,7 @@ public class CpuMeter implements PeriodMeter {
     try {
       allCpuUsage.update();
       processCpuUsage.update();
-    } catch (IOException e) {
+    } catch (Throwable e) {
       LOGGER.error("Failed to update cpu usage", e);
     }
   }
diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/NetMeter.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/NetMeter.java
index 28427801b..9cdf72318 100644
--- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/NetMeter.java
+++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/NetMeter.java
@@ -61,6 +61,7 @@ public class NetMeter implements PeriodMeter {
     this.meterRegistry = meterRegistry;
     this.name = name;
     this.tags = tags;
+    poll(0, 0);
   }
 
   @Override
diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/net/InterfaceUsage.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/net/InterfaceUsage.java
index 432975bfa..b33d30d8c 100644
--- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/net/InterfaceUsage.java
+++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/net/InterfaceUsage.java
@@ -22,6 +22,8 @@ import static org.apache.servicecomb.metrics.core.meter.os.NetMeter.TAG_PACKETS_
 import static org.apache.servicecomb.metrics.core.meter.os.NetMeter.TAG_RECEIVE;
 import static org.apache.servicecomb.metrics.core.meter.os.NetMeter.TAG_SEND;
 
+import com.google.common.annotations.VisibleForTesting;
+
 import io.micrometer.core.instrument.Gauge;
 import io.micrometer.core.instrument.MeterRegistry;
 import io.micrometer.core.instrument.Tag;
@@ -44,16 +46,16 @@ public class InterfaceUsage {
 
     // recv/Bps
     receive = new NetStat(0);
-    Gauge.builder(name, receive::getRate).tags(tags.and(TAG_RECEIVE, null)).register(meterRegistry);
+    Gauge.builder(name, receive::getRate).tags(tags.and(TAG_RECEIVE)).register(meterRegistry);
     // send/Bps
     send = new NetStat(8);
-    Gauge.builder(name, send::getRate).tags(tags.and(TAG_SEND, null)).register(meterRegistry);
+    Gauge.builder(name, send::getRate).tags(tags.and(TAG_SEND)).register(meterRegistry);
     // recv/pps
     packetsReceive = new NetStat(1);
-    Gauge.builder(name, packetsReceive::getRate).tags(tags.and(TAG_PACKETS_RECEIVE, null)).register(meterRegistry);
+    Gauge.builder(name, packetsReceive::getRate).tags(tags.and(TAG_PACKETS_RECEIVE)).register(meterRegistry);
     // send/pps
     packetsSend = new NetStat(9);
-    Gauge.builder(name, packetsSend::getRate).tags(tags.and(TAG_PACKETS_SEND, null)).register(meterRegistry);
+    Gauge.builder(name, packetsSend::getRate).tags(tags.and(TAG_PACKETS_SEND)).register(meterRegistry);
   }
 
   public void update(String interfaceData, long secondInterval) {
@@ -64,6 +66,26 @@ public class InterfaceUsage {
     packetsSend.update(netInfo, secondInterval);
   }
 
+  @VisibleForTesting
+  public NetStat getReceive() {
+    return receive;
+  }
+
+  @VisibleForTesting
+  public NetStat getSend() {
+    return send;
+  }
+
+  @VisibleForTesting
+  public NetStat getPacketsReceive() {
+    return packetsReceive;
+  }
+
+  @VisibleForTesting
+  public NetStat getPacketsSend() {
+    return packetsSend;
+  }
+
   public String getName() {
     return interfaceName;
   }
diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/EndpointMeter.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/EndpointMeter.java
index 3624ef441..f00cf22e0 100644
--- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/EndpointMeter.java
+++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/EndpointMeter.java
@@ -18,6 +18,7 @@ package org.apache.servicecomb.metrics.core.meter.vertx;
 
 import java.util.concurrent.TimeUnit;
 
+import org.apache.servicecomb.foundation.metrics.meter.PeriodMeter;
 import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultEndpointMetric;
 
 import io.micrometer.core.instrument.Gauge;
@@ -25,7 +26,7 @@ import io.micrometer.core.instrument.MeterRegistry;
 import io.micrometer.core.instrument.Tag;
 import io.micrometer.core.instrument.Tags;
 
-public class EndpointMeter {
+public class EndpointMeter implements PeriodMeter {
   private static final double SNV_MILLI_SECONDS = 1.0 / TimeUnit.MILLISECONDS.toNanos(1L);
 
   public static final String ADDRESS = "address";
@@ -64,72 +65,60 @@ public class EndpointMeter {
 
   private final Gauge latency;
 
+  private long currentConnectCount;
+
   private long lastConnectCount;
 
+  private long currentDisconnectCount;
+
   private long lastDisconnectCount;
 
+  private long currentBytesRead;
+
   private long lastBytesRead;
 
+  private long currentBytesWritten;
+
   private long lastBytesWritten;
 
+  private long currentRequests;
+
   private long lastRequests;
 
+  private long currentRequestsForLatency;
+
   private long lastRequestsForLatency;
 
+  private long currentLatency;
+
   private long lastLatency;
 
   public EndpointMeter(MeterRegistry meterRegistry, String name, Tags tags, DefaultEndpointMetric metric) {
     this.meterRegistry = meterRegistry;
+    this.metric = metric;
 
     tags = tags.and(Tag.of(ADDRESS, metric.getAddress()));
-    connectCount = Gauge.builder(name, () -> {
-          long current = metric.getConnectCount();
-          long result = current - lastConnectCount;
-          lastConnectCount = current;
-          return result;
-        })
+    connectCount = Gauge.builder(name, () -> currentConnectCount)
         .tags(tags.and(Tag.of(STATISTIC, CONNECT_COUNT)))
         .register(meterRegistry);
-    disconnectCount = Gauge.builder(name, () -> {
-          long current = metric.getDisconnectCount();
-          long result = current - lastDisconnectCount;
-          lastDisconnectCount = current;
-          return result;
-        }).tags(tags.and(Tag.of(STATISTIC, DISCONNECT_COUNT)))
+    disconnectCount = Gauge.builder(name, () -> currentDisconnectCount)
+        .tags(tags.and(Tag.of(STATISTIC, DISCONNECT_COUNT)))
         .register(meterRegistry);
-    connections = Gauge.builder(name, () -> metric.getConnectCount() - metric.getDisconnectCount())
+    connections = Gauge.builder(name, () -> currentConnectCount - currentDisconnectCount)
         .tags(tags.and(Tag.of(STATISTIC, CONNECTIONS)))
         .register(meterRegistry);
-    bytesRead = Gauge.builder(name, () -> {
-          long current = metric.getBytesRead();
-          long result = current - lastBytesRead;
-          lastBytesRead = current;
-          return result;
-        }).tags(tags.and(Tag.of(STATISTIC, BYTES_READ)))
+    bytesRead = Gauge.builder(name, () -> currentBytesRead)
+        .tags(tags.and(Tag.of(STATISTIC, BYTES_READ)))
         .register(meterRegistry);
-    bytesWritten = Gauge.builder(name, () -> {
-          long current = metric.getBytesWritten();
-          long result = current - lastBytesWritten;
-          lastBytesWritten = current;
-          return result;
-        }).tags(tags.and(Tag.of(STATISTIC, BYTES_WRITTEN)))
+    bytesWritten = Gauge.builder(name, () -> currentBytesWritten)
+        .tags(tags.and(Tag.of(STATISTIC, BYTES_WRITTEN)))
         .register(meterRegistry);
-    requests = Gauge.builder(name, () -> {
-          long current = metric.getRequests();
-          long result = current - lastRequests;
-          lastRequests = current;
-          return result;
-        }).tags(tags.and(Tag.of(STATISTIC, REQUESTS)))
+    requests = Gauge.builder(name, () -> currentRequests)
+        .tags(tags.and(Tag.of(STATISTIC, REQUESTS)))
         .register(meterRegistry);
-    latency = Gauge.builder(name, () -> {
-          long currentLatency = metric.getLatency();
-          long currentRequests = metric.getRequests();
-          double result = currentRequests - lastRequestsForLatency == 0 ? 0 :
-              (currentLatency - lastLatency) / ((double) (currentRequests - lastRequestsForLatency)) * SNV_MILLI_SECONDS;
-          lastRequestsForLatency = currentRequests;
-          lastLatency = currentLatency;
-          return result;
-        }).tags(tags.and(Tag.of(STATISTIC, LATENCY)))
+    latency = Gauge.builder(name, () -> currentRequestsForLatency == 0 ? 0 :
+            (currentLatency) / ((double) (currentRequestsForLatency)) * SNV_MILLI_SECONDS)
+        .tags(tags.and(Tag.of(STATISTIC, LATENCY)))
         .register(meterRegistry);
   }
 
@@ -146,4 +135,34 @@ public class EndpointMeter {
     this.meterRegistry.remove(requests);
     this.meterRegistry.remove(latency);
   }
+
+  @Override
+  public void poll(long msNow, long secondInterval) {
+    long temp = metric.getConnectCount();
+    currentConnectCount = temp - lastConnectCount;
+    lastConnectCount = temp;
+
+    temp = metric.getDisconnectCount();
+    currentDisconnectCount = temp - lastDisconnectCount;
+    lastDisconnectCount = temp;
+
+    temp = metric.getBytesRead();
+    currentBytesRead = temp - lastBytesRead;
+    lastBytesRead = temp;
+
+    temp = metric.getBytesWritten();
+    currentBytesWritten = temp - lastBytesWritten;
+    lastBytesWritten = temp;
+
+    temp = metric.getRequests();
+    currentRequests = temp - lastRequests;
+    lastRequests = temp;
+
+    temp = metric.getRequests();
+    currentRequestsForLatency = temp - lastRequestsForLatency;
+    lastRequestsForLatency = temp;
+    temp = metric.getLatency();
+    currentLatency = temp - lastLatency;
+    lastLatency = temp;
+  }
 }
diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/HttpClientEndpointMeter.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/HttpClientEndpointMeter.java
index a80f4643e..ad3143de3 100644
--- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/HttpClientEndpointMeter.java
+++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/HttpClientEndpointMeter.java
@@ -27,10 +27,19 @@ import io.micrometer.core.instrument.Tags;
 public class HttpClientEndpointMeter extends EndpointMeter {
   public static final String QUEUE_COUNT = "queueCount";
 
+  private long currentQueueCount;
+
   public HttpClientEndpointMeter(MeterRegistry meterRegistry, String name, Tags tags, DefaultEndpointMetric metric) {
     super(meterRegistry, name, tags, metric);
-    Gauge.builder(name, () -> ((DefaultClientEndpointMetric) metric).getQueueCount())
-        .tags(tags.and(Tag.of(STATISTIC, QUEUE_COUNT)))
+    Gauge.builder(name, () -> currentQueueCount)
+        .tags(tags.and(Tag.of(STATISTIC, QUEUE_COUNT), Tag.of(ADDRESS, metric.getAddress())))
         .register(meterRegistry);
   }
+
+  @Override
+  public void poll(long msNow, long secondInterval) {
+    super.poll(msNow, secondInterval);
+
+    currentQueueCount = ((DefaultClientEndpointMetric) metric).getQueueCount();
+  }
 }
diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/HttpClientEndpointsMeter.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/HttpClientEndpointsMeter.java
index 78169f6af..f68f8413c 100644
--- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/HttpClientEndpointsMeter.java
+++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/HttpClientEndpointsMeter.java
@@ -26,7 +26,6 @@ public class HttpClientEndpointsMeter extends VertxEndpointsMeter {
   public <T extends DefaultEndpointMetric> HttpClientEndpointsMeter(MeterRegistry meterRegistry, String name, Tags tags,
       DefaultClientEndpointMetricManager clientEndpointMetricManager) {
     super(meterRegistry, name, tags, clientEndpointMetricManager.getClientEndpointMetricMap());
-    clientEndpointMetricManager.addChangeListener(this::onChanged);
   }
 
   @Override
diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/ServerEndpointMeter.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/ServerEndpointMeter.java
index 2e8c6d2fb..aa16f3eed 100644
--- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/ServerEndpointMeter.java
+++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/ServerEndpointMeter.java
@@ -29,15 +29,21 @@ public class ServerEndpointMeter extends EndpointMeter {
 
   private long lastRejectByConnectionLimit;
 
+  private long currentRejectByConnectionLimit;
+
   public ServerEndpointMeter(MeterRegistry meterRegistry, String name, Tags tags, DefaultEndpointMetric metric) {
     super(meterRegistry, name, tags, metric);
-    Gauge.builder(name, () -> {
-          long current = ((DefaultServerEndpointMetric) metric).getRejectByConnectionLimitCount();
-          long result = current - lastRejectByConnectionLimit;
-          lastRejectByConnectionLimit = current;
-          return result;
-        })
-        .tags(tags.and(Tag.of(STATISTIC, REJECT_BY_CONNECTION_LIMIT)))
+    Gauge.builder(name, () -> currentRejectByConnectionLimit)
+        .tags(tags.and(Tag.of(STATISTIC, REJECT_BY_CONNECTION_LIMIT), Tag.of(ADDRESS, metric.getAddress())))
         .register(meterRegistry);
   }
+
+  @Override
+  public void poll(long msNow, long secondInterval) {
+    super.poll(msNow, secondInterval);
+
+    long current = ((DefaultServerEndpointMetric) metric).getRejectByConnectionLimitCount();
+    currentRejectByConnectionLimit = current - lastRejectByConnectionLimit;
+    lastRejectByConnectionLimit = current;
+  }
 }
diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/VertxEndpointsMeter.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/VertxEndpointsMeter.java
index 829f17746..fd4d10f0d 100644
--- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/VertxEndpointsMeter.java
+++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/VertxEndpointsMeter.java
@@ -19,12 +19,13 @@ package org.apache.servicecomb.metrics.core.meter.vertx;
 import java.util.Map;
 
 import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx;
+import org.apache.servicecomb.foundation.metrics.meter.PeriodMeter;
 import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultEndpointMetric;
 
 import io.micrometer.core.instrument.MeterRegistry;
 import io.micrometer.core.instrument.Tags;
 
-public class VertxEndpointsMeter {
+public class VertxEndpointsMeter implements PeriodMeter {
   private final Map<String, DefaultEndpointMetric> endpointMetricMap;
 
   protected final MeterRegistry meterRegistry;
@@ -42,10 +43,9 @@ public class VertxEndpointsMeter {
     this.name = name;
     this.tags = tags;
     this.endpointMetricMap = (Map<String, DefaultEndpointMetric>) endpointMetricMap;
-    syncMeters();
   }
 
-  private void syncMeters() {
+  private void syncMeters(long msNow, long secondInterval) {
     for (EndpointMeter meter : endpointMeterMap.values()) {
       if (!endpointMetricMap.containsKey(meter.getMetric().getAddress())) {
         EndpointMeter removed = endpointMeterMap.remove(meter.getMetric().getAddress());
@@ -53,7 +53,9 @@ public class VertxEndpointsMeter {
       }
     }
     for (DefaultEndpointMetric metric : endpointMetricMap.values()) {
-      endpointMeterMap.computeIfAbsent(metric.getAddress(), address -> createEndpointMeter(metric));
+      EndpointMeter updated = endpointMeterMap.computeIfAbsent(metric.getAddress(),
+          address -> createEndpointMeter(metric));
+      updated.poll(msNow, secondInterval);
     }
   }
 
@@ -61,7 +63,8 @@ public class VertxEndpointsMeter {
     return new EndpointMeter(meterRegistry, name, tags, metric);
   }
 
-  protected void onChanged() {
-    syncMeters();
+  @Override
+  public void poll(long msNow, long secondInterval) {
+    syncMeters(msNow, secondInterval);
   }
 }
diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/DefaultLogPublisher.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/DefaultLogPublisher.java
index 48e0d047d..4d7ae9954 100644
--- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/DefaultLogPublisher.java
+++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/DefaultLogPublisher.java
@@ -119,7 +119,7 @@ public class DefaultLogPublisher implements MetricsInitializer {
     latencyDistributionConfig = new LatencyDistributionConfig(config.getLatencyDistribution());
     String header;
     for (LatencyScopeConfig scopeConfig : latencyDistributionConfig.getScopeConfigs()) {
-      if (scopeConfig.getMsMax() == Long.MAX_VALUE) {
+      if (scopeConfig.getMsMax() == LatencyDistributionConfig.MAX_LATENCY) {
         header = String.format("[%d,) ", scopeConfig.getMsMin());
       } else {
         header = String.format("[%d,%d) ", scopeConfig.getMsMin(), scopeConfig.getMsMax());
diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/PublishModelFactory.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/PublishModelFactory.java
index 68aaad35c..6554fc936 100644
--- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/PublishModelFactory.java
+++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/PublishModelFactory.java
@@ -31,7 +31,7 @@ import org.apache.servicecomb.metrics.core.publish.model.DefaultPublishModel;
 import org.apache.servicecomb.metrics.core.publish.model.invocation.OperationPerfGroups;
 import org.apache.servicecomb.swagger.invocation.InvocationType;
 
-import com.netflix.spectator.api.patterns.ThreadPoolMonitorPublishModelFactory;
+import org.apache.servicecomb.metrics.core.meter.ThreadPoolMonitorPublishModelFactory;
 
 import io.micrometer.core.instrument.Meter;
 
@@ -62,19 +62,16 @@ public class PublishModelFactory {
         MeterInvocationConst.TAG_OPERATION,
         MeterInvocationConst.TAG_STATUS,
         MeterInvocationConst.TAG_TYPE,
-        new DefaultTagFinder(MeterInvocationConst.TAG_STAGE, true),
-        MeterInvocationConst.TAG_STATISTIC);
+        new DefaultTagFinder(MeterInvocationConst.TAG_STAGE, true));
 
     //os config
     groupConfig.addGroup(OsMeter.OS_NAME,
         OsMeter.OS_TYPE,
-        new DefaultTagFinder(NetMeter.INTERFACE, true),
-        NetMeter.STATISTIC);
+        new DefaultTagFinder(NetMeter.INTERFACE, true));
 
     groupConfig.addGroup(VertxMetersInitializer.VERTX_ENDPOINTS,
         VertxMetersInitializer.ENDPOINTS_TYPE,
-        EndpointMeter.ADDRESS,
-        EndpointMeter.STATISTIC);
+        EndpointMeter.ADDRESS, EndpointMeter.STATISTIC);
 
     return groupConfig;
   }
diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/PublishUtils.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/PublishUtils.java
index 3196120d7..94b9c43ab 100644
--- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/PublishUtils.java
+++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/PublishUtils.java
@@ -26,7 +26,7 @@ import org.apache.servicecomb.metrics.core.publish.model.invocation.OperationPer
 import org.apache.servicecomb.metrics.core.publish.model.invocation.OperationPerfGroups;
 import org.apache.servicecomb.metrics.core.publish.model.invocation.PerfInfo;
 
-import com.netflix.spectator.api.Statistic;
+import io.micrometer.core.instrument.Statistic;
 
 public final class PublishUtils {
   private PublishUtils() {
@@ -34,10 +34,10 @@ public final class PublishUtils {
 
   public static PerfInfo createPerfInfo(MeasurementNode stageNode) {
     PerfInfo perfInfo = new PerfInfo();
-    perfInfo.setTps(stageNode.findChild(Statistic.count.name()).summary());
-    perfInfo.setMsTotalTime(stageNode.findChild(Statistic.totalTime.name()).summary() * 1000);
+    perfInfo.setTps(stageNode.findChild(Statistic.COUNT.name()).summary());
+    perfInfo.setMsTotalTime(stageNode.findChild(Statistic.TOTAL_TIME.name()).summary() * 1000);
     // when UT with DefaultRegistry, there is no max value
-    MeasurementNode maxNode = stageNode.findChild(Statistic.max.name());
+    MeasurementNode maxNode = stageNode.findChild(Statistic.MAX.name());
     if (maxNode != null) {
       perfInfo.setMsMaxLatency(maxNode.summary() * 1000);
     }
@@ -57,7 +57,7 @@ public final class PublishUtils {
     MeasurementNode latencyNode = statusNode.findChild(MeterInvocationConst.TAG_LATENCY_DISTRIBUTION);
     if (latencyNode != null && latencyNode.getMeasurements() != null) {
       operationPerf.setLatencyDistribution(latencyNode.getMeasurements().stream()
-          .map(m -> (int) m.value())
+          .map(m -> (int) m.getValue())
           .toArray(Integer[]::new));
     }
     return operationPerf;
diff --git a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/AssertUtil.java b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/AssertUtil.java
index f89216299..f154ba654 100644
--- a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/AssertUtil.java
+++ b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/AssertUtil.java
@@ -16,15 +16,15 @@
  */
 package org.apache.servicecomb.metrics.core;
 
-import com.netflix.spectator.api.Measurement;
+import java.util.List;
+
 import org.junit.jupiter.api.Assertions;
 
-import java.util.List;
+import io.micrometer.core.instrument.Measurement;
 
 public class AssertUtil {
 
-    public static void assertMeasure(List<Measurement> measurements, int index, String expected) {
-        Assertions.assertEquals(String.format("Measurement(%s)", expected), measurements.get(index).toString());
-    }
-
+  public static void assertMeasure(List<Measurement> measurements, int index, String expected) {
+    Assertions.assertEquals(String.format("Measurement{%s}", expected), measurements.get(index).toString());
+  }
 }
diff --git a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestInvocationMetersInitializer.java b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestInvocationMetersInitializer.java
index c25487746..16f9b67ad 100644
--- a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestInvocationMetersInitializer.java
+++ b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestInvocationMetersInitializer.java
@@ -38,20 +38,19 @@ import org.mockito.Mockito;
 import org.springframework.core.env.Environment;
 
 import com.google.common.eventbus.EventBus;
-import com.netflix.spectator.api.DefaultRegistry;
-import com.netflix.spectator.api.ManualClock;
-import com.netflix.spectator.api.Measurement;
-import com.netflix.spectator.api.Registry;
 
+import io.micrometer.core.instrument.Measurement;
+import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
 import mockit.Expectations;
 import mockit.Mocked;
 
 public class TestInvocationMetersInitializer {
   EventBus eventBus = new EventBus();
 
-  GlobalRegistry globalRegistry = new GlobalRegistry(new ManualClock());
+  MeterRegistry registry = new SimpleMeterRegistry();
 
-  Registry registry = new DefaultRegistry(globalRegistry.getClock());
+  GlobalRegistry globalRegistry = new GlobalRegistry(registry);
 
   InvocationMetersInitializer invocationMetersInitializer = new InvocationMetersInitializer();
 
@@ -67,8 +66,7 @@ public class TestInvocationMetersInitializer {
     Mockito.when(environment.getProperty(
             CONFIG_LATENCY_DISTRIBUTION_MIN_SCOPE_LEN, int.class, 7))
         .thenReturn(7);
-    globalRegistry.add(registry);
-    invocationMetersInitializer.init(globalRegistry, eventBus, new MetricsBootstrapConfig(environment));
+    invocationMetersInitializer.init(registry, eventBus, new MetricsBootstrapConfig(environment));
   }
 
   @Test
@@ -109,51 +107,88 @@ public class TestInvocationMetersInitializer {
     globalRegistry.poll(1);
 
     MeasurementTree tree = new MeasurementTree();
-    tree.from(registry.iterator(), new MeasurementGroupConfig(MeterInvocationConst.INVOCATION_NAME));
-    List<Measurement> measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME).getMeasurements();
-    Assert.assertEquals(21, measurements.size());
+    tree.from(registry.getMeters().iterator(),
+        new MeasurementGroupConfig(MeterInvocationConst.INVOCATION_NAME, "stage"));
+    List<Measurement> measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "total")
+        .getMeasurements();
+    Assert.assertEquals(3, measurements.size());
     AssertUtil.assertMeasure(measurements, 0,
-        "servicecomb.invocation:operation=m.s.o:role=CONSUMER:stage=total:statistic=count:status=0:transport=rest:type=stage,0,2.0");
+        "statistic='COUNT', value=2.0");
     AssertUtil.assertMeasure(measurements, 1,
-        "servicecomb.invocation:operation=m.s.o:role=CONSUMER:stage=total:statistic=totalTime:status=0:transport=rest:type=stage,0,1.8000000000000002E-8");
+        "statistic='TOTAL_TIME', value=1.8E-8");
     AssertUtil.assertMeasure(measurements, 2,
-        "servicecomb.invocation:operation=m.s.o:role=CONSUMER:stage=total:statistic=max:status=0:transport=rest:type=stage,0,9.000000000000001E-9");
-    AssertUtil.assertMeasure(measurements, 3,
-        "servicecomb.invocation:operation=m.s.o:role=CONSUMER:stage=prepare:statistic=count:status=0:transport=rest:type=stage,0,2.0");
-    AssertUtil.assertMeasure(measurements, 4,
-        "servicecomb.invocation:operation=m.s.o:role=CONSUMER:stage=prepare:statistic=totalTime:status=0:transport=rest:type=stage,0,1.8000000000000002E-8");
-    AssertUtil.assertMeasure(measurements, 5,
-        "servicecomb.invocation:operation=m.s.o:role=CONSUMER:stage=prepare:statistic=max:status=0:transport=rest:type=stage,0,9.000000000000001E-9");
-    AssertUtil.assertMeasure(measurements, 6,
-        "servicecomb.invocation:operation=m.s.o:role=CONSUMER:stage=consumer-send:statistic=count:status=0:transport=rest:type=stage,0,2.0");
-    AssertUtil.assertMeasure(measurements, 7,
-        "servicecomb.invocation:operation=m.s.o:role=CONSUMER:stage=consumer-send:statistic=totalTime:status=0:transport=rest:type=stage,0,1.0E-8");
-    AssertUtil.assertMeasure(measurements, 8,
-        "servicecomb.invocation:operation=m.s.o:role=CONSUMER:stage=consumer-send:statistic=max:status=0:transport=rest:type=stage,0,5.0E-9");
-    AssertUtil.assertMeasure(measurements, 9,
-        "servicecomb.invocation:operation=m.s.o:role=CONSUMER:stage=connection:statistic=count:status=0:transport=rest:type=stage,0,2.0");
-    AssertUtil.assertMeasure(measurements, 10,
-        "servicecomb.invocation:operation=m.s.o:role=CONSUMER:stage=connection:statistic=totalTime:status=0:transport=rest:type=stage,0,1.8000000000000002E-8");
-    AssertUtil.assertMeasure(measurements, 11,
-        "servicecomb.invocation:operation=m.s.o:role=CONSUMER:stage=connection:statistic=max:status=0:transport=rest:type=stage,0,9.000000000000001E-9");
-    AssertUtil.assertMeasure(measurements, 12,
-        "servicecomb.invocation:operation=m.s.o:role=CONSUMER:stage=consumer-encode:statistic=count:status=0:transport=rest:type=stage,0,2.0");
-    AssertUtil.assertMeasure(measurements, 13,
-        "servicecomb.invocation:operation=m.s.o:role=CONSUMER:stage=consumer-encode:statistic=totalTime:status=0:transport=rest:type=stage,0,8.0E-9");
-    AssertUtil.assertMeasure(measurements, 14,
-        "servicecomb.invocation:operation=m.s.o:role=CONSUMER:stage=consumer-encode:statistic=max:status=0:transport=rest:type=stage,0,4.0E-9");
-    AssertUtil.assertMeasure(measurements, 15,
-        "servicecomb.invocation:operation=m.s.o:role=CONSUMER:stage=wait:statistic=count:status=0:transport=rest:type=stage,0,2.0");
-    AssertUtil.assertMeasure(measurements, 16,
-        "servicecomb.invocation:operation=m.s.o:role=CONSUMER:stage=wait:statistic=totalTime:status=0:transport=rest:type=stage,0,1.8000000000000002E-8");
-    AssertUtil.assertMeasure(measurements, 17,
-        "servicecomb.invocation:operation=m.s.o:role=CONSUMER:stage=wait:statistic=max:status=0:transport=rest:type=stage,0,9.000000000000001E-9");
-    AssertUtil.assertMeasure(measurements, 18,
-        "servicecomb.invocation:operation=m.s.o:role=CONSUMER:stage=consumer-decode:statistic=count:status=0:transport=rest:type=stage,0,2.0");
-    AssertUtil.assertMeasure(measurements, 19,
-        "servicecomb.invocation:operation=m.s.o:role=CONSUMER:stage=consumer-decode:statistic=totalTime:status=0:transport=rest:type=stage,0,1.8000000000000002E-8");
-    AssertUtil.assertMeasure(measurements, 20,
-        "servicecomb.invocation:operation=m.s.o:role=CONSUMER:stage=consumer-decode:statistic=max:status=0:transport=rest:type=stage,0,9.000000000000001E-9");
+        "statistic='MAX', value=9.0E-9");
+
+    measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "prepare")
+        .getMeasurements();
+    Assert.assertEquals(3, measurements.size());
+    AssertUtil.assertMeasure(measurements, 0,
+        "statistic='COUNT', value=2.0");
+    AssertUtil.assertMeasure(measurements, 1,
+        "statistic='TOTAL_TIME', value=1.8E-8");
+    AssertUtil.assertMeasure(measurements, 2,
+        "statistic='MAX', value=9.0E-9");
+
+    measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "consumer-send")
+        .getMeasurements();
+    Assert.assertEquals(3, measurements.size());
+    AssertUtil.assertMeasure(measurements, 0,
+        "statistic='COUNT', value=2.0");
+    AssertUtil.assertMeasure(measurements, 1,
+        "statistic='TOTAL_TIME', value=1.0E-8");
+    AssertUtil.assertMeasure(measurements, 2,
+        "statistic='MAX', value=5.0E-9");
+
+    measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "connection")
+        .getMeasurements();
+    Assert.assertEquals(3, measurements.size());
+    AssertUtil.assertMeasure(measurements, 0,
+        "statistic='COUNT', value=2.0");
+    AssertUtil.assertMeasure(measurements, 1,
+        "statistic='TOTAL_TIME', value=1.8E-8");
+    AssertUtil.assertMeasure(measurements, 2,
+        "statistic='MAX', value=9.0E-9");
+
+    measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "consumer-encode")
+        .getMeasurements();
+    Assert.assertEquals(3, measurements.size());
+    Assert.assertEquals(3, measurements.size());
+    AssertUtil.assertMeasure(measurements, 0,
+        "statistic='COUNT', value=2.0");
+    AssertUtil.assertMeasure(measurements, 1,
+        "statistic='TOTAL_TIME', value=8.0E-9");
+    AssertUtil.assertMeasure(measurements, 2,
+        "statistic='MAX', value=4.0E-9");
+
+    measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "connection")
+        .getMeasurements();
+    Assert.assertEquals(3, measurements.size());
+    AssertUtil.assertMeasure(measurements, 0,
+        "statistic='COUNT', value=2.0");
+    AssertUtil.assertMeasure(measurements, 1,
+        "statistic='TOTAL_TIME', value=1.8E-8");
+    AssertUtil.assertMeasure(measurements, 2,
+        "statistic='MAX', value=9.0E-9");
+
+    measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "wait")
+        .getMeasurements();
+    Assert.assertEquals(3, measurements.size());
+    AssertUtil.assertMeasure(measurements, 0,
+        "statistic='COUNT', value=2.0");
+    AssertUtil.assertMeasure(measurements, 1,
+        "statistic='TOTAL_TIME', value=1.8E-8");
+    AssertUtil.assertMeasure(measurements, 2,
+        "statistic='MAX', value=9.0E-9");
+
+    measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "consumer-decode")
+        .getMeasurements();
+    Assert.assertEquals(3, measurements.size());
+    AssertUtil.assertMeasure(measurements, 0,
+        "statistic='COUNT', value=2.0");
+    AssertUtil.assertMeasure(measurements, 1,
+        "statistic='TOTAL_TIME', value=1.8E-8");
+    AssertUtil.assertMeasure(measurements, 2,
+        "statistic='MAX', value=9.0E-9");
   }
 
   @Test
@@ -199,70 +234,108 @@ public class TestInvocationMetersInitializer {
     globalRegistry.poll(1);
 
     MeasurementTree tree = new MeasurementTree();
-    tree.from(registry.iterator(), new MeasurementGroupConfig(MeterInvocationConst.INVOCATION_NAME));
-    List<Measurement> measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME).getMeasurements();
-    Assert.assertEquals(30, measurements.size());
-    int i = 0;
-    AssertUtil.assertMeasure(measurements, i++,
-        "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=total:statistic=count:status=0:transport=rest:type=stage,0,2.0");
-    AssertUtil.assertMeasure(measurements, i++,
-        "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=total:statistic=totalTime:status=0:transport=rest:type=stage,0,1.8000000000000002E-8");
-    AssertUtil.assertMeasure(measurements, i++,
-        "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=total:statistic=max:status=0:transport=rest:type=stage,0,9.000000000000001E-9");
-    AssertUtil.assertMeasure(measurements, i++,
-        "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=prepare:statistic=count:status=0:transport=rest:type=stage,0,2.0");
-    AssertUtil.assertMeasure(measurements, i++,
-        "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=prepare:statistic=totalTime:status=0:transport=rest:type=stage,0,1.8000000000000002E-8");
-    AssertUtil.assertMeasure(measurements, i++,
-        "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=prepare:statistic=max:status=0:transport=rest:type=stage,0,9.000000000000001E-9");
-    AssertUtil.assertMeasure(measurements, i++,
-        "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=consumer-send:statistic=count:status=0:transport=rest:type=stage,0,2.0");
-    AssertUtil.assertMeasure(measurements, i++,
-        "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=consumer-send:statistic=totalTime:status=0:transport=rest:type=stage,0,1.0E-8");
-    AssertUtil.assertMeasure(measurements, i++,
-        "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=consumer-send:statistic=max:status=0:transport=rest:type=stage,0,5.0E-9");
-    AssertUtil.assertMeasure(measurements, i++,
-        "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=connection:statistic=count:status=0:transport=rest:type=stage,0,2.0");
-    AssertUtil.assertMeasure(measurements, i++,
-        "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=connection:statistic=totalTime:status=0:transport=rest:type=stage,0,1.8000000000000002E-8");
-    AssertUtil.assertMeasure(measurements, i++,
-        "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=connection:statistic=max:status=0:transport=rest:type=stage,0,9.000000000000001E-9");
-    AssertUtil.assertMeasure(measurements, i++,
-        "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=consumer-encode:statistic=count:status=0:transport=rest:type=stage,0,2.0");
-    AssertUtil.assertMeasure(measurements, i++,
-        "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=consumer-encode:statistic=totalTime:status=0:transport=rest:type=stage,0,8.0E-9");
-    AssertUtil.assertMeasure(measurements, i++,
-        "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=consumer-encode:statistic=max:status=0:transport=rest:type=stage,0,4.0E-9");
-    AssertUtil.assertMeasure(measurements, i++,
-        "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=wait:statistic=count:status=0:transport=rest:type=stage,0,2.0");
-    AssertUtil.assertMeasure(measurements, i++,
-        "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=wait:statistic=totalTime:status=0:transport=rest:type=stage,0,1.8000000000000002E-8");
-    AssertUtil.assertMeasure(measurements, i++,
-        "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=wait:statistic=max:status=0:transport=rest:type=stage,0,9.000000000000001E-9");
-    AssertUtil.assertMeasure(measurements, i++,
-        "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=consumer-decode:statistic=count:status=0:transport=rest:type=stage,0,2.0");
-    AssertUtil.assertMeasure(measurements, i++,
-        "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=consumer-decode:statistic=totalTime:status=0:transport=rest:type=stage,0,1.6E-8");
-    AssertUtil.assertMeasure(measurements, i++,
-        "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=consumer-decode:statistic=max:status=0:transport=rest:type=stage,0,8.0E-9");
-    AssertUtil.assertMeasure(measurements, i++,
-        "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=provider-decode:statistic=count:status=0:transport=rest:type=stage,0,2.0");
-    AssertUtil.assertMeasure(measurements, i++,
-        "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=provider-decode:statistic=totalTime:status=0:transport=rest:type=stage,0,1.8000000000000002E-8");
-    AssertUtil.assertMeasure(measurements, i++,
-        "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=provider-decode:statistic=max:status=0:transport=rest:type=stage,0,9.000000000000001E-9");
-    AssertUtil.assertMeasure(measurements, i++,
-        "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=provider-encode:statistic=count:status=0:transport=rest:type=stage,0,2.0");
-    AssertUtil.assertMeasure(measurements, i++,
-        "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=provider-encode:statistic=totalTime:status=0:transport=rest:type=stage,0,1.8000000000000002E-8");
-    AssertUtil.assertMeasure(measurements, i++,
-        "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=provider-encode:statistic=max:status=0:transport=rest:type=stage,0,9.000000000000001E-9");
-    AssertUtil.assertMeasure(measurements, i++,
-        "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=provider-send:statistic=count:status=0:transport=rest:type=stage,0,2.0");
-    AssertUtil.assertMeasure(measurements, i++,
-        "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=provider-send:statistic=totalTime:status=0:transport=rest:type=stage,0,1.8000000000000002E-8");
-    AssertUtil.assertMeasure(measurements, i++,
-        "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=provider-send:statistic=max:status=0:transport=rest:type=stage,0,9.000000000000001E-9");
+    tree.from(registry.getMeters().iterator(),
+        new MeasurementGroupConfig(MeterInvocationConst.INVOCATION_NAME, "stage"));
+
+    List<Measurement> measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "total")
+        .getMeasurements();
+    Assert.assertEquals(3, measurements.size());
+    AssertUtil.assertMeasure(measurements, 0,
+        "statistic='COUNT', value=2.0");
+    AssertUtil.assertMeasure(measurements, 1,
+        "statistic='TOTAL_TIME', value=1.8E-8");
+    AssertUtil.assertMeasure(measurements, 2,
+        "statistic='MAX', value=9.0E-9");
+
+    measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "prepare")
+        .getMeasurements();
+    Assert.assertEquals(3, measurements.size());
+    AssertUtil.assertMeasure(measurements, 0,
+        "statistic='COUNT', value=2.0");
+    AssertUtil.assertMeasure(measurements, 1,
+        "statistic='TOTAL_TIME', value=1.8E-8");
+    AssertUtil.assertMeasure(measurements, 2,
+        "statistic='MAX', value=9.0E-9");
+
+    measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "consumer-send")
+        .getMeasurements();
+    Assert.assertEquals(3, measurements.size());
+    AssertUtil.assertMeasure(measurements, 0,
+        "statistic='COUNT', value=2.0");
+    AssertUtil.assertMeasure(measurements, 1,
+        "statistic='TOTAL_TIME', value=1.0E-8");
+    AssertUtil.assertMeasure(measurements, 2,
+        "statistic='MAX', value=5.0E-9");
+
+    measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "connection")
+        .getMeasurements();
+    Assert.assertEquals(3, measurements.size());
+    AssertUtil.assertMeasure(measurements, 0,
+        "statistic='COUNT', value=2.0");
+    AssertUtil.assertMeasure(measurements, 1,
+        "statistic='TOTAL_TIME', value=1.8E-8");
+    AssertUtil.assertMeasure(measurements, 2,
+        "statistic='MAX', value=9.0E-9");
+
+    measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "consumer-encode")
+        .getMeasurements();
+    Assert.assertEquals(3, measurements.size());
+    AssertUtil.assertMeasure(measurements, 0,
+        "statistic='COUNT', value=2.0");
+    AssertUtil.assertMeasure(measurements, 1,
+        "statistic='TOTAL_TIME', value=8.0E-9");
+    AssertUtil.assertMeasure(measurements, 2,
+        "statistic='MAX', value=4.0E-9");
+
+    measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "wait")
+        .getMeasurements();
+    Assert.assertEquals(3, measurements.size());
+    AssertUtil.assertMeasure(measurements, 0,
+        "statistic='COUNT', value=2.0");
+    AssertUtil.assertMeasure(measurements, 1,
+        "statistic='TOTAL_TIME', value=1.8E-8");
+    AssertUtil.assertMeasure(measurements, 2,
+        "statistic='MAX', value=9.0E-9");
+
+    measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "consumer-decode")
+        .getMeasurements();
+    Assert.assertEquals(3, measurements.size());
+    AssertUtil.assertMeasure(measurements, 0,
+        "statistic='COUNT', value=2.0");
+    AssertUtil.assertMeasure(measurements, 1,
+        "statistic='TOTAL_TIME', value=1.6E-8");
+    AssertUtil.assertMeasure(measurements, 2,
+        "statistic='MAX', value=8.0E-9");
+
+    measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "provider-decode")
+        .getMeasurements();
+    Assert.assertEquals(3, measurements.size());
+    AssertUtil.assertMeasure(measurements, 0,
+        "statistic='COUNT', value=2.0");
+    AssertUtil.assertMeasure(measurements, 1,
+        "statistic='TOTAL_TIME', value=1.8E-8");
+    AssertUtil.assertMeasure(measurements, 2,
+        "statistic='MAX', value=9.0E-9");
+
+    measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "provider-encode")
+        .getMeasurements();
+    Assert.assertEquals(3, measurements.size());
+    AssertUtil.assertMeasure(measurements, 0,
+        "statistic='COUNT', value=2.0");
+    AssertUtil.assertMeasure(measurements, 1,
+        "statistic='TOTAL_TIME', value=1.8E-8");
+    AssertUtil.assertMeasure(measurements, 2,
+        "statistic='MAX', value=9.0E-9");
+
+    measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "provider-send")
+        .getMeasurements();
+    Assert.assertEquals(3, measurements.size());
+    AssertUtil.assertMeasure(measurements, 0,
+        "statistic='COUNT', value=2.0");
+    AssertUtil.assertMeasure(measurements, 1,
+        "statistic='TOTAL_TIME', value=1.8E-8");
+    AssertUtil.assertMeasure(measurements, 2,
+        "statistic='MAX', value=9.0E-9");
   }
 
   @Test
@@ -302,51 +375,77 @@ public class TestInvocationMetersInitializer {
     globalRegistry.poll(1);
 
     MeasurementTree tree = new MeasurementTree();
-    tree.from(registry.iterator(), new MeasurementGroupConfig(MeterInvocationConst.INVOCATION_NAME));
-    List<Measurement> measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME).getMeasurements();
-    Assert.assertEquals(21, measurements.size());
-    int i = 0;
-    AssertUtil.assertMeasure(measurements, i++,
-        "servicecomb.invocation:operation=m.s.o:role=PROVIDER:stage=total:statistic=count:status=0:transport=rest:type=stage,0,2.0");
-    AssertUtil.assertMeasure(measurements, i++,
-        "servicecomb.invocation:operation=m.s.o:role=PROVIDER:stage=total:statistic=totalTime:status=0:transport=rest:type=stage,0,1.8000000000000002E-8");
-    AssertUtil.assertMeasure(measurements, i++,
-        "servicecomb.invocation:operation=m.s.o:role=PROVIDER:stage=total:statistic=max:status=0:transport=rest:type=stage,0,9.000000000000001E-9");
-    AssertUtil.assertMeasure(measurements, i++,
-        "servicecomb.invocation:operation=m.s.o:role=PROVIDER:stage=prepare:statistic=count:status=0:transport=rest:type=stage,0,2.0");
-    AssertUtil.assertMeasure(measurements, i++,
-        "servicecomb.invocation:operation=m.s.o:role=PROVIDER:stage=prepare:statistic=totalTime:status=0:transport=rest:type=stage,0,1.8000000000000002E-8");
-    AssertUtil.assertMeasure(measurements, i++,
-        "servicecomb.invocation:operation=m.s.o:role=PROVIDER:stage=prepare:statistic=max:status=0:transport=rest:type=stage,0,9.000000000000001E-9");
-    AssertUtil.assertMeasure(measurements, i++,
-        "servicecomb.invocation:operation=m.s.o:role=PROVIDER:stage=queue:statistic=count:status=0:transport=rest:type=stage,0,2.0");
-    AssertUtil.assertMeasure(measurements, i++,
-        "servicecomb.invocation:operation=m.s.o:role=PROVIDER:stage=queue:statistic=totalTime:status=0:transport=rest:type=stage,0,1.8000000000000002E-8");
-    AssertUtil.assertMeasure(measurements, i++,
-        "servicecomb.invocation:operation=m.s.o:role=PROVIDER:stage=queue:statistic=max:status=0:transport=rest:type=stage,0,9.000000000000001E-9");
-    AssertUtil.assertMeasure(measurements, i++,
-        "servicecomb.invocation:operation=m.s.o:role=PROVIDER:stage=execute:statistic=count:status=0:transport=rest:type=stage,0,2.0");
-    AssertUtil.assertMeasure(measurements, i++,
-        "servicecomb.invocation:operation=m.s.o:role=PROVIDER:stage=execute:statistic=totalTime:status=0:transport=rest:type=stage,0,1.8000000000000002E-8");
-    AssertUtil.assertMeasure(measurements, i++,
-        "servicecomb.invocation:operation=m.s.o:role=PROVIDER:stage=execute:statistic=max:status=0:transport=rest:type=stage,0,9.000000000000001E-9");
-    AssertUtil.assertMeasure(measurements, i++,
-        "servicecomb.invocation:operation=m.s.o:role=PROVIDER:stage=provider-decode:statistic=count:status=0:transport=rest:type=stage,0,2.0");
-    AssertUtil.assertMeasure(measurements, i++,
-        "servicecomb.invocation:operation=m.s.o:role=PROVIDER:stage=provider-decode:statistic=totalTime:status=0:transport=rest:type=stage,0,1.8000000000000002E-8");
-    AssertUtil.assertMeasure(measurements, i++,
-        "servicecomb.invocation:operation=m.s.o:role=PROVIDER:stage=provider-decode:statistic=max:status=0:transport=rest:type=stage,0,9.000000000000001E-9");
-    AssertUtil.assertMeasure(measurements, i++,
-        "servicecomb.invocation:operation=m.s.o:role=PROVIDER:stage=provider-encode:statistic=count:status=0:transport=rest:type=stage,0,2.0");
-    AssertUtil.assertMeasure(measurements, i++,
-        "servicecomb.invocation:operation=m.s.o:role=PROVIDER:stage=provider-encode:statistic=totalTime:status=0:transport=rest:type=stage,0,1.8000000000000002E-8");
-    AssertUtil.assertMeasure(measurements, i++,
-        "servicecomb.invocation:operation=m.s.o:role=PROVIDER:stage=provider-encode:statistic=max:status=0:transport=rest:type=stage,0,9.000000000000001E-9");
-    AssertUtil.assertMeasure(measurements, i++,
-        "servicecomb.invocation:operation=m.s.o:role=PROVIDER:stage=provider-send:statistic=count:status=0:transport=rest:type=stage,0,2.0");
-    AssertUtil.assertMeasure(measurements, i++,
-        "servicecomb.invocation:operation=m.s.o:role=PROVIDER:stage=provider-send:statistic=totalTime:status=0:transport=rest:type=stage,0,1.8000000000000002E-8");
-    AssertUtil.assertMeasure(measurements, i++,
-        "servicecomb.invocation:operation=m.s.o:role=PROVIDER:stage=provider-send:statistic=max:status=0:transport=rest:type=stage,0,9.000000000000001E-9");
+    tree.from(registry.getMeters().iterator(),
+        new MeasurementGroupConfig(MeterInvocationConst.INVOCATION_NAME, "stage"));
+
+    List<Measurement> measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "total")
+        .getMeasurements();
+    Assert.assertEquals(3, measurements.size());
+    AssertUtil.assertMeasure(measurements, 0,
+        "statistic='COUNT', value=2.0");
+    AssertUtil.assertMeasure(measurements, 1,
+        "statistic='TOTAL_TIME', value=1.8E-8");
+    AssertUtil.assertMeasure(measurements, 2,
+        "statistic='MAX', value=9.0E-9");
+
+    measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "prepare")
+        .getMeasurements();
+    Assert.assertEquals(3, measurements.size());
+    AssertUtil.assertMeasure(measurements, 0,
+        "statistic='COUNT', value=2.0");
+    AssertUtil.assertMeasure(measurements, 1,
+        "statistic='TOTAL_TIME', value=1.8E-8");
+    AssertUtil.assertMeasure(measurements, 2,
+        "statistic='MAX', value=9.0E-9");
+
+    measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "queue")
+        .getMeasurements();
+    Assert.assertEquals(3, measurements.size());
+    AssertUtil.assertMeasure(measurements, 0,
+        "statistic='COUNT', value=2.0");
+    AssertUtil.assertMeasure(measurements, 1,
+        "statistic='TOTAL_TIME', value=1.8E-8");
+    AssertUtil.assertMeasure(measurements, 2,
+        "statistic='MAX', value=9.0E-9");
+
+    measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "execute")
+        .getMeasurements();
+    Assert.assertEquals(3, measurements.size());
+    AssertUtil.assertMeasure(measurements, 0,
+        "statistic='COUNT', value=2.0");
+    AssertUtil.assertMeasure(measurements, 1,
+        "statistic='TOTAL_TIME', value=1.8E-8");
+    AssertUtil.assertMeasure(measurements, 2,
+        "statistic='MAX', value=9.0E-9");
+
+    measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "provider-decode")
+        .getMeasurements();
+    Assert.assertEquals(3, measurements.size());
+    AssertUtil.assertMeasure(measurements, 0,
+        "statistic='COUNT', value=2.0");
+    AssertUtil.assertMeasure(measurements, 1,
+        "statistic='TOTAL_TIME', value=1.8E-8");
+    AssertUtil.assertMeasure(measurements, 2,
+        "statistic='MAX', value=9.0E-9");
+
+    measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "provider-encode")
+        .getMeasurements();
+    Assert.assertEquals(3, measurements.size());
+    AssertUtil.assertMeasure(measurements, 0,
+        "statistic='COUNT', value=2.0");
+    AssertUtil.assertMeasure(measurements, 1,
+        "statistic='TOTAL_TIME', value=1.8E-8");
+    AssertUtil.assertMeasure(measurements, 2,
+        "statistic='MAX', value=9.0E-9");
+
+    measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "provider-send")
+        .getMeasurements();
+    Assert.assertEquals(3, measurements.size());
+    AssertUtil.assertMeasure(measurements, 0,
+        "statistic='COUNT', value=2.0");
+    AssertUtil.assertMeasure(measurements, 1,
+        "statistic='TOTAL_TIME', value=1.8E-8");
+    AssertUtil.assertMeasure(measurements, 2,
+        "statistic='MAX', value=9.0E-9");
   }
 }
diff --git a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestOsMeterInitializer.java b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestOsMeterInitializer.java
index 83bc88bc2..a373e21ba 100644
--- a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestOsMeterInitializer.java
+++ b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestOsMeterInitializer.java
@@ -25,30 +25,26 @@ import java.util.List;
 import java.util.Map;
 
 import org.apache.commons.io.FileUtils;
-import org.apache.servicecomb.foundation.metrics.registry.GlobalRegistry;
 import org.apache.servicecomb.metrics.core.meter.os.CpuMeter;
 import org.apache.servicecomb.metrics.core.meter.os.NetMeter;
 import org.apache.servicecomb.metrics.core.meter.os.OsMeter;
 import org.apache.servicecomb.metrics.core.meter.os.cpu.CpuUtils;
 import org.apache.servicecomb.metrics.core.meter.os.net.InterfaceUsage;
 import org.junit.Test;
+import org.junit.jupiter.api.Assertions;
 
 import com.google.common.eventbus.EventBus;
 import com.google.common.io.Files;
-import com.netflix.spectator.api.DefaultRegistry;
-import com.netflix.spectator.api.ManualClock;
-import com.netflix.spectator.api.Registry;
 
+import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
 import mockit.Expectations;
 import mockit.Mock;
 import mockit.MockUp;
 import mockit.Mocked;
-import org.junit.jupiter.api.Assertions;
 
 public class TestOsMeterInitializer {
-  GlobalRegistry globalRegistry = new GlobalRegistry(new ManualClock());
-
-  Registry registry = new DefaultRegistry(globalRegistry.getClock());
+  MeterRegistry registry = new SimpleMeterRegistry();
 
   @Mocked
   EventBus eventBus;
@@ -97,10 +93,11 @@ public class TestOsMeterInitializer {
         result = 2;
       }
     };
-    globalRegistry.add(registry);
+
     OsMetersInitializer osMetersInitializer = new OsMetersInitializer();
     osMetersInitializer.setOsLinux(true);
-    osMetersInitializer.init(globalRegistry, eventBus, null);
+    osMetersInitializer.init(registry, eventBus, null);
+    osMetersInitializer.poll(System.currentTimeMillis(), 1000);
     OsMeter osMeter = osMetersInitializer.getOsMeter();
     Assertions.assertNotNull(osMeter);
     Assertions.assertNotNull(osMeter.getCpuMeter());
@@ -114,25 +111,24 @@ public class TestOsMeterInitializer {
     Map<String, InterfaceUsage> interfaceInfoMap = netMeter.getInterfaceUsageMap();
     Assertions.assertEquals(1, interfaceInfoMap.size());
     InterfaceUsage eth0 = interfaceInfoMap.get("eth0");
-    Assertions.assertEquals(4, eth0.getNetStats().size());
     // recv Bps
-    Assertions.assertEquals(0L, eth0.getNetStats().get(0).getLastValue());
-    Assertions.assertEquals(0, eth0.getNetStats().get(0).getRate(), 0.0);
-    Assertions.assertEquals(0, eth0.getNetStats().get(0).getIndex());
+    Assertions.assertEquals(0L, eth0.getReceive().getLastValue());
+    Assertions.assertEquals(0, eth0.getReceive().getRate(), 0.0);
+    Assertions.assertEquals(0, eth0.getReceive().getIndex());
     // send Bps
-    Assertions.assertEquals(0L, eth0.getNetStats().get(1).getLastValue());
-    Assertions.assertEquals(0, eth0.getNetStats().get(1).getRate(), 0.0);
-    Assertions.assertEquals(8, eth0.getNetStats().get(1).getIndex());
+    Assertions.assertEquals(0L, eth0.getSend().getLastValue());
+    Assertions.assertEquals(0, eth0.getSend().getRate(), 0.0);
+    Assertions.assertEquals(8, eth0.getSend().getIndex());
 
     // recv pps
-    Assertions.assertEquals(0L, eth0.getNetStats().get(2).getLastValue());
-    Assertions.assertEquals(0, eth0.getNetStats().get(2).getRate(), 0.0);
-    Assertions.assertEquals(1, eth0.getNetStats().get(2).getIndex());
+    Assertions.assertEquals(0L, eth0.getPacketsReceive().getLastValue());
+    Assertions.assertEquals(0, eth0.getPacketsReceive().getRate(), 0.0);
+    Assertions.assertEquals(1, eth0.getPacketsReceive().getIndex());
 
     // send pps
-    Assertions.assertEquals(0L, eth0.getNetStats().get(3).getLastValue());
-    Assertions.assertEquals(0, eth0.getNetStats().get(3).getRate(), 0.0);
-    Assertions.assertEquals(9, eth0.getNetStats().get(3).getIndex());
+    Assertions.assertEquals(0L, eth0.getPacketsSend().getLastValue());
+    Assertions.assertEquals(0, eth0.getPacketsSend().getRate(), 0.0);
+    Assertions.assertEquals(9, eth0.getPacketsSend().getIndex());
   }
 
   @Test
diff --git a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestThreadPoolMetersInitializer.java b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestThreadPoolMetersInitializer.java
index ea908c88f..dba231e87 100644
--- a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestThreadPoolMetersInitializer.java
+++ b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestThreadPoolMetersInitializer.java
@@ -16,48 +16,41 @@
  */
 package org.apache.servicecomb.metrics.core;
 
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
-import java.util.List;
 import java.util.Map;
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.Executor;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.RunnableScheduledFuture;
 import java.util.concurrent.ScheduledThreadPoolExecutor;
-import java.util.concurrent.ThreadPoolExecutor;
 
 import org.apache.servicecomb.core.SCBEngine;
 import org.apache.servicecomb.core.definition.MicroserviceMeta;
 import org.apache.servicecomb.core.definition.OperationMeta;
 import org.apache.servicecomb.core.executor.GroupExecutor;
+import org.apache.servicecomb.core.executor.ThreadPoolExecutorEx;
 import org.apache.servicecomb.foundation.common.utils.BeanUtils;
-import org.apache.servicecomb.foundation.metrics.registry.GlobalRegistry;
-import org.hamcrest.MatcherAssert;
-import org.hamcrest.Matchers;
+import org.apache.servicecomb.foundation.metrics.publish.spectator.MeasurementGroupConfig;
+import org.apache.servicecomb.foundation.metrics.publish.spectator.MeasurementTree;
 import org.junit.Test;
+import org.junit.jupiter.api.Assertions;
 import org.mockito.Mockito;
 import org.springframework.context.ApplicationContext;
 
-import com.netflix.spectator.api.DefaultRegistry;
-import com.netflix.spectator.api.ManualClock;
-import com.netflix.spectator.api.Registry;
-import com.netflix.spectator.api.patterns.PolledMeter;
-
+import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
 import mockit.Expectations;
 import mockit.Mock;
 import mockit.MockUp;
 import mockit.Mocked;
 
 public class TestThreadPoolMetersInitializer {
-  GlobalRegistry globalRegistry = new GlobalRegistry();
-
-  Registry registry = new DefaultRegistry(new ManualClock());
+  MeterRegistry registry = new SimpleMeterRegistry();
 
   ThreadPoolMetersInitializer threadPoolMetersInitializer = new ThreadPoolMetersInitializer();
 
-  ThreadPoolExecutor threadPoolExecutor = Mockito.mock(ThreadPoolExecutor.class);
+  ThreadPoolExecutorEx threadPoolExecutor = Mockito.mock(ThreadPoolExecutorEx.class);
 
   @Mocked
   BlockingQueue<Runnable> queue;
@@ -130,21 +123,26 @@ public class TestThreadPoolMetersInitializer {
       }
     };
 
-    globalRegistry.add(registry);
-    threadPoolMetersInitializer.init(globalRegistry, null, null);
-
-    PolledMeter.update(registry);
-    List<String> result = new ArrayList<>();
-    registry.iterator().forEachRemaining(meter -> result.add(meter.measure().toString()));
-
-    MatcherAssert.assertThat(result,
-        Matchers.containsInAnyOrder("[Measurement(threadpool.maxThreads:id=groupExecutor-group0,0,0.0)]",
-            "[Measurement(threadpool.rejectedTaskCount:id=groupExecutor-group0,0,0.0)]",
-            "[Measurement(threadpool.completedTaskCount:id=groupExecutor-group0,0,0.0)]",
-            "[Measurement(threadpool.currentThreadsBusy:id=groupExecutor-group0,0,0.0)]",
-            "[Measurement(threadpool.corePoolSize:id=groupExecutor-group0,0,0.0)]",
-            "[Measurement(threadpool.poolSize:id=groupExecutor-group0,0,0.0)]",
-            "[Measurement(threadpool.queueSize:id=groupExecutor-group0,0,10.0)]",
-            "[Measurement(threadpool.taskCount:id=groupExecutor-group0,0,0.0)]"));
+    threadPoolMetersInitializer.init(registry, null, null);
+
+    MeasurementTree tree = new MeasurementTree();
+    MeasurementGroupConfig group = new MeasurementGroupConfig();
+    group.addGroup("threadpool.maxThreads", "id");
+    group.addGroup("threadpool.rejectedCount", "id");
+    group.addGroup("threadpool.completedTaskCount", "id");
+    group.addGroup("threadpool.currentThreadsBusy", "id");
+    group.addGroup("threadpool.corePoolSize", "id");
+    group.addGroup("threadpool.poolSize", "id");
+    group.addGroup("threadpool.queueSize", "id");
+    group.addGroup("threadpool.taskCount", "id");
+    tree.from(registry.getMeters().iterator(), group);
+    Assertions.assertEquals(tree.findChild("threadpool.maxThreads", "groupExecutor-group0").summary(), 0, 0);
+    Assertions.assertEquals(tree.findChild("threadpool.rejectedCount", "groupExecutor-group0").summary(), 0, 0);
+    Assertions.assertEquals(tree.findChild("threadpool.completedTaskCount", "groupExecutor-group0").summary(), 0, 0);
+    Assertions.assertEquals(tree.findChild("threadpool.currentThreadsBusy", "groupExecutor-group0").summary(), 0, 0);
+    Assertions.assertEquals(tree.findChild("threadpool.corePoolSize", "groupExecutor-group0").summary(), 0, 0);
+    Assertions.assertEquals(tree.findChild("threadpool.poolSize", "groupExecutor-group0").summary(), 0, 0);
+    Assertions.assertEquals(tree.findChild("threadpool.queueSize", "groupExecutor-group0").summary(), 10, 0);
+    Assertions.assertEquals(tree.findChild("threadpool.taskCount", "groupExecutor-group0").summary(), 0, 0);
   }
 }
diff --git a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestVertxMetersInitializer.java b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestVertxMetersInitializer.java
index 85a91c9f9..fc2c20c8c 100644
--- a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestVertxMetersInitializer.java
+++ b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestVertxMetersInitializer.java
@@ -21,13 +21,11 @@ import static org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig.D
 import static org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig.METRICS_WINDOW_TIME;
 import static org.apache.servicecomb.metrics.core.publish.DefaultLogPublisher.ENDPOINTS_CLIENT_DETAIL_ENABLED;
 
-import java.util.ArrayList;
 import java.util.List;
 
 import org.apache.servicecomb.foundation.common.LegacyPropertyFactory;
 import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig;
 import org.apache.servicecomb.foundation.metrics.PolledEvent;
-import org.apache.servicecomb.foundation.metrics.registry.GlobalRegistry;
 import org.apache.servicecomb.foundation.test.scaffolding.log.LogCollector;
 import org.apache.servicecomb.foundation.vertx.SharedVertxFactory;
 import org.apache.servicecomb.foundation.vertx.VertxUtils;
@@ -40,14 +38,11 @@ import org.junit.jupiter.api.Assertions;
 import org.mockito.Mockito;
 import org.springframework.core.env.Environment;
 
-import com.google.common.collect.Lists;
 import com.google.common.eventbus.EventBus;
-import com.netflix.spectator.api.DefaultRegistry;
-import com.netflix.spectator.api.ManualClock;
-import com.netflix.spectator.api.Measurement;
-import com.netflix.spectator.api.Meter;
-import com.netflix.spectator.api.Registry;
 
+import io.micrometer.core.instrument.Meter;
+import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
 import io.vertx.core.AbstractVerticle;
 import io.vertx.core.DeploymentOptions;
 import io.vertx.core.Promise;
@@ -59,9 +54,7 @@ import io.vertx.core.http.HttpServer;
 import io.vertx.ext.web.Router;
 
 public class TestVertxMetersInitializer {
-  GlobalRegistry globalRegistry = new GlobalRegistry(new ManualClock());
-
-  Registry registry = new DefaultRegistry(globalRegistry.getClock());
+  MeterRegistry registry = new SimpleMeterRegistry();
 
   EventBus eventBus = new EventBus();
 
@@ -135,36 +128,31 @@ public class TestVertxMetersInitializer {
             CONFIG_LATENCY_DISTRIBUTION_MIN_SCOPE_LEN, int.class, 7))
         .thenReturn(7);
     Mockito.when(environment.getProperty(DefaultLogPublisher.ENABLED, boolean.class, false)).thenReturn(false);
-
-    globalRegistry.add(registry);
-    vertxMetersInitializer.init(globalRegistry, eventBus, new MetricsBootstrapConfig(environment));
-    logPublisher.setEnvironment(environment);
-    logPublisher.init(null, eventBus, null);
     VertxUtils
         .blockDeploy(SharedVertxFactory.getSharedVertx(environment), TestServerVerticle.class, new DeploymentOptions());
     VertxUtils
         .blockDeploy(SharedVertxFactory.getSharedVertx(environment), TestClientVerticle.class, new DeploymentOptions());
 
-    globalRegistry.poll(1);
-    List<Meter> meters = Lists.newArrayList(registry.iterator());
-    List<Measurement> measurements = new ArrayList<>();
-    for (Meter meter : meters) {
-      meter.measure().forEach(measurements::add);
-    }
+    vertxMetersInitializer.init(registry, eventBus, new MetricsBootstrapConfig(environment));
+    logPublisher.setEnvironment(environment);
+    logPublisher.init(registry, eventBus, null);
+
+    vertxMetersInitializer.poll(0, 1);
+    List<Meter> meters = registry.getMeters();
 
     LogCollector logCollector = new LogCollector();
 
-    testLog(logCollector, meters, measurements, true);
+    testLog(logCollector, meters, true);
     logCollector.clear();
-    testLog(logCollector, meters, measurements, false);
+
+    testLog(logCollector, meters, false);
 
     logCollector.teardown();
   }
 
-  private void testLog(LogCollector logCollector, List<Meter> meters, List<Measurement> measurements,
-      boolean printDetail) {
+  private void testLog(LogCollector logCollector, List<Meter> meters, boolean printDetail) {
     Mockito.when(environment.getProperty(ENDPOINTS_CLIENT_DETAIL_ENABLED, boolean.class, true)).thenReturn(printDetail);
-    logPublisher.onPolledEvent(new PolledEvent(meters, measurements));
+    logPublisher.onPolledEvent(new PolledEvent(meters));
 
     StringBuilder sb = new StringBuilder();
     logCollector.getEvents().forEach(event -> sb.append(event.getMessage()).append("\n"));
diff --git a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/meter/os/TestCpuMeter.java b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/meter/os/TestCpuMeter.java
index 8295bdcee..872dbe1fe 100644
--- a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/meter/os/TestCpuMeter.java
+++ b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/meter/os/TestCpuMeter.java
@@ -26,22 +26,23 @@ import java.util.List;
 
 import org.apache.servicecomb.metrics.core.meter.os.cpu.CpuUtils;
 import org.junit.Test;
+import org.junit.jupiter.api.Assertions;
 
 import com.google.common.io.CharSource;
 import com.google.common.io.Files;
-import com.netflix.spectator.api.Id;
-import com.netflix.spectator.api.Measurement;
 
+import io.micrometer.core.instrument.Measurement;
+import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
 import mockit.Expectations;
 import mockit.Mock;
 import mockit.MockUp;
 import mockit.Mocked;
-import org.junit.jupiter.api.Assertions;
 
 public class TestCpuMeter {
 
   @Test
-  public void testRefreshCpuSuccess(@Mocked Id id, @Mocked Runtime runtime, @Mocked RuntimeMXBean mxBean,
+  public void testRefreshCpuSuccess(@Mocked Runtime runtime, @Mocked RuntimeMXBean mxBean,
       @Mocked CharSource charSource) throws IOException {
     new MockUp<Files>() {
       @Mock
@@ -75,7 +76,8 @@ public class TestCpuMeter {
         result = "1 1 1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1 1";
       }
     };
-    CpuMeter cpuMeter = new CpuMeter(id);
+    MeterRegistry meterRegistry = new SimpleMeterRegistry();
+    CpuMeter cpuMeter = new CpuMeter(meterRegistry, "cpu");
     Assertions.assertEquals(0.0, cpuMeter.getAllCpuUsage().getUsage(), 0.0);
     Assertions.assertEquals(0.0, cpuMeter.getProcessCpuUsage().getUsage(), 0.0);
 
@@ -85,14 +87,14 @@ public class TestCpuMeter {
         result = "2 2 2 2 2 2 2 2 2 0 0 2 2 2 2 2 2 2 2 2 2";
       }
     };
-    cpuMeter.update();
+    cpuMeter.poll(0, 0);
 
     Assertions.assertEquals(0.875, cpuMeter.getAllCpuUsage().getUsage(), 0.0);
     Assertions.assertEquals(0.5, cpuMeter.getProcessCpuUsage().getUsage(), 0.0);
   }
 
   @Test
-  public void testRefreshError(@Mocked Id id, @Mocked Runtime runtime, @Mocked RuntimeMXBean mxBean,
+  public void testRefreshError(@Mocked Runtime runtime, @Mocked RuntimeMXBean mxBean,
       @Mocked CharSource charSource) throws IOException {
 
     new MockUp<Files>() {
@@ -127,7 +129,8 @@ public class TestCpuMeter {
         result = "1 1 1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1 1";
       }
     };
-    CpuMeter cpuMeter = new CpuMeter(id);
+    MeterRegistry meterRegistry = new SimpleMeterRegistry();
+    CpuMeter cpuMeter = new CpuMeter(meterRegistry, "cpu");
     Assertions.assertEquals(0.0, cpuMeter.getAllCpuUsage().getUsage(), 0.0);
     Assertions.assertEquals(0.0, cpuMeter.getProcessCpuUsage().getUsage(), 0.0);
     new Expectations() {
@@ -136,17 +139,15 @@ public class TestCpuMeter {
         result = "1 1 1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1 1";
       }
     };
-    cpuMeter.update();
+    cpuMeter.poll(0, 0);
 
     Assertions.assertEquals(0.0, cpuMeter.getAllCpuUsage().getUsage(), 0.0);
     Assertions.assertEquals(0.0, cpuMeter.getProcessCpuUsage().getUsage(), 0.0);
   }
 
   @Test
-  public void testCalcMeasurements(@Mocked Id id, @Mocked Runtime runtime, @Mocked RuntimeMXBean mxBean,
+  public void testCalcMeasurements(@Mocked Runtime runtime, @Mocked RuntimeMXBean mxBean,
       @Mocked CharSource charSource) throws IOException {
-    List<Measurement> measurements = new ArrayList<>();
-
     new MockUp<Files>() {
       @Mock
       public CharSource asCharSource(File file, Charset encoding) {
@@ -179,7 +180,9 @@ public class TestCpuMeter {
         result = "1 1 1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1 1";
       }
     };
-    CpuMeter cpuMeter = new CpuMeter(id);
+
+    MeterRegistry meterRegistry = new SimpleMeterRegistry();
+    CpuMeter cpuMeter = new CpuMeter(meterRegistry, "cpu");
 
     new Expectations() {
       {
@@ -187,13 +190,14 @@ public class TestCpuMeter {
         result = "2 2 2 2 2 2 2 2 2 0 0 2 2 2 2 2 2 2 2 2 2";
       }
     };
-    cpuMeter.calcMeasurements(measurements, 0);
-    Assertions.assertEquals(2, measurements.size());
+
+    cpuMeter.poll(System.currentTimeMillis(), 1000);
+
+    List<Measurement> measurements = new ArrayList<>();
+    meterRegistry.getMeters().forEach(meter -> meter.measure().forEach(measurement -> measurements.add(measurement)));
     Measurement measurement = measurements.get(0);
-    Assertions.assertEquals(0, measurement.timestamp());
-    Assertions.assertEquals(0.875, measurement.value(), 0.0);
+    Assertions.assertEquals(0.5, measurement.getValue(), 0.0);
     measurement = measurements.get(1);
-    Assertions.assertEquals(0, measurement.timestamp());
-    Assertions.assertEquals(0.5, measurement.value(), 0.0);
+    Assertions.assertEquals(0.875, measurement.getValue(), 0.0);
   }
 }
diff --git a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/meter/os/TestNetMeter.java b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/meter/os/TestNetMeter.java
index 89b8b49e4..60f54e3d7 100644
--- a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/meter/os/TestNetMeter.java
+++ b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/meter/os/TestNetMeter.java
@@ -23,20 +23,22 @@ import java.util.List;
 import java.util.Map;
 
 import org.apache.commons.io.FileUtils;
+import org.apache.servicecomb.foundation.metrics.publish.spectator.MeasurementGroupConfig;
+import org.apache.servicecomb.foundation.metrics.publish.spectator.MeasurementTree;
 import org.apache.servicecomb.metrics.core.meter.os.net.InterfaceUsage;
 import org.junit.Test;
+import org.junit.jupiter.api.Assertions;
 
-import com.netflix.spectator.api.Id;
-import com.netflix.spectator.api.Measurement;
-
+import io.micrometer.core.instrument.Measurement;
+import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.Tags;
+import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
 import mockit.Mock;
 import mockit.MockUp;
-import mockit.Mocked;
-import org.junit.jupiter.api.Assertions;
 
 public class TestNetMeter {
   @Test
-  public void testNetRefreshUnchanged(@Mocked Id id) {
+  public void testNetRefreshUnchanged() {
     List<String> list = new ArrayList<>();
     list.add("useless");
     list.add("useless");
@@ -47,7 +49,8 @@ public class TestNetMeter {
         return list;
       }
     };
-    NetMeter netMeter = new NetMeter(id);
+    MeterRegistry meterRegistry = new SimpleMeterRegistry();
+    NetMeter netMeter = new NetMeter(meterRegistry, "net", Tags.empty());
     list.remove(2);
     list.add("eth0: 1 1    0    0    0     0          0          1         1 1    1      0     0     0    0    0");
     netMeter.refreshNet(1);
@@ -58,30 +61,29 @@ public class TestNetMeter {
 
     Assertions.assertEquals("eth0", eth0.getName());
 
-    Assertions.assertEquals(4, eth0.getNetStats().size());
     // recv Bps
-    Assertions.assertEquals(1L, eth0.getNetStats().get(0).getLastValue());
-    Assertions.assertEquals(1, eth0.getNetStats().get(0).getRate(), 0.0);
-    Assertions.assertEquals(0, eth0.getNetStats().get(0).getIndex());
+    Assertions.assertEquals(1L, eth0.getReceive().getLastValue());
+    Assertions.assertEquals(1, eth0.getReceive().getRate(), 0.0);
+    Assertions.assertEquals(0, eth0.getReceive().getIndex());
     // send Bps
-    Assertions.assertEquals(1L, eth0.getNetStats().get(1).getLastValue());
-    Assertions.assertEquals(1, eth0.getNetStats().get(1).getRate(), 0.0);
-    Assertions.assertEquals(8, eth0.getNetStats().get(1).getIndex());
+    Assertions.assertEquals(1L, eth0.getSend().getLastValue());
+    Assertions.assertEquals(1, eth0.getSend().getRate(), 0.0);
+    Assertions.assertEquals(8, eth0.getSend().getIndex());
 
     // recv pps
-    Assertions.assertEquals(1L, eth0.getNetStats().get(2).getLastValue());
-    Assertions.assertEquals(1, eth0.getNetStats().get(2).getRate(), 0.0);
-    Assertions.assertEquals(1, eth0.getNetStats().get(2).getIndex());
+    Assertions.assertEquals(1L, eth0.getPacketsReceive().getLastValue());
+    Assertions.assertEquals(1, eth0.getPacketsReceive().getRate(), 0.0);
+    Assertions.assertEquals(1, eth0.getPacketsReceive().getIndex());
 
     // send pps
-    Assertions.assertEquals(1L, eth0.getNetStats().get(3).getLastValue());
-    Assertions.assertEquals(1, eth0.getNetStats().get(3).getRate(), 0.0);
-    Assertions.assertEquals(9, eth0.getNetStats().get(3).getIndex());
+    Assertions.assertEquals(1L, eth0.getPacketsSend().getLastValue());
+    Assertions.assertEquals(1, eth0.getPacketsSend().getRate(), 0.0);
+    Assertions.assertEquals(9, eth0.getPacketsSend().getIndex());
   }
 
 
   @Test
-  public void testNetRefreshAdd(@Mocked Id id) {
+  public void testNetRefreshAdd() {
     List<String> list = new ArrayList<>();
     list.add("useless");
     list.add("useless");
@@ -92,7 +94,9 @@ public class TestNetMeter {
         return list;
       }
     };
-    NetMeter netMeter = new NetMeter(id);
+    MeterRegistry meterRegistry = new SimpleMeterRegistry();
+    NetMeter netMeter = new NetMeter(meterRegistry, "net", Tags.empty());
+    netMeter.poll(0, 0);
     Map<String, InterfaceUsage> netMap = netMeter.getInterfaceUsageMap();
     Assertions.assertEquals(1, netMap.size());
     list.remove(2);
@@ -102,53 +106,52 @@ public class TestNetMeter {
     Assertions.assertEquals(2, netMap.size());
     InterfaceUsage eth0 = netMap.get("eth0");
     Assertions.assertEquals("eth0", eth0.getName());
-    Assertions.assertEquals(4, eth0.getNetStats().size());
+
     // recv Bps
-    Assertions.assertEquals(1L, eth0.getNetStats().get(0).getLastValue());
-    Assertions.assertEquals(1, eth0.getNetStats().get(0).getRate(), 0.0);
-    Assertions.assertEquals(0, eth0.getNetStats().get(0).getIndex());
+    Assertions.assertEquals(1L, eth0.getReceive().getLastValue());
+    Assertions.assertEquals(1, eth0.getReceive().getRate(), 0.0);
+    Assertions.assertEquals(0, eth0.getReceive().getIndex());
     // send Bps
-    Assertions.assertEquals(1L, eth0.getNetStats().get(1).getLastValue());
-    Assertions.assertEquals(1, eth0.getNetStats().get(1).getRate(), 0.0);
-    Assertions.assertEquals(8, eth0.getNetStats().get(1).getIndex());
+    Assertions.assertEquals(1L, eth0.getSend().getLastValue());
+    Assertions.assertEquals(1, eth0.getSend().getRate(), 0.0);
+    Assertions.assertEquals(8, eth0.getSend().getIndex());
 
     // recv pps
-    Assertions.assertEquals(1L, eth0.getNetStats().get(2).getLastValue());
-    Assertions.assertEquals(1, eth0.getNetStats().get(2).getRate(), 0.0);
-    Assertions.assertEquals(1, eth0.getNetStats().get(2).getIndex());
+    Assertions.assertEquals(1L, eth0.getPacketsReceive().getLastValue());
+    Assertions.assertEquals(1, eth0.getPacketsReceive().getRate(), 0.0);
+    Assertions.assertEquals(1, eth0.getPacketsReceive().getIndex());
 
     // send pps
-    Assertions.assertEquals(1L, eth0.getNetStats().get(3).getLastValue());
-    Assertions.assertEquals(1, eth0.getNetStats().get(3).getRate(), 0.0);
-    Assertions.assertEquals(9, eth0.getNetStats().get(3).getIndex());
+    Assertions.assertEquals(1L, eth0.getPacketsSend().getLastValue());
+    Assertions.assertEquals(1, eth0.getPacketsSend().getRate(), 0.0);
+    Assertions.assertEquals(9, eth0.getPacketsSend().getIndex());
 
     InterfaceUsage lo = netMap.get("lo");
     Assertions.assertEquals("lo", lo.getName());
 
-    Assertions.assertEquals(4, lo.getNetStats().size());
     // recv Bps
-    Assertions.assertEquals(0L, lo.getNetStats().get(0).getLastValue());
-    Assertions.assertEquals(0, lo.getNetStats().get(0).getRate(), 0.0);
-    Assertions.assertEquals(0, lo.getNetStats().get(0).getIndex());
+    Assertions.assertEquals(0L, lo.getReceive().getLastValue());
+    Assertions.assertEquals(0, lo.getReceive().getRate(), 0.0);
+    Assertions.assertEquals(0, lo.getReceive().getIndex());
     // send Bps
-    Assertions.assertEquals(0L, lo.getNetStats().get(1).getLastValue());
-    Assertions.assertEquals(0, lo.getNetStats().get(1).getRate(), 0.0);
-    Assertions.assertEquals(8, lo.getNetStats().get(1).getIndex());
+    Assertions.assertEquals(0L, lo.getSend().getLastValue());
+    Assertions.assertEquals(0, lo.getSend().getRate(), 0.0);
+    Assertions.assertEquals(8, lo.getSend().getIndex());
 
     // recv pps
-    Assertions.assertEquals(0L, lo.getNetStats().get(2).getLastValue());
-    Assertions.assertEquals(0, lo.getNetStats().get(2).getRate(), 0.0);
-    Assertions.assertEquals(1, lo.getNetStats().get(2).getIndex());
+    Assertions.assertEquals(0L, lo.getPacketsReceive().getLastValue());
+    Assertions.assertEquals(0, lo.getPacketsReceive().getRate(), 0.0);
+    Assertions.assertEquals(1, lo.getPacketsReceive().getIndex());
 
     // send pps
-    Assertions.assertEquals(0L, lo.getNetStats().get(3).getLastValue());
-    Assertions.assertEquals(0, lo.getNetStats().get(3).getRate(), 0.0);
-    Assertions.assertEquals(9, lo.getNetStats().get(3).getIndex());
+    Assertions.assertEquals(0L, lo.getPacketsSend().getLastValue());
+    Assertions.assertEquals(0, lo.getPacketsSend().getRate(), 0.0);
+    Assertions.assertEquals(9, lo.getPacketsSend().getIndex());
   }
 
 
   @Test
-  public void testNetRefreshRemove(@Mocked Id id) {
+  public void testNetRefreshRemove() {
     List<String> list = new ArrayList<>();
     list.add("useless");
     list.add("useless");
@@ -160,83 +163,82 @@ public class TestNetMeter {
         return list;
       }
     };
-    NetMeter netMeter = new NetMeter(id);
+    MeterRegistry meterRegistry = new SimpleMeterRegistry();
+    NetMeter netMeter = new NetMeter(meterRegistry, "net", Tags.empty());
+    netMeter.poll(0, 1);
     Map<String, InterfaceUsage> netMap = netMeter.getInterfaceUsageMap();
     Assertions.assertEquals(2, netMap.size());
     InterfaceUsage lo = netMap.get("lo");
     InterfaceUsage eth0 = netMap.get("eth0");
     Assertions.assertEquals("lo", lo.getName());
-    Assertions.assertEquals(4, lo.getNetStats().size());
     // recv Bps
-    Assertions.assertEquals(0L, lo.getNetStats().get(0).getLastValue());
-    Assertions.assertEquals(0, lo.getNetStats().get(0).getRate(), 0.0);
-    Assertions.assertEquals(0, lo.getNetStats().get(0).getIndex());
+    Assertions.assertEquals(0L, lo.getReceive().getLastValue());
+    Assertions.assertEquals(0, lo.getReceive().getRate(), 0.0);
+    Assertions.assertEquals(0, lo.getReceive().getIndex());
     // send Bps
-    Assertions.assertEquals(0L, lo.getNetStats().get(1).getLastValue());
-    Assertions.assertEquals(0, lo.getNetStats().get(1).getRate(), 0.0);
-    Assertions.assertEquals(8, lo.getNetStats().get(1).getIndex());
+    Assertions.assertEquals(0L, lo.getSend().getLastValue());
+    Assertions.assertEquals(0, lo.getSend().getRate(), 0.0);
+    Assertions.assertEquals(8, lo.getSend().getIndex());
 
     // recv pps
-    Assertions.assertEquals(0L, lo.getNetStats().get(2).getLastValue());
-    Assertions.assertEquals(0, lo.getNetStats().get(2).getRate(), 0.0);
-    Assertions.assertEquals(1, lo.getNetStats().get(2).getIndex());
+    Assertions.assertEquals(0L, lo.getPacketsReceive().getLastValue());
+    Assertions.assertEquals(0, lo.getPacketsReceive().getRate(), 0.0);
+    Assertions.assertEquals(1, lo.getPacketsReceive().getIndex());
 
     // send pps
-    Assertions.assertEquals(0L, lo.getNetStats().get(3).getLastValue());
-    Assertions.assertEquals(0, lo.getNetStats().get(3).getRate(), 0.0);
-    Assertions.assertEquals(9, lo.getNetStats().get(3).getIndex());
+    Assertions.assertEquals(0L, lo.getPacketsSend().getLastValue());
+    Assertions.assertEquals(0, lo.getPacketsSend().getRate(), 0.0);
+    Assertions.assertEquals(9, lo.getPacketsSend().getIndex());
 
     Assertions.assertEquals("eth0", eth0.getName());
-    Assertions.assertEquals(4, eth0.getNetStats().size());
     // recv Bps
-    Assertions.assertEquals(0L, eth0.getNetStats().get(0).getLastValue());
-    Assertions.assertEquals(0, eth0.getNetStats().get(0).getRate(), 0.0);
-    Assertions.assertEquals(0, eth0.getNetStats().get(0).getIndex());
+    Assertions.assertEquals(0L, eth0.getReceive().getLastValue());
+    Assertions.assertEquals(0, eth0.getReceive().getRate(), 0.0);
+    Assertions.assertEquals(0, eth0.getReceive().getIndex());
     // send Bps
-    Assertions.assertEquals(0L, eth0.getNetStats().get(1).getLastValue());
-    Assertions.assertEquals(0, eth0.getNetStats().get(1).getRate(), 0.0);
-    Assertions.assertEquals(8, eth0.getNetStats().get(1).getIndex());
+    Assertions.assertEquals(0L, eth0.getSend().getLastValue());
+    Assertions.assertEquals(0, eth0.getSend().getRate(), 0.0);
+    Assertions.assertEquals(8, eth0.getSend().getIndex());
 
     // recv pps
-    Assertions.assertEquals(0L, eth0.getNetStats().get(2).getLastValue());
-    Assertions.assertEquals(0, eth0.getNetStats().get(2).getRate(), 0.0);
-    Assertions.assertEquals(1, eth0.getNetStats().get(2).getIndex());
+    Assertions.assertEquals(0L, eth0.getPacketsReceive().getLastValue());
+    Assertions.assertEquals(0, eth0.getPacketsReceive().getRate(), 0.0);
+    Assertions.assertEquals(1, eth0.getPacketsReceive().getIndex());
 
     // send pps
-    Assertions.assertEquals(0L, eth0.getNetStats().get(3).getLastValue());
-    Assertions.assertEquals(0, eth0.getNetStats().get(3).getRate(), 0.0);
-    Assertions.assertEquals(9, eth0.getNetStats().get(3).getIndex());
+    Assertions.assertEquals(0L, eth0.getPacketsSend().getLastValue());
+    Assertions.assertEquals(0, eth0.getPacketsSend().getRate(), 0.0);
+    Assertions.assertEquals(9, eth0.getPacketsSend().getIndex());
     list.remove(2);
     list.remove(2);
     list.add("eth0: 1 1    0    0    0     0          0          1         1 1    1      0     0     0    0    0");
     netMeter.refreshNet(1);
-    Assertions.assertNull(netMap.get("lo"));
-    Assertions.assertEquals(1, netMap.size());
+
     Assertions.assertEquals("eth0", eth0.getName());
-    Assertions.assertEquals(4, eth0.getNetStats().size());
+
     // recv Bps
-    Assertions.assertEquals(1L, eth0.getNetStats().get(0).getLastValue());
-    Assertions.assertEquals(1, eth0.getNetStats().get(0).getRate(), 0.0);
-    Assertions.assertEquals(0, eth0.getNetStats().get(0).getIndex());
+    Assertions.assertEquals(1L, eth0.getReceive().getLastValue());
+    Assertions.assertEquals(1, eth0.getReceive().getRate(), 0.0);
+    Assertions.assertEquals(0, eth0.getReceive().getIndex());
     // send Bps
-    Assertions.assertEquals(1L, eth0.getNetStats().get(1).getLastValue());
-    Assertions.assertEquals(1, eth0.getNetStats().get(1).getRate(), 0.0);
-    Assertions.assertEquals(8, eth0.getNetStats().get(1).getIndex());
+    Assertions.assertEquals(1L, eth0.getSend().getLastValue());
+    Assertions.assertEquals(1, eth0.getSend().getRate(), 0.0);
+    Assertions.assertEquals(8, eth0.getSend().getIndex());
 
     // recv pps
-    Assertions.assertEquals(1L, eth0.getNetStats().get(2).getLastValue());
-    Assertions.assertEquals(1, eth0.getNetStats().get(2).getRate(), 0.0);
-    Assertions.assertEquals(1, eth0.getNetStats().get(2).getIndex());
+    Assertions.assertEquals(1L, eth0.getPacketsReceive().getLastValue());
+    Assertions.assertEquals(1, eth0.getPacketsReceive().getRate(), 0.0);
+    Assertions.assertEquals(1, eth0.getPacketsReceive().getIndex());
 
     // send pps
-    Assertions.assertEquals(1L, eth0.getNetStats().get(3).getLastValue());
-    Assertions.assertEquals(1, eth0.getNetStats().get(3).getRate(), 0.0);
-    Assertions.assertEquals(9, eth0.getNetStats().get(3).getIndex());
+    Assertions.assertEquals(1L, eth0.getPacketsSend().getLastValue());
+    Assertions.assertEquals(1, eth0.getPacketsSend().getRate(), 0.0);
+    Assertions.assertEquals(9, eth0.getPacketsSend().getIndex());
   }
 
 
   @Test
-  public void testCalcMeasurements(@Mocked Id id) {
+  public void testCalcMeasurements() {
     List<String> list = new ArrayList<>();
     list.add("useless");
     list.add("useless");
@@ -247,19 +249,23 @@ public class TestNetMeter {
         return list;
       }
     };
-    NetMeter netMeter = new NetMeter(id);
+    MeterRegistry meterRegistry = new SimpleMeterRegistry();
+    NetMeter netMeter = new NetMeter(meterRegistry, "net", Tags.empty());
     list.remove(2);
     list.add("eth0: 3 1    0    0    0     0          0          1         3 1    1      0     0     0    0    0");
-    List<Measurement> measurements = new ArrayList<>();
-    netMeter.calcMeasurements(measurements, 0L, 1);
-    Assertions.assertEquals(4, measurements.size());
-    Measurement receive = measurements.get(0);
-    Measurement send = measurements.get(1);
-    Measurement receivePackets = measurements.get(2);
-    Measurement sendPackets = measurements.get(3);
-    Assertions.assertEquals(3.0, send.value(), 0.0);
-    Assertions.assertEquals(1.0, sendPackets.value(), 0.0);
-    Assertions.assertEquals(3.0, receive.value(), 0.0);
-    Assertions.assertEquals(1.0, receivePackets.value(), 0.0);
+    netMeter.poll(0, 1);
+
+    MeasurementTree tree = new MeasurementTree();
+    tree.from(meterRegistry.getMeters().iterator(),
+        new MeasurementGroupConfig("net", "statistic"));
+
+    Measurement receive = tree.findChild("net", "receive").getMeasurements().get(0);
+    Measurement send = tree.findChild("net", "send").getMeasurements().get(0);
+    Measurement receivePackets = tree.findChild("net", "receivePackets").getMeasurements().get(0);
+    Measurement sendPackets = tree.findChild("net", "sendPackets").getMeasurements().get(0);
+    Assertions.assertEquals(3.0, send.getValue(), 0.0);
+    Assertions.assertEquals(1.0, sendPackets.getValue(), 0.0);
+    Assertions.assertEquals(3.0, receive.getValue(), 0.0);
+    Assertions.assertEquals(1.0, receivePackets.getValue(), 0.0);
   }
 }
diff --git a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/meter/os/TestOsMeter.java b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/meter/os/TestOsMeter.java
index 6f71a126d..98863c911 100644
--- a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/meter/os/TestOsMeter.java
+++ b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/meter/os/TestOsMeter.java
@@ -26,28 +26,28 @@ import java.util.ArrayList;
 import java.util.List;
 
 import org.apache.commons.io.FileUtils;
+import org.apache.servicecomb.foundation.metrics.publish.spectator.MeasurementGroupConfig;
+import org.apache.servicecomb.foundation.metrics.publish.spectator.MeasurementTree;
 import org.apache.servicecomb.metrics.core.meter.os.cpu.CpuUtils;
 import org.junit.Test;
 import org.junit.jupiter.api.Assertions;
-import org.mockito.Mockito;
 
-import com.google.common.collect.Lists;
 import com.google.common.io.CharSource;
 import com.google.common.io.Files;
 
-import io.micrometer.core.instrument.Measurement;
 import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
 import mockit.Expectations;
 import mockit.Mock;
 import mockit.MockUp;
 import mockit.Mocked;
 
 public class TestOsMeter {
-  MeterRegistry meterRegistry = Mockito.mock(MeterRegistry.class);
-
   @Test
   public void testCalcMeasurement(@Mocked Runtime runtime, @Mocked RuntimeMXBean mxBean,
       @Mocked CharSource charSource) throws IOException {
+    MeterRegistry meterRegistry = new SimpleMeterRegistry();
+
     List<String> list = new ArrayList<>();
     list.add("useless");
     list.add("useless");
@@ -101,14 +101,17 @@ public class TestOsMeter {
         result = "2 2 2 2 2 2 2 2 2 0 0 2 2 2 2 2 2 2 2 2 2";
       }
     };
-    osMeter.calcMeasurements(1, 1);
-    ArrayList<Measurement> measurements = Lists.newArrayList(osMeter.measure());
-    Assertions.assertEquals(6, measurements.size());
-    Assertions.assertEquals(0.875, measurements.get(0).getValue(), 0.0);
-    Assertions.assertEquals(0.5, measurements.get(1).getValue(), 0.0);
-    Assertions.assertEquals(1.0, measurements.get(2).getValue(), 0.0);
-    Assertions.assertEquals(1.0, measurements.get(3).getValue(), 0.0);
-    Assertions.assertEquals(1.0, measurements.get(4).getValue(), 0.0);
-    Assertions.assertEquals(1.0, measurements.get(5).getValue(), 0.0);
+    osMeter.poll(0, 1);
+
+    MeasurementTree tree = new MeasurementTree();
+    tree.from(meterRegistry.getMeters().iterator(),
+        new MeasurementGroupConfig("os", "type"));
+
+    Assertions.assertEquals(0.875, tree.findChild("os", "cpu").getMeasurements().get(0).getValue(), 0.0);
+    Assertions.assertEquals(0.5, tree.findChild("os", "processCpu").getMeasurements().get(0).getValue(), 0.0);
+    Assertions.assertEquals(1.0, tree.findChild("os", "net").getMeasurements().get(0).getValue(), 0.0);
+    Assertions.assertEquals(1.0, tree.findChild("os", "net").getMeasurements().get(1).getValue(), 0.0);
+    Assertions.assertEquals(1.0, tree.findChild("os", "net").getMeasurements().get(2).getValue(), 0.0);
+    Assertions.assertEquals(1.0, tree.findChild("os", "net").getMeasurements().get(3).getValue(), 0.0);
   }
 }
diff --git a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestDefaultLogPublisher.java b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestDefaultLogPublisher.java
index 90cf30e4c..e0b1618e8 100644
--- a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestDefaultLogPublisher.java
+++ b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestDefaultLogPublisher.java
@@ -32,7 +32,6 @@ import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig;
 import org.apache.servicecomb.foundation.metrics.PolledEvent;
 import org.apache.servicecomb.foundation.metrics.publish.spectator.MeasurementNode;
 import org.apache.servicecomb.foundation.metrics.publish.spectator.MeasurementTree;
-import org.apache.servicecomb.foundation.metrics.registry.GlobalRegistry;
 import org.apache.servicecomb.foundation.test.scaffolding.log.LogCollector;
 import org.apache.servicecomb.foundation.vertx.VertxUtils;
 import org.apache.servicecomb.metrics.core.meter.os.OsMeter;
@@ -52,8 +51,14 @@ import org.mockito.Mockito;
 import org.springframework.core.env.Environment;
 
 import com.google.common.eventbus.EventBus;
-import com.netflix.spectator.api.Measurement;
 
+import io.micrometer.core.instrument.Measurement;
+import io.micrometer.core.instrument.Meter.Id;
+import io.micrometer.core.instrument.Meter.Type;
+import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.Statistic;
+import io.micrometer.core.instrument.Tags;
+import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
 import io.vertx.core.impl.VertxImpl;
 import jakarta.ws.rs.core.Response.Status;
 import mockit.Expectations;
@@ -63,7 +68,7 @@ import mockit.MockUp;
 
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 public class TestDefaultLogPublisher {
-  GlobalRegistry globalRegistry = new GlobalRegistry();
+  MeterRegistry meterRegistry = new SimpleMeterRegistry();
 
   EventBus eventBus = new EventBus();
 
@@ -99,7 +104,7 @@ public class TestDefaultLogPublisher {
       }
     };
 
-    publisher.init(globalRegistry, eventBus, new MetricsBootstrapConfig(environment));
+    publisher.init(meterRegistry, eventBus, new MetricsBootstrapConfig(environment));
     Assertions.assertFalse(registered.value);
   }
 
@@ -114,7 +119,7 @@ public class TestDefaultLogPublisher {
       }
     };
 
-    publisher.init(globalRegistry, eventBus, new MetricsBootstrapConfig(environment));
+    publisher.init(meterRegistry, eventBus, new MetricsBootstrapConfig(environment));
     Assertions.assertTrue(registered.value);
   }
 
@@ -129,7 +134,7 @@ public class TestDefaultLogPublisher {
       }
     };
 
-    publisher.init(globalRegistry, eventBus, new MetricsBootstrapConfig(environment));
+    publisher.init(meterRegistry, eventBus, new MetricsBootstrapConfig(environment));
     Assertions.assertNull(registered.value);
   }
 
@@ -144,12 +149,12 @@ public class TestDefaultLogPublisher {
 
   @Test
   public void onPolledEvent(@Injectable VertxImpl vertxImpl, @Injectable MeasurementTree tree,
-      @Injectable GlobalRegistry globalRegistry, @Injectable EventBus eventBus) {
+      @Injectable EventBus eventBus) {
     MetricsBootstrapConfig config = Mockito.mock(MetricsBootstrapConfig.class);
     try {
       Mockito.when(environment.getProperty(DefaultLogPublisher.ENABLED, boolean.class, false)).thenReturn(true);
       Mockito.when(config.getLatencyDistribution()).thenReturn("0,1,100");
-      publisher.init(globalRegistry, eventBus, config);
+      publisher.init(meterRegistry, eventBus, config);
       new Expectations(VertxUtils.class) {
         {
           VertxUtils.getVertxMap();
@@ -188,17 +193,18 @@ public class TestDefaultLogPublisher {
       model.getEdge().setOperationPerfGroups(operationPerfGroups);
 
       model.getThreadPools().put("test", new ThreadPoolPublishModel());
-      Measurement measurement = new Measurement(null, 0L, 1.0);
+      Id id = new Id("test", Tags.empty(), null, null, Type.OTHER);
+      Measurement measurement = new Measurement(() -> 1.0, Statistic.VALUE);
 
-      MeasurementNode measurementNodeCpuAll = new MeasurementNode("allProcess", new HashMap<>());
-      MeasurementNode measurementNodeCpuProcess = new MeasurementNode("currentProcess", new HashMap<>());
-      MeasurementNode measurementNodeSend = new MeasurementNode("send", new HashMap<>());
-      MeasurementNode measurementNodeSendPacket = new MeasurementNode("sendPackets", new HashMap<>());
-      MeasurementNode measurementNodeRecv = new MeasurementNode("receive", new HashMap<>());
-      MeasurementNode measurementNodeRecvPacket = new MeasurementNode("receivePackets", new HashMap<>());
-      MeasurementNode measurementNodeEth0 = new MeasurementNode("eth0", new HashMap<>());
-      MeasurementNode measurementNodeNet = new MeasurementNode("net", new HashMap<>());
-      MeasurementNode measurementNodeOs = new MeasurementNode("os", new HashMap<>());
+      MeasurementNode measurementNodeCpuAll = new MeasurementNode("allProcess", id, new HashMap<>());
+      MeasurementNode measurementNodeCpuProcess = new MeasurementNode("currentProcess", id, new HashMap<>());
+      MeasurementNode measurementNodeSend = new MeasurementNode("send", id, new HashMap<>());
+      MeasurementNode measurementNodeSendPacket = new MeasurementNode("sendPackets", id, new HashMap<>());
+      MeasurementNode measurementNodeRecv = new MeasurementNode("receive", id, new HashMap<>());
+      MeasurementNode measurementNodeRecvPacket = new MeasurementNode("receivePackets", id, new HashMap<>());
+      MeasurementNode measurementNodeEth0 = new MeasurementNode("eth0", id, new HashMap<>());
+      MeasurementNode measurementNodeNet = new MeasurementNode("net", id, new HashMap<>());
+      MeasurementNode measurementNodeOs = new MeasurementNode("os", id, new HashMap<>());
 
       measurementNodeSend.getMeasurements().add(measurement);
       measurementNodeRecv.getMeasurements().add(measurement);
@@ -238,7 +244,7 @@ public class TestDefaultLogPublisher {
           result = measurementNodeOs;
         }
       };
-      publisher.onPolledEvent(new PolledEvent(Collections.emptyList(), Collections.emptyList()));
+      publisher.onPolledEvent(new PolledEvent(Collections.emptyList()));
       List<LogEvent> events = collector.getEvents().stream()
           .filter(e -> "scb-metrics".equals(e.getLoggerName())).toList();
       LogEvent event = events.get(0);
diff --git a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestInvocationPublishModelFactory.java b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestInvocationPublishModelFactory.java
index 6480928f9..535c28700 100644
--- a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestInvocationPublishModelFactory.java
+++ b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestInvocationPublishModelFactory.java
@@ -36,20 +36,18 @@ import org.junit.jupiter.api.Assertions;
 import org.mockito.Mockito;
 import org.springframework.core.env.Environment;
 
-import com.google.common.collect.Lists;
 import com.google.common.eventbus.EventBus;
-import com.netflix.spectator.api.DefaultRegistry;
-import com.netflix.spectator.api.ManualClock;
-import com.netflix.spectator.api.Registry;
 
+import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
 import io.vertx.core.json.Json;
 
 public class TestInvocationPublishModelFactory {
   EventBus eventBus = new EventBus();
 
-  GlobalRegistry globalRegistry = new GlobalRegistry();
+  MeterRegistry meterRegistry = new SimpleMeterRegistry();
 
-  Registry registry = new DefaultRegistry(new ManualClock());
+  GlobalRegistry globalRegistry = new GlobalRegistry(meterRegistry);
 
   InvocationMetersInitializer invocationMetersInitializer = new InvocationMetersInitializer();
 
@@ -71,208 +69,208 @@ public class TestInvocationPublishModelFactory {
             CONFIG_LATENCY_DISTRIBUTION_MIN_SCOPE_LEN, int.class, 7))
         .thenReturn(7);
     Mockito.when(environment.getProperty(CONFIG_LATENCY_DISTRIBUTION, String.class)).thenReturn("0,1,100");
-    globalRegistry.add(registry);
-    invocationMetersInitializer.init(globalRegistry, eventBus, new MetricsBootstrapConfig(environment));
+
+    invocationMetersInitializer.init(meterRegistry, eventBus, new MetricsBootstrapConfig(environment));
     prepareInvocation();
 
     globalRegistry.poll(1);
-    PublishModelFactory factory = new PublishModelFactory(Lists.newArrayList(registry.iterator()));
+    PublishModelFactory factory = new PublishModelFactory(meterRegistry.getMeters());
     DefaultPublishModel model = factory.createDefaultPublishModel();
 
     String expect = """
         {
-          "operationPerfGroups" : {
-            "groups" : {
-              "rest" : {
-                "200" : {
-                  "transport" : "rest",
-                  "status" : "200",
-                  "operationPerfs" : [ {
-                    "operation" : "m.s.o",
-                    "stages" : {
-                      "consumer-encode" : {
-                        "tps" : 1.0,
-                        "msTotalTime" : 1.0000000000000002E-6,
-                        "msMaxLatency" : 1.0000000000000002E-6
-                      },
-                      "prepare" : {
-                        "tps" : 1.0,
-                        "msTotalTime" : 1.0000000000000002E-6,
-                        "msMaxLatency" : 1.0000000000000002E-6
-                      },
-                      "total" : {
-                        "tps" : 1.0,
-                        "msTotalTime" : 1.4000000000000001E-5,
-                        "msMaxLatency" : 1.4000000000000001E-5
-                      },
-                      "wait" : {
-                        "tps" : 1.0,
-                        "msTotalTime" : 1.0000000000000002E-6,
-                        "msMaxLatency" : 1.0000000000000002E-6
-                      },
-                      "consumer-send" : {
-                        "tps" : 1.0,
-                        "msTotalTime" : 1.0000000000000002E-6,
-                        "msMaxLatency" : 1.0000000000000002E-6
-                      },
-                      "connection" : {
-                        "tps" : 1.0,
-                        "msTotalTime" : 1.0000000000000002E-6,
-                        "msMaxLatency" : 1.0000000000000002E-6
-                      },
-                      "consumer-decode" : {
-                        "tps" : 1.0,
-                        "msTotalTime" : 1.0000000000000002E-6,
-                        "msMaxLatency" : 1.0000000000000002E-6
-                      }
-                    },
-                    "latencyDistribution" : [ 1, 0, 0 ]
-                  } ],
-                  "summary" : {
-                    "operation" : "",
-                    "stages" : {
-                      "consumer-encode" : {
-                        "tps" : 1.0,
-                        "msTotalTime" : 1.0000000000000002E-6,
-                        "msMaxLatency" : 1.0000000000000002E-6
-                      },
-                      "prepare" : {
-                        "tps" : 1.0,
-                        "msTotalTime" : 1.0000000000000002E-6,
-                        "msMaxLatency" : 1.0000000000000002E-6
-                      },
-                      "wait" : {
-                        "tps" : 1.0,
-                        "msTotalTime" : 1.0000000000000002E-6,
-                        "msMaxLatency" : 1.0000000000000002E-6
-                      },
-                      "total" : {
-                        "tps" : 1.0,
-                        "msTotalTime" : 1.4000000000000001E-5,
-                        "msMaxLatency" : 1.4000000000000001E-5
-                      },
-                      "consumer-send" : {
-                        "tps" : 1.0,
-                        "msTotalTime" : 1.0000000000000002E-6,
-                        "msMaxLatency" : 1.0000000000000002E-6
-                      },
-                      "connection" : {
-                        "tps" : 1.0,
-                        "msTotalTime" : 1.0000000000000002E-6,
-                        "msMaxLatency" : 1.0000000000000002E-6
-                      },
-                      "consumer-decode" : {
-                        "tps" : 1.0,
-                        "msTotalTime" : 1.0000000000000002E-6,
-                        "msMaxLatency" : 1.0000000000000002E-6
-                      }
-                    },
-                    "latencyDistribution" : [ 1, 0, 0 ]
-                  }
-                }
-              }
-            }
-          }
-        }
+           "operationPerfGroups" : {
+             "groups" : {
+               "rest" : {
+                 "200" : {
+                   "transport" : "rest",
+                   "status" : "200",
+                   "operationPerfs" : [ {
+                     "operation" : "m.s.o",
+                     "stages" : {
+                       "consumer-encode" : {
+                         "tps" : 1.0,
+                         "msTotalTime" : 1.0000000000000002E-6,
+                         "msMaxLatency" : 1.0000000000000002E-6
+                       },
+                       "prepare" : {
+                         "tps" : 1.0,
+                         "msTotalTime" : 1.0000000000000002E-6,
+                         "msMaxLatency" : 1.0000000000000002E-6
+                       },
+                       "wait" : {
+                         "tps" : 1.0,
+                         "msTotalTime" : 1.0000000000000002E-6,
+                         "msMaxLatency" : 1.0000000000000002E-6
+                       },
+                       "total" : {
+                         "tps" : 1.0,
+                         "msTotalTime" : 1.4E-5,
+                         "msMaxLatency" : 1.4E-5
+                       },
+                       "consumer-send" : {
+                         "tps" : 1.0,
+                         "msTotalTime" : 1.0000000000000002E-6,
+                         "msMaxLatency" : 1.0000000000000002E-6
+                       },
+                       "connection" : {
+                         "tps" : 1.0,
+                         "msTotalTime" : 1.0000000000000002E-6,
+                         "msMaxLatency" : 1.0000000000000002E-6
+                       },
+                       "consumer-decode" : {
+                         "tps" : 1.0,
+                         "msTotalTime" : 1.0000000000000002E-6,
+                         "msMaxLatency" : 1.0000000000000002E-6
+                       }
+                     },
+                     "latencyDistribution" : [ 1, 0, 0 ]
+                   } ],
+                   "summary" : {
+                     "operation" : "",
+                     "stages" : {
+                       "consumer-encode" : {
+                         "tps" : 1.0,
+                         "msTotalTime" : 1.0000000000000002E-6,
+                         "msMaxLatency" : 1.0000000000000002E-6
+                       },
+                       "prepare" : {
+                         "tps" : 1.0,
+                         "msTotalTime" : 1.0000000000000002E-6,
+                         "msMaxLatency" : 1.0000000000000002E-6
+                       },
+                       "total" : {
+                         "tps" : 1.0,
+                         "msTotalTime" : 1.4E-5,
+                         "msMaxLatency" : 1.4E-5
+                       },
+                       "wait" : {
+                         "tps" : 1.0,
+                         "msTotalTime" : 1.0000000000000002E-6,
+                         "msMaxLatency" : 1.0000000000000002E-6
+                       },
+                       "consumer-send" : {
+                         "tps" : 1.0,
+                         "msTotalTime" : 1.0000000000000002E-6,
+                         "msMaxLatency" : 1.0000000000000002E-6
+                       },
+                       "connection" : {
+                         "tps" : 1.0,
+                         "msTotalTime" : 1.0000000000000002E-6,
+                         "msMaxLatency" : 1.0000000000000002E-6
+                       },
+                       "consumer-decode" : {
+                         "tps" : 1.0,
+                         "msTotalTime" : 1.0000000000000002E-6,
+                         "msMaxLatency" : 1.0000000000000002E-6
+                       }
+                     },
+                     "latencyDistribution" : [ 1, 0, 0 ]
+                   }
+                 }
+               }
+             }
+           }
+         }
         """;
     Assertions.assertEquals(Json.encodePrettily(Json.decodeValue(expect, Object.class)),
         Json.encodePrettily(model.getConsumer()));
 
     expect = """
         {
-          "operationPerfGroups" : {
-            "groups" : {
-              "rest" : {
-                "200" : {
-                  "transport" : "rest",
-                  "status" : "200",
-                  "operationPerfs" : [ {
-                    "operation" : "m.s.o",
-                    "stages" : {
-                      "consumer-encode" : {
-                        "tps" : 1.0,
-                        "msTotalTime" : 1.0000000000000002E-6,
-                        "msMaxLatency" : 1.0000000000000002E-6
-                      },
-                      "prepare" : {
-                        "tps" : 1.0,
-                        "msTotalTime" : 1.0000000000000002E-6,
-                        "msMaxLatency" : 1.0000000000000002E-6
-                      },
-                      "total" : {
-                        "tps" : 1.0,
-                        "msTotalTime" : 1.4000000000000001E-5,
-                        "msMaxLatency" : 1.4000000000000001E-5
-                      },
-                      "wait" : {
-                        "tps" : 1.0,
-                        "msTotalTime" : 1.0000000000000002E-6,
-                        "msMaxLatency" : 1.0000000000000002E-6
-                      },
-                      "consumer-send" : {
-                        "tps" : 1.0,
-                        "msTotalTime" : 1.0000000000000002E-6,
-                        "msMaxLatency" : 1.0000000000000002E-6
-                      },
-                      "connection" : {
-                        "tps" : 1.0,
-                        "msTotalTime" : 1.0000000000000002E-6,
-                        "msMaxLatency" : 1.0000000000000002E-6
-                      },
-                      "consumer-decode" : {
-                        "tps" : 1.0,
-                        "msTotalTime" : 1.0000000000000002E-6,
-                        "msMaxLatency" : 1.0000000000000002E-6
-                      }
-                    },
-                    "latencyDistribution" : [ 1, 0, 0 ]
-                  } ],
-                  "summary" : {
-                    "operation" : "",
-                    "stages" : {
-                      "consumer-encode" : {
-                        "tps" : 1.0,
-                        "msTotalTime" : 1.0000000000000002E-6,
-                        "msMaxLatency" : 1.0000000000000002E-6
-                      },
-                      "prepare" : {
-                        "tps" : 1.0,
-                        "msTotalTime" : 1.0000000000000002E-6,
-                        "msMaxLatency" : 1.0000000000000002E-6
-                      },
-                      "wait" : {
-                        "tps" : 1.0,
-                        "msTotalTime" : 1.0000000000000002E-6,
-                        "msMaxLatency" : 1.0000000000000002E-6
-                      },
-                      "total" : {
-                        "tps" : 1.0,
-                        "msTotalTime" : 1.4000000000000001E-5,
-                        "msMaxLatency" : 1.4000000000000001E-5
-                      },
-                      "consumer-send" : {
-                        "tps" : 1.0,
-                        "msTotalTime" : 1.0000000000000002E-6,
-                        "msMaxLatency" : 1.0000000000000002E-6
-                      },
-                      "connection" : {
-                        "tps" : 1.0,
-                        "msTotalTime" : 1.0000000000000002E-6,
-                        "msMaxLatency" : 1.0000000000000002E-6
-                      },
-                      "consumer-decode" : {
-                        "tps" : 1.0,
-                        "msTotalTime" : 1.0000000000000002E-6,
-                        "msMaxLatency" : 1.0000000000000002E-6
-                      }
-                    },
-                    "latencyDistribution" : [ 1, 0, 0 ]
-                  }
-                }
-              }
-            }
-          }
-        }
+           "operationPerfGroups" : {
+             "groups" : {
+               "rest" : {
+                 "200" : {
+                   "transport" : "rest",
+                   "status" : "200",
+                   "operationPerfs" : [ {
+                     "operation" : "m.s.o",
+                     "stages" : {
+                       "consumer-encode" : {
+                         "tps" : 1.0,
+                         "msTotalTime" : 1.0000000000000002E-6,
+                         "msMaxLatency" : 1.0000000000000002E-6
+                       },
+                       "prepare" : {
+                         "tps" : 1.0,
+                         "msTotalTime" : 1.0000000000000002E-6,
+                         "msMaxLatency" : 1.0000000000000002E-6
+                       },
+                       "wait" : {
+                         "tps" : 1.0,
+                         "msTotalTime" : 1.0000000000000002E-6,
+                         "msMaxLatency" : 1.0000000000000002E-6
+                       },
+                       "total" : {
+                         "tps" : 1.0,
+                         "msTotalTime" : 1.4E-5,
+                         "msMaxLatency" : 1.4E-5
+                       },
+                       "consumer-send" : {
+                         "tps" : 1.0,
+                         "msTotalTime" : 1.0000000000000002E-6,
+                         "msMaxLatency" : 1.0000000000000002E-6
+                       },
+                       "connection" : {
+                         "tps" : 1.0,
+                         "msTotalTime" : 1.0000000000000002E-6,
+                         "msMaxLatency" : 1.0000000000000002E-6
+                       },
+                       "consumer-decode" : {
+                         "tps" : 1.0,
+                         "msTotalTime" : 1.0000000000000002E-6,
+                         "msMaxLatency" : 1.0000000000000002E-6
+                       }
+                     },
+                     "latencyDistribution" : [ 1, 0, 0 ]
+                   } ],
+                   "summary" : {
+                     "operation" : "",
+                     "stages" : {
+                       "consumer-encode" : {
+                         "tps" : 1.0,
+                         "msTotalTime" : 1.0000000000000002E-6,
+                         "msMaxLatency" : 1.0000000000000002E-6
+                       },
+                       "prepare" : {
+                         "tps" : 1.0,
+                         "msTotalTime" : 1.0000000000000002E-6,
+                         "msMaxLatency" : 1.0000000000000002E-6
+                       },
+                       "total" : {
+                         "tps" : 1.0,
+                         "msTotalTime" : 1.4E-5,
+                         "msMaxLatency" : 1.4E-5
+                       },
+                       "wait" : {
+                         "tps" : 1.0,
+                         "msTotalTime" : 1.0000000000000002E-6,
+                         "msMaxLatency" : 1.0000000000000002E-6
+                       },
+                       "consumer-send" : {
+                         "tps" : 1.0,
+                         "msTotalTime" : 1.0000000000000002E-6,
+                         "msMaxLatency" : 1.0000000000000002E-6
+                       },
+                       "connection" : {
+                         "tps" : 1.0,
+                         "msTotalTime" : 1.0000000000000002E-6,
+                         "msMaxLatency" : 1.0000000000000002E-6
+                       },
+                       "consumer-decode" : {
+                         "tps" : 1.0,
+                         "msTotalTime" : 1.0000000000000002E-6,
+                         "msMaxLatency" : 1.0000000000000002E-6
+                       }
+                     },
+                     "latencyDistribution" : [ 1, 0, 0 ]
+                   }
+                 }
+               }
+             }
+           }
+         }
         """;
     Assertions.assertEquals(Json.encodePrettily(Json.decodeValue(expect, Object.class)),
         Json.encodePrettily(model.getProducer()));
diff --git a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestPublishUtils.java b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestPublishUtils.java
index 66a451aef..a540eb21b 100644
--- a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestPublishUtils.java
+++ b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestPublishUtils.java
@@ -32,7 +32,7 @@ import org.apache.servicecomb.metrics.core.publish.model.invocation.Utils;
 import org.junit.Test;
 import org.junit.jupiter.api.Assertions;
 
-import com.netflix.spectator.api.patterns.ThreadPoolMonitorPublishModelFactory;
+import org.apache.servicecomb.metrics.core.meter.ThreadPoolMonitorPublishModelFactory;
 
 import jakarta.ws.rs.core.Response.Status;
 
diff --git a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestThreadPoolPublishModelFactory.java b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestThreadPoolPublishModelFactory.java
index ef37c4f93..64778d3ee 100644
--- a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestThreadPoolPublishModelFactory.java
+++ b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestThreadPoolPublishModelFactory.java
@@ -19,27 +19,26 @@ package org.apache.servicecomb.metrics.core.publish;
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.ThreadPoolExecutor;
 
+import org.apache.servicecomb.foundation.common.event.EventManager;
 import org.apache.servicecomb.foundation.common.utils.JsonUtils;
+import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig;
+import org.apache.servicecomb.metrics.core.ThreadPoolMetersInitializer;
 import org.apache.servicecomb.metrics.core.publish.model.DefaultPublishModel;
 import org.junit.FixMethodOrder;
 import org.junit.Test;
 import org.junit.jupiter.api.Assertions;
 import org.junit.runners.MethodSorters;
+import org.mockito.Mockito;
 
-import com.google.common.collect.Lists;
-import com.netflix.spectator.api.DefaultRegistry;
-import com.netflix.spectator.api.ManualClock;
-import com.netflix.spectator.api.Registry;
-import com.netflix.spectator.api.patterns.PolledMeter;
-import com.netflix.spectator.api.patterns.ThreadPoolMonitor;
-
+import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
 import mockit.Expectations;
 import mockit.Injectable;
 import mockit.Mocked;
 
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 public class TestThreadPoolPublishModelFactory {
-  protected Registry registry = new DefaultRegistry(new ManualClock());
+  MeterRegistry registry = new SimpleMeterRegistry();
 
   @Mocked
   BlockingQueue<Runnable> queue;
@@ -55,10 +54,15 @@ public class TestThreadPoolPublishModelFactory {
       }
     };
 
-    ThreadPoolMonitor.attach(registry, threadPoolExecutor, "test");
-
-    PolledMeter.update(registry);
-    PublishModelFactory factory = new PublishModelFactory(Lists.newArrayList(registry.iterator()));
+    MetricsBootstrapConfig metricsBootstrapConfig = Mockito.mock(MetricsBootstrapConfig.class);
+    ThreadPoolMetersInitializer threadPoolMetersInitializer = new ThreadPoolMetersInitializer() {
+      @Override
+      public void createThreadPoolMeters() {
+        createThreadPoolMeters("test", threadPoolExecutor);
+      }
+    };
+    threadPoolMetersInitializer.init(registry, EventManager.getEventBus(), metricsBootstrapConfig);
+    PublishModelFactory factory = new PublishModelFactory(registry.getMeters());
     DefaultPublishModel model = factory.createDefaultPublishModel();
 
     Assertions.assertEquals(
diff --git a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/model/invocation/Utils.java b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/model/invocation/Utils.java
index 9c65ee24a..2a6247870 100644
--- a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/model/invocation/Utils.java
+++ b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/model/invocation/Utils.java
@@ -24,46 +24,47 @@ import org.apache.servicecomb.foundation.metrics.publish.spectator.MeasurementNo
 import org.apache.servicecomb.metrics.core.meter.invocation.MeterInvocationConst;
 import org.apache.servicecomb.metrics.core.publish.PublishUtils;
 
-import com.netflix.spectator.api.DefaultRegistry;
-import com.netflix.spectator.api.Id;
-import com.netflix.spectator.api.Measurement;
-import com.netflix.spectator.api.Registry;
-import com.netflix.spectator.api.Statistic;
-
+import io.micrometer.core.instrument.Measurement;
+import io.micrometer.core.instrument.Meter.Id;
+import io.micrometer.core.instrument.Meter.Type;
+import io.micrometer.core.instrument.Statistic;
+import io.micrometer.core.instrument.Tags;
 import jakarta.ws.rs.core.Response.Status;
 
 public class Utils {
-  static Registry registry = new DefaultRegistry();
-
   public static MeasurementNode totalStageNode = Utils.createStageNode(InvocationStageTrace.STAGE_TOTAL, 10, 10, 100);
 
   public static MeasurementNode executeStageNode =
       Utils.createStageNode(InvocationStageTrace.STAGE_PROVIDER_BUSINESS, 10, 10, 100);
 
+  public static Id initId = new Id("id", Tags.empty(), null, null, Type.OTHER);
+
   public static MeasurementNode createStageNode(String stage,
       double count,
       double totalTime,
       double max) {
-    Id id = registry.createId("id").withTag(Statistic.count);
-    Measurement countMeasurement = new Measurement(id.withTag(Statistic.count), 0, count);
-    Measurement totalTimeMeasurement = new Measurement(id.withTag(Statistic.totalTime), 0, totalTime);
-    Measurement maxMeasurement = new Measurement(id.withTag(Statistic.max), 0, max);
+    Id id = initId;
+    Measurement countMeasurement = new Measurement(() -> count, Statistic.COUNT);
+    Measurement totalTimeMeasurement = new Measurement(() -> totalTime, Statistic.TOTAL_TIME);
+    Measurement maxMeasurement = new Measurement(() -> max, Statistic.MAX);
 
-    MeasurementNode stageNode = new MeasurementNode(stage, null);
-    stageNode.addChild(Statistic.count.name(), countMeasurement);
-    stageNode.addChild(Statistic.totalTime.name(), totalTimeMeasurement);
-    stageNode.addChild(Statistic.max.name(), maxMeasurement);
+    MeasurementNode stageNode = new MeasurementNode(stage, id, null);
+    stageNode.addChild(Statistic.COUNT.name(), id, countMeasurement);
+    stageNode.addChild(Statistic.TOTAL_TIME.name(), id, totalTimeMeasurement);
+    stageNode.addChild(Statistic.MAX.name(), id, maxMeasurement);
 
     return stageNode;
   }
 
   public static MeasurementNode createStatusNode(String status, MeasurementNode... stageNodes) {
-    MeasurementNode statusNode = new MeasurementNode(status, new HashMap<>());
-    MeasurementNode typeNode = new MeasurementNode(MeterInvocationConst.TAG_STAGE, new HashMap<>());
-    MeasurementNode latencyNode = new MeasurementNode(MeterInvocationConst.TAG_LATENCY_DISTRIBUTION, new HashMap<>());
+    Id id = initId;
+    MeasurementNode statusNode = new MeasurementNode(status, id, new HashMap<>());
+    MeasurementNode typeNode = new MeasurementNode(MeterInvocationConst.TAG_STAGE, id, new HashMap<>());
+    MeasurementNode latencyNode = new MeasurementNode(MeterInvocationConst.TAG_LATENCY_DISTRIBUTION, id,
+        new HashMap<>());
     List<Measurement> measurements = latencyNode.getMeasurements();
-    measurements.add(new Measurement(null, 0L, 1));
-    measurements.add(new Measurement(null, 0L, 2));
+    measurements.add(new Measurement(() -> 1, Statistic.VALUE));
+    measurements.add(new Measurement(() -> 2, Statistic.VALUE));
     for (MeasurementNode stageNode : stageNodes) {
       typeNode.getChildren().put(stageNode.getName(), stageNode);
     }
diff --git a/metrics/metrics-integration/metrics-prometheus/src/test/java/org/apache/servicecomb/metrics/prometheus/TestPrometheusPublisher.java b/metrics/metrics-integration/metrics-prometheus/src/test/java/org/apache/servicecomb/metrics/prometheus/TestPrometheusPublisher.java
index 167a66b40..bd9fdd0bf 100644
--- a/metrics/metrics-integration/metrics-prometheus/src/test/java/org/apache/servicecomb/metrics/prometheus/TestPrometheusPublisher.java
+++ b/metrics/metrics-integration/metrics-prometheus/src/test/java/org/apache/servicecomb/metrics/prometheus/TestPrometheusPublisher.java
@@ -33,7 +33,6 @@ import org.apache.commons.lang3.reflect.FieldUtils;
 import org.apache.servicecomb.config.BootStrapProperties;
 import org.apache.servicecomb.foundation.common.exceptions.ServiceCombException;
 import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig;
-import org.apache.servicecomb.foundation.metrics.registry.GlobalRegistry;
 import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeEach;
@@ -41,16 +40,15 @@ import org.junit.jupiter.api.Test;
 import org.mockito.Mockito;
 import org.springframework.core.env.Environment;
 
-import com.netflix.spectator.api.Counter;
-import com.netflix.spectator.api.DefaultRegistry;
-import com.netflix.spectator.api.ManualClock;
-import com.netflix.spectator.api.Registry;
 import com.sun.net.httpserver.HttpServer;
 
+import io.micrometer.core.instrument.Counter;
+import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
 import io.prometheus.client.exporter.HTTPServer;
 
 public class TestPrometheusPublisher {
-  GlobalRegistry globalRegistry = new GlobalRegistry(new ManualClock());
+  MeterRegistry meterRegistry = new SimpleMeterRegistry();
 
   PrometheusPublisher publisher = new PrometheusPublisher();
 
@@ -75,7 +73,7 @@ public class TestPrometheusPublisher {
     Mockito.when(environment.getProperty(METRICS_PROMETHEUS_ADDRESS, String.class, "0.0.0.0:9696"))
         .thenReturn("a:b:c");
     Assertions.assertThrows(ServiceCombException.class, () -> {
-      publisher.init(globalRegistry, null, null);
+      publisher.init(meterRegistry, null, null);
     });
   }
 
@@ -84,7 +82,7 @@ public class TestPrometheusPublisher {
     Mockito.when(environment.getProperty(METRICS_PROMETHEUS_ADDRESS, String.class, "0.0.0.0:9696"))
         .thenReturn("localhost:xxxx");
     Assertions.assertThrows(ServiceCombException.class, () -> {
-      publisher.init(globalRegistry, null, null);
+      publisher.init(meterRegistry, null, null);
     });
   }
 
@@ -93,7 +91,7 @@ public class TestPrometheusPublisher {
     Mockito.when(environment.getProperty(METRICS_PROMETHEUS_ADDRESS, String.class, "0.0.0.0:9696"))
         .thenReturn("localhost:9999999");
     Assertions.assertThrows(ServiceCombException.class, () -> {
-      publisher.init(globalRegistry, null, null);
+      publisher.init(meterRegistry, null, null);
     });
   }
 
@@ -103,12 +101,9 @@ public class TestPrometheusPublisher {
     Mockito.when(environment.getProperty(METRICS_PROMETHEUS_ADDRESS, String.class, "0.0.0.0:9696"))
         .thenReturn("localhost:0");
     publisher.setEnvironment(environment);
-    publisher.init(globalRegistry, null, new MetricsBootstrapConfig(environment));
+    publisher.init(meterRegistry, null, new MetricsBootstrapConfig(environment));
 
-    Registry registry = new DefaultRegistry(new ManualClock());
-    globalRegistry.add(registry);
-
-    Counter counter = registry.counter("count.name", "tag1", "tag1v", "tag2", "tag2v");
+    Counter counter = meterRegistry.counter("count.name", "tag1", "tag1v", "tag2", "tag2v");
     counter.increment();
 
     HTTPServer httpServer = (HTTPServer) FieldUtils.readField(publisher, "httpServer", true);
diff --git a/solutions/solution-basic/src/main/java/org/apache/servicecomb/solution/basic/integration/MetricsEndpointImpl.java b/solutions/solution-basic/src/main/java/org/apache/servicecomb/solution/basic/integration/MetricsEndpointImpl.java
index 65103dbb2..e989a5b8a 100644
--- a/solutions/solution-basic/src/main/java/org/apache/servicecomb/solution/basic/integration/MetricsEndpointImpl.java
+++ b/solutions/solution-basic/src/main/java/org/apache/servicecomb/solution/basic/integration/MetricsEndpointImpl.java
@@ -24,13 +24,16 @@ import java.util.stream.StreamSupport;
 
 import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig;
 import org.apache.servicecomb.foundation.metrics.MetricsInitializer;
+import org.apache.servicecomb.metrics.core.meter.vertx.EndpointMeter;
 import org.apache.servicecomb.provider.rest.common.RestSchema;
 
 import com.google.common.eventbus.EventBus;
 
+import io.micrometer.core.instrument.Measurement;
 import io.micrometer.core.instrument.Meter;
 import io.micrometer.core.instrument.Meter.Id;
 import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.Tag;
 
 @RestSchema(schemaId = MetricsEndpoint.NAME, schemaInterface = MetricsEndpoint.class)
 public class MetricsEndpointImpl implements MetricsInitializer, MetricsEndpoint {
@@ -48,7 +51,7 @@ public class MetricsEndpointImpl implements MetricsInitializer, MetricsEndpoint
 
     for (Meter meter : this.meterRegistry.getMeters()) {
       meter.measure().forEach(measurement -> {
-        String key = idToString(meter.getId(), sb);
+        String key = idToString(meter.getId(), measurement, sb);
         measurements.put(key, measurement.getValue());
       });
     }
@@ -58,18 +61,23 @@ public class MetricsEndpointImpl implements MetricsInitializer, MetricsEndpoint
 
   // format id to string:
   // idName(tag1=value1,tag2=value2)
-  protected String idToString(Id id, StringBuilder sb) {
+  protected String idToString(Id id, Measurement measurement, StringBuilder sb) {
     sb.setLength(0);
-    sb.append(id.getName()).append('(');
+    sb.append(id.getName()).append("(").append(EndpointMeter.STATISTIC).append("=")
+        .append(measurement.getStatistic().name()).append(",");
     sb.append(StreamSupport
         .stream(id
             .getTags()
             .spliterator(), false)
-        .map(Object::toString)
+        .map(this::tagToString)
         .collect(
             Collectors.joining(",")));
     sb.append(')');
 
     return sb.toString();
   }
+
+  private String tagToString(Tag tag) {
+    return tag.getKey() + "=" + tag.getValue();
+  }
 }
diff --git a/solutions/solution-basic/src/test/java/org/apache/servicecomb/solution/basic/integration/TestMetricsEndpointImpl.java b/solutions/solution-basic/src/test/java/org/apache/servicecomb/solution/basic/integration/TestMetricsEndpointImpl.java
index a7698d609..cb175d9b7 100644
--- a/solutions/solution-basic/src/test/java/org/apache/servicecomb/solution/basic/integration/TestMetricsEndpointImpl.java
+++ b/solutions/solution-basic/src/test/java/org/apache/servicecomb/solution/basic/integration/TestMetricsEndpointImpl.java
@@ -23,25 +23,38 @@ import static org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig.M
 import java.util.Map;
 
 import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig;
-import org.apache.servicecomb.foundation.metrics.registry.GlobalRegistry;
-import org.junit.Test;
 import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
 import org.mockito.Mockito;
 import org.springframework.core.env.Environment;
 
 import com.google.common.eventbus.EventBus;
-import com.netflix.spectator.api.Clock;
-import com.netflix.spectator.api.DefaultRegistry;
-import com.netflix.spectator.api.ManualClock;
-import com.netflix.spectator.api.Registry;
+
+import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
 
 public class TestMetricsEndpointImpl {
   MetricsEndpointImpl publisher = new MetricsEndpointImpl();
 
+  EventBus eventBus = new EventBus();
+
   Environment environment = Mockito.mock(Environment.class);
 
+  @BeforeEach
+  public void setUp() {
+    Mockito.when(environment.getProperty(METRICS_WINDOW_TIME, int.class, DEFAULT_METRICS_WINDOW_TIME))
+        .thenReturn(DEFAULT_METRICS_WINDOW_TIME);
+    Mockito.when(environment.getProperty(
+            CONFIG_LATENCY_DISTRIBUTION_MIN_SCOPE_LEN, int.class, 7))
+        .thenReturn(7);
+  }
+
   @Test
   public void measure_globalRegistryNull() {
+    MeterRegistry registry = new SimpleMeterRegistry();
+    publisher.init(registry, eventBus, new MetricsBootstrapConfig(environment));
+
     Map<String, Double> result = publisher.measure();
 
     Assertions.assertEquals(0, result.size());
@@ -49,25 +62,15 @@ public class TestMetricsEndpointImpl {
 
   @Test
   public void measure_normal() {
-    Mockito.when(environment.getProperty(METRICS_WINDOW_TIME, int.class, DEFAULT_METRICS_WINDOW_TIME))
-        .thenReturn(DEFAULT_METRICS_WINDOW_TIME);
-    Mockito.when(environment.getProperty(
-            CONFIG_LATENCY_DISTRIBUTION_MIN_SCOPE_LEN, int.class, 7))
-        .thenReturn(7);
-
-    Clock clock = new ManualClock();
-    GlobalRegistry globalRegistry = new GlobalRegistry();
-    Registry registry = new DefaultRegistry(clock);
-    registry.timer(registry.createId("name", "t1", "v1", "t2", "v2"));
-    globalRegistry.add(registry);
-
-    EventBus eventBus = new EventBus();
+    MeterRegistry registry = new SimpleMeterRegistry();
+    registry.timer("name", "t1", "v1", "t2", "v2");
 
-    publisher.init(globalRegistry, eventBus, new MetricsBootstrapConfig(environment));
+    publisher.init(registry, eventBus, new MetricsBootstrapConfig(environment));
     Map<String, Double> result = publisher.measure();
 
-    Assertions.assertEquals(2, result.size());
-    Assertions.assertEquals(0, result.get("name(statistic=count,t1=v1,t2=v2)"), 0);
-    Assertions.assertEquals(0, result.get("name(statistic=totalTime,t1=v1,t2=v2)"), 0);
+    Assertions.assertEquals(3, result.size());
+    Assertions.assertEquals(0, result.get("name(statistic=COUNT,t1=v1,t2=v2)"), 0);
+    Assertions.assertEquals(0, result.get("name(statistic=TOTAL_TIME,t1=v1,t2=v2)"), 0);
+    Assertions.assertEquals(0, result.get("name(statistic=MAX,t1=v1,t2=v2)"), 0);
   }
 }