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 2018/03/27 12:15:39 UTC

[incubator-servicecomb-java-chassis] 04/04: SCB-383 subscribe invocation life event and statistics

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/incubator-servicecomb-java-chassis.git

commit ae28a9faff24e773520f3452dfd80b86dfbe7aa4
Author: wujimin <wu...@huawei.com>
AuthorDate: Sat Mar 17 10:54:16 2018 +0800

    SCB-383 subscribe invocation life event and statistics
---
 .../core/event/InvocationStartEvent.java           |   1 -
 .../foundation/common/event/EventManager.java      |   4 +
 metrics/metrics-core/pom.xml                       |   8 +-
 .../metrics/core/DefaultMetricsInitializer.java    |  82 ++++++++++++
 .../metrics/core/MetricsBootListener.java          |  42 +++----
 .../metrics/core/meter/ConsumerMeters.java         |  20 +--
 .../metrics/core/meter/ProducerMeters.java         |  20 +--
 .../meter/invocation/AbstractInvocationMeter.java  |  23 ++--
 .../meter/invocation/AbstractInvocationMeters.java |  79 ++++++++++++
 .../meter/invocation/ConsumerInvocationMeter.java  |  17 ++-
 .../meter/invocation/ConsumerInvocationMeters.java |  18 +--
 .../meter/invocation/MeterInvocationConst.java     |  32 +++--
 .../meter/invocation/ProducerInvocationMeter.java  |  51 ++++++++
 .../meter/invocation/ProducerInvocationMeters.java |  18 +--
 ...rvicecomb.foundation.metrics.MetricsInitializer |  18 +++
 .../core/TestDefaultMetricsInitializer.java        | 138 +++++++++++++++++++++
 16 files changed, 478 insertions(+), 93 deletions(-)

