diff --git a/.travis.yml b/.travis.yml
index ba30c9d01036206a0d9b1239b7bb71976b23fbca..1b46f5d872932b7ed307ceaf802c95997b2800e6 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -13,6 +13,9 @@ install: true
 
 script:
   - go fmt ./... && [[ -z `git status -s` ]]
+  - sh before_validate_license.sh
+  - chmod u+x /tmp/tools/license/license-header-checker
+  - /tmp/tools/license/license-header-checker -v -a -r -i vendor  /tmp/tools/license/license.txt . go  && [[ -z `git status -s` ]]
   - chmod u+x before_ut.sh && ./before_ut.sh
   - go mod vendor && go test ./... -coverprofile=coverage.txt -covermode=atomic
 
@@ -20,4 +23,4 @@ after_success:
   - bash <(curl -s https://codecov.io/bash)
 
 notifications:
-  webhooks: https://oapi.dingtalk.com/robot/send?access_token=f5d6237f2c79db584e75604f7f88db1ce1673c8c0e98451217b28fde791e1d4f
\ No newline at end of file
+  webhooks: https://oapi.dingtalk.com/robot/send?access_token=f5d6237f2c79db584e75604f7f88db1ce1673c8c0e98451217b28fde791e1d4f
diff --git a/README.md b/README.md
index b3e7173c43fc4fd3d278e0d2a9ea7dab55837949..9bade617c8b05ec52c2018cf231ae036a7ae91d3 100644
--- a/README.md
+++ b/README.md
@@ -30,7 +30,7 @@ Apache License, Version 2.0
 
 Both extension module and layered project architecture is according to Apache Dubbo (including protocol layer, registry layer, cluster layer, config layer and so on), the advantage of this arch is as following: you can implement these layered interfaces in your own way, override the default implementation of dubbo-go by calling 'extension.SetXXX' of extension, complete your special needs without modifying the source code. At the same time, you are welcome to contribute implementation of useful extension to the community.
 
-![frame design](https://raw.githubusercontent.com/wiki/dubbo/dubbo-go/dubbo-go%E4%BB%A3%E7%A0%81%E5%88%86%E5%B1%82%E8%AE%BE%E8%AE%A1.png)
+![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)
 
diff --git a/README_CN.md b/README_CN.md
index 5c40249ed26ee36bc16bdd65c970e9f61a6caadb..180759f36663a587ee02232e229ae7c3ebbb06c1 100644
--- a/README_CN.md
+++ b/README_CN.md
@@ -29,7 +29,7 @@ Apache License, Version 2.0
 
 基于dubbo的extension模块和分层的代码设计(包括 protocol layer, registry layer, cluster layer, config 等等)。我们的目标是:你可以对这些分层接口进行新的实现,并通过调用 extension 模块的“ extension.SetXXX ”方法来覆盖 dubbo-go [同 go-for-apache-dubbo ]的默认实现,以完成自己的特殊需求而无需修改源代码。同时,欢迎你为社区贡献有用的拓展实现。
 
-![框架设计](https://raw.githubusercontent.com/wiki/dubbo/dubbo-go/dubbo-go%E4%BB%A3%E7%A0%81%E5%88%86%E5%B1%82%E8%AE%BE%E8%AE%A1.png)
+![dubbo go extend](./doc/pic/arch/dubbo-go-ext.png)
 
 关于详细设计请阅读 [code layered design](https://github.com/apache/dubbo-go/wiki/dubbo-go-V1.0-design)
 
@@ -108,6 +108,7 @@ Apache License, Version 2.0
     * [For dubbo](https://github.com/apache/dubbo-go/pull/344)
     * [For grpc](https://github.com/apache/dubbo-go/pull/397)
 
+
 - 其他功能支持:
     * 启动时检查
     * 服务直连
diff --git a/before_validate_license.sh b/before_validate_license.sh
new file mode 100644
index 0000000000000000000000000000000000000000..8fa6e381c7a4cd44835d107ba9213f685f899a10
--- /dev/null
+++ b/before_validate_license.sh
@@ -0,0 +1,26 @@
+#
+#  Licensed to the Apache Software Foundation (ASF) under one or more
+#  contributor license agreements.  See the NOTICE file distributed with
+#  this work for additional information regarding copyright ownership.
+#  The ASF licenses this file to You under the Apache License, Version 2.0
+#  (the "License"); you may not use this file except in compliance with
+#  the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+
+remoteLicenseCheckerPath="https://github.com/dubbogo/resources/raw/master/tools/license"
+remoteLicenseCheckerName="license-header-checker"
+remoteLicenseCheckerURL="${remoteLicenseCheckerPath}/${remoteLicenseCheckerName}"
+remoteLicenseName="license.txt"
+remoteLicenseURL="${remoteLicenseCheckerPath}/${remoteLicenseName}"
+
+licensePath="/tmp/tools/license"
+mkdir -p ${licensePath}
+wget -P "${licensePath}" ${remoteLicenseCheckerURL}
+wget -P "${licensePath}" ${remoteLicenseURL}
diff --git a/cluster/router/condition/app_router_test.go b/cluster/router/condition/app_router_test.go
index bd817af36c8c144295479fb07ada9411f4115bbc..e99307625baf34fa6b744f168ff4e6cb8e042502 100644
--- a/cluster/router/condition/app_router_test.go
+++ b/cluster/router/condition/app_router_test.go
@@ -113,7 +113,7 @@ conditions:
 	assert.Nil(t, err)
 	assert.NotNil(t, appRouter)
 
-	rule, err := Parse(testYML)
+	rule, err := getRule(testYML)
 	assert.Nil(t, err)
 	appRouter.generateConditions(rule)
 
diff --git a/cluster/router/condition/file.go b/cluster/router/condition/file.go
index efeec53efc840d93c4b6906adfd19820a57b36fd..b2c876690043d18a1a9e746fee13f06c77a0de03 100644
--- a/cluster/router/condition/file.go
+++ b/cluster/router/condition/file.go
@@ -44,7 +44,7 @@ type FileConditionRouter struct {
 // NewFileConditionRouter Create file condition router instance with content ( from config file)
 func NewFileConditionRouter(content []byte) (*FileConditionRouter, error) {
 	fileRouter := &FileConditionRouter{}
-	rule, err := Parse(string(content))
+	rule, err := getRule(string(content))
 	if err != nil {
 		return nil, perrors.Errorf("yaml.Unmarshal() failed , error:%v", perrors.WithStack(err))
 	}
diff --git a/cluster/router/condition/listenable_router.go b/cluster/router/condition/listenable_router.go
index ba2fbb0eb2f482dfde215c1b078ecad60e66bc14..4ccc19e95521d03ae1f663ec276646cf30926533 100644
--- a/cluster/router/condition/listenable_router.go
+++ b/cluster/router/condition/listenable_router.go
@@ -102,7 +102,7 @@ func (l *listenableRouter) Process(event *config_center.ConfigChangeEvent) {
 		return
 	}
 
-	routerRule, err := Parse(content)
+	routerRule, err := getRule(content)
 	if err != nil {
 		logger.Errorf("Parse condition router rule fail,error:[%s] ", err)
 		return
diff --git a/cluster/router/condition/router.go b/cluster/router/condition/router.go
index c5d46444bde921386d14a8be7eb0a89d855f8ece..0267a3c7a462acb43f84ccb4701247147699804a 100644
--- a/cluster/router/condition/router.go
+++ b/cluster/router/condition/router.go
@@ -27,7 +27,6 @@ import (
 )
 
 import (
-	matcher "github.com/apache/dubbo-go/cluster/router/match"
 	"github.com/apache/dubbo-go/common"
 	"github.com/apache/dubbo-go/common/constant"
 	"github.com/apache/dubbo-go/common/logger"
@@ -301,7 +300,7 @@ func (pair MatchPair) isMatch(value string, param *common.URL) bool {
 	if !pair.Matches.Empty() && pair.Mismatches.Empty() {
 
 		for match := range pair.Matches.Items {
-			if matcher.IsMatchGlobalPattern(match.(string), value, param) {
+			if isMatchGlobalPattern(match.(string), value, param) {
 				return true
 			}
 		}
@@ -310,7 +309,7 @@ func (pair MatchPair) isMatch(value string, param *common.URL) bool {
 	if !pair.Mismatches.Empty() && pair.Matches.Empty() {
 
 		for mismatch := range pair.Mismatches.Items {
-			if matcher.IsMatchGlobalPattern(mismatch.(string), value, param) {
+			if isMatchGlobalPattern(mismatch.(string), value, param) {
 				return false
 			}
 		}
@@ -319,12 +318,12 @@ func (pair MatchPair) isMatch(value string, param *common.URL) bool {
 	if !pair.Mismatches.Empty() && !pair.Matches.Empty() {
 		//when both mismatches and matches contain the same value, then using mismatches first
 		for mismatch := range pair.Mismatches.Items {
-			if matcher.IsMatchGlobalPattern(mismatch.(string), value, param) {
+			if isMatchGlobalPattern(mismatch.(string), value, param) {
 				return false
 			}
 		}
 		for match := range pair.Matches.Items {
-			if matcher.IsMatchGlobalPattern(match.(string), value, param) {
+			if isMatchGlobalPattern(match.(string), value, param) {
 				return true
 			}
 		}
diff --git a/cluster/router/condition/router_rule.go b/cluster/router/condition/router_rule.go
index 1374cf9de2585f78a27e3de99f356c6900268927..ce397d6cc0f51519123dd427709e8dba42d72a20 100644
--- a/cluster/router/condition/router_rule.go
+++ b/cluster/router/condition/router_rule.go
@@ -18,11 +18,17 @@
 package condition
 
 import (
-	"gopkg.in/yaml.v2"
+	"strings"
+)
+
+import (
+	gxstrings "github.com/dubbogo/gost/strings"
 )
 
 import (
 	"github.com/apache/dubbo-go/cluster/router"
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/common/yaml"
 )
 
 // RouterRule RouterRule config read from config file or config center
@@ -44,9 +50,9 @@ type RouterRule struct {
  *     =>
  *     1.1.1.1
  */
-func Parse(rawRule string) (*RouterRule, error) {
+func getRule(rawRule string) (*RouterRule, error) {
 	r := &RouterRule{}
-	err := yaml.Unmarshal([]byte(rawRule), r)
+	err := yaml.UnmarshalYML([]byte(rawRule), r)
 	if err != nil {
 		return r, err
 	}
@@ -57,3 +63,11 @@ func Parse(rawRule string) (*RouterRule, error) {
 
 	return r, nil
 }
+
+// isMatchGlobalPattern Match value to param content by pattern
+func isMatchGlobalPattern(pattern string, value string, param *common.URL) bool {
+	if param != nil && strings.HasPrefix(pattern, "$") {
+		pattern = param.GetRawParam(pattern[1:])
+	}
+	return gxstrings.IsMatchPattern(pattern, value)
+}
diff --git a/cluster/router/condition/router_rule_test.go b/cluster/router/condition/router_rule_test.go
index 5acc7283917a7aa662b60cd90daba89d312db0cd..675acaec912b413d8fa3d1a25463b1fd4813a7f5 100644
--- a/cluster/router/condition/router_rule_test.go
+++ b/cluster/router/condition/router_rule_test.go
@@ -20,11 +20,16 @@ package condition
 import (
 	"testing"
 )
+
 import (
 	"github.com/stretchr/testify/assert"
 )
 
-func TestParse(t *testing.T) {
+import (
+	"github.com/apache/dubbo-go/common"
+)
+
+func TestGetRule(t *testing.T) {
 	testyml := `
 scope: application
 runtime: true
@@ -36,7 +41,7 @@ conditions:
     ip=127.0.0.1
     =>
     1.1.1.1`
-	rule, e := Parse(testyml)
+	rule, e := getRule(testyml)
 
 	assert.Nil(t, e)
 	assert.NotNil(t, rule)
@@ -50,3 +55,8 @@ conditions:
 	assert.Equal(t, false, rule.Dynamic)
 	assert.Equal(t, "", rule.Key)
 }
+
+func TestIsMatchGlobPattern(t *testing.T) {
+	url, _ := common.NewURL("dubbo://localhost:8080/Foo?key=v*e")
+	assert.Equal(t, true, isMatchGlobalPattern("$key", "value", &url))
+}
diff --git a/cluster/router/match/match_utils.go b/cluster/router/match/match_utils.go
deleted file mode 100644
index 28fe7151c5126c41fbadf9f4d54da2b9df74a7fe..0000000000000000000000000000000000000000
--- a/cluster/router/match/match_utils.go
+++ /dev/null
@@ -1,63 +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 match
-
-import (
-	"strings"
-)
-
-import (
-	"github.com/apache/dubbo-go/common"
-)
-
-// IsMatchGlobalPattern Match value to param content by pattern
-func IsMatchGlobalPattern(pattern string, value string, param *common.URL) bool {
-	if param != nil && strings.HasPrefix(pattern, "$") {
-		pattern = param.GetRawParam(pattern[1:])
-	}
-	return isMatchInternalPattern(pattern, value)
-}
-
-func isMatchInternalPattern(pattern string, value string) bool {
-	if "*" == pattern {
-		return true
-	}
-	if len(pattern) == 0 && len(value) == 0 {
-		return true
-	}
-	if len(pattern) == 0 || len(value) == 0 {
-		return false
-	}
-	i := strings.LastIndex(pattern, "*")
-	switch i {
-	case -1:
-		// doesn't find "*"
-		return value == pattern
-	case len(pattern) - 1:
-		// "*" is at the end
-		return strings.HasPrefix(value, pattern[0:i])
-	case 0:
-		// "*" is at the beginning
-		return strings.HasSuffix(value, pattern[i+1:])
-	default:
-		// "*" is in the middle
-		prefix := pattern[0:1]
-		suffix := pattern[i+1:]
-		return strings.HasPrefix(value, prefix) && strings.HasSuffix(value, suffix)
-	}
-}
diff --git a/cluster/router/router.go b/cluster/router/router.go
index a28002a09e3b7217549b896d452f70997504ac8f..9ee1154437e6fd205f08098deabb1ca260c3c040 100644
--- a/cluster/router/router.go
+++ b/cluster/router/router.go
@@ -31,7 +31,7 @@ type RouterFactory interface {
 }
 
 // RouterFactory Router create factory use for parse config file
-type FIleRouterFactory interface {
+type FileRouterFactory interface {
 	// NewFileRouters Create file router with config file
 	NewFileRouter([]byte) (Router, error)
 }
diff --git a/cluster/router/tag/factory.go b/cluster/router/tag/factory.go
new file mode 100644
index 0000000000000000000000000000000000000000..d74924c89862ae4f4cd85b59c7008880298c0c99
--- /dev/null
+++ b/cluster/router/tag/factory.go
@@ -0,0 +1,47 @@
+/*
+ * 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 tag
+
+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"
+)
+
+func init() {
+	extension.SetRouterFactory(constant.TagRouterName, NewTagRouterFactory)
+}
+
+type tagRouterFactory struct{}
+
+// NewTagRouterFactory create a tagRouterFactory
+func NewTagRouterFactory() router.RouterFactory {
+	return &tagRouterFactory{}
+}
+
+// NewRouter create a tagRouter by tagRouterFactory with a url
+// The url contains router configuration information
+func (c *tagRouterFactory) NewRouter(url *common.URL) (router.Router, error) {
+	return NewTagRouter(url)
+}
+
+// NewFileRouter create a tagRouter by profile content
+func (c *tagRouterFactory) NewFileRouter(content []byte) (router.Router, error) {
+	return NewFileTagRouter(content)
+}
diff --git a/cluster/router/match/match_utils_test.go b/cluster/router/tag/factory_test.go
similarity index 55%
rename from cluster/router/match/match_utils_test.go
rename to cluster/router/tag/factory_test.go
index f16480f1d3b7dd5ca820c81d5d04d837c129687f..58bff5b18113d69f97ec513e393aa6759a3cf050 100644
--- a/cluster/router/match/match_utils_test.go
+++ b/cluster/router/tag/factory_test.go
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package match
+package tag
 
 import (
 	"testing"
@@ -29,18 +29,11 @@ import (
 	"github.com/apache/dubbo-go/common"
 )
 
-func TestIsMatchInternalPattern(t *testing.T) {
-	assert.Equal(t, true, isMatchInternalPattern("*", "value"))
-	assert.Equal(t, true, isMatchInternalPattern("", ""))
-	assert.Equal(t, false, isMatchInternalPattern("", "value"))
-	assert.Equal(t, true, isMatchInternalPattern("value", "value"))
-	assert.Equal(t, true, isMatchInternalPattern("v*", "value"))
-	assert.Equal(t, true, isMatchInternalPattern("*ue", "value"))
-	assert.Equal(t, true, isMatchInternalPattern("*e", "value"))
-	assert.Equal(t, true, isMatchInternalPattern("v*e", "value"))
-}
-
-func TestIsMatchGlobPattern(t *testing.T) {
-	url, _ := common.NewURL("dubbo://localhost:8080/Foo?key=v*e")
-	assert.Equal(t, true, IsMatchGlobalPattern("$key", "value", &url))
+func TestTagRouterFactory_NewRouter(t *testing.T) {
+	u1, err := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=&version=2.6.0&enabled=true")
+	assert.Nil(t, err)
+	factory := NewTagRouterFactory()
+	tagRouter, e := factory.NewRouter(&u1)
+	assert.Nil(t, e)
+	assert.NotNil(t, tagRouter)
 }
diff --git a/cluster/router/tag/file.go b/cluster/router/tag/file.go
new file mode 100644
index 0000000000000000000000000000000000000000..8144c83203dbe98778dd6bb8dcdb9888be664b3b
--- /dev/null
+++ b/cluster/router/tag/file.go
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package tag
+
+import (
+	"net/url"
+	"strconv"
+	"sync"
+)
+
+import (
+	perrors "github.com/pkg/errors"
+)
+
+import (
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/common/constant"
+	"github.com/apache/dubbo-go/protocol"
+)
+
+// FileTagRouter Use for parse config file of Tag router
+type FileTagRouter struct {
+	parseOnce  sync.Once
+	router     *tagRouter
+	routerRule *RouterRule
+	url        *common.URL
+	force      bool
+}
+
+// NewFileTagRouter Create file tag router instance with content ( from config file)
+func NewFileTagRouter(content []byte) (*FileTagRouter, error) {
+	fileRouter := &FileTagRouter{}
+	rule, err := getRule(string(content))
+	if err != nil {
+		return nil, perrors.Errorf("yaml.Unmarshal() failed , error:%v", perrors.WithStack(err))
+	}
+	fileRouter.routerRule = rule
+	url := fileRouter.URL()
+	fileRouter.router, err = NewTagRouter(&url)
+	return fileRouter, err
+}
+
+// URL Return URL in file tag router n
+func (f *FileTagRouter) URL() common.URL {
+	f.parseOnce.Do(func() {
+		routerRule := f.routerRule
+		f.url = common.NewURLWithOptions(
+			common.WithProtocol(constant.TAG_ROUTE_PROTOCOL),
+			common.WithParams(url.Values{}),
+			common.WithParamsValue(constant.ForceUseTag, strconv.FormatBool(routerRule.Force)),
+			common.WithParamsValue(constant.RouterPriority, strconv.Itoa(routerRule.Priority)),
+			common.WithParamsValue(constant.ROUTER_KEY, constant.TAG_ROUTE_PROTOCOL))
+	})
+	return *f.url
+}
+
+// Priority Return Priority in listenable router
+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 {
+		return invokers
+	}
+	return f.Route(invokers, url, invocation)
+}
diff --git a/cluster/router/tag/file_test.go b/cluster/router/tag/file_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..94fcf9e0e0fabed2445417d14b711f91b65b9e5e
--- /dev/null
+++ b/cluster/router/tag/file_test.go
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package tag
+
+import (
+	"testing"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+import (
+	"github.com/apache/dubbo-go/common/constant"
+)
+
+func TestNewFileTagRouter(t *testing.T) {
+	router, e := NewFileTagRouter([]byte(`priority: 100
+force: true`))
+	assert.Nil(t, e)
+	assert.NotNil(t, router)
+	assert.Equal(t, 100, router.routerRule.Priority)
+	assert.Equal(t, true, router.routerRule.Force)
+}
+
+func TestFileTagRouter_URL(t *testing.T) {
+	router, e := NewFileTagRouter([]byte(`priority: 100
+force: true`))
+	assert.Nil(t, e)
+	assert.NotNil(t, router)
+	url := router.URL()
+	assert.NotNil(t, url)
+	force := url.GetParam(constant.ForceUseTag, "false")
+	priority := url.GetParam(constant.RouterPriority, "0")
+	assert.Equal(t, "true", force)
+	assert.Equal(t, "100", priority)
+
+}
+
+func TestFileTagRouter_Priority(t *testing.T) {
+	router, e := NewFileTagRouter([]byte(`priority: 100
+force: true`))
+	assert.Nil(t, e)
+	assert.NotNil(t, router)
+	priority := router.Priority()
+	assert.Equal(t, int64(100), priority)
+}
diff --git a/cluster/router/tag/router_rule.go b/cluster/router/tag/router_rule.go
new file mode 100644
index 0000000000000000000000000000000000000000..926446dcb2f18fa2fd4639a9246a85f435d75d45
--- /dev/null
+++ b/cluster/router/tag/router_rule.go
@@ -0,0 +1,38 @@
+/*
+ * 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 tag
+
+import (
+	"github.com/apache/dubbo-go/cluster/router"
+	"github.com/apache/dubbo-go/common/yaml"
+)
+
+// RouterRule RouterRule config read from config file or config center
+type RouterRule struct {
+	router.BaseRouterRule `yaml:",inline""`
+}
+
+func getRule(rawRule string) (*RouterRule, error) {
+	r := &RouterRule{}
+	err := yaml.UnmarshalYML([]byte(rawRule), r)
+	if err != nil {
+		return r, err
+	}
+	r.RawRule = rawRule
+	return r, nil
+}
diff --git a/cluster/router/tag/router_rule_test.go b/cluster/router/tag/router_rule_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..2df65193f9d0cf607258f3080e22b42cd6e9b16a
--- /dev/null
+++ b/cluster/router/tag/router_rule_test.go
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package tag
+
+import (
+	"testing"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+func TestGetRule(t *testing.T) {
+	yml := `
+scope: application
+runtime: true
+force: true
+`
+	rule, e := getRule(yml)
+	assert.Nil(t, e)
+	assert.NotNil(t, rule)
+	assert.Equal(t, true, rule.Force)
+	assert.Equal(t, true, rule.Runtime)
+	assert.Equal(t, "application", rule.Scope)
+}
diff --git a/cluster/router/tag/tag_router.go b/cluster/router/tag/tag_router.go
new file mode 100644
index 0000000000000000000000000000000000000000..87da418943e90c63a905f35260ada7880d6f51b9
--- /dev/null
+++ b/cluster/router/tag/tag_router.go
@@ -0,0 +1,94 @@
+/*
+ * 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 tag
+
+import (
+	"strconv"
+)
+
+import (
+	perrors "github.com/pkg/errors"
+)
+
+import (
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/common/constant"
+	"github.com/apache/dubbo-go/protocol"
+)
+
+type tagRouter struct {
+	url      *common.URL
+	enabled  bool
+	priority int64
+}
+
+func NewTagRouter(url *common.URL) (*tagRouter, error) {
+	if url == nil {
+		return nil, perrors.Errorf("Illegal route URL!")
+	}
+	return &tagRouter{
+		url:      url,
+		enabled:  url.GetParamBool(constant.RouterEnabled, true),
+		priority: url.GetParamInt(constant.RouterPriority, 0),
+	}, nil
+}
+
+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() {
+		return invokers
+	}
+	if len(invokers) == 0 {
+		return invokers
+	}
+	return filterUsingStaticTag(invokers, url, invocation)
+}
+
+func (c *tagRouter) URL() common.URL {
+	return *c.url
+}
+
+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)
+			}
+		}
+		if len(result) == 0 && !isForceUseTag(url, invocation) {
+			return invokers
+		}
+		return result
+	}
+	return invokers
+}
+
+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
+}
diff --git a/cluster/router/tag/tag_router_test.go b/cluster/router/tag/tag_router_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..280b56c8ccb69eb5d32dae2369bdc862adb8e6fd
--- /dev/null
+++ b/cluster/router/tag/tag_router_test.go
@@ -0,0 +1,147 @@
+/*
+ * 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 tag
+
+import (
+	"context"
+	"testing"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+import (
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/protocol"
+	"github.com/apache/dubbo-go/protocol/invocation"
+)
+
+// MockInvoker is only mock the Invoker to support test tagRouter
+type MockInvoker struct {
+	url          common.URL
+	available    bool
+	destroyed    bool
+	successCount int
+}
+
+func NewMockInvoker(url common.URL) *MockInvoker {
+	return &MockInvoker{
+		url:          url,
+		available:    true,
+		destroyed:    false,
+		successCount: 0,
+	}
+}
+
+func (bi *MockInvoker) GetUrl() common.URL {
+	return bi.url
+}
+
+func (bi *MockInvoker) IsAvailable() bool {
+	return bi.available
+}
+
+func (bi *MockInvoker) IsDestroyed() bool {
+	return bi.destroyed
+}
+
+func (bi *MockInvoker) Invoke(_ context.Context, _ protocol.Invocation) protocol.Result {
+	bi.successCount++
+
+	result := &protocol.RPCResult{Err: nil}
+	return result
+}
+
+func (bi *MockInvoker) Destroy() {
+	bi.destroyed = true
+	bi.available = false
+}
+
+func TestTagRouter_Priority(t *testing.T) {
+	u1, err := common.NewURL("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")
+	assert.Nil(t, err)
+	tagRouter, e := NewTagRouter(&u1)
+	assert.Nil(t, e)
+	p := tagRouter.Priority()
+	assert.Equal(t, int64(0), p)
+}
+
+func TestTagRouter_Route_force(t *testing.T) {
+	u1, e1 := common.NewURL("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")
+	assert.Nil(t, e1)
+	tagRouter, e := NewTagRouter(&u1)
+	assert.Nil(t, e)
+
+	u2, e2 := common.NewURL("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")
+	u3, e3 := common.NewURL("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")
+	u4, e4 := common.NewURL("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")
+	assert.Nil(t, e2)
+	assert.Nil(t, e3)
+	assert.Nil(t, e4)
+	inv2 := NewMockInvoker(u2)
+	inv3 := NewMockInvoker(u3)
+	inv4 := NewMockInvoker(u4)
+	var invokers []protocol.Invoker
+	invokers = append(invokers, inv2, inv3, inv4)
+	inv := &invocation.RPCInvocation{}
+	inv.SetAttachments("dubbo.tag", "hangzhou")
+	invRst1 := tagRouter.Route(invokers, &u1, inv)
+	assert.Equal(t, 1, len(invRst1))
+	assert.Equal(t, "hangzhou", invRst1[0].GetUrl().GetParam("dubbo.tag", ""))
+
+	inv.SetAttachments("dubbo.tag", "guangzhou")
+	invRst2 := tagRouter.Route(invokers, &u1, inv)
+	assert.Equal(t, 0, len(invRst2))
+	inv.SetAttachments("dubbo.force.tag", "false")
+	inv.SetAttachments("dubbo.tag", "guangzhou")
+	invRst3 := tagRouter.Route(invokers, &u1, inv)
+	assert.Equal(t, 3, len(invRst3))
+}
+
+func TestTagRouter_Route_noForce(t *testing.T) {
+	u1, e1 := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserConsumer?interface=com.ikurento.user.UserConsumer&group=&version=2.6.0&enabled=true")
+	assert.Nil(t, e1)
+	tagRouter, e := NewTagRouter(&u1)
+	assert.Nil(t, e)
+
+	u2, e2 := common.NewURL("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")
+	u3, e3 := common.NewURL("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")
+	u4, e4 := common.NewURL("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")
+	assert.Nil(t, e2)
+	assert.Nil(t, e3)
+	assert.Nil(t, e4)
+	inv2 := NewMockInvoker(u2)
+	inv3 := NewMockInvoker(u3)
+	inv4 := NewMockInvoker(u4)
+	var invokers []protocol.Invoker
+	invokers = append(invokers, inv2, inv3, inv4)
+	inv := &invocation.RPCInvocation{}
+	inv.SetAttachments("dubbo.tag", "hangzhou")
+	invRst := tagRouter.Route(invokers, &u1, inv)
+	assert.Equal(t, 1, len(invRst))
+	assert.Equal(t, "hangzhou", invRst[0].GetUrl().GetParam("dubbo.tag", ""))
+
+	inv.SetAttachments("dubbo.tag", "guangzhou")
+	inv.SetAttachments("dubbo.force.tag", "true")
+	invRst1 := tagRouter.Route(invokers, &u1, inv)
+	assert.Equal(t, 0, len(invRst1))
+	inv.SetAttachments("dubbo.force.tag", "false")
+	invRst2 := tagRouter.Route(invokers, &u1, inv)
+	assert.Equal(t, 3, len(invRst2))
+}
diff --git a/common/constant/key.go b/common/constant/key.go
index 07335bed599788b0240b28b096c7d7a395ee9c11..d9413fcc9e24a857cdb398cc6fb96074ffef31b4 100644
--- a/common/constant/key.go
+++ b/common/constant/key.go
@@ -40,7 +40,9 @@ const (
 	TOKEN_KEY              = "token"
 	LOCAL_ADDR             = "local-addr"
 	REMOTE_ADDR            = "remote-addr"
-	PATH_SEPARATOR         = "/"
+	DUBBO_KEY              = "dubbo"
+	RELEASE_KEY            = "release"
+	ANYHOST_KEY            = "anyhost"
 )
 
 const (
@@ -105,6 +107,7 @@ const (
 	ROUTERS_CATEGORY         = "routers"
 	ROUTE_PROTOCOL           = "route"
 	CONDITION_ROUTE_PROTOCOL = "condition"
+	TAG_ROUTE_PROTOCOL       = "tag"
 	PROVIDERS_CATEGORY       = "providers"
 	ROUTER_KEY               = "router"
 )
@@ -128,6 +131,7 @@ const (
 	ProviderConfigPrefix       = "dubbo.provider."
 	ConsumerConfigPrefix       = "dubbo.consumer."
 	ShutdownConfigPrefix       = "dubbo.shutdown."
+	MetadataReportPrefix       = "dubbo.metadata-report."
 	RouterConfigPrefix         = "dubbo.router."
 )
 
@@ -161,7 +165,8 @@ const (
 	ListenableRouterName = "listenable"
 	// HealthCheckRouterName Specify the name of HealthCheckRouter
 	HealthCheckRouterName = "health_check"
-
+	// TagRouterName Specify the name of TagRouter
+	TagRouterName = "tag"
 	// ConditionRouterRuleSuffix Specify condition router suffix
 	ConditionRouterRuleSuffix = ".condition-router"
 
@@ -171,6 +176,10 @@ const (
 	RouterEnabled = "enabled"
 	// Priority Priority key in router module
 	RouterPriority = "priority"
+
+	// ForceUseTag is the tag in attachment
+	ForceUseTag = "dubbo.force.tag"
+	Tagkey      = "dubbo.tag"
 )
 
 const (
@@ -206,6 +215,20 @@ const (
 	SECRET_ACCESS_KEY_KEY = "secretAccessKey"
 )
 
+// metadata report
+
+const (
+	METACONFIG_REMOTE  = "remote"
+	METACONFIG_LOCAL   = "local"
+	KEY_SEPARATOR      = ":"
+	DEFAULT_PATH_TAG   = "metadata"
+	KEY_REVISON_PREFIX = "revision"
+	PATH_SEPARATOR     = "/"
+
+	// metadata service
+	METADATA_SERVICE_NAME = "org.apache.dubbo.metadata.MetadataService"
+)
+
 // HealthCheck Router
 const (
 	// The key of HealthCheck SPI
@@ -227,3 +250,9 @@ const (
 	// The default time window of circuit-tripped  in millisecond if not specfied
 	MAX_CIRCUIT_TRIPPED_TIMEOUT_IN_MS = 30000
 )
+
+// service discovery
+
+const (
+	NACOS_GROUP = "nacos.group"
+)
diff --git a/common/extension/metadata_report_factory.go b/common/extension/metadata_report_factory.go
new file mode 100644
index 0000000000000000000000000000000000000000..0ae0793bb4459767cb42fb1860fc484388aae1a3
--- /dev/null
+++ b/common/extension/metadata_report_factory.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 extension
+
+import (
+	"github.com/apache/dubbo-go/metadata"
+)
+
+var (
+	metaDataReportFactories = make(map[string]func() metadata.MetadataReportFactory, 8)
+)
+
+// SetMetadataReportFactory ...
+func SetMetadataReportFactory(name string, v func() metadata.MetadataReportFactory) {
+	metaDataReportFactories[name] = v
+}
+
+// GetMetadataReportFactory ...
+func GetMetadataReportFactory(name string) metadata.MetadataReportFactory {
+	if metaDataReportFactories[name] == nil {
+		panic("metadata report for " + name + " is not existing, make sure you have import the package.")
+	}
+	return metaDataReportFactories[name]()
+}
diff --git a/common/extension/registry_directory.go b/common/extension/registry_directory.go
new file mode 100644
index 0000000000000000000000000000000000000000..6b92189c4e98b391a90e6e71a68d51a252eede2a
--- /dev/null
+++ b/common/extension/registry_directory.go
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package extension
+
+import (
+	"github.com/apache/dubbo-go/cluster"
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/registry"
+)
+
+type registryDirectory func(url *common.URL, registry registry.Registry) (cluster.Directory, error)
+
+var defaultRegistry registryDirectory
+
+// SetDefaultRegistryDirectory ...
+func SetDefaultRegistryDirectory(v registryDirectory) {
+	defaultRegistry = v
+}
+
+// GetDefaultRegistryDirectory ...
+func GetDefaultRegistryDirectory(config *common.URL, registry registry.Registry) (cluster.Directory, error) {
+	if defaultRegistry == nil {
+		panic("registry directory is not existing, make sure you have import the package.")
+	}
+	return defaultRegistry(config, registry)
+}
diff --git a/common/extension/router_factory.go b/common/extension/router_factory.go
index 70d71dfa859b996030c865775a588da20039f9a5..1339228618def41ccebc8d54cdebb5a623e605fa 100644
--- a/common/extension/router_factory.go
+++ b/common/extension/router_factory.go
@@ -28,7 +28,7 @@ import (
 var (
 	routers               = make(map[string]func() router.RouterFactory)
 	fileRouterFactoryOnce sync.Once
-	fileRouterFactories   = make(map[string]router.FIleRouterFactory)
+	fileRouterFactories   = make(map[string]router.FileRouterFactory)
 )
 
 // SetRouterFactory Set create router factory function by name
@@ -50,7 +50,7 @@ func GetRouterFactories() map[string]func() router.RouterFactory {
 }
 
 // GetFileRouterFactories Get all create file router factory instance
-func GetFileRouterFactories() map[string]router.FIleRouterFactory {
+func GetFileRouterFactories() map[string]router.FileRouterFactory {
 	l := len(routers)
 	if l == 0 {
 		return nil
@@ -58,7 +58,7 @@ func GetFileRouterFactories() map[string]router.FIleRouterFactory {
 	fileRouterFactoryOnce.Do(func() {
 		for k := range routers {
 			factory := GetRouterFactory(k)
-			if fr, ok := factory.(router.FIleRouterFactory); ok {
+			if fr, ok := factory.(router.FileRouterFactory); ok {
 				fileRouterFactories[k] = fr
 			}
 		}
diff --git a/common/extension/service_discovery.go b/common/extension/service_discovery.go
new file mode 100644
index 0000000000000000000000000000000000000000..25b80cf3353505c058bea40cc4c80712ad923d2d
--- /dev/null
+++ b/common/extension/service_discovery.go
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package extension
+
+import (
+	perrors "github.com/pkg/errors"
+)
+import (
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/registry"
+)
+
+var (
+	discoveryCreatorMap = make(map[string]func(url *common.URL) (registry.ServiceDiscovery, error), 4)
+)
+
+// SetServiceDiscovery will store the creator and name
+func SetServiceDiscovery(name string, creator func(url *common.URL) (registry.ServiceDiscovery, error)) {
+	discoveryCreatorMap[name] = creator
+}
+
+// GetServiceDiscovery will return the registry.ServiceDiscovery
+// if not found, or initialize instance failed, it will return error.
+func GetServiceDiscovery(name string, url *common.URL) (registry.ServiceDiscovery, error) {
+	creator, ok := discoveryCreatorMap[name]
+	if !ok {
+		return nil, perrors.New("Could not find the service discovery with name: " + name)
+	}
+	return creator(url)
+}
diff --git a/common/rpc_service.go b/common/rpc_service.go
index b235c32abc9a971d7144605c8b4b82953ac8f3c4..ebd1d02f843bc339c3a37d977e2138798307475d 100644
--- a/common/rpc_service.go
+++ b/common/rpc_service.go
@@ -71,7 +71,8 @@ var (
 	// ServiceMap ...
 	// todo: lowerecas?
 	ServiceMap = &serviceMap{
-		serviceMap: make(map[string]map[string]*Service),
+		serviceMap:   make(map[string]map[string]*Service),
+		interfaceMap: make(map[string][]*Service),
 	}
 )
 
@@ -147,10 +148,12 @@ func (s *Service) Rcvr() reflect.Value {
 //////////////////////////
 
 type serviceMap struct {
-	mutex      sync.RWMutex                   // protects the serviceMap
-	serviceMap map[string]map[string]*Service // protocol -> service name -> service
+	mutex        sync.RWMutex                   // protects the serviceMap
+	serviceMap   map[string]map[string]*Service // protocol -> service name -> service
+	interfaceMap map[string][]*Service          // interface -> service
 }
 
+// GetService get a service defination by protocol and name
 func (sm *serviceMap) GetService(protocol, name string) *Service {
 	sm.mutex.RLock()
 	defer sm.mutex.RUnlock()
@@ -163,10 +166,24 @@ func (sm *serviceMap) GetService(protocol, name string) *Service {
 	return nil
 }
 
-func (sm *serviceMap) Register(protocol string, rcvr RPCService) (string, error) {
+// GetInterface get an interface defination by interface name
+func (sm *serviceMap) GetInterface(interfaceName string) []*Service {
+	sm.mutex.RLock()
+	defer sm.mutex.RUnlock()
+	if s, ok := sm.interfaceMap[interfaceName]; ok {
+		return s
+	}
+	return nil
+}
+
+// Register register a service by @interfaceName and @protocol
+func (sm *serviceMap) Register(interfaceName, protocol string, rcvr RPCService) (string, error) {
 	if sm.serviceMap[protocol] == nil {
 		sm.serviceMap[protocol] = make(map[string]*Service)
 	}
+	if sm.interfaceMap[interfaceName] == nil {
+		sm.interfaceMap[interfaceName] = make([]*Service, 0, 16)
+	}
 
 	s := new(Service)
 	s.rcvrType = reflect.TypeOf(rcvr)
@@ -201,32 +218,65 @@ func (sm *serviceMap) Register(protocol string, rcvr RPCService) (string, error)
 	}
 	sm.mutex.Lock()
 	sm.serviceMap[protocol][s.name] = s
+	sm.interfaceMap[interfaceName] = append(sm.interfaceMap[interfaceName], s)
 	sm.mutex.Unlock()
 
 	return strings.TrimSuffix(methods, ","), nil
 }
 
-func (sm *serviceMap) UnRegister(protocol, serviceId string) error {
+// UnRegister cancel a service by @interfaceName, @protocol and @serviceId
+func (sm *serviceMap) UnRegister(interfaceName, protocol, serviceId string) error {
 	if protocol == "" || serviceId == "" {
 		return perrors.New("protocol or serviceName is nil")
 	}
-	sm.mutex.RLock()
-	svcs, ok := sm.serviceMap[protocol]
-	if !ok {
-		sm.mutex.RUnlock()
-		return perrors.New("no services for " + protocol)
+
+	var (
+		err   error
+		index = -1
+		svcs  map[string]*Service
+		svrs  []*Service
+		ok    bool
+	)
+
+	f := func() error {
+		sm.mutex.RLock()
+		defer sm.mutex.RUnlock()
+		svcs, ok = sm.serviceMap[protocol]
+		if !ok {
+			return perrors.New("no services for " + protocol)
+		}
+		s, ok := svcs[serviceId]
+		if !ok {
+			return perrors.New("no service for " + serviceId)
+		}
+		svrs, ok = sm.interfaceMap[interfaceName]
+		if !ok {
+			return perrors.New("no service for " + interfaceName)
+		}
+		for i, svr := range svrs {
+			if svr == s {
+				index = i
+			}
+		}
+		return nil
 	}
-	_, ok = svcs[serviceId]
-	if !ok {
-		sm.mutex.RUnlock()
-		return perrors.New("no service for " + serviceId)
+
+	if err = f(); err != nil {
+		return err
 	}
-	sm.mutex.RUnlock()
 
 	sm.mutex.Lock()
 	defer sm.mutex.Unlock()
+	sm.interfaceMap[interfaceName] = make([]*Service, 0, len(svrs))
+	for i, _ := range svrs {
+		if i != index {
+			sm.interfaceMap[interfaceName] = append(sm.interfaceMap[interfaceName], svrs[i])
+		}
+	}
 	delete(svcs, serviceId)
-	delete(sm.serviceMap, protocol)
+	if len(sm.serviceMap) == 0 {
+		delete(sm.serviceMap, protocol)
+	}
 
 	return nil
 }
diff --git a/common/rpc_service_test.go b/common/rpc_service_test.go
index 8c9b9d15cdd4061dbe2f445b5fff7a868e5ae67e..2311205d0ec0c2fd4642a4d8639c0bf871fe1d17 100644
--- a/common/rpc_service_test.go
+++ b/common/rpc_service_test.go
@@ -77,46 +77,48 @@ func TestServiceMap_Register(t *testing.T) {
 	// lowercase
 	s0 := &testService{}
 	// methods, err := ServiceMap.Register("testporotocol", s0)
-	_, err := ServiceMap.Register("testporotocol", s0)
+	_, err := ServiceMap.Register("testService", "testporotocol", s0)
 	assert.EqualError(t, err, "type testService is not exported")
 
 	// succ
 	s := &TestService{}
-	methods, err := ServiceMap.Register("testporotocol", s)
+	methods, err := ServiceMap.Register("testService", "testporotocol", s)
 	assert.NoError(t, err)
 	assert.Equal(t, "MethodOne,MethodThree,methodTwo", methods)
 
 	// repeat
-	_, err = ServiceMap.Register("testporotocol", s)
+	_, err = ServiceMap.Register("testService", "testporotocol", s)
 	assert.EqualError(t, err, "service already defined: com.test.Path")
 
 	// no method
 	s1 := &TestService1{}
-	_, err = ServiceMap.Register("testporotocol", s1)
+	_, err = ServiceMap.Register("testService", "testporotocol", s1)
 	assert.EqualError(t, err, "type com.test.Path1 has no exported methods of suitable type")
 
 	ServiceMap = &serviceMap{
-		serviceMap: make(map[string]map[string]*Service),
+		serviceMap:   make(map[string]map[string]*Service),
+		interfaceMap: make(map[string][]*Service),
 	}
 }
 
 func TestServiceMap_UnRegister(t *testing.T) {
 	s := &TestService{}
-	_, err := ServiceMap.Register("testprotocol", s)
+	_, err := ServiceMap.Register("TestService", "testprotocol", s)
 	assert.NoError(t, err)
 	assert.NotNil(t, ServiceMap.GetService("testprotocol", "com.test.Path"))
+	assert.Equal(t, 1, len(ServiceMap.GetInterface("TestService")))
 
-	err = ServiceMap.UnRegister("", "com.test.Path")
+	err = ServiceMap.UnRegister("", "", "com.test.Path")
 	assert.EqualError(t, err, "protocol or serviceName is nil")
 
-	err = ServiceMap.UnRegister("protocol", "com.test.Path")
+	err = ServiceMap.UnRegister("", "protocol", "com.test.Path")
 	assert.EqualError(t, err, "no services for protocol")
 
-	err = ServiceMap.UnRegister("testprotocol", "com.test.Path1")
+	err = ServiceMap.UnRegister("", "testprotocol", "com.test.Path1")
 	assert.EqualError(t, err, "no service for com.test.Path1")
 
 	// succ
-	err = ServiceMap.UnRegister("testprotocol", "com.test.Path")
+	err = ServiceMap.UnRegister("TestService", "testprotocol", "com.test.Path")
 	assert.NoError(t, err)
 }
 
diff --git a/common/url.go b/common/url.go
index ebb648db27c3efff534f0d0a545f2211f335aa89..768b62d4bf293b92202563cd3db16aa996dba12a 100644
--- a/common/url.go
+++ b/common/url.go
@@ -195,7 +195,6 @@ func NewURLWithOptions(opts ...option) *URL {
 // NewURL will create a new url
 // the urlString should not be empty
 func NewURL(urlString string, opts ...option) (URL, error) {
-
 	var (
 		err          error
 		rawUrlString string
@@ -265,17 +264,19 @@ func (c URL) URLEqual(url URL) bool {
 	} else if urlGroup == constant.ANY_VALUE {
 		urlKey = strings.Replace(urlKey, "group=*", "group="+cGroup, 1)
 	}
+
+	// 1. protocol, username, password, ip, port, service name, group, version should be equal
 	if cKey != urlKey {
 		return false
 	}
+
+	// 2. if url contains enabled key, should be true, or *
 	if url.GetParam(constant.ENABLED_KEY, "true") != "true" && url.GetParam(constant.ENABLED_KEY, "") != constant.ANY_VALUE {
 		return false
 	}
+
 	//TODO :may need add interface key any value condition
-	if !isMatchCategory(url.GetParam(constant.CATEGORY_KEY, constant.DEFAULT_CATEGORY), c.GetParam(constant.CATEGORY_KEY, constant.DEFAULT_CATEGORY)) {
-		return false
-	}
-	return true
+	return isMatchCategory(url.GetParam(constant.CATEGORY_KEY, constant.DEFAULT_CATEGORY), c.GetParam(constant.CATEGORY_KEY, constant.DEFAULT_CATEGORY))
 }
 
 func isMatchCategory(category1 string, category2 string) bool {
@@ -313,7 +314,6 @@ func (c URL) Key() string {
 		"%s://%s:%s@%s:%s/?interface=%s&group=%s&version=%s",
 		c.Protocol, c.Username, c.Password, c.Ip, c.Port, c.Service(), c.GetParam(constant.GROUP_KEY, ""), c.GetParam(constant.VERSION_KEY, ""))
 	return buildString
-	//return c.ServiceKey()
 }
 
 // ServiceKey ...
@@ -409,12 +409,12 @@ func (c *URL) RangeParams(f func(key, value string) bool) {
 
 // GetParam ...
 func (c URL) GetParam(s string, d string) string {
-	var r string
 	c.paramsLock.RLock()
-	if r = c.params.Get(s); len(r) == 0 {
+	defer c.paramsLock.RUnlock()
+	r := c.params.Get(s)
+	if len(r) == 0 {
 		r = d
 	}
-	c.paramsLock.RUnlock()
 	return r
 }
 
@@ -454,10 +454,8 @@ func (c URL) GetRawParam(key string) string {
 
 // GetParamBool ...
 func (c URL) GetParamBool(s string, d bool) bool {
-
-	var r bool
-	var err error
-	if r, err = strconv.ParseBool(c.GetParam(s, "")); err != nil {
+	r, err := strconv.ParseBool(c.GetParam(s, ""))
+	if err != nil {
 		return d
 	}
 	return r
@@ -465,10 +463,8 @@ func (c URL) GetParamBool(s string, d bool) bool {
 
 // GetParamInt ...
 func (c URL) GetParamInt(s string, d int64) int64 {
-	var r int
-	var err error
-
-	if r, err = strconv.Atoi(c.GetParam(s, "")); r == 0 || err != nil {
+	r, err := strconv.Atoi(c.GetParam(s, ""))
+	if r == 0 || err != nil {
 		return d
 	}
 	return int64(r)
@@ -476,11 +472,8 @@ func (c URL) GetParamInt(s string, d int64) int64 {
 
 // GetMethodParamInt ...
 func (c URL) GetMethodParamInt(method string, key string, d int64) int64 {
-	var r int
-	var err error
-	c.paramsLock.RLock()
-	defer c.paramsLock.RUnlock()
-	if r, err = strconv.Atoi(c.GetParam("methods."+method+"."+key, "")); r == 0 || err != nil {
+	r, err := strconv.Atoi(c.GetParam("methods."+method+"."+key, ""))
+	if r == 0 || err != nil {
 		return d
 	}
 	return int64(r)
@@ -492,14 +485,13 @@ func (c URL) GetMethodParamInt64(method string, key string, d int64) int64 {
 	if r == math.MinInt64 {
 		return c.GetParamInt(key, d)
 	}
-
 	return r
 }
 
 // GetMethodParam ...
 func (c URL) GetMethodParam(method string, key string, d string) string {
-	var r string
-	if r = c.GetParam("methods."+method+"."+key, ""); r == "" {
+	r := c.GetParam("methods."+method+"."+key, "")
+	if r == "" {
 		r = d
 	}
 	return r
@@ -530,7 +522,6 @@ func (c *URL) SetParams(m url.Values) {
 
 // ToMap transfer URL to Map
 func (c URL) ToMap() map[string]string {
-
 	paramsMap := make(map[string]string)
 
 	c.RangeParams(func(key, value string) bool {
@@ -615,8 +606,30 @@ func (c *URL) Clone() *URL {
 	return newUrl
 }
 
+// Copy url based on the reserved parameters' keys.
+func (c *URL) CloneWithParams(reserveParams []string) *URL {
+	params := url.Values{}
+	for _, reserveParam := range reserveParams {
+		v := c.GetParam(reserveParam, "")
+		if len(v) != 0 {
+			params.Set(reserveParam, v)
+		}
+	}
+
+	return NewURLWithOptions(
+		WithProtocol(c.Protocol),
+		WithUsername(c.Username),
+		WithPassword(c.Password),
+		WithIp(c.Ip),
+		WithPort(c.Port),
+		WithPath(c.Path),
+		WithMethods(c.Methods),
+		WithParams(params),
+	)
+}
+
 func mergeNormalParam(mergedUrl *URL, referenceUrl *URL, paramKeys []string) []func(method string) {
-	var methodConfigMergeFcn = []func(method string){}
+	methodConfigMergeFcn := make([]func(method string), 0, len(paramKeys))
 	for _, paramKey := range paramKeys {
 		if v := referenceUrl.GetParam(paramKey, ""); len(v) > 0 {
 			mergedUrl.SetParam(paramKey, v)
diff --git a/common/yaml/yaml.go b/common/yaml/yaml.go
index 7c31d71c35fff547d2ed0a765e8245717148a451..93ebb166144510236aff27a67422a6377ccb5c9f 100644
--- a/common/yaml/yaml.go
+++ b/common/yaml/yaml.go
@@ -48,3 +48,7 @@ func UnmarshalYMLConfig(confProFile string, out interface{}) ([]byte, error) {
 	}
 	return confFileStream, yaml.Unmarshal(confFileStream, out)
 }
+
+func UnmarshalYML(data []byte, out interface{}) error {
+	return yaml.Unmarshal(data, out)
+}
diff --git a/common/yaml/yaml_test.go b/common/yaml/yaml_test.go
index 45eee59048c1c074b9c386e26cc7a2252896e6ea..c8b8258a68951a1437ac2e617c13ee5af4b3a5ee 100644
--- a/common/yaml/yaml_test.go
+++ b/common/yaml/yaml_test.go
@@ -46,6 +46,18 @@ func TestUnmarshalYMLConfig_Error(t *testing.T) {
 	assert.Error(t, err)
 }
 
+func TestUnmarshalYML(t *testing.T) {
+	c := &Config{}
+	b, err := LoadYMLConfig("./testdata/config.yml")
+	assert.NoError(t, err)
+	err = UnmarshalYML(b, c)
+	assert.NoError(t, err)
+	assert.Equal(t, "strTest", c.StrTest)
+	assert.Equal(t, 11, c.IntTest)
+	assert.Equal(t, false, c.BooleanTest)
+	assert.Equal(t, "childStrTest", c.ChildConfig.StrTest)
+}
+
 type Config struct {
 	StrTest     string      `yaml:"strTest" default:"default" json:"strTest,omitempty" property:"strTest"`
 	IntTest     int         `default:"109"  yaml:"intTest" json:"intTest,omitempty" property:"intTest"`
diff --git a/config/application_config.go b/config/application_config.go
index 23ab7d34aceaba02d7f592906d6f4e3d6cf36dae..33b47c81dd0da9959984cd1f53648167863cb713 100644
--- a/config/application_config.go
+++ b/config/application_config.go
@@ -33,6 +33,7 @@ type ApplicationConfig struct {
 	Version      string `yaml:"version" json:"version,omitempty" property:"version"`
 	Owner        string `yaml:"owner" json:"owner,omitempty" property:"owner"`
 	Environment  string `yaml:"environment" json:"environment,omitempty" property:"environment"`
+	MetadataType string `default:"local" yaml:"metadataType" json:"metadataType,omitempty" property:"metadataType"` //field for metadata report
 }
 
 // Prefix ...
diff --git a/config/config_loader.go b/config/config_loader.go
index c0687d8fc162331afc5098e347d4bbba6a1750c6..c84817eaea9d2ba825c96bdb7b2945018b523114 100644
--- a/config/config_loader.go
+++ b/config/config_loader.go
@@ -81,128 +81,142 @@ func checkApplicationName(config *ApplicationConfig) {
 	}
 }
 
-// Load Dubbo Init
-func Load() {
-
-	// init router
-	if confRouterFile != "" {
-		if errPro := RouterInit(confRouterFile); errPro != nil {
-			log.Printf("[routerConfig init] %#v", errPro)
-		}
-	}
-
-	// reference config
+func loadConsumerConfig() {
 	if consumerConfig == nil {
 		logger.Warnf("consumerConfig is nil!")
-	} else {
-		// init other consumer config
-		conConfigType := consumerConfig.ConfigType
-		for key, value := range extension.GetDefaultConfigReader() {
-			if conConfigType == nil {
-				if v, ok := conConfigType[key]; ok {
-					value = v
-				}
-			}
-			if err := extension.GetConfigReaders(value).ReadConsumerConfig(consumerConfig.fileStream); err != nil {
-				logger.Errorf("ReadConsumerConfig error: %#v for %s", perrors.WithStack(err), value)
+		return
+	}
+	// init other consumer config
+	conConfigType := consumerConfig.ConfigType
+	for key, value := range extension.GetDefaultConfigReader() {
+		if conConfigType == nil {
+			if v, ok := conConfigType[key]; ok {
+				value = v
 			}
 		}
+		if err := extension.GetConfigReaders(value).ReadConsumerConfig(consumerConfig.fileStream); err != nil {
+			logger.Errorf("ReadConsumerConfig error: %#v for %s", perrors.WithStack(err), value)
+		}
+	}
 
-		metricConfig = consumerConfig.MetricConfig
-		applicationConfig = consumerConfig.ApplicationConfig
+	metricConfig = consumerConfig.MetricConfig
+	applicationConfig = consumerConfig.ApplicationConfig
 
-		checkApplicationName(consumerConfig.ApplicationConfig)
-		if err := configCenterRefreshConsumer(); err != nil {
-			logger.Errorf("[consumer config center refresh] %#v", err)
+	checkApplicationName(consumerConfig.ApplicationConfig)
+	if err := configCenterRefreshConsumer(); err != nil {
+		logger.Errorf("[consumer config center refresh] %#v", err)
+	}
+	checkRegistries(consumerConfig.Registries, consumerConfig.Registry)
+	for key, ref := range consumerConfig.References {
+		if ref.Generic {
+			genericService := NewGenericService(key)
+			SetConsumerService(genericService)
 		}
-		checkRegistries(consumerConfig.Registries, consumerConfig.Registry)
-		for key, ref := range consumerConfig.References {
-			if ref.Generic {
-				genericService := NewGenericService(key)
-				SetConsumerService(genericService)
-			}
-			rpcService := GetConsumerService(key)
-			if rpcService == nil {
-				logger.Warnf("%s does not exist!", key)
-				continue
-			}
-			ref.id = key
-			ref.Refer(rpcService)
-			ref.Implement(rpcService)
+		rpcService := GetConsumerService(key)
+		if rpcService == nil {
+			logger.Warnf("%s does not exist!", key)
+			continue
 		}
+		ref.id = key
+		ref.Refer(rpcService)
+		ref.Implement(rpcService)
+	}
 
-		//wait for invoker is available, if wait over default 3s, then panic
-		var count int
-		checkok := true
-		for {
-			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() {
-						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)
-							logger.Error(errMsg)
-							panic(errMsg)
-						}
-						time.Sleep(time.Second * 1)
-						break
-					}
-					if refconfig.invoker == nil {
-						logger.Warnf("The interface %s invoker not exist , may you should check your interface config.", refconfig.InterfaceName)
+	//wait for invoker is available, if wait over default 3s, then panic
+	var count int
+	checkok := true
+	for {
+		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() {
+					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)
+						logger.Error(errMsg)
+						panic(errMsg)
 					}
+					time.Sleep(time.Second * 1)
+					break
+				}
+				if refconfig.invoker == nil {
+					logger.Warnf("The interface %s invoker not exist , may you should check your interface config.", refconfig.InterfaceName)
 				}
 			}
-			if checkok {
-				break
-			}
-			checkok = true
 		}
+		if checkok {
+			break
+		}
+		checkok = true
 	}
+}
 
-	// service config
+func loadProviderConfig() {
 	if providerConfig == nil {
 		logger.Warnf("providerConfig is nil!")
-	} else {
-		// init other provider config
-		proConfigType := providerConfig.ConfigType
-		for key, value := range extension.GetDefaultConfigReader() {
-			if proConfigType != nil {
-				if v, ok := proConfigType[key]; ok {
-					value = v
-				}
-			}
-			if err := extension.GetConfigReaders(value).ReadProviderConfig(providerConfig.fileStream); err != nil {
-				logger.Errorf("ReadProviderConfig error: %#v for %s", perrors.WithStack(err), value)
+		return
+	}
+
+	// init other provider config
+	proConfigType := providerConfig.ConfigType
+	for key, value := range extension.GetDefaultConfigReader() {
+		if proConfigType != nil {
+			if v, ok := proConfigType[key]; ok {
+				value = v
 			}
 		}
+		if err := extension.GetConfigReaders(value).ReadProviderConfig(providerConfig.fileStream); err != nil {
+			logger.Errorf("ReadProviderConfig error: %#v for %s", perrors.WithStack(err), value)
+		}
+	}
 
-		// so, you should know that the consumer's config will be override
-		metricConfig = providerConfig.MetricConfig
-		applicationConfig = providerConfig.ApplicationConfig
+	// so, you should know that the consumer's config will be override
+	metricConfig = providerConfig.MetricConfig
+	applicationConfig = providerConfig.ApplicationConfig
 
-		checkApplicationName(providerConfig.ApplicationConfig)
-		if err := configCenterRefreshProvider(); err != nil {
-			logger.Errorf("[provider config center refresh] %#v", err)
+	checkApplicationName(providerConfig.ApplicationConfig)
+	if err := configCenterRefreshProvider(); err != nil {
+		logger.Errorf("[provider config center refresh] %#v", err)
+	}
+	checkRegistries(providerConfig.Registries, providerConfig.Registry)
+	for key, svs := range providerConfig.Services {
+		rpcService := GetProviderService(key)
+		if rpcService == nil {
+			logger.Warnf("%s does not exist!", key)
+			continue
 		}
-		checkRegistries(providerConfig.Registries, providerConfig.Registry)
-		for key, svs := range providerConfig.Services {
-			rpcService := GetProviderService(key)
-			if rpcService == nil {
-				logger.Warnf("%s does not exist!", key)
-				continue
-			}
-			svs.id = key
-			svs.Implement(rpcService)
-			if err := svs.Export(); err != nil {
-				panic(fmt.Sprintf("service %s export failed! ", key))
-			}
+		svs.id = key
+		svs.Implement(rpcService)
+		if err := svs.Export(); err != nil {
+			panic(fmt.Sprintf("service %s export failed! err: %#v", key, err))
 		}
 	}
+}
+
+func initRouter() {
+	if confRouterFile != "" {
+		if err := RouterInit(confRouterFile); err != nil {
+			log.Printf("[routerConfig init] %#v", err)
+		}
+	}
+}
+
+// Load Dubbo Init
+func Load() {
+
+	// init router
+	initRouter()
+
+	// reference config
+	loadConsumerConfig()
+
+	// service config
+	loadProviderConfig()
+
 	// init the shutdown callback
 	GracefulShutdownInit()
 }
diff --git a/config/config_loader_test.go b/config/config_loader_test.go
index 498f82678070d194e3ffe1539064be7aec19f719..6368fcbd2c7bc675231e7b7835750f26743708af 100644
--- a/config/config_loader_test.go
+++ b/config/config_loader_test.go
@@ -82,7 +82,8 @@ func TestLoad(t *testing.T) {
 
 	conServices = map[string]common.RPCService{}
 	proServices = map[string]common.RPCService{}
-	common.ServiceMap.UnRegister("mock", "MockService")
+	err := common.ServiceMap.UnRegister("com.MockService", "mock", "MockService")
+	assert.Nil(t, err)
 	consumerConfig = nil
 	providerConfig = nil
 }
@@ -110,7 +111,7 @@ func TestLoadWithSingleReg(t *testing.T) {
 
 	conServices = map[string]common.RPCService{}
 	proServices = map[string]common.RPCService{}
-	common.ServiceMap.UnRegister("mock", "MockService")
+	common.ServiceMap.UnRegister("com.MockService", "mock", "MockService")
 	consumerConfig = nil
 	providerConfig = nil
 }
@@ -139,7 +140,7 @@ func TestWithNoRegLoad(t *testing.T) {
 
 	conServices = map[string]common.RPCService{}
 	proServices = map[string]common.RPCService{}
-	common.ServiceMap.UnRegister("mock", "MockService")
+	common.ServiceMap.UnRegister("com.MockService", "mock", "MockService")
 	consumerConfig = nil
 	providerConfig = nil
 }
diff --git a/config/consumer_config.go b/config/consumer_config.go
index 1fa68415bfc3c7e622c0b455e9945c926fed4df2..debcd79fa281c40e5526f60f5c5cdb66688688f4 100644
--- a/config/consumer_config.go
+++ b/config/consumer_config.go
@@ -44,6 +44,7 @@ type ConsumerConfig struct {
 	Filter     string `yaml:"filter" json:"filter,omitempty" property:"filter"`
 	// application
 	ApplicationConfig *ApplicationConfig `yaml:"application" json:"application,omitempty" property:"application"`
+
 	// client
 	Connect_Timeout string `default:"100ms"  yaml:"connect_timeout" json:"connect_timeout,omitempty" property:"connect_timeout"`
 	ConnectTimeout  time.Duration
@@ -117,6 +118,7 @@ func ConsumerInit(confConFile string) error {
 			return perrors.WithMessagef(err, "time.ParseDuration(Connect_Timeout{%#v})", consumerConfig.Connect_Timeout)
 		}
 	}
+
 	logger.Debugf("consumer config{%#v}\n", consumerConfig)
 
 	return nil
diff --git a/config/instance/metedata_report.go b/config/instance/metedata_report.go
new file mode 100644
index 0000000000000000000000000000000000000000..cd54b0a7940df166c88f02234ab1a4e3bf384163
--- /dev/null
+++ b/config/instance/metedata_report.go
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package instance
+
+import (
+	"sync"
+)
+
+import (
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/common/extension"
+	"github.com/apache/dubbo-go/metadata"
+)
+
+var (
+	instance metadata.MetadataReport
+	once     sync.Once
+)
+
+// GetMetadataReportInstance ...
+func GetMetadataReportInstance(url *common.URL) metadata.MetadataReport {
+	once.Do(func() {
+		instance = extension.GetMetadataReportFactory(url.Protocol).CreateMetadataReport(url)
+	})
+	return instance
+}
diff --git a/config/metadata_report_config.go b/config/metadata_report_config.go
new file mode 100644
index 0000000000000000000000000000000000000000..41fb6b4769e59784d8d18c3f82b956fd029d4ff7
--- /dev/null
+++ b/config/metadata_report_config.go
@@ -0,0 +1,110 @@
+/*
+ * 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 (
+	"net/url"
+)
+
+import (
+	"github.com/creasty/defaults"
+	perrors "github.com/pkg/errors"
+)
+
+import (
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/common/constant"
+	"github.com/apache/dubbo-go/config/instance"
+)
+
+// MethodConfig ...
+type MetadataReportConfig struct {
+	Protocol   string            `required:"true"  yaml:"protocol"  json:"protocol,omitempty"`
+	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"`
+	Params     map[string]string `yaml:"params" json:"params,omitempty" property:"params"`
+	TimeoutStr string            `yaml:"timeout" default:"5s" json:"timeout,omitempty" property:"timeout"` // unit: second
+	Group      string            `yaml:"group" json:"group,omitempty" property:"group"`
+}
+
+// Prefix ...
+func (c *MetadataReportConfig) Prefix() string {
+	return constant.MetadataReportPrefix
+}
+
+// UnmarshalYAML ...
+func (c *MetadataReportConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
+	if err := defaults.Set(c); err != nil {
+		return perrors.WithStack(err)
+	}
+	type plain MetadataReportConfig
+	if err := unmarshal((*plain)(c)); err != nil {
+		return perrors.WithStack(err)
+	}
+	return nil
+}
+
+// ToUrl ...
+func (c *MetadataReportConfig) ToUrl() (*common.URL, error) {
+	urlMap := make(url.Values)
+
+	if c.Params != nil {
+		for k, v := range c.Params {
+			urlMap.Set(k, v)
+		}
+	}
+
+	url, err := common.NewURL(c.Address,
+		common.WithParams(urlMap),
+		common.WithUsername(c.Username),
+		common.WithPassword(c.Password),
+		common.WithLocation(c.Address),
+		common.WithProtocol(c.Protocol),
+	)
+	if err != nil || len(url.Protocol) == 0 {
+		return nil, perrors.New("Invalid MetadataReportConfig.")
+	}
+	url.SetParam("metadata", url.Protocol)
+	return &url, nil
+}
+
+func (c *MetadataReportConfig) IsValid() bool {
+	return len(c.Protocol) != 0
+}
+
+// StartMetadataReport: The entry of metadata report start
+func startMetadataReport(metadataType string, metadataReportConfig *MetadataReportConfig) error {
+	if metadataReportConfig == nil || metadataReportConfig.IsValid() {
+		return nil
+	}
+
+	if metadataType == constant.METACONFIG_REMOTE {
+		return perrors.New("No MetadataConfig found, you must specify the remote Metadata Center address when 'metadata=remote' is enabled.")
+	} else if metadataType == constant.METACONFIG_REMOTE && len(metadataReportConfig.Address) == 0 {
+		return perrors.New("MetadataConfig address can not be empty.")
+	}
+
+	if url, err := metadataReportConfig.ToUrl(); err == nil {
+		instance.GetMetadataReportInstance(url)
+	} else {
+		return perrors.New("MetadataConfig is invalid!")
+	}
+
+	return nil
+}
diff --git a/config/metadata_report_config_test.go b/config/metadata_report_config_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..635feecc2d433366534566d184e058eb54a881ed
--- /dev/null
+++ b/config/metadata_report_config_test.go
@@ -0,0 +1,47 @@
+/*
+ * 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"
+)
+
+func TestMetadataReportConfig_ToUrl(t *testing.T) {
+	metadataReportConfig := MetadataReportConfig{
+		Protocol:   "mock",
+		Address:    "127.0.0.1:2181",
+		Username:   "test",
+		Password:   "test",
+		TimeoutStr: "3s",
+		Params: map[string]string{
+			"k": "v",
+		},
+	}
+	url, error := metadataReportConfig.ToUrl()
+	assert.NoError(t, error)
+	assert.Equal(t, "mock", url.Protocol)
+	assert.Equal(t, "127.0.0.1:2181", url.Location)
+	assert.Equal(t, "127.0.0.1", url.Ip)
+	assert.Equal(t, "2181", url.Port)
+	assert.Equal(t, "test", url.Username)
+	assert.Equal(t, "test", url.Password)
+	assert.Equal(t, "v", url.GetParam("k", ""))
+	assert.Equal(t, "mock", url.GetParam("metadata", ""))
+}
diff --git a/config/provider_config.go b/config/provider_config.go
index 14b77cafb3487754b9583d3b4e64ff605394b7db..79569917455773653750d1d5921a722daf079b0a 100644
--- a/config/provider_config.go
+++ b/config/provider_config.go
@@ -41,6 +41,8 @@ type ProviderConfig struct {
 	BaseConfig   `yaml:",inline"`
 	Filter       string `yaml:"filter" json:"filter,omitempty" property:"filter"`
 	ProxyFactory string `yaml:"proxy_factory" default:"default" json:"proxy_factory,omitempty" property:"proxy_factory"`
+	// metadata-report
+	MetadataReportConfig *MetadataReportConfig `yaml:"metadata_report" json:"metadata_report,omitempty" property:"metadata_report"`
 
 	ApplicationConfig *ApplicationConfig         `yaml:"application" json:"application,omitempty" property:"application"`
 	Registry          *RegistryConfig            `yaml:"registry" json:"registry,omitempty" property:"registry"`
@@ -95,7 +97,10 @@ func ProviderInit(confProFile string) error {
 			n.InterfaceId = k
 		}
 	}
-
+	//start the metadata report if config set
+	if err := startMetadataReport(providerConfig.ApplicationConfig.MetadataType, providerConfig.MetadataReportConfig); err != nil {
+		return perrors.WithMessagef(err, "Provider starts metadata report error, and the error is {%#v}", err)
+	}
 	logger.Debugf("provider config{%#v}\n", providerConfig)
 
 	return nil
diff --git a/config/reference_config.go b/config/reference_config.go
index 7ce0013194f5c1a1d09e014a858433833aa07f0e..3710cbc4bc62a01a014e91bcb978742c4a93c5cb 100644
--- a/config/reference_config.go
+++ b/config/reference_config.go
@@ -63,6 +63,7 @@ type ReferenceConfig struct {
 	Generic        bool   `yaml:"generic"  json:"generic,omitempty" property:"generic"`
 	Sticky         bool   `yaml:"sticky"   json:"sticky,omitempty" property:"sticky"`
 	RequestTimeout string `yaml:"timeout"  json:"timeout,omitempty" property:"timeout"`
+	ForceTag       bool   `yaml:"force.tag"  json:"force.tag,omitempty" property:"force.tag"`
 }
 
 // Prefix ...
@@ -99,7 +100,9 @@ func (c *ReferenceConfig) Refer(_ interface{}) {
 		common.WithParams(c.getUrlMap()),
 		common.WithParamsValue(constant.BEAN_NAME_KEY, c.id),
 	)
-
+	if c.ForceTag {
+		cfgURL.AddParam(constant.ForceUseTag, "true")
+	}
 	if c.Url != "" {
 		// 1. user specified URL, could be peer-to-peer address, or register center's address.
 		urlStrings := gxstrings.RegSplit(c.Url, "\\s*[;]+\\s*")
@@ -185,6 +188,10 @@ func (c *ReferenceConfig) getUrlMap() url.Values {
 	urlMap.Set(constant.VERSION_KEY, c.Version)
 	urlMap.Set(constant.GENERIC_KEY, strconv.FormatBool(c.Generic))
 	urlMap.Set(constant.ROLE_KEY, strconv.Itoa(common.CONSUMER))
+
+	urlMap.Set(constant.RELEASE_KEY, "dubbo-golang-"+constant.Version)
+	urlMap.Set(constant.SIDE_KEY, (common.RoleType(common.CONSUMER)).Role())
+
 	if len(c.RequestTimeout) != 0 {
 		urlMap.Set(constant.TIMEOUT_KEY, c.RequestTimeout)
 	}
diff --git a/config/registry_config.go b/config/registry_config.go
index 4e4b6e97d79a9402616b6cac954f7a09b2973dcc..e877a2c19dd0c4dabdce9f7ee65c2404b82d615e 100644
--- a/config/registry_config.go
+++ b/config/registry_config.go
@@ -36,14 +36,15 @@ import (
 // RegistryConfig ...
 type RegistryConfig struct {
 	Protocol string `required:"true" yaml:"protocol"  json:"protocol,omitempty" property:"protocol"`
-	//I changed "type" to "protocol" ,the same as "protocol" field in java class RegistryConfig
+	// 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"`
-	//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"`
-	Params   map[string]string `yaml:"params" json:"params,omitempty" property:"params"`
+	// 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"`
 }
 
 // UnmarshalYAML ...
@@ -70,9 +71,11 @@ func loadRegistries(targetRegistries string, registries map[string]*RegistryConf
 	for k, registryConf := range registries {
 		target := false
 
-		// if user not config targetRegistries,default load all
-		// Notice:in func "func Split(s, sep string) []string"  comment : if s does not contain sep and sep is not empty, SplitAfter returns a slice of length 1 whose only element is s.
-		// So we have to add the condition when targetRegistries string is not set (it will be "" when not set)
+		// if user not config targetRegistries, default load all
+		// Notice: in func "func Split(s, sep string) []string" comment:
+		// if s does not contain sep and sep is not empty, SplitAfter returns
+		// a slice of length 1 whose only element is s. So we have to add the
+		// condition when targetRegistries string is not set (it will be "" when not set)
 		if len(trSlice) == 0 || (len(trSlice) == 1 && trSlice[0] == "") {
 			target = true
 		} else {
@@ -86,29 +89,24 @@ func loadRegistries(targetRegistries string, registries map[string]*RegistryConf
 		}
 
 		if target {
-			var (
-				url common.URL
-				err error
-			)
-
 			addresses := strings.Split(registryConf.Address, ",")
 			address := addresses[0]
-			address = traslateRegistryConf(address, registryConf)
-			url, err = common.NewURL(constant.REGISTRY_PROTOCOL+"://"+address,
+			address = translateRegistryConf(address, registryConf)
+			url, err := common.NewURL(constant.REGISTRY_PROTOCOL+"://"+address,
 				common.WithParams(registryConf.getUrlMap(roleType)),
+				common.WithParamsValue("simplified", strconv.FormatBool(registryConf.Simplified)),
 				common.WithUsername(registryConf.Username),
 				common.WithPassword(registryConf.Password),
 				common.WithLocation(registryConf.Address),
 			)
 
 			if err != nil {
-				logger.Errorf("The registry id:%s url is invalid , error: %#v", k, err)
+				logger.Errorf("The registry id: %s url is invalid, error: %#v", k, err)
 				panic(err)
 			} else {
 				urls = append(urls, &url)
 			}
 		}
-
 	}
 
 	return urls
@@ -123,15 +121,14 @@ func (c *RegistryConfig) getUrlMap(roleType common.RoleType) url.Values {
 	for k, v := range c.Params {
 		urlMap.Set(k, v)
 	}
-
 	return urlMap
 }
 
-func traslateRegistryConf(address string, registryConf *RegistryConfig) string {
+func translateRegistryConf(address string, registryConf *RegistryConfig) string {
 	if strings.Contains(address, "://") {
 		translatedUrl, err := url.Parse(address)
 		if err != nil {
-			logger.Errorf("The registry  url is invalid , error: %#v", err)
+			logger.Errorf("The registry url is invalid, error: %#v", err)
 			panic(err)
 		}
 		address = translatedUrl.Host
diff --git a/config/condition_router_config.go b/config/router_config.go
similarity index 89%
rename from config/condition_router_config.go
rename to config/router_config.go
index 87e835108efd2ccac4f829386ec44a3916339f85..0670ee9c20f618021d1d574344a0df85d837bd66 100644
--- a/config/condition_router_config.go
+++ b/config/router_config.go
@@ -28,13 +28,14 @@ import (
 	"github.com/apache/dubbo-go/common/yaml"
 )
 
-//RouterInit Load config file to init router config
+// 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)
 		if e == nil {
@@ -42,7 +43,7 @@ func RouterInit(confRouterFile string) error {
 			directory.AddRouterURLSet(&url)
 			return nil
 		}
-		logger.Warnf("router config type %s create fail \n", k)
+		logger.Warnf("router config type %s create fail {%v}\n", k, e)
 	}
 	return perrors.Errorf("no file router exists for parse %s , implement router.FIleRouterFactory please.", confRouterFile)
 }
diff --git a/config/condition_router_config_test.go b/config/router_config_test.go
similarity index 100%
rename from config/condition_router_config_test.go
rename to config/router_config_test.go
diff --git a/config/service_config.go b/config/service_config.go
index 7d97fa4d1e95bd79e051f77deaeafa1afcc58b0f..50bf5e12c3247340f177a84c72446383ec5c3450 100644
--- a/config/service_config.go
+++ b/config/service_config.go
@@ -69,6 +69,7 @@ type ServiceConfig struct {
 	ExecuteLimitRejectedHandler string            `yaml:"execute.limit.rejected.handler" json:"execute.limit.rejected.handler,omitempty" property:"execute.limit.rejected.handler"`
 	Auth                        string            `yaml:"auth" json:"auth,omitempty" property:"auth"`
 	ParamSign                   string            `yaml:"param.sign" json:"param.sign,omitempty" property:"param.sign"`
+	Tag                         string            `yaml:"tag" json:"tag,omitempty" property:"tag"`
 
 	unexported    *atomic.Bool
 	exported      *atomic.Bool
@@ -128,7 +129,7 @@ func (c *ServiceConfig) Export() error {
 	}
 	for _, proto := range protocolConfigs {
 		// registry the service reflect
-		methods, err := common.ServiceMap.Register(proto.Name, c.rpcService)
+		methods, err := common.ServiceMap.Register(c.InterfaceName, proto.Name, c.rpcService)
 		if err != nil {
 			err := perrors.Errorf("The service %v  export the protocol %v error! Error message is %v .", c.InterfaceName, proto.Name, err.Error())
 			logger.Errorf(err.Error())
@@ -144,7 +145,9 @@ func (c *ServiceConfig) Export() error {
 			common.WithMethods(strings.Split(methods, ",")),
 			common.WithToken(c.Token),
 		)
-
+		if len(c.Tag) > 0 {
+			ivkURL.AddParam(constant.Tagkey, c.Tag)
+		}
 		if len(regUrls) > 0 {
 			for _, regUrl := range regUrls {
 				regUrl.SubURL = ivkURL
@@ -193,6 +196,9 @@ func (c *ServiceConfig) getUrlMap() url.Values {
 	urlMap.Set(constant.GROUP_KEY, c.Group)
 	urlMap.Set(constant.VERSION_KEY, c.Version)
 	urlMap.Set(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER))
+	urlMap.Set(constant.RELEASE_KEY, "dubbo-golang-"+constant.Version)
+	urlMap.Set(constant.SIDE_KEY, (common.RoleType(common.PROVIDER)).Role())
+
 	// application info
 	urlMap.Set(constant.APPLICATION_KEY, providerConfig.ApplicationConfig.Name)
 	urlMap.Set(constant.ORGANIZATION_KEY, providerConfig.ApplicationConfig.Organization)
diff --git a/config_center/apollo/impl.go b/config_center/apollo/impl.go
index 3b5d1f4ebe8e728eb4d21e78ab675036c1ca9f63..b049d334bca7e5191caaf9674734e731bc709ba2 100644
--- a/config_center/apollo/impl.go
+++ b/config_center/apollo/impl.go
@@ -25,7 +25,8 @@ import (
 )
 
 import (
-	"github.com/pkg/errors"
+	gxset "github.com/dubbogo/gost/container/set"
+	perrors "github.com/pkg/errors"
 	"github.com/zouyx/agollo"
 )
 
@@ -119,7 +120,7 @@ func getNamespaceName(namespace string, configFileFormat agollo.ConfigFileFormat
 func (c *apolloConfiguration) GetInternalProperty(key string, opts ...cc.Option) (string, error) {
 	config := agollo.GetConfig(c.appConf.NamespaceName)
 	if config == nil {
-		return "", errors.New(fmt.Sprintf("nothing in namespace:%s ", key))
+		return "", perrors.New(fmt.Sprintf("nothing in namespace:%s ", key))
 	}
 	return config.GetStringValue(key, ""), nil
 }
@@ -128,6 +129,16 @@ func (c *apolloConfiguration) GetRule(key string, opts ...cc.Option) (string, er
 	return c.GetInternalProperty(key, opts...)
 }
 
+// PublishConfig will publish the config with the (key, group, value) pair
+func (c *apolloConfiguration) PublishConfig(string, string, string) error {
+	return perrors.New("unsupport operation")
+}
+
+// GetConfigKeysByGroup will return all keys with the group
+func (c *apolloConfiguration) GetConfigKeysByGroup(group string) (*gxset.HashSet, error) {
+	return nil, perrors.New("unsupport operation")
+}
+
 func (c *apolloConfiguration) GetProperties(key string, opts ...cc.Option) (string, error) {
 	/**
 	 * when group is not null, we are getting startup configs(config file) from Config Center, for example:
@@ -135,7 +146,7 @@ func (c *apolloConfiguration) GetProperties(key string, opts ...cc.Option) (stri
 	 */
 	config := agollo.GetConfig(key)
 	if config == nil {
-		return "", errors.New(fmt.Sprintf("nothing in namespace:%s ", key))
+		return "", perrors.New(fmt.Sprintf("nothing in namespace:%s ", key))
 	}
 	return config.GetContent(agollo.Properties), nil
 }
diff --git a/config_center/dynamic_configuration.go b/config_center/dynamic_configuration.go
index d6c3b06b327f16c709b09121e589db6694d3663e..9013d7140e757520f2e8f048ce53a5ac2a13f982 100644
--- a/config_center/dynamic_configuration.go
+++ b/config_center/dynamic_configuration.go
@@ -21,14 +21,18 @@ import (
 	"time"
 )
 
+import (
+	gxset "github.com/dubbogo/gost/container/set"
+)
+
 import (
 	"github.com/apache/dubbo-go/common"
 	"github.com/apache/dubbo-go/config_center/parser"
 )
 
-//////////////////////////////////////////
+// ////////////////////////////////////////
 // DynamicConfiguration
-//////////////////////////////////////////
+// ////////////////////////////////////////
 const (
 	// DEFAULT_GROUP: default group
 	DEFAULT_GROUP = "dubbo"
@@ -42,14 +46,20 @@ type DynamicConfiguration interface {
 	SetParser(parser.ConfigurationParser)
 	AddListener(string, ConfigurationListener, ...Option)
 	RemoveListener(string, ConfigurationListener, ...Option)
-	//GetProperties get properties file
+	// GetProperties get properties file
 	GetProperties(string, ...Option) (string, error)
 
-	//GetRule get Router rule properties file
+	// GetRule get Router rule properties file
 	GetRule(string, ...Option) (string, error)
 
-	//GetInternalProperty get value by key in Default properties file(dubbo.properties)
+	// GetInternalProperty get value by key in Default properties file(dubbo.properties)
 	GetInternalProperty(string, ...Option) (string, error)
+
+	// PublishConfig will publish the config with the (key, group, value) pair
+	PublishConfig(string, string, string) error
+
+	// GetConfigKeysByGroup will return all keys with the group
+	GetConfigKeysByGroup(group string) (*gxset.HashSet, error)
 }
 
 // Options ...
@@ -75,7 +85,7 @@ func WithTimeout(time time.Duration) Option {
 	}
 }
 
-//GetRuleKey The format is '{interfaceName}:[version]:[group]'
+// GetRuleKey The format is '{interfaceName}:[version]:[group]'
 func GetRuleKey(url common.URL) string {
 	return url.ColonSeparatedKey()
 }
diff --git a/config_center/mock_dynamic_config.go b/config_center/mock_dynamic_config.go
index 4d972b629abb7abd7cc0d0018026e4ccc04a1e4f..59c788b65bce2a4773975ea1a96a314649781832 100644
--- a/config_center/mock_dynamic_config.go
+++ b/config_center/mock_dynamic_config.go
@@ -22,6 +22,7 @@ import (
 )
 
 import (
+	gxset "github.com/dubbogo/gost/container/set"
 	"gopkg.in/yaml.v2"
 )
 
@@ -81,6 +82,16 @@ func (f *MockDynamicConfigurationFactory) GetDynamicConfiguration(_ *common.URL)
 
 }
 
+// PublishConfig will publish the config with the (key, group, value) pair
+func (c *MockDynamicConfiguration) PublishConfig(string, string, string) error {
+	return nil
+}
+
+// GetConfigKeysByGroup will return all keys with the group
+func (c *MockDynamicConfiguration) GetConfigKeysByGroup(group string) (*gxset.HashSet, error) {
+	return gxset.NewSet(c.content), nil
+}
+
 // MockDynamicConfiguration ...
 type MockDynamicConfiguration struct {
 	parser   parser.ConfigurationParser
diff --git a/config_center/nacos/impl.go b/config_center/nacos/impl.go
index 60ab89b003ff62016b9137223425c1051356975f..007b8be142274b63ceb56dd00399cdaf29c3746d 100644
--- a/config_center/nacos/impl.go
+++ b/config_center/nacos/impl.go
@@ -18,10 +18,12 @@
 package nacos
 
 import (
+	"strings"
 	"sync"
 )
 
 import (
+	gxset "github.com/dubbogo/gost/container/set"
 	"github.com/nacos-group/nacos-sdk-go/vo"
 	perrors "github.com/pkg/errors"
 )
@@ -74,7 +76,7 @@ func (n *nacosDynamicConfiguration) RemoveListener(key string, listener config_c
 	n.removeListener(key, listener)
 }
 
-//nacos distinguishes configuration files based on group and dataId. defalut group = "dubbo" and dataId = key
+// GetProperties nacos distinguishes configuration files based on group and dataId. defalut group = "dubbo" and dataId = key
 func (n *nacosDynamicConfiguration) GetProperties(key string, opts ...config_center.Option) (string, error) {
 	return n.GetRule(key, opts...)
 }
@@ -84,6 +86,33 @@ func (n *nacosDynamicConfiguration) GetInternalProperty(key string, opts ...conf
 	return n.GetProperties(key, opts...)
 }
 
+// PublishConfig will publish the config with the (key, group, value) pair
+func (n *nacosDynamicConfiguration) PublishConfig(key string, group string, value string) error {
+
+	group = n.resolvedGroup(group)
+
+	ok, err := (*n.client.Client()).PublishConfig(vo.ConfigParam{
+		DataId:  key,
+		Group:   group,
+		Content: value,
+	})
+
+	if err != nil {
+		return perrors.WithStack(err)
+	}
+	if !ok {
+		return perrors.New("publish config to Nocos failed")
+	}
+	return nil
+}
+
+// GetConfigKeysByGroup will return all keys with the group
+func (n *nacosDynamicConfiguration) GetConfigKeysByGroup(group string) (*gxset.HashSet, error) {
+	// TODO (the golang client of nacos does not support batch API)
+	// we should build a issue and then think about how to resolve this problem
+	return nil, perrors.New("unsupport operation, wait for implement")
+}
+
 // GetRule Get router rule
 func (n *nacosDynamicConfiguration) GetRule(key string, opts ...config_center.Option) (string, error) {
 	tmpOpts := &config_center.Options{}
@@ -92,12 +121,12 @@ func (n *nacosDynamicConfiguration) GetRule(key string, opts ...config_center.Op
 	}
 	content, err := (*n.client.Client()).GetConfig(vo.ConfigParam{
 		DataId: key,
-		Group:  tmpOpts.Group,
+		Group:  n.resolvedGroup(tmpOpts.Group),
 	})
 	if err != nil {
 		return "", perrors.WithStack(err)
 	} else {
-		return string(content), nil
+		return content, nil
 	}
 }
 
@@ -145,6 +174,15 @@ func (n *nacosDynamicConfiguration) Destroy() {
 	n.closeConfigs()
 }
 
+// resolvedGroup will regular the group. Now, it will replace the '/' with '-'.
+// '/' is a special character for nacos
+func (n *nacosDynamicConfiguration) resolvedGroup(group string) string {
+	if len(group) <= 0 {
+		return group
+	}
+	return strings.ReplaceAll(group, "/", "-")
+}
+
 // IsAvailable Get available status
 func (n *nacosDynamicConfiguration) IsAvailable() bool {
 	select {
@@ -155,12 +193,12 @@ func (n *nacosDynamicConfiguration) IsAvailable() bool {
 	}
 }
 
-func (r *nacosDynamicConfiguration) closeConfigs() {
-	r.cltLock.Lock()
-	client := r.client
-	r.client = nil
-	r.cltLock.Unlock()
+func (n *nacosDynamicConfiguration) closeConfigs() {
+	n.cltLock.Lock()
+	client := n.client
+	n.client = nil
+	n.cltLock.Unlock()
 	// Close the old client first to close the tmp node
 	client.Close()
-	logger.Infof("begin to close provider nacos client")
+	logger.Infof("begin to close provider n client")
 }
diff --git a/config_center/nacos/impl_test.go b/config_center/nacos/impl_test.go
index b4e6f1d0259979eba28dd81e8f480ab4ae03a39f..4032c91cda512b649140db6eea3dc11eeb482f27 100644
--- a/config_center/nacos/impl_test.go
+++ b/config_center/nacos/impl_test.go
@@ -60,12 +60,7 @@ func runMockConfigServer(configHandler func(http.ResponseWriter, *http.Request),
 
 func mockCommonNacosServer() *httptest.Server {
 	return runMockConfigServer(func(writer http.ResponseWriter, request *http.Request) {
-		data := `
-	dubbo.service.com.ikurento.user.UserProvider.cluster=failback
-	dubbo.service.com.ikurento.user.UserProvider.protocol=myDubbo1
-	dubbo.protocols.myDubbo.port=20000
-	dubbo.protocols.myDubbo.name=dubbo
-`
+		data := "true"
 		fmt.Fprintf(writer, "%s", data)
 	}, func(writer http.ResponseWriter, request *http.Request) {
 		data := `dubbo.properties%02dubbo%02dubbo.service.com.ikurento.user.UserProvider.cluster=failback`
@@ -93,6 +88,16 @@ func Test_GetConfig(t *testing.T) {
 	assert.NoError(t, err)
 }
 
+func TestNacosDynamicConfiguration_PublishConfig(t *testing.T) {
+	nacos, err := initNacosData(t)
+	assert.Nil(t, err)
+	key := "myKey"
+	group := "/custom/a/b"
+	value := "MyValue"
+	err = nacos.PublishConfig(key, group, value)
+	assert.Nil(t, err)
+}
+
 func Test_AddListener(t *testing.T) {
 	nacos, err := initNacosData(t)
 	assert.NoError(t, err)
diff --git a/config_center/nacos/listener.go b/config_center/nacos/listener.go
index 25c586586c7202e42ff44d6104e8132961add25a..de74cff8f64683a47278825b670352a04b69b791 100644
--- a/config_center/nacos/listener.go
+++ b/config_center/nacos/listener.go
@@ -35,11 +35,11 @@ func callback(listener config_center.ConfigurationListener, namespace, group, da
 	listener.Process(&config_center.ConfigChangeEvent{Key: dataId, Value: data, ConfigType: remoting.EventTypeUpdate})
 }
 
-func (l *nacosDynamicConfiguration) addListener(key string, listener config_center.ConfigurationListener) {
-	_, loaded := l.keyListeners.Load(key)
+func (n *nacosDynamicConfiguration) addListener(key string, listener config_center.ConfigurationListener) {
+	_, loaded := n.keyListeners.Load(key)
 	if !loaded {
 		_, cancel := context.WithCancel(context.Background())
-		err := (*l.client.Client()).ListenConfig(vo.ConfigParam{
+		err := (*n.client.Client()).ListenConfig(vo.ConfigParam{
 			DataId: key,
 			Group:  "dubbo",
 			OnChange: func(namespace, group, dataId, data string) {
@@ -49,14 +49,14 @@ func (l *nacosDynamicConfiguration) addListener(key string, listener config_cent
 		logger.Errorf("nacos : listen config fail, error:%v ", err)
 		newListener := make(map[config_center.ConfigurationListener]context.CancelFunc)
 		newListener[listener] = cancel
-		l.keyListeners.Store(key, newListener)
+		n.keyListeners.Store(key, newListener)
 	} else {
 		// TODO check goroutine alive, but this version of go_nacos_sdk is not support.
 		logger.Infof("profile:%s. this profile is already listening", key)
 	}
 }
 
-func (l *nacosDynamicConfiguration) removeListener(key string, listener config_center.ConfigurationListener) {
+func (n *nacosDynamicConfiguration) removeListener(key string, listener config_center.ConfigurationListener) {
 	// TODO: not supported in current go_nacos_sdk version
 	logger.Warn("not supported in current go_nacos_sdk version")
 }
diff --git a/config_center/zookeeper/impl.go b/config_center/zookeeper/impl.go
index 404243d4751146d1edc9a61d51cbb81d73c2ffb1..0a1ce35306dab98363ca475cd5d1b0648e924b90 100644
--- a/config_center/zookeeper/impl.go
+++ b/config_center/zookeeper/impl.go
@@ -25,6 +25,7 @@ import (
 
 import (
 	"github.com/dubbogo/go-zookeeper/zk"
+	gxset "github.com/dubbogo/gost/container/set"
 	perrors "github.com/pkg/errors"
 )
 
@@ -39,8 +40,9 @@ import (
 
 const (
 	// ZkClient
-	//zookeeper client name
-	ZkClient = "zk config_center"
+	// zookeeper client name
+	ZkClient      = "zk config_center"
+	pathSeparator = "/"
 )
 
 type zookeeperDynamicConfiguration struct {
@@ -74,7 +76,7 @@ func newZookeeperDynamicConfiguration(url *common.URL) (*zookeeperDynamicConfigu
 	c.cacheListener = NewCacheListener(c.rootPath)
 
 	err = c.client.Create(c.rootPath)
-	c.listener.ListenServiceEvent(c.rootPath, c.cacheListener)
+	c.listener.ListenServiceEvent(url, c.rootPath, c.cacheListener)
 	return c, err
 
 }
@@ -100,7 +102,7 @@ func newMockZookeeperDynamicConfiguration(url *common.URL, opts ...zookeeper.Opt
 	c.cacheListener = NewCacheListener(c.rootPath)
 
 	err = c.client.Create(c.rootPath)
-	go c.listener.ListenServiceEvent(c.rootPath, c.cacheListener)
+	go c.listener.ListenServiceEvent(url, c.rootPath, c.cacheListener)
 	return tc, c, err
 
 }
@@ -143,11 +145,39 @@ func (c *zookeeperDynamicConfiguration) GetProperties(key string, opts ...config
 	return string(content), nil
 }
 
-//For zookeeper, getConfig and getConfigs have the same meaning.
+// GetInternalProperty For zookeeper, getConfig and getConfigs have the same meaning.
 func (c *zookeeperDynamicConfiguration) GetInternalProperty(key string, opts ...config_center.Option) (string, error) {
 	return c.GetProperties(key, opts...)
 }
 
+// PublishConfig will put the value into Zk with specific path
+func (c *zookeeperDynamicConfiguration) PublishConfig(key string, group string, value string) error {
+	path := c.getPath(key, group)
+	err := c.client.CreateWithValue(path, []byte(value))
+	if err != nil {
+		return perrors.WithStack(err)
+	}
+	return nil
+}
+
+// GetConfigKeysByGroup will return all keys with the group
+func (c *zookeeperDynamicConfiguration) GetConfigKeysByGroup(group string) (*gxset.HashSet, error) {
+	path := c.getPath("", group)
+	result, err := c.client.GetChildren(path)
+	if err != nil {
+		return nil, perrors.WithStack(err)
+	}
+
+	if len(result) == 0 {
+		return nil, perrors.New("could not find keys with group: " + group)
+	}
+	set := gxset.NewSet()
+	for _, e := range result {
+		set.Add(e)
+	}
+	return set, nil
+}
+
 func (c *zookeeperDynamicConfiguration) GetRule(key string, opts ...config_center.Option) (string, error) {
 	return c.GetProperties(key, opts...)
 }
@@ -214,3 +244,17 @@ func (c *zookeeperDynamicConfiguration) closeConfigs() {
 func (c *zookeeperDynamicConfiguration) RestartCallBack() bool {
 	return true
 }
+
+func (c *zookeeperDynamicConfiguration) getPath(key string, group string) string {
+	if len(key) == 0 {
+		return c.buildPath(group)
+	}
+	return c.buildPath(group) + pathSeparator + key
+}
+
+func (c *zookeeperDynamicConfiguration) buildPath(group string) string {
+	if len(group) == 0 {
+		group = config_center.DEFAULT_GROUP
+	}
+	return c.rootPath + pathSeparator + group
+}
diff --git a/config_center/zookeeper/impl_test.go b/config_center/zookeeper/impl_test.go
index 22e15193cba1b533a2b1b965a44bf9665a6a4e5e..30389122a3a06ee260f2ed8b21057523137995d5 100644
--- a/config_center/zookeeper/impl_test.go
+++ b/config_center/zookeeper/impl_test.go
@@ -24,6 +24,7 @@ import (
 
 import (
 	"github.com/dubbogo/go-zookeeper/zk"
+	gxset "github.com/dubbogo/gost/container/set"
 	"github.com/stretchr/testify/assert"
 )
 
@@ -156,6 +157,26 @@ func Test_RemoveListener(t *testing.T) {
 	assert.Equal(t, "", listener.event)
 }
 
+func TestZookeeperDynamicConfiguration_PublishConfig(t *testing.T) {
+	value := "Test Data"
+	customGroup := "Custom Group"
+	key := "myKey"
+	ts, zk := initZkData(config_center.DEFAULT_GROUP, t)
+	defer ts.Stop()
+	err := zk.PublishConfig(key, customGroup, value)
+	assert.Nil(t, err)
+	result, err := zk.GetInternalProperty("myKey", config_center.WithGroup(customGroup))
+	assert.Nil(t, err)
+	assert.Equal(t, value, result)
+
+	var keys *gxset.HashSet
+	keys, err = zk.GetConfigKeysByGroup(customGroup)
+	assert.Nil(t, err)
+	assert.Equal(t, 1, keys.Size())
+	assert.True(t, keys.Contains(key))
+
+}
+
 type mockDataListener struct {
 	wg    sync.WaitGroup
 	event string
diff --git a/doc/pic/arch/dubbo-go-arch.png b/doc/pic/arch/dubbo-go-arch.png
index 87726d88484c23d6395023bb10e86009d59a1fd7..e5f192715216257929cf4a550a1adf3588ab0e0f 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
new file mode 100644
index 0000000000000000000000000000000000000000..a5285c95570afa13212f7ee6eac5510f20243c3c
Binary files /dev/null and b/doc/pic/arch/dubbo-go-ext.png differ
diff --git a/filter/filter_impl/generic_service_filter_test.go b/filter/filter_impl/generic_service_filter_test.go
index 37c6af7450a75449fce51182684be2f619eda9d8..2a911659f068b53836b87af5caf5773d8ac5f119 100644
--- a/filter/filter_impl/generic_service_filter_test.go
+++ b/filter/filter_impl/generic_service_filter_test.go
@@ -96,7 +96,7 @@ func TestGenericServiceFilter_Invoke(t *testing.T) {
 			hessian.Object("222")},
 	}
 	s := &TestService{}
-	_, _ = common.ServiceMap.Register("testprotocol", s)
+	_, _ = common.ServiceMap.Register("TestService", "testprotocol", s)
 	rpcInvocation := invocation.NewRPCInvocation(methodName, aurguments, nil)
 	filter := GetGenericServiceFilter()
 	url, _ := common.NewURL("testprotocol://127.0.0.1:20000/com.test.Path")
diff --git a/filter/filter_impl/tps/tps_limit_strategy_mock.go b/filter/filter_impl/tps/tps_limit_strategy_mock.go
index 18809fccc9d2cd3de0820c96b0335729796e06f8..c228c7349ce6ad305051e0ba9b26dddee8c12c71 100644
--- a/filter/filter_impl/tps/tps_limit_strategy_mock.go
+++ b/filter/filter_impl/tps/tps_limit_strategy_mock.go
@@ -15,22 +15,6 @@
  * limitations under the License.
  */
 
-//  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.
-//
-
 // Code generated by MockGen. DO NOT EDIT.
 // Source: tps_limit_strategy.go
 
diff --git a/filter/filter_impl/tps/tps_limiter_mock.go b/filter/filter_impl/tps/tps_limiter_mock.go
index 131a2e512175e098da16365d2d714a400a7e080f..b49084f28e7d4b62d64762170b6dfbbec4a4de96 100644
--- a/filter/filter_impl/tps/tps_limiter_mock.go
+++ b/filter/filter_impl/tps/tps_limiter_mock.go
@@ -15,22 +15,6 @@
  * limitations under the License.
  */
 
-//  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.
-//
-
 // Code generated by MockGen. DO NOT EDIT.
 // Source: tps_limiter.go
 
diff --git a/filter/handler/rejected_execution_handler_mock.go b/filter/handler/rejected_execution_handler_mock.go
index 469c06b6b9692c583a045a5af09fd0cc17403aaa..bff54769cb007ed2fce5a7623c96edc370e63903 100644
--- a/filter/handler/rejected_execution_handler_mock.go
+++ b/filter/handler/rejected_execution_handler_mock.go
@@ -15,22 +15,6 @@
  * limitations under the License.
  */
 
-//  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.
-//
-
 // Code generated by MockGen. DO NOT EDIT.
 // Source: rejected_execution_handler.go
 
diff --git a/go.mod b/go.mod
index 83091cf8b985d2dec3d1d53967593fc9dee0d17e..1f8edc647c4b65e15c1bab266091295d7ed0b96d 100644
--- a/go.mod
+++ b/go.mod
@@ -4,7 +4,7 @@ require (
 	github.com/Workiva/go-datastructures v1.0.50
 	github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5
 	github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190802083043-4cd0c391755e // indirect
-	github.com/apache/dubbo-go-hessian2 v1.4.0
+	github.com/apache/dubbo-go-hessian2 v1.5.0
 	github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23 // indirect
 	github.com/coreos/bbolt v1.3.3 // indirect
 	github.com/coreos/etcd v3.3.13+incompatible
@@ -12,9 +12,9 @@ require (
 	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.3
+	github.com/dubbogo/getty v1.3.4
 	github.com/dubbogo/go-zookeeper v1.0.0
-	github.com/dubbogo/gost v1.5.2
+	github.com/dubbogo/gost v1.8.0
 	github.com/emicklei/go-restful/v3 v3.0.0
 	github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239 // indirect
 	github.com/go-errors/errors v1.0.1 // indirect
@@ -32,13 +32,15 @@ require (
 	github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 // indirect
 	github.com/jinzhu/copier v0.0.0-20190625015134-976e0346caa8
 	github.com/jonboulle/clockwork v0.1.0 // indirect
+	github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8 // indirect
+	github.com/juju/testing v0.0.0-20191001232224-ce9dec17d28b // indirect
 	github.com/lestrrat/go-envload v0.0.0-20180220120943-6ed08b54a570 // indirect
 	github.com/lestrrat/go-file-rotatelogs v0.0.0-20180223000712-d3151e2a480f // indirect
 	github.com/lestrrat/go-strftime v0.0.0-20180220042222-ba3bf9c1d042 // indirect
 	github.com/magiconair/properties v1.8.1
 	github.com/mitchellh/mapstructure v1.1.2
 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd
-	github.com/nacos-group/nacos-sdk-go v0.0.0-20190723125407-0242d42e3dbb
+	github.com/nacos-group/nacos-sdk-go v0.0.0-20191128082542-fe1b325b125c
 	github.com/opentracing/opentracing-go v1.1.0
 	github.com/pkg/errors v0.8.1
 	github.com/prometheus/client_golang v1.1.0
diff --git a/go.sum b/go.sum
index 813496b6eea7838756e7f62f2b31aa58cd858356..83a96618e7dd70164b2eca66597bb20e18346fdd 100644
--- a/go.sum
+++ b/go.sum
@@ -38,8 +38,8 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF
 github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190802083043-4cd0c391755e h1:MSuLXx/mveDbpDNhVrcWTMeV4lbYWKcyO4rH+jAxmX0=
 github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190802083043-4cd0c391755e/go.mod h1:myCDvQSzCW+wB1WAlocEru4wMGJxy+vlxHdhegi1CDQ=
 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.4.0 h1:Cb9FQVTy3G93dnDr7P93U8DeKFYpDTJjQp44JG5TafA=
-github.com/apache/dubbo-go-hessian2 v1.4.0/go.mod h1:VwEnsOMidkM1usya2uPfGpSLO9XUF//WQcWn3y+jFz8=
+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/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/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
@@ -106,13 +106,15 @@ github.com/docker/go-connections v0.3.0 h1:3lOnM9cSzgGwx8VfK/NGOW5fLQ0GjIlCkaktF
 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.3 h1:8m4zZBqFHO+NmhH7rMPlFuuYRVjcPD7cUhumevqMZZs=
-github.com/dubbogo/getty v1.3.3/go.mod h1:U92BDyJ6sW9Jpohr2Vlz8w2uUbIbNZ3d+6rJvFTSPp0=
+github.com/dubbogo/getty v1.3.4 h1:5TvH213pnSIKYzY7IK8TT/r6yr5uPTB/U6YNLT+GsU0=
+github.com/dubbogo/getty v1.3.4/go.mod h1:36f+gH/ekaqcDWKbxNBQk9b9HXcGtaI6YHxp4YTntX8=
 github.com/dubbogo/go-zookeeper v1.0.0 h1:RsYdlGwhDW+iKXM3eIIcvt34P2swLdmQfuIJxsHlGoM=
 github.com/dubbogo/go-zookeeper v1.0.0/go.mod h1:fn6n2CAEer3novYgk9ULLwAjuV8/g4DdC2ENwRb6E+c=
 github.com/dubbogo/gost v1.5.1/go.mod h1:pPTjVyoJan3aPxBPNUX0ADkXjPibLo+/Ib0/fADXSG8=
 github.com/dubbogo/gost v1.5.2 h1:ri/03971hdpnn3QeCU+4UZgnRNGDXLDGDucR/iozZm8=
 github.com/dubbogo/gost v1.5.2/go.mod h1:pPTjVyoJan3aPxBPNUX0ADkXjPibLo+/Ib0/fADXSG8=
+github.com/dubbogo/gost v1.8.0 h1:9ACbQe5OwMjqtinQcNJC5xp16kky27OsfSGw5L9A6vw=
+github.com/dubbogo/gost v1.8.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=
@@ -322,6 +324,12 @@ github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u
 github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
 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=
@@ -381,8 +389,8 @@ github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9
 github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
 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.0.0-20190723125407-0242d42e3dbb h1:lbmvw8r9W55w+aQgWn35W1nuleRIECMoqUrmwAOAvoI=
-github.com/nacos-group/nacos-sdk-go v0.0.0-20190723125407-0242d42e3dbb/go.mod h1:CEkSvEpoveoYjA81m4HNeYQ0sge0LFGKSEqO3JKHllo=
+github.com/nacos-group/nacos-sdk-go v0.0.0-20191128082542-fe1b325b125c h1:WoCa3AvgQMVKNs+RIFlWPRgY9QVJwUxJDrGxHs0fcRo=
+github.com/nacos-group/nacos-sdk-go v0.0.0-20191128082542-fe1b325b125c/go.mod h1:CEkSvEpoveoYjA81m4HNeYQ0sge0LFGKSEqO3JKHllo=
 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=
diff --git a/metadata/definition/definition.go b/metadata/definition/definition.go
new file mode 100644
index 0000000000000000000000000000000000000000..ead984345efde1ddd1d54b7599fd9d5584947ea2
--- /dev/null
+++ b/metadata/definition/definition.go
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package definition
+
+type ServiceDefinition struct {
+	CanonicalName string
+	CodeSource    string
+	Methods       []MethodDefinition
+	Types         []TypeDefinition
+}
+
+type MethodDefinition struct {
+	Name           string
+	ParameterTypes []string
+	ReturnType     string
+	Parameters     []TypeDefinition
+}
+
+type TypeDefinition struct {
+	Id              string
+	Type            string
+	Items           []TypeDefinition
+	Enums           []string
+	Properties      map[string]TypeDefinition
+	TypeBuilderName string
+}
diff --git a/metadata/exporter.go b/metadata/exporter.go
new file mode 100644
index 0000000000000000000000000000000000000000..5d47f8bd808ec802ba73c7db73d22c78c675d12a
--- /dev/null
+++ b/metadata/exporter.go
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package metadata
+
+import (
+	"github.com/apache/dubbo-go/common"
+)
+
+type MetadataExporter interface {
+	Export() MetadataExporter
+	Unexport() MetadataExporter
+	GetExportedURLs() []*common.URL
+	IsExported() bool
+}
diff --git a/metadata/identifier/base_metadata_identifier.go b/metadata/identifier/base_metadata_identifier.go
new file mode 100644
index 0000000000000000000000000000000000000000..a314671055be523844fd7d8f9589b8b6031632bc
--- /dev/null
+++ b/metadata/identifier/base_metadata_identifier.go
@@ -0,0 +1,92 @@
+/*
+ * 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 identifier
+
+import (
+	"encoding/base64"
+)
+
+import (
+	"github.com/apache/dubbo-go/common/constant"
+)
+
+type BaseMetadataIdentifier interface {
+	getFilePathKey(params ...string) string
+	getIdentifierKey(params ...string) string
+}
+
+type BaseServiceMetadataIdentifier struct {
+	serviceInterface string
+	version          string
+	group            string
+	side             string
+}
+
+// joinParams...
+func joinParams(joinChar string, params []string) string {
+	var joinedStr string
+	for _, param := range params {
+		joinedStr += joinChar
+		joinedStr += param
+	}
+	return joinedStr
+}
+
+// getIdentifierKey...
+func (mdi *BaseServiceMetadataIdentifier) getIdentifierKey(params ...string) string {
+	return mdi.serviceInterface +
+		constant.KEY_SEPARATOR + mdi.version +
+		constant.KEY_SEPARATOR + mdi.group +
+		constant.KEY_SEPARATOR + mdi.side +
+		joinParams(constant.KEY_SEPARATOR, params)
+}
+
+// getFilePathKey...
+func (mdi *BaseServiceMetadataIdentifier) getFilePathKey(params ...string) string {
+	path := serviceToPath(mdi.serviceInterface)
+
+	return constant.DEFAULT_PATH_TAG +
+		withPathSeparator(path) +
+		withPathSeparator(mdi.version) +
+		withPathSeparator(mdi.group) +
+		withPathSeparator(mdi.side) +
+		joinParams(constant.PATH_SEPARATOR, params)
+
+}
+
+// serviceToPath...
+func serviceToPath(serviceInterface string) string {
+	if serviceInterface == constant.ANY_VALUE {
+		return ""
+	} else {
+		decoded, err := base64.URLEncoding.DecodeString(serviceInterface)
+		if err != nil {
+			return ""
+		}
+		return string(decoded)
+	}
+
+}
+
+//withPathSeparator...
+func withPathSeparator(path string) string {
+	if len(path) != 0 {
+		path = constant.PATH_SEPARATOR + path
+	}
+	return path
+}
diff --git a/metadata/identifier/metadata_identifier.go b/metadata/identifier/metadata_identifier.go
new file mode 100644
index 0000000000000000000000000000000000000000..f3df8f36546093a826279c4e9ec1546f78d444bd
--- /dev/null
+++ b/metadata/identifier/metadata_identifier.go
@@ -0,0 +1,33 @@
+/*
+ * 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 identifier
+
+type MetadataIdentifier struct {
+	application string
+	BaseMetadataIdentifier
+}
+
+// getIdentifierKey...
+func (mdi *MetadataIdentifier) getIdentifierKey(params ...string) string {
+	return mdi.BaseMetadataIdentifier.getIdentifierKey(mdi.application)
+}
+
+// getIdentifierKey...
+func (mdi *MetadataIdentifier) getFilePathKey(params ...string) string {
+	return mdi.BaseMetadataIdentifier.getFilePathKey(mdi.application)
+}
diff --git a/metadata/identifier/service_metadata_identifier.go b/metadata/identifier/service_metadata_identifier.go
new file mode 100644
index 0000000000000000000000000000000000000000..373df0130dd1f87e3175918bde50060c4be89616
--- /dev/null
+++ b/metadata/identifier/service_metadata_identifier.go
@@ -0,0 +1,38 @@
+/*
+ * 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 identifier
+
+import (
+	"github.com/apache/dubbo-go/common/constant"
+)
+
+type ServiceMetadataIdentifier struct {
+	revision string
+	protocol string
+	BaseMetadataIdentifier
+}
+
+// getIdentifierKey...
+func (mdi *ServiceMetadataIdentifier) getIdentifierKey(params ...string) string {
+	return mdi.BaseMetadataIdentifier.getIdentifierKey(mdi.protocol + constant.KEY_REVISON_PREFIX + mdi.revision)
+}
+
+// getIdentifierKey...
+func (mdi *ServiceMetadataIdentifier) getFilePathKey(params ...string) string {
+	return mdi.BaseMetadataIdentifier.getFilePathKey(mdi.protocol + constant.KEY_REVISON_PREFIX + mdi.revision)
+}
diff --git a/metadata/identifier/subscribe_metadata_identifier.go b/metadata/identifier/subscribe_metadata_identifier.go
new file mode 100644
index 0000000000000000000000000000000000000000..321a216a3e3ad3f2390ab832782924a81e226160
--- /dev/null
+++ b/metadata/identifier/subscribe_metadata_identifier.go
@@ -0,0 +1,33 @@
+/*
+ * 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 identifier
+
+type SubscriberMetadataIdentifier struct {
+	revision string
+	BaseMetadataIdentifier
+}
+
+// getIdentifierKey...
+func (mdi *SubscriberMetadataIdentifier) getIdentifierKey(params ...string) string {
+	return mdi.BaseMetadataIdentifier.getIdentifierKey(mdi.revision)
+}
+
+// getIdentifierKey...
+func (mdi *SubscriberMetadataIdentifier) getFilePathKey(params ...string) string {
+	return mdi.BaseMetadataIdentifier.getFilePathKey(mdi.revision)
+}
diff --git a/metadata/namemapping/dynamic/service_name_mapping.go b/metadata/namemapping/dynamic/service_name_mapping.go
new file mode 100644
index 0000000000000000000000000000000000000000..e93c256fe093b4a3e3c431e1d012038b2bb7976b
--- /dev/null
+++ b/metadata/namemapping/dynamic/service_name_mapping.go
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dynamic
+
+import (
+	"strconv"
+	"time"
+)
+
+import (
+	"github.com/dubbogo/gost/container/set"
+	perrors "github.com/pkg/errors"
+)
+
+import (
+	"github.com/apache/dubbo-go/common/constant"
+	"github.com/apache/dubbo-go/config"
+	"github.com/apache/dubbo-go/config_center"
+	"github.com/apache/dubbo-go/metadata"
+)
+
+const (
+	defaultGroup = config_center.DEFAULT_GROUP
+	slash        = "/"
+)
+
+// DynamicConfigurationServiceNameMapping is the implementation based on config center
+type DynamicConfigurationServiceNameMapping struct {
+	dc config_center.DynamicConfiguration
+}
+
+// Map will map the service to this application-level service
+func (d *DynamicConfigurationServiceNameMapping) Map(serviceInterface string, group string, version string, protocol string) error {
+	// metadata service is admin service, should not be mapped
+	if constant.METADATA_SERVICE_NAME == serviceInterface {
+		return perrors.New("try to map the metadata service, will be ignored")
+	}
+
+	appName := config.GetApplicationConfig().Name
+	value := time.Now().UnixNano()
+
+	err := d.dc.PublishConfig(appName,
+		d.buildGroup(serviceInterface),
+		strconv.FormatInt(value, 10))
+	if err != nil {
+		return perrors.WithStack(err)
+	}
+	return nil
+}
+
+// Get will return the application-level services. If not found, the empty set will be returned.
+// if the dynamic configuration got error, the error will return
+func (d *DynamicConfigurationServiceNameMapping) Get(serviceInterface string, group string, version string, protocol string) (*gxset.HashSet, error) {
+	return d.dc.GetConfigKeysByGroup(d.buildGroup(serviceInterface))
+}
+
+// buildGroup will return group, now it looks like defaultGroup/serviceInterface
+func (d *DynamicConfigurationServiceNameMapping) buildGroup(serviceInterface string) string {
+	// the issue : https://github.com/apache/dubbo/issues/4671
+	// so other params are ignored and remove, including group string, version string, protocol string
+	return defaultGroup + slash + serviceInterface
+}
+
+// NewServiceNameMapping will create an instance of DynamicConfigurationServiceNameMapping
+func NewServiceNameMapping(dc config_center.DynamicConfiguration) metadata.ServiceNameMapping {
+	return &DynamicConfigurationServiceNameMapping{dc: dc}
+}
diff --git a/metadata/namemapping/dynamic/service_name_mapping_test.go b/metadata/namemapping/dynamic/service_name_mapping_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..e3d620cd738421c256d8fd232b1afcfd425ca989
--- /dev/null
+++ b/metadata/namemapping/dynamic/service_name_mapping_test.go
@@ -0,0 +1,61 @@
+/*
+ * 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 dynamic
+
+import (
+	"testing"
+)
+
+import (
+	gxset "github.com/dubbogo/gost/container/set"
+	"github.com/stretchr/testify/assert"
+)
+
+import (
+	"github.com/apache/dubbo-go/common/constant"
+	"github.com/apache/dubbo-go/config"
+	"github.com/apache/dubbo-go/config_center"
+)
+
+func TestDynamicConfigurationServiceNameMapping(t *testing.T) {
+
+	// mock data
+	appName := "myApp"
+	dc, err := (&config_center.MockDynamicConfigurationFactory{
+		Content: appName,
+	}).GetDynamicConfiguration(nil)
+	config.GetApplicationConfig().Name = appName
+
+	mapping := NewServiceNameMapping(dc)
+	intf := constant.METADATA_SERVICE_NAME
+	group := "myGroup"
+	version := "myVersion"
+	protocol := "myProtocol"
+
+	err = mapping.Map(intf, group, version, protocol)
+	assert.NotNil(t, err)
+	intf = "MyService"
+	err = mapping.Map(intf, group, version, protocol)
+	assert.Nil(t, err)
+
+	var result *gxset.HashSet
+	result, err = mapping.Get(intf, group, version, protocol)
+	assert.Nil(t, err)
+	assert.Equal(t, 1, result.Size())
+	assert.True(t, result.Contains(appName))
+}
diff --git a/metadata/namemapping/memory/service_name_mapping.go b/metadata/namemapping/memory/service_name_mapping.go
new file mode 100644
index 0000000000000000000000000000000000000000..8a891491bdb97808b77422092a1043c1c0ffafbf
--- /dev/null
+++ b/metadata/namemapping/memory/service_name_mapping.go
@@ -0,0 +1,36 @@
+/*
+ * 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 memory
+
+import (
+	gxset "github.com/dubbogo/gost/container/set"
+)
+
+import (
+	"github.com/apache/dubbo-go/config"
+)
+
+type InMemoryServiceNameMapping struct{}
+
+func (i InMemoryServiceNameMapping) Map(serviceInterface string, group string, version string, protocol string) error {
+	return nil
+}
+
+func (i InMemoryServiceNameMapping) Get(serviceInterface string, group string, version string, protocol string) (*gxset.HashSet, error) {
+	return gxset.NewSet(config.GetApplicationConfig().Name), nil
+}
diff --git a/metadata/report.go b/metadata/report.go
new file mode 100644
index 0000000000000000000000000000000000000000..3fcc71241411d4a8f9577bb5fb3233e67942cd52
--- /dev/null
+++ b/metadata/report.go
@@ -0,0 +1,35 @@
+/*
+ * 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 metadata
+
+import (
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/metadata/definition"
+	"github.com/apache/dubbo-go/metadata/identifier"
+)
+
+type MetadataReport interface {
+	StoreProviderMetadata(*identifier.MetadataIdentifier, *definition.ServiceDefinition)
+	StoreConsumeretadata(*identifier.MetadataIdentifier, map[string]string)
+	SaveServiceMetadata(*identifier.ServiceMetadataIdentifier, *common.URL)
+	RemoveServiceMetadata(*identifier.ServiceMetadataIdentifier)
+	GetExportedURLs(*identifier.ServiceMetadataIdentifier) []string
+	SaveSubscribedData(*identifier.SubscriberMetadataIdentifier, []*common.URL)
+	GetSubscribedURLs(*identifier.SubscriberMetadataIdentifier) []string
+	GetServiceDefinition(*identifier.MetadataIdentifier)
+}
diff --git a/metadata/report_factory.go b/metadata/report_factory.go
new file mode 100644
index 0000000000000000000000000000000000000000..19b1004eee57073acec13c7f114179c47c73f145
--- /dev/null
+++ b/metadata/report_factory.go
@@ -0,0 +1,30 @@
+/*
+ * 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 metadata
+
+import (
+	"github.com/apache/dubbo-go/common"
+)
+
+var (
+	MetadataReportInstance MetadataReport
+)
+
+type MetadataReportFactory interface {
+	CreateMetadataReport(*common.URL) MetadataReport
+}
diff --git a/metadata/service.go b/metadata/service.go
new file mode 100644
index 0000000000000000000000000000000000000000..d85703c95a57183d5c0a5b2445839e946dc6a59b
--- /dev/null
+++ b/metadata/service.go
@@ -0,0 +1,37 @@
+/*
+ * 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 metadata
+
+import (
+	"github.com/apache/dubbo-go/common"
+	gxset "github.com/dubbogo/gost/container/set"
+)
+
+type MetadataService interface {
+	ServiceName() string
+	ExportURL(url *common.URL) bool
+	UnexportURL(url *common.URL) bool
+	RefreshMetadata(exportedRevision string, subscribedRevision string) bool
+	SubscribeURL(url *common.URL) bool
+	UnsubscribeURL(url *common.URL) bool
+	PublishServiceDefinition(url *common.URL)
+
+	GetExportedURLs(serviceInterface string, group string, version string, protocol string) gxset.HashSet
+	GetServiceDefinition(interfaceName string, version string, group string) string
+	GetServiceDefinitionByServiceKey(serviceKey string) string
+}
diff --git a/metadata/service_name_mapping.go b/metadata/service_name_mapping.go
new file mode 100644
index 0000000000000000000000000000000000000000..c14e8ce2e7c40d1573897dfd6ba64c16e18acac7
--- /dev/null
+++ b/metadata/service_name_mapping.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 metadata
+
+import (
+	gxset "github.com/dubbogo/gost/container/set"
+)
+
+// ServiceNameMapping try to build the mapping between application-level service and interface-level service.
+type ServiceNameMapping interface {
+
+	// Map will map the service to this application-level service
+	Map(serviceInterface string, group string, version string, protocol string) error
+
+	// Get will return the application-level services
+	Get(serviceInterface string, group string, version string, protocol string) (*gxset.HashSet, error)
+}
diff --git a/protocol/dubbo/client_test.go b/protocol/dubbo/client_test.go
index 1e0a73fac1a6cf6d4d102e5f4f6f1ba60fc4102a..744ffa80d6bc65e8526201b8cd327bb12b43caef 100644
--- a/protocol/dubbo/client_test.go
+++ b/protocol/dubbo/client_test.go
@@ -162,7 +162,7 @@ func InitTest(t *testing.T) (protocol.Protocol, common.URL) {
 
 	hessian.RegisterPOJO(&User{})
 
-	methods, err := common.ServiceMap.Register("dubbo", &UserProvider{})
+	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)
 
diff --git a/protocol/dubbo/dubbo_exporter.go b/protocol/dubbo/dubbo_exporter.go
index f4cd0cc1234f71bdcf6ce746f01ff3618d820fc5..1c45c40056f7690ba64838f641fe0b13a1554727 100644
--- a/protocol/dubbo/dubbo_exporter.go
+++ b/protocol/dubbo/dubbo_exporter.go
@@ -43,8 +43,9 @@ func NewDubboExporter(key string, invoker protocol.Invoker, exporterMap *sync.Ma
 // Unexport ...
 func (de *DubboExporter) Unexport() {
 	serviceId := de.GetInvoker().GetUrl().GetParam(constant.BEAN_NAME_KEY, "")
+	interfaceName := de.GetInvoker().GetUrl().GetParam(constant.INTERFACE_KEY, "")
 	de.BaseExporter.Unexport()
-	err := common.ServiceMap.UnRegister(DUBBO, serviceId)
+	err := common.ServiceMap.UnRegister(interfaceName, DUBBO, serviceId)
 	if err != nil {
 		logger.Errorf("[DubboExporter.Unexport] error: %v", err)
 	}
diff --git a/protocol/grpc/grpc_exporter.go b/protocol/grpc/grpc_exporter.go
index 1acd2fec391df2c104524792cf40d895cff3faac..0296ad8b5b06b693fd5e0972f18be77a69cd21ed 100644
--- a/protocol/grpc/grpc_exporter.go
+++ b/protocol/grpc/grpc_exporter.go
@@ -43,8 +43,9 @@ func NewGrpcExporter(key string, invoker protocol.Invoker, exporterMap *sync.Map
 // Unexport ...
 func (gg *GrpcExporter) Unexport() {
 	serviceId := gg.GetInvoker().GetUrl().GetParam(constant.BEAN_NAME_KEY, "")
+	interfaceName := gg.GetInvoker().GetUrl().GetParam(constant.INTERFACE_KEY, "")
 	gg.BaseExporter.Unexport()
-	err := common.ServiceMap.UnRegister(GRPC, serviceId)
+	err := common.ServiceMap.UnRegister(interfaceName, GRPC, serviceId)
 	if err != nil {
 		logger.Errorf("[GrpcExporter.Unexport] error: %v", err)
 	}
diff --git a/protocol/jsonrpc/http_test.go b/protocol/jsonrpc/http_test.go
index 0cb88b36a8f330059906eb70417b6d4841020c38..f8480bf32e15ad428209eedb72757a299455eb20 100644
--- a/protocol/jsonrpc/http_test.go
+++ b/protocol/jsonrpc/http_test.go
@@ -50,7 +50,7 @@ type (
 
 func TestHTTPClient_Call(t *testing.T) {
 
-	methods, err := common.ServiceMap.Register("jsonrpc", &UserProvider{})
+	methods, err := common.ServiceMap.Register("com.ikurento.user.UserProvider", "jsonrpc", &UserProvider{})
 	assert.NoError(t, err)
 	assert.Equal(t, "GetUser,GetUser0,GetUser1,GetUser2,GetUser3,GetUser4", methods)
 
diff --git a/protocol/jsonrpc/jsonrpc_exporter.go b/protocol/jsonrpc/jsonrpc_exporter.go
index 7f8fd491854f1ab25e63410a22ef5664db92f614..c61cf9adaebe9105a87ece1dcbae4dbe706cb3fc 100644
--- a/protocol/jsonrpc/jsonrpc_exporter.go
+++ b/protocol/jsonrpc/jsonrpc_exporter.go
@@ -43,8 +43,9 @@ func NewJsonrpcExporter(key string, invoker protocol.Invoker, exporterMap *sync.
 // Unexport ...
 func (je *JsonrpcExporter) Unexport() {
 	serviceId := je.GetInvoker().GetUrl().GetParam(constant.BEAN_NAME_KEY, "")
+	interfaceName := je.GetInvoker().GetUrl().GetParam(constant.INTERFACE_KEY, "")
 	je.BaseExporter.Unexport()
-	err := common.ServiceMap.UnRegister(JSONRPC, serviceId)
+	err := common.ServiceMap.UnRegister(interfaceName, JSONRPC, serviceId)
 	if err != nil {
 		logger.Errorf("[JsonrpcExporter.Unexport] error: %v", err)
 	}
diff --git a/protocol/jsonrpc/jsonrpc_invoker_test.go b/protocol/jsonrpc/jsonrpc_invoker_test.go
index 9e08eed2b4c61e686073a9039a605c4f73aa08c5..0f14ba11e2dec18bbd4d63e87e8c0fb2727f3755 100644
--- a/protocol/jsonrpc/jsonrpc_invoker_test.go
+++ b/protocol/jsonrpc/jsonrpc_invoker_test.go
@@ -36,7 +36,7 @@ import (
 
 func TestJsonrpcInvoker_Invoke(t *testing.T) {
 
-	methods, err := common.ServiceMap.Register("jsonrpc", &UserProvider{})
+	methods, err := common.ServiceMap.Register("UserProvider", "jsonrpc", &UserProvider{})
 	assert.NoError(t, err)
 	assert.Equal(t, "GetUser,GetUser0,GetUser1,GetUser2,GetUser3,GetUser4", methods)
 
diff --git a/protocol/mock/mock_invoker.go b/protocol/mock/mock_invoker.go
index f22b803e75c6430ebc97e0b3174923b2d56653e0..0c88b47e36122dc1a9bc9345d7e93f62cd76f13b 100644
--- a/protocol/mock/mock_invoker.go
+++ b/protocol/mock/mock_invoker.go
@@ -15,22 +15,6 @@
  * limitations under the License.
  */
 
-//  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.
-//
-
 // Code generated by MockGen. DO NOT EDIT.
 // Source: invoker.go
 
diff --git a/protocol/rest/client/client_impl/resty_client.go b/protocol/rest/client/client_impl/resty_client.go
index aa6c23137dc68492948b85a85555a5340572ac49..b60f50a5a70cde01a051dbb2a4490cbb792e0116 100644
--- a/protocol/rest/client/client_impl/resty_client.go
+++ b/protocol/rest/client/client_impl/resty_client.go
@@ -40,10 +40,12 @@ func init() {
 	extension.SetRestClient(constant.DEFAULT_REST_CLIENT, NewRestyClient)
 }
 
+// RestyClient a rest client implement by Resty
 type RestyClient struct {
 	client *resty.Client
 }
 
+// NewRestyClient a constructor of RestyClient
 func NewRestyClient(restOption *client.RestOptions) client.RestClient {
 	client := resty.New()
 	client.SetTransport(
@@ -65,21 +67,21 @@ func NewRestyClient(restOption *client.RestOptions) client.RestClient {
 	}
 }
 
-func (rc *RestyClient) Do(restRequest *client.RestRequest, res interface{}) error {
-	r, err := rc.client.R().
-		SetHeader("Content-Type", restRequest.Consumes).
-		SetHeader("Accept", restRequest.Produces).
+// Do send request by RestyClient
+func (rc *RestyClient) Do(restRequest *client.RestClientRequest, res interface{}) error {
+	req := rc.client.R()
+	req.Header = restRequest.Header
+	resp, err := req.
 		SetPathParams(restRequest.PathParams).
 		SetQueryParams(restRequest.QueryParams).
-		SetHeaders(restRequest.Headers).
 		SetBody(restRequest.Body).
 		SetResult(res).
 		Execute(restRequest.Method, "http://"+path.Join(restRequest.Location, restRequest.Path))
 	if err != nil {
 		return perrors.WithStack(err)
 	}
-	if r.IsError() {
-		return perrors.New(r.String())
+	if resp.IsError() {
+		return perrors.New(resp.String())
 	}
 	return nil
 }
diff --git a/protocol/rest/client/rest_client.go b/protocol/rest/client/rest_client.go
index 7d020abc81c2bd44473ed25ffec4b2b657e7bcc0..d63c5e0bd0c7e6392eb5b0efc50bb0a585f4192d 100644
--- a/protocol/rest/client/rest_client.go
+++ b/protocol/rest/client/rest_client.go
@@ -18,26 +18,28 @@
 package client
 
 import (
+	"net/http"
 	"time"
 )
 
+// RestOptions
 type RestOptions struct {
 	RequestTimeout time.Duration
 	ConnectTimeout time.Duration
 }
 
-type RestRequest struct {
+// RestClientRequest
+type RestClientRequest struct {
+	Header      http.Header
 	Location    string
 	Path        string
-	Produces    string
-	Consumes    string
 	Method      string
 	PathParams  map[string]string
 	QueryParams map[string]string
 	Body        interface{}
-	Headers     map[string]string
 }
 
+// RestClient user can implement this client interface to send request
 type RestClient interface {
-	Do(request *RestRequest, res interface{}) error
+	Do(request *RestClientRequest, res interface{}) error
 }
diff --git a/protocol/rest/rest_exporter.go b/protocol/rest/rest_exporter.go
index 470d525ad806687e7a732ce5681eb372eb431a63..1ee208615ea07e3f2850920492ab9e9821e7ffef 100644
--- a/protocol/rest/rest_exporter.go
+++ b/protocol/rest/rest_exporter.go
@@ -40,8 +40,9 @@ func NewRestExporter(key string, invoker protocol.Invoker, exporterMap *sync.Map
 
 func (re *RestExporter) Unexport() {
 	serviceId := re.GetInvoker().GetUrl().GetParam(constant.BEAN_NAME_KEY, "")
+	interfaceName := re.GetInvoker().GetUrl().GetParam(constant.INTERFACE_KEY, "")
 	re.BaseExporter.Unexport()
-	err := common.ServiceMap.UnRegister(REST, serviceId)
+	err := common.ServiceMap.UnRegister(interfaceName, REST, serviceId)
 	if err != nil {
 		logger.Errorf("[RestExporter.Unexport] error: %v", err)
 	}
diff --git a/protocol/rest/rest_invoker.go b/protocol/rest/rest_invoker.go
index 0c82035ac5eb9a52ab188baa971dbdf1b864e970..121d1217efd3baea0f961b67e243e9a0450aefc2 100644
--- a/protocol/rest/rest_invoker.go
+++ b/protocol/rest/rest_invoker.go
@@ -20,6 +20,7 @@ package rest
 import (
 	"context"
 	"fmt"
+	"net/http"
 )
 
 import (
@@ -56,7 +57,7 @@ func (ri *RestInvoker) Invoke(ctx context.Context, invocation protocol.Invocatio
 		body        interface{}
 		pathParams  map[string]string
 		queryParams map[string]string
-		headers     map[string]string
+		header      http.Header
 		err         error
 	)
 	if methodConfig == nil {
@@ -71,24 +72,21 @@ func (ri *RestInvoker) Invoke(ctx context.Context, invocation protocol.Invocatio
 		result.Err = err
 		return &result
 	}
-	if headers, err = restStringMapTransform(methodConfig.HeadersMap, inv.Arguments()); err != nil {
+	if header, err = getRestHttpHeader(methodConfig, inv.Arguments()); err != nil {
 		result.Err = err
 		return &result
 	}
 	if len(inv.Arguments()) > methodConfig.Body && methodConfig.Body >= 0 {
 		body = inv.Arguments()[methodConfig.Body]
 	}
-
-	req := &client.RestRequest{
+	req := &client.RestClientRequest{
 		Location:    ri.GetUrl().Location,
-		Produces:    methodConfig.Produces,
-		Consumes:    methodConfig.Consumes,
 		Method:      methodConfig.MethodType,
 		Path:        methodConfig.Path,
 		PathParams:  pathParams,
 		QueryParams: queryParams,
 		Body:        body,
-		Headers:     headers,
+		Header:      header,
 	}
 	result.Err = ri.client.Do(req, inv.Reply())
 	if result.Err == nil {
@@ -107,3 +105,17 @@ func restStringMapTransform(paramsMap map[int]string, args []interface{}) (map[s
 	}
 	return resMap, nil
 }
+
+func getRestHttpHeader(methodConfig *config.RestMethodConfig, args []interface{}) (http.Header, error) {
+	header := http.Header{}
+	headersMap := methodConfig.HeadersMap
+	header.Set("Content-Type", methodConfig.Consumes)
+	header.Set("Accept", methodConfig.Produces)
+	for k, v := range headersMap {
+		if k >= len(args) || k < 0 {
+			return nil, perrors.Errorf("[Rest Invoke] Index %v is out of bundle", k)
+		}
+		header.Set(v, fmt.Sprint(args[k]))
+	}
+	return header, nil
+}
diff --git a/protocol/rest/rest_invoker_test.go b/protocol/rest/rest_invoker_test.go
index e44c5d9a21026992178bd432676c99bc837c361b..2ea260c58d03c27a691e48b953ce6d64f75040a2 100644
--- a/protocol/rest/rest_invoker_test.go
+++ b/protocol/rest/rest_invoker_test.go
@@ -61,7 +61,7 @@ func TestRestInvoker_Invoke(t *testing.T) {
 		"module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&" +
 		"side=provider&timeout=3000&timestamp=1556509797245")
 	assert.NoError(t, err)
-	_, err = common.ServiceMap.Register(url.Protocol, &UserProvider{})
+	_, err = common.ServiceMap.Register("UserProvider", url.Protocol, &UserProvider{})
 	assert.NoError(t, err)
 	con := config.ProviderConfig{}
 	config.SetProviderConfig(con)
@@ -206,6 +206,6 @@ func TestRestInvoker_Invoke(t *testing.T) {
 	assert.Error(t, res.Error(), "test error")
 
 	assert.Equal(t, filterNum, 12)
-	err = common.ServiceMap.UnRegister(url.Protocol, "com.ikurento.user.UserProvider")
+	err = common.ServiceMap.UnRegister("UserProvider", url.Protocol, "com.ikurento.user.UserProvider")
 	assert.NoError(t, err)
 }
diff --git a/protocol/rest/rest_protocol.go b/protocol/rest/rest_protocol.go
index 47ecb6093b4cfa12a1d3397fa45d59b1e173a93a..e15eeb39d72212eb9f1d0235313eba231d3b0a36 100644
--- a/protocol/rest/rest_protocol.go
+++ b/protocol/rest/rest_protocol.go
@@ -75,7 +75,9 @@ func (rp *RestProtocol) Export(invoker protocol.Invoker) protocol.Exporter {
 	}
 	rp.SetExporterMap(serviceKey, exporter)
 	restServer := rp.getServer(url, restServiceConfig.Server)
-	restServer.Deploy(invoker, restServiceConfig.RestMethodConfigsMap)
+	for _, methodConfig := range restServiceConfig.RestMethodConfigsMap {
+		restServer.Deploy(methodConfig, server.GetRouteFunc(invoker, methodConfig))
+	}
 	return exporter
 }
 
diff --git a/protocol/rest/rest_protocol_test.go b/protocol/rest/rest_protocol_test.go
index 8af73a1839c159fdf58c64d12e039c20bb3221c6..9117148777ca868cb7d2672236e800c836d3de84 100644
--- a/protocol/rest/rest_protocol_test.go
+++ b/protocol/rest/rest_protocol_test.go
@@ -80,7 +80,7 @@ func TestRestProtocol_Export(t *testing.T) {
 		"module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&" +
 		"side=provider&timeout=3000&timestamp=1556509797245")
 	assert.NoError(t, err)
-	_, err = common.ServiceMap.Register(url.Protocol, &UserProvider{})
+	_, err = common.ServiceMap.Register("UserProvider", url.Protocol, &UserProvider{})
 	assert.NoError(t, err)
 	con := config.ProviderConfig{}
 	config.SetProviderConfig(con)
@@ -128,7 +128,7 @@ func TestRestProtocol_Export(t *testing.T) {
 	proto.Destroy()
 	_, ok = proto.(*RestProtocol).serverMap[url.Location]
 	assert.False(t, ok)
-	err = common.ServiceMap.UnRegister(url.Protocol, "com.ikurento.user.UserProvider")
+	err = common.ServiceMap.UnRegister("UserProvider", url.Protocol, "com.ikurento.user.UserProvider")
 	assert.NoError(t, err)
 }
 
diff --git a/protocol/rest/server/rest_server.go b/protocol/rest/server/rest_server.go
index c10c98a7b677d47c43b64643a69d5b3768a6c663..fbd6fb7ad9dd81f043e4d45ee94a54e12ef89cdd 100644
--- a/protocol/rest/server/rest_server.go
+++ b/protocol/rest/server/rest_server.go
@@ -17,15 +17,306 @@
 
 package server
 
+import (
+	"context"
+	"errors"
+	"net/http"
+	"reflect"
+	"strconv"
+	"strings"
+)
+
+import (
+	perrors "github.com/pkg/errors"
+)
+
 import (
 	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/common/logger"
 	"github.com/apache/dubbo-go/protocol"
-	"github.com/apache/dubbo-go/protocol/rest/config"
+	"github.com/apache/dubbo-go/protocol/invocation"
+	rest_config "github.com/apache/dubbo-go/protocol/rest/config"
 )
 
+const parseParameterErrorStr = "An error occurred while parsing parameters on the server"
+
+// RestServer user can implement this server interface
 type RestServer interface {
+	// Start rest server
 	Start(url common.URL)
-	Deploy(invoker protocol.Invoker, restMethodConfig map[string]*config.RestMethodConfig)
-	UnDeploy(restMethodConfig map[string]*config.RestMethodConfig)
+	// Deploy a http api
+	Deploy(restMethodConfig *rest_config.RestMethodConfig, routeFunc func(request RestServerRequest, response RestServerResponse))
+	// UnDeploy a http api
+	UnDeploy(restMethodConfig *rest_config.RestMethodConfig)
+	// Destroy rest server
 	Destroy()
 }
+
+// RestServerRequest interface
+type RestServerRequest interface {
+	// RawRequest get the Ptr of http.Request
+	RawRequest() *http.Request
+	// PathParameter get the path parameter by name
+	PathParameter(name string) string
+	// PathParameters get the map of the path parameters
+	PathParameters() map[string]string
+	// QueryParameter get the query parameter by name
+	QueryParameter(name string) string
+	// QueryParameters get the map of query parameters
+	QueryParameters(name string) []string
+	// BodyParameter get the body parameter of name
+	BodyParameter(name string) (string, error)
+	// HeaderParameter get the header parameter of name
+	HeaderParameter(name string) string
+	// ReadEntity checks the Accept header and reads the content into the entityPointer.
+	ReadEntity(entityPointer interface{}) error
+}
+
+// RestServerResponse interface
+type RestServerResponse interface {
+	http.ResponseWriter
+	// WriteError writes the http status and the error string on the response. err can be nil.
+	// Return an error if writing was not successful.
+	WriteError(httpStatus int, err error) (writeErr error)
+	// WriteEntity marshals the value using the representation denoted by the Accept Header.
+	WriteEntity(value interface{}) error
+}
+
+// GetRouteFunc
+// A route function will be invoked by http server
+func GetRouteFunc(invoker protocol.Invoker, methodConfig *rest_config.RestMethodConfig) func(req RestServerRequest, resp RestServerResponse) {
+	return func(req RestServerRequest, resp RestServerResponse) {
+		var (
+			err  error
+			args []interface{}
+		)
+		svc := common.ServiceMap.GetService(invoker.GetUrl().Protocol, strings.TrimPrefix(invoker.GetUrl().Path, "/"))
+		// get method
+		method := svc.Method()[methodConfig.MethodName]
+		argsTypes := method.ArgsType()
+		replyType := method.ReplyType()
+		// two ways to prepare arguments
+		// if method like this 'func1(req []interface{}, rsp *User) error'
+		// we don't have arguments type
+		if (len(argsTypes) == 1 || len(argsTypes) == 2 && replyType == nil) &&
+			argsTypes[0].String() == "[]interface {}" {
+			args, err = getArgsInterfaceFromRequest(req, methodConfig)
+		} else {
+			args, err = getArgsFromRequest(req, argsTypes, methodConfig)
+		}
+		if err != nil {
+			logger.Errorf("[Go Restful] parsing http parameters error:%v", err)
+			err = resp.WriteError(http.StatusInternalServerError, errors.New(parseParameterErrorStr))
+			if err != nil {
+				logger.Errorf("[Go Restful] WriteErrorString error:%v", err)
+			}
+		}
+		result := invoker.Invoke(context.Background(), invocation.NewRPCInvocation(methodConfig.MethodName, args, make(map[string]string)))
+		if result.Error() != nil {
+			err = resp.WriteError(http.StatusInternalServerError, result.Error())
+			if err != nil {
+				logger.Errorf("[Go Restful] WriteError error:%v", err)
+			}
+			return
+		}
+		err = resp.WriteEntity(result.Result())
+		if err != nil {
+			logger.Errorf("[Go Restful] WriteEntity error:%v", err)
+		}
+	}
+}
+
+// getArgsInterfaceFromRequest when service function like GetUser(req []interface{}, rsp *User) error
+// use this method to get arguments
+func getArgsInterfaceFromRequest(req RestServerRequest, methodConfig *rest_config.RestMethodConfig) ([]interface{}, error) {
+	argsMap := make(map[int]interface{}, 8)
+	maxKey := 0
+	for k, v := range methodConfig.PathParamsMap {
+		if maxKey < k {
+			maxKey = k
+		}
+		argsMap[k] = req.PathParameter(v)
+	}
+	for k, v := range methodConfig.QueryParamsMap {
+		if maxKey < k {
+			maxKey = k
+		}
+		params := req.QueryParameters(v)
+		if len(params) == 1 {
+			argsMap[k] = params[0]
+		} else {
+			argsMap[k] = params
+		}
+	}
+	for k, v := range methodConfig.HeadersMap {
+		if maxKey < k {
+			maxKey = k
+		}
+		argsMap[k] = req.HeaderParameter(v)
+	}
+	if methodConfig.Body >= 0 {
+		if maxKey < methodConfig.Body {
+			maxKey = methodConfig.Body
+		}
+		m := make(map[string]interface{})
+		// TODO read as a slice
+		if err := req.ReadEntity(&m); err != nil {
+			return nil, perrors.Errorf("[Go restful] Read body entity as map[string]interface{} error:%v", err)
+		}
+		argsMap[methodConfig.Body] = m
+	}
+	args := make([]interface{}, maxKey+1)
+	for k, v := range argsMap {
+		if k >= 0 {
+			args[k] = v
+		}
+	}
+	return args, nil
+}
+
+// getArgsFromRequest get arguments from server.RestServerRequest
+func getArgsFromRequest(req RestServerRequest, argsTypes []reflect.Type, methodConfig *rest_config.RestMethodConfig) ([]interface{}, error) {
+	argsLength := len(argsTypes)
+	args := make([]interface{}, argsLength)
+	for i, t := range argsTypes {
+		args[i] = reflect.Zero(t).Interface()
+	}
+	if err := assembleArgsFromPathParams(methodConfig, argsLength, argsTypes, req, args); err != nil {
+		return nil, err
+	}
+	if err := assembleArgsFromQueryParams(methodConfig, argsLength, argsTypes, req, args); err != nil {
+		return nil, err
+	}
+	if err := assembleArgsFromBody(methodConfig, argsTypes, req, args); err != nil {
+		return nil, err
+	}
+	if err := assembleArgsFromHeaders(methodConfig, req, argsLength, argsTypes, args); err != nil {
+		return nil, err
+	}
+	return args, nil
+}
+
+// assembleArgsFromHeaders assemble arguments from headers
+func assembleArgsFromHeaders(methodConfig *rest_config.RestMethodConfig, req RestServerRequest, argsLength int, argsTypes []reflect.Type, args []interface{}) error {
+	for k, v := range methodConfig.HeadersMap {
+		param := req.HeaderParameter(v)
+		if k < 0 || k >= argsLength {
+			return perrors.Errorf("[Go restful] Header param parse error, the index %v args of method:%v doesn't exist", k, methodConfig.MethodName)
+		}
+		t := argsTypes[k]
+		if t.Kind() == reflect.Ptr {
+			t = t.Elem()
+		}
+		if t.Kind() == reflect.String {
+			args[k] = param
+		} else {
+			return perrors.Errorf("[Go restful] Header param parse error, the index %v args's type isn't string", k)
+		}
+	}
+	return nil
+}
+
+// assembleArgsFromBody assemble arguments from body
+func assembleArgsFromBody(methodConfig *rest_config.RestMethodConfig, argsTypes []reflect.Type, req RestServerRequest, args []interface{}) error {
+	if methodConfig.Body >= 0 && methodConfig.Body < len(argsTypes) {
+		t := argsTypes[methodConfig.Body]
+		kind := t.Kind()
+		if kind == reflect.Ptr {
+			t = t.Elem()
+		}
+		var ni interface{}
+		if t.String() == "[]interface {}" {
+			ni = make([]map[string]interface{}, 0)
+		} else if t.String() == "interface {}" {
+			ni = make(map[string]interface{})
+		} else {
+			n := reflect.New(t)
+			if n.CanInterface() {
+				ni = n.Interface()
+			}
+		}
+		if err := req.ReadEntity(&ni); err != nil {
+			return perrors.Errorf("[Go restful] Read body entity error, error is %v", perrors.WithStack(err))
+		}
+		args[methodConfig.Body] = ni
+	}
+	return nil
+}
+
+// assembleArgsFromQueryParams assemble arguments from query params
+func assembleArgsFromQueryParams(methodConfig *rest_config.RestMethodConfig, argsLength int, argsTypes []reflect.Type, req RestServerRequest, args []interface{}) error {
+	var (
+		err   error
+		param interface{}
+		i64   int64
+	)
+	for k, v := range methodConfig.QueryParamsMap {
+		if k < 0 || k >= argsLength {
+			return perrors.Errorf("[Go restful] Query param parse error, the index %v args of method:%v doesn't exist", k, methodConfig.MethodName)
+		}
+		t := argsTypes[k]
+		kind := t.Kind()
+		if kind == reflect.Ptr {
+			t = t.Elem()
+		}
+		if kind == reflect.Slice {
+			param = req.QueryParameters(v)
+		} else if kind == reflect.String {
+			param = req.QueryParameter(v)
+		} else if kind == reflect.Int {
+			param, err = strconv.Atoi(req.QueryParameter(v))
+		} else if kind == reflect.Int32 {
+			i64, err = strconv.ParseInt(req.QueryParameter(v), 10, 32)
+			if err == nil {
+				param = int32(i64)
+			}
+		} else if kind == reflect.Int64 {
+			param, err = strconv.ParseInt(req.QueryParameter(v), 10, 64)
+		} else {
+			return perrors.Errorf("[Go restful] Query param parse error, the index %v args's type isn't int or string or slice", k)
+		}
+		if err != nil {
+			return perrors.Errorf("[Go restful] Query param parse error, error:%v", perrors.WithStack(err))
+		}
+		args[k] = param
+	}
+	return nil
+}
+
+// assembleArgsFromPathParams assemble arguments from path params
+func assembleArgsFromPathParams(methodConfig *rest_config.RestMethodConfig, argsLength int, argsTypes []reflect.Type, req RestServerRequest, args []interface{}) error {
+	var (
+		err   error
+		param interface{}
+		i64   int64
+	)
+	for k, v := range methodConfig.PathParamsMap {
+		if k < 0 || k >= argsLength {
+			return perrors.Errorf("[Go restful] Path param parse error, the index %v args of method:%v doesn't exist", k, methodConfig.MethodName)
+		}
+		t := argsTypes[k]
+		kind := t.Kind()
+		if kind == reflect.Ptr {
+			t = t.Elem()
+		}
+		if kind == reflect.Int {
+			param, err = strconv.Atoi(req.PathParameter(v))
+		} else if kind == reflect.Int32 {
+			i64, err = strconv.ParseInt(req.PathParameter(v), 10, 32)
+			if err == nil {
+				param = int32(i64)
+			}
+		} else if kind == reflect.Int64 {
+			param, err = strconv.ParseInt(req.PathParameter(v), 10, 64)
+		} else if kind == reflect.String {
+			param = req.PathParameter(v)
+		} else {
+			return perrors.Errorf("[Go restful] Path param parse error, the index %v args's type isn't int or string", k)
+		}
+		if err != nil {
+			return perrors.Errorf("[Go restful] Path param parse error, error is %v", perrors.WithStack(err))
+		}
+		args[k] = param
+	}
+	return nil
+}
diff --git a/protocol/rest/server/server_impl/go_restful_server.go b/protocol/rest/server/server_impl/go_restful_server.go
index 69f36a5c80aa51f52dfcfabc5a1bd4003f4cd727..c7d971fcaa5ada0ba02cc436b5ae6705793887ef 100644
--- a/protocol/rest/server/server_impl/go_restful_server.go
+++ b/protocol/rest/server/server_impl/go_restful_server.go
@@ -22,8 +22,6 @@ import (
 	"fmt"
 	"net"
 	"net/http"
-	"reflect"
-	"strconv"
 	"strings"
 	"time"
 )
@@ -38,27 +36,29 @@ import (
 	"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"
-	"github.com/apache/dubbo-go/protocol/invocation"
 	"github.com/apache/dubbo-go/protocol/rest/config"
 	"github.com/apache/dubbo-go/protocol/rest/server"
 )
 
 func init() {
-	extension.SetRestServer(constant.DEFAULT_REST_SERVER, GetNewGoRestfulServer)
+	extension.SetRestServer(constant.DEFAULT_REST_SERVER, NewGoRestfulServer)
 }
 
 var filterSlice []restful.FilterFunction
 
+// GoRestfulServer a rest server implement by go-restful
 type GoRestfulServer struct {
 	srv       *http.Server
 	container *restful.Container
 }
 
-func NewGoRestfulServer() *GoRestfulServer {
+// NewGoRestfulServer a constructor of GoRestfulServer
+func NewGoRestfulServer() server.RestServer {
 	return &GoRestfulServer{}
 }
 
+// Start go-restful server
+// It will add all go-restful filters
 func (grs *GoRestfulServer) Start(url common.URL) {
 	grs.container = restful.NewContainer()
 	for _, filter := range filterSlice {
@@ -80,61 +80,32 @@ func (grs *GoRestfulServer) Start(url common.URL) {
 	}()
 }
 
-func (grs *GoRestfulServer) Deploy(invoker protocol.Invoker, restMethodConfig map[string]*config.RestMethodConfig) {
-	svc := common.ServiceMap.GetService(invoker.GetUrl().Protocol, strings.TrimPrefix(invoker.GetUrl().Path, "/"))
-	for methodName, config := range restMethodConfig {
-		// get method
-		method := svc.Method()[methodName]
-		argsTypes := method.ArgsType()
-		replyType := method.ReplyType()
-		ws := new(restful.WebService)
-		ws.Path(config.Path).
-			Produces(strings.Split(config.Produces, ",")...).
-			Consumes(strings.Split(config.Consumes, ",")...).
-			Route(ws.Method(config.MethodType).To(getFunc(methodName, invoker, argsTypes, replyType, config)))
-		grs.container.Add(ws)
+// 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).
+		Produces(strings.Split(restMethodConfig.Produces, ",")...).
+		Consumes(strings.Split(restMethodConfig.Consumes, ",")...).
+		Route(ws.Method(restMethodConfig.MethodType).To(rf))
+	grs.container.Add(ws)
 
 }
 
-func getFunc(methodName string, invoker protocol.Invoker, argsTypes []reflect.Type,
-	replyType reflect.Type, config *config.RestMethodConfig) func(req *restful.Request, resp *restful.Response) {
-	return func(req *restful.Request, resp *restful.Response) {
-		var (
-			err  error
-			args []interface{}
-		)
-		if (len(argsTypes) == 1 || len(argsTypes) == 2 && replyType == nil) &&
-			argsTypes[0].String() == "[]interface {}" {
-			args = getArgsInterfaceFromRequest(req, config)
-		} else {
-			args = getArgsFromRequest(req, argsTypes, config)
-		}
-		result := invoker.Invoke(context.Background(), invocation.NewRPCInvocation(methodName, args, make(map[string]string)))
-		if result.Error() != nil {
-			err = resp.WriteError(http.StatusInternalServerError, result.Error())
-			if err != nil {
-				logger.Errorf("[Go Restful] WriteError error:%v", err)
-			}
-			return
-		}
-		err = resp.WriteEntity(result.Result())
-		if err != nil {
-			logger.Error("[Go Restful] WriteEntity error:%v", err)
-		}
-	}
-}
-func (grs *GoRestfulServer) UnDeploy(restMethodConfig map[string]*config.RestMethodConfig) {
-	for _, config := range restMethodConfig {
-		ws := new(restful.WebService)
-		ws.Path(config.Path)
-		err := grs.container.Remove(ws)
-		if err != nil {
-			logger.Warnf("[Go restful] Remove web service error:%v", err)
-		}
+// 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)
+	if err != nil {
+		logger.Warnf("[Go restful] Remove web service error:%v", err)
 	}
 }
 
+// Destroy the go-restful server
 func (grs *GoRestfulServer) Destroy() {
 	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
 	defer cancel()
@@ -144,179 +115,59 @@ func (grs *GoRestfulServer) Destroy() {
 	logger.Infof("[Go Restful] Server exiting")
 }
 
-func getArgsInterfaceFromRequest(req *restful.Request, config *config.RestMethodConfig) []interface{} {
-	argsMap := make(map[int]interface{}, 8)
-	maxKey := 0
-	for k, v := range config.PathParamsMap {
-		if maxKey < k {
-			maxKey = k
-		}
-		argsMap[k] = req.PathParameter(v)
-	}
-	for k, v := range config.QueryParamsMap {
-		if maxKey < k {
-			maxKey = k
-		}
-		params := req.QueryParameters(v)
-		if len(params) == 1 {
-			argsMap[k] = params[0]
-		} else {
-			argsMap[k] = params
-		}
-	}
-	for k, v := range config.HeadersMap {
-		if maxKey < k {
-			maxKey = k
-		}
-		argsMap[k] = req.HeaderParameter(v)
-	}
-	if config.Body >= 0 {
-		if maxKey < config.Body {
-			maxKey = config.Body
-		}
-		m := make(map[string]interface{})
-		// TODO read as a slice
-		if err := req.ReadEntity(&m); err != nil {
-			logger.Warnf("[Go restful] Read body entity as map[string]interface{} error:%v", perrors.WithStack(err))
-		} else {
-			argsMap[config.Body] = m
-		}
-	}
-	args := make([]interface{}, maxKey+1)
-	for k, v := range argsMap {
-		if k >= 0 {
-			args[k] = v
-		}
-	}
-	return args
+// AddGoRestfulServerFilter let user add the http server of go-restful
+// addFilter should before config.Load()
+func AddGoRestfulServerFilter(filterFuc restful.FilterFunction) {
+	filterSlice = append(filterSlice, filterFuc)
 }
 
-func getArgsFromRequest(req *restful.Request, argsTypes []reflect.Type, config *config.RestMethodConfig) []interface{} {
-	argsLength := len(argsTypes)
-	args := make([]interface{}, argsLength)
-	for i, t := range argsTypes {
-		args[i] = reflect.Zero(t).Interface()
-	}
-	var (
-		err   error
-		param interface{}
-		i64   int64
-	)
-	for k, v := range config.PathParamsMap {
-		if k < 0 || k >= argsLength {
-			logger.Errorf("[Go restful] Path param parse error, the args:%v doesn't exist", k)
-			continue
-		}
-		t := argsTypes[k]
-		kind := t.Kind()
-		if kind == reflect.Ptr {
-			t = t.Elem()
-		}
-		if kind == reflect.Int {
-			param, err = strconv.Atoi(req.PathParameter(v))
-		} else if kind == reflect.Int32 {
-			i64, err = strconv.ParseInt(req.PathParameter(v), 10, 32)
-			if err == nil {
-				param = int32(i64)
-			}
-		} else if kind == reflect.Int64 {
-			param, err = strconv.ParseInt(req.PathParameter(v), 10, 64)
-		} else if kind == reflect.String {
-			param = req.PathParameter(v)
-		} else {
-			logger.Warnf("[Go restful] Path param parse error, the args:%v of type isn't int or string", k)
-			continue
-		}
-		if err != nil {
-			logger.Errorf("[Go restful] Path param parse error, error is %v", err)
-			continue
-		}
-		args[k] = param
-	}
-	for k, v := range config.QueryParamsMap {
-		if k < 0 || k >= argsLength {
-			logger.Errorf("[Go restful] Query param parse error, the args:%v doesn't exist", k)
-			continue
-		}
-		t := argsTypes[k]
-		kind := t.Kind()
-		if kind == reflect.Ptr {
-			t = t.Elem()
-		}
-		if kind == reflect.Slice {
-			param = req.QueryParameters(v)
-		} else if kind == reflect.String {
-			param = req.QueryParameter(v)
-		} else if kind == reflect.Int {
-			param, err = strconv.Atoi(req.QueryParameter(v))
-		} else if kind == reflect.Int32 {
-			i64, err = strconv.ParseInt(req.QueryParameter(v), 10, 32)
-			if err == nil {
-				param = int32(i64)
-			}
-		} else if kind == reflect.Int64 {
-			param, err = strconv.ParseInt(req.QueryParameter(v), 10, 64)
-		} else {
-			logger.Errorf("[Go restful] Query param parse error, the args:%v of type isn't int or string or slice", k)
-			continue
-		}
-		if err != nil {
-			logger.Errorf("[Go restful] Query param parse error, error is %v", err)
-			continue
-		}
-		args[k] = param
-	}
+// GoRestfulRequestAdapter a adapter struct about RestServerRequest
+type GoRestfulRequestAdapter struct {
+	server.RestServerRequest
+	request *restful.Request
+}
 
-	if config.Body >= 0 && config.Body < len(argsTypes) {
-		t := argsTypes[config.Body]
-		kind := t.Kind()
-		if kind == reflect.Ptr {
-			t = t.Elem()
-		}
-		var ni interface{}
-		if t.String() == "[]interface {}" {
-			ni = make([]map[string]interface{}, 0)
-		} else if t.String() == "interface {}" {
-			ni = make(map[string]interface{})
-		} else {
-			n := reflect.New(t)
-			if n.CanInterface() {
-				ni = n.Interface()
-			}
-		}
-		if err := req.ReadEntity(&ni); err != nil {
-			logger.Errorf("[Go restful] Read body entity error:%v", err)
-		} else {
-			args[config.Body] = ni
-		}
-	}
+// NewGoRestfulRequestAdapter a constructor of GoRestfulRequestAdapter
+func NewGoRestfulRequestAdapter(request *restful.Request) *GoRestfulRequestAdapter {
+	return &GoRestfulRequestAdapter{request: request}
+}
 
-	for k, v := range config.HeadersMap {
-		param := req.HeaderParameter(v)
-		if k < 0 || k >= argsLength {
-			logger.Errorf("[Go restful] Header param parse error, the args:%v doesn't exist", k)
-			continue
-		}
-		t := argsTypes[k]
-		if t.Kind() == reflect.Ptr {
-			t = t.Elem()
-		}
-		if t.Kind() == reflect.String {
-			args[k] = param
-		} else {
-			logger.Errorf("[Go restful] Header param parse error, the args:%v of type isn't string", k)
-		}
-	}
+// RawRequest a adapter function of server.RestServerRequest's RawRequest
+func (grra *GoRestfulRequestAdapter) RawRequest() *http.Request {
+	return grra.request.Request
+}
 
-	return args
+// PathParameter a adapter function of server.RestServerRequest's PathParameter
+func (grra *GoRestfulRequestAdapter) PathParameter(name string) string {
+	return grra.request.PathParameter(name)
 }
 
-func GetNewGoRestfulServer() server.RestServer {
-	return NewGoRestfulServer()
+// PathParameters a adapter function of server.RestServerRequest's QueryParameter
+func (grra *GoRestfulRequestAdapter) PathParameters() map[string]string {
+	return grra.request.PathParameters()
 }
 
-// Let user addFilter
-// addFilter should before config.Load()
-func AddGoRestfulServerFilter(filterFuc restful.FilterFunction) {
-	filterSlice = append(filterSlice, filterFuc)
+// QueryParameter a adapter function of server.RestServerRequest's QueryParameters
+func (grra *GoRestfulRequestAdapter) QueryParameter(name string) string {
+	return grra.request.QueryParameter(name)
+}
+
+// QueryParameters a adapter function of server.RestServerRequest's QueryParameters
+func (grra *GoRestfulRequestAdapter) QueryParameters(name string) []string {
+	return grra.request.QueryParameters(name)
+}
+
+// BodyParameter a adapter function of server.RestServerRequest's BodyParameter
+func (grra *GoRestfulRequestAdapter) BodyParameter(name string) (string, error) {
+	return grra.request.BodyParameter(name)
+}
+
+// HeaderParameter a adapter function of server.RestServerRequest's HeaderParameter
+func (grra *GoRestfulRequestAdapter) HeaderParameter(name string) string {
+	return grra.request.HeaderParameter(name)
+}
+
+// ReadEntity a adapter func of server.RestServerRequest's ReadEntity
+func (grra *GoRestfulRequestAdapter) ReadEntity(entityPointer interface{}) error {
+	return grra.request.ReadEntity(entityPointer)
 }
diff --git a/registry/base_registry.go b/registry/base_registry.go
index 3b64e93e2f6b5b58a70650f589dec3ca092376c1..3e1bddf233310871182544b6415c10c8df27e622 100644
--- a/registry/base_registry.go
+++ b/registry/base_registry.go
@@ -121,6 +121,7 @@ func (r *BaseRegistry) Destroy() {
 	close(r.done)
 	// wait waitgroup done (wait listeners outside close over)
 	r.wg.Wait()
+
 	//close registry client
 	r.closeRegisters()
 }
@@ -178,7 +179,10 @@ func (r *BaseRegistry) RestartCallBack() bool {
 		}
 		logger.Infof("success to re-register service :%v", confIf.Key())
 	}
-	r.facadeBasedRegistry.InitListeners()
+
+	if flag {
+		r.facadeBasedRegistry.InitListeners()
+	}
 
 	return flag
 }
@@ -245,19 +249,13 @@ func (r *BaseRegistry) providerRegistry(c common.URL, params url.Values) (string
 		logger.Errorf("facadeBasedRegistry.CreatePath(path{%s}) = error{%#v}", dubboPath, perrors.WithStack(err))
 		return "", "", perrors.WithMessagef(err, "facadeBasedRegistry.CreatePath(path:%s)", dubboPath)
 	}
-	params.Add("anyhost", "true")
+	params.Add(constant.ANYHOST_KEY, "true")
 
 	// Dubbo java consumer to start looking for the provider url,because the category does not match,
 	// the provider will not find, causing the consumer can not start, so we use consumers.
-	// DubboRole               = [...]string{"consumer", "", "", "provider"}
-	// params.Add("category", (RoleType(PROVIDER)).Role())
-	params.Add("category", (common.RoleType(common.PROVIDER)).String())
-	params.Add("dubbo", "dubbo-provider-golang-"+constant.Version)
-
-	params.Add("side", (common.RoleType(common.PROVIDER)).Role())
 
 	if len(c.Methods) == 0 {
-		params.Add("methods", strings.Join(c.Methods, ","))
+		params.Add(constant.METHODS_KEY, strings.Join(c.Methods, ","))
 	}
 	logger.Debugf("provider url params:%#v", params)
 	var host string
@@ -308,9 +306,6 @@ func (r *BaseRegistry) consumerRegistry(c common.URL, params url.Values) (string
 	}
 
 	params.Add("protocol", c.Protocol)
-	params.Add("category", (common.RoleType(common.CONSUMER)).String())
-	params.Add("dubbo", "dubbogo-consumer-"+constant.Version)
-
 	rawURL = fmt.Sprintf("consumer://%s%s?%s", localIP, c.Path, params.Encode())
 	dubboPath = fmt.Sprintf("/dubbo/%s/%s", r.service(c), (common.RoleType(common.CONSUMER)).String())
 
diff --git a/registry/directory/directory.go b/registry/directory/directory.go
index a6d2cdf49b0935b2402e03208d1ff5f702e1cc52..552aa57061c99bf92ff986b6e672743ebb375e76 100644
--- a/registry/directory/directory.go
+++ b/registry/directory/directory.go
@@ -19,7 +19,6 @@ package directory
 
 import (
 	"sync"
-	"time"
 )
 
 import (
@@ -28,6 +27,7 @@ import (
 )
 
 import (
+	"github.com/apache/dubbo-go/cluster"
 	"github.com/apache/dubbo-go/cluster/directory"
 	"github.com/apache/dubbo-go/common"
 	"github.com/apache/dubbo-go/common/constant"
@@ -42,15 +42,11 @@ import (
 	"github.com/apache/dubbo-go/remoting"
 )
 
-// Options ...
-type Options struct {
-	serviceTTL time.Duration
+func init() {
+	extension.SetDefaultRegistryDirectory(NewRegistryDirectory)
 }
 
-// Option ...
-type Option func(*Options)
-
-type registryDirectory struct {
+type RegistryDirectory struct {
 	directory.BaseDirectory
 	cacheInvokers                  []protocol.Invoker
 	listenerLock                   sync.Mutex
@@ -61,48 +57,41 @@ type registryDirectory struct {
 	configurators                  []config_center.Configurator
 	consumerConfigurationListener  *consumerConfigurationListener
 	referenceConfigurationListener *referenceConfigurationListener
-	Options
-	serviceKey string
-	forbidden  atomic.Bool
+	serviceKey                     string
+	forbidden                      atomic.Bool
 }
 
 // NewRegistryDirectory ...
-func NewRegistryDirectory(url *common.URL, registry registry.Registry, opts ...Option) (*registryDirectory, error) {
-	options := Options{
-		//default 300s
-		serviceTTL: time.Duration(300e9),
-	}
-	for _, opt := range opts {
-		opt(&options)
-	}
+func NewRegistryDirectory(url *common.URL, registry registry.Registry) (cluster.Directory, error) {
 	if url.SubURL == nil {
 		return nil, perrors.Errorf("url is invalid, suburl can not be nil")
 	}
-	dir := &registryDirectory{
+	dir := &RegistryDirectory{
 		BaseDirectory:    directory.NewBaseDirectory(url),
 		cacheInvokers:    []protocol.Invoker{},
 		cacheInvokersMap: &sync.Map{},
 		serviceType:      url.SubURL.Service(),
 		registry:         registry,
-		Options:          options,
 	}
 	dir.consumerConfigurationListener = newConsumerConfigurationListener(dir)
+
+	go dir.subscribe(url.SubURL)
 	return dir, nil
 }
 
 //subscribe from registry
-func (dir *registryDirectory) Subscribe(url *common.URL) {
+func (dir *RegistryDirectory) subscribe(url *common.URL) {
 	dir.consumerConfigurationListener.addNotifyListener(dir)
 	dir.referenceConfigurationListener = newReferenceConfigurationListener(dir, url)
 	dir.registry.Subscribe(url, dir)
 }
 
-func (dir *registryDirectory) Notify(event *registry.ServiceEvent) {
+func (dir *RegistryDirectory) Notify(event *registry.ServiceEvent) {
 	go dir.update(event)
 }
 
-//subscribe service from registry, and update the cacheServices
-func (dir *registryDirectory) update(res *registry.ServiceEvent) {
+// update the cacheServices and subscribe service from registry
+func (dir *RegistryDirectory) update(res *registry.ServiceEvent) {
 	if res == nil {
 		return
 	}
@@ -111,7 +100,7 @@ func (dir *registryDirectory) update(res *registry.ServiceEvent) {
 	dir.refreshInvokers(res)
 }
 
-func (dir *registryDirectory) refreshInvokers(res *registry.ServiceEvent) {
+func (dir *RegistryDirectory) refreshInvokers(res *registry.ServiceEvent) {
 	var (
 		url        *common.URL
 		oldInvoker protocol.Invoker = nil
@@ -127,12 +116,13 @@ func (dir *registryDirectory) refreshInvokers(res *registry.ServiceEvent) {
 		} else if url.Protocol == constant.ROUTER_PROTOCOL || //2.for router
 			url.GetParam(constant.CATEGORY_KEY, constant.DEFAULT_CATEGORY) == constant.ROUTER_CATEGORY {
 			url = nil
+
 		}
 		switch res.Action {
 		case remoting.EventTypeAdd, remoting.EventTypeUpdate:
 			logger.Infof("selector add service url{%s}", res.Service)
-			var urls []*common.URL
 
+			var urls []*common.URL
 			for _, v := range directory.GetRouterURLSet().Values() {
 				urls = append(urls, v.(*common.URL))
 			}
@@ -140,8 +130,6 @@ func (dir *registryDirectory) refreshInvokers(res *registry.ServiceEvent) {
 			if len(urls) > 0 {
 				dir.SetRouters(urls)
 			}
-
-			//dir.cacheService.EventTypeAdd(res.Path, dir.serviceTTL)
 			oldInvoker = dir.cacheInvoker(url)
 		case remoting.EventTypeDel:
 			oldInvoker = dir.uncacheInvoker(url)
@@ -163,7 +151,7 @@ func (dir *registryDirectory) refreshInvokers(res *registry.ServiceEvent) {
 
 }
 
-func (dir *registryDirectory) toGroupInvokers() []protocol.Invoker {
+func (dir *RegistryDirectory) toGroupInvokers() []protocol.Invoker {
 	newInvokersList := []protocol.Invoker{}
 	groupInvokersMap := make(map[string][]protocol.Invoker)
 	groupInvokersList := []protocol.Invoker{}
@@ -199,8 +187,8 @@ func (dir *registryDirectory) toGroupInvokers() []protocol.Invoker {
 	return groupInvokersList
 }
 
-// uncacheInvoker return abandoned Invoker,if no Invoker to be abandoned,return nil
-func (dir *registryDirectory) uncacheInvoker(url *common.URL) protocol.Invoker {
+// 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())
@@ -209,8 +197,8 @@ func (dir *registryDirectory) uncacheInvoker(url *common.URL) protocol.Invoker {
 	return nil
 }
 
-// cacheInvoker return abandoned Invoker,if no Invoker to be abandoned,return nil
-func (dir *registryDirectory) cacheInvoker(url *common.URL) protocol.Invoker {
+// cacheInvoker will return abandoned Invoker,if no Invoker to be abandoned,return nil
+func (dir *RegistryDirectory) cacheInvoker(url *common.URL) protocol.Invoker {
 	dir.overrideUrl(dir.GetDirectoryUrl())
 	referenceUrl := dir.GetDirectoryUrl().SubURL
 
@@ -245,8 +233,8 @@ func (dir *registryDirectory) cacheInvoker(url *common.URL) protocol.Invoker {
 	return nil
 }
 
-//select the protocol invokers from the directory
-func (dir *registryDirectory) List(invocation protocol.Invocation) []protocol.Invoker {
+// List selected protocol invokers from the directory
+func (dir *RegistryDirectory) List(invocation protocol.Invocation) []protocol.Invoker {
 	invokers := dir.cacheInvokers
 	routerChain := dir.RouterChain()
 
@@ -256,7 +244,7 @@ func (dir *registryDirectory) List(invocation protocol.Invocation) []protocol.In
 	return routerChain.Route(invokers, dir.cacheOriginUrl, invocation)
 }
 
-func (dir *registryDirectory) IsAvailable() bool {
+func (dir *RegistryDirectory) IsAvailable() bool {
 	if !dir.BaseDirectory.IsAvailable() {
 		return dir.BaseDirectory.IsAvailable()
 	}
@@ -270,7 +258,7 @@ func (dir *registryDirectory) IsAvailable() bool {
 	return false
 }
 
-func (dir *registryDirectory) Destroy() {
+func (dir *RegistryDirectory) Destroy() {
 	//TODO:unregister & unsubscribe
 	dir.BaseDirectory.Destroy(func() {
 		invokers := dir.cacheInvokers
@@ -281,7 +269,7 @@ func (dir *registryDirectory) Destroy() {
 	})
 }
 
-func (dir *registryDirectory) overrideUrl(targetUrl *common.URL) {
+func (dir *RegistryDirectory) overrideUrl(targetUrl *common.URL) {
 	doOverrideUrl(dir.configurators, targetUrl)
 	doOverrideUrl(dir.consumerConfigurationListener.Configurators(), targetUrl)
 	doOverrideUrl(dir.referenceConfigurationListener.Configurators(), targetUrl)
@@ -295,11 +283,11 @@ func doOverrideUrl(configurators []config_center.Configurator, targetUrl *common
 
 type referenceConfigurationListener struct {
 	registry.BaseConfigurationListener
-	directory *registryDirectory
+	directory *RegistryDirectory
 	url       *common.URL
 }
 
-func newReferenceConfigurationListener(dir *registryDirectory, url *common.URL) *referenceConfigurationListener {
+func newReferenceConfigurationListener(dir *RegistryDirectory, url *common.URL) *referenceConfigurationListener {
 	listener := &referenceConfigurationListener{directory: dir, url: url}
 	listener.InitWith(
 		url.EncodedServiceKey()+constant.CONFIGURATORS_SUFFIX,
@@ -317,10 +305,10 @@ func (l *referenceConfigurationListener) Process(event *config_center.ConfigChan
 type consumerConfigurationListener struct {
 	registry.BaseConfigurationListener
 	listeners []registry.NotifyListener
-	directory *registryDirectory
+	directory *RegistryDirectory
 }
 
-func newConsumerConfigurationListener(dir *registryDirectory) *consumerConfigurationListener {
+func newConsumerConfigurationListener(dir *RegistryDirectory) *consumerConfigurationListener {
 	listener := &consumerConfigurationListener{directory: dir}
 	listener.InitWith(
 		config.GetConsumerConfig().ApplicationConfig.Name+constant.CONFIGURATORS_SUFFIX,
diff --git a/registry/directory/directory_test.go b/registry/directory/directory_test.go
index 0dde44e73c18f65f262e01f499e198995907dece..f1d5ce434aa00185f784f208eefe603274f05ab0 100644
--- a/registry/directory/directory_test.go
+++ b/registry/directory/directory_test.go
@@ -79,10 +79,9 @@ func TestSubscribe_Group(t *testing.T) {
 	suburl.SetParam(constant.CLUSTER_KEY, "mock")
 	regurl.SubURL = &suburl
 	mockRegistry, _ := registry.NewMockRegistry(&common.URL{})
-	registryDirectory, _ := NewRegistryDirectory(&regurl, mockRegistry)
-
-	go registryDirectory.Subscribe(common.NewURLWithOptions(common.WithPath("testservice")))
+	dir, _ := NewRegistryDirectory(&regurl, mockRegistry)
 
+	go dir.(*RegistryDirectory).subscribe(common.NewURLWithOptions(common.WithPath("testservice")))
 	//for group1
 	urlmap := url.Values{}
 	urlmap.Set(constant.GROUP_KEY, "group1")
@@ -101,7 +100,7 @@ func TestSubscribe_Group(t *testing.T) {
 	}
 
 	time.Sleep(1e9)
-	assert.Len(t, registryDirectory.cacheInvokers, 2)
+	assert.Len(t, dir.(*RegistryDirectory).cacheInvokers, 2)
 }
 
 func Test_Destroy(t *testing.T) {
@@ -173,7 +172,7 @@ Loop1:
 
 }
 
-func normalRegistryDir(noMockEvent ...bool) (*registryDirectory, *registry.MockRegistry) {
+func normalRegistryDir(noMockEvent ...bool) (*RegistryDirectory, *registry.MockRegistry) {
 	extension.SetProtocol(protocolwrapper.FILTER, protocolwrapper.NewMockProtocolFilter)
 
 	url, _ := common.NewURL("mock://127.0.0.1:1111")
@@ -185,9 +184,9 @@ func normalRegistryDir(noMockEvent ...bool) (*registryDirectory, *registry.MockR
 	)
 	url.SubURL = &suburl
 	mockRegistry, _ := registry.NewMockRegistry(&common.URL{})
-	registryDirectory, _ := NewRegistryDirectory(&url, mockRegistry)
+	dir, _ := NewRegistryDirectory(&url, mockRegistry)
 
-	go registryDirectory.Subscribe(&suburl)
+	go dir.(*RegistryDirectory).subscribe(&suburl)
 	if len(noMockEvent) == 0 {
 		for i := 0; i < 3; i++ {
 			mockRegistry.(*registry.MockRegistry).MockEvent(
@@ -201,5 +200,5 @@ func normalRegistryDir(noMockEvent ...bool) (*registryDirectory, *registry.MockR
 			)
 		}
 	}
-	return registryDirectory, mockRegistry.(*registry.MockRegistry)
+	return dir.(*RegistryDirectory), mockRegistry.(*registry.MockRegistry)
 }
diff --git a/registry/etcdv3/registry.go b/registry/etcdv3/registry.go
index e1c25768119ea7d7122b9aa22a5f881db44bafd9..5d389c36374fe9de5561418bc90d44a7d780fd48 100644
--- a/registry/etcdv3/registry.go
+++ b/registry/etcdv3/registry.go
@@ -164,9 +164,7 @@ func (r *etcdV3Registry) DoSubscribe(svc *common.URL) (registry.Listener, error)
 
 	//register the svc to dataListener
 	r.dataListener.AddInterestedURL(svc)
-	for _, v := range strings.Split(svc.GetParam(constant.CATEGORY_KEY, constant.DEFAULT_CATEGORY), ",") {
-		go r.listener.ListenServiceEvent(fmt.Sprintf("/dubbo/%s/"+v, svc.Service()), r.dataListener)
-	}
+	go r.listener.ListenServiceEvent(fmt.Sprintf("/dubbo/%s/"+constant.DEFAULT_CATEGORY, svc.Service()), r.dataListener)
 
 	return configListener, nil
 }
diff --git a/registry/etcdv3/registry_test.go b/registry/etcdv3/registry_test.go
index dc4e382979d910e4f42453fa0a409afbcb0ecabc..164fe9ca61793614e3d9d2f77f49cdfaebdd7317 100644
--- a/registry/etcdv3/registry_test.go
+++ b/registry/etcdv3/registry_test.go
@@ -63,7 +63,7 @@ func (suite *RegistryTestSuite) TestRegister() {
 	if err != nil {
 		t.Fatal(err)
 	}
-	assert.Regexp(t, ".*dubbo%3A%2F%2F127.0.0.1%3A20000%2Fcom.ikurento.user.UserProvider%3Fanyhost%3Dtrue%26category%3Dproviders%26cluster%3Dmock%26dubbo%3Ddubbo-provider-golang-1.3.0%26.*provider", children)
+	assert.Regexp(t, ".*dubbo%3A%2F%2F127.0.0.1%3A20000%2Fcom.ikurento.user.UserProvider%3Fanyhost%3Dtrue%26cluster%3Dmock", children)
 	assert.NoError(t, err)
 }
 
diff --git a/registry/event.go b/registry/event.go
index 37d863d2162cb3b9d6a9f7eba8823286eb99441c..be9f11d00bb5a70b0d787d15bcdc98471aad0a4b 100644
--- a/registry/event.go
+++ b/registry/event.go
@@ -32,9 +32,9 @@ func init() {
 	rand.Seed(time.Now().UnixNano())
 }
 
-//////////////////////////////////////////
+// ////////////////////////////////////////
 // service event
-//////////////////////////////////////////
+// ////////////////////////////////////////
 
 // ServiceEvent ...
 type ServiceEvent struct {
@@ -42,6 +42,69 @@ type ServiceEvent struct {
 	Service common.URL
 }
 
+// String return the description of event
 func (e ServiceEvent) String() string {
 	return fmt.Sprintf("ServiceEvent{Action{%s}, Path{%s}}", e.Action, e.Service)
 }
+
+// Event is align with Event interface in Java.
+// it's the top abstraction
+// Align with 2.7.5
+type Event interface {
+	fmt.Stringer
+	GetSource() interface{}
+	GetTimestamp() time.Time
+}
+
+// baseEvent is the base implementation of Event
+// You should never use it directly
+type baseEvent struct {
+	source    interface{}
+	timestamp time.Time
+}
+
+// GetSource return the source
+func (b *baseEvent) GetSource() interface{} {
+	return b.source
+}
+
+// GetTimestamp return the timestamp when the event is created
+func (b *baseEvent) GetTimestamp() time.Time {
+	return b.timestamp
+}
+
+// String return a human readable string representing this event
+func (b *baseEvent) String() string {
+	return fmt.Sprintf("baseEvent[source = %#v]", b.source)
+}
+
+func newBaseEvent(source interface{}) *baseEvent {
+	return &baseEvent{
+		source:    source,
+		timestamp: time.Now(),
+	}
+}
+
+// ServiceInstancesChangedEvent represents service instances make some changing
+type ServiceInstancesChangedEvent struct {
+	baseEvent
+	ServiceName string
+	Instances   []ServiceInstance
+}
+
+// String return the description of the event
+func (s *ServiceInstancesChangedEvent) String() string {
+	return fmt.Sprintf("ServiceInstancesChangedEvent[source=%s]", s.ServiceName)
+}
+
+// NewServiceInstancesChangedEvent will create the ServiceInstanceChangedEvent instance
+func NewServiceInstancesChangedEvent(serviceName string, instances []ServiceInstance) *ServiceInstancesChangedEvent {
+	return &ServiceInstancesChangedEvent{
+		baseEvent: baseEvent{
+			source:    serviceName,
+			timestamp: time.Now(),
+		},
+		ServiceName: serviceName,
+		Instances:   instances,
+	}
+}
diff --git a/registry/event_listener.go b/registry/event_listener.go
new file mode 100644
index 0000000000000000000000000000000000000000..b8d6148442d9e10e210958dead690c4a95b33fb6
--- /dev/null
+++ b/registry/event_listener.go
@@ -0,0 +1,42 @@
+/*
+ * 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
+
+import (
+	gxsort "github.com/dubbogo/gost/sort"
+)
+
+// EventListener is an new interface used to align with dubbo 2.7.5
+// It contains the Prioritized means that the listener has its priority
+type EventListener interface {
+	gxsort.Prioritizer
+	// OnEvent handle this event
+	OnEvent(e Event) error
+}
+
+// ConditionalEventListener only handle the event which it can handle
+type ConditionalEventListener interface {
+	EventListener
+	// Accept will make the decision whether it should handle this event
+	Accept(e Event) bool
+}
+
+// TODO (implement ConditionalEventListener)
+type ServiceInstancesChangedListener struct {
+	ServiceName string
+}
diff --git a/registry/kubernetes/registry.go b/registry/kubernetes/registry.go
index 7212a83d63b34cc08f193994f004efccf9e21a0c..8a02d0e3e693b58946a97e7b47238e0be4272dcf 100644
--- a/registry/kubernetes/registry.go
+++ b/registry/kubernetes/registry.go
@@ -21,7 +21,6 @@ import (
 	"fmt"
 	"os"
 	"path"
-	"strings"
 	"sync"
 	"time"
 )
@@ -135,9 +134,7 @@ func (r *kubernetesRegistry) DoSubscribe(svc *common.URL) (registry.Listener, er
 
 	//register the svc to dataListener
 	r.dataListener.AddInterestedURL(svc)
-	for _, v := range strings.Split(svc.GetParam(constant.CATEGORY_KEY, constant.DEFAULT_CATEGORY), ",") {
-		go r.listener.ListenServiceEvent(fmt.Sprintf("/dubbo/%s/"+v, svc.Service()), r.dataListener)
-	}
+	go r.listener.ListenServiceEvent(fmt.Sprintf("/dubbo/%s/"+constant.DEFAULT_CATEGORY, svc.Service()), r.dataListener)
 
 	return configListener, nil
 }
diff --git a/registry/nacos/base_registry.go b/registry/nacos/base_registry.go
new file mode 100644
index 0000000000000000000000000000000000000000..63f4999675470853d0f48d1a22b709efdc1c9d26
--- /dev/null
+++ b/registry/nacos/base_registry.go
@@ -0,0 +1,102 @@
+/*
+ * 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 nacos
+
+import (
+	"net"
+	"strconv"
+	"strings"
+	"time"
+)
+
+import (
+	"github.com/nacos-group/nacos-sdk-go/clients"
+	"github.com/nacos-group/nacos-sdk-go/clients/naming_client"
+	nacosConstant "github.com/nacos-group/nacos-sdk-go/common/constant"
+	perrors "github.com/pkg/errors"
+)
+
+import (
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/common/constant"
+)
+
+// baseRegistry is the parent of both interface-level registry
+// and service discovery(related to application-level registry)
+type nacosBaseRegistry struct {
+	*common.URL
+	namingClient naming_client.INamingClient
+}
+
+// newBaseRegistry will create new instance
+func newBaseRegistry(url *common.URL) (nacosBaseRegistry, error) {
+	nacosConfig, err := getNacosConfig(url)
+	if err != nil {
+		return nacosBaseRegistry{}, err
+	}
+	client, err := clients.CreateNamingClient(nacosConfig)
+	if err != nil {
+		return nacosBaseRegistry{}, err
+	}
+	registry := nacosBaseRegistry{
+		URL:          url,
+		namingClient: client,
+	}
+	return registry, nil
+}
+
+// getNacosConfig will return the nacos config
+func getNacosConfig(url *common.URL) (map[string]interface{}, error) {
+	if url == nil {
+		return nil, perrors.New("url is empty!")
+	}
+	if len(url.Location) == 0 {
+		return nil, perrors.New("url.location is empty!")
+	}
+	configMap := make(map[string]interface{}, 2)
+
+	addresses := strings.Split(url.Location, ",")
+	serverConfigs := make([]nacosConstant.ServerConfig, 0, len(addresses))
+	for _, addr := range addresses {
+		ip, portStr, err := net.SplitHostPort(addr)
+		if err != nil {
+			return nil, perrors.WithMessagef(err, "split [%s] ", addr)
+		}
+		port, _ := strconv.Atoi(portStr)
+		serverConfigs = append(serverConfigs, nacosConstant.ServerConfig{
+			IpAddr: ip,
+			Port:   uint64(port),
+		})
+	}
+	configMap["serverConfigs"] = serverConfigs
+
+	var clientConfig nacosConstant.ClientConfig
+	timeout, err := time.ParseDuration(url.GetParam(constant.REGISTRY_TIMEOUT_KEY, constant.DEFAULT_REG_TIMEOUT))
+	if err != nil {
+		return nil, err
+	}
+	clientConfig.TimeoutMs = uint64(timeout.Seconds() * 1000)
+	clientConfig.ListenInterval = 2 * clientConfig.TimeoutMs
+	clientConfig.CacheDir = url.GetParam(constant.NACOS_CACHE_DIR_KEY, "")
+	clientConfig.LogDir = url.GetParam(constant.NACOS_LOG_DIR_KEY, "")
+	clientConfig.Endpoint = url.GetParam(constant.NACOS_ENDPOINT, "")
+	clientConfig.NotLoadCacheAtStart = true
+	configMap["clientConfig"] = clientConfig
+
+	return configMap, nil
+}
diff --git a/registry/nacos/registry.go b/registry/nacos/registry.go
index 965e91e894ac61562bfd25c8f564f789afd6c8a1..a436b85064829b9f42c9dcc45545e5bf2fd2fefe 100644
--- a/registry/nacos/registry.go
+++ b/registry/nacos/registry.go
@@ -19,7 +19,6 @@ package nacos
 
 import (
 	"bytes"
-	"net"
 	"strconv"
 	"strings"
 	"time"
@@ -27,9 +26,6 @@ import (
 
 import (
 	gxnet "github.com/dubbogo/gost/net"
-	"github.com/nacos-group/nacos-sdk-go/clients"
-	"github.com/nacos-group/nacos-sdk-go/clients/naming_client"
-	nacosConstant "github.com/nacos-group/nacos-sdk-go/common/constant"
 	"github.com/nacos-group/nacos-sdk-go/vo"
 	perrors "github.com/pkg/errors"
 )
@@ -57,64 +53,18 @@ func init() {
 }
 
 type nacosRegistry struct {
-	*common.URL
-	namingClient naming_client.INamingClient
-}
-
-func getNacosConfig(url *common.URL) (map[string]interface{}, error) {
-	if url == nil {
-		return nil, perrors.New("url is empty!")
-	}
-	if len(url.Location) == 0 {
-		return nil, perrors.New("url.location is empty!")
-	}
-	configMap := make(map[string]interface{}, 2)
-
-	addresses := strings.Split(url.Location, ",")
-	serverConfigs := make([]nacosConstant.ServerConfig, 0, len(addresses))
-	for _, addr := range addresses {
-		ip, portStr, err := net.SplitHostPort(addr)
-		if err != nil {
-			return nil, perrors.WithMessagef(err, "split [%s] ", addr)
-		}
-		port, _ := strconv.Atoi(portStr)
-		serverConfigs = append(serverConfigs, nacosConstant.ServerConfig{
-			IpAddr: ip,
-			Port:   uint64(port),
-		})
-	}
-	configMap["serverConfigs"] = serverConfigs
-
-	var clientConfig nacosConstant.ClientConfig
-	timeout, err := time.ParseDuration(url.GetParam(constant.REGISTRY_TIMEOUT_KEY, constant.DEFAULT_REG_TIMEOUT))
-	if err != nil {
-		return nil, err
-	}
-	clientConfig.TimeoutMs = uint64(timeout.Seconds() * 1000)
-	clientConfig.ListenInterval = 2 * clientConfig.TimeoutMs
-	clientConfig.CacheDir = url.GetParam(constant.NACOS_CACHE_DIR_KEY, "")
-	clientConfig.LogDir = url.GetParam(constant.NACOS_LOG_DIR_KEY, "")
-	clientConfig.Endpoint = url.GetParam(constant.NACOS_ENDPOINT, "")
-	clientConfig.NotLoadCacheAtStart = true
-	configMap["clientConfig"] = clientConfig
-
-	return configMap, nil
+	nacosBaseRegistry
 }
 
+// newNacosRegistry will create an instance
 func newNacosRegistry(url *common.URL) (registry.Registry, error) {
-	nacosConfig, err := getNacosConfig(url)
+	base, err := newBaseRegistry(url)
 	if err != nil {
-		return nil, err
-	}
-	client, err := clients.CreateNamingClient(nacosConfig)
-	if err != nil {
-		return nil, err
-	}
-	registry := nacosRegistry{
-		URL:          url,
-		namingClient: client,
+		return nil, perrors.WithStack(err)
 	}
-	return &registry, nil
+	return &nacosRegistry{
+		base,
+	}, nil
 }
 
 func getCategory(url common.URL) string {
diff --git a/registry/nacos/service_discovery.go b/registry/nacos/service_discovery.go
new file mode 100644
index 0000000000000000000000000000000000000000..7d3406cac233dd5293c7522b4f12148fdcdd704e
--- /dev/null
+++ b/registry/nacos/service_discovery.go
@@ -0,0 +1,285 @@
+/*
+ * 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 nacos
+
+import (
+	"github.com/dubbogo/gost/container/set"
+	"github.com/dubbogo/gost/page"
+	"github.com/nacos-group/nacos-sdk-go/model"
+	"github.com/nacos-group/nacos-sdk-go/vo"
+	perrors "github.com/pkg/errors"
+)
+
+import (
+	"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/registry"
+)
+
+const (
+	defaultGroup = "DEFAULT_GROUP"
+	idKey        = "id"
+)
+
+// init will put the service discovery into extension
+func init() {
+	extension.SetServiceDiscovery(constant.NACOS_KEY, newNacosServiceDiscovery)
+}
+
+// nacosServiceDiscovery is the implementation of service discovery based on nacos.
+// There is a problem, the go client for nacos does not support the id field.
+// we will use the metadata to store the id of ServiceInstance
+type nacosServiceDiscovery struct {
+	nacosBaseRegistry
+	group string
+}
+
+// Destroy will close the service discovery.
+// Actually, it only marks the naming client as null and then return
+func (n *nacosServiceDiscovery) Destroy() error {
+	n.namingClient = nil
+	return nil
+}
+
+// Register will register the service to nacos
+func (n *nacosServiceDiscovery) Register(instance registry.ServiceInstance) error {
+	ins := n.toRegisterInstance(instance)
+	ok, err := n.namingClient.RegisterInstance(ins)
+	if err != nil || !ok {
+		return perrors.WithMessage(err, "Could not register the instance. "+instance.GetServiceName())
+	}
+	return nil
+}
+
+// Update will update the information
+// However, because nacos client doesn't support the update API,
+// so we should unregister the instance and then register it again.
+// the error handling is hard to implement
+func (n *nacosServiceDiscovery) Update(instance registry.ServiceInstance) error {
+	// TODO(wait for nacos support)
+	err := n.Unregister(instance)
+	if err != nil {
+		return perrors.WithStack(err)
+	}
+	return n.Register(instance)
+}
+
+// Unregister will unregister the instance
+func (n *nacosServiceDiscovery) Unregister(instance registry.ServiceInstance) error {
+	ok, err := n.namingClient.DeregisterInstance(n.toDeregisterInstance(instance))
+	if err != nil || !ok {
+		return perrors.WithMessage(err, "Could not unregister the instance. "+instance.GetServiceName())
+	}
+	return nil
+}
+
+// GetDefaultPageSize will return the constant registry.DefaultPageSize
+func (n *nacosServiceDiscovery) GetDefaultPageSize() int {
+	return registry.DefaultPageSize
+}
+
+// GetServices will return the all services
+func (n *nacosServiceDiscovery) GetServices() *gxset.HashSet {
+	services, err := n.namingClient.GetAllServicesInfo(vo.GetAllServiceInfoParam{
+		GroupName: n.group,
+	})
+
+	res := gxset.NewSet()
+	if err != nil {
+		logger.Errorf("Could not query the services: %v", err)
+		return res
+	}
+
+	for _, e := range services {
+		res.Add(e.Name)
+	}
+	return res
+}
+
+// GetInstances will return the instances of serviceName and the group
+func (n *nacosServiceDiscovery) GetInstances(serviceName string) []registry.ServiceInstance {
+	instances, err := n.namingClient.SelectAllInstances(vo.SelectAllInstancesParam{
+		ServiceName: serviceName,
+		GroupName:   n.group,
+	})
+	if err != nil {
+		logger.Errorf("Could not query the instances for service: " + serviceName + ", group: " + n.group)
+		return make([]registry.ServiceInstance, 0, 0)
+	}
+	res := make([]registry.ServiceInstance, 0, len(instances))
+	for _, ins := range instances {
+		metadata := ins.Metadata
+		id := metadata[idKey]
+
+		delete(metadata, idKey)
+
+		res = append(res, &registry.DefaultServiceInstance{
+			Id:          id,
+			ServiceName: ins.ServiceName,
+			Host:        ins.Ip,
+			Port:        int(ins.Port),
+			Enable:      ins.Enable,
+			Healthy:     ins.Healthy,
+			Metadata:    metadata,
+		})
+	}
+
+	return res
+}
+
+// GetInstancesByPage will return the instances
+// Due to nacos client does not support pagination, so we have to query all instances and then return part of them
+func (n *nacosServiceDiscovery) GetInstancesByPage(serviceName string, offset int, pageSize int) gxpage.Pager {
+	all := n.GetInstances(serviceName)
+	res := make([]interface{}, 0, pageSize)
+	// could not use res = all[a:b] here because the res should be []interface{}, not []ServiceInstance
+	for i := offset; i < len(all) && i < offset+pageSize; i++ {
+		res = append(res, all[i])
+	}
+	return gxpage.New(offset, pageSize, res, len(all))
+}
+
+// GetHealthyInstancesByPage will return the instance
+// The nacos client has an API SelectInstances, which has a parameter call HealthyOnly.
+// However, the healthy parameter in this method maybe false. So we can not use that API.
+// Thus, we must query all instances and then do filter
+func (n *nacosServiceDiscovery) GetHealthyInstancesByPage(serviceName string, offset int, pageSize int, healthy bool) gxpage.Pager {
+	all := n.GetInstances(serviceName)
+	res := make([]interface{}, 0, pageSize)
+	// could not use res = all[a:b] here because the res should be []interface{}, not []ServiceInstance
+	var (
+		i     = offset
+		count = 0
+	)
+	for i < len(all) && count < pageSize {
+		ins := all[i]
+		if ins.IsHealthy() == healthy {
+			res = append(res, all[i])
+			count++
+		}
+		i++
+	}
+	return gxpage.New(offset, pageSize, res, len(all))
+}
+
+// GetRequestInstances will return the instances
+// The nacos client doesn't have batch API, so we should query those serviceNames one by one.
+func (n *nacosServiceDiscovery) GetRequestInstances(serviceNames []string, offset int, requestedSize int) map[string]gxpage.Pager {
+	res := make(map[string]gxpage.Pager, len(serviceNames))
+	for _, name := range serviceNames {
+		res[name] = n.GetInstancesByPage(name, offset, requestedSize)
+	}
+	return res
+}
+
+// AddListener will add a listener
+func (n *nacosServiceDiscovery) AddListener(listener *registry.ServiceInstancesChangedListener) error {
+	return n.namingClient.Subscribe(&vo.SubscribeParam{
+		ServiceName: listener.ServiceName,
+		SubscribeCallback: func(services []model.SubscribeService, err error) {
+			if err != nil {
+				logger.Errorf("Could not handle the subscribe notification because the err is not nil."+
+					" service name: %s, err: %v", listener.ServiceName, err)
+			}
+			instances := make([]registry.ServiceInstance, 0, len(services))
+			for _, service := range services {
+				// we won't use the nacos instance id here but use our instance id
+				metadata := service.Metadata
+				id := metadata[idKey]
+
+				delete(metadata, idKey)
+
+				instances = append(instances, &registry.DefaultServiceInstance{
+					Id:          id,
+					ServiceName: service.ServiceName,
+					Host:        service.Ip,
+					Port:        int(service.Port),
+					Enable:      service.Enable,
+					Healthy:     true,
+					Metadata:    metadata,
+				})
+			}
+
+			e := n.DispatchEventForInstances(listener.ServiceName, instances)
+			if e != nil {
+				logger.Errorf("Dispatching event got exception, service name: %s, err: %v", listener.ServiceName, err)
+			}
+		},
+	})
+}
+
+// DispatchEventByServiceName will dispatch the event for the service with the service name
+func (n *nacosServiceDiscovery) DispatchEventByServiceName(serviceName string) error {
+	return n.DispatchEventForInstances(serviceName, n.GetInstances(serviceName))
+}
+
+// DispatchEventForInstances will dispatch the event to those instances
+func (n *nacosServiceDiscovery) DispatchEventForInstances(serviceName string, instances []registry.ServiceInstance) error {
+	return n.DispatchEvent(registry.NewServiceInstancesChangedEvent(serviceName, instances))
+}
+
+// DispatchEvent will dispatch the event
+func (n *nacosServiceDiscovery) DispatchEvent(event *registry.ServiceInstancesChangedEvent) error {
+	// TODO(waiting for event dispatcher, another task)
+	return nil
+}
+
+// toRegisterInstance convert the ServiceInstance to RegisterInstanceParam
+// the Ephemeral will be true
+func (n *nacosServiceDiscovery) toRegisterInstance(instance registry.ServiceInstance) vo.RegisterInstanceParam {
+	metadata := instance.GetMetadata()
+	if metadata == nil {
+		metadata = make(map[string]string, 1)
+	}
+	metadata[idKey] = instance.GetId()
+	return vo.RegisterInstanceParam{
+		ServiceName: instance.GetServiceName(),
+		Ip:          instance.GetHost(),
+		Port:        uint64(instance.GetPort()),
+		Metadata:    metadata,
+		Enable:      instance.IsEnable(),
+		Healthy:     instance.IsHealthy(),
+		GroupName:   n.group,
+		Ephemeral:   true,
+	}
+}
+
+// toDeregisterInstance will convert the ServiceInstance to DeregisterInstanceParam
+func (n *nacosServiceDiscovery) toDeregisterInstance(instance registry.ServiceInstance) vo.DeregisterInstanceParam {
+	return vo.DeregisterInstanceParam{
+		ServiceName: instance.GetServiceName(),
+		Ip:          instance.GetHost(),
+		Port:        uint64(instance.GetPort()),
+		GroupName:   n.group,
+	}
+}
+
+// toDeregisterInstance will create new service discovery instance
+func newNacosServiceDiscovery(url *common.URL) (registry.ServiceDiscovery, error) {
+
+	base, err := newBaseRegistry(url)
+	if err != nil {
+		return nil, perrors.WithStack(err)
+	}
+	return &nacosServiceDiscovery{
+		nacosBaseRegistry: base,
+		group:             url.GetParam(constant.NACOS_GROUP, defaultGroup),
+	}, nil
+}
diff --git a/registry/nacos/service_discovery_test.go b/registry/nacos/service_discovery_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..a756e8669301919d406a4bcf0e1c962cf532a5c6
--- /dev/null
+++ b/registry/nacos/service_discovery_test.go
@@ -0,0 +1,122 @@
+/*
+ * 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 nacos
+
+import (
+	"strconv"
+	"testing"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+import (
+	"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/registry"
+)
+
+func TestNacosServiceDiscovery_Destroy(t *testing.T) {
+	serviceDiscovry, err := extension.GetServiceDiscovery(constant.NACOS_KEY, mockUrl())
+	assert.Nil(t, err)
+	assert.NotNil(t, serviceDiscovry)
+	err = serviceDiscovry.Destroy()
+	assert.Nil(t, err)
+	assert.Nil(t, serviceDiscovry.(*nacosServiceDiscovery).namingClient)
+}
+
+func TestNacosServiceDiscovery_CRUD(t *testing.T) {
+	serviceName := "service-name"
+	id := "id"
+	host := "host"
+	port := 123
+	instance := &registry.DefaultServiceInstance{
+		Id:          id,
+		ServiceName: serviceName,
+		Host:        host,
+		Port:        port,
+		Enable:      true,
+		Healthy:     true,
+		Metadata:    nil,
+	}
+
+	// clean data
+
+	serviceDiscovry, _ := extension.GetServiceDiscovery(constant.NACOS_KEY, mockUrl())
+
+	// clean data for local test
+	serviceDiscovry.Unregister(&registry.DefaultServiceInstance{
+		Id:          id,
+		ServiceName: serviceName,
+		Host:        host,
+		Port:        port,
+	})
+
+	err := serviceDiscovry.Register(instance)
+	assert.Nil(t, err)
+
+	page := serviceDiscovry.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())
+
+	instance = page.GetData()[0].(*registry.DefaultServiceInstance)
+	assert.NotNil(t, instance)
+	assert.Equal(t, id, instance.GetId())
+	assert.Equal(t, host, instance.GetHost())
+	assert.Equal(t, port, instance.GetPort())
+	assert.Equal(t, serviceName, instance.GetServiceName())
+	assert.Equal(t, 0, len(instance.GetMetadata()))
+
+	instance.Metadata["a"] = "b"
+
+	err = serviceDiscovry.Update(instance)
+	assert.Nil(t, err)
+
+	pageMap := serviceDiscovry.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()))
+
+	instance = page.GetData()[0].(*registry.DefaultServiceInstance)
+	v, _ := instance.Metadata["a"]
+	assert.Equal(t, "b", v)
+
+	// test dispatcher event
+	err = serviceDiscovry.DispatchEventByServiceName(serviceName)
+	assert.Nil(t, err)
+
+	// test AddListener
+	err = serviceDiscovry.AddListener(&registry.ServiceInstancesChangedListener{})
+	assert.Nil(t, err)
+}
+
+func TestNacosServiceDiscovery_GetDefaultPageSize(t *testing.T) {
+	serviceDiscovry, _ := extension.GetServiceDiscovery(constant.NACOS_KEY, mockUrl())
+	assert.Equal(t, registry.DefaultPageSize, serviceDiscovry.GetDefaultPageSize())
+}
+
+func mockUrl() *common.URL {
+	regurl, _ := common.NewURL("registry://console.nacos.io:80", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER)))
+	return &regurl
+}
diff --git a/registry/protocol/protocol.go b/registry/protocol/protocol.go
index a7678ba4e2f38cfeb77f202103e03066a7efdbef..52a7dcbfc77fd576ef8d2917ce51cc09f3cd0b97 100644
--- a/registry/protocol/protocol.go
+++ b/registry/protocol/protocol.go
@@ -24,7 +24,7 @@ import (
 )
 
 import (
-	gxset "github.com/dubbogo/gost/container/set"
+	"github.com/dubbogo/gost/container/set"
 )
 
 import (
@@ -39,12 +39,18 @@ import (
 	"github.com/apache/dubbo-go/protocol"
 	"github.com/apache/dubbo-go/protocol/protocolwrapper"
 	"github.com/apache/dubbo-go/registry"
-	directory2 "github.com/apache/dubbo-go/registry/directory"
+	_ "github.com/apache/dubbo-go/registry/directory"
 	"github.com/apache/dubbo-go/remoting"
 )
 
 var (
-	regProtocol *registryProtocol
+	regProtocol   *registryProtocol
+	once          sync.Once
+	reserveParams = []string{
+		"application", "codec", "exchanger", "serialization", "cluster", "connections", "deprecated", "group",
+		"loadbalance", "mock", "path", "timeout", "token", "version", "warmup", "weight", "timestamp", "dubbo",
+		"release", "interface",
+	}
 )
 
 type registryProtocol struct {
@@ -87,6 +93,13 @@ func getRegistry(regUrl *common.URL) registry.Registry {
 	return reg
 }
 
+func getUrlToRegistry(providerUrl *common.URL, registryUrl *common.URL) *common.URL {
+	if registryUrl.GetParamBool("simplified", false) {
+		return providerUrl.CloneWithParams(reserveParams)
+	}
+	return providerUrl
+}
+
 func (proto *registryProtocol) initConfigurationListeners() {
 	proto.overrideListeners = &sync.Map{}
 	proto.serviceConfigurationListeners = &sync.Map{}
@@ -111,7 +124,7 @@ func (proto *registryProtocol) Refer(url common.URL) protocol.Invoker {
 	}
 
 	//new registry directory for store service url from registry
-	directory, err := directory2.NewRegistryDirectory(&registryUrl, reg)
+	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!",
 			serviceUrl.String(), err.Error())
@@ -123,7 +136,6 @@ func (proto *registryProtocol) Refer(url common.URL) protocol.Invoker {
 		logger.Errorf("consumer service %v register registry %v error, error message is %s",
 			serviceUrl.String(), registryUrl.String(), err.Error())
 	}
-	go directory.Subscribe(serviceUrl)
 
 	//new cluster invoker
 	cluster := extension.GetCluster(serviceUrl.GetParam(constant.CLUSTER_KEY, constant.DEFAULT_CLUSTER))
@@ -150,7 +162,6 @@ func (proto *registryProtocol) Export(invoker protocol.Invoker) protocol.Exporte
 	serviceConfigurationListener.OverrideUrl(providerUrl)
 
 	var reg registry.Registry
-
 	if regI, loaded := proto.registries.Load(registryUrl.Key()); !loaded {
 		reg = getRegistry(registryUrl)
 		proto.registries.Store(registryUrl.Key(), reg)
@@ -158,7 +169,8 @@ func (proto *registryProtocol) Export(invoker protocol.Invoker) protocol.Exporte
 		reg = regI.(registry.Registry)
 	}
 
-	err := reg.Register(*providerUrl)
+	registeredProviderUrl := getUrlToRegistry(providerUrl, registryUrl)
+	err := reg.Register(*registeredProviderUrl)
 	if err != nil {
 		logger.Errorf("provider service %v register registry %v error, error message is %s",
 			providerUrl.Key(), registryUrl.Key(), err.Error())
@@ -346,12 +358,12 @@ func setProviderUrl(regURL *common.URL, providerURL *common.URL) {
 	regURL.SubURL = providerURL
 }
 
-// GetProtocol ...
+// GetProtocol return the singleton RegistryProtocol
 func GetProtocol() protocol.Protocol {
-	if regProtocol != nil {
-		return regProtocol
-	}
-	return newRegistryProtocol()
+	once.Do(func() {
+		regProtocol = newRegistryProtocol()
+	})
+	return regProtocol
 }
 
 type wrappedInvoker struct {
diff --git a/registry/service_discovery.go b/registry/service_discovery.go
new file mode 100644
index 0000000000000000000000000000000000000000..a8228a4abe8ed07e3c5afda300702f778daea4ae
--- /dev/null
+++ b/registry/service_discovery.go
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package registry
+
+import (
+	"fmt"
+)
+
+import (
+	gxset "github.com/dubbogo/gost/container/set"
+	gxpage "github.com/dubbogo/gost/page"
+)
+
+const DefaultPageSize = 100
+
+type ServiceDiscovery interface {
+	fmt.Stringer
+
+	// ----------------- lifecycle -------------------
+
+	// Destroy will destroy the service discovery.
+	// If the discovery cannot be destroy, it will return an error.
+	Destroy() error
+
+	// ----------------- registration ----------------
+
+	// Register will register an instance of ServiceInstance to registry
+	Register(instance ServiceInstance) error
+
+	// Update will update the data of the instance in registry
+	Update(instance ServiceInstance) error
+
+	// Unregister will unregister this instance from registry
+	Unregister(instance ServiceInstance) error
+
+	// ----------------- discovery -------------------
+	// GetDefaultPageSize will return the default page size
+	GetDefaultPageSize() int
+
+	// GetServices will return the all service names.
+	GetServices() *gxset.HashSet
+
+	// GetInstances will return all service instances with serviceName
+	GetInstances(serviceName string) []ServiceInstance
+
+	// GetInstancesByPage will return a page containing instances of ServiceInstance with the serviceName
+	// the page will start at offset
+	GetInstancesByPage(serviceName string, offset int, pageSize int) gxpage.Pager
+
+	// GetHealthyInstancesByPage will return a page containing instances of ServiceInstance.
+	// The param healthy indices that the instance should be healthy or not.
+	// The page will start at offset
+	GetHealthyInstancesByPage(serviceName string, offset int, pageSize int, healthy bool) gxpage.Pager
+
+	// Batch get all instances by the specified service names
+	GetRequestInstances(serviceNames []string, offset int, requestedSize int) map[string]gxpage.Pager
+
+	// ----------------- event ----------------------
+	// AddListener adds a new ServiceInstancesChangedListener
+	// see addServiceInstancesChangedListener in Java
+	AddListener(listener *ServiceInstancesChangedListener) error
+
+	// DispatchEventByServiceName dispatches the ServiceInstancesChangedEvent to service instance whose name is serviceName
+	DispatchEventByServiceName(serviceName string) error
+
+	// DispatchEventForInstances dispatches the ServiceInstancesChangedEvent to target instances
+	DispatchEventForInstances(serviceName string, instances []ServiceInstance) error
+
+	// DispatchEvent dispatches the event
+	DispatchEvent(event *ServiceInstancesChangedEvent) error
+}
diff --git a/registry/service_instance.go b/registry/service_instance.go
new file mode 100644
index 0000000000000000000000000000000000000000..2cc229ee3b056da2d9f1a1b70d3e0f5858c9da5f
--- /dev/null
+++ b/registry/service_instance.go
@@ -0,0 +1,89 @@
+/*
+ * 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
+
+type ServiceInstance interface {
+
+	// GetId will return this instance's id. It should be unique.
+	GetId() string
+
+	// GetServiceName will return the serviceName
+	GetServiceName() string
+
+	// GetHost will return the hostname
+	GetHost() string
+
+	// GetPort will return the port.
+	GetPort() int
+
+	// IsEnable will return the enable status of this instance
+	IsEnable() bool
+
+	// IsHealthy will return the value represent the instance whether healthy or not
+	IsHealthy() bool
+
+	// GetMetadata will return the metadata
+	GetMetadata() map[string]string
+}
+
+// DefaultServiceInstance the default implementation of ServiceInstance
+// or change the ServiceInstance to be struct???
+type DefaultServiceInstance struct {
+	Id          string
+	ServiceName string
+	Host        string
+	Port        int
+	Enable      bool
+	Healthy     bool
+	Metadata    map[string]string
+}
+
+// GetId will return this instance's id. It should be unique.
+func (d *DefaultServiceInstance) GetId() string {
+	return d.Id
+}
+
+// GetServiceName will return the serviceName
+func (d *DefaultServiceInstance) GetServiceName() string {
+	return d.ServiceName
+}
+
+// GetHost will return the hostname
+func (d *DefaultServiceInstance) GetHost() string {
+	return d.Host
+}
+
+// GetPort will return the port.
+func (d *DefaultServiceInstance) GetPort() int {
+	return d.Port
+}
+
+// IsEnable will return the enable status of this instance
+func (d *DefaultServiceInstance) IsEnable() bool {
+	return d.Enable
+}
+
+// IsHealthy will return the value represent the instance whether healthy or not
+func (d *DefaultServiceInstance) IsHealthy() bool {
+	return d.Healthy
+}
+
+// GetMetadata will return the metadata
+func (d *DefaultServiceInstance) GetMetadata() map[string]string {
+	return d.Metadata
+}
diff --git a/registry/zookeeper/listener.go b/registry/zookeeper/listener.go
index bef1760e04fd6597721fd19b5d19820f45ed2bf0..c5b2f33c6107e82aa172c818c0d8aca1483248c6 100644
--- a/registry/zookeeper/listener.go
+++ b/registry/zookeeper/listener.go
@@ -35,23 +35,28 @@ import (
 	zk "github.com/apache/dubbo-go/remoting/zookeeper"
 )
 
-// RegistryDataListener ...
+// RegistryDataListener contains all URL information subscribed by zookeeper registry
 type RegistryDataListener struct {
-	interestedURL []*common.URL
-	listener      config_center.ConfigurationListener
+	subscribed map[*common.URL]config_center.ConfigurationListener
+	mutex      sync.Mutex
+	closed     bool
 }
 
-// NewRegistryDataListener ...
-func NewRegistryDataListener(listener config_center.ConfigurationListener) *RegistryDataListener {
-	return &RegistryDataListener{listener: listener}
+// NewRegistryDataListener constructs a new RegistryDataListener
+func NewRegistryDataListener() *RegistryDataListener {
+	return &RegistryDataListener{
+		subscribed: make(map[*common.URL]config_center.ConfigurationListener)}
 }
 
-// AddInterestedURL ...
-func (l *RegistryDataListener) AddInterestedURL(url *common.URL) {
-	l.interestedURL = append(l.interestedURL, url)
+// SubscribeURL is used to set a watch listener for url
+func (l *RegistryDataListener) SubscribeURL(url *common.URL, listener config_center.ConfigurationListener) {
+	if l.closed {
+		return
+	}
+	l.subscribed[url] = listener
 }
 
-// DataChange ...
+// DataChange accepts all events sent from the zookeeper server and trigger the corresponding listener for processing
 func (l *RegistryDataListener) DataChange(eventType remoting.Event) bool {
 	// Intercept the last bit
 	index := strings.Index(eventType.Path, "/providers/")
@@ -65,10 +70,14 @@ func (l *RegistryDataListener) DataChange(eventType remoting.Event) bool {
 		logger.Errorf("Listen NewURL(r{%s}) = error{%v} eventType.Path={%v}", url, err, eventType.Path)
 		return false
 	}
-
-	for _, v := range l.interestedURL {
-		if serviceURL.URLEqual(*v) {
-			l.listener.Process(
+	l.mutex.Lock()
+	defer l.mutex.Unlock()
+	if l.closed {
+		return false
+	}
+	for url, listener := range l.subscribed {
+		if serviceURL.URLEqual(*url) {
+			listener.Process(
 				&config_center.ConfigChangeEvent{
 					Key:        eventType.Path,
 					Value:      serviceURL,
@@ -81,38 +90,48 @@ func (l *RegistryDataListener) DataChange(eventType remoting.Event) bool {
 	return false
 }
 
-// RegistryConfigurationListener ...
+// Close all RegistryConfigurationListener in subscribed
+func (l *RegistryDataListener) Close() {
+	l.mutex.Lock()
+	defer l.mutex.Unlock()
+	for _, listener := range l.subscribed {
+		listener.(*RegistryConfigurationListener).Close()
+	}
+}
+
+// RegistryConfigurationListener represent the processor of zookeeper watcher
 type RegistryConfigurationListener struct {
 	client    *zk.ZookeeperClient
 	registry  *zkRegistry
 	events    chan *config_center.ConfigChangeEvent
 	isClosed  bool
+	close     chan struct{}
 	closeOnce sync.Once
 }
 
 // NewRegistryConfigurationListener for listening the event of zk.
 func NewRegistryConfigurationListener(client *zk.ZookeeperClient, reg *zkRegistry) *RegistryConfigurationListener {
 	reg.WaitGroup().Add(1)
-	return &RegistryConfigurationListener{client: client, registry: reg, events: make(chan *config_center.ConfigChangeEvent, 32), isClosed: false}
+	return &RegistryConfigurationListener{client: client, registry: reg, events: make(chan *config_center.ConfigChangeEvent, 32), isClosed: false, close: make(chan struct{}, 1)}
 }
 
-// Process ...
+// Process submit the ConfigChangeEvent to the event chan to notify all observer
 func (l *RegistryConfigurationListener) Process(configType *config_center.ConfigChangeEvent) {
 	l.events <- configType
 }
 
-// Next ...
+// Next will observe the registry state and events chan
 func (l *RegistryConfigurationListener) Next() (*registry.ServiceEvent, error) {
 	for {
 		select {
 		case <-l.client.Done():
-			logger.Warnf("listener's zk client connection is broken, so zk event listener exit now.")
-			return nil, perrors.New("listener stopped")
-
+			logger.Warnf("listener's zk client connection (address {%s}) is broken, so zk event listener exit now.", l.client.ZkAddrs)
+			return nil, perrors.New("zookeeper client stopped")
+		case <-l.close:
+			return nil, perrors.New("listener have been closed")
 		case <-l.registry.Done():
-			logger.Warnf("zk consumer register has quit, so zk event listener exit now.")
-			return nil, perrors.New("listener stopped")
-
+			logger.Warnf("zk consumer register has quit, so zk event listener exit now. (registry url {%v}", l.registry.BaseRegistry.URL)
+			return nil, perrors.New("zookeeper registry, (registry url{%v}) stopped")
 		case e := <-l.events:
 			logger.Debugf("got zk event %s", e)
 			if e.ConfigType == remoting.EventTypeDel && !l.valid() {
@@ -127,15 +146,17 @@ func (l *RegistryConfigurationListener) Next() (*registry.ServiceEvent, error) {
 	}
 }
 
-// Close ...
+// Close RegistryConfigurationListener only once
 func (l *RegistryConfigurationListener) Close() {
 	// ensure that the listener will be closed at most once.
 	l.closeOnce.Do(func() {
 		l.isClosed = true
+		l.close <- struct{}{}
 		l.registry.WaitGroup().Done()
 	})
 }
 
+// valid return the true if the client conn isn't nil
 func (l *RegistryConfigurationListener) valid() bool {
 	return l.client.ZkConnValid()
 }
diff --git a/registry/zookeeper/listener_test.go b/registry/zookeeper/listener_test.go
index 1a76b29a6f64e0329b289ce50218032a25f6f5cd..a0e9147a9e0ee8767efcf78d5e2aa536140f6a8b 100644
--- a/registry/zookeeper/listener_test.go
+++ b/registry/zookeeper/listener_test.go
@@ -32,15 +32,15 @@ import (
 )
 
 func Test_DataChange(t *testing.T) {
-	listener := NewRegistryDataListener(&MockDataListener{})
+	listener := NewRegistryDataListener()
 	url, _ := common.NewURL("jsonrpc%3A%2F%2F127.0.0.1%3A20001%2Fcom.ikurento.user.UserProvider%3Fanyhost%3Dtrue%26app.version%3D0.0.1%26application%3DBDTService%26category%3Dproviders%26cluster%3Dfailover%26dubbo%3Ddubbo-provider-golang-1.3.0%26environment%3Ddev%26group%3D%26interface%3Dcom.ikurento.user.UserProvider%26ip%3D10.32.20.124%26loadbalance%3Drandom%26methods.GetUser.loadbalance%3Drandom%26methods.GetUser.retries%3D1%26methods.GetUser.weight%3D0%26module%3Ddubbogo%2Buser-info%2Bserver%26name%3DBDTService%26organization%3Dikurento.com%26owner%3DZX%26pid%3D74500%26retries%3D0%26service.filter%3Decho%26side%3Dprovider%26timestamp%3D1560155407%26version%3D%26warmup%3D100")
-	listener.AddInterestedURL(&url)
+	listener.SubscribeURL(&url, &MockConfigurationListener{})
 	int := listener.DataChange(remoting.Event{Path: "/dubbo/com.ikurento.user.UserProvider/providers/jsonrpc%3A%2F%2F127.0.0.1%3A20001%2Fcom.ikurento.user.UserProvider%3Fanyhost%3Dtrue%26app.version%3D0.0.1%26application%3DBDTService%26category%3Dproviders%26cluster%3Dfailover%26dubbo%3Ddubbo-provider-golang-1.3.0%26environment%3Ddev%26group%3D%26interface%3Dcom.ikurento.user.UserProvider%26ip%3D10.32.20.124%26loadbalance%3Drandom%26methods.GetUser.loadbalance%3Drandom%26methods.GetUser.retries%3D1%26methods.GetUser.weight%3D0%26module%3Ddubbogo%2Buser-info%2Bserver%26name%3DBDTService%26organization%3Dikurento.com%26owner%3DZX%26pid%3D74500%26retries%3D0%26service.filter%3Decho%26side%3Dprovider%26timestamp%3D1560155407%26version%3D%26warmup%3D100"})
 	assert.Equal(t, true, int)
 }
 
-type MockDataListener struct {
+type MockConfigurationListener struct {
 }
 
-func (*MockDataListener) Process(configType *config_center.ConfigChangeEvent) {
+func (*MockConfigurationListener) Process(configType *config_center.ConfigChangeEvent) {
 }
diff --git a/registry/zookeeper/registry.go b/registry/zookeeper/registry.go
index e13443d57d7dae9fb5d50b2e1c28f618780fd850..88d5d6221b4bc7136ba4c3e7c95fb53ba35a9a58 100644
--- a/registry/zookeeper/registry.go
+++ b/registry/zookeeper/registry.go
@@ -20,7 +20,6 @@ package zookeeper
 import (
 	"fmt"
 	"net/url"
-	"strings"
 	"sync"
 	"time"
 )
@@ -54,12 +53,11 @@ func init() {
 
 type zkRegistry struct {
 	registry.BaseRegistry
-	client         *zookeeper.ZookeeperClient
-	listenerLock   sync.Mutex
-	listener       *zookeeper.ZkEventListener
-	dataListener   *RegistryDataListener
-	configListener *RegistryConfigurationListener
-	cltLock        sync.Mutex
+	client       *zookeeper.ZookeeperClient
+	listenerLock sync.Mutex
+	listener     *zookeeper.ZkEventListener
+	dataListener *RegistryDataListener
+	cltLock      sync.Mutex
 	//for provider
 	zkPath map[string]int // key = protocol://ip:port/interface
 }
@@ -78,13 +76,12 @@ func newZkRegistry(url *common.URL) (registry.Registry, error) {
 	if err != nil {
 		return nil, err
 	}
-	r.WaitGroup().Add(1) //zk client start successful, then wg +1
 
 	go zookeeper.HandleClientRestart(r)
 
 	r.listener = zookeeper.NewZkEventListener(r.client)
-	r.configListener = NewRegistryConfigurationListener(r.client, r)
-	r.dataListener = NewRegistryDataListener(r.configListener)
+
+	r.dataListener = NewRegistryDataListener()
 
 	return r, nil
 }
@@ -121,8 +118,27 @@ func newMockZkRegistry(url *common.URL, opts ...zookeeper.Option) (*zk.TestClust
 
 func (r *zkRegistry) InitListeners() {
 	r.listener = zookeeper.NewZkEventListener(r.client)
-	r.configListener = NewRegistryConfigurationListener(r.client, r)
-	r.dataListener = NewRegistryDataListener(r.configListener)
+	newDataListener := NewRegistryDataListener()
+	// should recover if dataListener isn't nil before
+	if r.dataListener != nil {
+		// close all old listener
+		oldDataListener := r.dataListener
+		oldDataListener.mutex.Lock()
+		defer oldDataListener.mutex.Unlock()
+		recoverd := r.dataListener.subscribed
+		if recoverd != nil && len(recoverd) > 0 {
+			// recover all subscribed url
+			for conf, oldListener := range recoverd {
+				if regConfigListener, ok := oldListener.(*RegistryConfigurationListener); ok {
+					regConfigListener.Close()
+				}
+				newDataListener.SubscribeURL(conf, NewRegistryConfigurationListener(r.client, r))
+				go r.listener.ListenServiceEvent(conf, fmt.Sprintf("/dubbo/%s/"+constant.DEFAULT_CATEGORY, url.QueryEscape(conf.Service())), newDataListener)
+
+			}
+		}
+	}
+	r.dataListener = newDataListener
 }
 
 func (r *zkRegistry) CreatePath(path string) error {
@@ -155,8 +171,8 @@ func (r *zkRegistry) ZkClientLock() *sync.Mutex {
 }
 
 func (r *zkRegistry) CloseListener() {
-	if r.configListener != nil {
-		r.configListener.Close()
+	if r.dataListener != nil {
+		r.dataListener.Close()
 	}
 }
 
@@ -173,32 +189,49 @@ func (r *zkRegistry) registerTempZookeeperNode(root string, node string) error {
 		logger.Errorf("zk.Create(root{%s}) = err{%v}", root, perrors.WithStack(err))
 		return perrors.WithStack(err)
 	}
+
+	// try to register the node
 	zkPath, err = r.client.RegisterTemp(root, node)
 	if err != nil {
-		if err == zk.ErrNodeExists {
-			logger.Warnf("RegisterTempNode(root{%s}, node{%s}) = error{%v}", root, node, perrors.WithStack(err))
-		} else {
-			logger.Errorf("RegisterTempNode(root{%s}, node{%s}) = error{%v}", root, node, perrors.WithStack(err))
+		logger.Errorf("Register temp node(root{%s}, node{%s}) = error{%v}", root, node, perrors.WithStack(err))
+		if perrors.Cause(err) == zk.ErrNodeExists {
+			// should delete the old node
+			logger.Info("Register temp node failed, try to delete the old and recreate  (root{%s}, node{%s}) , ignore!", root, node)
+			if err = r.client.Delete(zkPath); err == nil {
+				_, err = r.client.RegisterTemp(root, node)
+			}
+			if err != nil {
+				logger.Errorf("Recreate the temp node failed, (root{%s}, node{%s}) = error{%v}", root, node, perrors.WithStack(err))
+			}
 		}
 		return perrors.WithMessagef(err, "RegisterTempNode(root{%s}, node{%s})", root, node)
 	}
-	logger.Debugf("create a zookeeper node:%s", zkPath)
+	logger.Debugf("Create a zookeeper node:%s", zkPath)
 
 	return nil
 }
 
 func (r *zkRegistry) getListener(conf *common.URL) (*RegistryConfigurationListener, error) {
-	var (
-		zkListener *RegistryConfigurationListener
-	)
 
-	r.listenerLock.Lock()
-	if r.configListener.isClosed {
-		r.listenerLock.Unlock()
-		return nil, perrors.New("configListener already been closed")
+	var zkListener *RegistryConfigurationListener
+	dataListener := r.dataListener
+	dataListener.mutex.Lock()
+	defer dataListener.mutex.Unlock()
+	if r.dataListener.subscribed[conf] != nil {
+
+		zkListener, _ := r.dataListener.subscribed[conf].(*RegistryConfigurationListener)
+		if zkListener != nil {
+			r.listenerLock.Lock()
+			defer r.listenerLock.Unlock()
+			if zkListener.isClosed {
+				return nil, perrors.New("configListener already been closed")
+			} else {
+				return zkListener, nil
+			}
+		}
 	}
-	zkListener = r.configListener
-	r.listenerLock.Unlock()
+
+	zkListener = NewRegistryConfigurationListener(r.client, r)
 	if r.listener == nil {
 		r.cltLock.Lock()
 		client := r.client
@@ -216,10 +249,9 @@ func (r *zkRegistry) getListener(conf *common.URL) (*RegistryConfigurationListen
 	}
 
 	//Interested register to dataconfig.
-	r.dataListener.AddInterestedURL(conf)
-	for _, v := range strings.Split(conf.GetParam(constant.CATEGORY_KEY, constant.DEFAULT_CATEGORY), ",") {
-		go r.listener.ListenServiceEvent(fmt.Sprintf("/dubbo/%s/"+v, url.QueryEscape(conf.Service())), r.dataListener)
-	}
+	r.dataListener.SubscribeURL(conf, zkListener)
+
+	go r.listener.ListenServiceEvent(conf, fmt.Sprintf("/dubbo/%s/"+constant.DEFAULT_CATEGORY, url.QueryEscape(conf.Service())), r.dataListener)
 
 	return zkListener, nil
 }
diff --git a/registry/zookeeper/registry_test.go b/registry/zookeeper/registry_test.go
index 0d7623ca12a9b4e49f84ec988c796f2e913d537f..688deccfbec67771c4071f6307802a16e4e0fc8b 100644
--- a/registry/zookeeper/registry_test.go
+++ b/registry/zookeeper/registry_test.go
@@ -41,7 +41,7 @@ func Test_Register(t *testing.T) {
 	defer ts.Stop()
 	err := reg.Register(url)
 	children, _ := reg.client.GetChildren("/dubbo/com.ikurento.user.UserProvider/providers")
-	assert.Regexp(t, ".*dubbo%3A%2F%2F127.0.0.1%3A20000%2Fcom.ikurento.user.UserProvider%3Fanyhost%3Dtrue%26category%3Dproviders%26cluster%3Dmock%26dubbo%3Ddubbo-provider-golang-1.3.0%26.*.serviceid%3Dsoa.mock%26.*provider", children)
+	assert.Regexp(t, ".*dubbo%3A%2F%2F127.0.0.1%3A20000%2Fcom.ikurento.user.UserProvider%3Fanyhost%3Dtrue%26cluster%3Dmock%26.*.serviceid%3Dsoa.mock", children)
 	assert.NoError(t, err)
 }
 
diff --git a/remoting/zookeeper/client.go b/remoting/zookeeper/client.go
index 21486aab59c3f9b44c25b68d7433f864a990149a..bd1da547766abb12dc742234787262212e3db314 100644
--- a/remoting/zookeeper/client.go
+++ b/remoting/zookeeper/client.go
@@ -118,7 +118,7 @@ func ValidateZookeeperClient(container zkClientFacade, opts ...Option) error {
 	for _, opt := range opts {
 		opt(opions)
 	}
-
+	connected := false
 	err = nil
 
 	lock := container.ZkClientLock()
@@ -143,6 +143,7 @@ func ValidateZookeeperClient(container zkClientFacade, opts ...Option) error {
 			return perrors.WithMessagef(err, "newZookeeperClient(address:%+v)", url.Location)
 		}
 		container.SetZkClient(newClient)
+		connected = true
 	}
 
 	if container.ZkClient().Conn == nil {
@@ -150,10 +151,16 @@ func ValidateZookeeperClient(container zkClientFacade, opts ...Option) error {
 		container.ZkClient().Conn, event, err = zk.Connect(container.ZkClient().ZkAddrs, container.ZkClient().Timeout)
 		if err == nil {
 			container.ZkClient().Wait.Add(1)
+			connected = true
 			go container.ZkClient().HandleZkEvent(event)
 		}
 	}
 
+	if connected {
+		logger.Info("Connect to zookeeper successfully, name{%s}, zk address{%v}", opions.zkName, url.Location)
+		container.WaitGroup().Add(1) //zk client start successful, then registry wg +1
+	}
+
 	return perrors.WithMessagef(err, "newZookeeperClient(address:%+v)", url.PrimitiveURL)
 }
 
@@ -386,14 +393,23 @@ func (z *ZookeeperClient) Close() {
 	z.Conn = nil
 	z.Unlock()
 	if conn != nil {
+		logger.Warnf("zkClient Conn{name:%s, zk addr:%s} exit now.", z.name, conn.SessionID())
 		conn.Close()
 	}
 
 	logger.Warnf("zkClient{name:%s, zk addr:%s} exit now.", z.name, z.ZkAddrs)
 }
 
-// Create ...
+// Create will create the node recursively, which means that if the parent node is absent,
+// it will create parent node first.
+// And the value for the basePath is ""
 func (z *ZookeeperClient) Create(basePath string) error {
+	return z.CreateWithValue(basePath, []byte(""))
+}
+
+// CreateWithValue will create the node recursively, which means that if the parent node is absent,
+// it will create parent node first.
+func (z *ZookeeperClient) CreateWithValue(basePath string, value []byte) error {
 	var (
 		err     error
 		tmpPath string
@@ -407,7 +423,7 @@ func (z *ZookeeperClient) Create(basePath string) error {
 		conn := z.Conn
 		z.Unlock()
 		if conn != nil {
-			_, err = conn.Create(tmpPath, []byte(""), 0, zk.WorldACL(zk.PermAll))
+			_, err = conn.Create(tmpPath, value, 0, zk.WorldACL(zk.PermAll))
 		}
 
 		if err != nil {
@@ -462,7 +478,7 @@ func (z *ZookeeperClient) RegisterTemp(basePath string, node string) (string, er
 	//if err != nil && err != zk.ErrNodeExists {
 	if err != nil {
 		logger.Warnf("conn.Create(\"%s\", zk.FlagEphemeral) = error(%v)\n", zkPath, perrors.WithStack(err))
-		return "", perrors.WithStack(err)
+		return zkPath, perrors.WithStack(err)
 	}
 	logger.Debugf("zkClient{%s} create a temp zookeeper node:%s\n", z.name, tmpPath)
 
diff --git a/remoting/zookeeper/facade.go b/remoting/zookeeper/facade.go
index 055db4f716a914354d1bada653fbc0a850b615b5..4e3945388ff402f60a02150615a8914f9cba2435 100644
--- a/remoting/zookeeper/facade.go
+++ b/remoting/zookeeper/facade.go
@@ -48,11 +48,11 @@ func HandleClientRestart(r zkClientFacade) {
 		failTimes int
 	)
 
-	defer r.WaitGroup().Done()
 LOOP:
 	for {
 		select {
 		case <-r.Done():
+			r.WaitGroup().Done() // dec the wg when registry is closed
 			logger.Warnf("(ZkProviderRegistry)reconnectZkRegistry goroutine exit now...")
 			break LOOP
 			// re-register all services
@@ -63,12 +63,14 @@ LOOP:
 			zkAddress := r.ZkClient().ZkAddrs
 			r.SetZkClient(nil)
 			r.ZkClientLock().Unlock()
+			r.WaitGroup().Done() // dec the wg when zk client is closed
 
 			// Connect zk until success.
 			failTimes = 0
 			for {
 				select {
 				case <-r.Done():
+					r.WaitGroup().Done() // dec the wg when registry is closed
 					logger.Warnf("(ZkProviderRegistry)reconnectZkRegistry goroutine exit now...")
 					break LOOP
 				case <-getty.GetTimeWheel().After(timeSecondDuration(failTimes * ConnDelay)): // Prevent crazy reconnection zk.
diff --git a/remoting/zookeeper/facade_test.go b/remoting/zookeeper/facade_test.go
index a41f6cd3230700332519ce1c2d3489bfcc4b6ef0..01d46da6cc1abae90210a323d32ac84bad80249b 100644
--- a/remoting/zookeeper/facade_test.go
+++ b/remoting/zookeeper/facade_test.go
@@ -38,6 +38,16 @@ type mockFacade struct {
 	done    chan struct{}
 }
 
+func newMockFacade(client *ZookeeperClient, url *common.URL) zkClientFacade {
+	mock := &mockFacade{
+		client: client,
+		URL:    url,
+	}
+
+	mock.wg.Add(1)
+	return mock
+}
+
 func (r *mockFacade) ZkClient() *ZookeeperClient {
 	return r.client
 }
@@ -80,7 +90,7 @@ func Test_Facade(t *testing.T) {
 	assert.NoError(t, err)
 	defer ts.Stop()
 	url, _ := common.NewURL("mock://127.0.0.1")
-	mock := &mockFacade{client: z, URL: &url}
+	mock := newMockFacade(z, &url)
 	go HandleClientRestart(mock)
 	states := []zk.State{zk.StateConnecting, zk.StateConnected, zk.StateHasSession}
 	verifyEventStateOrder(t, event, states, "event channel")
diff --git a/remoting/zookeeper/listener.go b/remoting/zookeeper/listener.go
index eaf259f4417201c95172e95d7a87476575e004d5..84877667763ce870e76202844e9dc9dc1c3f008c 100644
--- a/remoting/zookeeper/listener.go
+++ b/remoting/zookeeper/listener.go
@@ -18,6 +18,7 @@
 package zookeeper
 
 import (
+	"github.com/apache/dubbo-go/common"
 	"path"
 	"strings"
 	"sync"
@@ -173,7 +174,7 @@ func (l *ZkEventListener) handleZkNodeEvent(zkPath string, children []string, li
 	}
 }
 
-func (l *ZkEventListener) listenDirEvent(zkPath string, listener remoting.DataListener) {
+func (l *ZkEventListener) listenDirEvent(conf *common.URL, zkPath string, listener remoting.DataListener) {
 	defer l.wg.Done()
 
 	var (
@@ -224,7 +225,16 @@ func (l *ZkEventListener) listenDirEvent(zkPath string, listener remoting.DataLi
 		}
 		failTimes = 0
 		for _, c := range children {
-			// listen l service node
+
+			// Only need to compare Path when subscribing to provider
+			if strings.LastIndex(zkPath, constant.PROVIDER_CATEGORY) != -1 {
+				provider, _ := common.NewURL(c)
+				if provider.Path != conf.Path {
+					continue
+				}
+			}
+
+			//listen l service node
 			dubboPath := path.Join(zkPath, c)
 
 			//Save the path to avoid listen repeatedly
@@ -232,7 +242,7 @@ func (l *ZkEventListener) listenDirEvent(zkPath string, listener remoting.DataLi
 			_, ok := l.pathMap[dubboPath]
 			l.pathMapLock.Unlock()
 			if ok {
-				logger.Warnf("@zkPath %s has already been listened.", zkPath)
+				logger.Warnf("@zkPath %s has already been listened.", dubboPath)
 				continue
 			}
 
@@ -263,7 +273,7 @@ func (l *ZkEventListener) listenDirEvent(zkPath string, listener remoting.DataLi
 				strings.LastIndex(zkPath, constant.CONSUMER_CATEGORY) == -1 {
 				l.wg.Add(1)
 				go func(zkPath string, listener remoting.DataListener) {
-					l.listenDirEvent(zkPath, listener)
+					l.listenDirEvent(conf, zkPath, listener)
 					logger.Warnf("listenDirEvent(zkPath{%s}) goroutine exit now", zkPath)
 				}(dubboPath, listener)
 			}
@@ -291,11 +301,11 @@ func timeSecondDuration(sec int) time.Duration {
 // registry.go:Listen -> listenServiceEvent -> listenDirEvent -> ListenServiceNodeEvent
 //                            |
 //                            --------> ListenServiceNodeEvent
-func (l *ZkEventListener) ListenServiceEvent(zkPath string, listener remoting.DataListener) {
+func (l *ZkEventListener) ListenServiceEvent(conf *common.URL, zkPath string, listener remoting.DataListener) {
 	logger.Infof("listen dubbo path{%s}", zkPath)
 	l.wg.Add(1)
 	go func(zkPath string, listener remoting.DataListener) {
-		l.listenDirEvent(zkPath, listener)
+		l.listenDirEvent(conf, zkPath, listener)
 		logger.Warnf("listenDirEvent(zkPath{%s}) goroutine exit now", zkPath)
 	}(zkPath, listener)
 }
diff --git a/remoting/zookeeper/listener_test.go b/remoting/zookeeper/listener_test.go
index 7301cd52c392b6950b3a49f78e8124eae532b083..ba7d6ba81b6af97dc5ad3788e8399d08cbe5b2bb 100644
--- a/remoting/zookeeper/listener_test.go
+++ b/remoting/zookeeper/listener_test.go
@@ -97,7 +97,7 @@ func TestListener(t *testing.T) {
 	go client.HandleZkEvent(event)
 	listener := NewZkEventListener(client)
 	dataListener := &mockDataListener{client: client, changedData: changedData, wait: &wait}
-	listener.ListenServiceEvent("/dubbo", dataListener)
+	listener.ListenServiceEvent(nil, "/dubbo", dataListener)
 	time.Sleep(1 * time.Second)
 	_, err := client.Conn.Set("/dubbo/dubbo.properties", []byte(changedData), 1)
 	assert.NoError(t, err)