diff --git a/CHANGE.md b/CHANGE.md
index 4a75ad3519fce9e8fe52fabc516d4a66c6f7b135..90cb5a1443e8062125cbb9f2b3cc0ac1cf759d06 100644
--- a/CHANGE.md
+++ b/CHANGE.md
@@ -1,6 +1,34 @@
 # Release Notes
 ---
 
+## 1.5.1
+
+### New Features
+- [Add dynamic tag router](https://github.com/apache/dubbo-go/pull/703)
+- [Add TLS support](https://github.com/apache/dubbo-go/pull/685)
+- [Add Nearest first for multiple registry](https://github.com/apache/dubbo-go/pull/659)
+- [Add application and service level router](https://github.com/apache/dubbo-go/pull/662)
+- [Add dynamic tag router](https://github.com/apache/dubbo-go/pull/665)
+
+### Enhancement
+- [Avoid init the log twice](https://github.com/apache/dubbo-go/pull/719)
+- [Correct words and format codes](https://github.com/apache/dubbo-go/pull/704)
+- [Change log stack level from warn to error](https://github.com/apache/dubbo-go/pull/702)
+- [Optimize remotes configuration](https://github.com/apache/dubbo-go/pull/687)
+
+### Bugfixes
+- [Fix register service instance after provider config load](https://github.com/apache/dubbo-go/pull/694)
+- [Fix call subscribe function asynchronously](https://github.com/apache/dubbo-go/pull/721)
+- [Fix tag router rule copy](https://github.com/apache/dubbo-go/pull/721)
+- [Fix nacos unit test failed](https://github.com/apache/dubbo-go/pull/705)
+- [Fix can not inovke nacos destroy when graceful shutdown](https://github.com/apache/dubbo-go/pull/689)
+- [Fix zk lost event](https://github.com/apache/dubbo-go/pull/692)
+- [Fix k8s ut bug](https://github.com/apache/dubbo-go/pull/693)
+
+Milestone: [https://github.com/apache/dubbo-go/milestone/2?closed=1](https://github.com/apache/dubbo-go/milestone/2?closed=1)
+
+Project: [https://github.com/apache/dubbo-go/projects/8](https://github.com/apache/dubbo-go/projects/8)
+
 ## 1.5.0
 
 ### New Features
diff --git a/README.md b/README.md
index 7df7e7973a7e3e808f4b8feb4f26b98c0f21e9ef..9e1edd3af1cd8957b1daa9b9fe2cadf121bc2d6d 100644
--- a/README.md
+++ b/README.md
@@ -16,6 +16,8 @@ Apache License, Version 2.0
 
 ## Release note ##
 
+[v1.5.1 - Aug 23, 2020](https://github.com/apache/dubbo-go/releases/tag/v1.5.1)
+
 [v1.5.0 - July 24, 2020](https://github.com/apache/dubbo-go/releases/tag/v1.5.0)
 
 [v1.4.0 - Mar 17, 2020](https://github.com/apache/dubbo-go/releases/tag/v1.4.0)
diff --git a/cluster/directory/static_directory.go b/cluster/directory/static_directory.go
index 87f51356495dbd0a956c42bf4f34022b4d21ad4d..6d75dff5da09cc18828c9105d257c8f80b8de885 100644
--- a/cluster/directory/static_directory.go
+++ b/cluster/directory/static_directory.go
@@ -39,10 +39,13 @@ func NewStaticDirectory(invokers []protocol.Invoker) *staticDirectory {
 	if len(invokers) > 0 {
 		url = invokers[0].GetUrl()
 	}
-	return &staticDirectory{
+	dir := &staticDirectory{
 		BaseDirectory: NewBaseDirectory(&url),
 		invokers:      invokers,
 	}
+
+	dir.routerChain.SetInvokers(invokers)
+	return dir
 }
 
 //for-loop invokers ,if all invokers is available ,then it means directory is available
@@ -69,7 +72,7 @@ func (dir *staticDirectory) List(invocation protocol.Invocation) []protocol.Invo
 		return invokers
 	}
 	dirUrl := dir.GetUrl()
-	return routerChain.Route(invokers, &dirUrl, invocation)
+	return routerChain.Route(&dirUrl, invocation)
 }
 
 // Destroy Destroy
@@ -92,6 +95,7 @@ func (dir *staticDirectory) BuildRouterChain(invokers []protocol.Invoker) error
 	if e != nil {
 		return e
 	}
+	routerChain.SetInvokers(dir.invokers)
 	dir.SetRouterChain(routerChain)
 	return nil
 }
diff --git a/cluster/router/chain.go b/cluster/router/chain.go
new file mode 100644
index 0000000000000000000000000000000000000000..3614d0a5a3d6cfb462ef63149ae99da2c4541b8d
--- /dev/null
+++ b/cluster/router/chain.go
@@ -0,0 +1,32 @@
+/*
+ * 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 router
+
+import (
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/protocol"
+)
+
+// Chain
+type Chain interface {
+	Route(*common.URL, protocol.Invocation) []protocol.Invoker
+	// Refresh invokers
+	SetInvokers([]protocol.Invoker)
+	// AddRouters Add routers
+	AddRouters([]PriorityRouter)
+}
diff --git a/cluster/router/chain/chain.go b/cluster/router/chain/chain.go
index 8746c1daf7f878a066ea005f910520e07c28318c..952aedf92d70d92b3b029a9809826295e1cc7dc5 100644
--- a/cluster/router/chain/chain.go
+++ b/cluster/router/chain/chain.go
@@ -18,9 +18,10 @@
 package chain
 
 import (
-	"math"
 	"sort"
 	"sync"
+	"sync/atomic"
+	"time"
 )
 
 import (
@@ -30,11 +31,18 @@ import (
 import (
 	"github.com/apache/dubbo-go/cluster/router"
 	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/common/constant"
 	"github.com/apache/dubbo-go/common/extension"
 	"github.com/apache/dubbo-go/common/logger"
 	"github.com/apache/dubbo-go/protocol"
 )
 
+const (
+	timeInterval   = 5 * time.Second
+	timeThreshold  = 2 * time.Second
+	countThreshold = 5
+)
+
 // RouterChain Router chain
 type RouterChain struct {
 	// Full list of addresses from registry, classified by method name.
@@ -48,30 +56,38 @@ type RouterChain struct {
 	mutex sync.RWMutex
 
 	url common.URL
+
+	// The times of address notification since last update for address cache
+	count int64
+	// The timestamp of last update for address cache
+	last time.Time
+	// Channel for notify to update the address cache
+	notify chan struct{}
+	// Address cache
+	cache atomic.Value
 }
 
 // Route Loop routers in RouterChain and call Route method to determine the target invokers list.
-func (c *RouterChain) Route(invokers []protocol.Invoker, url *common.URL, invocation protocol.Invocation) []protocol.Invoker {
-	finalInvokers := invokers
-	l := len(c.routers)
-	rs := make([]router.PriorityRouter, l, int(math.Ceil(float64(l)*1.2)))
-	c.mutex.RLock()
-	copy(rs, c.routers)
-	c.mutex.RUnlock()
+func (c *RouterChain) Route(url *common.URL, invocation protocol.Invocation) []protocol.Invoker {
+	cache := c.loadCache()
+	if cache == nil {
+		c.mutex.RLock()
+		defer c.mutex.RUnlock()
+		return c.invokers
+	}
 
-	for _, r := range rs {
-		finalInvokers = r.Route(finalInvokers, url, invocation)
+	bitmap := cache.bitmap
+	for _, r := range c.copyRouters() {
+		bitmap = r.Route(bitmap, cache, url, invocation)
 	}
-	return finalInvokers
-}
 
-// SetInvokers notify router chain of the initial addresses from registry at the first time. Notify whenever addresses in registry change.
-func (c *RouterChain) SetInvokers(invokers []protocol.Invoker) {
-	for _, r := range c.routers {
-		if notifyRouter, ok := r.(router.NotifyRouter); ok {
-			notifyRouter.Notify(invokers)
-		}
+	indexes := bitmap.ToArray()
+	finalInvokers := make([]protocol.Invoker, len(indexes))
+	for i, index := range indexes {
+		finalInvokers[i] = cache.invokers[index]
 	}
+
+	return finalInvokers
 }
 
 // AddRouters Add routers to router chain
@@ -88,6 +104,116 @@ func (c *RouterChain) AddRouters(routers []router.PriorityRouter) {
 	c.routers = newRouters
 }
 
+// SetInvokers receives updated invokers from registry center. If the times of notification exceeds countThreshold and
+// time interval exceeds timeThreshold since last cache update, then notify to update the cache.
+func (c *RouterChain) SetInvokers(invokers []protocol.Invoker) {
+	c.mutex.Lock()
+	c.invokers = invokers
+	c.mutex.Unlock()
+
+	c.count++
+	now := time.Now()
+	if c.count >= countThreshold && now.Sub(c.last) >= timeThreshold {
+		c.last = now
+		c.count = 0
+		go func() {
+			c.notify <- struct{}{}
+		}()
+	}
+}
+
+// loop listens on events to update the address cache when it's necessary, either when it receives notification
+// from address update, or when timeInterval exceeds.
+func (c *RouterChain) loop() {
+	for {
+		ticker := time.NewTicker(timeInterval)
+		select {
+		case <-ticker.C:
+			c.buildCache()
+		case <-c.notify:
+			c.buildCache()
+		}
+	}
+}
+
+// copyRouters make a snapshot copy from RouterChain's router list.
+func (c *RouterChain) copyRouters() []router.PriorityRouter {
+	c.mutex.RLock()
+	defer c.mutex.RUnlock()
+	ret := make([]router.PriorityRouter, 0, len(c.routers))
+	ret = append(ret, c.routers...)
+	return ret
+}
+
+// copyInvokers copies a snapshot of the received invokers.
+func (c *RouterChain) copyInvokers() []protocol.Invoker {
+	c.mutex.RLock()
+	defer c.mutex.RUnlock()
+	if c.invokers == nil || len(c.invokers) == 0 {
+		return nil
+	}
+	ret := make([]protocol.Invoker, 0, len(c.invokers))
+	ret = append(ret, c.invokers...)
+	return ret
+}
+
+// loadCache loads cache from sync.Value to guarantee the visibility
+func (c *RouterChain) loadCache() *InvokerCache {
+	v := c.cache.Load()
+	if v == nil {
+		return nil
+	}
+
+	return v.(*InvokerCache)
+}
+
+// copyInvokerIfNecessary compares chain's invokers copy and cache's invokers copy, to avoid copy as much as possible
+func (c *RouterChain) copyInvokerIfNecessary(cache *InvokerCache) []protocol.Invoker {
+	var invokers []protocol.Invoker
+	if cache != nil {
+		invokers = cache.invokers
+	}
+
+	c.mutex.RLock()
+	defer c.mutex.RUnlock()
+	if isInvokersChanged(invokers, c.invokers) {
+		invokers = c.copyInvokers()
+	}
+	return invokers
+}
+
+// buildCache builds address cache with the new invokers for all poolable routers.
+func (c *RouterChain) buildCache() {
+	origin := c.loadCache()
+	invokers := c.copyInvokerIfNecessary(origin)
+	if invokers == nil || len(invokers) == 0 {
+		return
+	}
+
+	var (
+		mutex sync.Mutex
+		wg    sync.WaitGroup
+	)
+
+	cache := BuildCache(invokers)
+	for _, r := range c.copyRouters() {
+		if p, ok := r.(router.Poolable); ok {
+			wg.Add(1)
+			go func(p router.Poolable) {
+				defer wg.Done()
+				pool, info := poolRouter(p, origin, invokers)
+				mutex.Lock()
+				defer mutex.Unlock()
+				cache.pools[p.Name()] = pool
+				cache.metadatas[p.Name()] = info
+			}(p)
+		}
+	}
+	wg.Wait()
+
+	c.cache.Store(cache)
+}
+
 // URL Return URL in RouterChain
 func (c *RouterChain) URL() common.URL {
 	return c.url
@@ -118,14 +244,62 @@ func NewRouterChain(url *common.URL) (*RouterChain, error) {
 	chain := &RouterChain{
 		builtinRouters: routers,
 		routers:        newRouters,
+		last:           time.Now(),
+		notify:         make(chan struct{}),
 	}
 	if url != nil {
 		chain.url = *url
 	}
 
+	go chain.loop()
 	return chain, nil
 }
 
+// poolRouter calls poolable router's Pool() to create new address pool and address metadata if necessary.
+// If the corresponding cache entry exists, and the poolable router answers no need to re-pool (possibly because its
+// rule doesn't change), and the address list doesn't change, then the existing data will be re-used.
+func poolRouter(p router.Poolable, origin *InvokerCache, invokers []protocol.Invoker) (router.AddrPool, router.AddrMetadata) {
+	name := p.Name()
+	if isCacheMiss(origin, name) || p.ShouldPool() || &(origin.invokers) != &invokers {
+		logger.Debugf("build address cache for router %q", name)
+		return p.Pool(invokers)
+	}
+
+	logger.Debugf("reuse existing address cache for router %q", name)
+	return origin.pools[name], origin.metadatas[name]
+}
+
+// isCacheMiss checks if the corresponding cache entry for a poolable router has already existed.
+// False returns when the cache is nil, or cache's pool is nil, or cache's invokers snapshot is nil, or the entry
+// doesn't exist.
+func isCacheMiss(cache *InvokerCache, key string) bool {
+	if cache == nil || cache.pools == nil || cache.invokers == nil || cache.pools[key] == nil {
+		return true
+	}
+	return false
+}
+
+// isInvokersChanged compares new invokers on the right changes, compared with the old invokers on the left.
+func isInvokersChanged(left []protocol.Invoker, right []protocol.Invoker) bool {
+	if len(right) != len(left) {
+		return true
+	}
+
+	for _, r := range right {
+		found := false
+		for _, l := range left {
+			if common.IsEquals(l.GetUrl(), r.GetUrl(), constant.TIMESTAMP_KEY, constant.REMOTE_TIMESTAMP_KEY) {
+				found = true
+				break
+			}
+		}
+		if !found {
+			return true
+		}
+	}
+	return false
+}
+
 // sortRouter Sort router instance by priority with stable algorithm
 func sortRouter(routers []router.PriorityRouter) {
 	sort.Stable(byPriority(routers))
diff --git a/cluster/router/chain/chain_test.go b/cluster/router/chain/chain_test.go
index dec03894ebc73e315c2bb161911bdc67235e1ebb..5a9f2d1c74db847df56b1ebab609b65c0cbd7484 100644
--- a/cluster/router/chain/chain_test.go
+++ b/cluster/router/chain/chain_test.go
@@ -172,12 +172,12 @@ func TestRouterChainRoute(t *testing.T) {
 	invokers := []protocol.Invoker{}
 	dubboURL, _ := common.NewURL(fmt.Sprintf(dubboForamt, test1234IP, port20000))
 	invokers = append(invokers, protocol.NewBaseInvoker(dubboURL))
-
 	chain.SetInvokers(invokers)
+	chain.buildCache()
 
 	targetURL, _ := common.NewURL(fmt.Sprintf(consumerFormat, test1111IP))
 	inv := &invocation.RPCInvocation{}
-	finalInvokers := chain.Route(invokers, &targetURL, inv)
+	finalInvokers := chain.Route(&targetURL, inv)
 
 	assert.Equal(t, 1, len(finalInvokers))
 }
@@ -213,10 +213,12 @@ conditions:
 	invokers := []protocol.Invoker{}
 	dubboURL, _ := common.NewURL(fmt.Sprintf(dubboForamt, test1234IP, port20000))
 	invokers = append(invokers, protocol.NewBaseInvoker(dubboURL))
+	chain.SetInvokers(invokers)
+	chain.buildCache()
 
 	targetURL, _ := common.NewURL(fmt.Sprintf(consumerFormat, test1111IP))
 	inv := &invocation.RPCInvocation{}
-	finalInvokers := chain.Route(invokers, &targetURL, inv)
+	finalInvokers := chain.Route(&targetURL, inv)
 
 	assert.Equal(t, 0, len(finalInvokers))
 }
@@ -236,13 +238,16 @@ func TestRouterChainRouteNoRoute(t *testing.T) {
 
 	url := getConditionRouteUrl(applicationKey)
 	assert.NotNil(t, url)
+
 	invokers := []protocol.Invoker{}
 	dubboURL, _ := common.NewURL(fmt.Sprintf(dubboForamt, test1234IP, port20000))
 	invokers = append(invokers, protocol.NewBaseInvoker(dubboURL))
+	chain.SetInvokers(invokers)
+	chain.buildCache()
 
 	targetURL, _ := common.NewURL(fmt.Sprintf(consumerFormat, test1111IP))
 	inv := &invocation.RPCInvocation{}
-	finalInvokers := chain.Route(invokers, &targetURL, inv)
+	finalInvokers := chain.Route(&targetURL, inv)
 
 	assert.Equal(t, 0, len(finalInvokers))
 }
diff --git a/cluster/router/chain/invoker_cache.go b/cluster/router/chain/invoker_cache.go
new file mode 100644
index 0000000000000000000000000000000000000000..43cdfa50670f5f09666dbea10a2837d58cafd1b0
--- /dev/null
+++ b/cluster/router/chain/invoker_cache.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 chain
+
+import (
+	"github.com/RoaringBitmap/roaring"
+)
+
+import (
+	"github.com/apache/dubbo-go/cluster/router"
+	"github.com/apache/dubbo-go/cluster/router/utils"
+	"github.com/apache/dubbo-go/protocol"
+)
+
+// Cache caches all addresses relevant info for a snapshot of received invokers. It keeps a snapshot of the received
+// address list, and also keeps address pools and address metadata from routers based on the same address snapshot, if
+// the router implements Poolable.
+type InvokerCache struct {
+	// The snapshot of invokers
+	invokers []protocol.Invoker
+
+	// The bitmap representation for invokers snapshot
+	bitmap *roaring.Bitmap
+
+	// Address pool from routers which implement Poolable
+	pools map[string]router.AddrPool
+
+	// Address metadata from routers which implement Poolable
+	metadatas map[string]router.AddrMetadata
+}
+
+// BuildCache builds address cache from the given invokers.
+func BuildCache(invokers []protocol.Invoker) *InvokerCache {
+	return &InvokerCache{
+		invokers:  invokers,
+		bitmap:    utils.ToBitmap(invokers),
+		pools:     make(map[string]router.AddrPool, 8),
+		metadatas: make(map[string]router.AddrMetadata, 8),
+	}
+}
+
+// GetInvokers get invokers snapshot.
+func (c *InvokerCache) GetInvokers() []protocol.Invoker {
+	return c.invokers
+}
+
+// FindAddrPool finds address pool for a poolable router.
+func (c *InvokerCache) FindAddrPool(p router.Poolable) router.AddrPool {
+	return c.pools[p.Name()]
+}
+
+// FindAddrMeta finds address metadata for a poolable router.
+func (c *InvokerCache) FindAddrMeta(p router.Poolable) router.AddrMetadata {
+	return c.metadatas[p.Name()]
+}
+
+// SetAddrPool sets address pool for a poolable router, for unit test only
+func (c *InvokerCache) SetAddrPool(name string, pool router.AddrPool) {
+	c.pools[name] = pool
+}
+
+// SetAddrMeta sets address metadata for a poolable router, for unit test only
+func (c *InvokerCache) SetAddrMeta(name string, meta router.AddrMetadata) {
+	c.metadatas[name] = meta
+}
diff --git a/cluster/router/condition/factory_test.go b/cluster/router/condition/factory_test.go
index 0f61b39fc71af3aaeffc731974a0fa997503693e..b31f6c7a82ea102c6e0b80d68e43f7b359785544 100644
--- a/cluster/router/condition/factory_test.go
+++ b/cluster/router/condition/factory_test.go
@@ -32,6 +32,9 @@ import (
 )
 
 import (
+	"github.com/apache/dubbo-go/cluster/router"
+	"github.com/apache/dubbo-go/cluster/router/chain"
+	"github.com/apache/dubbo-go/cluster/router/utils"
 	"github.com/apache/dubbo-go/common"
 	"github.com/apache/dubbo-go/common/constant"
 	"github.com/apache/dubbo-go/common/logger"
@@ -180,30 +183,30 @@ func TestRoute_matchFilter(t *testing.T) {
 	router5, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule5))
 	router6, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule6))
 	cUrl, _ := common.NewURL(fmt.Sprintf(factoryConsumerFormat, localIP))
-	fileredInvokers1 := router1.Route(invokers, &cUrl, &invocation.RPCInvocation{})
-	fileredInvokers2 := router2.Route(invokers, &cUrl, &invocation.RPCInvocation{})
-	fileredInvokers3 := router3.Route(invokers, &cUrl, &invocation.RPCInvocation{})
-	fileredInvokers4 := router4.Route(invokers, &cUrl, &invocation.RPCInvocation{})
-	fileredInvokers5 := router5.Route(invokers, &cUrl, &invocation.RPCInvocation{})
-	fileredInvokers6 := router6.Route(invokers, &cUrl, &invocation.RPCInvocation{})
-	assert.Equal(t, 1, len(fileredInvokers1))
-	assert.Equal(t, 0, len(fileredInvokers2))
-	assert.Equal(t, 0, len(fileredInvokers3))
-	assert.Equal(t, 1, len(fileredInvokers4))
-	assert.Equal(t, 2, len(fileredInvokers5))
-	assert.Equal(t, 1, len(fileredInvokers6))
+	ret1 := router1.Route(utils.ToBitmap(invokers), setUpAddrCache(invokers), &cUrl, &invocation.RPCInvocation{})
+	ret2 := router2.Route(utils.ToBitmap(invokers), setUpAddrCache(invokers), &cUrl, &invocation.RPCInvocation{})
+	ret3 := router3.Route(utils.ToBitmap(invokers), setUpAddrCache(invokers), &cUrl, &invocation.RPCInvocation{})
+	ret4 := router4.Route(utils.ToBitmap(invokers), setUpAddrCache(invokers), &cUrl, &invocation.RPCInvocation{})
+	ret5 := router5.Route(utils.ToBitmap(invokers), setUpAddrCache(invokers), &cUrl, &invocation.RPCInvocation{})
+	ret6 := router6.Route(utils.ToBitmap(invokers), setUpAddrCache(invokers), &cUrl, &invocation.RPCInvocation{})
+	assert.Equal(t, 1, len(ret1.ToArray()))
+	assert.Equal(t, 0, len(ret2.ToArray()))
+	assert.Equal(t, 0, len(ret3.ToArray()))
+	assert.Equal(t, 1, len(ret4.ToArray()))
+	assert.Equal(t, 2, len(ret5.ToArray()))
+	assert.Equal(t, 1, len(ret6.ToArray()))
 
 }
 
 func TestRoute_methodRoute(t *testing.T) {
 	inv := invocation.NewRPCInvocationWithOptions(invocation.WithMethodName("getFoo"), invocation.WithParameterTypes([]reflect.Type{}), invocation.WithArguments([]interface{}{}))
 	rule := base64.URLEncoding.EncodeToString([]byte("host !=4.4.4.* & host = 2.2.2.2,1.1.1.1,3.3.3.3 => host = 1.2.3.4"))
-	router, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule))
+	r, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule))
 	url, _ := common.NewURL("consumer://1.1.1.1/com.foo.BarService?methods=setFoo,getFoo,findFoo")
-	matchWhen := router.(*ConditionRouter).MatchWhen(&url, inv)
+	matchWhen := r.(*ConditionRouter).MatchWhen(&url, inv)
 	assert.Equal(t, true, matchWhen)
 	url1, _ := common.NewURL(fmt.Sprintf(factoryConsumerMethodFormat, factory1111Ip))
-	matchWhen = router.(*ConditionRouter).MatchWhen(&url1, inv)
+	matchWhen = r.(*ConditionRouter).MatchWhen(&url1, inv)
 	assert.Equal(t, true, matchWhen)
 	url2, _ := common.NewURL(fmt.Sprintf(factoryConsumerMethodFormat, factory1111Ip))
 	rule2 := base64.URLEncoding.EncodeToString([]byte("methods=getFoo & host!=1.1.1.1 => host = 1.2.3.4"))
@@ -225,9 +228,9 @@ func TestRoute_ReturnFalse(t *testing.T) {
 	inv := &invocation.RPCInvocation{}
 	rule := base64.URLEncoding.EncodeToString([]byte("host = " + localIP + " => false"))
 	curl, _ := common.NewURL(fmt.Sprintf(factoryConsumerFormat, localIP))
-	router, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule))
-	fileredInvokers := router.(*ConditionRouter).Route(invokers, &curl, inv)
-	assert.Equal(t, 0, len(fileredInvokers))
+	r, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule))
+	ret := r.Route(utils.ToBitmap(invokers), setUpAddrCache(invokers), &curl, inv)
+	assert.Equal(t, 0, len(ret.ToArray()))
 }
 
 func TestRoute_ReturnEmpty(t *testing.T) {
@@ -237,9 +240,9 @@ func TestRoute_ReturnEmpty(t *testing.T) {
 	inv := &invocation.RPCInvocation{}
 	rule := base64.URLEncoding.EncodeToString([]byte("host = " + localIP + " => "))
 	curl, _ := common.NewURL(fmt.Sprintf(factoryConsumerFormat, localIP))
-	router, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule))
-	fileredInvokers := router.(*ConditionRouter).Route(invokers, &curl, inv)
-	assert.Equal(t, 0, len(fileredInvokers))
+	r, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule))
+	ret := r.Route(utils.ToBitmap(invokers), setUpAddrCache(invokers), &curl, inv)
+	assert.Equal(t, 0, len(ret.ToArray()))
 }
 
 func TestRoute_ReturnAll(t *testing.T) {
@@ -253,9 +256,9 @@ func TestRoute_ReturnAll(t *testing.T) {
 	inv := &invocation.RPCInvocation{}
 	rule := base64.URLEncoding.EncodeToString([]byte("host = " + localIP + " => " + " host = " + localIP))
 	curl, _ := common.NewURL(fmt.Sprintf(factoryConsumerFormat, localIP))
-	router, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule))
-	fileredInvokers := router.(*ConditionRouter).Route(invokers, &curl, inv)
-	assert.Equal(t, invokers, fileredInvokers)
+	r, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule))
+	ret := r.Route(utils.ToBitmap(invokers), setUpAddrCache(invokers), &curl, inv)
+	assert.Equal(t, len(invokers), len(ret.ToArray()))
 }
 
 func TestRoute_HostFilter(t *testing.T) {
@@ -270,11 +273,11 @@ func TestRoute_HostFilter(t *testing.T) {
 	inv := &invocation.RPCInvocation{}
 	rule := base64.URLEncoding.EncodeToString([]byte("host = " + localIP + " => " + " host = " + localIP))
 	curl, _ := common.NewURL(fmt.Sprintf(factoryConsumerFormat, localIP))
-	router, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule))
-	fileredInvokers := router.(*ConditionRouter).Route(invokers, &curl, inv)
-	assert.Equal(t, 2, len(fileredInvokers))
-	assert.Equal(t, invoker2, fileredInvokers[0])
-	assert.Equal(t, invoker3, fileredInvokers[1])
+	r, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule))
+	ret := r.Route(utils.ToBitmap(invokers), setUpAddrCache(invokers), &curl, inv)
+	assert.Equal(t, 2, len(ret.ToArray()))
+	assert.Equal(t, invoker2, invokers[ret.ToArray()[0]])
+	assert.Equal(t, invoker3, invokers[ret.ToArray()[1]])
 }
 
 func TestRoute_Empty_HostFilter(t *testing.T) {
@@ -289,11 +292,11 @@ func TestRoute_Empty_HostFilter(t *testing.T) {
 	inv := &invocation.RPCInvocation{}
 	rule := base64.URLEncoding.EncodeToString([]byte(" => " + " host = " + localIP))
 	curl, _ := common.NewURL(fmt.Sprintf(factoryConsumerFormat, localIP))
-	router, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule))
-	fileredInvokers := router.(*ConditionRouter).Route(invokers, &curl, inv)
-	assert.Equal(t, 2, len(fileredInvokers))
-	assert.Equal(t, invoker2, fileredInvokers[0])
-	assert.Equal(t, invoker3, fileredInvokers[1])
+	r, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule))
+	ret := r.Route(utils.ToBitmap(invokers), setUpAddrCache(invokers), &curl, inv)
+	assert.Equal(t, 2, len(ret.ToArray()))
+	assert.Equal(t, invoker2, invokers[ret.ToArray()[0]])
+	assert.Equal(t, invoker3, invokers[ret.ToArray()[1]])
 }
 
 func TestRoute_False_HostFilter(t *testing.T) {
@@ -308,11 +311,11 @@ func TestRoute_False_HostFilter(t *testing.T) {
 	inv := &invocation.RPCInvocation{}
 	rule := base64.URLEncoding.EncodeToString([]byte("true => " + " host = " + localIP))
 	curl, _ := common.NewURL(fmt.Sprintf(factoryConsumerFormat, localIP))
-	router, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule))
-	fileredInvokers := router.(*ConditionRouter).Route(invokers, &curl, inv)
-	assert.Equal(t, 2, len(fileredInvokers))
-	assert.Equal(t, invoker2, fileredInvokers[0])
-	assert.Equal(t, invoker3, fileredInvokers[1])
+	r, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule))
+	ret := r.Route(utils.ToBitmap(invokers), setUpAddrCache(invokers), &curl, inv)
+	assert.Equal(t, 2, len(ret.ToArray()))
+	assert.Equal(t, invoker2, invokers[ret.ToArray()[0]])
+	assert.Equal(t, invoker3, invokers[ret.ToArray()[1]])
 }
 
 func TestRoute_Placeholder(t *testing.T) {
@@ -327,11 +330,11 @@ func TestRoute_Placeholder(t *testing.T) {
 	inv := &invocation.RPCInvocation{}
 	rule := base64.URLEncoding.EncodeToString([]byte("host = " + localIP + " => " + " host = $host"))
 	curl, _ := common.NewURL(fmt.Sprintf(factoryConsumerFormat, localIP))
-	router, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule))
-	fileredInvokers := router.(*ConditionRouter).Route(invokers, &curl, inv)
-	assert.Equal(t, 2, len(fileredInvokers))
-	assert.Equal(t, invoker2, fileredInvokers[0])
-	assert.Equal(t, invoker3, fileredInvokers[1])
+	r, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule))
+	ret := r.Route(utils.ToBitmap(invokers), setUpAddrCache(invokers), &curl, inv)
+	assert.Equal(t, 2, len(ret.ToArray()))
+	assert.Equal(t, invoker2, invokers[ret.ToArray()[0]])
+	assert.Equal(t, invoker3, invokers[ret.ToArray()[1]])
 }
 
 func TestRoute_NoForce(t *testing.T) {
@@ -346,9 +349,9 @@ func TestRoute_NoForce(t *testing.T) {
 	inv := &invocation.RPCInvocation{}
 	rule := base64.URLEncoding.EncodeToString([]byte(fmt.Sprintf(factoryHostIp1234Format, localIP)))
 	curl, _ := common.NewURL(fmt.Sprintf(factoryConsumerFormat, localIP))
-	router, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrlWithNoForce(rule))
-	fileredInvokers := router.(*ConditionRouter).Route(invokers, &curl, inv)
-	assert.Equal(t, invokers, fileredInvokers)
+	r, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrlWithNoForce(rule))
+	ret := r.Route(utils.ToBitmap(invokers), setUpAddrCache(invokers), &curl, inv)
+	assert.Equal(t, len(invokers), len(ret.ToArray()))
 }
 
 func TestRoute_Force(t *testing.T) {
@@ -363,9 +366,9 @@ func TestRoute_Force(t *testing.T) {
 	inv := &invocation.RPCInvocation{}
 	rule := base64.URLEncoding.EncodeToString([]byte(fmt.Sprintf(factoryHostIp1234Format, localIP)))
 	curl, _ := common.NewURL(fmt.Sprintf(factoryConsumerFormat, localIP))
-	router, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrlWithForce(rule, "true"))
-	fileredInvokers := router.(*ConditionRouter).Route(invokers, &curl, inv)
-	assert.Equal(t, 0, len(fileredInvokers))
+	r, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrlWithForce(rule, "true"))
+	fileredInvokers := r.Route(utils.ToBitmap(invokers), setUpAddrCache(invokers), &curl, inv)
+	assert.Equal(t, 0, len(fileredInvokers.ToArray()))
 }
 
 func TestNewConditionRouterFactory(t *testing.T) {
@@ -377,3 +380,7 @@ func TestNewAppRouterFactory(t *testing.T) {
 	factory := newAppRouterFactory()
 	assert.NotNil(t, factory)
 }
+
+func setUpAddrCache(addrs []protocol.Invoker) router.Cache {
+	return chain.BuildCache(addrs)
+}
diff --git a/cluster/router/condition/listenable_router.go b/cluster/router/condition/listenable_router.go
index 7f4f14a8e47173253e2e5b7f4eed5db2bed64958..19e3a00bf6f592a14c867ae580946bd3b94f4bea 100644
--- a/cluster/router/condition/listenable_router.go
+++ b/cluster/router/condition/listenable_router.go
@@ -22,10 +22,12 @@ import (
 )
 
 import (
+	"github.com/RoaringBitmap/roaring"
 	perrors "github.com/pkg/errors"
 )
 
 import (
+	"github.com/apache/dubbo-go/cluster/router"
 	"github.com/apache/dubbo-go/common"
 	"github.com/apache/dubbo-go/common/config"
 	"github.com/apache/dubbo-go/common/constant"
@@ -64,14 +66,14 @@ func newListenableRouter(url *common.URL, ruleKey string) (*AppRouter, error) {
 	l.priority = listenableRouterDefaultPriority
 
 	routerKey := ruleKey + constant.ConditionRouterRuleSuffix
-	//add listener
+	// add listener
 	dynamicConfiguration := config.GetEnvInstance().GetDynamicConfiguration()
 	if dynamicConfiguration == nil {
 		return nil, perrors.Errorf("Get dynamicConfiguration fail, dynamicConfiguration is nil, init config center plugin please")
 	}
 
 	dynamicConfiguration.AddListener(routerKey, l)
-	//get rule
+	// get rule
 	rule, err := dynamicConfiguration.GetRule(routerKey, config_center.WithGroup(config_center.DEFAULT_GROUP))
 	if len(rule) == 0 || err != nil {
 		return nil, perrors.Errorf("Get rule fail, config rule{%s},  error{%v}", rule, err)
@@ -129,13 +131,13 @@ func (l *listenableRouter) generateConditions(rule *RouterRule) {
 }
 
 // Route Determine the target invokers list.
-func (l *listenableRouter) Route(invokers []protocol.Invoker, url *common.URL, invocation protocol.Invocation) []protocol.Invoker {
-	if len(invokers) == 0 || len(l.conditionRouters) == 0 {
+func (l *listenableRouter) Route(invokers *roaring.Bitmap, cache router.Cache, url *common.URL, invocation protocol.Invocation) *roaring.Bitmap {
+	if invokers.IsEmpty() || len(l.conditionRouters) == 0 {
 		return invokers
 	}
-	//We will check enabled status inside each router.
+	// We will check enabled status inside each router.
 	for _, r := range l.conditionRouters {
-		invokers = r.Route(invokers, url, invocation)
+		invokers = r.Route(invokers, cache, url, invocation)
 	}
 	return invokers
 }
diff --git a/cluster/router/condition/router.go b/cluster/router/condition/router.go
index 751b5a7111655577566c561614d39093485130cd..4267f5b405367950d38c5d7af705e663f4416602 100644
--- a/cluster/router/condition/router.go
+++ b/cluster/router/condition/router.go
@@ -23,20 +23,23 @@ import (
 )
 
 import (
+	"github.com/RoaringBitmap/roaring"
+	"github.com/dubbogo/gost/container/set"
+	"github.com/dubbogo/gost/net"
 	perrors "github.com/pkg/errors"
 )
 
 import (
+	"github.com/apache/dubbo-go/cluster/router"
+	"github.com/apache/dubbo-go/cluster/router/utils"
 	"github.com/apache/dubbo-go/common"
 	"github.com/apache/dubbo-go/common/constant"
 	"github.com/apache/dubbo-go/common/logger"
 	"github.com/apache/dubbo-go/protocol"
-	"github.com/dubbogo/gost/container/set"
-	"github.com/dubbogo/gost/net"
 )
 
 const (
-	//pattern route pattern regex
+	// pattern route pattern regex
 	pattern = `([&!=,]*)\\s*([^&!=,\\s]+)`
 )
 
@@ -148,29 +151,36 @@ func (c *ConditionRouter) Enabled() bool {
 }
 
 // Route Determine the target invokers list.
-func (c *ConditionRouter) Route(invokers []protocol.Invoker, url *common.URL, invocation protocol.Invocation) []protocol.Invoker {
+func (c *ConditionRouter) Route(invokers *roaring.Bitmap, cache router.Cache, url *common.URL, invocation protocol.Invocation) *roaring.Bitmap {
 	if !c.Enabled() {
 		return invokers
 	}
-	if len(invokers) == 0 {
+
+	if invokers.IsEmpty() {
 		return invokers
 	}
+
 	isMatchWhen := c.MatchWhen(url, invocation)
 	if !isMatchWhen {
 		return invokers
 	}
-	var result []protocol.Invoker
+
 	if len(c.ThenCondition) == 0 {
-		return result
+		return utils.EmptyAddr
 	}
-	for _, invoker := range invokers {
+
+	result := roaring.NewBitmap()
+	for iter := invokers.Iterator(); iter.HasNext(); {
+		index := iter.Next()
+		invoker := cache.GetInvokers()[index]
 		invokerUrl := invoker.GetUrl()
 		isMatchThen := c.MatchThen(&invokerUrl, url)
 		if isMatchThen {
-			result = append(result, invoker)
+			result.Add(index)
 		}
 	}
-	if len(result) > 0 {
+
+	if !result.IsEmpty() {
 		return result
 	} else if c.Force {
 		rule, _ := url.GetParamAndDecoded(constant.RULE_KEY)
@@ -178,6 +188,7 @@ func (c *ConditionRouter) Route(invokers []protocol.Invoker, url *common.URL, in
 		logger.Warnf("The route result is empty and force execute. consumer: %s, service: %s, router: %s", localIP, url.Service(), rule)
 		return result
 	}
+
 	return invokers
 }
 
diff --git a/cluster/router/healthcheck/health_check_route.go b/cluster/router/healthcheck/health_check_route.go
index ee42e47e3b26c9a1976b4599d3464d752b615e0a..75ad189967ed75b9f6153640f49f29217543ede7 100644
--- a/cluster/router/healthcheck/health_check_route.go
+++ b/cluster/router/healthcheck/health_check_route.go
@@ -17,8 +17,13 @@
 
 package healthcheck
 
+import (
+	"github.com/RoaringBitmap/roaring"
+)
+
 import (
 	"github.com/apache/dubbo-go/cluster/router"
+	"github.com/apache/dubbo-go/cluster/router/utils"
 	"github.com/apache/dubbo-go/common"
 	"github.com/apache/dubbo-go/common/constant"
 	"github.com/apache/dubbo-go/common/extension"
@@ -28,6 +33,8 @@ import (
 
 const (
 	HEALTH_ROUTE_ENABLED_KEY = "health.route.enabled"
+	healthy                  = "healthy"
+	name                     = "health-check-router"
 )
 
 // HealthCheckRouter provides a health-first routing mechanism through HealthChecker
@@ -51,25 +58,48 @@ func NewHealthCheckRouter(url *common.URL) (router.PriorityRouter, error) {
 }
 
 // Route gets a list of healthy invoker
-func (r *HealthCheckRouter) Route(invokers []protocol.Invoker, url *common.URL, invocation protocol.Invocation) []protocol.Invoker {
+func (r *HealthCheckRouter) Route(invokers *roaring.Bitmap, cache router.Cache, url *common.URL, invocation protocol.Invocation) *roaring.Bitmap {
 	if !r.enabled {
 		return invokers
 	}
-	healthyInvokers := make([]protocol.Invoker, 0, len(invokers))
+
+	addrPool := cache.FindAddrPool(r)
 	// Add healthy invoker to the list
-	for _, invoker := range invokers {
-		if r.checker.IsHealthy(invoker) {
-			healthyInvokers = append(healthyInvokers, invoker)
-		}
-	}
-	// If all Invoke are considered unhealthy, downgrade to all inovker
-	if len(healthyInvokers) == 0 {
+	healthyInvokers := utils.JoinIfNotEqual(addrPool[healthy], invokers)
+	// If all invokers are considered unhealthy, downgrade to all invoker
+	if healthyInvokers.IsEmpty() {
 		logger.Warnf(" Now all invokers are unhealthy, so downgraded to all! Service: [%s]", url.ServiceKey())
 		return invokers
 	}
 	return healthyInvokers
 }
 
+// Pool separates healthy invokers from others.
+func (r *HealthCheckRouter) Pool(invokers []protocol.Invoker) (router.AddrPool, router.AddrMetadata) {
+	if !r.enabled {
+		return nil, nil
+	}
+
+	rb := make(router.AddrPool, 8)
+	rb[healthy] = roaring.NewBitmap()
+	for i, invoker := range invokers {
+		if r.checker.IsHealthy(invoker) {
+			rb[healthy].Add(uint32(i))
+		}
+	}
+
+	return rb, nil
+}
+
+// ShouldPool will always return true to make sure healthy check constantly.
+func (r *HealthCheckRouter) ShouldPool() bool {
+	return r.enabled
+}
+
+func (r *HealthCheckRouter) Name() string {
+	return name
+}
+
 // Priority
 func (r *HealthCheckRouter) Priority() int64 {
 	return 0
diff --git a/cluster/router/healthcheck/health_check_route_test.go b/cluster/router/healthcheck/health_check_route_test.go
index d5862fb884114bac0ea2ec9ee8926baac57d5ba6..c321b56156b9ae272048f32ef50eb419add27b03 100644
--- a/cluster/router/healthcheck/health_check_route_test.go
+++ b/cluster/router/healthcheck/health_check_route_test.go
@@ -29,6 +29,9 @@ import (
 )
 
 import (
+	"github.com/apache/dubbo-go/cluster/router"
+	"github.com/apache/dubbo-go/cluster/router/chain"
+	"github.com/apache/dubbo-go/cluster/router/utils"
 	"github.com/apache/dubbo-go/common"
 	"github.com/apache/dubbo-go/common/constant"
 	"github.com/apache/dubbo-go/protocol"
@@ -59,25 +62,25 @@ func TestHealthCheckRouterRoute(t *testing.T) {
 	invoker3 := NewMockInvoker(url3)
 	invokers = append(invokers, invoker1, invoker2, invoker3)
 	inv := invocation.NewRPCInvocation(healthCheckRouteMethodNameTest, nil, nil)
-	res := hcr.Route(invokers, &consumerURL, inv)
+	res := hcr.Route(utils.ToBitmap(invokers), setUpAddrCache(hcr.(*HealthCheckRouter), invokers), &consumerURL, inv)
 	// now all invokers are healthy
-	assert.True(t, len(res) == len(invokers))
+	assert.True(t, len(res.ToArray()) == len(invokers))
 
 	for i := 0; i < 10; i++ {
 		request(url1, healthCheckRouteMethodNameTest, 0, false, false)
 	}
-	res = hcr.Route(invokers, &consumerURL, inv)
+	res = hcr.Route(utils.ToBitmap(invokers), setUpAddrCache(hcr.(*HealthCheckRouter), invokers), &consumerURL, inv)
 	// invokers1  is unhealthy now
-	assert.True(t, len(res) == 2 && !contains(res, invoker1))
+	assert.True(t, len(res.ToArray()) == 2 && !res.Contains(0))
 
 	for i := 0; i < 10; i++ {
 		request(url1, healthCheckRouteMethodNameTest, 0, false, false)
 		request(url2, healthCheckRouteMethodNameTest, 0, false, false)
 	}
 
-	res = hcr.Route(invokers, &consumerURL, inv)
+	res = hcr.Route(utils.ToBitmap(invokers), setUpAddrCache(hcr.(*HealthCheckRouter), invokers), &consumerURL, inv)
 	// only invokers3  is healthy now
-	assert.True(t, len(res) == 1 && !contains(res, invoker1) && !contains(res, invoker2))
+	assert.True(t, len(res.ToArray()) == 1 && !res.Contains(0) && !res.Contains(1))
 
 	for i := 0; i < 10; i++ {
 		request(url1, healthCheckRouteMethodNameTest, 0, false, false)
@@ -85,37 +88,28 @@ func TestHealthCheckRouterRoute(t *testing.T) {
 		request(url3, healthCheckRouteMethodNameTest, 0, false, false)
 	}
 
-	res = hcr.Route(invokers, &consumerURL, inv)
+	res = hcr.Route(utils.ToBitmap(invokers), setUpAddrCache(hcr.(*HealthCheckRouter), invokers), &consumerURL, inv)
 	// now all invokers are unhealthy, so downgraded to all
-	assert.True(t, len(res) == 3)
+	assert.True(t, len(res.ToArray()) == 3)
 
 	// reset the invoker1 successive failed count, so invoker1 go to healthy
 	request(url1, healthCheckRouteMethodNameTest, 0, false, true)
-	res = hcr.Route(invokers, &consumerURL, inv)
-	assert.True(t, contains(res, invoker1))
+	res = hcr.Route(utils.ToBitmap(invokers), setUpAddrCache(hcr.(*HealthCheckRouter), invokers), &consumerURL, inv)
+	assert.True(t, res.Contains(0))
 
 	for i := 0; i < 6; i++ {
 		request(url1, healthCheckRouteMethodNameTest, 0, false, false)
 	}
 	// now all invokers are unhealthy, so downgraded to all again
-	res = hcr.Route(invokers, &consumerURL, inv)
-	assert.True(t, len(res) == 3)
+	res = hcr.Route(utils.ToBitmap(invokers), setUpAddrCache(hcr.(*HealthCheckRouter), invokers), &consumerURL, inv)
+	assert.True(t, len(res.ToArray()) == 3)
 	time.Sleep(time.Second * 2)
 	// invoker1 go to healthy again after 2s
-	res = hcr.Route(invokers, &consumerURL, inv)
-	assert.True(t, contains(res, invoker1))
+	res = hcr.Route(utils.ToBitmap(invokers), setUpAddrCache(hcr.(*HealthCheckRouter), invokers), &consumerURL, inv)
+	assert.True(t, res.Contains(0))
 
 }
 
-func contains(invokers []protocol.Invoker, invoker protocol.Invoker) bool {
-	for _, e := range invokers {
-		if e == invoker {
-			return true
-		}
-	}
-	return false
-}
-
 func TestNewHealthCheckRouter(t *testing.T) {
 	defer protocol.CleanAllStatus()
 	url, _ := common.NewURL(fmt.Sprintf(healthCheckDubboUrlFormat, healthCheckDubbo1010IP))
@@ -143,3 +137,11 @@ func TestNewHealthCheckRouter(t *testing.T) {
 	assert.Equal(t, dhc.requestSuccessiveFailureThreshold, int32(10))
 	assert.Equal(t, dhc.circuitTrippedTimeoutFactor, int32(500))
 }
+
+func setUpAddrCache(r router.Poolable, addrs []protocol.Invoker) router.Cache {
+	pool, info := r.Pool(addrs)
+	cache := chain.BuildCache(addrs)
+	cache.SetAddrMeta(r.Name(), info)
+	cache.SetAddrPool(r.Name(), pool)
+	return cache
+}
diff --git a/cluster/router/router.go b/cluster/router/router.go
index 66603c1d4d0efedad3489712ecea91b43254fffd..ddca42a01d5d5b56a33b9145926c77a843ea9787 100644
--- a/cluster/router/router.go
+++ b/cluster/router/router.go
@@ -17,6 +17,10 @@
 
 package router
 
+import (
+	"github.com/RoaringBitmap/roaring"
+)
+
 import (
 	"github.com/apache/dubbo-go/common"
 	"github.com/apache/dubbo-go/protocol"
@@ -38,7 +42,8 @@ type FilePriorityRouterFactory interface {
 // Router
 type router interface {
 	// Route Determine the target invokers list.
-	Route([]protocol.Invoker, *common.URL, protocol.Invocation) []protocol.Invoker
+	Route(*roaring.Bitmap, Cache, *common.URL, protocol.Invocation) *roaring.Bitmap
+
 	// URL Return URL in router
 	URL() common.URL
 }
@@ -51,10 +56,38 @@ type PriorityRouter interface {
 	Priority() int64
 }
 
-// NotifyRouter notify router use the invoker list. Invoker list may change from time to time. This method gives the router a
-// chance to prepare before {@link Router#route(List, URL, Invocation)} gets called.
-type NotifyRouter interface {
-	PriorityRouter
-	// Notify notify whenever addresses in registry change
-	Notify([]protocol.Invoker)
+// Poolable caches address pool and address metadata for a router instance which will be used later in Router's Route.
+type Poolable interface {
+	// Pool created address pool and address metadata from the invokers.
+	Pool([]protocol.Invoker) (AddrPool, AddrMetadata)
+
+	// ShouldPool returns if it should pool. One typical scenario is a router rule changes, in this case, a pooling
+	// is necessary, even if the addresses not changed at all.
+	ShouldPool() bool
+
+	// Name return the Poolable's name.
+	Name() string
+}
+
+// AddrPool is an address pool, backed by a snapshot of address list, divided into categories.
+type AddrPool map[string]*roaring.Bitmap
+
+// AddrMetadta is address metadata, collected from a snapshot of address list by a router, if it implements Poolable.
+type AddrMetadata interface {
+	// Source indicates where the metadata comes from.
+	Source() string
+}
+
+// Cache caches all addresses relevant info for a snapshot of received invokers. It keeps a snapshot of the received
+// address list, and also keeps address pools and address metadata from routers based on the same address snapshot, if
+// the router implements Poolable.
+type Cache interface {
+	// GetInvokers returns the snapshot of received invokers.
+	GetInvokers() []protocol.Invoker
+
+	// FindAddrPool returns address pool associated with the given Poolable instance.
+	FindAddrPool(Poolable) AddrPool
+
+	// FindAddrMeta returns address metadata associated with the given Poolable instance.
+	FindAddrMeta(Poolable) AddrMetadata
 }
diff --git a/cluster/router/tag/file.go b/cluster/router/tag/file.go
index 433abcb72eb6e201e64790af932e847d8faba8af..0ee43116474905d17328364a40889baef0f8c0d4 100644
--- a/cluster/router/tag/file.go
+++ b/cluster/router/tag/file.go
@@ -24,10 +24,12 @@ import (
 )
 
 import (
+	"github.com/RoaringBitmap/roaring"
 	perrors "github.com/pkg/errors"
 )
 
 import (
+	"github.com/apache/dubbo-go/cluster/router"
 	"github.com/apache/dubbo-go/common"
 	"github.com/apache/dubbo-go/common/constant"
 	"github.com/apache/dubbo-go/protocol"
@@ -74,9 +76,10 @@ func (f *FileTagRouter) Priority() int64 {
 	return f.router.priority
 }
 
-func (f *FileTagRouter) Route(invokers []protocol.Invoker, url *common.URL, invocation protocol.Invocation) []protocol.Invoker {
-	if len(invokers) == 0 {
+func (f *FileTagRouter) Route(invokers *roaring.Bitmap, cache router.Cache, url *common.URL, invocation protocol.Invocation) *roaring.Bitmap {
+	if invokers.IsEmpty() {
 		return invokers
 	}
-	return f.Route(invokers, url, invocation)
+	// FIXME: I believe this is incorrect.
+	return f.Route(invokers, cache, url, invocation)
 }
diff --git a/cluster/router/tag/router_rule.go b/cluster/router/tag/router_rule.go
index b35d455a6e763056b5d52da7f494258a22fd4b8c..c0a2d763ae7396ae41694f558f443d46084c3357 100644
--- a/cluster/router/tag/router_rule.go
+++ b/cluster/router/tag/router_rule.go
@@ -41,8 +41,8 @@ import (
 type RouterRule struct {
 	router.BaseRouterRule `yaml:",inline"`
 	Tags                  []Tag
-	addressToTagNames     map[string][]string
-	tagNameToAddresses    map[string][]string
+	AddressToTagNames     map[string][]string
+	TagNameToAddresses    map[string][]string
 }
 
 func getRule(rawRule string) (*RouterRule, error) {
@@ -58,13 +58,13 @@ func getRule(rawRule string) (*RouterRule, error) {
 
 // parseTags use for flattening tags data to @addressToTagNames and @tagNameToAddresses
 func (t *RouterRule) parseTags() {
-	t.addressToTagNames = make(map[string][]string, 2*len(t.Tags))
-	t.tagNameToAddresses = make(map[string][]string, len(t.Tags))
+	t.AddressToTagNames = make(map[string][]string, 2*len(t.Tags))
+	t.TagNameToAddresses = make(map[string][]string, len(t.Tags))
 	for _, tag := range t.Tags {
 		for _, address := range tag.Addresses {
-			t.addressToTagNames[address] = append(t.addressToTagNames[address], tag.Name)
+			t.AddressToTagNames[address] = append(t.AddressToTagNames[address], tag.Name)
 		}
-		t.tagNameToAddresses[tag.Name] = tag.Addresses
+		t.TagNameToAddresses[tag.Name] = tag.Addresses
 	}
 }
 
@@ -85,15 +85,15 @@ func (t *RouterRule) getTagNames() []string {
 }
 
 func (t *RouterRule) hasTag(tag string) bool {
-	return len(t.tagNameToAddresses[tag]) > 0
+	return len(t.TagNameToAddresses[tag]) > 0
 }
 
 func (t *RouterRule) getAddressToTagNames() map[string][]string {
-	return t.addressToTagNames
+	return t.AddressToTagNames
 }
 
 func (t *RouterRule) getTagNameToAddresses() map[string][]string {
-	return t.tagNameToAddresses
+	return t.TagNameToAddresses
 }
 
 func (t *RouterRule) getTags() []Tag {
diff --git a/cluster/router/tag/tag_router.go b/cluster/router/tag/tag_router.go
index bd5c359524e005a48d593a353118bc0a2c10b3f8..0acdf70ce97b7e1984d32ae9d4fc1b50d97ba071 100644
--- a/cluster/router/tag/tag_router.go
+++ b/cluster/router/tag/tag_router.go
@@ -18,16 +18,20 @@
 package tag
 
 import (
-	"net"
 	"strconv"
+	"strings"
+	"sync"
 )
 
 import (
+	"github.com/RoaringBitmap/roaring"
 	gxnet "github.com/dubbogo/gost/net"
 	perrors "github.com/pkg/errors"
 )
 
 import (
+	"github.com/apache/dubbo-go/cluster/router"
+	"github.com/apache/dubbo-go/cluster/router/utils"
 	"github.com/apache/dubbo-go/common"
 	"github.com/apache/dubbo-go/common/config"
 	"github.com/apache/dubbo-go/common/constant"
@@ -37,6 +41,32 @@ import (
 	"github.com/apache/dubbo-go/remoting"
 )
 
+const (
+	name          = "tag-router"
+	staticPrefix  = "static-"
+	dynamicPrefix = "dynamic-"
+)
+
+// addrMetadata keeps snapshot data for app name, and some infos extracted from dynamic tag rule in order to make
+// Route() method lock-free.
+type addrMetadata struct {
+	// application name
+	application string
+	// is rule a runtime rule
+	ruleRuntime bool
+	// is rule a force rule
+	ruleForce bool
+	// is rule a valid rule
+	ruleValid bool
+	// is rule an enabled rule
+	ruleEnabled bool
+}
+
+// Source indicates where the metadata comes from.
+func (m *addrMetadata) Source() string {
+	return name
+}
+
 // tagRouter defines url, enable and the priority
 type tagRouter struct {
 	url           *common.URL
@@ -44,6 +74,8 @@ type tagRouter struct {
 	enabled       bool
 	priority      int64
 	application   string
+	ruleChanged   bool
+	mutex         sync.RWMutex
 }
 
 // NewTagRouter returns a tagRouter instance if url is not nil
@@ -63,61 +95,80 @@ func (c *tagRouter) isEnabled() bool {
 	return c.enabled
 }
 
-func (c *tagRouter) tagRouterRuleCopy() RouterRule {
-	routerRule := *c.tagRouterRule
-	return routerRule
-}
-
 // Route gets a list of invoker
-func (c *tagRouter) Route(invokers []protocol.Invoker, url *common.URL, invocation protocol.Invocation) []protocol.Invoker {
-	var (
-		result    []protocol.Invoker
-		addresses []string
-	)
+func (c *tagRouter) Route(invokers *roaring.Bitmap, cache router.Cache, url *common.URL, invocation protocol.Invocation) *roaring.Bitmap {
+	if !c.isEnabled() || invokers.IsEmpty() {
+		return invokers
+	}
+
+	if shouldUseDynamicTag(cache.FindAddrMeta(c)) {
+		return c.routeWithDynamicTag(invokers, cache, url, invocation)
+	}
+	return c.routeWithStaticTag(invokers, cache, url, invocation)
+}
 
-	if !c.isEnabled() || len(invokers) == 0 {
+// routeWithStaticTag routes with static tag rule
+func (c *tagRouter) routeWithStaticTag(invokers *roaring.Bitmap, cache router.Cache, url *common.URL, invocation protocol.Invocation) *roaring.Bitmap {
+	tag := findTag(invocation, url)
+	if tag == "" {
 		return invokers
 	}
 
-	// Use static tags if dynamic tags are not set or invalid
-	if c.tagRouterRule == nil || !c.tagRouterRule.Valid || !c.tagRouterRule.Enabled {
-		return filterUsingStaticTag(invokers, url, invocation)
+	ret, _ := c.filterWithTag(invokers, cache, staticPrefix+tag)
+	if ret.IsEmpty() && !isForceUseTag(url, invocation) {
+		return invokers
 	}
 
-	// since the rule can be changed by config center, we should copy one to use.
-	tagRouterRuleCopy := c.tagRouterRuleCopy()
-	tag, ok := invocation.Attachments()[constant.Tagkey]
-	if !ok {
-		tag = url.GetParam(constant.Tagkey, "")
+	return ret
+}
+
+// routeWithDynamicTag routes with dynamic tag rule
+func (c *tagRouter) routeWithDynamicTag(invokers *roaring.Bitmap, cache router.Cache, url *common.URL, invocation protocol.Invocation) *roaring.Bitmap {
+	tag := findTag(invocation, url)
+	if tag == "" {
+		return c.filterNotInDynamicTag(invokers, cache)
 	}
 
-	// if we are requesting for a Provider with a specific tag
-	if len(tag) > 0 {
-		return filterInvokersWithTag(invokers, url, invocation, tagRouterRuleCopy, tag)
+	ret, ok := c.filterWithTag(invokers, cache, dynamicPrefix+tag)
+	if ok && (!ret.IsEmpty() || isTagRuleForce(cache.FindAddrMeta(c))) {
+		return ret
 	}
 
-	// return all addresses in dynamic tag group.
-	addresses = tagRouterRuleCopy.getAddresses()
-	if len(addresses) > 0 {
-		filterAddressNotMatches := func(invoker protocol.Invoker) bool {
-			url := invoker.GetUrl()
-			return len(addresses) == 0 || !checkAddressMatch(addresses, url.Ip, url.Port)
-		}
-		result = filterInvoker(invokers, filterAddressNotMatches)
-		// 1. all addresses are in dynamic tag group, return empty list.
-		if len(result) == 0 {
-			return result
+	// dynamic tag group doesn't have any item about the requested app OR it's null after filtered by
+	// dynamic tag group but force=false. check static tag
+	if ret.IsEmpty() {
+		ret, _ = c.filterWithTag(invokers, cache, staticPrefix+tag)
+		// If there's no tagged providers that can match the current tagged request. force.tag is set by default
+		// to false, which means it will invoke any providers without a tag unless it's explicitly disallowed.
+		if !ret.IsEmpty() || isForceUseTag(url, invocation) {
+			return ret
 		}
+		return c.filterNotInDynamicTag(invokers, cache)
+	}
+	return ret
+}
+
+// filterWithTag filters incoming invokers with the given tag
+func (c *tagRouter) filterWithTag(invokers *roaring.Bitmap, cache router.Cache, tag string) (*roaring.Bitmap, bool) {
+	if target, ok := cache.FindAddrPool(c)[tag]; ok {
+		return utils.JoinIfNotEqual(target, invokers), true
 	}
-	// 2. if there are some addresses that are not in any dynamic tag group, continue to filter using the
-	// static tag group.
-	filter := func(invoker protocol.Invoker) bool {
-		localTag := invoker.GetUrl().GetParam(constant.Tagkey, "")
-		return len(localTag) == 0 || !(tagRouterRuleCopy.hasTag(localTag))
+	return utils.EmptyAddr, false
+}
+
+// filterNotInDynamicTag filters incoming invokers not applied to dynamic tag rule
+func (c *tagRouter) filterNotInDynamicTag(invokers *roaring.Bitmap, cache router.Cache) *roaring.Bitmap {
+	// FAILOVER: return all Providers without any tags.
+	invokers = invokers.Clone()
+	for k, v := range cache.FindAddrPool(c) {
+		if strings.HasPrefix(k, dynamicPrefix) {
+			invokers.AndNot(v)
+		}
 	}
-	return filterInvoker(result, filter)
+	return invokers
 }
 
+// Process parses dynamic tag rule
 func (c *tagRouter) Process(event *config_center.ConfigChangeEvent) {
 	logger.Infof("Notification of tag rule, change type is:[%s] , raw rule is:[%v]", event.ConfigType, event.Value)
 	if remoting.EventTypeDel == event.ConfigType {
@@ -135,16 +186,53 @@ func (c *tagRouter) Process(event *config_center.ConfigChangeEvent) {
 		logger.Errorf("Parse dynamic tag router rule fail,error:[%s] ", err)
 		return
 	}
+
+	c.mutex.Lock()
+	defer c.mutex.Unlock()
 	c.tagRouterRule = routerRule
+	c.ruleChanged = true
 	return
 }
 
-func (c *tagRouter) Notify(invokers []protocol.Invoker) {
-	if len(invokers) == 0 {
+// URL gets the url of tagRouter
+func (c *tagRouter) URL() common.URL {
+	return *c.url
+}
+
+// Priority gets the priority of tagRouter
+func (c *tagRouter) Priority() int64 {
+	return c.priority
+}
+
+// Pool divided invokers into different address pool by tag.
+func (c *tagRouter) Pool(invokers []protocol.Invoker) (router.AddrPool, router.AddrMetadata) {
+	c.fetchRuleIfNecessary(invokers)
+
+	rb := make(router.AddrPool, 8)
+	poolWithStaticTag(invokers, rb)
+
+	c.mutex.Lock()
+	defer c.mutex.Unlock()
+	poolWithDynamicTag(invokers, c.tagRouterRule, rb)
+	c.ruleChanged = false
+	// create metadata in order to avoid lock in route()
+	meta := addrMetadata{application: c.application}
+	if c.tagRouterRule != nil {
+		meta.ruleForce = c.tagRouterRule.Force
+		meta.ruleEnabled = c.tagRouterRule.Enabled
+		meta.ruleValid = c.tagRouterRule.Valid
+	}
+
+	return rb, &meta
+}
+
+// fetchRuleIfNecessary fetches, parses rule and register listener for the further change
+func (c *tagRouter) fetchRuleIfNecessary(invokers []protocol.Invoker) {
+	if invokers == nil || len(invokers) == 0 {
 		return
 	}
-	invoker := invokers[0]
-	url := invoker.GetUrl()
+
+	url := invokers[0].GetUrl()
 	providerApplication := url.GetParam(constant.RemoteApplicationKey, "")
 	if len(providerApplication) == 0 {
 		logger.Error("TagRouter must getConfig from or subscribe to a specific application, but the application " +
@@ -159,11 +247,15 @@ func (c *tagRouter) Notify(invokers []protocol.Invoker) {
 
 	if providerApplication != c.application {
 		dynamicConfiguration.RemoveListener(c.application+constant.TagRouterRuleSuffix, c)
+	} else {
+		// if app name from URL is as same as the current app name, then it is safe to jump out
+		return
 	}
 
+	c.application = providerApplication
 	routerKey := providerApplication + constant.TagRouterRuleSuffix
 	dynamicConfiguration.AddListener(routerKey, c)
-	//get rule
+	// get rule
 	rule, err := dynamicConfiguration.GetRule(routerKey, config_center.WithGroup(config_center.DEFAULT_GROUP))
 	if len(rule) == 0 || err != nil {
 		logger.Errorf("Get rule fail, config rule{%s},  error{%v}", rule, err)
@@ -177,71 +269,52 @@ func (c *tagRouter) Notify(invokers []protocol.Invoker) {
 	}
 }
 
-// URL gets the url of tagRouter
-func (c *tagRouter) URL() common.URL {
-	return *c.url
+// ShouldPool returns false, to make sure address cache for tag router happens once and only once.
+func (c *tagRouter) ShouldPool() bool {
+	c.mutex.RLock()
+	defer c.mutex.RUnlock()
+	return c.ruleChanged
 }
 
-// Priority gets the priority of tagRouter
-func (c *tagRouter) Priority() int64 {
-	return c.priority
+// Name returns pool's name
+func (c *tagRouter) Name() string {
+	return name
 }
 
-// filterUsingStaticTag gets a list of invoker using static tag
-func filterUsingStaticTag(invokers []protocol.Invoker, url *common.URL, invocation protocol.Invocation) []protocol.Invoker {
-	if tag, ok := invocation.Attachments()[constant.Tagkey]; ok {
-		result := make([]protocol.Invoker, 0, 8)
-		for _, v := range invokers {
-			if v.GetUrl().GetParam(constant.Tagkey, "") == tag {
-				result = append(result, v)
-			}
-		}
-		if len(result) == 0 && !isForceUseTag(url, invocation) {
-			return invokers
-		}
-		return result
+// poolWithDynamicTag pools addresses with the tags defined in dynamic tag rule, all keys have prefix "dynamic-"
+func poolWithDynamicTag(invokers []protocol.Invoker, rule *RouterRule, pool router.AddrPool) {
+	if rule == nil {
+		return
 	}
-	return invokers
-}
 
-// filterInvokersWithTag gets a list of invoker using dynamic route with tag
-func filterInvokersWithTag(invokers []protocol.Invoker, url *common.URL, invocation protocol.Invocation, tagRouterRule RouterRule, tag string) []protocol.Invoker {
-	var (
-		result    []protocol.Invoker
-		addresses []string
-	)
-	addresses, _ = tagRouterRule.getTagNameToAddresses()[tag]
-	// filter by dynamic tag group first
-	if len(addresses) > 0 {
-		filterAddressMatches := func(invoker protocol.Invoker) bool {
-			url := invoker.GetUrl()
-			return len(addresses) > 0 && checkAddressMatch(addresses, url.Ip, url.Port)
-		}
-		result = filterInvoker(invokers, filterAddressMatches)
-		if len(result) > 0 || tagRouterRule.Force {
-			return result
-		}
-	}
-	// dynamic tag group doesn't have any item about the requested app OR it's null after filtered by
-	// dynamic tag group but force=false. check static tag
-	filter := func(invoker protocol.Invoker) bool {
-		return invoker.GetUrl().GetParam(constant.Tagkey, "") == tag
-	}
-	result = filterInvoker(invokers, filter)
-	// If there's no tagged providers that can match the current tagged request. force.tag is set by default
-	// to false, which means it will invoke any providers without a tag unless it's explicitly disallowed.
-	if len(result) > 0 || isForceUseTag(url, invocation) {
-		return result
+	tagNameToAddresses := rule.getTagNameToAddresses()
+	for tag, addrs := range tagNameToAddresses {
+		pool[dynamicPrefix+tag] = addrsToBitmap(addrs, invokers)
 	}
-	// FAILOVER: return all Providers without any tags.
-	filterAddressNotMatches := func(invoker protocol.Invoker) bool {
+}
+
+// poolWithStaticTag pools addresses with tags found from incoming URLs, all keys have prefix "static-"
+func poolWithStaticTag(invokers []protocol.Invoker, pool router.AddrPool) {
+	for i, invoker := range invokers {
 		url := invoker.GetUrl()
-		return len(addresses) == 0 || !checkAddressMatch(tagRouterRule.getAddresses(), url.Ip, url.Port)
-	}
-	filterTagIsEmpty := func(invoker protocol.Invoker) bool {
-		return invoker.GetUrl().GetParam(constant.Tagkey, "") == ""
+		tag := url.GetParam(constant.Tagkey, "")
+		if len(tag) > 0 {
+			if _, ok := pool[staticPrefix+tag]; !ok {
+				pool[staticPrefix+tag] = roaring.NewBitmap()
+			}
+			pool[staticPrefix+tag].AddInt(i)
+		}
 	}
-	return filterInvoker(invokers, filterAddressNotMatches, filterTagIsEmpty)
+}
+
+// shouldUseDynamicTag uses the snapshot data from the parsed rule to decide if dynamic tag rule should be used or not
+func shouldUseDynamicTag(meta router.AddrMetadata) bool {
+	return meta.(*addrMetadata).ruleValid && meta.(*addrMetadata).ruleEnabled
+}
+
+// isTagRuleForce uses the snapshot data from the parsed rule to decide if dynamic tag rule is forced or not
+func isTagRuleForce(meta router.AddrMetadata) bool {
+	return meta.(*addrMetadata).ruleForce
 }
 
 // isForceUseTag returns whether force use tag
@@ -252,31 +325,44 @@ func isForceUseTag(url *common.URL, invocation protocol.Invocation) bool {
 	return false
 }
 
-type filter func(protocol.Invoker) bool
+// addrsToBitmap finds indexes for the given IP addresses in the target URL list, if any '0.0.0.0' IP address is met,
+// then returns back all indexes of the URLs list.
+func addrsToBitmap(addrs []string, invokers []protocol.Invoker) *roaring.Bitmap {
+	ret := roaring.NewBitmap()
+	for _, addr := range addrs {
+		if isAnyHost(addr) {
+			ret.AddRange(0, uint64(len(invokers)))
+			return ret
+		}
 
-func filterInvoker(invokers []protocol.Invoker, filters ...filter) []protocol.Invoker {
-	var res []protocol.Invoker
-OUTER:
-	for _, invoker := range invokers {
-		for _, filter := range filters {
-			if !filter(invoker) {
-				continue OUTER
-			}
+		index := findIndexWithIp(addr, invokers)
+		if index != -1 {
+			ret.AddInt(index)
 		}
-		res = append(res, invoker)
 	}
-	return res
+	return ret
 }
 
-func checkAddressMatch(addresses []string, host, port string) bool {
-	addr := net.JoinHostPort(constant.ANYHOST_VALUE, port)
-	for _, address := range addresses {
-		if gxnet.MatchIP(address, host, port) {
-			return true
-		}
-		if address == addr {
-			return true
+// findIndexWithIp finds index for one particular IP
+func findIndexWithIp(addr string, invokers []protocol.Invoker) int {
+	for i, invoker := range invokers {
+		if gxnet.MatchIP(addr, invoker.GetUrl().Ip, invoker.GetUrl().Port) {
+			return i
 		}
 	}
-	return false
+	return -1
+}
+
+// isAnyHost checks if an IP is '0.0.0.0'
+func isAnyHost(addr string) bool {
+	return strings.HasPrefix(addr, constant.ANYHOST_VALUE)
+}
+
+// findTag finds tag, first from invocation's attachment, then from URL
+func findTag(invocation protocol.Invocation, consumerUrl *common.URL) string {
+	tag, ok := invocation.Attachments()[constant.Tagkey]
+	if !ok {
+		tag = consumerUrl.GetParam(constant.Tagkey, "")
+	}
+	return tag
 }
diff --git a/cluster/router/tag/tag_router_test.go b/cluster/router/tag/tag_router_test.go
index 6b9a5e08f133403565d8b2dd38e03a789c5e95e7..92611ac2c59538ea090763d91cb7c5a6ef44e8ee 100644
--- a/cluster/router/tag/tag_router_test.go
+++ b/cluster/router/tag/tag_router_test.go
@@ -25,15 +25,18 @@ import (
 )
 
 import (
+	"github.com/RoaringBitmap/roaring"
 	"github.com/dubbogo/go-zookeeper/zk"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/suite"
 )
 
 import (
+	"github.com/apache/dubbo-go/cluster/router"
+	"github.com/apache/dubbo-go/cluster/router/chain"
+	"github.com/apache/dubbo-go/cluster/router/utils"
 	"github.com/apache/dubbo-go/common"
 	"github.com/apache/dubbo-go/common/config"
-	"github.com/apache/dubbo-go/common/constant"
 	"github.com/apache/dubbo-go/common/extension"
 	"github.com/apache/dubbo-go/config_center"
 	_ "github.com/apache/dubbo-go/config_center/zookeeper"
@@ -143,17 +146,17 @@ func TestTagRouterRouteForce(t *testing.T) {
 	invokers = append(invokers, inv2, inv3, inv4)
 	inv := &invocation.RPCInvocation{}
 	inv.SetAttachments(tagRouterTestDubboTag, tagRouterTestHangZhou)
-	invRst1 := tagRouter.Route(invokers, &u1, inv)
-	assert.Equal(t, 1, len(invRst1))
-	assert.Equal(t, tagRouterTestHangZhou, invRst1[0].GetUrl().GetParam(tagRouterTestDubboTag, ""))
+	invRst1 := tagRouter.Route(utils.ToBitmap(invokers), setUpAddrCache(tagRouter, invokers), &u1, inv)
+	assert.Equal(t, 1, len(invRst1.ToArray()))
+	assert.Equal(t, tagRouterTestHangZhou, invokers[invRst1.ToArray()[0]].GetUrl().GetParam(tagRouterTestDubboTag, ""))
 
 	inv.SetAttachments(tagRouterTestDubboTag, tagRouterTestGuangZhou)
-	invRst2 := tagRouter.Route(invokers, &u1, inv)
-	assert.Equal(t, 0, len(invRst2))
+	invRst2 := tagRouter.Route(utils.ToBitmap(invokers), setUpAddrCache(tagRouter, invokers), &u1, inv)
+	assert.Equal(t, 0, len(invRst2.ToArray()))
 	inv.SetAttachments(tagRouterTestDubboForceTag, tagRouterTestFalse)
 	inv.SetAttachments(tagRouterTestDubboTag, tagRouterTestGuangZhou)
-	invRst3 := tagRouter.Route(invokers, &u1, inv)
-	assert.Equal(t, 3, len(invRst3))
+	invRst3 := tagRouter.Route(utils.ToBitmap(invokers), setUpAddrCache(tagRouter, invokers), &u1, inv)
+	assert.Equal(t, 3, len(invRst3.ToArray()))
 }
 
 func TestTagRouterRouteNoForce(t *testing.T) {
@@ -175,20 +178,37 @@ func TestTagRouterRouteNoForce(t *testing.T) {
 	invokers = append(invokers, inv2, inv3, inv4)
 	inv := &invocation.RPCInvocation{}
 	inv.SetAttachments(tagRouterTestDubboTag, tagRouterTestHangZhou)
-	invRst := tagRouter.Route(invokers, &u1, inv)
-	assert.Equal(t, 1, len(invRst))
-	assert.Equal(t, tagRouterTestHangZhou, invRst[0].GetUrl().GetParam(tagRouterTestDubboTag, ""))
+	invRst := tagRouter.Route(utils.ToBitmap(invokers), setUpAddrCache(tagRouter, invokers), &u1, inv)
+	assert.Equal(t, 1, len(invRst.ToArray()))
+	assert.Equal(t, tagRouterTestHangZhou, invokers[invRst.ToArray()[0]].GetUrl().GetParam(tagRouterTestDubboTag, ""))
 
 	inv.SetAttachments(tagRouterTestDubboTag, tagRouterTestGuangZhou)
 	inv.SetAttachments(tagRouterTestDubboForceTag, tagRouterTestTrue)
-	invRst1 := tagRouter.Route(invokers, &u1, inv)
-	assert.Equal(t, 0, len(invRst1))
+	invRst1 := tagRouter.Route(utils.ToBitmap(invokers), setUpAddrCache(tagRouter, invokers), &u1, inv)
+	assert.Equal(t, 0, len(invRst1.ToArray()))
 	inv.SetAttachments(tagRouterTestDubboForceTag, tagRouterTestFalse)
-	invRst2 := tagRouter.Route(invokers, &u1, inv)
-	assert.Equal(t, 3, len(invRst2))
+	invRst2 := tagRouter.Route(utils.ToBitmap(invokers), setUpAddrCache(tagRouter, invokers), &u1, inv)
+	assert.Equal(t, 3, len(invRst2.ToArray()))
 }
 
-func TestFilterInvoker(t *testing.T) {
+func setUpAddrCache(r router.Poolable, addrs []protocol.Invoker) router.Cache {
+	pool, info := r.Pool(addrs)
+	cache := chain.BuildCache(addrs)
+	cache.SetAddrPool(r.Name(), pool)
+	cache.SetAddrMeta(r.Name(), info)
+	return cache
+}
+
+func setUpAddrCacheWithRuleDisabled(r router.Poolable, addrs []protocol.Invoker) router.Cache {
+	pool, info := r.Pool(addrs)
+	info.(*addrMetadata).ruleEnabled = false
+	cache := chain.BuildCache(addrs)
+	cache.SetAddrPool(r.Name(), pool)
+	cache.SetAddrMeta(r.Name(), info)
+	return cache
+}
+
+func TestRouteBeijingInvoker(t *testing.T) {
 	u2, e2 := common.NewURL(tagRouterTestHangZhouUrl)
 	u3, e3 := common.NewURL(tagRouterTestShangHaiUrl)
 	u4, e4 := common.NewURL(tagRouterTestBeijingUrl)
@@ -203,23 +223,17 @@ func TestFilterInvoker(t *testing.T) {
 	inv5 := NewMockInvoker(u5)
 	var invokers []protocol.Invoker
 	invokers = append(invokers, inv2, inv3, inv4, inv5)
-	filterTag := func(invoker protocol.Invoker) bool {
-		if invoker.GetUrl().GetParam(constant.Tagkey, "") == "beijing" {
-			return true
-		}
-		return false
-	}
-	res := filterInvoker(invokers, filterTag)
-	assert.Equal(t, []protocol.Invoker{inv4, inv5}, res)
-	flag := true
-	filterEnabled := func(invoker protocol.Invoker) bool {
-		if invoker.GetUrl().GetParamBool(constant.RouterEnabled, false) == flag {
-			return true
-		}
-		return false
-	}
-	res2 := filterInvoker(invokers, filterTag, filterEnabled)
-	assert.Equal(t, []protocol.Invoker{inv4}, res2)
+
+	url, _ := common.NewURL(tagRouterTestBeijingUrl)
+	tagRouter, _ := NewTagRouter(&url)
+
+	rb := roaring.NewBitmap()
+	rb.AddRange(0, uint64(len(invokers)))
+	cache := setUpAddrCache(tagRouter, invokers)
+	inv := &invocation.RPCInvocation{}
+	res := tagRouter.Route(rb, cache, &url, inv)
+	// inv4 and inv5
+	assert.Equal(t, []uint32{2, 3}, res.ToArray())
 }
 
 type DynamicTagRouter struct {
@@ -301,44 +315,51 @@ func (suite *DynamicTagRouter) TearDownTest() {
 
 func (suite *DynamicTagRouter) TestDynamicTagRouterSetByIPv4() {
 	invokers := suite.invokers
-	suite.route.Notify(invokers)
+	rb := roaring.NewBitmap()
+	rb.AddRange(0, uint64(len(invokers)))
+	cache := setUpAddrCache(suite.route, invokers)
 	suite.NotNil(suite.route.tagRouterRule)
 
 	consumer := &invocation.RPCInvocation{}
 	consumer.SetAttachments(tagRouterTestDubboTag, "tag1")
-	targetInvokers := suite.route.Route(invokers, suite.url, consumer)
-	suite.Equal(1, len(targetInvokers))
-	suite.Equal(targetInvokers[0], suite.invokers[0])
+	targetInvokers := suite.route.Route(rb, cache, suite.url, consumer)
+	suite.Equal(uint64(1), targetInvokers.GetCardinality())
+	suite.Equal(targetInvokers.ToArray()[0], uint32(0))
 
 	consumer.SetAttachments(tagRouterTestDubboTag, "tag3")
-	targetInvokers = suite.route.Route(invokers, suite.url, consumer)
-	suite.Equal(2, len(targetInvokers))
-	suite.Equal(targetInvokers, []protocol.Invoker{suite.invokers[2], suite.invokers[3]})
+	targetInvokers = suite.route.Route(rb, cache, suite.url, consumer)
+	suite.Equal(uint64(2), targetInvokers.GetCardinality())
+	suite.True(targetInvokers.Contains(2) && targetInvokers.Contains(3))
 }
 
 func (suite *DynamicTagRouter) TestDynamicTagRouterStaticTag() {
 	invokers := suite.invokers
+	rb := roaring.NewBitmap()
+	rb.AddRange(0, uint64(len(invokers)))
+	cache := setUpAddrCacheWithRuleDisabled(suite.route, invokers)
 	consumer := &invocation.RPCInvocation{}
 	consumer.SetAttachments(tagRouterTestDubboTag, "tag4")
-	targetInvokers := suite.route.Route(invokers, suite.url, consumer)
-	suite.Equal(1, len(targetInvokers))
-	suite.Equal(targetInvokers[0], suite.invokers[3])
+	targetInvokers := suite.route.Route(rb, cache, suite.url, consumer)
+	suite.Equal(uint64(1), targetInvokers.GetCardinality())
+	suite.Equal(targetInvokers.ToArray()[0], uint32(3))
 }
 
 // Teas no tag and return a address are not in dynamic tag group
 func (suite *DynamicTagRouter) TestDynamicTagRouterByNoTagAndAddressMatch() {
 	invokers := suite.invokers
-	suite.route.Notify(invokers)
+	rb := roaring.NewBitmap()
+	rb.AddRange(0, uint64(len(invokers)))
+	cache := setUpAddrCache(suite.route, invokers)
 	suite.NotNil(suite.route.tagRouterRule)
 	consumer := &invocation.RPCInvocation{}
-	targetInvokers := suite.route.Route(invokers, suite.url, consumer)
-	suite.Equal(1, len(targetInvokers))
-	suite.Equal(targetInvokers[0], suite.invokers[4])
+	targetInvokers := suite.route.Route(rb, cache, suite.url, consumer)
+	suite.Equal(uint64(1), targetInvokers.GetCardinality())
+	suite.Equal(targetInvokers.ToArray()[0], uint32(4))
 	// test if there are some addresses that are not in any dynamic tag group, continue to filter using the static tag group.
 	consumer.SetAttachments(tagRouterTestDubboTag, "tag5")
-	targetInvokers = suite.route.Route(invokers, suite.url, consumer)
-	suite.Equal(1, len(targetInvokers))
-	suite.Equal(targetInvokers[0], suite.invokers[4])
+	targetInvokers = suite.route.Route(rb, cache, suite.url, consumer)
+	suite.Equal(uint64(1), targetInvokers.GetCardinality())
+	suite.Equal(targetInvokers.ToArray()[0], uint32(4))
 }
 
 func TestProcess(t *testing.T) {
diff --git a/cluster/router/utils/bitmap_util.go b/cluster/router/utils/bitmap_util.go
new file mode 100644
index 0000000000000000000000000000000000000000..8b4ee5538f339276fb4c41ceefe4df3575723f15
--- /dev/null
+++ b/cluster/router/utils/bitmap_util.go
@@ -0,0 +1,50 @@
+/*
+ * 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 utils
+
+import (
+	"github.com/RoaringBitmap/roaring"
+)
+
+import (
+	"github.com/apache/dubbo-go/protocol"
+)
+
+var EmptyAddr = roaring.NewBitmap()
+
+func JoinIfNotEqual(left *roaring.Bitmap, right *roaring.Bitmap) *roaring.Bitmap {
+	if !left.Equals(right) {
+		left = left.Clone()
+		left.And(right)
+	}
+	return left
+}
+
+func FallbackIfJoinToEmpty(left *roaring.Bitmap, right *roaring.Bitmap) *roaring.Bitmap {
+	ret := JoinIfNotEqual(left, right)
+	if ret == nil || ret.IsEmpty() {
+		return right
+	}
+	return ret
+}
+
+func ToBitmap(invokers []protocol.Invoker) *roaring.Bitmap {
+	bitmap := roaring.NewBitmap()
+	bitmap.AddRange(0, uint64(len(invokers)))
+	return bitmap
+}
diff --git a/common/extension/metadata_service.go b/common/extension/metadata_service.go
index 1823273b8f7f86b2d96abf990359e14b569abddb..e35677d148eee121c3a6c018a128b5d372c6f2c7 100644
--- a/common/extension/metadata_service.go
+++ b/common/extension/metadata_service.go
@@ -21,6 +21,10 @@ import (
 	"fmt"
 )
 
+import (
+	perrors "github.com/pkg/errors"
+)
+
 import (
 	"github.com/apache/dubbo-go/metadata/service"
 )
@@ -36,12 +40,11 @@ func SetMetadataService(msType string, creator func() (service.MetadataService,
 }
 
 // GetMetadataService will create a MetadataService instance
-// it will panic if msType not found
 func GetMetadataService(msType string) (service.MetadataService, error) {
 	if creator, ok := metadataServiceInsMap[msType]; ok {
 		return creator()
 	}
-	panic(fmt.Sprintf("could not find the metadata service creator for metadataType: %s, please check whether you have imported relative packages, \n"+
+	return nil, perrors.New(fmt.Sprintf("could not find the metadata service creator for metadataType: %s, please check whether you have imported relative packages, \n"+
 		"local - github.com/apache/dubbo-go/metadata/service/inmemory, \n"+
 		"remote - github.com/apache/dubbo-go/metadata/service/remote", msType))
 }
diff --git a/common/logger/logger.go b/common/logger/logger.go
index 88ed0c29f897f13dabe2dd3979232b4e6c84b968..63eda231ddd174468602577d8b042bc0664700d0 100644
--- a/common/logger/logger.go
+++ b/common/logger/logger.go
@@ -25,7 +25,7 @@ import (
 )
 
 import (
-	"github.com/dubbogo/getty"
+	"github.com/apache/dubbo-getty"
 	perrors "github.com/pkg/errors"
 	"go.uber.org/zap"
 	"go.uber.org/zap/zapcore"
@@ -60,6 +60,10 @@ type Logger interface {
 }
 
 func init() {
+	// forbidden to executing twice.
+	if logger != nil {
+		return
+	}
 	logConfFile := os.Getenv(constant.APP_LOG_CONF_FILE)
 	err := InitLog(logConfFile)
 	if err != nil {
diff --git a/common/url.go b/common/url.go
index 176a95127ed0cdd3b11d48a45562f92ab37b4feb..b9d8b924c46bbec6dacb82e03f3b5ade40e7bb5c 100644
--- a/common/url.go
+++ b/common/url.go
@@ -647,6 +647,34 @@ func (c *URL) CloneWithParams(reserveParams []string) *URL {
 	)
 }
 
+// IsEquals compares if two URLs equals with each other. Excludes are all parameter keys which should ignored.
+func IsEquals(left URL, right URL, excludes ...string) bool {
+	if left.Ip != right.Ip || left.Port != right.Port {
+		return false
+	}
+
+	leftMap := left.ToMap()
+	rightMap := right.ToMap()
+	for _, exclude := range excludes {
+		delete(leftMap, exclude)
+		delete(rightMap, exclude)
+	}
+
+	if len(leftMap) != len(rightMap) {
+		return false
+	}
+
+	for lk, lv := range leftMap {
+		if rv, ok := rightMap[lk]; !ok {
+			return false
+		} else if lv != rv {
+			return false
+		}
+	}
+
+	return true
+}
+
 func mergeNormalParam(mergedUrl *URL, referenceUrl *URL, paramKeys []string) []func(method string) {
 	methodConfigMergeFcn := make([]func(method string), 0, len(paramKeys))
 	for _, paramKey := range paramKeys {
diff --git a/config/config_loader.go b/config/config_loader.go
index 8b196305b9cf7d33cb149def149af178f0e2808c..75b82628d68a23e575cfa637b3603d09e09ea9d6 100644
--- a/config/config_loader.go
+++ b/config/config_loader.go
@@ -21,11 +21,14 @@ import (
 	"fmt"
 	"log"
 	"os"
+	"reflect"
+	"strconv"
 	"sync"
 	"time"
 )
 
 import (
+	gxnet "github.com/dubbogo/gost/net"
 	perrors "github.com/pkg/errors"
 )
 
@@ -35,6 +38,7 @@ import (
 	"github.com/apache/dubbo-go/common/extension"
 	"github.com/apache/dubbo-go/common/logger"
 	_ "github.com/apache/dubbo-go/common/observer/dispatcher"
+	"github.com/apache/dubbo-go/registry"
 )
 
 var (
@@ -206,6 +210,97 @@ func loadProviderConfig() {
 			panic(fmt.Sprintf("service %s export failed! err: %#v", key, err))
 		}
 	}
+	registerServiceInstance()
+}
+
+// registerServiceInstance register service instance
+func registerServiceInstance() {
+	url := selectMetadataServiceExportedURL()
+	if url == nil {
+		return
+	}
+	instance, err := createInstance(*url)
+	if err != nil {
+		panic(err)
+	}
+	p := extension.GetProtocol(constant.REGISTRY_KEY)
+	var rp registry.RegistryFactory
+	var ok bool
+	if rp, ok = p.(registry.RegistryFactory); !ok {
+		panic("dubbo registry protocol{" + reflect.TypeOf(p).String() + "} is invalid")
+	}
+	rs := rp.GetRegistries()
+	for _, r := range rs {
+		var sdr registry.ServiceDiscoveryHolder
+		if sdr, ok = r.(registry.ServiceDiscoveryHolder); !ok {
+			continue
+		}
+		err := sdr.GetServiceDiscovery().Register(instance)
+		if err != nil {
+			panic(err)
+		}
+	}
+}
+
+// nolint
+func createInstance(url common.URL) (registry.ServiceInstance, error) {
+	appConfig := GetApplicationConfig()
+	port, err := strconv.ParseInt(url.Port, 10, 32)
+	if err != nil {
+		return nil, perrors.WithMessage(err, "invalid port: "+url.Port)
+	}
+
+	host := url.Ip
+	if len(host) == 0 {
+		host, err = gxnet.GetLocalIP()
+		if err != nil {
+			return nil, perrors.WithMessage(err, "could not get the local Ip")
+		}
+	}
+
+	// usually we will add more metadata
+	metadata := make(map[string]string, 8)
+	metadata[constant.METADATA_STORAGE_TYPE_PROPERTY_NAME] = appConfig.MetadataType
+
+	return &registry.DefaultServiceInstance{
+		ServiceName: appConfig.Name,
+		Host:        host,
+		Port:        int(port),
+		Id:          host + constant.KEY_SEPARATOR + url.Port,
+		Enable:      true,
+		Healthy:     true,
+		Metadata:    metadata,
+	}, nil
+}
+
+// selectMetadataServiceExportedURL get already be exported url
+func selectMetadataServiceExportedURL() *common.URL {
+	var selectedUrl common.URL
+	metaDataService, err := extension.GetMetadataService(GetApplicationConfig().MetadataType)
+	if err != nil {
+		logger.Warn(err)
+		return nil
+	}
+	list, err := metaDataService.GetExportedURLs(constant.ANY_VALUE, constant.ANY_VALUE, constant.ANY_VALUE, constant.ANY_VALUE)
+	if err != nil {
+		panic(err)
+	}
+	if len(list) == 0 {
+		return nil
+	}
+	for _, urlStr := range list {
+		url, err := common.NewURL(urlStr.(string))
+		if err != nil {
+			logger.Errorf("url format error {%v}", url)
+			continue
+		}
+		selectedUrl = url
+		// rest first
+		if url.Protocol == "rest" {
+			break
+		}
+	}
+	return &selectedUrl
 }
 
 func initRouter() {
diff --git a/config/config_loader_test.go b/config/config_loader_test.go
index a219b9f465c71186e8cd482a7bbca32a03cdb396..461e607c1e0ad2e9471770224c8eb6d5f3ee96f6 100644
--- a/config/config_loader_test.go
+++ b/config/config_loader_test.go
@@ -19,10 +19,16 @@ package config
 
 import (
 	"path/filepath"
+	"sort"
+	"sync"
 	"testing"
 )
 
 import (
+	cm "github.com/Workiva/go-datastructures/common"
+	"github.com/Workiva/go-datastructures/slice/skip"
+	gxset "github.com/dubbogo/gost/container/set"
+	gxpage "github.com/dubbogo/gost/page"
 	"github.com/stretchr/testify/assert"
 	"go.uber.org/atomic"
 )
@@ -33,8 +39,11 @@ import (
 	"github.com/apache/dubbo-go/common/config"
 	"github.com/apache/dubbo-go/common/constant"
 	"github.com/apache/dubbo-go/common/extension"
+	"github.com/apache/dubbo-go/common/logger"
 	"github.com/apache/dubbo-go/common/proxy/proxy_factory"
 	"github.com/apache/dubbo-go/config_center"
+	"github.com/apache/dubbo-go/metadata/service"
+	"github.com/apache/dubbo-go/registry"
 )
 
 const mockConsumerConfigPath = "./testdata/consumer_config.yml"
@@ -74,7 +83,17 @@ func TestLoad(t *testing.T) {
 	extension.SetProtocol("registry", GetProtocol)
 	extension.SetCluster(constant.ZONEAWARE_CLUSTER_NAME, cluster_impl.NewZoneAwareCluster)
 	extension.SetProxyFactory("default", proxy_factory.NewDefaultProxyFactory)
-
+	GetApplicationConfig().MetadataType = "mock"
+	var mm *mockMetadataService
+	extension.SetMetadataService("mock", func() (metadataService service.MetadataService, err error) {
+		if mm == nil {
+			mm = &mockMetadataService{
+				exportedServiceURLs: new(sync.Map),
+				lock:                new(sync.RWMutex),
+			}
+		}
+		return mm, nil
+	})
 	Load()
 
 	assert.Equal(t, ms, GetRPCService(ms.Reference()))
@@ -103,7 +122,17 @@ func TestLoadWithSingleReg(t *testing.T) {
 	extension.SetProtocol("registry", GetProtocol)
 	extension.SetCluster(constant.ZONEAWARE_CLUSTER_NAME, cluster_impl.NewZoneAwareCluster)
 	extension.SetProxyFactory("default", proxy_factory.NewDefaultProxyFactory)
-
+	var mm *mockMetadataService
+	GetApplicationConfig().MetadataType = "mock"
+	extension.SetMetadataService("mock", func() (metadataService service.MetadataService, err error) {
+		if mm == nil {
+			mm = &mockMetadataService{
+				exportedServiceURLs: new(sync.Map),
+				lock:                new(sync.RWMutex),
+			}
+		}
+		return mm, nil
+	})
 	Load()
 
 	assert.Equal(t, ms, GetRPCService(ms.Reference()))
@@ -132,7 +161,17 @@ func TestWithNoRegLoad(t *testing.T) {
 	extension.SetProtocol("registry", GetProtocol)
 	extension.SetCluster(constant.ZONEAWARE_CLUSTER_NAME, cluster_impl.NewZoneAwareCluster)
 	extension.SetProxyFactory("default", proxy_factory.NewDefaultProxyFactory)
-
+	var mm *mockMetadataService
+	GetApplicationConfig().MetadataType = "mock"
+	extension.SetMetadataService("mock", func() (metadataService service.MetadataService, err error) {
+		if mm == nil {
+			mm = &mockMetadataService{
+				exportedServiceURLs: new(sync.Map),
+				lock:                new(sync.RWMutex),
+			}
+		}
+		return mm, nil
+	})
 	Load()
 
 	assert.Equal(t, ms, GetRPCService(ms.Reference()))
@@ -300,3 +339,234 @@ func mockInitProviderWithSingleRegistry() {
 		},
 	}
 }
+
+type mockMetadataService struct {
+	exportedServiceURLs *sync.Map
+	lock                *sync.RWMutex
+}
+
+func (m *mockMetadataService) Reference() string {
+	panic("implement me")
+}
+
+func (m *mockMetadataService) ServiceName() (string, error) {
+	panic("implement me")
+}
+
+func (m *mockMetadataService) ExportURL(url common.URL) (bool, error) {
+	return m.addURL(m.exportedServiceURLs, &url), nil
+}
+
+func (m *mockMetadataService) UnexportURL(url common.URL) error {
+	panic("implement me")
+}
+
+func (m *mockMetadataService) SubscribeURL(url common.URL) (bool, error) {
+	panic("implement me")
+}
+
+func (m *mockMetadataService) UnsubscribeURL(url common.URL) error {
+	panic("implement me")
+}
+
+func (m *mockMetadataService) PublishServiceDefinition(url common.URL) error {
+	return nil
+}
+
+func (m *mockMetadataService) GetExportedURLs(serviceInterface string, group string, version string, protocol string) ([]interface{}, error) {
+	return ConvertURLArrToIntfArr(m.getAllService(m.exportedServiceURLs)), nil
+}
+
+func (m *mockMetadataService) MethodMapper() map[string]string {
+	panic("implement me")
+}
+
+func (m *mockMetadataService) GetSubscribedURLs() ([]common.URL, error) {
+	panic("implement me")
+}
+
+func (m *mockMetadataService) GetServiceDefinition(interfaceName string, group string, version string) (string, error) {
+	panic("implement me")
+}
+
+func (m *mockMetadataService) GetServiceDefinitionByServiceKey(serviceKey string) (string, error) {
+	panic("implement me")
+}
+
+func (m *mockMetadataService) RefreshMetadata(exportedRevision string, subscribedRevision string) (bool, error) {
+	panic("implement me")
+}
+
+func (m *mockMetadataService) Version() (string, error) {
+	panic("implement me")
+}
+
+func (mts *mockMetadataService) addURL(targetMap *sync.Map, url *common.URL) bool {
+	var (
+		urlSet interface{}
+		loaded bool
+	)
+	logger.Debug(url.ServiceKey())
+	if urlSet, loaded = targetMap.LoadOrStore(url.ServiceKey(), skip.New(uint64(0))); loaded {
+		mts.lock.RLock()
+		wantedUrl := urlSet.(*skip.SkipList).Get(Comparator(*url))
+		if len(wantedUrl) > 0 && wantedUrl[0] != nil {
+			mts.lock.RUnlock()
+			return false
+		}
+		mts.lock.RUnlock()
+	}
+	mts.lock.Lock()
+	// double chk
+	wantedUrl := urlSet.(*skip.SkipList).Get(Comparator(*url))
+	if len(wantedUrl) > 0 && wantedUrl[0] != nil {
+		mts.lock.Unlock()
+		return false
+	}
+	urlSet.(*skip.SkipList).Insert(Comparator(*url))
+	mts.lock.Unlock()
+	return true
+}
+
+func (m *mockMetadataService) getAllService(services *sync.Map) []common.URL {
+	// using skip list to dedup and sorting
+	res := make([]common.URL, 0)
+	services.Range(func(key, value interface{}) bool {
+		urls := value.(*skip.SkipList)
+		for i := uint64(0); i < urls.Len(); i++ {
+			url := common.URL(urls.ByPosition(i).(Comparator))
+			if url.GetParam(constant.INTERFACE_KEY, url.Path) != constant.METADATA_SERVICE_NAME {
+				res = append(res, url)
+			}
+		}
+		return true
+	})
+	sort.Sort(common.URLSlice(res))
+	return res
+}
+
+type Comparator common.URL
+
+// Compare is defined as Comparator for skip list to compare the URL
+func (c Comparator) Compare(comp cm.Comparator) int {
+	a := common.URL(c).String()
+	b := common.URL(comp.(Comparator)).String()
+	switch {
+	case a > b:
+		return 1
+	case a < b:
+		return -1
+	default:
+		return 0
+	}
+}
+
+type mockServiceDiscoveryRegistry struct {
+}
+
+func (mr *mockServiceDiscoveryRegistry) GetUrl() common.URL {
+	panic("implement me")
+}
+
+func (mr *mockServiceDiscoveryRegistry) IsAvailable() bool {
+	panic("implement me")
+}
+
+func (mr *mockServiceDiscoveryRegistry) Destroy() {
+	panic("implement me")
+}
+
+func (mr *mockServiceDiscoveryRegistry) Register(url common.URL) error {
+	panic("implement me")
+}
+
+func (mr *mockServiceDiscoveryRegistry) UnRegister(url common.URL) error {
+	panic("implement me")
+}
+
+func (mr *mockServiceDiscoveryRegistry) Subscribe(*common.URL, registry.NotifyListener) error {
+	panic("implement me")
+}
+
+func (mr *mockServiceDiscoveryRegistry) UnSubscribe(*common.URL, registry.NotifyListener) error {
+	panic("implement me")
+}
+
+func (s *mockServiceDiscoveryRegistry) GetServiceDiscovery() registry.ServiceDiscovery {
+	return &mockServiceDiscovery{}
+}
+
+type mockServiceDiscovery struct {
+}
+
+func (m *mockServiceDiscovery) String() string {
+	panic("implement me")
+}
+
+func (m *mockServiceDiscovery) Destroy() error {
+	panic("implement me")
+}
+
+func (m *mockServiceDiscovery) Register(instance registry.ServiceInstance) error {
+	return nil
+}
+
+func (m *mockServiceDiscovery) Update(instance registry.ServiceInstance) error {
+	panic("implement me")
+}
+
+func (m *mockServiceDiscovery) Unregister(instance registry.ServiceInstance) error {
+	panic("implement me")
+}
+
+func (m *mockServiceDiscovery) GetDefaultPageSize() int {
+	panic("implement me")
+}
+
+func (m *mockServiceDiscovery) GetServices() *gxset.HashSet {
+	panic("implement me")
+}
+
+func (m *mockServiceDiscovery) GetInstances(serviceName string) []registry.ServiceInstance {
+	panic("implement me")
+}
+
+func (m *mockServiceDiscovery) GetInstancesByPage(serviceName string, offset int, pageSize int) gxpage.Pager {
+	panic("implement me")
+}
+
+func (m *mockServiceDiscovery) GetHealthyInstancesByPage(serviceName string, offset int, pageSize int, healthy bool) gxpage.Pager {
+	panic("implement me")
+}
+
+func (m *mockServiceDiscovery) GetRequestInstances(serviceNames []string, offset int, requestedSize int) map[string]gxpage.Pager {
+	panic("implement me")
+}
+
+func (m *mockServiceDiscovery) AddListener(listener *registry.ServiceInstancesChangedListener) error {
+	panic("implement me")
+}
+
+func (m *mockServiceDiscovery) DispatchEventByServiceName(serviceName string) error {
+	panic("implement me")
+}
+
+func (m *mockServiceDiscovery) DispatchEventForInstances(serviceName string, instances []registry.ServiceInstance) error {
+	panic("implement me")
+}
+
+func (m *mockServiceDiscovery) DispatchEvent(event *registry.ServiceInstancesChangedEvent) error {
+	panic("implement me")
+}
+
+func ConvertURLArrToIntfArr(urls []common.URL) []interface{} {
+	if len(urls) == 0 {
+		return []interface{}{}
+	}
+
+	res := make([]interface{}, 0, len(urls))
+	for _, u := range urls {
+		res = append(res, u.String())
+	}
+	return res
+}
diff --git a/config/consumer_config.go b/config/consumer_config.go
index 177531209225b7a4b25751352d9777c0d4ee260c..9d283eeca7bbaf5a82f71357853c6b53560b2fe4 100644
--- a/config/consumer_config.go
+++ b/config/consumer_config.go
@@ -24,7 +24,6 @@ import (
 
 import (
 	"github.com/creasty/defaults"
-	"github.com/dubbogo/getty"
 	perrors "github.com/pkg/errors"
 )
 
@@ -34,6 +33,10 @@ import (
 	"github.com/apache/dubbo-go/common/yaml"
 )
 
+const (
+	MaxWheelTimeSpan = 900e9 // 900s, 15 minute
+)
+
 /////////////////////////
 // consumerConfig
 /////////////////////////
@@ -107,9 +110,9 @@ func ConsumerInit(confConFile string) error {
 		if consumerConfig.RequestTimeout, err = time.ParseDuration(consumerConfig.Request_Timeout); err != nil {
 			return perrors.WithMessagef(err, "time.ParseDuration(Request_Timeout{%#v})", consumerConfig.Request_Timeout)
 		}
-		if consumerConfig.RequestTimeout >= time.Duration(getty.MaxWheelTimeSpan) {
+		if consumerConfig.RequestTimeout >= time.Duration(MaxWheelTimeSpan) {
 			return perrors.WithMessagef(err, "request_timeout %s should be less than %s",
-				consumerConfig.Request_Timeout, time.Duration(getty.MaxWheelTimeSpan))
+				consumerConfig.Request_Timeout, time.Duration(MaxWheelTimeSpan))
 		}
 	}
 	if consumerConfig.Connect_Timeout != "" {
diff --git a/config/reference_config_test.go b/config/reference_config_test.go
index e45780159615c8896627b003fd45c35af86d3f02..a4345ad13d4944b8fae0930930584fccbd0fb33c 100644
--- a/config/reference_config_test.go
+++ b/config/reference_config_test.go
@@ -32,6 +32,7 @@ import (
 	"github.com/apache/dubbo-go/common/constant"
 	"github.com/apache/dubbo-go/common/extension"
 	"github.com/apache/dubbo-go/protocol"
+	"github.com/apache/dubbo-go/registry"
 )
 
 var regProtocol protocol.Protocol
@@ -338,9 +339,37 @@ func (*mockRegistryProtocol) Refer(url common.URL) protocol.Invoker {
 }
 
 func (*mockRegistryProtocol) Export(invoker protocol.Invoker) protocol.Exporter {
+	registryUrl := getRegistryUrl(invoker)
+	if registryUrl.Protocol == "service-discovery" {
+		metaDataService, err := extension.GetMetadataService(GetApplicationConfig().MetadataType)
+		if err != nil {
+			panic(err)
+		}
+		ok, err := metaDataService.ExportURL(*invoker.GetUrl().SubURL.Clone())
+		if err != nil {
+			panic(err)
+		}
+		if !ok {
+			panic("The URL has been registry!")
+		}
+	}
 	return protocol.NewBaseExporter("test", invoker, &sync.Map{})
 }
 
 func (*mockRegistryProtocol) Destroy() {
 	// Destroy is a mock function
 }
+func getRegistryUrl(invoker protocol.Invoker) *common.URL {
+	// here add * for return a new url
+	url := invoker.GetUrl()
+	// if the protocol == registry ,set protocol the registry value in url.params
+	if url.Protocol == constant.REGISTRY_PROTOCOL {
+		protocol := url.GetParam(constant.REGISTRY_KEY, "")
+		url.Protocol = protocol
+	}
+	return &url
+}
+
+func (p *mockRegistryProtocol) GetRegistries() []registry.Registry {
+	return []registry.Registry{&mockServiceDiscoveryRegistry{}}
+}
diff --git a/config/service_config_test.go b/config/service_config_test.go
index d2bbda0c49ee18dfeb8e6475203f09d4c2fbb3b9..4d4122ee7057043af47aa0400ca8c5b5e9a20cd0 100644
--- a/config/service_config_test.go
+++ b/config/service_config_test.go
@@ -40,13 +40,33 @@ func doInitProvider() {
 				Module:       "module",
 				Version:      "2.6.0",
 				Owner:        "dubbo",
-				Environment:  "test"},
+				Environment:  "test",
+			},
+			Remotes: map[string]*RemoteConfig{
+				"test1": {
+					Address:    "127.0.0.5:2181",
+					TimeoutStr: "5s",
+					Username:   "user1",
+					Password:   "pwd1",
+					Params:     nil,
+				},
+			},
+			ServiceDiscoveries: map[string]*ServiceDiscoveryConfig{
+				"mock_servicediscovery": {
+					Protocol:  "mock",
+					RemoteRef: "test1",
+				},
+			},
+			MetadataReportConfig: &MetadataReportConfig{
+				Protocol:  "mock",
+				RemoteRef: "test1",
+			},
 		},
 		Services: map[string]*ServiceConfig{
 			"MockService": {
 				InterfaceName: "com.MockService",
 				Protocol:      "mock",
-				Registry:      "shanghai_reg1,shanghai_reg2,hangzhou_reg1,hangzhou_reg2",
+				Registry:      "shanghai_reg1,shanghai_reg2,hangzhou_reg1,hangzhou_reg2,hangzhou_service_discovery_reg",
 				Cluster:       "failover",
 				Loadbalance:   "random",
 				Retries:       "3",
@@ -71,7 +91,7 @@ func doInitProvider() {
 			"MockServiceNoRightProtocol": {
 				InterfaceName: "com.MockService",
 				Protocol:      "mock1",
-				Registry:      "shanghai_reg1,shanghai_reg2,hangzhou_reg1,hangzhou_reg2",
+				Registry:      "shanghai_reg1,shanghai_reg2,hangzhou_reg1,hangzhou_reg2,hangzhou_service_discovery_reg",
 				Cluster:       "failover",
 				Loadbalance:   "random",
 				Retries:       "3",
@@ -128,6 +148,14 @@ func doInitProvider() {
 				Username:   "user1",
 				Password:   "pwd1",
 			},
+			"hangzhou_service_discovery_reg": {
+				Protocol: "service-discovery",
+				Params: map[string]string{
+					"service_discovery": "mock_servicediscovery",
+					"name_mapping":      "in-memory",
+					"metadata":          "default",
+				},
+			},
 		},
 
 		Protocols: map[string]*ProtocolConfig{
diff --git a/config/ssl_config.go b/config/ssl_config.go
index 019f83d3cbbc3191712f71fc4c6b6c9c972270b2..8576930367a487269bf82d51191bb02289dd1e73 100644
--- a/config/ssl_config.go
+++ b/config/ssl_config.go
@@ -18,7 +18,7 @@
 package config
 
 import (
-	"github.com/dubbogo/getty"
+	"github.com/apache/dubbo-getty"
 )
 
 var (
diff --git a/config_center/apollo/impl.go b/config_center/apollo/impl.go
index b049d334bca7e5191caaf9674734e731bc709ba2..8030a2c800c67d47a27e7aaa5d6f1bb39a83cdc9 100644
--- a/config_center/apollo/impl.go
+++ b/config_center/apollo/impl.go
@@ -27,7 +27,9 @@ import (
 import (
 	gxset "github.com/dubbogo/gost/container/set"
 	perrors "github.com/pkg/errors"
-	"github.com/zouyx/agollo"
+	"github.com/zouyx/agollo/v3"
+	agolloConstant "github.com/zouyx/agollo/v3/constant"
+	"github.com/zouyx/agollo/v3/env/config"
 )
 
 import (
@@ -35,19 +37,18 @@ import (
 	"github.com/apache/dubbo-go/common/constant"
 	cc "github.com/apache/dubbo-go/config_center"
 	"github.com/apache/dubbo-go/config_center/parser"
-	"github.com/apache/dubbo-go/remoting"
 )
 
 const (
 	apolloProtocolPrefix = "http://"
-	apolloConfigFormat   = "%s.%s"
+	apolloConfigFormat   = "%s%s"
 )
 
 type apolloConfiguration struct {
 	url *common.URL
 
 	listeners sync.Map
-	appConf   *agollo.AppConfig
+	appConf   *config.AppConfig
 	parser    parser.ConfigurationParser
 }
 
@@ -60,31 +61,20 @@ func newApolloConfiguration(url *common.URL) (*apolloConfiguration, error) {
 
 	appId := url.GetParam(constant.CONFIG_APP_ID_KEY, "")
 	namespaces := getProperties(url.GetParam(constant.CONFIG_NAMESPACE_KEY, cc.DEFAULT_GROUP))
-	c.appConf = &agollo.AppConfig{
-		AppId:         appId,
+	c.appConf = &config.AppConfig{
+		AppID:         appId,
 		Cluster:       configCluster,
 		NamespaceName: namespaces,
-		Ip:            configAddr,
+		IP:            configAddr,
 	}
 
-	agollo.InitCustomConfig(func() (*agollo.AppConfig, error) {
+	agollo.InitCustomConfig(func() (*config.AppConfig, error) {
 		return c.appConf, nil
 	})
 
 	return c, agollo.Start()
 }
 
-func getChangeType(change agollo.ConfigChangeType) remoting.EventType {
-	switch change {
-	case agollo.ADDED:
-		return remoting.EventTypeAdd
-	case agollo.DELETED:
-		return remoting.EventTypeDel
-	default:
-		return remoting.EventTypeUpdate
-	}
-}
-
 func (c *apolloConfiguration) AddListener(key string, listener cc.ConfigurationListener, opts ...cc.Option) {
 	k := &cc.Options{}
 	for _, opt := range opts {
@@ -92,7 +82,7 @@ func (c *apolloConfiguration) AddListener(key string, listener cc.ConfigurationL
 	}
 
 	key = k.Group + key
-	l, _ := c.listeners.LoadOrStore(key, NewApolloListener())
+	l, _ := c.listeners.LoadOrStore(key, newApolloListener())
 	l.(*apolloListener).AddListener(listener)
 }
 
@@ -110,10 +100,10 @@ func (c *apolloConfiguration) RemoveListener(key string, listener cc.Configurati
 }
 
 func getProperties(namespace string) string {
-	return getNamespaceName(namespace, agollo.Properties)
+	return getNamespaceName(namespace, agolloConstant.Properties)
 }
 
-func getNamespaceName(namespace string, configFileFormat agollo.ConfigFileFormat) string {
+func getNamespaceName(namespace string, configFileFormat agolloConstant.ConfigFileFormat) string {
 	return fmt.Sprintf(apolloConfigFormat, namespace, configFileFormat)
 }
 
@@ -148,7 +138,7 @@ func (c *apolloConfiguration) GetProperties(key string, opts ...cc.Option) (stri
 	if config == nil {
 		return "", perrors.New(fmt.Sprintf("nothing in namespace:%s ", key))
 	}
-	return config.GetContent(agollo.Properties), nil
+	return config.GetContent(), nil
 }
 
 func (c *apolloConfiguration) getAddressWithProtocolPrefix(url *common.URL) string {
diff --git a/config_center/apollo/impl_test.go b/config_center/apollo/impl_test.go
index 335fb71045ca6349557cf7c736cead0565bdc193..50c4e689de4542e1c595d0778e2dce69b1e7dda9 100644
--- a/config_center/apollo/impl_test.go
+++ b/config_center/apollo/impl_test.go
@@ -202,9 +202,9 @@ func initMockApollo(t *testing.T) *apolloConfiguration {
 	return configuration
 }
 
-func TestAddListener(t *testing.T) {
+func TestListener(t *testing.T) {
 	listener := &apolloDataListener{}
-	listener.wg.Add(1)
+	listener.wg.Add(2)
 	apollo := initMockApollo(t)
 	mockConfigRes = `{
 	"appId": "testApplication_yang",
@@ -215,28 +215,14 @@ func TestAddListener(t *testing.T) {
 	},
 	"releaseKey": "20191104105242-0f13805d89f834a4"
 }`
+	//test add
 	apollo.AddListener(mockNamespace, listener)
 	listener.wg.Wait()
-	assert.Equal(t, "registries.hangzhouzk.username", listener.event)
+	assert.Equal(t, "mockDubbog.properties", listener.event)
 	assert.Greater(t, listener.count, 0)
-	deleteMockJson(t)
-}
 
-func TestRemoveListener(t *testing.T) {
-	listener := &apolloDataListener{}
-	apollo := initMockApollo(t)
-	mockConfigRes = `{
-	"appId": "testApplication_yang",
-	"cluster": "default",
-	"namespaceName": "mockDubbog.properties",
-	"configurations": {
-		"registries.hangzhouzk.username": "11111"
-	},
-	"releaseKey": "20191104105242-0f13805d89f834a4"
-}`
-	apollo.AddListener(mockNamespace, listener)
+	//test remove
 	apollo.RemoveListener(mockNamespace, listener)
-	assert.Equal(t, "", listener.event)
 	listenerCount := 0
 	apollo.listeners.Range(func(_, value interface{}) bool {
 		apolloListener := value.(*apolloListener)
@@ -247,7 +233,6 @@ func TestRemoveListener(t *testing.T) {
 		return true
 	})
 	assert.Equal(t, listenerCount, 0)
-	assert.Equal(t, listener.count, 0)
 	deleteMockJson(t)
 }
 
diff --git a/config_center/apollo/listener.go b/config_center/apollo/listener.go
index 48a35093d20c445f6b6ff391f6a2e0e5b737e8f2..ace5ed026875d2478e5d81b1974a6c60f856378c 100644
--- a/config_center/apollo/listener.go
+++ b/config_center/apollo/listener.go
@@ -18,34 +18,48 @@
 package apollo
 
 import (
-	"github.com/zouyx/agollo"
+	"github.com/zouyx/agollo/v3"
+	"github.com/zouyx/agollo/v3/storage"
+	"gopkg.in/yaml.v2"
 )
 
 import (
+	"github.com/apache/dubbo-go/common/logger"
 	"github.com/apache/dubbo-go/config_center"
+	"github.com/apache/dubbo-go/remoting"
 )
 
 type apolloListener struct {
 	listeners map[config_center.ConfigurationListener]struct{}
 }
 
-// NewApolloListener creates a new apolloListener
-func NewApolloListener() *apolloListener {
+// nolint
+func newApolloListener() *apolloListener {
 	return &apolloListener{
 		listeners: make(map[config_center.ConfigurationListener]struct{}, 0),
 	}
 }
 
 // OnChange process each listener
-func (a *apolloListener) OnChange(changeEvent *agollo.ChangeEvent) {
-	for key, change := range changeEvent.Changes {
-		for listener := range a.listeners {
-			listener.Process(&config_center.ConfigChangeEvent{
-				ConfigType: getChangeType(change.ChangeType),
-				Key:        key,
-				Value:      change.NewValue,
-			})
-		}
+func (a *apolloListener) OnChange(changeEvent *storage.ChangeEvent) {
+
+}
+
+// OnNewestChange process each listener by all changes
+func (a *apolloListener) OnNewestChange(changeEvent *storage.FullChangeEvent) {
+	b, err := yaml.Marshal(changeEvent.Changes)
+	if err != nil {
+		logger.Errorf("apollo onNewestChange err %+v",
+			err)
+		return
+	}
+	content := string(b)
+	for listener := range a.listeners {
+		listener.Process(&config_center.ConfigChangeEvent{
+			ConfigType: remoting.EventTypeUpdate,
+			Key:        changeEvent.Namespace,
+			Value:      content,
+		})
 	}
 }
 
diff --git a/config_center/nacos/client.go b/config_center/nacos/client.go
index 9a09b713fad11afdc06310a2d0072454342ccb0b..acfe2609ceb0fc62650b7b494b25084ea4df6946 100644
--- a/config_center/nacos/client.go
+++ b/config_center/nacos/client.go
@@ -98,6 +98,7 @@ func ValidateNacosClient(container nacosClientFacade, opts ...option) error {
 	}
 	nacosAddresses := strings.Split(url.Location, ",")
 	if container.NacosClient() == nil {
+		//in dubbo ,every registry only connect one node ,so this is []string{r.Address}
 		newClient, err := newNacosClient(os.nacosName, nacosAddresses, timeout, url)
 		if err != nil {
 			logger.Errorf("newNacosClient(name{%s}, nacos address{%v}, timeout{%d}) = error{%v}",
@@ -115,6 +116,7 @@ func ValidateNacosClient(container nacosClientFacade, opts ...option) error {
 			return perrors.WithMessagef(err, "newNacosClient(address:%+v)", url.Location)
 		}
 		container.NacosClient().SetClient(&configClient)
+
 	}
 
 	return perrors.WithMessagef(nil, "newNacosClient(address:%+v)", url.PrimitiveURL)
@@ -167,8 +169,9 @@ func initNacosConfigClient(nacosAddrs []string, timeout time.Duration, url commo
 		"serverConfigs": svrConfList,
 		"clientConfig": nacosconst.ClientConfig{
 			TimeoutMs:           uint64(int32(timeout / time.Millisecond)),
+			ListenInterval:      uint64(int32(timeout / time.Millisecond)),
 			NotLoadCacheAtStart: true,
-			LogDir:              url.GetParam(constant.NACOS_LOG_DIR_KEY, logDir),
+			LogDir:              url.GetParam(constant.NACOS_LOG_DIR_KEY, ""),
 			CacheDir:            url.GetParam(constant.NACOS_CACHE_DIR_KEY, ""),
 			Endpoint:            url.GetParam(constant.NACOS_ENDPOINT, ""),
 			Username:            url.GetParam(constant.NACOS_USERNAME, ""),
diff --git a/config_center/nacos/facade.go b/config_center/nacos/facade.go
index 77a79ed091461ea5184cb2531d985c233ccd92e9..d089ed26d2810e5c5076ec4b6342cbe10d00799d 100644
--- a/config_center/nacos/facade.go
+++ b/config_center/nacos/facade.go
@@ -22,7 +22,7 @@ import (
 	"time"
 )
 import (
-	"github.com/dubbogo/getty"
+	"github.com/apache/dubbo-getty"
 	perrors "github.com/pkg/errors"
 )
 
diff --git a/config_center/nacos/listener.go b/config_center/nacos/listener.go
index 3118a9d0529bf8762dc7ee864af32b044b74fd49..57df0363a1d7712e1e02a66595030c207b19c170 100644
--- a/config_center/nacos/listener.go
+++ b/config_center/nacos/listener.go
@@ -48,6 +48,7 @@ func (n *nacosDynamicConfiguration) addListener(key string, listener config_cent
 		})
 		if err != nil {
 			logger.Errorf("nacos : listen config fail, error:%v ", err)
+			return
 		}
 		newListener := make(map[config_center.ConfigurationListener]context.CancelFunc)
 		newListener[listener] = cancel
diff --git a/contributing.md b/contributing.md
index 9ee2dae32fad6caaf9e19c5e98e8b99b61c26a51..51301511e01c12e388edf574e5c8660ad4a542a1 100644
--- a/contributing.md
+++ b/contributing.md
@@ -38,4 +38,27 @@ The title format of the pull request `MUST` follow the following rules:
 ### 3.3 comment
 
 >- 1 there should be comment for every export func/var.
->- 2 the comment should begin with function name/var name.
\ No newline at end of file
+>- 2 the comment should begin with function name/var name.
+
+### 3.4 import 
+
+We dubbogo import blocks should be splited into 3 blocks.
+
+```Go
+// block 1: the go internal package
+import (
+  "fmt"
+)
+
+// block 2: the third package
+import (
+  "github.com/dubbogo/xxx"
+  
+  "github.com/RoaringBitmap/roaring"
+)
+
+// block 3: the dubbo-go package
+import (
+  "github.com/apache/dubbo-go/common"
+) 
+```
\ No newline at end of file
diff --git a/doc/pic/arch/dubbo-go-arch.png b/doc/pic/arch/dubbo-go-arch.png
index e5f192715216257929cf4a550a1adf3588ab0e0f..0a28cb6a3af6953d2ad10390ddff5b7d82166ecc 100644
Binary files a/doc/pic/arch/dubbo-go-arch.png and b/doc/pic/arch/dubbo-go-arch.png differ
diff --git a/doc/pic/arch/dubbo-go-ext.png b/doc/pic/arch/dubbo-go-ext.png
index d065ef4d8e28e507b2ee36fd8c6c6928ab7c9b5d..b60ecdf47ef190f097fccaf2d74070f6712ea80d 100644
Binary files a/doc/pic/arch/dubbo-go-ext.png and b/doc/pic/arch/dubbo-go-ext.png differ
diff --git a/filter/filter_impl/hystrix_filter.go b/filter/filter_impl/hystrix_filter.go
index e2275149f1229c87ed4ae3f89dc9ae32d9fe6461..d13e02c06f7b2191e4d01c013a72300e1b095616 100644
--- a/filter/filter_impl/hystrix_filter.go
+++ b/filter/filter_impl/hystrix_filter.go
@@ -53,10 +53,6 @@ var (
 	providerConfigOnce sync.Once
 )
 
-//The filter in the server end of dubbo-go can't get the invoke result for now,
-//this filter ONLY works in CLIENT end (consumer side) temporarily
-//Only after the callService logic is integrated into the filter chain of server end then the filter can be used,
-//which will be done soon
 func init() {
 	extension.SetFilter(HYSTRIX_CONSUMER, GetHystrixFilterConsumer)
 	extension.SetFilter(HYSTRIX_PROVIDER, GetHystrixFilterProvider)
@@ -85,7 +81,47 @@ func NewHystrixFilterError(err error, failByHystrix bool) error {
 	}
 }
 
-// nolint
+/**
+ * HystrixFilter
+ * You should add hystrix related configuration in provider or consumer config or both, according to which side you are to apply HystrixFilter.
+ * For example:
+ * filter_conf:
+ * 	hystrix:
+ * 	 configs:
+ * 	  # =========== Define config here ============
+ * 	  "Default":
+ * 	    timeout : 1000
+ * 	    max_concurrent_requests : 25
+ * 	    sleep_window : 5000
+ * 	    error_percent_threshold : 50
+ * 	    request_volume_threshold: 20
+ * 	  "userp":
+ * 	    timeout: 2000
+ * 	    max_concurrent_requests: 512
+ * 	    sleep_window: 4000
+ * 	    error_percent_threshold: 35
+ * 	    request_volume_threshold: 6
+ * 	  "userp_m":
+ * 	    timeout : 1200
+ * 	    max_concurrent_requests : 512
+ * 	    sleep_window : 6000
+ * 	    error_percent_threshold : 60
+ * 	    request_volume_threshold: 16
+ *      # =========== Define error whitelist which will be ignored by Hystrix counter ============
+ * 	    error_whitelist: [".*exception.*"]
+ *
+ * 	 # =========== Apply default config here ===========
+ * 	 default: "Default"
+ *
+ * 	 services:
+ * 	  "com.ikurento.user.UserProvider":
+ * 	    # =========== Apply service level config ===========
+ * 	    service_config: "userp"
+ * 	    # =========== Apply method level config ===========
+ * 	    methods:
+ * 	      "GetUser": "userp_m"
+ * 	      "GetUser1": "userp_m"
+ */
 type HystrixFilter struct {
 	COrP     bool //true for consumer
 	res      map[string][]*regexp.Regexp
@@ -213,11 +249,11 @@ func getConfig(service string, method string, cOrP bool) CommandConfigWithError
 
 func initHystrixConfigConsumer() error {
 	if config.GetConsumerConfig().FilterConf == nil {
-		return perrors.Errorf("no config for hystrix")
+		return perrors.Errorf("no config for hystrix_consumer")
 	}
 	filterConfig := config.GetConsumerConfig().FilterConf.(map[interface{}]interface{})[HYSTRIX]
 	if filterConfig == nil {
-		return perrors.Errorf("no config for hystrix")
+		return perrors.Errorf("no config for hystrix_consumer")
 	}
 	hystrixConfByte, err := yaml.Marshal(filterConfig)
 	if err != nil {
@@ -232,11 +268,11 @@ func initHystrixConfigConsumer() error {
 
 func initHystrixConfigProvider() error {
 	if config.GetProviderConfig().FilterConf == nil {
-		return perrors.Errorf("no config for hystrix")
+		return perrors.Errorf("no config for hystrix_provider")
 	}
-	filterConfig := config.GetConsumerConfig().FilterConf.(map[interface{}]interface{})[HYSTRIX]
+	filterConfig := config.GetProviderConfig().FilterConf.(map[interface{}]interface{})[HYSTRIX]
 	if filterConfig == nil {
-		return perrors.Errorf("no config for hystrix")
+		return perrors.Errorf("no config for hystrix_provider")
 	}
 	hystrixConfByte, err := yaml.Marshal(filterConfig)
 	if err != nil {
diff --git a/go.mod b/go.mod
index 511f823088b27774ee6c41772d00af7e13a2ea91..c9737b9af90178cf4e689771e962fc7590d875a2 100644
--- a/go.mod
+++ b/go.mod
@@ -1,19 +1,18 @@
 module github.com/apache/dubbo-go
 
 require (
-	cloud.google.com/go v0.39.0 // indirect
 	github.com/Microsoft/go-winio v0.4.13 // indirect
 	github.com/NYTimes/gziphandler v1.1.1 // indirect
+	github.com/RoaringBitmap/roaring v0.4.23
 	github.com/Workiva/go-datastructures v1.0.50
 	github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5
-	github.com/apache/dubbo-go-hessian2 v1.6.1
+	github.com/apache/dubbo-getty v1.3.10
+	github.com/apache/dubbo-go-hessian2 v1.6.2
 	github.com/coreos/bbolt v1.3.3 // indirect
 	github.com/coreos/etcd v3.3.13+incompatible
-	github.com/coreos/go-semver v0.3.0 // indirect
 	github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f // indirect
 	github.com/creasty/defaults v1.3.0
 	github.com/docker/go-connections v0.4.0 // indirect
-	github.com/dubbogo/getty v1.3.9
 	github.com/dubbogo/go-zookeeper v1.0.1
 	github.com/dubbogo/gost v1.9.1
 	github.com/elazarl/go-bindata-assetfs v1.0.0 // indirect
@@ -48,20 +47,19 @@ require (
 	github.com/prometheus/client_golang v1.1.0
 	github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b
 	github.com/shirou/gopsutil v2.19.9+incompatible // indirect
-	github.com/smartystreets/goconvey v0.0.0-20190710185942-9d28bd7c0945 // indirect
 	github.com/stretchr/objx v0.2.0 // indirect
 	github.com/stretchr/testify v1.5.1
-	github.com/zouyx/agollo v0.0.0-20191114083447-dde9fc9f35b8
+	github.com/zouyx/agollo/v3 v3.4.4
 	go.etcd.io/bbolt v1.3.4 // indirect
 	go.uber.org/atomic v1.6.0
 	go.uber.org/zap v1.15.0
-	google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64 // indirect
 	google.golang.org/grpc v1.23.0
 	gopkg.in/yaml.v2 v2.2.8
 	k8s.io/api v0.16.9
 	k8s.io/apimachinery v0.16.9
 	k8s.io/client-go v0.16.9
 	k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a // indirect
+
 )
 
 go 1.13
diff --git a/go.sum b/go.sum
index fd448bf3bcdfa8478bd60bbb6b44c1250e79eb7e..c5ab85db14cc95ca332d6f81b0aca9c6a9823090 100644
--- a/go.sum
+++ b/go.sum
@@ -1,8 +1,17 @@
 cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
 cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
 cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
-cloud.google.com/go v0.39.0 h1:UgQP9na6OTfp4dsAiz/eFpFA1C6tPdH5wiRdi19tuMw=
-cloud.google.com/go v0.39.0/go.mod h1:rVLT6fkc8chs9sfPtFc1SBH6em7n+ZoXaG+87tDISts=
+cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
+cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
+cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
+cloud.google.com/go v0.46.3 h1:AVXDdKsrtX33oR9fbCMu/+c1o8Ofjq6Ku/MInaLVg5Y=
+cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
+cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
+cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
+cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
+cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
+cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
+dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
 github.com/Azure/azure-sdk-for-go v40.3.0+incompatible h1:NthZg3psrLxvQLN6rVm07pZ9mv2wvGNaBNGQ3fnPvLE=
 github.com/Azure/azure-sdk-for-go v40.3.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
 github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
@@ -35,6 +44,7 @@ github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VY
 github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
 github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
 github.com/DataDog/datadog-go v2.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
 github.com/DataDog/datadog-go v3.2.0+incompatible h1:qSG2N4FghB1He/r2mFrWKCaL7dXCilEuNEeAn20fdD4=
 github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
@@ -48,6 +58,8 @@ github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMo
 github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
 github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
 github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
+github.com/RoaringBitmap/roaring v0.4.23 h1:gpyfd12QohbqhFO4NVDUdoPOCXsyahYRQhINmlHxKeo=
+github.com/RoaringBitmap/roaring v0.4.23/go.mod h1:D0gp8kJQgE1A4LQ5wFLggQEyvDi06Mq5mKs52e1TwOo=
 github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 h1:fLjPD/aNc3UIOA6tDi6QXUemppXK3P9BI7mr2hd6gx8=
 github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
 github.com/Workiva/go-datastructures v1.0.50 h1:slDmfW6KCHcC7U+LP3DDBbm4fqTwZGn1beOFPfGaLvo=
@@ -60,8 +72,10 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy
 github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
 github.com/aliyun/alibaba-cloud-sdk-go v1.61.18 h1:zOVTBdCKFd9JbCKz9/nt+FovbjPFmb7mUnp8nH9fQBA=
 github.com/aliyun/alibaba-cloud-sdk-go v1.61.18/go.mod h1:v8ESoHo4SyHmuB4b1tJqDHxfTGEciD+yhvOU/5s1Rfk=
-github.com/apache/dubbo-go-hessian2 v1.6.1 h1:mFKeCZzaCkk4mMOyP+LQ85GHbRyqKT7858KS21JQYA4=
-github.com/apache/dubbo-go-hessian2 v1.6.1/go.mod h1:7rEw9guWABQa6Aqb8HeZcsYPHsOS7XT1qtJvkmI6c5w=
+github.com/apache/dubbo-getty v1.3.10 h1:ys5mwjPdxG/KwkPjS6EI0RzQtU6p6FCPoKpaFEzpAL0=
+github.com/apache/dubbo-getty v1.3.10/go.mod h1:x6rraK01BL5C7jUM2fPl5KMkAxLVIx54ZB8/XEOik9Y=
+github.com/apache/dubbo-go-hessian2 v1.6.2 h1:i7F5GjVaUatLQz1x9vUmmSIFj49L8J6rVICdF6xw4qw=
+github.com/apache/dubbo-go-hessian2 v1.6.2/go.mod h1:7rEw9guWABQa6Aqb8HeZcsYPHsOS7XT1qtJvkmI6c5w=
 github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e h1:QEF07wC0T1rKkctt1RINW/+RMTVmiwxETico2l3gxJA=
 github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
 github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
@@ -80,6 +94,7 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
 github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
 github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY=
 github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
+github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
 github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4=
 github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
 github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23 h1:D21IyuvjDCshj1/qq+pCNd3VZOAEI9jy6Bi131YlXgI=
@@ -133,8 +148,6 @@ github.com/docker/go-connections v0.3.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5Xh
 github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
 github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
 github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
-github.com/dubbogo/getty v1.3.9 h1:Ip/4Yl7GyDzt3ddZhjXN1Vmxvo9y/hdtoJ4yFc7czLk=
-github.com/dubbogo/getty v1.3.9/go.mod h1:JIN5lNZznGnBuoYjlEe/yjNmq7eSjKaawHdnBAdtiEo=
 github.com/dubbogo/go-zookeeper v1.0.1 h1:irLzvOsDOTNsN8Sv9tvYYxVu6DCQfLtziZQtUHmZgz8=
 github.com/dubbogo/go-zookeeper v1.0.1/go.mod h1:fn6n2CAEer3novYgk9ULLwAjuV8/g4DdC2ENwRb6E+c=
 github.com/dubbogo/gost v1.9.0 h1:UT+dWwvLyJiDotxJERO75jB3Yxgsdy10KztR5ycxRAk=
@@ -170,12 +183,17 @@ github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2H
 github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
 github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 h1:Mn26/9ZMNWSw9C9ERFA1PUxfmGpolnw2v0bKOREu5ew=
 github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I=
+github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2 h1:Ujru1hufTHVb++eG6OuNDKMxZnGIvF6o/u8q/8h2+I4=
+github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
+github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31 h1:gclg6gY70GLy3PbkQ1AERPfmLMMagS60DKF78eWwLn8=
+github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24=
 github.com/go-asn1-ber/asn1-ber v1.3.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
 github.com/go-check/check v0.0.0-20140225173054-eb6ee6f84d0a/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98=
 github.com/go-co-op/gocron v0.1.1 h1:OfDmkqkCguFtFMsm6Eaayci3DADLa8pXvdmOlPU/JcU=
 github.com/go-co-op/gocron v0.1.1/go.mod h1:Y9PWlYqDChf2Nbgg7kfS+ZsXHDTZbMZYPEQ0MILqH+M=
 github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
 github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
+github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
 github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc=
 github.com/go-ldap/ldap/v3 v3.1.3/go.mod h1:3rbOH3jRS2u6jg2rJnKAMLE/xQyCKIveG2Sa/Cohzb8=
@@ -234,11 +252,14 @@ github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
 github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
+github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
 github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
 github.com/google/tcpproxy v0.0.0-20180808230851-dfa16c61dad2 h1:AtvtonGEH/fZK0XPNNBdB6swgy7Iudfx88wzyIpwqJ8=
 github.com/google/tcpproxy v0.0.0-20180808230851-dfa16c61dad2/go.mod h1:DavVbd41y+b7ukKDmlnPR4nGYmkWXR6vHUkjQNiHPBs=
 github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
+github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
+github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
 github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
 github.com/googleapis/gnostic v0.2.0 h1:l6N3VoaVzTncYYW+9yOz2LJJammFZGBO13sqgEhpy9g=
 github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
@@ -246,6 +267,8 @@ github.com/gophercloud/gophercloud v0.1.0 h1:P/nh25+rzXouhytV2pUHBb65fnds26Ghl8/
 github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
 github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
 github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99 h1:twflg0XRTjwKpxb/jFExr4HGq6on2dEOmnL6FV+fgPw=
+github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
 github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
 github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
 github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
@@ -262,8 +285,10 @@ github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 h1
 github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645/go.mod h1:6iZfnjpejD4L/4DwD7NryNaJyCQdzwWwH2MWhCA90Kw=
 github.com/hashicorp/consul v1.8.0 h1:yRKMKZyPLqUxl37t4nFt5OuGmTXoFhTJrakhfnYKCYA=
 github.com/hashicorp/consul v1.8.0/go.mod h1:Gg9/UgAQ9rdY3CTvzQZ6g2jcIb7NlIfjI+0pvLk5D1A=
+github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
 github.com/hashicorp/consul/api v1.5.0 h1:Yo2bneoGy68A7aNwmuETFnPhjyBEm7n3vzRacEVMjvI=
 github.com/hashicorp/consul/api v1.5.0/go.mod h1:LqwrLNW876eYSuUOo4ZLHBcdKc038txr/IMfbLPATa4=
+github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
 github.com/hashicorp/consul/sdk v0.5.0 h1:WC4594Wp/LkEeML/OdQKEC1yqBmEYkRp6i7X5u0zDAs=
 github.com/hashicorp/consul/sdk v0.5.0/go.mod h1:fY08Y9z5SvJqevyZNy6WWPXiG3KwBPAvlcdx16zZ0fM=
 github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
@@ -303,6 +328,7 @@ github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es
 github.com/hashicorp/go-retryablehttp v0.5.4/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
 github.com/hashicorp/go-retryablehttp v0.6.2 h1:bHM2aVXwBtBJWxHtkSrWuI4umABCUczs52eiUS9nSiw=
 github.com/hashicorp/go-retryablehttp v0.6.2/go.mod h1:gEx6HMUGxYYhJScX7W1Il64m6cc2C1mDaW3NQ9sY1FY=
+github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
 github.com/hashicorp/go-rootcerts v1.0.1/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
 github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc=
 github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
@@ -319,6 +345,7 @@ github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b
 github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
 github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+dIzX/E=
 github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
+github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
 github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk=
@@ -328,8 +355,10 @@ github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T
 github.com/hashicorp/hil v0.0.0-20160711231837-1e86c6b523c5 h1:uk280DXEbQiCOZgCOI3elFSeNxf8YIZiNsbr2pQLYD0=
 github.com/hashicorp/hil v0.0.0-20160711231837-1e86c6b523c5/go.mod h1:KHvg/R2/dPtaePb16oW4qIyzkMxXOL38xjRN64adsts=
 github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
+github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
 github.com/hashicorp/mdns v1.0.1 h1:XFSOubp8KWB+Jd2PDyaX5xUd5bhSP/+pTDZVDMzZJM8=
 github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY=
+github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
 github.com/hashicorp/memberlist v0.2.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE=
 github.com/hashicorp/memberlist v0.2.2 h1:5+RffWKwqJ71YPu9mWsF7ZOscZmwfasdA8kbdC7AO2g=
 github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE=
@@ -341,6 +370,7 @@ github.com/hashicorp/raft v1.1.2 h1:oxEL5DDeurYxLd3UbcY/hccgSPhLLpiBZ1YxtWEq59c=
 github.com/hashicorp/raft v1.1.2/go.mod h1:vPAJM8Asw6u8LxC3eJCUZmRP/E4QmUGE1R7g7k8sG/8=
 github.com/hashicorp/raft-boltdb v0.0.0-20171010151810-6e5ba93211ea h1:xykPFhrBAS2J0VBzVa5e80b5ZtYuNQtgXjN40qBZlD4=
 github.com/hashicorp/raft-boltdb v0.0.0-20171010151810-6e5ba93211ea/go.mod h1:pNv7Wc3ycL6F5oOWn+tPGo2gWD4a5X+yp/ntwdKLjRk=
+github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
 github.com/hashicorp/serf v0.9.0/go.mod h1:YL0HO+FifKOW2u1ke99DGVu1zhcpZzNwrLIqBC7vbYU=
 github.com/hashicorp/serf v0.9.2 h1:yJoyfZXo4Pk2p/M/viW+YLibBFiIbKoP79gu7kDAFP0=
 github.com/hashicorp/serf v0.9.2/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk=
@@ -433,6 +463,7 @@ github.com/mitchellh/cli v1.1.0 h1:tEElEatulEHDeedTxwckzyYMA5c86fbmNIUL1hBIiTg=
 github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI=
 github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ=
 github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
+github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
 github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
 github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
 github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
@@ -440,9 +471,11 @@ github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eI
 github.com/mitchellh/go-testing-interface v1.14.0 h1:/x0XQ6h+3U3nAyk1yx+bHPURrKa9sVVvYbuqZ7pIAtI=
 github.com/mitchellh/go-testing-interface v1.14.0/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8=
 github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
+github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
 github.com/mitchellh/hashstructure v0.0.0-20170609045927-2bca23e0e452/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ=
 github.com/mitchellh/hashstructure v1.0.0 h1:ZkRJX1CyOoTkar7p/mLS5TZU4nJ1Rn/F8u9dGS02Q3Y=
 github.com/mitchellh/hashstructure v1.0.0/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ=
+github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
 github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
 github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
 github.com/mitchellh/mapstructure v1.2.3 h1:f/MjBEBDLttYCGfRaKBbKSRVF5aV2O6fnBpzknuE3jU=
@@ -459,6 +492,8 @@ github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lN
 github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
 github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
 github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae h1:VeRdUYdCw49yizlSbMEn2SZ+gT+3IUKx8BqxyQdz+BY=
+github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg=
 github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
 github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
 github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
@@ -487,8 +522,11 @@ github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0Mw
 github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
 github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
 github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
+github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
 github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
 github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
+github.com/philhofer/fwd v1.0.0 h1:UbZqGr5Y38ApvM/V/jEljVxwocdweyH+vmYvRPBnbqQ=
+github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
 github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
 github.com/pierrec/lz4 v2.2.6+incompatible h1:6aCX4/YZ9v8q69hTyiR7dNLnTA3fgtKHVVW5BCd5Znw=
 github.com/pierrec/lz4 v2.2.6+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
@@ -558,8 +596,8 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd
 github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
 github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
 github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
-github.com/smartystreets/goconvey v0.0.0-20190710185942-9d28bd7c0945 h1:N8Bg45zpk/UcpNGnfJt2y/3lRWASHNTUET8owPYCgYI=
-github.com/smartystreets/goconvey v0.0.0-20190710185942-9d28bd7c0945/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
+github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
+github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
 github.com/softlayer/softlayer-go v0.0.0-20180806151055-260589d94c7d h1:bVQRCxQvfjNUeRqaY/uT0tFuvuFY0ulgnczuR684Xic=
 github.com/softlayer/softlayer-go v0.0.0-20180806151055-260589d94c7d/go.mod h1:Cw4GTlQccdRGSEf6KiMju767x0NEHE0YIVPJSaXjlsw=
 github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E=
@@ -567,9 +605,12 @@ github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4k
 github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
 github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
 github.com/spf13/afero v1.2.1/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
+github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
 github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
+github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
 github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
 github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
+github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
 github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
 github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
@@ -577,6 +618,8 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
 github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
 github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
 github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
+github.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM=
+github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
@@ -587,6 +630,8 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
 github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
+github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
 github.com/tebeka/strftime v0.1.3 h1:5HQXOqWKYRFfNyBMNVc9z5+QzuBtIXy03psIhtdJYto=
 github.com/tebeka/strftime v0.1.3/go.mod h1:7wJm3dZlpr4l/oVK0t1HYIc4rMzQ2XJlOMIUJUJH6XQ=
 github.com/tencentcloud/tencentcloud-sdk-go v3.0.83+incompatible h1:8uRvJleFpqLsO77WaAh2UrasMOzd8MxXrNj20e7El+Q=
@@ -594,6 +639,8 @@ github.com/tencentcloud/tencentcloud-sdk-go v3.0.83+incompatible/go.mod h1:0PfYo
 github.com/tent/http-link-go v0.0.0-20130702225549-ac974c61c2f9/go.mod h1:RHkNRtSLfOK7qBTHaeSX1D6BNpI3qw7NTxsmNr4RvN8=
 github.com/tevid/gohamcrest v1.1.1 h1:ou+xSqlIw1xfGTg1uq1nif/htZ2S3EzRqLm2BP+tYU0=
 github.com/tevid/gohamcrest v1.1.1/go.mod h1:3UvtWlqm8j5JbwYZh80D/PVBt0mJ1eJiYgZMibh0H/k=
+github.com/tinylib/msgp v1.1.0 h1:9fQd+ICuRIu/ue4vxJZu6/LzxN0HwMds2nq/0cFvxHU=
+github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
 github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 h1:LnC5Kc/wtumK+WB441p7ynQJzVuNRJiqddSIE3IlSEQ=
 github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
 github.com/toolkits/concurrent v0.0.0-20150624120057-a4371d70e3e3 h1:kF/7m/ZU+0D4Jj5eZ41Zm3IH/J8OElK1Qtd7tVKAwLk=
@@ -604,11 +651,13 @@ github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGr
 github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
 github.com/vmware/govmomi v0.18.0 h1:f7QxSmP7meCtoAmiKZogvVbLInT+CZx6Px6K5rYsJZo=
 github.com/vmware/govmomi v0.18.0/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU=
+github.com/willf/bitset v1.1.10 h1:NotGKqX0KwQ72NUzqrjZq5ipPNDQex9lo3WpaS8L2sc=
+github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
 github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=
 github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
 github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
-github.com/zouyx/agollo v0.0.0-20191114083447-dde9fc9f35b8 h1:k8TV7Gz7cpWpOw/dz71fx8cCZdWoPuckHJ/wkJl+meg=
-github.com/zouyx/agollo v0.0.0-20191114083447-dde9fc9f35b8/go.mod h1:S1cAa98KMFv4Sa8SbJ6ZtvOmf0VlgH0QJ1gXI0lBfBY=
+github.com/zouyx/agollo/v3 v3.4.4 h1:5G7QNw3fw74Ns8SfnHNhjndV2mlz5Fg8bB7q84ydFYI=
+github.com/zouyx/agollo/v3 v3.4.4/go.mod h1:ag0XmE1r4iAgPd6PUnU9TJ0DMEjM1VKX1HUNqQJ2ywU=
 go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
 go.etcd.io/bbolt v1.3.4 h1:hi1bXHMVrlQh6WwxAy+qZCV/SYIlqo+Ushwdpa4tAKg=
 go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
@@ -632,20 +681,31 @@ golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnf
 golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
 golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975 h1:/Tl7pH94bvbAAHBdZJT947M/+gp0+CqQXDtMRC0fseo=
 golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
+golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
+golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
+golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
+golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
 golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
 golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
 golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
 golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
 golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
+golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
 golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
+golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
 golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -661,6 +721,7 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
 golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
+golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
 golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -692,6 +753,7 @@ golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5h
 golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -700,6 +762,8 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20190508220229-2d0786266e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190523142557-0e01d883c5c5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -727,41 +791,58 @@ golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGm
 golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
 golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
 golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
 golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs=
 golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc h1:NCy3Ohtk6Iny5V/reW2Ktypo4zIpWBdRJ1uFMjBxdg8=
+golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
-google.golang.org/api v0.5.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
 google.golang.org/api v0.7.0 h1:9sdfJOzWlkqPltHAuzT2Cp+yrBeY1KRVYgms8soxMwM=
 google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
+google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
+google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
+google.golang.org/api v0.13.0 h1:Q3Ui3V3/CVinFWFiW39Iw0kMuVrRzYX0wN6OPFp0lTA=
+google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
 google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
 google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 google.golang.org/appengine v1.6.0 h1:Tfd7cKwKbFRsI8RMAD3oqqw7JPFRrvFlOsfbgVkjOOw=
 google.golang.org/appengine v1.6.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.6.1 h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I=
+google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
 google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
 google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190508193815-b515fa19cec8/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
 google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64 h1:iKtrH9Y8mcbADOP0YFaEMth7OfuHY9xHOwNj4znpM1A=
 google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
+google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a h1:Ob5/580gVHBJZgXnff1cZDbG+xLtMVE5mDRTe+nIsX4=
+google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
 google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
 google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
+google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
 google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
 google.golang.org/grpc v1.23.0 h1:AzbTB6ux+okLTzP8Ru1Xs41C303zdcfEht7MQnYJt5A=
 google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
@@ -780,6 +861,8 @@ gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
 gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
 gopkg.in/ini.v1 v1.42.0 h1:7N3gPTt50s8GuLortA00n8AqRTk75qOP98+mTPpgzRk=
 gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
+gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=
+gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
 gopkg.in/resty.v1 v1.12.0 h1:CuXP0Pjfw9rOuY6EP+UvtNvt5DSqHpIxILZKT/quCZI=
 gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
 gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
@@ -790,6 +873,7 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWD
 gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
 gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
 gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
@@ -815,6 +899,7 @@ k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a h1:UcxjrRMyNx/i/y8G7kPvLy
 k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
 k8s.io/utils v0.0.0-20190801114015-581e00157fb1 h1:+ySTxfHnfzZb9ys375PXNlLhkJPLKgHajBU0N62BDvE=
 k8s.io/utils v0.0.0-20190801114015-581e00157fb1/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
+rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
 sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
 sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
 sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
diff --git a/registry/directory/directory.go b/registry/directory/directory.go
index 2fbf9410f76c473362964c9ef148e3c581d3d045..8940d2ab5e6d71116149a8bb2bf617776a73ed93 100644
--- a/registry/directory/directory.go
+++ b/registry/directory/directory.go
@@ -18,10 +18,14 @@
 package directory
 
 import (
+	"fmt"
+	"net/url"
+	"os"
 	"sync"
 )
 
 import (
+	gxnet "github.com/dubbogo/gost/net"
 	perrors "github.com/pkg/errors"
 	"go.uber.org/atomic"
 )
@@ -29,6 +33,7 @@ import (
 import (
 	"github.com/apache/dubbo-go/cluster"
 	"github.com/apache/dubbo-go/cluster/directory"
+	"github.com/apache/dubbo-go/cluster/router/chain"
 	"github.com/apache/dubbo-go/common"
 	"github.com/apache/dubbo-go/common/constant"
 	"github.com/apache/dubbo-go/common/extension"
@@ -55,6 +60,7 @@ type RegistryDirectory struct {
 	serviceType                    string
 	registry                       registry.Registry
 	cacheInvokersMap               *sync.Map // use sync.map
+	consumerURL                    *common.URL
 	cacheOriginUrl                 *common.URL
 	configurators                  []config_center.Configurator
 	consumerConfigurationListener  *consumerConfigurationListener
@@ -75,6 +81,15 @@ func NewRegistryDirectory(url *common.URL, registry registry.Registry) (cluster.
 		serviceType:      url.SubURL.Service(),
 		registry:         registry,
 	}
+
+	dir.consumerURL = dir.getConsumerUrl(url.SubURL)
+
+	if routerChain, err := chain.NewRouterChain(dir.consumerURL); err == nil {
+		dir.BaseDirectory.SetRouterChain(routerChain)
+	} else {
+		logger.Warnf("fail to create router chain with url: %s, err is: %v", url.SubURL, err)
+	}
+
 	dir.consumerConfigurationListener = newConsumerConfigurationListener(dir)
 
 	go dir.subscribe(url.SubURL)
@@ -145,6 +160,9 @@ func (dir *RegistryDirectory) refreshInvokers(res *registry.ServiceEvent) {
 	newInvokers := dir.toGroupInvokers()
 	dir.listenerLock.Lock()
 	dir.cacheInvokers = newInvokers
+	if res != nil {
+		dir.RouterChain().SetInvokers(newInvokers)
+	}
 	dir.listenerLock.Unlock()
 	// After dir.cacheInvokers is updated,destroy the oldInvoker
 	// Ensure that no request will enter the oldInvoker
@@ -251,7 +269,7 @@ func (dir *RegistryDirectory) List(invocation protocol.Invocation) []protocol.In
 	if routerChain == nil {
 		return invokers
 	}
-	return routerChain.Route(invokers, dir.cacheOriginUrl, invocation)
+	return routerChain.Route(dir.consumerURL, invocation)
 }
 
 // IsAvailable  whether the directory is available
@@ -287,6 +305,24 @@ func (dir *RegistryDirectory) overrideUrl(targetUrl *common.URL) {
 	doOverrideUrl(dir.referenceConfigurationListener.Configurators(), targetUrl)
 }
 
+func (dir *RegistryDirectory) getConsumerUrl(c *common.URL) *common.URL {
+	processID := fmt.Sprintf("%d", os.Getpid())
+	localIP, _ := gxnet.GetLocalIP()
+
+	params := url.Values{}
+	c.RangeParams(func(key, value string) bool {
+		params.Add(key, value)
+		return true
+	})
+
+	params.Add("pid", processID)
+	params.Add("ip", localIP)
+	params.Add("protocol", c.Protocol)
+
+	return common.NewURLWithOptions(common.WithProtocol("consumer"), common.WithIp(localIP), common.WithPath(c.Path),
+		common.WithParams(params))
+}
+
 func doOverrideUrl(configurators []config_center.Configurator, targetUrl *common.URL) {
 	for _, v := range configurators {
 		v.Configure(targetUrl)
diff --git a/registry/etcdv3/listener_test.go b/registry/etcdv3/listener_test.go
index f27e7ce8ba5ab3515f8290b208b4823872ba48a3..cc4ea257ccabb5591689ec961ae279e12f24dd29 100644
--- a/registry/etcdv3/listener_test.go
+++ b/registry/etcdv3/listener_test.go
@@ -24,8 +24,8 @@ import (
 )
 
 import (
+	"github.com/apache/dubbo-getty"
 	"github.com/coreos/etcd/embed"
-	"github.com/dubbogo/getty"
 	"github.com/stretchr/testify/suite"
 )
 
diff --git a/registry/kubernetes/registry.go b/registry/kubernetes/registry.go
index 7c5162670d85f661fb8460cc69537ac9b7a12a23..88895855689df42f2cd66ec158e6b8f2806ef6f0 100644
--- a/registry/kubernetes/registry.go
+++ b/registry/kubernetes/registry.go
@@ -26,7 +26,7 @@ import (
 )
 
 import (
-	"github.com/dubbogo/getty"
+	"github.com/apache/dubbo-getty"
 	"github.com/dubbogo/gost/net"
 	perrors "github.com/pkg/errors"
 	v1 "k8s.io/api/core/v1"
diff --git a/registry/nacos/listener.go b/registry/nacos/listener.go
index 669900736214178799294f1ba1f97841bb6b81a4..cf6a73d38fbefc1ecb6a80e4911bb0d8fb13a6f6 100644
--- a/registry/nacos/listener.go
+++ b/registry/nacos/listener.go
@@ -132,7 +132,7 @@ func (nl *nacosListener) Callback(services []model.SubscribeService, err error)
 		instance := generateInstance(services[i])
 		newInstanceMap[host] = instance
 		if old, ok := nl.instanceMap[host]; !ok {
-			// instance is not exist in cache, add it to cache
+			// instance does not exist in cache, add it to cache
 			addInstances = append(addInstances, instance)
 		} else {
 			// instance is not different from cache, update it to cache
@@ -144,7 +144,7 @@ func (nl *nacosListener) Callback(services []model.SubscribeService, err error)
 
 	for host, inst := range nl.instanceMap {
 		if _, ok := newInstanceMap[host]; !ok {
-			// cache instance is not exist in new instance list, remove it from cache
+			// cache instance does not exist in new instance list, remove it from cache
 			delInstances = append(delInstances, inst)
 		}
 	}
@@ -188,7 +188,8 @@ func (nl *nacosListener) startListen() error {
 	}
 	serviceName := getSubscribeName(nl.listenUrl)
 	nl.subscribeParam = &vo.SubscribeParam{ServiceName: serviceName, SubscribeCallback: nl.Callback}
-	return nl.namingClient.Subscribe(nl.subscribeParam)
+	go nl.namingClient.Subscribe(nl.subscribeParam)
+	return nil
 }
 
 func (nl *nacosListener) stopListen() error {
diff --git a/registry/protocol/protocol.go b/registry/protocol/protocol.go
index 4c669b2cee74b95ceb3bc8287f145ccd6b99bc0b..c0608ad7989e69c104f07aa95e9a77fb9ac212fa 100644
--- a/registry/protocol/protocol.go
+++ b/registry/protocol/protocol.go
@@ -117,6 +117,18 @@ func (proto *registryProtocol) initConfigurationListeners() {
 	proto.providerConfigurationListener = newProviderConfigurationListener(proto.overrideListeners)
 }
 
+// nolint
+func (proto *registryProtocol) GetRegistries() []registry.Registry {
+	var rs []registry.Registry
+	proto.registries.Range(func(_, v interface{}) bool {
+		if r, ok := v.(registry.Registry); ok {
+			rs = append(rs, r)
+		}
+		return true
+	})
+	return rs
+}
+
 // Refer provider service from registry center
 func (proto *registryProtocol) Refer(url common.URL) protocol.Invoker {
 	var registryUrl = url
@@ -178,6 +190,7 @@ func (proto *registryProtocol) Export(invoker protocol.Invoker) protocol.Exporte
 	if regI, loaded := proto.registries.Load(registryUrl.Key()); !loaded {
 		reg = getRegistry(registryUrl)
 		proto.registries.Store(registryUrl.Key(), reg)
+		logger.Infof("Export proto:%p registries address:%p", proto, proto.registries)
 	} else {
 		reg = regI.(registry.Registry)
 	}
@@ -334,14 +347,12 @@ func (proto *registryProtocol) Destroy() {
 		ivk.Destroy()
 	}
 	proto.invokers = []protocol.Invoker{}
-
 	proto.bounds.Range(func(key, value interface{}) bool {
 		exporter := value.(protocol.Exporter)
 		exporter.Unexport()
 		proto.bounds.Delete(key)
 		return true
 	})
-
 	proto.registries.Range(func(key, value interface{}) bool {
 		reg := value.(registry.Registry)
 		if reg.IsAvailable() {
@@ -373,7 +384,7 @@ func setProviderUrl(regURL *common.URL, providerURL *common.URL) {
 	regURL.SubURL = providerURL
 }
 
-// GetProtocol return the singleton RegistryProtocol
+// GetProtocol return the singleton registryProtocol
 func GetProtocol() protocol.Protocol {
 	once.Do(func() {
 		regProtocol = newRegistryProtocol()
diff --git a/cluster/router/chan.go b/registry/registry_factory.go
similarity index 86%
rename from cluster/router/chan.go
rename to registry/registry_factory.go
index 6904e1734a7cbdaa00afa1b30797d19ca502453c..caefbce2eb833437a0e55323fad5ab334210db4e 100644
--- a/cluster/router/chan.go
+++ b/registry/registry_factory.go
@@ -15,11 +15,10 @@
  * limitations under the License.
  */
 
-package router
+package registry
 
-// Chain
-type Chain interface {
-	router
-	// AddRouters Add routers
-	AddRouters([]PriorityRouter)
+// RegistryFactory
+type RegistryFactory interface {
+	// GetRegistries get registries
+	GetRegistries() []Registry
 }
diff --git a/registry/service_discovery_holder.go b/registry/service_discovery_holder.go
new file mode 100644
index 0000000000000000000000000000000000000000..3bcf72612a7bb5ab2460b4063a2132716dd6249e
--- /dev/null
+++ b/registry/service_discovery_holder.go
@@ -0,0 +1,25 @@
+/*
+ * 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 registry
+
+// ServiceDiscoveryHolder we can get a service discovery
+// it always be a service discovery registry
+type ServiceDiscoveryHolder interface {
+	// GetServiceDiscovery get service discovery
+	GetServiceDiscovery() ServiceDiscovery
+}
diff --git a/registry/servicediscovery/service_discovery_registry.go b/registry/servicediscovery/service_discovery_registry.go
index 061d832b0328a5e1754c7804bf40cf83ac216a8b..7576804eb563e16a043f63f17db2532f48c878f1 100644
--- a/registry/servicediscovery/service_discovery_registry.go
+++ b/registry/servicediscovery/service_discovery_registry.go
@@ -28,7 +28,6 @@ import (
 import (
 	cm "github.com/Workiva/go-datastructures/common"
 	gxset "github.com/dubbogo/gost/container/set"
-	gxnet "github.com/dubbogo/gost/net"
 	perrors "github.com/pkg/errors"
 	"go.uber.org/atomic"
 )
@@ -176,18 +175,6 @@ func (s *serviceDiscoveryRegistry) Register(url common.URL) error {
 		logger.Warnf("The URL[%s] has been registry!", url.String())
 	}
 
-	// we try to register this instance. Dubbo do this in org.apache.dubbo.config.bootstrap.DubboBootstrap
-	// But we don't want to design a similar bootstrap class.
-	ins, err := createInstance(url)
-	if err != nil {
-		return perrors.WithMessage(err, "could not create servcie instance, please check your service url")
-	}
-
-	err = s.serviceDiscovery.Register(ins)
-	if err != nil {
-		return perrors.WithMessage(err, "register the service failed")
-	}
-
 	err = s.metaDataService.PublishServiceDefinition(url)
 	if err != nil {
 		return perrors.WithMessage(err, "publish the service definition failed. ")
@@ -198,36 +185,6 @@ func (s *serviceDiscoveryRegistry) Register(url common.URL) error {
 		url.Protocol)
 }
 
-func createInstance(url common.URL) (registry.ServiceInstance, error) {
-	appConfig := config.GetApplicationConfig()
-	port, err := strconv.ParseInt(url.Port, 10, 32)
-	if err != nil {
-		return nil, perrors.WithMessage(err, "invalid port: "+url.Port)
-	}
-
-	host := url.Ip
-	if len(host) == 0 {
-		host, err = gxnet.GetLocalIP()
-		if err != nil {
-			return nil, perrors.WithMessage(err, "could not get the local Ip")
-		}
-	}
-
-	// usually we will add more metadata
-	metadata := make(map[string]string, 8)
-	metadata[constant.METADATA_STORAGE_TYPE_PROPERTY_NAME] = appConfig.MetadataType
-
-	return &registry.DefaultServiceInstance{
-		ServiceName: appConfig.Name,
-		Host:        host,
-		Port:        int(port),
-		Id:          host + constant.KEY_SEPARATOR + url.Port,
-		Enable:      true,
-		Healthy:     true,
-		Metadata:    metadata,
-	}, nil
-}
-
 func shouldRegister(url common.URL) bool {
 	side := url.GetParam(constant.SIDE_KEY, "")
 	if side == constant.PROVIDER_PROTOCOL {
@@ -675,7 +632,7 @@ func (icn *InstanceChangeNotify) Notify(event observer.Event) {
 
 	if se, ok := event.(*registry.ServiceInstancesChangedEvent); ok {
 		sdr := icn.serviceDiscoveryRegistry
-		sdr.subscribe(sdr.url, icn.notify, se.ServiceName, se.Instances)
+		sdr.subscribe(sdr.url.SubURL, icn.notify, se.ServiceName, se.Instances)
 	}
 }
 
diff --git a/remoting/etcdv3/facade.go b/remoting/etcdv3/facade.go
index 2edbb6650890d6d655d8356e1c0a2979a022f0a8..52b1cce3e4618e9c2669e7a3b37256ebe6d61c41 100644
--- a/remoting/etcdv3/facade.go
+++ b/remoting/etcdv3/facade.go
@@ -23,7 +23,7 @@ import (
 )
 
 import (
-	"github.com/dubbogo/getty"
+	"github.com/apache/dubbo-getty"
 	perrors "github.com/pkg/errors"
 )
 
diff --git a/remoting/exchange.go b/remoting/exchange.go
index b97d6073c1f734d91c094372cf7de0919d088d99..848d9cbbcc23b0f565c45b646cf443be3f811efc 100644
--- a/remoting/exchange.go
+++ b/remoting/exchange.go
@@ -77,7 +77,7 @@ type Response struct {
 	Result   interface{}
 }
 
-// NewResponse aims to create Response
+// NewResponse create to a new Response.
 func NewResponse(id int64, version string) *Response {
 	return &Response{
 		ID:      id,
diff --git a/remoting/getty/config.go b/remoting/getty/config.go
index 4eaa4d428ec687a5fbc53b2b87e1b73b9e08b0cc..4213a8705d883e030aaed15d80e222e6b7bf5c47 100644
--- a/remoting/getty/config.go
+++ b/remoting/getty/config.go
@@ -22,10 +22,13 @@ import (
 )
 
 import (
-	"github.com/dubbogo/getty"
 	perrors "github.com/pkg/errors"
 )
 
+import (
+	"github.com/apache/dubbo-go/config"
+)
+
 type (
 	// GettySessionParam ...
 	GettySessionParam struct {
@@ -182,9 +185,9 @@ func (c *ClientConfig) CheckValidity() error {
 		return perrors.WithMessagef(err, "time.ParseDuration(HeartbeatPeroid{%#v})", c.HeartbeatPeriod)
 	}
 
-	if c.heartbeatPeriod >= time.Duration(getty.MaxWheelTimeSpan) {
+	if c.heartbeatPeriod >= time.Duration(config.MaxWheelTimeSpan) {
 		return perrors.WithMessagef(err, "heartbeat_period %s should be less than %s",
-			c.HeartbeatPeriod, time.Duration(getty.MaxWheelTimeSpan))
+			c.HeartbeatPeriod, time.Duration(config.MaxWheelTimeSpan))
 	}
 
 	if c.sessionTimeout, err = time.ParseDuration(c.SessionTimeout); err != nil {
@@ -202,9 +205,9 @@ func (c *ServerConfig) CheckValidity() error {
 		return perrors.WithMessagef(err, "time.ParseDuration(SessionTimeout{%#v})", c.SessionTimeout)
 	}
 
-	if c.sessionTimeout >= time.Duration(getty.MaxWheelTimeSpan) {
+	if c.sessionTimeout >= time.Duration(config.MaxWheelTimeSpan) {
 		return perrors.WithMessagef(err, "session_timeout %s should be less than %s",
-			c.SessionTimeout, time.Duration(getty.MaxWheelTimeSpan))
+			c.SessionTimeout, time.Duration(config.MaxWheelTimeSpan))
 	}
 
 	return perrors.WithStack(c.GettySessionParam.CheckValidity())
diff --git a/remoting/getty/getty_client.go b/remoting/getty/getty_client.go
index 63ca165bec224a674d57c761224e07d03a949a6d..6af3971f5c1f9ff5b0fafbc00ae2ba3f44eb34b5 100644
--- a/remoting/getty/getty_client.go
+++ b/remoting/getty/getty_client.go
@@ -18,13 +18,12 @@
 package getty
 
 import (
-	"github.com/apache/dubbo-go/common/constant"
 	"math/rand"
 	"time"
 )
 
 import (
-	"github.com/dubbogo/getty"
+	"github.com/apache/dubbo-getty"
 	gxsync "github.com/dubbogo/gost/sync"
 	perrors "github.com/pkg/errors"
 	"gopkg.in/yaml.v2"
@@ -32,6 +31,7 @@ import (
 
 import (
 	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/common/constant"
 	"github.com/apache/dubbo-go/common/logger"
 	"github.com/apache/dubbo-go/config"
 	"github.com/apache/dubbo-go/remoting"
diff --git a/remoting/getty/getty_server.go b/remoting/getty/getty_server.go
index 693b42fbbb0dbc280749a999fa2702515f7f5400..731ad8795236fa529a786e1bff2faf5735df2df6 100644
--- a/remoting/getty/getty_server.go
+++ b/remoting/getty/getty_server.go
@@ -24,7 +24,7 @@ import (
 )
 
 import (
-	"github.com/dubbogo/getty"
+	"github.com/apache/dubbo-getty"
 	gxsync "github.com/dubbogo/gost/sync"
 	"gopkg.in/yaml.v2"
 )
diff --git a/remoting/getty/listener.go b/remoting/getty/listener.go
index 777e14c2ecb344d3fa2aa2084b79c7805f542e28..198ec51daa6fb8dd8d1d55d484e179f685f94275 100644
--- a/remoting/getty/listener.go
+++ b/remoting/getty/listener.go
@@ -25,8 +25,8 @@ import (
 )
 
 import (
+	"github.com/apache/dubbo-getty"
 	hessian "github.com/apache/dubbo-go-hessian2"
-	"github.com/dubbogo/getty"
 	perrors "github.com/pkg/errors"
 )
 
diff --git a/remoting/getty/pool.go b/remoting/getty/pool.go
index dc439e1ba493b3b2fe4e77f0d3f4d90a0ec4bd6b..464cff956e0b4667944942be1c4721a05845bd33 100644
--- a/remoting/getty/pool.go
+++ b/remoting/getty/pool.go
@@ -28,7 +28,7 @@ import (
 )
 
 import (
-	"github.com/dubbogo/getty"
+	"github.com/apache/dubbo-getty"
 	perrors "github.com/pkg/errors"
 )
 
diff --git a/remoting/getty/readwriter.go b/remoting/getty/readwriter.go
index e290d439b23f38ce528aa5f7e960a985df7c6acd..ae96f35391a9c23bad68c78fbb89f4d1667ef709 100644
--- a/remoting/getty/readwriter.go
+++ b/remoting/getty/readwriter.go
@@ -22,8 +22,8 @@ import (
 )
 
 import (
+	"github.com/apache/dubbo-getty"
 	hessian "github.com/apache/dubbo-go-hessian2"
-	"github.com/dubbogo/getty"
 	perrors "github.com/pkg/errors"
 )
 
diff --git a/remoting/zookeeper/facade.go b/remoting/zookeeper/facade.go
index d5d9e6e74858e3ec520aedee5b8ba059baf928d8..2a034390c016864f95303b12b6c56533771075f3 100644
--- a/remoting/zookeeper/facade.go
+++ b/remoting/zookeeper/facade.go
@@ -21,7 +21,7 @@ import (
 	"sync"
 )
 import (
-	"github.com/dubbogo/getty"
+	"github.com/apache/dubbo-getty"
 	perrors "github.com/pkg/errors"
 )
 
diff --git a/remoting/zookeeper/listener.go b/remoting/zookeeper/listener.go
index 4f50c18ab61ba6faf373dfd0f831c14ae7ab6d5d..486a67e20a0736be20813226b622f1bfa5bd87c0 100644
--- a/remoting/zookeeper/listener.go
+++ b/remoting/zookeeper/listener.go
@@ -25,7 +25,7 @@ import (
 )
 
 import (
-	"github.com/dubbogo/getty"
+	"github.com/apache/dubbo-getty"
 	"github.com/dubbogo/go-zookeeper/zk"
 	perrors "github.com/pkg/errors"
 )