diff --git a/core/src/main/java/org/apache/servicecomb/core/event/InvocationStartEvent.java b/core/src/main/java/org/apache/servicecomb/core/event/InvocationStartEvent.java
index 5b93e66..e83c447 100644
--- a/core/src/main/java/org/apache/servicecomb/core/event/InvocationStartEvent.java
+++ b/core/src/main/java/org/apache/servicecomb/core/event/InvocationStartEvent.java
@@ -22,7 +22,6 @@ public class InvocationStartEvent {
   private Invocation invocation;
 
   public InvocationStartEvent(Invocation invocation) {
-    super();
     this.invocation = invocation;
   }
 
diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/event/EventManager.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/event/EventManager.java
index d50e767..9672a92 100644
--- a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/event/EventManager.java
+++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/event/EventManager.java
@@ -27,6 +27,10 @@ import com.google.common.eventbus.SimpleEventBus;
 public class EventManager {
   public static EventBus eventBus = new SimpleEventBus();
 
+  public static EventBus getEventBus() {
+    return eventBus;
+  }
+
   /**
    * Registering listener.
    */
diff --git a/metrics/metrics-core/pom.xml b/metrics/metrics-core/pom.xml
index 8e4d3a4..f4d1e76 100644
--- a/metrics/metrics-core/pom.xml
+++ b/metrics/metrics-core/pom.xml
@@ -16,8 +16,7 @@
   ~ limitations under the License.
   -->
 
-<project xmlns="http://maven.apache.org/POM/4.0.0"
-  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <parent>
     <artifactId>metrics</artifactId>
@@ -48,6 +47,11 @@
       <artifactId>slf4j-log4j12</artifactId>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>org.apache.servicecomb</groupId>
+      <artifactId>foundation-test-scaffolding</artifactId>
+    </dependency>
+
   </dependencies>
 
 </project>
diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/DefaultMetricsInitializer.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/DefaultMetricsInitializer.java
new file mode 100644
index 0000000..12e090e
--- /dev/null
+++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/DefaultMetricsInitializer.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.servicecomb.metrics.core;
+
+import org.apache.servicecomb.core.Invocation;
+import org.apache.servicecomb.core.event.InvocationFinishEvent;
+import org.apache.servicecomb.core.event.InvocationStartEvent;
+import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig;
+import org.apache.servicecomb.foundation.metrics.MetricsInitializer;
+import org.apache.servicecomb.metrics.core.meter.ConsumerMeters;
+import org.apache.servicecomb.metrics.core.meter.ProducerMeters;
+import org.apache.servicecomb.metrics.core.meter.invocation.AbstractInvocationMeters;
+
+import com.google.common.eventbus.AllowConcurrentEvents;
+import com.google.common.eventbus.EventBus;
+import com.google.common.eventbus.Subscribe;
+import com.netflix.spectator.api.CompositeRegistry;
+import com.netflix.spectator.api.Registry;
+import com.netflix.spectator.servo.ServoRegistry;
+
+public class DefaultMetricsInitializer implements MetricsInitializer {
+  public static final String METRICS_WINDOW_TIME = "servicecomb.metrics.window_time";
+
+  public static final int DEFAULT_METRICS_WINDOW_TIME = 5000;
+
+  private Registry registry;
+
+  private ConsumerMeters consumerMeters;
+
+  private ProducerMeters producerMeters;
+
+  @Override
+  public void init(CompositeRegistry globalRegistry, EventBus eventBus, MetricsBootstrapConfig config) {
+    registry = createRegistry(config);
+
+    this.consumerMeters = new ConsumerMeters(registry);
+    this.producerMeters = new ProducerMeters(registry);
+
+    globalRegistry.add(registry);
+    eventBus.register(this);
+  }
+
+  protected Registry createRegistry(MetricsBootstrapConfig config) {
+    System.getProperties().setProperty("servo.pollers", String.valueOf(config.getMsPollInterval()));
+    return new ServoRegistry();
+  }
+
+  protected AbstractInvocationMeters findInvocationMeters(Invocation invocation) {
+    if (invocation.isConsumer()) {
+      return consumerMeters.getInvocationMeters();
+    }
+    return producerMeters.getInvocationMeters();
+  }
+
+  @Subscribe
+  @AllowConcurrentEvents
+  public void onInvocationStart(InvocationStartEvent event) {
+    AbstractInvocationMeters invocationMeters = findInvocationMeters(event.getInvocation());
+    invocationMeters.onInvocationStart(event);
+  }
+
+  @Subscribe
+  @AllowConcurrentEvents
+  public void onInvocationFinish(InvocationFinishEvent event) {
+    AbstractInvocationMeters invocationMeters = findInvocationMeters(event.getInvocation());
+    invocationMeters.onInvocationFinish(event);
+  }
+}
diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/event/EventManager.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/MetricsBootListener.java
similarity index 54%
copy from foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/event/EventManager.java
copy to metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/MetricsBootListener.java
index d50e767..48c2837 100644
--- a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/event/EventManager.java
+++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/MetricsBootListener.java
@@ -14,37 +14,25 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package org.apache.servicecomb.metrics.core;
 
-package org.apache.servicecomb.foundation.common.event;
+import org.apache.servicecomb.core.BootListener;
+import org.apache.servicecomb.foundation.common.event.EventManager;
+import org.apache.servicecomb.foundation.metrics.MetricsBootstrap;
+import org.springframework.stereotype.Component;
 
-import com.google.common.eventbus.EventBus;
-import com.google.common.eventbus.SimpleEventBus;
+import com.netflix.spectator.api.Spectator;
 
-/**
- * EventManager for chassis events
- *
- */
-public class EventManager {
-  public static EventBus eventBus = new SimpleEventBus();
+@Component
+public class MetricsBootListener implements BootListener {
+  private MetricsBootstrap metricsBootstrap = new MetricsBootstrap();
 
-  /**
-   * Registering listener.
-   */
-  public static void register(Object listener) {
-    eventBus.register(listener);
-  }
-
-  /**
-   * post event.
-   */
-  public static void post(Object event) {
-    eventBus.post(event);
-  }
+  @Override
+  public void onBootEvent(BootEvent event) {
+    if (!EventType.AFTER_REGISTRY.equals(event.getEventType())) {
+      return;
+    }
 
-  /**
-   * Unregistering listener.
-   */
-  public static void unregister(Object listener) {
-    eventBus.unregister(listener);
+    metricsBootstrap.start(Spectator.globalRegistry(), EventManager.getEventBus());
   }
 }
diff --git a/core/src/main/java/org/apache/servicecomb/core/event/InvocationStartEvent.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/ConsumerMeters.java
similarity index 59%
copy from core/src/main/java/org/apache/servicecomb/core/event/InvocationStartEvent.java
copy to metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/ConsumerMeters.java
index 5b93e66..b249fdc 100644
--- a/core/src/main/java/org/apache/servicecomb/core/event/InvocationStartEvent.java
+++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/ConsumerMeters.java
@@ -14,19 +14,21 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.servicecomb.core.event;
+package org.apache.servicecomb.metrics.core.meter;
 
-import org.apache.servicecomb.core.Invocation;
+import org.apache.servicecomb.metrics.core.meter.invocation.AbstractInvocationMeters;
+import org.apache.servicecomb.metrics.core.meter.invocation.ConsumerInvocationMeters;
 
-public class InvocationStartEvent {
-  private Invocation invocation;
+import com.netflix.spectator.api.Registry;
 
-  public InvocationStartEvent(Invocation invocation) {
-    super();
-    this.invocation = invocation;
+public class ConsumerMeters {
+  private AbstractInvocationMeters invocationMeters;
+
+  public ConsumerMeters(Registry registry) {
+    invocationMeters = new ConsumerInvocationMeters(registry);
   }
 
-  public Invocation getInvocation() {
-    return invocation;
+  public AbstractInvocationMeters getInvocationMeters() {
+    return invocationMeters;
   }
 }
diff --git a/core/src/main/java/org/apache/servicecomb/core/event/InvocationStartEvent.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/ProducerMeters.java
similarity index 59%
copy from core/src/main/java/org/apache/servicecomb/core/event/InvocationStartEvent.java
copy to metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/ProducerMeters.java
index 5b93e66..e867f8e 100644
--- a/core/src/main/java/org/apache/servicecomb/core/event/InvocationStartEvent.java
+++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/ProducerMeters.java
@@ -14,19 +14,21 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.servicecomb.core.event;
+package org.apache.servicecomb.metrics.core.meter;
 
-import org.apache.servicecomb.core.Invocation;
+import org.apache.servicecomb.metrics.core.meter.invocation.AbstractInvocationMeters;
+import org.apache.servicecomb.metrics.core.meter.invocation.ProducerInvocationMeters;
 
-public class InvocationStartEvent {
-  private Invocation invocation;
+import com.netflix.spectator.api.Registry;
 
-  public InvocationStartEvent(Invocation invocation) {
-    super();
-    this.invocation = invocation;
+public class ProducerMeters {
+  private AbstractInvocationMeters invocationMeters;
+
+  public ProducerMeters(Registry registry) {
+    invocationMeters = new ProducerInvocationMeters(registry);
   }
 
-  public Invocation getInvocation() {
-    return invocation;
+  public AbstractInvocationMeters getInvocationMeters() {
+    return invocationMeters;
   }
 }
diff --git a/core/src/main/java/org/apache/servicecomb/core/event/InvocationStartEvent.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/AbstractInvocationMeter.java
similarity index 51%
copy from core/src/main/java/org/apache/servicecomb/core/event/InvocationStartEvent.java
copy to metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/AbstractInvocationMeter.java
index 5b93e66..d2ae510 100644
--- a/core/src/main/java/org/apache/servicecomb/core/event/InvocationStartEvent.java
+++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/AbstractInvocationMeter.java
@@ -14,19 +14,26 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.servicecomb.core.event;
+package org.apache.servicecomb.metrics.core.meter.invocation;
+
+import java.util.concurrent.TimeUnit;
 
 import org.apache.servicecomb.core.Invocation;
+import org.apache.servicecomb.core.event.InvocationFinishEvent;
+import org.apache.servicecomb.swagger.invocation.Response;
+
+import com.netflix.spectator.api.Id;
+import com.netflix.spectator.api.Registry;
+import com.netflix.spectator.api.Timer;
 
-public class InvocationStartEvent {
-  private Invocation invocation;
+public abstract class AbstractInvocationMeter {
+  private Timer totalTimer;
 
-  public InvocationStartEvent(Invocation invocation) {
-    super();
-    this.invocation = invocation;
+  public AbstractInvocationMeter(Registry registry, Id id, Invocation invocation, Response response) {
+    totalTimer = registry.timer(id.withTag(MeterInvocationConst.TAG_STAGE, MeterInvocationConst.STAGE_TOTAL));
   }
 
-  public Invocation getInvocation() {
-    return invocation;
+  public void onInvocationFinish(InvocationFinishEvent event) {
+    totalTimer.record(event.getNanoCurrent() - event.getInvocation().getStartTime(), TimeUnit.NANOSECONDS);
   }
 }
diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/AbstractInvocationMeters.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/AbstractInvocationMeters.java
new file mode 100644
index 0000000..43df838
--- /dev/null
+++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/AbstractInvocationMeters.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.servicecomb.metrics.core.meter.invocation;
+
+import java.util.Map;
+
+import org.apache.servicecomb.core.Invocation;
+import org.apache.servicecomb.core.event.InvocationFinishEvent;
+import org.apache.servicecomb.core.event.InvocationStartEvent;
+import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx;
+import org.apache.servicecomb.swagger.invocation.Response;
+
+import com.netflix.spectator.api.Id;
+import com.netflix.spectator.api.Registry;
+
+public abstract class AbstractInvocationMeters {
+  protected Registry registry;
+
+  private Map<String, AbstractInvocationMeter> metersMap = new ConcurrentHashMapEx<>();
+
+  // not care for concurrency, just for make build key faster 
+  private int maxKeyLen = 64;
+
+  public AbstractInvocationMeters(Registry registry) {
+    this.registry = registry;
+  }
+
+  protected AbstractInvocationMeter getOrCreateMeters(Invocation invocation, Response response) {
+    // build string key is faster then use Id to locate timer directly
+    StringBuilder keyBuilder = new StringBuilder(maxKeyLen);
+    keyBuilder
+        .append(invocation.getInvocationType().name())
+        .append(invocation.getRealTransportName())
+        .append(invocation.getMicroserviceQualifiedName())
+        .append(response.getStatusCode());
+    if (keyBuilder.length() > maxKeyLen) {
+      maxKeyLen = keyBuilder.length();
+    }
+
+    return metersMap.computeIfAbsent(keyBuilder.toString(), k -> {
+      Id id = registry.createId(MeterInvocationConst.INVOCATION_NAME,
+          MeterInvocationConst.TAG_ROLE,
+          invocation.getInvocationType().name(),
+          MeterInvocationConst.TAG_TRANSPORT,
+          invocation.getRealTransportName(),
+          MeterInvocationConst.TAG_OPERATION,
+          invocation.getMicroserviceQualifiedName(),
+          MeterInvocationConst.TAG_STATUS,
+          String.valueOf(response.getStatusCode()));
+
+      return createMeter(id, invocation, response);
+    });
+  }
+
+  protected abstract AbstractInvocationMeter createMeter(Id id, Invocation invocation,
+      Response response);
+
+  public void onInvocationStart(InvocationStartEvent event) {
+  }
+
+  public void onInvocationFinish(InvocationFinishEvent event) {
+    AbstractInvocationMeter meters = getOrCreateMeters(event.getInvocation(), event.getResponse());
+    meters.onInvocationFinish(event);
+  }
+}
diff --git a/core/src/main/java/org/apache/servicecomb/core/event/InvocationStartEvent.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/ConsumerInvocationMeter.java
similarity index 67%
copy from core/src/main/java/org/apache/servicecomb/core/event/InvocationStartEvent.java
copy to metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/ConsumerInvocationMeter.java
index 5b93e66..0f473a0 100644
--- a/core/src/main/java/org/apache/servicecomb/core/event/InvocationStartEvent.java
+++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/ConsumerInvocationMeter.java
@@ -14,19 +14,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.servicecomb.core.event;
+package org.apache.servicecomb.metrics.core.meter.invocation;
 
 import org.apache.servicecomb.core.Invocation;
+import org.apache.servicecomb.swagger.invocation.Response;
 
-public class InvocationStartEvent {
-  private Invocation invocation;
+import com.netflix.spectator.api.Id;
+import com.netflix.spectator.api.Registry;
 
-  public InvocationStartEvent(Invocation invocation) {
-    super();
-    this.invocation = invocation;
-  }
-
-  public Invocation getInvocation() {
-    return invocation;
+public class ConsumerInvocationMeter extends AbstractInvocationMeter {
+  public ConsumerInvocationMeter(Registry registry, Id id, Invocation invocation, Response response) {
+    super(registry, id, invocation, response);
   }
 }
diff --git a/core/src/main/java/org/apache/servicecomb/core/event/InvocationStartEvent.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/ConsumerInvocationMeters.java
similarity index 61%
copy from core/src/main/java/org/apache/servicecomb/core/event/InvocationStartEvent.java
copy to metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/ConsumerInvocationMeters.java
index 5b93e66..dd24ecb 100644
--- a/core/src/main/java/org/apache/servicecomb/core/event/InvocationStartEvent.java
+++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/ConsumerInvocationMeters.java
@@ -14,19 +14,21 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.servicecomb.core.event;
+package org.apache.servicecomb.metrics.core.meter.invocation;
 
 import org.apache.servicecomb.core.Invocation;
+import org.apache.servicecomb.swagger.invocation.Response;
 
-public class InvocationStartEvent {
-  private Invocation invocation;
+import com.netflix.spectator.api.Id;
+import com.netflix.spectator.api.Registry;
 
-  public InvocationStartEvent(Invocation invocation) {
-    super();
-    this.invocation = invocation;
+public class ConsumerInvocationMeters extends AbstractInvocationMeters {
+  public ConsumerInvocationMeters(Registry registry) {
+    super(registry);
   }
 
-  public Invocation getInvocation() {
-    return invocation;
+  @Override
+  protected AbstractInvocationMeter createMeter(Id id, Invocation invocation, Response response) {
+    return new ConsumerInvocationMeter(registry, id, invocation, response);
   }
 }
diff --git a/core/src/main/java/org/apache/servicecomb/core/event/InvocationStartEvent.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/MeterInvocationConst.java
similarity index 56%
copy from core/src/main/java/org/apache/servicecomb/core/event/InvocationStartEvent.java
copy to metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/MeterInvocationConst.java
index 5b93e66..3d208c3 100644
--- a/core/src/main/java/org/apache/servicecomb/core/event/InvocationStartEvent.java
+++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/MeterInvocationConst.java
@@ -14,19 +14,29 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.servicecomb.core.event;
+package org.apache.servicecomb.metrics.core.meter.invocation;
 
-import org.apache.servicecomb.core.Invocation;
+import com.netflix.spectator.api.Statistic;
 
-public class InvocationStartEvent {
-  private Invocation invocation;
+public interface MeterInvocationConst {
+  final String INVOCATION_NAME = "servicecomb.invocation";
 
-  public InvocationStartEvent(Invocation invocation) {
-    super();
-    this.invocation = invocation;
-  }
+  // consumer or producer
+  final String TAG_ROLE = "role";
 
-  public Invocation getInvocation() {
-    return invocation;
-  }
+  final String TAG_OPERATION = "operation";
+
+  final String TAG_TRANSPORT = "transport";
+
+  final String TAG_STAGE = "stage";
+
+  final String TAG_STATUS = "status";
+
+  final String TAG_STATISTIC = Statistic.count.key();
+
+  final String STAGE_TOTAL = "total";
+
+  final String STAGE_EXECUTOR_QUEUE = "queue";
+
+  final String STAGE_EXECUTION = "execution";
 }
diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/ProducerInvocationMeter.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/ProducerInvocationMeter.java
new file mode 100644
index 0000000..1106265
--- /dev/null
+++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/ProducerInvocationMeter.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.servicecomb.metrics.core.meter.invocation;
+
+import java.util.concurrent.TimeUnit;
+
+import org.apache.servicecomb.core.Invocation;
+import org.apache.servicecomb.core.event.InvocationFinishEvent;
+import org.apache.servicecomb.swagger.invocation.Response;
+
+import com.netflix.spectator.api.Id;
+import com.netflix.spectator.api.Registry;
+import com.netflix.spectator.api.Timer;
+
+public class ProducerInvocationMeter extends AbstractInvocationMeter {
+  private Timer executorQueueTimer;
+
+  private Timer executionTimer;
+
+  public ProducerInvocationMeter(Registry registry, Id id, Invocation invocation, Response response) {
+    super(registry, id, invocation, response);
+
+    executorQueueTimer =
+        registry.timer(id.withTag(MeterInvocationConst.TAG_STAGE, MeterInvocationConst.STAGE_EXECUTOR_QUEUE));
+    executionTimer =
+        registry.timer(id.withTag(MeterInvocationConst.TAG_STAGE, MeterInvocationConst.STAGE_EXECUTION));
+  }
+
+  @Override
+  public void onInvocationFinish(InvocationFinishEvent event) {
+    super.onInvocationFinish(event);
+
+    Invocation invocation = event.getInvocation();
+    executorQueueTimer.record(invocation.getStartExecutionTime() - invocation.getStartTime(), TimeUnit.NANOSECONDS);
+    executionTimer.record(event.getNanoCurrent() - invocation.getStartExecutionTime(), TimeUnit.NANOSECONDS);
+  }
+}
diff --git a/core/src/main/java/org/apache/servicecomb/core/event/InvocationStartEvent.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/ProducerInvocationMeters.java
similarity index 61%
copy from core/src/main/java/org/apache/servicecomb/core/event/InvocationStartEvent.java
copy to metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/ProducerInvocationMeters.java
index 5b93e66..56031d0 100644
--- a/core/src/main/java/org/apache/servicecomb/core/event/InvocationStartEvent.java
+++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/ProducerInvocationMeters.java
@@ -14,19 +14,21 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.servicecomb.core.event;
+package org.apache.servicecomb.metrics.core.meter.invocation;
 
 import org.apache.servicecomb.core.Invocation;
+import org.apache.servicecomb.swagger.invocation.Response;
 
-public class InvocationStartEvent {
-  private Invocation invocation;
+import com.netflix.spectator.api.Id;
+import com.netflix.spectator.api.Registry;
 
-  public InvocationStartEvent(Invocation invocation) {
-    super();
-    this.invocation = invocation;
+public class ProducerInvocationMeters extends AbstractInvocationMeters {
+  public ProducerInvocationMeters(Registry registry) {
+    super(registry);
   }
 
-  public Invocation getInvocation() {
-    return invocation;
+  @Override
+  protected AbstractInvocationMeter createMeter(Id id, Invocation invocation, Response response) {
+    return new ProducerInvocationMeter(registry, id, invocation, response);
   }
 }
diff --git a/metrics/metrics-core/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.metrics.MetricsInitializer b/metrics/metrics-core/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.metrics.MetricsInitializer
new file mode 100644
index 0000000..2bf2069
--- /dev/null
+++ b/metrics/metrics-core/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.metrics.MetricsInitializer
@@ -0,0 +1,18 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+org.apache.servicecomb.metrics.core.DefaultMetricsInitializer
diff --git a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestDefaultMetricsInitializer.java b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestDefaultMetricsInitializer.java
new file mode 100644
index 0000000..2c48866
--- /dev/null
+++ b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestDefaultMetricsInitializer.java
@@ -0,0 +1,138 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.servicecomb.metrics.core;
+
+import org.apache.servicecomb.core.Const;
+import org.apache.servicecomb.core.Invocation;
+import org.apache.servicecomb.core.event.InvocationFinishEvent;
+import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig;
+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.invocation.MeterInvocationConst;
+import org.apache.servicecomb.swagger.invocation.InvocationType;
+import org.apache.servicecomb.swagger.invocation.Response;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.google.common.eventbus.EventBus;
+import com.netflix.spectator.api.CompositeRegistry;
+import com.netflix.spectator.api.DefaultRegistry;
+import com.netflix.spectator.api.ManualClock;
+import com.netflix.spectator.api.Registry;
+import com.netflix.spectator.api.SpectatorUtils;
+
+import mockit.Expectations;
+import mockit.Mocked;
+
+public class TestDefaultMetricsInitializer {
+  EventBus eventBus = new EventBus();
+
+  ManualClock clock = new ManualClock();
+
+  CompositeRegistry globalRegistry = SpectatorUtils.createCompositeRegistry(new ManualClock());
+
+  DefaultMetricsInitializer metricsInitializer = new DefaultMetricsInitializer() {
+    protected Registry createRegistry(MetricsBootstrapConfig config) {
+      return new DefaultRegistry(new ManualClock());
+    };
+  };
+
+  MetricsBootstrapConfig config = new MetricsBootstrapConfig();
+
+  @Mocked
+  private Invocation invocation;
+
+  @Mocked
+  private Response response;
+
+
+  @Before
+  public void setup() {
+    metricsInitializer.init(globalRegistry, eventBus, config);
+  }
+
+  @Test
+  public void consumerInvocation(@Mocked InvocationFinishEvent event) {
+    new Expectations() {
+      {
+        invocation.isConsumer();
+        result = true;
+        invocation.getInvocationType();
+        result = InvocationType.CONSUMER;
+        invocation.getRealTransportName();
+        result = Const.RESTFUL;
+        invocation.getMicroserviceQualifiedName();
+        result = "m.s.o";
+        invocation.getStartTime();
+        result = 1;
+        event.getInvocation();
+        result = invocation;
+        event.getNanoCurrent();
+        result = 10;
+      }
+    };
+
+    eventBus.post(event);
+    eventBus.post(event);
+
+    MeasurementTree tree = new MeasurementTree();
+    tree.from(globalRegistry.iterator(), new MeasurementGroupConfig(MeterInvocationConst.INVOCATION_NAME));
+    Assert.assertEquals(
+        "[Measurement(servicecomb.invocation:operation=m.s.o:role=CONSUMER:stage=total:statistic=count:status=0:transport=rest,0,2.0), "
+            + "Measurement(servicecomb.invocation:operation=m.s.o:role=CONSUMER:stage=total:statistic=totalTime:status=0:transport=rest,0,18.0)]",
+        tree.findChild(MeterInvocationConst.INVOCATION_NAME).getMeasurements().toString());
+  }
+
+  @Test
+  public void producerInvocation(@Mocked InvocationFinishEvent event) {
+    new Expectations() {
+      {
+        invocation.isConsumer();
+        result = false;
+        invocation.getInvocationType();
+        result = InvocationType.PRODUCER;
+        invocation.getRealTransportName();
+        result = Const.RESTFUL;
+        invocation.getMicroserviceQualifiedName();
+        result = "m.s.o";
+        invocation.getStartTime();
+        result = 1;
+        invocation.getStartExecutionTime();
+        result = 3;
+        event.getNanoCurrent();
+        result = 10;
+        event.getInvocation();
+        result = invocation;
+      }
+    };
+
+    eventBus.post(event);
+    eventBus.post(event);
+
+    MeasurementTree tree = new MeasurementTree();
+    tree.from(globalRegistry.iterator(), new MeasurementGroupConfig(MeterInvocationConst.INVOCATION_NAME));
+    Assert.assertEquals(
+        "[Measurement(servicecomb.invocation:operation=m.s.o:role=PRODUCER:stage=execution:statistic=count:status=0:transport=rest,0,2.0), "
+            + "Measurement(servicecomb.invocation:operation=m.s.o:role=PRODUCER:stage=execution:statistic=totalTime:status=0:transport=rest,0,14.0), "
+            + "Measurement(servicecomb.invocation:operation=m.s.o:role=PRODUCER:stage=total:statistic=count:status=0:transport=rest,0,2.0), "
+            + "Measurement(servicecomb.invocation:operation=m.s.o:role=PRODUCER:stage=total:statistic=totalTime:status=0:transport=rest,0,18.0), "
+            + "Measurement(servicecomb.invocation:operation=m.s.o:role=PRODUCER:stage=queue:statistic=count:status=0:transport=rest,0,2.0), "
+            + "Measurement(servicecomb.invocation:operation=m.s.o:role=PRODUCER:stage=queue:statistic=totalTime:status=0:transport=rest,0,4.0)]",
+        tree.findChild(MeterInvocationConst.INVOCATION_NAME).getMeasurements().toString());
+  }
+}

-- 
To stop receiving notification emails like this one, please contact
liubao@apache.org.