diff --git a/.gitignore b/.gitignore
index f369c2833aeacbff3aa85a6cd1cdc25520928209..568e9f24541dd6f02dd8670436fd48db481b7f21 100644
--- a/.gitignore
+++ b/.gitignore
@@ -30,3 +30,5 @@ coverage.txt
 remoting/zookeeper/zookeeper-4unittest/
 config_center/zookeeper/zookeeper-4unittest/
 registry/zookeeper/zookeeper-4unittest/
+registry/consul/agent*
+config_center/apollo/mockDubbog.properties.json
diff --git a/.travis.yml b/.travis.yml
index ea6b2035584ba07ae7caa709f83be59cf20c730e..7f30febe7bbd95ffbf1c25abce997408c7681074 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,7 +1,11 @@
 language: go
 
+os:
+  - linux
+  - osx
+
 go:
-  - "1.12"
+  - "1.13"
 
 env:
   - GO111MODULE=on
@@ -10,11 +14,11 @@ install: true
 
 script:
   - go fmt ./... && [[ -z `git status -s` ]]
-  - mkdir -p remoting/zookeeper/zookeeper-4unittest/contrib/fatjar config_center/zookeeper/zookeeper-4unittest/contrib/fatjar registry/zookeeper/zookeeper-4unittest/contrib/fatjar
-  - wget -P "remoting/zookeeper/zookeeper-4unittest/contrib/fatjar" https://github.com/dubbogo/resources/raw/master/zookeeper-4unitest/contrib/fatjar/zookeeper-3.4.9-fatjar.jar
-  - cp remoting/zookeeper/zookeeper-4unittest/contrib/fatjar/zookeeper-3.4.9-fatjar.jar config_center/zookeeper/zookeeper-4unittest/contrib/fatjar/
-  - cp remoting/zookeeper/zookeeper-4unittest/contrib/fatjar/zookeeper-3.4.9-fatjar.jar registry/zookeeper/zookeeper-4unittest/contrib/fatjar/
+  - chmod u+x before_ut.sh && ./before_ut.sh
   - go mod vendor && go test ./... -coverprofile=coverage.txt -covermode=atomic
 
 after_success:
   - bash <(curl -s https://codecov.io/bash)
+
+notifications:
+  webhooks: https://oapi.dingtalk.com/robot/send?access_token=f5d6237f2c79db584e75604f7f88db1ce1673c8c0e98451217b28fde791e1d4f
\ No newline at end of file
diff --git a/CHANGE.md b/CHANGE.md
index 947a695ca854fe8c3d91d8ea989b52dcddbe1523..9864601a3746031c7c3f31afabc62fb56a48eedb 100644
--- a/CHANGE.md
+++ b/CHANGE.md
@@ -1,4 +1,76 @@
 # Release Notes
+---
+
+## 1.3.0
+
+### New Features
+
+- [Add apollo config center support](https://github.com/apache/dubbo-go/pull/250)
+- [Gracefully shutdown](https://github.com/apache/dubbo-go/pull/255)
+- [Add consistent hash load balance support](https://github.com/apache/dubbo-go/pull/261)
+- [Add sticky connection support](https://github.com/apache/dubbo-go/pull/270)
+- [Add async call for dubbo protocol](https://github.com/apache/dubbo-go/pull/272)
+- [Add generic implement](https://github.com/apache/dubbo-go/pull/291)
+- [Add request timeout for method](https://github.com/apache/dubbo-go/pull/284)
+- [Add grpc protocol](https://github.com/apache/dubbo-go/pull/311)
+
+### Enhancement
+    
+- [The SIGSYS and SIGSTOP are not supported in windows platform](https://github.com/apache/dubbo-go/pull/262)
+- [Error should be returned when `NewURL` failed](https://github.com/apache/dubbo-go/pull/266)
+- [Split config center GetConfig method](https://github.com/apache/dubbo-go/pull/267)
+- [Modify closing method for dubbo protocol](https://github.com/apache/dubbo-go/pull/268)
+- [Add SetLoggerLevel method](https://github.com/apache/dubbo-go/pull/271)
+- [Change the position of the lock](https://github.com/apache/dubbo-go/pull/286)
+- [Change zk version and add base_registry](https://github.com/apache/dubbo-go/pull/355)
+
+### Bugfixes
+    
+- [Fix negative wait group count](https://github.com/apache/dubbo-go/pull/253)
+- [After disconnection with ZK registry, cosumer can't listen to provider changes](https://github.com/apache/dubbo-go/pull/258)
+- [The generic filter and default reference filters lack ','](https://github.com/apache/dubbo-go/pull/260)
+- [Url encode zkpath](https://github.com/apache/dubbo-go/pull/283)
+- [Fix jsonrpc about HTTP/1.1](https://github.com/apache/dubbo-go/pull/327)
+- [Fix zk bug](https://github.com/apache/dubbo-go/pull/346)
+- [HessianCodec failed to check package header length](https://github.com/apache/dubbo-go/pull/381)
+
+## 1.2.0
+
+### New Features
+
+- [Add etcdv3 registry support](https://github.com/apache/dubbo-go/pull/148)
+- [Add nacos registry support](https://github.com/apache/dubbo-go/pull/151)
+- [Add fail fast cluster support](https://github.com/apache/dubbo-go/pull/140)
+- [Add available cluster support](https://github.com/apache/dubbo-go/pull/155)
+- [Add broadcast cluster support](https://github.com/apache/dubbo-go/pull/158)
+- [Add forking cluster support](https://github.com/apache/dubbo-go/pull/161)
+- [Add service token authorization support](https://github.com/apache/dubbo-go/pull/202)
+- [Add accessLog filter support](https://github.com/apache/dubbo-go/pull/214)
+- [Add tps limit support](https://github.com/apache/dubbo-go/pull/237)
+- [Add execute limit support](https://github.com/apache/dubbo-go/pull/246)
+- [Move callService to invoker & support attachments](https://github.com/apache/dubbo-go/pull/193)
+- [Move example in dubbo-go project away](https://github.com/apache/dubbo-go/pull/228)
+- [Support dynamic config center which compatible with dubbo 2.6.x & 2.7.x and commit the zookeeper impl](https://github.com/apache/dubbo-go/pull/194)
+
+### Enhancement
+
+- [Split gettyRPCClient.close and gettyRPCClientPool.remove in protocol/dubbo/pool.go](https://github.com/apache/dubbo-go/pull/186)
+- [Remove client from pool before closing it](https://github.com/apache/dubbo-go/pull/190)
+- [Enhance the logic for fetching the local address](https://github.com/apache/dubbo-go/pull/209)
+- [Add protocol_conf default values](https://github.com/apache/dubbo-go/pull/221)
+- [Add task pool for getty](https://github.com/apache/dubbo-go/pull/141)
+- [Update getty: remove read queue](https://github.com/apache/dubbo-go/pull/137)
+- [Clean heartbeat from PendingResponse](https://github.com/apache/dubbo-go/pull/166)
+
+### Bugfixes
+
+- [GettyRPCClientPool remove deadlock](https://github.com/apache/dubbo-go/pull/183/files)
+- [Fix failover cluster bug and url parameter retries change int to string type](https://github.com/apache/dubbo-go/pull/195)
+- [Fix url params unsafe map](https://github.com/apache/dubbo-go/pull/201)
+- [Read protocol config by map key in config yaml instead of protocol name](https://github.com/apache/dubbo-go/pull/218)
+- *Fix dubbo group issues #238* [pr #243](https://github.com/apache/dubbo-go/pull/243) and [pr #244](https://github.com/apache/dubbo-go/pull/244)
+- [Fix bug in reference_config](https://github.com/apache/dubbo-go/pull/157)
+- [Fix high memory bug in zookeeper listener](https://github.com/apache/dubbo-go/pull/168)
 
 ## 1.2.0
 
@@ -42,23 +114,23 @@
 
 ### New Features
 
-- Support Java bigdecimal<https://github.com/apache/dubbo-go/pull/126>锛�
-- Support all JDK exceptions<https://github.com/apache/dubbo-go/pull/120>锛�
-- Support multi-version of service<https://github.com/apache/dubbo-go/pull/119>锛�
-- Allow user set custom params for registry<https://github.com/apache/dubbo-go/pull/117>锛�
-- Support zookeeper config center<https://github.com/apache/dubbo-go/pull/99>锛�
-- Failsafe/Failback  Cluster Strategy<https://github.com/apache/dubbo-go/pull/136>;
+- [Support Java bigdecimal](https://github.com/apache/dubbo-go/pull/126)
+- [Support all JDK exceptions](https://github.com/apache/dubbo-go/pull/120)
+- [Support multi-version of service](https://github.com/apache/dubbo-go/pull/119)
+- [Allow user set custom params for registry](https://github.com/apache/dubbo-go/pull/117)
+- [Support zookeeper config center](https://github.com/apache/dubbo-go/pull/99)
+- [Failsafe/Failback  Cluster Strategy](https://github.com/apache/dubbo-go/pull/136)
 
 ### Enhancement
 
-- Use time wheel instead of time.After to defeat timer object memory leakage<https://github.com/apache/dubbo-go/pull/130> 锛�
+- [Use time wheel instead of time.After to defeat timer object memory leakage](https://github.com/apache/dubbo-go/pull/130)
 
 ### Bugfixes
 
-- Preventing dead loop when got zookeeper unregister event<https://github.com/apache/dubbo-go/pull/129>锛�
-- Delete ineffassign<https://github.com/apache/dubbo-go/pull/127>锛�
-- Add wg.Done() for mockDataListener<https://github.com/apache/dubbo-go/pull/118>锛�
-- Delete wrong spelling words<https://github.com/apache/dubbo-go/pull/107>锛�
-- Use sync.Map to defeat from gettyClientPool deadlock<https://github.com/apache/dubbo-go/pull/106>锛�
-- Handle panic when function args list is empty<https://github.com/apache/dubbo-go/pull/98>锛�
-- url.Values is not safe map<https://github.com/apache/dubbo-go/pull/172>;
+- [Preventing dead loop when got zookeeper unregister event](https://github.com/apache/dubbo-go/pull/129)
+- [Delete ineffassign](https://github.com/apache/dubbo-go/pull/127)
+- [Add wg.Done() for mockDataListener](https://github.com/apache/dubbo-go/pull/118)
+- [Delete wrong spelling words](https://github.com/apache/dubbo-go/pull/107)
+- [Use sync.Map to defeat from gettyClientPool deadlock](https://github.com/apache/dubbo-go/pull/106)
+- [Handle panic when function args list is empty](https://github.com/apache/dubbo-go/pull/98)
+- [url.Values is not safe map](https://github.com/apache/dubbo-go/pull/172)
diff --git a/LICENSE b/LICENSE
index e76f9d9dd705daad997776153b1060f5bf8c2a1d..75b52484ea471f882c29e02693b4f02dba175b5e 100644
--- a/LICENSE
+++ b/LICENSE
@@ -176,6 +176,19 @@
 
    END OF TERMS AND CONDITIONS
 
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
    Licensed 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
diff --git a/NOTICE b/NOTICE
index a9bd809c5c43a9d88a773b5f0c421b252abf38de..d7aa899d1cef0fba67826bebd0d587e9cc17ba5d 100644
--- a/NOTICE
+++ b/NOTICE
@@ -1,5 +1,5 @@
 Apache Dubbo Go
-Copyright 2018-2019 The Apache Software Foundation
+Copyright 2018-2020 The Apache Software Foundation
 
 This product includes software developed at
 The Apache Software Foundation (http://www.apache.org/).
diff --git a/README.md b/README.md
index 6ff358d9d603cb25833a36afb774cc46595e63f5..1dde951d350e6ee51f3f2aeeac7bb516b1b999be 100644
--- a/README.md
+++ b/README.md
@@ -2,6 +2,7 @@
 
 [![Build Status](https://travis-ci.org/apache/dubbo-go.svg?branch=master)](https://travis-ci.org/apache/dubbo-go)
 [![codecov](https://codecov.io/gh/apache/dubbo-go/branch/master/graph/badge.svg)](https://codecov.io/gh/apache/dubbo-go)
+[![go.dev reference](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=flat-square)](https://pkg.go.dev/github.com/apache/dubbo-go?tab=doc)
 
 ---
 Apache Dubbo Go Implementation.
@@ -13,6 +14,12 @@ Apache License, Version 2.0
 
 ## Release note ##
 
+[v1.3.0 - Mar 1, 2020](https://github.com/apache/dubbo-go/releases/tag/v1.3.0)
+
+[v1.2.0 - Nov 15, 2019](https://github.com/apache/dubbo-go/releases/tag/v1.2.0)
+
+[v1.1.0 - Sep 7, 2019 the first release after transferred to apache](https://github.com/apache/dubbo-go/releases/tag/v1.1.0)
+
 [v1.0.0 - May 29, 2019 compatible with dubbo v2.6.5](https://github.com/apache/dubbo-go/releases/tag/v1.0.0)
 
 ## Project Architecture ##
@@ -27,39 +34,84 @@ If you wanna know more about dubbo-go, please visit this reference [Project Arch
 
 Finished List:
 
-- Role: Consumer, Provider
-- Transport: HTTP, TCP
-- Codec: JsonRPC v2, Hessian v2
-- Registry: ZooKeeper/[etcd v3](https://github.com/apache/dubbo-go/pull/148)/[nacos](https://github.com/apache/dubbo-go/pull/151)/[consul](https://github.com/apache/dubbo-go/pull/121)
-- Dynamic Configure Center & Service Management Configurator: Zookeeper
-- Cluster Strategy: Failover/[Failfast](https://github.com/apache/dubbo-go/pull/140)/[Failsafe/Failback](https://github.com/apache/dubbo-go/pull/136)/[Available](https://github.com/apache/dubbo-go/pull/155)/[Broadcast](https://github.com/apache/dubbo-go/pull/158)/[Forking](https://github.com/apache/dubbo-go/pull/161)
-- Load Balance: Random/[RoundRobin](https://github.com/apache/dubbo-go/pull/66)/[LeastActive](https://github.com/apache/dubbo-go/pull/65)
-- Filter: Echo Health Check/[Circuit break and service downgrade](https://github.com/apache/dubbo-go/pull/133)/[TokenFilter](https://github.com/apache/dubbo-go/pull/202)/[AccessLogFilter](https://github.com/apache/dubbo-go/pull/214)/[TpsLimitFilter](https://github.com/apache/dubbo-go/pull/237)/[ExecuteLimitFilter](https://github.com/apache/dubbo-go/pull/246)
-- Other feature: [generic invoke](https://github.com/apache/dubbo-go/pull/122)/start check/connecting certain provider/multi-protocols/multi-registries/multi-versions/service group
+- Role
+    * Consumer
+    * Provider
+
+- Transport
+    * HTTP
+    * TCP
+
+- Codec
+    * JsonRPC V2
+    * Hessian V2
+
+- Protocol
+    * Dubbo
+    * Jsonrpc2.0
+    * [gRPC](https://github.com/apache/dubbo-go/pull/311)
+
+- Registry
+    * ZooKeeper
+    * [etcd v3](https://github.com/apache/dubbo-go/pull/148)
+    * [nacos](https://github.com/apache/dubbo-go/pull/151)
+    * [consul](https://github.com/apache/dubbo-go/pull/121)
+
+- Dynamic Configure Center & Service Management Configurator
+    * Zookeeper
+    * [apollo](https://github.com/apache/dubbo-go/pull/250)
+
+- Cluster Strategy
+    * Failover
+    * [Failfast](https://github.com/apache/dubbo-go/pull/140)
+    * [Failsafe/Failback](https://github.com/apache/dubbo-go/pull/136)
+    * [Available](https://github.com/apache/dubbo-go/pull/155)
+    * [Broadcast](https://github.com/apache/dubbo-go/pull/158)
+    * [Forking](https://github.com/apache/dubbo-go/pull/161)
+
+- Load Balance
+    * Random
+    * [RoundRobin](https://github.com/apache/dubbo-go/pull/66)
+    * [LeastActive](https://github.com/apache/dubbo-go/pull/65)
+    * [ConsistentHash](https://github.com/apache/dubbo-go/pull/261)
+
+- Filter
+    * Echo Health Check
+    * [Circuit break and service downgrade](https://github.com/apache/dubbo-go/pull/133)
+    * [TokenFilter](https://github.com/apache/dubbo-go/pull/202)
+    * [AccessLogFilter](https://github.com/apache/dubbo-go/pull/214)
+    * [TpsLimitFilter](https://github.com/apache/dubbo-go/pull/237)
+    * [ExecuteLimitFilter](https://github.com/apache/dubbo-go/pull/246)
+    * [GenericServiceFilter](https://github.com/apache/dubbo-go/pull/291)
+
+- Invoke
+    * [generic invoke](https://github.com/apache/dubbo-go/pull/122)
+
+- Others:
+    * start check
+    * connecting certain provider
+    * multi-protocols
+    * multi-registries
+    * multi-versions
+    * service group
 
 Working List:
 
-- Load Balance: ConsistentHash
 - Registry: k8s
-- Configure Center: apollo
 - Metadata Center (dubbo v2.7.x)
-- Metrics: Promethus(dubbo v2.7.x)
-
-Todo List:
-
-- Registry: kubernetes
-- Routing: istio
-- tracing (dubbo ecosystem)
+- Metrics: Opentracing/Promethus(dubbo v2.7.x)
 
 You can know more about dubbo-go by its [roadmap](https://github.com/apache/dubbo-go/wiki/Roadmap).
 
+![feature](https://raw.githubusercontent.com/wiki/apache/dubbo-go/dubbo-go-arch.png)
+
 ## Document
 
-TODO
+https://dubbogo.github.io/dubbo-go-website (**Improving**)
 
 ## Quick Start
 
-[dubbogo-samples](https://github.com/dubbogo/dubbogo-samples) shows how to use dubbo-go. Please read the [dubbogo-samples/README.md](https://github.com/dubbogo/dubbogo-samples/blob/master/README.md) carefully to learn how to dispose the configuration and compile the program.
+[dubbo-samples/golang](https://github.com/dubbogo/dubbo-samples) shows how to use dubbo-go. Please read the [dubbo-samples/golang/README.md](https://github.com/dubbogo/dubbo-samples/blob/master/golang/README.md) carefully to learn how to dispose the configuration and compile the program.
 
 ## Running unit tests
 
@@ -75,7 +127,7 @@ Windows
 before_ut.bat
 ```
 
-# Run
+### Run
 ```bash
 go test ./...
 
@@ -83,6 +135,10 @@ go test ./...
 go test ./... -coverprofile=coverage.txt -covermode=atomic
 ```
 
+## Build
+
+Please move to [dubbo-samples/golang](https://github.com/dubbogo/dubbo-samples)
+
 ## Contributing
 
 If you are willing to do some code contributions and document contributions to [Apache/dubbo-go](https://github.com/apache/dubbo-go), please visit [contribution intro](https://github.com/apache/dubbo-go/blob/master/contributing.md).
@@ -97,11 +153,6 @@ About dubbo-go benchmarking report, please refer to [dubbo benchmarking report](
 
 If you are using [apache/dubbo-go](github.com/apache/dubbo-go) and think that it helps you or want do some contributions to it, please add your company to to [the user list](https://github.com/apache/dubbo-go/issues/2) to let us know your needs.
 
-
-![ctrip](https://pic.c-ctrip.com/common/c_logo2013.png)![Excellent Health Technology Group](https://raw.githubusercontent.com/dajiiu/photo/static/mirror/haozhuo_logo.png)
-![ctrip](https://raw.githubusercontent.com/pantianying/go-tool/master/picture/logo_2-removebg-preview.png)
-
-## Stargazers
-
-[![Stargazers over time](https://starchart.cc/apache/dubbo-go.svg)](https://starchart.cc/apache/dubbo-go)
-
+![ctrip](https://pic.c-ctrip.com/common/c_logo2013.png)
+![Excellent Health Technology Group](https://raw.githubusercontent.com/dajiiu/photo/static/mirror/haozhuo_logo.png)
+![tuya](https://raw.githubusercontent.com/pantianying/go-tool/master/picture/logo_2-removebg-preview.png)
diff --git a/README_CN.md b/README_CN.md
index 7a7b061960e8dcc49f1da0ba6490ca8bbbf09e22..ade924e7a9a6206b6e935e084d68679957dd7fcb 100644
--- a/README_CN.md
+++ b/README_CN.md
@@ -2,6 +2,7 @@
 
 [![Build Status](https://travis-ci.org/apache/dubbo-go.svg?branch=master)](https://travis-ci.org/apache/dubbo-go)
 [![codecov](https://codecov.io/gh/apache/dubbo-go/branch/master/graph/badge.svg)](https://codecov.io/gh/apache/dubbo-go)
+[![go.dev reference](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=flat-square)](https://pkg.go.dev/github.com/apache/dubbo-go?tab=doc)
 
 ---
 Apache Dubbo Go 璇█瀹炵幇
@@ -12,6 +13,12 @@ Apache License, Version 2.0
 
 ## 鍙戝竷鏃ュ織 ##
 
+[v1.3.0 - 2020骞�3鏈�1鏃(https://github.com/apache/dubbo-go/releases/tag/v1.3.0)
+
+[v1.2.0 - 2019骞�11鏈�15鏃(https://github.com/apache/dubbo-go/releases/tag/v1.2.0)
+
+[v1.1.0 - 2019骞�9鏈�7鏃� 鎹愮尞缁橝pache涔嬪悗鐨勭涓€娆elease](https://github.com/apache/dubbo-go/releases/tag/v1.1.0)
+
 [v1.0.0 - 2019骞�5鏈�29鏃� 鍏煎dubbo v2.6.5 鐗堟湰](https://github.com/apache/dubbo-go/releases/tag/v1.0.0)
 
 ## 宸ョ▼鏋舵瀯 ##
@@ -26,40 +33,83 @@ Apache License, Version 2.0
 
 瀹炵幇鍒楄〃:
 
-- 瑙掕壊绔�: Consumer, Provider
-- 浼犺緭鍗忚: HTTP, TCP
-- 搴忓垪鍖栧崗璁�: JsonRPC v2, Hessian v2
-- 娉ㄥ唽涓績: ZooKeeper/[etcd v3](https://github.com/apache/dubbo-go/pull/148)/[nacos](https://github.com/apache/dubbo-go/pull/151)/[consul](https://github.com/apache/dubbo-go/pull/121)
-- 鍔ㄦ€侀厤缃腑蹇冧笌鏈嶅姟娌荤悊閰嶇疆鍣紙config center锛�: Zookeeper
-- 闆嗙兢绛栫暐: Failover/[Failfast](https://github.com/apache/dubbo-go/pull/140)/[Failsafe/Failback](https://github.com/apache/dubbo-go/pull/136)/[Available](https://github.com/apache/dubbo-go/pull/155)/[Broadcast](https://github.com/apache/dubbo-go/pull/158)/[Forking](https://github.com/apache/dubbo-go/pull/161)
-- 璐熻浇鍧囪 绛栫暐: Random/[RoundRobin](https://github.com/apache/dubbo-go/pull/66)/[LeastActive](https://github.com/apache/dubbo-go/pull/65)
-- 杩囨护鍣�: Echo Health Check/[鏈嶅姟鐔旀柇&闄嶇骇](https://github.com/apache/dubbo-go/pull/133)/[TokenFilter](https://github.com/apache/dubbo-go/pull/202)/[AccessLogFilter](https://github.com/apache/dubbo-go/pull/214)/[TpsLimitFilter](https://github.com/apache/dubbo-go/pull/237)[ExecuteLimitFilter](https://github.com/apache/dubbo-go/pull/246)
-- 鍏朵粬鍔熻兘鏀寔: [娉涘寲璋冪敤](https://github.com/apache/dubbo-go/pull/122)/鍚姩鏃舵鏌�/鏈嶅姟鐩磋繛/澶氭湇鍔″崗璁�/澶氭敞鍐屼腑蹇�/澶氭湇鍔$増鏈�/鏈嶅姟鍒嗙粍
+- 瑙掕壊绔�
+    * Consumer
+    * Provider
+
+- 浼犺緭鍗忚
+    * HTTP
+    * TCP
+
+- 搴忓垪鍖栧崗璁�
+    * JsonRPC V2
+    * Hessian V2
+
+- 鍗忚
+    * Dubbo
+    * Jsonrpc2.0
+    * [gRPC](https://github.com/apache/dubbo-go/pull/311)
+
+- 娉ㄥ唽涓績
+    * ZooKeeper
+    * [etcd v3](https://github.com/apache/dubbo-go/pull/148)
+    * [nacos](https://github.com/apache/dubbo-go/pull/151)
+    * [consul](https://github.com/apache/dubbo-go/pull/121)
+
+- 鍔ㄦ€侀厤缃腑蹇冧笌鏈嶅姟娌荤悊閰嶇疆鍣�
+    * Zookeeper
+    * [apollo](https://github.com/apache/dubbo-go/pull/250)
+
+- 闆嗙兢绛栫暐
+    * Failover
+    * [Failfast](https://github.com/apache/dubbo-go/pull/140)
+    * [Failsafe/Failback](https://github.com/apache/dubbo-go/pull/136)
+    * [Available](https://github.com/apache/dubbo-go/pull/155)
+    * [Broadcast](https://github.com/apache/dubbo-go/pull/158)
+    * [Forking](https://github.com/apache/dubbo-go/pull/161)
+
+- 璐熻浇鍧囪 绛栫暐
+    * Random
+    * [RoundRobin](https://github.com/apache/dubbo-go/pull/66)
+    * [LeastActive](https://github.com/apache/dubbo-go/pull/65)
+    * [ConsistentHash](https://github.com/apache/dubbo-go/pull/261)
+
+- 杩囨护鍣�
+    * Echo Health Check
+    * [鏈嶅姟鐔旀柇&闄嶇骇](https://github.com/apache/dubbo-go/pull/133)
+    * [TokenFilter](https://github.com/apache/dubbo-go/pull/202)
+    * [AccessLogFilter](https://github.com/apache/dubbo-go/pull/214)
+    * [TpsLimitFilter](https://github.com/apache/dubbo-go/pull/237)
+    * [ExecuteLimitFilter](https://github.com/apache/dubbo-go/pull/246)
+
+- 璋冪敤
+    * [娉涘寲璋冪敤](https://github.com/apache/dubbo-go/pull/122)
+
+- 鍏朵粬鍔熻兘鏀寔:
+    * 鍚姩鏃舵鏌�
+    * 鏈嶅姟鐩磋繛
+    * 澶氭湇鍔″崗璁�
+    * 澶氭敞鍐屼腑蹇�
+    * 澶氭湇鍔$増鏈�
+    * 鏈嶅姟鍒嗙粍
 
 寮€鍙戜腑鍒楄〃:
 
-- 闆嗙兢绛栫暐: Forking
-- 璐熻浇鍧囪 绛栫暐: ConsistentHash
 - 娉ㄥ唽涓績: k8s
-- 閰嶇疆涓績: apollo
 - 鍏冩暟鎹腑蹇� (dubbo v2.7.x)
-- Metrics: Promethus(dubbo v2.7.x)
+- Metrics: Opentracing/Promethus(dubbo v2.7.x)
 
-浠诲姟鍒楄〃:
+浣犲彲浠ラ€氳繃璁块棶 [roadmap](https://github.com/apache/dubbo-go/wiki/Roadmap) 鐭ラ亾鏇村鍏充簬 dubbo-go 鐨勪俊鎭€�
 
-- 娉ㄥ唽涓績: kubernetes
-- Routing: istio
-- tracing (dubbo ecosystem)
-
-浣犲彲浠ラ€氳繃璁块棶 [roadmap](https://github.com/apache/dubbo-go/wiki/Roadmap) 鐭ラ亾鏇村鍏充簬 dubbo-go 鐨勪俊鎭�
+![feature](https://raw.githubusercontent.com/wiki/apache/dubbo-go/dubbo-go-arch.png)
 
 ## 鏂囨。
 
-TODO
+https://dubbogo.github.io/dubbo-go-website (**瀹屽杽涓�**)
 
 ## 蹇€熷紑濮� ##
 
-[dubbogo-samples](https://github.com/dubbogo/dubbogo-samples)杩欎釜椤圭洰鐨勪簨渚嬪睍绀轰簡濡備綍浣跨敤 dubbo-go 銆傝浠旂粏闃呰 [dubbogo-samples/README.md](https://github.com/dubbogo/dubbogo-samples/blob/master/README.md) 瀛︿範濡備綍澶勭悊閰嶇疆骞剁紪璇戠▼搴忋€�
+[dubbo-samples/golang](https://github.com/dubbogo/dubbo-samples)杩欎釜椤圭洰鐨勪簨渚嬪睍绀轰簡濡備綍浣跨敤 dubbo-go 銆傝浠旂粏闃呰 [dubbo-samples/golang/README.md](https://github.com/dubbogo/dubbo-samples/blob/master/golang/README.md) 瀛︿範濡備綍澶勭悊閰嶇疆骞剁紪璇戠▼搴忋€�
 
 ## 杩愯鍗曟祴
 
@@ -75,7 +125,7 @@ Windows
 before_ut.bat
 ```
 
-# 鎵ц
+### 鎵ц
 ```bash
 go test ./...
 
@@ -83,6 +133,10 @@ go test ./...
 go test ./... -coverprofile=coverage.txt -covermode=atomic
 ```
 
+## 缂栬瘧
+
+璇风Щ姝� [dubbo-samples/golang](https://github.com/dubbogo/dubbo-samples)
+
 ## 濡備綍璐$尞
 
 濡傛灉鎮ㄦ効鎰忕粰 [Apache/dubbo-go](https://github.com/apache/dubbo-go) 璐$尞浠g爜鎴栬€呮枃妗o紝鎴戜滑閮界儹鐑堟杩庛€傚叿浣撹鍙傝€� [contribution intro](https://github.com/apache/dubbo-go/blob/master/contributing.md)銆�
@@ -98,8 +152,5 @@ go test ./... -coverprofile=coverage.txt -covermode=atomic
 鑻ヤ綘姝e湪浣跨敤 [apache/dubbo-go](github.com/apache/dubbo-go) 涓旇涓哄叾鏈夌敤鎴栬€呭悜瀵瑰叾鍋氭敼杩涳紝璇峰繚鍒楄吹鍙镐俊鎭簬 [鐢ㄦ埛鍒楄〃](https://github.com/apache/dubbo-go/issues/2)锛屼互渚挎垜浠煡鏅撲箣銆�
 
 ![ctrip](https://pic.c-ctrip.com/common/c_logo2013.png)
-
-## Stargazers
-
-[![Stargazers over time](https://starchart.cc/apache/dubbo-go.svg)](https://starchart.cc/apache/dubbo-go)
-
+![Excellent Health Technology Group](https://raw.githubusercontent.com/dajiiu/photo/static/mirror/haozhuo_logo.png)
+![tuya](https://raw.githubusercontent.com/pantianying/go-tool/master/picture/logo_2-removebg-preview.png)
diff --git a/before_ut.bat b/before_ut.bat
index 5296d0f8769b7b9f521f82e68bf3b10f4b5d16b4..abe7fc250ef44bf01396ae20c4dacc9db3f60ce2 100644
--- a/before_ut.bat
+++ b/before_ut.bat
@@ -14,8 +14,24 @@
 ::  See the License for the specific language governing permissions and
 ::  limitations under the License.
 
-set zkJar=zookeeper-3.4.9-fatjar.jar
-md remoting\zookeeper\zookeeper-4unittest\contrib\fatjar config_center\zookeeper\zookeeper-4unittest\contrib\fatjar registry\zookeeper\zookeeper-4unittest\contrib\fatjar
-curl -L https://github.com/dubbogo/resources/raw/master/zookeeper-4unitest/contrib/fatjar/%zkJar% -o remoting/zookeeper/zookeeper-4unittest/contrib/fatjar/%zkJar%
-xcopy /f "remoting/zookeeper/zookeeper-4unittest/contrib/fatjar/%zkJar%" "config_center/zookeeper/zookeeper-4unittest/contrib/fatjar/"
-xcopy /f "remoting/zookeeper/zookeeper-4unittest/contrib/fatjar/%zkJar%" "registry/zookeeper/zookeeper-4unittest/contrib/fatjar/"
\ No newline at end of file
+set zkJarName="zookeeper-3.4.9-fatjar.jar"
+set remoteJarUrl="https://github.com/dubbogo/resources/raw/master/zookeeper-4unitest/contrib/fatjar/%zkJarName%"
+set zkJarPath="remoting/zookeeper/zookeeper-4unittest/contrib/fatjar"
+set zkJar="%zkJarPath%/%zkJarName%"
+
+if not exist "%zkJar%" (
+   md %zkJarPath%
+   curl -L %remoteJarUrl% -o %zkJar%
+)
+
+md config_center\zookeeper\zookeeper-4unittest\contrib\fatjar
+xcopy /f "%zkJar%" "config_center/zookeeper/zookeeper-4unittest/contrib/fatjar/"
+
+md registry\zookeeper\zookeeper-4unittest\contrib\fatjar
+xcopy /f "%zkJar%" "registry/zookeeper/zookeeper-4unittest/contrib/fatjar/"
+
+md cluster\router\chain\zookeeper-4unittest\contrib\fatjar
+xcopy /f "%zkJar%" "cluster/router/chain/zookeeper-4unittest/contrib/fatjar/"
+
+md cluster\router\condition\zookeeper-4unittest\contrib\fatjar
+xcopy /f "%zkJar%" "cluster/router/condition/zookeeper-4unittest/contrib/fatjar/"
\ No newline at end of file
diff --git a/before_ut.sh b/before_ut.sh
old mode 100644
new mode 100755
index 323173bcc64c3cbe9916747e10dd3ea8538457ea..7ee92e57a26cbdbb1d1a0b3e792726ad5e1954f8
--- a/before_ut.sh
+++ b/before_ut.sh
@@ -14,8 +14,24 @@
 #  See the License for the specific language governing permissions and
 #  limitations under the License.
 
+zkJarName="zookeeper-3.4.9-fatjar.jar"
+remoteJarUrl="https://github.com/dubbogo/resources/raw/master/zookeeper-4unitest/contrib/fatjar/${zkJarName}"
+zkJarPath="remoting/zookeeper/zookeeper-4unittest/contrib/fatjar"
+zkJar="${zkJarPath}/${zkJarName}"
 
-mkdir -p remoting/zookeeper/zookeeper-4unittest/contrib/fatjar config_center/zookeeper/zookeeper-4unittest/contrib/fatjar registry/zookeeper/zookeeper-4unittest/contrib/fatjar
-wget -P "remoting/zookeeper/zookeeper-4unittest/contrib/fatjar" https://github.com/dubbogo/resources/raw/master/zookeeper-4unitest/contrib/fatjar/zookeeper-3.4.9-fatjar.jar
-cp remoting/zookeeper/zookeeper-4unittest/contrib/fatjar/zookeeper-3.4.9-fatjar.jar config_center/zookeeper/zookeeper-4unittest/contrib/fatjar/
-cp remoting/zookeeper/zookeeper-4unittest/contrib/fatjar/zookeeper-3.4.9-fatjar.jar registry/zookeeper/zookeeper-4unittest/contrib/fatjar/
\ No newline at end of file
+if [ ! -f "${zkJar}" ]; then
+    mkdir -p ${zkJarPath}
+    wget -P "${zkJarPath}" ${remoteJarUrl}
+fi
+
+mkdir -p config_center/zookeeper/zookeeper-4unittest/contrib/fatjar
+cp ${zkJar} config_center/zookeeper/zookeeper-4unittest/contrib/fatjar/
+
+mkdir -p registry/zookeeper/zookeeper-4unittest/contrib/fatjar
+cp ${zkJar} registry/zookeeper/zookeeper-4unittest/contrib/fatjar/
+
+mkdir -p cluster/router/chain/zookeeper-4unittest/contrib/fatjar
+cp ${zkJar} cluster/router/chain/zookeeper-4unittest/contrib/fatjar
+
+mkdir -p cluster/router/condition/zookeeper-4unittest/contrib/fatjar
+cp ${zkJar} cluster/router/condition/zookeeper-4unittest/contrib/fatjar
\ No newline at end of file
diff --git a/cluster/cluster.go b/cluster/cluster.go
index d89df5a21954d63552b857d988fa9a9e7d1fcfb5..617ce5ebf0fa7b5dc7f6047caacec9865aa6960f 100644
--- a/cluster/cluster.go
+++ b/cluster/cluster.go
@@ -21,6 +21,7 @@ import (
 	"github.com/apache/dubbo-go/protocol"
 )
 
+// Cluster ...
 type Cluster interface {
 	Join(Directory) protocol.Invoker
 }
diff --git a/cluster/cluster_impl/available_cluster.go b/cluster/cluster_impl/available_cluster.go
index 7e748cd938319ff437bb3fb6c7945b857d316069..2ad140b93e15b97d1517119b07b1080a68a0503f 100644
--- a/cluster/cluster_impl/available_cluster.go
+++ b/cluster/cluster_impl/available_cluster.go
@@ -31,6 +31,7 @@ func init() {
 	extension.SetCluster(available, NewAvailableCluster)
 }
 
+// NewAvailableCluster ...
 func NewAvailableCluster() cluster.Cluster {
 	return &availableCluster{}
 }
diff --git a/cluster/cluster_impl/available_cluster_invoker.go b/cluster/cluster_impl/available_cluster_invoker.go
index c59c0702c216fe5c58d190a023322aaa00ac9c17..6f6d2dffbbbf2f6c758097b11713ae0c1b6bd387 100644
--- a/cluster/cluster_impl/available_cluster_invoker.go
+++ b/cluster/cluster_impl/available_cluster_invoker.go
@@ -18,6 +18,7 @@ limitations under the License.
 package cluster_impl
 
 import (
+	"context"
 	"fmt"
 )
 
@@ -34,13 +35,14 @@ type availableClusterInvoker struct {
 	baseClusterInvoker
 }
 
+// NewAvailableClusterInvoker ...
 func NewAvailableClusterInvoker(directory cluster.Directory) protocol.Invoker {
 	return &availableClusterInvoker{
 		baseClusterInvoker: newBaseClusterInvoker(directory),
 	}
 }
 
-func (invoker *availableClusterInvoker) Invoke(invocation protocol.Invocation) protocol.Result {
+func (invoker *availableClusterInvoker) Invoke(ctx context.Context, invocation protocol.Invocation) protocol.Result {
 	invokers := invoker.directory.List(invocation)
 	err := invoker.checkInvokers(invokers, invocation)
 	if err != nil {
@@ -54,7 +56,7 @@ func (invoker *availableClusterInvoker) Invoke(invocation protocol.Invocation) p
 
 	for _, ivk := range invokers {
 		if ivk.IsAvailable() {
-			return ivk.Invoke(invocation)
+			return ivk.Invoke(ctx, invocation)
 		}
 	}
 	return &protocol.RPCResult{Err: errors.New(fmt.Sprintf("no provider available in %v", invokers))}
diff --git a/cluster/cluster_impl/available_cluster_invoker_test.go b/cluster/cluster_impl/available_cluster_invoker_test.go
index 04032a7f24dec0e73acb15921f753921391f1515..dc0666d5afa633c86fdfaa48b93a334c19bb0248 100644
--- a/cluster/cluster_impl/available_cluster_invoker_test.go
+++ b/cluster/cluster_impl/available_cluster_invoker_test.go
@@ -39,7 +39,7 @@ import (
 )
 
 var (
-	availableUrl, _ = common.NewURL(context.Background(), "dubbo://192.168.1.1:20000/com.ikurento.user.UserProvider")
+	availableUrl, _ = common.NewURL("dubbo://192.168.1.1:20000/com.ikurento.user.UserProvider")
 )
 
 func registerAvailable(t *testing.T, invoker *mock.MockInvoker) protocol.Invoker {
@@ -66,7 +66,7 @@ func TestAvailableClusterInvokerSuccess(t *testing.T) {
 	invoker.EXPECT().IsAvailable().Return(true)
 	invoker.EXPECT().Invoke(gomock.Any()).Return(mockResult)
 
-	result := clusterInvoker.Invoke(&invocation.RPCInvocation{})
+	result := clusterInvoker.Invoke(context.Background(), &invocation.RPCInvocation{})
 
 	assert.Equal(t, mockResult, result)
 }
@@ -80,7 +80,7 @@ func TestAvailableClusterInvokerNoAvail(t *testing.T) {
 
 	invoker.EXPECT().IsAvailable().Return(false)
 
-	result := clusterInvoker.Invoke(&invocation.RPCInvocation{})
+	result := clusterInvoker.Invoke(context.TODO(), &invocation.RPCInvocation{})
 
 	assert.NotNil(t, result.Error())
 	assert.True(t, strings.Contains(result.Error().Error(), "no provider available"))
diff --git a/cluster/cluster_impl/base_cluster_invoker.go b/cluster/cluster_impl/base_cluster_invoker.go
index d93e9a6a98a8cbf7ee2cb97abd0248353e0c3154..12799994125c4bf5d968dfc811cda374effbf85c 100644
--- a/cluster/cluster_impl/base_cluster_invoker.go
+++ b/cluster/cluster_impl/base_cluster_invoker.go
@@ -35,6 +35,7 @@ type baseClusterInvoker struct {
 	directory      cluster.Directory
 	availablecheck bool
 	destroyed      *atomic.Bool
+	stickyInvoker  protocol.Invoker
 }
 
 func newBaseClusterInvoker(directory cluster.Directory) baseClusterInvoker {
@@ -44,6 +45,7 @@ func newBaseClusterInvoker(directory cluster.Directory) baseClusterInvoker {
 		destroyed:      atomic.NewBool(false),
 	}
 }
+
 func (invoker *baseClusterInvoker) GetUrl() common.URL {
 	return invoker.directory.GetUrl()
 }
@@ -56,7 +58,9 @@ func (invoker *baseClusterInvoker) Destroy() {
 }
 
 func (invoker *baseClusterInvoker) IsAvailable() bool {
-	//TODO:sticky connection
+	if invoker.stickyInvoker != nil {
+		return invoker.stickyInvoker.IsAvailable()
+	}
 	return invoker.directory.IsAvailable()
 }
 
@@ -83,15 +87,42 @@ func (invoker *baseClusterInvoker) checkWhetherDestroyed() error {
 }
 
 func (invoker *baseClusterInvoker) doSelect(lb cluster.LoadBalance, invocation protocol.Invocation, invokers []protocol.Invoker, invoked []protocol.Invoker) protocol.Invoker {
-	//todo:sticky connect
+
+	var selectedInvoker protocol.Invoker
+	url := invokers[0].GetUrl()
+	sticky := url.GetParamBool(constant.STICKY_KEY, false)
+	//Get the service method sticky config if have
+	sticky = url.GetMethodParamBool(invocation.MethodName(), constant.STICKY_KEY, sticky)
+
+	if invoker.stickyInvoker != nil && !isInvoked(invoker.stickyInvoker, invokers) {
+		invoker.stickyInvoker = nil
+	}
+
+	if sticky && invoker.stickyInvoker != nil && (invoked == nil || !isInvoked(invoker.stickyInvoker, invoked)) {
+		if invoker.availablecheck && invoker.stickyInvoker.IsAvailable() {
+			return invoker.stickyInvoker
+		}
+	}
+
+	selectedInvoker = invoker.doSelectInvoker(lb, invocation, invokers, invoked)
+
+	if sticky {
+		invoker.stickyInvoker = selectedInvoker
+	}
+	return selectedInvoker
+
+}
+
+func (invoker *baseClusterInvoker) doSelectInvoker(lb cluster.LoadBalance, invocation protocol.Invocation, invokers []protocol.Invoker, invoked []protocol.Invoker) protocol.Invoker {
 	if len(invokers) == 1 {
 		return invokers[0]
 	}
+
 	selectedInvoker := lb.Select(invokers, invocation)
 
 	//judge to if the selectedInvoker is invoked
 
-	if !selectedInvoker.IsAvailable() || !invoker.availablecheck || isInvoked(selectedInvoker, invoked) {
+	if (!selectedInvoker.IsAvailable() && invoker.availablecheck) || isInvoked(selectedInvoker, invoked) {
 		// do reselect
 		var reslectInvokers []protocol.Invoker
 
@@ -106,13 +137,12 @@ func (invoker *baseClusterInvoker) doSelect(lb cluster.LoadBalance, invocation p
 		}
 
 		if len(reslectInvokers) > 0 {
-			return lb.Select(reslectInvokers, invocation)
+			selectedInvoker = lb.Select(reslectInvokers, invocation)
 		} else {
 			return nil
 		}
 	}
 	return selectedInvoker
-
 }
 
 func isInvoked(selectedInvoker protocol.Invoker, invoked []protocol.Invoker) bool {
diff --git a/cluster/cluster_impl/base_cluster_invoker_test.go b/cluster/cluster_impl/base_cluster_invoker_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..d074697b85a3cf5b770de90da4847043d98c9df1
--- /dev/null
+++ b/cluster/cluster_impl/base_cluster_invoker_test.go
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cluster_impl
+
+import (
+	"fmt"
+	"testing"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+import (
+	"github.com/apache/dubbo-go/cluster/loadbalance"
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/protocol"
+	"github.com/apache/dubbo-go/protocol/invocation"
+)
+
+func Test_StickyNormal(t *testing.T) {
+	invokers := []protocol.Invoker{}
+	for i := 0; i < 10; i++ {
+		url, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i))
+		url.SetParam("sticky", "true")
+		invokers = append(invokers, NewMockInvoker(url, 1))
+	}
+	base := &baseClusterInvoker{}
+	base.availablecheck = true
+	invoked := []protocol.Invoker{}
+	result := base.doSelect(loadbalance.NewRandomLoadBalance(), invocation.NewRPCInvocation("getUser", nil, nil), invokers, invoked)
+	result1 := base.doSelect(loadbalance.NewRandomLoadBalance(), invocation.NewRPCInvocation("getUser", nil, nil), invokers, invoked)
+	assert.Equal(t, result, result1)
+}
+
+func Test_StickyNormalWhenError(t *testing.T) {
+	invokers := []protocol.Invoker{}
+	for i := 0; i < 10; i++ {
+		url, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i))
+		url.SetParam("sticky", "true")
+		invokers = append(invokers, NewMockInvoker(url, 1))
+	}
+	base := &baseClusterInvoker{}
+	base.availablecheck = true
+
+	invoked := []protocol.Invoker{}
+	result := base.doSelect(loadbalance.NewRandomLoadBalance(), invocation.NewRPCInvocation("getUser", nil, nil), invokers, invoked)
+	invoked = append(invoked, result)
+	result1 := base.doSelect(loadbalance.NewRandomLoadBalance(), invocation.NewRPCInvocation("getUser", nil, nil), invokers, invoked)
+	assert.NotEqual(t, result, result1)
+}
diff --git a/cluster/cluster_impl/broadcast_cluster.go b/cluster/cluster_impl/broadcast_cluster.go
index 50aae3cfab8d67570b50dcab4e53bbfad29d6d30..9b27a4ce37bc73e42b55e4e20deb9593fd837444 100644
--- a/cluster/cluster_impl/broadcast_cluster.go
+++ b/cluster/cluster_impl/broadcast_cluster.go
@@ -31,6 +31,7 @@ func init() {
 	extension.SetCluster(broadcast, NewBroadcastCluster)
 }
 
+// NewBroadcastCluster ...
 func NewBroadcastCluster() cluster.Cluster {
 	return &broadcastCluster{}
 }
diff --git a/cluster/cluster_impl/broadcast_cluster_invoker.go b/cluster/cluster_impl/broadcast_cluster_invoker.go
index 238df0acfa7fb946e38bfbfd490bce7c0bb34e60..1b49e9a115252d4eca94bedd557ebcc21fee4cc7 100644
--- a/cluster/cluster_impl/broadcast_cluster_invoker.go
+++ b/cluster/cluster_impl/broadcast_cluster_invoker.go
@@ -17,6 +17,9 @@ limitations under the License.
 
 package cluster_impl
 
+import (
+	"context"
+)
 import (
 	"github.com/apache/dubbo-go/cluster"
 	"github.com/apache/dubbo-go/common/logger"
@@ -33,7 +36,7 @@ func newBroadcastClusterInvoker(directory cluster.Directory) protocol.Invoker {
 	}
 }
 
-func (invoker *broadcastClusterInvoker) Invoke(invocation protocol.Invocation) protocol.Result {
+func (invoker *broadcastClusterInvoker) Invoke(ctx context.Context, invocation protocol.Invocation) protocol.Result {
 	invokers := invoker.directory.List(invocation)
 	err := invoker.checkInvokers(invokers, invocation)
 	if err != nil {
@@ -46,7 +49,7 @@ func (invoker *broadcastClusterInvoker) Invoke(invocation protocol.Invocation) p
 
 	var result protocol.Result
 	for _, ivk := range invokers {
-		result = ivk.Invoke(invocation)
+		result = ivk.Invoke(ctx, invocation)
 		if result.Error() != nil {
 			logger.Warnf("broadcast invoker invoke err: %v when use invoker: %v\n", result.Error(), ivk)
 			err = result.Error()
diff --git a/cluster/cluster_impl/broadcast_cluster_invoker_test.go b/cluster/cluster_impl/broadcast_cluster_invoker_test.go
index 565684a8ae25c648ff77aef71d2ced0665202fe7..1de5270265a79b4d1d62317730bd06927d939acd 100644
--- a/cluster/cluster_impl/broadcast_cluster_invoker_test.go
+++ b/cluster/cluster_impl/broadcast_cluster_invoker_test.go
@@ -39,7 +39,7 @@ import (
 )
 
 var (
-	broadcastUrl, _ = common.NewURL(context.TODO(), "dubbo://192.168.1.1:20000/com.ikurento.user.UserProvider")
+	broadcastUrl, _ = common.NewURL("dubbo://192.168.1.1:20000/com.ikurento.user.UserProvider")
 )
 
 func registerBroadcast(t *testing.T, mockInvokers ...*mock.MockInvoker) protocol.Invoker {
@@ -74,7 +74,7 @@ func Test_BroadcastInvokeSuccess(t *testing.T) {
 
 	clusterInvoker := registerBroadcast(t, invokers...)
 
-	result := clusterInvoker.Invoke(&invocation.RPCInvocation{})
+	result := clusterInvoker.Invoke(context.Background(), &invocation.RPCInvocation{})
 	assert.Equal(t, mockResult, result)
 }
 
@@ -104,6 +104,6 @@ func Test_BroadcastInvokeFailed(t *testing.T) {
 
 	clusterInvoker := registerBroadcast(t, invokers...)
 
-	result := clusterInvoker.Invoke(&invocation.RPCInvocation{})
+	result := clusterInvoker.Invoke(context.Background(), &invocation.RPCInvocation{})
 	assert.Equal(t, mockFailedResult.Err, result.Error())
 }
diff --git a/cluster/cluster_impl/failback_cluster.go b/cluster/cluster_impl/failback_cluster.go
index de22c78e947d0b8124add721ab7ff42efebcdbe4..76573571684c07f63609009f59ab0ac881ae1b50 100644
--- a/cluster/cluster_impl/failback_cluster.go
+++ b/cluster/cluster_impl/failback_cluster.go
@@ -31,6 +31,7 @@ func init() {
 	extension.SetCluster(failback, NewFailbackCluster)
 }
 
+// NewFailbackCluster ...
 func NewFailbackCluster() cluster.Cluster {
 	return &failbackCluster{}
 }
diff --git a/cluster/cluster_impl/failback_cluster_invoker.go b/cluster/cluster_impl/failback_cluster_invoker.go
index c8dbeda09f62e88b51dd4ad2b6b09d5715f0b224..46b0ff634e56c45223a5aeb5566b9b1401518960 100644
--- a/cluster/cluster_impl/failback_cluster_invoker.go
+++ b/cluster/cluster_impl/failback_cluster_invoker.go
@@ -18,6 +18,7 @@
 package cluster_impl
 
 import (
+	"context"
 	"strconv"
 	"sync"
 	"time"
@@ -71,7 +72,7 @@ func newFailbackClusterInvoker(directory cluster.Directory) protocol.Invoker {
 	return invoker
 }
 
-func (invoker *failbackClusterInvoker) process() {
+func (invoker *failbackClusterInvoker) process(ctx context.Context) {
 	invoker.ticker = time.NewTicker(time.Second * 1)
 	for range invoker.ticker.C {
 		// check each timeout task and re-run
@@ -102,7 +103,7 @@ func (invoker *failbackClusterInvoker) process() {
 
 				retryInvoker := invoker.doSelect(retryTask.loadbalance, retryTask.invocation, retryTask.invokers, invoked)
 				var result protocol.Result
-				result = retryInvoker.Invoke(retryTask.invocation)
+				result = retryInvoker.Invoke(ctx, retryTask.invocation)
 				if result.Error() != nil {
 					retryTask.lastInvoker = retryInvoker
 					invoker.checkRetry(retryTask, result.Error())
@@ -126,7 +127,7 @@ func (invoker *failbackClusterInvoker) checkRetry(retryTask *retryTimerTask, err
 	}
 }
 
-func (invoker *failbackClusterInvoker) Invoke(invocation protocol.Invocation) protocol.Result {
+func (invoker *failbackClusterInvoker) Invoke(ctx context.Context, invocation protocol.Invocation) protocol.Result {
 	invokers := invoker.directory.List(invocation)
 	err := invoker.checkInvokers(invokers, invocation)
 	if err != nil {
@@ -150,11 +151,11 @@ func (invoker *failbackClusterInvoker) Invoke(invocation protocol.Invocation) pr
 
 	ivk := invoker.doSelect(loadbalance, invocation, invokers, invoked)
 	//DO INVOKE
-	result = ivk.Invoke(invocation)
+	result = ivk.Invoke(ctx, invocation)
 	if result.Error() != nil {
 		invoker.once.Do(func() {
 			invoker.taskList = queue.New(invoker.failbackTasks)
-			go invoker.process()
+			go invoker.process(ctx)
 		})
 
 		taskLen := invoker.taskList.Len()
diff --git a/cluster/cluster_impl/failback_cluster_test.go b/cluster/cluster_impl/failback_cluster_test.go
index c94347a1251a69a10c0a4d50007ef569bd6dd996..4571fccec59a2f995009f57c35b56ec5e1cb3ea6 100644
--- a/cluster/cluster_impl/failback_cluster_test.go
+++ b/cluster/cluster_impl/failback_cluster_test.go
@@ -41,7 +41,7 @@ import (
 )
 
 var (
-	failbackUrl, _ = common.NewURL(context.TODO(), "dubbo://192.168.1.1:20000/com.ikurento.user.UserProvider")
+	failbackUrl, _ = common.NewURL("dubbo://192.168.1.1:20000/com.ikurento.user.UserProvider")
 )
 
 // registerFailback register failbackCluster to cluster extension.
@@ -67,12 +67,12 @@ func Test_FailbackSuceess(t *testing.T) {
 	invoker := mock.NewMockInvoker(ctrl)
 	clusterInvoker := registerFailback(t, invoker).(*failbackClusterInvoker)
 
-	invoker.EXPECT().GetUrl().Return(failbackUrl).Times(1)
+	invoker.EXPECT().GetUrl().Return(failbackUrl).AnyTimes()
 
 	mockResult := &protocol.RPCResult{Rest: rest{tried: 0, success: true}}
 	invoker.EXPECT().Invoke(gomock.Any()).Return(mockResult)
 
-	result := clusterInvoker.Invoke(&invocation.RPCInvocation{})
+	result := clusterInvoker.Invoke(context.Background(), &invocation.RPCInvocation{})
 	assert.Equal(t, mockResult, result)
 }
 
@@ -102,7 +102,7 @@ func Test_FailbackRetryOneSuccess(t *testing.T) {
 		return mockSuccResult
 	})
 
-	result := clusterInvoker.Invoke(&invocation.RPCInvocation{})
+	result := clusterInvoker.Invoke(context.Background(), &invocation.RPCInvocation{})
 	assert.Nil(t, result.Error())
 	assert.Nil(t, result.Result())
 	assert.Equal(t, 0, len(result.Attachments()))
@@ -150,7 +150,7 @@ func Test_FailbackRetryFailed(t *testing.T) {
 	}
 
 	// first call should failed.
-	result := clusterInvoker.Invoke(&invocation.RPCInvocation{})
+	result := clusterInvoker.Invoke(context.Background(), &invocation.RPCInvocation{})
 	assert.Nil(t, result.Error())
 	assert.Nil(t, result.Result())
 	assert.Equal(t, 0, len(result.Attachments()))
@@ -192,7 +192,7 @@ func Test_FailbackRetryFailed10Times(t *testing.T) {
 	}).Times(10)
 
 	for i := 0; i < 10; i++ {
-		result := clusterInvoker.Invoke(&invocation.RPCInvocation{})
+		result := clusterInvoker.Invoke(context.Background(), &invocation.RPCInvocation{})
 		assert.Nil(t, result.Error())
 		assert.Nil(t, result.Result())
 		assert.Equal(t, 0, len(result.Attachments()))
@@ -222,14 +222,14 @@ func Test_FailbackOutOfLimit(t *testing.T) {
 	invoker.EXPECT().Invoke(gomock.Any()).Return(mockFailedResult).Times(11)
 
 	// reached limit
-	result := clusterInvoker.Invoke(&invocation.RPCInvocation{})
+	result := clusterInvoker.Invoke(context.Background(), &invocation.RPCInvocation{})
 	assert.Nil(t, result.Error())
 	assert.Nil(t, result.Result())
 	assert.Equal(t, 0, len(result.Attachments()))
 
 	// all will be out of limit
 	for i := 0; i < 10; i++ {
-		result := clusterInvoker.Invoke(&invocation.RPCInvocation{})
+		result := clusterInvoker.Invoke(context.Background(), &invocation.RPCInvocation{})
 		assert.Nil(t, result.Error())
 		assert.Nil(t, result.Result())
 		assert.Equal(t, 0, len(result.Attachments()))
diff --git a/cluster/cluster_impl/failfast_cluster.go b/cluster/cluster_impl/failfast_cluster.go
index 6301d945626103226132b433dd21e8647f53a38b..e0b80ded041cd30b379857ff00d307811e53765d 100644
--- a/cluster/cluster_impl/failfast_cluster.go
+++ b/cluster/cluster_impl/failfast_cluster.go
@@ -31,6 +31,7 @@ func init() {
 	extension.SetCluster(failfast, NewFailFastCluster)
 }
 
+// NewFailFastCluster ...
 func NewFailFastCluster() cluster.Cluster {
 	return &failfastCluster{}
 }
diff --git a/cluster/cluster_impl/failfast_cluster_invoker.go b/cluster/cluster_impl/failfast_cluster_invoker.go
index 734ea2c6cb19bf54a338a76a10c9cfcc59d3954b..49e7c7689f5a19a36154e092a6a83cc39da604ba 100644
--- a/cluster/cluster_impl/failfast_cluster_invoker.go
+++ b/cluster/cluster_impl/failfast_cluster_invoker.go
@@ -17,6 +17,9 @@ limitations under the License.
 
 package cluster_impl
 
+import (
+	"context"
+)
 import (
 	"github.com/apache/dubbo-go/cluster"
 	"github.com/apache/dubbo-go/protocol"
@@ -32,7 +35,7 @@ func newFailFastClusterInvoker(directory cluster.Directory) protocol.Invoker {
 	}
 }
 
-func (invoker *failfastClusterInvoker) Invoke(invocation protocol.Invocation) protocol.Result {
+func (invoker *failfastClusterInvoker) Invoke(ctx context.Context, invocation protocol.Invocation) protocol.Result {
 	invokers := invoker.directory.List(invocation)
 	err := invoker.checkInvokers(invokers, invocation)
 	if err != nil {
@@ -47,5 +50,5 @@ func (invoker *failfastClusterInvoker) Invoke(invocation protocol.Invocation) pr
 	}
 
 	ivk := invoker.doSelect(loadbalance, invocation, invokers, nil)
-	return ivk.Invoke(invocation)
+	return ivk.Invoke(ctx, invocation)
 }
diff --git a/cluster/cluster_impl/failfast_cluster_test.go b/cluster/cluster_impl/failfast_cluster_test.go
index 7a19e80ccda15aa13a1c4fcf250e05a6effa7f0b..38e258199e05c396e22118337bbdb3ae2b955bd0 100644
--- a/cluster/cluster_impl/failfast_cluster_test.go
+++ b/cluster/cluster_impl/failfast_cluster_test.go
@@ -39,7 +39,7 @@ import (
 )
 
 var (
-	failfastUrl, _ = common.NewURL(context.TODO(), "dubbo://192.168.1.1:20000/com.ikurento.user.UserProvider")
+	failfastUrl, _ = common.NewURL("dubbo://192.168.1.1:20000/com.ikurento.user.UserProvider")
 )
 
 // registerFailfast register failfastCluster to cluster extension.
@@ -64,12 +64,12 @@ func Test_FailfastInvokeSuccess(t *testing.T) {
 	invoker := mock.NewMockInvoker(ctrl)
 	clusterInvoker := registerFailfast(t, invoker)
 
-	invoker.EXPECT().GetUrl().Return(failfastUrl)
+	invoker.EXPECT().GetUrl().Return(failfastUrl).AnyTimes()
 
 	mockResult := &protocol.RPCResult{Rest: rest{tried: 0, success: true}}
 
 	invoker.EXPECT().Invoke(gomock.Any()).Return(mockResult)
-	result := clusterInvoker.Invoke(&invocation.RPCInvocation{})
+	result := clusterInvoker.Invoke(context.Background(), &invocation.RPCInvocation{})
 
 	assert.NoError(t, result.Error())
 	res := result.Result().(rest)
@@ -84,12 +84,12 @@ func Test_FailfastInvokeFail(t *testing.T) {
 	invoker := mock.NewMockInvoker(ctrl)
 	clusterInvoker := registerFailfast(t, invoker)
 
-	invoker.EXPECT().GetUrl().Return(failfastUrl)
+	invoker.EXPECT().GetUrl().Return(failfastUrl).AnyTimes()
 
 	mockResult := &protocol.RPCResult{Err: perrors.New("error")}
 
 	invoker.EXPECT().Invoke(gomock.Any()).Return(mockResult)
-	result := clusterInvoker.Invoke(&invocation.RPCInvocation{})
+	result := clusterInvoker.Invoke(context.Background(), &invocation.RPCInvocation{})
 
 	assert.NotNil(t, result.Error())
 	assert.Equal(t, "error", result.Error().Error())
diff --git a/cluster/cluster_impl/failover_cluster.go b/cluster/cluster_impl/failover_cluster.go
index 0f1aa0371f57df5414a04a59e2a6772a4cd382b3..b16be3bafd43c7de8e2fadd109a73a3ea710e225 100644
--- a/cluster/cluster_impl/failover_cluster.go
+++ b/cluster/cluster_impl/failover_cluster.go
@@ -31,6 +31,7 @@ func init() {
 	extension.SetCluster(name, NewFailoverCluster)
 }
 
+// NewFailoverCluster ...
 func NewFailoverCluster() cluster.Cluster {
 	return &failoverCluster{}
 }
diff --git a/cluster/cluster_impl/failover_cluster_invoker.go b/cluster/cluster_impl/failover_cluster_invoker.go
index dcce7369931a11f31fb6b9e4e1a6c0aa0ec7cdf6..6178a05a1226ba629d2456ad6886b02a26288e45 100644
--- a/cluster/cluster_impl/failover_cluster_invoker.go
+++ b/cluster/cluster_impl/failover_cluster_invoker.go
@@ -18,6 +18,7 @@
 package cluster_impl
 
 import (
+	"context"
 	"strconv"
 )
 
@@ -43,7 +44,7 @@ func newFailoverClusterInvoker(directory cluster.Directory) protocol.Invoker {
 	}
 }
 
-func (invoker *failoverClusterInvoker) Invoke(invocation protocol.Invocation) protocol.Result {
+func (invoker *failoverClusterInvoker) Invoke(ctx context.Context, invocation protocol.Invocation) protocol.Result {
 
 	invokers := invoker.directory.List(invocation)
 	err := invoker.checkInvokers(invokers, invocation)
@@ -95,7 +96,7 @@ func (invoker *failoverClusterInvoker) Invoke(invocation protocol.Invocation) pr
 		}
 		invoked = append(invoked, ivk)
 		//DO INVOKE
-		result = ivk.Invoke(invocation)
+		result = ivk.Invoke(ctx, invocation)
 		if result.Error() != nil {
 			providers = append(providers, ivk.GetUrl().Key())
 			continue
diff --git a/cluster/cluster_impl/failover_cluster_test.go b/cluster/cluster_impl/failover_cluster_test.go
index 78b799320dfa58d55e531c658ec5eb0e69306cff..1be21067a6a9045cb6ae6f84655d516fea1f844b 100644
--- a/cluster/cluster_impl/failover_cluster_test.go
+++ b/cluster/cluster_impl/failover_cluster_test.go
@@ -39,9 +39,9 @@ import (
 	"github.com/apache/dubbo-go/protocol/invocation"
 )
 
-/////////////////////////////
+// ///////////////////////////
 // mock invoker
-/////////////////////////////
+// ///////////////////////////
 
 type MockInvoker struct {
 	url       common.URL
@@ -77,10 +77,12 @@ type rest struct {
 	success bool
 }
 
-func (bi *MockInvoker) Invoke(invocation protocol.Invocation) protocol.Result {
+func (bi *MockInvoker) Invoke(c context.Context, invocation protocol.Invocation) protocol.Result {
 	count++
-	var success bool
-	var err error = nil
+	var (
+		success bool
+		err     error
+	)
 	if count >= bi.successCount {
 		success = true
 	} else {
@@ -105,17 +107,18 @@ func normalInvoke(t *testing.T, successCount int, urlParam url.Values, invocatio
 
 	invokers := []protocol.Invoker{}
 	for i := 0; i < 10; i++ {
-		url, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i), common.WithParams(urlParam))
+		url, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i), common.WithParams(urlParam))
 		invokers = append(invokers, NewMockInvoker(url, successCount))
 	}
 
 	staticDir := directory.NewStaticDirectory(invokers)
 	clusterInvoker := failoverCluster.Join(staticDir)
 	if len(invocations) > 0 {
-		return clusterInvoker.Invoke(invocations[0])
+		return clusterInvoker.Invoke(context.Background(), invocations[0])
 	}
-	return clusterInvoker.Invoke(&invocation.RPCInvocation{})
+	return clusterInvoker.Invoke(context.Background(), &invocation.RPCInvocation{})
 }
+
 func Test_FailoverInvokeSuccess(t *testing.T) {
 	urlParams := url.Values{}
 	result := normalInvoke(t, 3, urlParams)
@@ -155,14 +158,14 @@ func Test_FailoverDestroy(t *testing.T) {
 
 	invokers := []protocol.Invoker{}
 	for i := 0; i < 10; i++ {
-		url, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i))
+		url, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i))
 		invokers = append(invokers, NewMockInvoker(url, 1))
 	}
 
 	staticDir := directory.NewStaticDirectory(invokers)
 	clusterInvoker := failoverCluster.Join(staticDir)
 	assert.Equal(t, true, clusterInvoker.IsAvailable())
-	result := clusterInvoker.Invoke(&invocation.RPCInvocation{})
+	result := clusterInvoker.Invoke(context.Background(), &invocation.RPCInvocation{})
 	assert.NoError(t, result.Error())
 	count = 0
 	clusterInvoker.Destroy()
diff --git a/cluster/cluster_impl/failsafe_cluster.go b/cluster/cluster_impl/failsafe_cluster.go
index 3ff97d25eae80980a90a03e71865bb8f9a63defe..177d24a585b5f72fb0667215beb8d11147cc2922 100644
--- a/cluster/cluster_impl/failsafe_cluster.go
+++ b/cluster/cluster_impl/failsafe_cluster.go
@@ -31,6 +31,7 @@ func init() {
 	extension.SetCluster(failsafe, NewFailsafeCluster)
 }
 
+// NewFailsafeCluster ...
 func NewFailsafeCluster() cluster.Cluster {
 	return &failsafeCluster{}
 }
diff --git a/cluster/cluster_impl/failsafe_cluster_invoker.go b/cluster/cluster_impl/failsafe_cluster_invoker.go
index b95f997fef87cf466f07c4e506e41758e7998e52..4d8fe27719eb71fa287fe4142d8e92ca17acfba4 100644
--- a/cluster/cluster_impl/failsafe_cluster_invoker.go
+++ b/cluster/cluster_impl/failsafe_cluster_invoker.go
@@ -17,6 +17,9 @@
 
 package cluster_impl
 
+import (
+	"context"
+)
 import (
 	"github.com/apache/dubbo-go/cluster"
 	"github.com/apache/dubbo-go/common/constant"
@@ -42,7 +45,7 @@ func newFailsafeClusterInvoker(directory cluster.Directory) protocol.Invoker {
 	}
 }
 
-func (invoker *failsafeClusterInvoker) Invoke(invocation protocol.Invocation) protocol.Result {
+func (invoker *failsafeClusterInvoker) Invoke(ctx context.Context, invocation protocol.Invocation) protocol.Result {
 	invokers := invoker.directory.List(invocation)
 
 	err := invoker.checkInvokers(invokers, invocation)
@@ -65,7 +68,7 @@ func (invoker *failsafeClusterInvoker) Invoke(invocation protocol.Invocation) pr
 
 	ivk := invoker.doSelect(loadbalance, invocation, invokers, invoked)
 	//DO INVOKE
-	result = ivk.Invoke(invocation)
+	result = ivk.Invoke(ctx, invocation)
 	if result.Error() != nil {
 		// ignore
 		logger.Errorf("Failsafe ignore exception: %v.\n", result.Error().Error())
diff --git a/cluster/cluster_impl/failsafe_cluster_test.go b/cluster/cluster_impl/failsafe_cluster_test.go
index 9ee9d9fee31b0cb24d877ab3dc0e24fb552f5f11..2e35de8da91cc78730b6380bf039f0626ca75ec0 100644
--- a/cluster/cluster_impl/failsafe_cluster_test.go
+++ b/cluster/cluster_impl/failsafe_cluster_test.go
@@ -39,11 +39,11 @@ import (
 )
 
 var (
-	failsafeUrl, _ = common.NewURL(context.TODO(), "dubbo://192.168.1.1:20000/com.ikurento.user.UserProvider")
+	failsafeUrl, _ = common.NewURL("dubbo://192.168.1.1:20000/com.ikurento.user.UserProvider")
 )
 
-// register_failsafe register failsafeCluster to cluster extension.
-func register_failsafe(t *testing.T, invoker *mock.MockInvoker) protocol.Invoker {
+// registerFailsafe register failsafeCluster to cluster extension.
+func registerFailsafe(t *testing.T, invoker *mock.MockInvoker) protocol.Invoker {
 	extension.SetLoadbalance("random", loadbalance.NewRandomLoadBalance)
 	failsafeCluster := NewFailsafeCluster()
 
@@ -62,14 +62,14 @@ func Test_FailSafeInvokeSuccess(t *testing.T) {
 	defer ctrl.Finish()
 
 	invoker := mock.NewMockInvoker(ctrl)
-	clusterInvoker := register_failsafe(t, invoker)
+	clusterInvoker := registerFailsafe(t, invoker)
 
-	invoker.EXPECT().GetUrl().Return(failsafeUrl)
+	invoker.EXPECT().GetUrl().Return(failsafeUrl).AnyTimes()
 
 	mockResult := &protocol.RPCResult{Rest: rest{tried: 0, success: true}}
 
 	invoker.EXPECT().Invoke(gomock.Any()).Return(mockResult)
-	result := clusterInvoker.Invoke(&invocation.RPCInvocation{})
+	result := clusterInvoker.Invoke(context.Background(), &invocation.RPCInvocation{})
 
 	assert.NoError(t, result.Error())
 	res := result.Result().(rest)
@@ -81,14 +81,14 @@ func Test_FailSafeInvokeFail(t *testing.T) {
 	defer ctrl.Finish()
 
 	invoker := mock.NewMockInvoker(ctrl)
-	clusterInvoker := register_failsafe(t, invoker)
+	clusterInvoker := registerFailsafe(t, invoker)
 
-	invoker.EXPECT().GetUrl().Return(failsafeUrl)
+	invoker.EXPECT().GetUrl().Return(failsafeUrl).AnyTimes()
 
 	mockResult := &protocol.RPCResult{Err: perrors.New("error")}
 
 	invoker.EXPECT().Invoke(gomock.Any()).Return(mockResult)
-	result := clusterInvoker.Invoke(&invocation.RPCInvocation{})
+	result := clusterInvoker.Invoke(context.Background(), &invocation.RPCInvocation{})
 
 	assert.NoError(t, result.Error())
 	assert.Nil(t, result.Result())
diff --git a/cluster/cluster_impl/forking_cluster.go b/cluster/cluster_impl/forking_cluster.go
index 0a3c2b313ff3c4e89e592af9256fc42713419914..6b0572b15088e86870b3d9fd911a1d0b022378be 100644
--- a/cluster/cluster_impl/forking_cluster.go
+++ b/cluster/cluster_impl/forking_cluster.go
@@ -31,6 +31,7 @@ func init() {
 	extension.SetCluster(forking, NewForkingCluster)
 }
 
+// NewForkingCluster ...
 func NewForkingCluster() cluster.Cluster {
 	return &forkingCluster{}
 }
diff --git a/cluster/cluster_impl/forking_cluster_invoker.go b/cluster/cluster_impl/forking_cluster_invoker.go
index d6cf2f4b89ab4f322fa758deecae90c60742ef49..058d7fefd6edf6c43e5eda4b8f2f6a9c161189e2 100644
--- a/cluster/cluster_impl/forking_cluster_invoker.go
+++ b/cluster/cluster_impl/forking_cluster_invoker.go
@@ -18,7 +18,7 @@ limitations under the License.
 package cluster_impl
 
 import (
-	"errors"
+	"context"
 	"fmt"
 	"time"
 )
@@ -44,7 +44,8 @@ func newForkingClusterInvoker(directory cluster.Directory) protocol.Invoker {
 	}
 }
 
-func (invoker *forkingClusterInvoker) Invoke(invocation protocol.Invocation) protocol.Result {
+// Invoke ...
+func (invoker *forkingClusterInvoker) Invoke(ctx context.Context, invocation protocol.Invocation) protocol.Result {
 	err := invoker.checkWhetherDestroyed()
 	if err != nil {
 		return &protocol.RPCResult{Err: err}
@@ -75,7 +76,7 @@ func (invoker *forkingClusterInvoker) Invoke(invocation protocol.Invocation) pro
 	resultQ := queue.New(1)
 	for _, ivk := range selected {
 		go func(k protocol.Invoker) {
-			result := k.Invoke(invocation)
+			result := k.Invoke(ctx, invocation)
 			err := resultQ.Put(result)
 			if err != nil {
 				logger.Errorf("resultQ put failed with exception: %v.\n", err)
@@ -86,14 +87,18 @@ func (invoker *forkingClusterInvoker) Invoke(invocation protocol.Invocation) pro
 	rsps, err := resultQ.Poll(1, time.Millisecond*time.Duration(timeouts))
 	if err != nil {
 		return &protocol.RPCResult{
-			Err: errors.New(fmt.Sprintf("failed to forking invoke provider %v, but no luck to perform the invocation. Last error is: %s", selected, err.Error()))}
+			Err: fmt.Errorf("failed to forking invoke provider %v, "+
+				"but no luck to perform the invocation. Last error is: %v", selected, err),
+		}
 	}
 	if len(rsps) == 0 {
-		return &protocol.RPCResult{Err: errors.New(fmt.Sprintf("failed to forking invoke provider %v, but no resp", selected))}
+		return &protocol.RPCResult{Err: fmt.Errorf("failed to forking invoke provider %v, but no resp", selected)}
 	}
+
 	result, ok := rsps[0].(protocol.Result)
 	if !ok {
-		return &protocol.RPCResult{Err: errors.New(fmt.Sprintf("failed to forking invoke provider %v, but not legal resp", selected))}
+		return &protocol.RPCResult{Err: fmt.Errorf("failed to forking invoke provider %v, but not legal resp", selected)}
 	}
+
 	return result
 }
diff --git a/cluster/cluster_impl/forking_cluster_test.go b/cluster/cluster_impl/forking_cluster_test.go
index 8603f8aedc4e28a3a4ca2f115355debc1a5ecc62..9797ecbd041ae2e09c3d0566f88d08b7246b900f 100644
--- a/cluster/cluster_impl/forking_cluster_test.go
+++ b/cluster/cluster_impl/forking_cluster_test.go
@@ -42,7 +42,7 @@ import (
 )
 
 var (
-	forkingUrl, _ = common.NewURL(context.TODO(), "dubbo://192.168.1.1:20000/com.ikurento.user.UserProvider")
+	forkingUrl, _ = common.NewURL("dubbo://192.168.1.1:20000/com.ikurento.user.UserProvider")
 )
 
 func registerForking(t *testing.T, mockInvokers ...*mock.MockInvoker) protocol.Invoker {
@@ -87,7 +87,7 @@ func Test_ForkingInvokeSuccess(t *testing.T) {
 
 	clusterInvoker := registerForking(t, invokers...)
 
-	result := clusterInvoker.Invoke(&invocation.RPCInvocation{})
+	result := clusterInvoker.Invoke(context.Background(), &invocation.RPCInvocation{})
 	assert.Equal(t, mockResult, result)
 	wg.Wait()
 }
@@ -117,7 +117,7 @@ func Test_ForkingInvokeTimeout(t *testing.T) {
 
 	clusterInvoker := registerForking(t, invokers...)
 
-	result := clusterInvoker.Invoke(&invocation.RPCInvocation{})
+	result := clusterInvoker.Invoke(context.Background(), &invocation.RPCInvocation{})
 	assert.NotNil(t, result)
 	assert.NotNil(t, result.Error())
 	wg.Wait()
@@ -156,7 +156,7 @@ func Test_ForkingInvokeHalfTimeout(t *testing.T) {
 
 	clusterInvoker := registerForking(t, invokers...)
 
-	result := clusterInvoker.Invoke(&invocation.RPCInvocation{})
+	result := clusterInvoker.Invoke(context.Background(), &invocation.RPCInvocation{})
 	assert.Equal(t, mockResult, result)
 	wg.Wait()
 }
diff --git a/cluster/cluster_impl/mock_cluster.go b/cluster/cluster_impl/mock_cluster.go
index 50b2735554b61600fb090f382f3d2920b3d445e3..943c2add68281d01e320252d07b7d58e27b51283 100644
--- a/cluster/cluster_impl/mock_cluster.go
+++ b/cluster/cluster_impl/mock_cluster.go
@@ -24,6 +24,7 @@ import (
 
 type mockCluster struct{}
 
+// NewMockCluster ...
 func NewMockCluster() cluster.Cluster {
 	return &mockCluster{}
 }
diff --git a/cluster/cluster_impl/registry_aware_cluster.go b/cluster/cluster_impl/registry_aware_cluster.go
index f4a28d6dcd5fbab8c62ee1f79bdd8576d8774a4c..079b688da65b3e6f6595212ad6e93c3b6ecc6504 100644
--- a/cluster/cluster_impl/registry_aware_cluster.go
+++ b/cluster/cluster_impl/registry_aware_cluster.go
@@ -29,6 +29,7 @@ func init() {
 	extension.SetCluster("registryAware", NewRegistryAwareCluster)
 }
 
+// NewRegistryAwareCluster ...
 func NewRegistryAwareCluster() cluster.Cluster {
 	return &registryAwareCluster{}
 }
diff --git a/cluster/cluster_impl/registry_aware_cluster_invoker.go b/cluster/cluster_impl/registry_aware_cluster_invoker.go
index 5785c02489f95168d5419f0087f38b07c851a4a3..cded5bf16432e6b0c590e15b81c28369889a5f88 100644
--- a/cluster/cluster_impl/registry_aware_cluster_invoker.go
+++ b/cluster/cluster_impl/registry_aware_cluster_invoker.go
@@ -17,6 +17,9 @@
 
 package cluster_impl
 
+import (
+	"context"
+)
 import (
 	"github.com/apache/dubbo-go/cluster"
 	"github.com/apache/dubbo-go/common/constant"
@@ -33,19 +36,19 @@ func newRegistryAwareClusterInvoker(directory cluster.Directory) protocol.Invoke
 	}
 }
 
-func (invoker *registryAwareClusterInvoker) Invoke(invocation protocol.Invocation) protocol.Result {
+func (invoker *registryAwareClusterInvoker) Invoke(ctx context.Context, invocation protocol.Invocation) protocol.Result {
 	invokers := invoker.directory.List(invocation)
 	//First, pick the invoker (XXXClusterInvoker) that comes from the local registry, distinguish by a 'default' key.
 	for _, invoker := range invokers {
 		if invoker.IsAvailable() && invoker.GetUrl().GetParam(constant.REGISTRY_DEFAULT_KEY, "false") == "true" {
-			return invoker.Invoke(invocation)
+			return invoker.Invoke(ctx, invocation)
 		}
 	}
 
 	//If none of the invokers has a local signal, pick the first one available.
 	for _, invoker := range invokers {
 		if invoker.IsAvailable() {
-			return invoker.Invoke(invocation)
+			return invoker.Invoke(ctx, invocation)
 		}
 	}
 	return nil
diff --git a/cluster/cluster_impl/registry_aware_cluster_test.go b/cluster/cluster_impl/registry_aware_cluster_test.go
index 4ae15cc5066c70646dee66cf4ef601202653cb07..3d0dcc0159839eb0a08aed842ee084449458c645 100644
--- a/cluster/cluster_impl/registry_aware_cluster_test.go
+++ b/cluster/cluster_impl/registry_aware_cluster_test.go
@@ -39,13 +39,13 @@ func Test_RegAwareInvokeSuccess(t *testing.T) {
 
 	invokers := []protocol.Invoker{}
 	for i := 0; i < 10; i++ {
-		url, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i))
+		url, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i))
 		invokers = append(invokers, NewMockInvoker(url, 1))
 	}
 
 	staticDir := directory.NewStaticDirectory(invokers)
 	clusterInvoker := regAwareCluster.Join(staticDir)
-	result := clusterInvoker.Invoke(&invocation.RPCInvocation{})
+	result := clusterInvoker.Invoke(context.Background(), &invocation.RPCInvocation{})
 	assert.NoError(t, result.Error())
 	count = 0
 }
@@ -55,14 +55,14 @@ func TestDestroy(t *testing.T) {
 
 	invokers := []protocol.Invoker{}
 	for i := 0; i < 10; i++ {
-		url, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i))
+		url, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i))
 		invokers = append(invokers, NewMockInvoker(url, 1))
 	}
 
 	staticDir := directory.NewStaticDirectory(invokers)
 	clusterInvoker := regAwareCluster.Join(staticDir)
 	assert.Equal(t, true, clusterInvoker.IsAvailable())
-	result := clusterInvoker.Invoke(&invocation.RPCInvocation{})
+	result := clusterInvoker.Invoke(context.Background(), &invocation.RPCInvocation{})
 	assert.NoError(t, result.Error())
 	count = 0
 	clusterInvoker.Destroy()
diff --git a/cluster/directory.go b/cluster/directory.go
index 045296ce549b13c327b6f479ca0bd75d0b6ce131..5a03b3a4490ce0b3aadece8a9ef43395f845dd12 100644
--- a/cluster/directory.go
+++ b/cluster/directory.go
@@ -22,7 +22,8 @@ import (
 	"github.com/apache/dubbo-go/protocol"
 )
 
-// Extension - Directory
+// Directory
+//Extension - Directory
 type Directory interface {
 	common.Node
 	List(invocation protocol.Invocation) []protocol.Invoker
diff --git a/cluster/directory/base_directory.go b/cluster/directory/base_directory.go
index 1d59b51cc36858b80fb43c1d76e368e89e26ae36..75d9ef26567df0fbd83f5d9f94c8548d1e8e633d 100644
--- a/cluster/directory/base_directory.go
+++ b/cluster/directory/base_directory.go
@@ -20,32 +20,94 @@ package directory
 import (
 	"sync"
 )
+
 import (
+	"github.com/dubbogo/gost/container/set"
 	"go.uber.org/atomic"
 )
+
 import (
+	"github.com/apache/dubbo-go/cluster/router"
+	"github.com/apache/dubbo-go/cluster/router/chain"
 	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/common/constant"
+	"github.com/apache/dubbo-go/common/extension"
+	"github.com/apache/dubbo-go/common/logger"
 )
 
+var routerURLSet = gxset.NewSet()
+
+// BaseDirectory Abstract implementation of Directory: Invoker list returned from this Directory's list method have been filtered by Routers
 type BaseDirectory struct {
 	url       *common.URL
 	destroyed *atomic.Bool
-	mutex     sync.Mutex
+	// this mutex for change the properties in BaseDirectory, like routerChain , destroyed etc
+	mutex       sync.Mutex
+	routerChain router.Chain
 }
 
+// NewBaseDirectory Create BaseDirectory with URL
 func NewBaseDirectory(url *common.URL) BaseDirectory {
 	return BaseDirectory{
-		url:       url,
-		destroyed: atomic.NewBool(false),
+		url:         url,
+		destroyed:   atomic.NewBool(false),
+		routerChain: &chain.RouterChain{},
 	}
 }
+
+// RouterChain Return router chain in directory
+func (dir *BaseDirectory) RouterChain() router.Chain {
+	return dir.routerChain
+}
+
+// SetRouterChain Set router chain in directory
+func (dir *BaseDirectory) SetRouterChain(routerChain router.Chain) {
+	dir.mutex.Lock()
+	defer dir.mutex.Unlock()
+	dir.routerChain = routerChain
+}
+
+// GetUrl Get URL
 func (dir *BaseDirectory) GetUrl() common.URL {
 	return *dir.url
 }
+
+// GetDirectoryUrl Get URL instance
 func (dir *BaseDirectory) GetDirectoryUrl() *common.URL {
 	return dir.url
 }
 
+// SetRouters Convert url to routers and add them into dir.routerChain
+func (dir *BaseDirectory) SetRouters(urls []*common.URL) {
+	if len(urls) == 0 {
+		return
+	}
+
+	routers := make([]router.Router, 0, len(urls))
+
+	for _, url := range urls {
+		routerKey := url.GetParam(constant.ROUTER_KEY, "")
+
+		if len(routerKey) > 0 {
+			factory := extension.GetRouterFactory(url.Protocol)
+			r, err := factory.NewRouter(url)
+			if err != nil {
+				logger.Errorf("Create router fail. router key: %s, error: %v", routerKey, url.Service(), err)
+				return
+			}
+			routers = append(routers, r)
+		}
+	}
+
+	logger.Infof("Init file condition router success, size: %v", len(routers))
+	dir.mutex.Lock()
+	rc := dir.routerChain
+	dir.mutex.Unlock()
+
+	rc.AddRouters(routers)
+}
+
+// Destroy Destroy
 func (dir *BaseDirectory) Destroy(doDestroy func()) {
 	if dir.destroyed.CAS(false, true) {
 		dir.mutex.Lock()
@@ -54,6 +116,18 @@ func (dir *BaseDirectory) Destroy(doDestroy func()) {
 	}
 }
 
+// IsAvailable Once directory init finish, it will change to true
 func (dir *BaseDirectory) IsAvailable() bool {
 	return !dir.destroyed.Load()
 }
+
+// GetRouterURLSet Return router URL
+func GetRouterURLSet() *gxset.HashSet {
+	return routerURLSet
+}
+
+// AddRouterURLSet Add router URL
+// Router URL will init in config/config_loader.go
+func AddRouterURLSet(url *common.URL) {
+	routerURLSet.Add(url)
+}
diff --git a/cluster/directory/base_directory_test.go b/cluster/directory/base_directory_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..d5993959f1d37f343a612e2bee305461d49535d0
--- /dev/null
+++ b/cluster/directory/base_directory_test.go
@@ -0,0 +1,71 @@
+/*
+ * 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 directory
+
+import (
+	"encoding/base64"
+	"fmt"
+	"testing"
+)
+
+import (
+	gxnet "github.com/dubbogo/gost/net"
+	"github.com/stretchr/testify/assert"
+)
+
+import (
+	_ "github.com/apache/dubbo-go/cluster/router/condition"
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/common/constant"
+)
+
+func TestNewBaseDirectory(t *testing.T) {
+	url, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.1:20000/com.ikurento.user.UserProvider"))
+	directory := NewBaseDirectory(&url)
+
+	assert.NotNil(t, directory)
+
+	assert.Equal(t, url, directory.GetUrl())
+	assert.Equal(t, &url, directory.GetDirectoryUrl())
+
+}
+
+func TestBuildRouterChain(t *testing.T) {
+	url, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.1:20000/com.ikurento.user.UserProvider"))
+	directory := NewBaseDirectory(&url)
+
+	assert.NotNil(t, directory)
+
+	localIP, _ := gxnet.GetLocalIP()
+	rule := base64.URLEncoding.EncodeToString([]byte("true => " + " host = " + localIP))
+	routeURL := getRouteUrl(rule)
+	routerURLs := make([]*common.URL, 0)
+	routerURLs = append(routerURLs, routeURL)
+	directory.SetRouters(routerURLs)
+	chain := directory.RouterChain()
+
+	assert.NotNil(t, chain)
+}
+
+func getRouteUrl(rule string) *common.URL {
+	url, _ := common.NewURL("condition://0.0.0.0/com.foo.BarService")
+	url.AddParam("rule", rule)
+	url.AddParam("force", "true")
+	url.AddParam(constant.ROUTER_KEY, "router")
+	return &url
+}
diff --git a/cluster/directory/static_directory.go b/cluster/directory/static_directory.go
index e7a0e6e569db620ee83521505c9568199d45fe1e..9f600fedc40cf29a40abca6c11652935f20473b4 100644
--- a/cluster/directory/static_directory.go
+++ b/cluster/directory/static_directory.go
@@ -18,6 +18,11 @@
 package directory
 
 import (
+	perrors "github.com/pkg/errors"
+)
+
+import (
+	"github.com/apache/dubbo-go/cluster/router/chain"
 	"github.com/apache/dubbo-go/common"
 	"github.com/apache/dubbo-go/protocol"
 )
@@ -27,6 +32,7 @@ type staticDirectory struct {
 	invokers []protocol.Invoker
 }
 
+// NewStaticDirectory Create a new staticDirectory with invokers
 func NewStaticDirectory(invokers []protocol.Invoker) *staticDirectory {
 	var url common.URL
 
@@ -52,11 +58,21 @@ func (dir *staticDirectory) IsAvailable() bool {
 	return true
 }
 
+// List List invokers
 func (dir *staticDirectory) List(invocation protocol.Invocation) []protocol.Invoker {
-	//TODO:Here should add router
-	return dir.invokers
+	l := len(dir.invokers)
+	invokers := make([]protocol.Invoker, l, l)
+	copy(invokers, dir.invokers)
+	routerChain := dir.RouterChain()
+
+	if routerChain == nil {
+		return invokers
+	}
+	dirUrl := dir.GetUrl()
+	return routerChain.Route(invokers, &dirUrl, invocation)
 }
 
+// Destroy Destroy
 func (dir *staticDirectory) Destroy() {
 	dir.BaseDirectory.Destroy(func() {
 		for _, ivk := range dir.invokers {
@@ -65,3 +81,17 @@ func (dir *staticDirectory) Destroy() {
 		dir.invokers = []protocol.Invoker{}
 	})
 }
+
+// BuildRouterChain build router chain by invokers
+func (dir *staticDirectory) BuildRouterChain(invokers []protocol.Invoker) error {
+	if len(invokers) == 0 {
+		return perrors.Errorf("invokers == null")
+	}
+	url := invokers[0].GetUrl()
+	routerChain, e := chain.NewRouterChain(&url)
+	if e != nil {
+		return e
+	}
+	dir.SetRouterChain(routerChain)
+	return nil
+}
diff --git a/cluster/directory/static_directory_test.go b/cluster/directory/static_directory_test.go
index 3ce772e8a6287aebef3fdad039e2b12be421c4e3..c50c9a4063bd1a372c27e47687cbf63850f76cef 100644
--- a/cluster/directory/static_directory_test.go
+++ b/cluster/directory/static_directory_test.go
@@ -18,7 +18,6 @@
 package directory
 
 import (
-	"context"
 	"fmt"
 	"testing"
 )
@@ -36,18 +35,20 @@ import (
 func Test_StaticDirList(t *testing.T) {
 	invokers := []protocol.Invoker{}
 	for i := 0; i < 10; i++ {
-		url, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i))
+		url, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i))
 		invokers = append(invokers, protocol.NewBaseInvoker(url))
 	}
 
 	staticDir := NewStaticDirectory(invokers)
-	assert.Len(t, staticDir.List(&invocation.RPCInvocation{}), 10)
+	list := staticDir.List(&invocation.RPCInvocation{})
+
+	assert.Len(t, list, 10)
 }
 
 func Test_StaticDirDestroy(t *testing.T) {
 	invokers := []protocol.Invoker{}
 	for i := 0; i < 10; i++ {
-		url, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i))
+		url, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i))
 		invokers = append(invokers, protocol.NewBaseInvoker(url))
 	}
 
diff --git a/cluster/loadbalance.go b/cluster/loadbalance.go
index 9ae4e4eb808b28581d12b72829c921c4f0cc9ac8..fb3641a77377eabbd692729a32e2c0c096282f18 100644
--- a/cluster/loadbalance.go
+++ b/cluster/loadbalance.go
@@ -21,7 +21,8 @@ import (
 	"github.com/apache/dubbo-go/protocol"
 )
 
-// Extension - LoadBalance
+// LoadBalance
+//Extension - LoadBalance
 type LoadBalance interface {
 	Select([]protocol.Invoker, protocol.Invocation) protocol.Invoker
 }
diff --git a/cluster/loadbalance/consistent_hash.go b/cluster/loadbalance/consistent_hash.go
new file mode 100644
index 0000000000000000000000000000000000000000..957c110663d6c56ada15543d372e210fa83bf74b
--- /dev/null
+++ b/cluster/loadbalance/consistent_hash.go
@@ -0,0 +1,174 @@
+/*
+ * 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 loadbalance
+
+import (
+	"crypto/md5"
+	"encoding/json"
+	"fmt"
+	"hash/crc32"
+	"regexp"
+	"sort"
+	"strconv"
+	"strings"
+)
+
+import (
+	"github.com/apache/dubbo-go/cluster"
+	"github.com/apache/dubbo-go/common/constant"
+	"github.com/apache/dubbo-go/common/extension"
+	"github.com/apache/dubbo-go/protocol"
+)
+
+const (
+	// ConsistentHash ...
+	ConsistentHash = "consistenthash"
+	// HashNodes ...
+	HashNodes = "hash.nodes"
+	// HashArguments ...
+	HashArguments = "hash.arguments"
+)
+
+var (
+	selectors = make(map[string]*ConsistentHashSelector)
+	re        = regexp.MustCompile(constant.COMMA_SPLIT_PATTERN)
+)
+
+func init() {
+	extension.SetLoadbalance(ConsistentHash, NewConsistentHashLoadBalance)
+}
+
+// ConsistentHashLoadBalance ...
+type ConsistentHashLoadBalance struct {
+}
+
+// NewConsistentHashLoadBalance ...
+func NewConsistentHashLoadBalance() cluster.LoadBalance {
+	return &ConsistentHashLoadBalance{}
+}
+
+// Select ...
+func (lb *ConsistentHashLoadBalance) Select(invokers []protocol.Invoker, invocation protocol.Invocation) protocol.Invoker {
+	methodName := invocation.MethodName()
+	key := invokers[0].GetUrl().ServiceKey() + "." + methodName
+
+	// hash the invokers
+	bs := make([]byte, 0)
+	for _, invoker := range invokers {
+		b, err := json.Marshal(invoker)
+		if err != nil {
+			return nil
+		}
+		bs = append(bs, b...)
+	}
+	hashCode := crc32.ChecksumIEEE(bs)
+	selector, ok := selectors[key]
+	if !ok || selector.hashCode != hashCode {
+		selectors[key] = newConsistentHashSelector(invokers, methodName, hashCode)
+		selector = selectors[key]
+	}
+	return selector.Select(invocation)
+}
+
+// Uint32Slice ...
+type Uint32Slice []uint32
+
+func (s Uint32Slice) Len() int {
+	return len(s)
+}
+
+func (s Uint32Slice) Less(i, j int) bool {
+	return s[i] < s[j]
+}
+
+func (s Uint32Slice) Swap(i, j int) {
+	s[i], s[j] = s[j], s[i]
+}
+
+// ConsistentHashSelector ...
+type ConsistentHashSelector struct {
+	hashCode        uint32
+	replicaNum      int
+	virtualInvokers map[uint32]protocol.Invoker
+	keys            Uint32Slice
+	argumentIndex   []int
+}
+
+func newConsistentHashSelector(invokers []protocol.Invoker, methodName string,
+	hashCode uint32) *ConsistentHashSelector {
+
+	selector := &ConsistentHashSelector{}
+	selector.virtualInvokers = make(map[uint32]protocol.Invoker)
+	selector.hashCode = hashCode
+	url := invokers[0].GetUrl()
+	selector.replicaNum = int(url.GetMethodParamInt(methodName, HashNodes, 160))
+	indices := re.Split(url.GetMethodParam(methodName, HashArguments, "0"), -1)
+	for _, index := range indices {
+		i, err := strconv.Atoi(index)
+		if err != nil {
+			return nil
+		}
+		selector.argumentIndex = append(selector.argumentIndex, i)
+	}
+	for _, invoker := range invokers {
+		u := invoker.GetUrl()
+		address := u.Ip + ":" + u.Port
+		for i := 0; i < selector.replicaNum/4; i++ {
+			digest := md5.Sum([]byte(address + strconv.Itoa(i)))
+			for j := 0; j < 4; j++ {
+				key := selector.hash(digest, j)
+				selector.keys = append(selector.keys, key)
+				selector.virtualInvokers[key] = invoker
+			}
+		}
+	}
+	sort.Sort(selector.keys)
+	return selector
+}
+
+// Select ...
+func (c *ConsistentHashSelector) Select(invocation protocol.Invocation) protocol.Invoker {
+	key := c.toKey(invocation.Arguments())
+	digest := md5.Sum([]byte(key))
+	return c.selectForKey(c.hash(digest, 0))
+}
+
+func (c *ConsistentHashSelector) toKey(args []interface{}) string {
+	var sb strings.Builder
+	for i := range c.argumentIndex {
+		if i >= 0 && i < len(args) {
+			fmt.Fprint(&sb, args[i].(string))
+		}
+	}
+	return sb.String()
+}
+
+func (c *ConsistentHashSelector) selectForKey(hash uint32) protocol.Invoker {
+	idx := sort.Search(len(c.keys), func(i int) bool {
+		return c.keys[i] >= hash
+	})
+	if idx == len(c.keys) {
+		idx = 0
+	}
+	return c.virtualInvokers[c.keys[idx]]
+}
+
+func (c *ConsistentHashSelector) hash(digest [16]byte, i int) uint32 {
+	return uint32((digest[3+i*4]&0xFF)<<24) | uint32((digest[2+i*4]&0xFF)<<16) |
+		uint32((digest[1+i*4]&0xFF)<<8) | uint32(digest[i*4]&0xFF)&0xFFFFFFF
+}
diff --git a/cluster/loadbalance/consistent_hash_test.go b/cluster/loadbalance/consistent_hash_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..a44293172c6e2c96bd098a19306f69260b713689
--- /dev/null
+++ b/cluster/loadbalance/consistent_hash_test.go
@@ -0,0 +1,109 @@
+/*
+ * 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 loadbalance
+
+import (
+	"testing"
+)
+
+import (
+	"github.com/stretchr/testify/suite"
+)
+
+import (
+	"github.com/apache/dubbo-go/cluster"
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/protocol"
+	"github.com/apache/dubbo-go/protocol/invocation"
+)
+
+func TestConsistentHashSelectorSuite(t *testing.T) {
+	suite.Run(t, new(consistentHashSelectorSuite))
+}
+
+type consistentHashSelectorSuite struct {
+	suite.Suite
+	selector *ConsistentHashSelector
+}
+
+func (s *consistentHashSelectorSuite) SetupTest() {
+	var invokers []protocol.Invoker
+	url, _ := common.NewURL(
+		"dubbo://192.168.1.0:20000/org.apache.demo.HelloService?methods.echo.hash.arguments=0,1")
+	invokers = append(invokers, protocol.NewBaseInvoker(url))
+	s.selector = newConsistentHashSelector(invokers, "echo", 999944)
+}
+
+func (s *consistentHashSelectorSuite) TestToKey() {
+	result := s.selector.toKey([]interface{}{"username", "age"})
+	s.Equal(result, "usernameage")
+}
+
+func (s *consistentHashSelectorSuite) TestSelectForKey() {
+	url1, _ := common.NewURL("dubbo://192.168.1.0:8080")
+	url2, _ := common.NewURL("dubbo://192.168.1.0:8081")
+	s.selector.virtualInvokers = make(map[uint32]protocol.Invoker)
+	s.selector.virtualInvokers[99874] = protocol.NewBaseInvoker(url1)
+	s.selector.virtualInvokers[9999945] = protocol.NewBaseInvoker(url2)
+	s.selector.keys = []uint32{99874, 9999945}
+	result := s.selector.selectForKey(9999944)
+	s.Equal(result.GetUrl().String(), "dubbo://192.168.1.0:8081?")
+}
+
+func TestConsistentHashLoadBalanceSuite(t *testing.T) {
+	suite.Run(t, new(consistentHashLoadBalanceSuite))
+}
+
+type consistentHashLoadBalanceSuite struct {
+	suite.Suite
+	url1     common.URL
+	url2     common.URL
+	url3     common.URL
+	invokers []protocol.Invoker
+	invoker1 protocol.Invoker
+	invoker2 protocol.Invoker
+	invoker3 protocol.Invoker
+	lb       cluster.LoadBalance
+}
+
+func (s *consistentHashLoadBalanceSuite) SetupTest() {
+	var err error
+	s.url1, err = common.NewURL("dubbo://192.168.1.0:8080/org.apache.demo.HelloService?methods.echo.hash.arguments=0,1")
+	s.NoError(err)
+	s.url2, err = common.NewURL("dubbo://192.168.1.0:8081/org.apache.demo.HelloService?methods.echo.hash.arguments=0,1")
+	s.NoError(err)
+	s.url3, err = common.NewURL("dubbo://192.168.1.0:8082/org.apache.demo.HelloService?methods.echo.hash.arguments=0,1")
+	s.NoError(err)
+
+	s.invoker1 = protocol.NewBaseInvoker(s.url1)
+	s.invoker2 = protocol.NewBaseInvoker(s.url2)
+	s.invoker3 = protocol.NewBaseInvoker(s.url3)
+
+	s.invokers = append(s.invokers, s.invoker1, s.invoker2, s.invoker3)
+	s.lb = NewConsistentHashLoadBalance()
+}
+
+func (s *consistentHashLoadBalanceSuite) TestSelect() {
+	args := []interface{}{"name", "password", "age"}
+	invoker := s.lb.Select(s.invokers, invocation.NewRPCInvocation("echo", args, nil))
+	s.Equal(invoker.GetUrl().Location, "192.168.1.0:8080")
+
+	args = []interface{}{"ok", "abc"}
+	invoker = s.lb.Select(s.invokers, invocation.NewRPCInvocation("echo", args, nil))
+	s.Equal(invoker.GetUrl().Location, "192.168.1.0:8082")
+}
diff --git a/cluster/loadbalance/least_active.go b/cluster/loadbalance/least_active.go
index aa69f3cc207ae7465bc6d5472bc075d0902c8978..e7c41aac93e8d3dfcef5d49fa486483bd045f569 100644
--- a/cluster/loadbalance/least_active.go
+++ b/cluster/loadbalance/least_active.go
@@ -28,6 +28,7 @@ import (
 )
 
 const (
+	// LeastActive ...
 	LeastActive = "leastactive"
 )
 
@@ -38,6 +39,7 @@ func init() {
 type leastActiveLoadBalance struct {
 }
 
+// NewLeastActiveLoadBalance ...
 func NewLeastActiveLoadBalance() cluster.LoadBalance {
 	return &leastActiveLoadBalance{}
 }
@@ -52,18 +54,18 @@ func (lb *leastActiveLoadBalance) Select(invokers []protocol.Invoker, invocation
 	}
 
 	var (
-		leastActive  int32 = -1                 // The least active value of all invokers
-		totalWeight  int64 = 0                  // The number of invokers having the same least active value (LEAST_ACTIVE)
-		firstWeight  int64 = 0                  // Initial value, used for comparison
-		leastIndexes       = make([]int, count) // The index of invokers having the same least active value (LEAST_ACTIVE)
-		leastCount         = 0                  // The number of invokers having the same least active value (LEAST_ACTIVE)
-		sameWeight         = true               // Every invoker has the same weight value?
+		leastActive  int32                = -1 // The least active value of all invokers
+		totalWeight  int64                     // The number of invokers having the same least active value (LEAST_ACTIVE)
+		firstWeight  int64                     // Initial value, used for comparison
+		leastCount   int                       // The number of invokers having the same least active value (LEAST_ACTIVE)
+		leastIndexes = make([]int, count)      // The index of invokers having the same least active value (LEAST_ACTIVE)
+		sameWeight   = true                    // Every invoker has the same weight value?
 	)
 
 	for i := 0; i < count; i++ {
 		invoker := invokers[i]
 		// Active number
-		active := protocol.GetStatus(invoker.GetUrl(), invocation.MethodName()).GetActive()
+		active := protocol.GetMethodStatus(invoker.GetUrl(), invocation.MethodName()).GetActive()
 		// current weight (maybe in warmUp)
 		weight := GetWeight(invoker, invocation)
 		// There are smaller active services
diff --git a/cluster/loadbalance/least_active_test.go b/cluster/loadbalance/least_active_test.go
index 6bc6985678d7c392ad21b49ec341c3550265f622..54e57e930f17008cf6d767ef47c0e754ac85d8f7 100644
--- a/cluster/loadbalance/least_active_test.go
+++ b/cluster/loadbalance/least_active_test.go
@@ -18,7 +18,6 @@
 package loadbalance
 
 import (
-	"context"
 	"fmt"
 	"testing"
 )
@@ -38,13 +37,13 @@ func TestLeastActiveSelect(t *testing.T) {
 
 	var invokers []protocol.Invoker
 
-	url, _ := common.NewURL(context.TODO(), "dubbo://192.168.1.0:20000/org.apache.demo.HelloService")
+	url, _ := common.NewURL("dubbo://192.168.1.0:20000/org.apache.demo.HelloService")
 	invokers = append(invokers, protocol.NewBaseInvoker(url))
 	i := loadBalance.Select(invokers, &invocation.RPCInvocation{})
 	assert.True(t, i.GetUrl().URLEqual(url))
 
 	for i := 1; i < 10; i++ {
-		url, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://192.168.1.%v:20000/org.apache.demo.HelloService", i))
+		url, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.%v:20000/org.apache.demo.HelloService", i))
 		invokers = append(invokers, protocol.NewBaseInvoker(url))
 	}
 	loadBalance.Select(invokers, &invocation.RPCInvocation{})
@@ -56,7 +55,7 @@ func TestLeastActiveByWeight(t *testing.T) {
 	var invokers []protocol.Invoker
 	loop := 3
 	for i := 1; i <= loop; i++ {
-		url, _ := common.NewURL(context.TODO(), fmt.Sprintf("test%v://192.168.1.%v:20000/org.apache.demo.HelloService?weight=%v", i, i, i))
+		url, _ := common.NewURL(fmt.Sprintf("test%v://192.168.1.%v:20000/org.apache.demo.HelloService?weight=%v", i, i, i))
 		invokers = append(invokers, protocol.NewBaseInvoker(url))
 	}
 
diff --git a/cluster/loadbalance/random.go b/cluster/loadbalance/random.go
index 919792162dc527fa8c1e5cf2911f2933fa8232ef..56f13631b653ed070dae7def5bea97d924141209 100644
--- a/cluster/loadbalance/random.go
+++ b/cluster/loadbalance/random.go
@@ -38,6 +38,7 @@ func init() {
 type randomLoadBalance struct {
 }
 
+// NewRandomLoadBalance ...
 func NewRandomLoadBalance() cluster.LoadBalance {
 	return &randomLoadBalance{}
 }
diff --git a/cluster/loadbalance/random_test.go b/cluster/loadbalance/random_test.go
index ffe65d78ac61e5210d23e44c7f802597fed78f96..ff876f4aef8d229e8041594aaaa096f3ad5b1834 100644
--- a/cluster/loadbalance/random_test.go
+++ b/cluster/loadbalance/random_test.go
@@ -18,7 +18,6 @@
 package loadbalance
 
 import (
-	"context"
 	"fmt"
 	"net/url"
 	"strconv"
@@ -42,13 +41,13 @@ func Test_RandomlbSelect(t *testing.T) {
 
 	invokers := []protocol.Invoker{}
 
-	url, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", 0))
+	url, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", 0))
 	invokers = append(invokers, protocol.NewBaseInvoker(url))
 	i := randomlb.Select(invokers, &invocation.RPCInvocation{})
 	assert.True(t, i.GetUrl().URLEqual(url))
 
 	for i := 1; i < 10; i++ {
-		url, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i))
+		url, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i))
 		invokers = append(invokers, protocol.NewBaseInvoker(url))
 	}
 	randomlb.Select(invokers, &invocation.RPCInvocation{})
@@ -59,13 +58,13 @@ func Test_RandomlbSelectWeight(t *testing.T) {
 
 	invokers := []protocol.Invoker{}
 	for i := 0; i < 10; i++ {
-		url, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i))
+		url, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i))
 		invokers = append(invokers, protocol.NewBaseInvoker(url))
 	}
 
 	urlParams := url.Values{}
 	urlParams.Set("methods.test."+constant.WEIGHT_KEY, "10000000000000")
-	urll, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://192.168.1.100:20000/com.ikurento.user.UserProvider"), common.WithParams(urlParams))
+	urll, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.100:20000/com.ikurento.user.UserProvider"), common.WithParams(urlParams))
 	invokers = append(invokers, protocol.NewBaseInvoker(urll))
 	ivc := invocation.NewRPCInvocationWithOptions(invocation.WithMethodName("test"))
 
@@ -80,7 +79,7 @@ func Test_RandomlbSelectWeight(t *testing.T) {
 	}
 
 	assert.Condition(t, func() bool {
-		//really is 0.9999999999999
+		// really is 0.9999999999999
 		return selected/10000 > 0.9
 	})
 }
@@ -90,13 +89,13 @@ func Test_RandomlbSelectWarmup(t *testing.T) {
 
 	invokers := []protocol.Invoker{}
 	for i := 0; i < 10; i++ {
-		url, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i))
+		url, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i))
 		invokers = append(invokers, protocol.NewBaseInvoker(url))
 	}
 
 	urlParams := url.Values{}
 	urlParams.Set(constant.REMOTE_TIMESTAMP_KEY, strconv.FormatInt(time.Now().Add(time.Minute*(-9)).Unix(), 10))
-	urll, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://192.168.1.100:20000/com.ikurento.user.UserProvider"), common.WithParams(urlParams))
+	urll, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.100:20000/com.ikurento.user.UserProvider"), common.WithParams(urlParams))
 	invokers = append(invokers, protocol.NewBaseInvoker(urll))
 	ivc := invocation.NewRPCInvocationWithOptions(invocation.WithMethodName("test"))
 
diff --git a/cluster/loadbalance/round_robin.go b/cluster/loadbalance/round_robin.go
index 075acac7cdc60086ececb7b655dee86ec5198369..4d039999677aefb1093071666a845279dc357ce9 100644
--- a/cluster/loadbalance/round_robin.go
+++ b/cluster/loadbalance/round_robin.go
@@ -31,16 +31,19 @@ import (
 )
 
 const (
+	// RoundRobin ...
 	RoundRobin = "roundrobin"
 
+	// COMPLETE ...
 	COMPLETE = 0
+	// UPDATING ...
 	UPDATING = 1
 )
 
 var (
-	methodWeightMap sync.Map            // [string]invokers
-	state           int32    = COMPLETE // update lock acquired ?
-	recyclePeriod   int64    = 60 * time.Second.Nanoseconds()
+	methodWeightMap sync.Map          // [string]invokers
+	state           = int32(COMPLETE) // update lock acquired ?
+	recyclePeriod   = 60 * time.Second.Nanoseconds()
 )
 
 func init() {
@@ -49,6 +52,7 @@ func init() {
 
 type roundRobinLoadBalance struct{}
 
+// NewRoundRobinLoadBalance ...
 func NewRoundRobinLoadBalance() cluster.LoadBalance {
 	return &roundRobinLoadBalance{}
 }
diff --git a/cluster/loadbalance/round_robin_test.go b/cluster/loadbalance/round_robin_test.go
index 0b8e89c7349afb056309b5a70d91aa5cce309aa0..1517f2a20b473af57cc23e61b988aa5a6a04de31 100644
--- a/cluster/loadbalance/round_robin_test.go
+++ b/cluster/loadbalance/round_robin_test.go
@@ -18,7 +18,6 @@
 package loadbalance
 
 import (
-	"context"
 	"fmt"
 	"strconv"
 	"testing"
@@ -39,13 +38,13 @@ func TestRoundRobinSelect(t *testing.T) {
 
 	var invokers []protocol.Invoker
 
-	url, _ := common.NewURL(context.TODO(), "dubbo://192.168.1.0:20000/org.apache.demo.HelloService")
+	url, _ := common.NewURL("dubbo://192.168.1.0:20000/org.apache.demo.HelloService")
 	invokers = append(invokers, protocol.NewBaseInvoker(url))
 	i := loadBalance.Select(invokers, &invocation.RPCInvocation{})
 	assert.True(t, i.GetUrl().URLEqual(url))
 
 	for i := 1; i < 10; i++ {
-		url, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://192.168.1.%v:20000/org.apache.demo.HelloService", i))
+		url, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.%v:20000/org.apache.demo.HelloService", i))
 		invokers = append(invokers, protocol.NewBaseInvoker(url))
 	}
 	loadBalance.Select(invokers, &invocation.RPCInvocation{})
@@ -57,7 +56,7 @@ func TestRoundRobinByWeight(t *testing.T) {
 	var invokers []protocol.Invoker
 	loop := 10
 	for i := 1; i <= loop; i++ {
-		url, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://192.168.1.%v:20000/org.apache.demo.HelloService?weight=%v", i, i))
+		url, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.%v:20000/org.apache.demo.HelloService?weight=%v", i, i))
 		invokers = append(invokers, protocol.NewBaseInvoker(url))
 	}
 
diff --git a/cluster/loadbalance/util.go b/cluster/loadbalance/util.go
index 7e0c2e265073c0a96032a6dd3294a6d73c1a4001..9f36ad9379a3a09a4a058f6179e3e537b9e105bc 100644
--- a/cluster/loadbalance/util.go
+++ b/cluster/loadbalance/util.go
@@ -26,6 +26,7 @@ import (
 	"github.com/apache/dubbo-go/protocol"
 )
 
+// GetWeight ...
 func GetWeight(invoker protocol.Invoker, invocation protocol.Invocation) int64 {
 	url := invoker.GetUrl()
 	weight := url.GetMethodParamInt64(invocation.MethodName(), constant.WEIGHT_KEY, constant.DEFAULT_WEIGHT)
diff --git a/cluster/router/chain/chain.go b/cluster/router/chain/chain.go
new file mode 100644
index 0000000000000000000000000000000000000000..d48a837eba2080867f370b0cd90e38a0bdf5e417
--- /dev/null
+++ b/cluster/router/chain/chain.go
@@ -0,0 +1,120 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package chain
+
+import (
+	"math"
+	"sort"
+	"sync"
+)
+
+import (
+	perrors "github.com/pkg/errors"
+)
+
+import (
+	"github.com/apache/dubbo-go/cluster/router"
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/common/extension"
+	"github.com/apache/dubbo-go/common/logger"
+	"github.com/apache/dubbo-go/protocol"
+)
+
+// RouterChain Router chain
+type RouterChain struct {
+	// Full list of addresses from registry, classified by method name.
+	invokers []protocol.Invoker
+	// Containing all routers, reconstruct every time 'route://' urls change.
+	routers []router.Router
+	// Fixed router instances: ConfigConditionRouter, TagRouter, e.g., the rule for each instance may change but the
+	// instance will never delete or recreate.
+	builtinRouters []router.Router
+
+	mutex sync.RWMutex
+}
+
+// Route Loop routers in RouterChain and call Route method to determine the target invokers list.
+func (c *RouterChain) Route(invoker []protocol.Invoker, url *common.URL, invocation protocol.Invocation) []protocol.Invoker {
+	finalInvokers := invoker
+	l := len(c.routers)
+	rs := make([]router.Router, l, int(math.Ceil(float64(l)*1.2)))
+	c.mutex.RLock()
+	copy(rs, c.routers)
+	c.mutex.RUnlock()
+
+	for _, r := range rs {
+		finalInvokers = r.Route(finalInvokers, url, invocation)
+	}
+	return finalInvokers
+}
+
+// AddRouters Add routers to router chain
+// New a array add builtinRouters which is not sorted in RouterChain and routers
+// Sort the array
+// Replace router array in RouterChain
+func (c *RouterChain) AddRouters(routers []router.Router) {
+	newRouters := make([]router.Router, 0, len(c.builtinRouters)+len(routers))
+	newRouters = append(newRouters, c.builtinRouters...)
+	newRouters = append(newRouters, routers...)
+	sortRouter(newRouters)
+	c.mutex.Lock()
+	defer c.mutex.Unlock()
+	c.routers = newRouters
+}
+
+// NewRouterChain Use url to init router chain
+// Loop routerFactories and call NewRouter method
+func NewRouterChain(url *common.URL) (*RouterChain, error) {
+	routerFactories := extension.GetRouterFactories()
+	if len(routerFactories) == 0 {
+		return nil, perrors.Errorf("No routerFactory exits , create one please")
+	}
+	routers := make([]router.Router, 0, len(routerFactories))
+	for key, routerFactory := range routerFactories {
+		r, err := routerFactory().NewRouter(url)
+		if r == nil || err != nil {
+			logger.Errorf("router chain build router fail! routerFactories key:%s  error:%s", key, err.Error())
+			continue
+		}
+		routers = append(routers, r)
+	}
+
+	newRouters := make([]router.Router, len(routers))
+	copy(newRouters, routers)
+
+	sortRouter(newRouters)
+
+	chain := &RouterChain{
+		builtinRouters: routers,
+		routers:        newRouters,
+	}
+
+	return chain, nil
+}
+
+// sortRouter Sort router instance by priority with stable algorithm
+func sortRouter(routers []router.Router) {
+	sort.Stable(byPriority(routers))
+}
+
+// byPriority Sort by priority
+type byPriority []router.Router
+
+func (a byPriority) Len() int           { return len(a) }
+func (a byPriority) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
+func (a byPriority) Less(i, j int) bool { return a[i].Priority() < a[j].Priority() }
diff --git a/cluster/router/chain/chain_test.go b/cluster/router/chain/chain_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..0cb47c4a185fe19b5f70ea4db2b80aab2f1aada5
--- /dev/null
+++ b/cluster/router/chain/chain_test.go
@@ -0,0 +1,248 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package chain
+
+import (
+	"encoding/base64"
+	"fmt"
+	"strconv"
+	"testing"
+	"time"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+import (
+	"github.com/apache/dubbo-go/cluster/router"
+	"github.com/apache/dubbo-go/cluster/router/condition"
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/common/config"
+	"github.com/apache/dubbo-go/common/constant"
+	"github.com/apache/dubbo-go/common/extension"
+	_ "github.com/apache/dubbo-go/config_center/zookeeper"
+	"github.com/apache/dubbo-go/protocol"
+	"github.com/apache/dubbo-go/protocol/invocation"
+	"github.com/apache/dubbo-go/remoting/zookeeper"
+)
+
+func TestNewRouterChain(t *testing.T) {
+	ts, z, _, err := zookeeper.NewMockZookeeperClient("test", 15*time.Second)
+	assert.NoError(t, err)
+	err = z.Create("/dubbo/config/dubbo/test-condition.condition-router")
+	assert.NoError(t, err)
+
+	testyml := `enabled: true
+force: true
+runtime: false
+conditions:
+  - => host != 172.22.3.91
+`
+
+	_, err = z.Conn.Set("/dubbo/config/dubbo/test-condition.condition-router", []byte(testyml), 0)
+	assert.NoError(t, err)
+	defer ts.Stop()
+	defer z.Close()
+
+	zkUrl, _ := common.NewURL("zookeeper://127.0.0.1:" + strconv.Itoa(ts.Servers[0].Port))
+	configuration, err := extension.GetConfigCenterFactory("zookeeper").GetDynamicConfiguration(&zkUrl)
+	config.GetEnvInstance().SetDynamicConfiguration(configuration)
+
+	assert.Nil(t, err)
+	assert.NotNil(t, configuration)
+
+	chain, err := NewRouterChain(getRouteUrl("test-condition"))
+	assert.Nil(t, err)
+	assert.Equal(t, 1, len(chain.routers))
+	appRouter := chain.routers[0].(*condition.AppRouter)
+
+	assert.NotNil(t, appRouter)
+	assert.NotNil(t, appRouter.RouterRule())
+	rule := appRouter.RouterRule()
+	assert.Equal(t, "", rule.Scope)
+	assert.True(t, rule.Force)
+	assert.True(t, rule.Enabled)
+	assert.True(t, rule.Valid)
+
+	assert.Equal(t, testyml, rule.RawRule)
+	assert.Equal(t, false, rule.Runtime)
+	assert.Equal(t, false, rule.Dynamic)
+	assert.Equal(t, "", rule.Key)
+}
+
+func TestNewRouterChainURLNil(t *testing.T) {
+	chain, err := NewRouterChain(nil)
+	assert.NoError(t, err)
+	assert.NotNil(t, chain)
+}
+
+func TestRouterChain_AddRouters(t *testing.T) {
+	ts, z, _, err := zookeeper.NewMockZookeeperClient("test", 15*time.Second)
+	assert.NoError(t, err)
+	err = z.Create("/dubbo/config/dubbo/test-condition.condition-router")
+	assert.NoError(t, err)
+
+	testyml := `enabled: true
+force: true
+runtime: false
+conditions:
+  - => host != 172.22.3.91
+`
+
+	_, err = z.Conn.Set("/dubbo/config/dubbo/test-condition.condition-router", []byte(testyml), 0)
+	assert.NoError(t, err)
+	defer ts.Stop()
+	defer z.Close()
+
+	zkUrl, _ := common.NewURL("zookeeper://127.0.0.1:" + strconv.Itoa(ts.Servers[0].Port))
+	configuration, err := extension.GetConfigCenterFactory("zookeeper").GetDynamicConfiguration(&zkUrl)
+	config.GetEnvInstance().SetDynamicConfiguration(configuration)
+
+	chain, err := NewRouterChain(getConditionRouteUrl("test-condition"))
+	assert.Nil(t, err)
+	assert.Equal(t, 2, len(chain.routers))
+
+	url := getConditionRouteUrl("test-condition")
+	assert.NotNil(t, url)
+	factory := extension.GetRouterFactory(url.Protocol)
+	r, err := factory.NewRouter(url)
+	assert.Nil(t, err)
+	assert.NotNil(t, r)
+
+	routers := make([]router.Router, 0)
+	routers = append(routers, r)
+	chain.AddRouters(routers)
+	assert.Equal(t, 3, len(chain.routers))
+}
+
+func TestRouterChain_Route(t *testing.T) {
+	ts, z, _, err := zookeeper.NewMockZookeeperClient("test", 15*time.Second)
+	defer ts.Stop()
+	defer z.Close()
+
+	zkUrl, _ := common.NewURL("zookeeper://127.0.0.1:" + strconv.Itoa(ts.Servers[0].Port))
+	configuration, err := extension.GetConfigCenterFactory("zookeeper").GetDynamicConfiguration(&zkUrl)
+	config.GetEnvInstance().SetDynamicConfiguration(configuration)
+
+	chain, err := NewRouterChain(getConditionRouteUrl("test-condition"))
+	assert.Nil(t, err)
+	assert.Equal(t, 1, len(chain.routers))
+
+	url := getConditionRouteUrl("test-condition")
+	assert.NotNil(t, url)
+
+	invokers := []protocol.Invoker{}
+	dubboURL, _ := common.NewURL(fmt.Sprintf("dubbo://1.2.3.4:20000/com.foo.BarService"))
+	invokers = append(invokers, protocol.NewBaseInvoker(dubboURL))
+
+	targetURL, _ := common.NewURL(fmt.Sprintf("consumer://1.1.1.1/com.foo.BarService"))
+	inv := &invocation.RPCInvocation{}
+	finalInvokers := chain.Route(invokers, &targetURL, inv)
+
+	assert.Equal(t, 1, len(finalInvokers))
+}
+
+func TestRouterChain_Route_AppRouter(t *testing.T) {
+	ts, z, _, err := zookeeper.NewMockZookeeperClient("test", 15*time.Second)
+	assert.NoError(t, err)
+	err = z.Create("/dubbo/config/dubbo/test-condition.condition-router")
+	assert.NoError(t, err)
+
+	testyml := `enabled: true
+force: true
+runtime: false
+conditions:
+  - => host = 1.1.1.1 => host != 1.2.3.4
+`
+
+	_, err = z.Conn.Set("/dubbo/config/dubbo/test-condition.condition-router", []byte(testyml), 0)
+	assert.NoError(t, err)
+	defer ts.Stop()
+	defer z.Close()
+
+	zkUrl, _ := common.NewURL("zookeeper://127.0.0.1:" + strconv.Itoa(ts.Servers[0].Port))
+	configuration, err := extension.GetConfigCenterFactory("zookeeper").GetDynamicConfiguration(&zkUrl)
+	config.GetEnvInstance().SetDynamicConfiguration(configuration)
+
+	chain, err := NewRouterChain(getConditionRouteUrl("test-condition"))
+	assert.Nil(t, err)
+	assert.Equal(t, 2, len(chain.routers))
+
+	invokers := []protocol.Invoker{}
+	dubboURL, _ := common.NewURL(fmt.Sprintf("dubbo://1.2.3.4:20000/com.foo.BarService"))
+	invokers = append(invokers, protocol.NewBaseInvoker(dubboURL))
+
+	targetURL, _ := common.NewURL(fmt.Sprintf("consumer://1.1.1.1/com.foo.BarService"))
+	inv := &invocation.RPCInvocation{}
+	finalInvokers := chain.Route(invokers, &targetURL, inv)
+
+	assert.Equal(t, 0, len(finalInvokers))
+}
+
+func TestRouterChain_Route_NoRoute(t *testing.T) {
+	ts, z, _, err := zookeeper.NewMockZookeeperClient("test", 15*time.Second)
+	defer ts.Stop()
+	defer z.Close()
+
+	zkUrl, _ := common.NewURL("zookeeper://127.0.0.1:" + strconv.Itoa(ts.Servers[0].Port))
+	configuration, err := extension.GetConfigCenterFactory("zookeeper").GetDynamicConfiguration(&zkUrl)
+	config.GetEnvInstance().SetDynamicConfiguration(configuration)
+
+	chain, err := NewRouterChain(getConditionNoRouteUrl("test-condition"))
+	assert.Nil(t, err)
+	assert.Equal(t, 1, len(chain.routers))
+
+	url := getConditionRouteUrl("test-condition")
+	assert.NotNil(t, url)
+
+	invokers := []protocol.Invoker{}
+	dubboURL, _ := common.NewURL(fmt.Sprintf("dubbo://1.2.3.4:20000/com.foo.BarService"))
+	invokers = append(invokers, protocol.NewBaseInvoker(dubboURL))
+
+	targetURL, _ := common.NewURL(fmt.Sprintf("consumer://1.1.1.1/com.foo.BarService"))
+	inv := &invocation.RPCInvocation{}
+	finalInvokers := chain.Route(invokers, &targetURL, inv)
+
+	assert.Equal(t, 0, len(finalInvokers))
+}
+
+func getConditionNoRouteUrl(applicationKey string) *common.URL {
+	url, _ := common.NewURL("condition://0.0.0.0/com.foo.BarService")
+	url.AddParam("application", applicationKey)
+	url.AddParam("force", "true")
+	rule := base64.URLEncoding.EncodeToString([]byte("host = 1.1.1.1 => host != 1.2.3.4"))
+	url.AddParam(constant.RULE_KEY, rule)
+	return &url
+}
+
+func getConditionRouteUrl(applicationKey string) *common.URL {
+	url, _ := common.NewURL("condition://0.0.0.0/com.foo.BarService")
+	url.AddParam("application", applicationKey)
+	url.AddParam("force", "true")
+	rule := base64.URLEncoding.EncodeToString([]byte("host = 1.1.1.1 => host = 1.2.3.4"))
+	url.AddParam(constant.RULE_KEY, rule)
+	return &url
+}
+
+func getRouteUrl(applicationKey string) *common.URL {
+	url, _ := common.NewURL("condition://0.0.0.0/com.foo.BarService")
+	url.AddParam("application", applicationKey)
+	url.AddParam("force", "true")
+	return &url
+}
diff --git a/cluster/router/condition/app_router.go b/cluster/router/condition/app_router.go
new file mode 100644
index 0000000000000000000000000000000000000000..056e32851c11696c80d18a2a55b109fcdae06627
--- /dev/null
+++ b/cluster/router/condition/app_router.go
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package condition
+
+import (
+	perrors "github.com/pkg/errors"
+)
+
+import (
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/common/constant"
+)
+
+const (
+	// Default priority for application router
+	appRouterDefaultPriority = int64(150)
+)
+
+// AppRouter For listen application router with config center
+type AppRouter struct {
+	listenableRouter
+}
+
+// NewAppRouter Init AppRouter by url
+func NewAppRouter(url *common.URL) (*AppRouter, error) {
+	if url == nil {
+		return nil, perrors.Errorf("No route URL for create app router!")
+	}
+	appRouter, err := newListenableRouter(url, url.GetParam(constant.APPLICATION_KEY, ""))
+	if err != nil {
+		return nil, err
+	}
+	appRouter.priority = appRouterDefaultPriority
+	return appRouter, nil
+}
diff --git a/cluster/router/condition/app_router_test.go b/cluster/router/condition/app_router_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..bd817af36c8c144295479fb07ada9411f4115bbc
--- /dev/null
+++ b/cluster/router/condition/app_router_test.go
@@ -0,0 +1,178 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package condition
+
+import (
+	"strconv"
+	"testing"
+	"time"
+)
+
+import (
+	_ "github.com/apache/dubbo-go/config_center/zookeeper"
+	"github.com/stretchr/testify/assert"
+)
+
+import (
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/common/config"
+	"github.com/apache/dubbo-go/common/extension"
+	"github.com/apache/dubbo-go/config_center"
+	"github.com/apache/dubbo-go/remoting"
+	"github.com/apache/dubbo-go/remoting/zookeeper"
+)
+
+func TestNewAppRouter(t *testing.T) {
+
+	testYML := `enabled: true
+force: true
+runtime: false
+conditions:
+  - => host != 172.22.3.91
+`
+	ts, z, _, err := zookeeper.NewMockZookeeperClient("test", 15*time.Second)
+	assert.NoError(t, err)
+	err = z.Create("/dubbo/config/dubbo/test-condition.condition-router")
+	assert.NoError(t, err)
+
+	_, err = z.Conn.Set("/dubbo/config/dubbo/test-condition.condition-router", []byte(testYML), 0)
+	assert.NoError(t, err)
+	defer ts.Stop()
+	defer z.Close()
+
+	zkUrl, _ := common.NewURL("zookeeper://127.0.0.1:" + strconv.Itoa(ts.Servers[0].Port))
+	configuration, err := extension.GetConfigCenterFactory("zookeeper").GetDynamicConfiguration(&zkUrl)
+	config.GetEnvInstance().SetDynamicConfiguration(configuration)
+
+	assert.Nil(t, err)
+	assert.NotNil(t, configuration)
+
+	appRouteURL := getAppRouteURL("test-condition")
+	appRouter, err := NewAppRouter(appRouteURL)
+	assert.Nil(t, err)
+	assert.NotNil(t, appRouter)
+
+	assert.NotNil(t, appRouter)
+	assert.NotNil(t, appRouter.RouterRule())
+	rule := appRouter.RouterRule()
+	assert.Equal(t, "", rule.Scope)
+	assert.True(t, rule.Force)
+	assert.True(t, rule.Enabled)
+	assert.True(t, rule.Valid)
+
+	assert.Equal(t, testYML, rule.RawRule)
+	assert.Equal(t, false, rule.Runtime)
+	assert.Equal(t, false, rule.Dynamic)
+	assert.Equal(t, "", rule.Key)
+	assert.Equal(t, 0, rule.Priority)
+}
+
+func TestGenerateConditions(t *testing.T) {
+
+	testYML := `enabled: true
+force: true
+runtime: false
+conditions:
+  - => host != 172.22.3.91
+  - host = 192.168.199.208 => host = 192.168.199.208 
+`
+	ts, z, _, err := zookeeper.NewMockZookeeperClient("test", 15*time.Second)
+	assert.NoError(t, err)
+	err = z.Create("/dubbo/config/dubbo/test-condition.condition-router")
+	assert.NoError(t, err)
+
+	_, err = z.Conn.Set("/dubbo/config/dubbo/test-condition.condition-router", []byte(testYML), 0)
+	assert.NoError(t, err)
+	defer ts.Stop()
+	defer z.Close()
+
+	zkUrl, _ := common.NewURL("zookeeper://127.0.0.1:" + strconv.Itoa(ts.Servers[0].Port))
+	configuration, err := extension.GetConfigCenterFactory("zookeeper").GetDynamicConfiguration(&zkUrl)
+	config.GetEnvInstance().SetDynamicConfiguration(configuration)
+
+	assert.Nil(t, err)
+	assert.NotNil(t, configuration)
+
+	appRouteURL := getAppRouteURL("test-condition")
+	appRouter, err := NewAppRouter(appRouteURL)
+	assert.Nil(t, err)
+	assert.NotNil(t, appRouter)
+
+	rule, err := Parse(testYML)
+	assert.Nil(t, err)
+	appRouter.generateConditions(rule)
+
+	assert.Equal(t, 2, len(appRouter.conditionRouters))
+}
+
+func TestProcess(t *testing.T) {
+
+	testYML := `enabled: true
+force: true
+runtime: false
+conditions:
+  - => host != 172.22.3.91
+`
+	ts, z, _, err := zookeeper.NewMockZookeeperClient("test", 15*time.Second)
+	assert.NoError(t, err)
+	err = z.Create("/dubbo/config/dubbo/test-condition.condition-router")
+	assert.NoError(t, err)
+
+	_, err = z.Conn.Set("/dubbo/config/dubbo/test-condition.condition-router", []byte(testYML), 0)
+	assert.NoError(t, err)
+	defer ts.Stop()
+	defer z.Close()
+
+	zkUrl, _ := common.NewURL("zookeeper://127.0.0.1:" + strconv.Itoa(ts.Servers[0].Port))
+	configuration, err := extension.GetConfigCenterFactory("zookeeper").GetDynamicConfiguration(&zkUrl)
+	config.GetEnvInstance().SetDynamicConfiguration(configuration)
+
+	assert.Nil(t, err)
+	assert.NotNil(t, configuration)
+
+	appRouteURL := getAppRouteURL("test-condition")
+	appRouter, err := NewAppRouter(appRouteURL)
+	assert.Nil(t, err)
+	assert.NotNil(t, appRouter)
+
+	assert.Equal(t, 1, len(appRouter.conditionRouters))
+
+	testNewYML := `
+enabled: true
+force: true
+runtime: false
+conditions:
+  - => host != 172.22.3.91
+  - host = 192.168.199.208 => host = 192.168.199.208
+`
+
+	appRouter.Process(&config_center.ConfigChangeEvent{ConfigType: remoting.EventTypeDel})
+
+	assert.Equal(t, 0, len(appRouter.conditionRouters))
+
+	appRouter.Process(&config_center.ConfigChangeEvent{Value: testNewYML, ConfigType: remoting.EventTypeAdd})
+
+	assert.Equal(t, 2, len(appRouter.conditionRouters))
+}
+
+func getAppRouteURL(applicationKey string) *common.URL {
+	url, _ := common.NewURL("condition://0.0.0.0/com.foo.BarService")
+	url.AddParam("application", applicationKey)
+	url.AddParam("force", "true")
+	return &url
+}
diff --git a/cluster/router/condition/factory.go b/cluster/router/condition/factory.go
new file mode 100644
index 0000000000000000000000000000000000000000..66512a138706e9b9ab565f7537a15c37a75deefd
--- /dev/null
+++ b/cluster/router/condition/factory.go
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package condition
+
+import (
+	"github.com/apache/dubbo-go/cluster/router"
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/common/constant"
+	"github.com/apache/dubbo-go/common/extension"
+)
+
+func init() {
+	extension.SetRouterFactory(constant.ConditionRouterName, newConditionRouterFactory)
+	extension.SetRouterFactory(constant.ConditionAppRouterName, newAppRouterFactory)
+}
+
+// ConditionRouterFactory Condition router factory
+type ConditionRouterFactory struct{}
+
+func newConditionRouterFactory() router.RouterFactory {
+	return &ConditionRouterFactory{}
+}
+
+// NewRouter Create ConditionRouterFactory by URL
+func (c *ConditionRouterFactory) NewRouter(url *common.URL) (router.Router, error) {
+	return NewConditionRouter(url)
+}
+
+// NewRouter Create FileRouterFactory by Content
+func (c *ConditionRouterFactory) NewFileRouter(content []byte) (router.Router, error) {
+	return NewFileConditionRouter(content)
+}
+
+// AppRouterFactory Application router factory
+type AppRouterFactory struct{}
+
+func newAppRouterFactory() router.RouterFactory {
+	return &AppRouterFactory{}
+}
+
+// NewRouter Create AppRouterFactory by URL
+func (c *AppRouterFactory) NewRouter(url *common.URL) (router.Router, error) {
+	return NewAppRouter(url)
+}
diff --git a/cluster/router/condition_router_test.go b/cluster/router/condition/factory_test.go
similarity index 54%
rename from cluster/router/condition_router_test.go
rename to cluster/router/condition/factory_test.go
index 7d8b0d88cab688e6ea10d1562a27de4609d51f58..99cec34096a55d3c2a967b63afdf5f6d0a77279a 100644
--- a/cluster/router/condition_router_test.go
+++ b/cluster/router/condition/factory_test.go
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package router
+package condition
 
 import (
 	"context"
@@ -59,21 +59,21 @@ func (bi *MockInvoker) GetUrl() common.URL {
 }
 
 func getRouteUrl(rule string) *common.URL {
-	url, _ := common.NewURL(context.TODO(), "condition://0.0.0.0/com.foo.BarService")
+	url, _ := common.NewURL("condition://0.0.0.0/com.foo.BarService")
 	url.AddParam("rule", rule)
 	url.AddParam("force", "true")
 	return &url
 }
 
 func getRouteUrlWithForce(rule, force string) *common.URL {
-	url, _ := common.NewURL(context.TODO(), "condition://0.0.0.0/com.foo.BarService")
+	url, _ := common.NewURL("condition://0.0.0.0/com.foo.BarService")
 	url.AddParam("rule", rule)
 	url.AddParam("force", force)
 	return &url
 }
 
 func getRouteUrlWithNoForce(rule string) *common.URL {
-	url, _ := common.NewURL(context.TODO(), "condition://0.0.0.0/com.foo.BarService")
+	url, _ := common.NewURL("condition://0.0.0.0/com.foo.BarService")
 	url.AddParam("rule", rule)
 	return &url
 }
@@ -93,15 +93,19 @@ type rest struct {
 
 var count int
 
-func (bi *MockInvoker) Invoke(invocation protocol.Invocation) protocol.Result {
+func (bi *MockInvoker) Invoke(_ context.Context, _ protocol.Invocation) protocol.Result {
 	count++
-	var success bool
-	var err error = nil
+
+	var (
+		success bool
+		err     error
+	)
 	if count >= bi.successCount {
 		success = true
 	} else {
 		err = perrors.New("error")
 	}
+
 	result := &protocol.RPCResult{Err: err, Rest: rest{tried: count, success: success}}
 	return result
 }
@@ -115,42 +119,42 @@ func (bi *MockInvoker) Destroy() {
 func TestRoute_matchWhen(t *testing.T) {
 	inv := &invocation.RPCInvocation{}
 	rule := base64.URLEncoding.EncodeToString([]byte("=> host = 1.2.3.4"))
-	router, _ := NewConditionRouterFactory().Router(getRouteUrl(rule))
-	cUrl, _ := common.NewURL(context.TODO(), "consumer://1.1.1.1/com.foo.BarService")
-	matchWhen, _ := router.(*ConditionRouter).MatchWhen(cUrl, inv)
+	router, _ := newConditionRouterFactory().NewRouter(getRouteUrl(rule))
+	cUrl, _ := common.NewURL("consumer://1.1.1.1/com.foo.BarService")
+	matchWhen := router.(*ConditionRouter).MatchWhen(&cUrl, inv)
 	assert.Equal(t, true, matchWhen)
 	rule1 := base64.URLEncoding.EncodeToString([]byte("host = 2.2.2.2,1.1.1.1,3.3.3.3 => host = 1.2.3.4"))
-	router1, _ := NewConditionRouterFactory().Router(getRouteUrl(rule1))
-	matchWhen1, _ := router1.(*ConditionRouter).MatchWhen(cUrl, inv)
+	router1, _ := newConditionRouterFactory().NewRouter(getRouteUrl(rule1))
+	matchWhen1 := router1.(*ConditionRouter).MatchWhen(&cUrl, inv)
 	assert.Equal(t, true, matchWhen1)
 	rule2 := base64.URLEncoding.EncodeToString([]byte("host = 2.2.2.2,1.1.1.1,3.3.3.3 & host !=1.1.1.1 => host = 1.2.3.4"))
-	router2, _ := NewConditionRouterFactory().Router(getRouteUrl(rule2))
-	matchWhen2, _ := router2.(*ConditionRouter).MatchWhen(cUrl, inv)
+	router2, _ := newConditionRouterFactory().NewRouter(getRouteUrl(rule2))
+	matchWhen2 := router2.(*ConditionRouter).MatchWhen(&cUrl, inv)
 	assert.Equal(t, false, matchWhen2)
 	rule3 := base64.URLEncoding.EncodeToString([]byte("host !=4.4.4.4 & host = 2.2.2.2,1.1.1.1,3.3.3.3 => host = 1.2.3.4"))
-	router3, _ := NewConditionRouterFactory().Router(getRouteUrl(rule3))
-	matchWhen3, _ := router3.(*ConditionRouter).MatchWhen(cUrl, inv)
+	router3, _ := newConditionRouterFactory().NewRouter(getRouteUrl(rule3))
+	matchWhen3 := router3.(*ConditionRouter).MatchWhen(&cUrl, inv)
 	assert.Equal(t, true, matchWhen3)
 	rule4 := base64.URLEncoding.EncodeToString([]byte("host !=4.4.4.* & host = 2.2.2.2,1.1.1.1,3.3.3.3 => host = 1.2.3.4"))
-	router4, _ := NewConditionRouterFactory().Router(getRouteUrl(rule4))
-	matchWhen4, _ := router4.(*ConditionRouter).MatchWhen(cUrl, inv)
+	router4, _ := newConditionRouterFactory().NewRouter(getRouteUrl(rule4))
+	matchWhen4 := router4.(*ConditionRouter).MatchWhen(&cUrl, inv)
 	assert.Equal(t, true, matchWhen4)
 	rule5 := base64.URLEncoding.EncodeToString([]byte("host = 2.2.2.2,1.1.1.*,3.3.3.3 & host != 1.1.1.1 => host = 1.2.3.4"))
-	router5, _ := NewConditionRouterFactory().Router(getRouteUrl(rule5))
-	matchWhen5, _ := router5.(*ConditionRouter).MatchWhen(cUrl, inv)
+	router5, _ := newConditionRouterFactory().NewRouter(getRouteUrl(rule5))
+	matchWhen5 := router5.(*ConditionRouter).MatchWhen(&cUrl, inv)
 	assert.Equal(t, false, matchWhen5)
 	rule6 := base64.URLEncoding.EncodeToString([]byte("host = 2.2.2.2,1.1.1.*,3.3.3.3 & host != 1.1.1.2 => host = 1.2.3.4"))
-	router6, _ := NewConditionRouterFactory().Router(getRouteUrl(rule6))
-	matchWhen6, _ := router6.(*ConditionRouter).MatchWhen(cUrl, inv)
+	router6, _ := newConditionRouterFactory().NewRouter(getRouteUrl(rule6))
+	matchWhen6 := router6.(*ConditionRouter).MatchWhen(&cUrl, inv)
 	assert.Equal(t, true, matchWhen6)
 }
 
 func TestRoute_matchFilter(t *testing.T) {
 	localIP, _ := gxnet.GetLocalIP()
 	t.Logf("The local ip is %s", localIP)
-	url1, _ := common.NewURL(context.TODO(), "dubbo://10.20.3.3:20880/com.foo.BarService?default.serialization=fastjson")
-	url2, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP))
-	url3, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP))
+	url1, _ := common.NewURL("dubbo://10.20.3.3:20880/com.foo.BarService?default.serialization=fastjson")
+	url2, _ := common.NewURL(fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP))
+	url3, _ := common.NewURL(fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP))
 	invokers := []protocol.Invoker{NewMockInvoker(url1, 1), NewMockInvoker(url2, 2), NewMockInvoker(url3, 3)}
 	rule1 := base64.URLEncoding.EncodeToString([]byte("host = " + localIP + " => " + " host = 10.20.3.3"))
 	rule2 := base64.URLEncoding.EncodeToString([]byte("host = " + localIP + " => " + " host = 10.20.3.* & host != 10.20.3.3"))
@@ -158,19 +162,19 @@ func TestRoute_matchFilter(t *testing.T) {
 	rule4 := base64.URLEncoding.EncodeToString([]byte("host = " + localIP + " => " + " host = 10.20.3.2,10.20.3.3,10.20.3.4"))
 	rule5 := base64.URLEncoding.EncodeToString([]byte("host = " + localIP + " => " + " host != 10.20.3.3"))
 	rule6 := base64.URLEncoding.EncodeToString([]byte("host = " + localIP + " => " + " serialization = fastjson"))
-	router1, _ := NewConditionRouterFactory().Router(getRouteUrl(rule1))
-	router2, _ := NewConditionRouterFactory().Router(getRouteUrl(rule2))
-	router3, _ := NewConditionRouterFactory().Router(getRouteUrl(rule3))
-	router4, _ := NewConditionRouterFactory().Router(getRouteUrl(rule4))
-	router5, _ := NewConditionRouterFactory().Router(getRouteUrl(rule5))
-	router6, _ := NewConditionRouterFactory().Router(getRouteUrl(rule6))
-	cUrl, _ := common.NewURL(context.TODO(), "consumer://"+localIP+"/com.foo.BarService")
-	fileredInvokers1 := router1.Route(invokers, cUrl, &invocation.RPCInvocation{})
-	fileredInvokers2 := router2.Route(invokers, cUrl, &invocation.RPCInvocation{})
-	fileredInvokers3 := router3.Route(invokers, cUrl, &invocation.RPCInvocation{})
-	fileredInvokers4 := router4.Route(invokers, cUrl, &invocation.RPCInvocation{})
-	fileredInvokers5 := router5.Route(invokers, cUrl, &invocation.RPCInvocation{})
-	fileredInvokers6 := router6.Route(invokers, cUrl, &invocation.RPCInvocation{})
+	router1, _ := newConditionRouterFactory().NewRouter(getRouteUrl(rule1))
+	router2, _ := newConditionRouterFactory().NewRouter(getRouteUrl(rule2))
+	router3, _ := newConditionRouterFactory().NewRouter(getRouteUrl(rule3))
+	router4, _ := newConditionRouterFactory().NewRouter(getRouteUrl(rule4))
+	router5, _ := newConditionRouterFactory().NewRouter(getRouteUrl(rule5))
+	router6, _ := newConditionRouterFactory().NewRouter(getRouteUrl(rule6))
+	cUrl, _ := common.NewURL("consumer://" + localIP + "/com.foo.BarService")
+	fileredInvokers1 := router1.Route(invokers, &cUrl, &invocation.RPCInvocation{})
+	fileredInvokers2 := router2.Route(invokers, &cUrl, &invocation.RPCInvocation{})
+	fileredInvokers3 := router3.Route(invokers, &cUrl, &invocation.RPCInvocation{})
+	fileredInvokers4 := router4.Route(invokers, &cUrl, &invocation.RPCInvocation{})
+	fileredInvokers5 := router5.Route(invokers, &cUrl, &invocation.RPCInvocation{})
+	fileredInvokers6 := router6.Route(invokers, &cUrl, &invocation.RPCInvocation{})
 	assert.Equal(t, 1, len(fileredInvokers1))
 	assert.Equal(t, 0, len(fileredInvokers2))
 	assert.Equal(t, 0, len(fileredInvokers3))
@@ -183,75 +187,80 @@ func TestRoute_matchFilter(t *testing.T) {
 func TestRoute_methodRoute(t *testing.T) {
 	inv := invocation.NewRPCInvocationWithOptions(invocation.WithMethodName("getFoo"), invocation.WithParameterTypes([]reflect.Type{}), invocation.WithArguments([]interface{}{}))
 	rule := base64.URLEncoding.EncodeToString([]byte("host !=4.4.4.* & host = 2.2.2.2,1.1.1.1,3.3.3.3 => host = 1.2.3.4"))
-	router, _ := NewConditionRouterFactory().Router(getRouteUrl(rule))
-	url, _ := common.NewURL(context.TODO(), "consumer://1.1.1.1/com.foo.BarService?methods=setFoo,getFoo,findFoo")
-	matchWhen, _ := router.(*ConditionRouter).MatchWhen(url, inv)
+	router, _ := newConditionRouterFactory().NewRouter(getRouteUrl(rule))
+	url, _ := common.NewURL("consumer://1.1.1.1/com.foo.BarService?methods=setFoo,getFoo,findFoo")
+	matchWhen := router.(*ConditionRouter).MatchWhen(&url, inv)
 	assert.Equal(t, true, matchWhen)
-	url1, _ := common.NewURL(context.TODO(), "consumer://1.1.1.1/com.foo.BarService?methods=getFoo")
-	matchWhen, _ = router.(*ConditionRouter).MatchWhen(url1, inv)
+	url1, _ := common.NewURL("consumer://1.1.1.1/com.foo.BarService?methods=getFoo")
+	matchWhen = router.(*ConditionRouter).MatchWhen(&url1, inv)
 	assert.Equal(t, true, matchWhen)
-	url2, _ := common.NewURL(context.TODO(), "consumer://1.1.1.1/com.foo.BarService?methods=getFoo")
+	url2, _ := common.NewURL("consumer://1.1.1.1/com.foo.BarService?methods=getFoo")
 	rule2 := base64.URLEncoding.EncodeToString([]byte("methods=getFoo & host!=1.1.1.1 => host = 1.2.3.4"))
-	router2, _ := NewConditionRouterFactory().Router(getRouteUrl(rule2))
-	matchWhen, _ = router2.(*ConditionRouter).MatchWhen(url2, inv)
+	router2, _ := newConditionRouterFactory().NewRouter(getRouteUrl(rule2))
+	matchWhen = router2.(*ConditionRouter).MatchWhen(&url2, inv)
 	assert.Equal(t, false, matchWhen)
-	url3, _ := common.NewURL(context.TODO(), "consumer://1.1.1.1/com.foo.BarService?methods=getFoo")
+	url3, _ := common.NewURL("consumer://1.1.1.1/com.foo.BarService?methods=getFoo")
 	rule3 := base64.URLEncoding.EncodeToString([]byte("methods=getFoo & host=1.1.1.1 => host = 1.2.3.4"))
-	router3, _ := NewConditionRouterFactory().Router(getRouteUrl(rule3))
-	matchWhen, _ = router3.(*ConditionRouter).MatchWhen(url3, inv)
+	router3, _ := newConditionRouterFactory().NewRouter(getRouteUrl(rule3))
+	matchWhen = router3.(*ConditionRouter).MatchWhen(&url3, inv)
 	assert.Equal(t, true, matchWhen)
 
 }
 
 func TestRoute_ReturnFalse(t *testing.T) {
-	url, _ := common.NewURL(context.TODO(), "")
+	url, _ := common.NewURL("")
 	localIP, _ := gxnet.GetLocalIP()
 	invokers := []protocol.Invoker{NewMockInvoker(url, 1), NewMockInvoker(url, 2), NewMockInvoker(url, 3)}
 	inv := &invocation.RPCInvocation{}
 	rule := base64.URLEncoding.EncodeToString([]byte("host = " + localIP + " => false"))
-	curl, _ := common.NewURL(context.TODO(), "consumer://"+localIP+"/com.foo.BarService")
-	router, _ := NewConditionRouterFactory().Router(getRouteUrl(rule))
-	fileredInvokers := router.(*ConditionRouter).Route(invokers, curl, inv)
+	curl, _ := common.NewURL("consumer://" + localIP + "/com.foo.BarService")
+	router, _ := newConditionRouterFactory().NewRouter(getRouteUrl(rule))
+	fileredInvokers := router.(*ConditionRouter).Route(invokers, &curl, inv)
 	assert.Equal(t, 0, len(fileredInvokers))
 }
 
 func TestRoute_ReturnEmpty(t *testing.T) {
 	localIP, _ := gxnet.GetLocalIP()
-	url, _ := common.NewURL(context.TODO(), "")
+	url, _ := common.NewURL("")
 	invokers := []protocol.Invoker{NewMockInvoker(url, 1), NewMockInvoker(url, 2), NewMockInvoker(url, 3)}
 	inv := &invocation.RPCInvocation{}
 	rule := base64.URLEncoding.EncodeToString([]byte("host = " + localIP + " => "))
-	curl, _ := common.NewURL(context.TODO(), "consumer://"+localIP+"/com.foo.BarService")
-	router, _ := NewConditionRouterFactory().Router(getRouteUrl(rule))
-	fileredInvokers := router.(*ConditionRouter).Route(invokers, curl, inv)
+	curl, _ := common.NewURL("consumer://" + localIP + "/com.foo.BarService")
+	router, _ := newConditionRouterFactory().NewRouter(getRouteUrl(rule))
+	fileredInvokers := router.(*ConditionRouter).Route(invokers, &curl, inv)
 	assert.Equal(t, 0, len(fileredInvokers))
 }
 
 func TestRoute_ReturnAll(t *testing.T) {
 	localIP, _ := gxnet.GetLocalIP()
-	invokers := []protocol.Invoker{&MockInvoker{}, &MockInvoker{}, &MockInvoker{}}
+	urlString := "dubbo://" + localIP + "/com.foo.BarService"
+	dubboURL, _ := common.NewURL(urlString)
+	mockInvoker1 := NewMockInvoker(dubboURL, 1)
+	mockInvoker2 := NewMockInvoker(dubboURL, 1)
+	mockInvoker3 := NewMockInvoker(dubboURL, 1)
+	invokers := []protocol.Invoker{mockInvoker1, mockInvoker2, mockInvoker3}
 	inv := &invocation.RPCInvocation{}
 	rule := base64.URLEncoding.EncodeToString([]byte("host = " + localIP + " => " + " host = " + localIP))
-	curl, _ := common.NewURL(context.TODO(), "consumer://"+localIP+"/com.foo.BarService")
-	router, _ := NewConditionRouterFactory().Router(getRouteUrl(rule))
-	fileredInvokers := router.(*ConditionRouter).Route(invokers, curl, inv)
+	curl, _ := common.NewURL("consumer://" + localIP + "/com.foo.BarService")
+	router, _ := newConditionRouterFactory().NewRouter(getRouteUrl(rule))
+	fileredInvokers := router.(*ConditionRouter).Route(invokers, &curl, inv)
 	assert.Equal(t, invokers, fileredInvokers)
 }
 
 func TestRoute_HostFilter(t *testing.T) {
 	localIP, _ := gxnet.GetLocalIP()
-	url1, _ := common.NewURL(context.TODO(), "dubbo://10.20.3.3:20880/com.foo.BarService")
-	url2, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP))
-	url3, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP))
+	url1, _ := common.NewURL("dubbo://10.20.3.3:20880/com.foo.BarService")
+	url2, _ := common.NewURL(fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP))
+	url3, _ := common.NewURL(fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP))
 	invoker1 := NewMockInvoker(url1, 1)
 	invoker2 := NewMockInvoker(url2, 2)
 	invoker3 := NewMockInvoker(url3, 3)
 	invokers := []protocol.Invoker{invoker1, invoker2, invoker3}
 	inv := &invocation.RPCInvocation{}
 	rule := base64.URLEncoding.EncodeToString([]byte("host = " + localIP + " => " + " host = " + localIP))
-	curl, _ := common.NewURL(context.TODO(), "consumer://"+localIP+"/com.foo.BarService")
-	router, _ := NewConditionRouterFactory().Router(getRouteUrl(rule))
-	fileredInvokers := router.(*ConditionRouter).Route(invokers, curl, inv)
+	curl, _ := common.NewURL("consumer://" + localIP + "/com.foo.BarService")
+	router, _ := newConditionRouterFactory().NewRouter(getRouteUrl(rule))
+	fileredInvokers := router.(*ConditionRouter).Route(invokers, &curl, inv)
 	assert.Equal(t, 2, len(fileredInvokers))
 	assert.Equal(t, invoker2, fileredInvokers[0])
 	assert.Equal(t, invoker3, fileredInvokers[1])
@@ -259,18 +268,18 @@ func TestRoute_HostFilter(t *testing.T) {
 
 func TestRoute_Empty_HostFilter(t *testing.T) {
 	localIP, _ := gxnet.GetLocalIP()
-	url1, _ := common.NewURL(context.TODO(), "dubbo://10.20.3.3:20880/com.foo.BarService")
-	url2, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP))
-	url3, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP))
+	url1, _ := common.NewURL("dubbo://10.20.3.3:20880/com.foo.BarService")
+	url2, _ := common.NewURL(fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP))
+	url3, _ := common.NewURL(fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP))
 	invoker1 := NewMockInvoker(url1, 1)
 	invoker2 := NewMockInvoker(url2, 2)
 	invoker3 := NewMockInvoker(url3, 3)
 	invokers := []protocol.Invoker{invoker1, invoker2, invoker3}
 	inv := &invocation.RPCInvocation{}
 	rule := base64.URLEncoding.EncodeToString([]byte(" => " + " host = " + localIP))
-	curl, _ := common.NewURL(context.TODO(), "consumer://"+localIP+"/com.foo.BarService")
-	router, _ := NewConditionRouterFactory().Router(getRouteUrl(rule))
-	fileredInvokers := router.(*ConditionRouter).Route(invokers, curl, inv)
+	curl, _ := common.NewURL("consumer://" + localIP + "/com.foo.BarService")
+	router, _ := newConditionRouterFactory().NewRouter(getRouteUrl(rule))
+	fileredInvokers := router.(*ConditionRouter).Route(invokers, &curl, inv)
 	assert.Equal(t, 2, len(fileredInvokers))
 	assert.Equal(t, invoker2, fileredInvokers[0])
 	assert.Equal(t, invoker3, fileredInvokers[1])
@@ -278,18 +287,18 @@ func TestRoute_Empty_HostFilter(t *testing.T) {
 
 func TestRoute_False_HostFilter(t *testing.T) {
 	localIP, _ := gxnet.GetLocalIP()
-	url1, _ := common.NewURL(context.TODO(), "dubbo://10.20.3.3:20880/com.foo.BarService")
-	url2, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP))
-	url3, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP))
+	url1, _ := common.NewURL("dubbo://10.20.3.3:20880/com.foo.BarService")
+	url2, _ := common.NewURL(fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP))
+	url3, _ := common.NewURL(fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP))
 	invoker1 := NewMockInvoker(url1, 1)
 	invoker2 := NewMockInvoker(url2, 2)
 	invoker3 := NewMockInvoker(url3, 3)
 	invokers := []protocol.Invoker{invoker1, invoker2, invoker3}
 	inv := &invocation.RPCInvocation{}
 	rule := base64.URLEncoding.EncodeToString([]byte("true => " + " host = " + localIP))
-	curl, _ := common.NewURL(context.TODO(), "consumer://"+localIP+"/com.foo.BarService")
-	router, _ := NewConditionRouterFactory().Router(getRouteUrl(rule))
-	fileredInvokers := router.(*ConditionRouter).Route(invokers, curl, inv)
+	curl, _ := common.NewURL("consumer://" + localIP + "/com.foo.BarService")
+	router, _ := newConditionRouterFactory().NewRouter(getRouteUrl(rule))
+	fileredInvokers := router.(*ConditionRouter).Route(invokers, &curl, inv)
 	assert.Equal(t, 2, len(fileredInvokers))
 	assert.Equal(t, invoker2, fileredInvokers[0])
 	assert.Equal(t, invoker3, fileredInvokers[1])
@@ -297,18 +306,18 @@ func TestRoute_False_HostFilter(t *testing.T) {
 
 func TestRoute_Placeholder(t *testing.T) {
 	localIP, _ := gxnet.GetLocalIP()
-	url1, _ := common.NewURL(context.TODO(), "dubbo://10.20.3.3:20880/com.foo.BarService")
-	url2, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP))
-	url3, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP))
+	url1, _ := common.NewURL("dubbo://10.20.3.3:20880/com.foo.BarService")
+	url2, _ := common.NewURL(fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP))
+	url3, _ := common.NewURL(fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP))
 	invoker1 := NewMockInvoker(url1, 1)
 	invoker2 := NewMockInvoker(url2, 2)
 	invoker3 := NewMockInvoker(url3, 3)
 	invokers := []protocol.Invoker{invoker1, invoker2, invoker3}
 	inv := &invocation.RPCInvocation{}
 	rule := base64.URLEncoding.EncodeToString([]byte("host = " + localIP + " => " + " host = $host"))
-	curl, _ := common.NewURL(context.TODO(), "consumer://"+localIP+"/com.foo.BarService")
-	router, _ := NewConditionRouterFactory().Router(getRouteUrl(rule))
-	fileredInvokers := router.(*ConditionRouter).Route(invokers, curl, inv)
+	curl, _ := common.NewURL("consumer://" + localIP + "/com.foo.BarService")
+	router, _ := newConditionRouterFactory().NewRouter(getRouteUrl(rule))
+	fileredInvokers := router.(*ConditionRouter).Route(invokers, &curl, inv)
 	assert.Equal(t, 2, len(fileredInvokers))
 	assert.Equal(t, invoker2, fileredInvokers[0])
 	assert.Equal(t, invoker3, fileredInvokers[1])
@@ -316,34 +325,44 @@ func TestRoute_Placeholder(t *testing.T) {
 
 func TestRoute_NoForce(t *testing.T) {
 	localIP, _ := gxnet.GetLocalIP()
-	url1, _ := common.NewURL(context.TODO(), "dubbo://10.20.3.3:20880/com.foo.BarService")
-	url2, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP))
-	url3, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP))
+	url1, _ := common.NewURL("dubbo://10.20.3.3:20880/com.foo.BarService")
+	url2, _ := common.NewURL(fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP))
+	url3, _ := common.NewURL(fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP))
 	invoker1 := NewMockInvoker(url1, 1)
 	invoker2 := NewMockInvoker(url2, 2)
 	invoker3 := NewMockInvoker(url3, 3)
 	invokers := []protocol.Invoker{invoker1, invoker2, invoker3}
 	inv := &invocation.RPCInvocation{}
 	rule := base64.URLEncoding.EncodeToString([]byte("host = " + localIP + " => " + " host = 1.2.3.4"))
-	curl, _ := common.NewURL(context.TODO(), "consumer://"+localIP+"/com.foo.BarService")
-	router, _ := NewConditionRouterFactory().Router(getRouteUrlWithNoForce(rule))
-	fileredInvokers := router.(*ConditionRouter).Route(invokers, curl, inv)
+	curl, _ := common.NewURL("consumer://" + localIP + "/com.foo.BarService")
+	router, _ := newConditionRouterFactory().NewRouter(getRouteUrlWithNoForce(rule))
+	fileredInvokers := router.(*ConditionRouter).Route(invokers, &curl, inv)
 	assert.Equal(t, invokers, fileredInvokers)
 }
 
 func TestRoute_Force(t *testing.T) {
 	localIP, _ := gxnet.GetLocalIP()
-	url1, _ := common.NewURL(context.TODO(), "dubbo://10.20.3.3:20880/com.foo.BarService")
-	url2, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP))
-	url3, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP))
+	url1, _ := common.NewURL("dubbo://10.20.3.3:20880/com.foo.BarService")
+	url2, _ := common.NewURL(fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP))
+	url3, _ := common.NewURL(fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP))
 	invoker1 := NewMockInvoker(url1, 1)
 	invoker2 := NewMockInvoker(url2, 2)
 	invoker3 := NewMockInvoker(url3, 3)
 	invokers := []protocol.Invoker{invoker1, invoker2, invoker3}
 	inv := &invocation.RPCInvocation{}
 	rule := base64.URLEncoding.EncodeToString([]byte("host = " + localIP + " => " + " host = 1.2.3.4"))
-	curl, _ := common.NewURL(context.TODO(), "consumer://"+localIP+"/com.foo.BarService")
-	router, _ := NewConditionRouterFactory().Router(getRouteUrlWithForce(rule, "true"))
-	fileredInvokers := router.(*ConditionRouter).Route(invokers, curl, inv)
+	curl, _ := common.NewURL("consumer://" + localIP + "/com.foo.BarService")
+	router, _ := newConditionRouterFactory().NewRouter(getRouteUrlWithForce(rule, "true"))
+	fileredInvokers := router.(*ConditionRouter).Route(invokers, &curl, inv)
 	assert.Equal(t, 0, len(fileredInvokers))
 }
+
+func TestNewConditionRouterFactory(t *testing.T) {
+	factory := newConditionRouterFactory()
+	assert.NotNil(t, factory)
+}
+
+func TestNewAppRouterFactory(t *testing.T) {
+	factory := newAppRouterFactory()
+	assert.NotNil(t, factory)
+}
diff --git a/cluster/router/condition/file.go b/cluster/router/condition/file.go
new file mode 100644
index 0000000000000000000000000000000000000000..efeec53efc840d93c4b6906adfd19820a57b36fd
--- /dev/null
+++ b/cluster/router/condition/file.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 condition
+
+import (
+	"encoding/base64"
+	"net/url"
+	"strconv"
+	"strings"
+	"sync"
+)
+
+import (
+	perrors "github.com/pkg/errors"
+)
+
+import (
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/common/constant"
+)
+
+// FileConditionRouter Use for parse config file of condition router
+type FileConditionRouter struct {
+	listenableRouter
+	parseOnce sync.Once
+	url       common.URL
+}
+
+// NewFileConditionRouter Create file condition router instance with content ( from config file)
+func NewFileConditionRouter(content []byte) (*FileConditionRouter, error) {
+	fileRouter := &FileConditionRouter{}
+	rule, err := Parse(string(content))
+	if err != nil {
+		return nil, perrors.Errorf("yaml.Unmarshal() failed , error:%v", perrors.WithStack(err))
+	}
+
+	if !rule.Valid {
+		return nil, perrors.Errorf("rule content is not verify for condition router , error:%v", perrors.WithStack(err))
+	}
+
+	fileRouter.generateConditions(rule)
+
+	return fileRouter, nil
+}
+
+// URL Return URL in file condition router n
+func (f *FileConditionRouter) URL() common.URL {
+	f.parseOnce.Do(func() {
+		routerRule := f.routerRule
+		rule := parseCondition(routerRule.Conditions)
+		f.url = *common.NewURLWithOptions(
+			common.WithProtocol(constant.CONDITION_ROUTE_PROTOCOL),
+			common.WithIp(constant.ANYHOST_VALUE),
+			common.WithParams(url.Values{}),
+			common.WithParamsValue(constant.RouterForce, strconv.FormatBool(routerRule.Force)),
+			common.WithParamsValue(constant.RouterPriority, strconv.Itoa(routerRule.Priority)),
+			common.WithParamsValue(constant.RULE_KEY, base64.URLEncoding.EncodeToString([]byte(rule))),
+			common.WithParamsValue(constant.ROUTER_KEY, constant.CONDITION_ROUTE_PROTOCOL),
+			common.WithParamsValue(constant.CATEGORY_KEY, constant.ROUTERS_CATEGORY))
+	})
+	return f.url
+}
+
+func parseCondition(conditions []string) string {
+	var (
+		when string
+		then string
+	)
+	for _, condition := range conditions {
+		condition = strings.Trim(condition, " ")
+		if strings.Contains(condition, "=>") {
+			array := strings.SplitN(condition, "=>", 2)
+			consumer := strings.Trim(array[0], " ")
+			provider := strings.Trim(array[1], " ")
+			if len(consumer) != 0 {
+				if len(when) != 0 {
+					when = strings.Join([]string{when, consumer}, " & ")
+				} else {
+					when = consumer
+				}
+			}
+			if len(provider) != 0 {
+				if len(then) != 0 {
+					then = strings.Join([]string{then, provider}, " & ")
+				} else {
+					then = provider
+				}
+			}
+
+		}
+
+	}
+
+	return strings.Join([]string{when, then}, " => ")
+}
diff --git a/cluster/router/condition/file_test.go b/cluster/router/condition/file_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..3092b12ff88dcacc9108c7cdd46ba1ac9f74eb2b
--- /dev/null
+++ b/cluster/router/condition/file_test.go
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package condition
+
+import (
+	"testing"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+func TestLoadYmlConfig(t *testing.T) {
+	router, e := NewFileConditionRouter([]byte(`priority: 1
+force: true
+conditions :
+  - "a => b"
+  - "c => d"`))
+	assert.Nil(t, e)
+	assert.NotNil(t, router)
+	assert.Equal(t, router.routerRule.Priority, 1)
+	assert.Equal(t, router.routerRule.Force, true)
+	assert.Equal(t, len(router.routerRule.Conditions), 2)
+}
+
+func TestParseCondition(t *testing.T) {
+	s := make([]string, 2)
+	s = append(s, "a => b")
+	s = append(s, "c => d")
+	condition := parseCondition(s)
+	assert.Equal(t, "a & c => b & d", condition)
+}
+
+func TestFileRouterURL(t *testing.T) {
+	router, e := NewFileConditionRouter([]byte(`priority: 1
+force: true
+conditions :
+  - "a => b"
+  - "c => d"`))
+	assert.Nil(t, e)
+	assert.NotNil(t, router)
+	assert.Equal(t, "condition://0.0.0.0:?category=routers&force=true&priority=1&router=condition&rule=YSAmIGMgPT4gYiAmIGQ%3D", router.URL().String())
+}
diff --git a/cluster/router/condition/listenable_router.go b/cluster/router/condition/listenable_router.go
new file mode 100644
index 0000000000000000000000000000000000000000..ba2fbb0eb2f482dfde215c1b078ecad60e66bc14
--- /dev/null
+++ b/cluster/router/condition/listenable_router.go
@@ -0,0 +1,151 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package condition
+
+import (
+	"fmt"
+)
+
+import (
+	perrors "github.com/pkg/errors"
+)
+
+import (
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/common/config"
+	"github.com/apache/dubbo-go/common/constant"
+	"github.com/apache/dubbo-go/common/logger"
+	"github.com/apache/dubbo-go/config_center"
+	"github.com/apache/dubbo-go/protocol"
+	"github.com/apache/dubbo-go/remoting"
+)
+
+const (
+	// Default priority for listenable router, use the maximum int64 value
+	listenableRouterDefaultPriority = ^int64(0)
+)
+
+// listenableRouter Abstract router which listens to dynamic configuration
+type listenableRouter struct {
+	conditionRouters []*ConditionRouter
+	routerRule       *RouterRule
+	url              *common.URL
+	force            bool
+	priority         int64
+}
+
+// RouterRule Get RouterRule instance from listenableRouter
+func (l *listenableRouter) RouterRule() *RouterRule {
+	return l.routerRule
+}
+
+func newListenableRouter(url *common.URL, ruleKey string) (*AppRouter, error) {
+	if ruleKey == "" {
+		return nil, perrors.Errorf("NewListenableRouter ruleKey is nil, can't create Listenable router")
+	}
+	l := &AppRouter{}
+
+	l.url = url
+	l.priority = listenableRouterDefaultPriority
+
+	routerKey := ruleKey + constant.ConditionRouterRuleSuffix
+	//add listener
+	dynamicConfiguration := config.GetEnvInstance().GetDynamicConfiguration()
+	if dynamicConfiguration == nil {
+		return nil, perrors.Errorf("Get dynamicConfiguration fail, dynamicConfiguration is nil, init config center plugin please")
+	}
+
+	dynamicConfiguration.AddListener(routerKey, l)
+	//get rule
+	rule, err := dynamicConfiguration.GetRule(routerKey, config_center.WithGroup(config_center.DEFAULT_GROUP))
+	if len(rule) == 0 || err != nil {
+		return nil, perrors.Errorf("Get rule fail, config rule{%s},  error{%v}", rule, err)
+	}
+	l.Process(&config_center.ConfigChangeEvent{
+		Key:        routerKey,
+		Value:      rule,
+		ConfigType: remoting.EventTypeUpdate})
+
+	logger.Info("Init app router success")
+	return l, nil
+}
+
+// Process Process config change event , generate routers and set them to the listenableRouter instance
+func (l *listenableRouter) Process(event *config_center.ConfigChangeEvent) {
+	logger.Infof("Notification of condition rule, change type is:[%s] , raw rule is:[%v]", event.ConfigType, event.Value)
+	if remoting.EventTypeDel == event.ConfigType {
+		l.routerRule = nil
+		if l.conditionRouters != nil {
+			l.conditionRouters = l.conditionRouters[:0]
+		}
+		return
+	}
+	content, ok := event.Value.(string)
+	if !ok {
+		msg := fmt.Sprintf("Convert event content fail,raw content:[%s] ", event.Value)
+		logger.Error(msg)
+		return
+	}
+
+	routerRule, err := Parse(content)
+	if err != nil {
+		logger.Errorf("Parse condition router rule fail,error:[%s] ", err)
+		return
+	}
+	l.generateConditions(routerRule)
+}
+
+func (l *listenableRouter) generateConditions(rule *RouterRule) {
+	if rule == nil || !rule.Valid {
+		return
+	}
+	l.conditionRouters = make([]*ConditionRouter, 0, len(rule.Conditions))
+	l.routerRule = rule
+	for _, c := range rule.Conditions {
+		router, e := NewConditionRouterWithRule(c)
+		if e != nil {
+			logger.Errorf("Create condition router with rule fail,raw rule:[%s] ", c)
+			continue
+		}
+		router.Force = rule.Force
+		router.enabled = rule.Enabled
+		l.conditionRouters = append(l.conditionRouters, router)
+	}
+}
+
+// Route Determine the target invokers list.
+func (l *listenableRouter) Route(invokers []protocol.Invoker, url *common.URL, invocation protocol.Invocation) []protocol.Invoker {
+	if len(invokers) == 0 || len(l.conditionRouters) == 0 {
+		return invokers
+	}
+	//We will check enabled status inside each router.
+	for _, r := range l.conditionRouters {
+		invokers = r.Route(invokers, url, invocation)
+	}
+	return invokers
+}
+
+// Priority Return Priority in listenable router
+func (l *listenableRouter) Priority() int64 {
+	return l.priority
+}
+
+// URL Return URL in listenable router
+func (l *listenableRouter) URL() common.URL {
+	return *l.url
+}
diff --git a/cluster/router/condition_router.go b/cluster/router/condition/router.go
similarity index 62%
rename from cluster/router/condition_router.go
rename to cluster/router/condition/router.go
index a196ceb5771422f06a820986a02499f9fe3523dc..c5d46444bde921386d14a8be7eb0a89d855f8ece 100644
--- a/cluster/router/condition_router.go
+++ b/cluster/router/condition/router.go
@@ -15,54 +15,55 @@
  * limitations under the License.
  */
 
-package router
+package condition
 
 import (
-	"reflect"
 	"regexp"
 	"strings"
 )
 
 import (
-	"github.com/dubbogo/gost/container/gxset"
-	gxnet "github.com/dubbogo/gost/net"
 	perrors "github.com/pkg/errors"
 )
 
 import (
+	matcher "github.com/apache/dubbo-go/cluster/router/match"
 	"github.com/apache/dubbo-go/common"
 	"github.com/apache/dubbo-go/common/constant"
 	"github.com/apache/dubbo-go/common/logger"
 	"github.com/apache/dubbo-go/protocol"
+	"github.com/dubbogo/gost/container/set"
+	"github.com/dubbogo/gost/net"
 )
 
 const (
-	ROUTE_PATTERN = `([&!=,]*)\\s*([^&!=,\\s]+)`
-	FORCE         = "force"
-	PRIORITY      = "priority"
+	//pattern route pattern regex
+	pattern = `([&!=,]*)\\s*([^&!=,\\s]+)`
 )
 
-//ConditionRouter condition router struct
+var (
+	routerPatternReg = regexp.MustCompile(`([&!=,]*)\s*([^&!=,\s]+)`)
+)
+
+// ConditionRouter Condition router struct
 type ConditionRouter struct {
 	Pattern       string
-	Url           *common.URL
-	Priority      int64
+	url           *common.URL
+	priority      int64
 	Force         bool
+	enabled       bool
 	WhenCondition map[string]MatchPair
 	ThenCondition map[string]MatchPair
 }
 
-func newConditionRouter(url *common.URL) (*ConditionRouter, error) {
+// NewConditionRouterWithRule Init condition router by raw rule
+func NewConditionRouterWithRule(rule string) (*ConditionRouter, error) {
 	var (
 		whenRule string
 		thenRule string
 		when     map[string]MatchPair
 		then     map[string]MatchPair
 	)
-	rule, err := url.GetParamAndDecoded(constant.RULE_KEY)
-	if err != nil || len(rule) == 0 {
-		return nil, perrors.Errorf("Illegal route rule!")
-	}
 	rule = strings.Replace(rule, "consumer.", "", -1)
 	rule = strings.Replace(rule, "provider.", "", -1)
 	i := strings.Index(rule, "=>")
@@ -95,30 +96,61 @@ func newConditionRouter(url *common.URL) (*ConditionRouter, error) {
 		then = t
 	}
 	return &ConditionRouter{
-		ROUTE_PATTERN,
-		url,
-		url.GetParamInt(PRIORITY, 0),
-		url.GetParamBool(FORCE, false),
-		when,
-		then,
+		Pattern:       pattern,
+		WhenCondition: when,
+		ThenCondition: then,
 	}, nil
 }
 
-//Router determine the target server list.
-func (c *ConditionRouter) Route(invokers []protocol.Invoker, url common.URL, invocation protocol.Invocation) []protocol.Invoker {
-	if len(invokers) == 0 {
-		return invokers
+// NewConditionRouter Init condition router by URL
+func NewConditionRouter(url *common.URL) (*ConditionRouter, error) {
+	if url == nil {
+		return nil, perrors.Errorf("Illegal route URL!")
+	}
+	rule, err := url.GetParamAndDecoded(constant.RULE_KEY)
+	if err != nil || len(rule) == 0 {
+		return nil, perrors.Errorf("Illegal route rule!")
 	}
-	isMatchWhen, err := c.MatchWhen(url, invocation)
+
+	router, err := NewConditionRouterWithRule(rule)
 	if err != nil {
+		return nil, err
+	}
 
-		var urls []string
-		for _, invo := range invokers {
-			urls = append(urls, reflect.TypeOf(invo).String())
-		}
-		logger.Warnf("Failed to execute condition router rule: %s , invokers: [%s], cause: %v", c.Url.String(), strings.Join(urls, ","), err)
+	router.url = url
+	router.priority = url.GetParamInt(constant.RouterPriority, 0)
+	router.Force = url.GetParamBool(constant.RouterForce, false)
+	router.enabled = url.GetParamBool(constant.RouterEnabled, true)
+
+	return router, nil
+}
+
+// Priority Return Priority in condition router
+func (c *ConditionRouter) Priority() int64 {
+	return c.priority
+}
+
+// URL Return URL in condition router
+func (c *ConditionRouter) URL() common.URL {
+	return *c.url
+}
+
+// Enabled Return is condition router is enabled
+// true: enabled
+// false: disabled
+func (c *ConditionRouter) Enabled() bool {
+	return c.enabled
+}
+
+// Route Determine the target invokers list.
+func (c *ConditionRouter) Route(invokers []protocol.Invoker, url *common.URL, invocation protocol.Invocation) []protocol.Invoker {
+	if !c.Enabled() {
+		return invokers
+	}
+	if len(invokers) == 0 {
 		return invokers
 	}
+	isMatchWhen := c.MatchWhen(url, invocation)
 	if !isMatchWhen {
 		return invokers
 	}
@@ -126,17 +158,9 @@ func (c *ConditionRouter) Route(invokers []protocol.Invoker, url common.URL, inv
 	if len(c.ThenCondition) == 0 {
 		return result
 	}
-	localIP, _ := gxnet.GetLocalIP()
 	for _, invoker := range invokers {
-		isMatchThen, err := c.MatchThen(invoker.GetUrl(), url)
-		if err != nil {
-			var urls []string
-			for _, invo := range invokers {
-				urls = append(urls, reflect.TypeOf(invo).String())
-			}
-			logger.Warnf("Failed to execute condition router rule: %s , invokers: [%s], cause: %v", c.Url.String(), strings.Join(urls, ","), err)
-			return invokers
-		}
+		invokerUrl := invoker.GetUrl()
+		isMatchThen := c.MatchThen(&invokerUrl, url)
 		if isMatchThen {
 			result = append(result, invoker)
 		}
@@ -145,6 +169,7 @@ func (c *ConditionRouter) Route(invokers []protocol.Invoker, url common.URL, inv
 		return result
 	} else if c.Force {
 		rule, _ := url.GetParamAndDecoded(constant.RULE_KEY)
+		localIP, _ := gxnet.GetLocalIP()
 		logger.Warnf("The route result is empty and force execute. consumer: %s, service: %s, router: %s", localIP, url.Service(), rule)
 		return result
 	}
@@ -156,14 +181,12 @@ func parseRule(rule string) (map[string]MatchPair, error) {
 	if len(rule) == 0 {
 		return condition, nil
 	}
-	var pair MatchPair
+
+	var (
+		pair MatchPair
+	)
 	values := gxset.NewSet()
-	reg := regexp.MustCompile(`([&!=,]*)\s*([^&!=,\s]+)`)
-	var startIndex = 0
-	if indexTuple := reg.FindIndex([]byte(rule)); len(indexTuple) > 0 {
-		startIndex = indexTuple[0]
-	}
-	matches := reg.FindAllSubmatch([]byte(rule), -1)
+	matches := routerPatternReg.FindAllSubmatch([]byte(rule), -1)
 	for _, groups := range matches {
 		separator := string(groups[1])
 		content := string(groups[2])
@@ -186,22 +209,26 @@ func parseRule(rule string) (map[string]MatchPair, error) {
 			}
 		case "=":
 			if &pair == nil {
+				var startIndex = getStartIndex(rule)
 				return nil, perrors.Errorf("Illegal route rule \"%s\", The error char '%s' at index %d before \"%d\".", rule, separator, startIndex, startIndex)
 			}
 			values = pair.Matches
 			values.Add(content)
 		case "!=":
 			if &pair == nil {
+				var startIndex = getStartIndex(rule)
 				return nil, perrors.Errorf("Illegal route rule \"%s\", The error char '%s' at index %d before \"%d\".", rule, separator, startIndex, startIndex)
 			}
 			values = pair.Mismatches
 			values.Add(content)
 		case ",":
 			if values.Empty() {
+				var startIndex = getStartIndex(rule)
 				return nil, perrors.Errorf("Illegal route rule \"%s\", The error char '%s' at index %d before \"%d\".", rule, separator, startIndex, startIndex)
 			}
 			values.Add(content)
 		default:
+			var startIndex = getStartIndex(rule)
 			return nil, perrors.Errorf("Illegal route rule \"%s\", The error char '%s' at index %d before \"%d\".", rule, separator, startIndex, startIndex)
 
 		}
@@ -209,25 +236,33 @@ func parseRule(rule string) (map[string]MatchPair, error) {
 	return condition, nil
 }
 
-//
-func (c *ConditionRouter) MatchWhen(url common.URL, invocation protocol.Invocation) (bool, error) {
-	condition, err := MatchCondition(c.WhenCondition, &url, nil, invocation)
-	return len(c.WhenCondition) == 0 || condition, err
+func getStartIndex(rule string) int {
+	if indexTuple := routerPatternReg.FindIndex([]byte(rule)); len(indexTuple) > 0 {
+		return indexTuple[0]
+	}
+	return -1
+}
+
+// MatchWhen MatchWhen
+func (c *ConditionRouter) MatchWhen(url *common.URL, invocation protocol.Invocation) bool {
+	condition := matchCondition(c.WhenCondition, url, nil, invocation)
+	return len(c.WhenCondition) == 0 || condition
 }
 
-//MatchThen MatchThen
-func (c *ConditionRouter) MatchThen(url common.URL, param common.URL) (bool, error) {
-	condition, err := MatchCondition(c.ThenCondition, &url, &param, nil)
-	return len(c.ThenCondition) > 0 && condition, err
+// MatchThen MatchThen
+func (c *ConditionRouter) MatchThen(url *common.URL, param *common.URL) bool {
+	condition := matchCondition(c.ThenCondition, url, param, nil)
+	return len(c.ThenCondition) > 0 && condition
 }
 
-//MatchCondition MatchCondition
-func MatchCondition(pairs map[string]MatchPair, url *common.URL, param *common.URL, invocation protocol.Invocation) (bool, error) {
+// MatchCondition MatchCondition
+func matchCondition(pairs map[string]MatchPair, url *common.URL, param *common.URL, invocation protocol.Invocation) bool {
 	sample := url.ToMap()
 	if sample == nil {
-		return true, perrors.Errorf("url is not allowed be nil")
+		// because url.ToMap() may return nil, but it should continue to process make condition
+		sample = make(map[string]string)
 	}
-	result := false
+	var result bool
 	for key, matchPair := range pairs {
 		var sampleValue string
 
@@ -241,21 +276,22 @@ func MatchCondition(pairs map[string]MatchPair, url *common.URL, param *common.U
 		}
 		if len(sampleValue) > 0 {
 			if !matchPair.isMatch(sampleValue, param) {
-				return false, nil
-			} else {
-				result = true
+				return false
 			}
+
+			result = true
 		} else {
 			if !(matchPair.Matches.Empty()) {
-				return false, nil
-			} else {
-				result = true
+				return false
 			}
+
+			result = true
 		}
 	}
-	return result, nil
+	return result
 }
 
+// MatchPair Match key pair , condition process
 type MatchPair struct {
 	Matches    *gxset.HashSet
 	Mismatches *gxset.HashSet
@@ -265,7 +301,7 @@ func (pair MatchPair) isMatch(value string, param *common.URL) bool {
 	if !pair.Matches.Empty() && pair.Mismatches.Empty() {
 
 		for match := range pair.Matches.Items {
-			if isMatchGlobPattern(match.(string), value, param) {
+			if matcher.IsMatchGlobalPattern(match.(string), value, param) {
 				return true
 			}
 		}
@@ -274,20 +310,21 @@ func (pair MatchPair) isMatch(value string, param *common.URL) bool {
 	if !pair.Mismatches.Empty() && pair.Matches.Empty() {
 
 		for mismatch := range pair.Mismatches.Items {
-			if isMatchGlobPattern(mismatch.(string), value, param) {
+			if matcher.IsMatchGlobalPattern(mismatch.(string), value, param) {
 				return false
 			}
 		}
 		return true
 	}
 	if !pair.Mismatches.Empty() && !pair.Matches.Empty() {
+		//when both mismatches and matches contain the same value, then using mismatches first
 		for mismatch := range pair.Mismatches.Items {
-			if isMatchGlobPattern(mismatch.(string), value, param) {
+			if matcher.IsMatchGlobalPattern(mismatch.(string), value, param) {
 				return false
 			}
 		}
 		for match := range pair.Matches.Items {
-			if isMatchGlobPattern(match.(string), value, param) {
+			if matcher.IsMatchGlobalPattern(match.(string), value, param) {
 				return true
 			}
 		}
@@ -295,31 +332,3 @@ func (pair MatchPair) isMatch(value string, param *common.URL) bool {
 	}
 	return false
 }
-
-func isMatchGlobPattern(pattern string, value string, param *common.URL) bool {
-	if param != nil && strings.HasPrefix(pattern, "$") {
-		pattern = param.GetRawParam(pattern[1:])
-	}
-	if "*" == pattern {
-		return true
-	}
-	if len(pattern) == 0 && len(value) == 0 {
-		return true
-	}
-	if len(pattern) == 0 || len(value) == 0 {
-		return false
-	}
-	i := strings.LastIndex(pattern, "*")
-	switch i {
-	case -1:
-		return value == pattern
-	case len(pattern) - 1:
-		return strings.HasPrefix(value, pattern[0:i])
-	case 0:
-		return strings.HasSuffix(value, pattern[:i+1])
-	default:
-		prefix := pattern[0:1]
-		suffix := pattern[i+1:]
-		return strings.HasPrefix(value, prefix) && strings.HasSuffix(value, suffix)
-	}
-}
diff --git a/cluster/router/condition/router_rule.go b/cluster/router/condition/router_rule.go
new file mode 100644
index 0000000000000000000000000000000000000000..1374cf9de2585f78a27e3de99f356c6900268927
--- /dev/null
+++ b/cluster/router/condition/router_rule.go
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package condition
+
+import (
+	"gopkg.in/yaml.v2"
+)
+
+import (
+	"github.com/apache/dubbo-go/cluster/router"
+)
+
+// RouterRule RouterRule config read from config file or config center
+type RouterRule struct {
+	router.BaseRouterRule `yaml:",inline""`
+	Conditions            []string
+}
+
+/* Parse Router raw rule parser
+ * example :
+ * scope: application
+ * runtime: true
+ * force: false
+ * conditions:
+ *   - >
+ *     method!=sayHello =>
+ *   - >
+ *     ip=127.0.0.1
+ *     =>
+ *     1.1.1.1
+ */
+func Parse(rawRule string) (*RouterRule, error) {
+	r := &RouterRule{}
+	err := yaml.Unmarshal([]byte(rawRule), r)
+	if err != nil {
+		return r, err
+	}
+	r.RawRule = rawRule
+	if len(r.Conditions) != 0 {
+		r.Valid = true
+	}
+
+	return r, nil
+}
diff --git a/cluster/router/condition/router_rule_test.go b/cluster/router/condition/router_rule_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..5acc7283917a7aa662b60cd90daba89d312db0cd
--- /dev/null
+++ b/cluster/router/condition/router_rule_test.go
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package condition
+
+import (
+	"testing"
+)
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+func TestParse(t *testing.T) {
+	testyml := `
+scope: application
+runtime: true
+force: false
+conditions:
+  - >
+    method!=sayHello =>
+  - >
+    ip=127.0.0.1
+    =>
+    1.1.1.1`
+	rule, e := Parse(testyml)
+
+	assert.Nil(t, e)
+	assert.NotNil(t, rule)
+	assert.Equal(t, 2, len(rule.Conditions))
+	assert.Equal(t, "application", rule.Scope)
+	assert.True(t, rule.Runtime)
+	assert.Equal(t, false, rule.Force)
+	assert.Equal(t, testyml, rule.RawRule)
+	assert.True(t, true, rule.Valid)
+	assert.Equal(t, false, rule.Enabled)
+	assert.Equal(t, false, rule.Dynamic)
+	assert.Equal(t, "", rule.Key)
+}
diff --git a/cluster/router/router_factory.go b/cluster/router/health_checker.go
similarity index 64%
rename from cluster/router/router_factory.go
rename to cluster/router/health_checker.go
index a9794cb885badae98445ef4d7c0bbc2230d25d5f..d9e3087a272dd500cdd1dc9dc6680d436891f88b 100644
--- a/cluster/router/router_factory.go
+++ b/cluster/router/health_checker.go
@@ -18,20 +18,11 @@
 package router
 
 import (
-	"github.com/apache/dubbo-go/cluster"
-	"github.com/apache/dubbo-go/common"
-	"github.com/apache/dubbo-go/common/extension"
+	"github.com/apache/dubbo-go/protocol"
 )
 
-func init() {
-	extension.SetRouterFactory("condition", NewConditionRouterFactory)
-}
-
-type ConditionRouterFactory struct{}
-
-func NewConditionRouterFactory() cluster.RouterFactory {
-	return ConditionRouterFactory{}
-}
-func (c ConditionRouterFactory) Router(url *common.URL) (cluster.Router, error) {
-	return newConditionRouter(url)
+// HealthChecker is used to determine whether the invoker is healthy or not
+type HealthChecker interface {
+	// IsHealthy evaluates the healthy state on the given Invoker
+	IsHealthy(invoker protocol.Invoker) bool
 }
diff --git a/cluster/router/healthcheck/default_health_check.go b/cluster/router/healthcheck/default_health_check.go
new file mode 100644
index 0000000000000000000000000000000000000000..a26f86ddac45aa6e999cd4453aa296d0786a02ba
--- /dev/null
+++ b/cluster/router/healthcheck/default_health_check.go
@@ -0,0 +1,117 @@
+/*
+ * 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 healthcheck
+
+import (
+	"math"
+)
+
+import (
+	"github.com/apache/dubbo-go/cluster/router"
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/common/constant"
+	"github.com/apache/dubbo-go/common/extension"
+	"github.com/apache/dubbo-go/common/logger"
+	"github.com/apache/dubbo-go/protocol"
+)
+
+func init() {
+	extension.SethealthChecker(constant.DEFAULT_HEALTH_CHECKER, NewDefaultHealthChecker)
+}
+
+// DefaultHealthChecker is the default implementation of HealthChecker, which determines the health status of
+// the invoker based on the number of successive bad request and the current active request.
+type DefaultHealthChecker struct {
+	// the limit of outstanding request
+	outStandingRequestConutLimit int32
+	// the threshold of successive-failure-request
+	requestSuccessiveFailureThreshold int32
+	// value of circuit-tripped timeout factor
+	circuitTrippedTimeoutFactor int32
+}
+
+// IsHealthy evaluates the healthy state on the given Invoker based on the number of successive bad request
+// and the current active request
+func (c *DefaultHealthChecker) IsHealthy(invoker protocol.Invoker) bool {
+	urlStatus := protocol.GetURLStatus(invoker.GetUrl())
+	if c.isCircuitBreakerTripped(urlStatus) || urlStatus.GetActive() > c.GetOutStandingRequestConutLimit() {
+		logger.Debugf("Invoker [%s] is currently in circuitbreaker tripped state", invoker.GetUrl().Key())
+		return false
+	}
+	return true
+}
+
+// isCircuitBreakerTripped determine whether the invoker is in the tripped state by the number of successive bad request
+func (c *DefaultHealthChecker) isCircuitBreakerTripped(status *protocol.RPCStatus) bool {
+	circuitBreakerTimeout := c.getCircuitBreakerTimeout(status)
+	currentTime := protocol.CurrentTimeMillis()
+	if circuitBreakerTimeout <= 0 {
+		return false
+	}
+	return circuitBreakerTimeout > currentTime
+}
+
+// getCircuitBreakerTimeout get the timestamp recovered from tripped state, the unit is millisecond
+func (c *DefaultHealthChecker) getCircuitBreakerTimeout(status *protocol.RPCStatus) int64 {
+	sleepWindow := c.getCircuitBreakerSleepWindowTime(status)
+	if sleepWindow <= 0 {
+		return 0
+	}
+	return status.GetLastRequestFailedTimestamp() + sleepWindow
+}
+
+// getCircuitBreakerSleepWindowTime get the sleep window time of invoker, the unit is millisecond
+func (c *DefaultHealthChecker) getCircuitBreakerSleepWindowTime(status *protocol.RPCStatus) int64 {
+
+	successiveFailureCount := status.GetSuccessiveRequestFailureCount()
+	diff := successiveFailureCount - c.GetRequestSuccessiveFailureThreshold()
+	if diff < 0 {
+		return 0
+	} else if diff > constant.DEFAULT_SUCCESSIVE_FAILED_REQUEST_MAX_DIFF {
+		diff = constant.DEFAULT_SUCCESSIVE_FAILED_REQUEST_MAX_DIFF
+	}
+	sleepWindow := (1 << diff) * c.GetCircuitTrippedTimeoutFactor()
+	if sleepWindow > constant.MAX_CIRCUIT_TRIPPED_TIMEOUT_IN_MS {
+		sleepWindow = constant.MAX_CIRCUIT_TRIPPED_TIMEOUT_IN_MS
+	}
+	return int64(sleepWindow)
+}
+
+// GetOutStandingRequestConutLimit return the requestSuccessiveFailureThreshold bound to this DefaultHealthChecker
+func (c *DefaultHealthChecker) GetRequestSuccessiveFailureThreshold() int32 {
+	return c.requestSuccessiveFailureThreshold
+}
+
+// GetOutStandingRequestConutLimit return the circuitTrippedTimeoutFactor bound to this DefaultHealthChecker
+func (c *DefaultHealthChecker) GetCircuitTrippedTimeoutFactor() int32 {
+	return c.circuitTrippedTimeoutFactor
+}
+
+// GetOutStandingRequestConutLimit return the outStandingRequestConutLimit bound to this DefaultHealthChecker
+func (c *DefaultHealthChecker) GetOutStandingRequestConutLimit() int32 {
+	return c.outStandingRequestConutLimit
+}
+
+// NewDefaultHealthChecker constructs a new DefaultHealthChecker based on the url
+func NewDefaultHealthChecker(url *common.URL) router.HealthChecker {
+	return &DefaultHealthChecker{
+		outStandingRequestConutLimit:      int32(url.GetParamInt(constant.OUTSTANDING_REQUEST_COUNT_LIMIT_KEY, math.MaxInt32)),
+		requestSuccessiveFailureThreshold: int32(url.GetParamInt(constant.SUCCESSIVE_FAILED_REQUEST_THRESHOLD_KEY, constant.DEFAULT_SUCCESSIVE_FAILED_REQUEST_MAX_DIFF)),
+		circuitTrippedTimeoutFactor:       int32(url.GetParamInt(constant.CIRCUIT_TRIPPED_TIMEOUT_FACTOR_KEY, constant.DEFAULT_CIRCUIT_TRIPPED_TIMEOUT_FACTOR)),
+	}
+}
diff --git a/cluster/router/healthcheck/default_health_check_test.go b/cluster/router/healthcheck/default_health_check_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..74aa3940743a012f907cfe3d8811a618f07ff800
--- /dev/null
+++ b/cluster/router/healthcheck/default_health_check_test.go
@@ -0,0 +1,157 @@
+/*
+ * 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 healthcheck
+
+import (
+	"math"
+	"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/protocol"
+)
+
+func TestDefaultHealthChecker_IsHealthy(t *testing.T) {
+
+	defer protocol.CleanAllStatus()
+	url, _ := common.NewURL("dubbo://192.168.10.10:20000/com.ikurento.user.UserProvider")
+	hc := NewDefaultHealthChecker(&url).(*DefaultHealthChecker)
+	invoker := NewMockInvoker(url, 1)
+	healthy := hc.IsHealthy(invoker)
+	assert.True(t, healthy)
+
+	url.SetParam(constant.OUTSTANDING_REQUEST_COUNT_LIMIT_KEY, "10")
+	url.SetParam(constant.SUCCESSIVE_FAILED_REQUEST_THRESHOLD_KEY, "100")
+	// fake the outgoing request
+	for i := 0; i < 11; i++ {
+		request(url, "test", 0, true, false)
+	}
+	hc = NewDefaultHealthChecker(&url).(*DefaultHealthChecker)
+	healthy = hc.IsHealthy(invoker)
+	// the outgoing request is more than OUTSTANDING_REQUEST_COUNT_LIMIT, go to unhealthy
+	assert.False(t, hc.IsHealthy(invoker))
+
+	// successive failed count is more than constant.SUCCESSIVE_FAILED_REQUEST_THRESHOLD_KEY, go to unhealthy
+	for i := 0; i < 11; i++ {
+		request(url, "test", 0, false, false)
+	}
+	url.SetParam(constant.SUCCESSIVE_FAILED_REQUEST_THRESHOLD_KEY, "10")
+	url.SetParam(constant.OUTSTANDING_REQUEST_COUNT_LIMIT_KEY, "1000")
+	hc = NewDefaultHealthChecker(&url).(*DefaultHealthChecker)
+	healthy = hc.IsHealthy(invoker)
+	assert.False(t, hc.IsHealthy(invoker))
+
+	// reset successive failed count and go to healthy
+	request(url, "test", 0, false, true)
+	healthy = hc.IsHealthy(invoker)
+	assert.True(t, hc.IsHealthy(invoker))
+}
+
+func TestDefaultHealthChecker_getCircuitBreakerSleepWindowTime(t *testing.T) {
+	defer protocol.CleanAllStatus()
+	url, _ := common.NewURL("dubbo://192.168.10.10:20000/com.ikurento.user.UserProvider")
+	defaultHc := NewDefaultHealthChecker(&url).(*DefaultHealthChecker)
+	// Increase the number of failed requests
+	for i := 0; i < 100; i++ {
+		request(url, "test", 1, false, false)
+	}
+	sleepWindowTime := defaultHc.getCircuitBreakerSleepWindowTime(protocol.GetURLStatus(url))
+	assert.True(t, sleepWindowTime == constant.MAX_CIRCUIT_TRIPPED_TIMEOUT_IN_MS)
+
+	// Adjust the threshold size to 1000
+	url.SetParam(constant.SUCCESSIVE_FAILED_REQUEST_THRESHOLD_KEY, "1000")
+	sleepWindowTime = NewDefaultHealthChecker(&url).(*DefaultHealthChecker).getCircuitBreakerSleepWindowTime(protocol.GetURLStatus(url))
+	assert.True(t, sleepWindowTime == 0)
+
+	url1, _ := common.NewURL("dubbo://192.168.10.11:20000/com.ikurento.user.UserProvider")
+	sleepWindowTime = defaultHc.getCircuitBreakerSleepWindowTime(protocol.GetURLStatus(url1))
+	assert.True(t, sleepWindowTime == 0)
+	request(url1, "test", 1, false, false)
+	request(url1, "test", 1, false, false)
+	request(url1, "test", 1, false, false)
+	request(url1, "test", 1, false, false)
+	request(url1, "test", 1, false, false)
+	request(url1, "test", 1, false, false)
+	sleepWindowTime = defaultHc.getCircuitBreakerSleepWindowTime(protocol.GetURLStatus(url1))
+	assert.True(t, sleepWindowTime > 0 && sleepWindowTime < constant.MAX_CIRCUIT_TRIPPED_TIMEOUT_IN_MS)
+}
+
+func TestDefaultHealthChecker_getCircuitBreakerTimeout(t *testing.T) {
+	defer protocol.CleanAllStatus()
+	url, _ := common.NewURL("dubbo://192.168.10.10:20000/com.ikurento.user.UserProvider")
+	defaultHc := NewDefaultHealthChecker(&url).(*DefaultHealthChecker)
+	timeout := defaultHc.getCircuitBreakerTimeout(protocol.GetURLStatus(url))
+	assert.True(t, timeout == 0)
+	url1, _ := common.NewURL("dubbo://192.168.10.11:20000/com.ikurento.user.UserProvider")
+	request(url1, "test", 1, false, false)
+	request(url1, "test", 1, false, false)
+	request(url1, "test", 1, false, false)
+	request(url1, "test", 1, false, false)
+	request(url1, "test", 1, false, false)
+	request(url1, "test", 1, false, false)
+	timeout = defaultHc.getCircuitBreakerTimeout(protocol.GetURLStatus(url1))
+	// timeout must after the current time
+	assert.True(t, timeout > protocol.CurrentTimeMillis())
+
+}
+
+func TestDefaultHealthChecker_isCircuitBreakerTripped(t *testing.T) {
+	defer protocol.CleanAllStatus()
+	url, _ := common.NewURL("dubbo://192.168.10.10:20000/com.ikurento.user.UserProvider")
+	defaultHc := NewDefaultHealthChecker(&url).(*DefaultHealthChecker)
+	status := protocol.GetURLStatus(url)
+	tripped := defaultHc.isCircuitBreakerTripped(status)
+	assert.False(t, tripped)
+	// Increase the number of failed requests
+	for i := 0; i < 100; i++ {
+		request(url, "test", 1, false, false)
+	}
+	tripped = defaultHc.isCircuitBreakerTripped(protocol.GetURLStatus(url))
+	assert.True(t, tripped)
+
+}
+
+func TestNewDefaultHealthChecker(t *testing.T) {
+	defer protocol.CleanAllStatus()
+	url, _ := common.NewURL("dubbo://192.168.10.10:20000/com.ikurento.user.UserProvider")
+	defaultHc := NewDefaultHealthChecker(&url).(*DefaultHealthChecker)
+	assert.NotNil(t, defaultHc)
+	assert.Equal(t, defaultHc.outStandingRequestConutLimit, int32(math.MaxInt32))
+	assert.Equal(t, defaultHc.requestSuccessiveFailureThreshold, int32(constant.DEFAULT_SUCCESSIVE_FAILED_REQUEST_MAX_DIFF))
+
+	url1, _ := common.NewURL("dubbo://192.168.10.10:20000/com.ikurento.user.UserProvider")
+	url1.SetParam(constant.OUTSTANDING_REQUEST_COUNT_LIMIT_KEY, "10")
+	url1.SetParam(constant.SUCCESSIVE_FAILED_REQUEST_THRESHOLD_KEY, "10")
+	nondefaultHc := NewDefaultHealthChecker(&url1).(*DefaultHealthChecker)
+	assert.NotNil(t, nondefaultHc)
+	assert.Equal(t, nondefaultHc.outStandingRequestConutLimit, int32(10))
+	assert.Equal(t, nondefaultHc.requestSuccessiveFailureThreshold, int32(10))
+}
+
+func request(url common.URL, method string, elapsed int64, active, succeeded bool) {
+	protocol.BeginCount(url, method)
+	if !active {
+		protocol.EndCount(url, method, elapsed, succeeded)
+	}
+}
diff --git a/cluster/router/healthcheck/factory.go b/cluster/router/healthcheck/factory.go
new file mode 100644
index 0000000000000000000000000000000000000000..32d84d145ceb2aa05f5a75de352e52d13dd9d6b3
--- /dev/null
+++ b/cluster/router/healthcheck/factory.go
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package healthcheck
+
+import (
+	"github.com/apache/dubbo-go/cluster/router"
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/common/constant"
+	"github.com/apache/dubbo-go/common/extension"
+)
+
+func init() {
+	extension.SetRouterFactory(constant.HealthCheckRouterName, newHealthCheckRouteFactory)
+}
+
+// HealthCheckRouteFactory
+type HealthCheckRouteFactory struct {
+}
+
+// newHealthCheckRouteFactory construct a new HealthCheckRouteFactory
+func newHealthCheckRouteFactory() router.RouterFactory {
+	return &HealthCheckRouteFactory{}
+}
+
+// NewRouter construct a new NewHealthCheckRouter via url
+func (f *HealthCheckRouteFactory) NewRouter(url *common.URL) (router.Router, error) {
+	return NewHealthCheckRouter(url)
+}
diff --git a/cluster/router/healthcheck/factory_test.go b/cluster/router/healthcheck/factory_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..a9d94da7c37f0e0c9640de1386998a85823e80a6
--- /dev/null
+++ b/cluster/router/healthcheck/factory_test.go
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package healthcheck
+
+import (
+	"context"
+	"testing"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+import (
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/protocol"
+)
+
+type MockInvoker struct {
+	url common.URL
+}
+
+func NewMockInvoker(url common.URL, successCount int) *MockInvoker {
+	return &MockInvoker{
+		url: url,
+	}
+}
+
+func (bi *MockInvoker) GetUrl() common.URL {
+	return bi.url
+}
+func (bi *MockInvoker) IsAvailable() bool {
+	return true
+}
+
+func (bi *MockInvoker) IsDestroyed() bool {
+	return true
+}
+
+func (bi *MockInvoker) Invoke(_ context.Context, _ protocol.Invocation) protocol.Result {
+	return nil
+}
+
+func (bi *MockInvoker) Destroy() {
+}
+
+func TestHealthCheckRouteFactory(t *testing.T) {
+	factory := newHealthCheckRouteFactory()
+	assert.NotNil(t, factory)
+}
diff --git a/cluster/router/healthcheck/health_check_route.go b/cluster/router/healthcheck/health_check_route.go
new file mode 100644
index 0000000000000000000000000000000000000000..1ddc9ccb173881a87bc5351711326f02ab2da3f6
--- /dev/null
+++ b/cluster/router/healthcheck/health_check_route.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 healthcheck
+
+import (
+	"github.com/apache/dubbo-go/cluster/router"
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/common/constant"
+	"github.com/apache/dubbo-go/common/extension"
+	"github.com/apache/dubbo-go/common/logger"
+	"github.com/apache/dubbo-go/protocol"
+)
+
+const (
+	HEALTH_ROUTE_ENABLED_KEY = "health.route.enabled"
+)
+
+// HealthCheckRouter provides a health-first routing mechanism through HealthChecker
+type HealthCheckRouter struct {
+	url     *common.URL
+	enabled bool
+	checker router.HealthChecker
+}
+
+// NewHealthCheckRouter construct an HealthCheckRouter via url
+func NewHealthCheckRouter(url *common.URL) (router.Router, error) {
+	r := &HealthCheckRouter{
+		url:     url,
+		enabled: url.GetParamBool(HEALTH_ROUTE_ENABLED_KEY, false),
+	}
+	if r.enabled {
+		checkerName := url.GetParam(constant.HEALTH_CHECKER, constant.DEFAULT_HEALTH_CHECKER)
+		r.checker = extension.GetHealthChecker(checkerName, url)
+	}
+	return r, nil
+}
+
+// Route gets a list of healthy invoker
+func (r *HealthCheckRouter) Route(invokers []protocol.Invoker, url *common.URL, invocation protocol.Invocation) []protocol.Invoker {
+	if !r.enabled {
+		return invokers
+	}
+	healthyInvokers := make([]protocol.Invoker, 0, len(invokers))
+	// Add healthy invoker to the list
+	for _, invoker := range invokers {
+		if r.checker.IsHealthy(invoker) {
+			healthyInvokers = append(healthyInvokers, invoker)
+		}
+	}
+	// If all Invoke are considered unhealthy, downgrade to all inovker
+	if len(healthyInvokers) == 0 {
+		logger.Warnf(" Now all invokers are unhealthy, so downgraded to all! Service: [%s]", url.ServiceKey())
+		return invokers
+	}
+	return healthyInvokers
+}
+
+// Priority
+func (r *HealthCheckRouter) Priority() int64 {
+	return 0
+}
+
+// URL Return URL in router
+func (r *HealthCheckRouter) URL() common.URL {
+	return *r.url
+}
+
+// HealthyChecker returns the HealthChecker bound to this HealthCheckRouter
+func (r *HealthCheckRouter) HealthyChecker() router.HealthChecker {
+	return r.checker
+}
diff --git a/cluster/router/healthcheck/health_check_route_test.go b/cluster/router/healthcheck/health_check_route_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..759ef93dbeb8d91a82eefd59060afbe8a10a4440
--- /dev/null
+++ b/cluster/router/healthcheck/health_check_route_test.go
@@ -0,0 +1,135 @@
+/*
+ * 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 healthcheck
+
+import (
+	"math"
+	"testing"
+	"time"
+)
+
+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/protocol"
+	"github.com/apache/dubbo-go/protocol/invocation"
+)
+
+func TestHealthCheckRouter_Route(t *testing.T) {
+	defer protocol.CleanAllStatus()
+	consumerURL, _ := common.NewURL("dubbo://192.168.10.1/com.ikurento.user.UserProvider")
+	consumerURL.SetParam(HEALTH_ROUTE_ENABLED_KEY, "true")
+	url1, _ := common.NewURL("dubbo://192.168.10.10:20000/com.ikurento.user.UserProvider")
+	url2, _ := common.NewURL("dubbo://192.168.10.11:20000/com.ikurento.user.UserProvider")
+	url3, _ := common.NewURL("dubbo://192.168.10.12:20000/com.ikurento.user.UserProvider")
+	hcr, _ := NewHealthCheckRouter(&consumerURL)
+
+	var invokers []protocol.Invoker
+	invoker1 := NewMockInvoker(url1, 1)
+	invoker2 := NewMockInvoker(url2, 1)
+	invoker3 := NewMockInvoker(url3, 1)
+	invokers = append(invokers, invoker1, invoker2, invoker3)
+	inv := invocation.NewRPCInvocation("test", nil, nil)
+	res := hcr.Route(invokers, &consumerURL, inv)
+	// now all invokers are healthy
+	assert.True(t, len(res) == len(invokers))
+
+	for i := 0; i < 10; i++ {
+		request(url1, "test", 0, false, false)
+	}
+	res = hcr.Route(invokers, &consumerURL, inv)
+	// invokers1  is unhealthy now
+	assert.True(t, len(res) == 2 && !contains(res, invoker1))
+
+	for i := 0; i < 10; i++ {
+		request(url1, "test", 0, false, false)
+		request(url2, "test", 0, false, false)
+	}
+
+	res = hcr.Route(invokers, &consumerURL, inv)
+	// only invokers3  is healthy now
+	assert.True(t, len(res) == 1 && !contains(res, invoker1) && !contains(res, invoker2))
+
+	for i := 0; i < 10; i++ {
+		request(url1, "test", 0, false, false)
+		request(url2, "test", 0, false, false)
+		request(url3, "test", 0, false, false)
+	}
+
+	res = hcr.Route(invokers, &consumerURL, inv)
+	// now all invokers are unhealthy, so downgraded to all
+	assert.True(t, len(res) == 3)
+
+	// reset the invoker1 successive failed count, so invoker1 go to healthy
+	request(url1, "test", 0, false, true)
+	res = hcr.Route(invokers, &consumerURL, inv)
+	assert.True(t, contains(res, invoker1))
+
+	for i := 0; i < 6; i++ {
+		request(url1, "test", 0, false, false)
+	}
+	// now all invokers are unhealthy, so downgraded to all again
+	res = hcr.Route(invokers, &consumerURL, inv)
+	assert.True(t, len(res) == 3)
+	time.Sleep(time.Second * 2)
+	// invoker1 go to healthy again after 2s
+	res = hcr.Route(invokers, &consumerURL, inv)
+	assert.True(t, contains(res, invoker1))
+
+}
+
+func contains(invokers []protocol.Invoker, invoker protocol.Invoker) bool {
+	for _, e := range invokers {
+		if e == invoker {
+			return true
+		}
+	}
+	return false
+}
+
+func TestNewHealthCheckRouter(t *testing.T) {
+	defer protocol.CleanAllStatus()
+	url, _ := common.NewURL("dubbo://192.168.10.10:20000/com.ikurento.user.UserProvider")
+	hcr, _ := NewHealthCheckRouter(&url)
+	h := hcr.(*HealthCheckRouter)
+	assert.Nil(t, h.checker)
+
+	url.SetParam(HEALTH_ROUTE_ENABLED_KEY, "true")
+	hcr, _ = NewHealthCheckRouter(&url)
+	h = hcr.(*HealthCheckRouter)
+	assert.NotNil(t, h.checker)
+
+	dhc := h.checker.(*DefaultHealthChecker)
+	assert.Equal(t, dhc.outStandingRequestConutLimit, int32(math.MaxInt32))
+	assert.Equal(t, dhc.requestSuccessiveFailureThreshold, int32(constant.DEFAULT_SUCCESSIVE_FAILED_THRESHOLD))
+	assert.Equal(t, dhc.circuitTrippedTimeoutFactor, int32(constant.DEFAULT_CIRCUIT_TRIPPED_TIMEOUT_FACTOR))
+
+	url.SetParam(constant.CIRCUIT_TRIPPED_TIMEOUT_FACTOR_KEY, "500")
+	url.SetParam(constant.SUCCESSIVE_FAILED_REQUEST_THRESHOLD_KEY, "10")
+	url.SetParam(constant.OUTSTANDING_REQUEST_COUNT_LIMIT_KEY, "1000")
+	hcr, _ = NewHealthCheckRouter(&url)
+	h = hcr.(*HealthCheckRouter)
+	dhc = h.checker.(*DefaultHealthChecker)
+	assert.Equal(t, dhc.outStandingRequestConutLimit, int32(1000))
+	assert.Equal(t, dhc.requestSuccessiveFailureThreshold, int32(10))
+	assert.Equal(t, dhc.circuitTrippedTimeoutFactor, int32(500))
+}
diff --git a/cluster/router/match/match_utils.go b/cluster/router/match/match_utils.go
new file mode 100644
index 0000000000000000000000000000000000000000..28fe7151c5126c41fbadf9f4d54da2b9df74a7fe
--- /dev/null
+++ b/cluster/router/match/match_utils.go
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package match
+
+import (
+	"strings"
+)
+
+import (
+	"github.com/apache/dubbo-go/common"
+)
+
+// IsMatchGlobalPattern Match value to param content by pattern
+func IsMatchGlobalPattern(pattern string, value string, param *common.URL) bool {
+	if param != nil && strings.HasPrefix(pattern, "$") {
+		pattern = param.GetRawParam(pattern[1:])
+	}
+	return isMatchInternalPattern(pattern, value)
+}
+
+func isMatchInternalPattern(pattern string, value string) bool {
+	if "*" == pattern {
+		return true
+	}
+	if len(pattern) == 0 && len(value) == 0 {
+		return true
+	}
+	if len(pattern) == 0 || len(value) == 0 {
+		return false
+	}
+	i := strings.LastIndex(pattern, "*")
+	switch i {
+	case -1:
+		// doesn't find "*"
+		return value == pattern
+	case len(pattern) - 1:
+		// "*" is at the end
+		return strings.HasPrefix(value, pattern[0:i])
+	case 0:
+		// "*" is at the beginning
+		return strings.HasSuffix(value, pattern[i+1:])
+	default:
+		// "*" is in the middle
+		prefix := pattern[0:1]
+		suffix := pattern[i+1:]
+		return strings.HasPrefix(value, prefix) && strings.HasSuffix(value, suffix)
+	}
+}
diff --git a/cluster/router/match/match_utils_test.go b/cluster/router/match/match_utils_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..f16480f1d3b7dd5ca820c81d5d04d837c129687f
--- /dev/null
+++ b/cluster/router/match/match_utils_test.go
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package match
+
+import (
+	"testing"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+import (
+	"github.com/apache/dubbo-go/common"
+)
+
+func TestIsMatchInternalPattern(t *testing.T) {
+	assert.Equal(t, true, isMatchInternalPattern("*", "value"))
+	assert.Equal(t, true, isMatchInternalPattern("", ""))
+	assert.Equal(t, false, isMatchInternalPattern("", "value"))
+	assert.Equal(t, true, isMatchInternalPattern("value", "value"))
+	assert.Equal(t, true, isMatchInternalPattern("v*", "value"))
+	assert.Equal(t, true, isMatchInternalPattern("*ue", "value"))
+	assert.Equal(t, true, isMatchInternalPattern("*e", "value"))
+	assert.Equal(t, true, isMatchInternalPattern("v*e", "value"))
+}
+
+func TestIsMatchGlobPattern(t *testing.T) {
+	url, _ := common.NewURL("dubbo://localhost:8080/Foo?key=v*e")
+	assert.Equal(t, true, IsMatchGlobalPattern("$key", "value", &url))
+}
diff --git a/cluster/router/router.go b/cluster/router/router.go
new file mode 100644
index 0000000000000000000000000000000000000000..a28002a09e3b7217549b896d452f70997504ac8f
--- /dev/null
+++ b/cluster/router/router.go
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package router
+
+import (
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/protocol"
+)
+
+// Extension - Router
+
+// RouterFactory Router create factory
+type RouterFactory interface {
+	// NewRouter Create router instance with URL
+	NewRouter(*common.URL) (Router, error)
+}
+
+// RouterFactory Router create factory use for parse config file
+type FIleRouterFactory interface {
+	// NewFileRouters Create file router with config file
+	NewFileRouter([]byte) (Router, error)
+}
+
+// Router
+type Router interface {
+	// Route Determine the target invokers list.
+	Route([]protocol.Invoker, *common.URL, protocol.Invocation) []protocol.Invoker
+	// Priority Return Priority in router
+	// 0 to ^int(0) is better
+	Priority() int64
+	// URL Return URL in router
+	URL() common.URL
+}
+
+// Chain
+type Chain interface {
+	// Route Determine the target invokers list with chain.
+	Route([]protocol.Invoker, *common.URL, protocol.Invocation) []protocol.Invoker
+	// AddRouters Add routers
+	AddRouters([]Router)
+}
diff --git a/cluster/router/rule.go b/cluster/router/rule.go
new file mode 100644
index 0000000000000000000000000000000000000000..42c08a7009a9509749c27e17c465187fe2c85c03
--- /dev/null
+++ b/cluster/router/rule.go
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package router
+
+// BaseRouterRule
+type BaseRouterRule struct {
+	RawRule  string
+	Runtime  bool
+	Force    bool
+	Valid    bool
+	Enabled  bool
+	Priority int
+	Dynamic  bool
+	Scope    string
+	Key      string
+}
diff --git a/common/config/environment.go b/common/config/environment.go
index 931f0460917d68d3bd7adcd6a6bc1b49c7d3bba8..071af31152ba4ce3c579f70aa23df59d718ce506 100644
--- a/common/config/environment.go
+++ b/common/config/environment.go
@@ -27,6 +27,7 @@ import (
 	"github.com/apache/dubbo-go/config_center"
 )
 
+// Environment
 // There is dubbo.properties file and application level config center configuration which higner than normal config center in java. So in java the
 // configuration sequence will be config center > application level config center > dubbo.properties > spring bean configuration.
 // But in go, neither the dubbo.properties file or application level config center configuration will not support for the time being.
@@ -45,12 +46,15 @@ var (
 	once     sync.Once
 )
 
+// GetEnvInstance ...
 func GetEnvInstance() *Environment {
 	once.Do(func() {
 		instance = &Environment{configCenterFirst: true}
 	})
 	return instance
 }
+
+// NewEnvInstance ...
 func NewEnvInstance() {
 	instance = &Environment{configCenterFirst: true}
 }
@@ -63,34 +67,40 @@ func NewEnvInstance() {
 //	return env.configCenterFirst
 //}
 
+// UpdateExternalConfigMap ...
 func (env *Environment) UpdateExternalConfigMap(externalMap map[string]string) {
 	for k, v := range externalMap {
 		env.externalConfigMap.Store(k, v)
 	}
 }
 
+// UpdateAppExternalConfigMap ...
 func (env *Environment) UpdateAppExternalConfigMap(externalMap map[string]string) {
 	for k, v := range externalMap {
 		env.appExternalConfigMap.Store(k, v)
 	}
 }
 
+// Configuration ...
 func (env *Environment) Configuration() *list.List {
-	list := list.New()
+	cfgList := list.New()
 	// The sequence would be: SystemConfiguration -> ExternalConfiguration -> AppExternalConfiguration -> AbstractConfig -> PropertiesConfiguration
-	list.PushFront(newInmemoryConfiguration(&(env.externalConfigMap)))
-	list.PushFront(newInmemoryConfiguration(&(env.appExternalConfigMap)))
-	return list
+	cfgList.PushFront(newInmemoryConfiguration(&(env.externalConfigMap)))
+	cfgList.PushFront(newInmemoryConfiguration(&(env.appExternalConfigMap)))
+	return cfgList
 }
 
+// SetDynamicConfiguration ...
 func (env *Environment) SetDynamicConfiguration(dc config_center.DynamicConfiguration) {
 	env.dynamicConfiguration = dc
 }
 
+// GetDynamicConfiguration ...
 func (env *Environment) GetDynamicConfiguration() config_center.DynamicConfiguration {
 	return env.dynamicConfiguration
 }
 
+// InmemoryConfiguration ...
 type InmemoryConfiguration struct {
 	store *sync.Map
 }
@@ -99,6 +109,7 @@ func newInmemoryConfiguration(p *sync.Map) *InmemoryConfiguration {
 	return &InmemoryConfiguration{store: p}
 }
 
+// GetProperty ...
 func (conf *InmemoryConfiguration) GetProperty(key string) (bool, string) {
 	if conf.store == nil {
 		return false, ""
@@ -112,6 +123,7 @@ func (conf *InmemoryConfiguration) GetProperty(key string) (bool, string) {
 	return false, ""
 }
 
+// GetSubProperty ...
 func (conf *InmemoryConfiguration) GetSubProperty(subKey string) map[string]struct{} {
 	if conf.store == nil {
 		return nil
diff --git a/common/constant/default.go b/common/constant/default.go
index cb6d68af0561d44f4306f16973a89759c9a9ac37..8ed645e84a724531080eff6efe5fdb0df5479e80 100644
--- a/common/constant/default.go
+++ b/common/constant/default.go
@@ -46,8 +46,8 @@ const (
 const (
 	DEFAULT_KEY               = "default"
 	PREFIX_DEFAULT_KEY        = "default."
-	DEFAULT_SERVICE_FILTERS   = "echo,token,accesslog,tps,execute"
-	DEFAULT_REFERENCE_FILTERS = ""
+	DEFAULT_SERVICE_FILTERS   = "echo,token,accesslog,tps,generic_service,execute,pshutdown"
+	DEFAULT_REFERENCE_FILTERS = "cshutdown"
 	GENERIC_REFERENCE_FILTERS = "generic"
 	GENERIC                   = "$invoke"
 	ECHO                      = "$echo"
@@ -66,4 +66,9 @@ const (
 	DYNAMIC_CONFIGURATORS_CATEGORY     = "dynamicconfigurators"
 	APP_DYNAMIC_CONFIGURATORS_CATEGORY = "appdynamicconfigurators"
 	PROVIDER_CATEGORY                  = "providers"
+	CONSUMER_CATEGORY                  = "consumers"
+)
+
+const (
+	COMMA_SPLIT_PATTERN = "\\s*[,]+\\s*"
 )
diff --git a/common/constant/env.go b/common/constant/env.go
index 759cb0be0a2bd36a2a345a360c541b7d56813d70..5376323328f431083a47395c9e2ebbab5b37f307 100644
--- a/common/constant/env.go
+++ b/common/constant/env.go
@@ -18,7 +18,12 @@
 package constant
 
 const (
+	// CONF_CONSUMER_FILE_PATH ...
 	CONF_CONSUMER_FILE_PATH = "CONF_CONSUMER_FILE_PATH"
+	// CONF_PROVIDER_FILE_PATH ...
 	CONF_PROVIDER_FILE_PATH = "CONF_PROVIDER_FILE_PATH"
-	APP_LOG_CONF_FILE       = "APP_LOG_CONF_FILE"
+	// APP_LOG_CONF_FILE ...
+	APP_LOG_CONF_FILE = "APP_LOG_CONF_FILE"
+	// CONF_ROUTER_FILE_PATH Specify Path variable of router config file
+	CONF_ROUTER_FILE_PATH = "CONF_ROUTER_FILE_PATH"
 )
diff --git a/common/constant/key.go b/common/constant/key.go
index ff371d08c669df07f24a3e81e0be0a29a6b17ddd..c8a03b3be9f0179bb5317640d38abef8d9cc2b3a 100644
--- a/common/constant/key.go
+++ b/common/constant/key.go
@@ -40,6 +40,7 @@ const (
 	TOKEN_KEY              = "token"
 	LOCAL_ADDR             = "local-addr"
 	REMOTE_ADDR            = "remote-addr"
+	PATH_SEPARATOR         = "/"
 )
 
 const (
@@ -55,6 +56,7 @@ const (
 	WEIGHT_KEY                             = "weight"
 	WARMUP_KEY                             = "warmup"
 	RETRIES_KEY                            = "retries"
+	STICKY_KEY                             = "sticky"
 	BEAN_NAME                              = "bean.name"
 	FAIL_BACK_TASKS_KEY                    = "failbacktasks"
 	FORKS_KEY                              = "forks"
@@ -71,6 +73,8 @@ const (
 	EXECUTE_LIMIT_KEY                      = "execute.limit"
 	DEFAULT_EXECUTE_LIMIT                  = "-1"
 	EXECUTE_REJECTED_EXECUTION_HANDLER_KEY = "execute.limit.rejected.handler"
+	PROVIDER_SHUTDOWN_FILTER               = "pshutdown"
+	CONSUMER_SHUTDOWN_FILTER               = "cshutdown"
 )
 
 const (
@@ -86,20 +90,31 @@ const (
 )
 
 const (
-	APPLICATION_KEY  = "application"
-	ORGANIZATION_KEY = "organization"
-	NAME_KEY         = "name"
-	MODULE_KEY       = "module"
-	APP_VERSION_KEY  = "app.version"
-	OWNER_KEY        = "owner"
-	ENVIRONMENT_KEY  = "environment"
-	METHOD_KEY       = "method"
-	METHOD_KEYS      = "methods"
-	RULE_KEY         = "rule"
+	APPLICATION_KEY          = "application"
+	ORGANIZATION_KEY         = "organization"
+	NAME_KEY                 = "name"
+	MODULE_KEY               = "module"
+	APP_VERSION_KEY          = "app.version"
+	OWNER_KEY                = "owner"
+	ENVIRONMENT_KEY          = "environment"
+	METHOD_KEY               = "method"
+	METHOD_KEYS              = "methods"
+	RULE_KEY                 = "rule"
+	RUNTIME_KEY              = "runtime"
+	BACKUP_KEY               = "backup"
+	ROUTERS_CATEGORY         = "routers"
+	ROUTE_PROTOCOL           = "route"
+	CONDITION_ROUTE_PROTOCOL = "condition"
+	PROVIDERS_CATEGORY       = "providers"
+	ROUTER_KEY               = "router"
 )
 
 const (
 	CONFIG_NAMESPACE_KEY  = "config.namespace"
+	CONFIG_GROUP_KEY      = "config.group"
+	CONFIG_APP_ID_KEY     = "config.appId"
+	CONFIG_CLUSTER_KEY    = "config.cluster"
+	CONFIG_CHECK_KEY      = "config.check"
 	CONFIG_TIMEOUT_KET    = "config.timeout"
 	CONFIG_VERSION_KEY    = "configVersion"
 	COMPATIBLE_CONFIG_KEY = "compatible_config"
@@ -112,6 +127,8 @@ const (
 	ProtocolConfigPrefix       = "dubbo.protocols."
 	ProviderConfigPrefix       = "dubbo.provider."
 	ConsumerConfigPrefix       = "dubbo.consumer."
+	ShutdownConfigPrefix       = "dubbo.shutdown."
+	RouterConfigPrefix         = "dubbo.router."
 )
 
 const (
@@ -129,3 +146,69 @@ const (
 	NACOS_PROTOCOL_KEY           = "protocol"
 	NACOS_PATH_KEY               = "path"
 )
+
+const (
+	TRACING_REMOTE_SPAN_CTX = "tracing.remote.span.ctx"
+)
+
+// Use for router module
+const (
+	// ConditionRouterName Specify file condition router name
+	ConditionRouterName = "condition"
+	// ConditionAppRouterName Specify listenable application router name
+	ConditionAppRouterName = "app"
+	// ListenableRouterName Specify listenable router name
+	ListenableRouterName = "listenable"
+	// HealthCheckRouterName Specify the name of HealthCheckRouter
+	HealthCheckRouterName = "health_check"
+
+	// ConditionRouterRuleSuffix Specify condition router suffix
+	ConditionRouterRuleSuffix = ".condition-router"
+
+	// Force Force key in router module
+	RouterForce = "force"
+	// Enabled Enabled key in router module
+	RouterEnabled = "enabled"
+	// Priority Priority key in router module
+	RouterPriority = "priority"
+)
+
+const (
+	CONSUMER_SIGN_FILTER          = "sign"
+	PROVIDER_AUTH_FILTER          = "auth"
+	SERVICE_AUTH_KEY              = "auth"
+	AUTHENTICATOR_KEY             = "authenticator"
+	DEFAULT_AUTHENTICATOR         = "accesskeys"
+	DEFAULT_ACCESS_KEY_STORAGE    = "urlstorage"
+	ACCESS_KEY_STORAGE_KEY        = "accessKey.storage"
+	REQUEST_TIMESTAMP_KEY         = "timestamp"
+	REQUEST_SIGNATURE_KEY         = "signature"
+	AK_KEY                        = "ak"
+	SIGNATURE_STRING_FORMAT       = "%s#%s#%s#%s"
+	PARAMTER_SIGNATURE_ENABLE_KEY = "param.sign"
+	CONSUMER                      = "consumer"
+	ACCESS_KEY_ID_KEY             = "accessKeyId"
+	SECRET_ACCESS_KEY_KEY         = "secretAccessKey"
+)
+
+// HealthCheck Router
+const (
+	// The key of HealthCheck SPI
+	HEALTH_CHECKER = "health.checker"
+	// The name of the default implementation of HealthChecker
+	DEFAULT_HEALTH_CHECKER = "default"
+	// The key of oustanding-request-limit
+	OUTSTANDING_REQUEST_COUNT_LIMIT_KEY = "outstanding.request.limit"
+	// The key of successive-failed-request's threshold
+	SUCCESSIVE_FAILED_REQUEST_THRESHOLD_KEY = "successive.failed.threshold"
+	// The key of circuit-tripped timeout factor
+	CIRCUIT_TRIPPED_TIMEOUT_FACTOR_KEY = "circuit.tripped.timeout.factor"
+	// The default threshold of  successive-failed-request if not specfied
+	DEFAULT_SUCCESSIVE_FAILED_THRESHOLD = 5
+	// The default maximum diff between successive-failed-request's threshold and actual successive-failed-request's count
+	DEFAULT_SUCCESSIVE_FAILED_REQUEST_MAX_DIFF = 5
+	// The default factor of  circuit-tripped timeout if not specfied
+	DEFAULT_CIRCUIT_TRIPPED_TIMEOUT_FACTOR = 1000
+	// The default time window of circuit-tripped  in millisecond if not specfied
+	MAX_CIRCUIT_TRIPPED_TIMEOUT_IN_MS = 30000
+)
diff --git a/common/constant/time.go b/common/constant/time.go
new file mode 100644
index 0000000000000000000000000000000000000000..be1baaca67f474aa92e86e529d03400948ef4612
--- /dev/null
+++ b/common/constant/time.go
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package constant
+
+import (
+	"time"
+)
+
+var (
+	MsToNanoRate = int64(time.Millisecond / time.Nanosecond)
+)
diff --git a/common/constant/version.go b/common/constant/version.go
index d4c6821e76894cbd82dc5fae09124263b5c6aa0f..730224376054a36b0c7cfeda7d5ea5e7ce058618 100644
--- a/common/constant/version.go
+++ b/common/constant/version.go
@@ -18,7 +18,10 @@
 package constant
 
 const (
-	Version = "2.6.0"
-	Name    = "dubbogo"
-	DATE    = "2019/05/06"
+	// Version apache/dubbo-go version
+	Version = "1.3.0"
+	// Name module name
+	Name = "dubbogo"
+	// Date release date
+	DATE = "2020/01/12"
 )
diff --git a/common/extension/auth.go b/common/extension/auth.go
new file mode 100644
index 0000000000000000000000000000000000000000..e57e22f660b6d4dec63f8b4a06c25b05bd5c8d72
--- /dev/null
+++ b/common/extension/auth.go
@@ -0,0 +1,32 @@
+package extension
+
+import (
+	"github.com/apache/dubbo-go/filter"
+)
+
+var (
+	authenticators    = make(map[string]func() filter.Authenticator)
+	accesskeyStorages = make(map[string]func() filter.AccessKeyStorage)
+)
+
+func SetAuthenticator(name string, fcn func() filter.Authenticator) {
+	authenticators[name] = fcn
+}
+
+func GetAuthenticator(name string) filter.Authenticator {
+	if authenticators[name] == nil {
+		panic("authenticator for " + name + " is not existing, make sure you have import the package.")
+	}
+	return authenticators[name]()
+}
+
+func SetAccesskeyStorages(name string, fcn func() filter.AccessKeyStorage) {
+	accesskeyStorages[name] = fcn
+}
+
+func GetAccesskeyStorages(name string) filter.AccessKeyStorage {
+	if accesskeyStorages[name] == nil {
+		panic("accesskeyStorages for " + name + " is not existing, make sure you have import the package.")
+	}
+	return accesskeyStorages[name]()
+}
diff --git a/common/extension/cluster.go b/common/extension/cluster.go
index 91e9f953b505e31c1a4f448e1504a6ae50a9663f..b2d81f6b1e56bb487b1d408b878308f6dfe042e4 100644
--- a/common/extension/cluster.go
+++ b/common/extension/cluster.go
@@ -25,10 +25,12 @@ var (
 	clusters = make(map[string]func() cluster.Cluster)
 )
 
+// SetCluster ...
 func SetCluster(name string, fcn func() cluster.Cluster) {
 	clusters[name] = fcn
 }
 
+// GetCluster ...
 func GetCluster(name string) cluster.Cluster {
 	if clusters[name] == nil {
 		panic("cluster for " + name + " is not existing, make sure you have import the package.")
diff --git a/common/extension/config_center.go b/common/extension/config_center.go
index be4b62ccdd9c36500c306c7f16abd054f91ae86b..03d27db46c94b0ea0e212646077d97f948a8e328 100644
--- a/common/extension/config_center.go
+++ b/common/extension/config_center.go
@@ -26,10 +26,12 @@ var (
 	configCenters = make(map[string]func(config *common.URL) (config_center.DynamicConfiguration, error))
 )
 
+// SetConfigCenter ...
 func SetConfigCenter(name string, v func(config *common.URL) (config_center.DynamicConfiguration, error)) {
 	configCenters[name] = v
 }
 
+// GetConfigCenter ...
 func GetConfigCenter(name string, config *common.URL) (config_center.DynamicConfiguration, error) {
 	if configCenters[name] == nil {
 		panic("config center for " + name + " is not existing, make sure you have import the package.")
diff --git a/common/extension/config_center_factory.go b/common/extension/config_center_factory.go
index 82e0ef6ebcf632ccff32aec5c69c2082a28c51af..85913fdce1ed3472c2bd9eb4aadbb0f631481dbd 100644
--- a/common/extension/config_center_factory.go
+++ b/common/extension/config_center_factory.go
@@ -25,10 +25,12 @@ var (
 	configCenterFactories = make(map[string]func() config_center.DynamicConfigurationFactory)
 )
 
+// SetConfigCenterFactory ...
 func SetConfigCenterFactory(name string, v func() config_center.DynamicConfigurationFactory) {
 	configCenterFactories[name] = v
 }
 
+// GetConfigCenterFactory ...
 func GetConfigCenterFactory(name string) config_center.DynamicConfigurationFactory {
 	if configCenterFactories[name] == nil {
 		panic("config center for " + name + " is not existing, make sure you have import the package.")
diff --git a/common/extension/configurator.go b/common/extension/configurator.go
index 40d134f474ae792afb76f1d8e3f56d172bfd07e2..de98f8a260ea1f3a2e2a1f32c82dc869585e2789 100644
--- a/common/extension/configurator.go
+++ b/common/extension/configurator.go
@@ -22,7 +22,10 @@ import (
 	"github.com/apache/dubbo-go/config_center"
 )
 
-const DefaultKey = "default"
+const (
+	// DefaultKey ...
+	DefaultKey = "default"
+)
 
 type getConfiguratorFunc func(url *common.URL) config_center.Configurator
 
@@ -30,10 +33,12 @@ var (
 	configurator = make(map[string]getConfiguratorFunc)
 )
 
+// SetConfigurator ...
 func SetConfigurator(name string, v getConfiguratorFunc) {
 	configurator[name] = v
 }
 
+// GetConfigurator ...
 func GetConfigurator(name string, url *common.URL) config_center.Configurator {
 	if configurator[name] == nil {
 		panic("configurator for " + name + " is not existing, make sure you have import the package.")
@@ -41,10 +46,13 @@ func GetConfigurator(name string, url *common.URL) config_center.Configurator {
 	return configurator[name](url)
 
 }
+
+// SetDefaultConfigurator ...
 func SetDefaultConfigurator(v getConfiguratorFunc) {
 	configurator[DefaultKey] = v
 }
 
+// GetDefaultConfigurator ...
 func GetDefaultConfigurator(url *common.URL) config_center.Configurator {
 	if configurator[DefaultKey] == nil {
 		panic("configurator for default is not existing, make sure you have import the package.")
@@ -52,6 +60,8 @@ func GetDefaultConfigurator(url *common.URL) config_center.Configurator {
 	return configurator[DefaultKey](url)
 
 }
+
+// GetDefaultConfiguratorFunc ...
 func GetDefaultConfiguratorFunc() getConfiguratorFunc {
 	if configurator[DefaultKey] == nil {
 		panic("configurator for default is not existing, make sure you have import the package.")
diff --git a/common/extension/filter.go b/common/extension/filter.go
index e2a66c7449448a2229c53eabb478f2c96a429bc7..deea2d908bc2741e0f15ecc36e9d4fc5975e531e 100644
--- a/common/extension/filter.go
+++ b/common/extension/filter.go
@@ -19,30 +19,33 @@ package extension
 
 import (
 	"github.com/apache/dubbo-go/filter"
-	"github.com/apache/dubbo-go/filter/common"
 )
 
 var (
 	filters                  = make(map[string]func() filter.Filter)
-	rejectedExecutionHandler = make(map[string]func() common.RejectedExecutionHandler)
+	rejectedExecutionHandler = make(map[string]func() filter.RejectedExecutionHandler)
 )
 
+// SetFilter ...
 func SetFilter(name string, v func() filter.Filter) {
 	filters[name] = v
 }
 
+// GetFilter ...
 func GetFilter(name string) filter.Filter {
 	if filters[name] == nil {
-		panic("filter for " + name + " is not existing, make sure you have import the package.")
+		panic("filter for " + name + " is not existing, make sure you have imported the package.")
 	}
 	return filters[name]()
 }
 
-func SetRejectedExecutionHandler(name string, creator func() common.RejectedExecutionHandler) {
+// SetRejectedExecutionHandler ...
+func SetRejectedExecutionHandler(name string, creator func() filter.RejectedExecutionHandler) {
 	rejectedExecutionHandler[name] = creator
 }
 
-func GetRejectedExecutionHandler(name string) common.RejectedExecutionHandler {
+// GetRejectedExecutionHandler ...
+func GetRejectedExecutionHandler(name string) filter.RejectedExecutionHandler {
 	creator, ok := rejectedExecutionHandler[name]
 	if !ok {
 		panic("RejectedExecutionHandler for " + name + " is not existing, make sure you have import the package " +
diff --git a/common/extension/graceful_shutdown.go b/common/extension/graceful_shutdown.go
new file mode 100644
index 0000000000000000000000000000000000000000..3abd75c0aa328f3553c3d83340ae440b8dfe3356
--- /dev/null
+++ b/common/extension/graceful_shutdown.go
@@ -0,0 +1,55 @@
+/*
+ * 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 (
+	"container/list"
+)
+
+var (
+	customShutdownCallbacks = list.New()
+)
+
+/**
+ * AddCustomShutdownCallback
+ * you should not make any assumption about the order.
+ * For example, if you have more than one callbacks, and you wish the order is:
+ * callback1()
+ * callback2()
+ * ...
+ * callbackN()
+ * Then you should put then together:
+ * func callback() {
+ *     callback1()
+ *     callback2()
+ *     ...
+ *     callbackN()
+ * }
+ * I think the order of custom callbacks should be decided by the users.
+ * Even though I can design a mechanism to support the ordered custom callbacks,
+ * the benefit of that mechanism is low.
+ * And it may introduce much complication for another users.
+ */
+func AddCustomShutdownCallback(callback func()) {
+	customShutdownCallbacks.PushBack(callback)
+}
+
+// GetAllCustomShutdownCallbacks ...
+func GetAllCustomShutdownCallbacks() *list.List {
+	return customShutdownCallbacks
+}
diff --git a/common/extension/health_checker.go b/common/extension/health_checker.go
new file mode 100644
index 0000000000000000000000000000000000000000..365c5d0910812efb00eb94408bb226115b037c02
--- /dev/null
+++ b/common/extension/health_checker.go
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package extension
+
+import (
+	"github.com/apache/dubbo-go/cluster/router"
+	"github.com/apache/dubbo-go/common"
+)
+
+var (
+	healthCheckers = make(map[string]func(url *common.URL) router.HealthChecker)
+)
+
+// SethealthChecker set the HealthChecker with name
+func SethealthChecker(name string, fcn func(url *common.URL) router.HealthChecker) {
+	healthCheckers[name] = fcn
+}
+
+// GetHealthChecker get the HealthChecker with name
+func GetHealthChecker(name string, url *common.URL) router.HealthChecker {
+	if healthCheckers[name] == nil {
+		panic("healthCheckers for " + name + " is not existing, make sure you have import the package.")
+	}
+	return healthCheckers[name](url)
+}
diff --git a/common/extension/health_checker_test.go b/common/extension/health_checker_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..ec934e6e9cedc5acbef350f17b87b0b2e37bc844
--- /dev/null
+++ b/common/extension/health_checker_test.go
@@ -0,0 +1,49 @@
+/*
+ * 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 (
+	"testing"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+import (
+	"github.com/apache/dubbo-go/cluster/router"
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/protocol"
+)
+
+func TestGetHealthChecker(t *testing.T) {
+	SethealthChecker("mock", newMockhealthCheck)
+	checker := GetHealthChecker("mock", common.NewURLWithOptions())
+	assert.NotNil(t, checker)
+}
+
+type mockHealthChecker struct {
+}
+
+func (m mockHealthChecker) IsHealthy(invoker protocol.Invoker) bool {
+	return true
+}
+
+func newMockhealthCheck(url *common.URL) router.HealthChecker {
+	return &mockHealthChecker{}
+}
diff --git a/common/extension/loadbalance.go b/common/extension/loadbalance.go
index f1f97b9399a2b33a3e06213fc0b2f84e73b002b7..0d557a4640ed892a18ad59a3247763ab5807a593 100644
--- a/common/extension/loadbalance.go
+++ b/common/extension/loadbalance.go
@@ -25,10 +25,12 @@ var (
 	loadbalances = make(map[string]func() cluster.LoadBalance)
 )
 
+// SetLoadbalance ...
 func SetLoadbalance(name string, fcn func() cluster.LoadBalance) {
 	loadbalances[name] = fcn
 }
 
+// GetLoadbalance ...
 func GetLoadbalance(name string) cluster.LoadBalance {
 	if loadbalances[name] == nil {
 		panic("loadbalance for " + name + " is not existing, make sure you have import the package.")
diff --git a/common/extension/metrics.go b/common/extension/metrics.go
new file mode 100644
index 0000000000000000000000000000000000000000..42fca7a2db36614fcef31dd5ba7324a156164d4f
--- /dev/null
+++ b/common/extension/metrics.go
@@ -0,0 +1,44 @@
+/*
+ * 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/metrics"
+)
+
+var (
+	// we couldn't store the instance because the some instance may initialize before loading configuration
+	// so lazy initialization will be better.
+	metricReporterMap = make(map[string]func() metrics.Reporter, 4)
+)
+
+// SetMetricReporter set a reporter with the name
+func SetMetricReporter(name string, reporterFunc func() metrics.Reporter) {
+	metricReporterMap[name] = reporterFunc
+}
+
+// GetMetricReporter find the reporter with name.
+// if not found, it will panic.
+// we should know that this method usually is called when system starts, so we should panic
+func GetMetricReporter(name string) metrics.Reporter {
+	reporterFunc, found := metricReporterMap[name]
+	if !found {
+		panic("Cannot find the reporter with name: " + name)
+	}
+	return reporterFunc()
+}
diff --git a/common/extension/metrics_test.go b/common/extension/metrics_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..6a8a3fe538a9cd68c57c91592a88ec257ae4a267
--- /dev/null
+++ b/common/extension/metrics_test.go
@@ -0,0 +1,49 @@
+/*
+ * 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 (
+	"context"
+	"testing"
+	"time"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+import (
+	"github.com/apache/dubbo-go/metrics"
+	"github.com/apache/dubbo-go/protocol"
+)
+
+func TestGetMetricReporter(t *testing.T) {
+	reporter := &mockReporter{}
+	name := "mock"
+	SetMetricReporter(name, func() metrics.Reporter {
+		return reporter
+	})
+	res := GetMetricReporter(name)
+	assert.Equal(t, reporter, res)
+}
+
+type mockReporter struct {
+}
+
+func (m mockReporter) Report(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation, cost time.Duration, res protocol.Result) {
+}
diff --git a/common/extension/protocol.go b/common/extension/protocol.go
index 50d339476d024c04b182c38632689a99bc5c1680..009687a17ace8cea567248af655e04604d09d9b8 100644
--- a/common/extension/protocol.go
+++ b/common/extension/protocol.go
@@ -25,10 +25,12 @@ var (
 	protocols = make(map[string]func() protocol.Protocol)
 )
 
+// SetProtocol ...
 func SetProtocol(name string, v func() protocol.Protocol) {
 	protocols[name] = v
 }
 
+// GetProtocol ...
 func GetProtocol(name string) protocol.Protocol {
 	if protocols[name] == nil {
 		panic("protocol for " + name + " is not existing, make sure you have import the package.")
diff --git a/common/extension/proxy_factory.go b/common/extension/proxy_factory.go
index 53cbbee54054bf8ad87964393b01ca6601106066..19826bb0560ea0d3fa471c04873b20a6878f57d8 100644
--- a/common/extension/proxy_factory.go
+++ b/common/extension/proxy_factory.go
@@ -22,18 +22,21 @@ import (
 )
 
 var (
-	proxy_factories = make(map[string]func(...proxy.Option) proxy.ProxyFactory)
+	proxyFactories = make(map[string]func(...proxy.Option) proxy.ProxyFactory)
 )
 
+// SetProxyFactory ...
 func SetProxyFactory(name string, f func(...proxy.Option) proxy.ProxyFactory) {
-	proxy_factories[name] = f
+	proxyFactories[name] = f
 }
+
+// GetProxyFactory ...
 func GetProxyFactory(name string) proxy.ProxyFactory {
 	if name == "" {
 		name = "default"
 	}
-	if proxy_factories[name] == nil {
+	if proxyFactories[name] == nil {
 		panic("proxy factory for " + name + " is not existing, make sure you have import the package.")
 	}
-	return proxy_factories[name]()
+	return proxyFactories[name]()
 }
diff --git a/common/extension/registry.go b/common/extension/registry.go
index 776c2b5df542e56f8c120c850f20093a971d8602..6ba746dc47382927d12ce39b7936212c5d75153d 100644
--- a/common/extension/registry.go
+++ b/common/extension/registry.go
@@ -26,10 +26,12 @@ var (
 	registrys = make(map[string]func(config *common.URL) (registry.Registry, error))
 )
 
+// SetRegistry ...
 func SetRegistry(name string, v func(config *common.URL) (registry.Registry, error)) {
 	registrys[name] = v
 }
 
+// GetRegistry ...
 func GetRegistry(name string, config *common.URL) (registry.Registry, error) {
 	if registrys[name] == nil {
 		panic("registry for " + name + " is not existing, make sure you have import the package.")
diff --git a/common/extension/router_factory.go b/common/extension/router_factory.go
index 6f27aafaebf87147116e74272cc229657f436201..70d71dfa859b996030c865775a588da20039f9a5 100644
--- a/common/extension/router_factory.go
+++ b/common/extension/router_factory.go
@@ -18,21 +18,50 @@
 package extension
 
 import (
-	"github.com/apache/dubbo-go/cluster"
+	"sync"
+)
+
+import (
+	"github.com/apache/dubbo-go/cluster/router"
 )
 
 var (
-	routers = make(map[string]func() cluster.RouterFactory)
+	routers               = make(map[string]func() router.RouterFactory)
+	fileRouterFactoryOnce sync.Once
+	fileRouterFactories   = make(map[string]router.FIleRouterFactory)
 )
 
-func SetRouterFactory(name string, fun func() cluster.RouterFactory) {
+// SetRouterFactory Set create router factory function by name
+func SetRouterFactory(name string, fun func() router.RouterFactory) {
 	routers[name] = fun
 }
 
-func GetRouterFactory(name string) cluster.RouterFactory {
+// GetRouterFactory Get create router factory function by name
+func GetRouterFactory(name string) router.RouterFactory {
 	if routers[name] == nil {
 		panic("router_factory for " + name + " is not existing, make sure you have import the package.")
 	}
 	return routers[name]()
+}
 
+// GetRouterFactories Get all create router factory function
+func GetRouterFactories() map[string]func() router.RouterFactory {
+	return routers
+}
+
+// GetFileRouterFactories Get all create file router factory instance
+func GetFileRouterFactories() map[string]router.FIleRouterFactory {
+	l := len(routers)
+	if l == 0 {
+		return nil
+	}
+	fileRouterFactoryOnce.Do(func() {
+		for k := range routers {
+			factory := GetRouterFactory(k)
+			if fr, ok := factory.(router.FIleRouterFactory); ok {
+				fileRouterFactories[k] = fr
+			}
+		}
+	})
+	return fileRouterFactories
 }
diff --git a/common/extension/tps_limit.go b/common/extension/tps_limit.go
index 65891c79336224f59b66f8312693c6b5151a7e28..c72c2b030fc0f391362189bfe18a65582543693a 100644
--- a/common/extension/tps_limit.go
+++ b/common/extension/tps_limit.go
@@ -18,19 +18,21 @@
 package extension
 
 import (
-	"github.com/apache/dubbo-go/filter/impl/tps"
+	"github.com/apache/dubbo-go/filter"
 )
 
 var (
-	tpsLimitStrategy = make(map[string]func(rate int, interval int) tps.TpsLimitStrategy)
-	tpsLimiter       = make(map[string]func() tps.TpsLimiter)
+	tpsLimitStrategy = make(map[string]filter.TpsLimitStrategyCreator)
+	tpsLimiter       = make(map[string]func() filter.TpsLimiter)
 )
 
-func SetTpsLimiter(name string, creator func() tps.TpsLimiter) {
+// SetTpsLimiter ...
+func SetTpsLimiter(name string, creator func() filter.TpsLimiter) {
 	tpsLimiter[name] = creator
 }
 
-func GetTpsLimiter(name string) tps.TpsLimiter {
+// GetTpsLimiter ...
+func GetTpsLimiter(name string) filter.TpsLimiter {
 	creator, ok := tpsLimiter[name]
 	if !ok {
 		panic("TpsLimiter for " + name + " is not existing, make sure you have import the package " +
@@ -39,11 +41,13 @@ func GetTpsLimiter(name string) tps.TpsLimiter {
 	return creator()
 }
 
-func SetTpsLimitStrategy(name string, creator func(rate int, interval int) tps.TpsLimitStrategy) {
+// SetTpsLimitStrategy ...
+func SetTpsLimitStrategy(name string, creator filter.TpsLimitStrategyCreator) {
 	tpsLimitStrategy[name] = creator
 }
 
-func GetTpsLimitStrategyCreator(name string) func(rate int, interval int) tps.TpsLimitStrategy {
+// GetTpsLimitStrategyCreator ...
+func GetTpsLimitStrategyCreator(name string) filter.TpsLimitStrategyCreator {
 	creator, ok := tpsLimitStrategy[name]
 	if !ok {
 		panic("TpsLimitStrategy for " + name + " is not existing, make sure you have import the package " +
diff --git a/common/logger/logger.go b/common/logger/logger.go
index f41e95744f954da69b0e3695c97ba3389c69160a..016afe69808f2007541c617f406db64beb511f1c 100644
--- a/common/logger/logger.go
+++ b/common/logger/logger.go
@@ -40,6 +40,13 @@ var (
 	logger Logger
 )
 
+// DubboLogger ...
+type DubboLogger struct {
+	Logger
+	dynamicLevel zap.AtomicLevel
+}
+
+// Logger ...
 type Logger interface {
 	Info(args ...interface{})
 	Warn(args ...interface{})
@@ -60,6 +67,7 @@ func init() {
 	}
 }
 
+// InitLog ...
 func InitLog(logConfFile string) error {
 	if logConfFile == "" {
 		InitLogger(nil)
@@ -88,6 +96,7 @@ func InitLog(logConfFile string) error {
 	return nil
 }
 
+// InitLogger ...
 func InitLogger(conf *zap.Config) {
 	var zapLoggerConfig zap.Config
 	if conf == nil {
@@ -109,17 +118,42 @@ func InitLogger(conf *zap.Config) {
 		zapLoggerConfig = *conf
 	}
 	zapLogger, _ := zapLoggerConfig.Build(zap.AddCallerSkip(1))
-	logger = zapLogger.Sugar()
+	//logger = zapLogger.Sugar()
+	logger = &DubboLogger{Logger: zapLogger.Sugar(), dynamicLevel: zapLoggerConfig.Level}
 
 	// set getty log
 	getty.SetLogger(logger)
 }
 
+// SetLogger ...
 func SetLogger(log Logger) {
 	logger = log
 	getty.SetLogger(logger)
 }
 
+// GetLogger ...
 func GetLogger() Logger {
 	return logger
 }
+
+// SetLoggerLevel ...
+func SetLoggerLevel(level string) bool {
+	if l, ok := logger.(OpsLogger); ok {
+		l.SetLoggerLevel(level)
+		return true
+	}
+	return false
+}
+
+// OpsLogger ...
+type OpsLogger interface {
+	Logger
+	SetLoggerLevel(level string)
+}
+
+// SetLoggerLevel ...
+func (dl *DubboLogger) SetLoggerLevel(level string) {
+	l := new(zapcore.Level)
+	l.Set(level)
+	dl.dynamicLevel.SetLevel(*l)
+}
diff --git a/common/logger/logger_test.go b/common/logger/logger_test.go
index e29b7cbc8e9bbd67df41df5ac687a079621c3360..6081f71aecccbfab5fd574335effe7788b6ce799 100644
--- a/common/logger/logger_test.go
+++ b/common/logger/logger_test.go
@@ -65,3 +65,19 @@ func TestInitLog(t *testing.T) {
 	Warnf("%s", "warn")
 	Errorf("%s", "error")
 }
+
+func TestSetLevel(t *testing.T) {
+	err := InitLog("./log.yml")
+	assert.NoError(t, err)
+	Debug("debug")
+	Info("info")
+
+	assert.True(t, SetLoggerLevel("info"))
+	Debug("debug")
+	Info("info")
+
+	SetLogger(GetLogger().(*DubboLogger).Logger)
+	assert.False(t, SetLoggerLevel("debug"))
+	Debug("debug")
+	Info("info")
+}
diff --git a/common/logger/logging.go b/common/logger/logging.go
index 4638c9a41dfe986d256e1ff4d52b690c1747fc94..36d48ee61e8a4a986abfbaa79f3d361cd81494f4 100644
--- a/common/logger/logging.go
+++ b/common/logger/logging.go
@@ -17,27 +17,42 @@
 
 package logger
 
+// Info ...
 func Info(args ...interface{}) {
 	logger.Info(args...)
 }
+
+// Warn ...
 func Warn(args ...interface{}) {
 	logger.Warn(args...)
 }
+
+// Error ...
 func Error(args ...interface{}) {
 	logger.Error(args...)
 }
+
+// Debug ...
 func Debug(args ...interface{}) {
 	logger.Debug(args...)
 }
+
+// Infof ...
 func Infof(fmt string, args ...interface{}) {
 	logger.Infof(fmt, args...)
 }
+
+// Warnf ...
 func Warnf(fmt string, args ...interface{}) {
 	logger.Warnf(fmt, args...)
 }
+
+// Errorf ...
 func Errorf(fmt string, args ...interface{}) {
 	logger.Errorf(fmt, args...)
 }
+
+// Debugf ...
 func Debugf(fmt string, args ...interface{}) {
 	logger.Debugf(fmt, args...)
 }
diff --git a/common/node.go b/common/node.go
index 992ead38d8acf85bbb14f02eebca4c4fe5a0a1e2..979eee31ef3a63eb21af6c9045aee7f6d784f2ba 100644
--- a/common/node.go
+++ b/common/node.go
@@ -17,6 +17,7 @@
 
 package common
 
+// Node ...
 type Node interface {
 	GetUrl() URL
 	IsAvailable() bool
diff --git a/common/proxy/proxy.go b/common/proxy/proxy.go
index 1c079f6bca52bf8f6e8c5ebb168da82ab8ccb5f2..6765a810a5ed48d95f49b5b97fbf660dd8587715 100644
--- a/common/proxy/proxy.go
+++ b/common/proxy/proxy.go
@@ -18,6 +18,7 @@
 package proxy
 
 import (
+	"context"
 	"reflect"
 	"sync"
 )
@@ -39,8 +40,11 @@ type Proxy struct {
 	once sync.Once
 }
 
-var typError = reflect.Zero(reflect.TypeOf((*error)(nil)).Elem()).Type()
+var (
+	typError = reflect.Zero(reflect.TypeOf((*error)(nil)).Elem()).Type()
+)
 
+// NewProxy ...
 func NewProxy(invoke protocol.Invoker, callBack interface{}, attachments map[string]string) *Proxy {
 	return &Proxy{
 		invoke:      invoke,
@@ -49,11 +53,13 @@ func NewProxy(invoke protocol.Invoker, callBack interface{}, attachments map[str
 	}
 }
 
+// Implement
 // proxy implement
 // In consumer, RPCService like:
 // 		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.
@@ -72,10 +78,11 @@ func (p *Proxy) Implement(v common.RPCService) {
 	makeDubboCallProxy := func(methodName string, outs []reflect.Type) func(in []reflect.Value) []reflect.Value {
 		return func(in []reflect.Value) []reflect.Value {
 			var (
-				err   error
-				inv   *invocation_impl.RPCInvocation
-				inArr []interface{}
-				reply reflect.Value
+				err    error
+				inv    *invocation_impl.RPCInvocation
+				inIArr []interface{}
+				inVArr []reflect.Value
+				reply  reflect.Value
 			)
 			if methodName == "Echo" {
 				methodName = "$echo"
@@ -93,8 +100,13 @@ func (p *Proxy) Implement(v common.RPCService) {
 
 			start := 0
 			end := len(in)
+			invCtx := context.Background()
 			if end > 0 {
 				if in[0].Type().String() == "context.Context" {
+					if !in[0].IsNil() {
+						// the user declared context as method's parameter
+						invCtx = in[0].Interface().(context.Context)
+					}
 					start += 1
 				}
 				if len(outs) == 1 && in[end-1].Type().Kind() == reflect.Ptr {
@@ -104,30 +116,34 @@ func (p *Proxy) Implement(v common.RPCService) {
 			}
 
 			if end-start <= 0 {
-				inArr = []interface{}{}
+				inIArr = []interface{}{}
+				inVArr = []reflect.Value{}
 			} else if v, ok := in[start].Interface().([]interface{}); ok && end-start == 1 {
-				inArr = v
+				inIArr = v
+				inVArr = []reflect.Value{in[start]}
 			} else {
-				inArr = make([]interface{}, end-start)
+				inIArr = make([]interface{}, end-start)
+				inVArr = make([]reflect.Value, end-start)
 				index := 0
 				for i := start; i < end; i++ {
-					inArr[index] = in[i].Interface()
+					inIArr[index] = in[i].Interface()
+					inVArr[index] = in[i]
 					index++
 				}
 			}
 
 			inv = invocation_impl.NewRPCInvocationWithOptions(invocation_impl.WithMethodName(methodName),
-				invocation_impl.WithArguments(inArr), invocation_impl.WithReply(reply.Interface()),
-				invocation_impl.WithCallBack(p.callBack))
+				invocation_impl.WithArguments(inIArr), invocation_impl.WithReply(reply.Interface()),
+				invocation_impl.WithCallBack(p.callBack), invocation_impl.WithParameterValues(inVArr))
 
 			for k, value := range p.attachments {
 				inv.SetAttachments(k, value)
 			}
 
-			result := p.invoke.Invoke(inv)
+			result := p.invoke.Invoke(invCtx, inv)
 
 			err = result.Error()
-			logger.Infof("[makeDubboCallProxy] result: %v, err: %v", result.Result(), err)
+			logger.Debugf("[makeDubboCallProxy] result: %v, err: %v", result.Result(), err)
 			if len(outs) == 1 {
 				return []reflect.Value{reflect.ValueOf(&err).Elem()}
 			}
@@ -178,6 +194,12 @@ func (p *Proxy) Implement(v common.RPCService) {
 
 }
 
+// Get ...
 func (p *Proxy) Get() common.RPCService {
 	return p.rpc
 }
+
+// GetCallback ...
+func (p *Proxy) GetCallback() interface{} {
+	return p.callBack
+}
diff --git a/common/proxy/proxy_factory.go b/common/proxy/proxy_factory.go
index 2567e0ee09cf7fa5aef7fde46872eb88205d8e45..7b249a3e9754b097130a80bf3819d282dad6b6e8 100644
--- a/common/proxy/proxy_factory.go
+++ b/common/proxy/proxy_factory.go
@@ -22,9 +22,12 @@ import (
 	"github.com/apache/dubbo-go/protocol"
 )
 
+// ProxyFactory ...
 type ProxyFactory interface {
 	GetProxy(invoker protocol.Invoker, url *common.URL) *Proxy
+	GetAsyncProxy(invoker protocol.Invoker, callBack interface{}, url *common.URL) *Proxy
 	GetInvoker(url common.URL) protocol.Invoker
 }
 
+// Option ...
 type Option func(ProxyFactory)
diff --git a/common/proxy/proxy_factory/default.go b/common/proxy/proxy_factory/default.go
index bafba60b400ec59d99e2d68ecf4d067c906ba6fb..114cfee2363022da5f7957a825a16fc42b8c928f 100644
--- a/common/proxy/proxy_factory/default.go
+++ b/common/proxy/proxy_factory/default.go
@@ -18,6 +18,7 @@
 package proxy_factory
 
 import (
+	"context"
 	"reflect"
 	"strings"
 )
@@ -39,6 +40,7 @@ func init() {
 	extension.SetProxyFactory("default", NewDefaultProxyFactory)
 }
 
+// DefaultProxyFactory ...
 type DefaultProxyFactory struct {
 	//delegate ProxyFactory
 }
@@ -51,26 +53,38 @@ type DefaultProxyFactory struct {
 //	}
 //}
 
+// NewDefaultProxyFactory ...
 func NewDefaultProxyFactory(options ...proxy.Option) proxy.ProxyFactory {
 	return &DefaultProxyFactory{}
 }
+
+// GetProxy ...
 func (factory *DefaultProxyFactory) GetProxy(invoker protocol.Invoker, url *common.URL) *proxy.Proxy {
+	return factory.GetAsyncProxy(invoker, nil, url)
+}
+
+// GetAsyncProxy ...
+func (factory *DefaultProxyFactory) GetAsyncProxy(invoker protocol.Invoker, callBack interface{}, url *common.URL) *proxy.Proxy {
 	//create proxy
 	attachments := map[string]string{}
 	attachments[constant.ASYNC_KEY] = url.GetParam(constant.ASYNC_KEY, "false")
-	return proxy.NewProxy(invoker, nil, attachments)
+	return proxy.NewProxy(invoker, callBack, attachments)
 }
+
+// GetInvoker ...
 func (factory *DefaultProxyFactory) GetInvoker(url common.URL) protocol.Invoker {
 	return &ProxyInvoker{
 		BaseInvoker: *protocol.NewBaseInvoker(url),
 	}
 }
 
+// ProxyInvoker ...
 type ProxyInvoker struct {
 	protocol.BaseInvoker
 }
 
-func (pi *ProxyInvoker) Invoke(invocation protocol.Invocation) protocol.Result {
+// Invoke ...
+func (pi *ProxyInvoker) Invoke(ctx context.Context, invocation protocol.Invocation) protocol.Result {
 	result := &protocol.RPCResult{}
 	result.SetAttachments(invocation.Attachments())
 
@@ -99,7 +113,7 @@ func (pi *ProxyInvoker) Invoke(invocation protocol.Invocation) protocol.Result {
 
 	in := []reflect.Value{svc.Rcvr()}
 	if method.CtxType() != nil {
-		in = append(in, method.SuiteContext(nil)) // todo: ctx will be used later.
+		in = append(in, method.SuiteContext(ctx))
 	}
 
 	// prepare argv
diff --git a/common/proxy/proxy_factory/default_test.go b/common/proxy/proxy_factory/default_test.go
index b6a6b675baf992b2d64ffd19291ee2dc009bd1e3..7159b4b00eb2fcddb0f20f701f56b3179e57c4a0 100644
--- a/common/proxy/proxy_factory/default_test.go
+++ b/common/proxy/proxy_factory/default_test.go
@@ -18,6 +18,7 @@
 package proxy_factory
 
 import (
+	"fmt"
 	"testing"
 )
 
@@ -37,6 +38,21 @@ func Test_GetProxy(t *testing.T) {
 	assert.NotNil(t, proxy)
 }
 
+type TestAsync struct {
+}
+
+func (u *TestAsync) CallBack(res common.CallbackResponse) {
+	fmt.Println("CallBack res:", res)
+}
+
+func Test_GetAsyncProxy(t *testing.T) {
+	proxyFactory := NewDefaultProxyFactory()
+	url := common.NewURLWithOptions()
+	async := &TestAsync{}
+	proxy := proxyFactory.GetAsyncProxy(protocol.NewBaseInvoker(*url), async.CallBack, url)
+	assert.NotNil(t, proxy)
+}
+
 func Test_GetInvoker(t *testing.T) {
 	proxyFactory := NewDefaultProxyFactory()
 	url := common.NewURLWithOptions()
diff --git a/common/rpc_service.go b/common/rpc_service.go
index 4741a6fa3c0daef97f044f639a5e64a38fe4a187..b235c32abc9a971d7144605c8b4b82953ac8f3c4 100644
--- a/common/rpc_service.go
+++ b/common/rpc_service.go
@@ -34,22 +34,41 @@ import (
 	"github.com/apache/dubbo-go/common/logger"
 )
 
-// rpc service interface
+// RPCService
+//rpc service interface
 type RPCService interface {
-	Reference() string // rpc service id or reference id
+	// Reference:
+	// rpc service id or reference id
+	Reference() string
 }
 
+//AsyncCallbackService callback interface for async
+type AsyncCallbackService interface {
+	// Callback: callback
+	CallBack(response CallbackResponse)
+}
+
+//CallbackResponse for different protocol
+type CallbackResponse interface{}
+
+//AsyncCallback async callback method
+type AsyncCallback func(response CallbackResponse)
+
 // for lowercase func
 // func MethodMapper() map[string][string] {
 //     return map[string][string]{}
 // }
-const METHOD_MAPPER = "MethodMapper"
+const (
+	// METHOD_MAPPER ...
+	METHOD_MAPPER = "MethodMapper"
+)
 
 var (
 	// Precompute the reflect type for error. Can't use error directly
 	// because Typeof takes an empty interface value. This is annoying.
 	typeOfError = reflect.TypeOf((*error)(nil)).Elem()
 
+	// ServiceMap ...
 	// todo: lowerecas?
 	ServiceMap = &serviceMap{
 		serviceMap: make(map[string]map[string]*Service),
@@ -60,6 +79,7 @@ var (
 // info of method
 //////////////////////////
 
+// MethodType ...
 type MethodType struct {
 	method    reflect.Method
 	ctxType   reflect.Type   // request context
@@ -67,18 +87,27 @@ type MethodType struct {
 	replyType reflect.Type   // return value, otherwise it is nil
 }
 
+// Method ...
 func (m *MethodType) Method() reflect.Method {
 	return m.method
 }
+
+// CtxType ...
 func (m *MethodType) CtxType() reflect.Type {
 	return m.ctxType
 }
+
+// ArgsType ...
 func (m *MethodType) ArgsType() []reflect.Type {
 	return m.argsType
 }
+
+// ReplyType ...
 func (m *MethodType) ReplyType() reflect.Type {
 	return m.replyType
 }
+
+// SuiteContext ...
 func (m *MethodType) SuiteContext(ctx context.Context) reflect.Value {
 	if contextv := reflect.ValueOf(ctx); contextv.IsValid() {
 		return contextv
@@ -90,6 +119,7 @@ func (m *MethodType) SuiteContext(ctx context.Context) reflect.Value {
 // info of service interface
 //////////////////////////
 
+// Service ...
 type Service struct {
 	name     string
 	rcvr     reflect.Value
@@ -97,12 +127,17 @@ type Service struct {
 	methods  map[string]*MethodType
 }
 
+// Method ...
 func (s *Service) Method() map[string]*MethodType {
 	return s.methods
 }
+
+// RcvrType ...
 func (s *Service) RcvrType() reflect.Type {
 	return s.rcvrType
 }
+
+// Rcvr ...
 func (s *Service) Rcvr() reflect.Value {
 	return s.rcvr
 }
@@ -198,8 +233,8 @@ func (sm *serviceMap) UnRegister(protocol, serviceId string) error {
 
 // Is this an exported - upper case - name
 func isExported(name string) bool {
-	rune, _ := utf8.DecodeRuneInString(name)
-	return unicode.IsUpper(rune)
+	s, _ := utf8.DecodeRuneInString(name)
+	return unicode.IsUpper(s)
 }
 
 // Is this type exported or a builtin?
diff --git a/common/rpc_service_test.go b/common/rpc_service_test.go
index 7df039b905d3cc064c5d6d9404fc874cf693dac9..8c9b9d15cdd4061dbe2f445b5fff7a868e5ae67e 100644
--- a/common/rpc_service_test.go
+++ b/common/rpc_service_test.go
@@ -122,9 +122,8 @@ func TestServiceMap_UnRegister(t *testing.T) {
 
 func TestMethodType_SuiteContext(t *testing.T) {
 	mt := &MethodType{ctxType: reflect.TypeOf(context.TODO())}
-	c := context.TODO()
-	c = context.WithValue(c, "key", "value")
-	assert.Equal(t, reflect.ValueOf(c), mt.SuiteContext(c))
+	ctx := context.WithValue(context.Background(), "key", "value")
+	assert.Equal(t, reflect.ValueOf(ctx), mt.SuiteContext(ctx))
 
 	assert.Equal(t, reflect.Zero(mt.ctxType), mt.SuiteContext(nil))
 }
diff --git a/common/url.go b/common/url.go
index 6e7a843c8f4d2d3b24caf50983baf041e2dd036d..ebb648db27c3efff534f0d0a545f2211f335aa89 100644
--- a/common/url.go
+++ b/common/url.go
@@ -19,7 +19,6 @@ package common
 
 import (
 	"bytes"
-	"context"
 	"encoding/base64"
 	"fmt"
 	"math"
@@ -31,7 +30,7 @@ import (
 )
 
 import (
-	"github.com/dubbogo/gost/container/gxset"
+	gxset "github.com/dubbogo/gost/container/set"
 	"github.com/jinzhu/copier"
 	perrors "github.com/pkg/errors"
 	"github.com/satori/go.uuid"
@@ -45,24 +44,33 @@ import (
 // dubbo role type
 /////////////////////////////////
 
+// role constant
 const (
+	// CONSUMER ...
 	CONSUMER = iota
+	// CONFIGURATOR ...
 	CONFIGURATOR
+	// ROUTER ...
 	ROUTER
+	// PROVIDER ...
 	PROVIDER
 )
 
 var (
+	// DubboNodes ...
 	DubboNodes = [...]string{"consumers", "configurators", "routers", "providers"}
-	DubboRole  = [...]string{"consumer", "", "", "provider"}
+	// DubboRole Dubbo service role
+	DubboRole = [...]string{"consumer", "", "routers", "provider"}
 )
 
+// RoleType ...
 type RoleType int
 
 func (t RoleType) String() string {
 	return DubboNodes[t]
 }
 
+// Role ...
 func (t RoleType) Role() string {
 	return DubboRole[t]
 }
@@ -76,9 +84,9 @@ type baseUrl struct {
 	paramsLock   sync.RWMutex
 	params       url.Values
 	PrimitiveURL string
-	ctx          context.Context
 }
 
+// URL ...
 type URL struct {
 	baseUrl
 	Path     string // like  /com.ikurento.dubbo.UserProvider3
@@ -91,66 +99,77 @@ type URL struct {
 
 type option func(*URL)
 
+// WithUsername ...
 func WithUsername(username string) option {
 	return func(url *URL) {
 		url.Username = username
 	}
 }
 
+// WithPassword ...
 func WithPassword(pwd string) option {
 	return func(url *URL) {
 		url.Password = pwd
 	}
 }
 
+// WithMethods ...
 func WithMethods(methods []string) option {
 	return func(url *URL) {
 		url.Methods = methods
 	}
 }
 
+// WithParams ...
 func WithParams(params url.Values) option {
 	return func(url *URL) {
 		url.params = params
 	}
 }
 
+// WithParamsValue ...
 func WithParamsValue(key, val string) option {
 	return func(url *URL) {
 		url.SetParam(key, val)
 	}
 }
 
+// WithProtocol ...
 func WithProtocol(proto string) option {
 	return func(url *URL) {
 		url.Protocol = proto
 	}
 }
 
+// WithIp ...
 func WithIp(ip string) option {
 	return func(url *URL) {
 		url.Ip = ip
 	}
 }
 
+// WithPort ...
 func WithPort(port string) option {
 	return func(url *URL) {
 		url.Port = port
 	}
 }
 
+// WithPath ...
 func WithPath(path string) option {
 	return func(url *URL) {
 		url.Path = "/" + strings.TrimPrefix(path, "/")
 	}
 }
 
+// WithLocation ...
 func WithLocation(location string) option {
 	return func(url *URL) {
 		url.Location = location
 	}
 }
 
+// WithToken ...
 func WithToken(token string) option {
 	return func(url *URL) {
 		if len(token) > 0 {
@@ -163,6 +182,7 @@ func WithToken(token string) option {
 	}
 }
 
+// NewURLWithOptions ...
 func NewURLWithOptions(opts ...option) *URL {
 	url := &URL{}
 	for _, opt := range opts {
@@ -172,13 +192,15 @@ func NewURLWithOptions(opts ...option) *URL {
 	return url
 }
 
-func NewURL(ctx context.Context, urlString string, opts ...option) (URL, error) {
+// 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
 		serviceUrl   *url.URL
-		s            = URL{baseUrl: baseUrl{ctx: ctx}}
+		s            = URL{baseUrl: baseUrl{}}
 	)
 
 	// new a null instance
@@ -193,7 +215,7 @@ func NewURL(ctx context.Context, urlString string, opts ...option) (URL, error)
 
 	//rawUrlString = "//" + rawUrlString
 	if strings.Index(rawUrlString, "//") < 0 {
-		t := URL{baseUrl: baseUrl{ctx: ctx}}
+		t := URL{baseUrl: baseUrl{}}
 		for _, opt := range opts {
 			opt(&t)
 		}
@@ -218,7 +240,7 @@ func NewURL(ctx context.Context, urlString string, opts ...option) (URL, error)
 	if strings.Contains(s.Location, ":") {
 		s.Ip, s.Port, err = net.SplitHostPort(s.Location)
 		if err != nil {
-			return s, perrors.Errorf("net.SplitHostPort(Url.Host{%s}), error{%v}", s.Location, err)
+			return s, perrors.Errorf("net.SplitHostPort(url.Host{%s}), error{%v}", s.Location, err)
 		}
 	}
 	for _, opt := range opts {
@@ -227,6 +249,7 @@ func NewURL(ctx context.Context, urlString string, opts ...option) (URL, error)
 	return s, nil
 }
 
+// URLEqual ...
 func (c URL) URLEqual(url URL) bool {
 	c.Ip = ""
 	c.Port = ""
@@ -254,6 +277,7 @@ func (c URL) URLEqual(url URL) bool {
 	}
 	return true
 }
+
 func isMatchCategory(category1 string, category2 string) bool {
 	if len(category2) == 0 {
 		return category1 == constant.DEFAULT_CATEGORY
@@ -265,6 +289,7 @@ func isMatchCategory(category1 string, category2 string) bool {
 		return strings.Contains(category2, category1)
 	}
 }
+
 func (c URL) String() string {
 	var buildString string
 	if len(c.Username) == 0 && len(c.Password) == 0 {
@@ -282,6 +307,7 @@ func (c URL) String() string {
 	return buildString
 }
 
+// Key ...
 func (c URL) Key() string {
 	buildString := fmt.Sprintf(
 		"%s://%s:%s@%s:%s/?interface=%s&group=%s&version=%s",
@@ -290,6 +316,7 @@ func (c URL) Key() string {
 	//return c.ServiceKey()
 }
 
+// ServiceKey ...
 func (c URL) ServiceKey() string {
 	intf := c.GetParam(constant.INTERFACE_KEY, strings.TrimPrefix(c.Path, "/"))
 	if intf == "" {
@@ -313,40 +340,63 @@ func (c URL) ServiceKey() string {
 	return buf.String()
 }
 
+// ColonSeparatedKey
+// The format is "{interface}:[version]:[group]"
+func (c *URL) ColonSeparatedKey() string {
+	intf := c.GetParam(constant.INTERFACE_KEY, strings.TrimPrefix(c.Path, "/"))
+	if intf == "" {
+		return ""
+	}
+	buf := &bytes.Buffer{}
+	buf.WriteString(intf)
+	buf.WriteString(":")
+	version := c.GetParam(constant.VERSION_KEY, "")
+	if version != "" && version != "0.0.0" {
+		buf.WriteString(version)
+	}
+	group := c.GetParam(constant.GROUP_KEY, "")
+	buf.WriteString(":")
+	if group != "" {
+		buf.WriteString(group)
+	}
+	return buf.String()
+}
+
+// EncodedServiceKey ...
 func (c *URL) EncodedServiceKey() string {
 	serviceKey := c.ServiceKey()
 	return strings.Replace(serviceKey, "/", "*", 1)
 }
 
-func (c URL) Context() context.Context {
-	return c.ctx
-}
-
+// Service ...
 func (c URL) Service() string {
 	service := c.GetParam(constant.INTERFACE_KEY, strings.TrimPrefix(c.Path, "/"))
 	if service != "" {
 		return service
 	} else if c.SubURL != nil {
 		service = c.GetParam(constant.INTERFACE_KEY, strings.TrimPrefix(c.Path, "/"))
-		if service != "" { //if url.path is "" then return suburl's path, special for registry Url
+		if service != "" { //if url.path is "" then return suburl's path, special for registry url
 			return service
 		}
 	}
 	return ""
 }
 
+// AddParam ...
 func (c *URL) AddParam(key string, value string) {
 	c.paramsLock.Lock()
 	c.params.Add(key, value)
 	c.paramsLock.Unlock()
 }
 
+// SetParam ...
 func (c *URL) SetParam(key string, value string) {
 	c.paramsLock.Lock()
 	c.params.Set(key, value)
 	c.paramsLock.Unlock()
 }
 
+// RangeParams ...
 func (c *URL) RangeParams(f func(key, value string) bool) {
 	c.paramsLock.RLock()
 	defer c.paramsLock.RUnlock()
@@ -357,6 +407,7 @@ 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()
@@ -367,10 +418,12 @@ func (c URL) GetParam(s string, d string) string {
 	return r
 }
 
+// GetParams ...
 func (c URL) GetParams() url.Values {
 	return c.params
 }
 
+// GetParamAndDecoded ...
 func (c URL) GetParamAndDecoded(key string) (string, error) {
 	c.paramsLock.RLock()
 	defer c.paramsLock.RUnlock()
@@ -379,6 +432,7 @@ func (c URL) GetParamAndDecoded(key string) (string, error) {
 	return value, err
 }
 
+// GetRawParam ...
 func (c URL) GetRawParam(key string) string {
 	switch key {
 	case "protocol":
@@ -398,7 +452,7 @@ func (c URL) GetRawParam(key string) string {
 	}
 }
 
-// GetParamBool
+// GetParamBool ...
 func (c URL) GetParamBool(s string, d bool) bool {
 
 	var r bool
@@ -409,6 +463,7 @@ func (c URL) GetParamBool(s string, d bool) bool {
 	return r
 }
 
+// GetParamInt ...
 func (c URL) GetParamInt(s string, d int64) int64 {
 	var r int
 	var err error
@@ -419,6 +474,7 @@ func (c URL) GetParamInt(s string, d int64) int64 {
 	return int64(r)
 }
 
+// GetMethodParamInt ...
 func (c URL) GetMethodParamInt(method string, key string, d int64) int64 {
 	var r int
 	var err error
@@ -430,6 +486,7 @@ func (c URL) GetMethodParamInt(method string, key string, d int64) int64 {
 	return int64(r)
 }
 
+// GetMethodParamInt64 ...
 func (c URL) GetMethodParamInt64(method string, key string, d int64) int64 {
 	r := c.GetMethodParamInt(method, key, math.MinInt64)
 	if r == math.MinInt64 {
@@ -439,6 +496,7 @@ func (c URL) GetMethodParamInt64(method string, key string, d int64) int64 {
 	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 == "" {
@@ -447,6 +505,13 @@ func (c URL) GetMethodParam(method string, key string, d string) string {
 	return r
 }
 
+// GetMethodParamBool ...
+func (c URL) GetMethodParamBool(method string, key string, d bool) bool {
+	r := c.GetParamBool("methods."+method+"."+key, d)
+	return r
+}
+
+// RemoveParams ...
 func (c *URL) RemoveParams(set *gxset.HashSet) {
 	c.paramsLock.Lock()
 	defer c.paramsLock.Unlock()
@@ -456,6 +521,7 @@ func (c *URL) RemoveParams(set *gxset.HashSet) {
 	}
 }
 
+// SetParams ...
 func (c *URL) SetParams(m url.Values) {
 	for k := range m {
 		c.SetParam(k, m.Get(k))
@@ -507,6 +573,7 @@ func (c URL) ToMap() map[string]string {
 //  in this function we should merge the reference local url config into the service url from registry.
 //TODO configuration merge, in the future , the configuration center's config should merge too.
 
+// MergeUrl ...
 func MergeUrl(serviceUrl *URL, referenceUrl *URL) *URL {
 	mergedUrl := serviceUrl.Clone()
 
@@ -518,7 +585,7 @@ func MergeUrl(serviceUrl *URL, referenceUrl *URL) *URL {
 		return true
 	})
 	//loadBalance,cluster,retries strategy config
-	methodConfigMergeFcn := mergeNormalParam(mergedUrl, referenceUrl, []string{constant.LOADBALANCE_KEY, constant.CLUSTER_KEY, constant.RETRIES_KEY})
+	methodConfigMergeFcn := mergeNormalParam(mergedUrl, referenceUrl, []string{constant.LOADBALANCE_KEY, constant.CLUSTER_KEY, constant.RETRIES_KEY, constant.TIMEOUT_KEY})
 
 	//remote timestamp
 	if v := serviceUrl.GetParam(constant.TIMESTAMP_KEY, ""); len(v) > 0 {
@@ -535,6 +602,8 @@ func MergeUrl(serviceUrl *URL, referenceUrl *URL) *URL {
 
 	return mergedUrl
 }
+
+// Clone ...
 func (c *URL) Clone() *URL {
 	newUrl := &URL{}
 	copier.Copy(newUrl, c)
diff --git a/common/url_test.go b/common/url_test.go
index 41fd374a4d8a4ad3e15de1080fe46d426620909f..2372de520e88b0949023e88cec64871736dd6aa0 100644
--- a/common/url_test.go
+++ b/common/url_test.go
@@ -18,7 +18,6 @@
 package common
 
 import (
-	"context"
 	"encoding/base64"
 	"net/url"
 	"testing"
@@ -56,10 +55,10 @@ func TestNewURLWithOptions(t *testing.T) {
 }
 
 func TestURL(t *testing.T) {
-	u, err := NewURL(context.TODO(), "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&"+
-		"application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&"+
-		"environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&"+
-		"module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&"+
+	u, err := NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&" +
+		"application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&" +
+		"environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&" +
+		"module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&" +
 		"side=provider&timeout=3000&timestamp=1556509797245")
 	assert.NoError(t, err)
 
@@ -83,7 +82,7 @@ func TestURL(t *testing.T) {
 }
 
 func TestURLWithoutSchema(t *testing.T) {
-	u, err := NewURL(context.TODO(), "127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&"+
+	u, err := NewURL("127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&"+
 		"application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&"+
 		"environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&"+
 		"module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&"+
@@ -110,13 +109,13 @@ func TestURLWithoutSchema(t *testing.T) {
 }
 
 func TestURL_URLEqual(t *testing.T) {
-	u1, err := NewURL(context.TODO(), "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=&version=2.6.0")
+	u1, err := NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=&version=2.6.0")
 	assert.NoError(t, err)
-	u2, err := NewURL(context.TODO(), "dubbo://127.0.0.2:20001/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=&version=2.6.0")
+	u2, err := NewURL("dubbo://127.0.0.2:20001/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=&version=2.6.0")
 	assert.NoError(t, err)
 	assert.True(t, u1.URLEqual(u2))
 
-	u3, err := NewURL(context.TODO(), "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=gg&version=2.6.0")
+	u3, err := NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=gg&version=2.6.0")
 	assert.NoError(t, err)
 	assert.False(t, u1.URLEqual(u3))
 }
@@ -165,8 +164,9 @@ func TestURL_GetParamAndDecoded(t *testing.T) {
 	v, _ := u.GetParamAndDecoded("rule")
 	assert.Equal(t, rule, v)
 }
+
 func TestURL_GetRawParam(t *testing.T) {
-	u, _ := NewURL(context.TODO(), "condition://0.0.0.0:8080/com.foo.BarService?serialization=fastjson")
+	u, _ := NewURL("condition://0.0.0.0:8080/com.foo.BarService?serialization=fastjson")
 	u.Username = "test"
 	u.Password = "test"
 	assert.Equal(t, "condition", u.GetRawParam("protocol"))
@@ -177,8 +177,9 @@ func TestURL_GetRawParam(t *testing.T) {
 	assert.Equal(t, "/com.foo.BarService", u.GetRawParam("path"))
 	assert.Equal(t, "fastjson", u.GetRawParam("serialization"))
 }
+
 func TestURL_ToMap(t *testing.T) {
-	u, _ := NewURL(context.TODO(), "condition://0.0.0.0:8080/com.foo.BarService?serialization=fastjson")
+	u, _ := NewURL("condition://0.0.0.0:8080/com.foo.BarService?serialization=fastjson")
 	u.Username = "test"
 	u.Password = "test"
 
@@ -217,6 +218,18 @@ func TestURL_GetMethodParam(t *testing.T) {
 	assert.Equal(t, "1s", v)
 }
 
+func TestURL_GetMethodParamBool(t *testing.T) {
+	params := url.Values{}
+	params.Set("methods.GetValue.async", "true")
+	u := URL{baseUrl: baseUrl{params: params}}
+	v := u.GetMethodParamBool("GetValue", "async", false)
+	assert.Equal(t, true, v)
+
+	u = URL{}
+	v = u.GetMethodParamBool("GetValue2", "async", false)
+	assert.Equal(t, false, v)
+}
+
 func TestMergeUrl(t *testing.T) {
 	referenceUrlParams := url.Values{}
 	referenceUrlParams.Set(constant.CLUSTER_KEY, "random")
@@ -227,20 +240,20 @@ func TestMergeUrl(t *testing.T) {
 	serviceUrlParams.Set("test2", "1")
 	serviceUrlParams.Set(constant.CLUSTER_KEY, "roundrobin")
 	serviceUrlParams.Set(constant.RETRIES_KEY, "2")
-	serviceUrlParams.Set("methods.testMethod."+constant.RETRIES_KEY, "2")
-	referenceUrl, _ := NewURL(context.TODO(), "mock1://127.0.0.1:1111", WithParams(referenceUrlParams), WithMethods([]string{"testMethod"}))
-	serviceUrl, _ := NewURL(context.TODO(), "mock2://127.0.0.1:20000", WithParams(serviceUrlParams))
+	serviceUrlParams.Set(constant.METHOD_KEYS+".testMethod."+constant.RETRIES_KEY, "2")
+	referenceUrl, _ := NewURL("mock1://127.0.0.1:1111", WithParams(referenceUrlParams), WithMethods([]string{"testMethod"}))
+	serviceUrl, _ := NewURL("mock2://127.0.0.1:20000", WithParams(serviceUrlParams))
 
 	mergedUrl := MergeUrl(&serviceUrl, &referenceUrl)
 	assert.Equal(t, "random", mergedUrl.GetParam(constant.CLUSTER_KEY, ""))
 	assert.Equal(t, "1", mergedUrl.GetParam("test2", ""))
 	assert.Equal(t, "1", mergedUrl.GetParam("test3", ""))
 	assert.Equal(t, "1", mergedUrl.GetParam(constant.RETRIES_KEY, ""))
-	assert.Equal(t, "1", mergedUrl.GetParam("methods.testMethod."+constant.RETRIES_KEY, ""))
+	assert.Equal(t, "2", mergedUrl.GetParam(constant.METHOD_KEYS+".testMethod."+constant.RETRIES_KEY, ""))
 }
 
 func TestURL_SetParams(t *testing.T) {
-	u1, err := NewURL(context.TODO(), "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=&version=2.6.0&configVersion=1.0")
+	u1, err := NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=&version=2.6.0&configVersion=1.0")
 	assert.NoError(t, err)
 	params := url.Values{}
 	params.Set("key", "3")
@@ -250,7 +263,7 @@ func TestURL_SetParams(t *testing.T) {
 }
 
 func TestClone(t *testing.T) {
-	u1, err := NewURL(context.TODO(), "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=&version=2.6.0&configVersion=1.0")
+	u1, err := NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=&version=2.6.0&configVersion=1.0")
 	assert.NoError(t, err)
 	u2 := u1.Clone()
 	assert.Equal(t, u2.Protocol, "dubbo")
@@ -259,3 +272,17 @@ func TestClone(t *testing.T) {
 	assert.Equal(t, u1.Protocol, "dubbo")
 	assert.Equal(t, u2.Protocol, "provider")
 }
+
+func TestColonSeparatedKey(t *testing.T) {
+	u1, _ := NewURL("dubbo://127.0.0.1:20000")
+	u1.AddParam(constant.INTERFACE_KEY, "com.ikurento.user.UserProvider")
+
+	assert.Equal(t, u1.ColonSeparatedKey(), u1.GetParam(constant.INTERFACE_KEY, "")+"::")
+	u1.AddParam(constant.VERSION_KEY, "version1")
+	assert.Equal(t, u1.ColonSeparatedKey(), u1.GetParam(constant.INTERFACE_KEY, "")+":version1:")
+	u1.AddParam(constant.GROUP_KEY, "group1")
+	assert.Equal(t, u1.ColonSeparatedKey(), u1.GetParam(constant.INTERFACE_KEY, "")+":version1:group1")
+	u1.SetParam(constant.VERSION_KEY, "")
+	assert.Equal(t, u1.ColonSeparatedKey(), u1.GetParam(constant.INTERFACE_KEY, "")+"::group1")
+
+}
diff --git a/config/application_config.go b/config/application_config.go
index fcd4d38c9b55963c32d58fdd1b80375083a76d8c..23ab7d34aceaba02d7f592906d6f4e3d6cf36dae 100644
--- a/config/application_config.go
+++ b/config/application_config.go
@@ -25,6 +25,7 @@ import (
 	"github.com/apache/dubbo-go/common/constant"
 )
 
+// ApplicationConfig ...
 type ApplicationConfig struct {
 	Organization string `yaml:"organization"  json:"organization,omitempty" property:"organization"`
 	Name         string `yaml:"name" json:"name,omitempty" property:"name"`
@@ -34,15 +35,22 @@ type ApplicationConfig struct {
 	Environment  string `yaml:"environment" json:"environment,omitempty" property:"environment"`
 }
 
+// Prefix ...
 func (*ApplicationConfig) Prefix() string {
 	return constant.DUBBO + ".application."
 }
+
+// Id ...
 func (c *ApplicationConfig) Id() string {
 	return ""
 }
+
+// SetId ...
 func (c *ApplicationConfig) SetId(id string) {
 
 }
+
+// UnmarshalYAML ...
 func (c *ApplicationConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
 	if err := defaults.Set(c); err != nil {
 		return err
diff --git a/config/base_config.go b/config/base_config.go
index 264eeda3cc20da1b097a24dc35cf4f9b2291eeeb..6d5ec7e2498ba65b2a6833b6c9cefcb3394e60df 100644
--- a/config/base_config.go
+++ b/config/base_config.go
@@ -14,10 +14,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package config
 
 import (
-	"context"
+	"io/ioutil"
+	"path"
 	"reflect"
 	"strconv"
 	"strings"
@@ -25,6 +27,7 @@ import (
 
 import (
 	perrors "github.com/pkg/errors"
+	"gopkg.in/yaml.v2"
 )
 
 import (
@@ -39,15 +42,21 @@ type multiConfiger interface {
 	Prefix() string
 }
 
+// BaseConfig is the common configuration for provider and consumer
 type BaseConfig struct {
 	ConfigCenterConfig *ConfigCenterConfig `yaml:"config_center" json:"config_center,omitempty"`
 	configCenterUrl    *common.URL
 	prefix             string
 	fatherConfig       interface{}
+
+	MetricConfig *MetricConfig `yaml:"metrics" json:"metrics,omitempty"`
 }
 
-func (c *BaseConfig) startConfigCenter(ctx context.Context) error {
-	url, err := common.NewURL(ctx, c.ConfigCenterConfig.Address, common.WithProtocol(c.ConfigCenterConfig.Protocol))
+// startConfigCenter will start the config center.
+// it will prepare the environment
+func (c *BaseConfig) startConfigCenter() error {
+	url, err := common.NewURL(c.ConfigCenterConfig.Address,
+		common.WithProtocol(c.ConfigCenterConfig.Protocol), common.WithParams(c.ConfigCenterConfig.GetUrlMap()))
 	if err != nil {
 		return err
 	}
@@ -68,7 +77,7 @@ func (c *BaseConfig) prepareEnvironment() error {
 		logger.Errorf("Get dynamic configuration error , error message is %v", err)
 		return perrors.WithStack(err)
 	}
-	content, err := dynamicConfig.GetConfig(c.ConfigCenterConfig.ConfigFile, config_center.WithGroup(c.ConfigCenterConfig.Group))
+	content, err := dynamicConfig.GetProperties(c.ConfigCenterConfig.ConfigFile, config_center.WithGroup(c.ConfigCenterConfig.Group))
 	if err != nil {
 		logger.Errorf("Get config content in dynamic configuration error , error message is %v", err)
 		return perrors.WithStack(err)
@@ -88,7 +97,10 @@ func (c *BaseConfig) prepareEnvironment() error {
 		if len(configFile) == 0 {
 			configFile = c.ConfigCenterConfig.ConfigFile
 		}
-		appContent, err = dynamicConfig.GetConfig(configFile, config_center.WithGroup(appGroup))
+		appContent, err = dynamicConfig.GetProperties(configFile, config_center.WithGroup(appGroup))
+		if err != nil {
+			return perrors.WithStack(err)
+		}
 	}
 	//global config file
 	mapContent, err := dynamicConfig.Parser().Parse(content)
@@ -129,6 +141,7 @@ func getKeyPrefix(val reflect.Value) []string {
 	return retPrefixs
 
 }
+
 func getPtrElement(v reflect.Value) reflect.Value {
 	if v.Kind() == reflect.Ptr {
 		v = v.Elem()
@@ -138,6 +151,7 @@ func getPtrElement(v reflect.Value) reflect.Value {
 	}
 	return v
 }
+
 func setFieldValue(val reflect.Value, id reflect.Value, config *config.InmemoryConfiguration) {
 	for i := 0; i < val.NumField(); i++ {
 		if key := val.Type().Field(i).Tag.Get("property"); key != "-" && key != "" {
@@ -291,11 +305,12 @@ func setFieldValue(val reflect.Value, id reflect.Value, config *config.InmemoryC
 		}
 	}
 }
+
 func (c *BaseConfig) fresh() {
 	configList := config.GetEnvInstance().Configuration()
 	for element := configList.Front(); element != nil; element = element.Next() {
-		config := element.Value.(*config.InmemoryConfiguration)
-		c.freshInternalConfig(config)
+		cfg := element.Value.(*config.InmemoryConfiguration)
+		c.freshInternalConfig(cfg)
 	}
 }
 
@@ -308,6 +323,7 @@ func (c *BaseConfig) freshInternalConfig(config *config.InmemoryConfiguration) {
 	setFieldValue(val, reflect.Value{}, config)
 }
 
+// SetFatherConfig ...
 func (c *BaseConfig) SetFatherConfig(fatherConfig interface{}) {
 	c.fatherConfig = fatherConfig
 }
@@ -350,3 +366,25 @@ func initializeStruct(t reflect.Type, v reflect.Value) {
 	}
 
 }
+
+// loadYMLConfig Load yml config byte from file
+func loadYMLConfig(confProFile string) ([]byte, error) {
+	if len(confProFile) == 0 {
+		return nil, perrors.Errorf("application configure(provider) file name is nil")
+	}
+
+	if path.Ext(confProFile) != ".yml" {
+		return nil, perrors.Errorf("application configure file name{%v} suffix must be .yml", confProFile)
+	}
+
+	return ioutil.ReadFile(confProFile)
+}
+
+// unmarshalYMLConfig Load yml config byte from file , then unmarshal to object
+func unmarshalYMLConfig(confProFile string, out interface{}) error {
+	confFileStream, err := loadYMLConfig(confProFile)
+	if err != nil {
+		return perrors.Errorf("ioutil.ReadFile(file:%s) = error:%v", confProFile, perrors.WithStack(err))
+	}
+	return yaml.Unmarshal(confFileStream, out)
+}
diff --git a/config/base_config_test.go b/config/base_config_test.go
index 6dc3749e55f7efbfb1177079f613360cd0d4cc33..6973a4a18b5e3a78d9039bc818a5a2a046613783 100644
--- a/config/base_config_test.go
+++ b/config/base_config_test.go
@@ -17,8 +17,8 @@
 package config
 
 import (
-	"context"
 	"fmt"
+	"path/filepath"
 	"reflect"
 	"testing"
 )
@@ -29,6 +29,7 @@ import (
 	"github.com/apache/dubbo-go/common/config"
 	"github.com/apache/dubbo-go/common/extension"
 	"github.com/apache/dubbo-go/config_center"
+	_ "github.com/apache/dubbo-go/config_center/apollo"
 )
 
 func Test_refresh(t *testing.T) {
@@ -39,6 +40,7 @@ func Test_refresh(t *testing.T) {
 	mockMap["dubbo.com.MockService.MockService.GetUser.retries"] = "10"
 	mockMap["dubbo.consumer.check"] = "false"
 	mockMap["dubbo.application.name"] = "dubbo"
+	mockMap["dubbo.shutdown.timeout"] = "12s"
 
 	config.GetEnvInstance().UpdateExternalConfigMap(mockMap)
 
@@ -113,6 +115,13 @@ func Test_refresh(t *testing.T) {
 				},
 			},
 		},
+		ShutdownConfig: &ShutdownConfig{
+			Timeout:              "12s",
+			StepTimeout:          "2s",
+			RejectRequestHandler: "mock",
+			RejectRequest:        false,
+			RequestsFinished:     false,
+		},
 	}
 
 	c.SetFatherConfig(father)
@@ -483,7 +492,7 @@ func Test_startConfigCenter(t *testing.T) {
 		Group:      "dubbo",
 		ConfigFile: "mockDubbo.properties",
 	}}
-	err := c.startConfigCenter(context.Background())
+	err := c.startConfigCenter()
 	assert.NoError(t, err)
 	b, v := config.GetEnvInstance().Configuration().Back().Value.(*config.InmemoryConfiguration).GetProperty("dubbo.application.organization")
 	assert.True(t, b)
@@ -509,3 +518,13 @@ func Test_initializeStruct(t *testing.T) {
 		return consumerConfig.References != nil
 	})
 }
+
+func TestUnmarshalYMLConfig(t *testing.T) {
+	conPath, err := filepath.Abs("./testdata/consumer_config_with_configcenter.yml")
+	assert.NoError(t, err)
+	c := &ConsumerConfig{}
+	assert.NoError(t, unmarshalYMLConfig(conPath, c))
+	assert.Equal(t, "default", c.ProxyFactory)
+	assert.Equal(t, "dubbo.properties", c.ConfigCenterConfig.ConfigFile)
+	assert.Equal(t, "100ms", c.Connect_Timeout)
+}
diff --git a/config/condition_router_config.go b/config/condition_router_config.go
new file mode 100644
index 0000000000000000000000000000000000000000..a95b2d2b1265a4c069abd8cbc682a9474c15f454
--- /dev/null
+++ b/config/condition_router_config.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 (
+	perrors "github.com/pkg/errors"
+)
+
+import (
+	"github.com/apache/dubbo-go/cluster/directory"
+	"github.com/apache/dubbo-go/common/extension"
+	"github.com/apache/dubbo-go/common/logger"
+)
+
+//RouterInit Load config file to init router config
+func RouterInit(confRouterFile string) error {
+	fileRouterFactories := extension.GetFileRouterFactories()
+	bytes, err := loadYMLConfig(confRouterFile)
+	if err != nil {
+		return perrors.Errorf("ioutil.ReadFile(file:%s) = error:%v", confRouterFile, perrors.WithStack(err))
+	}
+	for k, factory := range fileRouterFactories {
+		r, e := factory.NewFileRouter(bytes)
+		if e == nil {
+			url := r.URL()
+			directory.AddRouterURLSet(&url)
+			return nil
+		}
+		logger.Warnf("router config type %s create fail \n", k)
+	}
+	return perrors.Errorf("no file router exists for parse %s , implement router.FIleRouterFactory please.", confRouterFile)
+}
diff --git a/config/condition_router_config_test.go b/config/condition_router_config_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..2f0a38b2fdf59578c77076680c05b3eca5c26a1c
--- /dev/null
+++ b/config/condition_router_config_test.go
@@ -0,0 +1,67 @@
+/*
+ * 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 (
+	"strings"
+	"testing"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+import (
+	"github.com/apache/dubbo-go/cluster/directory"
+	_ "github.com/apache/dubbo-go/cluster/router/condition"
+)
+
+const testYML = "testdata/router_config.yml"
+const errorTestYML = "testdata/router_config_error.yml"
+
+func TestString(t *testing.T) {
+
+	s := "a1=>a2"
+	s1 := "=>a2"
+	s2 := "a1=>"
+
+	n := strings.SplitN(s, "=>", 2)
+	n1 := strings.SplitN(s1, "=>", 2)
+	n2 := strings.SplitN(s2, "=>", 2)
+
+	assert.Equal(t, n[0], "a1")
+	assert.Equal(t, n[1], "a2")
+
+	assert.Equal(t, n1[0], "")
+	assert.Equal(t, n1[1], "a2")
+
+	assert.Equal(t, n2[0], "a1")
+	assert.Equal(t, n2[1], "")
+}
+
+func TestRouterInit(t *testing.T) {
+	errPro := RouterInit(errorTestYML)
+	assert.Error(t, errPro)
+
+	assert.Equal(t, 0, directory.GetRouterURLSet().Size())
+
+	errPro = RouterInit(testYML)
+	assert.NoError(t, errPro)
+
+	assert.Equal(t, 1, directory.GetRouterURLSet().Size())
+}
diff --git a/config/config_center_config.go b/config/config_center_config.go
index ed43558956a181e669a1a8936182b65a2fb2766c..40b9b6517186a8a4f7956db3d23f0a1cdfbdc8cb 100644
--- a/config/config_center_config.go
+++ b/config/config_center_config.go
@@ -19,6 +19,7 @@ package config
 
 import (
 	"context"
+	"net/url"
 	"time"
 )
 
@@ -26,6 +27,11 @@ import (
 	"github.com/creasty/defaults"
 )
 
+import (
+	"github.com/apache/dubbo-go/common/constant"
+)
+
+// ConfigCenterConfig ...
 type ConfigCenterConfig struct {
 	context       context.Context
 	Protocol      string `required:"true"  yaml:"protocol"  json:"protocol,omitempty"`
@@ -35,11 +41,14 @@ type ConfigCenterConfig struct {
 	Username      string `yaml:"username" json:"username,omitempty"`
 	Password      string `yaml:"password" json:"password,omitempty"`
 	ConfigFile    string `default:"dubbo.properties" yaml:"config_file"  json:"config_file,omitempty"`
+	Namespace     string `default:"dubbo" yaml:"namespace"  json:"namespace,omitempty"`
 	AppConfigFile string `default:"dubbo.properties" yaml:"app_config_file"  json:"app_config_file,omitempty"`
+	AppId         string `default:"dubbo" yaml:"app_id"  json:"app_id,omitempty"`
 	TimeoutStr    string `yaml:"timeout"  json:"timeout,omitempty"`
 	timeout       time.Duration
 }
 
+// UnmarshalYAML ...
 func (c *ConfigCenterConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
 	if err := defaults.Set(c); err != nil {
 		return err
@@ -50,3 +59,13 @@ func (c *ConfigCenterConfig) UnmarshalYAML(unmarshal func(interface{}) error) er
 	}
 	return nil
 }
+
+// GetUrlMap ...
+func (c *ConfigCenterConfig) GetUrlMap() url.Values {
+	urlMap := url.Values{}
+	urlMap.Set(constant.CONFIG_NAMESPACE_KEY, c.Namespace)
+	urlMap.Set(constant.CONFIG_GROUP_KEY, c.Group)
+	urlMap.Set(constant.CONFIG_CLUSTER_KEY, c.Cluster)
+	urlMap.Set(constant.CONFIG_APP_ID_KEY, c.AppId)
+	return urlMap
+}
diff --git a/config/config_loader.go b/config/config_loader.go
index b737d3f233700f596469cfd678aa7ae7f9a82b85..437f4d7323e66afcf62808b3c8d6bf51cc5bce88 100644
--- a/config/config_loader.go
+++ b/config/config_loader.go
@@ -31,24 +31,31 @@ import (
 )
 
 var (
-	consumerConfig *ConsumerConfig
-	providerConfig *ProviderConfig
-	maxWait        = 3
+	consumerConfig    *ConsumerConfig
+	providerConfig    *ProviderConfig
+	metricConfig      *MetricConfig
+	applicationConfig *ApplicationConfig
+	maxWait           = 3
+	confRouterFile    string
 )
 
 // loaded consumer & provider config from xxx.yml, and log config from xxx.xml
 // Namely: dubbo.consumer.xml & dubbo.provider.xml in java dubbo
 func init() {
 	var (
-		confConFile, confProFile string
+		confConFile string
+		confProFile string
 	)
 
 	confConFile = os.Getenv(constant.CONF_CONSUMER_FILE_PATH)
 	confProFile = os.Getenv(constant.CONF_PROVIDER_FILE_PATH)
+	confRouterFile = os.Getenv(constant.CONF_ROUTER_FILE_PATH)
+
 	if errCon := ConsumerInit(confConFile); errCon != nil {
 		log.Printf("[consumerInit] %#v", errCon)
 		consumerConfig = nil
 	}
+
 	if errPro := ProviderInit(confProFile); errPro != nil {
 		log.Printf("[providerInit] %#v", errPro)
 		providerConfig = nil
@@ -69,12 +76,23 @@ func checkApplicationName(config *ApplicationConfig) {
 	}
 }
 
-// Dubbo Init
+// Load Dubbo Init
 func Load() {
+	// init router
+	if confRouterFile != "" {
+		if errPro := RouterInit(confRouterFile); errPro != nil {
+			log.Printf("[routerConfig init] %#v", errPro)
+		}
+	}
+
 	// reference config
 	if consumerConfig == nil {
 		logger.Warnf("consumerConfig is nil!")
 	} else {
+
+		metricConfig = consumerConfig.MetricConfig
+		applicationConfig = consumerConfig.ApplicationConfig
+
 		checkApplicationName(consumerConfig.ApplicationConfig)
 		if err := configCenterRefreshConsumer(); err != nil {
 			logger.Errorf("[consumer config center refresh] %#v", err)
@@ -91,9 +109,10 @@ func Load() {
 				continue
 			}
 			ref.id = key
-			ref.Refer()
+			ref.Refer(rpcService)
 			ref.Implement(rpcService)
 		}
+
 		//wait for invoker is available, if wait over default 3s, then panic
 		var count int
 		checkok := true
@@ -131,6 +150,11 @@ func Load() {
 	if providerConfig == nil {
 		logger.Warnf("providerConfig is nil!")
 	} else {
+
+		// 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)
@@ -149,14 +173,55 @@ func Load() {
 			}
 		}
 	}
+	// init the shutdown callback
+	GracefulShutdownInit()
 }
 
-// get rpc service for consumer
+// GetRPCService get rpc service for consumer
 func GetRPCService(name string) common.RPCService {
 	return consumerConfig.References[name].GetRPCService()
 }
 
-// create rpc service for consumer
+// RPCService create rpc service for consumer
 func RPCService(service common.RPCService) {
 	consumerConfig.References[service.Reference()].Implement(service)
 }
+
+// GetMetricConfig find the MetricConfig
+// if it is nil, create a new one
+func GetMetricConfig() *MetricConfig {
+	if metricConfig == nil {
+		metricConfig = &MetricConfig{}
+	}
+	return metricConfig
+}
+
+// GetApplicationConfig find the application config
+// if not, we will create one
+// Usually applicationConfig will be initialized when system start
+func GetApplicationConfig() *ApplicationConfig {
+	if applicationConfig == nil {
+		applicationConfig = &ApplicationConfig{}
+	}
+	return applicationConfig
+}
+
+// GetProviderConfig find the provider config
+// if not found, create new one
+func GetProviderConfig() ProviderConfig {
+	if providerConfig == nil {
+		logger.Warnf("providerConfig is nil!")
+		return ProviderConfig{}
+	}
+	return *providerConfig
+}
+
+// GetConsumerConfig find the consumer config
+// if not found, create new one
+func GetConsumerConfig() ConsumerConfig {
+	if consumerConfig == nil {
+		logger.Warnf("consumerConfig is nil!")
+		return ConsumerConfig{}
+	}
+	return *consumerConfig
+}
diff --git a/config/consumer_config.go b/config/consumer_config.go
index b1ebdd5d8e082bf836071460e2a330632e07335c..94da301ce45acedb720120d56dc07bf76c780d7f 100644
--- a/config/consumer_config.go
+++ b/config/consumer_config.go
@@ -14,19 +14,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package config
 
 import (
-	"context"
-	"io/ioutil"
-	"path"
 	"time"
 )
 
 import (
 	"github.com/creasty/defaults"
+	"github.com/dubbogo/getty"
 	perrors "github.com/pkg/errors"
-	"gopkg.in/yaml.v2"
 )
 
 import (
@@ -38,6 +36,7 @@ import (
 // consumerConfig
 /////////////////////////
 
+// ConsumerConfig ...
 type ConsumerConfig struct {
 	BaseConfig `yaml:",inline"`
 	Filter     string `yaml:"filter" json:"filter,omitempty" property:"filter"`
@@ -52,13 +51,15 @@ type ConsumerConfig struct {
 	ProxyFactory    string `yaml:"proxy_factory" default:"default" json:"proxy_factory,omitempty" property:"proxy_factory"`
 	Check           *bool  `yaml:"check"  json:"check,omitempty" property:"check"`
 
-	Registry     *RegistryConfig             `yaml:"registry" json:"registry,omitempty" property:"registry"`
-	Registries   map[string]*RegistryConfig  `yaml:"registries" json:"registries,omitempty" property:"registries"`
-	References   map[string]*ReferenceConfig `yaml:"references" json:"references,omitempty" property:"references"`
-	ProtocolConf interface{}                 `yaml:"protocol_conf" json:"protocol_conf,omitempty" property:"protocol_conf"`
-	FilterConf   interface{}                 `yaml:"filter_conf" json:"filter_conf,omitempty" property:"filter_conf" `
+	Registry       *RegistryConfig             `yaml:"registry" json:"registry,omitempty" property:"registry"`
+	Registries     map[string]*RegistryConfig  `yaml:"registries" json:"registries,omitempty" property:"registries"`
+	References     map[string]*ReferenceConfig `yaml:"references" json:"references,omitempty" property:"references"`
+	ProtocolConf   interface{}                 `yaml:"protocol_conf" json:"protocol_conf,omitempty" property:"protocol_conf"`
+	FilterConf     interface{}                 `yaml:"filter_conf" json:"filter_conf,omitempty" property:"filter_conf" `
+	ShutdownConfig *ShutdownConfig             `yaml:"shutdown_conf" json:"shutdown_conf,omitempty" property:"shutdown_conf" `
 }
 
+// UnmarshalYAML ...
 func (c *ConsumerConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
 	if err := defaults.Set(c); err != nil {
 		return err
@@ -70,37 +71,24 @@ func (c *ConsumerConfig) UnmarshalYAML(unmarshal func(interface{}) error) error
 	return nil
 }
 
+// Prefix ...
 func (*ConsumerConfig) Prefix() string {
 	return constant.ConsumerConfigPrefix
 }
 
+// SetConsumerConfig ...
 func SetConsumerConfig(c ConsumerConfig) {
 	consumerConfig = &c
 }
 
-func GetConsumerConfig() ConsumerConfig {
-	if consumerConfig == nil {
-		logger.Warnf("consumerConfig is nil!")
-		return ConsumerConfig{}
-	}
-	return *consumerConfig
-}
-
+// ConsumerInit ...
 func ConsumerInit(confConFile string) error {
 	if confConFile == "" {
 		return perrors.Errorf("application configure(consumer) file name is nil")
 	}
 
-	if path.Ext(confConFile) != ".yml" {
-		return perrors.Errorf("application configure file name{%v} suffix must be .yml", confConFile)
-	}
-
-	confFileStream, err := ioutil.ReadFile(confConFile)
-	if err != nil {
-		return perrors.Errorf("ioutil.ReadFile(file:%s) = error:%v", confConFile, perrors.WithStack(err))
-	}
 	consumerConfig = &ConsumerConfig{}
-	err = yaml.Unmarshal(confFileStream, consumerConfig)
+	err := unmarshalYMLConfig(confConFile, consumerConfig)
 	if err != nil {
 		return perrors.Errorf("yaml.Unmarshal() = error:%v", perrors.WithStack(err))
 	}
@@ -117,6 +105,10 @@ func ConsumerInit(confConFile string) error {
 		if consumerConfig.RequestTimeout, err = time.ParseDuration(consumerConfig.Request_Timeout); err != nil {
 			return perrors.WithMessagef(err, "time.ParseDuration(Request_Timeout{%#v})", consumerConfig.Request_Timeout)
 		}
+		if consumerConfig.RequestTimeout >= time.Duration(getty.MaxWheelTimeSpan) {
+			return perrors.WithMessagef(err, "request_timeout %s should be less than %s",
+				consumerConfig.Request_Timeout, time.Duration(getty.MaxWheelTimeSpan))
+		}
 	}
 	if consumerConfig.Connect_Timeout != "" {
 		if consumerConfig.ConnectTimeout, err = time.ParseDuration(consumerConfig.Connect_Timeout); err != nil {
@@ -132,7 +124,7 @@ func configCenterRefreshConsumer() error {
 	var err error
 	if consumerConfig.ConfigCenterConfig != nil {
 		consumerConfig.SetFatherConfig(consumerConfig)
-		if err := consumerConfig.startConfigCenter(context.Background()); err != nil {
+		if err := consumerConfig.startConfigCenter(); err != nil {
 			return perrors.Errorf("start config center error , error message is {%v}", perrors.WithStack(err))
 		}
 		consumerConfig.fresh()
diff --git a/config/generic_service.go b/config/generic_service.go
index 8a4e88df9788554bc4a5ee33884166e4ccede37f..9895486e977a9848e576597f31b724d51d144d4e 100644
--- a/config/generic_service.go
+++ b/config/generic_service.go
@@ -14,17 +14,21 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package config
 
+// GenericService ...
 type GenericService struct {
 	Invoke       func(req []interface{}) (interface{}, error) `dubbo:"$invoke"`
 	referenceStr string
 }
 
+// NewGenericService ...
 func NewGenericService(referenceStr string) *GenericService {
 	return &GenericService{referenceStr: referenceStr}
 }
 
+// Reference ...
 func (u *GenericService) Reference() string {
 	return u.referenceStr
 }
diff --git a/config/graceful_shutdown.go b/config/graceful_shutdown.go
new file mode 100644
index 0000000000000000000000000000000000000000..382f05c8d57c4363108873433fd03565d03b9a50
--- /dev/null
+++ b/config/graceful_shutdown.go
@@ -0,0 +1,231 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package config
+
+import (
+	"os"
+	"os/signal"
+	"runtime/debug"
+	"time"
+)
+
+import (
+	gxset "github.com/dubbogo/gost/container/set"
+)
+
+import (
+	"github.com/apache/dubbo-go/common/constant"
+	"github.com/apache/dubbo-go/common/extension"
+	"github.com/apache/dubbo-go/common/logger"
+)
+
+/*
+ * The key point is that find out the signals to handle.
+ * The most important documentation is https://golang.org/pkg/os/signal/
+ * From this documentation, we can know that:
+ * 1. The signals SIGKILL and SIGSTOP may not be caught by signal package;
+ * 2. SIGHUP, SIGINT, or SIGTERM signal causes the program to exit
+ * 3. SIGQUIT, SIGILL, SIGTRAP, SIGABRT, SIGSTKFLT, SIGEMT, or SIGSYS signal causes the program to exit with a stack dump
+ * 4. The invocation of Notify(signal...) will disable the default behavior of those signals.
+ *
+ * So the signals SIGKILL, SIGSTOP, SIGHUP, SIGINT, SIGTERM, SIGQUIT, SIGILL, SIGTRAP, SIGABRT, SIGSTKFLT, SIGEMT, SIGSYS
+ * should be processed.
+ * syscall.SIGEMT cannot be found in CI
+ * It's seems that the Unix/Linux does not have the signal SIGSTKFLT. https://github.com/golang/go/issues/33381
+ * So this signal will be ignored.
+ * The signals are different on different platforms.
+ * We define them by using 'package build' feature https://golang.org/pkg/go/build/
+ */
+
+// GracefulShutdownInit ...
+func GracefulShutdownInit() {
+
+	signals := make(chan os.Signal, 1)
+
+	signal.Notify(signals, ShutdownSignals...)
+
+	go func() {
+		select {
+		case sig := <-signals:
+			logger.Infof("get signal %s, application will shutdown.", sig)
+			// gracefulShutdownOnce.Do(func() {
+			BeforeShutdown()
+
+			// those signals' original behavior is exit with dump ths stack, so we try to keep the behavior
+			for _, dumpSignal := range DumpHeapShutdownSignals {
+				if sig == dumpSignal {
+					debug.WriteHeapDump(os.Stdout.Fd())
+				}
+			}
+
+			time.AfterFunc(totalTimeout(), func() {
+				logger.Warn("Shutdown gracefully timeout, application will shutdown immediately. ")
+				os.Exit(0)
+			})
+
+			os.Exit(0)
+		}
+	}()
+}
+
+// BeforeShutdown ...
+func BeforeShutdown() {
+
+	destroyAllRegistries()
+	// waiting for a short time so that the clients have enough time to get the notification that server shutdowns
+	// The value of configuration depends on how long the clients will get notification.
+	waitAndAcceptNewRequests()
+
+	// reject the new request, but keeping waiting for accepting requests
+	waitForReceivingRequests()
+
+	// we fetch the protocols from Consumer.References. Consumer.ProtocolConfig doesn't contains all protocol, like jsonrpc
+	consumerProtocols := getConsumerProtocols()
+
+	// If this application is not the provider, it will do nothing
+	destroyProviderProtocols(consumerProtocols)
+
+	// reject sending the new request, and waiting for response of sending requests
+	waitForSendingRequests()
+
+	// If this application is not the consumer, it will do nothing
+	destroyConsumerProtocols(consumerProtocols)
+
+	logger.Info("Graceful shutdown --- Execute the custom callbacks.")
+	customCallbacks := extension.GetAllCustomShutdownCallbacks()
+	for callback := customCallbacks.Front(); callback != nil; callback = callback.Next() {
+		callback.Value.(func())()
+	}
+}
+
+func destroyAllRegistries() {
+	logger.Info("Graceful shutdown --- Destroy all registries. ")
+	registryProtocol := extension.GetProtocol(constant.REGISTRY_KEY)
+	registryProtocol.Destroy()
+}
+
+func destroyConsumerProtocols(consumerProtocols *gxset.HashSet) {
+	logger.Info("Graceful shutdown --- Destroy consumer's protocols. ")
+	for name := range consumerProtocols.Items {
+		extension.GetProtocol(name.(string)).Destroy()
+	}
+}
+
+/**
+ * destroy the provider's protocol.
+ * if the protocol is consumer's protocol too, we will keep it.
+ */
+func destroyProviderProtocols(consumerProtocols *gxset.HashSet) {
+
+	logger.Info("Graceful shutdown --- Destroy provider's protocols. ")
+
+	if providerConfig == nil || providerConfig.Protocols == nil {
+		return
+	}
+
+	for _, protocol := range providerConfig.Protocols {
+
+		// the protocol is the consumer's protocol too, we can not destroy it.
+		if consumerProtocols.Contains(protocol.Name) {
+			continue
+		}
+		extension.GetProtocol(protocol.Name).Destroy()
+	}
+}
+
+func waitAndAcceptNewRequests() {
+
+	logger.Info("Graceful shutdown --- Keep waiting and accept new requests for a short time. ")
+	if providerConfig == nil || providerConfig.ShutdownConfig == nil {
+		return
+	}
+
+	timeout := providerConfig.ShutdownConfig.GetStepTimeout()
+
+	// ignore this step
+	if timeout < 0 {
+		return
+	}
+	time.Sleep(timeout)
+}
+
+// for provider. It will wait for processing receiving requests
+func waitForReceivingRequests() {
+	logger.Info("Graceful shutdown --- Keep waiting until accepting requests finish or timeout. ")
+	if providerConfig == nil || providerConfig.ShutdownConfig == nil {
+		// ignore this step
+		return
+	}
+	waitingProcessedTimeout(providerConfig.ShutdownConfig)
+}
+
+// for consumer. It will wait for the response of sending requests
+func waitForSendingRequests() {
+	logger.Info("Graceful shutdown --- Keep waiting until sending requests getting response or timeout ")
+	if consumerConfig == nil || consumerConfig.ShutdownConfig == nil {
+		// ignore this step
+		return
+	}
+	waitingProcessedTimeout(consumerConfig.ShutdownConfig)
+}
+
+func waitingProcessedTimeout(shutdownConfig *ShutdownConfig) {
+	timeout := shutdownConfig.GetStepTimeout()
+	if timeout <= 0 {
+		return
+	}
+	start := time.Now()
+
+	for time.Now().After(start.Add(timeout)) && !shutdownConfig.RequestsFinished {
+		// sleep 10 ms and then we check it again
+		time.Sleep(10 * time.Millisecond)
+	}
+}
+
+func totalTimeout() time.Duration {
+	var providerShutdown time.Duration
+	if providerConfig != nil && providerConfig.ShutdownConfig != nil {
+		providerShutdown = providerConfig.ShutdownConfig.GetTimeout()
+	}
+
+	var consumerShutdown time.Duration
+	if consumerConfig != nil && consumerConfig.ShutdownConfig != nil {
+		consumerShutdown = consumerConfig.ShutdownConfig.GetTimeout()
+	}
+
+	var timeout = providerShutdown
+	if consumerShutdown > providerShutdown {
+		timeout = consumerShutdown
+	}
+	return timeout
+}
+
+/*
+ * we can not get the protocols from consumerConfig because some protocol don't have configuration, like jsonrpc.
+ */
+func getConsumerProtocols() *gxset.HashSet {
+	result := gxset.NewSet()
+	if consumerConfig == nil || consumerConfig.References == nil {
+		return result
+	}
+
+	for _, reference := range consumerConfig.References {
+		result.Add(reference.Protocol)
+	}
+	return result
+}
diff --git a/config/graceful_shutdown_config.go b/config/graceful_shutdown_config.go
new file mode 100644
index 0000000000000000000000000000000000000000..6bbabebf2538effcbbe4bddc50857acf5f962a61
--- /dev/null
+++ b/config/graceful_shutdown_config.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 config
+
+import (
+	"time"
+)
+
+import (
+	"github.com/apache/dubbo-go/common/constant"
+	"github.com/apache/dubbo-go/common/logger"
+)
+
+const (
+	defaultTimeout     = 60 * time.Second
+	defaultStepTimeout = 10 * time.Second
+)
+
+// ShutdownConfig ...
+type ShutdownConfig struct {
+	/*
+	 * Total timeout. Even though we don't release all resources,
+	 * the application will shutdown if the costing time is over this configuration. The unit is ms.
+	 * default value is 60 * 1000 ms = 1 minutes
+	 * In general, it should be bigger than 3 * StepTimeout.
+	 */
+	Timeout string `default:"60s" yaml:"timeout" json:"timeout,omitempty" property:"timeout"`
+	/*
+	 * the timeout on each step. You should evaluate the response time of request
+	 * and the time that client noticed that server shutdown.
+	 * For example, if your client will received the notification within 10s when you start to close server,
+	 * and the 99.9% requests will return response in 2s, so the StepTimeout will be bigger than(10+2) * 1000ms,
+	 * maybe (10 + 2*3) * 1000ms is a good choice.
+	 */
+	StepTimeout string `default:"10s" yaml:"step_timeout" json:"step.timeout,omitempty" property:"step.timeout"`
+	// when we try to shutdown the application, we will reject the new requests. In most cases, you don't need to configure this.
+	RejectRequestHandler string `yaml:"reject_handler" json:"reject_handler,omitempty" property:"reject_handler"`
+	// true -> new request will be rejected.
+	RejectRequest bool
+
+	// true -> all requests had been processed. In provider side it means that all requests are returned response to clients
+	// In consumer side, it means that all requests getting response from servers
+	RequestsFinished bool
+}
+
+// Prefix ...
+func (config *ShutdownConfig) Prefix() string {
+	return constant.ShutdownConfigPrefix
+}
+
+// GetTimeout ...
+func (config *ShutdownConfig) GetTimeout() time.Duration {
+	result, err := time.ParseDuration(config.Timeout)
+	if err != nil {
+		logger.Errorf("The Timeout configuration is invalid: %s, and we will use the default value: %s, err: %v",
+			config.Timeout, defaultTimeout.String(), err)
+		return defaultTimeout
+	}
+	return result
+}
+
+// GetStepTimeout ...
+func (config *ShutdownConfig) GetStepTimeout() time.Duration {
+	result, err := time.ParseDuration(config.StepTimeout)
+	if err != nil {
+		logger.Errorf("The StepTimeout configuration is invalid: %s, and we will use the default value: %s, err: %v",
+			config.StepTimeout, defaultStepTimeout.String(), err)
+		return defaultStepTimeout
+	}
+	return result
+}
diff --git a/config/graceful_shutdown_config_test.go b/config/graceful_shutdown_config_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..583ed70b838a8271a47e180ee3c6eb32cbb46984
--- /dev/null
+++ b/config/graceful_shutdown_config_test.go
@@ -0,0 +1,49 @@
+/*
+ * 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"
+	"time"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+func TestShutdownConfig_GetTimeout(t *testing.T) {
+	config := ShutdownConfig{}
+	assert.False(t, config.RejectRequest)
+	assert.False(t, config.RequestsFinished)
+
+	config = ShutdownConfig{
+		Timeout:     "12x",
+		StepTimeout: "34a",
+	}
+
+	assert.Equal(t, 60*time.Second, config.GetTimeout())
+	assert.Equal(t, 10*time.Second, config.GetStepTimeout())
+
+	config = ShutdownConfig{
+		Timeout:     "34ms",
+		StepTimeout: "79ms",
+	}
+
+	assert.Equal(t, 34*time.Millisecond, config.GetTimeout())
+	assert.Equal(t, 79*time.Millisecond, config.GetStepTimeout())
+}
diff --git a/config/graceful_shutdown_signal_darwin.go b/config/graceful_shutdown_signal_darwin.go
new file mode 100644
index 0000000000000000000000000000000000000000..8ad79ffa62ceed4096c60bfb9139b7ff1586808e
--- /dev/null
+++ b/config/graceful_shutdown_signal_darwin.go
@@ -0,0 +1,34 @@
+/*
+ * 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 (
+	"os"
+	"syscall"
+)
+
+var (
+	// ShutdownSignals ...
+	ShutdownSignals = []os.Signal{os.Interrupt, os.Kill, syscall.SIGKILL, syscall.SIGSTOP,
+		syscall.SIGHUP, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGILL, syscall.SIGTRAP,
+		syscall.SIGABRT, syscall.SIGSYS}
+
+	// DumpHeapShutdownSignals ...
+	DumpHeapShutdownSignals = []os.Signal{syscall.SIGQUIT, syscall.SIGILL,
+		syscall.SIGTRAP, syscall.SIGABRT, syscall.SIGSYS}
+)
diff --git a/config/graceful_shutdown_signal_linux.go b/config/graceful_shutdown_signal_linux.go
new file mode 100644
index 0000000000000000000000000000000000000000..8ad79ffa62ceed4096c60bfb9139b7ff1586808e
--- /dev/null
+++ b/config/graceful_shutdown_signal_linux.go
@@ -0,0 +1,34 @@
+/*
+ * 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 (
+	"os"
+	"syscall"
+)
+
+var (
+	// ShutdownSignals ...
+	ShutdownSignals = []os.Signal{os.Interrupt, os.Kill, syscall.SIGKILL, syscall.SIGSTOP,
+		syscall.SIGHUP, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGILL, syscall.SIGTRAP,
+		syscall.SIGABRT, syscall.SIGSYS}
+
+	// DumpHeapShutdownSignals ...
+	DumpHeapShutdownSignals = []os.Signal{syscall.SIGQUIT, syscall.SIGILL,
+		syscall.SIGTRAP, syscall.SIGABRT, syscall.SIGSYS}
+)
diff --git a/config/graceful_shutdown_signal_windows.go b/config/graceful_shutdown_signal_windows.go
new file mode 100644
index 0000000000000000000000000000000000000000..815a05ecb20a8fc202debaf6f39d699845cd689e
--- /dev/null
+++ b/config/graceful_shutdown_signal_windows.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 config
+
+import (
+	"os"
+	"syscall"
+)
+
+var (
+	// ShutdownSignals ...
+	ShutdownSignals = []os.Signal{os.Interrupt, os.Kill, syscall.SIGKILL,
+		syscall.SIGHUP, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGILL, syscall.SIGTRAP,
+		syscall.SIGABRT}
+
+	// DumpHeapShutdownSignals ...
+	DumpHeapShutdownSignals = []os.Signal{syscall.SIGQUIT, syscall.SIGILL, syscall.SIGTRAP, syscall.SIGABRT}
+)
diff --git a/config/graceful_shutdown_test.go b/config/graceful_shutdown_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..de203572c76281d221181dea90b0f31b43038de6
--- /dev/null
+++ b/config/graceful_shutdown_test.go
@@ -0,0 +1,99 @@
+/*
+ * 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/apache/dubbo-go/common/constant"
+	"github.com/apache/dubbo-go/common/extension"
+	"github.com/apache/dubbo-go/protocol"
+)
+
+func TestGracefulShutdownInit(t *testing.T) {
+	GracefulShutdownInit()
+}
+
+func TestBeforeShutdown(t *testing.T) {
+	extension.SetProtocol("registry", func() protocol.Protocol {
+		return &mockRegistryProtocol{}
+	})
+	extension.SetProtocol(constant.DUBBO, func() protocol.Protocol {
+		return &mockRegistryProtocol{}
+	})
+
+	extension.SetProtocol("mock", func() protocol.Protocol {
+		return &mockRegistryProtocol{}
+	})
+
+	// protocolConfigs := make(map[interface{}]interface{}, 16)
+	consumerReferences := map[string]*ReferenceConfig{}
+	consumerReferences[constant.DUBBO] = &ReferenceConfig{
+		Protocol: constant.DUBBO,
+	}
+
+	// without configuration
+	BeforeShutdown()
+
+	consumerConfig = &ConsumerConfig{
+		References: consumerReferences,
+		ShutdownConfig: &ShutdownConfig{
+			Timeout:     "1",
+			StepTimeout: "1s",
+		}}
+
+	providerProtocols := map[string]*ProtocolConfig{}
+	providerProtocols[constant.DUBBO] = &ProtocolConfig{
+		Name: constant.DUBBO,
+	}
+
+	providerProtocols["mock"] = &ProtocolConfig{
+		Name: "mock",
+	}
+
+	providerConfig = &ProviderConfig{
+		ShutdownConfig: &ShutdownConfig{
+			Timeout:     "1",
+			StepTimeout: "1s",
+		},
+		Protocols: providerProtocols,
+	}
+	// test destroy protocol
+	BeforeShutdown()
+
+	providerConfig = &ProviderConfig{
+		ShutdownConfig: &ShutdownConfig{
+			Timeout:     "1",
+			StepTimeout: "-1s",
+		},
+		Protocols: providerProtocols,
+	}
+
+	consumerConfig = &ConsumerConfig{
+		References: consumerReferences,
+		ShutdownConfig: &ShutdownConfig{
+			Timeout:     "1",
+			StepTimeout: "-1s",
+		},
+	}
+
+	// test ignore steps
+	BeforeShutdown()
+}
diff --git a/config/method_config.go b/config/method_config.go
index e3f0b1b01b5c6d753da216ecf906aee3bf305944..8f196d9e2c03071a663db03cb185fb9106d6484a 100644
--- a/config/method_config.go
+++ b/config/method_config.go
@@ -14,6 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package config
 
 import (
@@ -24,6 +25,7 @@ import (
 	"github.com/apache/dubbo-go/common/constant"
 )
 
+// MethodConfig ...
 type MethodConfig struct {
 	InterfaceId                 string
 	InterfaceName               string
@@ -36,16 +38,20 @@ type MethodConfig struct {
 	TpsLimitStrategy            string `yaml:"tps.limit.strategy" json:"tps.limit.strategy,omitempty" property:"tps.limit.strategy"`
 	ExecuteLimit                string `yaml:"execute.limit" json:"execute.limit,omitempty" property:"execute.limit"`
 	ExecuteLimitRejectedHandler string `yaml:"execute.limit.rejected.handler" json:"execute.limit.rejected.handler,omitempty" property:"execute.limit.rejected.handler"`
+	Sticky                      bool   `yaml:"sticky"   json:"sticky,omitempty" property:"sticky"`
+	RequestTimeout              string `yaml:"timeout"  json:"timeout,omitempty" property:"timeout"`
 }
 
+// Prefix ...
 func (c *MethodConfig) Prefix() string {
-	if c.InterfaceId != "" {
+	if len(c.InterfaceId) != 0 {
 		return constant.DUBBO + "." + c.InterfaceName + "." + c.InterfaceId + "." + c.Name + "."
-	} else {
-		return constant.DUBBO + "." + c.InterfaceName + "." + c.Name + "."
 	}
+
+	return constant.DUBBO + "." + c.InterfaceName + "." + c.Name + "."
 }
 
+// UnmarshalYAML ...
 func (c *MethodConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
 	if err := defaults.Set(c); err != nil {
 		return err
diff --git a/config/metric_config.go b/config/metric_config.go
new file mode 100644
index 0000000000000000000000000000000000000000..73a3ca1cfe4f1461db2e225947dd13199b2ad55e
--- /dev/null
+++ b/config/metric_config.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 config
+
+var (
+	defaultHistogramBucket = []float64{10, 50, 100, 200, 500, 1000, 10000}
+)
+
+// This is the config struct for all metrics implementation
+type MetricConfig struct {
+	Reporters       []string  `yaml:"reporters" json:"reporters,omitempty"`
+	HistogramBucket []float64 `yaml:"histogram_bucket" json:"histogram_bucket,omitempty"`
+}
+
+// find the histogram bucket
+// if it's empty, the default value will be return
+func (mc *MetricConfig) GetHistogramBucket() []float64 {
+	if len(mc.HistogramBucket) == 0 {
+		mc.HistogramBucket = defaultHistogramBucket
+	}
+	return mc.HistogramBucket
+}
diff --git a/config/metric_config_test.go b/config/metric_config_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..fe9d2493f37c0bd563931f5acf133105d72d0e53
--- /dev/null
+++ b/config/metric_config_test.go
@@ -0,0 +1,31 @@
+/*
+ * 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 TestGetMetricConfig(t *testing.T) {
+	empty := GetMetricConfig()
+	assert.NotNil(t, empty)
+}
diff --git a/config/mock_rpcservice.go b/config/mock_rpcservice.go
index 64d431ffb6dfbc7e25a988c6093cf0ab5cbd2db5..6c43699128247bf0ec483eb83f879bf4c3b67a37 100644
--- a/config/mock_rpcservice.go
+++ b/config/mock_rpcservice.go
@@ -21,16 +21,20 @@ import (
 	"context"
 )
 
+// MockService ...
 type MockService struct{}
 
+// Reference ...
 func (*MockService) Reference() string {
 	return "MockService"
 }
 
+// GetUser ...
 func (*MockService) GetUser(ctx context.Context, itf []interface{}, str *struct{}) error {
 	return nil
 }
 
+// GetUser1 ...
 func (*MockService) GetUser1(ctx context.Context, itf []interface{}, str *struct{}) error {
 	return nil
 }
diff --git a/config/protocol_config.go b/config/protocol_config.go
index 2803456dbcd44211fb6deef883beb7f5dbbf54ad..33de976bc6f5bf7341ddcff8d51c505cf23bbccd 100644
--- a/config/protocol_config.go
+++ b/config/protocol_config.go
@@ -14,6 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package config
 
 import (
@@ -24,25 +25,26 @@ import (
 	"github.com/apache/dubbo-go/common/constant"
 )
 
+// ProtocolConfig ...
 type ProtocolConfig struct {
 	Name string `required:"true" yaml:"name"  json:"name,omitempty" property:"name"`
 	Ip   string `required:"true" yaml:"ip"  json:"ip,omitempty" property:"ip"`
 	Port string `required:"true" yaml:"port"  json:"port,omitempty" property:"port"`
 }
 
+// Prefix ...
 func (c *ProtocolConfig) Prefix() string {
 	return constant.ProtocolConfigPrefix
 }
 
 func loadProtocol(protocolsIds string, protocols map[string]*ProtocolConfig) []*ProtocolConfig {
-	returnProtocols := []*ProtocolConfig{}
+	returnProtocols := make([]*ProtocolConfig, 0, len(protocols))
 	for _, v := range strings.Split(protocolsIds, ",") {
-		for k, prot := range protocols {
+		for k, protocol := range protocols {
 			if v == k {
-				returnProtocols = append(returnProtocols, prot)
+				returnProtocols = append(returnProtocols, protocol)
 			}
 		}
-
 	}
 	return returnProtocols
 }
diff --git a/config/provider_config.go b/config/provider_config.go
index 00faa1d0ab1b65a7a39d7d3548e5b89b0f250aba..a36fd4d0a07c3203e53582cbf2f3442d880a3981 100644
--- a/config/provider_config.go
+++ b/config/provider_config.go
@@ -14,18 +14,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package config
 
-import (
-	"context"
-	"io/ioutil"
-	"path"
-)
+package config
 
 import (
 	"github.com/creasty/defaults"
 	perrors "github.com/pkg/errors"
-	"gopkg.in/yaml.v2"
 )
 
 import (
@@ -37,6 +31,7 @@ import (
 // providerConfig
 /////////////////////////
 
+// ProviderConfig ...
 type ProviderConfig struct {
 	BaseConfig   `yaml:",inline"`
 	Filter       string `yaml:"filter" json:"filter,omitempty" property:"filter"`
@@ -49,8 +44,10 @@ type ProviderConfig struct {
 	Protocols         map[string]*ProtocolConfig `yaml:"protocols" json:"protocols,omitempty" property:"protocols"`
 	ProtocolConf      interface{}                `yaml:"protocol_conf" json:"protocol_conf,omitempty" property:"protocol_conf" `
 	FilterConf        interface{}                `yaml:"filter_conf" json:"filter_conf,omitempty" property:"filter_conf" `
+	ShutdownConfig    *ShutdownConfig            `yaml:"shutdown_conf" json:"shutdown_conf,omitempty" property:"shutdown_conf" `
 }
 
+// UnmarshalYAML ...
 func (c *ProviderConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
 	if err := defaults.Set(c); err != nil {
 		return err
@@ -62,36 +59,24 @@ func (c *ProviderConfig) UnmarshalYAML(unmarshal func(interface{}) error) error
 	return nil
 }
 
+// Prefix ...
 func (*ProviderConfig) Prefix() string {
 	return constant.ProviderConfigPrefix
 }
 
+// SetProviderConfig ...
 func SetProviderConfig(p ProviderConfig) {
 	providerConfig = &p
 }
-func GetProviderConfig() ProviderConfig {
-	if providerConfig == nil {
-		logger.Warnf("providerConfig is nil!")
-		return ProviderConfig{}
-	}
-	return *providerConfig
-}
 
+// ProviderInit ...
 func ProviderInit(confProFile string) error {
 	if len(confProFile) == 0 {
 		return perrors.Errorf("application configure(provider) file name is nil")
 	}
 
-	if path.Ext(confProFile) != ".yml" {
-		return perrors.Errorf("application configure file name{%v} suffix must be .yml", confProFile)
-	}
-
-	confFileStream, err := ioutil.ReadFile(confProFile)
-	if err != nil {
-		return perrors.Errorf("ioutil.ReadFile(file:%s) = error:%v", confProFile, perrors.WithStack(err))
-	}
 	providerConfig = &ProviderConfig{}
-	err = yaml.Unmarshal(confFileStream, providerConfig)
+	err := unmarshalYMLConfig(confProFile, providerConfig)
 	if err != nil {
 		return perrors.Errorf("yaml.Unmarshal() = error:%v", perrors.WithStack(err))
 	}
@@ -106,6 +91,7 @@ func ProviderInit(confProFile string) error {
 	}
 
 	logger.Debugf("provider config{%#v}\n", providerConfig)
+
 	return nil
 }
 
@@ -113,7 +99,7 @@ func configCenterRefreshProvider() error {
 	//fresh it
 	if providerConfig.ConfigCenterConfig != nil {
 		providerConfig.fatherConfig = providerConfig
-		if err := providerConfig.startConfigCenter(context.Background()); err != nil {
+		if err := providerConfig.startConfigCenter(); err != nil {
 			return perrors.Errorf("start config center error , error message is {%v}", perrors.WithStack(err))
 		}
 		providerConfig.fresh()
diff --git a/config/reference_config.go b/config/reference_config.go
index c63ac2ef28ff85d07b76ad0f5fef669d83bca3a5..7ce0013194f5c1a1d09e014a858433833aa07f0e 100644
--- a/config/reference_config.go
+++ b/config/reference_config.go
@@ -39,93 +39,103 @@ import (
 	"github.com/apache/dubbo-go/protocol"
 )
 
+// ReferenceConfig ...
 type ReferenceConfig struct {
-	context       context.Context
-	pxy           *proxy.Proxy
-	id            string
-	InterfaceName string            `required:"true"  yaml:"interface"  json:"interface,omitempty" property:"interface"`
-	Check         *bool             `yaml:"check"  json:"check,omitempty" property:"check"`
-	Url           string            `yaml:"url"  json:"url,omitempty" property:"url"`
-	Filter        string            `yaml:"filter" json:"filter,omitempty" property:"filter"`
-	Protocol      string            `default:"dubbo"  yaml:"protocol"  json:"protocol,omitempty" property:"protocol"`
-	Registry      string            `yaml:"registry"  json:"registry,omitempty"  property:"registry"`
-	Cluster       string            `yaml:"cluster"  json:"cluster,omitempty" property:"cluster"`
-	Loadbalance   string            `yaml:"loadbalance"  json:"loadbalance,omitempty" property:"loadbalance"`
-	Retries       string            `yaml:"retries"  json:"retries,omitempty" property:"retries"`
-	Group         string            `yaml:"group"  json:"group,omitempty" property:"group"`
-	Version       string            `yaml:"version"  json:"version,omitempty" property:"version"`
-	Methods       []*MethodConfig   `yaml:"methods"  json:"methods,omitempty" property:"methods"`
-	async         bool              `yaml:"async"  json:"async,omitempty" property:"async"`
-	Params        map[string]string `yaml:"params"  json:"params,omitempty" property:"params"`
-	invoker       protocol.Invoker
-	urls          []*common.URL
-	Generic       bool `yaml:"generic"  json:"generic,omitempty" property:"generic"`
+	context        context.Context
+	pxy            *proxy.Proxy
+	id             string
+	InterfaceName  string            `required:"true"  yaml:"interface"  json:"interface,omitempty" property:"interface"`
+	Check          *bool             `yaml:"check"  json:"check,omitempty" property:"check"`
+	Url            string            `yaml:"url"  json:"url,omitempty" property:"url"`
+	Filter         string            `yaml:"filter" json:"filter,omitempty" property:"filter"`
+	Protocol       string            `default:"dubbo"  yaml:"protocol"  json:"protocol,omitempty" property:"protocol"`
+	Registry       string            `yaml:"registry"  json:"registry,omitempty"  property:"registry"`
+	Cluster        string            `yaml:"cluster"  json:"cluster,omitempty" property:"cluster"`
+	Loadbalance    string            `yaml:"loadbalance"  json:"loadbalance,omitempty" property:"loadbalance"`
+	Retries        string            `yaml:"retries"  json:"retries,omitempty" property:"retries"`
+	Group          string            `yaml:"group"  json:"group,omitempty" property:"group"`
+	Version        string            `yaml:"version"  json:"version,omitempty" property:"version"`
+	Methods        []*MethodConfig   `yaml:"methods"  json:"methods,omitempty" property:"methods"`
+	Async          bool              `yaml:"async"  json:"async,omitempty" property:"async"`
+	Params         map[string]string `yaml:"params"  json:"params,omitempty" property:"params"`
+	invoker        protocol.Invoker
+	urls           []*common.URL
+	Generic        bool   `yaml:"generic"  json:"generic,omitempty" property:"generic"`
+	Sticky         bool   `yaml:"sticky"   json:"sticky,omitempty" property:"sticky"`
+	RequestTimeout string `yaml:"timeout"  json:"timeout,omitempty" property:"timeout"`
 }
 
+// Prefix ...
 func (c *ReferenceConfig) Prefix() string {
 	return constant.ReferenceConfigPrefix + c.InterfaceName + "."
 }
 
-// The only way to get a new ReferenceConfig
+// NewReferenceConfig The only way to get a new ReferenceConfig
 func NewReferenceConfig(id string, ctx context.Context) *ReferenceConfig {
 	return &ReferenceConfig{id: id, context: ctx}
 }
 
-func (refconfig *ReferenceConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
-
+// UnmarshalYAML ...
+func (c *ReferenceConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
 	type rf ReferenceConfig
 	raw := rf{} // Put your defaults here
 	if err := unmarshal(&raw); err != nil {
 		return err
 	}
 
-	*refconfig = ReferenceConfig(raw)
-	if err := defaults.Set(refconfig); err != nil {
+	*c = ReferenceConfig(raw)
+	if err := defaults.Set(c); err != nil {
 		return err
 	}
 
 	return nil
 }
 
-func (refconfig *ReferenceConfig) Refer() {
-	url := common.NewURLWithOptions(common.WithPath(refconfig.id), common.WithProtocol(refconfig.Protocol), common.WithParams(refconfig.getUrlMap()))
-
-	//1. user specified URL, could be peer-to-peer address, or register center's address.
-	if refconfig.Url != "" {
-		urlStrings := gxstrings.RegSplit(refconfig.Url, "\\s*[;]+\\s*")
+// Refer ...
+func (c *ReferenceConfig) Refer(_ interface{}) {
+	cfgURL := common.NewURLWithOptions(
+		common.WithPath(c.id),
+		common.WithProtocol(c.Protocol),
+		common.WithParams(c.getUrlMap()),
+		common.WithParamsValue(constant.BEAN_NAME_KEY, c.id),
+	)
+
+	if c.Url != "" {
+		// 1. user specified URL, could be peer-to-peer address, or register center's address.
+		urlStrings := gxstrings.RegSplit(c.Url, "\\s*[;]+\\s*")
 		for _, urlStr := range urlStrings {
-			serviceUrl, err := common.NewURL(context.Background(), urlStr)
+			serviceUrl, err := common.NewURL(urlStr)
 			if err != nil {
 				panic(fmt.Sprintf("user specified URL %v refer error, error message is %v ", urlStr, err.Error()))
 			}
 			if serviceUrl.Protocol == constant.REGISTRY_PROTOCOL {
-				serviceUrl.SubURL = url
-				refconfig.urls = append(refconfig.urls, &serviceUrl)
+				serviceUrl.SubURL = cfgURL
+				c.urls = append(c.urls, &serviceUrl)
 			} else {
 				if serviceUrl.Path == "" {
-					serviceUrl.Path = "/" + refconfig.id
+					serviceUrl.Path = "/" + c.id
 				}
 				// merge url need to do
-				newUrl := common.MergeUrl(&serviceUrl, url)
-				refconfig.urls = append(refconfig.urls, newUrl)
+				newUrl := common.MergeUrl(&serviceUrl, cfgURL)
+				c.urls = append(c.urls, newUrl)
 			}
-
 		}
 	} else {
-		//2. assemble SubURL from register center's configuration妯″紡
-		refconfig.urls = loadRegistries(refconfig.Registry, consumerConfig.Registries, common.CONSUMER)
+		// 2. assemble SubURL from register center's configuration mode
+		c.urls = loadRegistries(c.Registry, consumerConfig.Registries, common.CONSUMER)
 
-		//set url to regUrls
-		for _, regUrl := range refconfig.urls {
-			regUrl.SubURL = url
+		// set url to regUrls
+		for _, regUrl := range c.urls {
+			regUrl.SubURL = cfgURL
 		}
 	}
-	if len(refconfig.urls) == 1 {
-		refconfig.invoker = extension.GetProtocol(refconfig.urls[0].Protocol).Refer(*refconfig.urls[0])
+
+	if len(c.urls) == 1 {
+		c.invoker = extension.GetProtocol(c.urls[0].Protocol).Refer(*c.urls[0])
 	} else {
-		invokers := []protocol.Invoker{}
+		invokers := make([]protocol.Invoker, 0, len(c.urls))
 		var regUrl *common.URL
-		for _, u := range refconfig.urls {
+		for _, u := range c.urls {
 			invokers = append(invokers, extension.GetProtocol(u.Protocol).Refer(*u))
 			if u.Protocol == constant.REGISTRY_PROTOCOL {
 				regUrl = u
@@ -133,43 +143,54 @@ func (refconfig *ReferenceConfig) Refer() {
 		}
 		if regUrl != nil {
 			cluster := extension.GetCluster("registryAware")
-			refconfig.invoker = cluster.Join(directory.NewStaticDirectory(invokers))
+			c.invoker = cluster.Join(directory.NewStaticDirectory(invokers))
 		} else {
-			cluster := extension.GetCluster(refconfig.Cluster)
-			refconfig.invoker = cluster.Join(directory.NewStaticDirectory(invokers))
+			cluster := extension.GetCluster(c.Cluster)
+			c.invoker = cluster.Join(directory.NewStaticDirectory(invokers))
 		}
 	}
 
-	//create proxy
-	refconfig.pxy = extension.GetProxyFactory(consumerConfig.ProxyFactory).GetProxy(refconfig.invoker, url)
+	// create proxy
+	if c.Async {
+		callback := GetCallback(c.id)
+		c.pxy = extension.GetProxyFactory(consumerConfig.ProxyFactory).GetAsyncProxy(c.invoker, callback, cfgURL)
+	} else {
+		c.pxy = extension.GetProxyFactory(consumerConfig.ProxyFactory).GetProxy(c.invoker, cfgURL)
+	}
 }
 
+// Implement
 // @v is service provider implemented RPCService
-func (refconfig *ReferenceConfig) Implement(v common.RPCService) {
-	refconfig.pxy.Implement(v)
+func (c *ReferenceConfig) Implement(v common.RPCService) {
+	c.pxy.Implement(v)
 }
 
-func (refconfig *ReferenceConfig) GetRPCService() common.RPCService {
-	return refconfig.pxy.Get()
+// GetRPCService ...
+func (c *ReferenceConfig) GetRPCService() common.RPCService {
+	return c.pxy.Get()
 }
 
-func (refconfig *ReferenceConfig) getUrlMap() url.Values {
+func (c *ReferenceConfig) getUrlMap() url.Values {
 	urlMap := url.Values{}
 	//first set user params
-	for k, v := range refconfig.Params {
+	for k, v := range c.Params {
 		urlMap.Set(k, v)
 	}
-	urlMap.Set(constant.INTERFACE_KEY, refconfig.InterfaceName)
+	urlMap.Set(constant.INTERFACE_KEY, c.InterfaceName)
 	urlMap.Set(constant.TIMESTAMP_KEY, strconv.FormatInt(time.Now().Unix(), 10))
-	urlMap.Set(constant.CLUSTER_KEY, refconfig.Cluster)
-	urlMap.Set(constant.LOADBALANCE_KEY, refconfig.Loadbalance)
-	urlMap.Set(constant.RETRIES_KEY, refconfig.Retries)
-	urlMap.Set(constant.GROUP_KEY, refconfig.Group)
-	urlMap.Set(constant.VERSION_KEY, refconfig.Version)
-	urlMap.Set(constant.GENERIC_KEY, strconv.FormatBool(refconfig.Generic))
+	urlMap.Set(constant.CLUSTER_KEY, c.Cluster)
+	urlMap.Set(constant.LOADBALANCE_KEY, c.Loadbalance)
+	urlMap.Set(constant.RETRIES_KEY, c.Retries)
+	urlMap.Set(constant.GROUP_KEY, c.Group)
+	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))
+	if len(c.RequestTimeout) != 0 {
+		urlMap.Set(constant.TIMEOUT_KEY, c.RequestTimeout)
+	}
 	//getty invoke async or sync
-	urlMap.Set(constant.ASYNC_KEY, strconv.FormatBool(refconfig.async))
+	urlMap.Set(constant.ASYNC_KEY, strconv.FormatBool(c.Async))
+	urlMap.Set(constant.STICKY_KEY, strconv.FormatBool(c.Sticky))
 
 	//application info
 	urlMap.Set(constant.APPLICATION_KEY, consumerConfig.ApplicationConfig.Name)
@@ -182,24 +203,29 @@ func (refconfig *ReferenceConfig) getUrlMap() url.Values {
 
 	//filter
 	var defaultReferenceFilter = constant.DEFAULT_REFERENCE_FILTERS
-	if refconfig.Generic {
-		defaultReferenceFilter = constant.GENERIC_REFERENCE_FILTERS + defaultReferenceFilter
+	if c.Generic {
+		defaultReferenceFilter = constant.GENERIC_REFERENCE_FILTERS + "," + defaultReferenceFilter
 	}
-	urlMap.Set(constant.REFERENCE_FILTER_KEY, mergeValue(consumerConfig.Filter, refconfig.Filter, defaultReferenceFilter))
+	urlMap.Set(constant.REFERENCE_FILTER_KEY, mergeValue(consumerConfig.Filter, c.Filter, defaultReferenceFilter))
 
-	for _, v := range refconfig.Methods {
+	for _, v := range c.Methods {
 		urlMap.Set("methods."+v.Name+"."+constant.LOADBALANCE_KEY, v.Loadbalance)
 		urlMap.Set("methods."+v.Name+"."+constant.RETRIES_KEY, v.Retries)
+		urlMap.Set("methods."+v.Name+"."+constant.STICKY_KEY, strconv.FormatBool(v.Sticky))
+		if len(v.RequestTimeout) != 0 {
+			urlMap.Set("methods."+v.Name+"."+constant.TIMEOUT_KEY, v.RequestTimeout)
+		}
 	}
 
 	return urlMap
-
 }
-func (refconfig *ReferenceConfig) GenericLoad(id string) {
-	genericService := NewGenericService(refconfig.id)
+
+// GenericLoad ...
+func (c *ReferenceConfig) GenericLoad(id string) {
+	genericService := NewGenericService(c.id)
 	SetConsumerService(genericService)
-	refconfig.id = id
-	refconfig.Refer()
-	refconfig.Implement(genericService)
+	c.id = id
+	c.Refer(genericService)
+	c.Implement(genericService)
 	return
 }
diff --git a/config/reference_config_test.go b/config/reference_config_test.go
index a81dbf06cef7d275cf6af4a7f651ff8d1600a3c9..7a65e55f09c997cb49b83f1f185faf9338cf0f5a 100644
--- a/config/reference_config_test.go
+++ b/config/reference_config_test.go
@@ -81,10 +81,12 @@ func doInitConsumer() {
 		},
 		References: map[string]*ReferenceConfig{
 			"MockService": {
+				id: "MockProvider",
 				Params: map[string]string{
 					"serviceid": "soa.mock",
 					"forks":     "5",
 				},
+				Sticky:        false,
 				Registry:      "shanghai_reg1,shanghai_reg2,hangzhou_reg1,hangzhou_reg2",
 				InterfaceName: "com.MockService",
 				Protocol:      "mock",
@@ -103,6 +105,7 @@ func doInitConsumer() {
 						Name:        "GetUser1",
 						Retries:     "2",
 						Loadbalance: "random",
+						Sticky:      true,
 					},
 				},
 			},
@@ -110,6 +113,26 @@ func doInitConsumer() {
 	}
 }
 
+var mockProvider = new(MockProvider)
+
+type MockProvider struct {
+}
+
+func (m *MockProvider) Reference() string {
+	return "MockProvider"
+}
+
+func (m *MockProvider) CallBack(res common.CallbackResponse) {
+}
+
+func doInitConsumerAsync() {
+	doInitConsumer()
+	SetConsumerService(mockProvider)
+	for _, v := range consumerConfig.References {
+		v.Async = true
+	}
+}
+
 func doInitConsumerWithSingleRegistry() {
 	consumerConfig = &ConsumerConfig{
 		ApplicationConfig: &ApplicationConfig{
@@ -161,7 +184,7 @@ func Test_ReferMultireg(t *testing.T) {
 	extension.SetCluster("registryAware", cluster_impl.NewRegistryAwareCluster)
 
 	for _, reference := range consumerConfig.References {
-		reference.Refer()
+		reference.Refer(nil)
 		assert.NotNil(t, reference.invoker)
 		assert.NotNil(t, reference.pxy)
 	}
@@ -174,13 +197,29 @@ func Test_Refer(t *testing.T) {
 	extension.SetCluster("registryAware", cluster_impl.NewRegistryAwareCluster)
 
 	for _, reference := range consumerConfig.References {
-		reference.Refer()
+		reference.Refer(nil)
+		assert.Equal(t, "soa.mock", reference.Params["serviceid"])
+		assert.NotNil(t, reference.invoker)
+		assert.NotNil(t, reference.pxy)
+	}
+	consumerConfig = nil
+}
+
+func Test_ReferAsync(t *testing.T) {
+	doInitConsumerAsync()
+	extension.SetProtocol("registry", GetProtocol)
+	extension.SetCluster("registryAware", cluster_impl.NewRegistryAwareCluster)
+
+	for _, reference := range consumerConfig.References {
+		reference.Refer(nil)
 		assert.Equal(t, "soa.mock", reference.Params["serviceid"])
 		assert.NotNil(t, reference.invoker)
 		assert.NotNil(t, reference.pxy)
+		assert.NotNil(t, reference.pxy.GetCallback())
 	}
 	consumerConfig = nil
 }
+
 func Test_ReferP2P(t *testing.T) {
 	doInitConsumer()
 	extension.SetProtocol("dubbo", GetProtocol)
@@ -188,7 +227,7 @@ func Test_ReferP2P(t *testing.T) {
 	m.Url = "dubbo://127.0.0.1:20000"
 
 	for _, reference := range consumerConfig.References {
-		reference.Refer()
+		reference.Refer(nil)
 		assert.NotNil(t, reference.invoker)
 		assert.NotNil(t, reference.pxy)
 	}
@@ -202,7 +241,7 @@ func Test_ReferMultiP2P(t *testing.T) {
 	m.Url = "dubbo://127.0.0.1:20000;dubbo://127.0.0.2:20000"
 
 	for _, reference := range consumerConfig.References {
-		reference.Refer()
+		reference.Refer(nil)
 		assert.NotNil(t, reference.invoker)
 		assert.NotNil(t, reference.pxy)
 	}
@@ -217,7 +256,7 @@ func Test_ReferMultiP2PWithReg(t *testing.T) {
 	m.Url = "dubbo://127.0.0.1:20000;registry://127.0.0.2:20000"
 
 	for _, reference := range consumerConfig.References {
-		reference.Refer()
+		reference.Refer(nil)
 		assert.NotNil(t, reference.invoker)
 		assert.NotNil(t, reference.pxy)
 	}
@@ -229,7 +268,7 @@ func Test_Implement(t *testing.T) {
 	extension.SetProtocol("registry", GetProtocol)
 	extension.SetCluster("registryAware", cluster_impl.NewRegistryAwareCluster)
 	for _, reference := range consumerConfig.References {
-		reference.Refer()
+		reference.Refer(nil)
 		reference.Implement(&MockService{})
 		assert.NotNil(t, reference.GetRPCService())
 
@@ -245,7 +284,7 @@ func Test_Forking(t *testing.T) {
 	m.Url = "dubbo://127.0.0.1:20000;registry://127.0.0.2:20000"
 
 	for _, reference := range consumerConfig.References {
-		reference.Refer()
+		reference.Refer(nil)
 		forks := int(reference.invoker.GetUrl().GetParamInt(constant.FORKS_KEY, constant.DEFAULT_FORKS))
 		assert.Equal(t, 5, forks)
 		assert.NotNil(t, reference.pxy)
@@ -254,6 +293,24 @@ func Test_Forking(t *testing.T) {
 	consumerConfig = nil
 }
 
+func Test_Sticky(t *testing.T) {
+	doInitConsumer()
+	extension.SetProtocol("dubbo", GetProtocol)
+	extension.SetProtocol("registry", GetProtocol)
+	m := consumerConfig.References["MockService"]
+	m.Url = "dubbo://127.0.0.1:20000;registry://127.0.0.2:20000"
+
+	reference := consumerConfig.References["MockService"]
+	reference.Refer(nil)
+	referenceSticky := reference.invoker.GetUrl().GetParam(constant.STICKY_KEY, "false")
+	assert.Equal(t, "false", referenceSticky)
+
+	method0StickKey := reference.invoker.GetUrl().GetMethodParam(reference.Methods[0].Name, constant.STICKY_KEY, "false")
+	assert.Equal(t, "false", method0StickKey)
+	method1StickKey := reference.invoker.GetUrl().GetMethodParam(reference.Methods[1].Name, constant.STICKY_KEY, "false")
+	assert.Equal(t, "true", method1StickKey)
+}
+
 func GetProtocol() protocol.Protocol {
 	if regProtocol != nil {
 		return regProtocol
diff --git a/config/registry_config.go b/config/registry_config.go
index 9ffa41eb5b5b3b5ae4dc9f77812c0aef5ce9835f..4e4b6e97d79a9402616b6cac954f7a09b2973dcc 100644
--- a/config/registry_config.go
+++ b/config/registry_config.go
@@ -18,7 +18,6 @@
 package config
 
 import (
-	"context"
 	"net/url"
 	"strconv"
 	"strings"
@@ -34,6 +33,7 @@ import (
 	"github.com/apache/dubbo-go/common/logger"
 )
 
+// 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
@@ -46,6 +46,7 @@ type RegistryConfig struct {
 	Params   map[string]string `yaml:"params" json:"params,omitempty" property:"params"`
 }
 
+// UnmarshalYAML ...
 func (c *RegistryConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
 	if err := defaults.Set(c); err != nil {
 		return err
@@ -57,6 +58,7 @@ func (c *RegistryConfig) UnmarshalYAML(unmarshal func(interface{}) error) error
 	return nil
 }
 
+// Prefix ...
 func (*RegistryConfig) Prefix() string {
 	return constant.RegistryConfigPrefix + "|" + constant.SingleRegistryConfigPrefix
 }
@@ -92,9 +94,7 @@ func loadRegistries(targetRegistries string, registries map[string]*RegistryConf
 			addresses := strings.Split(registryConf.Address, ",")
 			address := addresses[0]
 			address = traslateRegistryConf(address, registryConf)
-			url, err = common.NewURL(
-				context.Background(),
-				constant.REGISTRY_PROTOCOL+"://"+address,
+			url, err = common.NewURL(constant.REGISTRY_PROTOCOL+"://"+address,
 				common.WithParams(registryConf.getUrlMap(roleType)),
 				common.WithUsername(registryConf.Username),
 				common.WithPassword(registryConf.Password),
@@ -114,15 +114,16 @@ func loadRegistries(targetRegistries string, registries map[string]*RegistryConf
 	return urls
 }
 
-func (regconfig *RegistryConfig) getUrlMap(roleType common.RoleType) url.Values {
+func (c *RegistryConfig) getUrlMap(roleType common.RoleType) url.Values {
 	urlMap := url.Values{}
-	urlMap.Set(constant.GROUP_KEY, regconfig.Group)
+	urlMap.Set(constant.GROUP_KEY, c.Group)
 	urlMap.Set(constant.ROLE_KEY, strconv.Itoa(int(roleType)))
-	urlMap.Set(constant.REGISTRY_KEY, regconfig.Protocol)
-	urlMap.Set(constant.REGISTRY_TIMEOUT_KEY, regconfig.TimeoutStr)
-	for k, v := range regconfig.Params {
+	urlMap.Set(constant.REGISTRY_KEY, c.Protocol)
+	urlMap.Set(constant.REGISTRY_TIMEOUT_KEY, c.TimeoutStr)
+	for k, v := range c.Params {
 		urlMap.Set(k, v)
 	}
+
 	return urlMap
 }
 
diff --git a/config/registry_config_test.go b/config/registry_config_test.go
index 45d38b29cc7089dabc5d7b7e34390ee48a58dc97..6c2fed605d6c50b483f7ad2900e5a483b3986e1b 100644
--- a/config/registry_config_test.go
+++ b/config/registry_config_test.go
@@ -46,6 +46,7 @@ func Test_loadRegistries(t *testing.T) {
 	fmt.Println(urls[0])
 	assert.Equal(t, "127.0.0.2:2181,128.0.0.1:2181", urls[0].Location)
 }
+
 func Test_loadRegistries1(t *testing.T) {
 	target := "shanghai1"
 	regs := map[string]*RegistryConfig{
diff --git a/config/service.go b/config/service.go
index 2bceac4a8c20bb598dc2607c90c8206e4a448808..b7e7dc2a425b42363d570fc37a70e2e5094e7d9d 100644
--- a/config/service.go
+++ b/config/service.go
@@ -26,20 +26,31 @@ var (
 	proServices = map[string]common.RPCService{} // service name -> service
 )
 
-// SetConService is called by init() of implement of RPCService
+// SetConsumerService is called by init() of implement of RPCService
 func SetConsumerService(service common.RPCService) {
 	conServices[service.Reference()] = service
 }
 
-// SetProService is called by init() of implement of RPCService
+// SetProviderService is called by init() of implement of RPCService
 func SetProviderService(service common.RPCService) {
 	proServices[service.Reference()] = service
 }
 
+// GetConsumerService ...
 func GetConsumerService(name string) common.RPCService {
 	return conServices[name]
 }
 
+// GetProviderService ...
 func GetProviderService(name string) common.RPCService {
 	return proServices[name]
 }
+
+// GetCallback ...
+func GetCallback(name string) func(response common.CallbackResponse) {
+	service := GetConsumerService(name)
+	if sv, ok := service.(common.AsyncCallbackService); ok {
+		return sv.CallBack
+	}
+	return nil
+}
diff --git a/config/service_config.go b/config/service_config.go
index c17846322e20120bfdf00f1afe24bd20efe7510b..7d97fa4d1e95bd79e051f77deaeafa1afcc58b0f 100644
--- a/config/service_config.go
+++ b/config/service_config.go
@@ -42,6 +42,7 @@ import (
 	"github.com/apache/dubbo-go/protocol/protocolwrapper"
 )
 
+// ServiceConfig ...
 type ServiceConfig struct {
 	context                     context.Context
 	id                          string
@@ -66,6 +67,8 @@ type ServiceConfig struct {
 	TpsLimitRejectedHandler     string            `yaml:"tps.limit.rejected.handler" json:"tps.limit.rejected.handler,omitempty" property:"tps.limit.rejected.handler"`
 	ExecuteLimit                string            `yaml:"execute.limit" json:"execute.limit,omitempty" property:"execute.limit"`
 	ExecuteLimitRejectedHandler string            `yaml:"execute.limit.rejected.handler" json:"execute.limit.rejected.handler,omitempty" property:"execute.limit.rejected.handler"`
+	Auth                        string            `yaml:"auth" json:"auth,omitempty" property:"auth"`
+	ParamSign                   string            `yaml:"param.sign" json:"param.sign,omitempty" property:"param.sign"`
 
 	unexported    *atomic.Bool
 	exported      *atomic.Bool
@@ -74,10 +77,12 @@ type ServiceConfig struct {
 	cacheMutex    sync.Mutex
 }
 
+// Prefix ...
 func (c *ServiceConfig) Prefix() string {
 	return constant.ServiceConfigPrefix + c.InterfaceName + "."
 }
 
+// UnmarshalYAML ...
 func (c *ServiceConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
 	if err := defaults.Set(c); err != nil {
 		return err
@@ -89,101 +94,104 @@ func (c *ServiceConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
 	return nil
 }
 
-// The only way to get a new ServiceConfig
+// NewServiceConfig The only way to get a new ServiceConfig
 func NewServiceConfig(id string, context context.Context) *ServiceConfig {
-
 	return &ServiceConfig{
 		context:    context,
 		id:         id,
 		unexported: atomic.NewBool(false),
 		exported:   atomic.NewBool(false),
 	}
-
 }
 
-func (srvconfig *ServiceConfig) Export() error {
+// Export ...
+func (c *ServiceConfig) Export() error {
 	// TODO: config center start here
 
 	// TODO:delay export
-	if srvconfig.unexported != nil && srvconfig.unexported.Load() {
-		err := perrors.Errorf("The service %v has already unexported! ", srvconfig.InterfaceName)
+	if c.unexported != nil && c.unexported.Load() {
+		err := perrors.Errorf("The service %v has already unexported! ", c.InterfaceName)
 		logger.Errorf(err.Error())
 		return err
 	}
-	if srvconfig.unexported != nil && srvconfig.exported.Load() {
-		logger.Warnf("The service %v has already exported! ", srvconfig.InterfaceName)
+	if c.unexported != nil && c.exported.Load() {
+		logger.Warnf("The service %v has already exported! ", c.InterfaceName)
 		return nil
 	}
 
-	regUrls := loadRegistries(srvconfig.Registry, providerConfig.Registries, common.PROVIDER)
-	urlMap := srvconfig.getUrlMap()
-
-	for _, proto := range loadProtocol(srvconfig.Protocol, providerConfig.Protocols) {
+	regUrls := loadRegistries(c.Registry, providerConfig.Registries, common.PROVIDER)
+	urlMap := c.getUrlMap()
+	protocolConfigs := loadProtocol(c.Protocol, providerConfig.Protocols)
+	if len(protocolConfigs) == 0 {
+		logger.Warnf("The service %v's '%v' protocols don't has right protocolConfigs ", c.InterfaceName, c.Protocol)
+		return nil
+	}
+	for _, proto := range protocolConfigs {
 		// registry the service reflect
-		methods, err := common.ServiceMap.Register(proto.Name, srvconfig.rpcService)
+		methods, err := common.ServiceMap.Register(proto.Name, c.rpcService)
 		if err != nil {
-			err := perrors.Errorf("The service %v  export the protocol %v error! Error message is %v .", srvconfig.InterfaceName, proto.Name, err.Error())
+			err := perrors.Errorf("The service %v  export the protocol %v error! Error message is %v .", c.InterfaceName, proto.Name, err.Error())
 			logger.Errorf(err.Error())
 			return err
 		}
-		url := common.NewURLWithOptions(common.WithPath(srvconfig.id),
+		ivkURL := common.NewURLWithOptions(
+			common.WithPath(c.id),
 			common.WithProtocol(proto.Name),
 			common.WithIp(proto.Ip),
 			common.WithPort(proto.Port),
 			common.WithParams(urlMap),
-			common.WithParamsValue(constant.BEAN_NAME_KEY, srvconfig.id),
+			common.WithParamsValue(constant.BEAN_NAME_KEY, c.id),
 			common.WithMethods(strings.Split(methods, ",")),
-			common.WithToken(srvconfig.Token),
+			common.WithToken(c.Token),
 		)
 
 		if len(regUrls) > 0 {
 			for _, regUrl := range regUrls {
-				regUrl.SubURL = url
+				regUrl.SubURL = ivkURL
 
-				srvconfig.cacheMutex.Lock()
-				if srvconfig.cacheProtocol == nil {
-					logger.Infof(fmt.Sprintf("First load the registry protocol , url is {%v}!", url))
-					srvconfig.cacheProtocol = extension.GetProtocol("registry")
+				c.cacheMutex.Lock()
+				if c.cacheProtocol == nil {
+					logger.Infof(fmt.Sprintf("First load the registry protocol , url is {%v}!", ivkURL))
+					c.cacheProtocol = extension.GetProtocol("registry")
 				}
-				srvconfig.cacheMutex.Unlock()
+				c.cacheMutex.Unlock()
 
 				invoker := extension.GetProxyFactory(providerConfig.ProxyFactory).GetInvoker(*regUrl)
-				exporter := srvconfig.cacheProtocol.Export(invoker)
+				exporter := c.cacheProtocol.Export(invoker)
 				if exporter == nil {
-					panic(perrors.New(fmt.Sprintf("Registry protocol new exporter error,registry is {%v},url is {%v}", regUrl, url)))
+					panic(perrors.New(fmt.Sprintf("Registry protocol new exporter error,registry is {%v},url is {%v}", regUrl, ivkURL)))
 				}
 			}
 		} else {
-			invoker := extension.GetProxyFactory(providerConfig.ProxyFactory).GetInvoker(*url)
+			invoker := extension.GetProxyFactory(providerConfig.ProxyFactory).GetInvoker(*ivkURL)
 			exporter := extension.GetProtocol(protocolwrapper.FILTER).Export(invoker)
 			if exporter == nil {
-				panic(perrors.New(fmt.Sprintf("Filter protocol without registry new exporter error,url is {%v}", url)))
+				panic(perrors.New(fmt.Sprintf("Filter protocol without registry new exporter error,url is {%v}", ivkURL)))
 			}
 		}
-
 	}
 	return nil
-
 }
 
-func (srvconfig *ServiceConfig) Implement(s common.RPCService) {
-	srvconfig.rpcService = s
+// Implement ...
+func (c *ServiceConfig) Implement(s common.RPCService) {
+	c.rpcService = s
 }
 
-func (srvconfig *ServiceConfig) getUrlMap() url.Values {
+func (c *ServiceConfig) getUrlMap() url.Values {
 	urlMap := url.Values{}
 	// first set user params
-	for k, v := range srvconfig.Params {
+	for k, v := range c.Params {
 		urlMap.Set(k, v)
 	}
-	urlMap.Set(constant.INTERFACE_KEY, srvconfig.InterfaceName)
+	urlMap.Set(constant.INTERFACE_KEY, c.InterfaceName)
 	urlMap.Set(constant.TIMESTAMP_KEY, strconv.FormatInt(time.Now().Unix(), 10))
-	urlMap.Set(constant.CLUSTER_KEY, srvconfig.Cluster)
-	urlMap.Set(constant.LOADBALANCE_KEY, srvconfig.Loadbalance)
-	urlMap.Set(constant.WARMUP_KEY, srvconfig.Warmup)
-	urlMap.Set(constant.RETRIES_KEY, srvconfig.Retries)
-	urlMap.Set(constant.GROUP_KEY, srvconfig.Group)
-	urlMap.Set(constant.VERSION_KEY, srvconfig.Version)
+	urlMap.Set(constant.CLUSTER_KEY, c.Cluster)
+	urlMap.Set(constant.LOADBALANCE_KEY, c.Loadbalance)
+	urlMap.Set(constant.WARMUP_KEY, c.Warmup)
+	urlMap.Set(constant.RETRIES_KEY, c.Retries)
+	urlMap.Set(constant.GROUP_KEY, c.Group)
+	urlMap.Set(constant.VERSION_KEY, c.Version)
 	urlMap.Set(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER))
 	// application info
 	urlMap.Set(constant.APPLICATION_KEY, providerConfig.ApplicationConfig.Name)
@@ -195,22 +203,26 @@ func (srvconfig *ServiceConfig) getUrlMap() url.Values {
 	urlMap.Set(constant.ENVIRONMENT_KEY, providerConfig.ApplicationConfig.Environment)
 
 	// filter
-	urlMap.Set(constant.SERVICE_FILTER_KEY, mergeValue(providerConfig.Filter, srvconfig.Filter, constant.DEFAULT_SERVICE_FILTERS))
+	urlMap.Set(constant.SERVICE_FILTER_KEY, mergeValue(providerConfig.Filter, c.Filter, constant.DEFAULT_SERVICE_FILTERS))
 
 	// filter special config
-	urlMap.Set(constant.ACCESS_LOG_KEY, srvconfig.AccessLog)
+	urlMap.Set(constant.ACCESS_LOG_KEY, c.AccessLog)
 	// tps limiter
-	urlMap.Set(constant.TPS_LIMIT_STRATEGY_KEY, srvconfig.TpsLimitStrategy)
-	urlMap.Set(constant.TPS_LIMIT_INTERVAL_KEY, srvconfig.TpsLimitInterval)
-	urlMap.Set(constant.TPS_LIMIT_RATE_KEY, srvconfig.TpsLimitRate)
-	urlMap.Set(constant.TPS_LIMITER_KEY, srvconfig.TpsLimiter)
-	urlMap.Set(constant.TPS_REJECTED_EXECUTION_HANDLER_KEY, srvconfig.TpsLimitRejectedHandler)
+	urlMap.Set(constant.TPS_LIMIT_STRATEGY_KEY, c.TpsLimitStrategy)
+	urlMap.Set(constant.TPS_LIMIT_INTERVAL_KEY, c.TpsLimitInterval)
+	urlMap.Set(constant.TPS_LIMIT_RATE_KEY, c.TpsLimitRate)
+	urlMap.Set(constant.TPS_LIMITER_KEY, c.TpsLimiter)
+	urlMap.Set(constant.TPS_REJECTED_EXECUTION_HANDLER_KEY, c.TpsLimitRejectedHandler)
 
 	// execute limit filter
-	urlMap.Set(constant.EXECUTE_LIMIT_KEY, srvconfig.ExecuteLimit)
-	urlMap.Set(constant.EXECUTE_REJECTED_EXECUTION_HANDLER_KEY, srvconfig.ExecuteLimitRejectedHandler)
+	urlMap.Set(constant.EXECUTE_LIMIT_KEY, c.ExecuteLimit)
+	urlMap.Set(constant.EXECUTE_REJECTED_EXECUTION_HANDLER_KEY, c.ExecuteLimitRejectedHandler)
 
-	for _, v := range srvconfig.Methods {
+	// auth filter
+	urlMap.Set(constant.SERVICE_AUTH_KEY, c.Auth)
+	urlMap.Set(constant.PARAMTER_SIGNATURE_ENABLE_KEY, c.ParamSign)
+
+	for _, v := range c.Methods {
 		prefix := "methods." + v.Name + "."
 		urlMap.Set(prefix+constant.LOADBALANCE_KEY, v.Loadbalance)
 		urlMap.Set(prefix+constant.RETRIES_KEY, v.Retries)
@@ -226,5 +238,4 @@ func (srvconfig *ServiceConfig) getUrlMap() url.Values {
 	}
 
 	return urlMap
-
 }
diff --git a/config/service_config_test.go b/config/service_config_test.go
index 8ae67533bd1cdd1f9170efd762de51d371d0ad38..6f3230890348e77ea26c9c0eaf9165090c8cd09f 100644
--- a/config/service_config_test.go
+++ b/config/service_config_test.go
@@ -93,6 +93,30 @@ func doInitProvider() {
 					},
 				},
 			},
+			"MockServiceNoRightProtocol": {
+				InterfaceName: "com.MockService",
+				Protocol:      "mock1",
+				Registry:      "shanghai_reg1,shanghai_reg2,hangzhou_reg1,hangzhou_reg2",
+				Cluster:       "failover",
+				Loadbalance:   "random",
+				Retries:       "3",
+				Group:         "huadong_idc",
+				Version:       "1.0.0",
+				Methods: []*MethodConfig{
+					{
+						Name:        "GetUser",
+						Retries:     "2",
+						Loadbalance: "random",
+						Weight:      200,
+					},
+					{
+						Name:        "GetUser1",
+						Retries:     "2",
+						Loadbalance: "random",
+						Weight:      200,
+					},
+				},
+			},
 		},
 		Protocols: map[string]*ProtocolConfig{
 			"mock": {
diff --git a/config/testdata/consumer_config.properties b/config/testdata/consumer_config.properties
new file mode 100644
index 0000000000000000000000000000000000000000..da9fe4f3b3f2ae47dafc8252e388678e7cd5d03b
--- /dev/null
+++ b/config/testdata/consumer_config.properties
@@ -0,0 +1,52 @@
+filter=
+request_timeout=100ms
+connect_timeout=100ms
+check=true
+application.organization=ikurento.com
+application.name=BDTService
+application.module=dubbogo user-info client
+application.version=0.0.1
+application.owner=ZX
+application.environment=dev
+registries.hangzhouzk.protocol=zookeeper
+registries.hangzhouzk.timeout=3s
+registries.hangzhouzk.address=127.0.0.1:2181
+registries.hangzhouzk.username=
+registries.hangzhouzk.password=
+registries.shanghaizk.protocol=zookeeper
+registries.shanghaizk.timeout=3s
+registries.shanghaizk.address=127.0.0.1:2182
+registries.shanghaizk.username=
+registries.shanghaizk.password=
+references.UserProvider.registry=hangzhouzk,shanghaizk
+references.UserProvider.filter=
+references.UserProvider.version=1.0
+references.UserProvider.group=as
+references.UserProvider.interface=com.ikurento.user.UserProvider
+references.UserProvider.url=dubbo://127.0.0.1:20000/UserProvider
+references.UserProvider.cluster=failover
+references.UserProvider.methods[0].name=GetUser
+references.UserProvider.methods[0].retries=3
+references.UserProvider.params.serviceid=soa.com.ikurento.user.UserProvider
+references.UserProvider.params.forks=5
+protocol_conf.dubbo.reconnect_interval=0
+protocol_conf.dubbo.connection_number=2
+protocol_conf.dubbo.heartbeat_period=5s
+protocol_conf.dubbo.session_timeout=20s
+protocol_conf.dubbo.pool_size=64
+protocol_conf.dubbo.pool_ttl=600
+protocol_conf.dubbo.gr_pool_size=1200
+protocol_conf.dubbo.queue_len=64
+protocol_conf.dubbo.queue_number=60
+protocol_conf.dubbo.getty_session_param.compress_encoding=false
+protocol_conf.dubbo.getty_session_param.tcp_no_delay=true
+protocol_conf.dubbo.getty_session_param.tcp_keep_alive=true
+protocol_conf.dubbo.getty_session_param.keep_alive_period=120s
+protocol_conf.dubbo.getty_session_param.tcp_r_buf_size=262144
+protocol_conf.dubbo.getty_session_param.tcp_w_buf_size=65536
+protocol_conf.dubbo.getty_session_param.pkg_wq_size=512
+protocol_conf.dubbo.getty_session_param.tcp_read_timeout=1s
+protocol_conf.dubbo.getty_session_param.tcp_write_timeout=5s
+protocol_conf.dubbo.getty_session_param.wait_timeout=1s
+protocol_conf.dubbo.getty_session_param.max_msg_len=1024
+protocol_conf.dubbo.getty_session_param.session_name=client
\ No newline at end of file
diff --git a/config/testdata/consumer_config.yml b/config/testdata/consumer_config.yml
index 9fd50bb4d35a40d8532c9a644a86ad6834f8e89b..2034186c0fa0ccf21c3f6fb9df0f5cfd69315113 100644
--- a/config/testdata/consumer_config.yml
+++ b/config/testdata/consumer_config.yml
@@ -41,21 +41,36 @@ references:
     interface : "com.ikurento.user.UserProvider"
     url: "dubbo://127.0.0.1:20000/UserProvider"
     cluster: "failover"
+    timeout: "3s"
     methods :
       - name: "GetUser"
         retries: "3"
+        timeout: "5s"
     params:
       "serviceid":
         "soa.com.ikurento.user.UserProvider"
       "forks": 5
 
+shutdown_conf:
+  timeout: 60s
+  step_timeout: 10s
+
 protocol_conf:
+  # when you choose the Dubbo protocol, the following configuration takes effect
   dubbo:
     reconnect_interval: 0
+    # reconnect_interval is the actual number of connections a session can use
     connection_number: 2
-    heartbeat_period: "5s"
-    session_timeout: "20s"
-    pool_size: 64
+    # heartbeat_period is heartbeat interval between server and client connection.
+    # Effective by client configuration
+    heartbeat_period: "30s"
+    # when the session is inactive for more than session_timeout, the session may be closed
+    session_timeout: "30s"
+    # a reference has the size of the session connection pool
+    # that is the maximum number of sessions it may have
+    pool_size: 4
+    # dubbo-go uses getty as the network connection library.
+    # The following is the relevant configuration of getty
     pool_ttl: 600
     # gr_pool_size is recommended to be set to [cpu core number] * 100
     gr_pool_size: 1200
@@ -63,6 +78,8 @@ protocol_conf:
     queue_len: 64
     # queue_number is recommended to be set to gr_pool_size / 20
     queue_number: 60
+    # dubbo-go uses getty as the network connection library.
+    # The following is the relevant configuration of getty
     getty_session_param:
       compress_encoding: false
       tcp_no_delay: true
@@ -74,5 +91,7 @@ protocol_conf:
       tcp_read_timeout: "1s"
       tcp_write_timeout: "5s"
       wait_timeout: "1s"
-      max_msg_len: 1024
+      # maximum len of data per request
+      # this refers to the total amount of data requested or returned
+      max_msg_len: 102400
       session_name: "client"
diff --git a/config/testdata/consumer_config_with_configcenter.yml b/config/testdata/consumer_config_with_configcenter.yml
index 0550cc89741b6a490aaba9ff8906d7dda1b3ed49..ebe56fa93f9f5728aa365ee5b7a99b6bb5857a8e 100644
--- a/config/testdata/consumer_config_with_configcenter.yml
+++ b/config/testdata/consumer_config_with_configcenter.yml
@@ -17,6 +17,10 @@ references:
       - name: "GetUser"
         retries: "3"
 
+shutdown_conf:
+  timeout: 60s
+  step_timeout: 10s
+
 protocol_conf:
   dubbo:
     reconnect_interval: 0
diff --git a/config/testdata/consumer_config_with_configcenter_apollo.yml b/config/testdata/consumer_config_with_configcenter_apollo.yml
new file mode 100644
index 0000000000000000000000000000000000000000..49b8fff59541766729433138574d909ae4b9566e
--- /dev/null
+++ b/config/testdata/consumer_config_with_configcenter_apollo.yml
@@ -0,0 +1,24 @@
+# use apollo config center for fetch config file
+# default config file namespace is dubbo.properties
+# consumer config file Ref:consumer_config.properties
+# provider config file Ref:provider_config.properties
+config_center:
+  protocol: apollo
+  address: 106.12.25.204:8080
+  group: testApplication_yang
+  cluster: dev
+  # 'namespace' can be used for router rule , default value is dubbo.properties
+  # but if you want to change router rule config file ,just open this item
+#  namespace: governance.properties
+  # 'config_file' is not necessary ,default : dubbo.properties
+  # but if you want to change config file ,just open this item
+#  config_file: mockDubbog.properties
+
+# application config required
+application:
+  organization: "ikurento.com"
+  name: "BDTService"
+  module: "dubbogo user-info server"
+  version: "0.0.1"
+  owner: "ZX"
+  environment: "dev"
\ No newline at end of file
diff --git a/config/testdata/consumer_config_withoutProtocol.yml b/config/testdata/consumer_config_withoutProtocol.yml
index 5e57c7ddf6e82152e4f207b2d06df1443766717c..32bad8b91db3fac9c026fca36c5dc3b84f4c3fc9 100644
--- a/config/testdata/consumer_config_withoutProtocol.yml
+++ b/config/testdata/consumer_config_withoutProtocol.yml
@@ -48,6 +48,10 @@ references:
         "soa.com.ikurento.user.UserProvider"
       "forks": 5
 
+shutdown_conf:
+  timeout: 60s
+  step_timeout: 10s
+
 protocol_conf:
   dubbo:
     reconnect_interval: 0
diff --git a/config/testdata/provider_config.properties b/config/testdata/provider_config.properties
new file mode 100644
index 0000000000000000000000000000000000000000..f7d70f5cd635cae0b14d79890c1bab976978d1c6
--- /dev/null
+++ b/config/testdata/provider_config.properties
@@ -0,0 +1,58 @@
+filter=
+application.organization=ikurento.com
+application.name=BDTService
+application.module=dubbogo user-info server
+application.version=0.0.1
+application.owner=ZX
+application.environment=dev
+registries.hangzhouzk.protocol=zookeeper
+registries.hangzhouzk.timeout=3s
+registries.hangzhouzk.address=127.0.0.1:2181
+registries.hangzhouzk.username=
+registries.hangzhouzk.password=
+registries.shanghaizk.protocol=zookeeper
+registries.shanghaizk.timeout=3s
+registries.shanghaizk.address=127.0.0.1:2182
+registries.shanghaizk.username=
+registries.shanghaizk.password=
+services.UserProvider.registry=hangzhouzk,shanghaizk
+services.UserProvider.filter=
+services.UserProvider.tps.limiter=default
+services.UserProvider.tps.limit.interval=60000
+services.UserProvider.tps.limit.rate=200
+services.UserProvider.tps.limit.strategy=slidingWindow
+services.UserProvider.tps.limit.rejected.handler=default
+services.UserProvider.execute.limit=200
+services.UserProvider.execute.limit.rejected.handler=default
+services.UserProvider.protocol=dubbo
+services.UserProvider.interface=com.ikurento.user.UserProvider
+services.UserProvider.loadbalance=random
+services.UserProvider.version=1.0
+services.UserProvider.group=as
+services.UserProvider.warmup=100
+services.UserProvider.cluster=failover
+services.UserProvider.methods[0].name=GetUser
+services.UserProvider.methods[0].retries=1
+services.UserProvider.methods[0].loadbalance=random
+services.UserProvider.methods[0].execute.limit=200
+services.UserProvider.methods[0].execute.limit.rejected.handler=default
+protocols.dubbo.name=dubbo
+protocols.dubbo.ip=127.0.0.1
+protocols.dubbo.port=20000
+protocol_conf.dubbo.session_number=700
+protocol_conf.dubbo.session_timeout=20s
+protocol_conf.dubbo.gr_pool_size=120
+protocol_conf.dubbo.queue_len=64
+protocol_conf.dubbo.queue_number=6
+protocol_conf.dubbo.getty_session_param.compress_encoding=false
+protocol_conf.dubbo.getty_session_param.tcp_no_delay=true
+protocol_conf.dubbo.getty_session_param.tcp_keep_alive=true
+protocol_conf.dubbo.getty_session_param.keep_alive_period=120s
+protocol_conf.dubbo.getty_session_param.tcp_r_buf_size=262144
+protocol_conf.dubbo.getty_session_param.tcp_w_buf_size=65536
+protocol_conf.dubbo.getty_session_param.pkg_wq_size=512
+protocol_conf.dubbo.getty_session_param.tcp_read_timeout=1s
+protocol_conf.dubbo.getty_session_param.tcp_write_timeout=5s
+protocol_conf.dubbo.getty_session_param.wait_timeout=1s
+protocol_conf.dubbo.getty_session_param.max_msg_len=1024
+protocol_conf.dubbo.getty_session_param.session_name=server
\ No newline at end of file
diff --git a/config/testdata/provider_config.yml b/config/testdata/provider_config.yml
index 080feb7dcd1cccd06ae436b2854b2531177d23e3..7c46f9101aa9a6ecb88a92953dfcec28dda4e0ff 100644
--- a/config/testdata/provider_config.yml
+++ b/config/testdata/provider_config.yml
@@ -71,6 +71,10 @@ protocols:
   #    ip: "127.0.0.1"
   #    port: 20001
 
+shutdown_conf:
+  timeout: 60s
+  step_timeout: 10s
+
 protocol_conf:
   dubbo:
     session_number: 700
diff --git a/config/testdata/provider_config_withoutProtocol.yml b/config/testdata/provider_config_withoutProtocol.yml
index 2f65868d4948db9a8b99c500014ea1307569d86f..532d3005aa351820bd540b31e2721dc2a0b5c6ed 100644
--- a/config/testdata/provider_config_withoutProtocol.yml
+++ b/config/testdata/provider_config_withoutProtocol.yml
@@ -51,6 +51,10 @@ protocols:
   #    ip: "127.0.0.1"
   #    port: 20001
 
+shutdown_conf:
+  timeout: 60s
+  step_timeout: 10s
+
 protocol_conf:
   dubbo:
     session_number: 700
diff --git a/config/testdata/router_config.yml b/config/testdata/router_config.yml
new file mode 100644
index 0000000000000000000000000000000000000000..f6b91f5da7d95256e6279924b884bfd450c45a08
--- /dev/null
+++ b/config/testdata/router_config.yml
@@ -0,0 +1,6 @@
+# dubbo router yaml configure file
+priority: 1
+force: true
+conditions :
+  - "a => b"
+  - "c => d"
\ No newline at end of file
diff --git a/config/testdata/router_config_error.yml b/config/testdata/router_config_error.yml
new file mode 100644
index 0000000000000000000000000000000000000000..37894ac96474281d10131b53d8e644f10a18b14e
--- /dev/null
+++ b/config/testdata/router_config_error.yml
@@ -0,0 +1,6 @@
+# dubbo router yaml configure file
+priority: 1
+force: true
+noConditions :
+  - "a => b"
+  - "c => d"
\ No newline at end of file
diff --git a/config_center/apollo/factory.go b/config_center/apollo/factory.go
new file mode 100644
index 0000000000000000000000000000000000000000..a5a69e121598bea4194398423775a99f04b61ced
--- /dev/null
+++ b/config_center/apollo/factory.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 apollo
+
+import (
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/common/extension"
+	"github.com/apache/dubbo-go/config_center"
+	"github.com/apache/dubbo-go/config_center/parser"
+)
+
+func init() {
+	extension.SetConfigCenterFactory("apollo", createDynamicConfigurationFactory)
+}
+
+func createDynamicConfigurationFactory() config_center.DynamicConfigurationFactory {
+	return &apolloConfigurationFactory{}
+}
+
+type apolloConfigurationFactory struct{}
+
+func (f *apolloConfigurationFactory) GetDynamicConfiguration(url *common.URL) (config_center.DynamicConfiguration, error) {
+	dynamicConfiguration, err := newApolloConfiguration(url)
+	if err != nil {
+		return nil, err
+	}
+	dynamicConfiguration.SetParser(&parser.DefaultConfigurationParser{})
+	return dynamicConfiguration, err
+
+}
diff --git a/config_center/apollo/impl.go b/config_center/apollo/impl.go
new file mode 100644
index 0000000000000000000000000000000000000000..4dc19817846fe5c9c0552738f2058a15d20efabc
--- /dev/null
+++ b/config_center/apollo/impl.go
@@ -0,0 +1,169 @@
+/*
+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 apollo
+
+import (
+	"fmt"
+	"regexp"
+	"strings"
+	"sync"
+)
+
+import (
+	"github.com/pkg/errors"
+	"github.com/zouyx/agollo"
+)
+
+import (
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/common/constant"
+	cc "github.com/apache/dubbo-go/config_center"
+	"github.com/apache/dubbo-go/config_center/parser"
+	"github.com/apache/dubbo-go/remoting"
+)
+
+const (
+	apolloProtocolPrefix = "http://"
+	apolloConfigFormat   = "%s.%s"
+)
+
+type apolloConfiguration struct {
+	url *common.URL
+
+	listeners sync.Map
+	appConf   *agollo.AppConfig
+	parser    parser.ConfigurationParser
+}
+
+func newApolloConfiguration(url *common.URL) (*apolloConfiguration, error) {
+	c := &apolloConfiguration{
+		url: url,
+	}
+	configAddr := c.getAddressWithProtocolPrefix(url)
+	configCluster := url.GetParam(constant.CONFIG_CLUSTER_KEY, "")
+
+	appId := url.GetParam(constant.CONFIG_APP_ID_KEY, "")
+	namespaces := getProperties(url.GetParam(constant.CONFIG_NAMESPACE_KEY, cc.DEFAULT_GROUP))
+	c.appConf = &agollo.AppConfig{
+		AppId:         appId,
+		Cluster:       configCluster,
+		NamespaceName: namespaces,
+		Ip:            configAddr,
+	}
+
+	agollo.InitCustomConfig(func() (*agollo.AppConfig, error) {
+		return c.appConf, nil
+	})
+
+	return c, agollo.Start()
+}
+
+func getChangeType(change agollo.ConfigChangeType) remoting.EventType {
+	switch change {
+	case agollo.ADDED:
+		return remoting.EventTypeAdd
+	case agollo.DELETED:
+		return remoting.EventTypeDel
+	default:
+		return remoting.EventTypeUpdate
+	}
+}
+
+func (c *apolloConfiguration) AddListener(key string, listener cc.ConfigurationListener, opts ...cc.Option) {
+	k := &cc.Options{}
+	for _, opt := range opts {
+		opt(k)
+	}
+
+	key = k.Group + key
+	l, _ := c.listeners.LoadOrStore(key, NewApolloListener())
+	l.(*apolloListener).AddListener(listener)
+}
+
+func (c *apolloConfiguration) RemoveListener(key string, listener cc.ConfigurationListener, opts ...cc.Option) {
+	k := &cc.Options{}
+	for _, opt := range opts {
+		opt(k)
+	}
+
+	key = k.Group + key
+	l, ok := c.listeners.Load(key)
+	if ok {
+		l.(*apolloListener).RemoveListener(listener)
+	}
+}
+
+func getProperties(namespace string) string {
+	return getNamespaceName(namespace, agollo.Properties)
+}
+
+func getNamespaceName(namespace string, configFileFormat agollo.ConfigFileFormat) string {
+	return fmt.Sprintf(apolloConfigFormat, namespace, 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 config.GetStringValue(key, ""), nil
+}
+
+func (c *apolloConfiguration) GetRule(key string, opts ...cc.Option) (string, error) {
+	return c.GetInternalProperty(key, opts...)
+}
+
+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:
+	 * key=dubbo.propertie
+	 */
+	config := agollo.GetConfig(key)
+	if config == nil {
+		return "", errors.New(fmt.Sprintf("nothing in namespace:%s ", key))
+	}
+	return config.GetContent(agollo.Properties), nil
+}
+
+func (c *apolloConfiguration) getAddressWithProtocolPrefix(url *common.URL) string {
+	address := url.Location
+	converted := address
+	if len(address) != 0 {
+		reg := regexp.MustCompile("\\s+")
+		address = reg.ReplaceAllString(address, "")
+		parts := strings.Split(address, ",")
+		addrs := make([]string, 0)
+		for _, part := range parts {
+			addr := part
+			if !strings.HasPrefix(part, apolloProtocolPrefix) {
+				addr = apolloProtocolPrefix + part
+			}
+			addrs = append(addrs, addr)
+		}
+		converted = strings.Join(addrs, ",")
+	}
+	return converted
+}
+
+func (c *apolloConfiguration) Parser() parser.ConfigurationParser {
+	return c.parser
+}
+
+func (c *apolloConfiguration) SetParser(p parser.ConfigurationParser) {
+	c.parser = p
+}
diff --git a/config_center/apollo/impl_test.go b/config_center/apollo/impl_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..a95524b41b887313993aad4e774ed6d96b24c08f
--- /dev/null
+++ b/config_center/apollo/impl_test.go
@@ -0,0 +1,274 @@
+/*
+ * 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 apollo
+
+import (
+	"fmt"
+	"net/http"
+	"net/http/httptest"
+	"os"
+	"strings"
+	"sync"
+	"testing"
+	"time"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+import (
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/config"
+	"github.com/apache/dubbo-go/config_center"
+	"github.com/apache/dubbo-go/config_center/parser"
+	"github.com/apache/dubbo-go/remoting"
+)
+
+const (
+	mockAppId     = "testApplication_yang"
+	mockCluster   = "dev"
+	mockNamespace = "mockDubbog.properties"
+	mockNotifyRes = `[{
+	"namespaceName": "mockDubbog.properties",
+	"notificationId": 53050,
+	"messages": {
+		"details": {
+			"testApplication_yang+default+mockDubbog": 53050
+		}
+	}
+}]`
+	mockServiceConfigRes = `[{
+	"appName": "APOLLO-CONFIGSERVICE",
+	"instanceId": "instance-300408ep:apollo-configservice:8080",
+	"homepageUrl": "http://localhost:8080"
+}]`
+)
+
+var (
+	mockConfigRes = `{
+	"appId": "testApplication_yang",
+	"cluster": "default",
+	"namespaceName": "mockDubbog.properties",
+	"configurations": {
+		"registries.hangzhouzk.username": "",
+		"application.owner": "ZX",
+		"registries.shanghaizk.username": "",
+		"protocols.dubbo.ip": "127.0.0.1",
+		"protocol_conf.dubbo.getty_session_param.tcp_write_timeout": "5s",
+		"services.UserProvider.cluster": "failover",
+		"application.module": "dubbogo user-info server",
+		"services.UserProvider.interface": "com.ikurento.user.UserProvider",
+		"protocol_conf.dubbo.getty_session_param.compress_encoding": "false",
+		"registries.shanghaizk.address": "127.0.0.1:2182",
+		"protocol_conf.dubbo.session_timeout": "20s",
+		"registries.shanghaizk.timeout": "3s",
+		"protocol_conf.dubbo.getty_session_param.keep_alive_period": "120s",
+		"services.UserProvider.warmup": "100",
+		"application.version": "0.0.1",
+		"registries.hangzhouzk.protocol": "zookeeper",
+		"registries.hangzhouzk.password": "",
+		"protocols.dubbo.name": "dubbo",
+		"protocol_conf.dubbo.getty_session_param.wait_timeout": "1s",
+		"protocols.dubbo.port": "20000",
+		"application_config.owner": "demo",
+		"application_config.name": "demo",
+		"application_config.version": "0.0.1",
+		"application_config.environment": "dev",
+		"protocol_conf.dubbo.getty_session_param.session_name": "server",
+		"application.name": "BDTService",
+		"registries.hangzhouzk.timeout": "3s",
+		"protocol_conf.dubbo.getty_session_param.tcp_read_timeout": "1s",
+		"services.UserProvider.loadbalance": "random",
+		"protocol_conf.dubbo.session_number": "700",
+		"protocol_conf.dubbo.getty_session_param.max_msg_len": "1024",
+		"services.UserProvider.registry": "hangzhouzk",
+		"application_config.module": "demo",
+		"services.UserProvider.methods[0].name": "GetUser",
+		"protocol_conf.dubbo.getty_session_param.tcp_no_delay": "true",
+		"services.UserProvider.methods[0].retries": "1",
+		"protocol_conf.dubbo.getty_session_param.tcp_w_buf_size": "65536",
+		"protocol_conf.dubbo.getty_session_param.tcp_r_buf_size": "262144",
+		"registries.shanghaizk.password": "",
+		"application_config.organization": "demo",
+		"registries.shanghaizk.protocol": "zookeeper",
+		"protocol_conf.dubbo.getty_session_param.tcp_keep_alive": "true",
+		"registries.hangzhouzk.address": "127.0.0.1:2181",
+		"application.environment": "dev",
+		"services.UserProvider.protocol": "dubbo",
+		"application.organization": "ikurento.com",
+		"protocol_conf.dubbo.getty_session_param.pkg_wq_size": "512",
+		"services.UserProvider.methods[0].loadbalance": "random"
+	},
+	"releaseKey": "20191104105242-0f13805d89f834a4"
+}`
+)
+
+func initApollo() *httptest.Server {
+	handlerMap := make(map[string]func(http.ResponseWriter, *http.Request), 1)
+	handlerMap[mockNamespace] = configResponse
+
+	return runMockConfigServer(handlerMap, notifyResponse)
+}
+
+func configResponse(rw http.ResponseWriter, req *http.Request) {
+	result := fmt.Sprintf(mockConfigRes)
+	fmt.Fprintf(rw, "%s", result)
+}
+
+func notifyResponse(rw http.ResponseWriter, req *http.Request) {
+	result := fmt.Sprintf(mockNotifyRes)
+	fmt.Fprintf(rw, "%s", result)
+}
+
+func serviceConfigResponse(rw http.ResponseWriter, req *http.Request) {
+	result := fmt.Sprintf(mockServiceConfigRes)
+	fmt.Fprintf(rw, "%s", result)
+}
+
+// run mock config server
+func runMockConfigServer(handlerMap map[string]func(http.ResponseWriter, *http.Request),
+	notifyHandler func(http.ResponseWriter, *http.Request)) *httptest.Server {
+	uriHandlerMap := make(map[string]func(http.ResponseWriter, *http.Request), 0)
+	for namespace, handler := range handlerMap {
+		uri := fmt.Sprintf("/configs/%s/%s/%s", mockAppId, mockCluster, namespace)
+		uriHandlerMap[uri] = handler
+	}
+	uriHandlerMap["/notifications/v2"] = notifyHandler
+	uriHandlerMap["/services/config"] = serviceConfigResponse
+
+	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		uri := r.RequestURI
+		for path, handler := range uriHandlerMap {
+			if strings.HasPrefix(uri, path) {
+				handler(w, r)
+				break
+			}
+		}
+	}))
+
+	return ts
+}
+
+func Test_GetConfig(t *testing.T) {
+	configuration := initMockApollo(t)
+	configs, err := configuration.GetProperties(mockNamespace, config_center.WithGroup("dubbo"))
+	assert.NoError(t, err)
+	configuration.SetParser(&parser.DefaultConfigurationParser{})
+	mapContent, err := configuration.Parser().Parse(configs)
+	assert.NoError(t, err)
+	assert.Equal(t, "ikurento.com", mapContent["application.organization"])
+	deleteMockJson(t)
+}
+
+func Test_GetConfigItem(t *testing.T) {
+	configuration := initMockApollo(t)
+	configs, err := configuration.GetInternalProperty("application.organization")
+	assert.NoError(t, err)
+	configuration.SetParser(&parser.DefaultConfigurationParser{})
+	assert.NoError(t, err)
+	assert.Equal(t, "ikurento.com", configs)
+	deleteMockJson(t)
+}
+
+func initMockApollo(t *testing.T) *apolloConfiguration {
+	c := &config.BaseConfig{ConfigCenterConfig: &config.ConfigCenterConfig{
+		Protocol:  "apollo",
+		Address:   "106.12.25.204:8080",
+		AppId:     "testApplication_yang",
+		Cluster:   "dev",
+		Namespace: "mockDubbog",
+	}}
+	apollo := initApollo()
+	apolloUrl := strings.ReplaceAll(apollo.URL, "http", "apollo")
+	url, err := common.NewURL(apolloUrl, common.WithParams(c.ConfigCenterConfig.GetUrlMap()))
+	assert.NoError(t, err)
+	configuration, err := newApolloConfiguration(&url)
+	assert.NoError(t, err)
+	return configuration
+}
+
+func TestAddListener(t *testing.T) {
+	listener := &apolloDataListener{}
+	listener.wg.Add(1)
+	apollo := initMockApollo(t)
+	mockConfigRes = `{
+	"appId": "testApplication_yang",
+	"cluster": "default",
+	"namespaceName": "mockDubbog.properties",
+	"configurations": {
+		"registries.hangzhouzk.username": "11111"
+	},
+	"releaseKey": "20191104105242-0f13805d89f834a4"
+}`
+	apollo.AddListener(mockNamespace, listener)
+	listener.wg.Wait()
+	assert.Equal(t, "registries.hangzhouzk.username", listener.event)
+	assert.Greater(t, listener.count, 0)
+	deleteMockJson(t)
+}
+
+func TestRemoveListener(t *testing.T) {
+	listener := &apolloDataListener{}
+	apollo := initMockApollo(t)
+	mockConfigRes = `{
+	"appId": "testApplication_yang",
+	"cluster": "default",
+	"namespaceName": "mockDubbog.properties",
+	"configurations": {
+		"registries.hangzhouzk.username": "11111"
+	},
+	"releaseKey": "20191104105242-0f13805d89f834a4"
+}`
+	apollo.AddListener(mockNamespace, listener)
+	apollo.RemoveListener(mockNamespace, listener)
+	assert.Equal(t, "", listener.event)
+	listenerCount := 0
+	apollo.listeners.Range(func(key, value interface{}) bool {
+		apolloListener := value.(*apolloListener)
+		for e := range apolloListener.listeners {
+			fmt.Println(e)
+			listenerCount++
+		}
+		return true
+	})
+	assert.Equal(t, listenerCount, 0)
+	assert.Equal(t, listener.count, 0)
+	deleteMockJson(t)
+}
+
+type apolloDataListener struct {
+	wg    sync.WaitGroup
+	count int
+	event string
+}
+
+func (l *apolloDataListener) Process(configType *config_center.ConfigChangeEvent) {
+	if configType.ConfigType != remoting.EventTypeUpdate {
+		return
+	}
+	l.wg.Done()
+	l.count++
+	l.event = configType.Key
+}
+
+func deleteMockJson(t *testing.T) {
+	// because the file write in another goroutine,so have a break ...
+	time.Sleep(100 * time.Millisecond)
+	remove := os.Remove("mockDubbog.properties.json")
+	t.Log("remove result:", remove)
+}
diff --git a/config_center/apollo/listener.go b/config_center/apollo/listener.go
new file mode 100644
index 0000000000000000000000000000000000000000..820d02fb48e2204c3f1eb74fd5624132a63d367e
--- /dev/null
+++ b/config_center/apollo/listener.go
@@ -0,0 +1,63 @@
+/*
+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 apollo
+
+import (
+	"github.com/zouyx/agollo"
+)
+
+import (
+	"github.com/apache/dubbo-go/config_center"
+)
+
+type apolloListener struct {
+	listeners map[config_center.ConfigurationListener]struct{}
+}
+
+// NewApolloListener ...
+func NewApolloListener() *apolloListener {
+	return &apolloListener{
+		listeners: make(map[config_center.ConfigurationListener]struct{}, 0),
+	}
+}
+
+// OnChange ...
+func (a *apolloListener) OnChange(changeEvent *agollo.ChangeEvent) {
+	for key, change := range changeEvent.Changes {
+		for listener := range a.listeners {
+			listener.Process(&config_center.ConfigChangeEvent{
+				ConfigType: getChangeType(change.ChangeType),
+				Key:        key,
+				Value:      change.NewValue,
+			})
+		}
+	}
+}
+
+// AddListener ...
+func (a *apolloListener) AddListener(l config_center.ConfigurationListener) {
+	if _, ok := a.listeners[l]; !ok {
+		a.listeners[l] = struct{}{}
+		agollo.AddChangeListener(a)
+	}
+}
+
+// RemoveListener ...
+func (a *apolloListener) RemoveListener(l config_center.ConfigurationListener) {
+	delete(a.listeners, l)
+}
diff --git a/config_center/configuration_listener.go b/config_center/configuration_listener.go
index 1419bcdd0ce10ec15d0c24c2439bb02747ce5391..e70e4f68075c51c33f1110ef44a7b703e36fb78d 100644
--- a/config_center/configuration_listener.go
+++ b/config_center/configuration_listener.go
@@ -25,10 +25,12 @@ import (
 	"github.com/apache/dubbo-go/remoting"
 )
 
+// ConfigurationListener ...
 type ConfigurationListener interface {
 	Process(*ConfigChangeEvent)
 }
 
+// ConfigChangeEvent ...
 type ConfigChangeEvent struct {
 	Key        string
 	Value      interface{}
diff --git a/config_center/configurator.go b/config_center/configurator.go
index 3ba293ec60302b76becce357f49b2baa543f69cd..ffa9034e05c4c3d4cc254886e2ed19576f155dec 100644
--- a/config_center/configurator.go
+++ b/config_center/configurator.go
@@ -21,6 +21,7 @@ import (
 	"github.com/apache/dubbo-go/common"
 )
 
+// Configurator ...
 type Configurator interface {
 	GetUrl() *common.URL
 	Configure(url *common.URL)
diff --git a/config_center/configurator/mock.go b/config_center/configurator/mock.go
index 1f03d107c8f588cfd4c23c9086bb0fbe42e05fff..d294b9195db9cfe60056bc29ec26816f740ea396 100644
--- a/config_center/configurator/mock.go
+++ b/config_center/configurator/mock.go
@@ -14,6 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package configurator
 
 import (
@@ -22,6 +23,7 @@ import (
 	"github.com/apache/dubbo-go/config_center"
 )
 
+// NewMockConfigurator ...
 func NewMockConfigurator(url *common.URL) config_center.Configurator {
 	return &mockConfigurator{configuratorUrl: url}
 }
@@ -30,10 +32,12 @@ type mockConfigurator struct {
 	configuratorUrl *common.URL
 }
 
+// GetUrl ...
 func (c *mockConfigurator) GetUrl() *common.URL {
 	return c.configuratorUrl
 }
 
+// Configure ...
 func (c *mockConfigurator) Configure(url *common.URL) {
 	if cluster := c.GetUrl().GetParam(constant.CLUSTER_KEY, ""); cluster != "" {
 		url.SetParam(constant.CLUSTER_KEY, cluster)
diff --git a/config_center/configurator/override.go b/config_center/configurator/override.go
index e85b4d3ec9d5e6f9f7163cefce3f328f8dcc225a..18415bee3a28b37ffc2f3f73cc7309b685de5408 100644
--- a/config_center/configurator/override.go
+++ b/config_center/configurator/override.go
@@ -14,6 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package configurator
 
 import (
@@ -21,7 +22,7 @@ import (
 )
 
 import (
-	"github.com/dubbogo/gost/container/gxset"
+	gxset "github.com/dubbogo/gost/container/set"
 	gxnet "github.com/dubbogo/gost/net"
 )
 
@@ -35,6 +36,7 @@ import (
 func init() {
 	extension.SetDefaultConfigurator(newConfigurator)
 }
+
 func newConfigurator(url *common.URL) config_center.Configurator {
 	return &overrideConfigurator{configuratorUrl: url}
 }
diff --git a/config_center/configurator/override_test.go b/config_center/configurator/override_test.go
index a585f4217f81a5d600ec9a48c12b3b47ff2d5322..c0aeb15130e7862fcb00d6cb82cbef60df777acb 100644
--- a/config_center/configurator/override_test.go
+++ b/config_center/configurator/override_test.go
@@ -17,7 +17,6 @@
 package configurator
 
 import (
-	"context"
 	"testing"
 )
 
@@ -32,45 +31,49 @@ import (
 )
 
 func Test_configureVerison2p6(t *testing.T) {
-	url, err := common.NewURL(context.Background(), "override://0.0.0.0:0/com.xxx.mock.userProvider?group=1&version=1&cluster=failfast&application=BDTService")
+	url, err := common.NewURL("override://0.0.0.0:0/com.xxx.mock.userProvider?group=1&version=1&cluster=failfast&application=BDTService")
 	assert.NoError(t, err)
 	configurator := extension.GetConfigurator("default", &url)
 	assert.Equal(t, "override", configurator.GetUrl().Protocol)
 
-	providerUrl, err := common.NewURL(context.Background(), "jsonrpc://127.0.0.1:20001/com.ikurento.user.UserProvider?anyhost=true&app.version=0.0.1&application=BDTService&category=providers&cluster=failover&dubbo=dubbo-provider-golang-2.6.0&environment=dev&group=&interface=com.ikurento.user.UserProvider&ip=10.32.20.124&loadbalance=random&methods.GetUser.loadbalance=random&methods.GetUser.retries=1&methods.GetUser.weight=0&module=dubbogo+user-info+server&name=BDTService&organization=ikurento.com&owner=ZX&pid=64225&retries=0&service.filter=echo&side=provider&timestamp=1562076628&version=&warmup=100")
+	providerUrl, err := common.NewURL("jsonrpc://127.0.0.1:20001/com.ikurento.user.UserProvider?anyhost=true&app.version=0.0.1&application=BDTService&category=providers&cluster=failover&dubbo=dubbo-provider-golang-2.6.0&environment=dev&group=&interface=com.ikurento.user.UserProvider&ip=10.32.20.124&loadbalance=random&methods.GetUser.loadbalance=random&methods.GetUser.retries=1&methods.GetUser.weight=0&module=dubbogo+user-info+server&name=BDTService&organization=ikurento.com&owner=ZX&pid=64225&retries=0&service.filter=echo&side=provider&timestamp=1562076628&version=&warmup=100")
+	assert.NoError(t, err)
 	configurator.Configure(&providerUrl)
 	assert.Equal(t, "failfast", providerUrl.GetParam(constant.CLUSTER_KEY, ""))
-
 }
+
 func Test_configureVerisonOverrideAddr(t *testing.T) {
-	url, err := common.NewURL(context.Background(), "override://0.0.0.0:0/com.xxx.mock.userProvider?group=1&version=1&cluster=failfast&application=BDTService&providerAddresses=127.0.0.2:20001|127.0.0.3:20001")
+	url, err := common.NewURL("override://0.0.0.0:0/com.xxx.mock.userProvider?group=1&version=1&cluster=failfast&application=BDTService&providerAddresses=127.0.0.2:20001|127.0.0.3:20001")
 	assert.NoError(t, err)
 	configurator := extension.GetConfigurator("default", &url)
 	assert.Equal(t, "override", configurator.GetUrl().Protocol)
 
-	providerUrl, err := common.NewURL(context.Background(), "jsonrpc://127.0.0.1:20001/com.ikurento.user.UserProvider?anyhost=true&app.version=0.0.1&application=BDTService&category=providers&cluster=failover&dubbo=dubbo-provider-golang-2.6.0&environment=dev&group=&interface=com.ikurento.user.UserProvider&ip=10.32.20.124&loadbalance=random&methods.GetUser.loadbalance=random&methods.GetUser.retries=1&methods.GetUser.weight=0&module=dubbogo+user-info+server&name=BDTService&organization=ikurento.com&owner=ZX&pid=64225&retries=0&service.filter=echo&side=provider&timestamp=1562076628&version=&warmup=100")
+	providerUrl, err := common.NewURL("jsonrpc://127.0.0.1:20001/com.ikurento.user.UserProvider?anyhost=true&app.version=0.0.1&application=BDTService&category=providers&cluster=failover&dubbo=dubbo-provider-golang-2.6.0&environment=dev&group=&interface=com.ikurento.user.UserProvider&ip=10.32.20.124&loadbalance=random&methods.GetUser.loadbalance=random&methods.GetUser.retries=1&methods.GetUser.weight=0&module=dubbogo+user-info+server&name=BDTService&organization=ikurento.com&owner=ZX&pid=64225&retries=0&service.filter=echo&side=provider&timestamp=1562076628&version=&warmup=100")
+	assert.NoError(t, err)
 	configurator.Configure(&providerUrl)
 	assert.Equal(t, "failover", providerUrl.GetParam(constant.CLUSTER_KEY, ""))
-
 }
+
 func Test_configureVerison2p6WithIp(t *testing.T) {
-	url, err := common.NewURL(context.Background(), "override://127.0.0.1:20001/com.xxx.mock.userProvider?group=1&version=1&cluster=failfast&application=BDTService")
+	url, err := common.NewURL("override://127.0.0.1:20001/com.xxx.mock.userProvider?group=1&version=1&cluster=failfast&application=BDTService")
 	assert.NoError(t, err)
 	configurator := extension.GetConfigurator("default", &url)
 	assert.Equal(t, "override", configurator.GetUrl().Protocol)
 
-	providerUrl, err := common.NewURL(context.Background(), "jsonrpc://127.0.0.1:20001/com.ikurento.user.UserProvider?anyhost=true&app.version=0.0.1&application=BDTService&category=providers&cluster=failover&dubbo=dubbo-provider-golang-2.6.0&environment=dev&group=&interface=com.ikurento.user.UserProvider&ip=10.32.20.124&loadbalance=random&methods.GetUser.loadbalance=random&methods.GetUser.retries=1&methods.GetUser.weight=0&module=dubbogo+user-info+server&name=BDTService&organization=ikurento.com&owner=ZX&pid=64225&retries=0&service.filter=echo&side=provider&timestamp=1562076628&version=&warmup=100")
+	providerUrl, err := common.NewURL("jsonrpc://127.0.0.1:20001/com.ikurento.user.UserProvider?anyhost=true&app.version=0.0.1&application=BDTService&category=providers&cluster=failover&dubbo=dubbo-provider-golang-2.6.0&environment=dev&group=&interface=com.ikurento.user.UserProvider&ip=10.32.20.124&loadbalance=random&methods.GetUser.loadbalance=random&methods.GetUser.retries=1&methods.GetUser.weight=0&module=dubbogo+user-info+server&name=BDTService&organization=ikurento.com&owner=ZX&pid=64225&retries=0&service.filter=echo&side=provider&timestamp=1562076628&version=&warmup=100")
+	assert.NoError(t, err)
 	configurator.Configure(&providerUrl)
 	assert.Equal(t, "failfast", providerUrl.GetParam(constant.CLUSTER_KEY, ""))
 
 }
 
 func Test_configureVerison2p7(t *testing.T) {
-	url, err := common.NewURL(context.Background(), "jsonrpc://0.0.0.0:20001/com.xxx.mock.userProvider?group=1&version=1&cluster=failfast&application=BDTService&configVersion=1.0&side=provider")
+	url, err := common.NewURL("jsonrpc://0.0.0.0:20001/com.xxx.mock.userProvider?group=1&version=1&cluster=failfast&application=BDTService&configVersion=1.0&side=provider")
 	assert.NoError(t, err)
 	configurator := extension.GetConfigurator("default", &url)
 
-	providerUrl, err := common.NewURL(context.Background(), "jsonrpc://127.0.0.1:20001/com.ikurento.user.UserProvider?anyhost=true&app.version=0.0.1&application=BDTService&category=providers&cluster=failover&dubbo=dubbo-provider-golang-2.6.0&environment=dev&group=&interface=com.ikurento.user.UserProvider&ip=10.32.20.124&loadbalance=random&methods.GetUser.loadbalance=random&methods.GetUser.retries=1&methods.GetUser.weight=0&module=dubbogo+user-info+server&name=BDTService&organization=ikurento.com&owner=ZX&pid=64225&retries=0&service.filter=echo&side=provider&timestamp=1562076628&version=&warmup=100")
+	providerUrl, err := common.NewURL("jsonrpc://127.0.0.1:20001/com.ikurento.user.UserProvider?anyhost=true&app.version=0.0.1&application=BDTService&category=providers&cluster=failover&dubbo=dubbo-provider-golang-2.6.0&environment=dev&group=&interface=com.ikurento.user.UserProvider&ip=10.32.20.124&loadbalance=random&methods.GetUser.loadbalance=random&methods.GetUser.retries=1&methods.GetUser.weight=0&module=dubbogo+user-info+server&name=BDTService&organization=ikurento.com&owner=ZX&pid=64225&retries=0&service.filter=echo&side=provider&timestamp=1562076628&version=&warmup=100")
+	assert.NoError(t, err)
 	configurator.Configure(&providerUrl)
 	assert.Equal(t, "failfast", providerUrl.GetParam(constant.CLUSTER_KEY, ""))
 
diff --git a/config_center/dynamic_configuration.go b/config_center/dynamic_configuration.go
index 1028b26d963cfcb02636113abc3e482bb22192a0..d6c3b06b327f16c709b09121e589db6694d3663e 100644
--- a/config_center/dynamic_configuration.go
+++ b/config_center/dynamic_configuration.go
@@ -22,39 +22,60 @@ import (
 )
 
 import (
+	"github.com/apache/dubbo-go/common"
 	"github.com/apache/dubbo-go/config_center/parser"
 )
 
 //////////////////////////////////////////
 // DynamicConfiguration
 //////////////////////////////////////////
-const DEFAULT_GROUP = "dubbo"
-const DEFAULT_CONFIG_TIMEOUT = "10s"
+const (
+	// DEFAULT_GROUP: default group
+	DEFAULT_GROUP = "dubbo"
+	// DEFAULT_CONFIG_TIMEOUT: default config timeout
+	DEFAULT_CONFIG_TIMEOUT = "10s"
+)
 
+// DynamicConfiguration ...
 type DynamicConfiguration interface {
 	Parser() parser.ConfigurationParser
 	SetParser(parser.ConfigurationParser)
 	AddListener(string, ConfigurationListener, ...Option)
 	RemoveListener(string, ConfigurationListener, ...Option)
-	GetConfig(string, ...Option) (string, error)
-	GetConfigs(string, ...Option) (string, error)
+	//GetProperties get properties file
+	GetProperties(string, ...Option) (string, error)
+
+	//GetRule get Router rule properties file
+	GetRule(string, ...Option) (string, error)
+
+	//GetInternalProperty get value by key in Default properties file(dubbo.properties)
+	GetInternalProperty(string, ...Option) (string, error)
 }
 
+// Options ...
 type Options struct {
 	Group   string
 	Timeout time.Duration
 }
 
+// Option ...
 type Option func(*Options)
 
+// WithGroup ...
 func WithGroup(group string) Option {
 	return func(opt *Options) {
 		opt.Group = group
 	}
 }
 
+// WithTimeout ...
 func WithTimeout(time time.Duration) Option {
 	return func(opt *Options) {
 		opt.Timeout = time
 	}
 }
+
+//GetRuleKey The format is '{interfaceName}:[version]:[group]'
+func GetRuleKey(url common.URL) string {
+	return url.ColonSeparatedKey()
+}
diff --git a/config_center/dynamic_configuration_factory.go b/config_center/dynamic_configuration_factory.go
index 0720896fb615f8639c20a46d2078c3dfcd112c32..9f9b13227f6623a02b0261c46d8d1e43624005f8 100644
--- a/config_center/dynamic_configuration_factory.go
+++ b/config_center/dynamic_configuration_factory.go
@@ -21,6 +21,7 @@ import (
 	"github.com/apache/dubbo-go/common"
 )
 
+// DynamicConfigurationFactory ...
 type DynamicConfigurationFactory interface {
 	GetDynamicConfiguration(*common.URL) (DynamicConfiguration, error)
 }
diff --git a/config_center/mock_dynamic_config.go b/config_center/mock_dynamic_config.go
index 47b509231d225491e6791e295a707756256f61d5..4d972b629abb7abd7cc0d0018026e4ccc04a1e4f 100644
--- a/config_center/mock_dynamic_config.go
+++ b/config_center/mock_dynamic_config.go
@@ -32,6 +32,7 @@ import (
 	"github.com/apache/dubbo-go/remoting"
 )
 
+// MockDynamicConfigurationFactory ...
 type MockDynamicConfigurationFactory struct {
 	Content string
 }
@@ -41,7 +42,8 @@ var (
 	dynamicConfiguration *MockDynamicConfiguration
 )
 
-func (f *MockDynamicConfigurationFactory) GetDynamicConfiguration(url *common.URL) (DynamicConfiguration, error) {
+// GetDynamicConfiguration ...
+func (f *MockDynamicConfigurationFactory) GetDynamicConfiguration(_ *common.URL) (DynamicConfiguration, error) {
 	var err error
 	once.Do(func() {
 		dynamicConfiguration = &MockDynamicConfiguration{listener: map[string]ConfigurationListener{}}
@@ -79,36 +81,59 @@ func (f *MockDynamicConfigurationFactory) GetDynamicConfiguration(url *common.UR
 
 }
 
+// MockDynamicConfiguration ...
 type MockDynamicConfiguration struct {
 	parser   parser.ConfigurationParser
 	content  string
 	listener map[string]ConfigurationListener
 }
 
-func (c *MockDynamicConfiguration) AddListener(key string, listener ConfigurationListener, opions ...Option) {
+// AddListener ...
+func (c *MockDynamicConfiguration) AddListener(key string, listener ConfigurationListener, _ ...Option) {
 	c.listener[key] = listener
 }
 
-func (c *MockDynamicConfiguration) RemoveListener(key string, listener ConfigurationListener, opions ...Option) {
+// RemoveListener ...
+func (c *MockDynamicConfiguration) RemoveListener(_ string, _ ConfigurationListener, _ ...Option) {
 }
 
-func (c *MockDynamicConfiguration) GetConfig(key string, opts ...Option) (string, error) {
+// GetConfig ...
+func (c *MockDynamicConfiguration) GetConfig(_ string, _ ...Option) (string, error) {
 
 	return c.content, nil
 }
 
-//For zookeeper, getConfig and getConfigs have the same meaning.
+// GetConfigs For zookeeper, getConfig and getConfigs have the same meaning.
 func (c *MockDynamicConfiguration) GetConfigs(key string, opts ...Option) (string, error) {
 	return c.GetConfig(key, opts...)
 }
 
+// Parser ...
 func (c *MockDynamicConfiguration) Parser() parser.ConfigurationParser {
 	return c.parser
 }
+
+// SetParser ...
 func (c *MockDynamicConfiguration) SetParser(p parser.ConfigurationParser) {
 	c.parser = p
 }
 
+// GetProperties ...
+func (c *MockDynamicConfiguration) GetProperties(_ string, _ ...Option) (string, error) {
+	return c.content, nil
+}
+
+// GetInternalProperty For zookeeper, getConfig and getConfigs have the same meaning.
+func (c *MockDynamicConfiguration) GetInternalProperty(key string, opts ...Option) (string, error) {
+	return c.GetProperties(key, opts...)
+}
+
+// GetRule ...
+func (c *MockDynamicConfiguration) GetRule(key string, opts ...Option) (string, error) {
+	return c.GetProperties(key, opts...)
+}
+
+// MockServiceConfigEvent ...
 func (c *MockDynamicConfiguration) MockServiceConfigEvent() {
 	config := &parser.ConfiguratorConfig{
 		ConfigVersion: "2.7.1",
@@ -130,6 +155,7 @@ func (c *MockDynamicConfiguration) MockServiceConfigEvent() {
 	c.listener[key].Process(&ConfigChangeEvent{Key: key, Value: string(value), ConfigType: remoting.EventTypeAdd})
 }
 
+// MockApplicationConfigEvent ...
 func (c *MockDynamicConfiguration) MockApplicationConfigEvent() {
 	config := &parser.ConfiguratorConfig{
 		ConfigVersion: "2.7.1",
diff --git a/config_center/nacos/client.go b/config_center/nacos/client.go
new file mode 100644
index 0000000000000000000000000000000000000000..1bf61a942ba9f7530b495a57465c4ee6cb0c98c1
--- /dev/null
+++ b/config_center/nacos/client.go
@@ -0,0 +1,234 @@
+/*
+ * 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"
+	"strings"
+	"sync"
+	"time"
+)
+
+import (
+	"github.com/nacos-group/nacos-sdk-go/clients"
+	"github.com/nacos-group/nacos-sdk-go/clients/config_client"
+	nacosconst "github.com/nacos-group/nacos-sdk-go/common/constant"
+	perrors "github.com/pkg/errors"
+)
+
+import (
+	"github.com/apache/dubbo-go/common/constant"
+	"github.com/apache/dubbo-go/common/logger"
+)
+
+const logDir = "logs/nacos/log"
+
+// NacosClient Nacos client
+type NacosClient struct {
+	name       string
+	NacosAddrs []string
+	sync.Mutex // for Client
+	client     *config_client.IConfigClient
+	exit       chan struct{}
+	Timeout    time.Duration
+	once       sync.Once
+	onceClose  func()
+}
+
+// Client Get Client
+func (n *NacosClient) Client() *config_client.IConfigClient {
+	return n.client
+}
+
+// SetClient Set client
+func (n *NacosClient) SetClient(client *config_client.IConfigClient) {
+	n.Lock()
+	n.client = client
+	n.Unlock()
+}
+
+type option func(*options)
+
+type options struct {
+	nacosName string
+	client    *NacosClient
+}
+
+// WithNacosName Set nacos name
+func WithNacosName(name string) option {
+	return func(opt *options) {
+		opt.nacosName = name
+	}
+}
+
+// ValidateNacosClient Validate nacos client , if null then create it
+func ValidateNacosClient(container nacosClientFacade, opts ...option) error {
+	if container == nil {
+		return perrors.Errorf("container can not be null")
+	}
+	os := &options{}
+	for _, opt := range opts {
+		opt(os)
+	}
+
+	url := container.GetUrl()
+
+	if container.NacosClient() == nil {
+		//in dubbo ,every registry only connect one node ,so this is []string{r.Address}
+		timeout, err := time.ParseDuration(url.GetParam(constant.REGISTRY_TIMEOUT_KEY, constant.DEFAULT_REG_TIMEOUT))
+		if err != nil {
+			logger.Errorf("timeout config %v is invalid ,err is %v",
+				url.GetParam(constant.REGISTRY_TIMEOUT_KEY, constant.DEFAULT_REG_TIMEOUT), err.Error())
+			return perrors.WithMessagef(err, "newNacosClient(address:%+v)", url.Location)
+		}
+		nacosAddresses := strings.Split(url.Location, ",")
+		newClient, err := newNacosClient(os.nacosName, nacosAddresses, timeout)
+		if err != nil {
+			logger.Warnf("newNacosClient(name{%s}, nacos address{%v}, timeout{%d}) = error{%v}",
+				os.nacosName, url.Location, timeout.String(), err)
+			return perrors.WithMessagef(err, "newNacosClient(address:%+v)", url.Location)
+		}
+		container.SetNacosClient(newClient)
+	}
+
+	if container.NacosClient().Client() == nil {
+		svrConfList := []nacosconst.ServerConfig{}
+		for _, nacosAddr := range container.NacosClient().NacosAddrs {
+			split := strings.Split(nacosAddr, ":")
+			port, err := strconv.ParseUint(split[1], 10, 64)
+			if err != nil {
+				logger.Warnf("nacos addr port parse error ,error message is %v", err)
+				continue
+			}
+			svrconf := nacosconst.ServerConfig{
+				IpAddr: split[0],
+				Port:   port,
+			}
+			svrConfList = append(svrConfList, svrconf)
+		}
+
+		client, err := clients.CreateConfigClient(map[string]interface{}{
+			"serverConfigs": svrConfList,
+			"clientConfig": nacosconst.ClientConfig{
+				TimeoutMs:           uint64(int32(container.NacosClient().Timeout / time.Millisecond)),
+				ListenInterval:      10000,
+				NotLoadCacheAtStart: true,
+				LogDir:              logDir,
+			},
+		})
+
+		container.NacosClient().SetClient(&client)
+		if err != nil {
+			logger.Errorf("nacos create config client error:%v", err)
+		}
+	}
+
+	return perrors.WithMessagef(nil, "newNacosClient(address:%+v)", url.PrimitiveURL)
+}
+
+func newNacosClient(name string, nacosAddrs []string, timeout time.Duration) (*NacosClient, error) {
+	var (
+		err error
+		n   *NacosClient
+	)
+
+	n = &NacosClient{
+		name:       name,
+		NacosAddrs: nacosAddrs,
+		Timeout:    timeout,
+		exit:       make(chan struct{}),
+		onceClose: func() {
+			close(n.exit)
+		},
+	}
+
+	svrConfList := []nacosconst.ServerConfig{}
+	for _, nacosAddr := range n.NacosAddrs {
+		split := strings.Split(nacosAddr, ":")
+		port, err := strconv.ParseUint(split[1], 10, 64)
+		if err != nil {
+			logger.Warnf("convert port , source:%s , error:%v ", split[1], err)
+			continue
+		}
+		svrconf := nacosconst.ServerConfig{
+			IpAddr: split[0],
+			Port:   port,
+		}
+		svrConfList = append(svrConfList, svrconf)
+	}
+	client, err := clients.CreateConfigClient(map[string]interface{}{
+		"serverConfigs": svrConfList,
+		"clientConfig": nacosconst.ClientConfig{
+			TimeoutMs:           uint64(timeout / time.Millisecond),
+			ListenInterval:      20000,
+			NotLoadCacheAtStart: true,
+			LogDir:              logDir,
+		},
+	})
+	n.SetClient(&client)
+	if err != nil {
+		return nil, perrors.WithMessagef(err, "nacos clients.CreateConfigClient(nacosAddrs:%+v)", nacosAddrs)
+	}
+
+	return n, nil
+}
+
+// Done Get nacos client exit signal
+func (n *NacosClient) Done() <-chan struct{} {
+	return n.exit
+}
+
+func (n *NacosClient) stop() bool {
+	select {
+	case <-n.exit:
+		return true
+	default:
+		n.once.Do(n.onceClose)
+	}
+
+	return false
+}
+
+// NacosClientValid Get nacos client valid status
+func (n *NacosClient) NacosClientValid() bool {
+	select {
+	case <-n.exit:
+		return false
+	default:
+	}
+
+	valid := true
+	n.Lock()
+	if n.Client() == nil {
+		valid = false
+	}
+	n.Unlock()
+
+	return valid
+}
+
+// Close Close nacos client , then set null
+func (n *NacosClient) Close() {
+	if n == nil {
+		return
+	}
+
+	n.stop()
+	n.SetClient(nil)
+	logger.Warnf("nacosClient{name:%s, nacos addr:%s} exit now.", n.name, n.NacosAddrs)
+}
diff --git a/config_center/nacos/client_test.go b/config_center/nacos/client_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..ef63eeff6ddf4e5cd6fa2ba7da7996b3dbed94ac
--- /dev/null
+++ b/config_center/nacos/client_test.go
@@ -0,0 +1,55 @@
+/*
+ * 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 (
+	"strings"
+	"testing"
+	"time"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+import (
+	"github.com/apache/dubbo-go/common"
+)
+
+func Test_newNacosClient(t *testing.T) {
+	server := mockCommonNacosServer()
+	nacosURL := strings.ReplaceAll(server.URL, "http", "registry")
+	registryUrl, _ := common.NewURL(nacosURL)
+	c := &nacosDynamicConfiguration{
+		url:  &registryUrl,
+		done: make(chan struct{}),
+	}
+	err := ValidateNacosClient(c, WithNacosName(nacosClientName))
+	assert.NoError(t, err)
+	c.wg.Add(1)
+	go HandleClientRestart(c)
+	go func() {
+		// c.client.Close() and <-c.client.Done() have order requirements.
+		// If c.client.Close() is called first.It is possible that "go HandleClientRestart(c)"
+		// sets c.client to nil before calling c.client.Done().
+		time.Sleep(time.Second)
+		c.client.Close()
+	}()
+	<-c.client.Done()
+	c.Destroy()
+}
diff --git a/config_center/nacos/facade.go b/config_center/nacos/facade.go
new file mode 100644
index 0000000000000000000000000000000000000000..fc83e14eac7fcc51025b54f6daff2553f309312c
--- /dev/null
+++ b/config_center/nacos/facade.go
@@ -0,0 +1,97 @@
+/*
+ * 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 (
+	"sync"
+	"time"
+)
+import (
+	"github.com/dubbogo/getty"
+	perrors "github.com/pkg/errors"
+)
+
+import (
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/common/logger"
+)
+
+const (
+	connDelay    = 3
+	maxFailTimes = 15
+)
+
+type nacosClientFacade interface {
+	NacosClient() *NacosClient
+	SetNacosClient(*NacosClient)
+	// WaitGroup for wait group control, zk client listener & zk client container
+	WaitGroup() *sync.WaitGroup
+	// GetDone For nacos client control	RestartCallBack() bool
+	GetDone() chan struct{}
+	common.Node
+}
+
+func timeSecondDuration(sec int) time.Duration {
+	return time.Duration(sec) * time.Second
+}
+
+// HandleClientRestart Restart client handler
+func HandleClientRestart(r nacosClientFacade) {
+	var (
+		err error
+
+		failTimes int
+	)
+
+	defer r.WaitGroup().Done()
+LOOP:
+	for {
+		select {
+		case <-r.GetDone():
+			logger.Warnf("(NacosProviderRegistry)reconnectNacosRegistry goroutine exit now...")
+			break LOOP
+			// re-register all services
+		case <-r.NacosClient().Done():
+			r.NacosClient().Close()
+			nacosName := r.NacosClient().name
+			nacosAddress := r.NacosClient().NacosAddrs
+			r.SetNacosClient(nil)
+
+			// Connect nacos until success.
+			failTimes = 0
+			for {
+				select {
+				case <-r.GetDone():
+					logger.Warnf("(NacosProviderRegistry)reconnectZkRegistry goroutine exit now...")
+					break LOOP
+				case <-getty.GetTimeWheel().After(timeSecondDuration(failTimes * connDelay)): // Prevent crazy reconnection nacos.
+				}
+				err = ValidateNacosClient(r, WithNacosName(nacosName))
+				logger.Infof("NacosProviderRegistry.validateNacosClient(nacosAddr{%s}) = error{%#v}",
+					nacosAddress, perrors.WithStack(err))
+				if err == nil {
+					break
+				}
+				failTimes++
+				if maxFailTimes <= failTimes {
+					failTimes = maxFailTimes
+				}
+			}
+		}
+	}
+}
diff --git a/config_center/nacos/factory.go b/config_center/nacos/factory.go
new file mode 100644
index 0000000000000000000000000000000000000000..3de91ea013df0c6bef8d70c741ff840ba3b77572
--- /dev/null
+++ b/config_center/nacos/factory.go
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package nacos
+
+import (
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/common/extension"
+	"github.com/apache/dubbo-go/config_center"
+	"github.com/apache/dubbo-go/config_center/parser"
+)
+
+func init() {
+	extension.SetConfigCenterFactory("nacos", func() config_center.DynamicConfigurationFactory { return &nacosDynamicConfigurationFactory{} })
+}
+
+type nacosDynamicConfigurationFactory struct {
+}
+
+// GetDynamicConfiguration Get Configuration with URL
+func (f *nacosDynamicConfigurationFactory) GetDynamicConfiguration(url *common.URL) (config_center.DynamicConfiguration, error) {
+	dynamicConfiguration, err := newNacosDynamicConfiguration(url)
+	if err != nil {
+		return nil, err
+	}
+	dynamicConfiguration.SetParser(&parser.DefaultConfigurationParser{})
+	return dynamicConfiguration, err
+
+}
diff --git a/config_center/nacos/impl.go b/config_center/nacos/impl.go
new file mode 100644
index 0000000000000000000000000000000000000000..60ab89b003ff62016b9137223425c1051356975f
--- /dev/null
+++ b/config_center/nacos/impl.go
@@ -0,0 +1,166 @@
+/*
+ * 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 (
+	"sync"
+)
+
+import (
+	"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/logger"
+	"github.com/apache/dubbo-go/config_center"
+	"github.com/apache/dubbo-go/config_center/parser"
+)
+
+const nacosClientName = "nacos config_center"
+
+type nacosDynamicConfiguration struct {
+	url          *common.URL
+	rootPath     string
+	wg           sync.WaitGroup
+	cltLock      sync.Mutex
+	done         chan struct{}
+	client       *NacosClient
+	keyListeners sync.Map
+	parser       parser.ConfigurationParser
+}
+
+func newNacosDynamicConfiguration(url *common.URL) (*nacosDynamicConfiguration, error) {
+	c := &nacosDynamicConfiguration{
+		rootPath: "/" + url.GetParam(constant.CONFIG_NAMESPACE_KEY, config_center.DEFAULT_GROUP) + "/config",
+		url:      url,
+		done:     make(chan struct{}),
+	}
+	err := ValidateNacosClient(c, WithNacosName(nacosClientName))
+	if err != nil {
+		logger.Errorf("nacos client start error ,error message is %v", err)
+		return nil, err
+	}
+	c.wg.Add(1)
+	go HandleClientRestart(c)
+	return c, err
+
+}
+
+// AddListener Add listener
+func (n *nacosDynamicConfiguration) AddListener(key string, listener config_center.ConfigurationListener, opions ...config_center.Option) {
+	n.addListener(key, listener)
+}
+
+// RemoveListener Remove listener
+func (n *nacosDynamicConfiguration) RemoveListener(key string, listener config_center.ConfigurationListener, opions ...config_center.Option) {
+	n.removeListener(key, listener)
+}
+
+//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...)
+}
+
+// GetInternalProperty Get properties value by key
+func (n *nacosDynamicConfiguration) GetInternalProperty(key string, opts ...config_center.Option) (string, error) {
+	return n.GetProperties(key, opts...)
+}
+
+// GetRule Get router rule
+func (n *nacosDynamicConfiguration) GetRule(key string, opts ...config_center.Option) (string, error) {
+	tmpOpts := &config_center.Options{}
+	for _, opt := range opts {
+		opt(tmpOpts)
+	}
+	content, err := (*n.client.Client()).GetConfig(vo.ConfigParam{
+		DataId: key,
+		Group:  tmpOpts.Group,
+	})
+	if err != nil {
+		return "", perrors.WithStack(err)
+	} else {
+		return string(content), nil
+	}
+}
+
+// Parser Get Parser
+func (n *nacosDynamicConfiguration) Parser() parser.ConfigurationParser {
+	return n.parser
+}
+
+// SetParser Set Parser
+func (n *nacosDynamicConfiguration) SetParser(p parser.ConfigurationParser) {
+	n.parser = p
+}
+
+// NacosClient Get Nacos Client
+func (n *nacosDynamicConfiguration) NacosClient() *NacosClient {
+	return n.client
+}
+
+// SetNacosClient Set Nacos Client
+func (n *nacosDynamicConfiguration) SetNacosClient(client *NacosClient) {
+	n.cltLock.Lock()
+	n.client = client
+	n.cltLock.Unlock()
+}
+
+// WaitGroup for wait group control, zk client listener & zk client container
+func (n *nacosDynamicConfiguration) WaitGroup() *sync.WaitGroup {
+	return &n.wg
+}
+
+// GetDone For nacos client control	RestartCallBack() bool
+func (n *nacosDynamicConfiguration) GetDone() chan struct{} {
+	return n.done
+}
+
+// GetUrl Get Url
+func (n *nacosDynamicConfiguration) GetUrl() common.URL {
+	return *n.url
+}
+
+// Destroy Destroy configuration instance
+func (n *nacosDynamicConfiguration) Destroy() {
+	close(n.done)
+	n.wg.Wait()
+	n.closeConfigs()
+}
+
+// IsAvailable Get available status
+func (n *nacosDynamicConfiguration) IsAvailable() bool {
+	select {
+	case <-n.done:
+		return false
+	default:
+		return true
+	}
+}
+
+func (r *nacosDynamicConfiguration) closeConfigs() {
+	r.cltLock.Lock()
+	client := r.client
+	r.client = nil
+	r.cltLock.Unlock()
+	// Close the old client first to close the tmp node
+	client.Close()
+	logger.Infof("begin to close provider nacos client")
+}
diff --git a/config_center/nacos/impl_test.go b/config_center/nacos/impl_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..b4e6f1d0259979eba28dd81e8f480ab4ae03a39f
--- /dev/null
+++ b/config_center/nacos/impl_test.go
@@ -0,0 +1,118 @@
+/*
+ * 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 (
+	"fmt"
+	"net/http"
+	"net/http/httptest"
+	"strings"
+	"sync"
+	"testing"
+	"time"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+import (
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/config_center"
+	"github.com/apache/dubbo-go/config_center/parser"
+)
+
+// run mock config server
+func runMockConfigServer(configHandler func(http.ResponseWriter, *http.Request),
+	configListenHandler func(http.ResponseWriter, *http.Request)) *httptest.Server {
+	uriHandlerMap := make(map[string]func(http.ResponseWriter, *http.Request), 0)
+
+	uriHandlerMap["/nacos/v1/cs/configs"] = configHandler
+	uriHandlerMap["/nacos/v1/cs/configs/listener"] = configListenHandler
+
+	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		uri := r.RequestURI
+		for path, handler := range uriHandlerMap {
+			if uri == path {
+				handler(w, r)
+				break
+			}
+		}
+	}))
+
+	return ts
+}
+
+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
+`
+		fmt.Fprintf(writer, "%s", data)
+	}, func(writer http.ResponseWriter, request *http.Request) {
+		data := `dubbo.properties%02dubbo%02dubbo.service.com.ikurento.user.UserProvider.cluster=failback`
+		fmt.Fprintf(writer, "%s", data)
+	})
+}
+
+func initNacosData(t *testing.T) (*nacosDynamicConfiguration, error) {
+	server := mockCommonNacosServer()
+	nacosURL := strings.ReplaceAll(server.URL, "http", "registry")
+	regurl, _ := common.NewURL(nacosURL)
+	nacosConfiguration, err := newNacosDynamicConfiguration(&regurl)
+	assert.NoError(t, err)
+
+	nacosConfiguration.SetParser(&parser.DefaultConfigurationParser{})
+
+	return nacosConfiguration, err
+}
+
+func Test_GetConfig(t *testing.T) {
+	nacos, err := initNacosData(t)
+	assert.NoError(t, err)
+	configs, err := nacos.GetProperties("dubbo.properties", config_center.WithGroup("dubbo"))
+	_, err = nacos.Parser().Parse(configs)
+	assert.NoError(t, err)
+}
+
+func Test_AddListener(t *testing.T) {
+	nacos, err := initNacosData(t)
+	assert.NoError(t, err)
+	listener := &mockDataListener{}
+	time.Sleep(time.Second * 2)
+	nacos.AddListener("dubbo.properties", listener)
+	listener.wg.Add(1)
+	listener.wg.Wait()
+}
+
+func Test_RemoveListener(t *testing.T) {
+	//TODO not supported in current go_nacos_sdk version
+}
+
+type mockDataListener struct {
+	wg    sync.WaitGroup
+	event string
+}
+
+func (l *mockDataListener) Process(configType *config_center.ConfigChangeEvent) {
+	l.wg.Done()
+	l.event = configType.Key
+}
diff --git a/config_center/nacos/listener.go b/config_center/nacos/listener.go
new file mode 100644
index 0000000000000000000000000000000000000000..25c586586c7202e42ff44d6104e8132961add25a
--- /dev/null
+++ b/config_center/nacos/listener.go
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package nacos
+
+import (
+	"context"
+)
+
+import (
+	"github.com/nacos-group/nacos-sdk-go/vo"
+)
+
+import (
+	"github.com/apache/dubbo-go/common/logger"
+	"github.com/apache/dubbo-go/config_center"
+	"github.com/apache/dubbo-go/remoting"
+)
+
+func callback(listener config_center.ConfigurationListener, namespace, group, dataId, data string) {
+	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)
+	if !loaded {
+		_, cancel := context.WithCancel(context.Background())
+		err := (*l.client.Client()).ListenConfig(vo.ConfigParam{
+			DataId: key,
+			Group:  "dubbo",
+			OnChange: func(namespace, group, dataId, data string) {
+				go callback(listener, namespace, group, dataId, data)
+			},
+		})
+		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)
+	} 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) {
+	// TODO: not supported in current go_nacos_sdk version
+	logger.Warn("not supported in current go_nacos_sdk version")
+}
diff --git a/config_center/parser/configuration_parser.go b/config_center/parser/configuration_parser.go
index 1ce6594017ddc3cf76ba01c985b01a97e5ec91f3..f342dc62e765f8d38c9e64ba3be03f3362f0bf61 100644
--- a/config_center/parser/configuration_parser.go
+++ b/config_center/parser/configuration_parser.go
@@ -18,7 +18,6 @@
 package parser
 
 import (
-	"context"
 	"strconv"
 	"strings"
 )
@@ -36,18 +35,22 @@ import (
 )
 
 const (
+	// ScopeApplication ...
 	ScopeApplication = "application"
-	GeneralType      = "general"
+	// GeneralType ...
+	GeneralType = "general"
 )
 
+// ConfigurationParser ...
 type ConfigurationParser interface {
 	Parse(string) (map[string]string, error)
 	ParseToUrls(content string) ([]*common.URL, error)
 }
 
-//for support properties file in config center
+// DefaultConfigurationParser for support properties file in config center
 type DefaultConfigurationParser struct{}
 
+// ConfiguratorConfig ...
 type ConfiguratorConfig struct {
 	ConfigVersion string       `yaml:"configVersion"`
 	Scope         string       `yaml:"scope"`
@@ -56,6 +59,7 @@ type ConfiguratorConfig struct {
 	Configs       []ConfigItem `yaml:"configs"`
 }
 
+// ConfigItem ...
 type ConfigItem struct {
 	Type              string            `yaml:"type"`
 	Enabled           bool              `yaml:"enabled"`
@@ -67,15 +71,17 @@ type ConfigItem struct {
 	Side              string            `yaml:"side"`
 }
 
+// Parse ...
 func (parser *DefaultConfigurationParser) Parse(content string) (map[string]string, error) {
-	properties, err := properties.LoadString(content)
+	pps, err := properties.LoadString(content)
 	if err != nil {
 		logger.Errorf("Parse the content {%v} in DefaultConfigurationParser error ,error message is {%v}", content, err)
 		return nil, err
 	}
-	return properties.Map(), nil
+	return pps.Map(), nil
 }
 
+// ParseToUrls ...
 func (parser *DefaultConfigurationParser) ParseToUrls(content string) ([]*common.URL, error) {
 	config := ConfiguratorConfig{}
 	if err := yaml.Unmarshal([]byte(content), &config); err != nil {
@@ -103,6 +109,7 @@ func (parser *DefaultConfigurationParser) ParseToUrls(content string) ([]*common
 	}
 	return allUrls, nil
 }
+
 func serviceItemToUrls(item ConfigItem, config ConfiguratorConfig) ([]*common.URL, error) {
 	var addresses = item.Addresses
 	if len(addresses) == 0 {
@@ -132,22 +139,23 @@ func serviceItemToUrls(item ConfigItem, config ConfiguratorConfig) ([]*common.UR
 				newUrlStr := urlStr
 				newUrlStr = newUrlStr + "&application"
 				newUrlStr = newUrlStr + v
-				url, err := common.NewURL(context.Background(), newUrlStr)
+				url, err := common.NewURL(newUrlStr)
 				if err != nil {
-					perrors.WithStack(err)
+					return nil, perrors.WithStack(err)
 				}
 				urls = append(urls, &url)
 			}
 		} else {
-			url, err := common.NewURL(context.Background(), urlStr)
+			url, err := common.NewURL(urlStr)
 			if err != nil {
-				perrors.WithStack(err)
+				return nil, perrors.WithStack(err)
 			}
 			urls = append(urls, &url)
 		}
 	}
 	return urls, nil
 }
+
 func appItemToUrls(item ConfigItem, config ConfiguratorConfig) ([]*common.URL, error) {
 	var addresses = item.Addresses
 	if len(addresses) == 0 {
@@ -178,7 +186,7 @@ func appItemToUrls(item ConfigItem, config ConfiguratorConfig) ([]*common.URL, e
 			urlStr = urlStr + constant.APP_DYNAMIC_CONFIGURATORS_CATEGORY
 			urlStr = urlStr + "&configVersion="
 			urlStr = urlStr + config.ConfigVersion
-			url, err := common.NewURL(context.Background(), urlStr)
+			url, err := common.NewURL(urlStr)
 			if err != nil {
 				return nil, perrors.WithStack(err)
 			}
@@ -240,6 +248,7 @@ func getParamString(item ConfigItem) (string, error) {
 
 	return retStr, nil
 }
+
 func getEnabledString(item ConfigItem, config ConfiguratorConfig) string {
 	retStr := "&enabled="
 	if len(item.Type) == 0 || item.Type == GeneralType {
diff --git a/config_center/zookeeper/factory.go b/config_center/zookeeper/factory.go
index 611f4b9785c38eac4181750eefcabfb39607135d..3f4690d4e0edb4a859d76bf0fd692ca54e1a7a6a 100644
--- a/config_center/zookeeper/factory.go
+++ b/config_center/zookeeper/factory.go
@@ -17,10 +17,6 @@
 
 package zookeeper
 
-import (
-	"sync"
-)
-
 import (
 	"github.com/apache/dubbo-go/common"
 	"github.com/apache/dubbo-go/common/extension"
@@ -35,14 +31,8 @@ func init() {
 type zookeeperDynamicConfigurationFactory struct {
 }
 
-var once sync.Once
-var dynamicConfiguration *zookeeperDynamicConfiguration
-
 func (f *zookeeperDynamicConfigurationFactory) GetDynamicConfiguration(url *common.URL) (config_center.DynamicConfiguration, error) {
-	var err error
-	once.Do(func() {
-		dynamicConfiguration, err = newZookeeperDynamicConfiguration(url)
-	})
+	dynamicConfiguration, err := newZookeeperDynamicConfiguration(url)
 	if err != nil {
 		return nil, err
 	}
diff --git a/config_center/zookeeper/impl.go b/config_center/zookeeper/impl.go
index 84e4b54e237fabb5775bfd0dfeb7043f1794a7ae..404243d4751146d1edc9a61d51cbb81d73c2ffb1 100644
--- a/config_center/zookeeper/impl.go
+++ b/config_center/zookeeper/impl.go
@@ -24,8 +24,8 @@ import (
 )
 
 import (
+	"github.com/dubbogo/go-zookeeper/zk"
 	perrors "github.com/pkg/errors"
-	"github.com/samuel/go-zookeeper/zk"
 )
 
 import (
@@ -37,7 +37,11 @@ import (
 	"github.com/apache/dubbo-go/remoting/zookeeper"
 )
 
-const ZkClient = "zk config_center"
+const (
+	// ZkClient
+	//zookeeper client name
+	ZkClient = "zk config_center"
+)
 
 type zookeeperDynamicConfiguration struct {
 	url      *common.URL
@@ -109,7 +113,7 @@ func (c *zookeeperDynamicConfiguration) RemoveListener(key string, listener conf
 	c.cacheListener.RemoveListener(key, listener)
 }
 
-func (c *zookeeperDynamicConfiguration) GetConfig(key string, opts ...config_center.Option) (string, error) {
+func (c *zookeeperDynamicConfiguration) GetProperties(key string, opts ...config_center.Option) (string, error) {
 
 	tmpOpts := &config_center.Options{}
 	for _, opt := range opts {
@@ -134,75 +138,79 @@ func (c *zookeeperDynamicConfiguration) GetConfig(key string, opts ...config_cen
 	content, _, err := c.client.GetContent(c.rootPath + "/" + key)
 	if err != nil {
 		return "", perrors.WithStack(err)
-	} else {
-		return string(content), nil
 	}
 
+	return string(content), nil
 }
 
 //For zookeeper, getConfig and getConfigs have the same meaning.
-func (c *zookeeperDynamicConfiguration) GetConfigs(key string, opts ...config_center.Option) (string, error) {
-	return c.GetConfig(key, opts...)
+func (c *zookeeperDynamicConfiguration) GetInternalProperty(key string, opts ...config_center.Option) (string, error) {
+	return c.GetProperties(key, opts...)
+}
+
+func (c *zookeeperDynamicConfiguration) GetRule(key string, opts ...config_center.Option) (string, error) {
+	return c.GetProperties(key, opts...)
 }
 
 func (c *zookeeperDynamicConfiguration) Parser() parser.ConfigurationParser {
 	return c.parser
 }
+
 func (c *zookeeperDynamicConfiguration) SetParser(p parser.ConfigurationParser) {
 	c.parser = p
 }
 
-func (r *zookeeperDynamicConfiguration) ZkClient() *zookeeper.ZookeeperClient {
-	return r.client
+func (c *zookeeperDynamicConfiguration) ZkClient() *zookeeper.ZookeeperClient {
+	return c.client
 }
 
-func (r *zookeeperDynamicConfiguration) SetZkClient(client *zookeeper.ZookeeperClient) {
-	r.client = client
+func (c *zookeeperDynamicConfiguration) SetZkClient(client *zookeeper.ZookeeperClient) {
+	c.client = client
 }
 
-func (r *zookeeperDynamicConfiguration) ZkClientLock() *sync.Mutex {
-	return &r.cltLock
+func (c *zookeeperDynamicConfiguration) ZkClientLock() *sync.Mutex {
+	return &c.cltLock
 }
 
-func (r *zookeeperDynamicConfiguration) WaitGroup() *sync.WaitGroup {
-	return &r.wg
+func (c *zookeeperDynamicConfiguration) WaitGroup() *sync.WaitGroup {
+	return &c.wg
 }
 
-func (r *zookeeperDynamicConfiguration) GetDone() chan struct{} {
-	return r.done
+func (c *zookeeperDynamicConfiguration) Done() chan struct{} {
+	return c.done
 }
 
-func (r *zookeeperDynamicConfiguration) GetUrl() common.URL {
-	return *r.url
+func (c *zookeeperDynamicConfiguration) GetUrl() common.URL {
+	return *c.url
 }
 
-func (r *zookeeperDynamicConfiguration) Destroy() {
-	if r.listener != nil {
-		r.listener.Close()
+func (c *zookeeperDynamicConfiguration) Destroy() {
+	if c.listener != nil {
+		c.listener.Close()
 	}
-	close(r.done)
-	r.wg.Wait()
-	r.closeConfigs()
+	close(c.done)
+	c.wg.Wait()
+	c.closeConfigs()
 }
 
-func (r *zookeeperDynamicConfiguration) IsAvailable() bool {
+func (c *zookeeperDynamicConfiguration) IsAvailable() bool {
 	select {
-	case <-r.done:
+	case <-c.done:
 		return false
 	default:
 		return true
 	}
 }
 
-func (r *zookeeperDynamicConfiguration) closeConfigs() {
-	r.cltLock.Lock()
-	defer r.cltLock.Unlock()
+func (c *zookeeperDynamicConfiguration) closeConfigs() {
+	c.cltLock.Lock()
+	defer c.cltLock.Unlock()
 	logger.Infof("begin to close provider zk client")
 	// Close the old client first to close the tmp node
-	r.client.Close()
-	r.client = nil
+	c.client.Close()
+	c.client = nil
 }
 
-func (r *zookeeperDynamicConfiguration) RestartCallBack() bool {
+func (c *zookeeperDynamicConfiguration) RestartCallBack() bool {
 	return true
 }
diff --git a/config_center/zookeeper/impl_test.go b/config_center/zookeeper/impl_test.go
index 2f620457f75b7e35f713423e3841d0272cbd0730..22e15193cba1b533a2b1b965a44bf9665a6a4e5e 100644
--- a/config_center/zookeeper/impl_test.go
+++ b/config_center/zookeeper/impl_test.go
@@ -17,14 +17,13 @@
 package zookeeper
 
 import (
-	"context"
 	"fmt"
 	"sync"
 	"testing"
 )
 
 import (
-	"github.com/samuel/go-zookeeper/zk"
+	"github.com/dubbogo/go-zookeeper/zk"
 	"github.com/stretchr/testify/assert"
 )
 
@@ -35,7 +34,7 @@ import (
 )
 
 func initZkData(group string, t *testing.T) (*zk.TestCluster, *zookeeperDynamicConfiguration) {
-	regurl, _ := common.NewURL(context.TODO(), "registry://127.0.0.1:1111")
+	regurl, _ := common.NewURL("registry://127.0.0.1:1111")
 	ts, reg, err := newMockZookeeperDynamicConfiguration(&regurl)
 	reg.SetParser(&parser.DefaultConfigurationParser{})
 
@@ -78,10 +77,11 @@ func initZkData(group string, t *testing.T) (*zk.TestCluster, *zookeeperDynamicC
 
 	return ts, reg
 }
+
 func Test_GetConfig(t *testing.T) {
 	ts, reg := initZkData("dubbo", t)
 	defer ts.Stop()
-	configs, err := reg.GetConfig("dubbo.properties", config_center.WithGroup("dubbo"))
+	configs, err := reg.GetProperties("dubbo.properties", config_center.WithGroup("dubbo"))
 	assert.NoError(t, err)
 	m, err := reg.Parser().Parse(configs)
 	assert.NoError(t, err)
diff --git a/config_center/zookeeper/listener.go b/config_center/zookeeper/listener.go
index 7128b6f5a39e243840a1076f9fc506d94c7ed2ed..122dfaf4f268a706151de6acdaa78bb46e59f8fb 100644
--- a/config_center/zookeeper/listener.go
+++ b/config_center/zookeeper/listener.go
@@ -27,25 +27,30 @@ import (
 	"github.com/apache/dubbo-go/remoting"
 )
 
+// CacheListener ...
 type CacheListener struct {
 	keyListeners sync.Map
 	rootPath     string
 }
 
+// NewCacheListener ...
 func NewCacheListener(rootPath string) *CacheListener {
 	return &CacheListener{rootPath: rootPath}
 }
+
+// AddListener ...
 func (l *CacheListener) AddListener(key string, listener config_center.ConfigurationListener) {
 
 	// reference from https://stackoverflow.com/questions/34018908/golang-why-dont-we-have-a-set-datastructure
 	// make a map[your type]struct{} like set in java
-	listeners, loaded := l.keyListeners.LoadOrStore(key, map[config_center.ConfigurationListener]struct{}{listener: struct{}{}})
+	listeners, loaded := l.keyListeners.LoadOrStore(key, map[config_center.ConfigurationListener]struct{}{listener: {}})
 	if loaded {
 		listeners.(map[config_center.ConfigurationListener]struct{})[listener] = struct{}{}
 		l.keyListeners.Store(key, listeners)
 	}
 }
 
+// RemoveListener ...
 func (l *CacheListener) RemoveListener(key string, listener config_center.ConfigurationListener) {
 	listeners, loaded := l.keyListeners.Load(key)
 	if loaded {
@@ -53,6 +58,7 @@ func (l *CacheListener) RemoveListener(key string, listener config_center.Config
 	}
 }
 
+// DataChange ...
 func (l *CacheListener) DataChange(event remoting.Event) bool {
 	if event.Content == "" {
 		//meanings new node
diff --git a/contributing.md b/contributing.md
index b1265c2351789d4929d81556d72234806aed6afa..9ee2dae32fad6caaf9e19c5e98e8b99b61c26a51 100644
--- a/contributing.md
+++ b/contributing.md
@@ -28,4 +28,14 @@ The title format of the pull request `MUST` follow the following rules:
 
 ### 3.1 log
 
-> 1 when logging the function's input parameter, you should add '@' before input parameter name.
+>- 1 when logging the function's input parameter, you should add '@' before input parameter name.
+
+### 3.2 naming
+
+>- 1 do not use an underscore in package name, such as `filter_impl`.
+>- 2 do not use an underscore in constants, such as `DUBBO_PROTOCOL`. use 'DubboProtocol' instead.
+
+### 3.3 comment
+
+>- 1 there should be comment for every export func/var.
+>- 2 the comment should begin with function name/var name.
\ No newline at end of file
diff --git a/doc/pic/arch/dubbo-go-arch.png b/doc/pic/arch/dubbo-go-arch.png
new file mode 100644
index 0000000000000000000000000000000000000000..8f8f19957b2a8639470e5c59a676a22762cc9778
Binary files /dev/null and b/doc/pic/arch/dubbo-go-arch.png differ
diff --git a/filter/access_key.go b/filter/access_key.go
new file mode 100644
index 0000000000000000000000000000000000000000..40d4157b31d13ed8fd8b1ba8cc9d16b53638ac6a
--- /dev/null
+++ b/filter/access_key.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 filter
+
+import (
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/protocol"
+)
+
+type AccessKeyPair struct {
+	AccessKey    string `yaml:"accessKey"   json:"accessKey,omitempty" property:"accessKey"`
+	SecretKey    string `yaml:"secretKey"   json:"secretKey,omitempty" property:"secretKey"`
+	ConsumerSide string `yaml:"consumerSide"   json:"ConsumerSide,consumerSide" property:"consumerSide"`
+	ProviderSide string `yaml:"providerSide"   json:"providerSide,omitempty" property:"providerSide"`
+	Creator      string `yaml:"creator"   json:"creator,omitempty" property:"creator"`
+	Options      string `yaml:"options"   json:"options,omitempty" property:"options"`
+}
+
+// AccessKeyStorage
+// This SPI Extension support us to store our AccessKeyPair or load AccessKeyPair from other
+// storage, such as filesystem.
+type AccessKeyStorage interface {
+	GetAccessKeyPair(protocol.Invocation, *common.URL) *AccessKeyPair
+}
diff --git a/cluster/router.go b/filter/authenticator.go
similarity index 74%
rename from cluster/router.go
rename to filter/authenticator.go
index 54a19695574f245fcac236e9308a2469f306a4f8..ac2c8601d4a0d2e5ae3aed56415d9d23856cb502 100644
--- a/cluster/router.go
+++ b/filter/authenticator.go
@@ -15,27 +15,21 @@
  * limitations under the License.
  */
 
-package cluster
+package filter
 
 import (
 	"github.com/apache/dubbo-go/common"
 	"github.com/apache/dubbo-go/protocol"
 )
 
-// Extension - Router
+// Authenticator
+type Authenticator interface {
 
-type RouterFactory interface {
-	Router(*common.URL) (Router, error)
-}
-
-type Router interface {
-	Route([]protocol.Invoker, common.URL, protocol.Invocation) []protocol.Invoker
-}
-
-type RouterChain struct {
-	routers []Router
-}
-
-func NewRouterChain(url common.URL) {
+	// Sign
+	// give a sign to request
+	Sign(protocol.Invocation, *common.URL) error
 
+	// Authenticate
+	// verify the signature of the request is valid or not
+	Authenticate(protocol.Invocation, *common.URL) error
 }
diff --git a/filter/filter.go b/filter/filter.go
index 5bd78998a76a1b0e8af99b0b3f0d7e6c103bb794..c069510498c7ac68b2bb2169dfe7132a4ef63229 100644
--- a/filter/filter.go
+++ b/filter/filter.go
@@ -17,12 +17,16 @@
 
 package filter
 
+import (
+	"context"
+)
 import (
 	"github.com/apache/dubbo-go/protocol"
 )
 
+// Filter
 // Extension - Filter
 type Filter interface {
-	Invoke(protocol.Invoker, protocol.Invocation) protocol.Result
-	OnResponse(protocol.Result, protocol.Invoker, protocol.Invocation) protocol.Result
+	Invoke(context.Context, protocol.Invoker, protocol.Invocation) protocol.Result
+	OnResponse(context.Context, protocol.Result, protocol.Invoker, protocol.Invocation) protocol.Result
 }
diff --git a/filter/impl/access_log_filter.go b/filter/filter_impl/access_log_filter.go
similarity index 89%
rename from filter/impl/access_log_filter.go
rename to filter/filter_impl/access_log_filter.go
index 89fa34952f99057f1d8bb35794a57f9905f5f169..fbfe7565170c7df468f755a4bd1aadde166a79c1 100644
--- a/filter/impl/access_log_filter.go
+++ b/filter/filter_impl/access_log_filter.go
@@ -15,9 +15,10 @@
  * limitations under the License.
  */
 
-package impl
+package filter_impl
 
 import (
+	"context"
 	"os"
 	"reflect"
 	"strings"
@@ -34,13 +35,21 @@ import (
 
 const (
 	//used in URL.
-	FileDateFormat    = "2006-01-02"
+
+	// FileDateFormat ...
+	FileDateFormat = "2006-01-02"
+	// MessageDateLayout ...
 	MessageDateLayout = "2006-01-02 15:04:05"
-	LogMaxBuffer      = 5000
-	LogFileMode       = 0600
+	// LogMaxBuffer ...
+	LogMaxBuffer = 5000
+	// LogFileMode ...
+	LogFileMode = 0600
 
 	// those fields are the data collected by this filter
-	Types     = "types"
+
+	// Types ...
+	Types = "types"
+	// Arguments ...
 	Arguments = "arguments"
 )
 
@@ -49,6 +58,7 @@ func init() {
 }
 
 /*
+ * AccessLogFilter
  * Although the access log filter is a default filter,
  * you should config "accesslog" in service's config to tell the filter where store the access log.
  * for example:
@@ -66,13 +76,14 @@ type AccessLogFilter struct {
 	logChan chan AccessLogData
 }
 
-func (ef *AccessLogFilter) Invoke(invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
+// Invoke ...
+func (ef *AccessLogFilter) Invoke(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
 	accessLog := invoker.GetUrl().GetParam(constant.ACCESS_LOG_KEY, "")
 	if len(accessLog) > 0 {
 		accessLogData := AccessLogData{data: ef.buildAccessLogData(invoker, invocation), accessLog: accessLog}
 		ef.logIntoChannel(accessLogData)
 	}
-	return invoker.Invoke(invocation)
+	return invoker.Invoke(ctx, invocation)
 }
 
 // it won't block the invocation
@@ -86,7 +97,7 @@ func (ef *AccessLogFilter) logIntoChannel(accessLogData AccessLogData) {
 	}
 }
 
-func (ef *AccessLogFilter) buildAccessLogData(invoker protocol.Invoker, invocation protocol.Invocation) map[string]string {
+func (ef *AccessLogFilter) buildAccessLogData(_ protocol.Invoker, invocation protocol.Invocation) map[string]string {
 	dataMap := make(map[string]string, 16)
 	attachments := invocation.Attachments()
 	dataMap[constant.INTERFACE_KEY] = attachments[constant.INTERFACE_KEY]
@@ -119,7 +130,8 @@ func (ef *AccessLogFilter) buildAccessLogData(invoker protocol.Invoker, invocati
 	return dataMap
 }
 
-func (ef *AccessLogFilter) OnResponse(result protocol.Result, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
+// OnResponse ...
+func (ef *AccessLogFilter) OnResponse(_ context.Context, result protocol.Result, _ protocol.Invoker, _ protocol.Invocation) protocol.Result {
 	return result
 }
 
@@ -172,6 +184,7 @@ func isDefault(accessLog string) bool {
 	return strings.EqualFold("true", accessLog) || strings.EqualFold("default", accessLog)
 }
 
+// GetAccessLogFilter ...
 func GetAccessLogFilter() filter.Filter {
 	accessLogFilter := &AccessLogFilter{logChan: make(chan AccessLogData, LogMaxBuffer)}
 	go func() {
@@ -182,6 +195,7 @@ func GetAccessLogFilter() filter.Filter {
 	return accessLogFilter
 }
 
+// AccessLogData ...
 type AccessLogData struct {
 	accessLog string
 	data      map[string]string
diff --git a/filter/impl/access_log_filter_test.go b/filter/filter_impl/access_log_filter_test.go
similarity index 84%
rename from filter/impl/access_log_filter_test.go
rename to filter/filter_impl/access_log_filter_test.go
index 834d531f05f952c41abfe8e1c56c20c0285926b8..f0de24d2a89f35876a32763eeb75495e8919ecd9 100644
--- a/filter/impl/access_log_filter_test.go
+++ b/filter/filter_impl/access_log_filter_test.go
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package impl
+package filter_impl
 
 import (
 	"context"
@@ -37,11 +37,11 @@ import (
 func TestAccessLogFilter_Invoke_Not_Config(t *testing.T) {
 	ctrl := gomock.NewController(t)
 	defer ctrl.Finish()
-	url, _ := common.NewURL(context.Background(),
-		"dubbo://:20000/UserProvider?app.version=0.0.1&application=BDTService&bean.name=UserProvider"+
-			"&cluster=failover&environment=dev&group=&interface=com.ikurento.user.UserProvider&loadbalance=random&methods.GetUser."+
-			"loadbalance=random&methods.GetUser.retries=1&methods.GetUser.weight=0&module=dubbogo+user-info+server&name="+
-			"BDTService&organization=ikurento.com&owner=ZX&registry.role=3&retries=&"+
+	url, _ := common.NewURL(
+		"dubbo://:20000/UserProvider?app.version=0.0.1&application=BDTService&bean.name=UserProvider" +
+			"&cluster=failover&environment=dev&group=&interface=com.ikurento.user.UserProvider&loadbalance=random&methods.GetUser." +
+			"loadbalance=random&methods.GetUser.retries=1&methods.GetUser.weight=0&module=dubbogo+user-info+server&name=" +
+			"BDTService&organization=ikurento.com&owner=ZX&registry.role=3&retries=&" +
 			"service.filter=echo%2Ctoken%2Caccesslog&timestamp=1569153406&token=934804bf-b007-4174-94eb-96e3e1d60cc7&version=&warmup=100")
 	invoker := protocol.NewBaseInvoker(url)
 
@@ -49,18 +49,18 @@ func TestAccessLogFilter_Invoke_Not_Config(t *testing.T) {
 	inv := invocation.NewRPCInvocation("MethodName", []interface{}{"OK", "Hello"}, attach)
 
 	accessLogFilter := GetAccessLogFilter()
-	result := accessLogFilter.Invoke(invoker, inv)
+	result := accessLogFilter.Invoke(context.Background(), invoker, inv)
 	assert.Nil(t, result.Error())
 }
 
 func TestAccessLogFilter_Invoke_Default_Config(t *testing.T) {
 	ctrl := gomock.NewController(t)
 	defer ctrl.Finish()
-	url, _ := common.NewURL(context.Background(),
-		"dubbo://:20000/UserProvider?app.version=0.0.1&application=BDTService&bean.name=UserProvider"+
-			"&cluster=failover&accesslog=true&environment=dev&group=&interface=com.ikurento.user.UserProvider&loadbalance=random&methods.GetUser."+
-			"loadbalance=random&methods.GetUser.retries=1&methods.GetUser.weight=0&module=dubbogo+user-info+server&name="+
-			"BDTService&organization=ikurento.com&owner=ZX&registry.role=3&retries=&"+
+	url, _ := common.NewURL(
+		"dubbo://:20000/UserProvider?app.version=0.0.1&application=BDTService&bean.name=UserProvider" +
+			"&cluster=failover&accesslog=true&environment=dev&group=&interface=com.ikurento.user.UserProvider&loadbalance=random&methods.GetUser." +
+			"loadbalance=random&methods.GetUser.retries=1&methods.GetUser.weight=0&module=dubbogo+user-info+server&name=" +
+			"BDTService&organization=ikurento.com&owner=ZX&registry.role=3&retries=&" +
 			"service.filter=echo%2Ctoken%2Caccesslog&timestamp=1569153406&token=934804bf-b007-4174-94eb-96e3e1d60cc7&version=&warmup=100")
 	invoker := protocol.NewBaseInvoker(url)
 
@@ -70,13 +70,13 @@ func TestAccessLogFilter_Invoke_Default_Config(t *testing.T) {
 	inv := invocation.NewRPCInvocation("MethodName", []interface{}{"OK", "Hello"}, attach)
 
 	accessLogFilter := GetAccessLogFilter()
-	result := accessLogFilter.Invoke(invoker, inv)
+	result := accessLogFilter.Invoke(context.Background(), invoker, inv)
 	assert.Nil(t, result.Error())
 }
 
 func TestAccessLogFilter_OnResponse(t *testing.T) {
 	result := &protocol.RPCResult{}
 	accessLogFilter := GetAccessLogFilter()
-	response := accessLogFilter.OnResponse(result, nil, nil)
+	response := accessLogFilter.OnResponse(nil, result, nil, nil)
 	assert.Equal(t, result, response)
 }
diff --git a/filter/filter_impl/active_filter.go b/filter/filter_impl/active_filter.go
new file mode 100644
index 0000000000000000000000000000000000000000..23f2c8e25609dff89392107251715fe6f5175f09
--- /dev/null
+++ b/filter/filter_impl/active_filter.go
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package filter_impl
+
+import (
+	"context"
+	"strconv"
+)
+
+import (
+	"github.com/apache/dubbo-go/common/extension"
+	"github.com/apache/dubbo-go/common/logger"
+	"github.com/apache/dubbo-go/filter"
+	"github.com/apache/dubbo-go/protocol"
+	invocation2 "github.com/apache/dubbo-go/protocol/invocation"
+)
+
+const (
+	active               = "active"
+	dubboInvokeStartTime = "dubboInvokeStartTime"
+)
+
+func init() {
+	extension.SetFilter(active, GetActiveFilter)
+}
+
+// ActiveFilter ...
+type ActiveFilter struct {
+}
+
+// Invoke ...
+func (ef *ActiveFilter) Invoke(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
+	logger.Infof("invoking active filter. %v,%v", invocation.MethodName(), len(invocation.Arguments()))
+	invocation.(*invocation2.RPCInvocation).SetAttachments(dubboInvokeStartTime, strconv.FormatInt(protocol.CurrentTimeMillis(), 10))
+	protocol.BeginCount(invoker.GetUrl(), invocation.MethodName())
+	return invoker.Invoke(ctx, invocation)
+}
+
+// OnResponse ...
+func (ef *ActiveFilter) OnResponse(ctx context.Context, result protocol.Result, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
+	startTime, err := strconv.ParseInt(invocation.(*invocation2.RPCInvocation).AttachmentsByKey(dubboInvokeStartTime, "0"), 10, 64)
+	if err != nil {
+		result.SetError(err)
+		logger.Errorf("parse dubbo_invoke_start_time to int64 failed")
+		return result
+	}
+	elapsed := protocol.CurrentTimeMillis() - startTime
+	protocol.EndCount(invoker.GetUrl(), invocation.MethodName(), elapsed, result.Error() == nil)
+	return result
+}
+
+// GetActiveFilter ...
+func GetActiveFilter() filter.Filter {
+	return &ActiveFilter{}
+}
diff --git a/filter/filter_impl/active_filter_test.go b/filter/filter_impl/active_filter_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..8917e9141cad4f22ea201a9a07c2873b584c1f92
--- /dev/null
+++ b/filter/filter_impl/active_filter_test.go
@@ -0,0 +1,66 @@
+package filter_impl
+
+import (
+	"context"
+	"errors"
+	"strconv"
+	"testing"
+)
+
+import (
+	"github.com/golang/mock/gomock"
+	"github.com/stretchr/testify/assert"
+)
+
+import (
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/protocol"
+	"github.com/apache/dubbo-go/protocol/invocation"
+	"github.com/apache/dubbo-go/protocol/mock"
+)
+
+func TestActiveFilter_Invoke(t *testing.T) {
+	invoc := invocation.NewRPCInvocation("test", []interface{}{"OK"}, make(map[string]string, 0))
+	url, _ := common.NewURL("dubbo://192.168.10.10:20000/com.ikurento.user.UserProvider")
+	filter := ActiveFilter{}
+	ctrl := gomock.NewController(t)
+	defer ctrl.Finish()
+	invoker := mock.NewMockInvoker(ctrl)
+	invoker.EXPECT().Invoke(gomock.Any()).Return(nil)
+	invoker.EXPECT().GetUrl().Return(url).Times(1)
+	filter.Invoke(context.Background(), invoker, invoc)
+	assert.True(t, invoc.AttachmentsByKey(dubboInvokeStartTime, "") != "")
+
+}
+
+func TestActiveFilter_OnResponse(t *testing.T) {
+	c := protocol.CurrentTimeMillis()
+	elapsed := 100
+	invoc := invocation.NewRPCInvocation("test", []interface{}{"OK"}, map[string]string{
+		dubboInvokeStartTime: strconv.FormatInt(c-int64(elapsed), 10),
+	})
+	url, _ := common.NewURL("dubbo://192.168.10.10:20000/com.ikurento.user.UserProvider")
+	filter := ActiveFilter{}
+	ctrl := gomock.NewController(t)
+	defer ctrl.Finish()
+	invoker := mock.NewMockInvoker(ctrl)
+	invoker.EXPECT().GetUrl().Return(url).Times(1)
+	result := &protocol.RPCResult{
+		Err: errors.New("test"),
+	}
+	filter.OnResponse(nil, result, invoker, invoc)
+	methodStatus := protocol.GetMethodStatus(url, "test")
+	urlStatus := protocol.GetURLStatus(url)
+
+	assert.Equal(t, int32(1), methodStatus.GetTotal())
+	assert.Equal(t, int32(1), urlStatus.GetTotal())
+	assert.Equal(t, int32(1), methodStatus.GetFailed())
+	assert.Equal(t, int32(1), urlStatus.GetFailed())
+	assert.Equal(t, int32(1), methodStatus.GetSuccessiveRequestFailureCount())
+	assert.Equal(t, int32(1), urlStatus.GetSuccessiveRequestFailureCount())
+	assert.True(t, methodStatus.GetFailedElapsed() >= int64(elapsed))
+	assert.True(t, urlStatus.GetFailedElapsed() >= int64(elapsed))
+	assert.True(t, urlStatus.GetLastRequestFailedTimestamp() != int64(0))
+	assert.True(t, methodStatus.GetLastRequestFailedTimestamp() != int64(0))
+
+}
diff --git a/filter/impl/active_filter.go b/filter/filter_impl/auth/accesskey_storage.go
similarity index 55%
rename from filter/impl/active_filter.go
rename to filter/filter_impl/auth/accesskey_storage.go
index 36a4e1a767ab7170ce8e5bebf2cfa4403f6ad4ff..5adb9d9ee37329228d1d02dc8802deeede68d327 100644
--- a/filter/impl/active_filter.go
+++ b/filter/filter_impl/auth/accesskey_storage.go
@@ -15,37 +15,34 @@
  * limitations under the License.
  */
 
-package impl
+package auth
 
 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/filter"
 	"github.com/apache/dubbo-go/protocol"
 )
 
-const active = "active"
-
-func init() {
-	extension.SetFilter(active, GetActiveFilter)
+// DefaultAccesskeyStorage
+// The default implementation of AccesskeyStorage
+type DefaultAccesskeyStorage struct {
 }
 
-type ActiveFilter struct {
+// GetAccessKeyPair
+// get AccessKeyPair from url by the key "accessKeyId" and "secretAccessKey"
+func (storage *DefaultAccesskeyStorage) GetAccessKeyPair(invocation protocol.Invocation, url *common.URL) *filter.AccessKeyPair {
+	return &filter.AccessKeyPair{
+		AccessKey: url.GetParam(constant.ACCESS_KEY_ID_KEY, ""),
+		SecretKey: url.GetParam(constant.SECRET_ACCESS_KEY_KEY, ""),
+	}
 }
 
-func (ef *ActiveFilter) Invoke(invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
-	logger.Infof("invoking active filter. %v,%v", invocation.MethodName(), len(invocation.Arguments()))
-
-	protocol.BeginCount(invoker.GetUrl(), invocation.MethodName())
-	return invoker.Invoke(invocation)
-}
-
-func (ef *ActiveFilter) OnResponse(result protocol.Result, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
-
-	protocol.EndCount(invoker.GetUrl(), invocation.MethodName())
-	return result
+func init() {
+	extension.SetAccesskeyStorages(constant.DEFAULT_ACCESS_KEY_STORAGE, GetDefaultAccesskeyStorage)
 }
 
-func GetActiveFilter() filter.Filter {
-	return &ActiveFilter{}
+func GetDefaultAccesskeyStorage() filter.AccessKeyStorage {
+	return &DefaultAccesskeyStorage{}
 }
diff --git a/filter/filter_impl/auth/accesskey_storage_test.go b/filter/filter_impl/auth/accesskey_storage_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..aa566b81761d1f51a5b9141f8f10e66844f272bb
--- /dev/null
+++ b/filter/filter_impl/auth/accesskey_storage_test.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 auth
+
+import (
+	"net/url"
+	"testing"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+import (
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/common/constant"
+	invocation2 "github.com/apache/dubbo-go/protocol/invocation"
+)
+
+func TestDefaultAccesskeyStorage_GetAccesskeyPair(t *testing.T) {
+	url := common.NewURLWithOptions(
+		common.WithParams(url.Values{}),
+		common.WithParamsValue(constant.SECRET_ACCESS_KEY_KEY, "skey"),
+		common.WithParamsValue(constant.ACCESS_KEY_ID_KEY, "akey"))
+	invocation := &invocation2.RPCInvocation{}
+	storage := GetDefaultAccesskeyStorage()
+	accesskeyPair := storage.GetAccessKeyPair(invocation, url)
+	assert.Equal(t, "skey", accesskeyPair.SecretKey)
+	assert.Equal(t, "akey", accesskeyPair.AccessKey)
+}
diff --git a/filter/filter_impl/auth/consumer_sign.go b/filter/filter_impl/auth/consumer_sign.go
new file mode 100644
index 0000000000000000000000000000000000000000..062744771acf8ccd505265875a103d24afeb06af
--- /dev/null
+++ b/filter/filter_impl/auth/consumer_sign.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 auth
+
+import (
+	"context"
+	"fmt"
+)
+import (
+	"github.com/apache/dubbo-go/common/constant"
+	"github.com/apache/dubbo-go/common/extension"
+	"github.com/apache/dubbo-go/common/logger"
+	"github.com/apache/dubbo-go/filter"
+	"github.com/apache/dubbo-go/protocol"
+)
+
+// ConsumerSignFilter
+// This filter is working for signing the request on consumer side
+type ConsumerSignFilter struct {
+}
+
+func init() {
+	extension.SetFilter(constant.CONSUMER_SIGN_FILTER, getConsumerSignFilter)
+}
+
+func (csf *ConsumerSignFilter) Invoke(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
+	logger.Infof("invoking ConsumerSign filter.")
+	url := invoker.GetUrl()
+
+	err := doAuthWork(&url, func(authenticator filter.Authenticator) error {
+		return authenticator.Sign(invocation, &url)
+	})
+	if err != nil {
+		panic(fmt.Sprintf("Sign for invocation %s # %s failed", url.ServiceKey(), invocation.MethodName()))
+
+	}
+	return invoker.Invoke(ctx, invocation)
+}
+
+func (csf *ConsumerSignFilter) OnResponse(ctx context.Context, result protocol.Result, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
+	return result
+}
+
+func getConsumerSignFilter() filter.Filter {
+	return &ConsumerSignFilter{}
+}
diff --git a/filter/filter_impl/auth/consumer_sign_test.go b/filter/filter_impl/auth/consumer_sign_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..b02380e28f51356efae385a2e20a6b1ee4e9aa5c
--- /dev/null
+++ b/filter/filter_impl/auth/consumer_sign_test.go
@@ -0,0 +1,54 @@
+/*
+ * 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 auth
+
+import (
+	"context"
+	"testing"
+)
+
+import (
+	"github.com/golang/mock/gomock"
+	"github.com/stretchr/testify/assert"
+)
+
+import (
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/common/constant"
+	"github.com/apache/dubbo-go/protocol"
+	"github.com/apache/dubbo-go/protocol/invocation"
+	"github.com/apache/dubbo-go/protocol/mock"
+)
+
+func TestConsumerSignFilter_Invoke(t *testing.T) {
+	url, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=gg&version=2.6.0")
+	url.SetParam(constant.SECRET_ACCESS_KEY_KEY, "sk")
+	url.SetParam(constant.ACCESS_KEY_ID_KEY, "ak")
+	inv := invocation.NewRPCInvocation("test", []interface{}{"OK"}, nil)
+	filter := &ConsumerSignFilter{}
+	ctrl := gomock.NewController(t)
+	defer ctrl.Finish()
+	invoker := mock.NewMockInvoker(ctrl)
+	result := &protocol.RPCResult{}
+	invoker.EXPECT().Invoke(inv).Return(result).Times(2)
+	invoker.EXPECT().GetUrl().Return(url).Times(2)
+	assert.Equal(t, result, filter.Invoke(context.Background(), invoker, inv))
+
+	url.SetParam(constant.SERVICE_AUTH_KEY, "true")
+	assert.Equal(t, result, filter.Invoke(context.Background(), invoker, inv))
+}
diff --git a/filter/filter_impl/auth/default_authenticator.go b/filter/filter_impl/auth/default_authenticator.go
new file mode 100644
index 0000000000000000000000000000000000000000..2b8d55927807407f350ecc6cfc28b6913a6d1a81
--- /dev/null
+++ b/filter/filter_impl/auth/default_authenticator.go
@@ -0,0 +1,137 @@
+/*
+ * 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 auth
+
+import (
+	"errors"
+	"fmt"
+	"github.com/apache/dubbo-go/filter"
+	"strconv"
+	"time"
+)
+
+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/protocol"
+	invocation_impl "github.com/apache/dubbo-go/protocol/invocation"
+)
+
+func init() {
+	extension.SetAuthenticator(constant.DEFAULT_AUTHENTICATOR, GetDefaultAuthenticator)
+}
+
+// DefaultAuthenticator
+// The default implemetation of Authenticator
+type DefaultAuthenticator struct {
+}
+
+// Sign
+// add the signature for the invocation
+func (authenticator *DefaultAuthenticator) Sign(invocation protocol.Invocation, url *common.URL) error {
+	currentTimeMillis := strconv.Itoa(int(time.Now().Unix() * 1000))
+
+	consumer := url.GetParam(constant.APPLICATION_KEY, "")
+	accessKeyPair, err := getAccessKeyPair(invocation, url)
+	if err != nil {
+		return errors.New("get accesskey pair failed, cause: " + err.Error())
+	}
+	inv := invocation.(*invocation_impl.RPCInvocation)
+	signature, err := getSignature(url, invocation, accessKeyPair.SecretKey, currentTimeMillis)
+	if err != nil {
+		return err
+	}
+	inv.SetAttachments(constant.REQUEST_SIGNATURE_KEY, signature)
+	inv.SetAttachments(constant.REQUEST_TIMESTAMP_KEY, currentTimeMillis)
+	inv.SetAttachments(constant.AK_KEY, accessKeyPair.AccessKey)
+	inv.SetAttachments(constant.CONSUMER, consumer)
+	return nil
+}
+
+// getSignature
+// get signature by the metadata and params of the invocation
+func getSignature(url *common.URL, invocation protocol.Invocation, secrectKey string, currentTime string) (string, error) {
+
+	requestString := fmt.Sprintf(constant.SIGNATURE_STRING_FORMAT,
+		url.ColonSeparatedKey(), invocation.MethodName(), secrectKey, currentTime)
+	var signature string
+	if parameterEncrypt := url.GetParamBool(constant.PARAMTER_SIGNATURE_ENABLE_KEY, false); parameterEncrypt {
+		var err error
+		if signature, err = SignWithParams(invocation.Arguments(), requestString, secrectKey); err != nil {
+			// TODO
+			return "", errors.New("sign the request with params failed, cause:" + err.Error())
+		}
+	} else {
+		signature = Sign(requestString, secrectKey)
+	}
+
+	return signature, nil
+}
+
+// Authenticate
+// This method verifies whether the signature sent by the requester is correct
+func (authenticator *DefaultAuthenticator) Authenticate(invocation protocol.Invocation, url *common.URL) error {
+	accessKeyId := invocation.AttachmentsByKey(constant.AK_KEY, "")
+
+	requestTimestamp := invocation.AttachmentsByKey(constant.REQUEST_TIMESTAMP_KEY, "")
+	originSignature := invocation.AttachmentsByKey(constant.REQUEST_SIGNATURE_KEY, "")
+	consumer := invocation.AttachmentsByKey(constant.CONSUMER, "")
+	if IsEmpty(accessKeyId, false) || IsEmpty(consumer, false) ||
+		IsEmpty(requestTimestamp, false) || IsEmpty(originSignature, false) {
+		return errors.New("failed to authenticate your ak/sk, maybe the consumer has not enabled the auth")
+	}
+
+	accessKeyPair, err := getAccessKeyPair(invocation, url)
+	if err != nil {
+		return errors.New("failed to authenticate , can't load the accessKeyPair")
+	}
+
+	computeSignature, err := getSignature(url, invocation, accessKeyPair.SecretKey, requestTimestamp)
+	if err != nil {
+		return err
+	}
+	if success := computeSignature == originSignature; !success {
+		return errors.New("failed to authenticate, signature is not correct")
+	}
+	return nil
+}
+
+func getAccessKeyPair(invocation protocol.Invocation, url *common.URL) (*filter.AccessKeyPair, error) {
+	accesskeyStorage := extension.GetAccesskeyStorages(url.GetParam(constant.ACCESS_KEY_STORAGE_KEY, constant.DEFAULT_ACCESS_KEY_STORAGE))
+	accessKeyPair := accesskeyStorage.GetAccessKeyPair(invocation, url)
+	if accessKeyPair == nil || IsEmpty(accessKeyPair.AccessKey, false) || IsEmpty(accessKeyPair.SecretKey, true) {
+		return nil, errors.New("accessKeyId or secretAccessKey not found")
+	} else {
+		return accessKeyPair, nil
+	}
+}
+
+func GetDefaultAuthenticator() filter.Authenticator {
+	return &DefaultAuthenticator{}
+}
+
+func doAuthWork(url *common.URL, do func(filter.Authenticator) error) error {
+
+	shouldAuth := url.GetParamBool(constant.SERVICE_AUTH_KEY, false)
+	if shouldAuth {
+		authenticator := extension.GetAuthenticator(url.GetParam(constant.AUTHENTICATOR_KEY, constant.DEFAULT_AUTHENTICATOR))
+		return do(authenticator)
+	}
+	return nil
+}
diff --git a/filter/filter_impl/auth/default_authenticator_test.go b/filter/filter_impl/auth/default_authenticator_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..5b107b5960ff5adc383d52aa5e393d9fc6e71d14
--- /dev/null
+++ b/filter/filter_impl/auth/default_authenticator_test.go
@@ -0,0 +1,147 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package auth
+
+import (
+	"fmt"
+	"net/url"
+	"strconv"
+	"testing"
+	"time"
+)
+
+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/protocol/invocation"
+)
+
+func TestDefaultAuthenticator_Authenticate(t *testing.T) {
+	secret := "dubbo-sk"
+	access := "dubbo-ak"
+	testurl, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=gg&version=2.6.0")
+	testurl.SetParam(constant.PARAMTER_SIGNATURE_ENABLE_KEY, "true")
+	testurl.SetParam(constant.ACCESS_KEY_ID_KEY, access)
+	testurl.SetParam(constant.SECRET_ACCESS_KEY_KEY, secret)
+	parmas := []interface{}{"OK", struct {
+		Name string
+		Id   int64
+	}{"YUYU", 1}}
+	inv := invocation.NewRPCInvocation("test", parmas, nil)
+	requestTime := strconv.Itoa(int(time.Now().Unix() * 1000))
+	signature, _ := getSignature(&testurl, inv, secret, requestTime)
+
+	var authenticator = &DefaultAuthenticator{}
+
+	invcation := invocation.NewRPCInvocation("test", parmas, map[string]string{
+		constant.REQUEST_SIGNATURE_KEY: signature,
+		constant.CONSUMER:              "test",
+		constant.REQUEST_TIMESTAMP_KEY: requestTime,
+		constant.AK_KEY:                access,
+	})
+	err := authenticator.Authenticate(invcation, &testurl)
+	assert.Nil(t, err)
+	// modify the params
+	invcation = invocation.NewRPCInvocation("test", parmas[:1], map[string]string{
+		constant.REQUEST_SIGNATURE_KEY: signature,
+		constant.CONSUMER:              "test",
+		constant.REQUEST_TIMESTAMP_KEY: requestTime,
+		constant.AK_KEY:                access,
+	})
+	err = authenticator.Authenticate(invcation, &testurl)
+	assert.NotNil(t, err)
+
+}
+
+func TestDefaultAuthenticator_Sign(t *testing.T) {
+	authenticator := &DefaultAuthenticator{}
+	testurl, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?application=test&interface=com.ikurento.user.UserProvider&group=gg&version=2.6.0")
+	testurl.SetParam(constant.ACCESS_KEY_ID_KEY, "akey")
+	testurl.SetParam(constant.SECRET_ACCESS_KEY_KEY, "skey")
+	testurl.SetParam(constant.PARAMTER_SIGNATURE_ENABLE_KEY, "false")
+	inv := invocation.NewRPCInvocation("test", []interface{}{"OK"}, nil)
+	_ = authenticator.Sign(inv, &testurl)
+	assert.NotEqual(t, inv.AttachmentsByKey(constant.REQUEST_SIGNATURE_KEY, ""), "")
+	assert.NotEqual(t, inv.AttachmentsByKey(constant.CONSUMER, ""), "")
+	assert.NotEqual(t, inv.AttachmentsByKey(constant.REQUEST_TIMESTAMP_KEY, ""), "")
+	assert.Equal(t, inv.AttachmentsByKey(constant.AK_KEY, ""), "akey")
+
+}
+
+func Test_getAccessKeyPairSuccess(t *testing.T) {
+	testurl := common.NewURLWithOptions(
+		common.WithParams(url.Values{}),
+		common.WithParamsValue(constant.SECRET_ACCESS_KEY_KEY, "skey"),
+		common.WithParamsValue(constant.ACCESS_KEY_ID_KEY, "akey"))
+	invcation := invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, nil)
+	_, e := getAccessKeyPair(invcation, testurl)
+	assert.Nil(t, e)
+}
+
+func Test_getAccessKeyPairFailed(t *testing.T) {
+	defer func() {
+		e := recover()
+		assert.NotNil(t, e)
+	}()
+	testurl := common.NewURLWithOptions(
+		common.WithParams(url.Values{}),
+		common.WithParamsValue(constant.ACCESS_KEY_ID_KEY, "akey"))
+	invcation := invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, nil)
+	_, e := getAccessKeyPair(invcation, testurl)
+	assert.NotNil(t, e)
+	testurl = common.NewURLWithOptions(
+		common.WithParams(url.Values{}),
+		common.WithParamsValue(constant.SECRET_ACCESS_KEY_KEY, "skey"),
+		common.WithParamsValue(constant.ACCESS_KEY_ID_KEY, "akey"), common.WithParamsValue(constant.ACCESS_KEY_STORAGE_KEY, "dubbo"))
+	_, e = getAccessKeyPair(invcation, testurl)
+
+}
+
+func Test_getSignatureWithinParams(t *testing.T) {
+	testurl, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=gg&version=2.6.0")
+	testurl.SetParam(constant.PARAMTER_SIGNATURE_ENABLE_KEY, "true")
+	inv := invocation.NewRPCInvocation("test", []interface{}{"OK"}, map[string]string{
+		"": "",
+	})
+	secret := "dubbo"
+	current := strconv.Itoa(int(time.Now().Unix() * 1000))
+	signature, _ := getSignature(&testurl, inv, secret, current)
+	requestString := fmt.Sprintf(constant.SIGNATURE_STRING_FORMAT,
+		testurl.ColonSeparatedKey(), inv.MethodName(), secret, current)
+	s, _ := SignWithParams(inv.Arguments(), requestString, secret)
+	assert.False(t, IsEmpty(signature, false))
+	assert.Equal(t, s, signature)
+}
+
+func Test_getSignature(t *testing.T) {
+	testurl, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=gg&version=2.6.0")
+	testurl.SetParam(constant.PARAMTER_SIGNATURE_ENABLE_KEY, "false")
+	inv := invocation.NewRPCInvocation("test", []interface{}{"OK"}, nil)
+	secret := "dubbo"
+	current := strconv.Itoa(int(time.Now().Unix() * 1000))
+	signature, _ := getSignature(&testurl, inv, secret, current)
+	requestString := fmt.Sprintf(constant.SIGNATURE_STRING_FORMAT,
+		testurl.ColonSeparatedKey(), inv.MethodName(), secret, current)
+	s := Sign(requestString, secret)
+	assert.False(t, IsEmpty(signature, false))
+	assert.Equal(t, s, signature)
+}
diff --git a/filter/filter_impl/auth/provider_auth.go b/filter/filter_impl/auth/provider_auth.go
new file mode 100644
index 0000000000000000000000000000000000000000..0d5772e5508894111a88443bfe2d1b02ebfac54a
--- /dev/null
+++ b/filter/filter_impl/auth/provider_auth.go
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package auth
+
+import (
+	"context"
+)
+
+import (
+	"github.com/apache/dubbo-go/common/constant"
+	"github.com/apache/dubbo-go/common/extension"
+	"github.com/apache/dubbo-go/common/logger"
+	"github.com/apache/dubbo-go/filter"
+	"github.com/apache/dubbo-go/protocol"
+)
+
+// ProviderAuthFilter
+// This filter is used to verify the correctness of the signature on provider side
+type ProviderAuthFilter struct {
+}
+
+func init() {
+	extension.SetFilter(constant.PROVIDER_AUTH_FILTER, getProviderAuthFilter)
+}
+
+func (paf *ProviderAuthFilter) Invoke(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
+	logger.Infof("invoking providerAuth filter.")
+	url := invoker.GetUrl()
+
+	err := doAuthWork(&url, func(authenticator filter.Authenticator) error {
+		return authenticator.Authenticate(invocation, &url)
+	})
+	if err != nil {
+		logger.Infof("auth the request: %v occur exception, cause: %s", invocation, err.Error())
+		return &protocol.RPCResult{
+			Err: err,
+		}
+	}
+
+	return invoker.Invoke(ctx, invocation)
+}
+
+func (paf *ProviderAuthFilter) OnResponse(ctx context.Context, result protocol.Result, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
+	return result
+}
+
+func getProviderAuthFilter() filter.Filter {
+	return &ProviderAuthFilter{}
+}
diff --git a/filter/filter_impl/auth/provider_auth_test.go b/filter/filter_impl/auth/provider_auth_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..626782ae8390f046f441c1f162700a883e6f22d0
--- /dev/null
+++ b/filter/filter_impl/auth/provider_auth_test.go
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package auth
+
+import (
+	"context"
+	"strconv"
+	"testing"
+	"time"
+)
+
+import (
+	"github.com/golang/mock/gomock"
+	"github.com/stretchr/testify/assert"
+)
+
+import (
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/common/constant"
+	"github.com/apache/dubbo-go/protocol"
+	"github.com/apache/dubbo-go/protocol/invocation"
+	"github.com/apache/dubbo-go/protocol/mock"
+)
+
+func TestProviderAuthFilter_Invoke(t *testing.T) {
+	secret := "dubbo-sk"
+	access := "dubbo-ak"
+	url, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=gg&version=2.6.0")
+	url.SetParam(constant.ACCESS_KEY_ID_KEY, access)
+	url.SetParam(constant.SECRET_ACCESS_KEY_KEY, secret)
+	parmas := []interface{}{
+		"OK",
+		struct {
+			Name string
+			Id   int64
+		}{"YUYU", 1},
+	}
+	inv := invocation.NewRPCInvocation("test", parmas, nil)
+	requestTime := strconv.Itoa(int(time.Now().Unix() * 1000))
+	signature, _ := getSignature(&url, inv, secret, requestTime)
+
+	inv = invocation.NewRPCInvocation("test", []interface{}{"OK"}, map[string]string{
+		constant.REQUEST_SIGNATURE_KEY: signature,
+		constant.CONSUMER:              "test",
+		constant.REQUEST_TIMESTAMP_KEY: requestTime,
+		constant.AK_KEY:                access,
+	})
+	ctrl := gomock.NewController(t)
+	filter := &ProviderAuthFilter{}
+	defer ctrl.Finish()
+	invoker := mock.NewMockInvoker(ctrl)
+	result := &protocol.RPCResult{}
+	invoker.EXPECT().Invoke(inv).Return(result).Times(2)
+	invoker.EXPECT().GetUrl().Return(url).Times(2)
+	assert.Equal(t, result, filter.Invoke(context.Background(), invoker, inv))
+	url.SetParam(constant.SERVICE_AUTH_KEY, "true")
+	assert.Equal(t, result, filter.Invoke(context.Background(), invoker, inv))
+
+}
diff --git a/filter/filter_impl/auth/sign_util.go b/filter/filter_impl/auth/sign_util.go
new file mode 100644
index 0000000000000000000000000000000000000000..043a549a849dde66712e1bef389dd91a024660df
--- /dev/null
+++ b/filter/filter_impl/auth/sign_util.go
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package auth
+
+import (
+	"crypto/hmac"
+	"crypto/sha256"
+	"encoding/base64"
+	"encoding/json"
+	"errors"
+	"strings"
+)
+
+// Sign
+// get a signature string with given information, such as metadata or parameters
+func Sign(metadata, key string) string {
+	return doSign([]byte(metadata), key)
+}
+
+func SignWithParams(params []interface{}, metadata, key string) (string, error) {
+	if params == nil || len(params) == 0 {
+		return Sign(metadata, key), nil
+	}
+
+	data := append(params, metadata)
+	if bytes, err := toBytes(data); err != nil {
+		// TODO
+		return "", errors.New("data convert to bytes failed")
+	} else {
+		return doSign(bytes, key), nil
+	}
+}
+
+func toBytes(data []interface{}) ([]byte, error) {
+	if bytes, err := json.Marshal(data); err != nil {
+		return nil, errors.New("")
+	} else {
+		return bytes, nil
+	}
+}
+
+func doSign(bytes []byte, key string) string {
+	mac := hmac.New(sha256.New, []byte(key))
+	mac.Write(bytes)
+	signature := mac.Sum(nil)
+	return base64.URLEncoding.EncodeToString(signature)
+}
+
+func IsEmpty(s string, allowSpace bool) bool {
+	if len(s) == 0 {
+		return true
+	}
+	if !allowSpace {
+		return strings.TrimSpace(s) == ""
+	}
+	return false
+}
diff --git a/filter/filter_impl/auth/sign_util_test.go b/filter/filter_impl/auth/sign_util_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..a4aaf2da27a8dd14969e0e3f93eaee16dfc31b03
--- /dev/null
+++ b/filter/filter_impl/auth/sign_util_test.go
@@ -0,0 +1,101 @@
+/*
+ * 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 auth
+
+import (
+	"testing"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+func TestIsEmpty(t *testing.T) {
+	type args struct {
+		s          string
+		allowSpace bool
+	}
+	tests := []struct {
+		name string
+		args args
+		want bool
+	}{
+		// TODO: Add test cases.
+		{"test1", args{s: "   ", allowSpace: false}, true},
+		{"test2", args{s: "   ", allowSpace: true}, false},
+		{"test3", args{s: "hello,dubbo", allowSpace: false}, false},
+		{"test4", args{s: "hello,dubbo", allowSpace: true}, false},
+		{"test5", args{s: "", allowSpace: true}, true},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if got := IsEmpty(tt.args.s, tt.args.allowSpace); got != tt.want {
+				t.Errorf("IsEmpty() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
+func TestSign(t *testing.T) {
+	metadata := "com.ikurento.user.UserProvider::sayHi"
+	key := "key"
+	signature := Sign(metadata, key)
+	assert.NotNil(t, signature)
+
+}
+
+func TestSignWithParams(t *testing.T) {
+	metadata := "com.ikurento.user.UserProvider::sayHi"
+	key := "key"
+	params := []interface{}{
+		"a", 1, struct {
+			Name string
+			Id   int64
+		}{"YuYu", 1},
+	}
+	signature, _ := SignWithParams(params, metadata, key)
+	assert.False(t, IsEmpty(signature, false))
+}
+
+func Test_doSign(t *testing.T) {
+	sign := doSign([]byte("DubboGo"), "key")
+	sign1 := doSign([]byte("DubboGo"), "key")
+	sign2 := doSign([]byte("DubboGo"), "key2")
+	assert.NotNil(t, sign)
+	assert.Equal(t, sign1, sign)
+	assert.NotEqual(t, sign1, sign2)
+}
+
+func Test_toBytes(t *testing.T) {
+	params := []interface{}{
+		"a", 1, struct {
+			Name string
+			Id   int64
+		}{"YuYu", 1},
+	}
+	params2 := []interface{}{
+		"a", 1, struct {
+			Name string
+			Id   int64
+		}{"YuYu", 1},
+	}
+	jsonBytes, _ := toBytes(params)
+	jsonBytes2, _ := toBytes(params2)
+	assert.NotNil(t, jsonBytes)
+	assert.Equal(t, jsonBytes, jsonBytes2)
+}
diff --git a/filter/impl/echo_filter.go b/filter/filter_impl/echo_filter.go
similarity index 79%
rename from filter/impl/echo_filter.go
rename to filter/filter_impl/echo_filter.go
index 18e42c8cb2b15acb27573c5e24f11a8b69e0d496..a12800a21a8ebe4545b4a8b5bd0f8a30c1462105 100644
--- a/filter/impl/echo_filter.go
+++ b/filter/filter_impl/echo_filter.go
@@ -15,7 +15,11 @@
  * limitations under the License.
  */
 
-package impl
+package filter_impl
+
+import (
+	"context"
+)
 
 import (
 	"github.com/apache/dubbo-go/common/constant"
@@ -26,6 +30,7 @@ import (
 )
 
 const (
+	// ECHO echo module name
 	ECHO = "echo"
 )
 
@@ -33,12 +38,14 @@ func init() {
 	extension.SetFilter(ECHO, GetFilter)
 }
 
+// EchoFilter
 // RPCService need a Echo method in consumer, if you want to use EchoFilter
 // eg:
 //		Echo func(ctx context.Context, arg interface{}, rsp *Xxx) error
 type EchoFilter struct{}
 
-func (ef *EchoFilter) Invoke(invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
+// Invoke ...
+func (ef *EchoFilter) Invoke(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
 	logger.Infof("invoking echo filter.")
 	logger.Debugf("%v,%v", invocation.MethodName(), len(invocation.Arguments()))
 	if invocation.MethodName() == constant.ECHO && len(invocation.Arguments()) == 1 {
@@ -48,13 +55,17 @@ func (ef *EchoFilter) Invoke(invoker protocol.Invoker, invocation protocol.Invoc
 		}
 	}
 
-	return invoker.Invoke(invocation)
+	return invoker.Invoke(ctx, invocation)
 }
 
-func (ef *EchoFilter) OnResponse(result protocol.Result, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
+// OnResponse ...
+func (ef *EchoFilter) OnResponse(_ context.Context, result protocol.Result, _ protocol.Invoker,
+	_ protocol.Invocation) protocol.Result {
+
 	return result
 }
 
+// GetFilter ...
 func GetFilter() filter.Filter {
 	return &EchoFilter{}
 }
diff --git a/filter/impl/echo_filter_test.go b/filter/filter_impl/echo_filter_test.go
similarity index 77%
rename from filter/impl/echo_filter_test.go
rename to filter/filter_impl/echo_filter_test.go
index e2e592974701ad18c5b01e884485c022ee2320b8..fc09bdce696c6be3c9e11d0ac864b187d1d85cde 100644
--- a/filter/impl/echo_filter_test.go
+++ b/filter/filter_impl/echo_filter_test.go
@@ -15,9 +15,10 @@
  * limitations under the License.
  */
 
-package impl
+package filter_impl
 
 import (
+	"context"
 	"testing"
 )
 
@@ -33,12 +34,10 @@ import (
 
 func TestEchoFilter_Invoke(t *testing.T) {
 	filter := GetFilter()
-	result := filter.Invoke(protocol.NewBaseInvoker(common.URL{}),
-		invocation.NewRPCInvocation("$echo", []interface{}{"OK"}, nil))
+	result := filter.Invoke(context.Background(), protocol.NewBaseInvoker(common.URL{}), invocation.NewRPCInvocation("$echo", []interface{}{"OK"}, nil))
 	assert.Equal(t, "OK", result.Result())
 
-	result = filter.Invoke(protocol.NewBaseInvoker(common.URL{}),
-		invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, nil))
+	result = filter.Invoke(context.Background(), protocol.NewBaseInvoker(common.URL{}), invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, nil))
 	assert.Nil(t, result.Error())
 	assert.Nil(t, result.Result())
 }
diff --git a/filter/impl/execute_limit_filter.go b/filter/filter_impl/execute_limit_filter.go
similarity index 78%
rename from filter/impl/execute_limit_filter.go
rename to filter/filter_impl/execute_limit_filter.go
index 156af1b140283dd76c4867ca26e9b42ce8eb25c0..434c378045456eb13317e0a48630ebd33f244c05 100644
--- a/filter/impl/execute_limit_filter.go
+++ b/filter/filter_impl/execute_limit_filter.go
@@ -15,9 +15,10 @@
  * limitations under the License.
  */
 
-package impl
+package filter_impl
 
 import (
+	"context"
 	"strconv"
 	"sync"
 	"sync/atomic"
@@ -32,17 +33,20 @@ import (
 	"github.com/apache/dubbo-go/common/extension"
 	"github.com/apache/dubbo-go/common/logger"
 	"github.com/apache/dubbo-go/filter"
-	_ "github.com/apache/dubbo-go/filter/common/impl"
+	_ "github.com/apache/dubbo-go/filter/handler"
 	"github.com/apache/dubbo-go/protocol"
 )
 
-const name = "execute"
+const (
+	name = "execute"
+)
 
 func init() {
 	extension.SetFilter(name, GetExecuteLimitFilter)
 }
 
 /**
+ * ExecuteLimitFilter
  * The filter will limit the number of in-progress request and it's thread-safe.
  * example:
  * "UserProvider":
@@ -71,23 +75,25 @@ type ExecuteLimitFilter struct {
 	executeState *concurrent.Map
 }
 
+// ExecuteState ...
 type ExecuteState struct {
 	concurrentCount int64
 }
 
-func (ef *ExecuteLimitFilter) Invoke(invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
+// Invoke ...
+func (ef *ExecuteLimitFilter) Invoke(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
 	methodConfigPrefix := "methods." + invocation.MethodName() + "."
-	url := invoker.GetUrl()
-	limitTarget := url.ServiceKey()
+	ivkURL := invoker.GetUrl()
+	limitTarget := ivkURL.ServiceKey()
 	limitRateConfig := constant.DEFAULT_EXECUTE_LIMIT
 
-	methodLevelConfig := url.GetParam(methodConfigPrefix+constant.EXECUTE_LIMIT_KEY, "")
+	methodLevelConfig := ivkURL.GetParam(methodConfigPrefix+constant.EXECUTE_LIMIT_KEY, "")
 	if len(methodLevelConfig) > 0 {
 		// we have the method-level configuration
 		limitTarget = limitTarget + "#" + invocation.MethodName()
 		limitRateConfig = methodLevelConfig
 	} else {
-		limitRateConfig = url.GetParam(constant.EXECUTE_LIMIT_KEY, constant.DEFAULT_EXECUTE_LIMIT)
+		limitRateConfig = ivkURL.GetParam(constant.EXECUTE_LIMIT_KEY, constant.DEFAULT_EXECUTE_LIMIT)
 	}
 
 	limitRate, err := strconv.ParseInt(limitRateConfig, 0, 0)
@@ -97,7 +103,7 @@ func (ef *ExecuteLimitFilter) Invoke(invoker protocol.Invoker, invocation protoc
 	}
 
 	if limitRate < 0 {
-		return invoker.Invoke(invocation)
+		return invoker.Invoke(ctx, invocation)
 	}
 
 	state, _ := ef.executeState.LoadOrStore(limitTarget, &ExecuteState{
@@ -107,16 +113,17 @@ func (ef *ExecuteLimitFilter) Invoke(invoker protocol.Invoker, invocation protoc
 	concurrentCount := state.(*ExecuteState).increase()
 	defer state.(*ExecuteState).decrease()
 	if concurrentCount > limitRate {
-		logger.Errorf("The invocation was rejected due to over the execute limitation, url: %s ", url.String())
-		rejectedHandlerConfig := url.GetParam(methodConfigPrefix+constant.EXECUTE_REJECTED_EXECUTION_HANDLER_KEY,
-			url.GetParam(constant.EXECUTE_REJECTED_EXECUTION_HANDLER_KEY, constant.DEFAULT_KEY))
-		return extension.GetRejectedExecutionHandler(rejectedHandlerConfig).RejectedExecution(url, invocation)
+		logger.Errorf("The invocation was rejected due to over the execute limitation, url: %s ", ivkURL.String())
+		rejectedHandlerConfig := ivkURL.GetParam(methodConfigPrefix+constant.EXECUTE_REJECTED_EXECUTION_HANDLER_KEY,
+			ivkURL.GetParam(constant.EXECUTE_REJECTED_EXECUTION_HANDLER_KEY, constant.DEFAULT_KEY))
+		return extension.GetRejectedExecutionHandler(rejectedHandlerConfig).RejectedExecution(ivkURL, invocation)
 	}
 
-	return invoker.Invoke(invocation)
+	return invoker.Invoke(ctx, invocation)
 }
 
-func (ef *ExecuteLimitFilter) OnResponse(result protocol.Result, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
+// OnResponse ...
+func (ef *ExecuteLimitFilter) OnResponse(_ context.Context, result protocol.Result, _ protocol.Invoker, _ protocol.Invocation) protocol.Result {
 	return result
 }
 
@@ -131,6 +138,7 @@ func (state *ExecuteState) decrease() {
 var executeLimitOnce sync.Once
 var executeLimitFilter *ExecuteLimitFilter
 
+// GetExecuteLimitFilter ...
 func GetExecuteLimitFilter() filter.Filter {
 	executeLimitOnce.Do(func() {
 		executeLimitFilter = &ExecuteLimitFilter{
diff --git a/filter/impl/execute_limit_filter_test.go b/filter/filter_impl/execute_limit_filter_test.go
similarity index 88%
rename from filter/impl/execute_limit_filter_test.go
rename to filter/filter_impl/execute_limit_filter_test.go
index 5d729c0e6a1205902856eccfa6aa96b0bee0e790..ae8641f2db0b98b59f9939cfc85f3ad096b1bc7f 100644
--- a/filter/impl/execute_limit_filter_test.go
+++ b/filter/filter_impl/execute_limit_filter_test.go
@@ -15,9 +15,10 @@
  * limitations under the License.
  */
 
-package impl
+package filter_impl
 
 import (
+	"context"
 	"net/url"
 	"testing"
 )
@@ -43,7 +44,7 @@ func TestExecuteLimitFilter_Invoke_Ignored(t *testing.T) {
 
 	limitFilter := GetExecuteLimitFilter()
 
-	result := limitFilter.Invoke(protocol.NewBaseInvoker(*invokeUrl), invoc)
+	result := limitFilter.Invoke(context.Background(), protocol.NewBaseInvoker(*invokeUrl), invoc)
 	assert.NotNil(t, result)
 	assert.Nil(t, result.Error())
 }
@@ -60,7 +61,7 @@ func TestExecuteLimitFilter_Invoke_Configure_Error(t *testing.T) {
 
 	limitFilter := GetExecuteLimitFilter()
 
-	result := limitFilter.Invoke(protocol.NewBaseInvoker(*invokeUrl), invoc)
+	result := limitFilter.Invoke(context.Background(), protocol.NewBaseInvoker(*invokeUrl), invoc)
 	assert.NotNil(t, result)
 	assert.Nil(t, result.Error())
 }
@@ -77,7 +78,7 @@ func TestExecuteLimitFilter_Invoke(t *testing.T) {
 
 	limitFilter := GetExecuteLimitFilter()
 
-	result := limitFilter.Invoke(protocol.NewBaseInvoker(*invokeUrl), invoc)
+	result := limitFilter.Invoke(context.Background(), protocol.NewBaseInvoker(*invokeUrl), invoc)
 	assert.NotNil(t, result)
 	assert.Nil(t, result.Error())
 }
diff --git a/filter/impl/generic_filter.go b/filter/filter_impl/generic_filter.go
similarity index 80%
rename from filter/impl/generic_filter.go
rename to filter/filter_impl/generic_filter.go
index 35aadb11a444bda56109e238b17267f71ec2606b..9bc131ef8903942b84df2b8fc14fd11143d1a7b6 100644
--- a/filter/impl/generic_filter.go
+++ b/filter/filter_impl/generic_filter.go
@@ -15,15 +15,18 @@
  * limitations under the License.
  */
 
-package impl
+package filter_impl
 
 import (
+	"context"
 	"reflect"
 	"strings"
 )
+
 import (
 	hessian "github.com/apache/dubbo-go-hessian2"
 )
+
 import (
 	"github.com/apache/dubbo-go/common/constant"
 	"github.com/apache/dubbo-go/common/extension"
@@ -33,6 +36,8 @@ import (
 )
 
 const (
+	// GENERIC
+	//generic module name
 	GENERIC = "generic"
 )
 
@@ -42,38 +47,43 @@ func init() {
 
 //  when do a generic invoke, struct need to be map
 
+// GenericFilter ...
 type GenericFilter struct{}
 
-func (ef *GenericFilter) Invoke(invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
+// Invoke ...
+func (ef *GenericFilter) Invoke(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
 	if invocation.MethodName() == constant.GENERIC && len(invocation.Arguments()) == 3 {
 		oldArguments := invocation.Arguments()
-		var newParams []hessian.Object
+
 		if oldParams, ok := oldArguments[2].([]interface{}); ok {
+			newParams := make([]hessian.Object, 0, len(oldParams))
 			for i := range oldParams {
 				newParams = append(newParams, hessian.Object(struct2MapAll(oldParams[i])))
 			}
-		} else {
-			return invoker.Invoke(invocation)
-		}
-		newArguments := []interface{}{
-			oldArguments[0],
-			oldArguments[1],
-			newParams,
+			newArguments := []interface{}{
+				oldArguments[0],
+				oldArguments[1],
+				newParams,
+			}
+			newInvocation := invocation2.NewRPCInvocation(invocation.MethodName(), newArguments, invocation.Attachments())
+			newInvocation.SetReply(invocation.Reply())
+			return invoker.Invoke(ctx, newInvocation)
 		}
-		newInvocation := invocation2.NewRPCInvocation(invocation.MethodName(), newArguments, invocation.Attachments())
-		newInvocation.SetReply(invocation.Reply())
-		return invoker.Invoke(newInvocation)
 	}
-	return invoker.Invoke(invocation)
+	return invoker.Invoke(ctx, invocation)
 }
 
-func (ef *GenericFilter) OnResponse(result protocol.Result, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
+// OnResponse ...
+func (ef *GenericFilter) OnResponse(_ context.Context, result protocol.Result, _ protocol.Invoker,
+	_ protocol.Invocation) protocol.Result {
 	return result
 }
 
+// GetGenericFilter ...
 func GetGenericFilter() filter.Filter {
 	return &GenericFilter{}
 }
+
 func struct2MapAll(obj interface{}) interface{} {
 	if obj == nil {
 		return obj
@@ -118,6 +128,7 @@ func struct2MapAll(obj interface{}) interface{} {
 		return obj
 	}
 }
+
 func setInMap(m map[string]interface{}, structField reflect.StructField, value interface{}) (result map[string]interface{}) {
 	result = m
 	if tagName := structField.Tag.Get("m"); tagName == "" {
@@ -127,6 +138,7 @@ func setInMap(m map[string]interface{}, structField reflect.StructField, value i
 	}
 	return
 }
+
 func headerAtoa(a string) (b string) {
 	b = strings.ToLower(a[:1]) + a[1:]
 	return
diff --git a/filter/impl/generic_filter_test.go b/filter/filter_impl/generic_filter_test.go
similarity index 99%
rename from filter/impl/generic_filter_test.go
rename to filter/filter_impl/generic_filter_test.go
index 9797c40df1f57017241675013620a53320e475ad..b08229199898a30657682d47c32689dc084f5bf4 100644
--- a/filter/impl/generic_filter_test.go
+++ b/filter/filter_impl/generic_filter_test.go
@@ -15,12 +15,13 @@
  * limitations under the License.
  */
 
-package impl
+package filter_impl
 
 import (
 	"reflect"
 	"testing"
 )
+
 import (
 	"github.com/stretchr/testify/assert"
 )
@@ -87,6 +88,7 @@ func Test_struct2MapAll_Slice(t *testing.T) {
 	assert.Equal(t, reflect.Slice, reflect.TypeOf(m["caCa"]).Kind())
 	assert.Equal(t, reflect.Map, reflect.TypeOf(m["caCa"].([]interface{})[0].(map[string]interface{})["xxYy"]).Kind())
 }
+
 func Test_struct2MapAll_Map(t *testing.T) {
 	var testData struct {
 		AaAa string
diff --git a/filter/filter_impl/generic_service_filter.go b/filter/filter_impl/generic_service_filter.go
new file mode 100644
index 0000000000000000000000000000000000000000..6272df6b39b0c18a77721f3a8c9e92618133aa6c
--- /dev/null
+++ b/filter/filter_impl/generic_service_filter.go
@@ -0,0 +1,133 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package filter_impl
+
+import (
+	"context"
+	"reflect"
+	"strings"
+)
+
+import (
+	hessian "github.com/apache/dubbo-go-hessian2"
+	"github.com/mitchellh/mapstructure"
+	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/filter"
+	"github.com/apache/dubbo-go/protocol"
+	invocation2 "github.com/apache/dubbo-go/protocol/invocation"
+)
+
+const (
+	// GENERIC_SERVICE ...
+	GENERIC_SERVICE = "generic_service"
+	// GENERIC_SERIALIZATION_DEFAULT ...
+	GENERIC_SERIALIZATION_DEFAULT = "true"
+)
+
+func init() {
+	extension.SetFilter(GENERIC_SERVICE, GetGenericServiceFilter)
+}
+
+// GenericServiceFilter ...
+type GenericServiceFilter struct{}
+
+// Invoke ...
+func (ef *GenericServiceFilter) Invoke(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
+	logger.Infof("invoking generic service filter.")
+	logger.Debugf("generic service filter methodName:%v,args:%v", invocation.MethodName(), len(invocation.Arguments()))
+
+	if invocation.MethodName() != constant.GENERIC || len(invocation.Arguments()) != 3 {
+		return invoker.Invoke(ctx, invocation)
+	}
+
+	var (
+		ok         bool
+		err        error
+		methodName string
+		newParams  []interface{}
+		genericKey string
+		argsType   []reflect.Type
+		oldParams  []hessian.Object
+	)
+
+	url := invoker.GetUrl()
+	methodName = invocation.Arguments()[0].(string)
+	// get service
+	svc := common.ServiceMap.GetService(url.Protocol, strings.TrimPrefix(url.Path, "/"))
+	// get method
+	method := svc.Method()[methodName]
+	if method == nil {
+		logger.Errorf("[Generic Service Filter] Don't have this method: %s", methodName)
+		return &protocol.RPCResult{}
+	}
+	argsType = method.ArgsType()
+	genericKey = invocation.AttachmentsByKey(constant.GENERIC_KEY, GENERIC_SERIALIZATION_DEFAULT)
+	if genericKey == GENERIC_SERIALIZATION_DEFAULT {
+		oldParams, ok = invocation.Arguments()[2].([]hessian.Object)
+	} else {
+		logger.Errorf("[Generic Service Filter] Don't support this generic: %s", genericKey)
+		return &protocol.RPCResult{}
+	}
+	if !ok {
+		logger.Errorf("[Generic Service Filter] wrong serialization")
+		return &protocol.RPCResult{}
+	}
+	if len(oldParams) != len(argsType) {
+		logger.Errorf("[Generic Service Filter] method:%s invocation arguments number was wrong", methodName)
+		return &protocol.RPCResult{}
+	}
+	// oldParams convert to newParams
+	newParams = make([]interface{}, len(oldParams))
+	for i := range argsType {
+		newParam := reflect.New(argsType[i]).Interface()
+		err = mapstructure.Decode(oldParams[i], newParam)
+		newParam = reflect.ValueOf(newParam).Elem().Interface()
+		if err != nil {
+			logger.Errorf("[Generic Service Filter] decode arguments map to struct wrong: error{%v}", perrors.WithStack(err))
+			return &protocol.RPCResult{}
+		}
+		newParams[i] = newParam
+	}
+	newInvocation := invocation2.NewRPCInvocation(methodName, newParams, invocation.Attachments())
+	newInvocation.SetReply(invocation.Reply())
+	return invoker.Invoke(ctx, newInvocation)
+}
+
+// OnResponse ...
+func (ef *GenericServiceFilter) OnResponse(ctx context.Context, result protocol.Result, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
+	if invocation.MethodName() == constant.GENERIC && len(invocation.Arguments()) == 3 && result.Result() != nil {
+		v := reflect.ValueOf(result.Result())
+		if v.Kind() == reflect.Ptr {
+			v = v.Elem()
+		}
+		result.SetResult(struct2MapAll(v.Interface()))
+	}
+	return result
+}
+
+// GetGenericServiceFilter ...
+func GetGenericServiceFilter() filter.Filter {
+	return &GenericServiceFilter{}
+}
diff --git a/filter/filter_impl/generic_service_filter_test.go b/filter/filter_impl/generic_service_filter_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..37c6af7450a75449fce51182684be2f619eda9d8
--- /dev/null
+++ b/filter/filter_impl/generic_service_filter_test.go
@@ -0,0 +1,149 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package filter_impl
+
+import (
+	"context"
+	"errors"
+	"reflect"
+	"testing"
+)
+
+import (
+	hessian "github.com/apache/dubbo-go-hessian2"
+	"github.com/stretchr/testify/assert"
+)
+
+import (
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/common/proxy/proxy_factory"
+	"github.com/apache/dubbo-go/protocol"
+	"github.com/apache/dubbo-go/protocol/invocation"
+)
+
+type TestStruct struct {
+	AaAa string
+	BaBa string `m:"baBa"`
+	XxYy struct {
+		xxXx string `m:"xxXx"`
+		Xx   string `m:"xx"`
+	} `m:"xxYy"`
+}
+
+func (c *TestStruct) JavaClassName() string {
+	return "com.test.testStruct"
+}
+
+type TestService struct{}
+
+// MethodOne ...
+func (ts *TestService) MethodOne(_ context.Context, test1 *TestStruct, test2 []TestStruct,
+	test3 interface{}, test4 []interface{}, test5 *string) (*TestStruct, error) {
+	if test1 == nil {
+		return nil, errors.New("param test1 is nil")
+	}
+	if test2 == nil {
+		return nil, errors.New("param test2 is nil")
+	}
+	if test3 == nil {
+		return nil, errors.New("param test3 is nil")
+	}
+	if test4 == nil {
+		return nil, errors.New("param test4 is nil")
+	}
+	if test5 == nil {
+		return nil, errors.New("param test5 is nil")
+	}
+	return &TestStruct{}, nil
+}
+
+// Reference ...
+func (*TestService) Reference() string {
+	return "com.test.Path"
+}
+
+func TestGenericServiceFilter_Invoke(t *testing.T) {
+	hessian.RegisterPOJO(&TestStruct{})
+	methodName := "$invoke"
+	m := make(map[string]interface{})
+	m["AaAa"] = "nihao"
+	x := make(map[string]interface{})
+	x["xxXX"] = "nihaoxxx"
+	m["XxYy"] = x
+	aurguments := []interface{}{
+		"MethodOne",
+		nil,
+		[]hessian.Object{
+			hessian.Object(m),
+			hessian.Object(append(make([]map[string]interface{}, 1), m)),
+			hessian.Object("111"),
+			hessian.Object(append(make([]map[string]interface{}, 1), m)),
+			hessian.Object("222")},
+	}
+	s := &TestService{}
+	_, _ = common.ServiceMap.Register("testprotocol", s)
+	rpcInvocation := invocation.NewRPCInvocation(methodName, aurguments, nil)
+	filter := GetGenericServiceFilter()
+	url, _ := common.NewURL("testprotocol://127.0.0.1:20000/com.test.Path")
+	result := filter.Invoke(context.Background(), &proxy_factory.ProxyInvoker{BaseInvoker: *protocol.NewBaseInvoker(url)}, rpcInvocation)
+	assert.NotNil(t, result)
+	assert.Nil(t, result.Error())
+}
+
+func TestGenericServiceFilter_ResponseTestStruct(t *testing.T) {
+	ts := &TestStruct{
+		AaAa: "aaa",
+		BaBa: "bbb",
+		XxYy: struct {
+			xxXx string `m:"xxXx"`
+			Xx   string `m:"xx"`
+		}{},
+	}
+	result := &protocol.RPCResult{
+		Rest: ts,
+	}
+	aurguments := []interface{}{
+		"MethodOne",
+		nil,
+		[]hessian.Object{nil},
+	}
+	filter := GetGenericServiceFilter()
+	methodName := "$invoke"
+	rpcInvocation := invocation.NewRPCInvocation(methodName, aurguments, nil)
+	r := filter.OnResponse(nil, result, nil, rpcInvocation)
+	assert.NotNil(t, r.Result())
+	assert.Equal(t, reflect.ValueOf(r.Result()).Kind(), reflect.Map)
+}
+
+func TestGenericServiceFilter_ResponseString(t *testing.T) {
+	str := "111"
+	result := &protocol.RPCResult{
+		Rest: str,
+	}
+	aurguments := []interface{}{
+		"MethodOne",
+		nil,
+		[]hessian.Object{nil},
+	}
+	filter := GetGenericServiceFilter()
+	methodName := "$invoke"
+	rpcInvocation := invocation.NewRPCInvocation(methodName, aurguments, nil)
+	r := filter.OnResponse(nil, result, nil, rpcInvocation)
+	assert.NotNil(t, r.Result())
+	assert.Equal(t, reflect.ValueOf(r.Result()).Kind(), reflect.String)
+}
diff --git a/filter/filter_impl/graceful_shutdown_filter.go b/filter/filter_impl/graceful_shutdown_filter.go
new file mode 100644
index 0000000000000000000000000000000000000000..95e625b2d56895a4d57823e4e0e2e7d1d5e90a08
--- /dev/null
+++ b/filter/filter_impl/graceful_shutdown_filter.go
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package filter_impl
+
+import (
+	"context"
+	"sync/atomic"
+)
+
+import (
+	"github.com/apache/dubbo-go/common/constant"
+	"github.com/apache/dubbo-go/common/extension"
+	"github.com/apache/dubbo-go/common/logger"
+	"github.com/apache/dubbo-go/config"
+	"github.com/apache/dubbo-go/filter"
+	"github.com/apache/dubbo-go/protocol"
+)
+
+func init() {
+	var consumerFiler = &gracefulShutdownFilter{
+		shutdownConfig: config.GetConsumerConfig().ShutdownConfig,
+	}
+	var providerFilter = &gracefulShutdownFilter{
+		shutdownConfig: config.GetProviderConfig().ShutdownConfig,
+	}
+
+	extension.SetFilter(constant.CONSUMER_SHUTDOWN_FILTER, func() filter.Filter {
+		return consumerFiler
+	})
+
+	extension.SetFilter(constant.PROVIDER_SHUTDOWN_FILTER, func() filter.Filter {
+		return providerFilter
+	})
+}
+
+type gracefulShutdownFilter struct {
+	activeCount    int32
+	shutdownConfig *config.ShutdownConfig
+}
+
+func (gf *gracefulShutdownFilter) Invoke(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
+	if gf.rejectNewRequest() {
+		logger.Info("The application is closing, new request will be rejected.")
+		return gf.getRejectHandler().RejectedExecution(invoker.GetUrl(), invocation)
+	}
+	atomic.AddInt32(&gf.activeCount, 1)
+	return invoker.Invoke(ctx, invocation)
+}
+
+func (gf *gracefulShutdownFilter) OnResponse(ctx context.Context, result protocol.Result, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
+	atomic.AddInt32(&gf.activeCount, -1)
+	// although this isn't thread safe, it won't be a problem if the gf.rejectNewRequest() is true.
+	if gf.shutdownConfig != nil && gf.activeCount <= 0 {
+		gf.shutdownConfig.RequestsFinished = true
+	}
+	return result
+}
+
+func (gf *gracefulShutdownFilter) rejectNewRequest() bool {
+	if gf.shutdownConfig == nil {
+		return false
+	}
+	return gf.shutdownConfig.RejectRequest
+}
+
+func (gf *gracefulShutdownFilter) getRejectHandler() filter.RejectedExecutionHandler {
+	handler := constant.DEFAULT_KEY
+	if gf.shutdownConfig != nil && len(gf.shutdownConfig.RejectRequestHandler) > 0 {
+		handler = gf.shutdownConfig.RejectRequestHandler
+	}
+	return extension.GetRejectedExecutionHandler(handler)
+}
diff --git a/filter/filter_impl/graceful_shutdown_filter_test.go b/filter/filter_impl/graceful_shutdown_filter_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..4c670933e3dcec29ad9ae7bfef250b4236ae7c54
--- /dev/null
+++ b/filter/filter_impl/graceful_shutdown_filter_test.go
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package filter_impl
+
+import (
+	"context"
+	"net/url"
+	"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/config"
+	"github.com/apache/dubbo-go/filter"
+	common2 "github.com/apache/dubbo-go/filter/handler"
+	"github.com/apache/dubbo-go/protocol"
+	"github.com/apache/dubbo-go/protocol/invocation"
+)
+
+func TestGenericFilter_Invoke(t *testing.T) {
+	invoc := invocation.NewRPCInvocation("GetUser", []interface{}{"OK"}, make(map[string]string, 0))
+
+	invokeUrl := common.NewURLWithOptions(
+		common.WithParams(url.Values{}))
+
+	shutdownFilter := extension.GetFilter(constant.PROVIDER_SHUTDOWN_FILTER).(*gracefulShutdownFilter)
+
+	providerConfig := config.GetProviderConfig()
+
+	assert.False(t, shutdownFilter.rejectNewRequest())
+	assert.Nil(t, providerConfig.ShutdownConfig)
+
+	assert.Equal(t, extension.GetRejectedExecutionHandler(constant.DEFAULT_KEY),
+		shutdownFilter.getRejectHandler())
+
+	result := shutdownFilter.Invoke(context.Background(), protocol.NewBaseInvoker(*invokeUrl), invoc)
+	assert.NotNil(t, result)
+	assert.Nil(t, result.Error())
+
+	providerConfig.ShutdownConfig = &config.ShutdownConfig{
+		RejectRequest:        true,
+		RejectRequestHandler: "mock",
+	}
+	shutdownFilter.shutdownConfig = providerConfig.ShutdownConfig
+
+	assert.True(t, shutdownFilter.rejectNewRequest())
+	result = shutdownFilter.OnResponse(nil, nil, protocol.NewBaseInvoker(*invokeUrl), invoc)
+
+	rejectHandler := &common2.OnlyLogRejectedExecutionHandler{}
+	extension.SetRejectedExecutionHandler("mock", func() filter.RejectedExecutionHandler {
+		return rejectHandler
+	})
+	assert.True(t, providerConfig.ShutdownConfig.RequestsFinished)
+	assert.Equal(t, rejectHandler, shutdownFilter.getRejectHandler())
+
+}
diff --git a/filter/impl/hystrix_filter.go b/filter/filter_impl/hystrix_filter.go
similarity index 91%
rename from filter/impl/hystrix_filter.go
rename to filter/filter_impl/hystrix_filter.go
index 3fd9f87168616b69d5ec72460767890d6956c154..9fd97b57b677c9aa8ec492151df9aace6dc78b62 100644
--- a/filter/impl/hystrix_filter.go
+++ b/filter/filter_impl/hystrix_filter.go
@@ -14,9 +14,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package impl
+
+package filter_impl
 
 import (
+	"context"
 	"fmt"
 	"regexp"
 	"sync"
@@ -35,9 +37,12 @@ import (
 )
 
 const (
+	// HYSTRIX_CONSUMER ...
 	HYSTRIX_CONSUMER = "hystrix_consumer"
+	// HYSTRIX_PROVIDER ...
 	HYSTRIX_PROVIDER = "hystrix_provider"
-	HYSTRIX          = "hystrix"
+	// HYSTRIX ...
+	HYSTRIX = "hystrix"
 )
 
 var (
@@ -57,6 +62,7 @@ func init() {
 	extension.SetFilter(HYSTRIX_PROVIDER, GetHystrixFilterProvider)
 }
 
+// HystrixFilterError ...
 type HystrixFilterError struct {
 	err           error
 	failByHystrix bool
@@ -66,9 +72,12 @@ func (hfError *HystrixFilterError) Error() string {
 	return hfError.err.Error()
 }
 
+// FailByHystrix ...
 func (hfError *HystrixFilterError) FailByHystrix() bool {
 	return hfError.failByHystrix
 }
+
+// NewHystrixFilterError ...
 func NewHystrixFilterError(err error, failByHystrix bool) error {
 	return &HystrixFilterError{
 		err:           err,
@@ -76,14 +85,15 @@ func NewHystrixFilterError(err error, failByHystrix bool) error {
 	}
 }
 
+// HystrixFilter ...
 type HystrixFilter struct {
 	COrP     bool //true for consumer
 	res      map[string][]*regexp.Regexp
 	ifNewMap sync.Map
 }
 
-func (hf *HystrixFilter) Invoke(invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
-
+// Invoke ...
+func (hf *HystrixFilter) Invoke(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
 	cmdName := fmt.Sprintf("%s&method=%s", invoker.GetUrl().Key(), invocation.MethodName())
 
 	// Do the configuration if the circuit breaker is created for the first time
@@ -115,12 +125,12 @@ func (hf *HystrixFilter) Invoke(invoker protocol.Invoker, invocation protocol.In
 	configLoadMutex.RUnlock()
 	if err != nil {
 		logger.Errorf("[Hystrix Filter]Errors occurred getting circuit for %s , will invoke without hystrix, error is: ", cmdName, err)
-		return invoker.Invoke(invocation)
+		return invoker.Invoke(ctx, invocation)
 	}
 	logger.Infof("[Hystrix Filter]Using hystrix filter: %s", cmdName)
 	var result protocol.Result
 	_ = hystrix.Do(cmdName, func() error {
-		result = invoker.Invoke(invocation)
+		result = invoker.Invoke(ctx, invocation)
 		err := result.Error()
 		if err != nil {
 			result.SetError(NewHystrixFilterError(err, false))
@@ -144,9 +154,12 @@ func (hf *HystrixFilter) Invoke(invoker protocol.Invoker, invocation protocol.In
 	return result
 }
 
-func (hf *HystrixFilter) OnResponse(result protocol.Result, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
+// OnResponse ...
+func (hf *HystrixFilter) OnResponse(ctx context.Context, result protocol.Result, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
 	return result
 }
+
+// GetHystrixFilterConsumer ...
 func GetHystrixFilterConsumer() filter.Filter {
 	//When first called, load the config in
 	consumerConfigOnce.Do(func() {
@@ -157,6 +170,7 @@ func GetHystrixFilterConsumer() filter.Filter {
 	return &HystrixFilter{COrP: true}
 }
 
+// GetHystrixFilterProvider ...
 func GetHystrixFilterProvider() filter.Filter {
 	providerConfigOnce.Do(func() {
 		if err := initHystrixConfigProvider(); err != nil {
@@ -215,6 +229,7 @@ func initHystrixConfigConsumer() error {
 	}
 	return nil
 }
+
 func initHystrixConfigProvider() error {
 	if config.GetProviderConfig().FilterConf == nil {
 		return perrors.Errorf("no config for hystrix")
@@ -241,6 +256,7 @@ func initHystrixConfigProvider() error {
 //	return initHystrixConfig()
 //}
 
+// CommandConfigWithError ...
 type CommandConfigWithError struct {
 	Timeout                int      `yaml:"timeout"`
 	MaxConcurrentRequests  int      `yaml:"max_concurrent_requests"`
@@ -258,11 +274,14 @@ type CommandConfigWithError struct {
 //- ErrorPercentThreshold: it causes circuits to open once the rolling measure of errors exceeds this percent of requests
 //See hystrix doc
 
+// HystrixFilterConfig ...
 type HystrixFilterConfig struct {
 	Configs  map[string]*CommandConfigWithError
 	Default  string
 	Services map[string]ServiceHystrixConfig
 }
+
+// ServiceHystrixConfig ...
 type ServiceHystrixConfig struct {
 	ServiceConfig string `yaml:"service_config"`
 	Methods       map[string]string
diff --git a/filter/impl/hystrix_filter_test.go b/filter/filter_impl/hystrix_filter_test.go
similarity index 90%
rename from filter/impl/hystrix_filter_test.go
rename to filter/filter_impl/hystrix_filter_test.go
index d3a5183ede25d8a325bb1c73020edddd2ffbc638..71fc097c8bf4752e0cb2b451b0da7e16480b0701 100644
--- a/filter/impl/hystrix_filter_test.go
+++ b/filter/filter_impl/hystrix_filter_test.go
@@ -14,17 +14,20 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package impl
+package filter_impl
 
 import (
+	"context"
 	"regexp"
 	"testing"
 )
+
 import (
 	"github.com/afex/hystrix-go/hystrix"
 	"github.com/pkg/errors"
 	"github.com/stretchr/testify/assert"
 )
+
 import (
 	"github.com/apache/dubbo-go/protocol"
 	"github.com/apache/dubbo-go/protocol/invocation"
@@ -125,9 +128,9 @@ type testMockSuccessInvoker struct {
 	protocol.BaseInvoker
 }
 
-func (iv *testMockSuccessInvoker) Invoke(invocation protocol.Invocation) protocol.Result {
+func (iv *testMockSuccessInvoker) Invoke(_ context.Context, _ protocol.Invocation) protocol.Result {
 	return &protocol.RPCResult{
-		Rest: "Sucess",
+		Rest: "Success",
 		Err:  nil,
 	}
 }
@@ -136,7 +139,7 @@ type testMockFailInvoker struct {
 	protocol.BaseInvoker
 }
 
-func (iv *testMockFailInvoker) Invoke(invocation protocol.Invocation) protocol.Result {
+func (iv *testMockFailInvoker) Invoke(_ context.Context, _ protocol.Invocation) protocol.Result {
 	return &protocol.RPCResult{
 		Err: errors.Errorf("exception"),
 	}
@@ -144,7 +147,7 @@ func (iv *testMockFailInvoker) Invoke(invocation protocol.Invocation) protocol.R
 
 func TestHystrixFilter_Invoke_Success(t *testing.T) {
 	hf := &HystrixFilter{}
-	result := hf.Invoke(&testMockSuccessInvoker{}, &invocation.RPCInvocation{})
+	result := hf.Invoke(context.Background(), &testMockSuccessInvoker{}, &invocation.RPCInvocation{})
 	assert.NotNil(t, result)
 	assert.NoError(t, result.Error())
 	assert.NotNil(t, result.Result())
@@ -152,7 +155,7 @@ func TestHystrixFilter_Invoke_Success(t *testing.T) {
 
 func TestHystrixFilter_Invoke_Fail(t *testing.T) {
 	hf := &HystrixFilter{}
-	result := hf.Invoke(&testMockFailInvoker{}, &invocation.RPCInvocation{})
+	result := hf.Invoke(context.Background(), &testMockFailInvoker{}, &invocation.RPCInvocation{})
 	assert.NotNil(t, result)
 	assert.Error(t, result.Error())
 }
@@ -164,7 +167,7 @@ func TestHystricFilter_Invoke_CircuitBreak(t *testing.T) {
 	resChan := make(chan protocol.Result, 50)
 	for i := 0; i < 50; i++ {
 		go func() {
-			result := hf.Invoke(&testMockFailInvoker{}, &invocation.RPCInvocation{})
+			result := hf.Invoke(context.Background(), &testMockFailInvoker{}, &invocation.RPCInvocation{})
 			resChan <- result
 		}()
 	}
@@ -189,7 +192,7 @@ func TestHystricFilter_Invoke_CircuitBreak_Omit_Exception(t *testing.T) {
 	resChan := make(chan protocol.Result, 50)
 	for i := 0; i < 50; i++ {
 		go func() {
-			result := hf.Invoke(&testMockFailInvoker{}, &invocation.RPCInvocation{})
+			result := hf.Invoke(context.Background(), &testMockFailInvoker{}, &invocation.RPCInvocation{})
 			resChan <- result
 		}()
 	}
@@ -210,6 +213,7 @@ func TestGetHystrixFilterConsumer(t *testing.T) {
 	assert.NotNil(t, get)
 	assert.True(t, get.(*HystrixFilter).COrP)
 }
+
 func TestGetHystrixFilterProvider(t *testing.T) {
 	get := GetHystrixFilterProvider()
 	assert.NotNil(t, get)
diff --git a/filter/filter_impl/metrics_filter.go b/filter/filter_impl/metrics_filter.go
new file mode 100644
index 0000000000000000000000000000000000000000..f4734172b74c8bbcdac5c9a9743acb4df5fcb6b5
--- /dev/null
+++ b/filter/filter_impl/metrics_filter.go
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package filter_impl
+
+import (
+	"context"
+	"time"
+)
+
+import (
+	"github.com/apache/dubbo-go/common/extension"
+	"github.com/apache/dubbo-go/config"
+	"github.com/apache/dubbo-go/filter"
+	"github.com/apache/dubbo-go/metrics"
+	"github.com/apache/dubbo-go/protocol"
+)
+
+const (
+	metricFilterName = "metrics"
+)
+
+var (
+	metricFilterInstance filter.Filter
+)
+
+// must initialized before using the filter and after loading configuration
+func init() {
+	extension.SetFilter(metricFilterName, newMetricsFilter)
+}
+
+// metricFilter will calculate the invocation's duration and the report to the reporters
+// If you want to use this filter to collect the metrics,
+// Adding this into your configuration file, like:
+// filter: "metrics"
+// metrics:
+//   reporter:
+//     - "your reporter" # here you should specify the reporter, for example 'prometheus'
+// more info please take a look at dubbo-samples projects
+type metricsFilter struct {
+	reporters []metrics.Reporter
+}
+
+// Invoke collect the duration of invocation and then report the duration by using goroutine
+func (p *metricsFilter) Invoke(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
+	start := time.Now()
+	res := invoker.Invoke(ctx, invocation)
+	end := time.Now()
+	duration := end.Sub(start)
+	go func() {
+		for _, reporter := range p.reporters {
+			reporter.Report(ctx, invoker, invocation, duration, res)
+		}
+	}()
+	return res
+}
+
+// OnResponse do nothing and return the result
+func (p *metricsFilter) OnResponse(ctx context.Context, res protocol.Result, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
+	return res
+}
+
+// newMetricsFilter the metricsFilter is singleton.
+// it's lazy initialization
+// make sure that the configuration had been loaded before invoking this method.
+func newMetricsFilter() filter.Filter {
+	if metricFilterInstance == nil {
+		reporterNames := config.GetMetricConfig().Reporters
+		reporters := make([]metrics.Reporter, 0, len(reporterNames))
+		for _, name := range reporterNames {
+			reporters = append(reporters, extension.GetMetricReporter(name))
+		}
+		metricFilterInstance = &metricsFilter{
+			reporters: reporters,
+		}
+	}
+
+	return metricFilterInstance
+}
diff --git a/filter/filter_impl/metrics_filter_test.go b/filter/filter_impl/metrics_filter_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..709404a2af4f4df0dbf625dbbbd673e34975c0db
--- /dev/null
+++ b/filter/filter_impl/metrics_filter_test.go
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package filter_impl
+
+import (
+	"context"
+	"sync"
+	"testing"
+	"time"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/mock"
+)
+
+import (
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/common/extension"
+	"github.com/apache/dubbo-go/config"
+	"github.com/apache/dubbo-go/metrics"
+	"github.com/apache/dubbo-go/protocol"
+	"github.com/apache/dubbo-go/protocol/invocation"
+)
+
+func TestMetricsFilter_Invoke(t *testing.T) {
+
+	// prepare the mock reporter
+	config.GetMetricConfig().Reporters = []string{"mock"}
+	mk := &mockReporter{}
+	extension.SetMetricReporter("mock", func() metrics.Reporter {
+		return mk
+	})
+
+	instance := extension.GetFilter(metricFilterName)
+
+	url, _ := common.NewURL(
+		"dubbo://:20000/UserProvider?app.version=0.0.1&application=BDTService&bean.name=UserProvider" +
+			"&cluster=failover&environment=dev&group=&interface=com.ikurento.user.UserProvider&loadbalance=random&methods.GetUser." +
+			"loadbalance=random&methods.GetUser.retries=1&methods.GetUser.weight=0&module=dubbogo+user-info+server&name=" +
+			"BDTService&organization=ikurento.com&owner=ZX&registry.role=3&retries=&" +
+			"service.filter=echo%2Ctoken%2Caccesslog&timestamp=1569153406&token=934804bf-b007-4174-94eb-96e3e1d60cc7&version=&warmup=100")
+	invoker := protocol.NewBaseInvoker(url)
+
+	attach := make(map[string]string, 10)
+	inv := invocation.NewRPCInvocation("MethodName", []interface{}{"OK", "Hello"}, attach)
+
+	ctx := context.Background()
+
+	mk.On("Report", ctx, invoker, inv).Return(true, nil)
+
+	mk.wg.Add(1)
+	result := instance.Invoke(ctx, invoker, inv)
+	assert.NotNil(t, result)
+	mk.AssertNotCalled(t, "Report", 1)
+	// it will do nothing
+	result = instance.OnResponse(ctx, nil, invoker, inv)
+	assert.Nil(t, result)
+}
+
+type mockReporter struct {
+	mock.Mock
+	wg sync.WaitGroup
+}
+
+func (m *mockReporter) Report(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation, cost time.Duration, res protocol.Result) {
+	m.Called(ctx, invoker, invocation)
+	m.wg.Done()
+}
diff --git a/filter/impl/token_filter.go b/filter/filter_impl/token_filter.go
similarity index 76%
rename from filter/impl/token_filter.go
rename to filter/filter_impl/token_filter.go
index d10dff5b761d0fbe40ff3a14a93ee8962d000e02..4605416c40a616361868c313881ae784257e6742 100644
--- a/filter/impl/token_filter.go
+++ b/filter/filter_impl/token_filter.go
@@ -15,9 +15,10 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
-package impl
+package filter_impl
 
 import (
+	"context"
 	"strings"
 )
 
@@ -33,6 +34,7 @@ import (
 )
 
 const (
+	// TOKEN ...
 	TOKEN = "token"
 )
 
@@ -40,27 +42,31 @@ func init() {
 	extension.SetFilter(TOKEN, GetTokenFilter)
 }
 
+// TokenFilter ...
 type TokenFilter struct{}
 
-func (tf *TokenFilter) Invoke(invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
+// Invoke ...
+func (tf *TokenFilter) Invoke(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
 	invokerTkn := invoker.GetUrl().GetParam(constant.TOKEN_KEY, "")
 	if len(invokerTkn) > 0 {
 		attachs := invocation.Attachments()
 		remoteTkn, exist := attachs[constant.TOKEN_KEY]
 		if exist && strings.EqualFold(invokerTkn, remoteTkn) {
-			return invoker.Invoke(invocation)
+			return invoker.Invoke(ctx, invocation)
 		}
 		return &protocol.RPCResult{Err: perrors.Errorf("Invalid token! Forbid invoke remote service %v method %s ",
 			invoker, invocation.MethodName())}
 	}
 
-	return invoker.Invoke(invocation)
+	return invoker.Invoke(ctx, invocation)
 }
 
-func (tf *TokenFilter) OnResponse(result protocol.Result, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
+// OnResponse ...
+func (tf *TokenFilter) OnResponse(ctx context.Context, result protocol.Result, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
 	return result
 }
 
+// GetTokenFilter ...
 func GetTokenFilter() filter.Filter {
 	return &TokenFilter{}
 }
diff --git a/filter/impl/token_filter_test.go b/filter/filter_impl/token_filter_test.go
similarity index 77%
rename from filter/impl/token_filter_test.go
rename to filter/filter_impl/token_filter_test.go
index 1473f274037699260725ff9ebb1b3d1377efb326..672082c729bc371a40573a66d13bc57a7024186b 100644
--- a/filter/impl/token_filter_test.go
+++ b/filter/filter_impl/token_filter_test.go
@@ -15,9 +15,10 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
-package impl
+package filter_impl
 
 import (
+	"context"
 	"net/url"
 	"testing"
 )
@@ -41,8 +42,10 @@ func TestTokenFilter_Invoke(t *testing.T) {
 		common.WithParamsValue(constant.TOKEN_KEY, "ori_key"))
 	attch := make(map[string]string, 0)
 	attch[constant.TOKEN_KEY] = "ori_key"
-	result := filter.Invoke(protocol.NewBaseInvoker(*url),
-		invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch))
+	result := filter.Invoke(context.Background(),
+		protocol.NewBaseInvoker(*url),
+		invocation.NewRPCInvocation("MethodName",
+			[]interface{}{"OK"}, attch))
 	assert.Nil(t, result.Error())
 	assert.Nil(t, result.Result())
 }
@@ -53,8 +56,7 @@ func TestTokenFilter_InvokeEmptyToken(t *testing.T) {
 	url := common.URL{}
 	attch := make(map[string]string, 0)
 	attch[constant.TOKEN_KEY] = "ori_key"
-	result := filter.Invoke(protocol.NewBaseInvoker(url),
-		invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch))
+	result := filter.Invoke(context.Background(), protocol.NewBaseInvoker(url), invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch))
 	assert.Nil(t, result.Error())
 	assert.Nil(t, result.Result())
 }
@@ -66,8 +68,7 @@ func TestTokenFilter_InvokeEmptyAttach(t *testing.T) {
 		common.WithParams(url.Values{}),
 		common.WithParamsValue(constant.TOKEN_KEY, "ori_key"))
 	attch := make(map[string]string, 0)
-	result := filter.Invoke(protocol.NewBaseInvoker(*url),
-		invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch))
+	result := filter.Invoke(context.Background(), protocol.NewBaseInvoker(*url), invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch))
 	assert.NotNil(t, result.Error())
 }
 
@@ -79,7 +80,7 @@ func TestTokenFilter_InvokeNotEqual(t *testing.T) {
 		common.WithParamsValue(constant.TOKEN_KEY, "ori_key"))
 	attch := make(map[string]string, 0)
 	attch[constant.TOKEN_KEY] = "err_key"
-	result := filter.Invoke(protocol.NewBaseInvoker(*url),
-		invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch))
+	result := filter.Invoke(context.Background(),
+		protocol.NewBaseInvoker(*url), invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch))
 	assert.NotNil(t, result.Error())
 }
diff --git a/filter/impl/tps/impl/tps_limit_fix_window_strategy.go b/filter/filter_impl/tps/tps_limit_fix_window_strategy.go
similarity index 82%
rename from filter/impl/tps/impl/tps_limit_fix_window_strategy.go
rename to filter/filter_impl/tps/tps_limit_fix_window_strategy.go
index 7290619d5551ebe29209e7cfa717bd66442ca2df..a9c2ac15a417ffa6ff8f5b8d78d5c6a94877db30 100644
--- a/filter/impl/tps/impl/tps_limit_fix_window_strategy.go
+++ b/filter/filter_impl/tps/tps_limit_fix_window_strategy.go
@@ -15,28 +15,32 @@
  * limitations under the License.
  */
 
-package impl
+package tps
 
 import (
 	"sync/atomic"
 	"time"
 )
+
 import (
 	"github.com/apache/dubbo-go/common/constant"
 	"github.com/apache/dubbo-go/common/extension"
-	"github.com/apache/dubbo-go/filter/impl/tps"
+	"github.com/apache/dubbo-go/filter"
 )
 
 const (
+	// FixedWindowKey ...
 	FixedWindowKey = "fixedWindow"
 )
 
 func init() {
-	extension.SetTpsLimitStrategy(FixedWindowKey, NewFixedWindowTpsLimitStrategyImpl)
-	extension.SetTpsLimitStrategy(constant.DEFAULT_KEY, NewFixedWindowTpsLimitStrategyImpl)
+	creator := &fixedWindowStrategyCreator{}
+	extension.SetTpsLimitStrategy(FixedWindowKey, creator)
+	extension.SetTpsLimitStrategy(constant.DEFAULT_KEY, creator)
 }
 
 /**
+ * FixedWindowTpsLimitStrategyImpl
  * It's the same as default implementation in Java
  * It's not a thread-safe implementation.
  * It you want to use the thread-safe implementation, please use ThreadSafeFixedWindowTpsLimitStrategyImpl
@@ -61,6 +65,7 @@ type FixedWindowTpsLimitStrategyImpl struct {
 	timestamp int64
 }
 
+// IsAllowable ...
 func (impl *FixedWindowTpsLimitStrategyImpl) IsAllowable() bool {
 
 	current := time.Now().UnixNano()
@@ -75,10 +80,12 @@ func (impl *FixedWindowTpsLimitStrategyImpl) IsAllowable() bool {
 	return atomic.AddInt32(&impl.count, 1) <= impl.rate
 }
 
-func NewFixedWindowTpsLimitStrategyImpl(rate int, interval int) tps.TpsLimitStrategy {
+type fixedWindowStrategyCreator struct{}
+
+func (creator *fixedWindowStrategyCreator) Create(rate int, interval int) filter.TpsLimitStrategy {
 	return &FixedWindowTpsLimitStrategyImpl{
 		rate:      int32(rate),
-		interval:  int64(interval * 1000), // convert to ns
+		interval:  int64(interval) * int64(time.Millisecond), // convert to ns
 		count:     0,
 		timestamp: time.Now().UnixNano(),
 	}
diff --git a/filter/impl/tps/impl/tps_limit_fix_window_strategy_test.go b/filter/filter_impl/tps/tps_limit_fix_window_strategy_test.go
similarity index 88%
rename from filter/impl/tps/impl/tps_limit_fix_window_strategy_test.go
rename to filter/filter_impl/tps/tps_limit_fix_window_strategy_test.go
index 55d0b14b75e69b44cf9ebe3a615e1a05c60d4b41..5eaf2f707dcc9dd6cf325988242623dd5161c1a8 100644
--- a/filter/impl/tps/impl/tps_limit_fix_window_strategy_test.go
+++ b/filter/filter_impl/tps/tps_limit_fix_window_strategy_test.go
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package impl
+package tps
 
 import (
 	"testing"
@@ -27,16 +27,17 @@ import (
 )
 
 func TestFixedWindowTpsLimitStrategyImpl_IsAllowable(t *testing.T) {
-	strategy := NewFixedWindowTpsLimitStrategyImpl(2, 60000)
+	creator := &fixedWindowStrategyCreator{}
+	strategy := creator.Create(2, 60000)
 	assert.True(t, strategy.IsAllowable())
 	assert.True(t, strategy.IsAllowable())
 	assert.False(t, strategy.IsAllowable())
 
-	strategy = NewFixedWindowTpsLimitStrategyImpl(2, 2000)
+	strategy = creator.Create(2, 2000)
 	assert.True(t, strategy.IsAllowable())
 	assert.True(t, strategy.IsAllowable())
 	assert.False(t, strategy.IsAllowable())
-	time.Sleep(time.Duration(2100 * 1000))
+	time.Sleep(2100 * time.Millisecond)
 	assert.True(t, strategy.IsAllowable())
 	assert.True(t, strategy.IsAllowable())
 	assert.False(t, strategy.IsAllowable())
diff --git a/filter/impl/tps/impl/tps_limit_sliding_window_strategy.go b/filter/filter_impl/tps/tps_limit_sliding_window_strategy.go
similarity index 85%
rename from filter/impl/tps/impl/tps_limit_sliding_window_strategy.go
rename to filter/filter_impl/tps/tps_limit_sliding_window_strategy.go
index de98eb7528f541ed57b04309e2c9c74b8310cc64..a781cc7bfbf297d0b9cf84ca0aa9dcfbbef7e14b 100644
--- a/filter/impl/tps/impl/tps_limit_sliding_window_strategy.go
+++ b/filter/filter_impl/tps/tps_limit_sliding_window_strategy.go
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package impl
+package tps
 
 import (
 	"container/list"
@@ -25,14 +25,15 @@ import (
 
 import (
 	"github.com/apache/dubbo-go/common/extension"
-	"github.com/apache/dubbo-go/filter/impl/tps"
+	"github.com/apache/dubbo-go/filter"
 )
 
 func init() {
-	extension.SetTpsLimitStrategy("slidingWindow", NewSlidingWindowTpsLimitStrategyImpl)
+	extension.SetTpsLimitStrategy("slidingWindow", &slidingWindowStrategyCreator{})
 }
 
 /**
+ * SlidingWindowTpsLimitStrategyImpl
  * it's thread-safe.
  * "UserProvider":
  *   registry: "hangzhouzk"
@@ -53,6 +54,7 @@ type SlidingWindowTpsLimitStrategyImpl struct {
 	queue    *list.List
 }
 
+// IsAllowable ...
 func (impl *SlidingWindowTpsLimitStrategyImpl) IsAllowable() bool {
 	impl.mutex.Lock()
 	defer impl.mutex.Unlock()
@@ -80,10 +82,12 @@ func (impl *SlidingWindowTpsLimitStrategyImpl) IsAllowable() bool {
 	return false
 }
 
-func NewSlidingWindowTpsLimitStrategyImpl(rate int, interval int) tps.TpsLimitStrategy {
+type slidingWindowStrategyCreator struct{}
+
+func (creator *slidingWindowStrategyCreator) Create(rate int, interval int) filter.TpsLimitStrategy {
 	return &SlidingWindowTpsLimitStrategyImpl{
 		rate:     rate,
-		interval: int64(interval * 1000),
+		interval: int64(interval) * int64(time.Millisecond),
 		mutex:    &sync.Mutex{},
 		queue:    list.New(),
 	}
diff --git a/filter/impl/tps/impl/tps_limit_sliding_window_strategy_test.go b/filter/filter_impl/tps/tps_limit_sliding_window_strategy_test.go
similarity index 86%
rename from filter/impl/tps/impl/tps_limit_sliding_window_strategy_test.go
rename to filter/filter_impl/tps/tps_limit_sliding_window_strategy_test.go
index 1d0187fa201741a32f109abe51ce63b5568e4cc4..57342d1c443993c49c6124f0ef28dae5ebb203e8 100644
--- a/filter/impl/tps/impl/tps_limit_sliding_window_strategy_test.go
+++ b/filter/filter_impl/tps/tps_limit_sliding_window_strategy_test.go
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package impl
+package tps
 
 import (
 	"testing"
@@ -27,18 +27,19 @@ import (
 )
 
 func TestSlidingWindowTpsLimitStrategyImpl_IsAllowable(t *testing.T) {
-	strategy := NewSlidingWindowTpsLimitStrategyImpl(2, 60000)
+	creator := &slidingWindowStrategyCreator{}
+	strategy := creator.Create(2, 60000)
 	assert.True(t, strategy.IsAllowable())
 	assert.True(t, strategy.IsAllowable())
 	assert.False(t, strategy.IsAllowable())
-	time.Sleep(time.Duration(2100 * 1000))
+	time.Sleep(2100 * time.Millisecond)
 	assert.False(t, strategy.IsAllowable())
 
-	strategy = NewSlidingWindowTpsLimitStrategyImpl(2, 2000)
+	strategy = creator.Create(2, 2000)
 	assert.True(t, strategy.IsAllowable())
 	assert.True(t, strategy.IsAllowable())
 	assert.False(t, strategy.IsAllowable())
-	time.Sleep(time.Duration(2100 * 1000))
+	time.Sleep(2100 * time.Millisecond)
 	assert.True(t, strategy.IsAllowable())
 	assert.True(t, strategy.IsAllowable())
 	assert.False(t, strategy.IsAllowable())
diff --git a/filter/impl/tps/impl/tps_limit_strategy_mock.go b/filter/filter_impl/tps/tps_limit_strategy_mock.go
similarity index 99%
rename from filter/impl/tps/impl/tps_limit_strategy_mock.go
rename to filter/filter_impl/tps/tps_limit_strategy_mock.go
index a653fb287a2d89d8c6151889ca14b4b7b4832505..72c658fb9a5d48b6080900a4645d318dfd2b0c21 100644
--- a/filter/impl/tps/impl/tps_limit_strategy_mock.go
+++ b/filter/filter_impl/tps/tps_limit_strategy_mock.go
@@ -18,7 +18,7 @@
 // Source: tps_limit_strategy.go
 
 // Package filter is a generated GoMock package.
-package impl
+package tps
 
 import (
 	gomock "github.com/golang/mock/gomock"
diff --git a/filter/impl/tps/impl/tps_limit_thread_safe_fix_window_strategy.go b/filter/filter_impl/tps/tps_limit_thread_safe_fix_window_strategy.go
similarity index 75%
rename from filter/impl/tps/impl/tps_limit_thread_safe_fix_window_strategy.go
rename to filter/filter_impl/tps/tps_limit_thread_safe_fix_window_strategy.go
index 5f43e8c3bf6c1db268282a0fb5d9ecc55fb357df..16624836e6397df5adda3f2aa5a80966721a97fb 100644
--- a/filter/impl/tps/impl/tps_limit_thread_safe_fix_window_strategy.go
+++ b/filter/filter_impl/tps/tps_limit_thread_safe_fix_window_strategy.go
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package impl
+package tps
 
 import (
 	"sync"
@@ -23,14 +23,17 @@ import (
 
 import (
 	"github.com/apache/dubbo-go/common/extension"
-	"github.com/apache/dubbo-go/filter/impl/tps"
+	"github.com/apache/dubbo-go/filter"
 )
 
 func init() {
-	extension.SetTpsLimitStrategy("threadSafeFixedWindow", NewThreadSafeFixedWindowTpsLimitStrategyImpl)
+	extension.SetTpsLimitStrategy("threadSafeFixedWindow", &threadSafeFixedWindowStrategyCreator{
+		fixedWindowStrategyCreator: &fixedWindowStrategyCreator{},
+	})
 }
 
 /**
+ * ThreadSafeFixedWindowTpsLimitStrategyImpl
  * it's the thread-safe implementation.
  * Also, it's a thread-safe decorator of FixedWindowTpsLimitStrategyImpl
  * "UserProvider":
@@ -50,14 +53,19 @@ type ThreadSafeFixedWindowTpsLimitStrategyImpl struct {
 	fixedWindow *FixedWindowTpsLimitStrategyImpl
 }
 
+// IsAllowable ...
 func (impl *ThreadSafeFixedWindowTpsLimitStrategyImpl) IsAllowable() bool {
 	impl.mutex.Lock()
 	defer impl.mutex.Unlock()
 	return impl.fixedWindow.IsAllowable()
 }
 
-func NewThreadSafeFixedWindowTpsLimitStrategyImpl(rate int, interval int) tps.TpsLimitStrategy {
-	fixedWindowStrategy := NewFixedWindowTpsLimitStrategyImpl(rate, interval).(*FixedWindowTpsLimitStrategyImpl)
+type threadSafeFixedWindowStrategyCreator struct {
+	fixedWindowStrategyCreator *fixedWindowStrategyCreator
+}
+
+func (creator *threadSafeFixedWindowStrategyCreator) Create(rate int, interval int) filter.TpsLimitStrategy {
+	fixedWindowStrategy := creator.fixedWindowStrategyCreator.Create(rate, interval).(*FixedWindowTpsLimitStrategyImpl)
 	return &ThreadSafeFixedWindowTpsLimitStrategyImpl{
 		fixedWindow: fixedWindowStrategy,
 		mutex:       &sync.Mutex{},
diff --git a/filter/impl/tps/impl/tps_limit_thread_safe_fix_window_strategy_test.go b/filter/filter_impl/tps/tps_limit_thread_safe_fix_window_strategy_test.go
similarity index 87%
rename from filter/impl/tps/impl/tps_limit_thread_safe_fix_window_strategy_test.go
rename to filter/filter_impl/tps/tps_limit_thread_safe_fix_window_strategy_test.go
index fea93dfa3b8ef49034e952619f617bf87e4be879..90cd15201cd71aafcc50a1dfb801ece7a5dee26a 100644
--- a/filter/impl/tps/impl/tps_limit_thread_safe_fix_window_strategy_test.go
+++ b/filter/filter_impl/tps/tps_limit_thread_safe_fix_window_strategy_test.go
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package impl
+package tps
 
 import (
 	"testing"
@@ -27,16 +27,17 @@ import (
 )
 
 func TestThreadSafeFixedWindowTpsLimitStrategyImpl_IsAllowable(t *testing.T) {
-	strategy := NewThreadSafeFixedWindowTpsLimitStrategyImpl(2, 60000)
+	creator := &threadSafeFixedWindowStrategyCreator{}
+	strategy := creator.Create(2, 60000)
 	assert.True(t, strategy.IsAllowable())
 	assert.True(t, strategy.IsAllowable())
 	assert.False(t, strategy.IsAllowable())
 
-	strategy = NewThreadSafeFixedWindowTpsLimitStrategyImpl(2, 2000)
+	strategy = creator.Create(2, 2000)
 	assert.True(t, strategy.IsAllowable())
 	assert.True(t, strategy.IsAllowable())
 	assert.False(t, strategy.IsAllowable())
-	time.Sleep(time.Duration(2100 * 1000))
+	time.Sleep(2100 * time.Millisecond)
 	assert.True(t, strategy.IsAllowable())
 	assert.True(t, strategy.IsAllowable())
 	assert.False(t, strategy.IsAllowable())
diff --git a/filter/impl/tps/impl/tps_limiter_method_service.go b/filter/filter_impl/tps/tps_limiter_method_service.go
similarity index 94%
rename from filter/impl/tps/impl/tps_limiter_method_service.go
rename to filter/filter_impl/tps/tps_limiter_method_service.go
index 3faf0d6e672315bb83e951be45a7f93c4ac508ef..7fe8de9237b82415a09083c2be59df5e232ecaf0 100644
--- a/filter/impl/tps/impl/tps_limiter_method_service.go
+++ b/filter/filter_impl/tps/tps_limiter_method_service.go
@@ -14,7 +14,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package impl
+
+package tps
 
 import (
 	"fmt"
@@ -30,11 +31,13 @@ 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/filter/impl/tps"
+	"github.com/apache/dubbo-go/filter"
 	"github.com/apache/dubbo-go/protocol"
 )
 
-const name = "method-service"
+const (
+	name = "method-service"
+)
 
 func init() {
 	extension.SetTpsLimiter(constant.DEFAULT_KEY, GetMethodServiceTpsLimiter)
@@ -42,6 +45,7 @@ func init() {
 }
 
 /**
+ * MethodServiceTpsLimiterImpl
  * This implementation allows developer to config both method-level and service-level tps limiter.
  * for example:
  * "UserProvider":
@@ -111,6 +115,7 @@ type MethodServiceTpsLimiterImpl struct {
 	tpsState *concurrent.Map
 }
 
+// IsAllowable ...
 func (limiter MethodServiceTpsLimiterImpl) IsAllowable(url common.URL, invocation protocol.Invocation) bool {
 
 	methodConfigPrefix := "methods." + invocation.MethodName() + "."
@@ -127,7 +132,7 @@ func (limiter MethodServiceTpsLimiterImpl) IsAllowable(url common.URL, invocatio
 
 	limitState, found := limiter.tpsState.Load(limitTarget)
 	if found {
-		return limitState.(tps.TpsLimitStrategy).IsAllowable()
+		return limitState.(filter.TpsLimitStrategy).IsAllowable()
 	}
 
 	limitRate := getLimitConfig(methodLimitRateConfig, url, invocation,
@@ -148,8 +153,8 @@ func (limiter MethodServiceTpsLimiterImpl) IsAllowable(url common.URL, invocatio
 	limitStrategyConfig := url.GetParam(methodConfigPrefix+constant.TPS_LIMIT_STRATEGY_KEY,
 		url.GetParam(constant.TPS_LIMIT_STRATEGY_KEY, constant.DEFAULT_KEY))
 	limitStateCreator := extension.GetTpsLimitStrategyCreator(limitStrategyConfig)
-	limitState, _ = limiter.tpsState.LoadOrStore(limitTarget, limitStateCreator(int(limitRate), int(limitInterval)))
-	return limitState.(tps.TpsLimitStrategy).IsAllowable()
+	limitState, _ = limiter.tpsState.LoadOrStore(limitTarget, limitStateCreator.Create(int(limitRate), int(limitInterval)))
+	return limitState.(filter.TpsLimitStrategy).IsAllowable()
 }
 
 func getLimitConfig(methodLevelConfig string,
@@ -178,7 +183,8 @@ func getLimitConfig(methodLevelConfig string,
 var methodServiceTpsLimiterInstance *MethodServiceTpsLimiterImpl
 var methodServiceTpsLimiterOnce sync.Once
 
-func GetMethodServiceTpsLimiter() tps.TpsLimiter {
+// GetMethodServiceTpsLimiter ...
+func GetMethodServiceTpsLimiter() filter.TpsLimiter {
 	methodServiceTpsLimiterOnce.Do(func() {
 		methodServiceTpsLimiterInstance = &MethodServiceTpsLimiterImpl{
 			tpsState: concurrent.NewMap(),
diff --git a/filter/impl/tps/impl/tps_limiter_method_service_test.go b/filter/filter_impl/tps/tps_limiter_method_service_test.go
similarity index 83%
rename from filter/impl/tps/impl/tps_limiter_method_service_test.go
rename to filter/filter_impl/tps/tps_limiter_method_service_test.go
index 006e9463871061488f696366d251c54fb8cefef5..441224a3e35147b85c3553871dcaa1fefd09db04 100644
--- a/filter/impl/tps/impl/tps_limiter_method_service_test.go
+++ b/filter/filter_impl/tps/tps_limiter_method_service_test.go
@@ -15,13 +15,14 @@
  * limitations under the License.
  */
 
-package impl
+package tps
 
 import (
 	"net/url"
 	"testing"
 )
 import (
+	"github.com/apache/dubbo-go/filter"
 	"github.com/golang/mock/gomock"
 	"github.com/stretchr/testify/assert"
 )
@@ -30,7 +31,6 @@ 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/filter/impl/tps"
 	"github.com/apache/dubbo-go/protocol/invocation"
 )
 
@@ -48,10 +48,12 @@ func TestMethodServiceTpsLimiterImpl_IsAllowable_Only_Service_Level(t *testing.T
 
 	mockStrategyImpl := NewMockTpsLimitStrategy(ctrl)
 	mockStrategyImpl.EXPECT().IsAllowable().Return(true).Times(1)
-	extension.SetTpsLimitStrategy(constant.DEFAULT_KEY, func(rate int, interval int) tps.TpsLimitStrategy {
-		assert.Equal(t, 20, rate)
-		assert.Equal(t, 60000, interval)
-		return mockStrategyImpl
+
+	extension.SetTpsLimitStrategy(constant.DEFAULT_KEY, &mockStrategyCreator{
+		rate:     20,
+		interval: 60000,
+		t:        t,
+		strategy: mockStrategyImpl,
 	})
 
 	limiter := GetMethodServiceTpsLimiter()
@@ -95,10 +97,12 @@ func TestMethodServiceTpsLimiterImpl_IsAllowable_Method_Level_Override(t *testin
 
 	mockStrategyImpl := NewMockTpsLimitStrategy(ctrl)
 	mockStrategyImpl.EXPECT().IsAllowable().Return(true).Times(1)
-	extension.SetTpsLimitStrategy(constant.DEFAULT_KEY, func(rate int, interval int) tps.TpsLimitStrategy {
-		assert.Equal(t, 40, rate)
-		assert.Equal(t, 7000, interval)
-		return mockStrategyImpl
+
+	extension.SetTpsLimitStrategy(constant.DEFAULT_KEY, &mockStrategyCreator{
+		rate:     40,
+		interval: 7000,
+		t:        t,
+		strategy: mockStrategyImpl,
 	})
 
 	limiter := GetMethodServiceTpsLimiter()
@@ -123,13 +127,28 @@ func TestMethodServiceTpsLimiterImpl_IsAllowable_Both_Method_And_Service(t *test
 
 	mockStrategyImpl := NewMockTpsLimitStrategy(ctrl)
 	mockStrategyImpl.EXPECT().IsAllowable().Return(true).Times(1)
-	extension.SetTpsLimitStrategy(constant.DEFAULT_KEY, func(rate int, interval int) tps.TpsLimitStrategy {
-		assert.Equal(t, 40, rate)
-		assert.Equal(t, 3000, interval)
-		return mockStrategyImpl
+
+	extension.SetTpsLimitStrategy(constant.DEFAULT_KEY, &mockStrategyCreator{
+		rate:     40,
+		interval: 3000,
+		t:        t,
+		strategy: mockStrategyImpl,
 	})
 
 	limiter := GetMethodServiceTpsLimiter()
 	result := limiter.IsAllowable(*invokeUrl, invoc)
 	assert.True(t, result)
 }
+
+type mockStrategyCreator struct {
+	rate     int
+	interval int
+	t        *testing.T
+	strategy filter.TpsLimitStrategy
+}
+
+func (creator *mockStrategyCreator) Create(rate int, interval int) filter.TpsLimitStrategy {
+	assert.Equal(creator.t, creator.rate, rate)
+	assert.Equal(creator.t, creator.interval, interval)
+	return creator.strategy
+}
diff --git a/filter/impl/tps/impl/tps_limiter_mock.go b/filter/filter_impl/tps/tps_limiter_mock.go
similarity index 98%
rename from filter/impl/tps/impl/tps_limiter_mock.go
rename to filter/filter_impl/tps/tps_limiter_mock.go
index ff2f984e13a8617aefdbef0137ed8feca1bfd4ba..463b0988acbeb17a967c9803337a61c4914bce42 100644
--- a/filter/impl/tps/impl/tps_limiter_mock.go
+++ b/filter/filter_impl/tps/tps_limiter_mock.go
@@ -18,13 +18,19 @@
 // Source: tps_limiter.go
 
 // Package filter is a generated GoMock package.
-package impl
+package tps
+
+import (
+	reflect "reflect"
+)
+
+import (
+	gomock "github.com/golang/mock/gomock"
+)
 
 import (
 	common "github.com/apache/dubbo-go/common"
 	protocol "github.com/apache/dubbo-go/protocol"
-	gomock "github.com/golang/mock/gomock"
-	reflect "reflect"
 )
 
 // MockTpsLimiter is a mock of TpsLimiter interface
diff --git a/filter/impl/tps_limit_filter.go b/filter/filter_impl/tps_limit_filter.go
similarity index 80%
rename from filter/impl/tps_limit_filter.go
rename to filter/filter_impl/tps_limit_filter.go
index 3cb7381c8616abd61fe2ac306b59694a92715dda..fa78288f9678d67d0eb0d025a83b75493f7fda80 100644
--- a/filter/impl/tps_limit_filter.go
+++ b/filter/filter_impl/tps_limit_filter.go
@@ -15,19 +15,23 @@
  * limitations under the License.
  */
 
-package impl
+package filter_impl
 
+import (
+	"context"
+)
 import (
 	"github.com/apache/dubbo-go/common/constant"
 	"github.com/apache/dubbo-go/common/extension"
 	"github.com/apache/dubbo-go/common/logger"
 	"github.com/apache/dubbo-go/filter"
-	_ "github.com/apache/dubbo-go/filter/common/impl"
-	_ "github.com/apache/dubbo-go/filter/impl/tps/impl"
+	_ "github.com/apache/dubbo-go/filter/filter_impl/tps"
+	_ "github.com/apache/dubbo-go/filter/handler"
 	"github.com/apache/dubbo-go/protocol"
 )
 
 const (
+	// TpsLimitFilterKey key
 	TpsLimitFilterKey = "tps"
 )
 
@@ -36,6 +40,7 @@ func init() {
 }
 
 /**
+ * TpsLimitFilter
  * if you wish to use the TpsLimiter, please add the configuration into your service provider configuration:
  * for example:
  * "UserProvider":
@@ -51,25 +56,29 @@ func init() {
 type TpsLimitFilter struct {
 }
 
-func (t TpsLimitFilter) Invoke(invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
+// Invoke ...
+func (t TpsLimitFilter) Invoke(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
 	url := invoker.GetUrl()
 	tpsLimiter := url.GetParam(constant.TPS_LIMITER_KEY, "")
 	rejectedExeHandler := url.GetParam(constant.TPS_REJECTED_EXECUTION_HANDLER_KEY, constant.DEFAULT_KEY)
 	if len(tpsLimiter) > 0 {
 		allow := extension.GetTpsLimiter(tpsLimiter).IsAllowable(invoker.GetUrl(), invocation)
 		if allow {
-			return invoker.Invoke(invocation)
+			return invoker.Invoke(ctx, invocation)
 		}
 		logger.Errorf("The invocation was rejected due to over the tps limitation, url: %s ", url.String())
 		return extension.GetRejectedExecutionHandler(rejectedExeHandler).RejectedExecution(url, invocation)
 	}
-	return invoker.Invoke(invocation)
+	return invoker.Invoke(ctx, invocation)
 }
 
-func (t TpsLimitFilter) OnResponse(result protocol.Result, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
+// OnResponse ...
+func (t TpsLimitFilter) OnResponse(_ context.Context, result protocol.Result, _ protocol.Invoker,
+	_ protocol.Invocation) protocol.Result {
 	return result
 }
 
+// GetTpsLimitFilter ...
 func GetTpsLimitFilter() filter.Filter {
 	return &TpsLimitFilter{}
 }
diff --git a/filter/impl/tps_limit_filter_test.go b/filter/filter_impl/tps_limit_filter_test.go
similarity index 73%
rename from filter/impl/tps_limit_filter_test.go
rename to filter/filter_impl/tps_limit_filter_test.go
index debdbd00dec97ed67d789bfc45103993c014ab4a..cc423ae1e5f3589dd60b0c8655f1123c290f0ffc 100644
--- a/filter/impl/tps_limit_filter_test.go
+++ b/filter/filter_impl/tps_limit_filter_test.go
@@ -15,14 +15,18 @@
  * limitations under the License.
  */
 
-package impl
+package filter_impl
 
 import (
+	"context"
 	"net/url"
 	"testing"
 )
 
 import (
+	"github.com/apache/dubbo-go/filter"
+	"github.com/apache/dubbo-go/filter/filter_impl/tps"
+	common2 "github.com/apache/dubbo-go/filter/handler"
 	"github.com/golang/mock/gomock"
 	"github.com/stretchr/testify/assert"
 )
@@ -31,10 +35,6 @@ import (
 	"github.com/apache/dubbo-go/common"
 	"github.com/apache/dubbo-go/common/constant"
 	"github.com/apache/dubbo-go/common/extension"
-	filterCommon "github.com/apache/dubbo-go/filter/common"
-	filterCommonImpl "github.com/apache/dubbo-go/filter/common/impl"
-	"github.com/apache/dubbo-go/filter/impl/tps"
-	"github.com/apache/dubbo-go/filter/impl/tps/impl"
 	"github.com/apache/dubbo-go/protocol"
 	"github.com/apache/dubbo-go/protocol/invocation"
 )
@@ -46,8 +46,10 @@ func TestTpsLimitFilter_Invoke_With_No_TpsLimiter(t *testing.T) {
 		common.WithParamsValue(constant.TPS_LIMITER_KEY, ""))
 	attch := make(map[string]string, 0)
 
-	result := tpsFilter.Invoke(protocol.NewBaseInvoker(*invokeUrl),
-		invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch))
+	result := tpsFilter.Invoke(context.Background(),
+		protocol.NewBaseInvoker(*invokeUrl),
+		invocation.NewRPCInvocation("MethodName",
+			[]interface{}{"OK"}, attch))
 	assert.Nil(t, result.Error())
 	assert.Nil(t, result.Result())
 
@@ -56,9 +58,9 @@ func TestTpsLimitFilter_Invoke_With_No_TpsLimiter(t *testing.T) {
 func TestGenericFilter_Invoke_With_Default_TpsLimiter(t *testing.T) {
 	ctrl := gomock.NewController(t)
 	defer ctrl.Finish()
-	mockLimiter := impl.NewMockTpsLimiter(ctrl)
+	mockLimiter := tps.NewMockTpsLimiter(ctrl)
 	mockLimiter.EXPECT().IsAllowable(gomock.Any(), gomock.Any()).Return(true).Times(1)
-	extension.SetTpsLimiter(constant.DEFAULT_KEY, func() tps.TpsLimiter {
+	extension.SetTpsLimiter(constant.DEFAULT_KEY, func() filter.TpsLimiter {
 		return mockLimiter
 	})
 
@@ -68,8 +70,10 @@ func TestGenericFilter_Invoke_With_Default_TpsLimiter(t *testing.T) {
 		common.WithParamsValue(constant.TPS_LIMITER_KEY, constant.DEFAULT_KEY))
 	attch := make(map[string]string, 0)
 
-	result := tpsFilter.Invoke(protocol.NewBaseInvoker(*invokeUrl),
-		invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch))
+	result := tpsFilter.Invoke(context.Background(),
+		protocol.NewBaseInvoker(*invokeUrl),
+		invocation.NewRPCInvocation("MethodName",
+			[]interface{}{"OK"}, attch))
 	assert.Nil(t, result.Error())
 	assert.Nil(t, result.Result())
 }
@@ -77,17 +81,17 @@ func TestGenericFilter_Invoke_With_Default_TpsLimiter(t *testing.T) {
 func TestGenericFilter_Invoke_With_Default_TpsLimiter_Not_Allow(t *testing.T) {
 	ctrl := gomock.NewController(t)
 	defer ctrl.Finish()
-	mockLimiter := impl.NewMockTpsLimiter(ctrl)
+	mockLimiter := tps.NewMockTpsLimiter(ctrl)
 	mockLimiter.EXPECT().IsAllowable(gomock.Any(), gomock.Any()).Return(false).Times(1)
-	extension.SetTpsLimiter(constant.DEFAULT_KEY, func() tps.TpsLimiter {
+	extension.SetTpsLimiter(constant.DEFAULT_KEY, func() filter.TpsLimiter {
 		return mockLimiter
 	})
 
 	mockResult := &protocol.RPCResult{}
-	mockRejectedHandler := filterCommonImpl.NewMockRejectedExecutionHandler(ctrl)
+	mockRejectedHandler := common2.NewMockRejectedExecutionHandler(ctrl)
 	mockRejectedHandler.EXPECT().RejectedExecution(gomock.Any(), gomock.Any()).Return(mockResult).Times(1)
 
-	extension.SetRejectedExecutionHandler(constant.DEFAULT_KEY, func() filterCommon.RejectedExecutionHandler {
+	extension.SetRejectedExecutionHandler(constant.DEFAULT_KEY, func() filter.RejectedExecutionHandler {
 		return mockRejectedHandler
 	})
 
@@ -97,8 +101,8 @@ func TestGenericFilter_Invoke_With_Default_TpsLimiter_Not_Allow(t *testing.T) {
 		common.WithParamsValue(constant.TPS_LIMITER_KEY, constant.DEFAULT_KEY))
 	attch := make(map[string]string, 0)
 
-	result := tpsFilter.Invoke(protocol.NewBaseInvoker(*invokeUrl),
-		invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch))
+	result := tpsFilter.Invoke(context.Background(),
+		protocol.NewBaseInvoker(*invokeUrl), invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch))
 	assert.Nil(t, result.Error())
 	assert.Nil(t, result.Result())
 }
diff --git a/filter/filter_impl/tracing_filter.go b/filter/filter_impl/tracing_filter.go
new file mode 100644
index 0000000000000000000000000000000000000000..b8058aa601af98b5416da882321546675459c413
--- /dev/null
+++ b/filter/filter_impl/tracing_filter.go
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package filter_impl
+
+import (
+	"context"
+)
+
+import (
+	"github.com/opentracing/opentracing-go"
+	"github.com/opentracing/opentracing-go/log"
+)
+
+import (
+	"github.com/apache/dubbo-go/common/constant"
+	"github.com/apache/dubbo-go/common/extension"
+	"github.com/apache/dubbo-go/filter"
+	"github.com/apache/dubbo-go/protocol"
+)
+
+const (
+	tracingFilterName = "tracing"
+)
+
+// this should be executed before users set their own Tracer
+func init() {
+	extension.SetFilter(tracingFilterName, newTracingFilter)
+	opentracing.SetGlobalTracer(opentracing.NoopTracer{})
+}
+
+var (
+	errorKey   = "ErrorMsg"
+	successKey = "Success"
+)
+
+// if you wish to using opentracing, please add the this filter into your filter attribute in your configure file.
+// notice that this could be used in both client-side and server-side.
+type tracingFilter struct {
+}
+
+func (tf *tracingFilter) Invoke(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
+	var (
+		spanCtx context.Context
+		span    opentracing.Span
+	)
+	operationName := invoker.GetUrl().ServiceKey() + "#" + invocation.MethodName()
+
+	wiredCtx := ctx.Value(constant.TRACING_REMOTE_SPAN_CTX)
+	preSpan := opentracing.SpanFromContext(ctx)
+
+	if preSpan != nil {
+		// it means that someone already create a span to trace, so we use the span to be the parent span
+		span = opentracing.StartSpan(operationName, opentracing.ChildOf(preSpan.Context()))
+		spanCtx = opentracing.ContextWithSpan(ctx, span)
+
+	} else if wiredCtx != nil {
+
+		// it means that there has a remote span, usually from client side. so we use this as the parent
+		span = opentracing.StartSpan(operationName, opentracing.ChildOf(wiredCtx.(opentracing.SpanContext)))
+		spanCtx = opentracing.ContextWithSpan(ctx, span)
+	} else {
+		// it means that there is not any span, so we create a span as the root span.
+		span, spanCtx = opentracing.StartSpanFromContext(ctx, operationName)
+	}
+
+	defer func() {
+		span.Finish()
+	}()
+
+	result := invoker.Invoke(spanCtx, invocation)
+	span.SetTag(successKey, result.Error() != nil)
+	if result.Error() != nil {
+		span.LogFields(log.String(errorKey, result.Error().Error()))
+	}
+	return result
+}
+
+func (tf *tracingFilter) OnResponse(ctx context.Context, result protocol.Result,
+	invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
+	return result
+}
+
+var (
+	tracingFilterInstance *tracingFilter
+)
+
+func newTracingFilter() filter.Filter {
+	if tracingFilterInstance == nil {
+		tracingFilterInstance = &tracingFilter{}
+	}
+	return tracingFilterInstance
+}
diff --git a/filter/filter_impl/tracing_filter_test.go b/filter/filter_impl/tracing_filter_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..a51692dddcc3400032650f4953eb1e28fb047709
--- /dev/null
+++ b/filter/filter_impl/tracing_filter_test.go
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package filter_impl
+
+import (
+	"context"
+	"testing"
+)
+
+import (
+	"github.com/opentracing/opentracing-go"
+)
+
+import (
+	"github.com/apache/dubbo-go/common/constant"
+)
+
+import (
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/protocol"
+	"github.com/apache/dubbo-go/protocol/invocation"
+)
+
+func TestTracingFilter_Invoke(t *testing.T) {
+	url, _ := common.NewURL(
+		"dubbo://:20000/UserProvider?app.version=0.0.1&application=BDTService&bean.name=UserProvider" +
+			"&cluster=failover&environment=dev&group=&interface=com.ikurento.user.UserProvider&loadbalance=random&methods.GetUser." +
+			"loadbalance=random&methods.GetUser.retries=1&methods.GetUser.weight=0&module=dubbogo+user-info+server&name=" +
+			"BDTService&organization=ikurento.com&owner=ZX&registry.role=3&retries=&" +
+			"service.filter=echo%2Ctoken%2Caccesslog&timestamp=1569153406&token=934804bf-b007-4174-94eb-96e3e1d60cc7&version=&warmup=100")
+	invoker := protocol.NewBaseInvoker(url)
+
+	attach := make(map[string]string, 10)
+	inv := invocation.NewRPCInvocation("MethodName", []interface{}{"OK", "Hello"}, attach)
+	ctx := context.Background()
+	tf := newTracingFilter()
+
+	// do not has any span
+	tf.Invoke(ctx, invoker, inv)
+
+	span, ctx := opentracing.StartSpanFromContext(ctx, "Test-Operation")
+	defer span.Finish()
+
+	// has previous span
+	tf.Invoke(ctx, invoker, inv)
+
+	// has remote ctx
+	ctx = context.WithValue(context.Background(), constant.TRACING_REMOTE_SPAN_CTX, span.Context())
+	tf.Invoke(ctx, invoker, inv)
+}
diff --git a/filter/common/impl/rejected_execution_handler_mock.go b/filter/handler/rejected_execution_handler_mock.go
similarity index 98%
rename from filter/common/impl/rejected_execution_handler_mock.go
rename to filter/handler/rejected_execution_handler_mock.go
index 2f7869d61ea2cf4cf8e490dd004ab086b9492132..a5bef63b3729a7b04d911c9844320aa778ac357a 100644
--- a/filter/common/impl/rejected_execution_handler_mock.go
+++ b/filter/handler/rejected_execution_handler_mock.go
@@ -18,13 +18,19 @@
 // Source: rejected_execution_handler.go
 
 // Package filter is a generated GoMock package.
-package impl
+package handler
+
+import (
+	reflect "reflect"
+)
+
+import (
+	gomock "github.com/golang/mock/gomock"
+)
 
 import (
 	common "github.com/apache/dubbo-go/common"
 	protocol "github.com/apache/dubbo-go/protocol"
-	gomock "github.com/golang/mock/gomock"
-	reflect "reflect"
 )
 
 // MockRejectedExecutionHandler is a mock of RejectedExecutionHandler interface
diff --git a/filter/common/impl/rejected_execution_handler_only_log.go b/filter/handler/rejected_execution_handler_only_log.go
similarity index 85%
rename from filter/common/impl/rejected_execution_handler_only_log.go
rename to filter/handler/rejected_execution_handler_only_log.go
index 8943433af1ba908fc834740163df78e3b2b6443a..0f9003c7df2165a2f3a364a5afc47f578db1d243 100644
--- a/filter/common/impl/rejected_execution_handler_only_log.go
+++ b/filter/handler/rejected_execution_handler_only_log.go
@@ -15,9 +15,10 @@
  * limitations under the License.
  */
 
-package impl
+package handler
 
 import (
+	"github.com/apache/dubbo-go/filter"
 	"sync"
 )
 
@@ -26,11 +27,13 @@ import (
 	"github.com/apache/dubbo-go/common/constant"
 	"github.com/apache/dubbo-go/common/extension"
 	"github.com/apache/dubbo-go/common/logger"
-	filterCommon "github.com/apache/dubbo-go/filter/common"
 	"github.com/apache/dubbo-go/protocol"
 )
 
-const HandlerName = "log"
+const (
+	// HandlerName handler name
+	HandlerName = "log"
+)
 
 func init() {
 	extension.SetRejectedExecutionHandler(HandlerName, GetOnlyLogRejectedExecutionHandler)
@@ -41,6 +44,7 @@ var onlyLogHandlerInstance *OnlyLogRejectedExecutionHandler
 var onlyLogHandlerOnce sync.Once
 
 /**
+ * OnlyLogRejectedExecutionHandler
  * This implementation only logs the invocation info.
  * it always return en error inside the result.
  * "UserProvider":
@@ -56,12 +60,16 @@ var onlyLogHandlerOnce sync.Once
 type OnlyLogRejectedExecutionHandler struct {
 }
 
-func (handler *OnlyLogRejectedExecutionHandler) RejectedExecution(url common.URL, invocation protocol.Invocation) protocol.Result {
+// RejectedExecution ...
+func (handler *OnlyLogRejectedExecutionHandler) RejectedExecution(url common.URL,
+	_ protocol.Invocation) protocol.Result {
+
 	logger.Errorf("The invocation was rejected. url: %s", url.String())
 	return &protocol.RPCResult{}
 }
 
-func GetOnlyLogRejectedExecutionHandler() filterCommon.RejectedExecutionHandler {
+// GetOnlyLogRejectedExecutionHandler ...
+func GetOnlyLogRejectedExecutionHandler() filter.RejectedExecutionHandler {
 	onlyLogHandlerOnce.Do(func() {
 		onlyLogHandlerInstance = &OnlyLogRejectedExecutionHandler{}
 	})
diff --git a/filter/common/impl/rejected_execution_handler_only_log_test.go b/filter/handler/rejected_execution_handler_only_log_test.go
similarity index 98%
rename from filter/common/impl/rejected_execution_handler_only_log_test.go
rename to filter/handler/rejected_execution_handler_only_log_test.go
index da54d8a106338dd4f21f9b01e66b031e3c311e01..409f09f61bd958992749231fca045b54601fc627 100644
--- a/filter/common/impl/rejected_execution_handler_only_log_test.go
+++ b/filter/handler/rejected_execution_handler_only_log_test.go
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package impl
+package handler
 
 import (
 	"net/url"
diff --git a/filter/common/rejected_execution_handler.go b/filter/rejected_execution_handler.go
similarity index 97%
rename from filter/common/rejected_execution_handler.go
rename to filter/rejected_execution_handler.go
index b993b8444c14c13ce9a8861c113dc02ca5fd335a..caeea1db6631d0968fd58f59f9577ee9272f3ca0 100644
--- a/filter/common/rejected_execution_handler.go
+++ b/filter/rejected_execution_handler.go
@@ -14,7 +14,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package common
+
+package filter
 
 import (
 	"github.com/apache/dubbo-go/common"
@@ -22,6 +23,7 @@ import (
 )
 
 /**
+ * RejectedExecutionHandler
  * If the invocation cannot pass any validation in filter, like ExecuteLimitFilter and TpsLimitFilter,
  * the implementation will be used.
  * The common case is that sometimes you want to return the default value when the request was rejected.
diff --git a/filter/impl/tps/tps_limit_strategy.go b/filter/tps_limit_strategy.go
similarity index 89%
rename from filter/impl/tps/tps_limit_strategy.go
rename to filter/tps_limit_strategy.go
index d1af85b464ca3cbb500100b895cdc0badff24898..5edf32ce1912642c7ad0ea0b3f6144b45c267eb4 100644
--- a/filter/impl/tps/tps_limit_strategy.go
+++ b/filter/tps_limit_strategy.go
@@ -15,9 +15,10 @@
  * limitations under the License.
  */
 
-package tps
+package filter
 
 /*
+ * TpsLimitStrategy
  * please register your implementation by invoking SetTpsLimitStrategy
  * "UserProvider":
  *   registry: "hangzhouzk"
@@ -34,3 +35,8 @@ package tps
 type TpsLimitStrategy interface {
 	IsAllowable() bool
 }
+
+// TpsLimitStrategyCreator ...
+type TpsLimitStrategyCreator interface {
+	Create(rate int, interval int) TpsLimitStrategy
+}
diff --git a/filter/impl/tps/tps_limiter.go b/filter/tps_limiter.go
similarity index 97%
rename from filter/impl/tps/tps_limiter.go
rename to filter/tps_limiter.go
index 0622a957a8ba14fdebb52ff44ecf72da17703163..dbc9f76838a4406b4788e7757453098613253d58 100644
--- a/filter/impl/tps/tps_limiter.go
+++ b/filter/tps_limiter.go
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package tps
+package filter
 
 import (
 	"github.com/apache/dubbo-go/common"
@@ -23,6 +23,7 @@ import (
 )
 
 /*
+ * TpsLimiter
  * please register your implementation by invoking SetTpsLimiter
  * The usage, for example:
  * "UserProvider":
diff --git a/go.mod b/go.mod
index fb972ebd038b9272fac94518f382d42e57afbb16..b977df68c3df370280dd47f59d6f4577d01b908c 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.2.5-0.20191029001541-894e45c9aaaa
+	github.com/apache/dubbo-go-hessian2 v1.3.1-0.20200302092433-6ae5479d93a3
 	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,12 +12,14 @@ 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.1
-	github.com/dubbogo/gost v1.3.0
+	github.com/dubbogo/getty v1.3.3
+	github.com/dubbogo/go-zookeeper v1.0.0
+	github.com/dubbogo/gost v1.5.2
 	github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239 // indirect
 	github.com/go-errors/errors v1.0.1 // indirect
 	github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 // indirect
 	github.com/golang/mock v1.3.1
+	github.com/golang/protobuf v1.3.2
 	github.com/google/btree v1.0.0 // indirect
 	github.com/grpc-ecosystem/go-grpc-middleware v1.0.0 // indirect
 	github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
@@ -31,19 +33,21 @@ require (
 	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/opentracing/opentracing-go v1.1.0
 	github.com/pkg/errors v0.8.1
-	github.com/prometheus/client_golang v1.1.0 // indirect
-	github.com/samuel/go-zookeeper v0.0.0-20180130194729-c4fab1ac1bec
+	github.com/prometheus/client_golang v1.1.0
 	github.com/satori/go.uuid v1.2.0
 	github.com/smartystreets/goconvey v0.0.0-20190710185942-9d28bd7c0945 // indirect
 	github.com/soheilhy/cmux v0.1.4 // indirect
-	github.com/stretchr/testify v1.4.0
+	github.com/stretchr/testify v1.5.1
 	github.com/tebeka/strftime v0.1.3 // indirect
 	github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 // indirect
 	github.com/toolkits/concurrent v0.0.0-20150624120057-a4371d70e3e3 // indirect
 	github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect
+	github.com/zouyx/agollo v0.0.0-20191114083447-dde9fc9f35b8
 	go.etcd.io/bbolt v1.3.3 // indirect
 	go.etcd.io/etcd v3.3.13+incompatible
 	go.uber.org/atomic v1.4.0
diff --git a/go.sum b/go.sum
index 783d51232a67ee20aa91f2f842924386eceeb0b5..eb775ec08af6b0e9e80b2f4782ca9432a8b1ecd8 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.2.5-0.20191029001541-894e45c9aaaa h1:11TO1wiM5bvGAVrmfN5atD8gZqUSPE1TBoIs8sI6Abk=
-github.com/apache/dubbo-go-hessian2 v1.2.5-0.20191029001541-894e45c9aaaa/go.mod h1:LWnndnrFXZmJLAzoyNAPNHSIJ1KOHVkTSsHgC3YYWlo=
+github.com/apache/dubbo-go-hessian2 v1.3.1-0.20200302092433-6ae5479d93a3 h1:1HM47ILUkLaMxLKUub+WHPncqrJGEQ0KRJzSJueMDpY=
+github.com/apache/dubbo-go-hessian2 v1.3.1-0.20200302092433-6ae5479d93a3/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,12 +106,14 @@ 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.1 h1:9fehwTo/D6+z6/+kADMbhbKeMkP80o/3g+XwV5lFLTY=
-github.com/dubbogo/getty v1.3.1/go.mod h1:dtLOEb1v6EMHsQNYRWEACiRLmTWB2kJGUAj1aXayPOg=
-github.com/dubbogo/gost v1.1.1 h1:JCM7vx5edPIjDA5ovJTuzEEXuw2t7xLyrlgi2mi5jHI=
-github.com/dubbogo/gost v1.1.1/go.mod h1:R7wZm1DrmrKGr50mBZVcg6C9ekG8aL5hP+sgWcIDwQg=
-github.com/dubbogo/gost v1.3.0 h1:n90mIUWCPD69BqW8wJ43NDy0RgNxx02aAG4QJcJ785U=
-github.com/dubbogo/gost v1.3.0/go.mod h1:R7wZm1DrmrKGr50mBZVcg6C9ekG8aL5hP+sgWcIDwQg=
+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/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 h1:oG5dzaWf1KYynBaBoUIOkgT+YD0niHV6xxI0Odq7hDg=
+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/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=
@@ -388,6 +390,8 @@ github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVo
 github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
 github.com/opencontainers/runc v0.1.1 h1:GlxAyO6x8rfZYN9Tt0Kti5a/cP41iuiO2yYT0IJGY8Y=
 github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
+github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU=
+github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
 github.com/ory/dockertest v3.3.4+incompatible h1:VrpM6Gqg7CrPm3bL4Wm1skO+zFWLbh7/Xb5kGEbJRh8=
 github.com/ory/dockertest v3.3.4+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs=
 github.com/packethost/packngo v0.1.1-0.20180711074735-b9cb5096f54c h1:vwpFWvAO8DeIZfFeqASzZfsxuWPno9ncAebBEP0N3uE=
@@ -430,8 +434,6 @@ github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6So
 github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
 github.com/ryanuber/go-glob v0.0.0-20170128012129-256dc444b735 h1:7YvPJVmEeFHR1Tj9sZEYsmarJEQfMVYpd/Vyy/A8dqE=
 github.com/ryanuber/go-glob v0.0.0-20170128012129-256dc444b735/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
-github.com/samuel/go-zookeeper v0.0.0-20180130194729-c4fab1ac1bec h1:6ncX5ko6B9LntYM0YBRXkiSaZMmLYeZ/NWcmeB43mMY=
-github.com/samuel/go-zookeeper v0.0.0-20180130194729-c4fab1ac1bec/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
 github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
 github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
 github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I=
@@ -468,9 +470,13 @@ github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
 github.com/tebeka/strftime v0.1.3 h1:5HQXOqWKYRFfNyBMNVc9z5+QzuBtIXy03psIhtdJYto=
 github.com/tebeka/strftime v0.1.3/go.mod h1:7wJm3dZlpr4l/oVK0t1HYIc4rMzQ2XJlOMIUJUJH6XQ=
 github.com/tent/http-link-go v0.0.0-20130702225549-ac974c61c2f9/go.mod h1:RHkNRtSLfOK7qBTHaeSX1D6BNpI3qw7NTxsmNr4RvN8=
+github.com/tevid/gohamcrest v1.1.1 h1:ou+xSqlIw1xfGTg1uq1nif/htZ2S3EzRqLm2BP+tYU0=
+github.com/tevid/gohamcrest v1.1.1/go.mod h1:3UvtWlqm8j5JbwYZh80D/PVBt0mJ1eJiYgZMibh0H/k=
 github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 h1:LnC5Kc/wtumK+WB441p7ynQJzVuNRJiqddSIE3IlSEQ=
 github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
 github.com/toolkits/concurrent v0.0.0-20150624120057-a4371d70e3e3 h1:kF/7m/ZU+0D4Jj5eZ41Zm3IH/J8OElK1Qtd7tVKAwLk=
@@ -481,6 +487,8 @@ github.com/vmware/govmomi v0.18.0 h1:f7QxSmP7meCtoAmiKZogvVbLInT+CZx6Px6K5rYsJZo
 github.com/vmware/govmomi v0.18.0/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU=
 github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=
 github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
+github.com/zouyx/agollo v0.0.0-20191114083447-dde9fc9f35b8 h1:k8TV7Gz7cpWpOw/dz71fx8cCZdWoPuckHJ/wkJl+meg=
+github.com/zouyx/agollo v0.0.0-20191114083447-dde9fc9f35b8/go.mod h1:S1cAa98KMFv4Sa8SbJ6ZtvOmf0VlgH0QJ1gXI0lBfBY=
 go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk=
 go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
 go.etcd.io/etcd v3.3.13+incompatible h1:jCejD5EMnlGxFvcGRyEV4VGlENZc7oPQX6o0t7n3xbw=
@@ -507,7 +515,6 @@ golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73r
 golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190613194153-d28f0bde5980 h1:dfGZHvZk057jK2MCeWus/TowKpJ8y4AmooUzdBSR9GU=
 golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
diff --git a/metrics/prometheus/reporter.go b/metrics/prometheus/reporter.go
new file mode 100644
index 0000000000000000000000000000000000000000..1636b14da2fe5ab714853aa662eaa774ddbc1791
--- /dev/null
+++ b/metrics/prometheus/reporter.go
@@ -0,0 +1,184 @@
+/*
+ * 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 prometheus
+
+import (
+	"context"
+	"strconv"
+	"strings"
+	"sync"
+	"time"
+)
+import (
+	"github.com/prometheus/client_golang/prometheus"
+)
+
+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/config"
+	"github.com/apache/dubbo-go/metrics"
+	"github.com/apache/dubbo-go/protocol"
+)
+
+const (
+	reporterName = "prometheus"
+	serviceKey   = constant.SERVICE_KEY
+	groupKey     = constant.GROUP_KEY
+	versionKey   = constant.VERSION_KEY
+	methodKey    = constant.METHOD_KEY
+	timeoutKey   = constant.TIMEOUT_KEY
+
+	providerKey = "provider"
+	consumerKey = "consumer"
+
+	// to identify the metric's type
+	histogramSuffix = "_histogram"
+	// to identify the metric's type
+	summarySuffix = "_summary"
+)
+
+var (
+	labelNames       = []string{serviceKey, groupKey, versionKey, methodKey, timeoutKey}
+	namespace        = config.GetApplicationConfig().Name
+	reporterInstance *PrometheusReporter
+	reporterInitOnce sync.Once
+)
+
+// should initialize after loading configuration
+func init() {
+
+	extension.SetMetricReporter(reporterName, newPrometheusReporter)
+}
+
+// PrometheusReporter
+// it will collect the data for Prometheus
+// if you want to use this, you should initialize your prometheus.
+// https://prometheus.io/docs/guides/go-application/
+type PrometheusReporter struct {
+
+	// report the consumer-side's summary data
+	consumerSummaryVec *prometheus.SummaryVec
+	// report the provider-side's summary data
+	providerSummaryVec *prometheus.SummaryVec
+
+	// report the provider-side's histogram data
+	providerHistogramVec *prometheus.HistogramVec
+	// report the consumer-side's histogram data
+	consumerHistogramVec *prometheus.HistogramVec
+}
+
+// Report report the duration to Prometheus
+// the role in url must be consumer or provider
+// or it will be ignored
+func (reporter *PrometheusReporter) Report(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation, cost time.Duration, res protocol.Result) {
+	url := invoker.GetUrl()
+	var sumVec *prometheus.SummaryVec
+	var hisVec *prometheus.HistogramVec
+	if isProvider(url) {
+		sumVec = reporter.providerSummaryVec
+		hisVec = reporter.providerHistogramVec
+	} else if isConsumer(url) {
+		sumVec = reporter.consumerSummaryVec
+		hisVec = reporter.consumerHistogramVec
+	} else {
+		logger.Warnf("The url is not the consumer's or provider's, "+
+			"so the invocation will be ignored. url: %s", url.String())
+		return
+	}
+
+	labels := prometheus.Labels{
+		serviceKey: url.Service(),
+		groupKey:   url.GetParam(groupKey, ""),
+		versionKey: url.GetParam(versionKey, ""),
+		methodKey:  invocation.MethodName(),
+		timeoutKey: url.GetParam(timeoutKey, ""),
+	}
+
+	costMs := float64(cost.Nanoseconds() / constant.MsToNanoRate)
+	sumVec.With(labels).Observe(costMs)
+	hisVec.With(labels).Observe(costMs)
+}
+
+func newHistogramVec(side string) *prometheus.HistogramVec {
+	mc := config.GetMetricConfig()
+	return prometheus.NewHistogramVec(
+		prometheus.HistogramOpts{
+			Namespace: namespace,
+			Subsystem: side,
+			Name:      serviceKey + histogramSuffix,
+			Help:      "This is the dubbo's histogram metrics",
+			Buckets:   mc.GetHistogramBucket(),
+		},
+		labelNames)
+}
+
+// whether this url represents the application received the request as server
+func isProvider(url common.URL) bool {
+	role := url.GetParam(constant.ROLE_KEY, "")
+	return strings.EqualFold(role, strconv.Itoa(common.PROVIDER))
+}
+
+// whether this url represents the application sent then request as client
+func isConsumer(url common.URL) bool {
+	role := url.GetParam(constant.ROLE_KEY, "")
+	return strings.EqualFold(role, strconv.Itoa(common.CONSUMER))
+}
+
+// newSummaryVec create SummaryVec, the Namespace is dubbo
+// the objectives is from my experience.
+func newSummaryVec(side string) *prometheus.SummaryVec {
+	return prometheus.NewSummaryVec(
+		prometheus.SummaryOpts{
+			Namespace: namespace,
+			Help:      "This is the dubbo's summary metrics",
+			Subsystem: side,
+			Name:      serviceKey + summarySuffix,
+			Objectives: map[float64]float64{
+				0.5:   0.01,
+				0.75:  0.01,
+				0.90:  0.005,
+				0.98:  0.002,
+				0.99:  0.001,
+				0.999: 0.0001,
+			},
+		},
+		labelNames,
+	)
+}
+
+// newPrometheusReporter create new prometheusReporter
+// it will register the metrics into prometheus
+func newPrometheusReporter() metrics.Reporter {
+	if reporterInstance == nil {
+		reporterInitOnce.Do(func() {
+			reporterInstance = &PrometheusReporter{
+				consumerSummaryVec: newSummaryVec(consumerKey),
+				providerSummaryVec: newSummaryVec(providerKey),
+
+				consumerHistogramVec: newHistogramVec(consumerKey),
+				providerHistogramVec: newHistogramVec(providerKey),
+			}
+			prometheus.MustRegister(reporterInstance.consumerSummaryVec, reporterInstance.providerSummaryVec,
+				reporterInstance.consumerHistogramVec, reporterInstance.providerHistogramVec)
+		})
+	}
+	return reporterInstance
+}
diff --git a/metrics/prometheus/reporter_test.go b/metrics/prometheus/reporter_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..0cb7d09a2c8e71fb88b54789c8eb3ee2cf967fbf
--- /dev/null
+++ b/metrics/prometheus/reporter_test.go
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package prometheus
+
+import (
+	"context"
+	"testing"
+	"time"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+import (
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/common/extension"
+	"github.com/apache/dubbo-go/protocol"
+	"github.com/apache/dubbo-go/protocol/invocation"
+)
+
+func TestPrometheusReporter_Report(t *testing.T) {
+	reporter := extension.GetMetricReporter(reporterName)
+	url, _ := common.NewURL(
+		"dubbo://:20000/UserProvider?app.version=0.0.1&application=BDTService&bean.name=UserProvider" +
+			"&cluster=failover&environment=dev&group=&interface=com.ikurento.user.UserProvider&loadbalance=random&methods.GetUser." +
+			"loadbalance=random&methods.GetUser.retries=1&methods.GetUser.weight=0&module=dubbogo+user-info+server&name=" +
+			"BDTService&organization=ikurento.com&owner=ZX&registry.role=3&retries=&" +
+			"service.filter=echo%2Ctoken%2Caccesslog&timestamp=1569153406&token=934804bf-b007-4174-94eb-96e3e1d60cc7&version=&warmup=100")
+	invoker := protocol.NewBaseInvoker(url)
+
+	attach := make(map[string]string, 10)
+	inv := invocation.NewRPCInvocation("MethodName", []interface{}{"OK", "Hello"}, attach)
+
+	assert.False(t, isConsumer(url))
+	ctx := context.Background()
+	reporter.Report(ctx, invoker, inv, 100*time.Millisecond, nil)
+
+	// consumer side
+	url, _ = common.NewURL(
+		"dubbo://:20000/UserProvider?app.version=0.0.1&application=BDTService&bean.name=UserProvider" +
+			"&cluster=failover&environment=dev&group=&interface=com.ikurento.user.UserProvider&loadbalance=random&methods.GetUser." +
+			"loadbalance=random&methods.GetUser.retries=1&methods.GetUser.weight=0&module=dubbogo+user-info+server&name=" +
+			"BDTService&organization=ikurento.com&owner=ZX&registry.role=0&retries=&" +
+			"service.filter=echo%2Ctoken%2Caccesslog&timestamp=1569153406&token=934804bf-b007-4174-94eb-96e3e1d60cc7&version=&warmup=100")
+	invoker = protocol.NewBaseInvoker(url)
+	reporter.Report(ctx, invoker, inv, 100*time.Millisecond, nil)
+
+	// invalid role
+	url, _ = common.NewURL(
+		"dubbo://:20000/UserProvider?app.version=0.0.1&application=BDTService&bean.name=UserProvider" +
+			"&cluster=failover&environment=dev&group=&interface=com.ikurento.user.UserProvider&loadbalance=random&methods.GetUser." +
+			"loadbalance=random&methods.GetUser.retries=1&methods.GetUser.weight=0&module=dubbogo+user-info+server&name=" +
+			"BDTService&organization=ikurento.com&owner=ZX&registry.role=9&retries=&" +
+			"service.filter=echo%2Ctoken%2Caccesslog&timestamp=1569153406&token=934804bf-b007-4174-94eb-96e3e1d60cc7&version=&warmup=100")
+	invoker = protocol.NewBaseInvoker(url)
+	reporter.Report(ctx, invoker, inv, 100*time.Millisecond, nil)
+}
diff --git a/metrics/reporter.go b/metrics/reporter.go
new file mode 100644
index 0000000000000000000000000000000000000000..85ef1dcdf0dad275edecc1f3a85502c1493c1395
--- /dev/null
+++ b/metrics/reporter.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 metrics
+
+import (
+	"context"
+	"time"
+)
+import (
+	"github.com/apache/dubbo-go/protocol"
+)
+
+const (
+	NameSpace = "dubbo"
+)
+
+// it will be use to report the invocation's duration
+type Reporter interface {
+	// report the duration of an invocation
+	Report(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation,
+		cost time.Duration, res protocol.Result)
+}
diff --git a/protocol/dubbo/client.go b/protocol/dubbo/client.go
index ba74d86c0c38ba02ec5e87423e0fe8990dae486b..5ec7db51af0ddfa6e49d3c65910355f0bf2de414 100644
--- a/protocol/dubbo/client.go
+++ b/protocol/dubbo/client.go
@@ -18,15 +18,16 @@
 package dubbo
 
 import (
+	"math/rand"
 	"strings"
 	"sync"
 	"time"
 )
 
 import (
-	"github.com/apache/dubbo-go-hessian2"
+	hessian "github.com/apache/dubbo-go-hessian2"
 	"github.com/dubbogo/getty"
-	"github.com/dubbogo/gost/sync"
+	gxsync "github.com/dubbogo/gost/sync"
 	perrors "github.com/pkg/errors"
 	"go.uber.org/atomic"
 	"gopkg.in/yaml.v2"
@@ -83,8 +84,11 @@ func init() {
 		return
 	}
 	setClientGrpool()
+
+	rand.Seed(time.Now().UnixNano())
 }
 
+// SetClientConf ...
 func SetClientConf(c ClientConfig) {
 	clientConf = &c
 	err := clientConf.CheckValidity()
@@ -95,6 +99,7 @@ func SetClientConf(c ClientConfig) {
 	setClientGrpool()
 }
 
+// GetClientConf ...
 func GetClientConf() ClientConfig {
 	return *clientConf
 }
@@ -106,6 +111,7 @@ func setClientGrpool() {
 	}
 }
 
+// Options ...
 type Options struct {
 	// connect timeout
 	ConnectTimeout time.Duration
@@ -113,7 +119,9 @@ type Options struct {
 	RequestTimeout time.Duration
 }
 
-type CallResponse struct {
+//AsyncCallbackResponse async response for dubbo
+type AsyncCallbackResponse struct {
+	common.CallbackResponse
 	Opts      Options
 	Cause     error
 	Start     time.Time // invoke(call) start time == write start time
@@ -121,8 +129,7 @@ type CallResponse struct {
 	Reply     interface{}
 }
 
-type AsyncCallback func(response CallResponse)
-
+// Client ...
 type Client struct {
 	opts     Options
 	conf     ClientConfig
@@ -132,14 +139,21 @@ type Client struct {
 	pendingResponses *sync.Map
 }
 
+// NewClient ...
 func NewClient(opt Options) *Client {
 
 	switch {
 	case opt.ConnectTimeout == 0:
-		opt.ConnectTimeout = 3e9
+		opt.ConnectTimeout = 3 * time.Second
 		fallthrough
 	case opt.RequestTimeout == 0:
-		opt.RequestTimeout = 3e9
+		opt.RequestTimeout = 3 * time.Second
+	}
+
+	// make sure that client request sequence is an odd number
+	initSequence := uint64(rand.Int63n(time.Now().UnixNano()))
+	if initSequence%2 == 0 {
+		initSequence++
 	}
 
 	c := &Client{
@@ -147,11 +161,13 @@ func NewClient(opt Options) *Client {
 		pendingResponses: new(sync.Map),
 		conf:             *clientConf,
 	}
+	c.sequence.Store(initSequence)
 	c.pool = newGettyRPCClientConnPool(c, clientConf.PoolSize, time.Duration(int(time.Second)*clientConf.PoolTTL))
 
 	return c
 }
 
+// Request ...
 type Request struct {
 	addr   string
 	svcUrl common.URL
@@ -160,6 +176,7 @@ type Request struct {
 	atta   map[string]string
 }
 
+// NewRequest ...
 func NewRequest(addr string, svcUrl common.URL, method string, args interface{}, atta map[string]string) *Request {
 	return &Request{
 		addr:   addr,
@@ -170,11 +187,13 @@ func NewRequest(addr string, svcUrl common.URL, method string, args interface{},
 	}
 }
 
+// Response ...
 type Response struct {
 	reply interface{}
 	atta  map[string]string
 }
 
+// NewResponse ...
 func NewResponse(reply interface{}, atta map[string]string) *Response {
 	return &Response{
 		reply: reply,
@@ -182,13 +201,13 @@ func NewResponse(reply interface{}, atta map[string]string) *Response {
 	}
 }
 
-// call one way
+// CallOneway call one way
 func (c *Client) CallOneway(request *Request) error {
 
 	return perrors.WithStack(c.call(CT_OneWay, request, NewResponse(nil, nil), nil))
 }
 
-// if @response is nil, the transport layer will get the response without notify the invoker.
+// Call if @response is nil, the transport layer will get the response without notify the invoker.
 func (c *Client) Call(request *Request, response *Response) error {
 
 	ct := CT_TwoWay
@@ -199,12 +218,13 @@ func (c *Client) Call(request *Request, response *Response) error {
 	return perrors.WithStack(c.call(ct, request, response, nil))
 }
 
-func (c *Client) AsyncCall(request *Request, callback AsyncCallback, response *Response) error {
+// AsyncCall ...
+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 AsyncCallback) error {
+func (c *Client) call(ct CallType, request *Request, response *Response, callback common.AsyncCallback) error {
 
 	p := &DubboPackage{}
 	p.Service.Path = strings.TrimPrefix(request.svcUrl.Path, "/")
@@ -212,7 +232,15 @@ func (c *Client) call(ct CallType, request *Request, response *Response, callbac
 	p.Service.Version = request.svcUrl.GetParam(constant.VERSION_KEY, "")
 	p.Service.Group = request.svcUrl.GetParam(constant.GROUP_KEY, "")
 	p.Service.Method = request.method
+
 	p.Service.Timeout = c.opts.RequestTimeout
+	var timeout = request.svcUrl.GetParam(strings.Join([]string{constant.METHOD_KEYS, request.method + constant.RETRIES_KEY}, "."), "")
+	if len(timeout) != 0 {
+		if t, err := time.ParseDuration(timeout); err == nil {
+			p.Service.Timeout = t
+		}
+	}
+
 	p.Header.SerialID = byte(S_Dubbo)
 	p.Body = hessian.NewRequest(request.args, request.atta)
 
@@ -238,7 +266,13 @@ func (c *Client) call(ct CallType, request *Request, response *Response, callbac
 	if session == nil {
 		return errSessionNotExist
 	}
-	defer c.pool.release(conn, err)
+	defer func() {
+		if err == nil {
+			c.pool.put(conn)
+			return
+		}
+		conn.close()
+	}()
 
 	if err = c.transfer(session, p, rsp); err != nil {
 		return perrors.WithStack(err)
@@ -250,8 +284,8 @@ func (c *Client) call(ct CallType, request *Request, response *Response, callbac
 
 	select {
 	case <-getty.GetTimeWheel().After(c.opts.RequestTimeout):
-		err = errClientReadTimeout
 		c.removePendingResponse(SequenceType(rsp.seq))
+		return perrors.WithStack(errClientReadTimeout)
 	case <-rsp.done:
 		err = rsp.err
 	}
@@ -259,6 +293,7 @@ func (c *Client) call(ct CallType, request *Request, response *Response, callbac
 	return perrors.WithStack(err)
 }
 
+// Close ...
 func (c *Client) Close() {
 	if c.pool != nil {
 		c.pool.close()
diff --git a/protocol/dubbo/client_test.go b/protocol/dubbo/client_test.go
index eb1f15c862a910120e118c06bf9b572e93f58832..1e0a73fac1a6cf6d4d102e5f4f6f1ba60fc4102a 100644
--- a/protocol/dubbo/client_test.go
+++ b/protocol/dubbo/client_test.go
@@ -144,8 +144,9 @@ func TestClient_AsyncCall(t *testing.T) {
 	user := &User{}
 	lock := sync.Mutex{}
 	lock.Lock()
-	err := c.AsyncCall(NewRequest("127.0.0.1:20000", url, "GetUser", []interface{}{"1", "username"}, nil), func(response CallResponse) {
-		assert.Equal(t, User{Id: "1", Name: "username"}, *response.Reply.(*Response).reply.(*User))
+	err := c.AsyncCall(NewRequest("127.0.0.1:20000", url, "GetUser", []interface{}{"1", "username"}, nil), func(response common.CallbackResponse) {
+		r := response.(AsyncCallbackResponse)
+		assert.Equal(t, User{Id: "1", Name: "username"}, *r.Reply.(*Response).reply.(*User))
 		lock.Unlock()
 	}, NewResponse(user, nil))
 	assert.NoError(t, err)
@@ -209,10 +210,10 @@ func InitTest(t *testing.T) (protocol.Protocol, common.URL) {
 
 	// Export
 	proto := GetProtocol()
-	url, err := common.NewURL(context.Background(), "dubbo://127.0.0.1:20000/UserProvider?anyhost=true&"+
-		"application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&"+
-		"environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&"+
-		"module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&"+
+	url, err := common.NewURL("dubbo://127.0.0.1:20000/UserProvider?anyhost=true&" +
+		"application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&" +
+		"environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&" +
+		"module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&" +
 		"side=provider&timeout=3000&timestamp=1556509797245&bean.name=UserProvider")
 	assert.NoError(t, err)
 	proto.Export(&proxy_factory.ProxyInvoker{
diff --git a/protocol/dubbo/codec.go b/protocol/dubbo/codec.go
index a878ffd91e29d6949870ec25fed9481f301b435a..76416b2baf1e1db516c00d92ecb8ad618bf186bd 100644
--- a/protocol/dubbo/codec.go
+++ b/protocol/dubbo/codec.go
@@ -26,31 +26,38 @@ import (
 
 import (
 	"github.com/apache/dubbo-go-hessian2"
+	"github.com/apache/dubbo-go/common"
 	perrors "github.com/pkg/errors"
 )
 
-// serial ID
+//SerialID serial ID
 type SerialID byte
 
 const (
+	// S_Dubbo dubbo serial id
 	S_Dubbo SerialID = 2
 )
 
-// call type
+//CallType call type
 type CallType int32
 
 const (
+	// CT_UNKNOWN unknown call type
 	CT_UNKNOWN CallType = 0
-	CT_OneWay  CallType = 1
-	CT_TwoWay  CallType = 2
+	// CT_OneWay call one way
+	CT_OneWay CallType = 1
+	// CT_TwoWay call in request/response
+	CT_TwoWay CallType = 2
 )
 
 ////////////////////////////////////////////
 // dubbo package
 ////////////////////////////////////////////
 
+// SequenceType ...
 type SequenceType int64
 
+// DubboPackage ...
 type DubboPackage struct {
 	Header  hessian.DubboHeader
 	Service hessian.Service
@@ -62,6 +69,7 @@ func (p DubboPackage) String() string {
 	return fmt.Sprintf("DubboPackage: Header-%v, Path-%v, Body-%v", p.Header, p.Service, p.Body)
 }
 
+// Marshal ...
 func (p *DubboPackage) Marshal() (*bytes.Buffer, error) {
 	codec := hessian.NewHessianCodec(nil)
 
@@ -73,8 +81,15 @@ func (p *DubboPackage) Marshal() (*bytes.Buffer, error) {
 	return bytes.NewBuffer(pkg), nil
 }
 
+// Unmarshal ...
 func (p *DubboPackage) Unmarshal(buf *bytes.Buffer, opts ...interface{}) error {
-	codec := hessian.NewHessianCodec(bufio.NewReaderSize(buf, buf.Len()))
+	// fix issue https://github.com/apache/dubbo-go/issues/380
+	bufLen := buf.Len()
+	if bufLen < hessian.HEADER_LENGTH {
+		return perrors.WithStack(hessian.ErrHeaderNotEnough)
+	}
+
+	codec := hessian.NewHessianCodec(bufio.NewReaderSize(buf, bufLen))
 
 	// read header
 	err := codec.ReadHeader(&p.Header)
@@ -88,11 +103,17 @@ func (p *DubboPackage) Unmarshal(buf *bytes.Buffer, opts ...interface{}) error {
 			return perrors.Errorf("opts[0] is not of type *Client")
 		}
 
-		pendingRsp, ok := client.pendingResponses.Load(SequenceType(p.Header.ID))
-		if !ok {
-			return perrors.Errorf("client.GetPendingResponse(%v) = nil", p.Header.ID)
+		if p.Header.Type&hessian.PackageRequest != 0x00 {
+			// size of this array must be '7'
+			// https://github.com/apache/dubbo-go-hessian2/blob/master/request.go#L272
+			p.Body = make([]interface{}, 7)
+		} else {
+			pendingRsp, ok := client.pendingResponses.Load(SequenceType(p.Header.ID))
+			if !ok {
+				return perrors.Errorf("client.GetPendingResponse(%v) = nil", p.Header.ID)
+			}
+			p.Body = &hessian.Response{RspObj: pendingRsp.(*PendingResponse).response.reply}
 		}
-		p.Body = &hessian.Response{RspObj: pendingRsp.(*PendingResponse).response.reply}
 	}
 
 	// read body
@@ -104,16 +125,18 @@ func (p *DubboPackage) Unmarshal(buf *bytes.Buffer, opts ...interface{}) error {
 // PendingResponse
 ////////////////////////////////////////////
 
+// PendingResponse ...
 type PendingResponse struct {
 	seq       uint64
 	err       error
 	start     time.Time
 	readStart time.Time
-	callback  AsyncCallback
+	callback  common.AsyncCallback
 	response  *Response
 	done      chan struct{}
 }
 
+// NewPendingResponse ...
 func NewPendingResponse() *PendingResponse {
 	return &PendingResponse{
 		start:    time.Now(),
@@ -122,8 +145,9 @@ func NewPendingResponse() *PendingResponse {
 	}
 }
 
-func (r PendingResponse) GetCallResponse() CallResponse {
-	return CallResponse{
+// GetCallResponse ...
+func (r PendingResponse) GetCallResponse() common.CallbackResponse {
+	return AsyncCallbackResponse{
 		Cause:     r.err,
 		Start:     r.start,
 		ReadStart: r.readStart,
diff --git a/protocol/dubbo/codec_test.go b/protocol/dubbo/codec_test.go
index 149644a6eadc23a72b672ae9fa653a1991802dc0..5dc71f0d080c8c862d68029c7983a4407913307e 100644
--- a/protocol/dubbo/codec_test.go
+++ b/protocol/dubbo/codec_test.go
@@ -18,12 +18,14 @@
 package dubbo
 
 import (
+	"bytes"
 	"testing"
 	"time"
 )
 
 import (
 	hessian "github.com/apache/dubbo-go-hessian2"
+	perrors "github.com/pkg/errors"
 	"github.com/stretchr/testify/assert"
 )
 
@@ -72,3 +74,10 @@ func TestDubboPackage_MarshalAndUnmarshal(t *testing.T) {
 	assert.Equal(t, []interface{}{"a"}, pkgres.Body.([]interface{})[5])
 	assert.Equal(t, map[string]string{"dubbo": "2.0.2", "interface": "Service", "path": "path", "timeout": "1000", "version": "2.6"}, pkgres.Body.([]interface{})[6])
 }
+
+func TestIssue380(t *testing.T) {
+	pkg := &DubboPackage{}
+	buf := bytes.NewBuffer([]byte("hello"))
+	err := pkg.Unmarshal(buf)
+	assert.True(t, perrors.Cause(err) == hessian.ErrHeaderNotEnough)
+}
diff --git a/protocol/dubbo/config.go b/protocol/dubbo/config.go
index 5371c587478ce7d064858108055c3f00f3ff4496..dbc6989c54780afacef717f1d110833d92967f9f 100644
--- a/protocol/dubbo/config.go
+++ b/protocol/dubbo/config.go
@@ -22,10 +22,12 @@ import (
 )
 
 import (
+	"github.com/dubbogo/getty"
 	perrors "github.com/pkg/errors"
 )
 
 type (
+	// GettySessionParam ...
 	GettySessionParam struct {
 		CompressEncoding bool   `default:"false" yaml:"compress_encoding" json:"compress_encoding,omitempty"`
 		TcpNoDelay       bool   `default:"true" yaml:"tcp_no_delay" json:"tcp_no_delay,omitempty"`
@@ -45,7 +47,8 @@ type (
 		SessionName      string `default:"rpc" yaml:"session_name" json:"session_name,omitempty"`
 	}
 
-	// Config holds supported types by the multiconfig package
+	// ServerConfig
+	//Config holds supported types by the multiconfig package
 	ServerConfig struct {
 		// session
 		SessionTimeout string `default:"60s" yaml:"session_timeout" json:"session_timeout,omitempty"`
@@ -61,7 +64,8 @@ type (
 		GettySessionParam GettySessionParam `required:"true" yaml:"getty_session_param" json:"getty_session_param,omitempty"`
 	}
 
-	// Config holds supported types by the multiconfig package
+	// ClientConfig
+	//Config holds supported types by the multiconfig package
 	ClientConfig struct {
 		ReconnectInterval int `default:"0" yaml:"reconnect_interval" json:"reconnect_interval,omitempty"`
 
@@ -90,13 +94,14 @@ type (
 	}
 )
 
+// GetDefaultClientConfig ...
 func GetDefaultClientConfig() ClientConfig {
 	return ClientConfig{
 		ReconnectInterval: 0,
 		ConnectionNum:     16,
-		HeartbeatPeriod:   "5s",
-		SessionTimeout:    "20s",
-		PoolSize:          64,
+		HeartbeatPeriod:   "30s",
+		SessionTimeout:    "180s",
+		PoolSize:          4,
 		PoolTTL:           600,
 		GrPoolSize:        200,
 		QueueLen:          64,
@@ -105,7 +110,7 @@ func GetDefaultClientConfig() ClientConfig {
 			CompressEncoding: false,
 			TcpNoDelay:       true,
 			TcpKeepAlive:     true,
-			KeepAlivePeriod:  "120s",
+			KeepAlivePeriod:  "180s",
 			TcpRBufSize:      262144,
 			TcpWBufSize:      65536,
 			PkgWQSize:        512,
@@ -117,9 +122,10 @@ func GetDefaultClientConfig() ClientConfig {
 		}}
 }
 
+// GetDefaultServerConfig ...
 func GetDefaultServerConfig() ServerConfig {
 	return ServerConfig{
-		SessionTimeout: "20s",
+		SessionTimeout: "180s",
 		SessionNumber:  700,
 		GrPoolSize:     120,
 		QueueNumber:    6,
@@ -128,7 +134,7 @@ func GetDefaultServerConfig() ServerConfig {
 			CompressEncoding: false,
 			TcpNoDelay:       true,
 			TcpKeepAlive:     true,
-			KeepAlivePeriod:  "120s",
+			KeepAlivePeriod:  "180s",
 			TcpRBufSize:      262144,
 			TcpWBufSize:      65536,
 			PkgWQSize:        512,
@@ -141,6 +147,7 @@ func GetDefaultServerConfig() ServerConfig {
 	}
 }
 
+// CheckValidity ...
 func (c *GettySessionParam) CheckValidity() error {
 	var err error
 
@@ -163,6 +170,7 @@ func (c *GettySessionParam) CheckValidity() error {
 	return nil
 }
 
+// CheckValidity ...
 func (c *ClientConfig) CheckValidity() error {
 	var err error
 
@@ -172,6 +180,11 @@ func (c *ClientConfig) CheckValidity() error {
 		return perrors.WithMessagef(err, "time.ParseDuration(HeartbeatPeroid{%#v})", c.HeartbeatPeriod)
 	}
 
+	if c.heartbeatPeriod >= time.Duration(getty.MaxWheelTimeSpan) {
+		return perrors.WithMessagef(err, "heartbeat_period %s should be less than %s",
+			c.HeartbeatPeriod, time.Duration(getty.MaxWheelTimeSpan))
+	}
+
 	if c.sessionTimeout, err = time.ParseDuration(c.SessionTimeout); err != nil {
 		return perrors.WithMessagef(err, "time.ParseDuration(SessionTimeout{%#v})", c.SessionTimeout)
 	}
@@ -179,6 +192,7 @@ func (c *ClientConfig) CheckValidity() error {
 	return perrors.WithStack(c.GettySessionParam.CheckValidity())
 }
 
+// CheckValidity ...
 func (c *ServerConfig) CheckValidity() error {
 	var err error
 
@@ -186,5 +200,10 @@ func (c *ServerConfig) CheckValidity() error {
 		return perrors.WithMessagef(err, "time.ParseDuration(SessionTimeout{%#v})", c.SessionTimeout)
 	}
 
+	if c.sessionTimeout >= time.Duration(getty.MaxWheelTimeSpan) {
+		return perrors.WithMessagef(err, "session_timeout %s should be less than %s",
+			c.SessionTimeout, time.Duration(getty.MaxWheelTimeSpan))
+	}
+
 	return perrors.WithStack(c.GettySessionParam.CheckValidity())
 }
diff --git a/protocol/dubbo/dubbo_exporter.go b/protocol/dubbo/dubbo_exporter.go
index cb06b6b69c9d0873342af5ea49fae054f029608c..f4cd0cc1234f71bdcf6ce746f01ff3618d820fc5 100644
--- a/protocol/dubbo/dubbo_exporter.go
+++ b/protocol/dubbo/dubbo_exporter.go
@@ -28,16 +28,19 @@ import (
 	"github.com/apache/dubbo-go/protocol"
 )
 
+// DubboExporter ...
 type DubboExporter struct {
 	protocol.BaseExporter
 }
 
+// NewDubboExporter ...
 func NewDubboExporter(key string, invoker protocol.Invoker, exporterMap *sync.Map) *DubboExporter {
 	return &DubboExporter{
 		BaseExporter: *protocol.NewBaseExporter(key, invoker, exporterMap),
 	}
 }
 
+// Unexport ...
 func (de *DubboExporter) Unexport() {
 	serviceId := de.GetInvoker().GetUrl().GetParam(constant.BEAN_NAME_KEY, "")
 	de.BaseExporter.Unexport()
diff --git a/protocol/dubbo/dubbo_invoker.go b/protocol/dubbo/dubbo_invoker.go
index bc321a97a4271c147d9317145d9f1aa76ca27902..09c3725710d2a0b821d8e641b0cb7b367189c244 100644
--- a/protocol/dubbo/dubbo_invoker.go
+++ b/protocol/dubbo/dubbo_invoker.go
@@ -18,11 +18,15 @@
 package dubbo
 
 import (
+	"context"
 	"strconv"
 	"sync"
+	"sync/atomic"
+	"time"
 )
 
 import (
+	"github.com/opentracing/opentracing-go"
 	perrors "github.com/pkg/errors"
 )
 
@@ -34,31 +38,49 @@ import (
 	invocation_impl "github.com/apache/dubbo-go/protocol/invocation"
 )
 
-var Err_No_Reply = perrors.New("request need @response")
+var (
+	// ErrNoReply ...
+	ErrNoReply          = perrors.New("request need @response")
+	ErrDestroyedInvoker = perrors.New("request Destroyed invoker")
+)
 
 var (
 	attachmentKey = []string{constant.INTERFACE_KEY, constant.GROUP_KEY, constant.TOKEN_KEY, constant.TIMEOUT_KEY}
 )
 
+// DubboInvoker ...
 type DubboInvoker struct {
 	protocol.BaseInvoker
-	client      *Client
-	destroyLock sync.Mutex
+	client   *Client
+	quitOnce sync.Once
+	// Used to record the number of requests. -1 represent this DubboInvoker is destroyed
+	reqNum int64
 }
 
+// NewDubboInvoker ...
 func NewDubboInvoker(url common.URL, client *Client) *DubboInvoker {
 	return &DubboInvoker{
 		BaseInvoker: *protocol.NewBaseInvoker(url),
 		client:      client,
+		reqNum:      0,
 	}
 }
 
-func (di *DubboInvoker) Invoke(invocation protocol.Invocation) protocol.Result {
-
+// Invoke ...
+func (di *DubboInvoker) Invoke(ctx context.Context, invocation protocol.Invocation) protocol.Result {
 	var (
 		err    error
 		result protocol.RPCResult
 	)
+	if di.reqNum < 0 {
+		// Generally, the case will not happen, because the invoker has been removed
+		// from the invoker list before destroy,so no new request will enter the destroyed invoker
+		logger.Warnf("this dubboInvoker is destroyed")
+		result.Err = ErrDestroyedInvoker
+		return &result
+	}
+	atomic.AddInt64(&(di.reqNum), 1)
+	defer atomic.AddInt64(&(di.reqNum), -1)
 
 	inv := invocation.(*invocation_impl.RPCInvocation)
 	for _, k := range attachmentKey {
@@ -66,6 +88,10 @@ func (di *DubboInvoker) Invoke(invocation protocol.Invocation) protocol.Result {
 			inv.SetAttachments(k, v)
 		}
 	}
+
+	// put the ctx into attachment
+	di.appendCtx(ctx, inv)
+
 	url := di.GetUrl()
 	// async
 	async, err := strconv.ParseBool(inv.AttachmentsByKey(constant.ASYNC_KEY, "false"))
@@ -75,14 +101,14 @@ func (di *DubboInvoker) Invoke(invocation protocol.Invocation) protocol.Result {
 	}
 	response := NewResponse(inv.Reply(), nil)
 	if async {
-		if callBack, ok := inv.CallBack().(func(response CallResponse)); ok {
+		if callBack, ok := inv.CallBack().(func(response common.CallbackResponse)); ok {
 			result.Err = di.client.AsyncCall(NewRequest(url.Location, url, inv.MethodName(), inv.Arguments(), inv.Attachments()), callBack, response)
 		} else {
 			result.Err = di.client.CallOneway(NewRequest(url.Location, url, inv.MethodName(), inv.Arguments(), inv.Attachments()))
 		}
 	} else {
 		if inv.Reply() == nil {
-			result.Err = Err_No_Reply
+			result.Err = ErrNoReply
 		} else {
 			result.Err = di.client.Call(NewRequest(url.Location, url, inv.MethodName(), inv.Arguments(), inv.Attachments()), response)
 		}
@@ -96,20 +122,37 @@ func (di *DubboInvoker) Invoke(invocation protocol.Invocation) protocol.Result {
 	return &result
 }
 
+// Destroy ...
 func (di *DubboInvoker) Destroy() {
-	if di.IsDestroyed() {
-		return
-	}
-	di.destroyLock.Lock()
-	defer di.destroyLock.Unlock()
-
-	if di.IsDestroyed() {
-		return
-	}
+	di.quitOnce.Do(func() {
+		for {
+			if di.reqNum == 0 {
+				di.reqNum = -1
+				logger.Infof("dubboInvoker is destroyed,url:{%s}", di.GetUrl().Key())
+				di.BaseInvoker.Destroy()
+				if di.client != nil {
+					di.client.Close()
+					di.client = nil
+				}
+				break
+			}
+			logger.Warnf("DubboInvoker is to be destroyed, wait {%v} req end,url:{%s}", di.reqNum, di.GetUrl().Key())
+			time.Sleep(1 * time.Second)
+		}
 
-	di.BaseInvoker.Destroy()
+	})
+}
 
-	if di.client != nil {
-		di.client.Close() // close client
+// Finally, I made the decision that I don't provide a general way to transfer the whole context
+// because it could be misused. If the context contains to many key-value pairs, the performance will be much lower.
+func (di *DubboInvoker) appendCtx(ctx context.Context, inv *invocation_impl.RPCInvocation) {
+	// inject opentracing ctx
+	currentSpan := opentracing.SpanFromContext(ctx)
+	if currentSpan != nil {
+		carrier := opentracing.TextMapCarrier(inv.Attachments())
+		err := opentracing.GlobalTracer().Inject(currentSpan.Context(), opentracing.TextMap, carrier)
+		if err != nil {
+			logger.Errorf("Could not inject the span context into attachments: %v", err)
+		}
 	}
 }
diff --git a/protocol/dubbo/dubbo_invoker_test.go b/protocol/dubbo/dubbo_invoker_test.go
index 0a765356f7353829c8486fddba986e3a444441a1..1a64301f8200a4264001284cca1af3f0f1e07814 100644
--- a/protocol/dubbo/dubbo_invoker_test.go
+++ b/protocol/dubbo/dubbo_invoker_test.go
@@ -18,16 +18,19 @@
 package dubbo
 
 import (
+	"context"
 	"sync"
 	"testing"
 	"time"
 )
 
 import (
+	"github.com/opentracing/opentracing-go"
 	"github.com/stretchr/testify/assert"
 )
 
 import (
+	"github.com/apache/dubbo-go/common"
 	"github.com/apache/dubbo-go/common/constant"
 	"github.com/apache/dubbo-go/protocol/invocation"
 )
@@ -39,8 +42,8 @@ func TestDubboInvoker_Invoke(t *testing.T) {
 		pendingResponses: new(sync.Map),
 		conf:             *clientConf,
 		opts: Options{
-			ConnectTimeout: 3e9,
-			RequestTimeout: 6e9,
+			ConnectTimeout: 3 * time.Second,
+			RequestTimeout: 6 * time.Second,
 		},
 	}
 	c.pool = newGettyRPCClientConnPool(c, clientConf.PoolSize, time.Duration(int(time.Second)*clientConf.PoolTTL))
@@ -52,32 +55,38 @@ func TestDubboInvoker_Invoke(t *testing.T) {
 		invocation.WithReply(user), invocation.WithAttachments(map[string]string{"test_key": "test_value"}))
 
 	// Call
-	res := invoker.Invoke(inv)
+	res := invoker.Invoke(context.Background(), inv)
 	assert.NoError(t, res.Error())
 	assert.Equal(t, User{Id: "1", Name: "username"}, *res.Result().(*User))
 	assert.Equal(t, "test_value", res.Attachments()["test_key"]) // test attachments for request/response
 
 	// CallOneway
 	inv.SetAttachments(constant.ASYNC_KEY, "true")
-	res = invoker.Invoke(inv)
+	res = invoker.Invoke(context.Background(), inv)
 	assert.NoError(t, res.Error())
 
 	// AsyncCall
 	lock := sync.Mutex{}
 	lock.Lock()
-	inv.SetCallBack(func(response CallResponse) {
-		assert.Equal(t, User{Id: "1", Name: "username"}, *response.Reply.(*Response).reply.(*User))
+	inv.SetCallBack(func(response common.CallbackResponse) {
+		r := response.(AsyncCallbackResponse)
+		assert.Equal(t, User{Id: "1", Name: "username"}, *r.Reply.(*Response).reply.(*User))
 		lock.Unlock()
 	})
-	res = invoker.Invoke(inv)
+	res = invoker.Invoke(context.Background(), inv)
 	assert.NoError(t, res.Error())
 
 	// Err_No_Reply
 	inv.SetAttachments(constant.ASYNC_KEY, "false")
 	inv.SetReply(nil)
-	res = invoker.Invoke(inv)
+	res = invoker.Invoke(context.Background(), inv)
 	assert.EqualError(t, res.Error(), "request need @response")
 
+	// testing appendCtx
+	span, ctx := opentracing.StartSpanFromContext(context.Background(), "TestOperation")
+	invoker.Invoke(ctx, inv)
+	span.Finish()
+
 	// destroy
 	lock.Lock()
 	proto.Destroy()
diff --git a/protocol/dubbo/dubbo_protocol.go b/protocol/dubbo/dubbo_protocol.go
index 59d1ea05160696754b46dfead5713684aa7a94f7..355dbc802488338ef4dbdd7290166038b312f183 100644
--- a/protocol/dubbo/dubbo_protocol.go
+++ b/protocol/dubbo/dubbo_protocol.go
@@ -19,17 +19,21 @@ package dubbo
 
 import (
 	"sync"
+	"time"
 )
 
 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/config"
 	"github.com/apache/dubbo-go/protocol"
 )
 
+// dubbo protocol constant
 const (
+	// DUBBO ...
 	DUBBO = "dubbo"
 )
 
@@ -41,12 +45,14 @@ var (
 	dubboProtocol *DubboProtocol
 )
 
+// DubboProtocol ...
 type DubboProtocol struct {
 	protocol.BaseProtocol
 	serverMap  map[string]*Server
 	serverLock sync.Mutex
 }
 
+// NewDubboProtocol ...
 func NewDubboProtocol() *DubboProtocol {
 	return &DubboProtocol{
 		BaseProtocol: protocol.NewBaseProtocol(),
@@ -54,6 +60,7 @@ func NewDubboProtocol() *DubboProtocol {
 	}
 }
 
+// Export ...
 func (dp *DubboProtocol) Export(invoker protocol.Invoker) protocol.Exporter {
 	url := invoker.GetUrl()
 	serviceKey := url.ServiceKey()
@@ -66,16 +73,26 @@ func (dp *DubboProtocol) Export(invoker protocol.Invoker) protocol.Exporter {
 	return exporter
 }
 
+// Refer ...
 func (dp *DubboProtocol) Refer(url common.URL) protocol.Invoker {
+	//default requestTimeout
+	var requestTimeout = config.GetConsumerConfig().RequestTimeout
+
+	requestTimeoutStr := url.GetParam(constant.TIMEOUT_KEY, config.GetConsumerConfig().Request_Timeout)
+	if t, err := time.ParseDuration(requestTimeoutStr); err == nil {
+		requestTimeout = t
+	}
+
 	invoker := NewDubboInvoker(url, NewClient(Options{
 		ConnectTimeout: config.GetConsumerConfig().ConnectTimeout,
-		RequestTimeout: config.GetConsumerConfig().RequestTimeout,
+		RequestTimeout: requestTimeout,
 	}))
 	dp.SetInvokers(invoker)
 	logger.Infof("Refer service: %s", url.String())
 	return invoker
 }
 
+// Destroy ...
 func (dp *DubboProtocol) Destroy() {
 	logger.Infof("DubboProtocol destroy.")
 
@@ -107,6 +124,7 @@ func (dp *DubboProtocol) openServer(url common.URL) {
 	}
 }
 
+// GetProtocol ...
 func GetProtocol() protocol.Protocol {
 	if dubboProtocol == nil {
 		dubboProtocol = NewDubboProtocol()
diff --git a/protocol/dubbo/dubbo_protocol_test.go b/protocol/dubbo/dubbo_protocol_test.go
index a6b0bc1df3cf2eb46e07c9dab149d04f62f78012..14f6868ad4a7f2bf6f549a2fbbce8234cbb4aa12 100644
--- a/protocol/dubbo/dubbo_protocol_test.go
+++ b/protocol/dubbo/dubbo_protocol_test.go
@@ -18,7 +18,6 @@
 package dubbo
 
 import (
-	"context"
 	"testing"
 )
 
@@ -36,10 +35,10 @@ func TestDubboProtocol_Export(t *testing.T) {
 	// Export
 	proto := GetProtocol()
 	srvConf = &ServerConfig{}
-	url, err := common.NewURL(context.Background(), "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&"+
-		"application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&"+
-		"environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&"+
-		"module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&"+
+	url, err := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&" +
+		"application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&" +
+		"environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&" +
+		"module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&" +
 		"side=provider&timeout=3000&timestamp=1556509797245")
 	assert.NoError(t, err)
 	exporter := proto.Export(protocol.NewBaseInvoker(url))
@@ -49,7 +48,7 @@ func TestDubboProtocol_Export(t *testing.T) {
 	assert.True(t, eq)
 
 	// second service: the same path and the different version
-	url2, err := common.NewURL(context.Background(), "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&"+
+	url2, err := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&"+
 		"application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&"+
 		"environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&"+
 		"module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&"+
@@ -78,10 +77,10 @@ func TestDubboProtocol_Export(t *testing.T) {
 func TestDubboProtocol_Refer(t *testing.T) {
 	// Refer
 	proto := GetProtocol()
-	url, err := common.NewURL(context.Background(), "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&"+
-		"application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&"+
-		"environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&"+
-		"module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&"+
+	url, err := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&" +
+		"application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&" +
+		"environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&" +
+		"module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&" +
 		"side=provider&timeout=3000&timestamp=1556509797245")
 	assert.NoError(t, err)
 	clientConf = &ClientConfig{}
diff --git a/protocol/dubbo/listener.go b/protocol/dubbo/listener.go
index df9ab28e0e4b896b11b2345a83cae14401a70759..0251b78a2b0d27a68461c16c284b1af53bcb08aa 100644
--- a/protocol/dubbo/listener.go
+++ b/protocol/dubbo/listener.go
@@ -18,15 +18,18 @@
 package dubbo
 
 import (
+	"context"
 	"fmt"
 	"net/url"
 	"sync"
+	"sync/atomic"
 	"time"
 )
 
 import (
 	"github.com/apache/dubbo-go-hessian2"
 	"github.com/dubbogo/getty"
+	"github.com/opentracing/opentracing-go"
 	perrors "github.com/pkg/errors"
 )
 
@@ -39,7 +42,10 @@ import (
 )
 
 // todo: WritePkg_Timeout will entry *.yml
-const WritePkg_Timeout = 5 * time.Second
+const (
+	// WritePkg_Timeout ...
+	WritePkg_Timeout = 5 * time.Second
+)
 
 var (
 	errTooManySessions = perrors.New("too many sessions")
@@ -50,33 +56,47 @@ type rpcSession struct {
 	reqNum  int32
 }
 
-////////////////////////////////////////////
+func (s *rpcSession) AddReqNum(num int32) {
+	atomic.AddInt32(&s.reqNum, num)
+}
+
+func (s *rpcSession) GetReqNum() int32 {
+	return atomic.LoadInt32(&s.reqNum)
+}
+
+// //////////////////////////////////////////
 // RpcClientHandler
-////////////////////////////////////////////
+// //////////////////////////////////////////
 
+// RpcClientHandler ...
 type RpcClientHandler struct {
 	conn *gettyRPCClient
 }
 
+// NewRpcClientHandler ...
 func NewRpcClientHandler(client *gettyRPCClient) *RpcClientHandler {
 	return &RpcClientHandler{conn: client}
 }
 
+// OnOpen ...
 func (h *RpcClientHandler) OnOpen(session getty.Session) error {
 	h.conn.addSession(session)
 	return nil
 }
 
+// OnError ...
 func (h *RpcClientHandler) OnError(session getty.Session, err error) {
 	logger.Infof("session{%s} got error{%v}, will be closed.", session.Stat(), err)
 	h.conn.removeSession(session)
 }
 
+// OnClose ...
 func (h *RpcClientHandler) OnClose(session getty.Session) {
 	logger.Infof("session{%s} is closing......", session.Stat())
 	h.conn.removeSession(session)
 }
 
+// OnMessage ...
 func (h *RpcClientHandler) OnMessage(session getty.Session, pkg interface{}) {
 	p, ok := pkg.(*DubboPackage)
 	if !ok {
@@ -85,11 +105,17 @@ func (h *RpcClientHandler) OnMessage(session getty.Session, pkg interface{}) {
 	}
 
 	if p.Header.Type&hessian.PackageHeartbeat != 0x00 {
-		logger.Debugf("get rpc heartbeat response{header: %#v, body: %#v}", p.Header, p.Body)
-		if p.Err != nil {
-			logger.Errorf("rpc heartbeat response{error: %#v}", p.Err)
+		if p.Header.Type&hessian.PackageResponse != 0x00 {
+			logger.Debugf("get rpc heartbeat response{header: %#v, body: %#v}", p.Header, p.Body)
+			if p.Err != nil {
+				logger.Errorf("rpc heartbeat response{error: %#v}", p.Err)
+			}
+			h.conn.pool.rpcClient.removePendingResponse(SequenceType(p.Header.ID))
+		} else {
+			logger.Debugf("get rpc heartbeat request{header: %#v, service: %#v, body: %#v}", p.Header, p.Service, p.Body)
+			p.Header.ResponseStatus = hessian.Response_OK
+			reply(session, p, hessian.PackageHeartbeat)
 		}
-		h.conn.pool.rpcClient.removePendingResponse(SequenceType(p.Header.ID))
 		return
 	}
 	logger.Debugf("get rpc response{header: %#v, body: %#v}", p.Header, p.Body)
@@ -98,6 +124,7 @@ func (h *RpcClientHandler) OnMessage(session getty.Session, pkg interface{}) {
 
 	pendingResponse := h.conn.pool.rpcClient.removePendingResponse(SequenceType(p.Header.ID))
 	if pendingResponse == nil {
+		logger.Errorf("failed to get pending response context for response package %s", *p)
 		return
 	}
 
@@ -114,6 +141,7 @@ func (h *RpcClientHandler) OnMessage(session getty.Session, pkg interface{}) {
 	}
 }
 
+// OnCron ...
 func (h *RpcClientHandler) OnCron(session getty.Session) {
 	rpcSession, err := h.conn.getClientRpcSession(session)
 	if err != nil {
@@ -131,10 +159,11 @@ func (h *RpcClientHandler) OnCron(session getty.Session) {
 	h.conn.pool.rpcClient.heartbeat(session)
 }
 
-////////////////////////////////////////////
+// //////////////////////////////////////////
 // RpcServerHandler
-////////////////////////////////////////////
+// //////////////////////////////////////////
 
+// RpcServerHandler ...
 type RpcServerHandler struct {
 	maxSessionNum  int
 	sessionTimeout time.Duration
@@ -142,6 +171,7 @@ type RpcServerHandler struct {
 	rwlock         sync.RWMutex
 }
 
+// NewRpcServerHandler ...
 func NewRpcServerHandler(maxSessionNum int, sessionTimeout time.Duration) *RpcServerHandler {
 	return &RpcServerHandler{
 		maxSessionNum:  maxSessionNum,
@@ -150,6 +180,7 @@ func NewRpcServerHandler(maxSessionNum int, sessionTimeout time.Duration) *RpcSe
 	}
 }
 
+// OnOpen ...
 func (h *RpcServerHandler) OnOpen(session getty.Session) error {
 	var err error
 	h.rwlock.RLock()
@@ -168,6 +199,7 @@ func (h *RpcServerHandler) OnOpen(session getty.Session) error {
 	return nil
 }
 
+// OnError ...
 func (h *RpcServerHandler) OnError(session getty.Session, err error) {
 	logger.Infof("session{%s} got error{%v}, will be closed.", session.Stat(), err)
 	h.rwlock.Lock()
@@ -175,6 +207,7 @@ func (h *RpcServerHandler) OnError(session getty.Session, err error) {
 	h.rwlock.Unlock()
 }
 
+// OnClose ...
 func (h *RpcServerHandler) OnClose(session getty.Session) {
 	logger.Infof("session{%s} is closing......", session.Stat())
 	h.rwlock.Lock()
@@ -182,6 +215,7 @@ func (h *RpcServerHandler) OnClose(session getty.Session) {
 	h.rwlock.Unlock()
 }
 
+// OnMessage ...
 func (h *RpcServerHandler) OnMessage(session getty.Session, pkg interface{}) {
 	h.rwlock.Lock()
 	if _, ok := h.sessionMap[session]; ok {
@@ -199,7 +233,7 @@ func (h *RpcServerHandler) OnMessage(session getty.Session, pkg interface{}) {
 	// heartbeat
 	if p.Header.Type&hessian.PackageHeartbeat != 0x00 {
 		logger.Debugf("get rpc heartbeat request{header: %#v, service: %#v, body: %#v}", p.Header, p.Service, p.Body)
-		h.reply(session, p, hessian.PackageHeartbeat)
+		reply(session, p, hessian.PackageHeartbeat)
 		return
 	}
 
@@ -226,7 +260,7 @@ func (h *RpcServerHandler) OnMessage(session getty.Session, pkg interface{}) {
 			if !twoway {
 				return
 			}
-			h.reply(session, p, hessian.PackageResponse)
+			reply(session, p, hessian.PackageResponse)
 		}
 
 	}()
@@ -241,7 +275,7 @@ func (h *RpcServerHandler) OnMessage(session getty.Session, pkg interface{}) {
 		logger.Errorf(err.Error())
 		p.Header.ResponseStatus = hessian.Response_OK
 		p.Body = err
-		h.reply(session, p, hessian.PackageResponse)
+		reply(session, p, hessian.PackageResponse)
 		return
 	}
 	invoker := exporter.(protocol.Exporter).GetInvoker()
@@ -252,7 +286,10 @@ func (h *RpcServerHandler) OnMessage(session getty.Session, pkg interface{}) {
 
 		args := p.Body.(map[string]interface{})["args"].([]interface{})
 		inv := invocation.NewRPCInvocation(p.Service.Method, args, attachments)
-		result := invoker.Invoke(inv)
+
+		ctx := rebuildCtx(inv)
+
+		result := invoker.Invoke(ctx, inv)
 		if err := result.Error(); err != nil {
 			p.Header.ResponseStatus = hessian.Response_OK
 			p.Body = hessian.NewResponse(nil, err, result.Attachments())
@@ -266,9 +303,10 @@ func (h *RpcServerHandler) OnMessage(session getty.Session, pkg interface{}) {
 	if !twoway {
 		return
 	}
-	h.reply(session, p, hessian.PackageResponse)
+	reply(session, p, hessian.PackageResponse)
 }
 
+// OnCron ...
 func (h *RpcServerHandler) OnCron(session getty.Session) {
 	var (
 		flag   bool
@@ -294,7 +332,22 @@ func (h *RpcServerHandler) OnCron(session getty.Session) {
 	}
 }
 
-func (h *RpcServerHandler) reply(session getty.Session, req *DubboPackage, tp hessian.PackageType) {
+// rebuildCtx rebuild the context by attachment.
+// Once we decided to transfer more context's key-value, we should change this.
+// now we only support rebuild the tracing context
+func rebuildCtx(inv *invocation.RPCInvocation) context.Context {
+	ctx := context.Background()
+
+	// actually, if user do not use any opentracing framework, the err will not be nil.
+	spanCtx, err := opentracing.GlobalTracer().Extract(opentracing.TextMap,
+		opentracing.TextMapCarrier(inv.Attachments()))
+	if err == nil {
+		ctx = context.WithValue(ctx, constant.TRACING_REMOTE_SPAN_CTX, spanCtx)
+	}
+	return ctx
+}
+
+func reply(session getty.Session, req *DubboPackage, tp hessian.PackageType) {
 	resp := &DubboPackage{
 		Header: hessian.DubboHeader{
 			SerialID:       req.Header.SerialID,
diff --git a/protocol/dubbo/listener_test.go b/protocol/dubbo/listener_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..5f809814607558650e09934019db96dbb2ceeeae
--- /dev/null
+++ b/protocol/dubbo/listener_test.go
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dubbo
+
+import (
+	"testing"
+)
+
+import (
+	"github.com/opentracing/opentracing-go"
+	"github.com/opentracing/opentracing-go/mocktracer"
+	"github.com/stretchr/testify/assert"
+)
+
+import (
+	"github.com/apache/dubbo-go/common/constant"
+	"github.com/apache/dubbo-go/protocol/invocation"
+)
+
+// test rebuild the ctx
+func TestRebuildCtx(t *testing.T) {
+	opentracing.SetGlobalTracer(mocktracer.New())
+	attach := make(map[string]string, 10)
+	attach[constant.VERSION_KEY] = "1.0"
+	attach[constant.GROUP_KEY] = "MyGroup"
+	inv := invocation.NewRPCInvocation("MethodName", []interface{}{"OK", "Hello"}, attach)
+
+	// attachment doesn't contains any tracing key-value pair,
+	ctx := rebuildCtx(inv)
+	assert.NotNil(t, ctx)
+	assert.Nil(t, ctx.Value(constant.TRACING_REMOTE_SPAN_CTX))
+
+	span, ctx := opentracing.StartSpanFromContext(ctx, "Test-Client")
+
+	opentracing.GlobalTracer().Inject(span.Context(), opentracing.TextMap,
+		opentracing.TextMapCarrier(inv.Attachments()))
+	// rebuild the context success
+	inv = invocation.NewRPCInvocation("MethodName", []interface{}{"OK", "Hello"}, attach)
+	ctx = rebuildCtx(inv)
+	span.Finish()
+	assert.NotNil(t, ctx)
+	assert.NotNil(t, ctx.Value(constant.TRACING_REMOTE_SPAN_CTX))
+}
diff --git a/protocol/dubbo/pool.go b/protocol/dubbo/pool.go
index d619a2f8fe78524b3d704cb9de280ebbf534eb12..918514c2676cfc69336a9f53e6d16d7f23cf7dca 100644
--- a/protocol/dubbo/pool.go
+++ b/protocol/dubbo/pool.go
@@ -78,7 +78,7 @@ func newGettyRPCClientConn(pool *gettyRPCClientPool, protocol, addr string) (*ge
 		}
 		time.Sleep(1e6)
 	}
-	logger.Infof("client init ok")
+	logger.Debug("client init ok")
 	c.updateActive(time.Now().Unix())
 
 	return c, nil
@@ -154,11 +154,11 @@ func (c *gettyRPCClient) addSession(session getty.Session) {
 	}
 
 	c.lock.Lock()
+	defer c.lock.Unlock()
 	if c.sessions == nil {
 		c.sessions = make([]*rpcSession, 0, 16)
 	}
 	c.sessions = append(c.sessions, &rpcSession{session: session})
-	c.lock.Unlock()
 }
 
 func (c *gettyRPCClient) removeSession(session getty.Session) {
@@ -166,21 +166,27 @@ func (c *gettyRPCClient) removeSession(session getty.Session) {
 		return
 	}
 
-	c.lock.Lock()
-	defer c.lock.Unlock()
-	if c.sessions == nil {
-		return
-	}
+	var removeFlag bool
+	func() {
+		c.lock.Lock()
+		defer c.lock.Unlock()
+		if c.sessions == nil {
+			return
+		}
 
-	for i, s := range c.sessions {
-		if s.session == session {
-			c.sessions = append(c.sessions[:i], c.sessions[i+1:]...)
-			logger.Debugf("delete session{%s}, its index{%d}", session.Stat(), i)
-			break
+		for i, s := range c.sessions {
+			if s.session == session {
+				c.sessions = append(c.sessions[:i], c.sessions[i+1:]...)
+				logger.Debugf("delete session{%s}, its index{%d}", session.Stat(), i)
+				break
+			}
 		}
-	}
-	logger.Infof("after remove session{%s}, left session number:%d", session.Stat(), len(c.sessions))
-	if len(c.sessions) == 0 {
+		logger.Infof("after remove session{%s}, left session number:%d", session.Stat(), len(c.sessions))
+		if len(c.sessions) == 0 {
+			removeFlag = true
+		}
+	}()
+	if removeFlag {
 		c.pool.safeRemove(c)
 		c.close()
 	}
@@ -190,17 +196,24 @@ func (c *gettyRPCClient) updateSession(session getty.Session) {
 	if session == nil {
 		return
 	}
-	c.lock.Lock()
-	defer c.lock.Unlock()
-	if c.sessions == nil {
-		return
-	}
 
-	for i, s := range c.sessions {
-		if s.session == session {
-			c.sessions[i].reqNum++
-			break
+	var rs *rpcSession
+	func() {
+		c.lock.RLock()
+		defer c.lock.RUnlock()
+		if c.sessions == nil {
+			return
+		}
+
+		for i, s := range c.sessions {
+			if s.session == session {
+				rs = c.sessions[i]
+				break
+			}
 		}
+	}()
+	if rs != nil {
+		rs.AddReqNum(1)
 	}
 }
 
@@ -238,28 +251,42 @@ func (c *gettyRPCClient) isAvailable() bool {
 func (c *gettyRPCClient) close() error {
 	closeErr := perrors.Errorf("close gettyRPCClient{%#v} again", c)
 	c.once.Do(func() {
-		c.gettyClient.Close()
-		c.gettyClient = nil
-		for _, s := range c.sessions {
-			logger.Infof("close client session{%s, last active:%s, request number:%d}",
-				s.session.Stat(), s.session.GetActive().String(), s.reqNum)
-			s.session.Close()
-		}
-		c.sessions = c.sessions[:0]
+		var (
+			gettyClient getty.Client
+			sessions    []*rpcSession
+		)
+		func() {
+			c.lock.Lock()
+			defer c.lock.Unlock()
+
+			gettyClient = c.gettyClient
+			c.gettyClient = nil
+
+			sessions = make([]*rpcSession, 0, len(c.sessions))
+			for _, s := range c.sessions {
+				sessions = append(sessions, s)
+			}
+			c.sessions = c.sessions[:0]
+		}()
 
 		c.updateActive(0)
+
+		go func() {
+			if gettyClient != nil {
+				gettyClient.Close()
+			}
+			for _, s := range sessions {
+				logger.Infof("close client session{%s, last active:%s, request number:%d}",
+					s.session.Stat(), s.session.GetActive().String(), s.GetReqNum())
+				s.session.Close()
+			}
+		}()
+
 		closeErr = nil
 	})
 	return closeErr
 }
 
-func (c *gettyRPCClient) safeClose() error {
-	c.lock.Lock()
-	defer c.lock.Unlock()
-
-	return c.close()
-}
-
 type gettyRPCClientPool struct {
 	rpcClient *Client
 	size      int   // size of []*gettyRPCClient
@@ -284,11 +311,22 @@ func (p *gettyRPCClientPool) close() {
 	p.conns = nil
 	p.Unlock()
 	for _, conn := range conns {
-		conn.safeClose()
+		conn.close()
 	}
 }
 
 func (p *gettyRPCClientPool) getGettyRpcClient(protocol, addr string) (*gettyRPCClient, error) {
+	conn, err := p.get()
+	if err == nil && conn == nil {
+		// create new conn
+		rpcClientConn, err := newGettyRPCClientConn(p, protocol, addr)
+		return rpcClientConn, perrors.WithStack(err)
+	}
+	return conn, perrors.WithStack(err)
+}
+
+func (p *gettyRPCClientPool) get() (*gettyRPCClient, error) {
+	now := time.Now().Unix()
 
 	p.Lock()
 	defer p.Unlock()
@@ -296,35 +334,26 @@ func (p *gettyRPCClientPool) getGettyRpcClient(protocol, addr string) (*gettyRPC
 		return nil, errClientPoolClosed
 	}
 
-	now := time.Now().Unix()
-
 	for len(p.conns) > 0 {
 		conn := p.conns[len(p.conns)-1]
 		p.conns = p.conns[:len(p.conns)-1]
 
 		if d := now - conn.getActive(); d > p.ttl {
 			p.remove(conn)
-			conn.safeClose()
+			go conn.close()
 			continue
 		}
 		conn.updateActive(now) //update active time
-
 		return conn, nil
 	}
-	// create new conn
-	return newGettyRPCClientConn(p, protocol, addr)
+	return nil, nil
 }
 
-func (p *gettyRPCClientPool) release(conn *gettyRPCClient, err error) {
+func (p *gettyRPCClientPool) put(conn *gettyRPCClient) {
 	if conn == nil || conn.getActive() == 0 {
 		return
 	}
 
-	if err != nil {
-		conn.safeClose()
-		return
-	}
-
 	p.Lock()
 	defer p.Unlock()
 
@@ -332,10 +361,17 @@ func (p *gettyRPCClientPool) release(conn *gettyRPCClient, err error) {
 		return
 	}
 
+	// check whether @conn has existed in p.conns or not.
+	for i := range p.conns {
+		if p.conns[i] == conn {
+			return
+		}
+	}
+
 	if len(p.conns) >= p.size {
 		// delete @conn from client pool
-		p.remove(conn)
-		conn.safeClose()
+		// p.remove(conn)
+		conn.close()
 		return
 	}
 	p.conns = append(p.conns, conn)
diff --git a/protocol/dubbo/readwriter.go b/protocol/dubbo/readwriter.go
index 930382cca8bac6955b516a88e93ce26d73e235fe..b5c4f509190dbdc85825ad424656240b234786df 100644
--- a/protocol/dubbo/readwriter.go
+++ b/protocol/dubbo/readwriter.go
@@ -38,10 +38,12 @@ import (
 // RpcClientPackageHandler
 ////////////////////////////////////////////
 
+// RpcClientPackageHandler ...
 type RpcClientPackageHandler struct {
 	client *Client
 }
 
+// NewRpcClientPackageHandler ...
 func NewRpcClientPackageHandler(client *Client) *RpcClientPackageHandler {
 	return &RpcClientPackageHandler{client: client}
 }
@@ -62,8 +64,10 @@ func (p *RpcClientPackageHandler) Read(ss getty.Session, data []byte) (interface
 		return nil, 0, perrors.WithStack(err)
 	}
 
-	pkg.Err = pkg.Body.(*hessian.Response).Exception
-	pkg.Body = NewResponse(pkg.Body.(*hessian.Response).RspObj, pkg.Body.(*hessian.Response).Attachments)
+	if pkg.Header.Type&hessian.PackageRequest == 0x00 {
+		pkg.Err = pkg.Body.(*hessian.Response).Exception
+		pkg.Body = NewResponse(pkg.Body.(*hessian.Response).RspObj, pkg.Body.(*hessian.Response).Attachments)
+	}
 
 	return pkg, hessian.HEADER_LENGTH + pkg.Header.BodyLen, nil
 }
@@ -92,6 +96,7 @@ var (
 	rpcServerPkgHandler = &RpcServerPackageHandler{}
 )
 
+// RpcServerPackageHandler ...
 type RpcServerPackageHandler struct{}
 
 func (p *RpcServerPackageHandler) Read(ss getty.Session, data []byte) (interface{}, int, error) {
diff --git a/protocol/dubbo/server.go b/protocol/dubbo/server.go
index 648c9f8aa8d24a321bfda85279a6470c745dbfa1..bd2b37b7a9f055745e183524d19a442af03360f4 100644
--- a/protocol/dubbo/server.go
+++ b/protocol/dubbo/server.go
@@ -74,6 +74,7 @@ func init() {
 	SetServerGrpool()
 }
 
+// SetServerConfig ...
 func SetServerConfig(s ServerConfig) {
 	srvConf = &s
 	err := srvConf.CheckValidity()
@@ -84,10 +85,12 @@ func SetServerConfig(s ServerConfig) {
 	SetServerGrpool()
 }
 
+// GetServerConfig ...
 func GetServerConfig() ServerConfig {
 	return *srvConf
 }
 
+// SetServerGrpool ...
 func SetServerGrpool() {
 	if srvConf.GrPoolSize > 1 {
 		srvGrpool = gxsync.NewTaskPool(gxsync.WithTaskPoolTaskPoolSize(srvConf.GrPoolSize), gxsync.WithTaskPoolTaskQueueLength(srvConf.QueueLen),
@@ -95,12 +98,14 @@ func SetServerGrpool() {
 	}
 }
 
+// Server ...
 type Server struct {
 	conf       ServerConfig
 	tcpServer  getty.Server
 	rpcHandler *RpcServerHandler
 }
 
+// NewServer ...
 func NewServer() *Server {
 
 	s := &Server{
@@ -151,6 +156,7 @@ func (s *Server) newSession(session getty.Session) error {
 	return nil
 }
 
+// Start ...
 func (s *Server) Start(url common.URL) {
 	var (
 		addr      string
@@ -167,6 +173,7 @@ func (s *Server) Start(url common.URL) {
 
 }
 
+// Stop ...
 func (s *Server) Stop() {
 	s.tcpServer.Close()
 }
diff --git a/protocol/grpc/client.go b/protocol/grpc/client.go
new file mode 100644
index 0000000000000000000000000000000000000000..d35a2c770cd8b9bda805715889791ccf53c562db
--- /dev/null
+++ b/protocol/grpc/client.go
@@ -0,0 +1,63 @@
+/*
+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 grpc
+
+import (
+	"reflect"
+)
+
+import (
+	"google.golang.org/grpc"
+)
+
+import (
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/common/constant"
+	"github.com/apache/dubbo-go/config"
+)
+
+// Client ...
+type Client struct {
+	*grpc.ClientConn
+	invoker reflect.Value
+}
+
+// NewClient ...
+func NewClient(url common.URL) *Client {
+	conn, err := grpc.Dial(url.Location, grpc.WithInsecure(), grpc.WithBlock())
+	if err != nil {
+		panic(err)
+	}
+
+	key := url.GetParam(constant.BEAN_NAME_KEY, "")
+	impl := config.GetConsumerService(key)
+	invoker := getInvoker(impl, conn)
+
+	return &Client{
+		ClientConn: conn,
+		invoker:    reflect.ValueOf(invoker),
+	}
+}
+
+func getInvoker(impl interface{}, conn *grpc.ClientConn) interface{} {
+	in := []reflect.Value{}
+	in = append(in, reflect.ValueOf(conn))
+	method := reflect.ValueOf(impl).MethodByName("GetDubboStub")
+	res := method.Call(in)
+	return res[0].Interface()
+}
diff --git a/protocol/grpc/client_test.go b/protocol/grpc/client_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..56ec766f70da93bcddbcff13667a34c39deffe06
--- /dev/null
+++ b/protocol/grpc/client_test.go
@@ -0,0 +1,53 @@
+/*
+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 grpc
+
+import (
+	"reflect"
+	"testing"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+	"google.golang.org/grpc"
+)
+
+import (
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/protocol/grpc/internal"
+)
+
+func TestGetInvoker(t *testing.T) {
+	var conn *grpc.ClientConn
+	var impl *internal.GrpcGreeterImpl
+	invoker := getInvoker(impl, conn)
+
+	i := reflect.TypeOf(invoker)
+	expected := reflect.TypeOf(internal.NewGreeterClient(nil))
+	assert.Equal(t, i, expected)
+}
+
+func TestNewClient(t *testing.T) {
+	go internal.InitGrpcServer()
+	defer internal.ShutdownGrpcServer()
+
+	url, err := common.NewURL("grpc://127.0.0.1:30000/GrpcGreeterImpl?accesslog=&anyhost=true&app.version=0.0.1&application=BDTService&async=false&bean.name=GrpcGreeterImpl&category=providers&cluster=failover&dubbo=dubbo-provider-golang-2.6.0&environment=dev&execute.limit=&execute.limit.rejected.handler=&generic=false&group=&interface=io.grpc.examples.helloworld.GreeterGrpc%24IGreeter&ip=192.168.1.106&loadbalance=random&methods.SayHello.loadbalance=random&methods.SayHello.retries=1&methods.SayHello.tps.limit.interval=&methods.SayHello.tps.limit.rate=&methods.SayHello.tps.limit.strategy=&methods.SayHello.weight=0&module=dubbogo+say-hello+client&name=BDTService&organization=ikurento.com&owner=ZX&pid=49427&reference.filter=cshutdown&registry.role=3&remote.timestamp=1576923717&retries=&service.filter=echo%2Ctoken%2Caccesslog%2Ctps%2Cexecute%2Cpshutdown&side=provider&timestamp=1576923740&tps.limit.interval=&tps.limit.rate=&tps.limit.rejected.handler=&tps.limit.strategy=&tps.limiter=&version=&warmup=100!")
+	assert.Nil(t, err)
+	cli := NewClient(url)
+	assert.NotNil(t, cli)
+}
diff --git a/protocol/grpc/common_test.go b/protocol/grpc/common_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..3d0823b1061a61cfa391358e270c8b9081e9031c
--- /dev/null
+++ b/protocol/grpc/common_test.go
@@ -0,0 +1,114 @@
+/*
+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 grpc
+
+import (
+	"context"
+	"fmt"
+)
+
+import (
+	native_grpc "google.golang.org/grpc"
+)
+
+import (
+	"github.com/apache/dubbo-go/config"
+	"github.com/apache/dubbo-go/protocol"
+	"github.com/apache/dubbo-go/protocol/grpc/internal"
+	"github.com/apache/dubbo-go/protocol/invocation"
+)
+
+// userd grpc-dubbo biz service
+func addService() {
+	config.SetProviderService(newGreeterProvider())
+}
+
+type greeterProvider struct {
+	*greeterProviderBase
+}
+
+func newGreeterProvider() *greeterProvider {
+	return &greeterProvider{
+		greeterProviderBase: &greeterProviderBase{},
+	}
+}
+
+func (g *greeterProvider) SayHello(ctx context.Context, req *internal.HelloRequest) (reply *internal.HelloReply, err error) {
+	fmt.Printf("req: %v", req)
+	return &internal.HelloReply{Message: "this is message from reply"}, nil
+}
+
+func (g *greeterProvider) Reference() string {
+	return "GrpcGreeterImpl"
+}
+
+// code generated by greeter.go
+type greeterProviderBase struct {
+	proxyImpl protocol.Invoker
+}
+
+func (g *greeterProviderBase) SetProxyImpl(impl protocol.Invoker) {
+	g.proxyImpl = impl
+}
+
+func (g *greeterProviderBase) GetProxyImpl() protocol.Invoker {
+	return g.proxyImpl
+}
+
+func (g *greeterProviderBase) ServiceDesc() *native_grpc.ServiceDesc {
+	return &native_grpc.ServiceDesc{
+		ServiceName: "helloworld.Greeter",
+		HandlerType: (*internal.GreeterServer)(nil),
+		Methods: []native_grpc.MethodDesc{
+			{
+				MethodName: "SayHello",
+				Handler:    dubboGreeterSayHelloHandler,
+			},
+		},
+		Streams:  []native_grpc.StreamDesc{},
+		Metadata: "helloworld.proto",
+	}
+}
+
+func dubboGreeterSayHelloHandler(srv interface{}, ctx context.Context,
+	dec func(interface{}) error, interceptor native_grpc.UnaryServerInterceptor) (interface{}, error) {
+
+	in := new(internal.HelloRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	base := srv.(DubboGrpcService)
+
+	args := []interface{}{}
+	args = append(args, in)
+	invo := invocation.NewRPCInvocation("SayHello", args, nil)
+
+	if interceptor == nil {
+		result := base.GetProxyImpl().Invoke(context.Background(), invo)
+		return result.Result(), result.Error()
+	}
+	info := &native_grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/helloworld.Greeter/SayHello",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		result := base.GetProxyImpl().Invoke(context.Background(), invo)
+		return result.Result(), result.Error()
+	}
+	return interceptor(ctx, in, info, handler)
+}
diff --git a/protocol/grpc/grpc_exporter.go b/protocol/grpc/grpc_exporter.go
new file mode 100644
index 0000000000000000000000000000000000000000..3c38ef974ca22a582ce83102718d01a8edd4258f
--- /dev/null
+++ b/protocol/grpc/grpc_exporter.go
@@ -0,0 +1,51 @@
+/*
+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 grpc
+
+import (
+	"sync"
+)
+
+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"
+)
+
+// GrpcExporter ...
+type GrpcExporter struct {
+	*protocol.BaseExporter
+}
+
+// NewGrpcExporter ...
+func NewGrpcExporter(key string, invoker protocol.Invoker, exporterMap *sync.Map) *GrpcExporter {
+	return &GrpcExporter{
+		BaseExporter: protocol.NewBaseExporter(key, invoker, exporterMap),
+	}
+}
+
+// Unexport ...
+func (gg *GrpcExporter) Unexport() {
+	serviceId := gg.GetInvoker().GetUrl().GetParam(constant.BEAN_NAME_KEY, "")
+	gg.BaseExporter.Unexport()
+	err := common.ServiceMap.UnRegister(GRPC, serviceId)
+	if err != nil {
+		logger.Errorf("[GrpcExporter.Unexport] error: %v", err)
+	}
+}
diff --git a/protocol/grpc/grpc_invoker.go b/protocol/grpc/grpc_invoker.go
new file mode 100644
index 0000000000000000000000000000000000000000..26bc86f3aa46c8048b16284bd61cb5d9fb4664f9
--- /dev/null
+++ b/protocol/grpc/grpc_invoker.go
@@ -0,0 +1,104 @@
+/*
+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 grpc
+
+import (
+	"context"
+	"reflect"
+	"sync"
+)
+
+import (
+	"github.com/pkg/errors"
+	"google.golang.org/grpc/connectivity"
+)
+
+import (
+	hessian2 "github.com/apache/dubbo-go-hessian2"
+
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/protocol"
+)
+
+// ErrNoReply ...
+var ErrNoReply = errors.New("request need @response")
+
+// GrpcInvoker ...
+type GrpcInvoker struct {
+	protocol.BaseInvoker
+	quitOnce sync.Once
+	client   *Client
+}
+
+// NewGrpcInvoker ...
+func NewGrpcInvoker(url common.URL, client *Client) *GrpcInvoker {
+	return &GrpcInvoker{
+		BaseInvoker: *protocol.NewBaseInvoker(url),
+		client:      client,
+	}
+}
+
+// Invoke ...
+func (gi *GrpcInvoker) Invoke(ctx context.Context, invocation protocol.Invocation) protocol.Result {
+	var (
+		result protocol.RPCResult
+	)
+
+	if invocation.Reply() == nil {
+		result.Err = ErrNoReply
+	}
+
+	in := []reflect.Value{}
+	in = append(in, reflect.ValueOf(context.Background()))
+	in = append(in, invocation.ParameterValues()...)
+
+	methodName := invocation.MethodName()
+	method := gi.client.invoker.MethodByName(methodName)
+	res := method.Call(in)
+
+	result.Rest = res[0]
+	// check err
+	if !res[1].IsNil() {
+		result.Err = res[1].Interface().(error)
+	} else {
+		_ = hessian2.ReflectResponse(res[0], invocation.Reply())
+	}
+
+	return &result
+}
+
+// IsAvailable ...
+func (gi *GrpcInvoker) IsAvailable() bool {
+	return gi.BaseInvoker.IsAvailable() && gi.client.GetState() != connectivity.Shutdown
+}
+
+// IsDestroyed ...
+func (gi *GrpcInvoker) IsDestroyed() bool {
+	return gi.BaseInvoker.IsDestroyed() && gi.client.GetState() == connectivity.Shutdown
+}
+
+// Destroy ...
+func (gi *GrpcInvoker) Destroy() {
+	gi.quitOnce.Do(func() {
+		gi.BaseInvoker.Destroy()
+
+		if gi.client != nil {
+			_ = gi.client.Close()
+		}
+	})
+}
diff --git a/protocol/grpc/grpc_invoker_test.go b/protocol/grpc/grpc_invoker_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..368c1392ec03af310f93e8fc2173b8354975c99e
--- /dev/null
+++ b/protocol/grpc/grpc_invoker_test.go
@@ -0,0 +1,56 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package grpc
+
+import (
+	"context"
+	"reflect"
+	"testing"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+import (
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/protocol/grpc/internal"
+	"github.com/apache/dubbo-go/protocol/invocation"
+)
+
+func TestInvoke(t *testing.T) {
+	go internal.InitGrpcServer()
+	defer internal.ShutdownGrpcServer()
+
+	url, err := common.NewURL("grpc://127.0.0.1:30000/GrpcGreeterImpl?accesslog=&anyhost=true&app.version=0.0.1&application=BDTService&async=false&bean.name=GrpcGreeterImpl&category=providers&cluster=failover&dubbo=dubbo-provider-golang-2.6.0&environment=dev&execute.limit=&execute.limit.rejected.handler=&generic=false&group=&interface=io.grpc.examples.helloworld.GreeterGrpc%24IGreeter&ip=192.168.1.106&loadbalance=random&methods.SayHello.loadbalance=random&methods.SayHello.retries=1&methods.SayHello.tps.limit.interval=&methods.SayHello.tps.limit.rate=&methods.SayHello.tps.limit.strategy=&methods.SayHello.weight=0&module=dubbogo+say-hello+client&name=BDTService&organization=ikurento.com&owner=ZX&pid=49427&reference.filter=cshutdown&registry.role=3&remote.timestamp=1576923717&retries=&service.filter=echo%2Ctoken%2Caccesslog%2Ctps%2Cexecute%2Cpshutdown&side=provider&timestamp=1576923740&tps.limit.interval=&tps.limit.rate=&tps.limit.rejected.handler=&tps.limit.strategy=&tps.limiter=&version=&warmup=100!")
+	assert.Nil(t, err)
+
+	cli := NewClient(url)
+
+	invoker := NewGrpcInvoker(url, cli)
+
+	args := []reflect.Value{}
+	args = append(args, reflect.ValueOf(&internal.HelloRequest{Name: "request name"}))
+	bizReply := &internal.HelloReply{}
+	invo := invocation.NewRPCInvocationWithOptions(invocation.WithMethodName("SayHello"),
+		invocation.WithParameterValues(args), invocation.WithReply(bizReply))
+	res := invoker.Invoke(context.Background(), invo)
+	assert.Nil(t, res.Error())
+	assert.NotNil(t, res.Result())
+	assert.Equal(t, "Hello request name", bizReply.Message)
+}
diff --git a/protocol/grpc/grpc_protocol.go b/protocol/grpc/grpc_protocol.go
new file mode 100644
index 0000000000000000000000000000000000000000..0f5625c152cc366289143b8a29d11cafb513b2f2
--- /dev/null
+++ b/protocol/grpc/grpc_protocol.go
@@ -0,0 +1,113 @@
+/*
+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 grpc
+
+import (
+	"sync"
+)
+
+import (
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/common/extension"
+	"github.com/apache/dubbo-go/common/logger"
+	"github.com/apache/dubbo-go/protocol"
+)
+
+const (
+	// GRPC module name
+	GRPC = "grpc"
+)
+
+func init() {
+	extension.SetProtocol(GRPC, GetProtocol)
+}
+
+var grpcProtocol *GrpcProtocol
+
+// GrpcProtocol ...
+type GrpcProtocol struct {
+	protocol.BaseProtocol
+	serverMap  map[string]*Server
+	serverLock sync.Mutex
+}
+
+// NewGRPCProtocol ...
+func NewGRPCProtocol() *GrpcProtocol {
+	return &GrpcProtocol{
+		BaseProtocol: protocol.NewBaseProtocol(),
+		serverMap:    make(map[string]*Server),
+	}
+}
+
+// Export ...
+func (gp *GrpcProtocol) Export(invoker protocol.Invoker) protocol.Exporter {
+	url := invoker.GetUrl()
+	serviceKey := url.ServiceKey()
+	exporter := NewGrpcExporter(serviceKey, invoker, gp.ExporterMap())
+	gp.SetExporterMap(serviceKey, exporter)
+	logger.Infof("Export service: %s", url.String())
+	gp.openServer(url)
+	return exporter
+}
+
+func (gp *GrpcProtocol) openServer(url common.URL) {
+	_, ok := gp.serverMap[url.Location]
+	if !ok {
+		_, ok := gp.ExporterMap().Load(url.ServiceKey())
+		if !ok {
+			panic("[GrpcProtocol]" + url.Key() + "is not existing")
+		}
+
+		gp.serverLock.Lock()
+		_, ok = gp.serverMap[url.Location]
+		if !ok {
+			srv := NewServer()
+			gp.serverMap[url.Location] = srv
+			srv.Start(url)
+		}
+		gp.serverLock.Unlock()
+	}
+}
+
+// Refer ...
+func (gp *GrpcProtocol) Refer(url common.URL) protocol.Invoker {
+	invoker := NewGrpcInvoker(url, NewClient(url))
+	gp.SetInvokers(invoker)
+	logger.Infof("Refer service: %s", url.String())
+	return invoker
+}
+
+// Destroy ...
+func (gp *GrpcProtocol) Destroy() {
+	logger.Infof("GrpcProtocol destroy.")
+
+	gp.BaseProtocol.Destroy()
+
+	for key, server := range gp.serverMap {
+		delete(gp.serverMap, key)
+		server.Stop()
+	}
+}
+
+// GetProtocol ...
+func GetProtocol() protocol.Protocol {
+	if grpcProtocol == nil {
+		grpcProtocol = NewGRPCProtocol()
+	}
+	return grpcProtocol
+}
diff --git a/protocol/grpc/grpc_protocol_test.go b/protocol/grpc/grpc_protocol_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..d0206a0fd953e40a478c26a2298f4889d8f72771
--- /dev/null
+++ b/protocol/grpc/grpc_protocol_test.go
@@ -0,0 +1,84 @@
+/*
+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 grpc
+
+import (
+	"testing"
+	"time"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+import (
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/protocol"
+	"github.com/apache/dubbo-go/protocol/grpc/internal"
+)
+
+func TestGrpcProtocol_Export(t *testing.T) {
+	// Export
+	addService()
+
+	proto := GetProtocol()
+	url, err := common.NewURL("grpc://127.0.0.1:40000/GrpcGreeterImpl?accesslog=&app.version=0.0.1&application=BDTService&bean.name=GrpcGreeterImpl&cluster=failover&environment=dev&execute.limit=&execute.limit.rejected.handler=&group=&interface=io.grpc.examples.helloworld.GreeterGrpc%24IGreeter&loadbalance=random&methods.SayHello.loadbalance=random&methods.SayHello.retries=1&methods.SayHello.tps.limit.interval=&methods.SayHello.tps.limit.rate=&methods.SayHello.tps.limit.strategy=&methods.SayHello.weight=0&module=dubbogo+say-hello+client&name=BDTService&organization=ikurento.com&owner=ZX&registry.role=3&retries=&service.filter=echo%2Ctoken%2Caccesslog%2Ctps%2Cexecute%2Cpshutdown&timestamp=1576923717&tps.limit.interval=&tps.limit.rate=&tps.limit.rejected.handler=&tps.limit.strategy=&tps.limiter=&version=&warmup=100")
+	assert.NoError(t, err)
+	exporter := proto.Export(protocol.NewBaseInvoker(url))
+	time.Sleep(time.Second)
+
+	// make sure url
+	eq := exporter.GetInvoker().GetUrl().URLEqual(url)
+	assert.True(t, eq)
+
+	// make sure exporterMap after 'Unexport'
+	_, ok := proto.(*GrpcProtocol).ExporterMap().Load(url.ServiceKey())
+	assert.True(t, ok)
+	exporter.Unexport()
+	_, ok = proto.(*GrpcProtocol).ExporterMap().Load(url.ServiceKey())
+	assert.False(t, ok)
+
+	// make sure serverMap after 'Destroy'
+	_, ok = proto.(*GrpcProtocol).serverMap[url.Location]
+	assert.True(t, ok)
+	proto.Destroy()
+	_, ok = proto.(*GrpcProtocol).serverMap[url.Location]
+	assert.False(t, ok)
+}
+
+func TestGrpcProtocol_Refer(t *testing.T) {
+	go internal.InitGrpcServer()
+	defer internal.ShutdownGrpcServer()
+	time.Sleep(time.Second)
+
+	proto := GetProtocol()
+	url, err := common.NewURL("grpc://127.0.0.1:30000/GrpcGreeterImpl?accesslog=&anyhost=true&app.version=0.0.1&application=BDTService&async=false&bean.name=GrpcGreeterImpl&category=providers&cluster=failover&dubbo=dubbo-provider-golang-2.6.0&environment=dev&execute.limit=&execute.limit.rejected.handler=&generic=false&group=&interface=io.grpc.examples.helloworld.GreeterGrpc%24IGreeter&ip=192.168.1.106&loadbalance=random&methods.SayHello.loadbalance=random&methods.SayHello.retries=1&methods.SayHello.tps.limit.interval=&methods.SayHello.tps.limit.rate=&methods.SayHello.tps.limit.strategy=&methods.SayHello.weight=0&module=dubbogo+say-hello+client&name=BDTService&organization=ikurento.com&owner=ZX&pid=49427&reference.filter=cshutdown&registry.role=3&remote.timestamp=1576923717&retries=&service.filter=echo%2Ctoken%2Caccesslog%2Ctps%2Cexecute%2Cpshutdown&side=provider&timestamp=1576923740&tps.limit.interval=&tps.limit.rate=&tps.limit.rejected.handler=&tps.limit.strategy=&tps.limiter=&version=&warmup=100!")
+	assert.NoError(t, err)
+	invoker := proto.Refer(url)
+
+	// make sure url
+	eq := invoker.GetUrl().URLEqual(url)
+	assert.True(t, eq)
+
+	// make sure invokers after 'Destroy'
+	invokersLen := len(proto.(*GrpcProtocol).Invokers())
+	assert.Equal(t, 1, invokersLen)
+	proto.Destroy()
+	invokersLen = len(proto.(*GrpcProtocol).Invokers())
+	assert.Equal(t, 0, invokersLen)
+}
diff --git a/protocol/grpc/internal/client.go b/protocol/grpc/internal/client.go
new file mode 100644
index 0000000000000000000000000000000000000000..d236e3046a90e9179fba07a0be5edb07f8c2a3e8
--- /dev/null
+++ b/protocol/grpc/internal/client.go
@@ -0,0 +1,50 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package internal
+
+import (
+	"context"
+)
+
+import (
+	"google.golang.org/grpc"
+)
+
+import (
+	"github.com/apache/dubbo-go/config"
+)
+
+func init() {
+	config.SetConsumerService(&GrpcGreeterImpl{})
+}
+
+// GrpcGreeterImpl
+//used for dubbo-grpc biz client
+type GrpcGreeterImpl struct {
+	SayHello func(ctx context.Context, in *HelloRequest, out *HelloReply) error
+}
+
+// Reference ...
+func (u *GrpcGreeterImpl) Reference() string {
+	return "GrpcGreeterImpl"
+}
+
+// GetDubboStub ...
+func (u *GrpcGreeterImpl) GetDubboStub(cc *grpc.ClientConn) GreeterClient {
+	return NewGreeterClient(cc)
+}
diff --git a/protocol/grpc/internal/doc.go b/protocol/grpc/internal/doc.go
new file mode 100644
index 0000000000000000000000000000000000000000..f2ef2ebd5e41980e1e1f1b0071ca7bb3885539f7
--- /dev/null
+++ b/protocol/grpc/internal/doc.go
@@ -0,0 +1,19 @@
+/*
+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.
+*/
+
+// just for test, never use internal for production.
+package internal
diff --git a/protocol/grpc/internal/helloworld.pb.go b/protocol/grpc/internal/helloworld.pb.go
new file mode 100644
index 0000000000000000000000000000000000000000..79b74ac65011208ae74f989cf86e4e6f9f446015
--- /dev/null
+++ b/protocol/grpc/internal/helloworld.pb.go
@@ -0,0 +1,227 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: helloworld.proto
+
+package internal
+
+import (
+	"context"
+	"fmt"
+	"math"
+
+	"github.com/golang/protobuf/proto"
+	"google.golang.org/grpc"
+	"google.golang.org/grpc/codes"
+	"google.golang.org/grpc/status"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
+
+// The request message containing the user's name.
+type HelloRequest struct {
+	Name                 string   `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *HelloRequest) Reset()         { *m = HelloRequest{} }
+func (m *HelloRequest) String() string { return proto.CompactTextString(m) }
+func (*HelloRequest) ProtoMessage()    {}
+func (*HelloRequest) Descriptor() ([]byte, []int) {
+	return fileDescriptor_17b8c58d586b62f2, []int{0}
+}
+
+func (m *HelloRequest) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_HelloRequest.Unmarshal(m, b)
+}
+func (m *HelloRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_HelloRequest.Marshal(b, m, deterministic)
+}
+func (m *HelloRequest) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_HelloRequest.Merge(m, src)
+}
+func (m *HelloRequest) XXX_Size() int {
+	return xxx_messageInfo_HelloRequest.Size(m)
+}
+func (m *HelloRequest) XXX_DiscardUnknown() {
+	xxx_messageInfo_HelloRequest.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_HelloRequest proto.InternalMessageInfo
+
+func (m *HelloRequest) GetName() string {
+	if m != nil {
+		return m.Name
+	}
+	return ""
+}
+
+// The response message containing the greetings
+type HelloReply struct {
+	Message              string   `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *HelloReply) Reset()         { *m = HelloReply{} }
+func (m *HelloReply) String() string { return proto.CompactTextString(m) }
+func (*HelloReply) ProtoMessage()    {}
+func (*HelloReply) Descriptor() ([]byte, []int) {
+	return fileDescriptor_17b8c58d586b62f2, []int{1}
+}
+
+func (m *HelloReply) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_HelloReply.Unmarshal(m, b)
+}
+func (m *HelloReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_HelloReply.Marshal(b, m, deterministic)
+}
+func (m *HelloReply) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_HelloReply.Merge(m, src)
+}
+func (m *HelloReply) XXX_Size() int {
+	return xxx_messageInfo_HelloReply.Size(m)
+}
+func (m *HelloReply) XXX_DiscardUnknown() {
+	xxx_messageInfo_HelloReply.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_HelloReply proto.InternalMessageInfo
+
+func (m *HelloReply) GetMessage() string {
+	if m != nil {
+		return m.Message
+	}
+	return ""
+}
+
+func init() {
+	proto.RegisterType((*HelloRequest)(nil), "helloworld.HelloRequest")
+	proto.RegisterType((*HelloReply)(nil), "helloworld.HelloReply")
+}
+
+func init() { proto.RegisterFile("helloworld.proto", fileDescriptor_17b8c58d586b62f2) }
+
+var fileDescriptor_17b8c58d586b62f2 = []byte{
+	// 175 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0xc8, 0x48, 0xcd, 0xc9,
+	0xc9, 0x2f, 0xcf, 0x2f, 0xca, 0x49, 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x42, 0x88,
+	0x28, 0x29, 0x71, 0xf1, 0x78, 0x80, 0x78, 0x41, 0xa9, 0x85, 0xa5, 0xa9, 0xc5, 0x25, 0x42, 0x42,
+	0x5c, 0x2c, 0x79, 0x89, 0xb9, 0xa9, 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0x9c, 0x41, 0x60, 0xb6, 0x92,
+	0x1a, 0x17, 0x17, 0x54, 0x4d, 0x41, 0x4e, 0xa5, 0x90, 0x04, 0x17, 0x7b, 0x6e, 0x6a, 0x71, 0x71,
+	0x62, 0x3a, 0x4c, 0x11, 0x8c, 0x6b, 0xe4, 0xc9, 0xc5, 0xee, 0x5e, 0x94, 0x9a, 0x5a, 0x92, 0x5a,
+	0x24, 0x64, 0xc7, 0xc5, 0x11, 0x9c, 0x58, 0x09, 0xd6, 0x25, 0x24, 0xa1, 0x87, 0xe4, 0x02, 0x64,
+	0xcb, 0xa4, 0xc4, 0xb0, 0xc8, 0x14, 0xe4, 0x54, 0x2a, 0x31, 0x38, 0x19, 0x70, 0x49, 0x67, 0xe6,
+	0xeb, 0xa5, 0x17, 0x15, 0x24, 0xeb, 0xa5, 0x56, 0x24, 0xe6, 0x16, 0xe4, 0xa4, 0x16, 0x23, 0xa9,
+	0x75, 0xe2, 0x07, 0x2b, 0x0e, 0x07, 0xb1, 0x03, 0x40, 0x5e, 0x0a, 0x60, 0x4c, 0x62, 0x03, 0xfb,
+	0xcd, 0x18, 0x10, 0x00, 0x00, 0xff, 0xff, 0x0f, 0xb7, 0xcd, 0xf2, 0xef, 0x00, 0x00, 0x00,
+}
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ context.Context
+var _ grpc.ClientConn
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the grpc package it is being compiled against.
+const _ = grpc.SupportPackageIsVersion4
+
+// GreeterClient is the client API for Greeter service.
+//
+// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
+type GreeterClient interface {
+	// Sends a greeting
+	SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error)
+}
+
+type greeterClient struct {
+	cc *grpc.ClientConn
+}
+
+func NewGreeterClient(cc *grpc.ClientConn) GreeterClient {
+	return &greeterClient{cc}
+}
+
+func (c *greeterClient) SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error) {
+	out := new(HelloReply)
+	err := c.cc.Invoke(ctx, "/helloworld.Greeter/SayHello", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+// GreeterServer is the server API for Greeter service.
+type GreeterServer interface {
+	// Sends a greeting
+	SayHello(context.Context, *HelloRequest) (*HelloReply, error)
+}
+
+// UnimplementedGreeterServer can be embedded to have forward compatible implementations.
+type UnimplementedGreeterServer struct {
+}
+
+func (*UnimplementedGreeterServer) SayHello(ctx context.Context, req *HelloRequest) (*HelloReply, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method SayHello not implemented")
+}
+
+func RegisterGreeterServer(s *grpc.Server, srv GreeterServer) {
+	s.RegisterService(&_Greeter_serviceDesc, srv)
+}
+
+func _Greeter_SayHello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(HelloRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(GreeterServer).SayHello(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/helloworld.Greeter/SayHello",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(GreeterServer).SayHello(ctx, req.(*HelloRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+var _Greeter_serviceDesc = grpc.ServiceDesc{
+	ServiceName: "helloworld.Greeter",
+	HandlerType: (*GreeterServer)(nil),
+	Methods: []grpc.MethodDesc{
+		{
+			MethodName: "SayHello",
+			Handler:    _Greeter_SayHello_Handler,
+		},
+	},
+	Streams:  []grpc.StreamDesc{},
+	Metadata: "helloworld.proto",
+}
diff --git a/protocol/grpc/internal/server.go b/protocol/grpc/internal/server.go
new file mode 100644
index 0000000000000000000000000000000000000000..a0759f757dc44153e7f09b726db5e66176796c96
--- /dev/null
+++ b/protocol/grpc/internal/server.go
@@ -0,0 +1,66 @@
+/*
+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 internal
+
+import (
+	"context"
+	"log"
+	"net"
+)
+
+import (
+	"google.golang.org/grpc"
+)
+
+var (
+	s *grpc.Server
+)
+
+// server is used to implement helloworld.GreeterServer.
+type server struct {
+	UnimplementedGreeterServer
+}
+
+// SayHello implements helloworld.GreeterServer
+func (s *server) SayHello(ctx context.Context, in *HelloRequest) (*HelloReply, error) {
+	log.Printf("Received: %v", in.GetName())
+	return &HelloReply{Message: "Hello " + in.GetName()}, nil
+}
+
+// InitGrpcServer ...
+func InitGrpcServer() {
+	port := ":30000"
+
+	lis, err := net.Listen("tcp", port)
+	if err != nil {
+		log.Fatalf("failed to listen: %v", err)
+	}
+	s = grpc.NewServer()
+	RegisterGreeterServer(s, &server{})
+	if err := s.Serve(lis); err != nil {
+		log.Fatalf("failed to serve: %v", err)
+	}
+}
+
+// ShutdownGrpcServer ...
+func ShutdownGrpcServer() {
+	if s == nil {
+		return
+	}
+	s.GracefulStop()
+}
diff --git a/protocol/grpc/protoc-gen-dubbo/examples/Makefile b/protocol/grpc/protoc-gen-dubbo/examples/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..7893bbc51aa436f711bfb653dc81d9ec66b7e5c0
--- /dev/null
+++ b/protocol/grpc/protoc-gen-dubbo/examples/Makefile
@@ -0,0 +1,19 @@
+# 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.
+
+grpc-gen:
+	protoc -I ./ helloworld.proto --go_out=plugins=grpc:.
+dubbo-gen:
+	protoc -I ./ helloworld.proto  --dubbo_out=plugins=grpc+dubbo:.
diff --git a/protocol/grpc/protoc-gen-dubbo/examples/helloworld.pb.go b/protocol/grpc/protoc-gen-dubbo/examples/helloworld.pb.go
new file mode 100644
index 0000000000000000000000000000000000000000..f5d3a49b0916050fc6b2e6373fde0b70df0a1c31
--- /dev/null
+++ b/protocol/grpc/protoc-gen-dubbo/examples/helloworld.pb.go
@@ -0,0 +1,301 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: helloworld.proto
+
+package main
+
+import (
+	"context"
+	"fmt"
+	"math"
+
+	"github.com/golang/protobuf/proto"
+	"google.golang.org/grpc"
+	"google.golang.org/grpc/codes"
+	"google.golang.org/grpc/status"
+)
+
+import (
+	"github.com/apache/dubbo-go/protocol"
+	dgrpc "github.com/apache/dubbo-go/protocol/grpc"
+	"github.com/apache/dubbo-go/protocol/invocation"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
+
+// The request message containing the user's name.
+type HelloRequest struct {
+	Name                 string   `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *HelloRequest) Reset()         { *m = HelloRequest{} }
+func (m *HelloRequest) String() string { return proto.CompactTextString(m) }
+func (*HelloRequest) ProtoMessage()    {}
+func (*HelloRequest) Descriptor() ([]byte, []int) {
+	return fileDescriptor_17b8c58d586b62f2, []int{0}
+}
+
+func (m *HelloRequest) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_HelloRequest.Unmarshal(m, b)
+}
+func (m *HelloRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_HelloRequest.Marshal(b, m, deterministic)
+}
+func (m *HelloRequest) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_HelloRequest.Merge(m, src)
+}
+func (m *HelloRequest) XXX_Size() int {
+	return xxx_messageInfo_HelloRequest.Size(m)
+}
+func (m *HelloRequest) XXX_DiscardUnknown() {
+	xxx_messageInfo_HelloRequest.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_HelloRequest proto.InternalMessageInfo
+
+func (m *HelloRequest) GetName() string {
+	if m != nil {
+		return m.Name
+	}
+	return ""
+}
+
+// The response message containing the greetings
+type HelloReply struct {
+	Message              string   `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *HelloReply) Reset()         { *m = HelloReply{} }
+func (m *HelloReply) String() string { return proto.CompactTextString(m) }
+func (*HelloReply) ProtoMessage()    {}
+func (*HelloReply) Descriptor() ([]byte, []int) {
+	return fileDescriptor_17b8c58d586b62f2, []int{1}
+}
+
+func (m *HelloReply) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_HelloReply.Unmarshal(m, b)
+}
+func (m *HelloReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_HelloReply.Marshal(b, m, deterministic)
+}
+func (m *HelloReply) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_HelloReply.Merge(m, src)
+}
+func (m *HelloReply) XXX_Size() int {
+	return xxx_messageInfo_HelloReply.Size(m)
+}
+func (m *HelloReply) XXX_DiscardUnknown() {
+	xxx_messageInfo_HelloReply.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_HelloReply proto.InternalMessageInfo
+
+func (m *HelloReply) GetMessage() string {
+	if m != nil {
+		return m.Message
+	}
+	return ""
+}
+
+func init() {
+	proto.RegisterType((*HelloRequest)(nil), "main.HelloRequest")
+	proto.RegisterType((*HelloReply)(nil), "main.HelloReply")
+}
+
+func init() { proto.RegisterFile("helloworld.proto", fileDescriptor_17b8c58d586b62f2) }
+
+var fileDescriptor_17b8c58d586b62f2 = []byte{
+	// 185 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0xc8, 0x48, 0xcd, 0xc9,
+	0xc9, 0x2f, 0xcf, 0x2f, 0xca, 0x49, 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0xc9, 0x4d,
+	0xcc, 0xcc, 0x53, 0x52, 0xe2, 0xe2, 0xf1, 0x00, 0xc9, 0x04, 0xa5, 0x16, 0x96, 0xa6, 0x16, 0x97,
+	0x08, 0x09, 0x71, 0xb1, 0xe4, 0x25, 0xe6, 0xa6, 0x4a, 0x30, 0x2a, 0x30, 0x6a, 0x70, 0x06, 0x81,
+	0xd9, 0x4a, 0x6a, 0x5c, 0x5c, 0x50, 0x35, 0x05, 0x39, 0x95, 0x42, 0x12, 0x5c, 0xec, 0xb9, 0xa9,
+	0xc5, 0xc5, 0x89, 0xe9, 0x30, 0x45, 0x30, 0xae, 0x91, 0x2d, 0x17, 0xbb, 0x7b, 0x51, 0x6a, 0x6a,
+	0x49, 0x6a, 0x91, 0x90, 0x11, 0x17, 0x47, 0x70, 0x62, 0x25, 0x58, 0x97, 0x90, 0x90, 0x1e, 0xc8,
+	0x26, 0x3d, 0x64, 0x6b, 0xa4, 0x04, 0x50, 0xc4, 0x0a, 0x72, 0x2a, 0x95, 0x18, 0x9c, 0xcc, 0xb8,
+	0xa4, 0x33, 0xf3, 0xf5, 0xd2, 0x8b, 0x0a, 0x92, 0xf5, 0x52, 0x2b, 0x12, 0x73, 0x0b, 0x72, 0x52,
+	0x8b, 0xf5, 0x10, 0xae, 0x76, 0xe2, 0x07, 0x2b, 0x0e, 0x07, 0xb1, 0x03, 0x40, 0x1e, 0x08, 0x60,
+	0x5c, 0xc4, 0xc4, 0xec, 0xe1, 0x13, 0x9e, 0xc4, 0x06, 0xf6, 0x8f, 0x31, 0x20, 0x00, 0x00, 0xff,
+	0xff, 0xd2, 0x16, 0x5f, 0x34, 0xe3, 0x00, 0x00, 0x00,
+}
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ context.Context
+var _ grpc.ClientConn
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the grpc package it is being compiled against.
+const _ = grpc.SupportPackageIsVersion4
+
+// GreeterClient is the client API for Greeter service.
+//
+// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
+type GreeterClient interface {
+	// Sends a greeting
+	SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error)
+}
+
+type greeterClient struct {
+	cc *grpc.ClientConn
+}
+
+func NewGreeterClient(cc *grpc.ClientConn) GreeterClient {
+	return &greeterClient{cc}
+}
+
+func (c *greeterClient) SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error) {
+	out := new(HelloReply)
+	err := c.cc.Invoke(ctx, "/main.Greeter/SayHello", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+// GreeterServer is the server API for Greeter service.
+type GreeterServer interface {
+	// Sends a greeting
+	SayHello(context.Context, *HelloRequest) (*HelloReply, error)
+}
+
+// UnimplementedGreeterServer can be embedded to have forward compatible implementations.
+type UnimplementedGreeterServer struct {
+}
+
+func (*UnimplementedGreeterServer) SayHello(ctx context.Context, req *HelloRequest) (*HelloReply, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method SayHello not implemented")
+}
+
+func RegisterGreeterServer(s *grpc.Server, srv GreeterServer) {
+	s.RegisterService(&_Greeter_serviceDesc, srv)
+}
+
+func _Greeter_SayHello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(HelloRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(GreeterServer).SayHello(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/main.Greeter/SayHello",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(GreeterServer).SayHello(ctx, req.(*HelloRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+var _Greeter_serviceDesc = grpc.ServiceDesc{
+	ServiceName: "main.Greeter",
+	HandlerType: (*GreeterServer)(nil),
+	Methods: []grpc.MethodDesc{
+		{
+			MethodName: "SayHello",
+			Handler:    _Greeter_SayHello_Handler,
+		},
+	},
+	Streams:  []grpc.StreamDesc{},
+	Metadata: "helloworld.proto",
+}
+
+// GreeterClientImpl is the client API for Greeter service.
+//
+// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
+type GreeterClientImpl struct {
+	// Sends a greeting
+	SayHello func(ctx context.Context, in *HelloRequest, out *HelloReply) error
+}
+
+func (c *GreeterClientImpl) Reference() string {
+	return "greeterImpl"
+}
+
+func (c *GreeterClientImpl) GetDubboStub(cc *grpc.ClientConn) GreeterClient {
+	return NewGreeterClient(cc)
+}
+
+type GreeterProviderBase struct {
+	proxyImpl protocol.Invoker
+}
+
+func (s *GreeterProviderBase) SetProxyImpl(impl protocol.Invoker) {
+	s.proxyImpl = impl
+}
+
+func (s *GreeterProviderBase) GetProxyImpl() protocol.Invoker {
+	return s.proxyImpl
+}
+
+func _DUBBO_Greeter_SayHello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(HelloRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	base := srv.(dgrpc.DubboGrpcService)
+	args := []interface{}{}
+	args = append(args, in)
+	invo := invocation.NewRPCInvocation("SayHello", args, nil)
+	if interceptor == nil {
+		result := base.GetProxyImpl().Invoke(context.Background(), invo)
+		return result.Result(), result.Error()
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/main.Greeter/SayHello",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		result := base.GetProxyImpl().Invoke(context.Background(), invo)
+		return result.Result(), result.Error()
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+func (s *GreeterProviderBase) ServiceDesc() *grpc.ServiceDesc {
+	return &grpc.ServiceDesc{
+		ServiceName: "main.Greeter",
+		HandlerType: (*GreeterServer)(nil),
+		Methods: []grpc.MethodDesc{
+			{
+				MethodName: "SayHello",
+				Handler:    _DUBBO_Greeter_SayHello_Handler,
+			},
+		},
+		Streams:  []grpc.StreamDesc{},
+		Metadata: "helloworld.proto",
+	}
+}
diff --git a/protocol/grpc/protoc-gen-dubbo/examples/helloworld.proto b/protocol/grpc/protoc-gen-dubbo/examples/helloworld.proto
new file mode 100644
index 0000000000000000000000000000000000000000..e73f72b1e06c90bd917e905f992efbddd744b4ad
--- /dev/null
+++ b/protocol/grpc/protoc-gen-dubbo/examples/helloworld.proto
@@ -0,0 +1,40 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+syntax = "proto3";
+
+option java_multiple_files = true;
+option java_package = "io.grpc.examples.helloworld";
+option java_outer_classname = "HelloWorldProto";
+option objc_class_prefix = "HLW";
+
+package main;
+
+// The greeting service definition.
+service Greeter {
+  // Sends a greeting
+  rpc SayHello (HelloRequest) returns (HelloReply) {}
+}
+
+// The request message containing the user's name.
+message HelloRequest {
+  string name = 1;
+}
+
+// The response message containing the greetings
+message HelloReply {
+  string message = 1;
+}
diff --git a/protocol/grpc/protoc-gen-dubbo/main.go b/protocol/grpc/protoc-gen-dubbo/main.go
new file mode 100644
index 0000000000000000000000000000000000000000..b2f0e82f74a4d3c1a7013714cd18d83562baff71
--- /dev/null
+++ b/protocol/grpc/protoc-gen-dubbo/main.go
@@ -0,0 +1,74 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package main
+
+import (
+	"io/ioutil"
+	"os"
+)
+
+import (
+	"github.com/golang/protobuf/proto"
+	"github.com/golang/protobuf/protoc-gen-go/generator"
+	_ "github.com/golang/protobuf/protoc-gen-go/grpc"
+)
+
+import (
+	_ "github.com/apache/dubbo-go/protocol/grpc/protoc-gen-dubbo/plugin/dubbo"
+)
+
+func main() {
+	// Begin by allocating a generate. The request and response structures are stored there
+	// so we can do error handling easily - the response structure contains the field to
+	// report failure.
+	g := generator.New()
+
+	data, err := ioutil.ReadAll(os.Stdin)
+	if err != nil {
+		g.Error(err, "reading input")
+	}
+
+	if err := proto.Unmarshal(data, g.Request); err != nil {
+		g.Error(err, "parsing input proto")
+	}
+
+	if len(g.Request.FileToGenerate) == 0 {
+		g.Fail("no files to generate")
+	}
+
+	g.CommandLineParameters(g.Request.GetParameter())
+
+	// Create a wrapped version of the Descriptors and EnumDescriptors that
+	// point to the file that defines them.
+	g.WrapTypes()
+
+	g.SetPackageNames()
+	g.BuildTypeNameMap()
+
+	g.GenerateAllFiles()
+
+	// Send back the results.
+	data, err = proto.Marshal(g.Response)
+	if err != nil {
+		g.Error(err, "failed to marshal output proto")
+	}
+	_, err = os.Stdout.Write(data)
+	if err != nil {
+		g.Error(err, "failed to write output proto")
+	}
+}
diff --git a/protocol/grpc/protoc-gen-dubbo/plugin/dubbo/doc.go b/protocol/grpc/protoc-gen-dubbo/plugin/dubbo/doc.go
new file mode 100644
index 0000000000000000000000000000000000000000..064c738a53d2200223b0ca81aca77358afad032b
--- /dev/null
+++ b/protocol/grpc/protoc-gen-dubbo/plugin/dubbo/doc.go
@@ -0,0 +1,19 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Package dubbo plugin for protobuf.
+package dubbo
diff --git a/protocol/grpc/protoc-gen-dubbo/plugin/dubbo/dubbo.go b/protocol/grpc/protoc-gen-dubbo/plugin/dubbo/dubbo.go
new file mode 100644
index 0000000000000000000000000000000000000000..e84a7d0cc96887cf728f499c28c26f061ed1ccdf
--- /dev/null
+++ b/protocol/grpc/protoc-gen-dubbo/plugin/dubbo/dubbo.go
@@ -0,0 +1,346 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package dubbo
+
+import (
+	"fmt"
+	"strconv"
+	"strings"
+)
+
+import (
+	pb "github.com/golang/protobuf/protoc-gen-go/descriptor"
+	"github.com/golang/protobuf/protoc-gen-go/generator"
+)
+
+// generatedCodeVersion indicates a version of the generated code.
+// It is incremented whenever an incompatibility between the generated code and
+// the grpc package is introduced; the generated code references
+// a constant, grpc.SupportPackageIsVersionN (where N is generatedCodeVersion).
+const generatedCodeVersion = 4
+
+// Paths for packages used by code generated in this file,
+// relative to the import_prefix of the generator.Generator.
+const (
+	contextPkgPath = "context"
+	grpcPkgPath    = "google.golang.org/grpc"
+	codePkgPath    = "google.golang.org/grpc/codes"
+	statusPkgPath  = "google.golang.org/grpc/status"
+)
+
+func init() {
+	generator.RegisterPlugin(new(dubboGrpc))
+}
+
+// grpc is an implementation of the Go protocol buffer compiler's
+// plugin architecture.  It generates bindings for gRPC-dubbo support.
+type dubboGrpc struct {
+	gen *generator.Generator
+}
+
+// Name returns the name of this plugin, "grpc".
+func (g *dubboGrpc) Name() string {
+	return "dubbo"
+}
+
+// The names for packages imported in the generated code.
+// They may vary from the final path component of the import path
+// if the name is used by other packages.
+var (
+	contextPkg string
+	grpcPkg    string
+)
+
+// Init initializes the plugin.
+func (g *dubboGrpc) Init(gen *generator.Generator) {
+	g.gen = gen
+}
+
+// Given a type name defined in a .proto, return its object.
+// Also record that we're using it, to guarantee the associated import.
+func (g *dubboGrpc) objectNamed(name string) generator.Object {
+	g.gen.RecordTypeUse(name)
+	return g.gen.ObjectNamed(name)
+}
+
+// Given a type name defined in a .proto, return its name as we will print it.
+func (g *dubboGrpc) typeName(str string) string {
+	return g.gen.TypeName(g.objectNamed(str))
+}
+
+// P forwards to g.gen.P.
+func (g *dubboGrpc) P(args ...interface{}) { g.gen.P(args...) }
+
+// Generate generates code for the services in the given file.
+// be consistent with grpc plugin
+func (g *dubboGrpc) Generate(file *generator.FileDescriptor) {
+	if len(file.FileDescriptorProto.Service) == 0 {
+		return
+	}
+
+	contextPkg = string(g.gen.AddImport(contextPkgPath))
+	grpcPkg = string(g.gen.AddImport(grpcPkgPath))
+
+	for i, service := range file.FileDescriptorProto.Service {
+		g.generateService(file, service, i)
+	}
+}
+
+// GenerateImports generates the import declaration for this file.
+func (g *dubboGrpc) GenerateImports(file *generator.FileDescriptor) {
+	g.P("import (")
+	g.P(`dgrpc "github.com/apache/dubbo-go/protocol/grpc"`)
+	g.P(`"github.com/apache/dubbo-go/protocol/invocation"`)
+	g.P(`"github.com/apache/dubbo-go/protocol"`)
+	g.P(`"github.com/apache/dubbo-go/config"`)
+	g.P(` ) `)
+}
+
+func unexport(s string) string { return strings.ToLower(s[:1]) + s[1:] }
+
+// deprecationComment is the standard comment added to deprecated
+// messages, fields, enums, and enum values.
+var deprecationComment = "// Deprecated: Do not use."
+
+// generateService generates all the code for the named service.
+func (g *dubboGrpc) generateService(file *generator.FileDescriptor, service *pb.ServiceDescriptorProto, index int) {
+	path := fmt.Sprintf("6,%d", index) // 6 means service.
+
+	origServName := service.GetName()
+	fullServName := origServName
+	if pkg := file.GetPackage(); pkg != "" {
+		fullServName = pkg + "." + fullServName
+	}
+	servName := generator.CamelCase(origServName)
+	deprecated := service.GetOptions().GetDeprecated()
+
+	g.P()
+	g.P(fmt.Sprintf(`// %sClientImpl is the client API for %s service.
+//
+// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.`, servName, servName))
+
+	// Client interface.
+	if deprecated {
+		g.P("//")
+		g.P(deprecationComment)
+	}
+	dubboSrvName := servName + "ClientImpl"
+	g.P("type ", dubboSrvName, " struct {")
+	for i, method := range service.Method {
+		g.gen.PrintComments(fmt.Sprintf("%s,2,%d", path, i)) // 2 means method in a service.
+		if method.GetOptions().GetDeprecated() {
+			g.P("//")
+			g.P(deprecationComment)
+		}
+		g.P(g.generateClientSignature(servName, method))
+	}
+	g.P("}")
+	g.P()
+
+	// NewClient factory.
+	if deprecated {
+		g.P(deprecationComment)
+	}
+
+	// add Reference method
+	//func (u *GrpcGreeterImpl) Reference() string {
+	//	return "GrpcGreeterImpl"
+	//}
+	g.P("func (c *", dubboSrvName, ") ", " Reference() string ", "{")
+	g.P(`return "`, unexport(servName), `Impl"`)
+	g.P("}")
+	g.P()
+
+	// add GetDubboStub method
+	// func (u *GrpcGreeterImpl) GetDubboStub(cc *grpc.ClientConn) GreeterClient {
+	//	return NewGreeterClient(cc)
+	//}
+	g.P("func (c *", dubboSrvName, ") ", " GetDubboStub(cc *grpc.ClientConn) ", servName, "Client {")
+	g.P(`return New`, servName, `Client(cc)`)
+	g.P("}")
+	g.P()
+
+	// Server interface.
+	serverType := servName + "ProviderBase"
+	g.P("type ", serverType, " struct {")
+	g.P("proxyImpl protocol.Invoker")
+	g.P("}")
+	g.P()
+
+	// add set method
+	//func (g *GreeterProviderBase) SetProxyImpl(impl protocol.Invoker) {
+	//	g.proxyImpl = impl
+	//}
+	g.P("func (s *", serverType, ") SetProxyImpl(impl protocol.Invoker) {")
+	g.P(`s.proxyImpl = impl`)
+	g.P("}")
+	g.P()
+
+	// return get method
+	g.P("func (s *", serverType, ") GetProxyImpl() protocol.Invoker {")
+	g.P(`return s.proxyImpl`)
+	g.P("}")
+	g.P()
+
+	// add handler
+	var handlerNames []string
+	for _, method := range service.Method {
+		hname := g.generateServerMethod(servName, fullServName, method)
+		handlerNames = append(handlerNames, hname)
+	}
+
+	grpcserverType := servName + "Server"
+	// return service desc
+	g.P("func (s *", serverType, ") ServiceDesc() *grpc.ServiceDesc {")
+	g.P(`return &grpc.ServiceDesc{`)
+	g.P("ServiceName: ", strconv.Quote(fullServName), ",")
+	g.P("HandlerType: (*", grpcserverType, ")(nil),")
+	g.P("Methods: []", grpcPkg, ".MethodDesc{")
+	for i, method := range service.Method {
+		if method.GetServerStreaming() || method.GetClientStreaming() {
+			continue
+		}
+		g.P("{")
+		g.P("MethodName: ", strconv.Quote(method.GetName()), ",")
+		g.P("Handler: ", handlerNames[i], ",")
+		g.P("},")
+	}
+	g.P("},")
+	g.P("Streams: []", grpcPkg, ".StreamDesc{},")
+	g.P("Metadata: \"", file.GetName(), "\",")
+	g.P("}")
+	g.P("}")
+	g.P()
+}
+
+// generateClientSignature returns the client-side signature for a method.
+func (g *dubboGrpc) generateClientSignature(servName string, method *pb.MethodDescriptorProto) string {
+	origMethName := method.GetName()
+	methName := generator.CamelCase(origMethName)
+	//if reservedClientName[methName] {
+	//  methName += "_"
+	//}
+	reqArg := ", in *" + g.typeName(method.GetInputType())
+	if method.GetClientStreaming() {
+		reqArg = ""
+	}
+	respName := "out *" + g.typeName(method.GetOutputType())
+	if method.GetServerStreaming() || method.GetClientStreaming() {
+		respName = servName + "_" + generator.CamelCase(origMethName) + "Client"
+	}
+	return fmt.Sprintf("%s func(ctx %s.Context%s, %s) error", methName, contextPkg, reqArg, respName)
+}
+
+func (g *dubboGrpc) generateClientMethod(servName, fullServName, serviceDescVar string, method *pb.MethodDescriptorProto, descExpr string) {
+}
+
+func (g *dubboGrpc) generateServerMethod(servName, fullServName string, method *pb.MethodDescriptorProto) string {
+	methName := generator.CamelCase(method.GetName())
+	hname := fmt.Sprintf("_DUBBO_%s_%s_Handler", servName, methName)
+	inType := g.typeName(method.GetInputType())
+	outType := g.typeName(method.GetOutputType())
+
+	if !method.GetServerStreaming() && !method.GetClientStreaming() {
+		g.P("func ", hname, "(srv interface{}, ctx ", contextPkg, ".Context, dec func(interface{}) error, interceptor ", grpcPkg, ".UnaryServerInterceptor) (interface{}, error) {")
+		g.P("in := new(", inType, ")")
+		g.P("if err := dec(in); err != nil { return nil, err }")
+
+		g.P("base := srv.(dgrpc.DubboGrpcService)")
+		g.P("args := []interface{}{}")
+		g.P("args = append(args, in)")
+		g.P(`invo := invocation.NewRPCInvocation("`, methName, `", args, nil)`)
+
+		g.P("if interceptor == nil {")
+		g.P("result := base.GetProxyImpl().Invoke(invo)")
+		g.P("return result.Result(), result.Error()")
+		g.P("}")
+
+		g.P("info := &", grpcPkg, ".UnaryServerInfo{")
+		g.P("Server: srv,")
+		g.P("FullMethod: ", strconv.Quote(fmt.Sprintf("/%s/%s", fullServName, methName)), ",")
+		g.P("}")
+
+		g.P("handler := func(ctx ", contextPkg, ".Context, req interface{}) (interface{}, error) {")
+		g.P("result := base.GetProxyImpl().Invoke(invo)")
+		g.P("return result.Result(), result.Error()")
+		g.P("}")
+
+		g.P("return interceptor(ctx, in, info, handler)")
+		g.P("}")
+		g.P()
+		return hname
+	}
+	streamType := unexport(servName) + methName + "Server"
+	g.P("func ", hname, "(srv interface{}, stream ", grpcPkg, ".ServerStream) error {")
+	if !method.GetClientStreaming() {
+		g.P("m := new(", inType, ")")
+		g.P("if err := stream.RecvMsg(m); err != nil { return err }")
+		g.P("return srv.(", servName, "Server).", methName, "(m, &", streamType, "{stream})")
+	} else {
+		g.P("return srv.(", servName, "Server).", methName, "(&", streamType, "{stream})")
+	}
+	g.P("}")
+	g.P()
+
+	genSend := method.GetServerStreaming()
+	genSendAndClose := !method.GetServerStreaming()
+	genRecv := method.GetClientStreaming()
+
+	// Stream auxiliary types and methods.
+	g.P("type ", servName, "_", methName, "Server interface {")
+	if genSend {
+		g.P("Send(*", outType, ") error")
+	}
+	if genSendAndClose {
+		g.P("SendAndClose(*", outType, ") error")
+	}
+	if genRecv {
+		g.P("Recv() (*", inType, ", error)")
+	}
+	g.P(grpcPkg, ".ServerStream")
+	g.P("}")
+	g.P()
+
+	g.P("type ", streamType, " struct {")
+	g.P(grpcPkg, ".ServerStream")
+	g.P("}")
+	g.P()
+
+	if genSend {
+		g.P("func (x *", streamType, ") Send(m *", outType, ") error {")
+		g.P("return x.ServerStream.SendMsg(m)")
+		g.P("}")
+		g.P()
+	}
+	if genSendAndClose {
+		g.P("func (x *", streamType, ") SendAndClose(m *", outType, ") error {")
+		g.P("return x.ServerStream.SendMsg(m)")
+		g.P("}")
+		g.P()
+	}
+	if genRecv {
+		g.P("func (x *", streamType, ") Recv() (*", inType, ", error) {")
+		g.P("m := new(", inType, ")")
+		g.P("if err := x.ServerStream.RecvMsg(m); err != nil { return nil, err }")
+		g.P("return m, nil")
+		g.P("}")
+		g.P()
+	}
+
+	return hname
+}
diff --git a/protocol/grpc/server.go b/protocol/grpc/server.go
new file mode 100644
index 0000000000000000000000000000000000000000..19b9db4ac743ceefcf035d399c0bbcdd99f1fa80
--- /dev/null
+++ b/protocol/grpc/server.go
@@ -0,0 +1,106 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package grpc
+
+import (
+	"fmt"
+	"net"
+	"reflect"
+)
+
+import (
+	"google.golang.org/grpc"
+)
+
+import (
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/common/constant"
+	"github.com/apache/dubbo-go/common/logger"
+	"github.com/apache/dubbo-go/config"
+	"github.com/apache/dubbo-go/protocol"
+)
+
+// Server ...
+type Server struct {
+	grpcServer *grpc.Server
+}
+
+// NewServer ...
+func NewServer() *Server {
+	return &Server{}
+}
+
+// DubboGrpcService ...
+type DubboGrpcService interface {
+	SetProxyImpl(impl protocol.Invoker)
+	GetProxyImpl() protocol.Invoker
+	ServiceDesc() *grpc.ServiceDesc
+}
+
+// Start ...
+func (s *Server) Start(url common.URL) {
+	var (
+		addr string
+		err  error
+	)
+	addr = url.Location
+	lis, err := net.Listen("tcp", addr)
+	if err != nil {
+		panic(err)
+	}
+	server := grpc.NewServer()
+
+	key := url.GetParam(constant.BEAN_NAME_KEY, "")
+	service := config.GetProviderService(key)
+
+	ds, ok := service.(DubboGrpcService)
+	if !ok {
+		panic("illegal service type registered")
+	}
+
+	m, ok := reflect.TypeOf(service).MethodByName("SetProxyImpl")
+	if !ok {
+		panic("method SetProxyImpl is necessary for grpc service")
+	}
+
+	exporter, _ := grpcProtocol.ExporterMap().Load(url.ServiceKey())
+	if exporter == nil {
+		panic(fmt.Sprintf("no exporter found for servicekey: %v", url.ServiceKey()))
+	}
+	invoker := exporter.(protocol.Exporter).GetInvoker()
+	if invoker == nil {
+		panic(fmt.Sprintf("no invoker found for servicekey: %v", url.ServiceKey()))
+	}
+	in := []reflect.Value{reflect.ValueOf(service)}
+	in = append(in, reflect.ValueOf(invoker))
+	m.Func.Call(in)
+
+	server.RegisterService(ds.ServiceDesc(), service)
+
+	s.grpcServer = server
+	go func() {
+		if err = server.Serve(lis); err != nil {
+			logger.Errorf("server serve failed with err: %v", err)
+		}
+	}()
+}
+
+// Stop ...
+func (s *Server) Stop() {
+	s.grpcServer.Stop()
+}
diff --git a/protocol/invocation.go b/protocol/invocation.go
index 055e7a4cd18707772d6ba75303053f15dc55dbe3..f32f2c3449ac063ecb89952bd4653312a07a3df4 100644
--- a/protocol/invocation.go
+++ b/protocol/invocation.go
@@ -21,9 +21,11 @@ import (
 	"reflect"
 )
 
+// Invocation ...
 type Invocation interface {
 	MethodName() string
 	ParameterTypes() []reflect.Type
+	ParameterValues() []reflect.Value
 	Arguments() []interface{}
 	Reply() interface{}
 	Attachments() map[string]string
diff --git a/protocol/invocation/rpcinvocation.go b/protocol/invocation/rpcinvocation.go
index 2124a22f1611b24d7f4370de64b117c58c4f7e7b..b207fd0b0cc4eb7de8409a8c46c6fc9ef0baa5c7 100644
--- a/protocol/invocation/rpcinvocation.go
+++ b/protocol/invocation/rpcinvocation.go
@@ -19,26 +19,32 @@ package invocation
 
 import (
 	"reflect"
+	"sync"
 )
 
 import (
 	"github.com/apache/dubbo-go/protocol"
 )
 
-/////////////////////////////
+// ///////////////////////////
 // Invocation Impletment of RPC
-/////////////////////////////
+// ///////////////////////////
+
 // todo: is it necessary to separate fields of consumer(provider) from RPCInvocation
+// RPCInvocation ...
 type RPCInvocation struct {
-	methodName     string
-	parameterTypes []reflect.Type
-	arguments      []interface{}
-	reply          interface{}
-	callBack       interface{}
-	attachments    map[string]string
-	invoker        protocol.Invoker
-}
-
+	methodName      string
+	parameterTypes  []reflect.Type
+	parameterValues []reflect.Value
+	arguments       []interface{}
+	reply           interface{}
+	callBack        interface{}
+	attachments     map[string]string
+	invoker         protocol.Invoker
+	lock            sync.RWMutex
+}
+
+// NewRPCInvocation ...
 func NewRPCInvocation(methodName string, arguments []interface{}, attachments map[string]string) *RPCInvocation {
 	return &RPCInvocation{
 		methodName:  methodName,
@@ -47,6 +53,7 @@ func NewRPCInvocation(methodName string, arguments []interface{}, attachments ma
 	}
 }
 
+// NewRPCInvocationWithOptions ...
 func NewRPCInvocationWithOptions(opts ...option) *RPCInvocation {
 	invo := &RPCInvocation{}
 	for _, opt := range opts {
@@ -55,31 +62,45 @@ func NewRPCInvocationWithOptions(opts ...option) *RPCInvocation {
 	return invo
 }
 
+// MethodName ...
 func (r *RPCInvocation) MethodName() string {
 	return r.methodName
 }
 
+// ParameterTypes ...
 func (r *RPCInvocation) ParameterTypes() []reflect.Type {
 	return r.parameterTypes
 }
 
+// ParameterValues ...
+func (r *RPCInvocation) ParameterValues() []reflect.Value {
+	return r.parameterValues
+}
+
+// Arguments ...
 func (r *RPCInvocation) Arguments() []interface{} {
 	return r.arguments
 }
 
+// Reply ...
 func (r *RPCInvocation) Reply() interface{} {
 	return r.reply
 }
 
+// SetReply ...
 func (r *RPCInvocation) SetReply(reply interface{}) {
 	r.reply = reply
 }
 
+// Attachments ...
 func (r *RPCInvocation) Attachments() map[string]string {
 	return r.attachments
 }
 
+// AttachmentsByKey ...
 func (r *RPCInvocation) AttachmentsByKey(key string, defaultValue string) string {
+	r.lock.RLock()
+	defer r.lock.RUnlock()
 	if r.attachments == nil {
 		return defaultValue
 	}
@@ -90,71 +111,92 @@ func (r *RPCInvocation) AttachmentsByKey(key string, defaultValue string) string
 	return defaultValue
 }
 
+// SetAttachments ...
 func (r *RPCInvocation) SetAttachments(key string, value string) {
+	r.lock.Lock()
+	defer r.lock.Unlock()
 	if r.attachments == nil {
 		r.attachments = make(map[string]string)
 	}
 	r.attachments[key] = value
 }
 
+// Invoker ...
 func (r *RPCInvocation) Invoker() protocol.Invoker {
 	return r.invoker
 }
 
+// SetInvoker ...
 func (r *RPCInvocation) SetInvoker() protocol.Invoker {
 	return r.invoker
 }
 
+// CallBack ...
 func (r *RPCInvocation) CallBack() interface{} {
 	return r.callBack
 }
 
+// SetCallBack ...
 func (r *RPCInvocation) SetCallBack(c interface{}) {
 	r.callBack = c
 }
 
-///////////////////////////
+// /////////////////////////
 // option
-///////////////////////////
+// /////////////////////////
 
 type option func(invo *RPCInvocation)
 
+// WithMethodName ...
 func WithMethodName(methodName string) option {
 	return func(invo *RPCInvocation) {
 		invo.methodName = methodName
 	}
 }
 
+// WithParameterTypes ...
 func WithParameterTypes(parameterTypes []reflect.Type) option {
 	return func(invo *RPCInvocation) {
 		invo.parameterTypes = parameterTypes
 	}
 }
 
+// WithParameterValues ...
+func WithParameterValues(parameterValues []reflect.Value) option {
+	return func(invo *RPCInvocation) {
+		invo.parameterValues = parameterValues
+	}
+}
+
+// WithArguments ...
 func WithArguments(arguments []interface{}) option {
 	return func(invo *RPCInvocation) {
 		invo.arguments = arguments
 	}
 }
 
+// WithReply ...
 func WithReply(reply interface{}) option {
 	return func(invo *RPCInvocation) {
 		invo.reply = reply
 	}
 }
 
+// WithCallBack ...
 func WithCallBack(callBack interface{}) option {
 	return func(invo *RPCInvocation) {
 		invo.callBack = callBack
 	}
 }
 
+// WithAttachments ...
 func WithAttachments(attachments map[string]string) option {
 	return func(invo *RPCInvocation) {
 		invo.attachments = attachments
 	}
 }
 
+// WithInvoker ...
 func WithInvoker(invoker protocol.Invoker) option {
 	return func(invo *RPCInvocation) {
 		invo.invoker = invoker
diff --git a/protocol/invoker.go b/protocol/invoker.go
index f5d41a09ad2778c12c7e5e68167a4d0acc9e3f4c..bb71bab1cfa2ede7fb035912ae996f9adb7411e0 100644
--- a/protocol/invoker.go
+++ b/protocol/invoker.go
@@ -17,28 +17,35 @@
 
 package protocol
 
+import (
+	"context"
+)
+
 import (
 	"github.com/apache/dubbo-go/common"
 	"github.com/apache/dubbo-go/common/logger"
 )
 
+// Invoker ...
 //go:generate mockgen -source invoker.go -destination mock/mock_invoker.go  -self_package github.com/apache/dubbo-go/protocol/mock --package mock  Invoker
 // Extension - Invoker
 type Invoker interface {
 	common.Node
-	Invoke(Invocation) Result
+	Invoke(context.Context, Invocation) Result
 }
 
 /////////////////////////////
 // base invoker
 /////////////////////////////
 
+// BaseInvoker ...
 type BaseInvoker struct {
 	url       common.URL
 	available bool
 	destroyed bool
 }
 
+// NewBaseInvoker ...
 func NewBaseInvoker(url common.URL) *BaseInvoker {
 	return &BaseInvoker{
 		url:       url,
@@ -47,22 +54,27 @@ func NewBaseInvoker(url common.URL) *BaseInvoker {
 	}
 }
 
+// GetUrl ...
 func (bi *BaseInvoker) GetUrl() common.URL {
 	return bi.url
 }
 
+// IsAvailable ...
 func (bi *BaseInvoker) IsAvailable() bool {
 	return bi.available
 }
 
+// IsDestroyed ...
 func (bi *BaseInvoker) IsDestroyed() bool {
 	return bi.destroyed
 }
 
-func (bi *BaseInvoker) Invoke(invocation Invocation) Result {
+// Invoke ...
+func (bi *BaseInvoker) Invoke(context context.Context, invocation Invocation) Result {
 	return &RPCResult{}
 }
 
+// Destroy ...
 func (bi *BaseInvoker) Destroy() {
 	logger.Infof("Destroy invoker: %s", bi.GetUrl().String())
 	bi.destroyed = true
diff --git a/protocol/jsonrpc/http.go b/protocol/jsonrpc/http.go
index b64a3a344e95164b8f49556cdc155bf34642a83b..ba7197dbc857c2ed7acda1a9f246a5b826e86915 100644
--- a/protocol/jsonrpc/http.go
+++ b/protocol/jsonrpc/http.go
@@ -33,18 +33,21 @@ import (
 )
 
 import (
+	"github.com/opentracing/opentracing-go"
 	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/logger"
 )
 
-//////////////////////////////////////////////
+// ////////////////////////////////////////////
 // Request
-//////////////////////////////////////////////
+// ////////////////////////////////////////////
 
+// Request ...
 type Request struct {
 	ID          int64
 	group       string
@@ -56,25 +59,28 @@ type Request struct {
 	contentType string
 }
 
-//////////////////////////////////////////////
+// ////////////////////////////////////////////
 // HTTP Client
-//////////////////////////////////////////////
+// ////////////////////////////////////////////
 
+// HTTPOptions ...
 type HTTPOptions struct {
 	HandshakeTimeout time.Duration
 	HTTPTimeout      time.Duration
 }
 
 var defaultHTTPOptions = HTTPOptions{
-	HandshakeTimeout: 3e9,
-	HTTPTimeout:      3e9,
+	HandshakeTimeout: 3 * time.Second,
+	HTTPTimeout:      3 * time.Second,
 }
 
+// HTTPClient ...
 type HTTPClient struct {
 	ID      int64
 	options HTTPOptions
 }
 
+// NewHTTPClient ...
 func NewHTTPClient(opt *HTTPOptions) *HTTPClient {
 	if opt == nil {
 		opt = &defaultHTTPOptions
@@ -94,6 +100,7 @@ func NewHTTPClient(opt *HTTPOptions) *HTTPClient {
 	}
 }
 
+// NewRequest ...
 func (c *HTTPClient) NewRequest(service common.URL, method string, args interface{}) *Request {
 
 	return &Request{
@@ -107,6 +114,7 @@ func (c *HTTPClient) NewRequest(service common.URL, method string, args interfac
 	}
 }
 
+// Call ...
 func (c *HTTPClient) Call(ctx context.Context, service common.URL, req *Request, rsp interface{}) error {
 	// header
 	httpHeader := http.Header{}
@@ -115,7 +123,7 @@ func (c *HTTPClient) Call(ctx context.Context, service common.URL, req *Request,
 
 	reqTimeout := c.options.HTTPTimeout
 	if reqTimeout <= 0 {
-		reqTimeout = 1e8
+		reqTimeout = 100 * time.Millisecond
 	}
 	httpHeader.Set("Timeout", reqTimeout.String())
 	if md, ok := ctx.Value(constant.DUBBOGO_CTX_KEY).(map[string]string); ok {
@@ -124,6 +132,13 @@ func (c *HTTPClient) Call(ctx context.Context, service common.URL, req *Request,
 		}
 	}
 
+	if span := opentracing.SpanFromContext(ctx); span != nil {
+		err := opentracing.GlobalTracer().Inject(span.Context(), opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(httpHeader))
+		if err != nil {
+			logger.Error("Could not inject the Context into http header.")
+		}
+	}
+
 	// body
 	codec := newJsonClientCodec()
 	codecData := CodecData{
@@ -144,6 +159,7 @@ func (c *HTTPClient) Call(ctx context.Context, service common.URL, req *Request,
 	return perrors.WithStack(codec.Read(rspBody, rsp))
 }
 
+// Do
 // !!The high level of complexity and the likelihood that the fasthttp client has not been extensively used
 // in production means that you would need to expect a very large benefit to justify the adoption of fasthttp today.
 func (c *HTTPClient) Do(addr, path string, httpHeader http.Header, body []byte) ([]byte, error) {
diff --git a/protocol/jsonrpc/http_test.go b/protocol/jsonrpc/http_test.go
index 9be55e247a730460a3adee5622fa978ef2defbfb..0cb88b36a8f330059906eb70417b6d4841020c38 100644
--- a/protocol/jsonrpc/http_test.go
+++ b/protocol/jsonrpc/http_test.go
@@ -25,6 +25,7 @@ import (
 )
 
 import (
+	"github.com/opentracing/opentracing-go"
 	perrors "github.com/pkg/errors"
 	"github.com/stretchr/testify/assert"
 )
@@ -55,10 +56,10 @@ func TestHTTPClient_Call(t *testing.T) {
 
 	// Export
 	proto := GetProtocol()
-	url, err := common.NewURL(context.Background(), "jsonrpc://127.0.0.1:20001/UserProvider?anyhost=true&"+
-		"application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&"+
-		"environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&"+
-		"module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&"+
+	url, err := common.NewURL("jsonrpc://127.0.0.1:20001/UserProvider?anyhost=true&" +
+		"application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&" +
+		"environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&" +
+		"module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&" +
 		"side=provider&timeout=3000&timestamp=1556509797245&bean.name=UserProvider")
 	assert.NoError(t, err)
 	proto.Export(&proxy_factory.ProxyInvoker{
@@ -74,6 +75,7 @@ func TestHTTPClient_Call(t *testing.T) {
 		"X-Services": url.Path,
 		"X-Method":   "GetUser",
 	})
+
 	req := client.NewRequest(url, "GetUser", []interface{}{"1", "username"})
 	reply := &User{}
 	err = client.Call(ctx, url, req, reply)
@@ -147,6 +149,10 @@ func TestHTTPClient_Call(t *testing.T) {
 		"X-Services": url.Path,
 		"X-Method":   "GetUser4",
 	})
+
+	span := opentracing.StartSpan("Test-Inject-Tracing-ID")
+	ctx = opentracing.ContextWithSpan(ctx, span)
+
 	req = client.NewRequest(url, "GetUser4", []interface{}{1})
 	reply = &User{}
 	err = client.Call(ctx, url, req, reply)
diff --git a/protocol/jsonrpc/json.go b/protocol/jsonrpc/json.go
index 7ee454e8ad16d2ee96ed08e7e5f55b2209a81054..d1c2a858b4e4223ac32fc1160b56f6ee1862c8ce 100644
--- a/protocol/jsonrpc/json.go
+++ b/protocol/jsonrpc/json.go
@@ -31,10 +31,13 @@ import (
 )
 
 const (
+	// MAX_JSONRPC_ID max jsonrpc request/response id
 	MAX_JSONRPC_ID = 0x7FFFFFFF
-	VERSION        = "2.0"
+	// VERSION jsonrpc version
+	VERSION = "2.0"
 )
 
+// CodecData ...
 type CodecData struct {
 	ID     int64
 	Method string
@@ -54,7 +57,7 @@ const (
 	codeServerErrorEnd   = -32000
 )
 
-// rsponse Error
+// Error response Error
 type Error struct {
 	Code    int         `json:"code"`
 	Message string      `json:"message"`
@@ -278,12 +281,14 @@ type serverResponse struct {
 	Error   interface{}      `json:"error,omitempty"`
 }
 
+// ServerCodec ...
 type ServerCodec struct {
 	req serverRequest
 }
 
 var (
-	null    = json.RawMessage([]byte("null"))
+	null = json.RawMessage([]byte("null"))
+	// Version ...
 	Version = "2.0"
 )
 
@@ -291,6 +296,7 @@ func newServerCodec() *ServerCodec {
 	return &ServerCodec{}
 }
 
+// ReadHeader ...
 func (c *ServerCodec) ReadHeader(header map[string]string, body []byte) error {
 	if header["HttpMethod"] != "POST" {
 		return &Error{Code: -32601, Message: "Method not found"}
@@ -322,6 +328,7 @@ func (c *ServerCodec) ReadHeader(header map[string]string, body []byte) error {
 	return nil
 }
 
+// ReadBody ...
 func (c *ServerCodec) ReadBody(x interface{}) error {
 	// If x!=nil and return error e:
 	// - Write() will be called with e.Error() in r.Error
@@ -355,6 +362,7 @@ func (c *ServerCodec) ReadBody(x interface{}) error {
 	return nil
 }
 
+// NewError ...
 func NewError(code int, message string) *Error {
 	return &Error{Code: code, Message: message}
 }
diff --git a/protocol/jsonrpc/jsonrpc_exporter.go b/protocol/jsonrpc/jsonrpc_exporter.go
index 6720330494a3b833d4a67d8b2408377ce62b1ddf..7f8fd491854f1ab25e63410a22ef5664db92f614 100644
--- a/protocol/jsonrpc/jsonrpc_exporter.go
+++ b/protocol/jsonrpc/jsonrpc_exporter.go
@@ -28,16 +28,19 @@ import (
 	"github.com/apache/dubbo-go/protocol"
 )
 
+// JsonrpcExporter ...
 type JsonrpcExporter struct {
 	protocol.BaseExporter
 }
 
+// NewJsonrpcExporter ...
 func NewJsonrpcExporter(key string, invoker protocol.Invoker, exporterMap *sync.Map) *JsonrpcExporter {
 	return &JsonrpcExporter{
 		BaseExporter: *protocol.NewBaseExporter(key, invoker, exporterMap),
 	}
 }
 
+// Unexport ...
 func (je *JsonrpcExporter) Unexport() {
 	serviceId := je.GetInvoker().GetUrl().GetParam(constant.BEAN_NAME_KEY, "")
 	je.BaseExporter.Unexport()
diff --git a/protocol/jsonrpc/jsonrpc_invoker.go b/protocol/jsonrpc/jsonrpc_invoker.go
index 2c130e0d7617e96a1724edc5b63f8e66f251446e..b6e194ce0e93e84c164eccf8574e5eb20430f6e8 100644
--- a/protocol/jsonrpc/jsonrpc_invoker.go
+++ b/protocol/jsonrpc/jsonrpc_invoker.go
@@ -29,11 +29,13 @@ import (
 	invocation_impl "github.com/apache/dubbo-go/protocol/invocation"
 )
 
+// JsonrpcInvoker ...
 type JsonrpcInvoker struct {
 	protocol.BaseInvoker
 	client *HTTPClient
 }
 
+// NewJsonrpcInvoker ...
 func NewJsonrpcInvoker(url common.URL, client *HTTPClient) *JsonrpcInvoker {
 	return &JsonrpcInvoker{
 		BaseInvoker: *protocol.NewBaseInvoker(url),
@@ -41,7 +43,8 @@ func NewJsonrpcInvoker(url common.URL, client *HTTPClient) *JsonrpcInvoker {
 	}
 }
 
-func (ji *JsonrpcInvoker) Invoke(invocation protocol.Invocation) protocol.Result {
+// Invoke ...
+func (ji *JsonrpcInvoker) Invoke(ctx context.Context, invocation protocol.Invocation) protocol.Result {
 
 	var (
 		result protocol.RPCResult
@@ -50,12 +53,12 @@ func (ji *JsonrpcInvoker) Invoke(invocation protocol.Invocation) protocol.Result
 	inv := invocation.(*invocation_impl.RPCInvocation)
 	url := ji.GetUrl()
 	req := ji.client.NewRequest(url, inv.MethodName(), inv.Arguments())
-	ctx := context.WithValue(context.Background(), constant.DUBBOGO_CTX_KEY, map[string]string{
+	ctxNew := context.WithValue(ctx, constant.DUBBOGO_CTX_KEY, map[string]string{
 		"X-Proxy-Id": "dubbogo",
 		"X-Services": url.Path,
 		"X-Method":   inv.MethodName(),
 	})
-	result.Err = ji.client.Call(ctx, url, req, inv.Reply())
+	result.Err = ji.client.Call(ctxNew, url, req, inv.Reply())
 	if result.Err == nil {
 		result.Rest = inv.Reply()
 	}
diff --git a/protocol/jsonrpc/jsonrpc_invoker_test.go b/protocol/jsonrpc/jsonrpc_invoker_test.go
index 8c910339858f4960ad0e394ae6271863d7654adc..9e08eed2b4c61e686073a9039a605c4f73aa08c5 100644
--- a/protocol/jsonrpc/jsonrpc_invoker_test.go
+++ b/protocol/jsonrpc/jsonrpc_invoker_test.go
@@ -42,10 +42,10 @@ func TestJsonrpcInvoker_Invoke(t *testing.T) {
 
 	// Export
 	proto := GetProtocol()
-	url, err := common.NewURL(context.Background(), "jsonrpc://127.0.0.1:20001/UserProvider?anyhost=true&"+
-		"application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&"+
-		"environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&"+
-		"module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&"+
+	url, err := common.NewURL("jsonrpc://127.0.0.1:20001/UserProvider?anyhost=true&" +
+		"application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&" +
+		"environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&" +
+		"module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&" +
 		"side=provider&timeout=3000&timestamp=1556509797245&bean.name=UserProvider")
 	assert.NoError(t, err)
 	proto.Export(&proxy_factory.ProxyInvoker{
@@ -60,7 +60,7 @@ func TestJsonrpcInvoker_Invoke(t *testing.T) {
 
 	jsonInvoker := NewJsonrpcInvoker(url, client)
 	user := &User{}
-	res := jsonInvoker.Invoke(invocation.NewRPCInvocationWithOptions(invocation.WithMethodName("GetUser"), invocation.WithArguments([]interface{}{"1", "username"}),
+	res := jsonInvoker.Invoke(context.Background(), invocation.NewRPCInvocationWithOptions(invocation.WithMethodName("GetUser"), invocation.WithArguments([]interface{}{"1", "username"}),
 		invocation.WithReply(user)))
 
 	assert.NoError(t, res.Error())
diff --git a/protocol/jsonrpc/jsonrpc_protocol.go b/protocol/jsonrpc/jsonrpc_protocol.go
index c18345d413edb2d263f1acaef1741514b665f042..bed7099ab60a6c05c3799f993c0bb348a4b00f02 100644
--- a/protocol/jsonrpc/jsonrpc_protocol.go
+++ b/protocol/jsonrpc/jsonrpc_protocol.go
@@ -20,17 +20,23 @@ package jsonrpc
 import (
 	"strings"
 	"sync"
+	"time"
 )
 
 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/config"
 	"github.com/apache/dubbo-go/protocol"
 )
 
-const JSONRPC = "jsonrpc"
+const (
+	// JSONRPC
+	//module name
+	JSONRPC = "jsonrpc"
+)
 
 func init() {
 	extension.SetProtocol(JSONRPC, GetProtocol)
@@ -38,12 +44,14 @@ func init() {
 
 var jsonrpcProtocol *JsonrpcProtocol
 
+// JsonrpcProtocol ...
 type JsonrpcProtocol struct {
 	protocol.BaseProtocol
 	serverMap  map[string]*Server
 	serverLock sync.Mutex
 }
 
+// NewJsonrpcProtocol ...
 func NewJsonrpcProtocol() *JsonrpcProtocol {
 	return &JsonrpcProtocol{
 		BaseProtocol: protocol.NewBaseProtocol(),
@@ -51,6 +59,7 @@ func NewJsonrpcProtocol() *JsonrpcProtocol {
 	}
 }
 
+// Export ...
 func (jp *JsonrpcProtocol) Export(invoker protocol.Invoker) protocol.Exporter {
 	url := invoker.GetUrl()
 	serviceKey := strings.TrimPrefix(url.Path, "/")
@@ -65,16 +74,26 @@ func (jp *JsonrpcProtocol) Export(invoker protocol.Invoker) protocol.Exporter {
 	return exporter
 }
 
+// Refer ...
 func (jp *JsonrpcProtocol) Refer(url common.URL) protocol.Invoker {
+	//default requestTimeout
+	var requestTimeout = config.GetConsumerConfig().RequestTimeout
+
+	requestTimeoutStr := url.GetParam(constant.TIMEOUT_KEY, config.GetConsumerConfig().Request_Timeout)
+	if t, err := time.ParseDuration(requestTimeoutStr); err == nil {
+		requestTimeout = t
+	}
+
 	invoker := NewJsonrpcInvoker(url, NewHTTPClient(&HTTPOptions{
 		HandshakeTimeout: config.GetConsumerConfig().ConnectTimeout,
-		HTTPTimeout:      config.GetConsumerConfig().RequestTimeout,
+		HTTPTimeout:      requestTimeout,
 	}))
 	jp.SetInvokers(invoker)
 	logger.Infof("Refer service: %s", url.String())
 	return invoker
 }
 
+// Destroy ...
 func (jp *JsonrpcProtocol) Destroy() {
 	logger.Infof("jsonrpcProtocol destroy.")
 
@@ -106,6 +125,7 @@ func (jp *JsonrpcProtocol) openServer(url common.URL) {
 	}
 }
 
+// GetProtocol ...
 func GetProtocol() protocol.Protocol {
 	if jsonrpcProtocol == nil {
 		jsonrpcProtocol = NewJsonrpcProtocol()
diff --git a/protocol/jsonrpc/jsonrpc_protocol_test.go b/protocol/jsonrpc/jsonrpc_protocol_test.go
index 253ab830dd85e5424811b7fd4e7e7e848adad415..c00bed12fe9fbb4937f21810cee548a25e3b1c05 100644
--- a/protocol/jsonrpc/jsonrpc_protocol_test.go
+++ b/protocol/jsonrpc/jsonrpc_protocol_test.go
@@ -18,7 +18,6 @@
 package jsonrpc
 
 import (
-	"context"
 	"fmt"
 	"strings"
 	"testing"
@@ -38,10 +37,10 @@ import (
 func TestJsonrpcProtocol_Export(t *testing.T) {
 	// Export
 	proto := GetProtocol()
-	url, err := common.NewURL(context.Background(), "jsonrpc://127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&"+
-		"application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&"+
-		"environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&"+
-		"module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&"+
+	url, err := common.NewURL("jsonrpc://127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&" +
+		"application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&" +
+		"environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&" +
+		"module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&" +
 		"side=provider&timeout=3000&timestamp=1556509797245")
 	assert.NoError(t, err)
 	exporter := proto.Export(protocol.NewBaseInvoker(url))
@@ -69,10 +68,10 @@ func TestJsonrpcProtocol_Export(t *testing.T) {
 func TestJsonrpcProtocol_Refer(t *testing.T) {
 	// Refer
 	proto := GetProtocol()
-	url, err := common.NewURL(context.Background(), "jsonrpc://127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&"+
-		"application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&"+
-		"environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&"+
-		"module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&"+
+	url, err := common.NewURL("jsonrpc://127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&" +
+		"application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&" +
+		"environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&" +
+		"module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&" +
 		"side=provider&timeout=3000&timestamp=1556509797245")
 	assert.NoError(t, err)
 	con := config.ConsumerConfig{
diff --git a/protocol/jsonrpc/server.go b/protocol/jsonrpc/server.go
index dc85e0f5e76fd07dbcd11646ae529c98e5323a15..8600f02dad3d32d797613823de0bbe40261d2e71 100644
--- a/protocol/jsonrpc/server.go
+++ b/protocol/jsonrpc/server.go
@@ -32,6 +32,7 @@ import (
 )
 
 import (
+	"github.com/opentracing/opentracing-go"
 	perrors "github.com/pkg/errors"
 )
 
@@ -50,11 +51,15 @@ var (
 )
 
 const (
-	DefaultMaxSleepTime      = 1 * time.Second // accept涓棿鏈€澶leep interval
+	// DefaultMaxSleepTime max sleep interval in accept
+	DefaultMaxSleepTime = 1 * time.Second
+	// DefaultHTTPRspBufferSize ...
 	DefaultHTTPRspBufferSize = 1024
-	PathPrefix               = byte('/')
+	// PathPrefix ...
+	PathPrefix = byte('/')
 )
 
+// Server ...
 type Server struct {
 	done chan struct{}
 	once sync.Once
@@ -64,6 +69,7 @@ type Server struct {
 	timeout time.Duration
 }
 
+// NewServer ...
 func NewServer() *Server {
 	return &Server{
 		done: make(chan struct{}),
@@ -93,6 +99,8 @@ func (s *Server) handlePkg(conn net.Conn) {
 		rsp := &http.Response{
 			Header:        header,
 			StatusCode:    500,
+			ProtoMajor:    1,
+			ProtoMinor:    1,
 			ContentLength: int64(len(body)),
 			Body:          ioutil.NopCloser(bytes.NewReader(body)),
 		}
@@ -147,6 +155,13 @@ func (s *Server) handlePkg(conn net.Conn) {
 		}
 
 		ctx := context.Background()
+
+		spanCtx, err := opentracing.GlobalTracer().Extract(opentracing.HTTPHeaders,
+			opentracing.HTTPHeadersCarrier(r.Header))
+		if err == nil {
+			ctx = context.WithValue(ctx, constant.TRACING_REMOTE_SPAN_CTX, spanCtx)
+		}
+
 		if len(reqHeader["Timeout"]) > 0 {
 			timeout, err := time.ParseDuration(reqHeader["Timeout"])
 			if err == nil {
@@ -213,6 +228,7 @@ func accept(listener net.Listener, fn func(net.Conn)) error {
 	}
 }
 
+// Start ...
 func (s *Server) Start(url common.URL) {
 	listener, err := net.Listen("tcp", url.Location)
 	if err != nil {
@@ -239,6 +255,7 @@ func (s *Server) Start(url common.URL) {
 	}()
 }
 
+// Stop ...
 func (s *Server) Stop() {
 	s.once.Do(func() {
 		close(s.done)
@@ -252,6 +269,8 @@ func serveRequest(ctx context.Context,
 		rsp := &http.Response{
 			Header:        make(http.Header),
 			StatusCode:    500,
+			ProtoMajor:    1,
+			ProtoMinor:    1,
 			ContentLength: int64(len(body)),
 			Body:          ioutil.NopCloser(bytes.NewReader(body)),
 		}
@@ -276,6 +295,8 @@ func serveRequest(ctx context.Context,
 		rsp := &http.Response{
 			Header:        make(http.Header),
 			StatusCode:    200,
+			ProtoMajor:    1,
+			ProtoMinor:    1,
 			ContentLength: int64(len(body)),
 			Body:          ioutil.NopCloser(bytes.NewReader(body)),
 		}
@@ -324,10 +345,9 @@ func serveRequest(ctx context.Context,
 	exporter, _ := jsonrpcProtocol.ExporterMap().Load(path)
 	invoker := exporter.(*JsonrpcExporter).GetInvoker()
 	if invoker != nil {
-		result := invoker.Invoke(invocation.NewRPCInvocation(methodName, args, map[string]string{
+		result := invoker.Invoke(ctx, invocation.NewRPCInvocation(methodName, args, map[string]string{
 			constant.PATH_KEY:    path,
-			constant.VERSION_KEY: codec.req.Version,
-		}))
+			constant.VERSION_KEY: codec.req.Version}))
 		if err := result.Error(); err != nil {
 			rspStream, err := codec.Write(err.Error(), invalidRequest)
 			if err != nil {
diff --git a/protocol/mock/mock_invoker.go b/protocol/mock/mock_invoker.go
index c509cef054f5a23fe504486e01d7cc0e8772711d..5c5b476b7b07f6c41a74a7ec8f51648aff84b1a3 100644
--- a/protocol/mock/mock_invoker.go
+++ b/protocol/mock/mock_invoker.go
@@ -21,6 +21,7 @@
 package mock
 
 import (
+	"context"
 	"reflect"
 )
 
@@ -91,7 +92,7 @@ func (mr *MockInvokerMockRecorder) Destroy() *gomock.Call {
 }
 
 // Invoke mocks base method
-func (m *MockInvoker) Invoke(arg0 protocol.Invocation) protocol.Result {
+func (m *MockInvoker) Invoke(ctx context.Context, arg0 protocol.Invocation) protocol.Result {
 	ret := m.ctrl.Call(m, "Invoke", arg0)
 	ret0, _ := ret[0].(protocol.Result)
 	return ret0
diff --git a/protocol/protocol.go b/protocol/protocol.go
index 814a85163a99aa3b161b5eafbfed5f13ac4e3eb4..a873469a8ba361c9dfc922b071ffbf256c6a8b98 100644
--- a/protocol/protocol.go
+++ b/protocol/protocol.go
@@ -26,6 +26,7 @@ import (
 	"github.com/apache/dubbo-go/common/logger"
 )
 
+// Protocol
 // Extension - protocol
 type Protocol interface {
 	Export(invoker Invoker) Exporter
@@ -33,6 +34,7 @@ type Protocol interface {
 	Destroy()
 }
 
+// Exporter
 // wrapping invoker
 type Exporter interface {
 	GetInvoker() Invoker
@@ -43,37 +45,45 @@ type Exporter interface {
 // base protocol
 /////////////////////////////
 
+// BaseProtocol ...
 type BaseProtocol struct {
 	exporterMap *sync.Map
 	invokers    []Invoker
 }
 
+// NewBaseProtocol ...
 func NewBaseProtocol() BaseProtocol {
 	return BaseProtocol{
 		exporterMap: new(sync.Map),
 	}
 }
 
+// SetExporterMap ...
 func (bp *BaseProtocol) SetExporterMap(key string, exporter Exporter) {
 	bp.exporterMap.Store(key, exporter)
 }
 
+// ExporterMap ...
 func (bp *BaseProtocol) ExporterMap() *sync.Map {
 	return bp.exporterMap
 }
 
+// SetInvokers ...
 func (bp *BaseProtocol) SetInvokers(invoker Invoker) {
 	bp.invokers = append(bp.invokers, invoker)
 }
 
+// Invokers ...
 func (bp *BaseProtocol) Invokers() []Invoker {
 	return bp.invokers
 }
 
+// Export ...
 func (bp *BaseProtocol) Export(invoker Invoker) Exporter {
 	return NewBaseExporter("base", invoker, bp.exporterMap)
 }
 
+// Refer ...
 func (bp *BaseProtocol) Refer(url common.URL) Invoker {
 	return NewBaseInvoker(url)
 }
@@ -103,12 +113,14 @@ func (bp *BaseProtocol) Destroy() {
 // base exporter
 /////////////////////////////
 
+// BaseExporter ...
 type BaseExporter struct {
 	key         string
 	invoker     Invoker
 	exporterMap *sync.Map
 }
 
+// NewBaseExporter ...
 func NewBaseExporter(key string, invoker Invoker, exporterMap *sync.Map) *BaseExporter {
 	return &BaseExporter{
 		key:         key,
@@ -117,11 +129,13 @@ func NewBaseExporter(key string, invoker Invoker, exporterMap *sync.Map) *BaseEx
 	}
 }
 
+// GetInvoker ...
 func (de *BaseExporter) GetInvoker() Invoker {
 	return de.invoker
 
 }
 
+// Unexport ...
 func (de *BaseExporter) Unexport() {
 	logger.Infof("Exporter unexport.")
 	de.invoker.Destroy()
diff --git a/protocol/protocolwrapper/mock_protocol_filter.go b/protocol/protocolwrapper/mock_protocol_filter.go
index 2efc34da4469cf369d4bbeb871ccfbdb73123f6a..dedf8aa64b6ae1b7b4782350e2625b02171aac44 100644
--- a/protocol/protocolwrapper/mock_protocol_filter.go
+++ b/protocol/protocolwrapper/mock_protocol_filter.go
@@ -28,6 +28,7 @@ import (
 
 type mockProtocolFilter struct{}
 
+// NewMockProtocolFilter ...
 func NewMockProtocolFilter() protocol.Protocol {
 	return &mockProtocolFilter{}
 }
diff --git a/protocol/protocolwrapper/protocol_filter_wrapper.go b/protocol/protocolwrapper/protocol_filter_wrapper.go
index 7c58fabea3cccf5a39e1622fedd4a3a297e05983..70d2da0faed3bc9797eb23cec653bea05d445d91 100644
--- a/protocol/protocolwrapper/protocol_filter_wrapper.go
+++ b/protocol/protocolwrapper/protocol_filter_wrapper.go
@@ -18,6 +18,7 @@
 package protocolwrapper
 
 import (
+	"context"
 	"strings"
 )
 
@@ -30,6 +31,7 @@ import (
 )
 
 const (
+	// FILTER ...
 	FILTER = "filter"
 )
 
@@ -37,11 +39,13 @@ func init() {
 	extension.SetProtocol(FILTER, GetProtocol)
 }
 
+// ProtocolFilterWrapper
 // protocol in url decide who ProtocolFilterWrapper.protocol is
 type ProtocolFilterWrapper struct {
 	protocol protocol.Protocol
 }
 
+// Export ...
 func (pfw *ProtocolFilterWrapper) Export(invoker protocol.Invoker) protocol.Exporter {
 	if pfw.protocol == nil {
 		pfw.protocol = extension.GetProtocol(invoker.GetUrl().Protocol)
@@ -50,6 +54,7 @@ func (pfw *ProtocolFilterWrapper) Export(invoker protocol.Invoker) protocol.Expo
 	return pfw.protocol.Export(invoker)
 }
 
+// Refer ...
 func (pfw *ProtocolFilterWrapper) Refer(url common.URL) protocol.Invoker {
 	if pfw.protocol == nil {
 		pfw.protocol = extension.GetProtocol(url.Protocol)
@@ -57,6 +62,7 @@ func (pfw *ProtocolFilterWrapper) Refer(url common.URL) protocol.Invoker {
 	return buildInvokerChain(pfw.protocol.Refer(url), constant.REFERENCE_FILTER_KEY)
 }
 
+// Destroy ...
 func (pfw *ProtocolFilterWrapper) Destroy() {
 	pfw.protocol.Destroy()
 }
@@ -72,14 +78,15 @@ func buildInvokerChain(invoker protocol.Invoker, key string) protocol.Invoker {
 	// The order of filters is from left to right, so loading from right to left
 
 	for i := len(filtNames) - 1; i >= 0; i-- {
-		filter := extension.GetFilter(filtNames[i])
-		fi := &FilterInvoker{next: next, invoker: invoker, filter: filter}
+		flt := extension.GetFilter(filtNames[i])
+		fi := &FilterInvoker{next: next, invoker: invoker, filter: flt}
 		next = fi
 	}
 
 	return next
 }
 
+// GetProtocol ...
 func GetProtocol() protocol.Protocol {
 	return &ProtocolFilterWrapper{}
 }
@@ -88,25 +95,30 @@ func GetProtocol() protocol.Protocol {
 // filter invoker
 ///////////////////////////
 
+// FilterInvoker ...
 type FilterInvoker struct {
 	next    protocol.Invoker
 	invoker protocol.Invoker
 	filter  filter.Filter
 }
 
+// GetUrl ...
 func (fi *FilterInvoker) GetUrl() common.URL {
 	return fi.invoker.GetUrl()
 }
 
+// IsAvailable ...
 func (fi *FilterInvoker) IsAvailable() bool {
 	return fi.invoker.IsAvailable()
 }
 
-func (fi *FilterInvoker) Invoke(invocation protocol.Invocation) protocol.Result {
-	result := fi.filter.Invoke(fi.next, invocation)
-	return fi.filter.OnResponse(result, fi.invoker, invocation)
+// Invoke ...
+func (fi *FilterInvoker) Invoke(ctx context.Context, invocation protocol.Invocation) protocol.Result {
+	result := fi.filter.Invoke(ctx, fi.next, invocation)
+	return fi.filter.OnResponse(ctx, result, fi.invoker, invocation)
 }
 
+// Destroy ...
 func (fi *FilterInvoker) Destroy() {
 	fi.invoker.Destroy()
 }
diff --git a/protocol/protocolwrapper/protocol_filter_wrapper_test.go b/protocol/protocolwrapper/protocol_filter_wrapper_test.go
index dc376313549c24da1cc6cb64a42e8445ef4fe346..8491d57462d47d6af72040d41b78dcb30e6da697 100644
--- a/protocol/protocolwrapper/protocol_filter_wrapper_test.go
+++ b/protocol/protocolwrapper/protocol_filter_wrapper_test.go
@@ -18,6 +18,7 @@
 package protocolwrapper
 
 import (
+	"context"
 	"net/url"
 	"testing"
 )
@@ -66,7 +67,7 @@ func init() {
 
 type EchoFilterForTest struct{}
 
-func (ef *EchoFilterForTest) Invoke(invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
+func (ef *EchoFilterForTest) Invoke(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
 	logger.Infof("invoking echo filter.")
 	logger.Debugf("%v,%v", invocation.MethodName(), len(invocation.Arguments()))
 	if invocation.MethodName() == constant.ECHO && len(invocation.Arguments()) == 1 {
@@ -75,10 +76,10 @@ func (ef *EchoFilterForTest) Invoke(invoker protocol.Invoker, invocation protoco
 		}
 	}
 
-	return invoker.Invoke(invocation)
+	return invoker.Invoke(ctx, invocation)
 }
 
-func (ef *EchoFilterForTest) OnResponse(result protocol.Result, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
+func (ef *EchoFilterForTest) OnResponse(ctx context.Context, result protocol.Result, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
 	return result
 }
 
diff --git a/protocol/result.go b/protocol/result.go
index dcdb62310d359d441067395ea92f8460df97eb22..34e76d2dddbaed33b2e2c015631443565cfaea87 100644
--- a/protocol/result.go
+++ b/protocol/result.go
@@ -17,6 +17,7 @@
 
 package protocol
 
+// Result ...
 type Result interface {
 	SetError(error)
 	Error() error
@@ -32,12 +33,14 @@ type Result interface {
 // Result Impletment of RPC
 /////////////////////////////
 
+// RPCResult ...
 type RPCResult struct {
 	Attrs map[string]string
 	Err   error
 	Rest  interface{}
 }
 
+// SetError ...
 func (r *RPCResult) SetError(err error) {
 	r.Err = err
 }
@@ -46,26 +49,32 @@ func (r *RPCResult) Error() error {
 	return r.Err
 }
 
+// SetResult ...
 func (r *RPCResult) SetResult(rest interface{}) {
 	r.Rest = rest
 }
 
+// Result ...
 func (r *RPCResult) Result() interface{} {
 	return r.Rest
 }
 
+// SetAttachments ...
 func (r *RPCResult) SetAttachments(attr map[string]string) {
 	r.Attrs = attr
 }
 
+// Attachments ...
 func (r *RPCResult) Attachments() map[string]string {
 	return r.Attrs
 }
 
+// AddAttachment ...
 func (r *RPCResult) AddAttachment(key, value string) {
 	r.Attrs[key] = value
 }
 
+// Attachment ...
 func (r *RPCResult) Attachment(key, defaultValue string) string {
 	v, ok := r.Attrs[key]
 	if !ok {
diff --git a/protocol/rpc_status.go b/protocol/rpc_status.go
index 3a8bfbc87f285e0e86269d44c47d6771566d97b1..13be47c98ece1cc006250ad49ab2b9a8c3b1f625 100644
--- a/protocol/rpc_status.go
+++ b/protocol/rpc_status.go
@@ -20,6 +20,7 @@ package protocol
 import (
 	"sync"
 	"sync/atomic"
+	"time"
 )
 
 import (
@@ -27,18 +28,82 @@ import (
 )
 
 var (
-	methodStatistics sync.Map // url -> { methodName : RpcStatus}
+	methodStatistics sync.Map // url -> { methodName : RPCStatus}
+	serviceStatistic sync.Map // url -> RPCStatus
 )
 
-type RpcStatus struct {
-	active int32
+// RPCStatus ...
+type RPCStatus struct {
+	active                        int32
+	failed                        int32
+	total                         int32
+	totalElapsed                  int64
+	failedElapsed                 int64
+	maxElapsed                    int64
+	failedMaxElapsed              int64
+	succeededMaxElapsed           int64
+	successiveRequestFailureCount int32
+	lastRequestFailedTimestamp    int64
 }
 
-func (rpc *RpcStatus) GetActive() int32 {
+// GetActive ...
+func (rpc *RPCStatus) GetActive() int32 {
 	return atomic.LoadInt32(&rpc.active)
 }
 
-func GetStatus(url common.URL, methodName string) *RpcStatus {
+// GetFailed ...
+func (rpc *RPCStatus) GetFailed() int32 {
+	return atomic.LoadInt32(&rpc.failed)
+}
+
+// GetTotal ...
+func (rpc *RPCStatus) GetTotal() int32 {
+	return atomic.LoadInt32(&rpc.total)
+}
+
+// GetTotalElapsed ...
+func (rpc *RPCStatus) GetTotalElapsed() int64 {
+	return atomic.LoadInt64(&rpc.totalElapsed)
+}
+
+// GetFailedElapsed ...
+func (rpc *RPCStatus) GetFailedElapsed() int64 {
+	return atomic.LoadInt64(&rpc.failedElapsed)
+}
+
+// GetMaxElapsed ...
+func (rpc *RPCStatus) GetMaxElapsed() int64 {
+	return atomic.LoadInt64(&rpc.maxElapsed)
+}
+
+// GetFailedMaxElapsed ...
+func (rpc *RPCStatus) GetFailedMaxElapsed() int64 {
+	return atomic.LoadInt64(&rpc.failedMaxElapsed)
+}
+
+// GetSucceededMaxElapsed ...
+func (rpc *RPCStatus) GetSucceededMaxElapsed() int64 {
+	return atomic.LoadInt64(&rpc.succeededMaxElapsed)
+}
+
+// GetLastRequestFailedTimestamp ...
+func (rpc *RPCStatus) GetLastRequestFailedTimestamp() int64 {
+	return atomic.LoadInt64(&rpc.lastRequestFailedTimestamp)
+}
+
+// GetSuccessiveRequestFailureCount ...
+func (rpc *RPCStatus) GetSuccessiveRequestFailureCount() int32 {
+	return atomic.LoadInt32(&rpc.successiveRequestFailureCount)
+}
+
+// GetURLStatus ...
+func GetURLStatus(url common.URL) *RPCStatus {
+	rpcStatus, _ := serviceStatistic.LoadOrStore(url.Key(), &RPCStatus{})
+	return rpcStatus.(*RPCStatus)
+}
+
+// GetMethodStatus ...
+func GetMethodStatus(url common.URL, methodName string) *RPCStatus {
 	identifier := url.Key()
 	methodMap, found := methodStatistics.Load(identifier)
 	if !found {
@@ -49,27 +114,70 @@ func GetStatus(url common.URL, methodName string) *RpcStatus {
 	methodActive := methodMap.(*sync.Map)
 	rpcStatus, found := methodActive.Load(methodName)
 	if !found {
-		rpcStatus = &RpcStatus{}
+		rpcStatus = &RPCStatus{}
 		methodActive.Store(methodName, rpcStatus)
 	}
 
-	status := rpcStatus.(*RpcStatus)
+	status := rpcStatus.(*RPCStatus)
 	return status
 }
 
+// BeginCount ...
 func BeginCount(url common.URL, methodName string) {
-	beginCount0(GetStatus(url, methodName))
+	beginCount0(GetURLStatus(url))
+	beginCount0(GetMethodStatus(url, methodName))
 }
 
-func EndCount(url common.URL, methodName string) {
-	endCount0(GetStatus(url, methodName))
+// EndCount ...
+func EndCount(url common.URL, methodName string, elapsed int64, succeeded bool) {
+	endCount0(GetURLStatus(url), elapsed, succeeded)
+	endCount0(GetMethodStatus(url, methodName), elapsed, succeeded)
 }
 
 // private methods
-func beginCount0(rpcStatus *RpcStatus) {
+func beginCount0(rpcStatus *RPCStatus) {
 	atomic.AddInt32(&rpcStatus.active, 1)
 }
 
-func endCount0(rpcStatus *RpcStatus) {
+func endCount0(rpcStatus *RPCStatus, elapsed int64, succeeded bool) {
 	atomic.AddInt32(&rpcStatus.active, -1)
+	atomic.AddInt32(&rpcStatus.total, 1)
+	atomic.AddInt64(&rpcStatus.totalElapsed, elapsed)
+
+	if rpcStatus.maxElapsed < elapsed {
+		atomic.StoreInt64(&rpcStatus.maxElapsed, elapsed)
+	}
+	if succeeded {
+		if rpcStatus.succeededMaxElapsed < elapsed {
+			atomic.StoreInt64(&rpcStatus.succeededMaxElapsed, elapsed)
+		}
+		atomic.StoreInt32(&rpcStatus.successiveRequestFailureCount, 0)
+	} else {
+		atomic.StoreInt64(&rpcStatus.lastRequestFailedTimestamp, CurrentTimeMillis())
+		atomic.AddInt32(&rpcStatus.successiveRequestFailureCount, 1)
+		atomic.AddInt32(&rpcStatus.failed, 1)
+		atomic.AddInt64(&rpcStatus.failedElapsed, elapsed)
+		if rpcStatus.failedMaxElapsed < elapsed {
+			atomic.StoreInt64(&rpcStatus.failedMaxElapsed, elapsed)
+		}
+	}
+}
+
+// CurrentTimeMillis ...
+func CurrentTimeMillis() int64 {
+	return time.Now().UnixNano() / int64(time.Millisecond)
+}
+
+// Destroy is used to clean all status
+func CleanAllStatus() {
+	delete1 := func(key interface{}, value interface{}) bool {
+		methodStatistics.Delete(key)
+		return true
+	}
+	methodStatistics.Range(delete1)
+	delete2 := func(key interface{}, value interface{}) bool {
+		serviceStatistic.Delete(key)
+		return true
+	}
+	serviceStatistic.Range(delete2)
 }
diff --git a/protocol/rpc_status_test.go b/protocol/rpc_status_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..5a07f44eab0db60ba65a155d6c6190ab4ce2d716
--- /dev/null
+++ b/protocol/rpc_status_test.go
@@ -0,0 +1,138 @@
+package protocol
+
+import (
+	"strconv"
+	"testing"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+import (
+	"github.com/apache/dubbo-go/common"
+)
+
+func TestBeginCount(t *testing.T) {
+	defer CleanAllStatus()
+
+	url, _ := common.NewURL("dubbo://192.168.10.10:20000/com.ikurento.user.UserProvider")
+	BeginCount(url, "test")
+	urlStatus := GetURLStatus(url)
+	methodStatus := GetMethodStatus(url, "test")
+	methodStatus1 := GetMethodStatus(url, "test1")
+	assert.Equal(t, int32(1), methodStatus.active)
+	assert.Equal(t, int32(1), urlStatus.active)
+	assert.Equal(t, int32(0), methodStatus1.active)
+
+}
+
+func TestEndCount(t *testing.T) {
+	defer CleanAllStatus()
+
+	url, _ := common.NewURL("dubbo://192.168.10.10:20000/com.ikurento.user.UserProvider")
+	EndCount(url, "test", 100, true)
+	urlStatus := GetURLStatus(url)
+	methodStatus := GetMethodStatus(url, "test")
+	assert.Equal(t, int32(-1), methodStatus.active)
+	assert.Equal(t, int32(-1), urlStatus.active)
+	assert.Equal(t, int32(1), methodStatus.total)
+	assert.Equal(t, int32(1), urlStatus.total)
+}
+
+func TestGetMethodStatus(t *testing.T) {
+	defer CleanAllStatus()
+
+	url, _ := common.NewURL("dubbo://192.168.10.10:20000/com.ikurento.user.UserProvider")
+	status := GetMethodStatus(url, "test")
+	assert.NotNil(t, status)
+	assert.Equal(t, int32(0), status.total)
+}
+
+func TestGetUrlStatus(t *testing.T) {
+	defer CleanAllStatus()
+
+	url, _ := common.NewURL("dubbo://192.168.10.10:20000/com.ikurento.user.UserProvider")
+	status := GetURLStatus(url)
+	assert.NotNil(t, status)
+	assert.Equal(t, int32(0), status.total)
+}
+
+func Test_beginCount0(t *testing.T) {
+	defer CleanAllStatus()
+
+	url, _ := common.NewURL("dubbo://192.168.10.10:20000/com.ikurento.user.UserProvider")
+	status := GetURLStatus(url)
+	beginCount0(status)
+	assert.Equal(t, int32(1), status.active)
+}
+
+func Test_All(t *testing.T) {
+	defer CleanAllStatus()
+
+	url, _ := common.NewURL("dubbo://192.168.10.10:20000/com.ikurento.user.UserProvider")
+	request(url, "test", 100, false, true)
+	urlStatus := GetURLStatus(url)
+	methodStatus := GetMethodStatus(url, "test")
+	assert.Equal(t, int32(1), methodStatus.total)
+	assert.Equal(t, int32(1), urlStatus.total)
+	assert.Equal(t, int32(0), methodStatus.active)
+	assert.Equal(t, int32(0), urlStatus.active)
+	assert.Equal(t, int32(0), methodStatus.failed)
+	assert.Equal(t, int32(0), urlStatus.failed)
+	assert.Equal(t, int32(0), methodStatus.successiveRequestFailureCount)
+	assert.Equal(t, int32(0), urlStatus.successiveRequestFailureCount)
+	assert.Equal(t, int64(100), methodStatus.totalElapsed)
+	assert.Equal(t, int64(100), urlStatus.totalElapsed)
+	request(url, "test", 100, false, false)
+	request(url, "test", 100, false, false)
+	request(url, "test", 100, false, false)
+	request(url, "test", 100, false, false)
+	request(url, "test", 100, false, false)
+	assert.Equal(t, int32(6), methodStatus.total)
+	assert.Equal(t, int32(6), urlStatus.total)
+	assert.Equal(t, int32(5), methodStatus.failed)
+	assert.Equal(t, int32(5), urlStatus.failed)
+	assert.Equal(t, int32(5), methodStatus.successiveRequestFailureCount)
+	assert.Equal(t, int32(5), urlStatus.successiveRequestFailureCount)
+	assert.Equal(t, int64(600), methodStatus.totalElapsed)
+	assert.Equal(t, int64(600), urlStatus.totalElapsed)
+	assert.Equal(t, int64(500), methodStatus.failedElapsed)
+	assert.Equal(t, int64(500), urlStatus.failedElapsed)
+
+	request(url, "test", 100, false, true)
+	assert.Equal(t, int32(0), methodStatus.successiveRequestFailureCount)
+	assert.Equal(t, int32(0), urlStatus.successiveRequestFailureCount)
+
+	request(url, "test", 200, false, false)
+	request(url, "test", 200, false, false)
+	assert.Equal(t, int32(2), methodStatus.successiveRequestFailureCount)
+	assert.Equal(t, int32(2), urlStatus.successiveRequestFailureCount)
+	assert.Equal(t, int64(200), methodStatus.maxElapsed)
+	assert.Equal(t, int64(200), urlStatus.maxElapsed)
+
+	request(url, "test1", 200, false, false)
+	request(url, "test1", 200, false, false)
+	request(url, "test1", 200, false, false)
+	assert.Equal(t, int32(5), urlStatus.successiveRequestFailureCount)
+	methodStatus1 := GetMethodStatus(url, "test1")
+	assert.Equal(t, int32(2), methodStatus.successiveRequestFailureCount)
+	assert.Equal(t, int32(3), methodStatus1.successiveRequestFailureCount)
+
+}
+
+func request(url common.URL, method string, elapsed int64, active, succeeded bool) {
+	BeginCount(url, method)
+	if !active {
+		EndCount(url, method, elapsed, succeeded)
+	}
+}
+
+func TestCurrentTimeMillis(t *testing.T) {
+	defer CleanAllStatus()
+	c := CurrentTimeMillis()
+	assert.NotNil(t, c)
+	str := strconv.FormatInt(c, 10)
+	i, _ := strconv.ParseInt(str, 10, 64)
+	assert.Equal(t, c, i)
+}
diff --git a/registry/base_configuration_listener.go b/registry/base_configuration_listener.go
index 925baa2198d9917824c1be78b7cd0c2f93bfb894..55418318dfc52ed9f17f1ec6a18ad9ef9d8163bf 100644
--- a/registry/base_configuration_listener.go
+++ b/registry/base_configuration_listener.go
@@ -29,15 +29,19 @@ import (
 	"github.com/apache/dubbo-go/remoting"
 )
 
+// BaseConfigurationListener ...
 type BaseConfigurationListener struct {
 	configurators           []config_center.Configurator
 	dynamicConfiguration    config_center.DynamicConfiguration
 	defaultConfiguratorFunc func(url *common.URL) config_center.Configurator
 }
 
+// Configurators ...
 func (bcl *BaseConfigurationListener) Configurators() []config_center.Configurator {
 	return bcl.configurators
 }
+
+// InitWith ...
 func (bcl *BaseConfigurationListener) InitWith(key string, listener config_center.ConfigurationListener, f func(url *common.URL) config_center.Configurator) {
 	bcl.dynamicConfiguration = config.GetEnvInstance().GetDynamicConfiguration()
 	if bcl.dynamicConfiguration == nil {
@@ -47,7 +51,7 @@ func (bcl *BaseConfigurationListener) InitWith(key string, listener config_cente
 	}
 	bcl.defaultConfiguratorFunc = f
 	bcl.dynamicConfiguration.AddListener(key, listener)
-	if rawConfig, err := bcl.dynamicConfiguration.GetConfig(key, config_center.WithGroup(constant.DUBBO)); err != nil {
+	if rawConfig, err := bcl.dynamicConfiguration.GetInternalProperty(key, config_center.WithGroup(constant.DUBBO)); err != nil {
 		//set configurators to empty
 		bcl.configurators = []config_center.Configurator{}
 		return
@@ -56,6 +60,7 @@ func (bcl *BaseConfigurationListener) InitWith(key string, listener config_cente
 	}
 }
 
+// Process ...
 func (bcl *BaseConfigurationListener) Process(event *config_center.ConfigChangeEvent) {
 	logger.Infof("Notification of overriding rule, change type is: %v , raw config content is:%v", event.ConfigType, event.Value)
 	if event.ConfigType == remoting.EventTypeDel {
@@ -76,12 +81,15 @@ func (bcl *BaseConfigurationListener) genConfiguratorFromRawRule(rawConfig strin
 	bcl.configurators = ToConfigurators(urls, bcl.defaultConfiguratorFunc)
 	return nil
 }
+
+// OverrideUrl ...
 func (bcl *BaseConfigurationListener) OverrideUrl(url *common.URL) {
 	for _, v := range bcl.configurators {
 		v.Configure(url)
 	}
 }
 
+// ToConfigurators ...
 func ToConfigurators(urls []*common.URL, f func(url *common.URL) config_center.Configurator) []config_center.Configurator {
 	if len(urls) == 0 {
 		return nil
diff --git a/registry/base_registry.go b/registry/base_registry.go
new file mode 100644
index 0000000000000000000000000000000000000000..5b9aef82928d491d4b8f4dbe3caa4bd64a185dad
--- /dev/null
+++ b/registry/base_registry.go
@@ -0,0 +1,375 @@
+/*
+ * 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 (
+	"context"
+	"fmt"
+	"net/url"
+	"os"
+	"strconv"
+	"strings"
+	"sync"
+	"time"
+)
+
+import (
+	gxnet "github.com/dubbogo/gost/net"
+	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/logger"
+)
+
+const (
+	// RegistryConnDelay connection delay
+	RegistryConnDelay = 3
+	// MaxWaitInterval max wait interval
+	MaxWaitInterval = 3 * time.Second
+)
+
+var (
+	processID = ""
+	localIP   = ""
+)
+
+func init() {
+	processID = fmt.Sprintf("%d", os.Getpid())
+	localIP, _ = gxnet.GetLocalIP()
+}
+
+/*
+ * -----------------------------------NOTICE---------------------------------------------
+ * If there is no special case, you'd better inherit BaseRegistry and implement the
+ * FacadeBasedRegistry interface instead of directly implementing the Registry interface.
+ * --------------------------------------------------------------------------------------
+ */
+
+/*
+ * FacadeBasedRegistry interface is subclass of Registry, and it is designed for registry who want to inherit BaseRegistry.
+ * You have to implement the interface to inherit BaseRegistry.
+ */
+type FacadeBasedRegistry interface {
+	Registry
+	CreatePath(string) error
+	DoRegister(string, string) error
+	DoSubscribe(conf *common.URL) (Listener, error)
+	CloseAndNilClient()
+	CloseListener()
+	InitListeners()
+}
+
+// BaseRegistry is a common logic abstract for registry. It implement Registry interface.
+type BaseRegistry struct {
+	context             context.Context
+	facadeBasedRegistry FacadeBasedRegistry
+	*common.URL
+	birth    int64          // time of file birth, seconds since Epoch; 0 if unknown
+	wg       sync.WaitGroup // wg+done for zk restart
+	done     chan struct{}
+	cltLock  sync.Mutex            //ctl lock is a lock for services map
+	services map[string]common.URL // service name + protocol -> service config, for store the service registered
+}
+
+// InitBaseRegistry for init some local variables and set BaseRegistry's subclass to it
+func (r *BaseRegistry) InitBaseRegistry(url *common.URL, facadeRegistry FacadeBasedRegistry) Registry {
+	r.URL = url
+	r.birth = time.Now().UnixNano()
+	r.done = make(chan struct{})
+	r.services = make(map[string]common.URL)
+	r.facadeBasedRegistry = facadeRegistry
+	return r
+}
+
+// GetUrl for get registry's url
+func (r *BaseRegistry) GetUrl() common.URL {
+	return *r.URL
+}
+
+// Destroy for graceful down
+func (r *BaseRegistry) Destroy() {
+	//first step close registry's all listeners
+	r.facadeBasedRegistry.CloseListener()
+	// then close r.done to notify other program who listen to it
+	close(r.done)
+	// wait waitgroup done (wait listeners outside close over)
+	r.wg.Wait()
+	//close registry client
+	r.closeRegisters()
+}
+
+// Register implement interface registry to register
+func (r *BaseRegistry) Register(conf common.URL) error {
+	var (
+		ok  bool
+		err error
+	)
+	role, _ := strconv.Atoi(r.URL.GetParam(constant.ROLE_KEY, ""))
+	// Check if the service has been registered
+	r.cltLock.Lock()
+	_, ok = r.services[conf.Key()]
+	r.cltLock.Unlock()
+	if ok {
+		return perrors.Errorf("Path{%s} has been registered", conf.Key())
+	}
+
+	err = r.register(conf)
+	if err != nil {
+		return perrors.WithMessagef(err, "register(conf:%+v)", conf)
+	}
+
+	r.cltLock.Lock()
+	r.services[conf.Key()] = conf
+	r.cltLock.Unlock()
+	logger.Debugf("(%sRegistry)Register(conf{%#v})", common.DubboRole[role], conf)
+
+	return nil
+}
+
+// service is for getting service path stored in url
+func (r *BaseRegistry) service(c common.URL) string {
+	return url.QueryEscape(c.Service())
+}
+
+// RestartCallBack for reregister when reconnect
+func (r *BaseRegistry) RestartCallBack() bool {
+
+	// copy r.services
+	services := []common.URL{}
+	for _, confIf := range r.services {
+		services = append(services, confIf)
+	}
+
+	flag := true
+	for _, confIf := range services {
+		err := r.register(confIf)
+		if err != nil {
+			logger.Errorf("(ZkProviderRegistry)register(conf{%#v}) = error{%#v}",
+				confIf, perrors.WithStack(err))
+			flag = false
+			break
+		}
+		logger.Infof("success to re-register service :%v", confIf.Key())
+	}
+	r.facadeBasedRegistry.InitListeners()
+
+	return flag
+}
+
+// register for register url to registry, include init params
+func (r *BaseRegistry) register(c common.URL) error {
+	var (
+		err error
+		//revision   string
+		params     url.Values
+		rawURL     string
+		encodedURL string
+		dubboPath  string
+		//conf       config.URL
+	)
+	params = url.Values{}
+
+	c.RangeParams(func(key, value string) bool {
+		params.Add(key, value)
+		return true
+	})
+
+	params.Add("pid", processID)
+	params.Add("ip", localIP)
+	//params.Add("timeout", fmt.Sprintf("%d", int64(r.Timeout)/1e6))
+
+	role, _ := strconv.Atoi(r.URL.GetParam(constant.ROLE_KEY, ""))
+	switch role {
+
+	case common.PROVIDER:
+		dubboPath, rawURL, err = r.providerRegistry(c, params)
+	case common.CONSUMER:
+		dubboPath, rawURL, err = r.consumerRegistry(c, params)
+	default:
+		return perrors.Errorf("@c{%v} type is not referencer or provider", c)
+	}
+	encodedURL = url.QueryEscape(rawURL)
+	dubboPath = strings.ReplaceAll(dubboPath, "$", "%24")
+	err = r.facadeBasedRegistry.DoRegister(dubboPath, encodedURL)
+
+	if err != nil {
+		return perrors.WithMessagef(err, "register Node(path:%s, url:%s)", dubboPath, rawURL)
+	}
+	return nil
+}
+
+// providerRegistry for provider role do
+func (r *BaseRegistry) providerRegistry(c common.URL, params url.Values) (string, string, error) {
+	var (
+		dubboPath string
+		rawURL    string
+		err       error
+	)
+	if c.Path == "" || len(c.Methods) == 0 {
+		return "", "", perrors.Errorf("conf{Path:%s, Methods:%s}", c.Path, c.Methods)
+	}
+	dubboPath = fmt.Sprintf("/dubbo/%s/%s", r.service(c), common.DubboNodes[common.PROVIDER])
+	r.cltLock.Lock()
+	err = r.facadeBasedRegistry.CreatePath(dubboPath)
+	r.cltLock.Unlock()
+	if err != nil {
+		logger.Errorf("facadeBasedRegistry.CreatePath(path{%s}) = error{%#v}", dubboPath, perrors.WithStack(err))
+		return "", "", perrors.WithMessagef(err, "facadeBasedRegistry.CreatePath(path:%s)", dubboPath)
+	}
+	params.Add("anyhost", "true")
+
+	// Dubbo java consumer to start looking for the provider url,because the category does not match,
+	// the provider will not find, causing the consumer can not start, so we use consumers.
+	// DubboRole               = [...]string{"consumer", "", "", "provider"}
+	// params.Add("category", (RoleType(PROVIDER)).Role())
+	params.Add("category", (common.RoleType(common.PROVIDER)).String())
+	params.Add("dubbo", "dubbo-provider-golang-"+constant.Version)
+
+	params.Add("side", (common.RoleType(common.PROVIDER)).Role())
+
+	if len(c.Methods) == 0 {
+		params.Add("methods", strings.Join(c.Methods, ","))
+	}
+	logger.Debugf("provider url params:%#v", params)
+	var host string
+	if c.Ip == "" {
+		host = localIP + ":" + c.Port
+	} else {
+		host = c.Ip + ":" + c.Port
+	}
+
+	rawURL = fmt.Sprintf("%s://%s%s?%s", c.Protocol, host, c.Path, params.Encode())
+	// Print your own registration service providers.
+	dubboPath = fmt.Sprintf("/dubbo/%s/%s", r.service(c), (common.RoleType(common.PROVIDER)).String())
+	logger.Debugf("provider path:%s, url:%s", dubboPath, rawURL)
+	return dubboPath, rawURL, nil
+}
+
+// consumerRegistry for consumer role do
+func (r *BaseRegistry) consumerRegistry(c common.URL, params url.Values) (string, string, error) {
+	var (
+		dubboPath string
+		rawURL    string
+		err       error
+	)
+	dubboPath = fmt.Sprintf("/dubbo/%s/%s", r.service(c), common.DubboNodes[common.CONSUMER])
+	r.cltLock.Lock()
+	err = r.facadeBasedRegistry.CreatePath(dubboPath)
+	r.cltLock.Unlock()
+	if err != nil {
+		logger.Errorf("facadeBasedRegistry.CreatePath(path{%s}) = error{%v}", dubboPath, perrors.WithStack(err))
+		return "", "", perrors.WithStack(err)
+	}
+	dubboPath = fmt.Sprintf("/dubbo/%s/%s", r.service(c), common.DubboNodes[common.PROVIDER])
+	r.cltLock.Lock()
+	err = r.facadeBasedRegistry.CreatePath(dubboPath)
+	r.cltLock.Unlock()
+	if err != nil {
+		logger.Errorf("facadeBasedRegistry.CreatePath(path{%s}) = error{%v}", dubboPath, perrors.WithStack(err))
+		return "", "", perrors.WithStack(err)
+	}
+
+	params.Add("protocol", c.Protocol)
+	params.Add("category", (common.RoleType(common.CONSUMER)).String())
+	params.Add("dubbo", "dubbogo-consumer-"+constant.Version)
+
+	rawURL = fmt.Sprintf("consumer://%s%s?%s", localIP, c.Path, params.Encode())
+	dubboPath = fmt.Sprintf("/dubbo/%s/%s", r.service(c), (common.RoleType(common.CONSUMER)).String())
+
+	logger.Debugf("consumer path:%s, url:%s", dubboPath, rawURL)
+	return dubboPath, rawURL, nil
+}
+
+// sleepWait...
+func sleepWait(n int) {
+	wait := time.Duration((n + 1) * 2e8)
+	if wait > MaxWaitInterval {
+		wait = MaxWaitInterval
+	}
+	time.Sleep(wait)
+}
+
+// Subscribe :subscribe from registry, event will notify by notifyListener
+func (r *BaseRegistry) Subscribe(url *common.URL, notifyListener NotifyListener) {
+	n := 0
+	for {
+		n++
+		if !r.IsAvailable() {
+			logger.Warnf("event listener game over.")
+			return
+		}
+
+		listener, err := r.facadeBasedRegistry.DoSubscribe(url)
+		if err != nil {
+			if !r.IsAvailable() {
+				logger.Warnf("event listener game over.")
+				return
+			}
+			logger.Warnf("getListener() = err:%v", perrors.WithStack(err))
+			time.Sleep(time.Duration(RegistryConnDelay) * time.Second)
+			continue
+		}
+
+		for {
+			if serviceEvent, err := listener.Next(); err != nil {
+				logger.Warnf("Selector.watch() = error{%v}", perrors.WithStack(err))
+				listener.Close()
+				break
+			} else {
+				logger.Infof("update begin, service event: %v", serviceEvent.String())
+				notifyListener.Notify(serviceEvent)
+			}
+
+		}
+		sleepWait(n)
+	}
+}
+
+// closeRegisters close and remove registry client and reset services map
+func (r *BaseRegistry) closeRegisters() {
+	r.cltLock.Lock()
+	defer r.cltLock.Unlock()
+	logger.Infof("begin to close provider client")
+	// Close and remove(set to nil) the registry client
+	r.facadeBasedRegistry.CloseAndNilClient()
+	// reset the services map
+	r.services = nil
+}
+
+// IsAvailable judge to is registry not closed by chan r.done
+func (r *BaseRegistry) IsAvailable() bool {
+	select {
+	case <-r.done:
+		return false
+	default:
+		return true
+	}
+}
+
+// WaitGroup open for outside add the waitgroup to add some logic before registry destroyed over(graceful down)
+func (r *BaseRegistry) WaitGroup() *sync.WaitGroup {
+	return &r.wg
+}
+
+// Done open for outside to listen the event of registry Destroy() called.
+func (r *BaseRegistry) Done() chan struct{} {
+	return r.done
+}
diff --git a/registry/consul/registry.go b/registry/consul/registry.go
index 73bf3975bc7c73f4a7748f46280ffb1aa5525ca8..c5b8510a6c87068a5b4f1ce52203d401a896a6c2 100644
--- a/registry/consul/registry.go
+++ b/registry/consul/registry.go
@@ -36,6 +36,7 @@ import (
 )
 
 const (
+	// RegistryConnDelay ...
 	RegistryConnDelay = 3
 )
 
@@ -137,14 +138,15 @@ func (r *consulRegistry) subscribe(url *common.URL, notifyListener registry.Noti
 		}
 
 		for {
-			if serviceEvent, err := listener.Next(); err != nil {
+			serviceEvent, err := listener.Next()
+			if err != nil {
 				logger.Warnf("Selector.watch() = error{%v}", perrors.WithStack(err))
 				listener.Close()
 				return
-			} else {
-				logger.Infof("update begin, service event: %v", serviceEvent.String())
-				notifyListener.Notify(serviceEvent)
 			}
+
+			logger.Infof("update begin, service event: %v", serviceEvent.String())
+			notifyListener.Notify(serviceEvent)
 		}
 	}
 }
diff --git a/registry/consul/utils.go b/registry/consul/utils.go
index d295f644631ae63b6bdf035f71f5f104a64083e2..05ac5e76e292a2b574bd5e661bbbcca4f419fc22 100644
--- a/registry/consul/utils.go
+++ b/registry/consul/utils.go
@@ -18,7 +18,6 @@
 package consul
 
 import (
-	"context"
 	"crypto/md5"
 	"encoding/hex"
 	"fmt"
@@ -100,7 +99,7 @@ func retrieveURL(service *consul.ServiceEntry) (common.URL, error) {
 	if !ok {
 		return common.URL{}, perrors.New("retrieve url fails with no url key in service meta")
 	}
-	url1, err := common.NewURL(context.Background(), url)
+	url1, err := common.NewURL(url)
 	if err != nil {
 		return common.URL{}, perrors.WithStack(err)
 	}
diff --git a/registry/directory/directory.go b/registry/directory/directory.go
index e88c611f6f20bc182c3630e328caab848affc08b..a6d2cdf49b0935b2402e03208d1ff5f702e1cc52 100644
--- a/registry/directory/directory.go
+++ b/registry/directory/directory.go
@@ -24,6 +24,7 @@ import (
 
 import (
 	perrors "github.com/pkg/errors"
+	"go.uber.org/atomic"
 )
 
 import (
@@ -41,10 +42,12 @@ import (
 	"github.com/apache/dubbo-go/remoting"
 )
 
+// Options ...
 type Options struct {
 	serviceTTL time.Duration
 }
 
+// Option ...
 type Option func(*Options)
 
 type registryDirectory struct {
@@ -59,8 +62,11 @@ type registryDirectory struct {
 	consumerConfigurationListener  *consumerConfigurationListener
 	referenceConfigurationListener *referenceConfigurationListener
 	Options
+	serviceKey string
+	forbidden  atomic.Bool
 }
 
+// NewRegistryDirectory ...
 func NewRegistryDirectory(url *common.URL, registry registry.Registry, opts ...Option) (*registryDirectory, error) {
 	options := Options{
 		//default 300s
@@ -106,7 +112,10 @@ func (dir *registryDirectory) update(res *registry.ServiceEvent) {
 }
 
 func (dir *registryDirectory) refreshInvokers(res *registry.ServiceEvent) {
-	var url *common.URL
+	var (
+		url        *common.URL
+		oldInvoker protocol.Invoker = nil
+	)
 	//judge is override or others
 	if res != nil {
 		url = &res.Service
@@ -118,15 +127,24 @@ func (dir *registryDirectory) refreshInvokers(res *registry.ServiceEvent) {
 		} else if url.Protocol == constant.ROUTER_PROTOCOL || //2.for router
 			url.GetParam(constant.CATEGORY_KEY, constant.DEFAULT_CATEGORY) == constant.ROUTER_CATEGORY {
 			url = nil
-			//TODO: router
 		}
 		switch res.Action {
 		case remoting.EventTypeAdd, remoting.EventTypeUpdate:
+			logger.Infof("selector add service url{%s}", res.Service)
+			var urls []*common.URL
+
+			for _, v := range directory.GetRouterURLSet().Values() {
+				urls = append(urls, v.(*common.URL))
+			}
+
+			if len(urls) > 0 {
+				dir.SetRouters(urls)
+			}
+
 			//dir.cacheService.EventTypeAdd(res.Path, dir.serviceTTL)
-			dir.cacheInvoker(url)
+			oldInvoker = dir.cacheInvoker(url)
 		case remoting.EventTypeDel:
-			//dir.cacheService.EventTypeDel(res.Path, dir.serviceTTL)
-			dir.uncacheInvoker(url)
+			oldInvoker = dir.uncacheInvoker(url)
 			logger.Infof("selector delete service url{%s}", res.Service)
 		default:
 			return
@@ -135,8 +153,14 @@ func (dir *registryDirectory) refreshInvokers(res *registry.ServiceEvent) {
 
 	newInvokers := dir.toGroupInvokers()
 	dir.listenerLock.Lock()
-	defer dir.listenerLock.Unlock()
 	dir.cacheInvokers = newInvokers
+	dir.listenerLock.Unlock()
+	// After dir.cacheInvokers is updated,destroy the oldInvoker
+	// Ensure that no request will enter the oldInvoker
+	if oldInvoker != nil {
+		oldInvoker.Destroy()
+	}
+
 }
 
 func (dir *registryDirectory) toGroupInvokers() []protocol.Invoker {
@@ -167,6 +191,7 @@ func (dir *registryDirectory) toGroupInvokers() []protocol.Invoker {
 		for _, invokers := range groupInvokersMap {
 			staticDir := directory.NewStaticDirectory(invokers)
 			cluster := extension.GetCluster(dir.GetUrl().SubURL.GetParam(constant.CLUSTER_KEY, constant.DEFAULT_CLUSTER))
+			staticDir.BuildRouterChain(invokers)
 			groupInvokersList = append(groupInvokersList, cluster.Join(staticDir))
 		}
 	}
@@ -174,12 +199,18 @@ func (dir *registryDirectory) toGroupInvokers() []protocol.Invoker {
 	return groupInvokersList
 }
 
-func (dir *registryDirectory) uncacheInvoker(url *common.URL) {
+// uncacheInvoker 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())
-	dir.cacheInvokersMap.Delete(url.Key())
+	if cacheInvoker, ok := dir.cacheInvokersMap.Load(url.Key()); ok {
+		dir.cacheInvokersMap.Delete(url.Key())
+		return cacheInvoker.(protocol.Invoker)
+	}
+	return nil
 }
 
-func (dir *registryDirectory) cacheInvoker(url *common.URL) {
+// cacheInvoker 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
 
@@ -190,55 +221,63 @@ func (dir *registryDirectory) cacheInvoker(url *common.URL) {
 	}
 	if url == nil {
 		logger.Error("URL is nil ,pls check if service url is subscribe successfully!")
-		return
+		return nil
 	}
 	//check the url's protocol is equal to the protocol which is configured in reference config or referenceUrl is not care about protocol
 	if url.Protocol == referenceUrl.Protocol || referenceUrl.Protocol == "" {
 		newUrl := common.MergeUrl(url, referenceUrl)
 		dir.overrideUrl(newUrl)
 		if cacheInvoker, ok := dir.cacheInvokersMap.Load(newUrl.Key()); !ok {
-			logger.Infof("service will be added in cache invokers: invokers url is  %s!", newUrl)
+			logger.Debugf("service will be added in cache invokers: invokers url is  %s!", newUrl)
 			newInvoker := extension.GetProtocol(protocolwrapper.FILTER).Refer(*newUrl)
 			if newInvoker != nil {
 				dir.cacheInvokersMap.Store(newUrl.Key(), newInvoker)
 			}
 		} else {
-			logger.Infof("service will be updated in cache invokers: new invoker url is %s, old invoker url is %s", newUrl, cacheInvoker.(protocol.Invoker).GetUrl())
+			logger.Debugf("service will be updated in cache invokers: new invoker url is %s, old invoker url is %s", newUrl, cacheInvoker.(protocol.Invoker).GetUrl())
 			newInvoker := extension.GetProtocol(protocolwrapper.FILTER).Refer(*newUrl)
 			if newInvoker != nil {
 				dir.cacheInvokersMap.Store(newUrl.Key(), newInvoker)
-				cacheInvoker.(protocol.Invoker).Destroy()
+				return cacheInvoker.(protocol.Invoker)
 			}
 		}
 	}
+	return nil
 }
 
 //select the protocol invokers from the directory
 func (dir *registryDirectory) List(invocation protocol.Invocation) []protocol.Invoker {
-	//TODO:router
-	return dir.cacheInvokers
+	invokers := dir.cacheInvokers
+	routerChain := dir.RouterChain()
+
+	if routerChain == nil {
+		return invokers
+	}
+	return routerChain.Route(invokers, dir.cacheOriginUrl, invocation)
 }
 
 func (dir *registryDirectory) IsAvailable() bool {
 	if !dir.BaseDirectory.IsAvailable() {
 		return dir.BaseDirectory.IsAvailable()
-	} else {
-		for _, ivk := range dir.cacheInvokers {
-			if ivk.IsAvailable() {
-				return true
-			}
+	}
+
+	for _, ivk := range dir.cacheInvokers {
+		if ivk.IsAvailable() {
+			return true
 		}
 	}
+
 	return false
 }
 
 func (dir *registryDirectory) Destroy() {
 	//TODO:unregister & unsubscribe
 	dir.BaseDirectory.Destroy(func() {
-		for _, ivk := range dir.cacheInvokers {
+		invokers := dir.cacheInvokers
+		dir.cacheInvokers = []protocol.Invoker{}
+		for _, ivk := range invokers {
 			ivk.Destroy()
 		}
-		dir.cacheInvokers = []protocol.Invoker{}
 	})
 }
 
diff --git a/registry/directory/directory_test.go b/registry/directory/directory_test.go
index b3c1d35aaa66b3437ff89807fba2df0a383921cb..0dde44e73c18f65f262e01f499e198995907dece 100644
--- a/registry/directory/directory_test.go
+++ b/registry/directory/directory_test.go
@@ -18,7 +18,6 @@
 package directory
 
 import (
-	"context"
 	"net/url"
 	"strconv"
 	"testing"
@@ -31,6 +30,8 @@ import (
 
 import (
 	"github.com/apache/dubbo-go/cluster/cluster_impl"
+	_ "github.com/apache/dubbo-go/cluster/router"
+	_ "github.com/apache/dubbo-go/cluster/router/condition"
 	"github.com/apache/dubbo-go/common"
 	"github.com/apache/dubbo-go/common/constant"
 	"github.com/apache/dubbo-go/common/extension"
@@ -44,6 +45,7 @@ import (
 func init() {
 	config.SetConsumerConfig(config.ConsumerConfig{ApplicationConfig: &config.ApplicationConfig{Name: "test-application"}})
 }
+
 func TestSubscribe(t *testing.T) {
 	registryDirectory, _ := normalRegistryDir()
 
@@ -62,7 +64,7 @@ func TestSubscribe(t *testing.T) {
 //}
 
 func TestSubscribe_InvalidUrl(t *testing.T) {
-	url, _ := common.NewURL(context.TODO(), "mock://127.0.0.1:1111")
+	url, _ := common.NewURL("mock://127.0.0.1:1111")
 	mockRegistry, _ := registry.NewMockRegistry(&common.URL{})
 	_, err := NewRegistryDirectory(&url, mockRegistry)
 	assert.Error(t, err)
@@ -72,8 +74,8 @@ func TestSubscribe_Group(t *testing.T) {
 	extension.SetProtocol(protocolwrapper.FILTER, protocolwrapper.NewMockProtocolFilter)
 	extension.SetCluster("mock", cluster_impl.NewMockCluster)
 
-	regurl, _ := common.NewURL(context.TODO(), "mock://127.0.0.1:1111")
-	suburl, _ := common.NewURL(context.TODO(), "dubbo://127.0.0.1:20000")
+	regurl, _ := common.NewURL("mock://127.0.0.1:1111")
+	suburl, _ := common.NewURL("dubbo://127.0.0.1:20000")
 	suburl.SetParam(constant.CLUSTER_KEY, "mock")
 	regurl.SubURL = &suburl
 	mockRegistry, _ := registry.NewMockRegistry(&common.URL{})
@@ -105,7 +107,7 @@ func TestSubscribe_Group(t *testing.T) {
 func Test_Destroy(t *testing.T) {
 	registryDirectory, _ := normalRegistryDir()
 
-	time.Sleep(1e9)
+	time.Sleep(3e9)
 	assert.Len(t, registryDirectory.cacheInvokers, 3)
 	assert.Equal(t, true, registryDirectory.IsAvailable())
 
@@ -117,14 +119,15 @@ func Test_Destroy(t *testing.T) {
 func Test_List(t *testing.T) {
 	registryDirectory, _ := normalRegistryDir()
 
-	time.Sleep(1e9)
+	time.Sleep(4e9)
 	assert.Len(t, registryDirectory.List(&invocation.RPCInvocation{}), 3)
 	assert.Equal(t, true, registryDirectory.IsAvailable())
 
 }
+
 func Test_MergeProviderUrl(t *testing.T) {
 	registryDirectory, mockRegistry := normalRegistryDir(true)
-	providerUrl, _ := common.NewURL(context.TODO(), "dubbo://0.0.0.0:20000/org.apache.dubbo-go.mockService",
+	providerUrl, _ := common.NewURL("dubbo://0.0.0.0:20000/org.apache.dubbo-go.mockService",
 		common.WithParamsValue(constant.CLUSTER_KEY, "mock1"),
 		common.WithParamsValue(constant.GROUP_KEY, "group"),
 		common.WithParamsValue(constant.VERSION_KEY, "1.0.0"))
@@ -139,7 +142,7 @@ func Test_MergeProviderUrl(t *testing.T) {
 
 func Test_MergeOverrideUrl(t *testing.T) {
 	registryDirectory, mockRegistry := normalRegistryDir(true)
-	providerUrl, _ := common.NewURL(context.TODO(), "dubbo://0.0.0.0:20000/org.apache.dubbo-go.mockService",
+	providerUrl, _ := common.NewURL("dubbo://0.0.0.0:20000/org.apache.dubbo-go.mockService",
 		common.WithParamsValue(constant.CLUSTER_KEY, "mock"),
 		common.WithParamsValue(constant.GROUP_KEY, "group"),
 		common.WithParamsValue(constant.VERSION_KEY, "1.0.0"))
@@ -147,7 +150,7 @@ func Test_MergeOverrideUrl(t *testing.T) {
 Loop1:
 	for {
 		if len(registryDirectory.cacheInvokers) > 0 {
-			overrideUrl, _ := common.NewURL(context.TODO(), "override://0.0.0.0:20000/org.apache.dubbo-go.mockService",
+			overrideUrl, _ := common.NewURL("override://0.0.0.0:20000/org.apache.dubbo-go.mockService",
 				common.WithParamsValue(constant.CLUSTER_KEY, "mock1"),
 				common.WithParamsValue(constant.GROUP_KEY, "group"),
 				common.WithParamsValue(constant.VERSION_KEY, "1.0.0"))
@@ -173,9 +176,8 @@ Loop1:
 func normalRegistryDir(noMockEvent ...bool) (*registryDirectory, *registry.MockRegistry) {
 	extension.SetProtocol(protocolwrapper.FILTER, protocolwrapper.NewMockProtocolFilter)
 
-	url, _ := common.NewURL(context.TODO(), "mock://127.0.0.1:1111")
+	url, _ := common.NewURL("mock://127.0.0.1:1111")
 	suburl, _ := common.NewURL(
-		context.TODO(),
 		"dubbo://127.0.0.1:20000/org.apache.dubbo-go.mockService",
 		common.WithParamsValue(constant.CLUSTER_KEY, "mock"),
 		common.WithParamsValue(constant.GROUP_KEY, "group"),
diff --git a/registry/etcdv3/listener.go b/registry/etcdv3/listener.go
index 31d62fa916e5659cf424839cedf8f063fabedaa0..f9b046a2c52814cd4e5ea38f9ea4c58c8bdb5bc4 100644
--- a/registry/etcdv3/listener.go
+++ b/registry/etcdv3/listener.go
@@ -18,7 +18,6 @@
 package etcdv3
 
 import (
-	"context"
 	"strings"
 )
 
@@ -39,6 +38,7 @@ type dataListener struct {
 	listener      config_center.ConfigurationListener
 }
 
+// NewRegistryDataListener ...
 func NewRegistryDataListener(listener config_center.ConfigurationListener) *dataListener {
 	return &dataListener{listener: listener, interestedURL: []*common.URL{}}
 }
@@ -50,7 +50,7 @@ func (l *dataListener) AddInterestedURL(url *common.URL) {
 func (l *dataListener) DataChange(eventType remoting.Event) bool {
 
 	url := eventType.Path[strings.Index(eventType.Path, "/providers/")+len("/providers/"):]
-	serviceURL, err := common.NewURL(context.Background(), url)
+	serviceURL, err := common.NewURL(url)
 	if err != nil {
 		logger.Warnf("Listen NewURL(r{%s}) = error{%v}", eventType.Path, err)
 		return false
@@ -77,11 +77,13 @@ type configurationListener struct {
 	events   chan *config_center.ConfigChangeEvent
 }
 
+// NewConfigurationListener for listening the event of etcdv3.
 func NewConfigurationListener(reg *etcdV3Registry) *configurationListener {
 	// add a new waiter
-	reg.wg.Add(1)
+	reg.WaitGroup().Add(1)
 	return &configurationListener{registry: reg, events: make(chan *config_center.ConfigChangeEvent, 32)}
 }
+
 func (l *configurationListener) Process(configType *config_center.ConfigChangeEvent) {
 	l.events <- configType
 }
@@ -89,7 +91,7 @@ func (l *configurationListener) Process(configType *config_center.ConfigChangeEv
 func (l *configurationListener) Next() (*registry.ServiceEvent, error) {
 	for {
 		select {
-		case <-l.registry.done:
+		case <-l.registry.Done():
 			logger.Warnf("listener's etcd client connection is broken, so etcd event listener exit now.")
 			return nil, perrors.New("listener stopped")
 
@@ -97,7 +99,7 @@ func (l *configurationListener) Next() (*registry.ServiceEvent, error) {
 			logger.Infof("got etcd event %#v", e)
 			if e.ConfigType == remoting.EventTypeDel {
 				select {
-				case <-l.registry.done:
+				case <-l.registry.Done():
 					logger.Warnf("update @result{%s}. But its connection to registry is invalid", e.Value)
 				default:
 				}
@@ -107,6 +109,7 @@ func (l *configurationListener) Next() (*registry.ServiceEvent, error) {
 		}
 	}
 }
+
 func (l *configurationListener) Close() {
-	l.registry.wg.Done()
+	l.registry.WaitGroup().Done()
 }
diff --git a/registry/etcdv3/listener_test.go b/registry/etcdv3/listener_test.go
index 842a216b94a3ae9092afba370aed154e0925c013..5f5d95aa3644fa201c10c510239a698b90b90847 100644
--- a/registry/etcdv3/listener_test.go
+++ b/registry/etcdv3/listener_test.go
@@ -18,7 +18,6 @@
 package etcdv3
 
 import (
-	"context"
 	"os/exec"
 	"testing"
 	"time"
@@ -81,7 +80,7 @@ func (suite *RegistryTestSuite) TestDataChange() {
 	t := suite.T()
 
 	listener := NewRegistryDataListener(&MockDataListener{})
-	url, _ := common.NewURL(context.Background(), "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-2.6.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")
+	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-2.6.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)
 	if !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-2.6.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"}) {
 		t.Fatal("data change not ok")
diff --git a/registry/etcdv3/registry.go b/registry/etcdv3/registry.go
index 68ad456ca1c531a562abb1252c696844d475de12..e1c25768119ea7d7122b9aa22a5f881db44bafd9 100644
--- a/registry/etcdv3/registry.go
+++ b/registry/etcdv3/registry.go
@@ -19,17 +19,13 @@ package etcdv3
 
 import (
 	"fmt"
-	"net/url"
-	"os"
 	"path"
-	"strconv"
 	"strings"
 	"sync"
 	"time"
 )
 
 import (
-	gxnet "github.com/dubbogo/gost/net"
 	perrors "github.com/pkg/errors"
 )
 
@@ -42,72 +38,39 @@ import (
 	"github.com/apache/dubbo-go/remoting/etcdv3"
 )
 
-var (
-	processID = ""
-	localIP   = ""
-)
-
 const (
-	Name              = "etcdv3"
-	RegistryConnDelay = 3
+	// Name module name
+	Name = "etcdv3"
 )
 
 func init() {
-	processID = fmt.Sprintf("%d", os.Getpid())
-	localIP, _ = gxnet.GetLocalIP()
 	extension.SetRegistry(Name, newETCDV3Registry)
 }
 
 type etcdV3Registry struct {
-	*common.URL
-	birth int64 // time of file birth, seconds since Epoch; 0 if unknown
-
-	cltLock  sync.Mutex
-	client   *etcdv3.Client
-	services map[string]common.URL // service name + protocol -> service config
-
+	registry.BaseRegistry
+	cltLock        sync.Mutex
+	client         *etcdv3.Client
 	listenerLock   sync.Mutex
 	listener       *etcdv3.EventListener
 	dataListener   *dataListener
 	configListener *configurationListener
-
-	wg   sync.WaitGroup // wg+done for etcd client restart
-	done chan struct{}
 }
 
+// Client get the etcdv3 client
 func (r *etcdV3Registry) Client() *etcdv3.Client {
 	return r.client
 }
+
+//SetClient set the etcdv3 client
 func (r *etcdV3Registry) SetClient(client *etcdv3.Client) {
 	r.client = client
 }
+
+//
 func (r *etcdV3Registry) ClientLock() *sync.Mutex {
 	return &r.cltLock
 }
-func (r *etcdV3Registry) WaitGroup() *sync.WaitGroup {
-	return &r.wg
-}
-func (r *etcdV3Registry) GetDone() chan struct{} {
-	return r.done
-}
-func (r *etcdV3Registry) RestartCallBack() bool {
-
-	services := []common.URL{}
-	for _, confIf := range r.services {
-		services = append(services, confIf)
-	}
-
-	for _, confIf := range services {
-		err := r.Register(confIf)
-		if err != nil {
-			logger.Errorf("(etcdV3ProviderRegistry)register(conf{%#v}) = error{%#v}",
-				confIf, perrors.WithStack(err))
-			return false
-		}
-		logger.Infof("success to re-register service :%v", confIf.Key())
-	}
-	return true
-}
 
 func newETCDV3Registry(url *common.URL) (registry.Registry, error) {
 
@@ -120,12 +83,9 @@ func newETCDV3Registry(url *common.URL) (registry.Registry, error) {
 
 	logger.Infof("etcd address is: %v, timeout is: %s", url.Location, timeout.String())
 
-	r := &etcdV3Registry{
-		URL:      url,
-		birth:    time.Now().UnixNano(),
-		done:     make(chan struct{}),
-		services: make(map[string]common.URL),
-	}
+	r := &etcdV3Registry{}
+
+	r.InitBaseRegistry(url, r)
 
 	if err := etcdv3.ValidateClient(
 		r,
@@ -135,89 +95,37 @@ func newETCDV3Registry(url *common.URL) (registry.Registry, error) {
 	); err != nil {
 		return nil, err
 	}
+	r.WaitGroup().Add(1) //etcdv3 client start successful, then wg +1
 
-	r.wg.Add(1)
 	go etcdv3.HandleClientRestart(r)
 
-	r.listener = etcdv3.NewEventListener(r.client)
-	r.configListener = NewConfigurationListener(r)
-	r.dataListener = NewRegistryDataListener(r.configListener)
+	r.InitListeners()
 
 	return r, nil
 }
 
-func (r *etcdV3Registry) GetUrl() common.URL {
-	return *r.URL
-}
-
-func (r *etcdV3Registry) IsAvailable() bool {
-
-	select {
-	case <-r.done:
-		return false
-	default:
-		return true
-	}
+func (r *etcdV3Registry) InitListeners() {
+	r.listener = etcdv3.NewEventListener(r.client)
+	r.configListener = NewConfigurationListener(r)
+	r.dataListener = NewRegistryDataListener(r.configListener)
 }
 
-func (r *etcdV3Registry) Destroy() {
-
-	if r.configListener != nil {
-		r.configListener.Close()
-	}
-	r.stop()
+func (r *etcdV3Registry) DoRegister(root string, node string) error {
+	return r.client.Create(path.Join(root, node), "")
 }
 
-func (r *etcdV3Registry) stop() {
-
-	close(r.done)
-
-	// close current client
+func (r *etcdV3Registry) CloseAndNilClient() {
 	r.client.Close()
-
-	r.cltLock.Lock()
 	r.client = nil
-	r.services = nil
-	r.cltLock.Unlock()
 }
 
-func (r *etcdV3Registry) Register(svc common.URL) error {
-
-	role, err := strconv.Atoi(r.URL.GetParam(constant.ROLE_KEY, ""))
-	if err != nil {
-		return perrors.WithMessage(err, "get registry role")
-	}
-
-	r.cltLock.Lock()
-	if _, ok := r.services[svc.Key()]; ok {
-		r.cltLock.Unlock()
-		return perrors.New(fmt.Sprintf("Path{%s} has been registered", svc.Path))
-	}
-	r.cltLock.Unlock()
-
-	switch role {
-	case common.PROVIDER:
-		logger.Debugf("(provider register )Register(conf{%#v})", svc)
-		if err := r.registerProvider(svc); err != nil {
-			return perrors.WithMessage(err, "register provider")
-		}
-	case common.CONSUMER:
-		logger.Debugf("(consumer register )Register(conf{%#v})", svc)
-		if err := r.registerConsumer(svc); err != nil {
-			return perrors.WithMessage(err, "register consumer")
-		}
-	default:
-		return perrors.New(fmt.Sprintf("unknown role %d", role))
+func (r *etcdV3Registry) CloseListener() {
+	if r.configListener != nil {
+		r.configListener.Close()
 	}
-
-	r.cltLock.Lock()
-	r.services[svc.Key()] = svc
-	r.cltLock.Unlock()
-	return nil
 }
 
-func (r *etcdV3Registry) createDirIfNotExist(k string) error {
-
+func (r *etcdV3Registry) CreatePath(k string) error {
 	var tmpPath string
 	for _, str := range strings.Split(k, "/")[1:] {
 		tmpPath = path.Join(tmpPath, "/", str)
@@ -229,85 +137,7 @@ func (r *etcdV3Registry) createDirIfNotExist(k string) error {
 	return nil
 }
 
-func (r *etcdV3Registry) registerConsumer(svc common.URL) error {
-
-	consumersNode := fmt.Sprintf("/dubbo/%s/%s", svc.Service(), common.DubboNodes[common.CONSUMER])
-	if err := r.createDirIfNotExist(consumersNode); err != nil {
-		logger.Errorf("etcd client create path %s: %v", consumersNode, err)
-		return perrors.WithMessage(err, "etcd create consumer nodes")
-	}
-	providersNode := fmt.Sprintf("/dubbo/%s/%s", svc.Service(), common.DubboNodes[common.PROVIDER])
-	if err := r.createDirIfNotExist(providersNode); err != nil {
-		return perrors.WithMessage(err, "create provider node")
-	}
-
-	params := url.Values{}
-
-	params.Add("protocol", svc.Protocol)
-
-	params.Add("category", (common.RoleType(common.CONSUMER)).String())
-	params.Add("dubbo", "dubbogo-consumer-"+constant.Version)
-
-	encodedURL := url.QueryEscape(fmt.Sprintf("consumer://%s%s?%s", localIP, svc.Path, params.Encode()))
-	dubboPath := fmt.Sprintf("/dubbo/%s/%s", svc.Service(), (common.RoleType(common.CONSUMER)).String())
-	if err := r.client.Create(path.Join(dubboPath, encodedURL), ""); err != nil {
-		return perrors.WithMessagef(err, "create k/v in etcd (path:%s, url:%s)", dubboPath, encodedURL)
-	}
-
-	return nil
-}
-
-func (r *etcdV3Registry) registerProvider(svc common.URL) error {
-
-	if len(svc.Path) == 0 || len(svc.Methods) == 0 {
-		return perrors.New(fmt.Sprintf("service path %s or service method %s", svc.Path, svc.Methods))
-	}
-
-	var (
-		urlPath    string
-		encodedURL string
-		dubboPath  string
-	)
-
-	providersNode := fmt.Sprintf("/dubbo/%s/%s", svc.Service(), common.DubboNodes[common.PROVIDER])
-	if err := r.createDirIfNotExist(providersNode); err != nil {
-		return perrors.WithMessage(err, "create provider node")
-	}
-
-	params := url.Values{}
-
-	svc.RangeParams(func(key, value string) bool {
-		params[key] = []string{value}
-		return true
-	})
-	params.Add("pid", processID)
-	params.Add("ip", localIP)
-	params.Add("anyhost", "true")
-	params.Add("category", (common.RoleType(common.PROVIDER)).String())
-	params.Add("dubbo", "dubbo-provider-golang-"+constant.Version)
-	params.Add("side", (common.RoleType(common.PROVIDER)).Role())
-
-	logger.Debugf("provider url params:%#v", params)
-	var host string
-	if len(svc.Ip) == 0 {
-		host = localIP + ":" + svc.Port
-	} else {
-		host = svc.Ip + ":" + svc.Port
-	}
-
-	urlPath = svc.Path
-
-	encodedURL = url.QueryEscape(fmt.Sprintf("%s://%s%s?%s", svc.Protocol, host, urlPath, params.Encode()))
-	dubboPath = fmt.Sprintf("/dubbo/%s/%s", svc.Service(), (common.RoleType(common.PROVIDER)).String())
-
-	if err := r.client.Create(path.Join(dubboPath, encodedURL), ""); err != nil {
-		return perrors.WithMessagef(err, "create k/v in etcd (path:%s, url:%s)", dubboPath, encodedURL)
-	}
-
-	return nil
-}
-
-func (r *etcdV3Registry) subscribe(svc *common.URL) (registry.Listener, error) {
+func (r *etcdV3Registry) DoSubscribe(svc *common.URL) (registry.Listener, error) {
 
 	var (
 		configListener *configurationListener
@@ -328,12 +158,7 @@ func (r *etcdV3Registry) subscribe(svc *common.URL) (registry.Listener, error) {
 		listener := etcdv3.NewEventListener(r.client)
 
 		r.listenerLock.Lock()
-		// NOTICE:
-		// double-check the listener
-		// if r.listener already be assigned, discard the new value
-		if r.listener == nil {
-			r.listener = listener
-		}
+		r.listener = listener
 		r.listenerLock.Unlock()
 	}
 
@@ -345,37 +170,3 @@ func (r *etcdV3Registry) subscribe(svc *common.URL) (registry.Listener, error) {
 
 	return configListener, nil
 }
-
-//subscribe from registry
-func (r *etcdV3Registry) Subscribe(url *common.URL, notifyListener registry.NotifyListener) {
-	for {
-		if !r.IsAvailable() {
-			logger.Warnf("event listener game over.")
-			return
-		}
-
-		listener, err := r.subscribe(url)
-		if err != nil {
-			if !r.IsAvailable() {
-				logger.Warnf("event listener game over.")
-				return
-			}
-			logger.Warnf("getListener() = err:%v", perrors.WithStack(err))
-			time.Sleep(time.Duration(RegistryConnDelay) * time.Second)
-			continue
-		}
-
-		for {
-			if serviceEvent, err := listener.Next(); err != nil {
-				logger.Warnf("Selector.watch() = error{%v}", perrors.WithStack(err))
-				listener.Close()
-				return
-			} else {
-				logger.Infof("update begin, service event: %v", serviceEvent.String())
-				notifyListener.Notify(serviceEvent)
-			}
-
-		}
-
-	}
-}
diff --git a/registry/etcdv3/registry_test.go b/registry/etcdv3/registry_test.go
index 3f8c0f4cfccc2bcc68fc1e55fa69d74e9f0f8c0f..6e26a8f3fcbbf50592520a44b253e5abbaedb061 100644
--- a/registry/etcdv3/registry_test.go
+++ b/registry/etcdv3/registry_test.go
@@ -18,7 +18,6 @@
 package etcdv3
 
 import (
-	"context"
 	"strconv"
 	"testing"
 	"time"
@@ -35,7 +34,7 @@ import (
 
 func initRegistry(t *testing.T) *etcdV3Registry {
 
-	regurl, err := common.NewURL(context.Background(), "registry://127.0.0.1:2379", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER)))
+	regurl, err := common.NewURL("registry://127.0.0.1:2379", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER)))
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -46,7 +45,8 @@ func initRegistry(t *testing.T) *etcdV3Registry {
 	}
 
 	out := reg.(*etcdV3Registry)
-	out.client.CleanKV()
+	err = out.client.CleanKV()
+	assert.NoError(t, err)
 	return out
 }
 
@@ -54,23 +54,24 @@ func (suite *RegistryTestSuite) TestRegister() {
 
 	t := suite.T()
 
-	url, _ := common.NewURL(context.Background(), "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParamsValue(constant.CLUSTER_KEY, "mock"), common.WithMethods([]string{"GetUser", "AddUser"}))
+	url, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParamsValue(constant.CLUSTER_KEY, "mock"), common.WithMethods([]string{"GetUser", "AddUser"}))
 
 	reg := initRegistry(t)
 	err := reg.Register(url)
+	assert.NoError(t, err)
 	children, _, err := reg.client.GetChildrenKVList("/dubbo/com.ikurento.user.UserProvider/providers")
 	if err != nil {
 		t.Fatal(err)
 	}
-	assert.Regexp(t, ".*dubbo%3A%2F%2F127.0.0.1%3A20000%2Fcom.ikurento.user.UserProvider%3Fanyhost%3Dtrue%26category%3Dproviders%26cluster%3Dmock%26dubbo%3Ddubbo-provider-golang-2.6.0%26.*provider", children)
+	assert.Regexp(t, ".*dubbo%3A%2F%2F127.0.0.1%3A20000%2Fcom.ikurento.user.UserProvider%3Fanyhost%3Dtrue%26category%3Dproviders%26cluster%3Dmock%26dubbo%3Ddubbo-provider-golang-1.3.0%26.*provider", children)
 	assert.NoError(t, err)
 }
 
 func (suite *RegistryTestSuite) TestSubscribe() {
 
 	t := suite.T()
-	regurl, _ := common.NewURL(context.Background(), "registry://127.0.0.1:1111", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER)))
-	url, _ := common.NewURL(context.Background(), "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParamsValue(constant.CLUSTER_KEY, "mock"), common.WithMethods([]string{"GetUser", "AddUser"}))
+	regurl, _ := common.NewURL("registry://127.0.0.1:1111", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER)))
+	url, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParamsValue(constant.CLUSTER_KEY, "mock"), common.WithMethods([]string{"GetUser", "AddUser"}))
 
 	reg := initRegistry(t)
 	//provider register
@@ -83,8 +84,9 @@ func (suite *RegistryTestSuite) TestSubscribe() {
 	regurl.SetParam(constant.ROLE_KEY, strconv.Itoa(common.CONSUMER))
 	reg2 := initRegistry(t)
 
-	reg2.Register(url)
-	listener, err := reg2.subscribe(&url)
+	err = reg2.Register(url)
+	assert.NoError(t, err)
+	listener, err := reg2.DoSubscribe(&url)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -99,10 +101,10 @@ func (suite *RegistryTestSuite) TestSubscribe() {
 func (suite *RegistryTestSuite) TestConsumerDestory() {
 
 	t := suite.T()
-	url, _ := common.NewURL(context.Background(), "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParamsValue(constant.CLUSTER_KEY, "mock"), common.WithMethods([]string{"GetUser", "AddUser"}))
+	url, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParamsValue(constant.CLUSTER_KEY, "mock"), common.WithMethods([]string{"GetUser", "AddUser"}))
 
 	reg := initRegistry(t)
-	_, err := reg.subscribe(&url)
+	_, err := reg.DoSubscribe(&url)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -119,8 +121,9 @@ func (suite *RegistryTestSuite) TestProviderDestory() {
 
 	t := suite.T()
 	reg := initRegistry(t)
-	url, _ := common.NewURL(context.Background(), "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParamsValue(constant.CLUSTER_KEY, "mock"), common.WithMethods([]string{"GetUser", "AddUser"}))
-	reg.Register(url)
+	url, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParamsValue(constant.CLUSTER_KEY, "mock"), common.WithMethods([]string{"GetUser", "AddUser"}))
+	err := reg.Register(url)
+	assert.NoError(t, err)
 
 	//listener.Close()
 	time.Sleep(1e9)
diff --git a/registry/event.go b/registry/event.go
index 24f5b72e8b27d4dc727e72d641d8bae3e00ff165..37d863d2162cb3b9d6a9f7eba8823286eb99441c 100644
--- a/registry/event.go
+++ b/registry/event.go
@@ -36,6 +36,7 @@ func init() {
 // service event
 //////////////////////////////////////////
 
+// ServiceEvent ...
 type ServiceEvent struct {
 	Action  remoting.EventType
 	Service common.URL
diff --git a/registry/mock_registry.go b/registry/mock_registry.go
index 512c452e39082d619ffceae7f82d28127fbe2975..9591928eebd22bf2a99ec9dcfeb285c4519a3b90 100644
--- a/registry/mock_registry.go
+++ b/registry/mock_registry.go
@@ -30,11 +30,13 @@ import (
 	"github.com/apache/dubbo-go/common/logger"
 )
 
+// MockRegistry ...
 type MockRegistry struct {
 	listener  *listener
 	destroyed *atomic.Bool
 }
 
+// NewMockRegistry ...
 func NewMockRegistry(url *common.URL) (Registry, error) {
 	registry := &MockRegistry{
 		destroyed: atomic.NewBool(false),
@@ -43,17 +45,24 @@ func NewMockRegistry(url *common.URL) (Registry, error) {
 	registry.listener = listener
 	return registry, nil
 }
+
+// Register ...
 func (*MockRegistry) Register(url common.URL) error {
 	return nil
 }
 
+// Destroy ...
 func (r *MockRegistry) Destroy() {
 	if r.destroyed.CAS(false, true) {
 	}
 }
+
+// IsAvailable ...
 func (r *MockRegistry) IsAvailable() bool {
 	return !r.destroyed.Load()
 }
+
+// GetUrl ...
 func (r *MockRegistry) GetUrl() common.URL {
 	return common.URL{}
 }
@@ -61,6 +70,8 @@ func (r *MockRegistry) GetUrl() common.URL {
 func (r *MockRegistry) subscribe(*common.URL) (Listener, error) {
 	return r.listener, nil
 }
+
+// Subscribe ...
 func (r *MockRegistry) Subscribe(url *common.URL, notifyListener NotifyListener) {
 	go func() {
 		for {
@@ -81,17 +92,16 @@ func (r *MockRegistry) Subscribe(url *common.URL, notifyListener NotifyListener)
 			}
 
 			for {
-				if serviceEvent, err := listener.Next(); err != nil {
+				serviceEvent, err := listener.Next()
+				if err != nil {
 					listener.Close()
 					time.Sleep(time.Duration(3) * time.Second)
 					return
-				} else {
-					logger.Infof("update begin, service event: %v", serviceEvent.String())
-					notifyListener.Notify(serviceEvent)
 				}
 
+				logger.Infof("update begin, service event: %v", serviceEvent.String())
+				notifyListener.Notify(serviceEvent)
 			}
-
 		}
 	}()
 }
@@ -113,6 +123,7 @@ func (*listener) Close() {
 
 }
 
+// MockEvent ...
 func (r *MockRegistry) MockEvent(event *ServiceEvent) {
 	r.listener.listenChan <- event
 }
diff --git a/registry/nacos/listener.go b/registry/nacos/listener.go
index 25cd3d09b5711e4e7db56cd8e40f3283f3252e10..a2237dca265f25b07b19a8e1f4fe5a5f6ea9183e 100644
--- a/registry/nacos/listener.go
+++ b/registry/nacos/listener.go
@@ -51,6 +51,7 @@ type nacosListener struct {
 	subscribeParam *vo.SubscribeParam
 }
 
+// NewNacosListener ...
 func NewNacosListener(url common.URL, namingClient naming_client.INamingClient) (*nacosListener, error) {
 	listener := &nacosListener{
 		namingClient: namingClient,
diff --git a/registry/nacos/registry.go b/registry/nacos/registry.go
index a8b9fa83fa73858064e570722341c14f974f5c9e..965e91e894ac61562bfd25c8f564f789afd6c8a1 100644
--- a/registry/nacos/registry.go
+++ b/registry/nacos/registry.go
@@ -47,6 +47,7 @@ var (
 )
 
 const (
+	//RegistryConnDelay registry connection delay
 	RegistryConnDelay = 3
 )
 
@@ -209,15 +210,15 @@ func (nr *nacosRegistry) Subscribe(url *common.URL, notifyListener registry.Noti
 		}
 
 		for {
-			if serviceEvent, err := listener.Next(); err != nil {
+			serviceEvent, err := listener.Next()
+			if err != nil {
 				logger.Warnf("Selector.watch() = error{%v}", perrors.WithStack(err))
 				listener.Close()
 				return
-			} else {
-				logger.Infof("update begin, service event: %v", serviceEvent.String())
-				notifyListener.Notify(serviceEvent)
 			}
 
+			logger.Infof("update begin, service event: %v", serviceEvent.String())
+			notifyListener.Notify(serviceEvent)
 		}
 
 	}
diff --git a/registry/nacos/registry_test.go b/registry/nacos/registry_test.go
index e6ab693cd3f5432fe30c2b83011cd56e44ac509f..7475b455c0dda09da65012465711ece264bb3dd5 100644
--- a/registry/nacos/registry_test.go
+++ b/registry/nacos/registry_test.go
@@ -18,7 +18,6 @@
 package nacos
 
 import (
-	"context"
 	"encoding/json"
 	"net/url"
 	"strconv"
@@ -36,14 +35,14 @@ import (
 )
 
 func TestNacosRegistry_Register(t *testing.T) {
-	regurl, _ := common.NewURL(context.TODO(), "registry://console.nacos.io:80", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER)))
+	regurl, _ := common.NewURL("registry://console.nacos.io:80", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER)))
 	urlMap := url.Values{}
 	urlMap.Set(constant.GROUP_KEY, "guangzhou-idc")
 	urlMap.Set(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER))
 	urlMap.Set(constant.INTERFACE_KEY, "com.ikurento.user.UserProvider")
 	urlMap.Set(constant.VERSION_KEY, "1.0.0")
 	urlMap.Set(constant.CLUSTER_KEY, "mock")
-	url, _ := common.NewURL(context.TODO(), "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParams(urlMap), common.WithMethods([]string{"GetUser", "AddUser"}))
+	url, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParams(urlMap), common.WithMethods([]string{"GetUser", "AddUser"}))
 
 	reg, err := newNacosRegistry(&regurl)
 	assert.Nil(t, err)
@@ -65,7 +64,7 @@ func TestNacosRegistry_Register(t *testing.T) {
 }
 
 func TestNacosRegistry_Subscribe(t *testing.T) {
-	regurl, _ := common.NewURL(context.TODO(), "registry://console.nacos.io:80", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER)))
+	regurl, _ := common.NewURL("registry://console.nacos.io:80", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER)))
 	urlMap := url.Values{}
 	urlMap.Set(constant.GROUP_KEY, "guangzhou-idc")
 	urlMap.Set(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER))
@@ -73,7 +72,7 @@ func TestNacosRegistry_Subscribe(t *testing.T) {
 	urlMap.Set(constant.VERSION_KEY, "1.0.0")
 	urlMap.Set(constant.CLUSTER_KEY, "mock")
 	urlMap.Set(constant.NACOS_PATH_KEY, "")
-	url, _ := common.NewURL(context.TODO(), "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParams(urlMap), common.WithMethods([]string{"GetUser", "AddUser"}))
+	url, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParams(urlMap), common.WithMethods([]string{"GetUser", "AddUser"}))
 
 	reg, _ := newNacosRegistry(&regurl)
 	err := reg.Register(url)
@@ -103,7 +102,7 @@ func TestNacosRegistry_Subscribe(t *testing.T) {
 }
 
 func TestNacosRegistry_Subscribe_del(t *testing.T) {
-	regurl, _ := common.NewURL(context.TODO(), "registry://console.nacos.io:80", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER)))
+	regurl, _ := common.NewURL("registry://console.nacos.io:80", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER)))
 	urlMap := url.Values{}
 	urlMap.Set(constant.GROUP_KEY, "guangzhou-idc")
 	urlMap.Set(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER))
@@ -111,8 +110,8 @@ func TestNacosRegistry_Subscribe_del(t *testing.T) {
 	urlMap.Set(constant.VERSION_KEY, "2.0.0")
 	urlMap.Set(constant.CLUSTER_KEY, "mock")
 	urlMap.Set(constant.NACOS_PATH_KEY, "")
-	url1, _ := common.NewURL(context.TODO(), "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParams(urlMap), common.WithMethods([]string{"GetUser", "AddUser"}))
-	url2, _ := common.NewURL(context.TODO(), "dubbo://127.0.0.2:20000/com.ikurento.user.UserProvider", common.WithParams(urlMap), common.WithMethods([]string{"GetUser", "AddUser"}))
+	url1, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParams(urlMap), common.WithMethods([]string{"GetUser", "AddUser"}))
+	url2, _ := common.NewURL("dubbo://127.0.0.2:20000/com.ikurento.user.UserProvider", common.WithParams(urlMap), common.WithMethods([]string{"GetUser", "AddUser"}))
 
 	reg, _ := newNacosRegistry(&regurl)
 	err := reg.Register(url1)
@@ -169,7 +168,7 @@ func TestNacosRegistry_Subscribe_del(t *testing.T) {
 }
 
 func TestNacosListener_Close(t *testing.T) {
-	regurl, _ := common.NewURL(context.TODO(), "registry://console.nacos.io:80", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER)))
+	regurl, _ := common.NewURL("registry://console.nacos.io:80", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER)))
 	urlMap := url.Values{}
 	urlMap.Set(constant.GROUP_KEY, "guangzhou-idc")
 	urlMap.Set(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER))
@@ -177,7 +176,7 @@ func TestNacosListener_Close(t *testing.T) {
 	urlMap.Set(constant.VERSION_KEY, "1.0.0")
 	urlMap.Set(constant.CLUSTER_KEY, "mock")
 	urlMap.Set(constant.NACOS_PATH_KEY, "")
-	url1, _ := common.NewURL(context.TODO(), "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider2", common.WithParams(urlMap), common.WithMethods([]string{"GetUser", "AddUser"}))
+	url1, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider2", common.WithParams(urlMap), common.WithMethods([]string{"GetUser", "AddUser"}))
 	reg, _ := newNacosRegistry(&regurl)
 	listener, err := reg.(*nacosRegistry).subscribe(&url1)
 	assert.Nil(t, err)
diff --git a/registry/protocol/protocol.go b/registry/protocol/protocol.go
index 534a4b945965f332e49ff343557fa20355921454..a7678ba4e2f38cfeb77f202103e03066a7efdbef 100644
--- a/registry/protocol/protocol.go
+++ b/registry/protocol/protocol.go
@@ -18,12 +18,13 @@
 package protocol
 
 import (
+	"context"
 	"strings"
 	"sync"
 )
 
 import (
-	"github.com/dubbogo/gost/container/gxset"
+	gxset "github.com/dubbogo/gost/container/set"
 )
 
 import (
@@ -76,6 +77,7 @@ func newRegistryProtocol() *registryProtocol {
 		bounds:     &sync.Map{},
 	}
 }
+
 func getRegistry(regUrl *common.URL) registry.Registry {
 	reg, err := extension.GetRegistry(regUrl.Protocol, regUrl)
 	if err != nil {
@@ -84,13 +86,14 @@ func getRegistry(regUrl *common.URL) registry.Registry {
 	}
 	return reg
 }
+
 func (proto *registryProtocol) initConfigurationListeners() {
 	proto.overrideListeners = &sync.Map{}
 	proto.serviceConfigurationListeners = &sync.Map{}
 	proto.providerConfigurationListener = newProviderConfigurationListener(proto.overrideListeners)
 }
-func (proto *registryProtocol) Refer(url common.URL) protocol.Invoker {
 
+func (proto *registryProtocol) Refer(url common.URL) protocol.Invoker {
 	var registryUrl = url
 	var serviceUrl = registryUrl.SubURL
 	if registryUrl.Protocol == constant.REGISTRY_PROTOCOL {
@@ -114,6 +117,7 @@ func (proto *registryProtocol) Refer(url common.URL) protocol.Invoker {
 			serviceUrl.String(), err.Error())
 		return nil
 	}
+
 	err = reg.Register(*serviceUrl)
 	if err != nil {
 		logger.Errorf("consumer service %v register registry %v error, error message is %s",
@@ -130,7 +134,6 @@ func (proto *registryProtocol) Refer(url common.URL) protocol.Invoker {
 }
 
 func (proto *registryProtocol) Export(invoker protocol.Invoker) protocol.Exporter {
-
 	proto.once.Do(func() {
 		proto.initConfigurationListeners()
 	})
@@ -171,13 +174,14 @@ func (proto *registryProtocol) Export(invoker protocol.Invoker) protocol.Exporte
 		wrappedInvoker := newWrappedInvoker(invoker, providerUrl)
 		cachedExporter = extension.GetProtocol(protocolwrapper.FILTER).Export(wrappedInvoker)
 		proto.bounds.Store(key, cachedExporter)
-		logger.Infof("The exporter has not been cached, and will return a new  exporter!")
+		logger.Infof("The exporter has not been cached, and will return a new exporter!")
 	}
 
 	go reg.Subscribe(overriderUrl, overrideSubscribeListener)
 	return cachedExporter.(protocol.Exporter)
 
 }
+
 func (proto *registryProtocol) reExport(invoker protocol.Invoker, newUrl *common.URL) {
 	url := getProviderUrl(invoker)
 	key := getCacheKey(url)
@@ -201,12 +205,14 @@ type overrideSubscribeListener struct {
 func newOverrideSubscribeListener(overriderUrl *common.URL, invoker protocol.Invoker, proto *registryProtocol) *overrideSubscribeListener {
 	return &overrideSubscribeListener{url: overriderUrl, originInvoker: invoker, protocol: proto}
 }
+
 func (nl *overrideSubscribeListener) Notify(event *registry.ServiceEvent) {
 	if isMatched(&(event.Service), nl.url) && event.Action == remoting.EventTypeAdd {
 		nl.configurator = extension.GetDefaultConfigurator(&(event.Service))
 		nl.doOverrideIfNecessary()
 	}
 }
+
 func (nl *overrideSubscribeListener) doOverrideIfNecessary() {
 	providerUrl := getProviderUrl(nl.originInvoker)
 	key := getCacheKey(providerUrl)
@@ -275,6 +281,7 @@ func isMatched(providerUrl *common.URL, consumerUrl *common.URL) bool {
 		consumerVersion == providerVersion) && (len(consumerClassifier) == 0 ||
 		consumerClassifier == constant.ANY_VALUE || consumerClassifier == providerClassifier)
 }
+
 func isMatchCategory(category string, categories string) bool {
 	if len(categories) == 0 {
 		return category == constant.DEFAULT_CATEGORY
@@ -286,6 +293,7 @@ func isMatchCategory(category string, categories string) bool {
 		return strings.Contains(categories, category)
 	}
 }
+
 func getSubscribedOverrideUrl(providerUrl *common.URL) *common.URL {
 	newUrl := providerUrl.Clone()
 	newUrl.Protocol = constant.PROVIDER_PROTOCOL
@@ -333,10 +341,12 @@ func getProviderUrl(invoker protocol.Invoker) *common.URL {
 	//be careful params maps in url is map type
 	return url.SubURL.Clone()
 }
+
 func setProviderUrl(regURL *common.URL, providerURL *common.URL) {
 	regURL.SubURL = providerURL
 }
 
+// GetProtocol ...
 func GetProtocol() protocol.Protocol {
 	if regProtocol != nil {
 		return regProtocol
@@ -356,10 +366,10 @@ func newWrappedInvoker(invoker protocol.Invoker, url *common.URL) *wrappedInvoke
 	}
 }
 
-func (ivk *wrappedInvoker) Invoke(invocation protocol.Invocation) protocol.Result {
+func (ivk *wrappedInvoker) Invoke(ctx context.Context, invocation protocol.Invocation) protocol.Result {
 	// get right url
 	ivk.invoker.(*proxy_factory.ProxyInvoker).BaseInvoker = *protocol.NewBaseInvoker(ivk.GetUrl())
-	return ivk.invoker.Invoke(invocation)
+	return ivk.invoker.Invoke(ctx, invocation)
 }
 
 type providerConfigurationListener struct {
diff --git a/registry/protocol/protocol_test.go b/registry/protocol/protocol_test.go
index 0c19da59df6e4fd2f663f9e8d541165fe26c3ffa..cee2a6a625368f655d1b9bc5fe8cc37031e1aef7 100644
--- a/registry/protocol/protocol_test.go
+++ b/registry/protocol/protocol_test.go
@@ -18,7 +18,6 @@
 package protocol
 
 import (
-	"context"
 	"testing"
 	"time"
 )
@@ -45,15 +44,15 @@ import (
 func init() {
 	config.SetProviderConfig(config.ProviderConfig{ApplicationConfig: &config.ApplicationConfig{Name: "test-application"}})
 }
+
 func referNormal(t *testing.T, regProtocol *registryProtocol) {
 	extension.SetProtocol("registry", GetProtocol)
 	extension.SetRegistry("mock", registry.NewMockRegistry)
 	extension.SetProtocol(protocolwrapper.FILTER, protocolwrapper.NewMockProtocolFilter)
 	extension.SetCluster("mock", cluster.NewMockCluster)
 
-	url, _ := common.NewURL(context.TODO(), "mock://127.0.0.1:1111")
+	url, _ := common.NewURL("mock://127.0.0.1:1111")
 	suburl, _ := common.NewURL(
-		context.TODO(),
 		"dubbo://127.0.0.1:20000//",
 		common.WithParamsValue(constant.CLUSTER_KEY, "mock"),
 	)
@@ -76,9 +75,8 @@ func TestRefer(t *testing.T) {
 func TestMultiRegRefer(t *testing.T) {
 	regProtocol := newRegistryProtocol()
 	referNormal(t, regProtocol)
-	url2, _ := common.NewURL(context.TODO(), "mock://127.0.0.1:2222")
+	url2, _ := common.NewURL("mock://127.0.0.1:2222")
 	suburl2, _ := common.NewURL(
-		context.TODO(),
 		"dubbo://127.0.0.1:20000//",
 		common.WithParamsValue(constant.CLUSTER_KEY, "mock"),
 	)
@@ -98,9 +96,8 @@ func TestOneRegRefer(t *testing.T) {
 	regProtocol := newRegistryProtocol()
 	referNormal(t, regProtocol)
 
-	url2, _ := common.NewURL(context.TODO(), "mock://127.0.0.1:1111")
+	url2, _ := common.NewURL("mock://127.0.0.1:1111")
 	suburl2, _ := common.NewURL(
-		context.TODO(),
 		"dubbo://127.0.0.1:20000//",
 		common.WithParamsValue(constant.CLUSTER_KEY, "mock"),
 	)
@@ -120,9 +117,8 @@ func exporterNormal(t *testing.T, regProtocol *registryProtocol) *common.URL {
 	extension.SetProtocol("registry", GetProtocol)
 	extension.SetRegistry("mock", registry.NewMockRegistry)
 	extension.SetProtocol(protocolwrapper.FILTER, protocolwrapper.NewMockProtocolFilter)
-	url, _ := common.NewURL(context.TODO(), "mock://127.0.0.1:1111")
+	url, _ := common.NewURL("mock://127.0.0.1:1111")
 	suburl, _ := common.NewURL(
-		context.TODO(),
 		"dubbo://127.0.0.1:20000/org.apache.dubbo-go.mockService",
 		common.WithParamsValue(constant.CLUSTER_KEY, "mock"),
 		common.WithParamsValue(constant.GROUP_KEY, "group"),
@@ -148,9 +144,8 @@ func TestMultiRegAndMultiProtoExporter(t *testing.T) {
 	regProtocol := newRegistryProtocol()
 	exporterNormal(t, regProtocol)
 
-	url2, _ := common.NewURL(context.TODO(), "mock://127.0.0.1:2222")
+	url2, _ := common.NewURL("mock://127.0.0.1:2222")
 	suburl2, _ := common.NewURL(
-		context.TODO(),
 		"jsonrpc://127.0.0.1:20000//",
 		common.WithParamsValue(constant.CLUSTER_KEY, "mock"),
 	)
@@ -178,9 +173,8 @@ func TestOneRegAndProtoExporter(t *testing.T) {
 	regProtocol := newRegistryProtocol()
 	exporterNormal(t, regProtocol)
 
-	url2, _ := common.NewURL(context.TODO(), "mock://127.0.0.1:1111")
+	url2, _ := common.NewURL("mock://127.0.0.1:1111")
 	suburl2, _ := common.NewURL(
-		context.TODO(),
 		"dubbo://127.0.0.1:20000/org.apache.dubbo-go.mockService",
 		common.WithParamsValue(constant.CLUSTER_KEY, "mock"),
 		common.WithParamsValue(constant.GROUP_KEY, "group"),
@@ -242,7 +236,6 @@ func TestExportWithOverrideListener(t *testing.T) {
 		return
 	}
 	overrideUrl, _ := common.NewURL(
-		context.Background(),
 		"override://0:0:0:0/org.apache.dubbo-go.mockService?cluster=mock1&&group=group&&version=1.0.0",
 	)
 	event := &registry.ServiceEvent{Action: remoting.EventTypeAdd, Service: overrideUrl}
@@ -256,7 +249,7 @@ func TestExportWithOverrideListener(t *testing.T) {
 
 func TestExportWithServiceConfig(t *testing.T) {
 	extension.SetDefaultConfigurator(configurator.NewMockConfigurator)
-	ccUrl, _ := common.NewURL(context.TODO(), "mock://127.0.0.1:1111")
+	ccUrl, _ := common.NewURL("mock://127.0.0.1:1111")
 	dc, _ := (&config_center.MockDynamicConfigurationFactory{}).GetDynamicConfiguration(&ccUrl)
 	common_cfg.GetEnvInstance().SetDynamicConfiguration(dc)
 	regProtocol := newRegistryProtocol()
@@ -275,7 +268,7 @@ func TestExportWithServiceConfig(t *testing.T) {
 
 func TestExportWithApplicationConfig(t *testing.T) {
 	extension.SetDefaultConfigurator(configurator.NewMockConfigurator)
-	ccUrl, _ := common.NewURL(context.TODO(), "mock://127.0.0.1:1111")
+	ccUrl, _ := common.NewURL("mock://127.0.0.1:1111")
 	dc, _ := (&config_center.MockDynamicConfigurationFactory{}).GetDynamicConfiguration(&ccUrl)
 	common_cfg.GetEnvInstance().SetDynamicConfiguration(dc)
 	regProtocol := newRegistryProtocol()
diff --git a/registry/registry.go b/registry/registry.go
index c7279a29e1f423ca200aa2bf9390c127efcf10cb..d673864700e6ba99e8f0283247d53760b85598aa 100644
--- a/registry/registry.go
+++ b/registry/registry.go
@@ -21,7 +21,13 @@ import (
 	"github.com/apache/dubbo-go/common"
 )
 
-// Extension - Registry
+/*
+ * -----------------------------------NOTICE---------------------------------------------
+ * If there is no special case, you'd better inherit BaseRegistry and implement the
+ * FacadeBasedRegistry interface instead of directly implementing the Registry interface.
+ * --------------------------------------------------------------------------------------
+ */
+// Registry Extension - Registry
 type Registry interface {
 	common.Node
 	//used for service provider calling , register services to registry
@@ -38,11 +44,13 @@ type Registry interface {
 	//mode2 : callback mode, subscribe with notify(notify listener).
 	Subscribe(*common.URL, NotifyListener)
 }
+
+// NotifyListener ...
 type NotifyListener interface {
 	Notify(*ServiceEvent)
 }
 
-//Deprecated!
+// Listener Deprecated!
 type Listener interface {
 	Next() (*ServiceEvent, error)
 	Close()
diff --git a/registry/zookeeper/listener.go b/registry/zookeeper/listener.go
index 857421f07706d6bdfec5a3ec21ba674627633458..fe8e42db9f39190e34142149a6b67c9638a84ed2 100644
--- a/registry/zookeeper/listener.go
+++ b/registry/zookeeper/listener.go
@@ -18,8 +18,8 @@
 package zookeeper
 
 import (
-	"context"
 	"strings"
+	"sync"
 )
 
 import (
@@ -35,27 +35,32 @@ import (
 	zk "github.com/apache/dubbo-go/remoting/zookeeper"
 )
 
+// RegistryDataListener ...
 type RegistryDataListener struct {
 	interestedURL []*common.URL
 	listener      config_center.ConfigurationListener
 }
 
+// NewRegistryDataListener ...
 func NewRegistryDataListener(listener config_center.ConfigurationListener) *RegistryDataListener {
 	return &RegistryDataListener{listener: listener, interestedURL: []*common.URL{}}
 }
+
+// AddInterestedURL ...
 func (l *RegistryDataListener) AddInterestedURL(url *common.URL) {
 	l.interestedURL = append(l.interestedURL, url)
 }
 
+// DataChange ...
 func (l *RegistryDataListener) DataChange(eventType remoting.Event) bool {
 	// Intercept the last bit
 	index := strings.Index(eventType.Path, "/providers/")
 	if index == -1 {
-		logger.Warn("Listen with no url, event.path={%v}", eventType.Path)
+		logger.Warnf("Listen with no url, event.path={%v}", eventType.Path)
 		return false
 	}
 	url := eventType.Path[index+len("/providers/"):]
-	serviceURL, err := common.NewURL(context.TODO(), url)
+	serviceURL, err := common.NewURL(url)
 	if err != nil {
 		logger.Errorf("Listen NewURL(r{%s}) = error{%v} eventType.Path={%v}", url, err, eventType.Path)
 		return false
@@ -70,20 +75,27 @@ func (l *RegistryDataListener) DataChange(eventType remoting.Event) bool {
 	return false
 }
 
+// RegistryConfigurationListener ...
 type RegistryConfigurationListener struct {
-	client   *zk.ZookeeperClient
-	registry *zkRegistry
-	events   chan *config_center.ConfigChangeEvent
+	client    *zk.ZookeeperClient
+	registry  *zkRegistry
+	events    chan *config_center.ConfigChangeEvent
+	isClosed  bool
+	closeOnce sync.Once
 }
 
+// NewRegistryConfigurationListener for listening the event of zk.
 func NewRegistryConfigurationListener(client *zk.ZookeeperClient, reg *zkRegistry) *RegistryConfigurationListener {
-	reg.wg.Add(1)
-	return &RegistryConfigurationListener{client: client, registry: reg, events: make(chan *config_center.ConfigChangeEvent, 32)}
+	reg.WaitGroup().Add(1)
+	return &RegistryConfigurationListener{client: client, registry: reg, events: make(chan *config_center.ConfigChangeEvent, 32), isClosed: false}
 }
+
+// Process ...
 func (l *RegistryConfigurationListener) Process(configType *config_center.ConfigChangeEvent) {
 	l.events <- configType
 }
 
+// Next ...
 func (l *RegistryConfigurationListener) Next() (*registry.ServiceEvent, error) {
 	for {
 		select {
@@ -91,8 +103,8 @@ func (l *RegistryConfigurationListener) Next() (*registry.ServiceEvent, error) {
 			logger.Warnf("listener's zk client connection is broken, so zk event listener exit now.")
 			return nil, perrors.New("listener stopped")
 
-		case <-l.registry.done:
-			logger.Warnf("zk consumer register has quit, so zk event listener exit asap now.")
+		case <-l.registry.Done():
+			logger.Warnf("zk consumer register has quit, so zk event listener exit now.")
 			return nil, perrors.New("listener stopped")
 
 		case e := <-l.events:
@@ -108,8 +120,14 @@ func (l *RegistryConfigurationListener) Next() (*registry.ServiceEvent, error) {
 		}
 	}
 }
+
+// Close ...
 func (l *RegistryConfigurationListener) Close() {
-	l.registry.wg.Done()
+	// ensure that the listener will be closed at most once.
+	l.closeOnce.Do(func() {
+		l.isClosed = true
+		l.registry.WaitGroup().Done()
+	})
 }
 
 func (l *RegistryConfigurationListener) valid() bool {
diff --git a/registry/zookeeper/listener_test.go b/registry/zookeeper/listener_test.go
index 910d47b7e4e3d27c6f7245777cba1f46adc8e318..1a76b29a6f64e0329b289ce50218032a25f6f5cd 100644
--- a/registry/zookeeper/listener_test.go
+++ b/registry/zookeeper/listener_test.go
@@ -18,7 +18,6 @@
 package zookeeper
 
 import (
-	"context"
 	"testing"
 )
 
@@ -34,9 +33,9 @@ import (
 
 func Test_DataChange(t *testing.T) {
 	listener := NewRegistryDataListener(&MockDataListener{})
-	url, _ := common.NewURL(context.TODO(), "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-2.6.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")
+	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)
-	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-2.6.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"})
+	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)
 }
 
diff --git a/registry/zookeeper/registry.go b/registry/zookeeper/registry.go
index 29ae51d44f3691807cbc74912290ba141d1f5d47..e13443d57d7dae9fb5d50b2e1c28f618780fd850 100644
--- a/registry/zookeeper/registry.go
+++ b/registry/zookeeper/registry.go
@@ -18,20 +18,16 @@
 package zookeeper
 
 import (
-	"context"
 	"fmt"
 	"net/url"
-	"os"
-	"strconv"
 	"strings"
 	"sync"
 	"time"
 )
 
 import (
-	gxnet "github.com/dubbogo/gost/net"
+	"github.com/dubbogo/go-zookeeper/zk"
 	perrors "github.com/pkg/errors"
-	"github.com/samuel/go-zookeeper/zk"
 )
 
 import (
@@ -44,19 +40,11 @@ import (
 )
 
 const (
-	RegistryZkClient  = "zk registry"
-	RegistryConnDelay = 3
-)
-
-var (
-	processID = ""
-	localIP   = ""
+	// RegistryZkClient zk client name
+	RegistryZkClient = "zk registry"
 )
 
 func init() {
-	processID = fmt.Sprintf("%d", os.Getpid())
-	localIP, _ = gxnet.GetLocalIP()
-	//plugins.PluggableRegistries["zookeeper"] = newZkRegistry
 	extension.SetRegistry("zookeeper", newZkRegistry)
 }
 
@@ -65,20 +53,13 @@ func init() {
 /////////////////////////////////////
 
 type zkRegistry struct {
-	context context.Context
-	*common.URL
-	birth int64          // time of file birth, seconds since Epoch; 0 if unknown
-	wg    sync.WaitGroup // wg+done for zk restart
-	done  chan struct{}
-
-	cltLock  sync.Mutex
-	client   *zookeeper.ZookeeperClient
-	services map[string]common.URL // service name + protocol -> service config
-
+	registry.BaseRegistry
+	client         *zookeeper.ZookeeperClient
 	listenerLock   sync.Mutex
 	listener       *zookeeper.ZkEventListener
 	dataListener   *RegistryDataListener
 	configListener *RegistryConfigurationListener
+	cltLock        sync.Mutex
 	//for provider
 	zkPath map[string]int // key = protocol://ip:port/interface
 }
@@ -88,21 +69,17 @@ func newZkRegistry(url *common.URL) (registry.Registry, error) {
 		err error
 		r   *zkRegistry
 	)
-
 	r = &zkRegistry{
-		URL:      url,
-		birth:    time.Now().UnixNano(),
-		done:     make(chan struct{}),
-		services: make(map[string]common.URL),
-		zkPath:   make(map[string]int),
+		zkPath: make(map[string]int),
 	}
+	r.InitBaseRegistry(url, r)
 
 	err = zookeeper.ValidateZookeeperClient(r, zookeeper.WithZkName(RegistryZkClient))
 	if err != nil {
 		return nil, err
 	}
+	r.WaitGroup().Add(1) //zk client start successful, then wg +1
 
-	r.wg.Add(1)
 	go zookeeper.HandleClientRestart(r)
 
 	r.listener = zookeeper.NewZkEventListener(r.client)
@@ -112,10 +89,12 @@ func newZkRegistry(url *common.URL) (registry.Registry, error) {
 	return r, nil
 }
 
+// Options ...
 type Options struct {
 	client *zookeeper.ZookeeperClient
 }
 
+// Option ...
 type Option func(*Options)
 
 func newMockZkRegistry(url *common.URL, opts ...zookeeper.Option) (*zk.TestCluster, *zkRegistry, error) {
@@ -127,246 +106,58 @@ func newMockZkRegistry(url *common.URL, opts ...zookeeper.Option) (*zk.TestClust
 	)
 
 	r = &zkRegistry{
-		URL:      url,
-		birth:    time.Now().UnixNano(),
-		done:     make(chan struct{}),
-		services: make(map[string]common.URL),
-		zkPath:   make(map[string]int),
+		zkPath: make(map[string]int),
 	}
-
+	r.InitBaseRegistry(url, r)
 	c, r.client, _, err = zookeeper.NewMockZookeeperClient("test", 15*time.Second, opts...)
 	if err != nil {
 		return nil, nil, err
 	}
-	r.wg.Add(1)
+	r.WaitGroup().Add(1) //zk client start successful, then wg +1
 	go zookeeper.HandleClientRestart(r)
+	r.InitListeners()
+	return c, r, nil
+}
 
+func (r *zkRegistry) InitListeners() {
 	r.listener = zookeeper.NewZkEventListener(r.client)
 	r.configListener = NewRegistryConfigurationListener(r.client, r)
 	r.dataListener = NewRegistryDataListener(r.configListener)
+}
 
-	return c, r, nil
+func (r *zkRegistry) CreatePath(path string) error {
+	return r.ZkClient().Create(path)
 }
 
-func (r *zkRegistry) ZkClient() *zookeeper.ZookeeperClient {
-	return r.client
+func (r *zkRegistry) DoRegister(root string, node string) error {
+	return r.registerTempZookeeperNode(root, node)
 }
 
-func (r *zkRegistry) SetZkClient(client *zookeeper.ZookeeperClient) {
-	r.client = client
+func (r *zkRegistry) DoSubscribe(conf *common.URL) (registry.Listener, error) {
+	return r.getListener(conf)
 }
 
-func (r *zkRegistry) ZkClientLock() *sync.Mutex {
-	return &r.cltLock
+func (r *zkRegistry) CloseAndNilClient() {
+	r.client.Close()
+	r.client = nil
 }
 
-func (r *zkRegistry) WaitGroup() *sync.WaitGroup {
-	return &r.wg
+func (r *zkRegistry) ZkClient() *zookeeper.ZookeeperClient {
+	return r.client
 }
 
-func (r *zkRegistry) GetDone() chan struct{} {
-	return r.done
+func (r *zkRegistry) SetZkClient(client *zookeeper.ZookeeperClient) {
+	r.client = client
 }
 
-func (r *zkRegistry) GetUrl() common.URL {
-	return *r.URL
+func (r *zkRegistry) ZkClientLock() *sync.Mutex {
+	return &r.cltLock
 }
 
-func (r *zkRegistry) Destroy() {
+func (r *zkRegistry) CloseListener() {
 	if r.configListener != nil {
 		r.configListener.Close()
 	}
-	close(r.done)
-	r.wg.Wait()
-	r.closeRegisters()
-}
-
-func (r *zkRegistry) RestartCallBack() bool {
-
-	// copy r.services
-	services := []common.URL{}
-	for _, confIf := range r.services {
-		services = append(services, confIf)
-	}
-
-	flag := true
-	for _, confIf := range services {
-		err := r.register(confIf)
-		if err != nil {
-			logger.Errorf("(ZkProviderRegistry)register(conf{%#v}) = error{%#v}",
-				confIf, perrors.WithStack(err))
-			flag = false
-			break
-		}
-		logger.Infof("success to re-register service :%v", confIf.Key())
-	}
-	return flag
-}
-
-func (r *zkRegistry) Register(conf common.URL) error {
-	var (
-		ok  bool
-		err error
-	)
-	role, _ := strconv.Atoi(r.URL.GetParam(constant.ROLE_KEY, ""))
-	switch role {
-	case common.CONSUMER:
-		r.cltLock.Lock()
-		_, ok = r.services[conf.Key()]
-		r.cltLock.Unlock()
-		if ok {
-			return perrors.Errorf("Path{%s} has been registered", conf.Path)
-		}
-
-		err = r.register(conf)
-		if err != nil {
-			return perrors.WithStack(err)
-		}
-
-		r.cltLock.Lock()
-		r.services[conf.Key()] = conf
-		r.cltLock.Unlock()
-		logger.Debugf("(consumerZkConsumerRegistry)Register(conf{%#v})", conf)
-
-	case common.PROVIDER:
-
-		// Check if the service has been registered
-		r.cltLock.Lock()
-		// Note the difference between consumer and consumerZookeeperRegistry (consumer use conf.Path).
-		// Because the consumer wants to provide monitoring functions for the selector,
-		// the provider allows multiple groups or versions of the same service to be registered.
-		_, ok = r.services[conf.Key()]
-		r.cltLock.Unlock()
-		if ok {
-			return perrors.Errorf("Path{%s} has been registered", conf.Key())
-		}
-
-		err = r.register(conf)
-		if err != nil {
-			return perrors.WithMessagef(err, "register(conf:%+v)", conf)
-		}
-
-		r.cltLock.Lock()
-		r.services[conf.Key()] = conf
-		r.cltLock.Unlock()
-
-		logger.Debugf("(ZkProviderRegistry)Register(conf{%#v})", conf)
-	}
-
-	return nil
-}
-
-func (r *zkRegistry) register(c common.URL) error {
-	var (
-		err error
-		//revision   string
-		params     url.Values
-		rawURL     string
-		encodedURL string
-		dubboPath  string
-		//conf       config.URL
-	)
-
-	err = zookeeper.ValidateZookeeperClient(r, zookeeper.WithZkName(RegistryZkClient))
-	if err != nil {
-		return perrors.WithStack(err)
-	}
-	params = url.Values{}
-
-	c.RangeParams(func(key, value string) bool {
-		params.Add(key, value)
-		return true
-	})
-
-	params.Add("pid", processID)
-	params.Add("ip", localIP)
-	//params.Add("timeout", fmt.Sprintf("%d", int64(r.Timeout)/1e6))
-
-	role, _ := strconv.Atoi(r.URL.GetParam(constant.ROLE_KEY, ""))
-	switch role {
-
-	case common.PROVIDER:
-
-		if c.Path == "" || len(c.Methods) == 0 {
-			return perrors.Errorf("conf{Path:%s, Methods:%s}", c.Path, c.Methods)
-		}
-		// 鍏堝垱寤烘湇鍔′笅闈㈢殑provider node
-		dubboPath = fmt.Sprintf("/dubbo/%s/%s", c.Service(), common.DubboNodes[common.PROVIDER])
-		r.cltLock.Lock()
-		err = r.client.Create(dubboPath)
-		r.cltLock.Unlock()
-		if err != nil {
-			logger.Errorf("zkClient.create(path{%s}) = error{%#v}", dubboPath, perrors.WithStack(err))
-			return perrors.WithMessagef(err, "zkclient.Create(path:%s)", dubboPath)
-		}
-		params.Add("anyhost", "true")
-
-		// Dubbo java consumer to start looking for the provider url,because the category does not match,
-		// the provider will not find, causing the consumer can not start, so we use consumers.
-		// DubboRole               = [...]string{"consumer", "", "", "provider"}
-		// params.Add("category", (RoleType(PROVIDER)).Role())
-		params.Add("category", (common.RoleType(common.PROVIDER)).String())
-		params.Add("dubbo", "dubbo-provider-golang-"+constant.Version)
-
-		params.Add("side", (common.RoleType(common.PROVIDER)).Role())
-
-		if len(c.Methods) == 0 {
-			params.Add("methods", strings.Join(c.Methods, ","))
-		}
-		logger.Debugf("provider zk url params:%#v", params)
-		var host string
-		if c.Ip == "" {
-			host = localIP + ":" + c.Port
-		} else {
-			host = c.Ip + ":" + c.Port
-		}
-
-		rawURL = fmt.Sprintf("%s://%s%s?%s", c.Protocol, host, c.Path, params.Encode())
-		encodedURL = url.QueryEscape(rawURL)
-
-		// Print your own registration service providers.
-		dubboPath = fmt.Sprintf("/dubbo/%s/%s", c.Service(), (common.RoleType(common.PROVIDER)).String())
-		logger.Debugf("provider path:%s, url:%s", dubboPath, rawURL)
-
-	case common.CONSUMER:
-		dubboPath = fmt.Sprintf("/dubbo/%s/%s", c.Service(), common.DubboNodes[common.CONSUMER])
-		r.cltLock.Lock()
-		err = r.client.Create(dubboPath)
-		r.cltLock.Unlock()
-		if err != nil {
-			logger.Errorf("zkClient.create(path{%s}) = error{%v}", dubboPath, perrors.WithStack(err))
-			return perrors.WithStack(err)
-		}
-		dubboPath = fmt.Sprintf("/dubbo/%s/%s", c.Service(), common.DubboNodes[common.PROVIDER])
-		r.cltLock.Lock()
-		err = r.client.Create(dubboPath)
-		r.cltLock.Unlock()
-		if err != nil {
-			logger.Errorf("zkClient.create(path{%s}) = error{%v}", dubboPath, perrors.WithStack(err))
-			return perrors.WithStack(err)
-		}
-
-		params.Add("protocol", c.Protocol)
-
-		params.Add("category", (common.RoleType(common.CONSUMER)).String())
-		params.Add("dubbo", "dubbogo-consumer-"+constant.Version)
-
-		rawURL = fmt.Sprintf("consumer://%s%s?%s", localIP, c.Path, params.Encode())
-		encodedURL = url.QueryEscape(rawURL)
-
-		dubboPath = fmt.Sprintf("/dubbo/%s/%s", c.Service(), (common.RoleType(common.CONSUMER)).String())
-		logger.Debugf("consumer path:%s, url:%s", dubboPath, rawURL)
-
-	default:
-		return perrors.Errorf("@c{%v} type is not referencer or provider", c)
-	}
-
-	err = r.registerTempZookeeperNode(dubboPath, encodedURL)
-
-	if err != nil {
-		return perrors.WithMessagef(err, "registerTempZookeeperNode(path:%s, url:%s)", dubboPath, rawURL)
-	}
-	return nil
 }
 
 func (r *zkRegistry) registerTempZookeeperNode(root string, node string) error {
@@ -396,50 +187,16 @@ func (r *zkRegistry) registerTempZookeeperNode(root string, node string) error {
 	return nil
 }
 
-func (r *zkRegistry) subscribe(conf *common.URL) (registry.Listener, error) {
-	return r.getListener(conf)
-}
-
-//subscribe from registry
-func (r *zkRegistry) Subscribe(url *common.URL, notifyListener registry.NotifyListener) {
-	for {
-		if !r.IsAvailable() {
-			logger.Warnf("event listener game over.")
-			return
-		}
-
-		listener, err := r.subscribe(url)
-		if err != nil {
-			if !r.IsAvailable() {
-				logger.Warnf("event listener game over.")
-				return
-			}
-			logger.Warnf("getListener() = err:%v", perrors.WithStack(err))
-			time.Sleep(time.Duration(RegistryConnDelay) * time.Second)
-			continue
-		}
-
-		for {
-			if serviceEvent, err := listener.Next(); err != nil {
-				logger.Warnf("Selector.watch() = error{%v}", perrors.WithStack(err))
-				listener.Close()
-				return
-			} else {
-				logger.Infof("update begin, service event: %v", serviceEvent.String())
-				notifyListener.Notify(serviceEvent)
-			}
-
-		}
-
-	}
-}
-
 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")
+	}
 	zkListener = r.configListener
 	r.listenerLock.Unlock()
 	if r.listener == nil {
@@ -461,27 +218,8 @@ func (r *zkRegistry) getListener(conf *common.URL) (*RegistryConfigurationListen
 	//Interested register to dataconfig.
 	r.dataListener.AddInterestedURL(conf)
 	for _, v := range strings.Split(conf.GetParam(constant.CATEGORY_KEY, constant.DEFAULT_CATEGORY), ",") {
-		go r.listener.ListenServiceEvent(fmt.Sprintf("/dubbo/%s/"+v, conf.Service()), r.dataListener)
+		go r.listener.ListenServiceEvent(fmt.Sprintf("/dubbo/%s/"+v, url.QueryEscape(conf.Service())), r.dataListener)
 	}
 
 	return zkListener, nil
 }
-
-func (r *zkRegistry) closeRegisters() {
-	r.cltLock.Lock()
-	defer r.cltLock.Unlock()
-	logger.Infof("begin to close provider zk client")
-	// Close the old client first to close the tmp node.
-	r.client.Close()
-	r.client = nil
-	r.services = nil
-}
-
-func (r *zkRegistry) IsAvailable() bool {
-	select {
-	case <-r.done:
-		return false
-	default:
-		return true
-	}
-}
diff --git a/registry/zookeeper/registry_test.go b/registry/zookeeper/registry_test.go
index 841c38da7fbf1830b6f7c55809fc50d52468ef46..0d7623ca12a9b4e49f84ec988c796f2e913d537f 100644
--- a/registry/zookeeper/registry_test.go
+++ b/registry/zookeeper/registry_test.go
@@ -18,7 +18,6 @@
 package zookeeper
 
 import (
-	"context"
 	"strconv"
 	"testing"
 	"time"
@@ -35,20 +34,20 @@ import (
 )
 
 func Test_Register(t *testing.T) {
-	regurl, _ := common.NewURL(context.TODO(), "registry://127.0.0.1:1111", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER)))
-	url, _ := common.NewURL(context.TODO(), "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParamsValue(constant.CLUSTER_KEY, "mock"), common.WithParamsValue("serviceid", "soa.mock"), common.WithMethods([]string{"GetUser", "AddUser"}))
+	regurl, _ := common.NewURL("registry://127.0.0.1:1111", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER)))
+	url, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParamsValue(constant.CLUSTER_KEY, "mock"), common.WithParamsValue("serviceid", "soa.mock"), common.WithMethods([]string{"GetUser", "AddUser"}))
 
 	ts, reg, _ := newMockZkRegistry(&regurl)
 	defer ts.Stop()
 	err := reg.Register(url)
 	children, _ := reg.client.GetChildren("/dubbo/com.ikurento.user.UserProvider/providers")
-	assert.Regexp(t, ".*dubbo%3A%2F%2F127.0.0.1%3A20000%2Fcom.ikurento.user.UserProvider%3Fanyhost%3Dtrue%26category%3Dproviders%26cluster%3Dmock%26dubbo%3Ddubbo-provider-golang-2.6.0%26.*.serviceid%3Dsoa.mock%26.*provider", children)
+	assert.Regexp(t, ".*dubbo%3A%2F%2F127.0.0.1%3A20000%2Fcom.ikurento.user.UserProvider%3Fanyhost%3Dtrue%26category%3Dproviders%26cluster%3Dmock%26dubbo%3Ddubbo-provider-golang-1.3.0%26.*.serviceid%3Dsoa.mock%26.*provider", children)
 	assert.NoError(t, err)
 }
 
 func Test_Subscribe(t *testing.T) {
-	regurl, _ := common.NewURL(context.TODO(), "registry://127.0.0.1:1111", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER)))
-	url, _ := common.NewURL(context.TODO(), "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParamsValue(constant.CLUSTER_KEY, "mock"), common.WithMethods([]string{"GetUser", "AddUser"}))
+	regurl, _ := common.NewURL("registry://127.0.0.1:1111", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER)))
+	url, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParamsValue(constant.CLUSTER_KEY, "mock"), common.WithMethods([]string{"GetUser", "AddUser"}))
 	ts, reg, _ := newMockZkRegistry(&regurl)
 
 	//provider register
@@ -64,7 +63,7 @@ func Test_Subscribe(t *testing.T) {
 	_, reg2, _ := newMockZkRegistry(&regurl, zookeeper.WithTestCluster(ts))
 
 	reg2.Register(url)
-	listener, _ := reg2.subscribe(&url)
+	listener, _ := reg2.DoSubscribe(&url)
 
 	serviceEvent, _ := listener.Next()
 	assert.NoError(t, err)
@@ -76,8 +75,8 @@ func Test_Subscribe(t *testing.T) {
 }
 
 func Test_ConsumerDestory(t *testing.T) {
-	regurl, _ := common.NewURL(context.TODO(), "registry://127.0.0.1:1111", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.CONSUMER)))
-	url, _ := common.NewURL(context.TODO(), "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParamsValue(constant.CLUSTER_KEY, "mock"), common.WithMethods([]string{"GetUser", "AddUser"}))
+	regurl, _ := common.NewURL("registry://127.0.0.1:1111", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.CONSUMER)))
+	url, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParamsValue(constant.CLUSTER_KEY, "mock"), common.WithMethods([]string{"GetUser", "AddUser"}))
 
 	ts, reg, err := newMockZkRegistry(&regurl)
 	defer ts.Stop()
@@ -85,7 +84,7 @@ func Test_ConsumerDestory(t *testing.T) {
 	assert.NoError(t, err)
 	err = reg.Register(url)
 	assert.NoError(t, err)
-	_, err = reg.subscribe(&url)
+	_, err = reg.DoSubscribe(&url)
 	assert.NoError(t, err)
 
 	//listener.Close()
@@ -96,8 +95,8 @@ func Test_ConsumerDestory(t *testing.T) {
 }
 
 func Test_ProviderDestory(t *testing.T) {
-	regurl, _ := common.NewURL(context.TODO(), "registry://127.0.0.1:1111", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER)))
-	url, _ := common.NewURL(context.TODO(), "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParamsValue(constant.CLUSTER_KEY, "mock"), common.WithMethods([]string{"GetUser", "AddUser"}))
+	regurl, _ := common.NewURL("registry://127.0.0.1:1111", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER)))
+	url, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParamsValue(constant.CLUSTER_KEY, "mock"), common.WithMethods([]string{"GetUser", "AddUser"}))
 
 	ts, reg, err := newMockZkRegistry(&regurl)
 	defer ts.Stop()
diff --git a/remoting/etcdv3/client.go b/remoting/etcdv3/client.go
index 050968565387fd31871b0aa8e9969496d39f6534..ba3ea6e864923b1e70cc4a0d31ee98415807699c 100644
--- a/remoting/etcdv3/client.go
+++ b/remoting/etcdv3/client.go
@@ -36,16 +36,22 @@ import (
 )
 
 const (
-	ConnDelay            = 3
-	MaxFailTimes         = 15
+	// ConnDelay connection dalay
+	ConnDelay = 3
+	// MaxFailTimes max failure times
+	MaxFailTimes = 15
+	// RegistryETCDV3Client client name
 	RegistryETCDV3Client = "etcd registry"
 )
 
 var (
+	// ErrNilETCDV3Client ...
 	ErrNilETCDV3Client = perrors.New("etcd raw client is nil") // full describe the ERR
-	ErrKVPairNotFound  = perrors.New("k/v pair not found")
+	// ErrKVPairNotFound ...
+	ErrKVPairNotFound = perrors.New("k/v pair not found")
 )
 
+// Options ...
 type Options struct {
 	name      string
 	endpoints []string
@@ -54,30 +60,38 @@ type Options struct {
 	heartbeat int // heartbeat second
 }
 
+// Option ...
 type Option func(*Options)
 
+// WithEndpoints ...
 func WithEndpoints(endpoints ...string) Option {
 	return func(opt *Options) {
 		opt.endpoints = endpoints
 	}
 }
+
+// WithName ...
 func WithName(name string) Option {
 	return func(opt *Options) {
 		opt.name = name
 	}
 }
+
+// WithTimeout ...
 func WithTimeout(timeout time.Duration) Option {
 	return func(opt *Options) {
 		opt.timeout = timeout
 	}
 }
 
+// WithHeartbeat ...
 func WithHeartbeat(heartbeat int) Option {
 	return func(opt *Options) {
 		opt.heartbeat = heartbeat
 	}
 }
 
+// ValidateClient ...
 func ValidateClient(container clientFacade, opts ...Option) error {
 
 	options := &Options{
@@ -117,6 +131,7 @@ func ValidateClient(container clientFacade, opts ...Option) error {
 	return nil
 }
 
+// Client ...
 type Client struct {
 	lock sync.RWMutex
 
@@ -191,6 +206,7 @@ func (c *Client) stop() bool {
 	return false
 }
 
+// Close ...
 func (c *Client) Close() {
 
 	if c == nil {
@@ -309,6 +325,7 @@ func (c *Client) get(k string) (string, error) {
 	return string(resp.Kvs[0].Value), nil
 }
 
+// CleanKV ...
 func (c *Client) CleanKV() error {
 
 	c.lock.RLock()
@@ -408,10 +425,12 @@ func (c *Client) keepAliveKV(k string, v string) error {
 	return nil
 }
 
+// Done ...
 func (c *Client) Done() <-chan struct{} {
 	return c.exit
 }
 
+// Valid ...
 func (c *Client) Valid() bool {
 	select {
 	case <-c.exit:
@@ -428,6 +447,7 @@ func (c *Client) Valid() bool {
 	return true
 }
 
+// Create ...
 func (c *Client) Create(k string, v string) error {
 
 	err := c.put(k, v)
@@ -437,6 +457,7 @@ func (c *Client) Create(k string, v string) error {
 	return nil
 }
 
+// Delete ...
 func (c *Client) Delete(k string) error {
 
 	err := c.delete(k)
@@ -447,6 +468,7 @@ func (c *Client) Delete(k string) error {
 	return nil
 }
 
+// RegisterTemp ...
 func (c *Client) RegisterTemp(basePath string, node string) (string, error) {
 
 	completeKey := path.Join(basePath, node)
@@ -459,6 +481,7 @@ func (c *Client) RegisterTemp(basePath string, node string) (string, error) {
 	return completeKey, nil
 }
 
+// GetChildrenKVList ...
 func (c *Client) GetChildrenKVList(k string) ([]string, []string, error) {
 
 	kList, vList, err := c.getChildren(k)
@@ -468,6 +491,7 @@ func (c *Client) GetChildrenKVList(k string) ([]string, []string, error) {
 	return kList, vList, nil
 }
 
+// Get ...
 func (c *Client) Get(k string) (string, error) {
 
 	v, err := c.get(k)
@@ -478,6 +502,7 @@ func (c *Client) Get(k string) (string, error) {
 	return v, nil
 }
 
+// Watch ...
 func (c *Client) Watch(k string) (clientv3.WatchChan, error) {
 
 	wc, err := c.watch(k)
@@ -487,6 +512,7 @@ func (c *Client) Watch(k string) (clientv3.WatchChan, error) {
 	return wc, nil
 }
 
+// WatchWithPrefix ...
 func (c *Client) WatchWithPrefix(prefix string) (clientv3.WatchChan, error) {
 
 	wc, err := c.watchWithPrefix(prefix)
diff --git a/remoting/etcdv3/client_test.go b/remoting/etcdv3/client_test.go
index b46a7eb5616f1623f8914b85827adf4278e0e7af..692ae2fb66c1094300ce67283d55722d84a6044e 100644
--- a/remoting/etcdv3/client_test.go
+++ b/remoting/etcdv3/client_test.go
@@ -32,6 +32,7 @@ import (
 import (
 	"github.com/coreos/etcd/mvcc/mvccpb"
 	perrors "github.com/pkg/errors"
+	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/suite"
 	"go.etcd.io/etcd/embed"
 	"google.golang.org/grpc/connectivity"
@@ -177,6 +178,10 @@ func (suite *ClientTestSuite) TestClientDone() {
 	}()
 
 	c.Wait.Wait()
+
+	if c.Valid() == true {
+		suite.T().Fatal("client should be invalid then")
+	}
 }
 
 func (suite *ClientTestSuite) TestClientCreateKV() {
@@ -301,13 +306,26 @@ func (suite *ClientTestSuite) TestClientWatch() {
 			t.Fatal(err)
 		}
 
+		events := make([]mvccpb.Event, 0)
+		var eCreate, eDelete mvccpb.Event
+
 		for e := range wc {
 
 			for _, event := range e.Events {
+				events = append(events, (mvccpb.Event)(*event))
+				if event.Type == mvccpb.PUT {
+					eCreate = (mvccpb.Event)(*event)
+				}
+				if event.Type == mvccpb.DELETE {
+					eDelete = (mvccpb.Event)(*event)
+				}
 				t.Logf("type IsCreate %v k %s v %s", event.IsCreate(), event.Kv.Key, event.Kv.Value)
 			}
 		}
 
+		assert.Equal(t, 2, len(events))
+		assert.Contains(t, events, eCreate)
+		assert.Contains(t, events, eDelete)
 	}()
 
 	for _, tc := range tests {
@@ -340,25 +358,35 @@ func (suite *ClientTestSuite) TestClientRegisterTemp() {
 	wg.Add(1)
 
 	go func() {
+		defer wg.Done()
+
 		completePath := path.Join("scott", "wang")
 		wc, err := observeC.watch(completePath)
 		if err != nil {
 			t.Fatal(err)
 		}
 
+		events := make([]mvccpb.Event, 0)
+		var eCreate, eDelete mvccpb.Event
+
 		for e := range wc {
 
 			for _, event := range e.Events {
-
+				events = append(events, (mvccpb.Event)(*event))
 				if event.Type == mvccpb.DELETE {
+					eDelete = (mvccpb.Event)(*event)
 					t.Logf("complete key (%s) is delete", completePath)
-					wg.Done()
 					observeC.Close()
-					return
+					break
 				}
+				eCreate = (mvccpb.Event)(*event)
 				t.Logf("type IsCreate %v k %s v %s", event.IsCreate(), event.Kv.Key, event.Kv.Value)
 			}
 		}
+
+		assert.Equal(t, 2, len(events))
+		assert.Contains(t, events, eCreate)
+		assert.Contains(t, events, eDelete)
 	}()
 
 	_, err := c.RegisterTemp("scott", "wang")
diff --git a/remoting/etcdv3/facade.go b/remoting/etcdv3/facade.go
index 499044b8d77d3dcd8d32b0cb70cb78f84fae8ec4..35befc85e449ec02a6377faec300aa6b46bcc8bf 100644
--- a/remoting/etcdv3/facade.go
+++ b/remoting/etcdv3/facade.go
@@ -38,11 +38,12 @@ type clientFacade interface {
 	SetClient(*Client)
 	ClientLock() *sync.Mutex
 	WaitGroup() *sync.WaitGroup //for wait group control, etcd client listener & etcd client container
-	GetDone() chan struct{}     //for etcd client control
+	Done() chan struct{}        //for etcd client control
 	RestartCallBack() bool
 	common.Node
 }
 
+// HandleClientRestart ...
 func HandleClientRestart(r clientFacade) {
 
 	var (
@@ -54,7 +55,7 @@ func HandleClientRestart(r clientFacade) {
 LOOP:
 	for {
 		select {
-		case <-r.GetDone():
+		case <-r.Done():
 			logger.Warnf("(ETCDV3ProviderRegistry)reconnectETCDV3 goroutine exit now...")
 			break LOOP
 			// re-register all services
@@ -71,7 +72,7 @@ LOOP:
 			failTimes = 0
 			for {
 				select {
-				case <-r.GetDone():
+				case <-r.Done():
 					logger.Warnf("(ETCDV3ProviderRegistry)reconnectETCDRegistry goroutine exit now...")
 					break LOOP
 				case <-getty.GetTimeWheel().After(timeSecondDuration(failTimes * ConnDelay)): // avoid connect frequent
diff --git a/remoting/etcdv3/listener.go b/remoting/etcdv3/listener.go
index 6c9905a1000561cfe394801fb66e1a2548e171d7..e69f7269879138d7579460fd059d28c297960d7e 100644
--- a/remoting/etcdv3/listener.go
+++ b/remoting/etcdv3/listener.go
@@ -33,6 +33,7 @@ import (
 	"github.com/apache/dubbo-go/remoting"
 )
 
+// EventListener ...
 type EventListener struct {
 	client     *Client
 	keyMapLock sync.Mutex
@@ -40,6 +41,7 @@ type EventListener struct {
 	wg         sync.WaitGroup
 }
 
+// NewEventListener ...
 func NewEventListener(client *Client) *EventListener {
 	return &EventListener{
 		client: client,
@@ -47,7 +49,7 @@ func NewEventListener(client *Client) *EventListener {
 	}
 }
 
-// Listen on a spec key
+// ListenServiceNodeEvent Listen on a spec key
 // this method will return true when spec key deleted,
 // this method will return false when deep layer connection lose
 func (l *EventListener) ListenServiceNodeEvent(key string, listener ...remoting.DataListener) bool {
@@ -134,7 +136,7 @@ func (l *EventListener) handleEvents(event *clientv3.Event, listeners ...remotin
 	panic("unreachable")
 }
 
-// Listen on a set of key with spec prefix
+// ListenServiceNodeEventWithPrefix Listen on a set of key with spec prefix
 func (l *EventListener) ListenServiceNodeEventWithPrefix(prefix string, listener ...remoting.DataListener) {
 
 	l.wg.Add(1)
@@ -180,7 +182,7 @@ func timeSecondDuration(sec int) time.Duration {
 	return time.Duration(sec) * time.Second
 }
 
-// this func is invoked by etcdv3 ConsumerRegistry::Registe/ etcdv3 ConsumerRegistry::get/etcdv3 ConsumerRegistry::getListener
+// ListenServiceEvent is invoked by etcdv3 ConsumerRegistry::Registe/ etcdv3 ConsumerRegistry::get/etcdv3 ConsumerRegistry::getListener
 // registry.go:Listen -> listenServiceEvent -> listenDirEvent -> ListenServiceNodeEvent
 //                            |
 //                            --------> ListenServiceNodeEvent
@@ -229,6 +231,7 @@ func (l *EventListener) ListenServiceEvent(key string, listener remoting.DataLis
 	}(key)
 }
 
+// Close ...
 func (l *EventListener) Close() {
 	l.wg.Wait()
 }
diff --git a/remoting/kubernetes/client.go b/remoting/kubernetes/client.go
index beb83b902e7acf1ad8dc4f51abbaf5f01b6bcbcd..e4e9f8eb8cc2fe0b403a87dcf2975913b033ace5 100644
--- a/remoting/kubernetes/client.go
+++ b/remoting/kubernetes/client.go
@@ -55,8 +55,7 @@ const (
 )
 
 var (
-	ErrDubboLabelAlreadyExist       = perrors.New("dubbo label already exist")
-	ErrDubboAnnotationsAlreadyExist = perrors.New("dubbo annotations already exist")
+	ErrDubboLabelAlreadyExist = perrors.New("dubbo label already exist")
 )
 
 type Client struct {
diff --git a/remoting/listener.go b/remoting/listener.go
index 8d1e357d37ff92e7bf60121133998dc1745c9af8..3713ba0ccf9d98d4470741785a9490e657cf051c 100644
--- a/remoting/listener.go
+++ b/remoting/listener.go
@@ -21,6 +21,7 @@ import (
 	"fmt"
 )
 
+// DataListener ...
 type DataListener interface {
 	DataChange(eventType Event) bool //bool is return for interface implement is interesting
 }
@@ -29,11 +30,15 @@ type DataListener interface {
 // event type
 //////////////////////////////////////////
 
+// EventType ...
 type EventType int
 
 const (
+	// EventTypeAdd ...
 	EventTypeAdd = iota
+	// EventTypeDel ...
 	EventTypeDel
+	// EventTypeUpdate ...
 	EventTypeUpdate
 )
 
@@ -51,6 +56,7 @@ func (t EventType) String() string {
 // service event
 //////////////////////////////////////////
 
+// Event ...
 type Event struct {
 	Path    string
 	Action  EventType
diff --git a/remoting/zookeeper/client.go b/remoting/zookeeper/client.go
index a7fc568f567d720448d0be63c592fae5f8df9bbf..21486aab59c3f9b44c25b68d7433f864a990149a 100644
--- a/remoting/zookeeper/client.go
+++ b/remoting/zookeeper/client.go
@@ -25,8 +25,8 @@ import (
 )
 
 import (
+	"github.com/dubbogo/go-zookeeper/zk"
 	perrors "github.com/pkg/errors"
-	"github.com/samuel/go-zookeeper/zk"
 )
 
 import (
@@ -35,14 +35,19 @@ import (
 )
 
 const (
-	ConnDelay    = 3
+	// ConnDelay connection delay interval
+	ConnDelay = 3
+	// MaxFailTimes max fail times
 	MaxFailTimes = 15
 )
 
 var (
 	errNilZkClientConn = perrors.New("zookeeperclient{conn} is nil")
+	errNilChildren     = perrors.Errorf("has none children")
+	errNilNode         = perrors.Errorf("node does not exist")
 )
 
+// ZookeeperClient ...
 type ZookeeperClient struct {
 	name          string
 	ZkAddrs       []string
@@ -54,6 +59,7 @@ type ZookeeperClient struct {
 	eventRegistry map[string][]*chan struct{}
 }
 
+// StateToString ...
 func StateToString(state zk.State) string {
 	switch state {
 	case zk.StateDisconnected:
@@ -85,6 +91,7 @@ func StateToString(state zk.State) string {
 	return "zookeeper unknown state"
 }
 
+// Options ...
 type Options struct {
 	zkName string
 	client *ZookeeperClient
@@ -92,14 +99,17 @@ type Options struct {
 	ts *zk.TestCluster
 }
 
+// Option ...
 type Option func(*Options)
 
+// WithZkName ...
 func WithZkName(name string) Option {
 	return func(opt *Options) {
 		opt.zkName = name
 	}
 }
 
+// ValidateZookeeperClient ...
 func ValidateZookeeperClient(container zkClientFacade, opts ...Option) error {
 	var (
 		err error
@@ -173,12 +183,14 @@ func newZookeeperClient(name string, zkAddrs []string, timeout time.Duration) (*
 	return z, nil
 }
 
+// WithTestCluster ...
 func WithTestCluster(ts *zk.TestCluster) Option {
 	return func(opt *Options) {
 		opt.ts = ts
 	}
 }
 
+// NewMockZookeeperClient ...
 func NewMockZookeeperClient(name string, timeout time.Duration, opts ...Option) (*zk.TestCluster, *ZookeeperClient, <-chan zk.Event, error) {
 	var (
 		err   error
@@ -224,6 +236,7 @@ func NewMockZookeeperClient(name string, timeout time.Duration, opts ...Option)
 	return ts, z, event, nil
 }
 
+// HandleZkEvent ...
 func (z *ZookeeperClient) HandleZkEvent(session <-chan zk.Event) {
 	var (
 		state int
@@ -248,11 +261,13 @@ LOOP:
 				logger.Warnf("zk{addr:%s} state is StateDisconnected, so close the zk client{name:%s}.", z.ZkAddrs, z.name)
 				z.stop()
 				z.Lock()
-				if z.Conn != nil {
-					z.Conn.Close()
-					z.Conn = nil
-				}
+				conn := z.Conn
+				z.Conn = nil
 				z.Unlock()
+				if conn != nil {
+					conn.Close()
+				}
+
 				break LOOP
 			case (int)(zk.EventNodeDataChanged), (int)(zk.EventNodeChildrenChanged):
 				logger.Infof("zkClient{%s} get zk node changed event{path:%s}", z.name, event.Path)
@@ -282,6 +297,7 @@ LOOP:
 	}
 }
 
+// RegisterEvent ...
 func (z *ZookeeperClient) RegisterEvent(zkPath string, event *chan struct{}) {
 	if zkPath == "" || event == nil {
 		return
@@ -290,11 +306,13 @@ func (z *ZookeeperClient) RegisterEvent(zkPath string, event *chan struct{}) {
 	z.Lock()
 	a := z.eventRegistry[zkPath]
 	a = append(a, event)
+
 	z.eventRegistry[zkPath] = a
 	logger.Debugf("zkClient{%s} register event{path:%s, ptr:%p}", z.name, zkPath, event)
 	z.Unlock()
 }
 
+// UnregisterEvent ...
 func (z *ZookeeperClient) UnregisterEvent(zkPath string, event *chan struct{}) {
 	if zkPath == "" {
 		return
@@ -321,6 +339,7 @@ func (z *ZookeeperClient) UnregisterEvent(zkPath string, event *chan struct{}) {
 	}
 }
 
+// Done ...
 func (z *ZookeeperClient) Done() <-chan struct{} {
 	return z.exit
 }
@@ -336,6 +355,7 @@ func (z *ZookeeperClient) stop() bool {
 	return false
 }
 
+// ZkConnValid ...
 func (z *ZookeeperClient) ZkConnValid() bool {
 	select {
 	case <-z.exit:
@@ -353,6 +373,7 @@ func (z *ZookeeperClient) ZkConnValid() bool {
 	return valid
 }
 
+// Close ...
 func (z *ZookeeperClient) Close() {
 	if z == nil {
 		return
@@ -361,14 +382,17 @@ func (z *ZookeeperClient) Close() {
 	z.stop()
 	z.Wait.Wait()
 	z.Lock()
-	if z.Conn != nil {
-		z.Conn.Close()
-		z.Conn = nil
-	}
+	conn := z.Conn
+	z.Conn = nil
 	z.Unlock()
+	if conn != nil {
+		conn.Close()
+	}
+
 	logger.Warnf("zkClient{name:%s, zk addr:%s} exit now.", z.name, z.ZkAddrs)
 }
 
+// Create ...
 func (z *ZookeeperClient) Create(basePath string) error {
 	var (
 		err     error
@@ -380,13 +404,15 @@ func (z *ZookeeperClient) Create(basePath string) error {
 		tmpPath = path.Join(tmpPath, "/", str)
 		err = errNilZkClientConn
 		z.Lock()
-		if z.Conn != nil {
-			_, err = z.Conn.Create(tmpPath, []byte(""), 0, zk.WorldACL(zk.PermAll))
-		}
+		conn := z.Conn
 		z.Unlock()
+		if conn != nil {
+			_, err = conn.Create(tmpPath, []byte(""), 0, zk.WorldACL(zk.PermAll))
+		}
+
 		if err != nil {
 			if err == zk.ErrNodeExists {
-				logger.Infof("zk.create(\"%s\") exists\n", tmpPath)
+				logger.Debugf("zk.create(\"%s\") exists\n", tmpPath)
 			} else {
 				logger.Errorf("zk.create(\"%s\") error(%v)\n", tmpPath, perrors.WithStack(err))
 				return perrors.WithMessagef(err, "zk.Create(path:%s)", basePath)
@@ -397,6 +423,7 @@ func (z *ZookeeperClient) Create(basePath string) error {
 	return nil
 }
 
+// Delete ...
 func (z *ZookeeperClient) Delete(basePath string) error {
 	var (
 		err error
@@ -404,14 +431,16 @@ func (z *ZookeeperClient) Delete(basePath string) error {
 
 	err = errNilZkClientConn
 	z.Lock()
-	if z.Conn != nil {
-		err = z.Conn.Delete(basePath, -1)
-	}
+	conn := z.Conn
 	z.Unlock()
+	if conn != nil {
+		err = conn.Delete(basePath, -1)
+	}
 
 	return perrors.WithMessagef(err, "Delete(basePath:%s)", basePath)
 }
 
+// RegisterTemp ...
 func (z *ZookeeperClient) RegisterTemp(basePath string, node string) (string, error) {
 	var (
 		err     error
@@ -424,10 +453,12 @@ func (z *ZookeeperClient) RegisterTemp(basePath string, node string) (string, er
 	data = []byte("")
 	zkPath = path.Join(basePath) + "/" + node
 	z.Lock()
-	if z.Conn != nil {
-		tmpPath, err = z.Conn.Create(zkPath, data, zk.FlagEphemeral, zk.WorldACL(zk.PermAll))
-	}
+	conn := z.Conn
 	z.Unlock()
+	if conn != nil {
+		tmpPath, err = conn.Create(zkPath, data, zk.FlagEphemeral, zk.WorldACL(zk.PermAll))
+	}
+
 	//if err != nil && err != zk.ErrNodeExists {
 	if err != nil {
 		logger.Warnf("conn.Create(\"%s\", zk.FlagEphemeral) = error(%v)\n", zkPath, perrors.WithStack(err))
@@ -438,6 +469,7 @@ func (z *ZookeeperClient) RegisterTemp(basePath string, node string) (string, er
 	return tmpPath, nil
 }
 
+// RegisterTempSeq ...
 func (z *ZookeeperClient) RegisterTempSeq(basePath string, data []byte) (string, error) {
 	var (
 		err     error
@@ -446,15 +478,17 @@ func (z *ZookeeperClient) RegisterTempSeq(basePath string, data []byte) (string,
 
 	err = errNilZkClientConn
 	z.Lock()
-	if z.Conn != nil {
-		tmpPath, err = z.Conn.Create(
+	conn := z.Conn
+	z.Unlock()
+	if conn != nil {
+		tmpPath, err = conn.Create(
 			path.Join(basePath)+"/",
 			data,
 			zk.FlagEphemeral|zk.FlagSequence,
 			zk.WorldACL(zk.PermAll),
 		)
 	}
-	z.Unlock()
+
 	logger.Debugf("zookeeperClient.RegisterTempSeq(basePath{%s}) = tempPath{%s}", basePath, tmpPath)
 	if err != nil && err != zk.ErrNodeExists {
 		logger.Errorf("zkClient{%s} conn.Create(\"%s\", \"%s\", zk.FlagEphemeral|zk.FlagSequence) error(%v)\n",
@@ -466,37 +500,44 @@ func (z *ZookeeperClient) RegisterTempSeq(basePath string, data []byte) (string,
 	return tmpPath, nil
 }
 
+// GetChildrenW ...
 func (z *ZookeeperClient) GetChildrenW(path string) ([]string, <-chan zk.Event, error) {
 	var (
 		err      error
 		children []string
 		stat     *zk.Stat
-		event    <-chan zk.Event
+		watcher  *zk.Watcher
 	)
 
 	err = errNilZkClientConn
 	z.Lock()
-	if z.Conn != nil {
-		children, stat, event, err = z.Conn.ChildrenW(path)
-	}
+	conn := z.Conn
 	z.Unlock()
+	if conn != nil {
+		children, stat, watcher, err = conn.ChildrenW(path)
+	}
+
 	if err != nil {
+		if err == zk.ErrNoChildrenForEphemerals {
+			return nil, nil, errNilChildren
+		}
 		if err == zk.ErrNoNode {
-			return nil, nil, perrors.Errorf("path{%s} has none children", path)
+			return nil, nil, errNilNode
 		}
 		logger.Errorf("zk.ChildrenW(path{%s}) = error(%v)", path, err)
 		return nil, nil, perrors.WithMessagef(err, "zk.ChildrenW(path:%s)", path)
 	}
 	if stat == nil {
-		return nil, nil, perrors.Errorf("path{%s} has none children", path)
+		return nil, nil, perrors.Errorf("path{%s} get stat is nil", path)
 	}
 	if len(children) == 0 {
-		return nil, nil, perrors.Errorf("path{%s} has none children", path)
+		return nil, nil, errNilChildren
 	}
 
-	return children, event, nil
+	return children, watcher.EvtCh, nil
 }
 
+// GetChildren ...
 func (z *ZookeeperClient) GetChildren(path string) ([]string, error) {
 	var (
 		err      error
@@ -506,10 +547,12 @@ func (z *ZookeeperClient) GetChildren(path string) ([]string, error) {
 
 	err = errNilZkClientConn
 	z.Lock()
-	if z.Conn != nil {
-		children, stat, err = z.Conn.Children(path)
-	}
+	conn := z.Conn
 	z.Unlock()
+	if conn != nil {
+		children, stat, err = conn.Children(path)
+	}
+
 	if err != nil {
 		if err == zk.ErrNoNode {
 			return nil, perrors.Errorf("path{%s} has none children", path)
@@ -521,25 +564,28 @@ func (z *ZookeeperClient) GetChildren(path string) ([]string, error) {
 		return nil, perrors.Errorf("path{%s} has none children", path)
 	}
 	if len(children) == 0 {
-		return nil, perrors.Errorf("path{%s} has none children", path)
+		return nil, errNilChildren
 	}
 
 	return children, nil
 }
 
+// ExistW ...
 func (z *ZookeeperClient) ExistW(zkPath string) (<-chan zk.Event, error) {
 	var (
-		exist bool
-		err   error
-		event <-chan zk.Event
+		exist   bool
+		err     error
+		watcher *zk.Watcher
 	)
 
 	err = errNilZkClientConn
 	z.Lock()
-	if z.Conn != nil {
-		exist, _, event, err = z.Conn.ExistsW(zkPath)
-	}
+	conn := z.Conn
 	z.Unlock()
+	if conn != nil {
+		exist, _, watcher, err = conn.ExistsW(zkPath)
+	}
+
 	if err != nil {
 		logger.Warnf("zkClient{%s}.ExistsW(path{%s}) = error{%v}.", z.name, zkPath, perrors.WithStack(err))
 		return nil, perrors.WithMessagef(err, "zk.ExistsW(path:%s)", zkPath)
@@ -549,9 +595,10 @@ func (z *ZookeeperClient) ExistW(zkPath string) (<-chan zk.Event, error) {
 		return nil, perrors.Errorf("zkClient{%s} App zk path{%s} does not exist.", z.name, zkPath)
 	}
 
-	return event, nil
+	return watcher.EvtCh, nil
 }
 
+// GetContent ...
 func (z *ZookeeperClient) GetContent(zkPath string) ([]byte, *zk.Stat, error) {
 	return z.Conn.Get(zkPath)
 }
diff --git a/remoting/zookeeper/client_test.go b/remoting/zookeeper/client_test.go
index f1bd0c2cb38669ad968bd83efae166a4432c6e2d..cb41eb326be95470e39694fc5df233fdf073b905 100644
--- a/remoting/zookeeper/client_test.go
+++ b/remoting/zookeeper/client_test.go
@@ -24,7 +24,7 @@ import (
 )
 
 import (
-	"github.com/samuel/go-zookeeper/zk"
+	"github.com/dubbogo/go-zookeeper/zk"
 	"github.com/stretchr/testify/assert"
 )
 
@@ -133,3 +133,12 @@ func TestRegisterTempSeq(t *testing.T) {
 	states := []zk.State{zk.StateConnecting, zk.StateConnected, zk.StateHasSession}
 	verifyEventStateOrder(t, event, states, "event channel")
 }
+
+func Test_UnregisterEvent(t *testing.T) {
+	client := &ZookeeperClient{}
+	client.eventRegistry = make(map[string][]*chan struct{})
+	array := []*chan struct{}{}
+	array = append(array, new(chan struct{}))
+	client.eventRegistry["test"] = array
+	client.UnregisterEvent("test", new(chan struct{}))
+}
diff --git a/remoting/zookeeper/facade.go b/remoting/zookeeper/facade.go
index cdc7ead61226906a629fdb99b6b966ada5ee5253..055db4f716a914354d1bada653fbc0a850b615b5 100644
--- a/remoting/zookeeper/facade.go
+++ b/remoting/zookeeper/facade.go
@@ -35,11 +35,12 @@ type zkClientFacade interface {
 	SetZkClient(*ZookeeperClient)
 	ZkClientLock() *sync.Mutex
 	WaitGroup() *sync.WaitGroup //for wait group control, zk client listener & zk client container
-	GetDone() chan struct{}     //for zk client control
+	Done() chan struct{}        //for zk client control
 	RestartCallBack() bool
 	common.Node
 }
 
+// HandleClientRestart ...
 func HandleClientRestart(r zkClientFacade) {
 	var (
 		err error
@@ -51,7 +52,7 @@ func HandleClientRestart(r zkClientFacade) {
 LOOP:
 	for {
 		select {
-		case <-r.GetDone():
+		case <-r.Done():
 			logger.Warnf("(ZkProviderRegistry)reconnectZkRegistry goroutine exit now...")
 			break LOOP
 			// re-register all services
@@ -67,7 +68,7 @@ LOOP:
 			failTimes = 0
 			for {
 				select {
-				case <-r.GetDone():
+				case <-r.Done():
 					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 58e0d69dcfd9bf645c147f6e920e56ed5f3951eb..a41f6cd3230700332519ce1c2d3489bfcc4b6ef0 100644
--- a/remoting/zookeeper/facade_test.go
+++ b/remoting/zookeeper/facade_test.go
@@ -18,13 +18,12 @@
 package zookeeper
 
 import (
-	"context"
 	"sync"
 	"testing"
 	"time"
 )
 import (
-	"github.com/samuel/go-zookeeper/zk"
+	"github.com/dubbogo/go-zookeeper/zk"
 	"github.com/stretchr/testify/assert"
 )
 import (
@@ -55,7 +54,7 @@ func (r *mockFacade) WaitGroup() *sync.WaitGroup {
 	return &r.wg
 }
 
-func (r *mockFacade) GetDone() chan struct{} {
+func (r *mockFacade) Done() chan struct{} {
 	return r.done
 }
 
@@ -71,6 +70,7 @@ func (r *mockFacade) Destroy() {
 func (r *mockFacade) RestartCallBack() bool {
 	return true
 }
+
 func (r *mockFacade) IsAvailable() bool {
 	return true
 }
@@ -79,7 +79,7 @@ func Test_Facade(t *testing.T) {
 	ts, z, event, err := NewMockZookeeperClient("test", 15*time.Second)
 	assert.NoError(t, err)
 	defer ts.Stop()
-	url, _ := common.NewURL(context.Background(), "mock://127.0.0.1")
+	url, _ := common.NewURL("mock://127.0.0.1")
 	mock := &mockFacade{client: z, URL: &url}
 	go HandleClientRestart(mock)
 	states := []zk.State{zk.StateConnecting, zk.StateConnected, zk.StateHasSession}
diff --git a/remoting/zookeeper/listener.go b/remoting/zookeeper/listener.go
index 9521ea749027582c015ac998a6f6f68d350cc3bc..77aa05ee9eada327475fa5bf86c7af2c65de0ef2 100644
--- a/remoting/zookeeper/listener.go
+++ b/remoting/zookeeper/listener.go
@@ -19,21 +19,24 @@ package zookeeper
 
 import (
 	"path"
+	"strings"
 	"sync"
 	"time"
 )
 
 import (
 	"github.com/dubbogo/getty"
+	"github.com/dubbogo/go-zookeeper/zk"
 	perrors "github.com/pkg/errors"
-	"github.com/samuel/go-zookeeper/zk"
 )
 
 import (
+	"github.com/apache/dubbo-go/common/constant"
 	"github.com/apache/dubbo-go/common/logger"
 	"github.com/apache/dubbo-go/remoting"
 )
 
+// ZkEventListener ...
 type ZkEventListener struct {
 	client      *ZookeeperClient
 	pathMapLock sync.Mutex
@@ -41,6 +44,7 @@ type ZkEventListener struct {
 	wg          sync.WaitGroup
 }
 
+// NewZkEventListener ...
 func NewZkEventListener(client *ZookeeperClient) *ZkEventListener {
 	return &ZkEventListener{
 		client:  client,
@@ -48,10 +52,12 @@ func NewZkEventListener(client *ZookeeperClient) *ZkEventListener {
 	}
 }
 
+// SetClient ...
 func (l *ZkEventListener) SetClient(client *ZookeeperClient) {
 	l.client = client
 }
 
+// ListenServiceNodeEvent ...
 func (l *ZkEventListener) ListenServiceNodeEvent(zkPath string, listener ...remoting.DataListener) bool {
 	l.wg.Add(1)
 	defer l.wg.Done()
@@ -106,8 +112,17 @@ func (l *ZkEventListener) handleZkNodeEvent(zkPath string, children []string, li
 
 	newChildren, err := l.client.GetChildren(zkPath)
 	if err != nil {
-		logger.Errorf("path{%s} child nodes changed, zk.Children() = error{%v}", zkPath, perrors.WithStack(err))
-		return
+		if err == errNilChildren {
+			content, _, err := l.client.Conn.Get(zkPath)
+			if err != nil {
+				logger.Errorf("Get new node path {%v} 's content error,message is  {%v}", zkPath, perrors.WithStack(err))
+			} else {
+				listener.DataChange(remoting.Event{Path: zkPath, Action: remoting.EventTypeUpdate, Content: string(content)})
+			}
+
+		} else {
+			logger.Errorf("path{%s} child nodes changed, zk.Children() = error{%v}", zkPath, perrors.WithStack(err))
+		}
 	}
 
 	// a node was added -- listen the new node
@@ -177,7 +192,7 @@ func (l *ZkEventListener) listenDirEvent(zkPath string, listener remoting.DataLi
 			if MaxFailTimes <= failTimes {
 				failTimes = MaxFailTimes
 			}
-			logger.Warnf("listenDirEvent(path{%s}) = error{%v}", zkPath, err)
+			logger.Infof("listenDirEvent(path{%s}) = error{%v}", zkPath, err)
 			// clear the event channel
 		CLEAR:
 			for {
@@ -188,6 +203,11 @@ func (l *ZkEventListener) listenDirEvent(zkPath string, listener remoting.DataLi
 				}
 			}
 			l.client.RegisterEvent(zkPath, &event)
+			if err == errNilNode {
+				logger.Warnf("listenDirEvent(path{%s}) got errNilNode,so exit listen", zkPath)
+				l.client.UnregisterEvent(zkPath, &event)
+				return
+			}
 			select {
 			case <-getty.GetTimeWheel().After(timeSecondDuration(failTimes * ConnDelay)):
 				l.client.UnregisterEvent(zkPath, &event)
@@ -225,7 +245,7 @@ func (l *ZkEventListener) listenDirEvent(zkPath string, listener remoting.DataLi
 			if err != nil {
 				logger.Errorf("Get new node path {%v} 's content error,message is  {%v}", dubboPath, perrors.WithStack(err))
 			}
-			logger.Infof("Get children!{%s}", dubboPath)
+			logger.Debugf("Get children!{%s}", dubboPath)
 			if !listener.DataChange(remoting.Event{Path: dubboPath, Action: remoting.EventTypeAdd, Content: string(content)}) {
 				continue
 			}
@@ -238,10 +258,14 @@ func (l *ZkEventListener) listenDirEvent(zkPath string, listener remoting.DataLi
 			}(dubboPath, listener)
 
 			//listen sub path recursive
-			go func(zkPath string, listener remoting.DataListener) {
-				l.listenDirEvent(zkPath, listener)
-				logger.Warnf("listenDirEvent(zkPath{%s}) goroutine exit now", zkPath)
-			}(dubboPath, listener)
+			//if zkPath is end of "providers/ & consumers/" we do not listen children dir
+			if strings.LastIndex(zkPath, constant.PROVIDER_CATEGORY) == -1 &&
+				strings.LastIndex(zkPath, constant.CONSUMER_CATEGORY) == -1 {
+				go func(zkPath string, listener remoting.DataListener) {
+					l.listenDirEvent(zkPath, listener)
+					logger.Warnf("listenDirEvent(zkPath{%s}) goroutine exit now", zkPath)
+				}(dubboPath, listener)
+			}
 		}
 		select {
 		case zkEvent = <-childEventCh:
@@ -262,55 +286,11 @@ func timeSecondDuration(sec int) time.Duration {
 	return time.Duration(sec) * time.Second
 }
 
-// this func is invoked by ZkConsumerRegistry::Register/ZkConsumerRegistry::get/ZkConsumerRegistry::getListener
+// ListenServiceEvent is invoked by ZkConsumerRegistry::Register/ZkConsumerRegistry::get/ZkConsumerRegistry::getListener
 // registry.go:Listen -> listenServiceEvent -> listenDirEvent -> ListenServiceNodeEvent
 //                            |
 //                            --------> ListenServiceNodeEvent
 func (l *ZkEventListener) ListenServiceEvent(zkPath string, listener remoting.DataListener) {
-	var (
-		err       error
-		dubboPath string
-		children  []string
-	)
-
-	l.pathMapLock.Lock()
-	_, ok := l.pathMap[zkPath]
-	l.pathMapLock.Unlock()
-	if ok {
-		logger.Warnf("@zkPath %s has already been listened.", zkPath)
-		return
-	}
-
-	l.pathMapLock.Lock()
-	l.pathMap[zkPath] = struct{}{}
-	l.pathMapLock.Unlock()
-
-	logger.Infof("listen dubbo provider path{%s} event and wait to get all provider zk nodes", zkPath)
-	children, err = l.client.GetChildren(zkPath)
-	if err != nil {
-		children = nil
-		logger.Warnf("fail to get children of zk path{%s}", zkPath)
-	}
-
-	for _, c := range children {
-		// listen l service node
-		dubboPath = path.Join(zkPath, c)
-		content, _, err := l.client.Conn.Get(dubboPath)
-		if err != nil {
-			logger.Errorf("Get new node path {%v} 's content error,message is  {%v}", dubboPath, perrors.WithStack(err))
-		}
-		if !listener.DataChange(remoting.Event{Path: dubboPath, Action: remoting.EventTypeAdd, Content: string(content)}) {
-			continue
-		}
-		logger.Infof("listen dubbo service key{%s}", dubboPath)
-		go func(zkPath string, listener remoting.DataListener) {
-			if l.ListenServiceNodeEvent(zkPath) {
-				listener.DataChange(remoting.Event{Path: zkPath, Action: remoting.EventTypeDel})
-			}
-			logger.Warnf("listenSelf(zk path{%s}) goroutine exit now", zkPath)
-		}(dubboPath, listener)
-	}
-
 	logger.Infof("listen dubbo path{%s}", zkPath)
 	go func(zkPath string, listener remoting.DataListener) {
 		l.listenDirEvent(zkPath, listener)
@@ -322,6 +302,7 @@ func (l *ZkEventListener) valid() bool {
 	return l.client.ZkConnValid()
 }
 
+// Close ...
 func (l *ZkEventListener) Close() {
 	l.wg.Wait()
 }
diff --git a/remoting/zookeeper/listener_test.go b/remoting/zookeeper/listener_test.go
index a90fbad05ae787f36d38607b0a73374d874e6994..7301cd52c392b6950b3a49f78e8124eae532b083 100644
--- a/remoting/zookeeper/listener_test.go
+++ b/remoting/zookeeper/listener_test.go
@@ -18,12 +18,13 @@
 package zookeeper
 
 import (
+	"net/url"
 	"sync"
 	"testing"
 	"time"
 )
 import (
-	"github.com/samuel/go-zookeeper/zk"
+	"github.com/dubbogo/go-zookeeper/zk"
 	"github.com/stretchr/testify/assert"
 )
 import (
@@ -65,6 +66,7 @@ func initZkData(t *testing.T) (*zk.TestCluster, *ZookeeperClient, <-chan zk.Even
 
 	return ts, client, event
 }
+
 func TestListener(t *testing.T) {
 	changedData := `
 	dubbo.consumer.request_timeout=3s
@@ -96,12 +98,11 @@ func TestListener(t *testing.T) {
 	listener := NewZkEventListener(client)
 	dataListener := &mockDataListener{client: client, changedData: changedData, wait: &wait}
 	listener.ListenServiceEvent("/dubbo", dataListener)
-
+	time.Sleep(1 * time.Second)
 	_, err := client.Conn.Set("/dubbo/dubbo.properties", []byte(changedData), 1)
 	assert.NoError(t, err)
 	wait.Wait()
 	assert.Equal(t, changedData, dataListener.eventList[1].Content)
-	client.Close()
 
 }
 
@@ -122,3 +123,9 @@ func (m *mockDataListener) DataChange(eventType remoting.Event) bool {
 	}
 	return true
 }
+
+func TestZkPath(t *testing.T) {
+	zkPath := "io.grpc.examples.helloworld.GreeterGrpc$IGreeter"
+	zkPath = url.QueryEscape(zkPath)
+	assert.Equal(t, zkPath, "io.grpc.examples.helloworld.GreeterGrpc%24IGreeter")
+}