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/NOTICE b/NOTICE
index d7aa899d1cef0fba67826bebd0d587e9cc17ba5d..1120c200c997fe6befbe3f78d95e9bdb8a05a487 100644
--- a/NOTICE
+++ b/NOTICE
@@ -1,4 +1,4 @@
-Apache Dubbo Go
+Apache Dubbo-go
 Copyright 2018-2020 The Apache Software Foundation
 
 This product includes software developed at
diff --git a/README.md b/README.md
index a1c09fc3ca4414188f42e8e5433b33b90d959865..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)
@@ -34,7 +36,7 @@ Both extension module and layered project architecture is according to Apache Du
 
 ![dubbo go extend](./doc/pic/arch/dubbo-go-ext.png)
 
-If you wanna know more about dubbo-go, please visit this reference [Project Architeture design](https://github.com/apache/dubbo-go/wiki/dubbo-go-V1.0-design)
+If you wanna know more about dubbo-go, please visit this reference [Project Architecture design](https://github.com/apache/dubbo-go/wiki/dubbo-go-V1.0-design)
 
 ## Feature list ##
 
diff --git a/README_CN.md b/README_CN.md
index 552685c7bba9888e089f635f5812d93355e31715..b76d8983deae427f9317c4f930f0e06da479f484 100644
--- a/README_CN.md
+++ b/README_CN.md
@@ -180,7 +180,7 @@ go test ./... -coverprofile=coverage.txt -covermode=atomic
 
 ## [User List](https://github.com/apache/dubbo-go/issues/2)
 
-鑻ヤ綘姝e湪浣跨敤 [apache/dubbo-go](github.com/apache/dubbo-go) 涓旇涓哄叾鏈夌敤鎴栬€呭悜瀵瑰叾鍋氭敼杩涳紝璇峰繚鍒楄吹鍙镐俊鎭簬 [鐢ㄦ埛鍒楄〃](https://github.com/apache/dubbo-go/issues/2)锛屼互渚挎垜浠煡鏅撲箣銆�
+鑻ヤ綘姝e湪浣跨敤 [apache/dubbo-go](github.com/apache/dubbo-go) 涓旇涓哄叾鏈夌敤鎴栬€呮兂瀵瑰叾鍋氭敼杩涳紝璇锋坊鍒楄吹鍙镐俊鎭簬 [鐢ㄦ埛鍒楄〃](https://github.com/apache/dubbo-go/issues/2)锛屼互渚挎垜浠煡鏅撱€�
 
 <div>
 <table>
diff --git a/before_ut.bat b/before_ut.bat
index 5d2b9e4682d0ac24efe4ae68be8d977933db103f..7f5cf50e900955f784552221569d9caf414274d4 100644
--- a/before_ut.bat
+++ b/before_ut.bat
@@ -36,5 +36,8 @@ xcopy /f "%zkJar%" "cluster/router/chain/zookeeper-4unittest/contrib/fatjar/"
 md cluster\router\condition\zookeeper-4unittest\contrib\fatjar
 xcopy /f "%zkJar%" "cluster/router/condition/zookeeper-4unittest/contrib/fatjar/"
 
+mkdir -p cluster/router/tag/zookeeper-4unittest/contrib/fatjar
+cp ${zkJar} cluster/router/tag/zookeeper-4unittest/contrib/fatjar
+
 md metadata\report\zookeeper\zookeeper-4unittest\contrib\fatjar
 xcopy /f "%zkJar%" "metadata/report/zookeeper/zookeeper-4unittest/contrib/fatjar/"
\ No newline at end of file
diff --git a/before_ut.sh b/before_ut.sh
index 210e9e723ba9e2118cf642729359808b78fddb8d..b55e424ef72b33181b2ea40fdb37ac319110aec0 100755
--- a/before_ut.sh
+++ b/before_ut.sh
@@ -36,5 +36,8 @@ cp ${zkJar} cluster/router/chain/zookeeper-4unittest/contrib/fatjar
 mkdir -p cluster/router/condition/zookeeper-4unittest/contrib/fatjar
 cp ${zkJar} cluster/router/condition/zookeeper-4unittest/contrib/fatjar
 
+mkdir -p cluster/router/tag/zookeeper-4unittest/contrib/fatjar
+cp ${zkJar} cluster/router/tag/zookeeper-4unittest/contrib/fatjar
+
 mkdir -p metadata/report/zookeeper/zookeeper-4unittest/contrib/fatjar
 cp ${zkJar} metadata/report/zookeeper/zookeeper-4unittest/contrib/fatjar
\ No newline at end of file
diff --git a/cluster/cluster_impl/available_cluster.go b/cluster/cluster_impl/available_cluster.go
index b70a97fad2de1b267ac1c6a5f0672ff445fadcc3..ebd5767e4c320f10c8911cf9ac3f2c81deaafb0e 100644
--- a/cluster/cluster_impl/available_cluster.go
+++ b/cluster/cluster_impl/available_cluster.go
@@ -38,6 +38,7 @@ func NewAvailableCluster() cluster.Cluster {
 	return &availableCluster{}
 }
 
+// Join returns a baseClusterInvoker instance
 func (cluser *availableCluster) Join(directory cluster.Directory) protocol.Invoker {
 	return NewAvailableClusterInvoker(directory)
 }
diff --git a/cluster/cluster_impl/base_cluster_invoker.go b/cluster/cluster_impl/base_cluster_invoker.go
index bbdfa715d7cdc461689e60a5a41171ad5c9770e1..ced5b15cb9a1c2292ca866f6f7478ce2b23a30b9 100644
--- a/cluster/cluster_impl/base_cluster_invoker.go
+++ b/cluster/cluster_impl/base_cluster_invoker.go
@@ -17,6 +17,10 @@
 
 package cluster_impl
 
+import (
+	"context"
+)
+
 import (
 	gxnet "github.com/dubbogo/gost/net"
 	perrors "github.com/pkg/errors"
@@ -36,6 +40,7 @@ type baseClusterInvoker struct {
 	availablecheck bool
 	destroyed      *atomic.Bool
 	stickyInvoker  protocol.Invoker
+	interceptor    cluster.ClusterInterceptor
 }
 
 func newBaseClusterInvoker(directory cluster.Directory) baseClusterInvoker {
@@ -146,6 +151,20 @@ func (invoker *baseClusterInvoker) doSelectInvoker(lb cluster.LoadBalance, invoc
 	return selectedInvoker
 }
 
+func (invoker *baseClusterInvoker) Invoke(ctx context.Context, invocation protocol.Invocation) protocol.Result {
+	if invoker.interceptor != nil {
+		invoker.interceptor.BeforeInvoker(ctx, invocation)
+
+		result := invoker.interceptor.DoInvoke(ctx, invocation)
+
+		invoker.interceptor.AfterInvoker(ctx, invocation)
+
+		return result
+	}
+
+	return nil
+}
+
 func isInvoked(selectedInvoker protocol.Invoker, invoked []protocol.Invoker) bool {
 	for _, i := range invoked {
 		if i == selectedInvoker {
diff --git a/cluster/cluster_impl/broadcast_cluster.go b/cluster/cluster_impl/broadcast_cluster.go
index ba454af6a8553f31b72b1d30ef5f44ec7a8278d2..ea3dee921888baa8f997cd02e92ee582f09a37d5 100644
--- a/cluster/cluster_impl/broadcast_cluster.go
+++ b/cluster/cluster_impl/broadcast_cluster.go
@@ -39,6 +39,7 @@ func NewBroadcastCluster() cluster.Cluster {
 	return &broadcastCluster{}
 }
 
+// Join returns a baseClusterInvoker instance
 func (cluster *broadcastCluster) Join(directory cluster.Directory) protocol.Invoker {
 	return newBroadcastClusterInvoker(directory)
 }
diff --git a/cluster/cluster_impl/broadcast_cluster_invoker.go b/cluster/cluster_impl/broadcast_cluster_invoker.go
index 3a97d3d9b499c011ac90cb88b6692565388411a7..b117dbb246bec6fed6ced4fb4abcdeb4a4b7cee5 100644
--- a/cluster/cluster_impl/broadcast_cluster_invoker.go
+++ b/cluster/cluster_impl/broadcast_cluster_invoker.go
@@ -36,6 +36,7 @@ func newBroadcastClusterInvoker(directory cluster.Directory) protocol.Invoker {
 	}
 }
 
+// nolint
 func (invoker *broadcastClusterInvoker) Invoke(ctx context.Context, invocation protocol.Invocation) protocol.Result {
 	invokers := invoker.directory.List(invocation)
 	err := invoker.checkInvokers(invokers, invocation)
diff --git a/cluster/cluster_impl/failback_cluster.go b/cluster/cluster_impl/failback_cluster.go
index 432e33122c2ee599bc848ca9ab1842084da5ef68..278ac5432e7db5d8379e4aba81065ff2ab704ff5 100644
--- a/cluster/cluster_impl/failback_cluster.go
+++ b/cluster/cluster_impl/failback_cluster.go
@@ -39,6 +39,7 @@ func NewFailbackCluster() cluster.Cluster {
 	return &failbackCluster{}
 }
 
+// Join returns a baseClusterInvoker instance
 func (cluster *failbackCluster) Join(directory cluster.Directory) protocol.Invoker {
 	return newFailbackClusterInvoker(directory)
 }
diff --git a/cluster/cluster_impl/failback_cluster_invoker.go b/cluster/cluster_impl/failback_cluster_invoker.go
index af17a93756a6f558c7da063eec9d8052b83cbe69..62f48045ec6edbc19d6603509fa1ae8c2d4ce9ee 100644
--- a/cluster/cluster_impl/failback_cluster_invoker.go
+++ b/cluster/cluster_impl/failback_cluster_invoker.go
@@ -126,6 +126,7 @@ func (invoker *failbackClusterInvoker) checkRetry(retryTask *retryTimerTask, err
 	}
 }
 
+// nolint
 func (invoker *failbackClusterInvoker) Invoke(ctx context.Context, invocation protocol.Invocation) protocol.Result {
 	invokers := invoker.directory.List(invocation)
 	if err := invoker.checkInvokers(invokers, invocation); err != nil {
diff --git a/cluster/cluster_impl/failfast_cluster.go b/cluster/cluster_impl/failfast_cluster.go
index ac9ec6b821c1d0333c73fae56169d5bc8256ec5b..a5ea7a05855e58b9db02f41fcfba83b52d35d8b0 100644
--- a/cluster/cluster_impl/failfast_cluster.go
+++ b/cluster/cluster_impl/failfast_cluster.go
@@ -39,6 +39,7 @@ func NewFailFastCluster() cluster.Cluster {
 	return &failfastCluster{}
 }
 
+// Join returns a baseClusterInvoker instance
 func (cluster *failfastCluster) Join(directory cluster.Directory) protocol.Invoker {
 	return newFailFastClusterInvoker(directory)
 }
diff --git a/cluster/cluster_impl/failfast_cluster_invoker.go b/cluster/cluster_impl/failfast_cluster_invoker.go
index 3b4dc9b721720948cf635f57191d1e6bce023a1e..d71ef5f5a1dfed2d29f6ae4f29fb08d34aae9c5d 100644
--- a/cluster/cluster_impl/failfast_cluster_invoker.go
+++ b/cluster/cluster_impl/failfast_cluster_invoker.go
@@ -35,6 +35,7 @@ func newFailFastClusterInvoker(directory cluster.Directory) protocol.Invoker {
 	}
 }
 
+// nolint
 func (invoker *failfastClusterInvoker) Invoke(ctx context.Context, invocation protocol.Invocation) protocol.Result {
 	invokers := invoker.directory.List(invocation)
 	err := invoker.checkInvokers(invokers, invocation)
diff --git a/cluster/cluster_impl/failover_cluster.go b/cluster/cluster_impl/failover_cluster.go
index d30a743e034dafabad87381cdaa356e7603b74d1..4c09fd16d3eb5ebaf7833443ee0f8a980c81b822 100644
--- a/cluster/cluster_impl/failover_cluster.go
+++ b/cluster/cluster_impl/failover_cluster.go
@@ -19,16 +19,15 @@ package cluster_impl
 
 import (
 	"github.com/apache/dubbo-go/cluster"
+	"github.com/apache/dubbo-go/common/constant"
 	"github.com/apache/dubbo-go/common/extension"
 	"github.com/apache/dubbo-go/protocol"
 )
 
 type failoverCluster struct{}
 
-const name = "failover"
-
 func init() {
-	extension.SetCluster(name, NewFailoverCluster)
+	extension.SetCluster(constant.FAILOVER_CLUSTER_NAME, NewFailoverCluster)
 }
 
 // NewFailoverCluster returns a failover cluster instance
@@ -40,6 +39,7 @@ func NewFailoverCluster() cluster.Cluster {
 	return &failoverCluster{}
 }
 
+// Join returns a baseClusterInvoker instance
 func (cluster *failoverCluster) Join(directory cluster.Directory) protocol.Invoker {
 	return newFailoverClusterInvoker(directory)
 }
diff --git a/cluster/cluster_impl/failover_cluster_invoker.go b/cluster/cluster_impl/failover_cluster_invoker.go
index 66adabd1043d6e5d770704774dda22ba9e6faebe..4260a9324dd360ac24f80e425d8542a423c8815e 100644
--- a/cluster/cluster_impl/failover_cluster_invoker.go
+++ b/cluster/cluster_impl/failover_cluster_invoker.go
@@ -19,6 +19,7 @@ package cluster_impl
 
 import (
 	"context"
+	"fmt"
 	"strconv"
 )
 
@@ -44,6 +45,7 @@ func newFailoverClusterInvoker(directory cluster.Directory) protocol.Invoker {
 	}
 }
 
+// nolint
 func (invoker *failoverClusterInvoker) Invoke(ctx context.Context, invocation protocol.Invocation) protocol.Result {
 	var (
 		result    protocol.Result
@@ -91,8 +93,10 @@ func (invoker *failoverClusterInvoker) Invoke(ctx context.Context, invocation pr
 	invokerSvc := invoker.GetUrl().Service()
 	invokerUrl := invoker.directory.GetUrl()
 	return &protocol.RPCResult{
-		Err: perrors.Errorf("Failed to invoke the method %v in the service %v. Tried %v times of the providers %v (%v/%v)from the registry %v on the consumer %v using the dubbo version %v. Last error is %v.",
-			methodName, invokerSvc, retries, providers, len(providers), len(invokers), invokerUrl, ip, constant.Version, result.Error().Error(),
+		Err: perrors.Wrap(result.Error(), fmt.Sprintf("Failed to invoke the method %v in the service %v. "+
+			"Tried %v times of the providers %v (%v/%v)from the registry %v on the consumer %v using the dubbo version %v. "+
+			"Last error is %+v.", methodName, invokerSvc, retries, providers, len(providers), len(invokers),
+			invokerUrl, ip, constant.Version, result.Error().Error()),
 		)}
 }
 
diff --git a/cluster/cluster_impl/failover_cluster_test.go b/cluster/cluster_impl/failover_cluster_test.go
index e05b79202cd202334db1c19421e3163ee28bac26..d3ac2c8a5ffd7fce647649c53fe343ba93999636 100644
--- a/cluster/cluster_impl/failover_cluster_test.go
+++ b/cluster/cluster_impl/failover_cluster_test.go
@@ -43,6 +43,7 @@ import (
 // mock invoker
 // ///////////////////////////
 
+// nolint
 type MockInvoker struct {
 	url       common.URL
 	available bool
@@ -51,6 +52,7 @@ type MockInvoker struct {
 	successCount int
 }
 
+// nolint
 func NewMockInvoker(url common.URL, successCount int) *MockInvoker {
 	return &MockInvoker{
 		url:          url,
@@ -60,23 +62,28 @@ func NewMockInvoker(url common.URL, successCount int) *MockInvoker {
 	}
 }
 
+// nolint
 func (bi *MockInvoker) GetUrl() common.URL {
 	return bi.url
 }
 
+// nolint
 func (bi *MockInvoker) IsAvailable() bool {
 	return bi.available
 }
 
+// nolint
 func (bi *MockInvoker) IsDestroyed() bool {
 	return bi.destroyed
 }
 
+// nolint
 type rest struct {
 	tried   int
 	success bool
 }
 
+// nolint
 func (bi *MockInvoker) Invoke(c context.Context, invocation protocol.Invocation) protocol.Result {
 	count++
 	var (
@@ -93,14 +100,17 @@ func (bi *MockInvoker) Invoke(c context.Context, invocation protocol.Invocation)
 	return result
 }
 
+// nolint
 func (bi *MockInvoker) Destroy() {
 	logger.Infof("Destroy invoker: %v", bi.GetUrl().String())
 	bi.destroyed = true
 	bi.available = false
 }
 
+// nolint
 var count int
 
+// nolint
 func normalInvoke(successCount int, urlParam url.Values, invocations ...*invocation.RPCInvocation) protocol.Result {
 	extension.SetLoadbalance("random", loadbalance.NewRandomLoadBalance)
 	failoverCluster := NewFailoverCluster()
@@ -119,6 +129,7 @@ func normalInvoke(successCount int, urlParam url.Values, invocations ...*invocat
 	return clusterInvoker.Invoke(context.Background(), &invocation.RPCInvocation{})
 }
 
+// nolint
 func TestFailoverInvokeSuccess(t *testing.T) {
 	urlParams := url.Values{}
 	result := normalInvoke(3, urlParams)
@@ -126,6 +137,7 @@ func TestFailoverInvokeSuccess(t *testing.T) {
 	count = 0
 }
 
+// nolint
 func TestFailoverInvokeFail(t *testing.T) {
 	urlParams := url.Values{}
 	result := normalInvoke(4, urlParams)
@@ -133,6 +145,7 @@ func TestFailoverInvokeFail(t *testing.T) {
 	count = 0
 }
 
+// nolint
 func TestFailoverInvoke1(t *testing.T) {
 	urlParams := url.Values{}
 	urlParams.Set(constant.RETRIES_KEY, "3")
@@ -141,6 +154,7 @@ func TestFailoverInvoke1(t *testing.T) {
 	count = 0
 }
 
+// nolint
 func TestFailoverInvoke2(t *testing.T) {
 	urlParams := url.Values{}
 	urlParams.Set(constant.RETRIES_KEY, "2")
@@ -152,6 +166,7 @@ func TestFailoverInvoke2(t *testing.T) {
 	count = 0
 }
 
+// nolint
 func TestFailoverDestroy(t *testing.T) {
 	extension.SetLoadbalance("random", loadbalance.NewRandomLoadBalance)
 	failoverCluster := NewFailoverCluster()
diff --git a/cluster/cluster_impl/failsafe_cluster.go b/cluster/cluster_impl/failsafe_cluster.go
index f708b7fb9108bdd17fec5dc68dc1e4249c8199d4..d9465c034cb9e76d2f360b395f17b23d9234c532 100644
--- a/cluster/cluster_impl/failsafe_cluster.go
+++ b/cluster/cluster_impl/failsafe_cluster.go
@@ -39,6 +39,7 @@ func NewFailsafeCluster() cluster.Cluster {
 	return &failsafeCluster{}
 }
 
+// Join returns a baseClusterInvoker instance
 func (cluster *failsafeCluster) Join(directory cluster.Directory) protocol.Invoker {
 	return newFailsafeClusterInvoker(directory)
 }
diff --git a/cluster/cluster_impl/failsafe_cluster_invoker.go b/cluster/cluster_impl/failsafe_cluster_invoker.go
index 4d8fe27719eb71fa287fe4142d8e92ca17acfba4..27c59fff18d4a93fc693d7edbca467e0ffef549a 100644
--- a/cluster/cluster_impl/failsafe_cluster_invoker.go
+++ b/cluster/cluster_impl/failsafe_cluster_invoker.go
@@ -45,6 +45,7 @@ func newFailsafeClusterInvoker(directory cluster.Directory) protocol.Invoker {
 	}
 }
 
+// nolint
 func (invoker *failsafeClusterInvoker) Invoke(ctx context.Context, invocation protocol.Invocation) protocol.Result {
 	invokers := invoker.directory.List(invocation)
 
diff --git a/cluster/cluster_impl/forking_cluster.go b/cluster/cluster_impl/forking_cluster.go
index 0e6cd26882788a1f897d0d4dc8e0d4eb0a9d4218..8c9911327527e66864200630f5a52dd5c7b9be53 100644
--- a/cluster/cluster_impl/forking_cluster.go
+++ b/cluster/cluster_impl/forking_cluster.go
@@ -39,6 +39,7 @@ func NewForkingCluster() cluster.Cluster {
 	return &forkingCluster{}
 }
 
+// Join returns a baseClusterInvoker instance
 func (cluster *forkingCluster) Join(directory cluster.Directory) protocol.Invoker {
 	return newForkingClusterInvoker(directory)
 }
diff --git a/cluster/cluster_impl/forking_cluster_invoker.go b/cluster/cluster_impl/forking_cluster_invoker.go
index a5a3f2ec6605dfb843fab09dff0a53000bbc3298..168444881653ca38ef61a9bc8e50f2d4bc3e624c 100644
--- a/cluster/cluster_impl/forking_cluster_invoker.go
+++ b/cluster/cluster_impl/forking_cluster_invoker.go
@@ -44,7 +44,7 @@ func newForkingClusterInvoker(directory cluster.Directory) protocol.Invoker {
 	}
 }
 
-// Invoke ...
+// nolint
 func (invoker *forkingClusterInvoker) Invoke(ctx context.Context, invocation protocol.Invocation) protocol.Result {
 	if err := invoker.checkWhetherDestroyed(); err != nil {
 		return &protocol.RPCResult{Err: err}
diff --git a/cluster/cluster_impl/mock_cluster.go b/cluster/cluster_impl/mock_cluster.go
index d887cfb45b9c92c859b1396046c1c1c73d46b295..a7fa5d4f02087d4ec8ee0e011c09b11805f79a3f 100644
--- a/cluster/cluster_impl/mock_cluster.go
+++ b/cluster/cluster_impl/mock_cluster.go
@@ -33,6 +33,7 @@ func NewMockCluster() cluster.Cluster {
 	return &mockCluster{}
 }
 
+// nolint
 func (cluster *mockCluster) Join(directory cluster.Directory) protocol.Invoker {
 	return protocol.NewBaseInvoker(directory.GetUrl())
 }
diff --git a/cluster/cluster_impl/registry_aware_cluster_invoker.go b/cluster/cluster_impl/registry_aware_cluster_invoker.go
deleted file mode 100644
index cded5bf16432e6b0c590e15b81c28369889a5f88..0000000000000000000000000000000000000000
--- a/cluster/cluster_impl/registry_aware_cluster_invoker.go
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * 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 cluster_impl
-
-import (
-	"context"
-)
-import (
-	"github.com/apache/dubbo-go/cluster"
-	"github.com/apache/dubbo-go/common/constant"
-	"github.com/apache/dubbo-go/protocol"
-)
-
-type registryAwareClusterInvoker struct {
-	baseClusterInvoker
-}
-
-func newRegistryAwareClusterInvoker(directory cluster.Directory) protocol.Invoker {
-	return &registryAwareClusterInvoker{
-		baseClusterInvoker: newBaseClusterInvoker(directory),
-	}
-}
-
-func (invoker *registryAwareClusterInvoker) Invoke(ctx context.Context, invocation protocol.Invocation) protocol.Result {
-	invokers := invoker.directory.List(invocation)
-	//First, pick the invoker (XXXClusterInvoker) that comes from the local registry, distinguish by a 'default' key.
-	for _, invoker := range invokers {
-		if invoker.IsAvailable() && invoker.GetUrl().GetParam(constant.REGISTRY_DEFAULT_KEY, "false") == "true" {
-			return invoker.Invoke(ctx, invocation)
-		}
-	}
-
-	//If none of the invokers has a local signal, pick the first one available.
-	for _, invoker := range invokers {
-		if invoker.IsAvailable() {
-			return invoker.Invoke(ctx, invocation)
-		}
-	}
-	return nil
-}
diff --git a/cluster/cluster_impl/registry_aware_cluster_test.go b/cluster/cluster_impl/registry_aware_cluster_test.go
deleted file mode 100644
index 74584b44800fce3342956f4237a63ffbbabf5544..0000000000000000000000000000000000000000
--- a/cluster/cluster_impl/registry_aware_cluster_test.go
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * 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 cluster_impl
-
-import (
-	"context"
-	"fmt"
-	"testing"
-)
-import (
-	"github.com/stretchr/testify/assert"
-)
-
-import (
-	"github.com/apache/dubbo-go/cluster/directory"
-	"github.com/apache/dubbo-go/common"
-	"github.com/apache/dubbo-go/protocol"
-	"github.com/apache/dubbo-go/protocol/invocation"
-)
-
-func TestRegAwareInvokeSuccess(t *testing.T) {
-
-	regAwareCluster := NewRegistryAwareCluster()
-
-	invokers := []protocol.Invoker{}
-	for i := 0; i < 10; i++ {
-		url, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i))
-		invokers = append(invokers, NewMockInvoker(url, 1))
-	}
-
-	staticDir := directory.NewStaticDirectory(invokers)
-	clusterInvoker := regAwareCluster.Join(staticDir)
-	result := clusterInvoker.Invoke(context.Background(), &invocation.RPCInvocation{})
-	assert.NoError(t, result.Error())
-	count = 0
-}
-
-func TestDestroy(t *testing.T) {
-	regAwareCluster := NewRegistryAwareCluster()
-
-	invokers := []protocol.Invoker{}
-	for i := 0; i < 10; i++ {
-		url, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i))
-		invokers = append(invokers, NewMockInvoker(url, 1))
-	}
-
-	staticDir := directory.NewStaticDirectory(invokers)
-	clusterInvoker := regAwareCluster.Join(staticDir)
-	assert.Equal(t, true, clusterInvoker.IsAvailable())
-	result := clusterInvoker.Invoke(context.Background(), &invocation.RPCInvocation{})
-	assert.NoError(t, result.Error())
-	count = 0
-	clusterInvoker.Destroy()
-	assert.Equal(t, false, clusterInvoker.IsAvailable())
-
-}
diff --git a/cluster/cluster_impl/registry_aware_cluster.go b/cluster/cluster_impl/zone_aware_cluster.go
similarity index 63%
rename from cluster/cluster_impl/registry_aware_cluster.go
rename to cluster/cluster_impl/zone_aware_cluster.go
index fcefa52862a39eece98dca8660e62d9ca144e955..7439db2d374f73571f3c8b95d8faca6cb98d4cd0 100644
--- a/cluster/cluster_impl/registry_aware_cluster.go
+++ b/cluster/cluster_impl/zone_aware_cluster.go
@@ -19,21 +19,26 @@ package cluster_impl
 
 import (
 	"github.com/apache/dubbo-go/cluster"
+	"github.com/apache/dubbo-go/common/constant"
 	"github.com/apache/dubbo-go/common/extension"
 	"github.com/apache/dubbo-go/protocol"
 )
 
-type registryAwareCluster struct{}
+type zoneAwareCluster struct{}
 
 func init() {
-	extension.SetCluster("registryAware", NewRegistryAwareCluster)
+	extension.SetCluster(constant.ZONEAWARE_CLUSTER_NAME, NewZoneAwareCluster)
 }
 
-// NewRegistryAwareCluster returns a registry aware cluster instance
-func NewRegistryAwareCluster() cluster.Cluster {
-	return &registryAwareCluster{}
+// NewZoneAwareCluster returns a zoneaware cluster instance.
+//
+// More than one registry for subscription.
+// Usually it is used for choose between registries.
+func NewZoneAwareCluster() cluster.Cluster {
+	return &zoneAwareCluster{}
 }
 
-func (cluster *registryAwareCluster) Join(directory cluster.Directory) protocol.Invoker {
-	return newRegistryAwareClusterInvoker(directory)
+// Join returns a zoneAwareClusterInvoker instance
+func (cluster *zoneAwareCluster) Join(directory cluster.Directory) protocol.Invoker {
+	return newZoneAwareClusterInvoker(directory)
 }
diff --git a/cluster/cluster_impl/zone_aware_cluster_invoker.go b/cluster/cluster_impl/zone_aware_cluster_invoker.go
new file mode 100644
index 0000000000000000000000000000000000000000..0f52b0442c235d570e4cc9d8491aa80a9df5842d
--- /dev/null
+++ b/cluster/cluster_impl/zone_aware_cluster_invoker.go
@@ -0,0 +1,133 @@
+/*
+ * 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 cluster_impl
+
+import (
+	"context"
+	"fmt"
+)
+
+import (
+	"github.com/apache/dubbo-go/cluster"
+	"github.com/apache/dubbo-go/common/constant"
+	"github.com/apache/dubbo-go/protocol"
+)
+
+// When there're more than one registry for subscription.
+//
+// This extension provides a strategy to decide how to distribute traffics among them:
+// 1. registry marked as 'preferred=true' has the highest priority.
+// 2. check the zone the current request belongs, pick the registry that has the same zone first.
+// 3. Evenly balance traffic between all registries based on each registry's weight.
+// 4. Pick anyone that's available.
+type zoneAwareClusterInvoker struct {
+	baseClusterInvoker
+}
+
+func newZoneAwareClusterInvoker(directory cluster.Directory) protocol.Invoker {
+	invoke := &zoneAwareClusterInvoker{
+		baseClusterInvoker: newBaseClusterInvoker(directory),
+	}
+	// add self to interceptor
+	invoke.interceptor = invoke
+	return invoke
+}
+
+// nolint
+func (invoker *zoneAwareClusterInvoker) DoInvoke(ctx context.Context, invocation protocol.Invocation) protocol.Result {
+	invokers := invoker.directory.List(invocation)
+
+	err := invoker.checkInvokers(invokers, invocation)
+	if err != nil {
+		return &protocol.RPCResult{Err: err}
+	}
+
+	// First, pick the invoker (XXXClusterInvoker) that comes from the local registry, distinguish by a 'preferred' key.
+	for _, invoker := range invokers {
+		key := constant.REGISTRY_KEY + "." + constant.PREFERRED_KEY
+		if invoker.IsAvailable() && matchParam("true", key, "false", invoker) {
+			return invoker.Invoke(ctx, invocation)
+		}
+	}
+
+	// providers in the registry with the same zone
+	key := constant.REGISTRY_KEY + "." + constant.ZONE_KEY
+	zone := invocation.AttachmentsByKey(key, "")
+	if "" != zone {
+		for _, invoker := range invokers {
+			if invoker.IsAvailable() && matchParam(zone, key, "", invoker) {
+				return invoker.Invoke(ctx, invocation)
+			}
+		}
+
+		force := invocation.AttachmentsByKey(constant.REGISTRY_KEY+"."+constant.ZONE_FORCE_KEY, "")
+		if "true" == force {
+			return &protocol.RPCResult{
+				Err: fmt.Errorf("no registry instance in zone or "+
+					"no available providers in the registry, zone: %v, "+
+					" registries: %v", zone, invoker.GetUrl()),
+			}
+		}
+	}
+
+	// load balance among all registries, with registry weight count in.
+	loadBalance := getLoadBalance(invokers[0], invocation)
+	ivk := invoker.doSelect(loadBalance, invocation, invokers, nil)
+	if ivk != nil && ivk.IsAvailable() {
+		return ivk.Invoke(ctx, invocation)
+	}
+
+	// If none of the invokers has a preferred signal or is picked by the loadBalancer, pick the first one available.
+	for _, invoker := range invokers {
+		if invoker.IsAvailable() {
+			return invoker.Invoke(ctx, invocation)
+		}
+	}
+
+	return &protocol.RPCResult{
+		Err: fmt.Errorf("no provider available in %v", invokers),
+	}
+}
+
+func (invoker *zoneAwareClusterInvoker) BeforeInvoker(ctx context.Context, invocation protocol.Invocation) {
+	key := constant.REGISTRY_KEY + "." + constant.ZONE_FORCE_KEY
+	force := ctx.Value(key)
+
+	if force != nil {
+		switch value := force.(type) {
+		case bool:
+			if value {
+				invocation.SetAttachments(key, "true")
+			}
+		case string:
+			if "true" == value {
+				invocation.SetAttachments(key, "true")
+			}
+		default:
+			// ignore
+		}
+	}
+}
+
+func (invoker *zoneAwareClusterInvoker) AfterInvoker(ctx context.Context, invocation protocol.Invocation) {
+
+}
+
+func matchParam(target, key, def string, invoker protocol.Invoker) bool {
+	return target == invoker.GetUrl().GetParam(key, def)
+}
diff --git a/cluster/cluster_impl/zone_aware_cluster_invoker_test.go b/cluster/cluster_impl/zone_aware_cluster_invoker_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..7f77f33166de293836c15391f5eedd5a18084dbe
--- /dev/null
+++ b/cluster/cluster_impl/zone_aware_cluster_invoker_test.go
@@ -0,0 +1,206 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cluster_impl
+
+import (
+	"context"
+	"fmt"
+	"testing"
+)
+
+import (
+	"github.com/golang/mock/gomock"
+	"github.com/stretchr/testify/assert"
+)
+
+import (
+	"github.com/apache/dubbo-go/cluster/directory"
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/common/constant"
+	"github.com/apache/dubbo-go/protocol"
+	"github.com/apache/dubbo-go/protocol/invocation"
+	"github.com/apache/dubbo-go/protocol/mock"
+)
+
+func TestZoneWareInvokerWithPreferredSuccess(t *testing.T) {
+	ctrl := gomock.NewController(t)
+	// In Go versions 1.14+, if you pass a *testing.T
+	// into gomock.NewController(t) you no longer need to call ctrl.Finish().
+	//defer ctrl.Finish()
+
+	mockResult := &protocol.RPCResult{
+		Attrs: map[string]interface{}{constant.PREFERRED_KEY: "true"},
+		Rest:  rest{tried: 0, success: true}}
+
+	var invokers []protocol.Invoker
+	for i := 0; i < 2; i++ {
+		url, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i))
+		invoker := mock.NewMockInvoker(ctrl)
+		invoker.EXPECT().IsAvailable().Return(true).AnyTimes()
+		invoker.EXPECT().GetUrl().Return(url).AnyTimes()
+		if 0 == i {
+			url.SetParam(constant.REGISTRY_KEY+"."+constant.PREFERRED_KEY, "true")
+			invoker.EXPECT().Invoke(gomock.Any()).DoAndReturn(
+				func(invocation protocol.Invocation) protocol.Result {
+					return mockResult
+				})
+		} else {
+			invoker.EXPECT().Invoke(gomock.Any()).DoAndReturn(
+				func(invocation protocol.Invocation) protocol.Result {
+					return &protocol.RPCResult{}
+				})
+		}
+
+		invokers = append(invokers, invoker)
+	}
+
+	zoneAwareCluster := NewZoneAwareCluster()
+	staticDir := directory.NewStaticDirectory(invokers)
+	clusterInvoker := zoneAwareCluster.Join(staticDir)
+
+	result := clusterInvoker.Invoke(context.Background(), &invocation.RPCInvocation{})
+
+	assert.Equal(t, mockResult, result)
+}
+
+func TestZoneWareInvokerWithWeightSuccess(t *testing.T) {
+	ctrl := gomock.NewController(t)
+	// In Go versions 1.14+, if you pass a *testing.T
+	// into gomock.NewController(t) you no longer need to call ctrl.Finish().
+	//defer ctrl.Finish()
+
+	w1 := "50"
+	w2 := "200"
+
+	var invokers []protocol.Invoker
+	for i := 0; i < 2; i++ {
+		url, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i))
+		invoker := mock.NewMockInvoker(ctrl)
+		invoker.EXPECT().IsAvailable().Return(true).AnyTimes()
+		invoker.EXPECT().GetUrl().Return(url).AnyTimes()
+		url.SetParam(constant.REGISTRY_KEY+"."+constant.REGISTRY_LABEL_KEY, "true")
+		if 1 == i {
+			url.SetParam(constant.REGISTRY_KEY+"."+constant.WEIGHT_KEY, w1)
+			invoker.EXPECT().Invoke(gomock.Any()).DoAndReturn(
+				func(invocation protocol.Invocation) protocol.Result {
+					return &protocol.RPCResult{
+						Attrs: map[string]interface{}{constant.WEIGHT_KEY: w1},
+						Rest:  rest{tried: 0, success: true}}
+				}).MaxTimes(100)
+		} else {
+			url.SetParam(constant.REGISTRY_KEY+"."+constant.WEIGHT_KEY, w2)
+			invoker.EXPECT().Invoke(gomock.Any()).DoAndReturn(
+				func(invocation protocol.Invocation) protocol.Result {
+					return &protocol.RPCResult{
+						Attrs: map[string]interface{}{constant.WEIGHT_KEY: w2},
+						Rest:  rest{tried: 0, success: true}}
+				}).MaxTimes(100)
+		}
+		invokers = append(invokers, invoker)
+	}
+
+	zoneAwareCluster := NewZoneAwareCluster()
+	staticDir := directory.NewStaticDirectory(invokers)
+	clusterInvoker := zoneAwareCluster.Join(staticDir)
+
+	var w2Count, w1Count int
+	loop := 50
+	for i := 0; i < loop; i++ {
+		result := clusterInvoker.Invoke(context.Background(), &invocation.RPCInvocation{})
+		if w2 == result.Attachment(constant.WEIGHT_KEY, "0") {
+			w2Count++
+		}
+		if w1 == result.Attachment(constant.WEIGHT_KEY, "0") {
+			w1Count++
+		}
+		assert.NoError(t, result.Error())
+	}
+	t.Logf("loop count : %d, w1 height : %s | count : %d, w2 height : %s | count : %d", loop,
+		w1, w1Count, w2, w2Count)
+}
+
+func TestZoneWareInvokerWithZoneSuccess(t *testing.T) {
+	var zoneArray = []string{"hangzhou", "shanghai"}
+
+	ctrl := gomock.NewController(t)
+	// In Go versions 1.14+, if you pass a *testing.T
+	// into gomock.NewController(t) you no longer need to call ctrl.Finish().
+	//defer ctrl.Finish()
+
+	var invokers []protocol.Invoker
+	for i := 0; i < 2; i++ {
+		zoneValue := zoneArray[i]
+		url, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i))
+		url.SetParam(constant.REGISTRY_KEY+"."+constant.ZONE_KEY, zoneValue)
+
+		invoker := mock.NewMockInvoker(ctrl)
+		invoker.EXPECT().IsAvailable().Return(true).AnyTimes()
+		invoker.EXPECT().GetUrl().Return(url).AnyTimes()
+		invoker.EXPECT().Invoke(gomock.Any()).DoAndReturn(
+			func(invocation protocol.Invocation) protocol.Result {
+				return &protocol.RPCResult{
+					Attrs: map[string]interface{}{constant.ZONE_KEY: zoneValue},
+					Rest:  rest{tried: 0, success: true}}
+			})
+		invokers = append(invokers, invoker)
+	}
+
+	zoneAwareCluster := NewZoneAwareCluster()
+	staticDir := directory.NewStaticDirectory(invokers)
+	clusterInvoker := zoneAwareCluster.Join(staticDir)
+
+	inv := &invocation.RPCInvocation{}
+	// zone hangzhou
+	hz := zoneArray[0]
+	inv.SetAttachments(constant.REGISTRY_KEY+"."+constant.ZONE_KEY, hz)
+
+	result := clusterInvoker.Invoke(context.Background(), inv)
+
+	assert.Equal(t, hz, result.Attachment(constant.ZONE_KEY, ""))
+}
+
+func TestZoneWareInvokerWithZoneForceFail(t *testing.T) {
+	ctrl := gomock.NewController(t)
+	// In Go versions 1.14+, if you pass a *testing.T
+	// into gomock.NewController(t) you no longer need to call ctrl.Finish().
+	//defer ctrl.Finish()
+
+	var invokers []protocol.Invoker
+	for i := 0; i < 2; i++ {
+		url, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i))
+
+		invoker := mock.NewMockInvoker(ctrl)
+		invoker.EXPECT().IsAvailable().Return(true).AnyTimes()
+		invoker.EXPECT().GetUrl().Return(url).AnyTimes()
+		invokers = append(invokers, invoker)
+	}
+
+	zoneAwareCluster := NewZoneAwareCluster()
+	staticDir := directory.NewStaticDirectory(invokers)
+	clusterInvoker := zoneAwareCluster.Join(staticDir)
+
+	inv := &invocation.RPCInvocation{}
+	// zone hangzhou
+	inv.SetAttachments(constant.REGISTRY_KEY+"."+constant.ZONE_KEY, "hangzhou")
+	// zone force
+	inv.SetAttachments(constant.REGISTRY_KEY+"."+constant.ZONE_FORCE_KEY, "true")
+
+	result := clusterInvoker.Invoke(context.Background(), inv)
+
+	assert.NotNil(t, result.Error())
+}
diff --git a/cluster/cluster_interceptor.go b/cluster/cluster_interceptor.go
new file mode 100644
index 0000000000000000000000000000000000000000..a627e81365781e83932668872cd94a2cdec3b71e
--- /dev/null
+++ b/cluster/cluster_interceptor.go
@@ -0,0 +1,39 @@
+/*
+ * 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 cluster
+
+import (
+	"context"
+)
+
+import (
+	"github.com/apache/dubbo-go/protocol"
+)
+
+// ClusterInterceptor
+// Extension - ClusterInterceptor
+type ClusterInterceptor interface {
+	// Before DoInvoke method
+	BeforeInvoker(ctx context.Context, invocation protocol.Invocation)
+
+	// After DoInvoke method
+	AfterInvoker(ctx context.Context, invocation protocol.Invocation)
+
+	// Corresponding cluster invoke
+	DoInvoke(ctx context.Context, invocation protocol.Invocation) protocol.Result
+}
diff --git a/cluster/directory/base_directory.go b/cluster/directory/base_directory.go
index 8a7e0c0e8359c69faf0e504adeb1778c38a08e04..20db1f2b7de71c843caf7c7abda39e40c68e4ecd 100644
--- a/cluster/directory/base_directory.go
+++ b/cluster/directory/base_directory.go
@@ -85,15 +85,21 @@ func (dir *BaseDirectory) SetRouters(urls []*common.URL) {
 	for _, url := range urls {
 		routerKey := url.GetParam(constant.ROUTER_KEY, "")
 
-		if len(routerKey) > 0 {
-			factory := extension.GetRouterFactory(url.Protocol)
-			r, err := factory.NewPriorityRouter(url)
-			if err != nil {
-				logger.Errorf("Create router fail. router key: %s, url:%s, error: %+v", routerKey, url.Service(), err)
-				return
+		if len(routerKey) == 0 {
+			continue
+		}
+		if url.Protocol == constant.CONDITION_ROUTE_PROTOCOL {
+			if !dir.isProperRouter(url) {
+				continue
 			}
-			routers = append(routers, r)
 		}
+		factory := extension.GetRouterFactory(url.Protocol)
+		r, err := factory.NewPriorityRouter(url)
+		if err != nil {
+			logger.Errorf("Create router fail. router key: %s, url:%s, error: %+v", routerKey, url.Service(), err)
+			return
+		}
+		routers = append(routers, r)
 	}
 
 	logger.Infof("Init file condition router success, size: %v", len(routers))
@@ -104,6 +110,25 @@ func (dir *BaseDirectory) SetRouters(urls []*common.URL) {
 	rc.AddRouters(routers)
 }
 
+func (dir *BaseDirectory) isProperRouter(url *common.URL) bool {
+	app := url.GetParam(constant.APPLICATION_KEY, "")
+	dirApp := dir.GetUrl().GetParam(constant.APPLICATION_KEY, "")
+	if len(dirApp) == 0 && dir.GetUrl().SubURL != nil {
+		dirApp = dir.GetUrl().SubURL.GetParam(constant.APPLICATION_KEY, "")
+	}
+	serviceKey := dir.GetUrl().ServiceKey()
+	if len(serviceKey) == 0 {
+		serviceKey = dir.GetUrl().SubURL.ServiceKey()
+	}
+	if len(app) > 0 && app == dirApp {
+		return true
+	}
+	if url.ServiceKey() == serviceKey {
+		return true
+	}
+	return false
+}
+
 // Destroy Destroy
 func (dir *BaseDirectory) Destroy(doDestroy func()) {
 	if dir.destroyed.CAS(false, true) {
diff --git a/cluster/directory/base_directory_test.go b/cluster/directory/base_directory_test.go
index 8b60163b79b7120829e51f69238474a127133fb4..a2b62dfa008e6cd17b1200d93cd235da17d03905 100644
--- a/cluster/directory/base_directory_test.go
+++ b/cluster/directory/base_directory_test.go
@@ -37,7 +37,7 @@ import (
 var (
 	url, _ = common.NewURL(
 		fmt.Sprintf("dubbo://%s:%d/com.ikurento.user.UserProvider", constant.LOCAL_HOST_VALUE, constant.DEFAULT_PORT))
-	anyUrl, _ = common.NewURL(fmt.Sprintf("condition://%s/com.foo.BarService", constant.ANYHOST_VALUE))
+	anyURL, _ = common.NewURL(fmt.Sprintf("condition://%s/com.foo.BarService", constant.ANYHOST_VALUE))
 )
 
 func TestNewBaseDirectory(t *testing.T) {
@@ -48,13 +48,17 @@ func TestNewBaseDirectory(t *testing.T) {
 }
 
 func TestBuildRouterChain(t *testing.T) {
-	directory := NewBaseDirectory(&url)
+
+	regURL := url
+	regURL.AddParam(constant.INTERFACE_KEY, "mock-app")
+	directory := NewBaseDirectory(&regURL)
 
 	assert.NotNil(t, directory)
 
 	localIP, _ := gxnet.GetLocalIP()
 	rule := base64.URLEncoding.EncodeToString([]byte("true => " + " host = " + localIP))
-	routeURL := getRouteUrl(rule)
+	routeURL := getRouteURL(rule, anyURL)
+	routeURL.AddParam(constant.INTERFACE_KEY, "mock-app")
 	routerURLs := make([]*common.URL, 0)
 	routerURLs = append(routerURLs, routeURL)
 	directory.SetRouters(routerURLs)
@@ -63,9 +67,53 @@ func TestBuildRouterChain(t *testing.T) {
 	assert.NotNil(t, chain)
 }
 
-func getRouteUrl(rule string) *common.URL {
-	anyUrl.AddParam("rule", rule)
-	anyUrl.AddParam("force", "true")
-	anyUrl.AddParam(constant.ROUTER_KEY, "router")
-	return &url
+func getRouteURL(rule string, u common.URL) *common.URL {
+	ru := u
+	ru.AddParam("rule", rule)
+	ru.AddParam("force", "true")
+	ru.AddParam(constant.ROUTER_KEY, "router")
+	return &ru
+}
+
+func TestIsProperRouter(t *testing.T) {
+	regURL := url
+	regURL.AddParam(constant.APPLICATION_KEY, "mock-app")
+	d := NewBaseDirectory(&regURL)
+	localIP, _ := gxnet.GetLocalIP()
+	rule := base64.URLEncoding.EncodeToString([]byte("true => " + " host = " + localIP))
+	routeURL := getRouteURL(rule, anyURL)
+	routeURL.AddParam(constant.APPLICATION_KEY, "mock-app")
+	rst := d.isProperRouter(routeURL)
+	assert.True(t, rst)
+
+	regURL.AddParam(constant.APPLICATION_KEY, "")
+	regURL.AddParam(constant.INTERFACE_KEY, "com.foo.BarService")
+	d = NewBaseDirectory(&regURL)
+	routeURL = getRouteURL(rule, anyURL)
+	routeURL.AddParam(constant.INTERFACE_KEY, "com.foo.BarService")
+	rst = d.isProperRouter(routeURL)
+	assert.True(t, rst)
+
+	regURL.AddParam(constant.APPLICATION_KEY, "")
+	regURL.AddParam(constant.INTERFACE_KEY, "")
+	d = NewBaseDirectory(&regURL)
+	routeURL = getRouteURL(rule, anyURL)
+	rst = d.isProperRouter(routeURL)
+	assert.True(t, rst)
+
+	regURL.SetParam(constant.APPLICATION_KEY, "")
+	regURL.SetParam(constant.INTERFACE_KEY, "")
+	d = NewBaseDirectory(&regURL)
+	routeURL = getRouteURL(rule, anyURL)
+	routeURL.AddParam(constant.APPLICATION_KEY, "mock-service")
+	rst = d.isProperRouter(routeURL)
+	assert.False(t, rst)
+
+	regURL.SetParam(constant.APPLICATION_KEY, "")
+	regURL.SetParam(constant.INTERFACE_KEY, "")
+	d = NewBaseDirectory(&regURL)
+	routeURL = getRouteURL(rule, anyURL)
+	routeURL.AddParam(constant.INTERFACE_KEY, "mock-service")
+	rst = d.isProperRouter(routeURL)
+	assert.False(t, rst)
 }
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/loadbalance/consistent_hash.go b/cluster/loadbalance/consistent_hash.go
index 84fbb268c7a8ec32f007a734e2d6da56ef3c6d25..27ce2369f9de6dfe76bd35581ea26f0e0c24e480 100644
--- a/cluster/loadbalance/consistent_hash.go
+++ b/cluster/loadbalance/consistent_hash.go
@@ -38,11 +38,11 @@ import (
 )
 
 const (
-	// ConsistentHash ...
+	// ConsistentHash consistent hash
 	ConsistentHash = "consistenthash"
-	// HashNodes ...
+	// HashNodes hash nodes
 	HashNodes = "hash.nodes"
-	// HashArguments  key of hash arguments  in url
+	// HashArguments key of hash arguments in url
 	HashArguments = "hash.arguments"
 )
 
@@ -157,6 +157,7 @@ func (c *ConsistentHashSelector) selectForKey(hash uint32) protocol.Invoker {
 	return c.virtualInvokers[c.keys[idx]]
 }
 
+// nolint
 func (c *ConsistentHashSelector) hash(digest [16]byte, i int) uint32 {
 	return uint32((digest[3+i*4]&0xFF)<<24) | uint32((digest[2+i*4]&0xFF)<<16) |
 		uint32((digest[1+i*4]&0xFF)<<8) | uint32(digest[i*4]&0xFF)&0xFFFFFFF
diff --git a/cluster/loadbalance/least_active.go b/cluster/loadbalance/least_active.go
index 37ad91c3ed6b44370820a989b7af8ccaa82c48a2..87767359a94c29c9aa83ae1fa5b042d7bac2548b 100644
--- a/cluster/loadbalance/least_active.go
+++ b/cluster/loadbalance/least_active.go
@@ -46,6 +46,7 @@ func NewLeastActiveLoadBalance() cluster.LoadBalance {
 	return &leastActiveLoadBalance{}
 }
 
+// Select gets invoker based on least active load balancing strategy
 func (lb *leastActiveLoadBalance) Select(invokers []protocol.Invoker, invocation protocol.Invocation) protocol.Invoker {
 	count := len(invokers)
 	if count == 0 {
diff --git a/cluster/loadbalance/round_robin.go b/cluster/loadbalance/round_robin.go
index c44b239dbcbcc744f47ca3c97128f92567e32a78..51a76da99818063b6f13de1c007ccbc3709e1701 100644
--- a/cluster/loadbalance/round_robin.go
+++ b/cluster/loadbalance/round_robin.go
@@ -31,12 +31,12 @@ import (
 )
 
 const (
-	// RoundRobin ...
+	// RoundRobin load balancing way
 	RoundRobin = "roundrobin"
 
-	// COMPLETE ...
+	// nolint
 	COMPLETE = 0
-	// UPDATING ...
+	// nolint
 	UPDATING = 1
 )
 
@@ -59,6 +59,7 @@ func NewRoundRobinLoadBalance() cluster.LoadBalance {
 	return &roundRobinLoadBalance{}
 }
 
+// Select gets invoker based on round robin load balancing strategy
 func (lb *roundRobinLoadBalance) Select(invokers []protocol.Invoker, invocation protocol.Invocation) protocol.Invoker {
 	count := len(invokers)
 	if count == 0 {
diff --git a/cluster/loadbalance/util.go b/cluster/loadbalance/util.go
index b6c013852bf55ce7eb67e4fa18802a938141d283..684ffe11a72058e188fdbcc5f8aa56fe16073619 100644
--- a/cluster/loadbalance/util.go
+++ b/cluster/loadbalance/util.go
@@ -28,23 +28,35 @@ import (
 
 // GetWeight gets weight for load balance strategy
 func GetWeight(invoker protocol.Invoker, invocation protocol.Invocation) int64 {
+	var weight int64
 	url := invoker.GetUrl()
-	weight := url.GetMethodParamInt64(invocation.MethodName(), constant.WEIGHT_KEY, constant.DEFAULT_WEIGHT)
+	// Multiple registry scenario, load balance among multiple registries.
+	isRegIvk := url.GetParamBool(constant.REGISTRY_KEY+"."+constant.REGISTRY_LABEL_KEY, false)
+	if isRegIvk {
+		weight = url.GetParamInt(constant.REGISTRY_KEY+"."+constant.WEIGHT_KEY, constant.DEFAULT_WEIGHT)
+	} else {
+		weight = url.GetMethodParamInt64(invocation.MethodName(), constant.WEIGHT_KEY, constant.DEFAULT_WEIGHT)
 
-	if weight > 0 {
-		//get service register time an do warm up time
-		now := time.Now().Unix()
-		timestamp := url.GetParamInt(constant.REMOTE_TIMESTAMP_KEY, now)
-		if uptime := now - timestamp; uptime > 0 {
-			warmup := url.GetParamInt(constant.WARMUP_KEY, constant.DEFAULT_WARMUP)
-			if uptime < warmup {
-				if ww := float64(uptime) / float64(warmup) / float64(weight); ww < 1 {
-					weight = 1
-				} else if int64(ww) <= weight {
-					weight = int64(ww)
+		if weight > 0 {
+			//get service register time an do warm up time
+			now := time.Now().Unix()
+			timestamp := url.GetParamInt(constant.REMOTE_TIMESTAMP_KEY, now)
+			if uptime := now - timestamp; uptime > 0 {
+				warmup := url.GetParamInt(constant.WARMUP_KEY, constant.DEFAULT_WARMUP)
+				if uptime < warmup {
+					if ww := float64(uptime) / float64(warmup) / float64(weight); ww < 1 {
+						weight = 1
+					} else if int64(ww) <= weight {
+						weight = int64(ww)
+					}
 				}
 			}
 		}
 	}
+
+	if weight < 0 {
+		weight = 0
+	}
+
 	return weight
 }
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 97d20ac5fca0688d3f1f48c6ffc12314f5dc3904..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,20 +56,37 @@ 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(invoker []protocol.Invoker, url *common.URL, invocation protocol.Invocation) []protocol.Invoker {
-	finalInvokers := invoker
-	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
+	}
+
+	bitmap := cache.bitmap
+	for _, r := range c.copyRouters() {
+		bitmap = r.Route(bitmap, cache, url, invocation)
+	}
 
-	for _, r := range rs {
-		finalInvokers = r.Route(finalInvokers, url, invocation)
+	indexes := bitmap.ToArray()
+	finalInvokers := make([]protocol.Invoker, len(indexes))
+	for i, index := range indexes {
+		finalInvokers[i] = cache.invokers[index]
 	}
+
 	return finalInvokers
 }
 
@@ -79,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
@@ -109,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 c1f723525f5307e7732f0ea1ecc27eca7ba09c8d..5a9f2d1c74db847df56b1ebab609b65c0cbd7484 100644
--- a/cluster/router/chain/chain_test.go
+++ b/cluster/router/chain/chain_test.go
@@ -66,7 +66,9 @@ func TestNewRouterChain(t *testing.T) {
 	err = z.Create(path)
 	assert.NoError(t, err)
 
-	testyml := `enabled: true
+	testyml := `scope: application
+key: mock-app
+enabled: true
 force: true
 runtime: false
 conditions:
@@ -93,7 +95,7 @@ conditions:
 	assert.NotNil(t, appRouter)
 	assert.NotNil(t, appRouter.RouterRule())
 	rule := appRouter.RouterRule()
-	assert.Equal(t, "", rule.Scope)
+	assert.Equal(t, "application", rule.Scope)
 	assert.True(t, rule.Force)
 	assert.True(t, rule.Enabled)
 	assert.True(t, rule.Valid)
@@ -101,7 +103,7 @@ conditions:
 	assert.Equal(t, testyml, rule.RawRule)
 	assert.Equal(t, false, rule.Runtime)
 	assert.Equal(t, false, rule.Dynamic)
-	assert.Equal(t, "", rule.Key)
+	assert.Equal(t, "mock-app", rule.Key)
 }
 
 func TestNewRouterChainURLNil(t *testing.T) {
@@ -116,7 +118,9 @@ func TestRouterChainAddRouters(t *testing.T) {
 	err = z.Create(path)
 	assert.NoError(t, err)
 
-	testyml := `enabled: true
+	testyml := `scope: application
+key: mock-app
+enabled: true
 force: true
 runtime: false
 conditions:
@@ -168,10 +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))
 }
@@ -182,7 +188,9 @@ func TestRouterChainRouteAppRouter(t *testing.T) {
 	err = z.Create(path)
 	assert.NoError(t, err)
 
-	testyml := `enabled: true
+	testyml := `scope: application
+key: mock-app
+enabled: true
 force: true
 runtime: false
 conditions:
@@ -205,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))
 }
@@ -232,10 +242,12 @@ func TestRouterChainRouteNoRoute(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, 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/app_router_test.go b/cluster/router/condition/app_router_test.go
index 8b38f2dd6136b4d31f46e7214c0ad1359537b198..cce96b12c95a691e828d91ba3d0629ddb6421954 100644
--- a/cluster/router/condition/app_router_test.go
+++ b/cluster/router/condition/app_router_test.go
@@ -24,7 +24,6 @@ import (
 )
 
 import (
-	_ "github.com/apache/dubbo-go/config_center/zookeeper"
 	"github.com/stretchr/testify/assert"
 )
 
@@ -34,6 +33,7 @@ import (
 	"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"
 	"github.com/apache/dubbo-go/remoting"
 	"github.com/apache/dubbo-go/remoting/zookeeper"
 )
@@ -52,7 +52,9 @@ var (
 
 func TestNewAppRouter(t *testing.T) {
 
-	testYML := `enabled: true
+	testYML := `scope: application
+key: mock-app
+enabled: true
 force: true
 runtime: false
 conditions:
@@ -83,7 +85,7 @@ conditions:
 	assert.NotNil(t, appRouter)
 	assert.NotNil(t, appRouter.RouterRule())
 	rule := appRouter.RouterRule()
-	assert.Equal(t, "", rule.Scope)
+	assert.Equal(t, "application", rule.Scope)
 	assert.True(t, rule.Force)
 	assert.True(t, rule.Enabled)
 	assert.True(t, rule.Valid)
@@ -91,13 +93,15 @@ conditions:
 	assert.Equal(t, testYML, rule.RawRule)
 	assert.Equal(t, false, rule.Runtime)
 	assert.Equal(t, false, rule.Dynamic)
-	assert.Equal(t, "", rule.Key)
+	assert.Equal(t, "mock-app", rule.Key)
 	assert.Equal(t, 0, rule.Priority)
 }
 
 func TestGenerateConditions(t *testing.T) {
 
-	testYML := `enabled: true
+	testYML := `scope: application
+key: mock-app
+enabled: true
 force: true
 runtime: false
 conditions:
@@ -135,7 +139,9 @@ conditions:
 
 func TestProcess(t *testing.T) {
 
-	testYML := `enabled: true
+	testYML := `scope: application
+key: mock-app
+enabled: true
 force: true
 runtime: false
 conditions:
@@ -165,7 +171,8 @@ conditions:
 
 	assert.Equal(t, 1, len(appRouter.conditionRouters))
 
-	testNewYML := `
+	testNewYML := `scope: application
+key: mock-app
 enabled: true
 force: true
 runtime: false
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/file.go b/cluster/router/condition/file.go
index eabdf1c263446140b359b3e791238b020cecb50c..996db7443faff88d3baa1a2363f98fa62623d121 100644
--- a/cluster/router/condition/file.go
+++ b/cluster/router/condition/file.go
@@ -20,6 +20,7 @@ package condition
 import (
 	"encoding/base64"
 	"net/url"
+	"regexp"
 	"strconv"
 	"strings"
 	"sync"
@@ -71,11 +72,53 @@ func (f *FileConditionRouter) URL() common.URL {
 			common.WithParamsValue(constant.RouterPriority, strconv.Itoa(routerRule.Priority)),
 			common.WithParamsValue(constant.RULE_KEY, base64.URLEncoding.EncodeToString([]byte(rule))),
 			common.WithParamsValue(constant.ROUTER_KEY, constant.CONDITION_ROUTE_PROTOCOL),
-			common.WithParamsValue(constant.CATEGORY_KEY, constant.ROUTERS_CATEGORY))
+			common.WithParamsValue(constant.CATEGORY_KEY, constant.ROUTERS_CATEGORY),
+		)
+		if routerRule.Scope == constant.RouterApplicationScope {
+			f.url.AddParam(constant.APPLICATION_KEY, routerRule.Key)
+			return
+		}
+		grp, srv, ver, e := parseServiceRouterKey(routerRule.Key)
+		if e != nil {
+			return
+		}
+		if len(grp) > 0 {
+			f.url.AddParam(constant.GROUP_KEY, grp)
+		}
+		if len(ver) > 0 {
+			f.url.AddParam(constant.VERSION_KEY, ver)
+		}
+		if len(srv) > 0 {
+			f.url.AddParam(constant.INTERFACE_KEY, srv)
+		}
 	})
 	return f.url
 }
 
+// The input value must follow [{group}/]{service}[:{version}] pattern
+// the returning strings are representing group, service, version respectively.
+// input: mock-group/mock-service:1.0.0 ==> "mock-group", "mock-service", "1.0.0"
+// input: mock-group/mock-service ==> "mock-group", "mock-service", ""
+// input: mock-service:1.0.0 ==> "", "mock-service", "1.0.0"
+// For more samples, please refer to unit test.
+func parseServiceRouterKey(key string) (string, string, string, error) {
+	if len(strings.TrimSpace(key)) == 0 {
+		return "", "", "", nil
+	}
+	reg := regexp.MustCompile(`(.*/{1})?([^:/]+)(:{1}[^:]*)?`)
+	strs := reg.FindAllStringSubmatch(key, -1)
+	if strs == nil || len(strs) > 1 {
+		return "", "", "", perrors.Errorf("Invalid key, service key must follow [{group}/]{service}[:{version}] pattern")
+	}
+	if len(strs[0]) != 4 {
+		return "", "", "", perrors.Errorf("Parse service router key failed")
+	}
+	grp := strings.TrimSpace(strings.TrimRight(strs[0][1], "/"))
+	srv := strings.TrimSpace(strs[0][2])
+	ver := strings.TrimSpace(strings.TrimLeft(strs[0][3], ":"))
+	return grp, srv, ver, nil
+}
+
 func parseCondition(conditions []string) string {
 	var when, then string
 	for _, condition := range conditions {
diff --git a/cluster/router/condition/file_test.go b/cluster/router/condition/file_test.go
index 3092b12ff88dcacc9108c7cdd46ba1ac9f74eb2b..bd19a0d18c6692af181ffef77c5cd3f9fc16d67d 100644
--- a/cluster/router/condition/file_test.go
+++ b/cluster/router/condition/file_test.go
@@ -26,7 +26,9 @@ import (
 )
 
 func TestLoadYmlConfig(t *testing.T) {
-	router, e := NewFileConditionRouter([]byte(`priority: 1
+	router, e := NewFileConditionRouter([]byte(`scope: application
+key: mock-app
+priority: 1
 force: true
 conditions :
   - "a => b"
@@ -47,12 +49,78 @@ func TestParseCondition(t *testing.T) {
 }
 
 func TestFileRouterURL(t *testing.T) {
-	router, e := NewFileConditionRouter([]byte(`priority: 1
+	router, e := NewFileConditionRouter([]byte(`scope: application
+key: mock-app
+priority: 1
 force: true
 conditions :
   - "a => b"
   - "c => d"`))
 	assert.Nil(t, e)
 	assert.NotNil(t, router)
-	assert.Equal(t, "condition://0.0.0.0:?category=routers&force=true&priority=1&router=condition&rule=YSAmIGMgPT4gYiAmIGQ%3D", router.URL().String())
+	assert.Equal(t, "condition://0.0.0.0:?application=mock-app&category=routers&force=true&priority=1&router=condition&rule=YSAmIGMgPT4gYiAmIGQ%3D", router.URL().String())
+
+	router, e = NewFileConditionRouter([]byte(`scope: service
+key: mock-service
+priority: 1
+force: true
+conditions :
+  - "a => b"
+  - "c => d"`))
+	assert.Nil(t, e)
+	assert.NotNil(t, router)
+	assert.Equal(t, "condition://0.0.0.0:?category=routers&force=true&interface=mock-service&priority=1&router=condition&rule=YSAmIGMgPT4gYiAmIGQ%3D", router.URL().String())
+
+	router, e = NewFileConditionRouter([]byte(`scope: service
+key: grp1/mock-service:v1
+priority: 1
+force: true
+conditions :
+  - "a => b"
+  - "c => d"`))
+	assert.Nil(t, e)
+	assert.NotNil(t, router)
+	assert.Equal(t, "condition://0.0.0.0:?category=routers&force=true&group=grp1&interface=mock-service&priority=1&router=condition&rule=YSAmIGMgPT4gYiAmIGQ%3D&version=v1", router.URL().String())
+}
+
+func TestParseServiceRouterKey(t *testing.T) {
+	testString := " mock-group / mock-service:1.0.0"
+	grp, srv, ver, err := parseServiceRouterKey(testString)
+	assert.Equal(t, "mock-group", grp)
+	assert.Equal(t, "mock-service", srv)
+	assert.Equal(t, "1.0.0", ver)
+
+	testString = "mock-group/mock-service"
+	grp, srv, ver, err = parseServiceRouterKey(testString)
+	assert.Equal(t, "mock-group", grp)
+	assert.Equal(t, "mock-service", srv)
+	assert.Equal(t, "", ver)
+
+	testString = "mock-service:1.0.0"
+	grp, srv, ver, err = parseServiceRouterKey(testString)
+	assert.Equal(t, "", grp)
+	assert.Equal(t, "mock-service", srv)
+	assert.Equal(t, "1.0.0", ver)
+
+	testString = "mock-service"
+	grp, srv, ver, err = parseServiceRouterKey(testString)
+	assert.Equal(t, "", grp)
+	assert.Equal(t, "mock-service", srv)
+	assert.Equal(t, "", ver)
+
+	testString = "/mock-service:"
+	grp, srv, ver, err = parseServiceRouterKey(testString)
+	assert.Equal(t, "", grp)
+	assert.Equal(t, "mock-service", srv)
+	assert.Equal(t, "", ver)
+
+	testString = "grp:mock-service:123"
+	grp, srv, ver, err = parseServiceRouterKey(testString)
+	assert.Error(t, err)
+
+	testString = ""
+	grp, srv, ver, err = parseServiceRouterKey(testString)
+	assert.Equal(t, "", grp)
+	assert.Equal(t, "", srv)
+	assert.Equal(t, "", ver)
 }
diff --git a/cluster/router/condition/listenable_router.go b/cluster/router/condition/listenable_router.go
index 4ccc19e95521d03ae1f663ec276646cf30926533..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)
@@ -85,7 +87,7 @@ func newListenableRouter(url *common.URL, ruleKey string) (*AppRouter, error) {
 	return l, nil
 }
 
-// Process Process config change event , generate routers and set them to the listenableRouter instance
+// Process Process config change event, generate routers and set them to the listenableRouter instance
 func (l *listenableRouter) Process(event *config_center.ConfigChangeEvent) {
 	logger.Infof("Notification of condition rule, change type is:[%s] , raw rule is:[%v]", event.ConfigType, event.Value)
 	if remoting.EventTypeDel == event.ConfigType {
@@ -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 40a251573f5e73d40032972313565d98b288b1b1..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]+)`
 )
 
@@ -117,7 +120,13 @@ func NewConditionRouter(url *common.URL) (*ConditionRouter, error) {
 	}
 
 	router.url = url
-	router.priority = url.GetParamInt(constant.RouterPriority, 0)
+	var defaultPriority int64 = 0
+	if url.GetParam(constant.APPLICATION_KEY, "") != "" {
+		defaultPriority = 150
+	} else if url.GetParam(constant.INTERFACE_KEY, "") != "" {
+		defaultPriority = 140
+	}
+	router.priority = url.GetParamInt(constant.RouterPriority, defaultPriority)
 	router.Force = url.GetParamBool(constant.RouterForce, false)
 	router.enabled = url.GetParamBool(constant.RouterEnabled, true)
 
@@ -142,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)
@@ -172,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
 }
 
@@ -288,7 +305,7 @@ func matchCondition(pairs map[string]MatchPair, url *common.URL, param *common.U
 	return result
 }
 
-// MatchPair Match key pair , condition process
+// MatchPair Match key pair, condition process
 type MatchPair struct {
 	Matches    *gxset.HashSet
 	Mismatches *gxset.HashSet
@@ -314,7 +331,7 @@ func (pair MatchPair) isMatch(value string, param *common.URL) bool {
 		return true
 	}
 	if !pair.Mismatches.Empty() && !pair.Matches.Empty() {
-		//when both mismatches and matches contain the same value, then using mismatches first
+		// when both mismatches and matches contain the same value, then using mismatches first
 		for mismatch := range pair.Mismatches.Items {
 			if isMatchGlobalPattern(mismatch.(string), value, param) {
 				return false
diff --git a/cluster/router/condition/router_rule.go b/cluster/router/condition/router_rule.go
index ce397d6cc0f51519123dd427709e8dba42d72a20..9f2bf2d41a941398698f110f77a7a3051a0c9c11 100644
--- a/cluster/router/condition/router_rule.go
+++ b/cluster/router/condition/router_rule.go
@@ -28,12 +28,13 @@ 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/yaml"
 )
 
 // RouterRule RouterRule config read from config file or config center
 type RouterRule struct {
-	router.BaseRouterRule `yaml:",inline""`
+	router.BaseRouterRule `yaml:",inline"`
 	Conditions            []string
 }
 
@@ -57,7 +58,7 @@ func getRule(rawRule string) (*RouterRule, error) {
 		return r, err
 	}
 	r.RawRule = rawRule
-	if len(r.Conditions) != 0 {
+	if len(r.Conditions) > 0 && len(r.Key) > 0 && (r.Scope == constant.RouterApplicationScope || r.Scope == constant.RouterServiceScope) {
 		r.Valid = true
 	}
 
diff --git a/cluster/router/condition/router_rule_test.go b/cluster/router/condition/router_rule_test.go
index 675acaec912b413d8fa3d1a25463b1fd4813a7f5..369b14f08a6a650b36fe63a2d890c04fe7b892a5 100644
--- a/cluster/router/condition/router_rule_test.go
+++ b/cluster/router/condition/router_rule_test.go
@@ -32,6 +32,7 @@ import (
 func TestGetRule(t *testing.T) {
 	testyml := `
 scope: application
+key: test-provider
 runtime: true
 force: false
 conditions:
@@ -50,10 +51,31 @@ conditions:
 	assert.True(t, rule.Runtime)
 	assert.Equal(t, false, rule.Force)
 	assert.Equal(t, testyml, rule.RawRule)
-	assert.True(t, true, rule.Valid)
+	assert.True(t, rule.Valid)
 	assert.Equal(t, false, rule.Enabled)
 	assert.Equal(t, false, rule.Dynamic)
-	assert.Equal(t, "", rule.Key)
+	assert.Equal(t, "test-provider", rule.Key)
+
+	testyml = `
+key: test-provider
+runtime: true
+force: false
+conditions:
+  - >
+    method!=sayHello =>`
+	rule, e = getRule(testyml)
+	assert.Nil(t, e)
+	assert.False(t, rule.Valid)
+
+	testyml = `
+scope: noApplication
+key: test-provider
+conditions:
+  - >
+    method!=sayHello =>`
+	rule, e = getRule(testyml)
+	assert.Nil(t, e)
+	assert.False(t, rule.Valid)
 }
 
 func TestIsMatchGlobPattern(t *testing.T) {
diff --git a/cluster/router/condition/router_test.go b/cluster/router/condition/router_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..c27a1d9552a331d7e67af0eb3d444c480758891e
--- /dev/null
+++ b/cluster/router/condition/router_test.go
@@ -0,0 +1,88 @@
+/*
+ * 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 condition
+
+import (
+	"testing"
+)
+
+import (
+	"github.com/apache/dubbo-go/common"
+	"github.com/dubbogo/gost/container/set"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestParseRule(t *testing.T) {
+	testString := ``
+	matchPair, err := parseRule(testString)
+	assert.Nil(t, err)
+	assert.EqualValues(t, matchPair, make(map[string]MatchPair, 16))
+
+	testString = `method!=sayHello&application=sayGoodBye`
+	matchPair, err = parseRule(testString)
+	assert.Nil(t, err)
+	assert.EqualValues(t, matchPair["method"].Mismatches, gxset.NewSet("sayHello"))
+	assert.EqualValues(t, matchPair["application"].Matches, gxset.NewSet("sayGoodBye"))
+
+	testString = `noRule`
+	matchPair, err = parseRule(testString)
+	assert.Nil(t, err)
+	assert.EqualValues(t, matchPair["noRule"].Mismatches, gxset.NewSet())
+	assert.EqualValues(t, matchPair["noRule"].Matches, gxset.NewSet())
+
+	testString = `method!=sayHello,sayGoodBye`
+	matchPair, err = parseRule(testString)
+	assert.Nil(t, err)
+	assert.EqualValues(t, matchPair["method"].Mismatches, gxset.NewSet(`sayHello`, `sayGoodBye`))
+
+	testString = `method!=sayHello,sayGoodDay=sayGoodBye`
+	matchPair, err = parseRule(testString)
+	assert.Nil(t, err)
+	assert.EqualValues(t, matchPair["method"].Mismatches, gxset.NewSet(`sayHello`, `sayGoodDay`))
+	assert.EqualValues(t, matchPair["method"].Matches, gxset.NewSet(`sayGoodBye`))
+}
+
+func TestNewConditionRouter(t *testing.T) {
+	url, _ := common.NewURL(`condition://0.0.0.0:?application=mock-app&category=routers&force=true&priority=1&router=condition&rule=YSAmIGMgPT4gYiAmIGQ%3D`)
+	router, err := NewConditionRouter(&url)
+	assert.Nil(t, err)
+	assert.Equal(t, true, router.Enabled())
+	assert.Equal(t, true, router.Force)
+	assert.Equal(t, int64(1), router.Priority())
+	whenRule, _ := parseRule("a & c")
+	thenRule, _ := parseRule("b & d")
+	assert.EqualValues(t, router.WhenCondition, whenRule)
+	assert.EqualValues(t, router.ThenCondition, thenRule)
+
+	router, err = NewConditionRouter(nil)
+	assert.Error(t, err)
+
+	url, _ = common.NewURL(`condition://0.0.0.0:?application=mock-app&category=routers&force=true&priority=1&router=condition&rule=YSAmT4gYiAmIGQ%3D`)
+	router, err = NewConditionRouter(&url)
+	assert.Error(t, err)
+
+	url, _ = common.NewURL(`condition://0.0.0.0:?application=mock-app&category=routers&force=true&router=condition&rule=YSAmIGMgPT4gYiAmIGQ%3D`)
+	router, err = NewConditionRouter(&url)
+	assert.Nil(t, err)
+	assert.Equal(t, int64(150), router.Priority())
+
+	url, _ = common.NewURL(`condition://0.0.0.0:?category=routers&force=true&interface=mock-service&router=condition&rule=YSAmIGMgPT4gYiAmIGQ%3D`)
+	router, err = NewConditionRouter(&url)
+	assert.Nil(t, err)
+	assert.Equal(t, int64(140), router.Priority())
+}
diff --git a/cluster/router/healthcheck/default_health_check.go b/cluster/router/healthcheck/default_health_check.go
index a26f86ddac45aa6e999cd4453aa296d0786a02ba..c522bdf0f98244f5f8bdfba6085cb45dd89c1004 100644
--- a/cluster/router/healthcheck/default_health_check.go
+++ b/cluster/router/healthcheck/default_health_check.go
@@ -31,7 +31,7 @@ import (
 )
 
 func init() {
-	extension.SethealthChecker(constant.DEFAULT_HEALTH_CHECKER, NewDefaultHealthChecker)
+	extension.SetHealthChecker(constant.DEFAULT_HEALTH_CHECKER, NewDefaultHealthChecker)
 }
 
 // DefaultHealthChecker is the default implementation of HealthChecker, which determines the health status of
@@ -49,7 +49,7 @@ type DefaultHealthChecker struct {
 // and the current active request
 func (c *DefaultHealthChecker) IsHealthy(invoker protocol.Invoker) bool {
 	urlStatus := protocol.GetURLStatus(invoker.GetUrl())
-	if c.isCircuitBreakerTripped(urlStatus) || urlStatus.GetActive() > c.GetOutStandingRequestConutLimit() {
+	if c.isCircuitBreakerTripped(urlStatus) || urlStatus.GetActive() > c.GetOutStandingRequestCountLimit() {
 		logger.Debugf("Invoker [%s] is currently in circuitbreaker tripped state", invoker.GetUrl().Key())
 		return false
 	}
@@ -85,25 +85,25 @@ func (c *DefaultHealthChecker) getCircuitBreakerSleepWindowTime(status *protocol
 	} else if diff > constant.DEFAULT_SUCCESSIVE_FAILED_REQUEST_MAX_DIFF {
 		diff = constant.DEFAULT_SUCCESSIVE_FAILED_REQUEST_MAX_DIFF
 	}
-	sleepWindow := (1 << diff) * c.GetCircuitTrippedTimeoutFactor()
+	sleepWindow := (1 << uint(diff)) * c.GetCircuitTrippedTimeoutFactor()
 	if sleepWindow > constant.MAX_CIRCUIT_TRIPPED_TIMEOUT_IN_MS {
 		sleepWindow = constant.MAX_CIRCUIT_TRIPPED_TIMEOUT_IN_MS
 	}
 	return int64(sleepWindow)
 }
 
-// GetOutStandingRequestConutLimit return the requestSuccessiveFailureThreshold bound to this DefaultHealthChecker
+// GetRequestSuccessiveFailureThreshold return the requestSuccessiveFailureThreshold bound to this DefaultHealthChecker
 func (c *DefaultHealthChecker) GetRequestSuccessiveFailureThreshold() int32 {
 	return c.requestSuccessiveFailureThreshold
 }
 
-// GetOutStandingRequestConutLimit return the circuitTrippedTimeoutFactor bound to this DefaultHealthChecker
+// GetCircuitTrippedTimeoutFactor return the circuitTrippedTimeoutFactor bound to this DefaultHealthChecker
 func (c *DefaultHealthChecker) GetCircuitTrippedTimeoutFactor() int32 {
 	return c.circuitTrippedTimeoutFactor
 }
 
-// GetOutStandingRequestConutLimit return the outStandingRequestConutLimit bound to this DefaultHealthChecker
-func (c *DefaultHealthChecker) GetOutStandingRequestConutLimit() int32 {
+// GetOutStandingRequestCountLimit return the outStandingRequestConutLimit bound to this DefaultHealthChecker
+func (c *DefaultHealthChecker) GetOutStandingRequestCountLimit() int32 {
 	return c.outStandingRequestConutLimit
 }
 
diff --git a/cluster/router/healthcheck/factory_test.go b/cluster/router/healthcheck/factory_test.go
index c3a26a93896e185f0dea3732ca5afcf7687ad5ea..e80fd4c4d38dbb1c0f2b14ba1e22971249bc54b6 100644
--- a/cluster/router/healthcheck/factory_test.go
+++ b/cluster/router/healthcheck/factory_test.go
@@ -31,34 +31,43 @@ import (
 	"github.com/apache/dubbo-go/protocol"
 )
 
+// nolint
 type MockInvoker struct {
 	url common.URL
 }
 
+// nolint
 func NewMockInvoker(url common.URL) *MockInvoker {
 	return &MockInvoker{
 		url: url,
 	}
 }
 
+// nolint
 func (bi *MockInvoker) GetUrl() common.URL {
 	return bi.url
 }
+
+// nolint
 func (bi *MockInvoker) IsAvailable() bool {
 	return true
 }
 
+// nolint
 func (bi *MockInvoker) IsDestroyed() bool {
 	return true
 }
 
+// nolint
 func (bi *MockInvoker) Invoke(_ context.Context, _ protocol.Invocation) protocol.Result {
 	return nil
 }
 
+// nolint
 func (bi *MockInvoker) Destroy() {
 }
 
+// nolint
 func TestHealthCheckRouteFactory(t *testing.T) {
 	factory := newHealthCheckRouteFactory()
 	assert.NotNil(t, factory)
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 1d1f79d277860abf34bebb9deab8a0f0a67c7b5d..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
 }
@@ -50,3 +55,39 @@ type PriorityRouter interface {
 	// 0 to ^int(0) is better
 	Priority() int64
 }
+
+// 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 8144c83203dbe98778dd6bb8dcdb9888be664b3b..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"
@@ -42,7 +44,7 @@ type FileTagRouter struct {
 	force      bool
 }
 
-// NewFileTagRouter Create file tag router instance with content ( from config file)
+// NewFileTagRouter Create file tag router instance with content (from config file)
 func NewFileTagRouter(content []byte) (*FileTagRouter, error) {
 	fileRouter := &FileTagRouter{}
 	rule, err := getRule(string(content))
@@ -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 926446dcb2f18fa2fd4639a9246a85f435d75d45..c0a2d763ae7396ae41694f558f443d46084c3357 100644
--- a/cluster/router/tag/router_rule.go
+++ b/cluster/router/tag/router_rule.go
@@ -22,9 +22,27 @@ import (
 	"github.com/apache/dubbo-go/common/yaml"
 )
 
+/**
+ * %YAML1.2
+ * ---
+ * force: true
+ * runtime: false
+ * enabled: true
+ * priority: 1
+ * key: demo-provider
+ * tags:
+ * - name: tag1
+ * addresses: [ip1, ip2]
+ * - name: tag2
+ * addresses: [ip3, ip4]
+ * ...
+ */
 // RouterRule RouterRule config read from config file or config center
 type RouterRule struct {
-	router.BaseRouterRule `yaml:",inline""`
+	router.BaseRouterRule `yaml:",inline"`
+	Tags                  []Tag
+	AddressToTagNames     map[string][]string
+	TagNameToAddresses    map[string][]string
 }
 
 func getRule(rawRule string) (*RouterRule, error) {
@@ -34,5 +52,54 @@ func getRule(rawRule string) (*RouterRule, error) {
 		return r, err
 	}
 	r.RawRule = rawRule
+	r.parseTags()
 	return r, nil
 }
+
+// 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))
+	for _, tag := range t.Tags {
+		for _, address := range tag.Addresses {
+			t.AddressToTagNames[address] = append(t.AddressToTagNames[address], tag.Name)
+		}
+		t.TagNameToAddresses[tag.Name] = tag.Addresses
+	}
+}
+
+func (t *RouterRule) getAddresses() []string {
+	var result = make([]string, 0, 2*len(t.Tags))
+	for _, tag := range t.Tags {
+		result = append(result, tag.Addresses...)
+	}
+	return result
+}
+
+func (t *RouterRule) getTagNames() []string {
+	var result = make([]string, 0, len(t.Tags))
+	for _, tag := range t.Tags {
+		result = append(result, tag.Name)
+	}
+	return result
+}
+
+func (t *RouterRule) hasTag(tag string) bool {
+	return len(t.TagNameToAddresses[tag]) > 0
+}
+
+func (t *RouterRule) getAddressToTagNames() map[string][]string {
+	return t.AddressToTagNames
+}
+
+func (t *RouterRule) getTagNameToAddresses() map[string][]string {
+	return t.TagNameToAddresses
+}
+
+func (t *RouterRule) getTags() []Tag {
+	return t.Tags
+}
+
+func (t *RouterRule) setTags(tags []Tag) {
+	t.Tags = tags
+}
diff --git a/cluster/router/chan.go b/cluster/router/tag/tag.go
similarity index 87%
rename from cluster/router/chan.go
rename to cluster/router/tag/tag.go
index 6904e1734a7cbdaa00afa1b30797d19ca502453c..468b426a4cfe9f0534ad80a74b2315bdbd85b9c0 100644
--- a/cluster/router/chan.go
+++ b/cluster/router/tag/tag.go
@@ -15,11 +15,9 @@
  * limitations under the License.
  */
 
-package router
+package tag
 
-// Chain
-type Chain interface {
-	router
-	// AddRouters Add routers
-	AddRouters([]PriorityRouter)
+type Tag struct {
+	Name      string
+	Addresses []string
 }
diff --git a/cluster/router/tag/tag_router.go b/cluster/router/tag/tag_router.go
index 87da418943e90c63a905f35260ada7880d6f51b9..9f92e83b210fd2bc04c30769f115601dcb253766 100644
--- a/cluster/router/tag/tag_router.go
+++ b/cluster/router/tag/tag_router.go
@@ -19,24 +19,66 @@ package tag
 
 import (
 	"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"
+	"github.com/apache/dubbo-go/common/logger"
+	"github.com/apache/dubbo-go/config_center"
 	"github.com/apache/dubbo-go/protocol"
+	"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
-	enabled  bool
-	priority int64
+	url           *common.URL
+	tagRouterRule *RouterRule
+	enabled       bool
+	priority      int64
+	application   string
+	ruleChanged   bool
+	mutex         sync.RWMutex
 }
 
+// NewTagRouter returns a tagRouter instance if url is not nil
 func NewTagRouter(url *common.URL) (*tagRouter, error) {
 	if url == nil {
 		return nil, perrors.Errorf("Illegal route URL!")
@@ -48,47 +90,280 @@ func NewTagRouter(url *common.URL) (*tagRouter, error) {
 	}, nil
 }
 
+// nolint
 func (c *tagRouter) isEnabled() bool {
 	return c.enabled
 }
 
-func (c *tagRouter) Route(invokers []protocol.Invoker, url *common.URL, invocation protocol.Invocation) []protocol.Invoker {
-	if !c.isEnabled() {
+// Route gets a list of invoker
+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 len(invokers) == 0 {
+
+	if shouldUseDynamicTag(cache.FindAddrMeta(c)) {
+		return c.routeWithDynamicTag(invokers, cache, url, invocation)
+	}
+	return c.routeWithStaticTag(invokers, cache, url, invocation)
+}
+
+// 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
 	}
-	return filterUsingStaticTag(invokers, url, invocation)
+
+	ret, _ := c.filterWithTag(invokers, cache, staticPrefix+tag)
+	if ret.IsEmpty() && !isForceUseTag(url, invocation) {
+		return invokers
+	}
+
+	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)
+	}
+
+	ret, ok := c.filterWithTag(invokers, cache, dynamicPrefix+tag)
+	if ok && (!ret.IsEmpty() || isTagRuleForce(cache.FindAddrMeta(c))) {
+		return ret
+	}
+
+	// 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
+	}
+	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 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 {
+		c.tagRouterRule = nil
+		return
+	}
+	content, ok := event.Value.(string)
+	if !ok {
+		logger.Errorf("Convert event content fail,raw content:[%s] ", event.Value)
+		return
+	}
+
+	routerRule, err := getRule(content)
+	if err != nil {
+		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
+}
+
+// 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
 }
 
-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)
+// 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
+	}
+
+	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 " +
+			"in this TagRouter is not specified.")
+		return
+	}
+	dynamicConfiguration := config.GetEnvInstance().GetDynamicConfiguration()
+	if dynamicConfiguration == nil {
+		logger.Error("Get dynamicConfiguration fail, dynamicConfiguration is nil, init config center plugin please")
+		return
+	}
+
+	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
+	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)
+		return
+	}
+	if len(rule) > 0 {
+		c.Process(&config_center.ConfigChangeEvent{
+			Key:        routerKey,
+			Value:      rule,
+			ConfigType: remoting.EventTypeUpdate})
+	}
+}
+
+// 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
+}
+
+// Name returns pool's name
+func (c *tagRouter) Name() string {
+	return name
+}
+
+// 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
+	}
+
+	tagNameToAddresses := rule.getTagNameToAddresses()
+	for tag, addrs := range tagNameToAddresses {
+		pool[dynamicPrefix+tag] = addrsToBitmap(addrs, invokers)
+	}
+}
+
+// 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()
+		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)
 		}
-		if len(result) == 0 && !isForceUseTag(url, invocation) {
-			return invokers
-		}
-		return result
 	}
-	return invokers
 }
 
+// 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
 func isForceUseTag(url *common.URL, invocation protocol.Invocation) bool {
 	if b, e := strconv.ParseBool(invocation.AttachmentsByKey(constant.ForceUseTag, url.GetParam(constant.ForceUseTag, "false"))); e == nil {
 		return b
 	}
 	return false
 }
+
+// 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
+		}
+
+		index := findIndexWithIp(addr, invokers)
+		if index != -1 {
+			ret.AddInt(index)
+		}
+	}
+	return ret
+}
+
+// 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 -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 {
+		return consumerUrl.GetParam(constant.Tagkey, "")
+	} else if v, t := tag.(string); t {
+		return v
+	}
+	return ""
+}
diff --git a/cluster/router/tag/tag_router_test.go b/cluster/router/tag/tag_router_test.go
index 000b3ec6724d85590c86456a009d5194c4e71e03..92611ac2c59538ea090763d91cb7c5a6ef44e8ee 100644
--- a/cluster/router/tag/tag_router_test.go
+++ b/cluster/router/tag/tag_router_test.go
@@ -19,25 +19,46 @@ package tag
 
 import (
 	"context"
+	"fmt"
 	"testing"
+	"time"
 )
 
 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/extension"
+	"github.com/apache/dubbo-go/config_center"
+	_ "github.com/apache/dubbo-go/config_center/zookeeper"
 	"github.com/apache/dubbo-go/protocol"
 	"github.com/apache/dubbo-go/protocol/invocation"
+	"github.com/apache/dubbo-go/remoting"
+	"github.com/apache/dubbo-go/remoting/zookeeper"
 )
 
 const (
-	tagRouterTestHangZhouUrl     = "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=&version=2.6.0&enabled=true&dubbo.tag=hangzhou"
-	tagRouterTestShangHaiUrl     = "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=&version=2.6.0&enabled=true&dubbo.tag=shanghai"
-	tagRouterTestBeijingUrl      = "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=&version=2.6.0&enabled=true&dubbo.tag=beijing"
-	tagRouterTestUserConsumer    = "dubbo://127.0.0.1:20000/com.ikurento.user.UserConsumer?interface=com.ikurento.user.UserConsumer&group=&version=2.6.0&enabled=true"
-	tagRouterTestUserConsumerTag = "dubbo://127.0.0.1:20000/com.ikurento.user.UserConsumer?interface=com.ikurento.user.UserConsumer&group=&version=2.6.0&enabled=true&dubbo.force.tag=true"
+	tagRouterTestHangZhouUrl       = "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=&version=2.6.0&enabled=true&dubbo.tag=hangzhou&remote.application=test-tag"
+	tagRouterTestShangHaiUrl       = "dubbo://127.0.0.1:20002/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=&version=2.6.0&enabled=true&dubbo.tag=shanghai&remote.application=test-tag"
+	tagRouterTestBeijingUrl        = "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=&version=2.6.0&enabled=true&dubbo.tag=beijing&remote.application=test-tag"
+	tagRouterTestEnabledBeijingUrl = "dubbo://127.0.0.1:20004/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=&version=2.6.0&enabled=false&dubbo.tag=beijing&remote.application=test-tag"
+	tagRouterTestUserConsumer      = "dubbo://127.0.0.1:20005/com.ikurento.user.UserConsumer?interface=com.ikurento.user.UserConsumer&group=&version=2.6.0&enabled=true&remote.application=test-tag"
+	tagRouterTestUserConsumerTag   = "dubbo://127.0.0.1:20000/com.ikurento.user.UserConsumer?interface=com.ikurento.user.UserConsumer&group=&version=2.6.0&enabled=true&dubbo.force.tag=true&remote.application=test-tag"
+
+	tagRouterTestDynamicIpv4Provider1 = "dubbo://127.0.0.1:20001/com.ikurento.user.UserConsumer?interface=com.ikurento.user.UserConsumer&group=&version=2.6.0&enabled=true&remote.application=test-tag"
+	tagRouterTestDynamicIpv4Provider2 = "dubbo://127.0.0.1:20002/com.ikurento.user.UserConsumer?interface=com.ikurento.user.UserConsumer&group=&version=2.6.0&enabled=true&remote.application=test-tag"
+	tagRouterTestDynamicIpv4Provider3 = "dubbo://127.0.0.1:20003/com.ikurento.user.UserConsumer?interface=com.ikurento.user.UserConsumer&group=&version=2.6.0&enabled=true&remote.application=test-tag"
+	tagRouterTestDynamicIpv4Provider4 = "dubbo://127.0.0.1:20004/com.ikurento.user.UserConsumer?interface=com.ikurento.user.UserConsumer&group=&version=2.6.0&enabled=true&remote.application=test-tag&dubbo.tag=tag4"
+	tagRouterTestDynamicIpv4Provider5 = "dubbo://127.0.0.1:20005/com.ikurento.user.UserConsumer?interface=com.ikurento.user.UserConsumer&group=&version=2.6.0&enabled=true&remote.application=test-tag&dubbo.tag=tag5"
 
 	tagRouterTestDubboTag      = "dubbo.tag"
 	tagRouterTestDubboForceTag = "dubbo.force.tag"
@@ -45,6 +66,15 @@ const (
 	tagRouterTestGuangZhou     = "guangzhou"
 	tagRouterTestFalse         = "false"
 	tagRouterTestTrue          = "true"
+
+	routerPath    = "/dubbo/config/dubbo/test-tag.tag-router"
+	routerLocalIP = "127.0.0.1"
+	routerZk      = "zookeeper"
+)
+
+var (
+	zkFormat        = "zookeeper://%s:%d"
+	conditionFormat = "condition://%s/com.foo.BarService"
 )
 
 // MockInvoker is only mock the Invoker to support test tagRouter
@@ -116,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) {
@@ -148,15 +178,217 @@ 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 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)
+	u5, e5 := common.NewURL(tagRouterTestEnabledBeijingUrl)
+	assert.Nil(t, e2)
+	assert.Nil(t, e3)
+	assert.Nil(t, e4)
+	assert.Nil(t, e5)
+	inv2 := NewMockInvoker(u2)
+	inv3 := NewMockInvoker(u3)
+	inv4 := NewMockInvoker(u4)
+	inv5 := NewMockInvoker(u5)
+	var invokers []protocol.Invoker
+	invokers = append(invokers, inv2, inv3, inv4, inv5)
+
+	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 {
+	suite.Suite
+	rule *RouterRule
+
+	route       *tagRouter
+	zkClient    *zookeeper.ZookeeperClient
+	testCluster *zk.TestCluster
+	invokers    []protocol.Invoker
+	url         *common.URL
+}
+
+func TestDynamicTagRouter(t *testing.T) {
+	dtg := &DynamicTagRouter{}
+	u1, _ := common.NewURL(tagRouterTestDynamicIpv4Provider1)
+	u2, _ := common.NewURL(tagRouterTestDynamicIpv4Provider2)
+	u3, _ := common.NewURL(tagRouterTestDynamicIpv4Provider3)
+	u4, _ := common.NewURL(tagRouterTestDynamicIpv4Provider4)
+	u5, _ := common.NewURL(tagRouterTestDynamicIpv4Provider5)
+	inv1 := NewMockInvoker(u1)
+	inv2 := NewMockInvoker(u2)
+	inv3 := NewMockInvoker(u3)
+	inv4 := NewMockInvoker(u4)
+	inv5 := NewMockInvoker(u5)
+	dtg.invokers = append(dtg.invokers, inv1, inv2, inv3, inv4, inv5)
+	suite.Run(t, dtg)
+}
+
+func (suite *DynamicTagRouter) SetupTest() {
+	var err error
+	testYML := `enabled: true
+scope: application
+force: true
+runtime: false
+valid: true
+priority: 1
+key: demo-provider
+tags:
+  - name: tag1
+    addresses: ["127.0.0.1:20001"]
+  - name: tag2
+    addresses: ["127.0.0.1:20002"]
+  - name: tag3
+    addresses: ["127.0.0.1:20003", "127.0.0.1:20004"]
+`
+	ts, z, _, err := zookeeper.NewMockZookeeperClient("test", 15*time.Second)
+	suite.NoError(err)
+	err = z.Create(routerPath)
+	suite.NoError(err)
+
+	suite.zkClient = z
+	suite.testCluster = ts
+
+	_, err = z.Conn.Set(routerPath, []byte(testYML), 0)
+	suite.NoError(err)
+
+	zkUrl, _ := common.NewURL(fmt.Sprintf(zkFormat, routerLocalIP, suite.testCluster.Servers[0].Port))
+	configuration, err := extension.GetConfigCenterFactory(routerZk).GetDynamicConfiguration(&zkUrl)
+	config.GetEnvInstance().SetDynamicConfiguration(configuration)
+
+	suite.Nil(err)
+	suite.NotNil(configuration)
+
+	url, e1 := common.NewURL(tagRouterTestUserConsumerTag)
+	suite.Nil(e1)
+
+	tagRouter, err := NewTagRouter(&url)
+	suite.Nil(err)
+	suite.NotNil(tagRouter)
+	suite.route = tagRouter
+	suite.url = &url
+}
+
+func (suite *DynamicTagRouter) TearDownTest() {
+	suite.zkClient.Close()
+	suite.testCluster.Stop()
+}
+
+func (suite *DynamicTagRouter) TestDynamicTagRouterSetByIPv4() {
+	invokers := suite.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(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(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(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
+	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(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(rb, cache, suite.url, consumer)
+	suite.Equal(uint64(1), targetInvokers.GetCardinality())
+	suite.Equal(targetInvokers.ToArray()[0], uint32(4))
+}
+
+func TestProcess(t *testing.T) {
+	u1, err := common.NewURL(tagRouterTestUserConsumerTag)
+	assert.Nil(t, err)
+	tagRouter, e := NewTagRouter(&u1)
+	assert.Nil(t, e)
+	assert.NotNil(t, tagRouter)
+
+	testYML := `
+scope: application
+force: true
+runtime: false
+enabled: true
+valid: true
+priority: 1
+key: demo-provider
+tags:
+  - name: beijing
+    addresses: [192.168.1.1, 192.168.1.2]
+  - name: hangzhou
+    addresses: [192.168.1.3, 192.168.1.4]
+`
+	tagRouter.Process(&config_center.ConfigChangeEvent{Value: testYML, ConfigType: remoting.EventTypeAdd})
+	assert.NotNil(t, tagRouter.tagRouterRule)
+	assert.Equal(t, []string{"beijing", "hangzhou"}, tagRouter.tagRouterRule.getTagNames())
+	assert.Equal(t, []string{"192.168.1.1", "192.168.1.2", "192.168.1.3", "192.168.1.4"}, tagRouter.tagRouterRule.getAddresses())
+	assert.Equal(t, []string{"192.168.1.3", "192.168.1.4"}, tagRouter.tagRouterRule.getTagNameToAddresses()["hangzhou"])
+	assert.Equal(t, []string{"beijing"}, tagRouter.tagRouterRule.getAddressToTagNames()["192.168.1.1"])
+	tagRouter.Process(&config_center.ConfigChangeEvent{ConfigType: remoting.EventTypeDel})
+	assert.Nil(t, tagRouter.tagRouterRule)
 }
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/constant/cluster.go b/common/constant/cluster.go
new file mode 100644
index 0000000000000000000000000000000000000000..6894f3595ea8dfdc83f0ce372bb7f22a47e3e434
--- /dev/null
+++ b/common/constant/cluster.go
@@ -0,0 +1,24 @@
+/*
+ * 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 constant
+
+// nolint
+const (
+	FAILOVER_CLUSTER_NAME  = "failover"
+	ZONEAWARE_CLUSTER_NAME = "zoneAware"
+)
diff --git a/common/constant/default.go b/common/constant/default.go
index c1c404e089ea90899d2b599b01cd5980c3e92ab1..629aa32392a0151046eaaea67287618eae02158d 100644
--- a/common/constant/default.go
+++ b/common/constant/default.go
@@ -37,6 +37,7 @@ const (
 	DEFAULT_RETRIES_INT        = 2
 	DEFAULT_PROTOCOL           = "dubbo"
 	DEFAULT_REG_TIMEOUT        = "10s"
+	DEFAULT_REG_TTL            = "15m"
 	DEFAULT_CLUSTER            = "failover"
 	DEFAULT_FAILBACK_TIMES     = "3"
 	DEFAULT_FAILBACK_TIMES_INT = 3
diff --git a/common/constant/env.go b/common/constant/env.go
index 5376323328f431083a47395c9e2ebbab5b37f307..f8402b9cb8649f9471d8ae6f0d26314053496bb7 100644
--- a/common/constant/env.go
+++ b/common/constant/env.go
@@ -17,6 +17,7 @@
 
 package constant
 
+// nolint
 const (
 	// CONF_CONSUMER_FILE_PATH ...
 	CONF_CONSUMER_FILE_PATH = "CONF_CONSUMER_FILE_PATH"
diff --git a/common/constant/key.go b/common/constant/key.go
index cd23dd0f1ad3e000fe54c251d9563bcf12ba86c7..7c45a1397d8767510f1f8b92f4e82f0ece05c810 100644
--- a/common/constant/key.go
+++ b/common/constant/key.go
@@ -46,6 +46,7 @@ const (
 	DUBBO_KEY              = "dubbo"
 	RELEASE_KEY            = "release"
 	ANYHOST_KEY            = "anyhost"
+	SSL_ENABLED_KEY        = "ssl-enabled"
 )
 
 const (
@@ -97,6 +98,11 @@ const (
 	ROLE_KEY             = "registry.role"
 	REGISTRY_DEFAULT_KEY = "registry.default"
 	REGISTRY_TIMEOUT_KEY = "registry.timeout"
+	REGISTRY_LABEL_KEY   = "label"
+	PREFERRED_KEY        = "preferred"
+	ZONE_KEY             = "zone"
+	ZONE_FORCE_KEY       = "zone.force"
+	REGISTRY_TTL_KEY     = "registry.ttl"
 )
 
 const (
@@ -187,6 +193,9 @@ const (
 	HealthCheckRouterName = "health_check"
 	// TagRouterName Specify the name of TagRouter
 	TagRouterName = "tag"
+	// TagRouterRuleSuffix Specify tag router suffix
+	TagRouterRuleSuffix  = ".tag-router"
+	RemoteApplicationKey = "remote.application"
 	// ConditionRouterRuleSuffix Specify condition router suffix
 	ConditionRouterRuleSuffix = ".condition-router"
 
@@ -196,7 +205,14 @@ const (
 	RouterEnabled = "enabled"
 	// Priority Priority key in router module
 	RouterPriority = "priority"
-
+	// RouterScope Scope key in router module
+	RouterScope = "scope"
+	// RouterApplicationScope Scope key in router module
+	RouterApplicationScope = "application"
+	// RouterServiceScope Scope key in router module
+	RouterServiceScope = "service"
+	// RouterRuleKey defines the key of the router, service's/application's name
+	RouterRuleKey = "key"
 	// ForceUseTag is the tag in attachment
 	ForceUseTag = "dubbo.force.tag"
 	Tagkey      = "dubbo.tag"
diff --git a/common/extension/health_checker.go b/common/extension/health_checker.go
index 8def727614dad8393eeef9ced5e30a056fa65461..cec4c2defc291c617a0549c3296e07851b2ec128 100644
--- a/common/extension/health_checker.go
+++ b/common/extension/health_checker.go
@@ -26,8 +26,8 @@ var (
 	healthCheckers = make(map[string]func(url *common.URL) router.HealthChecker)
 )
 
-// SethealthChecker sets the HealthChecker with @name
-func SethealthChecker(name string, fcn func(_ *common.URL) router.HealthChecker) {
+// SetHealthChecker sets the HealthChecker with @name
+func SetHealthChecker(name string, fcn func(_ *common.URL) router.HealthChecker) {
 	healthCheckers[name] = fcn
 }
 
diff --git a/common/extension/health_checker_test.go b/common/extension/health_checker_test.go
index 4e83a6f6e1ed8a57b6e6374377d08eabfb56c604..af6b114a612a465d4397be7a599ddfc9ff7edab9 100644
--- a/common/extension/health_checker_test.go
+++ b/common/extension/health_checker_test.go
@@ -32,7 +32,7 @@ import (
 )
 
 func TestGetHealthChecker(t *testing.T) {
-	SethealthChecker("mock", newMockhealthCheck)
+	SetHealthChecker("mock", newMockHealthCheck)
 	checker := GetHealthChecker("mock", common.NewURLWithOptions())
 	assert.NotNil(t, checker)
 }
@@ -44,6 +44,6 @@ func (m mockHealthChecker) IsHealthy(invoker protocol.Invoker) bool {
 	return true
 }
 
-func newMockhealthCheck(_ *common.URL) router.HealthChecker {
+func newMockHealthCheck(_ *common.URL) router.HealthChecker {
 	return &mockHealthChecker{}
 }
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/log.yml b/common/logger/log.yml
index 59fa4279ad85272c4c49d532beaf23b74d00f58a..21f97bcbc40218ab23a6fc022b148d321cbe23cc 100644
--- a/common/logger/log.yml
+++ b/common/logger/log.yml
@@ -1,6 +1,5 @@
-
 level: "debug"
-development: true
+development: false
 disableCaller: false
 disableStacktrace: false
 sampling:
diff --git a/common/logger/logger.go b/common/logger/logger.go
index 9bc6a461003d086e8951ebac3d6997774ac69b90..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 {
@@ -100,7 +104,6 @@ func InitLog(logConfFile string) error {
 func InitLogger(conf *zap.Config) {
 	var zapLoggerConfig zap.Config
 	if conf == nil {
-		zapLoggerConfig = zap.NewDevelopmentConfig()
 		zapLoggerEncoderConfig := zapcore.EncoderConfig{
 			TimeKey:        "time",
 			LevelKey:       "level",
@@ -113,12 +116,18 @@ func InitLogger(conf *zap.Config) {
 			EncodeDuration: zapcore.SecondsDurationEncoder,
 			EncodeCaller:   zapcore.ShortCallerEncoder,
 		}
-		zapLoggerConfig.EncoderConfig = zapLoggerEncoderConfig
+		zapLoggerConfig = zap.Config{
+			Level:            zap.NewAtomicLevelAt(zap.DebugLevel),
+			Development:      false,
+			Encoding:         "console",
+			EncoderConfig:    zapLoggerEncoderConfig,
+			OutputPaths:      []string{"stderr"},
+			ErrorOutputPaths: []string{"stderr"},
+		}
 	} else {
 		zapLoggerConfig = *conf
 	}
 	zapLogger, _ := zapLoggerConfig.Build(zap.AddCallerSkip(1))
-	//logger = zapLogger.Sugar()
 	logger = &DubboLogger{Logger: zapLogger.Sugar(), dynamicLevel: zapLoggerConfig.Level}
 
 	// set getty log
diff --git a/common/proxy/proxy.go b/common/proxy/proxy.go
index abcf87cd9d297769bf8aff6fa07d6a4659091eb6..d51ce1cc1bf40a8ad25804c797eeed3b88e7d132 100644
--- a/common/proxy/proxy.go
+++ b/common/proxy/proxy.go
@@ -23,6 +23,11 @@ import (
 	"sync"
 )
 
+import (
+	"github.com/apache/dubbo-go-hessian2/java_exception"
+	perrors "github.com/pkg/errors"
+)
+
 import (
 	"github.com/apache/dubbo-go/common"
 	"github.com/apache/dubbo-go/common/constant"
@@ -140,12 +145,17 @@ func (p *Proxy) Implement(v common.RPCService) {
 				inv.SetAttachments(k, value)
 			}
 
-			// add user setAttachment
+			// add user setAttachment.  It is compatibility with previous versions.
 			atm := invCtx.Value(constant.AttachmentKey)
 			if m, ok := atm.(map[string]string); ok {
 				for k, value := range m {
 					inv.SetAttachments(k, value)
 				}
+			} else if m2, ok2 := atm.(map[string]interface{}); ok2 {
+				// it is support to transfer map[string]interface{}. It refers to dubbo-java 2.7.
+				for k, value := range m2 {
+					inv.SetAttachments(k, value)
+				}
 			}
 
 			result := p.invoke.Invoke(invCtx, inv)
@@ -154,7 +164,18 @@ func (p *Proxy) Implement(v common.RPCService) {
 			}
 
 			err = result.Error()
-			logger.Debugf("[makeDubboCallProxy] result: %v, err: %v", result.Result(), err)
+			if err != nil {
+				// the cause reason
+				err = perrors.Cause(err)
+				// if some error happened, it should be log some info in the seperate file.
+				if throwabler, ok := err.(java_exception.Throwabler); ok {
+					logger.Warnf("invoke service throw exception: %v , stackTraceElements: %v", err.Error(), throwabler.GetStackTrace())
+				} else {
+					logger.Warnf("result err: %v", err)
+				}
+			} else {
+				logger.Debugf("[makeDubboCallProxy] result: %v, err: %v", result.Result(), err)
+			}
 			if len(outs) == 1 {
 				return []reflect.Value{reflect.ValueOf(&err).Elem()}
 			}
diff --git a/common/proxy/proxy_test.go b/common/proxy/proxy_test.go
index 14b2befbc47242d9cc9a2f88e9070b84828062c0..c6066157fd8d2147fd3a319c8d48fdd910752711 100644
--- a/common/proxy/proxy_test.go
+++ b/common/proxy/proxy_test.go
@@ -24,6 +24,7 @@ import (
 )
 
 import (
+	"github.com/apache/dubbo-go/protocol/invocation"
 	perrors "github.com/pkg/errors"
 	"github.com/stretchr/testify/assert"
 )
@@ -32,6 +33,7 @@ import (
 	"github.com/apache/dubbo-go/common"
 	"github.com/apache/dubbo-go/common/constant"
 	"github.com/apache/dubbo-go/protocol"
+	"github.com/apache/dubbo-go/protocol/dubbo/hessian2"
 )
 
 type TestService struct {
@@ -40,6 +42,7 @@ type TestService struct {
 	MethodThree func(int, bool) (interface{}, error)
 	MethodFour  func(int, bool) (*interface{}, error) `dubbo:"methodFour"`
 	MethodFive  func() error
+	MethodSix   func(context.Context, string) (interface{}, error)
 	Echo        func(interface{}, *interface{}) error
 }
 
@@ -120,3 +123,34 @@ func TestProxyImplement(t *testing.T) {
 	assert.Nil(t, s3.MethodOne)
 
 }
+
+func TestProxyImplementForContext(t *testing.T) {
+	invoker := &TestProxyInvoker{
+		BaseInvoker: *protocol.NewBaseInvoker(common.URL{}),
+	}
+	p := NewProxy(invoker, nil, map[string]string{constant.ASYNC_KEY: "false"})
+	s := &TestService{}
+	p.Implement(s)
+	attahments1 := make(map[string]interface{}, 4)
+	attahments1["k1"] = "v1"
+	attahments1["k2"] = "v2"
+	context := context.WithValue(context.Background(), constant.AttachmentKey, attahments1)
+	r, err := p.Get().(*TestService).MethodSix(context, "xxx")
+	v1 := r.(map[string]interface{})
+	assert.NoError(t, err)
+	assert.Equal(t, v1["TestProxyInvoker"], "TestProxyInvokerValue")
+}
+
+type TestProxyInvoker struct {
+	protocol.BaseInvoker
+}
+
+func (bi *TestProxyInvoker) Invoke(context context.Context, inv protocol.Invocation) protocol.Result {
+	rpcInv := inv.(*invocation.RPCInvocation)
+	mapV := inv.Attachments()
+	mapV["TestProxyInvoker"] = "TestProxyInvokerValue"
+	hessian2.ReflectResponse(mapV, rpcInv.Reply())
+	return &protocol.RPCResult{
+		Rest: inv.Arguments(),
+	}
+}
diff --git a/common/rpc_service.go b/common/rpc_service.go
index 9ef2b956aa955f4fc79c6f75bd060ccfee2d02ca..5ed4df6d717db31021d8585a88d5576f59756f8a 100644
--- a/common/rpc_service.go
+++ b/common/rpc_service.go
@@ -169,7 +169,7 @@ func (sm *serviceMap) GetService(protocol, name string) *Service {
 	return nil
 }
 
-// GetInterface gets an interface defination by interface name
+// GetInterface gets an interface definition by interface name
 func (sm *serviceMap) GetInterface(interfaceName string) []*Service {
 	sm.mutex.RLock()
 	defer sm.mutex.RUnlock()
@@ -271,7 +271,7 @@ func (sm *serviceMap) UnRegister(interfaceName, protocol, serviceId string) erro
 	sm.mutex.Lock()
 	defer sm.mutex.Unlock()
 	sm.interfaceMap[interfaceName] = make([]*Service, 0, len(svrs))
-	for i, _ := range svrs {
+	for i := range svrs {
 		if i != index {
 			sm.interfaceMap[interfaceName] = append(sm.interfaceMap[interfaceName], svrs[i])
 		}
diff --git a/common/url.go b/common/url.go
index 807d0ed5eff4ecb70d3adeb8524b841d0ec92a58..e5fa895adb80fb327c712d5d95d2479cc897f970 100644
--- a/common/url.go
+++ b/common/url.go
@@ -222,7 +222,7 @@ func NewURL(urlString string, opts ...option) (URL, error) {
 	}
 
 	// rawUrlString = "//" + rawUrlString
-	if strings.Index(rawUrlString, "//") < 0 {
+	if !strings.Contains(rawUrlString, "//") {
 		t := URL{baseUrl: baseUrl{}}
 		for _, opt := range opts {
 			opt(&t)
@@ -381,7 +381,7 @@ func (c URL) Service() string {
 	if service != "" {
 		return service
 	} else if c.SubURL != nil {
-		service = c.GetParam(constant.INTERFACE_KEY, strings.TrimPrefix(c.Path, "/"))
+		service = c.SubURL.GetParam(constant.INTERFACE_KEY, strings.TrimPrefix(c.Path, "/"))
 		if service != "" { // if url.path is "" then return suburl's path, special for registry url
 			return service
 		}
@@ -643,6 +643,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/common/yaml/yaml.go b/common/yaml/yaml.go
index 5edda1b3c7751e8171528d121148b6c3c60fe128..d7e1ca4e898ce64f316b2abf8cb9e3324eb31e32 100644
--- a/common/yaml/yaml.go
+++ b/common/yaml/yaml.go
@@ -27,7 +27,7 @@ import (
 	"gopkg.in/yaml.v2"
 )
 
-// loadYMLConfig Load yml config byte from file
+// LoadYMLConfig Load yml config byte from file
 func LoadYMLConfig(confProFile string) ([]byte, error) {
 	if len(confProFile) == 0 {
 		return nil, perrors.Errorf("application configure(provider) file name is nil")
@@ -40,7 +40,7 @@ func LoadYMLConfig(confProFile string) ([]byte, error) {
 	return ioutil.ReadFile(confProFile)
 }
 
-// unmarshalYMLConfig Load yml config byte from file, then unmarshal to object
+// UnmarshalYMLConfig Load yml config byte from file, then unmarshal to object
 func UnmarshalYMLConfig(confProFile string, out interface{}) ([]byte, error) {
 	confFileStream, err := LoadYMLConfig(confProFile)
 	if err != nil {
@@ -49,6 +49,12 @@ func UnmarshalYMLConfig(confProFile string, out interface{}) ([]byte, error) {
 	return confFileStream, yaml.Unmarshal(confFileStream, out)
 }
 
+//UnmarshalYML unmarshals decodes the first document found within the in byte slice and assigns decoded values into the out value.
 func UnmarshalYML(data []byte, out interface{}) error {
 	return yaml.Unmarshal(data, out)
 }
+
+// MarshalYML serializes the value provided into a YAML document.
+func MarshalYML(in interface{}) ([]byte, error) {
+	return yaml.Marshal(in)
+}
diff --git a/config/base_config.go b/config/base_config.go
index 0ba5bc7ef98cb30a13890b93a659c467adcbf73b..336bb03c7b61ad8aad8465bb3c7754abeb9e9f5a 100644
--- a/config/base_config.go
+++ b/config/base_config.go
@@ -29,11 +29,8 @@ import (
 )
 
 import (
-	"github.com/apache/dubbo-go/common"
 	"github.com/apache/dubbo-go/common/config"
-	"github.com/apache/dubbo-go/common/extension"
 	"github.com/apache/dubbo-go/common/logger"
-	"github.com/apache/dubbo-go/config_center"
 )
 
 type multiConfiger interface {
@@ -52,7 +49,6 @@ type BaseConfig struct {
 	// application config
 	ApplicationConfig *ApplicationConfig `yaml:"application" json:"application,omitempty" property:"application"`
 
-	configCenterUrl     *common.URL
 	prefix              string
 	fatherConfig        interface{}
 	EventDispatcherType string        `default:"direct" yaml:"event_dispatcher_type" json:"event_dispatcher_type,omitempty"`
@@ -72,74 +68,6 @@ func (c *BaseConfig) GetRemoteConfig(name string) (config *RemoteConfig, ok bool
 	return
 }
 
-// startConfigCenter will start the config center.
-// it will prepare the environment
-func (c *BaseConfig) startConfigCenter() error {
-	url, err := common.NewURL(c.ConfigCenterConfig.Address,
-		common.WithProtocol(c.ConfigCenterConfig.Protocol), common.WithParams(c.ConfigCenterConfig.GetUrlMap()))
-	if err != nil {
-		return err
-	}
-	c.configCenterUrl = &url
-	if c.prepareEnvironment() != nil {
-		return perrors.WithMessagef(err, "start config center error!")
-	}
-	// c.fresh()
-	return err
-}
-
-func (c *BaseConfig) prepareEnvironment() error {
-	factory := extension.GetConfigCenterFactory(c.ConfigCenterConfig.Protocol)
-	dynamicConfig, err := factory.GetDynamicConfiguration(c.configCenterUrl)
-	config.GetEnvInstance().SetDynamicConfiguration(dynamicConfig)
-	if err != nil {
-		logger.Errorf("Get dynamic configuration error , error message is %v", err)
-		return perrors.WithStack(err)
-	}
-	content, err := dynamicConfig.GetProperties(c.ConfigCenterConfig.ConfigFile, config_center.WithGroup(c.ConfigCenterConfig.Group))
-	if err != nil {
-		logger.Errorf("Get config content in dynamic configuration error , error message is %v", err)
-		return perrors.WithStack(err)
-	}
-	var appGroup string
-	var appContent string
-	if providerConfig != nil && providerConfig.ApplicationConfig != nil &&
-		reflect.ValueOf(c.fatherConfig).Elem().Type().Name() == "ProviderConfig" {
-		appGroup = providerConfig.ApplicationConfig.Name
-	} else if consumerConfig != nil && consumerConfig.ApplicationConfig != nil &&
-		reflect.ValueOf(c.fatherConfig).Elem().Type().Name() == "ConsumerConfig" {
-		appGroup = consumerConfig.ApplicationConfig.Name
-	}
-
-	if len(appGroup) != 0 {
-		configFile := c.ConfigCenterConfig.AppConfigFile
-		if len(configFile) == 0 {
-			configFile = c.ConfigCenterConfig.ConfigFile
-		}
-		appContent, err = dynamicConfig.GetProperties(configFile, config_center.WithGroup(appGroup))
-		if err != nil {
-			return perrors.WithStack(err)
-		}
-	}
-	// global config file
-	mapContent, err := dynamicConfig.Parser().Parse(content)
-	if err != nil {
-		return perrors.WithStack(err)
-	}
-	config.GetEnvInstance().UpdateExternalConfigMap(mapContent)
-
-	// appGroup config file
-	if len(appContent) != 0 {
-		appMapConent, err := dynamicConfig.Parser().Parse(appContent)
-		if err != nil {
-			return perrors.WithStack(err)
-		}
-		config.GetEnvInstance().UpdateAppExternalConfigMap(appMapConent)
-	}
-
-	return nil
-}
-
 func getKeyPrefix(val reflect.Value) []string {
 	var (
 		prefix string
@@ -150,15 +78,8 @@ func getKeyPrefix(val reflect.Value) []string {
 	} else {
 		prefix = val.MethodByName(configPrefixMethod).Call(nil)[0].String()
 	}
-	var retPrefixes []string
-
-	for _, pfx := range strings.Split(prefix, "|") {
-
-		retPrefixes = append(retPrefixes, pfx)
-
-	}
-	return retPrefixes
 
+	return strings.Split(prefix, "|")
 }
 
 func getPtrElement(v reflect.Value) reflect.Value {
@@ -288,12 +209,9 @@ func setFieldValue(val reflect.Value, id reflect.Value, config *config.InmemoryC
 						prefix := s.MethodByName("Prefix").Call(nil)[0].String()
 						for _, pfx := range strings.Split(prefix, "|") {
 							m := config.GetSubProperty(pfx)
-							if m != nil {
-								for k := range m {
-									f.SetMapIndex(reflect.ValueOf(k), reflect.New(f.Type().Elem().Elem()))
-								}
+							for k := range m {
+								f.SetMapIndex(reflect.ValueOf(k), reflect.New(f.Type().Elem().Elem()))
 							}
-
 						}
 
 					}
diff --git a/config/base_config_test.go b/config/base_config_test.go
index 15b468753ddfd99e77b3d99c0994c0599c649793..849a9c4586c0c8cd2d74e3dd1011aaab466f0e93 100644
--- a/config/base_config_test.go
+++ b/config/base_config_test.go
@@ -28,8 +28,6 @@ import (
 
 import (
 	"github.com/apache/dubbo-go/common/config"
-	"github.com/apache/dubbo-go/common/extension"
-	"github.com/apache/dubbo-go/config_center"
 	_ "github.com/apache/dubbo-go/config_center/apollo"
 )
 
@@ -95,14 +93,14 @@ var baseMockRef = map[string]*ReferenceConfig{
 				InterfaceName: "com.MockService",
 				Name:          "GetUser",
 				Retries:       "2",
-				Loadbalance:   "random",
+				LoadBalance:   "random",
 			},
 			{
 				InterfaceId:   "MockService",
 				InterfaceName: "com.MockService",
 				Name:          "GetUser1",
 				Retries:       "2",
-				Loadbalance:   "random",
+				LoadBalance:   "random",
 			},
 		},
 	},
@@ -258,14 +256,14 @@ func TestRefreshProvider(t *testing.T) {
 						InterfaceName: "com.MockService",
 						Name:          "GetUser",
 						Retries:       "2",
-						Loadbalance:   "random",
+						LoadBalance:   "random",
 					},
 					{
 						InterfaceId:   "MockService",
 						InterfaceName: "com.MockService",
 						Name:          "GetUser1",
 						Retries:       "2",
-						Loadbalance:   "random",
+						LoadBalance:   "random",
 					},
 				},
 			},
@@ -282,23 +280,6 @@ func TestRefreshProvider(t *testing.T) {
 	assert.Equal(t, "20001", father.Protocols["jsonrpc1"].Port)
 }
 
-func TestStartConfigCenter(t *testing.T) {
-	extension.SetConfigCenterFactory("mock", func() config_center.DynamicConfigurationFactory {
-		return &config_center.MockDynamicConfigurationFactory{}
-	})
-	c := &BaseConfig{ConfigCenterConfig: &ConfigCenterConfig{
-		Protocol:   "mock",
-		Address:    "172.0.0.1",
-		Group:      "dubbo",
-		ConfigFile: "mockDubbo.properties",
-	}}
-	err := c.startConfigCenter()
-	assert.NoError(t, err)
-	b, v := config.GetEnvInstance().Configuration().Back().Value.(*config.InmemoryConfiguration).GetProperty("dubbo.application.organization")
-	assert.True(t, b)
-	assert.Equal(t, "ikurento.com", v)
-}
-
 func TestInitializeStruct(t *testing.T) {
 	testConsumerConfig := &ConsumerConfig{}
 	tp := reflect.TypeOf(ConsumerConfig{})
diff --git a/config/config_center_config.go b/config/config_center_config.go
index c9133dc26df0b05e3bb61df0f612d0e2914e98bb..0fc4007940d9b1ac2456c9b2d379493bb5d8edb0 100644
--- a/config/config_center_config.go
+++ b/config/config_center_config.go
@@ -20,6 +20,7 @@ package config
 import (
 	"context"
 	"net/url"
+	"reflect"
 	"time"
 )
 
@@ -28,7 +29,13 @@ import (
 )
 
 import (
+	"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/common/logger"
+	"github.com/apache/dubbo-go/config_center"
+	perrors "github.com/pkg/errors"
 )
 
 // ConfigCenterConfig is configuration for config center
@@ -52,6 +59,7 @@ type ConfigCenterConfig struct {
 	AppConfigFile string `default:"dubbo.properties" yaml:"app_config_file"  json:"app_config_file,omitempty"`
 	AppId         string `default:"dubbo" yaml:"app_id"  json:"app_id,omitempty"`
 	TimeoutStr    string `yaml:"timeout"  json:"timeout,omitempty"`
+	RemoteRef     string `required:"false"  yaml:"remote_ref"  json:"remote_ref,omitempty"`
 	timeout       time.Duration
 }
 
@@ -77,3 +85,94 @@ func (c *ConfigCenterConfig) GetUrlMap() url.Values {
 	urlMap.Set(constant.CONFIG_LOG_DIR_KEY, c.LogDir)
 	return urlMap
 }
+
+type configCenter struct {
+}
+
+// toURL will compatible with baseConfig.ConfigCenterConfig.Address and baseConfig.ConfigCenterConfig.RemoteRef before 1.6.0
+// After 1.6.0 will not compatible, only baseConfig.ConfigCenterConfig.RemoteRef
+func (b *configCenter) toURL(baseConfig BaseConfig) (common.URL, error) {
+	if len(baseConfig.ConfigCenterConfig.Address) > 0 {
+		return common.NewURL(baseConfig.ConfigCenterConfig.Address,
+			common.WithProtocol(baseConfig.ConfigCenterConfig.Protocol), common.WithParams(baseConfig.ConfigCenterConfig.GetUrlMap()))
+	}
+
+	remoteRef := baseConfig.ConfigCenterConfig.RemoteRef
+	rc, ok := baseConfig.GetRemoteConfig(remoteRef)
+
+	if !ok {
+		return common.URL{}, perrors.New("Could not find out the remote ref config, name: " + remoteRef)
+	}
+
+	newURL, err := rc.toURL()
+	if err == nil {
+		newURL.SetParams(baseConfig.ConfigCenterConfig.GetUrlMap())
+	}
+	return newURL, err
+}
+
+// startConfigCenter will start the config center.
+// it will prepare the environment
+func (b *configCenter) startConfigCenter(baseConfig BaseConfig) error {
+	url, err := b.toURL(baseConfig)
+	if err != nil {
+		return err
+	}
+	if err = b.prepareEnvironment(baseConfig, &url); err != nil {
+		return perrors.WithMessagef(err, "start config center error!")
+	}
+	// c.fresh()
+	return nil
+}
+
+func (b *configCenter) prepareEnvironment(baseConfig BaseConfig, configCenterUrl *common.URL) error {
+	factory := extension.GetConfigCenterFactory(configCenterUrl.Protocol)
+	dynamicConfig, err := factory.GetDynamicConfiguration(configCenterUrl)
+	if err != nil {
+		logger.Errorf("Get dynamic configuration error , error message is %v", err)
+		return perrors.WithStack(err)
+	}
+	config.GetEnvInstance().SetDynamicConfiguration(dynamicConfig)
+	content, err := dynamicConfig.GetProperties(baseConfig.ConfigCenterConfig.ConfigFile, config_center.WithGroup(baseConfig.ConfigCenterConfig.Group))
+	if err != nil {
+		logger.Errorf("Get config content in dynamic configuration error , error message is %v", err)
+		return perrors.WithStack(err)
+	}
+	var appGroup string
+	var appContent string
+	if providerConfig != nil && providerConfig.ApplicationConfig != nil &&
+		reflect.ValueOf(baseConfig.fatherConfig).Elem().Type().Name() == "ProviderConfig" {
+		appGroup = providerConfig.ApplicationConfig.Name
+	} else if consumerConfig != nil && consumerConfig.ApplicationConfig != nil &&
+		reflect.ValueOf(baseConfig.fatherConfig).Elem().Type().Name() == "ConsumerConfig" {
+		appGroup = consumerConfig.ApplicationConfig.Name
+	}
+
+	if len(appGroup) != 0 {
+		configFile := baseConfig.ConfigCenterConfig.AppConfigFile
+		if len(configFile) == 0 {
+			configFile = baseConfig.ConfigCenterConfig.ConfigFile
+		}
+		appContent, err = dynamicConfig.GetProperties(configFile, config_center.WithGroup(appGroup))
+		if err != nil {
+			return perrors.WithStack(err)
+		}
+	}
+	// global config file
+	mapContent, err := dynamicConfig.Parser().Parse(content)
+	if err != nil {
+		return perrors.WithStack(err)
+	}
+	config.GetEnvInstance().UpdateExternalConfigMap(mapContent)
+
+	// appGroup config file
+	if len(appContent) != 0 {
+		appMapConent, err := dynamicConfig.Parser().Parse(appContent)
+		if err != nil {
+			return perrors.WithStack(err)
+		}
+		config.GetEnvInstance().UpdateAppExternalConfigMap(appMapConent)
+	}
+
+	return nil
+}
diff --git a/config/config_center_config_test.go b/config/config_center_config_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..2299167bb62a019f6854ea48d61442f5bde7e646
--- /dev/null
+++ b/config/config_center_config_test.go
@@ -0,0 +1,93 @@
+/*
+ * 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 config
+
+import (
+	"testing"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+import (
+	"github.com/apache/dubbo-go/common/config"
+	"github.com/apache/dubbo-go/common/extension"
+	"github.com/apache/dubbo-go/config_center"
+)
+
+func TestStartConfigCenter(t *testing.T) {
+	extension.SetConfigCenterFactory("mock", func() config_center.DynamicConfigurationFactory {
+		return &config_center.MockDynamicConfigurationFactory{}
+	})
+	baseConfig := &BaseConfig{ConfigCenterConfig: &ConfigCenterConfig{
+		Protocol:   "mock",
+		Address:    "172.0.0.1",
+		Group:      "dubbo",
+		ConfigFile: "mockDubbo.properties",
+	}}
+
+	c := &configCenter{}
+	err := c.startConfigCenter(*baseConfig)
+	assert.NoError(t, err)
+	b, v := config.GetEnvInstance().Configuration().Back().Value.(*config.InmemoryConfiguration).GetProperty("dubbo.application.organization")
+	assert.True(t, b)
+	assert.Equal(t, "ikurento.com", v)
+}
+
+func TestStartConfigCenterWithRemoteRef(t *testing.T) {
+	extension.SetConfigCenterFactory("mock", func() config_center.DynamicConfigurationFactory {
+		return &config_center.MockDynamicConfigurationFactory{}
+	})
+	m := make(map[string]*RemoteConfig)
+	m["mock"] = &RemoteConfig{Protocol: "mock", Address: "172.0.0.1"}
+	baseConfig := &BaseConfig{
+		Remotes: m,
+		ConfigCenterConfig: &ConfigCenterConfig{
+			Group:      "dubbo",
+			RemoteRef:  "mock",
+			ConfigFile: "mockDubbo.properties",
+		}}
+
+	c := &configCenter{}
+	err := c.startConfigCenter(*baseConfig)
+	assert.NoError(t, err)
+	b, v := config.GetEnvInstance().Configuration().Back().Value.(*config.InmemoryConfiguration).GetProperty("dubbo.application.organization")
+	assert.True(t, b)
+	assert.Equal(t, "ikurento.com", v)
+}
+
+func TestStartConfigCenterWithRemoteRefError(t *testing.T) {
+	extension.SetConfigCenterFactory("mock", func() config_center.DynamicConfigurationFactory {
+		return &config_center.MockDynamicConfigurationFactory{}
+	})
+	m := make(map[string]*RemoteConfig)
+	m["mock"] = &RemoteConfig{Address: "172.0.0.1"}
+	baseConfig := &BaseConfig{
+		Remotes: m,
+		ConfigCenterConfig: &ConfigCenterConfig{
+			Protocol:   "mock",
+			Group:      "dubbo",
+			RemoteRef:  "mock",
+			ConfigFile: "mockDubbo.properties",
+		}}
+
+	c := &configCenter{}
+	err := c.startConfigCenter(*baseConfig)
+	assert.Error(t, err)
+}
diff --git a/config/config_loader.go b/config/config_loader.go
index d5f8c68c1bf35c40c09d7d15bae4b6b9f161e9e7..c66e526921e7e5ab017105e2f4ea2baa62563205 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 (
@@ -42,6 +46,7 @@ var (
 	providerConfig *ProviderConfig
 	// baseConfig = providerConfig.BaseConfig or consumerConfig
 	baseConfig *BaseConfig
+	sslEnabled = false
 
 	// configAccessMutex is used to make sure that xxxxConfig will only be created once if needed.
 	// it should be used combine with double-check to avoid the race condition
@@ -136,19 +141,18 @@ func loadConsumerConfig() {
 
 	// wait for invoker is available, if wait over default 3s, then panic
 	var count int
-	checkok := true
 	for {
+		checkok := true
 		for _, refconfig := range consumerConfig.References {
 			if (refconfig.Check != nil && *refconfig.Check) ||
 				(refconfig.Check == nil && consumerConfig.Check != nil && *consumerConfig.Check) ||
 				(refconfig.Check == nil && consumerConfig.Check == nil) { // default to true
 
-				if refconfig.invoker != nil &&
-					!refconfig.invoker.IsAvailable() {
+				if refconfig.invoker != nil && !refconfig.invoker.IsAvailable() {
 					checkok = false
 					count++
 					if count > maxWait {
-						errMsg := fmt.Sprintf("Failed to check the status of the service %v . No provider available for the service to the consumer use dubbo version %v", refconfig.InterfaceName, constant.Version)
+						errMsg := fmt.Sprintf("Failed to check the status of the service %v. No provider available for the service to the consumer use dubbo version %v", refconfig.InterfaceName, constant.Version)
 						logger.Error(errMsg)
 						panic(errMsg)
 					}
@@ -156,14 +160,13 @@ func loadConsumerConfig() {
 					break
 				}
 				if refconfig.invoker == nil {
-					logger.Warnf("The interface %s invoker not exist , may you should check your interface config.", refconfig.InterfaceName)
+					logger.Warnf("The interface %s invoker not exist, may you should check your interface config.", refconfig.InterfaceName)
 				}
 			}
 		}
 		if checkok {
 			break
 		}
-		checkok = true
 	}
 }
 
@@ -205,6 +208,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() {
@@ -325,6 +419,12 @@ func GetBaseConfig() *BaseConfig {
 	return baseConfig
 }
 
+func GetSslEnabled() bool {
+	return sslEnabled
+}
+func SetSslEnabled(enabled bool) {
+	sslEnabled = enabled
+}
 func IsProvider() bool {
 	return providerConfig != nil
 }
diff --git a/config/config_loader_test.go b/config/config_loader_test.go
index 2cbf526a70b52f184f500bad98edb01b97d239ec..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"
@@ -72,9 +81,19 @@ func TestLoad(t *testing.T) {
 	SetProviderService(ms)
 
 	extension.SetProtocol("registry", GetProtocol)
-	extension.SetCluster("registryAware", cluster_impl.NewRegistryAwareCluster)
+	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()))
@@ -101,9 +120,19 @@ func TestLoadWithSingleReg(t *testing.T) {
 	SetProviderService(ms)
 
 	extension.SetProtocol("registry", GetProtocol)
-	extension.SetCluster("registryAware", cluster_impl.NewRegistryAwareCluster)
+	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()))
@@ -130,9 +159,19 @@ func TestWithNoRegLoad(t *testing.T) {
 	SetProviderService(ms)
 
 	extension.SetProtocol("registry", GetProtocol)
-	extension.SetCluster("registryAware", cluster_impl.NewRegistryAwareCluster)
+	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()))
@@ -278,13 +317,13 @@ func mockInitProviderWithSingleRegistry() {
 					{
 						Name:        "GetUser",
 						Retries:     "2",
-						Loadbalance: "random",
+						LoadBalance: "random",
 						Weight:      200,
 					},
 					{
 						Name:        "GetUser1",
 						Retries:     "2",
-						Loadbalance: "random",
+						LoadBalance: "random",
 						Weight:      200,
 					},
 				},
@@ -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 48f29f0e70028a7c057ee3831b45afa72446f3d0..c8083603e12570a4492dd63a749adb4aa89663c8 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
 /////////////////////////
@@ -41,7 +44,8 @@ import (
 // ConsumerConfig is Consumer default configuration
 type ConsumerConfig struct {
 	BaseConfig `yaml:",inline"`
-	Filter     string `yaml:"filter" json:"filter,omitempty" property:"filter"`
+	configCenter
+	Filter string `yaml:"filter" json:"filter,omitempty" property:"filter"`
 	// client
 	Connect_Timeout string `default:"100ms"  yaml:"connect_timeout" json:"connect_timeout,omitempty" property:"connect_timeout"`
 	ConnectTimeout  time.Duration
@@ -56,8 +60,8 @@ type ConsumerConfig struct {
 
 	References     map[string]*ReferenceConfig `yaml:"references" json:"references,omitempty" property:"references"`
 	ProtocolConf   interface{}                 `yaml:"protocol_conf" json:"protocol_conf,omitempty" property:"protocol_conf"`
-	FilterConf     interface{}                 `yaml:"filter_conf" json:"filter_conf,omitempty" property:"filter_conf" `
-	ShutdownConfig *ShutdownConfig             `yaml:"shutdown_conf" json:"shutdown_conf,omitempty" property:"shutdown_conf" `
+	FilterConf     interface{}                 `yaml:"filter_conf" json:"filter_conf,omitempty" property:"filter_conf"`
+	ShutdownConfig *ShutdownConfig             `yaml:"shutdown_conf" json:"shutdown_conf,omitempty" property:"shutdown_conf"`
 	ConfigType     map[string]string           `yaml:"config_type" json:"config_type,omitempty" property:"config_type"`
 }
 
@@ -106,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 != "" {
@@ -125,13 +129,6 @@ func ConsumerInit(confConFile string) error {
 func configCenterRefreshConsumer() error {
 	//fresh it
 	var err error
-	if consumerConfig.ConfigCenterConfig != nil {
-		consumerConfig.SetFatherConfig(consumerConfig)
-		if err = consumerConfig.startConfigCenter(); err != nil {
-			return perrors.Errorf("start config center error , error message is {%v}", perrors.WithStack(err))
-		}
-		consumerConfig.fresh()
-	}
 	if consumerConfig.Request_Timeout != "" {
 		if consumerConfig.RequestTimeout, err = time.ParseDuration(consumerConfig.Request_Timeout); err != nil {
 			return perrors.WithMessagef(err, "time.ParseDuration(Request_Timeout{%#v})", consumerConfig.Request_Timeout)
@@ -142,5 +139,12 @@ func configCenterRefreshConsumer() error {
 			return perrors.WithMessagef(err, "time.ParseDuration(Connect_Timeout{%#v})", consumerConfig.Connect_Timeout)
 		}
 	}
+	if consumerConfig.ConfigCenterConfig != nil {
+		consumerConfig.SetFatherConfig(consumerConfig)
+		if err = consumerConfig.startConfigCenter((*consumerConfig).BaseConfig); err != nil {
+			return perrors.Errorf("start config center error , error message is {%v}", perrors.WithStack(err))
+		}
+		consumerConfig.fresh()
+	}
 	return nil
 }
diff --git a/config/method_config.go b/config/method_config.go
index e64773eb135b2f9ec55377bded815147e2e192af..b64306fd6aa865d219506ea2722067619b00fea7 100644
--- a/config/method_config.go
+++ b/config/method_config.go
@@ -25,13 +25,13 @@ import (
 	"github.com/apache/dubbo-go/common/constant"
 )
 
-// MethodConfig ...
+// MethodConfig defines method config
 type MethodConfig struct {
 	InterfaceId                 string
 	InterfaceName               string
 	Name                        string `yaml:"name"  json:"name,omitempty" property:"name"`
 	Retries                     string `yaml:"retries"  json:"retries,omitempty" property:"retries"`
-	Loadbalance                 string `yaml:"loadbalance"  json:"loadbalance,omitempty" property:"loadbalance"`
+	LoadBalance                 string `yaml:"loadbalance"  json:"loadbalance,omitempty" property:"loadbalance"`
 	Weight                      int64  `yaml:"weight"  json:"weight,omitempty" property:"weight"`
 	TpsLimitInterval            string `yaml:"tps.limit.interval" json:"tps.limit.interval,omitempty" property:"tps.limit.interval"`
 	TpsLimitRate                string `yaml:"tps.limit.rate" json:"tps.limit.rate,omitempty" property:"tps.limit.rate"`
diff --git a/config/provider_config.go b/config/provider_config.go
index 7cd3c1e98bfb8c35abb2b414b782ec709d0a8d0d..fcb429b6400936f72c09035968b4d4c72387246c 100644
--- a/config/provider_config.go
+++ b/config/provider_config.go
@@ -37,14 +37,15 @@ import (
 
 // ProviderConfig is the default configuration of service provider
 type ProviderConfig struct {
-	BaseConfig     `yaml:",inline"`
+	BaseConfig `yaml:",inline"`
+	configCenter
 	Filter         string                     `yaml:"filter" json:"filter,omitempty" property:"filter"`
 	ProxyFactory   string                     `yaml:"proxy_factory" default:"default" json:"proxy_factory,omitempty" property:"proxy_factory"`
 	Services       map[string]*ServiceConfig  `yaml:"services" json:"services,omitempty" property:"services"`
 	Protocols      map[string]*ProtocolConfig `yaml:"protocols" json:"protocols,omitempty" property:"protocols"`
-	ProtocolConf   interface{}                `yaml:"protocol_conf" json:"protocol_conf,omitempty" property:"protocol_conf" `
-	FilterConf     interface{}                `yaml:"filter_conf" json:"filter_conf,omitempty" property:"filter_conf" `
-	ShutdownConfig *ShutdownConfig            `yaml:"shutdown_conf" json:"shutdown_conf,omitempty" property:"shutdown_conf" `
+	ProtocolConf   interface{}                `yaml:"protocol_conf" json:"protocol_conf,omitempty" property:"protocol_conf"`
+	FilterConf     interface{}                `yaml:"filter_conf" json:"filter_conf,omitempty" property:"filter_conf"`
+	ShutdownConfig *ShutdownConfig            `yaml:"shutdown_conf" json:"shutdown_conf,omitempty" property:"shutdown_conf"`
 	ConfigType     map[string]string          `yaml:"config_type" json:"config_type,omitempty" property:"config_type"`
 
 	Registry   *RegistryConfig            `yaml:"registry" json:"registry,omitempty" property:"registry"`
@@ -101,7 +102,7 @@ func configCenterRefreshProvider() error {
 	// fresh it
 	if providerConfig.ConfigCenterConfig != nil {
 		providerConfig.fatherConfig = providerConfig
-		if err := providerConfig.startConfigCenter(); err != nil {
+		if err := providerConfig.startConfigCenter((*providerConfig).BaseConfig); err != nil {
 			return perrors.Errorf("start config center error , error message is {%v}", perrors.WithStack(err))
 		}
 		providerConfig.fresh()
diff --git a/config/reference_config.go b/config/reference_config.go
index 748b2d403fe315f879c817c4e0a4cf3197d807da..cd10f89eb7773e16ed953623c6fb38dcb98b01b4 100644
--- a/config/reference_config.go
+++ b/config/reference_config.go
@@ -147,13 +147,26 @@ func (c *ReferenceConfig) Refer(_ interface{}) {
 		}
 
 		// TODO(decouple from directory, config should not depend on directory module)
+		var hitClu string
 		if regUrl != nil {
-			cluster := extension.GetCluster("registryAware")
-			c.invoker = cluster.Join(directory.NewStaticDirectory(invokers))
+			// for multi-subscription scenario, use 'zone-aware' policy by default
+			hitClu = constant.ZONEAWARE_CLUSTER_NAME
 		} else {
-			cluster := extension.GetCluster(c.Cluster)
-			c.invoker = cluster.Join(directory.NewStaticDirectory(invokers))
+			// not a registry url, must be direct invoke.
+			hitClu = constant.FAILOVER_CLUSTER_NAME
+			if len(invokers) > 0 {
+				u := invokers[0].GetUrl()
+				if nil != &u {
+					hitClu = u.GetParam(constant.CLUSTER_KEY, constant.ZONEAWARE_CLUSTER_NAME)
+				}
+			}
 		}
+
+		cluster := extension.GetCluster(hitClu)
+		// If 'zone-aware' policy select, the invoker wrap sequence would be:
+		// ZoneAwareClusterInvoker(StaticDirectory) ->
+		// FailoverClusterInvoker(RegistryDirectory, routing happens here) -> Invoker
+		c.invoker = cluster.Join(directory.NewStaticDirectory(invokers))
 	}
 
 	// create proxy
@@ -220,7 +233,7 @@ func (c *ReferenceConfig) getUrlMap() url.Values {
 	urlMap.Set(constant.REFERENCE_FILTER_KEY, mergeValue(consumerConfig.Filter, c.Filter, defaultReferenceFilter))
 
 	for _, v := range c.Methods {
-		urlMap.Set("methods."+v.Name+"."+constant.LOADBALANCE_KEY, v.Loadbalance)
+		urlMap.Set("methods."+v.Name+"."+constant.LOADBALANCE_KEY, v.LoadBalance)
 		urlMap.Set("methods."+v.Name+"."+constant.RETRIES_KEY, v.Retries)
 		urlMap.Set("methods."+v.Name+"."+constant.STICKY_KEY, strconv.FormatBool(v.Sticky))
 		if len(v.RequestTimeout) != 0 {
@@ -238,5 +251,4 @@ func (c *ReferenceConfig) GenericLoad(id string) {
 	c.id = id
 	c.Refer(genericService)
 	c.Implement(genericService)
-	return
 }
diff --git a/config/reference_config_test.go b/config/reference_config_test.go
index 3fbf8da44ca7d00e335260cf107e99dae3a2fa8a..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
@@ -103,12 +104,12 @@ func doInitConsumer() {
 					{
 						Name:        "GetUser",
 						Retries:     "2",
-						Loadbalance: "random",
+						LoadBalance: "random",
 					},
 					{
 						Name:        "GetUser1",
 						Retries:     "2",
-						Loadbalance: "random",
+						LoadBalance: "random",
 						Sticky:      true,
 					},
 				},
@@ -174,12 +175,12 @@ func doInitConsumerWithSingleRegistry() {
 					{
 						Name:        "GetUser",
 						Retries:     "2",
-						Loadbalance: "random",
+						LoadBalance: "random",
 					},
 					{
 						Name:        "GetUser1",
 						Retries:     "2",
-						Loadbalance: "random",
+						LoadBalance: "random",
 					},
 				},
 			},
@@ -187,10 +188,10 @@ func doInitConsumerWithSingleRegistry() {
 	}
 }
 
-func TestReferMultireg(t *testing.T) {
+func TestReferMultiReg(t *testing.T) {
 	doInitConsumer()
 	extension.SetProtocol("registry", GetProtocol)
-	extension.SetCluster("registryAware", cluster_impl.NewRegistryAwareCluster)
+	extension.SetCluster(constant.ZONEAWARE_CLUSTER_NAME, cluster_impl.NewZoneAwareCluster)
 
 	for _, reference := range consumerConfig.References {
 		reference.Refer(nil)
@@ -203,7 +204,7 @@ func TestReferMultireg(t *testing.T) {
 func TestRefer(t *testing.T) {
 	doInitConsumer()
 	extension.SetProtocol("registry", GetProtocol)
-	extension.SetCluster("registryAware", cluster_impl.NewRegistryAwareCluster)
+	extension.SetCluster(constant.ZONEAWARE_CLUSTER_NAME, cluster_impl.NewZoneAwareCluster)
 
 	for _, reference := range consumerConfig.References {
 		reference.Refer(nil)
@@ -217,7 +218,7 @@ func TestRefer(t *testing.T) {
 func TestReferAsync(t *testing.T) {
 	doInitConsumerAsync()
 	extension.SetProtocol("registry", GetProtocol)
-	extension.SetCluster("registryAware", cluster_impl.NewRegistryAwareCluster)
+	extension.SetCluster(constant.ZONEAWARE_CLUSTER_NAME, cluster_impl.NewZoneAwareCluster)
 
 	for _, reference := range consumerConfig.References {
 		reference.Refer(nil)
@@ -275,7 +276,7 @@ func TestReferMultiP2PWithReg(t *testing.T) {
 func TestImplement(t *testing.T) {
 	doInitConsumer()
 	extension.SetProtocol("registry", GetProtocol)
-	extension.SetCluster("registryAware", cluster_impl.NewRegistryAwareCluster)
+	extension.SetCluster(constant.ZONEAWARE_CLUSTER_NAME, cluster_impl.NewZoneAwareCluster)
 	for _, reference := range consumerConfig.References {
 		reference.Refer(nil)
 		reference.Implement(&MockService{})
@@ -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/registry_config.go b/config/registry_config.go
index ef527c827e9dac4cd2762f579d30254e9e51150f..89566c428ed14f460c0f214358c9fa05d529ddb6 100644
--- a/config/registry_config.go
+++ b/config/registry_config.go
@@ -39,12 +39,22 @@ type RegistryConfig struct {
 	// I changed "type" to "protocol" ,the same as "protocol" field in java class RegistryConfig
 	TimeoutStr string `yaml:"timeout" default:"5s" json:"timeout,omitempty" property:"timeout"` // unit: second
 	Group      string `yaml:"group" json:"group,omitempty" property:"group"`
+	TTL        string `yaml:"ttl" default:"10m" json:"ttl,omitempty" property:"ttl"` // unit: minute
 	// for registry
-	Address    string            `yaml:"address" json:"address,omitempty" property:"address"`
-	Username   string            `yaml:"username" json:"username,omitempty" property:"username"`
-	Password   string            `yaml:"password" json:"password,omitempty"  property:"password"`
-	Simplified bool              `yaml:"simplified" json:"simplified,omitempty"  property:"simplified"`
-	Params     map[string]string `yaml:"params" json:"params,omitempty" property:"params"`
+	Address    string `yaml:"address" json:"address,omitempty" property:"address"`
+	Username   string `yaml:"username" json:"username,omitempty" property:"username"`
+	Password   string `yaml:"password" json:"password,omitempty"  property:"password"`
+	Simplified bool   `yaml:"simplified" json:"simplified,omitempty"  property:"simplified"`
+	// Always use this registry first if set to true, useful when subscribe to multiple registries
+	Preferred bool `yaml:"preferred" json:"preferred,omitempty" property:"preferred"`
+	// The region where the registry belongs, usually used to isolate traffics
+	Zone string `yaml:"zone" json:"zone,omitempty" property:"zone"`
+	//// Force must user the region, property zone is specified.
+	//ZoneForce bool `yaml:"zoneForce" json:"zoneForce,omitempty" property:"zoneForce"`
+	// Affects traffic distribution among registries,
+	// useful when subscribe to multiple registries Take effect only when no preferred registry is specified.
+	Weight int64             `yaml:"weight" json:"params,omitempty" property:"weight"`
+	Params map[string]string `yaml:"params" json:"params,omitempty" property:"params"`
 }
 
 // UnmarshalYAML unmarshals the RegistryConfig by @unmarshal function
@@ -118,6 +128,13 @@ func (c *RegistryConfig) getUrlMap(roleType common.RoleType) url.Values {
 	urlMap.Set(constant.ROLE_KEY, strconv.Itoa(int(roleType)))
 	urlMap.Set(constant.REGISTRY_KEY, c.Protocol)
 	urlMap.Set(constant.REGISTRY_TIMEOUT_KEY, c.TimeoutStr)
+	// multi registry invoker weight label for load balance
+	urlMap.Set(constant.REGISTRY_KEY+"."+constant.REGISTRY_LABEL_KEY, strconv.FormatBool(true))
+	urlMap.Set(constant.REGISTRY_KEY+"."+constant.PREFERRED_KEY, strconv.FormatBool(c.Preferred))
+	urlMap.Set(constant.REGISTRY_KEY+"."+constant.ZONE_KEY, c.Zone)
+	//urlMap.Set(constant.REGISTRY_KEY+"."+constant.ZONE_FORCE_KEY, strconv.FormatBool(c.ZoneForce))
+	urlMap.Set(constant.REGISTRY_KEY+"."+constant.WEIGHT_KEY, strconv.FormatInt(c.Weight, 10))
+	urlMap.Set(constant.REGISTRY_TTL_KEY, c.TTL)
 	for k, v := range c.Params {
 		urlMap.Set(k, v)
 	}
diff --git a/config/remote_config.go b/config/remote_config.go
index 5e0330c571715d99e63688ee944c61f8e48117bb..55380dd5a05b47b5b4677b32daf73b37376673d0 100644
--- a/config/remote_config.go
+++ b/config/remote_config.go
@@ -22,6 +22,11 @@ import (
 )
 
 import (
+	perrors "github.com/pkg/errors"
+)
+
+import (
+	"github.com/apache/dubbo-go/common"
 	"github.com/apache/dubbo-go/common/logger"
 )
 
@@ -30,6 +35,7 @@ import (
 // so that other module, like config center, registry could reuse the config
 // but now, only metadata report, metadata service, service discovery use this structure
 type RemoteConfig struct {
+	Protocol   string            `yaml:"protocol"  json:"protocol,omitempty"`
 	Address    string            `yaml:"address" json:"address,omitempty"`
 	TimeoutStr string            `default:"5s" yaml:"timeout" json:"timeout,omitempty"`
 	Username   string            `yaml:"username" json:"username,omitempty" property:"username"`
@@ -56,3 +62,15 @@ func (rc *RemoteConfig) GetParam(key string, def string) string {
 	}
 	return param
 }
+
+func (rc *RemoteConfig) toURL() (common.URL, error) {
+	if len(rc.Protocol) == 0 {
+		return common.URL{}, perrors.Errorf("Must provide protocol in RemoteConfig.")
+	}
+	return common.NewURL(rc.Address,
+		common.WithUsername(rc.Username),
+		common.WithPassword(rc.Password),
+		common.WithLocation(rc.Address),
+		common.WithProtocol(rc.Protocol),
+	)
+}
diff --git a/config/router_config.go b/config/router_config.go
index 16a2bec918d0f9a2de2174324e78ca21e853dabf..ed42577ed3cce2e5a1ab0da290f0d5450553d8fb 100644
--- a/config/router_config.go
+++ b/config/router_config.go
@@ -23,6 +23,7 @@ import (
 )
 
 import (
+	"github.com/apache/dubbo-go/cluster/router"
 	"github.com/apache/dubbo-go/common/extension"
 	"github.com/apache/dubbo-go/common/logger"
 	"github.com/apache/dubbo-go/common/yaml"
@@ -32,16 +33,37 @@ var (
 	routerURLSet = gxset.NewSet()
 )
 
+// LocalRouterRules defines the local router config structure
+type LocalRouterRules struct {
+	RouterRules []interface{} `yaml:"routerRules"`
+}
+
 // RouterInit Load config file to init router config
 func RouterInit(confRouterFile string) error {
-	fileRouterFactories := extension.GetFileRouterFactories()
 	bytes, err := yaml.LoadYMLConfig(confRouterFile)
 	if err != nil {
 		return perrors.Errorf("ioutil.ReadFile(file:%s) = error:%v", confRouterFile, perrors.WithStack(err))
 	}
-	logger.Warnf("get fileRouterFactories len{%+v})", len(fileRouterFactories))
-	for k, factory := range fileRouterFactories {
-		r, e := factory.NewFileRouter(bytes)
+	routerRules := &LocalRouterRules{}
+	err = yaml.UnmarshalYML(bytes, routerRules)
+	if err != nil {
+		return perrors.Errorf("Load router file %s failed due to error: %v", confRouterFile, perrors.WithStack(err))
+	}
+	if len(routerRules.RouterRules) == 0 {
+		return perrors.Errorf("No router configurations in file %s", confRouterFile)
+	}
+	fileRouterFactories := extension.GetFileRouterFactories()
+	for _, v := range routerRules.RouterRules {
+		content, _ := yaml.MarshalYML(v)
+		err = initRouterConfig(content, fileRouterFactories)
+	}
+	return err
+}
+
+func initRouterConfig(content []byte, factories map[string]router.FilePriorityRouterFactory) error {
+	logger.Warnf("get fileRouterFactories len{%+v})", len(factories))
+	for k, factory := range factories {
+		r, e := factory.NewFileRouter(content)
 		if e == nil {
 			url := r.URL()
 			routerURLSet.Add(&url)
@@ -52,6 +74,7 @@ func RouterInit(confRouterFile string) error {
 	return perrors.Errorf("no file router exists for parse %s , implement router.FIleRouterFactory please.", confRouterFile)
 }
 
+// GetRouterURLSet exposes the routerURLSet
 func GetRouterURLSet() *gxset.HashSet {
 	return routerURLSet
 }
diff --git a/config/router_config_test.go b/config/router_config_test.go
index 72e51c1c82562b03736fd0afef79b78d83d6f4f3..13af7056d5280ef4cca3c0f9ede9397407df7478 100644
--- a/config/router_config_test.go
+++ b/config/router_config_test.go
@@ -23,6 +23,7 @@ import (
 )
 
 import (
+	"github.com/dubbogo/gost/container/set"
 	"github.com/stretchr/testify/assert"
 )
 
@@ -31,6 +32,7 @@ import (
 )
 
 const testYML = "testdata/router_config.yml"
+const testMultiRouterYML = "testdata/router_multi_config.yml"
 const errorTestYML = "testdata/router_config_error.yml"
 
 func TestString(t *testing.T) {
@@ -63,4 +65,10 @@ func TestRouterInit(t *testing.T) {
 	assert.NoError(t, errPro)
 
 	assert.Equal(t, 1, routerURLSet.Size())
+
+	routerURLSet = gxset.NewSet()
+	errPro = RouterInit(testMultiRouterYML)
+	assert.NoError(t, errPro)
+
+	assert.Equal(t, 2, routerURLSet.Size())
 }
diff --git a/config/service_config.go b/config/service_config.go
index 57fce028fa59893979fc6f50671fe8681770a2b8..8968bac88f55c614841462517c5a5a4ecbe6f594 100644
--- a/config/service_config.go
+++ b/config/service_config.go
@@ -133,7 +133,7 @@ func getRandomPort(protocolConfigs []*ProtocolConfig) *list.List {
 
 		tcp, err := gxnet.ListenOnTCPRandomPort(proto.Ip)
 		if err != nil {
-			panic(perrors.New(fmt.Sprintf("Get tcp port error,err is {%v}", err)))
+			panic(perrors.New(fmt.Sprintf("Get tcp port error, err is {%v}", err)))
 		}
 		defer tcp.Close()
 		ports.PushBack(strings.Split(tcp.Addr().String(), ":")[1])
@@ -145,14 +145,14 @@ func getRandomPort(protocolConfigs []*ProtocolConfig) *list.List {
 func (c *ServiceConfig) Export() error {
 	// TODO: config center start here
 
-	// TODO:delay export
+	// TODO: delay export
 	if c.unexported != nil && c.unexported.Load() {
-		err := perrors.Errorf("The service %v has already unexported! ", c.InterfaceName)
+		err := perrors.Errorf("The service %v has already unexported!", c.InterfaceName)
 		logger.Errorf(err.Error())
 		return err
 	}
 	if c.unexported != nil && c.exported.Load() {
-		logger.Warnf("The service %v has already exported! ", c.InterfaceName)
+		logger.Warnf("The service %v has already exported!", c.InterfaceName)
 		return nil
 	}
 
@@ -160,23 +160,23 @@ func (c *ServiceConfig) Export() error {
 	urlMap := c.getUrlMap()
 	protocolConfigs := loadProtocol(c.Protocol, c.Protocols)
 	if len(protocolConfigs) == 0 {
-		logger.Warnf("The service %v's '%v' protocols don't has right protocolConfigs ", c.InterfaceName, c.Protocol)
+		logger.Warnf("The service %v's '%v' protocols don't has right protocolConfigs", c.InterfaceName, c.Protocol)
 		return nil
 	}
 
 	ports := getRandomPort(protocolConfigs)
 	nextPort := ports.Front()
+	proxyFactory := extension.GetProxyFactory(providerConfig.ProxyFactory)
 	for _, proto := range protocolConfigs {
 		// registry the service reflect
 		methods, err := common.ServiceMap.Register(c.InterfaceName, proto.Name, c.rpcService)
 		if err != nil {
-			formatErr := perrors.Errorf("The service %v  export the protocol %v error! Error message is %v .", c.InterfaceName, proto.Name, err.Error())
+			formatErr := perrors.Errorf("The service %v export the protocol %v error! Error message is %v.", c.InterfaceName, proto.Name, err.Error())
 			logger.Errorf(formatErr.Error())
 			return formatErr
 		}
 
 		port := proto.Port
-
 		if len(proto.Port) == 0 {
 			port = nextPort.Value.(string)
 			nextPort = nextPort.Next()
@@ -188,6 +188,7 @@ func (c *ServiceConfig) Export() error {
 			common.WithPort(port),
 			common.WithParams(urlMap),
 			common.WithParamsValue(constant.BEAN_NAME_KEY, c.id),
+			common.WithParamsValue(constant.SSL_ENABLED_KEY, strconv.FormatBool(GetSslEnabled())),
 			common.WithMethods(strings.Split(methods, ",")),
 			common.WithToken(c.Token),
 		)
@@ -195,33 +196,31 @@ func (c *ServiceConfig) Export() error {
 			ivkURL.AddParam(constant.Tagkey, c.Tag)
 		}
 
-		var exporter protocol.Exporter
-
 		if len(regUrls) > 0 {
+			c.cacheMutex.Lock()
+			if c.cacheProtocol == nil {
+				logger.Infof(fmt.Sprintf("First load the registry protocol, url is {%v}!", ivkURL))
+				c.cacheProtocol = extension.GetProtocol("registry")
+			}
+			c.cacheMutex.Unlock()
+
 			for _, regUrl := range regUrls {
 				regUrl.SubURL = ivkURL
-
-				c.cacheMutex.Lock()
-				if c.cacheProtocol == nil {
-					logger.Infof(fmt.Sprintf("First load the registry protocol , url is {%v}!", ivkURL))
-					c.cacheProtocol = extension.GetProtocol("registry")
-				}
-				c.cacheMutex.Unlock()
-
-				invoker := extension.GetProxyFactory(providerConfig.ProxyFactory).GetInvoker(*regUrl)
-				exporter = c.cacheProtocol.Export(invoker)
+				invoker := proxyFactory.GetInvoker(*regUrl)
+				exporter := c.cacheProtocol.Export(invoker)
 				if exporter == nil {
-					panic(perrors.New(fmt.Sprintf("Registry protocol new exporter error,registry is {%v},url is {%v}", regUrl, ivkURL)))
+					return perrors.New(fmt.Sprintf("Registry protocol new exporter error, registry is {%v}, url is {%v}", regUrl, ivkURL))
 				}
+				c.exporters = append(c.exporters, exporter)
 			}
 		} else {
-			invoker := extension.GetProxyFactory(providerConfig.ProxyFactory).GetInvoker(*ivkURL)
-			exporter = extension.GetProtocol(protocolwrapper.FILTER).Export(invoker)
+			invoker := proxyFactory.GetInvoker(*ivkURL)
+			exporter := extension.GetProtocol(protocolwrapper.FILTER).Export(invoker)
 			if exporter == nil {
-				panic(perrors.New(fmt.Sprintf("Filter protocol without registry new exporter error,url is {%v}", ivkURL)))
+				return perrors.New(fmt.Sprintf("Filter protocol without registry new exporter error, url is {%v}", ivkURL))
 			}
+			c.exporters = append(c.exporters, exporter)
 		}
-		c.exporters = append(c.exporters, exporter)
 	}
 	c.exported.Store(true)
 	return nil
@@ -303,7 +302,7 @@ func (c *ServiceConfig) getUrlMap() url.Values {
 
 	for _, v := range c.Methods {
 		prefix := "methods." + v.Name + "."
-		urlMap.Set(prefix+constant.LOADBALANCE_KEY, v.Loadbalance)
+		urlMap.Set(prefix+constant.LOADBALANCE_KEY, v.LoadBalance)
 		urlMap.Set(prefix+constant.RETRIES_KEY, v.Retries)
 		urlMap.Set(prefix+constant.WEIGHT_KEY, strconv.FormatInt(v.Weight, 10))
 
@@ -313,7 +312,6 @@ func (c *ServiceConfig) getUrlMap() url.Values {
 
 		urlMap.Set(constant.EXECUTE_LIMIT_KEY, v.ExecuteLimit)
 		urlMap.Set(constant.EXECUTE_REJECTED_EXECUTION_HANDLER_KEY, v.ExecuteLimitRejectedHandler)
-
 	}
 
 	return urlMap
diff --git a/config/service_config_test.go b/config/service_config_test.go
index e7d55077beb56b0ccf8da833f5aeebfac07a9e3f..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",
@@ -56,13 +76,13 @@ func doInitProvider() {
 					{
 						Name:        "GetUser",
 						Retries:     "2",
-						Loadbalance: "random",
+						LoadBalance: "random",
 						Weight:      200,
 					},
 					{
 						Name:        "GetUser1",
 						Retries:     "2",
-						Loadbalance: "random",
+						LoadBalance: "random",
 						Weight:      200,
 					},
 				},
@@ -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",
@@ -81,13 +101,13 @@ func doInitProvider() {
 					{
 						Name:        "GetUser",
 						Retries:     "2",
-						Loadbalance: "random",
+						LoadBalance: "random",
 						Weight:      200,
 					},
 					{
 						Name:        "GetUser1",
 						Retries:     "2",
-						Loadbalance: "random",
+						LoadBalance: "random",
 						Weight:      200,
 					},
 				},
@@ -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{
@@ -153,7 +181,7 @@ func TestExport(t *testing.T) {
 	providerConfig = nil
 }
 
-func TestgetRandomPort(t *testing.T) {
+func TestGetRandomPort(t *testing.T) {
 	protocolConfigs := make([]*ProtocolConfig, 0, 3)
 
 	ip, err := gxnet.GetLocalIP()
diff --git a/config/ssl_config.go b/config/ssl_config.go
new file mode 100644
index 0000000000000000000000000000000000000000..8576930367a487269bf82d51191bb02289dd1e73
--- /dev/null
+++ b/config/ssl_config.go
@@ -0,0 +1,43 @@
+/*
+ * 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 config
+
+import (
+	"github.com/apache/dubbo-getty"
+)
+
+var (
+	serverTlsConfigBuilder getty.TlsConfigBuilder
+	clientTlsConfigBuilder getty.TlsConfigBuilder
+)
+
+func GetServerTlsConfigBuilder() getty.TlsConfigBuilder {
+	return serverTlsConfigBuilder
+}
+
+func GetClientTlsConfigBuilder() getty.TlsConfigBuilder {
+	return clientTlsConfigBuilder
+}
+
+func SetServerTlsConfigBuilder(configBuilder getty.TlsConfigBuilder) {
+	serverTlsConfigBuilder = configBuilder
+}
+
+func SetClientTlsConfigBuilder(configBuilder getty.TlsConfigBuilder) {
+	clientTlsConfigBuilder = configBuilder
+}
diff --git a/config/testdata/router_config.yml b/config/testdata/router_config.yml
index f6b91f5da7d95256e6279924b884bfd450c45a08..1845650d93c152585c4d4caf60ab7aa3d5b04887 100644
--- a/config/testdata/router_config.yml
+++ b/config/testdata/router_config.yml
@@ -1,6 +1,9 @@
 # dubbo router yaml configure file
-priority: 1
-force: true
-conditions :
-  - "a => b"
-  - "c => d"
\ No newline at end of file
+routerRules: 
+  - scope: application
+    key: mock-app
+    priority: 1
+    force: true
+    conditions :
+      - "a => b"
+      - "c => d"
\ No newline at end of file
diff --git a/config/testdata/router_config_error.yml b/config/testdata/router_config_error.yml
index 37894ac96474281d10131b53d8e644f10a18b14e..74e89cc52ec772a52c3c424a94276075fb099f8c 100644
--- a/config/testdata/router_config_error.yml
+++ b/config/testdata/router_config_error.yml
@@ -1,6 +1,7 @@
 # dubbo router yaml configure file
-priority: 1
-force: true
-noConditions :
-  - "a => b"
-  - "c => d"
\ No newline at end of file
+routerRules: 
+  - priority: 1
+    force: true
+    noConditions :
+    - "a => b"
+    - "c => d"
\ No newline at end of file
diff --git a/config/testdata/router_multi_config.yml b/config/testdata/router_multi_config.yml
new file mode 100644
index 0000000000000000000000000000000000000000..42bb4cbe70a43dc82ad4a4849059ad4344e6fd85
--- /dev/null
+++ b/config/testdata/router_multi_config.yml
@@ -0,0 +1,16 @@
+# dubbo router yaml configure file
+routerRules:
+  - scope: application
+    key: mock-app
+    priority: 1
+    force: true
+    conditions :
+      - "a => b"
+      - "c => d"
+  - scope: application
+    key: mock-app2
+    priority: 1
+    force: true
+    conditions :
+      - "a => b"
+      - "c => d"
\ No newline at end of file
diff --git a/config_center/apollo/factory.go b/config_center/apollo/factory.go
index f975ce13d8e5832f03051c42f92157532347d283..c52d942c4f481c64d0c465cafbd6f7f8c3bb1346 100644
--- a/config_center/apollo/factory.go
+++ b/config_center/apollo/factory.go
@@ -34,6 +34,7 @@ func createDynamicConfigurationFactory() config_center.DynamicConfigurationFacto
 
 type apolloConfigurationFactory struct{}
 
+// GetDynamicConfiguration gets the dynamic configuration
 func (f *apolloConfigurationFactory) GetDynamicConfiguration(url *common.URL) (config_center.DynamicConfiguration, error) {
 	dynamicConfiguration, err := newApolloConfiguration(url)
 	if err != nil {
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 1cf65ed22ba0a1f765af66191ed19a04f81b0fe6..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 ...
-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,
-			})
-		}
+// OnChange process each listener
+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/configurator/override.go b/config_center/configurator/override.go
index 294a60ebb2e4e18cfc47cd90aedeaa615b5626d2..ec4e606e0dff300729e2a2bc56f510db94ea9f26 100644
--- a/config_center/configurator/override.go
+++ b/config_center/configurator/override.go
@@ -110,7 +110,7 @@ func (c *overrideConfigurator) configureIfMatchInternal(url *common.URL) {
 func (c *overrideConfigurator) configureIfMatch(host string, url *common.URL) {
 	if constant.ANYHOST_VALUE == c.configuratorUrl.Ip || host == c.configuratorUrl.Ip {
 		providers := c.configuratorUrl.GetParam(constant.OVERRIDE_PROVIDERS_KEY, "")
-		if len(providers) == 0 || strings.Index(providers, url.Location) >= 0 || strings.Index(providers, constant.ANYHOST_VALUE) >= 0 {
+		if len(providers) == 0 || strings.Contains(providers, url.Location) || strings.Contains(providers, constant.ANYHOST_VALUE) {
 			c.configureIfMatchInternal(url)
 		}
 	}
diff --git a/config_center/mock_dynamic_config.go b/config_center/mock_dynamic_config.go
index de208946f1715878c2afc62fdd41df93f74797c7..8fe0a251239f7bfc6a3f70c3834da1b3af8484ba 100644
--- a/config_center/mock_dynamic_config.go
+++ b/config_center/mock_dynamic_config.go
@@ -33,7 +33,7 @@ import (
 	"github.com/apache/dubbo-go/remoting"
 )
 
-// MockDynamicConfigurationFactory ...
+// MockDynamicConfigurationFactory defines content
 type MockDynamicConfigurationFactory struct {
 	Content string
 }
@@ -96,7 +96,7 @@ func (c *MockDynamicConfiguration) GetConfigKeysByGroup(group string) (*gxset.Ha
 	return gxset.NewSet(c.content), nil
 }
 
-// MockDynamicConfiguration ...
+// MockDynamicConfiguration uses to parse content and defines listener
 type MockDynamicConfiguration struct {
 	parser   parser.ConfigurationParser
 	content  string
@@ -149,7 +149,7 @@ func (c *MockDynamicConfiguration) GetRule(key string, opts ...Option) (string,
 	return c.GetProperties(key, opts...)
 }
 
-// MockServiceConfigEvent ...
+// MockServiceConfigEvent returns ConfiguratorConfig
 func (c *MockDynamicConfiguration) MockServiceConfigEvent() {
 	config := &parser.ConfiguratorConfig{
 		ConfigVersion: "2.7.1",
@@ -171,7 +171,7 @@ func (c *MockDynamicConfiguration) MockServiceConfigEvent() {
 	c.listener[key].Process(&ConfigChangeEvent{Key: key, Value: string(value), ConfigType: remoting.EventTypeAdd})
 }
 
-// MockApplicationConfigEvent ...
+// MockApplicationConfigEvent returns ConfiguratorConfig
 func (c *MockDynamicConfiguration) MockApplicationConfigEvent() {
 	config := &parser.ConfiguratorConfig{
 		ConfigVersion: "2.7.1",
diff --git a/config_center/nacos/client.go b/config_center/nacos/client.go
index 6fe5c4d7df28a7693c732543140ed74f959dc77e..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)
@@ -169,7 +171,7 @@ func initNacosConfigClient(nacosAddrs []string, timeout time.Duration, url commo
 			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/config_center/parser/configuration_parser.go b/config_center/parser/configuration_parser.go
index 6fbdc27d4339150bfec624f7dc5ea0f6a608d7a7..f794221f9cf3c689d52b32a57ab51f7908f17297 100644
--- a/config_center/parser/configuration_parser.go
+++ b/config_center/parser/configuration_parser.go
@@ -35,13 +35,13 @@ import (
 )
 
 const (
-	// ScopeApplication ...
+	// ScopeApplication : scope application
 	ScopeApplication = "application"
-	// GeneralType ...
+	// GeneralType defines the general type
 	GeneralType = "general"
 )
 
-// ConfigurationParser ...
+// ConfigurationParser interface
 type ConfigurationParser interface {
 	Parse(string) (map[string]string, error)
 	ParseToUrls(content string) ([]*common.URL, error)
@@ -50,7 +50,7 @@ type ConfigurationParser interface {
 // DefaultConfigurationParser for supporting properties file in config center
 type DefaultConfigurationParser struct{}
 
-// ConfiguratorConfig ...
+// ConfiguratorConfig defines configurator config
 type ConfiguratorConfig struct {
 	ConfigVersion string       `yaml:"configVersion"`
 	Scope         string       `yaml:"scope"`
@@ -59,7 +59,7 @@ type ConfiguratorConfig struct {
 	Configs       []ConfigItem `yaml:"configs"`
 }
 
-// ConfigItem ...
+// ConfigItem defines config item
 type ConfigItem struct {
 	Type              string            `yaml:"type"`
 	Enabled           bool              `yaml:"enabled"`
@@ -81,7 +81,7 @@ func (parser *DefaultConfigurationParser) Parse(content string) (map[string]stri
 	return pps.Map(), nil
 }
 
-// ParseToUrls ...
+// ParseToUrls is used to parse content to urls
 func (parser *DefaultConfigurationParser) ParseToUrls(content string) ([]*common.URL, error) {
 	config := ConfiguratorConfig{}
 	if err := yaml.Unmarshal([]byte(content), &config); err != nil {
@@ -110,6 +110,7 @@ func (parser *DefaultConfigurationParser) ParseToUrls(content string) ([]*common
 	return allUrls, nil
 }
 
+// serviceItemToUrls is used to transfer item and config to urls
 func serviceItemToUrls(item ConfigItem, config ConfiguratorConfig) ([]*common.URL, error) {
 	var addresses = item.Addresses
 	if len(addresses) == 0 {
@@ -156,6 +157,7 @@ func serviceItemToUrls(item ConfigItem, config ConfiguratorConfig) ([]*common.UR
 	return urls, nil
 }
 
+// nolint
 func appItemToUrls(item ConfigItem, config ConfiguratorConfig) ([]*common.URL, error) {
 	var addresses = item.Addresses
 	if len(addresses) == 0 {
@@ -196,6 +198,7 @@ func appItemToUrls(item ConfigItem, config ConfiguratorConfig) ([]*common.URL, e
 	return urls, nil
 }
 
+// getServiceString returns service string
 func getServiceString(service string) (string, error) {
 	if len(service) == 0 {
 		return "", perrors.New("service field in configuration is null.")
@@ -219,6 +222,7 @@ func getServiceString(service string) (string, error) {
 	return serviceStr, nil
 }
 
+// nolint
 func getParamString(item ConfigItem) (string, error) {
 	var retStr string
 	retStr = retStr + "category="
@@ -241,6 +245,7 @@ func getParamString(item ConfigItem) (string, error) {
 	return retStr, nil
 }
 
+// getEnabledString returns enabled string
 func getEnabledString(item ConfigItem, config ConfiguratorConfig) string {
 	retStr := "&enabled="
 	if len(item.Type) == 0 || item.Type == GeneralType {
diff --git a/config_center/zookeeper/listener.go b/config_center/zookeeper/listener.go
index 747c4be352add3f549eaf02e83da6442a8a84c6a..bc6eb6d6eec4413515228a712cd11810e11f08cc 100644
--- a/config_center/zookeeper/listener.go
+++ b/config_center/zookeeper/listener.go
@@ -27,7 +27,7 @@ import (
 	"github.com/apache/dubbo-go/remoting"
 )
 
-// CacheListener ...
+// CacheListener defines keyListeners and rootPath
 type CacheListener struct {
 	keyListeners sync.Map
 	rootPath     string
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.go b/filter/filter.go
index d20ca72c345c6812f4bce6df5dbaf683429a9874..804bf3b9df8040c6377648f150c25d421b29b93d 100644
--- a/filter/filter.go
+++ b/filter/filter.go
@@ -27,7 +27,7 @@ import (
 // Filter interface defines the functions of a filter
 // Extension - Filter
 type Filter interface {
-	// Invoke is the core function of a filter, it determins the process of the filter
+	// Invoke is the core function of a filter, it determines the process of the filter
 	Invoke(context.Context, protocol.Invoker, protocol.Invocation) protocol.Result
 	// OnResponse updates the results from Invoke and then returns the modified results.
 	OnResponse(context.Context, protocol.Result, protocol.Invoker, protocol.Invocation) protocol.Result
diff --git a/filter/filter_impl/access_log_filter.go b/filter/filter_impl/access_log_filter.go
index 49cdc2287c28ae0cbbd0fcab3700536595bb0f5e..6eaf9cb00bafe8fb0d4b9f8cda50e6bc7115461b 100644
--- a/filter/filter_impl/access_log_filter.go
+++ b/filter/filter_impl/access_log_filter.go
@@ -36,20 +36,20 @@ import (
 const (
 	//used in URL.
 
-	// FileDateFormat ...
+	// nolint
 	FileDateFormat = "2006-01-02"
-	// MessageDateLayout ...
+	// nolint
 	MessageDateLayout = "2006-01-02 15:04:05"
-	// LogMaxBuffer ...
+	// nolint
 	LogMaxBuffer = 5000
-	// LogFileMode ...
+	// nolint
 	LogFileMode = 0600
 
 	// those fields are the data collected by this filter
 
-	// Types ...
+	// nolint
 	Types = "types"
-	// Arguments ...
+	// nolint
 	Arguments = "arguments"
 )
 
@@ -105,13 +105,27 @@ func (ef *AccessLogFilter) logIntoChannel(accessLogData AccessLogData) {
 func (ef *AccessLogFilter) buildAccessLogData(_ protocol.Invoker, invocation protocol.Invocation) map[string]string {
 	dataMap := make(map[string]string, 16)
 	attachments := invocation.Attachments()
-	dataMap[constant.INTERFACE_KEY] = attachments[constant.INTERFACE_KEY]
-	dataMap[constant.METHOD_KEY] = invocation.MethodName()
-	dataMap[constant.VERSION_KEY] = attachments[constant.VERSION_KEY]
-	dataMap[constant.GROUP_KEY] = attachments[constant.GROUP_KEY]
-	dataMap[constant.TIMESTAMP_KEY] = time.Now().Format(MessageDateLayout)
-	dataMap[constant.LOCAL_ADDR], _ = attachments[constant.LOCAL_ADDR]
-	dataMap[constant.REMOTE_ADDR], _ = attachments[constant.REMOTE_ADDR]
+	if v, ok := attachments[constant.INTERFACE_KEY]; ok && v != nil {
+		dataMap[constant.INTERFACE_KEY] = v.(string)
+	}
+	if v, ok := attachments[constant.METHOD_KEY]; ok && v != nil {
+		dataMap[constant.METHOD_KEY] = v.(string)
+	}
+	if v, ok := attachments[constant.VERSION_KEY]; ok && v != nil {
+		dataMap[constant.VERSION_KEY] = v.(string)
+	}
+	if v, ok := attachments[constant.GROUP_KEY]; ok && v != nil {
+		dataMap[constant.GROUP_KEY] = v.(string)
+	}
+	if v, ok := attachments[constant.TIMESTAMP_KEY]; ok && v != nil {
+		dataMap[constant.TIMESTAMP_KEY] = v.(string)
+	}
+	if v, ok := attachments[constant.LOCAL_ADDR]; ok && v != nil {
+		dataMap[constant.LOCAL_ADDR] = v.(string)
+	}
+	if v, ok := attachments[constant.REMOTE_ADDR]; ok && v != nil {
+		dataMap[constant.REMOTE_ADDR] = v.(string)
+	}
 
 	if len(invocation.Arguments()) > 0 {
 		builder := strings.Builder{}
diff --git a/filter/filter_impl/access_log_filter_test.go b/filter/filter_impl/access_log_filter_test.go
index 55c328cc30ae892c603fcc65034e48d2a52403d2..a3a6151aa1b6a933c57543248f3703125fa356d9 100644
--- a/filter/filter_impl/access_log_filter_test.go
+++ b/filter/filter_impl/access_log_filter_test.go
@@ -45,7 +45,7 @@ func TestAccessLogFilter_Invoke_Not_Config(t *testing.T) {
 			"service.filter=echo%2Ctoken%2Caccesslog&timestamp=1569153406&token=934804bf-b007-4174-94eb-96e3e1d60cc7&version=&warmup=100")
 	invoker := protocol.NewBaseInvoker(url)
 
-	attach := make(map[string]string, 10)
+	attach := make(map[string]interface{}, 10)
 	inv := invocation.NewRPCInvocation("MethodName", []interface{}{"OK", "Hello"}, attach)
 
 	accessLogFilter := GetAccessLogFilter()
@@ -64,7 +64,7 @@ func TestAccessLogFilterInvokeDefaultConfig(t *testing.T) {
 			"service.filter=echo%2Ctoken%2Caccesslog&timestamp=1569153406&token=934804bf-b007-4174-94eb-96e3e1d60cc7&version=&warmup=100")
 	invoker := protocol.NewBaseInvoker(url)
 
-	attach := make(map[string]string, 10)
+	attach := make(map[string]interface{}, 10)
 	attach[constant.VERSION_KEY] = "1.0"
 	attach[constant.GROUP_KEY] = "MyGroup"
 	inv := invocation.NewRPCInvocation("MethodName", []interface{}{"OK", "Hello"}, attach)
diff --git a/filter/filter_impl/active_filter_test.go b/filter/filter_impl/active_filter_test.go
index 6b72830e6a1a523b775b9294863ab18f8fe518a2..9837a49c72e28c7a7209f8af6059bdc30c222cc2 100644
--- a/filter/filter_impl/active_filter_test.go
+++ b/filter/filter_impl/active_filter_test.go
@@ -37,7 +37,7 @@ import (
 )
 
 func TestActiveFilterInvoke(t *testing.T) {
-	invoc := invocation.NewRPCInvocation("test", []interface{}{"OK"}, make(map[string]string, 0))
+	invoc := invocation.NewRPCInvocation("test", []interface{}{"OK"}, make(map[string]interface{}, 0))
 	url, _ := common.NewURL("dubbo://192.168.10.10:20000/com.ikurento.user.UserProvider")
 	filter := ActiveFilter{}
 	ctrl := gomock.NewController(t)
@@ -53,7 +53,7 @@ func TestActiveFilterInvoke(t *testing.T) {
 func TestActiveFilterOnResponse(t *testing.T) {
 	c := protocol.CurrentTimeMillis()
 	elapsed := 100
-	invoc := invocation.NewRPCInvocation("test", []interface{}{"OK"}, map[string]string{
+	invoc := invocation.NewRPCInvocation("test", []interface{}{"OK"}, map[string]interface{}{
 		dubboInvokeStartTime: strconv.FormatInt(c-int64(elapsed), 10),
 	})
 	url, _ := common.NewURL("dubbo://192.168.10.10:20000/com.ikurento.user.UserProvider")
diff --git a/filter/filter_impl/auth/default_authenticator_test.go b/filter/filter_impl/auth/default_authenticator_test.go
index 5b107b5960ff5adc383d52aa5e393d9fc6e71d14..8b0fb6b0e3de04ccb2c30d2c7e22c32af5733742 100644
--- a/filter/filter_impl/auth/default_authenticator_test.go
+++ b/filter/filter_impl/auth/default_authenticator_test.go
@@ -52,7 +52,7 @@ func TestDefaultAuthenticator_Authenticate(t *testing.T) {
 
 	var authenticator = &DefaultAuthenticator{}
 
-	invcation := invocation.NewRPCInvocation("test", parmas, map[string]string{
+	invcation := invocation.NewRPCInvocation("test", parmas, map[string]interface{}{
 		constant.REQUEST_SIGNATURE_KEY: signature,
 		constant.CONSUMER:              "test",
 		constant.REQUEST_TIMESTAMP_KEY: requestTime,
@@ -61,7 +61,7 @@ func TestDefaultAuthenticator_Authenticate(t *testing.T) {
 	err := authenticator.Authenticate(invcation, &testurl)
 	assert.Nil(t, err)
 	// modify the params
-	invcation = invocation.NewRPCInvocation("test", parmas[:1], map[string]string{
+	invcation = invocation.NewRPCInvocation("test", parmas[:1], map[string]interface{}{
 		constant.REQUEST_SIGNATURE_KEY: signature,
 		constant.CONSUMER:              "test",
 		constant.REQUEST_TIMESTAMP_KEY: requestTime,
@@ -119,7 +119,7 @@ func Test_getAccessKeyPairFailed(t *testing.T) {
 func Test_getSignatureWithinParams(t *testing.T) {
 	testurl, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=gg&version=2.6.0")
 	testurl.SetParam(constant.PARAMTER_SIGNATURE_ENABLE_KEY, "true")
-	inv := invocation.NewRPCInvocation("test", []interface{}{"OK"}, map[string]string{
+	inv := invocation.NewRPCInvocation("test", []interface{}{"OK"}, map[string]interface{}{
 		"": "",
 	})
 	secret := "dubbo"
diff --git a/filter/filter_impl/auth/provider_auth_test.go b/filter/filter_impl/auth/provider_auth_test.go
index 626782ae8390f046f441c1f162700a883e6f22d0..f6ebfcd7abb83a55b4a09c1ec2d6c71b88bf7727 100644
--- a/filter/filter_impl/auth/provider_auth_test.go
+++ b/filter/filter_impl/auth/provider_auth_test.go
@@ -54,7 +54,7 @@ func TestProviderAuthFilter_Invoke(t *testing.T) {
 	requestTime := strconv.Itoa(int(time.Now().Unix() * 1000))
 	signature, _ := getSignature(&url, inv, secret, requestTime)
 
-	inv = invocation.NewRPCInvocation("test", []interface{}{"OK"}, map[string]string{
+	inv = invocation.NewRPCInvocation("test", []interface{}{"OK"}, map[string]interface{}{
 		constant.REQUEST_SIGNATURE_KEY: signature,
 		constant.CONSUMER:              "test",
 		constant.REQUEST_TIMESTAMP_KEY: requestTime,
diff --git a/filter/filter_impl/echo_filter.go b/filter/filter_impl/echo_filter.go
index 7da5ec7029ea698b1bf1a14ad36123fbec3aacf7..24ba6e47acbffd28eedfffcd0dbaf9717e222946 100644
--- a/filter/filter_impl/echo_filter.go
+++ b/filter/filter_impl/echo_filter.go
@@ -65,7 +65,7 @@ func (ef *EchoFilter) OnResponse(_ context.Context, result protocol.Result, _ pr
 	return result
 }
 
-// GetFilter ...
+// GetFilter gets the Filter
 func GetFilter() filter.Filter {
 	return &EchoFilter{}
 }
diff --git a/filter/filter_impl/execute_limit_filter.go b/filter/filter_impl/execute_limit_filter.go
index bfc5096ca089867f6e6234089e387d3f9b48a3aa..5fc309cfb49fbeef5e933a84c1b80654087da701 100644
--- a/filter/filter_impl/execute_limit_filter.go
+++ b/filter/filter_impl/execute_limit_filter.go
@@ -74,7 +74,7 @@ type ExecuteLimitFilter struct {
 	executeState *concurrent.Map
 }
 
-// ExecuteState ...
+// ExecuteState defines the concurrent count
 type ExecuteState struct {
 	concurrentCount int64
 }
diff --git a/filter/filter_impl/execute_limit_filter_test.go b/filter/filter_impl/execute_limit_filter_test.go
index d36d6edef1ec52c24a9ccd64233b4620b4f10bc7..953f5e1cc8f6ff3299dcac21c5cd2a41de08cdc1 100644
--- a/filter/filter_impl/execute_limit_filter_test.go
+++ b/filter/filter_impl/execute_limit_filter_test.go
@@ -36,7 +36,7 @@ import (
 
 func TestExecuteLimitFilterInvokeIgnored(t *testing.T) {
 	methodName := "hello"
-	invoc := invocation.NewRPCInvocation(methodName, []interface{}{"OK"}, make(map[string]string, 0))
+	invoc := invocation.NewRPCInvocation(methodName, []interface{}{"OK"}, make(map[string]interface{}, 0))
 
 	invokeUrl := common.NewURLWithOptions(
 		common.WithParams(url.Values{}),
@@ -51,7 +51,7 @@ func TestExecuteLimitFilterInvokeIgnored(t *testing.T) {
 
 func TestExecuteLimitFilterInvokeConfigureError(t *testing.T) {
 	methodName := "hello1"
-	invoc := invocation.NewRPCInvocation(methodName, []interface{}{"OK"}, make(map[string]string, 0))
+	invoc := invocation.NewRPCInvocation(methodName, []interface{}{"OK"}, make(map[string]interface{}, 0))
 
 	invokeUrl := common.NewURLWithOptions(
 		common.WithParams(url.Values{}),
@@ -68,7 +68,7 @@ func TestExecuteLimitFilterInvokeConfigureError(t *testing.T) {
 
 func TestExecuteLimitFilterInvoke(t *testing.T) {
 	methodName := "hello1"
-	invoc := invocation.NewRPCInvocation(methodName, []interface{}{"OK"}, make(map[string]string, 0))
+	invoc := invocation.NewRPCInvocation(methodName, []interface{}{"OK"}, make(map[string]interface{}, 0))
 
 	invokeUrl := common.NewURLWithOptions(
 		common.WithParams(url.Values{}),
diff --git a/filter/filter_impl/generic_filter.go b/filter/filter_impl/generic_filter.go
index 3f4d714e6b0cbdf48f5e1afce3222a18857041f9..d385054ed98518177aea5e574e034cb35c72e398 100644
--- a/filter/filter_impl/generic_filter.go
+++ b/filter/filter_impl/generic_filter.go
@@ -47,7 +47,7 @@ func init() {
 
 //  when do a generic invoke, struct need to be map
 
-// GenericFilter ...
+// nolint
 type GenericFilter struct{}
 
 // Invoke turns the parameters to map for generic method
diff --git a/filter/filter_impl/generic_service_filter.go b/filter/filter_impl/generic_service_filter.go
index 6272df6b39b0c18a77721f3a8c9e92618133aa6c..3711e68cce67dacfec074a5e44c080a629bce305 100644
--- a/filter/filter_impl/generic_service_filter.go
+++ b/filter/filter_impl/generic_service_filter.go
@@ -40,9 +40,9 @@ import (
 )
 
 const (
-	// GENERIC_SERVICE ...
+	// GENERIC_SERVICE defines the filter name
 	GENERIC_SERVICE = "generic_service"
-	// GENERIC_SERIALIZATION_DEFAULT ...
+	// nolint
 	GENERIC_SERIALIZATION_DEFAULT = "true"
 )
 
@@ -50,10 +50,10 @@ func init() {
 	extension.SetFilter(GENERIC_SERVICE, GetGenericServiceFilter)
 }
 
-// GenericServiceFilter ...
+// nolint
 type GenericServiceFilter struct{}
 
-// Invoke ...
+// Invoke is used to call service method by invocation
 func (ef *GenericServiceFilter) Invoke(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
 	logger.Infof("invoking generic service filter.")
 	logger.Debugf("generic service filter methodName:%v,args:%v", invocation.MethodName(), len(invocation.Arguments()))
@@ -115,7 +115,7 @@ func (ef *GenericServiceFilter) Invoke(ctx context.Context, invoker protocol.Inv
 	return invoker.Invoke(ctx, newInvocation)
 }
 
-// OnResponse ...
+// nolint
 func (ef *GenericServiceFilter) OnResponse(ctx context.Context, result protocol.Result, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
 	if invocation.MethodName() == constant.GENERIC && len(invocation.Arguments()) == 3 && result.Result() != nil {
 		v := reflect.ValueOf(result.Result())
@@ -127,7 +127,7 @@ func (ef *GenericServiceFilter) OnResponse(ctx context.Context, result protocol.
 	return result
 }
 
-// GetGenericServiceFilter ...
+// nolint
 func GetGenericServiceFilter() filter.Filter {
 	return &GenericServiceFilter{}
 }
diff --git a/filter/filter_impl/generic_service_filter_test.go b/filter/filter_impl/generic_service_filter_test.go
index f0bdb7fb334f31de27d32d79124e4cb8c03f45a0..67819717cf07383373f905ed9420a42cc0bfddb9 100644
--- a/filter/filter_impl/generic_service_filter_test.go
+++ b/filter/filter_impl/generic_service_filter_test.go
@@ -51,7 +51,7 @@ func (c *TestStruct) JavaClassName() string {
 
 type TestService struct{}
 
-// MethodOne ...
+// nolint
 func (ts *TestService) MethodOne(_ context.Context, test1 *TestStruct, test2 []TestStruct,
 	test3 interface{}, test4 []interface{}, test5 *string) (*TestStruct, error) {
 	if test1 == nil {
@@ -72,7 +72,7 @@ func (ts *TestService) MethodOne(_ context.Context, test1 *TestStruct, test2 []T
 	return &TestStruct{}, nil
 }
 
-// Reference ...
+// nolint
 func (*TestService) Reference() string {
 	return "com.test.Path"
 }
diff --git a/filter/filter_impl/graceful_shutdown_filter_test.go b/filter/filter_impl/graceful_shutdown_filter_test.go
index 87ac2eac616a20617b7a5e68254a1f47ecb8ac17..220ef6f20857aa06bcc45d3ac0a9bd52b0d65af2 100644
--- a/filter/filter_impl/graceful_shutdown_filter_test.go
+++ b/filter/filter_impl/graceful_shutdown_filter_test.go
@@ -39,7 +39,7 @@ import (
 )
 
 func TestGenericFilterInvoke(t *testing.T) {
-	invoc := invocation.NewRPCInvocation("GetUser", []interface{}{"OK"}, make(map[string]string, 0))
+	invoc := invocation.NewRPCInvocation("GetUser", []interface{}{"OK"}, make(map[string]interface{}, 0))
 
 	invokeUrl := common.NewURLWithOptions(
 		common.WithParams(url.Values{}))
diff --git a/filter/filter_impl/hystrix_filter.go b/filter/filter_impl/hystrix_filter.go
index 711ef71c44192c5a1d76783a3b3d4cbd0b97632c..d13e02c06f7b2191e4d01c013a72300e1b095616 100644
--- a/filter/filter_impl/hystrix_filter.go
+++ b/filter/filter_impl/hystrix_filter.go
@@ -37,11 +37,11 @@ import (
 )
 
 const (
-	// HYSTRIX_CONSUMER ...
+	// nolint
 	HYSTRIX_CONSUMER = "hystrix_consumer"
-	// HYSTRIX_PROVIDER ...
+	// nolint
 	HYSTRIX_PROVIDER = "hystrix_provider"
-	// HYSTRIX ...
+	// nolint
 	HYSTRIX = "hystrix"
 )
 
@@ -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,14 +81,54 @@ func NewHystrixFilterError(err error, failByHystrix bool) error {
 	}
 }
 
-// HystrixFilter ...
+/**
+ * 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
 	ifNewMap sync.Map
 }
 
-// Invoke is an implentation of filter, provides Hystrix pattern latency and fault tolerance
+// Invoke is an implementation of filter, provides Hystrix pattern latency and fault tolerance
 func (hf *HystrixFilter) Invoke(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
 	cmdName := fmt.Sprintf("%s&method=%s", invoker.GetUrl().Key(), invocation.MethodName())
 
@@ -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 {
@@ -256,7 +292,7 @@ func initHystrixConfigProvider() error {
 //	return initHystrixConfig()
 //}
 
-// CommandConfigWithError ...
+// nolint
 type CommandConfigWithError struct {
 	Timeout                int      `yaml:"timeout"`
 	MaxConcurrentRequests  int      `yaml:"max_concurrent_requests"`
@@ -274,14 +310,14 @@ type CommandConfigWithError struct {
 //- ErrorPercentThreshold: it causes circuits to open once the rolling measure of errors exceeds this percent of requests
 //See hystrix doc
 
-// HystrixFilterConfig ...
+// nolint
 type HystrixFilterConfig struct {
 	Configs  map[string]*CommandConfigWithError
 	Default  string
 	Services map[string]ServiceHystrixConfig
 }
 
-// ServiceHystrixConfig ...
+// nolint
 type ServiceHystrixConfig struct {
 	ServiceConfig string `yaml:"service_config"`
 	Methods       map[string]string
diff --git a/filter/filter_impl/metrics_filter_test.go b/filter/filter_impl/metrics_filter_test.go
index 881106f4bc5a7890569be347122da5144e440c8b..ac10d52cf3c156e3580760a4409bab49bb4d0c4f 100644
--- a/filter/filter_impl/metrics_filter_test.go
+++ b/filter/filter_impl/metrics_filter_test.go
@@ -57,7 +57,7 @@ func TestMetricsFilterInvoke(t *testing.T) {
 			"service.filter=echo%2Ctoken%2Caccesslog&timestamp=1569153406&token=934804bf-b007-4174-94eb-96e3e1d60cc7&version=&warmup=100")
 	invoker := protocol.NewBaseInvoker(url)
 
-	attach := make(map[string]string, 10)
+	attach := make(map[string]interface{}, 10)
 	inv := invocation.NewRPCInvocation("MethodName", []interface{}{"OK", "Hello"}, attach)
 
 	ctx := context.Background()
diff --git a/filter/filter_impl/seata_filter.go b/filter/filter_impl/seata_filter.go
new file mode 100644
index 0000000000000000000000000000000000000000..7722d2954f905ece4a1b48628c31c06debf45614
--- /dev/null
+++ b/filter/filter_impl/seata_filter.go
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package filter_impl
+
+import (
+	"context"
+	"strings"
+)
+
+import (
+	"github.com/apache/dubbo-go/common/extension"
+	"github.com/apache/dubbo-go/common/logger"
+	"github.com/apache/dubbo-go/filter"
+	"github.com/apache/dubbo-go/protocol"
+)
+
+const (
+	SEATA     = "seata"
+	SEATA_XID = "SEATA_XID"
+)
+
+func init() {
+	extension.SetFilter(SEATA, getSeataFilter)
+}
+
+// SeataFilter when use seata-golang, use this filter to transfer xid
+type SeataFilter struct{}
+
+// When use Seata, transfer xid by attachments
+// Invoke Get Xid by attachment key `SEATA_XID`
+func (sf *SeataFilter) Invoke(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
+	logger.Infof("invoking seata filter.")
+	xid := invocation.AttachmentsByKey(SEATA_XID, "")
+	if strings.TrimSpace(xid) != "" {
+		logger.Debugf("Method: %v,Xid: %v", invocation.MethodName(), xid)
+		return invoker.Invoke(context.WithValue(ctx, SEATA_XID, xid), invocation)
+	}
+	return invoker.Invoke(ctx, invocation)
+}
+
+// OnResponse dummy process, returns the result directly
+func (sf *SeataFilter) OnResponse(ctx context.Context, result protocol.Result, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
+	return result
+}
+
+// getSeataFilter create SeataFilter instance
+func getSeataFilter() filter.Filter {
+	return &SeataFilter{}
+}
diff --git a/filter/filter_impl/seata_filter_test.go b/filter/filter_impl/seata_filter_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..45817e95cbd2eaa7365adc8a299523af8310f797
--- /dev/null
+++ b/filter/filter_impl/seata_filter_test.go
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package filter_impl
+
+import (
+	"context"
+	"testing"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+import (
+	"github.com/apache/dubbo-go/protocol"
+	"github.com/apache/dubbo-go/protocol/invocation"
+)
+
+type testMockSeataInvoker struct {
+	protocol.BaseInvoker
+}
+
+func (iv *testMockSeataInvoker) Invoke(ctx context.Context, _ protocol.Invocation) protocol.Result {
+	val := ctx.Value(SEATA_XID)
+	if val != nil {
+		xid, ok := val.(string)
+		if ok {
+			return &protocol.RPCResult{Rest: xid}
+		}
+	}
+	return &protocol.RPCResult{}
+}
+
+func TestSeataFilter_Invoke(t *testing.T) {
+	filter := getSeataFilter()
+	result := filter.Invoke(context.Background(), &testMockSeataInvoker{}, invocation.NewRPCInvocation("$echo",
+		[]interface{}{"OK"}, map[string]interface{}{
+			SEATA_XID: "10.30.21.227:8091:2000047792",
+		}))
+	assert.Equal(t, "10.30.21.227:8091:2000047792", result.Result())
+}
diff --git a/filter/filter_impl/token_filter.go b/filter/filter_impl/token_filter.go
index 23742c66e94d9ecfc09d004441a54aad86ef049e..b5e05605c29905d9e66a63129f064c2f844f0e71 100644
--- a/filter/filter_impl/token_filter.go
+++ b/filter/filter_impl/token_filter.go
@@ -34,7 +34,7 @@ import (
 )
 
 const (
-	// TOKEN ...
+	// nolint
 	TOKEN = "token"
 )
 
@@ -51,7 +51,7 @@ func (tf *TokenFilter) Invoke(ctx context.Context, invoker protocol.Invoker, inv
 	if len(invokerTkn) > 0 {
 		attachs := invocation.Attachments()
 		remoteTkn, exist := attachs[constant.TOKEN_KEY]
-		if exist && strings.EqualFold(invokerTkn, remoteTkn) {
+		if exist && remoteTkn != nil && strings.EqualFold(invokerTkn, remoteTkn.(string)) {
 			return invoker.Invoke(ctx, invocation)
 		}
 		return &protocol.RPCResult{Err: perrors.Errorf("Invalid token! Forbid invoke remote service %v method %s ",
@@ -66,7 +66,7 @@ func (tf *TokenFilter) OnResponse(ctx context.Context, result protocol.Result, i
 	return result
 }
 
-// GetTokenFilter ...
+// nolint
 func GetTokenFilter() filter.Filter {
 	return &TokenFilter{}
 }
diff --git a/filter/filter_impl/token_filter_test.go b/filter/filter_impl/token_filter_test.go
index c2f69bd03941b1404585dc5842c56eb2bf3c918f..cd1bba3d4a830822c67f1e6157653d5477264c94 100644
--- a/filter/filter_impl/token_filter_test.go
+++ b/filter/filter_impl/token_filter_test.go
@@ -40,7 +40,7 @@ func TestTokenFilterInvoke(t *testing.T) {
 	url := common.NewURLWithOptions(
 		common.WithParams(url.Values{}),
 		common.WithParamsValue(constant.TOKEN_KEY, "ori_key"))
-	attch := make(map[string]string, 0)
+	attch := make(map[string]interface{}, 0)
 	attch[constant.TOKEN_KEY] = "ori_key"
 	result := filter.Invoke(context.Background(),
 		protocol.NewBaseInvoker(*url),
@@ -54,7 +54,7 @@ func TestTokenFilterInvokeEmptyToken(t *testing.T) {
 	filter := GetTokenFilter()
 
 	testUrl := common.URL{}
-	attch := make(map[string]string, 0)
+	attch := make(map[string]interface{}, 0)
 	attch[constant.TOKEN_KEY] = "ori_key"
 	result := filter.Invoke(context.Background(), protocol.NewBaseInvoker(testUrl), invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch))
 	assert.Nil(t, result.Error())
@@ -67,7 +67,7 @@ func TestTokenFilterInvokeEmptyAttach(t *testing.T) {
 	testUrl := common.NewURLWithOptions(
 		common.WithParams(url.Values{}),
 		common.WithParamsValue(constant.TOKEN_KEY, "ori_key"))
-	attch := make(map[string]string, 0)
+	attch := make(map[string]interface{}, 0)
 	result := filter.Invoke(context.Background(), protocol.NewBaseInvoker(*testUrl), invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch))
 	assert.NotNil(t, result.Error())
 }
@@ -78,7 +78,7 @@ func TestTokenFilterInvokeNotEqual(t *testing.T) {
 	testUrl := common.NewURLWithOptions(
 		common.WithParams(url.Values{}),
 		common.WithParamsValue(constant.TOKEN_KEY, "ori_key"))
-	attch := make(map[string]string, 0)
+	attch := make(map[string]interface{}, 0)
 	attch[constant.TOKEN_KEY] = "err_key"
 	result := filter.Invoke(context.Background(),
 		protocol.NewBaseInvoker(*testUrl), invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch))
diff --git a/filter/filter_impl/tps/tps_limit_fix_window_strategy.go b/filter/filter_impl/tps/tps_limit_fix_window_strategy.go
index 7419a4576122d4db334969b0711666b5b2816e60..d495e035de521612cc9b85ec9e817ae3355a818b 100644
--- a/filter/filter_impl/tps/tps_limit_fix_window_strategy.go
+++ b/filter/filter_impl/tps/tps_limit_fix_window_strategy.go
@@ -29,7 +29,7 @@ import (
 )
 
 const (
-	// FixedWindowKey ...
+	// FixedWindowKey defines tps limit algorithm
 	FixedWindowKey = "fixedWindow"
 )
 
diff --git a/filter/filter_impl/tps/tps_limit_strategy_mock.go b/filter/filter_impl/tps/tps_limit_strategy_mock.go
index c228c7349ce6ad305051e0ba9b26dddee8c12c71..be76466092b48022330a6fb35ba99bbf0d03134e 100644
--- a/filter/filter_impl/tps/tps_limit_strategy_mock.go
+++ b/filter/filter_impl/tps/tps_limit_strategy_mock.go
@@ -23,6 +23,9 @@ package tps
 
 import (
 	gomock "github.com/golang/mock/gomock"
+)
+
+import (
 	reflect "reflect"
 )
 
diff --git a/filter/filter_impl/tps/tps_limiter_method_service_test.go b/filter/filter_impl/tps/tps_limiter_method_service_test.go
index edae99ec2d3157ad7f0d81c95a2fb181410475fa..61f28e442f4b76d18d7750aa58831c322f939207 100644
--- a/filter/filter_impl/tps/tps_limiter_method_service_test.go
+++ b/filter/filter_impl/tps/tps_limiter_method_service_test.go
@@ -36,7 +36,7 @@ import (
 
 func TestMethodServiceTpsLimiterImplIsAllowableOnlyServiceLevel(t *testing.T) {
 	methodName := "hello"
-	invoc := invocation.NewRPCInvocation(methodName, []interface{}{"OK"}, make(map[string]string, 0))
+	invoc := invocation.NewRPCInvocation(methodName, []interface{}{"OK"}, make(map[string]interface{}, 0))
 
 	ctrl := gomock.NewController(t)
 	defer ctrl.Finish()
@@ -63,7 +63,7 @@ func TestMethodServiceTpsLimiterImplIsAllowableOnlyServiceLevel(t *testing.T) {
 
 func TestMethodServiceTpsLimiterImplIsAllowableNoConfig(t *testing.T) {
 	methodName := "hello1"
-	invoc := invocation.NewRPCInvocation(methodName, []interface{}{"OK"}, make(map[string]string, 0))
+	invoc := invocation.NewRPCInvocation(methodName, []interface{}{"OK"}, make(map[string]interface{}, 0))
 	// ctrl := gomock.NewController(t)
 	// defer ctrl.Finish()
 
@@ -80,7 +80,7 @@ func TestMethodServiceTpsLimiterImplIsAllowableNoConfig(t *testing.T) {
 func TestMethodServiceTpsLimiterImplIsAllowableMethodLevelOverride(t *testing.T) {
 	methodName := "hello2"
 	methodConfigPrefix := "methods." + methodName + "."
-	invoc := invocation.NewRPCInvocation(methodName, []interface{}{"OK"}, make(map[string]string, 0))
+	invoc := invocation.NewRPCInvocation(methodName, []interface{}{"OK"}, make(map[string]interface{}, 0))
 	ctrl := gomock.NewController(t)
 	defer ctrl.Finish()
 
@@ -113,7 +113,7 @@ func TestMethodServiceTpsLimiterImplIsAllowableMethodLevelOverride(t *testing.T)
 func TestMethodServiceTpsLimiterImplIsAllowableBothMethodAndService(t *testing.T) {
 	methodName := "hello3"
 	methodConfigPrefix := "methods." + methodName + "."
-	invoc := invocation.NewRPCInvocation(methodName, []interface{}{"OK"}, make(map[string]string, 0))
+	invoc := invocation.NewRPCInvocation(methodName, []interface{}{"OK"}, make(map[string]interface{}, 0))
 	ctrl := gomock.NewController(t)
 	defer ctrl.Finish()
 
diff --git a/filter/filter_impl/tps_limit_filter_test.go b/filter/filter_impl/tps_limit_filter_test.go
index 274e4e6de61b94079e9ad3b2f7a5bcd79a276cc6..da0fc482ce559dad52947786823f604128857c30 100644
--- a/filter/filter_impl/tps_limit_filter_test.go
+++ b/filter/filter_impl/tps_limit_filter_test.go
@@ -44,7 +44,7 @@ func TestTpsLimitFilterInvokeWithNoTpsLimiter(t *testing.T) {
 	invokeUrl := common.NewURLWithOptions(
 		common.WithParams(url.Values{}),
 		common.WithParamsValue(constant.TPS_LIMITER_KEY, ""))
-	attch := make(map[string]string, 0)
+	attch := make(map[string]interface{}, 0)
 
 	result := tpsFilter.Invoke(context.Background(),
 		protocol.NewBaseInvoker(*invokeUrl),
@@ -68,7 +68,7 @@ func TestGenericFilterInvokeWithDefaultTpsLimiter(t *testing.T) {
 	invokeUrl := common.NewURLWithOptions(
 		common.WithParams(url.Values{}),
 		common.WithParamsValue(constant.TPS_LIMITER_KEY, constant.DEFAULT_KEY))
-	attch := make(map[string]string, 0)
+	attch := make(map[string]interface{}, 0)
 
 	result := tpsFilter.Invoke(context.Background(),
 		protocol.NewBaseInvoker(*invokeUrl),
@@ -99,7 +99,7 @@ func TestGenericFilterInvokeWithDefaultTpsLimiterNotAllow(t *testing.T) {
 	invokeUrl := common.NewURLWithOptions(
 		common.WithParams(url.Values{}),
 		common.WithParamsValue(constant.TPS_LIMITER_KEY, constant.DEFAULT_KEY))
-	attch := make(map[string]string, 0)
+	attch := make(map[string]interface{}, 0)
 
 	result := tpsFilter.Invoke(context.Background(),
 		protocol.NewBaseInvoker(*invokeUrl), invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch))
diff --git a/filter/filter_impl/tracing_filter_test.go b/filter/filter_impl/tracing_filter_test.go
index 15dc32e7ec1d30936b37ab2350c899e5c30d2ab7..e159b7400d46069018a00a849319423285072dc2 100644
--- a/filter/filter_impl/tracing_filter_test.go
+++ b/filter/filter_impl/tracing_filter_test.go
@@ -26,12 +26,9 @@ import (
 	"github.com/opentracing/opentracing-go"
 )
 
-import (
-	"github.com/apache/dubbo-go/common/constant"
-)
-
 import (
 	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/common/constant"
 	"github.com/apache/dubbo-go/protocol"
 	"github.com/apache/dubbo-go/protocol/invocation"
 )
@@ -45,7 +42,7 @@ func TestTracingFilterInvoke(t *testing.T) {
 			"service.filter=echo%2Ctoken%2Caccesslog&timestamp=1569153406&token=934804bf-b007-4174-94eb-96e3e1d60cc7&version=&warmup=100")
 	invoker := protocol.NewBaseInvoker(url)
 
-	attach := make(map[string]string, 10)
+	attach := make(map[string]interface{}, 10)
 	inv := invocation.NewRPCInvocation("MethodName", []interface{}{"OK", "Hello"}, attach)
 	ctx := context.Background()
 	tf := newTracingFilter()
diff --git a/go.mod b/go.mod
index e82a04b279ef16297d029b2a3993b5327b23c804..9e18cc276aca3e45c58e33b7e9c27cae5afcfdce 100644
--- a/go.mod
+++ b/go.mod
@@ -1,61 +1,66 @@
 module github.com/apache/dubbo-go
 
 require (
+	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/aliyun/aliyun-oss-go-sdk v0.0.0-20190307165228-86c17b95fcd5 // indirect
-	github.com/apache/dubbo-go-hessian2 v1.6.1
-	github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f // indirect
+	github.com/apache/dubbo-getty v1.3.10
+	github.com/apache/dubbo-go-hessian2 v1.6.0-rc1.0.20200906044240-6c1fb5c3bd44
 	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/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f // indirect
 	github.com/creasty/defaults v1.3.0
-	github.com/dubbogo/getty v1.3.7
+	github.com/docker/go-connections v0.4.0 // indirect
 	github.com/dubbogo/go-zookeeper v1.0.1
-	github.com/dubbogo/gost v1.9.0
+	github.com/dubbogo/gost v1.9.1
+	github.com/elazarl/go-bindata-assetfs v1.0.0 // indirect
 	github.com/emicklei/go-restful/v3 v3.0.0
+	github.com/frankban/quicktest v1.4.1 // indirect
+	github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 // indirect
 	github.com/go-co-op/gocron v0.1.1
 	github.com/go-resty/resty/v2 v2.1.0
 	github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 // indirect
 	github.com/golang/mock v1.3.1
 	github.com/golang/protobuf v1.3.2
-	github.com/google/btree v1.0.0 // indirect
-	github.com/grpc-ecosystem/go-grpc-middleware v1.0.0 // indirect
-	github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
+	github.com/google/go-cmp v0.3.1 // indirect
+	github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4 // indirect
 	github.com/grpc-ecosystem/grpc-gateway v1.9.5 // indirect
 	github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645
-	github.com/hashicorp/consul v1.5.3
-	github.com/hashicorp/consul/api v1.1.0
-	github.com/hashicorp/vault v0.10.3
+	github.com/hashicorp/consul v1.8.0
+	github.com/hashicorp/consul/api v1.5.0
+	github.com/hashicorp/go-raftchunking v0.6.3-0.20191002164813-7e9e8525653a // indirect
+	github.com/hashicorp/golang-lru v0.5.3 // indirect
+	github.com/hashicorp/vault/api v1.0.5-0.20191108163347-bdd38fca2cff // indirect
+	github.com/hashicorp/vault/sdk v0.1.14-0.20191112033314-390e96e22eb2
 	github.com/jinzhu/copier v0.0.0-20190625015134-976e0346caa8
-	github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8 // indirect
-	github.com/juju/testing v0.0.0-20191001232224-ce9dec17d28b // indirect
 	github.com/magiconair/properties v1.8.1
-	github.com/mitchellh/mapstructure v1.1.2
+	github.com/mitchellh/hashstructure v1.0.0 // indirect
+	github.com/mitchellh/mapstructure v1.2.3
 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd
-	github.com/nacos-group/nacos-sdk-go v0.3.3-0.20200617023039-50c7537d6a5f
+	github.com/nacos-group/nacos-sdk-go v1.0.0
 	github.com/opentracing/opentracing-go v1.1.0
+	github.com/pierrec/lz4 v2.2.6+incompatible // indirect
 	github.com/pkg/errors v0.9.1
 	github.com/prometheus/client_golang v1.1.0
 	github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b
-	github.com/smartystreets/goconvey v0.0.0-20190710185942-9d28bd7c0945 // indirect
-	github.com/soheilhy/cmux v0.1.4 // indirect
+	github.com/shirou/gopsutil v2.19.9+incompatible // indirect
+	github.com/stretchr/objx v0.2.0 // indirect
 	github.com/stretchr/testify v1.5.1
-	github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 // indirect
-	github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect
-	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
-	golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 // indirect
-	google.golang.org/grpc v1.22.1
-	gopkg.in/yaml.v2 v2.2.2
-	k8s.io/api v0.0.0-20190325185214-7544f9db76f6
-	k8s.io/apimachinery v0.0.0-20190223001710-c182ff3b9841
-	k8s.io/client-go v8.0.0+incompatible
+	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
+
+replace launchpad.net/gocheck => github.com/go-check/check v0.0.0-20140225173054-eb6ee6f84d0a
diff --git a/go.sum b/go.sum
index 8c8c4ef132e2c4fe5a9631f74fcabc8cae9767c9..f744fc313ba5dd7932f330ccb3d3e50a054a15a3 100644
--- a/go.sum
+++ b/go.sum
@@ -1,30 +1,65 @@
-cloud.google.com/go v0.26.0 h1:e0WKqKTd5BnrG8aKH3J3h+QvEIQtSUcf2n5UZ5ZgLtQ=
 cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
-github.com/Azure/azure-sdk-for-go v16.0.0+incompatible h1:gr1qKY/Ll72VjFTZmaBwRK1yQHAxCnV25ekOKroc9ws=
-github.com/Azure/azure-sdk-for-go v16.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
-github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
-github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
-github.com/Azure/go-autorest v10.7.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
-github.com/Azure/go-autorest v10.15.3+incompatible h1:nhKI/bvazIs3C3TFGoSqKY6hZ8f5od5mb5/UcS6HVIY=
-github.com/Azure/go-autorest v10.15.3+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
+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.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=
+github.com/Azure/go-autorest/autorest v0.9.3/go.mod h1:GsRuLYvwzLjjjRoWEIyMUaYq8GNUx2nRB378IPt/1p0=
+github.com/Azure/go-autorest/autorest v0.10.0 h1:mvdtztBqcL8se7MdrUweNieTNi4kfNG6GOJuurQJpuY=
+github.com/Azure/go-autorest/autorest v0.10.0/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630=
+github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
+github.com/Azure/go-autorest/autorest/adal v0.8.0/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc=
+github.com/Azure/go-autorest/autorest/adal v0.8.1/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q=
+github.com/Azure/go-autorest/autorest/adal v0.8.2 h1:O1X4oexUxnZCaEUGsvMnr8ZGj8HI37tNezwY4npRqA0=
+github.com/Azure/go-autorest/autorest/adal v0.8.2/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q=
+github.com/Azure/go-autorest/autorest/azure/auth v0.4.2 h1:iM6UAvjR97ZIeR93qTcwpKNMpV+/FTWjwEbuPD495Tk=
+github.com/Azure/go-autorest/autorest/azure/auth v0.4.2/go.mod h1:90gmfKdlmKgfjUpnCEpOJzsUEjrWDSLwHIG73tSXddM=
+github.com/Azure/go-autorest/autorest/azure/cli v0.3.1 h1:LXl088ZQlP0SBppGFsRZonW6hSvwgL5gRByMbvUbx8U=
+github.com/Azure/go-autorest/autorest/azure/cli v0.3.1/go.mod h1:ZG5p860J94/0kI9mNJVoIoLgXcirM2gF5i2kWloofxw=
+github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
+github.com/Azure/go-autorest/autorest/date v0.2.0 h1:yW+Zlqf26583pE43KhfnhFcdmSWlm5Ew6bxipnr/tbM=
+github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g=
+github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
+github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
+github.com/Azure/go-autorest/autorest/mocks v0.3.0 h1:qJumjCaCudz+OcqE9/XtEPfvtOjOmKaui4EOpFI6zZc=
+github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM=
+github.com/Azure/go-autorest/autorest/to v0.3.0 h1:zebkZaadz7+wIQYgC7GXaz3Wb28yKYfVkkBKwc38VF8=
+github.com/Azure/go-autorest/autorest/to v0.3.0/go.mod h1:MgwOyqaIuKdG4TL/2ywSsIWKAfJfgHDo8ObuUk3t5sA=
+github.com/Azure/go-autorest/autorest/validation v0.2.0 h1:15vMO4y76dehZSq7pAaOLQxC6dZYsSrj2GQpflyM/L4=
+github.com/Azure/go-autorest/autorest/validation v0.2.0/go.mod h1:3EEqHnBxQGHXRYq3HT1WyXAvT7LLY3tl70hw6tQIbjI=
+github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1GnWeHDdaNKY=
+github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
+github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VYyQflFE619k=
+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/DataDog/datadog-go v2.2.0+incompatible h1:V5BKkxACZLjzHjSgBbr2gvLA2Ae49yhc6CSY7MLy5k4=
+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/Jeffail/gabs v1.1.0 h1:kw5zCcl9tlJNHTDme7qbi21fDHZmXrnjMoXos3Jw/NI=
-github.com/Jeffail/gabs v1.1.0/go.mod h1:6xMvQMK4k33lb7GUUpaAPh6nKMmemQeg5d4gn7/bOXc=
-github.com/Microsoft/go-winio v0.4.3 h1:M3NHMuPgMSUPdE5epwNUHlRPSVzHs8HpRTrVXhR0myo=
+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=
 github.com/Microsoft/go-winio v0.4.3/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
+github.com/Microsoft/go-winio v0.4.13 h1:Hmi80lzZuI/CaYmlJp/b+FjZdRZhKu9c2mDVqKlLWVs=
+github.com/Microsoft/go-winio v0.4.13/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
 github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
-github.com/NYTimes/gziphandler v1.0.1 h1:iLrQrdwjDd52kHDA5op2UBJFjmOb9g+7scBan4RN8F0=
 github.com/NYTimes/gziphandler v1.0.1/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
-github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw=
-github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk=
+github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I=
+github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
+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/SAP/go-hdb v0.12.0 h1:5hBQZ2jjyZ268qjDmoDZJuCyLzR6oRLI60eYzmTW9m4=
-github.com/SAP/go-hdb v0.12.0/go.mod h1:etBT+FAi1t5k3K3tf5vQTnosgYmhDkRi8jEnQqCnxF0=
-github.com/SermoDigital/jose v0.0.0-20180104203859-803625baeddc h1:LkkwnbY+S8WmwkWq1SVyRWMH9nYWO1P5XN3OD1tts/w=
-github.com/SermoDigital/jose v0.0.0-20180104203859-803625baeddc/go.mod h1:ARgCUhI1MHQH+ONky/PAtmVHQrP5JlGY0F3poXOp/fA=
+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=
@@ -35,97 +70,94 @@ github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5 h1:rFw4nCn9iMW+Vaj
 github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
 github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
 github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
-github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190802083043-4cd0c391755e/go.mod h1:myCDvQSzCW+wB1WAlocEru4wMGJxy+vlxHdhegi1CDQ=
 github.com/aliyun/alibaba-cloud-sdk-go v1.61.18 h1:zOVTBdCKFd9JbCKz9/nt+FovbjPFmb7mUnp8nH9fQBA=
-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/aliyun/alibaba-cloud-sdk-go v1.61.18/go.mod h1:v8ESoHo4SyHmuB4b1tJqDHxfTGEciD+yhvOU/5s1Rfk=
-github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190307165228-86c17b95fcd5/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
-github.com/apache/dubbo-go-hessian2 v1.5.0 h1:fzulDG5G7nX0ccgKdiN9XipJ7tZ4WXKgmk4stdlDS6s=
-github.com/apache/dubbo-go-hessian2 v1.5.0/go.mod h1:VwEnsOMidkM1usya2uPfGpSLO9XUF//WQcWn3y+jFz8=
-github.com/apache/dubbo-go-hessian2 v1.6.1-0.20200623062814-707fde850279 h1:1g3IJdaUjXWs++NA9Ail8+r6WgrkfhjS6hD/YXvRzjk=
-github.com/apache/dubbo-go-hessian2 v1.6.1-0.20200623062814-707fde850279/go.mod h1:7rEw9guWABQa6Aqb8HeZcsYPHsOS7XT1qtJvkmI6c5w=
-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.0-rc1.0.20200906044240-6c1fb5c3bd44 h1:9biQu3Z0PjDN1m8h6poo76dFkvaIpfryUVpJ5VsYVrM=
+github.com/apache/dubbo-go-hessian2 v1.6.0-rc1.0.20200906044240-6c1fb5c3bd44/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=
 github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
-github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878 h1:EFSB7Zo9Eg91v7MJPVsifUysc/wPdN+NOnVe6bWbdBM=
 github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878/go.mod h1:3AMJUQhVx52RsWOnlkpikZr01T/yAVN2gn0861vByNg=
-github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310 h1:BUAU3CGlLvorLI26FmByPp2eC2qla6E1Tw+scpcg/to=
+github.com/armon/go-metrics v0.3.0 h1:B7AQgHi8QSEi4uHu7Sbsga+IJDU+CENgjxoo81vDUqU=
+github.com/armon/go-metrics v0.3.0/go.mod h1:zXjbSimjXTd7vOpY8B0/2LpvNvDoXBuplAD+gJD3GYs=
 github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
-github.com/asaskevich/govalidator v0.0.0-20180319081651-7d2e70ef918f h1:/8NcnxL60YFll4ehCwibKotx0BR9v2ND40fomga8qDs=
-github.com/asaskevich/govalidator v0.0.0-20180319081651-7d2e70ef918f/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
-github.com/aws/aws-sdk-go v1.15.24 h1:xLAdTA/ore6xdPAljzZRed7IGqQgC+nY+ERS5vaj4Ro=
-github.com/aws/aws-sdk-go v1.15.24/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0=
-github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc=
+github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI=
+github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
+github.com/aws/aws-sdk-go v1.25.41 h1:/hj7nZ0586wFqpwjNpzWiUTwtaMgxAZNZKHay80MdXw=
+github.com/aws/aws-sdk-go v1.25.41/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
 github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
 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/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 h1:mXoPYz/Ul5HYEDvkta6I8/rnYM5gSdSV2tJ6XbZuEtY=
-github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k=
-github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
-github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
+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=
 github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
-github.com/cenkalti/backoff v2.1.1+incompatible h1:tKJnvO2kl0zmb/jA5UKAt4VoEVw1qxKWjE/Bpp46npY=
-github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
+github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
 github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible h1:C29Ae4G5GtYyYMm1aztcyj/J5ckgJm2zwdDajFbx1NY=
 github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
 github.com/circonus-labs/circonusllhist v0.1.3 h1:TJH+oke8D16535+jHExHj4nQvzlZrj7ug5D7I/orNUA=
 github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
-github.com/containerd/continuity v0.0.0-20181203112020-004b46473808 h1:4BX8f882bXEDKfWIf0wa8HRvpnBoPszJJXL+TVbBw4M=
-github.com/containerd/continuity v0.0.0-20181203112020-004b46473808/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
+github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
 github.com/coredns/coredns v1.1.2 h1:bAFHrSsBeTeRG5W3Nf2su3lUGw7Npw2UKeCJm/3A638=
 github.com/coredns/coredns v1.1.2/go.mod h1:zASH/MVDgR6XZTbxvOnsZfffS+31vg6Ackf/wo1+AM0=
+github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
 github.com/coreos/bbolt v1.3.3 h1:n6AiVyVRKQFNb6mJlwESEvvLoDyiTzXX7ORAUlkeBdY=
 github.com/coreos/bbolt v1.3.3/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
+github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
 github.com/coreos/etcd v3.3.13+incompatible h1:8F3hqu9fGYLBifCmRCJsicFqDx/D68Rt3q1JMazcgBQ=
 github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
+github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
+github.com/coreos/go-oidc v2.1.0+incompatible h1:sdJrfw8akMnCuUlaZU3tE/uYXFgfqom8DBE9so9EBsM=
+github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
+github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
 github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=
 github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
 github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f h1:JOrtw2xFKzlg+cbHpyrpLDmnN1HqhBfnX7WDiW7eG2c=
 github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
 github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg=
 github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
+github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
 github.com/creasty/defaults v1.3.0 h1:uG+RAxYbJgOPCOdKEcec9ZJXeva7Y6mj/8egdzwmLtw=
 github.com/creasty/defaults v1.3.0/go.mod h1:CIEEvs7oIVZm30R8VxtFJs+4k201gReYyuYHJxZc68I=
 github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/denisenkom/go-mssqldb v0.0.0-20180620032804-94c9c97e8c9f h1:JtRnQbMXb3TcSIm1j452zI45lPMiAQ0puF8iK5EnY9M=
-github.com/denisenkom/go-mssqldb v0.0.0-20180620032804-94c9c97e8c9f/go.mod h1:xN/JuLBIz4bjkxNmByTiV1IbhfnYb6oo99phBn4Eqhc=
 github.com/denverdino/aliyungo v0.0.0-20170926055100-d3308649c661 h1:lrWnAyy/F72MbxIxFUzKmcMCdt9Oi8RzpAxzTNQHD7o=
 github.com/denverdino/aliyungo v0.0.0-20170926055100-d3308649c661/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0=
 github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
 github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
+github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
 github.com/digitalocean/godo v1.1.1/go.mod h1:h6faOIcZ8lWIwNQ+DN7b3CgX4Kwby5T+nbpNqkUIozU=
 github.com/digitalocean/godo v1.10.0 h1:uW1/FcvZE/hoixnJcnlmIUvTVNdZCLjRLzmDtRi1xXY=
 github.com/digitalocean/godo v1.10.0/go.mod h1:h6faOIcZ8lWIwNQ+DN7b3CgX4Kwby5T+nbpNqkUIozU=
-github.com/docker/go-connections v0.3.0 h1:3lOnM9cSzgGwx8VfK/NGOW5fLQ0GjIlCkaktF+n1M6o=
+github.com/dimchansky/utfbom v1.1.0 h1:FcM3g+nofKgUteL8dm/UpdRXNC9KmADgTpLKsu0TRo4=
+github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8=
+github.com/dnaeon/go-vcr v1.0.1 h1:r8L/HqC0Hje5AXMu1ooW8oyQyOFv4GxqpL0nRP7SLLY=
+github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
 github.com/docker/go-connections v0.3.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
-github.com/docker/go-units v0.3.3 h1:Xk8S3Xj5sLGlG5g67hJmYMmUgXv5N4PhkjJHHqrwnTk=
-github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
-github.com/dubbogo/getty v1.3.5 h1:xJxdDj9jm7wlrRSsVZSk2TDNxJbbac5GpxV0QpjO+Tw=
-github.com/dubbogo/getty v1.3.5/go.mod h1:T55vN8Q6tZjf2AQZiGmkujneD3LfqYbv2b3QjacwYOY=
-github.com/dubbogo/getty v1.3.7 h1:xlkYD2/AH34iGteuLMsGjLl2PwBVrbIhHjf3tlUsv1M=
-github.com/dubbogo/getty v1.3.7/go.mod h1:XWO4+wAaMqgnBN9Ykv2YxxOAkGxymg6LGO9RK+EiCDY=
+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/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.5.1/go.mod h1:pPTjVyoJan3aPxBPNUX0ADkXjPibLo+/Ib0/fADXSG8=
-github.com/dubbogo/gost v1.9.0 h1:UT+dWwvLyJiDotxJERO75jB3Yxgsdy10KztR5ycxRAk=
 github.com/dubbogo/gost v1.9.0/go.mod h1:pPTjVyoJan3aPxBPNUX0ADkXjPibLo+/Ib0/fADXSG8=
-github.com/duosecurity/duo_api_golang v0.0.0-20190308151101-6c680f768e74 h1:2MIhn2R6oXQbgW5yHfS+d6YqyMfXiu2L55rFZC4UD/M=
-github.com/duosecurity/duo_api_golang v0.0.0-20190308151101-6c680f768e74/go.mod h1:UqXY1lYT/ERa4OEAywUqdok1T4RCRdArkhic1Opuavo=
-github.com/elazarl/go-bindata-assetfs v0.0.0-20160803192304-e1a2a7ec64b0 h1:ZoRgc53qJCfSLimXqJDrmBhnt5GChDsExMCK7t48o0Y=
+github.com/dubbogo/gost v1.9.1 h1:0/PPFo13zPbjt4Ia0zYWMFi3C6rAe9X7O1J2Iv+BHNM=
+github.com/dubbogo/gost v1.9.1/go.mod h1:pPTjVyoJan3aPxBPNUX0ADkXjPibLo+/Ib0/fADXSG8=
+github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
 github.com/elazarl/go-bindata-assetfs v0.0.0-20160803192304-e1a2a7ec64b0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
+github.com/elazarl/go-bindata-assetfs v1.0.0 h1:G/bYguwHIzWq9ZoyUQqrjTmJbbYn3j3CKKpKinvZLFk=
+github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
+github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
 github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633 h1:H2pdYOb3KQ1/YsqVWoWNLQO+fusocsw354rqGTZtAgw=
 github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
 github.com/emicklei/go-restful/v3 v3.0.0 h1:Duxxa4x0WIHW3bYEDmoAPNjmy8Rbqn+utcF74dlF/G8=
@@ -134,28 +166,39 @@ github.com/envoyproxy/go-control-plane v0.8.0 h1:uE6Fp4fOcAJdc1wTQXLJ+SYistkbG1d
 github.com/envoyproxy/go-control-plane v0.8.0/go.mod h1:GSSbY9P1neVhdY7G4wu+IK1rk/dqhiCC/4ExuWJZVuk=
 github.com/envoyproxy/protoc-gen-validate v0.0.14 h1:YBW6/cKy9prEGRYLnaGa4IDhzxZhRCtKsax8srGKDnM=
 github.com/envoyproxy/protoc-gen-validate v0.0.14/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
+github.com/evanphx/json-patch v4.2.0+incompatible h1:fUDGZCv/7iAN7u0puUVhvKCcsR6vRfwrJatElLBEf0I=
+github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
 github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239 h1:Ghm4eQYC0nEPnSJdVkTrXpu9KtoVCSo1hg7mtI7G9KU=
 github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239/go.mod h1:Gdwt2ce0yfBxPvZrHkprdPPTTS3N5rwmLE8T22KBXlw=
-github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
 github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
-github.com/fatih/structs v0.0.0-20180123065059-ebf56d35bba7 h1:bGT+Ub6bpzHl7AAYQhBrZ5nYTAH2SF/848WducU0Ao4=
-github.com/fatih/structs v0.0.0-20180123065059-ebf56d35bba7/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
+github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
+github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
+github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
+github.com/frankban/quicktest v1.4.1 h1:Wv2VwvNn73pAdFIVUQRXYDFp31lXKbqblIXo/Q5GPSg=
+github.com/frankban/quicktest v1.4.1/go.mod h1:36zfPVQyHxymz4cH7wlDmVwDrJuljRB60qkgn7rorfQ=
 github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
 github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
-github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
 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-ini/ini v1.25.4 h1:Mujh4R/dH6YL8bxuISne3xX2+qcQ9p0IxKAP6ExWoUo=
-github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
+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 h1:kD5HQcAzlQ7yrhfn+h+MSABeAy/jAJhvIJ/QDllP44g=
 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=
 github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
 github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
+github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
 github.com/go-ole/go-ole v1.2.1 h1:2lOsA72HgjxAuMlKpFiCbHTvu44PIVkZ5hqm3RSdI/E=
 github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
 github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
@@ -165,24 +208,25 @@ github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dp
 github.com/go-redis/redis v6.15.5+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
 github.com/go-resty/resty/v2 v2.1.0 h1:Z6IefCpUMfnvItVJaJXWv/pMiiD11So35QgwEELsldE=
 github.com/go-resty/resty/v2 v2.1.0/go.mod h1:dZGr0i9PLlaaTD4H/hoZIDjQ+r6xq8mgbRzHZf7f2J8=
-github.com/go-sql-driver/mysql v0.0.0-20180618115901-749ddf1598b4 h1:1LlmVz15APoKz9dnm5j2ePptburJlwEH+/v/pUuoxck=
-github.com/go-sql-driver/mysql v0.0.0-20180618115901-749ddf1598b4/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
 github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
 github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw=
 github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
-github.com/gocql/gocql v0.0.0-20180617115710-e06f8c1bcd78 h1:G7iRamCffNivybfZvsJjtk3k2qHa73xW+OysVkukcGk=
-github.com/gocql/gocql v0.0.0-20180617115710-e06f8c1bcd78/go.mod h1:4Fw1eo5iaEhDUs8XyuhSVCVy52Jq3L+/3GJgYkwc+/0=
 github.com/gogo/googleapis v1.1.0 h1:kFkMAZBNAn4j7K0GiZr8cRYzejq68VbheufiV3YuyFI=
 github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
 github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
-github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
 github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
+github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d h1:3PaI8p3seN09VjbTYC/QWlUZdZ1qS1zGjy7LH2Wt07I=
+github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
 github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I=
 github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
 github.com/golang/mock v1.3.1 h1:qGJ6qTW+x6xX/my+8YUVl4WNpX9B7+/l2tRsHGZ7f2s=
 github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
 github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@@ -190,169 +234,197 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
 github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
 github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
-github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
 github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
 github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
-github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
 github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
-github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY=
-github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
-github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135 h1:zLTLjkaOFEFIOxY5BWLFLwh+cL8vOBW4XJ2aqLE/Tf0=
+github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
+github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
+github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
+github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
 github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
-github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
 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=
-github.com/gophercloud/gophercloud v0.0.0-20180828235145-f29afc2cceca h1:wobTb8SE189AuxzEKClyYxiI4nUGWlpVtl13eLiFlOE=
-github.com/gophercloud/gophercloud v0.0.0-20180828235145-f29afc2cceca/go.mod h1:3WdhXV3rUYy9p6AUW8d94kr+HS62Y4VL9mBnFxsD8q4=
-github.com/gopherjs/gopherjs v0.0.0-20180825215210-0210a2f0f73c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/gophercloud/gophercloud v0.1.0 h1:P/nh25+rzXouhytV2pUHBb65fnds26Ghl8/391+sT5o=
+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/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
+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=
-github.com/gotestyourself/gotestyourself v2.2.0+incompatible h1:AQwinXlbQR2HvPjQZOmDhRqsv5mZf+Jb1RnSLxcqZcI=
-github.com/gotestyourself/gotestyourself v2.2.0+incompatible/go.mod h1:zZKM6oeNM8k+FRljX1mnzVYeS8wiGgQyvST1/GafPbY=
-github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM=
 github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
-github.com/grpc-ecosystem/go-grpc-middleware v1.0.0 h1:Iju5GlWwrvL6UBg4zJJt3btmonfrMlCDdsejg4CZE7c=
 github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
+github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4 h1:z53tR0945TRRQO/fLEVPI6SMv7ZflF0TEaTAoU7tOzg=
+github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
 github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
 github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
+github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
 github.com/grpc-ecosystem/grpc-gateway v1.9.5 h1:UImYN5qQ8tuGpGE16ZmjvcTtTw24zw1QAp/SlnNrZhI=
 github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
 github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 h1:MJG/KsmcqMwFAkh8mTnAwhyKoB+sTAnY4CACC110tbU=
 github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645/go.mod h1:6iZfnjpejD4L/4DwD7NryNaJyCQdzwWwH2MWhCA90Kw=
-github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8=
-github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4=
-github.com/hashicorp/consul v1.5.3 h1:EmTWRf/cuqZk6Ug9tgFUVE9xNgJPpmBvJwJMvm+agSk=
-github.com/hashicorp/consul v1.5.3/go.mod h1:61E2GJCPEP3oq8La7sfDdWGQ66+Zbxzw5ecOdFD7xIE=
-github.com/hashicorp/consul/api v1.1.0 h1:BNQPM9ytxj6jbjjdRPioQ94T6YXriSopn0i8COv6SRA=
+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/sdk v0.1.1 h1:LnuDWGNsoajlhGyHJvuWW6FVqRl8JOTPqS6CPTsYjhY=
+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=
 github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
-github.com/hashicorp/go-bexpr v0.1.0 h1:hA/9CWGPsQ6YZXvPvizD+VEEjBG4V6Un0Qcyav5ghK4=
-github.com/hashicorp/go-bexpr v0.1.0/go.mod h1:ANbpTX1oAql27TZkKVeW8p1w8NTdnyzPe/0qqPCKohU=
+github.com/hashicorp/go-bexpr v0.1.2 h1:ijMXI4qERbzxbCnkxmfUtwMyjrrk3y+Vt0MxojNCbBs=
+github.com/hashicorp/go-bexpr v0.1.2/go.mod h1:ANbpTX1oAql27TZkKVeW8p1w8NTdnyzPe/0qqPCKohU=
 github.com/hashicorp/go-checkpoint v0.0.0-20171009173528-1545e56e46de/go.mod h1:xIwEieBHERyEvaeKF/TcHh1Hu+lxPM+n2vT1+g9I4m4=
 github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
 github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
 github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
-github.com/hashicorp/go-discover v0.0.0-20190403160810-22221edb15cd h1:SynRxs8h2h7lLSA5py5a3WWkYpImhREtju0CuRd97wc=
-github.com/hashicorp/go-discover v0.0.0-20190403160810-22221edb15cd/go.mod h1:ueUgD9BeIocT7QNuvxSyJyPAM9dfifBcaWmeybb67OY=
-github.com/hashicorp/go-hclog v0.9.1 h1:9PZfAcVEvez4yhLH2TBU64/h/z4xlFI80cWXRrxuKuM=
+github.com/hashicorp/go-connlimit v0.2.0 h1:OZjcfNxH/hPh/bT2Iw5yOJcLzz+zuIWpsp3I1S4Pjw4=
+github.com/hashicorp/go-connlimit v0.2.0/go.mod h1:OUj9FGL1tPIhl/2RCfzYHrIiWj+VVPGNyVPnUX8AqS0=
+github.com/hashicorp/go-discover v0.0.0-20200501174627-ad1e96bde088 h1:jBvElOilnIl6mm8S6gva/dfeTJCcMs9TGO6/2C6k52E=
+github.com/hashicorp/go-discover v0.0.0-20200501174627-ad1e96bde088/go.mod h1:vZu6Opqf49xX5lsFAu7iFNewkcVF1sn/wyapZh5ytlg=
+github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI=
+github.com/hashicorp/go-hclog v0.8.0/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
 github.com/hashicorp/go-hclog v0.9.1/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
-github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0=
+github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
+github.com/hashicorp/go-hclog v0.12.0 h1:d4QkX8FRTYaKaCZBoXYY8zJX2BXjWxurN/GA2tkrmZM=
+github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
 github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
-github.com/hashicorp/go-memdb v0.0.0-20180223233045-1289e7fffe71 h1:yxxFgVz31vFoKKTtRUNbXLNe4GFnbLKqg+0N7yG42L8=
-github.com/hashicorp/go-memdb v0.0.0-20180223233045-1289e7fffe71/go.mod h1:kbfItVoBJwCfKXDXN4YoAXjxcFVZ7MRrJzyTX6H4giE=
+github.com/hashicorp/go-immutable-radix v1.1.0 h1:vN9wG1D6KG6YHRTWr8512cxGOVgTMEfgEdSj/hr8MPc=
+github.com/hashicorp/go-immutable-radix v1.1.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
+github.com/hashicorp/go-memdb v1.0.3 h1:iiqzNk8jKB6/sLRj623Ui/Vi1zf21LOUpgzGjTge6a8=
+github.com/hashicorp/go-memdb v1.0.3/go.mod h1:LWQ8R70vPrS4OEY9k28D2z8/Zzyu34NVzeRibGAzHO0=
 github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
 github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI=
 github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
-github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o=
 github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
-github.com/hashicorp/go-plugin v0.0.0-20180331002553-e8d22c780116 h1:Y4V/yReWjQo/Ngyc0w6C3EKXKincp4YgvXeo8lI4LrI=
-github.com/hashicorp/go-plugin v0.0.0-20180331002553-e8d22c780116/go.mod h1:JSqWYsict+jzcj0+xElxyrBQRPNoiWQuddnxArJ7XHQ=
-github.com/hashicorp/go-raftchunking v0.6.1 h1:moEnaG3gcwsWNyIBJoD5PCByE+Ewkqxh6N05CT+MbwA=
+github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI=
+github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
+github.com/hashicorp/go-plugin v1.0.1/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY=
 github.com/hashicorp/go-raftchunking v0.6.1/go.mod h1:cGlg3JtDy7qy6c/3Bu660Mic1JF+7lWqIwCFSb08fX0=
-github.com/hashicorp/go-retryablehttp v0.5.3 h1:QlWt0KvWT0lq8MFppF9tsJGF+ynG7ztc2KIPhzRGk7s=
+github.com/hashicorp/go-raftchunking v0.6.3-0.20191002164813-7e9e8525653a h1:FmnBDwGwlTgugDGbVxwV8UavqSMACbGrUpfc98yFLR4=
+github.com/hashicorp/go-raftchunking v0.6.3-0.20191002164813-7e9e8525653a/go.mod h1:xbXnmKqX9/+RhPkJ4zrEx4738HacP72aaUPlT2RZ4sU=
 github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
-github.com/hashicorp/go-rootcerts v1.0.0 h1:Rqb66Oo1X/eSV1x66xbDccZjhJigjg0+e82kpwzSwCI=
+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-sockaddr v1.0.0 h1:GeH6tui99pF4NJgfnhp+L6+FfobzVW3Ah46sLo0ICXs=
+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=
 github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
+github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc=
+github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A=
 github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE=
 github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
 github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
-github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE=
 github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
-github.com/hashicorp/go-version v0.0.0-20170202080759-03c5bf6be031 h1:c3Xdf5fTpk+hqhxqCO+ymqjfUXV9+GZqNgTtlnVzDos=
-github.com/hashicorp/go-version v0.0.0-20170202080759-03c5bf6be031/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
+github.com/hashicorp/go-uuid v1.0.2-0.20191001231223-f32f5fe8d6a8/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
+github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE=
+github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
+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 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo=
 github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
-github.com/hashicorp/hcl v0.0.0-20180906183839-65a6292f0157 h1:PJ+K03hio6ADVjEc6lFu5r866o67xEEMQ73CFdI6R2U=
-github.com/hashicorp/hcl v0.0.0-20180906183839-65a6292f0157/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
+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=
+github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
+github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
+github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
 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 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y=
 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.1.4 h1:gkyML/r71w3FL8gUi74Vk76avkj/9lYAY9lvg0OcoGs=
-github.com/hashicorp/memberlist v0.1.4/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=
 github.com/hashicorp/net-rpc-msgpackrpc v0.0.0-20151116020338-a14192a58a69 h1:lc3c72qGlIMDqQpQH82Y4vaglRMMFdJbziYWriR4UcE=
 github.com/hashicorp/net-rpc-msgpackrpc v0.0.0-20151116020338-a14192a58a69/go.mod h1:/z+jUGRBlwVpUZfjute9jWaF6/HuhjuFQuL1YXzVD1Q=
-github.com/hashicorp/raft v1.1.1 h1:HJr7UE1x/JrJSc9Oy6aDBHtNHUUBHjcQjTgvUVihoZs=
 github.com/hashicorp/raft v1.1.1/go.mod h1:vPAJM8Asw6u8LxC3eJCUZmRP/E4QmUGE1R7g7k8sG/8=
+github.com/hashicorp/raft v1.1.2-0.20191002163536-9c6bd3e3eb17/go.mod h1:vPAJM8Asw6u8LxC3eJCUZmRP/E4QmUGE1R7g7k8sG/8=
+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 h1:YZ7UKsJv+hKjqGVUUbtE3HNj79Eln2oQ75tniF6iPt0=
 github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
-github.com/hashicorp/vault v0.10.3 h1:3Hf6mwC4rggOq6ViWSoJ2yfk1oBS5ed58LLcP33gmEg=
-github.com/hashicorp/vault v0.10.3/go.mod h1:KfSyffbKxoVyspOdlaGVjIuwLobi07qD1bAbosPMpP0=
-github.com/hashicorp/vault-plugin-secrets-kv v0.0.0-20190318174639-195e0e9d07f1 h1:7IvvWArBoSjStPohKqHj3ksXNjcyPsyXYDIhCQw6Vtg=
-github.com/hashicorp/vault-plugin-secrets-kv v0.0.0-20190318174639-195e0e9d07f1/go.mod h1:VJHHT2SC1tAPrfENQeBhLlb5FbZoKZM+oC/ROmEftz0=
+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=
+github.com/hashicorp/vault/api v1.0.4/go.mod h1:gDcqh3WGcR1cpF5AJz/B1UFheUEneMoIospckxBxk6Q=
+github.com/hashicorp/vault/api v1.0.5-0.20191108163347-bdd38fca2cff h1:cl94LQIrs/mNbh3ny1R8lM1gtYcUBa7HnGtOCi35SlQ=
+github.com/hashicorp/vault/api v1.0.5-0.20191108163347-bdd38fca2cff/go.mod h1:Uf8LaHyrYsgVgHzO2tMZKhqRGlL3UJ6XaSwW2EA1Iqo=
+github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M=
+github.com/hashicorp/vault/sdk v0.1.14-0.20191108161836-82f2b5571044/go.mod h1:PcekaFGiPJyHnFy+NZhP6ll650zEw51Ag7g/YEa+EOU=
+github.com/hashicorp/vault/sdk v0.1.14-0.20191112033314-390e96e22eb2 h1:mKYi4Fm2uSfe94Ji89CoAaP7SPEEkfdtaUlgRGGb2go=
+github.com/hashicorp/vault/sdk v0.1.14-0.20191112033314-390e96e22eb2/go.mod h1:PcekaFGiPJyHnFy+NZhP6ll650zEw51Ag7g/YEa+EOU=
 github.com/hashicorp/vic v1.5.1-0.20190403131502-bbfe86ec9443 h1:O/pT5C1Q3mVXMyuqg7yuAWUg/jMZR1/0QTzTRdNR6Uw=
 github.com/hashicorp/vic v1.5.1-0.20190403131502-bbfe86ec9443/go.mod h1:bEpDU35nTu0ey1EXjwNwPjI9xErAsoOCmcMb9GKvyxo=
+github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
 github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ=
 github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
 github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
 github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
+github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
 github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28=
 github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
+github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
+github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ=
+github.com/jackc/pgx v3.3.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I=
 github.com/jarcoal/httpmock v0.0.0-20180424175123-9c70cfe4a1da h1:FjHUJJ7oBW4G/9j1KzlHaXL09LyMVM9rupS39lncbXk=
 github.com/jarcoal/httpmock v0.0.0-20180424175123-9c70cfe4a1da/go.mod h1:ks+b9deReOc7jgqp+e7LuFiCBH6Rm5hL32cLcEAArb4=
-github.com/jefferai/jsonx v0.0.0-20160721235117-9cc31c3135ee h1:AQ/QmCk6x8ECPpf2pkPtA4lyncEEBbs8VFnVXPYKhIs=
-github.com/jefferai/jsonx v0.0.0-20160721235117-9cc31c3135ee/go.mod h1:N0t2vlmpe8nyZB5ouIbJQPDSR+mH6oe7xHB9VZHSUzM=
 github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 h1:IPJ3dvxmJ4uczJe5YQdrYB16oTJlGSC/OyZDqUk9xX4=
 github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869/go.mod h1:cJ6Cj7dQo+O6GJNiMx+Pa94qKj+TG8ONdKHgMNIyyag=
 github.com/jinzhu/copier v0.0.0-20190625015134-976e0346caa8 h1:mGIXW/lubQ4B+3bXTLxcTMTjUNDqoF6T/HUW9LbFx9s=
 github.com/jinzhu/copier v0.0.0-20190625015134-976e0346caa8/go.mod h1:yL958EeXv8Ylng6IfnvG4oflryUi3vgA3xPs9hmII1s=
-github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
 github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
 github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
 github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo=
 github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
-github.com/joyent/triton-go v0.0.0-20180628001255-830d2b111e62 h1:JHCT6xuyPUrbbgAPE/3dqlvUKzRHMNuTBKKUb6OeR/k=
 github.com/joyent/triton-go v0.0.0-20180628001255-830d2b111e62/go.mod h1:U+RSyWxWd04xTqnuOQxnai7XGS2PrPY2cfGoDKtMHjA=
+github.com/joyent/triton-go v1.7.1-0.20200416154420-6801d15b779f h1:ENpDacvnr8faw5ugQmEF1QYk+f/Y9lXFvuYmRxykago=
+github.com/joyent/triton-go v1.7.1-0.20200416154420-6801d15b779f/go.mod h1:KDSfL7qe5ZfQqvlDMkVjCztbmcpp/c8M77vhQP8ZPvk=
 github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
 github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
 github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
 github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo=
 github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
-github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
+github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
 github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
 github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
-github.com/juju/errors v0.0.0-20190930114154-d42613fe1ab9 h1:hJix6idebFclqlfZCHE7EUX7uqLCyb70nHNHH1XKGBg=
-github.com/juju/errors v0.0.0-20190930114154-d42613fe1ab9/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q=
-github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8 h1:UUHMLvzt/31azWTN/ifGWef4WUqvXk0iRqdhdy/2uzI=
-github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U=
-github.com/juju/testing v0.0.0-20191001232224-ce9dec17d28b h1:Rrp0ByJXEjhREMPGTt3aWYjoIsUGCbt21ekbeJcTWv0=
-github.com/juju/testing v0.0.0-20191001232224-ce9dec17d28b/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA=
 github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
-github.com/keybase/go-crypto v0.0.0-20180614160407-5114a9a81e1b h1:VE6r2OwP5gj+Z9aCkSKl3MlmnZbfMAjhvR5T7abKHEo=
-github.com/keybase/go-crypto v0.0.0-20180614160407-5114a9a81e1b/go.mod h1:ghbZscTyKdM07+Fw3KSi0hcJm+AlEUWj8QLlPtijN/M=
 github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
+github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
 github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
-github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
 github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
+github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
-github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
+github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
@@ -362,34 +434,53 @@ github.com/lestrrat/go-file-rotatelogs v0.0.0-20180223000712-d3151e2a480f h1:sgU
 github.com/lestrrat/go-file-rotatelogs v0.0.0-20180223000712-d3151e2a480f/go.mod h1:UGmTpUd3rjbtfIpwAPrcfmGf/Z1HS95TATB+m57TPB8=
 github.com/lestrrat/go-strftime v0.0.0-20180220042222-ba3bf9c1d042 h1:Bvq8AziQ5jFF4BHGAEDSqwPW1NJS3XshxbRCxtjFAZc=
 github.com/lestrrat/go-strftime v0.0.0-20180220042222-ba3bf9c1d042/go.mod h1:TPpsiPUEh0zFL1Snz4crhMlBe60PYxRHr5oFF3rRYg0=
-github.com/lib/pq v0.0.0-20180523175426-90697d60dd84 h1:it29sI2IM490luSc3RAhp5WuCYnc6RtbfLVAB7nmC5M=
-github.com/lib/pq v0.0.0-20180523175426-90697d60dd84/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
+github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
+github.com/linode/linodego v0.7.1 h1:4WZmMpSA2NRwlPZcc0+4Gyn7rr99Evk9bnr0B3gXRKE=
+github.com/linode/linodego v0.7.1/go.mod h1:ga11n3ivecUrPCHN0rANxKmfWBJVkOXfLMZinAbj2sY=
+github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
 github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
 github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
 github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
-github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
 github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
-github.com/mattn/go-isatty v0.0.3 h1:ns/ykhmWi7G9O+8a448SecJU3nSMBXJfqQkl0upE1jI=
+github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
+github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE=
+github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
 github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
+github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
+github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
+github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
+github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
+github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
+github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
 github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
 github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
-github.com/miekg/dns v1.0.14 h1:9jZdLNd/P4+SfEJ0TNyxYpsK8N4GtfylBLqtbYN1sbA=
 github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
-github.com/mitchellh/cli v1.0.0 h1:iGBIsUe3+HZ/AD/Vd7DErOt5sU9fa8Uj7A2s1aggv1Y=
+github.com/miekg/dns v1.1.26 h1:gPxPSwALAeHJSjarOs00QjVdV9QoBvc1D2ujQUr5BzU=
+github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
 github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
+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 h1:vKb8ShqSby24Yrqr/yDYkuFz8d0WUjys40rvnGC8aR0=
 github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
-github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0=
+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=
 github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
+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 h1:hOY53G+kBFhbYFpRVxHl5eS7laP6B1+Cq+Z9Dry1iMU=
 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 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
 github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/mitchellh/mapstructure v1.2.3 h1:f/MjBEBDLttYCGfRaKBbKSRVF5aV2O6fnBpzknuE3jU=
+github.com/mitchellh/mapstructure v1.2.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
+github.com/mitchellh/pointerstructure v1.0.0 h1:ATSdz4NWrmWPOF1CeCBU4sMCno2hgqdbSrRPFWQSVZI=
+github.com/mitchellh/pointerstructure v1.0.0/go.mod h1:k4XwG94++jLVsSiTxo7qdIfXA9pj9EAeo0QsNNJOLZ8=
 github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
 github.com/mitchellh/reflectwalk v1.0.1 h1:FVzMWA5RllMAKIdUSC8mdWo3XtwoecrH79BY70sEEpE=
 github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
@@ -400,184 +491,251 @@ 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/nacos-group/nacos-sdk-go v0.3.2 h1:q+ukmIImL6u0zBtbceMZl2frgeAc45QT6cIrTZZz50c=
-github.com/nacos-group/nacos-sdk-go v0.3.2/go.mod h1:4TdsN7eZnnVCDlOlBa61b0gsRnvNJI74m9+2+OKZkcw=
-github.com/nacos-group/nacos-sdk-go v0.3.3-0.20200617023039-50c7537d6a5f h1:gid5/0AkHvINWK69Fgbidb3BVIXqlf1YEm7wO0NVPsw=
-github.com/nacos-group/nacos-sdk-go v0.3.3-0.20200617023039-50c7537d6a5f/go.mod h1:fti1GlX/EB6RDKvzK/P7Vuibqj0JMPJHQwrcTU1tLXk=
+github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
+github.com/nacos-group/nacos-sdk-go v1.0.0 h1:CufUF7DZca2ZzIrJtMMCDih1sA58BWCglArLMCZArUc=
+github.com/nacos-group/nacos-sdk-go v1.0.0/go.mod h1:hlAPn3UdzlxIlSILAyOXKxjFSvDJ9oLzTJ9hLAK1KzA=
 github.com/nicolai86/scaleway-sdk v1.10.2-0.20180628010248-798f60e20bb2 h1:BQ1HW7hr4IVovMwWg0E0PYcyW8CzqDcVmaew9cujU4s=
 github.com/nicolai86/scaleway-sdk v1.10.2-0.20180628010248-798f60e20bb2/go.mod h1:TLb2Sg7HQcgGdloNxkrmtgDNR9uVYF3lfdFIN4Ro6Sk=
-github.com/oklog/run v0.0.0-20180308005104-6934b124db28 h1:Hbr3fbVPXea52oPQeP7KLSxP52g6SFaNY1IqAmUyEW0=
-github.com/oklog/run v0.0.0-20180308005104-6934b124db28/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
+github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
+github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
+github.com/olekukonko/tablewriter v0.0.0-20180130162743-b8a9be070da4/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
 github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/ginkgo v1.6.0 h1:Ix8l273rp3QzYgXSR+c8d1fTG7UPgYkOSELPhiY/YGw=
 github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo=
 github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
-github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
-github.com/onsi/gomega v1.4.2 h1:3mYCb7aPxS/RU7TI1y4rkEn1oKmPRjNJLNEXgw7MH2I=
-github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
+github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
 github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME=
 github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
-github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
-github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
-github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI=
-github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
-github.com/opencontainers/runc v0.1.1 h1:GlxAyO6x8rfZYN9Tt0Kti5a/cP41iuiO2yYT0IJGY8Y=
-github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
 github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU=
 github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
-github.com/ory/dockertest v3.3.4+incompatible h1:VrpM6Gqg7CrPm3bL4Wm1skO+zFWLbh7/Xb5kGEbJRh8=
-github.com/ory/dockertest v3.3.4+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs=
 github.com/packethost/packngo v0.1.1-0.20180711074735-b9cb5096f54c h1:vwpFWvAO8DeIZfFeqASzZfsxuWPno9ncAebBEP0N3uE=
 github.com/packethost/packngo v0.1.1-0.20180711074735-b9cb5096f54c/go.mod h1:otzZQXgoO96RTzDB/Hycg0qZcXZsWJGJRSXbmEIJ+4M=
 github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
 github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
 github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
-github.com/patrickmn/go-cache v0.0.0-20180527043350-9f6ff22cfff8 h1:BR6MM54q4W9pn0SySwg6yctZtBKlTdUq6a+b0kArBnE=
-github.com/patrickmn/go-cache v0.0.0-20180527043350-9f6ff22cfff8/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
-github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
+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=
 github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/posener/complete v1.1.1 h1:ccV59UEOTzVDnDUEFdT95ZzHVZ+5+158q8+SJb2QV5w=
 github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
+github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXqo=
+github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
+github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 h1:J9b7z+QKAmPf4YLrFg6oQUotqHQeUNWwkvo7jZp1GLU=
+github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
 github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
 github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
+github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
 github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
 github.com/prometheus/client_golang v1.1.0 h1:BQ53HtBmfOitExawJ6LokA4x8ov/z0SYYb0+HxJfRI8=
 github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
 github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
 github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE=
 github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
 github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
+github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
 github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
 github.com/prometheus/common v0.6.0 h1:kRhiuYSXR3+uv2IbVbZhUxK5zVD/2pp3Gd2PpvPkpEo=
 github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
 github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
 github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
 github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
 github.com/prometheus/procfs v0.0.3 h1:CTwfnzjQ+8dS6MhHHu4YswVAD99sL2wjPqP+VkURmKE=
 github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
+github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
+github.com/rboyer/safeio v0.2.1/go.mod h1:Cq/cEPK+YXFn622lsQ0K4KsPZSPtaptHHEldsy7Fmig=
 github.com/renier/xmlrpc v0.0.0-20170708154548-ce4a1a486c03 h1:Wdi9nwnhFNAlseAOekn6B5G/+GMtks9UKbvRU/CMM/o=
 github.com/renier/xmlrpc v0.0.0-20170708154548-ce4a1a486c03/go.mod h1:gRAiPF5C5Nd0eyyRdqIu9qTiFSoZzpTq727b5B8fkkU=
 github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
 github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/rs/zerolog v1.4.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
+github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
 github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
-github.com/ryanuber/go-glob v0.0.0-20170128012129-256dc444b735 h1:7YvPJVmEeFHR1Tj9sZEYsmarJEQfMVYpd/Vyy/A8dqE=
-github.com/ryanuber/go-glob v0.0.0-20170128012129-256dc444b735/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
+github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
+github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
+github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
 github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
 github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b h1:gQZ0qzfKHQIybLANtM3mBXNUtOfsCFXeTsnBqCsx1KM=
-github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b h1:gQZ0qzfKHQIybLANtM3mBXNUtOfsCFXeTsnBqCsx1KM=
-github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
 github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
+github.com/sean-/conswriter v0.0.0-20180208195008-f5ae3917a627/go.mod h1:7zjs06qF79/FKAJpBvFx3P8Ww4UTIMAe+lpNXDHziac=
+github.com/sean-/pager v0.0.0-20180208200047-666be9bf53b5/go.mod h1:BeybITEsBEg6qbIiqJ6/Bqeq25bCLbL7YFmpaFfJDuM=
 github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I=
 github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
-github.com/shirou/gopsutil v0.0.0-20181107111621-48177ef5f880 h1:1Ge4j/3uB2rxzPWD3TC+daeCw+w91z8UCUL/7WH5gn8=
 github.com/shirou/gopsutil v0.0.0-20181107111621-48177ef5f880/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
+github.com/shirou/gopsutil v2.19.9+incompatible h1:IrPVlK4nfwW10DF7pW+7YJKws9NkgNzWozwwWv9FsgY=
+github.com/shirou/gopsutil v2.19.9+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
 github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4 h1:udFKJ0aHUL60LboW/A+DfgoHVedieIzIXE8uylPue0U=
 github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc=
+github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
 github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
-github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo=
 github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
-github.com/smartystreets/assertions v0.0.0-20180820201707-7c9eb446e3cf/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
+github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
+github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
+github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
 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-20180222194500-ef6db91d284a/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
 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=
 github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
+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.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
-github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
 github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+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 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
 github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
+github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
 github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 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=
+github.com/tencentcloud/tencentcloud-sdk-go v3.0.83+incompatible/go.mod h1:0PfYow01SHPMhKY31xa+EFz2RStxIqj6JFAJS+IkCi4=
 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=
 github.com/toolkits/concurrent v0.0.0-20150624120057-a4371d70e3e3/go.mod h1:QDlpd3qS71vYtakd2hmdpqhJ9nwv6mD6A30bQ1BPBFE=
 github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926 h1:G3dpKMzFDjgEh2q1Z7zUUtKa8ViPtH+ocF0bE0g00O8=
 github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
+github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
+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/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/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
+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=
-go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU=
+go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
+go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4=
+go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
 go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
 go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=
 go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
-go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=
 go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
 go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=
 go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
+go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
 go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
-go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM=
 go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
 go.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM=
 go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=
 golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+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-20190325154230-a5d413f7728c h1:Vj5n4GlwjmQteupaxJ9+0FNOmBrHfq7vN4btdGoDZgI=
-golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529 h1:iMGN4xG0cnqj3t+zOM8wUB0BiPKHEwSxEZCvzcbZuvk=
 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=
 golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+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 h1:rTIdg5QFRR7XCaK4LCjBiPbx8j4DQRpdYMnGn/bJUEU=
 golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8=
 golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/oauth2 v0.0.0-20170807180024-9a379c6b3e95/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
-golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
+golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
+golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
 golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -589,88 +747,153 @@ golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5h
 golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+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=
+golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 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-20190801041406-cbf593c0f2f3 h1:4y9KwBHBgBNwDbtu44R5o1fdOCQUEXhbk/P4A9WmJq0=
+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-20200202164722-d101bd2416d5 h1:LfCXLvNmTYH9kEmVgqbnsWfruoXZIrh4YBgqVHtDvw0=
+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=
+golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8=
+golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
-golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 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/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.0.0-20180829000535-087779f1d2c9 h1:z1TeLUmxf9ws9KLICfmX+KGXTs+rjm+aGWzfsv7MZ9w=
-google.golang.org/api v0.0.0-20180829000535-087779f1d2c9/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
-google.golang.org/appengine v1.1.0 h1:igQkv0AAhEIvTEpD5LIpAfav2eeVO9HBTjvKHVJPRSs=
+google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
+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/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc=
+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/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-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
+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.22.1 h1:/7cs52RnTJmD43s3uxzlq2U7nqVTd/37viQwMrMNlOM=
-google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+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=
 gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
 gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
-gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d h1:TxyelI5cVkbREznMhfzycHdkp5cLA7DpE+GKjSslYhM=
 gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
 gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
 gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
 gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
 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/mgo.v2 v2.0.0-20160818020120-3f83fa500528 h1:/saqWwm73dLmuzbNhe92F0QsZ/KiFND+esHco2v1hiY=
-gopkg.in/mgo.v2 v2.0.0-20160818020120-3f83fa500528/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
-gopkg.in/ory-am/dockertest.v3 v3.3.4 h1:oen8RiwxVNxtQ1pRoV4e4jqh6UjNsOuIZ1NXns6jdcw=
-gopkg.in/ory-am/dockertest.v3 v3.3.4/go.mod h1:s9mmoLkaGeAh97qygnNj4xWkiN7e1SKekYC6CovU+ek=
+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 h1:SK5KegNXmKmqE342YYN2qPHEnUYeoMiXXl1poUlI+o4=
 gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
+gopkg.in/square/go-jose.v2 v2.4.1 h1:H0TmLt7/KmzlrDOpa1F+zr0Tk90PbJYBfsVUmRLrf9Y=
+gopkg.in/square/go-jose.v2 v2.4.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
 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 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
-gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
+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=
+honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
 honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
 istio.io/gogo-genproto v0.0.0-20190124151557-6d926a6e6feb/go.mod h1:eIDJ6jNk/IeJz6ODSksHl5Aiczy5JUq6vFhJWI5OtiI=
-k8s.io/api v0.0.0-20180806132203-61b11ee65332/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA=
-k8s.io/api v0.0.0-20190325185214-7544f9db76f6 h1:9MWtbqhwTyDvF4cS1qAhxDb9Mi8taXiAu+5nEacl7gY=
-k8s.io/api v0.0.0-20190325185214-7544f9db76f6/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA=
-k8s.io/apimachinery v0.0.0-20180821005732-488889b0007f/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0=
-k8s.io/apimachinery v0.0.0-20190223001710-c182ff3b9841 h1:Q4RZrHNtlC/mSdC1sTrcZ5RchC/9vxLVj57pWiCBKv4=
-k8s.io/apimachinery v0.0.0-20190223001710-c182ff3b9841/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0=
-k8s.io/client-go v8.0.0+incompatible h1:tTI4hRmb1DRMl4fG6Vclfdi6nTM82oIrTT7HfitmxC4=
-k8s.io/client-go v8.0.0+incompatible/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s=
+k8s.io/api v0.16.9 h1:3vCx0WX9qcg1Hv4aQ/G1tiIKectGVuimvPVTJU4VOCA=
+k8s.io/api v0.16.9/go.mod h1:Y7dZNHs1Xy0mSwSlzL9QShi6qkljnN41yR8oWCRTDe8=
+k8s.io/apimachinery v0.16.9 h1:ESUZ4hMBUKF2kn2HBFL5zM/wQv4j/0uRbR7AjgqGJ4o=
+k8s.io/apimachinery v0.16.9/go.mod h1:Xk2vD2TRRpuWYLQNM6lT9R7DSFZUYG03SarNkbGrnKE=
+k8s.io/client-go v0.16.9 h1:6Eh4lMDxFtDzBkqid1AOL3bQ/pPYrulx8l23DXw4mRU=
+k8s.io/client-go v0.16.9/go.mod h1:ThjPlh7Kx+XoBFOCt775vx5J7atwY7F/zaFzTco5gL0=
 k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
 k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
+k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
+k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
+k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
+k8s.io/kube-openapi v0.0.0-20190816220812-743ec37842bf/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
 k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a h1:UcxjrRMyNx/i/y8G7kPvLyy7rfbeuf1PYyBf973pgyU=
 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/metadata/report/consul/report_test.go b/metadata/report/consul/report_test.go
index 34ee29de945f2b9ac6978a55008048e62f4c6812..e07a7429f12bac0cfa4b2701d3e87ef25bcb077a 100644
--- a/metadata/report/consul/report_test.go
+++ b/metadata/report/consul/report_test.go
@@ -143,7 +143,7 @@ func (suite *consulMetadataReportTestSuite) testGetServiceDefinition() {
 
 func test1(t *testing.T) {
 	consulAgent := consul.NewConsulAgent(t, 8500)
-	defer consulAgent.Close()
+	defer consulAgent.Shutdown()
 
 	url := newProviderRegistryUrl("localhost", 8500)
 	mf := extension.GetMetadataReportFactory("consul")
diff --git a/metadata/service/exporter/configurable/exporter.go b/metadata/service/exporter/configurable/exporter.go
index f8b4b0c0174cb0e5a8753b814f89ed4d332e2fbe..5a930c5e953aead15e4673f1b9537197128a7b35 100644
--- a/metadata/service/exporter/configurable/exporter.go
+++ b/metadata/service/exporter/configurable/exporter.go
@@ -48,7 +48,6 @@ func NewMetadataServiceExporter(metadataService service.MetadataService) exporte
 // Export will export the metadataService
 func (exporter *MetadataServiceExporter) Export() error {
 	if !exporter.IsExported() {
-
 		serviceConfig := config.NewServiceConfig(constant.SIMPLE_METADATA_SERVICE_NAME, context.Background())
 		serviceConfig.Protocol = constant.DEFAULT_PROTOCOL
 		serviceConfig.Protocols = map[string]*config.ProtocolConfig{
diff --git a/metadata/service/exporter/configurable/exporter_test.go b/metadata/service/exporter/configurable/exporter_test.go
index 4689c6660b7da78609501c5e98f0dd309e4bce7f..9fdbd76757815af0aa975ec6e5f1b20fa5f1a83e 100644
--- a/metadata/service/exporter/configurable/exporter_test.go
+++ b/metadata/service/exporter/configurable/exporter_test.go
@@ -97,13 +97,13 @@ func mockInitProviderWithSingleRegistry() {
 					{
 						Name:        "GetUser",
 						Retries:     "2",
-						Loadbalance: "random",
+						LoadBalance: "random",
 						Weight:      200,
 					},
 					{
 						Name:        "GetUser1",
 						Retries:     "2",
-						Loadbalance: "random",
+						LoadBalance: "random",
 						Weight:      200,
 					},
 				},
diff --git a/metadata/service/inmemory/service_proxy.go b/metadata/service/inmemory/service_proxy.go
index 7e01439f042a2046559188ec9df6924da0236cb1..e2b29686f49aeade5c61a46f464e6bf00165e70c 100644
--- a/metadata/service/inmemory/service_proxy.go
+++ b/metadata/service/inmemory/service_proxy.go
@@ -55,7 +55,7 @@ func (m *MetadataServiceProxy) GetExportedURLs(serviceInterface string, group st
 	inv := invocation.NewRPCInvocationWithOptions(invocation.WithMethodName(methodName),
 		invocation.WithArguments([]interface{}{siV.Interface(), gV.Interface(), vV.Interface(), pV.Interface()}),
 		invocation.WithReply(reflect.ValueOf(&[]interface{}{}).Interface()),
-		invocation.WithAttachments(map[string]string{constant.ASYNC_KEY: "false"}),
+		invocation.WithAttachments(map[string]interface{}{constant.ASYNC_KEY: "false"}),
 		invocation.WithParameterValues([]reflect.Value{siV, gV, vV, pV}))
 
 	res := m.invkr.Invoke(context.Background(), inv)
diff --git a/metrics/prometheus/reporter_test.go b/metrics/prometheus/reporter_test.go
index 0cb7d09a2c8e71fb88b54789c8eb3ee2cf967fbf..eaba0e324ff078bdfb2fd4b146ac9ea60d429724 100644
--- a/metrics/prometheus/reporter_test.go
+++ b/metrics/prometheus/reporter_test.go
@@ -43,7 +43,7 @@ func TestPrometheusReporter_Report(t *testing.T) {
 			"service.filter=echo%2Ctoken%2Caccesslog&timestamp=1569153406&token=934804bf-b007-4174-94eb-96e3e1d60cc7&version=&warmup=100")
 	invoker := protocol.NewBaseInvoker(url)
 
-	attach := make(map[string]string, 10)
+	attach := make(map[string]interface{}, 10)
 	inv := invocation.NewRPCInvocation("MethodName", []interface{}{"OK", "Hello"}, attach)
 
 	assert.False(t, isConsumer(url))
diff --git a/protocol/dubbo/client.go b/protocol/dubbo/client.go
index 6d1b771bf4108d17372e0ceb5ca818323278afd2..b6e4618bb572c1294c6e71e873825bc9fe90e588 100644
--- a/protocol/dubbo/client.go
+++ b/protocol/dubbo/client.go
@@ -25,8 +25,7 @@ import (
 )
 
 import (
-	hessian "github.com/apache/dubbo-go-hessian2"
-	"github.com/dubbogo/getty"
+	"github.com/apache/dubbo-getty"
 	gxsync "github.com/dubbogo/gost/sync"
 	perrors "github.com/pkg/errors"
 	"go.uber.org/atomic"
@@ -38,6 +37,7 @@ import (
 	"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/protocol/dubbo/hessian2"
 )
 
 var (
@@ -173,11 +173,11 @@ type Request struct {
 	svcUrl common.URL
 	method string
 	args   interface{}
-	atta   map[string]string
+	atta   map[string]interface{}
 }
 
 // NewRequest create a new Request.
-func NewRequest(addr string, svcUrl common.URL, method string, args interface{}, atta map[string]string) *Request {
+func NewRequest(addr string, svcUrl common.URL, method string, args interface{}, atta map[string]interface{}) *Request {
 	return &Request{
 		addr:   addr,
 		svcUrl: svcUrl,
@@ -190,11 +190,11 @@ func NewRequest(addr string, svcUrl common.URL, method string, args interface{},
 // Response is dubbo protocol response.
 type Response struct {
 	reply interface{}
-	atta  map[string]string
+	atta  map[string]interface{}
 }
 
-// NewResponse  create a new Response.
-func NewResponse(reply interface{}, atta map[string]string) *Response {
+// NewResponse creates a new Response.
+func NewResponse(reply interface{}, atta map[string]interface{}) *Response {
 	return &Response{
 		reply: reply,
 		atta:  atta,
@@ -229,6 +229,7 @@ func (c *Client) call(ct CallType, request *Request, response *Response, callbac
 	p.Service.Version = request.svcUrl.GetParam(constant.VERSION_KEY, "")
 	p.Service.Group = request.svcUrl.GetParam(constant.GROUP_KEY, "")
 	p.Service.Method = request.method
+	c.pool.sslEnabled = request.svcUrl.GetParamBool(constant.SSL_ENABLED_KEY, false)
 
 	p.Service.Timeout = c.opts.RequestTimeout
 	var timeout = request.svcUrl.GetParam(strings.Join([]string{constant.METHOD_KEYS, request.method + constant.RETRIES_KEY}, "."), "")
@@ -239,16 +240,16 @@ func (c *Client) call(ct CallType, request *Request, response *Response, callbac
 	}
 
 	p.Header.SerialID = byte(S_Dubbo)
-	p.Body = hessian.NewRequest(request.args, request.atta)
+	p.Body = hessian2.NewRequest(request.args, request.atta)
 
 	var rsp *PendingResponse
 	if ct != CT_OneWay {
-		p.Header.Type = hessian.PackageRequest_TwoWay
+		p.Header.Type = hessian2.PackageRequest_TwoWay
 		rsp = NewPendingResponse()
 		rsp.response = response
 		rsp.callback = callback
 	} else {
-		p.Header.Type = hessian.PackageRequest
+		p.Header.Type = hessian2.PackageRequest
 	}
 
 	var (
@@ -322,9 +323,9 @@ func (c *Client) transfer(session getty.Session, pkg *DubboPackage,
 
 	if pkg == nil {
 		pkg = &DubboPackage{}
-		pkg.Body = hessian.NewRequest([]interface{}{}, nil)
+		pkg.Body = hessian2.NewRequest([]interface{}{}, nil)
 		pkg.Body = []interface{}{}
-		pkg.Header.Type = hessian.PackageHeartbeat
+		pkg.Header.Type = hessian2.PackageHeartbeat
 		pkg.Header.SerialID = byte(S_Dubbo)
 	}
 	pkg.Header.ID = int64(sequence)
diff --git a/protocol/dubbo/client_test.go b/protocol/dubbo/client_test.go
index 8b0ba169b82910652c64011c47568c7a018ae5e0..a3b194ad407aa8579ced04b591588a2656423c2c 100644
--- a/protocol/dubbo/client_test.go
+++ b/protocol/dubbo/client_test.go
@@ -20,6 +20,7 @@ package dubbo
 import (
 	"bytes"
 	"context"
+	"fmt"
 	"sync"
 	"testing"
 	"time"
@@ -129,6 +130,15 @@ func TestClientCall(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, User{Id: "1", Name: ""}, *user)
 
+	user = &User{}
+	r1 := "v1"
+	r2 := &AttaTestObject{Ti: 45, Desc: "v2"}
+	err = c.Call(NewRequest(mockAddress, url, "GetUserForAttachment", []interface{}{1}, map[string]interface{}{"sim": r1, "comp": r2}), NewResponse(user, nil))
+	assert.NoError(t, err)
+	// the param is transfered from client to server by attachment, and the server will return the attachment value.
+	// the test should check the value came back from server.
+	assert.Equal(t, User{Id: r1, Name: fmt.Sprintf("%+v", *r2)}, *user)
+
 	// destroy
 	proto.Destroy()
 }
@@ -166,10 +176,11 @@ func TestClientAsyncCall(t *testing.T) {
 func InitTest(t *testing.T) (protocol.Protocol, common.URL) {
 
 	hessian.RegisterPOJO(&User{})
+	hessian.RegisterPOJO(&AttaTestObject{})
 
 	methods, err := common.ServiceMap.Register("com.ikurento.user.UserProvider", "dubbo", &UserProvider{})
 	assert.NoError(t, err)
-	assert.Equal(t, "GetBigPkg,GetUser,GetUser0,GetUser1,GetUser2,GetUser3,GetUser4,GetUser5,GetUser6", methods)
+	assert.Equal(t, "GetBigPkg,GetUser,GetUser0,GetUser1,GetUser2,GetUser3,GetUser4,GetUser5,GetUser6,GetUserForAttachment", methods)
 
 	// config
 	SetClientConf(ClientConfig{
@@ -296,6 +307,16 @@ func (u *UserProvider) GetUser6(id int64) (*User, error) {
 	return &User{Id: "1"}, nil
 }
 
+func (u *UserProvider) GetUserForAttachment(context context.Context, id int64) (*User, error) {
+	if id == 0 {
+		return nil, nil
+	}
+	var attachments = context.Value("attachment").(map[string]interface{})
+	Id := attachments["sim"].(string)
+	name := fmt.Sprintf("%+v", *(attachments["comp"].(*AttaTestObject)))
+	return &User{Id: Id, Name: name}, nil
+}
+
 func (u *UserProvider) Reference() string {
 	return "UserProvider"
 }
@@ -303,3 +324,12 @@ func (u *UserProvider) Reference() string {
 func (u User) JavaClassName() string {
 	return "com.ikurento.user.User"
 }
+
+type AttaTestObject struct {
+	Ti   int64
+	Desc string
+}
+
+func (u *AttaTestObject) JavaClassName() string {
+	return "UserProvider"
+}
diff --git a/protocol/dubbo/codec.go b/protocol/dubbo/codec.go
index 1f7d107544a06d0ef83bcb54ff6f03daf2dc517b..c33c92dfd924803b32a513d3767cbfaaa51f372c 100644
--- a/protocol/dubbo/codec.go
+++ b/protocol/dubbo/codec.go
@@ -25,11 +25,14 @@ import (
 )
 
 import (
-	"github.com/apache/dubbo-go-hessian2"
 	"github.com/apache/dubbo-go/common"
 	perrors "github.com/pkg/errors"
 )
 
+import (
+	"github.com/apache/dubbo-go/protocol/dubbo/hessian2"
+)
+
 //SerialID serial ID
 type SerialID byte
 
@@ -54,13 +57,13 @@ const (
 // dubbo package
 ////////////////////////////////////////////
 
-// SequenceType ...
+// SequenceType sequence type
 type SequenceType int64
 
-// DubboPackage ...
+// nolint
 type DubboPackage struct {
-	Header  hessian.DubboHeader
-	Service hessian.Service
+	Header  hessian2.DubboHeader
+	Service hessian2.Service
 	Body    interface{}
 	Err     error
 }
@@ -72,7 +75,7 @@ func (p DubboPackage) String() string {
 
 // Marshal encode hessian package.
 func (p *DubboPackage) Marshal() (*bytes.Buffer, error) {
-	codec := hessian.NewHessianCodec(nil)
+	codec := hessian2.NewHessianCodec(nil)
 
 	pkg, err := codec.Write(p.Service, p.Header, p.Body)
 	if err != nil {
@@ -82,15 +85,15 @@ func (p *DubboPackage) Marshal() (*bytes.Buffer, error) {
 	return bytes.NewBuffer(pkg), nil
 }
 
-// Unmarshal dncode hessian package.
+// Unmarshal decodes hessian package.
 func (p *DubboPackage) Unmarshal(buf *bytes.Buffer, opts ...interface{}) error {
 	// fix issue https://github.com/apache/dubbo-go/issues/380
 	bufLen := buf.Len()
-	if bufLen < hessian.HEADER_LENGTH {
-		return perrors.WithStack(hessian.ErrHeaderNotEnough)
+	if bufLen < hessian2.HEADER_LENGTH {
+		return perrors.WithStack(hessian2.ErrHeaderNotEnough)
 	}
 
-	codec := hessian.NewHessianCodec(bufio.NewReaderSize(buf, bufLen))
+	codec := hessian2.NewHessianCodec(bufio.NewReaderSize(buf, bufLen))
 
 	// read header
 	err := codec.ReadHeader(&p.Header)
@@ -104,7 +107,7 @@ func (p *DubboPackage) Unmarshal(buf *bytes.Buffer, opts ...interface{}) error {
 			return perrors.Errorf("opts[0] is not of type *Client")
 		}
 
-		if p.Header.Type&hessian.PackageRequest != 0x00 {
+		if p.Header.Type&hessian2.PackageRequest != 0x00 {
 			// size of this array must be '7'
 			// https://github.com/apache/dubbo-go-hessian2/blob/master/request.go#L272
 			p.Body = make([]interface{}, 7)
@@ -113,7 +116,7 @@ func (p *DubboPackage) Unmarshal(buf *bytes.Buffer, opts ...interface{}) error {
 			if !ok {
 				return perrors.Errorf("client.GetPendingResponse(%v) = nil", p.Header.ID)
 			}
-			p.Body = &hessian.Response{RspObj: pendingRsp.(*PendingResponse).response.reply}
+			p.Body = &hessian2.DubboResponse{RspObj: pendingRsp.(*PendingResponse).response.reply}
 		}
 	}
 
diff --git a/protocol/dubbo/codec_test.go b/protocol/dubbo/codec_test.go
index c2ca443637e23101679770e464f49e0cbdeab2a9..6dfb87ea009394a2319c4b353e8f9cadc7b50e81 100644
--- a/protocol/dubbo/codec_test.go
+++ b/protocol/dubbo/codec_test.go
@@ -24,15 +24,18 @@ import (
 )
 
 import (
-	hessian "github.com/apache/dubbo-go-hessian2"
 	perrors "github.com/pkg/errors"
 	"github.com/stretchr/testify/assert"
 )
 
+import (
+	"github.com/apache/dubbo-go/protocol/dubbo/hessian2"
+)
+
 func TestDubboPackageMarshalAndUnmarshal(t *testing.T) {
 	pkg := &DubboPackage{}
 	pkg.Body = []interface{}{"a"}
-	pkg.Header.Type = hessian.PackageHeartbeat
+	pkg.Header.Type = hessian2.PackageHeartbeat
 	pkg.Header.SerialID = byte(S_Dubbo)
 	pkg.Header.ID = 10086
 
@@ -44,13 +47,13 @@ func TestDubboPackageMarshalAndUnmarshal(t *testing.T) {
 	pkgres.Body = []interface{}{}
 	err = pkgres.Unmarshal(data)
 	assert.NoError(t, err)
-	assert.Equal(t, hessian.PackageHeartbeat|hessian.PackageRequest|hessian.PackageRequest_TwoWay, pkgres.Header.Type)
+	assert.Equal(t, hessian2.PackageHeartbeat|hessian2.PackageRequest|hessian2.PackageRequest_TwoWay, pkgres.Header.Type)
 	assert.Equal(t, byte(S_Dubbo), pkgres.Header.SerialID)
 	assert.Equal(t, int64(10086), pkgres.Header.ID)
 	assert.Equal(t, 0, len(pkgres.Body.([]interface{})))
 
 	// request
-	pkg.Header.Type = hessian.PackageRequest
+	pkg.Header.Type = hessian2.PackageRequest
 	pkg.Service.Interface = "Service"
 	pkg.Service.Path = "path"
 	pkg.Service.Version = "2.6"
@@ -63,7 +66,7 @@ func TestDubboPackageMarshalAndUnmarshal(t *testing.T) {
 	pkgres.Body = make([]interface{}, 7)
 	err = pkgres.Unmarshal(data)
 	assert.NoError(t, err)
-	assert.Equal(t, hessian.PackageRequest, pkgres.Header.Type)
+	assert.Equal(t, hessian2.PackageRequest, pkgres.Header.Type)
 	assert.Equal(t, byte(S_Dubbo), pkgres.Header.SerialID)
 	assert.Equal(t, int64(10086), pkgres.Header.ID)
 	assert.Equal(t, "2.0.2", pkgres.Body.([]interface{})[0])
@@ -72,12 +75,12 @@ func TestDubboPackageMarshalAndUnmarshal(t *testing.T) {
 	assert.Equal(t, "Method", pkgres.Body.([]interface{})[3])
 	assert.Equal(t, "Ljava/lang/String;", pkgres.Body.([]interface{})[4])
 	assert.Equal(t, []interface{}{"a"}, pkgres.Body.([]interface{})[5])
-	assert.Equal(t, map[string]string{"dubbo": "2.0.2", "interface": "Service", "path": "path", "timeout": "1000", "version": "2.6"}, pkgres.Body.([]interface{})[6])
+	assert.Equal(t, map[string]interface{}{"dubbo": "2.0.2", "interface": "Service", "path": "path", "timeout": "1000", "version": "2.6"}, pkgres.Body.([]interface{})[6])
 }
 
 func TestIssue380(t *testing.T) {
 	pkg := &DubboPackage{}
 	buf := bytes.NewBuffer([]byte("hello"))
 	err := pkg.Unmarshal(buf)
-	assert.True(t, perrors.Cause(err) == hessian.ErrHeaderNotEnough)
+	assert.True(t, perrors.Cause(err) == hessian2.ErrHeaderNotEnough)
 }
diff --git a/protocol/dubbo/config.go b/protocol/dubbo/config.go
index 635d12109add17cfac1056316c9d53817525fd67..b47ec1cc3422dcbcac921f08888c7a777e72e246 100644
--- a/protocol/dubbo/config.go
+++ b/protocol/dubbo/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 is session configuration for getty.
 	GettySessionParam struct {
@@ -178,9 +181,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 {
@@ -198,9 +201,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/protocol/dubbo/dubbo_invoker.go b/protocol/dubbo/dubbo_invoker.go
index 59202d5f49f30acb9e75c4e2f135601285f08e13..983a05dcec1ff444e6637c5094d691c266f6c855 100644
--- a/protocol/dubbo/dubbo_invoker.go
+++ b/protocol/dubbo/dubbo_invoker.go
@@ -150,8 +150,7 @@ func (di *DubboInvoker) appendCtx(ctx context.Context, inv *invocation_impl.RPCI
 	// inject opentracing ctx
 	currentSpan := opentracing.SpanFromContext(ctx)
 	if currentSpan != nil {
-		carrier := opentracing.TextMapCarrier(inv.Attachments())
-		err := opentracing.GlobalTracer().Inject(currentSpan.Context(), opentracing.TextMap, carrier)
+		err := injectTraceCtx(currentSpan, inv)
 		if err != nil {
 			logger.Errorf("Could not inject the span context into attachments: %v", err)
 		}
diff --git a/protocol/dubbo/dubbo_invoker_test.go b/protocol/dubbo/dubbo_invoker_test.go
index c0640d5558fcb9fb00f02eba0fddc54bb4162592..bf352c082c47d86e6382135bb036be6709ed804d 100644
--- a/protocol/dubbo/dubbo_invoker_test.go
+++ b/protocol/dubbo/dubbo_invoker_test.go
@@ -52,7 +52,7 @@ func TestDubboInvokerInvoke(t *testing.T) {
 	user := &User{}
 
 	inv := invocation.NewRPCInvocationWithOptions(invocation.WithMethodName(mockMethodNameGetUser), invocation.WithArguments([]interface{}{"1", "username"}),
-		invocation.WithReply(user), invocation.WithAttachments(map[string]string{"test_key": "test_value"}))
+		invocation.WithReply(user), invocation.WithAttachments(map[string]interface{}{"test_key": "test_value"}))
 
 	// Call
 	res := invoker.Invoke(context.Background(), inv)
diff --git a/protocol/dubbo/hessian2/const.go b/protocol/dubbo/hessian2/const.go
new file mode 100644
index 0000000000000000000000000000000000000000..74a00b601db22397916aab215ccd33bc918d91e7
--- /dev/null
+++ b/protocol/dubbo/hessian2/const.go
@@ -0,0 +1,243 @@
+/*
+ * 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.
+ */
+
+// This constants are also defined in dubbo constants.  But we will still used these until hessian is stable.
+
+package hessian2
+
+import (
+	"reflect"
+	"regexp"
+)
+
+import (
+	perrors "github.com/pkg/errors"
+)
+
+const (
+	mask = byte(127)
+	flag = byte(128)
+)
+
+const (
+	// Zero : byte zero
+	Zero = byte(0x00)
+)
+
+// constansts
+const (
+	TAG_READ        = int32(-1)
+	ASCII_GAP       = 32
+	CHUNK_SIZE      = 4096
+	BC_BINARY       = byte('B') // final chunk
+	BC_BINARY_CHUNK = byte('A') // non-final chunk
+
+	BC_BINARY_DIRECT  = byte(0x20) // 1-byte length binary
+	BINARY_DIRECT_MAX = byte(0x0f)
+	BC_BINARY_SHORT   = byte(0x34) // 2-byte length binary
+	BINARY_SHORT_MAX  = 0x3ff      // 0-1023 binary
+
+	BC_DATE        = byte(0x4a) // 64-bit millisecond UTC date
+	BC_DATE_MINUTE = byte(0x4b) // 32-bit minute UTC date
+
+	BC_DOUBLE = byte('D') // IEEE 64-bit double
+
+	BC_DOUBLE_ZERO  = byte(0x5b)
+	BC_DOUBLE_ONE   = byte(0x5c)
+	BC_DOUBLE_BYTE  = byte(0x5d)
+	BC_DOUBLE_SHORT = byte(0x5e)
+	BC_DOUBLE_MILL  = byte(0x5f)
+
+	BC_FALSE = byte('F') // boolean false
+
+	BC_INT = byte('I') // 32-bit int
+
+	INT_DIRECT_MIN = -0x10
+	INT_DIRECT_MAX = byte(0x2f)
+	BC_INT_ZERO    = byte(0x90)
+
+	INT_BYTE_MIN     = -0x800
+	INT_BYTE_MAX     = 0x7ff
+	BC_INT_BYTE_ZERO = byte(0xc8)
+
+	BC_END = byte('Z')
+
+	INT_SHORT_MIN     = -0x40000
+	INT_SHORT_MAX     = 0x3ffff
+	BC_INT_SHORT_ZERO = byte(0xd4)
+
+	BC_LIST_VARIABLE           = byte(0x55)
+	BC_LIST_FIXED              = byte('V')
+	BC_LIST_VARIABLE_UNTYPED   = byte(0x57)
+	BC_LIST_FIXED_UNTYPED      = byte(0x58)
+	_listFixedTypedLenTagMin   = byte(0x70)
+	_listFixedTypedLenTagMax   = byte(0x77)
+	_listFixedUntypedLenTagMin = byte(0x78)
+	_listFixedUntypedLenTagMax = byte(0x7f)
+
+	BC_LIST_DIRECT         = byte(0x70)
+	BC_LIST_DIRECT_UNTYPED = byte(0x78)
+	LIST_DIRECT_MAX        = byte(0x7)
+
+	BC_LONG         = byte('L') // 64-bit signed integer
+	LONG_DIRECT_MIN = -0x08
+	LONG_DIRECT_MAX = byte(0x0f)
+	BC_LONG_ZERO    = byte(0xe0)
+
+	LONG_BYTE_MIN     = -0x800
+	LONG_BYTE_MAX     = 0x7ff
+	BC_LONG_BYTE_ZERO = byte(0xf8)
+
+	LONG_SHORT_MIN     = -0x40000
+	LONG_SHORT_MAX     = 0x3ffff
+	BC_LONG_SHORT_ZERO = byte(0x3c)
+
+	BC_LONG_INT = byte(0x59)
+
+	BC_MAP         = byte('M')
+	BC_MAP_UNTYPED = byte('H')
+
+	BC_NULL = byte('N') // x4e
+
+	BC_OBJECT     = byte('O')
+	BC_OBJECT_DEF = byte('C')
+
+	BC_OBJECT_DIRECT  = byte(0x60)
+	OBJECT_DIRECT_MAX = byte(0x0f)
+
+	BC_REF = byte(0x51)
+
+	BC_STRING       = byte('S') // final string
+	BC_STRING_CHUNK = byte('R') // non-final string
+
+	BC_STRING_DIRECT  = byte(0x00)
+	STRING_DIRECT_MAX = byte(0x1f)
+	BC_STRING_SHORT   = byte(0x30)
+	STRING_SHORT_MAX  = 0x3ff
+
+	BC_TRUE = byte('T')
+
+	P_PACKET_CHUNK = byte(0x4f)
+	P_PACKET       = byte('P')
+
+	P_PACKET_DIRECT   = byte(0x80)
+	PACKET_DIRECT_MAX = byte(0x7f)
+
+	P_PACKET_SHORT   = byte(0x70)
+	PACKET_SHORT_MAX = 0xfff
+	ARRAY_STRING     = "[string"
+	ARRAY_INT        = "[int"
+	ARRAY_DOUBLE     = "[double"
+	ARRAY_FLOAT      = "[float"
+	ARRAY_BOOL       = "[boolean"
+	ARRAY_LONG       = "[long"
+
+	PATH_KEY      = "path"
+	GROUP_KEY     = "group"
+	INTERFACE_KEY = "interface"
+	VERSION_KEY   = "version"
+	TIMEOUT_KEY   = "timeout"
+
+	STRING_NIL   = ""
+	STRING_TRUE  = "true"
+	STRING_FALSE = "false"
+	STRING_ZERO  = "0.0"
+	STRING_ONE   = "1.0"
+)
+
+// DubboResponse related consts
+const (
+	Response_OK                byte = 20
+	Response_CLIENT_TIMEOUT    byte = 30
+	Response_SERVER_TIMEOUT    byte = 31
+	Response_BAD_REQUEST       byte = 40
+	Response_BAD_RESPONSE      byte = 50
+	Response_SERVICE_NOT_FOUND byte = 60
+	Response_SERVICE_ERROR     byte = 70
+	Response_SERVER_ERROR      byte = 80
+	Response_CLIENT_ERROR      byte = 90
+
+	// According to "java dubbo" There are two cases of response:
+	// 		1. with attachments
+	// 		2. no attachments
+	RESPONSE_WITH_EXCEPTION                  int32 = 0
+	RESPONSE_VALUE                           int32 = 1
+	RESPONSE_NULL_VALUE                      int32 = 2
+	RESPONSE_WITH_EXCEPTION_WITH_ATTACHMENTS int32 = 3
+	RESPONSE_VALUE_WITH_ATTACHMENTS          int32 = 4
+	RESPONSE_NULL_VALUE_WITH_ATTACHMENTS     int32 = 5
+)
+
+/**
+ * the dubbo protocol header length is 16 Bytes.
+ * the first 2 Bytes is magic code '0xdabb'
+ * the next 1 Byte is message flags, in which its 16-20 bit is serial id, 21 for event, 22 for two way, 23 for request/response flag
+ * the next 1 Bytes is response state.
+ * the next 8 Bytes is package DI.
+ * the next 4 Bytes is package length.
+ **/
+const (
+	// header length.
+	HEADER_LENGTH = 16
+
+	// magic header
+	MAGIC      = uint16(0xdabb)
+	MAGIC_HIGH = byte(0xda)
+	MAGIC_LOW  = byte(0xbb)
+
+	// message flag.
+	FLAG_REQUEST = byte(0x80)
+	FLAG_TWOWAY  = byte(0x40)
+	FLAG_EVENT   = byte(0x20) // for heartbeat
+	SERIAL_MASK  = 0x1f
+
+	DUBBO_VERSION                          = "2.5.4"
+	DUBBO_VERSION_KEY                      = "dubbo"
+	DEFAULT_DUBBO_PROTOCOL_VERSION         = "2.0.2" // Dubbo RPC protocol version, for compatibility, it must not be between 2.0.10 ~ 2.6.2
+	LOWEST_VERSION_FOR_RESPONSE_ATTACHMENT = 2000200
+	DEFAULT_LEN                            = 8388608 // 8 * 1024 * 1024 default body max length
+)
+
+// regular
+const (
+	JAVA_IDENT_REGEX = "(?:[_$a-zA-Z][_$a-zA-Z0-9]*)"
+	CLASS_DESC       = "(?:L" + JAVA_IDENT_REGEX + "(?:\\/" + JAVA_IDENT_REGEX + ")*;)"
+	ARRAY_DESC       = "(?:\\[+(?:(?:[VZBCDFIJS])|" + CLASS_DESC + "))"
+	DESC_REGEX       = "(?:(?:[VZBCDFIJS])|" + CLASS_DESC + "|" + ARRAY_DESC + ")"
+)
+
+// Dubbo request response related consts
+var (
+	DubboRequestHeaderBytesTwoWay = [HEADER_LENGTH]byte{MAGIC_HIGH, MAGIC_LOW, FLAG_REQUEST | FLAG_TWOWAY}
+	DubboRequestHeaderBytes       = [HEADER_LENGTH]byte{MAGIC_HIGH, MAGIC_LOW, FLAG_REQUEST}
+	DubboResponseHeaderBytes      = [HEADER_LENGTH]byte{MAGIC_HIGH, MAGIC_LOW, Zero, Response_OK}
+	DubboRequestHeartbeatHeader   = [HEADER_LENGTH]byte{MAGIC_HIGH, MAGIC_LOW, FLAG_REQUEST | FLAG_TWOWAY | FLAG_EVENT}
+	DubboResponseHeartbeatHeader  = [HEADER_LENGTH]byte{MAGIC_HIGH, MAGIC_LOW, FLAG_EVENT}
+)
+
+// Error part
+var (
+	ErrHeaderNotEnough = perrors.New("header buffer too short")
+	ErrBodyNotEnough   = perrors.New("body buffer too short")
+	ErrJavaException   = perrors.New("got java exception")
+	ErrIllegalPackage  = perrors.New("illegal package!")
+)
+
+// nolint
+var DescRegex, _ = regexp.Compile(DESC_REGEX)
+
+var NilValue = reflect.Zero(reflect.TypeOf((*interface{})(nil)).Elem())
diff --git a/protocol/dubbo/hessian2/hessian_dubbo.go b/protocol/dubbo/hessian2/hessian_dubbo.go
new file mode 100644
index 0000000000000000000000000000000000000000..1afa4ec96eccbb8077852dfcc020e0eb05be3257
--- /dev/null
+++ b/protocol/dubbo/hessian2/hessian_dubbo.go
@@ -0,0 +1,251 @@
+/*
+ * 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 hessian2
+
+import (
+	"bufio"
+	"encoding/binary"
+	"time"
+)
+
+import (
+	"github.com/apache/dubbo-go-hessian2"
+	perrors "github.com/pkg/errors"
+)
+
+// enum part
+const (
+	PackageError              = PackageType(0x01)
+	PackageRequest            = PackageType(0x02)
+	PackageResponse           = PackageType(0x04)
+	PackageHeartbeat          = PackageType(0x08)
+	PackageRequest_TwoWay     = PackageType(0x10)
+	PackageResponse_Exception = PackageType(0x20)
+	PackageType_BitSize       = 0x2f
+)
+
+// PackageType nolint
+type PackageType int
+
+// DubboHeader dubbo header
+type DubboHeader struct {
+	SerialID       byte
+	Type           PackageType
+	ID             int64
+	BodyLen        int
+	ResponseStatus byte
+}
+
+// Service defines service instance
+type Service struct {
+	Path      string
+	Interface string
+	Group     string
+	Version   string
+	Method    string
+	Timeout   time.Duration // request timeout
+}
+
+// HessianCodec defines hessian codec
+type HessianCodec struct {
+	pkgType PackageType
+	reader  *bufio.Reader
+	bodyLen int
+}
+
+// NewHessianCodec generate a new hessian codec instance
+func NewHessianCodec(reader *bufio.Reader) *HessianCodec {
+	return &HessianCodec{
+		reader: reader,
+	}
+}
+
+// NewHessianCodec generate a new hessian codec instance
+func NewHessianCodecCustom(pkgType PackageType, reader *bufio.Reader, bodyLen int) *HessianCodec {
+	return &HessianCodec{
+		pkgType: pkgType,
+		reader:  reader,
+		bodyLen: bodyLen,
+	}
+}
+
+func (h *HessianCodec) Write(service Service, header DubboHeader, body interface{}) ([]byte, error) {
+	switch header.Type {
+	case PackageHeartbeat:
+		if header.ResponseStatus == Zero {
+			return packRequest(service, header, body)
+		}
+		return packResponse(header, body)
+
+	case PackageRequest, PackageRequest_TwoWay:
+		return packRequest(service, header, body)
+
+	case PackageResponse:
+		return packResponse(header, body)
+
+	default:
+		return nil, perrors.Errorf("Unrecognised message type: %v", header.Type)
+	}
+
+	// unreachable return nil, nil
+}
+
+// ReadHeader uses hessian codec to read dubbo header
+func (h *HessianCodec) ReadHeader(header *DubboHeader) error {
+
+	var err error
+
+	if h.reader.Size() < HEADER_LENGTH {
+		return ErrHeaderNotEnough
+	}
+	buf, err := h.reader.Peek(HEADER_LENGTH)
+	if err != nil { // this is impossible
+		return perrors.WithStack(err)
+	}
+	_, err = h.reader.Discard(HEADER_LENGTH)
+	if err != nil { // this is impossible
+		return perrors.WithStack(err)
+	}
+
+	//// read header
+
+	if buf[0] != MAGIC_HIGH && buf[1] != MAGIC_LOW {
+		return ErrIllegalPackage
+	}
+
+	// Header{serialization id(5 bit), event, two way, req/response}
+	if header.SerialID = buf[2] & SERIAL_MASK; header.SerialID == Zero {
+		return perrors.Errorf("serialization ID:%v", header.SerialID)
+	}
+
+	flag := buf[2] & FLAG_EVENT
+	if flag != Zero {
+		header.Type |= PackageHeartbeat
+	}
+	flag = buf[2] & FLAG_REQUEST
+	if flag != Zero {
+		header.Type |= PackageRequest
+		flag = buf[2] & FLAG_TWOWAY
+		if flag != Zero {
+			header.Type |= PackageRequest_TwoWay
+		}
+	} else {
+		header.Type |= PackageResponse
+		header.ResponseStatus = buf[3]
+		if header.ResponseStatus != Response_OK {
+			header.Type |= PackageResponse_Exception
+		}
+	}
+
+	// Header{req id}
+	header.ID = int64(binary.BigEndian.Uint64(buf[4:]))
+
+	// Header{body len}
+	header.BodyLen = int(binary.BigEndian.Uint32(buf[12:]))
+	if header.BodyLen < 0 {
+		return ErrIllegalPackage
+	}
+
+	h.pkgType = header.Type
+	h.bodyLen = header.BodyLen
+
+	if h.reader.Buffered() < h.bodyLen {
+		return ErrBodyNotEnough
+	}
+
+	return perrors.WithStack(err)
+
+}
+
+// ReadBody uses hessian codec to read response body
+func (h *HessianCodec) ReadBody(rspObj interface{}) error {
+
+	if h.reader.Buffered() < h.bodyLen {
+		return ErrBodyNotEnough
+	}
+	buf, err := h.reader.Peek(h.bodyLen)
+	if err != nil {
+		return perrors.WithStack(err)
+	}
+	_, err = h.reader.Discard(h.bodyLen)
+	if err != nil { // this is impossible
+		return perrors.WithStack(err)
+	}
+
+	switch h.pkgType & PackageType_BitSize {
+	case PackageResponse | PackageHeartbeat | PackageResponse_Exception, PackageResponse | PackageResponse_Exception:
+		decoder := hessian.NewDecoder(buf[:])
+		exception, err := decoder.Decode()
+		if err != nil {
+			return perrors.WithStack(err)
+		}
+		rsp, ok := rspObj.(*DubboResponse)
+		if !ok {
+			return perrors.Errorf("java exception:%s", exception.(string))
+		}
+		rsp.Exception = perrors.Errorf("java exception:%s", exception.(string))
+		return nil
+	case PackageRequest | PackageHeartbeat, PackageResponse | PackageHeartbeat:
+	case PackageRequest:
+		if rspObj != nil {
+			if err = unpackRequestBody(hessian.NewDecoder(buf[:]), rspObj); err != nil {
+				return perrors.WithStack(err)
+			}
+		}
+	case PackageResponse:
+		if rspObj != nil {
+			if err = unpackResponseBody(hessian.NewDecoder(buf[:]), rspObj); err != nil {
+				return perrors.WithStack(err)
+			}
+		}
+	}
+
+	return nil
+}
+
+// ignore body, but only read attachments
+func (h *HessianCodec) ReadAttachments() (map[string]interface{}, error) {
+	if h.reader.Buffered() < h.bodyLen {
+		return nil, ErrBodyNotEnough
+	}
+	buf, err := h.reader.Peek(h.bodyLen)
+	if err != nil {
+		return nil, perrors.WithStack(err)
+	}
+	_, err = h.reader.Discard(h.bodyLen)
+	if err != nil { // this is impossible
+		return nil, perrors.WithStack(err)
+	}
+
+	switch h.pkgType & PackageType_BitSize {
+	case PackageRequest:
+		rspObj := make([]interface{}, 7)
+		if err = unpackRequestBody(hessian.NewDecoderWithSkip(buf[:]), rspObj); err != nil {
+			return nil, perrors.WithStack(err)
+		}
+		return rspObj[6].(map[string]interface{}), nil
+	case PackageResponse:
+		rspObj := &DubboResponse{}
+		if err = unpackResponseBody(hessian.NewDecoderWithSkip(buf[:]), rspObj); err != nil {
+			return nil, perrors.WithStack(err)
+		}
+		return rspObj.Attachments, nil
+	}
+
+	return nil, nil
+}
diff --git a/protocol/dubbo/hessian2/hessian_dubbo_test.go b/protocol/dubbo/hessian2/hessian_dubbo_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..c3f19f04536484816e4b4f709f534dcbf4adb2b4
--- /dev/null
+++ b/protocol/dubbo/hessian2/hessian_dubbo_test.go
@@ -0,0 +1,231 @@
+/*
+ * 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 hessian2
+
+import (
+	"bufio"
+	"bytes"
+	"reflect"
+	"testing"
+	"time"
+)
+
+import (
+	hessian "github.com/apache/dubbo-go-hessian2"
+	"github.com/stretchr/testify/assert"
+)
+
+type Case struct {
+	A string
+	B int
+}
+
+type CaseA struct {
+	A string
+	B int
+	C Case
+}
+
+type CaseB struct {
+	A string
+	B CaseA
+}
+
+func (c *CaseB) JavaClassName() string {
+	return "com.test.caseb"
+}
+
+func (c CaseA) JavaClassName() string {
+	return "com.test.casea"
+}
+
+//JavaClassName  java fully qualified path
+func (c Case) JavaClassName() string {
+	return "com.test.case"
+}
+
+func doTestHessianEncodeHeader(t *testing.T, packageType PackageType, responseStatus byte, body interface{}) ([]byte, error) {
+	hessian.RegisterPOJO(&Case{})
+	codecW := NewHessianCodec(nil)
+	resp, err := codecW.Write(Service{
+		Path:      "test",
+		Interface: "ITest",
+		Version:   "v1.0",
+		Method:    "test",
+		Timeout:   time.Second * 10,
+	}, DubboHeader{
+		SerialID:       2,
+		Type:           packageType,
+		ID:             1,
+		ResponseStatus: responseStatus,
+	}, body)
+	assert.Nil(t, err)
+	return resp, err
+}
+
+func doTestResponse(t *testing.T, packageType PackageType, responseStatus byte, body interface{}, decodedResponse *DubboResponse, assertFunc func()) {
+	resp, err := doTestHessianEncodeHeader(t, packageType, responseStatus, body)
+
+	codecR := NewHessianCodec(bufio.NewReader(bytes.NewReader(resp)))
+
+	h := &DubboHeader{}
+	err = codecR.ReadHeader(h)
+	assert.Nil(t, err)
+
+	assert.Equal(t, byte(2), h.SerialID)
+	assert.Equal(t, packageType, h.Type&(PackageRequest|PackageResponse|PackageHeartbeat))
+	assert.Equal(t, int64(1), h.ID)
+	assert.Equal(t, responseStatus, h.ResponseStatus)
+
+	err = codecR.ReadBody(decodedResponse)
+	assert.Nil(t, err)
+	t.Log(decodedResponse)
+
+	if assertFunc != nil {
+		assertFunc()
+		return
+	}
+
+	if h.ResponseStatus != Zero && h.ResponseStatus != Response_OK {
+		assert.Equal(t, "java exception:"+body.(string), decodedResponse.Exception.Error())
+		return
+	}
+
+	in, _ := hessian.EnsureInterface(hessian.UnpackPtrValue(hessian.EnsurePackValue(body)), nil)
+	out, _ := hessian.EnsureInterface(hessian.UnpackPtrValue(hessian.EnsurePackValue(decodedResponse.RspObj)), nil)
+	assert.Equal(t, in, out)
+}
+
+func TestResponse(t *testing.T) {
+	caseObj := Case{A: "a", B: 1}
+	decodedResponse := &DubboResponse{}
+
+	arr := []*Case{&caseObj}
+	var arrRes []interface{}
+	decodedResponse.RspObj = &arrRes
+	doTestResponse(t, PackageResponse, Response_OK, arr, decodedResponse, func() {
+		assert.Equal(t, 1, len(arrRes))
+		assert.Equal(t, &caseObj, arrRes[0])
+	})
+
+	decodedResponse.RspObj = &Case{}
+	doTestResponse(t, PackageResponse, Response_OK, &Case{A: "a", B: 1}, decodedResponse, nil)
+
+	s := "ok!!!!!"
+	strObj := ""
+	decodedResponse.RspObj = &strObj
+	doTestResponse(t, PackageResponse, Response_OK, s, decodedResponse, nil)
+
+	var intObj int64
+	decodedResponse.RspObj = &intObj
+	doTestResponse(t, PackageResponse, Response_OK, int64(3), decodedResponse, nil)
+
+	boolObj := false
+	decodedResponse.RspObj = &boolObj
+	doTestResponse(t, PackageResponse, Response_OK, true, decodedResponse, nil)
+
+	strObj = ""
+	decodedResponse.RspObj = &strObj
+	doTestResponse(t, PackageResponse, hessian.Response_SERVER_ERROR, "error!!!!!", decodedResponse, nil)
+
+	mapObj := map[string][]*Case{"key": {&caseObj}}
+	mapRes := map[interface{}]interface{}{}
+	decodedResponse.RspObj = &mapRes
+	doTestResponse(t, PackageResponse, Response_OK, mapObj, decodedResponse, func() {
+		c, ok := mapRes["key"]
+		if !ok {
+			assert.FailNow(t, "no key in decoded response map")
+		}
+
+		mapValueArr, ok := c.([]*Case)
+		if !ok {
+			assert.FailNow(t, "invalid decoded response map value", "expect []*Case, but get %v", reflect.TypeOf(c))
+		}
+		assert.Equal(t, 1, len(mapValueArr))
+		assert.Equal(t, &caseObj, mapValueArr[0])
+	})
+}
+
+func doTestRequest(t *testing.T, packageType PackageType, responseStatus byte, body interface{}) {
+	resp, err := doTestHessianEncodeHeader(t, packageType, responseStatus, body)
+
+	codecR := NewHessianCodec(bufio.NewReader(bytes.NewReader(resp)))
+
+	h := &DubboHeader{}
+	err = codecR.ReadHeader(h)
+	assert.Nil(t, err)
+	assert.Equal(t, byte(2), h.SerialID)
+	assert.Equal(t, packageType, h.Type&(PackageRequest|PackageResponse|PackageHeartbeat))
+	assert.Equal(t, int64(1), h.ID)
+	assert.Equal(t, responseStatus, h.ResponseStatus)
+
+	c := make([]interface{}, 7)
+	err = codecR.ReadBody(c)
+	assert.Nil(t, err)
+	t.Log(c)
+	assert.True(t, len(body.([]interface{})) == len(c[5].([]interface{})))
+}
+
+func TestRequest(t *testing.T) {
+	doTestRequest(t, PackageRequest, Zero, []interface{}{"a"})
+	doTestRequest(t, PackageRequest, Zero, []interface{}{"a", 3})
+	doTestRequest(t, PackageRequest, Zero, []interface{}{"a", true})
+	doTestRequest(t, PackageRequest, Zero, []interface{}{"a", 3, true})
+	doTestRequest(t, PackageRequest, Zero, []interface{}{3.2, true})
+	doTestRequest(t, PackageRequest, Zero, []interface{}{"a", 3, true, &Case{A: "a", B: 3}})
+	doTestRequest(t, PackageRequest, Zero, []interface{}{"a", 3, true, []*Case{{A: "a", B: 3}}})
+	doTestRequest(t, PackageRequest, Zero, []interface{}{map[string][]*Case{"key": {{A: "a", B: 3}}}})
+}
+
+func TestHessianCodec_ReadAttachments(t *testing.T) {
+	hessian.RegisterPOJO(&AttachTestObject{})
+	body := &DubboResponse{
+		RspObj:      &CaseB{A: "A", B: CaseA{A: "a", B: 1, C: Case{A: "c", B: 2}}},
+		Exception:   nil,
+		Attachments: map[string]interface{}{DUBBO_VERSION_KEY: "2.6.4", "att": AttachTestObject{Id: 23, Name: "haha"}},
+	}
+	resp, err := doTestHessianEncodeHeader(t, PackageResponse, Response_OK, body)
+	assert.NoError(t, err)
+	hessian.UnRegisterPOJOs(&CaseB{}, &CaseA{})
+	codecR1 := NewHessianCodec(bufio.NewReader(bytes.NewReader(resp)))
+	codecR2 := NewHessianCodec(bufio.NewReader(bytes.NewReader(resp)))
+	h := &DubboHeader{}
+	assert.NoError(t, codecR1.ReadHeader(h))
+	t.Log(h)
+	assert.NoError(t, codecR2.ReadHeader(h))
+	t.Log(h)
+
+	err = codecR1.ReadBody(body)
+	assert.Equal(t, "can not find go type name com.test.caseb in registry", err.Error())
+	attrs, err := codecR2.ReadAttachments()
+	assert.NoError(t, err)
+	assert.Equal(t, "2.6.4", attrs[DUBBO_VERSION_KEY])
+	assert.Equal(t, AttachTestObject{Id: 23, Name: "haha"}, *(attrs["att"].(*AttachTestObject)))
+	assert.NotEqual(t, AttachTestObject{Id: 24, Name: "nohaha"}, *(attrs["att"].(*AttachTestObject)))
+
+	t.Log(attrs)
+}
+
+type AttachTestObject struct {
+	Id   int32
+	Name string `dubbo:name`
+}
+
+func (AttachTestObject) JavaClassName() string {
+	return "com.test.Test"
+}
diff --git a/protocol/dubbo/hessian2/hessian_request.go b/protocol/dubbo/hessian2/hessian_request.go
new file mode 100644
index 0000000000000000000000000000000000000000..4ebb4aa1be05d4d1941661fed452dda06cf55fa0
--- /dev/null
+++ b/protocol/dubbo/hessian2/hessian_request.go
@@ -0,0 +1,350 @@
+/*
+ * 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 hessian2
+
+import (
+	"encoding/binary"
+	"reflect"
+	"strconv"
+	"strings"
+	"time"
+)
+
+import (
+	hessian "github.com/apache/dubbo-go-hessian2"
+	perrors "github.com/pkg/errors"
+)
+
+/////////////////////////////////////////
+// dubbo
+/////////////////////////////////////////
+
+func getArgType(v interface{}) string {
+	if v == nil {
+		return "V"
+	}
+
+	switch v.(type) {
+	// Serialized tags for base types
+	case nil:
+		return "V"
+	case bool:
+		return "Z"
+	case []bool:
+		return "[Z"
+	case byte:
+		return "B"
+	case []byte:
+		return "[B"
+	case int8:
+		return "B"
+	case []int8:
+		return "[B"
+	case int16:
+		return "S"
+	case []int16:
+		return "[S"
+	case uint16: // Equivalent to Char of Java
+		return "C"
+	case []uint16:
+		return "[C"
+	// case rune:
+	//	return "C"
+	case int:
+		return "J"
+	case []int:
+		return "[J"
+	case int32:
+		return "I"
+	case []int32:
+		return "[I"
+	case int64:
+		return "J"
+	case []int64:
+		return "[J"
+	case time.Time:
+		return "java.util.Date"
+	case []time.Time:
+		return "[Ljava.util.Date"
+	case float32:
+		return "F"
+	case []float32:
+		return "[F"
+	case float64:
+		return "D"
+	case []float64:
+		return "[D"
+	case string:
+		return "java.lang.String"
+	case []string:
+		return "[Ljava.lang.String;"
+	case []hessian.Object:
+		return "[Ljava.lang.Object;"
+	case map[interface{}]interface{}:
+		// return  "java.util.HashMap"
+		return "java.util.Map"
+	case hessian.POJOEnum:
+		return v.(hessian.POJOEnum).JavaClassName()
+	//  Serialized tags for complex types
+	default:
+		t := reflect.TypeOf(v)
+		if reflect.Ptr == t.Kind() {
+			t = reflect.TypeOf(reflect.ValueOf(v).Elem())
+		}
+		switch t.Kind() {
+		case reflect.Struct:
+			return "java.lang.Object"
+		case reflect.Slice, reflect.Array:
+			if t.Elem().Kind() == reflect.Struct {
+				return "[Ljava.lang.Object;"
+			}
+			// return "java.util.ArrayList"
+			return "java.util.List"
+		case reflect.Map: // Enter here, map may be map[string]int
+			return "java.util.Map"
+		default:
+			return ""
+		}
+	}
+
+	// unreachable
+	// return "java.lang.RuntimeException"
+}
+
+func getArgsTypeList(args []interface{}) (string, error) {
+	var (
+		typ   string
+		types string
+	)
+
+	for i := range args {
+		typ = getArgType(args[i])
+		if typ == "" {
+			return types, perrors.Errorf("cat not get arg %#v type", args[i])
+		}
+		if !strings.Contains(typ, ".") {
+			types += typ
+		} else if strings.Index(typ, "[") == 0 {
+			types += strings.Replace(typ, ".", "/", -1)
+		} else {
+			// java.util.List -> Ljava/util/List;
+			types += "L" + strings.Replace(typ, ".", "/", -1) + ";"
+		}
+	}
+
+	return types, nil
+}
+
+type DubboRequest struct {
+	Params      interface{}
+	Attachments map[string]interface{}
+}
+
+// NewRequest create a new DubboRequest
+func NewRequest(params interface{}, atta map[string]interface{}) *DubboRequest {
+	if atta == nil {
+		atta = make(map[string]interface{})
+	}
+	return &DubboRequest{
+		Params:      params,
+		Attachments: atta,
+	}
+}
+
+func EnsureRequest(body interface{}) *DubboRequest {
+	if req, ok := body.(*DubboRequest); ok {
+		return req
+	}
+	return NewRequest(body, nil)
+}
+
+func packRequest(service Service, header DubboHeader, req interface{}) ([]byte, error) {
+	var (
+		err       error
+		types     string
+		byteArray []byte
+		pkgLen    int
+	)
+
+	request := EnsureRequest(req)
+
+	args, ok := request.Params.([]interface{})
+	if !ok {
+		return nil, perrors.Errorf("@params is not of type: []interface{}")
+	}
+
+	hb := header.Type == PackageHeartbeat
+
+	//////////////////////////////////////////
+	// byteArray
+	//////////////////////////////////////////
+	// magic
+	switch header.Type {
+	case PackageHeartbeat:
+		byteArray = append(byteArray, DubboRequestHeartbeatHeader[:]...)
+	case PackageRequest_TwoWay:
+		byteArray = append(byteArray, DubboRequestHeaderBytesTwoWay[:]...)
+	default:
+		byteArray = append(byteArray, DubboRequestHeaderBytes[:]...)
+	}
+
+	// serialization id, two way flag, event, request/response flag
+	// SerialID is id of serialization approach in java dubbo
+	byteArray[2] |= header.SerialID & SERIAL_MASK
+	// request id
+	binary.BigEndian.PutUint64(byteArray[4:], uint64(header.ID))
+
+	encoder := hessian.NewEncoder()
+	encoder.Append(byteArray[:HEADER_LENGTH])
+
+	//////////////////////////////////////////
+	// body
+	//////////////////////////////////////////
+	if hb {
+		encoder.Encode(nil)
+		goto END
+	}
+
+	// dubbo version + path + version + method
+	encoder.Encode(DEFAULT_DUBBO_PROTOCOL_VERSION)
+	encoder.Encode(service.Path)
+	encoder.Encode(service.Version)
+	encoder.Encode(service.Method)
+
+	// args = args type list + args value list
+	if types, err = getArgsTypeList(args); err != nil {
+		return nil, perrors.Wrapf(err, " PackRequest(args:%+v)", args)
+	}
+	encoder.Encode(types)
+	for _, v := range args {
+		encoder.Encode(v)
+	}
+
+	request.Attachments[PATH_KEY] = service.Path
+	request.Attachments[VERSION_KEY] = service.Version
+	if len(service.Group) > 0 {
+		request.Attachments[GROUP_KEY] = service.Group
+	}
+	if len(service.Interface) > 0 {
+		request.Attachments[INTERFACE_KEY] = service.Interface
+	}
+	if service.Timeout != 0 {
+		request.Attachments[TIMEOUT_KEY] = strconv.Itoa(int(service.Timeout / time.Millisecond))
+	}
+
+	encoder.Encode(request.Attachments)
+
+END:
+	byteArray = encoder.Buffer()
+	pkgLen = len(byteArray)
+	if pkgLen > int(DEFAULT_LEN) { // 8M
+		return nil, perrors.Errorf("Data length %d too large, max payload %d", pkgLen, DEFAULT_LEN)
+	}
+	// byteArray{body length}
+	binary.BigEndian.PutUint32(byteArray[12:], uint32(pkgLen-HEADER_LENGTH))
+	return byteArray, nil
+}
+
+// hessian decode request body
+func unpackRequestBody(decoder *hessian.Decoder, reqObj interface{}) error {
+
+	if decoder == nil {
+		return perrors.Errorf("@decoder is nil")
+	}
+
+	req, ok := reqObj.([]interface{})
+	if !ok {
+		return perrors.Errorf("@reqObj is not of type: []interface{}")
+	}
+	if len(req) < 7 {
+		return perrors.New("length of @reqObj should  be 7")
+	}
+
+	var (
+		err                                                     error
+		dubboVersion, target, serviceVersion, method, argsTypes interface{}
+		args                                                    []interface{}
+	)
+
+	dubboVersion, err = decoder.Decode()
+	if err != nil {
+		return perrors.WithStack(err)
+	}
+	req[0] = dubboVersion
+
+	target, err = decoder.Decode()
+	if err != nil {
+		return perrors.WithStack(err)
+	}
+	req[1] = target
+
+	serviceVersion, err = decoder.Decode()
+	if err != nil {
+		return perrors.WithStack(err)
+	}
+	req[2] = serviceVersion
+
+	method, err = decoder.Decode()
+	if err != nil {
+		return perrors.WithStack(err)
+	}
+	req[3] = method
+
+	argsTypes, err = decoder.Decode()
+	if err != nil {
+		return perrors.WithStack(err)
+	}
+	req[4] = argsTypes
+
+	ats := DescRegex.FindAllString(argsTypes.(string), -1)
+	var arg interface{}
+	for i := 0; i < len(ats); i++ {
+		arg, err = decoder.Decode()
+		if err != nil {
+			return perrors.WithStack(err)
+		}
+		args = append(args, arg)
+	}
+	req[5] = args
+
+	attachments, err := decoder.Decode()
+	if err != nil {
+		return perrors.WithStack(err)
+	}
+	if v, ok := attachments.(map[interface{}]interface{}); ok {
+		v[DUBBO_VERSION_KEY] = dubboVersion
+		req[6] = ToMapStringInterface(v)
+		return nil
+	}
+
+	return perrors.Errorf("get wrong attachments: %+v", attachments)
+}
+
+func ToMapStringInterface(origin map[interface{}]interface{}) map[string]interface{} {
+	dest := make(map[string]interface{}, len(origin))
+	for k, v := range origin {
+		if kv, ok := k.(string); ok {
+			if v == nil {
+				dest[kv] = ""
+				continue
+			}
+			dest[kv] = v
+		}
+	}
+	return dest
+}
diff --git a/protocol/dubbo/hessian2/hessian_request_test.go b/protocol/dubbo/hessian2/hessian_request_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..98d5f2399c9fdbf99a7274e0c3e53c2c175f774f
--- /dev/null
+++ b/protocol/dubbo/hessian2/hessian_request_test.go
@@ -0,0 +1,158 @@
+/*
+ * 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 hessian2
+
+import (
+	"reflect"
+	"strconv"
+	"testing"
+	"time"
+)
+
+import (
+	hessian "github.com/apache/dubbo-go-hessian2"
+	"github.com/stretchr/testify/assert"
+)
+
+type TestEnumGender hessian.JavaEnum
+
+const (
+	MAN hessian.JavaEnum = iota
+	WOMAN
+)
+
+var genderName = map[hessian.JavaEnum]string{
+	MAN:   "MAN",
+	WOMAN: "WOMAN",
+}
+
+var genderValue = map[string]hessian.JavaEnum{
+	"MAN":   MAN,
+	"WOMAN": WOMAN,
+}
+
+func (g TestEnumGender) JavaClassName() string {
+	return "com.ikurento.test.TestEnumGender"
+}
+
+func (g TestEnumGender) String() string {
+	s, ok := genderName[hessian.JavaEnum(g)]
+	if ok {
+		return s
+	}
+
+	return strconv.Itoa(int(g))
+}
+
+func (g TestEnumGender) EnumValue(s string) hessian.JavaEnum {
+	v, ok := genderValue[s]
+	if ok {
+		return v
+	}
+
+	return hessian.InvalidJavaEnum
+}
+
+func TestPackRequest(t *testing.T) {
+	bytes, err := packRequest(Service{
+		Path:      "test",
+		Interface: "ITest",
+		Version:   "v1.0",
+		Method:    "test",
+		Timeout:   time.Second * 10,
+	}, DubboHeader{
+		SerialID: 0,
+		Type:     PackageRequest,
+		ID:       123,
+	}, []interface{}{1, 2})
+
+	assert.Nil(t, err)
+
+	if bytes != nil {
+		t.Logf("pack request: %s", string(bytes))
+	}
+}
+
+func TestGetArgsTypeList(t *testing.T) {
+	type Test struct{}
+	str, err := getArgsTypeList([]interface{}{nil, 1, []int{2}, true, []bool{false}, "a", []string{"b"}, Test{}, &Test{}, []Test{}, map[string]Test{}, TestEnumGender(MAN)})
+	assert.NoError(t, err)
+	assert.Equal(t, "VJ[JZ[ZLjava/lang/String;[Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;[Ljava/lang/Object;Ljava/util/Map;Lcom/ikurento/test/TestEnumGender;", str)
+}
+
+func TestDescRegex(t *testing.T) {
+	results := DescRegex.FindAllString("Ljava/lang/String;", -1)
+	assert.Equal(t, 1, len(results))
+	assert.Equal(t, "Ljava/lang/String;", results[0])
+
+	results = DescRegex.FindAllString("Ljava/lang/String;I", -1)
+	assert.Equal(t, 2, len(results))
+	assert.Equal(t, "Ljava/lang/String;", results[0])
+	assert.Equal(t, "I", results[1])
+
+	results = DescRegex.FindAllString("ILjava/lang/String;", -1)
+	assert.Equal(t, 2, len(results))
+	assert.Equal(t, "I", results[0])
+	assert.Equal(t, "Ljava/lang/String;", results[1])
+
+	results = DescRegex.FindAllString("ILjava/lang/String;IZ", -1)
+	assert.Equal(t, 4, len(results))
+	assert.Equal(t, "I", results[0])
+	assert.Equal(t, "Ljava/lang/String;", results[1])
+	assert.Equal(t, "I", results[2])
+	assert.Equal(t, "Z", results[3])
+
+	results = DescRegex.FindAllString("[Ljava/lang/String;[I", -1)
+	assert.Equal(t, 2, len(results))
+	assert.Equal(t, "[Ljava/lang/String;", results[0])
+	assert.Equal(t, "[I", results[1])
+}
+
+func TestIssue192(t *testing.T) {
+	type args struct {
+		origin map[interface{}]interface{}
+	}
+	tests := []struct {
+		name string
+		args args
+		want map[string]interface{}
+	}{
+		{
+			name: "not null",
+			args: args{
+				origin: map[interface{}]interface{}{
+					"1": nil,
+					"2": "3",
+					"":  "",
+				},
+			},
+			want: map[string]interface{}{
+				"1": "",
+				"2": "3",
+				"":  "",
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if got := ToMapStringInterface(tt.args.origin); !reflect.DeepEqual(got, tt.want) {
+				t.Errorf("ToMapStringString() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
diff --git a/protocol/dubbo/hessian2/hessian_response.go b/protocol/dubbo/hessian2/hessian_response.go
new file mode 100644
index 0000000000000000000000000000000000000000..982960ed87e74b325687ac364c97a347efe6c38f
--- /dev/null
+++ b/protocol/dubbo/hessian2/hessian_response.go
@@ -0,0 +1,377 @@
+/*
+ * 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 hessian2
+
+import (
+	"encoding/binary"
+	"math"
+	"reflect"
+	"strconv"
+	"strings"
+)
+
+import (
+	hessian "github.com/apache/dubbo-go-hessian2"
+	"github.com/apache/dubbo-go-hessian2/java_exception"
+	perrors "github.com/pkg/errors"
+)
+
+// DubboResponse dubbo response
+type DubboResponse struct {
+	RspObj      interface{}
+	Exception   error
+	Attachments map[string]interface{}
+}
+
+// NewResponse create a new DubboResponse
+func NewResponse(rspObj interface{}, exception error, attachments map[string]interface{}) *DubboResponse {
+	if attachments == nil {
+		attachments = make(map[string]interface{}, 8)
+	}
+	return &DubboResponse{
+		RspObj:      rspObj,
+		Exception:   exception,
+		Attachments: attachments,
+	}
+}
+
+// EnsureResponse check body type, make sure it's a DubboResponse or package it as a DubboResponse
+func EnsureResponse(body interface{}) *DubboResponse {
+	if res, ok := body.(*DubboResponse); ok {
+		return res
+	}
+	if exp, ok := body.(error); ok {
+		return NewResponse(nil, exp, nil)
+	}
+	return NewResponse(body, nil, nil)
+}
+
+// https://github.com/apache/dubbo/blob/dubbo-2.7.1/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/codec/ExchangeCodec.java#L256
+// hessian encode response
+func packResponse(header DubboHeader, ret interface{}) ([]byte, error) {
+	var (
+		byteArray []byte
+	)
+
+	response := EnsureResponse(ret)
+
+	hb := header.Type == PackageHeartbeat
+
+	// magic
+	if hb {
+		byteArray = append(byteArray, DubboResponseHeartbeatHeader[:]...)
+	} else {
+		byteArray = append(byteArray, DubboResponseHeaderBytes[:]...)
+	}
+	// set serialID, identify serialization types, eg: fastjson->6, hessian2->2
+	byteArray[2] |= header.SerialID & SERIAL_MASK
+	// response status
+	if header.ResponseStatus != 0 {
+		byteArray[3] = header.ResponseStatus
+	}
+
+	// request id
+	binary.BigEndian.PutUint64(byteArray[4:], uint64(header.ID))
+
+	// body
+	encoder := hessian.NewEncoder()
+	encoder.Append(byteArray[:HEADER_LENGTH])
+
+	if header.ResponseStatus == Response_OK {
+		if hb {
+			encoder.Encode(nil)
+		} else {
+			atta := isSupportResponseAttachment(response.Attachments[DUBBO_VERSION_KEY])
+
+			var resWithException, resValue, resNullValue int32
+			if atta {
+				resWithException = RESPONSE_WITH_EXCEPTION_WITH_ATTACHMENTS
+				resValue = RESPONSE_VALUE_WITH_ATTACHMENTS
+				resNullValue = RESPONSE_NULL_VALUE_WITH_ATTACHMENTS
+			} else {
+				resWithException = RESPONSE_WITH_EXCEPTION
+				resValue = RESPONSE_VALUE
+				resNullValue = RESPONSE_NULL_VALUE
+			}
+
+			if response.Exception != nil { // throw error
+				encoder.Encode(resWithException)
+				if t, ok := response.Exception.(java_exception.Throwabler); ok {
+					encoder.Encode(t)
+				} else {
+					encoder.Encode(java_exception.NewThrowable(response.Exception.Error()))
+				}
+			} else {
+				if response.RspObj == nil {
+					encoder.Encode(resNullValue)
+				} else {
+					encoder.Encode(resValue)
+					encoder.Encode(response.RspObj) // result
+				}
+			}
+
+			if atta {
+				encoder.Encode(response.Attachments) // attachments
+			}
+		}
+	} else {
+		if response.Exception != nil { // throw error
+			encoder.Encode(response.Exception.Error())
+		} else {
+			encoder.Encode(response.RspObj)
+		}
+	}
+
+	byteArray = encoder.Buffer()
+	byteArray = hessian.EncNull(byteArray) // if not, "java client" will throw exception  "unexpected end of file"
+	pkgLen := len(byteArray)
+	if pkgLen > int(DEFAULT_LEN) { // 8M
+		return nil, perrors.Errorf("Data length %d too large, max payload %d", pkgLen, DEFAULT_LEN)
+	}
+	// byteArray{body length}
+	binary.BigEndian.PutUint32(byteArray[12:], uint32(pkgLen-HEADER_LENGTH))
+	return byteArray, nil
+
+}
+
+// hessian decode response body
+func unpackResponseBody(decoder *hessian.Decoder, resp interface{}) error {
+	// body
+	if decoder == nil {
+		return perrors.Errorf("@decoder is nil")
+	}
+	rspType, err := decoder.Decode()
+	if err != nil {
+		return perrors.WithStack(err)
+	}
+
+	response := EnsureResponse(resp)
+
+	switch rspType {
+	case RESPONSE_WITH_EXCEPTION, RESPONSE_WITH_EXCEPTION_WITH_ATTACHMENTS:
+		expt, err := decoder.Decode()
+		if err != nil {
+			return perrors.WithStack(err)
+		}
+		if rspType == RESPONSE_WITH_EXCEPTION_WITH_ATTACHMENTS {
+			attachments, err := decoder.Decode()
+			if err != nil {
+				return perrors.WithStack(err)
+			}
+			if v, ok := attachments.(map[interface{}]interface{}); ok {
+				atta := ToMapStringInterface(v)
+				response.Attachments = atta
+			} else {
+				return perrors.Errorf("get wrong attachments: %+v", attachments)
+			}
+		}
+
+		if e, ok := expt.(error); ok {
+			response.Exception = e
+		} else {
+			response.Exception = perrors.Errorf("got exception: %+v", expt)
+		}
+		return nil
+
+	case RESPONSE_VALUE, RESPONSE_VALUE_WITH_ATTACHMENTS:
+		rsp, err := decoder.Decode()
+		if err != nil {
+			return perrors.WithStack(err)
+		}
+		if rspType == RESPONSE_VALUE_WITH_ATTACHMENTS {
+			attachments, err := decoder.Decode()
+			if err != nil {
+				return perrors.WithStack(err)
+			}
+			if v, ok := attachments.(map[interface{}]interface{}); ok {
+				response.Attachments = ToMapStringInterface(v)
+			} else {
+				return perrors.Errorf("get wrong attachments: %+v", attachments)
+			}
+		}
+
+		// If the return value is nil,
+		// we should consider it normal
+		if rsp == nil {
+			return nil
+		}
+
+		return perrors.WithStack(ReflectResponse(rsp, response.RspObj))
+
+	case RESPONSE_NULL_VALUE, RESPONSE_NULL_VALUE_WITH_ATTACHMENTS:
+		if rspType == RESPONSE_NULL_VALUE_WITH_ATTACHMENTS {
+			attachments, err := decoder.Decode()
+			if err != nil {
+				return perrors.WithStack(err)
+			}
+			if v, ok := attachments.(map[interface{}]interface{}); ok {
+				atta := ToMapStringInterface(v)
+				response.Attachments = atta
+			} else {
+				return perrors.Errorf("get wrong attachments: %+v", attachments)
+			}
+		}
+		return nil
+	}
+
+	return nil
+}
+
+// CopySlice copy from inSlice to outSlice
+func CopySlice(inSlice, outSlice reflect.Value) error {
+	if inSlice.IsNil() {
+		return perrors.New("@in is nil")
+	}
+	if inSlice.Kind() != reflect.Slice {
+		return perrors.Errorf("@in is not slice, but %v", inSlice.Kind())
+	}
+
+	for outSlice.Kind() == reflect.Ptr {
+		outSlice = outSlice.Elem()
+	}
+
+	size := inSlice.Len()
+	outSlice.Set(reflect.MakeSlice(outSlice.Type(), size, size))
+
+	for i := 0; i < size; i++ {
+		inSliceValue := inSlice.Index(i)
+		if !inSliceValue.Type().AssignableTo(outSlice.Index(i).Type()) {
+			return perrors.Errorf("in element type [%s] can not assign to out element type [%s]",
+				inSliceValue.Type().String(), outSlice.Type().String())
+		}
+		outSlice.Index(i).Set(inSliceValue)
+	}
+
+	return nil
+}
+
+// CopyMap copy from in map to out map
+func CopyMap(inMapValue, outMapValue reflect.Value) error {
+	if inMapValue.IsNil() {
+		return perrors.New("@in is nil")
+	}
+	if !inMapValue.CanInterface() {
+		return perrors.New("@in's Interface can not be used.")
+	}
+	if inMapValue.Kind() != reflect.Map {
+		return perrors.Errorf("@in is not map, but %v", inMapValue.Kind())
+	}
+
+	outMapType := hessian.UnpackPtrType(outMapValue.Type())
+	hessian.SetValue(outMapValue, reflect.MakeMap(outMapType))
+
+	outKeyType := outMapType.Key()
+
+	outMapValue = hessian.UnpackPtrValue(outMapValue)
+	outValueType := outMapValue.Type().Elem()
+
+	for _, inKey := range inMapValue.MapKeys() {
+		inValue := inMapValue.MapIndex(inKey)
+
+		if !inKey.Type().AssignableTo(outKeyType) {
+			return perrors.Errorf("in Key:{type:%s, value:%#v} can not assign to out Key:{type:%s} ",
+				inKey.Type().String(), inKey, outKeyType.String())
+		}
+		if !inValue.Type().AssignableTo(outValueType) {
+			return perrors.Errorf("in Value:{type:%s, value:%#v} can not assign to out value:{type:%s}",
+				inValue.Type().String(), inValue, outValueType.String())
+		}
+		outMapValue.SetMapIndex(inKey, inValue)
+	}
+
+	return nil
+}
+
+// ReflectResponse reflect return value
+// TODO response object should not be copied again to another object, it should be the exact type of the object
+func ReflectResponse(in interface{}, out interface{}) error {
+	if in == nil {
+		return perrors.Errorf("@in is nil")
+	}
+
+	if out == nil {
+		return perrors.Errorf("@out is nil")
+	}
+	if reflect.TypeOf(out).Kind() != reflect.Ptr {
+		return perrors.Errorf("@out should be a pointer")
+	}
+
+	inValue := hessian.EnsurePackValue(in)
+	outValue := hessian.EnsurePackValue(out)
+
+	outType := outValue.Type().String()
+	if outType == "interface {}" || outType == "*interface {}" {
+		hessian.SetValue(outValue, inValue)
+		return nil
+	}
+
+	switch inValue.Type().Kind() {
+	case reflect.Slice, reflect.Array:
+		return CopySlice(inValue, outValue)
+	case reflect.Map:
+		return CopyMap(inValue, outValue)
+	default:
+		hessian.SetValue(outValue, inValue)
+	}
+
+	return nil
+}
+
+var versionInt = make(map[string]int)
+
+// https://github.com/apache/dubbo/blob/dubbo-2.7.1/dubbo-common/src/main/java/org/apache/dubbo/common/Version.java#L96
+// isSupportResponseAttachment is for compatibility among some dubbo version
+func isSupportResponseAttachment(ver interface{}) bool {
+	version, ok := ver.(string)
+	if !ok || len(version) == 0 {
+		return false
+	}
+
+	v, ok := versionInt[version]
+	if !ok {
+		v = version2Int(version)
+		if v == -1 {
+			return false
+		}
+	}
+
+	if v >= 2001000 && v <= 2060200 { // 2.0.10 ~ 2.6.2
+		return false
+	}
+	return v >= LOWEST_VERSION_FOR_RESPONSE_ATTACHMENT
+}
+
+func version2Int(ver interface{}) int {
+	version, ok := ver.(string)
+	if !ok || len(version) == 0 {
+		return 0
+	}
+	var v = 0
+	varr := strings.Split(version, ".")
+	length := len(varr)
+	for key, value := range varr {
+		v0, err := strconv.Atoi(value)
+		if err != nil {
+			return -1
+		}
+		v += v0 * int(math.Pow10((length-key-1)*2))
+	}
+	if length == 3 {
+		return v * 100
+	}
+	return v
+}
diff --git a/protocol/dubbo/hessian2/hessian_response_test.go b/protocol/dubbo/hessian2/hessian_response_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..f5c84baa90b3b31b271979cb1107503facac71d9
--- /dev/null
+++ b/protocol/dubbo/hessian2/hessian_response_test.go
@@ -0,0 +1,225 @@
+/*
+ * 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 hessian2
+
+import (
+	"reflect"
+	"testing"
+)
+
+import (
+	hessian "github.com/apache/dubbo-go-hessian2"
+	"github.com/stretchr/testify/assert"
+)
+
+func doTestReflectResponse(t *testing.T, in interface{}, out interface{}) {
+	err := ReflectResponse(in, out)
+	if err != nil {
+		t.Error(err)
+		t.FailNow()
+	}
+
+	result := hessian.UnpackPtrValue(reflect.ValueOf(out)).Interface()
+
+	equal := reflect.DeepEqual(in, result)
+	if !equal {
+		t.Errorf("expect [%v]: %v, but got [%v]: %v", reflect.TypeOf(in), in, reflect.TypeOf(result), result)
+	}
+}
+
+func TestReflectResponse(t *testing.T) {
+	var b bool
+	doTestReflectResponse(t, true, &b)
+	doTestReflectResponse(t, false, &b)
+
+	var i int
+	doTestReflectResponse(t, 123, &i)
+	doTestReflectResponse(t, 234, &i)
+
+	var i16 int16
+	doTestReflectResponse(t, int16(456), &i16)
+
+	var i64 int64
+	doTestReflectResponse(t, int64(789), &i64)
+
+	var s string
+	doTestReflectResponse(t, "hello world", &s)
+
+	type rr struct {
+		Name string
+		Num  int
+	}
+
+	var r1 rr
+	doTestReflectResponse(t, rr{"dubbogo", 32}, &r1)
+
+	// ------ map test -------
+	m1 := make(map[interface{}]interface{})
+	var m1r map[interface{}]interface{}
+	m1["hello"] = "world"
+	m1[1] = "go"
+	m1["dubbo"] = 666
+	doTestReflectResponse(t, m1, &m1r)
+
+	m2 := make(map[string]string)
+	var m2r map[string]string
+	m2["hello"] = "world"
+	m2["dubbo"] = "666"
+	doTestReflectResponse(t, m2, &m2r)
+
+	m3 := make(map[string]rr)
+	var m3r map[string]rr
+	m3["dubbo"] = rr{"hello", 123}
+	m3["go"] = rr{"world", 456}
+	doTestReflectResponse(t, m3, &m3r)
+
+	// ------ slice test -------
+	s1 := []string{"abc", "def", "hello", "world"}
+	var s1r []string
+	doTestReflectResponse(t, s1, &s1r)
+
+	s2 := []rr{rr{"dubbo", 666}, rr{"go", 999}}
+	var s2r []rr
+	doTestReflectResponse(t, s2, &s2r)
+
+	s3 := []interface{}{rr{"dubbo", 666}, 123, "hello"}
+	var s3r []interface{}
+	doTestReflectResponse(t, s3, &s3r)
+
+	// ------ interface test -------
+	in1 := []interface{}{rr{"dubbo", 666}, 123, "hello"}
+	var inr1 *interface{}
+	doTestReflectResponse(t, in1, reflect.New(reflect.TypeOf(inr1).Elem()).Interface())
+
+	in2 := make(map[string]rr)
+	var inr2 map[string]rr
+	m3["dubbo"] = rr{"hello", 123}
+	m3["go"] = rr{"world", 456}
+	doTestReflectResponse(t, in2, &inr2)
+}
+
+// separately test copy normal map to map[interface{}]interface{}
+func TestCopyMap(t *testing.T) {
+	type rr struct {
+		Name string
+		Num  int
+	}
+
+	m3 := make(map[string]rr)
+	var m3r map[interface{}]interface{}
+	r1 := rr{"hello", 123}
+	r2 := rr{"world", 456}
+	m3["dubbo"] = r1
+	m3["go"] = r2
+
+	err := ReflectResponse(m3, &m3r)
+	if err != nil {
+		t.Error(err)
+		t.FailNow()
+	}
+
+	assert.Equal(t, 2, len(m3r))
+
+	rr1, ok := m3r["dubbo"]
+	assert.True(t, ok)
+	assert.True(t, reflect.DeepEqual(r1, rr1))
+
+	rr2, ok := m3r["go"]
+	assert.True(t, ok)
+	assert.True(t, reflect.DeepEqual(r2, rr2))
+}
+
+// separately test copy normal slice to []interface{}
+func TestCopySlice(t *testing.T) {
+	type rr struct {
+		Name string
+		Num  int
+	}
+
+	r1 := rr{"hello", 123}
+	r2 := rr{"world", 456}
+
+	s1 := []rr{r1, r2}
+	var s1r []interface{}
+
+	err := ReflectResponse(s1, &s1r)
+	if err != nil {
+		t.Error(err)
+		t.FailNow()
+	}
+
+	assert.Equal(t, 2, len(s1r))
+	assert.True(t, reflect.DeepEqual(r1, s1r[0]))
+	assert.True(t, reflect.DeepEqual(r2, s1r[1]))
+}
+
+func TestIsSupportResponseAttachment(t *testing.T) {
+	is := isSupportResponseAttachment("2.X")
+	assert.False(t, is)
+
+	is = isSupportResponseAttachment("2.0.10")
+	assert.False(t, is)
+
+	is = isSupportResponseAttachment("2.5.3")
+	assert.False(t, is)
+
+	is = isSupportResponseAttachment("2.6.2")
+	assert.False(t, is)
+
+	is = isSupportResponseAttachment("1.5.5")
+	assert.False(t, is)
+
+	is = isSupportResponseAttachment("0.0.0")
+	assert.False(t, is)
+
+	is = isSupportResponseAttachment("2.0.2")
+	assert.True(t, is)
+
+	is = isSupportResponseAttachment("2.7.2")
+	assert.True(t, is)
+}
+
+func TestVersion2Int(t *testing.T) {
+	v := version2Int("2.1.3")
+	assert.Equal(t, 2010300, v)
+
+	v = version2Int("22.11.33")
+	assert.Equal(t, 22113300, v)
+
+	v = version2Int("222.111.333")
+	assert.Equal(t, 223143300, v)
+
+	v = version2Int("220.110.333")
+	assert.Equal(t, 221133300, v)
+
+	v = version2Int("229.119.333")
+	assert.Equal(t, 230223300, v)
+
+	v = version2Int("2222.1111.3333")
+	assert.Equal(t, 2233443300, v)
+
+	v = version2Int("2.11")
+	assert.Equal(t, 211, v)
+
+	v = version2Int("2.1.3.4")
+	assert.Equal(t, 2010304, v)
+
+	v = version2Int("2.1.3.4.5")
+	assert.Equal(t, 201030405, v)
+
+}
diff --git a/protocol/dubbo/listener.go b/protocol/dubbo/listener.go
index 4834459390f39912f0683dfe52f65faa72b7c26d..180fd176f9262871d9250a2d9402735ff1b3b731 100644
--- a/protocol/dubbo/listener.go
+++ b/protocol/dubbo/listener.go
@@ -27,9 +27,7 @@ import (
 )
 
 import (
-	"github.com/apache/dubbo-go-hessian2"
-	"github.com/dubbogo/getty"
-	"github.com/opentracing/opentracing-go"
+	"github.com/apache/dubbo-getty"
 	perrors "github.com/pkg/errors"
 )
 
@@ -38,6 +36,7 @@ import (
 	"github.com/apache/dubbo-go/common/constant"
 	"github.com/apache/dubbo-go/common/logger"
 	"github.com/apache/dubbo-go/protocol"
+	"github.com/apache/dubbo-go/protocol/dubbo/hessian2"
 	"github.com/apache/dubbo-go/protocol/invocation"
 )
 
@@ -105,8 +104,8 @@ func (h *RpcClientHandler) OnMessage(session getty.Session, pkg interface{}) {
 		return
 	}
 
-	if p.Header.Type&hessian.PackageHeartbeat != 0x00 {
-		if p.Header.Type&hessian.PackageResponse != 0x00 {
+	if p.Header.Type&hessian2.PackageHeartbeat != 0x00 {
+		if p.Header.Type&hessian2.PackageResponse != 0x00 {
 			logger.Debugf("get rpc heartbeat response{header: %#v, body: %#v}", p.Header, p.Body)
 			if p.Err != nil {
 				logger.Errorf("rpc heartbeat response{error: %#v}", p.Err)
@@ -114,8 +113,8 @@ func (h *RpcClientHandler) OnMessage(session getty.Session, pkg interface{}) {
 			h.conn.pool.rpcClient.removePendingResponse(SequenceType(p.Header.ID))
 		} else {
 			logger.Debugf("get rpc heartbeat request{header: %#v, service: %#v, body: %#v}", p.Header, p.Service, p.Body)
-			p.Header.ResponseStatus = hessian.Response_OK
-			reply(session, p, hessian.PackageHeartbeat)
+			p.Header.ResponseStatus = hessian2.Response_OK
+			reply(session, p, hessian2.PackageHeartbeat)
 		}
 		return
 	}
@@ -229,24 +228,24 @@ func (h *RpcServerHandler) OnMessage(session getty.Session, pkg interface{}) {
 		logger.Errorf("illegal package{%#v}", pkg)
 		return
 	}
-	p.Header.ResponseStatus = hessian.Response_OK
+	p.Header.ResponseStatus = hessian2.Response_OK
 
 	// heartbeat
-	if p.Header.Type&hessian.PackageHeartbeat != 0x00 {
+	if p.Header.Type&hessian2.PackageHeartbeat != 0x00 {
 		logger.Debugf("get rpc heartbeat request{header: %#v, service: %#v, body: %#v}", p.Header, p.Service, p.Body)
-		reply(session, p, hessian.PackageHeartbeat)
+		reply(session, p, hessian2.PackageHeartbeat)
 		return
 	}
 
 	twoway := true
 	// not twoway
-	if p.Header.Type&hessian.PackageRequest_TwoWay == 0x00 {
+	if p.Header.Type&hessian2.PackageRequest_TwoWay == 0x00 {
 		twoway = false
 	}
 
 	defer func() {
 		if e := recover(); e != nil {
-			p.Header.ResponseStatus = hessian.Response_SERVER_ERROR
+			p.Header.ResponseStatus = hessian2.Response_SERVER_ERROR
 			if err, ok := e.(error); ok {
 				logger.Errorf("OnMessage panic: %+v", perrors.WithStack(err))
 				p.Body = perrors.WithStack(err)
@@ -261,7 +260,7 @@ func (h *RpcServerHandler) OnMessage(session getty.Session, pkg interface{}) {
 			if !twoway {
 				return
 			}
-			reply(session, p, hessian.PackageResponse)
+			reply(session, p, hessian2.PackageResponse)
 		}
 
 	}()
@@ -274,14 +273,14 @@ func (h *RpcServerHandler) OnMessage(session getty.Session, pkg interface{}) {
 	if exporter == nil {
 		err := fmt.Errorf("don't have this exporter, key: %s", u.ServiceKey())
 		logger.Errorf(err.Error())
-		p.Header.ResponseStatus = hessian.Response_OK
+		p.Header.ResponseStatus = hessian2.Response_OK
 		p.Body = err
-		reply(session, p, hessian.PackageResponse)
+		reply(session, p, hessian2.PackageResponse)
 		return
 	}
 	invoker := exporter.(protocol.Exporter).GetInvoker()
 	if invoker != nil {
-		attachments := p.Body.(map[string]interface{})["attachments"].(map[string]string)
+		attachments := p.Body.(map[string]interface{})["attachments"].(map[string]interface{})
 		attachments[constant.LOCAL_ADDR] = session.LocalAddr()
 		attachments[constant.REMOTE_ADDR] = session.RemoteAddr()
 
@@ -292,19 +291,19 @@ func (h *RpcServerHandler) OnMessage(session getty.Session, pkg interface{}) {
 
 		result := invoker.Invoke(ctx, inv)
 		if err := result.Error(); err != nil {
-			p.Header.ResponseStatus = hessian.Response_OK
-			p.Body = hessian.NewResponse(nil, err, result.Attachments())
+			p.Header.ResponseStatus = hessian2.Response_OK
+			p.Body = hessian2.NewResponse(nil, err, result.Attachments())
 		} else {
 			res := result.Result()
-			p.Header.ResponseStatus = hessian.Response_OK
-			p.Body = hessian.NewResponse(res, nil, result.Attachments())
+			p.Header.ResponseStatus = hessian2.Response_OK
+			p.Body = hessian2.NewResponse(res, nil, result.Attachments())
 		}
 	}
 
 	if !twoway {
 		return
 	}
-	reply(session, p, hessian.PackageResponse)
+	reply(session, p, hessian2.PackageResponse)
 }
 
 // OnCron notified when RPC server session got any message in cron job
@@ -340,17 +339,16 @@ func rebuildCtx(inv *invocation.RPCInvocation) context.Context {
 	ctx := context.Background()
 
 	// actually, if user do not use any opentracing framework, the err will not be nil.
-	spanCtx, err := opentracing.GlobalTracer().Extract(opentracing.TextMap,
-		opentracing.TextMapCarrier(inv.Attachments()))
+	spanCtx, err := extractTraceCtx(inv)
 	if err == nil {
 		ctx = context.WithValue(ctx, constant.TRACING_REMOTE_SPAN_CTX, spanCtx)
 	}
 	return ctx
 }
 
-func reply(session getty.Session, req *DubboPackage, tp hessian.PackageType) {
+func reply(session getty.Session, req *DubboPackage, tp hessian2.PackageType) {
 	resp := &DubboPackage{
-		Header: hessian.DubboHeader{
+		Header: hessian2.DubboHeader{
 			SerialID:       req.Header.SerialID,
 			Type:           tp,
 			ID:             req.Header.ID,
@@ -358,7 +356,7 @@ func reply(session getty.Session, req *DubboPackage, tp hessian.PackageType) {
 		},
 	}
 
-	if req.Header.Type&hessian.PackageRequest != 0x00 {
+	if req.Header.Type&hessian2.PackageRequest != 0x00 {
 		resp.Body = req.Body
 	} else {
 		resp.Body = nil
diff --git a/protocol/dubbo/listener_test.go b/protocol/dubbo/listener_test.go
index 5f809814607558650e09934019db96dbb2ceeeae..5ab73fd465c9eed508969387513df913433a3c4c 100644
--- a/protocol/dubbo/listener_test.go
+++ b/protocol/dubbo/listener_test.go
@@ -35,7 +35,7 @@ import (
 // test rebuild the ctx
 func TestRebuildCtx(t *testing.T) {
 	opentracing.SetGlobalTracer(mocktracer.New())
-	attach := make(map[string]string, 10)
+	attach := make(map[string]interface{}, 10)
 	attach[constant.VERSION_KEY] = "1.0"
 	attach[constant.GROUP_KEY] = "MyGroup"
 	inv := invocation.NewRPCInvocation("MethodName", []interface{}{"OK", "Hello"}, attach)
@@ -47,8 +47,7 @@ func TestRebuildCtx(t *testing.T) {
 
 	span, ctx := opentracing.StartSpanFromContext(ctx, "Test-Client")
 
-	opentracing.GlobalTracer().Inject(span.Context(), opentracing.TextMap,
-		opentracing.TextMapCarrier(inv.Attachments()))
+	injectTraceCtx(span, inv)
 	// rebuild the context success
 	inv = invocation.NewRPCInvocation("MethodName", []interface{}{"OK", "Hello"}, attach)
 	ctx = rebuildCtx(inv)
diff --git a/protocol/dubbo/opentracing.go b/protocol/dubbo/opentracing.go
new file mode 100644
index 0000000000000000000000000000000000000000..2dcd6a4d0d9f491ba6d51ea7a3ba96812a6f9e08
--- /dev/null
+++ b/protocol/dubbo/opentracing.go
@@ -0,0 +1,60 @@
+/*
+ * 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 dubbo
+
+import (
+	"github.com/opentracing/opentracing-go"
+)
+import (
+	invocation_impl "github.com/apache/dubbo-go/protocol/invocation"
+)
+
+func injectTraceCtx(currentSpan opentracing.Span, inv *invocation_impl.RPCInvocation) error {
+	// inject opentracing ctx
+	traceAttachments := filterContext(inv.Attachments())
+	carrier := opentracing.TextMapCarrier(traceAttachments)
+	err := opentracing.GlobalTracer().Inject(currentSpan.Context(), opentracing.TextMap, carrier)
+	if err == nil {
+		fillTraceAttachments(inv.Attachments(), traceAttachments)
+	}
+	return err
+}
+
+func extractTraceCtx(inv *invocation_impl.RPCInvocation) (opentracing.SpanContext, error) {
+	traceAttachments := filterContext(inv.Attachments())
+	// actually, if user do not use any opentracing framework, the err will not be nil.
+	spanCtx, err := opentracing.GlobalTracer().Extract(opentracing.TextMap,
+		opentracing.TextMapCarrier(traceAttachments))
+	return spanCtx, err
+}
+
+func filterContext(attachments map[string]interface{}) map[string]string {
+	var traceAttchment = make(map[string]string)
+	for k, v := range attachments {
+		if r, ok := v.(string); ok {
+			traceAttchment[k] = r
+		}
+	}
+	return traceAttchment
+}
+
+func fillTraceAttachments(attachments map[string]interface{}, traceAttachment map[string]string) {
+	for k, v := range traceAttachment {
+		attachments[k] = v
+	}
+}
diff --git a/protocol/dubbo/pool.go b/protocol/dubbo/pool.go
index c9f5e34fadf61fb36e92356f1b1d40fbc67e4c99..6a7d211b496d3f6905c8a84e2f1bd648718ebb92 100644
--- a/protocol/dubbo/pool.go
+++ b/protocol/dubbo/pool.go
@@ -18,6 +18,7 @@
 package dubbo
 
 import (
+	"crypto/tls"
 	"fmt"
 	"math/rand"
 	"net"
@@ -27,12 +28,13 @@ import (
 )
 
 import (
-	"github.com/dubbogo/getty"
+	"github.com/apache/dubbo-getty"
 	perrors "github.com/pkg/errors"
 )
 
 import (
 	"github.com/apache/dubbo-go/common/logger"
+	"github.com/apache/dubbo-go/config"
 )
 
 type gettyRPCClient struct {
@@ -53,15 +55,31 @@ var (
 )
 
 func newGettyRPCClientConn(pool *gettyRPCClientPool, protocol, addr string) (*gettyRPCClient, error) {
-	c := &gettyRPCClient{
-		protocol: protocol,
-		addr:     addr,
-		pool:     pool,
-		gettyClient: getty.NewTCPClient(
+	var (
+		gettyClient getty.Client
+		sslEnabled  bool
+	)
+	sslEnabled = pool.sslEnabled
+	if sslEnabled {
+		gettyClient = getty.NewTCPClient(
+			getty.WithServerAddress(addr),
+			getty.WithConnectionNumber((int)(pool.rpcClient.conf.ConnectionNum)),
+			getty.WithReconnectInterval(pool.rpcClient.conf.ReconnectInterval),
+			getty.WithClientSslEnabled(pool.sslEnabled),
+			getty.WithClientTlsConfigBuilder(config.GetClientTlsConfigBuilder()),
+		)
+	} else {
+		gettyClient = getty.NewTCPClient(
 			getty.WithServerAddress(addr),
 			getty.WithConnectionNumber((int)(pool.rpcClient.conf.ConnectionNum)),
 			getty.WithReconnectInterval(pool.rpcClient.conf.ReconnectInterval),
-		),
+		)
+	}
+	c := &gettyRPCClient{
+		protocol:    protocol,
+		addr:        addr,
+		pool:        pool,
+		gettyClient: gettyClient,
 	}
 	go c.gettyClient.RunEventLoop(c.newSession)
 	idx := 1
@@ -94,16 +112,34 @@ func (c *gettyRPCClient) getActive() int64 {
 
 func (c *gettyRPCClient) newSession(session getty.Session) error {
 	var (
-		ok      bool
-		tcpConn *net.TCPConn
-		conf    ClientConfig
+		ok         bool
+		tcpConn    *net.TCPConn
+		conf       ClientConfig
+		sslEnabled bool
 	)
 
 	conf = c.pool.rpcClient.conf
+	sslEnabled = c.pool.sslEnabled
 	if conf.GettySessionParam.CompressEncoding {
 		session.SetCompressType(getty.CompressZip)
 	}
-
+	if sslEnabled {
+		if _, ok = session.Conn().(*tls.Conn); !ok {
+			panic(fmt.Sprintf("%s, session.conn{%#v} is not tls connection\n", session.Stat(), session.Conn()))
+		}
+		session.SetName(conf.GettySessionParam.SessionName)
+		session.SetMaxMsgLen(conf.GettySessionParam.MaxMsgLen)
+		session.SetPkgHandler(NewRpcClientPackageHandler(c.pool.rpcClient))
+		session.SetEventListener(NewRpcClientHandler(c))
+		session.SetWQLen(conf.GettySessionParam.PkgWQSize)
+		session.SetReadTimeout(conf.GettySessionParam.tcpReadTimeout)
+		session.SetWriteTimeout(conf.GettySessionParam.tcpWriteTimeout)
+		session.SetCronPeriod((int)(conf.heartbeatPeriod.Nanoseconds() / 1e6))
+		session.SetWaitTime(conf.GettySessionParam.waitTimeout)
+		logger.Debugf("client new session:%s\n", session.Stat())
+		session.SetTaskPool(clientGrpool)
+		return nil
+	}
 	if tcpConn, ok = session.Conn().(*net.TCPConn); !ok {
 		panic(fmt.Sprintf("%s, session.conn{%#v} is not tcp connection\n", session.Stat(), session.Conn()))
 	}
@@ -288,9 +324,10 @@ func (c *gettyRPCClient) close() error {
 }
 
 type gettyRPCClientPool struct {
-	rpcClient *Client
-	size      int   // size of []*gettyRPCClient
-	ttl       int64 // ttl of every gettyRPCClient, it is checked when getConn
+	rpcClient  *Client
+	size       int   // size of []*gettyRPCClient
+	ttl        int64 // ttl of every gettyRPCClient, it is checked when getConn
+	sslEnabled bool
 
 	sync.Mutex
 	conns []*gettyRPCClient
diff --git a/protocol/dubbo/readwriter.go b/protocol/dubbo/readwriter.go
index 9cc7ea25cdfa6152d632f278b33285d7d38f47c9..a7b37aae76c208a8facb87c390ec446e1a26b51a 100644
--- a/protocol/dubbo/readwriter.go
+++ b/protocol/dubbo/readwriter.go
@@ -23,8 +23,7 @@ import (
 )
 
 import (
-	"github.com/apache/dubbo-go-hessian2"
-	"github.com/dubbogo/getty"
+	"github.com/apache/dubbo-getty"
 	perrors "github.com/pkg/errors"
 )
 
@@ -32,6 +31,7 @@ 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/protocol/dubbo/hessian2"
 )
 
 ////////////////////////////////////////////
@@ -56,7 +56,7 @@ func (p *RpcClientPackageHandler) Read(ss getty.Session, data []byte) (interface
 	err := pkg.Unmarshal(buf, p.client)
 	if err != nil {
 		originErr := perrors.Cause(err)
-		if originErr == hessian.ErrHeaderNotEnough || originErr == hessian.ErrBodyNotEnough {
+		if originErr == hessian2.ErrHeaderNotEnough || originErr == hessian2.ErrBodyNotEnough {
 			return nil, 0, nil
 		}
 
@@ -65,12 +65,12 @@ func (p *RpcClientPackageHandler) Read(ss getty.Session, data []byte) (interface
 		return nil, 0, perrors.WithStack(err)
 	}
 
-	if pkg.Header.Type&hessian.PackageRequest == 0x00 {
-		pkg.Err = pkg.Body.(*hessian.Response).Exception
-		pkg.Body = NewResponse(pkg.Body.(*hessian.Response).RspObj, pkg.Body.(*hessian.Response).Attachments)
+	if pkg.Header.Type&hessian2.PackageRequest == 0x00 {
+		pkg.Err = pkg.Body.(*hessian2.DubboResponse).Exception
+		pkg.Body = NewResponse(pkg.Body.(*hessian2.DubboResponse).RspObj, pkg.Body.(*hessian2.DubboResponse).Attachments)
 	}
 
-	return pkg, hessian.HEADER_LENGTH + pkg.Header.BodyLen, nil
+	return pkg, hessian2.HEADER_LENGTH + pkg.Header.BodyLen, nil
 }
 
 // Write encode @pkg.
@@ -111,7 +111,7 @@ func (p *RpcServerPackageHandler) Read(ss getty.Session, data []byte) (interface
 	err := pkg.Unmarshal(buf)
 	if err != nil {
 		originErr := perrors.Cause(err)
-		if originErr == hessian.ErrHeaderNotEnough || originErr == hessian.ErrBodyNotEnough {
+		if originErr == hessian2.ErrHeaderNotEnough || originErr == hessian2.ErrBodyNotEnough {
 			return nil, 0, nil
 		}
 
@@ -120,13 +120,13 @@ func (p *RpcServerPackageHandler) Read(ss getty.Session, data []byte) (interface
 		return nil, 0, perrors.WithStack(err)
 	}
 
-	if pkg.Header.Type&hessian.PackageHeartbeat == 0x00 {
+	if pkg.Header.Type&hessian2.PackageHeartbeat == 0x00 {
 		// convert params of request
 		req := pkg.Body.([]interface{}) // length of body should be 7
 		if len(req) > 0 {
 			var dubboVersion, argsTypes string
 			var args []interface{}
-			var attachments map[string]string
+			var attachments map[string]interface{}
 			if req[0] != nil {
 				dubboVersion = req[0].(string)
 			}
@@ -146,18 +146,18 @@ func (p *RpcServerPackageHandler) Read(ss getty.Session, data []byte) (interface
 				args = req[5].([]interface{})
 			}
 			if req[6] != nil {
-				attachments = req[6].(map[string]string)
+				attachments = req[6].(map[string]interface{})
 			}
-			if pkg.Service.Path == "" && len(attachments[constant.PATH_KEY]) > 0 {
-				pkg.Service.Path = attachments[constant.PATH_KEY]
+			if pkg.Service.Path == "" && attachments[constant.PATH_KEY] != nil && len(attachments[constant.PATH_KEY].(string)) > 0 {
+				pkg.Service.Path = attachments[constant.PATH_KEY].(string)
 			}
-			if _, ok := attachments[constant.INTERFACE_KEY]; ok {
-				pkg.Service.Interface = attachments[constant.INTERFACE_KEY]
+			if inter, ok := attachments[constant.INTERFACE_KEY]; ok && inter != nil {
+				pkg.Service.Interface = inter.(string)
 			} else {
 				pkg.Service.Interface = pkg.Service.Path
 			}
-			if len(attachments[constant.GROUP_KEY]) > 0 {
-				pkg.Service.Group = attachments[constant.GROUP_KEY]
+			if attachments[constant.GROUP_KEY] != nil && len(attachments[constant.GROUP_KEY].(string)) > 0 {
+				pkg.Service.Group = attachments[constant.GROUP_KEY].(string)
 			}
 			pkg.Body = map[string]interface{}{
 				"dubboVersion": dubboVersion,
@@ -169,7 +169,7 @@ func (p *RpcServerPackageHandler) Read(ss getty.Session, data []byte) (interface
 		}
 	}
 
-	return pkg, hessian.HEADER_LENGTH + pkg.Header.BodyLen, nil
+	return pkg, hessian2.HEADER_LENGTH + pkg.Header.BodyLen, nil
 }
 
 // Write encode @pkg.
diff --git a/protocol/dubbo/server.go b/protocol/dubbo/server.go
index 8de353a0b372785e89585f4bf2b00b2c42540a2b..4ad4796c5409e39984af3ff336c2556507d15d93 100644
--- a/protocol/dubbo/server.go
+++ b/protocol/dubbo/server.go
@@ -18,18 +18,20 @@
 package dubbo
 
 import (
+	"crypto/tls"
 	"fmt"
 	"net"
 )
 
 import (
-	"github.com/dubbogo/getty"
+	"github.com/apache/dubbo-getty"
 	"github.com/dubbogo/gost/sync"
 	"gopkg.in/yaml.v2"
 )
 
 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"
 )
@@ -126,7 +128,20 @@ func (s *Server) newSession(session getty.Session) error {
 	if conf.GettySessionParam.CompressEncoding {
 		session.SetCompressType(getty.CompressZip)
 	}
-
+	if _, ok = session.Conn().(*tls.Conn); ok {
+		session.SetName(conf.GettySessionParam.SessionName)
+		session.SetMaxMsgLen(conf.GettySessionParam.MaxMsgLen)
+		session.SetPkgHandler(rpcServerPkgHandler)
+		session.SetEventListener(s.rpcHandler)
+		session.SetWQLen(conf.GettySessionParam.PkgWQSize)
+		session.SetReadTimeout(conf.GettySessionParam.tcpReadTimeout)
+		session.SetWriteTimeout(conf.GettySessionParam.tcpWriteTimeout)
+		session.SetCronPeriod((int)(conf.sessionTimeout.Nanoseconds() / 1e6))
+		session.SetWaitTime(conf.GettySessionParam.waitTimeout)
+		logger.Debugf("server accepts new session:%s\n", session.Stat())
+		session.SetTaskPool(srvGrpool)
+		return nil
+	}
 	if tcpConn, ok = session.Conn().(*net.TCPConn); !ok {
 		panic(fmt.Sprintf("%s, session.conn{%#v} is not tcp connection\n", session.Stat(), session.Conn()))
 	}
@@ -163,9 +178,18 @@ func (s *Server) Start(url common.URL) {
 	)
 
 	addr = url.Location
-	tcpServer = getty.NewTCPServer(
-		getty.WithLocalAddress(addr),
-	)
+	if url.GetParamBool(constant.SSL_ENABLED_KEY, false) {
+		tcpServer = getty.NewTCPServer(
+			getty.WithLocalAddress(addr),
+			getty.WithServerSslEnabled(url.GetParamBool(constant.SSL_ENABLED_KEY, false)),
+			getty.WithServerTlsConfigBuilder(config.GetServerTlsConfigBuilder()),
+		)
+
+	} else {
+		tcpServer = getty.NewTCPServer(
+			getty.WithLocalAddress(addr),
+		)
+	}
 	tcpServer.RunEventLoop(s.newSession)
 	logger.Debugf("s bind addr{%s} ok!", addr)
 	s.tcpServer = tcpServer
diff --git a/protocol/grpc/grpc_exporter.go b/protocol/grpc/grpc_exporter.go
index 79962b59e29bb0e3aeb58776f6c26abc2e6832de..0dc764854d61576892800180041c53f0a7735c7c 100644
--- a/protocol/grpc/grpc_exporter.go
+++ b/protocol/grpc/grpc_exporter.go
@@ -28,7 +28,7 @@ import (
 	"github.com/apache/dubbo-go/protocol"
 )
 
-// GrpcExporter ...
+// nolint
 type GrpcExporter struct {
 	*protocol.BaseExporter
 }
diff --git a/protocol/grpc/grpc_invoker.go b/protocol/grpc/grpc_invoker.go
index e150d05e6fb39890bd3e355f4042b4ef34db42ed..737e8c40a063b07e56d7c90f7de04670461ce103 100644
--- a/protocol/grpc/grpc_invoker.go
+++ b/protocol/grpc/grpc_invoker.go
@@ -37,14 +37,14 @@ import (
 
 var errNoReply = errors.New("request need @response")
 
-// GrpcInvoker ...
+// nolint
 type GrpcInvoker struct {
 	protocol.BaseInvoker
 	quitOnce sync.Once
 	client   *Client
 }
 
-// NewGrpcInvoker ...
+// NewGrpcInvoker returns a Grpc invoker instance
 func NewGrpcInvoker(url common.URL, client *Client) *GrpcInvoker {
 	return &GrpcInvoker{
 		BaseInvoker: *protocol.NewBaseInvoker(url),
@@ -52,7 +52,7 @@ func NewGrpcInvoker(url common.URL, client *Client) *GrpcInvoker {
 	}
 }
 
-// Invoke ...
+// Invoke is used to call service method by invocation
 func (gi *GrpcInvoker) Invoke(ctx context.Context, invocation protocol.Invocation) protocol.Result {
 	var (
 		result protocol.RPCResult
@@ -81,17 +81,17 @@ func (gi *GrpcInvoker) Invoke(ctx context.Context, invocation protocol.Invocatio
 	return &result
 }
 
-// IsAvailable ...
+// IsAvailable get available status
 func (gi *GrpcInvoker) IsAvailable() bool {
 	return gi.BaseInvoker.IsAvailable() && gi.client.GetState() != connectivity.Shutdown
 }
 
-// IsDestroyed ...
+// IsDestroyed get destroyed status
 func (gi *GrpcInvoker) IsDestroyed() bool {
 	return gi.BaseInvoker.IsDestroyed() && gi.client.GetState() == connectivity.Shutdown
 }
 
-// Destroy ...
+// Destroy will destroy gRPC's invoker and client, so it is only called once
 func (gi *GrpcInvoker) Destroy() {
 	gi.quitOnce.Do(func() {
 		gi.BaseInvoker.Destroy()
diff --git a/protocol/grpc/server.go b/protocol/grpc/server.go
index 4017b65dd5c35ef19795b390e40d7afff6699306..2b7b1adddf573e3b84db32a11cfc286ff22a0276 100644
--- a/protocol/grpc/server.go
+++ b/protocol/grpc/server.go
@@ -69,7 +69,7 @@ func (s *Server) Start(url common.URL) {
 		panic(err)
 	}
 
-	// if global trace instance was set ,  then server tracer instance can be get. If not , will return Nooptracer
+	// if global trace instance was set, then server tracer instance can be get. If not , will return Nooptracer
 	tracer := opentracing.GlobalTracer()
 	server := grpc.NewServer(
 		grpc.UnaryInterceptor(otgrpc.OpenTracingServerInterceptor(tracer)))
diff --git a/protocol/invocation.go b/protocol/invocation.go
index ba5949794c0120874ebdf31cfb1fd9c7d8ac08e4..452f619e2dd9a5835141d91d7adfed37bb9f6859 100644
--- a/protocol/invocation.go
+++ b/protocol/invocation.go
@@ -34,13 +34,16 @@ type Invocation interface {
 	// Reply gets response of request
 	Reply() interface{}
 	// Attachments gets all attachments
-	Attachments() map[string]string
-	// AttachmentsByKey gets attachment by key , if nil then return default value
+	Attachments() map[string]interface{}
+	// AttachmentsByKey gets attachment by key , if nil then return default value. 锛圛t will be deprecated in the future锛�
 	AttachmentsByKey(string, string) string
+	Attachment(string) interface{}
 	// Attributes refers to dubbo 2.7.6.  It is different from attachment. It is used in internal process.
 	Attributes() map[string]interface{}
 	// AttributeByKey gets attribute by key , if nil then return default value
 	AttributeByKey(string, interface{}) interface{}
+	// SetAttachments sets attribute by @key and @value.
+	SetAttachments(key string, value interface{})
 	// Invoker gets the invoker in current context.
 	Invoker() Invoker
 }
diff --git a/protocol/invocation/rpcinvocation.go b/protocol/invocation/rpcinvocation.go
index b8b5b509702ea5ee62df83eb55bf7f1c86625b26..35d12965e811fb9ac6c312a83615c51204f9b993 100644
--- a/protocol/invocation/rpcinvocation.go
+++ b/protocol/invocation/rpcinvocation.go
@@ -27,7 +27,7 @@ import (
 )
 
 // ///////////////////////////
-// Invocation Impletment of RPC
+// Invocation Implement of RPC
 // ///////////////////////////
 
 // todo: is it necessary to separate fields of consumer(provider) from RPCInvocation
@@ -39,7 +39,7 @@ type RPCInvocation struct {
 	arguments       []interface{}
 	reply           interface{}
 	callBack        interface{}
-	attachments     map[string]string
+	attachments     map[string]interface{}
 	// Refer to dubbo 2.7.6.  It is different from attachment. It is used in internal process.
 	attributes map[string]interface{}
 	invoker    protocol.Invoker
@@ -47,7 +47,7 @@ type RPCInvocation struct {
 }
 
 // NewRPCInvocation creates a RPC invocation.
-func NewRPCInvocation(methodName string, arguments []interface{}, attachments map[string]string) *RPCInvocation {
+func NewRPCInvocation(methodName string, arguments []interface{}, attachments map[string]interface{}) *RPCInvocation {
 	return &RPCInvocation{
 		methodName:  methodName,
 		arguments:   arguments,
@@ -99,11 +99,11 @@ func (r *RPCInvocation) SetReply(reply interface{}) {
 }
 
 // Attachments gets all attachments of RPC.
-func (r *RPCInvocation) Attachments() map[string]string {
+func (r *RPCInvocation) Attachments() map[string]interface{} {
 	return r.attachments
 }
 
-// AttachmentsByKey gets RPC attachment by key , if nil then return default value.
+// AttachmentsByKey gets RPC attachment by key, if nil then return default value.
 func (r *RPCInvocation) AttachmentsByKey(key string, defaultValue string) string {
 	r.lock.RLock()
 	defer r.lock.RUnlock()
@@ -112,11 +112,25 @@ func (r *RPCInvocation) AttachmentsByKey(key string, defaultValue string) string
 	}
 	value, ok := r.attachments[key]
 	if ok {
-		return value
+		return value.(string)
 	}
 	return defaultValue
 }
 
+// Attachment returns the corresponding value from dubbo's attachment with the given key.
+func (r *RPCInvocation) Attachment(key string) interface{} {
+	r.lock.RLock()
+	defer r.lock.RUnlock()
+	if r.attachments == nil {
+		return nil
+	}
+	value, ok := r.attachments[key]
+	if ok {
+		return value
+	}
+	return nil
+}
+
 // Attributes gets all attributes of RPC.
 func (r *RPCInvocation) Attributes() map[string]interface{} {
 	return r.attributes
@@ -134,11 +148,11 @@ func (r *RPCInvocation) AttributeByKey(key string, defaultValue interface{}) int
 }
 
 // SetAttachments sets attribute by @key and @value.
-func (r *RPCInvocation) SetAttachments(key string, value string) {
+func (r *RPCInvocation) SetAttachments(key string, value interface{}) {
 	r.lock.Lock()
 	defer r.lock.Unlock()
 	if r.attachments == nil {
-		r.attachments = make(map[string]string)
+		r.attachments = make(map[string]interface{})
 	}
 	r.attachments[key] = value
 }
@@ -157,6 +171,8 @@ func (r *RPCInvocation) Invoker() protocol.Invoker {
 
 // nolint
 func (r *RPCInvocation) SetInvoker(invoker protocol.Invoker) {
+	r.lock.Lock()
+	defer r.lock.Unlock()
 	r.invoker = invoker
 }
 
@@ -219,7 +235,7 @@ func WithCallBack(callBack interface{}) option {
 }
 
 // WithAttachments creates option with @attachments.
-func WithAttachments(attachments map[string]string) option {
+func WithAttachments(attachments map[string]interface{}) option {
 	return func(invo *RPCInvocation) {
 		invo.attachments = attachments
 	}
diff --git a/protocol/jsonrpc/http.go b/protocol/jsonrpc/http.go
index 5fca66d99399b2974f858cbedb31d9615a303637..2a2ddfeeeb52b865e96ccff69d2b39d8a671ed41 100644
--- a/protocol/jsonrpc/http.go
+++ b/protocol/jsonrpc/http.go
@@ -47,7 +47,7 @@ import (
 // Request
 // ////////////////////////////////////////////
 
-// Request ...
+// Request is HTTP protocol request
 type Request struct {
 	ID          int64
 	group       string
diff --git a/protocol/jsonrpc/json.go b/protocol/jsonrpc/json.go
index 7b05a229437958b44a405780bbe1649a995478d0..506c4c953b1b1113b43669171efdeeaeb6fca10d 100644
--- a/protocol/jsonrpc/json.go
+++ b/protocol/jsonrpc/json.go
@@ -288,7 +288,7 @@ type ServerCodec struct {
 
 var (
 	null = json.RawMessage([]byte("null"))
-	// Version ...
+	// Version is json RPC's version
 	Version = "2.0"
 )
 
diff --git a/protocol/jsonrpc/jsonrpc_protocol.go b/protocol/jsonrpc/jsonrpc_protocol.go
index 90a6bf5ef7aa017488f723804b22cc613850bcf2..1778d99d4078058dc43a630b89b9d256350da0f3 100644
--- a/protocol/jsonrpc/jsonrpc_protocol.go
+++ b/protocol/jsonrpc/jsonrpc_protocol.go
@@ -59,7 +59,7 @@ func NewJsonrpcProtocol() *JsonrpcProtocol {
 	}
 }
 
-// Export JSON RPC service  for remote invocation
+// Export JSON RPC service for remote invocation
 func (jp *JsonrpcProtocol) Export(invoker protocol.Invoker) protocol.Exporter {
 	url := invoker.GetUrl()
 	serviceKey := strings.TrimPrefix(url.Path, "/")
diff --git a/protocol/jsonrpc/server.go b/protocol/jsonrpc/server.go
index aa458a1614df29997b05ac4462200f9e9ffffc25..9755a481fdba325887a1d16731cbbcb8e3943d8a 100644
--- a/protocol/jsonrpc/server.go
+++ b/protocol/jsonrpc/server.go
@@ -127,10 +127,10 @@ func (s *Server) handlePkg(conn net.Conn) {
 		}
 
 		reqBody, err := ioutil.ReadAll(r.Body)
+		r.Body.Close()
 		if err != nil {
 			return
 		}
-		r.Body.Close()
 
 		reqHeader := make(map[string]string)
 		for k := range r.Header {
@@ -263,8 +263,7 @@ func (s *Server) Stop() {
 	})
 }
 
-func serveRequest(ctx context.Context,
-	header map[string]string, body []byte, conn net.Conn) error {
+func serveRequest(ctx context.Context, header map[string]string, body []byte, conn net.Conn) error {
 	sendErrorResp := func(header map[string]string, body []byte) error {
 		rsp := &http.Response{
 			Header:        make(http.Header),
@@ -324,13 +323,12 @@ func serveRequest(ctx context.Context,
 		if err == io.EOF || err == io.ErrUnexpectedEOF {
 			return perrors.WithStack(err)
 		}
-
 		return perrors.New("server cannot decode request: " + err.Error())
 	}
+
 	path := header["Path"]
 	methodName := codec.req.Method
 	if len(path) == 0 || len(methodName) == 0 {
-		codec.ReadBody(nil)
 		return perrors.New("service/method request ill-formed: " + path + "/" + methodName)
 	}
 
@@ -345,7 +343,7 @@ func serveRequest(ctx context.Context,
 	exporter, _ := jsonrpcProtocol.ExporterMap().Load(path)
 	invoker := exporter.(*JsonrpcExporter).GetInvoker()
 	if invoker != nil {
-		result := invoker.Invoke(ctx, invocation.NewRPCInvocation(methodName, args, map[string]string{
+		result := invoker.Invoke(ctx, invocation.NewRPCInvocation(methodName, args, map[string]interface{}{
 			constant.PATH_KEY:    path,
 			constant.VERSION_KEY: codec.req.Version}))
 		if err := result.Error(); err != nil {
diff --git a/protocol/protocolwrapper/protocol_filter_wrapper.go b/protocol/protocolwrapper/protocol_filter_wrapper.go
index cba1d5d5bce8c3de387381d17cc3f7965bf3adac..4b2702b99f9793d4e567de65bd9555fb20381b1d 100644
--- a/protocol/protocolwrapper/protocol_filter_wrapper.go
+++ b/protocol/protocolwrapper/protocol_filter_wrapper.go
@@ -68,25 +68,23 @@ func (pfw *ProtocolFilterWrapper) Destroy() {
 }
 
 func buildInvokerChain(invoker protocol.Invoker, key string) protocol.Invoker {
-	filtName := invoker.GetUrl().GetParam(key, "")
-	if filtName == "" {
+	filterName := invoker.GetUrl().GetParam(key, "")
+	if filterName == "" {
 		return invoker
 	}
-	filtNames := strings.Split(filtName, ",")
-	next := invoker
+	filterNames := strings.Split(filterName, ",")
 
 	// The order of filters is from left to right, so loading from right to left
-
-	for i := len(filtNames) - 1; i >= 0; i-- {
-		flt := extension.GetFilter(filtNames[i])
+	next := invoker
+	for i := len(filterNames) - 1; i >= 0; i-- {
+		flt := extension.GetFilter(filterNames[i])
 		fi := &FilterInvoker{next: next, invoker: invoker, filter: flt}
 		next = fi
 	}
-
 	return next
 }
 
-// GetProtocol ...
+// nolint
 func GetProtocol() protocol.Protocol {
 	return &ProtocolFilterWrapper{}
 }
@@ -95,30 +93,30 @@ func GetProtocol() protocol.Protocol {
 // filter invoker
 ///////////////////////////
 
-// FilterInvoker ...
+// FilterInvoker defines invoker and filter
 type FilterInvoker struct {
 	next    protocol.Invoker
 	invoker protocol.Invoker
 	filter  filter.Filter
 }
 
-// GetUrl ...
+// GetUrl is used to get url from FilterInvoker
 func (fi *FilterInvoker) GetUrl() common.URL {
 	return fi.invoker.GetUrl()
 }
 
-// IsAvailable ...
+// IsAvailable is used to get available status
 func (fi *FilterInvoker) IsAvailable() bool {
 	return fi.invoker.IsAvailable()
 }
 
-// Invoke ...
+// Invoke is used to call service method by invocation
 func (fi *FilterInvoker) Invoke(ctx context.Context, invocation protocol.Invocation) protocol.Result {
 	result := fi.filter.Invoke(ctx, fi.next, invocation)
 	return fi.filter.OnResponse(ctx, result, fi.invoker, invocation)
 }
 
-// Destroy ...
+// Destroy will destroy invoker
 func (fi *FilterInvoker) Destroy() {
 	fi.invoker.Destroy()
 }
diff --git a/protocol/rest/config/rest_config.go b/protocol/rest/config/rest_config.go
index 168ec8ce525fc7fd5d4a30d4f11ba7bf42d1c921..4732dd8e4eae3ba874fdd8ed95380e6ace3ab66d 100644
--- a/protocol/rest/config/rest_config.go
+++ b/protocol/rest/config/rest_config.go
@@ -26,7 +26,7 @@ var (
 	restProviderServiceConfigMap map[string]*RestServiceConfig
 )
 
-// RestConsumerConfig ...
+// nolint
 type RestConsumerConfig struct {
 	Client                string                        `default:"resty" yaml:"rest_client" json:"rest_client,omitempty" property:"rest_client"`
 	Produces              string                        `default:"application/json" yaml:"rest_produces"  json:"rest_produces,omitempty" property:"rest_produces"`
@@ -34,7 +34,7 @@ type RestConsumerConfig struct {
 	RestServiceConfigsMap map[string]*RestServiceConfig `yaml:"references" json:"references,omitempty" property:"references"`
 }
 
-// UnmarshalYAML ...
+// UnmarshalYAML unmarshals the RestConsumerConfig by @unmarshal function
 func (c *RestConsumerConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
 	if err := defaults.Set(c); err != nil {
 		return err
@@ -46,7 +46,7 @@ func (c *RestConsumerConfig) UnmarshalYAML(unmarshal func(interface{}) error) er
 	return nil
 }
 
-// RestProviderConfig ...
+// nolint
 type RestProviderConfig struct {
 	Server                string                        `default:"go-restful" yaml:"rest_server" json:"rest_server,omitempty" property:"rest_server"`
 	Produces              string                        `default:"*/*" yaml:"rest_produces"  json:"rest_produces,omitempty" property:"rest_produces"`
@@ -54,7 +54,7 @@ type RestProviderConfig struct {
 	RestServiceConfigsMap map[string]*RestServiceConfig `yaml:"services" json:"services,omitempty" property:"services"`
 }
 
-// UnmarshalYAML ...
+// UnmarshalYAML unmarshals the RestProviderConfig by @unmarshal function
 func (c *RestProviderConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
 	if err := defaults.Set(c); err != nil {
 		return err
@@ -66,7 +66,7 @@ func (c *RestProviderConfig) UnmarshalYAML(unmarshal func(interface{}) error) er
 	return nil
 }
 
-// RestServiceConfig ...
+// nolint
 type RestServiceConfig struct {
 	InterfaceName        string              `required:"true"  yaml:"interface"  json:"interface,omitempty" property:"interface"`
 	Url                  string              `yaml:"url"  json:"url,omitempty" property:"url"`
@@ -80,7 +80,7 @@ type RestServiceConfig struct {
 	RestMethodConfigsMap map[string]*RestMethodConfig
 }
 
-// UnmarshalYAML ...
+// UnmarshalYAML unmarshals the RestServiceConfig by @unmarshal function
 func (c *RestServiceConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
 	if err := defaults.Set(c); err != nil {
 		return err
@@ -92,7 +92,7 @@ func (c *RestServiceConfig) UnmarshalYAML(unmarshal func(interface{}) error) err
 	return nil
 }
 
-// RestMethodConfig ...
+// nolint
 type RestMethodConfig struct {
 	InterfaceName  string
 	MethodName     string `required:"true" yaml:"name"  json:"name,omitempty" property:"name"`
@@ -110,7 +110,7 @@ type RestMethodConfig struct {
 	HeadersMap     map[int]string
 }
 
-// UnmarshalYAML ...
+// UnmarshalYAML unmarshals the RestMethodConfig by @unmarshal function
 func (c *RestMethodConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
 	if err := defaults.Set(c); err != nil {
 		return err
@@ -122,32 +122,32 @@ func (c *RestMethodConfig) UnmarshalYAML(unmarshal func(interface{}) error) erro
 	return nil
 }
 
-// GetRestConsumerServiceConfig ...
+// nolint
 func GetRestConsumerServiceConfig(path string) *RestServiceConfig {
 	return restConsumerServiceConfigMap[path]
 }
 
-// GetRestProviderServiceConfig ...
+// nolint
 func GetRestProviderServiceConfig(path string) *RestServiceConfig {
 	return restProviderServiceConfigMap[path]
 }
 
-// SetRestConsumerServiceConfigMap ...
+// nolint
 func SetRestConsumerServiceConfigMap(configMap map[string]*RestServiceConfig) {
 	restConsumerServiceConfigMap = configMap
 }
 
-// SetRestProviderServiceConfigMap ...
+// nolint
 func SetRestProviderServiceConfigMap(configMap map[string]*RestServiceConfig) {
 	restProviderServiceConfigMap = configMap
 }
 
-// GetRestConsumerServiceConfigMap ...
+// nolint
 func GetRestConsumerServiceConfigMap() map[string]*RestServiceConfig {
 	return restConsumerServiceConfigMap
 }
 
-// GetRestProviderServiceConfigMap ...
+// nolint
 func GetRestProviderServiceConfigMap() map[string]*RestServiceConfig {
 	return restProviderServiceConfigMap
 }
diff --git a/protocol/rest/rest_exporter.go b/protocol/rest/rest_exporter.go
index 1ee208615ea07e3f2850920492ab9e9821e7ffef..359506a842437ba15c121c29b50478ae775a8ed7 100644
--- a/protocol/rest/rest_exporter.go
+++ b/protocol/rest/rest_exporter.go
@@ -28,16 +28,19 @@ import (
 	"github.com/apache/dubbo-go/protocol"
 )
 
+// nolint
 type RestExporter struct {
 	protocol.BaseExporter
 }
 
+// NewRestExporter returns a RestExporter
 func NewRestExporter(key string, invoker protocol.Invoker, exporterMap *sync.Map) *RestExporter {
 	return &RestExporter{
 		BaseExporter: *protocol.NewBaseExporter(key, invoker, exporterMap),
 	}
 }
 
+// Unexport unexport the RestExporter
 func (re *RestExporter) Unexport() {
 	serviceId := re.GetInvoker().GetUrl().GetParam(constant.BEAN_NAME_KEY, "")
 	interfaceName := re.GetInvoker().GetUrl().GetParam(constant.INTERFACE_KEY, "")
@@ -46,5 +49,4 @@ func (re *RestExporter) Unexport() {
 	if err != nil {
 		logger.Errorf("[RestExporter.Unexport] error: %v", err)
 	}
-	return
 }
diff --git a/protocol/rest/rest_invoker.go b/protocol/rest/rest_invoker.go
index 121d1217efd3baea0f961b67e243e9a0450aefc2..691beeda4085f316075fe55b9328bc86d6328187 100644
--- a/protocol/rest/rest_invoker.go
+++ b/protocol/rest/rest_invoker.go
@@ -35,12 +35,14 @@ import (
 	"github.com/apache/dubbo-go/protocol/rest/config"
 )
 
+// nolint
 type RestInvoker struct {
 	protocol.BaseInvoker
 	client              client.RestClient
 	restMethodConfigMap map[string]*config.RestMethodConfig
 }
 
+// NewRestInvoker returns a RestInvoker
 func NewRestInvoker(url common.URL, client *client.RestClient, restMethodConfig map[string]*config.RestMethodConfig) *RestInvoker {
 	return &RestInvoker{
 		BaseInvoker:         *protocol.NewBaseInvoker(url),
@@ -49,6 +51,7 @@ func NewRestInvoker(url common.URL, client *client.RestClient, restMethodConfig
 	}
 }
 
+// Invoke is used to call service method by invocation
 func (ri *RestInvoker) Invoke(ctx context.Context, invocation protocol.Invocation) protocol.Result {
 	inv := invocation.(*invocation_impl.RPCInvocation)
 	methodConfig := ri.restMethodConfigMap[inv.MethodName()]
@@ -95,6 +98,7 @@ func (ri *RestInvoker) Invoke(ctx context.Context, invocation protocol.Invocatio
 	return &result
 }
 
+// restStringMapTransform is used to transform rest map
 func restStringMapTransform(paramsMap map[int]string, args []interface{}) (map[string]string, error) {
 	resMap := make(map[string]string, len(paramsMap))
 	for k, v := range paramsMap {
@@ -106,6 +110,7 @@ func restStringMapTransform(paramsMap map[int]string, args []interface{}) (map[s
 	return resMap, nil
 }
 
+// nolint
 func getRestHttpHeader(methodConfig *config.RestMethodConfig, args []interface{}) (http.Header, error) {
 	header := http.Header{}
 	headersMap := methodConfig.HeadersMap
diff --git a/protocol/rest/rest_protocol.go b/protocol/rest/rest_protocol.go
index e15eeb39d72212eb9f1d0235313eba231d3b0a36..0cd26c240a7eb1b83a04ca2f57c332bbda994967 100644
--- a/protocol/rest/rest_protocol.go
+++ b/protocol/rest/rest_protocol.go
@@ -44,10 +44,12 @@ var (
 
 const REST = "rest"
 
+// nolint
 func init() {
 	extension.SetProtocol(REST, GetRestProtocol)
 }
 
+// nolint
 type RestProtocol struct {
 	protocol.BaseProtocol
 	serverLock sync.Mutex
@@ -56,6 +58,7 @@ type RestProtocol struct {
 	clientMap  map[client.RestOptions]client.RestClient
 }
 
+// NewRestProtocol returns a RestProtocol
 func NewRestProtocol() *RestProtocol {
 	return &RestProtocol{
 		BaseProtocol: protocol.NewBaseProtocol(),
@@ -64,6 +67,7 @@ func NewRestProtocol() *RestProtocol {
 	}
 }
 
+// Export export rest service
 func (rp *RestProtocol) Export(invoker protocol.Invoker) protocol.Exporter {
 	url := invoker.GetUrl()
 	serviceKey := url.ServiceKey()
@@ -81,6 +85,7 @@ func (rp *RestProtocol) Export(invoker protocol.Invoker) protocol.Exporter {
 	return exporter
 }
 
+// Refer create rest service reference
 func (rp *RestProtocol) Refer(url common.URL) protocol.Invoker {
 	// create rest_invoker
 	var requestTimeout = config.GetConsumerConfig().RequestTimeout
@@ -101,6 +106,7 @@ func (rp *RestProtocol) Refer(url common.URL) protocol.Invoker {
 	return invoker
 }
 
+// nolint
 func (rp *RestProtocol) getServer(url common.URL, serverType string) server.RestServer {
 	restServer, ok := rp.serverMap[url.Location]
 	if ok {
@@ -122,6 +128,7 @@ func (rp *RestProtocol) getServer(url common.URL, serverType string) server.Rest
 	return restServer
 }
 
+// nolint
 func (rp *RestProtocol) getClient(restOptions client.RestOptions, clientType string) client.RestClient {
 	restClient, ok := rp.clientMap[restOptions]
 	if ok {
@@ -138,6 +145,7 @@ func (rp *RestProtocol) getClient(restOptions client.RestOptions, clientType str
 	return restClient
 }
 
+// Destroy destroy rest service
 func (rp *RestProtocol) Destroy() {
 	// destroy rest_server
 	rp.BaseProtocol.Destroy()
@@ -150,6 +158,7 @@ func (rp *RestProtocol) Destroy() {
 	}
 }
 
+// GetRestProtocol get a rest protocol
 func GetRestProtocol() protocol.Protocol {
 	if restProtocol == nil {
 		restProtocol = NewRestProtocol()
diff --git a/protocol/rest/server/rest_server.go b/protocol/rest/server/rest_server.go
index fbd6fb7ad9dd81f043e4d45ee94a54e12ef89cdd..d9542bb8760cd2ddb7b839ebc6761dab75ae9c30 100644
--- a/protocol/rest/server/rest_server.go
+++ b/protocol/rest/server/rest_server.go
@@ -111,7 +111,7 @@ func GetRouteFunc(invoker protocol.Invoker, methodConfig *rest_config.RestMethod
 				logger.Errorf("[Go Restful] WriteErrorString error:%v", err)
 			}
 		}
-		result := invoker.Invoke(context.Background(), invocation.NewRPCInvocation(methodConfig.MethodName, args, make(map[string]string)))
+		result := invoker.Invoke(context.Background(), invocation.NewRPCInvocation(methodConfig.MethodName, args, make(map[string]interface{})))
 		if result.Error() != nil {
 			err = resp.WriteError(http.StatusInternalServerError, result.Error())
 			if err != nil {
diff --git a/protocol/rest/server/server_impl/go_restful_server.go b/protocol/rest/server/server_impl/go_restful_server.go
index c7d971fcaa5ada0ba02cc436b5ae6705793887ef..6fb9ee8daa7383580b9144ea25954f8ead974dcc 100644
--- a/protocol/rest/server/server_impl/go_restful_server.go
+++ b/protocol/rest/server/server_impl/go_restful_server.go
@@ -48,8 +48,8 @@ var filterSlice []restful.FilterFunction
 
 // GoRestfulServer a rest server implement by go-restful
 type GoRestfulServer struct {
-	srv       *http.Server
-	container *restful.Container
+	srv *http.Server
+	ws  *restful.WebService
 }
 
 // NewGoRestfulServer a constructor of GoRestfulServer
@@ -60,13 +60,17 @@ func NewGoRestfulServer() server.RestServer {
 // Start go-restful server
 // It will add all go-restful filters
 func (grs *GoRestfulServer) Start(url common.URL) {
-	grs.container = restful.NewContainer()
+	container := restful.NewContainer()
 	for _, filter := range filterSlice {
-		grs.container.Filter(filter)
+		container.Filter(filter)
 	}
 	grs.srv = &http.Server{
-		Handler: grs.container,
+		Handler: container,
 	}
+	grs.ws = &restful.WebService{}
+	grs.ws.Path("/")
+	grs.ws.SetDynamicRoutes(true)
+	container.Add(grs.ws)
 	ln, err := net.Listen("tcp", url.Location)
 	if err != nil {
 		panic(perrors.New(fmt.Sprintf("Restful Server start error:%v", err)))
@@ -83,23 +87,21 @@ func (grs *GoRestfulServer) Start(url common.URL) {
 // Publish a http api in go-restful server
 // The routeFunc should be invoked when the server receive a request
 func (grs *GoRestfulServer) Deploy(restMethodConfig *config.RestMethodConfig, routeFunc func(request server.RestServerRequest, response server.RestServerResponse)) {
-	ws := &restful.WebService{}
+
 	rf := func(req *restful.Request, resp *restful.Response) {
 		routeFunc(NewGoRestfulRequestAdapter(req), resp)
 	}
-	ws.Path(restMethodConfig.Path).
+	grs.ws.Route(grs.ws.Method(restMethodConfig.MethodType).
 		Produces(strings.Split(restMethodConfig.Produces, ",")...).
 		Consumes(strings.Split(restMethodConfig.Consumes, ",")...).
-		Route(ws.Method(restMethodConfig.MethodType).To(rf))
-	grs.container.Add(ws)
-
+		Path(restMethodConfig.Path).To(rf))
 }
 
 // Delete a http api in go-restful server
 func (grs *GoRestfulServer) UnDeploy(restMethodConfig *config.RestMethodConfig) {
 	ws := new(restful.WebService)
 	ws.Path(restMethodConfig.Path)
-	err := grs.container.Remove(ws)
+	err := grs.ws.RemoveRoute(restMethodConfig.Path, restMethodConfig.MethodType)
 	if err != nil {
 		logger.Warnf("[Go restful] Remove web service error:%v", err)
 	}
diff --git a/protocol/rest/server/server_impl/go_restful_server_test.go b/protocol/rest/server/server_impl/go_restful_server_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..b1e66063bf11f1fa805a83b0633a0e1aa8b38b0f
--- /dev/null
+++ b/protocol/rest/server/server_impl/go_restful_server_test.go
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package server_impl
+
+import (
+	"testing"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+import (
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/protocol/rest/config"
+	"github.com/apache/dubbo-go/protocol/rest/server"
+)
+
+func TestGoRestfulServerDeploySameUrl(t *testing.T) {
+	grs := NewGoRestfulServer()
+	url, err := common.NewURL("http://127.0.0.1:43121")
+	assert.NoError(t, err)
+	grs.Start(url)
+	rmc := &config.RestMethodConfig{
+		Produces:   "*/*",
+		Consumes:   "*/*",
+		MethodType: "POST",
+		Path:       "/test",
+	}
+	f := func(request server.RestServerRequest, response server.RestServerResponse) {}
+	grs.Deploy(rmc, f)
+	rmc1 := &config.RestMethodConfig{
+		Produces:   "*/*",
+		Consumes:   "*/*",
+		MethodType: "GET",
+		Path:       "/test",
+	}
+	grs.Deploy(rmc1, f)
+	grs.UnDeploy(rmc)
+	grs.UnDeploy(rmc1)
+	grs.Destroy()
+}
diff --git a/protocol/result.go b/protocol/result.go
index 2e7a6e492a60888ec9d9f420c77e6b77aee6aa70..a36b16d1cc56557c2976df5550f5d9c01b88619b 100644
--- a/protocol/result.go
+++ b/protocol/result.go
@@ -28,22 +28,23 @@ type Result interface {
 	// Result gets invoker result.
 	Result() interface{}
 	// SetAttachments replaces the existing attachments with the specified param.
-	SetAttachments(map[string]string)
+	SetAttachments(map[string]interface{})
 	// Attachments gets all attachments
-	Attachments() map[string]string
+	Attachments() map[string]interface{}
+
 	// AddAttachment adds the specified map to existing attachments in this instance.
-	AddAttachment(string, string)
+	AddAttachment(string, interface{})
 	// Attachment gets attachment by key with default value.
-	Attachment(string, string) string
+	Attachment(string, interface{}) interface{}
 }
 
 /////////////////////////////
-// Result Impletment of RPC
+// Result Implement of RPC
 /////////////////////////////
 
 // RPCResult is default RPC result.
 type RPCResult struct {
-	Attrs map[string]string
+	Attrs map[string]interface{}
 	Err   error
 	Rest  interface{}
 }
@@ -69,22 +70,22 @@ func (r *RPCResult) Result() interface{} {
 }
 
 // SetAttachments replaces the existing attachments with the specified param.
-func (r *RPCResult) SetAttachments(attr map[string]string) {
+func (r *RPCResult) SetAttachments(attr map[string]interface{}) {
 	r.Attrs = attr
 }
 
 // Attachments gets all attachments
-func (r *RPCResult) Attachments() map[string]string {
+func (r *RPCResult) Attachments() map[string]interface{} {
 	return r.Attrs
 }
 
 // AddAttachment adds the specified map to existing attachments in this instance.
-func (r *RPCResult) AddAttachment(key, value string) {
+func (r *RPCResult) AddAttachment(key string, value interface{}) {
 	r.Attrs[key] = value
 }
 
 // Attachment gets attachment by key with default value.
-func (r *RPCResult) Attachment(key, defaultValue string) string {
+func (r *RPCResult) Attachment(key string, defaultValue interface{}) interface{} {
 	v, ok := r.Attrs[key]
 	if !ok {
 		v = defaultValue
diff --git a/protocol/rpc_status.go b/protocol/rpc_status.go
index 60becfb34135470b0e69972c25a743f44efe19d5..978534ea8b98ddd76f68619155ff90fe5e031ac1 100644
--- a/protocol/rpc_status.go
+++ b/protocol/rpc_status.go
@@ -98,7 +98,10 @@ func (rpc *RPCStatus) GetSuccessiveRequestFailureCount() int32 {
 
 // GetURLStatus get URL RPC status.
 func GetURLStatus(url common.URL) *RPCStatus {
-	rpcStatus, _ := serviceStatistic.LoadOrStore(url.Key(), &RPCStatus{})
+	rpcStatus, found := serviceStatistic.Load(url.Key())
+	if !found {
+		rpcStatus, _ = serviceStatistic.LoadOrStore(url.Key(), &RPCStatus{})
+	}
 	return rpcStatus.(*RPCStatus)
 }
 
@@ -107,15 +110,13 @@ func GetMethodStatus(url common.URL, methodName string) *RPCStatus {
 	identifier := url.Key()
 	methodMap, found := methodStatistics.Load(identifier)
 	if !found {
-		methodMap = &sync.Map{}
-		methodStatistics.Store(identifier, methodMap)
+		methodMap, _ = methodStatistics.LoadOrStore(identifier, &sync.Map{})
 	}
 
 	methodActive := methodMap.(*sync.Map)
 	rpcStatus, found := methodActive.Load(methodName)
 	if !found {
-		rpcStatus = &RPCStatus{}
-		methodActive.Store(methodName, rpcStatus)
+		rpcStatus, _ = methodActive.LoadOrStore(methodName, &RPCStatus{})
 	}
 
 	status := rpcStatus.(*RPCStatus)
diff --git a/registry/consul/listener.go b/registry/consul/listener.go
index cf3888dd16ee4a9b504664383be69ca1faf4d842..b1598345032bddd7358c2263e5fd71b3d4e5545d 100644
--- a/registry/consul/listener.go
+++ b/registry/consul/listener.go
@@ -197,7 +197,7 @@ func (l *consulListener) Next() (*registry.ServiceEvent, error) {
 	}
 }
 
-// Close closes this listener
+// Close the listener.
 func (l *consulListener) Close() {
 	close(l.done)
 	l.plan.Stop()
diff --git a/registry/consul/utils_test.go b/registry/consul/utils_test.go
index 327dd95f7181907f6635c7fe89ef726bdcef5204..939352dc088faa2c32be8173d0aa6f4516dfe503 100644
--- a/registry/consul/utils_test.go
+++ b/registry/consul/utils_test.go
@@ -148,7 +148,7 @@ func (suite *consulRegistryTestSuite) close() {
 // register -> subscribe -> unregister
 func test1(t *testing.T) {
 	consulAgent := consul.NewConsulAgent(t, registryPort)
-	defer consulAgent.Close()
+	defer consulAgent.Shutdown()
 
 	server := newServer(providerHost, providerPort)
 	defer server.close()
@@ -165,10 +165,10 @@ func test1(t *testing.T) {
 	suite.testListener(remoting.EventTypeDel)
 }
 
-// subscribe -> register
+// subscribe -> register -> unregister
 func test2(t *testing.T) {
 	consulAgent := consul.NewConsulAgent(t, registryPort)
-	defer consulAgent.Close()
+	defer consulAgent.Shutdown()
 
 	server := newServer(providerHost, providerPort)
 	defer server.close()
@@ -181,6 +181,8 @@ func test2(t *testing.T) {
 	suite.testNewProviderRegistry()
 	suite.testRegister()
 	suite.testListener(remoting.EventTypeAdd)
+	suite.testUnregister()
+	suite.testListener(remoting.EventTypeDel)
 }
 
 func TestConsulRegistry(t *testing.T) {
diff --git a/registry/directory/directory.go b/registry/directory/directory.go
index 2fbf9410f76c473362964c9ef148e3c581d3d045..5d4a890c6e2f59107a1dff3fc09ebf2c37777d56 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)
@@ -89,69 +104,120 @@ func (dir *RegistryDirectory) subscribe(url *common.URL) {
 }
 
 // Notify monitor changes from registry,and update the cacheServices
-func (dir *RegistryDirectory) Notify(event *registry.ServiceEvent) {
-	go dir.update(event)
+func (dir *RegistryDirectory) Notify(events ...*registry.ServiceEvent) {
+	go dir.refreshInvokers(events...)
 }
 
-// update the cacheServices and subscribe service from registry
-func (dir *RegistryDirectory) update(res *registry.ServiceEvent) {
-	if res == nil {
-		return
+// refreshInvokers refreshes service's events. It supports two modes: incremental mode and batch mode. If a single
+// service event is passed in, then it is incremental mode, and if an array of service events are passed in, it is
+// batch mode, in this mode, we assume the registry center have the complete list of the service events, therefore
+// in this case, we can safely assume any cached invoker not in the incoming list can be removed. It is necessary
+// since in batch mode, the register center handles the different type of events by itself, then notify the directory
+// a batch of 'Update' events, instead of omit the different type of event one by one.
+func (dir *RegistryDirectory) refreshInvokers(events ...*registry.ServiceEvent) {
+	var oldInvokers []protocol.Invoker
+
+	// in batch mode, it is safe to remove since we have the complete list of events.
+	if len(events) > 1 {
+		dir.cacheInvokersMap.Range(func(k, v interface{}) bool {
+			if !dir.eventMatched(k.(string), events) {
+				if invoker := dir.uncacheInvokerWithKey(k.(string)); invoker != nil {
+					oldInvokers = append(oldInvokers, invoker)
+				}
+			}
+			return true
+		})
 	}
-	logger.Debugf("registry update, result{%s}", res)
-	logger.Debugf("update service name: %s!", res.Service)
-	dir.refreshInvokers(res)
-}
-
-func (dir *RegistryDirectory) refreshInvokers(res *registry.ServiceEvent) {
-	var (
-		url        *common.URL
-		oldInvoker protocol.Invoker = nil
-	)
-	// judge is override or others
-	if res != nil {
-		url = &res.Service
-		// 1.for override url in 2.6.x
-		if url.Protocol == constant.OVERRIDE_PROTOCOL ||
-			url.GetParam(constant.CATEGORY_KEY, constant.DEFAULT_CATEGORY) == constant.CONFIGURATORS_CATEGORY {
-			dir.configurators = append(dir.configurators, extension.GetDefaultConfigurator(url))
-			url = nil
-		} else if url.Protocol == constant.ROUTER_PROTOCOL || // 2.for router
-			url.GetParam(constant.CATEGORY_KEY, constant.DEFAULT_CATEGORY) == constant.ROUTER_CATEGORY {
-			url = nil
 
+	for _, event := range events {
+		logger.Debugf("registry update, result{%s}", event)
+		if oldInvoker, _ := dir.cacheInvokerByEvent(event); oldInvoker != nil {
+			oldInvokers = append(oldInvokers, oldInvoker)
 		}
-		switch res.Action {
-		case remoting.EventTypeAdd, remoting.EventTypeUpdate:
-			logger.Infof("selector add service url{%s}", res.Service)
+	}
 
-			var urls []*common.URL
-			for _, v := range config.GetRouterURLSet().Values() {
-				urls = append(urls, v.(*common.URL))
-			}
+	if len(events) > 0 {
+		dir.setNewInvokers()
+	}
 
-			if len(urls) > 0 {
-				dir.SetRouters(urls)
-			}
-			oldInvoker = dir.cacheInvoker(url)
-		case remoting.EventTypeDel:
-			oldInvoker = dir.uncacheInvoker(url)
-			logger.Infof("selector delete service url{%s}", res.Service)
-		default:
-			return
+	// After dir.cacheInvokers is updated,destroy the oldInvoker
+	// Ensure that no request will enter the oldInvoker
+	for _, invoker := range oldInvokers {
+		invoker.Destroy()
+	}
+}
+
+// eventMatched checks if a cached invoker appears in the incoming invoker list, if no, then it is safe to remove.
+func (dir *RegistryDirectory) eventMatched(key string, events []*registry.ServiceEvent) bool {
+	for _, event := range events {
+		if dir.invokerCacheKey(&event.Service) == key {
+			return true
 		}
 	}
+	return false
+}
 
+// invokerCacheKey generates the key in the cache for a given URL.
+func (dir *RegistryDirectory) invokerCacheKey(url *common.URL) string {
+	referenceUrl := dir.GetDirectoryUrl().SubURL
+	newUrl := common.MergeUrl(url, referenceUrl)
+	return newUrl.Key()
+}
+
+// setNewInvokers groups the invokers from the cache first, then set the result to both directory and router chain.
+func (dir *RegistryDirectory) setNewInvokers() {
 	newInvokers := dir.toGroupInvokers()
 	dir.listenerLock.Lock()
+	defer dir.listenerLock.Unlock()
 	dir.cacheInvokers = newInvokers
-	dir.listenerLock.Unlock()
-	// After dir.cacheInvokers is updated,destroy the oldInvoker
-	// Ensure that no request will enter the oldInvoker
-	if oldInvoker != nil {
-		oldInvoker.Destroy()
+	dir.RouterChain().SetInvokers(newInvokers)
+}
+
+// cacheInvokerByEvent caches invokers from the service event
+func (dir *RegistryDirectory) cacheInvokerByEvent(event *registry.ServiceEvent) (protocol.Invoker, error) {
+	// judge is override or others
+	if event != nil {
+		u := dir.convertUrl(event)
+		switch event.Action {
+		case remoting.EventTypeAdd, remoting.EventTypeUpdate:
+			logger.Infof("selector add service url{%s}", event.Service)
+			// FIXME: routers are built in every address notification?
+			dir.configRouters()
+			return dir.cacheInvoker(u), nil
+		case remoting.EventTypeDel:
+			logger.Infof("selector delete service url{%s}", event.Service)
+			return dir.uncacheInvoker(u), nil
+		default:
+			return nil, fmt.Errorf("illegal event type: %v", event.Action)
+		}
+	}
+	return nil, nil
+}
+
+// configRouters configures dynamic routers into the router chain, but, the current impl is incorrect, see FIXME above.
+func (dir *RegistryDirectory) configRouters() {
+	var urls []*common.URL
+	for _, v := range config.GetRouterURLSet().Values() {
+		urls = append(urls, v.(*common.URL))
+	}
+
+	if len(urls) > 0 {
+		dir.SetRouters(urls)
 	}
+}
 
+// convertUrl processes override:// and router://
+func (dir *RegistryDirectory) convertUrl(res *registry.ServiceEvent) *common.URL {
+	ret := &res.Service
+	if ret.Protocol == constant.OVERRIDE_PROTOCOL || // 1.for override url in 2.6.x
+		ret.GetParam(constant.CATEGORY_KEY, constant.DEFAULT_CATEGORY) == constant.CONFIGURATORS_CATEGORY {
+		dir.configurators = append(dir.configurators, extension.GetDefaultConfigurator(ret))
+		ret = nil
+	} else if ret.Protocol == constant.ROUTER_PROTOCOL || // 2.for router
+		ret.GetParam(constant.CATEGORY_KEY, constant.DEFAULT_CATEGORY) == constant.ROUTER_CATEGORY {
+		ret = nil
+	}
+	return ret
 }
 
 func (dir *RegistryDirectory) toGroupInvokers() []protocol.Invoker {
@@ -197,11 +263,15 @@ func (dir *RegistryDirectory) toGroupInvokers() []protocol.Invoker {
 	return groupInvokersList
 }
 
-// uncacheInvoker will return abandoned Invoker,if no Invoker to be abandoned,return nil
+// uncacheInvoker will return abandoned Invoker, if no Invoker to be abandoned, return nil
 func (dir *RegistryDirectory) uncacheInvoker(url *common.URL) protocol.Invoker {
-	logger.Debugf("service will be deleted in cache invokers: invokers key is  %s!", url.Key())
-	if cacheInvoker, ok := dir.cacheInvokersMap.Load(url.Key()); ok {
-		dir.cacheInvokersMap.Delete(url.Key())
+	return dir.uncacheInvokerWithKey(url.Key())
+}
+
+func (dir *RegistryDirectory) uncacheInvokerWithKey(key string) protocol.Invoker {
+	logger.Debugf("service will be deleted in cache invokers: invokers key is  %s!", key)
+	if cacheInvoker, ok := dir.cacheInvokersMap.Load(key); ok {
+		dir.cacheInvokersMap.Delete(key)
 		return cacheInvoker.(protocol.Invoker)
 	}
 	return nil
@@ -232,6 +302,12 @@ func (dir *RegistryDirectory) cacheInvoker(url *common.URL) protocol.Invoker {
 				dir.cacheInvokersMap.Store(newUrl.Key(), newInvoker)
 			}
 		} else {
+			// if cached invoker has the same URL with the new URL, then no need to re-refer, and no need to destroy
+			// the old invoker.
+			if common.IsEquals(*newUrl, cacheInvoker.(protocol.Invoker).GetUrl()) {
+				return nil
+			}
+
 			logger.Debugf("service will be updated in cache invokers: new invoker url is %s, old invoker url is %s", newUrl, cacheInvoker.(protocol.Invoker).GetUrl())
 			newInvoker := extension.GetProtocol(protocolwrapper.FILTER).Refer(*newUrl)
 			if newInvoker != nil {
@@ -251,7 +327,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 +363,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)
@@ -312,7 +406,8 @@ func newReferenceConfigurationListener(dir *RegistryDirectory, url *common.URL)
 // Process handle events and update Invokers
 func (l *referenceConfigurationListener) Process(event *config_center.ConfigChangeEvent) {
 	l.BaseConfigurationListener.Process(event)
-	l.directory.refreshInvokers(nil)
+	// FIXME: this doesn't trigger dir.overrideUrl()
+	l.directory.refreshInvokers()
 }
 
 type consumerConfigurationListener struct {
@@ -338,5 +433,6 @@ func (l *consumerConfigurationListener) addNotifyListener(listener registry.Noti
 // Process handles events from Configuration Center and update Invokers
 func (l *consumerConfigurationListener) Process(event *config_center.ConfigChangeEvent) {
 	l.BaseConfigurationListener.Process(event)
-	l.directory.refreshInvokers(nil)
+	// FIXME: this doesn't trigger dir.overrideUrl()
+	l.directory.refreshInvokers()
 }
diff --git a/registry/etcdv3/listener_test.go b/registry/etcdv3/listener_test.go
index f27e7ce8ba5ab3515f8290b208b4823872ba48a3..1cf06d17dcd4c92afc5221dab404e9d180c03e6c 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"
 )
 
@@ -63,7 +63,6 @@ func (suite *RegistryTestSuite) SetupSuite() {
 	}
 
 	suite.etcd = e
-	return
 }
 
 // stop etcd server
diff --git a/registry/etcdv3/service_discovery.go b/registry/etcdv3/service_discovery.go
index 10396049fb6bb7a5a5935ce21639dc5a78a56b0b..dceaa99df8061c6f46baa52eb6f5cebe4477f120 100644
--- a/registry/etcdv3/service_discovery.go
+++ b/registry/etcdv3/service_discovery.go
@@ -26,7 +26,7 @@ import (
 import (
 	gxset "github.com/dubbogo/gost/container/set"
 	gxpage "github.com/dubbogo/gost/page"
-	"github.com/hashicorp/vault/helper/jsonutil"
+	"github.com/hashicorp/vault/sdk/helper/jsonutil"
 	perrors "github.com/pkg/errors"
 )
 
@@ -71,7 +71,7 @@ func (e *etcdV3ServiceDiscovery) String() string {
 	return e.descriptor
 }
 
-// Destory service discovery
+// Destroy service discovery
 func (e *etcdV3ServiceDiscovery) Destroy() error {
 	if e.client != nil {
 		e.client.Close()
diff --git a/registry/event/service_revision_customizer.go b/registry/event/service_revision_customizer.go
index fd21e8f4c7a71cedfe1de7e9c836e7cee278182e..4793e91948fe4c30fffbfd21f0dcc3efe57c5095 100644
--- a/registry/event/service_revision_customizer.go
+++ b/registry/event/service_revision_customizer.go
@@ -126,7 +126,7 @@ func resolveRevision(urls []interface{}) string {
 
 		// append url params if we need it
 	}
-	sort.Sort(sort.StringSlice(candidates))
+	sort.Strings(candidates)
 
 	// it's nearly impossible to be overflow
 	res := uint64(0)
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 36f733df5a32f57e3410a2f31f9ab4b0af735d49..cf6a73d38fbefc1ecb6a80e4911bb0d8fb13a6f6 100644
--- a/registry/nacos/listener.go
+++ b/registry/nacos/listener.go
@@ -132,10 +132,10 @@ 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 exsit 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
+			// instance is not different from cache, update it to cache
 			if !reflect.DeepEqual(old, instance) {
 				updateInstances = append(updateInstances, instance)
 			}
@@ -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 exsit 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/nacos/registry.go b/registry/nacos/registry.go
index 51d3e2f56abac8e4ab8b966870f1ff5bb79c4171..411090820c7682ab9c3b5576ea8ad5207c2c899f 100644
--- a/registry/nacos/registry.go
+++ b/registry/nacos/registry.go
@@ -59,6 +59,7 @@ func init() {
 type nacosRegistry struct {
 	*common.URL
 	namingClient naming_client.INamingClient
+	registryUrls []common.URL
 }
 
 func getCategory(url common.URL) string {
@@ -128,6 +129,36 @@ func (nr *nacosRegistry) Register(url common.URL) error {
 	if !isRegistry {
 		return perrors.New("registry [" + serviceName + "] to  nacos failed")
 	}
+	nr.registryUrls = append(nr.registryUrls, url)
+	return nil
+}
+
+func createDeregisterParam(url common.URL, serviceName string) vo.DeregisterInstanceParam {
+	if len(url.Ip) == 0 {
+		url.Ip = localIP
+	}
+	if len(url.Port) == 0 || url.Port == "0" {
+		url.Port = "80"
+	}
+	port, _ := strconv.Atoi(url.Port)
+	return vo.DeregisterInstanceParam{
+		Ip:          url.Ip,
+		Port:        uint64(port),
+		ServiceName: serviceName,
+		Ephemeral:   true,
+	}
+}
+
+func (nr *nacosRegistry) DeRegister(url common.URL) error {
+	serviceName := getServiceName(url)
+	param := createDeregisterParam(url, serviceName)
+	isDeRegistry, err := nr.namingClient.DeregisterInstance(param)
+	if err != nil {
+		return err
+	}
+	if !isDeRegistry {
+		return perrors.New("DeRegistry [" + serviceName + "] to nacos failed")
+	}
 	return nil
 }
 
@@ -193,6 +224,13 @@ func (nr *nacosRegistry) IsAvailable() bool {
 
 // nolint
 func (nr *nacosRegistry) Destroy() {
+	for _, url := range nr.registryUrls {
+		err := nr.DeRegister(url)
+		logger.Infof("DeRegister Nacos URL:%+v", url)
+		if err != nil {
+			logger.Errorf("Deregister URL:%+v err:%v", url, err.Error())
+		}
+	}
 	return
 }
 
@@ -209,6 +247,7 @@ func newNacosRegistry(url *common.URL) (registry.Registry, error) {
 	registry := &nacosRegistry{
 		URL:          url,
 		namingClient: client,
+		registryUrls: []common.URL{},
 	}
 	return registry, nil
 }
diff --git a/registry/nacos/service_discovery.go b/registry/nacos/service_discovery.go
index 63d92d70fd5e1a00f0ce1ca95b1926fb9c36c84b..0e5ad8e6990856aeb0dfdde72f9c3f7fdae3e985 100644
--- a/registry/nacos/service_discovery.go
+++ b/registry/nacos/service_discovery.go
@@ -60,11 +60,20 @@ type nacosServiceDiscovery struct {
 
 	// namingClient is the Nacos' client
 	namingClient naming_client.INamingClient
+	// cache registry instances
+	registryInstances []registry.ServiceInstance
 }
 
 // Destroy will close the service discovery.
 // Actually, it only marks the naming client as null and then return
 func (n *nacosServiceDiscovery) Destroy() error {
+	for _, inst := range n.registryInstances {
+		err := n.Unregister(inst)
+		logger.Infof("Unregister nacos instance:%+v", inst)
+		if err != nil {
+			logger.Errorf("Unregister nacos instance:%+v, err:%+v", inst, err)
+		}
+	}
 	n.namingClient = nil
 	return nil
 }
@@ -76,6 +85,7 @@ func (n *nacosServiceDiscovery) Register(instance registry.ServiceInstance) erro
 	if err != nil || !ok {
 		return perrors.WithMessage(err, "Could not register the instance. "+instance.GetServiceName())
 	}
+	n.registryInstances = append(n.registryInstances, instance)
 	return nil
 }
 
@@ -118,8 +128,8 @@ func (n *nacosServiceDiscovery) GetServices() *gxset.HashSet {
 		return res
 	}
 
-	for _, e := range services {
-		res.Add(e.Name)
+	for _, e := range services.Doms {
+		res.Add(e)
 	}
 	return res
 }
@@ -334,8 +344,9 @@ func newNacosServiceDiscovery(name string) (registry.ServiceDiscovery, error) {
 	descriptor := fmt.Sprintf("nacos-service-discovery[%s]", remoteConfig.Address)
 
 	return &nacosServiceDiscovery{
-		group:        group,
-		namingClient: client,
-		descriptor:   descriptor,
+		group:             group,
+		namingClient:      client,
+		descriptor:        descriptor,
+		registryInstances: []registry.ServiceInstance{},
 	}, nil
 }
diff --git a/registry/nacos/service_discovery_test.go b/registry/nacos/service_discovery_test.go
index 720c44a6f98e4693bb2395a538b2f5e679196647..119be0b3aad3a828470c8c72c775abaada9512c2 100644
--- a/registry/nacos/service_discovery_test.go
+++ b/registry/nacos/service_discovery_test.go
@@ -18,7 +18,10 @@
 package nacos
 
 import (
+	"math/rand"
+	"strconv"
 	"testing"
+	"time"
 )
 
 import (
@@ -84,8 +87,8 @@ func TestNacosServiceDiscovery_CRUD(t *testing.T) {
 	})
 
 	extension.SetAndInitGlobalDispatcher("mock")
-
-	serviceName := "service-name"
+	rand.Seed(time.Now().Unix())
+	serviceName := "service-name" + strconv.Itoa(rand.Intn(10000))
 	id := "id"
 	host := "host"
 	port := 123
@@ -100,23 +103,26 @@ func TestNacosServiceDiscovery_CRUD(t *testing.T) {
 	}
 
 	// clean data
-
-	serviceDiscovry, _ := extension.GetServiceDiscovery(constant.NACOS_KEY, testName)
+	serviceDiscovery, err := extension.GetServiceDiscovery(constant.NACOS_KEY, testName)
+	assert.Nil(t, err)
 
 	// clean data for local test
-	serviceDiscovry.Unregister(&registry.DefaultServiceInstance{
+	err = serviceDiscovery.Unregister(&registry.DefaultServiceInstance{
 		Id:          id,
 		ServiceName: serviceName,
 		Host:        host,
 		Port:        port,
 	})
+	assert.Nil(t, err)
 
-	err := serviceDiscovry.Register(instance)
+	err = serviceDiscovery.Register(instance)
 	assert.Nil(t, err)
 
-	page := serviceDiscovry.GetHealthyInstancesByPage(serviceName, 0, 10, true)
+	//sometimes nacos may be failed to push update of instance,
+	//so it need 10s to pull, we sleep 10 second to make sure instance has been update
+	time.Sleep(11 * time.Second)
+	page := serviceDiscovery.GetHealthyInstancesByPage(serviceName, 0, 10, true)
 	assert.NotNil(t, page)
-
 	assert.Equal(t, 0, page.GetOffset())
 	assert.Equal(t, 10, page.GetPageSize())
 	assert.Equal(t, 1, page.GetDataSize())
@@ -130,12 +136,13 @@ func TestNacosServiceDiscovery_CRUD(t *testing.T) {
 	assert.Equal(t, 0, len(instance.GetMetadata()))
 
 	instance.Metadata["a"] = "b"
-
-	err = serviceDiscovry.Update(instance)
+	err = serviceDiscovery.Update(instance)
 	assert.Nil(t, err)
 
-	pageMap := serviceDiscovry.GetRequestInstances([]string{serviceName}, 0, 1)
+	time.Sleep(5 * time.Second)
+	pageMap := serviceDiscovery.GetRequestInstances([]string{serviceName}, 0, 1)
 	assert.Equal(t, 1, len(pageMap))
+
 	page = pageMap[serviceName]
 	assert.NotNil(t, page)
 	assert.Equal(t, 1, len(page.GetData()))
@@ -145,11 +152,11 @@ func TestNacosServiceDiscovery_CRUD(t *testing.T) {
 	assert.Equal(t, "b", v)
 
 	// test dispatcher event
-	err = serviceDiscovry.DispatchEventByServiceName(serviceName)
+	err = serviceDiscovery.DispatchEventByServiceName(serviceName)
 	assert.Nil(t, err)
 
 	// test AddListener
-	err = serviceDiscovry.AddListener(&registry.ServiceInstancesChangedListener{})
+	err = serviceDiscovery.AddListener(&registry.ServiceInstancesChangedListener{ServiceName: serviceName})
 	assert.Nil(t, err)
 }
 
diff --git a/registry/protocol/protocol.go b/registry/protocol/protocol.go
index 4c669b2cee74b95ceb3bc8287f145ccd6b99bc0b..d313e482a7c945e15e8431ecc10461ec757268db 100644
--- a/registry/protocol/protocol.go
+++ b/registry/protocol/protocol.go
@@ -22,6 +22,7 @@ import (
 	"strings"
 	"sync"
 )
+
 import (
 	gxset "github.com/dubbogo/gost/container/set"
 )
@@ -54,9 +55,10 @@ var (
 
 type registryProtocol struct {
 	invokers []protocol.Invoker
-	// Registry  Map<RegistryAddress, Registry>
+	// Registry Map<RegistryAddress, Registry>
 	registries *sync.Map
-	// To solve the problem of RMI repeated exposure port conflicts, the services that have been exposed are no longer exposed.
+	// To solve the problem of RMI repeated exposure port conflicts,
+	// the services that have been exposed are no longer exposed.
 	// providerurl <--> exporter
 	bounds                        *sync.Map
 	overrideListeners             *sync.Map
@@ -100,10 +102,9 @@ func getUrlToRegistry(providerUrl *common.URL, registryUrl *common.URL) *common.
 
 // filterHideKey filter the parameters that do not need to be output in url(Starting with .)
 func filterHideKey(url *common.URL) *common.URL {
-
 	// be careful params maps in url is map type
 	removeSet := gxset.NewSet()
-	for k, _ := range url.GetParams() {
+	for k := range url.GetParams() {
 		if strings.HasPrefix(k, ".") {
 			removeSet.Add(k)
 		}
@@ -117,6 +118,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
@@ -127,7 +140,6 @@ func (proto *registryProtocol) Refer(url common.URL) protocol.Invoker {
 	}
 
 	var reg registry.Registry
-
 	if regI, loaded := proto.registries.Load(registryUrl.Key()); !loaded {
 		reg = getRegistry(&registryUrl)
 		proto.registries.Store(registryUrl.Key(), reg)
@@ -138,7 +150,7 @@ func (proto *registryProtocol) Refer(url common.URL) protocol.Invoker {
 	// new registry directory for store service url from registry
 	directory, err := extension.GetDefaultRegistryDirectory(&registryUrl, reg)
 	if err != nil {
-		logger.Errorf("consumer service %v  create registry directory  error, error message is %s, and will return nil invoker!",
+		logger.Errorf("consumer service %v create registry directory error, error message is %s, and will return nil invoker!",
 			serviceUrl.String(), err.Error())
 		return nil
 	}
@@ -151,7 +163,6 @@ func (proto *registryProtocol) Refer(url common.URL) protocol.Invoker {
 
 	// new cluster invoker
 	cluster := extension.GetCluster(serviceUrl.GetParam(constant.CLUSTER_KEY, constant.DEFAULT_CLUSTER))
-
 	invoker := cluster.Join(directory)
 	proto.invokers = append(proto.invokers, invoker)
 	return invoker
@@ -178,6 +189,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)
 	}
@@ -191,7 +203,7 @@ func (proto *registryProtocol) Export(invoker protocol.Invoker) protocol.Exporte
 	}
 
 	key := getCacheKey(providerUrl)
-	logger.Infof("The cached exporter keys is %v !", key)
+	logger.Infof("The cached exporter keys is %v!", key)
 	cachedExporter, loaded := proto.bounds.Load(key)
 	if loaded {
 		logger.Infof("The exporter has been cached, and will return cached exporter!")
@@ -204,7 +216,6 @@ func (proto *registryProtocol) Export(invoker protocol.Invoker) protocol.Exporte
 
 	go reg.Subscribe(overriderUrl, overrideSubscribeListener)
 	return cachedExporter.(protocol.Exporter)
-
 }
 
 func (proto *registryProtocol) reExport(invoker protocol.Invoker, newUrl *common.URL) {
@@ -216,7 +227,6 @@ func (proto *registryProtocol) reExport(invoker protocol.Invoker, newUrl *common
 		proto.bounds.Delete(key)
 		proto.Export(wrappedNewInvoker)
 		// TODO:  unregister & unsubscribe
-
 	}
 }
 
@@ -232,7 +242,12 @@ func newOverrideSubscribeListener(overriderUrl *common.URL, invoker protocol.Inv
 }
 
 // Notify will be triggered when a service change notification is received.
-func (nl *overrideSubscribeListener) Notify(event *registry.ServiceEvent) {
+func (nl *overrideSubscribeListener) Notify(events ...*registry.ServiceEvent) {
+	if len(events) == 0 {
+		return
+	}
+
+	event := events[0]
 	if isMatched(&(event.Service), nl.url) && event.Action == remoting.EventTypeAdd {
 		nl.configurator = extension.GetDefaultConfigurator(&(event.Service))
 		nl.doOverrideIfNecessary()
@@ -334,14 +349,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() {
@@ -355,7 +368,7 @@ func (proto *registryProtocol) Destroy() {
 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 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
@@ -373,7 +386,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/registry/registry.go b/registry/registry.go
index bb09ead7ef2af6707345086f8695b35286d76a10..2225d2c1fc6d465bfbf27cd9622d7296e4596d8f 100644
--- a/registry/registry.go
+++ b/registry/registry.go
@@ -30,38 +30,49 @@ import (
 // Registry Extension - Registry
 type Registry interface {
 	common.Node
-	//used for service provider calling , register services to registry
-	//And it is also used for service consumer calling , register services cared about ,for dubbo's admin monitoring.
+
+	// Register is used for service provider calling, register services
+	// to registry. And it is also used for service consumer calling, register
+	// services cared about, for dubbo's admin monitoring.
 	Register(url common.URL) error
 
 	// UnRegister is required to support the contract:
-	// 1. If it is the persistent stored data of dynamic=false, the registration data can not be found, then the IllegalStateException is thrown, otherwise it is ignored.
+	// 1. If it is the persistent stored data of dynamic=false, the
+	//    registration data can not be found, then the IllegalStateException
+	//    is thrown, otherwise it is ignored.
 	// 2. Unregister according to the full url match.
-	// url Registration information , is not allowed to be empty, e.g: dubbo://10.20.153.10/org.apache.dubbo.foo.BarService?version=1.0.0&application=kylin
+	// url Registration information, is not allowed to be empty, e.g:
+	// dubbo://10.20.153.10/org.apache.dubbo.foo.BarService?version=1.0.0&application=kylin
 	UnRegister(url common.URL) error
 
-	//When creating new registry extension,pls select one of the following modes.
-	//Will remove in dubbogo version v1.1.0
-	//mode1 : return Listener with Next function which can return subscribe service event from registry
-	//Deprecated!
-	//subscribe(event.URL) (Listener, error)
-
-	//Will relace mode1 in dubbogo version v1.1.0
-	//mode2 : callback mode, subscribe with notify(notify listener).
+	// Subscribe is required to support the contract:
+	// When creating new registry extension, pls select one of the
+	// following modes.
+	// Will remove in dubbogo version v1.1.0
+	// mode1: return Listener with Next function which can return
+	//        subscribe service event from registry
+	// Deprecated!
+	// subscribe(event.URL) (Listener, error)
+	// Will replace mode1 in dubbogo version v1.1.0
+	// mode2: callback mode, subscribe with notify(notify listener).
 	Subscribe(*common.URL, NotifyListener) error
 
 	// UnSubscribe is required to support the contract:
 	// 1. If don't subscribe, ignore it directly.
 	// 2. Unsubscribe by full URL match.
-	// url      Subscription condition, not allowed to be empty, e.g. consumer://10.20.153.10/org.apache.dubbo.foo.BarService?version=1.0.0&application=kylin
+	// url Subscription condition, not allowed to be empty, e.g.
+	// consumer://10.20.153.10/org.apache.dubbo.foo.BarService?version=1.0.0&application=kylin
 	// listener A listener of the change event, not allowed to be empty
 	UnSubscribe(*common.URL, NotifyListener) error
 }
 
 // nolint
 type NotifyListener interface {
-	// Notify supports notifications on the service interface and the dimension of the data type.
-	Notify(*ServiceEvent)
+	// Notify supports notifications on the service interface and the dimension of the data type. When a list of
+	// events are passed in, it's considered as a complete list, on the other side, if one single event is
+	// passed in, then it's a incremental event. Pls. note when a list (instead of single event) comes,
+	// the impl of NotifyListener may abandon the accumulated result from previous notifications.
+	Notify(...*ServiceEvent)
 }
 
 // Listener Deprecated!
diff --git a/registry/registry_factory.go b/registry/registry_factory.go
new file mode 100644
index 0000000000000000000000000000000000000000..caefbce2eb833437a0e55323fad5ab334210db4e
--- /dev/null
+++ b/registry/registry_factory.go
@@ -0,0 +1,24 @@
+/*
+ * 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
+
+// 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/registry/servicediscovery/synthesizer/subscribed_urls_synthesizer.go b/registry/servicediscovery/synthesizer/subscribed_urls_synthesizer.go
index 949a5822c237de413b59d35efe94f807975795cf..415ca35fbad2fa335d687dc7a7718fa3a4b2b487 100644
--- a/registry/servicediscovery/synthesizer/subscribed_urls_synthesizer.go
+++ b/registry/servicediscovery/synthesizer/subscribed_urls_synthesizer.go
@@ -22,6 +22,7 @@ import (
 	"github.com/apache/dubbo-go/registry"
 )
 
+// SubscribedURLsSynthesizer is used to synthesize the subscribed url
 type SubscribedURLsSynthesizer interface {
 	// Supports the synthesis of the subscribed url or not
 	Support(subscribedURL *common.URL) bool
diff --git a/registry/zookeeper/registry.go b/registry/zookeeper/registry.go
index 8f2ac1023b8ad34938b9996b480e3bbc4adbaaea..e8ee51beb70b5a08ec60b213c5342ef52972c59f 100644
--- a/registry/zookeeper/registry.go
+++ b/registry/zookeeper/registry.go
@@ -243,6 +243,8 @@ func (r *zkRegistry) getListener(conf *common.URL) (*RegistryConfigurationListen
 
 	var zkListener *RegistryConfigurationListener
 	dataListener := r.dataListener
+	ttl := r.GetParam(constant.REGISTRY_TTL_KEY, constant.DEFAULT_REG_TTL)
+	conf.SetParam(constant.REGISTRY_TTL_KEY, ttl)
 	dataListener.mutex.Lock()
 	defer dataListener.mutex.Unlock()
 	if r.dataListener.subscribed[conf.ServiceKey()] != nil {
diff --git a/remoting/consul/test_agent.go b/remoting/consul/test_agent.go
index 1744da7bd9992ae3cd376b22e9ea3a135dce2b16..f6ba336a95667fdf1d9b305e1844408c2501190a 100644
--- a/remoting/consul/test_agent.go
+++ b/remoting/consul/test_agent.go
@@ -18,8 +18,6 @@
 package consul
 
 import (
-	"io/ioutil"
-	"os"
 	"strconv"
 	"testing"
 )
@@ -30,35 +28,11 @@ import (
 
 // Consul agent, used for test, simulates
 // an embedded consul server.
-type ConsulAgent struct {
-	dataDir   string
-	testAgent *agent.TestAgent
-}
-
-func NewConsulAgent(t *testing.T, port int) *ConsulAgent {
-	dataDir, _ := ioutil.TempDir("./", "agent")
+func NewConsulAgent(t *testing.T, port int) *agent.TestAgent {
 	hcl := `
 		ports { 
 			http = ` + strconv.Itoa(port) + `
 		}
-		data_dir = "` + dataDir + `"
 	`
-	testAgent := &agent.TestAgent{Name: t.Name(), DataDir: dataDir, HCL: hcl}
-	testAgent.Start(t)
-
-	consulAgent := &ConsulAgent{
-		dataDir:   dataDir,
-		testAgent: testAgent,
-	}
-	return consulAgent
-}
-
-func (consulAgent *ConsulAgent) Close() error {
-	var err error
-
-	err = consulAgent.testAgent.Shutdown()
-	if err != nil {
-		return err
-	}
-	return os.RemoveAll(consulAgent.dataDir)
+	return agent.NewTestAgent(t, hcl)
 }
diff --git a/remoting/consul/test_agent_test.go b/remoting/consul/test_agent_test.go
index 8cf0ac6cd80e517ab7bc1b52cc7774a708082d5e..066e5e3c2dfca1e73451c54dc5519cccdaff6fb2 100644
--- a/remoting/consul/test_agent_test.go
+++ b/remoting/consul/test_agent_test.go
@@ -27,6 +27,6 @@ import (
 
 func TestNewConsulAgent(t *testing.T) {
 	consulAgent := NewConsulAgent(t, 8500)
-	err := consulAgent.Close()
+	err := consulAgent.Shutdown()
 	assert.NoError(t, err)
 }
diff --git a/remoting/etcdv3/client.go b/remoting/etcdv3/client.go
index 7632a7cd042d36db5e02280a14224b83e8736e6c..4e7436e445f652d8fe1db2991c87303653beb9ec 100644
--- a/remoting/etcdv3/client.go
+++ b/remoting/etcdv3/client.go
@@ -35,7 +35,7 @@ import (
 )
 
 const (
-	// ConnDelay connection dalay
+	// ConnDelay connection delay
 	ConnDelay = 3
 	// MaxFailTimes max failure times
 	MaxFailTimes = 15
@@ -93,7 +93,6 @@ func WithHeartbeat(heartbeat int) Option {
 
 // ValidateClient validates client and sets options
 func ValidateClient(container clientFacade, opts ...Option) error {
-
 	options := &Options{
 		heartbeat: 1, // default heartbeat
 	}
@@ -118,7 +117,6 @@ func ValidateClient(container clientFacade, opts ...Option) error {
 
 	// Client lose connection with etcd server
 	if container.Client().rawClient == nil {
-
 		newClient, err := NewClient(options.name, options.endpoints, options.timeout, options.heartbeat)
 		if err != nil {
 			logger.Warnf("new etcd client (name{%s}, etcd addresses{%v}, timeout{%d}) = error{%v}",
@@ -136,7 +134,6 @@ func NewServiceDiscoveryClient(opts ...Option) *Client {
 	options := &Options{
 		heartbeat: 1, // default heartbeat
 	}
-
 	for _, opt := range opts {
 		opt(options)
 	}
@@ -145,9 +142,7 @@ func NewServiceDiscoveryClient(opts ...Option) *Client {
 	if err != nil {
 		logger.Errorf("new etcd client (name{%s}, etcd addresses{%v}, timeout{%d}) = error{%v}",
 			options.name, options.endpoints, options.timeout, err)
-		return nil
 	}
-
 	return newClient
 }
 
@@ -171,7 +166,6 @@ type Client struct {
 
 // nolint
 func NewClient(name string, endpoints []string, timeout time.Duration, heartbeat int) (*Client, error) {
-
 	ctx, cancel := context.WithCancel(context.Background())
 	rawClient, err := clientv3.New(clientv3.Config{
 		Context:     ctx,
@@ -184,7 +178,6 @@ func NewClient(name string, endpoints []string, timeout time.Duration, heartbeat
 	}
 
 	c := &Client{
-
 		name:      name,
 		timeout:   timeout,
 		endpoints: endpoints,
@@ -205,7 +198,6 @@ func NewClient(name string, endpoints []string, timeout time.Duration, heartbeat
 
 // NOTICE: need to get the lock before calling this method
 func (c *Client) clean() {
-
 	// close raw client
 	c.rawClient.Close()
 
@@ -217,7 +209,6 @@ func (c *Client) clean() {
 }
 
 func (c *Client) stop() bool {
-
 	select {
 	case <-c.exit:
 		return true
@@ -229,7 +220,6 @@ func (c *Client) stop() bool {
 
 // nolint
 func (c *Client) Close() {
-
 	if c == nil {
 		return
 	}
@@ -241,15 +231,14 @@ func (c *Client) Close() {
 	c.Wait.Wait()
 
 	c.lock.Lock()
+	defer c.lock.Unlock()
 	if c.rawClient != nil {
 		c.clean()
 	}
-	c.lock.Unlock()
 	logger.Warnf("etcd client{name:%s, endpoints:%s} exit now.", c.name, c.endpoints)
 }
 
 func (c *Client) maintenanceStatus() error {
-
 	s, err := concurrency.NewSession(c.rawClient, concurrency.WithTTL(c.heartbeat))
 	if err != nil {
 		return perrors.WithMessage(err, "new session with server")
@@ -262,7 +251,6 @@ func (c *Client) maintenanceStatus() error {
 }
 
 func (c *Client) maintenanceStatusLoop(s *concurrency.Session) {
-
 	defer func() {
 		c.Wait.Done()
 		logger.Infof("etcd client {endpoints:%v, name:%s} maintenance goroutine game over.", c.endpoints, c.name)
@@ -288,7 +276,6 @@ func (c *Client) maintenanceStatusLoop(s *concurrency.Session) {
 
 // if k not exist will put k/v in etcd, otherwise return nil
 func (c *Client) put(k string, v string, opts ...clientv3.OpOption) error {
-
 	c.lock.RLock()
 	defer c.lock.RUnlock()
 
@@ -300,17 +287,12 @@ func (c *Client) put(k string, v string, opts ...clientv3.OpOption) error {
 		If(clientv3.Compare(clientv3.Version(k), "<", 1)).
 		Then(clientv3.OpPut(k, v, opts...)).
 		Commit()
-	if err != nil {
-		return err
-
-	}
-	return nil
+	return err
 }
 
 // if k not exist will put k/v in etcd
 // if k is already exist in etcd, replace it
 func (c *Client) update(k string, v string, opts ...clientv3.OpOption) error {
-
 	c.lock.RLock()
 	defer c.lock.RUnlock()
 
@@ -322,15 +304,10 @@ func (c *Client) update(k string, v string, opts ...clientv3.OpOption) error {
 		If(clientv3.Compare(clientv3.Version(k), "!=", -1)).
 		Then(clientv3.OpPut(k, v, opts...)).
 		Commit()
-	if err != nil {
-		return err
-
-	}
-	return nil
+	return err
 }
 
 func (c *Client) delete(k string) error {
-
 	c.lock.RLock()
 	defer c.lock.RUnlock()
 
@@ -339,15 +316,10 @@ func (c *Client) delete(k string) error {
 	}
 
 	_, err := c.rawClient.Delete(c.ctx, k)
-	if err != nil {
-		return err
-
-	}
-	return nil
+	return err
 }
 
 func (c *Client) get(k string) (string, error) {
-
 	c.lock.RLock()
 	defer c.lock.RUnlock()
 
@@ -369,7 +341,6 @@ func (c *Client) get(k string) (string, error) {
 
 // nolint
 func (c *Client) CleanKV() error {
-
 	c.lock.RLock()
 	defer c.lock.RUnlock()
 
@@ -378,15 +349,10 @@ func (c *Client) CleanKV() error {
 	}
 
 	_, err := c.rawClient.Delete(c.ctx, "", clientv3.WithPrefix())
-	if err != nil {
-		return err
-	}
-
-	return nil
+	return err
 }
 
 func (c *Client) getChildren(k string) ([]string, []string, error) {
-
 	c.lock.RLock()
 	defer c.lock.RUnlock()
 
@@ -403,21 +369,16 @@ func (c *Client) getChildren(k string) ([]string, []string, error) {
 		return nil, nil, ErrKVPairNotFound
 	}
 
-	var (
-		kList []string
-		vList []string
-	)
-
+	kList := make([]string, 0, len(resp.Kvs))
+	vList := make([]string, 0, len(resp.Kvs))
 	for _, kv := range resp.Kvs {
 		kList = append(kList, string(kv.Key))
 		vList = append(vList, string(kv.Value))
 	}
-
 	return kList, vList, nil
 }
 
 func (c *Client) watchWithPrefix(prefix string) (clientv3.WatchChan, error) {
-
 	c.lock.RLock()
 	defer c.lock.RUnlock()
 
@@ -429,7 +390,6 @@ func (c *Client) watchWithPrefix(prefix string) (clientv3.WatchChan, error) {
 }
 
 func (c *Client) watch(k string) (clientv3.WatchChan, error) {
-
 	c.lock.RLock()
 	defer c.lock.RUnlock()
 
@@ -441,7 +401,6 @@ func (c *Client) watch(k string) (clientv3.WatchChan, error) {
 }
 
 func (c *Client) keepAliveKV(k string, v string) error {
-
 	c.lock.RLock()
 	defer c.lock.RUnlock()
 
@@ -457,14 +416,15 @@ func (c *Client) keepAliveKV(k string, v string) error {
 	keepAlive, err := c.rawClient.KeepAlive(c.ctx, lease.ID)
 	if err != nil || keepAlive == nil {
 		c.rawClient.Revoke(c.ctx, lease.ID)
-		return perrors.WithMessage(err, "keep alive lease")
+		if err != nil {
+			return perrors.WithMessage(err, "keep alive lease")
+		} else {
+			return perrors.New("keep alive lease")
+		}
 	}
 
 	_, err = c.rawClient.Put(c.ctx, k, v, clientv3.WithLease(lease.ID))
-	if err != nil {
-		return perrors.WithMessage(err, "put k/v with lease")
-	}
-	return nil
+	return perrors.WithMessage(err, "put k/v with lease")
 }
 
 // nolint
@@ -481,92 +441,54 @@ func (c *Client) Valid() bool {
 	}
 
 	c.lock.RLock()
-	if c.rawClient == nil {
-		c.lock.RUnlock()
-		return false
-	}
-	c.lock.RUnlock()
-	return true
+	defer c.lock.RUnlock()
+	return c.rawClient != nil
 }
 
 // nolint
 func (c *Client) Create(k string, v string) error {
-
 	err := c.put(k, v)
-	if err != nil {
-		return perrors.WithMessagef(err, "put k/v (key: %s value %s)", k, v)
-	}
-	return nil
+	return perrors.WithMessagef(err, "put k/v (key: %s value %s)", k, v)
 }
 
 // Update key value ...
 func (c *Client) Update(k, v string) error {
 	err := c.update(k, v)
-	if err != nil {
-		return perrors.WithMessagef(err, "Update k/v (key: %s value %s)", k, v)
-	}
-	return nil
+	return perrors.WithMessagef(err, "Update k/v (key: %s value %s)", k, v)
 }
 
 // nolint
 func (c *Client) Delete(k string) error {
-
 	err := c.delete(k)
-	if err != nil {
-		return perrors.WithMessagef(err, "delete k/v (key %s)", k)
-	}
-
-	return nil
+	return perrors.WithMessagef(err, "delete k/v (key %s)", k)
 }
 
 // RegisterTemp registers a temporary node
 func (c *Client) RegisterTemp(k, v string) error {
-
 	err := c.keepAliveKV(k, v)
-	if err != nil {
-		return perrors.WithMessagef(err, "keepalive kv (key %s)", k)
-	}
-
-	return nil
+	return perrors.WithMessagef(err, "keepalive kv (key %s)", k)
 }
 
 // GetChildrenKVList gets children kv list by @k
 func (c *Client) GetChildrenKVList(k string) ([]string, []string, error) {
-
 	kList, vList, err := c.getChildren(k)
-	if err != nil {
-		return nil, nil, perrors.WithMessagef(err, "get key children (key %s)", k)
-	}
-	return kList, vList, nil
+	return kList, vList, perrors.WithMessagef(err, "get key children (key %s)", k)
 }
 
 // Get gets value by @k
 func (c *Client) Get(k string) (string, error) {
-
 	v, err := c.get(k)
-	if err != nil {
-		return "", perrors.WithMessagef(err, "get key value (key %s)", k)
-	}
-
-	return v, nil
+	return v, perrors.WithMessagef(err, "get key value (key %s)", k)
 }
 
 // Watch watches on spec key
 func (c *Client) Watch(k string) (clientv3.WatchChan, error) {
-
 	wc, err := c.watch(k)
-	if err != nil {
-		return nil, perrors.WithMessagef(err, "watch prefix (key %s)", k)
-	}
-	return wc, nil
+	return wc, perrors.WithMessagef(err, "watch prefix (key %s)", k)
 }
 
 // WatchWithPrefix watches on spec prefix
 func (c *Client) WatchWithPrefix(prefix string) (clientv3.WatchChan, error) {
-
 	wc, err := c.watchWithPrefix(prefix)
-	if err != nil {
-		return nil, perrors.WithMessagef(err, "watch prefix (key %s)", prefix)
-	}
-	return wc, nil
+	return wc, perrors.WithMessagef(err, "watch prefix (key %s)", prefix)
 }
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/kubernetes/listener_test.go b/remoting/kubernetes/listener_test.go
index 1f398485b2f16defddf44ce1a08a7ecfd9760dd1..0b05b6e6e09501dfd25164e865a3eb6eef91dc9f 100644
--- a/remoting/kubernetes/listener_test.go
+++ b/remoting/kubernetes/listener_test.go
@@ -19,6 +19,7 @@ package kubernetes
 
 import (
 	"testing"
+	"time"
 )
 
 import (
@@ -87,6 +88,7 @@ func TestListener(t *testing.T) {
 	listener := NewEventListener(c)
 	dataListener := &mockDataListener{client: c, changedData: changedData, rc: make(chan remoting.Event)}
 	listener.ListenServiceEvent("/dubbo", dataListener)
+	time.Sleep(1e9)
 
 	for _, tc := range tests {
 
diff --git a/remoting/kubernetes/registry_controller.go b/remoting/kubernetes/registry_controller.go
index f93a00a6f2df6022d0436f56e8c719f108be66f3..20be0d72ec1ce4c379f44d3218ffbd0cfd3d2a63 100644
--- a/remoting/kubernetes/registry_controller.go
+++ b/remoting/kubernetes/registry_controller.go
@@ -567,7 +567,7 @@ func (c *dubboRegistryController) addAnnotationForCurrentPod(k string, v string)
 	c.lock.Lock()
 	defer c.lock.Unlock()
 
-	// 1. accord old pod && (k, v) assemble new pod dubbo annotion v
+	// 1. accord old pod && (k, v) assemble new pod dubbo annotation v
 	// 2. get patch data
 	// 3. PATCH the pod
 	currentPod, err := c.readCurrentPod()
diff --git a/remoting/listener.go b/remoting/listener.go
index 6cbb883181ff8ec1c9124f8d8cc3d7ec0920abd9..eb27c71dfd64a1063927663a9817f8f23b85dd20 100644
--- a/remoting/listener.go
+++ b/remoting/listener.go
@@ -48,6 +48,7 @@ var serviceEventTypeStrings = [...]string{
 	"update",
 }
 
+// nolint
 func (t EventType) String() string {
 	return serviceEventTypeStrings[t]
 }
@@ -63,6 +64,7 @@ type Event struct {
 	Content string
 }
 
+// nolint
 func (e Event) String() string {
 	return fmt.Sprintf("Event{Action{%s}, Content{%s}}", e.Action, e.Content)
 }
diff --git a/remoting/zookeeper/client.go b/remoting/zookeeper/client.go
index 4ca34a6aeccf7b588a96edb44b4d5913a3e0fd8e..fbd90762eb34f361a38486ef2d8f5f10699a96f7 100644
--- a/remoting/zookeeper/client.go
+++ b/remoting/zookeeper/client.go
@@ -190,7 +190,7 @@ func NewZookeeperClient(name string, zkAddrs []string, timeout time.Duration) (*
 	return z, nil
 }
 
-// WithTestCluster sets test cluser for zk client
+// WithTestCluster sets test cluster for zk client
 func WithTestCluster(ts *zk.TestCluster) Option {
 	return func(opt *Options) {
 		opt.ts = ts
@@ -363,14 +363,9 @@ func (z *ZookeeperClient) ZkConnValid() bool {
 	default:
 	}
 
-	valid := true
 	z.RLock()
-	if z.Conn == nil {
-		valid = false
-	}
-	z.RUnlock()
-
-	return valid
+	defer z.RUnlock()
+	return z.Conn != nil
 }
 
 // nolint
diff --git a/remoting/zookeeper/curator_discovery/service_discovery.go b/remoting/zookeeper/curator_discovery/service_discovery.go
index 9566b5494389325520b4eb6a8eb170e0b305bb47..acd43c0b92bd6220efc6527efc1748ed3021f7ac 100644
--- a/remoting/zookeeper/curator_discovery/service_discovery.go
+++ b/remoting/zookeeper/curator_discovery/service_discovery.go
@@ -154,7 +154,6 @@ func (sd *ServiceDiscovery) updateInternalService(name, id string) {
 		return
 	}
 	entry.instance = instance
-	return
 }
 
 // UnregisterService un-register service in zookeeper and delete service in cache
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 9a4874db24696d90e4fcc7d9d987f5888f1be599..b6c6d78106a5a97ec94e621d2e664185a8216656 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"
 )
@@ -37,6 +37,10 @@ import (
 	"github.com/apache/dubbo-go/remoting"
 )
 
+var (
+	defaultTTL = 15 * time.Minute
+)
+
 // nolint
 type ZkEventListener struct {
 	client      *ZookeeperClient
@@ -197,10 +201,20 @@ func (l *ZkEventListener) listenDirEvent(conf *common.URL, zkPath string, listen
 
 	var (
 		failTimes int
+		ttl       time.Duration
 		event     chan struct{}
 		zkEvent   zk.Event
 	)
 	event = make(chan struct{}, 4)
+	ttl = defaultTTL
+	if conf != nil {
+		timeout, err := time.ParseDuration(conf.GetParam(constant.REGISTRY_TTL_KEY, constant.DEFAULT_REG_TTL))
+		if err == nil {
+			ttl = timeout
+		} else {
+			logger.Warnf("wrong configuration for registry ttl, error:=%+v, using default value %v instead", err, defaultTTL)
+		}
+	}
 	defer close(event)
 	for {
 		// get current children for a zkPath
@@ -302,18 +316,29 @@ func (l *ZkEventListener) listenDirEvent(conf *common.URL, zkPath string, listen
 				}(dubboPath, listener)
 			}
 		}
-		select {
-		case zkEvent = <-childEventCh:
-			logger.Warnf("get a zookeeper zkEvent{type:%s, server:%s, path:%s, state:%d-%s, err:%s}",
-				zkEvent.Type.String(), zkEvent.Server, zkEvent.Path, zkEvent.State, StateToString(zkEvent.State), zkEvent.Err)
-			if zkEvent.Type != zk.EventNodeChildrenChanged {
-				continue
+		// Periodically update provider information
+		ticker := time.NewTicker(ttl)
+	WATCH:
+		for {
+			select {
+			case <-ticker.C:
+				l.handleZkNodeEvent(zkPath, children, listener)
+			case zkEvent = <-childEventCh:
+				logger.Warnf("get a zookeeper zkEvent{type:%s, server:%s, path:%s, state:%d-%s, err:%s}",
+					zkEvent.Type.String(), zkEvent.Server, zkEvent.Path, zkEvent.State, StateToString(zkEvent.State), zkEvent.Err)
+				ticker.Stop()
+				if zkEvent.Type != zk.EventNodeChildrenChanged {
+					break WATCH
+				}
+				l.handleZkNodeEvent(zkEvent.Path, children, listener)
+				break WATCH
+			case <-l.client.Done():
+				logger.Warnf("client.done(), listen(path{%s}) goroutine exit now...", zkPath)
+				ticker.Stop()
+				return
 			}
-			l.handleZkNodeEvent(zkEvent.Path, children, listener)
-		case <-l.client.Done():
-			logger.Warnf("client.done(), listen(path{%s}) goroutine exit now...", zkPath)
-			return
 		}
+
 	}
 }
 
diff --git a/test/integrate/dubbo/go-client/go.mod b/test/integrate/dubbo/go-client/go.mod
index 4708eb1f0f48c10acc254880ecb6dad3a03529f2..b0be45ae9c820dc200a1e642a8c73535b13e9f4d 100644
--- a/test/integrate/dubbo/go-client/go.mod
+++ b/test/integrate/dubbo/go-client/go.mod
@@ -1,3 +1,7 @@
 module github.com/apache/dubbo-go/test/integrate/dubbo/go-client
 
+require (
+	github.com/apache/dubbo-go-hessian2 v1.6.0-rc1.0.20200906044240-6c1fb5c3bd44
+)
+
 go 1.13
diff --git a/test/integrate/dubbo/go-client/log.yml b/test/integrate/dubbo/go-client/log.yml
index 59fa4279ad85272c4c49d532beaf23b74d00f58a..21f97bcbc40218ab23a6fc022b148d321cbe23cc 100644
--- a/test/integrate/dubbo/go-client/log.yml
+++ b/test/integrate/dubbo/go-client/log.yml
@@ -1,6 +1,5 @@
-
 level: "debug"
-development: true
+development: false
 disableCaller: false
 disableStacktrace: false
 sampling:
diff --git a/test/integrate/dubbo/go-server/go.mod b/test/integrate/dubbo/go-server/go.mod
index 9e1162327de374fb131c2a0b89d1be3baa578a1b..6c530f6a5973c379e98d9c2cca5d7511eabab830 100644
--- a/test/integrate/dubbo/go-server/go.mod
+++ b/test/integrate/dubbo/go-server/go.mod
@@ -1,3 +1,7 @@
 module github.com/apache/dubbo-go/test/integrate/dubbo/go-server
 
+require (
+	github.com/apache/dubbo-go-hessian2 v1.6.0-rc1.0.20200906044240-6c1fb5c3bd44
+)
+
 go 1.13
diff --git a/test/integrate/dubbo/go-server/log.yml b/test/integrate/dubbo/go-server/log.yml
index 59fa4279ad85272c4c49d532beaf23b74d00f58a..21f97bcbc40218ab23a6fc022b148d321cbe23cc 100644
--- a/test/integrate/dubbo/go-server/log.yml
+++ b/test/integrate/dubbo/go-server/log.yml
@@ -1,6 +1,5 @@
-
 level: "debug"
-development: true
+development: false
 disableCaller: false
 disableStacktrace: false
 sampling:
diff --git a/test/integrate/dubbo/go-server/server.go b/test/integrate/dubbo/go-server/server.go
index 115bf0a4d78f171eb7f786808def91879ed93947..a5d18dba3c14a1219dcd0952e191395a27e5b9e7 100644
--- a/test/integrate/dubbo/go-server/server.go
+++ b/test/integrate/dubbo/go-server/server.go
@@ -48,7 +48,7 @@ func main() {
 	select {
 	case <-stopC:
 		// wait getty send resp to consumer
-		time.Sleep(3*time.Second)
+		time.Sleep(3 * time.Second)
 		return
 	case <-time.After(time.Minute):
 		panic("provider already running 1 min, but can't be call by consumer")