diff --git a/README.md b/README.md
index 5c4de0559d534e03b23d136da5f5f23ff6768d55..9bade617c8b05ec52c2018cf231ae036a7ae91d3 100644
--- a/README.md
+++ b/README.md
@@ -16,7 +16,7 @@ Apache License, Version 2.0
 
 ## Release note ##
 
-[v1.4.0-rc1 - Mar 12, 2020](https://github.com/apache/dubbo-go/releases/tag/v1.4.0-rc1)
+[v1.4.0 - Mar 17, 2020](https://github.com/apache/dubbo-go/releases/tag/v1.4.0)
 
 [v1.3.0 - Mar 1, 2020](https://github.com/apache/dubbo-go/releases/tag/v1.3.0)
 
diff --git a/README_CN.md b/README_CN.md
index 5dec68fd6134fc58bf265ea382473d724332ecc6..180759f36663a587ee02232e229ae7c3ebbb06c1 100644
--- a/README_CN.md
+++ b/README_CN.md
@@ -15,7 +15,7 @@ Apache License, Version 2.0
 
 ## 发布日志 ##
 
-[v1.4.0-rc1 - 2020年3月12日](https://github.com/apache/dubbo-go/releases/tag/v1.4.0-rc1)
+[v1.4.0 - 2020年3月17日](https://github.com/apache/dubbo-go/releases/tag/v1.4.0)
 
 [v1.3.0 - 2020年3月1日](https://github.com/apache/dubbo-go/releases/tag/v1.3.0)
 
diff --git a/common/constant/key.go b/common/constant/key.go
index 8c84a2a8884d909bdd743f738f3d69dde399cc22..9af387193e2ad47f5f6adb6f7c88eec6244467b2 100644
--- a/common/constant/key.go
+++ b/common/constant/key.go
@@ -131,6 +131,7 @@ const (
 	ProviderConfigPrefix       = "dubbo.provider."
 	ConsumerConfigPrefix       = "dubbo.consumer."
 	ShutdownConfigPrefix       = "dubbo.shutdown."
+	MetadataReportPrefix       = "dubbo.metadata-report."
 	RouterConfigPrefix         = "dubbo.router."
 )
 
@@ -179,6 +180,9 @@ const (
 	// ForceUseTag is the tag in attachment
 	ForceUseTag = "dubbo.force.tag"
 	Tagkey      = "dubbo.tag"
+
+	// Attachment key in context in invoker
+	AttachmentKey = "attachment"
 )
 
 const (
@@ -209,9 +213,23 @@ const (
 	// consumer
 	CONSUMER = "consumer"
 	// key of access key id
-	ACCESS_KEY_ID_KEY = "accessKeyId"
+	ACCESS_KEY_ID_KEY = ".accessKeyId"
 	// key of secret access key
-	SECRET_ACCESS_KEY_KEY = "secretAccessKey"
+	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
@@ -235,3 +253,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/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/proxy/proxy.go b/common/proxy/proxy.go
index 68ba3ff7882837a9419c5e47228461af11fd79ba..a77d26d1103e605f5d0b38ac14c8bb3e20fc27b8 100644
--- a/common/proxy/proxy.go
+++ b/common/proxy/proxy.go
@@ -25,6 +25,7 @@ import (
 
 import (
 	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/common/constant"
 	"github.com/apache/dubbo-go/common/logger"
 	"github.com/apache/dubbo-go/protocol"
 	invocation_impl "github.com/apache/dubbo-go/protocol/invocation"
@@ -44,7 +45,7 @@ var (
 	typError = reflect.Zero(reflect.TypeOf((*error)(nil)).Elem()).Type()
 )
 
-// NewProxy ...
+// NewProxy create service proxy.
 func NewProxy(invoke protocol.Invoker, callBack interface{}, attachments map[string]string) *Proxy {
 	return &Proxy{
 		invoke:      invoke,
@@ -59,7 +60,6 @@ func NewProxy(invoke protocol.Invoker, callBack interface{}, attachments map[str
 // 		type XxxProvider struct {
 //  		Yyy func(ctx context.Context, args []interface{}, rsp *Zzz) error
 // 		}
-
 func (p *Proxy) Implement(v common.RPCService) {
 
 	// check parameters, incoming interface must be a elem's pointer.
@@ -141,7 +141,7 @@ func (p *Proxy) Implement(v common.RPCService) {
 			}
 
 			// add user setAttachment
-			atm := invCtx.Value("attachment")
+			atm := invCtx.Value(constant.AttachmentKey)
 			if m, ok := atm.(map[string]string); ok {
 				for k, value := range m {
 					inv.SetAttachments(k, value)
@@ -149,6 +149,9 @@ func (p *Proxy) Implement(v common.RPCService) {
 			}
 
 			result := p.invoke.Invoke(invCtx, inv)
+			if len(result.Attachments()) > 0 {
+				invCtx = context.WithValue(invCtx, constant.AttachmentKey, result.Attachments())
+			}
 
 			err = result.Error()
 			logger.Debugf("[makeDubboCallProxy] result: %v, err: %v", result.Result(), err)
@@ -202,12 +205,12 @@ func (p *Proxy) Implement(v common.RPCService) {
 
 }
 
-// Get ...
+// Get get rpc service instance.
 func (p *Proxy) Get() common.RPCService {
 	return p.rpc
 }
 
-// GetCallback ...
+// GetCallback get callback.
 func (p *Proxy) GetCallback() interface{} {
 	return p.callBack
 }
diff --git a/common/proxy/proxy_factory.go b/common/proxy/proxy_factory.go
index 7b249a3e9754b097130a80bf3819d282dad6b6e8..34fa3fd07eacae95351f302158d7da68165ea5cf 100644
--- a/common/proxy/proxy_factory.go
+++ b/common/proxy/proxy_factory.go
@@ -22,7 +22,7 @@ import (
 	"github.com/apache/dubbo-go/protocol"
 )
 
-// ProxyFactory ...
+// ProxyFactory interface.
 type ProxyFactory interface {
 	GetProxy(invoker protocol.Invoker, url *common.URL) *Proxy
 	GetAsyncProxy(invoker protocol.Invoker, callBack interface{}, url *common.URL) *Proxy
diff --git a/common/proxy/proxy_factory/default.go b/common/proxy/proxy_factory/default.go
index 114cfee2363022da5f7957a825a16fc42b8c928f..013b39911054ef5bd89d38cd50116a204b117872 100644
--- a/common/proxy/proxy_factory/default.go
+++ b/common/proxy/proxy_factory/default.go
@@ -113,6 +113,7 @@ func (pi *ProxyInvoker) Invoke(ctx context.Context, invocation protocol.Invocati
 
 	in := []reflect.Value{svc.Rcvr()}
 	if method.CtxType() != nil {
+		ctx = context.WithValue(ctx, constant.AttachmentKey, invocation.Attachments())
 		in = append(in, method.SuiteContext(ctx))
 	}
 
diff --git a/common/rpc_service.go b/common/rpc_service.go
index b138b4f300387a512932ee8869db493c24f4cb27..d7d900718e911fe12915225c04338d52e5f084f5 100644
--- a/common/rpc_service.go
+++ b/common/rpc_service.go
@@ -59,7 +59,6 @@ type AsyncCallback func(response CallbackResponse)
 //     return map[string][string]{}
 // }
 const (
-	// METHOD_MAPPER ...
 	METHOD_MAPPER = "MethodMapper"
 )
 
@@ -68,7 +67,7 @@ var (
 	// because Typeof takes an empty interface value. This is annoying.
 	typeOfError = reflect.TypeOf((*error)(nil)).Elem()
 
-	// ServiceMap ...
+	// ServiceMap store description of service.
 	// todo: lowerecas?
 	ServiceMap = &serviceMap{
 		serviceMap:   make(map[string]map[string]*Service),
@@ -80,7 +79,7 @@ var (
 // info of method
 //////////////////////////
 
-// MethodType ...
+// MethodType is description of service method.
 type MethodType struct {
 	method    reflect.Method
 	ctxType   reflect.Type   // request context
@@ -88,27 +87,27 @@ type MethodType struct {
 	replyType reflect.Type   // return value, otherwise it is nil
 }
 
-// Method ...
+// Method get @m.method.
 func (m *MethodType) Method() reflect.Method {
 	return m.method
 }
 
-// CtxType ...
+// CtxType get @m.ctxType.
 func (m *MethodType) CtxType() reflect.Type {
 	return m.ctxType
 }
 
-// ArgsType ...
+// ArgsType get @m.argsType.
 func (m *MethodType) ArgsType() []reflect.Type {
 	return m.argsType
 }
 
-// ReplyType ...
+// ReplyType get @m.replyType.
 func (m *MethodType) ReplyType() reflect.Type {
 	return m.replyType
 }
 
-// SuiteContext ...
+// SuiteContext tranfer @ctx to reflect.Value type or get it from @m.ctxType.
 func (m *MethodType) SuiteContext(ctx context.Context) reflect.Value {
 	if contextv := reflect.ValueOf(ctx); contextv.IsValid() {
 		return contextv
@@ -120,7 +119,7 @@ func (m *MethodType) SuiteContext(ctx context.Context) reflect.Value {
 // info of service interface
 //////////////////////////
 
-// Service ...
+// Service is description of service
 type Service struct {
 	name     string
 	rcvr     reflect.Value
@@ -128,17 +127,17 @@ type Service struct {
 	methods  map[string]*MethodType
 }
 
-// Method ...
+// Method get @s.methods.
 func (s *Service) Method() map[string]*MethodType {
 	return s.methods
 }
 
-// RcvrType ...
+// RcvrType get @s.rcvrType.
 func (s *Service) RcvrType() reflect.Type {
 	return s.rcvrType
 }
 
-// Rcvr ...
+// Rcvr get @s.rcvr.
 func (s *Service) Rcvr() reflect.Value {
 	return s.rcvr
 }
@@ -274,7 +273,9 @@ func (sm *serviceMap) UnRegister(interfaceName, protocol, serviceId string) erro
 		}
 	}
 	delete(svcs, serviceId)
-	delete(sm.serviceMap, protocol)
+	if len(sm.serviceMap) == 0 {
+		delete(sm.serviceMap, protocol)
+	}
 
 	return nil
 }
diff --git a/common/url.go b/common/url.go
index ebb648db27c3efff534f0d0a545f2211f335aa89..a70ac7dc9dd6f415aa458689864604a793c8e256 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
@@ -249,7 +248,7 @@ func NewURL(urlString string, opts ...option) (URL, error) {
 	return s, nil
 }
 
-// URLEqual ...
+// URLEqual judge @url and @c is equal or not.
 func (c URL) URLEqual(url URL) bool {
 	c.Ip = ""
 	c.Port = ""
@@ -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,10 +314,9 @@ 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 ...
+// ServiceKey get a unique key of a service.
 func (c URL) ServiceKey() string {
 	intf := c.GetParam(constant.INTERFACE_KEY, strings.TrimPrefix(c.Path, "/"))
 	if intf == "" {
@@ -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/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 61cb49457b7a2435cb22f02a9df0a02a71ae68cb..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! err: %#v", key, err))
-			}
+		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/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 dcdba958058edb7c1f59f49f20a4cb738ea102f8..3710cbc4bc62a01a014e91bcb978742c4a93c5cb 100644
--- a/config/reference_config.go
+++ b/config/reference_config.go
@@ -188,6 +188,7 @@ 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())
 
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/service_config.go b/config/service_config.go
index 50bf5e12c3247340f177a84c72446383ec5c3450..45e7df6306fc016f014497868eb45ec3be768a11 100644
--- a/config/service_config.go
+++ b/config/service_config.go
@@ -18,6 +18,7 @@
 package config
 
 import (
+	"container/list"
 	"context"
 	"fmt"
 	"net/url"
@@ -29,6 +30,7 @@ import (
 
 import (
 	"github.com/creasty/defaults"
+	gxnet "github.com/dubbogo/gost/net"
 	perrors "github.com/pkg/errors"
 	"go.uber.org/atomic"
 )
@@ -105,6 +107,24 @@ func NewServiceConfig(id string, context context.Context) *ServiceConfig {
 	}
 }
 
+// Get Random Port
+func getRandomPort(protocolConfigs []*ProtocolConfig) *list.List {
+	ports := list.New()
+	for _, proto := range protocolConfigs {
+		if len(proto.Port) > 0 {
+			continue
+		}
+
+		tcp, err := gxnet.ListenOnTCPRandomPort(proto.Ip)
+		if err != nil {
+			panic(perrors.New(fmt.Sprintf("Get tcp port error,err is {%v}", err)))
+		}
+		defer tcp.Close()
+		ports.PushBack(strings.Split(tcp.Addr().String(), ":")[1])
+	}
+	return ports
+}
+
 // Export ...
 func (c *ServiceConfig) Export() error {
 	// TODO: config center start here
@@ -127,6 +147,9 @@ func (c *ServiceConfig) Export() error {
 		logger.Warnf("The service %v's '%v' protocols don't has right protocolConfigs ", c.InterfaceName, c.Protocol)
 		return nil
 	}
+
+	ports := getRandomPort(protocolConfigs)
+	nextPort := ports.Front()
 	for _, proto := range protocolConfigs {
 		// registry the service reflect
 		methods, err := common.ServiceMap.Register(c.InterfaceName, proto.Name, c.rpcService)
@@ -135,11 +158,18 @@ func (c *ServiceConfig) Export() error {
 			logger.Errorf(err.Error())
 			return err
 		}
+
+		port := proto.Port
+
+		if len(proto.Port) == 0 {
+			port = nextPort.Value.(string)
+			nextPort = nextPort.Next()
+		}
 		ivkURL := common.NewURLWithOptions(
 			common.WithPath(c.id),
 			common.WithProtocol(proto.Name),
 			common.WithIp(proto.Ip),
-			common.WithPort(proto.Port),
+			common.WithPort(port),
 			common.WithParams(urlMap),
 			common.WithParamsValue(constant.BEAN_NAME_KEY, c.id),
 			common.WithMethods(strings.Split(methods, ",")),
diff --git a/config/service_config_test.go b/config/service_config_test.go
index 6f3230890348e77ea26c9c0eaf9165090c8cd09f..e39d32b7f7976cbdc668153bcd901e40dd635556 100644
--- a/config/service_config_test.go
+++ b/config/service_config_test.go
@@ -21,6 +21,11 @@ import (
 	"testing"
 )
 
+import (
+	gxnet "github.com/dubbogo/gost/net"
+	"github.com/stretchr/testify/assert"
+)
+
 import (
 	"github.com/apache/dubbo-go/common/extension"
 )
@@ -189,3 +194,35 @@ func Test_Export(t *testing.T) {
 	}
 	providerConfig = nil
 }
+
+func Test_getRandomPort(t *testing.T) {
+	protocolConfigs := make([]*ProtocolConfig, 0, 3)
+
+	ip, err := gxnet.GetLocalIP()
+	protocolConfigs = append(protocolConfigs, &ProtocolConfig{
+		Ip: ip,
+	})
+	protocolConfigs = append(protocolConfigs, &ProtocolConfig{
+		Ip: ip,
+	})
+	protocolConfigs = append(protocolConfigs, &ProtocolConfig{
+		Ip: ip,
+	})
+	assert.NoError(t, err)
+	ports := getRandomPort(protocolConfigs)
+
+	assert.Equal(t, ports.Len(), len(protocolConfigs))
+
+	front := ports.Front()
+	for {
+		if front == nil {
+			break
+		}
+		t.Logf("port:%v", front.Value)
+		front = front.Next()
+	}
+
+	protocolConfigs = make([]*ProtocolConfig, 0, 3)
+	ports = getRandomPort(protocolConfigs)
+	assert.Equal(t, ports.Len(), len(protocolConfigs))
+}
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/filter/filter_impl/access_log_filter.go b/filter/filter_impl/access_log_filter.go
index fbfe7565170c7df468f755a4bd1aadde166a79c1..49cdc2287c28ae0cbbd0fcab3700536595bb0f5e 100644
--- a/filter/filter_impl/access_log_filter.go
+++ b/filter/filter_impl/access_log_filter.go
@@ -71,14 +71,18 @@ func init() {
  *
  * the value of "accesslog" can be "true" or "default" too.
  * If the value is one of them, the access log will be record in log file which defined in log.yml
+ * AccessLogFilter is designed to be singleton
  */
 type AccessLogFilter struct {
 	logChan chan AccessLogData
 }
 
-// Invoke ...
+// Invoke will check whether user wants to use this filter.
+// If we find the value of key constant.ACCESS_LOG_KEY, we will log the invocation info
 func (ef *AccessLogFilter) Invoke(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
 	accessLog := invoker.GetUrl().GetParam(constant.ACCESS_LOG_KEY, "")
+
+	// the user do not
 	if len(accessLog) > 0 {
 		accessLogData := AccessLogData{data: ef.buildAccessLogData(invoker, invocation), accessLog: accessLog}
 		ef.logIntoChannel(accessLogData)
@@ -86,7 +90,7 @@ func (ef *AccessLogFilter) Invoke(ctx context.Context, invoker protocol.Invoker,
 	return invoker.Invoke(ctx, invocation)
 }
 
-// it won't block the invocation
+// logIntoChannel won't block the invocation
 func (ef *AccessLogFilter) logIntoChannel(accessLogData AccessLogData) {
 	select {
 	case ef.logChan <- accessLogData:
@@ -97,6 +101,7 @@ func (ef *AccessLogFilter) logIntoChannel(accessLogData AccessLogData) {
 	}
 }
 
+// buildAccessLogData builds the access log data
 func (ef *AccessLogFilter) buildAccessLogData(_ protocol.Invoker, invocation protocol.Invocation) map[string]string {
 	dataMap := make(map[string]string, 16)
 	attachments := invocation.Attachments()
@@ -130,11 +135,12 @@ func (ef *AccessLogFilter) buildAccessLogData(_ protocol.Invoker, invocation pro
 	return dataMap
 }
 
-// OnResponse ...
+// OnResponse do nothing
 func (ef *AccessLogFilter) OnResponse(_ context.Context, result protocol.Result, _ protocol.Invoker, _ protocol.Invocation) protocol.Result {
 	return result
 }
 
+// writeLogToFile actually write the logs into file
 func (ef *AccessLogFilter) writeLogToFile(data AccessLogData) {
 	accessLog := data.accessLog
 	if isDefault(accessLog) {
@@ -156,6 +162,12 @@ func (ef *AccessLogFilter) writeLogToFile(data AccessLogData) {
 	}
 }
 
+// openLogFile will open the log file with append mode.
+// If the file is not found, it will create the file.
+// Actually, the accessLog is the filename
+// You may find out that, once we want to write access log into log file,
+// we open the file again and again.
+// It needs to be optimized.
 func (ef *AccessLogFilter) openLogFile(accessLog string) (*os.File, error) {
 	logFile, err := os.OpenFile(accessLog, os.O_CREATE|os.O_APPEND|os.O_RDWR, LogFileMode)
 	if err != nil {
@@ -169,6 +181,12 @@ func (ef *AccessLogFilter) openLogFile(accessLog string) (*os.File, error) {
 		return nil, err
 	}
 	last := fileInfo.ModTime().Format(FileDateFormat)
+
+	// this is confused.
+	// for example, if the last = '2020-03-04'
+	// and today is '2020-03-05'
+	// we will create one new file to log access data
+	// By this way, we can split the access log based on days.
 	if now != last {
 		err = os.Rename(fileInfo.Name(), fileInfo.Name()+"."+now)
 		if err != nil {
@@ -180,11 +198,12 @@ func (ef *AccessLogFilter) openLogFile(accessLog string) (*os.File, error) {
 	return logFile, err
 }
 
+// isDefault check whether accessLog == true or accessLog == default
 func isDefault(accessLog string) bool {
 	return strings.EqualFold("true", accessLog) || strings.EqualFold("default", accessLog)
 }
 
-// GetAccessLogFilter ...
+// GetAccessLogFilter return the instance of AccessLogFilter
 func GetAccessLogFilter() filter.Filter {
 	accessLogFilter := &AccessLogFilter{logChan: make(chan AccessLogData, LogMaxBuffer)}
 	go func() {
@@ -195,12 +214,13 @@ func GetAccessLogFilter() filter.Filter {
 	return accessLogFilter
 }
 
-// AccessLogData ...
+// AccessLogData defines the data that will be log into file
 type AccessLogData struct {
 	accessLog string
 	data      map[string]string
 }
 
+// toLogMessage convert the AccessLogData to String
 func (ef *AccessLogData) toLogMessage() string {
 	builder := strings.Builder{}
 	builder.WriteString("[")
diff --git a/filter/filter_impl/tps/tps_limiter_method_service.go b/filter/filter_impl/tps/tps_limiter_method_service.go
index 7fe8de9237b82415a09083c2be59df5e232ecaf0..2d44c688ebd460f60a49da0f148fd7c6e2b79f50 100644
--- a/filter/filter_impl/tps/tps_limiter_method_service.go
+++ b/filter/filter_impl/tps/tps_limiter_method_service.go
@@ -115,7 +115,12 @@ type MethodServiceTpsLimiterImpl struct {
 	tpsState *concurrent.Map
 }
 
-// IsAllowable ...
+// IsAllowable based on method-level and service-level.
+// The method-level has high priority which means that if there is any rate limit configuration for the method,
+// the service-level rate limit strategy will be ignored.
+// The key point is how to keep thread-safe
+// This implementation use concurrent map + loadOrStore to make implementation thread-safe
+// You can image that even multiple threads create limiter, but only one could store the limiter into tpsState
 func (limiter MethodServiceTpsLimiterImpl) IsAllowable(url common.URL, invocation protocol.Invocation) bool {
 
 	methodConfigPrefix := "methods." + invocation.MethodName() + "."
@@ -123,23 +128,30 @@ func (limiter MethodServiceTpsLimiterImpl) IsAllowable(url common.URL, invocatio
 	methodLimitRateConfig := url.GetParam(methodConfigPrefix+constant.TPS_LIMIT_RATE_KEY, "")
 	methodIntervalConfig := url.GetParam(methodConfigPrefix+constant.TPS_LIMIT_INTERVAL_KEY, "")
 
+	// service-level tps limit
 	limitTarget := url.ServiceKey()
 
 	// method-level tps limit
 	if len(methodIntervalConfig) > 0 || len(methodLimitRateConfig) > 0 {
+		// it means that if the method-level rate limit exist, we will use method-level rate limit strategy
 		limitTarget = limitTarget + "#" + invocation.MethodName()
 	}
 
+	// looking up the limiter from 'cache'
 	limitState, found := limiter.tpsState.Load(limitTarget)
 	if found {
+		// the limiter has been cached, we return its result
 		return limitState.(filter.TpsLimitStrategy).IsAllowable()
 	}
 
+	// we could not find the limiter, and try to create one.
+
 	limitRate := getLimitConfig(methodLimitRateConfig, url, invocation,
 		constant.TPS_LIMIT_RATE_KEY,
 		constant.DEFAULT_TPS_LIMIT_RATE)
 
 	if limitRate < 0 {
+		// the limitTarget is not necessary to be limited.
 		return true
 	}
 
@@ -150,13 +162,20 @@ func (limiter MethodServiceTpsLimiterImpl) IsAllowable(url common.URL, invocatio
 		panic(fmt.Sprintf("The interval must be positive, please check your configuration! url: %s", url.String()))
 	}
 
+	// find the strategy config and then create one
 	limitStrategyConfig := url.GetParam(methodConfigPrefix+constant.TPS_LIMIT_STRATEGY_KEY,
 		url.GetParam(constant.TPS_LIMIT_STRATEGY_KEY, constant.DEFAULT_KEY))
 	limitStateCreator := extension.GetTpsLimitStrategyCreator(limitStrategyConfig)
+
+	// we using loadOrStore to ensure thread-safe
 	limitState, _ = limiter.tpsState.LoadOrStore(limitTarget, limitStateCreator.Create(int(limitRate), int(limitInterval)))
+
 	return limitState.(filter.TpsLimitStrategy).IsAllowable()
 }
 
+// getLimitConfig will try to fetch the configuration from url.
+// If we can convert the methodLevelConfig to int64, return;
+// Or, we will try to look up server-level configuration and then convert it to int64
 func getLimitConfig(methodLevelConfig string,
 	url common.URL,
 	invocation protocol.Invocation,
@@ -172,6 +191,8 @@ func getLimitConfig(methodLevelConfig string,
 		return result
 	}
 
+	// actually there is no method-level configuration, so we use the service-level configuration
+
 	result, err := strconv.ParseInt(url.GetParam(configKey, defaultVal), 0, 0)
 
 	if err != nil {
@@ -183,7 +204,7 @@ func getLimitConfig(methodLevelConfig string,
 var methodServiceTpsLimiterInstance *MethodServiceTpsLimiterImpl
 var methodServiceTpsLimiterOnce sync.Once
 
-// GetMethodServiceTpsLimiter ...
+// GetMethodServiceTpsLimiter will return an MethodServiceTpsLimiterImpl instance.
 func GetMethodServiceTpsLimiter() filter.TpsLimiter {
 	methodServiceTpsLimiterOnce.Do(func() {
 		methodServiceTpsLimiterInstance = &MethodServiceTpsLimiterImpl{
diff --git a/filter/handler/rejected_execution_handler_only_log.go b/filter/handler/rejected_execution_handler_only_log.go
index 0f9003c7df2165a2f3a364a5afc47f578db1d243..fe9cf4869f16e1d7c136e7f48e4138d046fcb057 100644
--- a/filter/handler/rejected_execution_handler_only_log.go
+++ b/filter/handler/rejected_execution_handler_only_log.go
@@ -36,6 +36,7 @@ const (
 )
 
 func init() {
+	// this implementation is the the default implementation of RejectedExecutionHandler
 	extension.SetRejectedExecutionHandler(HandlerName, GetOnlyLogRejectedExecutionHandler)
 	extension.SetRejectedExecutionHandler(constant.DEFAULT_KEY, GetOnlyLogRejectedExecutionHandler)
 }
@@ -56,11 +57,12 @@ var onlyLogHandlerOnce sync.Once
  *   tps.limit.rejected.handler: "default" or "log"
  *   methods:
  *    - name: "GetUser"
+ * OnlyLogRejectedExecutionHandler is designed to be singleton
  */
 type OnlyLogRejectedExecutionHandler struct {
 }
 
-// RejectedExecution ...
+// RejectedExecution will do nothing, it only log the invocation.
 func (handler *OnlyLogRejectedExecutionHandler) RejectedExecution(url common.URL,
 	_ protocol.Invocation) protocol.Result {
 
@@ -68,7 +70,7 @@ func (handler *OnlyLogRejectedExecutionHandler) RejectedExecution(url common.URL
 	return &protocol.RPCResult{}
 }
 
-// GetOnlyLogRejectedExecutionHandler ...
+// GetOnlyLogRejectedExecutionHandler will return the instance of OnlyLogRejectedExecutionHandler
 func GetOnlyLogRejectedExecutionHandler() filter.RejectedExecutionHandler {
 	onlyLogHandlerOnce.Do(func() {
 		onlyLogHandlerInstance = &OnlyLogRejectedExecutionHandler{}
diff --git a/filter/rejected_execution_handler.go b/filter/rejected_execution_handler.go
index caeea1db6631d0968fd58f59f9577ee9272f3ca0..d02481b98d2937ac58d277becdb1240b8a4e9b0f 100644
--- a/filter/rejected_execution_handler.go
+++ b/filter/rejected_execution_handler.go
@@ -31,5 +31,7 @@ import (
  * In such situation, implement this interface and register it by invoking extension.SetRejectedExecutionHandler.
  */
 type RejectedExecutionHandler interface {
+
+	// RejectedExecution will be called if the invocation was rejected by some component.
 	RejectedExecution(url common.URL, invocation protocol.Invocation) protocol.Result
 }
diff --git a/filter/tps_limit_strategy.go b/filter/tps_limit_strategy.go
index 5edf32ce1912642c7ad0ea0b3f6144b45c267eb4..e194f1da06b0599464f0c66f6b7747fc78fbedce 100644
--- a/filter/tps_limit_strategy.go
+++ b/filter/tps_limit_strategy.go
@@ -33,10 +33,16 @@ package filter
  *      tps.limit.strategy: "name of implementation" # method-level
  */
 type TpsLimitStrategy interface {
+	// IsAllowable will return true if this invocation is not over limitation
 	IsAllowable() bool
 }
 
-// TpsLimitStrategyCreator ...
+// TpsLimitStrategyCreator, the creator abstraction for TpsLimitStrategy
 type TpsLimitStrategyCreator interface {
-	Create(rate int, interval int) TpsLimitStrategy
+	// Create will create an instance of TpsLimitStrategy
+	// It will be a little hard to understand this method.
+	// The unit of interval is ms
+	// for example, if the limit = 100, interval = 1000
+	// which means that the tps limitation is 100 times per 1000ms (100/1000ms)
+	Create(limit int, interval int) TpsLimitStrategy
 }
diff --git a/filter/tps_limiter.go b/filter/tps_limiter.go
index dbc9f76838a4406b4788e7757453098613253d58..531eb098232cd34a467d71882b29858c07f88aef 100644
--- a/filter/tps_limiter.go
+++ b/filter/tps_limiter.go
@@ -34,5 +34,6 @@ import (
  *   tps.limiter: "the name of limiter",
  */
 type TpsLimiter interface {
+	// IsAllowable will check whether this invocation should be enabled for further process
 	IsAllowable(common.URL, protocol.Invocation) bool
 }
diff --git a/go.mod b/go.mod
index 67ed1eb402c34eecc5214411531b0a4ab68fda0c..b238173cb904ec394d9dabb21c41f81e8b745ade 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.5
 	github.com/dubbogo/go-zookeeper v1.0.0
-	github.com/dubbogo/gost v1.8.0
+	github.com/dubbogo/gost v1.9.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 bbbaf34640b63d6b7c423d8c7f9c4419025842cf..62e51170c79815d6ba8498a60ec1a5af797819e4 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,15 +106,13 @@ 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.5 h1:xJxdDj9jm7wlrRSsVZSk2TDNxJbbac5GpxV0QpjO+Tw=
+github.com/dubbogo/getty v1.3.5/go.mod h1:T55vN8Q6tZjf2AQZiGmkujneD3LfqYbv2b3QjacwYOY=
 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/dubbogo/gost v1.9.0 h1:UT+dWwvLyJiDotxJERO75jB3Yxgsdy10KztR5ycxRAk=
+github.com/dubbogo/gost v1.9.0/go.mod h1:pPTjVyoJan3aPxBPNUX0ADkXjPibLo+/Ib0/fADXSG8=
 github.com/duosecurity/duo_api_golang v0.0.0-20190308151101-6c680f768e74 h1:2MIhn2R6oXQbgW5yHfS+d6YqyMfXiu2L55rFZC4UD/M=
 github.com/duosecurity/duo_api_golang v0.0.0-20190308151101-6c680f768e74/go.mod h1:UqXY1lYT/ERa4OEAywUqdok1T4RCRdArkhic1Opuavo=
 github.com/elazarl/go-bindata-assetfs v0.0.0-20160803192304-e1a2a7ec64b0 h1:ZoRgc53qJCfSLimXqJDrmBhnt5GChDsExMCK7t48o0Y=
@@ -324,6 +322,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=
@@ -383,8 +387,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.go b/protocol/dubbo/client.go
index 5ec7db51af0ddfa6e49d3c65910355f0bf2de414..e6ffa64d80a327517bcd5b6fb9ff2efdc9aed337 100644
--- a/protocol/dubbo/client.go
+++ b/protocol/dubbo/client.go
@@ -88,7 +88,7 @@ func init() {
 	rand.Seed(time.Now().UnixNano())
 }
 
-// SetClientConf ...
+// SetClientConf set dubbo client config.
 func SetClientConf(c ClientConfig) {
 	clientConf = &c
 	err := clientConf.CheckValidity()
@@ -99,7 +99,7 @@ func SetClientConf(c ClientConfig) {
 	setClientGrpool()
 }
 
-// GetClientConf ...
+// GetClientConf get dubbo client config.
 func GetClientConf() ClientConfig {
 	return *clientConf
 }
@@ -129,7 +129,7 @@ type AsyncCallbackResponse struct {
 	Reply     interface{}
 }
 
-// Client ...
+// Client is dubbo protocol client.
 type Client struct {
 	opts     Options
 	conf     ClientConfig
@@ -139,7 +139,7 @@ type Client struct {
 	pendingResponses *sync.Map
 }
 
-// NewClient ...
+// NewClient create a new Client.
 func NewClient(opt Options) *Client {
 
 	switch {
@@ -167,7 +167,7 @@ func NewClient(opt Options) *Client {
 	return c
 }
 
-// Request ...
+// Request is dubbo protocol request.
 type Request struct {
 	addr   string
 	svcUrl common.URL
@@ -176,7 +176,7 @@ type Request struct {
 	atta   map[string]string
 }
 
-// NewRequest ...
+// NewRequest create a new Request.
 func NewRequest(addr string, svcUrl common.URL, method string, args interface{}, atta map[string]string) *Request {
 	return &Request{
 		addr:   addr,
@@ -187,13 +187,13 @@ func NewRequest(addr string, svcUrl common.URL, method string, args interface{},
 	}
 }
 
-// Response ...
+// Response is dubbo protocol response.
 type Response struct {
 	reply interface{}
 	atta  map[string]string
 }
 
-// NewResponse ...
+// NewResponse  create a new Response.
 func NewResponse(reply interface{}, atta map[string]string) *Response {
 	return &Response{
 		reply: reply,
@@ -201,15 +201,14 @@ func NewResponse(reply interface{}, atta map[string]string) *Response {
 	}
 }
 
-// CallOneway call one way
+// CallOneway call by one way
 func (c *Client) CallOneway(request *Request) error {
 
 	return perrors.WithStack(c.call(CT_OneWay, request, NewResponse(nil, nil), nil))
 }
 
-// Call if @response is nil, the transport layer will get the response without notify the invoker.
+// Call call remoting by two way or one way, if @response.reply is nil, the way of call is one way.
 func (c *Client) Call(request *Request, response *Response) error {
-
 	ct := CT_TwoWay
 	if response.reply == nil {
 		ct = CT_OneWay
@@ -218,14 +217,12 @@ func (c *Client) Call(request *Request, response *Response) error {
 	return perrors.WithStack(c.call(ct, request, response, nil))
 }
 
-// AsyncCall ...
+// AsyncCall call remoting by async with callback.
 func (c *Client) AsyncCall(request *Request, callback common.AsyncCallback, response *Response) error {
-
 	return perrors.WithStack(c.call(CT_TwoWay, request, response, callback))
 }
 
 func (c *Client) call(ct CallType, request *Request, response *Response, callback common.AsyncCallback) error {
-
 	p := &DubboPackage{}
 	p.Service.Path = strings.TrimPrefix(request.svcUrl.Path, "/")
 	p.Service.Interface = request.svcUrl.GetParam(constant.INTERFACE_KEY, "")
@@ -293,7 +290,7 @@ func (c *Client) call(ct CallType, request *Request, response *Response, callbac
 	return perrors.WithStack(err)
 }
 
-// Close ...
+// Close close the client pool.
 func (c *Client) Close() {
 	if c.pool != nil {
 		c.pool.close()
diff --git a/protocol/dubbo/codec.go b/protocol/dubbo/codec.go
index 76416b2baf1e1db516c00d92ecb8ad618bf186bd..620a57d4a7f09da33f752b4890692d6102eb95df 100644
--- a/protocol/dubbo/codec.go
+++ b/protocol/dubbo/codec.go
@@ -69,7 +69,7 @@ func (p DubboPackage) String() string {
 	return fmt.Sprintf("DubboPackage: Header-%v, Path-%v, Body-%v", p.Header, p.Service, p.Body)
 }
 
-// Marshal ...
+// Marshal encode hessian package.
 func (p *DubboPackage) Marshal() (*bytes.Buffer, error) {
 	codec := hessian.NewHessianCodec(nil)
 
@@ -81,7 +81,7 @@ func (p *DubboPackage) Marshal() (*bytes.Buffer, error) {
 	return bytes.NewBuffer(pkg), nil
 }
 
-// Unmarshal ...
+// Unmarshal dncode hessian package.
 func (p *DubboPackage) Unmarshal(buf *bytes.Buffer, opts ...interface{}) error {
 	// fix issue https://github.com/apache/dubbo-go/issues/380
 	bufLen := buf.Len()
@@ -125,7 +125,7 @@ func (p *DubboPackage) Unmarshal(buf *bytes.Buffer, opts ...interface{}) error {
 // PendingResponse
 ////////////////////////////////////////////
 
-// PendingResponse ...
+// PendingResponse is a pending response.
 type PendingResponse struct {
 	seq       uint64
 	err       error
@@ -136,7 +136,7 @@ type PendingResponse struct {
 	done      chan struct{}
 }
 
-// NewPendingResponse ...
+// NewPendingResponse create a PendingResponses.
 func NewPendingResponse() *PendingResponse {
 	return &PendingResponse{
 		start:    time.Now(),
@@ -145,7 +145,7 @@ func NewPendingResponse() *PendingResponse {
 	}
 }
 
-// GetCallResponse ...
+// GetCallResponse get AsyncCallbackResponse.
 func (r PendingResponse) GetCallResponse() common.CallbackResponse {
 	return AsyncCallbackResponse{
 		Cause:     r.err,
diff --git a/protocol/dubbo/config.go b/protocol/dubbo/config.go
index dbc6989c54780afacef717f1d110833d92967f9f..6a1daf857a7c54ae2c37a1c85ab17481f6fe6068 100644
--- a/protocol/dubbo/config.go
+++ b/protocol/dubbo/config.go
@@ -147,7 +147,7 @@ func GetDefaultServerConfig() ServerConfig {
 	}
 }
 
-// CheckValidity ...
+// CheckValidity confirm getty sessian params.
 func (c *GettySessionParam) CheckValidity() error {
 	var err error
 
@@ -170,7 +170,7 @@ func (c *GettySessionParam) CheckValidity() error {
 	return nil
 }
 
-// CheckValidity ...
+// CheckValidity confirm client params.
 func (c *ClientConfig) CheckValidity() error {
 	var err error
 
@@ -192,7 +192,7 @@ func (c *ClientConfig) CheckValidity() error {
 	return perrors.WithStack(c.GettySessionParam.CheckValidity())
 }
 
-// CheckValidity ...
+// CheckValidity confirm server params.
 func (c *ServerConfig) CheckValidity() error {
 	var err error
 
diff --git a/protocol/dubbo/dubbo_exporter.go b/protocol/dubbo/dubbo_exporter.go
index 1c45c40056f7690ba64838f641fe0b13a1554727..dd80937c5bdf6718c2047b102115d8c08afcd899 100644
--- a/protocol/dubbo/dubbo_exporter.go
+++ b/protocol/dubbo/dubbo_exporter.go
@@ -28,19 +28,19 @@ import (
 	"github.com/apache/dubbo-go/protocol"
 )
 
-// DubboExporter ...
+// DubboExporter is dubbo service exporter.
 type DubboExporter struct {
 	protocol.BaseExporter
 }
 
-// NewDubboExporter ...
+// NewDubboExporter get a DubboExporter.
 func NewDubboExporter(key string, invoker protocol.Invoker, exporterMap *sync.Map) *DubboExporter {
 	return &DubboExporter{
 		BaseExporter: *protocol.NewBaseExporter(key, invoker, exporterMap),
 	}
 }
 
-// Unexport ...
+// Unexport unexport dubbo service exporter.
 func (de *DubboExporter) Unexport() {
 	serviceId := de.GetInvoker().GetUrl().GetParam(constant.BEAN_NAME_KEY, "")
 	interfaceName := de.GetInvoker().GetUrl().GetParam(constant.INTERFACE_KEY, "")
diff --git a/protocol/dubbo/dubbo_invoker.go b/protocol/dubbo/dubbo_invoker.go
index 09c3725710d2a0b821d8e641b0cb7b367189c244..59202d5f49f30acb9e75c4e2f135601285f08e13 100644
--- a/protocol/dubbo/dubbo_invoker.go
+++ b/protocol/dubbo/dubbo_invoker.go
@@ -39,8 +39,9 @@ import (
 )
 
 var (
-	// ErrNoReply ...
-	ErrNoReply          = perrors.New("request need @response")
+	// ErrNoReply
+	ErrNoReply = perrors.New("request need @response")
+	// ErrDestroyedInvoker
 	ErrDestroyedInvoker = perrors.New("request Destroyed invoker")
 )
 
@@ -48,7 +49,7 @@ var (
 	attachmentKey = []string{constant.INTERFACE_KEY, constant.GROUP_KEY, constant.TOKEN_KEY, constant.TIMEOUT_KEY}
 )
 
-// DubboInvoker ...
+// DubboInvoker is dubbo client invoker.
 type DubboInvoker struct {
 	protocol.BaseInvoker
 	client   *Client
@@ -57,7 +58,7 @@ type DubboInvoker struct {
 	reqNum int64
 }
 
-// NewDubboInvoker ...
+// NewDubboInvoker create dubbo client invoker.
 func NewDubboInvoker(url common.URL, client *Client) *DubboInvoker {
 	return &DubboInvoker{
 		BaseInvoker: *protocol.NewBaseInvoker(url),
@@ -66,7 +67,7 @@ func NewDubboInvoker(url common.URL, client *Client) *DubboInvoker {
 	}
 }
 
-// Invoke ...
+// Invoke call remoting.
 func (di *DubboInvoker) Invoke(ctx context.Context, invocation protocol.Invocation) protocol.Result {
 	var (
 		err    error
@@ -122,7 +123,7 @@ func (di *DubboInvoker) Invoke(ctx context.Context, invocation protocol.Invocati
 	return &result
 }
 
-// Destroy ...
+// Destroy destroy dubbo client invoker.
 func (di *DubboInvoker) Destroy() {
 	di.quitOnce.Do(func() {
 		for {
diff --git a/protocol/dubbo/dubbo_protocol.go b/protocol/dubbo/dubbo_protocol.go
index 355dbc802488338ef4dbdd7290166038b312f183..b7d0a8a26818bd318a2724d3310d7da23b046fae 100644
--- a/protocol/dubbo/dubbo_protocol.go
+++ b/protocol/dubbo/dubbo_protocol.go
@@ -45,14 +45,14 @@ var (
 	dubboProtocol *DubboProtocol
 )
 
-// DubboProtocol ...
+// DubboProtocol is a dubbo protocol implement.
 type DubboProtocol struct {
 	protocol.BaseProtocol
 	serverMap  map[string]*Server
 	serverLock sync.Mutex
 }
 
-// NewDubboProtocol ...
+// NewDubboProtocol create a dubbo protocol.
 func NewDubboProtocol() *DubboProtocol {
 	return &DubboProtocol{
 		BaseProtocol: protocol.NewBaseProtocol(),
@@ -60,7 +60,7 @@ func NewDubboProtocol() *DubboProtocol {
 	}
 }
 
-// Export ...
+// Export export dubbo service.
 func (dp *DubboProtocol) Export(invoker protocol.Invoker) protocol.Exporter {
 	url := invoker.GetUrl()
 	serviceKey := url.ServiceKey()
@@ -73,7 +73,7 @@ func (dp *DubboProtocol) Export(invoker protocol.Invoker) protocol.Exporter {
 	return exporter
 }
 
-// Refer ...
+// Refer create dubbo service reference.
 func (dp *DubboProtocol) Refer(url common.URL) protocol.Invoker {
 	//default requestTimeout
 	var requestTimeout = config.GetConsumerConfig().RequestTimeout
@@ -92,7 +92,7 @@ func (dp *DubboProtocol) Refer(url common.URL) protocol.Invoker {
 	return invoker
 }
 
-// Destroy ...
+// Destroy destroy dubbo service.
 func (dp *DubboProtocol) Destroy() {
 	logger.Infof("DubboProtocol destroy.")
 
@@ -124,7 +124,7 @@ func (dp *DubboProtocol) openServer(url common.URL) {
 	}
 }
 
-// GetProtocol ...
+// GetProtocol get a single dubbo protocol.
 func GetProtocol() protocol.Protocol {
 	if dubboProtocol == nil {
 		dubboProtocol = NewDubboProtocol()
diff --git a/protocol/dubbo/listener.go b/protocol/dubbo/listener.go
index 0251b78a2b0d27a68461c16c284b1af53bcb08aa..1f4cc0068efb5688b545fa35b784b4fb2e923dc7 100644
--- a/protocol/dubbo/listener.go
+++ b/protocol/dubbo/listener.go
@@ -86,7 +86,7 @@ func (h *RpcClientHandler) OnOpen(session getty.Session) error {
 
 // OnError ...
 func (h *RpcClientHandler) OnError(session getty.Session, err error) {
-	logger.Infof("session{%s} got error{%v}, will be closed.", session.Stat(), err)
+	logger.Warnf("session{%s} got error{%v}, will be closed.", session.Stat(), err)
 	h.conn.removeSession(session)
 }
 
@@ -201,7 +201,7 @@ func (h *RpcServerHandler) OnOpen(session getty.Session) error {
 
 // OnError ...
 func (h *RpcServerHandler) OnError(session getty.Session, err error) {
-	logger.Infof("session{%s} got error{%v}, will be closed.", session.Stat(), err)
+	logger.Warnf("session{%s} got error{%v}, will be closed.", session.Stat(), err)
 	h.rwlock.Lock()
 	delete(h.sessionMap, session)
 	h.rwlock.Unlock()
diff --git a/protocol/dubbo/readwriter.go b/protocol/dubbo/readwriter.go
index b5c4f509190dbdc85825ad424656240b234786df..9cc7ea25cdfa6152d632f278b33285d7d38f47c9 100644
--- a/protocol/dubbo/readwriter.go
+++ b/protocol/dubbo/readwriter.go
@@ -38,16 +38,17 @@ import (
 // RpcClientPackageHandler
 ////////////////////////////////////////////
 
-// RpcClientPackageHandler ...
+// RpcClientPackageHandler handle package for client in getty.
 type RpcClientPackageHandler struct {
 	client *Client
 }
 
-// NewRpcClientPackageHandler ...
+// NewRpcClientPackageHandler create a RpcClientPackageHandler.
 func NewRpcClientPackageHandler(client *Client) *RpcClientPackageHandler {
 	return &RpcClientPackageHandler{client: client}
 }
 
+// Read decode @data to DubboPackage.
 func (p *RpcClientPackageHandler) Read(ss getty.Session, data []byte) (interface{}, int, error) {
 	pkg := &DubboPackage{}
 
@@ -72,6 +73,7 @@ func (p *RpcClientPackageHandler) Read(ss getty.Session, data []byte) (interface
 	return pkg, hessian.HEADER_LENGTH + pkg.Header.BodyLen, nil
 }
 
+// Write encode @pkg.
 func (p *RpcClientPackageHandler) Write(ss getty.Session, pkg interface{}) ([]byte, error) {
 	req, ok := pkg.(*DubboPackage)
 	if !ok {
@@ -96,9 +98,10 @@ var (
 	rpcServerPkgHandler = &RpcServerPackageHandler{}
 )
 
-// RpcServerPackageHandler ...
+// RpcServerPackageHandler handle package for server in getty.
 type RpcServerPackageHandler struct{}
 
+// Read decode @data to DubboPackage.
 func (p *RpcServerPackageHandler) Read(ss getty.Session, data []byte) (interface{}, int, error) {
 	pkg := &DubboPackage{
 		Body: make([]interface{}, 7),
@@ -169,6 +172,7 @@ func (p *RpcServerPackageHandler) Read(ss getty.Session, data []byte) (interface
 	return pkg, hessian.HEADER_LENGTH + pkg.Header.BodyLen, nil
 }
 
+// Write encode @pkg.
 func (p *RpcServerPackageHandler) Write(ss getty.Session, pkg interface{}) ([]byte, error) {
 	res, ok := pkg.(*DubboPackage)
 	if !ok {
diff --git a/protocol/dubbo/server.go b/protocol/dubbo/server.go
index bd2b37b7a9f055745e183524d19a442af03360f4..8de353a0b372785e89585f4bf2b00b2c42540a2b 100644
--- a/protocol/dubbo/server.go
+++ b/protocol/dubbo/server.go
@@ -71,10 +71,10 @@ func init() {
 	if err := srvConf.CheckValidity(); err != nil {
 		panic(err)
 	}
-	SetServerGrpool()
+	setServerGrpool()
 }
 
-// SetServerConfig ...
+// SetServerConfig set dubbo server config.
 func SetServerConfig(s ServerConfig) {
 	srvConf = &s
 	err := srvConf.CheckValidity()
@@ -82,30 +82,29 @@ func SetServerConfig(s ServerConfig) {
 		logger.Warnf("[ServerConfig CheckValidity] error: %v", err)
 		return
 	}
-	SetServerGrpool()
+	setServerGrpool()
 }
 
-// GetServerConfig ...
+// GetServerConfig get dubbo server config.
 func GetServerConfig() ServerConfig {
 	return *srvConf
 }
 
-// SetServerGrpool ...
-func SetServerGrpool() {
+func setServerGrpool() {
 	if srvConf.GrPoolSize > 1 {
 		srvGrpool = gxsync.NewTaskPool(gxsync.WithTaskPoolTaskPoolSize(srvConf.GrPoolSize), gxsync.WithTaskPoolTaskQueueLength(srvConf.QueueLen),
 			gxsync.WithTaskPoolTaskQueueNumber(srvConf.QueueNumber))
 	}
 }
 
-// Server ...
+// Server is dubbo protocol server.
 type Server struct {
 	conf       ServerConfig
 	tcpServer  getty.Server
 	rpcHandler *RpcServerHandler
 }
 
-// NewServer ...
+// NewServer create a new Server.
 func NewServer() *Server {
 
 	s := &Server{
@@ -156,7 +155,7 @@ func (s *Server) newSession(session getty.Session) error {
 	return nil
 }
 
-// Start ...
+// Start start dubbo server.
 func (s *Server) Start(url common.URL) {
 	var (
 		addr      string
@@ -173,7 +172,7 @@ func (s *Server) Start(url common.URL) {
 
 }
 
-// Stop ...
+// Stop stop dubbo server.
 func (s *Server) Stop() {
 	s.tcpServer.Close()
 }
diff --git a/registry/base_registry.go b/registry/base_registry.go
index 04694da25a38899cfc6f56a8092a15547f142e9c..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
 }
diff --git a/registry/directory/directory.go b/registry/directory/directory.go
index 20be268d7401976ef1b7884f2a7bd40eeacb8158..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
@@ -162,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{}
@@ -198,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())
@@ -208,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
 
@@ -244,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()
 
@@ -255,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()
 	}
@@ -269,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
@@ -280,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)
@@ -294,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,
@@ -316,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/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/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..aa8fbcbe7d6eca682892d4627878fe6bfc3756fe 100644
--- a/registry/protocol/protocol.go
+++ b/registry/protocol/protocol.go
@@ -22,7 +22,6 @@ import (
 	"strings"
 	"sync"
 )
-
 import (
 	gxset "github.com/dubbogo/gost/container/set"
 )
@@ -39,12 +38,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 +92,29 @@ 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)
+	} else {
+		return filterHideKey(providerUrl)
+	}
+}
+
+// filterHideKey filter the parameters that do not need to be output in url(Starting with .)
+func filterHideKey(url *common.URL) *common.URL {
+
+	//be careful params maps in url is map type
+	cloneURL := url.Clone()
+	removeSet := gxset.NewSet()
+	for k, _ := range cloneURL.GetParams() {
+		if strings.HasPrefix(k, ".") {
+			removeSet.Add(k)
+		}
+	}
+	cloneURL.RemoveParams(removeSet)
+	return cloneURL
+}
+
 func (proto *registryProtocol) initConfigurationListeners() {
 	proto.overrideListeners = &sync.Map{}
 	proto.serviceConfigurationListeners = &sync.Map{}
@@ -111,7 +139,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 +151,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 +177,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 +184,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 +373,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/protocol/protocol_test.go b/registry/protocol/protocol_test.go
index cee2a6a625368f655d1b9bc5fe8cc37031e1aef7..15fd3cacfacad36309e0ad4deb3c7c7441e47e26 100644
--- a/registry/protocol/protocol_test.go
+++ b/registry/protocol/protocol_test.go
@@ -284,3 +284,12 @@ func TestExportWithApplicationConfig(t *testing.T) {
 	v2, _ := regProtocol.bounds.Load(getCacheKey(newUrl))
 	assert.NotNil(t, v2)
 }
+
+func TestGetProviderUrlWithHideKey(t *testing.T) {
+	url, _ := common.NewURL("dubbo://127.0.0.1:1111?a=a1&b=b1&.c=c1&.d=d1&e=e1&protocol=registry")
+	providerUrl := getUrlToRegistry(&url, &url)
+	assert.NotContains(t, providerUrl.GetParams(), ".c")
+	assert.NotContains(t, providerUrl.GetParams(), ".d")
+	assert.Contains(t, providerUrl.GetParams(), "a")
+
+}
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 4fd58e9e4d9ecd285675fc416f3d7a36bf19fd54..88d5d6221b4bc7136ba4c3e7c95fb53ba35a9a58 100644
--- a/registry/zookeeper/registry.go
+++ b/registry/zookeeper/registry.go
@@ -53,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
 }
@@ -77,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
 }
@@ -120,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 {
@@ -154,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()
 	}
 }
 
@@ -172,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
@@ -215,8 +249,9 @@ func (r *zkRegistry) getListener(conf *common.URL) (*RegistryConfigurationListen
 	}
 
 	//Interested register to dataconfig.
-	r.dataListener.AddInterestedURL(conf)
-	go r.listener.ListenServiceEvent(fmt.Sprintf("/dubbo/%s/"+constant.DEFAULT_CATEGORY, 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/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)