You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@servicecomb.apache.org by as...@apache.org on 2018/07/07 15:36:36 UTC

[incubator-servicecomb-service-center] branch master updated: SCB-680 Optimize find instance api (#387)

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

asifdxtreme pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-servicecomb-service-center.git


The following commit(s) were added to refs/heads/master by this push:
     new d085bc4  SCB-680 Optimize find instance api (#387)
d085bc4 is described below

commit d085bc48e18c30647bdc9d7e00b556e88b309514
Author: little-cui <su...@qq.com>
AuthorDate: Sat Jul 7 23:36:34 2018 +0800

    SCB-680 Optimize find instance api (#387)
    
    * Reconstruct core package
    
    * Update other packages
    
    * Fix some bugs
    
    * Optimize indexer
    
    * Fix UT failure
    
    * Optimize indexer and cache
    
    * Add cache tree package
    
    * Fix some Bugs
    
    * Release indexer when renew one
    
    * Optimize notification package
    
    * Optimize websocket package
    
    * Fix the subscriber map leak
    
    * Restructure notification package
    
    * Optimize notification package
    
    * Fix cache do get updating bug.
    
    * Add cache metrics
    
    * Fix timer bug and cache bug
    
    * Fix UT failure
    
    * Add notification UT
---
 pkg/async/service.go                               |  17 +-
 .../util/schema_util_test.go => pkg/cache/cache.go |  33 +-
 pkg/cache/cache_test.go                            | 178 +++++
 .../service/util/common.go => pkg/cache/config.go  |  38 +-
 .../core/backend/cacher.go => pkg/cache/filter.go  |  19 +-
 .../backend/extend_test.go => pkg/cache/node.go    |  45 +-
 .../core/backend/cacher.go => pkg/cache/option.go  |  31 +-
 .../backend/cacher.go => pkg/cache/option_test.go  |  27 +-
 pkg/cache/tree.go                                  | 160 ++++
 pkg/util/concurrent_map.go                         |  27 +-
 pkg/util/concurrent_map_test.go                    |  10 +-
 pkg/util/goroutines.go                             |   8 +-
 pkg/util/util.go                                   |  16 +
 pkg/util/util_test.go                              |  10 +
 server/broker/service.go                           |  22 +-
 server/broker/store.go                             |  28 +-
 server/broker/util.go                              |  18 +-
 server/core/backend/{cacher.go => cache.go}        |  13 +-
 server/core/backend/cache_kv.go                    | 534 +++----------
 server/core/backend/cache_null.go                  |  50 +-
 .../core/backend/{cacher_test.go => cache_test.go} |  76 +-
 server/core/backend/{cache_kv.go => cacher_kv.go}  | 362 ++++-----
 server/core/backend/common.go                      | 123 +--
 server/core/backend/config.go                      |  57 +-
 server/core/backend/defer_instance.go              |  44 +-
 server/core/backend/defer_test.go                  |  77 +-
 server/core/backend/event.go                       |   4 +-
 server/core/backend/event_proxy.go                 |   4 +-
 server/core/backend/extend.go                      |  37 +-
 server/core/backend/extend_test.go                 |   8 +-
 server/core/backend/indexer.go                     | 350 +--------
 server/core/backend/indexer_kv.go                  | 150 ++++
 server/core/backend/{cacher.go => kv.go}           |  31 +-
 server/core/backend/lease.go                       |  14 +-
 server/core/backend/listwatch.go                   |  52 +-
 server/core/backend/parser.go                      | 206 +++++
 server/core/backend/parser_test.go                 |  80 ++
 server/core/backend/store.go                       | 113 +--
 server/core/key_generator.go                       |  76 +-
 server/core/proto/services.go                      | 127 +--
 server/govern/service.go                           |  12 +-
 server/plugin/infra/quota/buildin/buildin.go       |   4 +-
 server/plugin/infra/registry/etcd/etcd.go          |   3 +-
 server/server.go                                   |   5 -
 server/service/{util => cache}/common.go           |  15 +-
 server/service/cache/dependency.go                 |  64 ++
 .../filter_consumer.go}                            |  24 +-
 server/service/cache/filter_instances.go           |  72 ++
 .../filter_permission.go}                          |  52 +-
 .../tag_util_test.go => cache/filter_service.go}   |  31 +-
 server/service/cache/filter_tags.go                |  83 ++
 .../filter_version.go}                             |  51 +-
 server/service/cache/instance.go                   |  73 ++
 server/service/event/dependency_event_handler.go   |  25 +-
 server/service/event/instance_event_handler.go     |  58 +-
 server/service/event/rule_event_handler.go         |  32 +-
 server/service/event/service_event_handler.go      |  37 +-
 server/service/event/tag_event_handler.go          |  49 +-
 server/service/instance.go                         | 117 ++-
 server/service/instance_test.go                    |   6 +-
 server/service/microservice.go                     |   2 +-
 server/service/notification/common.go              |  32 +-
 .../tag_util_test.go => notification/group.go}     |  52 +-
 server/service/notification/group_test.go          |  69 ++
 server/service/notification/listwatcher.go         |  81 +-
 server/service/notification/notice.go              |  22 +-
 .../notification/notification_healthchecker.go     |  18 +-
 .../service/notification/notification_service.go   | 233 ++----
 server/service/notification/notification_test.go   |  86 ++
 server/service/notification/processor.go           | 106 +++
 server/service/notification/processor_test.go      |  74 ++
 server/service/notification/publisher.go           | 103 +++
 server/service/notification/stream.go              |  23 +-
 .../stream_test.go}                                |  27 +-
 server/service/notification/subject.go             |  76 ++
 server/service/notification/subject_test.go        |  65 ++
 server/service/notification/subscriber.go          |  63 +-
 server/service/notification/websocket.go           | 259 +++---
 server/service/notification/websocket_test.go      | 120 +++
 server/service/schema.go                           |  16 +-
 server/service/tag_test.go                         |  10 +-
 server/service/util/common.go                      |   4 -
 server/service/util/dependency.go                  | 867 ---------------------
 server/service/util/dependency_query.go            | 351 +++++++++
 server/service/util/dependency_test.go             | 167 +---
 server/service/util/dependency_util.go             | 427 ++++++++++
 server/service/util/domain_util.go                 |   3 +-
 server/service/util/domain_util_test.go            |  37 +-
 server/service/util/find_cache.go                  |  71 --
 server/service/util/find_cache_test.go             |  60 --
 server/service/util/instance_util.go               | 112 +--
 server/service/util/instance_util_test.go          |  59 +-
 server/service/util/microservice_util.go           |  44 +-
 server/service/util/rule_util.go                   |  54 +-
 server/service/util/rule_util_test.go              |  62 +-
 server/service/util/schema_util_test.go            |  10 +-
 server/service/util/tag_util.go                    |  10 +-
 server/service/util/tag_util_test.go               |   8 +-
 server/service/util/util_suite_test.go             | 138 +---
 server/service/util/versionrule.go                 |  37 +-
 server/service/util/versionrule_test.go            |  85 +-
 server/service/watch.go                            |   4 +-
 102 files changed, 4235 insertions(+), 4000 deletions(-)

diff --git a/pkg/async/service.go b/pkg/async/service.go
index b83b8ea..3551e56 100644
--- a/pkg/async/service.go
+++ b/pkg/async/service.go
@@ -26,10 +26,10 @@ import (
 )
 
 const (
-	maxExecutorCount       = 1000
+	initExecutorCount      = 1000
 	removeExecutorInterval = 30 * time.Second
 	initExecutorTTL        = 4
-	executeInterval        = 100 * time.Millisecond
+	executeInterval        = 1 * time.Second
 	compactTimes           = 2
 )
 
@@ -107,12 +107,11 @@ func (lat *TaskService) daemon(ctx context.Context) {
 	util.SafeCloseChan(lat.ready)
 	ticker := time.NewTicker(removeExecutorInterval)
 	max := 0
+	timer := time.NewTimer(executeInterval)
+	defer timer.Stop()
 	for {
-		timer := time.NewTimer(executeInterval)
 		select {
 		case <-ctx.Done():
-			timer.Stop()
-
 			util.Logger().Debugf("daemon thread exited for TaskService is stopped")
 			return
 		case <-timer.C:
@@ -127,8 +126,10 @@ func (lat *TaskService) daemon(ctx context.Context) {
 			for _, s := range slice {
 				s.Execute() // non-blocked
 			}
+
+			timer.Reset(executeInterval)
 		case <-ticker.C:
-			timer.Stop()
+			util.ResetTimer(timer, executeInterval)
 
 			lat.lock.RLock()
 			l := len(lat.executors)
@@ -154,7 +155,7 @@ func (lat *TaskService) daemon(ctx context.Context) {
 			}
 
 			l = len(lat.executors)
-			if max > maxExecutorCount && max > l*compactTimes {
+			if max > initExecutorCount && max > l*compactTimes {
 				lat.renew()
 				max = l
 			}
@@ -200,7 +201,7 @@ func (lat *TaskService) Ready() <-chan struct{} {
 }
 
 func (lat *TaskService) renew() {
-	newExecutor := make(map[string]*executorWithTTL, maxExecutorCount)
+	newExecutor := make(map[string]*executorWithTTL)
 	for k, e := range lat.executors {
 		newExecutor[k] = e
 	}
diff --git a/server/service/util/schema_util_test.go b/pkg/cache/cache.go
similarity index 68%
copy from server/service/util/schema_util_test.go
copy to pkg/cache/cache.go
index 7ba7146..7f53552 100644
--- a/server/service/util/schema_util_test.go
+++ b/pkg/cache/cache.go
@@ -14,22 +14,33 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package util
+package cache
 
 import (
-	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
 	"golang.org/x/net/context"
-	"testing"
+	"sync"
 )
 
-func TestCheckSchemaInfoExist(t *testing.T) {
-	_, err := CheckSchemaInfoExist(util.SetContext(context.Background(), CTX_CACHEONLY, "1"), "")
-	if err != nil {
-		t.FailNow()
-	}
+type Cache struct {
+	data context.Context
+	lock sync.RWMutex
+}
+
+func (c *Cache) Set(k string, v interface{}) {
+	c.lock.Lock()
+	c.data = context.WithValue(c.data, k, v)
+	c.lock.Unlock()
+}
+
+func (c *Cache) Get(k string) (v interface{}) {
+	c.lock.RLock()
+	v = c.data.Value(k)
+	c.lock.RUnlock()
+	return
+}
 
-	_, err = CheckSchemaInfoExist(context.Background(), "")
-	if err == nil {
-		t.FailNow()
+func NewCache() *Cache {
+	return &Cache{
+		data: context.Background(),
 	}
 }
diff --git a/pkg/cache/cache_test.go b/pkg/cache/cache_test.go
new file mode 100644
index 0000000..331666e
--- /dev/null
+++ b/pkg/cache/cache_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 cache
+
+import (
+	"fmt"
+	"golang.org/x/net/context"
+	"testing"
+	"time"
+)
+
+type level1 struct {
+}
+
+func (l *level1) Name(ctx context.Context) string {
+	return ctx.Value("key1").(string)
+}
+
+func (l *level1) Init(ctx context.Context, parent *Node) (node *Node, err error) {
+	p := l.Name(ctx)
+	if p == "err" {
+		return nil, fmt.Errorf("wrong logic")
+	}
+
+	if p == "null" {
+		return
+	}
+
+	node = NewNode()
+	node.Cache.Set("a", "a")
+	node.Cache.Set("b", "b")
+	node.Cache.Set("c", "c")
+	return
+}
+
+type level2 struct {
+	changed string
+}
+
+func (l *level2) Name(ctx context.Context) string {
+	return ctx.Value("key2").(string)
+}
+
+func (l *level2) Init(ctx context.Context, parent *Node) (node *Node, err error) {
+	if parent == nil {
+		return
+	}
+
+	p := l.Name(ctx)
+	if p == "err" {
+		return nil, fmt.Errorf("wrong logic")
+	}
+
+	if p == "null" {
+		return
+	}
+
+	node = NewNode()
+	if len(l.changed) == 0 {
+		i := parent.Cache.Get(p)
+		node.Cache.Set("aa", i)
+	} else {
+		node.Cache.Set("aa", l.changed)
+	}
+
+	return
+}
+
+func TestTree_Get(t *testing.T) {
+	tree := NewTree(Configure().WithMaxSize(1).WithTTL(time.Second))
+	node, err := tree.Get(context.Background())
+	if node != nil || err != nil {
+		t.Fatalf("TestTree_Get failed")
+	}
+	tree.Remove(context.Background())
+
+	l2 := &level2{}
+	tree.AddFilter(&level1{}, l2)
+
+	ctx := context.WithValue(context.WithValue(context.Background(), "key1", "err"), "key2", "")
+	node, err = tree.Get(ctx)
+	if node != nil || err == nil {
+		t.Fatalf("TestTree_Get failed")
+	}
+
+	node, err = tree.Get(context.WithValue(context.WithValue(ctx, "key1", "null"), "key2", ""))
+	if node != nil || err != nil {
+		t.Fatalf("TestTree_Get failed")
+	}
+
+	node, err = tree.Get(context.WithValue(context.WithValue(ctx, "key1", "1"), "key2", "err"))
+	node, err = tree.Get(ctx)
+	if node != nil || err == nil {
+		t.Fatalf("TestTree_Get failed")
+	}
+
+	node, err = tree.Get(context.WithValue(context.WithValue(ctx, "key1", "1"), "key2", "null"))
+	if node != nil || err != nil {
+		t.Fatalf("TestTree_Get failed")
+	}
+
+	node, err = tree.Get(context.WithValue(context.WithValue(ctx, "key1", "1"), "key2", "err"))
+	if node != nil || err == nil {
+		t.Fatalf("TestTree_Get failed")
+	}
+
+	node, err = tree.Get(context.WithValue(context.WithValue(ctx, "key1", "1"), "key2", "b"))
+	if node == nil || err != nil {
+		t.Fatalf("TestTree_Get failed")
+	}
+	if node.Cache.Get("aa") != "b" {
+		t.Fatalf("TestTree_Get failed")
+	}
+
+	node, err = tree.Get(context.WithValue(context.WithValue(ctx, "key1", "1"), "key2", "a"))
+	if node == nil || err != nil {
+		t.Fatalf("TestTree_Get failed")
+	}
+	if node.Cache.Get("aa") != "a" {
+		t.Fatalf("TestTree_Get failed")
+	}
+
+	node, err = tree.Get(context.WithValue(context.WithValue(ctx, "key1", "1"), "key2", "b"))
+	if node == nil || err != nil {
+		t.Fatalf("TestTree_Get failed")
+	}
+	if node.Cache.Get("aa") != "b" {
+		t.Fatalf("TestTree_Get failed")
+	}
+
+	tree.Remove(context.WithValue(ctx, "key1", "1"))
+
+	l2.changed = "changed"
+	node, err = tree.Get(context.WithValue(context.WithValue(ctx, "key1", "1"), "key2", "b"))
+	if node == nil || err != nil {
+		t.Fatalf("TestTree_Get failed")
+	}
+	if node.Cache.Get("aa") != "changed" {
+		t.Fatalf("TestTree_Get failed")
+	}
+
+	l2.changed = ""
+	node, err = tree.Get(context.WithValue(context.WithValue(ctx, "key1", "1"), "key2", "b"),
+		Options().Temporary(true))
+	if node == nil || err != nil {
+		t.Fatalf("TestTree_Get failed")
+	}
+	if node.Cache.Get("aa") != "b" {
+		t.Fatalf("TestTree_Get failed")
+	}
+
+	node, err = tree.Get(context.WithValue(ctx, "key1", "1"), Options().ToLevel(1))
+	if node == nil || err != nil {
+		t.Fatalf("TestTree_Get failed")
+	}
+	if node.Cache.Get("a") != "a" || node.Cache.Get("aa") != nil {
+		t.Fatalf("TestTree_Get failed")
+	}
+
+	childs := node.ChildNodes()
+	if len(childs) != 1 || childs[0].Cache.Get("aa") != "changed" {
+		t.Fatalf("TestTree_Get failed")
+	}
+}
diff --git a/server/service/util/common.go b/pkg/cache/config.go
similarity index 66%
copy from server/service/util/common.go
copy to pkg/cache/config.go
index 9e31b3f..fac805d 100644
--- a/server/service/util/common.go
+++ b/pkg/cache/config.go
@@ -14,16 +14,36 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package util
+package cache
 
 import "time"
 
-const (
-	HEADER_REV            = "X-Resource-Revision"
-	CTX_NOCACHE           = "noCache"
-	CTX_CACHEONLY         = "cacheOnly"
-	CTX_REQUEST_REVISION  = "requestRev"
-	CTX_RESPONSE_REVISION = "responseRev"
+type Config struct {
+	ttl time.Duration
+	max int64
+}
 
-	cacheTTL = 5 * time.Minute
-)
+func (c *Config) TTL() time.Duration {
+	return c.ttl
+}
+
+func (c *Config) WithTTL(ttl time.Duration) *Config {
+	c.ttl = ttl
+	return c
+}
+
+func (c *Config) MaxSize() int64 {
+	return c.max
+}
+
+func (c *Config) WithMaxSize(s int64) *Config {
+	c.max = s
+	return c
+}
+
+func Configure() *Config {
+	return &Config{
+		ttl: 5 * time.Minute,
+		max: 5000,
+	}
+}
diff --git a/server/core/backend/cacher.go b/pkg/cache/filter.go
similarity index 75%
copy from server/core/backend/cacher.go
copy to pkg/cache/filter.go
index 410ef50..51c6448 100644
--- a/server/core/backend/cacher.go
+++ b/pkg/cache/filter.go
@@ -14,20 +14,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package backend
+package cache
 
-type Cache interface {
-	Version() int64
-	Data(interface{}) interface{}
-	Have(interface{}) bool
-	Size() int
-}
+import "golang.org/x/net/context"
 
-type Cacher interface {
-	// Name is the cache size metric name
-	Name() string
-	Cache() Cache
-	Run()
-	Stop()
-	Ready() <-chan struct{}
+type Filter interface {
+	Name(ctx context.Context) string
+	Init(ctx context.Context, parent *Node) (*Node, error)
 }
diff --git a/server/core/backend/extend_test.go b/pkg/cache/node.go
similarity index 60%
copy from server/core/backend/extend_test.go
copy to pkg/cache/node.go
index 6a37ce5..0e9b439 100644
--- a/server/core/backend/extend_test.go
+++ b/pkg/cache/node.go
@@ -14,36 +14,31 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package backend
+package cache
 
-import "testing"
+import "github.com/apache/incubator-servicecomb-service-center/pkg/util"
 
-type extend struct {
+type Node struct {
+	// user data
+	Cache *Cache
+	// tree will set the value below after the node added in.
+	Name   string
+	Tree   *Tree
+	Childs *util.ConcurrentMap
+	Level  int
 }
 
-func (e *extend) Name() string {
-	return "test"
+func (n *Node) ChildNodes() (nodes []*Node) {
+	n.Childs.ForEach(func(item util.MapItem) (next bool) {
+		nodes = append(nodes, item.Value.(*Node))
+		return true
+	})
+	return
 }
 
-func (e *extend) Prefix() string {
-	return "/test"
-}
-
-func (e *extend) InitSize() int {
-	return 0
-}
-
-func TestInstallType(t *testing.T) {
-	id, err := InstallType(&extend{})
-	if err != nil {
-		t.Fatal(err)
-	}
-	if id == NONEXIST {
-		t.Fatal(err)
-	}
-
-	id, err = InstallType(&extend{})
-	if id != NONEXIST || err == nil {
-		t.Fatal("InstallType fail", err)
+func NewNode() *Node {
+	return &Node{
+		Cache:  NewCache(),
+		Childs: util.NewConcurrentMap(0),
 	}
 }
diff --git a/server/core/backend/cacher.go b/pkg/cache/option.go
similarity index 73%
copy from server/core/backend/cacher.go
copy to pkg/cache/option.go
index 410ef50..4250ca1 100644
--- a/server/core/backend/cacher.go
+++ b/pkg/cache/option.go
@@ -14,20 +14,25 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package backend
+package cache
 
-type Cache interface {
-	Version() int64
-	Data(interface{}) interface{}
-	Have(interface{}) bool
-	Size() int
+type Option struct {
+	Level   int
+	NoCache bool
 }
 
-type Cacher interface {
-	// Name is the cache size metric name
-	Name() string
-	Cache() Cache
-	Run()
-	Stop()
-	Ready() <-chan struct{}
+// get node before level l
+func (op Option) ToLevel(l int) Option {
+	op.Level = l
+	return op
+}
+
+// get node without cache
+func (op Option) Temporary(b bool) Option {
+	op.NoCache = b
+	return op
+}
+
+func Options() (op Option) {
+	return
 }
diff --git a/server/core/backend/cacher.go b/pkg/cache/option_test.go
similarity index 70%
copy from server/core/backend/cacher.go
copy to pkg/cache/option_test.go
index 410ef50..f756a54 100644
--- a/server/core/backend/cacher.go
+++ b/pkg/cache/option_test.go
@@ -14,20 +14,19 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package backend
+package cache
 
-type Cache interface {
-	Version() int64
-	Data(interface{}) interface{}
-	Have(interface{}) bool
-	Size() int
-}
+import "testing"
 
-type Cacher interface {
-	// Name is the cache size metric name
-	Name() string
-	Cache() Cache
-	Run()
-	Stop()
-	Ready() <-chan struct{}
+func BenchmarkWithLevel(b *testing.B) {
+	b.RunParallel(func(pb *testing.PB) {
+		for pb.Next() {
+			op := Options().ToLevel(3).ToLevel(2).ToLevel(1)
+			if op.Level != 1 {
+				b.Fatalf("BenchmarkWithLevel failed")
+			}
+		}
+	})
+	b.ReportAllocs()
+	// 2000000000	         0.28 ns/op	       0 B/op	       0 allocs/op
 }
diff --git a/pkg/cache/tree.go b/pkg/cache/tree.go
new file mode 100644
index 0000000..9c89df0
--- /dev/null
+++ b/pkg/cache/tree.go
@@ -0,0 +1,160 @@
+/*
+ * 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 cache
+
+import (
+	"errors"
+	"github.com/karlseguin/ccache"
+	"golang.org/x/net/context"
+	"sync"
+)
+
+var errNilNode = errors.New("nil node")
+
+type Tree struct {
+	Config  *Config
+	roots   *ccache.Cache
+	filters []Filter
+	lock    sync.RWMutex
+}
+
+func (t *Tree) AddFilter(fs ...Filter) *Tree {
+	t.lock.Lock()
+	for _, f := range fs {
+		t.filters = append(t.filters, f)
+	}
+	t.lock.Unlock()
+	return t
+}
+
+func (t *Tree) Get(ctx context.Context, ops ...Option) (node *Node, err error) {
+	var op Option
+	if len(ops) > 0 {
+		op = ops[0]
+	}
+
+	var (
+		parent *Node
+		i      int
+	)
+
+	if !op.NoCache {
+		if parent, err = t.getOrCreateRoot(ctx); parent == nil {
+			return
+		}
+		i++
+		// parent may be a temp root in concurrent scene
+	}
+
+	for ; i < len(t.filters); i++ {
+		if op.Level > 0 && op.Level == i {
+			break
+		}
+		if parent, err = t.getOrCreateNode(ctx, i, parent); parent == nil {
+			break
+		}
+	}
+	node = parent
+	return
+}
+
+func (t *Tree) Remove(ctx context.Context) {
+	if len(t.filters) == 0 {
+		return
+	}
+
+	t.roots.Delete(t.filters[0].Name(ctx))
+}
+
+func (t *Tree) getOrCreateRoot(ctx context.Context) (node *Node, err error) {
+	if len(t.filters) == 0 {
+		return
+	}
+
+	filter := t.filters[0]
+	name := filter.Name(ctx)
+	item, err := t.roots.Fetch(name, t.Config.TTL(), func() (interface{}, error) {
+		node, err := t.getOrCreateNode(ctx, 0, nil)
+		if err != nil {
+			return nil, err
+		}
+		if node == nil {
+			return nil, errNilNode
+		}
+		return node, nil
+	})
+	switch err {
+	case nil:
+		node = item.Value().(*Node)
+	case errNilNode:
+		err = nil
+	}
+	return
+}
+
+func (t *Tree) getOrCreateNode(ctx context.Context, idx int, parent *Node) (node *Node, err error) {
+	filter := t.filters[idx]
+	name := t.nodeFullName(filter.Name(ctx), parent)
+
+	if parent == nil {
+		// new a temp node
+		return t.createNode(ctx, idx, name, parent)
+	}
+
+	item, err := parent.Childs.Fetch(name, func() (interface{}, error) {
+		node, err := t.createNode(ctx, idx, name, parent)
+		if err != nil {
+			return nil, err
+		}
+		if node == nil {
+			return nil, errNilNode
+		}
+		return node, nil
+	})
+	switch err {
+	case nil:
+		node = item.(*Node)
+	case errNilNode:
+		err = nil
+	}
+	return
+}
+
+func (t *Tree) nodeFullName(name string, parent *Node) string {
+	if parent != nil {
+		name = parent.Name + "." + name
+	}
+	return name
+}
+
+func (t *Tree) createNode(ctx context.Context, idx int, name string, parent *Node) (node *Node, err error) {
+	node, err = t.filters[idx].Init(ctx, parent)
+	if node == nil {
+		return
+	}
+	node.Name = name
+	node.Tree = t
+	node.Level = idx
+	return
+}
+
+func NewTree(cfg *Config) *Tree {
+	return &Tree{
+		Config: cfg,
+		roots:  ccache.New(ccache.Configure().MaxSize(cfg.MaxSize())),
+	}
+}
diff --git a/pkg/util/concurrent_map.go b/pkg/util/concurrent_map.go
index 546ec47..fce2c3c 100644
--- a/pkg/util/concurrent_map.go
+++ b/pkg/util/concurrent_map.go
@@ -46,18 +46,37 @@ func (cm *ConcurrentMap) Put(key, val interface{}) (old interface{}) {
 	return
 }
 
-func (cm *ConcurrentMap) PutIfAbsent(key, val interface{}) (old interface{}) {
-	var b bool
+func (cm *ConcurrentMap) PutIfAbsent(key, val interface{}) (exist interface{}) {
 	cm.init()
 	cm.mux.Lock()
-	old, b = cm.items[key]
+	var b bool
+	exist, b = cm.items[key]
 	if !b {
-		cm.items[key] = val
+		cm.items[key], exist = val, val
 	}
 	cm.mux.Unlock()
 	return
 }
 
+func (cm *ConcurrentMap) Fetch(key interface{}, f func() (interface{}, error)) (exist interface{}, err error) {
+	cm.init()
+	cm.mux.RLock()
+	var b bool
+	exist, b = cm.items[key]
+	cm.mux.RUnlock()
+	if !b {
+		cm.mux.Lock()
+		exist, b = cm.items[key]
+		if !b {
+			if exist, err = f(); err == nil {
+				cm.items[key] = exist
+			}
+		}
+		cm.mux.Unlock()
+	}
+	return
+}
+
 func (cm *ConcurrentMap) Get(key interface{}) (val interface{}, b bool) {
 	cm.init()
 	cm.mux.RLock()
diff --git a/pkg/util/concurrent_map_test.go b/pkg/util/concurrent_map_test.go
index 344de5d..9259d62 100644
--- a/pkg/util/concurrent_map_test.go
+++ b/pkg/util/concurrent_map_test.go
@@ -42,14 +42,8 @@ func TestConcurrentMap(t *testing.T) {
 	if v.(string) != "1" {
 		t.Fatalf("TestConcurrentMap Put an item again failed.")
 	}
-	v = cm.PutIfAbsent("b", "1")
-	if v != nil {
-		t.Fatalf("TestConcurrentMap PutIfAbsent a not exist item failed.")
-	}
-	v = cm.PutIfAbsent("a", "3")
-	if v.(string) != "2" {
-		t.Fatalf("TestConcurrentMap PutIfAbsent an item failed.")
-	}
+	cm.PutIfAbsent("b", "1")
+	cm.PutIfAbsent("a", "3")
 	v, b = cm.Get("a")
 	if !b || v.(string) != "2" {
 		t.Fatalf("TestConcurrentMap Get an item after PutIfAbsent failed.")
diff --git a/pkg/util/goroutines.go b/pkg/util/goroutines.go
index 7ee48a9..74689f3 100644
--- a/pkg/util/goroutines.go
+++ b/pkg/util/goroutines.go
@@ -63,15 +63,12 @@ func (g *GoRoutine) loop(f func(context.Context)) {
 	g.wg.Add(1)
 	defer g.wg.Done()
 	defer func() { <-g.concurrent }()
+
 	timer := time.NewTimer(GlobalPoolConfig.IdleTimeout)
+	defer timer.Stop()
 	for {
 		g.execute(f)
 
-		if !timer.Stop() {
-			<-timer.C
-		}
-		timer.Reset(GlobalPoolConfig.IdleTimeout)
-
 		select {
 		case <-timer.C:
 			return
@@ -79,6 +76,7 @@ func (g *GoRoutine) loop(f func(context.Context)) {
 			if f == nil {
 				return
 			}
+			ResetTimer(timer, GlobalPoolConfig.IdleTimeout)
 		}
 	}
 }
diff --git a/pkg/util/util.go b/pkg/util/util.go
index 44b8b09..5c3b3f0 100644
--- a/pkg/util/util.go
+++ b/pkg/util/util.go
@@ -211,3 +211,19 @@ func SliceHave(arr []string, str string) bool {
 	}
 	return false
 }
+
+// do not call after drain timer.C channel
+func ResetTimer(timer *time.Timer, d time.Duration) {
+	if !timer.Stop() {
+		// timer is expired: can not find the timer in timer stack
+		// select {
+		// case <-timer.C:
+		// 	// here block when drain channel call before timer.Stop()
+		// default:
+		// 	// here will cause a BUG When sendTime() after drain channel
+		// 	// BUG: timer.C still trigger even after timer.Reset()
+		// }
+		<-timer.C
+	}
+	timer.Reset(d)
+}
diff --git a/pkg/util/util_test.go b/pkg/util/util_test.go
index 932aa11..596e931 100644
--- a/pkg/util/util_test.go
+++ b/pkg/util/util_test.go
@@ -18,6 +18,7 @@ package util
 
 import (
 	"testing"
+	"time"
 )
 
 func TestInt16ToInt64(t *testing.T) {
@@ -72,3 +73,12 @@ func TestFileLastName(t *testing.T) {
 		t.Fatal("TestFileLastName 'b' failed", n)
 	}
 }
+
+func TestResetTimer(t *testing.T) {
+	timer := time.NewTimer(time.Microsecond)
+	<-timer.C
+	ResetTimer(timer, time.Microsecond)
+	<-timer.C
+	ResetTimer(timer, time.Second)
+	<-timer.C
+}
diff --git a/server/broker/service.go b/server/broker/service.go
index f4ea526..c36cb3e 100644
--- a/server/broker/service.go
+++ b/server/broker/service.go
@@ -151,7 +151,7 @@ func (*BrokerService) RetrieveProviderPacts(ctx context.Context,
 	versionObjects := make(map[int32]Version)
 	for i := 0; i < len(versions.Kvs); i++ {
 		version := &Version{}
-		err = json.Unmarshal(versions.Kvs[i].Value, version)
+		err = json.Unmarshal(versions.Kvs[i].Value.([]byte), version)
 		if err != nil {
 			return nil, err
 		}
@@ -174,7 +174,7 @@ func (*BrokerService) RetrieveProviderPacts(ctx context.Context,
 	participantToVersionObj := make(map[int32]Version)
 	for i := 0; i < len(pactVersions.Kvs); i++ {
 		pactVersion := &PactVersion{}
-		err = json.Unmarshal(pactVersions.Kvs[i].Value, pactVersion)
+		err = json.Unmarshal(pactVersions.Kvs[i].Value.([]byte), pactVersion)
 		if err != nil {
 			return nil, err
 		}
@@ -206,7 +206,7 @@ func (*BrokerService) RetrieveProviderPacts(ctx context.Context,
 	consumerInfoArr := make([]*ConsumerInfo, 0)
 	for i := 0; i < len(participants.Kvs); i++ {
 		participant := &Participant{}
-		err = json.Unmarshal(participants.Kvs[i].Value, participant)
+		err = json.Unmarshal(participants.Kvs[i].Value.([]byte), participant)
 		if err != nil {
 			return nil, err
 		}
@@ -304,7 +304,7 @@ func (*BrokerService) GetAllProviderPacts(ctx context.Context,
 	versionObjects := make(map[int32]Version)
 	for i := 0; i < len(versions.Kvs); i++ {
 		version := &Version{}
-		err = json.Unmarshal(versions.Kvs[i].Value, version)
+		err = json.Unmarshal(versions.Kvs[i].Value.([]byte), version)
 		if err != nil {
 			return nil, err
 		}
@@ -327,7 +327,7 @@ func (*BrokerService) GetAllProviderPacts(ctx context.Context,
 	participantToVersionObj := make(map[int32]Version)
 	for i := 0; i < len(pactVersions.Kvs); i++ {
 		pactVersion := &PactVersion{}
-		err = json.Unmarshal(pactVersions.Kvs[i].Value, pactVersion)
+		err = json.Unmarshal(pactVersions.Kvs[i].Value.([]byte), pactVersion)
 		if err != nil {
 			return nil, err
 		}
@@ -359,7 +359,7 @@ func (*BrokerService) GetAllProviderPacts(ctx context.Context,
 	consumerInfoArr := make([]*ConsumerInfo, 0)
 	for i := 0; i < len(participants.Kvs); i++ {
 		participant := &Participant{}
-		err = json.Unmarshal(participants.Kvs[i].Value, participant)
+		err = json.Unmarshal(participants.Kvs[i].Value.([]byte), participant)
 		if err != nil {
 			return nil, err
 		}
@@ -466,7 +466,7 @@ func (*BrokerService) RetrieveVerificationResults(ctx context.Context, in *Retri
 	verificationDetailsArr := make([]*VerificationDetail, 0)
 	for j := 0; j < len(pactVersions.Kvs); j++ {
 		pactVersion := &PactVersion{}
-		err = json.Unmarshal(pactVersions.Kvs[j].Value, &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{
@@ -488,7 +488,7 @@ func (*BrokerService) RetrieveVerificationResults(ctx context.Context, in *Retri
 		var lastVerificationResult *Verification
 		for i := 0; i < len(verifications.Kvs); i++ {
 			verification := &Verification{}
-			err = json.Unmarshal(verifications.Kvs[i].Value, &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{
@@ -525,7 +525,7 @@ func (*BrokerService) RetrieveVerificationResults(ctx context.Context, in *Retri
 		var providerParticipant *Participant
 		for i := 0; i < len(participants.Kvs); i++ {
 			participant := &Participant{}
-			err = json.Unmarshal(participants.Kvs[i].Value, &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{
@@ -635,7 +635,7 @@ func (*BrokerService) PublishVerificationResults(ctx context.Context, in *Publis
 	pactExists := false
 	for i := 0; i < len(pacts.Kvs); i++ {
 		pact := &Pact{}
-		err = json.Unmarshal(pacts.Kvs[i].Value, &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{
@@ -675,7 +675,7 @@ func (*BrokerService) PublishVerificationResults(ctx context.Context, in *Publis
 	if len(verifications.Kvs) != 0 {
 		for i := 0; i < len(verifications.Kvs); i++ {
 			verification := &Verification{}
-			err = json.Unmarshal(verifications.Kvs[i].Value, &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{
diff --git a/server/broker/store.go b/server/broker/store.go
index 51acaca..cb73923 100644
--- a/server/broker/store.go
+++ b/server/broker/store.go
@@ -33,44 +33,44 @@ var (
 var brokerKvStore = &BKvStore{}
 
 func init() {
-	PARTICIPANT = backend.Store().MustInstall(backend.NewEntity("PARTICIPANT", GetBrokerParticipantKey("")))
-	VERSION = backend.Store().MustInstall(backend.NewEntity("VERSION", GetBrokerVersionKey("")))
-	PACT = backend.Store().MustInstall(backend.NewEntity("PACT", GetBrokerPactKey("")))
-	PACT_VERSION = backend.Store().MustInstall(backend.NewEntity("PACT_VERSION", GetBrokerPactVersionKey("")))
-	PACT_TAG = backend.Store().MustInstall(backend.NewEntity("PACT_TAG", GetBrokerTagKey("")))
-	VERIFICATION = backend.Store().MustInstall(backend.NewEntity("VERIFICATION", GetBrokerVerificationKey("")))
-	PACT_LATEST = backend.Store().MustInstall(backend.NewEntity("PACT_LATEST", GetBrokerLatestKey("")))
+	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(""))))
 
 }
 
 type BKvStore struct {
 }
 
-func (s *BKvStore) Participant() *backend.Indexer {
+func (s *BKvStore) Participant() backend.Indexer {
 	return backend.Store().Entity(PARTICIPANT)
 }
 
-func (s *BKvStore) Version() *backend.Indexer {
+func (s *BKvStore) Version() backend.Indexer {
 	return backend.Store().Entity(VERSION)
 }
 
-func (s *BKvStore) Pact() *backend.Indexer {
+func (s *BKvStore) Pact() backend.Indexer {
 	return backend.Store().Entity(PACT)
 }
 
-func (s *BKvStore) PactVersion() *backend.Indexer {
+func (s *BKvStore) PactVersion() backend.Indexer {
 	return backend.Store().Entity(PACT_VERSION)
 }
 
-func (s *BKvStore) PactTag() *backend.Indexer {
+func (s *BKvStore) PactTag() backend.Indexer {
 	return backend.Store().Entity(PACT_TAG)
 }
 
-func (s *BKvStore) Verification() *backend.Indexer {
+func (s *BKvStore) Verification() backend.Indexer {
 	return backend.Store().Entity(VERIFICATION)
 }
 
-func (s *BKvStore) PactLatest() *backend.Indexer {
+func (s *BKvStore) PactLatest() backend.Indexer {
 	return backend.Store().Entity(PACT_LATEST)
 }
 
diff --git a/server/broker/util.go b/server/broker/util.go
index 895ee8c..3d398b5 100644
--- a/server/broker/util.go
+++ b/server/broker/util.go
@@ -187,7 +187,7 @@ func GetBrokerParticipantUtils(ctx context.Context, tenant string, appId string,
 	}
 
 	participant := &Participant{}
-	err = json.Unmarshal(participants.Kvs[0].Value, participant)
+	err = json.Unmarshal(participants.Kvs[0].Value.([]byte), participant)
 	if err != nil {
 		return nil, err
 	}
@@ -259,7 +259,7 @@ func GetParticipant(ctx context.Context, domain string, appId string,
 		return nil, nil
 	}
 	participant := &Participant{}
-	err = json.Unmarshal(participants.Kvs[0].Value, participant)
+	err = json.Unmarshal(participants.Kvs[0].Value.([]byte), participant)
 	if err != nil {
 		return nil, err
 	}
@@ -278,7 +278,7 @@ func GetVersion(ctx context.Context, domain string, number string,
 		return nil, nil
 	}
 	version := &Version{}
-	err = json.Unmarshal(versions.Kvs[0].Value, version)
+	err = json.Unmarshal(versions.Kvs[0].Value.([]byte), version)
 	if err != nil {
 		return nil, err
 	}
@@ -296,7 +296,7 @@ func GetPact(ctx context.Context, domain string, consumerParticipantId int32, pr
 		return nil, nil
 	}
 	pact := &Pact{}
-	err = json.Unmarshal(versions.Kvs[0].Value, pact)
+	err = json.Unmarshal(versions.Kvs[0].Value.([]byte), pact)
 	if err != nil {
 		return nil, err
 	}
@@ -315,7 +315,7 @@ func GetPactVersion(ctx context.Context, domain string, versionId int32,
 		return nil, nil
 	}
 	pactVersion := &PactVersion{}
-	err = json.Unmarshal(versions.Kvs[0].Value, pactVersion)
+	err = json.Unmarshal(versions.Kvs[0].Value.([]byte), pactVersion)
 	if err != nil {
 		return nil, err
 	}
@@ -331,7 +331,7 @@ func GetData(ctx context.Context, key string) (int, error) {
 	if len(values.Kvs) == 0 {
 		return -1, nil
 	}
-	id, err := strconv.Atoi(string(values.Kvs[0].Value))
+	id, err := strconv.Atoi(string(values.Kvs[0].Value.([]byte)))
 	if err != nil {
 		return -1, err
 	}
@@ -525,7 +525,7 @@ func GetLastestVersionNumberForParticipant(ctx context.Context,
 	order := int32(math.MinInt32)
 	for i := 0; i < len(versions.Kvs); i++ {
 		version := &Version{}
-		err = json.Unmarshal(versions.Kvs[i].Value, &version)
+		err = json.Unmarshal(versions.Kvs[i].Value.([]byte), &version)
 		if err != nil {
 			return -1
 		}
@@ -622,7 +622,7 @@ func RetrieveProviderConsumerPact(ctx context.Context,
 	pactIds := make(map[int32]int32)
 	for i := 0; i < len(pactVersions.Kvs); i++ {
 		pactVersion := &PactVersion{}
-		err = json.Unmarshal(pactVersions.Kvs[i].Value, pactVersion)
+		err = json.Unmarshal(pactVersions.Kvs[i].Value.([]byte), pactVersion)
 		if err != nil {
 			return nil, -1, err
 		}
@@ -656,7 +656,7 @@ func RetrieveProviderConsumerPact(ctx context.Context,
 	}
 	for i := 0; i < len(pacts.Kvs); i++ {
 		pactObj := &Pact{}
-		err = json.Unmarshal(pacts.Kvs[i].Value, pactObj)
+		err = json.Unmarshal(pacts.Kvs[i].Value.([]byte), pactObj)
 		if err != nil {
 			return nil, -1, err
 		}
diff --git a/server/core/backend/cacher.go b/server/core/backend/cache.go
similarity index 84%
copy from server/core/backend/cacher.go
copy to server/core/backend/cache.go
index 410ef50..2f9107e 100644
--- a/server/core/backend/cacher.go
+++ b/server/core/backend/cache.go
@@ -17,15 +17,18 @@
 package backend
 
 type Cache interface {
-	Version() int64
-	Data(interface{}) interface{}
-	Have(interface{}) bool
+	Name() string
 	Size() int
+
+	Get(k string) *KeyValue
+	GetAll(prefix string, arr *[]*KeyValue) int
+	ForEach(iter func(k string, v *KeyValue) (next bool))
+
+	Put(k string, v *KeyValue)
+	Remove(k string)
 }
 
 type Cacher interface {
-	// Name is the cache size metric name
-	Name() string
 	Cache() Cache
 	Run()
 	Stop()
diff --git a/server/core/backend/cache_kv.go b/server/core/backend/cache_kv.go
index cedb9dc..b001544 100644
--- a/server/core/backend/cache_kv.go
+++ b/server/core/backend/cache_kv.go
@@ -17,511 +17,155 @@
 package backend
 
 import (
-	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
-	"github.com/apache/incubator-servicecomb-service-center/server/core/proto"
-	"github.com/coreos/etcd/mvcc/mvccpb"
-	"golang.org/x/net/context"
+	"strings"
 	"sync"
 	"time"
 )
 
 type KvCache struct {
-	owner       *KvCacher
-	size        int
-	store       map[string]*mvccpb.KeyValue
+	Cfg         *Config
+	name        string
+	store       map[string]map[string]*KeyValue
 	rwMux       sync.RWMutex
 	lastRefresh time.Time
 	lastMaxSize int
 }
 
-func (c *KvCache) Version() int64 {
-	return c.owner.lw.Revision()
+func (c *KvCache) Name() string {
+	return c.name
 }
 
-func (c *KvCache) Data(k interface{}) interface{} {
-	c.rwMux.RLock()
-	kv, ok := c.store[k.(string)]
-	c.rwMux.RUnlock()
-	if !ok {
-		return nil
-	}
-	return kv
+func (c *KvCache) Size() (l int) {
+	l = c.GetAll(c.Cfg.Prefix, nil)
+	return
 }
 
-func (c *KvCache) Have(k interface{}) (ok bool) {
+func (c *KvCache) Get(key string) (v *KeyValue) {
 	c.rwMux.RLock()
-	_, ok = c.store[k.(string)]
+	prefix := c.prefix(key)
+	if p, ok := c.store[prefix]; ok {
+		v, _ = p[key]
+	}
 	c.rwMux.RUnlock()
 	return
 }
 
-func (c *KvCache) RLock() map[string]*mvccpb.KeyValue {
+func (c *KvCache) GetAll(prefix string, arr *[]*KeyValue) (count int) {
 	c.rwMux.RLock()
-	return c.store
-}
-
-func (c *KvCache) RUnlock() {
+	count = c.getPrefixKey(arr, prefix)
 	c.rwMux.RUnlock()
+	return
 }
 
-func (c *KvCache) Lock() map[string]*mvccpb.KeyValue {
+func (c *KvCache) Put(key string, v *KeyValue) {
 	c.rwMux.Lock()
-	return c.store
-}
-
-func (c *KvCache) Unlock() {
-	l := len(c.store)
-	if l > c.lastMaxSize {
-		c.lastMaxSize = l
-	}
-	if c.size >= l &&
-		c.lastMaxSize > c.size*DEFAULT_COMPACT_TIMES &&
-		time.Now().Sub(c.lastRefresh) >= DEFAULT_COMPACT_TIMEOUT {
-		c.compact()
-		c.lastMaxSize = l
-		c.lastRefresh = time.Now()
-	}
-
+	c.addPrefixKey(key, v)
 	c.rwMux.Unlock()
 }
 
-func (c *KvCache) compact() {
-	// gc
-	newCache := make(map[string]*mvccpb.KeyValue, c.size)
-	for k, v := range c.store {
-		newCache[k] = v
-	}
-	c.store = newCache
-
-	util.Logger().Infof("cache %s is not in use over %s, compact capacity to size %d->%d",
-		c.owner.Cfg.Prefix, DEFAULT_COMPACT_TIMEOUT, c.lastMaxSize, c.size)
+func (c *KvCache) Remove(key string) {
+	c.rwMux.Lock()
+	c.deletePrefixKey(key)
+	c.rwMux.Unlock()
 }
 
-func (c *KvCache) Size() (l int) {
+func (c *KvCache) ForEach(iter func(k string, v *KeyValue) (next bool)) {
 	c.rwMux.RLock()
-	l = len(c.store)
-	c.rwMux.RUnlock()
-	return
-}
-
-type KvCacher struct {
-	Cfg Config
-
-	name         string
-	lastRev      int64
-	noEventCount int
-
-	ready     chan struct{}
-	lw        ListWatcher
-	mux       sync.Mutex
-	once      sync.Once
-	cache     *KvCache
-	goroutine *util.GoRoutine
-}
-
-func (c *KvCacher) Name() string {
-	return c.name
-}
-
-func (c *KvCacher) needList() bool {
-	rev := c.lw.Revision()
-	defer func() { c.lastRev = rev }()
-
-	if rev == 0 {
-		c.noEventCount = 0
-		return true
-	}
-	if c.lastRev != rev {
-		c.noEventCount = 0
-		return false
-	}
-	c.noEventCount++
-	if c.noEventCount < c.Cfg.NoEventMaxInterval {
-		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
-	return true
-}
-
-func (c *KvCacher) doList(cfg ListWatchConfig) error {
-	kvs, err := c.lw.List(cfg)
-	if err != nil {
-		return err
-	}
-
-	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())
-	}
-	c.sync(evts)
-	util.LogDebugOrWarnf(start, "finish to cache key %s, %d items, rev: %d",
-		c.Cfg.Prefix, len(kvs), c.lw.Revision())
-
-	return nil
-}
-
-func (c *KvCacher) doWatch(cfg ListWatchConfig) error {
-	watcher := c.lw.Watch(cfg)
-	return c.handleWatcher(watcher)
-}
-
-func (c *KvCacher) ListAndWatch(ctx context.Context) error {
-	c.mux.Lock()
-	defer c.mux.Unlock()
-
-	cfg := ListWatchConfig{
-		Timeout: c.Cfg.Timeout,
-		Context: ctx,
-	}
-	if c.needList() {
-		c.doList(cfg)
-	}
-
-	util.SafeCloseChan(c.ready)
-
-	return c.doWatch(cfg)
-}
-
-func (c *KvCacher) handleWatcher(watcher *Watcher) error {
-	defer watcher.Stop()
-	for evts := range watcher.EventBus() {
-		if evts[0].Type == proto.EVT_ERROR {
-			err := evts[0].Object.(error)
-			return err
-		}
-		c.sync(evts)
-	}
-	return nil
-}
-
-func (c *KvCacher) needDeferHandle(evts []KvEvent) bool {
-	if c.Cfg.DeferHandler == nil || !c.IsReady() {
-		return false
-	}
-
-	return c.Cfg.DeferHandler.OnCondition(c.Cache(), evts)
-}
-
-func (c *KvCacher) refresh(ctx context.Context) {
-	util.Logger().Debugf("start to list and watch %s", c.Cfg)
-	retries := 0
-	ticker := time.NewTicker(DEFAULT_METRICS_INTERVAL)
-	for {
-		nextPeriod := minWaitInterval
-		if err := c.ListAndWatch(ctx); err != nil {
-			nextPeriod = util.GetBackoff().Delay(retries)
-			retries++
-		} else {
-			retries = 0
-		}
-
-		timer := time.NewTimer(nextPeriod)
-		select {
-		case <-ctx.Done():
-			timer.Stop()
-
-			util.Logger().Debugf("stop to list and watch %s", c.Cfg)
-			return
-		case <-ticker.C:
-			timer.Stop()
-
-			ReportCacheMetrics(c.Name(), "raw", c.cache.RLock())
-			c.cache.RUnlock()
-		case <-timer.C:
-		}
-	}
-}
-
-func (c *KvCacher) deferHandle(ctx context.Context) {
-	if c.Cfg.DeferHandler == nil {
-		return
-	}
-
-	var (
-		evts = make([]KvEvent, eventBlockSize)
-		i    int
-	)
-	for {
-		timer := time.NewTimer(300 * time.Millisecond)
-		select {
-		case <-ctx.Done():
-			timer.Stop()
-			return
-		case evt, ok := <-c.Cfg.DeferHandler.HandleChan():
-			timer.Stop()
-
-			if !ok {
-				<-time.After(time.Second)
-				continue
+loopParent:
+	for _, p := range c.store {
+		for k, v := range p {
+			if v == nil {
+				continue loopParent
 			}
-
-			if i >= eventBlockSize {
-				c.onEvents(evts[:i])
-				evts = make([]KvEvent, eventBlockSize)
-				i = 0
-			}
-
-			evts[i] = evt
-			i++
-		case <-timer.C:
-			if i == 0 {
-				continue
+			if !iter(k, v) {
+				break loopParent
 			}
-
-			c.onEvents(evts[:i])
-			evts = make([]KvEvent, eventBlockSize)
-			i = 0
 		}
 	}
+	c.rwMux.RUnlock()
 }
 
-func (c *KvCacher) sync(evts []KvEvent) {
-	if len(evts) == 0 {
-		return
+func (c *KvCache) prefix(key string) string {
+	if len(key) == 0 {
+		return ""
 	}
-
-	if c.needDeferHandle(evts) {
-		return
-	}
-
-	c.onEvents(evts)
+	return key[:strings.LastIndex(key[:len(key)-1], "/")+1]
 }
 
-func (c *KvCacher) filter(rev int64, items []*mvccpb.KeyValue) []KvEvent {
-	store := c.cache.RLock()
-	defer c.cache.RUnlock()
-
-	oc, nc := len(store), len(items)
-	tc := oc + nc
-	if tc == 0 {
-		return nil
-	}
-	max := oc
-	if nc > oc {
-		max = nc
-	}
-
-	newStore := make(map[string]*mvccpb.KeyValue, nc)
-	for _, kv := range items {
-		newStore[util.BytesToStringWithNoCopy(kv.Key)] = kv
-	}
-	filterStopCh := make(chan struct{})
-	eventsCh := make(chan [eventBlockSize]KvEvent, 2)
-
-	go c.filterDelete(store, newStore, rev, eventsCh, filterStopCh)
-
-	go c.filterCreateOrUpdate(store, newStore, rev, eventsCh, filterStopCh)
-
-	evts := make([]KvEvent, 0, max)
-	for block := range eventsCh {
-		for _, e := range block {
-			if e.Object == nil {
-				break
+func (c *KvCache) getPrefixKey(arr *[]*KeyValue, prefix string) (count int) {
+	keysRef, ok := c.store[prefix]
+	if !ok {
+		return 0
+	}
+
+	for key := range keysRef {
+		n := c.getPrefixKey(arr, key)
+		if n == 0 {
+			count += len(keysRef)
+			if arr != nil {
+				// TODO support sort option
+				for _, v := range keysRef {
+					*arr = append(*arr, v)
+				}
 			}
-			evts = append(evts, e)
+			break
 		}
+		count += n
 	}
-	return evts
+	return count
 }
 
-func (c *KvCacher) filterDelete(store map[string]*mvccpb.KeyValue, newStore map[string]*mvccpb.KeyValue,
-	rev int64, eventsCh chan [eventBlockSize]KvEvent, filterStopCh chan struct{}) {
-	var block [eventBlockSize]KvEvent
-	i := 0
-	for k, v := range store {
-		_, ok := newStore[k]
-		if ok {
-			continue
-		}
-
-		if i >= eventBlockSize {
-			eventsCh <- block
-			block = [eventBlockSize]KvEvent{}
-			i = 0
-		}
-
-		block[i] = KvEvent{
-			Revision: rev,
-			Type:     proto.EVT_DELETE,
-			Prefix:   c.Cfg.Prefix,
-			Object:   v,
-		}
-		i++
-	}
-
-	if i > 0 {
-		eventsCh <- block
-	}
-
-	close(filterStopCh)
-}
-
-func (c *KvCacher) filterCreateOrUpdate(store map[string]*mvccpb.KeyValue, newStore map[string]*mvccpb.KeyValue,
-	rev int64, eventsCh chan [eventBlockSize]KvEvent, filterStopCh chan struct{}) {
-	var block [eventBlockSize]KvEvent
-	i := 0
-	for k, v := range newStore {
-		ov, ok := store[k]
-		if !ok {
-			if i >= eventBlockSize {
-				eventsCh <- block
-				block = [eventBlockSize]KvEvent{}
-				i = 0
-			}
-
-			block[i] = KvEvent{
-				Revision: rev,
-				Type:     proto.EVT_CREATE,
-				Prefix:   c.Cfg.Prefix,
-				Object:   v,
-			}
-			i++
-			continue
-		}
-
-		if ov.CreateRevision == v.CreateRevision && ov.ModRevision == v.ModRevision {
-			continue
-		}
-
-		if i >= eventBlockSize {
-			eventsCh <- block
-			block = [eventBlockSize]KvEvent{}
-			i = 0
-		}
-
-		block[i] = KvEvent{
-			Revision: rev,
-			Type:     proto.EVT_UPDATE,
-			Prefix:   c.Cfg.Prefix,
-			Object:   v,
-		}
-		i++
+func (c *KvCache) addPrefixKey(key string, val *KeyValue) {
+	if len(c.Cfg.Prefix) >= len(key) {
+		return
 	}
-
-	if i > 0 {
-		eventsCh <- block
+	prefix := c.prefix(key)
+	if len(prefix) == 0 {
+		return
 	}
-
-	<-filterStopCh
-
-	close(eventsCh)
-}
-
-func (c *KvCacher) onEvents(evts []KvEvent) {
-	init := !c.IsReady()
-	store := c.cache.Lock()
-	for i, evt := range evts {
-		kv := evt.Object.(*mvccpb.KeyValue)
-		key := util.BytesToStringWithNoCopy(kv.Key)
-		prevKv, ok := store[key]
-
-		switch evt.Type {
-		case proto.EVT_CREATE, proto.EVT_UPDATE:
-			switch {
-			case init:
-				evt.Type = proto.EVT_INIT
-			case !ok && evt.Type != proto.EVT_CREATE:
-				util.Logger().Warnf(nil, "unexpected %s event! it should be %s key %s",
-					evt.Type, proto.EVT_CREATE, key)
-				evt.Type = proto.EVT_CREATE
-			case ok && evt.Type != proto.EVT_UPDATE:
-				util.Logger().Warnf(nil, "unexpected %s event! it should be %s key %s",
-					evt.Type, proto.EVT_UPDATE, key)
-				evt.Type = proto.EVT_UPDATE
-			}
-
-			store[key] = kv
-			evts[i] = evt
-		case proto.EVT_DELETE:
-			if !ok {
-				util.Logger().Warnf(nil, "unexpected %s event! key %s does not cache", evt.Type, key)
-			} else {
-				delete(store, key)
-			}
-			evt.Object = prevKv // maybe nil
-			evts[i] = evt
+	keys, ok := c.store[prefix]
+	if !ok {
+		// build parent index key and new child nodes
+		keys = make(map[string]*KeyValue)
+		c.store[prefix] = keys
+	} else if _, ok := keys[key]; ok {
+		if val != nil {
+			// override the value
+			keys[key] = val
 		}
+		return
 	}
 
-	c.cache.Unlock()
-
-	c.onKvEvents(evts)
+	keys[key], key = val, prefix
+	c.addPrefixKey(key, nil)
 }
 
-func (c *KvCacher) onKvEvents(evts []KvEvent) {
-	if c.Cfg.OnEvent == nil {
+func (c *KvCache) deletePrefixKey(key string) {
+	prefix := c.prefix(key)
+	m, ok := c.store[prefix]
+	if !ok {
 		return
 	}
-	defer util.RecoverAndReport()
+	delete(m, key)
 
-	for _, evt := range evts {
-		if evt.Object == nil {
-			continue
-		}
-		c.Cfg.OnEvent(evt)
+	// remove parent which has no child
+	if len(m) == 0 {
+		delete(c.store, prefix)
+		c.deletePrefixKey(prefix)
 	}
 }
 
-func (c *KvCacher) Cache() Cache {
-	return c.cache
-}
-
-func (c *KvCacher) Run() {
-	c.once.Do(func() {
-		c.goroutine.Do(c.refresh)
-		c.goroutine.Do(c.deferHandle)
-	})
-}
-
-func (c *KvCacher) Stop() {
-	c.goroutine.Close(true)
-
-	util.SafeCloseChan(c.ready)
-}
-
-func (c *KvCacher) Ready() <-chan struct{} {
-	return c.ready
-}
-
-func (c *KvCacher) IsReady() bool {
-	select {
-	case <-c.ready:
-		return true
-	default:
-		return false
-	}
+func (c *KvCache) ReportMetrics() {
+	c.rwMux.RLock()
+	ReportCacheMetrics(c.Name(), "raw", c.store)
+	c.rwMux.RUnlock()
 }
 
-func NewKvCache(c *KvCacher, size int) *KvCache {
+func NewKvCache(name string, cfg *Config) *KvCache {
 	return &KvCache{
-		owner:       c,
-		size:        size,
-		store:       make(map[string]*mvccpb.KeyValue, size),
+		Cfg:         cfg,
+		name:        name,
+		store:       make(map[string]map[string]*KeyValue),
 		lastRefresh: time.Now(),
 	}
 }
-
-func NewKvCacher(name string, opts ...ConfigOption) *KvCacher {
-	cfg := DefaultConfig()
-	for _, opt := range opts {
-		opt(&cfg)
-	}
-
-	cacher := &KvCacher{
-		name:  name,
-		Cfg:   cfg,
-		ready: make(chan struct{}),
-		lw: ListWatcher{
-			Client: Registry(),
-			Prefix: cfg.Prefix,
-		},
-		goroutine: util.NewGo(context.Background()),
-	}
-	cacher.cache = NewKvCache(cacher, cfg.InitSize)
-	return cacher
-}
diff --git a/server/core/backend/cache_null.go b/server/core/backend/cache_null.go
index d6681aa..cce930e 100644
--- a/server/core/backend/cache_null.go
+++ b/server/core/backend/cache_null.go
@@ -19,47 +19,27 @@ package backend
 var (
 	NullCache  = &nullCache{}
 	NullCacher = &nullCacher{}
-	closedCh   = make(chan struct{})
 )
 
-func init() {
-	close(closedCh)
-}
-
 type nullCache struct {
 }
 
-func (n *nullCache) Version() int64 {
-	return 0
-}
-
-func (n *nullCache) Data(interface{}) interface{} {
-	return nil
-}
-
-func (n *nullCache) Have(interface{}) bool {
-	return false
-}
-
-func (n *nullCache) Size() int {
-	return 0
-}
+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) 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) Name() string {
-	return ""
-}
-
-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) Cache() Cache           { return NullCache }
+func (n *nullCacher) Run()                   {}
+func (n *nullCacher) Stop()                  {}
+func (n *nullCacher) Ready() <-chan struct{} { return closedCh }
diff --git a/server/core/backend/cacher_test.go b/server/core/backend/cache_test.go
similarity index 56%
rename from server/core/backend/cacher_test.go
rename to server/core/backend/cache_test.go
index 9e06f6d..e15ba9c 100644
--- a/server/core/backend/cacher_test.go
+++ b/server/core/backend/cache_test.go
@@ -35,10 +35,10 @@ func BenchmarkFilter(b *testing.B) {
 	}
 	v, _ := json.Marshal(inst)
 
-	cacher := &KvCacher{}
+	cfg := DefaultConfig().WithParser(InstanceParser)
 
 	n := 300 * 1000 // 30w
-	cache := NewKvCache(cacher, n)
+	cache := NewKvCache("test", cfg)
 	items := make([]*mvccpb.KeyValue, 0, n)
 	for ; n > 0; n-- {
 		k := fmt.Sprintf("/%d", n)
@@ -51,11 +51,11 @@ func BenchmarkFilter(b *testing.B) {
 			})
 		} else if n > 100*1000 && n <= 20*1000 {
 			// update
-			cache.store[k] = &mvccpb.KeyValue{
+			cache.Put(k, &KeyValue{
 				Key:         util.StringToBytesWithNoCopy(k),
-				Value:       v,
+				Value:       inst,
 				ModRevision: 1,
-			}
+			})
 			items = append(items, &mvccpb.KeyValue{
 				Key:         util.StringToBytesWithNoCopy(k),
 				Value:       v,
@@ -63,13 +63,14 @@ func BenchmarkFilter(b *testing.B) {
 			})
 		} else {
 			// delete
-			cache.store[k] = &mvccpb.KeyValue{
+			cache.Put(k, &KeyValue{
 				Key:         util.StringToBytesWithNoCopy(k),
-				Value:       v,
+				Value:       inst,
 				ModRevision: 1,
-			}
+			})
 		}
 	}
+	cacher := &KvCacher{Cfg: cfg}
 	cacher.cache = cache
 
 	b.ResetTimer()
@@ -79,6 +80,61 @@ func BenchmarkFilter(b *testing.B) {
 	b.ReportAllocs()
 
 	// TODO bad performance!!!
-	//10	 120612060 ns/op	37128035 B/op	     134 allocs/op
-	//
+	//20	  82367261 ns/op	37964987 B/op	   80132 allocs/op
+}
+
+func TestKvCache_Get(t *testing.T) {
+	c := NewKvCache("test", DefaultConfig())
+	c.Put("", &KeyValue{Version: 1})
+	c.Put("/", &KeyValue{Version: 1})
+	c.Put("/a/b/c/d/e/1", &KeyValue{Version: 1})
+	c.Put("/a/b/c/d/e/2", &KeyValue{Version: 2})
+	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 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)
+	}
+
+	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)
+	}
+
+	l, b := -1, false
+	c.ForEach(func(k string, v *KeyValue) (next bool) {
+		next = false
+		l++
+		return
+	})
+	if l != 0 {
+		t.Fatalf("TestKvCache ForEach() failed, %d", l)
+	}
+	c.ForEach(func(k string, v *KeyValue) (next bool) {
+		next = true
+		l++
+		if v.Version == 4 {
+			b = true
+		}
+		return
+	})
+	if l != 4 || !b {
+		t.Fatalf("TestKvCache ForEach() failed, %d, %v", l, b)
+	}
+
+	c.Remove("")
+	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)
+	}
 }
diff --git a/server/core/backend/cache_kv.go b/server/core/backend/cacher_kv.go
similarity index 66%
copy from server/core/backend/cache_kv.go
copy to server/core/backend/cacher_kv.go
index cedb9dc..6c6aa99 100644
--- a/server/core/backend/cache_kv.go
+++ b/server/core/backend/cacher_kv.go
@@ -17,97 +17,20 @@
 package backend
 
 import (
+	"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/core/proto"
+	"github.com/apache/incubator-servicecomb-service-center/server/infra/registry"
 	"github.com/coreos/etcd/mvcc/mvccpb"
 	"golang.org/x/net/context"
 	"sync"
 	"time"
 )
 
-type KvCache struct {
-	owner       *KvCacher
-	size        int
-	store       map[string]*mvccpb.KeyValue
-	rwMux       sync.RWMutex
-	lastRefresh time.Time
-	lastMaxSize int
-}
-
-func (c *KvCache) Version() int64 {
-	return c.owner.lw.Revision()
-}
-
-func (c *KvCache) Data(k interface{}) interface{} {
-	c.rwMux.RLock()
-	kv, ok := c.store[k.(string)]
-	c.rwMux.RUnlock()
-	if !ok {
-		return nil
-	}
-	return kv
-}
-
-func (c *KvCache) Have(k interface{}) (ok bool) {
-	c.rwMux.RLock()
-	_, ok = c.store[k.(string)]
-	c.rwMux.RUnlock()
-	return
-}
-
-func (c *KvCache) RLock() map[string]*mvccpb.KeyValue {
-	c.rwMux.RLock()
-	return c.store
-}
-
-func (c *KvCache) RUnlock() {
-	c.rwMux.RUnlock()
-}
-
-func (c *KvCache) Lock() map[string]*mvccpb.KeyValue {
-	c.rwMux.Lock()
-	return c.store
-}
-
-func (c *KvCache) Unlock() {
-	l := len(c.store)
-	if l > c.lastMaxSize {
-		c.lastMaxSize = l
-	}
-	if c.size >= l &&
-		c.lastMaxSize > c.size*DEFAULT_COMPACT_TIMES &&
-		time.Now().Sub(c.lastRefresh) >= DEFAULT_COMPACT_TIMEOUT {
-		c.compact()
-		c.lastMaxSize = l
-		c.lastRefresh = time.Now()
-	}
-
-	c.rwMux.Unlock()
-}
-
-func (c *KvCache) compact() {
-	// gc
-	newCache := make(map[string]*mvccpb.KeyValue, c.size)
-	for k, v := range c.store {
-		newCache[k] = v
-	}
-	c.store = newCache
-
-	util.Logger().Infof("cache %s is not in use over %s, compact capacity to size %d->%d",
-		c.owner.Cfg.Prefix, DEFAULT_COMPACT_TIMEOUT, c.lastMaxSize, c.size)
-}
-
-func (c *KvCache) Size() (l int) {
-	c.rwMux.RLock()
-	l = len(c.store)
-	c.rwMux.RUnlock()
-	return
-}
-
 type KvCacher struct {
-	Cfg Config
+	Cfg *Config
 
-	name         string
 	lastRev      int64
 	noEventCount int
 
@@ -119,10 +42,6 @@ type KvCacher struct {
 	goroutine *util.GoRoutine
 }
 
-func (c *KvCacher) Name() string {
-	return c.name
-}
-
 func (c *KvCacher) needList() bool {
 	rev := c.lw.Revision()
 	defer func() { c.lastRev = rev }()
@@ -147,11 +66,12 @@ func (c *KvCacher) needList() bool {
 }
 
 func (c *KvCacher) doList(cfg ListWatchConfig) error {
-	kvs, err := c.lw.List(cfg)
+	resp, err := c.lw.List(cfg)
 	if err != nil {
 		return err
 	}
 
+	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 &&
@@ -189,12 +109,40 @@ func (c *KvCacher) ListAndWatch(ctx context.Context) error {
 
 func (c *KvCacher) handleWatcher(watcher *Watcher) error {
 	defer watcher.Stop()
-	for evts := range watcher.EventBus() {
-		if evts[0].Type == proto.EVT_ERROR {
-			err := evts[0].Object.(error)
-			return err
+	for resp := range watcher.EventBus() {
+		if resp == nil {
+			return errors.New("handle watcher error")
+		}
+
+		start := time.Now()
+		evts := make([]KvEvent, 0, len(resp.Kvs))
+		for _, kv := range resp.Kvs {
+			evt := KvEvent{Prefix: c.lw.Prefix, Revision: kv.ModRevision}
+			switch {
+			case resp.Action == registry.Put && kv.Version == 1:
+				evt.Type, evt.KV = proto.EVT_CREATE, c.doParse(kv)
+			case resp.Action == registry.Put:
+				evt.Type, evt.KV = proto.EVT_UPDATE, c.doParse(kv)
+			case resp.Action == registry.Delete:
+				evt.Type = proto.EVT_DELETE
+				if kv.Value == nil {
+					// it will happen in embed mode, and then need to get the cache value to unmarshal
+					evt.KV = c.cache.Get(util.BytesToStringWithNoCopy(kv.Key))
+				} else {
+					evt.KV = c.doParse(kv)
+				}
+			default:
+				util.Logger().Errorf(nil, "unknown KeyValue %v", kv)
+				continue
+			}
+			if evt.KV == nil {
+				continue
+			}
+			evts = append(evts, evt)
 		}
 		c.sync(evts)
+		util.LogDebugOrWarnf(start, "finish to handle %d events, prefix: %s, rev: %d",
+			len(evts), c.Cfg.Prefix, c.lw.Revision())
 	}
 	return nil
 }
@@ -210,7 +158,9 @@ func (c *KvCacher) needDeferHandle(evts []KvEvent) bool {
 func (c *KvCacher) refresh(ctx context.Context) {
 	util.Logger().Debugf("start to list and watch %s", c.Cfg)
 	retries := 0
-	ticker := time.NewTicker(DEFAULT_METRICS_INTERVAL)
+
+	timer := time.NewTimer(minWaitInterval)
+	defer timer.Stop()
 	for {
 		nextPeriod := minWaitInterval
 		if err := c.ListAndWatch(ctx); err != nil {
@@ -220,66 +170,17 @@ func (c *KvCacher) refresh(ctx context.Context) {
 			retries = 0
 		}
 
-		timer := time.NewTimer(nextPeriod)
 		select {
 		case <-ctx.Done():
-			timer.Stop()
-
 			util.Logger().Debugf("stop to list and watch %s", c.Cfg)
 			return
-		case <-ticker.C:
-			timer.Stop()
-
-			ReportCacheMetrics(c.Name(), "raw", c.cache.RLock())
-			c.cache.RUnlock()
-		case <-timer.C:
-		}
-	}
-}
-
-func (c *KvCacher) deferHandle(ctx context.Context) {
-	if c.Cfg.DeferHandler == nil {
-		return
-	}
-
-	var (
-		evts = make([]KvEvent, eventBlockSize)
-		i    int
-	)
-	for {
-		timer := time.NewTimer(300 * time.Millisecond)
-		select {
-		case <-ctx.Done():
-			timer.Stop()
-			return
-		case evt, ok := <-c.Cfg.DeferHandler.HandleChan():
-			timer.Stop()
-
-			if !ok {
-				<-time.After(time.Second)
-				continue
-			}
-
-			if i >= eventBlockSize {
-				c.onEvents(evts[:i])
-				evts = make([]KvEvent, eventBlockSize)
-				i = 0
-			}
-
-			evts[i] = evt
-			i++
 		case <-timer.C:
-			if i == 0 {
-				continue
-			}
-
-			c.onEvents(evts[:i])
-			evts = make([]KvEvent, eventBlockSize)
-			i = 0
+			timer.Reset(nextPeriod)
 		}
 	}
 }
 
+// keep the evts valid when call sync
 func (c *KvCacher) sync(evts []KvEvent) {
 	if len(evts) == 0 {
 		return
@@ -293,19 +194,7 @@ func (c *KvCacher) sync(evts []KvEvent) {
 }
 
 func (c *KvCacher) filter(rev int64, items []*mvccpb.KeyValue) []KvEvent {
-	store := c.cache.RLock()
-	defer c.cache.RUnlock()
-
-	oc, nc := len(store), len(items)
-	tc := oc + nc
-	if tc == 0 {
-		return nil
-	}
-	max := oc
-	if nc > oc {
-		max = nc
-	}
-
+	nc := len(items)
 	newStore := make(map[string]*mvccpb.KeyValue, nc)
 	for _, kv := range items {
 		newStore[util.BytesToStringWithNoCopy(kv.Key)] = kv
@@ -313,14 +202,14 @@ func (c *KvCacher) filter(rev int64, items []*mvccpb.KeyValue) []KvEvent {
 	filterStopCh := make(chan struct{})
 	eventsCh := make(chan [eventBlockSize]KvEvent, 2)
 
-	go c.filterDelete(store, newStore, rev, eventsCh, filterStopCh)
+	go c.filterDelete(newStore, rev, eventsCh, filterStopCh)
 
-	go c.filterCreateOrUpdate(store, newStore, rev, eventsCh, filterStopCh)
+	go c.filterCreateOrUpdate(newStore, rev, eventsCh, filterStopCh)
 
-	evts := make([]KvEvent, 0, max)
+	evts := make([]KvEvent, 0, nc)
 	for block := range eventsCh {
 		for _, e := range block {
-			if e.Object == nil {
+			if e.KV == nil {
 				break
 			}
 			evts = append(evts, e)
@@ -329,14 +218,17 @@ func (c *KvCacher) filter(rev int64, items []*mvccpb.KeyValue) []KvEvent {
 	return evts
 }
 
-func (c *KvCacher) filterDelete(store map[string]*mvccpb.KeyValue, newStore map[string]*mvccpb.KeyValue,
+func (c *KvCacher) filterDelete(newStore map[string]*mvccpb.KeyValue,
 	rev int64, eventsCh chan [eventBlockSize]KvEvent, filterStopCh chan struct{}) {
 	var block [eventBlockSize]KvEvent
 	i := 0
-	for k, v := range store {
+
+	c.cache.ForEach(func(k string, v *KeyValue) (next bool) {
+		next = true
+
 		_, ok := newStore[k]
 		if ok {
-			continue
+			return
 		}
 
 		if i >= eventBlockSize {
@@ -349,10 +241,11 @@ func (c *KvCacher) filterDelete(store map[string]*mvccpb.KeyValue, newStore map[
 			Revision: rev,
 			Type:     proto.EVT_DELETE,
 			Prefix:   c.Cfg.Prefix,
-			Object:   v,
+			KV:       v,
 		}
 		i++
-	}
+		return
+	})
 
 	if i > 0 {
 		eventsCh <- block
@@ -361,26 +254,29 @@ func (c *KvCacher) filterDelete(store map[string]*mvccpb.KeyValue, newStore map[
 	close(filterStopCh)
 }
 
-func (c *KvCacher) filterCreateOrUpdate(store map[string]*mvccpb.KeyValue, newStore map[string]*mvccpb.KeyValue,
+func (c *KvCacher) filterCreateOrUpdate(newStore map[string]*mvccpb.KeyValue,
 	rev int64, eventsCh chan [eventBlockSize]KvEvent, filterStopCh chan struct{}) {
 	var block [eventBlockSize]KvEvent
 	i := 0
+
 	for k, v := range newStore {
-		ov, ok := store[k]
-		if !ok {
+		ov := c.cache.Get(k)
+		if ov == nil {
 			if i >= eventBlockSize {
 				eventsCh <- block
 				block = [eventBlockSize]KvEvent{}
 				i = 0
 			}
 
-			block[i] = KvEvent{
-				Revision: rev,
-				Type:     proto.EVT_CREATE,
-				Prefix:   c.Cfg.Prefix,
-				Object:   v,
+			if kv := c.doParse(v); kv != nil {
+				block[i] = KvEvent{
+					Revision: rev,
+					Type:     proto.EVT_CREATE,
+					Prefix:   c.Cfg.Prefix,
+					KV:       kv,
+				}
+				i++
 			}
-			i++
 			continue
 		}
 
@@ -394,13 +290,15 @@ func (c *KvCacher) filterCreateOrUpdate(store map[string]*mvccpb.KeyValue, newSt
 			i = 0
 		}
 
-		block[i] = KvEvent{
-			Revision: rev,
-			Type:     proto.EVT_UPDATE,
-			Prefix:   c.Cfg.Prefix,
-			Object:   v,
+		if kv := c.doParse(v); kv != nil {
+			block[i] = KvEvent{
+				Revision: rev,
+				Type:     proto.EVT_UPDATE,
+				Prefix:   c.Cfg.Prefix,
+				KV:       kv,
+			}
+			i++
 		}
-		i++
 	}
 
 	if i > 0 {
@@ -412,13 +310,57 @@ func (c *KvCacher) filterCreateOrUpdate(store map[string]*mvccpb.KeyValue, newSt
 	close(eventsCh)
 }
 
+func (c *KvCacher) deferHandle(ctx context.Context) {
+	if c.Cfg.DeferHandler == nil {
+		return
+	}
+
+	var (
+		evts = make([]KvEvent, eventBlockSize)
+		i    int
+	)
+	interval := 300 * time.Millisecond
+	timer := time.NewTimer(interval)
+	defer timer.Stop()
+	for {
+		select {
+		case <-ctx.Done():
+			return
+		case evt, ok := <-c.Cfg.DeferHandler.HandleChan():
+			if !ok {
+				return
+			}
+
+			if i >= eventBlockSize {
+				c.onEvents(evts[:i])
+				evts = make([]KvEvent, eventBlockSize)
+				i = 0
+			}
+
+			evts[i] = evt
+			i++
+
+			util.ResetTimer(timer, interval)
+		case <-timer.C:
+			timer.Reset(interval)
+
+			if i == 0 {
+				continue
+			}
+
+			c.onEvents(evts[:i])
+			evts = make([]KvEvent, eventBlockSize)
+			i = 0
+		}
+	}
+}
+
 func (c *KvCacher) onEvents(evts []KvEvent) {
 	init := !c.IsReady()
-	store := c.cache.Lock()
 	for i, evt := range evts {
-		kv := evt.Object.(*mvccpb.KeyValue)
-		key := util.BytesToStringWithNoCopy(kv.Key)
-		prevKv, ok := store[key]
+		key := util.BytesToStringWithNoCopy(evt.KV.Key)
+		prevKv := c.cache.Get(key)
+		ok := prevKv != nil
 
 		switch evt.Type {
 		case proto.EVT_CREATE, proto.EVT_UPDATE:
@@ -435,21 +377,20 @@ func (c *KvCacher) onEvents(evts []KvEvent) {
 				evt.Type = proto.EVT_UPDATE
 			}
 
-			store[key] = kv
+			c.cache.Put(key, evt.KV)
 			evts[i] = evt
 		case proto.EVT_DELETE:
 			if !ok {
-				util.Logger().Warnf(nil, "unexpected %s event! key %s does not cache", evt.Type, key)
+				util.Logger().Warnf(nil, "unexpected %s event! key %s does not cache",
+					evt.Type, key)
 			} else {
-				delete(store, key)
+				evt.KV = prevKv
+				c.cache.Remove(key)
 			}
-			evt.Object = prevKv // maybe nil
 			evts[i] = evt
 		}
 	}
 
-	c.cache.Unlock()
-
 	c.onKvEvents(evts)
 }
 
@@ -457,16 +398,22 @@ func (c *KvCacher) onKvEvents(evts []KvEvent) {
 	if c.Cfg.OnEvent == nil {
 		return
 	}
+
 	defer util.RecoverAndReport()
 
 	for _, evt := range evts {
-		if evt.Object == nil {
-			continue
-		}
 		c.Cfg.OnEvent(evt)
 	}
 }
 
+func (c *KvCacher) doParse(src *mvccpb.KeyValue) (kv *KeyValue) {
+	kv = new(KeyValue)
+	if err := kv.From(c.Cfg.Parser, src); err != nil {
+		util.Logger().Errorf(err, "parse %s value failed", util.BytesToStringWithNoCopy(src.Key))
+	}
+	return kv
+}
+
 func (c *KvCacher) Cache() Cache {
 	return c.cache
 }
@@ -475,6 +422,7 @@ func (c *KvCacher) Run() {
 	c.once.Do(func() {
 		c.goroutine.Do(c.refresh)
 		c.goroutine.Do(c.deferHandle)
+		c.goroutine.Do(c.reportMetrics)
 	})
 }
 
@@ -497,23 +445,27 @@ func (c *KvCacher) IsReady() bool {
 	}
 }
 
-func NewKvCache(c *KvCacher, size int) *KvCache {
-	return &KvCache{
-		owner:       c,
-		size:        size,
-		store:       make(map[string]*mvccpb.KeyValue, size),
-		lastRefresh: time.Now(),
+func (c *KvCacher) reportMetrics(ctx context.Context) {
+	if !core.ServerInfo.Config.EnablePProf ||
+		!core.ServerInfo.Config.EnableCache {
+		return
 	}
-}
+	timer := time.NewTimer(DEFAULT_METRICS_INTERVAL)
+	defer timer.Stop()
+	for {
+		select {
+		case <-ctx.Done():
+			return
+		case <-timer.C:
+			c.cache.ReportMetrics()
 
-func NewKvCacher(name string, opts ...ConfigOption) *KvCacher {
-	cfg := DefaultConfig()
-	for _, opt := range opts {
-		opt(&cfg)
+			timer.Reset(DEFAULT_METRICS_INTERVAL)
+		}
 	}
+}
 
+func NewKvCacher(name string, cfg *Config) *KvCacher {
 	cacher := &KvCacher{
-		name:  name,
 		Cfg:   cfg,
 		ready: make(chan struct{}),
 		lw: ListWatcher{
@@ -522,6 +474,6 @@ func NewKvCacher(name string, opts ...ConfigOption) *KvCacher {
 		},
 		goroutine: util.NewGo(context.Background()),
 	}
-	cacher.cache = NewKvCache(cacher, cfg.InitSize)
+	cacher.cache = NewKvCache(name, cfg)
 	return cacher
 }
diff --git a/server/core/backend/common.go b/server/core/backend/common.go
index 3895f57..1ae79d3 100644
--- a/server/core/backend/common.go
+++ b/server/core/backend/common.go
@@ -22,6 +22,36 @@ import (
 	"time"
 )
 
+const (
+	// re-list etcd when there is no event coming in more than 1h(=120*30s)
+	DEFAULT_MAX_NO_EVENT_INTERVAL = 120
+	DEFAULT_LISTWATCH_TIMEOUT     = 30 * time.Second
+
+	DEFAULT_SELF_PRESERVATION_PERCENT = 0.8
+	DEFAULT_CACHE_INIT_SIZE           = 100
+)
+
+const (
+	DEFAULT_METRICS_INTERVAL = 30 * time.Second
+	DEFAULT_COMPACT_TIMES    = 2
+	DEFAULT_COMPACT_TIMEOUT  = 5 * time.Minute
+
+	minWaitInterval = 1 * time.Second
+	eventBlockSize  = 1000
+)
+
+const DEFAULT_CHECK_WINDOW = 2 * time.Second // instance DELETE event will be delay.
+
+const TIME_FORMAT = "15:04:05.000"
+
+const EVENT_BUS_MAX_SIZE = 1000
+
+var closedCh = make(chan struct{})
+
+func init() {
+	close(closedCh)
+}
+
 type StoreType int
 
 func (st StoreType) String() string {
@@ -74,67 +104,50 @@ var TypeNames = []string{
 	typeEnd:          "TYPEEND",
 }
 
-var TypeRoots = map[StoreType]string{
-	SERVICE:          apt.GetServiceRootKey(""),
-	INSTANCE:         apt.GetInstanceRootKey(""),
-	DOMAIN:           apt.GetDomainRootKey() + "/",
-	SCHEMA:           apt.GetServiceSchemaRootKey(""),
-	SCHEMA_SUMMARY:   apt.GetServiceSchemaSummaryRootKey(""),
-	RULE:             apt.GetServiceRuleRootKey(""),
-	LEASE:            apt.GetInstanceLeaseRootKey(""),
-	SERVICE_INDEX:    apt.GetServiceIndexRootKey(""),
-	SERVICE_ALIAS:    apt.GetServiceAliasRootKey(""),
-	SERVICE_TAG:      apt.GetServiceTagRootKey(""),
-	RULE_INDEX:       apt.GetServiceRuleIndexRootKey(""),
-	DEPENDENCY:       apt.GetServiceDependencyRootKey(""),
-	DEPENDENCY_RULE:  apt.GetServiceDependencyRuleRootKey(""),
-	DEPENDENCY_QUEUE: apt.GetServiceDependencyQueueRootKey(""),
-	PROJECT:          apt.GetProjectRootKey(""),
-}
+var TypeConfig = map[StoreType]*Config{
+	SERVICE: DefaultConfig().WithPrefix(apt.GetServiceRootKey("")).
+		WithInitSize(500).WithParser(ServiceParser),
 
-var TypeInitSize = map[StoreType]int{
-	SERVICE:          500,
-	INSTANCE:         1000,
-	DOMAIN:           100,
-	SCHEMA:           0,
-	SCHEMA_SUMMARY:   100,
-	RULE:             100,
-	LEASE:            1000,
-	SERVICE_INDEX:    500,
-	SERVICE_ALIAS:    100,
-	SERVICE_TAG:      100,
-	RULE_INDEX:       100,
-	DEPENDENCY:       100,
-	DEPENDENCY_RULE:  100,
-	DEPENDENCY_QUEUE: 100,
-	PROJECT:          100,
-}
+	INSTANCE: DefaultConfig().WithPrefix(apt.GetInstanceRootKey("")).
+		WithInitSize(1000).WithParser(InstanceParser).
+		WithDeferHandler(NewInstanceEventDeferHandler()),
 
-const (
-	// re-list etcd when there is no event coming in more than 2h(=240*30s)
-	DEFAULT_MAX_NO_EVENT_INTERVAL = 240
-	DEFAULT_LISTWATCH_TIMEOUT     = 30 * time.Second
+	DOMAIN: DefaultConfig().WithPrefix(apt.GetDomainRootKey() + "/").
+		WithInitSize(100).WithParser(StringParser),
 
-	DEFAULT_SELF_PRESERVATION_PERCENT = 0.8
-	DEFAULT_CACHE_INIT_SIZE           = 100
-)
+	SCHEMA: DefaultConfig().WithPrefix(apt.GetServiceSchemaRootKey("")).
+		WithInitSize(0),
 
-const (
-	DEFAULT_METRICS_INTERVAL = 30 * time.Second
+	SCHEMA_SUMMARY: DefaultConfig().WithPrefix(apt.GetServiceSchemaSummaryRootKey("")).
+		WithInitSize(100).WithParser(StringParser),
 
-	DEFAULT_COMPACT_TIMES   = 2
-	DEFAULT_COMPACT_TIMEOUT = 5 * time.Minute
-	minWaitInterval         = 1 * time.Second
-	eventBlockSize          = 1000
-)
+	RULE: DefaultConfig().WithPrefix(apt.GetServiceRuleRootKey("")).
+		WithInitSize(100).WithParser(RuleParser),
 
-const (
-	DEFAULT_MAX_EVENT_COUNT   = 1000
-	DEFAULT_ADD_QUEUE_TIMEOUT = 5 * time.Second
-)
+	LEASE: DefaultConfig().WithPrefix(apt.GetInstanceLeaseRootKey("")).
+		WithInitSize(1000).WithParser(StringParser),
 
-const DEFAULT_CHECK_WINDOW = 2 * time.Second // instance DELETE event will be delay.
+	SERVICE_INDEX: DefaultConfig().WithPrefix(apt.GetServiceIndexRootKey("")).
+		WithInitSize(500).WithParser(StringParser),
 
-const TIME_FORMAT = "15:04:05.000"
+	SERVICE_ALIAS: DefaultConfig().WithPrefix(apt.GetServiceAliasRootKey("")).
+		WithInitSize(100).WithParser(StringParser),
 
-const EVENT_BUS_MAX_SIZE = 1000
+	SERVICE_TAG: DefaultConfig().WithPrefix(apt.GetServiceTagRootKey("")).
+		WithInitSize(100).WithParser(MapParser),
+
+	RULE_INDEX: DefaultConfig().WithPrefix(apt.GetServiceRuleIndexRootKey("")).
+		WithInitSize(100).WithParser(StringParser),
+
+	DEPENDENCY: DefaultConfig().WithPrefix(apt.GetServiceDependencyRootKey("")).
+		WithInitSize(100),
+
+	DEPENDENCY_RULE: DefaultConfig().WithPrefix(apt.GetServiceDependencyRuleRootKey("")).
+		WithInitSize(100).WithParser(DependencyRuleParser),
+
+	DEPENDENCY_QUEUE: DefaultConfig().WithPrefix(apt.GetServiceDependencyQueueRootKey("")).
+		WithInitSize(100).WithParser(DependencyQueueParser),
+
+	PROJECT: DefaultConfig().WithPrefix(apt.GetProjectRootKey("")).
+		WithInitSize(100).WithParser(StringParser),
+}
diff --git a/server/core/backend/config.go b/server/core/backend/config.go
index 13e162d..32c2e41 100644
--- a/server/core/backend/config.go
+++ b/server/core/backend/config.go
@@ -28,48 +28,71 @@ type Config struct {
 	NoEventMaxInterval int
 	Timeout            time.Duration
 	Period             time.Duration
-	OnEvent            KvEventFunc
 	DeferHandler       DeferHandler
+	OnEvent            KvEventFunc
+	Parser             *Parser
 }
 
-func (cfg Config) String() string {
+func (cfg *Config) String() string {
 	return fmt.Sprintf("{prefix: %s, timeout: %s, period: %s}",
 		cfg.Prefix, cfg.Timeout, cfg.Period)
 }
 
-type ConfigOption func(*Config)
+func (cfg *Config) WithPrefix(key string) *Config {
+	cfg.Prefix = key
+	return cfg
+}
+
+func (cfg *Config) WithInitSize(size int) *Config {
+	cfg.InitSize = size
+	return cfg
+}
 
-func WithPrefix(key string) ConfigOption {
-	return func(cfg *Config) { cfg.Prefix = key }
+func (cfg *Config) WithTimeout(ot time.Duration) *Config {
+	cfg.Timeout = ot
+	return cfg
 }
 
-func WithInitSize(size int) ConfigOption {
-	return func(cfg *Config) { cfg.InitSize = size }
+func (cfg *Config) WithPeriod(ot time.Duration) *Config {
+	cfg.Period = ot
+	return cfg
 }
 
-func WithTimeout(ot time.Duration) ConfigOption {
-	return func(cfg *Config) { cfg.Timeout = ot }
+func (cfg *Config) WithDeferHandler(h DeferHandler) *Config {
+	cfg.DeferHandler = h
+	return cfg
 }
 
-func WithPeriod(ot time.Duration) ConfigOption {
-	return func(cfg *Config) { cfg.Period = ot }
+func (cfg *Config) WithEventFunc(f KvEventFunc) *Config {
+	cfg.OnEvent = f
+	return cfg
 }
 
-func WithEventFunc(f KvEventFunc) ConfigOption {
-	return func(cfg *Config) { cfg.OnEvent = f }
+func (cfg *Config) AppendEventFunc(f KvEventFunc) *Config {
+	if prev := cfg.OnEvent; prev != nil {
+		next := f
+		f = func(evt KvEvent) {
+			prev(evt)
+			next(evt)
+		}
+	}
+	cfg.OnEvent = f
+	return cfg
 }
 
-func WithDeferHandler(h DeferHandler) ConfigOption {
-	return func(cfg *Config) { cfg.DeferHandler = h }
+func (cfg *Config) WithParser(parser *Parser) *Config {
+	cfg.Parser = parser
+	return cfg
 }
 
-func DefaultConfig() Config {
-	return Config{
+func DefaultConfig() *Config {
+	return &Config{
 		Prefix:             "/",
 		Timeout:            DEFAULT_LISTWATCH_TIMEOUT,
 		Period:             time.Second,
 		NoEventMaxInterval: 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 aab844b..fea9485 100644
--- a/server/core/backend/defer_instance.go
+++ b/server/core/backend/defer_instance.go
@@ -17,10 +17,8 @@
 package backend
 
 import (
-	"encoding/json"
 	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
 	pb "github.com/apache/incubator-servicecomb-service-center/server/core/proto"
-	"github.com/coreos/etcd/mvcc/mvccpb"
 	"golang.org/x/net/context"
 	"sync"
 	"time"
@@ -50,7 +48,7 @@ func (iedh *InstanceEventDeferHandler) OnCondition(cache Cache, evts []KvEvent)
 
 	iedh.once.Do(func() {
 		iedh.cache = cache
-		iedh.items = make(map[string]deferItem, eventBlockSize)
+		iedh.items = make(map[string]deferItem)
 		iedh.pendingCh = make(chan []KvEvent, eventBlockSize)
 		iedh.deferCh = make(chan KvEvent, eventBlockSize)
 		iedh.resetCh = make(chan struct{})
@@ -62,7 +60,7 @@ func (iedh *InstanceEventDeferHandler) OnCondition(cache Cache, evts []KvEvent)
 }
 
 func (iedh *InstanceEventDeferHandler) recoverOrDefer(evt KvEvent) error {
-	kv := evt.Object.(*mvccpb.KeyValue)
+	kv := evt.KV
 	key := util.BytesToStringWithNoCopy(kv.Key)
 	_, ok := iedh.items[key]
 	switch evt.Type {
@@ -77,20 +75,7 @@ func (iedh *InstanceEventDeferHandler) recoverOrDefer(evt KvEvent) error {
 			return nil
 		}
 
-		var instance pb.MicroServiceInstance
-
-		// it will happen in embed mode, and then need to get the cache value to unmarshal
-		if kv.Value == nil {
-			if c, ok := iedh.cache.Data(key).(*mvccpb.KeyValue); ok {
-				kv.Value = c.Value
-			}
-		}
-		err := json.Unmarshal(kv.Value, &instance)
-		if err != nil {
-			util.Logger().Errorf(err, "unmarshal instance file failed, key is %s, value is %s", key,
-				util.BytesToStringWithNoCopy(kv.Value))
-			return err
-		}
+		instance := kv.Value.(*pb.MicroServiceInstance)
 		iedh.items[key] = deferItem{
 			ttl: time.NewTimer(
 				time.Duration(instance.HealthCheck.Interval*(instance.HealthCheck.Times+1)) * time.Second),
@@ -106,7 +91,9 @@ func (iedh *InstanceEventDeferHandler) HandleChan() <-chan KvEvent {
 
 func (iedh *InstanceEventDeferHandler) check(ctx context.Context) {
 	defer util.RecoverAndReport()
+
 	t, n := time.NewTimer(DEFAULT_CHECK_WINDOW), false
+	defer t.Stop()
 	for {
 		select {
 		case <-ctx.Done():
@@ -118,10 +105,7 @@ func (iedh *InstanceEventDeferHandler) check(ctx context.Context) {
 
 			del := len(iedh.items)
 			if del > 0 && !n {
-				if !t.Stop() {
-					<-t.C
-				}
-				t.Reset(DEFAULT_CHECK_WINDOW)
+				util.ResetTimer(t, DEFAULT_CHECK_WINDOW)
 				n = true
 			}
 
@@ -132,7 +116,6 @@ func (iedh *InstanceEventDeferHandler) check(ctx context.Context) {
 					del, total, iedh.Percent*100)
 			}
 		case <-t.C:
-			t.Reset(DEFAULT_CHECK_WINDOW)
 			n = false
 
 			for key, item := range iedh.items {
@@ -151,22 +134,29 @@ func (iedh *InstanceEventDeferHandler) check(ctx context.Context) {
 				iedh.renew()
 				util.Logger().Warnf(nil, "self preservation is stopped")
 			}
+
+			t.Reset(DEFAULT_CHECK_WINDOW)
 		case <-iedh.resetCh:
 			iedh.renew()
 			util.Logger().Warnf(nil, "self preservation is reset")
+
+			util.ResetTimer(t, DEFAULT_CHECK_WINDOW)
 		}
 	}
 }
 
 func (iedh *InstanceEventDeferHandler) recover(evt KvEvent) {
-	key := util.BytesToStringWithNoCopy(evt.Object.(*mvccpb.KeyValue).Key)
+	key := util.BytesToStringWithNoCopy(evt.KV.Key)
 	delete(iedh.items, key)
 	iedh.deferCh <- evt
 }
 
 func (iedh *InstanceEventDeferHandler) renew() {
 	iedh.enabled = false
-	iedh.items = make(map[string]deferItem, eventBlockSize)
+	for _, item := range iedh.items {
+		item.ttl.Stop()
+	}
+	iedh.items = make(map[string]deferItem)
 }
 
 func (iedh *InstanceEventDeferHandler) Reset() bool {
@@ -176,3 +166,7 @@ func (iedh *InstanceEventDeferHandler) Reset() bool {
 	}
 	return false
 }
+
+func NewInstanceEventDeferHandler() *InstanceEventDeferHandler {
+	return &InstanceEventDeferHandler{Percent: DEFAULT_SELF_PRESERVATION_PERCENT}
+}
diff --git a/server/core/backend/defer_test.go b/server/core/backend/defer_test.go
index 93acd97..42a43e8 100644
--- a/server/core/backend/defer_test.go
+++ b/server/core/backend/defer_test.go
@@ -17,11 +17,9 @@
 package backend
 
 import (
-	"encoding/json"
 	"fmt"
 	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
 	pb "github.com/apache/incubator-servicecomb-service-center/server/core/proto"
-	"github.com/coreos/etcd/mvcc/mvccpb"
 	"testing"
 	"time"
 )
@@ -42,94 +40,93 @@ func TestInstanceEventDeferHandler_OnCondition(t *testing.T) {
 }
 
 func TestInstanceEventDeferHandler_HandleChan(t *testing.T) {
-	inst := &pb.MicroServiceInstance{
+	b := &pb.MicroServiceInstance{
 		HealthCheck: &pb.HealthCheck{
 			Interval: 4,
 			Times:    0,
 		},
 	}
-	b, _ := json.Marshal(inst)
-	kv1 := &mvccpb.KeyValue{
+	kv1 := &KeyValue{
 		Key:   util.StringToBytesWithNoCopy("/1"),
 		Value: b,
 	}
-	kv2 := &mvccpb.KeyValue{
+	kv2 := &KeyValue{
 		Key:   util.StringToBytesWithNoCopy("/2"),
 		Value: b,
 	}
-	kv3 := &mvccpb.KeyValue{
+	kv3 := &KeyValue{
 		Key:   util.StringToBytesWithNoCopy("/3"),
 		Value: b,
 	}
-	kv4 := &mvccpb.KeyValue{
+	kv4 := &KeyValue{
 		Key:   util.StringToBytesWithNoCopy("/4"),
 		Value: b,
 	}
-	kv5 := &mvccpb.KeyValue{
+	kv5 := &KeyValue{
 		Key:   util.StringToBytesWithNoCopy("/5"),
 		Value: b,
 	}
-	kv6 := &mvccpb.KeyValue{
+	kv6 := &KeyValue{
 		Key:   util.StringToBytesWithNoCopy("/6"),
 		Value: b,
 	}
 
-	cache := NewKvCache(nil, 1)
-	cache.store["/1"] = kv1
-	cache.store["/2"] = kv2
-	cache.store["/3"] = kv3
-	cache.store["/4"] = kv4
-	cache.store["/5"] = kv5
-	cache.store["/6"] = kv6
+	cache := NewKvCache("test", DefaultConfig())
+	cache.Put("/1", kv1)
+	cache.Put("/2", kv2)
+	cache.Put("/3", kv3)
+	cache.Put("/4", kv4)
+	cache.Put("/5", kv5)
+	cache.Put("/6", kv6)
 
 	evts1 := []KvEvent{
 		{
-			Type:   pb.EVT_CREATE,
-			Object: kv1,
+			Type: pb.EVT_CREATE,
+			KV:   kv1,
 		},
 		{
-			Type:   pb.EVT_UPDATE,
-			Object: kv1,
+			Type: pb.EVT_UPDATE,
+			KV:   kv1,
 		},
 	}
 	evts2 := []KvEvent{
 		{
-			Type:   pb.EVT_DELETE,
-			Object: kv2,
+			Type: pb.EVT_DELETE,
+			KV:   kv2,
 		},
 		{
-			Type:   pb.EVT_DELETE,
-			Object: kv3,
+			Type: pb.EVT_DELETE,
+			KV:   kv3,
 		},
 		{
-			Type:   pb.EVT_DELETE,
-			Object: kv4,
+			Type: pb.EVT_DELETE,
+			KV:   kv4,
 		},
 		{
-			Type:   pb.EVT_DELETE,
-			Object: kv5,
+			Type: pb.EVT_DELETE,
+			KV:   kv5,
 		},
 		{
-			Type:   pb.EVT_DELETE,
-			Object: kv6,
+			Type: pb.EVT_DELETE,
+			KV:   kv6,
 		},
 	}
 	evts3 := []KvEvent{
 		{
-			Type:   pb.EVT_CREATE,
-			Object: kv2,
+			Type: pb.EVT_CREATE,
+			KV:   kv2,
 		},
 		{
-			Type:   pb.EVT_UPDATE,
-			Object: kv4,
+			Type: pb.EVT_UPDATE,
+			KV:   kv4,
 		},
 		{
-			Type:   pb.EVT_UPDATE,
-			Object: kv5,
+			Type: pb.EVT_UPDATE,
+			KV:   kv5,
 		},
 		{
-			Type:   pb.EVT_CREATE,
-			Object: kv6,
+			Type: pb.EVT_CREATE,
+			KV:   kv6,
 		},
 	}
 
@@ -159,7 +156,7 @@ func getEvents(t *testing.T, iedh *InstanceEventDeferHandler) {
 		select {
 		case evt := <-iedh.HandleChan():
 			fmt.Println(time.Now(), evt)
-			if string(evt.Object.(*mvccpb.KeyValue).Key) == "/3" {
+			if string(evt.KV.Key) == "/3" {
 				evt3 = &evt
 				if iedh.Percent == 0.01 && evt.Type == pb.EVT_DELETE {
 					t.Fatalf(`TestInstanceEventDeferHandler_HandleChan with 1%% failed`)
diff --git a/server/core/backend/event.go b/server/core/backend/event.go
index 839dc7a..720ef6f 100644
--- a/server/core/backend/event.go
+++ b/server/core/backend/event.go
@@ -20,13 +20,11 @@ import (
 	"github.com/apache/incubator-servicecomb-service-center/server/core/proto"
 )
 
-type KvEventFunc func(evt KvEvent)
-
 type KvEvent struct {
 	Revision int64
 	Type     proto.EventType
 	Prefix   string
-	Object   interface{}
+	KV       *KeyValue
 }
 
 type KvEventHandler interface {
diff --git a/server/core/backend/event_proxy.go b/server/core/backend/event_proxy.go
index 592c70a..3b02287 100644
--- a/server/core/backend/event_proxy.go
+++ b/server/core/backend/event_proxy.go
@@ -49,9 +49,7 @@ func (h *KvEventProxy) OnEvent(evt KvEvent) {
 }
 
 func NewEventProxy() *KvEventProxy {
-	return &KvEventProxy{
-		evtHandleFuncs: make([]KvEventFunc, 0, 5),
-	}
+	return &KvEventProxy{}
 }
 
 func EventProxy(t StoreType) *KvEventProxy {
diff --git a/server/core/backend/extend.go b/server/core/backend/extend.go
index 200d45e..dbc0cd1 100644
--- a/server/core/backend/extend.go
+++ b/server/core/backend/extend.go
@@ -23,26 +23,20 @@ import (
 
 type Entity interface {
 	Name() string
-	Prefix() string
-	InitSize() int
+	Config() *Config
 }
 
 type entity struct {
-	name     string
-	prefix   string
-	initSize int
+	name string
+	cfg  *Config
 }
 
 func (e *entity) Name() string {
 	return e.name
 }
 
-func (e *entity) Prefix() string {
-	return e.prefix
-}
-
-func (e *entity) InitSize() int {
-	return e.initSize
+func (e *entity) Config() *Config {
+	return e.cfg
 }
 
 func InstallType(e Entity) (id StoreType, err error) {
@@ -54,31 +48,22 @@ func InstallType(e Entity) (id StoreType, err error) {
 			return NONEXIST, fmt.Errorf("redeclare store type '%s'", n)
 		}
 	}
-	for _, r := range TypeRoots {
-		if r == e.Prefix() {
+	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())
-
-	TypeRoots[id] = e.Prefix()
-	TypeInitSize[id] = e.InitSize()
-
+	TypeConfig[id] = e.Config()
 	EventProxies[id] = NewEventProxy()
 	return
 }
 
-func NewEntity(name, prefix string, opts ...ConfigOption) Entity {
-	cfg := DefaultConfig()
-	for _, opt := range opts {
-		opt(&cfg)
-	}
-	cfg.Prefix = prefix
+func NewEntity(name string, cfg *Config) Entity {
 	return &entity{
-		name:     name,
-		prefix:   cfg.Prefix,
-		initSize: cfg.InitSize,
+		name: name,
+		cfg:  cfg,
 	}
 }
diff --git a/server/core/backend/extend_test.go b/server/core/backend/extend_test.go
index 6a37ce5..3f1e1af 100644
--- a/server/core/backend/extend_test.go
+++ b/server/core/backend/extend_test.go
@@ -25,12 +25,8 @@ func (e *extend) Name() string {
 	return "test"
 }
 
-func (e *extend) Prefix() string {
-	return "/test"
-}
-
-func (e *extend) InitSize() int {
-	return 0
+func (e *extend) Config() *Config {
+	return DefaultConfig().WithPrefix("/test")
 }
 
 func TestInstallType(t *testing.T) {
diff --git a/server/core/backend/indexer.go b/server/core/backend/indexer.go
index a88fec7..4f47a72 100644
--- a/server/core/backend/indexer.go
+++ b/server/core/backend/indexer.go
@@ -17,344 +17,78 @@
 package backend
 
 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"
 	"github.com/apache/incubator-servicecomb-service-center/server/infra/registry"
-	"github.com/coreos/etcd/mvcc/mvccpb"
 	"golang.org/x/net/context"
 	"strings"
-	"sync"
-	"time"
 )
 
-type Indexer struct {
-	BuildTimeout time.Duration
-	Root         string
-
-	cacher           Cacher
-	goroutine        *util.GoRoutine
-	ready            chan struct{}
-	prefixIndex      map[string]map[string]struct{}
-	prefixBuildQueue chan KvEvent
-	prefixLock       sync.RWMutex
-	isClose          bool
+type Response struct {
+	Kvs   []*KeyValue
+	Count int64
 }
 
-func (i *Indexer) Search(ctx context.Context, opts ...registry.PluginOpOption) (*registry.PluginResponse, error) {
-	op := registry.OpGet(opts...)
-
-	key := util.BytesToStringWithNoCopy(op.Key)
-
-	if !core.ServerInfo.Config.EnableCache ||
-		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.Name(), op)
-		return Registry().Do(ctx, opts...)
-	}
-
-	if op.Prefix {
-		resp, err := i.searchPrefixKeyWithCache(ctx, op)
-		if err != nil {
-			return nil, err
-		}
-
-		if len(resp.Kvs) > 0 || op.Mode == registry.MODE_CACHE {
-			return resp, nil
+func (pr *Response) MaxModRevision() (max int64) {
+	for _, kv := range pr.Kvs {
+		if max < kv.ModRevision {
+			max = kv.ModRevision
 		}
-
-		util.Logger().Debugf("can not find any key from %s cache with prefix, request etcd server, key: %s",
-			i.cacher.Name(), key)
-		return Registry().Do(ctx, opts...)
-	}
-
-	resp := &registry.PluginResponse{
-		Action:    op.Action,
-		Count:     0,
-		Revision:  i.Cache().Version(),
-		Succeeded: true,
-	}
-
-	if op.CountOnly {
-		if i.Cache().Have(key) {
-			resp.Count = 1
-			return resp, nil
-		}
-		if op.Mode == registry.MODE_CACHE {
-			return resp, nil
-		}
-
-		util.Logger().Debugf("%s cache does not store this key, request etcd server, key: %s",
-			i.cacher.Name(), key)
-		return Registry().Do(ctx, opts...)
-	}
-
-	cacheData := i.Cache().Data(key)
-	if cacheData == nil {
-		if op.Mode == registry.MODE_CACHE {
-			return resp, nil
-		}
-
-		util.Logger().Debugf("do not match any key in %s cache store, request etcd server, key: %s",
-			i.cacher.Name(), key)
-		return Registry().Do(ctx, opts...)
 	}
-
-	resp.Count = 1
-	resp.Kvs = []*mvccpb.KeyValue{cacheData.(*mvccpb.KeyValue)}
-	return resp, nil
+	return
 }
 
-func (i *Indexer) Cache() Cache {
-	return i.cacher.Cache()
+type Indexer interface {
+	Search(ctx context.Context, opts ...registry.PluginOpOption) (*Response, error)
+	Run()
+	Stop()
+	Ready() <-chan struct{}
 }
 
-func (i *Indexer) searchPrefixKeyWithCache(ctx context.Context, op registry.PluginOp) (*registry.PluginResponse, error) {
-	resp := &registry.PluginResponse{
-		Action:    op.Action,
-		Kvs:       []*mvccpb.KeyValue{},
-		Count:     0,
-		Revision:  i.Cache().Version(),
-		Succeeded: true,
-	}
-
-	prefix := util.BytesToStringWithNoCopy(op.Key)
-
-	i.prefixLock.RLock()
-	resp.Count = int64(i.getPrefixKey(nil, prefix))
-	if resp.Count == 0 || op.CountOnly {
-		i.prefixLock.RUnlock()
-		return resp, nil
-	}
-
-	t := time.Now()
-	keys := make([]string, 0, resp.Count)
-	i.getPrefixKey(&keys, prefix)
-	i.prefixLock.RUnlock()
-
-	kvs := make([]*mvccpb.KeyValue, resp.Count)
-	idx := 0
-	for _, key := range keys {
-		c := i.Cache().Data(key) // TODO too slow when big data is requested
-		if c == nil {
-			// it means resp.Count is not equal to len(keys)
-			util.Logger().Warnf(nil, "unexpected nil cache, maybe it is removed, key is %s", key)
-			continue
-		}
-		kvs[idx] = c.(*mvccpb.KeyValue)
-		idx++
-	}
-	util.LogNilOrWarnf(t, "too long to copy data[%d] from cache[%d] with prefix %s", idx, len(i.prefixIndex), prefix)
-
-	resp.Kvs = kvs[:idx]
-	return resp, nil
+type baseIndexer struct {
+	Cfg *Config
 }
 
-func (i *Indexer) OnCacheEvent(evt KvEvent) {
-	switch evt.Type {
-	case pb.EVT_INIT, pb.EVT_CREATE, pb.EVT_DELETE:
-	default:
-		return
-	}
-
-	if i.isClose {
-		return
-	}
-	defer util.RecoverAndReport()
-
-	ctx, _ := context.WithTimeout(context.Background(), i.BuildTimeout)
-	select {
-	case <-ctx.Done():
-		key := util.BytesToStringWithNoCopy(evt.Object.(*mvccpb.KeyValue).Key)
-		util.Logger().Warnf(nil, "add event to build index queue timed out(%s), key is %s [%s] event",
-			i.BuildTimeout, key, evt.Type)
-	case i.prefixBuildQueue <- evt:
-	}
-}
-
-func (i *Indexer) buildIndex() {
-	i.goroutine.Do(func(ctx context.Context) {
-		util.SafeCloseChan(i.ready)
-		lastCompactTime := time.Now()
-		ticker := time.NewTicker(DEFAULT_METRICS_INTERVAL)
-		max := 0
-		for {
-			select {
-			case <-ctx.Done():
-				return
-			case <-ticker.C:
-				i.prefixLock.RLock()
-				// report metrics
-				ReportCacheMetrics(i.cacher.Name(), "index", i.prefixIndex)
-				i.prefixLock.RUnlock()
-			case evt, ok := <-i.prefixBuildQueue:
-				if !ok {
-					return
-				}
-				t := time.Now()
-				key := util.BytesToStringWithNoCopy(evt.Object.(*mvccpb.KeyValue).Key)
-				prefix := key[:strings.LastIndex(key[:len(key)-1], "/")+1]
-
-				i.prefixLock.Lock()
-				switch evt.Type {
-				case pb.EVT_DELETE:
-					i.deletePrefixKey(prefix, key)
-				default:
-					i.addPrefixKey(prefix, key)
-				}
-
-				// compact
-				initSize, l := DEFAULT_CACHE_INIT_SIZE, len(i.prefixIndex)
-				if max < l {
-					max = l
-				}
-				if initSize >= l &&
-					max >= initSize*DEFAULT_COMPACT_TIMES &&
-					time.Now().Sub(lastCompactTime) >= DEFAULT_COMPACT_TIMEOUT {
-					i.compact()
-					max = l
-					lastCompactTime = time.Now()
-				}
-
-				i.prefixLock.Unlock()
-
-				util.LogNilOrWarnf(t, "too long to rebuild(action: %s) index[%d], key is %s",
-					evt.Type, len(i.prefixIndex), key)
-			}
-		}
-		util.Logger().Debugf("the goroutine building index %s is stopped", i.cacher.Name())
-	})
-	<-i.ready
-}
-
-func (i *Indexer) compact() {
-	n := make(map[string]map[string]struct{}, DEFAULT_CACHE_INIT_SIZE)
-	for k, v := range i.prefixIndex {
-		c, ok := n[k]
-		if !ok {
-			c = make(map[string]struct{}, len(v))
-			n[k] = c
-		}
-		for ck, cv := range v {
-			c[ck] = cv
-		}
-	}
-	i.prefixIndex = n
-
-	util.Logger().Infof("index %s: compact root capacity to size %d",
-		i.cacher.Name(), DEFAULT_CACHE_INIT_SIZE)
-}
-
-func (i *Indexer) getPrefixKey(arr *[]string, prefix string) (count int) {
-	keysRef, ok := i.prefixIndex[prefix]
-	if !ok {
-		return 0
-	}
-
-	for key := range keysRef {
-		n := i.getPrefixKey(arr, key)
-		if n == 0 {
-			count += len(keysRef)
-			if arr != nil {
-				// TODO support sort option
-				for k := range keysRef {
-					*arr = append(*arr, k)
-				}
-			}
-			break
-		}
-		count += n
+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)
 	}
-	return count
-}
 
-func (i *Indexer) addPrefixKey(prefix, key string) {
-	if i.Root == key {
-		return
+	resp, err := Registry().Do(ctx, opts...)
+	if err != nil {
+		return nil, err
 	}
 
-	keys, ok := i.prefixIndex[prefix]
-	if !ok {
-		// build parent index key and new child nodes
-		keys = make(map[string]struct{})
-		i.prefixIndex[prefix] = keys
-	} else if _, ok := keys[key]; ok {
+	r = new(Response)
+	r.Count = resp.Count
+	if len(resp.Kvs) == 0 {
 		return
 	}
 
-	keys[key], key = struct{}{}, prefix
-	prefix = key[:strings.LastIndex(key[:len(key)-1], "/")+1]
-
-	i.addPrefixKey(prefix, key)
-}
-
-func (i *Indexer) deletePrefixKey(prefix, key string) {
-	m, ok := i.prefixIndex[prefix]
-	if !ok {
-		return
-	}
-	delete(m, key)
-
-	// remove parent which has no child
-	if len(m) == 0 {
-		delete(i.prefixIndex, prefix)
-		i.deletePrefixKey(prefix[:strings.LastIndex(prefix[:len(prefix)-1], "/")+1], prefix)
+	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 *Indexer) Run() {
-	i.prefixLock.Lock()
-	if !i.isClose {
-		i.prefixLock.Unlock()
-		return
-	}
-	i.isClose = false
-	i.prefixLock.Unlock()
-
-	if !core.ServerInfo.Config.EnableCache {
-		util.SafeCloseChan(i.ready)
-		return
-	}
-
-	i.buildIndex()
-
-	i.cacher.Run()
+func (i *baseIndexer) Run() {
 }
 
-func (i *Indexer) Stop() {
-	i.prefixLock.Lock()
-	if i.isClose {
-		i.prefixLock.Unlock()
-		return
-	}
-	i.isClose = true
-	i.prefixLock.Unlock()
-
-	i.cacher.Stop()
-
-	i.goroutine.Close(true)
-
-	close(i.prefixBuildQueue)
-
-	util.SafeCloseChan(i.ready)
+func (i *baseIndexer) Stop() {
 }
 
-func (i *Indexer) Ready() <-chan struct{} {
-	<-i.cacher.Ready()
-	return i.ready
+func (i *baseIndexer) Ready() <-chan struct{} {
+	return closedCh
 }
 
-func NewCacheIndexer(root string, cr Cacher) *Indexer {
-	return &Indexer{
-		BuildTimeout:     DEFAULT_ADD_QUEUE_TIMEOUT,
-		Root:             root,
-		cacher:           cr,
-		prefixIndex:      make(map[string]map[string]struct{}, DEFAULT_CACHE_INIT_SIZE),
-		prefixBuildQueue: make(chan KvEvent, DEFAULT_MAX_EVENT_COUNT),
-		goroutine:        util.NewGo(context.Background()),
-		ready:            make(chan struct{}),
-		isClose:          true,
-	}
+func NewBaseIndexer(cfg *Config) (indexer *baseIndexer) {
+	return &baseIndexer{Cfg: cfg}
 }
diff --git a/server/core/backend/indexer_kv.go b/server/core/backend/indexer_kv.go
new file mode 100644
index 0000000..7cf2443
--- /dev/null
+++ b/server/core/backend/indexer_kv.go
@@ -0,0 +1,150 @@
+/*
+ * 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/core"
+	"github.com/apache/incubator-servicecomb-service-center/server/infra/registry"
+	"golang.org/x/net/context"
+	"strings"
+	"sync"
+	"time"
+)
+
+type CacheIndexer struct {
+	*baseIndexer
+
+	cacher  Cacher
+	lock    sync.Mutex
+	isClose bool
+}
+
+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 ||
+		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...)
+	}
+
+	var resp *Response
+	if op.Prefix {
+		resp = i.searchByPrefix(op)
+	} else {
+		resp = i.search(op)
+	}
+
+	if resp.Count > 0 || op.Mode == registry.MODE_CACHE {
+		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...)
+}
+
+func (i *CacheIndexer) search(op registry.PluginOp) *Response {
+	resp := new(Response)
+
+	key := util.BytesToStringWithNoCopy(op.Key)
+
+	kv := i.cacher.Cache().Get(key)
+	if kv != nil {
+		resp.Count = 1
+	}
+	if kv == nil || op.CountOnly {
+		return resp
+	}
+
+	resp.Kvs = []*KeyValue{kv}
+	return resp
+}
+
+func (i *CacheIndexer) searchByPrefix(op registry.PluginOp) *Response {
+	resp := new(Response)
+
+	prefix := util.BytesToStringWithNoCopy(op.Key)
+
+	resp.Count = int64(i.cacher.Cache().GetAll(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)
+
+	util.LogNilOrWarnf(t, "too long to index data[%d] from cache '%s'", len(kvs), i.cacher.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)
+	}
+	return
+}
diff --git a/server/core/backend/cacher.go b/server/core/backend/kv.go
similarity index 66%
rename from server/core/backend/cacher.go
rename to server/core/backend/kv.go
index 410ef50..73f0393 100644
--- a/server/core/backend/cacher.go
+++ b/server/core/backend/kv.go
@@ -16,18 +16,25 @@
  */
 package backend
 
-type Cache interface {
-	Version() int64
-	Data(interface{}) interface{}
-	Have(interface{}) bool
-	Size() int
+import (
+	"github.com/coreos/etcd/mvcc/mvccpb"
+)
+
+type KeyValue struct {
+	Key            []byte
+	Value          interface{}
+	Version        int64
+	CreateRevision int64
+	ModRevision    int64
 }
 
-type Cacher interface {
-	// Name is the cache size metric name
-	Name() string
-	Cache() Cache
-	Run()
-	Stop()
-	Ready() <-chan struct{}
+func (kv *KeyValue) From(p *Parser, s *mvccpb.KeyValue) error {
+	kv.Key = s.Key
+	kv.Version = s.Version
+	kv.CreateRevision = s.CreateRevision
+	kv.ModRevision = s.ModRevision
+
+	v, err := p.Unmarshal(s.Value)
+	kv.Value = v
+	return err
 }
diff --git a/server/core/backend/lease.go b/server/core/backend/lease.go
index e37cafc..89fcb2e 100644
--- a/server/core/backend/lease.go
+++ b/server/core/backend/lease.go
@@ -56,11 +56,15 @@ func (lat *LeaseTask) Do(ctx context.Context) (err error) {
 	}
 
 	lat.err, err = nil, nil
-	util.LogNilOrWarnf(recv, "renew lease %d(recv: %s, send: %s), key %s",
-		lat.LeaseID,
-		recv.Format(TIME_FORMAT),
-		start.Format(TIME_FORMAT),
-		lat.Key())
+
+	cost := time.Now().Sub(recv)
+	if cost >= 2*time.Second {
+		util.Logger().Warnf(nil, "[%s]renew lease %d(recv: %s, send: %s), key %s", cost,
+			lat.LeaseID,
+			recv.Format(TIME_FORMAT),
+			start.Format(TIME_FORMAT),
+			lat.Key())
+	}
 	return
 }
 
diff --git a/server/core/backend/listwatch.go b/server/core/backend/listwatch.go
index 0aa46ab..469a8d1 100644
--- a/server/core/backend/listwatch.go
+++ b/server/core/backend/listwatch.go
@@ -19,9 +19,7 @@ package backend
 import (
 	"fmt"
 	"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"
 	"sync"
 )
@@ -33,7 +31,7 @@ type ListWatcher struct {
 	rev int64
 }
 
-func (lw *ListWatcher) List(op ListWatchConfig) ([]*mvccpb.KeyValue, error) {
+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 {
@@ -41,10 +39,7 @@ func (lw *ListWatcher) List(op ListWatchConfig) ([]*mvccpb.KeyValue, error) {
 		return nil, err
 	}
 	lw.setRevision(resp.Revision)
-	if len(resp.Kvs) == 0 {
-		return nil, nil
-	}
-	return resp.Kvs, nil
+	return resp, nil
 }
 
 func (lw *ListWatcher) Revision() int64 {
@@ -59,7 +54,7 @@ func (lw *ListWatcher) Watch(op ListWatchConfig) *Watcher {
 	return newWatcher(lw, op)
 }
 
-func (lw *ListWatcher) doWatch(ctx context.Context, f func(evt []KvEvent)) error {
+func (lw *ListWatcher) doWatch(ctx context.Context, f func(*registry.PluginResponse)) error {
 	rev := lw.Revision()
 	opts := append(
 		registry.WatchPrefixOpOptions(lw.Prefix),
@@ -67,29 +62,12 @@ func (lw *ListWatcher) doWatch(ctx context.Context, f func(evt []KvEvent)) error
 		registry.WithWatchCallback(
 			func(message string, resp *registry.PluginResponse) error {
 				if resp == nil || len(resp.Kvs) == 0 {
-					return fmt.Errorf("unknown event %s", resp)
+					return fmt.Errorf("unknown event %s, watch prefix %s", resp, lw.Prefix)
 				}
 
-				util.Logger().Infof("caught event %s, watch prefix %s, start rev %d+1,", resp, lw.Prefix, rev)
-
 				lw.setRevision(resp.Revision)
 
-				evts := make([]KvEvent, len(resp.Kvs))
-				for i, kv := range resp.Kvs {
-					evt := KvEvent{Prefix: lw.Prefix, Revision: kv.ModRevision}
-					switch {
-					case resp.Action == registry.Put && kv.Version == 1:
-						evt.Type, evt.Object = proto.EVT_CREATE, kv
-					case resp.Action == registry.Put:
-						evt.Type, evt.Object = proto.EVT_UPDATE, kv
-					case resp.Action == registry.Delete:
-						evt.Type, evt.Object = proto.EVT_DELETE, kv
-					default:
-						return fmt.Errorf("unknown KeyValue %v", kv)
-					}
-					evts[i] = evt
-				}
-				f(evts)
+				f(resp)
 				return nil
 			}))
 
@@ -98,7 +76,7 @@ func (lw *ListWatcher) doWatch(ctx context.Context, f func(evt []KvEvent)) error
 		util.Logger().Errorf(err, "watch prefix %s failed, start rev: %d+1->%d->0", lw.Prefix, rev, lw.Revision())
 
 		lw.setRevision(0)
-		f([]KvEvent{errEvent(lw.Prefix, err)})
+		f(nil)
 	}
 	return err
 }
@@ -106,13 +84,13 @@ func (lw *ListWatcher) doWatch(ctx context.Context, f func(evt []KvEvent)) error
 type Watcher struct {
 	ListOps ListWatchConfig
 	lw      *ListWatcher
-	bus     chan []KvEvent
+	bus     chan *registry.PluginResponse
 	stopCh  chan struct{}
 	stop    bool
 	mux     sync.Mutex
 }
 
-func (w *Watcher) EventBus() <-chan []KvEvent {
+func (w *Watcher) EventBus() <-chan *registry.PluginResponse {
 	return w.bus
 }
 
@@ -133,9 +111,9 @@ func (w *Watcher) process(_ context.Context) {
 	}
 }
 
-func (w *Watcher) sendEvent(evts []KvEvent) {
+func (w *Watcher) sendEvent(resp *registry.PluginResponse) {
 	defer util.RecoverAndReport()
-	w.bus <- evts
+	w.bus <- resp
 }
 
 func (w *Watcher) Stop() {
@@ -150,19 +128,11 @@ func (w *Watcher) Stop() {
 	w.mux.Unlock()
 }
 
-func errEvent(key string, err error) KvEvent {
-	return KvEvent{
-		Type:   proto.EVT_ERROR,
-		Prefix: key,
-		Object: err,
-	}
-}
-
 func newWatcher(lw *ListWatcher, listOps ListWatchConfig) *Watcher {
 	w := &Watcher{
 		ListOps: listOps,
 		lw:      lw,
-		bus:     make(chan []KvEvent, EVENT_BUS_MAX_SIZE),
+		bus:     make(chan *registry.PluginResponse, EVENT_BUS_MAX_SIZE),
 		stopCh:  make(chan struct{}),
 	}
 	util.Go(w.process)
diff --git a/server/core/backend/parser.go b/server/core/backend/parser.go
new file mode 100644
index 0000000..8873988
--- /dev/null
+++ b/server/core/backend/parser.go
@@ -0,0 +1,206 @@
+/*
+ * 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 (
+	"encoding/json"
+	"fmt"
+	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
+	pb "github.com/apache/incubator-servicecomb-service-center/server/core/proto"
+	"strings"
+)
+
+// event
+type KvEventFunc func(evt KvEvent)
+
+// new
+type CreateValueFunc func() interface{}
+
+var (
+	newBytes           CreateValueFunc = func() interface{} { return []byte(nil) }
+	newString          CreateValueFunc = func() interface{} { return "" }
+	newMap             CreateValueFunc = func() interface{} { return make(map[string]string) }
+	newService         CreateValueFunc = func() interface{} { return new(pb.MicroService) }
+	newInstance        CreateValueFunc = func() interface{} { return new(pb.MicroServiceInstance) }
+	newRule            CreateValueFunc = func() interface{} { return new(pb.ServiceRule) }
+	newDependencyRule  CreateValueFunc = func() interface{} { return new(pb.MicroServiceDependency) }
+	newDependencyQueue CreateValueFunc = func() interface{} { return new(pb.ConsumerDependency) }
+)
+
+// parse
+type ParseValueFunc func(src []byte, dist interface{}) error
+
+var (
+	unParse ParseValueFunc = func(src []byte, dist interface{}) error {
+		d := dist.(*interface{})
+		*d = src
+		return nil
+	}
+	textUnmarshal ParseValueFunc = func(src []byte, dist interface{}) error {
+		d := dist.(*interface{})
+		*d = util.BytesToStringWithNoCopy(src)
+		return nil
+	}
+	mapUnmarshal ParseValueFunc = func(src []byte, dist interface{}) error {
+		d := dist.(*interface{})
+		m := (*d).(map[string]string)
+		return json.Unmarshal(src, &m)
+	}
+	jsonUnmarshal ParseValueFunc = func(src []byte, dist interface{}) error {
+		d := dist.(*interface{})
+		return json.Unmarshal(src, *d)
+	}
+)
+
+type Parser struct {
+	c CreateValueFunc
+	p ParseValueFunc
+}
+
+func (p *Parser) Unmarshal(src []byte) (interface{}, error) {
+	v := p.c()
+	err := p.p(src, &v)
+	return v, err
+}
+
+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}
+)
+
+func KvToResponse(kv *KeyValue) (keys []string) {
+	return strings.Split(util.BytesToStringWithNoCopy(kv.Key), "/")
+}
+
+func GetInfoFromSvcKV(kv *KeyValue) (serviceId, domainProject string) {
+	keys := KvToResponse(kv)
+	l := len(keys)
+	if l < 4 {
+		return
+	}
+	serviceId = keys[l-1]
+	domainProject = fmt.Sprintf("%s/%s", keys[l-3], keys[l-2])
+	return
+}
+
+func GetInfoFromInstKV(kv *KeyValue) (serviceId, instanceId, domainProject string) {
+	keys := KvToResponse(kv)
+	l := len(keys)
+	if l < 4 {
+		return
+	}
+	serviceId = keys[l-2]
+	instanceId = keys[l-1]
+	domainProject = fmt.Sprintf("%s/%s", keys[l-4], keys[l-3])
+	return
+}
+
+func GetInfoFromDomainKV(kv *KeyValue) (domain string) {
+	keys := KvToResponse(kv)
+	l := len(keys)
+	if l < 1 {
+		return
+	}
+	domain = keys[l-1]
+	return
+}
+
+func GetInfoFromProjectKV(kv *KeyValue) (domainProject string) {
+	keys := KvToResponse(kv)
+	l := len(keys)
+	if l < 2 {
+		return
+	}
+	domainProject = fmt.Sprintf("%s/%s", keys[l-2], keys[l-1])
+	return
+}
+
+func GetInfoFromRuleKV(kv *KeyValue) (serviceId, ruleId, domainProject string) {
+	keys := KvToResponse(kv)
+	l := len(keys)
+	if l < 4 {
+		return
+	}
+	serviceId = keys[l-2]
+	ruleId = keys[l-1]
+	domainProject = fmt.Sprintf("%s/%s", keys[l-4], keys[l-3])
+	return
+}
+
+func GetInfoFromTagKV(kv *KeyValue) (serviceId, domainProject string) {
+	keys := KvToResponse(kv)
+	l := len(keys)
+	if l < 3 {
+		return
+	}
+	serviceId = keys[l-1]
+	domainProject = fmt.Sprintf("%s/%s", keys[l-3], keys[l-2])
+	return
+}
+
+func GetInfoFromSvcIndexKV(kv *KeyValue) (key *pb.MicroServiceKey) {
+	keys := KvToResponse(kv)
+	l := len(keys)
+	if l < 6 {
+		return
+	}
+	domainProject := fmt.Sprintf("%s/%s", keys[l-6], keys[l-5])
+	return &pb.MicroServiceKey{
+		Tenant:      domainProject,
+		Environment: keys[l-4],
+		AppId:       keys[l-3],
+		ServiceName: keys[l-2],
+		Version:     keys[l-1],
+	}
+}
+
+func GetInfoFromSchemaSummaryKV(kv *KeyValue) (schemaId string) {
+	keys := KvToResponse(kv)
+	l := len(keys)
+	if l < 1 {
+		return
+	}
+
+	return keys[l-1]
+}
+
+func GetInfoFromSchemaKV(kv *KeyValue) (schemaId string) {
+	keys := KvToResponse(kv)
+	l := len(keys)
+	if l < 1 {
+		return
+	}
+
+	return keys[l-1]
+}
+
+func GetInfoFromDependencyQueueKV(kv *KeyValue) (consumerId, domainProject string) {
+	keys := KvToResponse(kv)
+	l := len(keys)
+	if l < 4 {
+		return
+	}
+	consumerId = keys[l-2]
+	domainProject = fmt.Sprintf("%s/%s", keys[l-4], keys[l-3])
+	return
+}
diff --git a/server/core/backend/parser_test.go b/server/core/backend/parser_test.go
new file mode 100644
index 0000000..dc20af9
--- /dev/null
+++ b/server/core/backend/parser_test.go
@@ -0,0 +1,80 @@
+/*
+ * 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/proto"
+	"testing"
+)
+
+func TestParseValueFunc(t *testing.T) {
+	r, err := BytesParser.Unmarshal([]byte("a"))
+	if err != nil {
+		t.Fatalf("BytesParser.Unmarshal failed, %s", err.Error())
+	}
+	if v, ok := r.([]byte); !ok || v[0] != 'a' {
+		t.Fatalf("BytesParser.Unmarshal failed, %s", v)
+	}
+
+	r, err = StringParser.Unmarshal([]byte("abc"))
+	if err != nil {
+		t.Fatalf("StringParser.Unmarshal failed, %s", err.Error())
+	}
+	if v, ok := r.(string); !ok || v != "abc" {
+		t.Fatalf("StringParser.Unmarshal failed, %s", v)
+	}
+
+	r, err = ServiceParser.Unmarshal([]byte(`{"serviceName": "abc"}`))
+	if err != nil {
+		t.Fatalf("ServiceParser.Unmarshal failed, %s", err.Error())
+	}
+	if v, ok := r.(*proto.MicroService); !ok || v.ServiceName != "abc" {
+		t.Fatalf("ServiceParser.Unmarshal failed, %s", v)
+	}
+
+	r, err = InstanceParser.Unmarshal([]byte(`{"hostName": "abc"}`))
+	if err != nil {
+		t.Fatalf("InstanceParser.Unmarshal failed, %s", err.Error())
+	}
+	if v, ok := r.(*proto.MicroServiceInstance); !ok || v.HostName != "abc" {
+		t.Fatalf("InstanceParser.Unmarshal failed, %s", v)
+	}
+
+	r, err = RuleParser.Unmarshal([]byte(`{"ruleId": "abc"}`))
+	if err != nil {
+		t.Fatalf("RuleParser.Unmarshal failed, %s", err.Error())
+	}
+	if v, ok := r.(*proto.ServiceRule); !ok || v.RuleId != "abc" {
+		t.Fatalf("RuleParser.Unmarshal failed, %s", v)
+	}
+
+	r, err = DependencyRuleParser.Unmarshal([]byte(`{"Dependency":[{"tenant":"opsadm/southchina","appId":"csezhq","serviceName":"zhqClient","version":"1.0.0"}]}`))
+	if err != nil {
+		t.Fatalf("DependencyRuleParser.Unmarshal failed, %s", err.Error())
+	}
+	if v, ok := r.(*proto.MicroServiceDependency); !ok || v.Dependency[0].ServiceName != "zhqClient" {
+		t.Fatalf("DependencyRuleParser.Unmarshal failed, %s", v)
+	}
+
+	r, err = MapParser.Unmarshal([]byte(`{"a": "abc"}`))
+	if err != nil {
+		t.Fatalf("MapParser.Unmarshal failed, %s", err.Error())
+	}
+	if v, ok := r.(map[string]string); !ok || v["a"] != "abc" {
+		t.Fatalf("MapParser.Unmarshal failed, %s", v)
+	}
+}
diff --git a/server/core/backend/store.go b/server/core/backend/store.go
index 7a09469..608b2d5 100644
--- a/server/core/backend/store.go
+++ b/server/core/backend/store.go
@@ -32,35 +32,43 @@ func init() {
 }
 
 type KvStore struct {
-	indexers    map[StoreType]*Indexer
+	indexers    map[StoreType]Indexer
 	taskService *async.TaskService
 	lock        sync.RWMutex
 	ready       chan struct{}
 	goroutine   *util.GoRoutine
 	isClose     bool
+	rev         int64
 }
 
 func (s *KvStore) Initialize() {
-	s.indexers = make(map[StoreType]*Indexer)
+	s.indexers = make(map[StoreType]Indexer)
 	s.taskService = async.NewTaskService()
 	s.ready = make(chan struct{})
 	s.goroutine = util.NewGo(context.Background())
-
-	for i := StoreType(0); i != typeEnd; i++ {
-		s.newIndexBuilder(i, NullCacher)
+	for t := StoreType(0); t != typeEnd; t++ {
+		s.setupIndexer(t, NewBaseIndexer(s.injectConfig(t)))
 	}
-	for i := StoreType(0); i != typeEnd; i++ {
-		s.indexers[i].Run()
+}
+
+func (s *KvStore) OnCacheEvent(evt KvEvent) {
+	if s.rev < evt.Revision {
+		s.rev = evt.Revision
 	}
 }
 
-func (s *KvStore) dispatchEvent(t StoreType, evt KvEvent) {
-	s.indexers[t].OnCacheEvent(evt)
-	EventProxy(t).OnEvent(evt)
+func (s *KvStore) injectConfig(t StoreType) *Config {
+	return TypeConfig[t].AppendEventFunc(s.OnCacheEvent).
+		AppendEventFunc(EventProxies[t].OnEvent)
 }
 
-func (s *KvStore) newIndexBuilder(t StoreType, cacher Cacher) {
-	s.indexers[t] = NewCacheIndexer(TypeRoots[t], cacher)
+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) Run() {
@@ -68,25 +76,6 @@ func (s *KvStore) Run() {
 	s.taskService.Run()
 }
 
-func (s *KvStore) getKvCacherCfgOptions(t StoreType) (opts []ConfigOption) {
-	switch t {
-	case INSTANCE:
-		opts = append(opts, WithDeferHandler(s.SelfPreservationHandler()))
-	}
-	sz := TypeInitSize[t]
-	if sz > 0 {
-		opts = append(opts, WithInitSize(sz))
-	}
-	opts = append(opts,
-		WithPrefix(TypeRoots[t]),
-		WithEventFunc(func(evt KvEvent) { s.dispatchEvent(t, evt) }))
-	return
-}
-
-func (s *KvStore) SelfPreservationHandler() DeferHandler {
-	return &InstanceEventDeferHandler{Percent: DEFAULT_SELF_PRESERVATION_PERCENT}
-}
-
 func (s *KvStore) store(ctx context.Context) {
 	defer s.wait(ctx)
 
@@ -96,17 +85,7 @@ func (s *KvStore) store(ctx context.Context) {
 	}
 
 	for t := StoreType(0); t != typeEnd; t++ {
-		switch t {
-		case SCHEMA:
-			util.Logger().Infof("service center will not cache '%s'", SCHEMA)
-			continue
-		default:
-			s.indexers[t].Stop() // release the exist indexer
-			s.newIndexBuilder(t, NewKvCacher(t.String(), s.getKvCacherCfgOptions(t)...))
-		}
-	}
-	for t := StoreType(0); t != typeEnd; t++ {
-		s.indexers[t].Run()
+		s.setupIndexer(t, NewCacheIndexer(t.String(), TypeConfig[t]))
 	}
 }
 
@@ -151,63 +130,63 @@ func (s *KvStore) Ready() <-chan struct{} {
 	return s.ready
 }
 
-func (s *KvStore) Service() *Indexer {
+func (s *KvStore) Service() Indexer {
 	return s.indexers[SERVICE]
 }
 
-func (s *KvStore) SchemaSummary() *Indexer {
+func (s *KvStore) SchemaSummary() Indexer {
 	return s.indexers[SCHEMA_SUMMARY]
 }
 
-func (s *KvStore) Instance() *Indexer {
+func (s *KvStore) Instance() Indexer {
 	return s.indexers[INSTANCE]
 }
 
-func (s *KvStore) Lease() *Indexer {
+func (s *KvStore) Lease() Indexer {
 	return s.indexers[LEASE]
 }
 
-func (s *KvStore) ServiceIndex() *Indexer {
+func (s *KvStore) ServiceIndex() Indexer {
 	return s.indexers[SERVICE_INDEX]
 }
 
-func (s *KvStore) ServiceAlias() *Indexer {
+func (s *KvStore) ServiceAlias() Indexer {
 	return s.indexers[SERVICE_ALIAS]
 }
 
-func (s *KvStore) ServiceTag() *Indexer {
+func (s *KvStore) ServiceTag() Indexer {
 	return s.indexers[SERVICE_TAG]
 }
 
-func (s *KvStore) Rule() *Indexer {
+func (s *KvStore) Rule() Indexer {
 	return s.indexers[RULE]
 }
 
-func (s *KvStore) RuleIndex() *Indexer {
+func (s *KvStore) RuleIndex() Indexer {
 	return s.indexers[RULE_INDEX]
 }
 
-func (s *KvStore) Schema() *Indexer {
+func (s *KvStore) Schema() Indexer {
 	return s.indexers[SCHEMA]
 }
 
-func (s *KvStore) Dependency() *Indexer {
+func (s *KvStore) Dependency() Indexer {
 	return s.indexers[DEPENDENCY]
 }
 
-func (s *KvStore) DependencyRule() *Indexer {
+func (s *KvStore) DependencyRule() Indexer {
 	return s.indexers[DEPENDENCY_RULE]
 }
 
-func (s *KvStore) DependencyQueue() *Indexer {
+func (s *KvStore) DependencyQueue() Indexer {
 	return s.indexers[DEPENDENCY_QUEUE]
 }
 
-func (s *KvStore) Domain() *Indexer {
+func (s *KvStore) Domain() Indexer {
 	return s.indexers[DOMAIN]
 }
 
-func (s *KvStore) Project() *Indexer {
+func (s *KvStore) Project() Indexer {
 	return s.indexers[PROJECT]
 }
 
@@ -234,7 +213,7 @@ func (s *KvStore) KeepAlive(ctx context.Context, opts ...registry.PluginOpOption
 	return pt.TTL, pt.Err()
 }
 
-func (s *KvStore) Entity(id StoreType) *Indexer {
+func (s *KvStore) Entity(id StoreType) Indexer {
 	return s.indexers[id]
 }
 
@@ -243,14 +222,9 @@ func (s *KvStore) Install(e Entity) (id StoreType, err error) {
 		return
 	}
 
-	util.Logger().Infof("install new store entity %d:%s->%s", id, e.Name(), e.Prefix())
+	util.Logger().Infof("install new store entity %d:%s->%s", id, e.Name(), e.Config().Prefix)
 
-	var cacher Cacher = NullCacher
-	if core.ServerInfo.Config.EnableCache {
-		cacher = NewKvCacher(id.String(), s.getKvCacherCfgOptions(id)...)
-	}
-	s.newIndexBuilder(id, cacher)
-	s.indexers[id].Run()
+	s.setupIndexer(id, NewCacheIndexer(id.String(), e.Config()))
 	return
 }
 
@@ -266,11 +240,6 @@ func Store() *KvStore {
 	return store
 }
 
-func Revision() (rev int64) {
-	for _, i := range Store().indexers {
-		if rev < i.Cache().Version() {
-			rev = i.Cache().Version()
-		}
-	}
-	return
+func Revision() int64 {
+	return store.rev
 }
diff --git a/server/core/key_generator.go b/server/core/key_generator.go
index 634ad68..884e001 100644
--- a/server/core/key_generator.go
+++ b/server/core/key_generator.go
@@ -22,6 +22,7 @@ import (
 )
 
 const (
+	SPLIT                       = "/"
 	REGISTRY_ROOT_KEY           = "cse-sr"
 	REGISTRY_SYS_KEY            = "sys"
 	REGISTRY_SERVICE_KEY        = "ms"
@@ -44,10 +45,7 @@ const (
 )
 
 func GetRootKey() string {
-	return util.StringJoin([]string{
-		"",
-		REGISTRY_ROOT_KEY,
-	}, "/")
+	return SPLIT + REGISTRY_ROOT_KEY
 }
 
 func GetServiceRootKey(domainProject string) string {
@@ -56,7 +54,7 @@ func GetServiceRootKey(domainProject string) string {
 		REGISTRY_SERVICE_KEY,
 		REGISTRY_FILE,
 		domainProject,
-	}, "/")
+	}, SPLIT)
 }
 
 func GetServiceIndexRootKey(domainProject string) string {
@@ -65,7 +63,7 @@ func GetServiceIndexRootKey(domainProject string) string {
 		REGISTRY_SERVICE_KEY,
 		REGISTRY_INDEX,
 		domainProject,
-	}, "/")
+	}, SPLIT)
 }
 
 func GetServiceAliasRootKey(domainProject string) string {
@@ -74,7 +72,7 @@ func GetServiceAliasRootKey(domainProject string) string {
 		REGISTRY_SERVICE_KEY,
 		REGISTRY_ALIAS_KEY,
 		domainProject,
-	}, "/")
+	}, SPLIT)
 }
 
 func GetServiceAppKey(domainProject, env, appId string) string {
@@ -82,7 +80,7 @@ func GetServiceAppKey(domainProject, env, appId string) string {
 		GetServiceIndexRootKey(domainProject),
 		env,
 		appId,
-	}, "/")
+	}, SPLIT)
 }
 
 func GetServiceRuleRootKey(domainProject string) string {
@@ -91,7 +89,7 @@ func GetServiceRuleRootKey(domainProject string) string {
 		REGISTRY_SERVICE_KEY,
 		REGISTRY_RULE_KEY,
 		domainProject,
-	}, "/")
+	}, SPLIT)
 }
 
 func GetServiceRuleIndexRootKey(domainProject string) string {
@@ -100,7 +98,7 @@ func GetServiceRuleIndexRootKey(domainProject string) string {
 		REGISTRY_SERVICE_KEY,
 		REGISTRY_RULE_INDEX_KEY,
 		domainProject,
-	}, "/")
+	}, SPLIT)
 }
 
 func GetServiceTagRootKey(domainProject string) string {
@@ -109,7 +107,7 @@ func GetServiceTagRootKey(domainProject string) string {
 		REGISTRY_SERVICE_KEY,
 		REGISTRY_TAG_KEY,
 		domainProject,
-	}, "/")
+	}, SPLIT)
 }
 
 func GetServiceSchemaRootKey(domainProject string) string {
@@ -118,7 +116,7 @@ func GetServiceSchemaRootKey(domainProject string) string {
 		REGISTRY_SERVICE_KEY,
 		REGISTRY_SCHEMA_KEY,
 		domainProject,
-	}, "/")
+	}, SPLIT)
 }
 
 func GetInstanceRootKey(domainProject string) string {
@@ -127,7 +125,7 @@ func GetInstanceRootKey(domainProject string) string {
 		REGISTRY_INSTANCE_KEY,
 		REGISTRY_FILE,
 		domainProject,
-	}, "/")
+	}, SPLIT)
 }
 
 func GetInstanceLeaseRootKey(domainProject string) string {
@@ -136,14 +134,14 @@ func GetInstanceLeaseRootKey(domainProject string) string {
 		REGISTRY_INSTANCE_KEY,
 		REGISTRY_LEASE_KEY,
 		domainProject,
-	}, "/")
+	}, SPLIT)
 }
 
 func GenerateServiceKey(domainProject string, serviceId string) string {
 	return util.StringJoin([]string{
 		GetServiceRootKey(domainProject),
 		serviceId,
-	}, "/")
+	}, SPLIT)
 }
 
 func GenerateRuleIndexKey(domainProject string, serviceId string, attr string, pattern string) string {
@@ -152,7 +150,7 @@ func GenerateRuleIndexKey(domainProject string, serviceId string, attr string, p
 		serviceId,
 		attr,
 		pattern,
-	}, "/")
+	}, SPLIT)
 }
 
 func GenerateServiceIndexKey(key *pb.MicroServiceKey) string {
@@ -162,7 +160,7 @@ func GenerateServiceIndexKey(key *pb.MicroServiceKey) string {
 		key.AppId,
 		key.ServiceName,
 		key.Version,
-	}, "/")
+	}, SPLIT)
 }
 
 func GenerateServiceAliasKey(key *pb.MicroServiceKey) string {
@@ -172,7 +170,7 @@ func GenerateServiceAliasKey(key *pb.MicroServiceKey) string {
 		key.AppId,
 		key.Alias,
 		key.Version,
-	}, "/")
+	}, SPLIT)
 }
 
 func GenerateServiceRuleKey(domainProject string, serviceId string, ruleId string) string {
@@ -180,14 +178,14 @@ func GenerateServiceRuleKey(domainProject string, serviceId string, ruleId strin
 		GetServiceRuleRootKey(domainProject),
 		serviceId,
 		ruleId,
-	}, "/")
+	}, SPLIT)
 }
 
 func GenerateServiceTagKey(domainProject string, serviceId string) string {
 	return util.StringJoin([]string{
 		GetServiceTagRootKey(domainProject),
 		serviceId,
-	}, "/")
+	}, SPLIT)
 }
 
 func GenerateServiceSchemaKey(domainProject string, serviceId string, schemaId string) string {
@@ -195,7 +193,7 @@ func GenerateServiceSchemaKey(domainProject string, serviceId string, schemaId s
 		GetServiceSchemaRootKey(domainProject),
 		serviceId,
 		schemaId,
-	}, "/")
+	}, SPLIT)
 }
 
 func GenerateServiceSchemaSummaryKey(domainProject string, serviceId string, schemaId string) string {
@@ -203,7 +201,7 @@ func GenerateServiceSchemaSummaryKey(domainProject string, serviceId string, sch
 		GetServiceSchemaSummaryRootKey(domainProject),
 		serviceId,
 		schemaId,
-	}, "/")
+	}, SPLIT)
 }
 
 func GetServiceSchemaSummaryRootKey(domainProject string) string {
@@ -212,7 +210,7 @@ func GetServiceSchemaSummaryRootKey(domainProject string) string {
 		REGISTRY_SERVICE_KEY,
 		REGISTRY_SCHEMA_SUMMARY_KEY,
 		domainProject,
-	}, "/")
+	}, SPLIT)
 }
 
 func GenerateInstanceKey(domainProject string, serviceId string, instanceId string) string {
@@ -220,7 +218,7 @@ func GenerateInstanceKey(domainProject string, serviceId string, instanceId stri
 		GetInstanceRootKey(domainProject),
 		serviceId,
 		instanceId,
-	}, "/")
+	}, SPLIT)
 }
 
 func GenerateInstanceLeaseKey(domainProject string, serviceId string, instanceId string) string {
@@ -228,7 +226,7 @@ func GenerateInstanceLeaseKey(domainProject string, serviceId string, instanceId
 		GetInstanceLeaseRootKey(domainProject),
 		serviceId,
 		instanceId,
-	}, "/")
+	}, SPLIT)
 }
 
 func GenerateServiceDependencyRuleKey(serviceType string, domainProject string, in *pb.MicroServiceKey) string {
@@ -238,7 +236,7 @@ func GenerateServiceDependencyRuleKey(serviceType string, domainProject string,
 			serviceType,
 			in.Environment,
 			in.ServiceName,
-		}, "/")
+		}, SPLIT)
 	}
 	return util.StringJoin([]string{
 		GetServiceDependencyRuleRootKey(domainProject),
@@ -247,7 +245,7 @@ func GenerateServiceDependencyRuleKey(serviceType string, domainProject string,
 		in.AppId,
 		in.ServiceName,
 		in.Version,
-	}, "/")
+	}, SPLIT)
 }
 
 func GenerateConsumerDependencyRuleKey(domainProject string, in *pb.MicroServiceKey) string {
@@ -264,7 +262,7 @@ func GetServiceDependencyRuleRootKey(domainProject string) string {
 		REGISTRY_SERVICE_KEY,
 		REGISTRY_DEPS_RULE_KEY,
 		domainProject,
-	}, "/")
+	}, SPLIT)
 }
 
 func GetServiceDependencyQueueRootKey(domainProject string) string {
@@ -273,7 +271,7 @@ func GetServiceDependencyQueueRootKey(domainProject string) string {
 		REGISTRY_SERVICE_KEY,
 		REGISTRY_DEPS_QUEUE_KEY,
 		domainProject,
-	}, "/")
+	}, SPLIT)
 }
 
 func GenerateConsumerDependencyKey(domainProject string, consumerId string, providerId string) string {
@@ -286,7 +284,7 @@ func GenerateServiceDependencyKey(serviceType string, domainProject string, serv
 		serviceType,
 		serviceId1,
 		serviceId2,
-	}, "/")
+	}, SPLIT)
 }
 
 func GenerateProviderDependencyKey(domainProject string, providerId string, consumerId string) string {
@@ -298,7 +296,7 @@ func GenerateConsumerDependencyQueueKey(domainProject, consumerId, uuid string)
 		GetServiceDependencyQueueRootKey(domainProject),
 		consumerId,
 		uuid,
-	}, "/")
+	}, SPLIT)
 }
 
 func GetServiceDependencyRootKey(domainProject string) string {
@@ -307,35 +305,35 @@ func GetServiceDependencyRootKey(domainProject string) string {
 		REGISTRY_SERVICE_KEY,
 		REGISTRY_DEPENDENCY_KEY,
 		domainProject,
-	}, "/")
+	}, SPLIT)
 }
 
 func GetDomainRootKey() string {
 	return util.StringJoin([]string{
 		GetRootKey(),
 		REGISTRY_DOMAIN_KEY,
-	}, "/")
+	}, SPLIT)
 }
 
 func GenerateDomainKey(domain string) string {
 	return util.StringJoin([]string{
 		GetDomainRootKey(),
 		domain,
-	}, "/")
+	}, SPLIT)
 }
 
 func GetServerInfoKey() string {
 	return util.StringJoin([]string{
 		GetRootKey(),
 		REGISTRY_SYS_KEY,
-	}, "/")
+	}, SPLIT)
 }
 
 func GetMetricsRootKey() string {
 	return util.StringJoin([]string{
 		GetRootKey(),
 		REGISTRY_METRICS_KEY,
-	}, "/")
+	}, SPLIT)
 }
 
 func GenerateMetricsKey(name, utc, domain string) string {
@@ -344,7 +342,7 @@ func GenerateMetricsKey(name, utc, domain string) string {
 		name,
 		utc,
 		domain,
-	}, "/")
+	}, SPLIT)
 }
 
 func GetProjectRootKey(domain string) string {
@@ -352,12 +350,12 @@ func GetProjectRootKey(domain string) string {
 		GetRootKey(),
 		REGISTRY_PROJECT_KEY,
 		domain,
-	}, "/")
+	}, SPLIT)
 }
 
 func GenerateProjectKey(domain, project string) string {
 	return util.StringJoin([]string{
 		GetProjectRootKey(domain),
 		project,
-	}, "/")
+	}, SPLIT)
 }
diff --git a/server/core/proto/services.go b/server/core/proto/services.go
index 133eb09..50f61e3 100644
--- a/server/core/proto/services.go
+++ b/server/core/proto/services.go
@@ -17,13 +17,9 @@
 package proto
 
 import (
-	"fmt"
-	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
 	scerr "github.com/apache/incubator-servicecomb-service-center/server/error"
-	"github.com/coreos/etcd/mvcc/mvccpb"
 	"github.com/gorilla/websocket"
 	"golang.org/x/net/context"
-	"strings"
 )
 
 type EventType string
@@ -58,8 +54,8 @@ const (
 	ENV_ACCEPT string = "acceptance"
 	ENV_PROD   string = "production"
 
-	REGISTERBY_SDK     string = "SDK"
-	REGISTERBY_SIDECAR string = "SIDECAR"
+	REGISTERBY_SDK      string = "SDK"
+	REGISTERBY_SIDECAR  string = "SIDECAR"
 	REGISTERBY_PLATFORM string = "PLATFORM"
 
 	APP_ID  = "default"
@@ -142,125 +138,6 @@ func CreateResponseWithSCErr(err *scerr.Error) *Response {
 	}
 }
 
-func KvToResponse(kv *mvccpb.KeyValue) (keys []string, data []byte) {
-	keys = strings.Split(util.BytesToStringWithNoCopy(kv.Key), "/")
-	data = kv.Value
-	return
-}
-
-func GetInfoFromSvcKV(kv *mvccpb.KeyValue) (serviceId, domainProject string, data []byte) {
-	keys, data := KvToResponse(kv)
-	l := len(keys)
-	if l < 4 {
-		return
-	}
-	serviceId = keys[l-1]
-	domainProject = fmt.Sprintf("%s/%s", keys[l-3], keys[l-2])
-	return
-}
-
-func GetInfoFromInstKV(kv *mvccpb.KeyValue) (serviceId, instanceId, domainProject string, data []byte) {
-	keys, data := KvToResponse(kv)
-	l := len(keys)
-	if l < 4 {
-		return
-	}
-	serviceId = keys[l-2]
-	instanceId = keys[l-1]
-	domainProject = fmt.Sprintf("%s/%s", keys[l-4], keys[l-3])
-	return
-}
-
-func GetInfoFromDomainKV(kv *mvccpb.KeyValue) (domain string, data []byte) {
-	keys, data := KvToResponse(kv)
-	l := len(keys)
-	if l < 1 {
-		return
-	}
-	domain = keys[l-1]
-	return
-}
-
-func GetInfoFromProjectKV(kv *mvccpb.KeyValue) (domainProject string, data []byte) {
-	keys, data := KvToResponse(kv)
-	l := len(keys)
-	if l < 2 {
-		return
-	}
-	domainProject = fmt.Sprintf("%s/%s", keys[l-2], keys[l-1])
-	return
-}
-
-func GetInfoFromRuleKV(kv *mvccpb.KeyValue) (serviceId, ruleId, domainProject string, data []byte) {
-	keys, data := KvToResponse(kv)
-	l := len(keys)
-	if l < 4 {
-		return
-	}
-	serviceId = keys[l-2]
-	ruleId = keys[l-1]
-	domainProject = fmt.Sprintf("%s/%s", keys[l-4], keys[l-3])
-	return
-}
-
-func GetInfoFromTagKV(kv *mvccpb.KeyValue) (serviceId, domainProject string, data []byte) {
-	keys, data := KvToResponse(kv)
-	l := len(keys)
-	if l < 3 {
-		return
-	}
-	serviceId = keys[l-1]
-	domainProject = fmt.Sprintf("%s/%s", keys[l-3], keys[l-2])
-	return
-}
-
-func GetInfoFromSvcIndexKV(kv *mvccpb.KeyValue) (key *MicroServiceKey, data []byte) {
-	keys, data := KvToResponse(kv)
-	l := len(keys)
-	if l < 6 {
-		return
-	}
-	domainProject := fmt.Sprintf("%s/%s", keys[l-6], keys[l-5])
-	return &MicroServiceKey{
-		Tenant:      domainProject,
-		Environment: keys[l-4],
-		AppId:       keys[l-3],
-		ServiceName: keys[l-2],
-		Version:     keys[l-1],
-	}, data
-}
-
-func GetInfoFromSchemaSummaryKV(kv *mvccpb.KeyValue) (schemaId string, data []byte) {
-	keys, data := KvToResponse(kv)
-	l := len(keys)
-	if l < 1 {
-		return
-	}
-
-	return keys[l-1], data
-}
-
-func GetInfoFromSchemaKV(kv *mvccpb.KeyValue) (schemaId string, data []byte) {
-	keys, data := KvToResponse(kv)
-	l := len(keys)
-	if l < 1 {
-		return
-	}
-
-	return keys[l-1], data
-}
-
-func GetInfoFromDependencyQueueKV(kv *mvccpb.KeyValue) (consumerId, domainProject string, data []byte) {
-	keys, data := KvToResponse(kv)
-	l := len(keys)
-	if l < 4 {
-		return
-	}
-	consumerId = keys[l-2]
-	domainProject = fmt.Sprintf("%s/%s", keys[l-4], keys[l-3])
-	return
-}
-
 func DependenciesToKeys(in []*MicroServiceKey, domainProject string) []*MicroServiceKey {
 	for _, value := range in {
 		if len(value.Tenant) == 0 {
diff --git a/server/govern/service.go b/server/govern/service.go
index e0c695f..139fe7b 100644
--- a/server/govern/service.go
+++ b/server/govern/service.go
@@ -206,7 +206,7 @@ func (governService *GovernService) GetApplications(ctx context.Context, in *pb.
 	apps := make([]string, 0, l)
 	appMap := make(map[string]struct{}, l)
 	for _, kv := range resp.Kvs {
-		key, _ := pb.GetInfoFromSvcIndexKV(kv)
+		key := backend.GetInfoFromSvcIndexKV(kv)
 		if _, ok := appMap[key.AppId]; ok {
 			continue
 		}
@@ -236,7 +236,7 @@ func getServiceAllVersions(ctx context.Context, serviceKey *pb.MicroServiceKey)
 		return versions, nil
 	}
 	for _, kv := range resp.Kvs {
-		key, _ := pb.GetInfoFromSvcIndexKV(kv)
+		key := backend.GetInfoFromSvcIndexKV(kv)
 		versions = append(versions, key.Version)
 	}
 	return versions, nil
@@ -255,7 +255,7 @@ func getSchemaInfoUtil(ctx context.Context, domainProject string, serviceId stri
 	schemas := make([]*pb.Schema, 0, len(resp.Kvs))
 	for _, kv := range resp.Kvs {
 		schemaInfo := &pb.Schema{}
-		schemaInfo.Schema = util.BytesToStringWithNoCopy(kv.Value)
+		schemaInfo.Schema = util.BytesToStringWithNoCopy(kv.Value.([]byte))
 		schemaInfo.SchemaId = util.BytesToStringWithNoCopy(kv.Key[len(key):])
 		schemas = append(schemas, schemaInfo)
 	}
@@ -367,7 +367,7 @@ func statistics(ctx context.Context) (*pb.Statistics, error) {
 	svcWithNonVersion := make(map[string]struct{}, respSvc.Count)
 	svcIdToNonVerKey := make(map[string]string, respSvc.Count)
 	for _, kv := range respSvc.Kvs {
-		key, val := pb.GetInfoFromSvcIndexKV(kv)
+		key := backend.GetInfoFromSvcIndexKV(kv)
 		if _, ok := app[key.AppId]; !ok {
 			app[key.AppId] = struct{}{}
 		}
@@ -377,7 +377,7 @@ func statistics(ctx context.Context) (*pb.Statistics, error) {
 		if _, ok := svcWithNonVersion[svcWithNonVersionKey]; !ok {
 			svcWithNonVersion[svcWithNonVersionKey] = struct{}{}
 		}
-		svcIdToNonVerKey[util.BytesToStringWithNoCopy(val)] = svcWithNonVersionKey
+		svcIdToNonVerKey[kv.Value.(string)] = svcWithNonVersionKey
 	}
 
 	result.Services.Count = int64(len(svcWithNonVersion))
@@ -401,7 +401,7 @@ func statistics(ctx context.Context) (*pb.Statistics, error) {
 
 	onlineServices := make(map[string]struct{}, respSvc.Count)
 	for _, kv := range respIns.Kvs {
-		serviceId, _, _, _ := pb.GetInfoFromInstKV(kv)
+		serviceId, _, _ := backend.GetInfoFromInstKV(kv)
 		key, ok := svcIdToNonVerKey[serviceId]
 		if !ok {
 			continue
diff --git a/server/plugin/infra/quota/buildin/buildin.go b/server/plugin/infra/quota/buildin/buildin.go
index 6dffeac..6c90e75 100644
--- a/server/plugin/infra/quota/buildin/buildin.go
+++ b/server/plugin/infra/quota/buildin/buildin.go
@@ -31,7 +31,7 @@ import (
 )
 
 const (
-	SERVICE_NUM_MAX_LIMIT            = 12000
+	SERVICE_NUM_MAX_LIMIT            = 50000
 	INSTANCE_NUM_MAX_LIMIT           = 150000
 	RULE_NUM_MAX_LIMIT_PER_SERVICE   = 100
 	SCHEMA_NUM_MAX_LIMIT_PER_SERVICE = 100
@@ -86,7 +86,7 @@ func (q *BuildInQuota) RemandQuotas(ctx context.Context, quotaType quota.Resourc
 func ResourceLimitHandler(ctx context.Context, res *quota.ApplyQuotaResource) *quota.ApplyQuotaResult {
 	var key string
 	var max int64 = 0
-	var indexer *backend.Indexer
+	var indexer backend.Indexer
 
 	domainProject := res.DomainProject
 	serviceId := res.ServiceId
diff --git a/server/plugin/infra/registry/etcd/etcd.go b/server/plugin/infra/registry/etcd/etcd.go
index 6a4d5db..db9bc89 100644
--- a/server/plugin/infra/registry/etcd/etcd.go
+++ b/server/plugin/infra/registry/etcd/etcd.go
@@ -462,7 +462,8 @@ func (c *EtcdClient) TxnWithCmp(ctx context.Context, success []registry.PluginOp
 	if err != nil {
 		return nil, err
 	}
-	util.LogNilOrWarnf(start, "registry client txn {if: %s, then: %d, else: %d}", cmps, len(success), len(fail))
+	util.LogNilOrWarnf(start, "registry client txn {if(%v): %s, then: %d, else: %d}, rev: %d",
+		resp.Succeeded, cmps, len(success), len(fail), resp.Header.Revision)
 	return &registry.PluginResponse{
 		Succeeded: resp.Succeeded,
 		Revision:  resp.Header.Revision,
diff --git a/server/server.go b/server/server.go
index 7e98b6a..66de109 100644
--- a/server/server.go
+++ b/server/server.go
@@ -180,11 +180,6 @@ func (s *ServiceCenterServer) autoCompactBackend() {
 }
 
 func (s *ServiceCenterServer) startNotifyService() {
-	s.notifyService.Config = nf.NotifyServiceConfig{
-		AddTimeout:    30 * time.Second,
-		NotifyTimeout: 30 * time.Second,
-		MaxQueue:      100,
-	}
 	s.notifyService.Start()
 }
 
diff --git a/server/service/util/common.go b/server/service/cache/common.go
similarity index 76%
copy from server/service/util/common.go
copy to server/service/cache/common.go
index 9e31b3f..b17da58 100644
--- a/server/service/util/common.go
+++ b/server/service/cache/common.go
@@ -14,16 +14,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package util
-
-import "time"
+package cache
 
 const (
-	HEADER_REV            = "X-Resource-Revision"
-	CTX_NOCACHE           = "noCache"
-	CTX_CACHEONLY         = "cacheOnly"
-	CTX_REQUEST_REVISION  = "requestRev"
-	CTX_RESPONSE_REVISION = "responseRev"
+	CTX_FIND_CONSUMER = "consumer"
+	CTX_FIND_PROVIDER = "provider"
+	CTX_FIND_TAGS     = "tags"
 
-	cacheTTL = 5 * time.Minute
+	CACHE_FIND = "find"
+	CACHE_DEP  = "dep"
 )
diff --git a/server/service/cache/dependency.go b/server/service/cache/dependency.go
new file mode 100644
index 0000000..23dd262
--- /dev/null
+++ b/server/service/cache/dependency.go
@@ -0,0 +1,64 @@
+/*
+ * 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 cache
+
+import (
+	"github.com/apache/incubator-servicecomb-service-center/pkg/cache"
+	pb "github.com/apache/incubator-servicecomb-service-center/server/core/proto"
+	serviceUtil "github.com/apache/incubator-servicecomb-service-center/server/service/util"
+	"golang.org/x/net/context"
+	"math"
+)
+
+var DependencyRule = &DependencyRuleCache{
+	Tree: cache.NewTree(cache.Configure().
+		WithMaxSize(math.MaxInt64))}
+
+func init() {
+	DependencyRule.AddFilter(
+		&ServiceFilter{},
+		&ConsumerFilter{})
+}
+
+type DependencyRuleItem struct {
+	VersionRule string
+}
+
+type DependencyRuleCache struct {
+	*cache.Tree
+}
+
+func (f *DependencyRuleCache) ExistVersionRule(ctx context.Context, consumerId string, provider *pb.MicroServiceKey) bool {
+	cloneCtx := context.WithValue(context.WithValue(ctx,
+		CTX_FIND_CONSUMER, consumerId),
+		CTX_FIND_PROVIDER, provider)
+
+	node, _ := f.Tree.Get(cloneCtx, cache.Options().Temporary(ctx.Value(serviceUtil.CTX_NOCACHE) == "1"))
+	if node == nil {
+		return false
+	}
+	v := node.Cache.Get(CACHE_DEP).(*DependencyRuleItem)
+	if v.VersionRule != provider.Version {
+		v.VersionRule = provider.Version
+		return false
+	}
+	return true
+}
+
+func (f *DependencyRuleCache) Remove(provider *pb.MicroServiceKey) {
+	f.Tree.Remove(context.WithValue(context.Background(), CTX_FIND_PROVIDER, provider))
+}
diff --git a/server/service/util/schema_util_test.go b/server/service/cache/filter_consumer.go
similarity index 67%
copy from server/service/util/schema_util_test.go
copy to server/service/cache/filter_consumer.go
index 7ba7146..25f29ce 100644
--- a/server/service/util/schema_util_test.go
+++ b/server/service/cache/filter_consumer.go
@@ -14,22 +14,22 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package util
+package cache
 
 import (
-	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
+	"github.com/apache/incubator-servicecomb-service-center/pkg/cache"
 	"golang.org/x/net/context"
-	"testing"
 )
 
-func TestCheckSchemaInfoExist(t *testing.T) {
-	_, err := CheckSchemaInfoExist(util.SetContext(context.Background(), CTX_CACHEONLY, "1"), "")
-	if err != nil {
-		t.FailNow()
-	}
+type ConsumerFilter struct {
+}
+
+func (f *ConsumerFilter) Name(ctx context.Context) string {
+	return ctx.Value(CTX_FIND_CONSUMER).(string)
+}
 
-	_, err = CheckSchemaInfoExist(context.Background(), "")
-	if err == nil {
-		t.FailNow()
-	}
+func (f *ConsumerFilter) Init(ctx context.Context, parent *cache.Node) (node *cache.Node, err error) {
+	node = cache.NewNode()
+	node.Cache.Set(CACHE_DEP, &DependencyRuleItem{})
+	return
 }
diff --git a/server/service/cache/filter_instances.go b/server/service/cache/filter_instances.go
new file mode 100644
index 0000000..1c38676
--- /dev/null
+++ b/server/service/cache/filter_instances.go
@@ -0,0 +1,72 @@
+/*
+ * 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 cache
+
+import (
+	"fmt"
+	"github.com/apache/incubator-servicecomb-service-center/pkg/cache"
+	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
+	apt "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"
+	"github.com/apache/incubator-servicecomb-service-center/server/infra/registry"
+	serviceUtil "github.com/apache/incubator-servicecomb-service-center/server/service/util"
+	"golang.org/x/net/context"
+)
+
+type InstancesFilter struct {
+}
+
+func (f *InstancesFilter) Name(ctx context.Context) string {
+	return ""
+}
+
+func (f *InstancesFilter) Init(ctx context.Context, parent *cache.Node) (node *cache.Node, err error) {
+	provider := ctx.Value(CTX_FIND_PROVIDER).(*pb.MicroServiceKey)
+	pCopy := *parent.Cache.Get(CACHE_FIND).(*VersionRuleCacheItem)
+
+	var (
+		instances []*pb.MicroServiceInstance
+		rev       int64
+	)
+	for _, providerServiceId := range pCopy.ServiceIds {
+		key := apt.GenerateInstanceKey(provider.Tenant, providerServiceId, "")
+		opts := append(serviceUtil.FromContext(ctx), registry.WithStrKey(key), registry.WithPrefix())
+		resp, err := backend.Store().Instance().Search(ctx, opts...)
+		if err != nil {
+			consumer := ctx.Value(CTX_FIND_CONSUMER).(*pb.MicroService)
+			findFlag := fmt.Sprintf("consumer %s find provider %s/%s/%s", consumer.ServiceId,
+				provider.AppId, provider.ServiceName, provider.Version)
+			util.Logger().Errorf(err, "Instance().Search failed, %s", findFlag)
+			return nil, err
+		}
+
+		for _, kv := range resp.Kvs {
+			if kv.ModRevision > rev {
+				rev = kv.ModRevision
+			}
+			instances = append(instances, kv.Value.(*pb.MicroServiceInstance))
+		}
+	}
+
+	pCopy.Instances = instances
+	pCopy.Rev = serviceUtil.FormatRevision(rev, int64(len(instances)))
+
+	node = cache.NewNode()
+	node.Cache.Set(CACHE_FIND, &pCopy)
+	return
+}
diff --git a/server/service/event/service_event_handler.go b/server/service/cache/filter_permission.go
similarity index 50%
copy from server/service/event/service_event_handler.go
copy to server/service/cache/filter_permission.go
index 45392f1..69e4dd8 100644
--- a/server/service/event/service_event_handler.go
+++ b/server/service/cache/filter_permission.go
@@ -14,49 +14,47 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package event
+package cache
 
 import (
+	"fmt"
+	"github.com/apache/incubator-servicecomb-service-center/pkg/cache"
 	"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"
 	serviceUtil "github.com/apache/incubator-servicecomb-service-center/server/service/util"
-	"github.com/coreos/etcd/mvcc/mvccpb"
 	"golang.org/x/net/context"
-	"strings"
 )
 
-type ServiceEventHandler struct {
+type AccessibleFilter struct {
 }
 
-func (h *ServiceEventHandler) Type() backend.StoreType {
-	return backend.SERVICE
+func (f *AccessibleFilter) Name(ctx context.Context) string {
+	consumer := ctx.Value(CTX_FIND_CONSUMER).(*pb.MicroService)
+	return consumer.ServiceId
 }
 
-func (h *ServiceEventHandler) OnEvent(evt backend.KvEvent) {
-	action := evt.Type
-	if action != pb.EVT_CREATE && action != pb.EVT_INIT {
-		return
+func (f *AccessibleFilter) Init(ctx context.Context, parent *cache.Node) (node *cache.Node, err error) {
+	var ids []string
+	consumerId := f.Name(ctx)
+	pCopy := *parent.Cache.Get(CACHE_FIND).(*VersionRuleCacheItem)
+	for _, providerServiceId := range pCopy.ServiceIds {
+		if err := serviceUtil.Accessible(ctx, consumerId, providerServiceId); err != nil {
+			provider := ctx.Value(CTX_FIND_PROVIDER).(*pb.MicroServiceKey)
+			findFlag := fmt.Sprintf("consumer %s find provider %s/%s/%s", consumerId,
+				provider.AppId, provider.ServiceName, provider.Version)
+			util.Logger().Errorf(err, "AccessibleFilter failed, %s", findFlag)
+			continue
+		}
+		ids = append(ids, providerServiceId)
 	}
 
-	kv := evt.Object.(*mvccpb.KeyValue)
-	serviceId, domainProject, data := pb.GetInfoFromSvcKV(kv)
-	if data == nil {
-		util.Logger().Errorf(nil,
-			"unmarshal service file failed, service %s [%s] event, data is nil",
-			serviceId, action)
+	if len(ids) == 0 {
 		return
 	}
 
-	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)
-		return
-	}
-}
+	pCopy.ServiceIds = ids
 
-func NewServiceEventHandler() *ServiceEventHandler {
-	return &ServiceEventHandler{}
+	node = cache.NewNode()
+	node.Cache.Set(CACHE_FIND, &pCopy)
+	return
 }
diff --git a/server/service/util/tag_util_test.go b/server/service/cache/filter_service.go
similarity index 61%
copy from server/service/util/tag_util_test.go
copy to server/service/cache/filter_service.go
index 20fa7d5..37bee6f 100644
--- a/server/service/util/tag_util_test.go
+++ b/server/service/cache/filter_service.go
@@ -14,29 +14,28 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package util
+package cache
 
 import (
+	"github.com/apache/incubator-servicecomb-service-center/pkg/cache"
 	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
+	pb "github.com/apache/incubator-servicecomb-service-center/server/core/proto"
 	"golang.org/x/net/context"
-	"testing"
 )
 
-func TestAddTagIntoETCD(t *testing.T) {
-	err := AddTagIntoETCD(context.Background(), "", "", map[string]string{"a": "1"})
-	if err == nil {
-		t.Fatalf(`AddTagIntoETCD with {"a": "1"} tags failed`)
-	}
+type ServiceFilter struct {
 }
 
-func TestGetTagsUtils(t *testing.T) {
-	_, err := GetTagsUtils(util.SetContext(context.Background(), CTX_CACHEONLY, "1"), "", "")
-	if err != nil {
-		t.Fatalf(`GetTagsUtils WithCacheOnly failed`)
-	}
+func (f *ServiceFilter) Name(ctx context.Context) string {
+	provider := ctx.Value(CTX_FIND_PROVIDER).(*pb.MicroServiceKey)
+	return util.StringJoin([]string{
+		provider.Tenant,
+		provider.Environment,
+		provider.AppId,
+		provider.ServiceName}, "/")
+}
 
-	_, err = GetTagsUtils(context.Background(), "", "")
-	if err == nil {
-		t.Fatalf(`GetTagsUtils failed`)
-	}
+func (f *ServiceFilter) Init(ctx context.Context, parent *cache.Node) (node *cache.Node, err error) {
+	node = cache.NewNode()
+	return
 }
diff --git a/server/service/cache/filter_tags.go b/server/service/cache/filter_tags.go
new file mode 100644
index 0000000..1964f15
--- /dev/null
+++ b/server/service/cache/filter_tags.go
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package cache
+
+import (
+	"fmt"
+	"github.com/apache/incubator-servicecomb-service-center/pkg/cache"
+	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
+	pb "github.com/apache/incubator-servicecomb-service-center/server/core/proto"
+	serviceUtil "github.com/apache/incubator-servicecomb-service-center/server/service/util"
+	"golang.org/x/net/context"
+	"sort"
+	"strings"
+)
+
+type TagsFilter struct {
+}
+
+func (f *TagsFilter) Name(ctx context.Context) string {
+	tags, _ := ctx.Value(CTX_FIND_TAGS).([]string)
+	sort.Strings(tags)
+	return strings.Join(tags, ",")
+}
+
+func (f *TagsFilter) Init(ctx context.Context, parent *cache.Node) (node *cache.Node, err error) {
+	tags, _ := ctx.Value(CTX_FIND_TAGS).([]string)
+	if len(tags) == 0 {
+		node = cache.NewNode()
+		node.Cache = parent.Cache
+		return
+	}
+
+	var ids []string
+
+	targetDomainProject := util.ParseTargetDomainProject(ctx)
+	pCopy := *parent.Cache.Get(CACHE_FIND).(*VersionRuleCacheItem)
+
+loopProviderIds:
+	for _, providerServiceId := range pCopy.ServiceIds {
+		tagsFromETCD, err := serviceUtil.GetTagsUtils(ctx, targetDomainProject, providerServiceId)
+		if err != nil {
+			consumer := ctx.Value(CTX_FIND_CONSUMER).(*pb.MicroService)
+			provider := ctx.Value(CTX_FIND_PROVIDER).(*pb.MicroServiceKey)
+			findFlag := fmt.Sprintf("consumer %s find provider %s/%s/%s", consumer.ServiceId,
+				provider.AppId, provider.ServiceName, provider.Version)
+			util.Logger().Errorf(err, "TagsFilter failed, %s", findFlag)
+			return nil, err
+		}
+		if len(tagsFromETCD) == 0 {
+			continue
+		}
+		for _, tag := range tags {
+			if _, ok := tagsFromETCD[tag]; !ok {
+				continue loopProviderIds
+			}
+		}
+		ids = append(ids, providerServiceId)
+	}
+
+	if len(ids) == 0 {
+		return
+	}
+
+	pCopy.ServiceIds = ids
+
+	node = cache.NewNode()
+	node.Cache.Set(CACHE_FIND, &pCopy)
+	return
+}
diff --git a/server/service/event/service_event_handler.go b/server/service/cache/filter_version.go
similarity index 52%
copy from server/service/event/service_event_handler.go
copy to server/service/cache/filter_version.go
index 45392f1..f5bb708 100644
--- a/server/service/event/service_event_handler.go
+++ b/server/service/cache/filter_version.go
@@ -14,49 +14,44 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package event
+package cache
 
 import (
+	"fmt"
+	"github.com/apache/incubator-servicecomb-service-center/pkg/cache"
 	"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"
 	serviceUtil "github.com/apache/incubator-servicecomb-service-center/server/service/util"
-	"github.com/coreos/etcd/mvcc/mvccpb"
 	"golang.org/x/net/context"
-	"strings"
 )
 
-type ServiceEventHandler struct {
+type VersionRuleFilter struct {
 }
 
-func (h *ServiceEventHandler) Type() backend.StoreType {
-	return backend.SERVICE
+func (f *VersionRuleFilter) Name(ctx context.Context) string {
+	provider := ctx.Value(CTX_FIND_PROVIDER).(*pb.MicroServiceKey)
+	return provider.Version
 }
 
-func (h *ServiceEventHandler) OnEvent(evt backend.KvEvent) {
-	action := evt.Type
-	if action != pb.EVT_CREATE && action != pb.EVT_INIT {
+func (f *VersionRuleFilter) Init(ctx context.Context, parent *cache.Node) (node *cache.Node, err error) {
+	provider := ctx.Value(CTX_FIND_PROVIDER).(*pb.MicroServiceKey)
+	// 版本规则
+	ids, err := serviceUtil.FindServiceIds(ctx, provider.Version, provider)
+	if err != nil {
+		consumer := ctx.Value(CTX_FIND_CONSUMER).(*pb.MicroService)
+		findFlag := fmt.Sprintf("consumer %s find provider %s/%s/%s", consumer.ServiceId,
+			provider.AppId, provider.ServiceName, provider.Version)
+		util.Logger().Errorf(err, "VersionRuleFilter failed, %s", findFlag)
 		return
 	}
-
-	kv := evt.Object.(*mvccpb.KeyValue)
-	serviceId, domainProject, data := pb.GetInfoFromSvcKV(kv)
-	if data == nil {
-		util.Logger().Errorf(nil,
-			"unmarshal service file failed, service %s [%s] event, data is nil",
-			serviceId, action)
+	if len(ids) == 0 {
 		return
 	}
 
-	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)
-		return
-	}
-}
-
-func NewServiceEventHandler() *ServiceEventHandler {
-	return &ServiceEventHandler{}
+	node = cache.NewNode()
+	node.Cache.Set(CACHE_FIND, &VersionRuleCacheItem{
+		VersionRule: provider.Version,
+		ServiceIds:  ids,
+	})
+	return
 }
diff --git a/server/service/cache/instance.go b/server/service/cache/instance.go
new file mode 100644
index 0000000..52967f7
--- /dev/null
+++ b/server/service/cache/instance.go
@@ -0,0 +1,73 @@
+/*
+ * 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 cache
+
+import (
+	"github.com/apache/incubator-servicecomb-service-center/pkg/cache"
+	pb "github.com/apache/incubator-servicecomb-service-center/server/core/proto"
+	serviceUtil "github.com/apache/incubator-servicecomb-service-center/server/service/util"
+	"golang.org/x/net/context"
+	"math"
+	"time"
+)
+
+var FindInstances = &FindInstancesCache{
+	Tree: cache.NewTree(cache.Configure().
+		WithTTL(2 * time.Minute).
+		WithMaxSize(math.MaxInt64))}
+
+func init() {
+	FindInstances.AddFilter(
+		&ServiceFilter{},
+		&VersionRuleFilter{},
+		&TagsFilter{},
+		&AccessibleFilter{},
+		&InstancesFilter{})
+}
+
+type VersionRuleCacheItem struct {
+	VersionRule string
+	ServiceIds  []string
+	Instances   []*pb.MicroServiceInstance
+	Rev         string
+}
+
+type FindInstancesCache struct {
+	*cache.Tree
+}
+
+func (f *FindInstancesCache) Get(ctx context.Context, consumer *pb.MicroService, provider *pb.MicroServiceKey, tags []string) (*VersionRuleCacheItem, error) {
+	cloneCtx := context.WithValue(context.WithValue(context.WithValue(ctx,
+		CTX_FIND_CONSUMER, consumer),
+		CTX_FIND_PROVIDER, provider),
+		CTX_FIND_TAGS, tags)
+
+	node, err := f.Tree.Get(cloneCtx, cache.Options().Temporary(ctx.Value(serviceUtil.CTX_NOCACHE) == "1"))
+	if node == nil {
+		return nil, err
+	}
+	return node.Cache.Get(CACHE_FIND).(*VersionRuleCacheItem), nil
+}
+
+func (f *FindInstancesCache) Remove(provider *pb.MicroServiceKey) {
+	f.Tree.Remove(context.WithValue(context.Background(), CTX_FIND_PROVIDER, provider))
+	if len(provider.Alias) > 0 {
+		copy := *provider
+		copy.ServiceName = copy.Alias
+		f.Tree.Remove(context.WithValue(context.Background(), CTX_FIND_PROVIDER, &copy))
+	}
+}
diff --git a/server/service/event/dependency_event_handler.go b/server/service/event/dependency_event_handler.go
index 2bcbf1d..235fa02 100644
--- a/server/service/event/dependency_event_handler.go
+++ b/server/service/event/dependency_event_handler.go
@@ -17,7 +17,6 @@
 package event
 
 import (
-	"encoding/json"
 	"fmt"
 	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
 	"github.com/apache/incubator-servicecomb-service-center/server/core"
@@ -26,7 +25,6 @@ import (
 	"github.com/apache/incubator-servicecomb-service-center/server/infra/registry"
 	"github.com/apache/incubator-servicecomb-service-center/server/mux"
 	serviceUtil "github.com/apache/incubator-servicecomb-service-center/server/service/util"
-	"github.com/coreos/etcd/mvcc/mvccpb"
 	"golang.org/x/net/context"
 	"time"
 )
@@ -90,11 +88,11 @@ func (h *DependencyEventHandler) loop() {
 
 type DependencyEventHandlerResource struct {
 	dep           *pb.ConsumerDependency
-	kv            *mvccpb.KeyValue
+	kv            *backend.KeyValue
 	domainProject string
 }
 
-func NewDependencyEventHandlerResource(dep *pb.ConsumerDependency, kv *mvccpb.KeyValue, domainProject string) *DependencyEventHandlerResource {
+func NewDependencyEventHandlerResource(dep *pb.ConsumerDependency, kv *backend.KeyValue, domainProject string) *DependencyEventHandlerResource {
 	return &DependencyEventHandlerResource{
 		dep,
 		kv,
@@ -126,25 +124,12 @@ func (h *DependencyEventHandler) Handle() error {
 		return nil
 	}
 
-	ctx := context.Background()
-
 	dependencyTree := util.NewTree(isAddToLeft)
 
 	for _, kv := range resp.Kvs {
-		r := &pb.ConsumerDependency{}
-		consumerId, domainProject, data := pb.GetInfoFromDependencyQueueKV(kv)
-
-		err := json.Unmarshal(data, r)
-		if err != nil {
-			util.Logger().Errorf(err, "maintain dependency failed, unmarshal failed, consumer %s dependency: %s",
-				consumerId, util.BytesToStringWithNoCopy(data))
-
-			if err = h.removeKV(ctx, kv); err != nil {
-				return err
-			}
-			continue
-		}
+		r := kv.Value.(*pb.ConsumerDependency)
 
+		_, domainProject := backend.GetInfoFromDependencyQueueKV(kv)
 		res := NewDependencyEventHandlerResource(r, kv, domainProject)
 
 		dependencyTree.AddNode(res)
@@ -188,7 +173,7 @@ func (h *DependencyEventHandler) dependencyRuleHandle(res interface{}) error {
 	return nil
 }
 
-func (h *DependencyEventHandler) removeKV(ctx context.Context, kv *mvccpb.KeyValue) error {
+func (h *DependencyEventHandler) removeKV(ctx context.Context, kv *backend.KeyValue) error {
 	dResp, err := backend.Registry().TxnWithCmp(ctx, []registry.PluginOp{registry.OpDel(registry.WithKey(kv.Key))},
 		[]registry.CompareOp{registry.OpCmp(registry.CmpVer(kv.Key), registry.CMP_EQUAL, kv.Version)},
 		nil)
diff --git a/server/service/event/instance_event_handler.go b/server/service/event/instance_event_handler.go
index a80376d..bd7e8eb 100644
--- a/server/service/event/instance_event_handler.go
+++ b/server/service/event/instance_event_handler.go
@@ -17,14 +17,13 @@
 package event
 
 import (
-	"encoding/json"
 	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
 	apt "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"
+	"github.com/apache/incubator-servicecomb-service-center/server/service/cache"
 	nf "github.com/apache/incubator-servicecomb-service-center/server/service/notification"
 	serviceUtil "github.com/apache/incubator-servicecomb-service-center/server/service/util"
-	"github.com/coreos/etcd/mvcc/mvccpb"
 	"golang.org/x/net/context"
 	"strings"
 )
@@ -42,33 +41,26 @@ func (h *InstanceEventHandler) OnEvent(evt backend.KvEvent) {
 		return
 	}
 
-	kv := evt.Object.(*mvccpb.KeyValue)
-	providerId, providerInstanceId, domainProject, data := pb.GetInfoFromInstKV(kv)
-	if data == nil {
-		util.Logger().Errorf(nil,
-			"unmarshal provider service instance file failed, instance %s/%s [%s] event, data is nil",
-			providerId, providerInstanceId, action)
-		return
-	}
+	providerId, providerInstanceId, domainProject := backend.GetInfoFromInstKV(evt.KV)
 	if action == pb.EVT_DELETE {
 		splited := strings.Split(domainProject, "/")
 		if len(splited) == 2 && !apt.IsDefaultDomainProject(domainProject) {
-			domainName := splited[0]
-			projectName := splited[1]
-			ctx := util.SetDomainProject(context.Background(), domainName, projectName)
-			serviceUtil.RemandInstanceQuota(ctx)
+			domainName, projectName := splited[0], splited[1]
+			serviceUtil.RemandInstanceQuota(
+				util.SetDomainProject(context.Background(), domainName, projectName))
 		}
 	}
 
 	if nf.GetNotifyService().Closed() {
-		util.Logger().Warnf(nil, "caught instance %s/%s [%s] event, but notify service is closed",
-			providerId, providerInstanceId, action)
+		util.Logger().Warnf(nil, "caught [%s] instance event %s/%s, but notify service is closed",
+			action, providerId, providerInstanceId)
 		return
 	}
-	util.Logger().Infof("caught instance %s/%s [%s] event", providerId, providerInstanceId, action)
+	util.Logger().Infof("caught [%s] instance event %s/%s", action, providerId, providerInstanceId)
 
 	// 查询服务版本信息
-	ms, err := serviceUtil.GetServiceInCache(context.Background(), domainProject, providerId)
+	ctx := util.SetContext(context.Background(), serviceUtil.CTX_CACHEONLY, "1")
+	ms, err := serviceUtil.GetService(ctx, domainProject, providerId)
 	if ms == nil {
 		util.Logger().Warnf(err, "get provider service %s/%s id in cache failed",
 			providerId, providerInstanceId)
@@ -76,29 +68,14 @@ func (h *InstanceEventHandler) OnEvent(evt backend.KvEvent) {
 	}
 
 	// 查询所有consumer
-	consumerIds, _, err := serviceUtil.GetConsumerIdsByProvider(context.Background(), domainProject, ms)
+	consumerIds, _, err := serviceUtil.GetAllConsumerIds(ctx, domainProject, ms)
 	if err != nil {
 		util.Logger().Errorf(err, "query service %s consumers failed", providerId)
 		return
 	}
-	if len(consumerIds) == 0 {
-		return
-	}
-
-	var instance pb.MicroServiceInstance
-	err = json.Unmarshal(data, &instance)
-	if err != nil {
-		util.Logger().Errorf(err, "unmarshal provider service instance %s/%s file failed",
-			providerId, providerInstanceId)
-		return
-	}
 
-	PublishInstanceEvent(domainProject, action, &pb.MicroServiceKey{
-		Environment: ms.Environment,
-		AppId:       ms.AppId,
-		ServiceName: ms.ServiceName,
-		Version:     ms.Version,
-	}, &instance, evt.Revision, consumerIds)
+	PublishInstanceEvent(domainProject, action, pb.MicroServiceToKey(domainProject, ms),
+		evt.KV.Value.(*pb.MicroServiceInstance), evt.Revision, consumerIds)
 }
 
 func NewInstanceEventHandler() *InstanceEventHandler {
@@ -106,6 +83,12 @@ func NewInstanceEventHandler() *InstanceEventHandler {
 }
 
 func PublishInstanceEvent(domainProject string, action pb.EventType, serviceKey *pb.MicroServiceKey, instance *pb.MicroServiceInstance, rev int64, subscribers []string) {
+	defer cache.FindInstances.Remove(serviceKey)
+
+	if len(subscribers) == 0 {
+		return
+	}
+
 	response := &pb.WatchInstanceResponse{
 		Response: pb.CreateResponse(pb.Response_SUCCESS, "Watch instance successfully."),
 		Action:   string(action),
@@ -113,9 +96,6 @@ func PublishInstanceEvent(domainProject string, action pb.EventType, serviceKey
 		Instance: instance,
 	}
 	for _, consumerId := range subscribers {
-		// expires cache
-		serviceUtil.FindInstancesCache.Delete(domainProject, consumerId, serviceKey)
-
 		// TODO add超时怎么处理?
 		job := nf.NewWatchJob(consumerId, apt.GetInstanceRootKey(domainProject)+"/", rev, response)
 		nf.GetNotifyService().AddJob(job)
diff --git a/server/service/event/rule_event_handler.go b/server/service/event/rule_event_handler.go
index 474811e..4e6cf92 100644
--- a/server/service/event/rule_event_handler.go
+++ b/server/service/event/rule_event_handler.go
@@ -17,7 +17,6 @@
 package event
 
 import (
-	"encoding/json"
 	"fmt"
 	"github.com/apache/incubator-servicecomb-service-center/pkg/async"
 	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
@@ -25,7 +24,6 @@ import (
 	pb "github.com/apache/incubator-servicecomb-service-center/server/core/proto"
 	nf "github.com/apache/incubator-servicecomb-service-center/server/service/notification"
 	serviceUtil "github.com/apache/incubator-servicecomb-service-center/server/service/util"
-	"github.com/coreos/etcd/mvcc/mvccpb"
 	"golang.org/x/net/context"
 )
 
@@ -52,7 +50,9 @@ func (apt *RulesChangedTask) Err() error {
 }
 
 func (apt *RulesChangedTask) publish(ctx context.Context, domainProject, providerId string, rev int64) error {
-	provider, err := serviceUtil.GetServiceInCache(ctx, domainProject, providerId)
+	ctx = util.SetContext(ctx, serviceUtil.CTX_CACHEONLY, "1")
+
+	provider, err := serviceUtil.GetService(ctx, domainProject, providerId)
 	if err != nil {
 		util.Logger().Errorf(err, "get provider %s service file failed", providerId)
 		return err
@@ -62,7 +62,7 @@ func (apt *RulesChangedTask) publish(ctx context.Context, domainProject, provide
 		return fmt.Errorf("provider %s does not exist", providerId)
 	}
 
-	consumerIds, err := serviceUtil.GetConsumersInCache(ctx, domainProject, provider)
+	consumerIds, err := serviceUtil.GetConsumerIds(ctx, domainProject, provider)
 	if err != nil {
 		util.Logger().Errorf(err, "get consumer services by provider %s failed", providerId)
 		return err
@@ -86,29 +86,13 @@ func (h *RuleEventHandler) OnEvent(evt backend.KvEvent) {
 		return
 	}
 
-	kv := evt.Object.(*mvccpb.KeyValue)
-	providerId, ruleId, domainProject, data := pb.GetInfoFromRuleKV(kv)
-	if data == nil {
-		util.Logger().Errorf(nil,
-			"unmarshal service rule file failed, service %s rule %s [%s] event, data is nil",
-			providerId, ruleId, action)
-		return
-	}
-
+	providerId, ruleId, domainProject := backend.GetInfoFromRuleKV(evt.KV)
 	if nf.GetNotifyService().Closed() {
-		util.Logger().Warnf(nil, "caught service %s rule %s [%s] event, but notify service is closed",
-			providerId, ruleId, action)
-		return
-	}
-	util.Logger().Infof("caught service %s rule %s [%s] event", providerId, ruleId, action)
-
-	var rule pb.ServiceRule
-	err := json.Unmarshal(data, &rule)
-	if err != nil {
-		util.Logger().Errorf(err, "unmarshal service %s rule %s file failed",
-			providerId, ruleId)
+		util.Logger().Warnf(nil, "caught [%s] service rule event %s/%s, but notify service is closed",
+			action, providerId, ruleId)
 		return
 	}
+	util.Logger().Infof("caught [%s] service rule event %s/%s", action, providerId, ruleId)
 
 	async.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 45392f1..dc17730 100644
--- a/server/service/event/service_event_handler.go
+++ b/server/service/event/service_event_handler.go
@@ -20,8 +20,8 @@ import (
 	"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"
+	"github.com/apache/incubator-servicecomb-service-center/server/service/cache"
 	serviceUtil "github.com/apache/incubator-servicecomb-service-center/server/service/util"
-	"github.com/coreos/etcd/mvcc/mvccpb"
 	"golang.org/x/net/context"
 	"strings"
 )
@@ -34,27 +34,30 @@ func (h *ServiceEventHandler) Type() backend.StoreType {
 }
 
 func (h *ServiceEventHandler) OnEvent(evt backend.KvEvent) {
-	action := evt.Type
-	if action != pb.EVT_CREATE && action != pb.EVT_INIT {
-		return
-	}
+	ms := evt.KV.Value.(*pb.MicroService)
+	_, domainProject := backend.GetInfoFromSvcKV(evt.KV)
 
-	kv := evt.Object.(*mvccpb.KeyValue)
-	serviceId, domainProject, data := pb.GetInfoFromSvcKV(kv)
-	if data == nil {
-		util.Logger().Errorf(nil,
-			"unmarshal service file failed, service %s [%s] event, data is nil",
-			serviceId, action)
-		return
+	switch evt.Type {
+	case pb.EVT_INIT, pb.EVT_CREATE:
+		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)
+		}
+	default:
 	}
 
-	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)
+	if evt.Type == pb.EVT_INIT {
 		return
 	}
+
+	util.Logger().Infof("caught [%s] service %s/%s/%s event",
+		evt.Type, ms.AppId, ms.ServiceName, ms.Version)
+
+	// cache
+	providerKey := pb.MicroServiceToKey(domainProject, ms)
+	cache.FindInstances.Remove(providerKey)
 }
 
 func NewServiceEventHandler() *ServiceEventHandler {
diff --git a/server/service/event/tag_event_handler.go b/server/service/event/tag_event_handler.go
index a83d2c5..fe09ad8 100644
--- a/server/service/event/tag_event_handler.go
+++ b/server/service/event/tag_event_handler.go
@@ -17,15 +17,14 @@
 package event
 
 import (
-	"encoding/json"
 	"fmt"
 	"github.com/apache/incubator-servicecomb-service-center/pkg/async"
 	"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"
+	"github.com/apache/incubator-servicecomb-service-center/server/service/cache"
 	nf "github.com/apache/incubator-servicecomb-service-center/server/service/notification"
 	serviceUtil "github.com/apache/incubator-servicecomb-service-center/server/service/util"
-	"github.com/coreos/etcd/mvcc/mvccpb"
 	"golang.org/x/net/context"
 )
 
@@ -52,34 +51,36 @@ func (apt *TagsChangedTask) Err() error {
 }
 
 func (apt *TagsChangedTask) publish(ctx context.Context, domainProject, consumerId string, rev int64) error {
-	consumer, err := serviceUtil.GetServiceInCache(ctx, domainProject, consumerId)
+	ctx = util.SetContext(ctx, serviceUtil.CTX_CACHEONLY, "1")
+
+	consumer, err := serviceUtil.GetService(ctx, domainProject, consumerId)
 	if err != nil {
-		util.Logger().Errorf(err, "get comsumer for publish event %s failed", consumerId)
+		util.Logger().Errorf(err, "get consumer for publish event %s failed", consumerId)
 		return err
 	}
 	if consumer == nil {
 		util.Logger().Errorf(nil, "service not exist, %s", consumerId)
 		return fmt.Errorf("service not exist, %s", consumerId)
 	}
-	providerIds, err := serviceUtil.GetProvidersInCache(ctx, domainProject, consumer)
+
+	serviceKey := pb.MicroServiceToKey(domainProject, consumer)
+	cache.FindInstances.Remove(serviceKey)
+
+	providerIds, err := serviceUtil.GetProviderIds(ctx, domainProject, consumer)
 	if err != nil {
 		util.Logger().Errorf(err, "get provider services by consumer %s failed", consumerId)
 		return err
 	}
 
 	for _, providerId := range providerIds {
-		provider, err := serviceUtil.GetServiceInCache(ctx, domainProject, providerId)
+		provider, err := serviceUtil.GetService(ctx, domainProject, providerId)
 		if provider == nil {
 			util.Logger().Warnf(err, "get service %s file failed", providerId)
 			continue
 		}
-		PublishInstanceEvent(domainProject, pb.EVT_EXPIRE,
-			&pb.MicroServiceKey{
-				Environment: provider.Environment,
-				AppId:       provider.AppId,
-				ServiceName: provider.ServiceName,
-				Version:     provider.Version,
-			}, nil, rev, []string{consumerId})
+		providerKey := pb.MicroServiceToKey(domainProject, provider)
+
+		PublishInstanceEvent(domainProject, pb.EVT_EXPIRE, providerKey, nil, rev, []string{consumerId})
 	}
 	return nil
 }
@@ -97,28 +98,14 @@ func (h *TagEventHandler) OnEvent(evt backend.KvEvent) {
 		return
 	}
 
-	kv := evt.Object.(*mvccpb.KeyValue)
-	consumerId, domainProject, data := pb.GetInfoFromTagKV(kv)
-	if data == nil {
-		util.Logger().Errorf(nil,
-			"unmarshal service rule file failed, service %s tags [%s] event, data is nil",
-			consumerId, action)
-		return
-	}
+	consumerId, domainProject := backend.GetInfoFromTagKV(evt.KV)
 
 	if nf.GetNotifyService().Closed() {
-		util.Logger().Warnf(nil, "caught service %s tags [%s] event, but notify service is closed",
-			consumerId, action)
-		return
-	}
-	util.Logger().Infof("caught service %s tags [%s] event", consumerId, action)
-
-	var rule pb.ServiceRule
-	err := json.Unmarshal(data, &rule)
-	if err != nil {
-		util.Logger().Errorf(err, "unmarshal service %s tags file failed", consumerId)
+		util.Logger().Warnf(nil, "caught [%s] service tags event %s/%s, but notify service is closed",
+			action, consumerId, evt.KV.Value)
 		return
 	}
+	util.Logger().Infof("caught [%s] service tags event %s/%s", action, consumerId, evt.KV.Value)
 
 	async.Service().Add(context.Background(),
 		NewTagsChangedAsyncTask(domainProject, consumerId, evt.Revision))
diff --git a/server/service/instance.go b/server/service/instance.go
index 4b90968..848291f 100644
--- a/server/service/instance.go
+++ b/server/service/instance.go
@@ -28,6 +28,7 @@ import (
 	"github.com/apache/incubator-servicecomb-service-center/server/infra/quota"
 	"github.com/apache/incubator-servicecomb-service-center/server/infra/registry"
 	"github.com/apache/incubator-servicecomb-service-center/server/plugin"
+	"github.com/apache/incubator-servicecomb-service-center/server/service/cache"
 	serviceUtil "github.com/apache/incubator-servicecomb-service-center/server/service/util"
 	"golang.org/x/net/context"
 	"math"
@@ -537,91 +538,87 @@ func (s *InstanceService) Find(ctx context.Context, in *pb.FindInstancesRequest)
 		provider.Tenant = util.ParseTargetDomainProject(ctx)
 	}
 
+	noCache, cacheOnly := ctx.Value(serviceUtil.CTX_NOCACHE) == "1", ctx.Value(serviceUtil.CTX_CACHEONLY) == "1"
+	rev, _ := ctx.Value(serviceUtil.CTX_REQUEST_REVISION).(string)
+	reqRev, _ := serviceUtil.ParseRevision(rev)
+	cloneCtx := util.CloneContext(ctx)
+
 	// cache
-	if item := serviceUtil.FindInstancesCache.Get(provider.Tenant, in.ConsumerServiceId, provider); item != nil {
-		noCache, cacheOnly := ctx.Value(serviceUtil.CTX_NOCACHE) == "1", ctx.Value(serviceUtil.CTX_CACHEONLY) == "1"
-		rev, _ := ctx.Value(serviceUtil.CTX_REQUEST_REVISION).(string)
-		reqRev, _ := serviceUtil.ParseRevision(rev)
-		cacheRev, _ := serviceUtil.ParseRevision(item.Rev)
-		if !noCache && (cacheOnly || reqRev <= cacheRev) {
-			instances := item.Instances
-			if rev == item.Rev {
-				instances = instances[:0]
-			}
-			util.SetContext(ctx, serviceUtil.CTX_RESPONSE_REVISION, item.Rev)
+	var (
+		item           *cache.VersionRuleCacheItem
+		i              = 0
+		newVersionRule = !cache.DependencyRule.ExistVersionRule(cloneCtx, in.ConsumerServiceId, provider)
+	)
+	if noCache {
+		i = 1
+	}
+	for ; i < 2; i++ {
+		item, err = cache.FindInstances.Get(cloneCtx, service, provider, in.Tags)
+		if err != nil {
+			util.Logger().Errorf(err, "FindInstancesCache.Get failed, %s", findFlag)
 			return &pb.FindInstancesResponse{
-				Response:  pb.CreateResponse(pb.Response_SUCCESS, "Query service instances successfully."),
-				Instances: instances,
+				Response: pb.CreateResponse(scerr.ErrInternal, err.Error()),
+			}, err
+		}
+		if item == nil {
+			mes := fmt.Sprintf("provider not exist, %s", findFlag)
+			util.Logger().Errorf(nil, "FindInstancesCache.Get failed, %s", mes)
+			return &pb.FindInstancesResponse{
+				Response: pb.CreateResponse(scerr.ErrServiceNotExists, mes),
 			}, nil
 		}
-	}
 
-	// 版本规则
-	ids, err := serviceUtil.FindServiceIds(ctx, in.VersionRule, provider)
-	if err != nil {
-		util.Logger().Errorf(err, "find instance failed, %s: get providers failed.", findFlag)
-		return &pb.FindInstancesResponse{
-			Response: pb.CreateResponse(scerr.ErrInternal, err.Error()),
-		}, err
-	}
-
-	if len(ids) == 0 {
-		mes := fmt.Sprintf("provider not exist, %s", findFlag)
-		util.Logger().Errorf(nil, "find instance failed, %s", mes)
-		return &pb.FindInstancesResponse{
-			Response: pb.CreateResponse(scerr.ErrServiceNotExists, mes),
-		}, nil
-	}
-
-	for i := 0; i < len(ids); i++ {
-		err := s.getInstancePreCheck(ctx, ids[i], in.ConsumerServiceId, in.Tags)
-		if err != nil {
-			ids = append(ids[:i], ids[i+1:]...)
-			i--
+		cacheRev, _ := serviceUtil.ParseRevision(item.Rev)
+		if noCache || cacheOnly || reqRev <= cacheRev {
+			break
 		}
+		util.SetContext(cloneCtx, serviceUtil.CTX_NOCACHE, "1")
 	}
 
-	if len(ids) > 0 {
-		//维护version的规则,service name 可能是别名,所以重新获取
-		providerService, err := serviceUtil.GetService(ctx, provider.Tenant, ids[0])
-		if providerService == nil {
-			util.Logger().Errorf(err, "find instance failed, %s: provider %s not exist.", findFlag, ids[0])
+	// add dependency queue
+	if newVersionRule {
+		provider, err = s.reshapeProviderKey(ctx, provider, item.ServiceIds[0])
+		if provider != nil {
+			err = serviceUtil.AddServiceVersionRule(ctx, domainProject, service, provider)
+		} else {
+			mes := fmt.Sprintf("provider not exist, %s", findFlag)
+			util.Logger().Errorf(nil, "AddServiceVersionRule failed, %s", mes)
 			return &pb.FindInstancesResponse{
-				Response: pb.CreateResponse(scerr.ErrServiceNotExists, "No provider matched."),
+				Response: pb.CreateResponse(scerr.ErrServiceNotExists, mes),
 			}, nil
 		}
-
-		provider = pb.MicroServiceToKey(provider.Tenant, providerService)
-		provider.Version = in.VersionRule
-
-		err = serviceUtil.AddServiceVersionRule(ctx, domainProject, service, provider)
 		if err != nil {
-			util.Logger().Errorf(err, "find instance failed, %s: add service version rule failed.", findFlag)
+			util.Logger().Errorf(err, "AddServiceVersionRule failed, %s", findFlag)
 			return &pb.FindInstancesResponse{
 				Response: pb.CreateResponse(scerr.ErrInternal, err.Error()),
 			}, err
 		}
 	}
 
-	instances, rev, err := serviceUtil.GetAllInstancesOfServices(ctx, util.ParseTargetDomainProject(ctx), ids)
-	if err != nil {
-		util.Logger().Errorf(err, "find instance failed, %s: GetAllInstancesOfServices failed.", findFlag)
-		return &pb.FindInstancesResponse{
-			Response: pb.CreateResponse(scerr.ErrInternal, err.Error()),
-		}, err
+	instances := item.Instances
+	if rev == item.Rev {
+		instances = instances[:0]
 	}
-
-	serviceUtil.FindInstancesCache.Set(provider.Tenant, in.ConsumerServiceId, provider, &serviceUtil.VersionRuleCacheItem{
-		Instances: instances,
-		Rev:       rev,
-	})
-	util.SetContext(ctx, serviceUtil.CTX_RESPONSE_REVISION, rev)
+	util.SetContext(ctx, serviceUtil.CTX_RESPONSE_REVISION, item.Rev)
 	return &pb.FindInstancesResponse{
 		Response:  pb.CreateResponse(pb.Response_SUCCESS, "Query service instances successfully."),
 		Instances: instances,
 	}, nil
 }
 
+func (s *InstanceService) reshapeProviderKey(ctx context.Context, provider *pb.MicroServiceKey, providerId string) (*pb.MicroServiceKey, error) {
+	//维护version的规则,service name 可能是别名,所以重新获取
+	providerService, err := serviceUtil.GetService(ctx, provider.Tenant, providerId)
+	if providerService == nil {
+		return nil, err
+	}
+
+	versionRule := provider.Version
+	provider = pb.MicroServiceToKey(provider.Tenant, providerService)
+	provider.Version = versionRule
+	return provider, nil
+}
+
 func (s *InstanceService) UpdateStatus(ctx context.Context, in *pb.UpdateInstanceStatusRequest) (*pb.UpdateInstanceStatusResponse, error) {
 	domainProject := util.ParseDomainProject(ctx)
 	updateStatusFlag := util.StringJoin([]string{in.ServiceId, in.InstanceId, in.Status}, "/")
diff --git a/server/service/instance_test.go b/server/service/instance_test.go
index c11866e..692e4d1 100644
--- a/server/service/instance_test.go
+++ b/server/service/instance_test.go
@@ -1235,8 +1235,7 @@ var _ = Describe("'Instance' service", func() {
 					VersionRule:       "1.0.5",
 				})
 				Expect(err).To(BeNil())
-				Expect(respFind.Response.Code).To(Equal(pb.Response_SUCCESS))
-				Expect(len(respFind.Instances)).To(Equal(0))
+				Expect(respFind.Response.Code).To(Equal(scerr.ErrServiceNotExists))
 
 				By("provider tag does not exist")
 				respFind, err = instanceResource.Find(getContext(), &pb.FindInstancesRequest{
@@ -1247,8 +1246,7 @@ var _ = Describe("'Instance' service", func() {
 					Tags:              []string{"notexisttag"},
 				})
 				Expect(err).To(BeNil())
-				Expect(respFind.Response.Code).To(Equal(pb.Response_SUCCESS))
-				Expect(len(respFind.Instances)).To(Equal(0))
+				Expect(respFind.Response.Code).To(Equal(scerr.ErrServiceNotExists))
 
 				By("shared service discovery")
 				os.Setenv("CSE_SHARED_SERVICES", "query_instance_shared_provider")
diff --git a/server/service/microservice.go b/server/service/microservice.go
index 2136f8c..b504648 100644
--- a/server/service/microservice.go
+++ b/server/service/microservice.go
@@ -189,7 +189,7 @@ func (s *MicroServiceService) CreateServicePri(ctx context.Context, in *pb.Creat
 
 func checkQuota(ctx context.Context, domainProject string) (quota.QuotaReporter, *scerr.Error) {
 	if core.IsSCInstance(ctx) {
-		util.Logger().Infof("it is service-center")
+		util.Logger().Debugf("service-center self register")
 		return nil, nil
 	}
 	res := quota.NewApplyQuotaResource(quota.MicroServiceQuotaType, domainProject, "", 1)
diff --git a/server/service/notification/common.go b/server/service/notification/common.go
index 7e2e16a..7b0728c 100644
--- a/server/service/notification/common.go
+++ b/server/service/notification/common.go
@@ -17,17 +17,17 @@
 package notification
 
 import (
-	"fmt"
 	"strconv"
 	"time"
 )
 
 const (
-	DEFAULT_MAX_QUEUE          = 1000
-	DEFAULT_INIT_SUBSCRIBERS   = 1000
-	DEFAULT_ON_MESSAGE_TIMEOUT = 100 * time.Millisecond
-	DEFAULT_TIMEOUT            = 30 * time.Second
+	DEFAULT_MAX_QUEUE       = 1000
+	DEFAULT_ADD_JOB_TIMEOUT = 1 * time.Second
+	DEFAULT_SEND_TIMEOUT    = 30 * time.Second
+)
 
+const (
 	NOTIFTY NotifyType = iota
 	INSTANCE
 	typeEnd
@@ -42,13 +42,21 @@ func (nt NotifyType) String() string {
 	return "NotifyType" + strconv.Itoa(int(nt))
 }
 
-type NotifyServiceConfig struct {
-	AddTimeout    time.Duration
-	NotifyTimeout time.Duration
-	MaxQueue      int64
+func (nt NotifyType) QueueSize() (s int) {
+	if int(nt) < len(notifyTypeQueues) {
+		s = notifyTypeQueues[nt]
+	}
+	if s <= 0 {
+		s = DEFAULT_MAX_QUEUE
+	}
+	return
+}
+
+var notifyTypeNames = []string{
+	NOTIFTY:  "NOTIFTY",
+	INSTANCE: "INSTANCE",
 }
 
-func (nsc NotifyServiceConfig) String() string {
-	return fmt.Sprintf("{acceptQueue: %d, accept: %s, notify: %s}",
-		nsc.MaxQueue, nsc.AddTimeout, nsc.NotifyTimeout)
+var notifyTypeQueues = []int{
+	INSTANCE: 100 * 1000,
 }
diff --git a/server/service/util/tag_util_test.go b/server/service/notification/group.go
similarity index 51%
copy from server/service/util/tag_util_test.go
copy to server/service/notification/group.go
index 20fa7d5..679acfd 100644
--- a/server/service/util/tag_util_test.go
+++ b/server/service/notification/group.go
@@ -14,29 +14,51 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package util
+package notification
 
 import (
 	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
-	"golang.org/x/net/context"
-	"testing"
 )
 
-func TestAddTagIntoETCD(t *testing.T) {
-	err := AddTagIntoETCD(context.Background(), "", "", map[string]string{"a": "1"})
-	if err == nil {
-		t.Fatalf(`AddTagIntoETCD with {"a": "1"} tags failed`)
-	}
+type Group struct {
+	name        string
+	subscribers *util.ConcurrentMap
+}
+
+func (g *Group) Name() string {
+	return g.name
 }
 
-func TestGetTagsUtils(t *testing.T) {
-	_, err := GetTagsUtils(util.SetContext(context.Background(), CTX_CACHEONLY, "1"), "", "")
-	if err != nil {
-		t.Fatalf(`GetTagsUtils WithCacheOnly failed`)
+func (g *Group) Notify(job NotifyJob) {
+	g.subscribers.ForEach(func(item util.MapItem) (next bool) {
+		item.Value.(Subscriber).OnMessage(job)
+		return true
+	})
+}
+
+func (g *Group) Subscribers(name string) Subscriber {
+	s, ok := g.subscribers.Get(name)
+	if !ok {
+		return nil
 	}
+	return s.(Subscriber)
+}
+
+func (g *Group) AddSubscriber(subscriber Subscriber) Subscriber {
+	return g.subscribers.PutIfAbsent(subscriber.Id(), subscriber).(Subscriber)
+}
+
+func (g *Group) Remove(name string) {
+	g.subscribers.Remove(name)
+}
+
+func (g *Group) Size() int {
+	return g.subscribers.Size()
+}
 
-	_, err = GetTagsUtils(context.Background(), "", "")
-	if err == nil {
-		t.Fatalf(`GetTagsUtils failed`)
+func NewGroup(name string) *Group {
+	return &Group{
+		name:        name,
+		subscribers: util.NewConcurrentMap(0),
 	}
 }
diff --git a/server/service/notification/group_test.go b/server/service/notification/group_test.go
new file mode 100644
index 0000000..b241eb4
--- /dev/null
+++ b/server/service/notification/group_test.go
@@ -0,0 +1,69 @@
+/*
+ * 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 notification
+
+import "testing"
+
+type mockSubscriber struct {
+	*BaseSubscriber
+	job NotifyJob
+}
+
+func (s *mockSubscriber) OnMessage(job NotifyJob) {
+	s.job = job
+}
+
+func TestGroup_Add(t *testing.T) {
+	m := NewSubscriber(INSTANCE, "s1", "g1")
+	g := NewGroup("g1")
+	if g.Name() != "g1" {
+		t.Fatalf("TestGroup_Add failed")
+	}
+	if g.AddSubscriber(m) != m {
+		t.Fatalf("TestGroup_Add failed")
+	}
+	if g.AddSubscriber(NewSubscriber(INSTANCE, "s1", "g1")) == m {
+		t.Fatalf("TestGroup_Add failed")
+	}
+	same := *m
+	if g.AddSubscriber(&same) != m {
+		t.Fatalf("TestGroup_Add failed")
+	}
+	if g.Size() != 2 {
+		t.Fatalf("TestGroup_Add failed")
+	}
+	g.Remove(m.Id())
+	if g.Size() != 1 {
+		t.Fatalf("TestGroup_Add failed")
+	}
+	if g.Subscribers(m.Id()) == m {
+		t.Fatalf("TestGroup_Add failed")
+	}
+
+	mock := &mockSubscriber{BaseSubscriber: NewSubscriber(INSTANCE, "s1", "g1")}
+	if g.AddSubscriber(mock) != mock {
+		t.Fatalf("TestGroup_Add failed")
+	}
+	if g.Subscribers(mock.Id()) != mock {
+		t.Fatalf("TestGroup_Add failed")
+	}
+	job := &BaseNotifyJob{nType: INSTANCE}
+	g.Notify(job)
+	if mock.job != job {
+		t.Fatalf("TestGroup_Add failed")
+	}
+}
diff --git a/server/service/notification/listwatcher.go b/server/service/notification/listwatcher.go
index a6942c9..0877b27 100644
--- a/server/service/notification/listwatcher.go
+++ b/server/service/notification/listwatcher.go
@@ -25,26 +25,30 @@ import (
 
 // 状态变化推送
 type WatchJob struct {
-	BaseNotifyJob
+	*BaseNotifyJob
 	Revision int64
 	Response *pb.WatchInstanceResponse
 }
 
 type ListWatcher struct {
-	BaseSubscriber
+	*BaseSubscriber
 	Job          chan *WatchJob
 	ListRevision int64
 	ListFunc     func() (results []*pb.WatchInstanceResponse, rev int64)
+	listCh       chan struct{}
+}
 
-	listCh chan struct{}
+func (s *ListWatcher) SetError(err error) {
+	s.BaseSubscriber.SetError(err)
+	// 触发清理job
+	s.Service().AddJob(NewNotifyServiceHealthCheckJob(s))
 }
 
 func (w *ListWatcher) OnAccept() {
 	if w.Err() != nil {
 		return
 	}
-
-	util.Logger().Debugf("accepted by notify service, %s watcher %s %s", w.Type(), w.Id(), w.Subject())
+	util.Logger().Debugf("accepted by notify service, %s watcher %s %s", w.Type(), w.Group(), w.Subject())
 	util.Go(w.listAndPublishJobs)
 }
 
@@ -56,7 +60,7 @@ func (w *ListWatcher) listAndPublishJobs(_ context.Context) {
 	results, rev := w.ListFunc()
 	w.ListRevision = rev
 	for _, response := range results {
-		w.sendMessage(NewWatchJob(w.Id(), w.Subject(), w.ListRevision, response))
+		w.sendMessage(NewWatchJob(w.Group(), w.Subject(), w.ListRevision, response))
 	}
 }
 
@@ -71,68 +75,73 @@ func (w *ListWatcher) OnMessage(job NotifyJob) {
 		return
 	}
 
-	timer := time.NewTimer(DEFAULT_ON_MESSAGE_TIMEOUT)
 	select {
 	case <-w.listCh:
-		timer.Stop()
-	case <-timer.C:
-		util.Logger().Errorf(nil,
-			"the %s listwatcher %s %s is not ready[over %s], drop the event %v",
-			w.Type(), w.Id(), w.Subject(), DEFAULT_ON_MESSAGE_TIMEOUT, job)
-		return
+	default:
+		timer := time.NewTimer(w.Timeout())
+		select {
+		case <-w.listCh:
+			timer.Stop()
+		case <-timer.C:
+			util.Logger().Errorf(nil,
+				"the %s listwatcher %s %s is not ready[over %s], send the event %v",
+				w.Type(), w.Group(), w.Subject(), w.Timeout(), job)
+		}
 	}
 
 	if wJob.Revision <= w.ListRevision {
 		util.Logger().Warnf(nil,
 			"unexpected notify %s job is coming in, watcher %s %s, job is %v, current revision is %v",
-			w.Type(), w.Id(), w.Subject(), job, w.ListRevision)
+			w.Type(), w.Group(), w.Subject(), job, w.ListRevision)
 		return
 	}
 	w.sendMessage(wJob)
 }
 
 func (w *ListWatcher) sendMessage(job *WatchJob) {
-	util.Logger().Debugf("start to notify %s watcher %s %s, job is %v, current revision is %v", w.Type(),
-		w.Id(), w.Subject(), job, w.ListRevision)
 	defer util.RecoverAndReport()
-	timer := time.NewTimer(DEFAULT_ON_MESSAGE_TIMEOUT)
 	select {
 	case w.Job <- job:
-		timer.Stop()
-	case <-timer.C:
-		util.Logger().Errorf(nil,
-			"the %s watcher %s %s event queue is full[over %s], drop the event %v",
-			w.Type(), w.Id(), w.Subject(), DEFAULT_ON_MESSAGE_TIMEOUT, job)
+	default:
+		timer := time.NewTimer(w.Timeout())
+		select {
+		case w.Job <- job:
+			timer.Stop()
+		case <-timer.C:
+			util.Logger().Errorf(nil,
+				"the %s watcher %s %s event queue is full[over %s], drop the event %v",
+				w.Type(), w.Group(), w.Subject(), w.Timeout(), job)
+		}
 	}
 }
 
+func (w *ListWatcher) Timeout() time.Duration {
+	return DEFAULT_ADD_JOB_TIMEOUT
+}
+
 func (w *ListWatcher) Close() {
 	close(w.Job)
 }
 
-func NewWatchJob(subscriberId, subject string, rev int64, response *pb.WatchInstanceResponse) *WatchJob {
+func NewWatchJob(group, subject string, rev int64, response *pb.WatchInstanceResponse) *WatchJob {
 	return &WatchJob{
-		BaseNotifyJob: BaseNotifyJob{
-			subscriberId: subscriberId,
-			subject:      subject,
-			nType:        INSTANCE,
+		BaseNotifyJob: &BaseNotifyJob{
+			group:   group,
+			subject: subject,
+			nType:   INSTANCE,
 		},
 		Revision: rev,
 		Response: response,
 	}
 }
 
-func NewListWatcher(id string, subject string,
+func NewListWatcher(group string, subject string,
 	listFunc func() (results []*pb.WatchInstanceResponse, rev int64)) *ListWatcher {
 	watcher := &ListWatcher{
-		BaseSubscriber: BaseSubscriber{
-			id:      id,
-			subject: subject,
-			nType:   INSTANCE,
-		},
-		Job:      make(chan *WatchJob, DEFAULT_MAX_QUEUE),
-		ListFunc: listFunc,
-		listCh:   make(chan struct{}),
+		BaseSubscriber: NewSubscriber(INSTANCE, subject, group),
+		Job:            make(chan *WatchJob, DEFAULT_MAX_QUEUE),
+		ListFunc:       listFunc,
+		listCh:         make(chan struct{}),
 	}
 	return watcher
 }
diff --git a/server/service/notification/notice.go b/server/service/notification/notice.go
index 498d435..26d5235 100644
--- a/server/service/notification/notice.go
+++ b/server/service/notification/notice.go
@@ -17,25 +17,25 @@
 package notification
 
 type NotifyJob interface {
-	SubscriberId() string
-	Subject() string
 	Type() NotifyType
+	Group() string
+	Subject() string
 }
 
 type BaseNotifyJob struct {
-	subscriberId string
-	subject      string
-	nType        NotifyType
+	nType   NotifyType
+	subject string
+	group   string
 }
 
-func (s *BaseNotifyJob) SubscriberId() string {
-	return s.subscriberId
+func (s *BaseNotifyJob) Type() NotifyType {
+	return s.nType
 }
 
-func (s *BaseNotifyJob) Subject() string {
-	return s.subject
+func (s *BaseNotifyJob) Group() string {
+	return s.group
 }
 
-func (s *BaseNotifyJob) Type() NotifyType {
-	return s.nType
+func (s *BaseNotifyJob) Subject() string {
+	return s.subject
 }
diff --git a/server/service/notification/notification_healthchecker.go b/server/service/notification/notification_healthchecker.go
index 310275b..597713c 100644
--- a/server/service/notification/notification_healthchecker.go
+++ b/server/service/notification/notification_healthchecker.go
@@ -29,7 +29,7 @@ type NotifyServiceHealthChecker struct {
 }
 
 type NotifyServiceHealthCheckJob struct {
-	BaseNotifyJob
+	*BaseNotifyJob
 	ErrorSubscriber Subscriber
 }
 
@@ -39,19 +39,19 @@ func (s *NotifyServiceHealthChecker) OnMessage(job NotifyJob) {
 
 	if j.ErrorSubscriber.Type() == NOTIFTY {
 		util.Logger().Errorf(nil, "remove %s watcher %s %s failed, here cause a dead lock",
-			j.ErrorSubscriber.Type(), j.ErrorSubscriber.Subject(), j.ErrorSubscriber.Id())
+			j.ErrorSubscriber.Type(), j.ErrorSubscriber.Subject(), j.ErrorSubscriber.Group())
 		return
 	}
 
-	util.Logger().Warnf(err, "notification service remove %s watcher %s %s",
-		j.ErrorSubscriber.Type(), j.ErrorSubscriber.Subject(), j.ErrorSubscriber.Id())
+	util.Logger().Debugf("notification service remove %s watcher, error: %s, subject: %s, group: %s",
+		j.ErrorSubscriber.Type(), err.Error(), j.ErrorSubscriber.Subject(), j.ErrorSubscriber.Group())
 	s.Service().RemoveSubscriber(j.ErrorSubscriber)
 }
 
 func NewNotifyServiceHealthChecker() *NotifyServiceHealthChecker {
 	return &NotifyServiceHealthChecker{
 		BaseSubscriber: BaseSubscriber{
-			id:      NOTIFY_SERVER_CHECKER_NAME,
+			group:   NOTIFY_SERVER_CHECKER_NAME,
 			subject: NOTIFY_SERVER_CHECK_SUBJECT,
 			nType:   NOTIFTY,
 		},
@@ -60,10 +60,10 @@ func NewNotifyServiceHealthChecker() *NotifyServiceHealthChecker {
 
 func NewNotifyServiceHealthCheckJob(s Subscriber) *NotifyServiceHealthCheckJob {
 	return &NotifyServiceHealthCheckJob{
-		BaseNotifyJob: BaseNotifyJob{
-			subscriberId: NOTIFY_SERVER_CHECKER_NAME,
-			subject:      NOTIFY_SERVER_CHECK_SUBJECT,
-			nType:        NOTIFTY,
+		BaseNotifyJob: &BaseNotifyJob{
+			group:   NOTIFY_SERVER_CHECKER_NAME,
+			subject: NOTIFY_SERVER_CHECK_SUBJECT,
+			nType:   NOTIFTY,
 		},
 		ErrorSubscriber: s,
 	}
diff --git a/server/service/notification/notification_service.go b/server/service/notification/notification_service.go
index 82cc9b1..a5ea4ea 100644
--- a/server/service/notification/notification_service.go
+++ b/server/service/notification/notification_service.go
@@ -17,19 +17,12 @@
 package notification
 
 import (
-	"container/list"
 	"errors"
 	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
 	"golang.org/x/net/context"
 	"sync"
-	"time"
 )
 
-var notifyTypeNames = []string{
-	NOTIFTY:  "NOTIFTY",
-	INSTANCE: "INSTANCE",
-}
-
 var notifyService *NotifyService
 
 func init() {
@@ -39,102 +32,78 @@ func init() {
 	}
 }
 
-type subscriberIndex map[string]*list.List
-
-type subscriberSubjectIndex map[string]subscriberIndex
-
-type serviceIndex map[NotifyType]subscriberSubjectIndex
-
 type NotifyService struct {
-	Config NotifyServiceConfig
-
-	services  serviceIndex
-	queues    map[NotifyType]chan NotifyJob
-	waits     sync.WaitGroup
-	mutexes   map[NotifyType]*sync.Mutex
-	err       chan error
-	closeMux  sync.RWMutex
-	isClose   bool
-	goroutine *util.GoRoutine
+	processors *util.ConcurrentMap
+	goroutine  *util.GoRoutine
+	err        chan error
+	closeMux   sync.RWMutex
+	isClose    bool
 }
 
 func (s *NotifyService) Err() <-chan error {
 	return s.err
 }
 
-func (s *NotifyService) AddSubscriber(n Subscriber) error {
-	if s.Closed() {
-		return errors.New("server is shutting down")
+func (s *NotifyService) init() {
+	s.processors = util.NewConcurrentMap(int(typeEnd))
+	s.err = make(chan error, 1)
+	for i := NotifyType(0); i != typeEnd; i++ {
+		s.processors.Put(i, NewProcessor(i.String(), i.QueueSize()))
 	}
+}
 
-	s.mutexes[n.Type()].Lock()
-	ss, ok := s.services[n.Type()]
-	if !ok {
-		s.mutexes[n.Type()].Unlock()
-		return errors.New("Unknown subscribe type")
+func (s *NotifyService) Start() {
+	if !s.Closed() {
+		util.Logger().Warnf(nil, "notify service is already running")
+		return
 	}
+	s.closeMux.Lock()
+	s.isClose = false
+	s.closeMux.Unlock()
 
-	sr, ok := ss[n.Subject()]
-	if !ok {
-		sr = make(subscriberIndex, DEFAULT_INIT_SUBSCRIBERS)
-		ss[n.Subject()] = sr // add a subscriber
+	s.init()
+	// 错误subscriber清理
+	s.AddSubscriber(NewNotifyServiceHealthChecker())
+
+	util.Logger().Debugf("notify service is started")
+
+	s.processors.ForEach(func(item util.MapItem) (next bool) {
+		s.goroutine.Do(item.Value.(*Processor).Do)
+		return true
+	})
+}
+
+func (s *NotifyService) AddSubscriber(n Subscriber) error {
+	if s.Closed() {
+		return errors.New("server is shutting down")
 	}
 
-	ns, ok := sr[n.Id()]
+	itf, ok := s.processors.Get(n.Type())
 	if !ok {
-		ns = list.New()
+		return errors.New("Unknown subscribe type")
 	}
-	ns.PushBack(n) // add a connection
-	sr[n.Id()] = ns
-
 	n.SetService(s)
-	s.mutexes[n.Type()].Unlock()
-
 	n.OnAccept()
+
+	itf.(*Processor).AddSubscriber(n)
 	return nil
 }
 
 func (s *NotifyService) RemoveSubscriber(n Subscriber) {
-	s.mutexes[n.Type()].Lock()
-	defer s.mutexes[n.Type()].Unlock()
-	ss, ok := s.services[n.Type()]
-	if !ok {
-		return
-	}
-
-	m, ok := ss[n.Subject()]
-	if !ok {
-		return
-	}
-
-	ns, ok := m[n.Id()]
+	itf, ok := s.processors.Get(n.Type())
 	if !ok {
 		return
 	}
 
-	for sr := ns.Front(); sr != nil; sr = sr.Next() {
-		if sr.Value == n {
-			ns.Remove(sr)
-			n.Close()
-			break
-		}
-	}
+	itf.(*Processor).Remove(n)
+	n.Close()
 }
 
 func (s *NotifyService) RemoveAllSubscribers() {
-	for t, ss := range s.services {
-		s.mutexes[t].Lock()
-		for _, subscribers := range ss {
-			for _, ns := range subscribers {
-				for e, n := ns.Front(), ns.Front(); e != nil; e = n {
-					e.Value.(Subscriber).Close()
-					n = e.Next()
-					ns.Remove(e)
-				}
-			}
-		}
-		s.mutexes[t].Unlock()
-	}
+	s.processors.ForEach(func(item util.MapItem) (next bool) {
+		item.Value.(*Processor).Clear()
+		return true
+	})
 }
 
 //通知内容塞到队列里
@@ -143,109 +112,12 @@ func (s *NotifyService) AddJob(job NotifyJob) error {
 		return errors.New("add notify job failed for server shutdown")
 	}
 
-	defer util.RecoverAndReport()
-
-	timer := time.NewTimer(s.Config.AddTimeout)
-	select {
-	case s.queues[job.Type()] <- job:
-		timer.Stop()
-		return nil
-	case <-timer.C:
-		util.Logger().Errorf(nil, "Add job failed.%s")
-		return errors.New("add notify job timeout")
-	}
-}
-
-func (s *NotifyService) getPublish2SubscriberFunc(t NotifyType) func(context.Context) {
-	return func(ctx context.Context) {
-		defer s.waits.Done()
-		for {
-			select {
-			case <-ctx.Done():
-				return
-			case job, ok := <-s.queues[t]:
-				if !ok {
-					return
-				}
-
-				util.Logger().Infof("notification service got a job %s: %s to notify subscriber %s",
-					job.Type(), job.Subject(), job.SubscriberId())
-
-				s.mutexes[t].Lock()
-
-				if s.Closed() && len(s.services[t]) == 0 {
-					s.mutexes[t].Unlock()
-					return
-				}
-
-				m, ok := s.services[t][job.Subject()]
-				if ok {
-					// publish的subject如果带上id,则单播,否则广播
-					if len(job.SubscriberId()) != 0 {
-						ns, ok := m[job.SubscriberId()]
-						if ok {
-							for n := ns.Front(); n != nil; n = n.Next() {
-								n.Value.(Subscriber).OnMessage(job)
-							}
-						}
-						s.mutexes[t].Unlock()
-						continue
-					}
-					for key := range m {
-						ns := m[key]
-						for n := ns.Front(); n != nil; n = n.Next() {
-							n.Value.(Subscriber).OnMessage(job)
-						}
-					}
-				}
-
-				s.mutexes[t].Unlock()
-			}
-		}
-	}
-}
-
-func (s *NotifyService) init() {
-	if s.Config.AddTimeout <= 0 {
-		s.Config.AddTimeout = DEFAULT_TIMEOUT
-	}
-	if s.Config.NotifyTimeout <= 0 {
-		s.Config.NotifyTimeout = DEFAULT_TIMEOUT
-	}
-	if s.Config.MaxQueue <= 0 || s.Config.MaxQueue > DEFAULT_MAX_QUEUE {
-		s.Config.MaxQueue = DEFAULT_MAX_QUEUE
-	}
-
-	s.services = make(serviceIndex, typeEnd)
-	s.err = make(chan error, 1)
-	s.queues = make(map[NotifyType]chan NotifyJob, typeEnd)
-	s.mutexes = make(map[NotifyType]*sync.Mutex, typeEnd)
-	for i := NotifyType(0); i != typeEnd; i++ {
-		s.services[i] = make(subscriberSubjectIndex, DEFAULT_INIT_SUBSCRIBERS)
-		s.queues[i] = make(chan NotifyJob, s.Config.MaxQueue)
-		s.mutexes[i] = &sync.Mutex{}
-		s.waits.Add(1)
-	}
-}
-
-func (s *NotifyService) Start() {
-	if !s.Closed() {
-		util.Logger().Warnf(nil, "notify service is already running with config %s", s.Config)
-		return
-	}
-	s.closeMux.Lock()
-	s.isClose = false
-	s.closeMux.Unlock()
-
-	s.init()
-	// 错误subscriber清理
-	s.AddSubscriber(NewNotifyServiceHealthChecker())
-
-	util.Logger().Debugf("notify service is started with config %s", s.Config)
-
-	for i := NotifyType(0); i != typeEnd; i++ {
-		s.goroutine.Do(s.getPublish2SubscriberFunc(i))
+	itf, ok := s.processors.Get(job.Type())
+	if !ok {
+		return errors.New("Unknown job type")
 	}
+	itf.(*Processor).Accept(job)
+	return nil
 }
 
 func (s *NotifyService) Closed() (b bool) {
@@ -263,17 +135,12 @@ func (s *NotifyService) Stop() {
 	s.isClose = true
 	s.closeMux.Unlock()
 
-	for _, c := range s.queues {
-		close(c)
-	}
-	s.waits.Wait()
+	s.goroutine.Close(true)
 
 	s.RemoveAllSubscribers()
 
 	close(s.err)
 
-	s.goroutine.Close(true)
-
 	util.Logger().Debug("notify service stopped.")
 }
 
diff --git a/server/service/notification/notification_test.go b/server/service/notification/notification_test.go
new file mode 100644
index 0000000..d7da51e
--- /dev/null
+++ b/server/service/notification/notification_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 notification
+
+import (
+	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
+	"golang.org/x/net/context"
+	"testing"
+	"time"
+)
+
+func TestGetNotifyService(t *testing.T) {
+	notifyService := &NotifyService{
+		isClose:   true,
+		goroutine: util.NewGo(context.Background()),
+	}
+	if notifyService == nil {
+		t.Fatalf("TestGetNotifyService failed")
+	}
+	if notifyService.Closed() != true {
+		t.Fatalf("TestGetNotifyService failed")
+	}
+
+	err := notifyService.AddSubscriber(nil)
+	if err == nil {
+		t.Fatalf("TestGetNotifyService failed")
+	}
+	err = notifyService.AddJob(nil)
+	if err == nil {
+		t.Fatalf("TestGetNotifyService failed")
+	}
+
+	notifyService.Start()
+	notifyService.Start()
+	if notifyService.Closed() != false {
+		t.Fatalf("TestGetNotifyService failed")
+	}
+	select {
+	case <-notifyService.Err():
+		t.Fatalf("TestGetNotifyService failed")
+	default:
+	}
+
+	s := NewSubscriber(-1, "s", "g")
+	err = notifyService.AddSubscriber(s)
+	if err == nil {
+		t.Fatalf("TestGetNotifyService failed")
+	}
+	s = NewSubscriber(INSTANCE, "s", "g")
+	err = notifyService.AddSubscriber(s)
+	if err != nil {
+		t.Fatalf("TestGetNotifyService failed")
+	}
+	j := &BaseNotifyJob{INSTANCE, "s", "g"}
+	err = notifyService.AddJob(j)
+	if err != nil {
+		t.Fatalf("TestGetNotifyService failed")
+	}
+	err = notifyService.AddJob(NewNotifyServiceHealthCheckJob(NewNotifyServiceHealthChecker()))
+	if err != nil {
+		t.Fatalf("TestGetNotifyService failed")
+	}
+	<-time.After(time.Second)
+	err = notifyService.AddJob(NewNotifyServiceHealthCheckJob(s))
+	if err != nil {
+		t.Fatalf("TestGetNotifyService failed")
+	}
+	notifyService.Stop()
+	if notifyService.Closed() != true {
+		t.Fatalf("TestGetNotifyService failed")
+	}
+}
diff --git a/server/service/notification/processor.go b/server/service/notification/processor.go
new file mode 100644
index 0000000..7fe27f1
--- /dev/null
+++ b/server/service/notification/processor.go
@@ -0,0 +1,106 @@
+/*
+ * 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 notification
+
+import (
+	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
+	"golang.org/x/net/context"
+)
+
+type Processor struct {
+	name     string
+	subjects *util.ConcurrentMap
+	queue    chan NotifyJob
+}
+
+func (p *Processor) Name() string {
+	return p.name
+}
+
+func (p *Processor) Notify(job NotifyJob) {
+	if itf, ok := p.subjects.Get(job.Subject()); ok {
+		itf.(*Subject).Notify(job)
+	}
+}
+
+func (p *Processor) Subjects(name string) *Subject {
+	itf, ok := p.subjects.Get(name)
+	if !ok {
+		return nil
+	}
+	return itf.(*Subject)
+}
+
+func (p *Processor) AddSubscriber(n Subscriber) {
+	item, _ := p.subjects.Fetch(n.Subject(), func() (interface{}, error) {
+		return NewSubject(n.Subject()), nil
+	})
+	item.(*Subject).GetOrNewGroup(n.Group()).AddSubscriber(n)
+}
+
+func (p *Processor) Remove(n Subscriber) {
+	itf, ok := p.subjects.Get(n.Subject())
+	if !ok {
+		return
+	}
+
+	s := itf.(*Subject)
+	g := s.Groups(n.Group())
+	if g == nil {
+		return
+	}
+
+	g.Remove(n.Id())
+
+	if g.Size() == 0 {
+		s.Remove(g.Name())
+	}
+	if s.Size() == 0 {
+		p.subjects.Remove(s.Name())
+	}
+}
+
+func (p *Processor) Clear() {
+	p.subjects.Clear()
+}
+
+func (p *Processor) Accept(job NotifyJob) {
+	defer util.RecoverAndReport()
+	p.queue <- job
+}
+
+func (p *Processor) Do(ctx context.Context) {
+	for {
+		select {
+		case <-ctx.Done():
+			return
+		case job, ok := <-p.queue:
+			if !ok {
+				return
+			}
+			p.Notify(job)
+		}
+	}
+}
+
+func NewProcessor(name string, queue int) *Processor {
+	return &Processor{
+		name:     name,
+		subjects: util.NewConcurrentMap(0),
+		queue:    make(chan NotifyJob, queue),
+	}
+}
diff --git a/server/service/notification/processor_test.go b/server/service/notification/processor_test.go
new file mode 100644
index 0000000..da4bd86
--- /dev/null
+++ b/server/service/notification/processor_test.go
@@ -0,0 +1,74 @@
+/*
+ * 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 notification
+
+import (
+	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
+	"testing"
+)
+
+func TestProcessor_Do(t *testing.T) {
+	mock1 := &mockSubscriber{BaseSubscriber: NewSubscriber(INSTANCE, "s1", "g1")}
+	mock2 := &mockSubscriber{BaseSubscriber: NewSubscriber(INSTANCE, "s1", "g2")}
+	p := NewProcessor("p1", 0)
+	util.Go(p.Do)
+	if p.Name() != "p1" {
+		t.Fatalf("TestProcessor_Do")
+	}
+	if p.Subjects(mock1.Subject()) != nil {
+		t.Fatalf("TestProcessor_Do")
+	}
+	p.AddSubscriber(mock1)
+	if p.Subjects(mock1.Subject()).Groups(mock1.Group()).Subscribers(mock1.Id()) != mock1 {
+		t.Fatalf("TestProcessor_Do")
+	}
+	p.Remove(NewSubscriber(INSTANCE, "s2", "g1"))
+	p.Remove(NewSubscriber(INSTANCE, "s1", "g2"))
+	p.Remove(mock1)
+	if p.Subjects(mock1.Subject()) != nil {
+		t.Fatalf("TestProcessor_Do")
+	}
+	p.AddSubscriber(mock1)
+	p.Clear()
+	if p.Subjects(mock1.Subject()) != nil {
+		t.Fatalf("TestProcessor_Do")
+	}
+	p.AddSubscriber(mock1)
+	job := &BaseNotifyJob{group: "g1"}
+	p.Accept(job)
+	if mock1.job != nil {
+		t.Fatalf("TestProcessor_Do")
+	}
+	job.subject = "s1"
+	job.group = "g3"
+	p.Accept(job)
+	if mock1.job != nil {
+		t.Fatalf("TestProcessor_Do")
+	}
+	job.subject = "s1"
+	job.group = "g1"
+	p.Accept(job)
+	if mock1.job != job || mock2.job != nil {
+		t.Fatalf("TestProcessor_Do")
+	}
+	job.subject = "s1"
+	job.group = ""
+	p.Accept(job)
+	if mock1.job != job && mock2.job != job {
+		t.Fatalf("TestProcessor_Do")
+	}
+}
diff --git a/server/service/notification/publisher.go b/server/service/notification/publisher.go
new file mode 100644
index 0000000..17bab45
--- /dev/null
+++ b/server/service/notification/publisher.go
@@ -0,0 +1,103 @@
+/*
+ * 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 notification
+
+import (
+	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
+	"golang.org/x/net/context"
+	"sync"
+	"time"
+)
+
+var publisher *Publisher
+
+func init() {
+	publisher = NewPublisher()
+	publisher.Run()
+}
+
+type Publisher struct {
+	wss       []*WebSocket
+	lock      sync.Mutex
+	goroutine *util.GoRoutine
+}
+
+func (wh *Publisher) Run() {
+	util.Go(publisher.loop)
+}
+
+func (wh *Publisher) Stop() {
+	wh.goroutine.Close(true)
+}
+
+func (wh *Publisher) dispatch(ws *WebSocket, payload interface{}) {
+	wh.goroutine.Do(func(ctx context.Context) {
+		ws.HandleWatchWebSocketJob(payload)
+	})
+}
+
+func (wh *Publisher) loop(ctx context.Context) {
+	defer wh.Stop()
+	ticker := time.NewTicker(500 * time.Millisecond)
+	for {
+		select {
+		case <-ctx.Done():
+			// server shutdown
+			return
+		case <-ticker.C:
+			var removes []int
+			for i, ws := range wh.wss {
+				if payload := ws.Pick(); payload != nil {
+					if _, ok := payload.(error); ok {
+						removes = append(removes, i)
+					}
+					wh.dispatch(ws, payload)
+				}
+			}
+			if len(removes) == 0 {
+				continue
+			}
+
+			wh.lock.Lock()
+			var (
+				news []*WebSocket
+				s    int
+			)
+			for _, e := range removes {
+				news = append(news, wh.wss[s:e]...)
+				s = e + 1
+			}
+			if s < len(wh.wss) {
+				news = append(news, wh.wss[s:]...)
+			}
+			wh.wss = news
+			wh.lock.Unlock()
+		}
+	}
+}
+
+func (wh *Publisher) Accept(ws *WebSocket) {
+	wh.lock.Lock()
+	wh.wss = append(wh.wss, ws)
+	wh.lock.Unlock()
+}
+
+func NewPublisher() *Publisher {
+	return &Publisher{
+		goroutine: util.NewGo(context.Background()),
+	}
+}
diff --git a/server/service/notification/stream.go b/server/service/notification/stream.go
index 101473d..b9bbea2 100644
--- a/server/service/notification/stream.go
+++ b/server/service/notification/stream.go
@@ -23,32 +23,35 @@ import (
 	"time"
 )
 
-func HandleWatchJob(watcher *ListWatcher, stream pb.ServiceInstanceCtrl_WatchServer, timeout time.Duration) (err error) {
+func HandleWatchJob(watcher *ListWatcher, stream pb.ServiceInstanceCtrl_WatchServer) (err error) {
+	timer := time.NewTimer(DEFAULT_SEND_TIMEOUT)
+	defer timer.Stop()
 	for {
-		timer := time.NewTimer(timeout)
 		select {
 		case <-timer.C:
+			timer.Reset(DEFAULT_SEND_TIMEOUT)
+
 			// TODO grpc 长连接心跳?
 		case job := <-watcher.Job:
-			timer.Stop()
-
 			if job == nil {
 				err = errors.New("channel is closed")
-				util.Logger().Errorf(err, "watcher %s %s caught an exception",
-					watcher.Subject(), watcher.Id())
+				util.Logger().Errorf(err, "watcher caught an exception, subject: %s, group: %s",
+					watcher.Subject(), watcher.Group())
 				return
 			}
 			resp := job.Response
-			util.Logger().Infof("event is coming in, watcher %s %s",
-				watcher.Subject(), watcher.Id())
+			util.Logger().Infof("event is coming in, watcher, subject: %s, group: %s",
+				watcher.Subject(), watcher.Group())
 
 			err = stream.Send(resp)
 			if err != nil {
-				util.Logger().Errorf(err, "send message error, watcher %s %s",
-					watcher.Subject(), watcher.Id())
+				util.Logger().Errorf(err, "send message error, subject: %s, group: %s",
+					watcher.Subject(), watcher.Group())
 				watcher.SetError(err)
 				return
 			}
+
+			util.ResetTimer(timer, DEFAULT_SEND_TIMEOUT)
 		}
 	}
 }
diff --git a/server/service/util/schema_util_test.go b/server/service/notification/stream_test.go
similarity index 70%
copy from server/service/util/schema_util_test.go
copy to server/service/notification/stream_test.go
index 7ba7146..5193045 100644
--- a/server/service/util/schema_util_test.go
+++ b/server/service/notification/stream_test.go
@@ -14,22 +14,21 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package util
+package notification
 
-import (
-	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
-	"golang.org/x/net/context"
-	"testing"
-)
+import "testing"
 
-func TestCheckSchemaInfoExist(t *testing.T) {
-	_, err := CheckSchemaInfoExist(util.SetContext(context.Background(), CTX_CACHEONLY, "1"), "")
-	if err != nil {
-		t.FailNow()
-	}
-
-	_, err = CheckSchemaInfoExist(context.Background(), "")
+func TestHandleWatchJob(t *testing.T) {
+	defer func() { recover() }()
+	w := NewListWatcher("g", "s", nil)
+	w.Job <- nil
+	err := HandleWatchJob(w, nil)
 	if err == nil {
-		t.FailNow()
+		t.Fatalf("TestHandleWatchJob failed")
+	}
+	w.Job <- NewWatchJob("g", "s", 1, nil)
+	err = HandleWatchJob(w, nil)
+	if err != nil {
+		t.Fatalf("TestHandleWatchJob failed")
 	}
 }
diff --git a/server/service/notification/subject.go b/server/service/notification/subject.go
new file mode 100644
index 0000000..2e2ce57
--- /dev/null
+++ b/server/service/notification/subject.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 notification
+
+import (
+	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
+)
+
+type Subject struct {
+	name   string
+	groups *util.ConcurrentMap
+}
+
+func (s *Subject) Name() string {
+	return s.name
+}
+
+func (s *Subject) Notify(job NotifyJob) {
+	if len(job.Group()) == 0 {
+		s.groups.ForEach(func(item util.MapItem) (next bool) {
+			item.Value.(*Group).Notify(job)
+			return true
+		})
+		return
+	}
+
+	itf, ok := s.groups.Get(job.Group())
+	if !ok {
+		return
+	}
+	itf.(*Group).Notify(job)
+}
+
+func (s *Subject) Groups(name string) *Group {
+	g, ok := s.groups.Get(name)
+	if !ok {
+		return nil
+	}
+	return g.(*Group)
+}
+
+func (s *Subject) GetOrNewGroup(name string) *Group {
+	item, _ := s.groups.Fetch(name, func() (interface{}, error) {
+		return NewGroup(name), nil
+	})
+	return item.(*Group)
+}
+
+func (s *Subject) Remove(name string) {
+	s.groups.Remove(name)
+}
+
+func (s *Subject) Size() int {
+	return s.groups.Size()
+}
+
+func NewSubject(name string) *Subject {
+	return &Subject{
+		name:   name,
+		groups: util.NewConcurrentMap(0),
+	}
+}
diff --git a/server/service/notification/subject_test.go b/server/service/notification/subject_test.go
new file mode 100644
index 0000000..293c500
--- /dev/null
+++ b/server/service/notification/subject_test.go
@@ -0,0 +1,65 @@
+/*
+ * 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 notification
+
+import "testing"
+
+func TestSubject_Fetch(t *testing.T) {
+	s := NewSubject("s1")
+	if s.Name() != "s1" {
+		t.Fatalf("TestSubject_Fetch failed")
+	}
+	g := s.GetOrNewGroup("g1")
+	if g == nil {
+		t.Fatalf("TestSubject_Fetch failed")
+	}
+	if s.GetOrNewGroup(g.Name()) != g {
+		t.Fatalf("TestSubject_Fetch failed")
+	}
+	o := s.GetOrNewGroup("g2")
+	if s.Groups("g2") != o {
+		t.Fatalf("TestSubject_Fetch failed")
+	}
+	if s.Size() != 2 {
+		t.Fatalf("TestSubject_Fetch failed")
+	}
+	s.Remove(o.Name())
+	if s.Groups("g2") != nil {
+		t.Fatalf("TestSubject_Fetch failed")
+	}
+	if s.Size() != 1 {
+		t.Fatalf("TestSubject_Fetch failed")
+	}
+	mock1 := &mockSubscriber{BaseSubscriber: NewSubscriber(INSTANCE, "s1", "g1")}
+	mock2 := &mockSubscriber{BaseSubscriber: NewSubscriber(INSTANCE, "s1", "g2")}
+	g.AddSubscriber(mock1)
+	job := &BaseNotifyJob{group: "g3"}
+	s.Notify(job)
+	if mock1.job != nil || mock2.job != nil {
+		t.Fatalf("TestSubject_Fetch failed")
+	}
+	job.group = "g1"
+	s.Notify(job)
+	if mock1.job != job || mock2.job != nil {
+		t.Fatalf("TestSubject_Fetch failed")
+	}
+	job.group = ""
+	s.Notify(job)
+	if mock1.job != job && mock2.job != job {
+		t.Fatalf("TestSubject_Fetch failed")
+	}
+}
diff --git a/server/service/notification/subscriber.go b/server/service/notification/subscriber.go
index 9f0b4df..ece7013 100644
--- a/server/service/notification/subscriber.go
+++ b/server/service/notification/subscriber.go
@@ -18,67 +18,54 @@ package notification
 
 import (
 	"errors"
+	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
 )
 
 type Subscriber interface {
-	Err() error
-	SetError(err error)
 	Id() string
 	Subject() string
+	Group() string
 	Type() NotifyType
 	Service() *NotifyService
 	SetService(*NotifyService)
+
+	Err() error
+	SetError(err error)
+
+	Close()
 	OnAccept()
 	// The event bus will callback this function, so it must be non-blocked.
 	OnMessage(job NotifyJob)
-	Close()
 }
 
 type BaseSubscriber struct {
 	id      string
 	subject string
+	group   string
 	nType   NotifyType
 	service *NotifyService
 	err     error
 }
 
-func (s *BaseSubscriber) Err() error {
-	return s.err
-}
-
-func (s *BaseSubscriber) SetError(err error) {
-	s.err = err
-	// 触发清理job
-	s.Service().AddJob(NewNotifyServiceHealthCheckJob(s))
-}
-
-func (s *BaseSubscriber) Id() string {
-	return s.id
-}
-
-func (s *BaseSubscriber) Subject() string {
-	return s.subject
-}
-
-func (s *BaseSubscriber) Type() NotifyType {
-	return s.nType
-}
-
-func (s *BaseSubscriber) Service() *NotifyService {
-	return s.service
-}
-
-func (s *BaseSubscriber) SetService(svc *NotifyService) {
-	s.service = svc
-}
-
-func (s *BaseSubscriber) OnAccept() {
-}
-
+func (s *BaseSubscriber) Id() string                    { return s.id }
+func (s *BaseSubscriber) Subject() string               { return s.subject }
+func (s *BaseSubscriber) Group() string                 { return s.group }
+func (s *BaseSubscriber) Type() NotifyType              { return s.nType }
+func (s *BaseSubscriber) Service() *NotifyService       { return s.service }
+func (s *BaseSubscriber) SetService(svc *NotifyService) { s.service = svc }
+func (s *BaseSubscriber) Err() error                    { return s.err }
+func (s *BaseSubscriber) SetError(err error)            { s.err = err }
+func (s *BaseSubscriber) Close()                        {}
+func (s *BaseSubscriber) OnAccept()                     {}
 func (s *BaseSubscriber) OnMessage(job NotifyJob) {
 	s.SetError(errors.New("do not call base notifier OnMessage method"))
 }
 
-func (s *BaseSubscriber) Close() {
-
+func NewSubscriber(nType NotifyType, subject, group string) *BaseSubscriber {
+	return &BaseSubscriber{
+		id:      util.GenerateUuid(),
+		group:   group,
+		subject: subject,
+		nType:   nType,
+	}
 }
diff --git a/server/service/notification/websocket.go b/server/service/notification/websocket.go
index f53e641..362d7e2 100644
--- a/server/service/notification/websocket.go
+++ b/server/service/notification/websocket.go
@@ -28,221 +28,212 @@ import (
 	"time"
 )
 
-type WebSocketHandler struct {
+type WebSocket struct {
 	ctx             context.Context
+	ticker          *time.Ticker
 	conn            *websocket.Conn
 	watcher         *ListWatcher
 	needPingWatcher bool
 	closed          chan struct{}
-	goroutine       *util.GoRoutine
 }
 
-func (wh *WebSocketHandler) Init() error {
+func (wh *WebSocket) Init() error {
+	wh.ticker = time.NewTicker(wh.Timeout())
+
 	remoteAddr := wh.conn.RemoteAddr().String()
+
+	// subscribe backend
 	if err := GetNotifyService().AddSubscriber(wh.watcher); err != nil {
 		err = fmt.Errorf("establish[%s] websocket watch failed: notify service error, %s",
 			remoteAddr, err.Error())
 		util.Logger().Errorf(nil, err.Error())
 
-		err = wh.conn.WriteMessage(websocket.TextMessage, util.StringToBytesWithNoCopy(err.Error()))
-		if err != nil {
-			util.Logger().Errorf(err, "establish[%s] websocket watch failed: write message failed.", remoteAddr)
+		werr := wh.conn.WriteMessage(websocket.TextMessage, util.StringToBytesWithNoCopy(err.Error()))
+		if werr != nil {
+			util.Logger().Errorf(werr, "establish[%s] websocket watch failed: write message failed.", remoteAddr)
 		}
 		return err
 	}
-	util.Logger().Debugf("start watching instance status, watcher[%s] %s %s",
-		remoteAddr, wh.watcher.Subject(), wh.watcher.Id())
+
+	// publish events
+	publisher.Accept(wh)
+
+	util.Logger().Debugf("start watching instance status, watcher[%s], subject: %s, group: %s",
+		remoteAddr, wh.watcher.Subject(), wh.watcher.Group())
 	return nil
 }
 
-func (wh *WebSocketHandler) Timeout() time.Duration {
-	return GetNotifyService().Config.NotifyTimeout
+func (wh *WebSocket) Timeout() time.Duration {
+	return DEFAULT_SEND_TIMEOUT
 }
 
-func (wh *WebSocketHandler) websocketHeartbeat(messageType int) error {
+func (wh *WebSocket) heartbeat(messageType int) error {
 	err := wh.conn.WriteControl(messageType, []byte{}, time.Now().Add(wh.Timeout()))
 	if err != nil {
 		messageTypeName := "Ping"
 		if messageType == websocket.PongMessage {
 			messageTypeName = "Pong"
 		}
-		util.Logger().Errorf(err, "fail to send '%s' to watcher[%s] %s %s", messageTypeName,
-			wh.conn.RemoteAddr(), wh.watcher.Subject(), wh.watcher.Id())
+		util.Logger().Errorf(err, "fail to send '%s' to watcher[%s], subject: %s, group: %s",
+			messageTypeName, wh.conn.RemoteAddr(), wh.watcher.Subject(), wh.watcher.Group())
 		//wh.watcher.SetError(err)
 		return err
 	}
 	return nil
 }
 
-func (wh *WebSocketHandler) HandleWatchWebSocketControlMessage(ctx context.Context) {
+func (wh *WebSocket) HandleWatchWebSocketControlMessage() {
 	defer close(wh.closed)
 
 	remoteAddr := wh.conn.RemoteAddr().String()
 	// PING
 	wh.conn.SetPingHandler(func(message string) error {
 		if wh.needPingWatcher {
-			util.Logger().Infof("received 'Ping' message '%s' from watcher[%s] %s %s, no longer send 'Ping' to it",
-				message, remoteAddr, wh.watcher.Subject(), wh.watcher.Id())
+			util.Logger().Infof("received 'Ping' message '%s' from watcher[%s], no longer send 'Ping' to it, subject: %s, group: %s",
+				message, remoteAddr, wh.watcher.Subject(), wh.watcher.Group())
 		}
 		wh.needPingWatcher = false
-		return wh.websocketHeartbeat(websocket.PongMessage)
+		return wh.heartbeat(websocket.PongMessage)
 	})
 	// PONG
 	wh.conn.SetPongHandler(func(message string) error {
-		util.Logger().Debugf("received 'Pong' message %s from watcher[%s] %s %s",
-			message, remoteAddr, wh.watcher.Subject(), wh.watcher.Id())
+		util.Logger().Debugf("received 'Pong' message '%s' from watcher[%s], subject: %s, group: %s",
+			message, remoteAddr, wh.watcher.Subject(), wh.watcher.Group())
 		return nil
 	})
 	// CLOSE
 	wh.conn.SetCloseHandler(func(code int, text string) error {
-		util.Logger().Warnf(nil, "watcher[%s] %s %s active closed", remoteAddr,
-			wh.watcher.Subject(), wh.watcher.Id())
-		return wh.Close(code, text)
+		util.Logger().Warnf(nil, "watcher[%s] active closed, subject: %s, group: %s",
+			remoteAddr, wh.watcher.Subject(), wh.watcher.Group())
+		return wh.sendClose(code, text)
 	})
 
 	for {
-		select {
-		case <-ctx.Done():
+		_, _, err := wh.conn.ReadMessage()
+		if err != nil {
+			wh.watcher.SetError(err)
 			return
-		default:
-			_, _, err := wh.conn.ReadMessage()
-			if err != nil {
-				wh.watcher.SetError(err)
-				return
-			}
 		}
 	}
 }
 
-func (wh *WebSocketHandler) HandleWatchWebSocketJob() {
-	wh.goroutine.Do(wh.HandleWatchWebSocketControlMessage)
-
+func (wh *WebSocket) sendClose(code int, text string) error {
 	remoteAddr := wh.conn.RemoteAddr().String()
-	for {
-		timer := time.NewTimer(wh.Timeout())
-		select {
-		case <-wh.closed:
-			timer.Stop()
-			return
-		case <-wh.ctx.Done():
-			timer.Stop()
+	var message []byte
+	if code != websocket.CloseNoStatusReceived {
+		message = websocket.FormatCloseMessage(code, text)
+	}
+	err := wh.conn.WriteControl(websocket.CloseMessage, message, time.Now().Add(wh.Timeout()))
+	if err != nil {
+		util.Logger().Errorf(err, "watcher[%s] catch an err, subject: %s, group: %s",
+			remoteAddr, wh.watcher.Subject(), wh.watcher.Group())
+		return err
+	}
+	return nil
+}
 
-			util.Logger().Warnf(nil, "handle timed out, watcher[%s] %s %s", remoteAddr,
-				wh.watcher.Subject(), wh.watcher.Id())
-			return
-		case <-timer.C:
-			if wh.watcher.Err() != nil {
-				return
-			}
-
-			domainProject := util.ParseDomainProject(wh.ctx)
-			if !serviceUtil.ServiceExist(context.Background(), domainProject, wh.watcher.Id()) {
-				err := fmt.Errorf("Service does not exit.")
-				util.Logger().Warnf(err, "watcher[%s] %s %s exit", remoteAddr, wh.watcher.Subject(), wh.watcher.Id())
-				err = wh.conn.WriteMessage(websocket.TextMessage, util.StringToBytesWithNoCopy(err.Error()))
-				if err != nil {
-					util.Logger().Errorf(err, "watcher[%s] %s %s catch an err: write message error",
-						remoteAddr, wh.watcher.Subject(), wh.watcher.Id())
-				}
-				return
-			}
-
-			if !wh.needPingWatcher {
-				continue
-			}
-
-			util.Logger().Debugf("send 'Ping' message to watcher[%s] %s %s", remoteAddr, wh.watcher.Subject(), wh.watcher.Id())
-			err := wh.websocketHeartbeat(websocket.PingMessage)
-			if err != nil {
-				return
-			}
-		case job := <-wh.watcher.Job:
-			timer.Stop()
-
-			if wh.watcher.Err() != nil {
-				return
-			}
-
-			if job == nil {
-				err := wh.conn.WriteMessage(websocket.TextMessage,
-					util.StringToBytesWithNoCopy("watcher catch an err: server shutdown"))
-				if err != nil {
-					util.Logger().Errorf(err, "watcher[%s] %s %s catch an err: write message error",
-						remoteAddr, wh.watcher.Subject(), wh.watcher.Id())
-					return
-				}
-				util.Logger().Warnf(nil, "watcher[%s] %s %s catch an err: server shutdown",
-					remoteAddr, wh.watcher.Subject(), wh.watcher.Id())
-				return
-			}
-
-			resp := job.Response
-
-			providerFlag := fmt.Sprintf("%s/%s/%s", resp.Key.AppId, resp.Key.ServiceName, resp.Key.Version)
-			if resp.Action != string(pb.EVT_EXPIRE) {
-				providerFlag = fmt.Sprintf("%s/%s(%s)", resp.Instance.ServiceId, resp.Instance.InstanceId, providerFlag)
-			}
-			util.Logger().Infof("event[%s] is coming in, watcher[%s] %s %s, providers' info %s",
-				resp.Action, remoteAddr, wh.watcher.Subject(), wh.watcher.Id(), providerFlag)
-
-			resp.Response = nil
-			data, err := json.Marshal(resp)
-			if err != nil {
-				util.Logger().Errorf(err, "watcher[%s] %s %s catch an err: marshal output file error",
-					remoteAddr, wh.watcher.Subject(), wh.watcher.Id())
-				message := fmt.Sprintf("marshal output file error, %s", err.Error())
-				err = wh.conn.WriteMessage(websocket.TextMessage, util.StringToBytesWithNoCopy(message))
-				if err != nil {
-					util.Logger().Errorf(err, "watcher[%s] %s %s catch an err: write message error",
-						remoteAddr, wh.watcher.Subject(), wh.watcher.Id())
-				}
-				return
-			}
-			err = wh.conn.WriteMessage(websocket.TextMessage, data)
-			if err != nil {
-				util.Logger().Errorf(err, "watcher[%s] %s %s catch an err: write message error",
-					remoteAddr, wh.watcher.Subject(), wh.watcher.Id())
-				return
-			}
+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")
 		}
+		return j
+	default:
+		return nil
 	}
 }
 
-func (wh *WebSocketHandler) Close(code int, text string) error {
-	defer wh.goroutine.Close(true)
-
+func (wh *WebSocket) HandleWatchWebSocketJob(o interface{}) {
 	remoteAddr := wh.conn.RemoteAddr().String()
 	var message []byte
-	if code != websocket.CloseNoStatusReceived {
-		message = websocket.FormatCloseMessage(code, text)
+
+	switch o.(type) {
+	case error:
+		err := o.(error)
+		util.Logger().Warnf(err, "watcher[%s] catch an err, subject: %s, group: %s",
+			remoteAddr, wh.watcher.Subject(), wh.watcher.Group())
+
+		message = util.StringToBytesWithNoCopy(fmt.Sprintf("watcher catch an err: %s", err.Error()))
+	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())
+			break
+		}
+
+		if !wh.needPingWatcher {
+			return
+		}
+
+		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)
+		}
+		return
+	case *WatchJob:
+		job := o.(*WatchJob)
+		resp := job.Response
+
+		providerFlag := fmt.Sprintf("%s/%s/%s", resp.Key.AppId, resp.Key.ServiceName, resp.Key.Version)
+		if resp.Action != string(pb.EVT_EXPIRE) {
+			providerFlag = fmt.Sprintf("%s/%s(%s)", resp.Instance.ServiceId, resp.Instance.InstanceId, providerFlag)
+		}
+		util.Logger().Infof("event[%s] is coming in, watcher[%s] watch %s, subject: %s, group: %s",
+			resp.Action, remoteAddr, providerFlag, wh.watcher.Subject(), wh.watcher.Group())
+
+		resp.Response = nil
+		data, err := json.Marshal(resp)
+		if err != nil {
+			wh.watcher.SetError(err)
+			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))
+		return
 	}
-	err := wh.conn.WriteControl(websocket.CloseMessage, message, time.Now().Add(wh.Timeout()))
+
+	select {
+	case <-wh.closed:
+		return
+	default:
+	}
+
+	err := wh.conn.WriteMessage(websocket.TextMessage, message)
 	if err != nil {
-		util.Logger().Errorf(err, "watcher[%s] %s %s catch an err: write 'Close' message error",
-			remoteAddr, wh.watcher.Subject(), wh.watcher.Id())
-		return err
+		util.Logger().Errorf(err, "watcher[%s] catch an err, subject: %s, group: %s",
+			remoteAddr, wh.watcher.Subject(), wh.watcher.Group())
 	}
-	return nil
 }
 
 func DoWebSocketListAndWatch(ctx context.Context, serviceId string, f func() ([]*pb.WatchInstanceResponse, int64), conn *websocket.Conn) {
 	domainProject := util.ParseDomainProject(ctx)
-	handler := &WebSocketHandler{
+	socket := &WebSocket{
 		ctx:             ctx,
 		conn:            conn,
 		watcher:         NewListWatcher(serviceId, apt.GetInstanceRootKey(domainProject)+"/", f),
 		needPingWatcher: true,
 		closed:          make(chan struct{}),
-		goroutine:       util.NewGo(context.Background()),
 	}
-	processHandler(handler)
+	process(socket)
 }
 
-func processHandler(handler *WebSocketHandler) {
-	if err := handler.Init(); err != nil {
+func process(socket *WebSocket) {
+	if err := socket.Init(); err != nil {
 		return
 	}
-	handler.HandleWatchWebSocketJob()
+	socket.HandleWatchWebSocketControlMessage()
 }
 
 func EstablishWebSocketError(conn *websocket.Conn, err error) {
diff --git a/server/service/notification/websocket_test.go b/server/service/notification/websocket_test.go
new file mode 100644
index 0000000..5b0a9f4
--- /dev/null
+++ b/server/service/notification/websocket_test.go
@@ -0,0 +1,120 @@
+/*
+ * 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 notification
+
+import (
+	"context"
+	"errors"
+	"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"
+	"net/http"
+	"net/http/httptest"
+	"strings"
+	"testing"
+	"time"
+)
+
+var closeCh = make(chan struct{})
+
+type watcherConn struct {
+}
+
+func (h *watcherConn) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	var upgrader = websocket.Upgrader{}
+	conn, _ := upgrader.Upgrade(w, r, nil)
+	for {
+		conn.WriteControl(websocket.PingMessage, []byte{}, time.Now().Add(time.Second))
+		conn.WriteControl(websocket.PongMessage, []byte{}, time.Now().Add(time.Second))
+		_, _, err := conn.ReadMessage()
+		if err != nil {
+			return
+		}
+		<-closeCh
+		conn.WriteControl(websocket.CloseMessage, []byte{}, time.Now().Add(time.Second))
+		conn.Close()
+		return
+	}
+}
+
+func TestDoWebSocketListAndWatch(t *testing.T) {
+	s := httptest.NewServer(&watcherConn{})
+
+	GetNotifyService().Start()
+
+	conn, _, _ := websocket.DefaultDialer.Dial(
+		strings.Replace(s.URL, "http://", "ws://", 1), nil)
+
+	go DoWebSocketListAndWatch(context.Background(), "", nil, conn)
+
+	EstablishWebSocketError(conn, errors.New("error"))
+
+	w := NewListWatcher("g", "s", func() (results []*proto.WatchInstanceResponse, rev int64) {
+		return
+	})
+	w.nType = -1
+
+	ws := &WebSocket{
+		ctx:             context.Background(),
+		conn:            conn,
+		watcher:         w,
+		needPingWatcher: true,
+		closed:          make(chan struct{}),
+	}
+	err := ws.Init()
+	if err == nil {
+		t.Fatalf("TestPublisher_Run")
+	}
+
+	w.nType = INSTANCE
+	err = ws.Init()
+	if err != nil {
+		t.Fatalf("TestPublisher_Run")
+	}
+	go ws.HandleWatchWebSocketControlMessage()
+
+	w.SetError(errors.New("err"))
+	w.OnMessage(nil)
+	w.SetError(nil)
+	w.OnMessage(nil)
+	w.OnMessage(&WatchJob{})
+
+	GetNotifyService().AddJob(NewWatchJob("g", "s", 1, &proto.WatchInstanceResponse{
+		Response: proto.CreateResponse(proto.Response_SUCCESS, "ok"),
+		Action:   string(proto.EVT_CREATE),
+		Key:      &proto.MicroServiceKey{},
+		Instance: &proto.MicroServiceInstance{},
+	}))
+
+	<-time.After(time.Second)
+
+	ws.HandleWatchWebSocketJob(nil)
+
+	ws.heartbeat(websocket.PingMessage)
+	ws.heartbeat(websocket.PongMessage)
+
+	ws.HandleWatchWebSocketJob(time.Now())
+
+	closeCh <- struct{}{}
+
+	<-time.After(time.Second)
+
+	ws.heartbeat(websocket.PingMessage)
+	ws.heartbeat(websocket.PongMessage)
+
+	publisher.Stop()
+}
diff --git a/server/service/schema.go b/server/service/schema.go
index b59a38d..17b1f08 100644
--- a/server/service/schema.go
+++ b/server/service/schema.go
@@ -75,7 +75,7 @@ func (s *MicroServiceService) GetSchemaInfo(ctx context.Context, in *pb.GetSchem
 
 	return &pb.GetSchemaResponse{
 		Response:      pb.CreateResponse(pb.Response_SUCCESS, "Get schema info successfully."),
-		Schema:        util.BytesToStringWithNoCopy(resp.Kvs[0].Value),
+		Schema:        util.BytesToStringWithNoCopy(resp.Kvs[0].Value.([]byte)),
 		SchemaSummary: schemaSummary,
 	}, nil
 }
@@ -123,7 +123,7 @@ func (s *MicroServiceService) GetAllSchemaInfo(ctx context.Context, in *pb.GetAl
 		}, errDo
 	}
 
-	respWithSchema := &registry.PluginResponse{}
+	respWithSchema := &backend.Response{}
 	if in.WithSchema {
 		key := apt.GenerateServiceSchemaKey(domainProject, in.ServiceId, "")
 		opts := append(serviceUtil.FromContext(ctx), registry.WithStrKey(key), registry.WithPrefix())
@@ -141,16 +141,16 @@ func (s *MicroServiceService) GetAllSchemaInfo(ctx context.Context, in *pb.GetAl
 		tempSchema := &pb.Schema{}
 		tempSchema.SchemaId = schemaId
 		for _, summarySchema := range resp.Kvs {
-			schemaIdOfSummary, summaryData := pb.GetInfoFromSchemaSummaryKV(summarySchema)
+			schemaIdOfSummary := backend.GetInfoFromSchemaSummaryKV(summarySchema)
 			if schemaId == schemaIdOfSummary {
-				tempSchema.Summary = util.BytesToStringWithNoCopy(summaryData)
+				tempSchema.Summary = summarySchema.Value.(string)
 			}
 		}
 
 		for _, contentSchema := range respWithSchema.Kvs {
-			schemaIdOfSchema, schemaData := pb.GetInfoFromSchemaKV(contentSchema)
+			schemaIdOfSchema := backend.GetInfoFromSchemaKV(contentSchema)
 			if schemaId == schemaIdOfSchema {
-				tempSchema.Schema = util.BytesToStringWithNoCopy(schemaData)
+				tempSchema.Schema = util.BytesToStringWithNoCopy(contentSchema.Value.([]byte))
 			}
 		}
 		schemas = append(schemas, tempSchema)
@@ -467,7 +467,7 @@ func GetSchemasFromDatabase(ctx context.Context, domainProject string, serviceId
 		key := util.BytesToStringWithNoCopy(kv.Key)
 		tmp := strings.Split(key, "/")
 		schemaId := tmp[len(tmp)-1]
-		schema := util.BytesToStringWithNoCopy(kv.Value)
+		schema := util.BytesToStringWithNoCopy(kv.Value.([]byte))
 		schemaStruct := &pb.Schema{
 			SchemaId: schemaId,
 			Schema:   schema,
@@ -672,5 +672,5 @@ func getSchemaSummary(ctx context.Context, domainProject string, serviceId strin
 	if len(resp.Kvs) == 0 {
 		return "", nil
 	}
-	return util.BytesToStringWithNoCopy(resp.Kvs[0].Value), nil
+	return resp.Kvs[0].Value.(string), nil
 }
diff --git a/server/service/tag_test.go b/server/service/tag_test.go
index cee8799..1ca9112 100644
--- a/server/service/tag_test.go
+++ b/server/service/tag_test.go
@@ -375,8 +375,7 @@ var _ = Describe("'Tag' service", func() {
 					VersionRule:       "1.0.0+",
 					Tags:              []string{"not-exist-tag"},
 				})
-				Expect(findResp.Response.Code).To(Equal(pb.Response_SUCCESS))
-				Expect(len(findResp.Instances)).To(Equal(0))
+				Expect(findResp.Response.Code).To(Equal(scerr.ErrServiceNotExists))
 
 				findResp, err = instanceResource.Find(getContext(), &pb.FindInstancesRequest{
 					ConsumerServiceId: consumerId,
@@ -394,7 +393,7 @@ var _ = Describe("'Tag' service", func() {
 					Rules: []*pb.AddOrUpdateServiceRule{
 						&pb.AddOrUpdateServiceRule{
 							RuleType:    "WHITE",
-							Attribute:   "tag_filter_tag",
+							Attribute:   "tag_consumer_tag",
 							Pattern:     "f*",
 							Description: "test white",
 						},
@@ -410,12 +409,11 @@ var _ = Describe("'Tag' service", func() {
 					VersionRule:       "1.0.0+",
 					Tags:              []string{"filter_tag"},
 				})
-				Expect(findResp.Response.Code).To(Equal(pb.Response_SUCCESS))
-				Expect(len(findResp.Instances)).To(Equal(0))
+				Expect(findResp.Response.Code).To(Equal(scerr.ErrServiceNotExists))
 
 				addTagResp, err = serviceResource.AddTags(getContext(), &pb.AddServiceTagsRequest{
 					ServiceId: consumerId,
-					Tags:      map[string]string{"filter_tag": "filter"},
+					Tags:      map[string]string{"consumer_tag": "filter"},
 				})
 				Expect(err).To(BeNil())
 				Expect(addTagResp.Response.Code).To(Equal(pb.Response_SUCCESS))
diff --git a/server/service/util/common.go b/server/service/util/common.go
index 9e31b3f..6c25e51 100644
--- a/server/service/util/common.go
+++ b/server/service/util/common.go
@@ -16,14 +16,10 @@
  */
 package util
 
-import "time"
-
 const (
 	HEADER_REV            = "X-Resource-Revision"
 	CTX_NOCACHE           = "noCache"
 	CTX_CACHEONLY         = "cacheOnly"
 	CTX_REQUEST_REVISION  = "requestRev"
 	CTX_RESPONSE_REVISION = "responseRev"
-
-	cacheTTL = 5 * time.Minute
 )
diff --git a/server/service/util/dependency.go b/server/service/util/dependency.go
index 4a5ed1b..f26f0f7 100644
--- a/server/service/util/dependency.go
+++ b/server/service/util/dependency.go
@@ -19,532 +19,14 @@ package util
 import (
 	"encoding/json"
 	"errors"
-	"fmt"
 	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
 	apt "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"
 	"github.com/apache/incubator-servicecomb-service-center/server/infra/registry"
 	"golang.org/x/net/context"
-	"strings"
 )
 
-func GetConsumersInCache(ctx context.Context, domainProject string, provider *pb.MicroService) ([]string, error) {
-	// 查询所有consumer
-	dr := NewProviderDependencyRelation(util.SetContext(util.CloneContext(ctx), CTX_CACHEONLY, "1"),
-		domainProject, provider)
-	consumerIds, err := dr.GetDependencyConsumerIds()
-	if err != nil {
-		util.Logger().Errorf(err, "Get dependency consumerIds failed.%s", provider.ServiceId)
-		return nil, err
-	}
-	return consumerIds, nil
-}
-
-func GetProvidersInCache(ctx context.Context, domainProject string, consumer *pb.MicroService) ([]string, error) {
-	// 查询所有provider
-	dr := NewConsumerDependencyRelation(util.SetContext(util.CloneContext(ctx), CTX_CACHEONLY, "1"),
-		domainProject, consumer)
-	providerIds, err := dr.GetDependencyProviderIds()
-	if err != nil {
-		util.Logger().Errorf(err, "Get dependency providerIds failed.%s", consumer.ServiceId)
-		return nil, err
-	}
-	return providerIds, nil
-}
-
-func GetConsumerIdsByProvider(ctx context.Context, domainProject string, provider *pb.MicroService) (allow []string, deny []string, _ error) {
-	if provider == nil || len(provider.ServiceId) == 0 {
-		return nil, nil, fmt.Errorf("invalid provider")
-	}
-
-	//todo 删除服务,最后实例推送有误差
-	providerRules, err := GetRulesUtil(util.SetContext(util.CloneContext(ctx), CTX_CACHEONLY, "1"),
-		domainProject, provider.ServiceId)
-	if err != nil {
-		return nil, nil, err
-	}
-	if len(providerRules) == 0 {
-		return getConsumerIdsWithFilter(ctx, domainProject, provider, noFilter)
-	}
-
-	rf := RuleFilter{
-		DomainProject: domainProject,
-		Provider:      provider,
-		ProviderRules: providerRules,
-	}
-
-	allow, deny, err = getConsumerIdsWithFilter(ctx, domainProject, provider, rf.Filter)
-	if err != nil {
-		return nil, nil, err
-	}
-	return allow, deny, nil
-}
-
-func getConsumerIdsWithFilter(ctx context.Context, domainProject string, provider *pb.MicroService,
-	filter func(ctx context.Context, consumerId string) (bool, error)) (allow []string, deny []string, err error) {
-	consumerIds, err := GetConsumersInCache(ctx, domainProject, provider)
-	if err != nil {
-		return nil, nil, err
-	}
-	return filterConsumerIds(ctx, consumerIds, filter)
-}
-
-func filterConsumerIds(ctx context.Context, consumerIds []string,
-	filter func(ctx context.Context, consumerId string) (bool, error)) (allow []string, deny []string, err error) {
-	l := len(consumerIds)
-	if l == 0 {
-		return nil, nil, nil
-	}
-	allowIdx, denyIdx := 0, l
-	consumers := make([]string, l)
-	for _, consumerId := range consumerIds {
-		ok, err := filter(ctx, consumerId)
-		if err != nil {
-			return nil, nil, err
-		}
-		if ok {
-			consumers[allowIdx] = consumerId
-			allowIdx++
-		} else {
-			denyIdx--
-			consumers[denyIdx] = consumerId
-		}
-	}
-	return consumers[:allowIdx], consumers[denyIdx:], nil
-}
-
-func noFilter(_ context.Context, _ string) (bool, error) {
-	return true, nil
-}
-
-func GetProviderIdsByConsumer(ctx context.Context, domainProject string, service *pb.MicroService) (allow []string, deny []string, _ error) {
-	providerIdsInCache, err := GetProvidersInCache(ctx, domainProject, service)
-	if err != nil {
-		return nil, nil, err
-	}
-	l := len(providerIdsInCache)
-	rf := RuleFilter{
-		DomainProject: domainProject,
-	}
-	allowIdx, denyIdx := 0, l
-	providerIds := make([]string, l)
-	copyCtx := util.SetContext(util.CloneContext(ctx), CTX_CACHEONLY, "1")
-	for _, providerId := range providerIdsInCache {
-		provider, err := GetService(ctx, domainProject, providerId)
-		if provider == nil {
-			continue
-		}
-		providerRules, err := GetRulesUtil(copyCtx, domainProject, provider.ServiceId)
-		if err != nil {
-			return nil, nil, err
-		}
-		if len(providerRules) == 0 {
-			providerIds[allowIdx] = providerId
-			allowIdx++
-			continue
-		}
-		rf.Provider = provider
-		rf.ProviderRules = providerRules
-		ok, err := rf.Filter(ctx, service.ServiceId)
-		if err != nil {
-			return nil, nil, err
-		}
-		if ok {
-			providerIds[allowIdx] = providerId
-			allowIdx++
-		} else {
-			denyIdx--
-			providerIds[denyIdx] = providerId
-		}
-	}
-	return providerIds[:allowIdx], providerIds[denyIdx:], nil
-}
-
-func DependencyRuleExist(ctx context.Context, provider *pb.MicroServiceKey, consumer *pb.MicroServiceKey) (bool, error) {
-	targetDomainProject := provider.Tenant
-	if len(targetDomainProject) == 0 {
-		targetDomainProject = consumer.Tenant
-	}
-
-	consumerKey := apt.GenerateConsumerDependencyRuleKey(consumer.Tenant, consumer)
-	existed, err := dependencyRuleExistUtil(ctx, consumerKey, provider)
-	if err != nil || existed {
-		return existed, err
-	}
-
-	providerKey := apt.GenerateProviderDependencyRuleKey(targetDomainProject, provider)
-	return dependencyRuleExistUtil(ctx, providerKey, consumer)
-}
-
-func dependencyRuleExistUtil(ctx context.Context, key string, target *pb.MicroServiceKey) (bool, error) {
-	compareData, err := TransferToMicroServiceDependency(ctx, key)
-	if err != nil {
-		return false, err
-	}
-	if len(compareData.Dependency) != 0 {
-		isEqual, err := containServiceDependency(compareData.Dependency, target)
-		if err != nil {
-			return false, err
-		}
-		if isEqual {
-			//删除之前的依赖
-			return true, nil
-		}
-	}
-	return false, nil
-}
-
-func AddServiceVersionRule(ctx context.Context, domainProject string, consumer *pb.MicroService, provider *pb.MicroServiceKey) error {
-	//创建依赖一致
-	consumerKey := pb.MicroServiceToKey(domainProject, consumer)
-	exist, err := DependencyRuleExist(ctx, provider, consumerKey)
-	if exist || err != nil {
-		return err
-	}
-
-	r := &pb.ConsumerDependency{
-		Consumer:  consumerKey,
-		Providers: []*pb.MicroServiceKey{provider},
-		Override:  false,
-	}
-	data, err := json.Marshal(r)
-	if err != nil {
-		return err
-	}
-
-	id := util.StringJoin([]string{provider.AppId, provider.ServiceName}, "_")
-	key := apt.GenerateConsumerDependencyQueueKey(domainProject, consumer.ServiceId, id)
-	resp, err := backend.Registry().TxnWithCmp(ctx,
-		nil,
-		[]registry.CompareOp{registry.OpCmp(registry.CmpStrVal(key), registry.CMP_EQUAL, util.BytesToStringWithNoCopy(data))},
-		[]registry.PluginOp{registry.OpPut(registry.WithStrKey(key), registry.WithValue(data))})
-	if err != nil {
-		return err
-	}
-	if !resp.Succeeded {
-		util.Logger().Infof("find request into dependency queue successfully, %s: %v", key, r)
-	}
-	return nil
-}
-
-func TransferToMicroServiceDependency(ctx context.Context, key string) (*pb.MicroServiceDependency, error) {
-	microServiceDependency := &pb.MicroServiceDependency{
-		Dependency: []*pb.MicroServiceKey{},
-	}
-
-	opts := append(FromContext(ctx), registry.WithStrKey(key))
-	res, err := backend.Store().DependencyRule().Search(ctx, opts...)
-	if err != nil {
-		util.Logger().Errorf(nil, "Get dependency rule failed.")
-		return nil, err
-	}
-	if len(res.Kvs) != 0 {
-		err = json.Unmarshal(res.Kvs[0].Value, microServiceDependency)
-		if err != nil {
-			util.Logger().Errorf(nil, "Unmarshal res failed.")
-			return nil, err
-		}
-	}
-	return microServiceDependency, nil
-}
-
-func updateProviderDependencyRuleUtil(consumersOfProvideRules *pb.MicroServiceDependency, consumer *pb.MicroServiceKey, providerRuleKey string) (registry.PluginOp, error) {
-	for key, consumerInner := range consumersOfProvideRules.Dependency {
-		if ok := equalServiceDependency(consumerInner, consumer); ok {
-			consumersOfProvideRules.Dependency = append(consumersOfProvideRules.Dependency[:key], consumersOfProvideRules.Dependency[key+1:]...)
-			break
-		}
-	}
-	if len(consumersOfProvideRules.Dependency) == 0 {
-		util.Logger().Infof("delete dependency rule key is %s", providerRuleKey)
-		return registry.OpDel(registry.WithStrKey(providerRuleKey)), nil
-	} else {
-		data, err := json.Marshal(consumersOfProvideRules)
-		if err != nil {
-			util.Logger().Errorf(nil, "update dependency key Marshal tmpValue failed.")
-			return registry.PluginOp{}, err
-		}
-		util.Logger().Infof("put provider's dependency rule, key is  %s, value is %v.", providerRuleKey, consumersOfProvideRules)
-		return registry.OpPut(registry.WithStrKey(providerRuleKey), registry.WithValue(data)), nil
-	}
-}
-
-func equalServiceDependency(serviceA *pb.MicroServiceKey, serviceB *pb.MicroServiceKey) bool {
-	stringA := toString(serviceA)
-	stringB := toString(serviceB)
-	if stringA == stringB {
-		return true
-	}
-	return false
-}
-
-func diffServiceVersion(serviceA *pb.MicroServiceKey, serviceB *pb.MicroServiceKey) bool {
-	stringA := toString(serviceA)
-	stringB := toString(serviceB)
-	if stringA != stringB &&
-		stringA[:strings.LastIndex(stringA, "/")+1] == stringB[:strings.LastIndex(stringB, "/")+1] {
-		return true
-	}
-	return false
-}
-
-func toString(in *pb.MicroServiceKey) string {
-	return apt.GenerateProviderDependencyRuleKey(in.Tenant, in)
-}
-
-func parseAddOrUpdateRules(ctx context.Context, dep *Dependency) (newDependencyRuleList, existDependencyRuleList, deleteDependencyRuleList []*pb.MicroServiceKey) {
-	conKey := apt.GenerateConsumerDependencyRuleKey(dep.DomainProject, dep.Consumer)
-
-	oldProviderRules, err := TransferToMicroServiceDependency(ctx, conKey)
-	if err != nil {
-		util.Logger().Errorf(err, "maintain dependency rule failed, consumer %s/%s/%s: get consumer depedency rule failed.",
-			dep.Consumer.AppId, dep.Consumer.ServiceName, dep.Consumer.Version)
-		return
-	}
-
-	deleteDependencyRuleList = make([]*pb.MicroServiceKey, 0, len(oldProviderRules.Dependency))
-	newDependencyRuleList = make([]*pb.MicroServiceKey, 0, len(dep.ProvidersRule))
-	existDependencyRuleList = make([]*pb.MicroServiceKey, 0, len(oldProviderRules.Dependency))
-	for _, tmpProviderRule := range dep.ProvidersRule {
-		if ok, _ := containServiceDependency(oldProviderRules.Dependency, tmpProviderRule); ok {
-			continue
-		}
-
-		if tmpProviderRule.ServiceName == "*" {
-			newDependencyRuleList = append([]*pb.MicroServiceKey{}, tmpProviderRule)
-			deleteDependencyRuleList = oldProviderRules.Dependency
-			break
-		}
-
-		newDependencyRuleList = append(newDependencyRuleList, tmpProviderRule)
-		old := isNeedUpdate(oldProviderRules.Dependency, tmpProviderRule)
-		if old != nil {
-			deleteDependencyRuleList = append(deleteDependencyRuleList, old)
-		}
-	}
-	for _, oldProviderRule := range oldProviderRules.Dependency {
-		if oldProviderRule.ServiceName == "*" {
-			newDependencyRuleList = nil
-			deleteDependencyRuleList = nil
-			return
-		}
-		if ok, _ := containServiceDependency(deleteDependencyRuleList, oldProviderRule); !ok {
-			existDependencyRuleList = append(existDependencyRuleList, oldProviderRule)
-		}
-	}
-
-	dep.ProvidersRule = append(newDependencyRuleList, existDependencyRuleList...)
-	return
-}
-
-func parseOverrideRules(ctx context.Context, dep *Dependency) (newDependencyRuleList, existDependencyRuleList, deleteDependencyRuleList []*pb.MicroServiceKey) {
-	conKey := apt.GenerateConsumerDependencyRuleKey(dep.DomainProject, dep.Consumer)
-
-	oldProviderRules, err := TransferToMicroServiceDependency(ctx, conKey)
-	if err != nil {
-		util.Logger().Errorf(err, "maintain dependency rule failed, consumer %s/%s/%s: get consumer depedency rule failed.",
-			dep.Consumer.AppId, dep.Consumer.ServiceName, dep.Consumer.Version)
-		return
-	}
-
-	deleteDependencyRuleList = make([]*pb.MicroServiceKey, 0, len(oldProviderRules.Dependency))
-	newDependencyRuleList = make([]*pb.MicroServiceKey, 0, len(dep.ProvidersRule))
-	existDependencyRuleList = make([]*pb.MicroServiceKey, 0, len(oldProviderRules.Dependency))
-	for _, oldProviderRule := range oldProviderRules.Dependency {
-		if ok, _ := containServiceDependency(dep.ProvidersRule, oldProviderRule); !ok {
-			deleteDependencyRuleList = append(deleteDependencyRuleList, oldProviderRule)
-		} else {
-			existDependencyRuleList = append(existDependencyRuleList, oldProviderRule)
-		}
-	}
-	for _, tmpProviderRule := range dep.ProvidersRule {
-		if ok, _ := containServiceDependency(existDependencyRuleList, tmpProviderRule); !ok {
-			newDependencyRuleList = append(newDependencyRuleList, tmpProviderRule)
-		}
-	}
-	return
-}
-
-func syncDependencyRule(ctx context.Context, dep *Dependency, filter func(context.Context, *Dependency) (_, _, _ []*pb.MicroServiceKey)) error {
-	//更新consumer的providers的值,consumer的版本是确定的
-	consumerFlag := strings.Join([]string{dep.Consumer.AppId, dep.Consumer.ServiceName, dep.Consumer.Version}, "/")
-
-	newDependencyRuleList, existDependencyRuleList, deleteDependencyRuleList := filter(ctx, dep)
-	if len(newDependencyRuleList) == 0 && len(existDependencyRuleList) == 0 && len(deleteDependencyRuleList) == 0 {
-		return nil
-	}
-
-	dep.err = make(chan error, 5)
-	dep.chanNum = 0
-	if len(deleteDependencyRuleList) != 0 {
-		util.Logger().Infof("Delete dependency rule remove for consumer %s, %v, ", consumerFlag, deleteDependencyRuleList)
-		dep.removedDependencyRuleList = deleteDependencyRuleList
-		dep.RemoveConsumerOfProviderRule()
-	}
-
-	if len(newDependencyRuleList) != 0 {
-		util.Logger().Infof("New dependency rule add for consumer %s, %v, ", consumerFlag, newDependencyRuleList)
-		dep.NewDependencyRuleList = newDependencyRuleList
-		dep.AddConsumerOfProviderRule()
-	}
-
-	conKey := apt.GenerateConsumerDependencyRuleKey(dep.DomainProject, dep.Consumer)
-	err := dep.UpdateProvidersRuleOfConsumer(conKey)
-	if err != nil {
-		return err
-	}
-
-	if dep.chanNum != 0 {
-		for tmpErr := range dep.err {
-			dep.chanNum--
-			if tmpErr != nil {
-				return tmpErr
-			}
-			if 0 == dep.chanNum {
-				close(dep.err)
-			}
-		}
-	}
-	return nil
-}
-
-func AddDependencyRule(ctx context.Context, dep *Dependency) error {
-	return syncDependencyRule(ctx, dep, parseAddOrUpdateRules)
-}
-
-func CreateDependencyRule(ctx context.Context, dep *Dependency) error {
-	return syncDependencyRule(ctx, dep, parseOverrideRules)
-}
-
-func isDependencyAll(dep *pb.MicroServiceDependency) bool {
-	for _, serviceDep := range dep.Dependency {
-		if serviceDep.ServiceName == "*" {
-			return true
-		}
-	}
-	return false
-}
-
-func isExist(services []*pb.MicroServiceKey, service *pb.MicroServiceKey) bool {
-	for _, tmp := range services {
-		if equalServiceDependency(tmp, service) {
-			return true
-		}
-	}
-	return false
-}
-
-func deleteConsumerDepOfProviderRule(ctx context.Context, domainProject string, providerRule *pb.MicroServiceKey, deleteConsumer *pb.MicroServiceKey) (registry.PluginOp, error) {
-	proKey := apt.GenerateProviderDependencyRuleKey(domainProject, providerRule)
-	consumerDepRules, err := TransferToMicroServiceDependency(ctx, proKey)
-	if err != nil {
-		return registry.PluginOp{}, err
-	}
-	return deleteDepRuleUtil(proKey, consumerDepRules, deleteConsumer)
-}
-
-func deleteDepRuleUtil(key string, deps *pb.MicroServiceDependency, deleteDepRule *pb.MicroServiceKey) (registry.PluginOp, error) {
-	for key, consumerDepRule := range deps.Dependency {
-		if equalServiceDependency(consumerDepRule, deleteDepRule) {
-			deps.Dependency = append(deps.Dependency[:key], deps.Dependency[key+1:]...)
-			break
-		}
-	}
-	data, err := json.Marshal(deps)
-	if err != nil {
-		return registry.PluginOp{}, err
-	}
-	return registry.OpPut(registry.WithStrKey(key), registry.WithValue(data)), nil
-}
-
-func addDepRuleUtil(key string, deps *pb.MicroServiceDependency, updateDepRule *pb.MicroServiceKey) (registry.PluginOp, error) {
-	deps.Dependency = append(deps.Dependency, updateDepRule)
-	data, err := json.Marshal(deps)
-	if err != nil {
-		util.Logger().Errorf(err, "marshal consumerDepRules failed for delete consumer rule from provider rule's dep.")
-		return registry.PluginOp{}, err
-	}
-	return registry.OpPut(registry.WithStrKey(key), registry.WithValue(data)), nil
-}
-
-func updateDepRuleUtil(key string, deps *pb.MicroServiceDependency, updateDepRule *pb.MicroServiceKey) (string, registry.PluginOp, error) {
-	oldRule := ""
-	for _, serviceRule := range deps.Dependency {
-		if diffServiceVersion(serviceRule, updateDepRule) {
-			oldRule = serviceRule.Version
-			serviceRule.Version = updateDepRule.Version
-			break
-		}
-	}
-	data, err := json.Marshal(deps)
-	if err != nil {
-		return oldRule, registry.PluginOp{}, err
-	}
-	return oldRule, registry.OpPut(registry.WithStrKey(key), registry.WithValue(data)), nil
-}
-
-func isNeedUpdate(services []*pb.MicroServiceKey, service *pb.MicroServiceKey) *pb.MicroServiceKey {
-	for _, tmp := range services {
-		if diffServiceVersion(tmp, service) {
-			return tmp
-		}
-	}
-	return nil
-}
-
-func containServiceDependency(services []*pb.MicroServiceKey, service *pb.MicroServiceKey) (bool, error) {
-	if services == nil || service == nil {
-		return false, errors.New("Invalid params input.")
-	}
-	for _, value := range services {
-		rst := equalServiceDependency(service, value)
-		if rst {
-			return true, nil
-		}
-	}
-	return false, nil
-}
-
-func BadParamsResponse(detailErr string) *pb.CreateDependenciesResponse {
-	util.Logger().Errorf(nil, "Request params is invalid. %s", detailErr)
-	if len(detailErr) == 0 {
-		detailErr = "Request params is invalid."
-	}
-	return &pb.CreateDependenciesResponse{
-		Response: pb.CreateResponse(scerr.ErrInvalidParams, detailErr),
-	}
-}
-
-func ParamsChecker(consumerInfo *pb.MicroServiceKey, providersInfo []*pb.MicroServiceKey) *pb.CreateDependenciesResponse {
-	flag := make(map[string]bool, len(providersInfo))
-	for _, providerInfo := range providersInfo {
-		//存在带*的情况,后面的数据就不校验了
-		if providerInfo.ServiceName == "*" {
-			util.Logger().Debugf("%s 's provider contains *.", consumerInfo.ServiceName)
-			break
-		}
-		if len(providerInfo.AppId) == 0 {
-			providerInfo.AppId = consumerInfo.AppId
-		}
-
-		version := providerInfo.Version
-		if len(version) == 0 {
-			return BadParamsResponse("Required provider version")
-		}
-
-		providerInfo.Version = ""
-		if _, ok := flag[toString(providerInfo)]; ok {
-			return BadParamsResponse("Invalid request body for provider info.Duplicate provider or (serviceName and appId is same).")
-		} else {
-			flag[toString(providerInfo)] = true
-		}
-		providerInfo.Version = version
-	}
-	return nil
-}
-
 type Dependency struct {
 	ConsumerId                string
 	DomainProject             string
@@ -673,352 +155,3 @@ func (dep *Dependency) UpdateProvidersRuleOfConsumer(conKey string) error {
 	}
 	return nil
 }
-
-type DependencyRelation struct {
-	ctx           context.Context
-	domainProject string
-	consumer      *pb.MicroService
-	provider      *pb.MicroService
-}
-
-func NewProviderDependencyRelation(ctx context.Context, domainProject string, provider *pb.MicroService) *DependencyRelation {
-	return NewDependencyRelation(ctx, domainProject, nil, provider)
-}
-
-func NewConsumerDependencyRelation(ctx context.Context, domainProject string, consumer *pb.MicroService) *DependencyRelation {
-	return NewDependencyRelation(ctx, domainProject, consumer, nil)
-}
-
-func NewDependencyRelation(ctx context.Context, domainProject string, consumer *pb.MicroService, provider *pb.MicroService) *DependencyRelation {
-	return &DependencyRelation{
-		ctx:           ctx,
-		domainProject: domainProject,
-		consumer:      consumer,
-		provider:      provider,
-	}
-}
-
-func (dr *DependencyRelation) GetDependencyProviders(opts ...DependencyRelationFilterOption) ([]*pb.MicroService, error) {
-	keys, err := dr.getProviderKeys()
-	if err != nil {
-		return nil, err
-	}
-	services := make([]*pb.MicroService, 0, len(keys))
-	op := toDependencyRelationFilterOpt(opts...)
-	for _, key := range keys {
-		if op.SameDomainProject && key.Tenant != dr.domainProject {
-			continue
-		}
-
-		providerIds, err := dr.parseDependencyRule(key)
-		if err != nil {
-			return nil, err
-		}
-
-		if key.ServiceName == "*" {
-			services = services[:0]
-		}
-
-		for _, providerId := range providerIds {
-			provider, err := GetService(dr.ctx, key.Tenant, providerId)
-			if err != nil {
-				util.Logger().Warnf(nil, "Provider does not exist, %s/%s/%s",
-					key.AppId, key.ServiceName, key.Version)
-				continue
-			}
-			if provider == nil {
-				continue
-			}
-			if op.NonSelf && providerId == dr.consumer.ServiceId {
-				continue
-			}
-			services = append(services, provider)
-		}
-
-		if key.ServiceName == "*" {
-			break
-		}
-	}
-	return services, nil
-}
-
-func (dr *DependencyRelation) GetDependencyProviderIds() ([]string, error) {
-	keys, err := dr.getProviderKeys()
-	if err != nil {
-		return nil, err
-	}
-	return dr.getDependencyProviderIds(keys)
-}
-
-func (dr *DependencyRelation) getProviderKeys() ([]*pb.MicroServiceKey, error) {
-	if dr.consumer == nil {
-		util.LOGGER.Infof("dr.consumer is nil ------->")
-		return nil, fmt.Errorf("Invalid consumer")
-	}
-	consumerMicroServiceKey := pb.MicroServiceToKey(dr.domainProject, dr.consumer)
-
-	conKey := apt.GenerateConsumerDependencyRuleKey(dr.domainProject, consumerMicroServiceKey)
-	consumerDependency, err := TransferToMicroServiceDependency(dr.ctx, conKey)
-	if err != nil {
-		return nil, err
-	}
-	return consumerDependency.Dependency, nil
-}
-
-func (dr *DependencyRelation) getDependencyProviderIds(providerRules []*pb.MicroServiceKey) ([]string, error) {
-	provideServiceIds := make([]string, 0, len(providerRules))
-	for _, provider := range providerRules {
-		serviceIds, err := dr.parseDependencyRule(provider)
-		switch {
-		case provider.ServiceName == "*":
-			if err != nil {
-				util.Logger().Errorf(err, "Add dependency failed, rely all service: get all services failed.")
-				return provideServiceIds, err
-			}
-			return serviceIds, nil
-		default:
-			if err != nil {
-				util.Logger().Errorf(err, "Get providerIds failed, service: %s/%s/%s",
-					provider.AppId, provider.ServiceName, provider.Version)
-				return provideServiceIds, err
-			}
-			if len(serviceIds) == 0 {
-				util.Logger().Warnf(nil, "Get providerIds is empty, service: %s/%s/%s does not exist",
-					provider.AppId, provider.ServiceName, provider.Version)
-				continue
-			}
-			provideServiceIds = append(provideServiceIds, serviceIds...)
-		}
-	}
-	return provideServiceIds, nil
-}
-
-func (dr *DependencyRelation) parseDependencyRule(dependencyRule *pb.MicroServiceKey) (serviceIds []string, err error) {
-	opts := FromContext(dr.ctx)
-	switch {
-	case dependencyRule.ServiceName == "*":
-		util.Logger().Infof("Rely all service, * type, consumerId %s", dr.consumer.ServiceId)
-		splited := strings.Split(apt.GenerateServiceIndexKey(dependencyRule), "/")
-		allServiceKey := util.StringJoin(splited[:len(splited)-3], "/") + "/"
-		sopts := append(opts,
-			registry.WithStrKey(allServiceKey),
-			registry.WithPrefix())
-		resp, err := backend.Store().Service().Search(dr.ctx, sopts...)
-		if err != nil {
-			return nil, err
-		}
-
-		for _, kv := range resp.Kvs {
-			serviceIds = append(serviceIds, util.BytesToStringWithNoCopy(kv.Value))
-		}
-	default:
-		serviceIds, err = FindServiceIds(dr.ctx, dependencyRule.Version, dependencyRule)
-	}
-	return
-}
-
-func (dr *DependencyRelation) GetDependencyConsumers(opts ...DependencyRelationFilterOption) ([]*pb.MicroService, error) {
-	consumerDependAllList, err := dr.getDependencyConsumersOfProvider()
-	if err != nil {
-		util.Logger().Errorf(err, "Get consumers of provider rule failed, %s", dr.provider.ServiceId)
-		return nil, err
-	}
-	consumers := make([]*pb.MicroService, 0)
-	op := toDependencyRelationFilterOpt(opts...)
-	for _, consumer := range consumerDependAllList {
-		if op.SameDomainProject && consumer.Tenant != dr.domainProject {
-			continue
-		}
-
-		service, err := dr.getServiceByMicroServiceKey(consumer)
-		if err != nil {
-			return nil, err
-		}
-		if service == nil {
-			util.Logger().Warnf(nil, "Consumer does not exist, %v", consumer)
-			continue
-		}
-
-		if op.NonSelf && service.ServiceId == dr.provider.ServiceId {
-			continue
-		}
-
-		consumers = append(consumers, service)
-	}
-	return consumers, nil
-}
-
-func (dr *DependencyRelation) getServiceByMicroServiceKey(service *pb.MicroServiceKey) (*pb.MicroService, error) {
-	serviceId, err := GetServiceId(dr.ctx, service)
-	if err != nil {
-		return nil, err
-	}
-	if len(serviceId) == 0 {
-		util.Logger().Warnf(nil, "Service not exist,%v", service)
-		return nil, nil
-	}
-	return GetService(dr.ctx, service.Tenant, serviceId)
-}
-
-func (dr *DependencyRelation) GetDependencyConsumerIds() ([]string, error) {
-	consumerDependAllList, err := dr.getDependencyConsumersOfProvider()
-	if err != nil {
-		return nil, err
-	}
-	consumerIds := make([]string, 0, len(consumerDependAllList))
-	for _, consumer := range consumerDependAllList {
-		consumerId, err := GetServiceId(dr.ctx, consumer)
-		if err != nil {
-			util.Logger().Errorf(err, "Get consumer failed, %v", consumer)
-			return nil, err
-		}
-		if len(consumerId) == 0 {
-			util.Logger().Warnf(nil, "Get consumer not exist, %v", consumer)
-			continue
-		}
-		consumerIds = append(consumerIds, consumerId)
-	}
-	return consumerIds, nil
-
-}
-
-func (dr *DependencyRelation) getDependencyConsumersOfProvider() ([]*pb.MicroServiceKey, error) {
-	if dr.provider == nil {
-		util.LOGGER.Infof("dr.provider is nil ------->")
-		return nil, fmt.Errorf("Invalid provider")
-	}
-	providerService := pb.MicroServiceToKey(dr.domainProject, dr.provider)
-	consumerDependAllList, err := dr.getConsumerOfDependAllServices()
-	if err != nil {
-		util.Logger().Errorf(err, "Get consumer that depend on all services failed, %s", dr.provider.ServiceId)
-		return nil, err
-	}
-
-	consumerDependList, err := dr.getConsumerOfSameServiceNameAndAppId(providerService)
-	if err != nil {
-		util.Logger().Errorf(err, "Get consumer that depend on same serviceName and appid rule failed, %s",
-			dr.provider.ServiceId)
-		return nil, err
-	}
-	consumerDependAllList = append(consumerDependAllList, consumerDependList...)
-	return consumerDependAllList, nil
-}
-
-func (dr *DependencyRelation) getConsumerOfDependAllServices() ([]*pb.MicroServiceKey, error) {
-	providerService := pb.MicroServiceToKey(dr.domainProject, dr.provider)
-	providerService.ServiceName = "*"
-	relyAllKey := apt.GenerateProviderDependencyRuleKey(dr.domainProject, providerService)
-	opts := append(FromContext(dr.ctx), registry.WithStrKey(relyAllKey))
-	rsp, err := backend.Store().DependencyRule().Search(dr.ctx, opts...)
-	if err != nil {
-		util.Logger().Errorf(err, "get consumer that rely all service failed.")
-		return nil, err
-	}
-	dependency := &pb.MicroServiceDependency{}
-	if len(rsp.Kvs) != 0 {
-		util.Logger().Infof("consumer that rely all service exist.ServiceName: %s.", dr.provider.ServiceName)
-		err = json.Unmarshal(rsp.Kvs[0].Value, dependency)
-		if err != nil {
-			return nil, err
-		}
-		return dependency.Dependency, nil
-	}
-	return dependency.Dependency, nil
-}
-
-func (dr *DependencyRelation) getConsumerOfSameServiceNameAndAppId(provider *pb.MicroServiceKey) ([]*pb.MicroServiceKey, error) {
-	providerVersion := provider.Version
-	provider.Version = ""
-	prefix := apt.GenerateProviderDependencyRuleKey(dr.domainProject, provider)
-	provider.Version = providerVersion
-
-	opts := append(FromContext(dr.ctx),
-		registry.WithStrKey(prefix),
-		registry.WithPrefix())
-	rsp, err := backend.Store().DependencyRule().Search(dr.ctx, opts...)
-	if err != nil {
-		util.Logger().Errorf(err, "get all dependency rule failed: provider rule key %v.", provider)
-		return nil, err
-	}
-
-	allConsumers := make([]*pb.MicroServiceKey, 0, len(rsp.Kvs))
-	var latestServiceId []string
-
-	for _, kv := range rsp.Kvs {
-		dependency := &pb.MicroServiceDependency{
-			Dependency: []*pb.MicroServiceKey{},
-		}
-		providerVersionRuleArr := strings.Split(util.BytesToStringWithNoCopy(kv.Key), "/")
-		providerVersionRule := providerVersionRuleArr[len(providerVersionRuleArr)-1]
-		if providerVersionRule == "latest" {
-			if latestServiceId == nil {
-				latestServiceId, err = FindServiceIds(dr.ctx, providerVersionRule, provider)
-				if err != nil {
-					util.Logger().Errorf(err, "Get latest service failed.")
-					return nil, err
-				}
-			}
-			if len(latestServiceId) == 0 {
-				util.Logger().Infof("%s 's providerId is empty,no this service.", provider.ServiceName)
-				continue
-			}
-			if dr.provider.ServiceId != latestServiceId[0] {
-				continue
-			}
-
-		} else {
-			if !VersionMatchRule(providerVersion, providerVersionRule) {
-				continue
-			}
-		}
-
-		util.Logger().Debugf("providerETCD is %s", providerVersionRuleArr)
-		err = json.Unmarshal(kv.Value, dependency)
-		if err != nil {
-			util.Logger().Errorf(err, "Unmarshal consumers failed.")
-			return nil, err
-		}
-		allConsumers = append(allConsumers, dependency.Dependency...)
-	}
-	return allConsumers, nil
-}
-
-func DeleteDependencyForDeleteService(domainProject string, serviceId string, service *pb.MicroServiceKey) (registry.PluginOp, error) {
-	key := apt.GenerateConsumerDependencyQueueKey(domainProject, serviceId, "0")
-	conDep := new(pb.ConsumerDependency)
-	conDep.Consumer = service
-	conDep.Providers = []*pb.MicroServiceKey{}
-	conDep.Override = true
-	data, err := json.Marshal(conDep)
-	if err != nil {
-		return registry.PluginOp{}, err
-	}
-	return registry.OpPut(registry.WithStrKey(key), registry.WithValue(data)), nil
-}
-
-type DependencyRelationFilterOpt struct {
-	SameDomainProject bool
-	NonSelf           bool
-}
-
-type DependencyRelationFilterOption func(opt DependencyRelationFilterOpt) DependencyRelationFilterOpt
-
-func WithSameDomainProject() DependencyRelationFilterOption {
-	return func(opt DependencyRelationFilterOpt) DependencyRelationFilterOpt {
-		opt.SameDomainProject = true
-		return opt
-	}
-}
-func WithoutSelfDependency() DependencyRelationFilterOption {
-	return func(opt DependencyRelationFilterOpt) DependencyRelationFilterOpt {
-		opt.NonSelf = true
-		return opt
-	}
-}
-
-func toDependencyRelationFilterOpt(opts ...DependencyRelationFilterOption) (op DependencyRelationFilterOpt) {
-	for _, opt := range opts {
-		op = opt(op)
-	}
-	return
-}
diff --git a/server/service/util/dependency_query.go b/server/service/util/dependency_query.go
new file mode 100644
index 0000000..343e9fa
--- /dev/null
+++ b/server/service/util/dependency_query.go
@@ -0,0 +1,351 @@
+/*
+ * 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 (
+	"fmt"
+	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
+	apt "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"
+	"github.com/apache/incubator-servicecomb-service-center/server/infra/registry"
+	"golang.org/x/net/context"
+	"strings"
+)
+
+type DependencyRelationFilterOpt struct {
+	SameDomainProject bool
+	NonSelf           bool
+}
+
+type DependencyRelationFilterOption func(opt DependencyRelationFilterOpt) DependencyRelationFilterOpt
+
+func WithSameDomainProject() DependencyRelationFilterOption {
+	return func(opt DependencyRelationFilterOpt) DependencyRelationFilterOpt {
+		opt.SameDomainProject = true
+		return opt
+	}
+}
+func WithoutSelfDependency() DependencyRelationFilterOption {
+	return func(opt DependencyRelationFilterOpt) DependencyRelationFilterOpt {
+		opt.NonSelf = true
+		return opt
+	}
+}
+
+func toDependencyRelationFilterOpt(opts ...DependencyRelationFilterOption) (op DependencyRelationFilterOpt) {
+	for _, opt := range opts {
+		op = opt(op)
+	}
+	return
+}
+
+type DependencyRelation struct {
+	ctx           context.Context
+	domainProject string
+	consumer      *pb.MicroService
+	provider      *pb.MicroService
+}
+
+func (dr *DependencyRelation) GetDependencyProviders(opts ...DependencyRelationFilterOption) ([]*pb.MicroService, error) {
+	keys, err := dr.getProviderKeys()
+	if err != nil {
+		return nil, err
+	}
+	services := make([]*pb.MicroService, 0, len(keys))
+	op := toDependencyRelationFilterOpt(opts...)
+	for _, key := range keys {
+		if op.SameDomainProject && key.Tenant != dr.domainProject {
+			continue
+		}
+
+		providerIds, err := dr.parseDependencyRule(key)
+		if err != nil {
+			return nil, err
+		}
+
+		if key.ServiceName == "*" {
+			services = services[:0]
+		}
+
+		for _, providerId := range providerIds {
+			provider, err := GetService(dr.ctx, key.Tenant, providerId)
+			if err != nil {
+				util.Logger().Warnf(nil, "Provider does not exist, %s/%s/%s",
+					key.AppId, key.ServiceName, key.Version)
+				continue
+			}
+			if provider == nil {
+				continue
+			}
+			if op.NonSelf && providerId == dr.consumer.ServiceId {
+				continue
+			}
+			services = append(services, provider)
+		}
+
+		if key.ServiceName == "*" {
+			break
+		}
+	}
+	return services, nil
+}
+
+func (dr *DependencyRelation) GetDependencyProviderIds() ([]string, error) {
+	keys, err := dr.getProviderKeys()
+	if err != nil {
+		return nil, err
+	}
+	return dr.getDependencyProviderIds(keys)
+}
+
+func (dr *DependencyRelation) getProviderKeys() ([]*pb.MicroServiceKey, error) {
+	if dr.consumer == nil {
+		util.LOGGER.Infof("dr.consumer is nil ------->")
+		return nil, fmt.Errorf("Invalid consumer")
+	}
+	consumerMicroServiceKey := pb.MicroServiceToKey(dr.domainProject, dr.consumer)
+
+	conKey := apt.GenerateConsumerDependencyRuleKey(dr.domainProject, consumerMicroServiceKey)
+	consumerDependency, err := TransferToMicroServiceDependency(dr.ctx, conKey)
+	if err != nil {
+		return nil, err
+	}
+	return consumerDependency.Dependency, nil
+}
+
+func (dr *DependencyRelation) getDependencyProviderIds(providerRules []*pb.MicroServiceKey) ([]string, error) {
+	provideServiceIds := make([]string, 0, len(providerRules))
+	for _, provider := range providerRules {
+		serviceIds, err := dr.parseDependencyRule(provider)
+		switch {
+		case provider.ServiceName == "*":
+			if err != nil {
+				util.Logger().Errorf(err, "Add dependency failed, rely all service: get all services failed.")
+				return provideServiceIds, err
+			}
+			return serviceIds, nil
+		default:
+			if err != nil {
+				util.Logger().Errorf(err, "Get providerIds failed, service: %s/%s/%s",
+					provider.AppId, provider.ServiceName, provider.Version)
+				return provideServiceIds, err
+			}
+			if len(serviceIds) == 0 {
+				util.Logger().Warnf(nil, "Get providerIds is empty, service: %s/%s/%s does not exist",
+					provider.AppId, provider.ServiceName, provider.Version)
+				continue
+			}
+			provideServiceIds = append(provideServiceIds, serviceIds...)
+		}
+	}
+	return provideServiceIds, nil
+}
+
+func (dr *DependencyRelation) parseDependencyRule(dependencyRule *pb.MicroServiceKey) (serviceIds []string, err error) {
+	opts := FromContext(dr.ctx)
+	switch {
+	case dependencyRule.ServiceName == "*":
+		util.Logger().Infof("Rely all service, * type, consumerId %s", dr.consumer.ServiceId)
+		splited := strings.Split(apt.GenerateServiceIndexKey(dependencyRule), "/")
+		allServiceKey := util.StringJoin(splited[:len(splited)-3], "/") + "/"
+		sopts := append(opts,
+			registry.WithStrKey(allServiceKey),
+			registry.WithPrefix())
+		resp, err := backend.Store().Service().Search(dr.ctx, sopts...)
+		if err != nil {
+			return nil, err
+		}
+
+		for _, kv := range resp.Kvs {
+			serviceIds = append(serviceIds, kv.Value.(string))
+		}
+	default:
+		serviceIds, err = FindServiceIds(dr.ctx, dependencyRule.Version, dependencyRule)
+	}
+	return
+}
+
+func (dr *DependencyRelation) GetDependencyConsumers(opts ...DependencyRelationFilterOption) ([]*pb.MicroService, error) {
+	consumerDependAllList, err := dr.getDependencyConsumersOfProvider()
+	if err != nil {
+		util.Logger().Errorf(err, "Get consumers of provider rule failed, %s", dr.provider.ServiceId)
+		return nil, err
+	}
+	consumers := make([]*pb.MicroService, 0)
+	op := toDependencyRelationFilterOpt(opts...)
+	for _, consumer := range consumerDependAllList {
+		if op.SameDomainProject && consumer.Tenant != dr.domainProject {
+			continue
+		}
+
+		service, err := dr.getServiceByMicroServiceKey(consumer)
+		if err != nil {
+			return nil, err
+		}
+		if service == nil {
+			util.Logger().Warnf(nil, "Consumer does not exist, %v", consumer)
+			continue
+		}
+
+		if op.NonSelf && service.ServiceId == dr.provider.ServiceId {
+			continue
+		}
+
+		consumers = append(consumers, service)
+	}
+	return consumers, nil
+}
+
+func (dr *DependencyRelation) getServiceByMicroServiceKey(service *pb.MicroServiceKey) (*pb.MicroService, error) {
+	serviceId, err := GetServiceId(dr.ctx, service)
+	if err != nil {
+		return nil, err
+	}
+	if len(serviceId) == 0 {
+		util.Logger().Warnf(nil, "Service not exist,%v", service)
+		return nil, nil
+	}
+	return GetService(dr.ctx, service.Tenant, serviceId)
+}
+
+func (dr *DependencyRelation) GetDependencyConsumerIds() ([]string, error) {
+	consumerDependAllList, err := dr.getDependencyConsumersOfProvider()
+	if err != nil {
+		return nil, err
+	}
+	consumerIds := make([]string, 0, len(consumerDependAllList))
+	for _, consumer := range consumerDependAllList {
+		consumerId, err := GetServiceId(dr.ctx, consumer)
+		if err != nil {
+			util.Logger().Errorf(err, "Get consumer failed, %v", consumer)
+			return nil, err
+		}
+		if len(consumerId) == 0 {
+			util.Logger().Warnf(nil, "Get consumer not exist, %v", consumer)
+			continue
+		}
+		consumerIds = append(consumerIds, consumerId)
+	}
+	return consumerIds, nil
+
+}
+
+func (dr *DependencyRelation) getDependencyConsumersOfProvider() ([]*pb.MicroServiceKey, error) {
+	if dr.provider == nil {
+		util.LOGGER.Infof("dr.provider is nil ------->")
+		return nil, fmt.Errorf("Invalid provider")
+	}
+	providerService := pb.MicroServiceToKey(dr.domainProject, dr.provider)
+	consumerDependAllList, err := dr.getConsumerOfDependAllServices()
+	if err != nil {
+		util.Logger().Errorf(err, "Get consumer that depend on all services failed, %s", dr.provider.ServiceId)
+		return nil, err
+	}
+
+	consumerDependList, err := dr.getConsumerOfSameServiceNameAndAppId(providerService)
+	if err != nil {
+		util.Logger().Errorf(err, "Get consumer that depend on same serviceName and appid rule failed, %s",
+			dr.provider.ServiceId)
+		return nil, err
+	}
+	consumerDependAllList = append(consumerDependAllList, consumerDependList...)
+	return consumerDependAllList, nil
+}
+
+func (dr *DependencyRelation) getConsumerOfDependAllServices() ([]*pb.MicroServiceKey, error) {
+	providerService := pb.MicroServiceToKey(dr.domainProject, dr.provider)
+	providerService.ServiceName = "*"
+	relyAllKey := apt.GenerateProviderDependencyRuleKey(dr.domainProject, providerService)
+	opts := append(FromContext(dr.ctx), registry.WithStrKey(relyAllKey))
+	rsp, err := backend.Store().DependencyRule().Search(dr.ctx, opts...)
+	if err != nil {
+		util.Logger().Errorf(err, "get consumer that rely all service failed.")
+		return nil, err
+	}
+	if len(rsp.Kvs) != 0 {
+		util.Logger().Infof("consumer that rely all service exist.ServiceName: %s.", dr.provider.ServiceName)
+		return rsp.Kvs[0].Value.(*pb.MicroServiceDependency).Dependency, nil
+	}
+	return nil, nil
+}
+
+func (dr *DependencyRelation) getConsumerOfSameServiceNameAndAppId(provider *pb.MicroServiceKey) ([]*pb.MicroServiceKey, error) {
+	providerVersion := provider.Version
+	provider.Version = ""
+	prefix := apt.GenerateProviderDependencyRuleKey(dr.domainProject, provider)
+	provider.Version = providerVersion
+
+	opts := append(FromContext(dr.ctx),
+		registry.WithStrKey(prefix),
+		registry.WithPrefix())
+	rsp, err := backend.Store().DependencyRule().Search(dr.ctx, opts...)
+	if err != nil {
+		util.Logger().Errorf(err, "get all dependency rule failed: provider rule key %v.", provider)
+		return nil, err
+	}
+
+	allConsumers := make([]*pb.MicroServiceKey, 0, len(rsp.Kvs))
+	var latestServiceId []string
+
+	for _, kv := range rsp.Kvs {
+		providerVersionRuleArr := strings.Split(util.BytesToStringWithNoCopy(kv.Key), "/")
+		providerVersionRule := providerVersionRuleArr[len(providerVersionRuleArr)-1]
+		if providerVersionRule == "latest" {
+			if latestServiceId == nil {
+				latestServiceId, err = FindServiceIds(dr.ctx, providerVersionRule, provider)
+				if err != nil {
+					util.Logger().Errorf(err, "Get latest service failed.")
+					return nil, err
+				}
+			}
+			if len(latestServiceId) == 0 {
+				util.Logger().Infof("%s 's providerId is empty,no this service.", provider.ServiceName)
+				continue
+			}
+			if dr.provider.ServiceId != latestServiceId[0] {
+				continue
+			}
+
+		} else {
+			if !VersionMatchRule(providerVersion, providerVersionRule) {
+				continue
+			}
+		}
+
+		util.Logger().Debugf("providerETCD is %s", providerVersionRuleArr)
+		allConsumers = append(allConsumers, kv.Value.(*pb.MicroServiceDependency).Dependency...)
+	}
+	return allConsumers, nil
+}
+
+func NewProviderDependencyRelation(ctx context.Context, domainProject string, provider *pb.MicroService) *DependencyRelation {
+	return NewDependencyRelation(ctx, domainProject, nil, provider)
+}
+
+func NewConsumerDependencyRelation(ctx context.Context, domainProject string, consumer *pb.MicroService) *DependencyRelation {
+	return NewDependencyRelation(ctx, domainProject, consumer, nil)
+}
+
+func NewDependencyRelation(ctx context.Context, domainProject string, consumer *pb.MicroService, provider *pb.MicroService) *DependencyRelation {
+	return &DependencyRelation{
+		ctx:           ctx,
+		domainProject: domainProject,
+		consumer:      consumer,
+		provider:      provider,
+	}
+}
diff --git a/server/service/util/dependency_test.go b/server/service/util/dependency_test.go
index ab63fd7..af7b3a4 100644
--- a/server/service/util/dependency_test.go
+++ b/server/service/util/dependency_test.go
@@ -17,7 +17,6 @@
 package util
 
 import (
-	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
 	"github.com/apache/incubator-servicecomb-service-center/server/core/proto"
 	"golang.org/x/net/context"
 	"testing"
@@ -28,51 +27,10 @@ func TestDeleteDependencyForService(t *testing.T) {
 	if err != nil {
 		t.Fatalf(`DeleteDependencyForDeleteService failed`)
 	}
-
-	_, err = updateProviderDependencyRuleUtil(
-		&proto.MicroServiceDependency{
-			Dependency: []*proto.MicroServiceKey{
-				{AppId: "a"},
-			},
-		},
-		&proto.MicroServiceKey{
-			AppId: "a",
-		}, "")
-	if err != nil {
-		t.Fatalf(`deleteDependencyRuleUtil with the same deps failed`)
-	}
-
-	_, err = updateProviderDependencyRuleUtil(
-		&proto.MicroServiceDependency{
-			Dependency: []*proto.MicroServiceKey{
-				{AppId: "b"},
-			},
-		},
-		&proto.MicroServiceKey{
-			AppId: "a",
-		}, "")
-	if err != nil {
-		t.Fatalf(`deleteDependencyRuleUtil failed`)
-	}
-
-	_, err = deleteConsumerDepOfProviderRule(context.Background(), "", &proto.MicroServiceKey{}, &proto.MicroServiceKey{})
-	if err == nil {
-		t.Fatalf(`deleteConsumerDepOfProviderRule failed`)
-	}
-
-	_, err = deleteDepRuleUtil("", &proto.MicroServiceDependency{}, &proto.MicroServiceKey{})
-	if err != nil {
-		t.Fatalf(`deleteDepRuleUtil failed`)
-	}
 }
 
 func TestTransferToMicroServiceDependency(t *testing.T) {
-	_, err := TransferToMicroServiceDependency(util.SetContext(context.Background(), "cacheOnly", "1"), "")
-	if err != nil {
-		t.Fatalf(`TransferToMicroServiceDependency WithCacheOnly failed`)
-	}
-
-	_, err = TransferToMicroServiceDependency(context.Background(), "")
+	_, err := TransferToMicroServiceDependency(context.Background(), "")
 	if err == nil {
 		t.Fatalf(`TransferToMicroServiceDependency failed`)
 	}
@@ -114,11 +72,6 @@ func TestCreateDependencyRule(t *testing.T) {
 		t.Fatalf(`AddServiceVersionRule failed`)
 	}
 
-	_, err = addDepRuleUtil("", &proto.MicroServiceDependency{}, &proto.MicroServiceKey{})
-	if err != nil {
-		t.Fatalf(`addDepRuleUtil failed`)
-	}
-
 	b, err := containServiceDependency([]*proto.MicroServiceKey{
 		{AppId: "a"},
 	}, &proto.MicroServiceKey{
@@ -154,33 +107,6 @@ func TestCreateDependencyRule(t *testing.T) {
 	if !ok {
 		t.Fatalf(`diffServiceVersion failed`)
 	}
-
-	ok = isDependencyAll(&proto.MicroServiceDependency{})
-	if ok {
-		t.Fatalf(`isDependencyAll not * failed`)
-	}
-
-	ok = isDependencyAll(&proto.MicroServiceDependency{
-		Dependency: []*proto.MicroServiceKey{
-			{
-				ServiceName: "*",
-			},
-		},
-	})
-	if !ok {
-		t.Fatalf(`isDependencyAll * failed`)
-	}
-
-	ok = isExist([]*proto.MicroServiceKey{
-		{
-			ServiceName: "*",
-		},
-	}, &proto.MicroServiceKey{
-		ServiceName: "*",
-	})
-	if !ok {
-		t.Fatalf(`isExist failed`)
-	}
 }
 
 func TestBadParamsResponse(t *testing.T) {
@@ -248,23 +174,13 @@ func TestParamsChecker(t *testing.T) {
 }
 
 func TestServiceDependencyRuleExist(t *testing.T) {
-	_, err := DependencyRuleExist(util.SetContext(context.Background(), "cacheOnly", "1"), &proto.MicroServiceKey{}, &proto.MicroServiceKey{})
-	if err != nil {
-		t.Fatalf(`ServiceDependencyRuleExist WithCacheOnly failed`)
-	}
-
-	_, err = DependencyRuleExist(context.Background(), &proto.MicroServiceKey{}, &proto.MicroServiceKey{})
+	_, err := DependencyRuleExist(context.Background(), &proto.MicroServiceKey{}, &proto.MicroServiceKey{})
 	if err == nil {
 		t.Fatalf(`ServiceDependencyRuleExist failed`)
 	}
 }
 
 func TestUpdateServiceForAddDependency(t *testing.T) {
-	_, _, err := updateDepRuleUtil("", &proto.MicroServiceDependency{}, &proto.MicroServiceKey{})
-	if err != nil {
-		t.Fatalf(`updateDepRuleUtil failed`)
-	}
-
 	old := isNeedUpdate([]*proto.MicroServiceKey{
 		{
 			AppId:       "a",
@@ -281,33 +197,6 @@ func TestUpdateServiceForAddDependency(t *testing.T) {
 	}
 }
 
-func TestFilter(t *testing.T) {
-	_, _, err := getConsumerIdsWithFilter(context.Background(), "", &proto.MicroService{}, noFilter)
-	if err != nil {
-		t.Fatalf(`getConsumerIdsWithFilter failed`)
-	}
-
-	_, _, err = filterConsumerIds(context.Background(), []string{}, noFilter)
-	if err != nil {
-		t.Fatalf(`filterConsumerIds invalid failed`)
-	}
-
-	_, _, err = filterConsumerIds(context.Background(), []string{"a"}, noFilter)
-	if err != nil {
-		t.Fatalf(`filterConsumerIds invalid failed`)
-	}
-
-	rf := RuleFilter{
-		DomainProject: "",
-		Provider:      &proto.MicroService{},
-		ProviderRules: []*proto.ServiceRule{},
-	}
-	_, _, err = filterConsumerIds(context.Background(), []string{"a"}, rf.Filter)
-	if err != nil {
-		t.Fatalf(`filterConsumerIds invalid failed`)
-	}
-}
-
 func TestDependency(t *testing.T) {
 	d := &Dependency{
 		removedDependencyRuleList: []*proto.MicroServiceKey{
@@ -327,47 +216,6 @@ func TestDependency(t *testing.T) {
 	dr := &DependencyRelation{
 		provider: &proto.MicroService{},
 		consumer: &proto.MicroService{},
-		ctx:      util.SetContext(context.Background(), "cacheOnly", "1"),
-	}
-	_, err = dr.GetDependencyProviders()
-	if err != nil {
-		t.Fatalf(`DependencyRelation_GetDependencyProviders failed`)
-	}
-
-	_, err = dr.getDependencyProviderIds([]*proto.MicroServiceKey{
-		{ServiceName: "*"},
-		{ServiceName: "a", Version: "1.0.0"},
-		{ServiceName: "b", Version: "latest"},
-	})
-	if err != nil {
-		t.Fatalf(`DependencyRelation_getDependencyProviderIds * WithCacheOnly failed`)
-	}
-	_, err = dr.getDependencyProviderIds([]*proto.MicroServiceKey{
-		{ServiceName: "a", Version: "1.0.0"},
-		{ServiceName: "b", Version: "latest"},
-	})
-	if err != nil {
-		t.Fatalf(`DependencyRelation_getDependencyProviderIds WithCacheOnly failed`)
-	}
-
-	_, err = dr.GetDependencyConsumers()
-	if err != nil {
-		t.Fatalf(`DependencyRelation_GetDependencyConsumers WithCacheOnly failed`)
-	}
-
-	_, err = dr.getServiceByMicroServiceKey(&proto.MicroServiceKey{})
-	if err != nil {
-		t.Fatalf(`DependencyRelation_getServiceByMicroServiceKey WithCacheOnly failed`)
-	}
-
-	_, err = dr.getConsumerOfSameServiceNameAndAppId(&proto.MicroServiceKey{})
-	if err != nil {
-		t.Fatalf(`DependencyRelation_getConsumerOfSameServiceNameAndAppId WithCacheOnly failed`)
-	}
-
-	dr = &DependencyRelation{
-		provider: &proto.MicroService{},
-		consumer: &proto.MicroService{},
 		ctx:      context.Background(),
 	}
 	_, err = dr.getDependencyProviderIds([]*proto.MicroServiceKey{
@@ -407,6 +255,10 @@ func TestDependency(t *testing.T) {
 	if err == nil {
 		t.Fatalf(`DependencyRelation_getDependencyConsumersOfProvider failed`)
 	}
+	_, err = dr.GetDependencyProviders()
+	if err == nil {
+		t.Fatalf(`DependencyRelation_GetDependencyProviders failed`)
+	}
 }
 
 func TestDependencyRelationFilterOpt(t *testing.T) {
@@ -418,3 +270,10 @@ func TestDependencyRelationFilterOpt(t *testing.T) {
 		t.Fatalf(`toDependencyRelationFilterOpt failed`)
 	}
 }
+
+func TestGetConsumerIdsWithFilter(t *testing.T) {
+	_, _, err := getConsumerIdsWithFilter(context.Background(), "", &proto.MicroService{}, nil)
+	if err == nil {
+		t.Fatalf(`TestGetConsumerIdsWithFilter failed`)
+	}
+}
diff --git a/server/service/util/dependency_util.go b/server/service/util/dependency_util.go
new file mode 100644
index 0000000..f7f4aae
--- /dev/null
+++ b/server/service/util/dependency_util.go
@@ -0,0 +1,427 @@
+/*
+ * 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 (
+	"encoding/json"
+	"errors"
+	"fmt"
+	"github.com/apache/incubator-servicecomb-service-center/pkg/util"
+	apt "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"
+	"github.com/apache/incubator-servicecomb-service-center/server/infra/registry"
+	"golang.org/x/net/context"
+	"strings"
+)
+
+func GetConsumerIds(ctx context.Context, domainProject string, provider *pb.MicroService) ([]string, error) {
+	// 查询所有consumer
+	dr := NewProviderDependencyRelation(ctx, domainProject, provider)
+	consumerIds, err := dr.GetDependencyConsumerIds()
+	if err != nil {
+		util.Logger().Errorf(err, "Get dependency consumerIds failed.%s", provider.ServiceId)
+		return nil, err
+	}
+	return consumerIds, nil
+}
+
+func GetProviderIds(ctx context.Context, domainProject string, consumer *pb.MicroService) ([]string, error) {
... 1838 lines suppressed ...