You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@servicecomb.apache.org by GitBox <gi...@apache.org> on 2018/08/01 11:32:01 UTC

[GitHub] asifdxtreme closed pull request #396: SCB-735 Add admin dump api

asifdxtreme closed pull request #396: SCB-735 Add admin dump api
URL: https://github.com/apache/incubator-servicecomb-service-center/pull/396
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/.gitignore b/.gitignore
index 84fb8827..8bb0528a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,8 @@
 bin/
 output
 **/*.coverprofile
+**/coverage.out
+**/coverage.txt
 
 service-center.iml
 .idea/
diff --git a/etc/conf/app.conf b/etc/conf/app.conf
index 4b49680a..16fe4842 100644
--- a/etc/conf/app.conf
+++ b/etc/conf/app.conf
@@ -60,17 +60,23 @@ registry_plugin = etcd
 # manager_cluster = "127.0.0.1:2379"
 manager_cluster = "127.0.0.1:2379"
 
-#heartbeat that sync synchronizes client's endpoints with the known endpoints from the etcd membership,unit is second.
-#<=0, use default 30s
+# heartbeat that sync synchronizes client's endpoints with the known endpoints from
+# the etcd membership, unit is second and value must greater then 1s, it is set
+# default 30s if value less then 0
 auto_sync_interval = 30s
 
+# bootstrap time out of registry
+connect_timeout = 10s
+
+# request registry time out
 registry_timeout = 30s
 
 # indicate how many revision you want to keep in etcd
 compact_index_delta = 100
 compact_interval = 12h
 
-# registry cache
+# registry cache, if this option value set 0, service center can run
+# in lower memory but no longer push the events to client.
 enable_cache = 1
 
 # pluggable cipher
@@ -112,7 +118,7 @@ ssl_mode = 0
 ssl_verify_client = 1
 # minimal tls protocol, [TLSv1.0, TLSv1.1, TLSv1.2]
 ssl_protocols = TLSv1.2
-ssl_ciphers = TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_RSA_WITH_AES_256_GCM_SHA384,TLS_RSA_WITH_AES_128_GCM_SHA256
+ssl_ciphers = TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_256_GCM_SHA384,TLS_RSA_WITH_AES_128_GCM_SHA256
 
 ###################################################################
 # log options
diff --git a/examples/service_center/ssl/cert_pwd b/examples/service_center/ssl/cert_pwd
new file mode 100644
index 00000000..0a783b78
--- /dev/null
+++ b/examples/service_center/ssl/cert_pwd
@@ -0,0 +1 @@
+Changeme_123
\ No newline at end of file
diff --git a/examples/service_center/ssl/server.cer b/examples/service_center/ssl/server.cer
new file mode 100644
index 00000000..c29eb6a2
--- /dev/null
+++ b/examples/service_center/ssl/server.cer
@@ -0,0 +1,13 @@
+-----BEGIN CERTIFICATE-----
+MIICATCCAWoCCQDmvlmfua/ThTANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJB
+VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0
+cyBQdHkgTHRkMB4XDTE3MTIwNTA5MzA0NloXDTE4MTIwNTA5MzA0NlowRTELMAkG
+A1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0
+IFdpZGdpdHMgUHR5IEx0ZDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAtu5H
+L8mlCL6n+BrQ71eJgJJJ427/DgovzHBqLTYuk/It987wNJFmTMgdWRXHujwe4bFt
+G78d7OJteXcR7a68sIxWrytWeAwG88M62duS+DCUke1YuQ6hrfIADvE2ZgYEuVxz
+5UxOTYYIjtSxCGKDuxmlvkJgO6lE0zIhtFxz1WcCAwEAATANBgkqhkiG9w0BAQsF
+AAOBgQCq89sMPmhVS5+Mh+FvnNC9qOnsnqWhyAEc5XEmqtCTAe1XpO3CvPH7DdHz
+Ss0FVqpBRqmxUR0sQo6t/S0kW7uwDgjm7nIy67wtTLOLclYW2Yw+d3ApwBVMhVBp
+yhDpV90YZF7QM9uhdsEgLpbTqs4hvPB1pUWH6oXdtjnEkp6lFQ==
+-----END CERTIFICATE-----
diff --git a/examples/service_center/ssl/server_key.pem b/examples/service_center/ssl/server_key.pem
new file mode 100644
index 00000000..c0b907e9
--- /dev/null
+++ b/examples/service_center/ssl/server_key.pem
@@ -0,0 +1,18 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: AES-128-CBC,22C64A026937643A490027AC7931D19A
+
+FdV2dDDn5l767Ulha9IMxiwvyMu6BziFCjXbfsHB1aHdBfuFWV8uJ5lEYiWPWdj3
+QIeK79QH8i6N0n2f1VfXtW7RJKS+6Gg9m1dttxGXY/6sEIkFJRPiWrNrzeHaXAoI
+38CBZli2nvtZMLo8oBj/iuQ0qlkh00Mm2RW1SgJx2I6FJZLS8xb9kKUhPL/SlBYd
+EboWXpKtubg5DhvQd8MKIaTJDf3L1Iqc7OMnLs64ONvfylYy39uv8yGsimHNItQh
+ylHjUhmqOjCwnogO1Nh2wsjQCODRPEJzpu2fJ1lb2+xqwbTg3ygVwzLoK2ScXND4
+DIo8YOa9jBCR9/Yg1IzqOIUD+UzQWhBqDejMHVXAGQb5e2AldLdFnGwx1Yz4FCIl
+nULP8lcMDeYRK0sS4N5/I4nqfvlVPyj96Nj4F3D68Q8oQXl1rTSbKXmWILxIIlHm
+tBf+NA16p9A9TegHr/qn3L3NRCxyY7gLwL+cNG2uZ9NqmxnSmWhL9UQgWYPeGr8Z
+Z9nqqshfzIMoBjZ4QP1pifKhEvb0w2MpjKtiAR3hmwn8SJDp7CPTlb7nm5e5fjZb
+nkzsTXYaJiYYiuEjiIsxJY/6ptWeYq9kqTT/3Hmy0MdkcqeDbcLwqo6jbAJxcPFf
+C0SCJ+woIZJeHpZZjmH/qsZUKq2UEDsLqGruNMDzxarmme67lwPIDG2pZ90EIcNx
+BR0ZU4owv9N+dO2sFsMNHae9X8mHWynUJ2V3ISxshvS3WJmq+YkeYTjYljeIZ28x
++UBhUrmikzx/CrIFd9irnkVhPIqJ+uUZEiMNEnT7VxCpHZHDU+ujweChUzaLkh0x
+-----END RSA PRIVATE KEY-----
diff --git a/examples/service_center/ssl/trust.cer b/examples/service_center/ssl/trust.cer
new file mode 100644
index 00000000..8fb3139b
--- /dev/null
+++ b/examples/service_center/ssl/trust.cer
@@ -0,0 +1,13 @@
+-----BEGIN CERTIFICATE-----
+MIICATCCAWoCCQCu7pAj81WabjANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJB
+VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0
+cyBQdHkgTHRkMB4XDTE3MTIwNTA5MTMzNVoXDTE4MTIwNTA5MTMzNVowRTELMAkG
+A1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0
+IFdpZGdpdHMgUHR5IEx0ZDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAyDpT
+In6x3H0HEGOXzJMByOnvzD4rL0B4wyE9K2Nng0Ev0pS0dlPQK67fZJ6e2KDcA72d
+dSps61YQ6tWnBbssCZat0qI2g3Kh4wKgEoCv/0Wm9J+c9gHO9VXyRd91FkJNFDB4
+Lsh4EF4kMVsdLfvP98LkQQAfjg621Yqa1bEu5RUCAwEAATANBgkqhkiG9w0BAQsF
+AAOBgQCbURtlhZMNUN8W2EQJVqgEbZmtNriI1VpKvfU4b8d05PwaoL3qV8tx6p5/
+2p/+diRH8XWkPMm0Ix+c7752ebWSVb8WoQL40ZBd4PIuy6RlS7/45VeMUk7LvxBG
+iPXnB72OzQmBiPhVNiINVumQWJ62NPlbYaJsG/WsZdaWYMDeww==
+-----END CERTIFICATE-----
diff --git a/frontend/main.go b/frontend/main.go
index 237b9ff5..c460299c 100644
--- a/frontend/main.go
+++ b/frontend/main.go
@@ -23,6 +23,7 @@ import (
 	"github.com/astaxie/beego"
 	"net"
 	"net/url"
+	"strconv"
 )
 
 type Config struct {
@@ -42,8 +43,8 @@ func main() {
 	flag.Parse()
 
 	cfg := Config{}
-	cfg.scAddr = fmt.Sprintf("http://%s/", net.JoinHostPort(url.PathEscape(scIp), fmt.Sprint(scPort)))
-	cfg.frontendAddr = net.JoinHostPort(frontendIp, fmt.Sprint(*port))
+	cfg.scAddr = fmt.Sprintf("http://%s/", net.JoinHostPort(url.PathEscape(scIp), strconv.Itoa(scPort)))
+	cfg.frontendAddr = net.JoinHostPort(frontendIp, strconv.Itoa(*port))
 
 	// run frontend web server
 	Serve(cfg)
diff --git a/integration/admin_test.go b/integration/admin_test.go
new file mode 100644
index 00000000..a8cb8881
--- /dev/null
+++ b/integration/admin_test.go
@@ -0,0 +1,40 @@
+/*
+ * 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 integrationtest_test
+
+import (
+	. "github.com/apache/incubator-servicecomb-service-center/integration"
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+	"github.com/widuu/gojson"
+	"io/ioutil"
+	"net/http"
+)
+
+var _ = Describe("Admin Api Test", func() {
+	It("dump", func() {
+		req, _ := http.NewRequest(GET, SCURL+DUMP, nil)
+		req.Header.Set("X-Domain-Name", "default")
+		resp, err := scclient.Do(req)
+		respbody, _ := ioutil.ReadAll(resp.Body)
+		_, d := gojson.Json(string(respbody)).Getpath("cache", "services").ToArray()
+		Expect(err).To(BeNil())
+		Expect(resp.StatusCode).To(Equal(http.StatusOK))
+		Expect(len(d)).ToNot(Equal(0))
+		defer resp.Body.Close()
+	})
+})
diff --git a/integration/apis.go b/integration/apis.go
index d4ee813b..ef652125 100644
--- a/integration/apis.go
+++ b/integration/apis.go
@@ -66,6 +66,9 @@ var UPDATETAG = "/v4/default/registry/microservices/:serviceId/tags/:key"
 var GETTAGS = "/v4/default/registry/microservices/:serviceId/tags"
 var DELETETAG = "/v4/default/registry/microservices/:serviceId/tags/:key"
 
+//Admin API's
+var DUMP = "/v4/default/admin/dump"
+
 // HTTP METHODS
 var GET = "GET"
 var POST = "POST"
diff --git a/integration/health-metrics-grafana.json b/integration/health-metrics-grafana.json
index 280aec39..fdc80d25 100644
--- a/integration/health-metrics-grafana.json
+++ b/integration/health-metrics-grafana.json
@@ -31,6 +31,12 @@
       "name": "Grafana",
       "version": "5.1.0"
     },
+    {
+      "type": "panel",
+      "id": "grafana-piechart-panel",
+      "name": "Pie Chart",
+      "version": "1.3.3"
+    },
     {
       "type": "panel",
       "id": "graph",
@@ -83,128 +89,31 @@
     }
   ],
   "panels": [
-    {
-      "aliasColors": {},
-      "bars": false,
-      "dashLength": 10,
-      "dashes": false,
-      "datasource": "${DS_LOCAL}",
-      "fill": 1,
-      "gridPos": {
-        "h": 6,
-        "w": 8,
-        "x": 0,
-        "y": 0
-      },
-      "height": "",
-      "id": 1,
-      "legend": {
-        "alignAsTable": true,
-        "avg": true,
-        "current": true,
-        "max": false,
-        "min": false,
-        "show": true,
-        "sort": null,
-        "sortDesc": null,
-        "total": false,
-        "values": true
-      },
-      "lines": true,
-      "linewidth": 1,
-      "links": [],
-      "minSpan": 4,
-      "nullPointMode": "null",
-      "percentage": false,
-      "pointradius": 5,
-      "points": false,
-      "renderer": "flot",
-      "seriesOverrides": [],
-      "spaceLength": 10,
-      "stack": false,
-      "steppedLine": false,
-      "targets": [
-        {
-          "expr": "max(max_over_time(service_center_http_request_durations_microseconds{job=\"service-center\",method=\"GET\"}[1m]))",
-          "format": "time_series",
-          "instant": false,
-          "intervalFactor": 2,
-          "legendFormat": "read",
-          "refId": "A"
-        },
-        {
-          "expr": "max(max_over_time(service_center_http_request_durations_microseconds{job=\"service-center\",method!=\"GET\"}[1m]))",
-          "format": "time_series",
-          "intervalFactor": 2,
-          "legendFormat": "write",
-          "refId": "B"
-        }
-      ],
-      "thresholds": [],
-      "timeFrom": null,
-      "timeShift": null,
-      "title": "I/O Latency",
-      "tooltip": {
-        "shared": true,
-        "sort": 0,
-        "value_type": "individual"
-      },
-      "type": "graph",
-      "xaxis": {
-        "buckets": null,
-        "mode": "time",
-        "name": null,
-        "show": true,
-        "values": []
-      },
-      "yaxes": [
-        {
-          "format": "µs",
-          "label": null,
-          "logBase": 1,
-          "max": null,
-          "min": null,
-          "show": true
-        },
-        {
-          "format": "short",
-          "label": null,
-          "logBase": 1,
-          "max": null,
-          "min": null,
-          "show": true
-        }
-      ],
-      "yaxis": {
-        "align": false,
-        "alignLevel": null
-      }
-    },
     {
       "cacheTimeout": null,
       "colorBackground": false,
-      "colorValue": true,
+      "colorValue": false,
       "colors": [
-        "#d44a3a",
+        "#299c46",
         "rgba(237, 129, 40, 0.89)",
-        "#299c46"
+        "#d44a3a"
       ],
       "datasource": "${DS_LOCAL}",
-      "format": "percentunit",
+      "format": "none",
       "gauge": {
-        "maxValue": 1,
+        "maxValue": 100,
         "minValue": 0,
-        "show": true,
+        "show": false,
         "thresholdLabels": false,
         "thresholdMarkers": true
       },
       "gridPos": {
-        "h": 6,
-        "w": 4,
-        "x": 8,
+        "h": 3,
+        "w": 2,
+        "x": 0,
         "y": 0
       },
-      "id": 12,
+      "id": 27,
       "interval": null,
       "links": [],
       "mappingType": 1,
@@ -237,20 +146,20 @@
         "fillColor": "rgba(31, 118, 189, 0.18)",
         "full": true,
         "lineColor": "rgb(31, 120, 193)",
-        "show": true
+        "show": false
       },
       "tableColumn": "",
       "targets": [
         {
-          "expr": "sum(service_center_http_success_total{job=\"service-center\"})/sum(service_center_http_request_total{job=\"service-center\"})",
+          "expr": "sum(service_center_db_sc_total{job=\"service-center\"})",
           "format": "time_series",
+          "instant": false,
           "intervalFactor": 2,
           "refId": "A"
         }
       ],
-      "thresholds": ".5,.8",
-      "title": "Global Success Rate",
-      "transparent": false,
+      "thresholds": "",
+      "title": "ServiceCenters",
       "type": "singlestat",
       "valueFontSize": "80%",
       "valueMaps": [
@@ -281,12 +190,12 @@
         "thresholdMarkers": true
       },
       "gridPos": {
-        "h": 6,
-        "w": 4,
-        "x": 12,
+        "h": 3,
+        "w": 2,
+        "x": 2,
         "y": 0
       },
-      "id": 13,
+      "id": 19,
       "interval": null,
       "links": [],
       "mappingType": 1,
@@ -319,12 +228,12 @@
         "fillColor": "rgba(31, 118, 189, 0.18)",
         "full": true,
         "lineColor": "rgb(31, 120, 193)",
-        "show": true
+        "show": false
       },
       "tableColumn": "",
       "targets": [
         {
-          "expr": "sum(irate(service_center_http_request_total{job=\"service-center\"}[1m]))",
+          "expr": "max(service_center_db_backend_total{job=\"service-center\"})",
           "format": "time_series",
           "instant": false,
           "intervalFactor": 2,
@@ -332,7 +241,7 @@
         }
       ],
       "thresholds": "",
-      "title": "Global Request Volume",
+      "title": "Backends",
       "type": "singlestat",
       "valueFontSize": "80%",
       "valueMaps": [
@@ -342,7 +251,7 @@
           "value": "null"
         }
       ],
-      "valueName": "avg"
+      "valueName": "current"
     },
     {
       "cacheTimeout": null,
@@ -354,7 +263,7 @@
         "#d44a3a"
       ],
       "datasource": "${DS_LOCAL}",
-      "format": "rps",
+      "format": "none",
       "gauge": {
         "maxValue": 100,
         "minValue": 0,
@@ -363,12 +272,12 @@
         "thresholdMarkers": true
       },
       "gridPos": {
-        "h": 6,
-        "w": 4,
-        "x": 16,
+        "h": 3,
+        "w": 3,
+        "x": 4,
         "y": 0
       },
-      "id": 10,
+      "id": 26,
       "interval": null,
       "links": [],
       "mappingType": 1,
@@ -397,130 +306,1511 @@
           "to": "null"
         }
       ],
-      "sparkline": {
-        "fillColor": "rgba(31, 118, 189, 0.18)",
-        "full": true,
-        "lineColor": "rgb(31, 120, 193)",
-        "show": true
+      "sparkline": {
+        "fillColor": "rgba(31, 118, 189, 0.18)",
+        "full": true,
+        "lineColor": "rgb(31, 120, 193)",
+        "show": false
+      },
+      "tableColumn": "",
+      "targets": [
+        {
+          "expr": "max(service_center_db_domain_total{job=\"service-center\"})",
+          "format": "time_series",
+          "instant": false,
+          "intervalFactor": 2,
+          "refId": "A"
+        }
+      ],
+      "thresholds": "",
+      "title": "Domain Volume",
+      "type": "singlestat",
+      "valueFontSize": "80%",
+      "valueMaps": [
+        {
+          "op": "=",
+          "text": "N/A",
+          "value": "null"
+        }
+      ],
+      "valueName": "current"
+    },
+    {
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": "${DS_LOCAL}",
+      "fill": 1,
+      "gridPos": {
+        "h": 5,
+        "w": 8,
+        "x": 7,
+        "y": 0
+      },
+      "height": "",
+      "id": 22,
+      "legend": {
+        "alignAsTable": true,
+        "avg": true,
+        "current": true,
+        "max": true,
+        "min": true,
+        "rightSide": false,
+        "show": true,
+        "sortDesc": true,
+        "total": false,
+        "values": true
+      },
+      "lines": true,
+      "linewidth": 1,
+      "links": [],
+      "minSpan": 4,
+      "nullPointMode": "null",
+      "percentage": false,
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": false,
+      "targets": [
+        {
+          "expr": "max(service_center_db_service_total{job=\"service-center\"})",
+          "format": "time_series",
+          "instant": false,
+          "intervalFactor": 2,
+          "legendFormat": "instances",
+          "refId": "A"
+        }
+      ],
+      "thresholds": [],
+      "timeFrom": null,
+      "timeShift": null,
+      "title": "Microservices",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "buckets": null,
+        "mode": "time",
+        "name": null,
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "locale",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        },
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        }
+      ],
+      "yaxis": {
+        "align": false,
+        "alignLevel": null
+      }
+    },
+    {
+      "aliasColors": {},
+      "breakPoint": "50%",
+      "cacheTimeout": null,
+      "combine": {
+        "label": "Others",
+        "threshold": 0
+      },
+      "datasource": "${DS_LOCAL}",
+      "decimals": null,
+      "fontSize": "80%",
+      "format": "short",
+      "gridPos": {
+        "h": 5,
+        "w": 9,
+        "x": 15,
+        "y": 0
+      },
+      "id": 25,
+      "interval": null,
+      "legend": {
+        "header": "",
+        "percentage": true,
+        "percentageDecimals": 1,
+        "show": true,
+        "sideWidth": null,
+        "sort": "current",
+        "sortDesc": true,
+        "values": true
+      },
+      "legendType": "Right side",
+      "links": [],
+      "maxDataPoints": 3,
+      "nullPointMode": "connected",
+      "pieType": "pie",
+      "strokeWidth": 1,
+      "targets": [
+        {
+          "expr": "max(service_center_db_service_total{job=\"service-center\"}) by (framework)",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "{{framework}}",
+          "refId": "A"
+        }
+      ],
+      "title": "Frameworks",
+      "type": "grafana-piechart-panel",
+      "valueName": "current"
+    },
+    {
+      "cacheTimeout": null,
+      "colorBackground": false,
+      "colorValue": false,
+      "colors": [
+        "#299c46",
+        "rgba(237, 129, 40, 0.89)",
+        "#d44a3a"
+      ],
+      "datasource": "${DS_LOCAL}",
+      "format": "none",
+      "gauge": {
+        "maxValue": 100,
+        "minValue": 0,
+        "show": false,
+        "thresholdLabels": false,
+        "thresholdMarkers": true
+      },
+      "gridPos": {
+        "h": 3,
+        "w": 3,
+        "x": 4,
+        "y": 3
+      },
+      "id": 20,
+      "interval": null,
+      "links": [],
+      "mappingType": 1,
+      "mappingTypes": [
+        {
+          "name": "value to text",
+          "value": 1
+        },
+        {
+          "name": "range to text",
+          "value": 2
+        }
+      ],
+      "maxDataPoints": 100,
+      "minSpan": 4,
+      "nullPointMode": "connected",
+      "nullText": null,
+      "postfix": "",
+      "postfixFontSize": "50%",
+      "prefix": "",
+      "prefixFontSize": "50%",
+      "rangeMaps": [
+        {
+          "from": "null",
+          "text": "N/A",
+          "to": "null"
+        }
+      ],
+      "sparkline": {
+        "fillColor": "rgba(31, 118, 189, 0.18)",
+        "full": true,
+        "lineColor": "rgb(31, 120, 193)",
+        "show": false
+      },
+      "tableColumn": "",
+      "targets": [
+        {
+          "expr": "max(sum(service_center_db_service_total{job=\"service-center\"}) by (instance))",
+          "format": "time_series",
+          "instant": false,
+          "intervalFactor": 2,
+          "refId": "A"
+        }
+      ],
+      "thresholds": "",
+      "title": "Microservice Volume",
+      "type": "singlestat",
+      "valueFontSize": "80%",
+      "valueMaps": [
+        {
+          "op": "=",
+          "text": "N/A",
+          "value": "null"
+        }
+      ],
+      "valueName": "current"
+    },
+    {
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": "${DS_LOCAL}",
+      "fill": 1,
+      "gridPos": {
+        "h": 5,
+        "w": 8,
+        "x": 7,
+        "y": 5
+      },
+      "height": "",
+      "id": 29,
+      "legend": {
+        "alignAsTable": true,
+        "avg": true,
+        "current": true,
+        "hideEmpty": false,
+        "hideZero": false,
+        "max": true,
+        "min": true,
+        "rightSide": false,
+        "show": true,
+        "sortDesc": true,
+        "total": false,
+        "values": true
+      },
+      "lines": true,
+      "linewidth": 1,
+      "links": [],
+      "minSpan": 4,
+      "nullPointMode": "null",
+      "percentage": false,
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": false,
+      "targets": [
+        {
+          "expr": "max(service_center_db_instance_total{job=\"service-center\"})",
+          "format": "time_series",
+          "instant": false,
+          "intervalFactor": 2,
+          "legendFormat": "instances",
+          "refId": "A"
+        }
+      ],
+      "thresholds": [],
+      "timeFrom": null,
+      "timeShift": null,
+      "title": "Instances",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "buckets": null,
+        "mode": "time",
+        "name": null,
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "locale",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        },
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        }
+      ],
+      "yaxis": {
+        "align": false,
+        "alignLevel": null
+      }
+    },
+    {
+      "aliasColors": {},
+      "breakPoint": "50%",
+      "cacheTimeout": null,
+      "combine": {
+        "label": "Others",
+        "threshold": 0
+      },
+      "datasource": "${DS_LOCAL}",
+      "decimals": null,
+      "fontSize": "80%",
+      "format": "short",
+      "gridPos": {
+        "h": 5,
+        "w": 3,
+        "x": 15,
+        "y": 5
+      },
+      "id": 31,
+      "interval": null,
+      "legend": {
+        "header": "",
+        "percentage": true,
+        "percentageDecimals": 1,
+        "show": false,
+        "sideWidth": null,
+        "sort": "current",
+        "sortDesc": true,
+        "values": false
+      },
+      "legendType": "On graph",
+      "links": [],
+      "maxDataPoints": 3,
+      "nullPointMode": "connected",
+      "pieType": "pie",
+      "strokeWidth": 1,
+      "targets": [
+        {
+          "expr": "max(service_center_db_service_total{job=\"service-center\", framework=\"servicecomb-java-chassis\"}) by (frameworkVersion)",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "{{frameworkVersion}}",
+          "refId": "A"
+        }
+      ],
+      "title": "Java Chassis Versions",
+      "type": "grafana-piechart-panel",
+      "valueName": "current"
+    },
+    {
+      "aliasColors": {},
+      "breakPoint": "50%",
+      "cacheTimeout": null,
+      "combine": {
+        "label": "Others",
+        "threshold": 0
+      },
+      "datasource": "${DS_LOCAL}",
+      "decimals": null,
+      "fontSize": "80%",
+      "format": "short",
+      "gridPos": {
+        "h": 5,
+        "w": 3,
+        "x": 18,
+        "y": 5
+      },
+      "id": 32,
+      "interval": null,
+      "legend": {
+        "header": "",
+        "percentage": true,
+        "percentageDecimals": 1,
+        "show": false,
+        "sideWidth": null,
+        "sort": "current",
+        "sortDesc": true,
+        "values": false
+      },
+      "legendType": "On graph",
+      "links": [],
+      "maxDataPoints": 3,
+      "nullPointMode": "connected",
+      "pieType": "pie",
+      "strokeWidth": 1,
+      "targets": [
+        {
+          "expr": "max(service_center_db_service_total{job=\"service-center\", framework=\"Go-Chassis\"}) by (frameworkVersion)",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "{{frameworkVersion}}",
+          "refId": "A"
+        }
+      ],
+      "title": "Go Chassis Versions",
+      "type": "grafana-piechart-panel",
+      "valueName": "current"
+    },
+    {
+      "aliasColors": {},
+      "breakPoint": "50%",
+      "cacheTimeout": null,
+      "combine": {
+        "label": "Others",
+        "threshold": 0
+      },
+      "datasource": "${DS_LOCAL}",
+      "decimals": null,
+      "fontSize": "80%",
+      "format": "short",
+      "gridPos": {
+        "h": 5,
+        "w": 3,
+        "x": 21,
+        "y": 5
+      },
+      "id": 33,
+      "interval": null,
+      "legend": {
+        "header": "",
+        "percentage": true,
+        "percentageDecimals": 1,
+        "show": false,
+        "sideWidth": null,
+        "sort": "current",
+        "sortDesc": true,
+        "values": false
+      },
+      "legendType": "On graph",
+      "links": [],
+      "maxDataPoints": 3,
+      "nullPointMode": "connected",
+      "pieType": "pie",
+      "strokeWidth": 1,
+      "targets": [
+        {
+          "expr": "max(service_center_db_service_total{job=\"service-center\", framework=\"Mesher\"}) by (frameworkVersion)",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "{{frameworkVersion}}",
+          "refId": "A"
+        }
+      ],
+      "title": "Mesher Versions",
+      "type": "grafana-piechart-panel",
+      "valueName": "current"
+    },
+    {
+      "cacheTimeout": null,
+      "colorBackground": false,
+      "colorValue": false,
+      "colors": [
+        "#299c46",
+        "rgba(237, 129, 40, 0.89)",
+        "#d44a3a"
+      ],
+      "datasource": "${DS_LOCAL}",
+      "format": "none",
+      "gauge": {
+        "maxValue": 100,
+        "minValue": 0,
+        "show": false,
+        "thresholdLabels": false,
+        "thresholdMarkers": true
+      },
+      "gridPos": {
+        "h": 3,
+        "w": 3,
+        "x": 4,
+        "y": 6
+      },
+      "id": 21,
+      "interval": null,
+      "links": [],
+      "mappingType": 1,
+      "mappingTypes": [
+        {
+          "name": "value to text",
+          "value": 1
+        },
+        {
+          "name": "range to text",
+          "value": 2
+        }
+      ],
+      "maxDataPoints": 100,
+      "minSpan": 4,
+      "nullPointMode": "connected",
+      "nullText": null,
+      "postfix": "",
+      "postfixFontSize": "50%",
+      "prefix": "",
+      "prefixFontSize": "50%",
+      "rangeMaps": [
+        {
+          "from": "null",
+          "text": "N/A",
+          "to": "null"
+        }
+      ],
+      "sparkline": {
+        "fillColor": "rgba(31, 118, 189, 0.18)",
+        "full": true,
+        "lineColor": "rgb(31, 120, 193)",
+        "show": false
+      },
+      "tableColumn": "Value",
+      "targets": [
+        {
+          "expr": "max(service_center_db_instance_total{job=\"service-center\"})",
+          "format": "time_series",
+          "instant": false,
+          "intervalFactor": 2,
+          "refId": "A"
+        }
+      ],
+      "thresholds": "",
+      "title": "Instance Volume",
+      "type": "singlestat",
+      "valueFontSize": "80%",
+      "valueMaps": [
+        {
+          "op": "=",
+          "text": "N/A",
+          "value": "null"
+        }
+      ],
+      "valueName": "current"
+    },
+    {
+      "content": "<div class=\"text-center dashboard-header\">\n  <span>PERFORMANCE</span>\n</div>\n",
+      "gridPos": {
+        "h": 3,
+        "w": 24,
+        "x": 0,
+        "y": 10
+      },
+      "height": "1px",
+      "id": 3,
+      "links": [],
+      "mode": "html",
+      "repeat": null,
+      "title": "",
+      "transparent": true,
+      "type": "text"
+    },
+    {
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": "${DS_LOCAL}",
+      "fill": 1,
+      "gridPos": {
+        "h": 6,
+        "w": 8,
+        "x": 0,
+        "y": 13
+      },
+      "height": "",
+      "id": 1,
+      "legend": {
+        "alignAsTable": true,
+        "avg": true,
+        "current": true,
+        "max": true,
+        "min": true,
+        "show": true,
+        "sort": null,
+        "sortDesc": null,
+        "total": false,
+        "values": true
+      },
+      "lines": true,
+      "linewidth": 1,
+      "links": [],
+      "minSpan": 4,
+      "nullPointMode": "null",
+      "percentage": false,
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": false,
+      "targets": [
+        {
+          "expr": "sum(irate(service_center_http_request_total{job=\"service-center\"}[1m]))",
+          "format": "time_series",
+          "instant": false,
+          "intervalFactor": 2,
+          "legendFormat": "tps",
+          "refId": "A"
+        }
+      ],
+      "thresholds": [],
+      "timeFrom": null,
+      "timeShift": null,
+      "title": "TPS",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "buckets": null,
+        "mode": "time",
+        "name": null,
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "ops",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        },
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        }
+      ],
+      "yaxis": {
+        "align": false,
+        "alignLevel": null
+      }
+    },
+    {
+      "cacheTimeout": null,
+      "colorBackground": false,
+      "colorValue": false,
+      "colors": [
+        "#299c46",
+        "rgba(237, 129, 40, 0.89)",
+        "#d44a3a"
+      ],
+      "datasource": "${DS_LOCAL}",
+      "format": "none",
+      "gauge": {
+        "maxValue": 100,
+        "minValue": 0,
+        "show": false,
+        "thresholdLabels": false,
+        "thresholdMarkers": true
+      },
+      "gridPos": {
+        "h": 6,
+        "w": 4,
+        "x": 8,
+        "y": 13
+      },
+      "id": 13,
+      "interval": null,
+      "links": [],
+      "mappingType": 1,
+      "mappingTypes": [
+        {
+          "name": "value to text",
+          "value": 1
+        },
+        {
+          "name": "range to text",
+          "value": 2
+        }
+      ],
+      "maxDataPoints": 100,
+      "minSpan": 4,
+      "nullPointMode": "connected",
+      "nullText": null,
+      "postfix": "",
+      "postfixFontSize": "50%",
+      "prefix": "",
+      "prefixFontSize": "50%",
+      "rangeMaps": [
+        {
+          "from": "null",
+          "text": "N/A",
+          "to": "null"
+        }
+      ],
+      "sparkline": {
+        "fillColor": "rgba(31, 118, 189, 0.18)",
+        "full": true,
+        "lineColor": "rgb(31, 120, 193)",
+        "show": true
+      },
+      "tableColumn": "",
+      "targets": [
+        {
+          "expr": "sum(irate(service_center_http_request_total{job=\"service-center\"}[1m]))",
+          "format": "time_series",
+          "instant": false,
+          "intervalFactor": 2,
+          "refId": "A"
+        }
+      ],
+      "thresholds": "",
+      "title": "Global Request Volume",
+      "type": "singlestat",
+      "valueFontSize": "80%",
+      "valueMaps": [
+        {
+          "op": "=",
+          "text": "N/A",
+          "value": "null"
+        }
+      ],
+      "valueName": "current"
+    },
+    {
+      "cacheTimeout": null,
+      "colorBackground": false,
+      "colorValue": true,
+      "colors": [
+        "#d44a3a",
+        "rgba(237, 129, 40, 0.89)",
+        "#299c46"
+      ],
+      "datasource": "${DS_LOCAL}",
+      "format": "percentunit",
+      "gauge": {
+        "maxValue": 1,
+        "minValue": 0,
+        "show": true,
+        "thresholdLabels": false,
+        "thresholdMarkers": true
+      },
+      "gridPos": {
+        "h": 6,
+        "w": 4,
+        "x": 12,
+        "y": 13
+      },
+      "id": 12,
+      "interval": null,
+      "links": [],
+      "mappingType": 1,
+      "mappingTypes": [
+        {
+          "name": "value to text",
+          "value": 1
+        },
+        {
+          "name": "range to text",
+          "value": 2
+        }
+      ],
+      "maxDataPoints": 100,
+      "minSpan": 4,
+      "nullPointMode": "connected",
+      "nullText": null,
+      "postfix": "",
+      "postfixFontSize": "50%",
+      "prefix": "",
+      "prefixFontSize": "50%",
+      "rangeMaps": [
+        {
+          "from": "null",
+          "text": "N/A",
+          "to": "null"
+        }
+      ],
+      "sparkline": {
+        "fillColor": "rgba(31, 118, 189, 0.18)",
+        "full": true,
+        "lineColor": "rgb(31, 120, 193)",
+        "show": true
+      },
+      "tableColumn": "",
+      "targets": [
+        {
+          "expr": "sum(service_center_http_success_total{job=\"service-center\"})/sum(service_center_http_request_total{job=\"service-center\"})",
+          "format": "time_series",
+          "intervalFactor": 2,
+          "refId": "A"
+        }
+      ],
+      "thresholds": ".5,.8",
+      "title": "Global Success Rate",
+      "transparent": false,
+      "type": "singlestat",
+      "valueFontSize": "80%",
+      "valueMaps": [
+        {
+          "op": "=",
+          "text": "N/A",
+          "value": "null"
+        }
+      ],
+      "valueName": "current"
+    },
+    {
+      "cacheTimeout": null,
+      "colorBackground": false,
+      "colorValue": false,
+      "colors": [
+        "#299c46",
+        "rgba(237, 129, 40, 0.89)",
+        "#d44a3a"
+      ],
+      "datasource": "${DS_LOCAL}",
+      "format": "ops",
+      "gauge": {
+        "maxValue": 100,
+        "minValue": 0,
+        "show": false,
+        "thresholdLabels": false,
+        "thresholdMarkers": true
+      },
+      "gridPos": {
+        "h": 6,
+        "w": 4,
+        "x": 16,
+        "y": 13
+      },
+      "id": 10,
+      "interval": null,
+      "links": [],
+      "mappingType": 1,
+      "mappingTypes": [
+        {
+          "name": "value to text",
+          "value": 1
+        },
+        {
+          "name": "range to text",
+          "value": 2
+        }
+      ],
+      "maxDataPoints": 100,
+      "minSpan": 4,
+      "nullPointMode": "connected",
+      "nullText": null,
+      "postfix": "",
+      "postfixFontSize": "50%",
+      "prefix": "",
+      "prefixFontSize": "50%",
+      "rangeMaps": [
+        {
+          "from": "null",
+          "text": "N/A",
+          "to": "null"
+        }
+      ],
+      "sparkline": {
+        "fillColor": "rgba(31, 118, 189, 0.18)",
+        "full": true,
+        "lineColor": "rgb(31, 120, 193)",
+        "show": true
+      },
+      "tableColumn": "",
+      "targets": [
+        {
+          "expr": "sum(irate(service_center_http_request_total{job=\"service-center\",code=~\"4.+\"}[1m]))",
+          "format": "time_series",
+          "instant": false,
+          "intervalFactor": 2,
+          "legendFormat": "",
+          "refId": "A"
+        }
+      ],
+      "thresholds": "",
+      "title": "4XX Errors",
+      "type": "singlestat",
+      "valueFontSize": "80%",
+      "valueMaps": [
+        {
+          "op": "=",
+          "text": "N/A",
+          "value": "null"
+        }
+      ],
+      "valueName": "avg"
+    },
+    {
+      "cacheTimeout": null,
+      "colorBackground": false,
+      "colorValue": false,
+      "colors": [
+        "#299c46",
+        "rgba(237, 129, 40, 0.89)",
+        "#d44a3a"
+      ],
+      "datasource": "${DS_LOCAL}",
+      "format": "ops",
+      "gauge": {
+        "maxValue": 100,
+        "minValue": 0,
+        "show": false,
+        "thresholdLabels": false,
+        "thresholdMarkers": true
+      },
+      "gridPos": {
+        "h": 6,
+        "w": 4,
+        "x": 20,
+        "y": 13
+      },
+      "id": 11,
+      "interval": null,
+      "links": [],
+      "mappingType": 1,
+      "mappingTypes": [
+        {
+          "name": "value to text",
+          "value": 1
+        },
+        {
+          "name": "range to text",
+          "value": 2
+        }
+      ],
+      "maxDataPoints": 100,
+      "minSpan": 4,
+      "nullPointMode": "connected",
+      "nullText": null,
+      "postfix": "",
+      "postfixFontSize": "50%",
+      "prefix": "",
+      "prefixFontSize": "50%",
+      "rangeMaps": [
+        {
+          "from": "null",
+          "text": "N/A",
+          "to": "null"
+        }
+      ],
+      "sparkline": {
+        "fillColor": "rgba(31, 118, 189, 0.18)",
+        "full": true,
+        "lineColor": "rgb(31, 120, 193)",
+        "show": true
+      },
+      "tableColumn": "",
+      "targets": [
+        {
+          "expr": "sum(irate(service_center_http_request_total{job=\"service-center\",code=~\"5.+\"}[1m]))",
+          "format": "time_series",
+          "intervalFactor": 2,
+          "refId": "A"
+        }
+      ],
+      "thresholds": "",
+      "title": "5XX Errors",
+      "type": "singlestat",
+      "valueFontSize": "80%",
+      "valueMaps": [
+        {
+          "op": "=",
+          "text": "N/A",
+          "value": "null"
+        }
+      ],
+      "valueName": "avg"
+    },
+    {
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": "${DS_LOCAL}",
+      "fill": 1,
+      "gridPos": {
+        "h": 6,
+        "w": 8,
+        "x": 0,
+        "y": 19
+      },
+      "height": "",
+      "id": 28,
+      "legend": {
+        "alignAsTable": true,
+        "avg": true,
+        "current": true,
+        "max": true,
+        "min": true,
+        "show": true,
+        "sort": null,
+        "sortDesc": null,
+        "total": false,
+        "values": true
+      },
+      "lines": true,
+      "linewidth": 1,
+      "links": [],
+      "minSpan": 4,
+      "nullPointMode": "null",
+      "percentage": false,
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": false,
+      "targets": [
+        {
+          "expr": "sum(service_center_http_success_total{job=\"service-center\"})/sum(service_center_http_request_total{job=\"service-center\"})",
+          "format": "time_series",
+          "instant": false,
+          "intervalFactor": 2,
+          "legendFormat": "rate",
+          "refId": "A"
+        }
+      ],
+      "thresholds": [],
+      "timeFrom": null,
+      "timeShift": null,
+      "title": "Success Rate",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "buckets": null,
+        "mode": "time",
+        "name": null,
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "percentunit",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        },
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        }
+      ],
+      "yaxis": {
+        "align": false,
+        "alignLevel": null
+      }
+    },
+    {
+      "content": "<div class=\"text-center dashboard-header\">\n  <span>INSTANCE METRICS</span>\n</div>\n",
+      "gridPos": {
+        "h": 3,
+        "w": 24,
+        "x": 0,
+        "y": 25
+      },
+      "height": "1px",
+      "id": 30,
+      "links": [],
+      "mode": "html",
+      "title": "",
+      "transparent": true,
+      "type": "text"
+    },
+    {
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": "${DS_LOCAL}",
+      "fill": 1,
+      "gridPos": {
+        "h": 6,
+        "w": 6,
+        "x": 0,
+        "y": 28
+      },
+      "height": "",
+      "id": 18,
+      "legend": {
+        "alignAsTable": false,
+        "avg": false,
+        "current": false,
+        "hideEmpty": true,
+        "hideZero": true,
+        "max": false,
+        "min": false,
+        "rightSide": false,
+        "show": false,
+        "sort": "avg",
+        "sortDesc": true,
+        "total": false,
+        "values": false
+      },
+      "lines": true,
+      "linewidth": 1,
+      "links": [],
+      "minSpan": 4,
+      "nullPointMode": "null",
+      "percentage": false,
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": false,
+      "targets": [
+        {
+          "expr": "sum(service_center_http_request_total{job=\"service-center\"}) by (method,api,instance)",
+          "format": "time_series",
+          "instant": false,
+          "intervalFactor": 2,
+          "legendFormat": "{{instance}}> {{method}} {{api}}",
+          "refId": "A"
+        },
+        {
+          "expr": "sum(service_center_http_request_total{job=\"service-center\"})",
+          "format": "time_series",
+          "intervalFactor": 1,
+          "legendFormat": "{{instance}}> Total",
+          "refId": "B"
+        }
+      ],
+      "thresholds": [],
+      "timeFrom": null,
+      "timeShift": null,
+      "title": "Request Total",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "transparent": false,
+      "type": "graph",
+      "xaxis": {
+        "buckets": null,
+        "mode": "time",
+        "name": null,
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "ops",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        },
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        }
+      ],
+      "yaxis": {
+        "align": false,
+        "alignLevel": null
+      }
+    },
+    {
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": "${DS_LOCAL}",
+      "fill": 1,
+      "gridPos": {
+        "h": 6,
+        "w": 6,
+        "x": 6,
+        "y": 28
+      },
+      "height": "",
+      "id": 17,
+      "legend": {
+        "alignAsTable": true,
+        "avg": true,
+        "current": true,
+        "hideEmpty": true,
+        "hideZero": true,
+        "max": true,
+        "min": true,
+        "show": false,
+        "sort": null,
+        "sortDesc": null,
+        "total": false,
+        "values": true
+      },
+      "lines": true,
+      "linewidth": 1,
+      "links": [],
+      "minSpan": 4,
+      "nullPointMode": "null",
+      "percentage": false,
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": false,
+      "targets": [
+        {
+          "expr": "sum(irate(service_center_http_request_total{job=\"service-center\"}[1m])) by (method,api,instance)",
+          "format": "time_series",
+          "instant": false,
+          "intervalFactor": 2,
+          "legendFormat": "{{instance}}> {{method}} {{api}}",
+          "refId": "A"
+        }
+      ],
+      "thresholds": [],
+      "timeFrom": null,
+      "timeShift": null,
+      "title": "TPS of APIs",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "type": "graph",
+      "xaxis": {
+        "buckets": null,
+        "mode": "time",
+        "name": null,
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
+        {
+          "format": "ops",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        },
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        }
+      ],
+      "yaxis": {
+        "align": false,
+        "alignLevel": null
+      }
+    },
+    {
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
+      "datasource": "${DS_LOCAL}",
+      "fill": 1,
+      "gridPos": {
+        "h": 6,
+        "w": 6,
+        "x": 12,
+        "y": 28
       },
-      "tableColumn": "",
+      "height": "",
+      "id": 15,
+      "legend": {
+        "alignAsTable": false,
+        "avg": true,
+        "current": false,
+        "hideEmpty": true,
+        "hideZero": true,
+        "max": true,
+        "min": true,
+        "rightSide": true,
+        "show": false,
+        "sort": "avg",
+        "sortDesc": true,
+        "total": false,
+        "values": true
+      },
+      "lines": true,
+      "linewidth": 1,
+      "links": [],
+      "minSpan": 4,
+      "nullPointMode": "null",
+      "percentage": false,
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": false,
       "targets": [
         {
-          "expr": "sum(irate(service_center_http_request_total{job=\"service-center\",code=~\"4.+\"}[1m]))",
+          "expr": "max(avg_over_time(service_center_http_request_durations_microseconds{job=\"service-center\",method=\"GET\"}[1m])) by (method,api,instance)",
           "format": "time_series",
           "instant": false,
           "intervalFactor": 2,
-          "legendFormat": "",
+          "legendFormat": "{{instance}}> {{method}} {{api}}",
           "refId": "A"
         }
       ],
-      "thresholds": "",
-      "title": "4XX Errors",
-      "type": "singlestat",
-      "valueFontSize": "80%",
-      "valueMaps": [
+      "thresholds": [],
+      "timeFrom": null,
+      "timeShift": null,
+      "title": "Read Latency",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "transparent": false,
+      "type": "graph",
+      "xaxis": {
+        "buckets": null,
+        "mode": "time",
+        "name": null,
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
         {
-          "op": "=",
-          "text": "N/A",
-          "value": "null"
+          "format": "µs",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        },
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
         }
       ],
-      "valueName": "avg"
+      "yaxis": {
+        "align": false,
+        "alignLevel": null
+      }
     },
     {
-      "cacheTimeout": null,
-      "colorBackground": false,
-      "colorValue": false,
-      "colors": [
-        "#299c46",
-        "rgba(237, 129, 40, 0.89)",
-        "#d44a3a"
-      ],
+      "aliasColors": {},
+      "bars": false,
+      "dashLength": 10,
+      "dashes": false,
       "datasource": "${DS_LOCAL}",
-      "format": "rps",
-      "gauge": {
-        "maxValue": 100,
-        "minValue": 0,
-        "show": false,
-        "thresholdLabels": false,
-        "thresholdMarkers": true
-      },
+      "fill": 1,
       "gridPos": {
         "h": 6,
-        "w": 4,
-        "x": 20,
-        "y": 0
+        "w": 6,
+        "x": 18,
+        "y": 28
       },
-      "id": 11,
-      "interval": null,
+      "height": "",
+      "id": 16,
+      "legend": {
+        "alignAsTable": false,
+        "avg": true,
+        "current": false,
+        "hideEmpty": true,
+        "hideZero": true,
+        "max": true,
+        "min": true,
+        "rightSide": true,
+        "show": false,
+        "sort": "avg",
+        "sortDesc": true,
+        "total": false,
+        "values": true
+      },
+      "lines": true,
+      "linewidth": 1,
       "links": [],
-      "mappingType": 1,
-      "mappingTypes": [
-        {
-          "name": "value to text",
-          "value": 1
-        },
-        {
-          "name": "range to text",
-          "value": 2
-        }
-      ],
-      "maxDataPoints": 100,
       "minSpan": 4,
-      "nullPointMode": "connected",
-      "nullText": null,
-      "postfix": "",
-      "postfixFontSize": "50%",
-      "prefix": "",
-      "prefixFontSize": "50%",
-      "rangeMaps": [
-        {
-          "from": "null",
-          "text": "N/A",
-          "to": "null"
-        }
-      ],
-      "sparkline": {
-        "fillColor": "rgba(31, 118, 189, 0.18)",
-        "full": true,
-        "lineColor": "rgb(31, 120, 193)",
-        "show": true
-      },
-      "tableColumn": "",
+      "nullPointMode": "null",
+      "percentage": false,
+      "pointradius": 5,
+      "points": false,
+      "renderer": "flot",
+      "seriesOverrides": [],
+      "spaceLength": 10,
+      "stack": false,
+      "steppedLine": false,
       "targets": [
         {
-          "expr": "sum(irate(service_center_http_request_total{job=\"service-center\",code=~\"5.+\"}[1m]))",
+          "expr": "max(avg_over_time(service_center_http_request_durations_microseconds{job=\"service-center\",method!=\"GET\"}[1m])) by (method,api,instance)",
           "format": "time_series",
+          "instant": false,
           "intervalFactor": 2,
+          "legendFormat": "{{instance}}> {{method}} {{api}}",
           "refId": "A"
         }
       ],
-      "thresholds": "",
-      "title": "5XX Errors",
-      "type": "singlestat",
-      "valueFontSize": "80%",
-      "valueMaps": [
+      "thresholds": [],
+      "timeFrom": null,
+      "timeShift": null,
+      "title": "Write Latency",
+      "tooltip": {
+        "shared": true,
+        "sort": 0,
+        "value_type": "individual"
+      },
+      "transparent": false,
+      "type": "graph",
+      "xaxis": {
+        "buckets": null,
+        "mode": "time",
+        "name": null,
+        "show": true,
+        "values": []
+      },
+      "yaxes": [
         {
-          "op": "=",
-          "text": "N/A",
-          "value": "null"
+          "format": "µs",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
+        },
+        {
+          "format": "short",
+          "label": null,
+          "logBase": 1,
+          "max": null,
+          "min": null,
+          "show": true
         }
       ],
-      "valueName": "avg"
+      "yaxis": {
+        "align": false,
+        "alignLevel": null
+      }
     },
     {
-      "content": "<div class=\"text-center dashboard-header\">\n  <span>INSTANCE METRICS</span>\n</div>\n",
+      "content": "<div class=\"text-center dashboard-header\">\n  <span>RESOURCES</span>\n</div>\n",
       "gridPos": {
         "h": 3,
         "w": 24,
         "x": 0,
-        "y": 6
+        "y": 34
       },
       "height": "1px",
-      "id": 3,
+      "id": 4,
       "links": [],
       "mode": "html",
-      "repeat": null,
       "title": "",
       "transparent": true,
       "type": "text"
@@ -536,7 +1826,7 @@
         "h": 7,
         "w": 8,
         "x": 0,
-        "y": 9
+        "y": 37
       },
       "height": "",
       "id": 2,
@@ -570,7 +1860,7 @@
           "format": "time_series",
           "instant": false,
           "intervalFactor": 2,
-          "legendFormat": "goroutines_{{instance}}",
+          "legendFormat": "{{instance}}",
           "refId": "A"
         }
       ],
@@ -625,7 +1915,7 @@
         "h": 7,
         "w": 8,
         "x": 8,
-        "y": 9
+        "y": 37
       },
       "height": "",
       "id": 9,
@@ -657,7 +1947,7 @@
           "format": "time_series",
           "instant": false,
           "intervalFactor": 2,
-          "legendFormat": "cpu_{{instance}}",
+          "legendFormat": "{{instance}}",
           "refId": "A"
         }
       ],
@@ -712,7 +2002,7 @@
         "h": 7,
         "w": 8,
         "x": 16,
-        "y": 9
+        "y": 37
       },
       "height": "",
       "id": 8,
@@ -740,11 +2030,11 @@
       "steppedLine": false,
       "targets": [
         {
-          "expr": "max(max_over_time(go_gc_duration_seconds{job=\"service-center\"}[1m])) by (instance)",
+          "expr": "max(avg_over_time(go_gc_duration_seconds{job=\"service-center\"}[1m])) by (instance)",
           "format": "time_series",
           "instant": false,
           "intervalFactor": 2,
-          "legendFormat": "gc_{{instance}}",
+          "legendFormat": "{{instance}}",
           "refId": "A"
         }
       ],
@@ -788,22 +2078,6 @@
         "alignLevel": null
       }
     },
-    {
-      "content": "<div class=\"text-center dashboard-header\">\n  <span>RESOURCES</span>\n</div>\n",
-      "gridPos": {
-        "h": 3,
-        "w": 24,
-        "x": 0,
-        "y": 16
-      },
-      "height": "1px",
-      "id": 4,
-      "links": [],
-      "mode": "html",
-      "title": "",
-      "transparent": true,
-      "type": "text"
-    },
     {
       "aliasColors": {},
       "bars": false,
@@ -815,7 +2089,7 @@
         "h": 7,
         "w": 8,
         "x": 0,
-        "y": 19
+        "y": 44
       },
       "id": 5,
       "legend": {
@@ -845,7 +2119,7 @@
           "expr": "go_threads{job=\"service-center\"}",
           "format": "time_series",
           "intervalFactor": 2,
-          "legendFormat": "threads_{{instance}}",
+          "legendFormat": "{{instance}}",
           "refId": "A"
         }
       ],
@@ -900,7 +2174,7 @@
         "h": 7,
         "w": 8,
         "x": 8,
-        "y": 19
+        "y": 44
       },
       "id": 6,
       "legend": {
@@ -930,7 +2204,7 @@
           "expr": "process_open_fds{job=\"service-center\"}",
           "format": "time_series",
           "intervalFactor": 2,
-          "legendFormat": "fds_{{instance}}",
+          "legendFormat": "{{instance}}",
           "refId": "A"
         }
       ],
@@ -985,7 +2259,7 @@
         "h": 7,
         "w": 8,
         "x": 16,
-        "y": 19
+        "y": 44
       },
       "id": 14,
       "legend": {
@@ -1011,25 +2285,11 @@
       "stack": false,
       "steppedLine": false,
       "targets": [
-        {
-          "expr": "go_memstats_heap_inuse_bytes{job=\"service-center\"} + go_memstats_heap_idle_bytes{job=\"service-center\"}",
-          "format": "time_series",
-          "intervalFactor": 1,
-          "legendFormat": "heap_{{instance}}",
-          "refId": "B"
-        },
-        {
-          "expr": "go_memstats_heap_inuse_bytes{job=\"service-center\"}",
-          "format": "time_series",
-          "intervalFactor": 1,
-          "legendFormat": "inuse_{{instance}}",
-          "refId": "C"
-        },
         {
           "expr": "process_resident_memory_bytes{job=\"service-center\"}",
           "format": "time_series",
           "intervalFactor": 1,
-          "legendFormat": "all_{{instance}}",
+          "legendFormat": "{{instance}}",
           "refId": "A"
         }
       ],
@@ -1084,7 +2344,7 @@
         "h": 7,
         "w": 24,
         "x": 0,
-        "y": 26
+        "y": 51
       },
       "id": 7,
       "legend": {
@@ -1117,14 +2377,14 @@
           "expr": "sum(service_center_local_cache_size_bytes{job=\"service-center\"}) by (resource,instance)",
           "format": "time_series",
           "intervalFactor": 2,
-          "legendFormat": "{{resource}}_{{instance}}",
+          "legendFormat": "{{instance}}> {{resource}}",
           "refId": "A"
         },
         {
           "expr": "sum(service_center_local_cache_size_bytes{job=\"service-center\"}) by (instance)",
           "format": "time_series",
           "intervalFactor": 2,
-          "legendFormat": "ALL_{{instance}}",
+          "legendFormat": "{{instance}}> ALL",
           "refId": "B"
         }
       ],
@@ -1208,5 +2468,5 @@
   "timezone": "",
   "title": "ServiceCenter",
   "uid": "Zg6NoHGiz",
-  "version": 6
+  "version": 45
 }
\ No newline at end of file
diff --git a/integration/instances_test.go b/integration/instances_test.go
index ec71f21f..458c45d1 100644
--- a/integration/instances_test.go
+++ b/integration/instances_test.go
@@ -249,7 +249,7 @@ var _ = Describe("MicroService Api Test", func() {
 
 		By("Discover MicroService Instance API", func() {
 			It("Find Micro-service Info by AppID", func() {
-				req, _ := http.NewRequest(GET, SCURL+FINDINSTANCE+"?noCache=1&appId="+serviceAppId+"&serviceName="+serviceName+"&version="+serviceVersion, nil)
+				req, _ := http.NewRequest(GET, SCURL+FINDINSTANCE+"?appId="+serviceAppId+"&serviceName="+serviceName+"&version="+serviceVersion, nil)
 				req.Header.Set("X-Domain-Name", "default")
 				req.Header.Set("X-ConsumerId", serviceId)
 				resp, _ := scclient.Do(req)
@@ -330,7 +330,7 @@ var _ = Describe("MicroService Api Test", func() {
 			})
 
 			It("Find Micro-Service Instance with rev", func() {
-				req, _ := http.NewRequest(GET, SCURL+FINDINSTANCE+"?noCache=1&appId="+serviceAppId+"&serviceName="+serviceName+"&version="+serviceVersion, nil)
+				req, _ := http.NewRequest(GET, SCURL+FINDINSTANCE+"?appId="+serviceAppId+"&serviceName="+serviceName+"&version="+serviceVersion, nil)
 				req.Header.Set("X-Domain-Name", "default")
 				req.Header.Set("X-ConsumerId", serviceId)
 				resp, _ := scclient.Do(req)
@@ -349,6 +349,7 @@ var _ = Describe("MicroService Api Test", func() {
 				Expect(rev).NotTo(BeEmpty())
 			})
 		})
+
 		By("Update Micro-Service Instance Information API's", func() {
 			It("Update Micro-Service Instance Properties", func() {
 				propertiesInstance := map[string]interface{}{
@@ -473,6 +474,7 @@ var _ = Describe("MicroService Api Test", func() {
 				}
 			})
 		})
+
 		By("Micro-Service Instance heartbeat API", func() {
 			It("Send HeartBeat for micro-service instance", func() {
 				url := strings.Replace(INSTANCEHEARTBEAT, ":serviceId", serviceId, 1)
diff --git a/integration/microservices_test.go b/integration/microservices_test.go
index 95445809..03612753 100644
--- a/integration/microservices_test.go
+++ b/integration/microservices_test.go
@@ -410,9 +410,21 @@ var _ = Describe("MicroService Api Test", func() {
 					// Validate the dependency creation
 					Expect(resp.StatusCode).To(Equal(http.StatusOK))
 
-					<-time.After(time.Second)
+					// add new dependency
+					dependency["providers"] = []interface{}{consumer}
+					body, _ = json.Marshal(bodyParams)
+					bodyBuf = bytes.NewReader(body)
+					req, _ = http.NewRequest(POST, SCURL+CREATEDEPENDENCIES, bodyBuf)
+					req.Header.Set("X-Domain-Name", "default")
+					resp, err = scclient.Do(req)
+					Expect(err).To(BeNil())
+					defer resp.Body.Close()
+
+					// Validate the dependency creation
+					Expect(resp.StatusCode).To(Equal(http.StatusOK))
 
 					//Get Provider by ConsumerID
+					<-time.After(time.Second)
 					url := strings.Replace(GETCONPRODEPENDENCY, ":consumerId", consumerServiceID, 1)
 					req, _ = http.NewRequest(GET, SCURL+url, nil)
 					req.Header.Set("X-Domain-Name", "default")
@@ -432,7 +444,6 @@ var _ = Describe("MicroService Api Test", func() {
 					Expect(foundMicroService).To(Equal(true))
 
 					//Get Consumer by ProviderID
-
 					url = strings.Replace(GETPROCONDEPENDENCY, ":providerId", serviceId, 1)
 					req, _ = http.NewRequest(GET, SCURL+url, nil)
 					req.Header.Set("X-Domain-Name", "default")
@@ -451,10 +462,30 @@ var _ = Describe("MicroService Api Test", func() {
 					}
 					Expect(foundMicroService).To(Equal(true))
 
-					providersArray = []interface{}{consumer}
+					//Get new dependency by ConsumerID
+					url = strings.Replace(GETCONPRODEPENDENCY, ":consumerId", consumerServiceID, 1)
+					req, _ = http.NewRequest(GET, SCURL+url, nil)
+					req.Header.Set("X-Domain-Name", "default")
+					resp, _ = scclient.Do(req)
+					respbody, _ = ioutil.ReadAll(resp.Body)
+					Expect(resp.StatusCode).To(Equal(http.StatusOK))
+					servicesStruct = map[string][]map[string]interface{}{}
+
+					json.Unmarshal(respbody, &servicesStruct)
+					foundMicroService = false
+					for _, services := range servicesStruct["providers"] {
+						if services["serviceName"] == consumerAppName {
+							foundMicroService = true
+							break
+						}
+					}
+					Expect(foundMicroService).To(Equal(true))
+
+					// override the dependency
+					dependency["providers"] = []interface{}{}
 					body, _ = json.Marshal(bodyParams)
 					bodyBuf = bytes.NewReader(body)
-					req, _ = http.NewRequest(POST, SCURL+CREATEDEPENDENCIES, bodyBuf)
+					req, _ = http.NewRequest(UPDATE, SCURL+CREATEDEPENDENCIES, bodyBuf)
 					req.Header.Set("X-Domain-Name", "default")
 					resp, err = scclient.Do(req)
 					Expect(err).To(BeNil())
@@ -463,7 +494,8 @@ var _ = Describe("MicroService Api Test", func() {
 					// Validate the dependency creation
 					Expect(resp.StatusCode).To(Equal(http.StatusOK))
 
-					//Get Provider by ConsumerID
+					//Get Provider by ConsumerID again
+					<-time.After(time.Second)
 					url = strings.Replace(GETCONPRODEPENDENCY, ":consumerId", consumerServiceID, 1)
 					req, _ = http.NewRequest(GET, SCURL+url, nil)
 					req.Header.Set("X-Domain-Name", "default")
@@ -471,16 +503,8 @@ var _ = Describe("MicroService Api Test", func() {
 					respbody, _ = ioutil.ReadAll(resp.Body)
 					Expect(resp.StatusCode).To(Equal(http.StatusOK))
 					servicesStruct = map[string][]map[string]interface{}{}
-
 					json.Unmarshal(respbody, &servicesStruct)
-					foundMicroService = false
-					for _, services := range servicesStruct["providers"] {
-						if services["serviceName"] == serviceName {
-							foundMicroService = true
-							break
-						}
-					}
-					Expect(foundMicroService).To(Equal(true))
+					Expect(len(servicesStruct["providers"])).To(Equal(0))
 
 					//Delete Consumer and Provider
 					url = strings.Replace(UNREGISTERMICROSERVICE, ":serviceId", consumerServiceID, 1)
@@ -498,7 +522,6 @@ var _ = Describe("MicroService Api Test", func() {
 				})
 
 				It("Invalid scenario for GET Providers and Consumers", func() {
-
 					//Get Provider by ConsumerID
 					url := strings.Replace(GETCONPRODEPENDENCY, ":consumerId", "wrongID", 1)
 					req, _ := http.NewRequest(GET, SCURL+url, nil)
diff --git a/integration/rules_test.go b/integration/rules_test.go
index 206f039b..13543c37 100644
--- a/integration/rules_test.go
+++ b/integration/rules_test.go
@@ -28,6 +28,7 @@ import (
 	"net/http"
 	"strconv"
 	"strings"
+	"time"
 )
 
 var _ = Describe("MicroService Api Test", func() {
@@ -185,8 +186,9 @@ var _ = Describe("MicroService Api Test", func() {
 				Expect(resp.StatusCode).To(Equal(http.StatusOK))
 
 				//Duplicate Request
+				<-time.After(time.Second)
 				bodyBuf = bytes.NewReader(body)
-				req, _ = http.NewRequest(POST, SCURL+url+"?noCache=1", bodyBuf)
+				req, _ = http.NewRequest(POST, SCURL+url, bodyBuf)
 				req.Header.Set("X-Domain-Name", "default")
 				resp, err = scclient.Do(req)
 				Expect(err).To(BeNil())
@@ -224,8 +226,9 @@ var _ = Describe("MicroService Api Test", func() {
 				Expect(resp.StatusCode).To(Equal(http.StatusOK))
 
 				//Get Rules
+				<-time.After(time.Second)
 				url = strings.Replace(GETRULES, ":serviceId", serviceId, 1)
-				req, _ = http.NewRequest(GET, SCURL+url+"?noCache=1", nil)
+				req, _ = http.NewRequest(GET, SCURL+url, nil)
 				req.Header.Set("X-Domain-Name", "default")
 				req.Header.Set("X-ConsumerId", serviceId)
 				resp, _ = scclient.Do(req)
@@ -295,6 +298,7 @@ var _ = Describe("MicroService Api Test", func() {
 				}
 
 				//Update Rules
+				<-time.After(time.Second)
 				updateParams := map[string]interface{}{
 					"ruleType":    "WHITE",
 					"attribute":   "tag_b",
@@ -305,7 +309,7 @@ var _ = Describe("MicroService Api Test", func() {
 				url = strings.Replace(url, ":rule_id", ruleID, 1)
 				body, _ = json.Marshal(updateParams)
 				bodyBuf = bytes.NewReader(body)
-				req, _ = http.NewRequest(UPDATE, SCURL+url+"?noCache=1", bodyBuf)
+				req, _ = http.NewRequest(UPDATE, SCURL+url, bodyBuf)
 				req.Header.Set("X-Domain-Name", "default")
 				resp, err = scclient.Do(req)
 				Expect(err).To(BeNil())
@@ -314,8 +318,9 @@ var _ = Describe("MicroService Api Test", func() {
 				Expect(resp.StatusCode).To(Equal(http.StatusOK))
 
 				//Get Rules
+				<-time.After(time.Second)
 				url = strings.Replace(GETRULES, ":serviceId", serviceId, 1)
-				req, _ = http.NewRequest(GET, SCURL+url+"?noCache=1", nil)
+				req, _ = http.NewRequest(GET, SCURL+url, nil)
 				req.Header.Set("X-Domain-Name", "default")
 				req.Header.Set("X-ConsumerId", serviceId)
 				resp, _ = scclient.Do(req)
@@ -362,6 +367,7 @@ var _ = Describe("MicroService Api Test", func() {
 				}
 
 				//Update Rules with invalid RuleType
+				<-time.After(time.Second)
 				updateParams := map[string]interface{}{
 					"ruleType":    "UNKNOWN",
 					"attribute":   "tag_b",
@@ -372,7 +378,7 @@ var _ = Describe("MicroService Api Test", func() {
 				url = strings.Replace(url, ":rule_id", ruleID, 1)
 				body, _ = json.Marshal(updateParams)
 				bodyBuf = bytes.NewReader(body)
-				req, _ = http.NewRequest(UPDATE, SCURL+url+"?noCache=1", bodyBuf)
+				req, _ = http.NewRequest(UPDATE, SCURL+url, bodyBuf)
 				req.Header.Set("X-Domain-Name", "default")
 				resp, err = scclient.Do(req)
 				Expect(err).To(BeNil())
@@ -380,6 +386,7 @@ var _ = Describe("MicroService Api Test", func() {
 				Expect(resp.StatusCode).To(Equal(http.StatusBadRequest))
 
 				//Update Rules with invalid pattern
+				<-time.After(time.Second)
 				updateParams = map[string]interface{}{
 					"ruleType":    "WHITE",
 					"attribute":   "tag_b",
@@ -390,7 +397,7 @@ var _ = Describe("MicroService Api Test", func() {
 				url = strings.Replace(url, ":rule_id", ruleID, 1)
 				body, _ = json.Marshal(updateParams)
 				bodyBuf = bytes.NewReader(body)
-				req, _ = http.NewRequest(UPDATE, SCURL+url+"?noCache=1", bodyBuf)
+				req, _ = http.NewRequest(UPDATE, SCURL+url, bodyBuf)
 				req.Header.Set("X-Domain-Name", "default")
 				resp, err = scclient.Do(req)
 				Expect(err).To(BeNil())
@@ -398,6 +405,7 @@ var _ = Describe("MicroService Api Test", func() {
 				Expect(resp.StatusCode).To(Equal(http.StatusBadRequest))
 
 				//Update Rules with invalid different ruleType
+				<-time.After(time.Second)
 				updateParams = map[string]interface{}{
 					"ruleType":    "BLACK",
 					"attribute":   "tag_b",
@@ -408,7 +416,7 @@ var _ = Describe("MicroService Api Test", func() {
 				url = strings.Replace(url, ":rule_id", ruleID, 1)
 				body, _ = json.Marshal(updateParams)
 				bodyBuf = bytes.NewReader(body)
-				req, _ = http.NewRequest(UPDATE, SCURL+url+"?noCache=1", bodyBuf)
+				req, _ = http.NewRequest(UPDATE, SCURL+url, bodyBuf)
 				req.Header.Set("X-Domain-Name", "default")
 				resp, err = scclient.Do(req)
 				Expect(err).To(BeNil())
@@ -470,9 +478,10 @@ var _ = Describe("MicroService Api Test", func() {
 				}
 
 				//Delete the Rules
+				<-time.After(time.Second)
 				url = strings.Replace(DELETERULES, ":serviceId", serviceId, 1)
 				url = strings.Replace(url, ":rule_id", ruleID, 1)
-				req, _ = http.NewRequest(DELETE, SCURL+url+"?noCache=1", nil)
+				req, _ = http.NewRequest(DELETE, SCURL+url, nil)
 				req.Header.Set("X-Domain-Name", "default")
 				resp, err = scclient.Do(req)
 				Expect(err).To(BeNil())
@@ -480,8 +489,9 @@ var _ = Describe("MicroService Api Test", func() {
 				Expect(resp.StatusCode).To(Equal(http.StatusOK))
 
 				//verify Delete
+				<-time.After(time.Second)
 				url = strings.Replace(GETTAGS, ":serviceId", serviceId, 1)
-				req, _ = http.NewRequest(GET, SCURL+url+"?noCache=1", nil)
+				req, _ = http.NewRequest(GET, SCURL+url, nil)
 				req.Header.Set("X-Domain-Name", "default")
 				req.Header.Set("X-ConsumerId", serviceId)
 				resp, _ = scclient.Do(req)
diff --git a/integration/tags_test.go b/integration/tags_test.go
index 78b3a507..ba96a7c8 100644
--- a/integration/tags_test.go
+++ b/integration/tags_test.go
@@ -159,8 +159,9 @@ var _ = Describe("MicroService Api Test", func() {
 				Expect(resp.StatusCode).To(Equal(http.StatusOK))
 
 				//Get Tags
+				<-time.After(time.Second)
 				url = strings.Replace(GETTAGS, ":serviceId", serviceId, 1)
-				req, _ = http.NewRequest(GET, SCURL+url+"?noCache=1", nil)
+				req, _ = http.NewRequest(GET, SCURL+url, nil)
 				req.Header.Set("X-Domain-Name", "default")
 				req.Header.Set("X-ConsumerId", serviceId)
 				resp, _ = scclient.Do(req)
@@ -215,9 +216,10 @@ var _ = Describe("MicroService Api Test", func() {
 				Expect(resp.StatusCode).To(Equal(http.StatusOK))
 
 				//Update Tags
+				<-time.After(time.Second)
 				url = strings.Replace(UPDATETAG, ":serviceId", serviceId, 1)
 				url = strings.Replace(url, ":key", "testkey", 1)
-				req, _ = http.NewRequest(UPDATE, SCURL+url+"?value=newValue&noCache=1", nil)
+				req, _ = http.NewRequest(UPDATE, SCURL+url+"?value=newValue", nil)
 				req.Header.Set("X-Domain-Name", "default")
 				resp, err = scclient.Do(req)
 				Expect(err).To(BeNil())
@@ -227,7 +229,7 @@ var _ = Describe("MicroService Api Test", func() {
 				//Verify the Tags
 				<-time.After(time.Second)
 				url = strings.Replace(GETTAGS, ":serviceId", serviceId, 1)
-				req, _ = http.NewRequest(GET, SCURL+url+"?noCache=1", nil)
+				req, _ = http.NewRequest(GET, SCURL+url, nil)
 				req.Header.Set("X-Domain-Name", "default")
 				req.Header.Set("X-ConsumerId", serviceId)
 				resp, _ = scclient.Do(req)
@@ -266,9 +268,10 @@ var _ = Describe("MicroService Api Test", func() {
 				Expect(resp.StatusCode).To(Equal(http.StatusOK))
 
 				//Update Tags
+				<-time.After(time.Second)
 				url = strings.Replace(UPDATETAG, ":serviceId", serviceId, 1)
 				url = strings.Replace(url, ":key", "unknownkey", 1)
-				req, _ = http.NewRequest(UPDATE, SCURL+url+"?value=newValue&noCache=1", nil)
+				req, _ = http.NewRequest(UPDATE, SCURL+url+"?value=newValue", nil)
 				req.Header.Set("X-Domain-Name", "default")
 				resp, err = scclient.Do(req)
 				Expect(err).To(BeNil())
@@ -311,9 +314,10 @@ var _ = Describe("MicroService Api Test", func() {
 				Expect(resp.StatusCode).To(Equal(http.StatusOK))
 
 				//Delete the tag
+				<-time.After(time.Second)
 				url = strings.Replace(DELETETAG, ":serviceId", serviceId, 1)
 				url = strings.Replace(url, ":key", "testkey", 1)
-				req, _ = http.NewRequest(DELETE, SCURL+url+"?noCache=1", nil)
+				req, _ = http.NewRequest(DELETE, SCURL+url, nil)
 				req.Header.Set("X-Domain-Name", "default")
 				resp, err = scclient.Do(req)
 				Expect(err).To(BeNil())
@@ -323,7 +327,7 @@ var _ = Describe("MicroService Api Test", func() {
 				//verify Delete
 				<-time.After(time.Second)
 				url = strings.Replace(GETTAGS, ":serviceId", serviceId, 1)
-				req, _ = http.NewRequest(GET, SCURL+url+"?noCache=1", nil)
+				req, _ = http.NewRequest(GET, SCURL+url, nil)
 				req.Header.Set("X-Domain-Name", "default")
 				req.Header.Set("X-ConsumerId", serviceId)
 				resp, _ = scclient.Do(req)
@@ -353,9 +357,10 @@ var _ = Describe("MicroService Api Test", func() {
 				Expect(resp.StatusCode).To(Equal(http.StatusOK))
 
 				//Delete the tag
+				<-time.After(time.Second)
 				url = strings.Replace(DELETETAG, ":serviceId", serviceId, 1)
 				url = strings.Replace(url, ":key", "unknowTag", 1)
-				req, _ = http.NewRequest(DELETE, SCURL+url+"?noCache=1", nil)
+				req, _ = http.NewRequest(DELETE, SCURL+url, nil)
 				req.Header.Set("X-Domain-Name", "default")
 				resp, err = scclient.Do(req)
 				Expect(err).To(BeNil())
@@ -363,8 +368,9 @@ var _ = Describe("MicroService Api Test", func() {
 				Expect(resp.StatusCode).To(Equal(http.StatusBadRequest))
 
 				//verify Non-deleted of exsiting tag
+				<-time.After(time.Second)
 				url = strings.Replace(GETTAGS, ":serviceId", serviceId, 1)
-				req, _ = http.NewRequest(GET, SCURL+url+"?noCache=1", nil)
+				req, _ = http.NewRequest(GET, SCURL+url, nil)
 				req.Header.Set("X-Domain-Name", "default")
 				req.Header.Set("X-ConsumerId", serviceId)
 				resp, _ = scclient.Do(req)
diff --git a/main.go b/main.go
index 3d7ac976..efbcaac5 100644
--- a/main.go
+++ b/main.go
@@ -16,20 +16,12 @@
  */
 package main
 
-// plugins
+import _ "github.com/apache/incubator-servicecomb-service-center/server/init"
 import _ "github.com/apache/incubator-servicecomb-service-center/server/bootstrap"
 import (
-	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
 	"github.com/apache/incubator-servicecomb-service-center/server"
-	"github.com/apache/incubator-servicecomb-service-center/server/core/backend"
 )
 
 func main() {
 	server.Run()
-
-	util.GoCloseAndWait()
-
-	backend.Registry().Close()
-
-	util.Logger().Warn("service center exited", nil)
 }
diff --git a/pkg/chain/callback_test.go b/pkg/chain/callback_test.go
new file mode 100644
index 00000000..db452a64
--- /dev/null
+++ b/pkg/chain/callback_test.go
@@ -0,0 +1,56 @@
+/*
+ * 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 chain
+
+import (
+	"errors"
+	"testing"
+)
+
+func TestCallback_Invoke(t *testing.T) {
+	cb := &Callback{Func: func(r Result) {
+		if r.Args[0].(bool) != r.OK {
+			t.Fatalf("TestCallback_Invoke failed")
+		}
+		if !r.OK && r.Err == nil {
+			t.Fatalf("TestCallback_Invoke failed")
+		}
+	}, Async: false}
+	cb.Success(true)
+	cb.Fail(errors.New("error"), false)
+	cb.Invoke(Result{OK: true, Args: []interface{}{true}})
+
+	ch := make(chan struct{})
+	cb = &Callback{Func: func(r Result) {
+		if r.Args[0].(bool) != r.OK {
+			t.Fatalf("TestCallback_Invoke failed")
+		}
+		if !r.OK && r.Err == nil {
+			t.Fatalf("TestCallback_Invoke failed")
+		}
+		ch <- struct{}{}
+	}, Async: true}
+	cb.Success(true)
+	cb.Fail(errors.New("error"), false)
+	cb.Invoke(Result{OK: true, Args: []interface{}{true}})
+	<-ch
+
+	cb = &Callback{}
+	cb.Success(true)
+	cb.Fail(errors.New("error"), false)
+	cb.Invoke(Result{OK: true, Args: []interface{}{true}})
+}
diff --git a/pkg/chain/chain_test.go b/pkg/chain/chain_test.go
index 1bec1642..d053a815 100644
--- a/pkg/chain/chain_test.go
+++ b/pkg/chain/chain_test.go
@@ -14,11 +14,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package chain_test
+package chain
 
 import (
 	"context"
-	"github.com/apache/incubator-servicecomb-service-center/pkg/chain"
+	"errors"
 	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
 	"testing"
 )
@@ -30,14 +30,14 @@ const (
 
 func init() {
 	for i := 0; i < count; i++ {
-		chain.RegisterHandler("_bench_handlers_", &handler{})
+		RegisterHandler("_bench_handlers_", &handler{})
 	}
 }
 
 type handler struct {
 }
 
-func (h *handler) Handle(i *chain.Invocation) {
+func (h *handler) Handle(i *Invocation) {
 	i.Next()
 }
 
@@ -50,9 +50,9 @@ func syncFunc(i int) {
 
 func BenchmarkInvocationOption(b *testing.B) {
 	var (
-		op   chain.InvocationOp
-		f    = func(r chain.Result) {}
-		opts = []chain.InvocationOption{chain.WithFunc(f), chain.WithAsyncFunc(f)}
+		op   InvocationOp
+		f    = func(r Result) {}
+		opts = []InvocationOption{WithFunc(f), WithAsyncFunc(f)}
 	)
 	b.RunParallel(func(pb *testing.PB) {
 		for pb.Next() {
@@ -68,14 +68,14 @@ func BenchmarkInvocationOption(b *testing.B) {
 func BenchmarkChain(b *testing.B) {
 	var (
 		ctx = util.NewStringContext(context.Background())
-		f   = func(r chain.Result) {}
+		f   = func(r Result) {}
 	)
 
 	b.N = times
 	b.ResetTimer()
 	b.RunParallel(func(pb *testing.PB) {
 		for pb.Next() {
-			inv := chain.NewInvocation(ctx, chain.NewChain("_bench_chain_", chain.Handlers("_bench_handlers_")))
+			inv := NewInvocation(ctx, NewChain("_bench_chain_", Handlers("_bench_handlers_")))
 			inv.Invoke(f)
 		}
 	})
@@ -94,3 +94,135 @@ func BenchmarkSync(b *testing.B) {
 	b.ReportAllocs()
 	// 1000000	        46.9 ns/op	       0 B/op	       0 allocs/op
 }
+
+type mockHandler struct {
+}
+
+func (h *mockHandler) Handle(i *Invocation) {
+	x := i.Context().Value("x").(int)
+	switch x {
+	case 1:
+		i.Success(x)
+	case 2:
+		i.Fail(errors.New("error"), x)
+	case 3:
+		i.Next(WithFunc(func(r Result) {
+			i.WithContext("x", x*x)
+		}))
+	case 4:
+		i.Next(WithAsyncFunc(func(r Result) {
+			i.WithContext("x", x*x)
+			i.Context().Value("ch").(chan struct{}) <- struct{}{}
+		}))
+	case 5:
+		panic(errors.New("error"))
+	case 6:
+		i.Next(WithFunc(func(r Result) {
+			panic(errors.New("error"))
+		}))
+	case 7:
+		i.Next(WithAsyncFunc(func(r Result) {
+			panic(errors.New("error"))
+		}))
+	default:
+		i.WithContext("x", x-1)
+		i.Next()
+	}
+}
+
+func TestChain_Next(t *testing.T) {
+	h := &mockHandler{}
+	hs := Handlers("test")
+	hs = []Handler{h, h, h}
+
+	x := 0
+	ch := NewChain("test", hs)
+	if ch.Name() != "test" {
+		t.Fatalf("TestChain_Next")
+	}
+	i := NewInvocation(context.Background(), ch)
+	i.WithContext("x", x)
+	i.Invoke(func(r Result) {
+		if !r.OK || i.Context().Value("x").(int) != -len(hs) {
+			t.Fatalf("TestChain_Next")
+		}
+	})
+
+	x = 1
+	ch = NewChain("test", hs)
+	i = NewInvocation(context.Background(), ch)
+	i.WithContext("x", x)
+	i.Invoke(func(r Result) {
+		if !r.OK || r.Args[0].(int) != x {
+			t.Fatalf("TestChain_Next")
+		}
+	})
+
+	x = 2
+	ch = NewChain("test", hs)
+	i = NewInvocation(context.Background(), ch)
+	i.WithContext("x", x)
+	i.Invoke(func(r Result) {
+		if r.OK || r.Args[0].(int) != x {
+			t.Fatalf("TestChain_Next")
+		}
+	})
+
+	x = 3
+	ch = NewChain("test", hs)
+	i = NewInvocation(context.Background(), ch)
+	i.WithContext("x", x)
+	i.Invoke(func(r Result) {
+		if !r.OK || i.Context().Value("x").(int) != x {
+			t.Fatalf("TestChain_Next")
+		}
+	})
+	if i.Context().Value("x").(int) != x*x {
+		t.Fatalf("TestChain_Next")
+	}
+
+	x = 4
+	ch = NewChain("test", hs)
+	i = NewInvocation(context.Background(), ch)
+	i.WithContext("x", x)
+	i.WithContext("ch", make(chan struct{}))
+	i.Invoke(func(r Result) {
+		if !r.OK || i.Context().Value("x").(int) != x {
+			t.Fatalf("TestChain_Next")
+		}
+	})
+	<-i.Context().Value("ch").(chan struct{})
+	if i.Context().Value("x").(int) != x*x {
+		t.Fatalf("TestChain_Next")
+	}
+
+	x = 5
+	ch = NewChain("test", hs)
+	i = NewInvocation(context.Background(), ch)
+	i.WithContext("x", x)
+	i.Invoke(func(r Result) {
+		if r.OK || r.Err == nil {
+			t.Fatalf("TestChain_Next")
+		}
+	})
+
+	x = 6
+	ch = NewChain("test", hs)
+	i = NewInvocation(context.Background(), ch)
+	i.WithContext("x", x)
+	i.Invoke(func(r Result) {
+		if !r.OK || r.Err != nil {
+			t.Fatalf("TestChain_Next")
+		}
+	})
+
+	x = 7
+	ch = NewChain("test", hs)
+	i = NewInvocation(context.Background(), ch)
+	i.WithContext("x", x)
+	i.Invoke(func(r Result) {
+		if !r.OK || r.Err != nil {
+			t.Fatalf("TestChain_Next")
+		}
+	})
+}
diff --git a/pkg/chain/handler.go b/pkg/chain/handler.go
index 372e8c7c..5efdab7f 100644
--- a/pkg/chain/handler.go
+++ b/pkg/chain/handler.go
@@ -36,8 +36,8 @@ func RegisterHandler(catalog string, h Handler) {
 	handlers = append(handlers, h)
 	handlersMap[catalog] = handlers
 
-	t := util.ReflectObject(h)
-	util.Logger().Infof("register handler[%s] %s/%s", catalog, t.Type.PkgPath(), t.Type.Name())
+	t := util.Reflect(h)
+	util.Logger().Infof("register handler[%s] %s", catalog, t.Name())
 }
 
 func Handlers(catalog string) []Handler {
diff --git a/pkg/chain/invocation.go b/pkg/chain/invocation.go
index 5e5276cb..bb024ca9 100644
--- a/pkg/chain/invocation.go
+++ b/pkg/chain/invocation.go
@@ -55,6 +55,12 @@ func (i *Invocation) WithContext(key string, val interface{}) *Invocation {
 	return i
 }
 
+// Next is the method to go next step in handler chain
+// WithFunc and WithAsyncFunc options can add customize callbacks in chain
+// and the callbacks seq like below
+// i.Success/Fail() -> CB1 ---> CB3 ----------> END           goroutine 0
+//                          \-> CB2(async) \                  goroutine 1
+//                                          \-> CB4(async)    goroutine 1 or 2
 func (i *Invocation) Next(opts ...InvocationOption) {
 	var op InvocationOp
 	for _, opt := range opts {
@@ -75,11 +81,6 @@ func (i *Invocation) setCallback(f CallbackFunc, async bool) {
 		i.Async = async
 		return
 	}
-
-	// the callbacks seq like below
-	// i.Success() -> CB1 ---> CB3 ----------> END           goroutine 0
-	//                     \-> CB2(async) \                  goroutine 1
-	//                                     \-> CB4(async)    goroutine 1 or 2
 	cb := i.Func
 	i.Func = func(r Result) {
 		cb(r)
diff --git a/pkg/etcdsync/etcdsync_suite_test.go b/pkg/etcdsync/etcdsync_suite_test.go
index 7af14fad..c5ded548 100644
--- a/pkg/etcdsync/etcdsync_suite_test.go
+++ b/pkg/etcdsync/etcdsync_suite_test.go
@@ -14,22 +14,19 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package etcdsync_test
+package etcdsync
 
 import (
-	_ "github.com/apache/incubator-servicecomb-service-center/server/plugin/infra/registry/embededetcd"
+	"fmt"
 	_ "github.com/apache/incubator-servicecomb-service-center/server/plugin/infra/registry/etcd"
+	_ "github.com/apache/incubator-servicecomb-service-center/server/plugin/infra/tracing/buildin"
 	. "github.com/onsi/ginkgo"
 	. "github.com/onsi/gomega"
-)
-import (
-	"fmt"
-	"github.com/apache/incubator-servicecomb-service-center/pkg/etcdsync"
 	"testing"
 )
 
 func init() {
-	etcdsync.IsDebug = true
+	IsDebug = true
 }
 
 func TestEtcdsync(t *testing.T) {
@@ -41,11 +38,11 @@ func BenchmarkLock(b *testing.B) {
 	var g = 0
 	b.RunParallel(func(pb *testing.PB) {
 		for pb.Next() {
-			lock, _ := etcdsync.Lock("/test", true)
-			defer lock.Unlock()
+			lock, _ := Lock("/test", true)
 			//do something
 			g += 1
 			fmt.Println(g)
+			lock.Unlock()
 		}
 	})
 	fmt.Println("Parallel:", b.N)
diff --git a/pkg/etcdsync/mutex.go b/pkg/etcdsync/mutex.go
index d1a35b94..cc7fc2d0 100644
--- a/pkg/etcdsync/mutex.go
+++ b/pkg/etcdsync/mutex.go
@@ -50,10 +50,11 @@ var (
 	globalMap = make(map[string]*DLockFactory)
 	globalMux sync.Mutex
 	IsDebug   bool
-	hostname  string = util.HostName()
-	pid       int    = os.Getpid()
+	hostname  = util.HostName()
+	pid       = os.Getpid()
 )
 
+// lock will not be release automatically if ttl = 0
 func NewLockFactory(key string, ttl int64) *DLockFactory {
 	if len(key) == 0 {
 		return nil
@@ -81,86 +82,92 @@ func (m *DLockFactory) NewDLock(wait bool) (l *DLock, err error) {
 	for try := 1; try <= DEFAULT_RETRY_TIMES; try++ {
 		err = l.Lock(wait)
 		if err == nil {
-			return l, nil
+			return
 		}
 
 		if !wait {
-			l, err = nil, nil
 			break
 		}
-
-		if try <= DEFAULT_RETRY_TIMES {
-			util.Logger().Warnf(err, "Try to lock key %s again, id=%s", m.key, l.id)
-		} else {
-			util.Logger().Errorf(err, "Lock key %s failed, id=%s", m.key, l.id)
-		}
 	}
+	// failed
+	util.Logger().Errorf(err, "Lock key %s failed, id=%s", m.key, l.id)
+	l = nil
+
 	if !IsDebug {
 		m.mutex.Unlock()
 	}
-	return l, err
+	return
 }
 
 func (m *DLock) ID() string {
 	return m.id
 }
 
-func (m *DLock) Lock(wait bool) error {
+func (m *DLock) Lock(wait bool) (err error) {
 	opts := []registry.PluginOpOption{
 		registry.WithStrKey(m.builder.key),
 		registry.WithStrValue(m.id)}
 
-	for {
-		util.Logger().Infof("Trying to create a lock: key=%s, id=%s", m.builder.key, m.id)
+	util.Logger().Infof("Trying to create a lock: key=%s, id=%s", m.builder.key, m.id)
 
-		putOpts := opts
-		if m.builder.ttl > 0 {
-			leaseID, err := backend.Registry().LeaseGrant(m.builder.ctx, m.builder.ttl)
-			if err != nil {
-				return err
-			}
-			putOpts = append(opts, registry.WithLease(leaseID))
-		}
-		success, err := backend.Registry().PutNoOverride(m.builder.ctx, putOpts...)
-		if err == nil && success {
-			util.Logger().Infof("Create Lock OK, key=%s, id=%s", m.builder.key, m.id)
-			return nil
+	var leaseID int64
+	putOpts := opts
+	if m.builder.ttl > 0 {
+		leaseID, err = backend.Registry().LeaseGrant(m.builder.ctx, m.builder.ttl)
+		if err != nil {
+			return err
 		}
+		putOpts = append(opts, registry.WithLease(leaseID))
+	}
+	success, err := backend.Registry().PutNoOverride(m.builder.ctx, putOpts...)
+	if err == nil && success {
+		util.Logger().Infof("Create Lock OK, key=%s, id=%s", m.builder.key, m.id)
+		return nil
+	}
 
-		if !wait {
-			return fmt.Errorf("Key %s is locked by id=%s", m.builder.key, m.id)
-		}
+	if leaseID > 0 {
+		backend.Registry().LeaseRevoke(m.builder.ctx, leaseID)
+	}
+
+	if m.builder.ttl == 0 || !wait {
+		return fmt.Errorf("Key %s is locked by id=%s", m.builder.key, m.id)
+	}
 
-		util.Logger().Warnf(err, "Key %s is locked, waiting for other node releases it, id=%s", m.builder.key, m.id)
-
-		ctx, cancel := context.WithTimeout(m.builder.ctx, DEFAULT_LOCK_TTL*time.Second)
-		util.Go(func(context.Context) {
-			defer cancel()
-			err := backend.Registry().Watch(ctx,
-				registry.WithStrKey(m.builder.key),
-				registry.WithWatchCallback(
-					func(message string, evt *registry.PluginResponse) error {
-						if evt != nil && evt.Action == registry.Delete {
-							// break this for-loop, and try to create the node again.
-							return fmt.Errorf("Lock released")
-						}
-						return nil
-					}))
-			if err != nil {
-				util.Logger().Warnf(nil, "%s, key=%s, id=%s", err.Error(), m.builder.key, m.id)
-			}
-		})
-		select {
-		case <-ctx.Done():
-			continue // 可以重新尝试获取锁
-		case <-m.builder.ctx.Done():
-			cancel()
-			return m.builder.ctx.Err() // 机制错误,不应该超时的
+	util.Logger().Warnf(err, "Key %s is locked, waiting for other node releases it, id=%s", m.builder.key, m.id)
+
+	ctx, cancel := context.WithTimeout(m.builder.ctx, time.Duration(m.builder.ttl)*time.Second)
+	util.Go(func(context.Context) {
+		defer cancel()
+		err := backend.Registry().Watch(ctx,
+			registry.WithStrKey(m.builder.key),
+			registry.WithWatchCallback(
+				func(message string, evt *registry.PluginResponse) error {
+					if evt != nil && evt.Action == registry.Delete {
+						// break this for-loop, and try to create the node again.
+						return fmt.Errorf("Lock released")
+					}
+					return nil
+				}))
+		if err != nil {
+			util.Logger().Warnf(nil, "%s, key=%s, id=%s", err.Error(), m.builder.key, m.id)
 		}
+	})
+	select {
+	case <-ctx.Done():
+		return ctx.Err() // 可以重新尝试获取锁
+	case <-m.builder.ctx.Done():
+		cancel()
+		return m.builder.ctx.Err() // 机制错误,不应该超时的
 	}
 }
 
 func (m *DLock) Unlock() (err error) {
+	defer func() {
+		if !IsDebug {
+			m.builder.mutex.Unlock()
+		}
+	}()
+
 	opts := []registry.PluginOpOption{
 		registry.DEL,
 		registry.WithStrKey(m.builder.key)}
@@ -168,24 +175,15 @@ func (m *DLock) Unlock() (err error) {
 	for i := 1; i <= DEFAULT_RETRY_TIMES; i++ {
 		_, err = backend.Registry().Do(m.builder.ctx, opts...)
 		if err == nil {
-			if !IsDebug {
-				m.builder.mutex.Unlock()
-			}
 			util.Logger().Infof("Delete lock OK, key=%s, id=%s", m.builder.key, m.id)
 			return nil
 		}
-		util.Logger().Errorf(err, "Delete lock falied, key=%s, id=%s", m.builder.key, m.id)
+		util.Logger().Errorf(err, "Delete lock failed, key=%s, id=%s", m.builder.key, m.id)
 		e, ok := err.(client.Error)
 		if ok && e.Code == client.ErrorCodeKeyNotFound {
-			if !IsDebug {
-				m.builder.mutex.Unlock()
-			}
 			return nil
 		}
 	}
-	if !IsDebug {
-		m.builder.mutex.Unlock()
-	}
 	return err
 }
 
diff --git a/pkg/etcdsync/mutex_test.go b/pkg/etcdsync/mutex_test.go
index 23a333b5..e6023882 100644
--- a/pkg/etcdsync/mutex_test.go
+++ b/pkg/etcdsync/mutex_test.go
@@ -14,36 +14,51 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package etcdsync_test
+package etcdsync
 
 import (
-	. "github.com/apache/incubator-servicecomb-service-center/pkg/etcdsync"
-
 	"fmt"
 	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
 )
 
 var _ = Describe("Mutex", func() {
 	Context("normal", func() {
 		It("TestLockTimeout", func() {
-			m1 := NewLockFactory("key1", 10)
-			m2 := NewLockFactory("key1", 2)
-			m1.NewDLock(true)
+			m1 := NewLockFactory("key1", 5)
+			m2 := NewLockFactory("key1", 1)
+			l1, err := m1.NewDLock(true)
+			Expect(l1).ToNot(BeNil())
+			Expect(err).To(BeNil())
+
 			fmt.Println("UT===================m1 locked")
 			ch := make(chan bool)
 			go func() {
-				l, _ := m2.NewDLock(true)
-				fmt.Println("UT===================m2 locked")
-				l.Unlock()
+				l2, err := m2.NewDLock(false)
+				Expect(l2).To(BeNil())
+				Expect(err).ToNot(BeNil())
+				fmt.Println("UT===================m2 try failed")
+
+				l2, err = m2.NewDLock(true) // 1s * 3
+				Expect(l2).To(BeNil())
+				Expect(err).ToNot(BeNil())
+				fmt.Println("UT===================m2 timed out")
 				ch <- true
 			}()
 			<-ch
-			fmt.Println("lock m1 timeout")
+
 			m3 := NewLockFactory("key1", 2)
-			l, _ := m3.NewDLock(true)
+			l3, err := m3.NewDLock(true)
+			Expect(l3).ToNot(BeNil())
+			Expect(err).To(BeNil())
+
 			fmt.Println("UT===================m3 locked")
-			l.Unlock()
+			err = l3.Unlock()
+			Expect(err).To(BeNil())
 
+			err = l1.Unlock()
+			Expect(err).To(BeNil())
+			fmt.Println("UT===================m1 unlocked")
 		})
 	})
 })
diff --git a/pkg/rest/client.go b/pkg/rest/client.go
index 6fc15d15..56f7423a 100644
--- a/pkg/rest/client.go
+++ b/pkg/rest/client.go
@@ -146,7 +146,9 @@ func (client *HttpClient) httpDo(method string, url string, headers map[string]s
 			var ok bool = false
 			bodyBytes, ok = body.([]byte)
 			if !ok {
-				util.Logger().Errorf(nil, "invalid body type '%s'(%s), body must type of byte array if Content-Type specified.", reflect.TypeOf(body), headers[HEADER_CONTENT_TYPE])
+				util.Logger().Errorf(nil,
+					"invalid body type '%s'(%s), body must type of byte array if Content-Type specified.",
+					reflect.TypeOf(body), headers[HEADER_CONTENT_TYPE])
 				return status, result
 			}
 		}
diff --git a/pkg/rest/listener.go b/pkg/rest/listener.go
index 0f119a5a..a5f8467c 100644
--- a/pkg/rest/listener.go
+++ b/pkg/rest/listener.go
@@ -22,28 +22,27 @@ import (
 	"syscall"
 )
 
-type restListener struct {
+type TcpListener struct {
 	net.Listener
 	stopCh chan error
 	closed bool
 	server *Server
 }
 
-func newRestListener(l net.Listener, srv *Server) (el *restListener) {
-	el = &restListener{
+func NewTcpListener(l net.Listener, srv *Server) (el *TcpListener) {
+	el = &TcpListener{
 		Listener: l,
 		stopCh:   make(chan error),
 		server:   srv,
 	}
 	go func() {
 		<-el.stopCh
-		el.closed = true
 		el.stopCh <- el.Listener.Close()
 	}()
 	return
 }
 
-func (rl *restListener) Accept() (c net.Conn, err error) {
+func (rl *TcpListener) Accept() (c net.Conn, err error) {
 	tc, err := rl.Listener.(*net.TCPListener).AcceptTCP()
 	if err != nil {
 		return
@@ -63,15 +62,16 @@ func (rl *restListener) Accept() (c net.Conn, err error) {
 	return
 }
 
-func (rl *restListener) Close() error {
+func (rl *TcpListener) Close() error {
 	if rl.closed {
 		return syscall.EINVAL
 	}
+	rl.closed = true
 	rl.stopCh <- nil
 	return <-rl.stopCh
 }
 
-func (rl *restListener) File() *os.File {
+func (rl *TcpListener) File() *os.File {
 	tl := rl.Listener.(*net.TCPListener)
 	fl, _ := tl.File()
 	return fl
diff --git a/pkg/rest/roa.go b/pkg/rest/roa.go
index 63d01c39..f217fad9 100644
--- a/pkg/rest/roa.go
+++ b/pkg/rest/roa.go
@@ -40,31 +40,31 @@ func initROAServerHandler() *ROAServerHandler {
 }
 
 // servant must be an pointer to service object
-func RegisterServent(servant interface{}) {
+func RegisterServant(servant interface{}) {
 	val := reflect.ValueOf(servant)
 	ind := reflect.Indirect(val)
 	typ := ind.Type()
-	name := typ.PkgPath() + "." + typ.Name()
+	name := util.FileLastName(typ.PkgPath() + "." + typ.Name())
 	if val.Kind() != reflect.Ptr {
-		util.Logger().Errorf(nil, "<rest.RegisterServent> cannot use non-ptr servant struct `%s`", name)
+		util.Logger().Errorf(nil, "<rest.RegisterServant> cannot use non-ptr servant struct `%s`", name)
 		return
 	}
 
 	urlPatternFunc := val.MethodByName("URLPatterns")
 	if !urlPatternFunc.IsValid() {
-		util.Logger().Errorf(nil, "<rest.RegisterServent> no 'URLPatterns' function in servant struct `%s`", name)
+		util.Logger().Errorf(nil, "<rest.RegisterServant> no 'URLPatterns' function in servant struct `%s`", name)
 		return
 	}
 
 	vals := urlPatternFunc.Call([]reflect.Value{})
 	if len(vals) <= 0 {
-		util.Logger().Errorf(nil, "<rest.RegisterServent> call 'URLPatterns' function failed in servant struct `%s`", name)
+		util.Logger().Errorf(nil, "<rest.RegisterServant> call 'URLPatterns' function failed in servant struct `%s`", name)
 		return
 	}
 
 	val0 := vals[0]
 	if !val.CanInterface() {
-		util.Logger().Errorf(nil, "<rest.RegisterServent> result of 'URLPatterns' function not interface type in servant struct `%s`", name)
+		util.Logger().Errorf(nil, "<rest.RegisterServant> result of 'URLPatterns' function not interface type in servant struct `%s`", name)
 		return
 	}
 
@@ -77,7 +77,7 @@ func RegisterServent(servant interface{}) {
 			}
 		}
 	} else {
-		util.Logger().Errorf(nil, "<rest.RegisterServent> result of 'URLPatterns' function not []*Route type in servant struct `%s`", name)
+		util.Logger().Errorf(nil, "<rest.RegisterServant> result of 'URLPatterns' function not []*Route type in servant struct `%s`", name)
 	}
 }
 
diff --git a/pkg/rest/server.go b/pkg/rest/server.go
index 4dd7e86d..06bbcaa3 100644
--- a/pkg/rest/server.go
+++ b/pkg/rest/server.go
@@ -89,9 +89,9 @@ type Server struct {
 	KeepaliveTimeout time.Duration
 	GraceTimeout     time.Duration
 
-	registerListener net.Listener
-	restListener     net.Listener
-	innerListener    *restListener
+	Listener    net.Listener
+	netListener net.Listener
+	tcpListener *TcpListener
 
 	conns int64
 	wg    sync.WaitGroup
@@ -104,7 +104,7 @@ func (srv *Server) Serve() (err error) {
 	}()
 	defer util.RecoverAndReport()
 	srv.state = serverStateRunning
-	err = srv.Server.Serve(srv.restListener)
+	err = srv.Server.Serve(srv.Listener)
 	util.Logger().Debugf("server serve failed(%s)", err)
 	srv.wg.Wait()
 	return
@@ -136,12 +136,12 @@ func (srv *Server) Listen() error {
 		addr = ":http"
 	}
 
-	l, err := srv.getListener(addr)
+	l, err := srv.getOrCreateListener(addr)
 	if err != nil {
 		return err
 	}
 
-	srv.restListener = newRestListener(l, srv)
+	srv.Listener = NewTcpListener(l, srv)
 	grace.RegisterFiles(addr, srv.File())
 	return nil
 }
@@ -152,13 +152,13 @@ func (srv *Server) ListenTLS() error {
 		addr = ":https"
 	}
 
-	l, err := srv.getListener(addr)
+	l, err := srv.getOrCreateListener(addr)
 	if err != nil {
 		return err
 	}
 
-	srv.innerListener = newRestListener(l, srv)
-	srv.restListener = tls.NewListener(srv.innerListener, srv.TLSConfig)
+	srv.tcpListener = NewTcpListener(l, srv)
+	srv.Listener = tls.NewListener(srv.tcpListener, srv.TLSConfig)
 	grace.RegisterFiles(addr, srv.File())
 	return nil
 }
@@ -181,7 +181,7 @@ func (srv *Server) ListenAndServeTLS(certFile, keyFile string) (err error) {
 		}
 	}
 	if srv.TLSConfig.NextProtos == nil {
-		srv.TLSConfig.NextProtos = []string{"http/1.1"}
+		srv.TLSConfig.NextProtos = []string{"h2", "http/1.1"}
 	}
 
 	err = srv.ListenTLS()
@@ -191,22 +191,19 @@ func (srv *Server) ListenAndServeTLS(certFile, keyFile string) (err error) {
 	return srv.Serve()
 }
 
+// RegisterListener register the instance created outside by net.Listen() in server
 func (srv *Server) RegisterListener(l net.Listener) {
-	srv.registerListener = l
+	srv.netListener = l
 }
 
-func (srv *Server) getListener(addr string) (l net.Listener, err error) {
-	l = srv.registerListener
-	if l != nil {
-		return
-	}
+func (srv *Server) getOrCreateListener(addr string) (l net.Listener, err error) {
 	if !grace.IsFork() {
-		return net.Listen(srv.Network, addr)
+		return srv.newListener(addr)
 	}
 
 	offset := grace.ExtraFileOrder(addr)
 	if offset < 0 {
-		return net.Listen(srv.Network, addr)
+		return srv.newListener(addr)
 	}
 
 	f := os.NewFile(uintptr(3+offset), "")
@@ -218,13 +215,21 @@ func (srv *Server) getListener(addr string) (l net.Listener, err error) {
 	return
 }
 
+func (srv *Server) newListener(addr string) (net.Listener, error) {
+	l := srv.netListener
+	if l != nil {
+		return l, nil
+	}
+	return net.Listen(srv.Network, addr)
+}
+
 func (srv *Server) Shutdown() {
 	if srv.state != serverStateRunning {
 		return
 	}
 
 	srv.state = serverStateTerminating
-	err := srv.restListener.Close()
+	err := srv.Listener.Close()
 	if err != nil {
 		util.Logger().Errorf(err, "server listener close failed")
 	}
@@ -264,10 +269,10 @@ func (srv *Server) gracefulStop(d time.Duration) {
 }
 
 func (srv *Server) File() *os.File {
-	switch srv.restListener.(type) {
-	case *restListener:
-		return srv.restListener.(*restListener).File()
+	switch srv.Listener.(type) {
+	case *TcpListener:
+		return srv.Listener.(*TcpListener).File()
 	default:
-		return srv.innerListener.File()
+		return srv.tcpListener.File()
 	}
 }
diff --git a/pkg/rpc/grpc.go b/pkg/rpc/grpc.go
index bc47949d..eaa9ec7a 100644
--- a/pkg/rpc/grpc.go
+++ b/pkg/rpc/grpc.go
@@ -30,7 +30,7 @@ func RegisterService(f RegisterServiceFunc) {
 	registerFuncs = append(registerFuncs, f)
 }
 
-func RegisterServer(s *grpc.Server) {
+func RegisterGRpcServer(s *grpc.Server) {
 	for _, f := range registerFuncs {
 		f(s)
 	}
diff --git a/pkg/async/executor.go b/pkg/task/executor.go
similarity index 99%
rename from pkg/async/executor.go
rename to pkg/task/executor.go
index d8845e3e..6e242855 100644
--- a/pkg/async/executor.go
+++ b/pkg/task/executor.go
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package async
+package task
 
 import (
 	"errors"
diff --git a/pkg/async/async.go b/pkg/task/service.go
similarity index 93%
rename from pkg/async/async.go
rename to pkg/task/service.go
index 87cc07b0..10dad534 100644
--- a/pkg/async/async.go
+++ b/pkg/task/service.go
@@ -14,12 +14,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package async
+package task
 
 import "sync"
 
 var (
-	service *TaskService
+	service TaskService
 	once    sync.Once
 )
 
@@ -27,7 +27,7 @@ func init() {
 	service = NewTaskService()
 }
 
-func Service() *TaskService {
+func Service() TaskService {
 	once.Do(service.Run)
 	return service
 }
diff --git a/pkg/async/service.go b/pkg/task/service_async.go
similarity index 85%
rename from pkg/async/service.go
rename to pkg/task/service_async.go
index 3551e560..53f8007d 100644
--- a/pkg/async/service.go
+++ b/pkg/task/service_async.go
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package async
+package task
 
 import (
 	"errors"
@@ -35,11 +35,10 @@ const (
 
 type executorWithTTL struct {
 	*Executor
-
 	TTL int64
 }
 
-type TaskService struct {
+type AsyncTaskService struct {
 	executors map[string]*executorWithTTL
 	goroutine *util.GoRoutine
 	lock      sync.RWMutex
@@ -47,7 +46,7 @@ type TaskService struct {
 	isClose   bool
 }
 
-func (lat *TaskService) getOrNewExecutor(task Task) (s *Executor, isNew bool) {
+func (lat *AsyncTaskService) getOrNewExecutor(task Task) (s *Executor, isNew bool) {
 	var (
 		ok  bool
 		key = task.Key()
@@ -73,7 +72,7 @@ func (lat *TaskService) getOrNewExecutor(task Task) (s *Executor, isNew bool) {
 	return se.Executor, isNew
 }
 
-func (lat *TaskService) Add(ctx context.Context, task Task) error {
+func (lat *AsyncTaskService) Add(ctx context.Context, task Task) error {
 	if task == nil || ctx == nil {
 		return errors.New("invalid parameters")
 	}
@@ -86,14 +85,14 @@ func (lat *TaskService) Add(ctx context.Context, task Task) error {
 	return s.AddTask(task)
 }
 
-func (lat *TaskService) removeExecutor(key string) {
+func (lat *AsyncTaskService) removeExecutor(key string) {
 	if s, ok := lat.executors[key]; ok {
 		s.Close()
 		delete(lat.executors, key)
 	}
 }
 
-func (lat *TaskService) LatestHandled(key string) (Task, error) {
+func (lat *AsyncTaskService) LatestHandled(key string) (Task, error) {
 	lat.lock.RLock()
 	s, ok := lat.executors[key]
 	lat.lock.RUnlock()
@@ -103,7 +102,7 @@ func (lat *TaskService) LatestHandled(key string) (Task, error) {
 	return s.latestTask, nil
 }
 
-func (lat *TaskService) daemon(ctx context.Context) {
+func (lat *AsyncTaskService) daemon(ctx context.Context) {
 	util.SafeCloseChan(lat.ready)
 	ticker := time.NewTicker(removeExecutorInterval)
 	max := 0
@@ -112,7 +111,7 @@ func (lat *TaskService) daemon(ctx context.Context) {
 	for {
 		select {
 		case <-ctx.Done():
-			util.Logger().Debugf("daemon thread exited for TaskService is stopped")
+			util.Logger().Debugf("daemon thread exited for AsyncTaskService stopped")
 			return
 		case <-timer.C:
 			lat.lock.RLock()
@@ -166,7 +165,7 @@ func (lat *TaskService) daemon(ctx context.Context) {
 	}
 }
 
-func (lat *TaskService) Run() {
+func (lat *AsyncTaskService) Run() {
 	lat.lock.Lock()
 	if !lat.isClose {
 		lat.lock.Unlock()
@@ -177,7 +176,7 @@ func (lat *TaskService) Run() {
 	lat.goroutine.Do(lat.daemon)
 }
 
-func (lat *TaskService) Stop() {
+func (lat *AsyncTaskService) Stop() {
 	lat.lock.Lock()
 	if lat.isClose {
 		lat.lock.Unlock()
@@ -196,11 +195,11 @@ func (lat *TaskService) Stop() {
 	util.SafeCloseChan(lat.ready)
 }
 
-func (lat *TaskService) Ready() <-chan struct{} {
+func (lat *AsyncTaskService) Ready() <-chan struct{} {
 	return lat.ready
 }
 
-func (lat *TaskService) renew() {
+func (lat *AsyncTaskService) renew() {
 	newExecutor := make(map[string]*executorWithTTL)
 	for k, e := range lat.executors {
 		newExecutor[k] = e
@@ -208,12 +207,12 @@ func (lat *TaskService) renew() {
 	lat.executors = newExecutor
 }
 
-func NewTaskService() (lat *TaskService) {
-	lat = &TaskService{
+func NewTaskService() TaskService {
+	lat := &AsyncTaskService{
 		goroutine: util.NewGo(context.Background()),
 		ready:     make(chan struct{}),
 		isClose:   true,
 	}
 	lat.renew()
-	return
+	return lat
 }
diff --git a/pkg/async/async_test.go b/pkg/task/service_test.go
similarity index 99%
rename from pkg/async/async_test.go
rename to pkg/task/service_test.go
index 77593d2c..6b4bfe6e 100644
--- a/pkg/async/async_test.go
+++ b/pkg/task/service_test.go
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package async
+package task
 
 import (
 	"testing"
@@ -58,6 +58,7 @@ func (tt *testTask) Do(ctx context.Context) error {
 func TestBaseAsyncTasker_AddTask(t *testing.T) {
 	at := NewTaskService()
 	at.Run()
+	<-at.Ready()
 	defer at.Stop()
 
 	ctx, cancel := context.WithCancel(context.Background())
diff --git a/pkg/task/task.go b/pkg/task/task.go
new file mode 100644
index 00000000..bff92119
--- /dev/null
+++ b/pkg/task/task.go
@@ -0,0 +1,34 @@
+/*
+ * 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 task
+
+import "golang.org/x/net/context"
+
+type Task interface {
+	Key() string
+	Do(ctx context.Context) error
+	Err() error
+}
+
+type TaskService interface {
+	Add(ctx context.Context, task Task) error
+	LatestHandled(key string) (Task, error)
+
+	Run()
+	Stop()
+	Ready() <-chan struct{}
+}
diff --git a/pkg/tlsutil/tlsutil.go b/pkg/tlsutil/tlsutil.go
index 063b30f5..b50acd17 100644
--- a/pkg/tlsutil/tlsutil.go
+++ b/pkg/tlsutil/tlsutil.go
@@ -98,8 +98,6 @@ func LoadTLSCertificate(certFile, keyFile, plainPassphase string) (tlsCert []tls
 	if x509.IsEncryptedPEMBlock(keyBlock) {
 		plainPassphaseBytes := util.StringToBytesWithNoCopy(plainPassphase)
 		keyData, err := x509.DecryptPEMBlock(keyBlock, plainPassphaseBytes)
-		util.ClearStringMemory(&plainPassphase)
-		util.ClearByteMemory(plainPassphaseBytes)
 		if err != nil {
 			util.Logger().Errorf(err, "decrypt key file %s failed.", keyFile)
 			return nil, err
@@ -186,10 +184,12 @@ func GetServerTLSConfig(opts ...SSLConfigOption) (tlsConfig *tls.Config, err err
 		ClientCAs:                pool,
 		Certificates:             certs,
 		CipherSuites:             cfg.CipherSuites,
+		CurvePreferences:         []tls.CurveID{tls.X25519, tls.CurveP256},
 		PreferServerCipherSuites: true,
 		ClientAuth:               clientAuthMode,
 		MinVersion:               cfg.MinVersion,
 		MaxVersion:               cfg.MaxVersion,
+		NextProtos:               []string{"h2", "http/1.1"},
 	}
 
 	return tlsConfig, nil
diff --git a/pkg/tlsutil/tlsutil_test.go b/pkg/tlsutil/tlsutil_test.go
index 4a51f746..fb2dc7df 100644
--- a/pkg/tlsutil/tlsutil_test.go
+++ b/pkg/tlsutil/tlsutil_test.go
@@ -22,6 +22,8 @@ import (
 	"testing"
 )
 
+const sslRoot = "../../examples/service_center/ssl/"
+
 func TestParseDefaultSSLCipherSuites(t *testing.T) {
 	c := ParseDefaultSSLCipherSuites("")
 	if c != nil {
@@ -42,7 +44,6 @@ func TestParseDefaultSSLCipherSuites(t *testing.T) {
 }
 
 func TestGetServerTLSConfig(t *testing.T) {
-	sslRoot := "../../etc/ssl/"
 	pw, _ := ioutil.ReadFile(sslRoot + "cert_pwd")
 	opts := append(DefaultServerTLSOptions(),
 		WithVerifyPeer(true),
@@ -78,7 +79,6 @@ func TestGetServerTLSConfig(t *testing.T) {
 }
 
 func TestGetClientTLSConfig(t *testing.T) {
-	sslRoot := "../../etc/ssl/"
 	pw, _ := ioutil.ReadFile(sslRoot + "cert_pwd")
 	opts := append(DefaultServerTLSOptions(),
 		WithVerifyPeer(true),
diff --git a/pkg/util/context.go b/pkg/util/context.go
index 565cf697..79fb515e 100644
--- a/pkg/util/context.go
+++ b/pkg/util/context.go
@@ -44,7 +44,10 @@ func (c *StringContext) Value(key interface{}) interface{} {
 	if !ok {
 		return c.parentCtx.Value(key)
 	}
-	v, _ := c.kv.Get(k)
+	v, ok := c.kv.Get(k)
+	if !ok {
+		return c.parentCtx.Value(key)
+	}
 	return v
 }
 
@@ -57,7 +60,7 @@ func NewStringContext(ctx context.Context) *StringContext {
 	if !ok {
 		strCtx = &StringContext{
 			parentCtx: ctx,
-			kv:        NewConcurrentMap(10),
+			kv:        NewConcurrentMap(0),
 		}
 	}
 	return strCtx
@@ -74,13 +77,13 @@ func CloneContext(ctx context.Context) context.Context {
 	if !ok {
 		return &StringContext{
 			parentCtx: ctx,
-			kv:        NewConcurrentMap(10),
+			kv:        NewConcurrentMap(0),
 		}
 	}
 
 	strCtx := &StringContext{
 		parentCtx: ctx,
-		kv:        NewConcurrentMap(old.kv.Size()),
+		kv:        NewConcurrentMap(0),
 	}
 
 	old.kv.ForEach(func(item MapItem) bool {
diff --git a/pkg/util/context_test.go b/pkg/util/context_test.go
new file mode 100644
index 00000000..4c079b92
--- /dev/null
+++ b/pkg/util/context_test.go
@@ -0,0 +1,77 @@
+/*
+ * 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 util
+
+import (
+	"context"
+	"net/http"
+	"testing"
+)
+
+func TestSetContext(t *testing.T) {
+	ctx := context.WithValue(context.Background(), "z", "z")
+	ctx = context.WithValue(ctx, 1, 1)
+	ctx = SetDomainProject(ctx, "x", "y")
+	if ParseDomainProject(ctx) != "x/y" {
+		t.Fatalf("TestSetContext failed")
+	}
+	if ctx.Value("z") != "z" {
+		t.Fatalf("TestSetContext failed")
+	}
+	if ctx.Value(1) != 1 {
+		t.Fatalf("TestSetContext failed")
+	}
+
+	nctx := CloneContext(ctx)
+	if nctx == ctx {
+		t.Fatalf("TestSetContext failed")
+	}
+	if ParseDomainProject(nctx) != "x/y" {
+		t.Fatalf("TestSetContext failed")
+	}
+	nctx = SetDomainProject(nctx, "x", "x")
+	if ParseDomainProject(ctx) != "x/y" {
+		t.Fatalf("TestSetContext failed")
+	}
+	if ParseDomainProject(nctx) != "x/x" {
+		t.Fatalf("TestSetContext failed")
+	}
+	if nctx.Value("z") != "z" {
+		t.Fatalf("TestSetContext failed")
+	}
+	if nctx.Value(1) != 1 {
+		t.Fatalf("TestSetContext failed")
+	}
+
+	ctx = SetTargetDomainProject(ctx, "x", "x")
+	if ParseTargetDomainProject(ctx) != "x/x" {
+		t.Fatalf("TestSetContext failed")
+	}
+	if ctx.Value("z") != "z" {
+		t.Fatalf("TestSetContext failed")
+	}
+	if ctx.Value(1) != 1 {
+		t.Fatalf("TestSetContext failed")
+	}
+
+	req, _ := http.NewRequest(http.MethodGet, "http://127.0.0.1:30100", nil)
+	ctx = req.Context()
+	SetRequestContext(req, "x", "y")
+	if req.Context() == ctx {
+		t.Fatalf("TestSetContext failed")
+	}
+}
diff --git a/pkg/util/goroutines.go b/pkg/util/goroutines.go
index 74689f3e..03d3d43f 100644
--- a/pkg/util/goroutines.go
+++ b/pkg/util/goroutines.go
@@ -22,26 +22,50 @@ import (
 	"time"
 )
 
-var GlobalPoolConfig = &PoolConfig{
-	Concurrent:  1000,
-	WaitTimeout: time.Second,
-	IdleTimeout: 60 * time.Second,
+var GlobalPoolConfig = PoolConfigure()
+
+var defaultGo *GoRoutine
+
+func init() {
+	defaultGo = NewGo(context.Background())
 }
 
 type PoolConfig struct {
 	Concurrent  int
-	WaitTimeout time.Duration
 	IdleTimeout time.Duration
 }
 
+func (c *PoolConfig) Workers(max int) *PoolConfig {
+	c.Concurrent = max
+	return c
+}
+
+func (c *PoolConfig) Idle(time time.Duration) *PoolConfig {
+	c.IdleTimeout = time
+	return c
+}
+
+func PoolConfigure() *PoolConfig {
+	return &PoolConfig{
+		Concurrent:  1000,
+		IdleTimeout: 60 * time.Second,
+	}
+}
+
 type GoRoutine struct {
-	ctx        context.Context
-	cancel     context.CancelFunc
-	wg         sync.WaitGroup
-	mux        sync.RWMutex
-	closed     bool
-	task       chan func(ctx context.Context)
-	concurrent chan struct{}
+	Cfg *PoolConfig
+
+	// job context
+	ctx    context.Context
+	cancel context.CancelFunc
+	// pending is the chan to block GoRoutine.Do() when go pool is full
+	pending chan func(ctx context.Context)
+	// workers is the counter of the worker
+	workers chan struct{}
+
+	mux    sync.RWMutex
+	wg     sync.WaitGroup
+	closed bool
 }
 
 func (g *GoRoutine) execute(f func(ctx context.Context)) {
@@ -49,22 +73,22 @@ func (g *GoRoutine) execute(f func(ctx context.Context)) {
 	f(g.ctx)
 }
 
-func (g *GoRoutine) Do(f func(context.Context)) {
+func (g *GoRoutine) Do(f func(context.Context)) *GoRoutine {
 	defer RecoverAndReport()
 	select {
-	case g.task <- f:
-		return
-	case g.concurrent <- struct{}{}:
+	case g.pending <- f:
+	case g.workers <- struct{}{}:
+		g.wg.Add(1)
 		go g.loop(f)
 	}
+	return g
 }
 
 func (g *GoRoutine) loop(f func(context.Context)) {
-	g.wg.Add(1)
 	defer g.wg.Done()
-	defer func() { <-g.concurrent }()
+	defer func() { <-g.workers }()
 
-	timer := time.NewTimer(GlobalPoolConfig.IdleTimeout)
+	timer := time.NewTimer(g.Cfg.IdleTimeout)
 	defer timer.Stop()
 	for {
 		g.execute(f)
@@ -72,16 +96,17 @@ func (g *GoRoutine) loop(f func(context.Context)) {
 		select {
 		case <-timer.C:
 			return
-		case f = <-g.task:
+		case f = <-g.pending:
 			if f == nil {
 				return
 			}
-			ResetTimer(timer, GlobalPoolConfig.IdleTimeout)
+			ResetTimer(timer, g.Cfg.IdleTimeout)
 		}
 	}
 }
 
-func (g *GoRoutine) Close(wait bool) {
+// Close will call context.Cancel(), so all goroutines maybe exit when job does not complete
+func (g *GoRoutine) Close(grace bool) {
 	g.mux.Lock()
 	if g.closed {
 		g.mux.Unlock()
@@ -90,22 +115,43 @@ func (g *GoRoutine) Close(wait bool) {
 	g.closed = true
 	g.mux.Unlock()
 
-	close(g.task)
-	close(g.concurrent)
+	close(g.pending)
+	close(g.workers)
 	g.cancel()
-	if wait {
-		g.Wait()
+	if grace {
+		g.wg.Wait()
 	}
 }
 
-func (g *GoRoutine) Wait() {
+// Done will wait for all goroutines complete the jobs and then close the pool
+func (g *GoRoutine) Done() {
+	g.mux.Lock()
+	if g.closed {
+		g.mux.Unlock()
+		return
+	}
+	g.closed = true
+	g.mux.Unlock()
+
+	close(g.pending)
+	close(g.workers)
 	g.wg.Wait()
 }
 
-var defaultGo *GoRoutine
-
-func init() {
-	defaultGo = NewGo(context.Background())
+func NewGo(ctx context.Context, cfgs ...*PoolConfig) *GoRoutine {
+	ctx, cancel := context.WithCancel(ctx)
+	if len(cfgs) == 0 {
+		cfgs = append(cfgs, GlobalPoolConfig)
+	}
+	cfg := cfgs[0]
+	gr := &GoRoutine{
+		Cfg:     cfg,
+		ctx:     ctx,
+		cancel:  cancel,
+		pending: make(chan func(context.Context)),
+		workers: make(chan struct{}, cfg.Concurrent),
+	}
+	return gr
 }
 
 func Go(f func(context.Context)) {
@@ -116,14 +162,3 @@ func GoCloseAndWait() {
 	defaultGo.Close(true)
 	Logger().Debugf("all goroutines exited")
 }
-
-func NewGo(ctx context.Context) *GoRoutine {
-	ctx, cancel := context.WithCancel(ctx)
-	gr := &GoRoutine{
-		ctx:        ctx,
-		cancel:     cancel,
-		task:       make(chan func(context.Context)),
-		concurrent: make(chan struct{}, GlobalPoolConfig.Concurrent),
-	}
-	return gr
-}
diff --git a/pkg/util/goroutines_test.go b/pkg/util/goroutines_test.go
index fe14e195..997ca95d 100644
--- a/pkg/util/goroutines_test.go
+++ b/pkg/util/goroutines_test.go
@@ -55,12 +55,10 @@ func TestGoRoutine_Do(t *testing.T) {
 }
 
 func TestGoRoutine_Wait(t *testing.T) {
-	GlobalPoolConfig.IdleTimeout = time.Second
-
 	var mux sync.Mutex
 	MAX := 10
 	resultArr := make([]int, 0, MAX)
-	test := NewGo(context.Background())
+	test := NewGo(context.Background(), PoolConfigure().Idle(time.Second).Workers(5))
 	for i := 0; i < MAX; i++ {
 		test.Do(func(ctx context.Context) {
 			select {
@@ -75,14 +73,14 @@ func TestGoRoutine_Wait(t *testing.T) {
 	}
 	<-time.After(time.Second)
 	fmt.Println("waiting for all goroutines finish.")
-	test.Wait()
+	test.Done()
 	fmt.Println(resultArr)
 	if len(resultArr) != MAX {
 		t.Fatalf("fail to wait all goroutines finish. expected %d to %d", len(resultArr), MAX)
 	}
 }
 
-func TestGoRoutine_Close(t *testing.T) {
+func TestGoRoutine_Exception1(t *testing.T) {
 	test := NewGo(context.Background())
 	test.Do(func(ctx context.Context) {
 		select {
@@ -98,11 +96,11 @@ func TestGoRoutine_Close(t *testing.T) {
 			t.Fatalf("can not execute f after closed.")
 		}
 	})
-	test.Wait()
+	test.Done()
 	test.Close(true)
 }
 
-func TestGo(t *testing.T) {
+func TestGoRoutine_Exception2(t *testing.T) {
 	Go(func(ctx context.Context) {
 		for {
 			select {
diff --git a/pkg/util/log.go b/pkg/util/log.go
index bc82a899..38d80519 100644
--- a/pkg/util/log.go
+++ b/pkg/util/log.go
@@ -31,10 +31,10 @@ import (
 
 //log var
 var (
-	LOGGER             lager.Logger
-	defaultLagerConfig = stlager.DefaultConfig()
-	loggerConfig       LoggerConfig
-	logLevel           lager.LogLevel
+	globalLogger     lager.Logger
+	globalConfig     LoggerConfig
+	innerLagerConfig = stlager.DefaultConfig()
+	logLevel         lager.LogLevel
 
 	loggers     map[string]lager.Logger
 	loggerNames map[string]string
@@ -57,8 +57,8 @@ type LoggerConfig struct {
 func init() {
 	loggers = make(map[string]lager.Logger, 10)
 	loggerNames = make(map[string]string, 10)
-	// make LOGGER do not be nil, new a stdout logger
-	LOGGER = NewLogger(fromLagerConfig(defaultLagerConfig))
+	// make globalLogger do not be nil, new a stdout for it
+	globalLogger = NewLogger(fromLagerConfig(innerLagerConfig))
 }
 
 func fromLagerConfig(c *stlager.Config) LoggerConfig {
@@ -89,14 +89,12 @@ func NewLogger(cfg LoggerConfig) lager.Logger {
 }
 
 func InitGlobalLogger(cfg LoggerConfig) {
-	// renew the global logger
+	// renew the global globalLogger
 	if len(cfg.LoggerLevel) == 0 {
-		cfg.LoggerLevel = defaultLagerConfig.LoggerLevel
+		cfg.LoggerLevel = innerLagerConfig.LoggerLevel
 	}
-	loggerConfig = cfg
-	LOGGER = NewLogger(cfg)
-	// log rotate
-	RunLogDirRotate(cfg)
+	globalConfig = cfg
+	globalLogger = NewLogger(cfg)
 	// recreate the deleted log file
 	switch strings.ToUpper(cfg.LoggerLevel) {
 	case "INFO":
@@ -110,12 +108,15 @@ func InitGlobalLogger(cfg LoggerConfig) {
 	default:
 		logLevel = lager.DEBUG
 	}
+
+	RunLogDirRotate(cfg)
+
 	monitorLogFile()
 }
 
 func Logger() lager.Logger {
 	if len(loggerNames) == 0 {
-		return LOGGER
+		return globalLogger
 	}
 	funcFullName := getCalleeFuncName()
 
@@ -141,19 +142,19 @@ func Logger() lager.Logger {
 		loggersMux.Lock()
 		logger, ok = loggers[logFile]
 		if !ok {
-			cfg := loggerConfig
+			cfg := globalConfig
 			if len(cfg.LoggerFile) != 0 {
 				cfg.LoggerFile = filepath.Join(filepath.Dir(cfg.LoggerFile), logFile+".log")
 			}
 			logger = NewLogger(cfg)
 			loggers[logFile] = logger
-			LOGGER.Warnf(nil, "match %s, new logger %s for %s", prefix, logFile, funcFullName)
+			logger.Warnf(nil, "match %s, new globalLogger %s for %s", prefix, logFile, funcFullName)
 		}
 		loggersMux.Unlock()
 		return logger
 	}
 
-	return LOGGER
+	return globalLogger
 }
 
 func getCalleeFuncName() string {
@@ -188,7 +189,7 @@ func CustomLogger(pkgOrFunc, fileName string) {
 }
 
 func monitorLogFile() {
-	if len(loggerConfig.LoggerFile) == 0 {
+	if len(globalConfig.LoggerFile) == 0 {
 		return
 	}
 	Go(func(ctx context.Context) {
@@ -198,18 +199,20 @@ func monitorLogFile() {
 				return
 			case <-time.After(time.Minute):
 				Logger().Debug(fmt.Sprintf("Check log file at %s", time.Now()))
+				if PathExist(globalConfig.LoggerFile) {
+					continue
+				}
 
-				if !PathExist(loggerConfig.LoggerFile) {
-					file, err := os.OpenFile(loggerConfig.LoggerFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0600)
-					if err != nil {
-						Logger().Errorf(err, "Create log file failed.")
-						return
-					}
-					// TODO Here will lead to file handle leak
-					sink := lager.NewReconfigurableSink(lager.NewWriterSink("file", file, lager.DEBUG), logLevel)
-					Logger().RegisterSink(sink)
-					Logger().Errorf(nil, "log file is removed, create again.")
+				file, err := os.OpenFile(globalConfig.LoggerFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0600)
+				if err != nil {
+					Logger().Errorf(err, "Create log file failed.")
+					continue
 				}
+
+				// TODO Here will lead to file handle leak
+				sink := lager.NewReconfigurableSink(lager.NewWriterSink("file", file, lager.DEBUG), logLevel)
+				Logger().RegisterSink(sink)
+				Logger().Errorf(nil, "log file is removed, create again.")
 			}
 		}
 	})
diff --git a/pkg/util/log_test.go b/pkg/util/log_test.go
index d513e22a..9d0bdbf7 100644
--- a/pkg/util/log_test.go
+++ b/pkg/util/log_test.go
@@ -18,29 +18,42 @@ package util
 
 import (
 	"testing"
+	"time"
 )
 
 func TestLogger(t *testing.T) {
+	InitGlobalLogger(LoggerConfig{
+		LoggerLevel: "DEBUG",
+	})
+
 	CustomLogger("Not Exist", "testDefaultLOGGER")
 	l := Logger()
-	if l != LOGGER {
-		t.Fatalf("should equal to LOGGER")
+	if l != globalLogger {
+		t.Fatalf("should equal to globalLogger")
 	}
 	CustomLogger("TestLogger", "testFuncName")
 	l = Logger()
-	if l == LOGGER || l == nil {
+	if l == globalLogger || l == nil {
 		t.Fatalf("should create a new instance for 'TestLogger'")
 	}
 	s := Logger()
 	if l != s {
-		t.Fatalf("should be the same logger")
+		t.Fatalf("should be the same globalLogger")
 	}
 	CustomLogger("github.com/apache/incubator-servicecomb-service-center/pkg/util", "testPkgPath")
 	l = Logger()
-	if l == LOGGER || l == nil {
+	if l == globalLogger || l == nil {
 		t.Fatalf("should create a new instance for 'util'")
 	}
 	// l.Infof("OK")
+
+	LogDebugOrWarnf(time.Now().Add(-time.Second), "x")
+	LogNilOrWarnf(time.Now().Add(-time.Second), "x")
+	LogInfoOrWarnf(time.Now().Add(-time.Second), "x")
+
+	LogDebugOrWarnf(time.Now(), "x")
+	LogNilOrWarnf(time.Now(), "x")
+	LogInfoOrWarnf(time.Now(), "x")
 }
 
 func BenchmarkLogger(b *testing.B) {
diff --git a/pkg/util/logrotate.go b/pkg/util/logrotate.go
index 919ee727..138677d0 100644
--- a/pkg/util/logrotate.go
+++ b/pkg/util/logrotate.go
@@ -295,6 +295,9 @@ func CopyFile(srcFile, destFile string) error {
 }
 
 func RunLogDirRotate(cfg LoggerConfig) {
+	if len(cfg.LoggerFile) == 0 {
+		return
+	}
 	Go(func(ctx context.Context) {
 		for {
 			select {
diff --git a/pkg/util/logrotate_test.go b/pkg/util/logrotate_test.go
index 407baa56..4ffad2ff 100644
--- a/pkg/util/logrotate_test.go
+++ b/pkg/util/logrotate_test.go
@@ -17,6 +17,7 @@
 package util
 
 import (
+	"os"
 	"testing"
 )
 
@@ -25,4 +26,20 @@ func TestLogRotate(t *testing.T) {
 	if s != "./sc.log" {
 		t.Fatal("pathReplacer failed", s)
 	}
+
+	LogRotate("../../etc", 1, 1)
+
+	LogRotateFile("../../etc/conf/app.conf", 1, 1)
+
+	if err := compressFile(
+		"../../etc/conf/app.conf",
+		"app",
+		false); err != nil {
+		t.Fatal("TestLogRotate failed", err)
+	}
+
+	os.Chmod("../../etc/conf/app.conf.zip", 0640)
+	if err := removeFile("../../etc/conf/app.conf.zip"); err != nil {
+		t.Fatal("TestLogRotate failed", err)
+	}
 }
diff --git a/pkg/util/net.go b/pkg/util/net.go
index 60825069..c3049336 100644
--- a/pkg/util/net.go
+++ b/pkg/util/net.go
@@ -38,18 +38,6 @@ func GetIPFromContext(ctx context.Context) string {
 	return v
 }
 
-func UrlEncode(keys map[string]string) string {
-	l := len(keys)
-	if l == 0 {
-		return ""
-	}
-	arr := make([]string, 0, l)
-	for k, v := range keys {
-		arr = append(arr, url.QueryEscape(k)+"="+url.QueryEscape(v))
-	}
-	return StringJoin(arr, "&")
-}
-
 func ParseEndpoint(ep string) (string, error) {
 	u, err := url.Parse(ep)
 	if err != nil {
@@ -79,11 +67,7 @@ func GetRealIP(r *http.Request) string {
 			return ip
 		}
 	}
-	addrs := strings.Split(r.RemoteAddr, ":")
-	if len(addrs) > 0 {
-		return addrs[0]
-	}
-	return ""
+	return strings.Split(r.RemoteAddr, ":")[0]
 }
 
 func InetNtoIP(ipnr uint32) net.IP {
diff --git a/pkg/util/net_test.go b/pkg/util/net_test.go
index 0116f3db..a5dbfdf4 100644
--- a/pkg/util/net_test.go
+++ b/pkg/util/net_test.go
@@ -16,7 +16,10 @@
  */
 package util
 
-import "testing"
+import (
+	"net/http"
+	"testing"
+)
 
 const (
 	ip1 = "127.0.0.1"       // 2130706433
@@ -94,3 +97,45 @@ func TestParseEndpoint(t *testing.T) {
 		t.Fatalf("ParseEndpoint(\"rest://[fe80::f816:3eff:fe17:c38b%%25eht0]:30100/?a=b\") failed, err = %s, ep = %s", err, ep)
 	}
 }
+
+func TestParseRequestURL(t *testing.T) {
+	req, _ := http.NewRequest(http.MethodGet, "https://127.0.0.1:30100/x/?a=b&c=d#e", nil)
+	url := ParseRequestURL(req)
+	if url != "https://127.0.0.1:30100/x/?a=b&c=d#e" {
+		t.Fatalf("TestParseRequestURL failed")
+	}
+	req.URL.Scheme = ""
+	req.Host = "127.0.0.1:30100"
+	req.RequestURI = "/x/?a=b&c=d#e"
+	url = ParseRequestURL(req)
+	if url != "http://127.0.0.1:30100/x/?a=b&c=d#e" {
+		t.Fatalf("TestParseRequestURL failed")
+	}
+}
+
+func TestGetRealIP(t *testing.T) {
+	req, _ := http.NewRequest(http.MethodGet, "https://127.0.0.1:30100/x/?a=b&c=d#e", nil)
+	req.RemoteAddr = "127.0.0.1:30100"
+	ip := GetRealIP(req)
+	if ip != "127.0.0.1" {
+		t.Fatalf("TestGetRealIP failed")
+	}
+
+	req.Header.Set("X-Real-Ip", "255.255.255.255")
+	ip = GetRealIP(req)
+	if ip != "127.0.0.1" {
+		t.Fatalf("TestGetRealIP failed")
+	}
+
+	req.Header.Set("X-Real-Ip", "4.4.4.4")
+	ip = GetRealIP(req)
+	if ip != "4.4.4.4" {
+		t.Fatalf("TestGetRealIP failed")
+	}
+
+	req.Header.Set("X-Forwarded-For", "1.1.1.1, 2.2.2.2, 3.3.3.3")
+	ip = GetRealIP(req)
+	if ip != "1.1.1.1" {
+		t.Fatalf("TestGetRealIP failed")
+	}
+}
diff --git a/pkg/util/reflect.go b/pkg/util/reflect.go
index 695a7b91..73a54665 100644
--- a/pkg/util/reflect.go
+++ b/pkg/util/reflect.go
@@ -18,6 +18,8 @@ package util
 
 import (
 	"reflect"
+	"runtime"
+	"strings"
 	"sync"
 	"unsafe"
 )
@@ -26,25 +28,34 @@ var (
 	reflector      *Reflector
 	sliceTypeSize  = uint64(reflect.TypeOf(reflect.SliceHeader{}).Size())
 	stringTypeSize = uint64(reflect.TypeOf(reflect.StringHeader{}).Size())
+	unknown        = new(reflectObject)
 )
 
 func init() {
 	reflector = &Reflector{
-		types: make(map[*uintptr]reflectObject, 100),
+		types: make(map[*uintptr]*reflectObject),
 	}
 }
 
 type reflectObject struct {
-	Type   reflect.Type
+	// full name
+	FullName string
+	Type     reflect.Type
+	// if type is not struct, Fields is nil
 	Fields []reflect.StructField
 }
 
+// Name returns a short name of the object type
+func (o *reflectObject) Name() string {
+	return FileLastName(o.FullName)
+}
+
 type Reflector struct {
-	types map[*uintptr]reflectObject
+	types map[*uintptr]*reflectObject
 	mux   sync.RWMutex
 }
 
-func (r *Reflector) Load(obj interface{}) reflectObject {
+func (r *Reflector) Load(obj interface{}) *reflectObject {
 	r.mux.RLock()
 	itab := *(**uintptr)(unsafe.Pointer(&obj))
 	t, ok := r.types[itab]
@@ -60,38 +71,48 @@ func (r *Reflector) Load(obj interface{}) reflectObject {
 		return t
 	}
 
+	t = new(reflectObject)
 	v := reflect.ValueOf(obj)
 	if !v.IsValid() {
 		r.mux.Unlock()
-		return reflectObject{}
+		return unknown
 	}
 	switch v.Kind() {
 	case reflect.Ptr:
-		if v.IsNil() {
-			r.mux.Unlock()
-			return reflectObject{}
-		}
 		fallthrough
 	case reflect.Interface:
 		r.mux.Unlock()
-		return r.Load(v.Elem().Interface())
-	default:
-		t = reflectObject{
-			Type: reflect.TypeOf(obj),
+		if v.IsNil() {
+			return unknown
 		}
-	}
-
-	if v.Kind() != reflect.Struct {
-		r.mux.Unlock()
-		return t
-	}
+		e := v.Elem()
+		if e.CanInterface() {
+			return r.Load(e.Interface())
+		}
+		return unknown
+	default:
+		t.Type = reflect.TypeOf(obj)
 
-	fl := t.Type.NumField()
-	if fl > 0 {
-		t.Fields = make([]reflect.StructField, fl)
-		for i := 0; i < fl; i++ {
-			f := t.Type.Field(i)
-			t.Fields[i] = f
+		switch v.Kind() {
+		case reflect.Func:
+			r.mux.Unlock()
+			f := runtime.FuncForPC(v.Pointer())
+			if f == nil {
+				return unknown
+			}
+			t.FullName = f.Name()
+			return t
+		case reflect.Struct:
+			if fl := t.Type.NumField(); fl > 0 {
+				t.Fields = make([]reflect.StructField, fl)
+				for i := 0; i < fl; i++ {
+					f := t.Type.Field(i)
+					t.Fields[i] = f
+				}
+			}
+			fallthrough
+		default:
+			t.FullName = t.Type.PkgPath() + "." + t.Type.Name()
 		}
 	}
 	r.types[itab] = t
@@ -99,7 +120,7 @@ func (r *Reflector) Load(obj interface{}) reflectObject {
 	return t
 }
 
-func ReflectObject(obj interface{}) (s reflectObject) {
+func Reflect(obj interface{}) *reflectObject {
 	return reflector.Load(obj)
 }
 
@@ -181,3 +202,18 @@ func isValueType(kind reflect.Kind) bool {
 		return false
 	}
 }
+
+func FormatFuncName(f string) string {
+	i := strings.LastIndex(f, "/")
+	j := strings.Index(f[i+1:], ".")
+	if j < 1 {
+		return "???"
+	}
+	_, fun := f[:i+j+1], f[i+j+2:]
+	i = strings.LastIndex(fun, ".")
+	return fun[i+1:]
+}
+
+func FuncName(f interface{}) string {
+	return Reflect(f).Name()
+}
diff --git a/pkg/util/reflect_test.go b/pkg/util/reflect_test.go
index f5a4c514..d6706ca7 100644
--- a/pkg/util/reflect_test.go
+++ b/pkg/util/reflect_test.go
@@ -18,7 +18,6 @@ package util
 
 import (
 	"fmt"
-	"reflect"
 	"testing"
 )
 
@@ -34,47 +33,69 @@ type testField struct {
 
 func TestLoadStruct(t *testing.T) {
 	obj1 := testStru{}
-	v := ReflectObject(obj1)
+	v := Reflect(obj1)
 	if v.Type.String() != "util.testStru" {
 		t.Fatalf("TestLoadStruct failed, %s != 'testStru'", v.Type.String())
 	}
 	if len(v.Fields) != 4 {
 		t.Fatalf("TestLoadStruct failed, wrong count of fields")
 	}
+	if v.Name() != "pkg/util.testStru" || v.FullName != "github.com/apache/incubator-servicecomb-service-center/pkg/util.testStru" {
+		t.Fatalf("TestLoadStruct failed")
+	}
 	for _, f := range v.Fields {
 		fmt.Println(f.Name, f.Type.String())
 	}
 
 	obj2 := testStru{}
-	v = ReflectObject(obj2)
-	v = ReflectObject(&obj2)
-	v = ReflectObject(nil)
+	v1 := Reflect(obj2)
+	if v1.FullName != v.FullName {
+		t.Fatalf("TestLoadStruct failed")
+	}
+	v2 := Reflect(&obj2)
+	if v2.FullName != v.FullName {
+		t.Fatalf("TestLoadStruct failed")
+	}
+	v = Reflect(nil)
+	if v.FullName != "" {
+		t.Fatalf("TestLoadStruct failed")
+	}
+
+	if FuncName(TestLoadStruct) != "pkg/util.TestLoadStruct" {
+		t.Fatalf("TestLoadStruct failed")
+	}
+	f := TestLoadStruct
+	if FormatFuncName(FuncName(f)) != "TestLoadStruct" {
+		t.Fatalf("TestLoadStruct failed")
+	}
+}
+
+func TestSizeof(t *testing.T) {
+	s := &S{}
+	if Sizeof(s) != 8+152 {
+		t.Fatalf("TestSizeof failed")
+	}
 }
 
 func BenchmarkLoadStruct(b *testing.B) {
 	b.RunParallel(func(pb *testing.PB) {
 		for pb.Next() {
-			ReflectObject(testStru{})
+			Reflect(testStru{})
 		}
 	})
 	b.ReportAllocs()
 	// 20000000	        86.9 ns/op	      32 B/op	       1 allocs/op
 }
 
-var (
-	sliceSize  = uint64(reflect.TypeOf(reflect.SliceHeader{}).Size())
-	stringSize = uint64(reflect.TypeOf(reflect.StringHeader{}).Size())
-)
-
 type S struct {
-	a  int
-	s  string
-	p  *S
-	m  map[int32]uint32
-	u  []uint64
-	ua [8]uint64
-	ch chan int
-	i  interface{}
+	a  int              // 8
+	s  string           // 16
+	p  *S               // 8
+	m  map[int32]uint32 // 8
+	u  []uint64         // 24
+	ua [8]uint64        // 64
+	ch chan int         // 8
+	i  interface{}      // 16
 }
 
 func BenchmarkSizeof(b *testing.B) {
diff --git a/pkg/util/util.go b/pkg/util/util.go
index 5c3b3f06..d1dfd0cb 100644
--- a/pkg/util/util.go
+++ b/pkg/util/util.go
@@ -17,11 +17,8 @@
 package util
 
 import (
-	"bytes"
-	"encoding/gob"
 	"fmt"
 	"os"
-	"reflect"
 	"runtime"
 	"runtime/debug"
 	"strings"
@@ -29,45 +26,11 @@ import (
 	"unsafe"
 )
 
-func MinInt(x, y int) int {
-	if x <= y {
-		return x
-	} else {
-		return y
-	}
-}
-
-func ClearStringMemory(src *string) {
-	p := (*struct {
-		ptr uintptr
-		len int
-	})(unsafe.Pointer(src))
-
-	l := MinInt(p.len, 32)
-	ptr := p.ptr
-	for idx := 0; idx < l; idx = idx + 1 {
-		b := (*byte)(unsafe.Pointer(ptr))
-		*b = 0
-		ptr += 1
-	}
-}
-
-func ClearByteMemory(src []byte) {
-	l := MinInt(len(src), 32)
-	for idx := 0; idx < l; idx = idx + 1 {
-		src[idx] = 0
+func SafeCloseChan(c chan struct{}) {
+	if c == nil {
+		return
 	}
-}
 
-func DeepCopy(dst, src interface{}) error {
-	var buf bytes.Buffer
-	if err := gob.NewEncoder(&buf).Encode(src); err != nil {
-		return err
-	}
-	return gob.NewDecoder(bytes.NewBuffer(buf.Bytes())).Decode(dst)
-}
-
-func SafeCloseChan(c chan struct{}) {
 	select {
 	case _, ok := <-c:
 		if ok {
@@ -133,6 +96,12 @@ func RecoverAndReport() (r interface{}) {
 	return
 }
 
+func GetCaller(skip int) (string, string, int, bool) {
+	pc, file, line, ok := runtime.Caller(skip + 1)
+	method := FormatFuncName(runtime.FuncForPC(pc).Name())
+	return file, method, line, ok
+}
+
 // this function can only be called in recover().
 func LogPanic(args ...interface{}) {
 	for i := 2; i < 10; i++ {
@@ -153,21 +122,6 @@ func LogPanic(args ...interface{}) {
 	fmt.Fprintln(os.Stderr, BytesToStringWithNoCopy(debug.Stack()))
 }
 
-func FileLastName(file string) string {
-	if sp1 := strings.LastIndex(file, "/"); sp1 >= 0 {
-		if sp2 := strings.LastIndex(file[:sp1], "/"); sp2 >= 0 {
-			file = file[sp2+1:]
-		}
-	}
-	return file
-}
-
-func GetCaller(skip int) (string, string, int, bool) {
-	pc, file, line, ok := runtime.Caller(skip + 1)
-	method := FormatFuncName(runtime.FuncForPC(pc).Name())
-	return file, method, line, ok
-}
-
 func Int16ToInt64(bs []int16) (in int64) {
 	l := len(bs)
 	if l > 4 || l == 0 {
@@ -188,19 +142,13 @@ func Int16ToInt64(bs []int16) (in int64) {
 	return
 }
 
-func FormatFuncName(f string) string {
-	i := strings.LastIndex(f, "/")
-	j := strings.Index(f[i+1:], ".")
-	if j < 1 {
-		return "???"
+func FileLastName(file string) string {
+	if sp1 := strings.LastIndex(file, "/"); sp1 >= 0 {
+		if sp2 := strings.LastIndex(file[:sp1], "/"); sp2 >= 0 {
+			file = file[sp2+1:]
+		}
 	}
-	_, fun := f[:i+j+1], f[i+j+2:]
-	i = strings.LastIndex(fun, ".")
-	return fun[i+1:]
-}
-
-func FuncName(f interface{}) string {
-	return runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name()
+	return file
 }
 
 func SliceHave(arr []string, str string) bool {
diff --git a/pkg/util/util_test.go b/pkg/util/util_test.go
index c50ae812..852851fc 100644
--- a/pkg/util/util_test.go
+++ b/pkg/util/util_test.go
@@ -80,3 +80,45 @@ func TestResetTimer(t *testing.T) {
 	ResetTimer(timer, time.Second)
 	<-timer.C
 }
+
+func TestStringJoin(t *testing.T) {
+	if StringJoin([]string{"a", "b", "c"}, ",") != "a,b,c" {
+		t.Fatalf("TestStringJoin failed")
+	}
+	if StringJoin([]string{"a"}, ",") != "a" {
+		t.Fatalf("TestStringJoin failed")
+	}
+	if StringJoin([]string{"a", "b", "c"}, "") != "abc" {
+		t.Fatalf("TestStringJoin failed")
+	}
+	if StringJoin([]string{}, ",") != "" {
+		t.Fatalf("TestStringJoin failed")
+	}
+	if StringJoin(nil, ",") != "" {
+		t.Fatalf("TestStringJoin failed")
+	}
+}
+
+func TestStringToBytesWithNoCopy(t *testing.T) {
+	b := StringToBytesWithNoCopy("ab")
+	if b[0] != 'a' || b[1] != 'b' {
+		t.Fatalf("TestStringToBytesWithNoCopy failed")
+	}
+}
+
+func TestSafeCloseChan(t *testing.T) {
+	var ch chan struct{}
+	SafeCloseChan(ch)
+	ch = make(chan struct{})
+	SafeCloseChan(ch)
+	SafeCloseChan(ch)
+}
+
+func TestSystemPackage(t *testing.T) {
+	if HostName() == "" {
+		t.Fatalf("TestSystemPackage failed")
+	}
+	if !PathExist("../../etc/conf/app.conf") {
+		t.Fatalf("TestSystemPackage failed")
+	}
+}
diff --git a/pkg/util/uuid_test.go b/pkg/util/uuid_test.go
new file mode 100644
index 00000000..392db1c9
--- /dev/null
+++ b/pkg/util/uuid_test.go
@@ -0,0 +1,29 @@
+/*
+ * 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 util
+
+import (
+	"strings"
+	"testing"
+)
+
+func TestGenerateUuid(t *testing.T) {
+	uuid := GenerateUuid()
+	if len(uuid) == 0 || strings.Contains(uuid, "-") {
+		t.Fatalf("TestGenerateUuid failed")
+	}
+}
diff --git a/pkg/validate/url.go b/pkg/validate/url.go
index f41eaa4e..824d0a4b 100644
--- a/pkg/validate/url.go
+++ b/pkg/validate/url.go
@@ -17,53 +17,24 @@
 package validate
 
 import (
-	"net/url"
-	"regexp"
 	"strings"
 )
 
-const (
-	PATTERN_URI      string = `#%`
-	PATTERN_URL             = "^(https|http):\\/\\/((?:(?:25[0-5]|2[0-4]\\d|((1\\d{2})|([1-9]?\\d)))\\.){3}(?:25[0-5]|2[0-4]\\d|((1\\d{2})|([1-9]?\\d)))):([0-9]+)$"
-	PATTERN_HOSTNAME string = `^(([1-9]\d?|1\d\d|2[01]\d|22[0-3])(\.(1?\d{1,2}|2[0-4]\d|25[0-5])){2}(\.([0-9]\d?|1\d\d|2[0-4]\d|25[0-4])))(:(\d{1,5}))$`
-)
-
-var (
-	rxURI      = regexp.MustCompile(PATTERN_URI)
-	rxURL      = regexp.MustCompile(PATTERN_URL)
-	rxHostName = regexp.MustCompile(PATTERN_HOSTNAME)
-)
-
-// IsURL check if the string is an URL.
-func IsURL(str string) bool {
-	if str == "" || len(str) >= 2083 || len(str) <= 3 || strings.HasPrefix(str, ".") {
-		return false
-	}
-	u, err := url.Parse(str)
-	if err != nil {
-		return false
-	}
-	if strings.HasPrefix(u.Host, ".") {
-		return false
-	}
-	if u.Host == "" && (u.Path != "" && !strings.Contains(u.Path, ".")) {
-		return false
-	}
-	return rxHostName.MatchString(str)
-
-}
-
+// IsRequestURI check if the string is an URL.
 func IsRequestURI(uri string) bool {
-	if uri == "" || len(uri) >= 2048 || strings.HasPrefix(uri, ".") {
+	if uri == "" || len(uri) >= 2048 {
 		return false
 	}
-	if strings.HasSuffix(uri, ";") || strings.HasSuffix(uri, "&") || strings.HasSuffix(uri, "?") || strings.HasSuffix(uri, "+") || strings.HasSuffix(uri, "@") || strings.Contains(uri, "//") {
+	if strings.HasSuffix(uri, ";") ||
+		strings.HasSuffix(uri, "&") ||
+		strings.HasSuffix(uri, "?") ||
+		strings.HasSuffix(uri, "+") ||
+		strings.HasSuffix(uri, "@") ||
+		//
+		strings.HasPrefix(uri, ".") ||
+		//
+		strings.Contains(uri, "//") {
 		return false
 	}
-	return !rxURI.MatchString(uri)
-}
-
-//format : https://10.21.119.167:30100 or http://10.21.119.167:30100
-func URLChecker(url string) bool {
-	return rxURL.MatchString(url)
+	return true
 }
diff --git a/pkg/validate/validator.go b/pkg/validate/validator.go
index d7f5c706..7a683017 100644
--- a/pkg/validate/validator.go
+++ b/pkg/validate/validator.go
@@ -105,7 +105,7 @@ func (v *Validator) Validate(s interface{}) error {
 		return fmt.Errorf("not support validate type '%s'", k)
 	}
 
-	st := util.ReflectObject(s)
+	st := util.Reflect(s)
 	for i, l := 0, sv.NumField(); i < l; i++ {
 		field := sv.Field(i)
 		fieldName := st.Fields[i].Name
diff --git a/scripts/prepare_env_ut.sh b/scripts/prepare_env_ut.sh
index a92980f9..2868ee14 100755
--- a/scripts/prepare_env_ut.sh
+++ b/scripts/prepare_env_ut.sh
@@ -14,6 +14,5 @@
 # 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.
-cp -r etc/conf server/interceptor/ratelimiter/
-cp -r etc/conf server/service/
+cp -r etc/conf server/plugin/infra/tls/buildin
 echo "mode: atomic" > coverage.txt
diff --git a/scripts/ut_test_in_docker.sh b/scripts/ut_test_in_docker.sh
index fed3fbe5..944e163e 100755
--- a/scripts/ut_test_in_docker.sh
+++ b/scripts/ut_test_in_docker.sh
@@ -37,9 +37,14 @@ echo "${green}Preparing the env for UT....${reset}"
 ./scripts/prepare_env_ut.sh
 
 echo "${green}Running UT for Service-Center server"
-bash -x ./scripts/ut.sh server/service
-  
-if [ $? == 0 ]; then
+bash -x ./scripts/ut.sh pkg
+ret=$?
+if [ ${ret} == 0 ]; then
+    bash -x ./scripts/ut.sh server
+    ret=$?
+fi
+
+if [ ${ret} == 0 ]; then
 	echo "${green}All the unit test passed..${reset}"
 	echo "${green}Coverage is created in the file ./coverage.txt${reset}"
 else
diff --git a/server/admin/admin.go b/server/admin/admin.go
new file mode 100644
index 00000000..4a7da27b
--- /dev/null
+++ b/server/admin/admin.go
@@ -0,0 +1,29 @@
+/*
+ * 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 admin
+
+import (
+	roa "github.com/apache/incubator-servicecomb-service-center/pkg/rest"
+)
+
+func init() {
+	registerREST()
+}
+
+func registerREST() {
+	roa.RegisterServant(&AdminServiceControllerV4{})
+}
diff --git a/server/admin/admin_suite_test.go b/server/admin/admin_suite_test.go
new file mode 100644
index 00000000..d2444ea1
--- /dev/null
+++ b/server/admin/admin_suite_test.go
@@ -0,0 +1,45 @@
+/*
+ * 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 admin
+
+import (
+	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
+	_ "github.com/apache/incubator-servicecomb-service-center/server/plugin/infra/quota/buildin"
+	_ "github.com/apache/incubator-servicecomb-service-center/server/plugin/infra/registry/etcd"
+	_ "github.com/apache/incubator-servicecomb-service-center/server/plugin/infra/uuid/buildin"
+	. "github.com/onsi/ginkgo"
+	"github.com/onsi/ginkgo/reporters"
+	. "github.com/onsi/gomega"
+	"golang.org/x/net/context"
+	"testing"
+)
+
+func TestAdmin(t *testing.T) {
+	RegisterFailHandler(Fail)
+	junitReporter := reporters.NewJUnitReporter("model.junit.xml")
+	RunSpecsWithDefaultAndCustomReporters(t, "model Suite", []Reporter{junitReporter})
+}
+
+var _ = BeforeSuite(func() {
+	//init plugin
+})
+
+func getContext() context.Context {
+	return util.SetContext(
+		util.SetDomainProject(context.Background(), "default", "default"),
+		"noCache", "1")
+}
diff --git a/server/admin/controller_v4.go b/server/admin/controller_v4.go
new file mode 100644
index 00000000..5fd786a1
--- /dev/null
+++ b/server/admin/controller_v4.go
@@ -0,0 +1,46 @@
+/*
+ * 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 admin
+
+import (
+	"net/http"
+
+	"github.com/apache/incubator-servicecomb-service-center/pkg/rest"
+	"github.com/apache/incubator-servicecomb-service-center/server/admin/model"
+	"github.com/apache/incubator-servicecomb-service-center/server/rest/controller"
+)
+
+// AdminService 治理相关接口服务
+type AdminServiceControllerV4 struct {
+}
+
+// URLPatterns 路由
+func (ctrl *AdminServiceControllerV4) URLPatterns() []rest.Route {
+	return []rest.Route{
+		{rest.HTTP_METHOD_GET, "/v4/:project/admin/dump", ctrl.Dump},
+	}
+}
+
+func (ctrl *AdminServiceControllerV4) Dump(w http.ResponseWriter, r *http.Request) {
+	request := &model.DumpRequest{}
+	ctx := r.Context()
+	resp, _ := AdminServiceAPI.Dump(ctx, request)
+
+	respInternal := resp.Response
+	resp.Response = nil
+	controller.WriteResponse(w, respInternal, resp)
+}
diff --git a/server/admin/model/dump.go b/server/admin/model/dump.go
new file mode 100644
index 00000000..fb91411f
--- /dev/null
+++ b/server/admin/model/dump.go
@@ -0,0 +1,185 @@
+/*
+ * 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 model
+
+import (
+	pb "github.com/apache/incubator-servicecomb-service-center/server/core/proto"
+)
+
+type Getter interface {
+	ForEach(i func(i int, v *KV))
+}
+
+type Setter interface {
+	SetValue(v *KV)
+}
+
+type MicroserviceSlice []*Microservice
+type MicroserviceIndexSlice []*MicroserviceIndex
+type MicroserviceAliasSlice []*MicroserviceAlias
+type TagSlice []*Tag
+type MicroServiceRuleSlice []*MicroServiceRule
+type MicroServiceDependencyRuleSlice []*MicroServiceDependencyRule
+type SummarySlice []*Summary
+type InstanceSlice []*Instance
+
+func (s *MicroserviceSlice) ForEach(f func(i int, v *KV)) {
+	for i, v := range *s {
+		v.KV.Value = v.Value
+		f(i, v.KV)
+	}
+}
+func (s *MicroserviceIndexSlice) ForEach(f func(i int, v *KV)) {
+	for i, v := range *s {
+		v.KV.Value = v.Value
+		f(i, v.KV)
+	}
+}
+func (s *MicroserviceAliasSlice) ForEach(f func(i int, v *KV)) {
+	for i, v := range *s {
+		v.KV.Value = v.Value
+		f(i, v.KV)
+	}
+}
+func (s *TagSlice) ForEach(f func(i int, v *KV)) {
+	for i, v := range *s {
+		v.KV.Value = v.Value
+		f(i, v.KV)
+	}
+}
+func (s *MicroServiceRuleSlice) ForEach(f func(i int, v *KV)) {
+	for i, v := range *s {
+		v.KV.Value = v.Value
+		f(i, v.KV)
+	}
+}
+func (s *MicroServiceDependencyRuleSlice) ForEach(f func(i int, v *KV)) {
+	for i, v := range *s {
+		v.KV.Value = v.Value
+		f(i, v.KV)
+	}
+}
+func (s *SummarySlice) ForEach(f func(i int, v *KV)) {
+	for i, v := range *s {
+		v.KV.Value = v.Value
+		f(i, v.KV)
+	}
+}
+func (s *InstanceSlice) ForEach(f func(i int, v *KV)) {
+	for i, v := range *s {
+		v.KV.Value = v.Value
+		f(i, v.KV)
+	}
+}
+
+func (s *MicroserviceSlice) SetValue(v *KV)      { *s = append(*s, NewMicroservice(v)) }
+func (s *MicroserviceIndexSlice) SetValue(v *KV) { *s = append(*s, NewMicroserviceIndex(v)) }
+func (s *MicroserviceAliasSlice) SetValue(v *KV) { *s = append(*s, NewMicroserviceAlias(v)) }
+func (s *TagSlice) SetValue(v *KV)               { *s = append(*s, NewTag(v)) }
+func (s *MicroServiceRuleSlice) SetValue(v *KV)  { *s = append(*s, NewMicroServiceRule(v)) }
+func (s *MicroServiceDependencyRuleSlice) SetValue(v *KV) {
+	*s = append(*s, NewMicroServiceDependencyRule(v))
+}
+func (s *SummarySlice) SetValue(v *KV)  { *s = append(*s, NewSummary(v)) }
+func (s *InstanceSlice) SetValue(v *KV) { *s = append(*s, NewInstance(v)) }
+
+func NewMicroservice(kv *KV) *Microservice {
+	return &Microservice{kv, kv.Value.(*pb.MicroService)}
+}
+func NewMicroserviceIndex(kv *KV) *MicroserviceIndex {
+	return &MicroserviceIndex{kv, kv.Value.(string)}
+}
+func NewMicroserviceAlias(kv *KV) *MicroserviceAlias {
+	return &MicroserviceAlias{kv, kv.Value.(string)}
+}
+func NewTag(kv *KV) *Tag { return &Tag{kv, kv.Value.(map[string]string)} }
+func NewMicroServiceRule(kv *KV) *MicroServiceRule {
+	return &MicroServiceRule{kv, kv.Value.(*pb.ServiceRule)}
+}
+func NewMicroServiceDependencyRule(kv *KV) *MicroServiceDependencyRule {
+	return &MicroServiceDependencyRule{kv, kv.Value.(*pb.MicroServiceDependency)}
+}
+func NewSummary(kv *KV) *Summary { return &Summary{kv, kv.Value.(string)} }
+func NewInstance(kv *KV) *Instance {
+	return &Instance{kv, kv.Value.(*pb.MicroServiceInstance)}
+}
+
+type Cache struct {
+	Microservices   MicroserviceSlice               `json:"services,omitempty"`
+	Indexes         MicroserviceIndexSlice          `json:"serviceIndexes,omitempty"`
+	Aliases         MicroserviceAliasSlice          `json:"serviceAliases,omitempty"`
+	Tags            TagSlice                        `json:"serviceTags,omitempty"`
+	Rules           MicroServiceRuleSlice           `json:"serviceRules,omitempty"`
+	DependencyRules MicroServiceDependencyRuleSlice `json:"dependencyRules,omitempty"`
+	Summaries       SummarySlice                    `json:"summaries,omitempty"`
+	Instances       InstanceSlice                   `json:"instances,omitempty"`
+}
+
+type KV struct {
+	Key   string      `json:"key"`
+	Rev   int64       `json:"rev"`
+	Value interface{} `json:"-"`
+}
+
+type Microservice struct {
+	*KV
+	Value *pb.MicroService `json:"value,omitempty"`
+}
+
+type MicroserviceIndex struct {
+	*KV
+	Value string `json:"value,omitempty"`
+}
+
+type MicroserviceAlias struct {
+	*KV
+	Value string `json:"value,omitempty"`
+}
+
+type MicroServiceDependencyRule struct {
+	*KV
+	Value *pb.MicroServiceDependency `json:"value,omitempty"`
+}
+
+type MicroServiceRule struct {
+	*KV
+	Value *pb.ServiceRule `json:"value,omitempty"`
+}
+
+type Summary struct {
+	*KV
+	Value string `json:"value,omitempty"`
+}
+
+type Tag struct {
+	*KV
+	Value map[string]string `json:"value,omitempty"`
+}
+
+type Instance struct {
+	*KV
+	Value *pb.MicroServiceInstance `json:"value,omitempty"`
+}
+
+type DumpRequest struct {
+	Options []string
+}
+
+type DumpResponse struct {
+	Response *pb.Response `json:"response,omitempty"`
+	Cache    *Cache       `json:"cache,omitempty"`
+}
diff --git a/server/admin/service.go b/server/admin/service.go
new file mode 100644
index 00000000..166a0571
--- /dev/null
+++ b/server/admin/service.go
@@ -0,0 +1,71 @@
+/*
+ * 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 admin
+
+import (
+	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
+	"github.com/apache/incubator-servicecomb-service-center/server/admin/model"
+	"github.com/apache/incubator-servicecomb-service-center/server/core"
+	"github.com/apache/incubator-servicecomb-service-center/server/core/backend"
+	pb "github.com/apache/incubator-servicecomb-service-center/server/core/proto"
+	scerr "github.com/apache/incubator-servicecomb-service-center/server/error"
+	"golang.org/x/net/context"
+)
+
+var AdminServiceAPI = &AdminService{}
+
+type AdminService struct {
+}
+
+func (service *AdminService) Dump(ctx context.Context, in *model.DumpRequest) (*model.DumpResponse, error) {
+
+	domainProject := util.ParseDomainProject(ctx)
+	var cache model.Cache
+
+	if !core.IsDefaultDomainProject(domainProject) {
+		return &model.DumpResponse{
+			Response: pb.CreateResponse(scerr.ErrForbidden, "Required admin permission"),
+		}, nil
+	}
+
+	service.dumpAll(ctx, &cache)
+
+	return &model.DumpResponse{
+		Response: pb.CreateResponse(pb.Response_SUCCESS, "Admin dump successfully"),
+		Cache:    &cache,
+	}, nil
+}
+
+func (service *AdminService) dumpAll(ctx context.Context, cache *model.Cache) {
+	util.NewGo(ctx, util.PoolConfigure().Workers(2)).
+		Do(func(_ context.Context) { setValue(backend.Store().Service(), &cache.Microservices) }).
+		Do(func(_ context.Context) { setValue(backend.Store().ServiceIndex(), &cache.Indexes) }).
+		Do(func(_ context.Context) { setValue(backend.Store().ServiceAlias(), &cache.Aliases) }).
+		Do(func(_ context.Context) { setValue(backend.Store().ServiceTag(), &cache.Tags) }).
+		Do(func(_ context.Context) { setValue(backend.Store().Rule(), &cache.Rules) }).
+		Do(func(_ context.Context) { setValue(backend.Store().DependencyRule(), &cache.DependencyRules) }).
+		Do(func(_ context.Context) { setValue(backend.Store().SchemaSummary(), &cache.Summaries) }).
+		Do(func(_ context.Context) { setValue(backend.Store().Instance(), &cache.Instances) }).
+		Done()
+}
+
+func setValue(e backend.Entity, setter model.Setter) {
+	e.Cache().ForEach(func(k string, kv *backend.KeyValue) (next bool) {
+		setter.SetValue(&model.KV{Key: k, Rev: kv.ModRevision, Value: kv.Value})
+		return true
+	})
+}
diff --git a/server/admin/service_test.go b/server/admin/service_test.go
new file mode 100644
index 00000000..ff32bdf1
--- /dev/null
+++ b/server/admin/service_test.go
@@ -0,0 +1,48 @@
+/*
+ * 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 admin
+
+import (
+	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
+	"github.com/apache/incubator-servicecomb-service-center/server/admin/model"
+	pb "github.com/apache/incubator-servicecomb-service-center/server/core/proto"
+	scerr "github.com/apache/incubator-servicecomb-service-center/server/error"
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+	"golang.org/x/net/context"
+)
+
+var _ = Describe("'Admin' service", func() {
+	Describe("execute 'dump' operation", func() {
+		Context("when get all", func() {
+			It("should be passed", func() {
+				resp, err := AdminServiceAPI.Dump(getContext(), &model.DumpRequest{})
+				Expect(err).To(BeNil())
+				Expect(resp.Response.Code).To(Equal(pb.Response_SUCCESS))
+			})
+		})
+		Context("when get by domain project", func() {
+			It("should be passed", func() {
+				resp, err := AdminServiceAPI.Dump(
+					util.SetDomainProject(context.Background(), "x", "x"),
+					&model.DumpRequest{})
+				Expect(err).To(BeNil())
+				Expect(resp.Response.Code).To(Equal(scerr.ErrForbidden))
+			})
+		})
+	})
+})
diff --git a/server/api.go b/server/api.go
index f606fee3..c6945353 100644
--- a/server/api.go
+++ b/server/api.go
@@ -27,6 +27,7 @@ import (
 	"github.com/apache/incubator-servicecomb-service-center/server/rpc"
 	"github.com/apache/incubator-servicecomb-service-center/server/service"
 	"golang.org/x/net/context"
+	"strconv"
 	"time"
 )
 
@@ -55,7 +56,7 @@ func (t APIType) String() string {
 	case REST:
 		return "rest"
 	default:
-		return fmt.Sprintf("SCHEME%d", t)
+		return "SCHEME" + strconv.Itoa(int(t))
 	}
 }
 
@@ -215,7 +216,7 @@ func (s *APIServer) startRESTServer() (err error) {
 	if err != nil {
 		return
 	}
-	util.Logger().Infof("Local listen address: %s, host: %s", addr, s.HostName)
+	util.Logger().Infof("listen address: %s://%s, host: %s.", REST, addr, s.HostName)
 
 	s.goroutine.Do(func(_ context.Context) {
 		err := s.restSrv.Serve()
@@ -238,7 +239,7 @@ func (s *APIServer) startRPCServer() (err error) {
 	if err != nil {
 		return
 	}
-	util.Logger().Infof("Local listen address: %s, host: %s.", addr, s.HostName)
+	util.Logger().Infof("listen address: %s://%s, host: %s.", RPC, addr, s.HostName)
 
 	s.goroutine.Do(func(_ context.Context) {
 		err := s.rpcSrv.Serve()
@@ -279,10 +280,11 @@ func (s *APIServer) Start() {
 		s.err <- err
 		return
 	}
-
 	// 心跳
 	s.startHeartBeatService()
 
+	ReportScInstance()
+
 	util.Logger().Info("api server is ready")
 }
 
diff --git a/server/bootstrap/bootstrap.go b/server/bootstrap/bootstrap.go
index 184cb6d4..f0efb35a 100644
--- a/server/bootstrap/bootstrap.go
+++ b/server/bootstrap/bootstrap.go
@@ -16,8 +16,6 @@
  */
 package bootstrap
 
-import _ "github.com/apache/incubator-servicecomb-service-center/server/core" // initialize
-
 // rest
 import _ "github.com/apache/incubator-servicecomb-service-center/server/rest/controller/v3"
 import _ "github.com/apache/incubator-servicecomb-service-center/server/rest/controller/v4"
@@ -51,6 +49,9 @@ import _ "github.com/apache/incubator-servicecomb-service-center/server/govern"
 // module 'broker'
 import _ "github.com/apache/incubator-servicecomb-service-center/server/broker"
 
+// module 'admin'
+import _ "github.com/apache/incubator-servicecomb-service-center/server/admin"
+
 // metrics
 import _ "github.com/apache/incubator-servicecomb-service-center/server/metric"
 
diff --git a/server/broker/broker.go b/server/broker/broker.go
index fc807bee..54840aef 100644
--- a/server/broker/broker.go
+++ b/server/broker/broker.go
@@ -25,5 +25,5 @@ func init() {
 }
 
 func registerREST() {
-	roa.RegisterServent(&BrokerController{})
+	roa.RegisterServant(&BrokerController{})
 }
diff --git a/server/broker/broker_suite_test.go b/server/broker/broker_suite_test.go
index 8bc652e1..2bdfa72e 100644
--- a/server/broker/broker_suite_test.go
+++ b/server/broker/broker_suite_test.go
@@ -17,7 +17,7 @@
 package broker
 
 import (
-	pb "github.com/apache/incubator-servicecomb-service-center/server/core/proto"
+	"github.com/apache/incubator-servicecomb-service-center/server/core"
 	_ "github.com/apache/incubator-servicecomb-service-center/server/plugin/infra/quota/buildin"
 	_ "github.com/apache/incubator-servicecomb-service-center/server/plugin/infra/registry/etcd"
 	_ "github.com/apache/incubator-servicecomb-service-center/server/plugin/infra/tracing/buildin"
@@ -29,13 +29,12 @@ import (
 	"testing"
 )
 
-var serviceResource pb.ServiceCtrlServer
-var instanceResource pb.SerivceInstanceCtrlServerEx
 var brokerResource = BrokerServiceAPI
 
 var _ = BeforeSuite(func() {
 	//init plugin
-	serviceResource, instanceResource = service.AssembleResources()
+	core.ServerInfo.Config.EnableCache = false
+	core.ServiceAPI, core.InstanceAPI = service.AssembleResources()
 })
 
 func TestBroker(t *testing.T) {
diff --git a/server/broker/broker.pb.go b/server/broker/brokerpb/broker.pb.go
similarity index 99%
rename from server/broker/broker.pb.go
rename to server/broker/brokerpb/broker.pb.go
index c2709cd4..d61f9d8a 100644
--- a/server/broker/broker.pb.go
+++ b/server/broker/brokerpb/broker.pb.go
@@ -49,7 +49,7 @@ It has these top-level messages:
 	BrokerAPIInfoEntry
 	BrokerHomeResponse
 */
-package broker
+package brokerpb
 
 import proto "github.com/golang/protobuf/proto"
 import fmt "fmt"
diff --git a/server/broker/broker.proto b/server/broker/brokerpb/broker.proto
similarity index 100%
rename from server/broker/broker.proto
rename to server/broker/brokerpb/broker.proto
diff --git a/server/broker/controller.go b/server/broker/controller.go
index 05101f09..dc64522d 100644
--- a/server/broker/controller.go
+++ b/server/broker/controller.go
@@ -23,6 +23,7 @@ import (
 	"strconv"
 
 	"github.com/apache/incubator-servicecomb-service-center/pkg/rest"
+	"github.com/apache/incubator-servicecomb-service-center/server/broker/brokerpb"
 	scerr "github.com/apache/incubator-servicecomb-service-center/server/error"
 	"github.com/apache/incubator-servicecomb-service-center/server/rest/controller"
 )
@@ -60,7 +61,7 @@ func (brokerService *BrokerController) URLPatterns() []rest.Route {
 }
 
 func (brokerService *BrokerController) GetHome(w http.ResponseWriter, r *http.Request) {
-	request := &BaseBrokerRequest{
+	request := &brokerpb.BaseBrokerRequest{
 		HostAddress: r.Host,
 		Scheme:      getScheme(r),
 	}
@@ -79,7 +80,7 @@ func (*BrokerController) PublishPact(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 	query := r.URL.Query()
-	request := &PublishPactRequest{
+	request := &brokerpb.PublishPactRequest{
 		ProviderId: query.Get(":providerId"),
 		ConsumerId: query.Get(":consumerId"),
 		Version:    query.Get(":number"),
@@ -95,9 +96,9 @@ func (*BrokerController) PublishPact(w http.ResponseWriter, r *http.Request) {
 }
 
 func (*BrokerController) GetAllProviderPacts(w http.ResponseWriter, r *http.Request) {
-	request := &GetAllProviderPactsRequest{
+	request := &brokerpb.GetAllProviderPactsRequest{
 		ProviderId: r.URL.Query().Get(":providerId"),
-		BaseUrl: &BaseBrokerRequest{
+		BaseUrl: &brokerpb.BaseBrokerRequest{
 			HostAddress: r.Host,
 			Scheme:      getScheme(r),
 		},
@@ -115,11 +116,11 @@ func (*BrokerController) GetAllProviderPacts(w http.ResponseWriter, r *http.Requ
 
 func (*BrokerController) GetPactsOfProvider(w http.ResponseWriter, r *http.Request) {
 	query := r.URL.Query()
-	request := &GetProviderConsumerVersionPactRequest{
+	request := &brokerpb.GetProviderConsumerVersionPactRequest{
 		ProviderId: query.Get(":providerId"),
 		ConsumerId: query.Get(":consumerId"),
 		Version:    query.Get(":number"),
-		BaseUrl: &BaseBrokerRequest{
+		BaseUrl: &brokerpb.BaseBrokerRequest{
 			HostAddress: r.Host,
 			Scheme:      getScheme(r),
 		},
@@ -133,7 +134,7 @@ func (*BrokerController) GetPactsOfProvider(w http.ResponseWriter, r *http.Reque
 }
 
 func (*BrokerController) DeletePacts(w http.ResponseWriter, r *http.Request) {
-	resp, _ := BrokerServiceAPI.DeletePacts(r.Context(), &BaseBrokerRequest{
+	resp, _ := BrokerServiceAPI.DeletePacts(r.Context(), &brokerpb.BaseBrokerRequest{
 		HostAddress: r.Host,
 		Scheme:      getScheme(r),
 	})
@@ -147,7 +148,7 @@ func (*BrokerController) PublishVerificationResults(w http.ResponseWriter, r *ht
 		controller.WriteError(w, scerr.ErrInvalidParams, err.Error())
 		return
 	}
-	request := &PublishVerificationRequest{}
+	request := &brokerpb.PublishVerificationRequest{}
 	err = json.Unmarshal(requestBody, request)
 	if err != nil {
 		PactLogger.Error("Unmarshal error", err)
@@ -175,7 +176,7 @@ func (*BrokerController) PublishVerificationResults(w http.ResponseWriter, r *ht
 }
 
 func (*BrokerController) RetrieveVerificationResults(w http.ResponseWriter, r *http.Request) {
-	request := &RetrieveVerificationRequest{}
+	request := &brokerpb.RetrieveVerificationRequest{}
 	query := r.URL.Query()
 	request.ConsumerId = query.Get(":consumerId")
 	request.ConsumerVersion = query.Get(":consumerVersion")
diff --git a/server/broker/controller_test.go b/server/broker/controller_test.go
new file mode 100644
index 00000000..fe42bf41
--- /dev/null
+++ b/server/broker/controller_test.go
@@ -0,0 +1,49 @@
+/*
+ * 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 broker
+
+import (
+	"bytes"
+	"net/http"
+	"net/http/httptest"
+	"testing"
+)
+
+var (
+	ctrl = &BrokerController{}
+	num  int
+)
+
+type mockBrokerHandler struct {
+}
+
+func (b *mockBrokerHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	route := ctrl.URLPatterns()[num]
+	r.Method = route.Method
+	r.URL.RawQuery = ":sha=1"
+	route.Func(w, r)
+	num++
+}
+
+func TestBrokerController_GetHome(t *testing.T) {
+	svr := httptest.NewServer(&mockBrokerHandler{})
+	defer svr.Close()
+
+	for range ctrl.URLPatterns() {
+		http.Post(svr.URL, "application/json", bytes.NewBuffer([]byte("{}")))
+	}
+}
diff --git a/server/broker/service.go b/server/broker/service.go
index c36cb3e1..ec0a96a0 100644
--- a/server/broker/service.go
+++ b/server/broker/service.go
@@ -19,13 +19,13 @@ package broker
 import (
 	"crypto/sha1"
 	"encoding/json"
-	"fmt"
 	"math"
 	"strconv"
 	"strings"
 	"time"
 
 	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
+	"github.com/apache/incubator-servicecomb-service-center/server/broker/brokerpb"
 	apt "github.com/apache/incubator-servicecomb-service-center/server/core"
 	pb "github.com/apache/incubator-servicecomb-service-center/server/core/proto"
 	scerr "github.com/apache/incubator-servicecomb-service-center/server/error"
@@ -40,11 +40,11 @@ type BrokerService struct {
 }
 
 func (*BrokerService) GetBrokerHome(ctx context.Context,
-	in *BaseBrokerRequest) (*BrokerHomeResponse, error) {
+	in *brokerpb.BaseBrokerRequest) (*brokerpb.BrokerHomeResponse, error) {
 
 	if in == nil || len(in.HostAddress) == 0 {
 		PactLogger.Errorf(nil, "Get Participant versions request failed: invalid params.")
-		return &BrokerHomeResponse{
+		return &brokerpb.BrokerHomeResponse{
 			Response: pb.CreateResponse(scerr.ErrInvalidParams, "Request format invalid."),
 		}, nil
 	}
@@ -53,15 +53,18 @@ func (*BrokerService) GetBrokerHome(ctx context.Context,
 }
 
 func (*BrokerService) GetPactsOfProvider(ctx context.Context,
-	in *GetProviderConsumerVersionPactRequest) (*GetProviderConsumerVersionPactResponse, error) {
+	in *brokerpb.GetProviderConsumerVersionPactRequest) (*brokerpb.GetProviderConsumerVersionPactResponse, error) {
 	PactLogger.Infof("GetPactsOfProvider: (%s, %s, %s)\n",
 		in.ProviderId, in.ConsumerId, in.Version)
 
 	resp, pactId, err := RetrieveProviderConsumerPact(ctx, in)
 	if err != nil || resp.GetPact() == nil || pactId == -1 {
-		PactLogger.Errorf(nil, "Get pacts of provider failed: %s\n",
-			resp.Response.Message)
-		return &GetProviderConsumerVersionPactResponse{
+		var message string
+		if resp != nil {
+			message = resp.Response.Message
+		}
+		PactLogger.Errorf(err, "Get pacts of provider failed: %s\n", message)
+		return &brokerpb.GetProviderConsumerVersionPactResponse{
 			Response: resp.GetResponse(),
 		}, err
 	}
@@ -70,7 +73,7 @@ func (*BrokerService) GetPactsOfProvider(ctx context.Context,
 		BROKER_PUBLISH_VERIFICATION_URL,
 		strings.NewReplacer(":providerId", in.ProviderId,
 			":consumerId", in.ConsumerId,
-			":pact", fmt.Sprint(pactId)))
+			":pact", strconv.FormatInt(int64(pactId), 10)))
 
 	links := ",\"_links\": {" +
 		"\"pb:publish-verification-results\": {" +
@@ -85,7 +88,7 @@ func (*BrokerService) GetPactsOfProvider(ctx context.Context,
 	sliceOfResp := pactBytes[0 : len(pactBytes)-2]
 	finalBytes := append(sliceOfResp, linksBytes...)
 
-	return &GetProviderConsumerVersionPactResponse{
+	return &brokerpb.GetProviderConsumerVersionPactResponse{
 		Response: pb.CreateResponse(pb.Response_SUCCESS, "Success."),
 		Pact:     finalBytes,
 	}, nil
@@ -94,7 +97,7 @@ func (*BrokerService) GetPactsOfProvider(ctx context.Context,
 }
 
 func (*BrokerService) DeletePacts(ctx context.Context,
-	in *BaseBrokerRequest) (*pb.Response, error) {
+	in *brokerpb.BaseBrokerRequest) (*pb.Response, error) {
 
 	resp, err := DeletePactData(ctx, in)
 
@@ -102,10 +105,10 @@ func (*BrokerService) DeletePacts(ctx context.Context,
 }
 
 func (*BrokerService) RetrieveProviderPacts(ctx context.Context,
-	in *GetAllProviderPactsRequest) (*GetAllProviderPactsResponse, error) {
+	in *brokerpb.GetAllProviderPactsRequest) (*brokerpb.GetAllProviderPactsResponse, error) {
 	if in == nil || len(in.ProviderId) == 0 {
 		PactLogger.Errorf(nil, "all provider pact retrieve request failed: invalid params.")
-		return &GetAllProviderPactsResponse{
+		return &brokerpb.GetAllProviderPactsResponse{
 			Response: pb.CreateResponse(scerr.ErrInvalidParams, "Request format invalid."),
 		}, nil
 	}
@@ -114,13 +117,13 @@ func (*BrokerService) RetrieveProviderPacts(ctx context.Context,
 	provider, err := serviceUtil.GetService(ctx, tenant, in.ProviderId)
 	if err != nil {
 		PactLogger.Errorf(err, "all provider pact retrieve failed, providerId is %s: query provider failed.", in.ProviderId)
-		return &GetAllProviderPactsResponse{
+		return &brokerpb.GetAllProviderPactsResponse{
 			Response: pb.CreateResponse(scerr.ErrInvalidParams, "Query provider failed."),
 		}, err
 	}
 	if provider == nil {
 		PactLogger.Errorf(nil, "all provider pact retrieve failed, providerId is %s: provider not exist.", in.ProviderId)
-		return &GetAllProviderPactsResponse{
+		return &brokerpb.GetAllProviderPactsResponse{
 			Response: pb.CreateResponse(scerr.ErrInvalidParams, "Provider does not exist."),
 		}, nil
 	}
@@ -129,7 +132,7 @@ func (*BrokerService) RetrieveProviderPacts(ctx context.Context,
 	providerParticipant, err := GetParticipant(ctx, tenant, provider.AppId, provider.ServiceName)
 	if err != nil || providerParticipant == nil {
 		PactLogger.Errorf(nil, "all provider pact retrieve failed, provider participant cannot be searched.", in.ProviderId)
-		return &GetAllProviderPactsResponse{
+		return &brokerpb.GetAllProviderPactsResponse{
 			Response: pb.CreateResponse(scerr.ErrInvalidParams, "Provider participant cannot be searched."),
 		}, err
 	}
@@ -148,9 +151,9 @@ func (*BrokerService) RetrieveProviderPacts(ctx context.Context,
 		return nil, nil
 	}
 	// Store versions in a map
-	versionObjects := make(map[int32]Version)
+	versionObjects := make(map[int32]brokerpb.Version)
 	for i := 0; i < len(versions.Kvs); i++ {
-		version := &Version{}
+		version := &brokerpb.Version{}
 		err = json.Unmarshal(versions.Kvs[i].Value.([]byte), version)
 		if err != nil {
 			return nil, err
@@ -171,9 +174,9 @@ func (*BrokerService) RetrieveProviderPacts(ctx context.Context,
 		PactLogger.Info("[RetrieveProviderPacts] No pact version found, sorry")
 		return nil, nil
 	}
-	participantToVersionObj := make(map[int32]Version)
+	participantToVersionObj := make(map[int32]brokerpb.Version)
 	for i := 0; i < len(pactVersions.Kvs); i++ {
-		pactVersion := &PactVersion{}
+		pactVersion := &brokerpb.PactVersion{}
 		err = json.Unmarshal(pactVersions.Kvs[i].Value.([]byte), pactVersion)
 		if err != nil {
 			return nil, err
@@ -203,9 +206,9 @@ func (*BrokerService) RetrieveProviderPacts(ctx context.Context,
 	if len(participants.Kvs) == 0 {
 		return nil, nil
 	}
-	consumerInfoArr := make([]*ConsumerInfo, 0)
+	consumerInfoArr := make([]*brokerpb.ConsumerInfo, 0)
 	for i := 0; i < len(participants.Kvs); i++ {
-		participant := &Participant{}
+		participant := &brokerpb.Participant{}
 		err = json.Unmarshal(participants.Kvs[i].Value.([]byte), participant)
 		if err != nil {
 			return nil, err
@@ -232,13 +235,13 @@ func (*BrokerService) RetrieveProviderPacts(ctx context.Context,
 				":consumerId", consumerId,
 				":number", consumerVersion))
 
-		consumerInfo := &ConsumerInfo{
+		consumerInfo := &brokerpb.ConsumerInfo{
 			Href: urlValue,
 			Name: consumerId,
 		}
 		consumerInfoArr = append(consumerInfoArr, consumerInfo)
 	}
-	links := &Links{
+	links := &brokerpb.Links{
 		Pacts: consumerInfoArr,
 	}
 	resJson, err := json.Marshal(links)
@@ -246,7 +249,7 @@ func (*BrokerService) RetrieveProviderPacts(ctx context.Context,
 		return nil, err
 	}
 	PactLogger.Infof("Json : %s", string(resJson))
-	response := &GetAllProviderPactsResponse{
+	response := &brokerpb.GetAllProviderPactsResponse{
 		Response: pb.CreateResponse(pb.Response_SUCCESS, "retrieve provider pact info succeeded."),
 		XLinks:   links,
 	}
@@ -254,11 +257,11 @@ func (*BrokerService) RetrieveProviderPacts(ctx context.Context,
 }
 
 func (*BrokerService) GetAllProviderPacts(ctx context.Context,
-	in *GetAllProviderPactsRequest) (*GetAllProviderPactsResponse, error) {
+	in *brokerpb.GetAllProviderPactsRequest) (*brokerpb.GetAllProviderPactsResponse, error) {
 
 	if in == nil || len(in.ProviderId) == 0 {
 		PactLogger.Errorf(nil, "all provider pact retrieve request failed: invalid params.")
-		return &GetAllProviderPactsResponse{
+		return &brokerpb.GetAllProviderPactsResponse{
 			Response: pb.CreateResponse(scerr.ErrInvalidParams, "Request format invalid."),
 		}, nil
 	}
@@ -267,13 +270,13 @@ func (*BrokerService) GetAllProviderPacts(ctx context.Context,
 	provider, err := serviceUtil.GetService(ctx, tenant, in.ProviderId)
 	if err != nil {
 		PactLogger.Errorf(err, "all provider pact retrieve failed, providerId is %s: query provider failed.", in.ProviderId)
-		return &GetAllProviderPactsResponse{
+		return &brokerpb.GetAllProviderPactsResponse{
 			Response: pb.CreateResponse(scerr.ErrInvalidParams, "Query provider failed."),
 		}, err
 	}
 	if provider == nil {
 		PactLogger.Errorf(nil, "all provider pact retrieve failed, providerId is %s: provider not exist.", in.ProviderId)
-		return &GetAllProviderPactsResponse{
+		return &brokerpb.GetAllProviderPactsResponse{
 			Response: pb.CreateResponse(scerr.ErrInvalidParams, "Provider does not exist."),
 		}, nil
 	}
@@ -282,7 +285,7 @@ func (*BrokerService) GetAllProviderPacts(ctx context.Context,
 	providerParticipant, err := GetParticipant(ctx, tenant, provider.AppId, provider.ServiceName)
 	if err != nil || providerParticipant == nil {
 		PactLogger.Errorf(nil, "all provider pact retrieve failed, provider participant cannot be searched.", in.ProviderId)
-		return &GetAllProviderPactsResponse{
+		return &brokerpb.GetAllProviderPactsResponse{
 			Response: pb.CreateResponse(scerr.ErrInvalidParams, "Provider participant cannot be searched."),
 		}, err
 	}
@@ -301,9 +304,9 @@ func (*BrokerService) GetAllProviderPacts(ctx context.Context,
 		return nil, nil
 	}
 	// Store versions in a map
-	versionObjects := make(map[int32]Version)
+	versionObjects := make(map[int32]brokerpb.Version)
 	for i := 0; i < len(versions.Kvs); i++ {
-		version := &Version{}
+		version := &brokerpb.Version{}
 		err = json.Unmarshal(versions.Kvs[i].Value.([]byte), version)
 		if err != nil {
 			return nil, err
@@ -324,9 +327,9 @@ func (*BrokerService) GetAllProviderPacts(ctx context.Context,
 		PactLogger.Info("[RetrieveProviderPacts] No pact version found, sorry")
 		return nil, nil
 	}
-	participantToVersionObj := make(map[int32]Version)
+	participantToVersionObj := make(map[int32]brokerpb.Version)
 	for i := 0; i < len(pactVersions.Kvs); i++ {
-		pactVersion := &PactVersion{}
+		pactVersion := &brokerpb.PactVersion{}
 		err = json.Unmarshal(pactVersions.Kvs[i].Value.([]byte), pactVersion)
 		if err != nil {
 			return nil, err
@@ -356,9 +359,9 @@ func (*BrokerService) GetAllProviderPacts(ctx context.Context,
 	if len(participants.Kvs) == 0 {
 		return nil, nil
 	}
-	consumerInfoArr := make([]*ConsumerInfo, 0)
+	consumerInfoArr := make([]*brokerpb.ConsumerInfo, 0)
 	for i := 0; i < len(participants.Kvs); i++ {
-		participant := &Participant{}
+		participant := &brokerpb.Participant{}
 		err = json.Unmarshal(participants.Kvs[i].Value.([]byte), participant)
 		if err != nil {
 			return nil, err
@@ -385,13 +388,13 @@ func (*BrokerService) GetAllProviderPacts(ctx context.Context,
 				":consumerId", consumerId,
 				":number", consumerVersion))
 
-		consumerInfo := &ConsumerInfo{
+		consumerInfo := &brokerpb.ConsumerInfo{
 			Href: urlValue,
 			Name: consumerId,
 		}
 		consumerInfoArr = append(consumerInfoArr, consumerInfo)
 	}
-	links := &Links{
+	links := &brokerpb.Links{
 		Pacts: consumerInfoArr,
 	}
 	resJson, err := json.Marshal(links)
@@ -399,17 +402,17 @@ func (*BrokerService) GetAllProviderPacts(ctx context.Context,
 		return nil, err
 	}
 	PactLogger.Infof("Json : %s", string(resJson))
-	response := &GetAllProviderPactsResponse{
+	response := &brokerpb.GetAllProviderPactsResponse{
 		Response: pb.CreateResponse(pb.Response_SUCCESS, "retrieve provider pact info succeeded."),
 		XLinks:   links,
 	}
 	return response, nil
 }
 
-func (*BrokerService) RetrieveVerificationResults(ctx context.Context, in *RetrieveVerificationRequest) (*RetrieveVerificationResponse, error) {
+func (*BrokerService) RetrieveVerificationResults(ctx context.Context, in *brokerpb.RetrieveVerificationRequest) (*brokerpb.RetrieveVerificationResponse, error) {
 	if in == nil || len(in.ConsumerId) == 0 || len(in.ConsumerVersion) == 0 {
 		PactLogger.Errorf(nil, "verification result retrieve request failed: invalid params.")
-		return &RetrieveVerificationResponse{
+		return &brokerpb.RetrieveVerificationResponse{
 			Response: pb.CreateResponse(scerr.ErrInvalidParams, "Request format invalid."),
 		}, nil
 	}
@@ -417,13 +420,13 @@ func (*BrokerService) RetrieveVerificationResults(ctx context.Context, in *Retri
 	consumer, err := serviceUtil.GetService(ctx, tenant, in.ConsumerId)
 	if err != nil {
 		PactLogger.Errorf(err, "verification result retrieve request failed, consumerId is %s: query consumer failed.", in.ConsumerId)
-		return &RetrieveVerificationResponse{
+		return &brokerpb.RetrieveVerificationResponse{
 			Response: pb.CreateResponse(scerr.ErrInvalidParams, "Query consumer failed."),
 		}, err
 	}
 	if consumer == nil {
 		PactLogger.Errorf(nil, "verification result retrieve request failed, consumerId is %s: consumer not exist.", in.ConsumerId)
-		return &RetrieveVerificationResponse{
+		return &brokerpb.RetrieveVerificationResponse{
 			Response: pb.CreateResponse(scerr.ErrInvalidParams, "Consumer does not exist."),
 		}, nil
 	}
@@ -432,7 +435,7 @@ func (*BrokerService) RetrieveVerificationResults(ctx context.Context, in *Retri
 	consumerParticipant, err := GetParticipant(ctx, tenant, consumer.AppId, consumer.ServiceName)
 	if err != nil || consumerParticipant == nil {
 		PactLogger.Errorf(nil, "verification result retrieve request failed, consumer participant cannot be searched.", in.ConsumerId)
-		return &RetrieveVerificationResponse{
+		return &brokerpb.RetrieveVerificationResponse{
 			Response: pb.CreateResponse(scerr.ErrInvalidParams, "consumer participant cannot be searched."),
 		}, err
 	}
@@ -441,7 +444,7 @@ func (*BrokerService) RetrieveVerificationResults(ctx context.Context, in *Retri
 	version, err := GetVersion(ctx, tenant, consumer.Version, consumerParticipant.Id)
 	if err != nil || version == nil {
 		PactLogger.Errorf(nil, "verification result retrieve request failed, version cannot be searched.")
-		return &RetrieveVerificationResponse{
+		return &brokerpb.RetrieveVerificationResponse{
 			Response: pb.CreateResponse(scerr.ErrInvalidParams, "version cannot be searched."),
 		}, err
 	}
@@ -453,7 +456,7 @@ func (*BrokerService) RetrieveVerificationResults(ctx context.Context, in *Retri
 
 	if err != nil || len(pactVersions.Kvs) == 0 {
 		PactLogger.Errorf(nil, "verification result publish request failed, pact version cannot be searched.")
-		return &RetrieveVerificationResponse{
+		return &brokerpb.RetrieveVerificationResponse{
 			Response: pb.CreateResponse(scerr.ErrInvalidParams, "pact version cannot be searched."),
 		}, err
 	}
@@ -463,13 +466,13 @@ func (*BrokerService) RetrieveVerificationResults(ctx context.Context, in *Retri
 	fails := make([]string, 0)
 	unknowns := make([]string, 0)
 
-	verificationDetailsArr := make([]*VerificationDetail, 0)
+	verificationDetailsArr := make([]*brokerpb.VerificationDetail, 0)
 	for j := 0; j < len(pactVersions.Kvs); j++ {
-		pactVersion := &PactVersion{}
+		pactVersion := &brokerpb.PactVersion{}
 		err = json.Unmarshal(pactVersions.Kvs[j].Value.([]byte), &pactVersion)
 		if err != nil {
 			PactLogger.Errorf(nil, "verification result retrieve request failed, pact version cannot be searched.")
-			return &RetrieveVerificationResponse{
+			return &brokerpb.RetrieveVerificationResponse{
 				Response: pb.CreateResponse(scerr.ErrInvalidParams, "pact version cannot be searched."),
 			}, err
 		}
@@ -480,18 +483,18 @@ func (*BrokerService) RetrieveVerificationResults(ctx context.Context, in *Retri
 
 		if err != nil || len(verifications.Kvs) == 0 {
 			PactLogger.Errorf(nil, "verification result retrieve request failed, verification results cannot be searched.")
-			return &RetrieveVerificationResponse{
+			return &brokerpb.RetrieveVerificationResponse{
 				Response: pb.CreateResponse(scerr.ErrInvalidParams, "verification results cannot be searched."),
 			}, err
 		}
 		lastNumber := int32(math.MinInt32)
-		var lastVerificationResult *Verification
+		var lastVerificationResult *brokerpb.Verification
 		for i := 0; i < len(verifications.Kvs); i++ {
-			verification := &Verification{}
+			verification := &brokerpb.Verification{}
 			err = json.Unmarshal(verifications.Kvs[i].Value.([]byte), &verification)
 			if err != nil {
 				PactLogger.Errorf(nil, "verification result retrieve request failed, verification result unmarshall error.")
-				return &RetrieveVerificationResponse{
+				return &brokerpb.RetrieveVerificationResponse{
 					Response: pb.CreateResponse(scerr.ErrInvalidParams, "verification result unmarshall error."),
 				}, err
 			}
@@ -502,7 +505,7 @@ func (*BrokerService) RetrieveVerificationResults(ctx context.Context, in *Retri
 		}
 		if lastVerificationResult == nil {
 			PactLogger.Errorf(nil, "verification result retrieve request failed, verification result cannot be found.")
-			return &RetrieveVerificationResponse{
+			return &brokerpb.RetrieveVerificationResponse{
 				Response: pb.CreateResponse(scerr.ErrInvalidParams, "verification result cannot be found."),
 			}, err
 		}
@@ -518,17 +521,17 @@ func (*BrokerService) RetrieveVerificationResults(ctx context.Context, in *Retri
 
 		if err != nil || len(participants.Kvs) == 0 {
 			PactLogger.Errorf(nil, "verification result retrieve request failed, provider participant cannot be searched.")
-			return &RetrieveVerificationResponse{
+			return &brokerpb.RetrieveVerificationResponse{
 				Response: pb.CreateResponse(scerr.ErrInvalidParams, "provider participant cannot be searched."),
 			}, err
 		}
-		var providerParticipant *Participant
+		var providerParticipant *brokerpb.Participant
 		for i := 0; i < len(participants.Kvs); i++ {
-			participant := &Participant{}
+			participant := &brokerpb.Participant{}
 			err = json.Unmarshal(participants.Kvs[i].Value.([]byte), &participant)
 			if err != nil {
 				PactLogger.Errorf(nil, "verification result retrieve request failed, verification result unmarshall error.")
-				return &RetrieveVerificationResponse{
+				return &brokerpb.RetrieveVerificationResponse{
 					Response: pb.CreateResponse(scerr.ErrInvalidParams, "verification result unmarshall error."),
 				}, err
 			}
@@ -539,7 +542,7 @@ func (*BrokerService) RetrieveVerificationResults(ctx context.Context, in *Retri
 		}
 		if providerParticipant == nil {
 			PactLogger.Errorf(nil, "verification result retrieve request failed, verification result unmarshall error.")
-			return &RetrieveVerificationResponse{
+			return &brokerpb.RetrieveVerificationResponse{
 				Response: pb.CreateResponse(scerr.ErrInvalidParams, "verification result unmarshall error."),
 			}, err
 		}
@@ -552,12 +555,12 @@ func (*BrokerService) RetrieveVerificationResults(ctx context.Context, in *Retri
 		resp, err := apt.ServiceAPI.Exist(ctx, serviceFindReq)
 		if err != nil {
 			PactLogger.Errorf(nil, "verification result retrieve request failed, provider service cannot be found.")
-			return &RetrieveVerificationResponse{
+			return &brokerpb.RetrieveVerificationResponse{
 				Response: pb.CreateResponse(scerr.ErrInvalidParams, "provider service cannot be found."),
 			}, err
 		}
 		providerName := resp.ServiceId
-		verificationDetail := &VerificationDetail{
+		verificationDetail := &brokerpb.VerificationDetail{
 			ProviderName:               providerName,
 			ProviderApplicationVersion: lastVerificationResult.ProviderVersion,
 			Success:                    lastVerificationResult.Success,
@@ -571,20 +574,20 @@ func (*BrokerService) RetrieveVerificationResults(ctx context.Context, in *Retri
 		}
 		overAllSuccess = overAllSuccess && verificationDetail.Success
 	}
-	verificationDetails := &VerificationDetails{VerificationResults: verificationDetailsArr}
-	verificationSummary := &VerificationSummary{Successful: successfuls, Failed: fails, Unknown: unknowns}
-	verificationResult := &VerificationResult{Success: overAllSuccess, ProviderSummary: verificationSummary, XEmbedded: verificationDetails}
+	verificationDetails := &brokerpb.VerificationDetails{VerificationResults: verificationDetailsArr}
+	verificationSummary := &brokerpb.VerificationSummary{Successful: successfuls, Failed: fails, Unknown: unknowns}
+	verificationResult := &brokerpb.VerificationResult{Success: overAllSuccess, ProviderSummary: verificationSummary, XEmbedded: verificationDetails}
 	PactLogger.Infof("Verification result retrieved successfully ...")
-	return &RetrieveVerificationResponse{
+	return &brokerpb.RetrieveVerificationResponse{
 		Response: pb.CreateResponse(pb.Response_SUCCESS, "Verification result retrieved successfully."),
 		Result:   verificationResult,
 	}, nil
 }
 
-func (*BrokerService) PublishVerificationResults(ctx context.Context, in *PublishVerificationRequest) (*PublishVerificationResponse, error) {
+func (*BrokerService) PublishVerificationResults(ctx context.Context, in *brokerpb.PublishVerificationRequest) (*brokerpb.PublishVerificationResponse, error) {
 	if in == nil || len(in.ProviderId) == 0 || len(in.ConsumerId) == 0 {
 		PactLogger.Errorf(nil, "verification result publish request failed: invalid params.")
-		return &PublishVerificationResponse{
+		return &brokerpb.PublishVerificationResponse{
 			Response: pb.CreateResponse(scerr.ErrInvalidParams, "Request format invalid."),
 		}, nil
 	}
@@ -592,13 +595,13 @@ func (*BrokerService) PublishVerificationResults(ctx context.Context, in *Publis
 	consumer, err := serviceUtil.GetService(ctx, tenant, in.ConsumerId)
 	if err != nil {
 		PactLogger.Errorf(err, "verification result publish request failed, consumerId is %s: query consumer failed.", in.ConsumerId)
-		return &PublishVerificationResponse{
+		return &brokerpb.PublishVerificationResponse{
 			Response: pb.CreateResponse(scerr.ErrInvalidParams, "Query consumer failed."),
 		}, err
 	}
 	if consumer == nil {
 		PactLogger.Errorf(nil, "verification result publish request failed, consumerId is %s: consumer not exist.", in.ConsumerId)
-		return &PublishVerificationResponse{
+		return &brokerpb.PublishVerificationResponse{
 			Response: pb.CreateResponse(scerr.ErrInvalidParams, "Consumer does not exist."),
 		}, nil
 	}
@@ -607,7 +610,7 @@ func (*BrokerService) PublishVerificationResults(ctx context.Context, in *Publis
 	consumerParticipant, err := GetParticipant(ctx, tenant, consumer.AppId, consumer.ServiceName)
 	if err != nil || consumerParticipant == nil {
 		PactLogger.Errorf(nil, "verification result publish request failed, consumer participant cannot be searched.", in.ConsumerId)
-		return &PublishVerificationResponse{
+		return &brokerpb.PublishVerificationResponse{
 			Response: pb.CreateResponse(scerr.ErrInvalidParams, "consumer participant cannot be searched."),
 		}, err
 	}
@@ -616,7 +619,7 @@ func (*BrokerService) PublishVerificationResults(ctx context.Context, in *Publis
 	version, err := GetVersion(ctx, tenant, consumer.Version, consumerParticipant.Id)
 	if err != nil || version == nil {
 		PactLogger.Errorf(nil, "verification result publish request failed, version cannot be searched.")
-		return &PublishVerificationResponse{
+		return &brokerpb.PublishVerificationResponse{
 			Response: pb.CreateResponse(scerr.ErrInvalidParams, "version cannot be searched."),
 		}, err
 	}
@@ -628,17 +631,17 @@ func (*BrokerService) PublishVerificationResults(ctx context.Context, in *Publis
 
 	if err != nil || len(pacts.Kvs) == 0 {
 		PactLogger.Errorf(nil, "verification result publish request failed, pact cannot be searched.")
-		return &PublishVerificationResponse{
+		return &brokerpb.PublishVerificationResponse{
 			Response: pb.CreateResponse(scerr.ErrInvalidParams, "pact cannot be searched."),
 		}, err
 	}
 	pactExists := false
 	for i := 0; i < len(pacts.Kvs); i++ {
-		pact := &Pact{}
+		pact := &brokerpb.Pact{}
 		err = json.Unmarshal(pacts.Kvs[i].Value.([]byte), &pact)
 		if err != nil {
 			PactLogger.Errorf(nil, "verification result publish request failed, pact cannot be searched.")
-			return &PublishVerificationResponse{
+			return &brokerpb.PublishVerificationResponse{
 				Response: pb.CreateResponse(scerr.ErrInvalidParams, "pact cannot be searched."),
 			}, err
 		}
@@ -648,14 +651,14 @@ func (*BrokerService) PublishVerificationResults(ctx context.Context, in *Publis
 	}
 	if pactExists == false {
 		PactLogger.Errorf(nil, "verification result publish request failed, pact does not exists.")
-		return &PublishVerificationResponse{
+		return &brokerpb.PublishVerificationResponse{
 			Response: pb.CreateResponse(scerr.ErrInvalidParams, "pact does not exists."),
 		}, err
 	}
 	pactVersion, err := GetPactVersion(ctx, tenant, version.Id, in.PactId)
 	if err != nil || pactVersion == nil {
 		PactLogger.Errorf(nil, "verification result publish request failed, pact version cannot be searched.")
-		return &PublishVerificationResponse{
+		return &brokerpb.PublishVerificationResponse{
 			Response: pb.CreateResponse(scerr.ErrInvalidParams, "pact version cannot be searched."),
 		}, err
 	}
@@ -667,18 +670,18 @@ func (*BrokerService) PublishVerificationResults(ctx context.Context, in *Publis
 
 	if err != nil {
 		PactLogger.Errorf(nil, "verification result publish request failed, verification result cannot be searched.")
-		return &PublishVerificationResponse{
+		return &brokerpb.PublishVerificationResponse{
 			Response: pb.CreateResponse(scerr.ErrInvalidParams, "verification result cannot be searched."),
 		}, err
 	}
 	lastNumber := int32(math.MinInt32)
 	if len(verifications.Kvs) != 0 {
 		for i := 0; i < len(verifications.Kvs); i++ {
-			verification := &Verification{}
+			verification := &brokerpb.Verification{}
 			err = json.Unmarshal(verifications.Kvs[i].Value.([]byte), &verification)
 			if err != nil {
 				PactLogger.Errorf(nil, "verification result publish request failed, verification result unmarshall error.")
-				return &PublishVerificationResponse{
+				return &brokerpb.PublishVerificationResponse{
 					Response: pb.CreateResponse(scerr.ErrInvalidParams, "verification result unmarshall error."),
 				}, err
 			}
@@ -695,7 +698,7 @@ func (*BrokerService) PublishVerificationResults(ctx context.Context, in *Publis
 	verificationDate := time.Now().Format(time.RFC3339)
 	verificationKey := GenerateBrokerVerificationKey(tenant, pactVersion.Id, lastNumber)
 	id, err := GetData(ctx, GetBrokerLatestVerificationIDKey())
-	verification := &Verification{
+	verification := &brokerpb.Verification{
 		Id:               int32(id) + 1,
 		Number:           lastNumber,
 		PactVersionId:    pactVersion.Id,
@@ -711,23 +714,23 @@ func (*BrokerService) PublishVerificationResults(ctx context.Context, in *Publis
 	PactLogger.Infof("Verification result inserted: (%d, %d, %d, %t, %s, %s, %s)",
 		verification.Id, verification.Number, verification.PactVersionId,
 		verification.Success, verification.ProviderVersion, verification.BuildUrl, verification.VerificationDate)
-	verificationResponse := &VerificationDetail{
+	verificationResponse := &brokerpb.VerificationDetail{
 		ProviderName:               in.ProviderId,
 		ProviderApplicationVersion: verification.ProviderVersion,
 		Success:                    verification.Success,
 		VerificationDate:           verification.VerificationDate,
 	}
 	PactLogger.Infof("Verification result published successfully ...")
-	return &PublishVerificationResponse{
+	return &brokerpb.PublishVerificationResponse{
 		Response:     pb.CreateResponse(pb.Response_SUCCESS, "Verification result published successfully."),
 		Confirmation: verificationResponse,
 	}, nil
 }
 
-func (*BrokerService) PublishPact(ctx context.Context, in *PublishPactRequest) (*PublishPactResponse, error) {
+func (*BrokerService) PublishPact(ctx context.Context, in *brokerpb.PublishPactRequest) (*brokerpb.PublishPactResponse, error) {
 	if in == nil || len(in.ProviderId) == 0 || len(in.ConsumerId) == 0 || len(in.Version) == 0 || len(in.Pact) == 0 {
 		PactLogger.Errorf(nil, "pact publish request failed: invalid params.")
-		return &PublishPactResponse{
+		return &brokerpb.PublishPactResponse{
 			Response: pb.CreateResponse(scerr.ErrInvalidParams, "Request format invalid."),
 		}, nil
 	}
@@ -736,13 +739,13 @@ func (*BrokerService) PublishPact(ctx context.Context, in *PublishPactRequest) (
 	provider, err := serviceUtil.GetService(ctx, tenant, in.ProviderId)
 	if err != nil {
 		PactLogger.Errorf(err, "pact publish failed, providerId is %s: query provider failed.", in.ProviderId)
-		return &PublishPactResponse{
+		return &brokerpb.PublishPactResponse{
 			Response: pb.CreateResponse(scerr.ErrInvalidParams, "Query provider failed."),
 		}, err
 	}
 	if provider == nil {
 		PactLogger.Errorf(nil, "pact publish failed, providerId is %s: provider not exist.", in.ProviderId)
-		return &PublishPactResponse{
+		return &brokerpb.PublishPactResponse{
 			Response: pb.CreateResponse(scerr.ErrInvalidParams, "Provider does not exist."),
 		}, nil
 	}
@@ -750,13 +753,13 @@ func (*BrokerService) PublishPact(ctx context.Context, in *PublishPactRequest) (
 	consumer, err := serviceUtil.GetService(ctx, tenant, in.ConsumerId)
 	if err != nil {
 		PactLogger.Errorf(err, "pact publish failed, consumerId is %s: query consumer failed.", in.ConsumerId)
-		return &PublishPactResponse{
+		return &brokerpb.PublishPactResponse{
 			Response: pb.CreateResponse(scerr.ErrInvalidParams, "Query consumer failed."),
 		}, err
 	}
 	if consumer == nil {
 		PactLogger.Errorf(nil, "pact publish failed, consumerId is %s: consumer not exist.", in.ConsumerId)
-		return &PublishPactResponse{
+		return &brokerpb.PublishPactResponse{
 			Response: pb.CreateResponse(scerr.ErrInvalidParams, "Consumer does not exist."),
 		}, nil
 	}
@@ -765,7 +768,7 @@ func (*BrokerService) PublishPact(ctx context.Context, in *PublishPactRequest) (
 	if strings.Compare(consumer.GetVersion(), in.Version) != 0 {
 		util.Logger().Errorf(nil,
 			"pact publish failed, version (%s) does not exist for consmer", in.Version)
-		return &PublishPactResponse{
+		return &brokerpb.PublishPactResponse{
 			Response: pb.CreateResponse(scerr.ErrInvalidParams, "Consumer Version does not exist."),
 		}, nil
 	}
@@ -776,13 +779,13 @@ func (*BrokerService) PublishPact(ctx context.Context, in *PublishPactRequest) (
 	providerParticipant, err := GetParticipant(ctx, tenant, provider.AppId, provider.ServiceName)
 	if err != nil {
 		PactLogger.Errorf(nil, "pact publish failed, provider participant cannot be searched.", in.ProviderId)
-		return &PublishPactResponse{
+		return &brokerpb.PublishPactResponse{
 			Response: pb.CreateResponse(scerr.ErrInvalidParams, "Provider participant cannot be searched."),
 		}, err
 	}
 	if providerParticipant == nil {
 		id, err := GetData(ctx, GetBrokerLatestParticipantIDKey())
-		providerParticipant = &Participant{Id: int32(id) + 1, AppId: provider.AppId, ServiceName: provider.ServiceName}
+		providerParticipant = &brokerpb.Participant{Id: int32(id) + 1, AppId: provider.AppId, ServiceName: provider.ServiceName}
 		response, err := CreateParticipant(PactLogger, ctx, providerParticipantKey, *providerParticipant)
 		if err != nil {
 			return response, err
@@ -794,13 +797,13 @@ func (*BrokerService) PublishPact(ctx context.Context, in *PublishPactRequest) (
 	consumerParticipant, err := GetParticipant(ctx, tenant, consumer.AppId, consumer.ServiceName)
 	if err != nil {
 		PactLogger.Errorf(nil, "pact publish failed, consumer participant cannot be searched.", in.ConsumerId)
-		return &PublishPactResponse{
+		return &brokerpb.PublishPactResponse{
 			Response: pb.CreateResponse(scerr.ErrInvalidParams, "consumer participant cannot be searched."),
 		}, err
 	}
 	if consumerParticipant == nil {
 		id, err := GetData(ctx, GetBrokerLatestParticipantIDKey())
-		consumerParticipant = &Participant{Id: int32(id) + 1, AppId: consumer.AppId, ServiceName: consumer.ServiceName}
+		consumerParticipant = &brokerpb.Participant{Id: int32(id) + 1, AppId: consumer.AppId, ServiceName: consumer.ServiceName}
 		response, err := CreateParticipant(PactLogger, ctx, consumerParticipantKey, *consumerParticipant)
 		if err != nil {
 			return response, err
@@ -812,7 +815,7 @@ func (*BrokerService) PublishPact(ctx context.Context, in *PublishPactRequest) (
 	version, err := GetVersion(ctx, tenant, in.Version, consumerParticipant.Id)
 	if err != nil {
 		PactLogger.Errorf(nil, "pact publish failed, version cannot be searched.")
-		return &PublishPactResponse{
+		return &brokerpb.PublishPactResponse{
 			Response: pb.CreateResponse(scerr.ErrInvalidParams, "version cannot be searched."),
 		}, err
 	}
@@ -821,7 +824,7 @@ func (*BrokerService) PublishPact(ctx context.Context, in *PublishPactRequest) (
 		PactLogger.Infof("Old version order: %d", order)
 		order++
 		id, err := GetData(ctx, GetBrokerLatestVersionIDKey())
-		version = &Version{Id: int32(id) + 1, Number: in.Version, ParticipantId: consumerParticipant.Id, Order: order}
+		version = &brokerpb.Version{Id: int32(id) + 1, Number: in.Version, ParticipantId: consumerParticipant.Id, Order: order}
 		response, err := CreateVersion(PactLogger, ctx, versionKey, *version)
 		if err != nil {
 			return response, err
@@ -835,13 +838,13 @@ func (*BrokerService) PublishPact(ctx context.Context, in *PublishPactRequest) (
 	pact, err := GetPact(ctx, tenant, consumerParticipant.Id, providerParticipant.Id, sha)
 	if err != nil {
 		PactLogger.Errorf(nil, "pact publish failed, pact cannot be searched.")
-		return &PublishPactResponse{
+		return &brokerpb.PublishPactResponse{
 			Response: pb.CreateResponse(scerr.ErrInvalidParams, "pact cannot be searched."),
 		}, err
 	}
 	if pact == nil {
 		id, err := GetData(ctx, GetBrokerLatestPactIDKey())
-		pact = &Pact{Id: int32(id) + 1, ConsumerParticipantId: consumerParticipant.Id,
+		pact = &brokerpb.Pact{Id: int32(id) + 1, ConsumerParticipantId: consumerParticipant.Id,
 			ProviderParticipantId: providerParticipant.Id, Sha: sha, Content: in.Pact}
 		response, err := CreatePact(PactLogger, ctx, pactKey, *pact)
 		if err != nil {
@@ -854,13 +857,13 @@ func (*BrokerService) PublishPact(ctx context.Context, in *PublishPactRequest) (
 	pactVersion, err := GetPactVersion(ctx, tenant, version.Id, pact.Id)
 	if err != nil {
 		PactLogger.Errorf(nil, "pact publish failed, pact version cannot be searched.")
-		return &PublishPactResponse{
+		return &brokerpb.PublishPactResponse{
 			Response: pb.CreateResponse(scerr.ErrInvalidParams, "pact version cannot be searched."),
 		}, err
 	}
 	if pactVersion == nil {
 		id, err := GetData(ctx, GetBrokerLatestPactVersionIDKey())
-		pactVersion = &PactVersion{Id: int32(id) + 1, VersionId: version.Id, PactId: pact.Id, ProviderParticipantId: providerParticipant.Id}
+		pactVersion = &brokerpb.PactVersion{Id: int32(id) + 1, VersionId: version.Id, PactId: pact.Id, ProviderParticipantId: providerParticipant.Id}
 		response, err := CreatePactVersion(PactLogger, ctx, pactVersionKey, *pactVersion)
 		if err != nil {
 			return response, err
@@ -868,7 +871,7 @@ func (*BrokerService) PublishPact(ctx context.Context, in *PublishPactRequest) (
 	}
 	PactLogger.Infof("PactVersion found/create: (%d, %d, %d, %d)", pactVersion.Id, pactVersion.VersionId, pactVersion.PactId, pactVersion.ProviderParticipantId)
 	PactLogger.Infof("Pact published successfully ...")
-	return &PublishPactResponse{
+	return &brokerpb.PublishPactResponse{
 		Response: pb.CreateResponse(pb.Response_SUCCESS, "Pact published successfully."),
 	}, nil
 }
diff --git a/server/broker/service_test.go b/server/broker/service_test.go
index ee273202..001bfa53 100644
--- a/server/broker/service_test.go
+++ b/server/broker/service_test.go
@@ -20,6 +20,8 @@ import (
 	"fmt"
 
 	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
+	"github.com/apache/incubator-servicecomb-service-center/server/broker/brokerpb"
+	"github.com/apache/incubator-servicecomb-service-center/server/core"
 	pb "github.com/apache/incubator-servicecomb-service-center/server/core/proto"
 	serviceUtil "github.com/apache/incubator-servicecomb-service-center/server/service/util"
 	. "github.com/onsi/ginkgo"
@@ -28,9 +30,8 @@ import (
 )
 
 const (
-	TEST_BROKER_NO_SERVICE_ID      = "noServiceId"
-	TEST_BROKER_NO_VERSION         = "noVersion"
-	TEST_BROKER_TOO_LONG_SERVICEID = "addasdfasaddasdfasaddasdfasaddasdfasaddasdfasaddasdfasaddasdfasadafd"
+	TEST_BROKER_NO_SERVICE_ID = "noServiceId"
+	TEST_BROKER_NO_VERSION    = "noVersion"
 	//Consumer
 	TEST_BROKER_CONSUMER_VERSION = "4.0.0"
 	TEST_BROKER_CONSUMER_NAME    = "broker_name_consumer"
@@ -51,7 +52,7 @@ var _ = Describe("BrokerController", func() {
 				fmt.Println("UT===========PublishPact")
 
 				//(1) create consumer service
-				resp, err := serviceResource.Create(getContext(), &pb.CreateServiceRequest{
+				resp, err := core.ServiceAPI.Create(getContext(), &pb.CreateServiceRequest{
 					Service: &pb.MicroService{
 						ServiceName: TEST_BROKER_CONSUMER_NAME,
 						AppId:       TEST_BROKER_CONSUMER_APP,
@@ -69,7 +70,7 @@ var _ = Describe("BrokerController", func() {
 				Expect(resp.GetResponse().Code).To(Equal(pb.Response_SUCCESS))
 
 				//(2) create provider service
-				resp, err = serviceResource.Create(getContext(), &pb.CreateServiceRequest{
+				resp, err = core.ServiceAPI.Create(getContext(), &pb.CreateServiceRequest{
 					Service: &pb.MicroService{
 						ServiceName: TEST_BROKER_PROVIDER_NAME,
 						AppId:       TEST_BROKER_PROVIDER_APP,
@@ -88,7 +89,7 @@ var _ = Describe("BrokerController", func() {
 
 				//(3) publish a pact between two services
 				respPublishPact, err := brokerResource.PublishPact(getContext(),
-					&PublishPactRequest{
+					&brokerpb.PublishPactRequest{
 						ProviderId: providerServiceId,
 						ConsumerId: consumerServiceId,
 						Version:    TEST_BROKER_CONSUMER_VERSION,
@@ -103,7 +104,7 @@ var _ = Describe("BrokerController", func() {
 				fmt.Println("UT===========PublishPact, no provider serviceID")
 
 				//publish a pact between two services
-				respPublishPact, _ := brokerResource.PublishPact(getContext(), &PublishPactRequest{
+				respPublishPact, _ := brokerResource.PublishPact(getContext(), &brokerpb.PublishPactRequest{
 					ProviderId: TEST_BROKER_NO_SERVICE_ID,
 					ConsumerId: consumerServiceId,
 					Version:    TEST_BROKER_CONSUMER_VERSION,
@@ -117,7 +118,7 @@ var _ = Describe("BrokerController", func() {
 				fmt.Println("UT===========PublishPact, no consumer serviceID")
 
 				//publish a pact between two services
-				respPublishPact, _ := brokerResource.PublishPact(getContext(), &PublishPactRequest{
+				respPublishPact, _ := brokerResource.PublishPact(getContext(), &brokerpb.PublishPactRequest{
 					ProviderId: providerServiceId,
 					ConsumerId: TEST_BROKER_NO_SERVICE_ID,
 					Version:    TEST_BROKER_CONSUMER_VERSION,
@@ -131,7 +132,7 @@ var _ = Describe("BrokerController", func() {
 				fmt.Println("UT===========PublishPact, no consumer Version")
 
 				//publish a pact between two services
-				respPublishPact, _ := brokerResource.PublishPact(getContext(), &PublishPactRequest{
+				respPublishPact, _ := brokerResource.PublishPact(getContext(), &brokerpb.PublishPactRequest{
 					ProviderId: providerServiceId,
 					ConsumerId: consumerServiceId,
 					Version:    TEST_BROKER_NO_VERSION,
@@ -144,7 +145,7 @@ var _ = Describe("BrokerController", func() {
 			It("GetBrokerHome", func() {
 				fmt.Println("UT===========GetBrokerHome")
 
-				respGetHome, _ := brokerResource.GetBrokerHome(getContext(), &BaseBrokerRequest{
+				respGetHome, _ := brokerResource.GetBrokerHome(getContext(), &brokerpb.BaseBrokerRequest{
 					HostAddress: "localhost",
 					Scheme:      "http",
 				})
@@ -157,9 +158,9 @@ var _ = Describe("BrokerController", func() {
 				fmt.Println("UT===========GetBrokerAllProviderPacts")
 
 				respGetAllProviderPacts, _ := brokerResource.GetAllProviderPacts(getContext(),
-					&GetAllProviderPactsRequest{
+					&brokerpb.GetAllProviderPactsRequest{
 						ProviderId: providerServiceId,
-						BaseUrl: &BaseBrokerRequest{
+						BaseUrl: &brokerpb.BaseBrokerRequest{
 							HostAddress: "localhost",
 							Scheme:      "http",
 						}})
@@ -172,11 +173,11 @@ var _ = Describe("BrokerController", func() {
 				fmt.Println("UT===========GetBrokerPactsOfProvider")
 
 				respGetAllProviderPacts, _ := brokerResource.GetPactsOfProvider(getContext(),
-					&GetProviderConsumerVersionPactRequest{
+					&brokerpb.GetProviderConsumerVersionPactRequest{
 						ProviderId: providerServiceId,
 						ConsumerId: consumerServiceId,
 						Version:    TEST_BROKER_CONSUMER_VERSION,
-						BaseUrl: &BaseBrokerRequest{
+						BaseUrl: &brokerpb.BaseBrokerRequest{
 							HostAddress: "localhost",
 							Scheme:      "http",
 						}})
@@ -185,6 +186,51 @@ var _ = Describe("BrokerController", func() {
 				Expect(respGetAllProviderPacts.GetResponse().Code).To(Equal(pb.Response_SUCCESS))
 			})
 
+			It("PublishVerificationResults", func() {
+				fmt.Println("UT===========PublishVerificationResults")
+
+				id, err := GetData(context.Background(), GetBrokerLatestPactIDKey())
+				Expect(err).To(BeNil())
+				respResults, err := brokerResource.PublishVerificationResults(getContext(),
+					&brokerpb.PublishVerificationRequest{
+						ProviderId: providerServiceId,
+						ConsumerId: consumerServiceId,
+						PactId:     int32(id),
+						ProviderApplicationVersion: TEST_BROKER_PROVIDER_VERSION,
+					})
+
+				Expect(respResults).NotTo(BeNil())
+				Expect(respResults.GetResponse().Code).To(Equal(pb.Response_SUCCESS))
+			})
+
+			It("RetrieveVerificationResults", func() {
+				fmt.Println("UT===========RetrieveVerificationResults")
+
+				respVerification, _ := brokerResource.RetrieveVerificationResults(getContext(),
+					&brokerpb.RetrieveVerificationRequest{
+						ConsumerId:      consumerServiceId,
+						ConsumerVersion: TEST_BROKER_CONSUMER_VERSION,
+					})
+
+				Expect(respVerification).NotTo(BeNil())
+				Expect(respVerification.GetResponse().Code).To(Equal(pb.Response_SUCCESS))
+			})
+
+			It("RetrieveProviderPacts", func() {
+				fmt.Println("UT===========RetrieveProviderPacts")
+
+				respProviderPact, _ := brokerResource.RetrieveProviderPacts(getContext(),
+					&brokerpb.GetAllProviderPactsRequest{
+						ProviderId: providerServiceId,
+						BaseUrl: &brokerpb.BaseBrokerRequest{
+							HostAddress: "localhost",
+							Scheme:      "http",
+						},
+					})
+
+				Expect(respProviderPact).NotTo(BeNil())
+				Expect(respProviderPact.GetResponse().Code).To(Equal(pb.Response_SUCCESS))
+			})
 		})
 	})
 })
diff --git a/server/broker/store.go b/server/broker/store.go
index cb73923a..0754c0ce 100644
--- a/server/broker/store.go
+++ b/server/broker/store.go
@@ -33,13 +33,13 @@ var (
 var brokerKvStore = &BKvStore{}
 
 func init() {
-	PARTICIPANT = backend.Store().MustInstall(backend.NewEntity("PARTICIPANT", backend.DefaultConfig().WithPrefix(GetBrokerParticipantKey(""))))
-	VERSION = backend.Store().MustInstall(backend.NewEntity("VERSION", backend.DefaultConfig().WithPrefix(GetBrokerVersionKey(""))))
-	PACT = backend.Store().MustInstall(backend.NewEntity("PACT", backend.DefaultConfig().WithPrefix(GetBrokerPactKey(""))))
-	PACT_VERSION = backend.Store().MustInstall(backend.NewEntity("PACT_VERSION", backend.DefaultConfig().WithPrefix(GetBrokerPactVersionKey(""))))
-	PACT_TAG = backend.Store().MustInstall(backend.NewEntity("PACT_TAG", backend.DefaultConfig().WithPrefix(GetBrokerTagKey(""))))
-	VERIFICATION = backend.Store().MustInstall(backend.NewEntity("VERIFICATION", backend.DefaultConfig().WithPrefix(GetBrokerVerificationKey(""))))
-	PACT_LATEST = backend.Store().MustInstall(backend.NewEntity("PACT_LATEST", backend.DefaultConfig().WithPrefix(GetBrokerLatestKey(""))))
+	PARTICIPANT = backend.Store().MustInstall(backend.NewExtension("PARTICIPANT", backend.Configure().WithPrefix(GetBrokerParticipantKey(""))))
+	VERSION = backend.Store().MustInstall(backend.NewExtension("VERSION", backend.Configure().WithPrefix(GetBrokerVersionKey(""))))
+	PACT = backend.Store().MustInstall(backend.NewExtension("PACT", backend.Configure().WithPrefix(GetBrokerPactKey(""))))
+	PACT_VERSION = backend.Store().MustInstall(backend.NewExtension("PACT_VERSION", backend.Configure().WithPrefix(GetBrokerPactVersionKey(""))))
+	PACT_TAG = backend.Store().MustInstall(backend.NewExtension("PACT_TAG", backend.Configure().WithPrefix(GetBrokerTagKey(""))))
+	VERIFICATION = backend.Store().MustInstall(backend.NewExtension("VERIFICATION", backend.Configure().WithPrefix(GetBrokerVerificationKey(""))))
+	PACT_LATEST = backend.Store().MustInstall(backend.NewExtension("PACT_LATEST", backend.Configure().WithPrefix(GetBrokerLatestKey(""))))
 
 }
 
@@ -47,31 +47,31 @@ type BKvStore struct {
 }
 
 func (s *BKvStore) Participant() backend.Indexer {
-	return backend.Store().Entity(PARTICIPANT)
+	return backend.Store().Entities(PARTICIPANT)
 }
 
 func (s *BKvStore) Version() backend.Indexer {
-	return backend.Store().Entity(VERSION)
+	return backend.Store().Entities(VERSION)
 }
 
 func (s *BKvStore) Pact() backend.Indexer {
-	return backend.Store().Entity(PACT)
+	return backend.Store().Entities(PACT)
 }
 
 func (s *BKvStore) PactVersion() backend.Indexer {
-	return backend.Store().Entity(PACT_VERSION)
+	return backend.Store().Entities(PACT_VERSION)
 }
 
 func (s *BKvStore) PactTag() backend.Indexer {
-	return backend.Store().Entity(PACT_TAG)
+	return backend.Store().Entities(PACT_TAG)
 }
 
 func (s *BKvStore) Verification() backend.Indexer {
-	return backend.Store().Entity(VERIFICATION)
+	return backend.Store().Entities(VERIFICATION)
 }
 
 func (s *BKvStore) PactLatest() backend.Indexer {
-	return backend.Store().Entity(PACT_LATEST)
+	return backend.Store().Entities(PACT_LATEST)
 }
 
 func Store() *BKvStore {
diff --git a/server/broker/util.go b/server/broker/util.go
index 3d398b50..8f8e70ab 100644
--- a/server/broker/util.go
+++ b/server/broker/util.go
@@ -19,7 +19,6 @@ package broker
 import (
 	"context"
 	"encoding/json"
-	"errors"
 	"math"
 	"net/url"
 	"strconv"
@@ -27,6 +26,7 @@ import (
 
 	"github.com/ServiceComb/paas-lager/third_party/forked/cloudfoundry/lager"
 	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
+	"github.com/apache/incubator-servicecomb-service-center/server/broker/brokerpb"
 	"github.com/apache/incubator-servicecomb-service-center/server/core"
 	"github.com/apache/incubator-servicecomb-service-center/server/core/backend"
 	pb "github.com/apache/incubator-servicecomb-service-center/server/core/proto"
@@ -132,27 +132,27 @@ func GetBrokerHomeLinksAPIS(scheme string, host string, apiKey string) string {
 }
 
 //CreateBrokerHomeResponse create the templated broker home response
-func CreateBrokerHomeResponse(host string, scheme string) *BrokerHomeResponse {
+func CreateBrokerHomeResponse(host string, scheme string) *brokerpb.BrokerHomeResponse {
 
-	var apiEntries map[string]*BrokerAPIInfoEntry
-	apiEntries = make(map[string]*BrokerAPIInfoEntry)
+	var apiEntries map[string]*brokerpb.BrokerAPIInfoEntry
+	apiEntries = make(map[string]*brokerpb.BrokerAPIInfoEntry)
 
 	for k := range brokerAPILinksValues {
-		apiEntries[k] = &BrokerAPIInfoEntry{
+		apiEntries[k] = &brokerpb.BrokerAPIInfoEntry{
 			Href:      GetBrokerHomeLinksAPIS(scheme, host, k),
 			Title:     brokerAPILinksTitles[k],
 			Templated: brokerAPILinksTempl[k],
 		}
 	}
 
-	curies := []*BrokerAPIInfoEntry{}
-	curies = append(curies, &BrokerAPIInfoEntry{
+	curies := []*brokerpb.BrokerAPIInfoEntry{}
+	curies = append(curies, &brokerpb.BrokerAPIInfoEntry{
 		Name: "pb",
 		Href: GenerateBrokerAPIPath(scheme, host, BROKER_CURIES_URL,
 			strings.NewReplacer(":rel", "{rel}")),
 	})
 
-	return &BrokerHomeResponse{
+	return &brokerpb.BrokerHomeResponse{
 		Response: pb.CreateResponse(pb.Response_SUCCESS, "Broker Home."),
 		XLinks:   apiEntries,
 		Curies:   curies,
@@ -160,7 +160,7 @@ func CreateBrokerHomeResponse(host string, scheme string) *BrokerHomeResponse {
 }
 
 //GetBrokerHomeResponse gets the homeResponse from cache if it exists
-func GetBrokerHomeResponse(host string, scheme string) *BrokerHomeResponse {
+func GetBrokerHomeResponse(host string, scheme string) *brokerpb.BrokerHomeResponse {
 	brokerResp := CreateBrokerHomeResponse(host, scheme)
 	if brokerResp == nil {
 		return nil
@@ -168,87 +168,8 @@ func GetBrokerHomeResponse(host string, scheme string) *BrokerHomeResponse {
 	return brokerResp
 }
 
-//GetBrokerParticipantUtils returns the participant from ETCD
-func GetBrokerParticipantUtils(ctx context.Context, tenant string, appId string,
-	serviceName string, opts ...registry.PluginOpOption) (*Participant, error) {
-
-	key := GenerateBrokerParticipantKey(tenant, appId, serviceName)
-	opts = append(opts, registry.WithStrKey(key))
-	participants, err := Store().Participant().Search(ctx, opts...)
-
-	if err != nil {
-		PactLogger.Errorf(nil, "pact publish failed, participant with, could not be searched.")
-		return nil, err
-	}
-
-	if len(participants.Kvs) == 0 {
-		PactLogger.Info("GetParticipant found no participant")
-		return nil, nil
-	}
-
-	participant := &Participant{}
-	err = json.Unmarshal(participants.Kvs[0].Value.([]byte), participant)
-	if err != nil {
-		return nil, err
-	}
-	PactLogger.Infof("GetParticipant: (%d, %s, %s)", participant.Id, participant.AppId,
-		participant.ServiceName)
-	return participant, nil
-}
-
-//GetBrokerParticipantFromServiceId returns the participant and the service from ETCD
-func GetBrokerParticipantFromServiceId(ctx context.Context, serviceId string) (*Participant,
-	*pb.MicroService, error, error) {
-
-	tenant := GetDefaultTenantProject()
-	serviceParticipant, err := serviceUtil.GetService(ctx, tenant, serviceId)
-	if err != nil {
-		PactLogger.Errorf(err,
-			"get participant failed, serviceId is %s: query provider failed.", serviceId)
-		return nil, nil, nil, err
-	}
-	if serviceParticipant == nil {
-		PactLogger.Errorf(nil,
-			"get participant failed, serviceId is %s: service not exist.", serviceId)
-		return nil, nil, nil, errors.New("get participant, serviceId not exist.")
-	}
-	// Get or create provider participant
-	participant, errBroker := GetBrokerParticipantUtils(ctx, tenant, serviceParticipant.AppId,
-		serviceParticipant.ServiceName)
-	if errBroker != nil {
-		PactLogger.Errorf(errBroker,
-			"get participant failed, serviceId %s: query participant failed.", serviceId)
-		return nil, serviceParticipant, errBroker, err
-	}
-	if participant == nil {
-		PactLogger.Errorf(nil,
-			"get participant failed, particpant does not exist for serviceId %s", serviceId)
-		return nil, serviceParticipant, errors.New("particpant does not exist for serviceId."), err
-	}
-
-	return participant, serviceParticipant, errBroker, nil
-}
-
-//GetBrokerParticipantFromService returns the participant given the microservice
-func GetBrokerParticipantFromService(ctx context.Context,
-	microservice *pb.MicroService) (*Participant, error) {
-	if microservice == nil {
-		return nil, nil
-	}
-	tenant := GetDefaultTenantProject()
-	participant, errBroker := GetBrokerParticipantUtils(ctx, tenant, microservice.AppId,
-		microservice.ServiceName)
-	if errBroker != nil {
-		PactLogger.Errorf(errBroker,
-			"get participant failed, serviceId %s: query participant failed.",
-			microservice.ServiceId)
-		return nil, errBroker
-	}
-	return participant, errBroker
-}
-
 func GetParticipant(ctx context.Context, domain string, appId string,
-	serviceName string) (*Participant, error) {
+	serviceName string) (*brokerpb.Participant, error) {
 	key := GenerateBrokerParticipantKey(domain, appId, serviceName)
 	participants, err := Store().Participant().Search(ctx, registry.WithStrKey(key))
 	if err != nil {
@@ -258,7 +179,7 @@ func GetParticipant(ctx context.Context, domain string, appId string,
 		PactLogger.Info("GetParticipant found no participant")
 		return nil, nil
 	}
-	participant := &Participant{}
+	participant := &brokerpb.Participant{}
 	err = json.Unmarshal(participants.Kvs[0].Value.([]byte), participant)
 	if err != nil {
 		return nil, err
@@ -268,7 +189,7 @@ func GetParticipant(ctx context.Context, domain string, appId string,
 }
 
 func GetVersion(ctx context.Context, domain string, number string,
-	participantId int32) (*Version, error) {
+	participantId int32) (*brokerpb.Version, error) {
 	key := GenerateBrokerVersionKey(domain, number, participantId)
 	versions, err := Store().Version().Search(ctx, registry.WithStrKey(key))
 	if err != nil {
@@ -277,7 +198,7 @@ func GetVersion(ctx context.Context, domain string, number string,
 	if len(versions.Kvs) == 0 {
 		return nil, nil
 	}
-	version := &Version{}
+	version := &brokerpb.Version{}
 	err = json.Unmarshal(versions.Kvs[0].Value.([]byte), version)
 	if err != nil {
 		return nil, err
@@ -286,7 +207,7 @@ func GetVersion(ctx context.Context, domain string, number string,
 	return version, nil
 }
 
-func GetPact(ctx context.Context, domain string, consumerParticipantId int32, producerParticipantId int32, sha []byte) (*Pact, error) {
+func GetPact(ctx context.Context, domain string, consumerParticipantId int32, producerParticipantId int32, sha []byte) (*brokerpb.Pact, error) {
 	key := GenerateBrokerPactKey(domain, consumerParticipantId, producerParticipantId, sha)
 	versions, err := Store().Pact().Search(ctx, registry.WithStrKey(key))
 	if err != nil {
@@ -295,7 +216,7 @@ func GetPact(ctx context.Context, domain string, consumerParticipantId int32, pr
 	if len(versions.Kvs) == 0 {
 		return nil, nil
 	}
-	pact := &Pact{}
+	pact := &brokerpb.Pact{}
 	err = json.Unmarshal(versions.Kvs[0].Value.([]byte), pact)
 	if err != nil {
 		return nil, err
@@ -305,7 +226,7 @@ func GetPact(ctx context.Context, domain string, consumerParticipantId int32, pr
 }
 
 func GetPactVersion(ctx context.Context, domain string, versionId int32,
-	pactId int32) (*PactVersion, error) {
+	pactId int32) (*brokerpb.PactVersion, error) {
 	key := GenerateBrokerPactVersionKey(domain, versionId, pactId)
 	versions, err := Store().PactVersion().Search(ctx, registry.WithStrKey(key))
 	if err != nil {
@@ -314,7 +235,7 @@ func GetPactVersion(ctx context.Context, domain string, versionId int32,
 	if len(versions.Kvs) == 0 {
 		return nil, nil
 	}
-	pactVersion := &PactVersion{}
+	pactVersion := &brokerpb.PactVersion{}
 	err = json.Unmarshal(versions.Kvs[0].Value.([]byte), pactVersion)
 	if err != nil {
 		return nil, err
@@ -345,11 +266,11 @@ func StoreData(ctx context.Context, key string, value string) error {
 	return err
 }
 
-func CreateParticipant(pactLogger lager.Logger, ctx context.Context, participantKey string, participant Participant) (*PublishPactResponse, error) {
+func CreateParticipant(pactLogger lager.Logger, ctx context.Context, participantKey string, participant brokerpb.Participant) (*brokerpb.PublishPactResponse, error) {
 	data, err := json.Marshal(participant)
 	if err != nil {
 		PactLogger.Errorf(nil, "pact publish failed, participant cannot be created.")
-		return &PublishPactResponse{
+		return &brokerpb.PublishPactResponse{
 			Response: pb.CreateResponse(scerr.ErrInternal, "participant cannot be created."),
 		}, err
 	}
@@ -360,7 +281,7 @@ func CreateParticipant(pactLogger lager.Logger, ctx context.Context, participant
 
 	if err != nil {
 		PactLogger.Errorf(nil, "pact publish failed, participant cannot be created.")
-		return &PublishPactResponse{
+		return &brokerpb.PublishPactResponse{
 			Response: pb.CreateResponse(scerr.ErrInternal, "participant cannot be created."),
 		}, err
 	}
@@ -371,7 +292,7 @@ func CreateParticipant(pactLogger lager.Logger, ctx context.Context, participant
 	err = StoreData(ctx, k, v)
 	if err != nil {
 		PactLogger.Errorf(nil, "pact publish failed, participant cannot be created.")
-		return &PublishPactResponse{
+		return &brokerpb.PublishPactResponse{
 			Response: pb.CreateResponse(scerr.ErrInternal, "participant cannot be created."),
 		}, err
 	}
@@ -380,11 +301,11 @@ func CreateParticipant(pactLogger lager.Logger, ctx context.Context, participant
 }
 
 func CreateVersion(pactLogger lager.Logger, ctx context.Context, versionKey string,
-	version Version) (*PublishPactResponse, error) {
+	version brokerpb.Version) (*brokerpb.PublishPactResponse, error) {
 	data, err := json.Marshal(version)
 	if err != nil {
 		PactLogger.Errorf(nil, "pact publish failed, version cannot be created.")
-		return &PublishPactResponse{
+		return &brokerpb.PublishPactResponse{
 			Response: pb.CreateResponse(scerr.ErrInternal, "version cannot be created."),
 		}, err
 	}
@@ -394,7 +315,7 @@ func CreateVersion(pactLogger lager.Logger, ctx context.Context, versionKey stri
 		registry.WithValue(data))
 	if err != nil {
 		PactLogger.Errorf(nil, "pact publish failed, version cannot be created.")
-		return &PublishPactResponse{
+		return &brokerpb.PublishPactResponse{
 			Response: pb.CreateResponse(scerr.ErrInternal, "version cannot be created."),
 		}, err
 	}
@@ -404,7 +325,7 @@ func CreateVersion(pactLogger lager.Logger, ctx context.Context, versionKey stri
 	err = StoreData(ctx, k, v)
 	if err != nil {
 		PactLogger.Errorf(nil, "pact publish failed, version cannot be created.")
-		return &PublishPactResponse{
+		return &brokerpb.PublishPactResponse{
 			Response: pb.CreateResponse(scerr.ErrInternal, "version cannot be created."),
 		}, err
 	}
@@ -413,11 +334,11 @@ func CreateVersion(pactLogger lager.Logger, ctx context.Context, versionKey stri
 }
 
 func CreatePact(pactLogger lager.Logger, ctx context.Context,
-	pactKey string, pact Pact) (*PublishPactResponse, error) {
+	pactKey string, pact brokerpb.Pact) (*brokerpb.PublishPactResponse, error) {
 	data, err := json.Marshal(pact)
 	if err != nil {
 		PactLogger.Errorf(nil, "pact publish failed, pact cannot be created.")
-		return &PublishPactResponse{
+		return &brokerpb.PublishPactResponse{
 			Response: pb.CreateResponse(scerr.ErrInternal, "pact cannot be created."),
 		}, err
 	}
@@ -429,7 +350,7 @@ func CreatePact(pactLogger lager.Logger, ctx context.Context,
 
 	if err != nil {
 		PactLogger.Errorf(nil, "pact publish failed, pact cannot be created.")
-		return &PublishPactResponse{
+		return &brokerpb.PublishPactResponse{
 			Response: pb.CreateResponse(scerr.ErrInternal, "pact cannot be created."),
 		}, err
 	}
@@ -439,7 +360,7 @@ func CreatePact(pactLogger lager.Logger, ctx context.Context,
 	err = StoreData(ctx, k, v)
 	if err != nil {
 		PactLogger.Errorf(nil, "pact publish failed, pact cannot be created.")
-		return &PublishPactResponse{
+		return &brokerpb.PublishPactResponse{
 			Response: pb.CreateResponse(scerr.ErrInternal, "pact cannot be created."),
 		}, err
 	}
@@ -447,11 +368,11 @@ func CreatePact(pactLogger lager.Logger, ctx context.Context,
 	return nil, nil
 }
 
-func CreatePactVersion(pactLogger lager.Logger, ctx context.Context, pactVersionKey string, pactVersion PactVersion) (*PublishPactResponse, error) {
+func CreatePactVersion(pactLogger lager.Logger, ctx context.Context, pactVersionKey string, pactVersion brokerpb.PactVersion) (*brokerpb.PublishPactResponse, error) {
 	data, err := json.Marshal(pactVersion)
 	if err != nil {
 		PactLogger.Errorf(nil, "pact publish failed, pact version cannot be created.")
-		return &PublishPactResponse{
+		return &brokerpb.PublishPactResponse{
 			Response: pb.CreateResponse(scerr.ErrInternal, "pact version cannot be created."),
 		}, err
 	}
@@ -460,7 +381,7 @@ func CreatePactVersion(pactLogger lager.Logger, ctx context.Context, pactVersion
 		registry.PUT, registry.WithValue(data), registry.WithStrKey(pactVersionKey))
 	if err != nil {
 		PactLogger.Errorf(nil, "pact publish failed, pact version cannot be created.")
-		return &PublishPactResponse{
+		return &brokerpb.PublishPactResponse{
 			Response: pb.CreateResponse(scerr.ErrInternal, "pact version cannot be created."),
 		}, err
 	}
@@ -470,7 +391,7 @@ func CreatePactVersion(pactLogger lager.Logger, ctx context.Context, pactVersion
 	err = StoreData(ctx, k, v)
 	if err != nil {
 		PactLogger.Errorf(nil, "pact publish failed, pact version cannot be created.")
-		return &PublishPactResponse{
+		return &brokerpb.PublishPactResponse{
 			Response: pb.CreateResponse(scerr.ErrInternal, "pact version cannot be created."),
 		}, err
 	}
@@ -479,11 +400,11 @@ func CreatePactVersion(pactLogger lager.Logger, ctx context.Context, pactVersion
 }
 
 func CreateVerification(pactLogger lager.Logger, ctx context.Context,
-	verificationKey string, verification Verification) (*PublishVerificationResponse, error) {
+	verificationKey string, verification brokerpb.Verification) (*brokerpb.PublishVerificationResponse, error) {
 	data, err := json.Marshal(verification)
 	if err != nil {
 		PactLogger.Errorf(nil, "verification result publish failed, verification result marshal error.")
-		return &PublishVerificationResponse{
+		return &brokerpb.PublishVerificationResponse{
 			Response: pb.CreateResponse(scerr.ErrInternal, "verification result marshal error."),
 		}, err
 	}
@@ -493,7 +414,7 @@ func CreateVerification(pactLogger lager.Logger, ctx context.Context,
 		registry.WithValue(data))
 	if err != nil {
 		PactLogger.Errorf(nil, "verification result publish failed, verification result cannot be created.")
-		return &PublishVerificationResponse{
+		return &brokerpb.PublishVerificationResponse{
 			Response: pb.CreateResponse(scerr.ErrInternal, "verification result cannot be created."),
 		}, err
 	}
@@ -503,7 +424,7 @@ func CreateVerification(pactLogger lager.Logger, ctx context.Context,
 	err = StoreData(ctx, k, v)
 	if err != nil {
 		PactLogger.Errorf(nil, "verification result publish failed, verification result cannot be created.")
-		return &PublishVerificationResponse{
+		return &brokerpb.PublishVerificationResponse{
 			Response: pb.CreateResponse(scerr.ErrInternal, "verification result cannot be created."),
 		}, err
 	}
@@ -524,7 +445,7 @@ func GetLastestVersionNumberForParticipant(ctx context.Context,
 	}
 	order := int32(math.MinInt32)
 	for i := 0; i < len(versions.Kvs); i++ {
-		version := &Version{}
+		version := &brokerpb.Version{}
 		err = json.Unmarshal(versions.Kvs[i].Value.([]byte), &version)
 		if err != nil {
 			return -1
@@ -540,10 +461,10 @@ func GetLastestVersionNumberForParticipant(ctx context.Context,
 }
 
 func RetrieveProviderConsumerPact(ctx context.Context,
-	in *GetProviderConsumerVersionPactRequest) (*GetProviderConsumerVersionPactResponse, int32, error) {
+	in *brokerpb.GetProviderConsumerVersionPactRequest) (*brokerpb.GetProviderConsumerVersionPactResponse, int32, error) {
 	if in == nil || len(in.ProviderId) == 0 || len(in.ConsumerId) == 0 || len(in.Version) == 0 {
 		PactLogger.Errorf(nil, "pact retrieve request failed: invalid params.")
-		return &GetProviderConsumerVersionPactResponse{
+		return &brokerpb.GetProviderConsumerVersionPactResponse{
 			Response: pb.CreateResponse(scerr.ErrInternal, "Request format invalid."),
 		}, -1, nil
 	}
@@ -552,13 +473,13 @@ func RetrieveProviderConsumerPact(ctx context.Context,
 	provider, err := serviceUtil.GetService(ctx, tenant, in.ProviderId)
 	if err != nil {
 		PactLogger.Errorf(err, "pact retrieve failed, providerId is %s: query provider failed.", in.ProviderId)
-		return &GetProviderConsumerVersionPactResponse{
+		return &brokerpb.GetProviderConsumerVersionPactResponse{
 			Response: pb.CreateResponse(scerr.ErrInternal, "Query provider failed."),
 		}, -1, err
 	}
 	if provider == nil {
 		PactLogger.Errorf(nil, "pact retrieve failed, providerId is %s: provider not exist.", in.ProviderId)
-		return &GetProviderConsumerVersionPactResponse{
+		return &brokerpb.GetProviderConsumerVersionPactResponse{
 			Response: pb.CreateResponse(scerr.ErrInternal, "Provider does not exist."),
 		}, -1, nil
 	}
@@ -566,13 +487,13 @@ func RetrieveProviderConsumerPact(ctx context.Context,
 	consumer, err := serviceUtil.GetService(ctx, tenant, in.ConsumerId)
 	if err != nil {
 		PactLogger.Errorf(err, "pact retrieve failed, consumerId is %s: query consumer failed.", in.ConsumerId)
-		return &GetProviderConsumerVersionPactResponse{
+		return &brokerpb.GetProviderConsumerVersionPactResponse{
 			Response: pb.CreateResponse(scerr.ErrInternal, "Query consumer failed."),
 		}, -1, err
 	}
 	if consumer == nil {
 		PactLogger.Errorf(nil, "pact retrieve failed, consumerId is %s: consumer not exist.", in.ConsumerId)
-		return &GetProviderConsumerVersionPactResponse{
+		return &brokerpb.GetProviderConsumerVersionPactResponse{
 			Response: pb.CreateResponse(scerr.ErrInternal, "Consumer does not exist."),
 		}, -1, nil
 	}
@@ -581,7 +502,7 @@ func RetrieveProviderConsumerPact(ctx context.Context,
 	providerParticipant, err := GetParticipant(ctx, tenant, provider.AppId, provider.ServiceName)
 	if err != nil || providerParticipant == nil {
 		PactLogger.Errorf(nil, "pact retrieve failed, provider participant %s cannot be searched.", in.ProviderId)
-		return &GetProviderConsumerVersionPactResponse{
+		return &brokerpb.GetProviderConsumerVersionPactResponse{
 			Response: pb.CreateResponse(scerr.ErrInternal, "Provider participant cannot be searched."),
 		}, -1, err
 	}
@@ -590,7 +511,7 @@ func RetrieveProviderConsumerPact(ctx context.Context,
 	consumerParticipant, err := GetParticipant(ctx, tenant, consumer.AppId, consumer.ServiceName)
 	if err != nil || consumerParticipant == nil {
 		PactLogger.Errorf(nil, "pact retrieve failed, consumer participant %s cannot be searched.", in.ConsumerId)
-		return &GetProviderConsumerVersionPactResponse{
+		return &brokerpb.GetProviderConsumerVersionPactResponse{
 			Response: pb.CreateResponse(scerr.ErrInternal, "consumer participant cannot be searched."),
 		}, -1, err
 	}
@@ -599,7 +520,7 @@ func RetrieveProviderConsumerPact(ctx context.Context,
 	version, err := GetVersion(ctx, tenant, in.Version, consumerParticipant.Id)
 	if err != nil || version == nil {
 		PactLogger.Errorf(nil, "pact retrieve failed, version cannot be searched.")
-		return &GetProviderConsumerVersionPactResponse{
+		return &brokerpb.GetProviderConsumerVersionPactResponse{
 			Response: pb.CreateResponse(scerr.ErrInternal, "version cannot be searched."),
 		}, -1, err
 	}
@@ -621,7 +542,7 @@ func RetrieveProviderConsumerPact(ctx context.Context,
 	}
 	pactIds := make(map[int32]int32)
 	for i := 0; i < len(pactVersions.Kvs); i++ {
-		pactVersion := &PactVersion{}
+		pactVersion := &brokerpb.PactVersion{}
 		err = json.Unmarshal(pactVersions.Kvs[i].Value.([]byte), pactVersion)
 		if err != nil {
 			return nil, -1, err
@@ -634,7 +555,7 @@ func RetrieveProviderConsumerPact(ctx context.Context,
 	}
 	if len(pactIds) == 0 {
 		PactLogger.Errorf(nil, "pact retrieve failed, pact cannot be found.")
-		return &GetProviderConsumerVersionPactResponse{
+		return &brokerpb.GetProviderConsumerVersionPactResponse{
 			Response: pb.CreateResponse(scerr.ErrInternal, "pact cannot be found."),
 		}, -1, err
 	}
@@ -643,7 +564,7 @@ func RetrieveProviderConsumerPact(ctx context.Context,
 		strconv.Itoa(int(consumerParticipant.Id)),
 		strconv.Itoa(int(providerParticipant.Id))},
 		"/")
-	pacts, err := Store().PactVersion().Search(ctx,
+	pacts, err := Store().Pact().Search(ctx,
 		registry.WithStrKey(pactKey),
 		registry.WithPrefix())
 
@@ -655,27 +576,27 @@ func RetrieveProviderConsumerPact(ctx context.Context,
 		return nil, -1, nil
 	}
 	for i := 0; i < len(pacts.Kvs); i++ {
-		pactObj := &Pact{}
+		pactObj := &brokerpb.Pact{}
 		err = json.Unmarshal(pacts.Kvs[i].Value.([]byte), pactObj)
 		if err != nil {
 			return nil, -1, err
 		}
 		if _, ok := pactIds[pactObj.Id]; ok {
 			//PactLogger.Infof("pact retrieve succeeded, found pact: %s", string(pactObj.Content))
-			return &GetProviderConsumerVersionPactResponse{
+			return &brokerpb.GetProviderConsumerVersionPactResponse{
 				Response: pb.CreateResponse(pb.Response_SUCCESS, "pact found."),
 				Pact:     pactObj.Content,
 			}, pactObj.Id, nil
 		}
 	}
 	PactLogger.Errorf(nil, "pact retrieve failed, pact cannot be found.")
-	return &GetProviderConsumerVersionPactResponse{
+	return &brokerpb.GetProviderConsumerVersionPactResponse{
 		Response: pb.CreateResponse(scerr.ErrInternal, "pact cannot be found."),
 	}, -1, nil
 }
 
 func DeletePactData(ctx context.Context,
-	in *BaseBrokerRequest) (*pb.Response, error) {
+	in *brokerpb.BaseBrokerRequest) (*pb.Response, error) {
 	//tenant := util.ParseTenantProject(ctx)
 	allPactKey := GetBrokerRootKey() //GetBrokerVerificationKey("default") //util.StringJoin([]string{ apt.GetRootKey(), apt.REGISTRY_PACT_ROOT_KEY }, "/")
 
diff --git a/server/core/backend/cache.go b/server/core/backend/cache.go
index 2f9107e1..acf9b998 100644
--- a/server/core/backend/cache.go
+++ b/server/core/backend/cache.go
@@ -18,10 +18,11 @@ package backend
 
 type Cache interface {
 	Name() string
-	Size() int
+	Size() int // the bytes size of the cache
 
 	Get(k string) *KeyValue
-	GetAll(prefix string, arr *[]*KeyValue) int
+	GetAll(arr *[]*KeyValue) int
+	GetPrefix(prefix string, arr *[]*KeyValue) int
 	ForEach(iter func(k string, v *KeyValue) (next bool))
 
 	Put(k string, v *KeyValue)
@@ -30,7 +31,4 @@ type Cache interface {
 
 type Cacher interface {
 	Cache() Cache
-	Run()
-	Stop()
-	Ready() <-chan struct{}
 }
diff --git a/server/core/backend/cache_kv.go b/server/core/backend/cache_kv.go
index b001544d..8b5d865e 100644
--- a/server/core/backend/cache_kv.go
+++ b/server/core/backend/cache_kv.go
@@ -17,6 +17,7 @@
 package backend
 
 import (
+	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
 	"strings"
 	"sync"
 	"time"
@@ -36,7 +37,9 @@ func (c *KvCache) Name() string {
 }
 
 func (c *KvCache) Size() (l int) {
-	l = c.GetAll(c.Cfg.Prefix, nil)
+	c.rwMux.RLock()
+	l = int(util.Sizeof(c.store))
+	c.rwMux.RUnlock()
 	return
 }
 
@@ -50,7 +53,14 @@ func (c *KvCache) Get(key string) (v *KeyValue) {
 	return
 }
 
-func (c *KvCache) GetAll(prefix string, arr *[]*KeyValue) (count int) {
+func (c *KvCache) GetAll(arr *[]*KeyValue) (count int) {
+	c.rwMux.RLock()
+	count = c.getPrefixKey(arr, c.Cfg.Key)
+	c.rwMux.RUnlock()
+	return
+}
+
+func (c *KvCache) GetPrefix(prefix string, arr *[]*KeyValue) (count int) {
 	c.rwMux.RLock()
 	count = c.getPrefixKey(arr, prefix)
 	c.rwMux.RUnlock()
@@ -116,7 +126,7 @@ func (c *KvCache) getPrefixKey(arr *[]*KeyValue, prefix string) (count int) {
 }
 
 func (c *KvCache) addPrefixKey(key string, val *KeyValue) {
-	if len(c.Cfg.Prefix) >= len(key) {
+	if len(c.Cfg.Key) >= len(key) {
 		return
 	}
 	prefix := c.prefix(key)
@@ -155,12 +165,6 @@ func (c *KvCache) deletePrefixKey(key string) {
 	}
 }
 
-func (c *KvCache) ReportMetrics() {
-	c.rwMux.RLock()
-	ReportCacheMetrics(c.Name(), "raw", c.store)
-	c.rwMux.RUnlock()
-}
-
 func NewKvCache(name string, cfg *Config) *KvCache {
 	return &KvCache{
 		Cfg:         cfg,
diff --git a/server/core/backend/cache_null.go b/server/core/backend/cache_null.go
index cce930e2..8d538d6a 100644
--- a/server/core/backend/cache_null.go
+++ b/server/core/backend/cache_null.go
@@ -24,22 +24,17 @@ var (
 type nullCache struct {
 }
 
-func (n *nullCache) Name() string                                         { return "null" }
+func (n *nullCache) Name() string                                         { return "NULL" }
 func (n *nullCache) Size() int                                            { return 0 }
-func (n *nullCache) RLock()                                               {}
-func (n *nullCache) RUnlock()                                             {}
 func (n *nullCache) Get(k string) *KeyValue                               { return nil }
-func (n *nullCache) GetAll(prefix string, arr *[]*KeyValue) int           { return 0 }
+func (n *nullCache) GetAll(arr *[]*KeyValue) int                          { return 0 }
+func (n *nullCache) GetPrefix(prefix string, arr *[]*KeyValue) int        { return 0 }
 func (n *nullCache) ForEach(iter func(k string, v *KeyValue) (next bool)) {}
-func (n *nullCache) Lock()                                                {}
-func (n *nullCache) Unlock()                                              {}
 func (n *nullCache) Put(k string, v *KeyValue)                            {}
 func (n *nullCache) Remove(k string)                                      {}
 
 type nullCacher struct {
 }
 
-func (n *nullCacher) Cache() Cache           { return NullCache }
-func (n *nullCacher) Run()                   {}
-func (n *nullCacher) Stop()                  {}
-func (n *nullCacher) Ready() <-chan struct{} { return closedCh }
+func (n *nullCacher) Config() *Config { return nil }
+func (n *nullCacher) Cache() Cache    { return NullCache }
diff --git a/server/core/backend/cache_test.go b/server/core/backend/cache_test.go
index e15ba9c3..b9593221 100644
--- a/server/core/backend/cache_test.go
+++ b/server/core/backend/cache_test.go
@@ -35,7 +35,7 @@ func BenchmarkFilter(b *testing.B) {
 	}
 	v, _ := json.Marshal(inst)
 
-	cfg := DefaultConfig().WithParser(InstanceParser)
+	cfg := Configure().WithParser(InstanceParser)
 
 	n := 300 * 1000 // 30w
 	cache := NewKvCache("test", cfg)
@@ -83,8 +83,36 @@ func BenchmarkFilter(b *testing.B) {
 	//20	  82367261 ns/op	37964987 B/op	   80132 allocs/op
 }
 
+func TestNullCache_Name(t *testing.T) {
+	NullCache.Put("", nil)
+	if NullCache.Name() != "NULL" {
+		t.Fatalf("TestNullCache_Name failed")
+	}
+	if NullCache.Size() != 0 {
+		t.Fatalf("TestNullCache_Name failed")
+	}
+	if NullCache.Get("") != nil {
+		t.Fatalf("TestNullCache_Name failed")
+	}
+	if NullCache.GetAll(nil) != 0 {
+		t.Fatalf("TestNullCache_Name failed")
+	}
+	if NullCache.GetPrefix("", nil) != 0 {
+		t.Fatalf("TestNullCache_Name failed")
+	}
+	NullCache.ForEach(func(k string, v *KeyValue) (next bool) {
+		t.Fatalf("TestNullCache_Name failed")
+		return false
+	})
+	NullCache.Remove("")
+
+	if NullCacher.Cache() != NullCache || NullCacher.Config() != nil {
+		t.Fatalf("TestNullCache_Name failed")
+	}
+}
+
 func TestKvCache_Get(t *testing.T) {
-	c := NewKvCache("test", DefaultConfig())
+	c := NewKvCache("test", Configure())
 	c.Put("", &KeyValue{Version: 1})
 	c.Put("/", &KeyValue{Version: 1})
 	c.Put("/a/b/c/d/e/1", &KeyValue{Version: 1})
@@ -92,21 +120,25 @@ func TestKvCache_Get(t *testing.T) {
 	c.Put("/a/b/d/d/f/3", &KeyValue{Version: 3})
 	c.Put("/a/b/e/d/g/4", &KeyValue{Version: 4})
 
-	if l := c.Size(); l != 4 {
-		t.Fatalf("TestKvCache Size() failed, %d", l)
+	if s := c.Size(); s == 0 {
+		t.Fatalf("TestKvCache Size() failed, %d", s)
+	}
+
+	if l := c.GetAll(nil); l != 4 {
+		t.Fatalf("TestKvCache GetAll() failed, %d", l)
 	}
 
 	if kv := c.Get("/a/b/c/d/e/2"); kv == nil || kv.Version != 2 {
 		t.Fatalf("TestKvCache Get() failed, %v", kv)
 	}
 
-	if l := c.GetAll("/", nil); l != 4 {
-		t.Fatalf("TestKvCache GetAll() failed, %d", l)
+	if l := c.GetPrefix("/", nil); l != 4 {
+		t.Fatalf("TestKvCache GetPrefix() failed, %d", l)
 	}
 
 	var arr []*KeyValue
-	if l := c.GetAll("/a/b/c/", &arr); l != 2 || (arr[0].Version != 1 && arr[1].Version != 1) {
-		t.Fatalf("TestKvCache GetAll() failed, %d, %v", l, arr)
+	if l := c.GetPrefix("/a/b/c/", &arr); l != 2 || (arr[0].Version != 1 && arr[1].Version != 1) {
+		t.Fatalf("TestKvCache GetPrefix() failed, %d, %v", l, arr)
 	}
 
 	l, b := -1, false
@@ -134,7 +166,12 @@ func TestKvCache_Get(t *testing.T) {
 	c.Remove("/")
 	c.Remove("/a/b/c/d/e/2")
 	c.Remove("/a/b/d/d/f/3")
-	if l := c.Size(); l != 2 {
-		t.Fatalf("TestKvCache Size() failed, %d", l)
+	if l := c.GetAll(nil); l != 2 {
+		t.Fatalf("TestKvCache GetAll() failed, %d", l)
+	}
+
+	c.Put("/a/b/c/d/e/1", &KeyValue{Version: 2})
+	if kv := c.Get("/a/b/c/d/e/1"); kv == nil || kv.Version != 2 {
+		t.Fatalf("TestKvCache Put() failed, %v", kv)
 	}
 }
diff --git a/server/core/backend/cacher_kv.go b/server/core/backend/cacher_kv.go
index 3d8a7939..9be17aa8 100644
--- a/server/core/backend/cacher_kv.go
+++ b/server/core/backend/cacher_kv.go
@@ -18,6 +18,7 @@ package backend
 
 import (
 	"errors"
+	"fmt"
 	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
 	"github.com/apache/incubator-servicecomb-service-center/server/core"
 	"github.com/apache/incubator-servicecomb-service-center/server/core/proto"
@@ -31,37 +32,39 @@ import (
 type KvCacher struct {
 	Cfg *Config
 
-	lastRev      int64
-	noEventCount int
+	latestListRev  int64
+	noEventPeriods int
 
 	ready     chan struct{}
-	lw        ListWatcher
+	lw        ListWatch
 	mux       sync.Mutex
 	once      sync.Once
-	cache     *KvCache
+	cache     Cache
 	goroutine *util.GoRoutine
 }
 
+func (c *KvCacher) Config() *Config {
+	return c.Cfg
+}
+
 func (c *KvCacher) needList() bool {
 	rev := c.lw.Revision()
-	defer func() { c.lastRev = rev }()
-
 	if rev == 0 {
-		c.noEventCount = 0
+		c.noEventPeriods = 0
 		return true
 	}
-	if c.lastRev != rev {
-		c.noEventCount = 0
+	if c.latestListRev != rev {
+		c.noEventPeriods = 0
 		return false
 	}
-	c.noEventCount++
-	if c.noEventCount < c.Cfg.NoEventMaxInterval {
+	c.noEventPeriods++
+	if c.Cfg.NoEventPeriods == 0 || c.noEventPeriods < c.Cfg.NoEventPeriods {
 		return false
 	}
 
 	util.Logger().Debugf("no events come in more then %s, need to list key %s, rev: %d",
-		time.Duration(c.noEventCount)*c.Cfg.Timeout, c.Cfg.Prefix, rev)
-	c.noEventCount = 0
+		time.Duration(c.noEventPeriods)*c.Cfg.Timeout, c.Cfg.Key, rev)
+	c.noEventPeriods = 0
 	return true
 }
 
@@ -70,24 +73,28 @@ func (c *KvCacher) doList(cfg ListWatchConfig) error {
 	if err != nil {
 		return err
 	}
+	c.latestListRev = c.lw.Revision()
 
 	kvs := resp.Kvs
 	start := time.Now()
 	evts := c.filter(c.lw.Revision(), kvs)
 	if ec, kc := len(evts), len(kvs); c.Cfg.DeferHandler != nil && ec == 0 && kc != 0 &&
 		c.Cfg.DeferHandler.Reset() {
-		util.Logger().Warnf(nil, "most of the protected data(%d/%d) are recovered", kc, c.cache.Size())
+		util.Logger().Warnf(nil, "most of the protected data(%d/%d) are recovered",
+			kc, c.cache.GetAll(nil))
 	}
 	c.sync(evts)
 	util.LogDebugOrWarnf(start, "finish to cache key %s, %d items, rev: %d",
-		c.Cfg.Prefix, len(kvs), c.lw.Revision())
+		c.Cfg.Key, len(kvs), c.lw.Revision())
 
 	return nil
 }
 
 func (c *KvCacher) doWatch(cfg ListWatchConfig) error {
-	watcher := c.lw.Watch(cfg)
-	return c.handleWatcher(watcher)
+	if watcher := c.lw.Watch(cfg); watcher != nil {
+		return c.handleWatcher(watcher)
+	}
+	return fmt.Errorf("handle a nil watcher")
 }
 
 func (c *KvCacher) ListAndWatch(ctx context.Context) error {
@@ -99,7 +106,10 @@ func (c *KvCacher) ListAndWatch(ctx context.Context) error {
 		Context: ctx,
 	}
 	if c.needList() {
-		c.doList(cfg)
+		if err := c.doList(cfg); err != nil && !c.IsReady() {
+			// cacher is not ready, so it need to retry util the cache is created
+			return err
+		}
 	}
 
 	util.SafeCloseChan(c.ready)
@@ -107,7 +117,7 @@ func (c *KvCacher) ListAndWatch(ctx context.Context) error {
 	return c.doWatch(cfg)
 }
 
-func (c *KvCacher) handleWatcher(watcher *Watcher) error {
+func (c *KvCacher) handleWatcher(watcher Watcher) error {
 	defer watcher.Stop()
 	for resp := range watcher.EventBus() {
 		if resp == nil {
@@ -115,9 +125,10 @@ func (c *KvCacher) handleWatcher(watcher *Watcher) error {
 		}
 
 		start := time.Now()
+		rev := resp.Revision
 		evts := make([]KvEvent, 0, len(resp.Kvs))
 		for _, kv := range resp.Kvs {
-			evt := KvEvent{Prefix: c.lw.Prefix, Revision: kv.ModRevision}
+			evt := KvEvent{Prefix: c.Cfg.Key, Revision: kv.ModRevision}
 			switch {
 			case resp.Action == registry.Put && kv.Version == 1:
 				evt.Type, evt.KV = proto.EVT_CREATE, c.doParse(kv)
@@ -142,7 +153,7 @@ func (c *KvCacher) handleWatcher(watcher *Watcher) error {
 		}
 		c.sync(evts)
 		util.LogDebugOrWarnf(start, "finish to handle %d events, prefix: %s, rev: %d",
-			len(evts), c.Cfg.Prefix, c.lw.Revision())
+			len(evts), c.Cfg.Key, rev)
 	}
 	return nil
 }
@@ -240,7 +251,7 @@ func (c *KvCacher) filterDelete(newStore map[string]*mvccpb.KeyValue,
 		block[i] = KvEvent{
 			Revision: rev,
 			Type:     proto.EVT_DELETE,
-			Prefix:   c.Cfg.Prefix,
+			Prefix:   c.Cfg.Key,
 			KV:       v,
 		}
 		i++
@@ -272,7 +283,7 @@ func (c *KvCacher) filterCreateOrUpdate(newStore map[string]*mvccpb.KeyValue,
 				block[i] = KvEvent{
 					Revision: rev,
 					Type:     proto.EVT_CREATE,
-					Prefix:   c.Cfg.Prefix,
+					Prefix:   c.Cfg.Key,
 					KV:       kv,
 				}
 				i++
@@ -294,7 +305,7 @@ func (c *KvCacher) filterCreateOrUpdate(newStore map[string]*mvccpb.KeyValue,
 			block[i] = KvEvent{
 				Revision: rev,
 				Type:     proto.EVT_UPDATE,
-				Prefix:   c.Cfg.Prefix,
+				Prefix:   c.Cfg.Key,
 				KV:       kv,
 			}
 			i++
@@ -447,8 +458,7 @@ func (c *KvCacher) IsReady() bool {
 }
 
 func (c *KvCacher) reportMetrics(ctx context.Context) {
-	if !core.ServerInfo.Config.EnablePProf ||
-		!core.ServerInfo.Config.EnableCache {
+	if !core.ServerInfo.Config.EnablePProf {
 		return
 	}
 	timer := time.NewTimer(DEFAULT_METRICS_INTERVAL)
@@ -458,8 +468,7 @@ func (c *KvCacher) reportMetrics(ctx context.Context) {
 		case <-ctx.Done():
 			return
 		case <-timer.C:
-			c.cache.ReportMetrics()
-
+			ReportCacheSize(c.cache.Name(), "raw", c.cache.Size())
 			timer.Reset(DEFAULT_METRICS_INTERVAL)
 		}
 	}
@@ -469,9 +478,9 @@ func NewKvCacher(name string, cfg *Config) *KvCacher {
 	cacher := &KvCacher{
 		Cfg:   cfg,
 		ready: make(chan struct{}),
-		lw: ListWatcher{
+		lw: &innerListWatch{
 			Client: Registry(),
-			Prefix: cfg.Prefix,
+			Prefix: cfg.Key,
 		},
 		goroutine: util.NewGo(context.Background()),
 	}
diff --git a/server/core/backend/cacher_kv_test.go b/server/core/backend/cacher_kv_test.go
new file mode 100644
index 00000000..82455ec6
--- /dev/null
+++ b/server/core/backend/cacher_kv_test.go
@@ -0,0 +1,340 @@
+/*
+ * 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 backend
+
+import (
+	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
+	"github.com/apache/incubator-servicecomb-service-center/server/core/proto"
+	"github.com/apache/incubator-servicecomb-service-center/server/infra/registry"
+	"github.com/coreos/etcd/mvcc/mvccpb"
+	"golang.org/x/net/context"
+	"strconv"
+	"testing"
+)
+
+type mockCache struct {
+	*nullCache
+	Key string
+	KV  *KeyValue
+}
+
+func (n *mockCache) Get(k string) *KeyValue {
+	if k == n.Key {
+		return n.KV
+	}
+	return nil
+}
+func (n *mockCache) GetPrefix(prefix string, arr *[]*KeyValue) int {
+	if prefix == n.Key {
+		if arr != nil {
+			*arr = append(*arr, n.KV)
+		}
+		return 1
+	}
+	return 0
+}
+func (n *mockCache) ForEach(iter func(k string, v *KeyValue) (next bool)) {
+	iter(n.Key, n.KV)
+}
+func (n *mockCache) Put(k string, v *KeyValue) {
+	n.Key = k
+	n.KV = v
+}
+func (n *mockCache) Remove(k string) {
+	if k == n.Key {
+		n.Key = ""
+		n.KV = nil
+	}
+}
+
+func TestNewKvCacher(t *testing.T) {
+	w := &mockWatcher{}
+	lw := &mockListWatch{
+		Bus: make(chan *registry.PluginResponse, 100),
+	}
+	w.lw = lw
+
+	cr := &KvCacher{
+		Cfg:       Configure(),
+		ready:     make(chan struct{}),
+		lw:        lw,
+		goroutine: util.NewGo(context.Background()),
+		cache:     &mockCache{},
+	}
+
+	ctx, cancel := context.WithCancel(context.Background())
+	cancel()
+
+	// case: cause list internal error
+	cr.refresh(ctx)
+	if cr.IsReady() {
+		t.Fatalf("TestNewKvCacher failed")
+	}
+
+	// prepare data
+	var evt KvEvent
+	cr = &KvCacher{
+		Cfg: Configure().
+			WithNoEventPeriods(0).
+			WithEventFunc(func(e KvEvent) {
+				evt = e
+			}),
+		ready:     make(chan struct{}),
+		lw:        lw,
+		goroutine: util.NewGo(context.Background()),
+		cache:     &mockCache{},
+	}
+
+	lw.Watcher = w
+	data := &mvccpb.KeyValue{Key: []byte("ka"), Value: []byte("va"), Version: 1, ModRevision: 2}
+	test := &registry.PluginResponse{
+		Action:   registry.Put,
+		Revision: 3,
+		Kvs:      []*mvccpb.KeyValue{data}}
+
+	// case: list 1 resp and watch 0 event
+	cr.cache.Remove("ka")
+	lw.ListResponse = test
+	lw.Bus <- nil
+
+	cr.refresh(ctx)
+	// check ready
+	if !cr.IsReady() {
+		t.Fatalf("TestNewKvCacher failed")
+	}
+	// check event
+	if evt.Type != proto.EVT_INIT || evt.Revision != 3 || evt.KV.ModRevision != 2 || string(evt.KV.Key) != "ka" || string(evt.KV.Value.([]byte)) != "va" {
+		t.Fatalf("TestNewKvCacher failed, %v", evt)
+	}
+	// check cache
+	kv := cr.cache.Get("ka")
+	if kv == nil || kv.ModRevision != 2 || string(kv.Key) != "ka" || string(kv.Value.([]byte)) != "va" {
+		t.Fatalf("TestNewKvCacher failed")
+	}
+
+	test.Revision = 4
+	data.ModRevision = 3
+
+	// case: re-list and should be no event
+	lw.Bus <- nil
+	evt.KV = nil
+	cr.refresh(ctx)
+	if evt.KV != nil {
+		t.Fatalf("TestNewKvCacher failed")
+	}
+	// check cache
+	kv = cr.cache.Get("ka")
+	if kv == nil || kv.ModRevision != 2 || string(kv.Key) != "ka" || string(kv.Value.([]byte)) != "va" {
+		t.Fatalf("TestNewKvCacher failed")
+	}
+
+	// case re-list and over no event times
+	lw.Bus <- nil
+	evt.KV = nil
+	old := *cr.Cfg
+	cr.Cfg.WithNoEventPeriods(1)
+	cr.refresh(ctx)
+	// check event
+	if evt.Type != proto.EVT_UPDATE || evt.Revision != 4 || evt.KV.ModRevision != 3 || string(evt.KV.Key) != "ka" || string(evt.KV.Value.([]byte)) != "va" {
+		t.Fatalf("TestNewKvCacher failed, %v", evt)
+	}
+	// check cache
+	kv = cr.cache.Get("ka")
+	if kv == nil || kv.ModRevision != 3 || string(kv.Key) != "ka" || string(kv.Value.([]byte)) != "va" {
+		t.Fatalf("TestNewKvCacher failed")
+	}
+
+	lw.ListResponse = &registry.PluginResponse{Revision: 5}
+	lw.Bus <- nil
+	evt.KV = nil
+	cr.refresh(ctx)
+	*cr.Cfg = old
+	// check event
+	if evt.Type != proto.EVT_DELETE || evt.Revision != 5 || evt.KV.ModRevision != 3 || string(evt.KV.Key) != "ka" || string(evt.KV.Value.([]byte)) != "va" {
+		t.Fatalf("TestNewKvCacher failed, %v", evt)
+	}
+	// check cache
+	kv = cr.cache.Get("ka")
+	if kv != nil {
+		t.Fatalf("TestNewKvCacher failed")
+	}
+
+	// case: no list and watch 1 event
+	test.Revision = 6
+	data.ModRevision = 5
+	lw.Bus <- test
+	lw.Bus <- nil
+
+	cr.refresh(ctx)
+	// check event
+	if evt.Type != proto.EVT_CREATE || evt.Revision != 5 || evt.KV.ModRevision != 5 || string(evt.KV.Key) != "ka" || string(evt.KV.Value.([]byte)) != "va" {
+		t.Fatalf("TestNewKvCacher failed, %v", evt)
+	}
+	// check cache
+	kv = cr.cache.Get("ka")
+	if kv == nil || kv.ModRevision != 5 || string(kv.Key) != "ka" || string(kv.Value.([]byte)) != "va" {
+		t.Fatalf("TestNewKvCacher failed")
+	}
+
+	test.Revision = 7
+	data.Version = 2
+	data.ModRevision = 6
+	lw.Bus <- test
+	lw.Bus <- nil
+
+	cr.refresh(ctx)
+	// check event
+	if evt.Type != proto.EVT_UPDATE || evt.Revision != 6 || evt.KV.ModRevision != 6 || string(evt.KV.Key) != "ka" || string(evt.KV.Value.([]byte)) != "va" {
+		t.Fatalf("TestNewKvCacher failed, %v", evt)
+	}
+	// check cache
+	kv = cr.cache.Get("ka")
+	if kv == nil || kv.ModRevision != 6 || string(kv.Key) != "ka" || string(kv.Value.([]byte)) != "va" {
+		t.Fatalf("TestNewKvCacher failed")
+	}
+
+	test.Revision = 8
+	test.Action = registry.Delete
+	data.Version = 0
+	data.ModRevision = 6
+	lw.Bus <- test
+	lw.Bus <- nil
+
+	cr.refresh(ctx)
+	// check event
+	if evt.Type != proto.EVT_DELETE || evt.Revision != 6 || evt.KV.ModRevision != 6 || string(evt.KV.Key) != "ka" || string(evt.KV.Value.([]byte)) != "va" {
+		t.Fatalf("TestNewKvCacher failed, %v", evt)
+	}
+	// check cache
+	kv = cr.cache.Get("ka")
+	if kv != nil {
+		t.Fatalf("TestNewKvCacher failed")
+	}
+
+	// case: list 1 item and watch no event
+	lw.Rev = 0
+	test.Revision = 9
+	data.Version = 1
+	data.ModRevision = 1
+	lw.ListResponse = test
+	lw.Bus <- nil
+	evt.KV = nil
+	cr.refresh(ctx)
+	// check event
+	if evt.Type != proto.EVT_CREATE || evt.Revision != 9 || evt.KV.ModRevision != 1 || string(evt.KV.Key) != "ka" || string(evt.KV.Value.([]byte)) != "va" {
+		t.Fatalf("TestNewKvCacher failed, %v", evt)
+	}
+	// check cache
+	kv = cr.cache.Get("ka")
+	if kv == nil || kv.ModRevision != 1 || string(kv.Key) != "ka" || string(kv.Value.([]byte)) != "va" {
+		t.Fatalf("TestNewKvCacher failed")
+	}
+
+	// case: caught delete event but value is nil
+	test.Revision = 10
+	test.Action = registry.Delete
+	data.Version = 0
+	data.ModRevision = 1
+	data.Value = nil
+	lw.Bus <- test
+	lw.Bus <- nil
+
+	cr.refresh(ctx)
+	data.Value = []byte("va")
+	// check event
+	if evt.Type != proto.EVT_DELETE || evt.Revision != 1 || evt.KV.ModRevision != 1 || string(evt.KV.Key) != "ka" || string(evt.KV.Value.([]byte)) != "va" {
+		t.Fatalf("TestNewKvCacher failed, %v", evt)
+	}
+	// check cache
+	kv = cr.cache.Get("ka")
+	if kv != nil {
+		t.Fatalf("TestNewKvCacher failed")
+	}
+
+	// case: parse failed
+	lw.Rev = 0
+	test.Revision = 1
+	data.Version = 1
+	data.ModRevision = 1
+	lw.ListResponse = test
+	lw.Bus <- nil
+	evt.KV = nil
+	old = *cr.Cfg
+	cr.Cfg.WithParser(MapParser)
+	cr.refresh(ctx)
+	// check event
+	if evt.KV != nil {
+		t.Fatalf("TestNewKvCacher failed, %v", evt)
+	}
+	// check cache
+	kv = cr.cache.Get("ka")
+	if kv != nil {
+		t.Fatalf("TestNewKvCacher failed")
+	}
+
+	lw.ListResponse = test
+	lw.Bus <- test
+	lw.Bus <- nil
+	cr.refresh(ctx)
+	*cr.Cfg = old
+	// check event
+	if evt.KV != nil {
+		t.Fatalf("TestNewKvCacher failed, %v", evt)
+	}
+	// check cache
+	kv = cr.cache.Get("ka")
+	if kv != nil {
+		t.Fatalf("TestNewKvCacher failed")
+	}
+
+	// case: list result is too large
+	var evts = make(map[string]KvEvent)
+	lw.Rev = 0
+	test.Revision = 3
+	test.Kvs = nil
+	for i := 0; i < eventBlockSize+1; i++ {
+		kv := *data
+		kv.Key = []byte(strconv.Itoa(i))
+		kv.Value = []byte(strconv.Itoa(i))
+		kv.Version = int64(i)
+		kv.ModRevision = int64(i)
+		test.Kvs = append(test.Kvs, &kv)
+	}
+	data.ModRevision = 2
+	test.Kvs = append(test.Kvs, data)
+	lw.ListResponse = test
+	lw.Bus <- nil
+	evt.KV = nil
+	old = *cr.Cfg
+	cr.Cfg.WithEventFunc(func(evt KvEvent) {
+		evts[string(evt.KV.Key)] = evt
+	})
+	cr.refresh(ctx)
+	// check all events
+	for i := 0; i < eventBlockSize+1; i++ {
+		s := strconv.Itoa(i)
+		if evt, ok := evts[s]; !ok || evt.Type != proto.EVT_CREATE || evt.KV.ModRevision != int64(i) || string(evt.KV.Value.([]byte)) != s {
+			t.Fatalf("TestNewKvCacher failed, %v", evt)
+		}
+		delete(evts, s)
+	}
+	evt = evts[string(data.Key)]
+	if len(evts) != 1 || evt.Type != proto.EVT_CREATE || evt.Revision != 3 || evt.KV.ModRevision != 2 {
+		t.Fatalf("TestNewKvCacher failed, %v %v", evts, evt)
+	}
+	delete(evts, string(data.Key))
+}
diff --git a/server/core/backend/common.go b/server/core/backend/common.go
index 1ae79d30..8c0eeeeb 100644
--- a/server/core/backend/common.go
+++ b/server/core/backend/common.go
@@ -17,6 +17,7 @@
 package backend
 
 import (
+	"errors"
 	apt "github.com/apache/incubator-servicecomb-service-center/server/core"
 	"strconv"
 	"time"
@@ -46,6 +47,11 @@ const TIME_FORMAT = "15:04:05.000"
 
 const EVENT_BUS_MAX_SIZE = 1000
 
+// errors
+var (
+	ErrNoImpl = errors.New("no implement")
+)
+
 var closedCh = make(chan struct{})
 
 func init() {
@@ -56,7 +62,7 @@ type StoreType int
 
 func (st StoreType) String() string {
 	if int(st) < 0 {
-		return "NONEXIST"
+		return "TypeError"
 	}
 	if int(st) < len(TypeNames) {
 		return TypeNames[st]
@@ -64,7 +70,7 @@ func (st StoreType) String() string {
 	return "TYPE" + strconv.Itoa(int(st))
 }
 
-const NONEXIST = StoreType(-1)
+const TypeError = StoreType(-1)
 
 const (
 	DOMAIN StoreType = iota
@@ -105,49 +111,49 @@ var TypeNames = []string{
 }
 
 var TypeConfig = map[StoreType]*Config{
-	SERVICE: DefaultConfig().WithPrefix(apt.GetServiceRootKey("")).
+	SERVICE: Configure().WithPrefix(apt.GetServiceRootKey("")).
 		WithInitSize(500).WithParser(ServiceParser),
 
-	INSTANCE: DefaultConfig().WithPrefix(apt.GetInstanceRootKey("")).
+	INSTANCE: Configure().WithPrefix(apt.GetInstanceRootKey("")).
 		WithInitSize(1000).WithParser(InstanceParser).
 		WithDeferHandler(NewInstanceEventDeferHandler()),
 
-	DOMAIN: DefaultConfig().WithPrefix(apt.GetDomainRootKey() + "/").
+	DOMAIN: Configure().WithPrefix(apt.GetDomainRootKey() + "/").
 		WithInitSize(100).WithParser(StringParser),
 
-	SCHEMA: DefaultConfig().WithPrefix(apt.GetServiceSchemaRootKey("")).
+	SCHEMA: Configure().WithPrefix(apt.GetServiceSchemaRootKey("")).
 		WithInitSize(0),
 
-	SCHEMA_SUMMARY: DefaultConfig().WithPrefix(apt.GetServiceSchemaSummaryRootKey("")).
+	SCHEMA_SUMMARY: Configure().WithPrefix(apt.GetServiceSchemaSummaryRootKey("")).
 		WithInitSize(100).WithParser(StringParser),
 
-	RULE: DefaultConfig().WithPrefix(apt.GetServiceRuleRootKey("")).
+	RULE: Configure().WithPrefix(apt.GetServiceRuleRootKey("")).
 		WithInitSize(100).WithParser(RuleParser),
 
-	LEASE: DefaultConfig().WithPrefix(apt.GetInstanceLeaseRootKey("")).
+	LEASE: Configure().WithPrefix(apt.GetInstanceLeaseRootKey("")).
 		WithInitSize(1000).WithParser(StringParser),
 
-	SERVICE_INDEX: DefaultConfig().WithPrefix(apt.GetServiceIndexRootKey("")).
+	SERVICE_INDEX: Configure().WithPrefix(apt.GetServiceIndexRootKey("")).
 		WithInitSize(500).WithParser(StringParser),
 
-	SERVICE_ALIAS: DefaultConfig().WithPrefix(apt.GetServiceAliasRootKey("")).
+	SERVICE_ALIAS: Configure().WithPrefix(apt.GetServiceAliasRootKey("")).
 		WithInitSize(100).WithParser(StringParser),
 
-	SERVICE_TAG: DefaultConfig().WithPrefix(apt.GetServiceTagRootKey("")).
+	SERVICE_TAG: Configure().WithPrefix(apt.GetServiceTagRootKey("")).
 		WithInitSize(100).WithParser(MapParser),
 
-	RULE_INDEX: DefaultConfig().WithPrefix(apt.GetServiceRuleIndexRootKey("")).
+	RULE_INDEX: Configure().WithPrefix(apt.GetServiceRuleIndexRootKey("")).
 		WithInitSize(100).WithParser(StringParser),
 
-	DEPENDENCY: DefaultConfig().WithPrefix(apt.GetServiceDependencyRootKey("")).
+	DEPENDENCY: Configure().WithPrefix(apt.GetServiceDependencyRootKey("")).
 		WithInitSize(100),
 
-	DEPENDENCY_RULE: DefaultConfig().WithPrefix(apt.GetServiceDependencyRuleRootKey("")).
+	DEPENDENCY_RULE: Configure().WithPrefix(apt.GetServiceDependencyRuleRootKey("")).
 		WithInitSize(100).WithParser(DependencyRuleParser),
 
-	DEPENDENCY_QUEUE: DefaultConfig().WithPrefix(apt.GetServiceDependencyQueueRootKey("")).
+	DEPENDENCY_QUEUE: Configure().WithPrefix(apt.GetServiceDependencyQueueRootKey("")).
 		WithInitSize(100).WithParser(DependencyQueueParser),
 
-	PROJECT: DefaultConfig().WithPrefix(apt.GetProjectRootKey("")).
+	PROJECT: Configure().WithPrefix(apt.GetProjectRootKey("")).
 		WithInitSize(100).WithParser(StringParser),
 }
diff --git a/server/core/backend/config.go b/server/core/backend/config.go
index 32c2e416..7f1870fc 100644
--- a/server/core/backend/config.go
+++ b/server/core/backend/config.go
@@ -23,23 +23,23 @@ import (
 )
 
 type Config struct {
-	Prefix             string
-	InitSize           int
-	NoEventMaxInterval int
-	Timeout            time.Duration
-	Period             time.Duration
-	DeferHandler       DeferHandler
-	OnEvent            KvEventFunc
-	Parser             *Parser
+	Key            string
+	InitSize       int
+	NoEventPeriods int
+	Timeout        time.Duration
+	Period         time.Duration
+	DeferHandler   DeferHandler
+	OnEvent        KvEventFunc
+	Parser         Parser
 }
 
 func (cfg *Config) String() string {
-	return fmt.Sprintf("{prefix: %s, timeout: %s, period: %s}",
-		cfg.Prefix, cfg.Timeout, cfg.Period)
+	return fmt.Sprintf("{key: %s, timeout: %s, period: %s}",
+		cfg.Key, cfg.Timeout, cfg.Period)
 }
 
 func (cfg *Config) WithPrefix(key string) *Config {
-	cfg.Prefix = key
+	cfg.Key = key
 	return cfg
 }
 
@@ -68,6 +68,11 @@ func (cfg *Config) WithEventFunc(f KvEventFunc) *Config {
 	return cfg
 }
 
+func (cfg *Config) WithNoEventPeriods(p int) *Config {
+	cfg.NoEventPeriods = p
+	return cfg
+}
+
 func (cfg *Config) AppendEventFunc(f KvEventFunc) *Config {
 	if prev := cfg.OnEvent; prev != nil {
 		next := f
@@ -80,19 +85,19 @@ func (cfg *Config) AppendEventFunc(f KvEventFunc) *Config {
 	return cfg
 }
 
-func (cfg *Config) WithParser(parser *Parser) *Config {
+func (cfg *Config) WithParser(parser Parser) *Config {
 	cfg.Parser = parser
 	return cfg
 }
 
-func DefaultConfig() *Config {
+func Configure() *Config {
 	return &Config{
-		Prefix:             "/",
-		Timeout:            DEFAULT_LISTWATCH_TIMEOUT,
-		Period:             time.Second,
-		NoEventMaxInterval: DEFAULT_MAX_NO_EVENT_INTERVAL,
-		InitSize:           DEFAULT_CACHE_INIT_SIZE,
-		Parser:             BytesParser,
+		Key:            "/",
+		Timeout:        DEFAULT_LISTWATCH_TIMEOUT,
+		Period:         time.Second,
+		NoEventPeriods: DEFAULT_MAX_NO_EVENT_INTERVAL,
+		InitSize:       DEFAULT_CACHE_INIT_SIZE,
+		Parser:         BytesParser,
 	}
 }
 
diff --git a/server/core/backend/defer_instance.go b/server/core/backend/defer_instance.go
index fea9485b..b89d511c 100644
--- a/server/core/backend/defer_instance.go
+++ b/server/core/backend/defer_instance.go
@@ -104,17 +104,25 @@ func (iedh *InstanceEventDeferHandler) check(ctx context.Context) {
 			}
 
 			del := len(iedh.items)
-			if del > 0 && !n {
-				util.ResetTimer(t, DEFAULT_CHECK_WINDOW)
-				n = true
+			if del == 0 {
+				continue
+			}
+
+			if iedh.enabled {
+				continue
 			}
 
-			total := iedh.cache.Size()
-			if !iedh.enabled && del > 0 && total > 5 && float64(del) >= float64(total)*iedh.Percent {
+			total := iedh.cache.GetAll(nil)
+			if total > 5 && float64(del) >= float64(total)*iedh.Percent {
 				iedh.enabled = true
 				util.Logger().Warnf(nil, "self preservation is enabled, caught %d/%d(>=%.0f%%) DELETE events",
 					del, total, iedh.Percent*100)
 			}
+
+			if !n {
+				util.ResetTimer(t, DEFAULT_CHECK_WINDOW)
+				n = true
+			}
 		case <-t.C:
 			n = false
 
diff --git a/server/core/backend/defer_test.go b/server/core/backend/defer_test.go
index 42a43e8b..b8b3ebf4 100644
--- a/server/core/backend/defer_test.go
+++ b/server/core/backend/defer_test.go
@@ -42,7 +42,7 @@ func TestInstanceEventDeferHandler_OnCondition(t *testing.T) {
 func TestInstanceEventDeferHandler_HandleChan(t *testing.T) {
 	b := &pb.MicroServiceInstance{
 		HealthCheck: &pb.HealthCheck{
-			Interval: 4,
+			Interval: 3,
 			Times:    0,
 		},
 	}
@@ -71,7 +71,7 @@ func TestInstanceEventDeferHandler_HandleChan(t *testing.T) {
 		Value: b,
 	}
 
-	cache := NewKvCache("test", DefaultConfig())
+	cache := NewKvCache("test", Configure())
 	cache.Put("/1", kv1)
 	cache.Put("/2", kv2)
 	cache.Put("/3", kv3)
@@ -140,7 +140,7 @@ func TestInstanceEventDeferHandler_HandleChan(t *testing.T) {
 
 	getEvents(t, iedh)
 
-	iedh.Percent = 0.8
+	iedh.Percent = 0.9
 	iedh.OnCondition(cache, evts1)
 	iedh.OnCondition(cache, evts2)
 	iedh.OnCondition(cache, evts3)
@@ -150,12 +150,12 @@ func TestInstanceEventDeferHandler_HandleChan(t *testing.T) {
 
 func getEvents(t *testing.T, iedh *InstanceEventDeferHandler) {
 	fmt.Println(time.Now())
-	c := time.After(3 * time.Second)
+	c := time.After(3500 * time.Millisecond)
 	var evt3 *KvEvent
 	for {
 		select {
 		case evt := <-iedh.HandleChan():
-			fmt.Println(time.Now(), evt)
+			fmt.Println(time.Now(), evt.Type, string(evt.KV.Key))
 			if string(evt.KV.Key) == "/3" {
 				evt3 = &evt
 				if iedh.Percent == 0.01 && evt.Type == pb.EVT_DELETE {
@@ -164,8 +164,8 @@ func getEvents(t *testing.T, iedh *InstanceEventDeferHandler) {
 			}
 			continue
 		case <-c:
-			if iedh.Percent == 0.8 && evt3 == nil {
-				t.Fatalf(`TestInstanceEventDeferHandler_HandleChan with 80%% failed`)
+			if iedh.Percent == 0.9 && evt3 == nil {
+				t.Fatalf(`TestInstanceEventDeferHandler_HandleChan with 90%% failed`)
 			}
 		}
 		break
diff --git a/server/core/backend/entity.go b/server/core/backend/entity.go
new file mode 100644
index 00000000..0d152e56
--- /dev/null
+++ b/server/core/backend/entity.go
@@ -0,0 +1,23 @@
+/*
+ * 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 backend
+
+type Entity interface {
+	Runnable
+	Indexer
+	Cacher
+}
diff --git a/server/core/backend/entity_kv.go b/server/core/backend/entity_kv.go
new file mode 100644
index 00000000..4496137f
--- /dev/null
+++ b/server/core/backend/entity_kv.go
@@ -0,0 +1,76 @@
+/*
+ * 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 backend
+
+import (
+	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
+	"github.com/apache/incubator-servicecomb-service-center/server/core"
+	"sync"
+)
+
+var (
+	defaultKvEntity *KvEntity
+	newKvEntityOnce sync.Once
+)
+
+type KvEntity struct {
+	Cacher
+	Indexer
+}
+
+func (se *KvEntity) Run() {
+	if r, ok := se.Cacher.(Runnable); ok {
+		r.Run()
+	}
+}
+
+func (se *KvEntity) Stop() {
+	if r, ok := se.Cacher.(Runnable); ok {
+		r.Stop()
+	}
+}
+
+func (se *KvEntity) Ready() <-chan struct{} {
+	if r, ok := se.Cacher.(Runnable); ok {
+		return r.Ready()
+	}
+	return closedCh
+}
+
+func NewKvEntity(name string, cfg *Config) *KvEntity {
+	var entity KvEntity
+	switch {
+	case core.ServerInfo.Config.EnableCache && cfg.InitSize > 0:
+		entity.Cacher = NewKvCacher(name, cfg)
+		entity.Indexer = NewCacheIndexer(cfg, entity.Cache())
+	default:
+		util.Logger().Infof(
+			"core will not cache '%s' and ignore all events of it, cache enabled: %v, init size: %d",
+			name, core.ServerInfo.Config.EnableCache, cfg.InitSize)
+		entity.Indexer = NewCommonIndexer(cfg.Key, cfg.Parser)
+	}
+	return &entity
+}
+
+func DefaultKvEntity() *KvEntity {
+	newKvEntityOnce.Do(func() {
+		defaultKvEntity = &KvEntity{
+			Indexer: NewCommonIndexer(Configure().Key, BytesParser),
+		}
+	})
+	return defaultKvEntity
+}
diff --git a/server/core/backend/entity_test.go b/server/core/backend/entity_test.go
new file mode 100644
index 00000000..1b6541fa
--- /dev/null
+++ b/server/core/backend/entity_test.go
@@ -0,0 +1,48 @@
+/*
+ * 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 backend
+
+import (
+	"github.com/apache/incubator-servicecomb-service-center/server/core"
+	"testing"
+)
+
+func TestNewKvEntity(t *testing.T) {
+	core.ServerInfo.Config.EnableCache = false
+	i := NewKvEntity("a", Configure().WithInitSize(1))
+	if _, ok := i.Indexer.(*CommonIndexer); !ok {
+		t.Fatalf("TestNewIndexer failed")
+	}
+	core.ServerInfo.Config.EnableCache = true
+
+	i.Run()
+	<-i.Ready()
+	i.Stop()
+
+	i = NewKvEntity("a", Configure().WithInitSize(0))
+	if _, ok := i.Indexer.(*CommonIndexer); !ok {
+		t.Fatalf("TestNewIndexer failed")
+	}
+
+	i = NewKvEntity("a", Configure())
+	if _, ok := i.Indexer.(*CacheIndexer); !ok {
+		t.Fatalf("TestNewIndexer failed")
+	}
+	if _, ok := i.Cacher.(*KvCacher); !ok {
+		t.Fatalf("TestNewIndexer failed")
+	}
+}
diff --git a/server/core/backend/event_proxy.go b/server/core/backend/event_proxy.go
index 3b022874..f3c77cb0 100644
--- a/server/core/backend/event_proxy.go
+++ b/server/core/backend/event_proxy.go
@@ -16,16 +16,18 @@
  */
 package backend
 
-import "sync"
+import (
+	"sync"
+)
 
 var (
-	EventProxies map[StoreType]*KvEventProxy
+	eventProxies map[StoreType]*KvEventProxy
 )
 
 func init() {
-	EventProxies = make(map[StoreType]*KvEventProxy, typeEnd)
+	eventProxies = make(map[StoreType]*KvEventProxy)
 	for i := StoreType(0); i != typeEnd; i++ {
-		EventProxies[i] = NewEventProxy()
+		NewEventProxy(i)
 	}
 }
 
@@ -48,10 +50,21 @@ func (h *KvEventProxy) OnEvent(evt KvEvent) {
 	h.lock.RUnlock()
 }
 
-func NewEventProxy() *KvEventProxy {
-	return &KvEventProxy{}
+func (h *KvEventProxy) injectConfig(t StoreType) *Config {
+	return TypeConfig[t].AppendEventFunc(h.OnEvent)
+}
+
+// unsafe
+func NewEventProxy(t StoreType) *KvEventProxy {
+	if proxy, ok := eventProxies[t]; ok {
+		return proxy
+	}
+	proxy := &KvEventProxy{}
+	proxy.injectConfig(t)
+	eventProxies[t] = proxy
+	return proxy
 }
 
 func EventProxy(t StoreType) *KvEventProxy {
-	return EventProxies[t]
+	return eventProxies[t]
 }
diff --git a/server/core/backend/event_test.go b/server/core/backend/event_test.go
new file mode 100644
index 00000000..97632cbf
--- /dev/null
+++ b/server/core/backend/event_test.go
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package backend
+
+import "testing"
+
+type mockEventHandler struct {
+	StoreType StoreType
+	Evt       KvEvent
+}
+
+func (h *mockEventHandler) Type() StoreType {
+	return h.StoreType
+}
+func (h *mockEventHandler) OnEvent(evt KvEvent) {
+	h.Evt = evt
+}
+
+func TestAddEventHandler(t *testing.T) {
+	h := &mockEventHandler{StoreType: SERVICE}
+	evt := KvEvent{Revision: 1}
+
+	// case: add
+	proxy := EventProxy(SERVICE)
+	if nil == proxy {
+		t.Fatalf("TestAddEventHandler failed")
+	}
+	if NewEventProxy(SERVICE) != proxy {
+		t.Fatalf("TestAddEventHandler failed")
+	}
+
+	// case: normal
+	AddEventHandler(h)
+	proxy.OnEvent(evt)
+	if h.Evt != evt {
+		t.Fatalf("TestAddEventHandler failed")
+	}
+}
diff --git a/server/core/backend/extend.go b/server/core/backend/extend.go
index dbc0cd14..83d16d5f 100644
--- a/server/core/backend/extend.go
+++ b/server/core/backend/extend.go
@@ -16,12 +16,7 @@
  */
 package backend
 
-import (
-	"errors"
-	"fmt"
-)
-
-type Entity interface {
+type Extension interface {
 	Name() string
 	Config() *Config
 }
@@ -39,29 +34,7 @@ func (e *entity) Config() *Config {
 	return e.cfg
 }
 
-func InstallType(e Entity) (id StoreType, err error) {
-	if e == nil {
-		return NONEXIST, errors.New("invalid parameter")
-	}
-	for _, n := range TypeNames {
-		if n == e.Name() {
-			return NONEXIST, fmt.Errorf("redeclare store type '%s'", n)
-		}
-	}
-	for _, r := range TypeConfig {
-		if r.Prefix == e.Config().Prefix {
-			return NONEXIST, fmt.Errorf("redeclare store root '%s'", r)
-		}
-	}
-
-	id = StoreType(len(TypeNames))
-	TypeNames = append(TypeNames, e.Name())
-	TypeConfig[id] = e.Config()
-	EventProxies[id] = NewEventProxy()
-	return
-}
-
-func NewEntity(name string, cfg *Config) Entity {
+func NewExtension(name string, cfg *Config) Extension {
 	return &entity{
 		name: name,
 		cfg:  cfg,
diff --git a/server/core/backend/indexer.go b/server/core/backend/indexer.go
index 4f47a72d..73333814 100644
--- a/server/core/backend/indexer.go
+++ b/server/core/backend/indexer.go
@@ -17,78 +17,10 @@
 package backend
 
 import (
-	"fmt"
-	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
 	"github.com/apache/incubator-servicecomb-service-center/server/infra/registry"
 	"golang.org/x/net/context"
-	"strings"
 )
 
-type Response struct {
-	Kvs   []*KeyValue
-	Count int64
-}
-
-func (pr *Response) MaxModRevision() (max int64) {
-	for _, kv := range pr.Kvs {
-		if max < kv.ModRevision {
-			max = kv.ModRevision
-		}
-	}
-	return
-}
-
 type Indexer interface {
 	Search(ctx context.Context, opts ...registry.PluginOpOption) (*Response, error)
-	Run()
-	Stop()
-	Ready() <-chan struct{}
-}
-
-type baseIndexer struct {
-	Cfg *Config
-}
-
-func (i *baseIndexer) Search(ctx context.Context, opts ...registry.PluginOpOption) (r *Response, err error) {
-	op := registry.OpGet(opts...)
-	key := util.BytesToStringWithNoCopy(op.Key)
-	if strings.Index(key, i.Cfg.Prefix) != 0 {
-		return nil, fmt.Errorf("search %s mismatch pattern %s", key, i.Cfg.Prefix)
-	}
-
-	resp, err := Registry().Do(ctx, opts...)
-	if err != nil {
-		return nil, err
-	}
-
-	r = new(Response)
-	r.Count = resp.Count
-	if len(resp.Kvs) == 0 {
-		return
-	}
-
-	kvs := make([]*KeyValue, 0, len(resp.Kvs))
-	for _, src := range resp.Kvs {
-		kv := new(KeyValue)
-		if err = kv.From(i.Cfg.Parser, src); err != nil {
-			continue
-		}
-		kvs = append(kvs, kv)
-	}
-	r.Kvs = kvs
-	return
-}
-
-func (i *baseIndexer) Run() {
-}
-
-func (i *baseIndexer) Stop() {
-}
-
-func (i *baseIndexer) Ready() <-chan struct{} {
-	return closedCh
-}
-
-func NewBaseIndexer(cfg *Config) (indexer *baseIndexer) {
-	return &baseIndexer{Cfg: cfg}
 }
diff --git a/server/core/backend/indexer_base.go b/server/core/backend/indexer_base.go
new file mode 100644
index 00000000..b2ba5bdd
--- /dev/null
+++ b/server/core/backend/indexer_base.go
@@ -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 backend
+
+import (
+	"fmt"
+	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
+	"github.com/apache/incubator-servicecomb-service-center/server/infra/registry"
+	"golang.org/x/net/context"
+	"strings"
+)
+
+type CommonIndexer struct {
+	Client registry.Registry
+	Parser Parser
+	Root   string
+}
+
+func (i *CommonIndexer) CheckPrefix(key string) error {
+	if strings.Index(key, i.Root) != 0 {
+		return fmt.Errorf("search '%s' mismatch pattern %s", key, i.Root)
+	}
+	return nil
+}
+
+func (i *CommonIndexer) Search(ctx context.Context, opts ...registry.PluginOpOption) (r *Response, err error) {
+	op := registry.OpGet(opts...)
+
+	key := util.BytesToStringWithNoCopy(op.Key)
+
+	if err := i.CheckPrefix(key); err != nil {
+		return nil, err
+	}
+
+	resp, err := i.Client.Do(ctx, opts...)
+	if err != nil {
+		return nil, err
+	}
+
+	r = new(Response)
+	r.Count = resp.Count
+	if len(resp.Kvs) == 0 || op.CountOnly {
+		return
+	}
+
+	p := i.Parser
+	if op.KeyOnly {
+		p = nil
+	}
+
+	kvs := make([]*KeyValue, 0, len(resp.Kvs))
+	for _, src := range resp.Kvs {
+		kv := new(KeyValue)
+		if err = kv.From(p, src); err != nil {
+			continue
+		}
+		kvs = append(kvs, kv)
+	}
+	r.Kvs = kvs
+	return
+}
+
+func NewCommonIndexer(root string, p Parser) (indexer *CommonIndexer) {
+	return &CommonIndexer{Client: Registry(), Parser: p, Root: root}
+}
diff --git a/server/core/backend/indexer_kv.go b/server/core/backend/indexer_kv.go
index 7cf24431..d1fb7662 100644
--- a/server/core/backend/indexer_kv.go
+++ b/server/core/backend/indexer_kv.go
@@ -19,37 +19,36 @@ package backend
 import (
 	"fmt"
 	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
-	"github.com/apache/incubator-servicecomb-service-center/server/core"
 	"github.com/apache/incubator-servicecomb-service-center/server/infra/registry"
 	"golang.org/x/net/context"
-	"strings"
 	"sync"
 	"time"
 )
 
 type CacheIndexer struct {
-	*baseIndexer
+	*CommonIndexer
 
-	cacher  Cacher
-	lock    sync.Mutex
-	isClose bool
+	Cache Cache
+
+	lock sync.Mutex
 }
 
 func (i *CacheIndexer) Search(ctx context.Context, opts ...registry.PluginOpOption) (*Response, error) {
 	op := registry.OpGet(opts...)
+
 	key := util.BytesToStringWithNoCopy(op.Key)
-	if strings.Index(key, i.baseIndexer.Cfg.Prefix) != 0 {
-		return nil, fmt.Errorf("search %s mismatch %s pattern %s",
-			key, i.cacher.Cache().Name(), i.baseIndexer.Cfg.Prefix)
-	}
 
-	if !core.ServerInfo.Config.EnableCache ||
+	if i.Cache == nil ||
 		op.Mode == registry.MODE_NO_CACHE ||
 		op.Revision > 0 ||
 		(op.Offset >= 0 && op.Limit > 0) {
-		util.Logger().Debugf("search %s match special options, request etcd server, opts: %s",
-			i.cacher.Cache().Name(), op)
-		return i.baseIndexer.Search(ctx, opts...)
+		util.Logger().Debugf("search '%s' match special options, request etcd server, opts: %s",
+			key, op)
+		return i.CommonIndexer.Search(ctx, opts...)
+	}
+
+	if err := i.CheckPrefix(key); err != nil {
+		return nil, fmt.Errorf("%s, cache is '%s'", err.Error(), i.Cache.Name())
 	}
 
 	var resp *Response
@@ -63,9 +62,9 @@ func (i *CacheIndexer) Search(ctx context.Context, opts ...registry.PluginOpOpti
 		return resp, nil
 	}
 
-	util.Logger().Debugf("can not find any key from %s cache with prefix, request etcd server, key: %s",
-		i.cacher.Cache().Name(), key)
-	return i.baseIndexer.Search(ctx, opts...)
+	util.Logger().Debugf("can not find any key from %s cache, request etcd server, key: %s",
+		i.Cache.Name(), key)
+	return i.CommonIndexer.Search(ctx, opts...)
 }
 
 func (i *CacheIndexer) search(op registry.PluginOp) *Response {
@@ -73,7 +72,7 @@ func (i *CacheIndexer) search(op registry.PluginOp) *Response {
 
 	key := util.BytesToStringWithNoCopy(op.Key)
 
-	kv := i.cacher.Cache().Get(key)
+	kv := i.Cache.Get(key)
 	if kv != nil {
 		resp.Count = 1
 	}
@@ -90,61 +89,24 @@ func (i *CacheIndexer) searchByPrefix(op registry.PluginOp) *Response {
 
 	prefix := util.BytesToStringWithNoCopy(op.Key)
 
-	resp.Count = int64(i.cacher.Cache().GetAll(prefix, nil))
+	resp.Count = int64(i.Cache.GetPrefix(prefix, nil))
 	if resp.Count == 0 || op.CountOnly {
 		return resp
 	}
 
 	t := time.Now()
 	kvs := make([]*KeyValue, 0, resp.Count)
-	i.cacher.Cache().GetAll(prefix, &kvs)
+	i.Cache.GetPrefix(prefix, &kvs)
 
-	util.LogNilOrWarnf(t, "too long to index data[%d] from cache '%s'", len(kvs), i.cacher.Cache().Name())
+	util.LogNilOrWarnf(t, "too long to index data[%d] from cache '%s'", len(kvs), i.Cache.Name())
 
 	resp.Kvs = kvs
 	return resp
 }
 
-func (i *CacheIndexer) Run() {
-	i.lock.Lock()
-	if !i.isClose {
-		i.lock.Unlock()
-		return
-	}
-	i.isClose = false
-	i.lock.Unlock()
-
-	i.cacher.Run()
-}
-
-func (i *CacheIndexer) Stop() {
-	i.lock.Lock()
-	if i.isClose {
-		i.lock.Unlock()
-		return
-	}
-	i.isClose = true
-	i.lock.Unlock()
-
-	i.cacher.Stop()
-}
-
-func (i *CacheIndexer) Ready() <-chan struct{} {
-	return i.cacher.Ready()
-}
-
-func NewCacheIndexer(name string, cfg *Config) (indexer *CacheIndexer) {
-	indexer = &CacheIndexer{
-		baseIndexer: NewBaseIndexer(cfg),
-		cacher:      NullCacher,
-		isClose:     true,
-	}
-
-	switch {
-	case core.ServerInfo.Config.EnableCache && cfg.InitSize > 0:
-		indexer.cacher = NewKvCacher(name, cfg)
-	default:
-		util.Logger().Infof("service center will not cache '%s'", name)
+func NewCacheIndexer(cfg *Config, cache Cache) *CacheIndexer {
+	return &CacheIndexer{
+		CommonIndexer: NewCommonIndexer(cfg.Key, cfg.Parser),
+		Cache:         cache,
 	}
-	return
 }
diff --git a/server/core/backend/indexer_test.go b/server/core/backend/indexer_test.go
new file mode 100644
index 00000000..c38e7273
--- /dev/null
+++ b/server/core/backend/indexer_test.go
@@ -0,0 +1,178 @@
+/*
+ * 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 backend
+
+import (
+	"context"
+	"github.com/apache/incubator-servicecomb-service-center/server/infra/registry"
+	"github.com/coreos/etcd/mvcc/mvccpb"
+	"testing"
+)
+
+func TestCommonIndexer_Search(t *testing.T) {
+	data := &registry.PluginResponse{Revision: 1}
+	c := &mockRegistry{}
+	i := &CommonIndexer{Client: c, Root: "/", Parser: BytesParser}
+
+	// case: key does not contain prefix
+	resp, err := i.Search(context.Background(), registry.WithStrKey("a"))
+	if err == nil || resp != nil {
+		t.Fatalf("TestBaseIndexer_Search failed")
+	}
+
+	// case: client return err
+	resp, err = i.Search(context.Background(), registry.WithStrKey("/a"))
+	if err == nil || resp != nil {
+		t.Fatalf("TestBaseIndexer_Search failed")
+	}
+
+	// case: kvs is empty
+	c.Response = data
+	resp, err = i.Search(context.Background(), registry.WithStrKey("/a"))
+	if err != nil || resp == nil || resp.Count != 0 {
+		t.Fatalf("TestBaseIndexer_Search failed")
+	}
+
+	// case: parse error
+	data.Count = 2
+	data.Kvs = []*mvccpb.KeyValue{{Key: []byte("/a/b"), Value: []byte("abc")}, {Key: []byte("/a/c"), Value: []byte("{}")}}
+	old := i.Parser
+	i.Parser = MapParser
+	c.Response = data
+	resp, err = i.Search(context.Background(), registry.WithStrKey("/a"))
+	if err != nil || resp == nil || resp.Count != 2 || len(resp.Kvs) != 1 {
+		t.Fatalf("TestBaseIndexer_Search failed, %v, %v", err, resp)
+	}
+	i.Parser = old
+
+	// case: normal
+	data.Count = 2
+	data.Kvs = []*mvccpb.KeyValue{{Key: []byte("/a/b"), Value: []byte("abc")}, {Key: []byte("/a/c"), Value: []byte("{}")}}
+	c.Response = data
+	resp, err = i.Search(context.Background(), registry.WithStrKey("/a"))
+	if err != nil || resp == nil || resp.Count != 2 || len(resp.Kvs) != 2 {
+		t.Fatalf("TestBaseIndexer_Search failed, %v, %v", err, resp)
+	}
+	resp, err = i.Search(context.Background(), registry.WithStrKey("/a"), registry.WithKeyOnly())
+	if err != nil || resp == nil || resp.Count != 2 || len(resp.Kvs) != 2 {
+		t.Fatalf("TestBaseIndexer_Search failed, %v, %v", err, resp)
+	}
+	resp, err = i.Search(context.Background(), registry.WithStrKey("/a"), registry.WithCountOnly())
+	if err != nil || resp == nil || resp.Count != 2 || len(resp.Kvs) != 0 {
+		t.Fatalf("TestBaseIndexer_Search failed, %v, %v", err, resp)
+	}
+}
+
+func TestCacheIndexer_Search(t *testing.T) {
+	c := &mockCache{}
+	cli := &mockRegistry{Response: &registry.PluginResponse{
+		Revision: 1,
+		Kvs:      []*mvccpb.KeyValue{{Key: []byte("/a/b"), Value: []byte("abc")}},
+		Count:    1,
+	}}
+	i := &CacheIndexer{
+		CommonIndexer: &CommonIndexer{
+			Root:   "/",
+			Client: cli,
+		},
+		Cache: c,
+	}
+
+	// case: key does not contain prefix
+	resp, err := i.Search(context.Background(), registry.WithStrKey("a"))
+	if err == nil || resp != nil {
+		t.Fatalf("TestBaseIndexer_Search failed, %v, %v", err, resp)
+	}
+
+	// case: not match cache search remote
+	resp, err = i.Search(context.Background(), registry.WithStrKey("/a"))
+	if err != nil || resp == nil || resp.Count != 1 || string(resp.Kvs[0].Key) != "/a/b" {
+		t.Fatalf("TestBaseIndexer_Search failed, %v, %v", err, resp)
+	}
+
+	// no cache
+	resp, err = i.Search(context.Background(), registry.WithStrKey("/a"), registry.WithNoCache())
+	if err != nil || resp == nil || resp.Count != 1 || string(resp.Kvs[0].Key) != "/a/b" {
+		t.Fatalf("TestBaseIndexer_Search failed, %v, %v", err, resp)
+	}
+
+	// rev > 0 or paging
+	resp, err = i.Search(context.Background(), registry.WithStrKey("/a"), registry.WithRev(1))
+	if err != nil || resp == nil || resp.Count != 1 || string(resp.Kvs[0].Key) != "/a/b" {
+		t.Fatalf("TestBaseIndexer_Search failed, %v, %v", err, resp)
+	}
+	resp, err = i.Search(context.Background(), registry.WithStrKey("/a"), registry.WithOffset(0), registry.WithLimit(1))
+	if err != nil || resp == nil || resp.Count != 1 || string(resp.Kvs[0].Key) != "/a/b" {
+		t.Fatalf("TestBaseIndexer_Search failed, %v, %v", err, resp)
+	}
+
+	// not match cache
+	c.Put("ka", &KeyValue{Key: []byte("ka"), Value: []byte("va"), Version: 1, ModRevision: 1})
+	resp, err = i.Search(context.Background(), registry.WithStrKey("/a"))
+	if err != nil || resp == nil || resp.Count != 1 || string(resp.Kvs[0].Key) != "/a/b" {
+		t.Fatalf("TestBaseIndexer_Search failed, %v, %v", err, resp)
+	}
+	resp, err = i.Search(context.Background(), registry.WithStrKey("/a"), registry.WithPrefix())
+	if err != nil || resp == nil || resp.Count != 1 || string(resp.Kvs[0].Key) != "/a/b" {
+		t.Fatalf("TestBaseIndexer_Search failed, %v, %v", err, resp)
+	}
+	resp, err = i.Search(context.Background(), registry.WithStrKey("/a"), registry.WithCountOnly())
+	if err != nil || resp == nil || resp.Count != 1 {
+		t.Fatalf("TestBaseIndexer_Search failed, %v, %v", err, resp)
+	}
+
+	// remote err
+	oldResp := cli.Response
+	cli.Response = nil
+	resp, err = i.Search(context.Background(), registry.WithStrKey("/a"))
+	if err == nil || resp != nil {
+		t.Fatalf("TestBaseIndexer_Search failed, %v, %v", err, resp)
+	}
+	cli.Response = oldResp
+
+	// case: use cache index
+	c.Put("/a", &KeyValue{Key: []byte("/a"), Value: []byte("va"), Version: 1, ModRevision: 1})
+
+	// exact match
+	resp, err = i.Search(context.Background(), registry.WithStrKey("/a"))
+	if err != nil || resp == nil || resp.Count != 1 || string(resp.Kvs[0].Value.([]byte)) != "va" {
+		t.Fatalf("TestBaseIndexer_Search failed, %v, %v", err, resp)
+	}
+	if resp.MaxModRevision() != 1 {
+		t.Fatalf("TestBaseIndexer_Search failed, %v, %v", err, resp)
+	}
+	resp, err = i.Search(context.Background(), registry.WithStrKey("/a"), registry.WithCountOnly())
+	if err != nil || resp == nil || resp.Count != 1 || len(resp.Kvs) != 0 {
+		t.Fatalf("TestBaseIndexer_Search failed, %v, %v", err, resp)
+	}
+
+	// prefix match
+	resp, err = i.Search(context.Background(), registry.WithStrKey("/a"), registry.WithPrefix())
+	if err != nil || resp == nil || resp.Count != 1 || string(resp.Kvs[0].Value.([]byte)) != "va" {
+		t.Fatalf("TestBaseIndexer_Search failed, %v, %v", err, resp)
+	}
+	resp, err = i.Search(context.Background(), registry.WithStrKey("/a"), registry.WithPrefix(), registry.WithCountOnly())
+	if err != nil || resp == nil || resp.Count != 1 || len(resp.Kvs) != 0 {
+		t.Fatalf("TestBaseIndexer_Search failed, %v, %v", err, resp)
+	}
+
+	// mismatch cache but cacheonly
+	resp, err = i.Search(context.Background(), registry.WithStrKey("/a/b"), registry.WithCacheOnly())
+	if err != nil || resp == nil || resp.Count != 0 || len(resp.Kvs) != 0 {
+		t.Fatalf("TestBaseIndexer_Search failed, %v, %v", err, resp)
+	}
+}
diff --git a/server/core/backend/lease.go b/server/core/backend/lease.go
index 89fcb2e0..d3e7e8ee 100644
--- a/server/core/backend/lease.go
+++ b/server/core/backend/lease.go
@@ -25,6 +25,8 @@ import (
 )
 
 type LeaseTask struct {
+	Client registry.Registry
+
 	key     string
 	LeaseID int64
 	TTL     int64
@@ -40,7 +42,7 @@ func (lat *LeaseTask) Key() string {
 
 func (lat *LeaseTask) Do(ctx context.Context) (err error) {
 	recv, start := lat.ReceiveTime(), time.Now()
-	lat.TTL, err = Registry().LeaseRenew(ctx, lat.LeaseID)
+	lat.TTL, err = lat.Client.LeaseRenew(ctx, lat.LeaseID)
 	if err != nil {
 		util.Logger().Errorf(err, "[%s]renew lease %d failed(recv: %s, send: %s), key %s",
 			time.Now().Sub(recv),
@@ -79,6 +81,7 @@ func (lat *LeaseTask) ReceiveTime() time.Time {
 func NewLeaseAsyncTask(op registry.PluginOp) *LeaseTask {
 	now := time.Now().UTC()
 	return &LeaseTask{
+		Client:   Registry(),
 		key:      ToLeaseAsyncTaskKey(util.BytesToStringWithNoCopy(op.Key)),
 		LeaseID:  op.Lease,
 		recvSec:  now.Unix(),
diff --git a/server/core/backend/lease_test.go b/server/core/backend/lease_test.go
new file mode 100644
index 00000000..62e8a887
--- /dev/null
+++ b/server/core/backend/lease_test.go
@@ -0,0 +1,49 @@
+/*
+ * 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 backend
+
+import (
+	"context"
+	"fmt"
+	errorsEx "github.com/apache/incubator-servicecomb-service-center/pkg/errors"
+	"testing"
+	"time"
+)
+
+func TestLeaseTask_Do(t *testing.T) {
+	now := time.Now().UTC()
+	c := &mockRegistry{}
+	lt := &LeaseTask{
+		Client:   c,
+		key:      ToLeaseAsyncTaskKey("/a"),
+		LeaseID:  1,
+		recvSec:  now.Unix(),
+		recvNsec: int64(now.Nanosecond()),
+	}
+
+	c.LeaseErr = errorsEx.InternalError("lease not found")
+	err := lt.Do(context.Background())
+	if err != nil || lt.Err() != nil {
+		t.Fatalf("TestLeaseTask_Do failed")
+	}
+
+	c.LeaseErr = fmt.Errorf("network error")
+	err = lt.Do(context.Background())
+	if err == nil || lt.Err() == nil {
+		t.Fatalf("TestLeaseTask_Do failed")
+	}
+}
diff --git a/server/core/backend/listwatch.go b/server/core/backend/listwatch.go
index 469a8d14..0286b2fc 100644
--- a/server/core/backend/listwatch.go
+++ b/server/core/backend/listwatch.go
@@ -17,124 +17,15 @@
 package backend
 
 import (
-	"fmt"
-	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
 	"github.com/apache/incubator-servicecomb-service-center/server/infra/registry"
 	"golang.org/x/net/context"
-	"sync"
 )
 
-type ListWatcher struct {
-	Client registry.Registry
-	Prefix string
-
-	rev int64
-}
-
-func (lw *ListWatcher) List(op ListWatchConfig) (*registry.PluginResponse, error) {
-	otCtx, _ := context.WithTimeout(op.Context, op.Timeout)
-	resp, err := lw.Client.Do(otCtx, registry.WatchPrefixOpOptions(lw.Prefix)...)
-	if err != nil {
-		util.Logger().Errorf(err, "list prefix %s failed, current rev: %d", lw.Prefix, lw.Revision())
-		return nil, err
-	}
-	lw.setRevision(resp.Revision)
-	return resp, nil
-}
-
-func (lw *ListWatcher) Revision() int64 {
-	return lw.rev
-}
-
-func (lw *ListWatcher) setRevision(rev int64) {
-	lw.rev = rev
-}
-
-func (lw *ListWatcher) Watch(op ListWatchConfig) *Watcher {
-	return newWatcher(lw, op)
-}
-
-func (lw *ListWatcher) doWatch(ctx context.Context, f func(*registry.PluginResponse)) error {
-	rev := lw.Revision()
-	opts := append(
-		registry.WatchPrefixOpOptions(lw.Prefix),
-		registry.WithRev(rev+1),
-		registry.WithWatchCallback(
-			func(message string, resp *registry.PluginResponse) error {
-				if resp == nil || len(resp.Kvs) == 0 {
-					return fmt.Errorf("unknown event %s, watch prefix %s", resp, lw.Prefix)
-				}
-
-				lw.setRevision(resp.Revision)
-
-				f(resp)
-				return nil
-			}))
-
-	err := lw.Client.Watch(ctx, opts...)
-	if err != nil { // compact可能会导致watch失败 or message body size lager than 4MB
-		util.Logger().Errorf(err, "watch prefix %s failed, start rev: %d+1->%d->0", lw.Prefix, rev, lw.Revision())
-
-		lw.setRevision(0)
-		f(nil)
-	}
-	return err
-}
-
-type Watcher struct {
-	ListOps ListWatchConfig
-	lw      *ListWatcher
-	bus     chan *registry.PluginResponse
-	stopCh  chan struct{}
-	stop    bool
-	mux     sync.Mutex
-}
-
-func (w *Watcher) EventBus() <-chan *registry.PluginResponse {
-	return w.bus
-}
-
-func (w *Watcher) process(_ context.Context) {
-	stopCh := make(chan struct{})
-	ctx, cancel := context.WithTimeout(w.ListOps.Context, w.ListOps.Timeout)
-	util.Go(func(_ context.Context) {
-		defer close(stopCh)
-		w.lw.doWatch(ctx, w.sendEvent)
-	})
-
-	select {
-	case <-stopCh:
-		// timed out or exception
-		w.Stop()
-	case <-w.stopCh:
-		cancel()
-	}
-}
-
-func (w *Watcher) sendEvent(resp *registry.PluginResponse) {
-	defer util.RecoverAndReport()
-	w.bus <- resp
-}
-
-func (w *Watcher) Stop() {
-	w.mux.Lock()
-	if w.stop {
-		w.mux.Unlock()
-		return
-	}
-	w.stop = true
-	close(w.stopCh)
-	close(w.bus)
-	w.mux.Unlock()
-}
-
-func newWatcher(lw *ListWatcher, listOps ListWatchConfig) *Watcher {
-	w := &Watcher{
-		ListOps: listOps,
-		lw:      lw,
-		bus:     make(chan *registry.PluginResponse, EVENT_BUS_MAX_SIZE),
-		stopCh:  make(chan struct{}),
-	}
-	util.Go(w.process)
-	return w
+type ListWatch interface {
+	List(op ListWatchConfig) (*registry.PluginResponse, error)
+	// not support new multiple watchers
+	Watch(op ListWatchConfig) Watcher
+	//
+	DoWatch(ctx context.Context, f func(*registry.PluginResponse)) error
+	Revision() int64
 }
diff --git a/server/core/backend/listwatch_inner.go b/server/core/backend/listwatch_inner.go
new file mode 100644
index 00000000..70b7234a
--- /dev/null
+++ b/server/core/backend/listwatch_inner.go
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package backend
+
+import (
+	"fmt"
+	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
+	"github.com/apache/incubator-servicecomb-service-center/server/infra/registry"
+	"golang.org/x/net/context"
+)
+
+type innerListWatch struct {
+	Client registry.Registry
+	Prefix string
+
+	rev int64
+}
+
+func (lw *innerListWatch) List(op ListWatchConfig) (*registry.PluginResponse, error) {
+	otCtx, _ := context.WithTimeout(op.Context, op.Timeout)
+	resp, err := lw.Client.Do(otCtx, registry.WatchPrefixOpOptions(lw.Prefix)...)
+	if err != nil {
+		util.Logger().Errorf(err, "list prefix %s failed, current rev: %d", lw.Prefix, lw.Revision())
+		return nil, err
+	}
+	lw.setRevision(resp.Revision)
+	return resp, nil
+}
+
+func (lw *innerListWatch) Revision() int64 {
+	return lw.rev
+}
+
+func (lw *innerListWatch) setRevision(rev int64) {
+	lw.rev = rev
+}
+
+func (lw *innerListWatch) Watch(op ListWatchConfig) Watcher {
+	return newInnerWatcher(lw, op)
+}
+
+func (lw *innerListWatch) DoWatch(ctx context.Context, f func(*registry.PluginResponse)) error {
+	rev := lw.Revision()
+	opts := append(
+		registry.WatchPrefixOpOptions(lw.Prefix),
+		registry.WithRev(rev+1),
+		registry.WithWatchCallback(
+			func(message string, resp *registry.PluginResponse) error {
+				if resp == nil || len(resp.Kvs) == 0 {
+					return fmt.Errorf("unknown event %s, watch prefix %s", resp, lw.Prefix)
+				}
+
+				lw.setRevision(resp.Revision)
+
+				f(resp)
+				return nil
+			}))
+
+	err := lw.Client.Watch(ctx, opts...)
+	if err != nil { // compact可能会导致watch失败 or message body size lager than 4MB
+		util.Logger().Errorf(err, "watch prefix %s failed, start rev: %d+1->%d->0", lw.Prefix, rev, lw.Revision())
+
+		lw.setRevision(0)
+		f(nil)
+	}
+	return err
+}
diff --git a/server/core/backend/listwatch_test.go b/server/core/backend/listwatch_test.go
new file mode 100644
index 00000000..1a7b9a96
--- /dev/null
+++ b/server/core/backend/listwatch_test.go
@@ -0,0 +1,119 @@
+/*
+ * 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 backend
+
+import (
+	"fmt"
+	"github.com/apache/incubator-servicecomb-service-center/server/infra/registry"
+	"github.com/apache/incubator-servicecomb-service-center/server/plugin/infra/registry/buildin"
+	"github.com/coreos/etcd/mvcc/mvccpb"
+	"golang.org/x/net/context"
+	"testing"
+	"time"
+)
+
+type mockRegistry struct {
+	*buildin.BuildinRegistry
+	Response *registry.PluginResponse
+	LeaseErr error
+}
+
+func (c *mockRegistry) Do(ctx context.Context, opts ...registry.PluginOpOption) (*registry.PluginResponse, error) {
+	if c.Response == nil {
+		return nil, fmt.Errorf("error")
+	}
+	return c.Response, nil
+}
+func (c *mockRegistry) Watch(ctx context.Context, opts ...registry.PluginOpOption) error {
+	op := registry.OptionsToOp(opts...)
+	if c.Response == nil {
+		return fmt.Errorf("error")
+	}
+	resp := *c.Response
+	if len(c.Response.Kvs) > 0 {
+		resp.Revision = c.Response.Kvs[0].ModRevision
+	}
+	err := op.WatchCallback("ok", &resp)
+	if err != nil {
+		return err
+	}
+	<-ctx.Done()
+	return nil
+}
+func (c *mockRegistry) LeaseRenew(ctx context.Context, leaseID int64) (TTL int64, err error) {
+	if c.LeaseErr != nil {
+		return 0, c.LeaseErr
+	}
+	return 1, nil
+}
+
+func TestPrefixListWatch(t *testing.T) {
+	lw := &innerListWatch{
+		Client: &mockRegistry{},
+		Prefix: "a",
+		rev:    1,
+	}
+	resp, err := lw.List(ListWatchConfig{Timeout: time.Second, Context: context.Background()})
+	if resp != nil || err == nil || lw.Revision() != 1 {
+		t.Fatalf("TestPrefixListWatch failed")
+	}
+	w := lw.Watch(ListWatchConfig{Timeout: time.Second, Context: context.Background()})
+	resp = <-w.EventBus()
+	if resp != nil || lw.Revision() != 0 {
+		t.Fatalf("TestPrefixListWatch failed")
+	}
+	w.Stop()
+
+	test := &registry.PluginResponse{
+		Revision: 2,
+	}
+	lw = &innerListWatch{
+		Client: &mockRegistry{Response: test},
+		Prefix: "a",
+		rev:    1,
+	}
+	resp, err = lw.List(ListWatchConfig{Timeout: time.Second, Context: context.Background()})
+	if resp == nil || err != nil || lw.Revision() != 2 {
+		t.Fatalf("TestPrefixListWatch failed")
+	}
+	w = lw.Watch(ListWatchConfig{Timeout: time.Second, Context: context.Background()})
+	resp = <-w.EventBus()
+	if resp != nil || lw.Revision() != 0 {
+		t.Fatalf("TestPrefixListWatch failed")
+	}
+	w.Stop()
+
+	test = &registry.PluginResponse{
+		Kvs:      []*mvccpb.KeyValue{{ModRevision: 3}},
+		Revision: 4,
+	}
+	lw = &innerListWatch{
+		Client: &mockRegistry{Response: test},
+		Prefix: "a",
+		rev:    1,
+	}
+	resp, err = lw.List(ListWatchConfig{Timeout: time.Second, Context: context.Background()})
+	if resp == nil || err != nil || lw.Revision() != 4 {
+		t.Fatalf("TestPrefixListWatch failed")
+	}
+	w = lw.Watch(ListWatchConfig{Timeout: time.Second, Context: context.Background()})
+	resp = <-w.EventBus()
+	if resp == nil || lw.Revision() != 3 {
+		t.Fatalf("TestPrefixListWatch failed")
+	}
+	w.Stop()
+}
diff --git a/server/core/backend/metrics.go b/server/core/backend/metrics.go
index 2423a7a0..2b5f4e8b 100644
--- a/server/core/backend/metrics.go
+++ b/server/core/backend/metrics.go
@@ -17,40 +17,31 @@
 package backend
 
 import (
-	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
-	"github.com/apache/incubator-servicecomb-service-center/server/core"
+	"github.com/apache/incubator-servicecomb-service-center/server/metric"
 	"github.com/prometheus/client_golang/prometheus"
-	"sync"
 )
 
 var (
 	cacheSizeGauge = prometheus.NewGaugeVec(
 		prometheus.GaugeOpts{
-			Namespace: "service_center",
+			Namespace: metric.FamilyName,
 			Subsystem: "local",
 			Name:      "cache_size_bytes",
 			Help:      "Local cache size summary of backend store",
 		}, []string{"instance", "resource", "type"})
 )
 
-var (
-	instance string
-	once     sync.Once
-)
-
 func init() {
 	prometheus.MustRegister(cacheSizeGauge)
 }
 
-func ReportCacheMetrics(resource, t string, obj interface{}) {
-	if len(core.Instance.Endpoints) == 0 || len(resource) == 0 {
+func ReportCacheSize(resource, t string, s int) {
+	instance := metric.InstanceName()
+	if len(instance) == 0 || len(resource) == 0 {
 		// endpoints list will be empty when initializing
 		// resource may be empty when report SCHEMA
 		return
 	}
 
-	once.Do(func() {
-		instance, _ = util.ParseEndpoint(core.Instance.Endpoints[0])
-	})
-	cacheSizeGauge.WithLabelValues(instance, resource, t).Set(float64(util.Sizeof(obj)))
+	cacheSizeGauge.WithLabelValues(instance, resource, t).Set(float64(s))
 }
diff --git a/server/core/backend/extend_test.go b/server/core/backend/metrics_test.go
similarity index 65%
rename from server/core/backend/extend_test.go
rename to server/core/backend/metrics_test.go
index 3f1e1af0..60766e90 100644
--- a/server/core/backend/extend_test.go
+++ b/server/core/backend/metrics_test.go
@@ -16,30 +16,23 @@
  */
 package backend
 
-import "testing"
+import (
+	"github.com/apache/incubator-servicecomb-service-center/server/metric"
+	"github.com/astaxie/beego"
+	"testing"
+)
 
-type extend struct {
+func init() {
+	beego.AppConfig.Set("httpaddr", "127.0.0.1")
 }
 
-func (e *extend) Name() string {
-	return "test"
-}
-
-func (e *extend) Config() *Config {
-	return DefaultConfig().WithPrefix("/test")
-}
-
-func TestInstallType(t *testing.T) {
-	id, err := InstallType(&extend{})
+func TestReportCacheSize(t *testing.T) {
+	ReportCacheSize("a", "b", 100)
+	err := metric.Gatherer.Collect()
 	if err != nil {
-		t.Fatal(err)
-	}
-	if id == NONEXIST {
-		t.Fatal(err)
+		t.Fatalf("TestReportCacheSize failed")
 	}
-
-	id, err = InstallType(&extend{})
-	if id != NONEXIST || err == nil {
-		t.Fatal("InstallType fail", err)
+	if metric.Gatherer.Records.Get("local_cache_size_bytes") != 100 {
+		t.Fatalf("TestReportCacheSize failed")
 	}
 }
diff --git a/server/core/backend/kv.go b/server/core/backend/model.go
similarity index 79%
rename from server/core/backend/kv.go
rename to server/core/backend/model.go
index f7b66881..2340dd9e 100644
--- a/server/core/backend/kv.go
+++ b/server/core/backend/model.go
@@ -28,11 +28,28 @@ type KeyValue struct {
 	ModRevision    int64
 }
 
-func (kv *KeyValue) From(p *Parser, s *mvccpb.KeyValue) (err error) {
+func (kv *KeyValue) From(p Parser, s *mvccpb.KeyValue) (err error) {
 	kv.Key = s.Key
 	kv.Version = s.Version
 	kv.CreateRevision = s.CreateRevision
 	kv.ModRevision = s.ModRevision
+	if p == nil {
+		return
+	}
 	kv.Value, err = p.Unmarshal(s.Value)
 	return
 }
+
+type Response struct {
+	Kvs   []*KeyValue
+	Count int64
+}
+
+func (pr *Response) MaxModRevision() (max int64) {
+	for _, kv := range pr.Kvs {
+		if max < kv.ModRevision {
+			max = kv.ModRevision
+		}
+	}
+	return
+}
diff --git a/server/core/backend/parser.go b/server/core/backend/parser.go
index dff9a2f4..26b291d6 100644
--- a/server/core/backend/parser.go
+++ b/server/core/backend/parser.go
@@ -66,28 +66,32 @@ var (
 	}
 )
 
-type Parser struct {
-	c CreateValueFunc
-	p ParseValueFunc
+type Parser interface {
+	Unmarshal(src []byte) (interface{}, error)
 }
 
-func (p *Parser) Unmarshal(src []byte) (interface{}, error) {
-	v := p.c()
-	if err := p.p(src, &v); err != nil {
+type CommonParser struct {
+	NewFunc  CreateValueFunc
+	FromFunc ParseValueFunc
+}
+
+func (p *CommonParser) Unmarshal(src []byte) (interface{}, error) {
+	v := p.NewFunc()
+	if err := p.FromFunc(src, &v); err != nil {
 		return nil, err
 	}
 	return v, nil
 }
 
 var (
-	BytesParser           = &Parser{newBytes, unParse}
-	StringParser          = &Parser{newString, textUnmarshal}
-	MapParser             = &Parser{newMap, mapUnmarshal}
-	ServiceParser         = &Parser{newService, jsonUnmarshal}
-	InstanceParser        = &Parser{newInstance, jsonUnmarshal}
-	RuleParser            = &Parser{newRule, jsonUnmarshal}
-	DependencyRuleParser  = &Parser{newDependencyRule, jsonUnmarshal}
-	DependencyQueueParser = &Parser{newDependencyQueue, jsonUnmarshal}
+	BytesParser           = &CommonParser{newBytes, unParse}
+	StringParser          = &CommonParser{newString, textUnmarshal}
+	MapParser             = &CommonParser{newMap, mapUnmarshal}
+	ServiceParser         = &CommonParser{newService, jsonUnmarshal}
+	InstanceParser        = &CommonParser{newInstance, jsonUnmarshal}
+	RuleParser            = &CommonParser{newRule, jsonUnmarshal}
+	DependencyRuleParser  = &CommonParser{newDependencyRule, jsonUnmarshal}
+	DependencyQueueParser = &CommonParser{newDependencyQueue, jsonUnmarshal}
 )
 
 func KvToResponse(kv *KeyValue) (keys []string) {
@@ -120,7 +124,7 @@ func GetInfoFromInstKV(kv *KeyValue) (serviceId, instanceId, domainProject strin
 func GetInfoFromDomainKV(kv *KeyValue) (domain string) {
 	keys := KvToResponse(kv)
 	l := len(keys)
-	if l < 1 {
+	if l < 2 {
 		return
 	}
 	domain = keys[l-1]
@@ -179,7 +183,7 @@ func GetInfoFromSvcIndexKV(kv *KeyValue) (key *pb.MicroServiceKey) {
 func GetInfoFromSchemaSummaryKV(kv *KeyValue) (schemaId string) {
 	keys := KvToResponse(kv)
 	l := len(keys)
-	if l < 1 {
+	if l < 2 {
 		return
 	}
 
@@ -189,7 +193,7 @@ func GetInfoFromSchemaSummaryKV(kv *KeyValue) (schemaId string) {
 func GetInfoFromSchemaKV(kv *KeyValue) (schemaId string) {
 	keys := KvToResponse(kv)
 	l := len(keys)
-	if l < 1 {
+	if l < 2 {
 		return
 	}
 
diff --git a/server/core/backend/parser_test.go b/server/core/backend/parser_test.go
index bf97d47c..77f34cc8 100644
--- a/server/core/backend/parser_test.go
+++ b/server/core/backend/parser_test.go
@@ -17,6 +17,7 @@
 package backend
 
 import (
+	"github.com/apache/incubator-servicecomb-service-center/server/core"
 	"github.com/apache/incubator-servicecomb-service-center/server/core/proto"
 	"testing"
 )
@@ -83,3 +84,111 @@ func TestParseValueFunc(t *testing.T) {
 		t.Fatalf("MapParser.Unmarshal failed, %s", v)
 	}
 }
+
+func TestGetInfoFromKV(t *testing.T) {
+	s, d := GetInfoFromSvcKV(&KeyValue{Key: []byte(core.GenerateServiceKey("a/b", "c"))})
+	if d != "a/b" || s != "c" {
+		t.Fatalf("TestGetInfoFromKV failed")
+	}
+	s, d = GetInfoFromSvcKV(&KeyValue{Key: []byte("sdf")})
+	if d != "" || s != "" {
+		t.Fatalf("TestGetInfoFromKV failed")
+	}
+
+	var i string
+	s, i, d = GetInfoFromInstKV(&KeyValue{Key: []byte(core.GenerateInstanceKey("a/b", "c", "d"))})
+	if d != "a/b" || s != "c" || i != "d" {
+		t.Fatalf("TestGetInfoFromKV failed")
+	}
+	s, i, d = GetInfoFromInstKV(&KeyValue{Key: []byte("sdf")})
+	if d != "" || s != "" || i != "" {
+		t.Fatalf("TestGetInfoFromKV failed")
+	}
+
+	d = GetInfoFromDomainKV(&KeyValue{Key: []byte(core.GenerateDomainKey("a"))})
+	if d != "a" {
+		t.Fatalf("TestGetInfoFromKV failed")
+	}
+	d = GetInfoFromDomainKV(&KeyValue{Key: []byte("sdf")})
+	if d != "" {
+		t.Fatalf("TestGetInfoFromKV failed")
+	}
+
+	d = GetInfoFromProjectKV(&KeyValue{Key: []byte(core.GenerateProjectKey("a", "b"))})
+	if d != "a/b" {
+		t.Fatalf("TestGetInfoFromKV failed")
+	}
+	d = GetInfoFromProjectKV(&KeyValue{Key: []byte("sdf")})
+	if d != "" {
+		t.Fatalf("TestGetInfoFromKV failed")
+	}
+
+	var r string
+	s, r, d = GetInfoFromRuleKV(&KeyValue{Key: []byte(core.GenerateServiceRuleKey("a/b", "c", "d"))})
+	if d != "a/b" || s != "c" || r != "d" {
+		t.Fatalf("TestGetInfoFromKV failed")
+	}
+	s, r, d = GetInfoFromRuleKV(&KeyValue{Key: []byte("sdf")})
+	if d != "" || s != "" || r != "" {
+		t.Fatalf("TestGetInfoFromKV failed")
+	}
+
+	s, d = GetInfoFromTagKV(&KeyValue{Key: []byte(core.GenerateServiceTagKey("a/b", "c"))})
+	if d != "a/b" || s != "c" {
+		t.Fatalf("TestGetInfoFromKV failed")
+	}
+	s, d = GetInfoFromTagKV(&KeyValue{Key: []byte("sdf")})
+	if d != "" || s != "" {
+		t.Fatalf("TestGetInfoFromKV failed")
+	}
+
+	key := GetInfoFromSvcIndexKV(&KeyValue{Key: []byte(core.GenerateServiceIndexKey(&proto.MicroServiceKey{
+		Tenant:      "a/b",
+		Project:     "",
+		AppId:       "c",
+		ServiceName: "d",
+		Version:     "e",
+		Environment: "f",
+		Alias:       "g",
+	}))})
+	if key.Tenant != "a/b" ||
+		key.AppId != "c" ||
+		key.ServiceName != "d" ||
+		key.Version != "e" ||
+		key.Environment != "f" ||
+		key.Project != "" ||
+		key.Alias != "" {
+		t.Fatalf("TestGetInfoFromKV failed")
+	}
+	key = GetInfoFromSvcIndexKV(&KeyValue{Key: []byte("sdf")})
+	if key != nil {
+		t.Fatalf("TestGetInfoFromKV failed")
+	}
+
+	m := GetInfoFromSchemaSummaryKV(&KeyValue{Key: []byte(core.GenerateServiceSchemaSummaryKey("a/b", "c", "d"))})
+	if m != "d" {
+		t.Fatalf("TestGetInfoFromKV failed")
+	}
+	m = GetInfoFromSchemaSummaryKV(&KeyValue{Key: []byte("sdf")})
+	if m != "" {
+		t.Fatalf("TestGetInfoFromKV failed")
+	}
+
+	m = GetInfoFromSchemaKV(&KeyValue{Key: []byte(core.GenerateServiceSchemaKey("a/b", "c", "d"))})
+	if m != "d" {
+		t.Fatalf("TestGetInfoFromKV failed")
+	}
+	m = GetInfoFromSchemaKV(&KeyValue{Key: []byte("sdf")})
+	if m != "" {
+		t.Fatalf("TestGetInfoFromKV failed")
+	}
+
+	s, d = GetInfoFromDependencyQueueKV(&KeyValue{Key: []byte(core.GenerateConsumerDependencyQueueKey("a/b", "c", "d"))})
+	if s != "c" || d != "a/b" {
+		t.Fatalf("TestGetInfoFromKV failed")
+	}
+	s, d = GetInfoFromDependencyQueueKV(&KeyValue{Key: []byte("sdf")})
+	if s != "" || d != "" {
+		t.Fatalf("TestGetInfoFromKV failed")
+	}
+}
diff --git a/server/core/backend/service.go b/server/core/backend/service.go
new file mode 100644
index 00000000..69b7e220
--- /dev/null
+++ b/server/core/backend/service.go
@@ -0,0 +1,23 @@
+/*
+ * 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 backend
+
+type Runnable interface {
+	Run()
+	Stop()
+	Ready() <-chan struct{}
+}
diff --git a/server/core/backend/store.go b/server/core/backend/store.go
index 608b2d50..6eb01e7f 100644
--- a/server/core/backend/store.go
+++ b/server/core/backend/store.go
@@ -17,9 +17,10 @@
 package backend
 
 import (
-	"github.com/apache/incubator-servicecomb-service-center/pkg/async"
+	"errors"
+	"fmt"
+	"github.com/apache/incubator-servicecomb-service-center/pkg/task"
 	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
-	"github.com/apache/incubator-servicecomb-service-center/server/core"
 	"github.com/apache/incubator-servicecomb-service-center/server/infra/registry"
 	"golang.org/x/net/context"
 	"sync"
@@ -32,8 +33,8 @@ func init() {
 }
 
 type KvStore struct {
-	indexers    map[StoreType]Indexer
-	taskService *async.TaskService
+	entities    *util.ConcurrentMap
+	taskService task.TaskService
 	lock        sync.RWMutex
 	ready       chan struct{}
 	goroutine   *util.GoRoutine
@@ -42,13 +43,10 @@ type KvStore struct {
 }
 
 func (s *KvStore) Initialize() {
-	s.indexers = make(map[StoreType]Indexer)
-	s.taskService = async.NewTaskService()
+	s.entities = util.NewConcurrentMap(0)
+	s.taskService = task.NewTaskService()
 	s.ready = make(chan struct{})
 	s.goroutine = util.NewGo(context.Background())
-	for t := StoreType(0); t != typeEnd; t++ {
-		s.setupIndexer(t, NewBaseIndexer(s.injectConfig(t)))
-	}
 }
 
 func (s *KvStore) OnCacheEvent(evt KvEvent) {
@@ -58,17 +56,27 @@ func (s *KvStore) OnCacheEvent(evt KvEvent) {
 }
 
 func (s *KvStore) injectConfig(t StoreType) *Config {
-	return TypeConfig[t].AppendEventFunc(s.OnCacheEvent).
-		AppendEventFunc(EventProxies[t].OnEvent)
+	return TypeConfig[t].AppendEventFunc(s.OnCacheEvent)
 }
 
-func (s *KvStore) setupIndexer(t StoreType, indexer Indexer) {
-	old := s.indexers[t]
-	s.indexers[t] = indexer
-	indexer.Run()
-	if old != nil {
-		old.Stop()
+func (s *KvStore) getOrCreateEntity(t StoreType) Entity {
+	v, err := s.entities.Fetch(t, func() (interface{}, error) {
+		cfg, ok := TypeConfig[t]
+		if !ok {
+			// do not new instance
+			return nil, ErrNoImpl
+		}
+
+		e := NewKvEntity(t.String(), cfg)
+		e.Run()
+		return e, nil
+	})
+	if err != nil {
+		util.Logger().Errorf(err, "can not find entity '%s', new default one", t.String())
+		return DefaultKvEntity()
+
 	}
+	return v.(Entity)
 }
 
 func (s *KvStore) Run() {
@@ -77,29 +85,17 @@ func (s *KvStore) Run() {
 }
 
 func (s *KvStore) store(ctx context.Context) {
-	defer s.wait(ctx)
-
-	if !core.ServerInfo.Config.EnableCache {
-		util.Logger().Warnf(nil, "registry cache mechanism is disabled")
-		return
-	}
-
-	for t := StoreType(0); t != typeEnd; t++ {
-		s.setupIndexer(t, NewCacheIndexer(t.String(), TypeConfig[t]))
-	}
-}
-
-func (s *KvStore) wait(ctx context.Context) {
-	for _, i := range s.indexers {
+	for i := range TypeConfig {
 		select {
 		case <-ctx.Done():
 			return
-		case <-i.Ready():
+		case <-s.Entities(i).Ready():
 		}
 	}
+
 	util.SafeCloseChan(s.ready)
 
-	util.Logger().Debugf("all indexers are ready")
+	util.Logger().Debugf("all entities are ready")
 }
 
 func (s *KvStore) closed() bool {
@@ -112,9 +108,10 @@ func (s *KvStore) Stop() {
 	}
 	s.isClose = true
 
-	for _, i := range s.indexers {
-		i.Stop()
-	}
+	s.entities.ForEach(func(item util.MapItem) bool {
+		item.Value.(Entity).Stop()
+		return true
+	})
 
 	s.taskService.Stop()
 
@@ -130,65 +127,65 @@ func (s *KvStore) Ready() <-chan struct{} {
 	return s.ready
 }
 
-func (s *KvStore) Service() Indexer {
-	return s.indexers[SERVICE]
-}
-
-func (s *KvStore) SchemaSummary() Indexer {
-	return s.indexers[SCHEMA_SUMMARY]
-}
-
-func (s *KvStore) Instance() Indexer {
-	return s.indexers[INSTANCE]
-}
-
-func (s *KvStore) Lease() Indexer {
-	return s.indexers[LEASE]
-}
-
-func (s *KvStore) ServiceIndex() Indexer {
-	return s.indexers[SERVICE_INDEX]
-}
-
-func (s *KvStore) ServiceAlias() Indexer {
-	return s.indexers[SERVICE_ALIAS]
-}
-
-func (s *KvStore) ServiceTag() Indexer {
-	return s.indexers[SERVICE_TAG]
-}
-
-func (s *KvStore) Rule() Indexer {
-	return s.indexers[RULE]
-}
-
-func (s *KvStore) RuleIndex() Indexer {
-	return s.indexers[RULE_INDEX]
-}
+func (s *KvStore) installType(e Extension) (id StoreType, err error) {
+	if e == nil {
+		return TypeError, errors.New("invalid parameter")
+	}
+	for _, n := range TypeNames {
+		if n == e.Name() {
+			return TypeError, fmt.Errorf("redeclare store type '%s'", n)
+		}
+	}
+	for _, r := range TypeConfig {
+		if r.Key == e.Config().Key {
+			return TypeError, fmt.Errorf("redeclare store root '%s'", r)
+		}
+	}
 
-func (s *KvStore) Schema() Indexer {
-	return s.indexers[SCHEMA]
-}
+	id = StoreType(len(TypeNames))
+	TypeNames = append(TypeNames, e.Name())
+	// config
+	TypeConfig[id] = e.Config()
+	// event proxy
+	NewEventProxy(id)
 
-func (s *KvStore) Dependency() Indexer {
-	return s.indexers[DEPENDENCY]
+	s.injectConfig(id)
+	return
 }
 
-func (s *KvStore) DependencyRule() Indexer {
-	return s.indexers[DEPENDENCY_RULE]
-}
+func (s *KvStore) Install(e Extension) (id StoreType, err error) {
+	if id, err = s.installType(e); err != nil {
+		return
+	}
 
-func (s *KvStore) DependencyQueue() Indexer {
-	return s.indexers[DEPENDENCY_QUEUE]
+	util.Logger().Infof("install new store entity %d:%s->%s", id, e.Name(), e.Config().Key)
+	return
 }
 
-func (s *KvStore) Domain() Indexer {
-	return s.indexers[DOMAIN]
+func (s *KvStore) MustInstall(e Extension) StoreType {
+	id, err := s.Install(e)
+	if err != nil {
+		panic(err)
+	}
+	return id
 }
 
-func (s *KvStore) Project() Indexer {
-	return s.indexers[PROJECT]
-}
+func (s *KvStore) Entities(id StoreType) Entity { return s.getOrCreateEntity(id) }
+func (s *KvStore) Service() Entity              { return s.Entities(SERVICE) }
+func (s *KvStore) SchemaSummary() Entity        { return s.Entities(SCHEMA_SUMMARY) }
+func (s *KvStore) Instance() Entity             { return s.Entities(INSTANCE) }
+func (s *KvStore) Lease() Entity                { return s.Entities(LEASE) }
+func (s *KvStore) ServiceIndex() Entity         { return s.Entities(SERVICE_INDEX) }
+func (s *KvStore) ServiceAlias() Entity         { return s.Entities(SERVICE_ALIAS) }
+func (s *KvStore) ServiceTag() Entity           { return s.Entities(SERVICE_TAG) }
+func (s *KvStore) Rule() Entity                 { return s.Entities(RULE) }
+func (s *KvStore) RuleIndex() Entity            { return s.Entities(RULE_INDEX) }
+func (s *KvStore) Schema() Entity               { return s.Entities(SCHEMA) }
+func (s *KvStore) Dependency() Entity           { return s.Entities(DEPENDENCY) }
+func (s *KvStore) DependencyRule() Entity       { return s.Entities(DEPENDENCY_RULE) }
+func (s *KvStore) DependencyQueue() Entity      { return s.Entities(DEPENDENCY_QUEUE) }
+func (s *KvStore) Domain() Entity               { return s.Entities(DOMAIN) }
+func (s *KvStore) Project() Entity              { return s.Entities(PROJECT) }
 
 func (s *KvStore) KeepAlive(ctx context.Context, opts ...registry.PluginOpOption) (int64, error) {
 	op := registry.OpPut(opts...)
@@ -213,29 +210,6 @@ func (s *KvStore) KeepAlive(ctx context.Context, opts ...registry.PluginOpOption
 	return pt.TTL, pt.Err()
 }
 
-func (s *KvStore) Entity(id StoreType) Indexer {
-	return s.indexers[id]
-}
-
-func (s *KvStore) Install(e Entity) (id StoreType, err error) {
-	if id, err = InstallType(e); err != nil {
-		return
-	}
-
-	util.Logger().Infof("install new store entity %d:%s->%s", id, e.Name(), e.Config().Prefix)
-
-	s.setupIndexer(id, NewCacheIndexer(id.String(), e.Config()))
-	return
-}
-
-func (s *KvStore) MustInstall(e Entity) StoreType {
-	id, err := s.Install(e)
-	if err != nil {
-		panic(err)
-	}
-	return id
-}
-
 func Store() *KvStore {
 	return store
 }
diff --git a/server/core/backend/store_test.go b/server/core/backend/store_test.go
new file mode 100644
index 00000000..7360d40a
--- /dev/null
+++ b/server/core/backend/store_test.go
@@ -0,0 +1,141 @@
+/*
+ * 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 backend
+
+import (
+	"errors"
+	"github.com/apache/incubator-servicecomb-service-center/pkg/task"
+	"github.com/apache/incubator-servicecomb-service-center/server/core"
+	"github.com/apache/incubator-servicecomb-service-center/server/infra/registry"
+	"golang.org/x/net/context"
+	"testing"
+)
+
+type mockAsyncTaskService struct {
+	Task task.Task
+}
+
+func (a *mockAsyncTaskService) Run()                   {}
+func (a *mockAsyncTaskService) Stop()                  {}
+func (a *mockAsyncTaskService) Ready() <-chan struct{} { return closedCh }
+func (a *mockAsyncTaskService) Add(ctx context.Context, task task.Task) error {
+	switch task.Key() {
+	case "LeaseAsyncTask_error":
+		return errors.New("error")
+	}
+	return nil
+}
+func (a *mockAsyncTaskService) LatestHandled(key string) (task.Task, error) {
+	switch a.Task.Key() {
+	case key:
+		return a.Task, nil
+	}
+	return nil, errors.New("error")
+}
+
+func TestStore(t *testing.T) {
+	s := &KvStore{}
+	s.Initialize()
+	e := s.Entities(999)
+	if e != DefaultKvEntity() {
+		t.Fatalf("TestStore failed")
+	}
+
+	core.ServerInfo.Config.EnableCache = false
+
+	s.store(context.Background())
+
+	tt := NewLeaseAsyncTask(registry.OpGet())
+	tt.TTL = 1
+	s.taskService = &mockAsyncTaskService{Task: tt}
+
+	<-s.Ready()
+
+	// KeepAlive case: add task error
+	ttl, err := s.KeepAlive(context.Background(), registry.WithKey([]byte("error")))
+	if err == nil || ttl > 0 {
+		t.Fatalf("TestStore failed")
+	}
+
+	// KeepAlive case: get last task error
+	tt.key = "LeaseAsyncTask_a"
+	ttl, err = s.KeepAlive(context.Background(), registry.WithKey([]byte("b")))
+	if err == nil || ttl > 0 {
+		t.Fatalf("TestStore failed")
+	}
+
+	// KeepAlive case: get last task error
+	tt.key = "LeaseAsyncTask_a"
+	ttl, err = s.KeepAlive(context.Background(), registry.WithKey([]byte("a")))
+	if err != nil || ttl != 1 {
+		t.Fatalf("TestStore failed")
+	}
+
+	core.ServerInfo.Config.EnableCache = true
+
+	s.Stop()
+}
+
+type extend struct {
+	evts []KvEvent
+}
+
+func (e *extend) Name() string {
+	return "test"
+}
+
+func (e *extend) Config() *Config {
+	return Configure().WithPrefix("/test").WithEventFunc(func(evt KvEvent) {
+		e.evts = append(e.evts, evt)
+	})
+}
+
+func TestInstallType(t *testing.T) {
+	s := &KvStore{}
+	s.Initialize()
+
+	// case: normal
+	e := &extend{}
+	id, err := s.Install(e)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if id == TypeError {
+		t.Fatal(err)
+	}
+	if id.String() != "test" {
+		t.Fatalf("TestInstallType failed")
+	}
+	if TypeConfig[id] == nil || TypeConfig[id].OnEvent == nil || EventProxy(id) == nil {
+		t.Fatal("installType fail", err)
+	}
+
+	// case: test event
+	h := &mockEventHandler{StoreType: id}
+	AddEventHandler(h)
+	TypeConfig[id].OnEvent(KvEvent{Revision: 1})
+	if len(e.evts) != 1 || e.evts[0].Revision != 1 || h.Evt.Revision != 1 || s.rev != 1 {
+		t.Fatalf("TestInstallType failed")
+	}
+
+	// case: install again
+	cfg := Configure().WithPrefix("/test")
+	id, err = s.Install(NewExtension("test", cfg))
+	if id != TypeError || err == nil {
+		t.Fatal("installType fail", err)
+	}
+}
diff --git a/server/core/backend/watcher.go b/server/core/backend/watcher.go
new file mode 100644
index 00000000..d0b13969
--- /dev/null
+++ b/server/core/backend/watcher.go
@@ -0,0 +1,26 @@
+/*
+ * 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 backend
+
+import (
+	"github.com/apache/incubator-servicecomb-service-center/server/infra/registry"
+)
+
+type Watcher interface {
+	EventBus() <-chan *registry.PluginResponse
+	Stop()
+}
diff --git a/server/core/backend/watcher_inner.go b/server/core/backend/watcher_inner.go
new file mode 100644
index 00000000..40ec466a
--- /dev/null
+++ b/server/core/backend/watcher_inner.go
@@ -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 backend
+
+import (
+	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
+	"github.com/apache/incubator-servicecomb-service-center/server/infra/registry"
+	"golang.org/x/net/context"
+	"sync"
+)
+
+type innerWatcher struct {
+	Cfg    ListWatchConfig
+	lw     ListWatch
+	bus    chan *registry.PluginResponse
+	stopCh chan struct{}
+	stop   bool
+	mux    sync.Mutex
+}
+
+func (w *innerWatcher) EventBus() <-chan *registry.PluginResponse {
+	return w.bus
+}
+
+func (w *innerWatcher) process(_ context.Context) {
+	stopCh := make(chan struct{})
+	ctx, cancel := context.WithTimeout(w.Cfg.Context, w.Cfg.Timeout)
+	util.Go(func(_ context.Context) {
+		defer close(stopCh)
+		w.lw.DoWatch(ctx, w.sendEvent)
+	})
+
+	select {
+	case <-stopCh:
+		// timed out or exception
+		w.Stop()
+	case <-w.stopCh:
+		cancel()
+	}
+}
+
+func (w *innerWatcher) sendEvent(resp *registry.PluginResponse) {
+	defer util.RecoverAndReport()
+	w.bus <- resp
+}
+
+func (w *innerWatcher) Stop() {
+	w.mux.Lock()
+	if w.stop {
+		w.mux.Unlock()
+		return
+	}
+	w.stop = true
+	close(w.stopCh)
+	close(w.bus)
+	w.mux.Unlock()
+}
+
+func newInnerWatcher(lw ListWatch, cfg ListWatchConfig) *innerWatcher {
+	w := &innerWatcher{
+		Cfg:    cfg,
+		lw:     lw,
+		bus:    make(chan *registry.PluginResponse, EVENT_BUS_MAX_SIZE),
+		stopCh: make(chan struct{}),
+	}
+	util.Go(w.process)
+	return w
+}
diff --git a/server/core/backend/watcher_test.go b/server/core/backend/watcher_test.go
new file mode 100644
index 00000000..6d8ce2b0
--- /dev/null
+++ b/server/core/backend/watcher_test.go
@@ -0,0 +1,86 @@
+/*
+ * 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 backend
+
+import (
+	"fmt"
+	"github.com/apache/incubator-servicecomb-service-center/server/infra/registry"
+	"golang.org/x/net/context"
+	"testing"
+	"time"
+)
+
+type mockWatcher struct {
+	lw *mockListWatch
+}
+
+func (w *mockWatcher) EventBus() <-chan *registry.PluginResponse {
+	return w.lw.Bus
+}
+func (w *mockWatcher) Stop() {
+}
+
+type mockListWatch struct {
+	ListResponse *registry.PluginResponse
+	Bus          chan *registry.PluginResponse
+	Watcher      Watcher
+	Rev          int64
+}
+
+func (lw *mockListWatch) List(op ListWatchConfig) (*registry.PluginResponse, error) {
+	if lw.ListResponse == nil {
+		return nil, fmt.Errorf("error")
+	}
+	lw.Rev = lw.ListResponse.Revision
+	return lw.ListResponse, nil
+}
+func (lw *mockListWatch) DoWatch(ctx context.Context, f func(*registry.PluginResponse)) error {
+	if lw.ListResponse == nil {
+		return fmt.Errorf("error")
+	}
+	if len(lw.ListResponse.Kvs) > 0 {
+		lw.Rev = lw.ListResponse.Kvs[0].ModRevision
+	}
+	f(lw.ListResponse)
+	<-ctx.Done()
+	return nil
+}
+func (lw *mockListWatch) Watch(op ListWatchConfig) Watcher {
+	return lw.Watcher
+}
+func (lw *mockListWatch) Revision() int64 {
+	return lw.Rev
+}
+
+func TestInnerWatcher_EventBus(t *testing.T) {
+	w := newInnerWatcher(&mockListWatch{}, ListWatchConfig{Timeout: time.Second, Context: context.Background()})
+	resp := <-w.EventBus()
+	if resp != nil {
+		t.Fatalf("TestInnerWatcher_EventBus failed")
+	}
+	w.Stop()
+
+	test := &registry.PluginResponse{
+		Action: registry.Put,
+	}
+	w = newInnerWatcher(&mockListWatch{ListResponse: test}, ListWatchConfig{Timeout: time.Second, Context: context.Background()})
+	resp = <-w.EventBus()
+	if resp == nil || resp.Action != registry.Put {
+		t.Fatalf("TestInnerWatcher_EventBus failed")
+	}
+	w.Stop()
+}
diff --git a/server/core/config.go b/server/core/config.go
index 81dae984..17151179 100644
--- a/server/core/config.go
+++ b/server/core/config.go
@@ -21,9 +21,17 @@ import (
 	"github.com/astaxie/beego"
 )
 
-var ServerInfo = newInfo()
+const (
+	INIT_VERSION = "0"
+)
+
+var ServerInfo = new(pb.ServerInformation)
+
+func Configure() {
+	*ServerInfo = newInfo()
+}
 
-func newInfo() *pb.ServerInformation {
+func newInfo() pb.ServerInformation {
 	maxLogFileSize := beego.AppConfig.DefaultInt64("log_rotate_size", 20)
 	if maxLogFileSize <= 0 || maxLogFileSize > 50 {
 		maxLogFileSize = 20
@@ -32,9 +40,9 @@ func newInfo() *pb.ServerInformation {
 	if maxLogBackupCount < 0 || maxLogBackupCount > 100 {
 		maxLogBackupCount = 50
 	}
-	return &pb.ServerInformation{
-		Version: "0",
-		Config: &pb.ServerConfig{
+	return pb.ServerInformation{
+		Version: INIT_VERSION,
+		Config: pb.ServerConfig{
 			MaxHeaderBytes: int64(beego.AppConfig.DefaultInt("max_header_bytes", 16384)),
 			MaxBodyBytes:   beego.AppConfig.DefaultInt64("max_body_bytes", 2097152),
 
diff --git a/server/core/core.go b/server/core/core.go
index 8749af62..0d5c2144 100644
--- a/server/core/core.go
+++ b/server/core/core.go
@@ -31,13 +31,11 @@ import (
 	"time"
 )
 
-func init() {
-	Initialize()
-}
-
 func Initialize() {
 	initCommandLine()
 
+	Configure()
+
 	plugin.SetPluginDir(ServerInfo.Config.PluginsDir)
 
 	initLogger()
diff --git a/server/core/proto/services.go b/server/core/proto/services.go
index 50f61e37..ff89bb34 100644
--- a/server/core/proto/services.go
+++ b/server/core/proto/services.go
@@ -75,7 +75,7 @@ type GovernServiceCtrlServerEx interface {
 }
 
 type MicroServiceDependency struct {
-	Dependency []*MicroServiceKey
+	Dependency []*MicroServiceKey `json:"Dependency,omitempty"`
 }
 
 type ServerConfig struct {
@@ -119,8 +119,8 @@ func (c *ServerConfig) LogPrint() {
 }
 
 type ServerInformation struct {
-	Version string        `json:"version"`
-	Config  *ServerConfig `json:"-"`
+	Version string       `json:"version"`
+	Config  ServerConfig `json:"-"`
 }
 
 func CreateResponse(code int32, message string) *Response {
diff --git a/server/core/swagger/v4.yaml b/server/core/swagger/v4.yaml
index f3a80d4f..eeccdaf8 100644
--- a/server/core/swagger/v4.yaml
+++ b/server/core/swagger/v4.yaml
@@ -310,7 +310,7 @@ paths:
             $ref: '#/definitions/Tags'
       tags:
         - microservices
-        - tag
+        - tags
       responses:
         200:
           description: 创建成功
@@ -343,7 +343,7 @@ paths:
           type: string
       tags:
         - microservices
-        - tag
+        - tags
       responses:
         200:
           description: 查询成功
@@ -388,7 +388,7 @@ paths:
           type: string
       tags:
         - microservices
-        - tag
+        - tags
       responses:
         200:
           description: 更新成功
@@ -426,7 +426,7 @@ paths:
           type: string
       tags:
         - microservices
-        - tag
+        - tags
       responses:
         200:
           description: 删除成功
@@ -465,7 +465,7 @@ paths:
             $ref: '#/definitions/AddRules'
       tags:
         - microservices
-        - rule
+        - rules
       responses:
         200:
           description: 创建成功
@@ -500,7 +500,7 @@ paths:
           type: string
       tags:
         - microservices
-        - rule
+        - rules
       responses:
         200:
           description: 查询成功
@@ -546,7 +546,7 @@ paths:
             $ref: "#/definitions/AddOrUpdateRule"
       tags:
         - microservices
-        - rule
+        - rules
       responses:
         200:
           description: 修改成功
@@ -586,7 +586,7 @@ paths:
           type: string
       tags:
         - microservices
-        - rule
+        - rules
       responses:
         200:
           description: 删除成功
@@ -626,7 +626,7 @@ paths:
           type: string
       tags:
         - microservices
-        - schema
+        - schemas
       responses:
         200:
           description: 查询成功,如果summary存在,则header里面的X-Schema-Summary的value为该schema对应的摘要
@@ -675,7 +675,7 @@ paths:
             $ref: '#/definitions/CreateSchema'
       tags:
         - microservices
-        - schema
+        - schemas
       responses:
         200:
           description: 修改成功
@@ -713,7 +713,7 @@ paths:
           type: string
       tags:
         - microservices
-        - schema
+        - schemas
       responses:
         200:
           description: 删除成功
@@ -752,7 +752,7 @@ paths:
             $ref: '#/definitions/ModifySchemasRequest'
       tags:
         - microservices
-        - schema
+        - schemas
       responses:
         200:
           description: 創建成功
@@ -789,7 +789,7 @@ paths:
           default: 0
       tags:
         - microservices
-        - schema
+        - schemas
       responses:
         200:
           description: 查询成功
@@ -824,7 +824,7 @@ paths:
           schema:
             $ref: '#/definitions/CreateDependenciesRequest'
       tags:
-        - dependency
+        - dependencies
       responses:
         200:
           description: 创建成功
@@ -858,7 +858,7 @@ paths:
           schema:
             $ref: '#/definitions/CreateDependenciesRequest'
       tags:
-        - dependency
+        - dependencies
       responses:
         200:
           description: 创建成功
@@ -902,7 +902,7 @@ paths:
           type: integer
           default: 0
       tags:
-        - dependency
+        - dependencies
       responses:
         200:
           description: 查询成功
@@ -946,7 +946,7 @@ paths:
           type: integer
           default: 0
       tags:
-        - dependency
+        - dependencies
       responses:
         200:
           description: 查询成功
@@ -1622,6 +1622,34 @@ paths:
           description: 内部错误
           schema:
             type: string
+  /v4/{project}/admin/dump:
+    get:
+      description: |
+        Dump the information of service center runtime
+      operationId: dump
+      parameters:
+        - name: x-domain-name
+          in: header
+          type: string
+          default: default
+          description: default租户
+          required: true
+        - name: project
+          in: path
+          description: default项目
+          required: true
+          type: string
+      tags:
+        - admin
+      responses:
+        200:
+          description: dump information
+          schema:
+            $ref: '#/definitions/DumpResponse'
+        403:
+          description: Forbidden
+          schema:
+            type: string
 definitions:
   Version:
     type: object
@@ -2224,3 +2252,104 @@ definitions:
        schema:
          description: shema
          type: string
+  MicroServiceKV:
+    type: object
+    properties:
+      key:
+        type: string
+      rev:
+        type: integer
+      value:
+        $ref: "#/definitions/MicroService"
+  MicroServiceInstanceKV:
+    type: object
+    properties:
+      key:
+        type: string
+      rev:
+        type: integer
+      value:
+        $ref: "#/definitions/MicroServiceInstance"
+  StringKV:
+    type: object
+    properties:
+      key:
+        type: string
+      rev:
+        type: integer
+      value:
+        type: string
+  MicroserviceTagKV:
+    type: object
+    properties:
+      key:
+        type: string
+      rev:
+        type: integer
+      value:
+        $ref: '#/definitions/Properties'
+  MicroserviceRuleKV:
+    type: object
+    properties:
+      key:
+        type: string
+      rev:
+        type: integer
+      value:
+        $ref: '#/definitions/Rule'
+  MicroServiceDependencyRule:
+    type: object
+    properties:
+      Dependency:
+        type: array
+        items:
+          $ref: '#/definitions/DependencyKey'
+  DependencyKV:
+    type: object
+    properties:
+      key:
+        type: string
+      rev:
+        type: integer
+      value:
+        $ref: '#/definitions/MicroServiceDependencyRule'
+  Cache:
+    type: object
+    properties:
+      services:
+        type: array
+        items:
+          $ref: "#/definitions/MicroServiceKV"
+      serviceIndexes:
+        type: array
+        items:
+          $ref: "#/definitions/StringKV"
+      serviceAliases:
+        type: array
+        items:
+          $ref: "#/definitions/StringKV"
+      serviceTags:
+        type: array
+        items:
+          $ref: "#/definitions/MicroserviceTagKV"
+      serviceRules:
+        type: array
+        items:
+          $ref: "#/definitions/MicroserviceRuleKV"
+      dependencyRules:
+        type: array
+        items:
+          $ref: "#/definitions/DependencyKV"
+      summaries:
+        type: array
+        items:
+          $ref: "#/definitions/StringKV"
+      instances:
+        type: array
+        items:
+          $ref: "#/definitions/MicroServiceInstanceKV"
+  DumpResponse:
+    type: object
+    properties:
+      cache:
+        $ref: "#/definitions/Cache"
diff --git a/server/error/error.go b/server/error/error.go
index 163d67d7..6b352b7c 100644
--- a/server/error/error.go
+++ b/server/error/error.go
@@ -53,6 +53,8 @@ var errors = map[int32]string{
 	ErrUnavailableQuota:   "Quota service is unavailable",
 
 	ErrEndpointAlreadyExists: "Endpoint is already belong to other service",
+
+	ErrForbidden: "Forbidden",
 }
 
 const (
@@ -90,6 +92,8 @@ const (
 
 	ErrNotEnoughQuota   int32 = 400100
 	ErrUnavailableQuota int32 = 500101
+
+	ErrForbidden int32 = 403001
 )
 
 type Error struct {
diff --git a/server/govern/controller_v4.go b/server/govern/controller_v4.go
index 46583287..93d29cc2 100644
--- a/server/govern/controller_v4.go
+++ b/server/govern/controller_v4.go
@@ -43,40 +43,6 @@ func (governService *GovernServiceControllerV4) URLPatterns() []rest.Route {
 	}
 }
 
-//Node 节点信息
-type Node struct {
-	Id       string   `json:"id"`
-	Name     string   `json:"name"`
-	AppID    string   `json:"appId"`
-	Version  string   `json:"version"`
-	Type     string   `json:"type"`
-	Color    string   `json:"color"`
-	Position string   `json:"position"`
-	Visits   []string `json:"-"`
-}
-
-//Line 连接线信息
-type Line struct {
-	From        Node   `json:"from"`
-	To          Node   `json:"to"`
-	Type        string `json:"type"`
-	Color       string `json:"color"`
-	Description string `json:"descriptor"`
-}
-
-//Circle 环信息
-type Circle struct {
-	Nodes []Node `json:"nodes"`
-}
-
-//Graph 图全集信息
-type Graph struct {
-	Nodes   []Node   `json:"nodes"`
-	Lines   []Line   `json:"lines"`
-	Circles []Circle `json:"circles"`
-	Visits  []string `json:"-"`
-}
-
 // GetGraph 获取依赖连接图详细依赖关系
 func (governService *GovernServiceControllerV4) GetGraph(w http.ResponseWriter, r *http.Request) {
 	var graph Graph
diff --git a/server/govern/govern.go b/server/govern/govern.go
index 019b2121..30c45ec8 100644
--- a/server/govern/govern.go
+++ b/server/govern/govern.go
@@ -36,6 +36,6 @@ func registerGRPC() {
 }
 
 func registerREST() {
-	roa.RegisterServent(&GovernServiceControllerV3{})
-	roa.RegisterServent(&GovernServiceControllerV4{})
+	roa.RegisterServant(&GovernServiceControllerV3{})
+	roa.RegisterServant(&GovernServiceControllerV4{})
 }
diff --git a/server/govern/govern_suite_test.go b/server/govern/govern_suite_test.go
index 8a71d2ee..a5c38b50 100644
--- a/server/govern/govern_suite_test.go
+++ b/server/govern/govern_suite_test.go
@@ -16,13 +16,14 @@
  */
 package govern_test
 
+// initialize
+import _ "github.com/apache/incubator-servicecomb-service-center/server/init"
+import _ "github.com/apache/incubator-servicecomb-service-center/server/bootstrap"
 import (
 	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
+	"github.com/apache/incubator-servicecomb-service-center/server/core"
 	pb "github.com/apache/incubator-servicecomb-service-center/server/core/proto"
 	"github.com/apache/incubator-servicecomb-service-center/server/govern"
-	_ "github.com/apache/incubator-servicecomb-service-center/server/plugin/infra/quota/buildin"
-	_ "github.com/apache/incubator-servicecomb-service-center/server/plugin/infra/registry/etcd"
-	_ "github.com/apache/incubator-servicecomb-service-center/server/plugin/infra/uuid/buildin"
 	"github.com/apache/incubator-servicecomb-service-center/server/service"
 	. "github.com/onsi/ginkgo"
 	"github.com/onsi/ginkgo/reporters"
@@ -37,13 +38,11 @@ func TestGovern(t *testing.T) {
 	RunSpecsWithDefaultAndCustomReporters(t, "model Suite", []Reporter{junitReporter})
 }
 
-var serviceResource pb.ServiceCtrlServer
-var instanceResource pb.SerivceInstanceCtrlServerEx
 var governService pb.GovernServiceCtrlServerEx
 
 var _ = BeforeSuite(func() {
 	//init plugin
-	serviceResource, instanceResource = service.AssembleResources()
+	core.ServiceAPI, core.InstanceAPI = service.AssembleResources()
 	governService = govern.GovernServiceAPI
 })
 
diff --git a/server/govern/graph.go b/server/govern/graph.go
new file mode 100644
index 00000000..d8546ed5
--- /dev/null
+++ b/server/govern/graph.go
@@ -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 govern
+
+//Node 节点信息
+type Node struct {
+	Id       string   `json:"id"`
+	Name     string   `json:"name"`
+	AppID    string   `json:"appId"`
+	Version  string   `json:"version"`
+	Type     string   `json:"type"`
+	Color    string   `json:"color"`
+	Position string   `json:"position"`
+	Visits   []string `json:"-"`
+}
+
+//Line 连接线信息
+type Line struct {
+	From        Node   `json:"from"`
+	To          Node   `json:"to"`
+	Type        string `json:"type"`
+	Color       string `json:"color"`
+	Description string `json:"descriptor"`
+}
+
+//Circle 环信息
+type Circle struct {
+	Nodes []Node `json:"nodes"`
+}
+
+//Graph 图全集信息
+type Graph struct {
+	Nodes   []Node   `json:"nodes"`
+	Lines   []Line   `json:"lines"`
+	Circles []Circle `json:"circles"`
+	Visits  []string `json:"-"`
+}
diff --git a/server/govern/service.go b/server/govern/service.go
index fbc19de7..247a35bf 100644
--- a/server/govern/service.go
+++ b/server/govern/service.go
@@ -221,8 +221,11 @@ func (governService *GovernService) GetApplications(ctx context.Context, in *pb.
 }
 
 func getServiceAllVersions(ctx context.Context, serviceKey *pb.MicroServiceKey) ([]string, error) {
-	versions := []string{}
-	key := apt.GenerateServiceIndexKey(serviceKey)
+	var versions []string
+
+	copyKey := *serviceKey
+	copyKey.Version = ""
+	key := apt.GenerateServiceIndexKey(&copyKey)
 
 	opts := append(serviceUtil.FromContext(ctx),
 		registry.WithStrKey(key),
diff --git a/server/govern/service_test.go b/server/govern/service_test.go
index f0008d43..f4900d2f 100644
--- a/server/govern/service_test.go
+++ b/server/govern/service_test.go
@@ -17,11 +17,25 @@
 package govern_test
 
 import (
+	"bytes"
+	"github.com/apache/incubator-servicecomb-service-center/server/core"
 	pb "github.com/apache/incubator-servicecomb-service-center/server/core/proto"
+	"github.com/apache/incubator-servicecomb-service-center/server/govern"
 	. "github.com/onsi/ginkgo"
 	. "github.com/onsi/gomega"
+	"io/ioutil"
+	"net/http"
+	"net/http/httptest"
 )
 
+type mockGovernHandler struct {
+	Func func(w http.ResponseWriter, r *http.Request)
+}
+
+func (m *mockGovernHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	m.Func(w, r)
+}
+
 var _ = Describe("'Govern' service", func() {
 	Describe("execute 'get all' operation", func() {
 		Context("when get all services", func() {
@@ -63,6 +77,38 @@ var _ = Describe("'Govern' service", func() {
 				Expect(resp.Response.Code).To(Equal(pb.Response_SUCCESS))
 			})
 		})
+
+		Context("when get top graph", func() {
+			It("should be passed", func() {
+				respC, err := core.ServiceAPI.Create(getContext(), &pb.CreateServiceRequest{
+					Service: &pb.MicroService{
+						AppId:       "govern_service_group",
+						ServiceName: "govern_service_graph",
+						Version:     "1.0.0",
+						Level:       "FRONT",
+						Status:      pb.MS_UP,
+					},
+				})
+				Expect(err).To(BeNil())
+				Expect(respC.Response.Code).To(Equal(pb.Response_SUCCESS))
+
+				svr := httptest.NewServer(&mockGovernHandler{func(w http.ResponseWriter, r *http.Request) {
+					ctrl := &govern.GovernServiceControllerV4{}
+					ctrl.GetGraph(w, r.WithContext(getContext()))
+				}})
+				defer svr.Close()
+
+				resp, err := http.Get(svr.URL)
+				Expect(err).To(BeNil())
+
+				body, err := ioutil.ReadAll(resp.Body)
+				resp.Body.Close()
+				Expect(err).To(BeNil())
+
+				Expect(string(body)).ToNot(Equal(""))
+				Expect(string(body)).ToNot(Equal("{}"))
+			})
+		})
 	})
 
 	Describe("execute 'get detail' operation", func() {
@@ -71,7 +117,7 @@ var _ = Describe("'Govern' service", func() {
 		)
 
 		It("should be passed", func() {
-			resp, err := serviceResource.Create(getContext(), &pb.CreateServiceRequest{
+			resp, err := core.ServiceAPI.Create(getContext(), &pb.CreateServiceRequest{
 				Service: &pb.MicroService{
 					AppId:       "govern_service_group",
 					ServiceName: "govern_service_name",
@@ -84,7 +130,7 @@ var _ = Describe("'Govern' service", func() {
 			Expect(resp.Response.Code).To(Equal(pb.Response_SUCCESS))
 			serviceId = resp.ServiceId
 
-			serviceResource.ModifySchema(getContext(), &pb.ModifySchemaRequest{
+			core.ServiceAPI.ModifySchema(getContext(), &pb.ModifySchemaRequest{
 				ServiceId: serviceId,
 				SchemaId:  "schemaId",
 				Schema:    "detail",
@@ -92,7 +138,7 @@ var _ = Describe("'Govern' service", func() {
 			Expect(err).To(BeNil())
 			Expect(resp.Response.Code).To(Equal(pb.Response_SUCCESS))
 
-			instanceResource.Register(getContext(), &pb.RegisterInstanceRequest{
+			core.InstanceAPI.Register(getContext(), &pb.RegisterInstanceRequest{
 				Instance: &pb.MicroServiceInstance{
 					ServiceId: serviceId,
 					Endpoints: []string{
@@ -124,7 +170,7 @@ var _ = Describe("'Govern' service", func() {
 				Expect(err).To(BeNil())
 				Expect(respGetServiceDetail.Response.Code).To(Equal(pb.Response_SUCCESS))
 
-				respDelete, err := serviceResource.Delete(getContext(), &pb.DeleteServiceRequest{
+				respDelete, err := core.ServiceAPI.Delete(getContext(), &pb.DeleteServiceRequest{
 					ServiceId: serviceId,
 					Force:     true,
 				})
@@ -165,4 +211,27 @@ var _ = Describe("'Govern' service", func() {
 			})
 		})
 	})
+
+	Describe("execute all operations", func() {
+		Context("when request is valid", func() {
+			It("should be passed", func() {
+				var num int
+				ctrl := &govern.GovernServiceControllerV4{}
+				svr := httptest.NewServer(&mockGovernHandler{func(w http.ResponseWriter, r *http.Request) {
+					defer func() {
+						Expect(recover()).To(BeNil())
+					}()
+					route := ctrl.URLPatterns()[num]
+					r.Method = route.Method
+					route.Func(w, r)
+					num++
+				}})
+				defer svr.Close()
+
+				for range ctrl.URLPatterns() {
+					http.Post(svr.URL, "application/json", bytes.NewBuffer([]byte("{}")))
+				}
+			})
+		})
+	})
 })
diff --git a/server/infra/quota/quota.go b/server/infra/quota/quota.go
index 97befc3d..bb37a45d 100644
--- a/server/infra/quota/quota.go
+++ b/server/infra/quota/quota.go
@@ -17,9 +17,9 @@
 package quota
 
 import (
-	"fmt"
 	scerr "github.com/apache/incubator-servicecomb-service-center/server/error"
 	"golang.org/x/net/context"
+	"strconv"
 )
 
 var (
@@ -90,6 +90,6 @@ func (r ResourceType) String() string {
 	case MicroServiceInstanceQuotaType:
 		return "INSTANCE"
 	default:
-		return "RESOURCE" + fmt.Sprint(r)
+		return "RESOURCE" + strconv.Itoa(int(r))
 	}
 }
diff --git a/server/infra/registry/registry.go b/server/infra/registry/registry.go
index 885a6549..62adac54 100644
--- a/server/infra/registry/registry.go
+++ b/server/infra/registry/registry.go
@@ -19,25 +19,51 @@ package registry
 import (
 	"bytes"
 	"fmt"
+	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
+	"github.com/apache/incubator-servicecomb-service-center/server/core"
 	"github.com/astaxie/beego"
 	"github.com/coreos/etcd/mvcc/mvccpb"
 	"golang.org/x/net/context"
 	"strconv"
+	"strings"
+	"sync"
 	"time"
-	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
 )
 
-var defaultRegistryConfig Config
+const (
+	// the timeout dial to etcd
+	defaultDialTimeout    = 10 * time.Second
+	defaultRequestTimeout = 30 * time.Second
+)
+
+var (
+	defaultRegistryConfig Config
+	once                  sync.Once
+)
 
-func init() {
-	defaultRegistryConfig.ClusterAddresses = beego.AppConfig.DefaultString("manager_cluster", "sc-0=http://127.0.0.1:2380")
-	requestTimeConfig := beego.AppConfig.DefaultString("registry_timeout", "30s")
-	var err error
-	defaultRegistryConfig.RequestTimeOut, err = time.ParseDuration(requestTimeConfig)
-	if err != nil {
-	    util.Logger().Errorf(err, "registry_timeout is invaild, use default time 30s")
-	    defaultRegistryConfig.RequestTimeOut, _ = time.ParseDuration("30s")
-	}
+func RegistryConfig() *Config {
+	once.Do(func() {
+		var err error
+
+		defaultRegistryConfig.ClusterAddresses = beego.AppConfig.DefaultString("manager_cluster", "http://127.0.0.1:2379")
+		defaultRegistryConfig.DialTimeout, err = time.ParseDuration(beego.AppConfig.DefaultString("registry_timeout", "30s"))
+		if err != nil {
+			util.Logger().Errorf(err, "connect_timeout is invalid, use default time %s", defaultDialTimeout)
+			defaultRegistryConfig.DialTimeout = defaultDialTimeout
+		}
+		defaultRegistryConfig.RequestTimeOut, err = time.ParseDuration(beego.AppConfig.DefaultString("registry_timeout", "30s"))
+		if err != nil {
+			util.Logger().Errorf(err, "registry_timeout is invalid, use default time %s", defaultRequestTimeout)
+			defaultRegistryConfig.RequestTimeOut = defaultRequestTimeout
+		}
+		defaultRegistryConfig.SslEnabled = core.ServerInfo.Config.SslEnabled &&
+			strings.Index(strings.ToLower(defaultRegistryConfig.ClusterAddresses), "https://") >= 0
+		defaultRegistryConfig.AutoSyncInterval, err = time.ParseDuration(core.ServerInfo.Config.AutoSyncInterval)
+		if err != nil {
+			util.Logger().Errorf(err, "auto_sync_interval is invalid")
+		}
+	})
+	return &defaultRegistryConfig
 }
 
 type ActionType int
@@ -176,9 +202,12 @@ type Registry interface {
 }
 
 type Config struct {
+	SslEnabled       bool
 	EmbedMode        string
 	ClusterAddresses string
+	DialTimeout      time.Duration
 	RequestTimeOut   time.Duration
+	AutoSyncInterval time.Duration
 }
 
 type PluginOp struct {
@@ -377,7 +406,3 @@ func OpCmp(opt CompareOperation, result CompareResult, v interface{}) (cmp Compa
 func WithTimeout(ctx context.Context) (context.Context, context.CancelFunc) {
 	return context.WithTimeout(ctx, defaultRegistryConfig.RequestTimeOut)
 }
-
-func RegistryConfig() *Config {
-	return &defaultRegistryConfig
-}
diff --git a/pkg/async/task.go b/server/init/init.go
similarity index 86%
rename from pkg/async/task.go
rename to server/init/init.go
index f4a884f3..f242c27d 100644
--- a/pkg/async/task.go
+++ b/server/init/init.go
@@ -14,12 +14,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package async
+package init
 
-import "golang.org/x/net/context"
+import "github.com/apache/incubator-servicecomb-service-center/server/core"
 
-type Task interface {
-	Key() string
-	Do(ctx context.Context) error
-	Err() error
+func init() {
+	core.Initialize()
 }
diff --git a/server/interceptor/interceptors_test.go b/server/interceptor/interceptors_test.go
new file mode 100644
index 00000000..4a494206
--- /dev/null
+++ b/server/interceptor/interceptors_test.go
@@ -0,0 +1,62 @@
+/*
+ * 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 interceptor
+
+import (
+	"errors"
+	"net/http"
+	"net/http/httptest"
+	"testing"
+)
+
+var i = 0
+
+func mockFunc(w http.ResponseWriter, r *http.Request) error {
+	switch i {
+	case 1:
+		return errors.New("error")
+	case 0:
+		panic(errors.New("panic"))
+	default:
+		i++
+	}
+	return nil
+}
+
+func TestInterception_Invoke(t *testing.T) {
+	RegisterInterceptFunc(mockFunc)
+	RegisterInterceptFunc(mockFunc)
+	RegisterInterceptFunc(mockFunc)
+
+	i = 1
+	err := InvokeInterceptors(nil, nil)
+	if err == nil || err.Error() != "error" {
+		t.Fatalf("TestInterception_Invoke failed")
+	}
+
+	i = 0
+	err = InvokeInterceptors(httptest.NewRecorder(), nil)
+	if err == nil || err.Error() != "panic" {
+		t.Fatalf("TestInterception_Invoke failed")
+	}
+
+	i = 2
+	err = InvokeInterceptors(nil, nil)
+	if err != nil || i != 2+len(interceptors) {
+		t.Fatalf("TestInterception_Invoke failed")
+	}
+}
diff --git a/server/main.go b/server/main.go
new file mode 100644
index 00000000..67807f6c
--- /dev/null
+++ b/server/main.go
@@ -0,0 +1,23 @@
+/*
+ * 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 server
+
+var server ServiceCenterServer
+
+func Run() {
+	server.Run()
+}
diff --git a/pkg/util/metric.go b/server/metric/calculator.go
similarity index 70%
rename from pkg/util/metric.go
rename to server/metric/calculator.go
index 2a805298..8b163c6d 100644
--- a/pkg/util/metric.go
+++ b/server/metric/calculator.go
@@ -14,14 +14,28 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package util
+package metric
 
 import (
 	dto "github.com/prometheus/client_model/go"
+	"strings"
 )
 
+var (
+	calculators       = make(map[string]Calculator)
+	DefaultCalculator = &CommonCalculator{}
+)
+
+type Calculator interface {
+	Calc(mf *dto.MetricFamily) float64
+	ReShape()
+}
+
+type CommonCalculator struct {
+}
+
 // Get value of metricFamily
-func MetricValueOf(mf *dto.MetricFamily) float64 {
+func (c *CommonCalculator) Calc(mf *dto.MetricFamily) float64 {
 	if len(mf.GetMetric()) == 0 {
 		return 0
 	}
@@ -38,6 +52,9 @@ func MetricValueOf(mf *dto.MetricFamily) float64 {
 	}
 }
 
+func (c *CommonCalculator) ReShape() {
+}
+
 func metricCounterOf(m []*dto.Metric) float64 {
 	var sum float64 = 0
 	for _, d := range m {
@@ -62,3 +79,20 @@ func metricSummaryOf(m []*dto.Metric) float64 {
 
 	return sum / float64(count)
 }
+
+func RegisterCalculator(family string, c Calculator) {
+	calculators[family] = c
+}
+
+func Calculate(mf *dto.MetricFamily) float64 {
+	if c, ok := calculators[strings.TrimPrefix(mf.GetName(), familyNamePrefix)]; ok {
+		return c.Calc(mf)
+	}
+	return DefaultCalculator.Calc(mf)
+}
+
+func ReShape() {
+	for _, c := range calculators {
+		c.ReShape()
+	}
+}
diff --git a/server/metric/common.go b/server/metric/common.go
index 5e423ada..b313cd38 100644
--- a/server/metric/common.go
+++ b/server/metric/common.go
@@ -16,17 +16,44 @@
  */
 package metric
 
-import "time"
+import (
+	"github.com/astaxie/beego"
+	"net"
+	"sync"
+	"time"
+)
 
 const (
-	defaultMetricsSize = 100
-	collectInterval    = 5 * time.Second
+	collectInterval  = 5 * time.Second
+	FamilyName       = "service_center"
+	familyNamePrefix = FamilyName + "_"
+)
 
-	familyName = "service_center_"
+var (
+	sysMetricNames = map[string]struct{}{
+		"process_resident_memory_bytes": {},
+		"process_cpu_seconds_total":     {},
+		"go_threads":                    {},
+	}
+	getEndpointOnce sync.Once
+	instance        string
 )
 
-var sysMetricNames = map[string]struct{}{
-	"process_resident_memory_bytes": {},
-	"process_cpu_seconds_total":     {},
-	"go_threads":                    {},
+func InstanceName() string {
+	getEndpointOnce.Do(func() {
+		restIp := beego.AppConfig.String("httpaddr")
+		restPort := beego.AppConfig.String("httpport")
+		if len(restIp) > 0 {
+			instance = net.JoinHostPort(restIp, restPort)
+			return
+		}
+
+		rpcIp := beego.AppConfig.String("rpcaddr")
+		rpcPort := beego.AppConfig.String("rpcport")
+		if len(rpcIp) > 0 {
+			instance = net.JoinHostPort(rpcIp, rpcPort)
+			return
+		}
+	})
+	return instance
 }
diff --git a/server/metric/gatherer.go b/server/metric/gatherer.go
index 7140909e..72a39471 100644
--- a/server/metric/gatherer.go
+++ b/server/metric/gatherer.go
@@ -82,9 +82,10 @@ func (mm *MetricsGatherer) Collect() error {
 
 	for _, mf := range mfs {
 		name := mf.GetName()
-		if _, ok := sysMetricNames[name]; strings.Index(name, familyName) == 0 || ok {
-			mm.Records.Put(strings.TrimPrefix(name, familyName), util.MetricValueOf(mf))
+		if _, ok := sysMetricNames[name]; strings.Index(name, familyNamePrefix) == 0 || ok {
+			mm.Records.Put(strings.TrimPrefix(name, familyNamePrefix), Calculate(mf))
 		}
 	}
+	ReShape()
 	return nil
 }
diff --git a/server/metric/metrics.go b/server/metric/metrics.go
index d78412ae..0afa93cf 100644
--- a/server/metric/metrics.go
+++ b/server/metric/metrics.go
@@ -20,7 +20,7 @@ import "github.com/apache/incubator-servicecomb-service-center/pkg/util"
 
 func NewMetrics() *Metrics {
 	return &Metrics{
-		ConcurrentMap: util.NewConcurrentMap(defaultMetricsSize),
+		ConcurrentMap: util.NewConcurrentMap(0),
 	}
 }
 
diff --git a/server/metrics.go b/server/metrics.go
new file mode 100644
index 00000000..57ec601f
--- /dev/null
+++ b/server/metrics.go
@@ -0,0 +1,41 @@
+/*
+ * 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 server
+
+import (
+	"github.com/apache/incubator-servicecomb-service-center/server/metric"
+	"github.com/prometheus/client_golang/prometheus"
+)
+
+var (
+	scCounter = prometheus.NewCounterVec(
+		prometheus.CounterOpts{
+			Namespace: metric.FamilyName,
+			Subsystem: "db",
+			Name:      "sc_total",
+			Help:      "Counter of the Service Center instance",
+		}, []string{"instance"})
+)
+
+func init() {
+	prometheus.MustRegister(scCounter)
+}
+
+func ReportScInstance() {
+	instance := metric.InstanceName()
+	scCounter.WithLabelValues(instance).Add(1)
+}
diff --git a/server/plugin/infra/registry/embededetcd/embededetcd.go b/server/plugin/infra/registry/embededetcd/embededetcd.go
index 3c115331..fd0a174e 100644
--- a/server/plugin/infra/registry/embededetcd/embededetcd.go
+++ b/server/plugin/infra/registry/embededetcd/embededetcd.go
@@ -22,7 +22,6 @@ import (
 	"fmt"
 	errorsEx "github.com/apache/incubator-servicecomb-service-center/pkg/errors"
 	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
-	"github.com/apache/incubator-servicecomb-service-center/server/core"
 	"github.com/apache/incubator-servicecomb-service-center/server/infra/registry"
 	mgr "github.com/apache/incubator-servicecomb-service-center/server/plugin"
 	"github.com/astaxie/beego"
@@ -40,8 +39,6 @@ import (
 
 var embedTLSConfig *tls.Config
 
-const START_MANAGER_SERVER_TIMEOUT = 10 * time.Second
-
 func init() {
 	mgr.RegisterPlugin(mgr.Plugin{mgr.REGISTRY, "embeded_etcd", getEmbedInstance})
 }
@@ -457,7 +454,7 @@ func (s *EtcdEmbed) Watch(ctx context.Context, opts ...registry.PluginOpOption)
 }
 
 func (s *EtcdEmbed) readyNotify() {
-	timeout := START_MANAGER_SERVER_TIMEOUT
+	timeout := registry.RegistryConfig().DialTimeout
 	select {
 	case <-s.Embed.Server.ReadyNotify():
 		close(s.ready)
@@ -509,7 +506,7 @@ func getEmbedInstance() mgr.PluginInstance {
 	util.Logger().Warnf(nil, "starting service center in embed mode")
 
 	hostName := beego.AppConfig.DefaultString("manager_name", "sc-0")
-	addrs := beego.AppConfig.DefaultString("manager_addr", "http://127.0.0.1:2380")
+	mgrAddrs := beego.AppConfig.DefaultString("manager_addr", "http://127.0.0.1:2380")
 
 	inst := &EtcdEmbed{
 		err:       make(chan error, 1),
@@ -517,7 +514,7 @@ func getEmbedInstance() mgr.PluginInstance {
 		goroutine: util.NewGo(context.Background()),
 	}
 
-	if core.ServerInfo.Config.SslEnabled {
+	if registry.RegistryConfig().SslEnabled {
 		var err error
 		embedTLSConfig, err = mgr.Plugins().TLS().ServerConfig()
 		if err != nil {
@@ -528,7 +525,7 @@ func getEmbedInstance() mgr.PluginInstance {
 	}
 
 	serverCfg := embed.NewConfig()
-	// TODO 不支持加密的TLS证书 ? managerTLSConfig
+	// TODO 不支持使用TLS通信
 	// 存储目录,相对于工作目录
 	serverCfg.Dir = "data"
 
@@ -536,8 +533,8 @@ func getEmbedInstance() mgr.PluginInstance {
 	serverCfg.Name = hostName
 	serverCfg.InitialCluster = registry.RegistryConfig().ClusterAddresses
 
-	// 管理端口
-	urls, err := parseURL(addrs)
+	// 1. 管理端口
+	urls, err := parseURL(mgrAddrs)
 	if err != nil {
 		util.Logger().Error(`"manager_addr" field configure error`, err)
 		inst.err <- err
@@ -545,14 +542,14 @@ func getEmbedInstance() mgr.PluginInstance {
 	}
 	serverCfg.LPUrls = urls
 	serverCfg.APUrls = urls
-	util.Logger().Debugf("--initial-cluster %s --initial-advertise-peer-urls %s --listen-peer-urls %s",
-		serverCfg.InitialCluster, addrs, addrs)
 
-	// 业务端口,关闭默认2379端口
-	// clients := beego.AppConfig.String("clientcluster")
+	// 2. 业务端口,关闭默认2379端口
 	serverCfg.LCUrls = nil
 	serverCfg.ACUrls = nil
 
+	util.Logger().Debugf("--initial-cluster %s --initial-advertise-peer-urls %s --listen-peer-urls %s",
+		serverCfg.InitialCluster, mgrAddrs, mgrAddrs)
+
 	// 自动压缩历史, 1 hour
 	serverCfg.AutoCompactionRetention = 1
 
diff --git a/server/plugin/infra/registry/etcd/etcd.go b/server/plugin/infra/registry/etcd/etcd.go
index db9bc898..5127db62 100644
--- a/server/plugin/infra/registry/etcd/etcd.go
+++ b/server/plugin/infra/registry/etcd/etcd.go
@@ -22,7 +22,6 @@ import (
 	"fmt"
 	errorsEx "github.com/apache/incubator-servicecomb-service-center/pkg/errors"
 	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
-	"github.com/apache/incubator-servicecomb-service-center/server/core"
 	"github.com/apache/incubator-servicecomb-service-center/server/infra/registry"
 	mgr "github.com/apache/incubator-servicecomb-service-center/server/plugin"
 	"github.com/coreos/etcd/clientv3"
@@ -31,52 +30,143 @@ import (
 	"golang.org/x/net/context"
 	"google.golang.org/grpc"
 	"net/url"
+	"strconv"
 	"strings"
 	"time"
 )
 
 const (
-	// the timeout dial to etcd
-	connectRegistryServerTimeout = 10 * time.Second
 	// here will new an etcd connection after about 30s(=5s * 3 + (backoff:8s))
 	// when the connected etcd member was hung but tcp is still alive
 	healthCheckTimeout    = 5 * time.Second
 	healthCheckRetryTimes = 3
 )
 
-var (
-	clientTLSConfig *tls.Config
-	firstEndpoint   string
-)
+var firstEndpoint string
 
 func init() {
 	clientv3.SetLogger(&clientLogger{})
+	mgr.RegisterPlugin(mgr.Plugin{mgr.REGISTRY, "buildin", NewRegistry})
 	mgr.RegisterPlugin(mgr.Plugin{mgr.REGISTRY, "etcd", NewRegistry})
 }
 
 type EtcdClient struct {
-	Endpoints []string
-	Client    *clientv3.Client
+	Client *clientv3.Client
+
+	Endpoints        []string
+	DialTimeout      time.Duration
+	TLSConfig        *tls.Config
+	AutoSyncInterval time.Duration
+
 	err       chan error
 	ready     chan int
 	goroutine *util.GoRoutine
 }
 
-func (s *EtcdClient) Err() <-chan error {
-	return s.err
+func (c *EtcdClient) Initialize() (err error) {
+	c.err = make(chan error, 1)
+	c.ready = make(chan int)
+	c.goroutine = util.NewGo(context.Background())
+
+	if len(c.Endpoints) == 0 {
+		// parse the endpoints from config
+		c.parseEndpoints()
+	}
+	if c.TLSConfig == nil && registry.RegistryConfig().SslEnabled {
+		var err error
+		// go client tls限制,提供身份证书、不认证服务端、不校验CN
+		c.TLSConfig, err = mgr.Plugins().TLS().ClientConfig()
+		if err != nil {
+			util.Logger().Error("get etcd client tls config failed", err)
+			return err
+		}
+	}
+	if c.DialTimeout == 0 {
+		c.DialTimeout = registry.RegistryConfig().DialTimeout
+	}
+	if c.AutoSyncInterval == 0 {
+		c.AutoSyncInterval = registry.RegistryConfig().AutoSyncInterval
+	}
+
+	c.Client, err = c.newClient()
+	if err != nil {
+		util.Logger().Errorf(err, "get etcd client %v failed.", c.Endpoints)
+		return
+	}
+
+	c.HealthCheck()
+
+	close(c.ready)
+
+	util.Logger().Warnf(nil, "get etcd client %v completed, auto sync endpoints interval is %s.",
+		c.Endpoints, c.AutoSyncInterval)
+	return
 }
 
-func (s *EtcdClient) Ready() <-chan int {
-	return s.ready
+func (c *EtcdClient) newClient() (*clientv3.Client, error) {
+	client, err := clientv3.New(clientv3.Config{
+		Endpoints:        c.Endpoints,
+		DialTimeout:      c.DialTimeout,
+		TLS:              c.TLSConfig,
+		AutoSyncInterval: 0,
+	})
+	if err != nil {
+		return nil, err
+	}
+	if len(c.Endpoints) == 1 {
+		ReportBackendInstance(1)
+		return client, nil
+	}
+
+	defer func() {
+		if err != nil {
+			client.Close()
+		}
+	}()
+
+	ctx, _ := context.WithTimeout(client.Ctx(), healthCheckTimeout)
+	resp, err := client.MemberList(ctx)
+	if err != nil {
+		return nil, err
+	}
+epLoop:
+	for _, ep := range c.Endpoints {
+		var cluster []string
+		for _, mem := range resp.Members {
+			for _, curl := range mem.ClientURLs {
+				u, err := url.Parse(curl)
+				if err != nil {
+					return nil, err
+				}
+				cluster = append(cluster, u.Host)
+				if u.Host == ep {
+					continue epLoop
+				}
+			}
+		}
+		// maybe endpoints = [domain A, domain B] or there are more than one cluster
+		err = fmt.Errorf("the etcd cluster endpoint list%v does not contain %s", cluster, ep)
+		return nil, err
+	}
+	ReportBackendInstance(len(resp.Members))
+	return client, nil
 }
 
-func (s *EtcdClient) Close() {
-	s.goroutine.Close(true)
+func (c *EtcdClient) Err() <-chan error {
+	return c.err
+}
 
-	if s.Client != nil {
-		s.Client.Close()
+func (c *EtcdClient) Ready() <-chan int {
+	return c.ready
+}
+
+func (c *EtcdClient) Close() {
+	c.goroutine.Close(true)
+
+	if c.Client != nil {
+		c.Client.Close()
 	}
-	util.Logger().Debugf("etcd client stopped.")
+	util.Logger().Debugf("etcd client stopped")
 }
 
 func (c *EtcdClient) Compact(ctx context.Context, reserve int64) error {
@@ -137,8 +227,8 @@ func max(n1, n2 int64) int64 {
 	return n2
 }
 
-func (s *EtcdClient) toGetRequest(op registry.PluginOp) []clientv3.OpOption {
-	opts := []clientv3.OpOption{}
+func (c *EtcdClient) toGetRequest(op registry.PluginOp) []clientv3.OpOption {
+	var opts []clientv3.OpOption
 	if op.Prefix {
 		opts = append(opts, clientv3.WithPrefix())
 	} else if len(op.EndKey) > 0 {
@@ -165,8 +255,8 @@ func (s *EtcdClient) toGetRequest(op registry.PluginOp) []clientv3.OpOption {
 	return opts
 }
 
-func (s *EtcdClient) toPutRequest(op registry.PluginOp) []clientv3.OpOption {
-	opts := []clientv3.OpOption{}
+func (c *EtcdClient) toPutRequest(op registry.PluginOp) []clientv3.OpOption {
+	var opts []clientv3.OpOption
 	if op.PrevKV {
 		opts = append(opts, clientv3.WithPrevKV())
 	}
@@ -179,8 +269,8 @@ func (s *EtcdClient) toPutRequest(op registry.PluginOp) []clientv3.OpOption {
 	return opts
 }
 
-func (s *EtcdClient) toDeleteRequest(op registry.PluginOp) []clientv3.OpOption {
-	opts := []clientv3.OpOption{}
+func (c *EtcdClient) toDeleteRequest(op registry.PluginOp) []clientv3.OpOption {
+	var opts []clientv3.OpOption
 	if op.Prefix {
 		opts = append(opts, clientv3.WithPrefix())
 	} else if len(op.EndKey) > 0 {
@@ -193,13 +283,13 @@ func (s *EtcdClient) toDeleteRequest(op registry.PluginOp) []clientv3.OpOption {
 }
 
 func (c *EtcdClient) toTxnRequest(opts []registry.PluginOp) []clientv3.Op {
-	etcdOps := []clientv3.Op{}
+	var etcdOps []clientv3.Op
 	for _, op := range opts {
 		switch op.Action {
 		case registry.Get:
 			etcdOps = append(etcdOps, clientv3.OpGet(util.BytesToStringWithNoCopy(op.Key), c.toGetRequest(op)...))
 		case registry.Put:
-			var value string = ""
+			var value string
 			if len(op.Value) > 0 {
 				value = util.BytesToStringWithNoCopy(op.Value)
 			}
@@ -212,7 +302,7 @@ func (c *EtcdClient) toTxnRequest(opts []registry.PluginOp) []clientv3.Op {
 }
 
 func (c *EtcdClient) toCompares(cmps []registry.CompareOp) []clientv3.Cmp {
-	etcdCmps := []clientv3.Cmp{}
+	var etcdCmps []clientv3.Cmp
 	for _, cmp := range cmps {
 		var cmpType clientv3.Cmp
 		var cmpResult string
@@ -267,11 +357,10 @@ func (c *EtcdClient) paging(ctx context.Context, op registry.PluginOp) (*clientv
 	}
 
 	recordCount := countResp.Count
-	if op.Offset == -1 && recordCount < op.Limit {
-		return nil, nil // no paging
+	if op.Offset == -1 && recordCount <= op.Limit {
+		return nil, nil // no need to do paging
 	}
 
-	tempOp.KeyOnly = false
 	tempOp.CountOnly = false
 	tempOp.Prefix = false
 	tempOp.SortOrder = registry.SORT_ASCEND
@@ -289,12 +378,25 @@ func (c *EtcdClient) paging(ctx context.Context, op registry.PluginOp) (*clientv
 	if remainCount > 0 {
 		pageCount++
 	}
+	minPage, maxPage := int64(0), pageCount
+	if op.Offset >= 0 {
+		count := op.Offset + 1
+		maxPage = count / op.Limit
+		if count%op.Limit > 0 {
+			maxPage++
+		}
+		minPage = maxPage - 1
+	}
 
-	baseOps := []clientv3.OpOption{}
+	var baseOps []clientv3.OpOption
 	baseOps = append(baseOps, c.toGetRequest(tempOp)...)
 
 	nextKey := key
 	for i := int64(0); i < pageCount; i++ {
+		if i >= maxPage {
+			break
+		}
+
 		limit, start := op.Limit, 0
 		if remainCount > 0 && i == pageCount-1 {
 			limit = remainCount
@@ -308,15 +410,13 @@ func (c *EtcdClient) paging(ctx context.Context, op registry.PluginOp) (*clientv
 		if err != nil {
 			return nil, err
 		}
+
 		l := int64(len(recordResp.Kvs))
 		nextKey = util.BytesToStringWithNoCopy(recordResp.Kvs[l-1].Key)
-
-		if op.Offset >= 0 {
-			if op.Offset < i*op.Limit {
-				continue
-			} else if op.Offset >= (i+1)*op.Limit {
-				break
-			}
+		if i < minPage {
+			// even through current page index less then the min page index,
+			// but here must to get the nextKey and then continue
+			continue
 		}
 		etcdResp.Kvs = append(etcdResp.Kvs, recordResp.Kvs[start:]...)
 	}
@@ -359,9 +459,6 @@ func (c *EtcdClient) Do(ctx context.Context, opts ...registry.PluginOpOption) (*
 	switch op.Action {
 	case registry.Get:
 		var etcdResp *clientv3.GetResponse
-		if op.Prefix && op.Key[len(op.Key)-1] != '/' {
-			op.Key = append(op.Key, '/')
-		}
 		key := util.BytesToStringWithNoCopy(op.Key)
 
 		if (op.Prefix || len(op.EndKey) > 0) && !op.CountOnly {
@@ -384,7 +481,7 @@ func (c *EtcdClient) Do(ctx context.Context, opts ...registry.PluginOpOption) (*
 			Revision: etcdResp.Header.Revision,
 		}
 	case registry.Put:
-		var value string = ""
+		var value string
 		if len(op.Value) > 0 {
 			value = util.BytesToStringWithNoCopy(op.Value)
 		}
@@ -473,7 +570,7 @@ func (c *EtcdClient) TxnWithCmp(ctx context.Context, success []registry.PluginOp
 func (c *EtcdClient) LeaseGrant(ctx context.Context, TTL int64) (int64, error) {
 	var err error
 	span := TracingBegin(ctx, "etcd:grant",
-		registry.PluginOp{Action: registry.Put, Key: util.StringToBytesWithNoCopy(fmt.Sprint(TTL))})
+		registry.PluginOp{Action: registry.Put, Key: util.StringToBytesWithNoCopy(strconv.FormatInt(TTL, 10))})
 	defer TracingEnd(span, err)
 
 	otCtx, cancel := registry.WithTimeout(ctx)
@@ -490,7 +587,7 @@ func (c *EtcdClient) LeaseGrant(ctx context.Context, TTL int64) (int64, error) {
 func (c *EtcdClient) LeaseRenew(ctx context.Context, leaseID int64) (int64, error) {
 	var err error
 	span := TracingBegin(ctx, "etcd:keepalive",
-		registry.PluginOp{Action: registry.Put, Key: util.StringToBytesWithNoCopy(fmt.Sprint(leaseID))})
+		registry.PluginOp{Action: registry.Put, Key: util.StringToBytesWithNoCopy(strconv.FormatInt(leaseID, 10))})
 	defer TracingEnd(span, err)
 
 	otCtx, cancel := registry.WithTimeout(ctx)
@@ -510,7 +607,7 @@ func (c *EtcdClient) LeaseRenew(ctx context.Context, leaseID int64) (int64, erro
 func (c *EtcdClient) LeaseRevoke(ctx context.Context, leaseID int64) error {
 	var err error
 	span := TracingBegin(ctx, "etcd:revoke",
-		registry.PluginOp{Action: registry.Delete, Key: util.StringToBytesWithNoCopy(fmt.Sprint(leaseID))})
+		registry.PluginOp{Action: registry.Delete, Key: util.StringToBytesWithNoCopy(strconv.FormatInt(leaseID, 10))})
 	defer TracingEnd(span, err)
 
 	otCtx, cancel := registry.WithTimeout(ctx)
@@ -543,9 +640,6 @@ func (c *EtcdClient) Watch(ctx context.Context, opts ...registry.PluginOpOption)
 		client := clientv3.NewWatcher(c.Client)
 		defer client.Close()
 
-		if op.Prefix && op.Key[len(op.Key)-1] != '/' {
-			op.Key = append(op.Key, '/')
-		}
 		key := util.BytesToStringWithNoCopy(op.Key)
 
 		// 不能设置超时context,内部判断了连接超时和watch超时
@@ -581,24 +675,26 @@ func (c *EtcdClient) Watch(ctx context.Context, opts ...registry.PluginOpOption)
 	return fmt.Errorf("no key has been watched")
 }
 
-func (c *EtcdClient) HealthCheck(ctx context.Context) {
-	retries := healthCheckRetryTimes
-	inv, err := time.ParseDuration(core.ServerInfo.Config.AutoSyncInterval)
-	if err != nil {
-		util.Logger().Errorf(err, "invalid auto sync interval '%s'.", core.ServerInfo.Config.AutoSyncInterval)
-		return
+func (c *EtcdClient) HealthCheck() {
+	if c.AutoSyncInterval >= time.Second {
+		c.goroutine.Do(c.healthCheckLoop)
 	}
+}
+
+func (c *EtcdClient) healthCheckLoop(pctx context.Context) {
+	retries := healthCheckRetryTimes
 hcLoop:
 	for {
 		select {
-		case <-ctx.Done():
+		case <-pctx.Done():
 			return
-		case <-time.After(inv):
+		case <-time.After(c.AutoSyncInterval):
 			var err error
 			for i := 0; i < retries; i++ {
-				if err = c.SyncMembers(); err != nil {
+				ctx, _ := context.WithTimeout(c.Client.Ctx(), healthCheckTimeout)
+				if err = c.SyncMembers(ctx); err != nil {
 					select {
-					case <-ctx.Done():
+					case <-pctx.Done():
 						return
 					case <-time.After(util.GetBackoff().Delay(i)):
 						continue
@@ -609,25 +705,30 @@ hcLoop:
 			}
 
 			retries = 1 // fail fast
-			client, cerr := newClient(c.Endpoints)
-			if cerr != nil {
-				util.Logger().Errorf(cerr, "create a new connection to etcd %v failed.",
-					c.Endpoints)
-				continue
+			if cerr := c.ReOpen(); cerr == nil {
+				util.Logger().Errorf(err, "re-connected to etcd %s", c.Endpoints)
 			}
-
-			c.Client, client = client, c.Client
-			util.Logger().Errorf(err, "re-connected to etcd %s", c.Endpoints)
-
-			if cerr = client.Close(); cerr != nil {
-				util.Logger().Errorf(cerr, "failed to close the unavailable etcd client.")
-			}
-			client = nil
 		}
 	}
 }
 
+func (c *EtcdClient) ReOpen() error {
+	client, cerr := c.newClient()
+	if cerr != nil {
+		util.Logger().Errorf(cerr, "create a new connection to etcd %v failed.",
+			c.Endpoints)
+		return cerr
+	}
+	c.Client, client = client, c.Client
+	if cerr = client.Close(); cerr != nil {
+		util.Logger().Errorf(cerr, "failed to close the unavailable etcd client.")
+	}
+	client = nil
+	return nil
+}
+
 func (c *EtcdClient) parseEndpoints() {
+	// use the default cluster endpoints
 	addrs := strings.Split(registry.RegistryConfig().ClusterAddresses, ",")
 
 	endpoints := make([]string, 0, len(addrs))
@@ -643,8 +744,7 @@ func (c *EtcdClient) parseEndpoints() {
 	c.Endpoints = endpoints
 }
 
-func (c *EtcdClient) SyncMembers() error {
-	ctx, _ := context.WithTimeout(c.Client.Ctx(), healthCheckTimeout)
+func (c *EtcdClient) SyncMembers(ctx context.Context) error {
 	if err := c.Client.Sync(ctx); err != nil && err != c.Client.Ctx().Err() {
 		return err
 	}
@@ -708,97 +808,20 @@ func callback(action registry.ActionType, rev int64, kvs []*mvccpb.KeyValue, cb
 	})
 }
 
-func sslEnabled() bool {
-	return core.ServerInfo.Config.SslEnabled &&
-		strings.Index(strings.ToLower(registry.RegistryConfig().ClusterAddresses), "https://") >= 0
-}
-
 func NewRegistry() mgr.PluginInstance {
 	util.Logger().Warnf(nil, "starting service center in proxy mode")
 
-	inst := &EtcdClient{
-		err:       make(chan error, 1),
-		ready:     make(chan int),
-		goroutine: util.NewGo(context.Background()),
+	inst := &EtcdClient{}
+	if err := inst.Initialize(); err != nil {
+		inst.err <- err
+		return inst
 	}
 
-	// parse the endpoints from config
-	inst.parseEndpoints()
-
 	scheme := "http://"
-	if sslEnabled() {
-		var err error
-		// go client tls限制,提供身份证书、不认证服务端、不校验CN
-		clientTLSConfig, err = mgr.Plugins().TLS().ClientConfig()
-		if err != nil {
-			util.Logger().Error("get etcd client tls config failed", err)
-			inst.err <- err
-			return inst
-		}
+	if inst.TLSConfig != nil {
 		scheme = "https://"
 	}
-
 	firstEndpoint = scheme + inst.Endpoints[0]
 
-	client, err := newClient(inst.Endpoints)
-	if err != nil {
-		util.Logger().Errorf(err, "get etcd client %v failed.", inst.Endpoints)
-		inst.err <- err
-		return inst
-	}
-
-	util.Logger().Warnf(nil, "get etcd client %v completed, auto sync endpoints interval is %s.",
-		inst.Endpoints, core.ServerInfo.Config.AutoSyncInterval)
-	inst.Client = client
-	inst.goroutine.Do(inst.HealthCheck)
-
-	close(inst.ready)
 	return inst
 }
-
-func newClient(endpoints []string) (*clientv3.Client, error) {
-	client, err := clientv3.New(clientv3.Config{
-		Endpoints:        endpoints,
-		DialTimeout:      connectRegistryServerTimeout,
-		TLS:              clientTLSConfig, // 暂时与API Server共用一套证书
-		AutoSyncInterval: 0,
-	})
-	if err != nil {
-		return nil, err
-	}
-	if len(endpoints) == 1 {
-		return client, nil
-	}
-
-	defer func() {
-		if err != nil {
-			client.Close()
-		}
-	}()
-
-	ctx, _ := context.WithTimeout(client.Ctx(), healthCheckTimeout)
-	resp, err := client.MemberList(ctx)
-	if err != nil {
-		return nil, err
-	}
-epLoop:
-	for _, ep := range endpoints {
-		var cluster []string
-		for _, mem := range resp.Members {
-			for _, curl := range mem.ClientURLs {
-				u, err := url.Parse(curl)
-				if err != nil {
-					return nil, err
-				}
-				cluster = append(cluster, u.Host)
-				if u.Host == ep {
-					continue epLoop
-				}
-			}
-		}
-		// maybe endpoints = [domain A, domain B] or there are more than one cluster
-		err = fmt.Errorf("the etcd cluster endpoint list%v does not contain %s", cluster, ep)
-		return nil, err
-	}
-	return client, nil
-}
diff --git a/server/plugin/infra/registry/etcd/etcd_test.go b/server/plugin/infra/registry/etcd/etcd_test.go
index e887d2e6..6b49ece0 100644
--- a/server/plugin/infra/registry/etcd/etcd_test.go
+++ b/server/plugin/infra/registry/etcd/etcd_test.go
@@ -16,59 +16,708 @@
  */
 package etcd
 
+import _ "github.com/apache/incubator-servicecomb-service-center/server/plugin/infra/tracing/buildin"
+import _ "github.com/apache/incubator-servicecomb-service-center/server/plugin/infra/security/buildin"
+import _ "github.com/apache/incubator-servicecomb-service-center/server/plugin/infra/tls/buildin"
 import (
-	"context"
+	"fmt"
+	"github.com/apache/incubator-servicecomb-service-center/server/core"
 	"github.com/apache/incubator-servicecomb-service-center/server/infra/registry"
-	"github.com/coreos/etcd/clientv3"
+	"github.com/apache/incubator-servicecomb-service-center/server/rpc"
+	"golang.org/x/net/context"
+	"google.golang.org/grpc/status"
+	"net/http"
+	"os"
+	"strconv"
+	"sync"
 	"testing"
 	"time"
 )
 
-// tracing
-import (
-	"fmt"
-	_ "github.com/apache/incubator-servicecomb-service-center/server/plugin/infra/tracing/buildin"
-)
+const dialTimeout = 500 * time.Millisecond
 
-func TestEtcdClient_Delete(t *testing.T) {
-	client, err := clientv3.New(clientv3.Config{
+func TestEtcdClient(t *testing.T) {
+	etcd := &EtcdClient{
 		Endpoints:   []string{"127.0.0.1:2379"},
-		DialTimeout: connectRegistryServerTimeout,
+		DialTimeout: dialTimeout,
+	}
+	err := etcd.Initialize()
+	if err != nil {
+		t.Fatalf("TestEtcdClient failed, %#v", err)
+	}
+	defer etcd.Close()
+
+	select {
+	case <-etcd.Ready():
+	default:
+		t.Fatalf("TestEtcdClient failed")
+	}
+
+	// base test
+	inst := NewRegistry()
+	if inst == nil || firstEndpoint != "http://127.0.0.1:2379" {
+		t.Fatalf("TestEtcdClient failed, %#v", firstEndpoint)
+	}
+	old1 := registry.RegistryConfig().ClusterAddresses
+	old2 := registry.RegistryConfig().DialTimeout
+	registry.RegistryConfig().ClusterAddresses = "x"
+	registry.RegistryConfig().DialTimeout = dialTimeout
+	inst = NewRegistry()
+	if inst == nil {
+		t.Fatalf("TestEtcdClient failed, %#v", err)
+	}
+	select {
+	case <-inst.(*EtcdClient).Err():
+	default:
+		t.Fatalf("TestEtcdClient failed, %#v", err)
+	}
+	registry.RegistryConfig().ClusterAddresses = old1
+	registry.RegistryConfig().DialTimeout = old2
+
+	// case: etcd do
+	// put
+	resp, err := etcd.Do(context.Background(), registry.PUT, registry.WithStrKey("/test_range/b"),
+		registry.WithStrValue("b"))
+	if err != nil || !resp.Succeeded {
+		t.Fatalf("TestEtcdClient_Do failed, %#v", err)
+	}
+	resp, err = etcd.Do(context.Background(), registry.GET, registry.WithStrKey("/test_range/b"))
+	if err != nil || !resp.Succeeded || resp.Count != 1 ||
+		string(resp.Kvs[0].Key) != "/test_range/b" || string(resp.Kvs[0].Value) != "b" {
+		t.Fatalf("TestEtcdClient_Do failed, %#v", err)
+	}
+
+	resp, err = etcd.Do(context.Background(), registry.PUT, registry.WithStrKey("/test_range/a"),
+		registry.WithStrValue("a"))
+	if err != nil || !resp.Succeeded {
+		t.Fatalf("TestEtcdClient_Do failed, %#v", err)
+	}
+	resp, err = etcd.Do(context.Background(), registry.GET, registry.WithStrKey("/test_range/a"),
+		registry.WithKeyOnly())
+	if err != nil || !resp.Succeeded || resp.Count != 1 ||
+		string(resp.Kvs[0].Key) != "/test_range/a" || resp.Kvs[0].Value != nil {
+		t.Fatalf("TestEtcdClient_Do failed, %#v", err)
+	}
+	resp, err = etcd.Do(context.Background(), registry.GET, registry.WithStrKey("/test_range/a"),
+		registry.WithCountOnly())
+	if err != nil || !resp.Succeeded || resp.Count != 1 || resp.Kvs != nil {
+		t.Fatalf("TestEtcdClient_Do failed, %#v", err)
+	}
+
+	resp, err = etcd.Do(context.Background(), registry.PUT, registry.WithStrKey("/test_range/c"),
+		registry.WithStrValue("c"))
+	if err != nil || !resp.Succeeded {
+		t.Fatalf("TestEtcdClient_Do failed, %#v", err)
+	}
+	resp, err = etcd.Do(context.Background(), registry.PUT, registry.WithStrKey("/test_range/d"),
+		registry.WithStrValue("d"))
+	if err != nil || !resp.Succeeded {
+		t.Fatalf("TestEtcdClient_Do failed, %#v", err)
+	}
+	resp, err = etcd.Do(context.Background(), registry.PUT, registry.WithStrKey("/test_range/dd"),
+		registry.WithStrValue("dd"))
+	if err != nil || !resp.Succeeded {
+		t.Fatalf("TestEtcdClient_Do failed, %#v", err)
+	}
+	// get prefix
+	resp, err = etcd.Do(context.Background(), registry.GET, registry.WithStrKey("/test_range/d"),
+		registry.WithPrefix())
+	if err != nil || !resp.Succeeded || resp.Count != 2 ||
+		string(resp.Kvs[0].Key) != "/test_range/d" || string(resp.Kvs[0].Value) != "d" ||
+		string(resp.Kvs[1].Key) != "/test_range/dd" || string(resp.Kvs[1].Value) != "dd" {
+		t.Fatalf("TestEtcdClient_Do failed, %#v", err)
+	}
+	resp, err = etcd.Do(context.Background(), registry.GET, registry.WithStrKey("/test_range/d"),
+		registry.WithPrefix(), registry.WithKeyOnly())
+	if err != nil || !resp.Succeeded || resp.Count != 2 ||
+		string(resp.Kvs[0].Key) != "/test_range/d" || resp.Kvs[0].Value != nil ||
+		string(resp.Kvs[1].Key) != "/test_range/dd" || resp.Kvs[1].Value != nil {
+		t.Fatalf("TestEtcdClient_Do failed, %#v", err)
+	}
+	resp, err = etcd.Do(context.Background(), registry.GET, registry.WithStrKey("/test_range/d"),
+		registry.WithPrefix(), registry.WithCountOnly())
+	if err != nil || !resp.Succeeded || resp.Count != 2 || resp.Kvs != nil {
+		t.Fatalf("TestEtcdClient_Do failed, %#v", err)
+	}
+	// get range
+	resp, err = etcd.Do(context.Background(), registry.GET,
+		registry.WithStrKey("/test_range/b"),
+		registry.WithStrEndKey("/test_range/dd")) // [b, dd) !!!
+	if err != nil || !resp.Succeeded || resp.Count != 3 ||
+		string(resp.Kvs[0].Key) != "/test_range/b" || string(resp.Kvs[0].Value) != "b" ||
+		string(resp.Kvs[1].Key) != "/test_range/c" || string(resp.Kvs[1].Value) != "c" ||
+		string(resp.Kvs[2].Key) != "/test_range/d" || string(resp.Kvs[2].Value) != "d" {
+		t.Fatalf("TestEtcdClient_Do failed, %#v", err)
+	}
+	resp, err = etcd.Do(context.Background(), registry.GET,
+		registry.WithStrKey("/test_range/b"),
+		registry.WithStrEndKey("/test_range/dd"), registry.WithKeyOnly()) // [b, dd) !!!
+	if err != nil || !resp.Succeeded || resp.Count != 3 ||
+		string(resp.Kvs[0].Key) != "/test_range/b" || resp.Kvs[0].Value != nil ||
+		string(resp.Kvs[1].Key) != "/test_range/c" || resp.Kvs[1].Value != nil ||
+		string(resp.Kvs[2].Key) != "/test_range/d" || resp.Kvs[2].Value != nil {
+		t.Fatalf("TestEtcdClient_Do failed, %#v", err)
+	}
+	resp, err = etcd.Do(context.Background(), registry.GET,
+		registry.WithStrKey("/test_range/b"),
+		registry.WithStrEndKey("/test_range/dd"), registry.WithCountOnly()) // [b, dd) !!!
+	if err != nil || !resp.Succeeded || resp.Count != 3 || resp.Kvs != nil {
+		t.Fatalf("TestEtcdClient_Do failed, %#v", err)
+	}
+	// get prefix paging
+	resp, err = etcd.Do(context.Background(), registry.GET,
+		registry.WithStrKey("/test_range/"), registry.WithPrefix(),
+		registry.WithOffset(2), registry.WithLimit(2))
+	if err != nil || !resp.Succeeded || resp.Count != 5 || len(resp.Kvs) != 2 ||
+		string(resp.Kvs[0].Key) != "/test_range/c" || string(resp.Kvs[0].Value) != "c" ||
+		string(resp.Kvs[1].Key) != "/test_range/d" || string(resp.Kvs[1].Value) != "d" {
+		t.Fatalf("TestEtcdClient_Do failed, %#v", err)
+	}
+	resp, err = etcd.Do(context.Background(), registry.GET,
+		registry.WithStrKey("/test_range/"), registry.WithPrefix(), registry.WithKeyOnly(),
+		registry.WithOffset(4), registry.WithLimit(2))
+	if err != nil || !resp.Succeeded || resp.Count != 5 || len(resp.Kvs) != 1 ||
+		string(resp.Kvs[0].Key) != "/test_range/dd" || resp.Kvs[0].Value != nil {
+		t.Fatalf("TestEtcdClient_Do failed, %#v", err)
+	}
+	resp, err = etcd.Do(context.Background(), registry.GET,
+		registry.WithStrKey("/test_range/d"), registry.WithPrefix(), registry.WithKeyOnly(),
+		registry.WithOffset(0), registry.WithLimit(2))
+	if err != nil || !resp.Succeeded || resp.Count != 2 || len(resp.Kvs) != 2 ||
+		string(resp.Kvs[0].Key) != "/test_range/d" || resp.Kvs[0].Value != nil ||
+		string(resp.Kvs[1].Key) != "/test_range/dd" || resp.Kvs[1].Value != nil {
+		t.Fatalf("TestEtcdClient_Do failed, %#v", err)
+	}
+	resp, err = etcd.Do(context.Background(), registry.GET,
+		registry.WithStrKey("/test_range/"), registry.WithPrefix(), registry.WithCountOnly(),
+		registry.WithOffset(2), registry.WithLimit(2))
+	if err != nil || !resp.Succeeded || resp.Count != 5 || resp.Kvs != nil {
+		t.Fatalf("TestEtcdClient_Do failed, %#v", err)
+	}
+	resp, err = etcd.Do(context.Background(), registry.GET,
+		registry.WithStrKey("/test_range/"), registry.WithPrefix(),
+		registry.WithOffset(6), registry.WithLimit(2))
+	if err != nil || !resp.Succeeded || resp.Count != 5 || len(resp.Kvs) != 0 {
+		t.Fatalf("TestEtcdClient_Do failed, %#v", err)
+	}
+	// if offset < -1, just paging by limit
+	resp, err = etcd.Do(context.Background(), registry.GET,
+		registry.WithStrKey("/test_range/"), registry.WithPrefix(),
+		registry.WithOffset(-2), registry.WithLimit(2))
+	if err != nil || !resp.Succeeded || resp.Count != 5 || len(resp.Kvs) != 5 {
+		t.Fatalf("TestEtcdClient_Do failed, %#v", err)
+	}
+	// get range paging
+	resp, err = etcd.Do(context.Background(), registry.GET,
+		registry.WithStrKey("/test_range/b"),
+		registry.WithStrEndKey("/test_range/dd"),
+		registry.WithOffset(2), registry.WithLimit(2))
+	if err != nil || !resp.Succeeded || resp.Count != 3 || len(resp.Kvs) != 1 ||
+		string(resp.Kvs[0].Key) != "/test_range/d" {
+		t.Fatalf("TestEtcdClient_Do failed, %#v", err)
+	}
+	resp, err = etcd.Do(context.Background(), registry.GET,
+		registry.WithStrKey("/test_range/a"),
+		registry.WithStrEndKey("/test_range/dd"),
+		registry.WithOffset(2), registry.WithLimit(2))
+	if err != nil || !resp.Succeeded || resp.Count != 4 || len(resp.Kvs) != 2 ||
+		string(resp.Kvs[0].Key) != "/test_range/c" ||
+		string(resp.Kvs[1].Key) != "/test_range/d" {
+		t.Fatalf("TestEtcdClient_Do failed, %#v", err)
+	}
+	resp, err = etcd.Do(context.Background(), registry.GET,
+		registry.WithStrKey("/test_range/a"),
+		registry.WithStrEndKey("/test_range/dd"), registry.WithKeyOnly(),
+		registry.WithOffset(2), registry.WithLimit(2))
+	if err != nil || !resp.Succeeded || resp.Count != 4 || len(resp.Kvs) != 2 ||
+		string(resp.Kvs[0].Key) != "/test_range/c" || resp.Kvs[0].Value != nil ||
+		string(resp.Kvs[1].Key) != "/test_range/d" || resp.Kvs[1].Value != nil {
+		t.Fatalf("TestEtcdClient_Do failed, %#v", err)
+	}
+	resp, err = etcd.Do(context.Background(), registry.GET,
+		registry.WithStrKey("/test_range/a"),
+		registry.WithStrEndKey("/test_range/dd"), registry.WithCountOnly(),
+		registry.WithOffset(2), registry.WithLimit(2))
+	if err != nil || !resp.Succeeded || resp.Count != 4 || resp.Kvs != nil {
+		t.Fatalf("TestEtcdClient_Do failed, %#v", err)
+	}
+	resp, err = etcd.Do(context.Background(), registry.GET,
+		registry.WithStrKey("/test_range/b"),
+		registry.WithStrEndKey("/test_range/dd"),
+		registry.WithOffset(5), registry.WithLimit(2))
+	if err != nil || !resp.Succeeded || resp.Count != 3 || len(resp.Kvs) != 0 {
+		t.Fatalf("TestEtcdClient_Do failed, %#v", err)
+	}
+	resp, err = etcd.Do(context.Background(), registry.GET,
+		registry.WithStrKey("/test_range/a"),
+		registry.WithStrEndKey("/test_range/dd"),
+		registry.WithOffset(4), registry.WithLimit(2))
+	if err != nil || !resp.Succeeded || resp.Count != 4 || len(resp.Kvs) != 0 {
+		t.Fatalf("TestEtcdClient_Do failed, %#v", err)
+	}
+	// delete range
+	resp, err = etcd.Do(context.Background(), registry.DEL,
+		registry.WithStrKey("/test_range/b"),
+		registry.WithStrEndKey("/test_range/dd")) // [b, d) !!!
+	if err != nil || !resp.Succeeded {
+		t.Fatalf("TestEtcdClient_Delete failed, %#v", err)
+	}
+	resp, err = etcd.Do(context.Background(), registry.GET, registry.WithStrKey("/test_range/"),
+		registry.WithPrefix())
+	if err != nil || !resp.Succeeded || len(resp.Kvs) != 2 || string(resp.Kvs[1].Key) != "/test_range/dd" {
+		t.Fatalf("TestEtcdClient_Delete failed, %#v", resp.Kvs)
+	}
+	// delete prefix
+	resp, err = etcd.Do(context.Background(), registry.DEL, registry.WithStrKey("/test_range/"),
+		registry.WithPrefix())
+	if err != nil || !resp.Succeeded {
+		t.Fatalf("TestEtcdClient_Delete failed, %#v", err)
+	}
+	resp, err = etcd.Do(context.Background(), registry.GET, registry.WithStrKey("/test_range/"),
+		registry.WithPrefix())
+	if err != nil || !resp.Succeeded || resp.Count != 0 {
+		t.Fatalf("TestEtcdClient_Delete failed, %#v", err)
+	}
+
+	// large data
+	var wg sync.WaitGroup
+	for i := 0; i < registry.DEFAULT_PAGE_COUNT+1; i++ {
+		wg.Add(1)
+		v := strconv.Itoa(i)
+		go func() {
+			defer wg.Done()
+			resp, err = etcd.Do(context.Background(), registry.PUT, registry.WithStrKey("/test_page/"+v),
+				registry.WithStrValue(v))
+			if err != nil || !resp.Succeeded {
+				t.Fatalf("TestEtcdClient_Do failed, %#v", err)
+			}
+		}()
+	}
+	wg.Wait()
+	resp, err = etcd.Do(context.Background(), registry.GET,
+		registry.WithStrKey("/test_page/"),
+		registry.WithStrEndKey("/test_page/9999"))
+	if err != nil || !resp.Succeeded || resp.Count != registry.DEFAULT_PAGE_COUNT+1 ||
+		len(resp.Kvs) != registry.DEFAULT_PAGE_COUNT+1 {
+		t.Fatalf("TestEtcdClient_Do failed, %#v", err)
+	}
+	resp, err = etcd.Do(context.Background(), registry.GET,
+		registry.WithStrKey("/test_page/"), registry.WithPrefix(), registry.WithDescendOrder())
+	if err != nil || !resp.Succeeded || resp.Count != registry.DEFAULT_PAGE_COUNT+1 ||
+		len(resp.Kvs) != registry.DEFAULT_PAGE_COUNT+1 ||
+		string(resp.Kvs[0].Key) != "/test_page/999" {
+		t.Fatalf("TestEtcdClient_Do failed, %#v", err)
+	}
+	// delete range
+	resp, err = etcd.Do(context.Background(), registry.DEL,
+		registry.WithStrKey("/test_page/"),
+		registry.WithStrEndKey("/test_page/9999"))
+	if err != nil || !resp.Succeeded {
+		t.Fatalf("TestEtcdClient_Delete failed, %#v", err)
+	}
+	resp, err = etcd.Do(context.Background(), registry.GET,
+		registry.WithStrKey("/test_page/"), registry.WithPrefix())
+	if err != nil || !resp.Succeeded || resp.Count != 0 {
+		t.Fatalf("TestEtcdClient_Do failed, %#v", err)
+	}
+}
+
+func TestEtcdClient_Compact(t *testing.T) {
+	etcd := &EtcdClient{
+		Endpoints:   []string{"127.0.0.1:2379"},
+		DialTimeout: dialTimeout,
+	}
+	err := etcd.Initialize()
+	if err != nil {
+		t.Fatalf("TestEtcdClient failed, %#v", err)
+	}
+	defer etcd.Close()
+
+	err = etcd.Compact(context.Background(), 0)
+	if err != nil {
+		t.Fatalf("TestEtcdClient_Compact failed")
+	}
+	err = etcd.Compact(context.Background(), 0)
+	if err == nil {
+		t.Fatalf("TestEtcdClient_Compact failed")
+	}
+}
+
+func TestEtcdClient_Txn(t *testing.T) {
+	etcd := &EtcdClient{
+		Endpoints:   []string{"127.0.0.1:2379"},
+		DialTimeout: dialTimeout,
+	}
+	err := etcd.Initialize()
+	if err != nil {
+		t.Fatalf("TestEtcdClient failed, %#v", err)
+	}
+	defer etcd.Close()
+
+	resp, err := etcd.Txn(context.Background(), nil)
+	if err == nil || resp != nil {
+		t.Fatalf("TestEtcdClient failed, %#v", err)
+	}
+
+	success, err := etcd.PutNoOverride(context.Background(), registry.WithStrKey("/test_txn/a"))
+	if err != nil || !success {
+		t.Fatalf("TestEtcdClient failed, %#v", err)
+	}
+	success, err = etcd.PutNoOverride(context.Background(), registry.WithStrKey("/test_txn/a"), registry.WithStrValue("a"))
+	if err != nil || success {
+		t.Fatalf("TestEtcdClient failed, %#v", err)
+	}
+
+	resp, err = etcd.Txn(context.Background(), []registry.PluginOp{
+		{Action: registry.Put, Key: []byte("/test_txn/a"), Value: []byte("a")},
+		{Action: registry.Put, Key: []byte("/test_txn/b"), Value: []byte("b")},
+	})
+	if err != nil || resp == nil || !resp.Succeeded {
+		t.Fatalf("TestEtcdClient failed, %#v", err)
+	}
+	resp, err = etcd.Do(context.Background(), registry.GET, registry.WithStrKey("/test_txn/"),
+		registry.WithPrefix(), registry.WithCountOnly())
+	if err != nil || !resp.Succeeded || resp.Count != 2 {
+		t.Fatalf("TestEtcdClient_Do failed, %#v", err)
+	}
+
+	resp, err = etcd.TxnWithCmp(context.Background(), []registry.PluginOp{
+		{Action: registry.Put, Key: []byte("/test_txn/a"), Value: []byte("a")},
+		{Action: registry.Put, Key: []byte("/test_txn/b"), Value: []byte("b")},
+	}, []registry.CompareOp{
+		{[]byte("/test_txn/a"), registry.CMP_VALUE, registry.CMP_EQUAL, "a"},
+	}, []registry.PluginOp{
+		{Action: registry.Put, Key: []byte("/test_txn/c"), Value: []byte("c")},
+		{Action: registry.Put, Key: []byte("/test_txn/d"), Value: []byte("d")},
+	})
+	if err != nil || resp == nil || !resp.Succeeded {
+		t.Fatalf("TestEtcdClient failed, %#v", err)
+	}
+
+	resp, err = etcd.TxnWithCmp(context.Background(), []registry.PluginOp{
+		{Action: registry.Put, Key: []byte("/test_txn/a"), Value: []byte("a")},
+		{Action: registry.Put, Key: []byte("/test_txn/b"), Value: []byte("b")},
+	}, []registry.CompareOp{
+		{[]byte("/test_txn/c"), registry.CMP_VALUE, registry.CMP_EQUAL, "c"},
+	}, []registry.PluginOp{
+		{Action: registry.Delete, Key: []byte("/test_txn/"), Prefix: true},
 	})
+	if err != nil || resp == nil || resp.Succeeded {
+		t.Fatalf("TestEtcdClient failed, %#v", err)
+	}
+
+	resp, err = etcd.Do(context.Background(), registry.GET, registry.WithStrKey("/test_txn/"),
+		registry.WithPrefix(), registry.WithCountOnly())
+	if err != nil || !resp.Succeeded || resp.Count != 0 {
+		t.Fatalf("TestEtcdClient_Do failed, %#v", err)
+	}
+}
+
+func TestEtcdClient_LeaseRenew(t *testing.T) {
+	etcd := &EtcdClient{
+		Endpoints:   []string{"127.0.0.1:2379"},
+		DialTimeout: dialTimeout,
+	}
+	err := etcd.Initialize()
 	if err != nil {
-		panic(err)
+		t.Fatalf("TestEtcdClient failed, %#v", err)
 	}
+	defer etcd.Close()
+
+	id, err := etcd.LeaseGrant(context.Background(), -1)
+	if err != nil || id == 0 {
+		t.Fatalf("TestEtcdClient failed, %#v", err)
+	}
+	id, err = etcd.LeaseGrant(context.Background(), 0)
+	if err != nil || id == 0 {
+		t.Fatalf("TestEtcdClient failed, %#v", err)
+	}
+	id, err = etcd.LeaseGrant(context.Background(), 2)
+	if err != nil || id == 0 {
+		t.Fatalf("TestEtcdClient failed, %#v", err)
+	}
+	ttl, err := etcd.LeaseRenew(context.Background(), id)
+	if err != nil || ttl != 2 {
+		t.Fatalf("TestEtcdClient failed, %#v", err)
+	}
+	err = etcd.LeaseRevoke(context.Background(), id)
+	if err != nil {
+		t.Fatalf("TestEtcdClient failed, %#v", err)
+	}
+	ttl, err = etcd.LeaseRenew(context.Background(), id)
+	if err == nil || ttl != 0 {
+		t.Fatalf("TestEtcdClient failed, %#v", err)
+	}
+}
+
+func TestEtcdClient_HealthCheck(t *testing.T) {
 	etcd := &EtcdClient{
-		err:    make(chan error, 1),
-		ready:  make(chan int),
-		Client: client,
+		Endpoints:        []string{"127.0.0.1:2379"},
+		DialTimeout:      dialTimeout,
+		AutoSyncInterval: time.Millisecond,
 	}
-	_, err = etcd.Do(context.Background(), registry.DEL, registry.WithStrKey("/test_range/"), registry.WithPrefix())
+	err := etcd.Initialize()
 	if err != nil {
-		panic(err)
+		t.Fatalf("TestEtcdClient failed, %#v", err)
 	}
-	_, err = etcd.Do(context.Background(), registry.PUT, registry.WithStrKey("/test_range/b"), registry.WithStrValue("b"))
+	defer etcd.Close()
+
+	err = etcd.ReOpen()
 	if err != nil {
-		panic(err)
+		t.Fatalf("TestEtcdClient failed, %#v", err)
 	}
-	_, err = etcd.Do(context.Background(), registry.PUT, registry.WithStrKey("/test_range/a"), registry.WithStrValue("a"))
+	ctx, _ := context.WithTimeout(context.Background(), dialTimeout)
+	err = etcd.SyncMembers(ctx)
 	if err != nil {
-		panic(err)
+		t.Fatalf("TestEtcdClient failed, %#v", err)
+	}
+	etcd.Endpoints = []string{"x"}
+	err = etcd.ReOpen()
+	if err == nil {
+		t.Fatalf("TestEtcdClient failed, %#v", err)
+	}
+	ctx, _ = context.WithTimeout(context.Background(), dialTimeout)
+	err = etcd.SyncMembers(ctx)
+	if err != nil {
+		t.Fatalf("TestEtcdClient failed, %#v", err)
+	}
+	etcd.Endpoints = []string{"127.0.0.1:2379"}
+
+	etcd.Close()
+	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
+	go func() {
+		for {
+			select {
+			case <-time.After(time.Second):
+				_, err = etcd.Do(context.Background(), registry.GET,
+					registry.WithStrKey("/test_health/"))
+				if err != nil {
+					continue
+				}
+			case <-ctx.Done():
+			}
+			break
+		}
+		if err != nil {
+			t.Fatalf("TestEtcdClient failed, %#v", err)
+		}
+		cancel()
+	}()
+	etcd.healthCheckLoop(ctx)
+}
+
+func TestEtcdClient_Watch(t *testing.T) {
+	etcd := &EtcdClient{
+		Endpoints:   []string{"127.0.0.1:2379"},
+		DialTimeout: dialTimeout,
 	}
-	_, err = etcd.Do(context.Background(), registry.PUT, registry.WithStrKey("/test_range/c"), registry.WithStrValue("c"))
+	err := etcd.Initialize()
 	if err != nil {
-		panic(err)
+		t.Fatalf("TestEtcdClient failed, %#v", err)
 	}
-	_, err = etcd.Do(context.Background(), registry.DEL, registry.WithStrKey("/test_range/b"),
-		registry.WithStrEndKey("/test_range/d")) // [b, d) !!!
+	defer etcd.Close()
+
+	defer func() {
+		resp, err := etcd.Do(context.Background(), registry.DEL, registry.WithStrKey("/test_watch/"),
+			registry.WithPrefix())
+		if err != nil || !resp.Succeeded {
+			t.Fatalf("TestEtcdClient_Do failed, %#v", err)
+		}
+	}()
+
+	ctx, cancel := context.WithCancel(context.Background())
+	cancel()
+	err = etcd.Watch(ctx, registry.WithStrKey("/test_watch/a"))
 	if err != nil {
-		panic(err)
+		t.Fatalf("TestEtcdClient failed, %#v", err)
+	}
+
+	ch := make(chan struct{})
+	go func() {
+		defer func() { ch <- struct{}{} }()
+		err = etcd.Watch(context.Background(), registry.WithStrKey("/test_watch/a"),
+			registry.WithWatchCallback(func(message string, evt *registry.PluginResponse) error {
+				if evt.Count != 1 || len(evt.Kvs) != 1 || evt.Action != registry.Put ||
+					string(evt.Kvs[0].Key) != "/test_watch/a" || string(evt.Kvs[0].Value) != "a" {
+					t.Fatalf("TestEtcdClient failed, %#v", evt)
+				}
+				return fmt.Errorf("error")
+			}))
+		if err == nil || err.Error() != "error" {
+			t.Fatalf("TestEtcdClient failed, %#v", err)
+		}
+	}()
+
+	<-time.After(500 * time.Millisecond)
+	resp, err := etcd.Do(context.Background(), registry.PUT, registry.WithStrKey("/test_watch/a"),
+		registry.WithStrValue("a"))
+	if err != nil || !resp.Succeeded {
+		t.Fatalf("TestEtcdClient_Do failed, %#v", err)
+	}
+	<-ch
+
+	go func() {
+		defer func() { ch <- struct{}{} }()
+		err = etcd.Watch(context.Background(), registry.WithStrKey("/test_watch/"),
+			registry.WithPrefix(),
+			registry.WithWatchCallback(func(message string, evt *registry.PluginResponse) error {
+				equalA := evt.Action == registry.Put && string(evt.Kvs[0].Key) == "/test_watch/a" && string(evt.Kvs[0].Value) == "a"
+				equalB := evt.Action == registry.Put && string(evt.Kvs[1].Key) == "/test_watch/b" && string(evt.Kvs[0].Value) == "b"
+				if evt.Count != 2 || len(evt.Kvs) != 2 || !(equalA || equalB) {
+					t.Fatalf("TestEtcdClient failed, %#v", evt)
+				}
+				return fmt.Errorf("error")
+			}))
+		if err == nil || err.Error() != "error" {
+			t.Fatalf("TestEtcdClient failed, %#v", err)
+		}
+	}()
+
+	<-time.After(500 * time.Millisecond)
+	resp, err = etcd.Txn(context.Background(), []registry.PluginOp{
+		{Action: registry.Put, Key: []byte("/test_watch/a"), Value: []byte("a")},
+		{Action: registry.Put, Key: []byte("/test_watch/b"), Value: []byte("b")},
+	})
+	if err != nil || !resp.Succeeded {
+		t.Fatalf("TestEtcdClient_Do failed, %#v", err)
 	}
-	resp, err := etcd.Do(context.Background(), registry.GET, registry.WithStrKey("/test_range/"), registry.WithPrefix())
+	<-ch
+
+	// diff action type will be split
+	go func() {
+		defer func() { ch <- struct{}{} }()
+		var times = 3
+		err = etcd.Watch(context.Background(), registry.WithStrKey("/test_watch/"),
+			registry.WithPrefix(),
+			registry.WithWatchCallback(func(message string, evt *registry.PluginResponse) error {
+				equalA := evt.Action == registry.Delete && string(evt.Kvs[0].Key) == "/test_watch/a" && evt.Kvs[0].Value == nil
+				equalB := evt.Action == registry.Put && string(evt.Kvs[0].Key) == "/test_watch/b" && string(evt.Kvs[0].Value) == "b"
+				equalC := evt.Action == registry.Put && string(evt.Kvs[0].Key) == "/test_watch/c" && string(evt.Kvs[0].Value) == "c"
+				if evt.Count != 1 || len(evt.Kvs) != 1 || !(equalA || equalB || equalC) {
+					t.Fatalf("TestEtcdClient failed, %#v", evt)
+				}
+				times--
+				if times == 0 {
+					return fmt.Errorf("error")
+				}
+				return nil
+			}))
+		if err == nil || err.Error() != "error" {
+			t.Fatalf("TestEtcdClient failed, %#v", err)
+		}
+	}()
+
+	<-time.After(500 * time.Millisecond)
+	resp, err = etcd.Txn(context.Background(), []registry.PluginOp{
+		{Action: registry.Put, Key: []byte("/test_watch/c"), Value: []byte("c")},
+		{Action: registry.Delete, Key: []byte("/test_watch/a"), Value: []byte("a")},
+		{Action: registry.Put, Key: []byte("/test_watch/b"), Value: []byte("b")},
+	})
+	if err != nil || !resp.Succeeded {
+		t.Fatalf("TestEtcdClient_Do failed, %#v", err)
+	}
+	<-ch
+
+	// watch with rev
+	resp, err = etcd.Do(context.Background(), registry.DEL, registry.WithStrKey("/test_watch/c"),
+		registry.WithStrValue("a"))
+	if err != nil || !resp.Succeeded {
+		t.Fatalf("TestEtcdClient_Do failed, %#v", err)
+	}
+	rev := resp.Revision
+	go func() {
+		defer func() { ch <- struct{}{} }()
+		err = etcd.Watch(context.Background(), registry.WithStrKey("/test_watch/"),
+			registry.WithPrefix(),
+			registry.WithRev(rev),
+			registry.WithWatchCallback(func(message string, evt *registry.PluginResponse) error {
+				if evt.Count != 1 || len(evt.Kvs) != 1 || evt.Action != registry.Delete ||
+					string(evt.Kvs[0].Key) != "/test_watch/c" || evt.Kvs[0].Value != nil {
+					t.Fatalf("TestEtcdClient failed, %#v", evt)
+				}
+				return fmt.Errorf("error")
+			}))
+		if err == nil || err.Error() != "error" {
+			t.Fatalf("TestEtcdClient failed, %#v", err)
+		}
+	}()
+	<-ch
+
+	// delete with prevKV
+	go func() {
+		defer func() { ch <- struct{}{} }()
+		err = etcd.Watch(context.Background(), registry.WithStrKey("/test_watch/"),
+			registry.WithPrefix(), registry.WithPrevKv(),
+			registry.WithWatchCallback(func(message string, evt *registry.PluginResponse) error {
+				if len(evt.Kvs) != 1 || evt.Action != registry.Delete ||
+					string(evt.Kvs[0].Key) != "/test_watch/b" || string(evt.Kvs[0].Value) != "b" {
+					t.Fatalf("TestEtcdClient failed, %#v", evt)
+				}
+				return fmt.Errorf("error")
+			}))
+		if err == nil || err.Error() != "error" {
+			t.Fatalf("TestEtcdClient failed, %#v", err)
+		}
+	}()
+	<-time.After(500 * time.Millisecond)
+	resp, err = etcd.Do(context.Background(), registry.DEL, registry.WithStrKey("/test_watch/b"))
+	if err != nil || !resp.Succeeded {
+		t.Fatalf("TestEtcdClient_Do failed, %#v", err)
+	}
+	<-ch
+}
+
+func TestNewRegistry(t *testing.T) {
+	etcd := &EtcdClient{
+		Endpoints:        []string{"127.0.0.1:2379", "0.0.0.0:2379"},
+		DialTimeout:      dialTimeout,
+		AutoSyncInterval: time.Millisecond,
+	}
+	err := etcd.Initialize()
+	if err == nil {
+		// should be err, member list does not contain one of the endpoints.
+		t.Fatalf("TestEtcdClient failed, %#v", err)
+	}
+}
+
+type mockTLSHandler struct {
+}
+
+func (m *mockTLSHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+}
+
+func TestWithTLS(t *testing.T) {
+	sslRoot := "../../../../../examples/service_center/ssl/"
+	os.Setenv("SSL_ROOT", sslRoot)
+
+	core.ServerInfo.Config.SslEnabled = true
+	registry.RegistryConfig().SslEnabled = true
+	defer func() {
+		core.ServerInfo.Config.SslEnabled = false
+		registry.RegistryConfig().SslEnabled = false
+		os.Setenv("SSL_ROOT", "")
+	}()
+
+	svr, err := rpc.NewServer("127.0.0.1:0")
+	go func() {
+		svr.Serve()
+	}()
+	defer svr.Stop()
+
+	etcd := &EtcdClient{
+		DialTimeout: dialTimeout,
+		Endpoints:   []string{svr.Listener.Addr().String()},
+	}
+
+	err = etcd.Initialize()
 	if err != nil {
-		panic(err)
+		t.Fatalf("TestEtcdClient failed, %#v", err)
 	}
-	if len(resp.Kvs) != 1 || string(resp.Kvs[0].Key) != "/test_range/a" {
-		t.Fatalf("TestEtcdClient_Delete failed, %#v", resp.Kvs)
+	defer etcd.Close()
+
+	ctx, _ := context.WithTimeout(context.Background(), dialTimeout)
+	err = etcd.SyncMembers(ctx)
+	if _, ok := status.FromError(err); !ok {
+		t.Fatalf("TestEtcdClient failed, %#v", err)
 	}
 }
diff --git a/server/plugin/infra/registry/etcd/logger_test.go b/server/plugin/infra/registry/etcd/logger_test.go
new file mode 100644
index 00000000..440059ad
--- /dev/null
+++ b/server/plugin/infra/registry/etcd/logger_test.go
@@ -0,0 +1,56 @@
+/*
+ * 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 etcd
+
+import "testing"
+
+func TestClientLogger_Print(t *testing.T) {
+	l := &clientLogger{}
+
+	defer func() {
+		recover()
+		defer func() {
+			recover()
+			defer func() {
+				recover()
+			}()
+			l.Fatalln("%s", "b")
+		}()
+		l.Fatalf("%s", "b")
+	}()
+	l.Info("a", "b")
+	l.Infof("%s", "b")
+	l.Infoln("a", "b")
+	l.Warning("a", "b")
+	l.Warningf("%s", "b")
+	l.Warningln("a", "b")
+	l.Error("a", "b")
+	l.Errorf("%s", "b")
+	l.Errorln("a", "b")
+	l.Print("a", "b")
+	l.Printf("%s", "b")
+	l.Println("a", "b")
+
+	l.Format("a/b", 1, 0, "c")
+	l.Format("a/b", 4, 0, "c")
+
+	if !l.V(0) {
+		t.Fatalf("TestClientLogger_Print")
+	}
+
+	l.Fatal("a", "b")
+}
diff --git a/server/plugin/infra/registry/etcd/metrics.go b/server/plugin/infra/registry/etcd/metrics.go
new file mode 100644
index 00000000..3a16a9e3
--- /dev/null
+++ b/server/plugin/infra/registry/etcd/metrics.go
@@ -0,0 +1,41 @@
+/*
+ * 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 etcd
+
+import (
+	"github.com/apache/incubator-servicecomb-service-center/server/metric"
+	"github.com/prometheus/client_golang/prometheus"
+)
+
+var (
+	backendCounter = prometheus.NewGaugeVec(
+		prometheus.GaugeOpts{
+			Namespace: metric.FamilyName,
+			Subsystem: "db",
+			Name:      "backend_total",
+			Help:      "Gauge of the backend instance",
+		}, []string{"instance"})
+)
+
+func init() {
+	prometheus.MustRegister(backendCounter)
+}
+
+func ReportBackendInstance(c int) {
+	instance := metric.InstanceName()
+	backendCounter.WithLabelValues(instance).Set(float64(c))
+}
diff --git a/server/plugin/infra/tls/buildin/tls_test.go b/server/plugin/infra/tls/buildin/tls_test.go
index 04ade81d..71e5e252 100644
--- a/server/plugin/infra/tls/buildin/tls_test.go
+++ b/server/plugin/infra/tls/buildin/tls_test.go
@@ -18,13 +18,16 @@ package buildin
 
 import (
 	"crypto/tls"
+	"github.com/apache/incubator-servicecomb-service-center/server/core"
 	_ "github.com/apache/incubator-servicecomb-service-center/server/plugin/infra/security/buildin"
 	"os"
 	"testing"
 )
 
 func init() {
-	sslRoot := "../../../../../etc/ssl/"
+	core.Initialize()
+
+	sslRoot := "../../../../../examples/service_center/ssl/"
 	os.Setenv("SSL_ROOT", sslRoot)
 }
 
diff --git a/server/plugin/infra/tracing/buildin/buildin.go b/server/plugin/infra/tracing/buildin/buildin.go
index 4c8bf157..12753b49 100644
--- a/server/plugin/infra/tracing/buildin/buildin.go
+++ b/server/plugin/infra/tracing/buildin/buildin.go
@@ -168,7 +168,7 @@ func (zp *Zipkin) ClientEnd(itf tracing.Span, code int, message string) {
 }
 
 func setResultTags(span opentracing.Span, code int, message string) {
-	if code >= http.StatusBadRequest && len(message) > 0 {
+	if code >= http.StatusBadRequest {
 		span.SetTag("error", message)
 	}
 	ext.HTTPStatusCode.Set(span, uint16(code))
diff --git a/server/plugin/infra/tracing/buildin/buildin_test.go b/server/plugin/infra/tracing/buildin/buildin_test.go
new file mode 100644
index 00000000..3c680d85
--- /dev/null
+++ b/server/plugin/infra/tracing/buildin/buildin_test.go
@@ -0,0 +1,75 @@
+/*
+ * 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 buildin
+
+import (
+	"context"
+	"github.com/apache/incubator-servicecomb-service-center/server/core"
+	"github.com/apache/incubator-servicecomb-service-center/server/infra/registry"
+	"github.com/apache/incubator-servicecomb-service-center/server/infra/tracing"
+	"net/http"
+	"os"
+	"testing"
+)
+
+func TestZipkin_XBegin(t *testing.T) {
+	os.Setenv("TRACING_COLLECTOR", "server")
+	core.RegisterInstanceRequest("x", []string{"x"})
+	initTracer()
+
+	zk := New().(*Zipkin)
+	span := zk.ServerBegin("x", nil)
+	if span != nil {
+		t.Fatalf("TestZipkin_XBegin failed")
+	}
+	span = zk.ClientBegin("x", nil)
+	if span != nil {
+		t.Fatalf("TestZipkin_XBegin failed")
+	}
+
+	req, _ := http.NewRequest(http.MethodGet, "http://127.0.0.1:30100", nil)
+	span = zk.ServerBegin("x", req)
+	if span == nil {
+		t.Fatalf("TestZipkin_XBegin failed")
+	}
+	zk.ServerEnd(span, 0, "")
+	zk.ServerEnd(span, 400, "")
+
+	if zk.ClientBegin("x", req) != nil {
+		t.Fatalf("TestZipkin_XBegin failed")
+	}
+
+	req = req.WithContext(context.WithValue(req.Context(), tracing.CTX_TRACE_SPAN, span))
+	span = zk.ClientBegin("x", req)
+	if span == nil {
+		t.Fatalf("TestZipkin_XBegin failed")
+	}
+
+	zk.ClientEnd(span, 0, "")
+
+	span = zk.ClientBegin("x", &tracing.RegistryRequest{
+		Ctx:      req.Context(),
+		Options:  registry.OpGet(),
+		Endpoint: "x",
+	})
+	if span == nil {
+		t.Fatalf("TestZipkin_XBegin failed")
+	}
+
+	zk.ClientEnd(span, 0, "")
+	zk.ClientEnd(span, 400, "")
+}
diff --git a/server/plugin/infra/tracing/buildin/common.go b/server/plugin/infra/tracing/buildin/common.go
index ad9fff76..fb839852 100644
--- a/server/plugin/infra/tracing/buildin/common.go
+++ b/server/plugin/infra/tracing/buildin/common.go
@@ -20,6 +20,7 @@ import (
 	"fmt"
 	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
 	"github.com/apache/incubator-servicecomb-service-center/server/core"
+	"github.com/apache/incubator-servicecomb-service-center/server/metric"
 	"github.com/opentracing/opentracing-go"
 	zipkin "github.com/openzipkin/zipkin-go-opentracing"
 	"os"
@@ -33,7 +34,7 @@ func initTracer() {
 		util.Logger().Warnf(err, "new tracing collector failed, use the noop tracer")
 		return
 	}
-	ipPort, _ := util.ParseEndpoint(core.Instance.Endpoints[0])
+	ipPort := metric.InstanceName()
 	recorder := zipkin.NewRecorder(collector, false, ipPort, strings.ToLower(core.Service.ServiceName))
 	tracer, err := zipkin.NewTracer(recorder, zipkin.TraceID128Bit(true))
 	if err != nil {
diff --git a/server/plugin/infra/tracing/buildin/file_collector.go b/server/plugin/infra/tracing/buildin/file_collector.go
index d4cefc6c..46de622a 100644
--- a/server/plugin/infra/tracing/buildin/file_collector.go
+++ b/server/plugin/infra/tracing/buildin/file_collector.go
@@ -82,6 +82,7 @@ func (f *FileCollector) write(batch []*zipkincore.Span) (c int) {
 		c++
 	}
 	if err := w.Flush(); err != nil {
+		c = 0
 		util.Logger().Errorf(err, "write span to file failed")
 	}
 	return
@@ -127,22 +128,29 @@ func (f *FileCollector) Run() {
 				f.write(batch)
 				return
 			case span := <-f.c:
+				l := len(batch)
+				if l >= max {
+					dispose := l - f.BatchSize
+					util.Logger().Errorf(nil, "backlog is full, dispose %d span(s), max: %d",
+						dispose, max)
+					batch = batch[dispose:] // allocate more
+				}
+
 				batch = append(batch, span)
-				if len(batch) >= f.BatchSize {
-					if len(batch) > max {
-						dispose := len(batch) - f.BatchSize
-						util.Logger().Errorf(nil, "backlog is full, dispose %d span(s), max: %d",
-							dispose, max)
-						batch = batch[dispose:] // allocate more
-					}
-					if c := f.write(batch); c == 0 {
-						continue
-					}
-					if prev != nil {
-						batch, prev = prev[:0], batch
-					} else {
-						prev, batch = batch, batch[len(batch):] // new one
-					}
+
+				l = len(batch)
+				if l < f.BatchSize {
+					continue
+				}
+
+				if c := f.write(batch); c == 0 {
+					continue
+				}
+
+				if prev != nil {
+					batch, prev = prev[:0], batch
+				} else {
+					prev, batch = batch, batch[len(batch):] // new one
 				}
 			case <-t.C:
 				if time.Now().After(nr) {
diff --git a/server/plugin/infra/tracing/buildin/file_collector_test.go b/server/plugin/infra/tracing/buildin/file_collector_test.go
index 92956dbf..c7fd087c 100644
--- a/server/plugin/infra/tracing/buildin/file_collector_test.go
+++ b/server/plugin/infra/tracing/buildin/file_collector_test.go
@@ -17,6 +17,7 @@
 package buildin
 
 import (
+	"fmt"
 	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
 	"github.com/openzipkin/zipkin-go-opentracing/thrift/gen-go/zipkincore"
 	"golang.org/x/net/context"
@@ -26,25 +27,31 @@ import (
 )
 
 func TestFileCollector_Collect(t *testing.T) {
+	bad := os.NewFile(0, "/dev/null") // bad fd
 	fc := &FileCollector{
-		Fd:        os.Stdout,
-		Timeout:   1 * time.Second,
-		Interval:  100 * time.Second,
+		Fd:        bad,
+		Timeout:   2 * time.Second,
+		Interval:  1 * time.Second,
 		BatchSize: 2,
 		c:         make(chan *zipkincore.Span, 100),
 		goroutine: util.NewGo(context.Background()),
 	}
-	defer func() {
-		fc.Close()
-	}()
 	fc.Run()
 
-	for i := 0; i < 10; i++ {
-		err := fc.Collect(&zipkincore.Span{})
+	// 0, 1 drop [2, 3, 4, 5], [6, 7] ok, 8 delay ok
+	for i := 0; i < 9; i++ {
+		if i > 4 {
+			fc.Fd = os.Stdout
+		}
+		err := fc.Collect(&zipkincore.Span{ID: int64(i)})
 		if err != nil {
-			t.Fatal(err)
+			t.Fatalf("TestFileCollector_Collect failed, %s", err.Error())
 		}
+		<-time.After(100 * time.Millisecond)
+		fmt.Println(i)
 	}
 
-	<-time.After(time.Second)
+	<-time.After(1 * time.Second)
+
+	// fc.Close()
 }
diff --git a/server/plugin/plugin.go b/server/plugin/plugin.go
index 7c8c1c2e..e4eb8049 100644
--- a/server/plugin/plugin.go
+++ b/server/plugin/plugin.go
@@ -17,7 +17,6 @@
 package plugin
 
 import (
-	"fmt"
 	"github.com/apache/incubator-servicecomb-service-center/pkg/plugin"
 	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
 	"github.com/apache/incubator-servicecomb-service-center/server/infra/auditlog"
@@ -30,6 +29,7 @@ import (
 	"github.com/apache/incubator-servicecomb-service-center/server/infra/uuid"
 	"github.com/astaxie/beego"
 	pg "plugin"
+	"strconv"
 	"sync"
 )
 
@@ -70,13 +70,15 @@ func (pn PluginName) String() string {
 	if name, ok := pluginNames[pn]; ok {
 		return name
 	}
-	return "PLUGIN" + fmt.Sprint(pn)
+	return "PLUGIN" + strconv.Itoa(int(pn))
 }
 
 type Plugin struct {
+	// plugin class name
 	PName PluginName
-	Name  string
-	New   func() PluginInstance
+	// plugin name
+	Name string
+	New  func() PluginInstance
 }
 
 type PluginInstance interface{}
@@ -97,15 +99,13 @@ func (pm *PluginManager) Initialize() {
 	pm.instances = make(map[PluginName]*wrapInstance, int(typeEnd))
 
 	for t := PluginName(0); t != typeEnd; t++ {
-		pluginMgr.instances[t] = &wrapInstance{}
+		pm.instances[t] = &wrapInstance{}
 	}
 }
 
 func (pm *PluginManager) ReloadAll() {
-	for _, i := range pm.instances {
-		i.lock.Lock()
-		i.instance = nil
-		i.lock.Unlock()
+	for pn := range pm.instances {
+		pm.Reload(pn)
 	}
 }
 
@@ -175,7 +175,8 @@ func (pm *PluginManager) New(pn PluginName) {
 
 		f = p.New
 	}
-	util.Logger().Infof("new '%s' instance from '%s' %s plugin", p.Name, p.PName, title)
+	util.Logger().Infof("call %s '%s' plugin %s(), new a '%s' instance",
+		title, p.PName, util.FuncName(f), p.Name)
 
 	wi.instance = f()
 }
@@ -192,6 +193,7 @@ func (pm *PluginManager) existDynamicPlugin(pn PluginName) *Plugin {
 	if !ok {
 		return nil
 	}
+	// 'buildin' implement of all plugins should call DynamicPluginFunc()
 	if plugin.PluginLoader().Exist(pn.String()) {
 		return m[BUILDIN]
 	}
@@ -201,34 +203,15 @@ func (pm *PluginManager) existDynamicPlugin(pn PluginName) *Plugin {
 func (pm *PluginManager) Registry() registry.Registry {
 	return pm.Instance(REGISTRY).(registry.Registry)
 }
-
-func (pm *PluginManager) UUID() uuid.UUID {
-	return pm.Instance(UUID).(uuid.UUID)
-}
-
+func (pm *PluginManager) UUID() uuid.UUID { return pm.Instance(UUID).(uuid.UUID) }
 func (pm *PluginManager) AuditLog() auditlog.AuditLogger {
 	return pm.Instance(AUDIT_LOG).(auditlog.AuditLogger)
 }
-
-func (pm *PluginManager) Auth() auth.Auth {
-	return pm.Instance(AUTH).(auth.Auth)
-}
-
-func (pm *PluginManager) Cipher() security.Cipher {
-	return pm.Instance(CIPHER).(security.Cipher)
-}
-
-func (pm *PluginManager) Quota() quota.QuotaManager {
-	return pm.Instance(QUOTA).(quota.QuotaManager)
-}
-
-func (pm *PluginManager) Tracing() tracing.Tracing {
-	return pm.Instance(TRACING).(tracing.Tracing)
-}
-
-func (pm *PluginManager) TLS() tls.TLS {
-	return pm.Instance(TLS).(tls.TLS)
-}
+func (pm *PluginManager) Auth() auth.Auth              { return pm.Instance(AUTH).(auth.Auth) }
+func (pm *PluginManager) Cipher() security.Cipher      { return pm.Instance(CIPHER).(security.Cipher) }
+func (pm *PluginManager) Quota() quota.QuotaManager    { return pm.Instance(QUOTA).(quota.QuotaManager) }
+func (pm *PluginManager) Tracing() (v tracing.Tracing) { return pm.Instance(TRACING).(tracing.Tracing) }
+func (pm *PluginManager) TLS() tls.TLS                 { return pm.Instance(TLS).(tls.TLS) }
 
 func Plugins() *PluginManager {
 	return pluginMgr
@@ -238,8 +221,9 @@ func RegisterPlugin(p Plugin) {
 	Plugins().Register(p)
 }
 
+// DynamicPluginFunc should be called in buildin implement
 func DynamicPluginFunc(pn PluginName, funcName string) pg.Symbol {
-	if wi := Plugins().instances[pn]; !wi.dynamic {
+	if wi, ok := Plugins().instances[pn]; ok && !wi.dynamic {
 		return nil
 	}
 
diff --git a/server/plugin/plugin_test.go b/server/plugin/plugin_test.go
new file mode 100644
index 00000000..7df1e843
--- /dev/null
+++ b/server/plugin/plugin_test.go
@@ -0,0 +1,66 @@
+/*
+ * 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 plugin
+
+import (
+	"net/http"
+	"testing"
+)
+
+type mockAuthPlugin struct {
+	Times int
+}
+
+func (*mockAuthPlugin) Identify(r *http.Request) error {
+	return nil
+}
+
+func TestPluginManager_New(t *testing.T) {
+	pm := &PluginManager{}
+	pm.Initialize()
+
+	p := pm.Get(AUTH, "buildin")
+	if p != nil {
+		t.Fatalf("TestPluginManager_New failed")
+	}
+
+	times := 0
+	fn := func() PluginInstance {
+		times++
+		return &mockAuthPlugin{times}
+	}
+	pm.Register(Plugin{AUTH, "buildin", fn})
+
+	i := pm.Instance(AUTH)
+	if i != pm.Instance(AUTH) {
+		t.Fatalf("TestPluginManager_New failed")
+	}
+
+	pm.ReloadAll()
+	n := pm.Instance(AUTH)
+	if i == n {
+		t.Fatalf("TestPluginManager_New failed")
+	}
+
+	defer func() {
+		if err := recover(); err != nil {
+			t.Fatalf("TestPluginManager_New failed")
+		}
+	}()
+	RegisterPlugin(Plugin{PluginName(999), "999", nil})
+	DynamicPluginFunc(PluginName(999), "999")
+}
diff --git a/server/rest/controller/rest_util.go b/server/rest/controller/rest_util.go
index 7e2491df..bb82fa57 100644
--- a/server/rest/controller/rest_util.go
+++ b/server/rest/controller/rest_util.go
@@ -24,11 +24,12 @@ import (
 	pb "github.com/apache/incubator-servicecomb-service-center/server/core/proto"
 	"github.com/apache/incubator-servicecomb-service-center/server/error"
 	"net/http"
+	"strconv"
 )
 
 func WriteError(w http.ResponseWriter, code int32, detail string) {
 	err := error.NewError(code, detail)
-	w.Header().Set(rest.HEADER_RESPONSE_STATUS, fmt.Sprint(err.StatusCode()))
+	w.Header().Set(rest.HEADER_RESPONSE_STATUS, strconv.Itoa(err.StatusCode()))
 	w.Header().Set(rest.HEADER_CONTENT_TYPE, rest.CONTENT_TYPE_JSON)
 	w.WriteHeader(err.StatusCode())
 	fmt.Fprintln(w, util.BytesToStringWithNoCopy(err.Marshal()))
@@ -41,7 +42,7 @@ func WriteResponse(w http.ResponseWriter, resp *pb.Response, obj interface{}) {
 	}
 
 	if obj == nil {
-		w.Header().Set(rest.HEADER_RESPONSE_STATUS, fmt.Sprint(http.StatusOK))
+		w.Header().Set(rest.HEADER_RESPONSE_STATUS, strconv.Itoa(http.StatusOK))
 		w.Header().Set(rest.HEADER_CONTENT_TYPE, rest.CONTENT_TYPE_TEXT)
 		w.WriteHeader(http.StatusOK)
 		return
@@ -52,7 +53,7 @@ func WriteResponse(w http.ResponseWriter, resp *pb.Response, obj interface{}) {
 		WriteError(w, error.ErrInternal, err.Error())
 		return
 	}
-	w.Header().Set(rest.HEADER_RESPONSE_STATUS, fmt.Sprint(http.StatusOK))
+	w.Header().Set(rest.HEADER_RESPONSE_STATUS, strconv.Itoa(http.StatusOK))
 	w.Header().Set(rest.HEADER_CONTENT_TYPE, rest.CONTENT_TYPE_JSON)
 	w.WriteHeader(http.StatusOK)
 	fmt.Fprintln(w, util.BytesToStringWithNoCopy(objJson))
@@ -60,7 +61,7 @@ func WriteResponse(w http.ResponseWriter, resp *pb.Response, obj interface{}) {
 
 func WriteJsonBytes(w http.ResponseWriter, resp *pb.Response, json []byte) {
 	if resp.GetCode() == pb.Response_SUCCESS {
-		w.Header().Set(rest.HEADER_RESPONSE_STATUS, fmt.Sprint(http.StatusOK))
+		w.Header().Set(rest.HEADER_RESPONSE_STATUS, strconv.Itoa(http.StatusOK))
 		w.Header().Set(rest.HEADER_CONTENT_TYPE, rest.CONTENT_TYPE_JSON)
 		w.WriteHeader(http.StatusOK)
 		w.Write(json)
diff --git a/server/rest/controller/v3/v3.go b/server/rest/controller/v3/v3.go
index 5f4a0e3b..21e0c421 100644
--- a/server/rest/controller/v3/v3.go
+++ b/server/rest/controller/v3/v3.go
@@ -25,12 +25,12 @@ func init() {
 }
 
 func initRouter() {
-	roa.RegisterServent(&MainService{})
-	roa.RegisterServent(&MicroServiceService{})
-	roa.RegisterServent(&SchemaService{})
-	roa.RegisterServent(&DependencyService{})
-	roa.RegisterServent(&TagService{})
-	roa.RegisterServent(&RuleService{})
-	roa.RegisterServent(&MicroServiceInstanceService{})
-	roa.RegisterServent(&WatchService{})
+	roa.RegisterServant(&MainService{})
+	roa.RegisterServant(&MicroServiceService{})
+	roa.RegisterServant(&SchemaService{})
+	roa.RegisterServant(&DependencyService{})
+	roa.RegisterServant(&TagService{})
+	roa.RegisterServant(&RuleService{})
+	roa.RegisterServant(&MicroServiceInstanceService{})
+	roa.RegisterServant(&WatchService{})
 }
diff --git a/server/rest/controller/v4/instance_controller.go b/server/rest/controller/v4/instance_controller.go
index a0ff0535..288b0499 100644
--- a/server/rest/controller/v4/instance_controller.go
+++ b/server/rest/controller/v4/instance_controller.go
@@ -18,7 +18,6 @@ package v4
 
 import (
 	"encoding/json"
-	"fmt"
 	"github.com/apache/incubator-servicecomb-service-center/pkg/rest"
 	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
 	"github.com/apache/incubator-servicecomb-service-center/server/core"
@@ -144,7 +143,7 @@ func (this *MicroServiceInstanceService) FindInstances(w http.ResponseWriter, r
 
 	iv, _ := r.Context().Value(serviceUtil.CTX_REQUEST_REVISION).(string)
 	ov, _ := r.Context().Value(serviceUtil.CTX_RESPONSE_REVISION).(string)
-	w.Header().Set(serviceUtil.HEADER_REV, fmt.Sprint(ov))
+	w.Header().Set(serviceUtil.HEADER_REV, ov)
 	if len(iv) > 0 && iv == ov {
 		w.WriteHeader(http.StatusNotModified)
 		return
diff --git a/server/rest/controller/v4/main_controller.go b/server/rest/controller/v4/main_controller.go
index c011d5df..a58bfc54 100644
--- a/server/rest/controller/v4/main_controller.go
+++ b/server/rest/controller/v4/main_controller.go
@@ -47,7 +47,7 @@ func init() {
 	result := Result{
 		version.Ver(),
 		API_VERSION,
-		core.ServerInfo.Config,
+		&core.ServerInfo.Config,
 	}
 	versionJsonCache, _ = json.Marshal(result)
 	versionResp = pb.CreateResponse(pb.Response_SUCCESS, "get version successfully")
diff --git a/server/rest/controller/v4/v4.go b/server/rest/controller/v4/v4.go
index 70a88080..501a527e 100644
--- a/server/rest/controller/v4/v4.go
+++ b/server/rest/controller/v4/v4.go
@@ -25,12 +25,12 @@ func init() {
 }
 
 func initRouter() {
-	roa.RegisterServent(&MainService{})
-	roa.RegisterServent(&MicroServiceService{})
-	roa.RegisterServent(&SchemaService{})
-	roa.RegisterServent(&DependencyService{})
-	roa.RegisterServent(&TagService{})
-	roa.RegisterServent(&RuleService{})
-	roa.RegisterServent(&MicroServiceInstanceService{})
-	roa.RegisterServent(&WatchService{})
+	roa.RegisterServant(&MainService{})
+	roa.RegisterServant(&MicroServiceService{})
+	roa.RegisterServant(&SchemaService{})
+	roa.RegisterServant(&DependencyService{})
+	roa.RegisterServant(&TagService{})
+	roa.RegisterServant(&RuleService{})
+	roa.RegisterServant(&MicroServiceInstanceService{})
+	roa.RegisterServant(&WatchService{})
 }
diff --git a/server/rest/metrics.go b/server/rest/metrics.go
index a15c45f1..dfe3ae2d 100644
--- a/server/rest/metrics.go
+++ b/server/rest/metrics.go
@@ -17,9 +17,8 @@
 package rest
 
 import (
-	"fmt"
 	"github.com/apache/incubator-servicecomb-service-center/pkg/rest"
-	"github.com/apache/incubator-servicecomb-service-center/server/core"
+	"github.com/apache/incubator-servicecomb-service-center/server/metric"
 	"github.com/prometheus/client_golang/prometheus"
 	"net/http"
 	"strconv"
@@ -30,7 +29,7 @@ import (
 var (
 	incomingRequests = prometheus.NewCounterVec(
 		prometheus.CounterOpts{
-			Namespace: "service_center",
+			Namespace: metric.FamilyName,
 			Subsystem: "http",
 			Name:      "request_total",
 			Help:      "Counter of requests received into ROA handler",
@@ -38,7 +37,7 @@ var (
 
 	successfulRequests = prometheus.NewCounterVec(
 		prometheus.CounterOpts{
-			Namespace: "service_center",
+			Namespace: metric.FamilyName,
 			Subsystem: "http",
 			Name:      "success_total",
 			Help:      "Counter of successful requests processed by ROA handler",
@@ -46,7 +45,7 @@ var (
 
 	reqDurations = prometheus.NewSummaryVec(
 		prometheus.SummaryOpts{
-			Namespace:  "service_center",
+			Namespace:  metric.FamilyName,
 			Subsystem:  "http",
 			Name:       "request_durations_microseconds",
 			Help:       "HTTP request latency summary of ROA handler",
@@ -61,7 +60,7 @@ func init() {
 }
 
 func ReportRequestCompleted(w http.ResponseWriter, r *http.Request, start time.Time) {
-	instance := fmt.Sprint(core.Instance.Endpoints)
+	instance := metric.InstanceName()
 	elapsed := float64(time.Since(start).Nanoseconds()) / float64(time.Microsecond)
 	route, _ := r.Context().Value(rest.CTX_MATCH_PATTERN).(string)
 
diff --git a/server/rest/server.go b/server/rest/server.go
index ec151c5c..461d7968 100644
--- a/server/rest/server.go
+++ b/server/rest/server.go
@@ -19,17 +19,11 @@ package rest
 import (
 	"crypto/tls"
 	"github.com/apache/incubator-servicecomb-service-center/pkg/rest"
-	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
 	"github.com/apache/incubator-servicecomb-service-center/server/core"
 	"github.com/apache/incubator-servicecomb-service-center/server/plugin"
-	"net/http"
 	"time"
 )
 
-var (
-	defaultRESTfulServer *rest.Server
-)
-
 func LoadConfig() (srvCfg *rest.ServerConfig, err error) {
 	srvCfg = rest.DefaultServerConfig()
 	readHeaderTimeout, _ := time.ParseDuration(core.ServerInfo.Config.ReadHeaderTimeout)
@@ -53,48 +47,6 @@ func LoadConfig() (srvCfg *rest.ServerConfig, err error) {
 	return
 }
 
-func newDefaultServer(addr string, handler http.Handler) error {
-	if defaultRESTfulServer != nil {
-		return nil
-	}
-	srvCfg, err := LoadConfig()
-	if err != nil {
-		return err
-	}
-	srvCfg.Handler = handler
-	defaultRESTfulServer = rest.NewServer(srvCfg)
-	util.Logger().Warnf(nil, "listen on server %s.", addr)
-
-	return nil
-}
-
-func ListenAndServeTLS(addr string, handler http.Handler) (err error) {
-	err = newDefaultServer(addr, handler)
-	if err != nil {
-		return err
-	}
-	// 证书已经在config里加载,这里不需要再重新加载
-	return defaultRESTfulServer.ListenAndServeTLS("", "")
-}
-func ListenAndServe(addr string, handler http.Handler) (err error) {
-	err = newDefaultServer(addr, handler)
-	if err != nil {
-		return err
-	}
-	return defaultRESTfulServer.ListenAndServe()
-}
-
-func GracefulStop() {
-	if defaultRESTfulServer == nil {
-		return
-	}
-	defaultRESTfulServer.Shutdown()
-}
-
-func DefaultServer() *rest.Server {
-	return defaultRESTfulServer
-}
-
 func NewServer(ipAddr string) (srv *rest.Server, err error) {
 	srvCfg, err := LoadConfig()
 	if err != nil {
diff --git a/server/rest/server_mux.go b/server/rest/server_mux.go
index 5f1c6f80..ec5bb488 100644
--- a/server/rest/server_mux.go
+++ b/server/rest/server_mux.go
@@ -21,17 +21,46 @@ import (
 	"net/http"
 )
 
-var DefaultServerMux = http.NewServeMux()
+// The rest middleware design:
+//   http requests -> DefaultServerMux [ -> ROA router -> handler chain ] -> services
+// Usages:
+//   1. if use rest.RegisterServant:
+//      to register in ROA and requests will go through the handler chain
+//   2. if use RegisterServerHandleFunc or RegisterServerHandler:
+//      to register in ServeMux directly
 
-func RegisterServerHandleFunc(pattern string, f http.HandlerFunc) {
-	DefaultServerMux.HandleFunc(pattern, f)
+const defaultServeMux = "default"
+
+var (
+	DefaultServerMux = http.NewServeMux()
+	serveMuxMap      = map[string]*http.ServeMux{
+		defaultServeMux: DefaultServerMux,
+	}
+)
 
-	util.Logger().Infof("register server http handle function %s(), pattern %s", util.FuncName(f), pattern)
+func RegisterServeMux(name string) {
+	serveMuxMap[name] = http.NewServeMux()
 }
 
-func RegisterServerHandler(pattern string, h http.Handler) {
-	DefaultServerMux.Handle(pattern, h)
+func RegisterServeMuxHandleFunc(name, pattern string, f http.HandlerFunc) {
+	serveMuxMap[name].HandleFunc(pattern, f)
+
+	util.Logger().Infof("register serve mux '%s' http handle function %s(), pattern %s",
+		name, util.FuncName(f), pattern)
+}
+
+func RegisterServeMuxHandler(name, pattern string, h http.Handler) {
+	serveMuxMap[name].Handle(pattern, h)
 
-	t := util.ReflectObject(h).Type
-	util.Logger().Infof("register server http handler %s/%s, pattern %s", t.PkgPath(), t.Name(), pattern)
+	t := util.Reflect(h).Type
+	util.Logger().Infof("register serve mux '%s' http handler %s/%s, pattern %s",
+		name, t.PkgPath(), t.Name(), pattern)
+}
+
+func RegisterServerHandleFunc(pattern string, f http.HandlerFunc) {
+	RegisterServeMuxHandleFunc(defaultServeMux, pattern, f)
+}
+
+func RegisterServerHandler(pattern string, h http.Handler) {
+	RegisterServeMuxHandler(defaultServeMux, pattern, h)
 }
diff --git a/server/rpc/server.go b/server/rpc/server.go
index 8c2a6624..ae7ef967 100644
--- a/server/rpc/server.go
+++ b/server/rpc/server.go
@@ -28,11 +28,11 @@ import (
 
 type Server struct {
 	*grpc.Server
-	innerListener net.Listener
+	Listener net.Listener
 }
 
 func (srv *Server) Serve() error {
-	return srv.Server.Serve(srv.innerListener)
+	return srv.Server.Serve(srv.Listener)
 }
 
 func NewServer(ipAddr string) (_ *Server, err error) {
@@ -49,7 +49,7 @@ func NewServer(ipAddr string) (_ *Server, err error) {
 		grpcSrv = grpc.NewServer()
 	}
 
-	rpc.RegisterServer(grpcSrv)
+	rpc.RegisterGRpcServer(grpcSrv)
 
 	ls, err := net.Listen("tcp", ipAddr)
 	if err != nil {
@@ -58,7 +58,7 @@ func NewServer(ipAddr string) (_ *Server, err error) {
 	}
 
 	return &Server{
-		Server:        grpcSrv,
-		innerListener: ls,
+		Server:   grpcSrv,
+		Listener: ls,
 	}, nil
 }
diff --git a/server/server.go b/server/server.go
index 66de1090..ca70ad42 100644
--- a/server/server.go
+++ b/server/server.go
@@ -18,12 +18,10 @@ package server
 
 import _ "github.com/apache/incubator-servicecomb-service-center/server/service/event"
 import (
-	"encoding/json"
 	"fmt"
 	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
 	"github.com/apache/incubator-servicecomb-service-center/server/core"
 	"github.com/apache/incubator-servicecomb-service-center/server/core/backend"
-	"github.com/apache/incubator-servicecomb-service-center/server/infra/registry"
 	"github.com/apache/incubator-servicecomb-service-center/server/mux"
 	nf "github.com/apache/incubator-servicecomb-service-center/server/service/notification"
 	serviceUtil "github.com/apache/incubator-servicecomb-service-center/server/service/util"
@@ -37,19 +35,6 @@ import (
 	"time"
 )
 
-var (
-	server *ServiceCenterServer
-)
-
-func init() {
-	server = &ServiceCenterServer{
-		store:         backend.Store(),
-		notifyService: nf.GetNotifyService(),
-		apiServer:     GetAPIServer(),
-		goroutine:     util.NewGo(context.Background()),
-	}
-}
-
 type ServiceCenterServer struct {
 	apiServer     *APIServer
 	notifyService *nf.NotifyService
@@ -78,66 +63,48 @@ func (s *ServiceCenterServer) waitForQuit() {
 	}
 
 	s.Stop()
-
-	util.Logger().Debugf("service center stopped")
 }
 
-func LoadServerInformation() error {
-	resp, err := backend.Registry().Do(context.Background(),
-		registry.GET, registry.WithStrKey(core.GetServerInfoKey()))
+func (s *ServiceCenterServer) needUpgrade() bool {
+	err := LoadServerVersion()
 	if err != nil {
-		return err
-	}
-	if len(resp.Kvs) == 0 {
-		return nil
+		util.Logger().Errorf(err, "check version failed, can not load the system config")
+		return false
 	}
 
-	err = json.Unmarshal(resp.Kvs[0].Value, core.ServerInfo)
-	if err != nil {
-		util.Logger().Errorf(err, "load system config failed, maybe incompatible")
-		return nil
+	update := !serviceUtil.VersionMatchRule(core.ServerInfo.Version,
+		fmt.Sprintf("%s+", version.Ver().Version))
+	if !update && version.Ver().Version != core.ServerInfo.Version {
+		util.Logger().Warnf(nil,
+			"there is a higher version '%s' in cluster, now running '%s' version may be incompatible",
+			core.ServerInfo.Version, version.Ver().Version)
 	}
-	return nil
-}
 
-func UpgradeServerVersion() error {
-	core.ServerInfo.Version = version.Ver().Version
-
-	bytes, err := json.Marshal(core.ServerInfo)
-	if err != nil {
-		return err
-	}
-	_, err = backend.Registry().Do(context.Background(),
-		registry.PUT, registry.WithStrKey(core.GetServerInfoKey()), registry.WithValue(bytes))
-	if err != nil {
-		return err
-	}
-	return nil
+	return update
 }
 
-func (s *ServiceCenterServer) needUpgrade() bool {
-	if core.ServerInfo.Version == "0" {
-		err := LoadServerInformation()
-		if err != nil {
-			util.Logger().Errorf(err, "check version failed, can not load the system config")
-			return false
-		}
-	}
-	return !serviceUtil.VersionMatchRule(core.ServerInfo.Version,
-		fmt.Sprintf("%s+", version.Ver().Version))
-}
-
-func (s *ServiceCenterServer) initialize() {
-	// check version
+func (s *ServiceCenterServer) loadOrUpgradeServerVersion() {
 	lock, err := mux.Lock(mux.GLOBAL_LOCK)
-	defer lock.Unlock()
 	if err != nil {
 		util.Logger().Errorf(err, "wait for server ready failed")
 		os.Exit(1)
 	}
 	if s.needUpgrade() {
+		core.ServerInfo.Version = version.Ver().Version
+
 		UpgradeServerVersion()
 	}
+	lock.Unlock()
+}
+
+func (s *ServiceCenterServer) initialize() {
+	s.store = backend.Store()
+	s.notifyService = nf.GetNotifyService()
+	s.apiServer = GetAPIServer()
+	s.goroutine = util.NewGo(context.Background())
+
+	// check version
+	s.loadOrUpgradeServerVersion()
 
 	// cache mechanism
 	s.store.Run()
@@ -226,8 +193,10 @@ func (s *ServiceCenterServer) Stop() {
 	}
 
 	s.goroutine.Close(true)
-}
 
-func Run() {
-	server.Run()
+	util.GoCloseAndWait()
+
+	backend.Registry().Close()
+
+	util.Logger().Warnf(nil, "service center stopped")
 }
diff --git a/server/service/dependency_test.go b/server/service/dependency_test.go
index 0586ceb4..b1bfcbce 100644
--- a/server/service/dependency_test.go
+++ b/server/service/dependency_test.go
@@ -17,12 +17,12 @@
 package service_test
 
 import (
-	"fmt"
 	pb "github.com/apache/incubator-servicecomb-service-center/server/core/proto"
 	scerr "github.com/apache/incubator-servicecomb-service-center/server/error"
 	"github.com/apache/incubator-servicecomb-service-center/server/service/event"
 	. "github.com/onsi/ginkgo"
 	. "github.com/onsi/gomega"
+	"strconv"
 )
 
 var deh event.DependencyEventHandler
@@ -356,7 +356,7 @@ var _ = Describe("'Dependency' service", func() {
 					deps = append(deps, &pb.ConsumerDependency{
 						Consumer: &pb.MicroServiceKey{
 							AppId:       "create_dep_group",
-							ServiceName: "create_dep_consumer" + fmt.Sprint(i),
+							ServiceName: "create_dep_consumer" + strconv.Itoa(i),
 							Version:     "1.0.0",
 						},
 						Providers: []*pb.MicroServiceKey{
diff --git a/server/service/event/domain_event_handler.go b/server/service/event/domain_event_handler.go
new file mode 100644
index 00000000..850f07d5
--- /dev/null
+++ b/server/service/event/domain_event_handler.go
@@ -0,0 +1,44 @@
+/*
+ * 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 event
+
+import (
+	"github.com/apache/incubator-servicecomb-service-center/server/core/backend"
+	pb "github.com/apache/incubator-servicecomb-service-center/server/core/proto"
+	"github.com/apache/incubator-servicecomb-service-center/server/service/metrics"
+)
+
+type DomainEventHandler struct {
+}
+
+func (h *DomainEventHandler) Type() backend.StoreType {
+	return backend.DOMAIN
+}
+
+func (h *DomainEventHandler) OnEvent(evt backend.KvEvent) {
+	action := evt.Type
+	switch action {
+	case pb.EVT_INIT, pb.EVT_CREATE:
+		metrics.ReportDomains(1)
+	case pb.EVT_DELETE:
+		metrics.ReportDomains(-1)
+	}
+}
+
+func NewDomainEventHandler() *DomainEventHandler {
+	return &DomainEventHandler{}
+}
diff --git a/server/service/event/event.go b/server/service/event/event.go
index 271d50be..91d5a116 100644
--- a/server/service/event/event.go
+++ b/server/service/event/event.go
@@ -21,6 +21,7 @@ import (
 )
 
 func init() {
+	backend.AddEventHandler(NewDomainEventHandler())
 	backend.AddEventHandler(NewServiceEventHandler())
 	backend.AddEventHandler(NewInstanceEventHandler())
 	backend.AddEventHandler(NewRuleEventHandler())
diff --git a/server/service/event/instance_event_handler.go b/server/service/event/instance_event_handler.go
index bd7e8eb7..b594e78e 100644
--- a/server/service/event/instance_event_handler.go
+++ b/server/service/event/instance_event_handler.go
@@ -22,6 +22,7 @@ import (
 	"github.com/apache/incubator-servicecomb-service-center/server/core/backend"
 	pb "github.com/apache/incubator-servicecomb-service-center/server/core/proto"
 	"github.com/apache/incubator-servicecomb-service-center/server/service/cache"
+	"github.com/apache/incubator-servicecomb-service-center/server/service/metrics"
 	nf "github.com/apache/incubator-servicecomb-service-center/server/service/notification"
 	serviceUtil "github.com/apache/incubator-servicecomb-service-center/server/service/util"
 	"golang.org/x/net/context"
@@ -37,12 +38,17 @@ func (h *InstanceEventHandler) Type() backend.StoreType {
 
 func (h *InstanceEventHandler) OnEvent(evt backend.KvEvent) {
 	action := evt.Type
-	if action == pb.EVT_INIT {
+	providerId, providerInstanceId, domainProject := backend.GetInfoFromInstKV(evt.KV)
+
+	switch action {
+	case pb.EVT_INIT:
+		metrics.ReportInstances(1)
 		return
-	}
+	case pb.EVT_CREATE:
+		metrics.ReportInstances(1)
+	case pb.EVT_DELETE:
+		metrics.ReportInstances(-1)
 
-	providerId, providerInstanceId, domainProject := backend.GetInfoFromInstKV(evt.KV)
-	if action == pb.EVT_DELETE {
 		splited := strings.Split(domainProject, "/")
 		if len(splited) == 2 && !apt.IsDefaultDomainProject(domainProject) {
 			domainName, projectName := splited[0], splited[1]
diff --git a/server/service/event/rule_event_handler.go b/server/service/event/rule_event_handler.go
index 4e6cf925..e0549233 100644
--- a/server/service/event/rule_event_handler.go
+++ b/server/service/event/rule_event_handler.go
@@ -18,7 +18,7 @@ package event
 
 import (
 	"fmt"
-	"github.com/apache/incubator-servicecomb-service-center/pkg/async"
+	"github.com/apache/incubator-servicecomb-service-center/pkg/task"
 	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
 	"github.com/apache/incubator-servicecomb-service-center/server/core/backend"
 	pb "github.com/apache/incubator-servicecomb-service-center/server/core/proto"
@@ -94,7 +94,7 @@ func (h *RuleEventHandler) OnEvent(evt backend.KvEvent) {
 	}
 	util.Logger().Infof("caught [%s] service rule event %s/%s", action, providerId, ruleId)
 
-	async.Service().Add(context.Background(),
+	task.Service().Add(context.Background(),
 		NewRulesChangedAsyncTask(domainProject, providerId, evt.Revision))
 }
 
diff --git a/server/service/event/service_event_handler.go b/server/service/event/service_event_handler.go
index dc17730c..613a087d 100644
--- a/server/service/event/service_event_handler.go
+++ b/server/service/event/service_event_handler.go
@@ -21,6 +21,7 @@ import (
 	"github.com/apache/incubator-servicecomb-service-center/server/core/backend"
 	pb "github.com/apache/incubator-servicecomb-service-center/server/core/proto"
 	"github.com/apache/incubator-servicecomb-service-center/server/service/cache"
+	"github.com/apache/incubator-servicecomb-service-center/server/service/metrics"
 	serviceUtil "github.com/apache/incubator-servicecomb-service-center/server/service/util"
 	"golang.org/x/net/context"
 	"strings"
@@ -36,15 +37,20 @@ func (h *ServiceEventHandler) Type() backend.StoreType {
 func (h *ServiceEventHandler) OnEvent(evt backend.KvEvent) {
 	ms := evt.KV.Value.(*pb.MicroService)
 	_, domainProject := backend.GetInfoFromSvcKV(evt.KV)
+	fn, fv := getFramework(ms)
 
 	switch evt.Type {
 	case pb.EVT_INIT, pb.EVT_CREATE:
+		metrics.ReportServices(fn, fv, 1)
+
 		newDomain := domainProject[:strings.Index(domainProject, "/")]
 		newProject := domainProject[strings.Index(domainProject, "/")+1:]
 		err := serviceUtil.NewDomainProject(context.Background(), newDomain, newProject)
 		if err != nil {
 			util.Logger().Errorf(err, "new domain(%s) or project(%s) failed", newDomain, newProject)
 		}
+	case pb.EVT_DELETE:
+		metrics.ReportServices(fn, fv, -1)
 	default:
 	}
 
@@ -60,6 +66,17 @@ func (h *ServiceEventHandler) OnEvent(evt backend.KvEvent) {
 	cache.FindInstances.Remove(providerKey)
 }
 
+func getFramework(ms *pb.MicroService) (string, string) {
+	if ms.Framework != nil && len(ms.Framework.Name) > 0 {
+		version := ms.Framework.Version
+		if len(ms.Framework.Version) == 0 {
+			version = "UNKNOWN"
+		}
+		return ms.Framework.Name, version
+	}
+	return "UNKNOWN", "UNKNOWN"
+}
+
 func NewServiceEventHandler() *ServiceEventHandler {
 	return &ServiceEventHandler{}
 }
diff --git a/server/service/event/tag_event_handler.go b/server/service/event/tag_event_handler.go
index fe09ad8d..8ad62c39 100644
--- a/server/service/event/tag_event_handler.go
+++ b/server/service/event/tag_event_handler.go
@@ -18,7 +18,7 @@ package event
 
 import (
 	"fmt"
-	"github.com/apache/incubator-servicecomb-service-center/pkg/async"
+	"github.com/apache/incubator-servicecomb-service-center/pkg/task"
 	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
 	"github.com/apache/incubator-servicecomb-service-center/server/core/backend"
 	pb "github.com/apache/incubator-servicecomb-service-center/server/core/proto"
@@ -107,7 +107,7 @@ func (h *TagEventHandler) OnEvent(evt backend.KvEvent) {
 	}
 	util.Logger().Infof("caught [%s] service tags event %s/%s", action, consumerId, evt.KV.Value)
 
-	async.Service().Add(context.Background(),
+	task.Service().Add(context.Background(),
 		NewTagsChangedAsyncTask(domainProject, consumerId, evt.Revision))
 }
 
diff --git a/server/service/instance_test.go b/server/service/instance_test.go
index 1e54a97f..978d9830 100644
--- a/server/service/instance_test.go
+++ b/server/service/instance_test.go
@@ -17,7 +17,6 @@
 package service_test
 
 import (
-	"fmt"
 	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
 	"github.com/apache/incubator-servicecomb-service-center/server/core"
 	pb "github.com/apache/incubator-servicecomb-service-center/server/core/proto"
@@ -1202,7 +1201,7 @@ var _ = Describe("'Instance' service", func() {
 				Expect(respFind.Instances[0].InstanceId).To(Equal(instanceId8))
 				Expect(reqRev).NotTo(Equal(0))
 
-				util.SetContext(ctx, serviceUtil.CTX_REQUEST_REVISION, fmt.Sprint(reqRev-1))
+				util.SetContext(ctx, serviceUtil.CTX_REQUEST_REVISION, strconv.FormatInt(reqRev-1, 10))
 				respFind, err = instanceResource.Find(ctx, &pb.FindInstancesRequest{
 					ConsumerServiceId: serviceId8,
 					AppId:             "query_instance",
@@ -1215,7 +1214,7 @@ var _ = Describe("'Instance' service", func() {
 				Expect(respFind.Instances[0].InstanceId).To(Equal(instanceId8))
 				Expect(ctx.Value(serviceUtil.CTX_RESPONSE_REVISION)).To(Equal(rev))
 
-				util.SetContext(ctx, serviceUtil.CTX_REQUEST_REVISION, fmt.Sprint(reqRev+1))
+				util.SetContext(ctx, serviceUtil.CTX_REQUEST_REVISION, strconv.FormatInt(reqRev+1, 10))
 				respFind, err = instanceResource.Find(ctx, &pb.FindInstancesRequest{
 					ConsumerServiceId: serviceId8,
 					AppId:             "query_instance",
@@ -1240,7 +1239,7 @@ var _ = Describe("'Instance' service", func() {
 				Expect(len(respFind.Instances)).To(Equal(0))
 				Expect(ctx.Value(serviceUtil.CTX_RESPONSE_REVISION)).To(Equal(rev))
 
-				By("find should return 200 even if consumer permission deny")
+				By("find should return 200 even if consumer is diff apps")
 				respFind, err = instanceResource.Find(getContext(), &pb.FindInstancesRequest{
 					ConsumerServiceId: serviceId3,
 					AppId:             "query_instance",
diff --git a/server/service/metrics/metrics.go b/server/service/metrics/metrics.go
new file mode 100644
index 00000000..48189575
--- /dev/null
+++ b/server/service/metrics/metrics.go
@@ -0,0 +1,67 @@
+/*
+ * 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 metrics
+
+import (
+	"github.com/apache/incubator-servicecomb-service-center/server/metric"
+	"github.com/prometheus/client_golang/prometheus"
+)
+
+var (
+	domainCounter = prometheus.NewGaugeVec(
+		prometheus.GaugeOpts{
+			Namespace: metric.FamilyName,
+			Subsystem: "db",
+			Name:      "domain_total",
+			Help:      "Gauge of domain created in Service Center",
+		}, []string{"instance"})
+
+	serviceCounter = prometheus.NewGaugeVec(
+		prometheus.GaugeOpts{
+			Namespace: metric.FamilyName,
+			Subsystem: "db",
+			Name:      "service_total",
+			Help:      "Gauge of microservice created in Service Center",
+		}, []string{"instance", "framework", "frameworkVersion"})
+
+	instanceCounter = prometheus.NewGaugeVec(
+		prometheus.GaugeOpts{
+			Namespace: metric.FamilyName,
+			Subsystem: "db",
+			Name:      "instance_total",
+			Help:      "Gauge of microservice created in Service Center",
+		}, []string{"instance"})
+)
+
+func init() {
+	prometheus.MustRegister(domainCounter, serviceCounter, instanceCounter)
+}
+
+func ReportDomains(c float64) {
+	instance := metric.InstanceName()
+	domainCounter.WithLabelValues(instance).Add(c)
+}
+
+func ReportServices(framework, frameworkVersion string, c float64) {
+	instance := metric.InstanceName()
+	serviceCounter.WithLabelValues(instance, framework, frameworkVersion).Add(c)
+}
+
+func ReportInstances(c float64) {
+	instance := metric.InstanceName()
+	instanceCounter.WithLabelValues(instance).Add(c)
+}
diff --git a/server/service/notification/common.go b/server/service/notification/common.go
index 7b0728cf..1617f6fb 100644
--- a/server/service/notification/common.go
+++ b/server/service/notification/common.go
@@ -22,9 +22,10 @@ import (
 )
 
 const (
-	DEFAULT_MAX_QUEUE       = 1000
-	DEFAULT_ADD_JOB_TIMEOUT = 1 * time.Second
-	DEFAULT_SEND_TIMEOUT    = 30 * time.Second
+	DEFAULT_MAX_QUEUE          = 1000
+	DEFAULT_ADD_JOB_TIMEOUT    = 1 * time.Second
+	DEFAULT_SEND_TIMEOUT       = 5 * time.Second
+	DEFAULT_HEARTBEAT_INTERVAL = 30 * time.Second
 )
 
 const (
diff --git a/server/service/notification/notification_service.go b/server/service/notification/notification_service.go
index a5ea4ea4..4f649815 100644
--- a/server/service/notification/notification_service.go
+++ b/server/service/notification/notification_service.go
@@ -141,7 +141,7 @@ func (s *NotifyService) Stop() {
 
 	close(s.err)
 
-	util.Logger().Debug("notify service stopped.")
+	util.Logger().Debug("notify service stopped")
 }
 
 func GetNotifyService() *NotifyService {
diff --git a/server/service/notification/stream.go b/server/service/notification/stream.go
index b9bbea27..f215553b 100644
--- a/server/service/notification/stream.go
+++ b/server/service/notification/stream.go
@@ -24,12 +24,12 @@ import (
 )
 
 func HandleWatchJob(watcher *ListWatcher, stream pb.ServiceInstanceCtrl_WatchServer) (err error) {
-	timer := time.NewTimer(DEFAULT_SEND_TIMEOUT)
+	timer := time.NewTimer(DEFAULT_HEARTBEAT_INTERVAL)
 	defer timer.Stop()
 	for {
 		select {
 		case <-timer.C:
-			timer.Reset(DEFAULT_SEND_TIMEOUT)
+			timer.Reset(DEFAULT_HEARTBEAT_INTERVAL)
 
 			// TODO grpc 长连接心跳?
 		case job := <-watcher.Job:
@@ -51,7 +51,7 @@ func HandleWatchJob(watcher *ListWatcher, stream pb.ServiceInstanceCtrl_WatchSer
 				return
 			}
 
-			util.ResetTimer(timer, DEFAULT_SEND_TIMEOUT)
+			util.ResetTimer(timer, DEFAULT_HEARTBEAT_INTERVAL)
 		}
 	}
 }
diff --git a/server/service/notification/websocket.go b/server/service/notification/websocket.go
index 362d7e2b..85c5e2fd 100644
--- a/server/service/notification/websocket.go
+++ b/server/service/notification/websocket.go
@@ -29,20 +29,27 @@ import (
 )
 
 type WebSocket struct {
-	ctx             context.Context
-	ticker          *time.Ticker
-	conn            *websocket.Conn
+	ctx    context.Context
+	ticker *time.Ticker
+	conn   *websocket.Conn
+	// watcher subscribe the notification service event
 	watcher         *ListWatcher
 	needPingWatcher bool
+	free            chan struct{}
 	closed          chan struct{}
 }
 
 func (wh *WebSocket) Init() error {
-	wh.ticker = time.NewTicker(wh.Timeout())
+	wh.ticker = time.NewTicker(DEFAULT_HEARTBEAT_INTERVAL)
+	wh.needPingWatcher = true
+	wh.free = make(chan struct{}, 1)
+	wh.closed = make(chan struct{})
+
+	wh.SetReady()
 
 	remoteAddr := wh.conn.RemoteAddr().String()
 
-	// subscribe backend
+	// put in notification service queue
 	if err := GetNotifyService().AddSubscriber(wh.watcher); err != nil {
 		err = fmt.Errorf("establish[%s] websocket watch failed: notify service error, %s",
 			remoteAddr, err.Error())
@@ -55,7 +62,7 @@ func (wh *WebSocket) Init() error {
 		return err
 	}
 
-	// publish events
+	// put in publisher queue
 	publisher.Accept(wh)
 
 	util.Logger().Debugf("start watching instance status, watcher[%s], subject: %s, group: %s",
@@ -83,8 +90,6 @@ func (wh *WebSocket) heartbeat(messageType int) error {
 }
 
 func (wh *WebSocket) HandleWatchWebSocketControlMessage() {
-	defer close(wh.closed)
-
 	remoteAddr := wh.conn.RemoteAddr().String()
 	// PING
 	wh.conn.SetPingHandler(func(message string) error {
@@ -103,14 +108,15 @@ func (wh *WebSocket) HandleWatchWebSocketControlMessage() {
 	})
 	// CLOSE
 	wh.conn.SetCloseHandler(func(code int, text string) error {
-		util.Logger().Warnf(nil, "watcher[%s] active closed, subject: %s, group: %s",
-			remoteAddr, wh.watcher.Subject(), wh.watcher.Group())
+		util.Logger().Infof("watcher[%s] active closed, code: %d, message: '%s', subject: %s, group: %s",
+			remoteAddr, code, text, wh.watcher.Subject(), wh.watcher.Group())
 		return wh.sendClose(code, text)
 	})
 
 	for {
 		_, _, err := wh.conn.ReadMessage()
 		if err != nil {
+			// client close or conn broken
 			wh.watcher.SetError(err)
 			return
 		}
@@ -132,24 +138,36 @@ func (wh *WebSocket) sendClose(code int, text string) error {
 	return nil
 }
 
+// Pick will be called by publisher
 func (wh *WebSocket) Pick() interface{} {
-	if wh.watcher.Err() != nil {
-		return wh.watcher.Err()
-	}
 	select {
-	case t := <-wh.ticker.C:
-		return t
-	case j := <-wh.watcher.Job:
-		if j == nil {
-			return fmt.Errorf("server shutdown")
+	case <-wh.Ready():
+		if wh.watcher.Err() != nil {
+			return wh.watcher.Err()
+		}
+
+		select {
+		case t := <-wh.ticker.C:
+			return t
+		case j := <-wh.watcher.Job:
+			if j == nil {
+				return fmt.Errorf("server shutdown")
+			}
+			return j
+		default:
+			// reset if idle
+			wh.SetReady()
 		}
-		return j
 	default:
-		return nil
 	}
+
+	return nil
 }
 
+// HandleWatchWebSocketJob will be called if Pick() returns not nil
 func (wh *WebSocket) HandleWatchWebSocketJob(o interface{}) {
+	defer wh.SetReady()
+
 	remoteAddr := wh.conn.RemoteAddr().String()
 	var message []byte
 
@@ -163,9 +181,7 @@ func (wh *WebSocket) HandleWatchWebSocketJob(o interface{}) {
 	case time.Time:
 		domainProject := util.ParseDomainProject(wh.ctx)
 		if !serviceUtil.ServiceExist(wh.ctx, domainProject, wh.watcher.Group()) {
-			err := fmt.Errorf("Service does not exit.")
-			wh.watcher.SetError(err)
-			message = util.StringToBytesWithNoCopy(err.Error())
+			message = util.StringToBytesWithNoCopy("Service does not exit.")
 			break
 		}
 
@@ -175,10 +191,7 @@ func (wh *WebSocket) HandleWatchWebSocketJob(o interface{}) {
 
 		util.Logger().Debugf("send 'Ping' message to watcher[%s], subject: %s, group: %s",
 			remoteAddr, wh.watcher.Subject(), wh.watcher.Group())
-		err := wh.heartbeat(websocket.PingMessage)
-		if err != nil {
-			wh.watcher.SetError(err)
-		}
+		wh.heartbeat(websocket.PingMessage)
 		return
 	case *WatchJob:
 		job := o.(*WatchJob)
@@ -194,13 +207,15 @@ func (wh *WebSocket) HandleWatchWebSocketJob(o interface{}) {
 		resp.Response = nil
 		data, err := json.Marshal(resp)
 		if err != nil {
-			wh.watcher.SetError(err)
+			util.Logger().Errorf(err, "watcher[%s] watch %s, subject: %s, group: %s",
+				remoteAddr, providerFlag, o, wh.watcher.Subject(), wh.watcher.Group())
 			message = util.StringToBytesWithNoCopy(fmt.Sprintf("marshal output file error, %s", err.Error()))
 			break
 		}
 		message = data
 	default:
-		wh.watcher.SetError(fmt.Errorf("unknown input %v", o))
+		util.Logger().Errorf(nil, "watcher[%s] unknown input %v, subject: %s, group: %s",
+			remoteAddr, o, wh.watcher.Subject(), wh.watcher.Group())
 		return
 	}
 
@@ -217,14 +232,27 @@ func (wh *WebSocket) HandleWatchWebSocketJob(o interface{}) {
 	}
 }
 
+func (wh *WebSocket) Ready() <-chan struct{} {
+	return wh.free
+}
+
+func (wh *WebSocket) SetReady() {
+	select {
+	case wh.free <- struct{}{}:
+	default:
+	}
+}
+
+func (wh *WebSocket) Stop() {
+	close(wh.closed)
+}
+
 func DoWebSocketListAndWatch(ctx context.Context, serviceId string, f func() ([]*pb.WatchInstanceResponse, int64), conn *websocket.Conn) {
 	domainProject := util.ParseDomainProject(ctx)
 	socket := &WebSocket{
-		ctx:             ctx,
-		conn:            conn,
-		watcher:         NewListWatcher(serviceId, apt.GetInstanceRootKey(domainProject)+"/", f),
-		needPingWatcher: true,
-		closed:          make(chan struct{}),
+		ctx:     ctx,
+		conn:    conn,
+		watcher: NewListWatcher(serviceId, apt.GetInstanceRootKey(domainProject)+"/", f),
 	}
 	process(socket)
 }
@@ -233,7 +261,10 @@ func process(socket *WebSocket) {
 	if err := socket.Init(); err != nil {
 		return
 	}
+
 	socket.HandleWatchWebSocketControlMessage()
+
+	socket.Stop()
 }
 
 func EstablishWebSocketError(conn *websocket.Conn, err error) {
diff --git a/server/service/notification/websocket_test.go b/server/service/notification/websocket_test.go
index d6d011fb..5e23f815 100644
--- a/server/service/notification/websocket_test.go
+++ b/server/service/notification/websocket_test.go
@@ -19,6 +19,7 @@ package notification
 import (
 	"context"
 	"errors"
+	"github.com/apache/incubator-servicecomb-service-center/server/core"
 	"github.com/apache/incubator-servicecomb-service-center/server/core/proto"
 	_ "github.com/apache/incubator-servicecomb-service-center/server/plugin/infra/registry/buildin"
 	"github.com/gorilla/websocket"
@@ -31,6 +32,10 @@ import (
 
 var closeCh = make(chan struct{})
 
+func init() {
+	core.Initialize()
+}
+
 type watcherConn struct {
 }
 
@@ -66,11 +71,9 @@ func TestDoWebSocketListAndWatch(t *testing.T) {
 			return
 		})
 		ws2 := &WebSocket{
-			ctx:             context.Background(),
-			conn:            conn,
-			watcher:         w2,
-			needPingWatcher: true,
-			closed:          make(chan struct{}),
+			ctx:     context.Background(),
+			conn:    conn,
+			watcher: w2,
 		}
 		err := ws2.Init()
 		if err != nil {
@@ -92,11 +95,9 @@ func TestDoWebSocketListAndWatch(t *testing.T) {
 	w.nType = -1
 
 	ws := &WebSocket{
-		ctx:             context.Background(),
-		conn:            conn,
-		watcher:         w,
-		needPingWatcher: true,
-		closed:          make(chan struct{}),
+		ctx:     context.Background(),
+		conn:    conn,
+		watcher: w,
 	}
 	err := ws.Init()
 	if err == nil {
diff --git a/server/service/rule_test.go b/server/service/rule_test.go
index 2a0950c3..6dbe8370 100644
--- a/server/service/rule_test.go
+++ b/server/service/rule_test.go
@@ -17,7 +17,6 @@
 package service_test
 
 import (
-	"fmt"
 	pb "github.com/apache/incubator-servicecomb-service-center/server/core/proto"
 	scerr "github.com/apache/incubator-servicecomb-service-center/server/error"
 	"github.com/apache/incubator-servicecomb-service-center/server/plugin/infra/quota/buildin"
@@ -538,7 +537,7 @@ var _ = Describe("'Rule' service", func() {
 				By("rules is invalid")
 				var arr []string
 				for i := 0; i < buildin.RULE_NUM_MAX_LIMIT_PER_SERVICE+1; i++ {
-					arr = append(arr, fmt.Sprint(i))
+					arr = append(arr, strconv.Itoa(i))
 				}
 				respAddRule, err = serviceResource.DeleteRule(getContext(), &pb.DeleteServiceRulesRequest{
 					ServiceId: serviceId,
@@ -581,7 +580,7 @@ var _ = Describe("'Rule' service", func() {
 			respCreate, err := serviceResource.Create(getContext(), &pb.CreateServiceRequest{
 				Service: &pb.MicroService{
 					AppId:       "query_instance_tag",
-					ServiceName: "query_instance_tag_service",
+					ServiceName: "query_instance_version_consumer",
 					Version:     "1.0.0",
 					Level:       "FRONT",
 					Status:      pb.MS_UP,
@@ -656,7 +655,7 @@ var _ = Describe("'Rule' service", func() {
 			respCreate, err = serviceResource.Create(getContext(), &pb.CreateServiceRequest{
 				Service: &pb.MicroService{
 					AppId:       "query_instance_tag",
-					ServiceName: "query_instance_tag_service",
+					ServiceName: "query_instance_tag_consumer",
 					Version:     "1.0.4",
 					Level:       "FRONT",
 					Status:      pb.MS_UP,
@@ -692,6 +691,26 @@ var _ = Describe("'Rule' service", func() {
 				Expect(err).To(BeNil())
 				Expect(resp.Response.Code).To(Equal(scerr.ErrPermissionDeny))
 
+				By("find should return 200 even if consumer permission deny")
+				respFind, err := instanceResource.Find(getContext(), &pb.FindInstancesRequest{
+					ConsumerServiceId: consumerVersion,
+					AppId:             "query_instance_tag",
+					ServiceName:       "query_instance_tag_service",
+					VersionRule:       "0+",
+				})
+				Expect(err).To(BeNil())
+				Expect(respFind.Response.Code).To(Equal(pb.Response_SUCCESS))
+				Expect(len(respFind.Instances)).To(Equal(0))
+				respFind, err = instanceResource.Find(getContext(), &pb.FindInstancesRequest{
+					ConsumerServiceId: consumerTag,
+					AppId:             "query_instance_tag",
+					ServiceName:       "query_instance_tag_service",
+					VersionRule:       "0+",
+				})
+				Expect(err).To(BeNil())
+				Expect(respFind.Response.Code).To(Equal(pb.Response_SUCCESS))
+				Expect(len(respFind.Instances)).To(Equal(0))
+
 				By("consumer not in black list")
 				resp, err = instanceResource.GetInstances(getContext(), &pb.GetInstancesRequest{
 					ConsumerServiceId: providerWhite,
diff --git a/server/service/service_suite_test.go b/server/service/service_suite_test.go
index 4b4e6e6d..09c0a202 100644
--- a/server/service/service_suite_test.go
+++ b/server/service/service_suite_test.go
@@ -16,14 +16,13 @@
  */
 package service_test
 
+// initialize
+import _ "github.com/apache/incubator-servicecomb-service-center/server/init"
+import _ "github.com/apache/incubator-servicecomb-service-center/server/bootstrap"
 import (
 	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
+	"github.com/apache/incubator-servicecomb-service-center/server/core"
 	pb "github.com/apache/incubator-servicecomb-service-center/server/core/proto"
-	_ "github.com/apache/incubator-servicecomb-service-center/server/plugin/infra/quota/buildin"
-	_ "github.com/apache/incubator-servicecomb-service-center/server/plugin/infra/quota/unlimit"
-	_ "github.com/apache/incubator-servicecomb-service-center/server/plugin/infra/registry/etcd"
-	_ "github.com/apache/incubator-servicecomb-service-center/server/plugin/infra/tracing/buildin"
-	_ "github.com/apache/incubator-servicecomb-service-center/server/plugin/infra/uuid/buildin"
 	"github.com/apache/incubator-servicecomb-service-center/server/service"
 	serviceUtil "github.com/apache/incubator-servicecomb-service-center/server/service/util"
 	. "github.com/onsi/ginkgo"
@@ -38,6 +37,7 @@ var instanceResource pb.SerivceInstanceCtrlServerEx
 
 var _ = BeforeSuite(func() {
 	//init plugin
+	core.ServerInfo.Config.EnableCache = false
 	serviceResource, instanceResource = service.AssembleResources()
 })
 
diff --git a/server/service/tag_test.go b/server/service/tag_test.go
index 3cdd30fb..322ce9b1 100644
--- a/server/service/tag_test.go
+++ b/server/service/tag_test.go
@@ -17,7 +17,6 @@
 package service_test
 
 import (
-	"fmt"
 	pb "github.com/apache/incubator-servicecomb-service-center/server/core/proto"
 	scerr "github.com/apache/incubator-servicecomb-service-center/server/error"
 	"github.com/apache/incubator-servicecomb-service-center/server/plugin/infra/quota/buildin"
@@ -515,7 +514,7 @@ var _ = Describe("'Tag' service", func() {
 
 				var arr []string
 				for i := 0; i < buildin.TAG_NUM_MAX_LIMIT_PER_SERVICE+1; i++ {
-					arr = append(arr, fmt.Sprint(i))
+					arr = append(arr, strconv.Itoa(i))
 				}
 				respAddTags, err = serviceResource.DeleteTags(getContext(), &pb.DeleteServiceTagsRequest{
 					ServiceId: serviceId,
diff --git a/server/service/util/dependency_query.go b/server/service/util/dependency_query.go
index 15bff785..d7f10293 100644
--- a/server/service/util/dependency_query.go
+++ b/server/service/util/dependency_query.go
@@ -115,7 +115,7 @@ func (dr *DependencyRelation) GetDependencyProviderIds() ([]string, error) {
 
 func (dr *DependencyRelation) getProviderKeys() ([]*pb.MicroServiceKey, error) {
 	if dr.consumer == nil {
-		util.LOGGER.Infof("dr.consumer is nil ------->")
+		util.Logger().Infof("dr.consumer is nil ------->")
 		return nil, fmt.Errorf("Invalid consumer")
 	}
 	consumerMicroServiceKey := pb.MicroServiceToKey(dr.domainProject, dr.consumer)
@@ -247,7 +247,7 @@ func (dr *DependencyRelation) GetDependencyConsumerIds() ([]string, error) {
 
 func (dr *DependencyRelation) getDependencyConsumersOfProvider() ([]*pb.MicroServiceKey, error) {
 	if dr.provider == nil {
-		util.LOGGER.Infof("dr.provider is nil ------->")
+		util.Logger().Infof("dr.provider is nil ------->")
 		return nil, fmt.Errorf("Invalid provider")
 	}
 	providerService := pb.MicroServiceToKey(dr.domainProject, dr.provider)
diff --git a/server/service/util/domain_util.go b/server/service/util/domain_util.go
index ede7ca7f..bd529ca0 100644
--- a/server/service/util/domain_util.go
+++ b/server/service/util/domain_util.go
@@ -82,30 +82,30 @@ func ProjectExist(ctx context.Context, domain, project string) (bool, error) {
 	return rsp.Count > 0, nil
 }
 
-func NewDomain(ctx context.Context, domain string) error {
-	_, err := backend.Registry().PutNoOverride(ctx,
+func NewDomain(ctx context.Context, domain string) (bool, error) {
+	ok, err := backend.Registry().PutNoOverride(ctx,
 		registry.WithStrKey(apt.GenerateDomainKey(domain)))
 	if err != nil {
-		return err
+		return false, err
 	}
-	return nil
+	return ok, nil
 }
 
-func NewProject(ctx context.Context, domain, project string) error {
-	_, err := backend.Registry().PutNoOverride(ctx,
+func NewProject(ctx context.Context, domain, project string) (bool, error) {
+	ok, err := backend.Registry().PutNoOverride(ctx,
 		registry.WithStrKey(apt.GenerateProjectKey(domain, project)))
 	if err != nil {
-		return err
+		return ok, err
 	}
-	return nil
+	return ok, nil
 }
 
 func NewDomainProject(ctx context.Context, domain, project string) error {
 	copyCtx := util.SetContext(util.CloneContext(ctx), CTX_CACHEONLY, "1")
 	ok, err := DomainExist(copyCtx, domain)
 	if !ok && err == nil {
-		err = NewDomain(ctx, domain)
-		if err == nil {
+		ok, err = NewDomain(ctx, domain)
+		if ok {
 			util.Logger().Infof("new domain(%s)", domain)
 		}
 	}
@@ -114,8 +114,8 @@ func NewDomainProject(ctx context.Context, domain, project string) error {
 	}
 	ok, err = ProjectExist(copyCtx, domain, project)
 	if !ok && err == nil {
-		err = NewProject(ctx, domain, project)
-		if err == nil {
+		ok, err = NewProject(ctx, domain, project)
+		if ok {
 			util.Logger().Infof("new project(%s/%s)", domain, project)
 		}
 	}
diff --git a/server/service/util/domain_util_test.go b/server/service/util/domain_util_test.go
index a86dc8f3..39b48920 100644
--- a/server/service/util/domain_util_test.go
+++ b/server/service/util/domain_util_test.go
@@ -55,7 +55,7 @@ func TestDomainExist(t *testing.T) {
 }
 
 func TestNewDomain(t *testing.T) {
-	err := NewDomain(context.Background(), "")
+	_, err := NewDomain(context.Background(), "")
 	if err == nil {
 		t.Fatalf("NewDomain failed")
 	}
@@ -69,7 +69,7 @@ func TestProjectExist(t *testing.T) {
 }
 
 func TestNewProject(t *testing.T) {
-	err := NewProject(context.Background(), "", "")
+	_, err := NewProject(context.Background(), "", "")
 	if err == nil {
 		t.Fatalf("NewProject failed")
 	}
diff --git a/server/service/watch.go b/server/service/watch.go
index b8093099..97fd9c36 100644
--- a/server/service/watch.go
+++ b/server/service/watch.go
@@ -52,7 +52,7 @@ func (s *InstanceService) Watch(in *pb.WatchInstanceRequest, stream pb.ServiceIn
 }
 
 func (s *InstanceService) WebSocketWatch(ctx context.Context, in *pb.WatchInstanceRequest, conn *websocket.Conn) {
-	util.Logger().Infof("New a web socket watch with %s", in.SelfServiceId)
+	util.Logger().Infof("new a web socket watch with %s", in.SelfServiceId)
 	if err := s.WatchPreOpera(ctx, in); err != nil {
 		nf.EstablishWebSocketError(conn, err)
 		return
@@ -61,7 +61,7 @@ func (s *InstanceService) WebSocketWatch(ctx context.Context, in *pb.WatchInstan
 }
 
 func (s *InstanceService) WebSocketListAndWatch(ctx context.Context, in *pb.WatchInstanceRequest, conn *websocket.Conn) {
-	util.Logger().Infof("New a web socket list and watch with %s", in.SelfServiceId)
+	util.Logger().Infof("new a web socket list and watch with %s", in.SelfServiceId)
 	if err := s.WatchPreOpera(ctx, in); err != nil {
 		nf.EstablishWebSocketError(conn, err)
 		return
diff --git a/server/version.go b/server/version.go
new file mode 100644
index 00000000..507bef64
--- /dev/null
+++ b/server/version.go
@@ -0,0 +1,57 @@
+/*
+ * 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 server
+
+import (
+	"encoding/json"
+	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
+	"github.com/apache/incubator-servicecomb-service-center/server/core"
+	"github.com/apache/incubator-servicecomb-service-center/server/core/backend"
+	"github.com/apache/incubator-servicecomb-service-center/server/infra/registry"
+	"golang.org/x/net/context"
+)
+
+func LoadServerVersion() error {
+	resp, err := backend.Registry().Do(context.Background(),
+		registry.GET, registry.WithStrKey(core.GetServerInfoKey()))
+	if err != nil {
+		return err
+	}
+	if len(resp.Kvs) == 0 {
+		return nil
+	}
+
+	err = json.Unmarshal(resp.Kvs[0].Value, &core.ServerInfo)
+	if err != nil {
+		util.Logger().Errorf(err, "load server version failed, maybe incompatible")
+		return nil
+	}
+	return nil
+}
+
+func UpgradeServerVersion() error {
+	bytes, err := json.Marshal(core.ServerInfo)
+	if err != nil {
+		return err
+	}
+	_, err = backend.Registry().Do(context.Background(),
+		registry.PUT, registry.WithStrKey(core.GetServerInfoKey()), registry.WithValue(bytes))
+	if err != nil {
+		return err
+	}
+	return nil
+}


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@infra.apache.org


With regards,
Apache Git Services