diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 3eb1ec055f18f29a7886e01c24e10c97f88fb1e8..9daa31016dca8890ef8ce64396e0f6ebe9a41462 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -1,4 +1,5 @@
-<!--  Thanks for sending a pull request! 
+<!--  Thanks for sending a pull request!
+Read https://github.com/apache/dubbo-go/blob/master/contributing.md before commit pull request. 
 -->
 
 **What this PR does**:
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000000000000000000000000000000000000..6ce02861026b4ee7e13f199c29ca74a9f78ffd32
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,20 @@
+# To get started with Dependabot version updates, you'll need to specify which
+# package ecosystems to update and where the package manifests are located.
+# Please see the documentation for all configuration options:
+# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
+
+version: 2
+updates:
+  - package-ecosystem: "gomod" # See documentation for possible values
+    directory: "/" # Location of package manifests
+    schedule:
+      interval: "weekly"
+    target-branch: "develop"
+
+  - package-ecosystem: "github-actions"
+    # Workflow files stored in the
+    # default location of `.github/workflows`
+    directory: "/"
+    schedule:
+      interval: "weekly"
+    target-branch: "develop"
\ No newline at end of file
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
new file mode 100644
index 0000000000000000000000000000000000000000..f2e185cc1eb21e0ac3458f06ef21c407c8e0d06f
--- /dev/null
+++ b/.github/workflows/codeql-analysis.yml
@@ -0,0 +1,54 @@
+name: "CodeQL"
+
+on:
+  push:
+    branches: [master, ]
+  pull_request:
+    # The branches below must be a subset of the branches above
+    branches: [master]
+  schedule:
+    - cron: '0 4 * * 5'
+
+jobs:
+  analyse:
+    name: Analyse
+    runs-on: ubuntu-latest
+
+    steps:
+    - name: Checkout repository
+      uses: actions/checkout@v2
+      with:
+        # We must fetch at least the immediate parents so that if this is
+        # a pull request then we can checkout the head.
+        fetch-depth: 2
+
+    # If this run was triggered by a pull request event, then checkout
+    # the head of the pull request instead of the merge commit.
+    - run: git checkout HEAD^2
+      if: ${{ github.event_name == 'pull_request' }}
+
+    # Initializes the CodeQL tools for scanning.
+    - name: Initialize CodeQL
+      uses: github/codeql-action/init@v1
+      # Override language selection by uncommenting this and choosing your languages
+      # with:
+      #   languages: go, javascript, csharp, python, cpp, java
+
+    # Autobuild attempts to build any compiled languages  (C/C++, C#, or Java).
+    # If this step fails, then you should remove it and run the build manually (see below)
+    - name: Autobuild
+      uses: github/codeql-action/autobuild@v1
+
+    # 鈩癸笍 Command-line programs to run using the OS shell.
+    # 馃摎 https://git.io/JvXDl
+
+    # 鉁忥笍 If the Autobuild fails above, remove it and uncomment the following three lines
+    #    and modify them (or add more) to build your code if your project
+    #    uses a compiled language
+
+    #- run: |
+    #   make bootstrap
+    #   make release
+
+    - name: Perform CodeQL Analysis
+      uses: github/codeql-action/analyze@v1
diff --git a/.github/workflows/github-actions.yml b/.github/workflows/github-actions.yml
new file mode 100644
index 0000000000000000000000000000000000000000..79f6f2066e38f91d862453c39f0c25797546a42a
--- /dev/null
+++ b/.github/workflows/github-actions.yml
@@ -0,0 +1,70 @@
+name: CI
+
+on:
+  push:
+    branches: [master, develop]
+  pull_request:
+    branches: "*"
+
+jobs:
+
+  build:
+    name: ${{ matrix.os }} - Go ${{ matrix.go_version }}
+    runs-on: ${{ matrix.os }}
+    strategy:
+      # If you want to matrix build , you can append the following list.
+      matrix:
+        go_version:
+          - 1.13
+        os:
+          - ubuntu-latest
+
+    env:
+      DING_TOKEN: ${{ secrets.DING_TOKEN }}
+      DING_SIGN: ${{ secrets.DING_SIGN }}
+
+    steps:
+
+    - name: Set up Go 1.x
+      uses: actions/setup-go@v2
+      with:
+        go-version: ${{ matrix.go_version }}
+      id: go
+
+    - name: Check out code into the Go module directory
+      uses: actions/checkout@v2
+
+    - name: Cache dependencies
+      uses: actions/cache@v2
+      with:
+        # Cache
+        path: ~/go/pkg/mod
+        # Cache key
+        key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
+        # An ordered list of keys to use for restoring the cache if no cache hit occurred for key
+        restore-keys: |
+          ${{ runner.os }}-go-
+
+    - name: Get dependencies
+      run: |
+        if [ -f Gopkg.toml ]; then
+            curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
+            dep ensure
+        else
+            go get -v -t -d ./...
+        fi
+
+    - name: Verify
+      run: |
+        make verify
+
+    - name: Integrate Test
+      run: |
+        chmod +x integrate_test.sh && ./integrate_test.sh
+
+    - name: Post Coverage
+      run: bash <(curl -s https://codecov.io/bash)
+
+    - name: Hello world
+      run: echo Hello world ${{ secrets.DING_TOKEN }} ${{ secrets.DING_SIGN }}
+
diff --git a/.gitignore b/.gitignore
index fabff68b874df4c2a7de15ce91798e9bb963b358..898962e244d0ba1d030837f76fed47576f38ab5e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -29,8 +29,12 @@ config_center/zookeeper/zookeeper-4unittest/
 registry/zookeeper/zookeeper-4unittest/
 metadata/report/zookeeper/zookeeper-4unittest/
 registry/consul/agent*
+metadata/report/consul/agent*
+remoting/consul/agent*
 config_center/apollo/mockDubbog.properties.json
 
 # vim stuff
 *~
 .*.sw?
+/license-header-checker-linux/
+/license-header-checker-linux.zip
diff --git a/.travis.yml b/.travis.yml
index 566c88ece05bd80175eea2d1de8fd061a279e273..4f79ecf3bc1439e00c88c4d0de74cc7a4bf26909 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -15,16 +15,9 @@ install: true
 
 # define ci-stage
 script:
-  # license-check
-  - echo 'start license check'
   - go fmt ./... && [[ -z `git status -s` ]]
-  - sh before_validate_license.sh
-  - chmod u+x /tmp/tools/license/license-header-checker
-  - /tmp/tools/license/license-header-checker -v -a -r -i vendor  /tmp/tools/license/license.txt . go  && [[ -z `git status -s` ]]
-  # unit-test
-  - echo 'start unit-test'
-  - chmod u+x before_ut.sh && ./before_ut.sh
-  - go mod vendor && go test ./... -coverprofile=coverage.txt -covermode=atomic
+  # license-check
+  - make verify
   # integrate-test
   - chmod +x integrate_test.sh && ./integrate_test.sh
 
diff --git a/CHANGE.md b/CHANGE.md
index 90cb5a1443e8062125cbb9f2b3cc0ac1cf759d06..e60e0b05039136305bd789d9347b100a55b90ba9 100644
--- a/CHANGE.md
+++ b/CHANGE.md
@@ -1,5 +1,54 @@
 # Release Notes
 ---
+## 1.4.5
+
+### Bugfixes
+- [Fix too many files open error](https://github.com/apache/dubbo-go/pull/828)  [@wenxuwan](https://github.com/wenxuwan) Milestone: [https://github.com/apache/dubbo-go/milestone/6](https://github.com/apache/dubbo-go/milestone/6?closed=1)
+
+## 1.5.4
+
+### Bugfixes
+- [Fix etcd cluster reconnect](https://github.com/apache/dubbo-go/pull/828)  
+- [Fix zookeeper deadlock problem](https://github.com/apache/dubbo-go/pull/826)      
+- [Fix generic struct2MapAll](https://github.com/apache/dubbo-go/pull/822)     
+- [Fix Consumer panic when restart provider](https://github.com/apache/dubbo-go/pull/803)     
+- [Fix etcd can not registry](https://github.com/apache/dubbo-go/pull/819) [@lin-jianjun](https://github.com/lin-jianjun)
+- [Fix cannot call go provider service when used by java dubbo 2.7.7 version](https://github.com/apache/dubbo-go/pull/815) [@jack15083](https://github.com/jack15083) 
+- [Fix go client quit abnormally when it connects java server](https://github.com/apache/dubbo-go/pull/820) [@wenxuwan](https://github.com/wenxuwan)      
+- [Fix sentinel windows issue](https://github.com/apache/dubbo-go/pull/821) [@louyuting](https://github.com/louyuting)      
+- [Fix metadata default port](https://github.com/apache/dubbo-go/pull/821) [@sanxun0325](https://github.com/sanxun0325)      
+- [Fix consul can not destory](https://github.com/apache/dubbo-go/pull/788) [@LaurenceLiZhixin](https://github.com/LaurenceLiZhixin)      
+
+Milestone: [https://github.com/apache/dubbo-go/milestone/6](https://github.com/apache/dubbo-go/milestone/6?closed=1)
+
+## 1.5.3
+
+### New Features
+- [Add consul service discovery](https://github.com/apache/dubbo-go/pull/701) [@zhangshen023](https://github.com/zhangshen023)
+- [Add File system service discovery](https://github.com/apache/dubbo-go/pull/732) [@DogBaoBao](https://github.com/DogBaoBao)
+- [Migrate travis Ci to Github Actions](https://github.com/apache/dubbo-go/pull/752) [@sdttttt](https://github.com/sdttttt)
+- [Add sentinel-golang flow control/circuit breaker](https://github.com/apache/dubbo-go/pull/748) [@louyuting](https://github.com/louyuting)
+- [Add dubbo-go docs and blog into doc directory](https://github.com/apache/dubbo-go/pull/767) [@oaoit](https://github.com/oaoit) 
+
+### Enhancement
+- [Add address notification batch mode](https://github.com/apache/dubbo-go/pull/741) [@beiwei30](https://github.com/beiwei30)
+- [Refactor network and codec model](https://github.com/apache/dubbo-go/pull/673) [@fangyincheng](https://github.com/fangyincheng) [@georgehao](https://github.com/georgehao)
+- [Remove unnecessary return and judgement](https://github.com/apache/dubbo-go/pull/730) [@YongHaoWu](https://github.com/YongHaoWu)
+- [Improve exporter append method](https://github.com/apache/dubbo-go/pull/722) [@gaoxinge](https://github.com/gaoxinge)
+- [Refactor for proxyInvoker cannot be extended](https://github.com/apache/dubbo-go/pull/747) [@cvictory](https://github.com/cvictory) 
+- [Refactor attachment type from map\[string\]stiring to map\[string\]interface{}](https://github.com/apache/dubbo-go/pull/713) [@cvictory](https://github.com/cvictory) 
+- [Improve map access concurrency](https://github.com/apache/dubbo-go/pull/739) [@skyao](https://github.com/skyao) 
+- [Improve code quantity](https://github.com/apache/dubbo-go/pull/763) [@gaoxinge](https://github.com/gaoxinge) 
+
+### Bugfixes
+- [Fix etcdv3 lease](https://github.com/apache/dubbo-go/pull/738) [@zhangshen023](https://github.com/zhangshen023) 
+- [Fix rename SethealthChecker to SetHealthChecker](https://github.com/apache/dubbo-go/pull/746) [@watermelo](https://github.com/watermelo)  
+- [Fix init config problem in HystrixFilter](https://github.com/apache/dubbo-go/pull/731) [@YGrylls](https://github.com/YGrylls)   
+- [Fix zookeeper listener report error after started](https://github.com/apache/dubbo-go/pull/735) [@wenxuwan](https://github.com/wenxuwan)      
+
+Milestone: [https://github.com/apache/dubbo-go/milestone/4](https://github.com/apache/dubbo-go/milestone/4?closed=1)
+
+Project: [https://github.com/apache/dubbo-go/projects/10](https://github.com/apache/dubbo-go/projects/10)
 
 ## 1.5.1
 
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..2f6c9bd4e21a56fa4a6060db8795ba6e716160bb
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,76 @@
+#
+# 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.
+#
+
+VERSION ?= latest
+
+GO = go
+GO_PATH = $(shell $(GO) env GOPATH)
+GO_OS = $(shell $(GO) env GOOS)
+ifeq ($(GO_OS), darwin)
+    GO_OS = mac
+endif
+GO_BUILD = $(GO) build
+GO_GET = $(GO) get
+GO_TEST = $(GO) test
+GO_BUILD_FLAGS = -v
+GO_BUILD_LDFLAGS = -X main.version=$(VERSION)
+
+GO_LICENSE_CHECKER_DIR = license-header-checker-$(GO_OS)
+GO_LICENSE_CHECKER = $(GO_PATH)/bin/license-header-checker
+LICENSE_DIR = /tmp/tools/license
+
+ARCH = amd64
+# for add zookeeper fatjar
+ZK_TEST_LIST=config_center/zookeeper registry/zookeeper cluster/router/chain cluster/router/condition cluster/router/tag  metadata/report/zookeeper
+ZK_JAR_NAME=zookeeper-3.4.9-fatjar.jar
+ZK_FATJAR_BASE=/zookeeper-4unittest/contrib/fatjar
+ZK_JAR_PATH=remoting/zookeeper$(ZK_FATJAR_BASE)
+ZK_JAR=$(ZK_JAR_PATH)/$(ZK_JAR_NAME)
+
+SHELL = /bin/bash
+
+prepareLic:
+	$(GO_LICENSE_CHECKER) -version || (wget https://github.com/lsm-dev/license-header-checker/releases/download/v1.2.0/$(GO_LICENSE_CHECKER_DIR).zip -O $(GO_LICENSE_CHECKER_DIR).zip && unzip -o $(GO_LICENSE_CHECKER_DIR).zip && mkdir -p $(GO_PATH)/bin/ && cp $(GO_LICENSE_CHECKER_DIR)/64bit/license-header-checker $(GO_PATH)/bin/)
+	ls /tmp/tools/license/license.txt || wget -P $(LICENSE_DIR) https://github.com/dubbogo/resources/raw/master/tools/license/license.txt
+
+prepareZk:
+	ls $(ZK_JAR) || (mkdir -p $(ZK_JAR_PATH)&&  wget -P $(ZK_JAR_PATH) https://github.com/dubbogo/resources/raw/master/zookeeper-4unitest/contrib/fatjar/${ZK_JAR_NAME})
+	@for i in $(ZK_TEST_LIST); do \
+		mkdir -p $$i$(ZK_FATJAR_BASE);\
+		cp ${ZK_JAR} $$i$(ZK_FATJAR_BASE);\
+	done
+
+prepare: prepareZk prepareLic
+
+.PHONE: test
+test: clean prepareZk
+	$(GO_TEST) ./... -coverprofile=coverage.txt -covermode=atomic
+
+deps: prepare
+	$(GO_GET) -v -t -d ./...
+
+.PHONY: license
+license: clean prepareLic
+	$(GO_LICENSE_CHECKER) -v -a -r -i vendor $(LICENSE_DIR)/license.txt . go && [[ -z `git status -s` ]]
+
+.PHONY: verify
+verify: clean license test
+
+.PHONY: clean
+clean: prepare
+	rm -rf coverage.txt
+	rm -rf license-header-checker*
diff --git a/README.md b/README.md
index 9e1edd3af1cd8957b1daa9b9fe2cadf121bc2d6d..f950a4b3e9c4e115eda2f0e99379286c4c66d24f 100644
--- a/README.md
+++ b/README.md
@@ -16,6 +16,14 @@ Apache License, Version 2.0
 
 ## Release note ##
 
+[v1.4.5 - Nov 18, 2020](https://github.com/apache/dubbo-go/releases/tag/v1.4.5)
+
+[v1.5.4 - Nov 1, 2020](https://github.com/apache/dubbo-go/releases/tag/v1.5.4)
+
+[v1.5.3 - Sep 23, 2020](https://github.com/apache/dubbo-go/releases/tag/v1.5.3)
+
+[v1.5.2 - discard]()
+
 [v1.5.1 - Aug 23, 2020](https://github.com/apache/dubbo-go/releases/tag/v1.5.1)
 
 [v1.5.0 - July 24, 2020](https://github.com/apache/dubbo-go/releases/tag/v1.5.0)
@@ -60,7 +68,7 @@ Finished List:
     * Jsonrpc2.0
     * [gRPC](https://github.com/apache/dubbo-go/pull/311)
     * [RESTful](https://github.com/apache/dubbo-go/pull/352)
-    
+
 - Router
     * [Condition router](https://github.com/apache/dubbo-go/pull/294)
     * [Health check router](https://github.com/apache/dubbo-go/pull/389)
@@ -105,7 +113,7 @@ Finished List:
 
 - Invoke
     * [generic invoke](https://github.com/apache/dubbo-go/pull/122)
-    
+
 - Monitor
     * Opentracing API
     * [Prometheus](https://github.com/apache/dubbo-go/pull/342)
@@ -144,28 +152,26 @@ https://dubbogo.github.io/dubbo-go-website (**Improving**)
 
 ## Quick Start
 
-[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.
+[dubbo-go-samples](https://github.com/apache/dubbo-go-samples) shows how to use dubbo-go. Please read the [dubbo-samples/golang/README.md](https://github.com/apache/dubbo-go-samples/blob/master/README.md) carefully to learn how to dispose the configuration and compile the program.
 
 ## Running unit tests
 
-### Prepare
+### Run
 
-Mac/Linux
 ```bash
-sh ./before_ut.sh
+make verify
 ```
 
-Windows
+### Verify license
+
 ```bash
-before_ut.bat
+make license
 ```
 
-### Run
-```bash
-go test ./...
+### Run unit test
 
-# coverage
-go test ./... -coverprofile=coverage.txt -covermode=atomic
+```bash
+make test
 ```
 
 ## Build
@@ -212,8 +218,49 @@ If you are using [apache/dubbo-go](github.com/apache/dubbo-go) and think that it
           <img width="222px"  src="https://raw.githubusercontent.com/mosn/community/master/icons/png/mosn-labeled-horizontal.png">
         </a>
       </td>
+      <td align="center"  valign="middle">
+        <a href="" target="_blank">
+          <img width="222px"  src="https://festatic.estudy.cn/assets/xhx-web/layout/logo.png">
+        </a>
+      </td>
+    </tr>
+    <tr></tr>
+    <tr>
+      <td align="center"  valign="middle">
+        <a href="http://www.j.cn" target="_blank">
+          <img width="222px"  src="http://image.guang.j.cn/bbs/imgs/home/pc/icon_8500.png">
+        </a>
+      </td>
+      <td align="center"  valign="middle">
+        <a href="https://www.genshuixue.com/" target="_blank">
+          <img width="222px"  src="https://i.gsxcdn.com/0cms/d/file/content/2020/02/5e572137d7d94.png">
+        </a>
+      </td>
+      <td align="center"  valign="middle">
+        <a href="http://www.51h5.com" target="_blank">
+          <img width="222px"  src="https://fs-ews.51h5.com/common/hw_220_black.png">
+        </a>
+      </td>
+      <td align="center"  valign="middle">
+        <a href="https://www.zto.com" target="_blank">
+          <img width="222px"  src="https://fscdn.zto.com/fs8/M02/B2/E4/wKhBD1-8o52Ae3GnAAASU3r62ME040.png">
+        </a>
+      </td>
+      <td align="center"  valign="middle">
+        <a href="https://www.icsoc.net/" target="_blank">
+          <img width="222px"  src="https://oss.icsoc.net/icsoc-ekt-test-files/icsoc.png">
+        </a>
+      </td>
     </tr>
     <tr></tr>
+    <tr>
+      <td align="center"  valign="middle">
+        <a href="http://www.mgtv.com" target="_blank">
+          <img width="222px"  src="https://ugc.hitv.com/platform_oss/F6077F1AA82542CDBDD88FD518E6E727.png">
+        </a>
+      </td>
+    </tr>
+    <tr></tr> 
   </tbody>
 </table>
 </div>
diff --git a/README_CN.md b/README_CN.md
index b76d8983deae427f9317c4f930f0e06da479f484..ff72428d977f8b24c7c60b228108987c823c9efa 100644
--- a/README_CN.md
+++ b/README_CN.md
@@ -15,6 +15,16 @@ Apache License, Version 2.0
 
 ## 鍙戝竷鏃ュ織 ##
 
+[v1.4.5 - 2020骞�11鏈�18鏃(https://github.com/apache/dubbo-go/releases/tag/v1.4.5)
+
+[v1.5.4 - 2020骞�11鏈�1鏃(https://github.com/apache/dubbo-go/releases/tag/v1.5.4)
+
+[v1.5.3 - 2020骞�9鏈�23鏃(https://github.com/apache/dubbo-go/releases/tag/v1.5.3)
+
+[v1.5.2 - 鑸嶅純]()
+
+[v1.5.1 - 2020骞�8鏈�23鏃(https://github.com/apache/dubbo-go/releases/tag/v1.5.1)
+
 [v1.5.0 - 2020骞�7鏈�24鏃(https://github.com/apache/dubbo-go/releases/tag/v1.5.0)
 
 [v1.4.0 - 2020骞�3鏈�17鏃(https://github.com/apache/dubbo-go/releases/tag/v1.4.0)
@@ -95,6 +105,7 @@ Apache License, Version 2.0
     * [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)
     * [Auth/Sign](https://github.com/apache/dubbo-go/pull/323)
     * [Metrics filter](https://github.com/apache/dubbo-go/pull/342)
     * [Tracing filter](https://github.com/apache/dubbo-go/pull/335)
@@ -144,29 +155,27 @@ https://dubbogo.github.io/dubbo-go-website (**瀹屽杽涓�**)
 
 ## 杩愯鍗曟祴
 
-### 鍑嗗
+### 鎵ц鍏ㄩ儴鏍¢獙
 
-Mac/Linux
 ```bash
-sh ./before_ut.sh
+make verify
 ```
 
-Windows
+### 鏍¢獙璁稿彲璇�
+
 ```bash
-before_ut.bat
+make license
 ```
 
-### 鎵ц
-```bash
-go test ./...
+### 鎵ц鍗曞厓娴嬭瘯
 
-# coverage
-go test ./... -coverprofile=coverage.txt -covermode=atomic
+```bash
+make test
 ```
 
 ## 缂栬瘧
 
-璇风Щ姝� [dubbo-samples/golang](https://github.com/dubbogo/dubbo-samples)
+璇风Щ姝� [dubbo-go-samples](https://github.com/apache/dubbo-go-samples)
 
 ## 濡備綍璐$尞
 
@@ -207,8 +216,49 @@ go test ./... -coverprofile=coverage.txt -covermode=atomic
           <img width="222px"  src="https://raw.githubusercontent.com/mosn/community/master/icons/png/mosn-labeled-horizontal.png">
         </a>
       </td>
+      <td align="center"  valign="middle">
+        <a href="" target="_blank">
+          <img width="222px"  src="https://festatic.estudy.cn/assets/xhx-web/layout/logo.png">
+        </a>
+      </td>
     </tr>
     <tr></tr>
+    <tr>
+      <td align="center"  valign="middle">
+        <a href="http://www.j.cn" target="_blank">
+          <img width="222px"  src="http://image.guang.j.cn/bbs/imgs/home/pc/icon_8500.png">
+        </a>
+      </td>
+      <td align="center"  valign="middle">
+        <a href="https://www.genshuixue.com/" target="_blank">
+          <img width="222px"  src="https://i.gsxcdn.com/0cms/d/file/content/2020/02/5e572137d7d94.png">
+        </a>
+      </td>
+      <td align="center"  valign="middle">
+        <a href="http://www.51h5.com" target="_blank">
+          <img width="222px"  src="https://fs-ews.51h5.com/common/hw_220_black.png">
+        </a>
+      </td>
+      <td align="center"  valign="middle">
+        <a href="https://www.zto.com" target="_blank">
+          <img width="222px"  src="https://fscdn.zto.com/fs8/M02/B2/E4/wKhBD1-8o52Ae3GnAAASU3r62ME040.png">
+        </a>
+      </td>
+      <td align="center"  valign="middle">
+        <a href="https://www.icsoc.net/" target="_blank">
+          <img width="222px"  src="https://oss.icsoc.net/icsoc-ekt-test-files/icsoc.png">
+        </a>
+      </td>
+    </tr>
+    <tr></tr>
+    <tr>
+      <td align="center"  valign="middle">
+        <a href="http://www.mgtv.com" target="_blank">
+          <img width="222px"  src="https://ugc.hitv.com/platform_oss/F6077F1AA82542CDBDD88FD518E6E727.png">
+        </a>
+      </td>
+    </tr>
+    <tr></tr> 
   </tbody>
 </table>
-</div>
+</div>
\ No newline at end of file
diff --git a/before_ut.bat b/before_ut.bat
deleted file mode 100644
index 7f5cf50e900955f784552221569d9caf414274d4..0000000000000000000000000000000000000000
--- a/before_ut.bat
+++ /dev/null
@@ -1,43 +0,0 @@
-::
-::  Licensed to the Apache Software Foundation (ASF) under one or more
-::  contributor license agreements.  See the NOTICE file distributed with
-::  this work for additional information regarding copyright ownership.
-::  The ASF licenses this file to You under the Apache License, Version 2.0
-::  (the "License"); you may not use this file except in compliance with
-::  the License.  You may obtain a copy of the License at
-::
-::      http://www.apache.org/licenses/LICENSE-2.0
-::
-::  Unless required by applicable law or agreed to in writing, software
-::  distributed under the License is distributed on an "AS IS" BASIS,
-::  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-::  See the License for the specific language governing permissions and
-::  limitations under the License.
-
-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/"
-
-mkdir -p cluster/router/tag/zookeeper-4unittest/contrib/fatjar
-cp ${zkJar} cluster/router/tag/zookeeper-4unittest/contrib/fatjar
-
-md metadata\report\zookeeper\zookeeper-4unittest\contrib\fatjar
-xcopy /f "%zkJar%" "metadata/report/zookeeper/zookeeper-4unittest/contrib/fatjar/"
\ No newline at end of file
diff --git a/before_ut.sh b/before_ut.sh
deleted file mode 100755
index b55e424ef72b33181b2ea40fdb37ac319110aec0..0000000000000000000000000000000000000000
--- a/before_ut.sh
+++ /dev/null
@@ -1,43 +0,0 @@
-#
-#  Licensed to the Apache Software Foundation (ASF) under one or more
-#  contributor license agreements.  See the NOTICE file distributed with
-#  this work for additional information regarding copyright ownership.
-#  The ASF licenses this file to You under the Apache License, Version 2.0
-#  (the "License"); you may not use this file except in compliance with
-#  the License.  You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-#  Unless required by applicable law or agreed to in writing, software
-#  distributed under the License is distributed on an "AS IS" BASIS,
-#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#  See the License for the specific language governing permissions and
-#  limitations under the License.
-
-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}"
-
-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
-
-mkdir -p cluster/router/tag/zookeeper-4unittest/contrib/fatjar
-cp ${zkJar} cluster/router/tag/zookeeper-4unittest/contrib/fatjar
-
-mkdir -p metadata/report/zookeeper/zookeeper-4unittest/contrib/fatjar
-cp ${zkJar} metadata/report/zookeeper/zookeeper-4unittest/contrib/fatjar
\ No newline at end of file
diff --git a/before_validate_license.sh b/before_validate_license.sh
deleted file mode 100644
index 8fa6e381c7a4cd44835d107ba9213f685f899a10..0000000000000000000000000000000000000000
--- a/before_validate_license.sh
+++ /dev/null
@@ -1,26 +0,0 @@
-#
-#  Licensed to the Apache Software Foundation (ASF) under one or more
-#  contributor license agreements.  See the NOTICE file distributed with
-#  this work for additional information regarding copyright ownership.
-#  The ASF licenses this file to You under the Apache License, Version 2.0
-#  (the "License"); you may not use this file except in compliance with
-#  the License.  You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-#  Unless required by applicable law or agreed to in writing, software
-#  distributed under the License is distributed on an "AS IS" BASIS,
-#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#  See the License for the specific language governing permissions and
-#  limitations under the License.
-
-remoteLicenseCheckerPath="https://github.com/dubbogo/resources/raw/master/tools/license"
-remoteLicenseCheckerName="license-header-checker"
-remoteLicenseCheckerURL="${remoteLicenseCheckerPath}/${remoteLicenseCheckerName}"
-remoteLicenseName="license.txt"
-remoteLicenseURL="${remoteLicenseCheckerPath}/${remoteLicenseName}"
-
-licensePath="/tmp/tools/license"
-mkdir -p ${licensePath}
-wget -P "${licensePath}" ${remoteLicenseCheckerURL}
-wget -P "${licensePath}" ${remoteLicenseURL}
diff --git a/cluster/cluster_impl/base_cluster_invoker.go b/cluster/cluster_impl/base_cluster_invoker.go
index ced5b15cb9a1c2292ca866f6f7478ce2b23a30b9..ed30559ed3c4b3930e65d6e66d5e91d8619516a1 100644
--- a/cluster/cluster_impl/base_cluster_invoker.go
+++ b/cluster/cluster_impl/base_cluster_invoker.go
@@ -22,7 +22,6 @@ import (
 )
 
 import (
-	gxnet "github.com/dubbogo/gost/net"
 	perrors "github.com/pkg/errors"
 	"go.uber.org/atomic"
 )
@@ -32,6 +31,7 @@ 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/protocol"
 )
 
@@ -51,7 +51,7 @@ func newBaseClusterInvoker(directory cluster.Directory) baseClusterInvoker {
 	}
 }
 
-func (invoker *baseClusterInvoker) GetUrl() common.URL {
+func (invoker *baseClusterInvoker) GetUrl() *common.URL {
 	return invoker.directory.GetUrl()
 }
 
@@ -72,7 +72,7 @@ func (invoker *baseClusterInvoker) IsAvailable() bool {
 //check invokers availables
 func (invoker *baseClusterInvoker) checkInvokers(invokers []protocol.Invoker, invocation protocol.Invocation) error {
 	if len(invokers) == 0 {
-		ip, _ := gxnet.GetLocalIP()
+		ip := common.GetLocalIp()
 		return perrors.Errorf("Failed to invoke the method %v. No provider available for the service %v from "+
 			"registry %v on the consumer %v using the dubbo version %v .Please check if the providers have been started and registered.",
 			invocation.MethodName(), invoker.directory.GetUrl().SubURL.Key(), invoker.directory.GetUrl().String(), ip, constant.Version)
@@ -84,7 +84,7 @@ func (invoker *baseClusterInvoker) checkInvokers(invokers []protocol.Invoker, in
 //check cluster invoker is destroyed or not
 func (invoker *baseClusterInvoker) checkWhetherDestroyed() error {
 	if invoker.destroyed.Load() {
-		ip, _ := gxnet.GetLocalIP()
+		ip := common.GetLocalIp()
 		return perrors.Errorf("Rpc cluster invoker for %v on consumer %v use dubbo version %v is now destroyed! can not invoke any more. ",
 			invoker.directory.GetUrl().Service(), ip, constant.Version)
 	}
@@ -120,6 +120,10 @@ func (invoker *baseClusterInvoker) doSelect(lb cluster.LoadBalance, invocation p
 }
 
 func (invoker *baseClusterInvoker) doSelectInvoker(lb cluster.LoadBalance, invocation protocol.Invocation, invokers []protocol.Invoker, invoked []protocol.Invoker) protocol.Invoker {
+	if len(invokers) == 0 {
+		logger.Errorf("the invokers of %s is nil. ", invocation.Invoker().GetUrl().ServiceKey())
+		return nil
+	}
 	if len(invokers) == 1 {
 		return invokers[0]
 	}
@@ -134,6 +138,8 @@ func (invoker *baseClusterInvoker) doSelectInvoker(lb cluster.LoadBalance, invoc
 
 		for _, invoker := range invokers {
 			if !invoker.IsAvailable() {
+				logger.Infof("the invoker of %s is not available, maybe some network error happened or the server is shutdown.",
+					invoker.GetUrl().Ip)
 				continue
 			}
 
@@ -145,6 +151,7 @@ func (invoker *baseClusterInvoker) doSelectInvoker(lb cluster.LoadBalance, invoc
 		if len(reslectInvokers) > 0 {
 			selectedInvoker = lb.Select(reslectInvokers, invocation)
 		} else {
+			logger.Errorf("all %d invokers is unavailable for %s.", len(invokers), selectedInvoker.GetUrl().String())
 			return nil
 		}
 	}
diff --git a/cluster/cluster_impl/failover_cluster_invoker.go b/cluster/cluster_impl/failover_cluster_invoker.go
index 4260a9324dd360ac24f80e425d8542a423c8815e..ca490e7f8e2ff44303abe7d695313153b7466a00 100644
--- a/cluster/cluster_impl/failover_cluster_invoker.go
+++ b/cluster/cluster_impl/failover_cluster_invoker.go
@@ -24,12 +24,12 @@ import (
 )
 
 import (
-	gxnet "github.com/dubbogo/gost/net"
 	perrors "github.com/pkg/errors"
 )
 
 import (
 	"github.com/apache/dubbo-go/cluster"
+	"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"
@@ -51,6 +51,7 @@ func (invoker *failoverClusterInvoker) Invoke(ctx context.Context, invocation pr
 		result    protocol.Result
 		invoked   []protocol.Invoker
 		providers []string
+		ivk       protocol.Invoker
 	)
 
 	invokers := invoker.directory.List(invocation)
@@ -75,7 +76,7 @@ func (invoker *failoverClusterInvoker) Invoke(ctx context.Context, invocation pr
 				return &protocol.RPCResult{Err: err}
 			}
 		}
-		ivk := invoker.doSelect(loadBalance, invocation, invokers, invoked)
+		ivk = invoker.doSelect(loadBalance, invocation, invokers, invoked)
 		if ivk == nil {
 			continue
 		}
@@ -88,10 +89,17 @@ func (invoker *failoverClusterInvoker) Invoke(ctx context.Context, invocation pr
 		}
 		return result
 	}
-
-	ip, _ := gxnet.GetLocalIP()
+	ip := common.GetLocalIp()
 	invokerSvc := invoker.GetUrl().Service()
 	invokerUrl := invoker.directory.GetUrl()
+	if ivk == nil {
+		logger.Errorf("Failed to invoke the method %s of the service %s .No provider is available.", methodName, invokerSvc)
+		return &protocol.RPCResult{
+			Err: perrors.Errorf("Failed to invoke the method %s of the service %s .No provider is available because can't connect server.",
+				methodName, invokerSvc),
+		}
+	}
+
 	return &protocol.RPCResult{
 		Err: perrors.Wrap(result.Error(), fmt.Sprintf("Failed to invoke the method %v in the service %v. "+
 			"Tried %v times of the providers %v (%v/%v)from the registry %v on the consumer %v using the dubbo version %v. "+
diff --git a/cluster/cluster_impl/failover_cluster_test.go b/cluster/cluster_impl/failover_cluster_test.go
index d3ac2c8a5ffd7fce647649c53fe343ba93999636..3ea6232d4747f722c5a933cc056ff06304913965 100644
--- a/cluster/cluster_impl/failover_cluster_test.go
+++ b/cluster/cluster_impl/failover_cluster_test.go
@@ -45,7 +45,7 @@ import (
 
 // nolint
 type MockInvoker struct {
-	url       common.URL
+	url       *common.URL
 	available bool
 	destroyed bool
 
@@ -53,7 +53,7 @@ type MockInvoker struct {
 }
 
 // nolint
-func NewMockInvoker(url common.URL, successCount int) *MockInvoker {
+func NewMockInvoker(url *common.URL, successCount int) *MockInvoker {
 	return &MockInvoker{
 		url:          url,
 		available:    true,
@@ -63,7 +63,7 @@ func NewMockInvoker(url common.URL, successCount int) *MockInvoker {
 }
 
 // nolint
-func (bi *MockInvoker) GetUrl() common.URL {
+func (bi *MockInvoker) GetUrl() *common.URL {
 	return bi.url
 }
 
diff --git a/cluster/cluster_impl/forking_cluster_invoker.go b/cluster/cluster_impl/forking_cluster_invoker.go
index 168444881653ca38ef61a9bc8e50f2d4bc3e624c..3ffda580ce9847bb60a0a837c5fac6b420450141 100644
--- a/cluster/cluster_impl/forking_cluster_invoker.go
+++ b/cluster/cluster_impl/forking_cluster_invoker.go
@@ -56,7 +56,7 @@ func (invoker *forkingClusterInvoker) Invoke(ctx context.Context, invocation pro
 	}
 
 	var selected []protocol.Invoker
-	forks := int(invoker.GetUrl().GetParamInt(constant.FORKS_KEY, constant.DEFAULT_FORKS))
+	forks := invoker.GetUrl().GetParamByIntValue(constant.FORKS_KEY, constant.DEFAULT_FORKS)
 	timeouts := invoker.GetUrl().GetParamInt(constant.TIMEOUT_KEY, constant.DEFAULT_TIMEOUT)
 	if forks < 0 || forks > len(invokers) {
 		selected = invokers
diff --git a/cluster/cluster_impl/zone_aware_cluster_invoker_test.go b/cluster/cluster_impl/zone_aware_cluster_invoker_test.go
index cd201a42c759354ca536ea3e9e77116d89ea8b4b..7f77f33166de293836c15391f5eedd5a18084dbe 100644
--- a/cluster/cluster_impl/zone_aware_cluster_invoker_test.go
+++ b/cluster/cluster_impl/zone_aware_cluster_invoker_test.go
@@ -44,7 +44,7 @@ func TestZoneWareInvokerWithPreferredSuccess(t *testing.T) {
 	//defer ctrl.Finish()
 
 	mockResult := &protocol.RPCResult{
-		Attrs: map[string]string{constant.PREFERRED_KEY: "true"},
+		Attrs: map[string]interface{}{constant.PREFERRED_KEY: "true"},
 		Rest:  rest{tried: 0, success: true}}
 
 	var invokers []protocol.Invoker
@@ -99,7 +99,7 @@ func TestZoneWareInvokerWithWeightSuccess(t *testing.T) {
 			invoker.EXPECT().Invoke(gomock.Any()).DoAndReturn(
 				func(invocation protocol.Invocation) protocol.Result {
 					return &protocol.RPCResult{
-						Attrs: map[string]string{constant.WEIGHT_KEY: w1},
+						Attrs: map[string]interface{}{constant.WEIGHT_KEY: w1},
 						Rest:  rest{tried: 0, success: true}}
 				}).MaxTimes(100)
 		} else {
@@ -107,7 +107,7 @@ func TestZoneWareInvokerWithWeightSuccess(t *testing.T) {
 			invoker.EXPECT().Invoke(gomock.Any()).DoAndReturn(
 				func(invocation protocol.Invocation) protocol.Result {
 					return &protocol.RPCResult{
-						Attrs: map[string]string{constant.WEIGHT_KEY: w2},
+						Attrs: map[string]interface{}{constant.WEIGHT_KEY: w2},
 						Rest:  rest{tried: 0, success: true}}
 				}).MaxTimes(100)
 		}
@@ -154,7 +154,7 @@ func TestZoneWareInvokerWithZoneSuccess(t *testing.T) {
 		invoker.EXPECT().Invoke(gomock.Any()).DoAndReturn(
 			func(invocation protocol.Invocation) protocol.Result {
 				return &protocol.RPCResult{
-					Attrs: map[string]string{constant.ZONE_KEY: zoneValue},
+					Attrs: map[string]interface{}{constant.ZONE_KEY: zoneValue},
 					Rest:  rest{tried: 0, success: true}}
 			})
 		invokers = append(invokers, invoker)
diff --git a/cluster/directory/base_directory.go b/cluster/directory/base_directory.go
index 20db1f2b7de71c843caf7c7abda39e40c68e4ecd..d1025a152b599d70c40bba5bc16009d8d5adee37 100644
--- a/cluster/directory/base_directory.go
+++ b/cluster/directory/base_directory.go
@@ -65,8 +65,8 @@ func (dir *BaseDirectory) SetRouterChain(routerChain router.Chain) {
 }
 
 // GetUrl Get URL
-func (dir *BaseDirectory) GetUrl() common.URL {
-	return *dir.url
+func (dir *BaseDirectory) GetUrl() *common.URL {
+	return dir.url
 }
 
 // GetDirectoryUrl Get URL instance
diff --git a/cluster/directory/base_directory_test.go b/cluster/directory/base_directory_test.go
index a2b62dfa008e6cd17b1200d93cd235da17d03905..16e3c5a960912af66739bcc1c5736d44d437503e 100644
--- a/cluster/directory/base_directory_test.go
+++ b/cluster/directory/base_directory_test.go
@@ -24,7 +24,6 @@ import (
 )
 
 import (
-	gxnet "github.com/dubbogo/gost/net"
 	"github.com/stretchr/testify/assert"
 )
 
@@ -41,21 +40,18 @@ var (
 )
 
 func TestNewBaseDirectory(t *testing.T) {
-	directory := NewBaseDirectory(&url)
-	assert.NotNil(t, directory)
-	assert.Equal(t, url, directory.GetUrl())
-	assert.Equal(t, &url, directory.GetDirectoryUrl())
+	dir := NewBaseDirectory(url)
+	assert.Equal(t, url, dir.GetUrl())
+	assert.Equal(t, url, dir.GetDirectoryUrl())
 }
 
 func TestBuildRouterChain(t *testing.T) {
 
 	regURL := url
 	regURL.AddParam(constant.INTERFACE_KEY, "mock-app")
-	directory := NewBaseDirectory(&regURL)
+	directory := NewBaseDirectory(regURL)
 
-	assert.NotNil(t, directory)
-
-	localIP, _ := gxnet.GetLocalIP()
+	localIP := common.GetLocalIp()
 	rule := base64.URLEncoding.EncodeToString([]byte("true => " + " host = " + localIP))
 	routeURL := getRouteURL(rule, anyURL)
 	routeURL.AddParam(constant.INTERFACE_KEY, "mock-app")
@@ -67,19 +63,19 @@ func TestBuildRouterChain(t *testing.T) {
 	assert.NotNil(t, chain)
 }
 
-func getRouteURL(rule string, u common.URL) *common.URL {
+func getRouteURL(rule string, u *common.URL) *common.URL {
 	ru := u
 	ru.AddParam("rule", rule)
 	ru.AddParam("force", "true")
 	ru.AddParam(constant.ROUTER_KEY, "router")
-	return &ru
+	return ru
 }
 
 func TestIsProperRouter(t *testing.T) {
 	regURL := url
 	regURL.AddParam(constant.APPLICATION_KEY, "mock-app")
-	d := NewBaseDirectory(&regURL)
-	localIP, _ := gxnet.GetLocalIP()
+	d := NewBaseDirectory(regURL)
+	localIP := common.GetLocalIp()
 	rule := base64.URLEncoding.EncodeToString([]byte("true => " + " host = " + localIP))
 	routeURL := getRouteURL(rule, anyURL)
 	routeURL.AddParam(constant.APPLICATION_KEY, "mock-app")
@@ -88,7 +84,7 @@ func TestIsProperRouter(t *testing.T) {
 
 	regURL.AddParam(constant.APPLICATION_KEY, "")
 	regURL.AddParam(constant.INTERFACE_KEY, "com.foo.BarService")
-	d = NewBaseDirectory(&regURL)
+	d = NewBaseDirectory(regURL)
 	routeURL = getRouteURL(rule, anyURL)
 	routeURL.AddParam(constant.INTERFACE_KEY, "com.foo.BarService")
 	rst = d.isProperRouter(routeURL)
@@ -96,14 +92,14 @@ func TestIsProperRouter(t *testing.T) {
 
 	regURL.AddParam(constant.APPLICATION_KEY, "")
 	regURL.AddParam(constant.INTERFACE_KEY, "")
-	d = NewBaseDirectory(&regURL)
+	d = NewBaseDirectory(regURL)
 	routeURL = getRouteURL(rule, anyURL)
 	rst = d.isProperRouter(routeURL)
 	assert.True(t, rst)
 
 	regURL.SetParam(constant.APPLICATION_KEY, "")
 	regURL.SetParam(constant.INTERFACE_KEY, "")
-	d = NewBaseDirectory(&regURL)
+	d = NewBaseDirectory(regURL)
 	routeURL = getRouteURL(rule, anyURL)
 	routeURL.AddParam(constant.APPLICATION_KEY, "mock-service")
 	rst = d.isProperRouter(routeURL)
@@ -111,7 +107,7 @@ func TestIsProperRouter(t *testing.T) {
 
 	regURL.SetParam(constant.APPLICATION_KEY, "")
 	regURL.SetParam(constant.INTERFACE_KEY, "")
-	d = NewBaseDirectory(&regURL)
+	d = NewBaseDirectory(regURL)
 	routeURL = getRouteURL(rule, anyURL)
 	routeURL.AddParam(constant.INTERFACE_KEY, "mock-service")
 	rst = d.isProperRouter(routeURL)
diff --git a/cluster/directory/static_directory.go b/cluster/directory/static_directory.go
index 87f51356495dbd0a956c42bf4f34022b4d21ad4d..d9695d46b3d25dd89a80e10c2396e650492840ad 100644
--- a/cluster/directory/static_directory.go
+++ b/cluster/directory/static_directory.go
@@ -34,15 +34,18 @@ type staticDirectory struct {
 
 // NewStaticDirectory Create a new staticDirectory with invokers
 func NewStaticDirectory(invokers []protocol.Invoker) *staticDirectory {
-	var url common.URL
+	var url *common.URL
 
 	if len(invokers) > 0 {
 		url = invokers[0].GetUrl()
 	}
-	return &staticDirectory{
-		BaseDirectory: NewBaseDirectory(&url),
+	dir := &staticDirectory{
+		BaseDirectory: NewBaseDirectory(url),
 		invokers:      invokers,
 	}
+
+	dir.routerChain.SetInvokers(invokers)
+	return dir
 }
 
 //for-loop invokers ,if all invokers is available ,then it means directory is available
@@ -69,7 +72,7 @@ func (dir *staticDirectory) List(invocation protocol.Invocation) []protocol.Invo
 		return invokers
 	}
 	dirUrl := dir.GetUrl()
-	return routerChain.Route(invokers, &dirUrl, invocation)
+	return routerChain.Route(dirUrl, invocation)
 }
 
 // Destroy Destroy
@@ -88,10 +91,11 @@ func (dir *staticDirectory) BuildRouterChain(invokers []protocol.Invoker) error
 		return perrors.Errorf("invokers == null")
 	}
 	url := invokers[0].GetUrl()
-	routerChain, e := chain.NewRouterChain(&url)
+	routerChain, e := chain.NewRouterChain(url)
 	if e != nil {
 		return e
 	}
+	routerChain.SetInvokers(dir.invokers)
 	dir.SetRouterChain(routerChain)
 	return nil
 }
diff --git a/cluster/loadbalance/consistent_hash.go b/cluster/loadbalance/consistent_hash.go
index 27ce2369f9de6dfe76bd35581ea26f0e0c24e480..3d036b4f3c5c50874e09efb8fd1ef34e969585d8 100644
--- a/cluster/loadbalance/consistent_hash.go
+++ b/cluster/loadbalance/consistent_hash.go
@@ -105,7 +105,7 @@ func newConsistentHashSelector(invokers []protocol.Invoker, methodName string,
 	selector.virtualInvokers = make(map[uint32]protocol.Invoker)
 	selector.hashCode = hashCode
 	url := invokers[0].GetUrl()
-	selector.replicaNum = int(url.GetMethodParamInt(methodName, HashNodes, 160))
+	selector.replicaNum = url.GetMethodParamIntValue(methodName, HashNodes, 160)
 	indices := re.Split(url.GetMethodParam(methodName, HashArguments, "0"), -1)
 	for _, index := range indices {
 		i, err := strconv.Atoi(index)
diff --git a/cluster/loadbalance/consistent_hash_test.go b/cluster/loadbalance/consistent_hash_test.go
index 9f22d39dc46243dddda89151e07dbea39ab933fb..0fbb74059cf0af6cf7e75aab646d7c5c146bcc28 100644
--- a/cluster/loadbalance/consistent_hash_test.go
+++ b/cluster/loadbalance/consistent_hash_test.go
@@ -84,9 +84,9 @@ func TestConsistentHashLoadBalanceSuite(t *testing.T) {
 
 type consistentHashLoadBalanceSuite struct {
 	suite.Suite
-	url1     common.URL
-	url2     common.URL
-	url3     common.URL
+	url1     *common.URL
+	url2     *common.URL
+	url3     *common.URL
 	invokers []protocol.Invoker
 	invoker1 protocol.Invoker
 	invoker2 protocol.Invoker
diff --git a/cluster/router/chain.go b/cluster/router/chain.go
new file mode 100644
index 0000000000000000000000000000000000000000..3614d0a5a3d6cfb462ef63149ae99da2c4541b8d
--- /dev/null
+++ b/cluster/router/chain.go
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package router
+
+import (
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/protocol"
+)
+
+// Chain
+type Chain interface {
+	Route(*common.URL, protocol.Invocation) []protocol.Invoker
+	// Refresh invokers
+	SetInvokers([]protocol.Invoker)
+	// AddRouters Add routers
+	AddRouters([]PriorityRouter)
+}
diff --git a/cluster/router/chain/chain.go b/cluster/router/chain/chain.go
index 8746c1daf7f878a066ea005f910520e07c28318c..8c4ffe01bd9fe251fab60743ad5fe2aa83e20653 100644
--- a/cluster/router/chain/chain.go
+++ b/cluster/router/chain/chain.go
@@ -18,9 +18,10 @@
 package chain
 
 import (
-	"math"
 	"sort"
 	"sync"
+	"sync/atomic"
+	"time"
 )
 
 import (
@@ -30,11 +31,18 @@ import (
 import (
 	"github.com/apache/dubbo-go/cluster/router"
 	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/common/constant"
 	"github.com/apache/dubbo-go/common/extension"
 	"github.com/apache/dubbo-go/common/logger"
 	"github.com/apache/dubbo-go/protocol"
 )
 
+const (
+	timeInterval   = 5 * time.Second
+	timeThreshold  = 2 * time.Second
+	countThreshold = 5
+)
+
 // RouterChain Router chain
 type RouterChain struct {
 	// Full list of addresses from registry, classified by method name.
@@ -47,31 +55,41 @@ type RouterChain struct {
 
 	mutex sync.RWMutex
 
-	url common.URL
+	url *common.URL
+
+	// The times of address notification since last update for address cache
+	count int64
+	// The timestamp of last update for address cache
+	last time.Time
+	// Channel for notify to update the address cache
+	notify chan struct{}
+	// Address cache
+	cache atomic.Value
+	// init
+	init sync.Once
 }
 
 // Route Loop routers in RouterChain and call Route method to determine the target invokers list.
-func (c *RouterChain) Route(invokers []protocol.Invoker, url *common.URL, invocation protocol.Invocation) []protocol.Invoker {
-	finalInvokers := invokers
-	l := len(c.routers)
-	rs := make([]router.PriorityRouter, l, int(math.Ceil(float64(l)*1.2)))
-	c.mutex.RLock()
-	copy(rs, c.routers)
-	c.mutex.RUnlock()
+func (c *RouterChain) Route(url *common.URL, invocation protocol.Invocation) []protocol.Invoker {
+	cache := c.loadCache()
+	if cache == nil {
+		c.mutex.RLock()
+		defer c.mutex.RUnlock()
+		return c.invokers
+	}
 
-	for _, r := range rs {
-		finalInvokers = r.Route(finalInvokers, url, invocation)
+	bitmap := cache.bitmap
+	for _, r := range c.copyRouters() {
+		bitmap = r.Route(bitmap, cache, url, invocation)
 	}
-	return finalInvokers
-}
 
-// SetInvokers notify router chain of the initial addresses from registry at the first time. Notify whenever addresses in registry change.
-func (c *RouterChain) SetInvokers(invokers []protocol.Invoker) {
-	for _, r := range c.routers {
-		if notifyRouter, ok := r.(router.NotifyRouter); ok {
-			notifyRouter.Notify(invokers)
-		}
+	indexes := bitmap.ToArray()
+	finalInvokers := make([]protocol.Invoker, len(indexes))
+	for i, index := range indexes {
+		finalInvokers[i] = cache.invokers[index]
 	}
+
+	return finalInvokers
 }
 
 // AddRouters Add routers to router chain
@@ -88,8 +106,122 @@ func (c *RouterChain) AddRouters(routers []router.PriorityRouter) {
 	c.routers = newRouters
 }
 
+// SetInvokers receives updated invokers from registry center. If the times of notification exceeds countThreshold and
+// time interval exceeds timeThreshold since last cache update, then notify to update the cache.
+func (c *RouterChain) SetInvokers(invokers []protocol.Invoker) {
+	c.mutex.Lock()
+	c.invokers = invokers
+	c.mutex.Unlock()
+
+	// it should trigger init router for first call
+	c.init.Do(func() {
+		go func() {
+			c.notify <- struct{}{}
+		}()
+	})
+
+	c.count++
+	now := time.Now()
+	if c.count >= countThreshold && now.Sub(c.last) >= timeThreshold {
+		c.last = now
+		c.count = 0
+		go func() {
+			c.notify <- struct{}{}
+		}()
+	}
+}
+
+// loop listens on events to update the address cache when it's necessary, either when it receives notification
+// from address update, or when timeInterval exceeds.
+func (c *RouterChain) loop() {
+	ticker := time.NewTicker(timeInterval)
+	for {
+		select {
+		case <-ticker.C:
+			c.buildCache()
+		case <-c.notify:
+			c.buildCache()
+		}
+	}
+}
+
+// copyRouters make a snapshot copy from RouterChain's router list.
+func (c *RouterChain) copyRouters() []router.PriorityRouter {
+	c.mutex.RLock()
+	defer c.mutex.RUnlock()
+	ret := make([]router.PriorityRouter, 0, len(c.routers))
+	ret = append(ret, c.routers...)
+	return ret
+}
+
+// copyInvokers copies a snapshot of the received invokers.
+func (c *RouterChain) copyInvokers() []protocol.Invoker {
+	c.mutex.RLock()
+	defer c.mutex.RUnlock()
+	if c.invokers == nil || len(c.invokers) == 0 {
+		return nil
+	}
+	ret := make([]protocol.Invoker, 0, len(c.invokers))
+	ret = append(ret, c.invokers...)
+	return ret
+}
+
+// loadCache loads cache from sync.Value to guarantee the visibility
+func (c *RouterChain) loadCache() *InvokerCache {
+	v := c.cache.Load()
+	if v == nil {
+		return nil
+	}
+
+	return v.(*InvokerCache)
+}
+
+// copyInvokerIfNecessary compares chain's invokers copy and cache's invokers copy, to avoid copy as much as possible
+func (c *RouterChain) copyInvokerIfNecessary(cache *InvokerCache) []protocol.Invoker {
+	var invokers []protocol.Invoker
+	if cache != nil {
+		invokers = cache.invokers
+	}
+
+	c.mutex.RLock()
+	defer c.mutex.RUnlock()
+	if isInvokersChanged(invokers, c.invokers) {
+		invokers = c.copyInvokers()
+	}
+	return invokers
+}
+
+// buildCache builds address cache with the new invokers for all poolable routers.
+func (c *RouterChain) buildCache() {
+	origin := c.loadCache()
+	invokers := c.copyInvokerIfNecessary(origin)
+
+	var (
+		mutex sync.Mutex
+		wg    sync.WaitGroup
+	)
+
+	cache := BuildCache(invokers)
+	for _, r := range c.copyRouters() {
+		if p, ok := r.(router.Poolable); ok {
+			wg.Add(1)
+			go func(p router.Poolable) {
+				defer wg.Done()
+				pool, info := poolRouter(p, origin, invokers)
+				mutex.Lock()
+				defer mutex.Unlock()
+				cache.pools[p.Name()] = pool
+				cache.metadatas[p.Name()] = info
+			}(p)
+		}
+	}
+	wg.Wait()
+
+	c.cache.Store(cache)
+}
+
 // URL Return URL in RouterChain
-func (c *RouterChain) URL() common.URL {
+func (c *RouterChain) URL() *common.URL {
 	return c.url
 }
 
@@ -118,14 +250,64 @@ func NewRouterChain(url *common.URL) (*RouterChain, error) {
 	chain := &RouterChain{
 		builtinRouters: routers,
 		routers:        newRouters,
+		last:           time.Now(),
+		notify:         make(chan struct{}),
 	}
 	if url != nil {
-		chain.url = *url
+		chain.url = url
 	}
 
+	go chain.loop()
 	return chain, nil
 }
 
+// poolRouter calls poolable router's Pool() to create new address pool and address metadata if necessary.
+// If the corresponding cache entry exists, and the poolable router answers no need to re-pool (possibly because its
+// rule doesn't change), and the address list doesn't change, then the existing data will be re-used.
+func poolRouter(p router.Poolable, origin *InvokerCache, invokers []protocol.Invoker) (router.AddrPool, router.AddrMetadata) {
+	name := p.Name()
+	if isCacheMiss(origin, name) || p.ShouldPool() || &(origin.invokers) != &invokers {
+		logger.Debugf("build address cache for router %q", name)
+		return p.Pool(invokers)
+	}
+
+	logger.Debugf("reuse existing address cache for router %q", name)
+	return origin.pools[name], origin.metadatas[name]
+}
+
+// isCacheMiss checks if the corresponding cache entry for a poolable router has already existed.
+// False returns when the cache is nil, or cache's pool is nil, or cache's invokers snapshot is nil, or the entry
+// doesn't exist.
+func isCacheMiss(cache *InvokerCache, key string) bool {
+	if cache == nil || cache.pools == nil || cache.invokers == nil || cache.pools[key] == nil {
+		return true
+	}
+	return false
+}
+
+// isInvokersChanged compares new invokers on the right changes, compared with the old invokers on the left.
+func isInvokersChanged(left []protocol.Invoker, right []protocol.Invoker) bool {
+	if len(right) != len(left) {
+		return true
+	}
+
+	for _, r := range right {
+		found := false
+		rurl := r.GetUrl()
+		for _, l := range left {
+			lurl := l.GetUrl()
+			if common.GetCompareURLEqualFunc()(lurl, rurl, constant.TIMESTAMP_KEY, constant.REMOTE_TIMESTAMP_KEY) {
+				found = true
+				break
+			}
+		}
+		if !found {
+			return true
+		}
+	}
+	return false
+}
+
 // sortRouter Sort router instance by priority with stable algorithm
 func sortRouter(routers []router.PriorityRouter) {
 	sort.Stable(byPriority(routers))
diff --git a/cluster/router/chain/chain_test.go b/cluster/router/chain/chain_test.go
index dec03894ebc73e315c2bb161911bdc67235e1ebb..b21990b08c5b960c407a395cddf7ef7518ba5822 100644
--- a/cluster/router/chain/chain_test.go
+++ b/cluster/router/chain/chain_test.go
@@ -65,7 +65,6 @@ func TestNewRouterChain(t *testing.T) {
 	assert.NoError(t, err)
 	err = z.Create(path)
 	assert.NoError(t, err)
-
 	testyml := `scope: application
 key: mock-app
 enabled: true
@@ -81,7 +80,7 @@ conditions:
 	defer z.Close()
 
 	zkUrl, _ := common.NewURL(fmt.Sprintf(zkFormat, localIP, ts.Servers[0].Port))
-	configuration, err := extension.GetConfigCenterFactory(zk).GetDynamicConfiguration(&zkUrl)
+	configuration, err := extension.GetConfigCenterFactory(zk).GetDynamicConfiguration(zkUrl)
 	config.GetEnvInstance().SetDynamicConfiguration(configuration)
 
 	assert.Nil(t, err)
@@ -133,7 +132,7 @@ conditions:
 	defer z.Close()
 
 	zkUrl, _ := common.NewURL(fmt.Sprintf(zkFormat, localIP, ts.Servers[0].Port))
-	configuration, err := extension.GetConfigCenterFactory(zk).GetDynamicConfiguration(&zkUrl)
+	configuration, err := extension.GetConfigCenterFactory(zk).GetDynamicConfiguration(zkUrl)
 	config.GetEnvInstance().SetDynamicConfiguration(configuration)
 
 	chain, err := NewRouterChain(getConditionRouteUrl(applicationKey))
@@ -159,7 +158,7 @@ func TestRouterChainRoute(t *testing.T) {
 	defer z.Close()
 
 	zkUrl, _ := common.NewURL(fmt.Sprintf(zkFormat, localIP, ts.Servers[0].Port))
-	configuration, err := extension.GetConfigCenterFactory(zk).GetDynamicConfiguration(&zkUrl)
+	configuration, err := extension.GetConfigCenterFactory(zk).GetDynamicConfiguration(zkUrl)
 	config.GetEnvInstance().SetDynamicConfiguration(configuration)
 
 	chain, err := NewRouterChain(getConditionRouteUrl(applicationKey))
@@ -169,15 +168,15 @@ func TestRouterChainRoute(t *testing.T) {
 	url := getConditionRouteUrl(applicationKey)
 	assert.NotNil(t, url)
 
-	invokers := []protocol.Invoker{}
+	var invokers []protocol.Invoker
 	dubboURL, _ := common.NewURL(fmt.Sprintf(dubboForamt, test1234IP, port20000))
 	invokers = append(invokers, protocol.NewBaseInvoker(dubboURL))
-
 	chain.SetInvokers(invokers)
+	chain.buildCache()
 
 	targetURL, _ := common.NewURL(fmt.Sprintf(consumerFormat, test1111IP))
 	inv := &invocation.RPCInvocation{}
-	finalInvokers := chain.Route(invokers, &targetURL, inv)
+	finalInvokers := chain.Route(targetURL, inv)
 
 	assert.Equal(t, 1, len(finalInvokers))
 }
@@ -203,20 +202,22 @@ conditions:
 	defer z.Close()
 
 	zkUrl, _ := common.NewURL(fmt.Sprintf(zkFormat, localIP, ts.Servers[0].Port))
-	configuration, err := extension.GetConfigCenterFactory(zk).GetDynamicConfiguration(&zkUrl)
+	configuration, err := extension.GetConfigCenterFactory(zk).GetDynamicConfiguration(zkUrl)
 	config.GetEnvInstance().SetDynamicConfiguration(configuration)
 
 	chain, err := NewRouterChain(getConditionRouteUrl(applicationKey))
 	assert.Nil(t, err)
 	assert.Equal(t, 2, len(chain.routers))
 
-	invokers := []protocol.Invoker{}
+	var invokers []protocol.Invoker
 	dubboURL, _ := common.NewURL(fmt.Sprintf(dubboForamt, test1234IP, port20000))
 	invokers = append(invokers, protocol.NewBaseInvoker(dubboURL))
+	chain.SetInvokers(invokers)
+	chain.buildCache()
 
 	targetURL, _ := common.NewURL(fmt.Sprintf(consumerFormat, test1111IP))
 	inv := &invocation.RPCInvocation{}
-	finalInvokers := chain.Route(invokers, &targetURL, inv)
+	finalInvokers := chain.Route(targetURL, inv)
 
 	assert.Equal(t, 0, len(finalInvokers))
 }
@@ -227,7 +228,7 @@ func TestRouterChainRouteNoRoute(t *testing.T) {
 	defer z.Close()
 
 	zkUrl, _ := common.NewURL(fmt.Sprintf(zkFormat, localIP, ts.Servers[0].Port))
-	configuration, err := extension.GetConfigCenterFactory(zk).GetDynamicConfiguration(&zkUrl)
+	configuration, err := extension.GetConfigCenterFactory(zk).GetDynamicConfiguration(zkUrl)
 	config.GetEnvInstance().SetDynamicConfiguration(configuration)
 
 	chain, err := NewRouterChain(getConditionNoRouteUrl(applicationKey))
@@ -236,13 +237,16 @@ func TestRouterChainRouteNoRoute(t *testing.T) {
 
 	url := getConditionRouteUrl(applicationKey)
 	assert.NotNil(t, url)
-	invokers := []protocol.Invoker{}
+
+	var invokers []protocol.Invoker
 	dubboURL, _ := common.NewURL(fmt.Sprintf(dubboForamt, test1234IP, port20000))
 	invokers = append(invokers, protocol.NewBaseInvoker(dubboURL))
+	chain.SetInvokers(invokers)
+	chain.buildCache()
 
 	targetURL, _ := common.NewURL(fmt.Sprintf(consumerFormat, test1111IP))
 	inv := &invocation.RPCInvocation{}
-	finalInvokers := chain.Route(invokers, &targetURL, inv)
+	finalInvokers := chain.Route(targetURL, inv)
 
 	assert.Equal(t, 0, len(finalInvokers))
 }
@@ -253,7 +257,7 @@ func getConditionNoRouteUrl(applicationKey string) *common.URL {
 	url.AddParam(forceField, forceValue)
 	rule := base64.URLEncoding.EncodeToString([]byte("host = 1.1.1.1 => host != 1.2.3.4"))
 	url.AddParam(constant.RULE_KEY, rule)
-	return &url
+	return url
 }
 
 func getConditionRouteUrl(applicationKey string) *common.URL {
@@ -262,12 +266,12 @@ func getConditionRouteUrl(applicationKey string) *common.URL {
 	url.AddParam(forceField, forceValue)
 	rule := base64.URLEncoding.EncodeToString([]byte("host = 1.1.1.1 => host = 1.2.3.4"))
 	url.AddParam(constant.RULE_KEY, rule)
-	return &url
+	return url
 }
 
 func getRouteUrl(applicationKey string) *common.URL {
 	url, _ := common.NewURL(fmt.Sprintf(anyUrlFormat, test0000IP))
 	url.AddParam(applicationField, applicationKey)
 	url.AddParam(forceField, forceValue)
-	return &url
+	return url
 }
diff --git a/cluster/router/chain/invoker_cache.go b/cluster/router/chain/invoker_cache.go
new file mode 100644
index 0000000000000000000000000000000000000000..43cdfa50670f5f09666dbea10a2837d58cafd1b0
--- /dev/null
+++ b/cluster/router/chain/invoker_cache.go
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package chain
+
+import (
+	"github.com/RoaringBitmap/roaring"
+)
+
+import (
+	"github.com/apache/dubbo-go/cluster/router"
+	"github.com/apache/dubbo-go/cluster/router/utils"
+	"github.com/apache/dubbo-go/protocol"
+)
+
+// Cache caches all addresses relevant info for a snapshot of received invokers. It keeps a snapshot of the received
+// address list, and also keeps address pools and address metadata from routers based on the same address snapshot, if
+// the router implements Poolable.
+type InvokerCache struct {
+	// The snapshot of invokers
+	invokers []protocol.Invoker
+
+	// The bitmap representation for invokers snapshot
+	bitmap *roaring.Bitmap
+
+	// Address pool from routers which implement Poolable
+	pools map[string]router.AddrPool
+
+	// Address metadata from routers which implement Poolable
+	metadatas map[string]router.AddrMetadata
+}
+
+// BuildCache builds address cache from the given invokers.
+func BuildCache(invokers []protocol.Invoker) *InvokerCache {
+	return &InvokerCache{
+		invokers:  invokers,
+		bitmap:    utils.ToBitmap(invokers),
+		pools:     make(map[string]router.AddrPool, 8),
+		metadatas: make(map[string]router.AddrMetadata, 8),
+	}
+}
+
+// GetInvokers get invokers snapshot.
+func (c *InvokerCache) GetInvokers() []protocol.Invoker {
+	return c.invokers
+}
+
+// FindAddrPool finds address pool for a poolable router.
+func (c *InvokerCache) FindAddrPool(p router.Poolable) router.AddrPool {
+	return c.pools[p.Name()]
+}
+
+// FindAddrMeta finds address metadata for a poolable router.
+func (c *InvokerCache) FindAddrMeta(p router.Poolable) router.AddrMetadata {
+	return c.metadatas[p.Name()]
+}
+
+// SetAddrPool sets address pool for a poolable router, for unit test only
+func (c *InvokerCache) SetAddrPool(name string, pool router.AddrPool) {
+	c.pools[name] = pool
+}
+
+// SetAddrMeta sets address metadata for a poolable router, for unit test only
+func (c *InvokerCache) SetAddrMeta(name string, meta router.AddrMetadata) {
+	c.metadatas[name] = meta
+}
diff --git a/cluster/router/condition/app_router_test.go b/cluster/router/condition/app_router_test.go
index cce96b12c95a691e828d91ba3d0629ddb6421954..879abc5cc8b607ee9245ce632800b056ff740cc5 100644
--- a/cluster/router/condition/app_router_test.go
+++ b/cluster/router/condition/app_router_test.go
@@ -71,7 +71,7 @@ conditions:
 	defer z.Close()
 
 	zkUrl, _ := common.NewURL(fmt.Sprintf(zkFormat, routerLocalIP, ts.Servers[0].Port))
-	configuration, err := extension.GetConfigCenterFactory(routerZk).GetDynamicConfiguration(&zkUrl)
+	configuration, err := extension.GetConfigCenterFactory(routerZk).GetDynamicConfiguration(zkUrl)
 	config.GetEnvInstance().SetDynamicConfiguration(configuration)
 
 	assert.Nil(t, err)
@@ -119,7 +119,7 @@ conditions:
 	defer z.Close()
 
 	zkUrl, _ := common.NewURL(fmt.Sprintf(zkFormat, routerLocalIP, ts.Servers[0].Port))
-	configuration, err := extension.GetConfigCenterFactory(routerZk).GetDynamicConfiguration(&zkUrl)
+	configuration, err := extension.GetConfigCenterFactory(routerZk).GetDynamicConfiguration(zkUrl)
 	config.GetEnvInstance().SetDynamicConfiguration(configuration)
 
 	assert.Nil(t, err)
@@ -158,7 +158,7 @@ conditions:
 	defer z.Close()
 
 	zkUrl, _ := common.NewURL(fmt.Sprintf(zkFormat, routerLocalIP, ts.Servers[0].Port))
-	configuration, err := extension.GetConfigCenterFactory(routerZk).GetDynamicConfiguration(&zkUrl)
+	configuration, err := extension.GetConfigCenterFactory(routerZk).GetDynamicConfiguration(zkUrl)
 	config.GetEnvInstance().SetDynamicConfiguration(configuration)
 
 	assert.Nil(t, err)
@@ -194,5 +194,5 @@ func getAppRouteURL(applicationKey string) *common.URL {
 	url, _ := common.NewURL(fmt.Sprintf(conditionFormat, constant.ANYHOST_VALUE))
 	url.AddParam("application", applicationKey)
 	url.AddParam("force", "true")
-	return &url
+	return url
 }
diff --git a/cluster/router/condition/factory_test.go b/cluster/router/condition/factory_test.go
index 0f61b39fc71af3aaeffc731974a0fa997503693e..c916588eeeef68b796c35ee6b3b175e8de859863 100644
--- a/cluster/router/condition/factory_test.go
+++ b/cluster/router/condition/factory_test.go
@@ -26,12 +26,14 @@ import (
 )
 
 import (
-	"github.com/dubbogo/gost/net"
 	perrors "github.com/pkg/errors"
 	"github.com/stretchr/testify/assert"
 )
 
 import (
+	"github.com/apache/dubbo-go/cluster/router"
+	"github.com/apache/dubbo-go/cluster/router/chain"
+	"github.com/apache/dubbo-go/cluster/router/utils"
 	"github.com/apache/dubbo-go/common"
 	"github.com/apache/dubbo-go/common/constant"
 	"github.com/apache/dubbo-go/common/logger"
@@ -50,13 +52,13 @@ const (
 )
 
 type MockInvoker struct {
-	url          common.URL
+	url          *common.URL
 	available    bool
 	destroyed    bool
 	successCount int
 }
 
-func NewMockInvoker(url common.URL, successCount int) *MockInvoker {
+func NewMockInvoker(url *common.URL, successCount int) *MockInvoker {
 	return &MockInvoker{
 		url:          url,
 		available:    true,
@@ -65,7 +67,7 @@ func NewMockInvoker(url common.URL, successCount int) *MockInvoker {
 	}
 }
 
-func (bi *MockInvoker) GetUrl() common.URL {
+func (bi *MockInvoker) GetUrl() *common.URL {
 	return bi.url
 }
 
@@ -73,20 +75,20 @@ func getRouteUrl(rule string) *common.URL {
 	url, _ := common.NewURL(fmt.Sprintf(factoryUrlFormat, constant.ANYHOST_VALUE))
 	url.AddParam("rule", rule)
 	url.AddParam("force", "true")
-	return &url
+	return url
 }
 
 func getRouteUrlWithForce(rule, force string) *common.URL {
 	url, _ := common.NewURL(fmt.Sprintf(factoryUrlFormat, constant.ANYHOST_VALUE))
 	url.AddParam("rule", rule)
 	url.AddParam("force", force)
-	return &url
+	return url
 }
 
 func getRouteUrlWithNoForce(rule string) *common.URL {
 	url, _ := common.NewURL(fmt.Sprintf(factoryUrlFormat, constant.ANYHOST_VALUE))
 	url.AddParam("rule", rule)
-	return &url
+	return url
 }
 
 func (bi *MockInvoker) IsAvailable() bool {
@@ -132,36 +134,36 @@ func TestRoute_matchWhen(t *testing.T) {
 	rule := base64.URLEncoding.EncodeToString([]byte("=> host = 1.2.3.4"))
 	router, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule))
 	cUrl, _ := common.NewURL(fmt.Sprintf(factoryDubboFormat, factory1111Ip))
-	matchWhen := router.(*ConditionRouter).MatchWhen(&cUrl, inv)
+	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().NewPriorityRouter(getRouteUrl(rule1))
-	matchWhen1 := router1.(*ConditionRouter).MatchWhen(&cUrl, inv)
+	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().NewPriorityRouter(getRouteUrl(rule2))
-	matchWhen2 := router2.(*ConditionRouter).MatchWhen(&cUrl, inv)
+	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().NewPriorityRouter(getRouteUrl(rule3))
-	matchWhen3 := router3.(*ConditionRouter).MatchWhen(&cUrl, inv)
+	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().NewPriorityRouter(getRouteUrl(rule4))
-	matchWhen4 := router4.(*ConditionRouter).MatchWhen(&cUrl, inv)
+	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().NewPriorityRouter(getRouteUrl(rule5))
-	matchWhen5 := router5.(*ConditionRouter).MatchWhen(&cUrl, inv)
+	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().NewPriorityRouter(getRouteUrl(rule6))
-	matchWhen6 := router6.(*ConditionRouter).MatchWhen(&cUrl, inv)
+	matchWhen6 := router6.(*ConditionRouter).MatchWhen(cUrl, inv)
 	assert.Equal(t, true, matchWhen6)
 }
 
 func TestRoute_matchFilter(t *testing.T) {
-	localIP, _ := gxnet.GetLocalIP()
+	localIP := common.GetLocalIp()
 	t.Logf("The local ip is %s", localIP)
 	url1, _ := common.NewURL("dubbo://10.20.3.3:20880/com.foo.BarService?default.serialization=fastjson")
 	url2, _ := common.NewURL(fmt.Sprintf(factoryDubboFormat, localIP))
@@ -180,70 +182,70 @@ func TestRoute_matchFilter(t *testing.T) {
 	router5, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule5))
 	router6, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule6))
 	cUrl, _ := common.NewURL(fmt.Sprintf(factoryConsumerFormat, localIP))
-	fileredInvokers1 := router1.Route(invokers, &cUrl, &invocation.RPCInvocation{})
-	fileredInvokers2 := router2.Route(invokers, &cUrl, &invocation.RPCInvocation{})
-	fileredInvokers3 := router3.Route(invokers, &cUrl, &invocation.RPCInvocation{})
-	fileredInvokers4 := router4.Route(invokers, &cUrl, &invocation.RPCInvocation{})
-	fileredInvokers5 := router5.Route(invokers, &cUrl, &invocation.RPCInvocation{})
-	fileredInvokers6 := router6.Route(invokers, &cUrl, &invocation.RPCInvocation{})
-	assert.Equal(t, 1, len(fileredInvokers1))
-	assert.Equal(t, 0, len(fileredInvokers2))
-	assert.Equal(t, 0, len(fileredInvokers3))
-	assert.Equal(t, 1, len(fileredInvokers4))
-	assert.Equal(t, 2, len(fileredInvokers5))
-	assert.Equal(t, 1, len(fileredInvokers6))
+	ret1 := router1.Route(utils.ToBitmap(invokers), setUpAddrCache(invokers), cUrl, &invocation.RPCInvocation{})
+	ret2 := router2.Route(utils.ToBitmap(invokers), setUpAddrCache(invokers), cUrl, &invocation.RPCInvocation{})
+	ret3 := router3.Route(utils.ToBitmap(invokers), setUpAddrCache(invokers), cUrl, &invocation.RPCInvocation{})
+	ret4 := router4.Route(utils.ToBitmap(invokers), setUpAddrCache(invokers), cUrl, &invocation.RPCInvocation{})
+	ret5 := router5.Route(utils.ToBitmap(invokers), setUpAddrCache(invokers), cUrl, &invocation.RPCInvocation{})
+	ret6 := router6.Route(utils.ToBitmap(invokers), setUpAddrCache(invokers), cUrl, &invocation.RPCInvocation{})
+	assert.Equal(t, 1, len(ret1.ToArray()))
+	assert.Equal(t, 0, len(ret2.ToArray()))
+	assert.Equal(t, 0, len(ret3.ToArray()))
+	assert.Equal(t, 1, len(ret4.ToArray()))
+	assert.Equal(t, 2, len(ret5.ToArray()))
+	assert.Equal(t, 1, len(ret6.ToArray()))
 
 }
 
 func TestRoute_methodRoute(t *testing.T) {
 	inv := invocation.NewRPCInvocationWithOptions(invocation.WithMethodName("getFoo"), invocation.WithParameterTypes([]reflect.Type{}), invocation.WithArguments([]interface{}{}))
 	rule := base64.URLEncoding.EncodeToString([]byte("host !=4.4.4.* & host = 2.2.2.2,1.1.1.1,3.3.3.3 => host = 1.2.3.4"))
-	router, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule))
+	r, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule))
 	url, _ := common.NewURL("consumer://1.1.1.1/com.foo.BarService?methods=setFoo,getFoo,findFoo")
-	matchWhen := router.(*ConditionRouter).MatchWhen(&url, inv)
+	matchWhen := r.(*ConditionRouter).MatchWhen(url, inv)
 	assert.Equal(t, true, matchWhen)
 	url1, _ := common.NewURL(fmt.Sprintf(factoryConsumerMethodFormat, factory1111Ip))
-	matchWhen = router.(*ConditionRouter).MatchWhen(&url1, inv)
+	matchWhen = r.(*ConditionRouter).MatchWhen(url1, inv)
 	assert.Equal(t, true, matchWhen)
 	url2, _ := common.NewURL(fmt.Sprintf(factoryConsumerMethodFormat, factory1111Ip))
 	rule2 := base64.URLEncoding.EncodeToString([]byte("methods=getFoo & host!=1.1.1.1 => host = 1.2.3.4"))
 	router2, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule2))
-	matchWhen = router2.(*ConditionRouter).MatchWhen(&url2, inv)
+	matchWhen = router2.(*ConditionRouter).MatchWhen(url2, inv)
 	assert.Equal(t, false, matchWhen)
 	url3, _ := common.NewURL(fmt.Sprintf(factoryConsumerMethodFormat, factory1111Ip))
 	rule3 := base64.URLEncoding.EncodeToString([]byte("methods=getFoo & host=1.1.1.1 => host = 1.2.3.4"))
 	router3, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule3))
-	matchWhen = router3.(*ConditionRouter).MatchWhen(&url3, inv)
+	matchWhen = router3.(*ConditionRouter).MatchWhen(url3, inv)
 	assert.Equal(t, true, matchWhen)
 
 }
 
 func TestRoute_ReturnFalse(t *testing.T) {
 	url, _ := common.NewURL("")
-	localIP, _ := gxnet.GetLocalIP()
+	localIP := common.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(fmt.Sprintf(factoryConsumerFormat, localIP))
-	router, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule))
-	fileredInvokers := router.(*ConditionRouter).Route(invokers, &curl, inv)
-	assert.Equal(t, 0, len(fileredInvokers))
+	r, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule))
+	ret := r.Route(utils.ToBitmap(invokers), setUpAddrCache(invokers), curl, inv)
+	assert.Equal(t, 0, len(ret.ToArray()))
 }
 
 func TestRoute_ReturnEmpty(t *testing.T) {
-	localIP, _ := gxnet.GetLocalIP()
+	localIP := common.GetLocalIp()
 	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(fmt.Sprintf(factoryConsumerFormat, localIP))
-	router, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule))
-	fileredInvokers := router.(*ConditionRouter).Route(invokers, &curl, inv)
-	assert.Equal(t, 0, len(fileredInvokers))
+	r, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule))
+	ret := r.Route(utils.ToBitmap(invokers), setUpAddrCache(invokers), curl, inv)
+	assert.Equal(t, 0, len(ret.ToArray()))
 }
 
 func TestRoute_ReturnAll(t *testing.T) {
-	localIP, _ := gxnet.GetLocalIP()
+	localIP := common.GetLocalIp()
 	urlString := "dubbo://" + localIP + "/com.foo.BarService"
 	dubboURL, _ := common.NewURL(urlString)
 	mockInvoker1 := NewMockInvoker(dubboURL, 1)
@@ -253,13 +255,13 @@ func TestRoute_ReturnAll(t *testing.T) {
 	inv := &invocation.RPCInvocation{}
 	rule := base64.URLEncoding.EncodeToString([]byte("host = " + localIP + " => " + " host = " + localIP))
 	curl, _ := common.NewURL(fmt.Sprintf(factoryConsumerFormat, localIP))
-	router, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule))
-	fileredInvokers := router.(*ConditionRouter).Route(invokers, &curl, inv)
-	assert.Equal(t, invokers, fileredInvokers)
+	r, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule))
+	ret := r.Route(utils.ToBitmap(invokers), setUpAddrCache(invokers), curl, inv)
+	assert.Equal(t, len(invokers), len(ret.ToArray()))
 }
 
 func TestRoute_HostFilter(t *testing.T) {
-	localIP, _ := gxnet.GetLocalIP()
+	localIP := common.GetLocalIp()
 	url1, _ := common.NewURL(factory333URL)
 	url2, _ := common.NewURL(fmt.Sprintf(factoryDubboFormat, localIP))
 	url3, _ := common.NewURL(fmt.Sprintf(factoryDubboFormat, localIP))
@@ -270,15 +272,15 @@ func TestRoute_HostFilter(t *testing.T) {
 	inv := &invocation.RPCInvocation{}
 	rule := base64.URLEncoding.EncodeToString([]byte("host = " + localIP + " => " + " host = " + localIP))
 	curl, _ := common.NewURL(fmt.Sprintf(factoryConsumerFormat, localIP))
-	router, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule))
-	fileredInvokers := router.(*ConditionRouter).Route(invokers, &curl, inv)
-	assert.Equal(t, 2, len(fileredInvokers))
-	assert.Equal(t, invoker2, fileredInvokers[0])
-	assert.Equal(t, invoker3, fileredInvokers[1])
+	r, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule))
+	ret := r.Route(utils.ToBitmap(invokers), setUpAddrCache(invokers), curl, inv)
+	assert.Equal(t, 2, len(ret.ToArray()))
+	assert.Equal(t, invoker2, invokers[ret.ToArray()[0]])
+	assert.Equal(t, invoker3, invokers[ret.ToArray()[1]])
 }
 
 func TestRoute_Empty_HostFilter(t *testing.T) {
-	localIP, _ := gxnet.GetLocalIP()
+	localIP := common.GetLocalIp()
 	url1, _ := common.NewURL(factory333URL)
 	url2, _ := common.NewURL(fmt.Sprintf(factoryDubboFormat, localIP))
 	url3, _ := common.NewURL(fmt.Sprintf(factoryDubboFormat, localIP))
@@ -289,15 +291,15 @@ func TestRoute_Empty_HostFilter(t *testing.T) {
 	inv := &invocation.RPCInvocation{}
 	rule := base64.URLEncoding.EncodeToString([]byte(" => " + " host = " + localIP))
 	curl, _ := common.NewURL(fmt.Sprintf(factoryConsumerFormat, localIP))
-	router, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule))
-	fileredInvokers := router.(*ConditionRouter).Route(invokers, &curl, inv)
-	assert.Equal(t, 2, len(fileredInvokers))
-	assert.Equal(t, invoker2, fileredInvokers[0])
-	assert.Equal(t, invoker3, fileredInvokers[1])
+	r, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule))
+	ret := r.Route(utils.ToBitmap(invokers), setUpAddrCache(invokers), curl, inv)
+	assert.Equal(t, 2, len(ret.ToArray()))
+	assert.Equal(t, invoker2, invokers[ret.ToArray()[0]])
+	assert.Equal(t, invoker3, invokers[ret.ToArray()[1]])
 }
 
 func TestRoute_False_HostFilter(t *testing.T) {
-	localIP, _ := gxnet.GetLocalIP()
+	localIP := common.GetLocalIp()
 	url1, _ := common.NewURL(factory333URL)
 	url2, _ := common.NewURL(fmt.Sprintf(factoryDubboFormat, localIP))
 	url3, _ := common.NewURL(fmt.Sprintf(factoryDubboFormat, localIP))
@@ -308,15 +310,15 @@ func TestRoute_False_HostFilter(t *testing.T) {
 	inv := &invocation.RPCInvocation{}
 	rule := base64.URLEncoding.EncodeToString([]byte("true => " + " host = " + localIP))
 	curl, _ := common.NewURL(fmt.Sprintf(factoryConsumerFormat, localIP))
-	router, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule))
-	fileredInvokers := router.(*ConditionRouter).Route(invokers, &curl, inv)
-	assert.Equal(t, 2, len(fileredInvokers))
-	assert.Equal(t, invoker2, fileredInvokers[0])
-	assert.Equal(t, invoker3, fileredInvokers[1])
+	r, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule))
+	ret := r.Route(utils.ToBitmap(invokers), setUpAddrCache(invokers), curl, inv)
+	assert.Equal(t, 2, len(ret.ToArray()))
+	assert.Equal(t, invoker2, invokers[ret.ToArray()[0]])
+	assert.Equal(t, invoker3, invokers[ret.ToArray()[1]])
 }
 
 func TestRoute_Placeholder(t *testing.T) {
-	localIP, _ := gxnet.GetLocalIP()
+	localIP := common.GetLocalIp()
 	url1, _ := common.NewURL(factory333URL)
 	url2, _ := common.NewURL(fmt.Sprintf(factoryDubboFormat, localIP))
 	url3, _ := common.NewURL(fmt.Sprintf(factoryDubboFormat, localIP))
@@ -327,15 +329,15 @@ func TestRoute_Placeholder(t *testing.T) {
 	inv := &invocation.RPCInvocation{}
 	rule := base64.URLEncoding.EncodeToString([]byte("host = " + localIP + " => " + " host = $host"))
 	curl, _ := common.NewURL(fmt.Sprintf(factoryConsumerFormat, localIP))
-	router, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule))
-	fileredInvokers := router.(*ConditionRouter).Route(invokers, &curl, inv)
-	assert.Equal(t, 2, len(fileredInvokers))
-	assert.Equal(t, invoker2, fileredInvokers[0])
-	assert.Equal(t, invoker3, fileredInvokers[1])
+	r, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule))
+	ret := r.Route(utils.ToBitmap(invokers), setUpAddrCache(invokers), curl, inv)
+	assert.Equal(t, 2, len(ret.ToArray()))
+	assert.Equal(t, invoker2, invokers[ret.ToArray()[0]])
+	assert.Equal(t, invoker3, invokers[ret.ToArray()[1]])
 }
 
 func TestRoute_NoForce(t *testing.T) {
-	localIP, _ := gxnet.GetLocalIP()
+	localIP := common.GetLocalIp()
 	url1, _ := common.NewURL(factory333URL)
 	url2, _ := common.NewURL(fmt.Sprintf(factoryDubboFormat, localIP))
 	url3, _ := common.NewURL(fmt.Sprintf(factoryDubboFormat, localIP))
@@ -346,13 +348,13 @@ func TestRoute_NoForce(t *testing.T) {
 	inv := &invocation.RPCInvocation{}
 	rule := base64.URLEncoding.EncodeToString([]byte(fmt.Sprintf(factoryHostIp1234Format, localIP)))
 	curl, _ := common.NewURL(fmt.Sprintf(factoryConsumerFormat, localIP))
-	router, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrlWithNoForce(rule))
-	fileredInvokers := router.(*ConditionRouter).Route(invokers, &curl, inv)
-	assert.Equal(t, invokers, fileredInvokers)
+	r, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrlWithNoForce(rule))
+	ret := r.Route(utils.ToBitmap(invokers), setUpAddrCache(invokers), curl, inv)
+	assert.Equal(t, len(invokers), len(ret.ToArray()))
 }
 
 func TestRoute_Force(t *testing.T) {
-	localIP, _ := gxnet.GetLocalIP()
+	localIP := common.GetLocalIp()
 	url1, _ := common.NewURL(factory333URL)
 	url2, _ := common.NewURL(fmt.Sprintf(factoryDubboFormat, localIP))
 	url3, _ := common.NewURL(fmt.Sprintf(factoryDubboFormat, localIP))
@@ -363,9 +365,9 @@ func TestRoute_Force(t *testing.T) {
 	inv := &invocation.RPCInvocation{}
 	rule := base64.URLEncoding.EncodeToString([]byte(fmt.Sprintf(factoryHostIp1234Format, localIP)))
 	curl, _ := common.NewURL(fmt.Sprintf(factoryConsumerFormat, localIP))
-	router, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrlWithForce(rule, "true"))
-	fileredInvokers := router.(*ConditionRouter).Route(invokers, &curl, inv)
-	assert.Equal(t, 0, len(fileredInvokers))
+	r, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrlWithForce(rule, "true"))
+	fileredInvokers := r.Route(utils.ToBitmap(invokers), setUpAddrCache(invokers), curl, inv)
+	assert.Equal(t, 0, len(fileredInvokers.ToArray()))
 }
 
 func TestNewConditionRouterFactory(t *testing.T) {
@@ -377,3 +379,7 @@ func TestNewAppRouterFactory(t *testing.T) {
 	factory := newAppRouterFactory()
 	assert.NotNil(t, factory)
 }
+
+func setUpAddrCache(addrs []protocol.Invoker) router.Cache {
+	return chain.BuildCache(addrs)
+}
diff --git a/cluster/router/condition/file.go b/cluster/router/condition/file.go
index 996db7443faff88d3baa1a2363f98fa62623d121..a97d9cb83b9c176a353a3851c5590df91eb22730 100644
--- a/cluster/router/condition/file.go
+++ b/cluster/router/condition/file.go
@@ -39,7 +39,7 @@ import (
 type FileConditionRouter struct {
 	listenableRouter
 	parseOnce sync.Once
-	url       common.URL
+	url       *common.URL
 }
 
 // NewFileConditionRouter Create file condition router instance with content ( from config file)
@@ -60,11 +60,11 @@ func NewFileConditionRouter(content []byte) (*FileConditionRouter, error) {
 }
 
 // URL Return URL in file condition router n
-func (f *FileConditionRouter) URL() common.URL {
+func (f *FileConditionRouter) URL() *common.URL {
 	f.parseOnce.Do(func() {
 		routerRule := f.routerRule
 		rule := parseCondition(routerRule.Conditions)
-		f.url = *common.NewURLWithOptions(
+		f.url = common.NewURLWithOptions(
 			common.WithProtocol(constant.CONDITION_ROUTE_PROTOCOL),
 			common.WithIp(constant.ANYHOST_VALUE),
 			common.WithParams(url.Values{}),
diff --git a/cluster/router/condition/listenable_router.go b/cluster/router/condition/listenable_router.go
index 7f4f14a8e47173253e2e5b7f4eed5db2bed64958..0b47310dbfe9987a593bc2d0d949a76f08052114 100644
--- a/cluster/router/condition/listenable_router.go
+++ b/cluster/router/condition/listenable_router.go
@@ -22,10 +22,12 @@ import (
 )
 
 import (
+	"github.com/RoaringBitmap/roaring"
 	perrors "github.com/pkg/errors"
 )
 
 import (
+	"github.com/apache/dubbo-go/cluster/router"
 	"github.com/apache/dubbo-go/common"
 	"github.com/apache/dubbo-go/common/config"
 	"github.com/apache/dubbo-go/common/constant"
@@ -64,14 +66,14 @@ func newListenableRouter(url *common.URL, ruleKey string) (*AppRouter, error) {
 	l.priority = listenableRouterDefaultPriority
 
 	routerKey := ruleKey + constant.ConditionRouterRuleSuffix
-	//add listener
+	// add listener
 	dynamicConfiguration := config.GetEnvInstance().GetDynamicConfiguration()
 	if dynamicConfiguration == nil {
 		return nil, perrors.Errorf("Get dynamicConfiguration fail, dynamicConfiguration is nil, init config center plugin please")
 	}
 
 	dynamicConfiguration.AddListener(routerKey, l)
-	//get rule
+	// get rule
 	rule, err := dynamicConfiguration.GetRule(routerKey, config_center.WithGroup(config_center.DEFAULT_GROUP))
 	if len(rule) == 0 || err != nil {
 		return nil, perrors.Errorf("Get rule fail, config rule{%s},  error{%v}", rule, err)
@@ -129,13 +131,13 @@ func (l *listenableRouter) generateConditions(rule *RouterRule) {
 }
 
 // Route Determine the target invokers list.
-func (l *listenableRouter) Route(invokers []protocol.Invoker, url *common.URL, invocation protocol.Invocation) []protocol.Invoker {
-	if len(invokers) == 0 || len(l.conditionRouters) == 0 {
+func (l *listenableRouter) Route(invokers *roaring.Bitmap, cache router.Cache, url *common.URL, invocation protocol.Invocation) *roaring.Bitmap {
+	if invokers.IsEmpty() || len(l.conditionRouters) == 0 {
 		return invokers
 	}
-	//We will check enabled status inside each router.
+	// We will check enabled status inside each router.
 	for _, r := range l.conditionRouters {
-		invokers = r.Route(invokers, url, invocation)
+		invokers = r.Route(invokers, cache, url, invocation)
 	}
 	return invokers
 }
@@ -146,6 +148,6 @@ func (l *listenableRouter) Priority() int64 {
 }
 
 // URL Return URL in listenable router
-func (l *listenableRouter) URL() common.URL {
-	return *l.url
+func (l *listenableRouter) URL() *common.URL {
+	return l.url
 }
diff --git a/cluster/router/condition/router.go b/cluster/router/condition/router.go
index 751b5a7111655577566c561614d39093485130cd..2fc33072d33517810f0c9ab3b8351d7bdcfea8b4 100644
--- a/cluster/router/condition/router.go
+++ b/cluster/router/condition/router.go
@@ -23,20 +23,22 @@ import (
 )
 
 import (
+	"github.com/RoaringBitmap/roaring"
+	"github.com/dubbogo/gost/container/set"
 	perrors "github.com/pkg/errors"
 )
 
 import (
+	"github.com/apache/dubbo-go/cluster/router"
+	"github.com/apache/dubbo-go/cluster/router/utils"
 	"github.com/apache/dubbo-go/common"
 	"github.com/apache/dubbo-go/common/constant"
 	"github.com/apache/dubbo-go/common/logger"
 	"github.com/apache/dubbo-go/protocol"
-	"github.com/dubbogo/gost/container/set"
-	"github.com/dubbogo/gost/net"
 )
 
 const (
-	//pattern route pattern regex
+	// pattern route pattern regex
 	pattern = `([&!=,]*)\\s*([^&!=,\\s]+)`
 )
 
@@ -136,8 +138,8 @@ func (c *ConditionRouter) Priority() int64 {
 }
 
 // URL Return URL in condition router
-func (c *ConditionRouter) URL() common.URL {
-	return *c.url
+func (c *ConditionRouter) URL() *common.URL {
+	return c.url
 }
 
 // Enabled Return is condition router is enabled
@@ -148,36 +150,44 @@ func (c *ConditionRouter) Enabled() bool {
 }
 
 // Route Determine the target invokers list.
-func (c *ConditionRouter) Route(invokers []protocol.Invoker, url *common.URL, invocation protocol.Invocation) []protocol.Invoker {
+func (c *ConditionRouter) Route(invokers *roaring.Bitmap, cache router.Cache, url *common.URL, invocation protocol.Invocation) *roaring.Bitmap {
 	if !c.Enabled() {
 		return invokers
 	}
-	if len(invokers) == 0 {
+
+	if invokers.IsEmpty() {
 		return invokers
 	}
+
 	isMatchWhen := c.MatchWhen(url, invocation)
 	if !isMatchWhen {
 		return invokers
 	}
-	var result []protocol.Invoker
+
 	if len(c.ThenCondition) == 0 {
-		return result
+		return utils.EmptyAddr
 	}
-	for _, invoker := range invokers {
+
+	result := roaring.NewBitmap()
+	for iter := invokers.Iterator(); iter.HasNext(); {
+		index := iter.Next()
+		invoker := cache.GetInvokers()[index]
 		invokerUrl := invoker.GetUrl()
-		isMatchThen := c.MatchThen(&invokerUrl, url)
+		isMatchThen := c.MatchThen(invokerUrl, url)
 		if isMatchThen {
-			result = append(result, invoker)
+			result.Add(index)
 		}
 	}
-	if len(result) > 0 {
+
+	if !result.IsEmpty() {
 		return result
 	} else if c.Force {
 		rule, _ := url.GetParamAndDecoded(constant.RULE_KEY)
-		localIP, _ := gxnet.GetLocalIP()
+		localIP := common.GetLocalIp()
 		logger.Warnf("The route result is empty and force execute. consumer: %s, service: %s, router: %s", localIP, url.Service(), rule)
 		return result
 	}
+
 	return invokers
 }
 
diff --git a/cluster/router/condition/router_rule_test.go b/cluster/router/condition/router_rule_test.go
index 369b14f08a6a650b36fe63a2d890c04fe7b892a5..192e5282f6f50c31b1106a5fc547a1616405a22a 100644
--- a/cluster/router/condition/router_rule_test.go
+++ b/cluster/router/condition/router_rule_test.go
@@ -80,5 +80,5 @@ conditions:
 
 func TestIsMatchGlobPattern(t *testing.T) {
 	url, _ := common.NewURL("dubbo://localhost:8080/Foo?key=v*e")
-	assert.Equal(t, true, isMatchGlobalPattern("$key", "value", &url))
+	assert.Equal(t, true, isMatchGlobalPattern("$key", "value", url))
 }
diff --git a/cluster/router/condition/router_test.go b/cluster/router/condition/router_test.go
index c27a1d9552a331d7e67af0eb3d444c480758891e..3d33ca275828b31ed480926d486c5844dac771c6 100644
--- a/cluster/router/condition/router_test.go
+++ b/cluster/router/condition/router_test.go
@@ -59,7 +59,7 @@ func TestParseRule(t *testing.T) {
 
 func TestNewConditionRouter(t *testing.T) {
 	url, _ := common.NewURL(`condition://0.0.0.0:?application=mock-app&category=routers&force=true&priority=1&router=condition&rule=YSAmIGMgPT4gYiAmIGQ%3D`)
-	router, err := NewConditionRouter(&url)
+	router, err := NewConditionRouter(url)
 	assert.Nil(t, err)
 	assert.Equal(t, true, router.Enabled())
 	assert.Equal(t, true, router.Force)
@@ -73,16 +73,16 @@ func TestNewConditionRouter(t *testing.T) {
 	assert.Error(t, err)
 
 	url, _ = common.NewURL(`condition://0.0.0.0:?application=mock-app&category=routers&force=true&priority=1&router=condition&rule=YSAmT4gYiAmIGQ%3D`)
-	router, err = NewConditionRouter(&url)
+	router, err = NewConditionRouter(url)
 	assert.Error(t, err)
 
 	url, _ = common.NewURL(`condition://0.0.0.0:?application=mock-app&category=routers&force=true&router=condition&rule=YSAmIGMgPT4gYiAmIGQ%3D`)
-	router, err = NewConditionRouter(&url)
+	router, err = NewConditionRouter(url)
 	assert.Nil(t, err)
 	assert.Equal(t, int64(150), router.Priority())
 
 	url, _ = common.NewURL(`condition://0.0.0.0:?category=routers&force=true&interface=mock-service&router=condition&rule=YSAmIGMgPT4gYiAmIGQ%3D`)
-	router, err = NewConditionRouter(&url)
+	router, err = NewConditionRouter(url)
 	assert.Nil(t, err)
 	assert.Equal(t, int64(140), router.Priority())
 }
diff --git a/cluster/router/healthcheck/default_health_check.go b/cluster/router/healthcheck/default_health_check.go
index c693b86ecdab7b32936185fe4bd614bd0f83fbeb..378463be56a43f4934d23c340d81d66bfdeeea4c 100644
--- a/cluster/router/healthcheck/default_health_check.go
+++ b/cluster/router/healthcheck/default_health_check.go
@@ -31,7 +31,7 @@ import (
 )
 
 func init() {
-	extension.SethealthChecker(constant.DEFAULT_HEALTH_CHECKER, NewDefaultHealthChecker)
+	extension.SetHealthChecker(constant.DEFAULT_HEALTH_CHECKER, NewDefaultHealthChecker)
 }
 
 // DefaultHealthChecker is the default implementation of HealthChecker, which determines the health status of
@@ -48,6 +48,10 @@ type DefaultHealthChecker struct {
 // 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 {
+	if !invoker.IsAvailable() {
+		return false
+	}
+
 	urlStatus := protocol.GetURLStatus(invoker.GetUrl())
 	if c.isCircuitBreakerTripped(urlStatus) || urlStatus.GetActive() > c.GetOutStandingRequestCountLimit() {
 		logger.Debugf("Invoker [%s] is currently in circuitbreaker tripped state", invoker.GetUrl().Key())
@@ -85,7 +89,7 @@ func (c *DefaultHealthChecker) getCircuitBreakerSleepWindowTime(status *protocol
 	} else if diff > constant.DEFAULT_SUCCESSIVE_FAILED_REQUEST_MAX_DIFF {
 		diff = constant.DEFAULT_SUCCESSIVE_FAILED_REQUEST_MAX_DIFF
 	}
-	sleepWindow := (1 << diff) * c.GetCircuitTrippedTimeoutFactor()
+	sleepWindow := (1 << uint(diff)) * c.GetCircuitTrippedTimeoutFactor()
 	if sleepWindow > constant.MAX_CIRCUIT_TRIPPED_TIMEOUT_IN_MS {
 		sleepWindow = constant.MAX_CIRCUIT_TRIPPED_TIMEOUT_IN_MS
 	}
@@ -110,8 +114,8 @@ func (c *DefaultHealthChecker) GetOutStandingRequestCountLimit() int32 {
 // 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)),
+		outStandingRequestConutLimit:      url.GetParamInt32(constant.OUTSTANDING_REQUEST_COUNT_LIMIT_KEY, math.MaxInt32),
+		requestSuccessiveFailureThreshold: url.GetParamInt32(constant.SUCCESSIVE_FAILED_REQUEST_THRESHOLD_KEY, constant.DEFAULT_SUCCESSIVE_FAILED_REQUEST_MAX_DIFF),
+		circuitTrippedTimeoutFactor:       url.GetParamInt32(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
index 5d35ae8e486e3f7b29b2a68a3864ef806a1053c7..39827c5f050a1a5ac9524345b59e37ec23efb90f 100644
--- a/cluster/router/healthcheck/default_health_check_test.go
+++ b/cluster/router/healthcheck/default_health_check_test.go
@@ -43,7 +43,7 @@ const (
 func TestDefaultHealthCheckerIsHealthy(t *testing.T) {
 	defer protocol.CleanAllStatus()
 	url, _ := common.NewURL(fmt.Sprintf(healthCheckDubboUrlFormat, healthCheckDubbo1010IP))
-	hc := NewDefaultHealthChecker(&url).(*DefaultHealthChecker)
+	hc := NewDefaultHealthChecker(url).(*DefaultHealthChecker)
 	invoker := NewMockInvoker(url)
 	healthy := hc.IsHealthy(invoker)
 	assert.True(t, healthy)
@@ -54,7 +54,7 @@ func TestDefaultHealthCheckerIsHealthy(t *testing.T) {
 	for i := 0; i < 11; i++ {
 		request(url, healthCheckMethodTest, 0, true, false)
 	}
-	hc = NewDefaultHealthChecker(&url).(*DefaultHealthChecker)
+	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))
@@ -65,7 +65,7 @@ func TestDefaultHealthCheckerIsHealthy(t *testing.T) {
 	}
 	url.SetParam(constant.SUCCESSIVE_FAILED_REQUEST_THRESHOLD_KEY, "10")
 	url.SetParam(constant.OUTSTANDING_REQUEST_COUNT_LIMIT_KEY, "1000")
-	hc = NewDefaultHealthChecker(&url).(*DefaultHealthChecker)
+	hc = NewDefaultHealthChecker(url).(*DefaultHealthChecker)
 	healthy = hc.IsHealthy(invoker)
 	assert.False(t, hc.IsHealthy(invoker))
 
@@ -78,7 +78,7 @@ func TestDefaultHealthCheckerIsHealthy(t *testing.T) {
 func TestDefaultHealthCheckerGetCircuitBreakerSleepWindowTime(t *testing.T) {
 	defer protocol.CleanAllStatus()
 	url, _ := common.NewURL(fmt.Sprintf(healthCheckDubboUrlFormat, healthCheckDubbo1010IP))
-	defaultHc := NewDefaultHealthChecker(&url).(*DefaultHealthChecker)
+	defaultHc := NewDefaultHealthChecker(url).(*DefaultHealthChecker)
 	// Increase the number of failed requests
 	for i := 0; i < 100; i++ {
 		request(url, healthCheckMethodTest, 1, false, false)
@@ -88,7 +88,7 @@ func TestDefaultHealthCheckerGetCircuitBreakerSleepWindowTime(t *testing.T) {
 
 	// Adjust the threshold size to 1000
 	url.SetParam(constant.SUCCESSIVE_FAILED_REQUEST_THRESHOLD_KEY, "1000")
-	sleepWindowTime = NewDefaultHealthChecker(&url).(*DefaultHealthChecker).getCircuitBreakerSleepWindowTime(protocol.GetURLStatus(url))
+	sleepWindowTime = NewDefaultHealthChecker(url).(*DefaultHealthChecker).getCircuitBreakerSleepWindowTime(protocol.GetURLStatus(url))
 	assert.True(t, sleepWindowTime == 0)
 
 	url1, _ := common.NewURL(fmt.Sprintf(healthCheckDubboUrlFormat, healthCheckDubbo1011IP))
@@ -107,7 +107,7 @@ func TestDefaultHealthCheckerGetCircuitBreakerSleepWindowTime(t *testing.T) {
 func TestDefaultHealthCheckerGetCircuitBreakerTimeout(t *testing.T) {
 	defer protocol.CleanAllStatus()
 	url, _ := common.NewURL(fmt.Sprintf(healthCheckDubboUrlFormat, healthCheckDubbo1010IP))
-	defaultHc := NewDefaultHealthChecker(&url).(*DefaultHealthChecker)
+	defaultHc := NewDefaultHealthChecker(url).(*DefaultHealthChecker)
 	timeout := defaultHc.getCircuitBreakerTimeout(protocol.GetURLStatus(url))
 	assert.True(t, timeout == 0)
 	url1, _ := common.NewURL(fmt.Sprintf(healthCheckDubboUrlFormat, healthCheckDubbo1011IP))
@@ -126,7 +126,7 @@ func TestDefaultHealthCheckerGetCircuitBreakerTimeout(t *testing.T) {
 func TestDefaultHealthCheckerIsCircuitBreakerTripped(t *testing.T) {
 	defer protocol.CleanAllStatus()
 	url, _ := common.NewURL(fmt.Sprintf(healthCheckDubboUrlFormat, healthCheckDubbo1010IP))
-	defaultHc := NewDefaultHealthChecker(&url).(*DefaultHealthChecker)
+	defaultHc := NewDefaultHealthChecker(url).(*DefaultHealthChecker)
 	status := protocol.GetURLStatus(url)
 	tripped := defaultHc.isCircuitBreakerTripped(status)
 	assert.False(t, tripped)
@@ -142,7 +142,7 @@ func TestDefaultHealthCheckerIsCircuitBreakerTripped(t *testing.T) {
 func TestNewDefaultHealthChecker(t *testing.T) {
 	defer protocol.CleanAllStatus()
 	url, _ := common.NewURL(fmt.Sprintf(healthCheckDubboUrlFormat, healthCheckDubbo1010IP))
-	defaultHc := NewDefaultHealthChecker(&url).(*DefaultHealthChecker)
+	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))
@@ -150,13 +150,13 @@ func TestNewDefaultHealthChecker(t *testing.T) {
 	url1, _ := common.NewURL(fmt.Sprintf(healthCheckDubboUrlFormat, healthCheckDubbo1010IP))
 	url1.SetParam(constant.OUTSTANDING_REQUEST_COUNT_LIMIT_KEY, "10")
 	url1.SetParam(constant.SUCCESSIVE_FAILED_REQUEST_THRESHOLD_KEY, "10")
-	nondefaultHc := NewDefaultHealthChecker(&url1).(*DefaultHealthChecker)
+	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) {
+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_test.go b/cluster/router/healthcheck/factory_test.go
index e80fd4c4d38dbb1c0f2b14ba1e22971249bc54b6..1e736832cecf07d523960fce8eebcc9248cee0a9 100644
--- a/cluster/router/healthcheck/factory_test.go
+++ b/cluster/router/healthcheck/factory_test.go
@@ -33,18 +33,18 @@ import (
 
 // nolint
 type MockInvoker struct {
-	url common.URL
+	url *common.URL
 }
 
 // nolint
-func NewMockInvoker(url common.URL) *MockInvoker {
+func NewMockInvoker(url *common.URL) *MockInvoker {
 	return &MockInvoker{
 		url: url,
 	}
 }
 
 // nolint
-func (bi *MockInvoker) GetUrl() common.URL {
+func (bi *MockInvoker) GetUrl() *common.URL {
 	return bi.url
 }
 
diff --git a/cluster/router/healthcheck/health_check_route.go b/cluster/router/healthcheck/health_check_route.go
index ee42e47e3b26c9a1976b4599d3464d752b615e0a..1a878af2127d2c5b979cfdb55ff1c1ec08463fe7 100644
--- a/cluster/router/healthcheck/health_check_route.go
+++ b/cluster/router/healthcheck/health_check_route.go
@@ -17,8 +17,13 @@
 
 package healthcheck
 
+import (
+	"github.com/RoaringBitmap/roaring"
+)
+
 import (
 	"github.com/apache/dubbo-go/cluster/router"
+	"github.com/apache/dubbo-go/cluster/router/utils"
 	"github.com/apache/dubbo-go/common"
 	"github.com/apache/dubbo-go/common/constant"
 	"github.com/apache/dubbo-go/common/extension"
@@ -28,6 +33,8 @@ import (
 
 const (
 	HEALTH_ROUTE_ENABLED_KEY = "health.route.enabled"
+	healthy                  = "healthy"
+	name                     = "health-check-router"
 )
 
 // HealthCheckRouter provides a health-first routing mechanism through HealthChecker
@@ -51,33 +58,56 @@ func NewHealthCheckRouter(url *common.URL) (router.PriorityRouter, error) {
 }
 
 // Route gets a list of healthy invoker
-func (r *HealthCheckRouter) Route(invokers []protocol.Invoker, url *common.URL, invocation protocol.Invocation) []protocol.Invoker {
+func (r *HealthCheckRouter) Route(invokers *roaring.Bitmap, cache router.Cache, url *common.URL, invocation protocol.Invocation) *roaring.Bitmap {
 	if !r.enabled {
 		return invokers
 	}
-	healthyInvokers := make([]protocol.Invoker, 0, len(invokers))
+
+	addrPool := cache.FindAddrPool(r)
 	// Add healthy invoker to the list
-	for _, invoker := range invokers {
-		if r.checker.IsHealthy(invoker) {
-			healthyInvokers = append(healthyInvokers, invoker)
-		}
-	}
-	// If all Invoke are considered unhealthy, downgrade to all inovker
-	if len(healthyInvokers) == 0 {
+	healthyInvokers := utils.JoinIfNotEqual(addrPool[healthy], invokers)
+	// If all invokers are considered unhealthy, downgrade to all invoker
+	if healthyInvokers.IsEmpty() {
 		logger.Warnf(" Now all invokers are unhealthy, so downgraded to all! Service: [%s]", url.ServiceKey())
 		return invokers
 	}
 	return healthyInvokers
 }
 
+// Pool separates healthy invokers from others.
+func (r *HealthCheckRouter) Pool(invokers []protocol.Invoker) (router.AddrPool, router.AddrMetadata) {
+	if !r.enabled {
+		return nil, nil
+	}
+
+	rb := make(router.AddrPool, 8)
+	rb[healthy] = roaring.NewBitmap()
+	for i, invoker := range invokers {
+		if r.checker.IsHealthy(invoker) {
+			rb[healthy].Add(uint32(i))
+		}
+	}
+
+	return rb, nil
+}
+
+// ShouldPool will always return true to make sure healthy check constantly.
+func (r *HealthCheckRouter) ShouldPool() bool {
+	return r.enabled
+}
+
+func (r *HealthCheckRouter) Name() string {
+	return name
+}
+
 // Priority
 func (r *HealthCheckRouter) Priority() int64 {
 	return 0
 }
 
 // URL Return URL in router
-func (r *HealthCheckRouter) URL() common.URL {
-	return *r.url
+func (r *HealthCheckRouter) URL() *common.URL {
+	return r.url
 }
 
 // HealthyChecker returns the HealthChecker bound to this HealthCheckRouter
diff --git a/cluster/router/healthcheck/health_check_route_test.go b/cluster/router/healthcheck/health_check_route_test.go
index d5862fb884114bac0ea2ec9ee8926baac57d5ba6..0730f105b7e010311576fc30adf94c8ad69b9614 100644
--- a/cluster/router/healthcheck/health_check_route_test.go
+++ b/cluster/router/healthcheck/health_check_route_test.go
@@ -29,6 +29,9 @@ import (
 )
 
 import (
+	"github.com/apache/dubbo-go/cluster/router"
+	"github.com/apache/dubbo-go/cluster/router/chain"
+	"github.com/apache/dubbo-go/cluster/router/utils"
 	"github.com/apache/dubbo-go/common"
 	"github.com/apache/dubbo-go/common/constant"
 	"github.com/apache/dubbo-go/protocol"
@@ -51,7 +54,7 @@ func TestHealthCheckRouterRoute(t *testing.T) {
 	url1, _ := common.NewURL(fmt.Sprintf(healthCheckRouteUrlFormat, healthCheckRoute1010IP))
 	url2, _ := common.NewURL(fmt.Sprintf(healthCheckRouteUrlFormat, healthCheckRoute1011IP))
 	url3, _ := common.NewURL(fmt.Sprintf(healthCheckRouteUrlFormat, healthCheckRoute1012IP))
-	hcr, _ := NewHealthCheckRouter(&consumerURL)
+	hcr, _ := NewHealthCheckRouter(consumerURL)
 
 	var invokers []protocol.Invoker
 	invoker1 := NewMockInvoker(url1)
@@ -59,25 +62,25 @@ func TestHealthCheckRouterRoute(t *testing.T) {
 	invoker3 := NewMockInvoker(url3)
 	invokers = append(invokers, invoker1, invoker2, invoker3)
 	inv := invocation.NewRPCInvocation(healthCheckRouteMethodNameTest, nil, nil)
-	res := hcr.Route(invokers, &consumerURL, inv)
+	res := hcr.Route(utils.ToBitmap(invokers), setUpAddrCache(hcr.(*HealthCheckRouter), invokers), consumerURL, inv)
 	// now all invokers are healthy
-	assert.True(t, len(res) == len(invokers))
+	assert.True(t, len(res.ToArray()) == len(invokers))
 
 	for i := 0; i < 10; i++ {
 		request(url1, healthCheckRouteMethodNameTest, 0, false, false)
 	}
-	res = hcr.Route(invokers, &consumerURL, inv)
+	res = hcr.Route(utils.ToBitmap(invokers), setUpAddrCache(hcr.(*HealthCheckRouter), invokers), consumerURL, inv)
 	// invokers1  is unhealthy now
-	assert.True(t, len(res) == 2 && !contains(res, invoker1))
+	assert.True(t, len(res.ToArray()) == 2 && !res.Contains(0))
 
 	for i := 0; i < 10; i++ {
 		request(url1, healthCheckRouteMethodNameTest, 0, false, false)
 		request(url2, healthCheckRouteMethodNameTest, 0, false, false)
 	}
 
-	res = hcr.Route(invokers, &consumerURL, inv)
+	res = hcr.Route(utils.ToBitmap(invokers), setUpAddrCache(hcr.(*HealthCheckRouter), invokers), consumerURL, inv)
 	// only invokers3  is healthy now
-	assert.True(t, len(res) == 1 && !contains(res, invoker1) && !contains(res, invoker2))
+	assert.True(t, len(res.ToArray()) == 1 && !res.Contains(0) && !res.Contains(1))
 
 	for i := 0; i < 10; i++ {
 		request(url1, healthCheckRouteMethodNameTest, 0, false, false)
@@ -85,46 +88,37 @@ func TestHealthCheckRouterRoute(t *testing.T) {
 		request(url3, healthCheckRouteMethodNameTest, 0, false, false)
 	}
 
-	res = hcr.Route(invokers, &consumerURL, inv)
+	res = hcr.Route(utils.ToBitmap(invokers), setUpAddrCache(hcr.(*HealthCheckRouter), invokers), consumerURL, inv)
 	// now all invokers are unhealthy, so downgraded to all
-	assert.True(t, len(res) == 3)
+	assert.True(t, len(res.ToArray()) == 3)
 
 	// reset the invoker1 successive failed count, so invoker1 go to healthy
 	request(url1, healthCheckRouteMethodNameTest, 0, false, true)
-	res = hcr.Route(invokers, &consumerURL, inv)
-	assert.True(t, contains(res, invoker1))
+	res = hcr.Route(utils.ToBitmap(invokers), setUpAddrCache(hcr.(*HealthCheckRouter), invokers), consumerURL, inv)
+	assert.True(t, res.Contains(0))
 
 	for i := 0; i < 6; i++ {
 		request(url1, healthCheckRouteMethodNameTest, 0, false, false)
 	}
 	// now all invokers are unhealthy, so downgraded to all again
-	res = hcr.Route(invokers, &consumerURL, inv)
-	assert.True(t, len(res) == 3)
+	res = hcr.Route(utils.ToBitmap(invokers), setUpAddrCache(hcr.(*HealthCheckRouter), invokers), consumerURL, inv)
+	assert.True(t, len(res.ToArray()) == 3)
 	time.Sleep(time.Second * 2)
 	// invoker1 go to healthy again after 2s
-	res = hcr.Route(invokers, &consumerURL, inv)
-	assert.True(t, contains(res, invoker1))
+	res = hcr.Route(utils.ToBitmap(invokers), setUpAddrCache(hcr.(*HealthCheckRouter), invokers), consumerURL, inv)
+	assert.True(t, res.Contains(0))
 
 }
 
-func contains(invokers []protocol.Invoker, invoker protocol.Invoker) bool {
-	for _, e := range invokers {
-		if e == invoker {
-			return true
-		}
-	}
-	return false
-}
-
 func TestNewHealthCheckRouter(t *testing.T) {
 	defer protocol.CleanAllStatus()
 	url, _ := common.NewURL(fmt.Sprintf(healthCheckDubboUrlFormat, healthCheckDubbo1010IP))
-	hcr, _ := NewHealthCheckRouter(&url)
+	hcr, _ := NewHealthCheckRouter(url)
 	h := hcr.(*HealthCheckRouter)
 	assert.Nil(t, h.checker)
 
 	url.SetParam(HEALTH_ROUTE_ENABLED_KEY, "true")
-	hcr, _ = NewHealthCheckRouter(&url)
+	hcr, _ = NewHealthCheckRouter(url)
 	h = hcr.(*HealthCheckRouter)
 	assert.NotNil(t, h.checker)
 
@@ -136,10 +130,18 @@ func TestNewHealthCheckRouter(t *testing.T) {
 	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)
+	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))
 }
+
+func setUpAddrCache(r router.Poolable, addrs []protocol.Invoker) router.Cache {
+	pool, info := r.Pool(addrs)
+	cache := chain.BuildCache(addrs)
+	cache.SetAddrMeta(r.Name(), info)
+	cache.SetAddrPool(r.Name(), pool)
+	return cache
+}
diff --git a/cluster/router/router.go b/cluster/router/router.go
index 66603c1d4d0efedad3489712ecea91b43254fffd..8a19dcf8cc2540ab0a30b8b6de8521e9759012ee 100644
--- a/cluster/router/router.go
+++ b/cluster/router/router.go
@@ -17,6 +17,10 @@
 
 package router
 
+import (
+	"github.com/RoaringBitmap/roaring"
+)
+
 import (
 	"github.com/apache/dubbo-go/common"
 	"github.com/apache/dubbo-go/protocol"
@@ -38,9 +42,10 @@ type FilePriorityRouterFactory interface {
 // Router
 type router interface {
 	// Route Determine the target invokers list.
-	Route([]protocol.Invoker, *common.URL, protocol.Invocation) []protocol.Invoker
+	Route(*roaring.Bitmap, Cache, *common.URL, protocol.Invocation) *roaring.Bitmap
+
 	// URL Return URL in router
-	URL() common.URL
+	URL() *common.URL
 }
 
 // Router
@@ -51,10 +56,38 @@ type PriorityRouter interface {
 	Priority() int64
 }
 
-// NotifyRouter notify router use the invoker list. Invoker list may change from time to time. This method gives the router a
-// chance to prepare before {@link Router#route(List, URL, Invocation)} gets called.
-type NotifyRouter interface {
-	PriorityRouter
-	// Notify notify whenever addresses in registry change
-	Notify([]protocol.Invoker)
+// Poolable caches address pool and address metadata for a router instance which will be used later in Router's Route.
+type Poolable interface {
+	// Pool created address pool and address metadata from the invokers.
+	Pool([]protocol.Invoker) (AddrPool, AddrMetadata)
+
+	// ShouldPool returns if it should pool. One typical scenario is a router rule changes, in this case, a pooling
+	// is necessary, even if the addresses not changed at all.
+	ShouldPool() bool
+
+	// Name return the Poolable's name.
+	Name() string
+}
+
+// AddrPool is an address pool, backed by a snapshot of address list, divided into categories.
+type AddrPool map[string]*roaring.Bitmap
+
+// AddrMetadta is address metadata, collected from a snapshot of address list by a router, if it implements Poolable.
+type AddrMetadata interface {
+	// Source indicates where the metadata comes from.
+	Source() string
+}
+
+// Cache caches all addresses relevant info for a snapshot of received invokers. It keeps a snapshot of the received
+// address list, and also keeps address pools and address metadata from routers based on the same address snapshot, if
+// the router implements Poolable.
+type Cache interface {
+	// GetInvokers returns the snapshot of received invokers.
+	GetInvokers() []protocol.Invoker
+
+	// FindAddrPool returns address pool associated with the given Poolable instance.
+	FindAddrPool(Poolable) AddrPool
+
+	// FindAddrMeta returns address metadata associated with the given Poolable instance.
+	FindAddrMeta(Poolable) AddrMetadata
 }
diff --git a/cluster/router/tag/factory_test.go b/cluster/router/tag/factory_test.go
index ee195820c123e1fc67a2c27cd12aaa544650b615..b350bb2a915ce9abae98ba46bda346fba7ad6b5b 100644
--- a/cluster/router/tag/factory_test.go
+++ b/cluster/router/tag/factory_test.go
@@ -39,7 +39,7 @@ func TestTagRouterFactoryNewRouter(t *testing.T) {
 	u1, err := common.NewURL(fmt.Sprintf(factoryFormat, factoryLocalIP))
 	assert.Nil(t, err)
 	factory := NewTagRouterFactory()
-	tagRouter, e := factory.NewPriorityRouter(&u1)
+	tagRouter, e := factory.NewPriorityRouter(u1)
 	assert.Nil(t, e)
 	assert.NotNil(t, tagRouter)
 }
diff --git a/cluster/router/tag/file.go b/cluster/router/tag/file.go
index 433abcb72eb6e201e64790af932e847d8faba8af..94daf1508eb7b3f4d8a8cacdbd6ed634be6852da 100644
--- a/cluster/router/tag/file.go
+++ b/cluster/router/tag/file.go
@@ -24,10 +24,12 @@ import (
 )
 
 import (
+	"github.com/RoaringBitmap/roaring"
 	perrors "github.com/pkg/errors"
 )
 
 import (
+	"github.com/apache/dubbo-go/cluster/router"
 	"github.com/apache/dubbo-go/common"
 	"github.com/apache/dubbo-go/common/constant"
 	"github.com/apache/dubbo-go/protocol"
@@ -50,13 +52,12 @@ func NewFileTagRouter(content []byte) (*FileTagRouter, error) {
 		return nil, perrors.Errorf("yaml.Unmarshal() failed , error:%v", perrors.WithStack(err))
 	}
 	fileRouter.routerRule = rule
-	url := fileRouter.URL()
-	fileRouter.router, err = NewTagRouter(&url)
+	fileRouter.router, err = NewTagRouter(fileRouter.URL())
 	return fileRouter, err
 }
 
 // URL Return URL in file tag router n
-func (f *FileTagRouter) URL() common.URL {
+func (f *FileTagRouter) URL() *common.URL {
 	f.parseOnce.Do(func() {
 		routerRule := f.routerRule
 		f.url = common.NewURLWithOptions(
@@ -66,7 +67,7 @@ func (f *FileTagRouter) URL() common.URL {
 			common.WithParamsValue(constant.RouterPriority, strconv.Itoa(routerRule.Priority)),
 			common.WithParamsValue(constant.ROUTER_KEY, constant.TAG_ROUTE_PROTOCOL))
 	})
-	return *f.url
+	return f.url
 }
 
 // Priority Return Priority in listenable router
@@ -74,9 +75,10 @@ func (f *FileTagRouter) Priority() int64 {
 	return f.router.priority
 }
 
-func (f *FileTagRouter) Route(invokers []protocol.Invoker, url *common.URL, invocation protocol.Invocation) []protocol.Invoker {
-	if len(invokers) == 0 {
+func (f *FileTagRouter) Route(invokers *roaring.Bitmap, cache router.Cache, url *common.URL, invocation protocol.Invocation) *roaring.Bitmap {
+	if invokers.IsEmpty() {
 		return invokers
 	}
-	return f.Route(invokers, url, invocation)
+	// FIXME: I believe this is incorrect.
+	return f.Route(invokers, cache, url, invocation)
 }
diff --git a/cluster/router/tag/tag_router.go b/cluster/router/tag/tag_router.go
index a5f1dc13d9385fe6bc230e79337230e674b97e96..c7f53047c1e2beee4545a10302e9f307f06c33d8 100644
--- a/cluster/router/tag/tag_router.go
+++ b/cluster/router/tag/tag_router.go
@@ -18,17 +18,20 @@
 package tag
 
 import (
-	"net"
 	"strconv"
+	"strings"
+	"sync"
 )
 
 import (
+	"github.com/RoaringBitmap/roaring"
 	gxnet "github.com/dubbogo/gost/net"
-	"github.com/jinzhu/copier"
 	perrors "github.com/pkg/errors"
 )
 
 import (
+	"github.com/apache/dubbo-go/cluster/router"
+	"github.com/apache/dubbo-go/cluster/router/utils"
 	"github.com/apache/dubbo-go/common"
 	"github.com/apache/dubbo-go/common/config"
 	"github.com/apache/dubbo-go/common/constant"
@@ -38,6 +41,32 @@ import (
 	"github.com/apache/dubbo-go/remoting"
 )
 
+const (
+	name          = "tag-router"
+	staticPrefix  = "static-"
+	dynamicPrefix = "dynamic-"
+)
+
+// addrMetadata keeps snapshot data for app name, and some infos extracted from dynamic tag rule in order to make
+// Route() method lock-free.
+type addrMetadata struct {
+	// application name
+	application string
+	// is rule a runtime rule
+	ruleRuntime bool
+	// is rule a force rule
+	ruleForce bool
+	// is rule a valid rule
+	ruleValid bool
+	// is rule an enabled rule
+	ruleEnabled bool
+}
+
+// Source indicates where the metadata comes from.
+func (m *addrMetadata) Source() string {
+	return name
+}
+
 // tagRouter defines url, enable and the priority
 type tagRouter struct {
 	url           *common.URL
@@ -45,6 +74,8 @@ type tagRouter struct {
 	enabled       bool
 	priority      int64
 	application   string
+	ruleChanged   bool
+	mutex         sync.RWMutex
 }
 
 // NewTagRouter returns a tagRouter instance if url is not nil
@@ -65,56 +96,79 @@ func (c *tagRouter) isEnabled() bool {
 }
 
 // Route gets a list of invoker
-func (c *tagRouter) Route(invokers []protocol.Invoker, url *common.URL, invocation protocol.Invocation) []protocol.Invoker {
-	var (
-		result    []protocol.Invoker
-		addresses []string
-	)
+func (c *tagRouter) Route(invokers *roaring.Bitmap, cache router.Cache, url *common.URL, invocation protocol.Invocation) *roaring.Bitmap {
+	if !c.isEnabled() || invokers.IsEmpty() {
+		return invokers
+	}
+
+	if shouldUseDynamicTag(cache.FindAddrMeta(c)) {
+		return c.routeWithDynamicTag(invokers, cache, url, invocation)
+	}
+	return c.routeWithStaticTag(invokers, cache, url, invocation)
+}
 
-	if !c.isEnabled() || len(invokers) == 0 {
+// routeWithStaticTag routes with static tag rule
+func (c *tagRouter) routeWithStaticTag(invokers *roaring.Bitmap, cache router.Cache, url *common.URL, invocation protocol.Invocation) *roaring.Bitmap {
+	tag := findTag(invocation, url)
+	if tag == "" {
 		return invokers
 	}
 
-	// Use static tags if dynamic tags are not set or invalid
-	if c.tagRouterRule == nil || !c.tagRouterRule.Valid || !c.tagRouterRule.Enabled {
-		return filterUsingStaticTag(invokers, url, invocation)
+	ret, _ := c.filterWithTag(invokers, cache, staticPrefix+tag)
+	if ret.IsEmpty() && !isForceUseTag(url, invocation) {
+		return invokers
 	}
 
-	// since the rule can be changed by config center, we should copy one to use.
-	tagRouterRuleCopy := new(RouterRule)
-	_ = copier.Copy(tagRouterRuleCopy, c.tagRouterRule)
-	tag, ok := invocation.Attachments()[constant.Tagkey]
-	if !ok {
-		tag = url.GetParam(constant.Tagkey, "")
+	return ret
+}
+
+// routeWithDynamicTag routes with dynamic tag rule
+func (c *tagRouter) routeWithDynamicTag(invokers *roaring.Bitmap, cache router.Cache, url *common.URL, invocation protocol.Invocation) *roaring.Bitmap {
+	tag := findTag(invocation, url)
+	if tag == "" {
+		return c.filterNotInDynamicTag(invokers, cache)
 	}
 
-	// if we are requesting for a Provider with a specific tag
-	if len(tag) > 0 {
-		return filterInvokersWithTag(invokers, url, invocation, *tagRouterRuleCopy, tag)
+	ret, ok := c.filterWithTag(invokers, cache, dynamicPrefix+tag)
+	if ok && (!ret.IsEmpty() || isTagRuleForce(cache.FindAddrMeta(c))) {
+		return ret
 	}
 
-	// return all addresses in dynamic tag group.
-	addresses = tagRouterRuleCopy.getAddresses()
-	if len(addresses) > 0 {
-		filterAddressNotMatches := func(invoker protocol.Invoker) bool {
-			url := invoker.GetUrl()
-			return len(addresses) == 0 || !checkAddressMatch(addresses, url.Ip, url.Port)
-		}
-		result = filterInvoker(invokers, filterAddressNotMatches)
-		// 1. all addresses are in dynamic tag group, return empty list.
-		if len(result) == 0 {
-			return result
+	// dynamic tag group doesn't have any item about the requested app OR it's null after filtered by
+	// dynamic tag group but force=false. check static tag
+	if ret.IsEmpty() {
+		ret, _ = c.filterWithTag(invokers, cache, staticPrefix+tag)
+		// If there's no tagged providers that can match the current tagged request. force.tag is set by default
+		// to false, which means it will invoke any providers without a tag unless it's explicitly disallowed.
+		if !ret.IsEmpty() || isForceUseTag(url, invocation) {
+			return ret
 		}
+		return c.filterNotInDynamicTag(invokers, cache)
 	}
-	// 2. if there are some addresses that are not in any dynamic tag group, continue to filter using the
-	// static tag group.
-	filter := func(invoker protocol.Invoker) bool {
-		localTag := invoker.GetUrl().GetParam(constant.Tagkey, "")
-		return len(localTag) == 0 || !(tagRouterRuleCopy.hasTag(localTag))
+	return ret
+}
+
+// filterWithTag filters incoming invokers with the given tag
+func (c *tagRouter) filterWithTag(invokers *roaring.Bitmap, cache router.Cache, tag string) (*roaring.Bitmap, bool) {
+	if target, ok := cache.FindAddrPool(c)[tag]; ok {
+		return utils.JoinIfNotEqual(target, invokers), true
 	}
-	return filterInvoker(result, filter)
+	return utils.EmptyAddr, false
 }
 
+// filterNotInDynamicTag filters incoming invokers not applied to dynamic tag rule
+func (c *tagRouter) filterNotInDynamicTag(invokers *roaring.Bitmap, cache router.Cache) *roaring.Bitmap {
+	// FAILOVER: return all Providers without any tags.
+	invokers = invokers.Clone()
+	for k, v := range cache.FindAddrPool(c) {
+		if strings.HasPrefix(k, dynamicPrefix) {
+			invokers.AndNot(v)
+		}
+	}
+	return invokers
+}
+
+// Process parses dynamic tag rule
 func (c *tagRouter) Process(event *config_center.ConfigChangeEvent) {
 	logger.Infof("Notification of tag rule, change type is:[%s] , raw rule is:[%v]", event.ConfigType, event.Value)
 	if remoting.EventTypeDel == event.ConfigType {
@@ -132,16 +186,52 @@ func (c *tagRouter) Process(event *config_center.ConfigChangeEvent) {
 		logger.Errorf("Parse dynamic tag router rule fail,error:[%s] ", err)
 		return
 	}
+
+	c.mutex.Lock()
+	defer c.mutex.Unlock()
 	c.tagRouterRule = routerRule
-	return
+	c.ruleChanged = true
+}
+
+// URL gets the url of tagRouter
+func (c *tagRouter) URL() *common.URL {
+	return c.url
 }
 
-func (c *tagRouter) Notify(invokers []protocol.Invoker) {
-	if len(invokers) == 0 {
+// Priority gets the priority of tagRouter
+func (c *tagRouter) Priority() int64 {
+	return c.priority
+}
+
+// Pool divided invokers into different address pool by tag.
+func (c *tagRouter) Pool(invokers []protocol.Invoker) (router.AddrPool, router.AddrMetadata) {
+	c.fetchRuleIfNecessary(invokers)
+
+	rb := make(router.AddrPool, 8)
+	poolWithStaticTag(invokers, rb)
+
+	c.mutex.Lock()
+	defer c.mutex.Unlock()
+	poolWithDynamicTag(invokers, c.tagRouterRule, rb)
+	c.ruleChanged = false
+	// create metadata in order to avoid lock in route()
+	meta := addrMetadata{application: c.application}
+	if c.tagRouterRule != nil {
+		meta.ruleForce = c.tagRouterRule.Force
+		meta.ruleEnabled = c.tagRouterRule.Enabled
+		meta.ruleValid = c.tagRouterRule.Valid
+	}
+
+	return rb, &meta
+}
+
+// fetchRuleIfNecessary fetches, parses rule and register listener for the further change
+func (c *tagRouter) fetchRuleIfNecessary(invokers []protocol.Invoker) {
+	if invokers == nil || len(invokers) == 0 {
 		return
 	}
-	invoker := invokers[0]
-	url := invoker.GetUrl()
+
+	url := invokers[0].GetUrl()
 	providerApplication := url.GetParam(constant.RemoteApplicationKey, "")
 	if len(providerApplication) == 0 {
 		logger.Error("TagRouter must getConfig from or subscribe to a specific application, but the application " +
@@ -156,11 +246,15 @@ func (c *tagRouter) Notify(invokers []protocol.Invoker) {
 
 	if providerApplication != c.application {
 		dynamicConfiguration.RemoveListener(c.application+constant.TagRouterRuleSuffix, c)
+	} else {
+		// if app name from URL is as same as the current app name, then it is safe to jump out
+		return
 	}
 
+	c.application = providerApplication
 	routerKey := providerApplication + constant.TagRouterRuleSuffix
 	dynamicConfiguration.AddListener(routerKey, c)
-	//get rule
+	// get rule
 	rule, err := dynamicConfiguration.GetRule(routerKey, config_center.WithGroup(config_center.DEFAULT_GROUP))
 	if len(rule) == 0 || err != nil {
 		logger.Errorf("Get rule fail, config rule{%s},  error{%v}", rule, err)
@@ -174,71 +268,52 @@ func (c *tagRouter) Notify(invokers []protocol.Invoker) {
 	}
 }
 
-// URL gets the url of tagRouter
-func (c *tagRouter) URL() common.URL {
-	return *c.url
+// ShouldPool returns false, to make sure address cache for tag router happens once and only once.
+func (c *tagRouter) ShouldPool() bool {
+	c.mutex.RLock()
+	defer c.mutex.RUnlock()
+	return c.ruleChanged
 }
 
-// Priority gets the priority of tagRouter
-func (c *tagRouter) Priority() int64 {
-	return c.priority
+// Name returns pool's name
+func (c *tagRouter) Name() string {
+	return name
 }
 
-// filterUsingStaticTag gets a list of invoker using static tag
-func filterUsingStaticTag(invokers []protocol.Invoker, url *common.URL, invocation protocol.Invocation) []protocol.Invoker {
-	if tag, ok := invocation.Attachments()[constant.Tagkey]; ok {
-		result := make([]protocol.Invoker, 0, 8)
-		for _, v := range invokers {
-			if v.GetUrl().GetParam(constant.Tagkey, "") == tag {
-				result = append(result, v)
-			}
-		}
-		if len(result) == 0 && !isForceUseTag(url, invocation) {
-			return invokers
-		}
-		return result
+// poolWithDynamicTag pools addresses with the tags defined in dynamic tag rule, all keys have prefix "dynamic-"
+func poolWithDynamicTag(invokers []protocol.Invoker, rule *RouterRule, pool router.AddrPool) {
+	if rule == nil {
+		return
 	}
-	return invokers
-}
 
-// filterInvokersWithTag gets a list of invoker using dynamic route with tag
-func filterInvokersWithTag(invokers []protocol.Invoker, url *common.URL, invocation protocol.Invocation, tagRouterRule RouterRule, tag string) []protocol.Invoker {
-	var (
-		result    []protocol.Invoker
-		addresses []string
-	)
-	addresses, _ = tagRouterRule.getTagNameToAddresses()[tag]
-	// filter by dynamic tag group first
-	if len(addresses) > 0 {
-		filterAddressMatches := func(invoker protocol.Invoker) bool {
-			url := invoker.GetUrl()
-			return len(addresses) > 0 && checkAddressMatch(addresses, url.Ip, url.Port)
-		}
-		result = filterInvoker(invokers, filterAddressMatches)
-		if len(result) > 0 || tagRouterRule.Force {
-			return result
-		}
+	tagNameToAddresses := rule.getTagNameToAddresses()
+	for tag, addrs := range tagNameToAddresses {
+		pool[dynamicPrefix+tag] = addrsToBitmap(addrs, invokers)
 	}
-	// dynamic tag group doesn't have any item about the requested app OR it's null after filtered by
-	// dynamic tag group but force=false. check static tag
-	filter := func(invoker protocol.Invoker) bool {
-		return invoker.GetUrl().GetParam(constant.Tagkey, "") == tag
-	}
-	result = filterInvoker(invokers, filter)
-	// If there's no tagged providers that can match the current tagged request. force.tag is set by default
-	// to false, which means it will invoke any providers without a tag unless it's explicitly disallowed.
-	if len(result) > 0 || isForceUseTag(url, invocation) {
-		return result
-	}
-	// FAILOVER: return all Providers without any tags.
-	filterAddressNotMatches := func(invoker protocol.Invoker) bool {
+}
+
+// poolWithStaticTag pools addresses with tags found from incoming URLs, all keys have prefix "static-"
+func poolWithStaticTag(invokers []protocol.Invoker, pool router.AddrPool) {
+	for i, invoker := range invokers {
 		url := invoker.GetUrl()
-		return len(addresses) == 0 || !checkAddressMatch(tagRouterRule.getAddresses(), url.Ip, url.Port)
-	}
-	filterTagIsEmpty := func(invoker protocol.Invoker) bool {
-		return invoker.GetUrl().GetParam(constant.Tagkey, "") == ""
+		tag := url.GetParam(constant.Tagkey, "")
+		if len(tag) > 0 {
+			if _, ok := pool[staticPrefix+tag]; !ok {
+				pool[staticPrefix+tag] = roaring.NewBitmap()
+			}
+			pool[staticPrefix+tag].AddInt(i)
+		}
 	}
-	return filterInvoker(invokers, filterAddressNotMatches, filterTagIsEmpty)
+}
+
+// shouldUseDynamicTag uses the snapshot data from the parsed rule to decide if dynamic tag rule should be used or not
+func shouldUseDynamicTag(meta router.AddrMetadata) bool {
+	return meta.(*addrMetadata).ruleValid && meta.(*addrMetadata).ruleEnabled
+}
+
+// isTagRuleForce uses the snapshot data from the parsed rule to decide if dynamic tag rule is forced or not
+func isTagRuleForce(meta router.AddrMetadata) bool {
+	return meta.(*addrMetadata).ruleForce
 }
 
 // isForceUseTag returns whether force use tag
@@ -249,31 +324,46 @@ func isForceUseTag(url *common.URL, invocation protocol.Invocation) bool {
 	return false
 }
 
-type filter func(protocol.Invoker) bool
+// addrsToBitmap finds indexes for the given IP addresses in the target URL list, if any '0.0.0.0' IP address is met,
+// then returns back all indexes of the URLs list.
+func addrsToBitmap(addrs []string, invokers []protocol.Invoker) *roaring.Bitmap {
+	ret := roaring.NewBitmap()
+	for _, addr := range addrs {
+		if isAnyHost(addr) {
+			ret.AddRange(0, uint64(len(invokers)))
+			return ret
+		}
 
-func filterInvoker(invokers []protocol.Invoker, filters ...filter) []protocol.Invoker {
-	var res []protocol.Invoker
-OUTER:
-	for _, invoker := range invokers {
-		for _, filter := range filters {
-			if !filter(invoker) {
-				continue OUTER
-			}
+		index := findIndexWithIp(addr, invokers)
+		if index != -1 {
+			ret.AddInt(index)
 		}
-		res = append(res, invoker)
 	}
-	return res
+	return ret
 }
 
-func checkAddressMatch(addresses []string, host, port string) bool {
-	addr := net.JoinHostPort(constant.ANYHOST_VALUE, port)
-	for _, address := range addresses {
-		if gxnet.MatchIP(address, host, port) {
-			return true
-		}
-		if address == addr {
-			return true
+// findIndexWithIp finds index for one particular IP
+func findIndexWithIp(addr string, invokers []protocol.Invoker) int {
+	for i, invoker := range invokers {
+		if gxnet.MatchIP(addr, invoker.GetUrl().Ip, invoker.GetUrl().Port) {
+			return i
 		}
 	}
-	return false
+	return -1
+}
+
+// isAnyHost checks if an IP is '0.0.0.0'
+func isAnyHost(addr string) bool {
+	return strings.HasPrefix(addr, constant.ANYHOST_VALUE)
+}
+
+// findTag finds tag, first from invocation's attachment, then from URL
+func findTag(invocation protocol.Invocation, consumerUrl *common.URL) string {
+	tag, ok := invocation.Attachments()[constant.Tagkey]
+	if !ok {
+		return consumerUrl.GetParam(constant.Tagkey, "")
+	} else if v, t := tag.(string); t {
+		return v
+	}
+	return ""
 }
diff --git a/cluster/router/tag/tag_router_test.go b/cluster/router/tag/tag_router_test.go
index 6b9a5e08f133403565d8b2dd38e03a789c5e95e7..3f7b979f3d52d51ab50c94ea32e87b5eb4f9591e 100644
--- a/cluster/router/tag/tag_router_test.go
+++ b/cluster/router/tag/tag_router_test.go
@@ -25,15 +25,18 @@ import (
 )
 
 import (
+	"github.com/RoaringBitmap/roaring"
 	"github.com/dubbogo/go-zookeeper/zk"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/suite"
 )
 
 import (
+	"github.com/apache/dubbo-go/cluster/router"
+	"github.com/apache/dubbo-go/cluster/router/chain"
+	"github.com/apache/dubbo-go/cluster/router/utils"
 	"github.com/apache/dubbo-go/common"
 	"github.com/apache/dubbo-go/common/config"
-	"github.com/apache/dubbo-go/common/constant"
 	"github.com/apache/dubbo-go/common/extension"
 	"github.com/apache/dubbo-go/config_center"
 	_ "github.com/apache/dubbo-go/config_center/zookeeper"
@@ -76,13 +79,13 @@ var (
 
 // MockInvoker is only mock the Invoker to support test tagRouter
 type MockInvoker struct {
-	url          common.URL
+	url          *common.URL
 	available    bool
 	destroyed    bool
 	successCount int
 }
 
-func NewMockInvoker(url common.URL) *MockInvoker {
+func NewMockInvoker(url *common.URL) *MockInvoker {
 	return &MockInvoker{
 		url:          url,
 		available:    true,
@@ -91,7 +94,7 @@ func NewMockInvoker(url common.URL) *MockInvoker {
 	}
 }
 
-func (bi *MockInvoker) GetUrl() common.URL {
+func (bi *MockInvoker) GetUrl() *common.URL {
 	return bi.url
 }
 
@@ -118,7 +121,7 @@ func (bi *MockInvoker) Destroy() {
 func TestTagRouterPriority(t *testing.T) {
 	u1, err := common.NewURL(tagRouterTestUserConsumerTag)
 	assert.Nil(t, err)
-	tagRouter, e := NewTagRouter(&u1)
+	tagRouter, e := NewTagRouter(u1)
 	assert.Nil(t, e)
 	p := tagRouter.Priority()
 	assert.Equal(t, int64(0), p)
@@ -127,7 +130,7 @@ func TestTagRouterPriority(t *testing.T) {
 func TestTagRouterRouteForce(t *testing.T) {
 	u1, e1 := common.NewURL(tagRouterTestUserConsumerTag)
 	assert.Nil(t, e1)
-	tagRouter, e := NewTagRouter(&u1)
+	tagRouter, e := NewTagRouter(u1)
 	assert.Nil(t, e)
 
 	u2, e2 := common.NewURL(tagRouterTestHangZhouUrl)
@@ -143,23 +146,23 @@ func TestTagRouterRouteForce(t *testing.T) {
 	invokers = append(invokers, inv2, inv3, inv4)
 	inv := &invocation.RPCInvocation{}
 	inv.SetAttachments(tagRouterTestDubboTag, tagRouterTestHangZhou)
-	invRst1 := tagRouter.Route(invokers, &u1, inv)
-	assert.Equal(t, 1, len(invRst1))
-	assert.Equal(t, tagRouterTestHangZhou, invRst1[0].GetUrl().GetParam(tagRouterTestDubboTag, ""))
+	invRst1 := tagRouter.Route(utils.ToBitmap(invokers), setUpAddrCache(tagRouter, invokers), u1, inv)
+	assert.Equal(t, 1, len(invRst1.ToArray()))
+	assert.Equal(t, tagRouterTestHangZhou, invokers[invRst1.ToArray()[0]].GetUrl().GetParam(tagRouterTestDubboTag, ""))
 
 	inv.SetAttachments(tagRouterTestDubboTag, tagRouterTestGuangZhou)
-	invRst2 := tagRouter.Route(invokers, &u1, inv)
-	assert.Equal(t, 0, len(invRst2))
+	invRst2 := tagRouter.Route(utils.ToBitmap(invokers), setUpAddrCache(tagRouter, invokers), u1, inv)
+	assert.Equal(t, 0, len(invRst2.ToArray()))
 	inv.SetAttachments(tagRouterTestDubboForceTag, tagRouterTestFalse)
 	inv.SetAttachments(tagRouterTestDubboTag, tagRouterTestGuangZhou)
-	invRst3 := tagRouter.Route(invokers, &u1, inv)
-	assert.Equal(t, 3, len(invRst3))
+	invRst3 := tagRouter.Route(utils.ToBitmap(invokers), setUpAddrCache(tagRouter, invokers), u1, inv)
+	assert.Equal(t, 3, len(invRst3.ToArray()))
 }
 
 func TestTagRouterRouteNoForce(t *testing.T) {
 	u1, e1 := common.NewURL(tagRouterTestUserConsumer)
 	assert.Nil(t, e1)
-	tagRouter, e := NewTagRouter(&u1)
+	tagRouter, e := NewTagRouter(u1)
 	assert.Nil(t, e)
 
 	u2, e2 := common.NewURL(tagRouterTestHangZhouUrl)
@@ -175,20 +178,37 @@ func TestTagRouterRouteNoForce(t *testing.T) {
 	invokers = append(invokers, inv2, inv3, inv4)
 	inv := &invocation.RPCInvocation{}
 	inv.SetAttachments(tagRouterTestDubboTag, tagRouterTestHangZhou)
-	invRst := tagRouter.Route(invokers, &u1, inv)
-	assert.Equal(t, 1, len(invRst))
-	assert.Equal(t, tagRouterTestHangZhou, invRst[0].GetUrl().GetParam(tagRouterTestDubboTag, ""))
+	invRst := tagRouter.Route(utils.ToBitmap(invokers), setUpAddrCache(tagRouter, invokers), u1, inv)
+	assert.Equal(t, 1, len(invRst.ToArray()))
+	assert.Equal(t, tagRouterTestHangZhou, invokers[invRst.ToArray()[0]].GetUrl().GetParam(tagRouterTestDubboTag, ""))
 
 	inv.SetAttachments(tagRouterTestDubboTag, tagRouterTestGuangZhou)
 	inv.SetAttachments(tagRouterTestDubboForceTag, tagRouterTestTrue)
-	invRst1 := tagRouter.Route(invokers, &u1, inv)
-	assert.Equal(t, 0, len(invRst1))
+	invRst1 := tagRouter.Route(utils.ToBitmap(invokers), setUpAddrCache(tagRouter, invokers), u1, inv)
+	assert.Equal(t, 0, len(invRst1.ToArray()))
 	inv.SetAttachments(tagRouterTestDubboForceTag, tagRouterTestFalse)
-	invRst2 := tagRouter.Route(invokers, &u1, inv)
-	assert.Equal(t, 3, len(invRst2))
+	invRst2 := tagRouter.Route(utils.ToBitmap(invokers), setUpAddrCache(tagRouter, invokers), u1, inv)
+	assert.Equal(t, 3, len(invRst2.ToArray()))
 }
 
-func TestFilterInvoker(t *testing.T) {
+func setUpAddrCache(r router.Poolable, addrs []protocol.Invoker) router.Cache {
+	pool, info := r.Pool(addrs)
+	cache := chain.BuildCache(addrs)
+	cache.SetAddrPool(r.Name(), pool)
+	cache.SetAddrMeta(r.Name(), info)
+	return cache
+}
+
+func setUpAddrCacheWithRuleDisabled(r router.Poolable, addrs []protocol.Invoker) router.Cache {
+	pool, info := r.Pool(addrs)
+	info.(*addrMetadata).ruleEnabled = false
+	cache := chain.BuildCache(addrs)
+	cache.SetAddrPool(r.Name(), pool)
+	cache.SetAddrMeta(r.Name(), info)
+	return cache
+}
+
+func TestRouteBeijingInvoker(t *testing.T) {
 	u2, e2 := common.NewURL(tagRouterTestHangZhouUrl)
 	u3, e3 := common.NewURL(tagRouterTestShangHaiUrl)
 	u4, e4 := common.NewURL(tagRouterTestBeijingUrl)
@@ -203,23 +223,17 @@ func TestFilterInvoker(t *testing.T) {
 	inv5 := NewMockInvoker(u5)
 	var invokers []protocol.Invoker
 	invokers = append(invokers, inv2, inv3, inv4, inv5)
-	filterTag := func(invoker protocol.Invoker) bool {
-		if invoker.GetUrl().GetParam(constant.Tagkey, "") == "beijing" {
-			return true
-		}
-		return false
-	}
-	res := filterInvoker(invokers, filterTag)
-	assert.Equal(t, []protocol.Invoker{inv4, inv5}, res)
-	flag := true
-	filterEnabled := func(invoker protocol.Invoker) bool {
-		if invoker.GetUrl().GetParamBool(constant.RouterEnabled, false) == flag {
-			return true
-		}
-		return false
-	}
-	res2 := filterInvoker(invokers, filterTag, filterEnabled)
-	assert.Equal(t, []protocol.Invoker{inv4}, res2)
+
+	url, _ := common.NewURL(tagRouterTestBeijingUrl)
+	tagRouter, _ := NewTagRouter(url)
+
+	rb := roaring.NewBitmap()
+	rb.AddRange(0, uint64(len(invokers)))
+	cache := setUpAddrCache(tagRouter, invokers)
+	inv := &invocation.RPCInvocation{}
+	res := tagRouter.Route(rb, cache, url, inv)
+	// inv4 and inv5
+	assert.Equal(t, []uint32{2, 3}, res.ToArray())
 }
 
 type DynamicTagRouter struct {
@@ -278,7 +292,7 @@ tags:
 	suite.NoError(err)
 
 	zkUrl, _ := common.NewURL(fmt.Sprintf(zkFormat, routerLocalIP, suite.testCluster.Servers[0].Port))
-	configuration, err := extension.GetConfigCenterFactory(routerZk).GetDynamicConfiguration(&zkUrl)
+	configuration, err := extension.GetConfigCenterFactory(routerZk).GetDynamicConfiguration(zkUrl)
 	config.GetEnvInstance().SetDynamicConfiguration(configuration)
 
 	suite.Nil(err)
@@ -287,11 +301,11 @@ tags:
 	url, e1 := common.NewURL(tagRouterTestUserConsumerTag)
 	suite.Nil(e1)
 
-	tagRouter, err := NewTagRouter(&url)
+	tagRouter, err := NewTagRouter(url)
 	suite.Nil(err)
 	suite.NotNil(tagRouter)
 	suite.route = tagRouter
-	suite.url = &url
+	suite.url = url
 }
 
 func (suite *DynamicTagRouter) TearDownTest() {
@@ -301,50 +315,57 @@ func (suite *DynamicTagRouter) TearDownTest() {
 
 func (suite *DynamicTagRouter) TestDynamicTagRouterSetByIPv4() {
 	invokers := suite.invokers
-	suite.route.Notify(invokers)
+	rb := roaring.NewBitmap()
+	rb.AddRange(0, uint64(len(invokers)))
+	cache := setUpAddrCache(suite.route, invokers)
 	suite.NotNil(suite.route.tagRouterRule)
 
 	consumer := &invocation.RPCInvocation{}
 	consumer.SetAttachments(tagRouterTestDubboTag, "tag1")
-	targetInvokers := suite.route.Route(invokers, suite.url, consumer)
-	suite.Equal(1, len(targetInvokers))
-	suite.Equal(targetInvokers[0], suite.invokers[0])
+	targetInvokers := suite.route.Route(rb, cache, suite.url, consumer)
+	suite.Equal(uint64(1), targetInvokers.GetCardinality())
+	suite.Equal(targetInvokers.ToArray()[0], uint32(0))
 
 	consumer.SetAttachments(tagRouterTestDubboTag, "tag3")
-	targetInvokers = suite.route.Route(invokers, suite.url, consumer)
-	suite.Equal(2, len(targetInvokers))
-	suite.Equal(targetInvokers, []protocol.Invoker{suite.invokers[2], suite.invokers[3]})
+	targetInvokers = suite.route.Route(rb, cache, suite.url, consumer)
+	suite.Equal(uint64(2), targetInvokers.GetCardinality())
+	suite.True(targetInvokers.Contains(2) && targetInvokers.Contains(3))
 }
 
 func (suite *DynamicTagRouter) TestDynamicTagRouterStaticTag() {
 	invokers := suite.invokers
+	rb := roaring.NewBitmap()
+	rb.AddRange(0, uint64(len(invokers)))
+	cache := setUpAddrCacheWithRuleDisabled(suite.route, invokers)
 	consumer := &invocation.RPCInvocation{}
 	consumer.SetAttachments(tagRouterTestDubboTag, "tag4")
-	targetInvokers := suite.route.Route(invokers, suite.url, consumer)
-	suite.Equal(1, len(targetInvokers))
-	suite.Equal(targetInvokers[0], suite.invokers[3])
+	targetInvokers := suite.route.Route(rb, cache, suite.url, consumer)
+	suite.Equal(uint64(1), targetInvokers.GetCardinality())
+	suite.Equal(targetInvokers.ToArray()[0], uint32(3))
 }
 
 // Teas no tag and return a address are not in dynamic tag group
 func (suite *DynamicTagRouter) TestDynamicTagRouterByNoTagAndAddressMatch() {
 	invokers := suite.invokers
-	suite.route.Notify(invokers)
+	rb := roaring.NewBitmap()
+	rb.AddRange(0, uint64(len(invokers)))
+	cache := setUpAddrCache(suite.route, invokers)
 	suite.NotNil(suite.route.tagRouterRule)
 	consumer := &invocation.RPCInvocation{}
-	targetInvokers := suite.route.Route(invokers, suite.url, consumer)
-	suite.Equal(1, len(targetInvokers))
-	suite.Equal(targetInvokers[0], suite.invokers[4])
+	targetInvokers := suite.route.Route(rb, cache, suite.url, consumer)
+	suite.Equal(uint64(1), targetInvokers.GetCardinality())
+	suite.Equal(targetInvokers.ToArray()[0], uint32(4))
 	// test if there are some addresses that are not in any dynamic tag group, continue to filter using the static tag group.
 	consumer.SetAttachments(tagRouterTestDubboTag, "tag5")
-	targetInvokers = suite.route.Route(invokers, suite.url, consumer)
-	suite.Equal(1, len(targetInvokers))
-	suite.Equal(targetInvokers[0], suite.invokers[4])
+	targetInvokers = suite.route.Route(rb, cache, suite.url, consumer)
+	suite.Equal(uint64(1), targetInvokers.GetCardinality())
+	suite.Equal(targetInvokers.ToArray()[0], uint32(4))
 }
 
 func TestProcess(t *testing.T) {
 	u1, err := common.NewURL(tagRouterTestUserConsumerTag)
 	assert.Nil(t, err)
-	tagRouter, e := NewTagRouter(&u1)
+	tagRouter, e := NewTagRouter(u1)
 	assert.Nil(t, e)
 	assert.NotNil(t, tagRouter)
 
diff --git a/cluster/router/utils/bitmap_util.go b/cluster/router/utils/bitmap_util.go
new file mode 100644
index 0000000000000000000000000000000000000000..8b4ee5538f339276fb4c41ceefe4df3575723f15
--- /dev/null
+++ b/cluster/router/utils/bitmap_util.go
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package utils
+
+import (
+	"github.com/RoaringBitmap/roaring"
+)
+
+import (
+	"github.com/apache/dubbo-go/protocol"
+)
+
+var EmptyAddr = roaring.NewBitmap()
+
+func JoinIfNotEqual(left *roaring.Bitmap, right *roaring.Bitmap) *roaring.Bitmap {
+	if !left.Equals(right) {
+		left = left.Clone()
+		left.And(right)
+	}
+	return left
+}
+
+func FallbackIfJoinToEmpty(left *roaring.Bitmap, right *roaring.Bitmap) *roaring.Bitmap {
+	ret := JoinIfNotEqual(left, right)
+	if ret == nil || ret.IsEmpty() {
+		return right
+	}
+	return ret
+}
+
+func ToBitmap(invokers []protocol.Invoker) *roaring.Bitmap {
+	bitmap := roaring.NewBitmap()
+	bitmap.AddRange(0, uint64(len(invokers)))
+	return bitmap
+}
diff --git a/common/constant/default.go b/common/constant/default.go
index 629aa32392a0151046eaaea67287618eae02158d..4165942a615e220f6384a898b07c04bafd39c3b0 100644
--- a/common/constant/default.go
+++ b/common/constant/default.go
@@ -45,6 +45,7 @@ const (
 	DEFAULT_REST_CLIENT        = "resty"
 	DEFAULT_REST_SERVER        = "go-restful"
 	DEFAULT_PORT               = 20000
+	DEFAULT_SERIALIZATION      = HESSIAN2_SERIALIZATION
 )
 
 const (
diff --git a/common/constant/key.go b/common/constant/key.go
index 7c45a1397d8767510f1f8b92f4e82f0ece05c810..c256659acb91ffb2f74473e4f1bc7aafa4b98c38 100644
--- a/common/constant/key.go
+++ b/common/constant/key.go
@@ -22,31 +22,33 @@ const (
 )
 
 const (
-	PORT_KEY               = "port"
-	GROUP_KEY              = "group"
-	VERSION_KEY            = "version"
-	INTERFACE_KEY          = "interface"
-	PATH_KEY               = "path"
-	PROTOCOL_KEY           = "protocol"
-	SERVICE_KEY            = "service"
-	METHODS_KEY            = "methods"
-	TIMEOUT_KEY            = "timeout"
-	CATEGORY_KEY           = "category"
-	CHECK_KEY              = "check"
-	ENABLED_KEY            = "enabled"
-	SIDE_KEY               = "side"
-	OVERRIDE_PROVIDERS_KEY = "providerAddresses"
-	BEAN_NAME_KEY          = "bean.name"
-	GENERIC_KEY            = "generic"
-	CLASSIFIER_KEY         = "classifier"
-	TOKEN_KEY              = "token"
-	LOCAL_ADDR             = "local-addr"
-	REMOTE_ADDR            = "remote-addr"
-	PATH_SEPARATOR         = "/"
-	DUBBO_KEY              = "dubbo"
-	RELEASE_KEY            = "release"
-	ANYHOST_KEY            = "anyhost"
-	SSL_ENABLED_KEY        = "ssl-enabled"
+	GROUP_KEY                = "group"
+	VERSION_KEY              = "version"
+	INTERFACE_KEY            = "interface"
+	MESSAGE_SIZE_KEY         = "message_size"
+	PATH_KEY                 = "path"
+	SERVICE_KEY              = "service"
+	METHODS_KEY              = "methods"
+	TIMEOUT_KEY              = "timeout"
+	CATEGORY_KEY             = "category"
+	CHECK_KEY                = "check"
+	ENABLED_KEY              = "enabled"
+	SIDE_KEY                 = "side"
+	OVERRIDE_PROVIDERS_KEY   = "providerAddresses"
+	BEAN_NAME_KEY            = "bean.name"
+	GENERIC_KEY              = "generic"
+	CLASSIFIER_KEY           = "classifier"
+	TOKEN_KEY                = "token"
+	LOCAL_ADDR               = "local-addr"
+	REMOTE_ADDR              = "remote-addr"
+	DEFAULT_REMOTING_TIMEOUT = 3000
+	RELEASE_KEY              = "release"
+	ANYHOST_KEY              = "anyhost"
+	PORT_KEY                 = "port"
+	PROTOCOL_KEY             = "protocol"
+	PATH_SEPARATOR           = "/"
+	DUBBO_KEY                = "dubbo"
+	SSL_ENABLED_KEY          = "ssl-enabled"
 )
 
 const (
@@ -81,6 +83,7 @@ const (
 	EXECUTE_REJECTED_EXECUTION_HANDLER_KEY = "execute.limit.rejected.handler"
 	PROVIDER_SHUTDOWN_FILTER               = "pshutdown"
 	CONSUMER_SHUTDOWN_FILTER               = "cshutdown"
+	SERIALIZATION_KEY                      = "serialization"
 	PID_KEY                                = "pid"
 	SYNC_REPORT_KEY                        = "sync.report"
 	RETRY_PERIOD_KEY                       = "retry.period"
@@ -124,6 +127,7 @@ const (
 	TAG_ROUTE_PROTOCOL       = "tag"
 	PROVIDERS_CATEGORY       = "providers"
 	ROUTER_KEY               = "router"
+	EXPORT_KEY               = "export"
 )
 
 const (
@@ -169,6 +173,10 @@ const (
 	NACOS_USERNAME               = "username"
 )
 
+const (
+	FILE_KEY = "file"
+)
+
 const (
 	ZOOKEEPER_KEY = "zookeeper"
 )
@@ -177,6 +185,18 @@ const (
 	ETCDV3_KEY = "etcdv3"
 )
 
+const (
+	CONSUL_KEY          = "consul"
+	CHECK_PASS_INTERVAL = "consul-check-pass-interval"
+	// default time-to-live in millisecond
+	DEFAULT_CHECK_PASS_INTERVAL = 16000
+	QUERY_TAG                   = "consul_query_tag"
+	ACL_TOKEN                   = "acl-token"
+	// default deregister critical server after
+	DEFAULT_DEREGISTER_TIME = "20s"
+	DEREGISTER_AFTER        = "consul-deregister-critical-service-after"
+)
+
 const (
 	TRACING_REMOTE_SPAN_CTX = "tracing.remote.span.ctx"
 )
diff --git a/cluster/router/chan.go b/common/constant/serializtion.go
similarity index 83%
rename from cluster/router/chan.go
rename to common/constant/serializtion.go
index 6904e1734a7cbdaa00afa1b30797d19ca502453c..f27598ccf5cf04a72d14d4ef97ae9298076efe1a 100644
--- a/cluster/router/chan.go
+++ b/common/constant/serializtion.go
@@ -15,11 +15,14 @@
  * limitations under the License.
  */
 
-package router
+package constant
 
-// Chain
-type Chain interface {
-	router
-	// AddRouters Add routers
-	AddRouters([]PriorityRouter)
-}
+const (
+	S_Hessian2 byte = 2
+	S_Proto    byte = 21
+)
+
+const (
+	HESSIAN2_SERIALIZATION = "hessian2"
+	PROTOBUF_SERIALIZATION = "protobuf"
+)
diff --git a/common/extension/config_post_processor.go b/common/extension/config_post_processor.go
new file mode 100644
index 0000000000000000000000000000000000000000..db126b744d54562a7f45b59aec26ef6e950a23a8
--- /dev/null
+++ b/common/extension/config_post_processor.go
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package extension
+
+import (
+	"github.com/apache/dubbo-go/config/interfaces"
+)
+
+var (
+	processors = make(map[string]interfaces.ConfigPostProcessor)
+)
+
+// SetConfigPostProcessor registers a ConfigPostProcessor with the given name.
+func SetConfigPostProcessor(name string, processor interfaces.ConfigPostProcessor) {
+	processors[name] = processor
+}
+
+// GetConfigPostProcessor finds a ConfigPostProcessor by name.
+func GetConfigPostProcessor(name string) interfaces.ConfigPostProcessor {
+	return processors[name]
+}
+
+// GetConfigPostProcessors returns all registered instances of ConfigPostProcessor.
+func GetConfigPostProcessors() []interfaces.ConfigPostProcessor {
+	ret := make([]interfaces.ConfigPostProcessor, 0, len(processors))
+	for _, v := range processors {
+		ret = append(ret, v)
+	}
+	return ret
+}
diff --git a/common/extension/health_checker.go b/common/extension/health_checker.go
index 8def727614dad8393eeef9ced5e30a056fa65461..cec4c2defc291c617a0549c3296e07851b2ec128 100644
--- a/common/extension/health_checker.go
+++ b/common/extension/health_checker.go
@@ -26,8 +26,8 @@ var (
 	healthCheckers = make(map[string]func(url *common.URL) router.HealthChecker)
 )
 
-// SethealthChecker sets the HealthChecker with @name
-func SethealthChecker(name string, fcn func(_ *common.URL) router.HealthChecker) {
+// SetHealthChecker sets the HealthChecker with @name
+func SetHealthChecker(name string, fcn func(_ *common.URL) router.HealthChecker) {
 	healthCheckers[name] = fcn
 }
 
diff --git a/common/extension/health_checker_test.go b/common/extension/health_checker_test.go
index 4e83a6f6e1ed8a57b6e6374377d08eabfb56c604..af6b114a612a465d4397be7a599ddfc9ff7edab9 100644
--- a/common/extension/health_checker_test.go
+++ b/common/extension/health_checker_test.go
@@ -32,7 +32,7 @@ import (
 )
 
 func TestGetHealthChecker(t *testing.T) {
-	SethealthChecker("mock", newMockhealthCheck)
+	SetHealthChecker("mock", newMockHealthCheck)
 	checker := GetHealthChecker("mock", common.NewURLWithOptions())
 	assert.NotNil(t, checker)
 }
@@ -44,6 +44,6 @@ func (m mockHealthChecker) IsHealthy(invoker protocol.Invoker) bool {
 	return true
 }
 
-func newMockhealthCheck(_ *common.URL) router.HealthChecker {
+func newMockHealthCheck(_ *common.URL) router.HealthChecker {
 	return &mockHealthChecker{}
 }
diff --git a/common/host_util.go b/common/host_util.go
new file mode 100644
index 0000000000000000000000000000000000000000..9861c92bee15624dd79e02a6e3aba6b443dbd7da
--- /dev/null
+++ b/common/host_util.go
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package common
+
+import gxnet "github.com/dubbogo/gost/net"
+
+var localIp string
+
+func GetLocalIp() string {
+	if len(localIp) != 0 {
+		return localIp
+	}
+	localIp, _ = gxnet.GetLocalIP()
+	return localIp
+}
diff --git a/common/host_util_test.go b/common/host_util_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..ad96dde4c77667a940c27455262a6aeca921a73d
--- /dev/null
+++ b/common/host_util_test.go
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package common
+
+import (
+	"testing"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+func TestGetLocalIp(t *testing.T) {
+	assert.NotNil(t, GetLocalIp())
+}
diff --git a/common/node.go b/common/node.go
index 4febd78536126c67bdc65fc09d4be47fb869ef5e..b9c1f39bf960b466276edf1e46bb7c1ecddd6f19 100644
--- a/common/node.go
+++ b/common/node.go
@@ -19,7 +19,7 @@ package common
 
 // Node use for process dubbo node
 type Node interface {
-	GetUrl() URL
+	GetUrl() *URL
 	IsAvailable() bool
 	Destroy()
 }
diff --git a/common/proxy/proxy.go b/common/proxy/proxy.go
index ce0f4d1d3f4dc8b93467aaeede40ea03b53c6e66..3a01941700ed277d9481c6c57a739c69f83b06b7 100644
--- a/common/proxy/proxy.go
+++ b/common/proxy/proxy.go
@@ -66,7 +66,6 @@ func NewProxy(invoke protocol.Invoker, callBack interface{}, attachments map[str
 //  		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.
 	valueOf := reflect.ValueOf(v)
 	logger.Debugf("[Implement] reflect.TypeOf: %s", valueOf.String())
@@ -145,12 +144,17 @@ func (p *Proxy) Implement(v common.RPCService) {
 				inv.SetAttachments(k, value)
 			}
 
-			// add user setAttachment
+			// add user setAttachment. It is compatibility with previous versions.
 			atm := invCtx.Value(constant.AttachmentKey)
 			if m, ok := atm.(map[string]string); ok {
 				for k, value := range m {
 					inv.SetAttachments(k, value)
 				}
+			} else if m2, ok2 := atm.(map[string]interface{}); ok2 {
+				// it is support to transfer map[string]interface{}. It refers to dubbo-java 2.7.
+				for k, value := range m2 {
+					inv.SetAttachments(k, value)
+				}
 			}
 
 			result := p.invoke.Invoke(invCtx, inv)
@@ -230,3 +234,8 @@ func (p *Proxy) Get() common.RPCService {
 func (p *Proxy) GetCallback() interface{} {
 	return p.callBack
 }
+
+// GetInvoker gets Invoker.
+func (p *Proxy) GetInvoker() protocol.Invoker {
+	return p.invoke
+}
diff --git a/common/proxy/proxy_factory.go b/common/proxy/proxy_factory.go
index 117428cb253e1ad4a4ceee59aa620d7097b41a75..2e66c346b2c8ab6af0b1485f0ea867eb1af0bfc5 100644
--- a/common/proxy/proxy_factory.go
+++ b/common/proxy/proxy_factory.go
@@ -26,7 +26,7 @@ import (
 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
+	GetInvoker(url *common.URL) protocol.Invoker
 }
 
 // Option will define a function of handling ProxyFactory
diff --git a/common/proxy/proxy_factory/default.go b/common/proxy/proxy_factory/default.go
index 1b8ca222011292040c57c3e86df0438943a5b464..ff3d7955a027411b0697797d32a44023f8f70caf 100644
--- a/common/proxy/proxy_factory/default.go
+++ b/common/proxy/proxy_factory/default.go
@@ -72,7 +72,7 @@ func (factory *DefaultProxyFactory) GetAsyncProxy(invoker protocol.Invoker, call
 }
 
 // GetInvoker gets a invoker
-func (factory *DefaultProxyFactory) GetInvoker(url common.URL) protocol.Invoker {
+func (factory *DefaultProxyFactory) GetInvoker(url *common.URL) protocol.Invoker {
 	return &ProxyInvoker{
 		BaseInvoker: *protocol.NewBaseInvoker(url),
 	}
@@ -88,7 +88,8 @@ func (pi *ProxyInvoker) Invoke(ctx context.Context, invocation protocol.Invocati
 	result := &protocol.RPCResult{}
 	result.SetAttachments(invocation.Attachments())
 
-	url := pi.GetUrl()
+	//get providerUrl. The origin url may be is registry URL.
+	url := getProviderURL(pi.GetUrl())
 
 	methodName := invocation.MethodName()
 	proto := url.Protocol
@@ -96,7 +97,7 @@ func (pi *ProxyInvoker) Invoke(ctx context.Context, invocation protocol.Invocati
 	args := invocation.Arguments()
 
 	// get service
-	svc := common.ServiceMap.GetService(proto, path)
+	svc := common.ServiceMap.GetServiceByServiceKey(proto, url.ServiceKey())
 	if svc == nil {
 		logger.Errorf("cannot find service [%s] in %s", path, proto)
 		result.SetError(perrors.Errorf("cannot find service [%s] in %s", path, proto))
@@ -159,3 +160,10 @@ func (pi *ProxyInvoker) Invoke(ctx context.Context, invocation protocol.Invocati
 	}
 	return result
 }
+
+func getProviderURL(url *common.URL) *common.URL {
+	if url.SubURL == nil {
+		return url
+	}
+	return url.SubURL
+}
diff --git a/common/proxy/proxy_factory/default_test.go b/common/proxy/proxy_factory/default_test.go
index 99d5c020f4bfd0254ddddc65e78bcae2e252b6be..4002ab95849b79aef26c8d94822e731cd6f229e3 100644
--- a/common/proxy/proxy_factory/default_test.go
+++ b/common/proxy/proxy_factory/default_test.go
@@ -34,7 +34,7 @@ import (
 func TestGetProxy(t *testing.T) {
 	proxyFactory := NewDefaultProxyFactory()
 	url := common.NewURLWithOptions()
-	proxy := proxyFactory.GetProxy(protocol.NewBaseInvoker(*url), url)
+	proxy := proxyFactory.GetProxy(protocol.NewBaseInvoker(url), url)
 	assert.NotNil(t, proxy)
 }
 
@@ -49,13 +49,13 @@ func TestGetAsyncProxy(t *testing.T) {
 	proxyFactory := NewDefaultProxyFactory()
 	url := common.NewURLWithOptions()
 	async := &TestAsync{}
-	proxy := proxyFactory.GetAsyncProxy(protocol.NewBaseInvoker(*url), async.CallBack, url)
+	proxy := proxyFactory.GetAsyncProxy(protocol.NewBaseInvoker(url), async.CallBack, url)
 	assert.NotNil(t, proxy)
 }
 
 func TestGetInvoker(t *testing.T) {
 	proxyFactory := NewDefaultProxyFactory()
 	url := common.NewURLWithOptions()
-	invoker := proxyFactory.GetInvoker(*url)
+	invoker := proxyFactory.GetInvoker(url)
 	assert.True(t, invoker.IsAvailable())
 }
diff --git a/common/proxy/proxy_test.go b/common/proxy/proxy_test.go
index 14b2befbc47242d9cc9a2f88e9070b84828062c0..c6f659666180310887f2101966e59669df707e54 100644
--- a/common/proxy/proxy_test.go
+++ b/common/proxy/proxy_test.go
@@ -32,6 +32,8 @@ import (
 	"github.com/apache/dubbo-go/common"
 	"github.com/apache/dubbo-go/common/constant"
 	"github.com/apache/dubbo-go/protocol"
+	"github.com/apache/dubbo-go/protocol/dubbo/hessian2"
+	"github.com/apache/dubbo-go/protocol/invocation"
 )
 
 type TestService struct {
@@ -40,6 +42,7 @@ type TestService struct {
 	MethodThree func(int, bool) (interface{}, error)
 	MethodFour  func(int, bool) (*interface{}, error) `dubbo:"methodFour"`
 	MethodFive  func() error
+	MethodSix   func(context.Context, string) (interface{}, error)
 	Echo        func(interface{}, *interface{}) error
 }
 
@@ -55,7 +58,7 @@ func (s *TestServiceInt) Reference() string {
 
 func TestProxyImplement(t *testing.T) {
 
-	invoker := protocol.NewBaseInvoker(common.URL{})
+	invoker := protocol.NewBaseInvoker(&common.URL{})
 	p := NewProxy(invoker, nil, map[string]string{constant.ASYNC_KEY: "false"})
 	s := &TestService{}
 	p.Implement(s)
@@ -120,3 +123,34 @@ func TestProxyImplement(t *testing.T) {
 	assert.Nil(t, s3.MethodOne)
 
 }
+
+func TestProxyImplementForContext(t *testing.T) {
+	invoker := &TestProxyInvoker{
+		BaseInvoker: *protocol.NewBaseInvoker(&common.URL{}),
+	}
+	p := NewProxy(invoker, nil, map[string]string{constant.ASYNC_KEY: "false"})
+	s := &TestService{}
+	p.Implement(s)
+	attahments1 := make(map[string]interface{}, 4)
+	attahments1["k1"] = "v1"
+	attahments1["k2"] = "v2"
+	context := context.WithValue(context.Background(), constant.AttachmentKey, attahments1)
+	r, err := p.Get().(*TestService).MethodSix(context, "xxx")
+	v1 := r.(map[string]interface{})
+	assert.NoError(t, err)
+	assert.Equal(t, v1["TestProxyInvoker"], "TestProxyInvokerValue")
+}
+
+type TestProxyInvoker struct {
+	protocol.BaseInvoker
+}
+
+func (bi *TestProxyInvoker) Invoke(context context.Context, inv protocol.Invocation) protocol.Result {
+	rpcInv := inv.(*invocation.RPCInvocation)
+	mapV := inv.Attachments()
+	mapV["TestProxyInvoker"] = "TestProxyInvokerValue"
+	hessian2.ReflectResponse(mapV, rpcInv.Reply())
+	return &protocol.RPCResult{
+		Rest: inv.Arguments(),
+	}
+}
diff --git a/common/rpc_service.go b/common/rpc_service.go
index 9ef2b956aa955f4fc79c6f75bd060ccfee2d02ca..572fc71701c411a7a0a070c2a84e9a4d96da6dfb 100644
--- a/common/rpc_service.go
+++ b/common/rpc_service.go
@@ -157,11 +157,17 @@ type serviceMap struct {
 }
 
 // GetService gets a service defination by protocol and name
-func (sm *serviceMap) GetService(protocol, name string) *Service {
+func (sm *serviceMap) GetService(protocol, interfaceName, group, version string) *Service {
+	serviceKey := ServiceKey(interfaceName, group, version)
+	return sm.GetServiceByServiceKey(protocol, serviceKey)
+}
+
+// GetService gets a service defination by protocol and service key
+func (sm *serviceMap) GetServiceByServiceKey(protocol, serviceKey string) *Service {
 	sm.mutex.RLock()
 	defer sm.mutex.RUnlock()
 	if s, ok := sm.serviceMap[protocol]; ok {
-		if srv, ok := s[name]; ok {
+		if srv, ok := s[serviceKey]; ok {
 			return srv
 		}
 		return nil
@@ -169,7 +175,7 @@ func (sm *serviceMap) GetService(protocol, name string) *Service {
 	return nil
 }
 
-// GetInterface gets an interface defination by interface name
+// GetInterface gets an interface definition by interface name
 func (sm *serviceMap) GetInterface(interfaceName string) []*Service {
 	sm.mutex.RLock()
 	defer sm.mutex.RUnlock()
@@ -180,7 +186,7 @@ func (sm *serviceMap) GetInterface(interfaceName string) []*Service {
 }
 
 // Register registers a service by @interfaceName and @protocol
-func (sm *serviceMap) Register(interfaceName, protocol string, rcvr RPCService) (string, error) {
+func (sm *serviceMap) Register(interfaceName, protocol, group, version string, rcvr RPCService) (string, error) {
 	if sm.serviceMap[protocol] == nil {
 		sm.serviceMap[protocol] = make(map[string]*Service)
 	}
@@ -203,8 +209,8 @@ func (sm *serviceMap) Register(interfaceName, protocol string, rcvr RPCService)
 		return "", perrors.New(s)
 	}
 
-	sname = rcvr.Reference()
-	if server := sm.GetService(protocol, sname); server != nil {
+	sname = ServiceKey(interfaceName, group, version)
+	if server := sm.GetService(protocol, interfaceName, group, version); server != nil {
 		return "", perrors.New("service already defined: " + sname)
 	}
 	s.name = sname
@@ -228,9 +234,9 @@ func (sm *serviceMap) Register(interfaceName, protocol string, rcvr RPCService)
 }
 
 // UnRegister cancels a service by @interfaceName, @protocol and @serviceId
-func (sm *serviceMap) UnRegister(interfaceName, protocol, serviceId string) error {
-	if protocol == "" || serviceId == "" {
-		return perrors.New("protocol or serviceName is nil")
+func (sm *serviceMap) UnRegister(interfaceName, protocol, serviceKey string) error {
+	if protocol == "" || serviceKey == "" {
+		return perrors.New("protocol or serviceKey is nil")
 	}
 
 	var (
@@ -248,9 +254,9 @@ func (sm *serviceMap) UnRegister(interfaceName, protocol, serviceId string) erro
 		if !ok {
 			return perrors.New("no services for " + protocol)
 		}
-		s, ok := svcs[serviceId]
+		s, ok := svcs[serviceKey]
 		if !ok {
-			return perrors.New("no service for " + serviceId)
+			return perrors.New("no service for " + serviceKey)
 		}
 		svrs, ok = sm.interfaceMap[interfaceName]
 		if !ok {
@@ -271,12 +277,12 @@ func (sm *serviceMap) UnRegister(interfaceName, protocol, serviceId string) erro
 	sm.mutex.Lock()
 	defer sm.mutex.Unlock()
 	sm.interfaceMap[interfaceName] = make([]*Service, 0, len(svrs))
-	for i, _ := range svrs {
+	for i := range svrs {
 		if i != index {
 			sm.interfaceMap[interfaceName] = append(sm.interfaceMap[interfaceName], svrs[i])
 		}
 	}
-	delete(svcs, serviceId)
+	delete(svcs, serviceKey)
 	if len(sm.serviceMap[protocol]) == 0 {
 		delete(sm.serviceMap, protocol)
 	}
diff --git a/common/rpc_service_test.go b/common/rpc_service_test.go
index 7c4eb421d7ed0e5e0306eb1a1a2ff5ac31c29b6d..b50d8d962c4277f702388b74e5137bdf1c8bdf7e 100644
--- a/common/rpc_service_test.go
+++ b/common/rpc_service_test.go
@@ -85,23 +85,23 @@ func TestServiceMapRegister(t *testing.T) {
 	// lowercase
 	s0 := &testService{}
 	// methods, err := ServiceMap.Register("testporotocol", s0)
-	_, err := ServiceMap.Register(testInterfaceName, "testporotocol", s0)
+	_, err := ServiceMap.Register(testInterfaceName, "testporotocol", "", "v0", s0)
 	assert.EqualError(t, err, "type testService is not exported")
 
 	// succ
 	s := &TestService{}
-	methods, err := ServiceMap.Register(testInterfaceName, "testporotocol", s)
+	methods, err := ServiceMap.Register(testInterfaceName, "testporotocol", "", "v1", s)
 	assert.NoError(t, err)
 	assert.Equal(t, "MethodOne,MethodThree,methodTwo", methods)
 
 	// repeat
-	_, err = ServiceMap.Register(testInterfaceName, "testporotocol", s)
-	assert.EqualError(t, err, "service already defined: com.test.Path")
+	_, err = ServiceMap.Register(testInterfaceName, "testporotocol", "", "v1", s)
+	assert.EqualError(t, err, "service already defined: testService:v1")
 
 	// no method
 	s1 := &TestService1{}
-	_, err = ServiceMap.Register(testInterfaceName, "testporotocol", s1)
-	assert.EqualError(t, err, "type com.test.Path1 has no exported methods of suitable type")
+	_, err = ServiceMap.Register(testInterfaceName, "testporotocol", "", "v2", s1)
+	assert.EqualError(t, err, "type testService:v2 has no exported methods of suitable type")
 
 	ServiceMap = &serviceMap{
 		serviceMap:   make(map[string]map[string]*Service),
@@ -111,22 +111,22 @@ func TestServiceMapRegister(t *testing.T) {
 
 func TestServiceMapUnRegister(t *testing.T) {
 	s := &TestService{}
-	_, err := ServiceMap.Register("TestService", testProtocol, s)
+	_, err := ServiceMap.Register("TestService", testProtocol, "", "v1", s)
 	assert.NoError(t, err)
-	assert.NotNil(t, ServiceMap.GetService(testProtocol, referenceTestPath))
+	assert.NotNil(t, ServiceMap.GetService(testProtocol, "TestService", "", "v1"))
 	assert.Equal(t, 1, len(ServiceMap.GetInterface("TestService")))
 
-	err = ServiceMap.UnRegister("", "", referenceTestPath)
-	assert.EqualError(t, err, "protocol or serviceName is nil")
+	err = ServiceMap.UnRegister("", "", ServiceKey("TestService", "", "v1"))
+	assert.EqualError(t, err, "protocol or serviceKey is nil")
 
-	err = ServiceMap.UnRegister("", "protocol", referenceTestPath)
+	err = ServiceMap.UnRegister("", "protocol", ServiceKey("TestService", "", "v1"))
 	assert.EqualError(t, err, "no services for protocol")
 
-	err = ServiceMap.UnRegister("", testProtocol, referenceTestPathDistinct)
-	assert.EqualError(t, err, "no service for com.test.Path1")
+	err = ServiceMap.UnRegister("", testProtocol, ServiceKey("TestService", "", "v0"))
+	assert.EqualError(t, err, "no service for TestService:v0")
 
 	// succ
-	err = ServiceMap.UnRegister("TestService", testProtocol, referenceTestPath)
+	err = ServiceMap.UnRegister("TestService", testProtocol, ServiceKey("TestService", "", "v1"))
 	assert.NoError(t, err)
 }
 
diff --git a/common/url.go b/common/url.go
index ec6dce9175596e4f1774614f8f0cb978d181f300..1ea2bc4321afcf93f148fea07019a892966fd720 100644
--- a/common/url.go
+++ b/common/url.go
@@ -18,13 +18,16 @@
 package common
 
 import (
+	"bytes"
 	"encoding/base64"
 	"fmt"
+	cm "github.com/Workiva/go-datastructures/common"
 	"math"
 	"net"
 	"net/url"
 	"strconv"
 	"strings"
+	"sync"
 )
 
 import (
@@ -61,8 +64,14 @@ var (
 	DubboNodes = [...]string{"consumers", "configurators", "routers", "providers"}
 	// DubboRole Dubbo service role
 	DubboRole = [...]string{"consumer", "", "routers", "provider"}
+	// CompareURLEqualFunc compare two url is equal
+	compareURLEqualFunc CompareURLEqualFunc
 )
 
+func init() {
+	compareURLEqualFunc = defaultCompareURLEqual
+}
+
 // nolint
 type RoleType int
 
@@ -76,22 +85,37 @@ func (t RoleType) Role() string {
 }
 
 type baseUrl struct {
-	Protocol     string
-	Location     string // ip+port
-	Ip           string
-	Port         string
+	Protocol string
+	Location string // ip+port
+	Ip       string
+	Port     string
+	//url.Values is not safe map, add to avoid concurrent map read and map write error
+	paramsLock   sync.RWMutex
 	params       url.Values
 	PrimitiveURL string
 }
 
-// URL is not thread-safe.
+// noCopy may be embedded into structs which must not be copied
+// after the first use.
+//
+// See https://golang.org/issues/8005#issuecomment-190753527
+// for details.
+type noCopy struct{}
+
+// Lock is a no-op used by -copylocks checker from `go vet`.
+func (*noCopy) Lock()   {}
+func (*noCopy) Unlock() {}
+
+// URL thread-safe. but this url should not be copied.
 // we fail to define this struct to be immutable object.
 // but, those method which will update the URL, including SetParam, SetParams
 // are only allowed to be invoked in creating URL instance
 // Please keep in mind that this struct is immutable after it has been created and initialized.
 type URL struct {
+	noCopy noCopy
+
 	baseUrl
-	Path     string // like  /com.ikurento.dubbo.UserProvider3
+	Path     string // like  /com.ikurento.dubbo.UserProvider
 	Username string
 	Password string
 	Methods  []string
@@ -101,80 +125,80 @@ type URL struct {
 
 // Option accepts url
 // Option will define a function of handling URL
-type option func(*URL)
+type Option func(*URL)
 
 // WithUsername sets username for url
-func WithUsername(username string) option {
+func WithUsername(username string) Option {
 	return func(url *URL) {
 		url.Username = username
 	}
 }
 
 // WithPassword sets password for url
-func WithPassword(pwd string) option {
+func WithPassword(pwd string) Option {
 	return func(url *URL) {
 		url.Password = pwd
 	}
 }
 
 // WithMethods sets methods for url
-func WithMethods(methods []string) option {
+func WithMethods(methods []string) Option {
 	return func(url *URL) {
 		url.Methods = methods
 	}
 }
 
 // WithParams sets params for url
-func WithParams(params url.Values) option {
+func WithParams(params url.Values) Option {
 	return func(url *URL) {
 		url.params = params
 	}
 }
 
 // WithParamsValue sets params field for url
-func WithParamsValue(key, val string) option {
+func WithParamsValue(key, val string) Option {
 	return func(url *URL) {
 		url.SetParam(key, val)
 	}
 }
 
 // WithProtocol sets protocol for url
-func WithProtocol(proto string) option {
+func WithProtocol(proto string) Option {
 	return func(url *URL) {
 		url.Protocol = proto
 	}
 }
 
 // WithIp sets ip for url
-func WithIp(ip string) option {
+func WithIp(ip string) Option {
 	return func(url *URL) {
 		url.Ip = ip
 	}
 }
 
 // WithPort sets port for url
-func WithPort(port string) option {
+func WithPort(port string) Option {
 	return func(url *URL) {
 		url.Port = port
 	}
 }
 
 // WithPath sets path for url
-func WithPath(path string) option {
+func WithPath(path string) Option {
 	return func(url *URL) {
 		url.Path = "/" + strings.TrimPrefix(path, "/")
 	}
 }
 
 // WithLocation sets location for url
-func WithLocation(location string) option {
+func WithLocation(location string) Option {
 	return func(url *URL) {
 		url.Location = location
 	}
 }
 
 // WithToken sets token for url
-func WithToken(token string) option {
+func WithToken(token string) Option {
 	return func(url *URL) {
 		if len(token) > 0 {
 			value := token
@@ -192,51 +216,45 @@ func WithToken(token string) option {
 }
 
 // NewURLWithOptions will create a new url with options
-func NewURLWithOptions(opts ...option) *URL {
-	url := &URL{}
+func NewURLWithOptions(opts ...Option) *URL {
+	newUrl := &URL{}
 	for _, opt := range opts {
-		opt(url)
+		opt(newUrl)
 	}
-	url.Location = url.Ip + ":" + url.Port
-	return url
+	newUrl.Location = newUrl.Ip + ":" + newUrl.Port
+	return newUrl
 }
 
 // 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{}}
-	)
-
-	// new a null instance
+func NewURL(urlString string, opts ...Option) (*URL, error) {
+	s := URL{baseUrl: baseUrl{}}
 	if urlString == "" {
-		return s, nil
+		return &s, nil
 	}
 
-	rawUrlString, err = url.QueryUnescape(urlString)
+	rawUrlString, err := url.QueryUnescape(urlString)
 	if err != nil {
-		return s, perrors.Errorf("url.QueryUnescape(%s),  error{%v}", urlString, err)
+		return &s, perrors.Errorf("url.QueryUnescape(%s),  error{%v}", urlString, err)
 	}
 
 	// rawUrlString = "//" + rawUrlString
-	if strings.Index(rawUrlString, "//") < 0 {
+	if !strings.Contains(rawUrlString, "//") {
 		t := URL{baseUrl: baseUrl{}}
 		for _, opt := range opts {
 			opt(&t)
 		}
 		rawUrlString = t.Protocol + "://" + rawUrlString
 	}
-	serviceUrl, err = url.Parse(rawUrlString)
-	if err != nil {
-		return s, perrors.Errorf("url.Parse(url string{%s}),  error{%v}", rawUrlString, err)
+
+	serviceUrl, urlParseErr := url.Parse(rawUrlString)
+	if urlParseErr != nil {
+		return &s, perrors.Errorf("url.Parse(url string{%s}),  error{%v}", rawUrlString, err)
 	}
 
 	s.params, err = url.ParseQuery(serviceUrl.RawQuery)
 	if err != nil {
-		return s, perrors.Errorf("url.ParseQuery(raw url string{%s}),  error{%v}", serviceUrl.RawQuery, err)
+		return &s, perrors.Errorf("url.ParseQuery(raw url string{%s}),  error{%v}", serviceUrl.RawQuery, err)
 	}
 
 	s.PrimitiveURL = urlString
@@ -248,25 +266,29 @@ func NewURL(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 {
 		opt(&s)
 	}
-	return s, nil
+	return &s, nil
 }
 
 // URLEqual judge @url and @c is equal or not.
-func (c URL) URLEqual(url URL) bool {
-	c.Ip = ""
-	c.Port = ""
-	url.Ip = ""
-	url.Port = ""
-	cGroup := c.GetParam(constant.GROUP_KEY, "")
-	urlGroup := url.GetParam(constant.GROUP_KEY, "")
-	cKey := c.Key()
-	urlKey := url.Key()
+func (c *URL) URLEqual(url *URL) bool {
+	tmpC := c.Clone()
+	tmpC.Ip = ""
+	tmpC.Port = ""
+
+	tmpUrl := url.Clone()
+	tmpUrl.Ip = ""
+	tmpUrl.Port = ""
+
+	cGroup := tmpC.GetParam(constant.GROUP_KEY, "")
+	urlGroup := tmpUrl.GetParam(constant.GROUP_KEY, "")
+	cKey := tmpC.Key()
+	urlKey := tmpUrl.Key()
 
 	if cGroup == constant.ANY_VALUE {
 		cKey = strings.Replace(cKey, "group=*", "group="+urlGroup, 1)
@@ -280,12 +302,12 @@ func (c URL) URLEqual(url URL) bool {
 	}
 
 	// 2. if url contains enabled key, should be true, or *
-	if url.GetParam(constant.ENABLED_KEY, "true") != "true" && url.GetParam(constant.ENABLED_KEY, "") != constant.ANY_VALUE {
+	if tmpUrl.GetParam(constant.ENABLED_KEY, "true") != "true" && tmpUrl.GetParam(constant.ENABLED_KEY, "") != constant.ANY_VALUE {
 		return false
 	}
 
 	// TODO :may need add interface key any value condition
-	return isMatchCategory(url.GetParam(constant.CATEGORY_KEY, constant.DEFAULT_CATEGORY), c.GetParam(constant.CATEGORY_KEY, constant.DEFAULT_CATEGORY))
+	return isMatchCategory(tmpUrl.GetParam(constant.CATEGORY_KEY, constant.DEFAULT_CATEGORY), tmpC.GetParam(constant.CATEGORY_KEY, constant.DEFAULT_CATEGORY))
 }
 
 func isMatchCategory(category1 string, category2 string) bool {
@@ -300,37 +322,37 @@ func isMatchCategory(category1 string, category2 string) bool {
 	}
 }
 
-func (c URL) String() string {
+func (c *URL) String() string {
+	c.paramsLock.Lock()
+	defer c.paramsLock.Unlock()
 	var buf strings.Builder
 	if len(c.Username) == 0 && len(c.Password) == 0 {
-		buf.WriteString(fmt.Sprintf(
-			"%s://%s:%s%s?",
-			c.Protocol, c.Ip, c.Port, c.Path))
+		buf.WriteString(fmt.Sprintf("%s://%s:%s%s?", c.Protocol, c.Ip, c.Port, c.Path))
 	} else {
-		buf.WriteString(fmt.Sprintf(
-			"%s://%s:%s@%s:%s%s?",
-			c.Protocol, c.Username, c.Password, c.Ip, c.Port, c.Path))
+		buf.WriteString(fmt.Sprintf("%s://%s:%s@%s:%s%s?", c.Protocol, c.Username, c.Password, c.Ip, c.Port, c.Path))
 	}
 	buf.WriteString(c.params.Encode())
 	return buf.String()
 }
 
 // Key gets key
-func (c URL) Key() string {
-	buildString := fmt.Sprintf(
-		"%s://%s:%s@%s:%s/?interface=%s&group=%s&version=%s",
+func (c *URL) Key() string {
+	buildString := fmt.Sprintf("%s://%s:%s@%s:%s/?interface=%s&group=%s&version=%s",
 		c.Protocol, c.Username, c.Password, c.Ip, c.Port, c.Service(), c.GetParam(constant.GROUP_KEY, ""), c.GetParam(constant.VERSION_KEY, ""))
 	return buildString
 }
 
 // ServiceKey gets a unique key of a service.
-func (c URL) ServiceKey() string {
-	intf := c.GetParam(constant.INTERFACE_KEY, strings.TrimPrefix(c.Path, "/"))
+func (c *URL) ServiceKey() string {
+	return ServiceKey(c.GetParam(constant.INTERFACE_KEY, strings.TrimPrefix(c.Path, "/")),
+		c.GetParam(constant.GROUP_KEY, ""), c.GetParam(constant.VERSION_KEY, ""))
+}
+
+func ServiceKey(intf string, group string, version string) string {
 	if intf == "" {
 		return ""
 	}
-	var buf strings.Builder
-	group := c.GetParam(constant.GROUP_KEY, "")
+	buf := &bytes.Buffer{}
 	if group != "" {
 		buf.WriteString(group)
 		buf.WriteString("/")
@@ -338,7 +360,6 @@ func (c URL) ServiceKey() string {
 
 	buf.WriteString(intf)
 
-	version := c.GetParam(constant.VERSION_KEY, "")
 	if version != "" && version != "0.0.0" {
 		buf.WriteString(":")
 		buf.WriteString(version)
@@ -376,7 +397,7 @@ func (c *URL) EncodedServiceKey() string {
 }
 
 // Service gets service
-func (c URL) Service() string {
+func (c *URL) Service() string {
 	service := c.GetParam(constant.INTERFACE_KEY, strings.TrimPrefix(c.Path, "/"))
 	if service != "" {
 		return service
@@ -390,23 +411,49 @@ func (c URL) Service() string {
 }
 
 // AddParam will add the key-value pair
-// Not thread-safe
-// think twice before using it.
 func (c *URL) AddParam(key string, value string) {
+	c.paramsLock.Lock()
+	defer c.paramsLock.Unlock()
+	c.params.Add(key, value)
+}
+
+// AddParamAvoidNil will add key-value pair
+func (c *URL) AddParamAvoidNil(key string, value string) {
+	c.paramsLock.Lock()
+	defer c.paramsLock.Unlock()
+	if c.params == nil {
+		c.params = url.Values{}
+	}
 	c.params.Add(key, value)
 }
 
 // SetParam will put the key-value pair into url
-// it's not thread safe.
-// think twice before you want to use this method
 // usually it should only be invoked when you want to initialized an url
 func (c *URL) SetParam(key string, value string) {
+	c.paramsLock.Lock()
+	defer c.paramsLock.Unlock()
 	c.params.Set(key, value)
 }
 
+// DelParam will delete the given key from the url
+func (c *URL) DelParam(key string) {
+	c.paramsLock.Lock()
+	defer c.paramsLock.Unlock()
+	c.params.Del(key)
+}
+
+// ReplaceParams will replace the URL.params
+// usually it should only be invoked when you want to modify an url, such as MergeURL
+func (c *URL) ReplaceParams(param url.Values) {
+	c.paramsLock.Lock()
+	defer c.paramsLock.Unlock()
+	c.params = param
+}
+
 // RangeParams will iterate the params
-// it's not thread-safe
 func (c *URL) RangeParams(f func(key, value string) bool) {
+	c.paramsLock.RLock()
+	defer c.paramsLock.RUnlock()
 	for k, v := range c.params {
 		if !f(k, v[0]) {
 			break
@@ -415,7 +462,9 @@ func (c *URL) RangeParams(f func(key, value string) bool) {
 }
 
 // GetParam gets value by key
-func (c URL) GetParam(s string, d string) string {
+func (c *URL) GetParam(s string, d string) string {
+	c.paramsLock.RLock()
+	defer c.paramsLock.RUnlock()
 	r := c.params.Get(s)
 	if len(r) == 0 {
 		r = d
@@ -424,19 +473,19 @@ func (c URL) GetParam(s string, d string) string {
 }
 
 // GetParams gets values
-func (c URL) GetParams() url.Values {
+func (c *URL) GetParams() url.Values {
 	return c.params
 }
 
 // GetParamAndDecoded gets values and decode
-func (c URL) GetParamAndDecoded(key string) (string, error) {
+func (c *URL) GetParamAndDecoded(key string) (string, error) {
 	ruleDec, err := base64.URLEncoding.DecodeString(c.GetParam(key, ""))
 	value := string(ruleDec)
 	return value, err
 }
 
 // GetRawParam gets raw param
-func (c URL) GetRawParam(key string) string {
+func (c *URL) GetRawParam(key string) string {
 	switch key {
 	case PROTOCOL:
 		return c.Protocol
@@ -456,7 +505,7 @@ func (c URL) GetRawParam(key string) string {
 }
 
 // GetParamBool judge whether @key exists or not
-func (c URL) GetParamBool(key string, d bool) bool {
+func (c *URL) GetParamBool(key string, d bool) bool {
 	r, err := strconv.ParseBool(c.GetParam(key, ""))
 	if err != nil {
 		return d
@@ -464,26 +513,53 @@ func (c URL) GetParamBool(key string, d bool) bool {
 	return r
 }
 
-// GetParamInt gets int value by @key
-func (c URL) GetParamInt(key string, d int64) int64 {
-	r, err := strconv.Atoi(c.GetParam(key, ""))
-	if r == 0 || err != nil {
+// GetParamInt gets int64 value by @key
+func (c *URL) GetParamInt(key string, d int64) int64 {
+	r, err := strconv.ParseInt(c.GetParam(key, ""), 10, 64)
+	if err != nil {
+		return d
+	}
+	return r
+}
+
+// GetParamInt32 gets int32 value by @key
+func (c *URL) GetParamInt32(key string, d int32) int32 {
+	r, err := strconv.ParseInt(c.GetParam(key, ""), 10, 32)
+	if err != nil {
+		return d
+	}
+	return int32(r)
+}
+
+// GetParamByIntValue gets int value by @key
+func (c *URL) GetParamByIntValue(key string, d int) int {
+	r, err := strconv.ParseInt(c.GetParam(key, ""), 10, 0)
+	if err != nil {
 		return d
 	}
-	return int64(r)
+	return int(r)
 }
 
 // GetMethodParamInt gets int method param
-func (c URL) GetMethodParamInt(method string, key string, d int64) int64 {
-	r, err := strconv.Atoi(c.GetParam("methods."+method+"."+key, ""))
-	if r == 0 || err != nil {
+func (c *URL) GetMethodParamInt(method string, key string, d int64) int64 {
+	r, err := strconv.ParseInt(c.GetParam("methods."+method+"."+key, ""), 10, 64)
+	if err != nil {
 		return d
 	}
-	return int64(r)
+	return r
+}
+
+// GetMethodParamIntValue gets int method param
+func (c *URL) GetMethodParamIntValue(method string, key string, d int) int {
+	r, err := strconv.ParseInt(c.GetParam("methods."+method+"."+key, ""), 10, 0)
+	if err != nil {
+		return d
+	}
+	return int(r)
 }
 
 // GetMethodParamInt64 gets int64 method param
-func (c URL) GetMethodParamInt64(method string, key string, d int64) int64 {
+func (c *URL) GetMethodParamInt64(method string, key string, d int64) int64 {
 	r := c.GetMethodParamInt(method, key, math.MinInt64)
 	if r == math.MinInt64 {
 		return c.GetParamInt(key, d)
@@ -492,7 +568,7 @@ func (c URL) GetMethodParamInt64(method string, key string, d int64) int64 {
 }
 
 // GetMethodParam gets method param
-func (c URL) GetMethodParam(method string, key string, d string) string {
+func (c *URL) GetMethodParam(method string, key string, d string) string {
 	r := c.GetParam("methods."+method+"."+key, "")
 	if r == "" {
 		r = d
@@ -501,15 +577,15 @@ func (c URL) GetMethodParam(method string, key string, d string) string {
 }
 
 // GetMethodParamBool judge whether @method param exists or not
-func (c URL) GetMethodParamBool(method string, key string, d bool) bool {
+func (c *URL) GetMethodParamBool(method string, key string, d bool) bool {
 	r := c.GetParamBool("methods."+method+"."+key, d)
 	return r
 }
 
-// SetParams will put all key-value pair into url.
-// 1. if there already has same key, the value will be override
-// 2. it's not thread safe
-// 3. think twice when you want to invoke this method
+//SetParams will put all key-value pair into url.
+//1. if there already has same key, the value will be override
+//2. it's not thread safe
+//3. think twice when you want to invoke this method
 func (c *URL) SetParams(m url.Values) {
 	for k := range m {
 		c.SetParam(k, m.Get(k))
@@ -517,7 +593,7 @@ func (c *URL) SetParams(m url.Values) {
 }
 
 // ToMap transfer URL to Map
-func (c URL) ToMap() map[string]string {
+func (c *URL) ToMap() map[string]string {
 	paramsMap := make(map[string]string)
 
 	c.RangeParams(func(key, value string) bool {
@@ -568,22 +644,26 @@ func (c URL) ToMap() map[string]string {
 // You should notice that the value of b1 is v2, not v4.
 // due to URL is not thread-safe, so this method is not thread-safe
 func MergeUrl(serviceUrl *URL, referenceUrl *URL) *URL {
+	// After Clone, it is a new url that there is no thread safe issue.
 	mergedUrl := serviceUrl.Clone()
-
+	params := mergedUrl.GetParams()
 	// iterator the referenceUrl if serviceUrl not have the key ,merge in
-	referenceUrl.RangeParams(func(key, value string) bool {
+	// referenceUrl usually will not changed. so change RangeParams to GetParams to avoid the string value copy.
+	for key, value := range referenceUrl.GetParams() {
 		if v := mergedUrl.GetParam(key, ""); len(v) == 0 {
-			mergedUrl.SetParam(key, value)
+			if len(value) > 0 {
+				params[key] = value
+			}
 		}
-		return true
-	})
+	}
+
 	// loadBalance,cluster,retries strategy config
-	methodConfigMergeFcn := mergeNormalParam(mergedUrl, referenceUrl, []string{constant.LOADBALANCE_KEY, constant.CLUSTER_KEY, constant.RETRIES_KEY, constant.TIMEOUT_KEY})
+	methodConfigMergeFcn := mergeNormalParam(params, 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 {
-		mergedUrl.SetParam(constant.REMOTE_TIMESTAMP_KEY, v)
-		mergedUrl.SetParam(constant.TIMESTAMP_KEY, referenceUrl.GetParam(constant.TIMESTAMP_KEY, ""))
+		params[constant.REMOTE_TIMESTAMP_KEY] = []string{v}
+		params[constant.TIMESTAMP_KEY] = []string{referenceUrl.GetParam(constant.TIMESTAMP_KEY, "")}
 	}
 
 	// finally execute methodConfigMergeFcn
@@ -592,7 +672,8 @@ func MergeUrl(serviceUrl *URL, referenceUrl *URL) *URL {
 			fcn("methods." + method)
 		}
 	}
-
+	// In this way, we will raise some performance.
+	mergedUrl.ReplaceParams(params)
 	return mergedUrl
 }
 
@@ -621,6 +702,19 @@ func (c *URL) CloneExceptParams(excludeParams *gxset.HashSet) *URL {
 	return newUrl
 }
 
+func (c *URL) Compare(comp cm.Comparator) int {
+	a := c.String()
+	b := comp.(*URL).String()
+	switch {
+	case a > b:
+		return 1
+	case a < b:
+		return -1
+	default:
+		return 0
+	}
+}
+
 // Copy url based on the reserved parameter's keys.
 func (c *URL) CloneWithParams(reserveParams []string) *URL {
 	params := url.Values{}
@@ -643,15 +737,46 @@ func (c *URL) CloneWithParams(reserveParams []string) *URL {
 	)
 }
 
-func mergeNormalParam(mergedUrl *URL, referenceUrl *URL, paramKeys []string) []func(method string) {
+// IsEquals compares if two URLs equals with each other. Excludes are all parameter keys which should ignored.
+func IsEquals(left *URL, right *URL, excludes ...string) bool {
+	if (left == nil && right != nil) || (right == nil && left != nil) {
+		return false
+	}
+	if left.Ip != right.Ip || left.Port != right.Port {
+		return false
+	}
+
+	leftMap := left.ToMap()
+	rightMap := right.ToMap()
+	for _, exclude := range excludes {
+		delete(leftMap, exclude)
+		delete(rightMap, exclude)
+	}
+
+	if len(leftMap) != len(rightMap) {
+		return false
+	}
+
+	for lk, lv := range leftMap {
+		if rv, ok := rightMap[lk]; !ok {
+			return false
+		} else if lv != rv {
+			return false
+		}
+	}
+
+	return true
+}
+
+func mergeNormalParam(params url.Values, referenceUrl *URL, paramKeys []string) []func(method string) {
 	methodConfigMergeFcn := make([]func(method string), 0, len(paramKeys))
 	for _, paramKey := range paramKeys {
 		if v := referenceUrl.GetParam(paramKey, ""); len(v) > 0 {
-			mergedUrl.SetParam(paramKey, v)
+			params[paramKey] = []string{v}
 		}
 		methodConfigMergeFcn = append(methodConfigMergeFcn, func(method string) {
 			if v := referenceUrl.GetParam(method+"."+paramKey, ""); len(v) > 0 {
-				mergedUrl.SetParam(method+"."+paramKey, v)
+				params[method+"."+paramKey] = []string{v}
 			}
 		})
 	}
@@ -660,7 +785,7 @@ func mergeNormalParam(mergedUrl *URL, referenceUrl *URL, paramKeys []string) []f
 
 // URLSlice will be used to sort URL instance
 // Instances will be order by URL.String()
-type URLSlice []URL
+type URLSlice []*URL
 
 // nolint
 func (s URLSlice) Len() int {
@@ -676,3 +801,17 @@ func (s URLSlice) Less(i, j int) bool {
 func (s URLSlice) Swap(i, j int) {
 	s[i], s[j] = s[j], s[i]
 }
+
+type CompareURLEqualFunc func(l *URL, r *URL, excludeParam ...string) bool
+
+func defaultCompareURLEqual(l *URL, r *URL, excludeParam ...string) bool {
+	return IsEquals(l, r, excludeParam...)
+}
+
+func SetCompareURLEqualFunc(f CompareURLEqualFunc) {
+	compareURLEqualFunc = f
+}
+
+func GetCompareURLEqualFunc() CompareURLEqualFunc {
+	return compareURLEqualFunc
+}
diff --git a/common/url_test.go b/common/url_test.go
index 6845190a7362571ebbd4738bd146c94f6d644253..9f413494c685ecb908e102aa5e8181fd473445e0 100644
--- a/common/url_test.go
+++ b/common/url_test.go
@@ -167,10 +167,22 @@ func TestURLGetParam(t *testing.T) {
 
 func TestURLGetParamInt(t *testing.T) {
 	params := url.Values{}
-	params.Set("key", "3")
+	params.Set("key", "")
 	u := URL{baseUrl: baseUrl{params: params}}
 	v := u.GetParamInt("key", 1)
-	assert.Equal(t, int64(3), v)
+	assert.Equal(t, int64(1), v)
+
+	u = URL{}
+	v = u.GetParamInt("key", 1)
+	assert.Equal(t, int64(1), v)
+}
+
+func TestURLGetParamIntValue(t *testing.T) {
+	params := url.Values{}
+	params.Set("key", "0")
+	u := URL{baseUrl: baseUrl{params: params}}
+	v := u.GetParamInt("key", 1)
+	assert.Equal(t, int64(0), v)
 
 	u = URL{}
 	v = u.GetParamInt("key", 1)
@@ -277,7 +289,7 @@ func TestMergeUrl(t *testing.T) {
 	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)
+	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", ""))
@@ -295,6 +307,16 @@ func TestURLSetParams(t *testing.T) {
 	assert.Equal(t, "2.6.0", u1.GetParam("version", ""))
 }
 
+func TestURLReplaceParams(t *testing.T) {
+	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")
+	u1.ReplaceParams(params)
+	assert.Equal(t, "3", u1.GetParam("key", ""))
+	assert.Equal(t, "", u1.GetParam("version", ""))
+}
+
 func TestClone(t *testing.T) {
 	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)
@@ -317,5 +339,56 @@ func TestColonSeparatedKey(t *testing.T) {
 	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")
+}
+
+func TestCompareURLEqualFunc(t *testing.T) {
+	// test Default
+	url1, _ := 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")
+	url2, _ := 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=155650979798")
+	assert.False(t, GetCompareURLEqualFunc()(url1, url2))
+	assert.True(t, GetCompareURLEqualFunc()(url1, url2, constant.TIMESTAMP_KEY, constant.REMOTE_TIMESTAMP_KEY))
+
+	// test custom
+	url1, _ = 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")
+	url2, _ = 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=155650979798")
+	assert.True(t, GetCompareURLEqualFunc()(url1, url2, constant.TIMESTAMP_KEY, constant.REMOTE_TIMESTAMP_KEY))
+	SetCompareURLEqualFunc(CustomCompareURLEqual)
+	assert.False(t, GetCompareURLEqualFunc()(url1, url2))
+	assert.False(t, GetCompareURLEqualFunc()(url1, url2, constant.TIMESTAMP_KEY, constant.REMOTE_TIMESTAMP_KEY))
+
+	url1, _ = 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")
+	url2, _ = 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")
+	assert.True(t, GetCompareURLEqualFunc()(url1, url2))
+	assert.True(t, GetCompareURLEqualFunc()(url1, url2, constant.TIMESTAMP_KEY, constant.REMOTE_TIMESTAMP_KEY))
+	SetCompareURLEqualFunc(CustomCompareURLEqual)
+	assert.True(t, GetCompareURLEqualFunc()(url1, url2))
+	assert.True(t, GetCompareURLEqualFunc()(url1, url2, constant.TIMESTAMP_KEY, constant.REMOTE_TIMESTAMP_KEY))
+}
 
+func CustomCompareURLEqual(l *URL, r *URL, execludeParam ...string) bool {
+	return l.PrimitiveURL == r.PrimitiveURL
 }
diff --git a/config/application_config.go b/config/application_config.go
index ef99664fa298c28365ed7acc54d0c18a88c9b5c2..6fe6c5d049f8ce95a56fcfd62eb1cffc03bf3c43 100644
--- a/config/application_config.go
+++ b/config/application_config.go
@@ -48,8 +48,5 @@ func (c *ApplicationConfig) UnmarshalYAML(unmarshal func(interface{}) error) err
 		return err
 	}
 	type plain ApplicationConfig
-	if err := unmarshal((*plain)(c)); err != nil {
-		return err
-	}
-	return nil
+	return unmarshal((*plain)(c))
 }
diff --git a/config/base_config.go b/config/base_config.go
index 22a0832731daff6c9957d4913a3784c9b268b11f..0cc6eec26c51cb7dfc164a3d43545f6b22658ca0 100644
--- a/config/base_config.go
+++ b/config/base_config.go
@@ -33,10 +33,6 @@ import (
 	"github.com/apache/dubbo-go/common/logger"
 )
 
-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"`
@@ -78,15 +74,8 @@ func getKeyPrefix(val reflect.Value) []string {
 	} else {
 		prefix = val.MethodByName(configPrefixMethod).Call(nil)[0].String()
 	}
-	var retPrefixes []string
-
-	for _, pfx := range strings.Split(prefix, "|") {
-
-		retPrefixes = append(retPrefixes, pfx)
-
-	}
-	return retPrefixes
 
+	return strings.Split(prefix, "|")
 }
 
 func getPtrElement(v reflect.Value) reflect.Value {
@@ -216,12 +205,9 @@ func setFieldValue(val reflect.Value, id reflect.Value, config *config.InmemoryC
 						prefix := s.MethodByName("Prefix").Call(nil)[0].String()
 						for _, pfx := range strings.Split(prefix, "|") {
 							m := config.GetSubProperty(pfx)
-							if m != nil {
-								for k := range m {
-									f.SetMapIndex(reflect.ValueOf(k), reflect.New(f.Type().Elem().Elem()))
-								}
+							for k := range m {
+								f.SetMapIndex(reflect.ValueOf(k), reflect.New(f.Type().Elem().Elem()))
 							}
-
 						}
 
 					}
diff --git a/config/config_center_config.go b/config/config_center_config.go
index 0fc4007940d9b1ac2456c9b2d379493bb5d8edb0..3bb856415d77344658055f03ccc4a6edd8c0f48a 100644
--- a/config/config_center_config.go
+++ b/config/config_center_config.go
@@ -26,6 +26,7 @@ import (
 
 import (
 	"github.com/creasty/defaults"
+	perrors "github.com/pkg/errors"
 )
 
 import (
@@ -35,7 +36,6 @@ import (
 	"github.com/apache/dubbo-go/common/extension"
 	"github.com/apache/dubbo-go/common/logger"
 	"github.com/apache/dubbo-go/config_center"
-	perrors "github.com/pkg/errors"
 )
 
 // ConfigCenterConfig is configuration for config center
@@ -69,10 +69,7 @@ func (c *ConfigCenterConfig) UnmarshalYAML(unmarshal func(interface{}) error) er
 		return err
 	}
 	type plain ConfigCenterConfig
-	if err := unmarshal((*plain)(c)); err != nil {
-		return err
-	}
-	return nil
+	return unmarshal((*plain)(c))
 }
 
 // GetUrlMap gets url map from ConfigCenterConfig
@@ -91,7 +88,7 @@ type configCenter struct {
 
 // toURL will compatible with baseConfig.ConfigCenterConfig.Address and baseConfig.ConfigCenterConfig.RemoteRef before 1.6.0
 // After 1.6.0 will not compatible, only baseConfig.ConfigCenterConfig.RemoteRef
-func (b *configCenter) toURL(baseConfig BaseConfig) (common.URL, error) {
+func (b *configCenter) toURL(baseConfig BaseConfig) (*common.URL, error) {
 	if len(baseConfig.ConfigCenterConfig.Address) > 0 {
 		return common.NewURL(baseConfig.ConfigCenterConfig.Address,
 			common.WithProtocol(baseConfig.ConfigCenterConfig.Protocol), common.WithParams(baseConfig.ConfigCenterConfig.GetUrlMap()))
@@ -101,7 +98,7 @@ func (b *configCenter) toURL(baseConfig BaseConfig) (common.URL, error) {
 	rc, ok := baseConfig.GetRemoteConfig(remoteRef)
 
 	if !ok {
-		return common.URL{}, perrors.New("Could not find out the remote ref config, name: " + remoteRef)
+		return nil, perrors.New("Could not find out the remote ref config, name: " + remoteRef)
 	}
 
 	newURL, err := rc.toURL()
@@ -114,11 +111,11 @@ func (b *configCenter) toURL(baseConfig BaseConfig) (common.URL, error) {
 // startConfigCenter will start the config center.
 // it will prepare the environment
 func (b *configCenter) startConfigCenter(baseConfig BaseConfig) error {
-	url, err := b.toURL(baseConfig)
+	newUrl, err := b.toURL(baseConfig)
 	if err != nil {
 		return err
 	}
-	if err = b.prepareEnvironment(baseConfig, &url); err != nil {
+	if err = b.prepareEnvironment(baseConfig, newUrl); err != nil {
 		return perrors.WithMessagef(err, "start config center error!")
 	}
 	// c.fresh()
diff --git a/config/config_loader.go b/config/config_loader.go
index 75b82628d68a23e575cfa637b3603d09e09ea9d6..ec591aad6526e5841656465e71465413d01ad5ba 100644
--- a/config/config_loader.go
+++ b/config/config_loader.go
@@ -28,7 +28,6 @@ import (
 )
 
 import (
-	gxnet "github.com/dubbogo/gost/net"
 	perrors "github.com/pkg/errors"
 )
 
@@ -141,19 +140,18 @@ func loadConsumerConfig() {
 
 	// wait for invoker is available, if wait over default 3s, then panic
 	var count int
-	checkok := true
 	for {
+		checkok := true
 		for _, refconfig := range consumerConfig.References {
 			if (refconfig.Check != nil && *refconfig.Check) ||
 				(refconfig.Check == nil && consumerConfig.Check != nil && *consumerConfig.Check) ||
 				(refconfig.Check == nil && consumerConfig.Check == nil) { // default to true
 
-				if refconfig.invoker != nil &&
-					!refconfig.invoker.IsAvailable() {
+				if refconfig.invoker != nil && !refconfig.invoker.IsAvailable() {
 					checkok = false
 					count++
 					if count > maxWait {
-						errMsg := fmt.Sprintf("Failed to check the status of the service %v . No provider available for the service to the consumer use dubbo version %v", refconfig.InterfaceName, constant.Version)
+						errMsg := fmt.Sprintf("Failed to check the status of the service %v. No provider available for the service to the consumer use dubbo version %v", refconfig.InterfaceName, constant.Version)
 						logger.Error(errMsg)
 						panic(errMsg)
 					}
@@ -161,14 +159,13 @@ func loadConsumerConfig() {
 					break
 				}
 				if refconfig.invoker == nil {
-					logger.Warnf("The interface %s invoker not exist , may you should check your interface config.", refconfig.InterfaceName)
+					logger.Warnf("The interface %s invoker not exist, may you should check your interface config.", refconfig.InterfaceName)
 				}
 			}
 		}
 		if checkok {
 			break
 		}
-		checkok = true
 	}
 }
 
@@ -219,7 +216,7 @@ func registerServiceInstance() {
 	if url == nil {
 		return
 	}
-	instance, err := createInstance(*url)
+	instance, err := createInstance(url)
 	if err != nil {
 		panic(err)
 	}
@@ -243,7 +240,7 @@ func registerServiceInstance() {
 }
 
 // nolint
-func createInstance(url common.URL) (registry.ServiceInstance, error) {
+func createInstance(url *common.URL) (registry.ServiceInstance, error) {
 	appConfig := GetApplicationConfig()
 	port, err := strconv.ParseInt(url.Port, 10, 32)
 	if err != nil {
@@ -252,10 +249,7 @@ func createInstance(url common.URL) (registry.ServiceInstance, error) {
 
 	host := url.Ip
 	if len(host) == 0 {
-		host, err = gxnet.GetLocalIP()
-		if err != nil {
-			return nil, perrors.WithMessage(err, "could not get the local Ip")
-		}
+		host = common.GetLocalIp()
 	}
 
 	// usually we will add more metadata
@@ -275,7 +269,7 @@ func createInstance(url common.URL) (registry.ServiceInstance, error) {
 
 // selectMetadataServiceExportedURL get already be exported url
 func selectMetadataServiceExportedURL() *common.URL {
-	var selectedUrl common.URL
+	var selectedUrl *common.URL
 	metaDataService, err := extension.GetMetadataService(GetApplicationConfig().MetadataType)
 	if err != nil {
 		logger.Warn(err)
@@ -300,7 +294,7 @@ func selectMetadataServiceExportedURL() *common.URL {
 			break
 		}
 	}
-	return &selectedUrl
+	return selectedUrl
 }
 
 func initRouter() {
diff --git a/config/config_loader_test.go b/config/config_loader_test.go
index 461e607c1e0ad2e9471770224c8eb6d5f3ee96f6..c3c3eb93526a51fe8f2041b17049144b4ec7b703 100644
--- a/config/config_loader_test.go
+++ b/config/config_loader_test.go
@@ -25,7 +25,6 @@ import (
 )
 
 import (
-	cm "github.com/Workiva/go-datastructures/common"
 	"github.com/Workiva/go-datastructures/slice/skip"
 	gxset "github.com/dubbogo/gost/container/set"
 	gxpage "github.com/dubbogo/gost/page"
@@ -105,7 +104,7 @@ func TestLoad(t *testing.T) {
 
 	conServices = map[string]common.RPCService{}
 	proServices = map[string]common.RPCService{}
-	err := common.ServiceMap.UnRegister("com.MockService", "mock", "MockService")
+	err := common.ServiceMap.UnRegister("com.MockService", "mock", common.ServiceKey("com.MockService", "huadong_idc", "1.0.0"))
 	assert.Nil(t, err)
 	consumerConfig = nil
 	providerConfig = nil
@@ -144,7 +143,7 @@ func TestLoadWithSingleReg(t *testing.T) {
 
 	conServices = map[string]common.RPCService{}
 	proServices = map[string]common.RPCService{}
-	common.ServiceMap.UnRegister("com.MockService", "mock", "MockService")
+	common.ServiceMap.UnRegister("com.MockService", "mock", common.ServiceKey("com.MockService", "huadong_idc", "1.0.0"))
 	consumerConfig = nil
 	providerConfig = nil
 }
@@ -183,7 +182,7 @@ func TestWithNoRegLoad(t *testing.T) {
 
 	conServices = map[string]common.RPCService{}
 	proServices = map[string]common.RPCService{}
-	common.ServiceMap.UnRegister("com.MockService", "mock", "MockService")
+	common.ServiceMap.UnRegister("com.MockService", "mock", common.ServiceKey("com.MockService", "huadong_idc", "1.0.0"))
 	consumerConfig = nil
 	providerConfig = nil
 }
@@ -353,27 +352,27 @@ func (m *mockMetadataService) ServiceName() (string, error) {
 	panic("implement me")
 }
 
-func (m *mockMetadataService) ExportURL(url common.URL) (bool, error) {
-	return m.addURL(m.exportedServiceURLs, &url), nil
+func (m *mockMetadataService) ExportURL(url *common.URL) (bool, error) {
+	return m.addURL(m.exportedServiceURLs, url), nil
 }
 
-func (m *mockMetadataService) UnexportURL(url common.URL) error {
+func (m *mockMetadataService) UnexportURL(*common.URL) error {
 	panic("implement me")
 }
 
-func (m *mockMetadataService) SubscribeURL(url common.URL) (bool, error) {
+func (m *mockMetadataService) SubscribeURL(*common.URL) (bool, error) {
 	panic("implement me")
 }
 
-func (m *mockMetadataService) UnsubscribeURL(url common.URL) error {
+func (m *mockMetadataService) UnsubscribeURL(*common.URL) error {
 	panic("implement me")
 }
 
-func (m *mockMetadataService) PublishServiceDefinition(url common.URL) error {
+func (m *mockMetadataService) PublishServiceDefinition(*common.URL) error {
 	return nil
 }
 
-func (m *mockMetadataService) GetExportedURLs(serviceInterface string, group string, version string, protocol string) ([]interface{}, error) {
+func (m *mockMetadataService) GetExportedURLs(string, string, string, string) ([]interface{}, error) {
 	return ConvertURLArrToIntfArr(m.getAllService(m.exportedServiceURLs)), nil
 }
 
@@ -381,19 +380,19 @@ func (m *mockMetadataService) MethodMapper() map[string]string {
 	panic("implement me")
 }
 
-func (m *mockMetadataService) GetSubscribedURLs() ([]common.URL, error) {
+func (m *mockMetadataService) GetSubscribedURLs() ([]*common.URL, error) {
 	panic("implement me")
 }
 
-func (m *mockMetadataService) GetServiceDefinition(interfaceName string, group string, version string) (string, error) {
+func (m *mockMetadataService) GetServiceDefinition(string, string, string) (string, error) {
 	panic("implement me")
 }
 
-func (m *mockMetadataService) GetServiceDefinitionByServiceKey(serviceKey string) (string, error) {
+func (m *mockMetadataService) GetServiceDefinitionByServiceKey(string) (string, error) {
 	panic("implement me")
 }
 
-func (m *mockMetadataService) RefreshMetadata(exportedRevision string, subscribedRevision string) (bool, error) {
+func (m *mockMetadataService) RefreshMetadata(string, string) (bool, error) {
 	panic("implement me")
 }
 
@@ -409,7 +408,7 @@ func (mts *mockMetadataService) addURL(targetMap *sync.Map, url *common.URL) boo
 	logger.Debug(url.ServiceKey())
 	if urlSet, loaded = targetMap.LoadOrStore(url.ServiceKey(), skip.New(uint64(0))); loaded {
 		mts.lock.RLock()
-		wantedUrl := urlSet.(*skip.SkipList).Get(Comparator(*url))
+		wantedUrl := urlSet.(*skip.SkipList).Get(url)
 		if len(wantedUrl) > 0 && wantedUrl[0] != nil {
 			mts.lock.RUnlock()
 			return false
@@ -418,23 +417,23 @@ func (mts *mockMetadataService) addURL(targetMap *sync.Map, url *common.URL) boo
 	}
 	mts.lock.Lock()
 	// double chk
-	wantedUrl := urlSet.(*skip.SkipList).Get(Comparator(*url))
+	wantedUrl := urlSet.(*skip.SkipList).Get(url)
 	if len(wantedUrl) > 0 && wantedUrl[0] != nil {
 		mts.lock.Unlock()
 		return false
 	}
-	urlSet.(*skip.SkipList).Insert(Comparator(*url))
+	urlSet.(*skip.SkipList).Insert(url)
 	mts.lock.Unlock()
 	return true
 }
 
-func (m *mockMetadataService) getAllService(services *sync.Map) []common.URL {
+func (m *mockMetadataService) getAllService(services *sync.Map) []*common.URL {
 	// using skip list to dedup and sorting
-	res := make([]common.URL, 0)
+	var res []*common.URL
 	services.Range(func(key, value interface{}) bool {
 		urls := value.(*skip.SkipList)
 		for i := uint64(0); i < urls.Len(); i++ {
-			url := common.URL(urls.ByPosition(i).(Comparator))
+			url := urls.ByPosition(i).(*common.URL)
 			if url.GetParam(constant.INTERFACE_KEY, url.Path) != constant.METADATA_SERVICE_NAME {
 				res = append(res, url)
 			}
@@ -445,26 +444,10 @@ func (m *mockMetadataService) getAllService(services *sync.Map) []common.URL {
 	return res
 }
 
-type Comparator common.URL
-
-// Compare is defined as Comparator for skip list to compare the URL
-func (c Comparator) Compare(comp cm.Comparator) int {
-	a := common.URL(c).String()
-	b := common.URL(comp.(Comparator)).String()
-	switch {
-	case a > b:
-		return 1
-	case a < b:
-		return -1
-	default:
-		return 0
-	}
-}
-
 type mockServiceDiscoveryRegistry struct {
 }
 
-func (mr *mockServiceDiscoveryRegistry) GetUrl() common.URL {
+func (mr *mockServiceDiscoveryRegistry) GetUrl() *common.URL {
 	panic("implement me")
 }
 
@@ -476,11 +459,11 @@ func (mr *mockServiceDiscoveryRegistry) Destroy() {
 	panic("implement me")
 }
 
-func (mr *mockServiceDiscoveryRegistry) Register(url common.URL) error {
+func (mr *mockServiceDiscoveryRegistry) Register(*common.URL) error {
 	panic("implement me")
 }
 
-func (mr *mockServiceDiscoveryRegistry) UnRegister(url common.URL) error {
+func (mr *mockServiceDiscoveryRegistry) UnRegister(*common.URL) error {
 	panic("implement me")
 }
 
@@ -507,15 +490,15 @@ func (m *mockServiceDiscovery) Destroy() error {
 	panic("implement me")
 }
 
-func (m *mockServiceDiscovery) Register(instance registry.ServiceInstance) error {
+func (m *mockServiceDiscovery) Register(registry.ServiceInstance) error {
 	return nil
 }
 
-func (m *mockServiceDiscovery) Update(instance registry.ServiceInstance) error {
+func (m *mockServiceDiscovery) Update(registry.ServiceInstance) error {
 	panic("implement me")
 }
 
-func (m *mockServiceDiscovery) Unregister(instance registry.ServiceInstance) error {
+func (m *mockServiceDiscovery) Unregister(registry.ServiceInstance) error {
 	panic("implement me")
 }
 
@@ -527,39 +510,39 @@ func (m *mockServiceDiscovery) GetServices() *gxset.HashSet {
 	panic("implement me")
 }
 
-func (m *mockServiceDiscovery) GetInstances(serviceName string) []registry.ServiceInstance {
+func (m *mockServiceDiscovery) GetInstances(string) []registry.ServiceInstance {
 	panic("implement me")
 }
 
-func (m *mockServiceDiscovery) GetInstancesByPage(serviceName string, offset int, pageSize int) gxpage.Pager {
+func (m *mockServiceDiscovery) GetInstancesByPage(string, int, int) gxpage.Pager {
 	panic("implement me")
 }
 
-func (m *mockServiceDiscovery) GetHealthyInstancesByPage(serviceName string, offset int, pageSize int, healthy bool) gxpage.Pager {
+func (m *mockServiceDiscovery) GetHealthyInstancesByPage(string, int, int, bool) gxpage.Pager {
 	panic("implement me")
 }
 
-func (m *mockServiceDiscovery) GetRequestInstances(serviceNames []string, offset int, requestedSize int) map[string]gxpage.Pager {
+func (m *mockServiceDiscovery) GetRequestInstances([]string, int, int) map[string]gxpage.Pager {
 	panic("implement me")
 }
 
-func (m *mockServiceDiscovery) AddListener(listener *registry.ServiceInstancesChangedListener) error {
+func (m *mockServiceDiscovery) AddListener(*registry.ServiceInstancesChangedListener) error {
 	panic("implement me")
 }
 
-func (m *mockServiceDiscovery) DispatchEventByServiceName(serviceName string) error {
+func (m *mockServiceDiscovery) DispatchEventByServiceName(string) error {
 	panic("implement me")
 }
 
-func (m *mockServiceDiscovery) DispatchEventForInstances(serviceName string, instances []registry.ServiceInstance) error {
+func (m *mockServiceDiscovery) DispatchEventForInstances(string, []registry.ServiceInstance) error {
 	panic("implement me")
 }
 
-func (m *mockServiceDiscovery) DispatchEvent(event *registry.ServiceInstancesChangedEvent) error {
+func (m *mockServiceDiscovery) DispatchEvent(*registry.ServiceInstancesChangedEvent) error {
 	panic("implement me")
 }
 
-func ConvertURLArrToIntfArr(urls []common.URL) []interface{} {
+func ConvertURLArrToIntfArr(urls []*common.URL) []interface{} {
 	if len(urls) == 0 {
 		return []interface{}{}
 	}
diff --git a/config/consumer_config.go b/config/consumer_config.go
index 9d283eeca7bbaf5a82f71357853c6b53560b2fe4..ff53366c45406dde8e4c0daac7a5d68aa6bd8934 100644
--- a/config/consumer_config.go
+++ b/config/consumer_config.go
@@ -60,8 +60,8 @@ type ConsumerConfig struct {
 
 	References     map[string]*ReferenceConfig `yaml:"references" json:"references,omitempty" property:"references"`
 	ProtocolConf   interface{}                 `yaml:"protocol_conf" json:"protocol_conf,omitempty" property:"protocol_conf"`
-	FilterConf     interface{}                 `yaml:"filter_conf" json:"filter_conf,omitempty" property:"filter_conf" `
-	ShutdownConfig *ShutdownConfig             `yaml:"shutdown_conf" json:"shutdown_conf,omitempty" property:"shutdown_conf" `
+	FilterConf     interface{}                 `yaml:"filter_conf" json:"filter_conf,omitempty" property:"filter_conf"`
+	ShutdownConfig *ShutdownConfig             `yaml:"shutdown_conf" json:"shutdown_conf,omitempty" property:"shutdown_conf"`
 	ConfigType     map[string]string           `yaml:"config_type" json:"config_type,omitempty" property:"config_type"`
 }
 
@@ -71,10 +71,7 @@ func (c *ConsumerConfig) UnmarshalYAML(unmarshal func(interface{}) error) error
 		return err
 	}
 	type plain ConsumerConfig
-	if err := unmarshal((*plain)(c)); err != nil {
-		return err
-	}
-	return nil
+	return unmarshal((*plain)(c))
 }
 
 // nolint
diff --git a/config/instance/metadata_report.go b/config/instance/metadata_report.go
index 8e833dd70bcc0db8e65cd8703f2bc1859432a887..68a197d3ddb4abfd8ab924749857a7f94a43e1d4 100644
--- a/config/instance/metadata_report.go
+++ b/config/instance/metadata_report.go
@@ -29,7 +29,7 @@ import (
 
 var (
 	instance  report.MetadataReport
-	reportUrl common.URL
+	reportUrl *common.URL
 	once      sync.Once
 )
 
@@ -41,18 +41,18 @@ func GetMetadataReportInstance(selectiveUrl ...*common.URL) report.MetadataRepor
 		if len(selectiveUrl) > 0 {
 			url = selectiveUrl[0]
 			instance = extension.GetMetadataReportFactory(url.Protocol).CreateMetadataReport(url)
-			reportUrl = *url
+			reportUrl = url
 		}
 	})
 	return instance
 }
 
 // GetMetadataReportUrl will return the report instance url
-func GetMetadataReportUrl() common.URL {
+func GetMetadataReportUrl() *common.URL {
 	return reportUrl
 }
 
 // SetMetadataReportUrl will only can be used by unit test to mock url
-func SetMetadataReportUrl(url common.URL) {
+func SetMetadataReportUrl(url *common.URL) {
 	reportUrl = url
 }
diff --git a/config/instance/metadata_report_test.go b/config/instance/metadata_report_test.go
index d489af048055b362f2fa68a8963dd2cdf9632945..110903a41f577ef78b4d7abbcef17864fd6e281c 100644
--- a/config/instance/metadata_report_test.go
+++ b/config/instance/metadata_report_test.go
@@ -38,7 +38,7 @@ func TestGetMetadataReportInstance(t *testing.T) {
 		return &mockMetadataReportFactory{}
 	})
 	u, _ := common.NewURL("mock://127.0.0.1")
-	rpt := GetMetadataReportInstance(&u)
+	rpt := GetMetadataReportInstance(u)
 	assert.NotNil(t, rpt)
 }
 
@@ -60,7 +60,7 @@ func (m mockMetadataReport) StoreConsumerMetadata(*identifier.MetadataIdentifier
 	panic("implement me")
 }
 
-func (m mockMetadataReport) SaveServiceMetadata(*identifier.ServiceMetadataIdentifier, common.URL) error {
+func (m mockMetadataReport) SaveServiceMetadata(*identifier.ServiceMetadataIdentifier, *common.URL) error {
 	panic("implement me")
 }
 
diff --git a/config/interfaces/config_post_processor.go b/config/interfaces/config_post_processor.go
new file mode 100644
index 0000000000000000000000000000000000000000..53dd71780ff0807065e8c4b0e00ab4fe8b6a4823
--- /dev/null
+++ b/config/interfaces/config_post_processor.go
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package interfaces
+
+import (
+	"github.com/apache/dubbo-go/common"
+)
+
+// ConfigPostProcessor is an extension to give users a chance to customize configs against ReferenceConfig and
+// ServiceConfig during deployment time.
+type ConfigPostProcessor interface {
+	// PostProcessReferenceConfig customizes ReferenceConfig's params.
+	PostProcessReferenceConfig(*common.URL)
+
+	// PostProcessServiceConfig customizes ServiceConfig's params.
+	PostProcessServiceConfig(*common.URL)
+}
diff --git a/config/metadata_report_config.go b/config/metadata_report_config.go
index 6d319e5ecb8007e06dcf790fff145bfab754df3d..24eba5ec8dc483966b8e63271b40cde9cbf54e3e 100644
--- a/config/metadata_report_config.go
+++ b/config/metadata_report_config.go
@@ -84,7 +84,7 @@ func (c *MetadataReportConfig) ToUrl() (*common.URL, error) {
 		return nil, perrors.New("Invalid MetadataReportConfig.")
 	}
 	res.SetParam("metadata", res.Protocol)
-	return &res, nil
+	return res, nil
 }
 
 func (c *MetadataReportConfig) IsValid() bool {
@@ -101,8 +101,8 @@ func startMetadataReport(metadataType string, metadataReportConfig *MetadataRepo
 		return perrors.New("MetadataConfig remote ref can not be empty.")
 	}
 
-	if url, err := metadataReportConfig.ToUrl(); err == nil {
-		instance.GetMetadataReportInstance(url)
+	if tmpUrl, err := metadataReportConfig.ToUrl(); err == nil {
+		instance.GetMetadataReportInstance(tmpUrl)
 	} else {
 		return perrors.Wrap(err, "Start MetadataReport failed.")
 	}
diff --git a/config/method_config.go b/config/method_config.go
index b64306fd6aa865d219506ea2722067619b00fea7..db52940c3db6b4b81b0467945b538c9b540cd46a 100644
--- a/config/method_config.go
+++ b/config/method_config.go
@@ -57,8 +57,5 @@ func (c *MethodConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
 		return err
 	}
 	type plain MethodConfig
-	if err := unmarshal((*plain)(c)); err != nil {
-		return err
-	}
-	return nil
+	return unmarshal((*plain)(c))
 }
diff --git a/config/provider_config.go b/config/provider_config.go
index c710e48dc233a62837b31a89828e9c612eaff093..0aee5600298233b764df75b02c745a3ec8d9faef 100644
--- a/config/provider_config.go
+++ b/config/provider_config.go
@@ -43,9 +43,9 @@ type ProviderConfig struct {
 	ProxyFactory   string                     `yaml:"proxy_factory" default:"default" json:"proxy_factory,omitempty" property:"proxy_factory"`
 	Services       map[string]*ServiceConfig  `yaml:"services" json:"services,omitempty" property:"services"`
 	Protocols      map[string]*ProtocolConfig `yaml:"protocols" json:"protocols,omitempty" property:"protocols"`
-	ProtocolConf   interface{}                `yaml:"protocol_conf" json:"protocol_conf,omitempty" property:"protocol_conf" `
-	FilterConf     interface{}                `yaml:"filter_conf" json:"filter_conf,omitempty" property:"filter_conf" `
-	ShutdownConfig *ShutdownConfig            `yaml:"shutdown_conf" json:"shutdown_conf,omitempty" property:"shutdown_conf" `
+	ProtocolConf   interface{}                `yaml:"protocol_conf" json:"protocol_conf,omitempty" property:"protocol_conf"`
+	FilterConf     interface{}                `yaml:"filter_conf" json:"filter_conf,omitempty" property:"filter_conf"`
+	ShutdownConfig *ShutdownConfig            `yaml:"shutdown_conf" json:"shutdown_conf,omitempty" property:"shutdown_conf"`
 	ConfigType     map[string]string          `yaml:"config_type" json:"config_type,omitempty" property:"config_type"`
 
 	Registry   *RegistryConfig            `yaml:"registry" json:"registry,omitempty" property:"registry"`
@@ -58,10 +58,7 @@ func (c *ProviderConfig) UnmarshalYAML(unmarshal func(interface{}) error) error
 		return err
 	}
 	type plain ProviderConfig
-	if err := unmarshal((*plain)(c)); err != nil {
-		return err
-	}
-	return nil
+	return unmarshal((*plain)(c))
 }
 
 // nolint
diff --git a/config/reference_config.go b/config/reference_config.go
index bbc875192c7a87354ccc81e28ea05bbc3bb71149..431ec0e2eb3c03b27cca40acd7b721cf6b8f9755 100644
--- a/config/reference_config.go
+++ b/config/reference_config.go
@@ -86,17 +86,13 @@ func (c *ReferenceConfig) UnmarshalYAML(unmarshal func(interface{}) error) error
 	}
 
 	*c = ReferenceConfig(raw)
-	if err := defaults.Set(c); err != nil {
-		return err
-	}
-
-	return nil
+	return defaults.Set(c)
 }
 
 // Refer ...
 func (c *ReferenceConfig) Refer(_ interface{}) {
 	cfgURL := common.NewURLWithOptions(
-		common.WithPath(c.id),
+		common.WithPath(c.InterfaceName),
 		common.WithProtocol(c.Protocol),
 		common.WithParams(c.getUrlMap()),
 		common.WithParamsValue(constant.BEAN_NAME_KEY, c.id),
@@ -104,6 +100,9 @@ func (c *ReferenceConfig) Refer(_ interface{}) {
 	if c.ForceTag {
 		cfgURL.AddParam(constant.ForceUseTag, "true")
 	}
+
+	c.postProcessConfig(cfgURL)
+
 	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*")
@@ -114,13 +113,13 @@ func (c *ReferenceConfig) Refer(_ interface{}) {
 			}
 			if serviceUrl.Protocol == constant.REGISTRY_PROTOCOL {
 				serviceUrl.SubURL = cfgURL
-				c.urls = append(c.urls, &serviceUrl)
+				c.urls = append(c.urls, serviceUrl)
 			} else {
 				if serviceUrl.Path == "" {
-					serviceUrl.Path = "/" + c.id
+					serviceUrl.Path = "/" + c.InterfaceName
 				}
 				// merge url need to do
-				newUrl := common.MergeUrl(&serviceUrl, cfgURL)
+				newUrl := common.MergeUrl(serviceUrl, cfgURL)
 				c.urls = append(c.urls, newUrl)
 			}
 		}
@@ -135,12 +134,12 @@ func (c *ReferenceConfig) Refer(_ interface{}) {
 	}
 
 	if len(c.urls) == 1 {
-		c.invoker = extension.GetProtocol(c.urls[0].Protocol).Refer(*c.urls[0])
+		c.invoker = extension.GetProtocol(c.urls[0].Protocol).Refer(c.urls[0])
 	} else {
 		invokers := make([]protocol.Invoker, 0, len(c.urls))
 		var regUrl *common.URL
 		for _, u := range c.urls {
-			invokers = append(invokers, extension.GetProtocol(u.Protocol).Refer(*u))
+			invokers = append(invokers, extension.GetProtocol(u.Protocol).Refer(u))
 			if u.Protocol == constant.REGISTRY_PROTOCOL {
 				regUrl = u
 			}
@@ -189,6 +188,11 @@ func (c *ReferenceConfig) GetRPCService() common.RPCService {
 	return c.pxy.Get()
 }
 
+// GetProxy gets proxy
+func (c *ReferenceConfig) GetProxy() *proxy.Proxy {
+	return c.pxy
+}
+
 func (c *ReferenceConfig) getUrlMap() url.Values {
 	urlMap := url.Values{}
 	//first set user params
@@ -251,5 +255,11 @@ func (c *ReferenceConfig) GenericLoad(id string) {
 	c.id = id
 	c.Refer(genericService)
 	c.Implement(genericService)
-	return
+}
+
+// postProcessConfig asks registered ConfigPostProcessor to post-process the current ReferenceConfig.
+func (c *ReferenceConfig) postProcessConfig(url *common.URL) {
+	for _, p := range extension.GetConfigPostProcessors() {
+		p.PostProcessReferenceConfig(url)
+	}
 }
diff --git a/config/reference_config_test.go b/config/reference_config_test.go
index a4345ad13d4944b8fae0930930584fccbd0fb33c..0207e1ff24af022690e3773573ddd61ebad88e15 100644
--- a/config/reference_config_test.go
+++ b/config/reference_config_test.go
@@ -334,7 +334,7 @@ func newRegistryProtocol() protocol.Protocol {
 
 type mockRegistryProtocol struct{}
 
-func (*mockRegistryProtocol) Refer(url common.URL) protocol.Invoker {
+func (*mockRegistryProtocol) Refer(url *common.URL) protocol.Invoker {
 	return protocol.NewBaseInvoker(url)
 }
 
@@ -345,7 +345,7 @@ func (*mockRegistryProtocol) Export(invoker protocol.Invoker) protocol.Exporter
 		if err != nil {
 			panic(err)
 		}
-		ok, err := metaDataService.ExportURL(*invoker.GetUrl().SubURL.Clone())
+		ok, err := metaDataService.ExportURL(invoker.GetUrl().SubURL.Clone())
 		if err != nil {
 			panic(err)
 		}
@@ -367,7 +367,7 @@ func getRegistryUrl(invoker protocol.Invoker) *common.URL {
 		protocol := url.GetParam(constant.REGISTRY_KEY, "")
 		url.Protocol = protocol
 	}
-	return &url
+	return url
 }
 
 func (p *mockRegistryProtocol) GetRegistries() []registry.Registry {
diff --git a/config/registry_config.go b/config/registry_config.go
index 89566c428ed14f460c0f214358c9fa05d529ddb6..ed81a07c637e5de2972bbbd21dab8847c3d42f78 100644
--- a/config/registry_config.go
+++ b/config/registry_config.go
@@ -53,7 +53,7 @@ type RegistryConfig struct {
 	//ZoneForce bool `yaml:"zoneForce" json:"zoneForce,omitempty" property:"zoneForce"`
 	// Affects traffic distribution among registries,
 	// useful when subscribe to multiple registries Take effect only when no preferred registry is specified.
-	Weight int64             `yaml:"weight" json:"params,omitempty" property:"weight"`
+	Weight int64             `yaml:"weight" json:"weight,omitempty" property:"weight"`
 	Params map[string]string `yaml:"params" json:"params,omitempty" property:"params"`
 }
 
@@ -63,10 +63,7 @@ func (c *RegistryConfig) UnmarshalYAML(unmarshal func(interface{}) error) error
 		return err
 	}
 	type plain RegistryConfig
-	if err := unmarshal((*plain)(c)); err != nil {
-		return err
-	}
-	return nil
+	return unmarshal((*plain)(c))
 }
 
 // nolint
@@ -114,7 +111,7 @@ func loadRegistries(targetRegistries string, registries map[string]*RegistryConf
 				logger.Errorf("The registry id: %s url is invalid, error: %#v", k, err)
 				panic(err)
 			} else {
-				urls = append(urls, &url)
+				urls = append(urls, url)
 			}
 		}
 	}
diff --git a/config/remote_config.go b/config/remote_config.go
index 55380dd5a05b47b5b4677b32daf73b37376673d0..61d4dce0cdc104d6f9710d1a1f3caa540edec076 100644
--- a/config/remote_config.go
+++ b/config/remote_config.go
@@ -40,7 +40,7 @@ type RemoteConfig struct {
 	TimeoutStr string            `default:"5s" yaml:"timeout" json:"timeout,omitempty"`
 	Username   string            `yaml:"username" json:"username,omitempty" property:"username"`
 	Password   string            `yaml:"password" json:"password,omitempty"  property:"password"`
-	Params     map[string]string `yaml:"params" json:"address,omitempty"`
+	Params     map[string]string `yaml:"params" json:"params,omitempty"`
 }
 
 // Timeout return timeout duration.
@@ -63,9 +63,9 @@ func (rc *RemoteConfig) GetParam(key string, def string) string {
 	return param
 }
 
-func (rc *RemoteConfig) toURL() (common.URL, error) {
+func (rc *RemoteConfig) toURL() (*common.URL, error) {
 	if len(rc.Protocol) == 0 {
-		return common.URL{}, perrors.Errorf("Must provide protocol in RemoteConfig.")
+		return nil, perrors.Errorf("Must provide protocol in RemoteConfig.")
 	}
 	return common.NewURL(rc.Address,
 		common.WithUsername(rc.Username),
diff --git a/config/router_config.go b/config/router_config.go
index ed42577ed3cce2e5a1ab0da290f0d5450553d8fb..ea19b46800d747de763bbb1ee679f8398e440f3d 100644
--- a/config/router_config.go
+++ b/config/router_config.go
@@ -66,7 +66,7 @@ func initRouterConfig(content []byte, factories map[string]router.FilePriorityRo
 		r, e := factory.NewFileRouter(content)
 		if e == nil {
 			url := r.URL()
-			routerURLSet.Add(&url)
+			routerURLSet.Add(url)
 			return nil
 		}
 		logger.Warnf("router config type %s create fail {%v}\n", k, e)
diff --git a/config/service_config.go b/config/service_config.go
index 54383e4791bf0d0749aea08d7ba9a613e4cfe70b..087f537b5937821fa39c042f21feb66792e22502 100644
--- a/config/service_config.go
+++ b/config/service_config.go
@@ -59,6 +59,7 @@ type ServiceConfig struct {
 	Methods                     []*MethodConfig   `yaml:"methods"  json:"methods,omitempty" property:"methods"`
 	Warmup                      string            `yaml:"warmup"  json:"warmup,omitempty"  property:"warmup"`
 	Retries                     string            `yaml:"retries"  json:"retries,omitempty" property:"retries"`
+	Serialization               string            `yaml:"serialization" json:"serialization" property:"serialization"`
 	Params                      map[string]string `yaml:"params"  json:"params,omitempty" property:"params"`
 	Token                       string            `yaml:"token" json:"token,omitempty" property:"token"`
 	AccessLog                   string            `yaml:"accesslog" json:"accesslog,omitempty" property:"accesslog"`
@@ -72,10 +73,12 @@ type ServiceConfig struct {
 	Auth                        string            `yaml:"auth" json:"auth,omitempty" property:"auth"`
 	ParamSign                   string            `yaml:"param.sign" json:"param.sign,omitempty" property:"param.sign"`
 	Tag                         string            `yaml:"tag" json:"tag,omitempty" property:"tag"`
+	GrpcMaxMessageSize          int               `default:"4" yaml:"max_message_size" json:"max_message_size,omitempty"`
 
 	Protocols     map[string]*ProtocolConfig
 	unexported    *atomic.Bool
 	exported      *atomic.Bool
+	export        bool // a flag to control whether the current service should export or not
 	rpcService    common.RPCService
 	cacheMutex    sync.Mutex
 	cacheProtocol protocol.Protocol
@@ -100,6 +103,7 @@ func (c *ServiceConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
 	}
 	c.exported = atomic.NewBool(false)
 	c.unexported = atomic.NewBool(false)
+	c.export = true
 	return nil
 }
 
@@ -110,6 +114,7 @@ func NewServiceConfig(id string, context context.Context) *ServiceConfig {
 		id:         id,
 		unexported: atomic.NewBool(false),
 		exported:   atomic.NewBool(false),
+		export:     true,
 	}
 }
 
@@ -133,7 +138,7 @@ func getRandomPort(protocolConfigs []*ProtocolConfig) *list.List {
 
 		tcp, err := gxnet.ListenOnTCPRandomPort(proto.Ip)
 		if err != nil {
-			panic(perrors.New(fmt.Sprintf("Get tcp port error,err is {%v}", err)))
+			panic(perrors.New(fmt.Sprintf("Get tcp port error, err is {%v}", err)))
 		}
 		defer tcp.Close()
 		ports.PushBack(strings.Split(tcp.Addr().String(), ":")[1])
@@ -145,14 +150,14 @@ func getRandomPort(protocolConfigs []*ProtocolConfig) *list.List {
 func (c *ServiceConfig) Export() error {
 	// TODO: config center start here
 
-	// TODO:delay export
+	// TODO: delay export
 	if c.unexported != nil && c.unexported.Load() {
-		err := perrors.Errorf("The service %v has already unexported! ", c.InterfaceName)
+		err := perrors.Errorf("The service %v has already unexported!", c.InterfaceName)
 		logger.Errorf(err.Error())
 		return err
 	}
 	if c.unexported != nil && c.exported.Load() {
-		logger.Warnf("The service %v has already exported! ", c.InterfaceName)
+		logger.Warnf("The service %v has already exported!", c.InterfaceName)
 		return nil
 	}
 
@@ -160,29 +165,29 @@ func (c *ServiceConfig) Export() error {
 	urlMap := c.getUrlMap()
 	protocolConfigs := loadProtocol(c.Protocol, c.Protocols)
 	if len(protocolConfigs) == 0 {
-		logger.Warnf("The service %v's '%v' protocols don't has right protocolConfigs ", c.InterfaceName, c.Protocol)
+		logger.Warnf("The service %v's '%v' protocols don't has right protocolConfigs", c.InterfaceName, c.Protocol)
 		return nil
 	}
 
 	ports := getRandomPort(protocolConfigs)
 	nextPort := ports.Front()
+	proxyFactory := extension.GetProxyFactory(providerConfig.ProxyFactory)
 	for _, proto := range protocolConfigs {
 		// registry the service reflect
-		methods, err := common.ServiceMap.Register(c.InterfaceName, proto.Name, c.rpcService)
+		methods, err := common.ServiceMap.Register(c.InterfaceName, proto.Name, c.Group, c.Version, c.rpcService)
 		if err != nil {
-			formatErr := perrors.Errorf("The service %v  export the protocol %v error! Error message is %v .", c.InterfaceName, proto.Name, err.Error())
+			formatErr := perrors.Errorf("The service %v export the protocol %v error! Error message is %v.", c.InterfaceName, proto.Name, err.Error())
 			logger.Errorf(formatErr.Error())
 			return formatErr
 		}
 
 		port := proto.Port
-
 		if len(proto.Port) == 0 {
 			port = nextPort.Value.(string)
 			nextPort = nextPort.Next()
 		}
 		ivkURL := common.NewURLWithOptions(
-			common.WithPath(c.id),
+			common.WithPath(c.InterfaceName),
 			common.WithProtocol(proto.Name),
 			common.WithIp(proto.Ip),
 			common.WithPort(port),
@@ -196,33 +201,38 @@ func (c *ServiceConfig) Export() error {
 			ivkURL.AddParam(constant.Tagkey, c.Tag)
 		}
 
-		var exporter protocol.Exporter
+		// post process the URL to be exported
+		c.postProcessConfig(ivkURL)
+		// config post processor may set "export" to false
+		if !ivkURL.GetParamBool(constant.EXPORT_KEY, true) {
+			return nil
+		}
 
 		if len(regUrls) > 0 {
+			c.cacheMutex.Lock()
+			if c.cacheProtocol == nil {
+				logger.Infof(fmt.Sprintf("First load the registry protocol, url is {%v}!", ivkURL))
+				c.cacheProtocol = extension.GetProtocol("registry")
+			}
+			c.cacheMutex.Unlock()
+
 			for _, regUrl := range regUrls {
 				regUrl.SubURL = ivkURL
-
-				c.cacheMutex.Lock()
-				if c.cacheProtocol == nil {
-					logger.Infof(fmt.Sprintf("First load the registry protocol , url is {%v}!", ivkURL))
-					c.cacheProtocol = extension.GetProtocol("registry")
-				}
-				c.cacheMutex.Unlock()
-
-				invoker := extension.GetProxyFactory(providerConfig.ProxyFactory).GetInvoker(*regUrl)
-				exporter = c.cacheProtocol.Export(invoker)
+				invoker := proxyFactory.GetInvoker(regUrl)
+				exporter := c.cacheProtocol.Export(invoker)
 				if exporter == nil {
-					panic(perrors.New(fmt.Sprintf("Registry protocol new exporter error,registry is {%v},url is {%v}", regUrl, ivkURL)))
+					return perrors.New(fmt.Sprintf("Registry protocol new exporter error, registry is {%v}, url is {%v}", regUrl, ivkURL))
 				}
+				c.exporters = append(c.exporters, exporter)
 			}
 		} else {
-			invoker := extension.GetProxyFactory(providerConfig.ProxyFactory).GetInvoker(*ivkURL)
-			exporter = extension.GetProtocol(protocolwrapper.FILTER).Export(invoker)
+			invoker := proxyFactory.GetInvoker(ivkURL)
+			exporter := extension.GetProtocol(protocolwrapper.FILTER).Export(invoker)
 			if exporter == nil {
-				panic(perrors.New(fmt.Sprintf("Filter protocol without registry new exporter error,url is {%v}", ivkURL)))
+				return perrors.New(fmt.Sprintf("Filter protocol without registry new exporter error, url is {%v}", ivkURL))
 			}
+			c.exporters = append(c.exporters, exporter)
 		}
-		c.exporters = append(c.exporters, exporter)
 	}
 	c.exported.Store(true)
 	return nil
@@ -272,7 +282,9 @@ func (c *ServiceConfig) getUrlMap() url.Values {
 	urlMap.Set(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER))
 	urlMap.Set(constant.RELEASE_KEY, "dubbo-golang-"+constant.Version)
 	urlMap.Set(constant.SIDE_KEY, (common.RoleType(common.PROVIDER)).Role())
-
+	urlMap.Set(constant.MESSAGE_SIZE_KEY, strconv.Itoa(c.GrpcMaxMessageSize))
+	// todo: move
+	urlMap.Set(constant.SERIALIZATION_KEY, c.Serialization)
 	// application info
 	urlMap.Set(constant.APPLICATION_KEY, providerConfig.ApplicationConfig.Name)
 	urlMap.Set(constant.ORGANIZATION_KEY, providerConfig.ApplicationConfig.Organization)
@@ -302,6 +314,9 @@ func (c *ServiceConfig) getUrlMap() url.Values {
 	urlMap.Set(constant.SERVICE_AUTH_KEY, c.Auth)
 	urlMap.Set(constant.PARAMTER_SIGNATURE_ENABLE_KEY, c.ParamSign)
 
+	// whether to export or not
+	urlMap.Set(constant.EXPORT_KEY, strconv.FormatBool(c.export))
+
 	for _, v := range c.Methods {
 		prefix := "methods." + v.Name + "."
 		urlMap.Set(prefix+constant.LOADBALANCE_KEY, v.LoadBalance)
@@ -314,7 +329,6 @@ func (c *ServiceConfig) getUrlMap() url.Values {
 
 		urlMap.Set(constant.EXECUTE_LIMIT_KEY, v.ExecuteLimit)
 		urlMap.Set(constant.EXECUTE_REJECTED_EXECUTION_HANDLER_KEY, v.ExecuteLimitRejectedHandler)
-
 	}
 
 	return urlMap
@@ -325,10 +339,16 @@ func (c *ServiceConfig) GetExportedUrls() []*common.URL {
 	if c.exported.Load() {
 		var urls []*common.URL
 		for _, exporter := range c.exporters {
-			url := exporter.GetInvoker().GetUrl()
-			urls = append(urls, &url)
+			urls = append(urls, exporter.GetInvoker().GetUrl())
 		}
 		return urls
 	}
 	return nil
 }
+
+// postProcessConfig asks registered ConfigPostProcessor to post-process the current ServiceConfig.
+func (c *ServiceConfig) postProcessConfig(url *common.URL) {
+	for _, p := range extension.GetConfigPostProcessors() {
+		p.PostProcessServiceConfig(url)
+	}
+}
diff --git a/config/service_config_test.go b/config/service_config_test.go
index 4d4122ee7057043af47aa0400ca8c5b5e9a20cd0..61c8864b6ab7eb146a92e76187cd6363bbdb8dc8 100644
--- a/config/service_config_test.go
+++ b/config/service_config_test.go
@@ -18,11 +18,11 @@
 package config
 
 import (
+	"github.com/apache/dubbo-go/common"
 	"testing"
 )
 
 import (
-	gxnet "github.com/dubbogo/gost/net"
 	"github.com/stretchr/testify/assert"
 	"go.uber.org/atomic"
 )
@@ -184,7 +184,7 @@ func TestExport(t *testing.T) {
 func TestGetRandomPort(t *testing.T) {
 	protocolConfigs := make([]*ProtocolConfig, 0, 3)
 
-	ip, err := gxnet.GetLocalIP()
+	ip := common.GetLocalIp()
 	protocolConfigs = append(protocolConfigs, &ProtocolConfig{
 		Ip: ip,
 	})
@@ -194,7 +194,7 @@ func TestGetRandomPort(t *testing.T) {
 	protocolConfigs = append(protocolConfigs, &ProtocolConfig{
 		Ip: ip,
 	})
-	assert.NoError(t, err)
+	//assert.NoError(t, err)
 	ports := getRandomPort(protocolConfigs)
 
 	assert.Equal(t, ports.Len(), len(protocolConfigs))
diff --git a/config_center/apollo/impl.go b/config_center/apollo/impl.go
index 8030a2c800c67d47a27e7aaa5d6f1bb39a83cdc9..c69fc2f66b23cd877b447fc78084fb9197c253aa 100644
--- a/config_center/apollo/impl.go
+++ b/config_center/apollo/impl.go
@@ -45,6 +45,7 @@ const (
 )
 
 type apolloConfiguration struct {
+	cc.BaseDynamicConfiguration
 	url *common.URL
 
 	listeners sync.Map
@@ -108,11 +109,11 @@ func getNamespaceName(namespace string, configFileFormat agolloConstant.ConfigFi
 }
 
 func (c *apolloConfiguration) GetInternalProperty(key string, opts ...cc.Option) (string, error) {
-	config := agollo.GetConfig(c.appConf.NamespaceName)
-	if config == nil {
+	newConfig := agollo.GetConfig(c.appConf.NamespaceName)
+	if newConfig == nil {
 		return "", perrors.New(fmt.Sprintf("nothing in namespace:%s ", key))
 	}
-	return config.GetStringValue(key, ""), nil
+	return newConfig.GetStringValue(key, ""), nil
 }
 
 func (c *apolloConfiguration) GetRule(key string, opts ...cc.Option) (string, error) {
@@ -134,11 +135,11 @@ func (c *apolloConfiguration) GetProperties(key string, opts ...cc.Option) (stri
 	 * 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 {
+	tmpConfig := agollo.GetConfig(key)
+	if tmpConfig == nil {
 		return "", perrors.New(fmt.Sprintf("nothing in namespace:%s ", key))
 	}
-	return config.GetContent(), nil
+	return tmpConfig.GetContent(), nil
 }
 
 func (c *apolloConfiguration) getAddressWithProtocolPrefix(url *common.URL) string {
diff --git a/config_center/apollo/impl_test.go b/config_center/apollo/impl_test.go
index 50c4e689de4542e1c595d0778e2dce69b1e7dda9..4720775c285f84bd4fe2146240feea87c8a20817 100644
--- a/config_center/apollo/impl_test.go
+++ b/config_center/apollo/impl_test.go
@@ -197,7 +197,7 @@ func initMockApollo(t *testing.T) *apolloConfiguration {
 	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)
+	configuration, err := newApolloConfiguration(url)
 	assert.NoError(t, err)
 	return configuration
 }
diff --git a/config_center/base_dynamic_configuration.go b/config_center/base_dynamic_configuration.go
new file mode 100644
index 0000000000000000000000000000000000000000..3d6757852ad83d54338b721d0cb617772f40b6b7
--- /dev/null
+++ b/config_center/base_dynamic_configuration.go
@@ -0,0 +1,27 @@
+/*
+ * 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_center
+
+// BaseDynamicConfiguration will default implementation DynamicConfiguration some method
+type BaseDynamicConfiguration struct {
+}
+
+// RemoveConfig
+func (bdc *BaseDynamicConfiguration) RemoveConfig(string, string) error {
+	return nil
+}
diff --git a/config_center/configurator/override.go b/config_center/configurator/override.go
index 294a60ebb2e4e18cfc47cd90aedeaa615b5626d2..7f5abb6175056b44a5d9c6a9fa527ad6e9ac6791 100644
--- a/config_center/configurator/override.go
+++ b/config_center/configurator/override.go
@@ -23,7 +23,6 @@ import (
 
 import (
 	gxset "github.com/dubbogo/gost/container/set"
-	gxnet "github.com/dubbogo/gost/net"
 )
 
 import (
@@ -61,7 +60,7 @@ func (c *overrideConfigurator) Configure(url *common.URL) {
 		currentSide := url.GetParam(constant.SIDE_KEY, "")
 		configuratorSide := c.configuratorUrl.GetParam(constant.SIDE_KEY, "")
 		if currentSide == configuratorSide && common.DubboRole[common.CONSUMER] == currentSide && c.configuratorUrl.Port == "0" {
-			localIP, _ := gxnet.GetLocalIP()
+			localIP := common.GetLocalIp()
 			c.configureIfMatch(localIP, url)
 		} else if currentSide == configuratorSide && common.DubboRole[common.PROVIDER] == currentSide && c.configuratorUrl.Port == url.Port {
 			c.configureIfMatch(url.Ip, url)
@@ -110,7 +109,7 @@ func (c *overrideConfigurator) configureIfMatchInternal(url *common.URL) {
 func (c *overrideConfigurator) configureIfMatch(host string, url *common.URL) {
 	if constant.ANYHOST_VALUE == c.configuratorUrl.Ip || host == c.configuratorUrl.Ip {
 		providers := c.configuratorUrl.GetParam(constant.OVERRIDE_PROVIDERS_KEY, "")
-		if len(providers) == 0 || strings.Index(providers, url.Location) >= 0 || strings.Index(providers, constant.ANYHOST_VALUE) >= 0 {
+		if len(providers) == 0 || strings.Contains(providers, url.Location) || strings.Contains(providers, constant.ANYHOST_VALUE) {
 			c.configureIfMatchInternal(url)
 		}
 	}
@@ -127,7 +126,7 @@ func (c *overrideConfigurator) configureDeprecated(url *common.URL) {
 		// 1.If it is a consumer ip address, the intention is to control a specific consumer instance, it must takes effect at the consumer side, any provider received this override url should ignore;
 		// 2.If the ip is 0.0.0.0, this override url can be used on consumer, and also can be used on provider
 		if url.GetParam(constant.SIDE_KEY, "") == common.DubboRole[common.CONSUMER] {
-			localIP, _ := gxnet.GetLocalIP()
+			localIP := common.GetLocalIp()
 			c.configureIfMatch(localIP, url)
 		} else {
 			c.configureIfMatch(constant.ANYHOST_VALUE, url)
diff --git a/config_center/configurator/override_test.go b/config_center/configurator/override_test.go
index 8eccb5091272b033cf31b612dfb19bce6514ccce..bb9c36776337c5dde1c5b2dcc9dd59ee82067a02 100644
--- a/config_center/configurator/override_test.go
+++ b/config_center/configurator/override_test.go
@@ -40,36 +40,36 @@ const (
 func TestConfigureVerison2p6(t *testing.T) {
 	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(defaults, &url)
+	configurator := extension.GetConfigurator(defaults, url)
 	assert.Equal(t, override, configurator.GetUrl().Protocol)
 
 	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)
+	configurator.Configure(providerUrl)
 	assert.Equal(t, failfast, providerUrl.GetParam(constant.CLUSTER_KEY, ""))
 }
 
 func TestConfigureVerisonOverrideAddr(t *testing.T) {
 	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(defaults, &url)
+	configurator := extension.GetConfigurator(defaults, url)
 	assert.Equal(t, override, configurator.GetUrl().Protocol)
 
 	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)
+	configurator.Configure(providerUrl)
 	assert.Equal(t, failover, providerUrl.GetParam(constant.CLUSTER_KEY, ""))
 }
 
 func TestConfigureVerison2p6WithIp(t *testing.T) {
 	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(defaults, &url)
+	configurator := extension.GetConfigurator(defaults, url)
 	assert.Equal(t, override, configurator.GetUrl().Protocol)
 
 	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)
+	configurator.Configure(providerUrl)
 	assert.Equal(t, failfast, providerUrl.GetParam(constant.CLUSTER_KEY, ""))
 
 }
@@ -77,11 +77,11 @@ func TestConfigureVerison2p6WithIp(t *testing.T) {
 func TestConfigureVerison2p7(t *testing.T) {
 	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(defaults, &url)
+	configurator := extension.GetConfigurator(defaults, url)
 
 	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)
+	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 540febc9d38e164afcc62538478df140b7d671c7..69f84213e7ecd226331821a07fc9bcfc48cd735c 100644
--- a/config_center/dynamic_configuration.go
+++ b/config_center/dynamic_configuration.go
@@ -58,6 +58,9 @@ type DynamicConfiguration interface {
 	// PublishConfig will publish the config with the (key, group, value) pair
 	PublishConfig(string, string, string) error
 
+	// RemoveConfig will remove the config white the (key, group) pair
+	RemoveConfig(string, string) error
+
 	// GetConfigKeysByGroup will return all keys with the group
 	GetConfigKeysByGroup(group string) (*gxset.HashSet, error)
 }
@@ -86,6 +89,6 @@ func WithTimeout(time time.Duration) Option {
 }
 
 // GetRuleKey The format is '{interfaceName}:[version]:[group]'
-func GetRuleKey(url common.URL) string {
+func GetRuleKey(url *common.URL) string {
 	return url.ColonSeparatedKey()
 }
diff --git a/config_center/file/factory.go b/config_center/file/factory.go
new file mode 100644
index 0000000000000000000000000000000000000000..2dda900e20cb7476b1d8da95e4b2b26fcb9dcefd
--- /dev/null
+++ b/config_center/file/factory.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 file
+
+import (
+	perrors "github.com/pkg/errors"
+)
+
+import (
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/common/constant"
+	"github.com/apache/dubbo-go/common/extension"
+	"github.com/apache/dubbo-go/config_center"
+	"github.com/apache/dubbo-go/config_center/parser"
+)
+
+func init() {
+	extension.SetConfigCenterFactory(constant.FILE_KEY, func() config_center.DynamicConfigurationFactory {
+		return &fileDynamicConfigurationFactory{}
+	})
+}
+
+type fileDynamicConfigurationFactory struct {
+}
+
+// GetDynamicConfiguration Get Configuration with URL
+func (f *fileDynamicConfigurationFactory) GetDynamicConfiguration(url *common.URL) (config_center.DynamicConfiguration,
+	error) {
+	dynamicConfiguration, err := newFileSystemDynamicConfiguration(url)
+	if err != nil {
+		return nil, perrors.WithStack(err)
+	}
+
+	dynamicConfiguration.SetParser(&parser.DefaultConfigurationParser{})
+	return dynamicConfiguration, err
+}
diff --git a/config_center/file/impl.go b/config_center/file/impl.go
new file mode 100644
index 0000000000000000000000000000000000000000..f29a33d5e2519b54a5496f5ea03ff170020bb28c
--- /dev/null
+++ b/config_center/file/impl.go
@@ -0,0 +1,309 @@
+/*
+ * 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 file
+
+import (
+	"bytes"
+	"errors"
+	"io/ioutil"
+	"os"
+	"os/exec"
+	"os/user"
+	"path"
+	"path/filepath"
+	"runtime"
+	"strings"
+)
+
+import (
+	gxset "github.com/dubbogo/gost/container/set"
+	perrors "github.com/pkg/errors"
+)
+
+import (
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/config_center"
+	"github.com/apache/dubbo-go/config_center/parser"
+)
+
+const (
+	PARAM_NAME_PREFIX                 = "dubbo.config-center."
+	CONFIG_CENTER_DIR_PARAM_NAME      = PARAM_NAME_PREFIX + "dir"
+	CONFIG_CENTER_ENCODING_PARAM_NAME = PARAM_NAME_PREFIX + "encoding"
+	DEFAULT_CONFIG_CENTER_ENCODING    = "UTF-8"
+)
+
+// FileSystemDynamicConfiguration
+type FileSystemDynamicConfiguration struct {
+	config_center.BaseDynamicConfiguration
+	url           *common.URL
+	rootPath      string
+	encoding      string
+	cacheListener *CacheListener
+	parser        parser.ConfigurationParser
+}
+
+func newFileSystemDynamicConfiguration(url *common.URL) (*FileSystemDynamicConfiguration, error) {
+	encode := url.GetParam(CONFIG_CENTER_ENCODING_PARAM_NAME, DEFAULT_CONFIG_CENTER_ENCODING)
+
+	root := url.GetParam(CONFIG_CENTER_DIR_PARAM_NAME, "")
+	var c *FileSystemDynamicConfiguration
+	if _, err := os.Stat(root); err != nil {
+		// not exist, use default, /XXX/xx/.dubbo/config-center
+		if rp, err := Home(); err != nil {
+			return nil, perrors.WithStack(err)
+		} else {
+			root = path.Join(rp, ".dubbo", "config-center")
+		}
+	}
+
+	if _, err := os.Stat(root); err != nil {
+		// it must be dir, if not exist, will create
+		if err = createDir(root); err != nil {
+			return nil, perrors.WithStack(err)
+		}
+	}
+
+	c = &FileSystemDynamicConfiguration{
+		url:      url,
+		rootPath: root,
+		encoding: encode,
+	}
+
+	c.cacheListener = NewCacheListener(c.rootPath)
+
+	return c, nil
+}
+
+// RootPath get root path
+func (fsdc *FileSystemDynamicConfiguration) RootPath() string {
+	return fsdc.rootPath
+}
+
+// Parser Get Parser
+func (fsdc *FileSystemDynamicConfiguration) Parser() parser.ConfigurationParser {
+	return fsdc.parser
+}
+
+// SetParser Set Parser
+func (fsdc *FileSystemDynamicConfiguration) SetParser(p parser.ConfigurationParser) {
+	fsdc.parser = p
+}
+
+// AddListener Add listener
+func (fsdc *FileSystemDynamicConfiguration) AddListener(key string, listener config_center.ConfigurationListener,
+	opts ...config_center.Option) {
+	tmpOpts := &config_center.Options{}
+	for _, opt := range opts {
+		opt(tmpOpts)
+	}
+
+	tmpPath := fsdc.GetPath(key, tmpOpts.Group)
+	fsdc.cacheListener.AddListener(tmpPath, listener)
+}
+
+// RemoveListener Remove listener
+func (fsdc *FileSystemDynamicConfiguration) RemoveListener(key string, listener config_center.ConfigurationListener,
+	opts ...config_center.Option) {
+	tmpOpts := &config_center.Options{}
+	for _, opt := range opts {
+		opt(tmpOpts)
+	}
+
+	tmpPath := fsdc.GetPath(key, tmpOpts.Group)
+	fsdc.cacheListener.RemoveListener(tmpPath, listener)
+}
+
+// GetProperties get properties file
+func (fsdc *FileSystemDynamicConfiguration) GetProperties(key string, opts ...config_center.Option) (string, error) {
+	tmpOpts := &config_center.Options{}
+	for _, opt := range opts {
+		opt(tmpOpts)
+	}
+
+	tmpPath := fsdc.GetPath(key, tmpOpts.Group)
+	file, err := ioutil.ReadFile(tmpPath)
+	if err != nil {
+		return "", perrors.WithStack(err)
+	}
+	return string(file), nil
+}
+
+// GetRule get Router rule properties file
+func (fsdc *FileSystemDynamicConfiguration) GetRule(key string, opts ...config_center.Option) (string, error) {
+	return fsdc.GetProperties(key, opts...)
+}
+
+// GetInternalProperty get value by key in Default properties file(dubbo.properties)
+func (fsdc *FileSystemDynamicConfiguration) GetInternalProperty(key string, opts ...config_center.Option) (string,
+	error) {
+	return fsdc.GetProperties(key, opts...)
+}
+
+// PublishConfig will publish the config with the (key, group, value) pair
+func (fsdc *FileSystemDynamicConfiguration) PublishConfig(key string, group string, value string) error {
+	tmpPath := fsdc.GetPath(key, group)
+	return fsdc.write2File(tmpPath, value)
+}
+
+// GetConfigKeysByGroup will return all keys with the group
+func (fsdc *FileSystemDynamicConfiguration) GetConfigKeysByGroup(group string) (*gxset.HashSet, error) {
+	tmpPath := fsdc.GetPath("", group)
+	r := gxset.NewSet()
+
+	fileInfo, _ := ioutil.ReadDir(tmpPath)
+
+	for _, file := range fileInfo {
+		// list file
+		if file.IsDir() {
+			continue
+		}
+
+		r.Add(file.Name())
+	}
+
+	return r, nil
+}
+
+// RemoveConfig will remove the config whit hte (key, group)
+func (fsdc *FileSystemDynamicConfiguration) RemoveConfig(key string, group string) error {
+	tmpPath := fsdc.GetPath(key, group)
+	_, err := fsdc.deleteDelay(tmpPath)
+	return err
+}
+
+// Close close file watcher
+func (fsdc *FileSystemDynamicConfiguration) Close() error {
+	return fsdc.cacheListener.Close()
+}
+
+// GetPath get path
+func (fsdc *FileSystemDynamicConfiguration) GetPath(key string, group string) string {
+	if len(key) == 0 {
+		return path.Join(fsdc.rootPath, group)
+	}
+
+	if len(group) == 0 {
+		group = config_center.DEFAULT_GROUP
+	}
+
+	return path.Join(fsdc.rootPath, group, key)
+}
+
+func (fsdc *FileSystemDynamicConfiguration) deleteDelay(path string) (bool, error) {
+	if path == "" {
+		return false, nil
+	}
+
+	if err := os.RemoveAll(path); err != nil {
+		return false, err
+	}
+
+	return true, nil
+}
+
+func (fsdc *FileSystemDynamicConfiguration) write2File(fp string, value string) error {
+	if err := forceMkdirParent(fp); err != nil {
+		return perrors.WithStack(err)
+	}
+
+	return ioutil.WriteFile(fp, []byte(value), os.ModePerm)
+}
+
+func forceMkdirParent(fp string) error {
+	pd := getParentDirectory(fp)
+
+	return createDir(pd)
+}
+
+func createDir(path string) error {
+	// create dir, chmod is drwxrwxrwx(0777)
+	if err := os.MkdirAll(path, os.ModePerm); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func getParentDirectory(fp string) string {
+	return substr(fp, 0, strings.LastIndex(fp, string(filepath.Separator)))
+}
+
+func substr(s string, pos, length int) string {
+	runes := []rune(s)
+	l := pos + length
+	if l > len(runes) {
+		l = len(runes)
+	}
+	return string(runes[pos:l])
+}
+
+// Home returns the home directory for the executing user.
+//
+// This uses an OS-specific method for discovering the home directory.
+// An error is returned if a home directory cannot be detected.
+func Home() (string, error) {
+	currentUser, err := user.Current()
+	if nil == err {
+		return currentUser.HomeDir, nil
+	}
+
+	// cross compile support
+	if "windows" == runtime.GOOS {
+		return homeWindows()
+	}
+
+	// Unix-like system, so just assume Unix
+	return homeUnix()
+}
+
+func homeUnix() (string, error) {
+	// First prefer the HOME environmental variable
+	if home := os.Getenv("HOME"); home != "" {
+		return home, nil
+	}
+
+	// If that fails, try the shell
+	var stdout bytes.Buffer
+	cmd := exec.Command("sh", "-c", "eval echo ~$USER")
+	cmd.Stdout = &stdout
+	if err := cmd.Run(); err != nil {
+		return "", err
+	}
+
+	result := strings.TrimSpace(stdout.String())
+	if result == "" {
+		return "", errors.New("blank output when reading home directory")
+	}
+
+	return result, nil
+}
+
+func homeWindows() (string, error) {
+	drive := os.Getenv("HOMEDRIVE")
+	homePath := os.Getenv("HOMEPATH")
+	home := drive + homePath
+	if drive == "" || homePath == "" {
+		home = os.Getenv("USERPROFILE")
+	}
+	if home == "" {
+		return "", errors.New("HOMEDRIVE, HOMEPATH, and USERPROFILE are blank")
+	}
+
+	return home, nil
+}
diff --git a/config_center/file/impl_test.go b/config_center/file/impl_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..e912cb7078fe42b514ba510db6b7f871ab998ea9
--- /dev/null
+++ b/config_center/file/impl_test.go
@@ -0,0 +1,156 @@
+/*
+ * 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 file
+
+import (
+	"fmt"
+	"os"
+	"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/config_center"
+)
+
+const (
+	key = "com.dubbo.go"
+)
+
+func initFileData(t *testing.T) (*FileSystemDynamicConfiguration, error) {
+	urlString := "registry://127.0.0.1:2181"
+	regurl, err := common.NewURL(urlString)
+	assert.NoError(t, err)
+	dc, err := extension.GetConfigCenterFactory("file").GetDynamicConfiguration(regurl)
+	assert.NoError(t, err)
+
+	return dc.(*FileSystemDynamicConfiguration), err
+}
+
+func TestPublishAndGetConfig(t *testing.T) {
+	file, err := initFileData(t)
+	assert.NoError(t, err)
+	err = file.PublishConfig(key, "", "A")
+	assert.NoError(t, err)
+
+	prop, err := file.GetProperties(key)
+	assert.NoError(t, err)
+	assert.Equal(t, "A", prop)
+
+	defer destroy(file.rootPath, file)
+}
+
+func TestAddListener(t *testing.T) {
+	file, err := initFileData(t)
+	group := "dubbogo"
+	value := "Test Value"
+	err = file.PublishConfig(key, group, value)
+	assert.NoError(t, err)
+
+	listener := &mockDataListener{}
+	file.AddListener(key, listener, config_center.WithGroup(group))
+
+	value = "Test Value 2"
+	err = file.PublishConfig(key, group, value)
+	assert.NoError(t, err)
+	// remove need wait a moment
+	time.Sleep(time.Second)
+	defer destroy(file.rootPath, file)
+}
+
+func TestRemoveListener(t *testing.T) {
+	file, err := initFileData(t)
+	group := "dubbogo"
+	value := "Test Value"
+	err = file.PublishConfig(key, group, value)
+	assert.NoError(t, err)
+
+	listener := &mockDataListener{}
+	file.AddListener(key, listener, config_center.WithGroup(group))
+
+	value = "Test Value 2"
+	err = file.PublishConfig(key, group, value)
+	assert.NoError(t, err)
+
+	// make sure callback before RemoveListener
+	time.Sleep(time.Second)
+	file.RemoveListener(key, listener, config_center.WithGroup(group))
+	value = "Test Value 3"
+	err = file.PublishConfig(key, group, value)
+	assert.NoError(t, err)
+	// remove need wait a moment
+	time.Sleep(time.Second)
+	defer destroy(file.rootPath, file)
+}
+
+func TestGetConfigKeysByGroup(t *testing.T) {
+	file, err := initFileData(t)
+	group := "dubbogo"
+	value := "Test Value"
+	err = file.PublishConfig(key, group, value)
+	gs, err := file.GetConfigKeysByGroup(group)
+	assert.NoError(t, err)
+	assert.Equal(t, 1, gs.Size())
+	assert.Equal(t, key, gs.Values()[0])
+	// remove need wait a moment
+	time.Sleep(time.Second)
+	defer destroy(file.rootPath, file)
+}
+
+func TestGetConfig(t *testing.T) {
+	file, err := initFileData(t)
+	assert.NoError(t, err)
+	group := "dubbogo"
+	value := "Test Value"
+	err = file.PublishConfig(key, group, value)
+	assert.NoError(t, err)
+	prop, err := file.GetProperties(key, config_center.WithGroup(group))
+	assert.NoError(t, err)
+	assert.Equal(t, value, prop)
+	defer destroy(file.rootPath, file)
+}
+
+func TestPublishConfig(t *testing.T) {
+	file, err := initFileData(t)
+	assert.NoError(t, err)
+	group := "dubbogo"
+	value := "Test Value"
+	err = file.PublishConfig(key, group, value)
+	assert.NoError(t, err)
+	prop, err := file.GetInternalProperty(key, config_center.WithGroup(group))
+	assert.NoError(t, err)
+	assert.Equal(t, value, prop)
+	defer destroy(file.rootPath, file)
+}
+
+func destroy(path string, fdc *FileSystemDynamicConfiguration) {
+	fdc.Close()
+	os.RemoveAll(path)
+}
+
+type mockDataListener struct{}
+
+func (l *mockDataListener) Process(configType *config_center.ConfigChangeEvent) {
+	fmt.Printf("process!!!!! %v", configType)
+}
diff --git a/config_center/file/listener.go b/config_center/file/listener.go
new file mode 100644
index 0000000000000000000000000000000000000000..d569030e5ac6a127a862c4d22d180f674cadce2d
--- /dev/null
+++ b/config_center/file/listener.go
@@ -0,0 +1,162 @@
+/*
+ * 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 file
+
+import (
+	"io/ioutil"
+	"sync"
+)
+
+import (
+	"github.com/fsnotify/fsnotify"
+)
+
+import (
+	"github.com/apache/dubbo-go/common/extension"
+	"github.com/apache/dubbo-go/common/logger"
+	"github.com/apache/dubbo-go/config_center"
+	"github.com/apache/dubbo-go/remoting"
+)
+
+// CacheListener is file watcher
+type CacheListener struct {
+	watch        *fsnotify.Watcher
+	keyListeners sync.Map
+	rootPath     string
+}
+
+// NewCacheListener creates a new CacheListener
+func NewCacheListener(rootPath string) *CacheListener {
+	cl := &CacheListener{rootPath: rootPath}
+	// start watcher
+	watch, err := fsnotify.NewWatcher()
+	if err != nil {
+		logger.Errorf("file : listen config fail, error:%v ", err)
+	}
+	go func() {
+		for {
+			select {
+			case event := <-watch.Events:
+				key := event.Name
+				logger.Debugf("watcher %s, event %v", cl.rootPath, event)
+				if event.Op&fsnotify.Write == fsnotify.Write {
+					if l, ok := cl.keyListeners.Load(key); ok {
+						dataChangeCallback(l.(map[config_center.ConfigurationListener]struct{}), key,
+							remoting.EventTypeUpdate)
+					}
+				}
+				if event.Op&fsnotify.Create == fsnotify.Create {
+					if l, ok := cl.keyListeners.Load(key); ok {
+						dataChangeCallback(l.(map[config_center.ConfigurationListener]struct{}), key,
+							remoting.EventTypeAdd)
+					}
+				}
+				if event.Op&fsnotify.Remove == fsnotify.Remove {
+					if l, ok := cl.keyListeners.Load(key); ok {
+						removeCallback(l.(map[config_center.ConfigurationListener]struct{}), key, remoting.EventTypeDel)
+					}
+				}
+			case err := <-watch.Errors:
+				// err may be nil, ignore
+				if err != nil {
+					logger.Warnf("file : listen watch fail:%+v", err)
+				}
+			}
+		}
+	}()
+	cl.watch = watch
+
+	extension.AddCustomShutdownCallback(func() {
+		cl.watch.Close()
+	})
+
+	return cl
+}
+
+func removeCallback(lmap map[config_center.ConfigurationListener]struct{}, key string, event remoting.EventType) {
+	if len(lmap) == 0 {
+		logger.Warnf("file watch callback but configuration listener is empty, key:%s, event:%v", key, event)
+		return
+	}
+	for l := range lmap {
+		callback(l, key, "", event)
+	}
+}
+
+func dataChangeCallback(lmap map[config_center.ConfigurationListener]struct{}, key string, event remoting.EventType) {
+	if len(lmap) == 0 {
+		logger.Warnf("file watch callback but configuration listener is empty, key:%s, event:%v", key, event)
+		return
+	}
+	c := getFileContent(key)
+	for l := range lmap {
+		callback(l, key, c, event)
+	}
+}
+
+func callback(listener config_center.ConfigurationListener, path, data string, event remoting.EventType) {
+	listener.Process(&config_center.ConfigChangeEvent{Key: path, Value: data, ConfigType: event})
+}
+
+// Close will remove key listener and close watcher
+func (cl *CacheListener) Close() error {
+	cl.keyListeners.Range(func(key, value interface{}) bool {
+		cl.keyListeners.Delete(key)
+		return true
+	})
+	return cl.watch.Close()
+}
+
+// AddListener will add a listener if loaded
+// if you watcher a file or directory not exist, will error with no such file or directory
+func (cl *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 := cl.keyListeners.LoadOrStore(key, map[config_center.ConfigurationListener]struct{}{
+		listener: {}})
+	if loaded {
+		listeners.(map[config_center.ConfigurationListener]struct{})[listener] = struct{}{}
+		cl.keyListeners.Store(key, listeners)
+		return
+	}
+	if err := cl.watch.Add(key); err != nil {
+		logger.Errorf("watcher add path:%s err:%v", key, err)
+	}
+}
+
+// RemoveListener will delete a listener if loaded
+func (cl *CacheListener) RemoveListener(key string, listener config_center.ConfigurationListener) {
+	listeners, loaded := cl.keyListeners.Load(key)
+	if !loaded {
+		return
+	}
+	delete(listeners.(map[config_center.ConfigurationListener]struct{}), listener)
+	if err := cl.watch.Remove(key); err != nil {
+		logger.Errorf("watcher remove path:%s err:%v", key, err)
+	}
+}
+
+func getFileContent(path string) string {
+	c, err := ioutil.ReadFile(path)
+	if err != nil {
+		logger.Errorf("read file path:%s err:%v", path, err)
+		return ""
+	}
+
+	return string(c)
+}
diff --git a/config_center/mock_dynamic_config.go b/config_center/mock_dynamic_config.go
index 8fe0a251239f7bfc6a3f70c3834da1b3af8484ba..9bebd600c6ba9e09f172f9260a920b6572fa694c 100644
--- a/config_center/mock_dynamic_config.go
+++ b/config_center/mock_dynamic_config.go
@@ -98,6 +98,7 @@ func (c *MockDynamicConfiguration) GetConfigKeysByGroup(group string) (*gxset.Ha
 
 // MockDynamicConfiguration uses to parse content and defines listener
 type MockDynamicConfiguration struct {
+	BaseDynamicConfiguration
 	parser   parser.ConfigurationParser
 	content  string
 	listener map[string]ConfigurationListener
diff --git a/config_center/nacos/client.go b/config_center/nacos/client.go
index acfe2609ceb0fc62650b7b494b25084ea4df6946..f3942c023d6bc7b7c7afe09863283cbc315fdb1f 100644
--- a/config_center/nacos/client.go
+++ b/config_center/nacos/client.go
@@ -122,7 +122,7 @@ func ValidateNacosClient(container nacosClientFacade, opts ...option) error {
 	return perrors.WithMessagef(nil, "newNacosClient(address:%+v)", url.PrimitiveURL)
 }
 
-func newNacosClient(name string, nacosAddrs []string, timeout time.Duration, url common.URL) (*NacosClient, error) {
+func newNacosClient(name string, nacosAddrs []string, timeout time.Duration, url *common.URL) (*NacosClient, error) {
 	var (
 		err error
 		n   *NacosClient
@@ -149,8 +149,8 @@ func newNacosClient(name string, nacosAddrs []string, timeout time.Duration, url
 	return n, nil
 }
 
-func initNacosConfigClient(nacosAddrs []string, timeout time.Duration, url common.URL) (config_client.IConfigClient, error) {
-	svrConfList := []nacosconst.ServerConfig{}
+func initNacosConfigClient(nacosAddrs []string, timeout time.Duration, url *common.URL) (config_client.IConfigClient, error) {
+	var svrConfList []nacosconst.ServerConfig
 	for _, nacosAddr := range nacosAddrs {
 		split := strings.Split(nacosAddr, ":")
 		port, err := strconv.ParseUint(split[1], 10, 64)
diff --git a/config_center/nacos/client_test.go b/config_center/nacos/client_test.go
index 01319f362b956a0a15be8d5d4dde2a8b2be57c89..2ce3e375557c69721611799d414b3db31d141fca 100644
--- a/config_center/nacos/client_test.go
+++ b/config_center/nacos/client_test.go
@@ -36,7 +36,7 @@ func TestNewNacosClient(t *testing.T) {
 	nacosURL := strings.ReplaceAll(server.URL, "http", "registry")
 	registryUrl, _ := common.NewURL(nacosURL)
 	c := &nacosDynamicConfiguration{
-		url:  &registryUrl,
+		url:  registryUrl,
 		done: make(chan struct{}),
 	}
 	err := ValidateNacosClient(c, WithNacosName(nacosClientName))
@@ -59,7 +59,7 @@ func TestSetNacosClient(t *testing.T) {
 	nacosURL := "registry://" + server.Listener.Addr().String()
 	registryUrl, _ := common.NewURL(nacosURL)
 	c := &nacosDynamicConfiguration{
-		url:  &registryUrl,
+		url:  registryUrl,
 		done: make(chan struct{}),
 	}
 	var client *NacosClient
@@ -93,7 +93,7 @@ func TestNewNacosClient_connectError(t *testing.T) {
 	registryUrl, err := common.NewURL(nacosURL)
 	assert.NoError(t, err)
 	c := &nacosDynamicConfiguration{
-		url:  &registryUrl,
+		url:  registryUrl,
 		done: make(chan struct{}),
 	}
 	err = ValidateNacosClient(c, WithNacosName(nacosClientName))
diff --git a/config_center/nacos/impl.go b/config_center/nacos/impl.go
index bbf707b93811663d0a259c6704e1008bfa91c5c1..7c67930026369e7e2b6c58fc57d05d3a9edb50cc 100644
--- a/config_center/nacos/impl.go
+++ b/config_center/nacos/impl.go
@@ -47,6 +47,7 @@ const (
 
 // nacosDynamicConfiguration is the implementation of DynamicConfiguration based on nacos
 type nacosDynamicConfiguration struct {
+	config_center.BaseDynamicConfiguration
 	url          *common.URL
 	rootPath     string
 	wg           sync.WaitGroup
@@ -185,8 +186,8 @@ func (n *nacosDynamicConfiguration) GetDone() chan struct{} {
 }
 
 // GetUrl Get Url
-func (n *nacosDynamicConfiguration) GetUrl() common.URL {
-	return *n.url
+func (n *nacosDynamicConfiguration) GetUrl() *common.URL {
+	return n.url
 }
 
 // Destroy Destroy configuration instance
diff --git a/config_center/nacos/impl_test.go b/config_center/nacos/impl_test.go
index 88d200edc927c62967975dff28e19c03743125f0..40efa5f4f288d0eff6d9648481cb2a14bfdb3931 100644
--- a/config_center/nacos/impl_test.go
+++ b/config_center/nacos/impl_test.go
@@ -73,7 +73,7 @@ func initNacosData(t *testing.T) (*nacosDynamicConfiguration, error) {
 	nacosURL := strings.ReplaceAll(server.URL, "http", "registry")
 	regurl, _ := common.NewURL(nacosURL)
 	factory := &nacosDynamicConfigurationFactory{}
-	nacosConfiguration, err := factory.GetDynamicConfiguration(&regurl)
+	nacosConfiguration, err := factory.GetDynamicConfiguration(regurl)
 	assert.NoError(t, err)
 
 	nacosConfiguration.SetParser(&parser.DefaultConfigurationParser{})
@@ -105,7 +105,7 @@ func TestNacosDynamicConfiguration_GetConfigKeysByGroup(t *testing.T) {
 
 	nacosURL := strings.ReplaceAll(ts.URL, "http", "registry")
 	regurl, _ := common.NewURL(nacosURL)
-	nacosConfiguration, err := newNacosDynamicConfiguration(&regurl)
+	nacosConfiguration, err := newNacosDynamicConfiguration(regurl)
 	assert.NoError(t, err)
 
 	nacosConfiguration.SetParser(&parser.DefaultConfigurationParser{})
diff --git a/config_center/parser/configuration_parser.go b/config_center/parser/configuration_parser.go
index f794221f9cf3c689d52b32a57ab51f7908f17297..b104d3ddb6b2e10842b82a0498e039cb7e17ab12 100644
--- a/config_center/parser/configuration_parser.go
+++ b/config_center/parser/configuration_parser.go
@@ -144,14 +144,14 @@ func serviceItemToUrls(item ConfigItem, config ConfiguratorConfig) ([]*common.UR
 				if err != nil {
 					return nil, perrors.WithStack(err)
 				}
-				urls = append(urls, &url)
+				urls = append(urls, url)
 			}
 		} else {
 			url, err := common.NewURL(urlStr)
 			if err != nil {
 				return nil, perrors.WithStack(err)
 			}
-			urls = append(urls, &url)
+			urls = append(urls, url)
 		}
 	}
 	return urls, nil
@@ -192,7 +192,7 @@ func appItemToUrls(item ConfigItem, config ConfiguratorConfig) ([]*common.URL, e
 			if err != nil {
 				return nil, perrors.WithStack(err)
 			}
-			urls = append(urls, &url)
+			urls = append(urls, url)
 		}
 	}
 	return urls, nil
diff --git a/config_center/zookeeper/impl.go b/config_center/zookeeper/impl.go
index ef579eb2d11cf5f5bafb132c3e201c12ee7845c0..17812e917f533d28c052c7cda3411de44b9aa7b5 100644
--- a/config_center/zookeeper/impl.go
+++ b/config_center/zookeeper/impl.go
@@ -44,6 +44,7 @@ const (
 )
 
 type zookeeperDynamicConfiguration struct {
+	config_center.BaseDynamicConfiguration
 	url      *common.URL
 	rootPath string
 	wg       sync.WaitGroup
@@ -182,8 +183,8 @@ func (c *zookeeperDynamicConfiguration) Done() chan struct{} {
 	return c.done
 }
 
-func (c *zookeeperDynamicConfiguration) GetUrl() common.URL {
-	return *c.url
+func (c *zookeeperDynamicConfiguration) GetUrl() *common.URL {
+	return c.url
 }
 
 func (c *zookeeperDynamicConfiguration) Destroy() {
diff --git a/config_center/zookeeper/impl_test.go b/config_center/zookeeper/impl_test.go
index ecc3527c486fdd2aa2bc6f4d2f2adab1147e495a..d4a9de41fd62d48ce4528056289839a38285c9a3 100644
--- a/config_center/zookeeper/impl_test.go
+++ b/config_center/zookeeper/impl_test.go
@@ -50,7 +50,7 @@ func initZkData(group string, t *testing.T) (*zk.TestCluster, *zookeeperDynamicC
 	assert.NoError(t, err)
 	regurl.AddParam(constant.REGISTRY_TIMEOUT_KEY, "15s")
 	zkFactory := &zookeeperDynamicConfigurationFactory{}
-	reg, err := zkFactory.GetDynamicConfiguration(&regurl)
+	reg, err := zkFactory.GetDynamicConfiguration(regurl)
 	zreg, ok := reg.(*zookeeperDynamicConfiguration)
 	assert.True(t, ok)
 	assert.NoError(t, err)
diff --git a/contributing.md b/contributing.md
index 9ee2dae32fad6caaf9e19c5e98e8b99b61c26a51..51301511e01c12e388edf574e5c8660ad4a542a1 100644
--- a/contributing.md
+++ b/contributing.md
@@ -38,4 +38,27 @@ The title format of the pull request `MUST` follow the following rules:
 ### 3.3 comment
 
 >- 1 there should be comment for every export func/var.
->- 2 the comment should begin with function name/var name.
\ No newline at end of file
+>- 2 the comment should begin with function name/var name.
+
+### 3.4 import 
+
+We dubbogo import blocks should be splited into 3 blocks.
+
+```Go
+// block 1: the go internal package
+import (
+  "fmt"
+)
+
+// block 2: the third package
+import (
+  "github.com/dubbogo/xxx"
+  
+  "github.com/RoaringBitmap/roaring"
+)
+
+// block 3: the dubbo-go package
+import (
+  "github.com/apache/dubbo-go/common"
+) 
+```
\ No newline at end of file
diff --git a/filter/filter.go b/filter/filter.go
index 804bf3b9df8040c6377648f150c25d421b29b93d..f17c1a231e53e9a3df48072c851cbb26b42a04b4 100644
--- a/filter/filter.go
+++ b/filter/filter.go
@@ -20,6 +20,7 @@ package filter
 import (
 	"context"
 )
+
 import (
 	"github.com/apache/dubbo-go/protocol"
 )
diff --git a/filter/filter_impl/access_log_filter.go b/filter/filter_impl/access_log_filter.go
index 621012c24c0ad12b3bda397148a3ed9c29d080ed..167b5edd804fd5b4b319e86d13815d1b4f698e1c 100644
--- a/filter/filter_impl/access_log_filter.go
+++ b/filter/filter_impl/access_log_filter.go
@@ -105,13 +105,31 @@ func (ef *AccessLogFilter) logIntoChannel(accessLogData AccessLogData) {
 func (ef *AccessLogFilter) buildAccessLogData(_ protocol.Invoker, invocation protocol.Invocation) map[string]string {
 	dataMap := make(map[string]string, 16)
 	attachments := invocation.Attachments()
-	dataMap[constant.INTERFACE_KEY] = attachments[constant.INTERFACE_KEY]
-	dataMap[constant.METHOD_KEY] = invocation.MethodName()
-	dataMap[constant.VERSION_KEY] = attachments[constant.VERSION_KEY]
-	dataMap[constant.GROUP_KEY] = attachments[constant.GROUP_KEY]
-	dataMap[constant.TIMESTAMP_KEY] = time.Now().Format(MessageDateLayout)
-	dataMap[constant.LOCAL_ADDR], _ = attachments[constant.LOCAL_ADDR]
-	dataMap[constant.REMOTE_ADDR], _ = attachments[constant.REMOTE_ADDR]
+	itf := attachments[constant.INTERFACE_KEY]
+	if itf == nil || len(itf.(string)) == 0 {
+		itf = attachments[constant.PATH_KEY]
+	}
+	if itf != nil {
+		dataMap[constant.INTERFACE_KEY] = itf.(string)
+	}
+	if v, ok := attachments[constant.METHOD_KEY]; ok && v != nil {
+		dataMap[constant.METHOD_KEY] = v.(string)
+	}
+	if v, ok := attachments[constant.VERSION_KEY]; ok && v != nil {
+		dataMap[constant.VERSION_KEY] = v.(string)
+	}
+	if v, ok := attachments[constant.GROUP_KEY]; ok && v != nil {
+		dataMap[constant.GROUP_KEY] = v.(string)
+	}
+	if v, ok := attachments[constant.TIMESTAMP_KEY]; ok && v != nil {
+		dataMap[constant.TIMESTAMP_KEY] = v.(string)
+	}
+	if v, ok := attachments[constant.LOCAL_ADDR]; ok && v != nil {
+		dataMap[constant.LOCAL_ADDR] = v.(string)
+	}
+	if v, ok := attachments[constant.REMOTE_ADDR]; ok && v != nil {
+		dataMap[constant.REMOTE_ADDR] = v.(string)
+	}
 
 	if len(invocation.Arguments()) > 0 {
 		builder := strings.Builder{}
diff --git a/filter/filter_impl/access_log_filter_test.go b/filter/filter_impl/access_log_filter_test.go
index 55c328cc30ae892c603fcc65034e48d2a52403d2..a3a6151aa1b6a933c57543248f3703125fa356d9 100644
--- a/filter/filter_impl/access_log_filter_test.go
+++ b/filter/filter_impl/access_log_filter_test.go
@@ -45,7 +45,7 @@ func TestAccessLogFilter_Invoke_Not_Config(t *testing.T) {
 			"service.filter=echo%2Ctoken%2Caccesslog&timestamp=1569153406&token=934804bf-b007-4174-94eb-96e3e1d60cc7&version=&warmup=100")
 	invoker := protocol.NewBaseInvoker(url)
 
-	attach := make(map[string]string, 10)
+	attach := make(map[string]interface{}, 10)
 	inv := invocation.NewRPCInvocation("MethodName", []interface{}{"OK", "Hello"}, attach)
 
 	accessLogFilter := GetAccessLogFilter()
@@ -64,7 +64,7 @@ func TestAccessLogFilterInvokeDefaultConfig(t *testing.T) {
 			"service.filter=echo%2Ctoken%2Caccesslog&timestamp=1569153406&token=934804bf-b007-4174-94eb-96e3e1d60cc7&version=&warmup=100")
 	invoker := protocol.NewBaseInvoker(url)
 
-	attach := make(map[string]string, 10)
+	attach := make(map[string]interface{}, 10)
 	attach[constant.VERSION_KEY] = "1.0"
 	attach[constant.GROUP_KEY] = "MyGroup"
 	inv := invocation.NewRPCInvocation("MethodName", []interface{}{"OK", "Hello"}, attach)
diff --git a/filter/filter_impl/active_filter_test.go b/filter/filter_impl/active_filter_test.go
index 6b72830e6a1a523b775b9294863ab18f8fe518a2..9837a49c72e28c7a7209f8af6059bdc30c222cc2 100644
--- a/filter/filter_impl/active_filter_test.go
+++ b/filter/filter_impl/active_filter_test.go
@@ -37,7 +37,7 @@ import (
 )
 
 func TestActiveFilterInvoke(t *testing.T) {
-	invoc := invocation.NewRPCInvocation("test", []interface{}{"OK"}, make(map[string]string, 0))
+	invoc := invocation.NewRPCInvocation("test", []interface{}{"OK"}, make(map[string]interface{}, 0))
 	url, _ := common.NewURL("dubbo://192.168.10.10:20000/com.ikurento.user.UserProvider")
 	filter := ActiveFilter{}
 	ctrl := gomock.NewController(t)
@@ -53,7 +53,7 @@ func TestActiveFilterInvoke(t *testing.T) {
 func TestActiveFilterOnResponse(t *testing.T) {
 	c := protocol.CurrentTimeMillis()
 	elapsed := 100
-	invoc := invocation.NewRPCInvocation("test", []interface{}{"OK"}, map[string]string{
+	invoc := invocation.NewRPCInvocation("test", []interface{}{"OK"}, map[string]interface{}{
 		dubboInvokeStartTime: strconv.FormatInt(c-int64(elapsed), 10),
 	})
 	url, _ := common.NewURL("dubbo://192.168.10.10:20000/com.ikurento.user.UserProvider")
diff --git a/filter/filter_impl/auth/consumer_sign.go b/filter/filter_impl/auth/consumer_sign.go
index 945cf3e6e7e728042b5422174162dd5aded50361..823db82bfcb0bb165ef36bb9d141650df1e01432 100644
--- a/filter/filter_impl/auth/consumer_sign.go
+++ b/filter/filter_impl/auth/consumer_sign.go
@@ -42,8 +42,8 @@ func (csf *ConsumerSignFilter) Invoke(ctx context.Context, invoker protocol.Invo
 	logger.Infof("invoking ConsumerSign filter.")
 	url := invoker.GetUrl()
 
-	err := doAuthWork(&url, func(authenticator filter.Authenticator) error {
-		return authenticator.Sign(invocation, &url)
+	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()))
diff --git a/filter/filter_impl/auth/default_authenticator_test.go b/filter/filter_impl/auth/default_authenticator_test.go
index 5b107b5960ff5adc383d52aa5e393d9fc6e71d14..37c7e9d1837a183e72db1134506cc3f89611ac76 100644
--- a/filter/filter_impl/auth/default_authenticator_test.go
+++ b/filter/filter_impl/auth/default_authenticator_test.go
@@ -48,26 +48,26 @@ func TestDefaultAuthenticator_Authenticate(t *testing.T) {
 	}{"YUYU", 1}}
 	inv := invocation.NewRPCInvocation("test", parmas, nil)
 	requestTime := strconv.Itoa(int(time.Now().Unix() * 1000))
-	signature, _ := getSignature(&testurl, inv, secret, requestTime)
+	signature, _ := getSignature(testurl, inv, secret, requestTime)
 
 	var authenticator = &DefaultAuthenticator{}
 
-	invcation := invocation.NewRPCInvocation("test", parmas, map[string]string{
+	invcation := invocation.NewRPCInvocation("test", parmas, map[string]interface{}{
 		constant.REQUEST_SIGNATURE_KEY: signature,
 		constant.CONSUMER:              "test",
 		constant.REQUEST_TIMESTAMP_KEY: requestTime,
 		constant.AK_KEY:                access,
 	})
-	err := authenticator.Authenticate(invcation, &testurl)
+	err := authenticator.Authenticate(invcation, testurl)
 	assert.Nil(t, err)
 	// modify the params
-	invcation = invocation.NewRPCInvocation("test", parmas[:1], map[string]string{
+	invcation = invocation.NewRPCInvocation("test", parmas[:1], map[string]interface{}{
 		constant.REQUEST_SIGNATURE_KEY: signature,
 		constant.CONSUMER:              "test",
 		constant.REQUEST_TIMESTAMP_KEY: requestTime,
 		constant.AK_KEY:                access,
 	})
-	err = authenticator.Authenticate(invcation, &testurl)
+	err = authenticator.Authenticate(invcation, testurl)
 	assert.NotNil(t, err)
 
 }
@@ -79,7 +79,7 @@ func TestDefaultAuthenticator_Sign(t *testing.T) {
 	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)
+	_ = 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, ""), "")
@@ -119,12 +119,12 @@ func Test_getAccessKeyPairFailed(t *testing.T) {
 func Test_getSignatureWithinParams(t *testing.T) {
 	testurl, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=gg&version=2.6.0")
 	testurl.SetParam(constant.PARAMTER_SIGNATURE_ENABLE_KEY, "true")
-	inv := invocation.NewRPCInvocation("test", []interface{}{"OK"}, map[string]string{
+	inv := invocation.NewRPCInvocation("test", []interface{}{"OK"}, map[string]interface{}{
 		"": "",
 	})
 	secret := "dubbo"
 	current := strconv.Itoa(int(time.Now().Unix() * 1000))
-	signature, _ := getSignature(&testurl, inv, secret, current)
+	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)
@@ -138,7 +138,7 @@ func Test_getSignature(t *testing.T) {
 	inv := invocation.NewRPCInvocation("test", []interface{}{"OK"}, nil)
 	secret := "dubbo"
 	current := strconv.Itoa(int(time.Now().Unix() * 1000))
-	signature, _ := getSignature(&testurl, inv, secret, current)
+	signature, _ := getSignature(testurl, inv, secret, current)
 	requestString := fmt.Sprintf(constant.SIGNATURE_STRING_FORMAT,
 		testurl.ColonSeparatedKey(), inv.MethodName(), secret, current)
 	s := Sign(requestString, secret)
diff --git a/filter/filter_impl/auth/provider_auth.go b/filter/filter_impl/auth/provider_auth.go
index d5f5db300d4e7c94978d5d52e32f741f7d27bb48..774fdb2fb8f69bc33fcbee36faae28f8f6e6d0cc 100644
--- a/filter/filter_impl/auth/provider_auth.go
+++ b/filter/filter_impl/auth/provider_auth.go
@@ -42,8 +42,8 @@ func (paf *ProviderAuthFilter) Invoke(ctx context.Context, invoker protocol.Invo
 	logger.Infof("invoking providerAuth filter.")
 	url := invoker.GetUrl()
 
-	err := doAuthWork(&url, func(authenticator filter.Authenticator) error {
-		return authenticator.Authenticate(invocation, &url)
+	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())
diff --git a/filter/filter_impl/auth/provider_auth_test.go b/filter/filter_impl/auth/provider_auth_test.go
index 626782ae8390f046f441c1f162700a883e6f22d0..dc130b59851ddb02572103b2e29361f9fc8f1c59 100644
--- a/filter/filter_impl/auth/provider_auth_test.go
+++ b/filter/filter_impl/auth/provider_auth_test.go
@@ -52,9 +52,9 @@ func TestProviderAuthFilter_Invoke(t *testing.T) {
 	}
 	inv := invocation.NewRPCInvocation("test", parmas, nil)
 	requestTime := strconv.Itoa(int(time.Now().Unix() * 1000))
-	signature, _ := getSignature(&url, inv, secret, requestTime)
+	signature, _ := getSignature(url, inv, secret, requestTime)
 
-	inv = invocation.NewRPCInvocation("test", []interface{}{"OK"}, map[string]string{
+	inv = invocation.NewRPCInvocation("test", []interface{}{"OK"}, map[string]interface{}{
 		constant.REQUEST_SIGNATURE_KEY: signature,
 		constant.CONSUMER:              "test",
 		constant.REQUEST_TIMESTAMP_KEY: requestTime,
diff --git a/filter/filter_impl/echo_filter_test.go b/filter/filter_impl/echo_filter_test.go
index b821a1a30cb8af7e957fac45beecd46067627f91..f2ec81a3e63a81c66ef08d482f111e8bae5774b8 100644
--- a/filter/filter_impl/echo_filter_test.go
+++ b/filter/filter_impl/echo_filter_test.go
@@ -34,10 +34,10 @@ import (
 
 func TestEchoFilterInvoke(t *testing.T) {
 	filter := GetFilter()
-	result := filter.Invoke(context.Background(), 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(context.Background(), 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/filter_impl/execute_limit_filter_test.go b/filter/filter_impl/execute_limit_filter_test.go
index d36d6edef1ec52c24a9ccd64233b4620b4f10bc7..2aebcaa8fa62278da3092d6359d2a01571be37d0 100644
--- a/filter/filter_impl/execute_limit_filter_test.go
+++ b/filter/filter_impl/execute_limit_filter_test.go
@@ -36,7 +36,7 @@ import (
 
 func TestExecuteLimitFilterInvokeIgnored(t *testing.T) {
 	methodName := "hello"
-	invoc := invocation.NewRPCInvocation(methodName, []interface{}{"OK"}, make(map[string]string, 0))
+	invoc := invocation.NewRPCInvocation(methodName, []interface{}{"OK"}, make(map[string]interface{}, 0))
 
 	invokeUrl := common.NewURLWithOptions(
 		common.WithParams(url.Values{}),
@@ -44,14 +44,14 @@ func TestExecuteLimitFilterInvokeIgnored(t *testing.T) {
 
 	limitFilter := GetExecuteLimitFilter()
 
-	result := limitFilter.Invoke(context.Background(), protocol.NewBaseInvoker(*invokeUrl), invoc)
+	result := limitFilter.Invoke(context.Background(), protocol.NewBaseInvoker(invokeUrl), invoc)
 	assert.NotNil(t, result)
 	assert.Nil(t, result.Error())
 }
 
 func TestExecuteLimitFilterInvokeConfigureError(t *testing.T) {
 	methodName := "hello1"
-	invoc := invocation.NewRPCInvocation(methodName, []interface{}{"OK"}, make(map[string]string, 0))
+	invoc := invocation.NewRPCInvocation(methodName, []interface{}{"OK"}, make(map[string]interface{}, 0))
 
 	invokeUrl := common.NewURLWithOptions(
 		common.WithParams(url.Values{}),
@@ -61,14 +61,14 @@ func TestExecuteLimitFilterInvokeConfigureError(t *testing.T) {
 
 	limitFilter := GetExecuteLimitFilter()
 
-	result := limitFilter.Invoke(context.Background(), protocol.NewBaseInvoker(*invokeUrl), invoc)
+	result := limitFilter.Invoke(context.Background(), protocol.NewBaseInvoker(invokeUrl), invoc)
 	assert.NotNil(t, result)
 	assert.Nil(t, result.Error())
 }
 
 func TestExecuteLimitFilterInvoke(t *testing.T) {
 	methodName := "hello1"
-	invoc := invocation.NewRPCInvocation(methodName, []interface{}{"OK"}, make(map[string]string, 0))
+	invoc := invocation.NewRPCInvocation(methodName, []interface{}{"OK"}, make(map[string]interface{}, 0))
 
 	invokeUrl := common.NewURLWithOptions(
 		common.WithParams(url.Values{}),
@@ -78,7 +78,7 @@ func TestExecuteLimitFilterInvoke(t *testing.T) {
 
 	limitFilter := GetExecuteLimitFilter()
 
-	result := limitFilter.Invoke(context.Background(), 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/filter_impl/generic_filter.go b/filter/filter_impl/generic_filter.go
index d385054ed98518177aea5e574e034cb35c72e398..36b4b13186361d85beea0625eeda1ad58c3f2dca 100644
--- a/filter/filter_impl/generic_filter.go
+++ b/filter/filter_impl/generic_filter.go
@@ -21,6 +21,7 @@ import (
 	"context"
 	"reflect"
 	"strings"
+	"time"
 )
 
 import (
@@ -93,13 +94,21 @@ func struct2MapAll(obj interface{}) interface{} {
 	if t.Kind() == reflect.Struct {
 		result := make(map[string]interface{}, t.NumField())
 		for i := 0; i < t.NumField(); i++ {
-			if v.Field(i).Kind() == reflect.Struct || v.Field(i).Kind() == reflect.Slice || v.Field(i).Kind() == reflect.Map {
-				if v.Field(i).CanInterface() {
-					setInMap(result, t.Field(i), struct2MapAll(v.Field(i).Interface()))
+			field := t.Field(i)
+			value := v.Field(i)
+			kind := value.Kind()
+			if kind == reflect.Struct || kind == reflect.Slice || kind == reflect.Map {
+				if value.CanInterface() {
+					tmp := value.Interface()
+					if _, ok := tmp.(time.Time); ok {
+						setInMap(result, field, tmp)
+						continue
+					}
+					setInMap(result, field, struct2MapAll(tmp))
 				}
 			} else {
-				if v.Field(i).CanInterface() {
-					setInMap(result, t.Field(i), v.Field(i).Interface())
+				if value.CanInterface() {
+					setInMap(result, field, value.Interface())
 				}
 			}
 		}
diff --git a/filter/filter_impl/generic_filter_test.go b/filter/filter_impl/generic_filter_test.go
index e40733209b2e1db972ab576dea54f206a1e888c0..40cf743106821f12425d1abe6dbc82bad0559917 100644
--- a/filter/filter_impl/generic_filter_test.go
+++ b/filter/filter_impl/generic_filter_test.go
@@ -20,6 +20,7 @@ package filter_impl
 import (
 	"reflect"
 	"testing"
+	"time"
 )
 
 import (
@@ -38,6 +39,8 @@ func TestStruct2MapAll(t *testing.T) {
 				Xx   string `m:"xx"`
 			} `m:"xxYy"`
 		} `m:"caCa"`
+		DaDa time.Time
+		EeEe int
 	}
 	testData.AaAa = "1"
 	testData.BaBa = "1"
@@ -45,6 +48,8 @@ func TestStruct2MapAll(t *testing.T) {
 	testData.CaCa.AaAa = "2"
 	testData.CaCa.XxYy.xxXx = "3"
 	testData.CaCa.XxYy.Xx = "3"
+	testData.DaDa = time.Date(2020, 10, 29, 2, 34, 0, 0, time.Local)
+	testData.EeEe = 100
 	m := struct2MapAll(testData).(map[string]interface{})
 	assert.Equal(t, "1", m["aaAa"].(string))
 	assert.Equal(t, "1", m["baBa"].(string))
@@ -53,6 +58,8 @@ func TestStruct2MapAll(t *testing.T) {
 
 	assert.Equal(t, reflect.Map, reflect.TypeOf(m["caCa"]).Kind())
 	assert.Equal(t, reflect.Map, reflect.TypeOf(m["caCa"].(map[string]interface{})["xxYy"]).Kind())
+	assert.Equal(t, "2020-10-29 02:34:00", m["daDa"].(time.Time).Format("2006-01-02 15:04:05"))
+	assert.Equal(t, 100, m["eeEe"].(int))
 }
 
 type testStruct struct {
diff --git a/filter/filter_impl/generic_service_filter.go b/filter/filter_impl/generic_service_filter.go
index 3711e68cce67dacfec074a5e44c080a629bce305..89b009b8d136d34ca0fd14548b3ef5536c7234d8 100644
--- a/filter/filter_impl/generic_service_filter.go
+++ b/filter/filter_impl/generic_service_filter.go
@@ -20,7 +20,6 @@ package filter_impl
 import (
 	"context"
 	"reflect"
-	"strings"
 )
 
 import (
@@ -75,7 +74,7 @@ func (ef *GenericServiceFilter) Invoke(ctx context.Context, invoker protocol.Inv
 	url := invoker.GetUrl()
 	methodName = invocation.Arguments()[0].(string)
 	// get service
-	svc := common.ServiceMap.GetService(url.Protocol, strings.TrimPrefix(url.Path, "/"))
+	svc := common.ServiceMap.GetServiceByServiceKey(url.Protocol, url.ServiceKey())
 	// get method
 	method := svc.Method()[methodName]
 	if method == nil {
diff --git a/filter/filter_impl/generic_service_filter_test.go b/filter/filter_impl/generic_service_filter_test.go
index 67819717cf07383373f905ed9420a42cc0bfddb9..8b2fb549c299bb6c1cf65c4254ba6784caec5655 100644
--- a/filter/filter_impl/generic_service_filter_test.go
+++ b/filter/filter_impl/generic_service_filter_test.go
@@ -96,7 +96,7 @@ func TestGenericServiceFilterInvoke(t *testing.T) {
 			hessian.Object("222")},
 	}
 	s := &TestService{}
-	_, _ = common.ServiceMap.Register("TestService", "testprotocol", s)
+	_, _ = common.ServiceMap.Register("com.test.Path", "testprotocol", "", "", s)
 	rpcInvocation := invocation.NewRPCInvocation(methodName, aurguments, nil)
 	filter := GetGenericServiceFilter()
 	url, _ := common.NewURL("testprotocol://127.0.0.1:20000/com.test.Path")
diff --git a/filter/filter_impl/graceful_shutdown_filter_test.go b/filter/filter_impl/graceful_shutdown_filter_test.go
index 87ac2eac616a20617b7a5e68254a1f47ecb8ac17..447a557ad5f508bf444ccae5dacb8a8559c933ea 100644
--- a/filter/filter_impl/graceful_shutdown_filter_test.go
+++ b/filter/filter_impl/graceful_shutdown_filter_test.go
@@ -39,7 +39,7 @@ import (
 )
 
 func TestGenericFilterInvoke(t *testing.T) {
-	invoc := invocation.NewRPCInvocation("GetUser", []interface{}{"OK"}, make(map[string]string, 0))
+	invoc := invocation.NewRPCInvocation("GetUser", []interface{}{"OK"}, make(map[string]interface{}, 0))
 
 	invokeUrl := common.NewURLWithOptions(
 		common.WithParams(url.Values{}))
@@ -54,7 +54,7 @@ func TestGenericFilterInvoke(t *testing.T) {
 	assert.Equal(t, extension.GetRejectedExecutionHandler(constant.DEFAULT_KEY),
 		shutdownFilter.getRejectHandler())
 
-	result := shutdownFilter.Invoke(context.Background(), protocol.NewBaseInvoker(*invokeUrl), invoc)
+	result := shutdownFilter.Invoke(context.Background(), protocol.NewBaseInvoker(invokeUrl), invoc)
 	assert.NotNil(t, result)
 	assert.Nil(t, result.Error())
 
@@ -65,7 +65,7 @@ func TestGenericFilterInvoke(t *testing.T) {
 	shutdownFilter.shutdownConfig = providerConfig.ShutdownConfig
 
 	assert.True(t, shutdownFilter.rejectNewRequest())
-	result = shutdownFilter.OnResponse(nil, nil, protocol.NewBaseInvoker(*invokeUrl), invoc)
+	result = shutdownFilter.OnResponse(nil, nil, protocol.NewBaseInvoker(invokeUrl), invoc)
 
 	rejectHandler := &common2.OnlyLogRejectedExecutionHandler{}
 	extension.SetRejectedExecutionHandler("mock", func() filter.RejectedExecutionHandler {
diff --git a/filter/filter_impl/hystrix_filter.go b/filter/filter_impl/hystrix_filter.go
index e2275149f1229c87ed4ae3f89dc9ae32d9fe6461..d13e02c06f7b2191e4d01c013a72300e1b095616 100644
--- a/filter/filter_impl/hystrix_filter.go
+++ b/filter/filter_impl/hystrix_filter.go
@@ -53,10 +53,6 @@ var (
 	providerConfigOnce sync.Once
 )
 
-//The filter in the server end of dubbo-go can't get the invoke result for now,
-//this filter ONLY works in CLIENT end (consumer side) temporarily
-//Only after the callService logic is integrated into the filter chain of server end then the filter can be used,
-//which will be done soon
 func init() {
 	extension.SetFilter(HYSTRIX_CONSUMER, GetHystrixFilterConsumer)
 	extension.SetFilter(HYSTRIX_PROVIDER, GetHystrixFilterProvider)
@@ -85,7 +81,47 @@ func NewHystrixFilterError(err error, failByHystrix bool) error {
 	}
 }
 
-// nolint
+/**
+ * HystrixFilter
+ * You should add hystrix related configuration in provider or consumer config or both, according to which side you are to apply HystrixFilter.
+ * For example:
+ * filter_conf:
+ * 	hystrix:
+ * 	 configs:
+ * 	  # =========== Define config here ============
+ * 	  "Default":
+ * 	    timeout : 1000
+ * 	    max_concurrent_requests : 25
+ * 	    sleep_window : 5000
+ * 	    error_percent_threshold : 50
+ * 	    request_volume_threshold: 20
+ * 	  "userp":
+ * 	    timeout: 2000
+ * 	    max_concurrent_requests: 512
+ * 	    sleep_window: 4000
+ * 	    error_percent_threshold: 35
+ * 	    request_volume_threshold: 6
+ * 	  "userp_m":
+ * 	    timeout : 1200
+ * 	    max_concurrent_requests : 512
+ * 	    sleep_window : 6000
+ * 	    error_percent_threshold : 60
+ * 	    request_volume_threshold: 16
+ *      # =========== Define error whitelist which will be ignored by Hystrix counter ============
+ * 	    error_whitelist: [".*exception.*"]
+ *
+ * 	 # =========== Apply default config here ===========
+ * 	 default: "Default"
+ *
+ * 	 services:
+ * 	  "com.ikurento.user.UserProvider":
+ * 	    # =========== Apply service level config ===========
+ * 	    service_config: "userp"
+ * 	    # =========== Apply method level config ===========
+ * 	    methods:
+ * 	      "GetUser": "userp_m"
+ * 	      "GetUser1": "userp_m"
+ */
 type HystrixFilter struct {
 	COrP     bool //true for consumer
 	res      map[string][]*regexp.Regexp
@@ -213,11 +249,11 @@ func getConfig(service string, method string, cOrP bool) CommandConfigWithError
 
 func initHystrixConfigConsumer() error {
 	if config.GetConsumerConfig().FilterConf == nil {
-		return perrors.Errorf("no config for hystrix")
+		return perrors.Errorf("no config for hystrix_consumer")
 	}
 	filterConfig := config.GetConsumerConfig().FilterConf.(map[interface{}]interface{})[HYSTRIX]
 	if filterConfig == nil {
-		return perrors.Errorf("no config for hystrix")
+		return perrors.Errorf("no config for hystrix_consumer")
 	}
 	hystrixConfByte, err := yaml.Marshal(filterConfig)
 	if err != nil {
@@ -232,11 +268,11 @@ func initHystrixConfigConsumer() error {
 
 func initHystrixConfigProvider() error {
 	if config.GetProviderConfig().FilterConf == nil {
-		return perrors.Errorf("no config for hystrix")
+		return perrors.Errorf("no config for hystrix_provider")
 	}
-	filterConfig := config.GetConsumerConfig().FilterConf.(map[interface{}]interface{})[HYSTRIX]
+	filterConfig := config.GetProviderConfig().FilterConf.(map[interface{}]interface{})[HYSTRIX]
 	if filterConfig == nil {
-		return perrors.Errorf("no config for hystrix")
+		return perrors.Errorf("no config for hystrix_provider")
 	}
 	hystrixConfByte, err := yaml.Marshal(filterConfig)
 	if err != nil {
diff --git a/filter/filter_impl/hystrix_filter_test.go b/filter/filter_impl/hystrix_filter_test.go
index eebbae55565aa7072846d368403605002df0c61b..4973ce7f70353a70befec74f1bab9333b6b58fd1 100644
--- a/filter/filter_impl/hystrix_filter_test.go
+++ b/filter/filter_impl/hystrix_filter_test.go
@@ -18,6 +18,7 @@ package filter_impl
 
 import (
 	"context"
+	"fmt"
 	"regexp"
 	"testing"
 )
@@ -29,6 +30,8 @@ import (
 )
 
 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"
 )
@@ -147,7 +150,11 @@ func (iv *testMockFailInvoker) Invoke(_ context.Context, _ protocol.Invocation)
 
 func TestHystrixFilterInvokeSuccess(t *testing.T) {
 	hf := &HystrixFilter{}
-	result := hf.Invoke(context.Background(), &testMockSuccessInvoker{}, &invocation.RPCInvocation{})
+	testUrl, err := common.NewURL(
+		fmt.Sprintf("dubbo://%s:%d/com.ikurento.user.UserProvider", constant.LOCAL_HOST_VALUE, constant.DEFAULT_PORT))
+	assert.NoError(t, err)
+	testInvoker := testMockSuccessInvoker{*protocol.NewBaseInvoker(testUrl)}
+	result := hf.Invoke(context.Background(), &testInvoker, &invocation.RPCInvocation{})
 	assert.NotNil(t, result)
 	assert.NoError(t, result.Error())
 	assert.NotNil(t, result.Result())
@@ -155,7 +162,11 @@ func TestHystrixFilterInvokeSuccess(t *testing.T) {
 
 func TestHystrixFilterInvokeFail(t *testing.T) {
 	hf := &HystrixFilter{}
-	result := hf.Invoke(context.Background(), &testMockFailInvoker{}, &invocation.RPCInvocation{})
+	testUrl, err := common.NewURL(
+		fmt.Sprintf("dubbo://%s:%d/com.ikurento.user.UserProvider", constant.LOCAL_HOST_VALUE, constant.DEFAULT_PORT))
+	assert.NoError(t, err)
+	testInvoker := testMockFailInvoker{*protocol.NewBaseInvoker(testUrl)}
+	result := hf.Invoke(context.Background(), &testInvoker, &invocation.RPCInvocation{})
 	assert.NotNil(t, result)
 	assert.Error(t, result.Error())
 }
@@ -167,7 +178,11 @@ func TestHystricFilterInvokeCircuitBreak(t *testing.T) {
 	resChan := make(chan protocol.Result, 50)
 	for i := 0; i < 50; i++ {
 		go func() {
-			result := hf.Invoke(context.Background(), &testMockFailInvoker{}, &invocation.RPCInvocation{})
+			testUrl, err := common.NewURL(
+				fmt.Sprintf("dubbo://%s:%d/com.ikurento.user.UserProvider", constant.LOCAL_HOST_VALUE, constant.DEFAULT_PORT))
+			assert.NoError(t, err)
+			testInvoker := testMockSuccessInvoker{*protocol.NewBaseInvoker(testUrl)}
+			result := hf.Invoke(context.Background(), &testInvoker, &invocation.RPCInvocation{})
 			resChan <- result
 		}()
 	}
@@ -192,7 +207,11 @@ func TestHystricFilterInvokeCircuitBreakOmitException(t *testing.T) {
 	resChan := make(chan protocol.Result, 50)
 	for i := 0; i < 50; i++ {
 		go func() {
-			result := hf.Invoke(context.Background(), &testMockFailInvoker{}, &invocation.RPCInvocation{})
+			testUrl, err := common.NewURL(
+				fmt.Sprintf("dubbo://%s:%d/com.ikurento.user.UserProvider", constant.LOCAL_HOST_VALUE, constant.DEFAULT_PORT))
+			assert.NoError(t, err)
+			testInvoker := testMockSuccessInvoker{*protocol.NewBaseInvoker(testUrl)}
+			result := hf.Invoke(context.Background(), &testInvoker, &invocation.RPCInvocation{})
 			resChan <- result
 		}()
 	}
diff --git a/filter/filter_impl/metrics_filter_test.go b/filter/filter_impl/metrics_filter_test.go
index 881106f4bc5a7890569be347122da5144e440c8b..ac10d52cf3c156e3580760a4409bab49bb4d0c4f 100644
--- a/filter/filter_impl/metrics_filter_test.go
+++ b/filter/filter_impl/metrics_filter_test.go
@@ -57,7 +57,7 @@ func TestMetricsFilterInvoke(t *testing.T) {
 			"service.filter=echo%2Ctoken%2Caccesslog&timestamp=1569153406&token=934804bf-b007-4174-94eb-96e3e1d60cc7&version=&warmup=100")
 	invoker := protocol.NewBaseInvoker(url)
 
-	attach := make(map[string]string, 10)
+	attach := make(map[string]interface{}, 10)
 	inv := invocation.NewRPCInvocation("MethodName", []interface{}{"OK", "Hello"}, attach)
 
 	ctx := context.Background()
diff --git a/filter/filter_impl/seata_filter_test.go b/filter/filter_impl/seata_filter_test.go
index 6c39897f7aa3a044490e9eece8f352a9db9fc74b..45817e95cbd2eaa7365adc8a299523af8310f797 100644
--- a/filter/filter_impl/seata_filter_test.go
+++ b/filter/filter_impl/seata_filter_test.go
@@ -48,8 +48,9 @@ func (iv *testMockSeataInvoker) Invoke(ctx context.Context, _ protocol.Invocatio
 
 func TestSeataFilter_Invoke(t *testing.T) {
 	filter := getSeataFilter()
-	result := filter.Invoke(context.Background(), &testMockSeataInvoker{}, invocation.NewRPCInvocation("$echo", []interface{}{"OK"}, map[string]string{
-		SEATA_XID: "10.30.21.227:8091:2000047792",
-	}))
+	result := filter.Invoke(context.Background(), &testMockSeataInvoker{}, invocation.NewRPCInvocation("$echo",
+		[]interface{}{"OK"}, map[string]interface{}{
+			SEATA_XID: "10.30.21.227:8091:2000047792",
+		}))
 	assert.Equal(t, "10.30.21.227:8091:2000047792", result.Result())
 }
diff --git a/filter/filter_impl/sentinel_filter.go b/filter/filter_impl/sentinel_filter.go
new file mode 100644
index 0000000000000000000000000000000000000000..3787a0284f5470282c14c8b1a0aaf0187d6239e3
--- /dev/null
+++ b/filter/filter_impl/sentinel_filter.go
@@ -0,0 +1,255 @@
+/*
+ * 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"
+	"fmt"
+	"strings"
+)
+
+import (
+	sentinel "github.com/alibaba/sentinel-golang/api"
+	"github.com/alibaba/sentinel-golang/core/base"
+	"github.com/alibaba/sentinel-golang/logging"
+)
+
+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"
+)
+
+// Integrate Sentinel Go MUST HAVE:
+// 1. Must initialize Sentinel Go run environment,
+//     refer to https://github.com/alibaba/sentinel-golang/blob/master/api/init.go
+// 2. Register rules for resources user want to guard
+
+func init() {
+	extension.SetFilter(SentinelProviderFilterName, GetSentinelProviderFilter)
+	extension.SetFilter(SentinelConsumerFilterName, GetSentinelConsumerFilter)
+
+	if err := logging.ResetGlobalLogger(DubboLoggerWrapper{Logger: logger.GetLogger()}); err != nil {
+		logger.Errorf("[Sentinel Filter] fail to ingest dubbo logger into sentinel")
+	}
+}
+
+type DubboLoggerWrapper struct {
+	logger.Logger
+}
+
+func (d DubboLoggerWrapper) Debug(msg string, keysAndValues ...interface{}) {
+	d.Logger.Debug(logging.AssembleMsg(logging.GlobalCallerDepth, "DEBUG", msg, nil, keysAndValues))
+}
+
+func (d DubboLoggerWrapper) DebugEnabled() bool {
+	return true
+}
+
+func (d DubboLoggerWrapper) Info(msg string, keysAndValues ...interface{}) {
+	d.Logger.Info(logging.AssembleMsg(logging.GlobalCallerDepth, "INFO", msg, nil, keysAndValues))
+}
+
+func (d DubboLoggerWrapper) InfoEnabled() bool {
+	return true
+}
+
+func (d DubboLoggerWrapper) Warn(msg string, keysAndValues ...interface{}) {
+	d.Logger.Warn(logging.AssembleMsg(logging.GlobalCallerDepth, "WARN", msg, nil, keysAndValues))
+}
+
+func (d DubboLoggerWrapper) WarnEnabled() bool {
+	return true
+}
+
+func (d DubboLoggerWrapper) Error(err error, msg string, keysAndValues ...interface{}) {
+	d.Logger.Warn(logging.AssembleMsg(logging.GlobalCallerDepth, "ERROR", msg, err, keysAndValues))
+}
+
+func (d DubboLoggerWrapper) ErrorEnabled() bool {
+	return true
+}
+
+func GetSentinelConsumerFilter() filter.Filter {
+	return &SentinelConsumerFilter{}
+}
+
+func GetSentinelProviderFilter() filter.Filter {
+	return &SentinelProviderFilter{}
+}
+
+func sentinelExit(ctx context.Context, result protocol.Result) {
+	if methodEntry := ctx.Value(MethodEntryKey); methodEntry != nil {
+		e := methodEntry.(*base.SentinelEntry)
+		sentinel.TraceError(e, result.Error())
+		e.Exit()
+	}
+	if interfaceEntry := ctx.Value(InterfaceEntryKey); interfaceEntry != nil {
+		e := interfaceEntry.(*base.SentinelEntry)
+		sentinel.TraceError(e, result.Error())
+		e.Exit()
+	}
+}
+
+type SentinelProviderFilter struct{}
+
+func (d *SentinelProviderFilter) Invoke(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
+	interfaceResourceName, methodResourceName := getResourceName(invoker, invocation, getProviderPrefix())
+
+	var (
+		interfaceEntry *base.SentinelEntry
+		methodEntry    *base.SentinelEntry
+		b              *base.BlockError
+	)
+	interfaceEntry, b = sentinel.Entry(interfaceResourceName, sentinel.WithResourceType(base.ResTypeRPC), sentinel.WithTrafficType(base.Inbound))
+	if b != nil {
+		// interface blocked
+		return sentinelDubboProviderFallback(ctx, invoker, invocation, b)
+	}
+	ctx = context.WithValue(ctx, InterfaceEntryKey, interfaceEntry)
+
+	methodEntry, b = sentinel.Entry(methodResourceName,
+		sentinel.WithResourceType(base.ResTypeRPC),
+		sentinel.WithTrafficType(base.Inbound),
+		sentinel.WithArgs(invocation.Arguments()...))
+	if b != nil {
+		// method blocked
+		return sentinelDubboProviderFallback(ctx, invoker, invocation, b)
+	}
+	ctx = context.WithValue(ctx, MethodEntryKey, methodEntry)
+	return invoker.Invoke(ctx, invocation)
+}
+
+func (d *SentinelProviderFilter) OnResponse(ctx context.Context, result protocol.Result, _ protocol.Invoker, _ protocol.Invocation) protocol.Result {
+	sentinelExit(ctx, result)
+	return result
+}
+
+type SentinelConsumerFilter struct{}
+
+func (d *SentinelConsumerFilter) Invoke(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
+	interfaceResourceName, methodResourceName := getResourceName(invoker, invocation, getConsumerPrefix())
+	var (
+		interfaceEntry *base.SentinelEntry
+		methodEntry    *base.SentinelEntry
+		b              *base.BlockError
+	)
+
+	interfaceEntry, b = sentinel.Entry(interfaceResourceName, sentinel.WithResourceType(base.ResTypeRPC), sentinel.WithTrafficType(base.Outbound))
+	if b != nil {
+		// interface blocked
+		return sentinelDubboConsumerFallback(ctx, invoker, invocation, b)
+	}
+	ctx = context.WithValue(ctx, InterfaceEntryKey, interfaceEntry)
+
+	methodEntry, b = sentinel.Entry(methodResourceName, sentinel.WithResourceType(base.ResTypeRPC),
+		sentinel.WithTrafficType(base.Outbound), sentinel.WithArgs(invocation.Arguments()...))
+	if b != nil {
+		// method blocked
+		return sentinelDubboConsumerFallback(ctx, invoker, invocation, b)
+	}
+	ctx = context.WithValue(ctx, MethodEntryKey, methodEntry)
+
+	return invoker.Invoke(ctx, invocation)
+}
+
+func (d *SentinelConsumerFilter) OnResponse(ctx context.Context, result protocol.Result, _ protocol.Invoker, _ protocol.Invocation) protocol.Result {
+	sentinelExit(ctx, result)
+	return result
+}
+
+var (
+	sentinelDubboConsumerFallback = getDefaultDubboFallback()
+	sentinelDubboProviderFallback = getDefaultDubboFallback()
+)
+
+type DubboFallback func(context.Context, protocol.Invoker, protocol.Invocation, *base.BlockError) protocol.Result
+
+func SetDubboConsumerFallback(f DubboFallback) {
+	sentinelDubboConsumerFallback = f
+}
+func SetDubboProviderFallback(f DubboFallback) {
+	sentinelDubboProviderFallback = f
+}
+func getDefaultDubboFallback() DubboFallback {
+	return func(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation, blockError *base.BlockError) protocol.Result {
+		result := &protocol.RPCResult{}
+		result.SetResult(nil)
+		result.SetError(blockError)
+		return result
+	}
+}
+
+const (
+	SentinelProviderFilterName = "sentinel-provider"
+	SentinelConsumerFilterName = "sentinel-consumer"
+
+	DefaultProviderPrefix = "dubbo:provider:"
+	DefaultConsumerPrefix = "dubbo:consumer:"
+
+	MethodEntryKey    = "$$sentinelMethodEntry"
+	InterfaceEntryKey = "$$sentinelInterfaceEntry"
+)
+
+func getResourceName(invoker protocol.Invoker, invocation protocol.Invocation, prefix string) (interfaceResourceName, methodResourceName string) {
+	var sb strings.Builder
+
+	sb.WriteString(prefix)
+	if getInterfaceGroupAndVersionEnabled() {
+		interfaceResourceName = getColonSeparatedKey(invoker.GetUrl())
+	} else {
+		interfaceResourceName = invoker.GetUrl().Service()
+	}
+	sb.WriteString(interfaceResourceName)
+	sb.WriteString(":")
+	sb.WriteString(invocation.MethodName())
+	sb.WriteString("(")
+	isFirst := true
+	for _, v := range invocation.ParameterTypes() {
+		if !isFirst {
+			sb.WriteString(",")
+		}
+		sb.WriteString(v.Name())
+		isFirst = false
+	}
+	sb.WriteString(")")
+	methodResourceName = sb.String()
+	return
+}
+
+func getConsumerPrefix() string {
+	return DefaultConsumerPrefix
+}
+
+func getProviderPrefix() string {
+	return DefaultProviderPrefix
+}
+
+func getInterfaceGroupAndVersionEnabled() bool {
+	return true
+}
+
+func getColonSeparatedKey(url *common.URL) string {
+	return fmt.Sprintf("%s:%s:%s",
+		url.Service(),
+		url.GetParam(constant.GROUP_KEY, ""),
+		url.GetParam(constant.VERSION_KEY, ""))
+}
diff --git a/filter/filter_impl/sentinel_filter_test.go b/filter/filter_impl/sentinel_filter_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..a1ca21ce6c634b9647e586df0ac97a51d3fae7cc
--- /dev/null
+++ b/filter/filter_impl/sentinel_filter_test.go
@@ -0,0 +1,127 @@
+/*
+ * 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"
+	"sync/atomic"
+	"testing"
+)
+
+import (
+	"github.com/alibaba/sentinel-golang/core/flow"
+	"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"
+)
+
+func TestSentinelFilter_QPS(t *testing.T) {
+	url, err := common.NewURL("dubbo://127.0.0.1:20000/UserProvider?anyhost=true&" +
+		"version=1.0.0&group=myGroup&" +
+		"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)
+	mockInvoker := protocol.NewBaseInvoker(url)
+	interfaceResourceName, _ := getResourceName(mockInvoker,
+		invocation.NewRPCInvocation("hello", []interface{}{"OK"}, make(map[string]interface{})), "prefix_")
+	mockInvocation := invocation.NewRPCInvocation("hello", []interface{}{"OK"}, make(map[string]interface{}))
+
+	_, err = flow.LoadRules([]*flow.Rule{
+		{
+			Resource:               interfaceResourceName,
+			//MetricType:             flow.QPS,
+			TokenCalculateStrategy: flow.Direct,
+			ControlBehavior:        flow.Reject,
+			Threshold:                  100,
+			RelationStrategy:       flow.CurrentResource,
+		},
+	})
+	assert.NoError(t, err)
+
+	wg := sync.WaitGroup{}
+	wg.Add(10)
+	f := GetSentinelProviderFilter()
+	pass := int64(0)
+	block := int64(0)
+	for i := 0; i < 10; i++ {
+		go func() {
+			for j := 0; j < 30; j++ {
+				result := f.Invoke(context.TODO(), mockInvoker, mockInvocation)
+				if result.Error() == nil {
+					atomic.AddInt64(&pass, 1)
+				} else {
+					atomic.AddInt64(&block, 1)
+				}
+			}
+			wg.Done()
+		}()
+	}
+	wg.Wait()
+	assert.True(t, atomic.LoadInt64(&pass) == 100)
+	assert.True(t, atomic.LoadInt64(&block) == 200)
+}
+
+func TestConsumerFilter_Invoke(t *testing.T) {
+	f := GetSentinelConsumerFilter()
+	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)
+	mockInvoker := protocol.NewBaseInvoker(url)
+	mockInvocation := invocation.NewRPCInvocation("hello", []interface{}{"OK"}, make(map[string]interface{}))
+	result := f.Invoke(context.TODO(), mockInvoker, mockInvocation)
+	assert.NoError(t, result.Error())
+}
+
+func TestProviderFilter_Invoke(t *testing.T) {
+	f := GetSentinelProviderFilter()
+	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)
+	mockInvoker := protocol.NewBaseInvoker(url)
+	mockInvocation := invocation.NewRPCInvocation("hello", []interface{}{"OK"}, make(map[string]interface{}))
+	result := f.Invoke(context.TODO(), mockInvoker, mockInvocation)
+	assert.NoError(t, result.Error())
+}
+
+func TestGetResourceName(t *testing.T) {
+	url, err := common.NewURL("dubbo://127.0.0.1:20000/UserProvider?anyhost=true&" +
+		"version=1.0.0&group=myGroup&" +
+		"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)
+	mockInvoker := protocol.NewBaseInvoker(url)
+	interfaceResourceName, methodResourceName := getResourceName(mockInvoker,
+		invocation.NewRPCInvocation("hello", []interface{}{"OK"}, make(map[string]interface{})), "prefix_")
+	assert.Equal(t, "com.ikurento.user.UserProvider:myGroup:1.0.0", interfaceResourceName)
+	assert.Equal(t, "prefix_com.ikurento.user.UserProvider:myGroup:1.0.0:hello()", methodResourceName)
+}
diff --git a/filter/filter_impl/token_filter.go b/filter/filter_impl/token_filter.go
index fe4e38747ed10a40950c87747220339d0d566781..b5e05605c29905d9e66a63129f064c2f844f0e71 100644
--- a/filter/filter_impl/token_filter.go
+++ b/filter/filter_impl/token_filter.go
@@ -51,7 +51,7 @@ func (tf *TokenFilter) Invoke(ctx context.Context, invoker protocol.Invoker, inv
 	if len(invokerTkn) > 0 {
 		attachs := invocation.Attachments()
 		remoteTkn, exist := attachs[constant.TOKEN_KEY]
-		if exist && strings.EqualFold(invokerTkn, remoteTkn) {
+		if exist && remoteTkn != nil && strings.EqualFold(invokerTkn, remoteTkn.(string)) {
 			return invoker.Invoke(ctx, invocation)
 		}
 		return &protocol.RPCResult{Err: perrors.Errorf("Invalid token! Forbid invoke remote service %v method %s ",
diff --git a/filter/filter_impl/token_filter_test.go b/filter/filter_impl/token_filter_test.go
index c2f69bd03941b1404585dc5842c56eb2bf3c918f..9ef8c98d6868c3d545fe963ab13eb3f27bb88cd0 100644
--- a/filter/filter_impl/token_filter_test.go
+++ b/filter/filter_impl/token_filter_test.go
@@ -40,10 +40,10 @@ func TestTokenFilterInvoke(t *testing.T) {
 	url := common.NewURLWithOptions(
 		common.WithParams(url.Values{}),
 		common.WithParamsValue(constant.TOKEN_KEY, "ori_key"))
-	attch := make(map[string]string, 0)
+	attch := make(map[string]interface{}, 0)
 	attch[constant.TOKEN_KEY] = "ori_key"
 	result := filter.Invoke(context.Background(),
-		protocol.NewBaseInvoker(*url),
+		protocol.NewBaseInvoker(url),
 		invocation.NewRPCInvocation("MethodName",
 			[]interface{}{"OK"}, attch))
 	assert.Nil(t, result.Error())
@@ -54,9 +54,9 @@ func TestTokenFilterInvokeEmptyToken(t *testing.T) {
 	filter := GetTokenFilter()
 
 	testUrl := common.URL{}
-	attch := make(map[string]string, 0)
+	attch := make(map[string]interface{}, 0)
 	attch[constant.TOKEN_KEY] = "ori_key"
-	result := filter.Invoke(context.Background(), protocol.NewBaseInvoker(testUrl), invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch))
+	result := filter.Invoke(context.Background(), protocol.NewBaseInvoker(&testUrl), invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch))
 	assert.Nil(t, result.Error())
 	assert.Nil(t, result.Result())
 }
@@ -67,8 +67,8 @@ func TestTokenFilterInvokeEmptyAttach(t *testing.T) {
 	testUrl := common.NewURLWithOptions(
 		common.WithParams(url.Values{}),
 		common.WithParamsValue(constant.TOKEN_KEY, "ori_key"))
-	attch := make(map[string]string, 0)
-	result := filter.Invoke(context.Background(), protocol.NewBaseInvoker(*testUrl), invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch))
+	attch := make(map[string]interface{}, 0)
+	result := filter.Invoke(context.Background(), protocol.NewBaseInvoker(testUrl), invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch))
 	assert.NotNil(t, result.Error())
 }
 
@@ -78,9 +78,9 @@ func TestTokenFilterInvokeNotEqual(t *testing.T) {
 	testUrl := common.NewURLWithOptions(
 		common.WithParams(url.Values{}),
 		common.WithParamsValue(constant.TOKEN_KEY, "ori_key"))
-	attch := make(map[string]string, 0)
+	attch := make(map[string]interface{}, 0)
 	attch[constant.TOKEN_KEY] = "err_key"
 	result := filter.Invoke(context.Background(),
-		protocol.NewBaseInvoker(*testUrl), invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch))
+		protocol.NewBaseInvoker(testUrl), invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch))
 	assert.NotNil(t, result.Error())
 }
diff --git a/filter/filter_impl/tps/tps_limiter_method_service.go b/filter/filter_impl/tps/tps_limiter_method_service.go
index 5761579a38a22500d54193a9564170cc0215cf0f..f0c276438beaa161a6f6b13bc95c1fd7f357e8b6 100644
--- a/filter/filter_impl/tps/tps_limiter_method_service.go
+++ b/filter/filter_impl/tps/tps_limiter_method_service.go
@@ -120,7 +120,7 @@ type MethodServiceTpsLimiterImpl struct {
 // The key point is how to keep thread-safe
 // This implementation use concurrent map + loadOrStore to make implementation thread-safe
 // You can image that even multiple threads create limiter, but only one could store the limiter into tpsState
-func (limiter MethodServiceTpsLimiterImpl) IsAllowable(url common.URL, invocation protocol.Invocation) bool {
+func (limiter MethodServiceTpsLimiterImpl) IsAllowable(url *common.URL, invocation protocol.Invocation) bool {
 
 	methodConfigPrefix := "methods." + invocation.MethodName() + "."
 
@@ -176,7 +176,7 @@ func (limiter MethodServiceTpsLimiterImpl) IsAllowable(url common.URL, invocatio
 // If we can convert the methodLevelConfig to int64, return;
 // Or, we will try to look up server-level configuration and then convert it to int64
 func getLimitConfig(methodLevelConfig string,
-	url common.URL,
+	url *common.URL,
 	invocation protocol.Invocation,
 	configKey string,
 	defaultVal string) int64 {
diff --git a/filter/filter_impl/tps/tps_limiter_method_service_test.go b/filter/filter_impl/tps/tps_limiter_method_service_test.go
index edae99ec2d3157ad7f0d81c95a2fb181410475fa..7435d9b92ac8270de0244c8a257aec82f9acc9a1 100644
--- a/filter/filter_impl/tps/tps_limiter_method_service_test.go
+++ b/filter/filter_impl/tps/tps_limiter_method_service_test.go
@@ -36,7 +36,7 @@ import (
 
 func TestMethodServiceTpsLimiterImplIsAllowableOnlyServiceLevel(t *testing.T) {
 	methodName := "hello"
-	invoc := invocation.NewRPCInvocation(methodName, []interface{}{"OK"}, make(map[string]string, 0))
+	invoc := invocation.NewRPCInvocation(methodName, []interface{}{"OK"}, make(map[string]interface{}, 0))
 
 	ctrl := gomock.NewController(t)
 	defer ctrl.Finish()
@@ -57,13 +57,13 @@ func TestMethodServiceTpsLimiterImplIsAllowableOnlyServiceLevel(t *testing.T) {
 	})
 
 	limiter := GetMethodServiceTpsLimiter()
-	result := limiter.IsAllowable(*invokeUrl, invoc)
+	result := limiter.IsAllowable(invokeUrl, invoc)
 	assert.True(t, result)
 }
 
 func TestMethodServiceTpsLimiterImplIsAllowableNoConfig(t *testing.T) {
 	methodName := "hello1"
-	invoc := invocation.NewRPCInvocation(methodName, []interface{}{"OK"}, make(map[string]string, 0))
+	invoc := invocation.NewRPCInvocation(methodName, []interface{}{"OK"}, make(map[string]interface{}, 0))
 	// ctrl := gomock.NewController(t)
 	// defer ctrl.Finish()
 
@@ -73,14 +73,14 @@ func TestMethodServiceTpsLimiterImplIsAllowableNoConfig(t *testing.T) {
 		common.WithParamsValue(constant.TPS_LIMIT_RATE_KEY, ""))
 
 	limiter := GetMethodServiceTpsLimiter()
-	result := limiter.IsAllowable(*invokeUrl, invoc)
+	result := limiter.IsAllowable(invokeUrl, invoc)
 	assert.True(t, result)
 }
 
 func TestMethodServiceTpsLimiterImplIsAllowableMethodLevelOverride(t *testing.T) {
 	methodName := "hello2"
 	methodConfigPrefix := "methods." + methodName + "."
-	invoc := invocation.NewRPCInvocation(methodName, []interface{}{"OK"}, make(map[string]string, 0))
+	invoc := invocation.NewRPCInvocation(methodName, []interface{}{"OK"}, make(map[string]interface{}, 0))
 	ctrl := gomock.NewController(t)
 	defer ctrl.Finish()
 
@@ -106,14 +106,14 @@ func TestMethodServiceTpsLimiterImplIsAllowableMethodLevelOverride(t *testing.T)
 	})
 
 	limiter := GetMethodServiceTpsLimiter()
-	result := limiter.IsAllowable(*invokeUrl, invoc)
+	result := limiter.IsAllowable(invokeUrl, invoc)
 	assert.True(t, result)
 }
 
 func TestMethodServiceTpsLimiterImplIsAllowableBothMethodAndService(t *testing.T) {
 	methodName := "hello3"
 	methodConfigPrefix := "methods." + methodName + "."
-	invoc := invocation.NewRPCInvocation(methodName, []interface{}{"OK"}, make(map[string]string, 0))
+	invoc := invocation.NewRPCInvocation(methodName, []interface{}{"OK"}, make(map[string]interface{}, 0))
 	ctrl := gomock.NewController(t)
 	defer ctrl.Finish()
 
@@ -136,7 +136,7 @@ func TestMethodServiceTpsLimiterImplIsAllowableBothMethodAndService(t *testing.T
 	})
 
 	limiter := GetMethodServiceTpsLimiter()
-	result := limiter.IsAllowable(*invokeUrl, invoc)
+	result := limiter.IsAllowable(invokeUrl, invoc)
 	assert.True(t, result)
 }
 
diff --git a/filter/filter_impl/tps/tps_limiter_mock.go b/filter/filter_impl/tps/tps_limiter_mock.go
index b49084f28e7d4b62d64762170b6dfbbec4a4de96..34c279032713595066638095b5f4d2f6fa2c4aa4 100644
--- a/filter/filter_impl/tps/tps_limiter_mock.go
+++ b/filter/filter_impl/tps/tps_limiter_mock.go
@@ -58,7 +58,7 @@ func (m *MockTpsLimiter) EXPECT() *MockTpsLimiterMockRecorder {
 }
 
 // IsAllowable mocks base method
-func (m *MockTpsLimiter) IsAllowable(arg0 common.URL, arg1 protocol.Invocation) bool {
+func (m *MockTpsLimiter) IsAllowable(arg0 *common.URL, arg1 protocol.Invocation) bool {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "IsAllowable", arg0, arg1)
 	ret0, _ := ret[0].(bool)
diff --git a/filter/filter_impl/tps_limit_filter_test.go b/filter/filter_impl/tps_limit_filter_test.go
index 274e4e6de61b94079e9ad3b2f7a5bcd79a276cc6..88e778105081129463b68618804bd9204d2f7113 100644
--- a/filter/filter_impl/tps_limit_filter_test.go
+++ b/filter/filter_impl/tps_limit_filter_test.go
@@ -44,10 +44,10 @@ func TestTpsLimitFilterInvokeWithNoTpsLimiter(t *testing.T) {
 	invokeUrl := common.NewURLWithOptions(
 		common.WithParams(url.Values{}),
 		common.WithParamsValue(constant.TPS_LIMITER_KEY, ""))
-	attch := make(map[string]string, 0)
+	attch := make(map[string]interface{}, 0)
 
 	result := tpsFilter.Invoke(context.Background(),
-		protocol.NewBaseInvoker(*invokeUrl),
+		protocol.NewBaseInvoker(invokeUrl),
 		invocation.NewRPCInvocation("MethodName",
 			[]interface{}{"OK"}, attch))
 	assert.Nil(t, result.Error())
@@ -68,10 +68,10 @@ func TestGenericFilterInvokeWithDefaultTpsLimiter(t *testing.T) {
 	invokeUrl := common.NewURLWithOptions(
 		common.WithParams(url.Values{}),
 		common.WithParamsValue(constant.TPS_LIMITER_KEY, constant.DEFAULT_KEY))
-	attch := make(map[string]string, 0)
+	attch := make(map[string]interface{}, 0)
 
 	result := tpsFilter.Invoke(context.Background(),
-		protocol.NewBaseInvoker(*invokeUrl),
+		protocol.NewBaseInvoker(invokeUrl),
 		invocation.NewRPCInvocation("MethodName",
 			[]interface{}{"OK"}, attch))
 	assert.Nil(t, result.Error())
@@ -99,10 +99,12 @@ func TestGenericFilterInvokeWithDefaultTpsLimiterNotAllow(t *testing.T) {
 	invokeUrl := common.NewURLWithOptions(
 		common.WithParams(url.Values{}),
 		common.WithParamsValue(constant.TPS_LIMITER_KEY, constant.DEFAULT_KEY))
-	attch := make(map[string]string, 0)
+	attch := make(map[string]interface{}, 0)
 
 	result := tpsFilter.Invoke(context.Background(),
-		protocol.NewBaseInvoker(*invokeUrl), invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch))
+		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
index b8058aa601af98b5416da882321546675459c413..dcdbe5b30e3c288462e9078df234243435ca86c6 100644
--- a/filter/filter_impl/tracing_filter.go
+++ b/filter/filter_impl/tracing_filter.go
@@ -83,7 +83,7 @@ func (tf *tracingFilter) Invoke(ctx context.Context, invoker protocol.Invoker, i
 	}()
 
 	result := invoker.Invoke(spanCtx, invocation)
-	span.SetTag(successKey, result.Error() != nil)
+	span.SetTag(successKey, result.Error() == nil)
 	if result.Error() != nil {
 		span.LogFields(log.String(errorKey, result.Error().Error()))
 	}
diff --git a/filter/filter_impl/tracing_filter_test.go b/filter/filter_impl/tracing_filter_test.go
index 57f4095d49be784d7688d2acf17c1ea0225d0000..e159b7400d46069018a00a849319423285072dc2 100644
--- a/filter/filter_impl/tracing_filter_test.go
+++ b/filter/filter_impl/tracing_filter_test.go
@@ -42,7 +42,7 @@ func TestTracingFilterInvoke(t *testing.T) {
 			"service.filter=echo%2Ctoken%2Caccesslog&timestamp=1569153406&token=934804bf-b007-4174-94eb-96e3e1d60cc7&version=&warmup=100")
 	invoker := protocol.NewBaseInvoker(url)
 
-	attach := make(map[string]string, 10)
+	attach := make(map[string]interface{}, 10)
 	inv := invocation.NewRPCInvocation("MethodName", []interface{}{"OK", "Hello"}, attach)
 	ctx := context.Background()
 	tf := newTracingFilter()
diff --git a/filter/handler/rejected_execution_handler_mock.go b/filter/handler/rejected_execution_handler_mock.go
index bff54769cb007ed2fce5a7623c96edc370e63903..5f2f458f83d0ca2cdff68eec4d0f3d22cf4e3277 100644
--- a/filter/handler/rejected_execution_handler_mock.go
+++ b/filter/handler/rejected_execution_handler_mock.go
@@ -58,7 +58,7 @@ func (m *MockRejectedExecutionHandler) EXPECT() *MockRejectedExecutionHandlerMoc
 }
 
 // RejectedExecution mocks base method
-func (m *MockRejectedExecutionHandler) RejectedExecution(url common.URL, invocation protocol.Invocation) protocol.Result {
+func (m *MockRejectedExecutionHandler) RejectedExecution(url *common.URL, invocation protocol.Invocation) protocol.Result {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "RejectedExecution", url, invocation)
 	ret0, _ := ret[0].(protocol.Result)
diff --git a/filter/handler/rejected_execution_handler_only_log.go b/filter/handler/rejected_execution_handler_only_log.go
index 52ac1765f78172c0062de8884198e759b8d494ca..5242b5b49e8a12322821481d237d84710c15eafd 100644
--- a/filter/handler/rejected_execution_handler_only_log.go
+++ b/filter/handler/rejected_execution_handler_only_log.go
@@ -63,7 +63,7 @@ type OnlyLogRejectedExecutionHandler struct {
 }
 
 // RejectedExecution will do nothing, it only log the invocation.
-func (handler *OnlyLogRejectedExecutionHandler) RejectedExecution(url common.URL,
+func (handler *OnlyLogRejectedExecutionHandler) RejectedExecution(url *common.URL,
 	_ protocol.Invocation) protocol.Result {
 
 	logger.Errorf("The invocation was rejected. url: %s", url.String())
diff --git a/filter/handler/rejected_execution_handler_only_log_test.go b/filter/handler/rejected_execution_handler_only_log_test.go
index 409f09f61bd958992749231fca045b54601fc627..7aa4aff936d6da1e46ed5a24609a0346e9d2edb4 100644
--- a/filter/handler/rejected_execution_handler_only_log_test.go
+++ b/filter/handler/rejected_execution_handler_only_log_test.go
@@ -31,5 +31,5 @@ func TestOnlyLogRejectedExecutionHandler_RejectedExecution(t *testing.T) {
 	invokeUrl := common.NewURLWithOptions(
 		common.WithParams(url.Values{}),
 		common.WithParamsValue(constant.INTERFACE_KEY, "methodName"))
-	handler.RejectedExecution(*invokeUrl, nil)
+	handler.RejectedExecution(invokeUrl, nil)
 }
diff --git a/filter/rejected_execution_handler.go b/filter/rejected_execution_handler.go
index 3d1e1c1e641a836411ce0f71f97acf5d5a55f6d1..73ac3d3bbf37cbce5822e1955fb49f84bdf6d678 100644
--- a/filter/rejected_execution_handler.go
+++ b/filter/rejected_execution_handler.go
@@ -33,5 +33,5 @@ import (
 type RejectedExecutionHandler interface {
 
 	// RejectedExecution will be called if the invocation was rejected by some component.
-	RejectedExecution(url common.URL, invocation protocol.Invocation) protocol.Result
+	RejectedExecution(url *common.URL, invocation protocol.Invocation) protocol.Result
 }
diff --git a/filter/tps_limiter.go b/filter/tps_limiter.go
index 8385d7b5d84a420b54df6bf51e32a35d17e1b249..6f2466c0a40620b9f7dee358db681c4032c773fc 100644
--- a/filter/tps_limiter.go
+++ b/filter/tps_limiter.go
@@ -35,5 +35,5 @@ import (
  */
 type TpsLimiter interface {
 	// IsAllowable will check whether this invocation should be enabled for further process
-	IsAllowable(common.URL, protocol.Invocation) bool
+	IsAllowable(*common.URL, protocol.Invocation) bool
 }
diff --git a/go.mod b/go.mod
index 3b622d924800a62c1774965ef1d14ce4928796f6..e822ab4cf352c5808747f358dc831071cf6a4906 100644
--- a/go.mod
+++ b/go.mod
@@ -1,64 +1,58 @@
 module github.com/apache/dubbo-go
 
+go 1.15
+
 require (
-	github.com/Microsoft/go-winio v0.4.13 // indirect
 	github.com/NYTimes/gziphandler v1.1.1 // indirect
-	github.com/Workiva/go-datastructures v1.0.50
+	github.com/RoaringBitmap/roaring v0.5.5
+	github.com/Workiva/go-datastructures v1.0.52
 	github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5
+	github.com/alibaba/sentinel-golang v1.0.1
 	github.com/apache/dubbo-getty v1.3.10
-	github.com/apache/dubbo-go-hessian2 v1.6.2
-	github.com/coreos/bbolt v1.3.3 // indirect
-	github.com/coreos/etcd v3.3.13+incompatible
-	github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f // indirect
-	github.com/creasty/defaults v1.3.0
-	github.com/docker/go-connections v0.4.0 // indirect
-	github.com/dubbogo/go-zookeeper v1.0.1
-	github.com/dubbogo/gost v1.9.1
+	github.com/apache/dubbo-go-hessian2 v1.8.0
+	github.com/coreos/etcd v3.3.25+incompatible
+	github.com/creasty/defaults v1.5.1
+	github.com/dubbogo/go-zookeeper v1.0.2
+	github.com/dubbogo/gost v1.9.5
 	github.com/elazarl/go-bindata-assetfs v1.0.0 // indirect
-	github.com/emicklei/go-restful/v3 v3.0.0
+	github.com/emicklei/go-restful/v3 v3.4.0
 	github.com/frankban/quicktest v1.4.1 // indirect
-	github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 // indirect
+	github.com/fsnotify/fsnotify v1.4.9
 	github.com/go-co-op/gocron v0.1.1
-	github.com/go-resty/resty/v2 v2.1.0
-	github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 // indirect
-	github.com/golang/mock v1.3.1
-	github.com/golang/protobuf v1.3.2
-	github.com/google/go-cmp v0.3.1 // indirect
-	github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4 // indirect
-	github.com/grpc-ecosystem/grpc-gateway v1.9.5 // indirect
+	github.com/go-resty/resty/v2 v2.3.0
+	github.com/golang/mock v1.4.4
+	github.com/golang/protobuf v1.4.3
 	github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645
 	github.com/hashicorp/consul v1.8.0
 	github.com/hashicorp/consul/api v1.5.0
 	github.com/hashicorp/go-raftchunking v0.6.3-0.20191002164813-7e9e8525653a // indirect
-	github.com/hashicorp/golang-lru v0.5.3 // indirect
 	github.com/hashicorp/vault/api v1.0.5-0.20191108163347-bdd38fca2cff // indirect
 	github.com/hashicorp/vault/sdk v0.1.14-0.20191112033314-390e96e22eb2
 	github.com/jinzhu/copier v0.0.0-20190625015134-976e0346caa8
-	github.com/magiconair/properties v1.8.1
-	github.com/mitchellh/hashstructure v1.0.0 // indirect
-	github.com/mitchellh/mapstructure v1.2.3
+	github.com/magiconair/properties v1.8.4
+	github.com/mitchellh/mapstructure v1.4.0
 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd
-	github.com/nacos-group/nacos-sdk-go v1.0.0
-	github.com/opentracing/opentracing-go v1.1.0
+	github.com/nacos-group/nacos-sdk-go v1.0.1
+	github.com/opentracing/opentracing-go v1.2.0
 	github.com/pierrec/lz4 v2.2.6+incompatible // indirect
 	github.com/pkg/errors v0.9.1
-	github.com/prometheus/client_golang v1.1.0
+	github.com/prometheus/client_golang v1.8.0
 	github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b
-	github.com/shirou/gopsutil v2.19.9+incompatible // indirect
 	github.com/stretchr/objx v0.2.0 // indirect
-	github.com/stretchr/testify v1.5.1
-	github.com/zouyx/agollo/v3 v3.4.4
-	go.etcd.io/bbolt v1.3.4 // indirect
-	go.uber.org/atomic v1.6.0
-	go.uber.org/zap v1.15.0
-	google.golang.org/grpc v1.23.0
-	gopkg.in/yaml.v2 v2.2.8
+	github.com/stretchr/testify v1.6.1
+	github.com/zouyx/agollo/v3 v3.4.5
+	go.uber.org/atomic v1.7.0
+	go.uber.org/zap v1.16.0
+	golang.org/x/sys v0.0.0-20201223074533-0d417f636930 // indirect
+	google.golang.org/grpc v1.26.0
+	gopkg.in/yaml.v2 v2.4.0
 	k8s.io/api v0.16.9
 	k8s.io/apimachinery v0.16.9
 	k8s.io/client-go v0.16.9
 	k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a // indirect
 )
 
-go 1.13
-
-replace launchpad.net/gocheck => github.com/go-check/check v0.0.0-20140225173054-eb6ee6f84d0a
+replace (
+	github.com/coreos/bbolt => go.etcd.io/bbolt v1.3.4
+	github.com/envoyproxy/go-control-plane => github.com/envoyproxy/go-control-plane v0.8.0
+)
diff --git a/go.sum b/go.sum
index 91cdb0da19ca1acd9d35b630a65a9d8596382910..7b1c1716aa6701a495dbed2f54d411927c7bd890 100644
--- a/go.sum
+++ b/go.sum
@@ -48,9 +48,9 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym
 github.com/DataDog/datadog-go v2.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
 github.com/DataDog/datadog-go v3.2.0+incompatible h1:qSG2N4FghB1He/r2mFrWKCaL7dXCilEuNEeAn20fdD4=
 github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
+github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
+github.com/Microsoft/go-winio v0.4.3 h1:M3NHMuPgMSUPdE5epwNUHlRPSVzHs8HpRTrVXhR0myo=
 github.com/Microsoft/go-winio v0.4.3/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
-github.com/Microsoft/go-winio v0.4.13 h1:Hmi80lzZuI/CaYmlJp/b+FjZdRZhKu9c2mDVqKlLWVs=
-github.com/Microsoft/go-winio v0.4.13/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
 github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
 github.com/NYTimes/gziphandler v1.0.1/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
 github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I=
@@ -58,22 +58,34 @@ github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMo
 github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
 github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
 github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
+github.com/RoaringBitmap/roaring v0.5.5 h1:naNqvO1mNnghk2UvcsqnzHDBn9DRbCIRy94GmDTRVTQ=
+github.com/RoaringBitmap/roaring v0.5.5/go.mod h1:puNo5VdzwbaIQxSiDIwfXl4Hnc+fbovcX4IW/dSTtUk=
+github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
+github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
 github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 h1:fLjPD/aNc3UIOA6tDi6QXUemppXK3P9BI7mr2hd6gx8=
 github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
-github.com/Workiva/go-datastructures v1.0.50 h1:slDmfW6KCHcC7U+LP3DDBbm4fqTwZGn1beOFPfGaLvo=
-github.com/Workiva/go-datastructures v1.0.50/go.mod h1:Z+F2Rca0qCsVYDS8z7bAGm8f3UkzuWYS/oBZz5a7VVA=
+github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
+github.com/Workiva/go-datastructures v1.0.52 h1:PLSK6pwn8mYdaoaCZEMsXBpBotr4HHn9abU0yMQt0NI=
+github.com/Workiva/go-datastructures v1.0.52/go.mod h1:Z+F2Rca0qCsVYDS8z7bAGm8f3UkzuWYS/oBZz5a7VVA=
 github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af h1:DBNMBMuMiWYu0b+8KMJuWmfCkcxl09JwdlqwDZZ6U14=
 github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af/go.mod h1:5Jv4cbFiHJMsVxt52+i0Ha45fjshj6wxYr1r19tB9bw=
 github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5 h1:rFw4nCn9iMW+Vajsk51NtYIcwSTkXr+JGrMd36kTDJw=
 github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
 github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
 github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
+github.com/alibaba/sentinel-golang v1.0.1 h1:WlhN0XUxRyfkiDc8TO6CcRrnakwFP9zFtvJTYxZRCgI=
+github.com/alibaba/sentinel-golang v1.0.1/go.mod h1:QsB99f/z35D2AiMrAWwgWE85kDTkBUIkcmPrRt+61NI=
 github.com/aliyun/alibaba-cloud-sdk-go v1.61.18 h1:zOVTBdCKFd9JbCKz9/nt+FovbjPFmb7mUnp8nH9fQBA=
 github.com/aliyun/alibaba-cloud-sdk-go v1.61.18/go.mod h1:v8ESoHo4SyHmuB4b1tJqDHxfTGEciD+yhvOU/5s1Rfk=
 github.com/apache/dubbo-getty v1.3.10 h1:ys5mwjPdxG/KwkPjS6EI0RzQtU6p6FCPoKpaFEzpAL0=
 github.com/apache/dubbo-getty v1.3.10/go.mod h1:x6rraK01BL5C7jUM2fPl5KMkAxLVIx54ZB8/XEOik9Y=
-github.com/apache/dubbo-go-hessian2 v1.6.2 h1:i7F5GjVaUatLQz1x9vUmmSIFj49L8J6rVICdF6xw4qw=
-github.com/apache/dubbo-go-hessian2 v1.6.2/go.mod h1:7rEw9guWABQa6Aqb8HeZcsYPHsOS7XT1qtJvkmI6c5w=
+github.com/apache/dubbo-go-hessian2 v1.8.0 h1:+GJQHxWd/WUw2p4hbfCal/zjKvGVb8yJZzOke8IEazc=
+github.com/apache/dubbo-go-hessian2 v1.8.0/go.mod h1:7rEw9guWABQa6Aqb8HeZcsYPHsOS7XT1qtJvkmI6c5w=
+github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
+github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
 github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e h1:QEF07wC0T1rKkctt1RINW/+RMTVmiwxETico2l3gxJA=
 github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
 github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
@@ -84,8 +96,12 @@ github.com/armon/go-metrics v0.3.0/go.mod h1:zXjbSimjXTd7vOpY8B0/2LpvNvDoXBuplAD
 github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
 github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI=
 github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
-github.com/aws/aws-sdk-go v1.25.41 h1:/hj7nZ0586wFqpwjNpzWiUTwtaMgxAZNZKHay80MdXw=
+github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
+github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
 github.com/aws/aws-sdk-go v1.25.41/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
+github.com/aws/aws-sdk-go v1.27.0 h1:0xphMHGMLBrPMfxR2AmVjZKcMEESEgWF8Kru94BNByk=
+github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
+github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
 github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
 github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
@@ -97,35 +113,44 @@ github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4=
 github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
 github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23 h1:D21IyuvjDCshj1/qq+pCNd3VZOAEI9jy6Bi131YlXgI=
 github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
+github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
+github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
+github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
 github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
+github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
+github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
 github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible h1:C29Ae4G5GtYyYMm1aztcyj/J5ckgJm2zwdDajFbx1NY=
 github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
 github.com/circonus-labs/circonusllhist v0.1.3 h1:TJH+oke8D16535+jHExHj4nQvzlZrj7ug5D7I/orNUA=
 github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
+github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
 github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
+github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
+github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
 github.com/coredns/coredns v1.1.2 h1:bAFHrSsBeTeRG5W3Nf2su3lUGw7Npw2UKeCJm/3A638=
 github.com/coredns/coredns v1.1.2/go.mod h1:zASH/MVDgR6XZTbxvOnsZfffS+31vg6Ackf/wo1+AM0=
-github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
-github.com/coreos/bbolt v1.3.3 h1:n6AiVyVRKQFNb6mJlwESEvvLoDyiTzXX7ORAUlkeBdY=
-github.com/coreos/bbolt v1.3.3/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
 github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
-github.com/coreos/etcd v3.3.13+incompatible h1:8F3hqu9fGYLBifCmRCJsicFqDx/D68Rt3q1JMazcgBQ=
 github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
+github.com/coreos/etcd v3.3.25+incompatible h1:0GQEw6h3YnuOVdtwygkIfJ+Omx0tZ8/QkVyXI4LkbeY=
+github.com/coreos/etcd v3.3.25+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
 github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
 github.com/coreos/go-oidc v2.1.0+incompatible h1:sdJrfw8akMnCuUlaZU3tE/uYXFgfqom8DBE9so9EBsM=
 github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
 github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
 github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=
 github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8=
 github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
-github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f h1:JOrtw2xFKzlg+cbHpyrpLDmnN1HqhBfnX7WDiW7eG2c=
-github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
 github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg=
 github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
 github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
-github.com/creasty/defaults v1.3.0 h1:uG+RAxYbJgOPCOdKEcec9ZJXeva7Y6mj/8egdzwmLtw=
-github.com/creasty/defaults v1.3.0/go.mod h1:CIEEvs7oIVZm30R8VxtFJs+4k201gReYyuYHJxZc68I=
+github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
+github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
+github.com/creasty/defaults v1.5.1 h1:j8WexcS3d/t4ZmllX4GEkl4wIB/trOr035ajcLHCISM=
+github.com/creasty/defaults v1.5.1/go.mod h1:FPZ+Y0WNrbqOVw+c6av63eyHUAl6pMHZwqLPvXUZGfY=
 github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
@@ -142,29 +167,34 @@ github.com/dimchansky/utfbom v1.1.0 h1:FcM3g+nofKgUteL8dm/UpdRXNC9KmADgTpLKsu0TR
 github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8=
 github.com/dnaeon/go-vcr v1.0.1 h1:r8L/HqC0Hje5AXMu1ooW8oyQyOFv4GxqpL0nRP7SLLY=
 github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
+github.com/docker/go-connections v0.3.0 h1:3lOnM9cSzgGwx8VfK/NGOW5fLQ0GjIlCkaktF+n1M6o=
 github.com/docker/go-connections v0.3.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
-github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
-github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
 github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
-github.com/dubbogo/go-zookeeper v1.0.1 h1:irLzvOsDOTNsN8Sv9tvYYxVu6DCQfLtziZQtUHmZgz8=
-github.com/dubbogo/go-zookeeper v1.0.1/go.mod h1:fn6n2CAEer3novYgk9ULLwAjuV8/g4DdC2ENwRb6E+c=
-github.com/dubbogo/gost v1.9.0 h1:UT+dWwvLyJiDotxJERO75jB3Yxgsdy10KztR5ycxRAk=
+github.com/dubbogo/go-zookeeper v1.0.2 h1:xmEnPL8SlCe3/+J5ZR9e8qE35LmFVYe8VVpDakjNM4A=
+github.com/dubbogo/go-zookeeper v1.0.2/go.mod h1:fn6n2CAEer3novYgk9ULLwAjuV8/g4DdC2ENwRb6E+c=
 github.com/dubbogo/gost v1.9.0/go.mod h1:pPTjVyoJan3aPxBPNUX0ADkXjPibLo+/Ib0/fADXSG8=
-github.com/dubbogo/gost v1.9.1 h1:0/PPFo13zPbjt4Ia0zYWMFi3C6rAe9X7O1J2Iv+BHNM=
-github.com/dubbogo/gost v1.9.1/go.mod h1:pPTjVyoJan3aPxBPNUX0ADkXjPibLo+/Ib0/fADXSG8=
+github.com/dubbogo/gost v1.9.5 h1:UeG4y0O55lR3dzgdmCm/7bMWvpKrlpR7fsfKjrcXq/g=
+github.com/dubbogo/gost v1.9.5/go.mod h1:QNM5RaeRdNWehUu8S0hUP5Qa8QUfGf6KH1JhqOVFvEI=
+github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
+github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
 github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
+github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
+github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
+github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
+github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
 github.com/elazarl/go-bindata-assetfs v0.0.0-20160803192304-e1a2a7ec64b0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
 github.com/elazarl/go-bindata-assetfs v1.0.0 h1:G/bYguwHIzWq9ZoyUQqrjTmJbbYn3j3CKKpKinvZLFk=
 github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
 github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
 github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633 h1:H2pdYOb3KQ1/YsqVWoWNLQO+fusocsw354rqGTZtAgw=
 github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
-github.com/emicklei/go-restful/v3 v3.0.0 h1:Duxxa4x0WIHW3bYEDmoAPNjmy8Rbqn+utcF74dlF/G8=
-github.com/emicklei/go-restful/v3 v3.0.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
+github.com/emicklei/go-restful/v3 v3.4.0 h1:IIDhql3oyWZj1ay2xBZGb4sTOWMad0HVW8rwhVxN/Yk=
+github.com/emicklei/go-restful/v3 v3.4.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
 github.com/envoyproxy/go-control-plane v0.8.0 h1:uE6Fp4fOcAJdc1wTQXLJ+SYistkbG1dNoi6Zs1+Ybvk=
 github.com/envoyproxy/go-control-plane v0.8.0/go.mod h1:GSSbY9P1neVhdY7G4wu+IK1rk/dqhiCC/4ExuWJZVuk=
-github.com/envoyproxy/protoc-gen-validate v0.0.14 h1:YBW6/cKy9prEGRYLnaGa4IDhzxZhRCtKsax8srGKDnM=
 github.com/envoyproxy/protoc-gen-validate v0.0.14/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
+github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A=
+github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
 github.com/evanphx/json-patch v4.2.0+incompatible h1:fUDGZCv/7iAN7u0puUVhvKCcsR6vRfwrJatElLBEf0I=
 github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
 github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239 h1:Ghm4eQYC0nEPnSJdVkTrXpu9KtoVCSo1hg7mtI7G9KU=
@@ -173,26 +203,33 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv
 github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
 github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
 github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
+github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
+github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
 github.com/frankban/quicktest v1.4.1 h1:Wv2VwvNn73pAdFIVUQRXYDFp31lXKbqblIXo/Q5GPSg=
 github.com/frankban/quicktest v1.4.1/go.mod h1:36zfPVQyHxymz4cH7wlDmVwDrJuljRB60qkgn7rorfQ=
-github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
+github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
 github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
 github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
-github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 h1:Mn26/9ZMNWSw9C9ERFA1PUxfmGpolnw2v0bKOREu5ew=
-github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I=
+github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2 h1:Ujru1hufTHVb++eG6OuNDKMxZnGIvF6o/u8q/8h2+I4=
+github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
+github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31 h1:gclg6gY70GLy3PbkQ1AERPfmLMMagS60DKF78eWwLn8=
+github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24=
 github.com/go-asn1-ber/asn1-ber v1.3.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
-github.com/go-check/check v0.0.0-20140225173054-eb6ee6f84d0a/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98=
 github.com/go-co-op/gocron v0.1.1 h1:OfDmkqkCguFtFMsm6Eaayci3DADLa8pXvdmOlPU/JcU=
 github.com/go-co-op/gocron v0.1.1/go.mod h1:Y9PWlYqDChf2Nbgg7kfS+ZsXHDTZbMZYPEQ0MILqH+M=
 github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
 github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
 github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
 github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=
 github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc=
 github.com/go-ldap/ldap/v3 v3.1.3/go.mod h1:3rbOH3jRS2u6jg2rJnKAMLE/xQyCKIveG2Sa/Cohzb8=
 github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
 github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
+github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
 github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
 github.com/go-ole/go-ole v1.2.1 h1:2lOsA72HgjxAuMlKpFiCbHTvu44PIVkZ5hqm3RSdI/E=
 github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
@@ -201,8 +238,9 @@ github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1
 github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
 github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
 github.com/go-redis/redis v6.15.5+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
-github.com/go-resty/resty/v2 v2.1.0 h1:Z6IefCpUMfnvItVJaJXWv/pMiiD11So35QgwEELsldE=
-github.com/go-resty/resty/v2 v2.1.0/go.mod h1:dZGr0i9PLlaaTD4H/hoZIDjQ+r6xq8mgbRzHZf7f2J8=
+github.com/go-resty/resty/v2 v2.3.0 h1:JOOeAvjSlapTT92p8xiS19Zxev1neGikoHsXJeOq8So=
+github.com/go-resty/resty/v2 v2.3.0/go.mod h1:UpN9CgLZNsv4e9XG50UU8xdI0F43UQ4HmxLBDwaroHU=
+github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
 github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
 github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
 github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw=
@@ -210,6 +248,7 @@ github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3a
 github.com/gogo/googleapis v1.1.0 h1:kFkMAZBNAn4j7K0GiZr8cRYzejq68VbheufiV3YuyFI=
 github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
 github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
 github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
 github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d h1:3PaI8p3seN09VjbTYC/QWlUZdZ1qS1zGjy7LH2Wt07I=
 github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
@@ -222,13 +261,22 @@ github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM
 github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
 github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
-github.com/golang/mock v1.3.1 h1:qGJ6qTW+x6xX/my+8YUVl4WNpX9B7+/l2tRsHGZ7f2s=
 github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
+github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc=
+github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
 github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
 github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
+github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
+github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
+github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
+github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
+github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
+github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
 github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
@@ -236,8 +284,9 @@ github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
 github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
 github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
-github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
 github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
+github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
 github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
 github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
@@ -250,6 +299,8 @@ github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OI
 github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
 github.com/google/tcpproxy v0.0.0-20180808230851-dfa16c61dad2 h1:AtvtonGEH/fZK0XPNNBdB6swgy7Iudfx88wzyIpwqJ8=
 github.com/google/tcpproxy v0.0.0-20180808230851-dfa16c61dad2/go.mod h1:DavVbd41y+b7ukKDmlnPR4nGYmkWXR6vHUkjQNiHPBs=
+github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
 github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
 github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
@@ -259,8 +310,13 @@ github.com/googleapis/gnostic v0.2.0 h1:l6N3VoaVzTncYYW+9yOz2LJJammFZGBO13sqgEhp
 github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
 github.com/gophercloud/gophercloud v0.1.0 h1:P/nh25+rzXouhytV2pUHBb65fnds26Ghl8/391+sT5o=
 github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
-github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
 github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99 h1:twflg0XRTjwKpxb/jFExr4HGq6on2dEOmnL6FV+fgPw=
+github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
+github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
+github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
+github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
 github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
 github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
 github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
@@ -278,9 +334,11 @@ github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645/go
 github.com/hashicorp/consul v1.8.0 h1:yRKMKZyPLqUxl37t4nFt5OuGmTXoFhTJrakhfnYKCYA=
 github.com/hashicorp/consul v1.8.0/go.mod h1:Gg9/UgAQ9rdY3CTvzQZ6g2jcIb7NlIfjI+0pvLk5D1A=
 github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
+github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
 github.com/hashicorp/consul/api v1.5.0 h1:Yo2bneoGy68A7aNwmuETFnPhjyBEm7n3vzRacEVMjvI=
 github.com/hashicorp/consul/api v1.5.0/go.mod h1:LqwrLNW876eYSuUOo4ZLHBcdKc038txr/IMfbLPATa4=
 github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
+github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
 github.com/hashicorp/consul/sdk v0.5.0 h1:WC4594Wp/LkEeML/OdQKEC1yqBmEYkRp6i7X5u0zDAs=
 github.com/hashicorp/consul/sdk v0.5.0/go.mod h1:fY08Y9z5SvJqevyZNy6WWPXiG3KwBPAvlcdx16zZ0fM=
 github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
@@ -339,9 +397,8 @@ github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+d
 github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
 github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
 github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
 github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
-github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk=
-github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
 github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
 github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
 github.com/hashicorp/hil v0.0.0-20160711231837-1e86c6b523c5 h1:uk280DXEbQiCOZgCOI3elFSeNxf8YIZiNsbr2pQLYD0=
@@ -380,10 +437,12 @@ github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1
 github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
 github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
 github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
+github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
 github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
 github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28=
 github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
 github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
+github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
 github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ=
 github.com/jackc/pgx v3.3.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I=
 github.com/jarcoal/httpmock v0.0.0-20180424175123-9c70cfe4a1da h1:FjHUJJ7oBW4G/9j1KzlHaXL09LyMVM9rupS39lncbXk=
@@ -399,21 +458,28 @@ github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22
 github.com/joyent/triton-go v0.0.0-20180628001255-830d2b111e62/go.mod h1:U+RSyWxWd04xTqnuOQxnai7XGS2PrPY2cfGoDKtMHjA=
 github.com/joyent/triton-go v1.7.1-0.20200416154420-6801d15b779f h1:ENpDacvnr8faw5ugQmEF1QYk+f/Y9lXFvuYmRxykago=
 github.com/joyent/triton-go v1.7.1-0.20200416154420-6801d15b779f/go.mod h1:KDSfL7qe5ZfQqvlDMkVjCztbmcpp/c8M77vhQP8ZPvk=
+github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
 github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
 github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
 github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
-github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo=
 github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
+github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
 github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
 github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
 github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
 github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
+github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
+github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k=
+github.com/k0kubun/pp v3.0.1+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg=
 github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
 github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
 github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
 github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
-github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
 github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=
+github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
@@ -428,22 +494,29 @@ github.com/lestrrat/go-file-rotatelogs v0.0.0-20180223000712-d3151e2a480f/go.mod
 github.com/lestrrat/go-strftime v0.0.0-20180220042222-ba3bf9c1d042 h1:Bvq8AziQ5jFF4BHGAEDSqwPW1NJS3XshxbRCxtjFAZc=
 github.com/lestrrat/go-strftime v0.0.0-20180220042222-ba3bf9c1d042/go.mod h1:TPpsiPUEh0zFL1Snz4crhMlBe60PYxRHr5oFF3rRYg0=
 github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
+github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
+github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
 github.com/linode/linodego v0.7.1 h1:4WZmMpSA2NRwlPZcc0+4Gyn7rr99Evk9bnr0B3gXRKE=
 github.com/linode/linodego v0.7.1/go.mod h1:ga11n3ivecUrPCHN0rANxKmfWBJVkOXfLMZinAbj2sY=
+github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
 github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
-github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
 github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
+github.com/magiconair/properties v1.8.4 h1:8KGKTcQQGm0Kv7vEbKFErAoAOFyyacLStRtQSeYtvkY=
+github.com/magiconair/properties v1.8.4/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
 github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
 github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
 github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
-github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE=
 github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
+github.com/mattn/go-colorable v0.1.7 h1:bQGKb3vps/j0E9GfJQ03JyhRuxsvdAanXlT9BTw3mdw=
+github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
 github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
+github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
 github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
 github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
 github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
 github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
 github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
+github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
 github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
 github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
 github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
@@ -464,14 +537,14 @@ github.com/mitchellh/go-testing-interface v1.14.0 h1:/x0XQ6h+3U3nAyk1yx+bHPURrKa
 github.com/mitchellh/go-testing-interface v1.14.0/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8=
 github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
 github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
+github.com/mitchellh/hashstructure v0.0.0-20170609045927-2bca23e0e452 h1:hOY53G+kBFhbYFpRVxHl5eS7laP6B1+Cq+Z9Dry1iMU=
 github.com/mitchellh/hashstructure v0.0.0-20170609045927-2bca23e0e452/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ=
-github.com/mitchellh/hashstructure v1.0.0 h1:ZkRJX1CyOoTkar7p/mLS5TZU4nJ1Rn/F8u9dGS02Q3Y=
-github.com/mitchellh/hashstructure v1.0.0/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ=
 github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
 github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
 github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
-github.com/mitchellh/mapstructure v1.2.3 h1:f/MjBEBDLttYCGfRaKBbKSRVF5aV2O6fnBpzknuE3jU=
 github.com/mitchellh/mapstructure v1.2.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
+github.com/mitchellh/mapstructure v1.4.0 h1:7ks8ZkOP5/ujthUsT07rNv+nkLXCQWKNHuwzOAesEks=
+github.com/mitchellh/mapstructure v1.4.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
 github.com/mitchellh/pointerstructure v1.0.0 h1:ATSdz4NWrmWPOF1CeCBU4sMCno2hgqdbSrRPFWQSVZI=
 github.com/mitchellh/pointerstructure v1.0.0/go.mod h1:k4XwG94++jLVsSiTxo7qdIfXA9pj9EAeo0QsNNJOLZ8=
 github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
@@ -484,37 +557,66 @@ github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lN
 github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
 github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
 github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae h1:VeRdUYdCw49yizlSbMEn2SZ+gT+3IUKx8BqxyQdz+BY=
+github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg=
 github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
 github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
+github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
 github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
-github.com/nacos-group/nacos-sdk-go v1.0.0 h1:CufUF7DZca2ZzIrJtMMCDih1sA58BWCglArLMCZArUc=
-github.com/nacos-group/nacos-sdk-go v1.0.0/go.mod h1:hlAPn3UdzlxIlSILAyOXKxjFSvDJ9oLzTJ9hLAK1KzA=
+github.com/nacos-group/nacos-sdk-go v1.0.1 h1:VNmXGlSS28xOmkO5Nxk5WRp6f1HMosAmG9pDtcnUFcw=
+github.com/nacos-group/nacos-sdk-go v1.0.1/go.mod h1:hlAPn3UdzlxIlSILAyOXKxjFSvDJ9oLzTJ9hLAK1KzA=
+github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
+github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU=
+github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k=
+github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w=
+github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
+github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
+github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
 github.com/nicolai86/scaleway-sdk v1.10.2-0.20180628010248-798f60e20bb2 h1:BQ1HW7hr4IVovMwWg0E0PYcyW8CzqDcVmaew9cujU4s=
 github.com/nicolai86/scaleway-sdk v1.10.2-0.20180628010248-798f60e20bb2/go.mod h1:TLb2Sg7HQcgGdloNxkrmtgDNR9uVYF3lfdFIN4Ro6Sk=
+github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
 github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
 github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
+github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
 github.com/olekukonko/tablewriter v0.0.0-20180130162743-b8a9be070da4/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
 github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo=
 github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
+github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
 github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
 github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME=
 github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
-github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU=
+github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
+github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
+github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
+github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
 github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
+github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
+github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
+github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA=
+github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
+github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
+github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
 github.com/packethost/packngo v0.1.1-0.20180711074735-b9cb5096f54c h1:vwpFWvAO8DeIZfFeqASzZfsxuWPno9ncAebBEP0N3uE=
 github.com/packethost/packngo v0.1.1-0.20180711074735-b9cb5096f54c/go.mod h1:otzZQXgoO96RTzDB/Hycg0qZcXZsWJGJRSXbmEIJ+4M=
+github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM=
 github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
 github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
 github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
 github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
 github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
+github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
 github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
 github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
+github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
 github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
+github.com/philhofer/fwd v1.0.0 h1:UbZqGr5Y38ApvM/V/jEljVxwocdweyH+vmYvRPBnbqQ=
+github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
+github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
 github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
 github.com/pierrec/lz4 v2.2.6+incompatible h1:6aCX4/YZ9v8q69hTyiR7dNLnTA3fgtKHVVW5BCd5Znw=
 github.com/pierrec/lz4 v2.2.6+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
@@ -522,6 +624,7 @@ github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
 github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@@ -532,37 +635,52 @@ github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 h1:J9b7z+QKAm
 github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
 github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
 github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
+github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
 github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
 github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
-github.com/prometheus/client_golang v1.1.0 h1:BQ53HtBmfOitExawJ6LokA4x8ov/z0SYYb0+HxJfRI8=
-github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
+github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=
+github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
+github.com/prometheus/client_golang v1.8.0 h1:zvJNkoCFAnYFNC24FV8nW4JdRJ3GIFcLbg65lL/JDcw=
+github.com/prometheus/client_golang v1.8.0/go.mod h1:O9VU6huf47PktckDQfMTX0Y8tY0/7TSWwj+ITvv0TnM=
 github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
-github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE=
+github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
 github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
+github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
 github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
+github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
 github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
 github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
-github.com/prometheus/common v0.6.0 h1:kRhiuYSXR3+uv2IbVbZhUxK5zVD/2pp3Gd2PpvPkpEo=
-github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
+github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
+github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
+github.com/prometheus/common v0.14.0 h1:RHRyE8UocrbjU+6UvRzwi6HjiDfxrrBU91TtbKzkGp4=
+github.com/prometheus/common v0.14.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s=
 github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
 github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
 github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
 github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
-github.com/prometheus/procfs v0.0.3 h1:CTwfnzjQ+8dS6MhHHu4YswVAD99sL2wjPqP+VkURmKE=
-github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
+github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
+github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
+github.com/prometheus/procfs v0.2.0 h1:wH4vA7pcjKuZzjF7lM8awk4fnuJO6idemZXoKnULUx4=
+github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
 github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
 github.com/rboyer/safeio v0.2.1/go.mod h1:Cq/cEPK+YXFn622lsQ0K4KsPZSPtaptHHEldsy7Fmig=
+github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
 github.com/renier/xmlrpc v0.0.0-20170708154548-ce4a1a486c03 h1:Wdi9nwnhFNAlseAOekn6B5G/+GMtks9UKbvRU/CMM/o=
 github.com/renier/xmlrpc v0.0.0-20170708154548-ce4a1a486c03/go.mod h1:gRAiPF5C5Nd0eyyRdqIu9qTiFSoZzpTq727b5B8fkkU=
 github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
 github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
 github.com/rs/zerolog v1.4.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
 github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
+github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
 github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
 github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
 github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
+github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
 github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
 github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b h1:gQZ0qzfKHQIybLANtM3mBXNUtOfsCFXeTsnBqCsx1KM=
 github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
@@ -571,16 +689,16 @@ github.com/sean-/pager v0.0.0-20180208200047-666be9bf53b5/go.mod h1:BeybITEsBEg6
 github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I=
 github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
 github.com/shirou/gopsutil v0.0.0-20181107111621-48177ef5f880/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
-github.com/shirou/gopsutil v2.19.9+incompatible h1:IrPVlK4nfwW10DF7pW+7YJKws9NkgNzWozwwWv9FsgY=
-github.com/shirou/gopsutil v2.19.9+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
-github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4 h1:udFKJ0aHUL60LboW/A+DfgoHVedieIzIXE8uylPue0U=
+github.com/shirou/gopsutil v3.20.11+incompatible h1:LJr4ZQK4mPpIV5gOa4jCOKOGb4ty4DZO54I4FGqIpto=
+github.com/shirou/gopsutil v3.20.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
 github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc=
 github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
+github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
 github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
 github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
-github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
-github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
 github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
+github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=
+github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
 github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
 github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
 github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
@@ -590,6 +708,7 @@ github.com/softlayer/softlayer-go v0.0.0-20180806151055-260589d94c7d h1:bVQRCxQv
 github.com/softlayer/softlayer-go v0.0.0-20180806151055-260589d94c7d/go.mod h1:Cw4GTlQccdRGSEf6KiMju767x0NEHE0YIVPJSaXjlsw=
 github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E=
 github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
+github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
 github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
 github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
 github.com/spf13/afero v1.2.1/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
@@ -597,17 +716,22 @@ github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
 github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
 github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
 github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
+github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
 github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
 github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
 github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
 github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
 github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
 github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
 github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
-github.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM=
-github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
+github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk=
+github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
+github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
+github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
+github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
@@ -616,8 +740,9 @@ github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRci
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
-github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
 github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
 github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
 github.com/tebeka/strftime v0.1.3 h1:5HQXOqWKYRFfNyBMNVc9z5+QzuBtIXy03psIhtdJYto=
@@ -627,6 +752,9 @@ github.com/tencentcloud/tencentcloud-sdk-go v3.0.83+incompatible/go.mod h1:0PfYo
 github.com/tent/http-link-go v0.0.0-20130702225549-ac974c61c2f9/go.mod h1:RHkNRtSLfOK7qBTHaeSX1D6BNpI3qw7NTxsmNr4RvN8=
 github.com/tevid/gohamcrest v1.1.1 h1:ou+xSqlIw1xfGTg1uq1nif/htZ2S3EzRqLm2BP+tYU0=
 github.com/tevid/gohamcrest v1.1.1/go.mod h1:3UvtWlqm8j5JbwYZh80D/PVBt0mJ1eJiYgZMibh0H/k=
+github.com/tinylib/msgp v1.1.0 h1:9fQd+ICuRIu/ue4vxJZu6/LzxN0HwMds2nq/0cFvxHU=
+github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
+github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
 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=
@@ -635,30 +763,47 @@ github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926 h1:G3dpKMzFDjgEh2q1Z
 github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
 github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
 github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
+github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
+github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
 github.com/vmware/govmomi v0.18.0 h1:f7QxSmP7meCtoAmiKZogvVbLInT+CZx6Px6K5rYsJZo=
 github.com/vmware/govmomi v0.18.0/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU=
+github.com/willf/bitset v1.1.10 h1:NotGKqX0KwQ72NUzqrjZq5ipPNDQex9lo3WpaS8L2sc=
+github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
 github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=
 github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
 github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
-github.com/zouyx/agollo/v3 v3.4.4 h1:5G7QNw3fw74Ns8SfnHNhjndV2mlz5Fg8bB7q84ydFYI=
-github.com/zouyx/agollo/v3 v3.4.4/go.mod h1:ag0XmE1r4iAgPd6PUnU9TJ0DMEjM1VKX1HUNqQJ2ywU=
+github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/zouyx/agollo/v3 v3.4.5 h1:7YCxzY9ZYaH9TuVUBvmI6Tk0mwMggikah+cfbYogcHQ=
+github.com/zouyx/agollo/v3 v3.4.5/go.mod h1:LJr3kDmm23QSW+F1Ol4TMHDa7HvJvscMdVxJ2IpUTVc=
 go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
+go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk=
+go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
 go.etcd.io/bbolt v1.3.4 h1:hi1bXHMVrlQh6WwxAy+qZCV/SYIlqo+Ushwdpa4tAKg=
 go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
+go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
+go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
+go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
 go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
-go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4=
 go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
+go.opencensus.io v0.22.2 h1:75k/FF0Q2YM8QYo07VPddOLBslDt1MZOdEslOHvmzAs=
+go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
+go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
 go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
-go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=
+go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
 go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
+go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
+go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
 go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
+go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
 go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=
 go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
 go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
 go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
 go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
-go.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM=
+go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
 go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=
+go.uber.org/zap v1.16.0 h1:uFRZXykJGK9lLY4HtgSw44DnIcAM+kRBP7x5m+NpAOM=
+go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ=
 golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@@ -667,10 +812,13 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
 golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975 h1:/Tl7pH94bvbAAHBdZJT947M/+gp0+CqQXDtMRC0fseo=
 golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -684,12 +832,16 @@ golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTk
 golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
 golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
 golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
 golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k=
+golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
 golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
 golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
 golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
 golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
+golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
+golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -699,6 +851,7 @@ 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-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@@ -708,11 +861,14 @@ golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR
 golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
 golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8=
 golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA=
+golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
@@ -722,8 +878,9 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ
 golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
 golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA=
+golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -732,6 +889,7 @@ golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5h
 golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -748,27 +906,39 @@ golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20190523142557-0e01d883c5c5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8=
 golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201223074533-0d417f636930 h1:vRgIt+nup/B/BwIS0g2oC0haq0iqbV3ZA+u6+0TlNCo=
+golang.org/x/sys v0.0.0-20201223074533-0d417f636930/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
+golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -789,22 +959,29 @@ golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtn
 golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs=
 golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc h1:NCy3Ohtk6Iny5V/reW2Ktypo4zIpWBdRJ1uFMjBxdg8=
 golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200928182047-19e03678916f h1:VwGa2Wf+rHGIxvsssCkUNIyFv8jQY0VCBCNWtikoWq0=
+golang.org/x/tools v0.0.0-20200928182047-19e03678916f/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
 google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
-google.golang.org/api v0.7.0 h1:9sdfJOzWlkqPltHAuzT2Cp+yrBeY1KRVYgms8soxMwM=
 google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
 google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
 google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
 google.golang.org/api v0.13.0 h1:Q3Ui3V3/CVinFWFiW39Iw0kMuVrRzYX0wN6OPFp0lTA=
 google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
 google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
-google.golang.org/appengine v1.6.0 h1:Tfd7cKwKbFRsI8RMAD3oqqw7JPFRrvFlOsfbgVkjOOw=
 google.golang.org/appengine v1.6.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 google.golang.org/appengine v1.6.1 h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I=
 google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
@@ -815,21 +992,32 @@ google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRn
 google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
-google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64 h1:iKtrH9Y8mcbADOP0YFaEMth7OfuHY9xHOwNj4znpM1A=
 google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
 google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
 google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
 google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a h1:Ob5/580gVHBJZgXnff1cZDbG+xLtMVE5mDRTe+nIsX4=
 google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
 google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
+google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
 google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
 google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
 google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
 google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
-google.golang.org/grpc v1.23.0 h1:AzbTB6ux+okLTzP8Ru1Xs41C303zdcfEht7MQnYJt5A=
+google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
 google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.26.0 h1:2dTRdpdFEEhJYQD8EMLB61nnrzSCTbG38PhqdhvOltg=
+google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
+google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
+google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
+google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
+google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
+google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
+google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
 gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
 gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
 gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw=
@@ -837,13 +1025,14 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
 gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
 gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
 gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
+gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
 gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
 gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
 gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
-gopkg.in/ini.v1 v1.42.0 h1:7N3gPTt50s8GuLortA00n8AqRTk75qOP98+mTPpgzRk=
 gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
 gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=
 gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
@@ -854,12 +1043,19 @@ gopkg.in/square/go-jose.v2 v2.4.1 h1:H0TmLt7/KmzlrDOpa1F+zr0Tk90PbJYBfsVUmRLrf9Y
 gopkg.in/square/go-jose.v2 v2.4.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
+gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
 gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
 gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
+gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
+gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
@@ -883,7 +1079,9 @@ k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a h1:UcxjrRMyNx/i/y8G7kPvLy
 k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
 k8s.io/utils v0.0.0-20190801114015-581e00157fb1 h1:+ySTxfHnfzZb9ys375PXNlLhkJPLKgHajBU0N62BDvE=
 k8s.io/utils v0.0.0-20190801114015-581e00157fb1/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
+launchpad.net/gocheck v0.0.0-20140225173054-000000000087/go.mod h1:hj7XX3B/0A+80Vse0e+BUHsHMTEhd0O4cpUHr/e/BUM=
 rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
 sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
 sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
 sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
+sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
diff --git a/integrate_test.sh b/integrate_test.sh
index c9c2f23b5b07f0baf96260d8092e7464d4d15659..deccda756a211821978e35b92a1f0865858ff59a 100644
--- a/integrate_test.sh
+++ b/integrate_test.sh
@@ -63,4 +63,4 @@ docker build . -t  ci-consumer --build-arg PR_ORIGIN_REPO=${TRAVIS_PULL_REQUEST_
 cd ${ROOT_DIR}
 # run provider
 # check consumer status
-docker run -it --network host ci-consumer
+docker run -i --network host ci-consumer
diff --git a/metadata/definition/definition.go b/metadata/definition/definition.go
index dbbc0c8f1685edf1a26ab1fe4ad091c501e76f5f..a0323137b70ff6ea34249b10e82d4f5cd84c9cb0 100644
--- a/metadata/definition/definition.go
+++ b/metadata/definition/definition.go
@@ -93,7 +93,7 @@ type TypeDefinition struct {
 }
 
 // BuildServiceDefinition can build service definition which will be used to describe a service
-func BuildServiceDefinition(service common.Service, url common.URL) *ServiceDefinition {
+func BuildServiceDefinition(service common.Service, url *common.URL) *ServiceDefinition {
 	sd := &ServiceDefinition{}
 	sd.CanonicalName = url.Service()
 
diff --git a/metadata/definition/definition_test.go b/metadata/definition/definition_test.go
index 958f9324d0f9f4a5027792bd8e54b238a5f56feb..389159239b4f4d7156983c83096ead151561ad28 100644
--- a/metadata/definition/definition_test.go
+++ b/metadata/definition/definition_test.go
@@ -28,7 +28,6 @@ import (
 
 import (
 	"github.com/apache/dubbo-go/common"
-	"github.com/apache/dubbo-go/common/constant"
 )
 
 func TestBuildServiceDefinition(t *testing.T) {
@@ -44,9 +43,9 @@ func TestBuildServiceDefinition(t *testing.T) {
 			"owner=ZX&pid=1447&revision=0.0.1&side=provider&timeout=3000&timestamp=1556509797245&group=%v&version=%v&bean.name=%v",
 		protocol, serviceName, group, version, beanName))
 	assert.NoError(t, err)
-	_, err = common.ServiceMap.Register(serviceName, protocol, &UserProvider{})
+	_, err = common.ServiceMap.Register(serviceName, protocol, group, version, &UserProvider{})
 	assert.NoError(t, err)
-	service := common.ServiceMap.GetService(url.Protocol, url.GetParam(constant.BEAN_NAME_KEY, url.Service()))
+	service := common.ServiceMap.GetServiceByServiceKey(url.Protocol, url.ServiceKey())
 	sd := BuildServiceDefinition(*service, url)
 	assert.Equal(t, "{canonicalName:com.ikurento.user.UserProvider, codeSource:, methods:[{name:GetUser,parameterTypes:[{type:slice}],returnType:ptr,params:[] }], types:[]}", sd.String())
 }
diff --git a/metadata/identifier/service_metadata_identifier.go b/metadata/identifier/service_metadata_identifier.go
index b9e65967e0f707a6efcc9f8ded2ce5dec4f058b8..3035cf3d720bd453baecb147c605f1bfc928d825 100644
--- a/metadata/identifier/service_metadata_identifier.go
+++ b/metadata/identifier/service_metadata_identifier.go
@@ -32,7 +32,7 @@ type ServiceMetadataIdentifier struct {
 // NewServiceMetadataIdentifier create instance.
 // The ServiceInterface is the @url.Service()
 // other parameters are read from @url
-func NewServiceMetadataIdentifier(url common.URL) *ServiceMetadataIdentifier {
+func NewServiceMetadataIdentifier(url *common.URL) *ServiceMetadataIdentifier {
 	return &ServiceMetadataIdentifier{
 		BaseMetadataIdentifier: BaseMetadataIdentifier{
 			ServiceInterface: url.Service(),
diff --git a/metadata/report/consul/report.go b/metadata/report/consul/report.go
index eb2bdc25ecec596a8f89abb80856b8d6e7be70a4..e211f7fde4d462bd20603af54444036681a46f4d 100644
--- a/metadata/report/consul/report.go
+++ b/metadata/report/consul/report.go
@@ -61,7 +61,7 @@ func (m *consulMetadataReport) StoreConsumerMetadata(consumerMetadataIdentifier
 }
 
 // SaveServiceMetadata saves the metadata.
-func (m *consulMetadataReport) SaveServiceMetadata(metadataIdentifier *identifier.ServiceMetadataIdentifier, url common.URL) error {
+func (m *consulMetadataReport) SaveServiceMetadata(metadataIdentifier *identifier.ServiceMetadataIdentifier, url *common.URL) error {
 	kv := &consul.KVPair{Key: metadataIdentifier.GetIdentifierKey(), Value: []byte(url.String())}
 	_, err := m.client.KV().Put(kv, nil)
 	return err
diff --git a/metadata/report/consul/report_test.go b/metadata/report/consul/report_test.go
index e07a7429f12bac0cfa4b2701d3e87ef25bcb077a..13d0c419bc0f0ff8426f73d4de5c85a346416770 100644
--- a/metadata/report/consul/report_test.go
+++ b/metadata/report/consul/report_test.go
@@ -100,7 +100,7 @@ func (suite *consulMetadataReportTestSuite) testStoreConsumerMetadata() {
 	assert.NoError(suite.t, err)
 }
 
-func (suite *consulMetadataReportTestSuite) testSaveServiceMetadata(url common.URL) {
+func (suite *consulMetadataReportTestSuite) testSaveServiceMetadata(url *common.URL) {
 	serviceMi := newServiceMetadataIdentifier("provider")
 	err := suite.m.SaveServiceMetadata(serviceMi, url)
 	assert.NoError(suite.t, err)
@@ -119,7 +119,7 @@ func (suite *consulMetadataReportTestSuite) testGetExportedURLs() {
 	assert.NoError(suite.t, err)
 }
 
-func (suite *consulMetadataReportTestSuite) testSaveSubscribedData(url common.URL) {
+func (suite *consulMetadataReportTestSuite) testSaveSubscribedData(url *common.URL) {
 	subscribeMi := newSubscribeMetadataIdentifier("provider")
 	urls := []string{url.String()}
 	bytes, _ := json.Marshal(urls)
@@ -152,10 +152,10 @@ func test1(t *testing.T) {
 	suite := newConsulMetadataReportTestSuite(t, m)
 	suite.testStoreProviderMetadata()
 	suite.testStoreConsumerMetadata()
-	suite.testSaveServiceMetadata(*url)
+	suite.testSaveServiceMetadata(url)
 	suite.testGetExportedURLs()
 	suite.testRemoveServiceMetadata()
-	suite.testSaveSubscribedData(*url)
+	suite.testSaveSubscribedData(url)
 	suite.testGetSubscribedURLs()
 	suite.testGetServiceDefinition()
 }
diff --git a/metadata/report/delegate/delegate_report.go b/metadata/report/delegate/delegate_report.go
index cdd29ab2e483647ed90f37032e10d174f7583e39..836a8f9ef4aac23558e44c1c9c5adedd585d6da4 100644
--- a/metadata/report/delegate/delegate_report.go
+++ b/metadata/report/delegate/delegate_report.go
@@ -94,7 +94,7 @@ func (mrr *metadataReportRetry) startRetryTask() {
 
 // MetadataReport is a absolute delegate for MetadataReport
 type MetadataReport struct {
-	reportUrl           common.URL
+	reportUrl           *common.URL
 	syncReport          bool
 	metadataReportRetry *metadataReportRetry
 
@@ -215,7 +215,7 @@ func (mr *MetadataReport) StoreConsumerMetadata(identifier *identifier.MetadataI
 }
 
 // SaveServiceMetadata will delegate to call remote metadata's sdk to save service metadata
-func (mr *MetadataReport) SaveServiceMetadata(identifier *identifier.ServiceMetadataIdentifier, url common.URL) error {
+func (mr *MetadataReport) SaveServiceMetadata(identifier *identifier.ServiceMetadataIdentifier, url *common.URL) error {
 	report := instance.GetMetadataReportInstance()
 	if mr.syncReport {
 		return report.SaveServiceMetadata(identifier, url)
@@ -241,7 +241,7 @@ func (mr *MetadataReport) GetExportedURLs(identifier *identifier.ServiceMetadata
 }
 
 // SaveSubscribedData will delegate to call remote metadata's sdk to save subscribed data
-func (mr *MetadataReport) SaveSubscribedData(identifier *identifier.SubscriberMetadataIdentifier, urls []common.URL) error {
+func (mr *MetadataReport) SaveSubscribedData(identifier *identifier.SubscriberMetadataIdentifier, urls []*common.URL) error {
 	urlStrList := make([]string, 0, len(urls))
 	for _, url := range urls {
 		urlStrList = append(urlStrList, url.String())
@@ -260,13 +260,13 @@ func (mr *MetadataReport) SaveSubscribedData(identifier *identifier.SubscriberMe
 }
 
 // GetSubscribedURLs will delegate to call remote metadata's sdk to get subscribed urls
-func (MetadataReport) GetSubscribedURLs(identifier *identifier.SubscriberMetadataIdentifier) ([]string, error) {
+func (mr *MetadataReport) GetSubscribedURLs(identifier *identifier.SubscriberMetadataIdentifier) ([]string, error) {
 	report := instance.GetMetadataReportInstance()
 	return report.GetSubscribedURLs(identifier)
 }
 
 // GetServiceDefinition will delegate to call remote metadata's sdk to get service definitions
-func (MetadataReport) GetServiceDefinition(identifier *identifier.MetadataIdentifier) (string, error) {
+func (mr *MetadataReport) GetServiceDefinition(identifier *identifier.MetadataIdentifier) (string, error) {
 	report := instance.GetMetadataReportInstance()
 	return report.GetServiceDefinition(identifier)
 }
diff --git a/metadata/report/delegate/delegate_report_test.go b/metadata/report/delegate/delegate_report_test.go
index 3dfca577ba06598b90c553048777951c8823b256..9c30ed9ffdb8424be38d273fd0a78f3351d6e0d5 100644
--- a/metadata/report/delegate/delegate_report_test.go
+++ b/metadata/report/delegate/delegate_report_test.go
@@ -116,8 +116,8 @@ func getMockDefinition(id *identifier.MetadataIdentifier, t *testing.T) *definit
 			"owner=ZX&pid=1447&revision=0.0.1&side=provider&timeout=3000&timestamp=1556509797245&group=%v&version=%v&bean.name=%v",
 		protocol, id.ServiceInterface, id.Group, id.Version, beanName))
 	assert.NoError(t, err)
-	_, err = common.ServiceMap.Register(id.ServiceInterface, protocol, &definition.UserProvider{})
+	_, err = common.ServiceMap.Register(id.ServiceInterface, protocol, id.Group, id.Version, &definition.UserProvider{})
 	assert.NoError(t, err)
-	service := common.ServiceMap.GetService(url.Protocol, url.GetParam(constant.BEAN_NAME_KEY, url.Service()))
+	service := common.ServiceMap.GetServiceByServiceKey(url.Protocol, url.ServiceKey())
 	return definition.BuildServiceDefinition(*service, url)
 }
diff --git a/metadata/report/etcd/report.go b/metadata/report/etcd/report.go
index 097835c986962850d137255ec807b417de1484ad..1939b911822b6b5db3d41f999d4dcb4ca3fae1f2 100644
--- a/metadata/report/etcd/report.go
+++ b/metadata/report/etcd/report.go
@@ -63,7 +63,7 @@ func (e *etcdMetadataReport) StoreConsumerMetadata(consumerMetadataIdentifier *i
 
 // SaveServiceMetadata will store the metadata
 // metadata including the basic info of the server, service info, and other user custom info
-func (e *etcdMetadataReport) SaveServiceMetadata(metadataIdentifier *identifier.ServiceMetadataIdentifier, url common.URL) error {
+func (e *etcdMetadataReport) SaveServiceMetadata(metadataIdentifier *identifier.ServiceMetadataIdentifier, url *common.URL) error {
 	key := e.getNodeKey(metadataIdentifier)
 	return e.client.Create(key, url.String())
 }
diff --git a/metadata/report/etcd/report_test.go b/metadata/report/etcd/report_test.go
index 5dd8780b48a741a8eb8735b059bc3617a100f63d..28c04869822b166f99d98e91ec6ec3b7fe6626cc 100644
--- a/metadata/report/etcd/report_test.go
+++ b/metadata/report/etcd/report_test.go
@@ -60,7 +60,7 @@ func TestEtcdMetadataReportFactory_CreateMetadataReport(t *testing.T) {
 		t.Fatal(err)
 	}
 	metadataReportFactory := &etcdMetadataReportFactory{}
-	metadataReport := metadataReportFactory.CreateMetadataReport(&url)
+	metadataReport := metadataReportFactory.CreateMetadataReport(url)
 	assert.NotNil(t, metadataReport)
 	e.Close()
 }
@@ -72,7 +72,7 @@ func TestEtcdMetadataReport_CRUD(t *testing.T) {
 		t.Fatal(err)
 	}
 	metadataReportFactory := &etcdMetadataReportFactory{}
-	metadataReport := metadataReportFactory.CreateMetadataReport(&url)
+	metadataReport := metadataReportFactory.CreateMetadataReport(url)
 	assert.NotNil(t, metadataReport)
 
 	err = metadataReport.StoreConsumerMetadata(newMetadataIdentifier("consumer"), "consumer metadata")
diff --git a/metadata/report/nacos/report.go b/metadata/report/nacos/report.go
index d69913bd8fbb04da2d50770c1196917cb1efdaa5..42e9859c9fae70cc110dc45fca43dd95a5627d6e 100644
--- a/metadata/report/nacos/report.go
+++ b/metadata/report/nacos/report.go
@@ -69,7 +69,7 @@ func (n *nacosMetadataReport) StoreConsumerMetadata(consumerMetadataIdentifier *
 }
 
 // SaveServiceMetadata saves the metadata.
-func (n *nacosMetadataReport) SaveServiceMetadata(metadataIdentifier *identifier.ServiceMetadataIdentifier, url common.URL) error {
+func (n *nacosMetadataReport) SaveServiceMetadata(metadataIdentifier *identifier.ServiceMetadataIdentifier, url *common.URL) error {
 	return n.storeMetadata(vo.ConfigParam{
 		DataId:  metadataIdentifier.GetIdentifierKey(),
 		Group:   metadataIdentifier.Group,
diff --git a/metadata/report/nacos/report_test.go b/metadata/report/nacos/report_test.go
index be01eb22f7e95966c3bf816fdf648629b64380a3..1b7075ec78e4ac496983564b684d8776e1476b63 100644
--- a/metadata/report/nacos/report_test.go
+++ b/metadata/report/nacos/report_test.go
@@ -19,8 +19,10 @@ package nacos
 
 import (
 	"encoding/json"
+	"net/http"
 	"strconv"
 	"testing"
+	"time"
 )
 
 import (
@@ -36,6 +38,9 @@ import (
 )
 
 func TestNacosMetadataReport_CRUD(t *testing.T) {
+	if !checkNacosServerAlive() {
+		return
+	}
 	rpt := newTestReport()
 	assert.NotNil(t, rpt)
 
@@ -111,6 +116,14 @@ func TestNacosMetadataReportFactory_CreateMetadataReport(t *testing.T) {
 
 func newTestReport() report.MetadataReport {
 	regurl, _ := common.NewURL("registry://console.nacos.io:80", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER)))
-	res := extension.GetMetadataReportFactory("nacos").CreateMetadataReport(&regurl)
+	res := extension.GetMetadataReportFactory("nacos").CreateMetadataReport(regurl)
 	return res
 }
+
+func checkNacosServerAlive() bool {
+	c := http.Client{Timeout: time.Second}
+	if _, err := c.Get("http://console.nacos.io/nacos/"); err != nil {
+		return false
+	}
+	return true
+}
diff --git a/metadata/report/report.go b/metadata/report/report.go
index 62a9055e843297bd0d69ad94cb09ece64efda85f..dcb414209c1e6188dc195e9609f60124adaf2173 100644
--- a/metadata/report/report.go
+++ b/metadata/report/report.go
@@ -38,7 +38,7 @@ type MetadataReport interface {
 	// SaveServiceMetadata saves the metadata.
 	// Metadata includes the basic info of the server,
 	// service info, and other user custom info.
-	SaveServiceMetadata(*identifier.ServiceMetadataIdentifier, common.URL) error
+	SaveServiceMetadata(*identifier.ServiceMetadataIdentifier, *common.URL) error
 
 	// RemoveServiceMetadata removes the metadata.
 	RemoveServiceMetadata(*identifier.ServiceMetadataIdentifier) error
diff --git a/metadata/report/zookeeper/report.go b/metadata/report/zookeeper/report.go
index 8f46bb023054ec27ca0dbff2c249dc0135af07cd..5d5e74007b9901276d7144bf0b052018e2f7e0a7 100644
--- a/metadata/report/zookeeper/report.go
+++ b/metadata/report/zookeeper/report.go
@@ -63,7 +63,7 @@ func (m *zookeeperMetadataReport) StoreConsumerMetadata(consumerMetadataIdentifi
 }
 
 // SaveServiceMetadata saves the metadata.
-func (m *zookeeperMetadataReport) SaveServiceMetadata(metadataIdentifier *identifier.ServiceMetadataIdentifier, url common.URL) error {
+func (m *zookeeperMetadataReport) SaveServiceMetadata(metadataIdentifier *identifier.ServiceMetadataIdentifier, url *common.URL) error {
 	k := m.rootDir + metadataIdentifier.GetFilePathKey()
 	return m.client.CreateWithValue(k, []byte(url.String()))
 }
diff --git a/metadata/report/zookeeper/report_test.go b/metadata/report/zookeeper/report_test.go
index a1e46e2e8d019c0415699ee409833b392a85b504..adedaaa6f4bd71511385085118f55538a5deb091 100644
--- a/metadata/report/zookeeper/report_test.go
+++ b/metadata/report/zookeeper/report_test.go
@@ -100,7 +100,7 @@ func (suite *zookeeperMetadataReportTestSuite) testStoreConsumerMetadata() {
 	assert.NoError(suite.t, err)
 }
 
-func (suite *zookeeperMetadataReportTestSuite) testSaveServiceMetadata(url common.URL) {
+func (suite *zookeeperMetadataReportTestSuite) testSaveServiceMetadata(url *common.URL) {
 	serviceMi := newServiceMetadataIdentifier("provider")
 	err := suite.m.SaveServiceMetadata(serviceMi, url)
 	assert.NoError(suite.t, err)
@@ -119,7 +119,7 @@ func (suite *zookeeperMetadataReportTestSuite) testGetExportedURLs() {
 	assert.NoError(suite.t, err)
 }
 
-func (suite *zookeeperMetadataReportTestSuite) testSaveSubscribedData(url common.URL) {
+func (suite *zookeeperMetadataReportTestSuite) testSaveSubscribedData(url *common.URL) {
 	subscribeMi := newSubscribeMetadataIdentifier("provider")
 	urls := []string{url.String()}
 	bytes, _ := json.Marshal(urls)
@@ -153,10 +153,10 @@ func test1(t *testing.T) {
 	suite := newZookeeperMetadataReportTestSuite(t, m)
 	suite.testStoreProviderMetadata()
 	suite.testStoreConsumerMetadata()
-	suite.testSaveServiceMetadata(*url)
+	suite.testSaveServiceMetadata(url)
 	suite.testGetExportedURLs()
 	suite.testRemoveServiceMetadata()
-	suite.testSaveSubscribedData(*url)
+	suite.testSaveSubscribedData(url)
 	suite.testGetSubscribedURLs()
 	suite.testGetServiceDefinition()
 }
diff --git a/metadata/service/exporter/configurable/exporter.go b/metadata/service/exporter/configurable/exporter.go
index f8b4b0c0174cb0e5a8753b814f89ed4d332e2fbe..75e52d8d1b833f95d7f6f09a778bd916bd4afee9 100644
--- a/metadata/service/exporter/configurable/exporter.go
+++ b/metadata/service/exporter/configurable/exporter.go
@@ -19,6 +19,7 @@ package configurable
 
 import (
 	"context"
+	"errors"
 	"sync"
 )
 
@@ -46,13 +47,18 @@ func NewMetadataServiceExporter(metadataService service.MetadataService) exporte
 }
 
 // Export will export the metadataService
-func (exporter *MetadataServiceExporter) Export() error {
+func (exporter *MetadataServiceExporter) Export(url *common.URL) error {
 	if !exporter.IsExported() {
-
 		serviceConfig := config.NewServiceConfig(constant.SIMPLE_METADATA_SERVICE_NAME, context.Background())
 		serviceConfig.Protocol = constant.DEFAULT_PROTOCOL
+		if url == nil || url.SubURL == nil {
+			return errors.New("metadata server url is nil, pls check your configuration")
+		}
 		serviceConfig.Protocols = map[string]*config.ProtocolConfig{
-			constant.DEFAULT_PROTOCOL: generateMetadataProtocol(),
+			constant.DEFAULT_PROTOCOL: {
+				Name: url.SubURL.Protocol,
+				Port: url.SubURL.Port,
+			},
 		}
 		serviceConfig.InterfaceName = constant.METADATA_SERVICE_NAME
 		// identify this is a golang server
@@ -96,11 +102,3 @@ func (exporter *MetadataServiceExporter) IsExported() bool {
 	defer exporter.lock.RUnlock()
 	return exporter.ServiceConfig != nil && exporter.ServiceConfig.IsExport()
 }
-
-// generateMetadataProtocol will return a default ProtocolConfig
-func generateMetadataProtocol() *config.ProtocolConfig {
-	return &config.ProtocolConfig{
-		Name: constant.DEFAULT_PROTOCOL,
-		Port: "20000",
-	}
-}
diff --git a/metadata/service/exporter/configurable/exporter_test.go b/metadata/service/exporter/configurable/exporter_test.go
index 9fdbd76757815af0aa975ec6e5f1b20fa5f1a83e..7c2baa2728b4c4888d9dbb117816648d72874c0b 100644
--- a/metadata/service/exporter/configurable/exporter_test.go
+++ b/metadata/service/exporter/configurable/exporter_test.go
@@ -26,19 +26,20 @@ import (
 )
 
 import (
+	"github.com/apache/dubbo-go/common"
 	_ "github.com/apache/dubbo-go/common/proxy/proxy_factory"
 	"github.com/apache/dubbo-go/config"
 	_ "github.com/apache/dubbo-go/filter/filter_impl"
 	"github.com/apache/dubbo-go/metadata/service/inmemory"
-	"github.com/apache/dubbo-go/protocol/dubbo"
 	_ "github.com/apache/dubbo-go/protocol/dubbo"
+	"github.com/apache/dubbo-go/remoting/getty"
 )
 
 func TestConfigurableExporter(t *testing.T) {
-	dubbo.SetServerConfig(dubbo.ServerConfig{
+	getty.SetServerConfig(getty.ServerConfig{
 		SessionNumber:  700,
 		SessionTimeout: "20s",
-		GettySessionParam: dubbo.GettySessionParam{
+		GettySessionParam: getty.GettySessionParam{
 			CompressEncoding: false,
 			TcpNoDelay:       true,
 			TcpKeepAlive:     true,
@@ -55,12 +56,23 @@ func TestConfigurableExporter(t *testing.T) {
 	mockInitProviderWithSingleRegistry()
 	metadataService, _ := inmemory.NewMetadataService()
 	exported := NewMetadataServiceExporter(metadataService)
-	assert.Equal(t, false, exported.IsExported())
-	assert.NoError(t, exported.Export())
-	assert.Equal(t, true, exported.IsExported())
-	assert.Regexp(t, "dubbo://:20000/MetadataService*", exported.GetExportedURLs()[0].String())
-	exported.Unexport()
-	assert.Equal(t, false, exported.IsExported())
+
+	t.Run("configurableExporterUrlNil", func(t *testing.T) {
+		assert.Equal(t, false, exported.IsExported())
+		assert.Error(t, exported.Export(nil), "metadata server url is nil, pls check your configuration")
+	})
+
+	t.Run("configurableExporter", func(t *testing.T) {
+		registryURL, _ := common.NewURL("service-discovery://localhost:12345")
+		subURL, _ := common.NewURL("dubbo://localhost:20003")
+		registryURL.SubURL = subURL
+		assert.Equal(t, false, exported.IsExported())
+		assert.NoError(t, exported.Export(registryURL))
+		assert.Equal(t, true, exported.IsExported())
+		assert.Regexp(t, "dubbo://:20003/org.apache.dubbo.metadata.MetadataService*", exported.GetExportedURLs()[0].String())
+		exported.Unexport()
+		assert.Equal(t, false, exported.IsExported())
+	})
 }
 
 // mockInitProviderWithSingleRegistry will init a mocked providerConfig
diff --git a/metadata/service/exporter/exporter.go b/metadata/service/exporter/exporter.go
index cfdef3a0e79d29ce31717c0fc3c575e9e4ba1759..33ceaca467220d1c0e39225abb006485e026f961 100644
--- a/metadata/service/exporter/exporter.go
+++ b/metadata/service/exporter/exporter.go
@@ -23,7 +23,7 @@ import (
 
 // MetadataServiceExporter will export & unexport the metadata service,  get exported url, and return is exported or not
 type MetadataServiceExporter interface {
-	Export() error
+	Export(url *common.URL) error
 	Unexport()
 	GetExportedURLs() []*common.URL
 	IsExported() bool
diff --git a/metadata/service/inmemory/metadata_service_proxy_factory.go b/metadata/service/inmemory/metadata_service_proxy_factory.go
index 1f8eeaa55f4a0240746508fee2ff088e3a653ca5..becd804f6741e27dda418a4ac9ce976755c634c4 100644
--- a/metadata/service/inmemory/metadata_service_proxy_factory.go
+++ b/metadata/service/inmemory/metadata_service_proxy_factory.go
@@ -50,7 +50,7 @@ func createProxy(ins registry.ServiceInstance) service.MetadataService {
 
 	u := urls[0]
 	p := extension.GetProtocol(u.Protocol)
-	invoker := p.Refer(*u)
+	invoker := p.Refer(u)
 	return &MetadataServiceProxy{
 		invkr: invoker,
 	}
diff --git a/metadata/service/inmemory/metadata_service_proxy_factory_test.go b/metadata/service/inmemory/metadata_service_proxy_factory_test.go
index 96020e1eb762442f946ccf8b368d6ebe9429d05e..f5e519cd5d2f83b85eaccf636e6771c18c74c277 100644
--- a/metadata/service/inmemory/metadata_service_proxy_factory_test.go
+++ b/metadata/service/inmemory/metadata_service_proxy_factory_test.go
@@ -66,11 +66,11 @@ func TestCreateProxy(t *testing.T) {
 type mockProtocol struct {
 }
 
-func (m mockProtocol) Export(invoker protocol.Invoker) protocol.Exporter {
+func (m mockProtocol) Export(protocol.Invoker) protocol.Exporter {
 	panic("implement me")
 }
 
-func (m mockProtocol) Refer(url common.URL) protocol.Invoker {
+func (m mockProtocol) Refer(*common.URL) protocol.Invoker {
 	return &mockInvoker{}
 }
 
@@ -81,7 +81,7 @@ func (m mockProtocol) Destroy() {
 type mockInvoker struct {
 }
 
-func (m *mockInvoker) GetUrl() common.URL {
+func (m *mockInvoker) GetUrl() *common.URL {
 	panic("implement me")
 }
 
diff --git a/metadata/service/inmemory/service.go b/metadata/service/inmemory/service.go
index 8269e691f1794fd9ac4b6091c157539e39ad7072..8da78c34207c37a7f7e3f475502ac43e7d88b6fd 100644
--- a/metadata/service/inmemory/service.go
+++ b/metadata/service/inmemory/service.go
@@ -22,7 +22,6 @@ import (
 )
 
 import (
-	cm "github.com/Workiva/go-datastructures/common"
 	"github.com/Workiva/go-datastructures/slice/skip"
 )
 
@@ -75,23 +74,6 @@ func NewMetadataService() (service.MetadataService, error) {
 	return metadataServiceInstance, nil
 }
 
-// Comparator is defined as Comparator for skip list to compare the URL
-type Comparator common.URL
-
-// Compare is defined as Comparator for skip list to compare the URL
-func (c Comparator) Compare(comp cm.Comparator) int {
-	a := common.URL(c).String()
-	b := common.URL(comp.(Comparator)).String()
-	switch {
-	case a > b:
-		return 1
-	case a < b:
-		return -1
-	default:
-		return 0
-	}
-}
-
 // addURL will add URL in memory
 func (mts *MetadataService) addURL(targetMap *sync.Map, url *common.URL) bool {
 	var (
@@ -101,7 +83,7 @@ func (mts *MetadataService) addURL(targetMap *sync.Map, url *common.URL) bool {
 	logger.Debug(url.ServiceKey())
 	if urlSet, loaded = targetMap.LoadOrStore(url.ServiceKey(), skip.New(uint64(0))); loaded {
 		mts.lock.RLock()
-		wantedUrl := urlSet.(*skip.SkipList).Get(Comparator(*url))
+		wantedUrl := urlSet.(*skip.SkipList).Get(url)
 		if len(wantedUrl) > 0 && wantedUrl[0] != nil {
 			mts.lock.RUnlock()
 			return false
@@ -110,12 +92,12 @@ func (mts *MetadataService) addURL(targetMap *sync.Map, url *common.URL) bool {
 	}
 	mts.lock.Lock()
 	// double chk
-	wantedUrl := urlSet.(*skip.SkipList).Get(Comparator(*url))
+	wantedUrl := urlSet.(*skip.SkipList).Get(url)
 	if len(wantedUrl) > 0 && wantedUrl[0] != nil {
 		mts.lock.Unlock()
 		return false
 	}
-	urlSet.(*skip.SkipList).Insert(Comparator(*url))
+	urlSet.(*skip.SkipList).Insert(url)
 	mts.lock.Unlock()
 	return true
 }
@@ -124,7 +106,7 @@ func (mts *MetadataService) addURL(targetMap *sync.Map, url *common.URL) bool {
 func (mts *MetadataService) removeURL(targetMap *sync.Map, url *common.URL) {
 	if value, loaded := targetMap.Load(url.ServiceKey()); loaded {
 		mts.lock.Lock()
-		value.(*skip.SkipList).Delete(Comparator(*url))
+		value.(*skip.SkipList).Delete(url)
 		mts.lock.Unlock()
 		mts.lock.RLock()
 		defer mts.lock.RUnlock()
@@ -135,14 +117,14 @@ func (mts *MetadataService) removeURL(targetMap *sync.Map, url *common.URL) {
 }
 
 // getAllService can return all the exportedUrlString except for metadataService
-func (mts *MetadataService) getAllService(services *sync.Map) []common.URL {
+func (mts *MetadataService) getAllService(services *sync.Map) []*common.URL {
 	// using skip list to dedup and sorting
-	res := make([]common.URL, 0)
+	var res []*common.URL
 	services.Range(func(key, value interface{}) bool {
 		urls := value.(*skip.SkipList)
 		for i := uint64(0); i < urls.Len(); i++ {
-			url := common.URL(urls.ByPosition(i).(Comparator))
-			if url.GetParam(constant.INTERFACE_KEY, url.Path) != constant.METADATA_SERVICE_NAME {
+			url := urls.ByPosition(i).(*common.URL)
+			if url.Service() != constant.METADATA_SERVICE_NAME {
 				res = append(res, url)
 			}
 		}
@@ -153,13 +135,13 @@ func (mts *MetadataService) getAllService(services *sync.Map) []common.URL {
 }
 
 // getSpecifiedService can return specified service url by serviceKey
-func (mts *MetadataService) getSpecifiedService(services *sync.Map, serviceKey string, protocol string) []common.URL {
-	res := make([]common.URL, 0)
+func (mts *MetadataService) getSpecifiedService(services *sync.Map, serviceKey string, protocol string) []*common.URL {
+	var res []*common.URL
 	serviceList, loaded := services.Load(serviceKey)
 	if loaded {
 		urls := serviceList.(*skip.SkipList)
 		for i := uint64(0); i < urls.Len(); i++ {
-			url := common.URL(urls.ByPosition(i).(Comparator))
+			url := urls.ByPosition(i).(*common.URL)
 			if len(protocol) == 0 || protocol == constant.ANY_VALUE || url.Protocol == protocol || url.GetParam(constant.PROTOCOL_KEY, "") == protocol {
 				res = append(res, url)
 			}
@@ -170,34 +152,34 @@ func (mts *MetadataService) getSpecifiedService(services *sync.Map, serviceKey s
 }
 
 // ExportURL can store the in memory
-func (mts *MetadataService) ExportURL(url common.URL) (bool, error) {
-	return mts.addURL(mts.exportedServiceURLs, &url), nil
+func (mts *MetadataService) ExportURL(url *common.URL) (bool, error) {
+	return mts.addURL(mts.exportedServiceURLs, url), nil
 }
 
 // UnexportURL can remove the url store in memory
-func (mts *MetadataService) UnexportURL(url common.URL) error {
-	mts.removeURL(mts.exportedServiceURLs, &url)
+func (mts *MetadataService) UnexportURL(url *common.URL) error {
+	mts.removeURL(mts.exportedServiceURLs, url)
 	return nil
 }
 
 // SubscribeURL can store the in memory
-func (mts *MetadataService) SubscribeURL(url common.URL) (bool, error) {
-	return mts.addURL(mts.subscribedServiceURLs, &url), nil
+func (mts *MetadataService) SubscribeURL(url *common.URL) (bool, error) {
+	return mts.addURL(mts.subscribedServiceURLs, url), nil
 }
 
 // UnsubscribeURL can remove the url store in memory
-func (mts *MetadataService) UnsubscribeURL(url common.URL) error {
-	mts.removeURL(mts.subscribedServiceURLs, &url)
+func (mts *MetadataService) UnsubscribeURL(url *common.URL) error {
+	mts.removeURL(mts.subscribedServiceURLs, url)
 	return nil
 }
 
 // PublishServiceDefinition: publish url's service metadata info, and write into memory
-func (mts *MetadataService) PublishServiceDefinition(url common.URL) error {
+func (mts *MetadataService) PublishServiceDefinition(url *common.URL) error {
 	interfaceName := url.GetParam(constant.INTERFACE_KEY, "")
 	isGeneric := url.GetParamBool(constant.GENERIC_KEY, false)
 	if len(interfaceName) > 0 && !isGeneric {
-		service := common.ServiceMap.GetService(url.Protocol, url.GetParam(constant.BEAN_NAME_KEY, url.Service()))
-		sd := definition.BuildServiceDefinition(*service, url)
+		tmpService := common.ServiceMap.GetServiceByServiceKey(url.Protocol, url.ServiceKey())
+		sd := definition.BuildServiceDefinition(*tmpService, url)
 		data, err := sd.ToBytes()
 		if err != nil {
 			logger.Errorf("publishProvider getServiceDescriptor error. providerUrl:%v , error:%v ", url, err)
@@ -221,7 +203,7 @@ func (mts *MetadataService) GetExportedURLs(serviceInterface string, group strin
 }
 
 // GetSubscribedURLs get all subscribedUrl
-func (mts *MetadataService) GetSubscribedURLs() ([]common.URL, error) {
+func (mts *MetadataService) GetSubscribedURLs() ([]*common.URL, error) {
 	return mts.getAllService(mts.subscribedServiceURLs), nil
 }
 
@@ -239,7 +221,7 @@ func (mts *MetadataService) GetServiceDefinitionByServiceKey(serviceKey string)
 }
 
 // RefreshMetadata will always return true because it will be implement by remote service
-func (mts *MetadataService) RefreshMetadata(exportedRevision string, subscribedRevision string) (bool, error) {
+func (mts *MetadataService) RefreshMetadata(string, string) (bool, error) {
 	return true, nil
 }
 
diff --git a/metadata/service/inmemory/service_proxy.go b/metadata/service/inmemory/service_proxy.go
index 7e01439f042a2046559188ec9df6924da0236cb1..7e14293a6766492c1c1b02eef9429d1adeb539ae 100644
--- a/metadata/service/inmemory/service_proxy.go
+++ b/metadata/service/inmemory/service_proxy.go
@@ -55,7 +55,7 @@ func (m *MetadataServiceProxy) GetExportedURLs(serviceInterface string, group st
 	inv := invocation.NewRPCInvocationWithOptions(invocation.WithMethodName(methodName),
 		invocation.WithArguments([]interface{}{siV.Interface(), gV.Interface(), vV.Interface(), pV.Interface()}),
 		invocation.WithReply(reflect.ValueOf(&[]interface{}{}).Interface()),
-		invocation.WithAttachments(map[string]string{constant.ASYNC_KEY: "false"}),
+		invocation.WithAttachments(map[string]interface{}{constant.ASYNC_KEY: "false"}),
 		invocation.WithParameterValues([]reflect.Value{siV, gV, vV, pV}))
 
 	res := m.invkr.Invoke(context.Background(), inv)
@@ -88,34 +88,34 @@ func (m *MetadataServiceProxy) ServiceName() (string, error) {
 	return "", nil
 }
 
-func (m *MetadataServiceProxy) ExportURL(url common.URL) (bool, error) {
+func (m *MetadataServiceProxy) ExportURL(url *common.URL) (bool, error) {
 	logger.Error("you should never invoke this implementation")
 	return false, nil
 }
 
-func (m *MetadataServiceProxy) UnexportURL(url common.URL) error {
+func (m *MetadataServiceProxy) UnexportURL(url *common.URL) error {
 	logger.Error("you should never invoke this implementation")
 	return nil
 }
 
-func (m *MetadataServiceProxy) SubscribeURL(url common.URL) (bool, error) {
+func (m *MetadataServiceProxy) SubscribeURL(url *common.URL) (bool, error) {
 	logger.Error("you should never invoke this implementation")
 	return false, nil
 }
 
-func (m *MetadataServiceProxy) UnsubscribeURL(url common.URL) error {
+func (m *MetadataServiceProxy) UnsubscribeURL(url *common.URL) error {
 	logger.Error("you should never invoke this implementation")
 	return nil
 }
 
-func (m *MetadataServiceProxy) PublishServiceDefinition(url common.URL) error {
+func (m *MetadataServiceProxy) PublishServiceDefinition(url *common.URL) error {
 	logger.Error("you should never invoke this implementation")
 	return nil
 }
 
-func (m *MetadataServiceProxy) GetSubscribedURLs() ([]common.URL, error) {
+func (m *MetadataServiceProxy) GetSubscribedURLs() ([]*common.URL, error) {
 	logger.Error("you should never invoke this implementation")
-	return []common.URL{}, nil
+	return nil, nil
 }
 
 func (m *MetadataServiceProxy) GetServiceDefinition(interfaceName string, group string, version string) (string, error) {
diff --git a/metadata/service/inmemory/service_proxy_test.go b/metadata/service/inmemory/service_proxy_test.go
index 0d75517e418133ffbf3804ec96f061dda09b9e5e..f7fc8fd88e5f31804253dc996cb99b241a519d14 100644
--- a/metadata/service/inmemory/service_proxy_test.go
+++ b/metadata/service/inmemory/service_proxy_test.go
@@ -49,16 +49,16 @@ func TestMetadataServiceProxy_GetExportedURLs(t *testing.T) {
 func TestNewMetadataService(t *testing.T) {
 	pxy := createPxy()
 	pxy.ServiceName()
-	pxy.PublishServiceDefinition(common.URL{})
+	pxy.PublishServiceDefinition(&common.URL{})
 	pxy.GetServiceDefinition(constant.ANY_VALUE, constant.ANY_VALUE, constant.ANY_VALUE)
 	pxy.Version()
 	pxy.GetSubscribedURLs()
-	pxy.UnsubscribeURL(common.URL{})
+	pxy.UnsubscribeURL(&common.URL{})
 	pxy.GetServiceDefinitionByServiceKey("any")
-	pxy.ExportURL(common.URL{})
-	pxy.SubscribeURL(common.URL{})
+	pxy.ExportURL(&common.URL{})
+	pxy.SubscribeURL(&common.URL{})
 	pxy.MethodMapper()
-	pxy.UnexportURL(common.URL{})
+	pxy.UnexportURL(&common.URL{})
 	pxy.RefreshMetadata(constant.ANY_VALUE, constant.ANY_VALUE)
 
 }
diff --git a/metadata/service/inmemory/service_test.go b/metadata/service/inmemory/service_test.go
index 048c286fdf28fba6a15a86164df0789d421f0797..256412c2917d39c0ff15db11121718ba983cd7ab 100644
--- a/metadata/service/inmemory/service_test.go
+++ b/metadata/service/inmemory/service_test.go
@@ -82,7 +82,7 @@ func TestMetadataService(t *testing.T) {
 	assert.Equal(t, 0, len(list4))
 
 	userProvider := &definition.UserProvider{}
-	common.ServiceMap.Register(serviceName, protocol, userProvider)
+	common.ServiceMap.Register(serviceName, protocol, group, version, userProvider)
 	mts.PublishServiceDefinition(u)
 	expected := "{\"CanonicalName\":\"com.ikurento.user.UserProvider\",\"CodeSource\":\"\"," +
 		"\"Methods\":[{\"Name\":\"GetUser\",\"ParameterTypes\":[\"slice\"],\"ReturnType\":\"ptr\"," +
diff --git a/metadata/service/remote/service.go b/metadata/service/remote/service.go
index ae83a69bef0af1614352c99c1e512a63770a0eff..efd16bdc6dad555f5a99bc4b2a02ee2a08202537 100644
--- a/metadata/service/remote/service.go
+++ b/metadata/service/remote/service.go
@@ -89,33 +89,33 @@ func (mts *MetadataService) setInMemoryMetadataService(metadata *inmemory.Metada
 }
 
 // ExportURL will be implemented by in memory service
-func (mts *MetadataService) ExportURL(url common.URL) (bool, error) {
+func (mts *MetadataService) ExportURL(url *common.URL) (bool, error) {
 	return mts.inMemoryMetadataService.ExportURL(url)
 }
 
 // UnexportURL remove @url's metadata
-func (mts *MetadataService) UnexportURL(url common.URL) error {
+func (mts *MetadataService) UnexportURL(url *common.URL) error {
 	smi := identifier.NewServiceMetadataIdentifier(url)
 	smi.Revision = mts.exportedRevision.Load()
 	return mts.delegateReport.RemoveServiceMetadata(smi)
 }
 
 // SubscribeURL will be implemented by in memory service
-func (mts *MetadataService) SubscribeURL(url common.URL) (bool, error) {
+func (mts *MetadataService) SubscribeURL(url *common.URL) (bool, error) {
 	return mts.inMemoryMetadataService.SubscribeURL(url)
 }
 
 // UnsubscribeURL will be implemented by in memory service
-func (mts *MetadataService) UnsubscribeURL(url common.URL) error {
+func (mts *MetadataService) UnsubscribeURL(url *common.URL) error {
 	return mts.UnsubscribeURL(url)
 }
 
 // PublishServiceDefinition will call remote metadata's StoreProviderMetadata to store url info and service definition
-func (mts *MetadataService) PublishServiceDefinition(url common.URL) error {
+func (mts *MetadataService) PublishServiceDefinition(url *common.URL) error {
 	interfaceName := url.GetParam(constant.INTERFACE_KEY, "")
 	isGeneric := url.GetParamBool(constant.GENERIC_KEY, false)
 	if len(interfaceName) > 0 && !isGeneric {
-		sv := common.ServiceMap.GetService(url.Protocol, url.GetParam(constant.BEAN_NAME_KEY, url.Service()))
+		sv := common.ServiceMap.GetServiceByServiceKey(url.Protocol, url.ServiceKey())
 		sd := definition.BuildServiceDefinition(*sv, url)
 		id := &identifier.MetadataIdentifier{
 			BaseMetadataIdentifier: identifier.BaseMetadataIdentifier{
@@ -139,7 +139,7 @@ func (mts *MetadataService) GetExportedURLs(serviceInterface string, group strin
 }
 
 // GetSubscribedURLs will be implemented by in memory service
-func (mts *MetadataService) GetSubscribedURLs() ([]common.URL, error) {
+func (mts *MetadataService) GetSubscribedURLs() ([]*common.URL, error) {
 	return mts.inMemoryMetadataService.GetSubscribedURLs()
 }
 
diff --git a/metadata/service/remote/service_proxy.go b/metadata/service/remote/service_proxy.go
index eaf7a02f4a0f3a8280835940bd8da720a0bde9f5..3199aa6dfff6f5e8b6036c44f452d16480b0380c 100644
--- a/metadata/service/remote/service_proxy.go
+++ b/metadata/service/remote/service_proxy.go
@@ -45,27 +45,27 @@ func (m *metadataServiceProxy) ServiceName() (string, error) {
 	return m.serviceName, nil
 }
 
-func (m *metadataServiceProxy) ExportURL(url common.URL) (bool, error) {
+func (m *metadataServiceProxy) ExportURL(url *common.URL) (bool, error) {
 	logger.Error("you should never invoke this implementation")
 	return true, nil
 }
 
-func (m *metadataServiceProxy) UnexportURL(url common.URL) error {
+func (m *metadataServiceProxy) UnexportURL(url *common.URL) error {
 	logger.Error("you should never invoke this implementation")
 	return nil
 }
 
-func (m *metadataServiceProxy) SubscribeURL(url common.URL) (bool, error) {
+func (m *metadataServiceProxy) SubscribeURL(url *common.URL) (bool, error) {
 	logger.Error("you should never invoke this implementation")
 	return true, nil
 }
 
-func (m *metadataServiceProxy) UnsubscribeURL(url common.URL) error {
+func (m *metadataServiceProxy) UnsubscribeURL(url *common.URL) error {
 	logger.Error("you should never invoke this implementation")
 	return nil
 }
 
-func (m *metadataServiceProxy) PublishServiceDefinition(url common.URL) error {
+func (m *metadataServiceProxy) PublishServiceDefinition(url *common.URL) error {
 	logger.Error("you should never invoke this implementation")
 	return nil
 }
@@ -85,7 +85,7 @@ func (m *metadataServiceProxy) GetExportedURLs(serviceInterface string, group st
 	if err != nil {
 		return []interface{}{}, nil
 	}
-	res := make([]common.URL, 0, len(urls))
+	var res []*common.URL
 	for _, s := range urls {
 		u, err := common.NewURL(s)
 		if err != nil {
@@ -101,9 +101,9 @@ func (m *metadataServiceProxy) MethodMapper() map[string]string {
 	return map[string]string{}
 }
 
-func (m *metadataServiceProxy) GetSubscribedURLs() ([]common.URL, error) {
+func (m *metadataServiceProxy) GetSubscribedURLs() ([]*common.URL, error) {
 	logger.Error("you should never invoke this implementation")
-	return []common.URL{}, nil
+	return nil, nil
 }
 
 func (m *metadataServiceProxy) GetServiceDefinition(interfaceName string, group string, version string) (string, error) {
diff --git a/metadata/service/remote/service_proxy_test.go b/metadata/service/remote/service_proxy_test.go
index c284bb22123e731f3b8905f70508856bc767ace6..8bccbb8bbb70d1533fa3dad78bab59e82ff2e5b3 100644
--- a/metadata/service/remote/service_proxy_test.go
+++ b/metadata/service/remote/service_proxy_test.go
@@ -55,15 +55,15 @@ func TestMetadataServiceProxy_GetServiceDefinition(t *testing.T) {
 func TestMetadataServiceProxy(t *testing.T) {
 	pxy := createProxy()
 	pxy.ServiceName()
-	pxy.PublishServiceDefinition(common.URL{})
+	pxy.PublishServiceDefinition(&common.URL{})
 	pxy.Version()
 	pxy.GetSubscribedURLs()
-	pxy.UnsubscribeURL(common.URL{})
+	pxy.UnsubscribeURL(&common.URL{})
 	pxy.GetServiceDefinitionByServiceKey("any")
-	pxy.ExportURL(common.URL{})
-	pxy.SubscribeURL(common.URL{})
+	pxy.ExportURL(&common.URL{})
+	pxy.SubscribeURL(&common.URL{})
 	pxy.MethodMapper()
-	pxy.UnexportURL(common.URL{})
+	pxy.UnexportURL(&common.URL{})
 	pxy.Reference()
 	pxy.RefreshMetadata(constant.ANY_VALUE, constant.ANY_VALUE)
 }
@@ -89,7 +89,7 @@ func prepareTest() {
 		return &mockMetadataReportFactory{}
 	})
 	u, _ := common.NewURL("mock://localhost")
-	instance.GetMetadataReportInstance(&u)
+	instance.GetMetadataReportInstance(u)
 }
 
 type mockMetadataReportFactory struct {
@@ -110,7 +110,7 @@ func (m mockMetadataReport) StoreConsumerMetadata(*identifier.MetadataIdentifier
 	panic("implement me")
 }
 
-func (m mockMetadataReport) SaveServiceMetadata(*identifier.ServiceMetadataIdentifier, common.URL) error {
+func (m mockMetadataReport) SaveServiceMetadata(*identifier.ServiceMetadataIdentifier, *common.URL) error {
 	return nil
 }
 
diff --git a/metadata/service/remote/service_test.go b/metadata/service/remote/service_test.go
index 734f0989ab06ef17caeef241cd067c678fb8b2ad..71586cc1dcc87dc8644a25c9a89842bd84fe9f0a 100644
--- a/metadata/service/remote/service_test.go
+++ b/metadata/service/remote/service_test.go
@@ -39,7 +39,7 @@ import (
 )
 
 var (
-	serviceMetadata    = make(map[*identifier.ServiceMetadataIdentifier]common.URL, 4)
+	serviceMetadata    = make(map[*identifier.ServiceMetadataIdentifier]*common.URL, 4)
 	subscribedMetadata = make(map[*identifier.SubscriberMetadataIdentifier]string, 4)
 )
 
@@ -65,7 +65,7 @@ func (metadataReport) StoreConsumerMetadata(*identifier.MetadataIdentifier, stri
 	return nil
 }
 
-func (mr *metadataReport) SaveServiceMetadata(id *identifier.ServiceMetadataIdentifier, url common.URL) error {
+func (mr *metadataReport) SaveServiceMetadata(id *identifier.ServiceMetadataIdentifier, url *common.URL) error {
 	logger.Infof("SaveServiceMetadata , url is %v", url)
 	serviceMetadata[id] = url
 	return nil
@@ -97,7 +97,7 @@ func TestMetadataService(t *testing.T) {
 	extension.SetMetadataReportFactory("mock", getMetadataReportFactory)
 	u, err := common.NewURL(fmt.Sprintf("mock://127.0.0.1:20000/?sync.report=true"))
 	assert.NoError(t, err)
-	instance.GetMetadataReportInstance(&u)
+	instance.GetMetadataReportInstance(u)
 	mts, err := newMetadataService()
 	assert.NoError(t, err)
 	mts.(*MetadataService).setInMemoryMetadataService(mockInmemoryProc(t))
@@ -126,7 +126,7 @@ func mockInmemoryProc(t *testing.T) *inmemory.MetadataService {
 	_, err = mts.SubscribeURL(u)
 	assert.NoError(t, err)
 
-	_, err = common.ServiceMap.Register(serviceName, protocol, userProvider)
+	_, err = common.ServiceMap.Register(serviceName, protocol, group, version, userProvider)
 	assert.NoError(t, err)
 	err = mts.PublishServiceDefinition(u)
 	assert.NoError(t, err)
diff --git a/metadata/service/service.go b/metadata/service/service.go
index f6509d0a72eb26e488dfb4fdeef5f4bbfd6b1bea..1d90f8a516831adcae20163a3620dd765459310d 100644
--- a/metadata/service/service.go
+++ b/metadata/service/service.go
@@ -34,15 +34,15 @@ type MetadataService interface {
 	// ServiceName will get the service's name in meta service , which is application name
 	ServiceName() (string, error)
 	// ExportURL will store the exported url in metadata
-	ExportURL(url common.URL) (bool, error)
+	ExportURL(url *common.URL) (bool, error)
 	// UnexportURL will delete the exported url in metadata
-	UnexportURL(url common.URL) error
+	UnexportURL(url *common.URL) error
 	// SubscribeURL will store the subscribed url in metadata
-	SubscribeURL(url common.URL) (bool, error)
+	SubscribeURL(url *common.URL) (bool, error)
 	// UnsubscribeURL will delete the subscribed url in metadata
-	UnsubscribeURL(url common.URL) error
+	UnsubscribeURL(url *common.URL) error
 	// PublishServiceDefinition will generate the target url's code info
-	PublishServiceDefinition(url common.URL) error
+	PublishServiceDefinition(url *common.URL) error
 	// GetExportedURLs will get the target exported url in metadata
 	// the url should be unique
 	// due to dubbo-go only support return array []interface{} in RPCService, so we should declare the return type as []interface{}
@@ -53,7 +53,7 @@ type MetadataService interface {
 
 	// GetExportedURLs will get the target subscribed url in metadata
 	// the url should be unique
-	GetSubscribedURLs() ([]common.URL, error)
+	GetSubscribedURLs() ([]*common.URL, error)
 	// GetServiceDefinition will get the target service info store in metadata
 	GetServiceDefinition(interfaceName string, group string, version string) (string, error)
 	// GetServiceDefinition will get the target service info store in metadata by service key
@@ -122,7 +122,7 @@ func getExportedServicesRevision(serviceInstance registry.ServiceInstance) strin
 	return metaData[constant.EXPORTED_SERVICES_REVISION_PROPERTY_NAME]
 }
 
-func ConvertURLArrToIntfArr(urls []common.URL) []interface{} {
+func ConvertURLArrToIntfArr(urls []*common.URL) []interface{} {
 	if len(urls) == 0 {
 		return []interface{}{}
 	}
diff --git a/metrics/prometheus/reporter.go b/metrics/prometheus/reporter.go
index bd1e7986ca709a4e10dfcad04d2380931308d568..266c8206c5ea5b6dc07402943bcebdeb6dd7dea7 100644
--- a/metrics/prometheus/reporter.go
+++ b/metrics/prometheus/reporter.go
@@ -24,6 +24,7 @@ import (
 	"sync"
 	"time"
 )
+
 import (
 	"github.com/prometheus/client_golang/prometheus"
 )
@@ -64,7 +65,6 @@ var (
 
 // should initialize after loading configuration
 func init() {
-
 	extension.SetMetricReporter(reporterName, newPrometheusReporter)
 }
 
@@ -72,12 +72,10 @@ func init() {
 // if you want to use this feature, you need to 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
@@ -130,13 +128,13 @@ func newHistogramVec(side string) *prometheus.HistogramVec {
 }
 
 // whether this url represents the application received the request as server
-func isProvider(url common.URL) bool {
+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 {
+func isConsumer(url *common.URL) bool {
 	role := url.GetParam(constant.ROLE_KEY, "")
 	return strings.EqualFold(role, strconv.Itoa(common.CONSUMER))
 }
diff --git a/metrics/prometheus/reporter_test.go b/metrics/prometheus/reporter_test.go
index 0cb7d09a2c8e71fb88b54789c8eb3ee2cf967fbf..1b9853f4d20739269861aac680b1ec491ef552d6 100644
--- a/metrics/prometheus/reporter_test.go
+++ b/metrics/prometheus/reporter_test.go
@@ -26,6 +26,7 @@ import (
 import (
 	"github.com/stretchr/testify/assert"
 )
+
 import (
 	"github.com/apache/dubbo-go/common"
 	"github.com/apache/dubbo-go/common/extension"
@@ -43,7 +44,7 @@ func TestPrometheusReporter_Report(t *testing.T) {
 			"service.filter=echo%2Ctoken%2Caccesslog&timestamp=1569153406&token=934804bf-b007-4174-94eb-96e3e1d60cc7&version=&warmup=100")
 	invoker := protocol.NewBaseInvoker(url)
 
-	attach := make(map[string]string, 10)
+	attach := make(map[string]interface{}, 10)
 	inv := invocation.NewRPCInvocation("MethodName", []interface{}{"OK", "Hello"}, attach)
 
 	assert.False(t, isConsumer(url))
diff --git a/metrics/reporter.go b/metrics/reporter.go
index 9a7094fa62d9c0fa3e6ee0a8ef373f91c28d2c90..24d75aa4fe949737d7ca94cfd339a32af14a40be 100644
--- a/metrics/reporter.go
+++ b/metrics/reporter.go
@@ -21,6 +21,7 @@ import (
 	"context"
 	"time"
 )
+
 import (
 	"github.com/apache/dubbo-go/protocol"
 )
diff --git a/protocol/dubbo/client.go b/protocol/dubbo/client.go
deleted file mode 100644
index 530beba3512ec09e353b632b1a4b75d75f8a5ae2..0000000000000000000000000000000000000000
--- a/protocol/dubbo/client.go
+++ /dev/null
@@ -1,364 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dubbo
-
-import (
-	"math/rand"
-	"strings"
-	"sync"
-	"time"
-)
-
-import (
-	"github.com/apache/dubbo-getty"
-	hessian "github.com/apache/dubbo-go-hessian2"
-	gxsync "github.com/dubbogo/gost/sync"
-	perrors "github.com/pkg/errors"
-	"go.uber.org/atomic"
-	"gopkg.in/yaml.v2"
-)
-
-import (
-	"github.com/apache/dubbo-go/common"
-	"github.com/apache/dubbo-go/common/constant"
-	"github.com/apache/dubbo-go/common/logger"
-	"github.com/apache/dubbo-go/config"
-)
-
-var (
-	errInvalidCodecType  = perrors.New("illegal CodecType")
-	errInvalidAddress    = perrors.New("remote address invalid or empty")
-	errSessionNotExist   = perrors.New("session not exist")
-	errClientClosed      = perrors.New("client closed")
-	errClientReadTimeout = perrors.New("client read timeout")
-
-	clientConf   *ClientConfig
-	clientGrpool *gxsync.TaskPool
-)
-
-func init() {
-
-	// load clientconfig from consumer_config
-	// default use dubbo
-	consumerConfig := config.GetConsumerConfig()
-	if consumerConfig.ApplicationConfig == nil {
-		return
-	}
-	protocolConf := config.GetConsumerConfig().ProtocolConf
-	defaultClientConfig := GetDefaultClientConfig()
-	if protocolConf == nil {
-		logger.Info("protocol_conf default use dubbo config")
-	} else {
-		dubboConf := protocolConf.(map[interface{}]interface{})[DUBBO]
-		if dubboConf == nil {
-			logger.Warnf("dubboConf is nil")
-			return
-		}
-		dubboConfByte, err := yaml.Marshal(dubboConf)
-		if err != nil {
-			panic(err)
-		}
-		err = yaml.Unmarshal(dubboConfByte, &defaultClientConfig)
-		if err != nil {
-			panic(err)
-		}
-	}
-	clientConf = &defaultClientConfig
-	if err := clientConf.CheckValidity(); err != nil {
-		logger.Warnf("[CheckValidity] error: %v", err)
-		return
-	}
-	setClientGrpool()
-
-	rand.Seed(time.Now().UnixNano())
-}
-
-// SetClientConf set dubbo client config.
-func SetClientConf(c ClientConfig) {
-	clientConf = &c
-	err := clientConf.CheckValidity()
-	if err != nil {
-		logger.Warnf("[ClientConfig CheckValidity] error: %v", err)
-		return
-	}
-	setClientGrpool()
-}
-
-// GetClientConf get dubbo client config.
-func GetClientConf() ClientConfig {
-	return *clientConf
-}
-
-func setClientGrpool() {
-	if clientConf.GrPoolSize > 1 {
-		clientGrpool = gxsync.NewTaskPool(gxsync.WithTaskPoolTaskPoolSize(clientConf.GrPoolSize), gxsync.WithTaskPoolTaskQueueLength(clientConf.QueueLen),
-			gxsync.WithTaskPoolTaskQueueNumber(clientConf.QueueNumber))
-	}
-}
-
-// Options is option for create dubbo client
-type Options struct {
-	// connect timeout
-	ConnectTimeout time.Duration
-	// request timeout
-	RequestTimeout time.Duration
-}
-
-//AsyncCallbackResponse async response for dubbo
-type AsyncCallbackResponse struct {
-	common.CallbackResponse
-	Opts      Options
-	Cause     error
-	Start     time.Time // invoke(call) start time == write start time
-	ReadStart time.Time // read start time, write duration = ReadStart - Start
-	Reply     interface{}
-}
-
-// Client is dubbo protocol client.
-type Client struct {
-	opts     Options
-	conf     ClientConfig
-	pool     *gettyRPCClientPool
-	sequence atomic.Uint64
-
-	pendingResponses *sync.Map
-}
-
-// NewClient create a new Client.
-func NewClient(opt Options) *Client {
-
-	switch {
-	case opt.ConnectTimeout == 0:
-		opt.ConnectTimeout = 3 * time.Second
-		fallthrough
-	case opt.RequestTimeout == 0:
-		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{
-		opts:             opt,
-		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 is dubbo protocol request.
-type Request struct {
-	addr   string
-	svcUrl common.URL
-	method string
-	args   interface{}
-	atta   map[string]string
-}
-
-// NewRequest create a new Request.
-func NewRequest(addr string, svcUrl common.URL, method string, args interface{}, atta map[string]string) *Request {
-	return &Request{
-		addr:   addr,
-		svcUrl: svcUrl,
-		method: method,
-		args:   args,
-		atta:   atta,
-	}
-}
-
-// Response is dubbo protocol response.
-type Response struct {
-	reply interface{}
-	atta  map[string]string
-}
-
-// NewResponse creates a new Response.
-func NewResponse(reply interface{}, atta map[string]string) *Response {
-	return &Response{
-		reply: reply,
-		atta:  atta,
-	}
-}
-
-// CallOneway call by one way
-func (c *Client) CallOneway(request *Request) error {
-
-	return perrors.WithStack(c.call(CT_OneWay, request, NewResponse(nil, nil), nil))
-}
-
-// Call call remoting by two way or one way, if @response.reply is nil, the way of call is one way.
-func (c *Client) Call(request *Request, response *Response) error {
-	ct := CT_TwoWay
-	if response.reply == nil {
-		ct = CT_OneWay
-	}
-
-	return perrors.WithStack(c.call(ct, request, response, nil))
-}
-
-// AsyncCall call remoting by async with callback.
-func (c *Client) AsyncCall(request *Request, callback common.AsyncCallback, response *Response) error {
-	return perrors.WithStack(c.call(CT_TwoWay, request, response, callback))
-}
-
-func (c *Client) call(ct CallType, request *Request, response *Response, callback common.AsyncCallback) error {
-	p := &DubboPackage{}
-	p.Service.Path = strings.TrimPrefix(request.svcUrl.Path, "/")
-	p.Service.Interface = request.svcUrl.GetParam(constant.INTERFACE_KEY, "")
-	p.Service.Version = request.svcUrl.GetParam(constant.VERSION_KEY, "")
-	p.Service.Group = request.svcUrl.GetParam(constant.GROUP_KEY, "")
-	p.Service.Method = request.method
-	c.pool.sslEnabled = request.svcUrl.GetParamBool(constant.SSL_ENABLED_KEY, false)
-
-	p.Service.Timeout = c.opts.RequestTimeout
-	var timeout = request.svcUrl.GetParam(strings.Join([]string{constant.METHOD_KEYS, request.method + constant.RETRIES_KEY}, "."), "")
-	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)
-
-	var rsp *PendingResponse
-	if ct != CT_OneWay {
-		p.Header.Type = hessian.PackageRequest_TwoWay
-		rsp = NewPendingResponse()
-		rsp.response = response
-		rsp.callback = callback
-	} else {
-		p.Header.Type = hessian.PackageRequest
-	}
-
-	var (
-		err     error
-		session getty.Session
-		conn    *gettyRPCClient
-	)
-	conn, session, err = c.selectSession(request.addr)
-	if err != nil {
-		return perrors.WithStack(err)
-	}
-	if session == nil {
-		return errSessionNotExist
-	}
-	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)
-	}
-
-	if ct == CT_OneWay || callback != nil {
-		return nil
-	}
-
-	select {
-	case <-getty.GetTimeWheel().After(c.opts.RequestTimeout):
-		c.removePendingResponse(SequenceType(rsp.seq))
-		return perrors.WithStack(errClientReadTimeout)
-	case <-rsp.done:
-		err = rsp.err
-	}
-
-	return perrors.WithStack(err)
-}
-
-// Close close the client pool.
-func (c *Client) Close() {
-	if c.pool != nil {
-		c.pool.close()
-	}
-	c.pool = nil
-}
-
-func (c *Client) selectSession(addr string) (*gettyRPCClient, getty.Session, error) {
-	rpcClient, err := c.pool.getGettyRpcClient(DUBBO, addr)
-	if err != nil {
-		return nil, nil, perrors.WithStack(err)
-	}
-	return rpcClient, rpcClient.selectSession(), nil
-}
-
-func (c *Client) heartbeat(session getty.Session) error {
-	return c.transfer(session, nil, NewPendingResponse())
-}
-
-func (c *Client) transfer(session getty.Session, pkg *DubboPackage,
-	rsp *PendingResponse) error {
-
-	var (
-		sequence uint64
-		err      error
-	)
-
-	sequence = c.sequence.Add(1)
-
-	if pkg == nil {
-		pkg = &DubboPackage{}
-		pkg.Body = hessian.NewRequest([]interface{}{}, nil)
-		pkg.Body = []interface{}{}
-		pkg.Header.Type = hessian.PackageHeartbeat
-		pkg.Header.SerialID = byte(S_Dubbo)
-	}
-	pkg.Header.ID = int64(sequence)
-
-	// cond1
-	if rsp != nil {
-		rsp.seq = sequence
-		c.addPendingResponse(rsp)
-	}
-
-	err = session.WritePkg(pkg, c.opts.RequestTimeout)
-	if err != nil {
-		c.removePendingResponse(SequenceType(rsp.seq))
-	} else if rsp != nil { // cond2
-		// cond2 should not merged with cond1. cause the response package may be returned very
-		// soon and it will be handled by other goroutine.
-		rsp.readStart = time.Now()
-	}
-
-	return perrors.WithStack(err)
-}
-
-func (c *Client) addPendingResponse(pr *PendingResponse) {
-	c.pendingResponses.Store(SequenceType(pr.seq), pr)
-}
-
-func (c *Client) removePendingResponse(seq SequenceType) *PendingResponse {
-	if c.pendingResponses == nil {
-		return nil
-	}
-	if presp, ok := c.pendingResponses.Load(seq); ok {
-		c.pendingResponses.Delete(seq)
-		return presp.(*PendingResponse)
-	}
-	return nil
-}
diff --git a/protocol/dubbo/client_test.go b/protocol/dubbo/client_test.go
deleted file mode 100644
index 8b0ba169b82910652c64011c47568c7a018ae5e0..0000000000000000000000000000000000000000
--- a/protocol/dubbo/client_test.go
+++ /dev/null
@@ -1,305 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dubbo
-
-import (
-	"bytes"
-	"context"
-	"sync"
-	"testing"
-	"time"
-)
-
-import (
-	hessian "github.com/apache/dubbo-go-hessian2"
-	perrors "github.com/pkg/errors"
-	"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"
-)
-
-const (
-	mockMethodNameGetUser   = "GetUser"
-	mockMethodNameGetBigPkg = "GetBigPkg"
-	mockAddress             = "127.0.0.1:20000"
-)
-
-func TestClientCallOneway(t *testing.T) {
-	proto, url := InitTest(t)
-
-	c := &Client{
-		pendingResponses: new(sync.Map),
-		conf:             *clientConf,
-		opts: Options{
-			ConnectTimeout: 3e9,
-			RequestTimeout: 6e9,
-		},
-	}
-	c.pool = newGettyRPCClientConnPool(c, clientConf.PoolSize, time.Duration(int(time.Second)*clientConf.PoolTTL))
-
-	err := c.CallOneway(NewRequest(mockAddress, url, mockMethodNameGetUser, []interface{}{"1", "username"}, nil))
-	assert.NoError(t, err)
-
-	// destroy
-	proto.Destroy()
-}
-
-func TestClientCall(t *testing.T) {
-	proto, url := InitTest(t)
-
-	c := &Client{
-		pendingResponses: new(sync.Map),
-		conf:             *clientConf,
-		opts: Options{
-			ConnectTimeout: 3e9,
-			RequestTimeout: 10e9,
-		},
-	}
-	c.pool = newGettyRPCClientConnPool(c, clientConf.PoolSize, time.Duration(int(time.Second)*clientConf.PoolTTL))
-
-	var (
-		user *User
-		err  error
-	)
-
-	user = &User{}
-	err = c.Call(NewRequest(mockAddress, url, mockMethodNameGetBigPkg, []interface{}{nil}, nil), NewResponse(user, nil))
-	assert.NoError(t, err)
-	assert.NotEqual(t, "", user.Id)
-	assert.NotEqual(t, "", user.Name)
-
-	user = &User{}
-	err = c.Call(NewRequest(mockAddress, url, mockMethodNameGetUser, []interface{}{"1", "username"}, nil), NewResponse(user, nil))
-	assert.NoError(t, err)
-	assert.Equal(t, User{Id: "1", Name: "username"}, *user)
-
-	user = &User{}
-	err = c.Call(NewRequest(mockAddress, url, "GetUser0", []interface{}{"1", nil, "username"}, nil), NewResponse(user, nil))
-	assert.NoError(t, err)
-	assert.Equal(t, User{Id: "1", Name: "username"}, *user)
-
-	err = c.Call(NewRequest(mockAddress, url, "GetUser1", []interface{}{}, nil), NewResponse(user, nil))
-	assert.NoError(t, err)
-
-	err = c.Call(NewRequest(mockAddress, url, "GetUser2", []interface{}{}, nil), NewResponse(user, nil))
-	assert.EqualError(t, err, "error")
-
-	user2 := []interface{}{}
-	err = c.Call(NewRequest(mockAddress, url, "GetUser3", []interface{}{}, nil), NewResponse(&user2, nil))
-	assert.NoError(t, err)
-	assert.Equal(t, &User{Id: "1", Name: "username"}, user2[0])
-
-	user2 = []interface{}{}
-	err = c.Call(NewRequest(mockAddress, url, "GetUser4", []interface{}{[]interface{}{"1", "username"}}, nil), NewResponse(&user2, nil))
-	assert.NoError(t, err)
-	assert.Equal(t, &User{Id: "1", Name: "username"}, user2[0])
-
-	user3 := map[interface{}]interface{}{}
-	err = c.Call(NewRequest(mockAddress, url, "GetUser5", []interface{}{map[interface{}]interface{}{"id": "1", "name": "username"}}, nil), NewResponse(&user3, nil))
-	assert.NoError(t, err)
-	assert.NotNil(t, user3)
-	assert.Equal(t, &User{Id: "1", Name: "username"}, user3["key"])
-
-	user = &User{}
-	err = c.Call(NewRequest(mockAddress, url, "GetUser6", []interface{}{0}, nil), NewResponse(user, nil))
-	assert.NoError(t, err)
-	assert.Equal(t, User{Id: "", Name: ""}, *user)
-
-	user = &User{}
-	err = c.Call(NewRequest(mockAddress, url, "GetUser6", []interface{}{1}, nil), NewResponse(user, nil))
-	assert.NoError(t, err)
-	assert.Equal(t, User{Id: "1", Name: ""}, *user)
-
-	// destroy
-	proto.Destroy()
-}
-
-func TestClientAsyncCall(t *testing.T) {
-	proto, url := InitTest(t)
-
-	c := &Client{
-		pendingResponses: new(sync.Map),
-		conf:             *clientConf,
-		opts: Options{
-			ConnectTimeout: 3e9,
-			RequestTimeout: 6e9,
-		},
-	}
-	c.pool = newGettyRPCClientConnPool(c, clientConf.PoolSize, time.Duration(int(time.Second)*clientConf.PoolTTL))
-
-	user := &User{}
-	lock := sync.Mutex{}
-	lock.Lock()
-	err := c.AsyncCall(NewRequest(mockAddress, url, mockMethodNameGetUser, []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)
-	assert.Equal(t, User{}, *user)
-
-	// destroy
-	lock.Lock()
-	proto.Destroy()
-	lock.Unlock()
-}
-
-func InitTest(t *testing.T) (protocol.Protocol, common.URL) {
-
-	hessian.RegisterPOJO(&User{})
-
-	methods, err := common.ServiceMap.Register("com.ikurento.user.UserProvider", "dubbo", &UserProvider{})
-	assert.NoError(t, err)
-	assert.Equal(t, "GetBigPkg,GetUser,GetUser0,GetUser1,GetUser2,GetUser3,GetUser4,GetUser5,GetUser6", methods)
-
-	// config
-	SetClientConf(ClientConfig{
-		ConnectionNum:   2,
-		HeartbeatPeriod: "5s",
-		SessionTimeout:  "20s",
-		PoolTTL:         600,
-		PoolSize:        64,
-		GettySessionParam: GettySessionParam{
-			CompressEncoding: false,
-			TcpNoDelay:       true,
-			TcpKeepAlive:     true,
-			KeepAlivePeriod:  "120s",
-			TcpRBufSize:      262144,
-			TcpWBufSize:      65536,
-			PkgWQSize:        512,
-			TcpReadTimeout:   "4s",
-			TcpWriteTimeout:  "5s",
-			WaitTimeout:      "1s",
-			MaxMsgLen:        10240000000,
-			SessionName:      "client",
-		},
-	})
-	assert.NoError(t, clientConf.CheckValidity())
-	SetServerConfig(ServerConfig{
-		SessionNumber:  700,
-		SessionTimeout: "20s",
-		GettySessionParam: GettySessionParam{
-			CompressEncoding: false,
-			TcpNoDelay:       true,
-			TcpKeepAlive:     true,
-			KeepAlivePeriod:  "120s",
-			TcpRBufSize:      262144,
-			TcpWBufSize:      65536,
-			PkgWQSize:        512,
-			TcpReadTimeout:   "1s",
-			TcpWriteTimeout:  "5s",
-			WaitTimeout:      "1s",
-			MaxMsgLen:        10240000000,
-			SessionName:      "server",
-		}})
-	assert.NoError(t, srvConf.CheckValidity())
-
-	// Export
-	proto := GetProtocol()
-	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{
-		BaseInvoker: *protocol.NewBaseInvoker(url),
-	})
-
-	time.Sleep(time.Second * 2)
-
-	return proto, url
-}
-
-//////////////////////////////////
-// provider
-//////////////////////////////////
-
-type (
-	User struct {
-		Id   string `json:"id"`
-		Name string `json:"name"`
-	}
-
-	UserProvider struct {
-		user map[string]User
-	}
-)
-
-// size:4801228
-func (u *UserProvider) GetBigPkg(ctx context.Context, req []interface{}, rsp *User) error {
-	argBuf := new(bytes.Buffer)
-	for i := 0; i < 4000; i++ {
-		argBuf.WriteString("鍑婚紦鍏堕晽锛岃笂璺冪敤鍏点€傚湡鍥藉煄婕曪紝鎴戠嫭鍗楄銆備粠瀛欏瓙浠诧紝骞抽檲涓庡畫銆備笉鎴戜互褰掞紝蹇у績鏈夊俊銆傜埌灞呯埌澶勶紵鐖颁抚鍏堕┈锛熶簬浠ユ眰涔嬶紵浜庢灄涔嬩笅銆傛鐢熷闃旓紝涓庡瓙鎴愯銆傛墽瀛愪箣鎵嬶紝涓庡瓙鍋曡€併€備簬鍡熼様鍏紝涓嶆垜娲诲叜銆備簬鍡熸吹鍏紝涓嶆垜淇″叜銆�")
-		argBuf.WriteString("鍑婚紦鍏堕晽锛岃笂璺冪敤鍏点€傚湡鍥藉煄婕曪紝鎴戠嫭鍗楄銆備粠瀛欏瓙浠诧紝骞抽檲涓庡畫銆備笉鎴戜互褰掞紝蹇у績鏈夊俊銆傜埌灞呯埌澶勶紵鐖颁抚鍏堕┈锛熶簬浠ユ眰涔嬶紵浜庢灄涔嬩笅銆傛鐢熷闃旓紝涓庡瓙鎴愯銆傛墽瀛愪箣鎵嬶紝涓庡瓙鍋曡€併€備簬鍡熼様鍏紝涓嶆垜娲诲叜銆備簬鍡熸吹鍏紝涓嶆垜淇″叜銆�")
-	}
-	rsp.Id = argBuf.String()
-	rsp.Name = argBuf.String()
-	return nil
-}
-
-func (u *UserProvider) GetUser(ctx context.Context, req []interface{}, rsp *User) error {
-	rsp.Id = req[0].(string)
-	rsp.Name = req[1].(string)
-	return nil
-}
-
-func (u *UserProvider) GetUser0(id string, k *User, name string) (User, error) {
-	return User{Id: id, Name: name}, nil
-}
-
-func (u *UserProvider) GetUser1() error {
-	return nil
-}
-
-func (u *UserProvider) GetUser2() error {
-	return perrors.New("error")
-}
-
-func (u *UserProvider) GetUser3(rsp *[]interface{}) error {
-	*rsp = append(*rsp, User{Id: "1", Name: "username"})
-	return nil
-}
-
-func (u *UserProvider) GetUser4(ctx context.Context, req []interface{}) ([]interface{}, error) {
-
-	return []interface{}{User{Id: req[0].([]interface{})[0].(string), Name: req[0].([]interface{})[1].(string)}}, nil
-}
-
-func (u *UserProvider) GetUser5(ctx context.Context, req []interface{}) (map[interface{}]interface{}, error) {
-	return map[interface{}]interface{}{"key": User{Id: req[0].(map[interface{}]interface{})["id"].(string), Name: req[0].(map[interface{}]interface{})["name"].(string)}}, nil
-}
-
-func (u *UserProvider) GetUser6(id int64) (*User, error) {
-	if id == 0 {
-		return nil, nil
-	}
-	return &User{Id: "1"}, nil
-}
-
-func (u *UserProvider) Reference() string {
-	return "UserProvider"
-}
-
-func (u User) JavaClassName() string {
-	return "com.ikurento.user.User"
-}
diff --git a/protocol/dubbo/codec.go b/protocol/dubbo/codec.go
deleted file mode 100644
index 9781c70115e3d8b9e41c3418ae7b859608651ee8..0000000000000000000000000000000000000000
--- a/protocol/dubbo/codec.go
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dubbo
-
-import (
-	"bufio"
-	"bytes"
-	"fmt"
-	"time"
-)
-
-import (
-	"github.com/apache/dubbo-go-hessian2"
-	"github.com/apache/dubbo-go/common"
-	perrors "github.com/pkg/errors"
-)
-
-//SerialID serial ID
-type SerialID byte
-
-const (
-	// S_Dubbo dubbo serial id
-	S_Dubbo SerialID = 2
-)
-
-//CallType call type
-type CallType int32
-
-const (
-	// CT_UNKNOWN unknown call type
-	CT_UNKNOWN CallType = 0
-	// CT_OneWay call one way
-	CT_OneWay CallType = 1
-	// CT_TwoWay call in request/response
-	CT_TwoWay CallType = 2
-)
-
-////////////////////////////////////////////
-// dubbo package
-////////////////////////////////////////////
-
-// SequenceType sequence type
-type SequenceType int64
-
-// nolint
-type DubboPackage struct {
-	Header  hessian.DubboHeader
-	Service hessian.Service
-	Body    interface{}
-	Err     error
-}
-
-// String prints dubbo package detail include header銆乸ath銆乥ody etc.
-func (p DubboPackage) String() string {
-	return fmt.Sprintf("DubboPackage: Header-%v, Path-%v, Body-%v", p.Header, p.Service, p.Body)
-}
-
-// Marshal encode hessian package.
-func (p *DubboPackage) Marshal() (*bytes.Buffer, error) {
-	codec := hessian.NewHessianCodec(nil)
-
-	pkg, err := codec.Write(p.Service, p.Header, p.Body)
-	if err != nil {
-		return nil, perrors.WithStack(err)
-	}
-
-	return bytes.NewBuffer(pkg), nil
-}
-
-// Unmarshal decodes hessian package.
-func (p *DubboPackage) Unmarshal(buf *bytes.Buffer, opts ...interface{}) error {
-	// fix issue https://github.com/apache/dubbo-go/issues/380
-	bufLen := buf.Len()
-	if bufLen < hessian.HEADER_LENGTH {
-		return perrors.WithStack(hessian.ErrHeaderNotEnough)
-	}
-
-	codec := hessian.NewHessianCodec(bufio.NewReaderSize(buf, bufLen))
-
-	// read header
-	err := codec.ReadHeader(&p.Header)
-	if err != nil {
-		return perrors.WithStack(err)
-	}
-
-	if len(opts) != 0 { // for client
-		client, ok := opts[0].(*Client)
-		if !ok {
-			return perrors.Errorf("opts[0] is not of type *Client")
-		}
-
-		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}
-		}
-	}
-
-	// read body
-	err = codec.ReadBody(p.Body)
-	return perrors.WithStack(err)
-}
-
-////////////////////////////////////////////
-// PendingResponse
-////////////////////////////////////////////
-
-// PendingResponse is a pending response.
-type PendingResponse struct {
-	seq       uint64
-	err       error
-	start     time.Time
-	readStart time.Time
-	callback  common.AsyncCallback
-	response  *Response
-	done      chan struct{}
-}
-
-// NewPendingResponse create a PendingResponses.
-func NewPendingResponse() *PendingResponse {
-	return &PendingResponse{
-		start:    time.Now(),
-		response: &Response{},
-		done:     make(chan struct{}),
-	}
-}
-
-// GetCallResponse get AsyncCallbackResponse.
-func (r PendingResponse) GetCallResponse() common.CallbackResponse {
-	return AsyncCallbackResponse{
-		Cause:     r.err,
-		Start:     r.start,
-		ReadStart: r.readStart,
-		Reply:     r.response,
-	}
-}
diff --git a/protocol/dubbo/dubbo_codec.go b/protocol/dubbo/dubbo_codec.go
new file mode 100644
index 0000000000000000000000000000000000000000..5e859a7fa2254ba0e4806bc60c037c47777bc641
--- /dev/null
+++ b/protocol/dubbo/dubbo_codec.go
@@ -0,0 +1,290 @@
+/*
+ * 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 (
+	"bytes"
+	"strconv"
+	"time"
+)
+
+import (
+	hessian "github.com/apache/dubbo-go-hessian2"
+	perrors "github.com/pkg/errors"
+)
+
+import (
+	"github.com/apache/dubbo-go/common/constant"
+	"github.com/apache/dubbo-go/common/logger"
+	"github.com/apache/dubbo-go/protocol"
+	"github.com/apache/dubbo-go/protocol/dubbo/impl"
+	"github.com/apache/dubbo-go/protocol/invocation"
+	"github.com/apache/dubbo-go/remoting"
+)
+
+//SerialID serial ID
+type SerialID byte
+
+func init() {
+	codec := &DubboCodec{}
+	// this is for registry dubboCodec of dubbo protocol
+	remoting.RegistryCodec("dubbo", codec)
+}
+
+// DubboCodec. It is implements remoting.Codec
+type DubboCodec struct {
+}
+
+// encode request for transport
+func (c *DubboCodec) EncodeRequest(request *remoting.Request) (*bytes.Buffer, error) {
+	if request.Event {
+		return c.encodeHeartbeartReqeust(request)
+	}
+
+	invoc, ok := request.Data.(*protocol.Invocation)
+	if !ok {
+		err := perrors.Errorf("encode request failed for parameter type :%+v", request)
+		logger.Errorf(err.Error())
+		return nil, err
+	}
+	invocation := *invoc
+
+	svc := impl.Service{}
+	svc.Path = invocation.AttachmentsByKey(constant.PATH_KEY, "")
+	svc.Interface = invocation.AttachmentsByKey(constant.INTERFACE_KEY, "")
+	svc.Version = invocation.AttachmentsByKey(constant.VERSION_KEY, "")
+	svc.Group = invocation.AttachmentsByKey(constant.GROUP_KEY, "")
+	svc.Method = invocation.MethodName()
+	timeout, err := strconv.Atoi(invocation.AttachmentsByKey(constant.TIMEOUT_KEY, strconv.Itoa(constant.DEFAULT_REMOTING_TIMEOUT)))
+	if err != nil {
+		// it will be wrapped in readwrite.Write .
+		return nil, perrors.WithStack(err)
+	}
+	svc.Timeout = time.Duration(timeout)
+
+	header := impl.DubboHeader{}
+	serialization := invocation.AttachmentsByKey(constant.SERIALIZATION_KEY, constant.HESSIAN2_SERIALIZATION)
+	if serialization == constant.PROTOBUF_SERIALIZATION {
+		header.SerialID = constant.S_Proto
+	} else {
+		header.SerialID = constant.S_Hessian2
+	}
+	header.ID = request.ID
+	if request.TwoWay {
+		header.Type = impl.PackageRequest_TwoWay
+	} else {
+		header.Type = impl.PackageRequest
+	}
+
+	pkg := &impl.DubboPackage{
+		Header:  header,
+		Service: svc,
+		Body:    impl.NewRequestPayload(invocation.Arguments(), invocation.Attachments()),
+		Err:     nil,
+		Codec:   impl.NewDubboCodec(nil),
+	}
+
+	if err := impl.LoadSerializer(pkg); err != nil {
+		return nil, perrors.WithStack(err)
+	}
+
+	return pkg.Marshal()
+}
+
+// encode heartbeart request
+func (c *DubboCodec) encodeHeartbeartReqeust(request *remoting.Request) (*bytes.Buffer, error) {
+	header := impl.DubboHeader{
+		Type:     impl.PackageHeartbeat,
+		SerialID: constant.S_Hessian2,
+		ID:       request.ID,
+	}
+
+	pkg := &impl.DubboPackage{
+		Header:  header,
+		Service: impl.Service{},
+		Body:    impl.NewRequestPayload([]interface{}{}, nil),
+		Err:     nil,
+		Codec:   impl.NewDubboCodec(nil),
+	}
+
+	if err := impl.LoadSerializer(pkg); err != nil {
+		return nil, err
+	}
+	return pkg.Marshal()
+}
+
+// encode response
+func (c *DubboCodec) EncodeResponse(response *remoting.Response) (*bytes.Buffer, error) {
+	var ptype = impl.PackageResponse
+	if response.IsHeartbeat() {
+		ptype = impl.PackageHeartbeat
+	}
+	resp := &impl.DubboPackage{
+		Header: impl.DubboHeader{
+			SerialID:       response.SerialID,
+			Type:           ptype,
+			ID:             response.ID,
+			ResponseStatus: response.Status,
+		},
+	}
+	if !response.IsHeartbeat() {
+		resp.Body = &impl.ResponsePayload{
+			RspObj:      response.Result.(protocol.RPCResult).Rest,
+			Exception:   response.Result.(protocol.RPCResult).Err,
+			Attachments: response.Result.(protocol.RPCResult).Attrs,
+		}
+	}
+
+	codec := impl.NewDubboCodec(nil)
+
+	pkg, err := codec.Encode(*resp)
+	if err != nil {
+		return nil, perrors.WithStack(err)
+	}
+
+	return bytes.NewBuffer(pkg), nil
+}
+
+// Decode data, including request and response.
+func (c *DubboCodec) Decode(data []byte) (remoting.DecodeResult, int, error) {
+	if c.isRequest(data) {
+		req, len, err := c.decodeRequest(data)
+		if err != nil {
+			return remoting.DecodeResult{}, len, perrors.WithStack(err)
+		}
+		return remoting.DecodeResult{IsRequest: true, Result: req}, len, perrors.WithStack(err)
+	}
+
+	resp, len, err := c.decodeResponse(data)
+	if err != nil {
+		return remoting.DecodeResult{}, len, perrors.WithStack(err)
+	}
+	return remoting.DecodeResult{IsRequest: false, Result: resp}, len, perrors.WithStack(err)
+}
+
+func (c *DubboCodec) isRequest(data []byte) bool {
+	if data[2]&byte(0x80) == 0x00 {
+		return false
+	}
+	return true
+}
+
+// decode request
+func (c *DubboCodec) decodeRequest(data []byte) (*remoting.Request, int, error) {
+	var request *remoting.Request = nil
+	buf := bytes.NewBuffer(data)
+	pkg := impl.NewDubboPackage(buf)
+	pkg.SetBody(make([]interface{}, 7))
+	err := pkg.Unmarshal()
+	if err != nil {
+		originErr := perrors.Cause(err)
+		if originErr == hessian.ErrHeaderNotEnough || originErr == hessian.ErrBodyNotEnough {
+			//FIXME
+			return nil, 0, originErr
+		}
+		logger.Errorf("pkg.Unmarshal(len(@data):%d) = error:%+v", buf.Len(), err)
+
+		return request, 0, perrors.WithStack(err)
+	}
+	request = &remoting.Request{
+		ID:       pkg.Header.ID,
+		SerialID: pkg.Header.SerialID,
+		TwoWay:   pkg.Header.Type&impl.PackageRequest_TwoWay != 0x00,
+		Event:    pkg.Header.Type&impl.PackageHeartbeat != 0x00,
+	}
+	if (pkg.Header.Type & impl.PackageHeartbeat) == 0x00 {
+		// convert params of request
+		req := pkg.Body.(map[string]interface{})
+
+		//invocation := request.Data.(*invocation.RPCInvocation)
+		var methodName string
+		var args []interface{}
+		attachments := make(map[string]interface{})
+		if req[impl.DubboVersionKey] != nil {
+			//dubbo version
+			request.Version = req[impl.DubboVersionKey].(string)
+		}
+		//path
+		attachments[constant.PATH_KEY] = pkg.Service.Path
+		//version
+		attachments[constant.VERSION_KEY] = pkg.Service.Version
+		//method
+		methodName = pkg.Service.Method
+		args = req[impl.ArgsKey].([]interface{})
+		attachments = req[impl.AttachmentsKey].(map[string]interface{})
+		invoc := invocation.NewRPCInvocationWithOptions(invocation.WithAttachments(attachments),
+			invocation.WithArguments(args), invocation.WithMethodName(methodName))
+		request.Data = invoc
+
+	}
+	return request, hessian.HEADER_LENGTH + pkg.Header.BodyLen, nil
+}
+
+// decode response
+func (c *DubboCodec) decodeResponse(data []byte) (*remoting.Response, int, error) {
+	buf := bytes.NewBuffer(data)
+	pkg := impl.NewDubboPackage(buf)
+	response := &remoting.Response{}
+	err := pkg.Unmarshal()
+	if err != nil {
+		originErr := perrors.Cause(err)
+		// if the data is very big, so the receive need much times.
+		if originErr == hessian.ErrHeaderNotEnough || originErr == hessian.ErrBodyNotEnough {
+			return nil, 0, originErr
+		}
+		logger.Errorf("pkg.Unmarshal(len(@data):%d) = error:%+v", buf.Len(), err)
+
+		return nil, 0, perrors.WithStack(err)
+	}
+	response = &remoting.Response{
+		ID: pkg.Header.ID,
+		//Version:  pkg.Header.,
+		SerialID: pkg.Header.SerialID,
+		Status:   pkg.Header.ResponseStatus,
+		Event:    (pkg.Header.Type & impl.PackageHeartbeat) != 0,
+	}
+	var error error
+	if pkg.Header.Type&impl.PackageHeartbeat != 0x00 {
+		if pkg.Header.Type&impl.PackageResponse != 0x00 {
+			logger.Debugf("get rpc heartbeat response{header: %#v, body: %#v}", pkg.Header, pkg.Body)
+			if pkg.Err != nil {
+				logger.Errorf("rpc heartbeat response{error: %#v}", pkg.Err)
+				error = pkg.Err
+			}
+		} else {
+			logger.Debugf("get rpc heartbeat request{header: %#v, service: %#v, body: %#v}", pkg.Header, pkg.Service, pkg.Body)
+			response.Status = hessian.Response_OK
+			//reply(session, p, hessian.PackageHeartbeat)
+		}
+		return response, hessian.HEADER_LENGTH + pkg.Header.BodyLen, error
+	}
+	logger.Debugf("get rpc response{header: %#v, body: %#v}", pkg.Header, pkg.Body)
+	rpcResult := &protocol.RPCResult{}
+	response.Result = rpcResult
+	if pkg.Header.Type&impl.PackageRequest == 0x00 {
+		if pkg.Err != nil {
+			rpcResult.Err = pkg.Err
+		} else if pkg.Body.(*impl.ResponsePayload).Exception != nil {
+			rpcResult.Err = pkg.Body.(*impl.ResponsePayload).Exception
+			response.Error = rpcResult.Err
+		}
+		rpcResult.Attrs = pkg.Body.(*impl.ResponsePayload).Attachments
+		rpcResult.Rest = pkg.Body.(*impl.ResponsePayload).RspObj
+	}
+
+	return response, hessian.HEADER_LENGTH + pkg.Header.BodyLen, nil
+}
diff --git a/protocol/dubbo/dubbo_exporter.go b/protocol/dubbo/dubbo_exporter.go
index dd80937c5bdf6718c2047b102115d8c08afcd899..1873a63fe14f1e2bbb415d5a22c157d03f9ae6ee 100644
--- a/protocol/dubbo/dubbo_exporter.go
+++ b/protocol/dubbo/dubbo_exporter.go
@@ -42,10 +42,9 @@ func NewDubboExporter(key string, invoker protocol.Invoker, exporterMap *sync.Ma
 
 // Unexport unexport dubbo service exporter.
 func (de *DubboExporter) Unexport() {
-	serviceId := de.GetInvoker().GetUrl().GetParam(constant.BEAN_NAME_KEY, "")
 	interfaceName := de.GetInvoker().GetUrl().GetParam(constant.INTERFACE_KEY, "")
 	de.BaseExporter.Unexport()
-	err := common.ServiceMap.UnRegister(interfaceName, DUBBO, serviceId)
+	err := common.ServiceMap.UnRegister(interfaceName, DUBBO, de.GetInvoker().GetUrl().ServiceKey())
 	if err != nil {
 		logger.Errorf("[DubboExporter.Unexport] error: %v", err)
 	}
diff --git a/protocol/dubbo/dubbo_invoker.go b/protocol/dubbo/dubbo_invoker.go
index 59202d5f49f30acb9e75c4e2f135601285f08e13..acddfd6cd51733794fcf2e93f096d898de7ec1cd 100644
--- a/protocol/dubbo/dubbo_invoker.go
+++ b/protocol/dubbo/dubbo_invoker.go
@@ -20,6 +20,7 @@ package dubbo
 import (
 	"context"
 	"strconv"
+	"strings"
 	"sync"
 	"sync/atomic"
 	"time"
@@ -34,8 +35,10 @@ 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"
 	invocation_impl "github.com/apache/dubbo-go/protocol/invocation"
+	"github.com/apache/dubbo-go/remoting"
 )
 
 var (
@@ -46,24 +49,35 @@ var (
 )
 
 var (
-	attachmentKey = []string{constant.INTERFACE_KEY, constant.GROUP_KEY, constant.TOKEN_KEY, constant.TIMEOUT_KEY}
+	attachmentKey = []string{constant.INTERFACE_KEY, constant.GROUP_KEY, constant.TOKEN_KEY, constant.TIMEOUT_KEY,
+		constant.VERSION_KEY}
 )
 
-// DubboInvoker is dubbo client invoker.
+// DubboInvoker is implement of protocol.Invoker. A dubboInvoker refer to one service and ip.
 type DubboInvoker struct {
 	protocol.BaseInvoker
-	client   *Client
+	// the exchange layer, it is focus on network communication.
+	client   *remoting.ExchangeClient
 	quitOnce sync.Once
+	// timeout for service(interface) level.
+	timeout time.Duration
 	// Used to record the number of requests. -1 represent this DubboInvoker is destroyed
 	reqNum int64
 }
 
-// NewDubboInvoker create dubbo client invoker.
-func NewDubboInvoker(url common.URL, client *Client) *DubboInvoker {
+// NewDubboInvoker constructor
+func NewDubboInvoker(url *common.URL, client *remoting.ExchangeClient) *DubboInvoker {
+	requestTimeout := config.GetConsumerConfig().RequestTimeout
+
+	requestTimeoutStr := url.GetParam(constant.TIMEOUT_KEY, config.GetConsumerConfig().Request_Timeout)
+	if t, err := time.ParseDuration(requestTimeoutStr); err == nil {
+		requestTimeout = t
+	}
 	return &DubboInvoker{
 		BaseInvoker: *protocol.NewBaseInvoker(url),
 		client:      client,
 		reqNum:      0,
+		timeout:     requestTimeout,
 	}
 }
 
@@ -84,6 +98,8 @@ func (di *DubboInvoker) Invoke(ctx context.Context, invocation protocol.Invocati
 	defer atomic.AddInt64(&(di.reqNum), -1)
 
 	inv := invocation.(*invocation_impl.RPCInvocation)
+	// init param
+	inv.SetAttachments(constant.PATH_KEY, di.GetUrl().GetParam(constant.INTERFACE_KEY, ""))
 	for _, k := range attachmentKey {
 		if v := di.GetUrl().GetParam(k, ""); len(v) > 0 {
 			inv.SetAttachments(k, v)
@@ -94,35 +110,61 @@ func (di *DubboInvoker) Invoke(ctx context.Context, invocation protocol.Invocati
 	di.appendCtx(ctx, inv)
 
 	url := di.GetUrl()
+	// default hessian2 serialization, compatible
+	if url.GetParam(constant.SERIALIZATION_KEY, "") == "" {
+		url.SetParam(constant.SERIALIZATION_KEY, constant.HESSIAN2_SERIALIZATION)
+	}
 	// async
 	async, err := strconv.ParseBool(inv.AttachmentsByKey(constant.ASYNC_KEY, "false"))
 	if err != nil {
 		logger.Errorf("ParseBool - error: %v", err)
 		async = false
 	}
-	response := NewResponse(inv.Reply(), nil)
+	//response := NewResponse(inv.Reply(), nil)
+	rest := &protocol.RPCResult{}
+	timeout := di.getTimeout(inv)
 	if async {
 		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)
+			//result.Err = di.client.AsyncCall(NewRequest(url.Location, url, inv.MethodName(), inv.Arguments(), inv.Attachments()), callBack, response)
+			result.Err = di.client.AsyncRequest(&invocation, url, timeout, callBack, rest)
 		} else {
-			result.Err = di.client.CallOneway(NewRequest(url.Location, url, inv.MethodName(), inv.Arguments(), inv.Attachments()))
+			result.Err = di.client.Send(&invocation, url, timeout)
 		}
 	} else {
 		if inv.Reply() == nil {
 			result.Err = ErrNoReply
 		} else {
-			result.Err = di.client.Call(NewRequest(url.Location, url, inv.MethodName(), inv.Arguments(), inv.Attachments()), response)
+			result.Err = di.client.Request(&invocation, url, timeout, rest)
 		}
 	}
 	if result.Err == nil {
 		result.Rest = inv.Reply()
-		result.Attrs = response.atta
+		result.Attrs = rest.Attrs
 	}
 	logger.Debugf("result.Err: %v, result.Rest: %v", result.Err, result.Rest)
 
 	return &result
 }
 
+// get timeout including methodConfig
+func (di *DubboInvoker) getTimeout(invocation *invocation_impl.RPCInvocation) time.Duration {
+	var timeout = di.GetUrl().GetParam(strings.Join([]string{constant.METHOD_KEYS, invocation.MethodName(), constant.TIMEOUT_KEY}, "."), "")
+	if len(timeout) != 0 {
+		if t, err := time.ParseDuration(timeout); err == nil {
+			// config timeout into attachment
+			invocation.SetAttachments(constant.TIMEOUT_KEY, strconv.Itoa(int(t.Milliseconds())))
+			return t
+		}
+	}
+	// set timeout into invocation at method level
+	invocation.SetAttachments(constant.TIMEOUT_KEY, strconv.Itoa(int(di.timeout.Milliseconds())))
+	return di.timeout
+}
+
+func (di *DubboInvoker) IsAvailable() bool {
+	return di.client.IsAvailable()
+}
+
 // Destroy destroy dubbo client invoker.
 func (di *DubboInvoker) Destroy() {
 	di.quitOnce.Do(func() {
@@ -150,8 +192,7 @@ func (di *DubboInvoker) appendCtx(ctx context.Context, inv *invocation_impl.RPCI
 	// inject opentracing ctx
 	currentSpan := opentracing.SpanFromContext(ctx)
 	if currentSpan != nil {
-		carrier := opentracing.TextMapCarrier(inv.Attachments())
-		err := opentracing.GlobalTracer().Inject(currentSpan.Context(), opentracing.TextMap, carrier)
+		err := injectTraceCtx(currentSpan, inv)
 		if err != nil {
 			logger.Errorf("Could not inject the span context into attachments: %v", err)
 		}
diff --git a/protocol/dubbo/dubbo_invoker_test.go b/protocol/dubbo/dubbo_invoker_test.go
index c0640d5558fcb9fb00f02eba0fddc54bb4162592..c7a9a2697529d096e57585347b0a8b0a535451b0 100644
--- a/protocol/dubbo/dubbo_invoker_test.go
+++ b/protocol/dubbo/dubbo_invoker_test.go
@@ -18,6 +18,7 @@
 package dubbo
 
 import (
+	"bytes"
 	"context"
 	"sync"
 	"testing"
@@ -25,40 +26,37 @@ import (
 )
 
 import (
+	hessian "github.com/apache/dubbo-go-hessian2"
 	"github.com/opentracing/opentracing-go"
+	perrors "github.com/pkg/errors"
 	"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/proxy/proxy_factory"
+	"github.com/apache/dubbo-go/protocol"
 	"github.com/apache/dubbo-go/protocol/invocation"
+	"github.com/apache/dubbo-go/remoting"
+	"github.com/apache/dubbo-go/remoting/getty"
 )
 
 func TestDubboInvokerInvoke(t *testing.T) {
 	proto, url := InitTest(t)
 
-	c := &Client{
-		pendingResponses: new(sync.Map),
-		conf:             *clientConf,
-		opts: Options{
-			ConnectTimeout: 3 * time.Second,
-			RequestTimeout: 6 * time.Second,
-		},
-	}
-	c.pool = newGettyRPCClientConnPool(c, clientConf.PoolSize, time.Duration(int(time.Second)*clientConf.PoolTTL))
+	c := getExchangeClient(url)
 
 	invoker := NewDubboInvoker(url, c)
 	user := &User{}
 
-	inv := invocation.NewRPCInvocationWithOptions(invocation.WithMethodName(mockMethodNameGetUser), invocation.WithArguments([]interface{}{"1", "username"}),
-		invocation.WithReply(user), invocation.WithAttachments(map[string]string{"test_key": "test_value"}))
+	inv := invocation.NewRPCInvocationWithOptions(invocation.WithMethodName("GetUser"), invocation.WithArguments([]interface{}{"1", "username"}),
+		invocation.WithReply(user), invocation.WithAttachments(map[string]interface{}{"test_key": "test_value"}))
 
 	// Call
 	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")
@@ -69,8 +67,10 @@ func TestDubboInvokerInvoke(t *testing.T) {
 	lock := sync.Mutex{}
 	lock.Lock()
 	inv.SetCallBack(func(response common.CallbackResponse) {
-		r := response.(AsyncCallbackResponse)
-		assert.Equal(t, User{Id: "1", Name: "username"}, *r.Reply.(*Response).reply.(*User))
+		r := response.(remoting.AsyncCallbackResponse)
+		rst := *r.Reply.(*remoting.Response).Result.(*protocol.RPCResult)
+		assert.Equal(t, User{Id: "1", Name: "username"}, *(rst.Rest.(*User)))
+		//assert.Equal(t, User{ID: "1", Name: "username"}, *r.Reply.(*Response).reply.(*User))
 		lock.Unlock()
 	})
 	res = invoker.Invoke(context.Background(), inv)
@@ -89,6 +89,145 @@ func TestDubboInvokerInvoke(t *testing.T) {
 
 	// destroy
 	lock.Lock()
+	defer lock.Unlock()
 	proto.Destroy()
-	lock.Unlock()
+}
+
+func InitTest(t *testing.T) (protocol.Protocol, *common.URL) {
+
+	hessian.RegisterPOJO(&User{})
+
+	methods, err := common.ServiceMap.Register("com.ikurento.user.UserProvider", "dubbo", "", "", &UserProvider{})
+	assert.NoError(t, err)
+	assert.Equal(t, "GetBigPkg,GetUser,GetUser0,GetUser1,GetUser2,GetUser3,GetUser4,GetUser5,GetUser6", methods)
+
+	// config
+	getty.SetClientConf(getty.ClientConfig{
+		ConnectionNum:   2,
+		HeartbeatPeriod: "5s",
+		SessionTimeout:  "20s",
+		PoolTTL:         600,
+		PoolSize:        64,
+		GettySessionParam: getty.GettySessionParam{
+			CompressEncoding: false,
+			TcpNoDelay:       true,
+			TcpKeepAlive:     true,
+			KeepAlivePeriod:  "120s",
+			TcpRBufSize:      262144,
+			TcpWBufSize:      65536,
+			PkgWQSize:        512,
+			TcpReadTimeout:   "4s",
+			TcpWriteTimeout:  "5s",
+			WaitTimeout:      "1s",
+			MaxMsgLen:        10240000000,
+			SessionName:      "client",
+		},
+	})
+	getty.SetServerConfig(getty.ServerConfig{
+		SessionNumber:  700,
+		SessionTimeout: "20s",
+		GettySessionParam: getty.GettySessionParam{
+			CompressEncoding: false,
+			TcpNoDelay:       true,
+			TcpKeepAlive:     true,
+			KeepAlivePeriod:  "120s",
+			TcpRBufSize:      262144,
+			TcpWBufSize:      65536,
+			PkgWQSize:        512,
+			TcpReadTimeout:   "1s",
+			TcpWriteTimeout:  "5s",
+			WaitTimeout:      "1s",
+			MaxMsgLen:        10240000000,
+			SessionName:      "server",
+		}})
+
+	// Export
+	proto := GetProtocol()
+	url, err := common.NewURL("dubbo://127.0.0.1:20702/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{
+		BaseInvoker: *protocol.NewBaseInvoker(url),
+	})
+
+	time.Sleep(time.Second * 2)
+
+	return proto, url
+}
+
+//////////////////////////////////
+// provider
+//////////////////////////////////
+
+type (
+	User struct {
+		Id   string `json:"id"`
+		Name string `json:"name"`
+	}
+
+	UserProvider struct {
+		user map[string]User
+	}
+)
+
+// size:4801228
+func (u *UserProvider) GetBigPkg(ctx context.Context, req []interface{}, rsp *User) error {
+	argBuf := new(bytes.Buffer)
+	for i := 0; i < 800; i++ {
+		// use chinese for test
+		argBuf.WriteString("鍑婚紦鍏堕晽锛岃笂璺冪敤鍏点€傚湡鍥藉煄婕曪紝鎴戠嫭鍗楄銆備粠瀛欏瓙浠诧紝骞抽檲涓庡畫銆備笉鎴戜互褰掞紝蹇у績鏈夊俊銆傜埌灞呯埌澶勶紵鐖颁抚鍏堕┈锛熶簬浠ユ眰涔嬶紵浜庢灄涔嬩笅銆傛鐢熷闃旓紝涓庡瓙鎴愯銆傛墽瀛愪箣鎵嬶紝涓庡瓙鍋曡€併€備簬鍡熼様鍏紝涓嶆垜娲诲叜銆備簬鍡熸吹鍏紝涓嶆垜淇″叜銆�")
+	}
+	rsp.Id = argBuf.String()
+	rsp.Name = argBuf.String()
+	return nil
+}
+
+func (u *UserProvider) GetUser(ctx context.Context, req []interface{}, rsp *User) error {
+	rsp.Id = req[0].(string)
+	rsp.Name = req[1].(string)
+	return nil
+}
+
+func (u *UserProvider) GetUser0(id string, k *User, name string) (User, error) {
+	return User{Id: id, Name: name}, nil
+}
+
+func (u *UserProvider) GetUser1() error {
+	return nil
+}
+
+func (u *UserProvider) GetUser2() error {
+	return perrors.New("error")
+}
+
+func (u *UserProvider) GetUser3(rsp *[]interface{}) error {
+	*rsp = append(*rsp, User{Id: "1", Name: "username"})
+	return nil
+}
+
+func (u *UserProvider) GetUser4(ctx context.Context, req []interface{}) ([]interface{}, error) {
+
+	return []interface{}{User{Id: req[0].([]interface{})[0].(string), Name: req[0].([]interface{})[1].(string)}}, nil
+}
+
+func (u *UserProvider) GetUser5(ctx context.Context, req []interface{}) (map[interface{}]interface{}, error) {
+	return map[interface{}]interface{}{"key": User{Id: req[0].(map[interface{}]interface{})["id"].(string), Name: req[0].(map[interface{}]interface{})["name"].(string)}}, nil
+}
+
+func (u *UserProvider) GetUser6(id int64) (*User, error) {
+	if id == 0 {
+		return nil, nil
+	}
+	return &User{Id: "1"}, nil
+}
+
+func (u *UserProvider) Reference() string {
+	return "UserProvider"
+}
+
+func (u User) JavaClassName() string {
+	return "com.ikurento.user.User"
 }
diff --git a/protocol/dubbo/dubbo_protocol.go b/protocol/dubbo/dubbo_protocol.go
index 9eeefd079279d82241da8e21df5edfe77b8003e0..4f03b8aba061ea9b37b35d89142eb7bec80f3a97 100644
--- a/protocol/dubbo/dubbo_protocol.go
+++ b/protocol/dubbo/dubbo_protocol.go
@@ -18,10 +18,16 @@
 package dubbo
 
 import (
+	"context"
+	"fmt"
 	"sync"
 	"time"
 )
 
+import (
+	"github.com/opentracing/opentracing-go"
+)
+
 import (
 	"github.com/apache/dubbo-go/common"
 	"github.com/apache/dubbo-go/common/constant"
@@ -29,14 +35,23 @@ import (
 	"github.com/apache/dubbo-go/common/logger"
 	"github.com/apache/dubbo-go/config"
 	"github.com/apache/dubbo-go/protocol"
+	"github.com/apache/dubbo-go/protocol/invocation"
+	"github.com/apache/dubbo-go/remoting"
+	"github.com/apache/dubbo-go/remoting/getty"
 )
 
-// dubbo protocol constant
 const (
 	// DUBBO is dubbo protocol name
 	DUBBO = "dubbo"
 )
 
+var (
+	// Make the connection can be shared.
+	// It will create one connection for one address (ip+port)
+	exchangeClientMap = new(sync.Map)
+	exchangeLock      = new(sync.Map)
+)
+
 func init() {
 	extension.SetProtocol(DUBBO, GetProtocol)
 }
@@ -45,10 +60,12 @@ var (
 	dubboProtocol *DubboProtocol
 )
 
-// DubboProtocol is a dubbo protocol implement.
+// It support dubbo protocol. It implements Protocol interface for dubbo protocol.
 type DubboProtocol struct {
 	protocol.BaseProtocol
-	serverMap  map[string]*Server
+	// It is store relationship about serviceKey(group/interface:version) and ExchangeServer
+	// The ExchangeServer is introduced to replace of Server. Because Server is depend on getty directly.
+	serverMap  map[string]*remoting.ExchangeServer
 	serverLock sync.Mutex
 }
 
@@ -56,7 +73,7 @@ type DubboProtocol struct {
 func NewDubboProtocol() *DubboProtocol {
 	return &DubboProtocol{
 		BaseProtocol: protocol.NewBaseProtocol(),
-		serverMap:    make(map[string]*Server),
+		serverMap:    make(map[string]*remoting.ExchangeServer),
 	}
 }
 
@@ -67,26 +84,19 @@ func (dp *DubboProtocol) Export(invoker protocol.Invoker) protocol.Exporter {
 	exporter := NewDubboExporter(serviceKey, invoker, dp.ExporterMap())
 	dp.SetExporterMap(serviceKey, exporter)
 	logger.Infof("Export service: %s", url.String())
-
 	// start server
 	dp.openServer(url)
 	return exporter
 }
 
 // Refer create dubbo service reference.
-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
+func (dp *DubboProtocol) Refer(url *common.URL) protocol.Invoker {
+	exchangeClient := getExchangeClient(url)
+	if exchangeClient == nil {
+		logger.Warnf("can't dial the server: %+v", url.Location)
+		return nil
 	}
-
-	invoker := NewDubboInvoker(url, NewClient(Options{
-		ConnectTimeout: config.GetConsumerConfig().ConnectTimeout,
-		RequestTimeout: requestTimeout,
-	}))
+	invoker := NewDubboInvoker(url, exchangeClient)
 	dp.SetInvokers(invoker)
 	logger.Infof("Refer service: %s", url.String())
 	return invoker
@@ -105,7 +115,7 @@ func (dp *DubboProtocol) Destroy() {
 	}
 }
 
-func (dp *DubboProtocol) openServer(url common.URL) {
+func (dp *DubboProtocol) openServer(url *common.URL) {
 	_, ok := dp.serverMap[url.Location]
 	if !ok {
 		_, ok := dp.ExporterMap().Load(url.ServiceKey())
@@ -116,9 +126,12 @@ func (dp *DubboProtocol) openServer(url common.URL) {
 		dp.serverLock.Lock()
 		_, ok = dp.serverMap[url.Location]
 		if !ok {
-			srv := NewServer()
+			handler := func(invocation *invocation.RPCInvocation) protocol.RPCResult {
+				return doHandleRequest(invocation)
+			}
+			srv := remoting.NewExchangeServer(url, getty.NewServer(url, handler))
 			dp.serverMap[url.Location] = srv
-			srv.Start(url)
+			srv.Start()
 		}
 		dp.serverLock.Unlock()
 	}
@@ -131,3 +144,91 @@ func GetProtocol() protocol.Protocol {
 	}
 	return dubboProtocol
 }
+
+func doHandleRequest(rpcInvocation *invocation.RPCInvocation) protocol.RPCResult {
+	exporter, _ := dubboProtocol.ExporterMap().Load(rpcInvocation.ServiceKey())
+	result := protocol.RPCResult{}
+	if exporter == nil {
+		err := fmt.Errorf("don't have this exporter, key: %s", rpcInvocation.ServiceKey())
+		logger.Errorf(err.Error())
+		result.Err = err
+		//reply(session, p, hessian.PackageResponse)
+		return result
+	}
+	invoker := exporter.(protocol.Exporter).GetInvoker()
+	if invoker != nil {
+		// FIXME
+		ctx := rebuildCtx(rpcInvocation)
+
+		invokeResult := invoker.Invoke(ctx, rpcInvocation)
+		if err := invokeResult.Error(); err != nil {
+			result.Err = invokeResult.Error()
+			//p.Header.ResponseStatus = hessian.Response_OK
+			//p.Body = hessian.NewResponse(nil, err, result.Attachments())
+		} else {
+			result.Rest = invokeResult.Result()
+			//p.Header.ResponseStatus = hessian.Response_OK
+			//p.Body = hessian.NewResponse(res, nil, result.Attachments())
+		}
+	} else {
+		result.Err = fmt.Errorf("don't have the invoker, key: %s", rpcInvocation.ServiceKey())
+	}
+	return result
+}
+
+func getExchangeClient(url *common.URL) *remoting.ExchangeClient {
+	clientTmp, ok := exchangeClientMap.Load(url.Location)
+	if !ok {
+		var exchangeClientTmp *remoting.ExchangeClient
+		func() {
+			// lock for NewExchangeClient and store into map.
+			_, loaded := exchangeLock.LoadOrStore(url.Location, 0x00)
+			// unlock
+			defer exchangeLock.Delete(url.Location)
+			if loaded {
+				// retry for 5 times.
+				for i := 0; i < 5; i++ {
+					if clientTmp, ok = exchangeClientMap.Load(url.Location); ok {
+						break
+					} else {
+						// if cannot get, sleep a while.
+						time.Sleep(time.Duration(i*100) * time.Millisecond)
+					}
+				}
+				return
+			}
+			// new ExchangeClient
+			exchangeClientTmp = remoting.NewExchangeClient(url, getty.NewClient(getty.Options{
+				ConnectTimeout: config.GetConsumerConfig().ConnectTimeout,
+				RequestTimeout: config.GetConsumerConfig().RequestTimeout,
+			}), config.GetConsumerConfig().ConnectTimeout, false)
+			// input store
+			if exchangeClientTmp != nil {
+				exchangeClientMap.Store(url.Location, exchangeClientTmp)
+			}
+		}()
+		if exchangeClientTmp != nil {
+			return exchangeClientTmp
+		}
+	}
+	// cannot dial the server
+	if clientTmp == nil {
+		return nil
+	}
+	return clientTmp.(*remoting.ExchangeClient)
+}
+
+// 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.WithValue(context.Background(), "attachment", inv.Attachments())
+
+	// actually, if user do not use any opentracing framework, the err will not be nil.
+	spanCtx, err := opentracing.GlobalTracer().Extract(opentracing.TextMap,
+		opentracing.TextMapCarrier(filterContext(inv.Attachments())))
+	if err == nil {
+		ctx = context.WithValue(ctx, constant.TRACING_REMOTE_SPAN_CTX, spanCtx)
+	}
+	return ctx
+}
diff --git a/protocol/dubbo/dubbo_protocol_test.go b/protocol/dubbo/dubbo_protocol_test.go
index 6f3892be67be533dea09dc7bd54de56844dbc79c..9eba90e9da564453649b378f061bc0179ffedc5e 100644
--- a/protocol/dubbo/dubbo_protocol_test.go
+++ b/protocol/dubbo/dubbo_protocol_test.go
@@ -28,7 +28,9 @@ import (
 import (
 	"github.com/apache/dubbo-go/common"
 	"github.com/apache/dubbo-go/common/constant"
+	"github.com/apache/dubbo-go/common/proxy/proxy_factory"
 	"github.com/apache/dubbo-go/protocol"
+	"github.com/apache/dubbo-go/remoting/getty"
 )
 
 const (
@@ -39,14 +41,56 @@ const (
 		"side=provider&timeout=3000&timestamp=1556509797245"
 )
 
-func TestDubboProtocolExport(t *testing.T) {
+func initDubboInvokerTest() {
+	getty.SetServerConfig(getty.ServerConfig{
+		SessionNumber:  700,
+		SessionTimeout: "20s",
+		GettySessionParam: getty.GettySessionParam{
+			CompressEncoding: false,
+			TcpNoDelay:       true,
+			TcpKeepAlive:     true,
+			KeepAlivePeriod:  "120s",
+			TcpRBufSize:      262144,
+			TcpWBufSize:      65536,
+			PkgWQSize:        512,
+			TcpReadTimeout:   "1s",
+			TcpWriteTimeout:  "5s",
+			WaitTimeout:      "1s",
+			MaxMsgLen:        10240000000,
+			SessionName:      "server",
+		}})
+	getty.SetClientConf(getty.ClientConfig{
+		ConnectionNum:   1,
+		HeartbeatPeriod: "3s",
+		SessionTimeout:  "20s",
+		PoolTTL:         600,
+		PoolSize:        64,
+		GettySessionParam: getty.GettySessionParam{
+			CompressEncoding: false,
+			TcpNoDelay:       true,
+			TcpKeepAlive:     true,
+			KeepAlivePeriod:  "120s",
+			TcpRBufSize:      262144,
+			TcpWBufSize:      65536,
+			PkgWQSize:        512,
+			TcpReadTimeout:   "4s",
+			TcpWriteTimeout:  "5s",
+			WaitTimeout:      "1s",
+			MaxMsgLen:        10240000000,
+			SessionName:      "client",
+		},
+	})
+}
+
+func TestDubboProtocol_Export(t *testing.T) {
+	initDubboInvokerTest()
+	srvCfg := getty.GetDefaultServerConfig()
+	getty.SetServerConfig(srvCfg)
 	// Export
 	proto := GetProtocol()
-	srvConf = &ServerConfig{}
 	url, err := common.NewURL(mockCommonUrl)
 	assert.NoError(t, err)
 	exporter := proto.Export(protocol.NewBaseInvoker(url))
-
 	// make sure url
 	eq := exporter.GetInvoker().GetUrl().URLEqual(url)
 	assert.True(t, eq)
@@ -60,10 +104,10 @@ func TestDubboProtocolExport(t *testing.T) {
 	assert.True(t, eq2)
 
 	// make sure exporterMap after 'Unexport'
-	_, ok := proto.(*DubboProtocol).ExporterMap().Load(url.ServiceKey())
+	_, ok := proto.(*DubboProtocol).ExporterMap().Load(url2.ServiceKey())
 	assert.True(t, ok)
-	exporter.Unexport()
-	_, ok = proto.(*DubboProtocol).ExporterMap().Load(url.ServiceKey())
+	exporter2.Unexport()
+	_, ok = proto.(*DubboProtocol).ExporterMap().Load(url2.ServiceKey())
 	assert.False(t, ok)
 
 	// make sure serverMap after 'Destroy'
@@ -74,14 +118,29 @@ func TestDubboProtocolExport(t *testing.T) {
 	assert.False(t, ok)
 }
 
-func TestDubboProtocolRefer(t *testing.T) {
+func TestDubboProtocolReferNoConnect(t *testing.T) {
 	// Refer
+	initDubboInvokerTest()
 	proto := GetProtocol()
 	url, err := common.NewURL(mockCommonUrl)
 	assert.NoError(t, err)
-	clientConf = &ClientConfig{}
 	invoker := proto.Refer(url)
+	assert.Nil(t, invoker)
+}
+
+func TestDubboProtocol_Refer(t *testing.T) {
+	initDubboInvokerTest()
+	cliCfg := getty.GetDefaultClientConfig()
+	getty.SetClientConf(cliCfg)
+	// Refer
+	proto := GetProtocol()
 
+	url, err := common.NewURL(mockCommonUrl)
+	proto.Export(&proxy_factory.ProxyInvoker{
+		BaseInvoker: *protocol.NewBaseInvoker(url),
+	})
+	assert.NoError(t, err)
+	invoker := proto.Refer(url)
 	// make sure url
 	eq := invoker.GetUrl().URLEqual(url)
 	assert.True(t, eq)
diff --git a/protocol/dubbo/hessian2/const.go b/protocol/dubbo/hessian2/const.go
new file mode 100644
index 0000000000000000000000000000000000000000..74a00b601db22397916aab215ccd33bc918d91e7
--- /dev/null
+++ b/protocol/dubbo/hessian2/const.go
@@ -0,0 +1,243 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// This constants are also defined in dubbo constants.  But we will still used these until hessian is stable.
+
+package hessian2
+
+import (
+	"reflect"
+	"regexp"
+)
+
+import (
+	perrors "github.com/pkg/errors"
+)
+
+const (
+	mask = byte(127)
+	flag = byte(128)
+)
+
+const (
+	// Zero : byte zero
+	Zero = byte(0x00)
+)
+
+// constansts
+const (
+	TAG_READ        = int32(-1)
+	ASCII_GAP       = 32
+	CHUNK_SIZE      = 4096
+	BC_BINARY       = byte('B') // final chunk
+	BC_BINARY_CHUNK = byte('A') // non-final chunk
+
+	BC_BINARY_DIRECT  = byte(0x20) // 1-byte length binary
+	BINARY_DIRECT_MAX = byte(0x0f)
+	BC_BINARY_SHORT   = byte(0x34) // 2-byte length binary
+	BINARY_SHORT_MAX  = 0x3ff      // 0-1023 binary
+
+	BC_DATE        = byte(0x4a) // 64-bit millisecond UTC date
+	BC_DATE_MINUTE = byte(0x4b) // 32-bit minute UTC date
+
+	BC_DOUBLE = byte('D') // IEEE 64-bit double
+
+	BC_DOUBLE_ZERO  = byte(0x5b)
+	BC_DOUBLE_ONE   = byte(0x5c)
+	BC_DOUBLE_BYTE  = byte(0x5d)
+	BC_DOUBLE_SHORT = byte(0x5e)
+	BC_DOUBLE_MILL  = byte(0x5f)
+
+	BC_FALSE = byte('F') // boolean false
+
+	BC_INT = byte('I') // 32-bit int
+
+	INT_DIRECT_MIN = -0x10
+	INT_DIRECT_MAX = byte(0x2f)
+	BC_INT_ZERO    = byte(0x90)
+
+	INT_BYTE_MIN     = -0x800
+	INT_BYTE_MAX     = 0x7ff
+	BC_INT_BYTE_ZERO = byte(0xc8)
+
+	BC_END = byte('Z')
+
+	INT_SHORT_MIN     = -0x40000
+	INT_SHORT_MAX     = 0x3ffff
+	BC_INT_SHORT_ZERO = byte(0xd4)
+
+	BC_LIST_VARIABLE           = byte(0x55)
+	BC_LIST_FIXED              = byte('V')
+	BC_LIST_VARIABLE_UNTYPED   = byte(0x57)
+	BC_LIST_FIXED_UNTYPED      = byte(0x58)
+	_listFixedTypedLenTagMin   = byte(0x70)
+	_listFixedTypedLenTagMax   = byte(0x77)
+	_listFixedUntypedLenTagMin = byte(0x78)
+	_listFixedUntypedLenTagMax = byte(0x7f)
+
+	BC_LIST_DIRECT         = byte(0x70)
+	BC_LIST_DIRECT_UNTYPED = byte(0x78)
+	LIST_DIRECT_MAX        = byte(0x7)
+
+	BC_LONG         = byte('L') // 64-bit signed integer
+	LONG_DIRECT_MIN = -0x08
+	LONG_DIRECT_MAX = byte(0x0f)
+	BC_LONG_ZERO    = byte(0xe0)
+
+	LONG_BYTE_MIN     = -0x800
+	LONG_BYTE_MAX     = 0x7ff
+	BC_LONG_BYTE_ZERO = byte(0xf8)
+
+	LONG_SHORT_MIN     = -0x40000
+	LONG_SHORT_MAX     = 0x3ffff
+	BC_LONG_SHORT_ZERO = byte(0x3c)
+
+	BC_LONG_INT = byte(0x59)
+
+	BC_MAP         = byte('M')
+	BC_MAP_UNTYPED = byte('H')
+
+	BC_NULL = byte('N') // x4e
+
+	BC_OBJECT     = byte('O')
+	BC_OBJECT_DEF = byte('C')
+
+	BC_OBJECT_DIRECT  = byte(0x60)
+	OBJECT_DIRECT_MAX = byte(0x0f)
+
+	BC_REF = byte(0x51)
+
+	BC_STRING       = byte('S') // final string
+	BC_STRING_CHUNK = byte('R') // non-final string
+
+	BC_STRING_DIRECT  = byte(0x00)
+	STRING_DIRECT_MAX = byte(0x1f)
+	BC_STRING_SHORT   = byte(0x30)
+	STRING_SHORT_MAX  = 0x3ff
+
+	BC_TRUE = byte('T')
+
+	P_PACKET_CHUNK = byte(0x4f)
+	P_PACKET       = byte('P')
+
+	P_PACKET_DIRECT   = byte(0x80)
+	PACKET_DIRECT_MAX = byte(0x7f)
+
+	P_PACKET_SHORT   = byte(0x70)
+	PACKET_SHORT_MAX = 0xfff
+	ARRAY_STRING     = "[string"
+	ARRAY_INT        = "[int"
+	ARRAY_DOUBLE     = "[double"
+	ARRAY_FLOAT      = "[float"
+	ARRAY_BOOL       = "[boolean"
+	ARRAY_LONG       = "[long"
+
+	PATH_KEY      = "path"
+	GROUP_KEY     = "group"
+	INTERFACE_KEY = "interface"
+	VERSION_KEY   = "version"
+	TIMEOUT_KEY   = "timeout"
+
+	STRING_NIL   = ""
+	STRING_TRUE  = "true"
+	STRING_FALSE = "false"
+	STRING_ZERO  = "0.0"
+	STRING_ONE   = "1.0"
+)
+
+// DubboResponse related consts
+const (
+	Response_OK                byte = 20
+	Response_CLIENT_TIMEOUT    byte = 30
+	Response_SERVER_TIMEOUT    byte = 31
+	Response_BAD_REQUEST       byte = 40
+	Response_BAD_RESPONSE      byte = 50
+	Response_SERVICE_NOT_FOUND byte = 60
+	Response_SERVICE_ERROR     byte = 70
+	Response_SERVER_ERROR      byte = 80
+	Response_CLIENT_ERROR      byte = 90
+
+	// According to "java dubbo" There are two cases of response:
+	// 		1. with attachments
+	// 		2. no attachments
+	RESPONSE_WITH_EXCEPTION                  int32 = 0
+	RESPONSE_VALUE                           int32 = 1
+	RESPONSE_NULL_VALUE                      int32 = 2
+	RESPONSE_WITH_EXCEPTION_WITH_ATTACHMENTS int32 = 3
+	RESPONSE_VALUE_WITH_ATTACHMENTS          int32 = 4
+	RESPONSE_NULL_VALUE_WITH_ATTACHMENTS     int32 = 5
+)
+
+/**
+ * the dubbo protocol header length is 16 Bytes.
+ * the first 2 Bytes is magic code '0xdabb'
+ * the next 1 Byte is message flags, in which its 16-20 bit is serial id, 21 for event, 22 for two way, 23 for request/response flag
+ * the next 1 Bytes is response state.
+ * the next 8 Bytes is package DI.
+ * the next 4 Bytes is package length.
+ **/
+const (
+	// header length.
+	HEADER_LENGTH = 16
+
+	// magic header
+	MAGIC      = uint16(0xdabb)
+	MAGIC_HIGH = byte(0xda)
+	MAGIC_LOW  = byte(0xbb)
+
+	// message flag.
+	FLAG_REQUEST = byte(0x80)
+	FLAG_TWOWAY  = byte(0x40)
+	FLAG_EVENT   = byte(0x20) // for heartbeat
+	SERIAL_MASK  = 0x1f
+
+	DUBBO_VERSION                          = "2.5.4"
+	DUBBO_VERSION_KEY                      = "dubbo"
+	DEFAULT_DUBBO_PROTOCOL_VERSION         = "2.0.2" // Dubbo RPC protocol version, for compatibility, it must not be between 2.0.10 ~ 2.6.2
+	LOWEST_VERSION_FOR_RESPONSE_ATTACHMENT = 2000200
+	DEFAULT_LEN                            = 8388608 // 8 * 1024 * 1024 default body max length
+)
+
+// regular
+const (
+	JAVA_IDENT_REGEX = "(?:[_$a-zA-Z][_$a-zA-Z0-9]*)"
+	CLASS_DESC       = "(?:L" + JAVA_IDENT_REGEX + "(?:\\/" + JAVA_IDENT_REGEX + ")*;)"
+	ARRAY_DESC       = "(?:\\[+(?:(?:[VZBCDFIJS])|" + CLASS_DESC + "))"
+	DESC_REGEX       = "(?:(?:[VZBCDFIJS])|" + CLASS_DESC + "|" + ARRAY_DESC + ")"
+)
+
+// Dubbo request response related consts
+var (
+	DubboRequestHeaderBytesTwoWay = [HEADER_LENGTH]byte{MAGIC_HIGH, MAGIC_LOW, FLAG_REQUEST | FLAG_TWOWAY}
+	DubboRequestHeaderBytes       = [HEADER_LENGTH]byte{MAGIC_HIGH, MAGIC_LOW, FLAG_REQUEST}
+	DubboResponseHeaderBytes      = [HEADER_LENGTH]byte{MAGIC_HIGH, MAGIC_LOW, Zero, Response_OK}
+	DubboRequestHeartbeatHeader   = [HEADER_LENGTH]byte{MAGIC_HIGH, MAGIC_LOW, FLAG_REQUEST | FLAG_TWOWAY | FLAG_EVENT}
+	DubboResponseHeartbeatHeader  = [HEADER_LENGTH]byte{MAGIC_HIGH, MAGIC_LOW, FLAG_EVENT}
+)
+
+// Error part
+var (
+	ErrHeaderNotEnough = perrors.New("header buffer too short")
+	ErrBodyNotEnough   = perrors.New("body buffer too short")
+	ErrJavaException   = perrors.New("got java exception")
+	ErrIllegalPackage  = perrors.New("illegal package!")
+)
+
+// nolint
+var DescRegex, _ = regexp.Compile(DESC_REGEX)
+
+var NilValue = reflect.Zero(reflect.TypeOf((*interface{})(nil)).Elem())
diff --git a/protocol/dubbo/hessian2/hessian_dubbo.go b/protocol/dubbo/hessian2/hessian_dubbo.go
new file mode 100644
index 0000000000000000000000000000000000000000..1afa4ec96eccbb8077852dfcc020e0eb05be3257
--- /dev/null
+++ b/protocol/dubbo/hessian2/hessian_dubbo.go
@@ -0,0 +1,251 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package hessian2
+
+import (
+	"bufio"
+	"encoding/binary"
+	"time"
+)
+
+import (
+	"github.com/apache/dubbo-go-hessian2"
+	perrors "github.com/pkg/errors"
+)
+
+// enum part
+const (
+	PackageError              = PackageType(0x01)
+	PackageRequest            = PackageType(0x02)
+	PackageResponse           = PackageType(0x04)
+	PackageHeartbeat          = PackageType(0x08)
+	PackageRequest_TwoWay     = PackageType(0x10)
+	PackageResponse_Exception = PackageType(0x20)
+	PackageType_BitSize       = 0x2f
+)
+
+// PackageType nolint
+type PackageType int
+
+// DubboHeader dubbo header
+type DubboHeader struct {
+	SerialID       byte
+	Type           PackageType
+	ID             int64
+	BodyLen        int
+	ResponseStatus byte
+}
+
+// Service defines service instance
+type Service struct {
+	Path      string
+	Interface string
+	Group     string
+	Version   string
+	Method    string
+	Timeout   time.Duration // request timeout
+}
+
+// HessianCodec defines hessian codec
+type HessianCodec struct {
+	pkgType PackageType
+	reader  *bufio.Reader
+	bodyLen int
+}
+
+// NewHessianCodec generate a new hessian codec instance
+func NewHessianCodec(reader *bufio.Reader) *HessianCodec {
+	return &HessianCodec{
+		reader: reader,
+	}
+}
+
+// NewHessianCodec generate a new hessian codec instance
+func NewHessianCodecCustom(pkgType PackageType, reader *bufio.Reader, bodyLen int) *HessianCodec {
+	return &HessianCodec{
+		pkgType: pkgType,
+		reader:  reader,
+		bodyLen: bodyLen,
+	}
+}
+
+func (h *HessianCodec) Write(service Service, header DubboHeader, body interface{}) ([]byte, error) {
+	switch header.Type {
+	case PackageHeartbeat:
+		if header.ResponseStatus == Zero {
+			return packRequest(service, header, body)
+		}
+		return packResponse(header, body)
+
+	case PackageRequest, PackageRequest_TwoWay:
+		return packRequest(service, header, body)
+
+	case PackageResponse:
+		return packResponse(header, body)
+
+	default:
+		return nil, perrors.Errorf("Unrecognised message type: %v", header.Type)
+	}
+
+	// unreachable return nil, nil
+}
+
+// ReadHeader uses hessian codec to read dubbo header
+func (h *HessianCodec) ReadHeader(header *DubboHeader) error {
+
+	var err error
+
+	if h.reader.Size() < HEADER_LENGTH {
+		return ErrHeaderNotEnough
+	}
+	buf, err := h.reader.Peek(HEADER_LENGTH)
+	if err != nil { // this is impossible
+		return perrors.WithStack(err)
+	}
+	_, err = h.reader.Discard(HEADER_LENGTH)
+	if err != nil { // this is impossible
+		return perrors.WithStack(err)
+	}
+
+	//// read header
+
+	if buf[0] != MAGIC_HIGH && buf[1] != MAGIC_LOW {
+		return ErrIllegalPackage
+	}
+
+	// Header{serialization id(5 bit), event, two way, req/response}
+	if header.SerialID = buf[2] & SERIAL_MASK; header.SerialID == Zero {
+		return perrors.Errorf("serialization ID:%v", header.SerialID)
+	}
+
+	flag := buf[2] & FLAG_EVENT
+	if flag != Zero {
+		header.Type |= PackageHeartbeat
+	}
+	flag = buf[2] & FLAG_REQUEST
+	if flag != Zero {
+		header.Type |= PackageRequest
+		flag = buf[2] & FLAG_TWOWAY
+		if flag != Zero {
+			header.Type |= PackageRequest_TwoWay
+		}
+	} else {
+		header.Type |= PackageResponse
+		header.ResponseStatus = buf[3]
+		if header.ResponseStatus != Response_OK {
+			header.Type |= PackageResponse_Exception
+		}
+	}
+
+	// Header{req id}
+	header.ID = int64(binary.BigEndian.Uint64(buf[4:]))
+
+	// Header{body len}
+	header.BodyLen = int(binary.BigEndian.Uint32(buf[12:]))
+	if header.BodyLen < 0 {
+		return ErrIllegalPackage
+	}
+
+	h.pkgType = header.Type
+	h.bodyLen = header.BodyLen
+
+	if h.reader.Buffered() < h.bodyLen {
+		return ErrBodyNotEnough
+	}
+
+	return perrors.WithStack(err)
+
+}
+
+// ReadBody uses hessian codec to read response body
+func (h *HessianCodec) ReadBody(rspObj interface{}) error {
+
+	if h.reader.Buffered() < h.bodyLen {
+		return ErrBodyNotEnough
+	}
+	buf, err := h.reader.Peek(h.bodyLen)
+	if err != nil {
+		return perrors.WithStack(err)
+	}
+	_, err = h.reader.Discard(h.bodyLen)
+	if err != nil { // this is impossible
+		return perrors.WithStack(err)
+	}
+
+	switch h.pkgType & PackageType_BitSize {
+	case PackageResponse | PackageHeartbeat | PackageResponse_Exception, PackageResponse | PackageResponse_Exception:
+		decoder := hessian.NewDecoder(buf[:])
+		exception, err := decoder.Decode()
+		if err != nil {
+			return perrors.WithStack(err)
+		}
+		rsp, ok := rspObj.(*DubboResponse)
+		if !ok {
+			return perrors.Errorf("java exception:%s", exception.(string))
+		}
+		rsp.Exception = perrors.Errorf("java exception:%s", exception.(string))
+		return nil
+	case PackageRequest | PackageHeartbeat, PackageResponse | PackageHeartbeat:
+	case PackageRequest:
+		if rspObj != nil {
+			if err = unpackRequestBody(hessian.NewDecoder(buf[:]), rspObj); err != nil {
+				return perrors.WithStack(err)
+			}
+		}
+	case PackageResponse:
+		if rspObj != nil {
+			if err = unpackResponseBody(hessian.NewDecoder(buf[:]), rspObj); err != nil {
+				return perrors.WithStack(err)
+			}
+		}
+	}
+
+	return nil
+}
+
+// ignore body, but only read attachments
+func (h *HessianCodec) ReadAttachments() (map[string]interface{}, error) {
+	if h.reader.Buffered() < h.bodyLen {
+		return nil, ErrBodyNotEnough
+	}
+	buf, err := h.reader.Peek(h.bodyLen)
+	if err != nil {
+		return nil, perrors.WithStack(err)
+	}
+	_, err = h.reader.Discard(h.bodyLen)
+	if err != nil { // this is impossible
+		return nil, perrors.WithStack(err)
+	}
+
+	switch h.pkgType & PackageType_BitSize {
+	case PackageRequest:
+		rspObj := make([]interface{}, 7)
+		if err = unpackRequestBody(hessian.NewDecoderWithSkip(buf[:]), rspObj); err != nil {
+			return nil, perrors.WithStack(err)
+		}
+		return rspObj[6].(map[string]interface{}), nil
+	case PackageResponse:
+		rspObj := &DubboResponse{}
+		if err = unpackResponseBody(hessian.NewDecoderWithSkip(buf[:]), rspObj); err != nil {
+			return nil, perrors.WithStack(err)
+		}
+		return rspObj.Attachments, nil
+	}
+
+	return nil, nil
+}
diff --git a/protocol/dubbo/hessian2/hessian_dubbo_test.go b/protocol/dubbo/hessian2/hessian_dubbo_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..13dab92ba874fb5d746c5d57134592865cbfc7bd
--- /dev/null
+++ b/protocol/dubbo/hessian2/hessian_dubbo_test.go
@@ -0,0 +1,231 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package hessian2
+
+import (
+	"bufio"
+	"bytes"
+	"reflect"
+	"testing"
+	"time"
+)
+
+import (
+	hessian "github.com/apache/dubbo-go-hessian2"
+	"github.com/stretchr/testify/assert"
+)
+
+type Case struct {
+	A string
+	B int
+}
+
+type CaseA struct {
+	A string
+	B int
+	C Case
+}
+
+type CaseB struct {
+	A string
+	B CaseA
+}
+
+func (c *CaseB) JavaClassName() string {
+	return "com.test.caseb"
+}
+
+func (c CaseA) JavaClassName() string {
+	return "com.test.casea"
+}
+
+//JavaClassName  java fully qualified path
+func (c Case) JavaClassName() string {
+	return "com.test.case"
+}
+
+func doTestHessianEncodeHeader(t *testing.T, packageType PackageType, responseStatus byte, body interface{}) ([]byte, error) {
+	hessian.RegisterPOJO(&Case{})
+	codecW := NewHessianCodec(nil)
+	resp, err := codecW.Write(Service{
+		Path:      "test",
+		Interface: "ITest",
+		Version:   "v1.0",
+		Method:    "test",
+		Timeout:   time.Second * 10,
+	}, DubboHeader{
+		SerialID:       2,
+		Type:           packageType,
+		ID:             1,
+		ResponseStatus: responseStatus,
+	}, body)
+	assert.Nil(t, err)
+	return resp, err
+}
+
+func doTestResponse(t *testing.T, packageType PackageType, responseStatus byte, body interface{}, decodedResponse *DubboResponse, assertFunc func()) {
+	resp, err := doTestHessianEncodeHeader(t, packageType, responseStatus, body)
+
+	codecR := NewHessianCodec(bufio.NewReader(bytes.NewReader(resp)))
+
+	h := &DubboHeader{}
+	err = codecR.ReadHeader(h)
+	assert.Nil(t, err)
+
+	assert.Equal(t, byte(2), h.SerialID)
+	assert.Equal(t, packageType, h.Type&(PackageRequest|PackageResponse|PackageHeartbeat))
+	assert.Equal(t, int64(1), h.ID)
+	assert.Equal(t, responseStatus, h.ResponseStatus)
+
+	err = codecR.ReadBody(decodedResponse)
+	assert.Nil(t, err)
+	t.Log(decodedResponse)
+
+	if assertFunc != nil {
+		assertFunc()
+		return
+	}
+
+	if h.ResponseStatus != Zero && h.ResponseStatus != Response_OK {
+		assert.Equal(t, "java exception:"+body.(string), decodedResponse.Exception.Error())
+		return
+	}
+
+	in, _ := hessian.EnsureInterface(hessian.UnpackPtrValue(hessian.EnsurePackValue(body)), nil)
+	out, _ := hessian.EnsureInterface(hessian.UnpackPtrValue(hessian.EnsurePackValue(decodedResponse.RspObj)), nil)
+	assert.Equal(t, in, out)
+}
+
+func TestResponse(t *testing.T) {
+	caseObj := Case{A: "a", B: 1}
+	decodedResponse := &DubboResponse{}
+
+	arr := []*Case{&caseObj}
+	var arrRes []interface{}
+	decodedResponse.RspObj = &arrRes
+	doTestResponse(t, PackageResponse, Response_OK, arr, decodedResponse, func() {
+		assert.Equal(t, 1, len(arrRes))
+		assert.Equal(t, &caseObj, arrRes[0])
+	})
+
+	decodedResponse.RspObj = &Case{}
+	doTestResponse(t, PackageResponse, Response_OK, &Case{A: "a", B: 1}, decodedResponse, nil)
+
+	s := "ok!!!!!"
+	strObj := ""
+	decodedResponse.RspObj = &strObj
+	doTestResponse(t, PackageResponse, Response_OK, s, decodedResponse, nil)
+
+	var intObj int64
+	decodedResponse.RspObj = &intObj
+	doTestResponse(t, PackageResponse, Response_OK, int64(3), decodedResponse, nil)
+
+	boolObj := false
+	decodedResponse.RspObj = &boolObj
+	doTestResponse(t, PackageResponse, Response_OK, true, decodedResponse, nil)
+
+	strObj = ""
+	decodedResponse.RspObj = &strObj
+	doTestResponse(t, PackageResponse, hessian.Response_SERVER_ERROR, "error!!!!!", decodedResponse, nil)
+
+	mapObj := map[string][]*Case{"key": {&caseObj}}
+	mapRes := map[interface{}]interface{}{}
+	decodedResponse.RspObj = &mapRes
+	doTestResponse(t, PackageResponse, Response_OK, mapObj, decodedResponse, func() {
+		c, ok := mapRes["key"]
+		if !ok {
+			assert.FailNow(t, "no key in decoded response map")
+		}
+
+		mapValueArr, ok := c.([]*Case)
+		if !ok {
+			assert.FailNow(t, "invalid decoded response map value", "expect []*Case, but get %v", reflect.TypeOf(c))
+		}
+		assert.Equal(t, 1, len(mapValueArr))
+		assert.Equal(t, &caseObj, mapValueArr[0])
+	})
+}
+
+func doTestRequest(t *testing.T, packageType PackageType, responseStatus byte, body interface{}) {
+	resp, err := doTestHessianEncodeHeader(t, packageType, responseStatus, body)
+
+	codecR := NewHessianCodec(bufio.NewReader(bytes.NewReader(resp)))
+
+	h := &DubboHeader{}
+	err = codecR.ReadHeader(h)
+	assert.Nil(t, err)
+	assert.Equal(t, byte(2), h.SerialID)
+	assert.Equal(t, packageType, h.Type&(PackageRequest|PackageResponse|PackageHeartbeat))
+	assert.Equal(t, int64(1), h.ID)
+	assert.Equal(t, responseStatus, h.ResponseStatus)
+
+	c := make([]interface{}, 7)
+	err = codecR.ReadBody(c)
+	assert.Nil(t, err)
+	t.Log(c)
+	assert.True(t, len(body.([]interface{})) == len(c[5].([]interface{})))
+}
+
+func TestRequest(t *testing.T) {
+	doTestRequest(t, PackageRequest, Zero, []interface{}{"a"})
+	doTestRequest(t, PackageRequest, Zero, []interface{}{"a", 3})
+	doTestRequest(t, PackageRequest, Zero, []interface{}{"a", true})
+	doTestRequest(t, PackageRequest, Zero, []interface{}{"a", 3, true})
+	doTestRequest(t, PackageRequest, Zero, []interface{}{3.2, true})
+	doTestRequest(t, PackageRequest, Zero, []interface{}{"a", 3, true, &Case{A: "a", B: 3}})
+	doTestRequest(t, PackageRequest, Zero, []interface{}{"a", 3, true, []*Case{{A: "a", B: 3}}})
+	doTestRequest(t, PackageRequest, Zero, []interface{}{map[string][]*Case{"key": {{A: "a", B: 3}}}})
+}
+
+func TestHessianCodec_ReadAttachments(t *testing.T) {
+	hessian.RegisterPOJO(&AttachTestObject{})
+	body := &DubboResponse{
+		RspObj:      &CaseB{A: "A", B: CaseA{A: "a", B: 1, C: Case{A: "c", B: 2}}},
+		Exception:   nil,
+		Attachments: map[string]interface{}{DUBBO_VERSION_KEY: "2.6.4", "att": AttachTestObject{Id: 23, Name: "haha"}},
+	}
+	resp, err := doTestHessianEncodeHeader(t, PackageResponse, Response_OK, body)
+	assert.NoError(t, err)
+	hessian.UnRegisterPOJOs(&CaseB{}, &CaseA{})
+	codecR1 := NewHessianCodec(bufio.NewReader(bytes.NewReader(resp)))
+	codecR2 := NewHessianCodec(bufio.NewReader(bytes.NewReader(resp)))
+	h := &DubboHeader{}
+	assert.NoError(t, codecR1.ReadHeader(h))
+	t.Log(h)
+	assert.NoError(t, codecR2.ReadHeader(h))
+	t.Log(h)
+
+	err = codecR1.ReadBody(body)
+	assert.Equal(t, "can not find go type name com.test.caseb in registry", err.Error())
+	attrs, err := codecR2.ReadAttachments()
+	assert.NoError(t, err)
+	assert.Equal(t, "2.6.4", attrs[DUBBO_VERSION_KEY])
+	assert.Equal(t, AttachTestObject{Id: 23, Name: "haha"}, *(attrs["att"].(*AttachTestObject)))
+	assert.NotEqual(t, AttachTestObject{Id: 24, Name: "nohaha"}, *(attrs["att"].(*AttachTestObject)))
+
+	t.Log(attrs)
+}
+
+type AttachTestObject struct {
+	Id   int32
+	Name string `dubbo:"name"`
+}
+
+func (AttachTestObject) JavaClassName() string {
+	return "com.test.Test"
+}
diff --git a/protocol/dubbo/hessian2/hessian_request.go b/protocol/dubbo/hessian2/hessian_request.go
new file mode 100644
index 0000000000000000000000000000000000000000..4ebb4aa1be05d4d1941661fed452dda06cf55fa0
--- /dev/null
+++ b/protocol/dubbo/hessian2/hessian_request.go
@@ -0,0 +1,350 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package hessian2
+
+import (
+	"encoding/binary"
+	"reflect"
+	"strconv"
+	"strings"
+	"time"
+)
+
+import (
+	hessian "github.com/apache/dubbo-go-hessian2"
+	perrors "github.com/pkg/errors"
+)
+
+/////////////////////////////////////////
+// dubbo
+/////////////////////////////////////////
+
+func getArgType(v interface{}) string {
+	if v == nil {
+		return "V"
+	}
+
+	switch v.(type) {
+	// Serialized tags for base types
+	case nil:
+		return "V"
+	case bool:
+		return "Z"
+	case []bool:
+		return "[Z"
+	case byte:
+		return "B"
+	case []byte:
+		return "[B"
+	case int8:
+		return "B"
+	case []int8:
+		return "[B"
+	case int16:
+		return "S"
+	case []int16:
+		return "[S"
+	case uint16: // Equivalent to Char of Java
+		return "C"
+	case []uint16:
+		return "[C"
+	// case rune:
+	//	return "C"
+	case int:
+		return "J"
+	case []int:
+		return "[J"
+	case int32:
+		return "I"
+	case []int32:
+		return "[I"
+	case int64:
+		return "J"
+	case []int64:
+		return "[J"
+	case time.Time:
+		return "java.util.Date"
+	case []time.Time:
+		return "[Ljava.util.Date"
+	case float32:
+		return "F"
+	case []float32:
+		return "[F"
+	case float64:
+		return "D"
+	case []float64:
+		return "[D"
+	case string:
+		return "java.lang.String"
+	case []string:
+		return "[Ljava.lang.String;"
+	case []hessian.Object:
+		return "[Ljava.lang.Object;"
+	case map[interface{}]interface{}:
+		// return  "java.util.HashMap"
+		return "java.util.Map"
+	case hessian.POJOEnum:
+		return v.(hessian.POJOEnum).JavaClassName()
+	//  Serialized tags for complex types
+	default:
+		t := reflect.TypeOf(v)
+		if reflect.Ptr == t.Kind() {
+			t = reflect.TypeOf(reflect.ValueOf(v).Elem())
+		}
+		switch t.Kind() {
+		case reflect.Struct:
+			return "java.lang.Object"
+		case reflect.Slice, reflect.Array:
+			if t.Elem().Kind() == reflect.Struct {
+				return "[Ljava.lang.Object;"
+			}
+			// return "java.util.ArrayList"
+			return "java.util.List"
+		case reflect.Map: // Enter here, map may be map[string]int
+			return "java.util.Map"
+		default:
+			return ""
+		}
+	}
+
+	// unreachable
+	// return "java.lang.RuntimeException"
+}
+
+func getArgsTypeList(args []interface{}) (string, error) {
+	var (
+		typ   string
+		types string
+	)
+
+	for i := range args {
+		typ = getArgType(args[i])
+		if typ == "" {
+			return types, perrors.Errorf("cat not get arg %#v type", args[i])
+		}
+		if !strings.Contains(typ, ".") {
+			types += typ
+		} else if strings.Index(typ, "[") == 0 {
+			types += strings.Replace(typ, ".", "/", -1)
+		} else {
+			// java.util.List -> Ljava/util/List;
+			types += "L" + strings.Replace(typ, ".", "/", -1) + ";"
+		}
+	}
+
+	return types, nil
+}
+
+type DubboRequest struct {
+	Params      interface{}
+	Attachments map[string]interface{}
+}
+
+// NewRequest create a new DubboRequest
+func NewRequest(params interface{}, atta map[string]interface{}) *DubboRequest {
+	if atta == nil {
+		atta = make(map[string]interface{})
+	}
+	return &DubboRequest{
+		Params:      params,
+		Attachments: atta,
+	}
+}
+
+func EnsureRequest(body interface{}) *DubboRequest {
+	if req, ok := body.(*DubboRequest); ok {
+		return req
+	}
+	return NewRequest(body, nil)
+}
+
+func packRequest(service Service, header DubboHeader, req interface{}) ([]byte, error) {
+	var (
+		err       error
+		types     string
+		byteArray []byte
+		pkgLen    int
+	)
+
+	request := EnsureRequest(req)
+
+	args, ok := request.Params.([]interface{})
+	if !ok {
+		return nil, perrors.Errorf("@params is not of type: []interface{}")
+	}
+
+	hb := header.Type == PackageHeartbeat
+
+	//////////////////////////////////////////
+	// byteArray
+	//////////////////////////////////////////
+	// magic
+	switch header.Type {
+	case PackageHeartbeat:
+		byteArray = append(byteArray, DubboRequestHeartbeatHeader[:]...)
+	case PackageRequest_TwoWay:
+		byteArray = append(byteArray, DubboRequestHeaderBytesTwoWay[:]...)
+	default:
+		byteArray = append(byteArray, DubboRequestHeaderBytes[:]...)
+	}
+
+	// serialization id, two way flag, event, request/response flag
+	// SerialID is id of serialization approach in java dubbo
+	byteArray[2] |= header.SerialID & SERIAL_MASK
+	// request id
+	binary.BigEndian.PutUint64(byteArray[4:], uint64(header.ID))
+
+	encoder := hessian.NewEncoder()
+	encoder.Append(byteArray[:HEADER_LENGTH])
+
+	//////////////////////////////////////////
+	// body
+	//////////////////////////////////////////
+	if hb {
+		encoder.Encode(nil)
+		goto END
+	}
+
+	// dubbo version + path + version + method
+	encoder.Encode(DEFAULT_DUBBO_PROTOCOL_VERSION)
+	encoder.Encode(service.Path)
+	encoder.Encode(service.Version)
+	encoder.Encode(service.Method)
+
+	// args = args type list + args value list
+	if types, err = getArgsTypeList(args); err != nil {
+		return nil, perrors.Wrapf(err, " PackRequest(args:%+v)", args)
+	}
+	encoder.Encode(types)
+	for _, v := range args {
+		encoder.Encode(v)
+	}
+
+	request.Attachments[PATH_KEY] = service.Path
+	request.Attachments[VERSION_KEY] = service.Version
+	if len(service.Group) > 0 {
+		request.Attachments[GROUP_KEY] = service.Group
+	}
+	if len(service.Interface) > 0 {
+		request.Attachments[INTERFACE_KEY] = service.Interface
+	}
+	if service.Timeout != 0 {
+		request.Attachments[TIMEOUT_KEY] = strconv.Itoa(int(service.Timeout / time.Millisecond))
+	}
+
+	encoder.Encode(request.Attachments)
+
+END:
+	byteArray = encoder.Buffer()
+	pkgLen = len(byteArray)
+	if pkgLen > int(DEFAULT_LEN) { // 8M
+		return nil, perrors.Errorf("Data length %d too large, max payload %d", pkgLen, DEFAULT_LEN)
+	}
+	// byteArray{body length}
+	binary.BigEndian.PutUint32(byteArray[12:], uint32(pkgLen-HEADER_LENGTH))
+	return byteArray, nil
+}
+
+// hessian decode request body
+func unpackRequestBody(decoder *hessian.Decoder, reqObj interface{}) error {
+
+	if decoder == nil {
+		return perrors.Errorf("@decoder is nil")
+	}
+
+	req, ok := reqObj.([]interface{})
+	if !ok {
+		return perrors.Errorf("@reqObj is not of type: []interface{}")
+	}
+	if len(req) < 7 {
+		return perrors.New("length of @reqObj should  be 7")
+	}
+
+	var (
+		err                                                     error
+		dubboVersion, target, serviceVersion, method, argsTypes interface{}
+		args                                                    []interface{}
+	)
+
+	dubboVersion, err = decoder.Decode()
+	if err != nil {
+		return perrors.WithStack(err)
+	}
+	req[0] = dubboVersion
+
+	target, err = decoder.Decode()
+	if err != nil {
+		return perrors.WithStack(err)
+	}
+	req[1] = target
+
+	serviceVersion, err = decoder.Decode()
+	if err != nil {
+		return perrors.WithStack(err)
+	}
+	req[2] = serviceVersion
+
+	method, err = decoder.Decode()
+	if err != nil {
+		return perrors.WithStack(err)
+	}
+	req[3] = method
+
+	argsTypes, err = decoder.Decode()
+	if err != nil {
+		return perrors.WithStack(err)
+	}
+	req[4] = argsTypes
+
+	ats := DescRegex.FindAllString(argsTypes.(string), -1)
+	var arg interface{}
+	for i := 0; i < len(ats); i++ {
+		arg, err = decoder.Decode()
+		if err != nil {
+			return perrors.WithStack(err)
+		}
+		args = append(args, arg)
+	}
+	req[5] = args
+
+	attachments, err := decoder.Decode()
+	if err != nil {
+		return perrors.WithStack(err)
+	}
+	if v, ok := attachments.(map[interface{}]interface{}); ok {
+		v[DUBBO_VERSION_KEY] = dubboVersion
+		req[6] = ToMapStringInterface(v)
+		return nil
+	}
+
+	return perrors.Errorf("get wrong attachments: %+v", attachments)
+}
+
+func ToMapStringInterface(origin map[interface{}]interface{}) map[string]interface{} {
+	dest := make(map[string]interface{}, len(origin))
+	for k, v := range origin {
+		if kv, ok := k.(string); ok {
+			if v == nil {
+				dest[kv] = ""
+				continue
+			}
+			dest[kv] = v
+		}
+	}
+	return dest
+}
diff --git a/protocol/dubbo/hessian2/hessian_request_test.go b/protocol/dubbo/hessian2/hessian_request_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..98d5f2399c9fdbf99a7274e0c3e53c2c175f774f
--- /dev/null
+++ b/protocol/dubbo/hessian2/hessian_request_test.go
@@ -0,0 +1,158 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package hessian2
+
+import (
+	"reflect"
+	"strconv"
+	"testing"
+	"time"
+)
+
+import (
+	hessian "github.com/apache/dubbo-go-hessian2"
+	"github.com/stretchr/testify/assert"
+)
+
+type TestEnumGender hessian.JavaEnum
+
+const (
+	MAN hessian.JavaEnum = iota
+	WOMAN
+)
+
+var genderName = map[hessian.JavaEnum]string{
+	MAN:   "MAN",
+	WOMAN: "WOMAN",
+}
+
+var genderValue = map[string]hessian.JavaEnum{
+	"MAN":   MAN,
+	"WOMAN": WOMAN,
+}
+
+func (g TestEnumGender) JavaClassName() string {
+	return "com.ikurento.test.TestEnumGender"
+}
+
+func (g TestEnumGender) String() string {
+	s, ok := genderName[hessian.JavaEnum(g)]
+	if ok {
+		return s
+	}
+
+	return strconv.Itoa(int(g))
+}
+
+func (g TestEnumGender) EnumValue(s string) hessian.JavaEnum {
+	v, ok := genderValue[s]
+	if ok {
+		return v
+	}
+
+	return hessian.InvalidJavaEnum
+}
+
+func TestPackRequest(t *testing.T) {
+	bytes, err := packRequest(Service{
+		Path:      "test",
+		Interface: "ITest",
+		Version:   "v1.0",
+		Method:    "test",
+		Timeout:   time.Second * 10,
+	}, DubboHeader{
+		SerialID: 0,
+		Type:     PackageRequest,
+		ID:       123,
+	}, []interface{}{1, 2})
+
+	assert.Nil(t, err)
+
+	if bytes != nil {
+		t.Logf("pack request: %s", string(bytes))
+	}
+}
+
+func TestGetArgsTypeList(t *testing.T) {
+	type Test struct{}
+	str, err := getArgsTypeList([]interface{}{nil, 1, []int{2}, true, []bool{false}, "a", []string{"b"}, Test{}, &Test{}, []Test{}, map[string]Test{}, TestEnumGender(MAN)})
+	assert.NoError(t, err)
+	assert.Equal(t, "VJ[JZ[ZLjava/lang/String;[Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;[Ljava/lang/Object;Ljava/util/Map;Lcom/ikurento/test/TestEnumGender;", str)
+}
+
+func TestDescRegex(t *testing.T) {
+	results := DescRegex.FindAllString("Ljava/lang/String;", -1)
+	assert.Equal(t, 1, len(results))
+	assert.Equal(t, "Ljava/lang/String;", results[0])
+
+	results = DescRegex.FindAllString("Ljava/lang/String;I", -1)
+	assert.Equal(t, 2, len(results))
+	assert.Equal(t, "Ljava/lang/String;", results[0])
+	assert.Equal(t, "I", results[1])
+
+	results = DescRegex.FindAllString("ILjava/lang/String;", -1)
+	assert.Equal(t, 2, len(results))
+	assert.Equal(t, "I", results[0])
+	assert.Equal(t, "Ljava/lang/String;", results[1])
+
+	results = DescRegex.FindAllString("ILjava/lang/String;IZ", -1)
+	assert.Equal(t, 4, len(results))
+	assert.Equal(t, "I", results[0])
+	assert.Equal(t, "Ljava/lang/String;", results[1])
+	assert.Equal(t, "I", results[2])
+	assert.Equal(t, "Z", results[3])
+
+	results = DescRegex.FindAllString("[Ljava/lang/String;[I", -1)
+	assert.Equal(t, 2, len(results))
+	assert.Equal(t, "[Ljava/lang/String;", results[0])
+	assert.Equal(t, "[I", results[1])
+}
+
+func TestIssue192(t *testing.T) {
+	type args struct {
+		origin map[interface{}]interface{}
+	}
+	tests := []struct {
+		name string
+		args args
+		want map[string]interface{}
+	}{
+		{
+			name: "not null",
+			args: args{
+				origin: map[interface{}]interface{}{
+					"1": nil,
+					"2": "3",
+					"":  "",
+				},
+			},
+			want: map[string]interface{}{
+				"1": "",
+				"2": "3",
+				"":  "",
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if got := ToMapStringInterface(tt.args.origin); !reflect.DeepEqual(got, tt.want) {
+				t.Errorf("ToMapStringString() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
diff --git a/protocol/dubbo/hessian2/hessian_response.go b/protocol/dubbo/hessian2/hessian_response.go
new file mode 100644
index 0000000000000000000000000000000000000000..982960ed87e74b325687ac364c97a347efe6c38f
--- /dev/null
+++ b/protocol/dubbo/hessian2/hessian_response.go
@@ -0,0 +1,377 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package hessian2
+
+import (
+	"encoding/binary"
+	"math"
+	"reflect"
+	"strconv"
+	"strings"
+)
+
+import (
+	hessian "github.com/apache/dubbo-go-hessian2"
+	"github.com/apache/dubbo-go-hessian2/java_exception"
+	perrors "github.com/pkg/errors"
+)
+
+// DubboResponse dubbo response
+type DubboResponse struct {
+	RspObj      interface{}
+	Exception   error
+	Attachments map[string]interface{}
+}
+
+// NewResponse create a new DubboResponse
+func NewResponse(rspObj interface{}, exception error, attachments map[string]interface{}) *DubboResponse {
+	if attachments == nil {
+		attachments = make(map[string]interface{}, 8)
+	}
+	return &DubboResponse{
+		RspObj:      rspObj,
+		Exception:   exception,
+		Attachments: attachments,
+	}
+}
+
+// EnsureResponse check body type, make sure it's a DubboResponse or package it as a DubboResponse
+func EnsureResponse(body interface{}) *DubboResponse {
+	if res, ok := body.(*DubboResponse); ok {
+		return res
+	}
+	if exp, ok := body.(error); ok {
+		return NewResponse(nil, exp, nil)
+	}
+	return NewResponse(body, nil, nil)
+}
+
+// https://github.com/apache/dubbo/blob/dubbo-2.7.1/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/codec/ExchangeCodec.java#L256
+// hessian encode response
+func packResponse(header DubboHeader, ret interface{}) ([]byte, error) {
+	var (
+		byteArray []byte
+	)
+
+	response := EnsureResponse(ret)
+
+	hb := header.Type == PackageHeartbeat
+
+	// magic
+	if hb {
+		byteArray = append(byteArray, DubboResponseHeartbeatHeader[:]...)
+	} else {
+		byteArray = append(byteArray, DubboResponseHeaderBytes[:]...)
+	}
+	// set serialID, identify serialization types, eg: fastjson->6, hessian2->2
+	byteArray[2] |= header.SerialID & SERIAL_MASK
+	// response status
+	if header.ResponseStatus != 0 {
+		byteArray[3] = header.ResponseStatus
+	}
+
+	// request id
+	binary.BigEndian.PutUint64(byteArray[4:], uint64(header.ID))
+
+	// body
+	encoder := hessian.NewEncoder()
+	encoder.Append(byteArray[:HEADER_LENGTH])
+
+	if header.ResponseStatus == Response_OK {
+		if hb {
+			encoder.Encode(nil)
+		} else {
+			atta := isSupportResponseAttachment(response.Attachments[DUBBO_VERSION_KEY])
+
+			var resWithException, resValue, resNullValue int32
+			if atta {
+				resWithException = RESPONSE_WITH_EXCEPTION_WITH_ATTACHMENTS
+				resValue = RESPONSE_VALUE_WITH_ATTACHMENTS
+				resNullValue = RESPONSE_NULL_VALUE_WITH_ATTACHMENTS
+			} else {
+				resWithException = RESPONSE_WITH_EXCEPTION
+				resValue = RESPONSE_VALUE
+				resNullValue = RESPONSE_NULL_VALUE
+			}
+
+			if response.Exception != nil { // throw error
+				encoder.Encode(resWithException)
+				if t, ok := response.Exception.(java_exception.Throwabler); ok {
+					encoder.Encode(t)
+				} else {
+					encoder.Encode(java_exception.NewThrowable(response.Exception.Error()))
+				}
+			} else {
+				if response.RspObj == nil {
+					encoder.Encode(resNullValue)
+				} else {
+					encoder.Encode(resValue)
+					encoder.Encode(response.RspObj) // result
+				}
+			}
+
+			if atta {
+				encoder.Encode(response.Attachments) // attachments
+			}
+		}
+	} else {
+		if response.Exception != nil { // throw error
+			encoder.Encode(response.Exception.Error())
+		} else {
+			encoder.Encode(response.RspObj)
+		}
+	}
+
+	byteArray = encoder.Buffer()
+	byteArray = hessian.EncNull(byteArray) // if not, "java client" will throw exception  "unexpected end of file"
+	pkgLen := len(byteArray)
+	if pkgLen > int(DEFAULT_LEN) { // 8M
+		return nil, perrors.Errorf("Data length %d too large, max payload %d", pkgLen, DEFAULT_LEN)
+	}
+	// byteArray{body length}
+	binary.BigEndian.PutUint32(byteArray[12:], uint32(pkgLen-HEADER_LENGTH))
+	return byteArray, nil
+
+}
+
+// hessian decode response body
+func unpackResponseBody(decoder *hessian.Decoder, resp interface{}) error {
+	// body
+	if decoder == nil {
+		return perrors.Errorf("@decoder is nil")
+	}
+	rspType, err := decoder.Decode()
+	if err != nil {
+		return perrors.WithStack(err)
+	}
+
+	response := EnsureResponse(resp)
+
+	switch rspType {
+	case RESPONSE_WITH_EXCEPTION, RESPONSE_WITH_EXCEPTION_WITH_ATTACHMENTS:
+		expt, err := decoder.Decode()
+		if err != nil {
+			return perrors.WithStack(err)
+		}
+		if rspType == RESPONSE_WITH_EXCEPTION_WITH_ATTACHMENTS {
+			attachments, err := decoder.Decode()
+			if err != nil {
+				return perrors.WithStack(err)
+			}
+			if v, ok := attachments.(map[interface{}]interface{}); ok {
+				atta := ToMapStringInterface(v)
+				response.Attachments = atta
+			} else {
+				return perrors.Errorf("get wrong attachments: %+v", attachments)
+			}
+		}
+
+		if e, ok := expt.(error); ok {
+			response.Exception = e
+		} else {
+			response.Exception = perrors.Errorf("got exception: %+v", expt)
+		}
+		return nil
+
+	case RESPONSE_VALUE, RESPONSE_VALUE_WITH_ATTACHMENTS:
+		rsp, err := decoder.Decode()
+		if err != nil {
+			return perrors.WithStack(err)
+		}
+		if rspType == RESPONSE_VALUE_WITH_ATTACHMENTS {
+			attachments, err := decoder.Decode()
+			if err != nil {
+				return perrors.WithStack(err)
+			}
+			if v, ok := attachments.(map[interface{}]interface{}); ok {
+				response.Attachments = ToMapStringInterface(v)
+			} else {
+				return perrors.Errorf("get wrong attachments: %+v", attachments)
+			}
+		}
+
+		// If the return value is nil,
+		// we should consider it normal
+		if rsp == nil {
+			return nil
+		}
+
+		return perrors.WithStack(ReflectResponse(rsp, response.RspObj))
+
+	case RESPONSE_NULL_VALUE, RESPONSE_NULL_VALUE_WITH_ATTACHMENTS:
+		if rspType == RESPONSE_NULL_VALUE_WITH_ATTACHMENTS {
+			attachments, err := decoder.Decode()
+			if err != nil {
+				return perrors.WithStack(err)
+			}
+			if v, ok := attachments.(map[interface{}]interface{}); ok {
+				atta := ToMapStringInterface(v)
+				response.Attachments = atta
+			} else {
+				return perrors.Errorf("get wrong attachments: %+v", attachments)
+			}
+		}
+		return nil
+	}
+
+	return nil
+}
+
+// CopySlice copy from inSlice to outSlice
+func CopySlice(inSlice, outSlice reflect.Value) error {
+	if inSlice.IsNil() {
+		return perrors.New("@in is nil")
+	}
+	if inSlice.Kind() != reflect.Slice {
+		return perrors.Errorf("@in is not slice, but %v", inSlice.Kind())
+	}
+
+	for outSlice.Kind() == reflect.Ptr {
+		outSlice = outSlice.Elem()
+	}
+
+	size := inSlice.Len()
+	outSlice.Set(reflect.MakeSlice(outSlice.Type(), size, size))
+
+	for i := 0; i < size; i++ {
+		inSliceValue := inSlice.Index(i)
+		if !inSliceValue.Type().AssignableTo(outSlice.Index(i).Type()) {
+			return perrors.Errorf("in element type [%s] can not assign to out element type [%s]",
+				inSliceValue.Type().String(), outSlice.Type().String())
+		}
+		outSlice.Index(i).Set(inSliceValue)
+	}
+
+	return nil
+}
+
+// CopyMap copy from in map to out map
+func CopyMap(inMapValue, outMapValue reflect.Value) error {
+	if inMapValue.IsNil() {
+		return perrors.New("@in is nil")
+	}
+	if !inMapValue.CanInterface() {
+		return perrors.New("@in's Interface can not be used.")
+	}
+	if inMapValue.Kind() != reflect.Map {
+		return perrors.Errorf("@in is not map, but %v", inMapValue.Kind())
+	}
+
+	outMapType := hessian.UnpackPtrType(outMapValue.Type())
+	hessian.SetValue(outMapValue, reflect.MakeMap(outMapType))
+
+	outKeyType := outMapType.Key()
+
+	outMapValue = hessian.UnpackPtrValue(outMapValue)
+	outValueType := outMapValue.Type().Elem()
+
+	for _, inKey := range inMapValue.MapKeys() {
+		inValue := inMapValue.MapIndex(inKey)
+
+		if !inKey.Type().AssignableTo(outKeyType) {
+			return perrors.Errorf("in Key:{type:%s, value:%#v} can not assign to out Key:{type:%s} ",
+				inKey.Type().String(), inKey, outKeyType.String())
+		}
+		if !inValue.Type().AssignableTo(outValueType) {
+			return perrors.Errorf("in Value:{type:%s, value:%#v} can not assign to out value:{type:%s}",
+				inValue.Type().String(), inValue, outValueType.String())
+		}
+		outMapValue.SetMapIndex(inKey, inValue)
+	}
+
+	return nil
+}
+
+// ReflectResponse reflect return value
+// TODO response object should not be copied again to another object, it should be the exact type of the object
+func ReflectResponse(in interface{}, out interface{}) error {
+	if in == nil {
+		return perrors.Errorf("@in is nil")
+	}
+
+	if out == nil {
+		return perrors.Errorf("@out is nil")
+	}
+	if reflect.TypeOf(out).Kind() != reflect.Ptr {
+		return perrors.Errorf("@out should be a pointer")
+	}
+
+	inValue := hessian.EnsurePackValue(in)
+	outValue := hessian.EnsurePackValue(out)
+
+	outType := outValue.Type().String()
+	if outType == "interface {}" || outType == "*interface {}" {
+		hessian.SetValue(outValue, inValue)
+		return nil
+	}
+
+	switch inValue.Type().Kind() {
+	case reflect.Slice, reflect.Array:
+		return CopySlice(inValue, outValue)
+	case reflect.Map:
+		return CopyMap(inValue, outValue)
+	default:
+		hessian.SetValue(outValue, inValue)
+	}
+
+	return nil
+}
+
+var versionInt = make(map[string]int)
+
+// https://github.com/apache/dubbo/blob/dubbo-2.7.1/dubbo-common/src/main/java/org/apache/dubbo/common/Version.java#L96
+// isSupportResponseAttachment is for compatibility among some dubbo version
+func isSupportResponseAttachment(ver interface{}) bool {
+	version, ok := ver.(string)
+	if !ok || len(version) == 0 {
+		return false
+	}
+
+	v, ok := versionInt[version]
+	if !ok {
+		v = version2Int(version)
+		if v == -1 {
+			return false
+		}
+	}
+
+	if v >= 2001000 && v <= 2060200 { // 2.0.10 ~ 2.6.2
+		return false
+	}
+	return v >= LOWEST_VERSION_FOR_RESPONSE_ATTACHMENT
+}
+
+func version2Int(ver interface{}) int {
+	version, ok := ver.(string)
+	if !ok || len(version) == 0 {
+		return 0
+	}
+	var v = 0
+	varr := strings.Split(version, ".")
+	length := len(varr)
+	for key, value := range varr {
+		v0, err := strconv.Atoi(value)
+		if err != nil {
+			return -1
+		}
+		v += v0 * int(math.Pow10((length-key-1)*2))
+	}
+	if length == 3 {
+		return v * 100
+	}
+	return v
+}
diff --git a/protocol/dubbo/hessian2/hessian_response_test.go b/protocol/dubbo/hessian2/hessian_response_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..f5c84baa90b3b31b271979cb1107503facac71d9
--- /dev/null
+++ b/protocol/dubbo/hessian2/hessian_response_test.go
@@ -0,0 +1,225 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package hessian2
+
+import (
+	"reflect"
+	"testing"
+)
+
+import (
+	hessian "github.com/apache/dubbo-go-hessian2"
+	"github.com/stretchr/testify/assert"
+)
+
+func doTestReflectResponse(t *testing.T, in interface{}, out interface{}) {
+	err := ReflectResponse(in, out)
+	if err != nil {
+		t.Error(err)
+		t.FailNow()
+	}
+
+	result := hessian.UnpackPtrValue(reflect.ValueOf(out)).Interface()
+
+	equal := reflect.DeepEqual(in, result)
+	if !equal {
+		t.Errorf("expect [%v]: %v, but got [%v]: %v", reflect.TypeOf(in), in, reflect.TypeOf(result), result)
+	}
+}
+
+func TestReflectResponse(t *testing.T) {
+	var b bool
+	doTestReflectResponse(t, true, &b)
+	doTestReflectResponse(t, false, &b)
+
+	var i int
+	doTestReflectResponse(t, 123, &i)
+	doTestReflectResponse(t, 234, &i)
+
+	var i16 int16
+	doTestReflectResponse(t, int16(456), &i16)
+
+	var i64 int64
+	doTestReflectResponse(t, int64(789), &i64)
+
+	var s string
+	doTestReflectResponse(t, "hello world", &s)
+
+	type rr struct {
+		Name string
+		Num  int
+	}
+
+	var r1 rr
+	doTestReflectResponse(t, rr{"dubbogo", 32}, &r1)
+
+	// ------ map test -------
+	m1 := make(map[interface{}]interface{})
+	var m1r map[interface{}]interface{}
+	m1["hello"] = "world"
+	m1[1] = "go"
+	m1["dubbo"] = 666
+	doTestReflectResponse(t, m1, &m1r)
+
+	m2 := make(map[string]string)
+	var m2r map[string]string
+	m2["hello"] = "world"
+	m2["dubbo"] = "666"
+	doTestReflectResponse(t, m2, &m2r)
+
+	m3 := make(map[string]rr)
+	var m3r map[string]rr
+	m3["dubbo"] = rr{"hello", 123}
+	m3["go"] = rr{"world", 456}
+	doTestReflectResponse(t, m3, &m3r)
+
+	// ------ slice test -------
+	s1 := []string{"abc", "def", "hello", "world"}
+	var s1r []string
+	doTestReflectResponse(t, s1, &s1r)
+
+	s2 := []rr{rr{"dubbo", 666}, rr{"go", 999}}
+	var s2r []rr
+	doTestReflectResponse(t, s2, &s2r)
+
+	s3 := []interface{}{rr{"dubbo", 666}, 123, "hello"}
+	var s3r []interface{}
+	doTestReflectResponse(t, s3, &s3r)
+
+	// ------ interface test -------
+	in1 := []interface{}{rr{"dubbo", 666}, 123, "hello"}
+	var inr1 *interface{}
+	doTestReflectResponse(t, in1, reflect.New(reflect.TypeOf(inr1).Elem()).Interface())
+
+	in2 := make(map[string]rr)
+	var inr2 map[string]rr
+	m3["dubbo"] = rr{"hello", 123}
+	m3["go"] = rr{"world", 456}
+	doTestReflectResponse(t, in2, &inr2)
+}
+
+// separately test copy normal map to map[interface{}]interface{}
+func TestCopyMap(t *testing.T) {
+	type rr struct {
+		Name string
+		Num  int
+	}
+
+	m3 := make(map[string]rr)
+	var m3r map[interface{}]interface{}
+	r1 := rr{"hello", 123}
+	r2 := rr{"world", 456}
+	m3["dubbo"] = r1
+	m3["go"] = r2
+
+	err := ReflectResponse(m3, &m3r)
+	if err != nil {
+		t.Error(err)
+		t.FailNow()
+	}
+
+	assert.Equal(t, 2, len(m3r))
+
+	rr1, ok := m3r["dubbo"]
+	assert.True(t, ok)
+	assert.True(t, reflect.DeepEqual(r1, rr1))
+
+	rr2, ok := m3r["go"]
+	assert.True(t, ok)
+	assert.True(t, reflect.DeepEqual(r2, rr2))
+}
+
+// separately test copy normal slice to []interface{}
+func TestCopySlice(t *testing.T) {
+	type rr struct {
+		Name string
+		Num  int
+	}
+
+	r1 := rr{"hello", 123}
+	r2 := rr{"world", 456}
+
+	s1 := []rr{r1, r2}
+	var s1r []interface{}
+
+	err := ReflectResponse(s1, &s1r)
+	if err != nil {
+		t.Error(err)
+		t.FailNow()
+	}
+
+	assert.Equal(t, 2, len(s1r))
+	assert.True(t, reflect.DeepEqual(r1, s1r[0]))
+	assert.True(t, reflect.DeepEqual(r2, s1r[1]))
+}
+
+func TestIsSupportResponseAttachment(t *testing.T) {
+	is := isSupportResponseAttachment("2.X")
+	assert.False(t, is)
+
+	is = isSupportResponseAttachment("2.0.10")
+	assert.False(t, is)
+
+	is = isSupportResponseAttachment("2.5.3")
+	assert.False(t, is)
+
+	is = isSupportResponseAttachment("2.6.2")
+	assert.False(t, is)
+
+	is = isSupportResponseAttachment("1.5.5")
+	assert.False(t, is)
+
+	is = isSupportResponseAttachment("0.0.0")
+	assert.False(t, is)
+
+	is = isSupportResponseAttachment("2.0.2")
+	assert.True(t, is)
+
+	is = isSupportResponseAttachment("2.7.2")
+	assert.True(t, is)
+}
+
+func TestVersion2Int(t *testing.T) {
+	v := version2Int("2.1.3")
+	assert.Equal(t, 2010300, v)
+
+	v = version2Int("22.11.33")
+	assert.Equal(t, 22113300, v)
+
+	v = version2Int("222.111.333")
+	assert.Equal(t, 223143300, v)
+
+	v = version2Int("220.110.333")
+	assert.Equal(t, 221133300, v)
+
+	v = version2Int("229.119.333")
+	assert.Equal(t, 230223300, v)
+
+	v = version2Int("2222.1111.3333")
+	assert.Equal(t, 2233443300, v)
+
+	v = version2Int("2.11")
+	assert.Equal(t, 211, v)
+
+	v = version2Int("2.1.3.4")
+	assert.Equal(t, 2010304, v)
+
+	v = version2Int("2.1.3.4.5")
+	assert.Equal(t, 201030405, v)
+
+}
diff --git a/protocol/dubbo/impl/codec.go b/protocol/dubbo/impl/codec.go
new file mode 100644
index 0000000000000000000000000000000000000000..17e7b57b45f2334e06b757ee07e3a9ef034c920a
--- /dev/null
+++ b/protocol/dubbo/impl/codec.go
@@ -0,0 +1,288 @@
+/*
+ * 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 impl
+
+import (
+	"bufio"
+	"encoding/binary"
+)
+
+import (
+	hessian "github.com/apache/dubbo-go-hessian2"
+	perrors "github.com/pkg/errors"
+)
+
+import (
+	"github.com/apache/dubbo-go/common/constant"
+	"github.com/apache/dubbo-go/common/logger"
+	"github.com/apache/dubbo-go/remoting"
+)
+
+type ProtocolCodec struct {
+	reader     *bufio.Reader
+	pkgType    PackageType
+	bodyLen    int
+	serializer Serializer
+	headerRead bool
+}
+
+func (c *ProtocolCodec) ReadHeader(header *DubboHeader) error {
+	var err error
+	if c.reader.Size() < HEADER_LENGTH {
+		return hessian.ErrHeaderNotEnough
+	}
+	buf, err := c.reader.Peek(HEADER_LENGTH)
+	if err != nil { // this is impossible
+		return perrors.WithStack(err)
+	}
+	_, err = c.reader.Discard(HEADER_LENGTH)
+	if err != nil { // this is impossible
+		return perrors.WithStack(err)
+	}
+
+	//// read header
+	if buf[0] != MAGIC_HIGH && buf[1] != MAGIC_LOW {
+		return hessian.ErrIllegalPackage
+	}
+
+	// Header{serialization id(5 bit), event, two way, req/response}
+	if header.SerialID = buf[2] & SERIAL_MASK; header.SerialID == Zero {
+		return perrors.Errorf("serialization ID:%v", header.SerialID)
+	}
+
+	flag := buf[2] & FLAG_EVENT
+	if flag != Zero {
+		header.Type |= PackageHeartbeat
+	}
+	flag = buf[2] & FLAG_REQUEST
+	if flag != Zero {
+		header.Type |= PackageRequest
+		flag = buf[2] & FLAG_TWOWAY
+		if flag != Zero {
+			header.Type |= PackageRequest_TwoWay
+		}
+	} else {
+		header.Type |= PackageResponse
+		header.ResponseStatus = buf[3]
+		if header.ResponseStatus != Response_OK {
+			header.Type |= PackageResponse_Exception
+		}
+	}
+
+	// Header{req id}
+	header.ID = int64(binary.BigEndian.Uint64(buf[4:]))
+
+	// Header{body len}
+	header.BodyLen = int(binary.BigEndian.Uint32(buf[12:]))
+	if header.BodyLen < 0 {
+		return hessian.ErrIllegalPackage
+	}
+
+	c.pkgType = header.Type
+	c.bodyLen = header.BodyLen
+
+	if c.reader.Buffered() < c.bodyLen {
+		return hessian.ErrBodyNotEnough
+	}
+	c.headerRead = true
+	return perrors.WithStack(err)
+}
+
+func (c *ProtocolCodec) EncodeHeader(p DubboPackage) []byte {
+	header := p.Header
+	bs := make([]byte, 0)
+	switch header.Type {
+	case PackageHeartbeat:
+		if header.ResponseStatus == Zero {
+			bs = append(bs, hessian.DubboRequestHeartbeatHeader[:]...)
+		} else {
+			bs = append(bs, hessian.DubboResponseHeartbeatHeader[:]...)
+		}
+	case PackageResponse:
+		bs = append(bs, hessian.DubboResponseHeaderBytes[:]...)
+		if header.ResponseStatus != 0 {
+			bs[3] = header.ResponseStatus
+		}
+	case PackageRequest_TwoWay:
+		bs = append(bs, hessian.DubboRequestHeaderBytesTwoWay[:]...)
+	}
+	bs[2] |= header.SerialID & hessian.SERIAL_MASK
+	binary.BigEndian.PutUint64(bs[4:], uint64(header.ID))
+	return bs
+}
+
+func (c *ProtocolCodec) Encode(p DubboPackage) ([]byte, error) {
+	// header
+	if c.serializer == nil {
+		return nil, perrors.New("serializer should not be nil")
+	}
+	header := p.Header
+	switch header.Type {
+	case PackageHeartbeat:
+		if header.ResponseStatus == Zero {
+			return packRequest(p, c.serializer)
+		}
+		return packResponse(p, c.serializer)
+
+	case PackageRequest, PackageRequest_TwoWay:
+		return packRequest(p, c.serializer)
+
+	case PackageResponse:
+		return packResponse(p, c.serializer)
+
+	default:
+		return nil, perrors.Errorf("Unrecognised message type: %v", header.Type)
+	}
+}
+
+func (c *ProtocolCodec) Decode(p *DubboPackage) error {
+	if !c.headerRead {
+		if err := c.ReadHeader(&p.Header); err != nil {
+			return err
+		}
+	}
+	body, err := c.reader.Peek(p.GetBodyLen())
+	if err != nil {
+		return err
+	}
+	if p.IsResponseWithException() {
+		logger.Infof("response with exception: %+v", p.Header)
+		decoder := hessian.NewDecoder(body)
+		p.Body = &ResponsePayload{}
+		exception, err := decoder.Decode()
+		if err != nil {
+			return perrors.WithStack(err)
+		}
+		p.Body.(*ResponsePayload).Exception = perrors.Errorf("java exception:%s", exception.(string))
+		return nil
+	} else if p.IsHeartBeat() {
+		// heartbeat no need to unmarshal contents
+		return nil
+	}
+	if c.serializer == nil {
+		return perrors.New("Codec serializer is nil")
+	}
+	if p.IsResponse() {
+		p.Body = &ResponsePayload{
+			RspObj: remoting.GetPendingResponse(remoting.SequenceType(p.Header.ID)).Reply,
+		}
+	}
+	return c.serializer.Unmarshal(body, p)
+}
+
+func (c *ProtocolCodec) SetSerializer(serializer Serializer) {
+	c.serializer = serializer
+}
+
+func packRequest(p DubboPackage, serializer Serializer) ([]byte, error) {
+	var (
+		byteArray []byte
+		pkgLen    int
+	)
+
+	header := p.Header
+
+	//////////////////////////////////////////
+	// byteArray
+	//////////////////////////////////////////
+	// magic
+	switch header.Type {
+	case PackageHeartbeat:
+		byteArray = append(byteArray, DubboRequestHeartbeatHeader[:]...)
+	case PackageRequest_TwoWay:
+		byteArray = append(byteArray, DubboRequestHeaderBytesTwoWay[:]...)
+	default:
+		byteArray = append(byteArray, DubboRequestHeaderBytes[:]...)
+	}
+
+	// serialization id, two way flag, event, request/response flag
+	// SerialID is id of serialization approach in java dubbo
+	byteArray[2] |= header.SerialID & SERIAL_MASK
+	// request id
+	binary.BigEndian.PutUint64(byteArray[4:], uint64(header.ID))
+
+	//////////////////////////////////////////
+	// body
+	//////////////////////////////////////////
+	if p.IsHeartBeat() {
+		byteArray = append(byteArray, byte('N'))
+		pkgLen = 1
+	} else {
+		body, err := serializer.Marshal(p)
+		if err != nil {
+			return nil, err
+		}
+		pkgLen = len(body)
+		if pkgLen > int(DEFAULT_LEN) { // 8M
+			return nil, perrors.Errorf("Data length %d too large, max payload %d", pkgLen, DEFAULT_LEN)
+		}
+		byteArray = append(byteArray, body...)
+	}
+	binary.BigEndian.PutUint32(byteArray[12:], uint32(pkgLen))
+	return byteArray, nil
+}
+
+func packResponse(p DubboPackage, serializer Serializer) ([]byte, error) {
+	var (
+		byteArray []byte
+	)
+	header := p.Header
+	hb := p.IsHeartBeat()
+
+	// magic
+	if hb {
+		byteArray = append(byteArray, DubboResponseHeartbeatHeader[:]...)
+	} else {
+		byteArray = append(byteArray, DubboResponseHeaderBytes[:]...)
+	}
+	// set serialID, identify serialization types, eg: fastjson->6, hessian2->2
+	byteArray[2] |= header.SerialID & SERIAL_MASK
+	// response status
+	if header.ResponseStatus != 0 {
+		byteArray[3] = header.ResponseStatus
+	}
+
+	// request id
+	binary.BigEndian.PutUint64(byteArray[4:], uint64(header.ID))
+
+	// body
+	body, err := serializer.Marshal(p)
+	if err != nil {
+		return nil, err
+	}
+
+	pkgLen := len(body)
+	if pkgLen > int(DEFAULT_LEN) { // 8M
+		return nil, perrors.Errorf("Data length %d too large, max payload %d", pkgLen, DEFAULT_LEN)
+	}
+	// byteArray{body length}
+	binary.BigEndian.PutUint32(byteArray[12:], uint32(pkgLen))
+	byteArray = append(byteArray, body...)
+	return byteArray, nil
+}
+
+func NewDubboCodec(reader *bufio.Reader) *ProtocolCodec {
+	s, _ := GetSerializerById(constant.S_Hessian2)
+	return &ProtocolCodec{
+		reader:     reader,
+		pkgType:    0,
+		bodyLen:    0,
+		headerRead: false,
+		serializer: s.(Serializer),
+	}
+}
diff --git a/protocol/dubbo/codec_test.go b/protocol/dubbo/impl/codec_test.go
similarity index 51%
rename from protocol/dubbo/codec_test.go
rename to protocol/dubbo/impl/codec_test.go
index c2ca443637e23101679770e464f49e0cbdeab2a9..1c379282383c90ef27628af19ddaaafb2a4db9a6 100644
--- a/protocol/dubbo/codec_test.go
+++ b/protocol/dubbo/impl/codec_test.go
@@ -15,42 +15,46 @@
  * limitations under the License.
  */
 
-package dubbo
+package impl
 
 import (
-	"bytes"
 	"testing"
 	"time"
 )
 
 import (
-	hessian "github.com/apache/dubbo-go-hessian2"
-	perrors "github.com/pkg/errors"
 	"github.com/stretchr/testify/assert"
 )
 
-func TestDubboPackageMarshalAndUnmarshal(t *testing.T) {
-	pkg := &DubboPackage{}
+import (
+	"github.com/apache/dubbo-go/common/constant"
+)
+
+func TestDubboPackage_MarshalAndUnmarshal(t *testing.T) {
+	pkg := NewDubboPackage(nil)
 	pkg.Body = []interface{}{"a"}
-	pkg.Header.Type = hessian.PackageHeartbeat
-	pkg.Header.SerialID = byte(S_Dubbo)
+	pkg.Header.Type = PackageHeartbeat
+	pkg.Header.SerialID = constant.S_Hessian2
 	pkg.Header.ID = 10086
+	pkg.SetSerializer(HessianSerializer{})
 
 	// heartbeat
 	data, err := pkg.Marshal()
 	assert.NoError(t, err)
 
-	pkgres := &DubboPackage{}
+	pkgres := NewDubboPackage(data)
+	pkgres.SetSerializer(HessianSerializer{})
+
 	pkgres.Body = []interface{}{}
-	err = pkgres.Unmarshal(data)
+	err = pkgres.Unmarshal()
 	assert.NoError(t, err)
-	assert.Equal(t, hessian.PackageHeartbeat|hessian.PackageRequest|hessian.PackageRequest_TwoWay, pkgres.Header.Type)
-	assert.Equal(t, byte(S_Dubbo), pkgres.Header.SerialID)
+	assert.Equal(t, PackageHeartbeat|PackageRequest|PackageRequest_TwoWay, pkgres.Header.Type)
+	assert.Equal(t, constant.S_Hessian2, pkgres.Header.SerialID)
 	assert.Equal(t, int64(10086), pkgres.Header.ID)
 	assert.Equal(t, 0, len(pkgres.Body.([]interface{})))
 
 	// request
-	pkg.Header.Type = hessian.PackageRequest
+	pkg.Header.Type = PackageRequest
 	pkg.Service.Interface = "Service"
 	pkg.Service.Path = "path"
 	pkg.Service.Version = "2.6"
@@ -59,25 +63,27 @@ func TestDubboPackageMarshalAndUnmarshal(t *testing.T) {
 	data, err = pkg.Marshal()
 	assert.NoError(t, err)
 
-	pkgres = &DubboPackage{}
+	pkgres = NewDubboPackage(data)
+	pkgres.SetSerializer(HessianSerializer{})
 	pkgres.Body = make([]interface{}, 7)
-	err = pkgres.Unmarshal(data)
+	err = pkgres.Unmarshal()
+	reassembleBody := pkgres.GetBody().(map[string]interface{})
 	assert.NoError(t, err)
-	assert.Equal(t, hessian.PackageRequest, pkgres.Header.Type)
-	assert.Equal(t, byte(S_Dubbo), pkgres.Header.SerialID)
+	assert.Equal(t, PackageRequest, pkgres.Header.Type)
+	assert.Equal(t, constant.S_Hessian2, pkgres.Header.SerialID)
 	assert.Equal(t, int64(10086), pkgres.Header.ID)
-	assert.Equal(t, "2.0.2", pkgres.Body.([]interface{})[0])
-	assert.Equal(t, "path", pkgres.Body.([]interface{})[1])
-	assert.Equal(t, "2.6", pkgres.Body.([]interface{})[2])
-	assert.Equal(t, "Method", pkgres.Body.([]interface{})[3])
-	assert.Equal(t, "Ljava/lang/String;", pkgres.Body.([]interface{})[4])
-	assert.Equal(t, []interface{}{"a"}, pkgres.Body.([]interface{})[5])
-	assert.Equal(t, map[string]string{"dubbo": "2.0.2", "interface": "Service", "path": "path", "timeout": "1000", "version": "2.6"}, pkgres.Body.([]interface{})[6])
-}
-
-func TestIssue380(t *testing.T) {
-	pkg := &DubboPackage{}
-	buf := bytes.NewBuffer([]byte("hello"))
-	err := pkg.Unmarshal(buf)
-	assert.True(t, perrors.Cause(err) == hessian.ErrHeaderNotEnough)
+	assert.Equal(t, "2.0.2", reassembleBody["dubboVersion"].(string))
+	assert.Equal(t, "path", pkgres.Service.Path)
+	assert.Equal(t, "2.6", pkgres.Service.Version)
+	assert.Equal(t, "Method", pkgres.Service.Method)
+	assert.Equal(t, "Ljava/lang/String;", reassembleBody["argsTypes"].(string))
+	assert.Equal(t, []interface{}{"a"}, reassembleBody["args"])
+	tmpData := map[string]interface{}{
+		"dubbo":     "2.0.2",
+		"interface": "Service",
+		"path":      "path",
+		"timeout":   "1000",
+		"version":   "2.6",
+	}
+	assert.Equal(t, tmpData, reassembleBody["attachments"])
 }
diff --git a/protocol/dubbo/impl/const.go b/protocol/dubbo/impl/const.go
new file mode 100644
index 0000000000000000000000000000000000000000..70d8bae6cad63d436f5d9f1ef69c397ee8a052f3
--- /dev/null
+++ b/protocol/dubbo/impl/const.go
@@ -0,0 +1,252 @@
+/*
+ * 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 impl
+
+import (
+	"reflect"
+	"regexp"
+
+	"github.com/pkg/errors"
+)
+
+const (
+	DUBBO = "dubbo"
+)
+
+const (
+	mask = byte(127)
+	flag = byte(128)
+)
+
+const (
+	// Zero : byte zero
+	Zero = byte(0x00)
+)
+
+// constansts
+const (
+	TAG_READ        = int32(-1)
+	ASCII_GAP       = 32
+	CHUNK_SIZE      = 4096
+	BC_BINARY       = byte('B') // final chunk
+	BC_BINARY_CHUNK = byte('A') // non-final chunk
+
+	BC_BINARY_DIRECT  = byte(0x20) // 1-byte length binary
+	BINARY_DIRECT_MAX = byte(0x0f)
+	BC_BINARY_SHORT   = byte(0x34) // 2-byte length binary
+	BINARY_SHORT_MAX  = 0x3ff      // 0-1023 binary
+
+	BC_DATE        = byte(0x4a) // 64-bit millisecond UTC date
+	BC_DATE_MINUTE = byte(0x4b) // 32-bit minute UTC date
+
+	BC_DOUBLE = byte('D') // IEEE 64-bit double
+
+	BC_DOUBLE_ZERO  = byte(0x5b)
+	BC_DOUBLE_ONE   = byte(0x5c)
+	BC_DOUBLE_BYTE  = byte(0x5d)
+	BC_DOUBLE_SHORT = byte(0x5e)
+	BC_DOUBLE_MILL  = byte(0x5f)
+
+	BC_FALSE = byte('F') // boolean false
+
+	BC_INT = byte('I') // 32-bit int
+
+	INT_DIRECT_MIN = -0x10
+	INT_DIRECT_MAX = byte(0x2f)
+	BC_INT_ZERO    = byte(0x90)
+
+	INT_BYTE_MIN     = -0x800
+	INT_BYTE_MAX     = 0x7ff
+	BC_INT_BYTE_ZERO = byte(0xc8)
+
+	BC_END = byte('Z')
+
+	INT_SHORT_MIN     = -0x40000
+	INT_SHORT_MAX     = 0x3ffff
+	BC_INT_SHORT_ZERO = byte(0xd4)
+
+	BC_LIST_VARIABLE           = byte(0x55)
+	BC_LIST_FIXED              = byte('V')
+	BC_LIST_VARIABLE_UNTYPED   = byte(0x57)
+	BC_LIST_FIXED_UNTYPED      = byte(0x58)
+	_listFixedTypedLenTagMin   = byte(0x70)
+	_listFixedTypedLenTagMax   = byte(0x77)
+	_listFixedUntypedLenTagMin = byte(0x78)
+	_listFixedUntypedLenTagMax = byte(0x7f)
+
+	BC_LIST_DIRECT         = byte(0x70)
+	BC_LIST_DIRECT_UNTYPED = byte(0x78)
+	LIST_DIRECT_MAX        = byte(0x7)
+
+	BC_LONG         = byte('L') // 64-bit signed integer
+	LONG_DIRECT_MIN = -0x08
+	LONG_DIRECT_MAX = byte(0x0f)
+	BC_LONG_ZERO    = byte(0xe0)
+
+	LONG_BYTE_MIN     = -0x800
+	LONG_BYTE_MAX     = 0x7ff
+	BC_LONG_BYTE_ZERO = byte(0xf8)
+
+	LONG_SHORT_MIN     = -0x40000
+	LONG_SHORT_MAX     = 0x3ffff
+	BC_LONG_SHORT_ZERO = byte(0x3c)
+
+	BC_LONG_INT = byte(0x59)
+
+	BC_MAP         = byte('M')
+	BC_MAP_UNTYPED = byte('H')
+
+	BC_NULL = byte('N') // x4e
+
+	BC_OBJECT     = byte('O')
+	BC_OBJECT_DEF = byte('C')
+
+	BC_OBJECT_DIRECT  = byte(0x60)
+	OBJECT_DIRECT_MAX = byte(0x0f)
+
+	BC_REF = byte(0x51)
+
+	BC_STRING       = byte('S') // final string
+	BC_STRING_CHUNK = byte('R') // non-final string
+
+	BC_STRING_DIRECT  = byte(0x00)
+	STRING_DIRECT_MAX = byte(0x1f)
+	BC_STRING_SHORT   = byte(0x30)
+	STRING_SHORT_MAX  = 0x3ff
+
+	BC_TRUE = byte('T')
+
+	P_PACKET_CHUNK = byte(0x4f)
+	P_PACKET       = byte('P')
+
+	P_PACKET_DIRECT   = byte(0x80)
+	PACKET_DIRECT_MAX = byte(0x7f)
+
+	P_PACKET_SHORT   = byte(0x70)
+	PACKET_SHORT_MAX = 0xfff
+	ARRAY_STRING     = "[string"
+	ARRAY_INT        = "[int"
+	ARRAY_DOUBLE     = "[double"
+	ARRAY_FLOAT      = "[float"
+	ARRAY_BOOL       = "[boolean"
+	ARRAY_LONG       = "[long"
+
+	PATH_KEY      = "path"
+	GROUP_KEY     = "group"
+	INTERFACE_KEY = "interface"
+	VERSION_KEY   = "version"
+	TIMEOUT_KEY   = "timeout"
+
+	STRING_NIL   = ""
+	STRING_TRUE  = "true"
+	STRING_FALSE = "false"
+	STRING_ZERO  = "0.0"
+	STRING_ONE   = "1.0"
+)
+
+// ResponsePayload related consts
+const (
+	Response_OK                byte = 20
+	Response_CLIENT_TIMEOUT    byte = 30
+	Response_SERVER_TIMEOUT    byte = 31
+	Response_BAD_REQUEST       byte = 40
+	Response_BAD_RESPONSE      byte = 50
+	Response_SERVICE_NOT_FOUND byte = 60
+	Response_SERVICE_ERROR     byte = 70
+	Response_SERVER_ERROR      byte = 80
+	Response_CLIENT_ERROR      byte = 90
+
+	// According to "java dubbo" There are two cases of response:
+	// 		1. with attachments
+	// 		2. no attachments
+	RESPONSE_WITH_EXCEPTION                  int32 = 0
+	RESPONSE_VALUE                           int32 = 1
+	RESPONSE_NULL_VALUE                      int32 = 2
+	RESPONSE_WITH_EXCEPTION_WITH_ATTACHMENTS int32 = 3
+	RESPONSE_VALUE_WITH_ATTACHMENTS          int32 = 4
+	RESPONSE_NULL_VALUE_WITH_ATTACHMENTS     int32 = 5
+)
+
+/**
+ * the dubbo protocol header length is 16 Bytes.
+ * the first 2 Bytes is magic code '0xdabb'
+ * the next 1 Byte is message flags, in which its 16-20 bit is serial id, 21 for event, 22 for two way, 23 for request/response flag
+ * the next 1 Bytes is response state.
+ * the next 8 Bytes is package DI.
+ * the next 4 Bytes is package length.
+ **/
+const (
+	// header length.
+	HEADER_LENGTH = 16
+
+	// magic header
+	MAGIC      = uint16(0xdabb)
+	MAGIC_HIGH = byte(0xda)
+	MAGIC_LOW  = byte(0xbb)
+
+	// message flag.
+	FLAG_REQUEST = byte(0x80)
+	FLAG_TWOWAY  = byte(0x40)
+	FLAG_EVENT   = byte(0x20) // for heartbeat
+	SERIAL_MASK  = 0x1f
+
+	DUBBO_VERSION                          = "2.5.4"
+	DUBBO_VERSION_KEY                      = "dubbo"
+	DEFAULT_DUBBO_PROTOCOL_VERSION         = "2.0.2" // Dubbo RPC protocol version, for compatibility, it must not be between 2.0.10 ~ 2.6.2
+	LOWEST_VERSION_FOR_RESPONSE_ATTACHMENT = 2000200
+	DEFAULT_LEN                            = 8388608 // 8 * 1024 * 1024 default body max length
+)
+
+// regular
+const (
+	JAVA_IDENT_REGEX = "(?:[_$a-zA-Z][_$a-zA-Z0-9]*)"
+	CLASS_DESC       = "(?:L" + JAVA_IDENT_REGEX + "(?:\\/" + JAVA_IDENT_REGEX + ")*;)"
+	ARRAY_DESC       = "(?:\\[+(?:(?:[VZBCDFIJS])|" + CLASS_DESC + "))"
+	DESC_REGEX       = "(?:(?:[VZBCDFIJS])|" + CLASS_DESC + "|" + ARRAY_DESC + ")"
+)
+
+// Dubbo request response related consts
+var (
+	DubboRequestHeaderBytesTwoWay = [HEADER_LENGTH]byte{MAGIC_HIGH, MAGIC_LOW, FLAG_REQUEST | FLAG_TWOWAY}
+	DubboRequestHeaderBytes       = [HEADER_LENGTH]byte{MAGIC_HIGH, MAGIC_LOW, FLAG_REQUEST}
+	DubboResponseHeaderBytes      = [HEADER_LENGTH]byte{MAGIC_HIGH, MAGIC_LOW, Zero, Response_OK}
+	DubboRequestHeartbeatHeader   = [HEADER_LENGTH]byte{MAGIC_HIGH, MAGIC_LOW, FLAG_REQUEST | FLAG_TWOWAY | FLAG_EVENT}
+	DubboResponseHeartbeatHeader  = [HEADER_LENGTH]byte{MAGIC_HIGH, MAGIC_LOW, FLAG_EVENT}
+)
+
+// Error part
+var (
+	ErrHeaderNotEnough = errors.New("header buffer too short")
+	ErrBodyNotEnough   = errors.New("body buffer too short")
+	ErrJavaException   = errors.New("got java exception")
+	ErrIllegalPackage  = errors.New("illegal package!")
+)
+
+// DescRegex ...
+var DescRegex, _ = regexp.Compile(DESC_REGEX)
+
+var NilValue = reflect.Zero(reflect.TypeOf((*interface{})(nil)).Elem())
+
+// Body map keys
+var (
+	DubboVersionKey = "dubboVersion"
+	ArgsTypesKey    = "argsTypes"
+	ArgsKey         = "args"
+	ServiceKey      = "service"
+	AttachmentsKey  = "attachments"
+)
diff --git a/protocol/dubbo/impl/hessian.go b/protocol/dubbo/impl/hessian.go
new file mode 100644
index 0000000000000000000000000000000000000000..5fa1f2ece337e268c4907465bdc69ced76641ce7
--- /dev/null
+++ b/protocol/dubbo/impl/hessian.go
@@ -0,0 +1,528 @@
+/*
+ * 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 impl
+
+import (
+	"math"
+	"reflect"
+	"strconv"
+	"strings"
+	"time"
+)
+
+import (
+	hessian "github.com/apache/dubbo-go-hessian2"
+	"github.com/apache/dubbo-go-hessian2/java_exception"
+	perrors "github.com/pkg/errors"
+)
+
+import (
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/common/constant"
+	"github.com/apache/dubbo-go/common/logger"
+)
+
+type HessianSerializer struct {
+}
+
+func (h HessianSerializer) Marshal(p DubboPackage) ([]byte, error) {
+	encoder := hessian.NewEncoder()
+	if p.IsRequest() {
+		return marshalRequest(encoder, p)
+	}
+	return marshalResponse(encoder, p)
+}
+
+func (h HessianSerializer) Unmarshal(input []byte, p *DubboPackage) error {
+	if p.IsHeartBeat() {
+		return nil
+	}
+	if p.IsRequest() {
+		return unmarshalRequestBody(input, p)
+	}
+	return unmarshalResponseBody(input, p)
+}
+
+func marshalResponse(encoder *hessian.Encoder, p DubboPackage) ([]byte, error) {
+	header := p.Header
+	response := EnsureResponsePayload(p.Body)
+	if header.ResponseStatus == Response_OK {
+		if p.IsHeartBeat() {
+			encoder.Encode(nil)
+		} else {
+			var version string
+			if attachmentVersion, ok := response.Attachments[DUBBO_VERSION_KEY]; ok {
+				version = attachmentVersion.(string)
+			}
+			atta := isSupportResponseAttachment(version)
+
+			var resWithException, resValue, resNullValue int32
+			if atta {
+				resWithException = RESPONSE_WITH_EXCEPTION_WITH_ATTACHMENTS
+				resValue = RESPONSE_VALUE_WITH_ATTACHMENTS
+				resNullValue = RESPONSE_NULL_VALUE_WITH_ATTACHMENTS
+			} else {
+				resWithException = RESPONSE_WITH_EXCEPTION
+				resValue = RESPONSE_VALUE
+				resNullValue = RESPONSE_NULL_VALUE
+			}
+
+			if response.Exception != nil { // throw error
+				encoder.Encode(resWithException)
+				if t, ok := response.Exception.(java_exception.Throwabler); ok {
+					encoder.Encode(t)
+				} else {
+					encoder.Encode(java_exception.NewThrowable(response.Exception.Error()))
+				}
+			} else {
+				if response.RspObj == nil {
+					encoder.Encode(resNullValue)
+				} else {
+					encoder.Encode(resValue)
+					encoder.Encode(response.RspObj) // result
+				}
+			}
+
+			if atta {
+				encoder.Encode(response.Attachments) // attachments
+			}
+		}
+	} else {
+		if response.Exception != nil { // throw error
+			encoder.Encode(response.Exception.Error())
+		} else {
+			encoder.Encode(response.RspObj)
+		}
+	}
+	bs := encoder.Buffer()
+	// encNull
+	bs = append(bs, byte('N'))
+	return bs, nil
+}
+
+func marshalRequest(encoder *hessian.Encoder, p DubboPackage) ([]byte, error) {
+	service := p.Service
+	request := EnsureRequestPayload(p.Body)
+	encoder.Encode(DEFAULT_DUBBO_PROTOCOL_VERSION)
+	encoder.Encode(service.Path)
+	encoder.Encode(service.Version)
+	encoder.Encode(service.Method)
+
+	args, ok := request.Params.([]interface{})
+
+	if !ok {
+		logger.Infof("request args are: %+v", request.Params)
+		return nil, perrors.Errorf("@params is not of type: []interface{}")
+	}
+	types, err := getArgsTypeList(args)
+	if err != nil {
+		return nil, perrors.Wrapf(err, " PackRequest(args:%+v)", args)
+	}
+	encoder.Encode(types)
+	for _, v := range args {
+		encoder.Encode(v)
+	}
+
+	request.Attachments[PATH_KEY] = service.Path
+	request.Attachments[VERSION_KEY] = service.Version
+	if len(service.Group) > 0 {
+		request.Attachments[GROUP_KEY] = service.Group
+	}
+	if len(service.Interface) > 0 {
+		request.Attachments[INTERFACE_KEY] = service.Interface
+	}
+	if service.Timeout != 0 {
+		request.Attachments[TIMEOUT_KEY] = strconv.Itoa(int(service.Timeout / time.Millisecond))
+	}
+
+	encoder.Encode(request.Attachments)
+	return encoder.Buffer(), nil
+
+}
+
+var versionInt = make(map[string]int)
+
+// https://github.com/apache/dubbo/blob/dubbo-2.7.1/dubbo-common/src/main/java/org/apache/dubbo/common/Version.java#L96
+// isSupportResponseAttachment is for compatibility among some dubbo version
+func isSupportResponseAttachment(version string) bool {
+	if version == "" {
+		return false
+	}
+
+	v, ok := versionInt[version]
+	if !ok {
+		v = version2Int(version)
+		if v == -1 {
+			return false
+		}
+	}
+
+	if v >= 2001000 && v <= 2060200 { // 2.0.10 ~ 2.6.2
+		return false
+	}
+	return v >= LOWEST_VERSION_FOR_RESPONSE_ATTACHMENT
+}
+
+func version2Int(version string) int {
+	var v = 0
+	varr := strings.Split(version, ".")
+	length := len(varr)
+	for key, value := range varr {
+		v0, err := strconv.Atoi(value)
+		if err != nil {
+			return -1
+		}
+		v += v0 * int(math.Pow10((length-key-1)*2))
+	}
+	if length == 3 {
+		return v * 100
+	}
+	return v
+}
+
+func unmarshalRequestBody(body []byte, p *DubboPackage) error {
+	if p.Body == nil {
+		p.SetBody(make([]interface{}, 7))
+	}
+	decoder := hessian.NewDecoder(body)
+	var (
+		err                                                     error
+		dubboVersion, target, serviceVersion, method, argsTypes interface{}
+		args                                                    []interface{}
+	)
+	req, ok := p.Body.([]interface{})
+	if !ok {
+		return perrors.Errorf("@reqObj is not of type: []interface{}")
+	}
+	dubboVersion, err = decoder.Decode()
+	if err != nil {
+		return perrors.WithStack(err)
+	}
+	req[0] = dubboVersion
+
+	target, err = decoder.Decode()
+	if err != nil {
+		return perrors.WithStack(err)
+	}
+	req[1] = target
+
+	serviceVersion, err = decoder.Decode()
+	if err != nil {
+		return perrors.WithStack(err)
+	}
+	req[2] = serviceVersion
+
+	method, err = decoder.Decode()
+	if err != nil {
+		return perrors.WithStack(err)
+	}
+	req[3] = method
+
+	argsTypes, err = decoder.Decode()
+	if err != nil {
+		return perrors.WithStack(err)
+	}
+	req[4] = argsTypes
+
+	ats := hessian.DescRegex.FindAllString(argsTypes.(string), -1)
+	var arg interface{}
+	for i := 0; i < len(ats); i++ {
+		arg, err = decoder.Decode()
+		if err != nil {
+			return perrors.WithStack(err)
+		}
+		args = append(args, arg)
+	}
+	req[5] = args
+
+	attachments, err := decoder.Decode()
+	if err != nil {
+		return perrors.WithStack(err)
+	}
+
+	if v, ok := attachments.(map[interface{}]interface{}); ok {
+		v[DUBBO_VERSION_KEY] = dubboVersion
+		req[6] = ToMapStringInterface(v)
+		buildServerSidePackageBody(p)
+		return nil
+	}
+	return perrors.Errorf("get wrong attachments: %+v", attachments)
+}
+
+func unmarshalResponseBody(body []byte, p *DubboPackage) error {
+	decoder := hessian.NewDecoder(body)
+	rspType, err := decoder.Decode()
+	if p.Body == nil {
+		p.SetBody(&ResponsePayload{})
+	}
+	if err != nil {
+		return perrors.WithStack(err)
+	}
+	response := EnsureResponsePayload(p.Body)
+
+	switch rspType {
+	case RESPONSE_WITH_EXCEPTION, RESPONSE_WITH_EXCEPTION_WITH_ATTACHMENTS:
+		expt, err := decoder.Decode()
+		if err != nil {
+			return perrors.WithStack(err)
+		}
+		if rspType == RESPONSE_WITH_EXCEPTION_WITH_ATTACHMENTS {
+			attachments, err := decoder.Decode()
+			if err != nil {
+				return perrors.WithStack(err)
+			}
+			if v, ok := attachments.(map[interface{}]interface{}); ok {
+				atta := ToMapStringInterface(v)
+				response.Attachments = atta
+			} else {
+				return perrors.Errorf("get wrong attachments: %+v", attachments)
+			}
+		}
+
+		if e, ok := expt.(error); ok {
+			response.Exception = e
+		} else {
+			response.Exception = perrors.Errorf("got exception: %+v", expt)
+		}
+		return nil
+
+	case RESPONSE_VALUE, RESPONSE_VALUE_WITH_ATTACHMENTS:
+		rsp, err := decoder.Decode()
+		if err != nil {
+			return perrors.WithStack(err)
+		}
+		if rspType == RESPONSE_VALUE_WITH_ATTACHMENTS {
+			attachments, err := decoder.Decode()
+			if err != nil {
+				return perrors.WithStack(err)
+			}
+			if v, ok := attachments.(map[interface{}]interface{}); ok {
+				atta := ToMapStringInterface(v)
+				response.Attachments = atta
+			} else {
+				return perrors.Errorf("get wrong attachments: %+v", attachments)
+			}
+		}
+
+		return perrors.WithStack(hessian.ReflectResponse(rsp, response.RspObj))
+
+	case RESPONSE_NULL_VALUE, RESPONSE_NULL_VALUE_WITH_ATTACHMENTS:
+		if rspType == RESPONSE_NULL_VALUE_WITH_ATTACHMENTS {
+			attachments, err := decoder.Decode()
+			if err != nil {
+				return perrors.WithStack(err)
+			}
+			if v, ok := attachments.(map[interface{}]interface{}); ok {
+				atta := ToMapStringInterface(v)
+				response.Attachments = atta
+			} else {
+				return perrors.Errorf("get wrong attachments: %+v", attachments)
+			}
+		}
+		return nil
+	}
+	return nil
+}
+
+func buildServerSidePackageBody(pkg *DubboPackage) {
+	req := pkg.GetBody().([]interface{}) // length of body should be 7
+	if len(req) > 0 {
+		var dubboVersion, argsTypes string
+		var args []interface{}
+		var attachments map[string]interface{}
+		svc := Service{}
+		if req[0] != nil {
+			dubboVersion = req[0].(string)
+		}
+		if req[1] != nil {
+			svc.Path = req[1].(string)
+		}
+		if req[2] != nil {
+			svc.Version = req[2].(string)
+		}
+		if req[3] != nil {
+			svc.Method = req[3].(string)
+		}
+		if req[4] != nil {
+			argsTypes = req[4].(string)
+		}
+		if req[5] != nil {
+			args = req[5].([]interface{})
+		}
+		if req[6] != nil {
+			attachments = req[6].(map[string]interface{})
+		}
+		if svc.Path == "" && attachments[constant.PATH_KEY] != nil && len(attachments[constant.PATH_KEY].(string)) > 0 {
+			svc.Path = attachments[constant.PATH_KEY].(string)
+		}
+		if _, ok := attachments[constant.INTERFACE_KEY]; ok {
+			svc.Interface = attachments[constant.INTERFACE_KEY].(string)
+		} else {
+			svc.Interface = svc.Path
+		}
+		if _, ok := attachments[constant.GROUP_KEY]; ok {
+			svc.Group = attachments[constant.GROUP_KEY].(string)
+		}
+		pkg.SetService(svc)
+		pkg.SetBody(map[string]interface{}{
+			"dubboVersion": dubboVersion,
+			"argsTypes":    argsTypes,
+			"args":         args,
+			"service":      common.ServiceMap.GetService(DUBBO, svc.Interface, svc.Group, svc.Version), // path as a key
+			"attachments":  attachments,
+		})
+	}
+}
+
+func getArgsTypeList(args []interface{}) (string, error) {
+	var (
+		typ   string
+		types string
+	)
+
+	for i := range args {
+		typ = getArgType(args[i])
+		if typ == "" {
+			return types, perrors.Errorf("cat not get arg %#v type", args[i])
+		}
+		if !strings.Contains(typ, ".") {
+			types += typ
+		} else if strings.Index(typ, "[") == 0 {
+			types += strings.Replace(typ, ".", "/", -1)
+		} else {
+			// java.util.List -> Ljava/util/List;
+			types += "L" + strings.Replace(typ, ".", "/", -1) + ";"
+		}
+	}
+
+	return types, nil
+}
+
+func getArgType(v interface{}) string {
+	if v == nil {
+		return "V"
+	}
+
+	switch v.(type) {
+	// Serialized tags for base types
+	case nil:
+		return "V"
+	case bool:
+		return "Z"
+	case []bool:
+		return "[Z"
+	case byte:
+		return "B"
+	case []byte:
+		return "[B"
+	case int8:
+		return "B"
+	case []int8:
+		return "[B"
+	case int16:
+		return "S"
+	case []int16:
+		return "[S"
+	case uint16: // Equivalent to Char of Java
+		return "C"
+	case []uint16:
+		return "[C"
+	// case rune:
+	//	return "C"
+	case int:
+		return "J"
+	case []int:
+		return "[J"
+	case int32:
+		return "I"
+	case []int32:
+		return "[I"
+	case int64:
+		return "J"
+	case []int64:
+		return "[J"
+	case time.Time:
+		return "java.util.Date"
+	case []time.Time:
+		return "[Ljava.util.Date"
+	case float32:
+		return "F"
+	case []float32:
+		return "[F"
+	case float64:
+		return "D"
+	case []float64:
+		return "[D"
+	case string:
+		return "java.lang.String"
+	case []string:
+		return "[Ljava.lang.String;"
+	case []hessian.Object:
+		return "[Ljava.lang.Object;"
+	case map[interface{}]interface{}:
+		// return  "java.util.HashMap"
+		return "java.util.Map"
+	case hessian.POJOEnum:
+		return v.(hessian.POJOEnum).JavaClassName()
+	//  Serialized tags for complex types
+	default:
+		t := reflect.TypeOf(v)
+		if reflect.Ptr == t.Kind() {
+			t = reflect.TypeOf(reflect.ValueOf(v).Elem())
+		}
+		switch t.Kind() {
+		case reflect.Struct:
+			v, ok := v.(hessian.POJO)
+			if ok {
+				return v.JavaClassName()
+			}
+			return "java.lang.Object"
+		case reflect.Slice, reflect.Array:
+			if t.Elem().Kind() == reflect.Struct {
+				return "[Ljava.lang.Object;"
+			}
+			// return "java.util.ArrayList"
+			return "java.util.List"
+		case reflect.Map: // Enter here, map may be map[string]int
+			return "java.util.Map"
+		default:
+			return ""
+		}
+	}
+
+	// unreachable
+	// return "java.lang.RuntimeException"
+}
+
+func ToMapStringInterface(origin map[interface{}]interface{}) map[string]interface{} {
+	dest := make(map[string]interface{}, len(origin))
+	for k, v := range origin {
+		if kv, ok := k.(string); ok {
+			if v == nil {
+				dest[kv] = ""
+				continue
+			}
+			dest[kv] = v
+		}
+	}
+	return dest
+}
+
+func init() {
+	SetSerializer("hessian2", HessianSerializer{})
+}
diff --git a/protocol/dubbo/impl/package.go b/protocol/dubbo/impl/package.go
new file mode 100644
index 0000000000000000000000000000000000000000..6f6d2ea9753a513412a4f5099c396dd90cf454ba
--- /dev/null
+++ b/protocol/dubbo/impl/package.go
@@ -0,0 +1,171 @@
+/*
+ * 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 impl
+
+import (
+	"bufio"
+	"bytes"
+	"fmt"
+	"time"
+)
+
+import (
+	"github.com/pkg/errors"
+)
+
+type PackageType int
+
+// enum part
+const (
+	PackageError              = PackageType(0x01)
+	PackageRequest            = PackageType(0x02)
+	PackageResponse           = PackageType(0x04)
+	PackageHeartbeat          = PackageType(0x08)
+	PackageRequest_TwoWay     = PackageType(0x10)
+	PackageResponse_Exception = PackageType(0x20)
+	PackageType_BitSize       = 0x2f
+)
+
+type DubboHeader struct {
+	SerialID       byte
+	Type           PackageType
+	ID             int64
+	BodyLen        int
+	ResponseStatus byte
+}
+
+// Service defines service instance
+type Service struct {
+	Path      string
+	Interface string
+	Group     string
+	Version   string
+	Method    string
+	Timeout   time.Duration // request timeout
+}
+
+type DubboPackage struct {
+	Header  DubboHeader
+	Service Service
+	Body    interface{}
+	Err     error
+	Codec   *ProtocolCodec
+}
+
+func (p DubboPackage) String() string {
+	return fmt.Sprintf("HessianPackage: Header-%v, Path-%v, Body-%v", p.Header, p.Service, p.Body)
+}
+
+func (p *DubboPackage) ReadHeader() error {
+	return p.Codec.ReadHeader(&p.Header)
+}
+
+func (p *DubboPackage) Marshal() (*bytes.Buffer, error) {
+	if p.Codec == nil {
+		return nil, errors.New("Codec is nil")
+	}
+	pkg, err := p.Codec.Encode(*p)
+	if err != nil {
+		return nil, errors.WithStack(err)
+	}
+	return bytes.NewBuffer(pkg), nil
+}
+
+func (p *DubboPackage) Unmarshal() error {
+	if p.Codec == nil {
+		return errors.New("Codec is nil")
+	}
+	return p.Codec.Decode(p)
+}
+
+func (p DubboPackage) IsHeartBeat() bool {
+	return p.Header.Type&PackageHeartbeat != 0
+}
+
+func (p DubboPackage) IsRequest() bool {
+	return p.Header.Type&(PackageRequest_TwoWay|PackageRequest) != 0
+}
+
+func (p DubboPackage) IsResponse() bool {
+	return p.Header.Type == PackageResponse
+}
+
+func (p DubboPackage) IsResponseWithException() bool {
+	flag := PackageResponse | PackageResponse_Exception
+	return p.Header.Type&flag == flag
+}
+
+func (p DubboPackage) GetBodyLen() int {
+	return p.Header.BodyLen
+}
+
+func (p DubboPackage) GetLen() int {
+	return HEADER_LENGTH + p.Header.BodyLen
+}
+
+func (p DubboPackage) GetBody() interface{} {
+	return p.Body
+}
+
+func (p *DubboPackage) SetBody(body interface{}) {
+	p.Body = body
+}
+
+func (p *DubboPackage) SetHeader(header DubboHeader) {
+	p.Header = header
+}
+
+func (p *DubboPackage) SetService(svc Service) {
+	p.Service = svc
+}
+
+func (p *DubboPackage) SetID(id int64) {
+	p.Header.ID = id
+}
+
+func (p DubboPackage) GetHeader() DubboHeader {
+	return p.Header
+}
+
+func (p DubboPackage) GetService() Service {
+	return p.Service
+}
+
+func (p *DubboPackage) SetResponseStatus(status byte) {
+	p.Header.ResponseStatus = status
+}
+
+func (p *DubboPackage) SetSerializer(serializer Serializer) {
+	p.Codec.SetSerializer(serializer)
+}
+
+func NewDubboPackage(data *bytes.Buffer) *DubboPackage {
+	var codec *ProtocolCodec
+	if data == nil {
+		codec = NewDubboCodec(nil)
+	} else {
+		codec = NewDubboCodec(bufio.NewReaderSize(data, len(data.Bytes())))
+	}
+	return &DubboPackage{
+		Header:  DubboHeader{},
+		Service: Service{},
+		Body:    nil,
+		Err:     nil,
+		Codec:   codec,
+	}
+}
diff --git a/protocol/dubbo/impl/request.go b/protocol/dubbo/impl/request.go
new file mode 100644
index 0000000000000000000000000000000000000000..ef520083e6457f0ceaf3e80778f79d3e0ce686ab
--- /dev/null
+++ b/protocol/dubbo/impl/request.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 impl
+
+type RequestPayload struct {
+	Params      interface{}
+	Attachments map[string]interface{}
+}
+
+func NewRequestPayload(args interface{}, atta map[string]interface{}) *RequestPayload {
+	if atta == nil {
+		atta = make(map[string]interface{})
+	}
+	return &RequestPayload{
+		Params:      args,
+		Attachments: atta,
+	}
+}
+
+func EnsureRequestPayload(body interface{}) *RequestPayload {
+	if req, ok := body.(*RequestPayload); ok {
+		return req
+	}
+	return NewRequestPayload(body, nil)
+}
diff --git a/protocol/dubbo/impl/response.go b/protocol/dubbo/impl/response.go
new file mode 100644
index 0000000000000000000000000000000000000000..9fde1eb249c40e546bdabc54d15e4a3b6d1ea399
--- /dev/null
+++ b/protocol/dubbo/impl/response.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 impl
+
+type ResponsePayload struct {
+	RspObj      interface{}
+	Exception   error
+	Attachments map[string]interface{}
+}
+
+// NewResponse create a new ResponsePayload
+func NewResponsePayload(rspObj interface{}, exception error, attachments map[string]interface{}) *ResponsePayload {
+	if attachments == nil {
+		attachments = make(map[string]interface{})
+	}
+	return &ResponsePayload{
+		RspObj:      rspObj,
+		Exception:   exception,
+		Attachments: attachments,
+	}
+}
+
+func EnsureResponsePayload(body interface{}) *ResponsePayload {
+	if res, ok := body.(*ResponsePayload); ok {
+		return res
+	}
+	if exp, ok := body.(error); ok {
+		return NewResponsePayload(nil, exp, nil)
+	}
+	return NewResponsePayload(body, nil, nil)
+}
diff --git a/protocol/dubbo/impl/serialization.go b/protocol/dubbo/impl/serialization.go
new file mode 100644
index 0000000000000000000000000000000000000000..7ce76a87c17376cfb17983b6b6b3bdaf2771c22e
--- /dev/null
+++ b/protocol/dubbo/impl/serialization.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 impl
+
+import (
+	"fmt"
+)
+
+import (
+	"github.com/apache/dubbo-go/common/constant"
+)
+
+var (
+	serializers = make(map[string]interface{})
+	nameMaps    = make(map[byte]string)
+)
+
+func init() {
+	nameMaps = map[byte]string{
+		constant.S_Hessian2: constant.HESSIAN2_SERIALIZATION,
+		constant.S_Proto:    constant.PROTOBUF_SERIALIZATION,
+	}
+}
+
+func SetSerializer(name string, serializer interface{}) {
+	serializers[name] = serializer
+}
+
+func GetSerializerById(id byte) (interface{}, error) {
+	name, ok := nameMaps[id]
+	if !ok {
+		panic(fmt.Sprintf("serialId %d not found", id))
+	}
+	serializer, ok := serializers[name]
+	if !ok {
+		panic(fmt.Sprintf("serialization %s not found", name))
+	}
+	return serializer, nil
+}
diff --git a/protocol/dubbo/impl/serialize.go b/protocol/dubbo/impl/serialize.go
new file mode 100644
index 0000000000000000000000000000000000000000..1f913f7caae2a2c758764a92e4e63a0d9555ff1a
--- /dev/null
+++ b/protocol/dubbo/impl/serialize.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 impl
+
+import (
+	"github.com/apache/dubbo-go/common/constant"
+)
+
+type Serializer interface {
+	Marshal(p DubboPackage) ([]byte, error)
+	Unmarshal([]byte, *DubboPackage) error
+}
+
+func LoadSerializer(p *DubboPackage) error {
+	// NOTE: default serialID is S_Hessian
+	serialID := p.Header.SerialID
+	if serialID == 0 {
+		serialID = constant.S_Hessian2
+	}
+	serializer, err := GetSerializerById(serialID)
+	if err != nil {
+		panic(err)
+	}
+	p.SetSerializer(serializer.(Serializer))
+	return nil
+}
diff --git a/protocol/dubbo/listener.go b/protocol/dubbo/listener.go
deleted file mode 100644
index a17b282fdf3a47cc739793eb93079f758d8de205..0000000000000000000000000000000000000000
--- a/protocol/dubbo/listener.go
+++ /dev/null
@@ -1,370 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dubbo
-
-import (
-	"context"
-	"fmt"
-	"net/url"
-	"sync"
-	"sync/atomic"
-	"time"
-)
-
-import (
-	"github.com/apache/dubbo-getty"
-	"github.com/apache/dubbo-go-hessian2"
-	"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"
-	"github.com/apache/dubbo-go/protocol"
-	"github.com/apache/dubbo-go/protocol/invocation"
-)
-
-// todo: writePkg_Timeout will entry *.yml
-const (
-	writePkg_Timeout = 5 * time.Second
-)
-
-var (
-	errTooManySessions = perrors.New("too many sessions")
-)
-
-type rpcSession struct {
-	session getty.Session
-	reqNum  int32
-}
-
-// AddReqNum adds total request number safely
-func (s *rpcSession) AddReqNum(num int32) {
-	atomic.AddInt32(&s.reqNum, num)
-}
-
-// GetReqNum gets total request number safely
-func (s *rpcSession) GetReqNum() int32 {
-	return atomic.LoadInt32(&s.reqNum)
-}
-
-// //////////////////////////////////////////
-// RpcClientHandler
-// //////////////////////////////////////////
-
-// RpcClientHandler is handler of RPC Client
-type RpcClientHandler struct {
-	conn *gettyRPCClient
-}
-
-// NewRpcClientHandler creates RpcClientHandler with @gettyRPCClient
-func NewRpcClientHandler(client *gettyRPCClient) *RpcClientHandler {
-	return &RpcClientHandler{conn: client}
-}
-
-// OnOpen notified when RPC client session opened
-func (h *RpcClientHandler) OnOpen(session getty.Session) error {
-	h.conn.addSession(session)
-	return nil
-}
-
-// OnError notified when RPC client session got any error
-func (h *RpcClientHandler) OnError(session getty.Session, err error) {
-	logger.Warnf("session{%s} got error{%v}, will be closed.", session.Stat(), err)
-	h.conn.removeSession(session)
-}
-
-// OnOpen notified when RPC client session closed
-func (h *RpcClientHandler) OnClose(session getty.Session) {
-	logger.Infof("session{%s} is closing......", session.Stat())
-	h.conn.removeSession(session)
-}
-
-// OnMessage notified when RPC client session got any message in connection
-func (h *RpcClientHandler) OnMessage(session getty.Session, pkg interface{}) {
-	p, ok := pkg.(*DubboPackage)
-	if !ok {
-		logger.Errorf("illegal package")
-		return
-	}
-
-	if p.Header.Type&hessian.PackageHeartbeat != 0x00 {
-		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)
-		}
-		return
-	}
-	logger.Debugf("get rpc response{header: %#v, body: %#v}", p.Header, p.Body)
-
-	h.conn.updateSession(session)
-
-	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
-	}
-
-	if p.Err != nil {
-		pendingResponse.err = p.Err
-	}
-
-	pendingResponse.response.atta = p.Body.(*Response).atta
-
-	if pendingResponse.callback == nil {
-		pendingResponse.done <- struct{}{}
-	} else {
-		pendingResponse.callback(pendingResponse.GetCallResponse())
-	}
-}
-
-// OnCron notified when RPC client session got any message in cron job
-func (h *RpcClientHandler) OnCron(session getty.Session) {
-	clientRpcSession, err := h.conn.getClientRpcSession(session)
-	if err != nil {
-		logger.Errorf("client.getClientSession(session{%s}) = error{%v}",
-			session.Stat(), perrors.WithStack(err))
-		return
-	}
-	if h.conn.pool.rpcClient.conf.sessionTimeout.Nanoseconds() < time.Since(session.GetActive()).Nanoseconds() {
-		logger.Warnf("session{%s} timeout{%s}, reqNum{%d}",
-			session.Stat(), time.Since(session.GetActive()).String(), clientRpcSession.reqNum)
-		h.conn.removeSession(session) // -> h.conn.close() -> h.conn.pool.remove(h.conn)
-		return
-	}
-
-	h.conn.pool.rpcClient.heartbeat(session)
-}
-
-// //////////////////////////////////////////
-// RpcServerHandler
-// //////////////////////////////////////////
-
-// RpcServerHandler is handler of RPC Server
-type RpcServerHandler struct {
-	maxSessionNum  int
-	sessionTimeout time.Duration
-	sessionMap     map[getty.Session]*rpcSession
-	rwlock         sync.RWMutex
-}
-
-// NewRpcServerHandler creates RpcServerHandler with @maxSessionNum and @sessionTimeout
-func NewRpcServerHandler(maxSessionNum int, sessionTimeout time.Duration) *RpcServerHandler {
-	return &RpcServerHandler{
-		maxSessionNum:  maxSessionNum,
-		sessionTimeout: sessionTimeout,
-		sessionMap:     make(map[getty.Session]*rpcSession),
-	}
-}
-
-// OnOpen notified when RPC server session opened
-func (h *RpcServerHandler) OnOpen(session getty.Session) error {
-	var err error
-	h.rwlock.RLock()
-	if h.maxSessionNum <= len(h.sessionMap) {
-		err = errTooManySessions
-	}
-	h.rwlock.RUnlock()
-	if err != nil {
-		return perrors.WithStack(err)
-	}
-
-	logger.Infof("got session:%s", session.Stat())
-	h.rwlock.Lock()
-	h.sessionMap[session] = &rpcSession{session: session}
-	h.rwlock.Unlock()
-	return nil
-}
-
-// OnError notified when RPC server session got any error
-func (h *RpcServerHandler) OnError(session getty.Session, err error) {
-	logger.Warnf("session{%s} got error{%v}, will be closed.", session.Stat(), err)
-	h.rwlock.Lock()
-	delete(h.sessionMap, session)
-	h.rwlock.Unlock()
-}
-
-// OnOpen notified when RPC server session closed
-func (h *RpcServerHandler) OnClose(session getty.Session) {
-	logger.Infof("session{%s} is closing......", session.Stat())
-	h.rwlock.Lock()
-	delete(h.sessionMap, session)
-	h.rwlock.Unlock()
-}
-
-// OnMessage notified when RPC server session got any message in connection
-func (h *RpcServerHandler) OnMessage(session getty.Session, pkg interface{}) {
-	h.rwlock.Lock()
-	if _, ok := h.sessionMap[session]; ok {
-		h.sessionMap[session].reqNum++
-	}
-	h.rwlock.Unlock()
-
-	p, ok := pkg.(*DubboPackage)
-	if !ok {
-		logger.Errorf("illegal package{%#v}", pkg)
-		return
-	}
-	p.Header.ResponseStatus = hessian.Response_OK
-
-	// 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)
-		reply(session, p, hessian.PackageHeartbeat)
-		return
-	}
-
-	twoway := true
-	// not twoway
-	if p.Header.Type&hessian.PackageRequest_TwoWay == 0x00 {
-		twoway = false
-	}
-
-	defer func() {
-		if e := recover(); e != nil {
-			p.Header.ResponseStatus = hessian.Response_SERVER_ERROR
-			if err, ok := e.(error); ok {
-				logger.Errorf("OnMessage panic: %+v", perrors.WithStack(err))
-				p.Body = perrors.WithStack(err)
-			} else if err, ok := e.(string); ok {
-				logger.Errorf("OnMessage panic: %+v", perrors.New(err))
-				p.Body = perrors.New(err)
-			} else {
-				logger.Errorf("OnMessage panic: %+v, this is impossible.", e)
-				p.Body = e
-			}
-
-			if !twoway {
-				return
-			}
-			reply(session, p, hessian.PackageResponse)
-		}
-
-	}()
-
-	u := common.NewURLWithOptions(common.WithPath(p.Service.Path), common.WithParams(url.Values{}),
-		common.WithParamsValue(constant.GROUP_KEY, p.Service.Group),
-		common.WithParamsValue(constant.INTERFACE_KEY, p.Service.Interface),
-		common.WithParamsValue(constant.VERSION_KEY, p.Service.Version))
-	exporter, _ := dubboProtocol.ExporterMap().Load(u.ServiceKey())
-	if exporter == nil {
-		err := fmt.Errorf("don't have this exporter, key: %s", u.ServiceKey())
-		logger.Errorf(err.Error())
-		p.Header.ResponseStatus = hessian.Response_OK
-		p.Body = err
-		reply(session, p, hessian.PackageResponse)
-		return
-	}
-	invoker := exporter.(protocol.Exporter).GetInvoker()
-	if invoker != nil {
-		attachments := p.Body.(map[string]interface{})["attachments"].(map[string]string)
-		attachments[constant.LOCAL_ADDR] = session.LocalAddr()
-		attachments[constant.REMOTE_ADDR] = session.RemoteAddr()
-
-		args := p.Body.(map[string]interface{})["args"].([]interface{})
-		inv := invocation.NewRPCInvocation(p.Service.Method, args, attachments)
-
-		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())
-		} else {
-			res := result.Result()
-			p.Header.ResponseStatus = hessian.Response_OK
-			p.Body = hessian.NewResponse(res, nil, result.Attachments())
-		}
-	}
-
-	if !twoway {
-		return
-	}
-	reply(session, p, hessian.PackageResponse)
-}
-
-// OnCron notified when RPC server session got any message in cron job
-func (h *RpcServerHandler) OnCron(session getty.Session) {
-	var (
-		flag   bool
-		active time.Time
-	)
-
-	h.rwlock.RLock()
-	if _, ok := h.sessionMap[session]; ok {
-		active = session.GetActive()
-		if h.sessionTimeout.Nanoseconds() < time.Since(active).Nanoseconds() {
-			flag = true
-			logger.Warnf("session{%s} timeout{%s}, reqNum{%d}",
-				session.Stat(), time.Since(active).String(), h.sessionMap[session].reqNum)
-		}
-	}
-	h.rwlock.RUnlock()
-
-	if flag {
-		h.rwlock.Lock()
-		delete(h.sessionMap, session)
-		h.rwlock.Unlock()
-		session.Close()
-	}
-}
-
-// 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,
-			Type:           tp,
-			ID:             req.Header.ID,
-			ResponseStatus: req.Header.ResponseStatus,
-		},
-	}
-
-	if req.Header.Type&hessian.PackageRequest != 0x00 {
-		resp.Body = req.Body
-	} else {
-		resp.Body = nil
-	}
-
-	if err := session.WritePkg(resp, writePkg_Timeout); err != nil {
-		logger.Errorf("WritePkg error: %#v, %#v", perrors.WithStack(err), req.Header)
-	}
-}
diff --git a/protocol/dubbo/opentracing.go b/protocol/dubbo/opentracing.go
new file mode 100644
index 0000000000000000000000000000000000000000..2dcd6a4d0d9f491ba6d51ea7a3ba96812a6f9e08
--- /dev/null
+++ b/protocol/dubbo/opentracing.go
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dubbo
+
+import (
+	"github.com/opentracing/opentracing-go"
+)
+import (
+	invocation_impl "github.com/apache/dubbo-go/protocol/invocation"
+)
+
+func injectTraceCtx(currentSpan opentracing.Span, inv *invocation_impl.RPCInvocation) error {
+	// inject opentracing ctx
+	traceAttachments := filterContext(inv.Attachments())
+	carrier := opentracing.TextMapCarrier(traceAttachments)
+	err := opentracing.GlobalTracer().Inject(currentSpan.Context(), opentracing.TextMap, carrier)
+	if err == nil {
+		fillTraceAttachments(inv.Attachments(), traceAttachments)
+	}
+	return err
+}
+
+func extractTraceCtx(inv *invocation_impl.RPCInvocation) (opentracing.SpanContext, error) {
+	traceAttachments := filterContext(inv.Attachments())
+	// actually, if user do not use any opentracing framework, the err will not be nil.
+	spanCtx, err := opentracing.GlobalTracer().Extract(opentracing.TextMap,
+		opentracing.TextMapCarrier(traceAttachments))
+	return spanCtx, err
+}
+
+func filterContext(attachments map[string]interface{}) map[string]string {
+	var traceAttchment = make(map[string]string)
+	for k, v := range attachments {
+		if r, ok := v.(string); ok {
+			traceAttchment[k] = r
+		}
+	}
+	return traceAttchment
+}
+
+func fillTraceAttachments(attachments map[string]interface{}, traceAttachment map[string]string) {
+	for k, v := range traceAttachment {
+		attachments[k] = v
+	}
+}
diff --git a/protocol/dubbo/readwriter.go b/protocol/dubbo/readwriter.go
deleted file mode 100644
index adc6311b0ee6b050a5da5ebb1b95472fe0602503..0000000000000000000000000000000000000000
--- a/protocol/dubbo/readwriter.go
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dubbo
-
-import (
-	"bytes"
-	"reflect"
-)
-
-import (
-	"github.com/apache/dubbo-getty"
-	"github.com/apache/dubbo-go-hessian2"
-	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"
-)
-
-////////////////////////////////////////////
-// RpcClientPackageHandler
-////////////////////////////////////////////
-
-// RpcClientPackageHandler handle package for client in getty.
-type RpcClientPackageHandler struct {
-	client *Client
-}
-
-// NewRpcClientPackageHandler create a RpcClientPackageHandler.
-func NewRpcClientPackageHandler(client *Client) *RpcClientPackageHandler {
-	return &RpcClientPackageHandler{client: client}
-}
-
-// Read decode @data to DubboPackage.
-func (p *RpcClientPackageHandler) Read(ss getty.Session, data []byte) (interface{}, int, error) {
-	pkg := &DubboPackage{}
-
-	buf := bytes.NewBuffer(data)
-	err := pkg.Unmarshal(buf, p.client)
-	if err != nil {
-		originErr := perrors.Cause(err)
-		if originErr == hessian.ErrHeaderNotEnough || originErr == hessian.ErrBodyNotEnough {
-			return nil, 0, nil
-		}
-
-		logger.Errorf("pkg.Unmarshal(ss:%+v, len(@data):%d) = error:%+v", ss, len(data), err)
-
-		return nil, 0, perrors.WithStack(err)
-	}
-
-	if pkg.Header.Type&hessian.PackageRequest == 0x00 {
-		pkg.Err = pkg.Body.(*hessian.Response).Exception
-		pkg.Body = NewResponse(pkg.Body.(*hessian.Response).RspObj, pkg.Body.(*hessian.Response).Attachments)
-	}
-
-	return pkg, hessian.HEADER_LENGTH + pkg.Header.BodyLen, nil
-}
-
-// Write encode @pkg.
-func (p *RpcClientPackageHandler) Write(ss getty.Session, pkg interface{}) ([]byte, error) {
-	req, ok := pkg.(*DubboPackage)
-	if !ok {
-		logger.Errorf("illegal pkg:%+v\n", pkg)
-		return nil, perrors.New("invalid rpc request")
-	}
-
-	buf, err := req.Marshal()
-	if err != nil {
-		logger.Warnf("binary.Write(req{%#v}) = err{%#v}", req, perrors.WithStack(err))
-		return nil, perrors.WithStack(err)
-	}
-
-	return buf.Bytes(), nil
-}
-
-////////////////////////////////////////////
-// RpcServerPackageHandler
-////////////////////////////////////////////
-
-var (
-	rpcServerPkgHandler = &RpcServerPackageHandler{}
-)
-
-// RpcServerPackageHandler handle package for server in getty.
-type RpcServerPackageHandler struct{}
-
-// Read decode @data to DubboPackage.
-func (p *RpcServerPackageHandler) Read(ss getty.Session, data []byte) (interface{}, int, error) {
-	pkg := &DubboPackage{
-		Body: make([]interface{}, 7),
-	}
-
-	buf := bytes.NewBuffer(data)
-	err := pkg.Unmarshal(buf)
-	if err != nil {
-		originErr := perrors.Cause(err)
-		if originErr == hessian.ErrHeaderNotEnough || originErr == hessian.ErrBodyNotEnough {
-			return nil, 0, nil
-		}
-
-		logger.Errorf("pkg.Unmarshal(ss:%+v, len(@data):%d) = error:%+v", ss, len(data), err)
-
-		return nil, 0, perrors.WithStack(err)
-	}
-
-	if pkg.Header.Type&hessian.PackageHeartbeat == 0x00 {
-		// convert params of request
-		req := pkg.Body.([]interface{}) // length of body should be 7
-		if len(req) > 0 {
-			var dubboVersion, argsTypes string
-			var args []interface{}
-			var attachments map[string]string
-			if req[0] != nil {
-				dubboVersion = req[0].(string)
-			}
-			if req[1] != nil {
-				pkg.Service.Path = req[1].(string)
-			}
-			if req[2] != nil {
-				pkg.Service.Version = req[2].(string)
-			}
-			if req[3] != nil {
-				pkg.Service.Method = req[3].(string)
-			}
-			if req[4] != nil {
-				argsTypes = req[4].(string)
-			}
-			if req[5] != nil {
-				args = req[5].([]interface{})
-			}
-			if req[6] != nil {
-				attachments = req[6].(map[string]string)
-			}
-			if pkg.Service.Path == "" && len(attachments[constant.PATH_KEY]) > 0 {
-				pkg.Service.Path = attachments[constant.PATH_KEY]
-			}
-			if _, ok := attachments[constant.INTERFACE_KEY]; ok {
-				pkg.Service.Interface = attachments[constant.INTERFACE_KEY]
-			} else {
-				pkg.Service.Interface = pkg.Service.Path
-			}
-			if len(attachments[constant.GROUP_KEY]) > 0 {
-				pkg.Service.Group = attachments[constant.GROUP_KEY]
-			}
-			pkg.Body = map[string]interface{}{
-				"dubboVersion": dubboVersion,
-				"argsTypes":    argsTypes,
-				"args":         args,
-				"service":      common.ServiceMap.GetService(DUBBO, pkg.Service.Path), // path as a key
-				"attachments":  attachments,
-			}
-		}
-	}
-
-	return pkg, hessian.HEADER_LENGTH + pkg.Header.BodyLen, nil
-}
-
-// Write encode @pkg.
-func (p *RpcServerPackageHandler) Write(ss getty.Session, pkg interface{}) ([]byte, error) {
-	res, ok := pkg.(*DubboPackage)
-	if !ok {
-		logger.Errorf("illegal pkg:%+v\n, it is %+v", pkg, reflect.TypeOf(pkg))
-		return nil, perrors.New("invalid rpc response")
-	}
-
-	buf, err := res.Marshal()
-	if err != nil {
-		logger.Warnf("binary.Write(res{%#v}) = err{%#v}", res, perrors.WithStack(err))
-		return nil, perrors.WithStack(err)
-	}
-
-	return buf.Bytes(), nil
-}
diff --git a/protocol/grpc/client.go b/protocol/grpc/client.go
index a0ab0be80cc905115e675c1c4dea2b1c748f6c09..33f0a088dcd6800ce7376faa3d5e05ab5d62596d 100644
--- a/protocol/grpc/client.go
+++ b/protocol/grpc/client.go
@@ -19,6 +19,7 @@ package grpc
 
 import (
 	"reflect"
+	"strconv"
 )
 
 import (
@@ -89,13 +90,17 @@ type Client struct {
 }
 
 // NewClient creates a new gRPC client.
-func NewClient(url common.URL) *Client {
+func NewClient(url *common.URL) *Client {
 	// if global trace instance was set , it means trace function enabled. If not , will return Nooptracer
 	tracer := opentracing.GlobalTracer()
 	dailOpts := make([]grpc.DialOption, 0, 4)
+	maxMessageSize, _ := strconv.Atoi(url.GetParam(constant.MESSAGE_SIZE_KEY, "4"))
 	dailOpts = append(dailOpts, grpc.WithInsecure(), grpc.WithBlock(), grpc.WithUnaryInterceptor(
 		otgrpc.OpenTracingClientInterceptor(tracer, otgrpc.LogPayloads())),
-		grpc.WithDefaultCallOptions(grpc.CallContentSubtype(clientConf.ContentSubType)))
+		grpc.WithDefaultCallOptions(
+			grpc.CallContentSubtype(clientConf.ContentSubType),
+			grpc.MaxCallRecvMsgSize(1024*1024*maxMessageSize),
+			grpc.MaxCallSendMsgSize(1024*1024*maxMessageSize)))
 	conn, err := grpc.Dial(url.Location, dailOpts...)
 	if err != nil {
 		panic(err)
@@ -112,7 +117,7 @@ func NewClient(url common.URL) *Client {
 }
 
 func getInvoker(impl interface{}, conn *grpc.ClientConn) interface{} {
-	in := []reflect.Value{}
+	var in []reflect.Value
 	in = append(in, reflect.ValueOf(conn))
 	method := reflect.ValueOf(impl).MethodByName("GetDubboStub")
 	res := method.Call(in)
diff --git a/protocol/grpc/grpc_exporter.go b/protocol/grpc/grpc_exporter.go
index 0dc764854d61576892800180041c53f0a7735c7c..5beb4fedb1aa0285288d5b2b52515d4f6b9498e8 100644
--- a/protocol/grpc/grpc_exporter.go
+++ b/protocol/grpc/grpc_exporter.go
@@ -42,10 +42,9 @@ func NewGrpcExporter(key string, invoker protocol.Invoker, exporterMap *sync.Map
 
 // Unexport and unregister gRPC service from registry and memory.
 func (gg *GrpcExporter) Unexport() {
-	serviceId := gg.GetInvoker().GetUrl().GetParam(constant.BEAN_NAME_KEY, "")
 	interfaceName := gg.GetInvoker().GetUrl().GetParam(constant.INTERFACE_KEY, "")
 	gg.BaseExporter.Unexport()
-	err := common.ServiceMap.UnRegister(interfaceName, GRPC, serviceId)
+	err := common.ServiceMap.UnRegister(interfaceName, GRPC, gg.GetInvoker().GetUrl().ServiceKey())
 	if err != nil {
 		logger.Errorf("[GrpcExporter.Unexport] error: %v", err)
 	}
diff --git a/protocol/grpc/grpc_invoker.go b/protocol/grpc/grpc_invoker.go
index 737e8c40a063b07e56d7c90f7de04670461ce103..02e7716115e6bb22f3de5a8c4d8a2131995e81c6 100644
--- a/protocol/grpc/grpc_invoker.go
+++ b/protocol/grpc/grpc_invoker.go
@@ -45,7 +45,7 @@ type GrpcInvoker struct {
 }
 
 // NewGrpcInvoker returns a Grpc invoker instance
-func NewGrpcInvoker(url common.URL, client *Client) *GrpcInvoker {
+func NewGrpcInvoker(url *common.URL, client *Client) *GrpcInvoker {
 	return &GrpcInvoker{
 		BaseInvoker: *protocol.NewBaseInvoker(url),
 		client:      client,
@@ -62,7 +62,7 @@ func (gi *GrpcInvoker) Invoke(ctx context.Context, invocation protocol.Invocatio
 		result.Err = errNoReply
 	}
 
-	in := []reflect.Value{}
+	var in []reflect.Value
 	in = append(in, reflect.ValueOf(context.Background()))
 	in = append(in, invocation.ParameterValues()...)
 
diff --git a/protocol/grpc/grpc_protocol.go b/protocol/grpc/grpc_protocol.go
index 68594a4b35921b6e3b1d59d404ed163025d57a81..3ad124532f389d6df5ef746eaca26158b8c42790 100644
--- a/protocol/grpc/grpc_protocol.go
+++ b/protocol/grpc/grpc_protocol.go
@@ -18,11 +18,13 @@
 package grpc
 
 import (
+	"strconv"
 	"sync"
 )
 
 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/protocol"
@@ -65,7 +67,7 @@ func (gp *GrpcProtocol) Export(invoker protocol.Invoker) protocol.Exporter {
 	return exporter
 }
 
-func (gp *GrpcProtocol) openServer(url common.URL) {
+func (gp *GrpcProtocol) openServer(url *common.URL) {
 	_, ok := gp.serverMap[url.Location]
 	if !ok {
 		_, ok := gp.ExporterMap().Load(url.ServiceKey())
@@ -76,7 +78,9 @@ func (gp *GrpcProtocol) openServer(url common.URL) {
 		gp.serverLock.Lock()
 		_, ok = gp.serverMap[url.Location]
 		if !ok {
+			grpcMessageSize, _ := strconv.Atoi(url.GetParam(constant.MESSAGE_SIZE_KEY, "4"))
 			srv := NewServer()
+			srv.SetBufferSize(grpcMessageSize)
 			gp.serverMap[url.Location] = srv
 			srv.Start(url)
 		}
@@ -85,7 +89,7 @@ func (gp *GrpcProtocol) openServer(url common.URL) {
 }
 
 // Refer a remote gRPC service
-func (gp *GrpcProtocol) Refer(url common.URL) protocol.Invoker {
+func (gp *GrpcProtocol) Refer(url *common.URL) protocol.Invoker {
 	invoker := NewGrpcInvoker(url, NewClient(url))
 	gp.SetInvokers(invoker)
 	logger.Infof("Refer service: %s", url.String())
diff --git a/protocol/grpc/protoc-gen-dubbo/plugin/dubbo/dubbo.go b/protocol/grpc/protoc-gen-dubbo/plugin/dubbo/dubbo.go
index 1af4fafdc606783e937ede63f99e5a08f0b2419e..a9f50e82879e1d6448300ec7b2a92e2a68070dc8 100644
--- a/protocol/grpc/protoc-gen-dubbo/plugin/dubbo/dubbo.go
+++ b/protocol/grpc/protoc-gen-dubbo/plugin/dubbo/dubbo.go
@@ -220,7 +220,23 @@ func (g *dubboGrpc) generateService(file *generator.FileDescriptor, service *pb.
 		g.P("},")
 	}
 	g.P("},")
-	g.P("Streams: []", grpcPkg, ".StreamDesc{},")
+	g.P("Streams: []", grpcPkg, ".StreamDesc{")
+	for i, method := range service.Method {
+		if !method.GetClientStreaming() && !method.GetServerStreaming() {
+			continue
+		}
+		g.P("{")
+		g.P("StreamName: ", strconv.Quote(method.GetName()), ",")
+		g.P("Handler: ", handlerNames[i], ",")
+		if method.GetServerStreaming() {
+			g.P("ServerStreams: true,")
+		}
+		if method.GetClientStreaming() {
+			g.P("ClientStreams: true,")
+		}
+		g.P("},")
+	}
+	g.P("},")
 	g.P("Metadata: \"", file.GetName(), "\",")
 	g.P("}")
 	g.P("}")
@@ -241,6 +257,7 @@ func (g *dubboGrpc) generateClientSignature(servName string, method *pb.MethodDe
 	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)
 	}
 	return fmt.Sprintf("%s func(ctx %s.Context%s, %s) error", methName, contextPkg, reqArg, respName)
 }
@@ -252,7 +269,6 @@ func (g *dubboGrpc) generateServerMethod(servName, fullServName string, method *
 	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) {")
@@ -286,6 +302,11 @@ func (g *dubboGrpc) generateServerMethod(servName, fullServName string, method *
 	}
 	streamType := unexport(servName) + methName + "Server"
 	g.P("func ", hname, "(srv interface{}, stream ", grpcPkg, ".ServerStream) error {")
+	g.P("_, ok := srv.(dgrpc.DubboGrpcService)")
+	g.P(`invo := invocation.NewRPCInvocation("`, methName, `", nil, nil)`)
+	g.P("if !ok {")
+	g.P("fmt.Println(invo)")
+	g.P("}")
 	if !method.GetClientStreaming() {
 		g.P("m := new(", inType, ")")
 		g.P("if err := stream.RecvMsg(m); err != nil { return err }")
@@ -296,50 +317,5 @@ func (g *dubboGrpc) generateServerMethod(servName, fullServName string, method *
 	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
index 2b7b1adddf573e3b84db32a11cfc286ff22a0276..e77e2babd0aba81cdcfe2288eeff69195decf9f4 100644
--- a/protocol/grpc/server.go
+++ b/protocol/grpc/server.go
@@ -40,6 +40,7 @@ import (
 // Server is a gRPC server
 type Server struct {
 	grpcServer *grpc.Server
+	bufferSize int
 }
 
 // NewServer creates a new server
@@ -57,8 +58,12 @@ type DubboGrpcService interface {
 	ServiceDesc() *grpc.ServiceDesc
 }
 
+func (s *Server) SetBufferSize(n int) {
+	s.bufferSize = n
+}
+
 // Start gRPC server with @url
-func (s *Server) Start(url common.URL) {
+func (s *Server) Start(url *common.URL) {
 	var (
 		addr string
 		err  error
@@ -72,7 +77,9 @@ func (s *Server) Start(url common.URL) {
 	// if global trace instance was set, then server tracer instance can be get. If not , will return Nooptracer
 	tracer := opentracing.GlobalTracer()
 	server := grpc.NewServer(
-		grpc.UnaryInterceptor(otgrpc.OpenTracingServerInterceptor(tracer)))
+		grpc.UnaryInterceptor(otgrpc.OpenTracingServerInterceptor(tracer)),
+		grpc.MaxRecvMsgSize(1024*1024*s.bufferSize),
+		grpc.MaxSendMsgSize(1024*1024*s.bufferSize))
 
 	key := url.GetParam(constant.BEAN_NAME_KEY, "")
 	service := config.GetProviderService(key)
diff --git a/protocol/invocation.go b/protocol/invocation.go
index 296ec0540c1eed69c30e1b1477be038a4a9cc00e..2ecc817cc91e08fb6be10a0348477943d05bb1e3 100644
--- a/protocol/invocation.go
+++ b/protocol/invocation.go
@@ -25,6 +25,8 @@ import (
 type Invocation interface {
 	// MethodName gets invocation method name.
 	MethodName() string
+	// ParameterTypeNames gets invocation parameter type names.
+	ParameterTypeNames() []string
 	// ParameterTypes gets invocation parameter types.
 	ParameterTypes() []reflect.Type
 	// ParameterValues gets invocation parameter values.
@@ -34,15 +36,16 @@ type Invocation interface {
 	// Reply gets response of request
 	Reply() interface{}
 	// Attachments gets all attachments
-	Attachments() map[string]string
-	// AttachmentsByKey gets attachment by key , if nil then return default value
+	Attachments() map[string]interface{}
+	// AttachmentsByKey gets attachment by key , if nil then return default value. 锛圛t will be deprecated in the future锛�
 	AttachmentsByKey(string, string) string
+	Attachment(string) interface{}
 	// Attributes refers to dubbo 2.7.6.  It is different from attachment. It is used in internal process.
 	Attributes() map[string]interface{}
 	// AttributeByKey gets attribute by key , if nil then return default value
 	AttributeByKey(string, interface{}) interface{}
 	// SetAttachments sets attribute by @key and @value.
-	SetAttachments(key string, value string)
+	SetAttachments(key string, value interface{})
 	// Invoker gets the invoker in current context.
 	Invoker() Invoker
 }
diff --git a/protocol/invocation/rpcinvocation.go b/protocol/invocation/rpcinvocation.go
index c72e105d3594195457a7d7abcccc8badb85c678b..ec68ca38991df1c1eae88f3ea64cca34c6981804 100644
--- a/protocol/invocation/rpcinvocation.go
+++ b/protocol/invocation/rpcinvocation.go
@@ -23,6 +23,8 @@ import (
 )
 
 import (
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/common/constant"
 	"github.com/apache/dubbo-go/protocol"
 )
 
@@ -33,13 +35,16 @@ import (
 // todo: is it necessary to separate fields of consumer(provider) from RPCInvocation
 // nolint
 type RPCInvocation struct {
-	methodName      string
-	parameterTypes  []reflect.Type
+	methodName string
+	// Parameter Type Names. It is used to specify the parameterType
+	parameterTypeNames []string
+	parameterTypes     []reflect.Type
+
 	parameterValues []reflect.Value
 	arguments       []interface{}
 	reply           interface{}
 	callBack        interface{}
-	attachments     map[string]string
+	attachments     map[string]interface{}
 	// Refer to dubbo 2.7.6.  It is different from attachment. It is used in internal process.
 	attributes map[string]interface{}
 	invoker    protocol.Invoker
@@ -47,7 +52,7 @@ type RPCInvocation struct {
 }
 
 // NewRPCInvocation creates a RPC invocation.
-func NewRPCInvocation(methodName string, arguments []interface{}, attachments map[string]string) *RPCInvocation {
+func NewRPCInvocation(methodName string, arguments []interface{}, attachments map[string]interface{}) *RPCInvocation {
 	return &RPCInvocation{
 		methodName:  methodName,
 		arguments:   arguments,
@@ -78,6 +83,11 @@ func (r *RPCInvocation) ParameterTypes() []reflect.Type {
 	return r.parameterTypes
 }
 
+// ParameterTypeNames gets RPC invocation parameter types of string expression.
+func (r *RPCInvocation) ParameterTypeNames() []string {
+	return r.parameterTypeNames
+}
+
 // ParameterValues gets RPC invocation parameter values.
 func (r *RPCInvocation) ParameterValues() []reflect.Value {
 	return r.parameterValues
@@ -99,7 +109,7 @@ func (r *RPCInvocation) SetReply(reply interface{}) {
 }
 
 // Attachments gets all attachments of RPC.
-func (r *RPCInvocation) Attachments() map[string]string {
+func (r *RPCInvocation) Attachments() map[string]interface{} {
 	return r.attachments
 }
 
@@ -112,11 +122,25 @@ func (r *RPCInvocation) AttachmentsByKey(key string, defaultValue string) string
 	}
 	value, ok := r.attachments[key]
 	if ok {
-		return value
+		return value.(string)
 	}
 	return defaultValue
 }
 
+// Attachment returns the corresponding value from dubbo's attachment with the given key.
+func (r *RPCInvocation) Attachment(key string) interface{} {
+	r.lock.RLock()
+	defer r.lock.RUnlock()
+	if r.attachments == nil {
+		return nil
+	}
+	value, ok := r.attachments[key]
+	if ok {
+		return value
+	}
+	return nil
+}
+
 // Attributes gets all attributes of RPC.
 func (r *RPCInvocation) Attributes() map[string]interface{} {
 	return r.attributes
@@ -134,11 +158,11 @@ func (r *RPCInvocation) AttributeByKey(key string, defaultValue interface{}) int
 }
 
 // SetAttachments sets attribute by @key and @value.
-func (r *RPCInvocation) SetAttachments(key string, value string) {
+func (r *RPCInvocation) SetAttachments(key string, value interface{}) {
 	r.lock.Lock()
 	defer r.lock.Unlock()
 	if r.attachments == nil {
-		r.attachments = make(map[string]string)
+		r.attachments = make(map[string]interface{})
 	}
 	r.attachments[key] = value
 }
@@ -172,6 +196,11 @@ func (r *RPCInvocation) SetCallBack(c interface{}) {
 	r.callBack = c
 }
 
+func (r *RPCInvocation) ServiceKey() string {
+	return common.ServiceKey(r.AttachmentsByKey(constant.INTERFACE_KEY, ""),
+		r.AttachmentsByKey(constant.GROUP_KEY, ""), r.AttachmentsByKey(constant.VERSION_KEY, ""))
+}
+
 // /////////////////////////
 // option
 // /////////////////////////
@@ -192,6 +221,18 @@ func WithParameterTypes(parameterTypes []reflect.Type) option {
 	}
 }
 
+// WithParameterTypeNames creates option with @parameterTypeNames.
+func WithParameterTypeNames(parameterTypeNames []string) option {
+	return func(invo *RPCInvocation) {
+		if len(parameterTypeNames) == 0 {
+			return
+		}
+		parameterTypeNamesTmp := make([]string, len(parameterTypeNames))
+		copy(parameterTypeNamesTmp, parameterTypeNames)
+		invo.parameterTypeNames = parameterTypeNamesTmp
+	}
+}
+
 // WithParameterValues creates option with @parameterValues
 func WithParameterValues(parameterValues []reflect.Value) option {
 	return func(invo *RPCInvocation) {
@@ -221,7 +262,7 @@ func WithCallBack(callBack interface{}) option {
 }
 
 // WithAttachments creates option with @attachments.
-func WithAttachments(attachments map[string]string) option {
+func WithAttachments(attachments map[string]interface{}) option {
 	return func(invo *RPCInvocation) {
 		invo.attachments = attachments
 	}
diff --git a/protocol/invoker.go b/protocol/invoker.go
index 3ca370479cbe2255f26628b855b11b07396f1b6d..5657b6b5b2c6d13e001bec2ba3ca7b4f02fc7e60 100644
--- a/protocol/invoker.go
+++ b/protocol/invoker.go
@@ -26,7 +26,7 @@ import (
 	"github.com/apache/dubbo-go/common/logger"
 )
 
-// Invoker ...
+// Invoker the service invocation interface for the consumer
 //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 {
@@ -41,13 +41,13 @@ type Invoker interface {
 
 // BaseInvoker provides default invoker implement
 type BaseInvoker struct {
-	url       common.URL
+	url       *common.URL
 	available bool
 	destroyed bool
 }
 
 // NewBaseInvoker creates a new BaseInvoker
-func NewBaseInvoker(url common.URL) *BaseInvoker {
+func NewBaseInvoker(url *common.URL) *BaseInvoker {
 	return &BaseInvoker{
 		url:       url,
 		available: true,
@@ -56,7 +56,7 @@ func NewBaseInvoker(url common.URL) *BaseInvoker {
 }
 
 // GetUrl gets base invoker URL
-func (bi *BaseInvoker) GetUrl() common.URL {
+func (bi *BaseInvoker) GetUrl() *common.URL {
 	return bi.url
 }
 
diff --git a/protocol/jsonrpc/http.go b/protocol/jsonrpc/http.go
index 2a2ddfeeeb52b865e96ccff69d2b39d8a671ed41..869617ea4eb512df6287d21e9d811145c1944e57 100644
--- a/protocol/jsonrpc/http.go
+++ b/protocol/jsonrpc/http.go
@@ -101,7 +101,7 @@ func NewHTTPClient(opt *HTTPOptions) *HTTPClient {
 }
 
 // NewRequest creates a new HTTP request with @service ,@method and @arguments.
-func (c *HTTPClient) NewRequest(service common.URL, method string, args interface{}) *Request {
+func (c *HTTPClient) NewRequest(service *common.URL, method string, args interface{}) *Request {
 
 	return &Request{
 		ID:       atomic.AddInt64(&c.ID, 1),
@@ -115,7 +115,7 @@ func (c *HTTPClient) NewRequest(service common.URL, method string, args interfac
 }
 
 // Call makes a HTTP call with @ctx , @service ,@req and @rsp
-func (c *HTTPClient) Call(ctx context.Context, service common.URL, req *Request, rsp interface{}) error {
+func (c *HTTPClient) Call(ctx context.Context, service *common.URL, req *Request, rsp interface{}) error {
 	// header
 	httpHeader := http.Header{}
 	httpHeader.Set("Content-Type", "application/json")
diff --git a/protocol/jsonrpc/http_test.go b/protocol/jsonrpc/http_test.go
index 576591940dd3021e7bbd9d2eda0ac5498391a1f7..c4801c8db883353c82762162a7b658f964fc6ffa 100644
--- a/protocol/jsonrpc/http_test.go
+++ b/protocol/jsonrpc/http_test.go
@@ -49,7 +49,7 @@ type (
 )
 
 const (
-	mockJsonCommonUrl = "jsonrpc://127.0.0.1:20001/UserProvider?anyhost=true&" +
+	mockJsonCommonUrl = "jsonrpc://127.0.0.1:20001/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&" +
@@ -58,7 +58,7 @@ const (
 
 func TestHTTPClientCall(t *testing.T) {
 
-	methods, err := common.ServiceMap.Register("com.ikurento.user.UserProvider", "jsonrpc", &UserProvider{})
+	methods, err := common.ServiceMap.Register("com.ikurento.user.UserProvider", "jsonrpc", "", "", &UserProvider{})
 	assert.NoError(t, err)
 	assert.Equal(t, "GetUser,GetUser0,GetUser1,GetUser2,GetUser3,GetUser4", methods)
 
@@ -75,7 +75,7 @@ func TestHTTPClientCall(t *testing.T) {
 
 	// call GetUser
 	ctx := context.WithValue(context.Background(), constant.DUBBOGO_CTX_KEY, map[string]string{
-		"X-Proxy-Id": "dubbogo",
+		"X-Proxy-ID": "dubbogo",
 		"X-Services": url.Path,
 		"X-Method":   "GetUser",
 	})
@@ -89,7 +89,7 @@ func TestHTTPClientCall(t *testing.T) {
 
 	// call GetUser0
 	ctx = context.WithValue(context.Background(), constant.DUBBOGO_CTX_KEY, map[string]string{
-		"X-Proxy-Id": "dubbogo",
+		"X-Proxy-ID": "dubbogo",
 		"X-Services": url.Path,
 		"X-Method":   "GetUser0",
 	})
@@ -102,7 +102,7 @@ func TestHTTPClientCall(t *testing.T) {
 
 	// call GetUser1
 	ctx = context.WithValue(context.Background(), constant.DUBBOGO_CTX_KEY, map[string]string{
-		"X-Proxy-Id": "dubbogo",
+		"X-Proxy-ID": "dubbogo",
 		"X-Services": url.Path,
 		"X-Method":   "GetUser1",
 	})
@@ -114,7 +114,7 @@ func TestHTTPClientCall(t *testing.T) {
 
 	// call GetUser2
 	ctx = context.WithValue(context.Background(), constant.DUBBOGO_CTX_KEY, map[string]string{
-		"X-Proxy-Id": "dubbogo",
+		"X-Proxy-ID": "dubbogo",
 		"X-Services": url.Path,
 		"X-Method":   "GetUser2",
 	})
@@ -126,7 +126,7 @@ func TestHTTPClientCall(t *testing.T) {
 
 	// call GetUser3
 	ctx = context.WithValue(context.Background(), constant.DUBBOGO_CTX_KEY, map[string]string{
-		"X-Proxy-Id": "dubbogo",
+		"X-Proxy-ID": "dubbogo",
 		"X-Services": url.Path,
 		"X-Method":   "GetUser3",
 	})
@@ -138,7 +138,7 @@ func TestHTTPClientCall(t *testing.T) {
 
 	// call GetUser4
 	ctx = context.WithValue(context.Background(), constant.DUBBOGO_CTX_KEY, map[string]string{
-		"X-Proxy-Id": "dubbogo",
+		"X-Proxy-ID": "dubbogo",
 		"X-Services": url.Path,
 		"X-Method":   "GetUser4",
 	})
@@ -149,7 +149,7 @@ func TestHTTPClientCall(t *testing.T) {
 	assert.Equal(t, &User{Id: "", Name: ""}, reply)
 
 	ctx = context.WithValue(context.Background(), constant.DUBBOGO_CTX_KEY, map[string]string{
-		"X-Proxy-Id": "dubbogo",
+		"X-Proxy-ID": "dubbogo",
 		"X-Services": url.Path,
 		"X-Method":   "GetUser4",
 	})
diff --git a/protocol/jsonrpc/jsonrpc_exporter.go b/protocol/jsonrpc/jsonrpc_exporter.go
index 6f75f6aeae9fb1a8d75ded5f558e0ddae84686a0..6b91d266a704d928bf1a308a79044055c9a64328 100644
--- a/protocol/jsonrpc/jsonrpc_exporter.go
+++ b/protocol/jsonrpc/jsonrpc_exporter.go
@@ -42,10 +42,9 @@ func NewJsonrpcExporter(key string, invoker protocol.Invoker, exporterMap *sync.
 
 // Unexport exported JSON RPC service.
 func (je *JsonrpcExporter) Unexport() {
-	serviceId := je.GetInvoker().GetUrl().GetParam(constant.BEAN_NAME_KEY, "")
 	interfaceName := je.GetInvoker().GetUrl().GetParam(constant.INTERFACE_KEY, "")
 	je.BaseExporter.Unexport()
-	err := common.ServiceMap.UnRegister(interfaceName, JSONRPC, serviceId)
+	err := common.ServiceMap.UnRegister(interfaceName, JSONRPC, je.GetInvoker().GetUrl().ServiceKey())
 	if err != nil {
 		logger.Errorf("[JsonrpcExporter.Unexport] error: %v", err)
 	}
diff --git a/protocol/jsonrpc/jsonrpc_invoker.go b/protocol/jsonrpc/jsonrpc_invoker.go
index d84b980216ade6e569e68af31fc90e1ea16b3056..357443f5d4efbe5159fb6c0e09d3dab51266dc73 100644
--- a/protocol/jsonrpc/jsonrpc_invoker.go
+++ b/protocol/jsonrpc/jsonrpc_invoker.go
@@ -36,7 +36,7 @@ type JsonrpcInvoker struct {
 }
 
 // NewJsonrpcInvoker creates JSON RPC invoker with @url and @client
-func NewJsonrpcInvoker(url common.URL, client *HTTPClient) *JsonrpcInvoker {
+func NewJsonrpcInvoker(url *common.URL, client *HTTPClient) *JsonrpcInvoker {
 	return &JsonrpcInvoker{
 		BaseInvoker: *protocol.NewBaseInvoker(url),
 		client:      client,
@@ -45,10 +45,7 @@ func NewJsonrpcInvoker(url common.URL, client *HTTPClient) *JsonrpcInvoker {
 
 // Invoke the JSON RPC invocation and return result.
 func (ji *JsonrpcInvoker) Invoke(ctx context.Context, invocation protocol.Invocation) protocol.Result {
-
-	var (
-		result protocol.RPCResult
-	)
+	var result protocol.RPCResult
 
 	inv := invocation.(*invocation_impl.RPCInvocation)
 	url := ji.GetUrl()
diff --git a/protocol/jsonrpc/jsonrpc_invoker_test.go b/protocol/jsonrpc/jsonrpc_invoker_test.go
index d7124ca07c6ba6d79dc72e7fb6bd98cd4b3a97b2..12a57052eba6e3a2af1b7cfb15e6ab368a7da286 100644
--- a/protocol/jsonrpc/jsonrpc_invoker_test.go
+++ b/protocol/jsonrpc/jsonrpc_invoker_test.go
@@ -36,13 +36,13 @@ import (
 
 func TestJsonrpcInvokerInvoke(t *testing.T) {
 
-	methods, err := common.ServiceMap.Register("UserProvider", "jsonrpc", &UserProvider{})
+	methods, err := common.ServiceMap.Register("com.ikurento.user.UserProvider", "jsonrpc", "", "", &UserProvider{})
 	assert.NoError(t, err)
 	assert.Equal(t, "GetUser,GetUser0,GetUser1,GetUser2,GetUser3,GetUser4", methods)
 
 	// Export
 	proto := GetProtocol()
-	url, err := common.NewURL("jsonrpc://127.0.0.1:20001/UserProvider?anyhost=true&" +
+	url, err := common.NewURL("jsonrpc://127.0.0.1:20001/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&" +
diff --git a/protocol/jsonrpc/jsonrpc_protocol.go b/protocol/jsonrpc/jsonrpc_protocol.go
index 90a6bf5ef7aa017488f723804b22cc613850bcf2..643bcde8cb41a3cb94e97b2d21df26894c574b28 100644
--- a/protocol/jsonrpc/jsonrpc_protocol.go
+++ b/protocol/jsonrpc/jsonrpc_protocol.go
@@ -59,7 +59,7 @@ func NewJsonrpcProtocol() *JsonrpcProtocol {
 	}
 }
 
-// Export JSON RPC service  for remote invocation
+// Export JSON RPC service for remote invocation
 func (jp *JsonrpcProtocol) Export(invoker protocol.Invoker) protocol.Exporter {
 	url := invoker.GetUrl()
 	serviceKey := strings.TrimPrefix(url.Path, "/")
@@ -75,7 +75,7 @@ func (jp *JsonrpcProtocol) Export(invoker protocol.Invoker) protocol.Exporter {
 }
 
 // Refer a remote JSON PRC service from registry
-func (jp *JsonrpcProtocol) Refer(url common.URL) protocol.Invoker {
+func (jp *JsonrpcProtocol) Refer(url *common.URL) protocol.Invoker {
 	//default requestTimeout
 	var requestTimeout = config.GetConsumerConfig().RequestTimeout
 
@@ -106,7 +106,7 @@ func (jp *JsonrpcProtocol) Destroy() {
 	}
 }
 
-func (jp *JsonrpcProtocol) openServer(url common.URL) {
+func (jp *JsonrpcProtocol) openServer(url *common.URL) {
 	_, ok := jp.serverMap[url.Location]
 	if !ok {
 		_, loadOk := jp.ExporterMap().Load(strings.TrimPrefix(url.Path, "/"))
diff --git a/protocol/jsonrpc/server.go b/protocol/jsonrpc/server.go
index aa458a1614df29997b05ac4462200f9e9ffffc25..755aa7da79384842d5a3f3c4364fc991d84b47df 100644
--- a/protocol/jsonrpc/server.go
+++ b/protocol/jsonrpc/server.go
@@ -127,10 +127,10 @@ func (s *Server) handlePkg(conn net.Conn) {
 		}
 
 		reqBody, err := ioutil.ReadAll(r.Body)
+		r.Body.Close()
 		if err != nil {
 			return
 		}
-		r.Body.Close()
 
 		reqHeader := make(map[string]string)
 		for k := range r.Header {
@@ -229,7 +229,7 @@ func accept(listener net.Listener, fn func(net.Conn)) error {
 }
 
 // Start JSON RPC server then ready for accept request.
-func (s *Server) Start(url common.URL) {
+func (s *Server) Start(url *common.URL) {
 	listener, err := net.Listen("tcp", url.Location)
 	if err != nil {
 		logger.Errorf("jsonrpc server [%s] start failed: %v", url.Path, err)
@@ -263,8 +263,7 @@ func (s *Server) Stop() {
 	})
 }
 
-func serveRequest(ctx context.Context,
-	header map[string]string, body []byte, conn net.Conn) error {
+func serveRequest(ctx context.Context, header map[string]string, body []byte, conn net.Conn) error {
 	sendErrorResp := func(header map[string]string, body []byte) error {
 		rsp := &http.Response{
 			Header:        make(http.Header),
@@ -324,13 +323,12 @@ func serveRequest(ctx context.Context,
 		if err == io.EOF || err == io.ErrUnexpectedEOF {
 			return perrors.WithStack(err)
 		}
-
 		return perrors.New("server cannot decode request: " + err.Error())
 	}
+
 	path := header["Path"]
 	methodName := codec.req.Method
 	if len(path) == 0 || len(methodName) == 0 {
-		codec.ReadBody(nil)
 		return perrors.New("service/method request ill-formed: " + path + "/" + methodName)
 	}
 
@@ -345,7 +343,7 @@ func serveRequest(ctx context.Context,
 	exporter, _ := jsonrpcProtocol.ExporterMap().Load(path)
 	invoker := exporter.(*JsonrpcExporter).GetInvoker()
 	if invoker != nil {
-		result := invoker.Invoke(ctx, invocation.NewRPCInvocation(methodName, args, map[string]string{
+		result := invoker.Invoke(ctx, invocation.NewRPCInvocation(methodName, args, map[string]interface{}{
 			constant.PATH_KEY:    path,
 			constant.VERSION_KEY: codec.req.Version}))
 		if err := result.Error(); err != nil {
diff --git a/protocol/mock/mock_invoker.go b/protocol/mock/mock_invoker.go
index 0c88b47e36122dc1a9bc9345d7e93f62cd76f13b..8a0973bd128a74677e8549793b6792e67b26b443 100644
--- a/protocol/mock/mock_invoker.go
+++ b/protocol/mock/mock_invoker.go
@@ -59,9 +59,9 @@ func (m *MockInvoker) EXPECT() *MockInvokerMockRecorder {
 }
 
 // GetUrl mocks base method
-func (m *MockInvoker) GetUrl() common.URL {
+func (m *MockInvoker) GetUrl() *common.URL {
 	ret := m.ctrl.Call(m, "GetUrl")
-	ret0, _ := ret[0].(common.URL)
+	ret0, _ := ret[0].(*common.URL)
 	return ret0
 }
 
diff --git a/protocol/protocol.go b/protocol/protocol.go
index 6bed5ec4c2be82edda7a642eb342381c53387460..d03e70f1ea4acbd1277d4b968be9109fdde187a9 100644
--- a/protocol/protocol.go
+++ b/protocol/protocol.go
@@ -32,7 +32,7 @@ type Protocol interface {
 	// Export service for remote invocation
 	Export(invoker Invoker) Exporter
 	// Refer a remote service
-	Refer(url common.URL) Invoker
+	Refer(url *common.URL) Invoker
 	// Destroy will destroy all invoker and exporter, so it only is called once.
 	Destroy()
 }
@@ -89,7 +89,7 @@ func (bp *BaseProtocol) Export(invoker Invoker) Exporter {
 }
 
 // Refer is default refer implement.
-func (bp *BaseProtocol) Refer(url common.URL) Invoker {
+func (bp *BaseProtocol) Refer(url *common.URL) Invoker {
 	return NewBaseInvoker(url)
 }
 
diff --git a/protocol/protocolwrapper/mock_protocol_filter.go b/protocol/protocolwrapper/mock_protocol_filter.go
index 2e9ffed06f03d196c91f6679df21538612bac405..18a4e1562107939e9c72090778422e0d8bb56a60 100644
--- a/protocol/protocolwrapper/mock_protocol_filter.go
+++ b/protocol/protocolwrapper/mock_protocol_filter.go
@@ -39,7 +39,7 @@ func (pfw *mockProtocolFilter) Export(invoker protocol.Invoker) protocol.Exporte
 }
 
 // Refer a mock remote service
-func (pfw *mockProtocolFilter) Refer(url common.URL) protocol.Invoker {
+func (pfw *mockProtocolFilter) Refer(url *common.URL) protocol.Invoker {
 	return protocol.NewBaseInvoker(url)
 }
 
diff --git a/protocol/protocolwrapper/protocol_filter_wrapper.go b/protocol/protocolwrapper/protocol_filter_wrapper.go
index 87f90d3b7c73328a30e883da8251ac8364a63b2d..79d2cf7f55ee81db39ea28448fc66a8a7494d8d1 100644
--- a/protocol/protocolwrapper/protocol_filter_wrapper.go
+++ b/protocol/protocolwrapper/protocol_filter_wrapper.go
@@ -55,7 +55,7 @@ func (pfw *ProtocolFilterWrapper) Export(invoker protocol.Invoker) protocol.Expo
 }
 
 // Refer a remote service
-func (pfw *ProtocolFilterWrapper) Refer(url common.URL) protocol.Invoker {
+func (pfw *ProtocolFilterWrapper) Refer(url *common.URL) protocol.Invoker {
 	if pfw.protocol == nil {
 		pfw.protocol = extension.GetProtocol(url.Protocol)
 	}
@@ -68,21 +68,19 @@ func (pfw *ProtocolFilterWrapper) Destroy() {
 }
 
 func buildInvokerChain(invoker protocol.Invoker, key string) protocol.Invoker {
-	filtName := invoker.GetUrl().GetParam(key, "")
-	if filtName == "" {
+	filterName := invoker.GetUrl().GetParam(key, "")
+	if filterName == "" {
 		return invoker
 	}
-	filtNames := strings.Split(filtName, ",")
-	next := invoker
+	filterNames := strings.Split(filterName, ",")
 
 	// The order of filters is from left to right, so loading from right to left
-
-	for i := len(filtNames) - 1; i >= 0; i-- {
-		flt := extension.GetFilter(filtNames[i])
+	next := invoker
+	for i := len(filterNames) - 1; i >= 0; i-- {
+		flt := extension.GetFilter(filterNames[i])
 		fi := &FilterInvoker{next: next, invoker: invoker, filter: flt}
 		next = fi
 	}
-
 	return next
 }
 
@@ -103,7 +101,7 @@ type FilterInvoker struct {
 }
 
 // GetUrl is used to get url from FilterInvoker
-func (fi *FilterInvoker) GetUrl() common.URL {
+func (fi *FilterInvoker) GetUrl() *common.URL {
 	return fi.invoker.GetUrl()
 }
 
diff --git a/protocol/protocolwrapper/protocol_filter_wrapper_test.go b/protocol/protocolwrapper/protocol_filter_wrapper_test.go
index b03ea7b9b66d926aff8851f88e1bd7434b254903..8f063f85521bbdcd3fa3891e7d09754fc9b58ac7 100644
--- a/protocol/protocolwrapper/protocol_filter_wrapper_test.go
+++ b/protocol/protocolwrapper/protocol_filter_wrapper_test.go
@@ -43,7 +43,7 @@ func TestProtocolFilterWrapperExport(t *testing.T) {
 	u := common.NewURLWithOptions(
 		common.WithParams(url.Values{}),
 		common.WithParamsValue(constant.SERVICE_FILTER_KEY, "echo"))
-	exporter := filtProto.Export(protocol.NewBaseInvoker(*u))
+	exporter := filtProto.Export(protocol.NewBaseInvoker(u))
 	_, ok := exporter.GetInvoker().(*FilterInvoker)
 	assert.True(t, ok)
 }
@@ -55,7 +55,7 @@ func TestProtocolFilterWrapperRefer(t *testing.T) {
 	u := common.NewURLWithOptions(
 		common.WithParams(url.Values{}),
 		common.WithParamsValue(constant.REFERENCE_FILTER_KEY, "echo"))
-	invoker := filtProto.Refer(*u)
+	invoker := filtProto.Refer(u)
 	_, ok := invoker.(*FilterInvoker)
 	assert.True(t, ok)
 }
diff --git a/protocol/rest/rest_exporter.go b/protocol/rest/rest_exporter.go
index e39558caeae9811817cc26a1717c1b8e3729234c..7a49a2063559c2f4e9b4975bc86ea708abbfb026 100644
--- a/protocol/rest/rest_exporter.go
+++ b/protocol/rest/rest_exporter.go
@@ -42,12 +42,10 @@ func NewRestExporter(key string, invoker protocol.Invoker, exporterMap *sync.Map
 
 // Unexport unexport the RestExporter
 func (re *RestExporter) Unexport() {
-	serviceId := re.GetInvoker().GetUrl().GetParam(constant.BEAN_NAME_KEY, "")
 	interfaceName := re.GetInvoker().GetUrl().GetParam(constant.INTERFACE_KEY, "")
 	re.BaseExporter.Unexport()
-	err := common.ServiceMap.UnRegister(interfaceName, REST, serviceId)
+	err := common.ServiceMap.UnRegister(interfaceName, REST, re.GetInvoker().GetUrl().ServiceKey())
 	if err != nil {
 		logger.Errorf("[RestExporter.Unexport] error: %v", err)
 	}
-	return
 }
diff --git a/protocol/rest/rest_invoker.go b/protocol/rest/rest_invoker.go
index 691beeda4085f316075fe55b9328bc86d6328187..898890e41732d3c874c47cf3b88bba2e201d01d0 100644
--- a/protocol/rest/rest_invoker.go
+++ b/protocol/rest/rest_invoker.go
@@ -43,7 +43,7 @@ type RestInvoker struct {
 }
 
 // NewRestInvoker returns a RestInvoker
-func NewRestInvoker(url common.URL, client *client.RestClient, restMethodConfig map[string]*config.RestMethodConfig) *RestInvoker {
+func NewRestInvoker(url *common.URL, client *client.RestClient, restMethodConfig map[string]*config.RestMethodConfig) *RestInvoker {
 	return &RestInvoker{
 		BaseInvoker:         *protocol.NewBaseInvoker(url),
 		client:              *client,
diff --git a/protocol/rest/rest_invoker_test.go b/protocol/rest/rest_invoker_test.go
index 9df97a211e9d90daa3206b94926ceeac42df4606..18843d2ae4e8dc54683226f2fb0c325e58c7e4ed 100644
--- a/protocol/rest/rest_invoker_test.go
+++ b/protocol/rest/rest_invoker_test.go
@@ -65,7 +65,7 @@ func TestRestInvokerInvoke(t *testing.T) {
 
 	url, err := common.NewURL(mockRestCommonUrl)
 	assert.NoError(t, err)
-	_, err = common.ServiceMap.Register("UserProvider", url.Protocol, &UserProvider{})
+	_, err = common.ServiceMap.Register(url.Service(), url.Protocol, "", "", &UserProvider{})
 	assert.NoError(t, err)
 	con := config.ProviderConfig{}
 	config.SetProviderConfig(con)
@@ -210,6 +210,6 @@ func TestRestInvokerInvoke(t *testing.T) {
 	assert.Error(t, res.Error(), "test error")
 
 	assert.Equal(t, filterNum, 12)
-	err = common.ServiceMap.UnRegister("UserProvider", url.Protocol, "com.ikurento.user.UserProvider")
+	err = common.ServiceMap.UnRegister(url.Service(), url.Protocol, url.ServiceKey())
 	assert.NoError(t, err)
 }
diff --git a/protocol/rest/rest_protocol.go b/protocol/rest/rest_protocol.go
index 0cd26c240a7eb1b83a04ca2f57c332bbda994967..05e119b0540b5f887d53d006e41d3083d1c46de4 100644
--- a/protocol/rest/rest_protocol.go
+++ b/protocol/rest/rest_protocol.go
@@ -86,7 +86,7 @@ func (rp *RestProtocol) Export(invoker protocol.Invoker) protocol.Exporter {
 }
 
 // Refer create rest service reference
-func (rp *RestProtocol) Refer(url common.URL) protocol.Invoker {
+func (rp *RestProtocol) Refer(url *common.URL) protocol.Invoker {
 	// create rest_invoker
 	var requestTimeout = config.GetConsumerConfig().RequestTimeout
 	requestTimeoutStr := url.GetParam(constant.TIMEOUT_KEY, config.GetConsumerConfig().Request_Timeout)
@@ -107,7 +107,7 @@ func (rp *RestProtocol) Refer(url common.URL) protocol.Invoker {
 }
 
 // nolint
-func (rp *RestProtocol) getServer(url common.URL, serverType string) server.RestServer {
+func (rp *RestProtocol) getServer(url *common.URL, serverType string) server.RestServer {
 	restServer, ok := rp.serverMap[url.Location]
 	if ok {
 		return restServer
@@ -149,8 +149,8 @@ func (rp *RestProtocol) getClient(restOptions client.RestOptions, clientType str
 func (rp *RestProtocol) Destroy() {
 	// destroy rest_server
 	rp.BaseProtocol.Destroy()
-	for key, server := range rp.serverMap {
-		server.Destroy()
+	for key, tmpServer := range rp.serverMap {
+		tmpServer.Destroy()
 		delete(rp.serverMap, key)
 	}
 	for key := range rp.clientMap {
diff --git a/protocol/rest/rest_protocol_test.go b/protocol/rest/rest_protocol_test.go
index 9ff4e7df7fe41d7fd028bf476cc2192cf7cefcca..580fc61fd6cfda731184eed6f785b80abc9fccfe 100644
--- a/protocol/rest/rest_protocol_test.go
+++ b/protocol/rest/rest_protocol_test.go
@@ -72,7 +72,7 @@ func TestRestProtocolExport(t *testing.T) {
 	proto := GetRestProtocol()
 	url, err := common.NewURL(mockRestCommonUrl)
 	assert.NoError(t, err)
-	_, err = common.ServiceMap.Register("UserProvider", url.Protocol, &UserProvider{})
+	_, err = common.ServiceMap.Register(url.Service(), url.Protocol, "", "", &UserProvider{})
 	assert.NoError(t, err)
 	con := config.ProviderConfig{}
 	config.SetProviderConfig(con)
@@ -120,8 +120,6 @@ func TestRestProtocolExport(t *testing.T) {
 	proto.Destroy()
 	_, ok = proto.(*RestProtocol).serverMap[url.Location]
 	assert.False(t, ok)
-	err = common.ServiceMap.UnRegister("UserProvider", url.Protocol, "com.ikurento.user.UserProvider")
-	assert.NoError(t, err)
 }
 
 type UserProvider struct {
diff --git a/protocol/rest/server/rest_server.go b/protocol/rest/server/rest_server.go
index fbd6fb7ad9dd81f043e4d45ee94a54e12ef89cdd..3a1cb19396b1c129aa836e228991a22426065f4a 100644
--- a/protocol/rest/server/rest_server.go
+++ b/protocol/rest/server/rest_server.go
@@ -23,7 +23,6 @@ import (
 	"net/http"
 	"reflect"
 	"strconv"
-	"strings"
 )
 
 import (
@@ -43,7 +42,7 @@ const parseParameterErrorStr = "An error occurred while parsing parameters on th
 // RestServer user can implement this server interface
 type RestServer interface {
 	// Start rest server
-	Start(url common.URL)
+	Start(url *common.URL)
 	// Deploy a http api
 	Deploy(restMethodConfig *rest_config.RestMethodConfig, routeFunc func(request RestServerRequest, response RestServerResponse))
 	// UnDeploy a http api
@@ -90,7 +89,7 @@ func GetRouteFunc(invoker protocol.Invoker, methodConfig *rest_config.RestMethod
 			err  error
 			args []interface{}
 		)
-		svc := common.ServiceMap.GetService(invoker.GetUrl().Protocol, strings.TrimPrefix(invoker.GetUrl().Path, "/"))
+		svc := common.ServiceMap.GetServiceByServiceKey(invoker.GetUrl().Protocol, invoker.GetUrl().ServiceKey())
 		// get method
 		method := svc.Method()[methodConfig.MethodName]
 		argsTypes := method.ArgsType()
@@ -111,7 +110,7 @@ func GetRouteFunc(invoker protocol.Invoker, methodConfig *rest_config.RestMethod
 				logger.Errorf("[Go Restful] WriteErrorString error:%v", err)
 			}
 		}
-		result := invoker.Invoke(context.Background(), invocation.NewRPCInvocation(methodConfig.MethodName, args, make(map[string]string)))
+		result := invoker.Invoke(context.Background(), invocation.NewRPCInvocation(methodConfig.MethodName, args, make(map[string]interface{})))
 		if result.Error() != nil {
 			err = resp.WriteError(http.StatusInternalServerError, result.Error())
 			if err != nil {
diff --git a/protocol/rest/server/server_impl/go_restful_server.go b/protocol/rest/server/server_impl/go_restful_server.go
index 6fb9ee8daa7383580b9144ea25954f8ead974dcc..4481f44912d8f1caf6f5250e9c13e90e99537cff 100644
--- a/protocol/rest/server/server_impl/go_restful_server.go
+++ b/protocol/rest/server/server_impl/go_restful_server.go
@@ -59,7 +59,7 @@ func NewGoRestfulServer() server.RestServer {
 
 // Start go-restful server
 // It will add all go-restful filters
-func (grs *GoRestfulServer) Start(url common.URL) {
+func (grs *GoRestfulServer) Start(url *common.URL) {
 	container := restful.NewContainer()
 	for _, filter := range filterSlice {
 		container.Filter(filter)
@@ -99,8 +99,6 @@ func (grs *GoRestfulServer) Deploy(restMethodConfig *config.RestMethodConfig, ro
 
 // Delete a http api in go-restful server
 func (grs *GoRestfulServer) UnDeploy(restMethodConfig *config.RestMethodConfig) {
-	ws := new(restful.WebService)
-	ws.Path(restMethodConfig.Path)
 	err := grs.ws.RemoveRoute(restMethodConfig.Path, restMethodConfig.MethodType)
 	if err != nil {
 		logger.Warnf("[Go restful] Remove web service error:%v", err)
diff --git a/protocol/result.go b/protocol/result.go
index 2a33be612fd1f319c8c46cbd480865d5564b189d..a36b16d1cc56557c2976df5550f5d9c01b88619b 100644
--- a/protocol/result.go
+++ b/protocol/result.go
@@ -28,13 +28,14 @@ type Result interface {
 	// Result gets invoker result.
 	Result() interface{}
 	// SetAttachments replaces the existing attachments with the specified param.
-	SetAttachments(map[string]string)
+	SetAttachments(map[string]interface{})
 	// Attachments gets all attachments
-	Attachments() map[string]string
+	Attachments() map[string]interface{}
+
 	// AddAttachment adds the specified map to existing attachments in this instance.
-	AddAttachment(string, string)
+	AddAttachment(string, interface{})
 	// Attachment gets attachment by key with default value.
-	Attachment(string, string) string
+	Attachment(string, interface{}) interface{}
 }
 
 /////////////////////////////
@@ -43,7 +44,7 @@ type Result interface {
 
 // RPCResult is default RPC result.
 type RPCResult struct {
-	Attrs map[string]string
+	Attrs map[string]interface{}
 	Err   error
 	Rest  interface{}
 }
@@ -69,22 +70,22 @@ func (r *RPCResult) Result() interface{} {
 }
 
 // SetAttachments replaces the existing attachments with the specified param.
-func (r *RPCResult) SetAttachments(attr map[string]string) {
+func (r *RPCResult) SetAttachments(attr map[string]interface{}) {
 	r.Attrs = attr
 }
 
 // Attachments gets all attachments
-func (r *RPCResult) Attachments() map[string]string {
+func (r *RPCResult) Attachments() map[string]interface{} {
 	return r.Attrs
 }
 
 // AddAttachment adds the specified map to existing attachments in this instance.
-func (r *RPCResult) AddAttachment(key, value string) {
+func (r *RPCResult) AddAttachment(key string, value interface{}) {
 	r.Attrs[key] = value
 }
 
 // Attachment gets attachment by key with default value.
-func (r *RPCResult) Attachment(key, defaultValue string) string {
+func (r *RPCResult) Attachment(key string, defaultValue interface{}) interface{} {
 	v, ok := r.Attrs[key]
 	if !ok {
 		v = defaultValue
diff --git a/protocol/rpc_status.go b/protocol/rpc_status.go
index 60becfb34135470b0e69972c25a743f44efe19d5..8d443e84f06b89ed708e080285aee3b054ea02e6 100644
--- a/protocol/rpc_status.go
+++ b/protocol/rpc_status.go
@@ -97,25 +97,26 @@ func (rpc *RPCStatus) GetSuccessiveRequestFailureCount() int32 {
 }
 
 // GetURLStatus get URL RPC status.
-func GetURLStatus(url common.URL) *RPCStatus {
-	rpcStatus, _ := serviceStatistic.LoadOrStore(url.Key(), &RPCStatus{})
+func GetURLStatus(url *common.URL) *RPCStatus {
+	rpcStatus, found := serviceStatistic.Load(url.Key())
+	if !found {
+		rpcStatus, _ = serviceStatistic.LoadOrStore(url.Key(), &RPCStatus{})
+	}
 	return rpcStatus.(*RPCStatus)
 }
 
 // GetMethodStatus get method RPC status.
-func GetMethodStatus(url common.URL, methodName string) *RPCStatus {
+func GetMethodStatus(url *common.URL, methodName string) *RPCStatus {
 	identifier := url.Key()
 	methodMap, found := methodStatistics.Load(identifier)
 	if !found {
-		methodMap = &sync.Map{}
-		methodStatistics.Store(identifier, methodMap)
+		methodMap, _ = methodStatistics.LoadOrStore(identifier, &sync.Map{})
 	}
 
 	methodActive := methodMap.(*sync.Map)
 	rpcStatus, found := methodActive.Load(methodName)
 	if !found {
-		rpcStatus = &RPCStatus{}
-		methodActive.Store(methodName, rpcStatus)
+		rpcStatus, _ = methodActive.LoadOrStore(methodName, &RPCStatus{})
 	}
 
 	status := rpcStatus.(*RPCStatus)
@@ -123,13 +124,13 @@ func GetMethodStatus(url common.URL, methodName string) *RPCStatus {
 }
 
 // BeginCount gets begin count.
-func BeginCount(url common.URL, methodName string) {
+func BeginCount(url *common.URL, methodName string) {
 	beginCount0(GetURLStatus(url))
 	beginCount0(GetMethodStatus(url, methodName))
 }
 
 // EndCount gets end count.
-func EndCount(url common.URL, methodName string, elapsed int64, succeeded bool) {
+func EndCount(url *common.URL, methodName string, elapsed int64, succeeded bool) {
 	endCount0(GetURLStatus(url), elapsed, succeeded)
 	endCount0(GetMethodStatus(url, methodName), elapsed, succeeded)
 }
diff --git a/protocol/rpc_status_test.go b/protocol/rpc_status_test.go
index cc12753cf25007ab6d1010836b203aee22d78ca9..6fd449ce77fada15b32cba936691b51d3c8218ed 100644
--- a/protocol/rpc_status_test.go
+++ b/protocol/rpc_status_test.go
@@ -79,7 +79,7 @@ func TestGetUrlStatus(t *testing.T) {
 	assert.Equal(t, int32(0), status.total)
 }
 
-func TestbeginCount0(t *testing.T) {
+func TestBeginCount0(t *testing.T) {
 	defer CleanAllStatus()
 
 	url, _ := common.NewURL(mockCommonDubboUrl)
@@ -142,7 +142,7 @@ func TestAll(t *testing.T) {
 
 }
 
-func request(url common.URL, method string, elapsed int64, active, succeeded bool) {
+func request(url *common.URL, method string, elapsed int64, active, succeeded bool) {
 	BeginCount(url, method)
 	if !active {
 		EndCount(url, method, elapsed, succeeded)
diff --git a/registry/base_registry.go b/registry/base_registry.go
index ad1a3b61741e003625612ad58409eb8615271a84..a6693be3a862eb208afc216840910c323b8e826f 100644
--- a/registry/base_registry.go
+++ b/registry/base_registry.go
@@ -29,7 +29,6 @@ import (
 )
 
 import (
-	gxnet "github.com/dubbogo/gost/net"
 	perrors "github.com/pkg/errors"
 )
 
@@ -53,7 +52,7 @@ var (
 
 func init() {
 	processID = fmt.Sprintf("%d", os.Getpid())
-	localIP, _ = gxnet.GetLocalIP()
+	localIP = common.GetLocalIp()
 }
 
 type createPathFunc func(dubboPath string) error
@@ -100,8 +99,8 @@ type BaseRegistry struct {
 	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.RWMutex          //ctl lock is a lock for services map
-	services map[string]common.URL // service name + protocol -> service config, for store the service registered
+	cltLock  sync.RWMutex           //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
@@ -109,14 +108,14 @@ func (r *BaseRegistry) InitBaseRegistry(url *common.URL, facadeRegistry FacadeBa
 	r.URL = url
 	r.birth = time.Now().UnixNano()
 	r.done = make(chan struct{})
-	r.services = make(map[string]common.URL)
+	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
+func (r *BaseRegistry) GetUrl() *common.URL {
+	return r.URL
 }
 
 // Destroy for graceful down
@@ -133,7 +132,7 @@ func (r *BaseRegistry) Destroy() {
 }
 
 // Register implement interface registry to register
-func (r *BaseRegistry) Register(conf common.URL) error {
+func (r *BaseRegistry) Register(conf *common.URL) error {
 	var (
 		ok  bool
 		err error
@@ -161,11 +160,11 @@ func (r *BaseRegistry) Register(conf common.URL) error {
 }
 
 // UnRegister implement interface registry to unregister
-func (r *BaseRegistry) UnRegister(conf common.URL) error {
+func (r *BaseRegistry) UnRegister(conf *common.URL) error {
 	var (
 		ok     bool
 		err    error
-		oldURL common.URL
+		oldURL *common.URL
 	)
 
 	func() {
@@ -198,7 +197,7 @@ func (r *BaseRegistry) UnRegister(conf common.URL) error {
 }
 
 // service is for getting service path stored in url
-func (r *BaseRegistry) service(c common.URL) string {
+func (r *BaseRegistry) service(c *common.URL) string {
 	return url.QueryEscape(c.Service())
 }
 
@@ -206,7 +205,7 @@ func (r *BaseRegistry) service(c common.URL) string {
 func (r *BaseRegistry) RestartCallBack() bool {
 
 	// copy r.services
-	services := make([]common.URL, 0, len(r.services))
+	services := make([]*common.URL, 0, len(r.services))
 	for _, confIf := range r.services {
 		services = append(services, confIf)
 	}
@@ -231,16 +230,16 @@ func (r *BaseRegistry) RestartCallBack() bool {
 }
 
 // register for register url to registry, include init params
-func (r *BaseRegistry) register(c common.URL) error {
+func (r *BaseRegistry) register(c *common.URL) error {
 	return r.processURL(c, r.facadeBasedRegistry.DoRegister, r.createPath)
 }
 
 // unregister for unregister url to registry, include init params
-func (r *BaseRegistry) unregister(c common.URL) error {
+func (r *BaseRegistry) unregister(c *common.URL) error {
 	return r.processURL(c, r.facadeBasedRegistry.DoUnregister, nil)
 }
 
-func (r *BaseRegistry) processURL(c common.URL, f func(string, string) error, cpf createPathFunc) error {
+func (r *BaseRegistry) processURL(c *common.URL, f func(string, string) error, cpf createPathFunc) error {
 	if f == nil {
 		panic(" Must provide a `function(string, string) error` to process URL. ")
 	}
@@ -292,7 +291,7 @@ func (r *BaseRegistry) createPath(dubboPath string) error {
 }
 
 // providerRegistry for provider role do
-func (r *BaseRegistry) providerRegistry(c common.URL, params url.Values, f createPathFunc) (string, string, error) {
+func (r *BaseRegistry) providerRegistry(c *common.URL, params url.Values, f createPathFunc) (string, string, error) {
 	var (
 		dubboPath string
 		rawURL    string
@@ -314,7 +313,7 @@ func (r *BaseRegistry) providerRegistry(c common.URL, params url.Values, f creat
 	// 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.
 
-	if len(c.Methods) == 0 {
+	if len(c.Methods) != 0 {
 		params.Add(constant.METHODS_KEY, strings.Join(c.Methods, ","))
 	}
 	logger.Debugf("provider url params:%#v", params)
@@ -326,7 +325,16 @@ func (r *BaseRegistry) providerRegistry(c common.URL, params url.Values, f creat
 	}
 	host += ":" + c.Port
 
-	rawURL = fmt.Sprintf("%s://%s%s?%s", c.Protocol, host, c.Path, params.Encode())
+	//delete empty param key
+	for key, val := range params {
+		if len(val) > 0 && val[0] == "" {
+			params.Del(key)
+		}
+	}
+
+	s, _ := url.QueryUnescape(params.Encode())
+	rawURL = fmt.Sprintf("%s://%s%s?%s", c.Protocol, host, c.Path, s)
+
 	// 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)
@@ -334,7 +342,7 @@ func (r *BaseRegistry) providerRegistry(c common.URL, params url.Values, f creat
 }
 
 // consumerRegistry for consumer role do
-func (r *BaseRegistry) consumerRegistry(c common.URL, params url.Values, f createPathFunc) (string, string, error) {
+func (r *BaseRegistry) consumerRegistry(c *common.URL, params url.Values, f createPathFunc) (string, string, error) {
 	var (
 		dubboPath string
 		rawURL    string
@@ -361,7 +369,8 @@ func (r *BaseRegistry) consumerRegistry(c common.URL, params url.Values, f creat
 	}
 
 	params.Add("protocol", c.Protocol)
-	rawURL = fmt.Sprintf("consumer://%s%s?%s", localIP, c.Path, params.Encode())
+	s, _ := url.QueryUnescape(params.Encode())
+	rawURL = fmt.Sprintf("consumer://%s%s?%s", localIP, c.Path, s)
 	dubboPath = fmt.Sprintf("/dubbo/%s/%s", r.service(c), (common.RoleType(common.CONSUMER)).String())
 
 	logger.Debugf("consumer path:%s, url:%s", dubboPath, rawURL)
diff --git a/registry/consul/listener.go b/registry/consul/listener.go
index b1598345032bddd7358c2263e5fd71b3d4e5545d..0d665afe5a8425bd91a02b74cfa451405974e21a 100644
--- a/registry/consul/listener.go
+++ b/registry/consul/listener.go
@@ -39,17 +39,17 @@ import (
 // registry.
 type consulListener struct {
 	// Registry url.
-	registryUrl common.URL
+	registryUrl *common.URL
 
 	// Consumer url.
-	consumerUrl common.URL
+	consumerUrl *common.URL
 
 	// Consul watcher.
 	plan *watch.Plan
 
 	// Most recent service urls return by
 	// watcher.
-	urls []common.URL
+	urls []*common.URL
 
 	// All service information changes will
 	// be wrapped into ServiceEvent, and be
@@ -78,7 +78,7 @@ type consulListener struct {
 	wg sync.WaitGroup
 }
 
-func newConsulListener(registryUrl common.URL, consumerUrl common.URL) (*consulListener, error) {
+func newConsulListener(registryUrl *common.URL, consumerUrl *common.URL) (*consulListener, error) {
 	params := make(map[string]interface{}, 8)
 	params["type"] = "service"
 	params["service"] = consumerUrl.Service()
@@ -93,7 +93,7 @@ func newConsulListener(registryUrl common.URL, consumerUrl common.URL) (*consulL
 		registryUrl: registryUrl,
 		consumerUrl: consumerUrl,
 		plan:        plan,
-		urls:        make([]common.URL, 0, 8),
+		urls:        make([]*common.URL, 0, 8),
 		eventCh:     make(chan *registry.ServiceEvent, 32),
 		errCh:       make(chan error, 32),
 		done:        make(chan struct{}),
@@ -142,7 +142,7 @@ func (l *consulListener) run() {
 func (l *consulListener) handler(idx uint64, raw interface{}) {
 	var (
 		service *consul.ServiceEntry
-		url     common.URL
+		url     *common.URL
 		ok      bool
 		err     error
 	)
@@ -153,7 +153,7 @@ func (l *consulListener) handler(idx uint64, raw interface{}) {
 		l.errCh <- err
 		return
 	}
-	newUrls := make([]common.URL, 0, 8)
+	newUrls := make([]*common.URL, 0, 8)
 	events := make([]*registry.ServiceEvent, 0, 8)
 
 	for _, service = range services {
diff --git a/registry/consul/registry.go b/registry/consul/registry.go
index c425c5ec20d36be02c00499340f13b13c9aa2655..0b7ba9758952b072d579ae9424e2c385e59a4378 100644
--- a/registry/consul/registry.go
+++ b/registry/consul/registry.go
@@ -36,7 +36,8 @@ import (
 )
 
 const (
-	registryConnDelay = 3
+	registryConnDelay             = 3
+	registryDestroyDefaultTimeout = time.Second * 3
 )
 
 func init() {
@@ -75,7 +76,7 @@ func newConsulRegistry(url *common.URL) (registry.Registry, error) {
 
 // Register register @url
 // it delegate the job to register() method
-func (r *consulRegistry) Register(url common.URL) error {
+func (r *consulRegistry) Register(url *common.URL) error {
 	var err error
 
 	role, _ := strconv.Atoi(r.URL.GetParam(constant.ROLE_KEY, ""))
@@ -89,7 +90,7 @@ func (r *consulRegistry) Register(url common.URL) error {
 }
 
 // register actually register the @url
-func (r *consulRegistry) register(url common.URL) error {
+func (r *consulRegistry) register(url *common.URL) error {
 	service, err := buildService(url)
 	if err != nil {
 		return err
@@ -99,7 +100,7 @@ func (r *consulRegistry) register(url common.URL) error {
 
 // UnRegister unregister the @url
 // it delegate the job to unregister() method
-func (r *consulRegistry) UnRegister(url common.URL) error {
+func (r *consulRegistry) UnRegister(url *common.URL) error {
 	var err error
 
 	role, _ := strconv.Atoi(r.URL.GetParam(constant.ROLE_KEY, ""))
@@ -113,7 +114,7 @@ func (r *consulRegistry) UnRegister(url common.URL) error {
 }
 
 // unregister actually unregister the @url
-func (r *consulRegistry) unregister(url common.URL) error {
+func (r *consulRegistry) unregister(url *common.URL) error {
 	return r.client.Agent().ServiceDeregister(buildId(url))
 }
 
@@ -140,7 +141,7 @@ func (r *consulRegistry) subscribe(url *common.URL, notifyListener registry.Noti
 			return
 		}
 
-		listener, err := r.getListener(*url)
+		listener, err := r.getListener(url)
 		if err != nil {
 			if !r.IsAvailable() {
 				logger.Warnf("event listener game over.")
@@ -165,14 +166,14 @@ func (r *consulRegistry) subscribe(url *common.URL, notifyListener registry.Noti
 	}
 }
 
-func (r *consulRegistry) getListener(url common.URL) (registry.Listener, error) {
-	listener, err := newConsulListener(*r.URL, url)
+func (r *consulRegistry) getListener(url *common.URL) (registry.Listener, error) {
+	listener, err := newConsulListener(r.URL, url)
 	return listener, err
 }
 
 // GetUrl get registry URL of consul registry center
-func (r *consulRegistry) GetUrl() common.URL {
-	return *r.URL
+func (r *consulRegistry) GetUrl() *common.URL {
+	return r.URL
 }
 
 // IsAvailable checks consul registry center whether is available
@@ -187,5 +188,25 @@ func (r *consulRegistry) IsAvailable() bool {
 
 // Destroy consul registry center
 func (r *consulRegistry) Destroy() {
+	if r.URL != nil {
+		done := make(chan struct{}, 1)
+		go func() {
+			defer func() {
+				if e := recover(); e != nil {
+					logger.Errorf("consulRegistry destory with panic: %v", e)
+				}
+				done <- struct{}{}
+			}()
+			if err := r.UnRegister(r.URL); err != nil {
+				logger.Errorf("consul registry unregister with err: %s", err.Error())
+			}
+		}()
+		select {
+		case <-done:
+			logger.Infof("consulRegistry unregister done")
+		case <-time.After(registryDestroyDefaultTimeout):
+			logger.Errorf("consul unregister timeout")
+		}
+	}
 	close(r.done)
 }
diff --git a/registry/consul/registry_test.go b/registry/consul/registry_test.go
index 94718f5ab657c198882f065a50e5d5a2c9d4bc6f..b300f7536ddf35f2a4d062900b1f3e4eda33f25d 100644
--- a/registry/consul/registry_test.go
+++ b/registry/consul/registry_test.go
@@ -55,3 +55,19 @@ func (suite *consulRegistryTestSuite) testSubscribe() {
 	assert.NoError(suite.t, err)
 	suite.listener = listener
 }
+
+func (suite *consulRegistryTestSuite) testDestroy() {
+	consumerRegistryUrl := newConsumerRegistryUrl(registryHost, registryPort)
+	consumerRegistry, _ := newConsulRegistry(consumerRegistryUrl)
+	consulRegistryImp := consumerRegistry.(*consulRegistry)
+	assert.True(suite.t, consulRegistryImp.IsAvailable())
+	consulRegistryImp.Destroy()
+	assert.False(suite.t, consulRegistryImp.IsAvailable())
+
+	consumerRegistry, _ = newConsulRegistry(consumerRegistryUrl)
+	consulRegistryImp = consumerRegistry.(*consulRegistry)
+	consulRegistryImp.URL = nil
+	assert.True(suite.t, consulRegistryImp.IsAvailable())
+	consulRegistryImp.Destroy()
+	assert.False(suite.t, consulRegistryImp.IsAvailable())
+}
diff --git a/registry/consul/service_discovery.go b/registry/consul/service_discovery.go
new file mode 100644
index 0000000000000000000000000000000000000000..d8ab93f31ee6fdcf79aa869a35548e8192841fe4
--- /dev/null
+++ b/registry/consul/service_discovery.go
@@ -0,0 +1,498 @@
+/*
+ * 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 consul
+
+import (
+	"encoding/base64"
+	"fmt"
+	"strconv"
+	"sync"
+	"time"
+)
+
+import (
+	"github.com/dubbogo/gost/container/set"
+	"github.com/dubbogo/gost/page"
+	consul "github.com/hashicorp/consul/api"
+	"github.com/hashicorp/consul/api/watch"
+	perrors "github.com/pkg/errors"
+)
+
+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/registry"
+)
+
+const (
+	enable                 = "enable"
+	watch_type             = "type"
+	watch_type_service     = "service"
+	watch_service          = "service"
+	watch_passingonly      = "passingonly"
+	watch_passingonly_true = true
+)
+
+var (
+	errConsulClientClosed = perrors.New("consul client is closed")
+)
+
+// init will put the service discovery into extension
+func init() {
+	extension.SetServiceDiscovery(constant.CONSUL_KEY, newConsulServiceDiscovery)
+}
+
+// consulServiceDiscovery is the implementation of service discovery based on consul.
+type consulServiceDiscovery struct {
+	// descriptor is a short string about the basic information of this instance
+	descriptor string
+	clientLock sync.RWMutex
+	// Consul client.
+	consulClient                   *consul.Client
+	checkPassInterval              int64
+	tag                            string
+	address                        string
+	deregisterCriticalServiceAfter string
+	ttl                            sync.Map
+	*consul.Config
+}
+
+// newConsulServiceDiscovery will create new service discovery instance
+// use double-check pattern to reduce race condition
+func newConsulServiceDiscovery(name string) (registry.ServiceDiscovery, error) {
+	sdc, ok := config.GetBaseConfig().GetServiceDiscoveries(name)
+	if !ok || len(sdc.RemoteRef) == 0 {
+		return nil, perrors.New("could not init the instance because the config is invalid")
+	}
+
+	remoteConfig, ok := config.GetBaseConfig().GetRemoteConfig(sdc.RemoteRef)
+	if !ok {
+		return nil, perrors.New("could not find the remote config for name: " + sdc.RemoteRef)
+	}
+
+	descriptor := fmt.Sprintf("consul-service-discovery[%s]", remoteConfig.Address)
+
+	config := &consul.Config{Address: remoteConfig.Address, Token: remoteConfig.Params[constant.ACL_TOKEN]}
+	client, err := consul.NewClient(config)
+	if err != nil {
+		return nil, perrors.WithMessage(err, "create consul client failed.")
+	}
+
+	return &consulServiceDiscovery{
+		address:                        remoteConfig.Address,
+		descriptor:                     descriptor,
+		checkPassInterval:              getCheckPassInterval(remoteConfig.Params),
+		Config:                         config,
+		tag:                            remoteConfig.Params[constant.QUERY_TAG],
+		consulClient:                   client,
+		deregisterCriticalServiceAfter: getDeregisterAfter(remoteConfig.Params),
+		clientLock:                     sync.RWMutex{},
+	}, nil
+}
+
+func (csd *consulServiceDiscovery) String() string {
+	return csd.descriptor
+}
+
+// nolint
+func (csd *consulServiceDiscovery) getConsulClient() *consul.Client {
+	csd.clientLock.RLock()
+	defer csd.clientLock.RUnlock()
+	return csd.consulClient
+}
+
+// nolint
+func (csd *consulServiceDiscovery) setConsulClient(consulClient *consul.Client) {
+	csd.clientLock.Lock()
+	defer csd.clientLock.Unlock()
+	csd.consulClient = consulClient
+}
+
+func (csd *consulServiceDiscovery) Destroy() error {
+	csd.setConsulClient(nil)
+	csd.ttl.Range(func(key, t interface{}) bool {
+		close(t.(chan struct{}))
+		csd.ttl.Delete(key)
+		return true
+	})
+	return nil
+}
+
+func (csd *consulServiceDiscovery) Register(instance registry.ServiceInstance) error {
+	var (
+		err          error
+		consulClient *consul.Client
+	)
+	ins, _ := csd.buildRegisterInstance(instance)
+	if consulClient = csd.getConsulClient(); consulClient == nil {
+		return errConsulClientClosed
+	}
+	err = consulClient.Agent().ServiceRegister(ins)
+	if err != nil {
+		logger.Errorf("consul register the instance %s fail:%v", instance.GetServiceName(), err)
+		return perrors.WithMessage(err, "consul could not register the instance. "+instance.GetServiceName())
+	}
+
+	return csd.registerTtl(instance)
+}
+
+func (csd *consulServiceDiscovery) registerTtl(instance registry.ServiceInstance) error {
+	var (
+		err          error
+		consulClient *consul.Client
+	)
+
+	checkID := buildID(instance)
+
+	stopChan := make(chan struct{})
+	csd.ttl.LoadOrStore(buildID(instance), stopChan)
+
+	period := time.Duration(csd.checkPassInterval/8) * time.Millisecond
+	timer := time.NewTicker(period)
+	go func() {
+		defer timer.Stop()
+		for {
+			select {
+			case <-timer.C:
+				if consulClient = csd.getConsulClient(); consulClient == nil {
+					logger.Debugf("consul client is closed!")
+					return
+				}
+				err = consulClient.Agent().PassTTL(fmt.Sprintf("service:%s", checkID), "")
+				if err != nil {
+					logger.Warnf("pass ttl heartbeat fail:%v", err)
+					break
+				}
+				logger.Debugf("passed ttl heartbeat for %s", checkID)
+				break
+			case <-stopChan:
+				logger.Info("ttl %s for service %s is stopped", checkID, instance.GetServiceName())
+				return
+			}
+		}
+	}()
+	return nil
+}
+
+func (csd *consulServiceDiscovery) Update(instance registry.ServiceInstance) error {
+	var (
+		err          error
+		consulClient *consul.Client
+	)
+	ins, _ := csd.buildRegisterInstance(instance)
+	consulClient = csd.getConsulClient()
+	if consulClient == nil {
+		return errConsulClientClosed
+	}
+	err = consulClient.Agent().ServiceDeregister(buildID(instance))
+	if err != nil {
+		logger.Warnf("unregister instance %s fail:%v", instance.GetServiceName(), err)
+	}
+	return consulClient.Agent().ServiceRegister(ins)
+}
+
+func (csd *consulServiceDiscovery) Unregister(instance registry.ServiceInstance) error {
+	var (
+		err          error
+		consulClient *consul.Client
+	)
+	if consulClient = csd.getConsulClient(); consulClient == nil {
+		return errConsulClientClosed
+	}
+	err = consulClient.Agent().ServiceDeregister(buildID(instance))
+	if err != nil {
+		logger.Errorf("unregister service instance %s,error: %v", instance.GetId(), err)
+		return err
+	}
+	stopChanel, ok := csd.ttl.Load(buildID(instance))
+	if !ok {
+		logger.Warnf("ttl for service instance %s didn't exist", instance.GetId())
+		return nil
+	}
+	close(stopChanel.(chan struct{}))
+	csd.ttl.Delete(buildID(instance))
+	return nil
+}
+
+func (csd *consulServiceDiscovery) GetDefaultPageSize() int {
+	return registry.DefaultPageSize
+}
+
+func (csd *consulServiceDiscovery) GetServices() *gxset.HashSet {
+	var (
+		err          error
+		consulClient *consul.Client
+		services     map[string][]string
+	)
+	var res = gxset.NewSet()
+	if consulClient = csd.getConsulClient(); consulClient == nil {
+		logger.Warnf("consul client is closed!")
+		return res
+	}
+	services, _, err = consulClient.Catalog().Services(nil)
+	if err != nil {
+		logger.Errorf("get services,error: %v", err)
+		return res
+	}
+
+	for service, _ := range services {
+		res.Add(service)
+	}
+	return res
+
+}
+
+// encodeConsulMetadata because consul validate key strictly.
+func encodeConsulMetadata(metadata map[string]string) map[string]string {
+	consulMetadata := make(map[string]string, len(metadata))
+	encoder := base64.RawStdEncoding
+	for k, v := range metadata {
+		consulMetadata[encoder.EncodeToString([]byte(k))] = v
+	}
+	return consulMetadata
+}
+
+// nolint
+func decodeConsulMetadata(metadata map[string]string) map[string]string {
+	dubboMetadata := make(map[string]string, len(metadata))
+	encoder := base64.RawStdEncoding
+	for k, v := range metadata {
+		kBytes, err := encoder.DecodeString(k)
+		if err != nil {
+			logger.Warnf("can not decoded consul metadata key %s", k)
+			continue
+		}
+		dubboMetadata[string(kBytes)] = v
+	}
+	return dubboMetadata
+}
+
+func (csd *consulServiceDiscovery) GetInstances(serviceName string) []registry.ServiceInstance {
+	var (
+		err          error
+		consulClient *consul.Client
+		instances    []*consul.ServiceEntry
+	)
+	if consulClient = csd.getConsulClient(); consulClient == nil {
+		logger.Warn("consul client is closed!")
+		return nil
+	}
+	instances, _, err = consulClient.Health().Service(serviceName, csd.tag, true, &consul.QueryOptions{
+		WaitTime: time.Duration(csd.checkPassInterval),
+	})
+
+	if err != nil {
+		logger.Errorf("get instances for service %s,error: %v", serviceName, err)
+		return nil
+	}
+
+	res := make([]registry.ServiceInstance, 0, len(instances))
+	for _, ins := range instances {
+		metadata := ins.Service.Meta
+
+		// enable status
+		enableStr := metadata[enable]
+		delete(metadata, enable)
+		enable, _ := strconv.ParseBool(enableStr)
+		metadata = decodeConsulMetadata(metadata)
+
+		// health status
+		status := ins.Checks.AggregatedStatus()
+		healthy := false
+		if status == consul.HealthPassing {
+			healthy = true
+		}
+		res = append(res, &registry.DefaultServiceInstance{
+			Id:          ins.Service.ID,
+			ServiceName: ins.Service.Service,
+			Host:        ins.Service.Address,
+			Port:        ins.Service.Port,
+			Enable:      enable,
+			Healthy:     healthy,
+			Metadata:    metadata,
+		})
+	}
+
+	return res
+}
+
+func (csd *consulServiceDiscovery) GetInstancesByPage(serviceName string, offset int, pageSize int) gxpage.Pager {
+	all := csd.GetInstances(serviceName)
+	res := make([]interface{}, 0, pageSize)
+	for i := offset; i < len(all) && i < offset+pageSize; i++ {
+		res = append(res, all[i])
+	}
+	return gxpage.New(offset, pageSize, res, len(all))
+}
+
+func (csd *consulServiceDiscovery) GetHealthyInstancesByPage(serviceName string, offset int, pageSize int, healthy bool) gxpage.Pager {
+	all := csd.GetInstances(serviceName)
+	res := make([]interface{}, 0, pageSize)
+	// could not use res = all[a:b] here because the res should be []interface{}, not []ServiceInstance
+	var (
+		i     = offset
+		count = 0
+	)
+	for i < len(all) && count < pageSize {
+		ins := all[i]
+		if ins.IsHealthy() == healthy {
+			res = append(res, all[i])
+			count++
+		}
+		i++
+	}
+	return gxpage.New(offset, pageSize, res, len(all))
+}
+
+func (csd *consulServiceDiscovery) GetRequestInstances(serviceNames []string, offset int, requestedSize int) map[string]gxpage.Pager {
+	res := make(map[string]gxpage.Pager, len(serviceNames))
+	for _, name := range serviceNames {
+		res[name] = csd.GetInstancesByPage(name, offset, requestedSize)
+	}
+	return res
+}
+
+func (csd *consulServiceDiscovery) AddListener(listener *registry.ServiceInstancesChangedListener) error {
+
+	params := make(map[string]interface{}, 8)
+	params[watch_type] = watch_type_service
+	params[watch_service] = listener.ServiceName
+	params[watch_passingonly] = watch_passingonly_true
+	plan, err := watch.Parse(params)
+	if err != nil {
+		logger.Errorf("add listener for service %s,error:%v", listener.ServiceName, err)
+		return err
+	}
+
+	plan.Handler = func(idx uint64, raw interface{}) {
+		services, ok := raw.([]*consul.ServiceEntry)
+		if !ok {
+			err = perrors.New("handler get non ServiceEntry type parameter")
+			return
+		}
+		instances := make([]registry.ServiceInstance, 0, len(services))
+		for _, ins := range services {
+			metadata := ins.Service.Meta
+
+			// enable status
+			enableStr := metadata[enable]
+			delete(metadata, enable)
+			enable, _ := strconv.ParseBool(enableStr)
+
+			// health status
+			status := ins.Checks.AggregatedStatus()
+			healthy := false
+			if status == consul.HealthPassing {
+				healthy = true
+			}
+			instances = append(instances, &registry.DefaultServiceInstance{
+				Id:          ins.Service.ID,
+				ServiceName: ins.Service.Service,
+				Host:        ins.Service.Address,
+				Port:        ins.Service.Port,
+				Enable:      enable,
+				Healthy:     healthy,
+				Metadata:    metadata,
+			})
+		}
+		e := csd.DispatchEventForInstances(listener.ServiceName, instances)
+		if e != nil {
+			logger.Errorf("Dispatching event got exception, service name: %s, err: %v", listener.ServiceName, err)
+		}
+	}
+	go func() {
+		err = plan.RunWithConfig(csd.Config.Address, csd.Config)
+		if err != nil {
+			logger.Error("consul plan run failure!error:%v", err)
+		}
+	}()
+	return nil
+}
+
+func (csd *consulServiceDiscovery) DispatchEventByServiceName(serviceName string) error {
+	return csd.DispatchEventForInstances(serviceName, csd.GetInstances(serviceName))
+}
+
+func (csd *consulServiceDiscovery) DispatchEventForInstances(serviceName string, instances []registry.ServiceInstance) error {
+	return csd.DispatchEvent(registry.NewServiceInstancesChangedEvent(serviceName, instances))
+}
+
+func (csd *consulServiceDiscovery) DispatchEvent(event *registry.ServiceInstancesChangedEvent) error {
+	extension.GetGlobalDispatcher().Dispatch(event)
+	return nil
+}
+
+func (csd *consulServiceDiscovery) buildRegisterInstance(instance registry.ServiceInstance) (*consul.AgentServiceRegistration, error) {
+	metadata := instance.GetMetadata()
+	metadata = encodeConsulMetadata(metadata)
+	metadata[enable] = strconv.FormatBool(instance.IsEnable())
+	// check
+	check := csd.buildCheck(instance)
+
+	return &consul.AgentServiceRegistration{
+		ID:      buildID(instance),
+		Name:    instance.GetServiceName(),
+		Port:    instance.GetPort(),
+		Address: instance.GetHost(),
+		Meta:    metadata,
+		Check:   &check,
+	}, nil
+}
+
+func (csd *consulServiceDiscovery) buildCheck(instance registry.ServiceInstance) consul.AgentServiceCheck {
+
+	deregister, ok := instance.GetMetadata()[constant.DEREGISTER_AFTER]
+	if !ok || len(deregister) == 0 {
+		deregister = constant.DEFAULT_DEREGISTER_TIME
+	}
+	return consul.AgentServiceCheck{
+		TTL:                            strconv.FormatInt(csd.checkPassInterval/1000, 10) + "s",
+		DeregisterCriticalServiceAfter: csd.deregisterCriticalServiceAfter,
+	}
+}
+
+// nolint
+func getCheckPassInterval(params map[string]string) int64 {
+	checkPassIntervalStr, ok := params[constant.CHECK_PASS_INTERVAL]
+	if !ok {
+		return constant.DEFAULT_CHECK_PASS_INTERVAL
+	}
+	checkPassInterval, err := strconv.ParseInt(checkPassIntervalStr, 10, 64)
+	if err != nil {
+		logger.Warnf("consul service discovery remote config error:%s", checkPassIntervalStr)
+		return constant.DEFAULT_CHECK_PASS_INTERVAL
+	}
+	return checkPassInterval
+}
+
+// nolint
+func getDeregisterAfter(metadata map[string]string) string {
+	deregister, ok := metadata[constant.DEREGISTER_AFTER]
+	if !ok || len(deregister) == 0 {
+		deregister = constant.DEFAULT_DEREGISTER_TIME
+	}
+	return deregister
+}
+
+// nolint
+func buildID(instance registry.ServiceInstance) string {
+	id := fmt.Sprintf("id:%s,serviceName:%s,host:%s,port:%d", instance.GetId(), instance.GetServiceName(), instance.GetHost(), instance.GetPort())
+	return id
+}
diff --git a/registry/consul/service_discovery_test.go b/registry/consul/service_discovery_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..2169857ee8f79a92322234a0b17a4d7122a0d975
--- /dev/null
+++ b/registry/consul/service_discovery_test.go
@@ -0,0 +1,230 @@
+/*
+ * 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 consul
+
+import (
+	"fmt"
+	"math/rand"
+	"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/common/extension"
+	"github.com/apache/dubbo-go/common/observer"
+	"github.com/apache/dubbo-go/config"
+	"github.com/apache/dubbo-go/registry"
+	"github.com/apache/dubbo-go/remoting/consul"
+)
+
+var (
+	testName                             = "test"
+	consulCheckPassInterval              = 17000
+	consulDeregisterCriticalServiceAfter = "20s"
+	consulWatchTimeout                   = 60000
+)
+
+func TestConsulServiceDiscovery_newConsulServiceDiscovery(t *testing.T) {
+	name := "consul1"
+	_, err := newConsulServiceDiscovery(name)
+	assert.NotNil(t, err)
+
+	sdc := &config.ServiceDiscoveryConfig{
+		Protocol:  "consul",
+		RemoteRef: "mock",
+	}
+
+	config.GetBaseConfig().ServiceDiscoveries[name] = sdc
+
+	_, err = newConsulServiceDiscovery(name)
+	assert.NotNil(t, err)
+
+	config.GetBaseConfig().Remotes["mock"] = &config.RemoteConfig{
+		Address: "localhost:8081",
+	}
+
+	res, err := newConsulServiceDiscovery(name)
+	assert.Nil(t, err)
+	assert.NotNil(t, res)
+}
+
+func TestConsulServiceDiscovery_Destroy(t *testing.T) {
+	prepareData()
+	serviceDiscovery, err := extension.GetServiceDiscovery(constant.CONSUL_KEY, testName)
+	prepareService()
+	assert.Nil(t, err)
+	assert.NotNil(t, serviceDiscovery)
+	err = serviceDiscovery.Destroy()
+	assert.Nil(t, err)
+	assert.Nil(t, serviceDiscovery.(*consulServiceDiscovery).consulClient)
+}
+
+func TestConsulServiceDiscovery_CRUD(t *testing.T) {
+	// start consul agent
+	consulAgent := consul.NewConsulAgent(t, registryPort)
+	defer consulAgent.Shutdown()
+
+	prepareData()
+	var eventDispatcher = MockEventDispatcher{Notify: make(chan struct{}, 1)}
+	extension.SetEventDispatcher("mock", func() observer.EventDispatcher {
+		return &eventDispatcher
+	})
+
+	extension.SetAndInitGlobalDispatcher("mock")
+	rand.Seed(time.Now().Unix())
+
+	instance, _ := prepareService()
+
+	// clean data
+	serviceDiscovery, err := extension.GetServiceDiscovery(constant.CONSUL_KEY, testName)
+	assert.Nil(t, err)
+
+	err = serviceDiscovery.Unregister(instance)
+	assert.Nil(t, err)
+
+	err = serviceDiscovery.Register(instance)
+	assert.Nil(t, err)
+
+	//sometimes nacos may be failed to push update of instance,
+	//so it need 10s to pull, we sleep 10 second to make sure instance has been update
+	time.Sleep(3 * time.Second)
+	page := serviceDiscovery.GetHealthyInstancesByPage(instance.GetServiceName(), 0, 10, true)
+	assert.NotNil(t, page)
+	assert.Equal(t, 0, page.GetOffset())
+	assert.Equal(t, 10, page.GetPageSize())
+	assert.Equal(t, 1, page.GetDataSize())
+
+	instanceResult := page.GetData()[0].(*registry.DefaultServiceInstance)
+	assert.NotNil(t, instanceResult)
+	assert.Equal(t, buildID(instance), instanceResult.GetId())
+	assert.Equal(t, instance.GetHost(), instanceResult.GetHost())
+	assert.Equal(t, instance.GetPort(), instanceResult.GetPort())
+	assert.Equal(t, instance.GetServiceName(), instanceResult.GetServiceName())
+	metadata := instanceResult.GetMetadata()
+	assert.Equal(t, 0, len(metadata))
+
+	instance.GetMetadata()["aaa"] = "bbb"
+	err = serviceDiscovery.Update(instance)
+	assert.Nil(t, err)
+
+	time.Sleep(3 * time.Second)
+	pageMap := serviceDiscovery.GetRequestInstances([]string{instance.GetServiceName()}, 0, 1)
+	assert.Equal(t, 1, len(pageMap))
+
+	page = pageMap[instance.GetServiceName()]
+	assert.NotNil(t, page)
+	assert.Equal(t, 1, len(page.GetData()))
+
+	instanceResult = page.GetData()[0].(*registry.DefaultServiceInstance)
+	v, _ := instanceResult.Metadata["aaa"]
+	assert.Equal(t, "bbb", v)
+
+	// test dispatcher event
+	//err = serviceDiscovery.DispatchEventByServiceName(instanceResult.GetServiceName())
+	//assert.Nil(t, err)
+
+	// test AddListener
+	err = serviceDiscovery.AddListener(&registry.ServiceInstancesChangedListener{ServiceName: instance.GetServiceName()})
+	assert.Nil(t, err)
+	err = serviceDiscovery.Unregister(instance)
+	assert.Nil(t, err)
+	timer := time.NewTimer(time.Second * 10)
+	select {
+	case <-eventDispatcher.Notify:
+		assert.NotNil(t, eventDispatcher.Event)
+		break
+	case <-timer.C:
+		assert.Fail(t, "")
+		break
+	}
+}
+
+func prepareData() {
+	config.GetBaseConfig().ServiceDiscoveries[testName] = &config.ServiceDiscoveryConfig{
+		Protocol:  "consul",
+		RemoteRef: testName,
+	}
+
+	config.GetBaseConfig().Remotes[testName] = &config.RemoteConfig{
+		Address: fmt.Sprintf("%s:%d", registryHost, registryPort),
+	}
+}
+
+func prepareService() (registry.ServiceInstance, *common.URL) {
+	id := "id"
+
+	registryUrl, _ := common.NewURL(protocol + "://" + providerHost + ":" + strconv.Itoa(providerPort) + "/" + service + "?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&consul-check-pass-interval=" + strconv.Itoa(consulCheckPassInterval) + "&consul-deregister-critical-service-after=" + consulDeregisterCriticalServiceAfter + "&" +
+		"consul-watch-timeout=" + strconv.Itoa(consulWatchTimeout))
+
+	return &registry.DefaultServiceInstance{
+		Id:          id,
+		ServiceName: service,
+		Host:        registryHost,
+		Port:        registryPort,
+		Enable:      true,
+		Healthy:     true,
+		Metadata:    nil,
+	}, registryUrl
+}
+
+type MockEventDispatcher struct {
+	Notify chan struct{}
+	Event  observer.Event
+}
+
+// AddEventListener do nothing
+func (m *MockEventDispatcher) AddEventListener(observer.EventListener) {
+}
+
+// AddEventListeners do nothing
+func (m *MockEventDispatcher) AddEventListeners([]observer.EventListener) {
+}
+
+// RemoveEventListener do nothing
+func (m *MockEventDispatcher) RemoveEventListener(observer.EventListener) {
+}
+
+// RemoveEventListeners do nothing
+func (m *MockEventDispatcher) RemoveEventListeners([]observer.EventListener) {
+}
+
+// GetAllEventListeners return empty list
+func (m *MockEventDispatcher) GetAllEventListeners() []observer.EventListener {
+	return make([]observer.EventListener, 0)
+}
+
+// RemoveAllEventListeners do nothing
+func (m *MockEventDispatcher) RemoveAllEventListeners() {
+}
+
+// Dispatch do nothing
+func (m *MockEventDispatcher) Dispatch(event observer.Event) {
+	m.Event = event
+	m.Notify <- struct{}{}
+}
diff --git a/registry/consul/utils.go b/registry/consul/utils.go
index 05ac5e76e292a2b574bd5e661bbbcca4f419fc22..76f5d2dd3378f67d300d10b4c5ecc72934ef7715 100644
--- a/registry/consul/utils.go
+++ b/registry/consul/utils.go
@@ -25,7 +25,6 @@ import (
 )
 
 import (
-	gxnet "github.com/dubbogo/gost/net"
 	consul "github.com/hashicorp/consul/api"
 	perrors "github.com/pkg/errors"
 )
@@ -34,12 +33,12 @@ import (
 	"github.com/apache/dubbo-go/common"
 )
 
-func buildId(url common.URL) string {
+func buildId(url *common.URL) string {
 	t := md5.Sum([]byte(url.String()))
 	return hex.EncodeToString(t[:])
 }
 
-func buildService(url common.URL) (*consul.AgentServiceRegistration, error) {
+func buildService(url *common.URL) (*consul.AgentServiceRegistration, error) {
 	var err error
 
 	// id
@@ -47,7 +46,7 @@ func buildService(url common.URL) (*consul.AgentServiceRegistration, error) {
 
 	// address
 	if url.Ip == "" {
-		url.Ip, _ = gxnet.GetLocalIP()
+		url.Ip = common.GetLocalIp()
 	}
 
 	// port
@@ -94,21 +93,21 @@ func buildService(url common.URL) (*consul.AgentServiceRegistration, error) {
 	return service, nil
 }
 
-func retrieveURL(service *consul.ServiceEntry) (common.URL, error) {
+func retrieveURL(service *consul.ServiceEntry) (*common.URL, error) {
 	url, ok := service.Service.Meta["url"]
 	if !ok {
-		return common.URL{}, perrors.New("retrieve url fails with no url key in service meta")
+		return nil, perrors.New("retrieve url fails with no url key in service meta")
 	}
 	url1, err := common.NewURL(url)
 	if err != nil {
-		return common.URL{}, perrors.WithStack(err)
+		return nil, perrors.WithStack(err)
 	}
 	return url1, nil
 }
 
-func in(url common.URL, urls []common.URL) bool {
+func in(url *common.URL, urls []*common.URL) bool {
 	for _, url1 := range urls {
-		if url.URLEqual(url1) {
+		if common.IsEquals(url, url1) {
 			return true
 		}
 	}
diff --git a/registry/consul/utils_test.go b/registry/consul/utils_test.go
index 939352dc088faa2c32be8173d0aa6f4516dfe503..d78c534e931f9aa3e0220bb08aa29222220ce619 100644
--- a/registry/consul/utils_test.go
+++ b/registry/consul/utils_test.go
@@ -63,8 +63,8 @@ func newConsumerRegistryUrl(host string, port int) *common.URL {
 	)
 }
 
-func newProviderUrl(host string, port int, service string, protocol string) common.URL {
-	return *common.NewURLWithOptions(
+func newProviderUrl(host string, port int, service string, protocol string) *common.URL {
+	return common.NewURLWithOptions(
 		common.WithIp(host),
 		common.WithPort(strconv.Itoa(port)),
 		common.WithPath(service),
@@ -72,8 +72,8 @@ func newProviderUrl(host string, port int, service string, protocol string) comm
 	)
 }
 
-func newConsumerUrl(host string, port int, service string, protocol string) common.URL {
-	return *common.NewURLWithOptions(
+func newConsumerUrl(host string, port int, service string, protocol string) *common.URL {
+	return common.NewURLWithOptions(
 		common.WithIp(host),
 		common.WithPort(strconv.Itoa(port)),
 		common.WithPath(service),
@@ -130,8 +130,8 @@ type consulRegistryTestSuite struct {
 	providerRegistry registry.Registry
 	consumerRegistry *consulRegistry
 	listener         registry.Listener
-	providerUrl      common.URL
-	consumerUrl      common.URL
+	providerUrl      *common.URL
+	consumerUrl      *common.URL
 }
 
 func newConsulRegistryTestSuite(t *testing.T) *consulRegistryTestSuite {
@@ -163,6 +163,7 @@ func test1(t *testing.T) {
 	suite.testListener(remoting.EventTypeAdd)
 	suite.testUnregister()
 	suite.testListener(remoting.EventTypeDel)
+	suite.testDestroy()
 }
 
 // subscribe -> register -> unregister
@@ -183,6 +184,7 @@ func test2(t *testing.T) {
 	suite.testListener(remoting.EventTypeAdd)
 	suite.testUnregister()
 	suite.testListener(remoting.EventTypeDel)
+	suite.testDestroy()
 }
 
 func TestConsulRegistry(t *testing.T) {
diff --git a/registry/directory/directory.go b/registry/directory/directory.go
index 2fbf9410f76c473362964c9ef148e3c581d3d045..ed1d8fb3cd310f4c0fd67d3d80f2c8ffe931ffaa 100644
--- a/registry/directory/directory.go
+++ b/registry/directory/directory.go
@@ -18,6 +18,9 @@
 package directory
 
 import (
+	"fmt"
+	"net/url"
+	"os"
 	"sync"
 )
 
@@ -29,6 +32,7 @@ import (
 import (
 	"github.com/apache/dubbo-go/cluster"
 	"github.com/apache/dubbo-go/cluster/directory"
+	"github.com/apache/dubbo-go/cluster/router/chain"
 	"github.com/apache/dubbo-go/common"
 	"github.com/apache/dubbo-go/common/constant"
 	"github.com/apache/dubbo-go/common/extension"
@@ -55,12 +59,14 @@ type RegistryDirectory struct {
 	serviceType                    string
 	registry                       registry.Registry
 	cacheInvokersMap               *sync.Map // use sync.map
+	consumerURL                    *common.URL
 	cacheOriginUrl                 *common.URL
 	configurators                  []config_center.Configurator
 	consumerConfigurationListener  *consumerConfigurationListener
 	referenceConfigurationListener *referenceConfigurationListener
 	serviceKey                     string
 	forbidden                      atomic.Bool
+	registerLock                   sync.Mutex // this lock if for register
 }
 
 // NewRegistryDirectory will create a new RegistryDirectory
@@ -68,6 +74,7 @@ func NewRegistryDirectory(url *common.URL, registry registry.Registry) (cluster.
 	if url.SubURL == nil {
 		return nil, perrors.Errorf("url is invalid, suburl can not be nil")
 	}
+	logger.Debugf("new RegistryDirectory for service :%s.", url.Key())
 	dir := &RegistryDirectory{
 		BaseDirectory:    directory.NewBaseDirectory(url),
 		cacheInvokers:    []protocol.Invoker{},
@@ -75,6 +82,15 @@ func NewRegistryDirectory(url *common.URL, registry registry.Registry) (cluster.
 		serviceType:      url.SubURL.Service(),
 		registry:         registry,
 	}
+
+	dir.consumerURL = dir.getConsumerUrl(url.SubURL)
+
+	if routerChain, err := chain.NewRouterChain(dir.consumerURL); err == nil {
+		dir.BaseDirectory.SetRouterChain(routerChain)
+	} else {
+		logger.Warnf("fail to create router chain with url: %s, err is: %v", url.SubURL, err)
+	}
+
 	dir.consumerConfigurationListener = newConsumerConfigurationListener(dir)
 
 	go dir.subscribe(url.SubURL)
@@ -83,6 +99,7 @@ func NewRegistryDirectory(url *common.URL, registry registry.Registry) (cluster.
 
 // subscribe from registry
 func (dir *RegistryDirectory) subscribe(url *common.URL) {
+	logger.Debugf("subscribe service :%s for RegistryDirectory.", url.Key())
 	dir.consumerConfigurationListener.addNotifyListener(dir)
 	dir.referenceConfigurationListener = newReferenceConfigurationListener(dir, url)
 	dir.registry.Subscribe(url, dir)
@@ -90,68 +107,177 @@ func (dir *RegistryDirectory) subscribe(url *common.URL) {
 
 // Notify monitor changes from registry,and update the cacheServices
 func (dir *RegistryDirectory) Notify(event *registry.ServiceEvent) {
-	go dir.update(event)
+	if event == nil {
+		return
+	}
+	go dir.refreshInvokers(event)
 }
 
-// update the cacheServices and subscribe service from registry
-func (dir *RegistryDirectory) update(res *registry.ServiceEvent) {
-	if res == nil {
-		return
+// NotifyAll notify the events that are complete Service Event List.
+// After notify the address, the callback func will be invoked.
+func (dir *RegistryDirectory) NotifyAll(events []*registry.ServiceEvent, callback func()) {
+	go dir.refreshAllInvokers(events, callback)
+}
+
+// refreshInvokers refreshes service's events.
+func (dir *RegistryDirectory) refreshInvokers(event *registry.ServiceEvent) {
+	if event != nil {
+		logger.Debugf("refresh invokers with %+v", event)
+	} else {
+		logger.Debug("refresh invokers with nil")
+	}
+
+	var oldInvoker protocol.Invoker
+	if event != nil {
+		oldInvoker, _ = dir.cacheInvokerByEvent(event)
+	}
+	dir.setNewInvokers()
+	if oldInvoker != nil {
+		oldInvoker.Destroy()
 	}
-	logger.Debugf("registry update, result{%s}", res)
-	logger.Debugf("update service name: %s!", res.Service)
-	dir.refreshInvokers(res)
 }
 
-func (dir *RegistryDirectory) refreshInvokers(res *registry.ServiceEvent) {
+// refreshAllInvokers the argument is the complete list of the service events,  we can safely assume any cached invoker
+// not in the incoming list can be removed.  The Action of serviceEvent should be EventTypeUpdate.
+func (dir *RegistryDirectory) refreshAllInvokers(events []*registry.ServiceEvent, callback func()) {
 	var (
-		url        *common.URL
-		oldInvoker protocol.Invoker = nil
+		oldInvokers []protocol.Invoker
+		addEvents   []*registry.ServiceEvent
 	)
-	// judge is override or others
-	if res != nil {
-		url = &res.Service
-		// 1.for override url in 2.6.x
-		if url.Protocol == constant.OVERRIDE_PROTOCOL ||
-			url.GetParam(constant.CATEGORY_KEY, constant.DEFAULT_CATEGORY) == constant.CONFIGURATORS_CATEGORY {
-			dir.configurators = append(dir.configurators, extension.GetDefaultConfigurator(url))
-			url = nil
-		} else if url.Protocol == constant.ROUTER_PROTOCOL || // 2.for router
-			url.GetParam(constant.CATEGORY_KEY, constant.DEFAULT_CATEGORY) == constant.ROUTER_CATEGORY {
-			url = nil
+	dir.overrideUrl(dir.GetDirectoryUrl())
+	referenceUrl := dir.GetDirectoryUrl().SubURL
 
+	// loop the events to check the Action should be EventTypeUpdate.
+	for _, event := range events {
+		if event.Action != remoting.EventTypeUpdate {
+			panic("Your implements of register center is wrong, " +
+				"please check the Action of ServiceEvent should be EventTypeUpdate")
+			return
 		}
-		switch res.Action {
-		case remoting.EventTypeAdd, remoting.EventTypeUpdate:
-			logger.Infof("selector add service url{%s}", res.Service)
-
-			var urls []*common.URL
-			for _, v := range config.GetRouterURLSet().Values() {
-				urls = append(urls, v.(*common.URL))
+		// Originally it will Merge URL many times, now we just execute once.
+		// MergeUrl is executed once and put the result into Event. After this, the key will get from Event.Key().
+		newUrl := dir.convertUrl(event)
+		newUrl = common.MergeUrl(newUrl, referenceUrl)
+		dir.overrideUrl(newUrl)
+		event.Update(newUrl)
+	}
+	// After notify all addresses, do some callback.
+	defer callback()
+	func() {
+		// this lock is work at batch update of InvokeCache
+		dir.registerLock.Lock()
+		defer dir.registerLock.Unlock()
+		// get need clear invokers from original invoker list
+		dir.cacheInvokersMap.Range(func(k, v interface{}) bool {
+			if !dir.eventMatched(k.(string), events) {
+				// delete unused invoker from cache
+				if invoker := dir.uncacheInvokerWithKey(k.(string)); invoker != nil {
+					oldInvokers = append(oldInvokers, invoker)
+				}
 			}
-
-			if len(urls) > 0 {
-				dir.SetRouters(urls)
+			return true
+		})
+		// get need add invokers from events
+		for _, event := range events {
+			// Get the key from Event.Key()
+			if _, ok := dir.cacheInvokersMap.Load(event.Key()); !ok {
+				addEvents = append(addEvents, event)
 			}
-			oldInvoker = dir.cacheInvoker(url)
-		case remoting.EventTypeDel:
-			oldInvoker = dir.uncacheInvoker(url)
-			logger.Infof("selector delete service url{%s}", res.Service)
-		default:
-			return
 		}
+		// loop the updateEvents
+		for _, event := range addEvents {
+			logger.Debugf("registry update, result{%s}", event)
+			logger.Infof("selector add service url{%s}", event.Service)
+			// FIXME: routers are built in every address notification?
+			dir.configRouters()
+			if oldInvoker, _ := dir.doCacheInvoker(event.Service); oldInvoker != nil {
+				oldInvokers = append(oldInvokers, oldInvoker)
+			}
+		}
+	}()
+	dir.setNewInvokers()
+	// destroy unused invokers
+	for _, invoker := range oldInvokers {
+		go invoker.Destroy()
+	}
+}
+
+// eventMatched checks if a cached invoker appears in the incoming invoker list, if no, then it is safe to remove.
+func (dir *RegistryDirectory) eventMatched(key string, events []*registry.ServiceEvent) bool {
+	for _, event := range events {
+		if dir.invokerCacheKey(event) == key {
+			return true
+		}
+	}
+	return false
+}
+
+// invokerCacheKey generates the key in the cache for a given ServiceEvent.
+func (dir *RegistryDirectory) invokerCacheKey(event *registry.ServiceEvent) string {
+	// If the url is merged, then return Event.Key() directly.
+	if event.Updated() {
+		return event.Key()
 	}
+	referenceUrl := dir.GetDirectoryUrl().SubURL
+	newUrl := common.MergeUrl(event.Service, referenceUrl)
+	event.Update(newUrl)
+	return newUrl.Key()
+}
 
+// setNewInvokers groups the invokers from the cache first, then set the result to both directory and router chain.
+func (dir *RegistryDirectory) setNewInvokers() {
 	newInvokers := dir.toGroupInvokers()
 	dir.listenerLock.Lock()
+	defer dir.listenerLock.Unlock()
 	dir.cacheInvokers = newInvokers
-	dir.listenerLock.Unlock()
-	// After dir.cacheInvokers is updated,destroy the oldInvoker
-	// Ensure that no request will enter the oldInvoker
-	if oldInvoker != nil {
-		oldInvoker.Destroy()
+	dir.RouterChain().SetInvokers(newInvokers)
+}
+
+// cacheInvokerByEvent caches invokers from the service event
+func (dir *RegistryDirectory) cacheInvokerByEvent(event *registry.ServiceEvent) (protocol.Invoker, error) {
+	// judge is override or others
+	if event != nil {
+		u := dir.convertUrl(event)
+		switch event.Action {
+		case remoting.EventTypeAdd, remoting.EventTypeUpdate:
+			logger.Infof("selector add service url{%s}", event.Service)
+			// FIXME: routers are built in every address notification?
+			dir.configRouters()
+			return dir.cacheInvoker(u), nil
+		case remoting.EventTypeDel:
+			logger.Infof("selector delete service url{%s}", event.Service)
+			return dir.uncacheInvoker(u), nil
+		default:
+			return nil, fmt.Errorf("illegal event type: %v", event.Action)
+		}
+	}
+	return nil, nil
+}
+
+// configRouters configures dynamic routers into the router chain, but, the current impl is incorrect, see FIXME above.
+func (dir *RegistryDirectory) configRouters() {
+	var urls []*common.URL
+	for _, v := range config.GetRouterURLSet().Values() {
+		urls = append(urls, v.(*common.URL))
 	}
 
+	if len(urls) > 0 {
+		dir.SetRouters(urls)
+	}
+}
+
+// convertUrl processes override:// and router://
+func (dir *RegistryDirectory) convertUrl(res *registry.ServiceEvent) *common.URL {
+	ret := res.Service
+	if ret.Protocol == constant.OVERRIDE_PROTOCOL || // 1.for override url in 2.6.x
+		ret.GetParam(constant.CATEGORY_KEY, constant.DEFAULT_CATEGORY) == constant.CONFIGURATORS_CATEGORY {
+		dir.configurators = append(dir.configurators, extension.GetDefaultConfigurator(ret))
+		ret = nil
+	} else if ret.Protocol == constant.ROUTER_PROTOCOL || // 2.for router
+		ret.GetParam(constant.CATEGORY_KEY, constant.DEFAULT_CATEGORY) == constant.ROUTER_CATEGORY {
+		ret = nil
+	}
+	return ret
 }
 
 func (dir *RegistryDirectory) toGroupInvokers() []protocol.Invoker {
@@ -197,11 +323,15 @@ func (dir *RegistryDirectory) toGroupInvokers() []protocol.Invoker {
 	return groupInvokersList
 }
 
-// uncacheInvoker will return abandoned Invoker,if no Invoker to be abandoned,return nil
+// uncacheInvoker will return abandoned Invoker, if no Invoker to be abandoned, return nil
 func (dir *RegistryDirectory) uncacheInvoker(url *common.URL) protocol.Invoker {
-	logger.Debugf("service will be deleted in cache invokers: invokers key is  %s!", url.Key())
-	if cacheInvoker, ok := dir.cacheInvokersMap.Load(url.Key()); ok {
-		dir.cacheInvokersMap.Delete(url.Key())
+	return dir.uncacheInvokerWithKey(url.Key())
+}
+
+func (dir *RegistryDirectory) uncacheInvokerWithKey(key string) protocol.Invoker {
+	logger.Debugf("service will be deleted in cache invokers: invokers key is  %s!", key)
+	if cacheInvoker, ok := dir.cacheInvokersMap.Load(key); ok {
+		dir.cacheInvokersMap.Delete(key)
 		return cacheInvoker.(protocol.Invoker)
 	}
 	return nil
@@ -225,24 +355,38 @@ func (dir *RegistryDirectory) cacheInvoker(url *common.URL) protocol.Invoker {
 	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.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.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)
-				return cacheInvoker.(protocol.Invoker)
-			}
+		if v, ok := dir.doCacheInvoker(newUrl); ok {
+			return v
 		}
 	}
 	return nil
 }
 
+func (dir *RegistryDirectory) doCacheInvoker(newUrl *common.URL) (protocol.Invoker, bool) {
+	key := newUrl.Key()
+	if cacheInvoker, ok := dir.cacheInvokersMap.Load(key); !ok {
+		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(key, newInvoker)
+		}
+	} else {
+		// if cached invoker has the same URL with the new URL, then no need to re-refer, and no need to destroy
+		// the old invoker.
+		if common.GetCompareURLEqualFunc()(newUrl, cacheInvoker.(protocol.Invoker).GetUrl()) {
+			return nil, true
+		}
+
+		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(key, newInvoker)
+			return cacheInvoker.(protocol.Invoker), true
+		}
+	}
+	return nil, false
+}
+
 // List selected protocol invokers from the directory
 func (dir *RegistryDirectory) List(invocation protocol.Invocation) []protocol.Invoker {
 	invokers := dir.cacheInvokers
@@ -251,7 +395,7 @@ func (dir *RegistryDirectory) List(invocation protocol.Invocation) []protocol.In
 	if routerChain == nil {
 		return invokers
 	}
-	return routerChain.Route(invokers, dir.cacheOriginUrl, invocation)
+	return routerChain.Route(dir.consumerURL, invocation)
 }
 
 // IsAvailable  whether the directory is available
@@ -287,6 +431,24 @@ func (dir *RegistryDirectory) overrideUrl(targetUrl *common.URL) {
 	doOverrideUrl(dir.referenceConfigurationListener.Configurators(), targetUrl)
 }
 
+func (dir *RegistryDirectory) getConsumerUrl(c *common.URL) *common.URL {
+	processID := fmt.Sprintf("%d", os.Getpid())
+	localIP := common.GetLocalIp()
+
+	params := url.Values{}
+	c.RangeParams(func(key, value string) bool {
+		params.Add(key, value)
+		return true
+	})
+
+	params.Add("pid", processID)
+	params.Add("ip", localIP)
+	params.Add("protocol", c.Protocol)
+
+	return common.NewURLWithOptions(common.WithProtocol("consumer"), common.WithIp(localIP), common.WithPath(c.Path),
+		common.WithParams(params))
+}
+
 func doOverrideUrl(configurators []config_center.Configurator, targetUrl *common.URL) {
 	for _, v := range configurators {
 		v.Configure(targetUrl)
@@ -312,6 +474,7 @@ func newReferenceConfigurationListener(dir *RegistryDirectory, url *common.URL)
 // Process handle events and update Invokers
 func (l *referenceConfigurationListener) Process(event *config_center.ConfigChangeEvent) {
 	l.BaseConfigurationListener.Process(event)
+	// FIXME: this doesn't trigger dir.overrideUrl()
 	l.directory.refreshInvokers(nil)
 }
 
@@ -338,5 +501,6 @@ func (l *consumerConfigurationListener) addNotifyListener(listener registry.Noti
 // Process handles events from Configuration Center and update Invokers
 func (l *consumerConfigurationListener) Process(event *config_center.ConfigChangeEvent) {
 	l.BaseConfigurationListener.Process(event)
+	// FIXME: this doesn't trigger dir.overrideUrl()
 	l.directory.refreshInvokers(nil)
 }
diff --git a/registry/directory/directory_test.go b/registry/directory/directory_test.go
index f2b2f8edd2d46950d2e74733b1d869e0de282ec0..b5d81eb0da0b7ae635b8a54e3002a9380a69a2fd 100644
--- a/registry/directory/directory_test.go
+++ b/registry/directory/directory_test.go
@@ -72,7 +72,7 @@ func TestSubscribe(t *testing.T) {
 func TestSubscribe_InvalidUrl(t *testing.T) {
 	url, _ := common.NewURL("mock://127.0.0.1:1111")
 	mockRegistry, _ := registry.NewMockRegistry(&common.URL{})
-	_, err := NewRegistryDirectory(&url, mockRegistry)
+	_, err := NewRegistryDirectory(url, mockRegistry)
 	assert.Error(t, err)
 }
 
@@ -83,9 +83,9 @@ func TestSubscribe_Group(t *testing.T) {
 	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
+	regurl.SubURL = suburl
 	mockRegistry, _ := registry.NewMockRegistry(&common.URL{})
-	dir, _ := NewRegistryDirectory(&regurl, mockRegistry)
+	dir, _ := NewRegistryDirectory(regurl, mockRegistry)
 
 	go dir.(*RegistryDirectory).subscribe(common.NewURLWithOptions(common.WithPath("testservice")))
 	//for group1
@@ -93,7 +93,7 @@ func TestSubscribe_Group(t *testing.T) {
 	urlmap.Set(constant.GROUP_KEY, "group1")
 	urlmap.Set(constant.CLUSTER_KEY, "failover") //to test merge url
 	for i := 0; i < 3; i++ {
-		mockRegistry.(*registry.MockRegistry).MockEvent(&registry.ServiceEvent{Action: remoting.EventTypeAdd, Service: *common.NewURLWithOptions(common.WithPath("TEST"+strconv.FormatInt(int64(i), 10)), common.WithProtocol("dubbo"),
+		mockRegistry.(*registry.MockRegistry).MockEvent(&registry.ServiceEvent{Action: remoting.EventTypeAdd, Service: common.NewURLWithOptions(common.WithPath("TEST"+strconv.FormatInt(int64(i), 10)), common.WithProtocol("dubbo"),
 			common.WithParams(urlmap))})
 	}
 	//for group2
@@ -101,7 +101,7 @@ func TestSubscribe_Group(t *testing.T) {
 	urlmap2.Set(constant.GROUP_KEY, "group2")
 	urlmap2.Set(constant.CLUSTER_KEY, "failover") //to test merge url
 	for i := 0; i < 3; i++ {
-		mockRegistry.(*registry.MockRegistry).MockEvent(&registry.ServiceEvent{Action: remoting.EventTypeAdd, Service: *common.NewURLWithOptions(common.WithPath("TEST"+strconv.FormatInt(int64(i), 10)), common.WithProtocol("dubbo"),
+		mockRegistry.(*registry.MockRegistry).MockEvent(&registry.ServiceEvent{Action: remoting.EventTypeAdd, Service: common.NewURLWithOptions(common.WithPath("TEST"+strconv.FormatInt(int64(i), 10)), common.WithProtocol("dubbo"),
 			common.WithParams(urlmap2))})
 	}
 
@@ -124,7 +124,7 @@ func Test_Destroy(t *testing.T) {
 func Test_List(t *testing.T) {
 	registryDirectory, _ := normalRegistryDir()
 
-	time.Sleep(4e9)
+	time.Sleep(6e9)
 	assert.Len(t, registryDirectory.List(&invocation.RPCInvocation{}), 3)
 	assert.Equal(t, true, registryDirectory.IsAvailable())
 
@@ -192,6 +192,34 @@ func Test_toGroupInvokers(t *testing.T) {
 	assert.Len(t, groupInvokers, 2)
 }
 
+func Test_RefreshUrl(t *testing.T) {
+	registryDirectory, mockRegistry := normalRegistryDir()
+	providerUrl, _ := common.NewURL("dubbo://0.0.0.0:20011/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"))
+	providerUrl2, _ := common.NewURL("dubbo://0.0.0.0:20012/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"))
+	time.Sleep(1e9)
+	assert.Len(t, registryDirectory.cacheInvokers, 3)
+	mockRegistry.MockEvent(&registry.ServiceEvent{Action: remoting.EventTypeAdd, Service: providerUrl})
+	time.Sleep(1e9)
+	assert.Len(t, registryDirectory.cacheInvokers, 4)
+	mockRegistry.MockEvents([]*registry.ServiceEvent{&registry.ServiceEvent{Action: remoting.EventTypeUpdate, Service: providerUrl}})
+	time.Sleep(1e9)
+	assert.Len(t, registryDirectory.cacheInvokers, 1)
+	mockRegistry.MockEvents([]*registry.ServiceEvent{&registry.ServiceEvent{Action: remoting.EventTypeUpdate, Service: providerUrl},
+		&registry.ServiceEvent{Action: remoting.EventTypeUpdate, Service: providerUrl2}})
+	time.Sleep(1e9)
+	assert.Len(t, registryDirectory.cacheInvokers, 2)
+	// clear all address
+	mockRegistry.MockEvents([]*registry.ServiceEvent{})
+	time.Sleep(1e9)
+	assert.Len(t, registryDirectory.cacheInvokers, 0)
+}
+
 func normalRegistryDir(noMockEvent ...bool) (*RegistryDirectory, *registry.MockRegistry) {
 	extension.SetProtocol(protocolwrapper.FILTER, protocolwrapper.NewMockProtocolFilter)
 
@@ -202,17 +230,17 @@ func normalRegistryDir(noMockEvent ...bool) (*RegistryDirectory, *registry.MockR
 		common.WithParamsValue(constant.GROUP_KEY, "group"),
 		common.WithParamsValue(constant.VERSION_KEY, "1.0.0"),
 	)
-	url.SubURL = &suburl
+	url.SubURL = suburl
 	mockRegistry, _ := registry.NewMockRegistry(&common.URL{})
-	dir, _ := NewRegistryDirectory(&url, mockRegistry)
+	dir, _ := NewRegistryDirectory(url, mockRegistry)
 
-	go dir.(*RegistryDirectory).subscribe(&suburl)
+	go dir.(*RegistryDirectory).subscribe(suburl)
 	if len(noMockEvent) == 0 {
 		for i := 0; i < 3; i++ {
 			mockRegistry.(*registry.MockRegistry).MockEvent(
 				&registry.ServiceEvent{
 					Action: remoting.EventTypeAdd,
-					Service: *common.NewURLWithOptions(
+					Service: common.NewURLWithOptions(
 						common.WithPath("TEST"+strconv.FormatInt(int64(i), 10)),
 						common.WithProtocol("dubbo"),
 					),
diff --git a/registry/etcdv3/listener.go b/registry/etcdv3/listener.go
index 436b6eca5b0dfb8514cbc21a47032490f7b1c21f..4bc387cafaa3c64539efb29a181a2198fbe8a30f 100644
--- a/registry/etcdv3/listener.go
+++ b/registry/etcdv3/listener.go
@@ -64,7 +64,7 @@ func (l *dataListener) DataChange(eventType remoting.Event) bool {
 	}
 
 	for _, v := range l.interestedURL {
-		if serviceURL.URLEqual(*v) {
+		if serviceURL.URLEqual(v) {
 			l.listener.Process(
 				&config_center.ConfigChangeEvent{
 					Key:        eventType.Path,
@@ -113,7 +113,7 @@ func (l *configurationListener) Next() (*registry.ServiceEvent, error) {
 				}
 				continue
 			}
-			return &registry.ServiceEvent{Action: e.ConfigType, Service: e.Value.(common.URL)}, nil
+			return &registry.ServiceEvent{Action: e.ConfigType, Service: e.Value.(*common.URL)}, nil
 		}
 	}
 }
diff --git a/registry/etcdv3/listener_test.go b/registry/etcdv3/listener_test.go
index cc4ea257ccabb5591689ec961ae279e12f24dd29..ff7f63f614c6c43564dc10e412a833449b3bea8a 100644
--- a/registry/etcdv3/listener_test.go
+++ b/registry/etcdv3/listener_test.go
@@ -63,7 +63,6 @@ func (suite *RegistryTestSuite) SetupSuite() {
 	}
 
 	suite.etcd = e
-	return
 }
 
 // stop etcd server
@@ -81,7 +80,7 @@ func (suite *RegistryTestSuite) TestDataChange() {
 
 	listener := NewRegistryDataListener(&MockDataListener{})
 	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)
+	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 2fec8eaad25e716fc5ed5ee33775d8898cb212e2..f3cc379bd8e94b15b678f0ac1d5ed5b6c917da6a 100644
--- a/registry/etcdv3/registry.go
+++ b/registry/etcdv3/registry.go
@@ -91,7 +91,7 @@ func newETCDV3Registry(url *common.URL) (registry.Registry, error) {
 		r,
 		etcdv3.WithName(etcdv3.RegistryETCDV3Client),
 		etcdv3.WithTimeout(timeout),
-		etcdv3.WithEndpoints(url.Location),
+		etcdv3.WithEndpoints(strings.Split(url.Location, ",")...),
 	); err != nil {
 		return nil, err
 	}
@@ -112,8 +112,9 @@ func (r *etcdV3Registry) InitListeners() {
 }
 
 // DoRegister actually do the register job in the registry center of etcd
+// for lease
 func (r *etcdV3Registry) DoRegister(root string, node string) error {
-	return r.client.Create(path.Join(root, node), "")
+	return r.client.RegisterTemp(path.Join(root, node), "")
 }
 
 // nolint
diff --git a/registry/etcdv3/registry_test.go b/registry/etcdv3/registry_test.go
index 164fe9ca61793614e3d9d2f77f49cdfaebdd7317..d94eff656dbc4f46e4b34c8c50b20396b335c073 100644
--- a/registry/etcdv3/registry_test.go
+++ b/registry/etcdv3/registry_test.go
@@ -39,7 +39,7 @@ func initRegistry(t *testing.T) *etcdV3Registry {
 		t.Fatal(err)
 	}
 
-	reg, err := newETCDV3Registry(&regurl)
+	reg, err := newETCDV3Registry(regurl)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -86,7 +86,7 @@ func (suite *RegistryTestSuite) TestSubscribe() {
 
 	err = reg2.Register(url)
 	assert.NoError(t, err)
-	listener, err := reg2.DoSubscribe(&url)
+	listener, err := reg2.DoSubscribe(url)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -104,7 +104,7 @@ func (suite *RegistryTestSuite) TestConsumerDestroy() {
 	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.DoSubscribe(&url)
+	_, err := reg.DoSubscribe(url)
 	if err != nil {
 		t.Fatal(err)
 	}
diff --git a/registry/etcdv3/service_discovery.go b/registry/etcdv3/service_discovery.go
index dceaa99df8061c6f46baa52eb6f5cebe4477f120..e8d4aea9a42634896c3c30e5c6b527a935179873 100644
--- a/registry/etcdv3/service_discovery.go
+++ b/registry/etcdv3/service_discovery.go
@@ -19,6 +19,7 @@ package etcdv3
 
 import (
 	"fmt"
+	"strings"
 	"sync"
 	"time"
 )
@@ -313,7 +314,7 @@ func newEtcdV3ServiceDiscovery(name string) (registry.ServiceDiscovery, error) {
 	client := etcdv3.NewServiceDiscoveryClient(
 		etcdv3.WithName(etcdv3.RegistryETCDV3Client),
 		etcdv3.WithTimeout(timeout),
-		etcdv3.WithEndpoints(remoteConfig.Address),
+		etcdv3.WithEndpoints(strings.Split(remoteConfig.Address, ",")...),
 	)
 
 	descriptor := fmt.Sprintf("etcd-service-discovery[%s]", remoteConfig.Address)
diff --git a/registry/event.go b/registry/event.go
index 39fb00c740ef2e70e2cd6768fa4a4bb3226f832c..2ebc375597aef67b7605073b852ce53f68cb58e4 100644
--- a/registry/event.go
+++ b/registry/event.go
@@ -40,12 +40,37 @@ func init() {
 // ServiceEvent includes create, update, delete event
 type ServiceEvent struct {
 	Action  remoting.EventType
-	Service common.URL
+	Service *common.URL
+	// store the key for Service.Key()
+	key string
+	// If the url is updated, such as Merged.
+	updated bool
 }
 
 // String return the description of event
-func (e ServiceEvent) String() string {
-	return fmt.Sprintf("ServiceEvent{Action{%s}, Path{%s}}", e.Action, e.Service)
+func (e *ServiceEvent) String() string {
+	return fmt.Sprintf("ServiceEvent{Action{%s}, Path{%s}, Key{%s}}", e.Action, e.Service, e.key)
+}
+
+// Update() update the url with the merged URL. Work with Updated() can reduce the process of some merging URL.
+func (e *ServiceEvent) Update(url *common.URL) {
+	e.Service = url
+	e.updated = true
+}
+
+// Updated() check if the url is updated.
+// If the serviceEvent is updated, then it don't need merge url again.
+func (e *ServiceEvent) Updated() bool {
+	return e.updated
+}
+
+// Key() generate the key for service.Key(). It is cached once.
+func (e *ServiceEvent) Key() string {
+	if len(e.key) > 0 {
+		return e.key
+	}
+	e.key = e.Service.Key()
+	return e.key
 }
 
 // ServiceInstancesChangedEvent represents service instances make some changing
diff --git a/registry/event/metadata_service_url_params_customizer_test.go b/registry/event/metadata_service_url_params_customizer_test.go
index 98ae2df883f590f4c3e4b379bb5a0fcbe46d946c..c041232b7836986db034da9c61acabc64050757e 100644
--- a/registry/event/metadata_service_url_params_customizer_test.go
+++ b/registry/event/metadata_service_url_params_customizer_test.go
@@ -70,27 +70,27 @@ func (m *mockMetadataService) ServiceName() (string, error) {
 	panic("implement me")
 }
 
-func (m *mockMetadataService) ExportURL(url common.URL) (bool, error) {
+func (m *mockMetadataService) ExportURL(*common.URL) (bool, error) {
 	panic("implement me")
 }
 
-func (m *mockMetadataService) UnexportURL(url common.URL) error {
+func (m *mockMetadataService) UnexportURL(*common.URL) error {
 	panic("implement me")
 }
 
-func (m *mockMetadataService) SubscribeURL(url common.URL) (bool, error) {
+func (m *mockMetadataService) SubscribeURL(*common.URL) (bool, error) {
 	panic("implement me")
 }
 
-func (m *mockMetadataService) UnsubscribeURL(url common.URL) error {
+func (m *mockMetadataService) UnsubscribeURL(*common.URL) error {
 	panic("implement me")
 }
 
-func (m *mockMetadataService) PublishServiceDefinition(url common.URL) error {
+func (m *mockMetadataService) PublishServiceDefinition(*common.URL) error {
 	panic("implement me")
 }
 
-func (m *mockMetadataService) GetExportedURLs(serviceInterface string, group string, version string, protocol string) ([]interface{}, error) {
+func (m *mockMetadataService) GetExportedURLs(string, string, string, string) ([]interface{}, error) {
 	return m.urls, nil
 }
 
@@ -98,8 +98,8 @@ func (m *mockMetadataService) MethodMapper() map[string]string {
 	panic("implement me")
 }
 
-func (m *mockMetadataService) GetSubscribedURLs() ([]common.URL, error) {
-	res := make([]common.URL, 0, len(m.urls))
+func (m *mockMetadataService) GetSubscribedURLs() ([]*common.URL, error) {
+	var res []*common.URL
 	for _, ui := range m.urls {
 		u, _ := common.NewURL(ui.(string))
 		res = append(res, u)
@@ -107,15 +107,15 @@ func (m *mockMetadataService) GetSubscribedURLs() ([]common.URL, error) {
 	return res, nil
 }
 
-func (m *mockMetadataService) GetServiceDefinition(interfaceName string, group string, version string) (string, error) {
+func (m *mockMetadataService) GetServiceDefinition(string, string, string) (string, error) {
 	panic("implement me")
 }
 
-func (m *mockMetadataService) GetServiceDefinitionByServiceKey(serviceKey string) (string, error) {
+func (m *mockMetadataService) GetServiceDefinitionByServiceKey(string) (string, error) {
 	panic("implement me")
 }
 
-func (m *mockMetadataService) RefreshMetadata(exportedRevision string, subscribedRevision string) (bool, error) {
+func (m *mockMetadataService) RefreshMetadata(string, string) (bool, error) {
 	panic("implement me")
 }
 
diff --git a/registry/event/service_revision_customizer.go b/registry/event/service_revision_customizer.go
index fd21e8f4c7a71cedfe1de7e9c836e7cee278182e..4793e91948fe4c30fffbfd21f0dcc3efe57c5095 100644
--- a/registry/event/service_revision_customizer.go
+++ b/registry/event/service_revision_customizer.go
@@ -126,7 +126,7 @@ func resolveRevision(urls []interface{}) string {
 
 		// append url params if we need it
 	}
-	sort.Sort(sort.StringSlice(candidates))
+	sort.Strings(candidates)
 
 	// it's nearly impossible to be overflow
 	res := uint64(0)
diff --git a/registry/file/listener.go b/registry/file/listener.go
new file mode 100644
index 0000000000000000000000000000000000000000..3fe7400226067f1232ed7993b1fe1b5575b870df
--- /dev/null
+++ b/registry/file/listener.go
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package file
+
+import "github.com/apache/dubbo-go/config_center"
+
+// RegistryConfigurationListener represent the processor of flie watcher
+type RegistryConfigurationListener struct {
+}
+
+// Process submit the ConfigChangeEvent to the event chan to notify all observer
+func (l *RegistryConfigurationListener) Process(configType *config_center.ConfigChangeEvent) {
+
+}
diff --git a/registry/file/service_discovery.go b/registry/file/service_discovery.go
new file mode 100644
index 0000000000000000000000000000000000000000..254c12688f47282343e0004dac86844ba51a3eb2
--- /dev/null
+++ b/registry/file/service_discovery.go
@@ -0,0 +1,285 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package file
+
+import (
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path"
+	"strconv"
+)
+
+import (
+	gxset "github.com/dubbogo/gost/container/set"
+	gxpage "github.com/dubbogo/gost/page"
+	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/config"
+	"github.com/apache/dubbo-go/config_center"
+	"github.com/apache/dubbo-go/config_center/file"
+	"github.com/apache/dubbo-go/registry"
+)
+
+// init will put the service discovery into extension
+func init() {
+	extension.SetServiceDiscovery(constant.FILE_KEY, newFileSystemServiceDiscovery)
+}
+
+// fileServiceDiscovery is the implementation of service discovery based on file.
+type fileSystemServiceDiscovery struct {
+	dynamicConfiguration file.FileSystemDynamicConfiguration
+	rootPath             string
+	fileMap              map[string]string
+}
+
+func newFileSystemServiceDiscovery(name string) (registry.ServiceDiscovery, error) {
+	sdc, ok := config.GetBaseConfig().GetServiceDiscoveries(name)
+	if !ok || sdc.Protocol != constant.FILE_KEY {
+		return nil, perrors.New("could not init the instance because the config is invalid")
+	}
+
+	rp, err := file.Home()
+	if err != nil {
+		return nil, perrors.WithStack(err)
+	}
+
+	fdcf := extension.GetConfigCenterFactory(constant.FILE_KEY)
+	p := path.Join(rp, ".dubbo", constant.REGISTRY_KEY)
+	url, _ := common.NewURL("")
+	url.AddParamAvoidNil(file.CONFIG_CENTER_DIR_PARAM_NAME, p)
+	c, err := fdcf.GetDynamicConfiguration(url)
+	if err != nil {
+		return nil, perrors.WithStack(err)
+	}
+
+	sd := &fileSystemServiceDiscovery{
+		dynamicConfiguration: *c.(*file.FileSystemDynamicConfiguration),
+		rootPath:             p,
+		fileMap:              make(map[string]string),
+	}
+
+	extension.AddCustomShutdownCallback(func() {
+		sd.Destroy()
+	})
+
+	for _, v := range sd.GetServices().Values() {
+		for _, i := range sd.GetInstances(v.(string)) {
+			// like java do nothing
+			l := &RegistryConfigurationListener{}
+			sd.dynamicConfiguration.AddListener(getServiceInstanceId(i), l, config_center.WithGroup(getServiceName(i)))
+		}
+	}
+
+	return sd, nil
+}
+
+// nolint
+func (fssd *fileSystemServiceDiscovery) String() string {
+	return fmt.Sprintf("file-system-service-discovery")
+}
+
+// Destroy will destroy the service discovery.
+// If the discovery cannot be destroy, it will return an error.
+func (fssd *fileSystemServiceDiscovery) Destroy() error {
+	fssd.dynamicConfiguration.Close()
+
+	for _, f := range fssd.fileMap {
+		fssd.releaseAndRemoveRegistrationFiles(f)
+	}
+
+	return nil
+}
+
+// nolint
+func (fssd *fileSystemServiceDiscovery) releaseAndRemoveRegistrationFiles(file string) {
+	os.RemoveAll(file)
+}
+
+// ----------------- registration ----------------
+
+// Register will register an instance of ServiceInstance to registry
+func (fssd *fileSystemServiceDiscovery) Register(instance registry.ServiceInstance) error {
+	id := getServiceInstanceId(instance)
+	sn := getServiceName(instance)
+
+	c, err := toJsonString(instance)
+	if err != nil {
+		return perrors.WithStack(err)
+	}
+
+	err = fssd.dynamicConfiguration.PublishConfig(id, sn, c)
+	if err != nil {
+		return perrors.WithStack(err)
+	}
+
+	fssd.fileMap[id] = fssd.dynamicConfiguration.GetPath(id, sn)
+
+	return nil
+}
+
+// nolint
+func getServiceInstanceId(si registry.ServiceInstance) string {
+	if si.GetId() == "" {
+		return si.GetHost() + "." + strconv.Itoa(si.GetPort())
+	}
+
+	return si.GetId()
+}
+
+// nolint
+func getServiceName(si registry.ServiceInstance) string {
+	return si.GetServiceName()
+}
+
+// toJsonString to json string
+func toJsonString(si registry.ServiceInstance) (string, error) {
+	bytes, err := json.Marshal(si)
+	if err != nil {
+		return "", perrors.WithStack(err)
+	}
+
+	return string(bytes), nil
+}
+
+// Update will update the data of the instance in registry
+func (fssd *fileSystemServiceDiscovery) Update(instance registry.ServiceInstance) error {
+	return fssd.Register(instance)
+}
+
+// Unregister will unregister this instance from registry
+func (fssd *fileSystemServiceDiscovery) Unregister(instance registry.ServiceInstance) error {
+	id := getServiceInstanceId(instance)
+	sn := getServiceName(instance)
+
+	err := fssd.dynamicConfiguration.RemoveConfig(id, sn)
+	if err != nil {
+		return perrors.WithStack(err)
+	}
+
+	delete(fssd.fileMap, instance.GetId())
+	return nil
+}
+
+// ----------------- discovery -------------------
+// GetDefaultPageSize will return the default page size
+func (fssd *fileSystemServiceDiscovery) GetDefaultPageSize() int {
+	return 100
+}
+
+// GetServices will return the all service names.
+func (fssd *fileSystemServiceDiscovery) GetServices() *gxset.HashSet {
+	r := gxset.NewSet()
+	// dynamicConfiguration root path is the actual root path
+	fileInfo, _ := ioutil.ReadDir(fssd.dynamicConfiguration.RootPath())
+
+	for _, file := range fileInfo {
+		if file.IsDir() {
+			r.Add(file.Name())
+		}
+	}
+
+	return r
+}
+
+// GetInstances will return all service instances with serviceName
+func (fssd *fileSystemServiceDiscovery) GetInstances(serviceName string) []registry.ServiceInstance {
+	set, err := fssd.dynamicConfiguration.GetConfigKeysByGroup(serviceName)
+	if err != nil {
+		logger.Errorf("[FileServiceDiscovery] Could not query the instances for service{%s}, error = err{%v} ",
+			serviceName, err)
+		return make([]registry.ServiceInstance, 0, 0)
+	}
+
+	res := make([]registry.ServiceInstance, 0, set.Size())
+	for _, v := range set.Values() {
+		id := v.(string)
+		p, err := fssd.dynamicConfiguration.GetProperties(id, config_center.WithGroup(serviceName))
+		if err != nil {
+			logger.Errorf("[FileServiceDiscovery] Could not get the properties for id{%s}, service{%s}, "+
+				"error = err{%v} ",
+				id, serviceName, err)
+			return make([]registry.ServiceInstance, 0, 0)
+		}
+
+		dsi := &registry.DefaultServiceInstance{}
+		err = json.Unmarshal([]byte(p), dsi)
+		if err != nil {
+			logger.Errorf("[FileServiceDiscovery] Could not unmarshal the properties for id{%s}, service{%s}, "+
+				"error = err{%v} ",
+				id, serviceName, err)
+			return make([]registry.ServiceInstance, 0, 0)
+		}
+
+		res = append(res, dsi)
+	}
+
+	return res
+}
+
+// GetInstancesByPage will return a page containing instances of ServiceInstance with the serviceName
+// the page will start at offset
+func (fssd *fileSystemServiceDiscovery) GetInstancesByPage(serviceName string, offset int, pageSize int) gxpage.Pager {
+	return nil
+}
+
+// GetHealthyInstancesByPage will return a page containing instances of ServiceInstance.
+// The param healthy indices that the instance should be healthy or not.
+// The page will start at offset
+func (fssd *fileSystemServiceDiscovery) GetHealthyInstancesByPage(serviceName string, offset int, pageSize int,
+	healthy bool) gxpage.Pager {
+	return nil
+}
+
+// Batch get all instances by the specified service names
+func (fssd *fileSystemServiceDiscovery) GetRequestInstances(serviceNames []string, offset int,
+	requestedSize int) map[string]gxpage.Pager {
+	return nil
+}
+
+// ----------------- event ----------------------
+// AddListener adds a new ServiceInstancesChangedListener
+// client
+func (fssd *fileSystemServiceDiscovery) AddListener(listener *registry.ServiceInstancesChangedListener) error {
+	//fssd.dynamicConfiguration.AddListener(listener.ServiceName)
+	return nil
+}
+
+// DispatchEventByServiceName dispatches the ServiceInstancesChangedEvent to service instance whose name is serviceName
+func (fssd *fileSystemServiceDiscovery) DispatchEventByServiceName(serviceName string) error {
+	return fssd.DispatchEvent(registry.NewServiceInstancesChangedEvent(serviceName, fssd.GetInstances(serviceName)))
+}
+
+// DispatchEventForInstances dispatches the ServiceInstancesChangedEvent to target instances
+func (fssd *fileSystemServiceDiscovery) DispatchEventForInstances(serviceName string,
+	instances []registry.ServiceInstance) error {
+	return fssd.DispatchEvent(registry.NewServiceInstancesChangedEvent(serviceName, instances))
+}
+
+// DispatchEvent dispatches the event
+func (fssd *fileSystemServiceDiscovery) DispatchEvent(event *registry.ServiceInstancesChangedEvent) error {
+	extension.GetGlobalDispatcher().Dispatch(event)
+	return nil
+}
diff --git a/registry/file/service_discovery_test.go b/registry/file/service_discovery_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..0bffcae31d039a49d8cf696a6de2f6858c42ada2
--- /dev/null
+++ b/registry/file/service_discovery_test.go
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package file
+
+import (
+	"math/rand"
+	"strconv"
+	"testing"
+	"time"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+import (
+	"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/registry"
+)
+
+var (
+	testName = "test"
+)
+
+func TestNewFileSystemServiceDiscoveryAndDestroy(t *testing.T) {
+	prepareData()
+	serviceDiscovery, err := newFileSystemServiceDiscovery(testName)
+	assert.NoError(t, err)
+	assert.NotNil(t, serviceDiscovery)
+	defer serviceDiscovery.Destroy()
+}
+
+func TestCURDFileSystemServiceDiscovery(t *testing.T) {
+	prepareData()
+	serviceDiscovery, err := extension.GetServiceDiscovery(constant.FILE_KEY, testName)
+	assert.NoError(t, err)
+	md := make(map[string]string)
+
+	rand.Seed(time.Now().Unix())
+	serviceName := "service-name" + strconv.Itoa(rand.Intn(10000))
+	md["t1"] = "test1"
+	r1 := &registry.DefaultServiceInstance{
+		Id:          "123456789",
+		ServiceName: serviceName,
+		Host:        "127.0.0.1",
+		Port:        2233,
+		Enable:      true,
+		Healthy:     true,
+		Metadata:    md,
+	}
+	err = serviceDiscovery.Register(r1)
+	assert.NoError(t, err)
+
+	instances := serviceDiscovery.GetInstances(r1.ServiceName)
+	assert.Equal(t, 1, len(instances))
+	assert.Equal(t, r1.Id, instances[0].GetId())
+	assert.Equal(t, r1.ServiceName, instances[0].GetServiceName())
+	assert.Equal(t, r1.Port, instances[0].GetPort())
+
+	err = serviceDiscovery.Unregister(r1)
+	assert.NoError(t, err)
+
+	err = serviceDiscovery.Register(r1)
+
+	defer serviceDiscovery.Destroy()
+}
+
+func prepareData() {
+	config.GetBaseConfig().ServiceDiscoveries[testName] = &config.ServiceDiscoveryConfig{
+		Protocol: "file",
+	}
+}
diff --git a/registry/kubernetes/listener.go b/registry/kubernetes/listener.go
index 24c8d81614c7dd323e4f23ec7de5d28b24eecb70..e20b7c7e7d2f72349f7ec268bbb3b09f135dc6ed 100644
--- a/registry/kubernetes/listener.go
+++ b/registry/kubernetes/listener.go
@@ -65,7 +65,7 @@ func (l *dataListener) DataChange(eventType remoting.Event) bool {
 	}
 
 	for _, v := range l.interestedURL {
-		if serviceURL.URLEqual(*v) {
+		if serviceURL.URLEqual(v) {
 			l.listener.Process(
 				&config_center.ConfigChangeEvent{
 					Key:        eventType.Path,
@@ -114,7 +114,7 @@ func (l *configurationListener) Next() (*registry.ServiceEvent, error) {
 				}
 				continue
 			}
-			return &registry.ServiceEvent{Action: e.ConfigType, Service: e.Value.(common.URL)}, nil
+			return &registry.ServiceEvent{Action: e.ConfigType, Service: e.Value.(*common.URL)}, nil
 		}
 	}
 }
diff --git a/registry/kubernetes/listener_test.go b/registry/kubernetes/listener_test.go
index ccaaf80907f9eb3d4956758374f518c66fa613d5..f1d8ff41761a841aa2bec888336756854ff16874 100644
--- a/registry/kubernetes/listener_test.go
+++ b/registry/kubernetes/listener_test.go
@@ -166,7 +166,7 @@ var clientPodJsonData = `{
 func Test_DataChange(t *testing.T) {
 	listener := NewRegistryDataListener(&MockDataListener{})
 	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)
+	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"})
 	assert.Equal(t, true, int)
 }
@@ -179,7 +179,7 @@ func TestDataChange(t *testing.T) {
 
 	listener := NewRegistryDataListener(&MockDataListener{})
 	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)
+	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/kubernetes/registry.go b/registry/kubernetes/registry.go
index 88895855689df42f2cd66ec158e6b8f2806ef6f0..c1e559e48dcc64972c9405e15405d7e6febcc845 100644
--- a/registry/kubernetes/registry.go
+++ b/registry/kubernetes/registry.go
@@ -27,7 +27,6 @@ import (
 
 import (
 	"github.com/apache/dubbo-getty"
-	"github.com/dubbogo/gost/net"
 	perrors "github.com/pkg/errors"
 	v1 "k8s.io/api/core/v1"
 )
@@ -54,7 +53,7 @@ const (
 
 func init() {
 	processID = fmt.Sprintf("%d", os.Getpid())
-	localIP, _ = gxnet.GetLocalIP()
+	localIP = common.GetLocalIp()
 	extension.SetRegistry(Name, newKubernetesRegistry)
 }
 
diff --git a/registry/kubernetes/registry_test.go b/registry/kubernetes/registry_test.go
index 347dadcd2c462e3a1caf9829b051a665ec61e8e3..9fb409a222be914cad5b64e7ff3aab9bc97a6f33 100644
--- a/registry/kubernetes/registry_test.go
+++ b/registry/kubernetes/registry_test.go
@@ -231,7 +231,7 @@ func getTestRegistry(t *testing.T) *kubernetesRegistry {
 	if err != nil {
 		t.Fatal(err)
 	}
-	out, err := newMockKubernetesRegistry(&regurl, pl)
+	out, err := newMockKubernetesRegistry(regurl, pl)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -268,7 +268,7 @@ func TestSubscribe(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	listener, err := r.DoSubscribe(&url)
+	listener, err := r.DoSubscribe(url)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -301,7 +301,7 @@ func TestConsumerDestroy(t *testing.T) {
 		common.WithParamsValue(constant.CLUSTER_KEY, "mock"),
 		common.WithMethods([]string{"GetUser", "AddUser"}))
 
-	_, err := r.DoSubscribe(&url)
+	_, err := r.DoSubscribe(url)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -336,7 +336,7 @@ func TestNewRegistry(t *testing.T) {
 	if err != nil {
 		t.Fatal(err)
 	}
-	_, err = newKubernetesRegistry(&regUrl)
+	_, err = newKubernetesRegistry(regUrl)
 	if err == nil {
 		t.Fatal("not in cluster, should be a err")
 	}
diff --git a/registry/mock_registry.go b/registry/mock_registry.go
index 10561d0f49e995c94c93fa0463fc0b0421ff6e20..7c269c3a4044079d19c7c41b53a9ae04b9eab86b 100644
--- a/registry/mock_registry.go
+++ b/registry/mock_registry.go
@@ -18,6 +18,7 @@
 package registry
 
 import (
+	"fmt"
 	"time"
 )
 
@@ -32,14 +33,16 @@ import (
 
 // MockRegistry is used as mock registry
 type MockRegistry struct {
-	listener  *listener
-	destroyed *atomic.Bool
+	listener   *listener
+	destroyed  *atomic.Bool
+	allAddress chan []*ServiceEvent
 }
 
 // NewMockRegistry creates a mock registry
 func NewMockRegistry(url *common.URL) (Registry, error) {
 	registry := &MockRegistry{
-		destroyed: atomic.NewBool(false),
+		destroyed:  atomic.NewBool(false),
+		allAddress: make(chan []*ServiceEvent),
 	}
 	listener := &listener{count: 0, registry: registry, listenChan: make(chan *ServiceEvent)}
 	registry.listener = listener
@@ -47,12 +50,12 @@ func NewMockRegistry(url *common.URL) (Registry, error) {
 }
 
 // Register is used as a mock registry
-func (*MockRegistry) Register(url common.URL) error {
+func (*MockRegistry) Register(url *common.URL) error {
 	return nil
 }
 
 // nolint
-func (r *MockRegistry) UnRegister(conf common.URL) error {
+func (r *MockRegistry) UnRegister(conf *common.URL) error {
 	return nil
 }
 
@@ -68,8 +71,8 @@ func (r *MockRegistry) IsAvailable() bool {
 }
 
 // nolint
-func (r *MockRegistry) GetUrl() common.URL {
-	return common.URL{}
+func (r *MockRegistry) GetUrl() *common.URL {
+	return nil
 }
 
 func (r *MockRegistry) subscribe(*common.URL) (Listener, error) {
@@ -80,22 +83,12 @@ func (r *MockRegistry) subscribe(*common.URL) (Listener, error) {
 func (r *MockRegistry) Subscribe(url *common.URL, notifyListener NotifyListener) error {
 	go func() {
 		for {
-			if !r.IsAvailable() {
-				logger.Warnf("event listener game over.")
-				time.Sleep(time.Duration(3) * time.Second)
-				return
-			}
-
-			listener, err := r.subscribe(url)
-			if err != nil {
-				if !r.IsAvailable() {
-					logger.Warnf("event listener game over.")
-					return
-				}
-				time.Sleep(time.Duration(3) * time.Second)
+			t, listener := r.checkLoopSubscribe(url)
+			if t == 0 {
 				continue
+			} else if t == -1 {
+				return
 			}
-
 			for {
 				serviceEvent, err := listener.Next()
 				if err != nil {
@@ -109,6 +102,26 @@ func (r *MockRegistry) Subscribe(url *common.URL, notifyListener NotifyListener)
 			}
 		}
 	}()
+	go func() {
+		for {
+			t, _ := r.checkLoopSubscribe(url)
+			if t == 0 {
+				continue
+			} else if t == -1 {
+				return
+			}
+
+			for {
+				select {
+				case e := <-r.allAddress:
+					notifyListener.NotifyAll(e, func() {
+						fmt.Print("notify all ok")
+					})
+					break
+				}
+			}
+		}
+	}()
 	return nil
 }
 
@@ -138,3 +151,27 @@ func (*listener) Close() {
 func (r *MockRegistry) MockEvent(event *ServiceEvent) {
 	r.listener.listenChan <- event
 }
+
+// nolint
+func (r *MockRegistry) MockEvents(events []*ServiceEvent) {
+	r.allAddress <- events
+}
+
+func (r *MockRegistry) checkLoopSubscribe(url *common.URL) (int, Listener) {
+	if !r.IsAvailable() {
+		logger.Warnf("event listener game over.")
+		time.Sleep(time.Duration(3) * time.Second)
+		return -1, nil
+	}
+
+	listener, err := r.subscribe(url)
+	if err != nil {
+		if !r.IsAvailable() {
+			logger.Warnf("event listener game over.")
+			return -1, nil
+		}
+		time.Sleep(time.Duration(3) * time.Second)
+		return 0, nil
+	}
+	return 1, listener
+}
diff --git a/registry/nacos/listener.go b/registry/nacos/listener.go
index cf6a73d38fbefc1ecb6a80e4911bb0d8fb13a6f6..7f27326d6d7b1000688cb03cd6406ea53745a119 100644
--- a/registry/nacos/listener.go
+++ b/registry/nacos/listener.go
@@ -43,7 +43,7 @@ import (
 
 type nacosListener struct {
 	namingClient   naming_client.INamingClient
-	listenUrl      common.URL
+	listenUrl      *common.URL
 	events         chan *config_center.ConfigChangeEvent
 	instanceMap    map[string]model.Instance
 	cacheLock      sync.Mutex
@@ -52,7 +52,7 @@ type nacosListener struct {
 }
 
 // NewRegistryDataListener creates a data listener for nacos
-func NewNacosListener(url common.URL, namingClient naming_client.INamingClient) (*nacosListener, error) {
+func NewNacosListener(url *common.URL, namingClient naming_client.INamingClient) (*nacosListener, error) {
 	listener := &nacosListener{
 		namingClient: namingClient,
 		listenUrl:    url, events: make(chan *config_center.ConfigChangeEvent, 32),
@@ -154,25 +154,25 @@ func (nl *nacosListener) Callback(services []model.SubscribeService, err error)
 	for i := range addInstances {
 		newUrl := generateUrl(addInstances[i])
 		if newUrl != nil {
-			nl.process(&config_center.ConfigChangeEvent{Value: *newUrl, ConfigType: remoting.EventTypeAdd})
+			nl.process(&config_center.ConfigChangeEvent{Value: newUrl, ConfigType: remoting.EventTypeAdd})
 		}
 	}
 	for i := range delInstances {
 		newUrl := generateUrl(delInstances[i])
 		if newUrl != nil {
-			nl.process(&config_center.ConfigChangeEvent{Value: *newUrl, ConfigType: remoting.EventTypeDel})
+			nl.process(&config_center.ConfigChangeEvent{Value: newUrl, ConfigType: remoting.EventTypeDel})
 		}
 	}
 
 	for i := range updateInstances {
 		newUrl := generateUrl(updateInstances[i])
 		if newUrl != nil {
-			nl.process(&config_center.ConfigChangeEvent{Value: *newUrl, ConfigType: remoting.EventTypeUpdate})
+			nl.process(&config_center.ConfigChangeEvent{Value: newUrl, ConfigType: remoting.EventTypeUpdate})
 		}
 	}
 }
 
-func getSubscribeName(url common.URL) string {
+func getSubscribeName(url *common.URL) string {
 	var buffer bytes.Buffer
 
 	buffer.Write([]byte(common.DubboNodes[common.PROVIDER]))
@@ -210,7 +210,7 @@ func (nl *nacosListener) Next() (*registry.ServiceEvent, error) {
 
 		case e := <-nl.events:
 			logger.Debugf("got nacos event %s", e)
-			return &registry.ServiceEvent{Action: e.ConfigType, Service: e.Value.(common.URL)}, nil
+			return &registry.ServiceEvent{Action: e.ConfigType, Service: e.Value.(*common.URL)}, nil
 		}
 	}
 }
diff --git a/registry/nacos/registry.go b/registry/nacos/registry.go
index 411090820c7682ab9c3b5576ea8ad5207c2c899f..ae2345e6d5c90fcfc5c4dfb5b4da07bcf1b6f43e 100644
--- a/registry/nacos/registry.go
+++ b/registry/nacos/registry.go
@@ -26,7 +26,6 @@ import (
 )
 
 import (
-	gxnet "github.com/dubbogo/gost/net"
 	"github.com/nacos-group/nacos-sdk-go/clients"
 	"github.com/nacos-group/nacos-sdk-go/clients/naming_client"
 	nacosConstant "github.com/nacos-group/nacos-sdk-go/common/constant"
@@ -52,23 +51,23 @@ const (
 )
 
 func init() {
-	localIP, _ = gxnet.GetLocalIP()
+	localIP = common.GetLocalIp()
 	extension.SetRegistry(constant.NACOS_KEY, newNacosRegistry)
 }
 
 type nacosRegistry struct {
 	*common.URL
 	namingClient naming_client.INamingClient
-	registryUrls []common.URL
+	registryUrls []*common.URL
 }
 
-func getCategory(url common.URL) string {
+func getCategory(url *common.URL) string {
 	role, _ := strconv.Atoi(url.GetParam(constant.ROLE_KEY, strconv.Itoa(constant.NACOS_DEFAULT_ROLETYPE)))
 	category := common.DubboNodes[role]
 	return category
 }
 
-func getServiceName(url common.URL) string {
+func getServiceName(url *common.URL) string {
 	var buffer bytes.Buffer
 
 	buffer.Write([]byte(getCategory(url)))
@@ -78,7 +77,7 @@ func getServiceName(url common.URL) string {
 	return buffer.String()
 }
 
-func appendParam(target *bytes.Buffer, url common.URL, key string) {
+func appendParam(target *bytes.Buffer, url *common.URL, key string) {
 	value := url.GetParam(key, "")
 	if strings.TrimSpace(value) != "" {
 		target.Write([]byte(constant.NACOS_SERVICE_NAME_SEPARATOR))
@@ -86,7 +85,7 @@ func appendParam(target *bytes.Buffer, url common.URL, key string) {
 	}
 }
 
-func createRegisterParam(url common.URL, serviceName string) vo.RegisterInstanceParam {
+func createRegisterParam(url *common.URL, serviceName string) vo.RegisterInstanceParam {
 	category := getCategory(url)
 	params := make(map[string]string)
 
@@ -119,7 +118,7 @@ func createRegisterParam(url common.URL, serviceName string) vo.RegisterInstance
 }
 
 // Register will register the service @url to its nacos registry center
-func (nr *nacosRegistry) Register(url common.URL) error {
+func (nr *nacosRegistry) Register(url *common.URL) error {
 	serviceName := getServiceName(url)
 	param := createRegisterParam(url, serviceName)
 	isRegistry, err := nr.namingClient.RegisterInstance(param)
@@ -133,7 +132,7 @@ func (nr *nacosRegistry) Register(url common.URL) error {
 	return nil
 }
 
-func createDeregisterParam(url common.URL, serviceName string) vo.DeregisterInstanceParam {
+func createDeregisterParam(url *common.URL, serviceName string) vo.DeregisterInstanceParam {
 	if len(url.Ip) == 0 {
 		url.Ip = localIP
 	}
@@ -149,7 +148,7 @@ func createDeregisterParam(url common.URL, serviceName string) vo.DeregisterInst
 	}
 }
 
-func (nr *nacosRegistry) DeRegister(url common.URL) error {
+func (nr *nacosRegistry) DeRegister(url *common.URL) error {
 	serviceName := getServiceName(url)
 	param := createDeregisterParam(url, serviceName)
 	isDeRegistry, err := nr.namingClient.DeregisterInstance(param)
@@ -163,12 +162,12 @@ func (nr *nacosRegistry) DeRegister(url common.URL) error {
 }
 
 // UnRegister
-func (nr *nacosRegistry) UnRegister(conf common.URL) error {
+func (nr *nacosRegistry) UnRegister(conf *common.URL) error {
 	return perrors.New("UnRegister is not support in nacosRegistry")
 }
 
 func (nr *nacosRegistry) subscribe(conf *common.URL) (registry.Listener, error) {
-	return NewNacosListener(*conf, nr.namingClient)
+	return NewNacosListener(conf, nr.namingClient)
 }
 
 // subscribe from registry
@@ -203,7 +202,6 @@ func (nr *nacosRegistry) Subscribe(url *common.URL, notifyListener registry.Noti
 		}
 
 	}
-	return nil
 }
 
 // UnSubscribe :
@@ -212,8 +210,8 @@ func (nr *nacosRegistry) UnSubscribe(url *common.URL, notifyListener registry.No
 }
 
 // GetUrl gets its registration URL
-func (nr *nacosRegistry) GetUrl() common.URL {
-	return *nr.URL
+func (nr *nacosRegistry) GetUrl() *common.URL {
+	return nr.URL
 }
 
 // IsAvailable determines nacos registry center whether it is available
@@ -244,12 +242,12 @@ func newNacosRegistry(url *common.URL) (registry.Registry, error) {
 	if err != nil {
 		return &nacosRegistry{}, err
 	}
-	registry := &nacosRegistry{
+	tmpRegistry := &nacosRegistry{
 		URL:          url,
 		namingClient: client,
-		registryUrls: []common.URL{},
+		registryUrls: []*common.URL{},
 	}
-	return registry, nil
+	return tmpRegistry, nil
 }
 
 // getNacosConfig will return the nacos config
@@ -288,6 +286,7 @@ func getNacosConfig(url *common.URL) (map[string]interface{}, error) {
 	clientConfig.CacheDir = url.GetParam(constant.NACOS_CACHE_DIR_KEY, "")
 	clientConfig.LogDir = url.GetParam(constant.NACOS_LOG_DIR_KEY, "")
 	clientConfig.Endpoint = url.GetParam(constant.NACOS_ENDPOINT, "")
+	clientConfig.NamespaceId = url.GetParam(constant.NACOS_NAMESPACE_ID, "")
 	clientConfig.NotLoadCacheAtStart = true
 	configMap["clientConfig"] = clientConfig
 
diff --git a/registry/nacos/registry_test.go b/registry/nacos/registry_test.go
index d0311b200b27081c60bc97b2307a54774ca977bd..078b8ce59c29acae35ccbefafbb9cbfb3d0b205d 100644
--- a/registry/nacos/registry_test.go
+++ b/registry/nacos/registry_test.go
@@ -19,9 +19,11 @@ package nacos
 
 import (
 	"encoding/json"
+	"net/http"
 	"net/url"
 	"strconv"
 	"testing"
+	"time"
 )
 
 import (
@@ -35,6 +37,9 @@ import (
 )
 
 func TestNacosRegistry_Register(t *testing.T) {
+	if !checkNacosServerAlive() {
+		return
+	}
 	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")
@@ -44,7 +49,7 @@ func TestNacosRegistry_Register(t *testing.T) {
 	urlMap.Set(constant.CLUSTER_KEY, "mock")
 	testUrl, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParams(urlMap), common.WithMethods([]string{"GetUser", "AddUser"}))
 
-	reg, err := newNacosRegistry(&regurl)
+	reg, err := newNacosRegistry(regurl)
 	assert.Nil(t, err)
 	if err != nil {
 		t.Errorf("new nacos registry error:%s \n", err.Error())
@@ -64,6 +69,9 @@ func TestNacosRegistry_Register(t *testing.T) {
 }
 
 func TestNacosRegistry_Subscribe(t *testing.T) {
+	if !checkNacosServerAlive() {
+		return
+	}
 	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")
@@ -74,7 +82,7 @@ func TestNacosRegistry_Subscribe(t *testing.T) {
 	urlMap.Set(constant.NACOS_PATH_KEY, "")
 	testUrl, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParams(urlMap), common.WithMethods([]string{"GetUser", "AddUser"}))
 
-	reg, _ := newNacosRegistry(&regurl)
+	reg, _ := newNacosRegistry(regurl)
 	err := reg.Register(testUrl)
 	assert.Nil(t, err)
 	if err != nil {
@@ -83,8 +91,8 @@ func TestNacosRegistry_Subscribe(t *testing.T) {
 	}
 
 	regurl.SetParam(constant.ROLE_KEY, strconv.Itoa(common.CONSUMER))
-	reg2, _ := newNacosRegistry(&regurl)
-	listener, err := reg2.(*nacosRegistry).subscribe(&testUrl)
+	reg2, _ := newNacosRegistry(regurl)
+	listener, err := reg2.(*nacosRegistry).subscribe(testUrl)
 	assert.Nil(t, err)
 	if err != nil {
 		t.Errorf("subscribe error:%s \n", err.Error())
@@ -102,6 +110,9 @@ func TestNacosRegistry_Subscribe(t *testing.T) {
 }
 
 func TestNacosRegistry_Subscribe_del(t *testing.T) {
+	if !checkNacosServerAlive() {
+		return
+	}
 	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")
@@ -113,7 +124,7 @@ func TestNacosRegistry_Subscribe_del(t *testing.T) {
 	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)
+	reg, _ := newNacosRegistry(regurl)
 	err := reg.Register(url1)
 	assert.Nil(t, err)
 	if err != nil {
@@ -128,8 +139,8 @@ func TestNacosRegistry_Subscribe_del(t *testing.T) {
 	}
 
 	regurl.SetParam(constant.ROLE_KEY, strconv.Itoa(common.CONSUMER))
-	reg2, _ := newNacosRegistry(&regurl)
-	listener, err := reg2.(*nacosRegistry).subscribe(&url1)
+	reg2, _ := newNacosRegistry(regurl)
+	listener, err := reg2.(*nacosRegistry).subscribe(url1)
 	assert.Nil(t, err)
 	if err != nil {
 		t.Errorf("subscribe error:%s \n", err.Error())
@@ -177,8 +188,8 @@ func TestNacosListener_Close(t *testing.T) {
 	urlMap.Set(constant.CLUSTER_KEY, "mock")
 	urlMap.Set(constant.NACOS_PATH_KEY, "")
 	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)
+	reg, _ := newNacosRegistry(regurl)
+	listener, err := reg.(*nacosRegistry).subscribe(url1)
 	assert.Nil(t, err)
 	if err != nil {
 		t.Errorf("subscribe error:%s \n", err.Error())
@@ -188,3 +199,11 @@ func TestNacosListener_Close(t *testing.T) {
 	_, err = listener.Next()
 	assert.NotNil(t, err)
 }
+
+func checkNacosServerAlive() bool {
+	c := http.Client{Timeout: time.Second}
+	if _, err := c.Get("http://console.nacos.io/nacos/"); err != nil {
+		return false
+	}
+	return true
+}
diff --git a/registry/nacos/service_discovery.go b/registry/nacos/service_discovery.go
index 0e5ad8e6990856aeb0dfdde72f9c3f7fdae3e985..b38e150e51075ce47ee38c9c8c9c7280e4437c53 100644
--- a/registry/nacos/service_discovery.go
+++ b/registry/nacos/service_discovery.go
@@ -141,7 +141,8 @@ func (n *nacosServiceDiscovery) GetInstances(serviceName string) []registry.Serv
 		GroupName:   n.group,
 	})
 	if err != nil {
-		logger.Errorf("Could not query the instances for service: " + serviceName + ", group: " + n.group)
+		logger.Errorf("Could not query the instances for service: %+v, group: %+v . It happened err %+v",
+			serviceName, n.group, err)
 		return make([]registry.ServiceInstance, 0, 0)
 	}
 	res := make([]registry.ServiceInstance, 0, len(instances))
diff --git a/registry/nacos/service_discovery_test.go b/registry/nacos/service_discovery_test.go
index 119be0b3aad3a828470c8c72c775abaada9512c2..3b09136d2e273090bea339e0a15b25dde719b6ac 100644
--- a/registry/nacos/service_discovery_test.go
+++ b/registry/nacos/service_discovery_test.go
@@ -81,6 +81,9 @@ func TestNacosServiceDiscovery_Destroy(t *testing.T) {
 }
 
 func TestNacosServiceDiscovery_CRUD(t *testing.T) {
+	if !checkNacosServerAlive() {
+		return
+	}
 	prepareData()
 	extension.SetEventDispatcher("mock", func() observer.EventDispatcher {
 		return &dispatcher.MockEventDispatcher{}
diff --git a/registry/protocol/protocol.go b/registry/protocol/protocol.go
index c0608ad7989e69c104f07aa95e9a77fb9ac212fa..3be88c3beb6d909c619ff5c8ff51b90359a31d7b 100644
--- a/registry/protocol/protocol.go
+++ b/registry/protocol/protocol.go
@@ -22,6 +22,7 @@ import (
 	"strings"
 	"sync"
 )
+
 import (
 	gxset "github.com/dubbogo/gost/container/set"
 )
@@ -31,7 +32,6 @@ 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/common/proxy/proxy_factory"
 	"github.com/apache/dubbo-go/config"
 	"github.com/apache/dubbo-go/config_center"
 	_ "github.com/apache/dubbo-go/config_center/configurator"
@@ -54,9 +54,10 @@ var (
 
 type registryProtocol struct {
 	invokers []protocol.Invoker
-	// Registry  Map<RegistryAddress, Registry>
+	// Registry Map<RegistryAddress, Registry>
 	registries *sync.Map
-	// To solve the problem of RMI repeated exposure port conflicts, the services that have been exposed are no longer exposed.
+	// To solve the problem of RMI repeated exposure port conflicts,
+	// the services that have been exposed are no longer exposed.
 	// providerurl <--> exporter
 	bounds                        *sync.Map
 	overrideListeners             *sync.Map
@@ -100,10 +101,9 @@ func getUrlToRegistry(providerUrl *common.URL, registryUrl *common.URL) *common.
 
 // filterHideKey filter the parameters that do not need to be output in url(Starting with .)
 func filterHideKey(url *common.URL) *common.URL {
-
 	// be careful params maps in url is map type
 	removeSet := gxset.NewSet()
-	for k, _ := range url.GetParams() {
+	for k := range url.GetParams() {
 		if strings.HasPrefix(k, ".") {
 			removeSet.Add(k)
 		}
@@ -130,32 +130,30 @@ func (proto *registryProtocol) GetRegistries() []registry.Registry {
 }
 
 // Refer provider service from registry center
-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 {
-		protocol := registryUrl.GetParam(constant.REGISTRY_KEY, "")
-		registryUrl.Protocol = protocol
+		registryUrl.Protocol = registryUrl.GetParam(constant.REGISTRY_KEY, "")
 	}
 
 	var reg registry.Registry
-
 	if regI, loaded := proto.registries.Load(registryUrl.Key()); !loaded {
-		reg = getRegistry(&registryUrl)
+		reg = getRegistry(registryUrl)
 		proto.registries.Store(registryUrl.Key(), reg)
 	} else {
 		reg = regI.(registry.Registry)
 	}
 
 	// new registry directory for store service url from registry
-	directory, err := extension.GetDefaultRegistryDirectory(&registryUrl, reg)
+	directory, err := extension.GetDefaultRegistryDirectory(registryUrl, reg)
 	if err != nil {
-		logger.Errorf("consumer service %v  create registry directory  error, error message is %s, and will return nil invoker!",
+		logger.Errorf("consumer service %v create registry directory error, error message is %s, and will return nil invoker!",
 			serviceUrl.String(), err.Error())
 		return nil
 	}
 
-	err = reg.Register(*serviceUrl)
+	err = reg.Register(serviceUrl)
 	if err != nil {
 		logger.Errorf("consumer service %v register registry %v error, error message is %s",
 			serviceUrl.String(), registryUrl.String(), err.Error())
@@ -163,7 +161,6 @@ func (proto *registryProtocol) Refer(url common.URL) protocol.Invoker {
 
 	// new cluster invoker
 	cluster := extension.GetCluster(serviceUrl.GetParam(constant.CLUSTER_KEY, constant.DEFAULT_CLUSTER))
-
 	invoker := cluster.Join(directory)
 	proto.invokers = append(proto.invokers, invoker)
 	return invoker
@@ -196,7 +193,7 @@ func (proto *registryProtocol) Export(invoker protocol.Invoker) protocol.Exporte
 	}
 
 	registeredProviderUrl := getUrlToRegistry(providerUrl, registryUrl)
-	err := reg.Register(*registeredProviderUrl)
+	err := reg.Register(registeredProviderUrl)
 	if err != nil {
 		logger.Errorf("provider service %v register registry %v error, error message is %s",
 			providerUrl.Key(), registryUrl.Key(), err.Error())
@@ -204,7 +201,7 @@ func (proto *registryProtocol) Export(invoker protocol.Invoker) protocol.Exporte
 	}
 
 	key := getCacheKey(providerUrl)
-	logger.Infof("The cached exporter keys is %v !", key)
+	logger.Infof("The cached exporter keys is %v!", key)
 	cachedExporter, loaded := proto.bounds.Load(key)
 	if loaded {
 		logger.Infof("The exporter has been cached, and will return cached exporter!")
@@ -217,7 +214,6 @@ func (proto *registryProtocol) Export(invoker protocol.Invoker) protocol.Exporte
 
 	go reg.Subscribe(overriderUrl, overrideSubscribeListener)
 	return cachedExporter.(protocol.Exporter)
-
 }
 
 func (proto *registryProtocol) reExport(invoker protocol.Invoker, newUrl *common.URL) {
@@ -229,7 +225,6 @@ func (proto *registryProtocol) reExport(invoker protocol.Invoker, newUrl *common
 		proto.bounds.Delete(key)
 		proto.Export(wrappedNewInvoker)
 		// TODO:  unregister & unsubscribe
-
 	}
 }
 
@@ -246,12 +241,22 @@ func newOverrideSubscribeListener(overriderUrl *common.URL, invoker protocol.Inv
 
 // Notify will be triggered when a service change notification is received.
 func (nl *overrideSubscribeListener) Notify(event *registry.ServiceEvent) {
-	if isMatched(&(event.Service), nl.url) && event.Action == remoting.EventTypeAdd {
-		nl.configurator = extension.GetDefaultConfigurator(&(event.Service))
+	if isMatched(event.Service, nl.url) && event.Action == remoting.EventTypeAdd {
+		nl.configurator = extension.GetDefaultConfigurator(event.Service)
 		nl.doOverrideIfNecessary()
 	}
 }
 
+func (nl *overrideSubscribeListener) NotifyAll(events []*registry.ServiceEvent, callback func()) {
+	defer callback()
+	if len(events) == 0 {
+		return
+	}
+	for _, e := range events {
+		nl.Notify(e)
+	}
+}
+
 func (nl *overrideSubscribeListener) doOverrideIfNecessary() {
 	providerUrl := getProviderUrl(nl.originInvoker)
 	key := getCacheKey(providerUrl)
@@ -274,9 +279,9 @@ func (nl *overrideSubscribeListener) doOverrideIfNecessary() {
 		}
 
 		if currentUrl.String() != providerUrl.String() {
-			newRegUrl := nl.originInvoker.GetUrl()
-			setProviderUrl(&newRegUrl, providerUrl)
-			nl.protocol.reExport(nl.originInvoker, &newRegUrl)
+			newRegUrl := nl.originInvoker.GetUrl().Clone()
+			setProviderUrl(newRegUrl, providerUrl)
+			nl.protocol.reExport(nl.originInvoker, newRegUrl)
 		}
 	}
 }
@@ -366,12 +371,11 @@ func (proto *registryProtocol) Destroy() {
 func getRegistryUrl(invoker protocol.Invoker) *common.URL {
 	// here add * for return a new url
 	url := invoker.GetUrl()
-	// if the protocol == registry ,set protocol the registry value in url.params
+	// if the protocol == registry, set protocol the registry value in url.params
 	if url.Protocol == constant.REGISTRY_PROTOCOL {
-		protocol := url.GetParam(constant.REGISTRY_KEY, "")
-		url.Protocol = protocol
+		url.Protocol = url.GetParam(constant.REGISTRY_KEY, "")
 	}
-	return &url
+	return url
 }
 
 func getProviderUrl(invoker protocol.Invoker) *common.URL {
@@ -400,14 +404,12 @@ type wrappedInvoker struct {
 func newWrappedInvoker(invoker protocol.Invoker, url *common.URL) *wrappedInvoker {
 	return &wrappedInvoker{
 		invoker:     invoker,
-		BaseInvoker: *protocol.NewBaseInvoker(*url),
+		BaseInvoker: *protocol.NewBaseInvoker(url),
 	}
 }
 
 // Invoke remote service base on URL of wrappedInvoker
 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(ctx, invocation)
 }
 
diff --git a/registry/protocol/protocol_test.go b/registry/protocol/protocol_test.go
index 2d6e0248fddb88bfa5ce19546fb7aed703b0fd3c..0fca55221d54c50645bf253c52ec10725f757db3 100644
--- a/registry/protocol/protocol_test.go
+++ b/registry/protocol/protocol_test.go
@@ -59,7 +59,7 @@ func referNormal(t *testing.T, regProtocol *registryProtocol) {
 		common.WithParamsValue(constant.CLUSTER_KEY, "mock"),
 	)
 
-	url.SubURL = &suburl
+	url.SubURL = suburl
 
 	invoker := regProtocol.Refer(url)
 	assert.IsType(t, &protocol.BaseInvoker{}, invoker)
@@ -84,7 +84,7 @@ func TestMultiRegRefer(t *testing.T) {
 		common.WithParamsValue(constant.CLUSTER_KEY, "mock"),
 	)
 
-	url2.SubURL = &suburl2
+	url2.SubURL = suburl2
 
 	regProtocol.Refer(url2)
 	var count int
@@ -105,7 +105,7 @@ func TestOneRegRefer(t *testing.T) {
 		common.WithParamsValue(constant.CLUSTER_KEY, "mock"),
 	)
 
-	url2.SubURL = &suburl2
+	url2.SubURL = suburl2
 
 	regProtocol.Refer(url2)
 	var count int
@@ -128,13 +128,13 @@ func exporterNormal(t *testing.T, regProtocol *registryProtocol) *common.URL {
 		common.WithParamsValue(constant.VERSION_KEY, "1.0.0"),
 	)
 
-	url.SubURL = &suburl
+	url.SubURL = suburl
 	invoker := protocol.NewBaseInvoker(url)
 	exporter := regProtocol.Export(invoker)
 
 	assert.IsType(t, &protocol.BaseExporter{}, exporter)
 	assert.Equal(t, exporter.GetInvoker().GetUrl().String(), suburl.String())
-	return &url
+	return url
 }
 
 func TestExporter(t *testing.T) {
@@ -153,7 +153,7 @@ func TestMultiRegAndMultiProtoExporter(t *testing.T) {
 		common.WithParamsValue(constant.CLUSTER_KEY, "mock"),
 	)
 
-	url2.SubURL = &suburl2
+	url2.SubURL = suburl2
 	invoker2 := protocol.NewBaseInvoker(url2)
 	regProtocol.Export(invoker2)
 
@@ -184,7 +184,7 @@ func TestOneRegAndProtoExporter(t *testing.T) {
 		common.WithParamsValue(constant.VERSION_KEY, "1.0.0"),
 	)
 
-	url2.SubURL = &suburl2
+	url2.SubURL = suburl2
 	invoker2 := protocol.NewBaseInvoker(url2)
 	regProtocol.Export(invoker2)
 
@@ -253,7 +253,7 @@ func TestExportWithOverrideListener(t *testing.T) {
 func TestExportWithServiceConfig(t *testing.T) {
 	extension.SetDefaultConfigurator(configurator.NewMockConfigurator)
 	ccUrl, _ := common.NewURL("mock://127.0.0.1:1111")
-	dc, _ := (&config_center.MockDynamicConfigurationFactory{}).GetDynamicConfiguration(&ccUrl)
+	dc, _ := (&config_center.MockDynamicConfigurationFactory{}).GetDynamicConfiguration(ccUrl)
 	common_cfg.GetEnvInstance().SetDynamicConfiguration(dc)
 	regProtocol := newRegistryProtocol()
 	url := exporterNormal(t, regProtocol)
@@ -265,6 +265,7 @@ func TestExportWithServiceConfig(t *testing.T) {
 
 	newUrl := url.SubURL.Clone()
 	newUrl.SetParam(constant.CLUSTER_KEY, "mock1")
+
 	v2, _ := regProtocol.bounds.Load(getCacheKey(newUrl))
 	assert.NotNil(t, v2)
 }
@@ -272,7 +273,7 @@ func TestExportWithServiceConfig(t *testing.T) {
 func TestExportWithApplicationConfig(t *testing.T) {
 	extension.SetDefaultConfigurator(configurator.NewMockConfigurator)
 	ccUrl, _ := common.NewURL("mock://127.0.0.1:1111")
-	dc, _ := (&config_center.MockDynamicConfigurationFactory{}).GetDynamicConfiguration(&ccUrl)
+	dc, _ := (&config_center.MockDynamicConfigurationFactory{}).GetDynamicConfiguration(ccUrl)
 	common_cfg.GetEnvInstance().SetDynamicConfiguration(dc)
 	regProtocol := newRegistryProtocol()
 	url := exporterNormal(t, regProtocol)
@@ -290,7 +291,7 @@ func TestExportWithApplicationConfig(t *testing.T) {
 
 func TestGetProviderUrlWithHideKey(t *testing.T) {
 	url, _ := common.NewURL("dubbo://127.0.0.1:1111?a=a1&b=b1&.c=c1&.d=d1&e=e1&protocol=registry")
-	providerUrl := getUrlToRegistry(&url, &url)
+	providerUrl := getUrlToRegistry(url, url)
 	assert.NotContains(t, providerUrl.GetParams(), ".c")
 	assert.NotContains(t, providerUrl.GetParams(), ".d")
 	assert.Contains(t, providerUrl.GetParams(), "a")
diff --git a/registry/registry.go b/registry/registry.go
index 5e77eab186680671f27b44bbe2e6a6b964a28721..439178390a2fcedba3bd1c9919d9a797f44e3a21 100644
--- a/registry/registry.go
+++ b/registry/registry.go
@@ -30,38 +30,54 @@ import (
 // Registry Extension - Registry
 type Registry interface {
 	common.Node
-	//used for service provider calling , register services to registry
-	//And it is also used for service consumer calling , register services cared about ,for dubbo's admin monitoring.
-	Register(url common.URL) error
+
+	// Register is used for service provider calling, register services
+	// to registry. And it is also used for service consumer calling, register
+	// services cared about, for dubbo's admin monitoring.
+	Register(url *common.URL) error
 
 	// UnRegister is required to support the contract:
-	// 1. If it is the persistent stored data of dynamic=false, the registration data can not be found, then the IllegalStateException is thrown, otherwise it is ignored.
+	// 1. If it is the persistent stored data of dynamic=false, the
+	//    registration data can not be found, then the IllegalStateException
+	//    is thrown, otherwise it is ignored.
 	// 2. Unregister according to the full url match.
-	// url Registration information , is not allowed to be empty, e.g: dubbo://10.20.153.10/org.apache.dubbo.foo.BarService?version=1.0.0&application=kylin
-	UnRegister(url common.URL) error
-
-	//When creating new registry extension,pls select one of the following modes.
-	//Will remove in dubbogo version v1.1.0
-	//mode1 : return Listener with Next function which can return subscribe service event from registry
-	//Deprecated!
-	//subscribe(event.URL) (Listener, error)
+	// url Registration information, is not allowed to be empty, e.g:
+	// dubbo://10.20.153.10/org.apache.dubbo.foo.BarService?version=1.0.0&application=kylin
+	UnRegister(url *common.URL) error
 
-	//Will replace mode1 in dubbogo version v1.1.0
-	//mode2 : callback mode, subscribe with notify(notify listener).
+	// Subscribe is required to support the contract:
+	// When creating new registry extension, pls select one of the
+	// following modes.
+	// Will remove in dubbogo version v1.1.0
+	// mode1: return Listener with Next function which can return
+	//        subscribe service event from registry
+	// Deprecated!
+	// subscribe(event.URL) (Listener, error)
+	// Will replace mode1 in dubbogo version v1.1.0
+	// mode2: callback mode, subscribe with notify(notify listener).
 	Subscribe(*common.URL, NotifyListener) error
 
 	// UnSubscribe is required to support the contract:
 	// 1. If don't subscribe, ignore it directly.
 	// 2. Unsubscribe by full URL match.
-	// url      Subscription condition, not allowed to be empty, e.g. consumer://10.20.153.10/org.apache.dubbo.foo.BarService?version=1.0.0&application=kylin
+	// url Subscription condition, not allowed to be empty, e.g.
+	// consumer://10.20.153.10/org.apache.dubbo.foo.BarService?version=1.0.0&application=kylin
 	// listener A listener of the change event, not allowed to be empty
 	UnSubscribe(*common.URL, NotifyListener) error
 }
 
 // nolint
 type NotifyListener interface {
-	// Notify supports notifications on the service interface and the dimension of the data type.
+	// Notify supports notifications on the service interface and the dimension of the data type. When a list of
+	// events are passed in, it's considered as a complete list, on the other side, if one single event is
+	// passed in, then it's a incremental event. Pls. note when a list (instead of single event) comes,
+	// the impl of NotifyListener may abandon the accumulated result from previous notifications.
 	Notify(*ServiceEvent)
+	// NotifyAll the events are complete Service Event List.
+	// The argument of events []*ServiceEvent is equal to urls []*URL, The Action of serviceEvent should be EventTypeUpdate.
+	// If your registry center can only get all urls but can't get individual event, you should use this one.
+	// After notify the address, the callback func will be invoked.
+	NotifyAll([]*ServiceEvent, func())
 }
 
 // Listener Deprecated!
diff --git a/registry/servicediscovery/instance/random/random_service_instance_selector.go b/registry/servicediscovery/instance/random/random_service_instance_selector.go
index 3f8f30dc8e9e91f9c75f8ff0611c98bb2f0c7b85..7e4e0eefbbe7f9ce39ee5da1e9f6a9d8ad61c737 100644
--- a/registry/servicediscovery/instance/random/random_service_instance_selector.go
+++ b/registry/servicediscovery/instance/random/random_service_instance_selector.go
@@ -41,7 +41,7 @@ func NewRandomServiceInstanceSelector() instance.ServiceInstanceSelector {
 	return &RandomServiceInstanceSelector{}
 }
 
-func (r *RandomServiceInstanceSelector) Select(url common.URL, serviceInstances []registry.ServiceInstance) registry.ServiceInstance {
+func (r *RandomServiceInstanceSelector) Select(url *common.URL, serviceInstances []registry.ServiceInstance) registry.ServiceInstance {
 	if len(serviceInstances) == 0 {
 		return nil
 	}
diff --git a/registry/servicediscovery/instance/random/random_service_instance_selector_test.go b/registry/servicediscovery/instance/random/random_service_instance_selector_test.go
index cddeb42c904131cdc6a62e5142de850410a3ec5a..c53c058be912ba6fd2a66e42883914fdf1d60eb1 100644
--- a/registry/servicediscovery/instance/random/random_service_instance_selector_test.go
+++ b/registry/servicediscovery/instance/random/random_service_instance_selector_test.go
@@ -52,5 +52,5 @@ func TestRandomServiceInstanceSelector_Select(t *testing.T) {
 			Metadata:    nil,
 		},
 	}
-	assert.NotNil(t, selector.Select(common.URL{}, serviceInstances))
+	assert.NotNil(t, selector.Select(&common.URL{}, serviceInstances))
 }
diff --git a/registry/servicediscovery/instance/service_instance_selector.go b/registry/servicediscovery/instance/service_instance_selector.go
index 82fb3458be2838e9a5780e95be71aa89039b664f..5690ab6c9013d82b1a6ee87797dd7e424e5c07b6 100644
--- a/registry/servicediscovery/instance/service_instance_selector.go
+++ b/registry/servicediscovery/instance/service_instance_selector.go
@@ -24,5 +24,5 @@ import (
 
 type ServiceInstanceSelector interface {
 	//Select an instance of ServiceInstance by the specified ServiceInstance service instances
-	Select(url common.URL, serviceInstances []registry.ServiceInstance) registry.ServiceInstance
+	Select(url *common.URL, serviceInstances []registry.ServiceInstance) registry.ServiceInstance
 }
diff --git a/registry/servicediscovery/service_discovery_registry.go b/registry/servicediscovery/service_discovery_registry.go
index 7576804eb563e16a043f63f17db2532f48c878f1..ad6ec981ded9e224cecc0fa37c8e7f8c0254a932 100644
--- a/registry/servicediscovery/service_discovery_registry.go
+++ b/registry/servicediscovery/service_discovery_registry.go
@@ -26,7 +26,6 @@ import (
 )
 
 import (
-	cm "github.com/Workiva/go-datastructures/common"
 	gxset "github.com/dubbogo/gost/container/set"
 	perrors "github.com/pkg/errors"
 	"go.uber.org/atomic"
@@ -70,12 +69,12 @@ type serviceDiscoveryRegistry struct {
 	metaDataService                  service.MetadataService
 	registeredListeners              *gxset.HashSet
 	subscribedURLsSynthesizers       []synthesizer.SubscribedURLsSynthesizer
-	serviceRevisionExportedURLsCache map[string]map[string][]common.URL
+	serviceRevisionExportedURLsCache map[string]map[string][]*common.URL
 }
 
 func newServiceDiscoveryRegistry(url *common.URL) (registry.Registry, error) {
 
-	tryInitMetadataService()
+	tryInitMetadataService(url)
 
 	serviceDiscovery, err := creatServiceDiscovery(url)
 	if err != nil {
@@ -94,13 +93,13 @@ func newServiceDiscoveryRegistry(url *common.URL) (registry.Registry, error) {
 		subscribedServices:               subscribedServices,
 		subscribedURLsSynthesizers:       subscribedURLsSynthesizers,
 		registeredListeners:              gxset.NewSet(),
-		serviceRevisionExportedURLsCache: make(map[string]map[string][]common.URL, 8),
+		serviceRevisionExportedURLsCache: make(map[string]map[string][]*common.URL, 8),
 		serviceNameMapping:               serviceNameMapping,
 		metaDataService:                  metaDataService,
 	}, nil
 }
 
-func (s *serviceDiscoveryRegistry) UnRegister(url common.URL) error {
+func (s *serviceDiscoveryRegistry) UnRegister(url *common.URL) error {
 	if !shouldRegister(url) {
 		return nil
 	}
@@ -108,10 +107,10 @@ func (s *serviceDiscoveryRegistry) UnRegister(url common.URL) error {
 }
 
 func (s *serviceDiscoveryRegistry) UnSubscribe(url *common.URL, listener registry.NotifyListener) error {
-	if !shouldSubscribe(*url) {
+	if !shouldSubscribe(url) {
 		return nil
 	}
-	return s.metaDataService.UnsubscribeURL(*url)
+	return s.metaDataService.UnsubscribeURL(url)
 }
 
 func creatServiceDiscovery(url *common.URL) (registry.ServiceDiscovery, error) {
@@ -145,8 +144,8 @@ func (s *serviceDiscoveryRegistry) GetServiceDiscovery() registry.ServiceDiscove
 	return s.serviceDiscovery
 }
 
-func (s *serviceDiscoveryRegistry) GetUrl() common.URL {
-	return *s.url
+func (s *serviceDiscoveryRegistry) GetUrl() *common.URL {
+	return s.url
 }
 
 func (s *serviceDiscoveryRegistry) IsAvailable() bool {
@@ -161,7 +160,7 @@ func (s *serviceDiscoveryRegistry) Destroy() {
 	}
 }
 
-func (s *serviceDiscoveryRegistry) Register(url common.URL) error {
+func (s *serviceDiscoveryRegistry) Register(url *common.URL) error {
 	if !shouldRegister(url) {
 		return nil
 	}
@@ -185,7 +184,7 @@ func (s *serviceDiscoveryRegistry) Register(url common.URL) error {
 		url.Protocol)
 }
 
-func shouldRegister(url common.URL) bool {
+func shouldRegister(url *common.URL) bool {
 	side := url.GetParam(constant.SIDE_KEY, "")
 	if side == constant.PROVIDER_PROTOCOL {
 		return true
@@ -195,14 +194,14 @@ func shouldRegister(url common.URL) bool {
 }
 
 func (s *serviceDiscoveryRegistry) Subscribe(url *common.URL, notify registry.NotifyListener) error {
-	if !shouldSubscribe(*url) {
+	if !shouldSubscribe(url) {
 		return nil
 	}
-	_, err := s.metaDataService.SubscribeURL(*url)
+	_, err := s.metaDataService.SubscribeURL(url)
 	if err != nil {
 		return perrors.WithMessage(err, "subscribe url error: "+url.String())
 	}
-	services := s.getServices(*url)
+	services := s.getServices(url)
 	if services.Empty() {
 		return perrors.Errorf("Should has at least one way to know which services this interface belongs to, "+
 			"subscription url:%s", url.String())
@@ -218,12 +217,12 @@ func (s *serviceDiscoveryRegistry) Subscribe(url *common.URL, notify registry.No
 				serviceDiscoveryRegistry: s,
 			},
 		}
-		s.registerServiceInstancesChangedListener(*url, listener)
+		s.registerServiceInstancesChangedListener(url, listener)
 	}
 	return nil
 }
 
-func (s *serviceDiscoveryRegistry) registerServiceInstancesChangedListener(url common.URL, listener *registry.ServiceInstancesChangedListener) {
+func (s *serviceDiscoveryRegistry) registerServiceInstancesChangedListener(url *common.URL, listener *registry.ServiceInstancesChangedListener) {
 	listenerId := listener.ServiceName + ":" + getUrlKey(url)
 	if !s.subscribedServices.Contains(listenerId) {
 		err := s.serviceDiscovery.AddListener(listener)
@@ -234,7 +233,7 @@ func (s *serviceDiscoveryRegistry) registerServiceInstancesChangedListener(url c
 
 }
 
-func getUrlKey(url common.URL) string {
+func getUrlKey(url *common.URL) string {
 	var bf bytes.Buffer
 	if len(url.Protocol) != 0 {
 		bf.WriteString(url.Protocol)
@@ -256,7 +255,7 @@ func getUrlKey(url common.URL) string {
 	return bf.String()
 }
 
-func appendParam(buffer bytes.Buffer, paramKey string, url common.URL) {
+func appendParam(buffer bytes.Buffer, paramKey string, url *common.URL) {
 	buffer.WriteString(paramKey)
 	buffer.WriteString("=")
 	buffer.WriteString(url.GetParam(paramKey, ""))
@@ -268,8 +267,8 @@ func (s *serviceDiscoveryRegistry) subscribe(url *common.URL, notify registry.No
 		logger.Warnf("here is no instance in service[name : %s]", serviceName)
 		return
 	}
-	var subscribedURLs []common.URL
-	subscribedURLs = append(subscribedURLs, s.getExportedUrls(*url, serviceInstances)...)
+	var subscribedURLs []*common.URL
+	subscribedURLs = append(subscribedURLs, s.getExportedUrls(url, serviceInstances)...)
 	if len(subscribedURLs) == 0 {
 		subscribedURLs = s.synthesizeSubscribedURLs(url, serviceInstances)
 	}
@@ -282,8 +281,8 @@ func (s *serviceDiscoveryRegistry) subscribe(url *common.URL, notify registry.No
 
 }
 
-func (s *serviceDiscoveryRegistry) synthesizeSubscribedURLs(subscribedURL *common.URL, serviceInstances []registry.ServiceInstance) []common.URL {
-	var urls []common.URL
+func (s *serviceDiscoveryRegistry) synthesizeSubscribedURLs(subscribedURL *common.URL, serviceInstances []registry.ServiceInstance) []*common.URL {
+	var urls []*common.URL
 	for _, syn := range s.subscribedURLsSynthesizers {
 		if syn.Support(subscribedURL) {
 			urls = append(urls, syn.Synthesize(subscribedURL, serviceInstances)...)
@@ -292,11 +291,11 @@ func (s *serviceDiscoveryRegistry) synthesizeSubscribedURLs(subscribedURL *commo
 	return urls
 }
 
-func shouldSubscribe(url common.URL) bool {
+func shouldSubscribe(url *common.URL) bool {
 	return !shouldRegister(url)
 }
 
-func (s *serviceDiscoveryRegistry) getServices(url common.URL) *gxset.HashSet {
+func (s *serviceDiscoveryRegistry) getServices(url *common.URL) *gxset.HashSet {
 	services := gxset.NewSet()
 	serviceNames := url.GetParam(constant.PROVIDER_BY, "")
 	if len(serviceNames) > 0 {
@@ -311,7 +310,7 @@ func (s *serviceDiscoveryRegistry) getServices(url common.URL) *gxset.HashSet {
 	return services
 }
 
-func (s *serviceDiscoveryRegistry) findMappedServices(url common.URL) *gxset.HashSet {
+func (s *serviceDiscoveryRegistry) findMappedServices(url *common.URL) *gxset.HashSet {
 	serviceInterface := url.GetParam(constant.INTERFACE_KEY, url.Path)
 	group := url.GetParam(constant.GROUP_KEY, "")
 	version := url.GetParam(constant.VERSION_KEY, "")
@@ -325,7 +324,7 @@ func (s *serviceDiscoveryRegistry) findMappedServices(url common.URL) *gxset.Has
 	return serviceNames
 }
 
-func (s *serviceDiscoveryRegistry) getExportedUrls(subscribedURL common.URL, serviceInstances []registry.ServiceInstance) []common.URL {
+func (s *serviceDiscoveryRegistry) getExportedUrls(subscribedURL *common.URL, serviceInstances []registry.ServiceInstance) []*common.URL {
 	var filterInstances []registry.ServiceInstance
 	for _, s := range serviceInstances {
 		if !s.IsEnable() || !s.IsHealthy() {
@@ -340,32 +339,15 @@ func (s *serviceDiscoveryRegistry) getExportedUrls(subscribedURL common.URL, ser
 		filterInstances = append(filterInstances, s)
 	}
 	if len(filterInstances) == 0 {
-		return []common.URL{}
+		return []*common.URL{}
 	}
 	s.prepareServiceRevisionExportedURLs(filterInstances)
 	subscribedURLs := s.cloneExportedURLs(subscribedURL, filterInstances)
 	return subscribedURLs
 }
 
-// comparator is defined as Comparator for skip list to compare the URL
-type comparator common.URL
-
-// Compare is defined as Comparator for skip list to compare the URL
-func (c comparator) Compare(comp cm.Comparator) int {
-	a := common.URL(c).String()
-	b := common.URL(comp.(comparator)).String()
-	switch {
-	case a > b:
-		return 1
-	case a < b:
-		return -1
-	default:
-		return 0
-	}
-}
-
-func (s *serviceDiscoveryRegistry) getExportedUrlsByInst(serviceInstance registry.ServiceInstance) []common.URL {
-	var urls []common.URL
+func (s *serviceDiscoveryRegistry) getExportedUrlsByInst(serviceInstance registry.ServiceInstance) []*common.URL {
+	var urls []*common.URL
 	metadataStorageType := getExportedStoreType(serviceInstance)
 	proxyFactory := extension.GetMetadataServiceProxyFactory(metadataStorageType)
 	if proxyFactory == nil {
@@ -381,7 +363,7 @@ func (s *serviceDiscoveryRegistry) getExportedUrlsByInst(serviceInstance registr
 		return urls
 	}
 
-	ret := make([]common.URL, 0, len(result))
+	ret := make([]*common.URL, 0, len(result))
 	for _, ui := range result {
 
 		u, err := common.NewURL(ui.(string))
@@ -464,18 +446,18 @@ func (s *serviceDiscoveryRegistry) selectServiceInstance(serviceInstances []regi
 		logger.Errorf("get service instance selector cathe error:%s", err.Error())
 		return nil
 	}
-	return selector.Select(*s.url, serviceInstances)
+	return selector.Select(s.url, serviceInstances)
 }
 
-func (s *serviceDiscoveryRegistry) initRevisionExportedURLsByInst(serviceInstance registry.ServiceInstance) []common.URL {
+func (s *serviceDiscoveryRegistry) initRevisionExportedURLsByInst(serviceInstance registry.ServiceInstance) []*common.URL {
 	if serviceInstance == nil {
-		return []common.URL{}
+		return nil
 	}
 	serviceName := serviceInstance.GetServiceName()
 	revision := getExportedServicesRevision(serviceInstance)
 	revisionExportedURLsMap := s.serviceRevisionExportedURLsCache[serviceName]
 	if revisionExportedURLsMap == nil {
-		revisionExportedURLsMap = make(map[string][]common.URL, 4)
+		revisionExportedURLsMap = make(map[string][]*common.URL, 4)
 		s.serviceRevisionExportedURLsCache[serviceName] = revisionExportedURLsMap
 	}
 	revisionExportedURLs := revisionExportedURLsMap[revision]
@@ -521,11 +503,11 @@ func getExportedStoreType(serviceInstance registry.ServiceInstance) string {
 	return result
 }
 
-func (s *serviceDiscoveryRegistry) cloneExportedURLs(url common.URL, serviceInsances []registry.ServiceInstance) []common.URL {
+func (s *serviceDiscoveryRegistry) cloneExportedURLs(url *common.URL, serviceInsances []registry.ServiceInstance) []*common.URL {
 	if len(serviceInsances) == 0 {
-		return []common.URL{}
+		return []*common.URL{}
 	}
-	var clonedExportedURLs []common.URL
+	var clonedExportedURLs []*common.URL
 	removeParamSet := gxset.NewSet()
 	removeParamSet.Add(constant.PID_KEY)
 	removeParamSet.Add(constant.TIMESTAMP_KEY)
@@ -540,7 +522,7 @@ func (s *serviceDiscoveryRegistry) cloneExportedURLs(url common.URL, serviceInsa
 			}
 
 			cloneUrl := u.CloneExceptParams(removeParamSet)
-			clonedExportedURLs = append(clonedExportedURLs, *cloneUrl)
+			clonedExportedURLs = append(clonedExportedURLs, cloneUrl)
 		}
 	}
 	return clonedExportedURLs
@@ -548,8 +530,8 @@ func (s *serviceDiscoveryRegistry) cloneExportedURLs(url common.URL, serviceInsa
 }
 
 type endpoint struct {
-	Port     int    `json:"port, omitempty"`
-	Protocol string `json:"protocol, omitempty"`
+	Port     int    `json:"port,omitempty"`
+	Protocol string `json:"protocol,omitempty"`
 }
 
 func getProtocolPort(serviceInstance registry.ServiceInstance, protocol string) int {
@@ -571,38 +553,38 @@ func getProtocolPort(serviceInstance registry.ServiceInstance, protocol string)
 	}
 	return -1
 }
-func (s *serviceDiscoveryRegistry) getTemplateExportedURLs(url common.URL, serviceInstance registry.ServiceInstance) []common.URL {
+func (s *serviceDiscoveryRegistry) getTemplateExportedURLs(url *common.URL, serviceInstance registry.ServiceInstance) []*common.URL {
 	exportedURLs := s.getRevisionExportedURLs(serviceInstance)
 	if len(exportedURLs) == 0 {
-		return []common.URL{}
+		return []*common.URL{}
 	}
 	return filterSubscribedURLs(url, exportedURLs)
 }
 
-func (s *serviceDiscoveryRegistry) getRevisionExportedURLs(serviceInstance registry.ServiceInstance) []common.URL {
+func (s *serviceDiscoveryRegistry) getRevisionExportedURLs(serviceInstance registry.ServiceInstance) []*common.URL {
 	if serviceInstance == nil {
-		return []common.URL{}
+		return []*common.URL{}
 	}
 	serviceName := serviceInstance.GetServiceName()
 	revision := getExportedServicesRevision(serviceInstance)
 	s.lock.RLock()
 	revisionExportedURLsMap, exist := s.serviceRevisionExportedURLsCache[serviceName]
 	if !exist {
-		return []common.URL{}
+		return []*common.URL{}
 	}
 	exportedURLs, exist := revisionExportedURLsMap[revision]
 	if !exist {
-		return []common.URL{}
+		return []*common.URL{}
 	}
 	s.lock.RUnlock()
 	// Get a copy from source in order to prevent the caller trying to change the cached data
-	cloneExportedURLs := make([]common.URL, len(exportedURLs))
+	cloneExportedURLs := make([]*common.URL, len(exportedURLs))
 	copy(cloneExportedURLs, exportedURLs)
 	return cloneExportedURLs
 }
 
-func filterSubscribedURLs(subscribedURL common.URL, exportedURLs []common.URL) []common.URL {
-	var filterExportedURLs []common.URL
+func filterSubscribedURLs(subscribedURL *common.URL, exportedURLs []*common.URL) []*common.URL {
+	var filterExportedURLs []*common.URL
 	for _, url := range exportedURLs {
 		if url.GetParam(constant.INTERFACE_KEY, url.Path) != subscribedURL.GetParam(constant.INTERFACE_KEY, url.Path) {
 			break
@@ -642,7 +624,7 @@ var (
 
 // tryInitMetadataService will try to initialize metadata service
 // TODO (move to somewhere)
-func tryInitMetadataService() {
+func tryInitMetadataService(url *common.URL) {
 
 	ms, err := extension.GetMetadataService(config.GetApplicationConfig().MetadataType)
 	if err != nil {
@@ -662,7 +644,7 @@ func tryInitMetadataService() {
 
 	expt := configurable.NewMetadataServiceExporter(ms)
 
-	err = expt.Export()
+	err = expt.Export(url)
 	if err != nil {
 		logger.Errorf("could not export the metadata service", err)
 	}
diff --git a/registry/servicediscovery/service_discovery_registry_test.go b/registry/servicediscovery/service_discovery_registry_test.go
index 53eb86507e635be32eb362519922f7042f945519..ad6b73d3b4da77e5fe21a3085cdc21d3eca0246d 100644
--- a/registry/servicediscovery/service_discovery_registry_test.go
+++ b/registry/servicediscovery/service_discovery_registry_test.go
@@ -76,7 +76,7 @@ func TestServiceDiscoveryRegistry_Register(t *testing.T) {
 		"&service_discovery=mock" +
 		"&methods=getAllServiceKeys,getServiceRestMetadata,getExportedURLs,getAllExportedURLs" +
 		"&side=provider")
-	registry, err := newServiceDiscoveryRegistry(&registryURL)
+	registry, err := newServiceDiscoveryRegistry(registryURL)
 	assert.Nil(t, err)
 	assert.NotNil(t, registry)
 	registry.Register(url)
@@ -85,19 +85,19 @@ func TestServiceDiscoveryRegistry_Register(t *testing.T) {
 type mockEventDispatcher struct {
 }
 
-func (m *mockEventDispatcher) AddEventListener(listener observer.EventListener) {
+func (m *mockEventDispatcher) AddEventListener(observer.EventListener) {
 
 }
 
-func (m *mockEventDispatcher) AddEventListeners(listenersSlice []observer.EventListener) {
+func (m *mockEventDispatcher) AddEventListeners([]observer.EventListener) {
 
 }
 
-func (m *mockEventDispatcher) RemoveEventListener(listener observer.EventListener) {
+func (m *mockEventDispatcher) RemoveEventListener(observer.EventListener) {
 	panic("implement me")
 }
 
-func (m *mockEventDispatcher) RemoveEventListeners(listenersSlice []observer.EventListener) {
+func (m *mockEventDispatcher) RemoveEventListeners([]observer.EventListener) {
 	panic("implement me")
 }
 
@@ -109,17 +109,17 @@ func (m *mockEventDispatcher) RemoveAllEventListeners() {
 	panic("implement me")
 }
 
-func (m *mockEventDispatcher) Dispatch(event observer.Event) {
+func (m *mockEventDispatcher) Dispatch(observer.Event) {
 }
 
 type mockServiceNameMapping struct {
 }
 
-func (m *mockServiceNameMapping) Map(serviceInterface string, group string, version string, protocol string) error {
+func (m *mockServiceNameMapping) Map(string, string, string, string) error {
 	return nil
 }
 
-func (m *mockServiceNameMapping) Get(serviceInterface string, group string, version string, protocol string) (*gxset.HashSet, error) {
+func (m *mockServiceNameMapping) Get(string, string, string, string) (*gxset.HashSet, error) {
 	panic("implement me")
 }
 
@@ -134,15 +134,15 @@ func (m *mockServiceDiscovery) Destroy() error {
 	panic("implement me")
 }
 
-func (m *mockServiceDiscovery) Register(instance registry.ServiceInstance) error {
+func (m *mockServiceDiscovery) Register(registry.ServiceInstance) error {
 	return nil
 }
 
-func (m *mockServiceDiscovery) Update(instance registry.ServiceInstance) error {
+func (m *mockServiceDiscovery) Update(registry.ServiceInstance) error {
 	panic("implement me")
 }
 
-func (m *mockServiceDiscovery) Unregister(instance registry.ServiceInstance) error {
+func (m *mockServiceDiscovery) Unregister(registry.ServiceInstance) error {
 	panic("implement me")
 }
 
@@ -154,35 +154,35 @@ func (m *mockServiceDiscovery) GetServices() *gxset.HashSet {
 	panic("implement me")
 }
 
-func (m *mockServiceDiscovery) GetInstances(serviceName string) []registry.ServiceInstance {
+func (m *mockServiceDiscovery) GetInstances(string) []registry.ServiceInstance {
 	panic("implement me")
 }
 
-func (m *mockServiceDiscovery) GetInstancesByPage(serviceName string, offset int, pageSize int) gxpage.Pager {
+func (m *mockServiceDiscovery) GetInstancesByPage(string, int, int) gxpage.Pager {
 	panic("implement me")
 }
 
-func (m *mockServiceDiscovery) GetHealthyInstancesByPage(serviceName string, offset int, pageSize int, healthy bool) gxpage.Pager {
+func (m *mockServiceDiscovery) GetHealthyInstancesByPage(string, int, int, bool) gxpage.Pager {
 	panic("implement me")
 }
 
-func (m *mockServiceDiscovery) GetRequestInstances(serviceNames []string, offset int, requestedSize int) map[string]gxpage.Pager {
+func (m *mockServiceDiscovery) GetRequestInstances([]string, int, int) map[string]gxpage.Pager {
 	panic("implement me")
 }
 
-func (m *mockServiceDiscovery) AddListener(listener *registry.ServiceInstancesChangedListener) error {
+func (m *mockServiceDiscovery) AddListener(*registry.ServiceInstancesChangedListener) error {
 	panic("implement me")
 }
 
-func (m *mockServiceDiscovery) DispatchEventByServiceName(serviceName string) error {
+func (m *mockServiceDiscovery) DispatchEventByServiceName(string) error {
 	panic("implement me")
 }
 
-func (m *mockServiceDiscovery) DispatchEventForInstances(serviceName string, instances []registry.ServiceInstance) error {
+func (m *mockServiceDiscovery) DispatchEventForInstances(string, []registry.ServiceInstance) error {
 	panic("implement me")
 }
 
-func (m *mockServiceDiscovery) DispatchEvent(event *registry.ServiceInstancesChangedEvent) error {
+func (m *mockServiceDiscovery) DispatchEvent(*registry.ServiceInstancesChangedEvent) error {
 	panic("implement me")
 }
 
@@ -197,27 +197,27 @@ func (m *mockMetadataService) ServiceName() (string, error) {
 	panic("implement me")
 }
 
-func (m *mockMetadataService) ExportURL(url common.URL) (bool, error) {
+func (m *mockMetadataService) ExportURL(*common.URL) (bool, error) {
 	return true, nil
 }
 
-func (m *mockMetadataService) UnexportURL(url common.URL) error {
+func (m *mockMetadataService) UnexportURL(*common.URL) error {
 	panic("implement me")
 }
 
-func (m *mockMetadataService) SubscribeURL(url common.URL) (bool, error) {
+func (m *mockMetadataService) SubscribeURL(*common.URL) (bool, error) {
 	panic("implement me")
 }
 
-func (m *mockMetadataService) UnsubscribeURL(url common.URL) error {
+func (m *mockMetadataService) UnsubscribeURL(*common.URL) error {
 	panic("implement me")
 }
 
-func (m *mockMetadataService) PublishServiceDefinition(url common.URL) error {
+func (m *mockMetadataService) PublishServiceDefinition(*common.URL) error {
 	return nil
 }
 
-func (m *mockMetadataService) GetExportedURLs(serviceInterface string, group string, version string, protocol string) ([]interface{}, error) {
+func (m *mockMetadataService) GetExportedURLs(string, string, string, string) ([]interface{}, error) {
 	panic("implement me")
 }
 
@@ -225,19 +225,19 @@ func (m *mockMetadataService) MethodMapper() map[string]string {
 	panic("implement me")
 }
 
-func (m *mockMetadataService) GetSubscribedURLs() ([]common.URL, error) {
+func (m *mockMetadataService) GetSubscribedURLs() ([]*common.URL, error) {
 	panic("implement me")
 }
 
-func (m *mockMetadataService) GetServiceDefinition(interfaceName string, group string, version string) (string, error) {
+func (m *mockMetadataService) GetServiceDefinition(string, string, string) (string, error) {
 	panic("implement me")
 }
 
-func (m *mockMetadataService) GetServiceDefinitionByServiceKey(serviceKey string) (string, error) {
+func (m *mockMetadataService) GetServiceDefinitionByServiceKey(string) (string, error) {
 	panic("implement me")
 }
 
-func (m *mockMetadataService) RefreshMetadata(exportedRevision string, subscribedRevision string) (bool, error) {
+func (m *mockMetadataService) RefreshMetadata(string, string) (bool, error) {
 	panic("implement me")
 }
 
diff --git a/registry/servicediscovery/synthesizer/rest/rest_subscribed_urls_synthesizer.go b/registry/servicediscovery/synthesizer/rest/rest_subscribed_urls_synthesizer.go
index 086a26de58f8472e35e07a8a174fdee86afa82f2..d1ab6113b31a779529cc7e33ffd0f14233dd9ad6 100644
--- a/registry/servicediscovery/synthesizer/rest/rest_subscribed_urls_synthesizer.go
+++ b/registry/servicediscovery/synthesizer/rest/rest_subscribed_urls_synthesizer.go
@@ -44,8 +44,8 @@ func (r RestSubscribedURLsSynthesizer) Support(subscribedURL *common.URL) bool {
 	return false
 }
 
-func (r RestSubscribedURLsSynthesizer) Synthesize(subscribedURL *common.URL, serviceInstances []registry.ServiceInstance) []common.URL {
-	urls := make([]common.URL, len(serviceInstances), len(serviceInstances))
+func (r RestSubscribedURLsSynthesizer) Synthesize(subscribedURL *common.URL, serviceInstances []registry.ServiceInstance) []*common.URL {
+	urls := make([]*common.URL, len(serviceInstances), len(serviceInstances))
 	for i, s := range serviceInstances {
 		splitHost := strings.Split(s.GetHost(), ":")
 		u := common.NewURLWithOptions(common.WithProtocol(subscribedURL.Protocol), common.WithIp(splitHost[0]),
@@ -55,7 +55,7 @@ func (r RestSubscribedURLsSynthesizer) Synthesize(subscribedURL *common.URL, ser
 			common.WithParamsValue(constant.APPLICATION_KEY, s.GetServiceName()),
 			common.WithParamsValue(constant.REGISTRY_KEY, "true"),
 		)
-		urls[i] = *u
+		urls[i] = u
 	}
 	return urls
 }
diff --git a/registry/servicediscovery/synthesizer/rest/rest_subscribed_urls_synthesizer_test.go b/registry/servicediscovery/synthesizer/rest/rest_subscribed_urls_synthesizer_test.go
index b52cc2323d6f9ae1bca8cfd1a4c5217af5e25f12..1bb38c92e32942fb1a39bbaeb9617013459bcdfe 100644
--- a/registry/servicediscovery/synthesizer/rest/rest_subscribed_urls_synthesizer_test.go
+++ b/registry/servicediscovery/synthesizer/rest/rest_subscribed_urls_synthesizer_test.go
@@ -56,7 +56,7 @@ func TestRestSubscribedURLsSynthesizer_Synthesize(t *testing.T) {
 		},
 	}
 
-	var expectUrls []common.URL
+	var expectUrls []*common.URL
 	u1 := common.NewURLWithOptions(common.WithProtocol("rest"), common.WithIp("127.0.0.1"),
 		common.WithPort("80"), common.WithPath("org.apache.dubbo-go.mockService"),
 		common.WithParams(url.Values{}),
@@ -69,7 +69,7 @@ func TestRestSubscribedURLsSynthesizer_Synthesize(t *testing.T) {
 		common.WithParamsValue(constant.SIDE_KEY, constant.PROVIDER_PROTOCOL),
 		common.WithParamsValue(constant.APPLICATION_KEY, "test2"),
 		common.WithParamsValue(constant.REGISTRY_KEY, "true"))
-	expectUrls = append(expectUrls, *u1, *u2)
-	result := syn.Synthesize(&subUrl, instances)
+	expectUrls = append(expectUrls, u1, u2)
+	result := syn.Synthesize(subUrl, instances)
 	assert.Equal(t, expectUrls, result)
 }
diff --git a/registry/servicediscovery/synthesizer/subscribed_urls_synthesizer.go b/registry/servicediscovery/synthesizer/subscribed_urls_synthesizer.go
index 415ca35fbad2fa335d687dc7a7718fa3a4b2b487..557c86ec127fe88411fdbe9dd83d43ff8cd18cec 100644
--- a/registry/servicediscovery/synthesizer/subscribed_urls_synthesizer.go
+++ b/registry/servicediscovery/synthesizer/subscribed_urls_synthesizer.go
@@ -27,5 +27,5 @@ type SubscribedURLsSynthesizer interface {
 	// Supports the synthesis of the subscribed url or not
 	Support(subscribedURL *common.URL) bool
 	// synthesize the subscribed url
-	Synthesize(subscribedURL *common.URL, serviceInstances []registry.ServiceInstance) []common.URL
+	Synthesize(subscribedURL *common.URL, serviceInstances []registry.ServiceInstance) []*common.URL
 }
diff --git a/registry/zookeeper/listener.go b/registry/zookeeper/listener.go
index ec82fa0309118fba4b5c21772d4dfd356f3b0c5c..88109fcf52ec27b87f6b1ddf2694a14576272fbe 100644
--- a/registry/zookeeper/listener.go
+++ b/registry/zookeeper/listener.go
@@ -104,6 +104,7 @@ func (l *RegistryDataListener) DataChange(eventType remoting.Event) bool {
 func (l *RegistryDataListener) Close() {
 	l.mutex.Lock()
 	defer l.mutex.Unlock()
+	l.closed = true
 	for _, listener := range l.subscribed {
 		listener.(*RegistryConfigurationListener).Close()
 	}
@@ -158,7 +159,7 @@ func (l *RegistryConfigurationListener) Next() (*registry.ServiceEvent, error) {
 			//r.update(e.res)
 			//write to invoker
 			//r.outerEventCh <- e.res
-			return &registry.ServiceEvent{Action: e.ConfigType, Service: e.Value.(common.URL)}, nil
+			return &registry.ServiceEvent{Action: e.ConfigType, Service: e.Value.(*common.URL)}, nil
 		}
 	}
 }
diff --git a/registry/zookeeper/listener_test.go b/registry/zookeeper/listener_test.go
index a0e9147a9e0ee8767efcf78d5e2aa536140f6a8b..20ec1cf69a65601e5dd8351083ce23edfb67d10c 100644
--- a/registry/zookeeper/listener_test.go
+++ b/registry/zookeeper/listener_test.go
@@ -34,7 +34,7 @@ import (
 func Test_DataChange(t *testing.T) {
 	listener := NewRegistryDataListener()
 	url, _ := common.NewURL("jsonrpc%3A%2F%2F127.0.0.1%3A20001%2Fcom.ikurento.user.UserProvider%3Fanyhost%3Dtrue%26app.version%3D0.0.1%26application%3DBDTService%26category%3Dproviders%26cluster%3Dfailover%26dubbo%3Ddubbo-provider-golang-1.3.0%26environment%3Ddev%26group%3D%26interface%3Dcom.ikurento.user.UserProvider%26ip%3D10.32.20.124%26loadbalance%3Drandom%26methods.GetUser.loadbalance%3Drandom%26methods.GetUser.retries%3D1%26methods.GetUser.weight%3D0%26module%3Ddubbogo%2Buser-info%2Bserver%26name%3DBDTService%26organization%3Dikurento.com%26owner%3DZX%26pid%3D74500%26retries%3D0%26service.filter%3Decho%26side%3Dprovider%26timestamp%3D1560155407%26version%3D%26warmup%3D100")
-	listener.SubscribeURL(&url, &MockConfigurationListener{})
+	listener.SubscribeURL(url, &MockConfigurationListener{})
 	int := listener.DataChange(remoting.Event{Path: "/dubbo/com.ikurento.user.UserProvider/providers/jsonrpc%3A%2F%2F127.0.0.1%3A20001%2Fcom.ikurento.user.UserProvider%3Fanyhost%3Dtrue%26app.version%3D0.0.1%26application%3DBDTService%26category%3Dproviders%26cluster%3Dfailover%26dubbo%3Ddubbo-provider-golang-1.3.0%26environment%3Ddev%26group%3D%26interface%3Dcom.ikurento.user.UserProvider%26ip%3D10.32.20.124%26loadbalance%3Drandom%26methods.GetUser.loadbalance%3Drandom%26methods.GetUser.retries%3D1%26methods.GetUser.weight%3D0%26module%3Ddubbogo%2Buser-info%2Bserver%26name%3DBDTService%26organization%3Dikurento.com%26owner%3DZX%26pid%3D74500%26retries%3D0%26service.filter%3Decho%26side%3Dprovider%26timestamp%3D1560155407%26version%3D%26warmup%3D100"})
 	assert.Equal(t, true, int)
 }
diff --git a/registry/zookeeper/registry.go b/registry/zookeeper/registry.go
index e8ee51beb70b5a08ec60b213c5342ef52972c59f..fe492c2b12712e0935ccd6fdd264e0fe8f24e213 100644
--- a/registry/zookeeper/registry.go
+++ b/registry/zookeeper/registry.go
@@ -127,15 +127,15 @@ func (r *zkRegistry) InitListeners() {
 		oldDataListener := r.dataListener
 		oldDataListener.mutex.Lock()
 		defer oldDataListener.mutex.Unlock()
-		recoverd := r.dataListener.subscribed
-		if recoverd != nil && len(recoverd) > 0 {
+		r.dataListener.closed = true
+		recovered := r.dataListener.subscribed
+		if recovered != nil && len(recovered) > 0 {
 			// recover all subscribed url
-			for _, oldListener := range recoverd {
+			for _, oldListener := range recovered {
 				var (
 					regConfigListener *RegistryConfigurationListener
 					ok                bool
 				)
-
 				if regConfigListener, ok = oldListener.(*RegistryConfigurationListener); ok {
 					regConfigListener.Close()
 				}
@@ -212,6 +212,9 @@ func (r *zkRegistry) registerTempZookeeperNode(root string, node string) error {
 
 	r.cltLock.Lock()
 	defer r.cltLock.Unlock()
+	if r.client == nil {
+		return perrors.WithStack(perrors.New("zk client already been closed"))
+	}
 	err = r.client.Create(root)
 	if err != nil {
 		logger.Errorf("zk.Create(root{%s}) = err{%v}", root, perrors.WithStack(err))
@@ -292,10 +295,10 @@ func (r *zkRegistry) getCloseListener(conf *common.URL) (*RegistryConfigurationL
 	r.dataListener.mutex.Lock()
 	configurationListener := r.dataListener.subscribed[conf.ServiceKey()]
 	if configurationListener != nil {
-
 		zkListener, _ := configurationListener.(*RegistryConfigurationListener)
 		if zkListener != nil {
 			if zkListener.isClosed {
+				r.dataListener.mutex.Unlock()
 				return nil, perrors.New("configListener already been closed")
 			}
 		}
diff --git a/registry/zookeeper/registry_test.go b/registry/zookeeper/registry_test.go
index d915fc2ce10359f0dd1970daf019746ce066f511..e630db7e41092e48592bf99690f11863a23e23d3 100644
--- a/registry/zookeeper/registry_test.go
+++ b/registry/zookeeper/registry_test.go
@@ -37,7 +37,7 @@ func Test_Register(t *testing.T) {
 	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)
+	ts, reg, _ := newMockZkRegistry(regurl)
 	defer ts.Stop()
 	err := reg.Register(url)
 	children, _ := reg.client.GetChildren("/dubbo/com.ikurento.user.UserProvider/providers")
@@ -50,7 +50,7 @@ func Test_UnRegister(t *testing.T) {
 	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)
+	ts, reg, _ := newMockZkRegistry(regurl)
 	defer ts.Stop()
 	err := reg.Register(url)
 	children, _ := reg.client.GetChildren("/dubbo/com.ikurento.user.UserProvider/providers")
@@ -73,7 +73,7 @@ func Test_UnRegister(t *testing.T) {
 func Test_Subscribe(t *testing.T) {
 	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)
+	ts, reg, _ := newMockZkRegistry(regurl)
 
 	//provider register
 	err := reg.Register(url)
@@ -85,10 +85,10 @@ func Test_Subscribe(t *testing.T) {
 
 	//consumer register
 	regurl.SetParam(constant.ROLE_KEY, strconv.Itoa(common.CONSUMER))
-	_, reg2, _ := newMockZkRegistry(&regurl, zookeeper.WithTestCluster(ts))
+	_, reg2, _ := newMockZkRegistry(regurl, zookeeper.WithTestCluster(ts))
 
 	reg2.Register(url)
-	listener, _ := reg2.DoSubscribe(&url)
+	listener, _ := reg2.DoSubscribe(url)
 
 	serviceEvent, _ := listener.Next()
 	assert.NoError(t, err)
@@ -102,7 +102,7 @@ func Test_Subscribe(t *testing.T) {
 func Test_UnSubscribe(t *testing.T) {
 	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)
+	ts, reg, _ := newMockZkRegistry(regurl)
 
 	//provider register
 	err := reg.Register(url)
@@ -114,10 +114,10 @@ func Test_UnSubscribe(t *testing.T) {
 
 	//consumer register
 	regurl.SetParam(constant.ROLE_KEY, strconv.Itoa(common.CONSUMER))
-	_, reg2, _ := newMockZkRegistry(&regurl, zookeeper.WithTestCluster(ts))
+	_, reg2, _ := newMockZkRegistry(regurl, zookeeper.WithTestCluster(ts))
 
 	reg2.Register(url)
-	listener, _ := reg2.DoSubscribe(&url)
+	listener, _ := reg2.DoSubscribe(url)
 
 	serviceEvent, _ := listener.Next()
 	assert.NoError(t, err)
@@ -126,7 +126,7 @@ func Test_UnSubscribe(t *testing.T) {
 	}
 	assert.Regexp(t, ".*ServiceEvent{Action{add}.*", serviceEvent.String())
 
-	reg2.UnSubscribe(&url, nil)
+	reg2.UnSubscribe(url, nil)
 	assert.Nil(t, reg2.listener)
 
 	defer ts.Stop()
@@ -136,13 +136,13 @@ func Test_ConsumerDestory(t *testing.T) {
 	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)
+	ts, reg, err := newMockZkRegistry(regurl)
 	defer ts.Stop()
 
 	assert.NoError(t, err)
 	err = reg.Register(url)
 	assert.NoError(t, err)
-	_, err = reg.DoSubscribe(&url)
+	_, err = reg.DoSubscribe(url)
 	assert.NoError(t, err)
 
 	//listener.Close()
@@ -156,7 +156,7 @@ func Test_ProviderDestory(t *testing.T) {
 	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)
+	ts, reg, err := newMockZkRegistry(regurl)
 	defer ts.Stop()
 
 	assert.NoError(t, err)
diff --git a/registry/zookeeper/service_discovery.go b/registry/zookeeper/service_discovery.go
index 5ad83ef90947afc0a5ca75af5009e8b55b4f6627..6d9582f33a7b2517c4edc96d00d00ad6b57a4835 100644
--- a/registry/zookeeper/service_discovery.go
+++ b/registry/zookeeper/service_discovery.go
@@ -154,8 +154,8 @@ func (zksd *zookeeperServiceDiscovery) RestartCallBack() bool {
 }
 
 // nolint
-func (zksd *zookeeperServiceDiscovery) GetUrl() common.URL {
-	return *zksd.url
+func (zksd *zookeeperServiceDiscovery) GetUrl() *common.URL {
+	return zksd.url
 }
 
 // nolint
diff --git a/remoting/codec.go b/remoting/codec.go
new file mode 100644
index 0000000000000000000000000000000000000000..607d1643cc1967e93bf5288d8d4c0788c73a735e
--- /dev/null
+++ b/remoting/codec.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 remoting
+
+import (
+	"bytes"
+)
+
+// codec for exchangeClient
+type Codec interface {
+	EncodeRequest(request *Request) (*bytes.Buffer, error)
+	EncodeResponse(response *Response) (*bytes.Buffer, error)
+	Decode(data []byte) (DecodeResult, int, error)
+}
+
+type DecodeResult struct {
+	IsRequest bool
+	Result    interface{}
+}
+
+var (
+	codec = make(map[string]Codec, 2)
+)
+
+func RegistryCodec(protocol string, codecTmp Codec) {
+	codec[protocol] = codecTmp
+}
+
+func GetCodec(protocol string) Codec {
+	return codec[protocol]
+}
diff --git a/remoting/etcdv3/client.go b/remoting/etcdv3/client.go
index 4e7436e445f652d8fe1db2991c87303653beb9ec..ebd454242d49ee82c81fe1a1fae1a19980c238a4 100644
--- a/remoting/etcdv3/client.go
+++ b/remoting/etcdv3/client.go
@@ -408,7 +408,8 @@ func (c *Client) keepAliveKV(k string, v string) error {
 		return ErrNilETCDV3Client
 	}
 
-	lease, err := c.rawClient.Grant(c.ctx, int64(time.Second.Seconds()))
+	// make lease time longer, since 1 second is too short
+	lease, err := c.rawClient.Grant(c.ctx, int64(30*time.Second.Seconds()))
 	if err != nil {
 		return perrors.WithMessage(err, "grant lease")
 	}
diff --git a/remoting/etcdv3/facade.go b/remoting/etcdv3/facade.go
index 52b1cce3e4618e9c2669e7a3b37256ebe6d61c41..614ba9ae3a1407daea2d9d534a0474a28ad8cac9 100644
--- a/remoting/etcdv3/facade.go
+++ b/remoting/etcdv3/facade.go
@@ -63,7 +63,7 @@ LOOP:
 			r.ClientLock().Lock()
 			clientName := RegistryETCDV3Client
 			timeout, _ := time.ParseDuration(r.GetUrl().GetParam(constant.REGISTRY_TIMEOUT_KEY, constant.DEFAULT_REG_TIMEOUT))
-			endpoint := r.GetUrl().Location
+			endpoints := r.Client().endpoints
 			r.Client().Close()
 			r.SetClient(nil)
 			r.ClientLock().Unlock()
@@ -80,11 +80,11 @@ LOOP:
 				err = ValidateClient(
 					r,
 					WithName(clientName),
-					WithEndpoints(endpoint),
+					WithEndpoints(endpoints...),
 					WithTimeout(timeout),
 				)
 				logger.Infof("ETCDV3ProviderRegistry.validateETCDV3Client(etcd Addr{%s}) = error{%#v}",
-					endpoint, perrors.WithStack(err))
+					endpoints, perrors.WithStack(err))
 				if err == nil && r.RestartCallBack() {
 					break
 				}
diff --git a/remoting/etcdv3/listener.go b/remoting/etcdv3/listener.go
index 4f80a89dfb713036a5d4d812bc7a2d5551f42284..c66928a6367cb2449de79a51b59d122a74a79911 100644
--- a/remoting/etcdv3/listener.go
+++ b/remoting/etcdv3/listener.go
@@ -129,8 +129,6 @@ func (l *EventListener) handleEvents(event *clientv3.Event, listeners ...remotin
 	default:
 		return false
 	}
-
-	panic("unreachable")
 }
 
 // ListenServiceNodeEventWithPrefix listens on a set of key with spec prefix
diff --git a/remoting/exchange.go b/remoting/exchange.go
new file mode 100644
index 0000000000000000000000000000000000000000..5fbd8ae9b449103d5f7e3b19db19bc6a9c73593d
--- /dev/null
+++ b/remoting/exchange.go
@@ -0,0 +1,193 @@
+/*
+ * 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 remoting
+
+import (
+	"sync"
+	"time"
+)
+
+import (
+	"go.uber.org/atomic"
+)
+
+import (
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/common/logger"
+)
+
+var (
+	// generate request ID for global use
+	sequence atomic.Int64
+
+	// store requestID and response
+	pendingResponses = new(sync.Map)
+)
+
+type SequenceType int64
+
+func init() {
+	// init request ID
+	sequence.Store(0)
+}
+
+func SequenceId() int64 {
+	// increse 2 for every request as the same before.
+	// We expect that the request from client to server, the requestId is even; but from server to client, the requestId is odd.
+	return sequence.Add(2)
+}
+
+// this is request for transport layer
+type Request struct {
+	ID int64
+	// protocol version
+	Version string
+	// serial ID (ignore)
+	SerialID byte
+	// Data
+	Data   interface{}
+	TwoWay bool
+	Event  bool
+}
+
+// NewRequest aims to create Request.
+// The ID is auto increase.
+func NewRequest(version string) *Request {
+	return &Request{
+		ID:      SequenceId(),
+		Version: version,
+	}
+}
+
+// this is response for transport layer
+type Response struct {
+	ID       int64
+	Version  string
+	SerialID byte
+	Status   uint8
+	Event    bool
+	Error    error
+	Result   interface{}
+}
+
+// NewResponse create to a new Response.
+func NewResponse(id int64, version string) *Response {
+	return &Response{
+		ID:      id,
+		Version: version,
+	}
+}
+
+// the response is heartbeat
+func (response *Response) IsHeartbeat() bool {
+	return response.Event && response.Result == nil
+}
+
+func (response *Response) Handle() {
+	pendingResponse := removePendingResponse(SequenceType(response.ID))
+	if pendingResponse == nil {
+		logger.Errorf("failed to get pending response context for response package %s", *response)
+		return
+	}
+
+	pendingResponse.response = response
+
+	if pendingResponse.Callback == nil {
+		pendingResponse.Err = pendingResponse.response.Error
+		close(pendingResponse.Done)
+	} else {
+		pendingResponse.Callback(pendingResponse.GetCallResponse())
+	}
+}
+
+type Options struct {
+	// connect timeout
+	ConnectTimeout time.Duration
+}
+
+//AsyncCallbackResponse async response for dubbo
+type AsyncCallbackResponse struct {
+	common.CallbackResponse
+	Opts      Options
+	Cause     error
+	Start     time.Time // invoke(call) start time == write start time
+	ReadStart time.Time // read start time, write duration = ReadStart - Start
+	Reply     interface{}
+}
+
+// the client sends requst to server, there is one pendingResponse at client side to wait the response from server
+type PendingResponse struct {
+	seq       int64
+	Err       error
+	start     time.Time
+	ReadStart time.Time
+	Callback  common.AsyncCallback
+	response  *Response
+	Reply     interface{}
+	Done      chan struct{}
+}
+
+// NewPendingResponse aims to create PendingResponse.
+// Id is always from ID of Request
+func NewPendingResponse(id int64) *PendingResponse {
+	return &PendingResponse{
+		seq:      id,
+		start:    time.Now(),
+		response: &Response{},
+		Done:     make(chan struct{}),
+	}
+}
+
+func (r *PendingResponse) SetResponse(response *Response) {
+	r.response = response
+}
+
+// GetCallResponse is used for callback of async.
+// It is will return AsyncCallbackResponse.
+func (r PendingResponse) GetCallResponse() common.CallbackResponse {
+	return AsyncCallbackResponse{
+		Cause:     r.Err,
+		Start:     r.start,
+		ReadStart: r.ReadStart,
+		Reply:     r.response,
+	}
+}
+
+// store response into map
+func AddPendingResponse(pr *PendingResponse) {
+	pendingResponses.Store(SequenceType(pr.seq), pr)
+}
+
+// get and remove response
+func removePendingResponse(seq SequenceType) *PendingResponse {
+	if pendingResponses == nil {
+		return nil
+	}
+	if presp, ok := pendingResponses.Load(seq); ok {
+		pendingResponses.Delete(seq)
+		return presp.(*PendingResponse)
+	}
+	return nil
+}
+
+// get response
+func GetPendingResponse(seq SequenceType) *PendingResponse {
+	if presp, ok := pendingResponses.Load(seq); ok {
+		return presp.(*PendingResponse)
+	}
+	return nil
+}
diff --git a/remoting/exchange_client.go b/remoting/exchange_client.go
new file mode 100644
index 0000000000000000000000000000000000000000..d65382035e397cc44d4cdcc2676be4bb7df3d02c
--- /dev/null
+++ b/remoting/exchange_client.go
@@ -0,0 +1,176 @@
+/*
+ * 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 remoting
+
+import (
+	"errors"
+	"time"
+)
+
+import (
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/common/logger"
+	"github.com/apache/dubbo-go/protocol"
+)
+
+// It is interface of client for network communication.
+// If you use getty as network communication, you should define GettyClient that implements this interface.
+type Client interface {
+	SetExchangeClient(client *ExchangeClient)
+	// connect url
+	Connect(url *common.URL) error
+	// close
+	Close()
+	// send request to server.
+	Request(request *Request, timeout time.Duration, response *PendingResponse) error
+	// check if the client is still available
+	IsAvailable() bool
+}
+
+// This is abstraction level. it is like facade.
+type ExchangeClient struct {
+	// connect server timeout
+	ConnectTimeout time.Duration
+	// to dial server address. The format: ip:port
+	address string
+	// the client that will deal with the transport. It is interface, and it will use gettyClient by default.
+	client Client
+	// the tag for init.
+	init bool
+}
+
+// create ExchangeClient
+func NewExchangeClient(url *common.URL, client Client, connectTimeout time.Duration, lazyInit bool) *ExchangeClient {
+	exchangeClient := &ExchangeClient{
+		ConnectTimeout: connectTimeout,
+		address:        url.Location,
+		client:         client,
+	}
+	client.SetExchangeClient(exchangeClient)
+	if !lazyInit {
+		if err := exchangeClient.doInit(url); err != nil {
+			return nil
+		}
+	}
+
+	return exchangeClient
+}
+
+func (cl *ExchangeClient) doInit(url *common.URL) error {
+	if cl.init {
+		return nil
+	}
+	if cl.client.Connect(url) != nil {
+		//retry for a while
+		time.Sleep(100 * time.Millisecond)
+		if cl.client.Connect(url) != nil {
+			logger.Errorf("Failed to connect server %+v " + url.Location)
+			return errors.New("Failed to connect server " + url.Location)
+		}
+	}
+	//FIXME atomic operation
+	cl.init = true
+	return nil
+}
+
+// two way request
+func (client *ExchangeClient) Request(invocation *protocol.Invocation, url *common.URL, timeout time.Duration,
+	result *protocol.RPCResult) error {
+	if er := client.doInit(url); er != nil {
+		return er
+	}
+	request := NewRequest("2.0.2")
+	request.Data = invocation
+	request.Event = false
+	request.TwoWay = true
+
+	rsp := NewPendingResponse(request.ID)
+	rsp.response = NewResponse(request.ID, "2.0.2")
+	rsp.Reply = (*invocation).Reply()
+	AddPendingResponse(rsp)
+
+	err := client.client.Request(request, timeout, rsp)
+	// request error
+	if err != nil {
+		result.Err = err
+		return err
+	}
+	if resultTmp, ok := rsp.response.Result.(*protocol.RPCResult); ok {
+		result.Rest = resultTmp.Rest
+		result.Attrs = resultTmp.Attrs
+		result.Err = resultTmp.Err
+	}
+	return nil
+}
+
+// async two way request
+func (client *ExchangeClient) AsyncRequest(invocation *protocol.Invocation, url *common.URL, timeout time.Duration,
+	callback common.AsyncCallback, result *protocol.RPCResult) error {
+	if er := client.doInit(url); er != nil {
+		return er
+	}
+	request := NewRequest("2.0.2")
+	request.Data = invocation
+	request.Event = false
+	request.TwoWay = true
+
+	rsp := NewPendingResponse(request.ID)
+	rsp.response = NewResponse(request.ID, "2.0.2")
+	rsp.Callback = callback
+	rsp.Reply = (*invocation).Reply()
+	AddPendingResponse(rsp)
+
+	err := client.client.Request(request, timeout, rsp)
+	if err != nil {
+		result.Err = err
+		return err
+	}
+	result.Rest = rsp.response
+	return nil
+}
+
+// oneway request
+func (client *ExchangeClient) Send(invocation *protocol.Invocation, url *common.URL, timeout time.Duration) error {
+	if er := client.doInit(url); er != nil {
+		return er
+	}
+	request := NewRequest("2.0.2")
+	request.Data = invocation
+	request.Event = false
+	request.TwoWay = false
+
+	rsp := NewPendingResponse(request.ID)
+	rsp.response = NewResponse(request.ID, "2.0.2")
+
+	err := client.client.Request(request, timeout, rsp)
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+// close client
+func (client *ExchangeClient) Close() {
+	client.client.Close()
+	// for reinit client
+	client.init = false
+}
+
+// IsAvailable to check if the underlying network client is available yet.
+func (client *ExchangeClient) IsAvailable() bool {
+	return client.client.IsAvailable()
+}
diff --git a/remoting/exchange_server.go b/remoting/exchange_server.go
new file mode 100644
index 0000000000000000000000000000000000000000..a8d7c73f305a42e0402861817e5dbe2abfcfdd01
--- /dev/null
+++ b/remoting/exchange_server.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 remoting
+
+import (
+	"github.com/apache/dubbo-go/common"
+)
+
+// It is interface of server for network communication.
+// If you use getty as network communication, you should define GettyServer that implements this interface.
+type Server interface {
+	//invoke once for connection
+	Start()
+	//it is for destroy
+	Stop()
+}
+
+// This is abstraction level. it is like facade.
+type ExchangeServer struct {
+	Server Server
+	Url    *common.URL
+}
+
+// Create ExchangeServer
+func NewExchangeServer(url *common.URL, server Server) *ExchangeServer {
+	exchangServer := &ExchangeServer{
+		Server: server,
+		Url:    url,
+	}
+	return exchangServer
+}
+
+// start server
+func (server *ExchangeServer) Start() {
+	server.Server.Start()
+}
+
+// stop server
+func (server *ExchangeServer) Stop() {
+	server.Server.Stop()
+}
diff --git a/protocol/dubbo/config.go b/remoting/getty/config.go
similarity index 80%
rename from protocol/dubbo/config.go
rename to remoting/getty/config.go
index b47ec1cc3422dcbcac921f08888c7a777e72e246..b6aa08206a1497b2bac5904eef7b86bde11a61ac 100644
--- a/protocol/dubbo/config.go
+++ b/remoting/getty/config.go
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package dubbo
+package getty
 
 import (
 	"time"
@@ -30,7 +30,7 @@ import (
 )
 
 type (
-	// GettySessionParam is session configuration for getty.
+	// GettySessionParam is session configuration for getty
 	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"`
@@ -52,6 +52,16 @@ type (
 
 	// ServerConfig holds supported types by the multiconfig package
 	ServerConfig struct {
+		SSLEnabled bool
+
+		// heartbeat
+		HeartbeatPeriod string `default:"60s" yaml:"heartbeat_period" json:"heartbeat_period,omitempty"`
+		heartbeatPeriod time.Duration
+
+		// heartbeat timeout
+		HeartbeatTimeout string `default:"5s" yaml:"heartbeat_timeout" json:"heartbeat_timeout,omitempty"`
+		heartbeatTimeout time.Duration
+
 		// session
 		SessionTimeout string `default:"60s" yaml:"session_timeout" json:"session_timeout,omitempty"`
 		sessionTimeout time.Duration
@@ -74,9 +84,13 @@ type (
 		ConnectionNum int `default:"16" yaml:"connection_number" json:"connection_number,omitempty"`
 
 		// heartbeat
-		HeartbeatPeriod string `default:"15s" yaml:"heartbeat_period" json:"heartbeat_period,omitempty"`
+		HeartbeatPeriod string `default:"60s" yaml:"heartbeat_period" json:"heartbeat_period,omitempty"`
 		heartbeatPeriod time.Duration
 
+		// heartbeat timeout
+		HeartbeatTimeout string `default:"5s" yaml:"heartbeat_timeout" json:"heartbeat_timeout,omitempty"`
+		heartbeatTimeout time.Duration
+
 		// session
 		SessionTimeout string `default:"60s" yaml:"session_timeout" json:"session_timeout,omitempty"`
 		sessionTimeout time.Duration
@@ -95,7 +109,7 @@ type (
 	}
 )
 
-// GetDefaultClientConfig gets client default configuration.
+// GetDefaultClientConfig gets client default configuration
 func GetDefaultClientConfig() ClientConfig {
 	return ClientConfig{
 		ReconnectInterval: 0,
@@ -123,7 +137,7 @@ func GetDefaultClientConfig() ClientConfig {
 		}}
 }
 
-// GetDefaultServerConfig gets server default configuration.
+// GetDefaultServerConfig gets server default configuration
 func GetDefaultServerConfig() ServerConfig {
 	return ServerConfig{
 		SessionTimeout: "180s",
@@ -148,7 +162,7 @@ func GetDefaultServerConfig() ServerConfig {
 	}
 }
 
-// CheckValidity confirm getty sessian params.
+// CheckValidity confirm getty sessian params
 func (c *GettySessionParam) CheckValidity() error {
 	var err error
 
@@ -186,6 +200,12 @@ func (c *ClientConfig) CheckValidity() error {
 			c.HeartbeatPeriod, time.Duration(config.MaxWheelTimeSpan))
 	}
 
+	if len(c.HeartbeatTimeout) == 0 {
+		c.heartbeatTimeout = 60 * time.Second
+	} else if c.heartbeatTimeout, err = time.ParseDuration(c.HeartbeatTimeout); err != nil {
+		return perrors.WithMessagef(err, "time.ParseDuration(HeartbeatTimeout{%#v})", c.HeartbeatTimeout)
+	}
+
 	if c.sessionTimeout, err = time.ParseDuration(c.SessionTimeout); err != nil {
 		return perrors.WithMessagef(err, "time.ParseDuration(SessionTimeout{%#v})", c.SessionTimeout)
 	}
@@ -193,10 +213,27 @@ func (c *ClientConfig) CheckValidity() error {
 	return perrors.WithStack(c.GettySessionParam.CheckValidity())
 }
 
-// CheckValidity confirm server params.
+// CheckValidity confirm server params
 func (c *ServerConfig) CheckValidity() error {
 	var err error
 
+	if len(c.HeartbeatPeriod) == 0 {
+		c.heartbeatPeriod = 60 * time.Second
+	} else if c.heartbeatPeriod, err = time.ParseDuration(c.HeartbeatPeriod); err != nil {
+		return perrors.WithMessagef(err, "time.ParseDuration(HeartbeatPeroid{%#v})", c.HeartbeatPeriod)
+	}
+
+	if c.heartbeatPeriod >= time.Duration(config.MaxWheelTimeSpan) {
+		return perrors.WithMessagef(err, "heartbeat_period %s should be less than %s",
+			c.HeartbeatPeriod, time.Duration(config.MaxWheelTimeSpan))
+	}
+
+	if len(c.HeartbeatTimeout) == 0 {
+		c.heartbeatTimeout = 60 * time.Second
+	} else if c.heartbeatTimeout, err = time.ParseDuration(c.HeartbeatTimeout); err != nil {
+		return perrors.WithMessagef(err, "time.ParseDuration(HeartbeatTimeout{%#v})", c.HeartbeatTimeout)
+	}
+
 	if c.sessionTimeout, err = time.ParseDuration(c.SessionTimeout); err != nil {
 		return perrors.WithMessagef(err, "time.ParseDuration(SessionTimeout{%#v})", c.SessionTimeout)
 	}
diff --git a/remoting/getty/dubbo_codec_for_test.go b/remoting/getty/dubbo_codec_for_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..fca5da89643adee2f5e6c1e8dca8ec3da4c987e4
--- /dev/null
+++ b/remoting/getty/dubbo_codec_for_test.go
@@ -0,0 +1,276 @@
+/*
+ * 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 getty
+
+// copy from dubbo/dubbo_codec.go .
+// it is used to unit test.
+import (
+	"bytes"
+	"strconv"
+	"time"
+)
+
+import (
+	hessian "github.com/apache/dubbo-go-hessian2"
+	perrors "github.com/pkg/errors"
+)
+
+import (
+	"github.com/apache/dubbo-go/common/constant"
+	"github.com/apache/dubbo-go/protocol"
+	"github.com/apache/dubbo-go/protocol/dubbo/impl"
+	"github.com/apache/dubbo-go/protocol/invocation"
+	"github.com/apache/dubbo-go/remoting"
+)
+
+func init() {
+	codec := &DubboTestCodec{}
+	remoting.RegistryCodec("dubbo", codec)
+}
+
+type DubboTestCodec struct {
+}
+
+// encode request for transport
+func (c *DubboTestCodec) EncodeRequest(request *remoting.Request) (*bytes.Buffer, error) {
+	if request.Event {
+		return c.encodeHeartbeartReqeust(request)
+	}
+
+	invoc, ok := request.Data.(*invocation.RPCInvocation)
+	if !ok {
+		return nil, perrors.Errorf("encode request failed for parameter type :%+v", request)
+	}
+	tmpInvocation := invoc
+
+	svc := impl.Service{}
+	svc.Path = tmpInvocation.AttachmentsByKey(constant.PATH_KEY, "")
+	svc.Interface = tmpInvocation.AttachmentsByKey(constant.INTERFACE_KEY, "")
+	svc.Version = tmpInvocation.AttachmentsByKey(constant.VERSION_KEY, "")
+	svc.Group = tmpInvocation.AttachmentsByKey(constant.GROUP_KEY, "")
+	svc.Method = tmpInvocation.MethodName()
+	timeout, err := strconv.Atoi(tmpInvocation.AttachmentsByKey(constant.TIMEOUT_KEY, strconv.Itoa(constant.DEFAULT_REMOTING_TIMEOUT)))
+	if err != nil {
+		// it will be wrapped in readwrite.Write .
+		return nil, perrors.WithStack(err)
+	}
+	svc.Timeout = time.Duration(timeout)
+
+	header := impl.DubboHeader{}
+	serialization := tmpInvocation.AttachmentsByKey(constant.SERIALIZATION_KEY, constant.HESSIAN2_SERIALIZATION)
+	if serialization == constant.PROTOBUF_SERIALIZATION {
+		header.SerialID = constant.S_Proto
+	} else {
+		header.SerialID = constant.S_Hessian2
+	}
+	header.ID = request.ID
+	if request.TwoWay {
+		header.Type = impl.PackageRequest_TwoWay
+	} else {
+		header.Type = impl.PackageRequest
+	}
+
+	pkg := &impl.DubboPackage{
+		Header:  header,
+		Service: svc,
+		Body:    impl.NewRequestPayload(tmpInvocation.Arguments(), tmpInvocation.Attachments()),
+		Err:     nil,
+		Codec:   impl.NewDubboCodec(nil),
+	}
+
+	if err := impl.LoadSerializer(pkg); err != nil {
+		return nil, perrors.WithStack(err)
+	}
+
+	return pkg.Marshal()
+}
+
+// encode heartbeart request
+func (c *DubboTestCodec) encodeHeartbeartReqeust(request *remoting.Request) (*bytes.Buffer, error) {
+	header := impl.DubboHeader{
+		Type:     impl.PackageHeartbeat,
+		SerialID: constant.S_Hessian2,
+		ID:       request.ID,
+	}
+
+	pkg := &impl.DubboPackage{
+		Header:  header,
+		Service: impl.Service{},
+		Body:    impl.NewRequestPayload([]interface{}{}, nil),
+		Err:     nil,
+		Codec:   impl.NewDubboCodec(nil),
+	}
+
+	if err := impl.LoadSerializer(pkg); err != nil {
+		return nil, err
+	}
+	return pkg.Marshal()
+}
+
+// encode response
+func (c *DubboTestCodec) EncodeResponse(response *remoting.Response) (*bytes.Buffer, error) {
+	var ptype = impl.PackageResponse
+	if response.IsHeartbeat() {
+		ptype = impl.PackageHeartbeat
+	}
+	resp := &impl.DubboPackage{
+		Header: impl.DubboHeader{
+			SerialID:       response.SerialID,
+			Type:           ptype,
+			ID:             response.ID,
+			ResponseStatus: response.Status,
+		},
+	}
+	if !response.IsHeartbeat() {
+		resp.Body = &impl.ResponsePayload{
+			RspObj:      response.Result.(protocol.RPCResult).Rest,
+			Exception:   response.Result.(protocol.RPCResult).Err,
+			Attachments: response.Result.(protocol.RPCResult).Attrs,
+		}
+	}
+
+	codec := impl.NewDubboCodec(nil)
+
+	pkg, err := codec.Encode(*resp)
+	if err != nil {
+		return nil, perrors.WithStack(err)
+	}
+
+	return bytes.NewBuffer(pkg), nil
+}
+
+// Decode data, including request and response.
+func (c *DubboTestCodec) Decode(data []byte) (remoting.DecodeResult, int, error) {
+	if c.isRequest(data) {
+		req, len, err := c.decodeRequest(data)
+		if err != nil {
+			return remoting.DecodeResult{}, len, perrors.WithStack(err)
+		}
+		return remoting.DecodeResult{IsRequest: true, Result: req}, len, perrors.WithStack(err)
+	} else {
+		resp, len, err := c.decodeResponse(data)
+		if err != nil {
+			return remoting.DecodeResult{}, len, perrors.WithStack(err)
+		}
+		return remoting.DecodeResult{IsRequest: false, Result: resp}, len, perrors.WithStack(err)
+	}
+}
+
+func (c *DubboTestCodec) isRequest(data []byte) bool {
+	if data[2]&byte(0x80) == 0x00 {
+		return false
+	}
+	return true
+}
+
+// decode request
+func (c *DubboTestCodec) decodeRequest(data []byte) (*remoting.Request, int, error) {
+	var request *remoting.Request = nil
+	buf := bytes.NewBuffer(data)
+	pkg := impl.NewDubboPackage(buf)
+	pkg.SetBody(make([]interface{}, 7))
+	err := pkg.Unmarshal()
+	if err != nil {
+		originErr := perrors.Cause(err)
+		if originErr == hessian.ErrHeaderNotEnough || originErr == hessian.ErrBodyNotEnough {
+			//FIXME
+			return nil, 0, originErr
+		}
+		return request, 0, perrors.WithStack(err)
+	}
+	request = &remoting.Request{
+		ID:       pkg.Header.ID,
+		SerialID: pkg.Header.SerialID,
+		TwoWay:   pkg.Header.Type&impl.PackageRequest_TwoWay != 0x00,
+		Event:    pkg.Header.Type&impl.PackageHeartbeat != 0x00,
+	}
+	if (pkg.Header.Type & impl.PackageHeartbeat) == 0x00 {
+		// convert params of request
+		req := pkg.Body.(map[string]interface{})
+
+		//invocation := request.Data.(*invocation.RPCInvocation)
+		var methodName string
+		var args []interface{}
+		attachments := make(map[string]interface{})
+		if req[impl.DubboVersionKey] != nil {
+			//dubbo version
+			request.Version = req[impl.DubboVersionKey].(string)
+		}
+		//path
+		attachments[constant.PATH_KEY] = pkg.Service.Path
+		//version
+		attachments[constant.VERSION_KEY] = pkg.Service.Version
+		//method
+		methodName = pkg.Service.Method
+		args = req[impl.ArgsKey].([]interface{})
+		attachments = req[impl.AttachmentsKey].(map[string]interface{})
+		invoc := invocation.NewRPCInvocationWithOptions(invocation.WithAttachments(attachments),
+			invocation.WithArguments(args), invocation.WithMethodName(methodName))
+		request.Data = invoc
+
+	}
+	return request, hessian.HEADER_LENGTH + pkg.Header.BodyLen, nil
+}
+
+// decode response
+func (c *DubboTestCodec) decodeResponse(data []byte) (*remoting.Response, int, error) {
+	buf := bytes.NewBuffer(data)
+	pkg := impl.NewDubboPackage(buf)
+	response := &remoting.Response{}
+	err := pkg.Unmarshal()
+	if err != nil {
+		originErr := perrors.Cause(err)
+		// if the data is very big, so the receive need much times.
+		if originErr == hessian.ErrHeaderNotEnough || originErr == hessian.ErrBodyNotEnough {
+			return nil, 0, originErr
+		}
+		return nil, 0, perrors.WithStack(err)
+	}
+	response = &remoting.Response{
+		ID: pkg.Header.ID,
+		//Version:  pkg.Header.,
+		SerialID: pkg.Header.SerialID,
+		Status:   pkg.Header.ResponseStatus,
+		Event:    (pkg.Header.Type & impl.PackageHeartbeat) != 0,
+	}
+	var error error
+	if pkg.Header.Type&impl.PackageHeartbeat != 0x00 {
+		if pkg.Header.Type&impl.PackageResponse != 0x00 {
+			if pkg.Err != nil {
+				error = pkg.Err
+			}
+		} else {
+			response.Status = hessian.Response_OK
+			//reply(session, p, hessian.PackageHeartbeat)
+		}
+		return response, hessian.HEADER_LENGTH + pkg.Header.BodyLen, error
+	}
+	rpcResult := &protocol.RPCResult{}
+	response.Result = rpcResult
+	if pkg.Header.Type&impl.PackageRequest == 0x00 {
+		if pkg.Err != nil {
+			rpcResult.Err = pkg.Err
+		} else if pkg.Body.(*impl.ResponsePayload).Exception != nil {
+			rpcResult.Err = pkg.Body.(*impl.ResponsePayload).Exception
+			response.Error = rpcResult.Err
+		}
+		rpcResult.Attrs = pkg.Body.(*impl.ResponsePayload).Attachments
+		rpcResult.Rest = pkg.Body.(*impl.ResponsePayload).RspObj
+	}
+
+	return response, hessian.HEADER_LENGTH + pkg.Header.BodyLen, nil
+}
diff --git a/remoting/getty/getty_client.go b/remoting/getty/getty_client.go
new file mode 100644
index 0000000000000000000000000000000000000000..e432ca55a9bfb32575fad183f93438b8cb7801fb
--- /dev/null
+++ b/remoting/getty/getty_client.go
@@ -0,0 +1,222 @@
+/*
+ * 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 getty
+
+import (
+	"math/rand"
+	"time"
+)
+
+import (
+	"github.com/apache/dubbo-getty"
+	gxsync "github.com/dubbogo/gost/sync"
+	perrors "github.com/pkg/errors"
+	"gopkg.in/yaml.v2"
+)
+
+import (
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/common/constant"
+	"github.com/apache/dubbo-go/common/logger"
+	"github.com/apache/dubbo-go/config"
+	"github.com/apache/dubbo-go/remoting"
+)
+
+var (
+	errInvalidCodecType  = perrors.New("illegal CodecType")
+	errInvalidAddress    = perrors.New("remote address invalid or empty")
+	errSessionNotExist   = perrors.New("session not exist")
+	errClientClosed      = perrors.New("client closed")
+	errClientReadTimeout = perrors.New("client read timeout")
+
+	clientConf   *ClientConfig
+	clientGrpool *gxsync.TaskPool
+)
+
+// it is init client for single protocol.
+func initClient(protocol string) {
+	if protocol == "" {
+		return
+	}
+
+	// load clientconfig from consumer_config
+	// default use dubbo
+	consumerConfig := config.GetConsumerConfig()
+	if consumerConfig.ApplicationConfig == nil {
+		return
+	}
+	protocolConf := config.GetConsumerConfig().ProtocolConf
+	defaultClientConfig := GetDefaultClientConfig()
+	if protocolConf == nil {
+		logger.Info("protocol_conf default use dubbo config")
+	} else {
+		dubboConf := protocolConf.(map[interface{}]interface{})[protocol]
+		if dubboConf == nil {
+			logger.Warnf("dubboConf is nil")
+			return
+		}
+		dubboConfByte, err := yaml.Marshal(dubboConf)
+		if err != nil {
+			panic(err)
+		}
+		err = yaml.Unmarshal(dubboConfByte, &defaultClientConfig)
+		if err != nil {
+			panic(err)
+		}
+	}
+	clientConf = &defaultClientConfig
+	if err := clientConf.CheckValidity(); err != nil {
+		logger.Warnf("[CheckValidity] error: %v", err)
+		return
+	}
+	setClientGrpool()
+
+	rand.Seed(time.Now().UnixNano())
+}
+
+// Config ClientConf
+func SetClientConf(c ClientConfig) {
+	clientConf = &c
+	err := clientConf.CheckValidity()
+	if err != nil {
+		logger.Warnf("[ClientConfig CheckValidity] error: %v", err)
+		return
+	}
+	setClientGrpool()
+}
+
+func setClientGrpool() {
+	if clientConf.GrPoolSize > 1 {
+		clientGrpool = gxsync.NewTaskPool(gxsync.WithTaskPoolTaskPoolSize(clientConf.GrPoolSize), gxsync.WithTaskPoolTaskQueueLength(clientConf.QueueLen),
+			gxsync.WithTaskPoolTaskQueueNumber(clientConf.QueueNumber))
+	}
+}
+
+// Options : param config
+type Options struct {
+	// connect timeout
+	// remove request timeout, it will be calulate for every request
+	ConnectTimeout time.Duration
+	// request timeout
+	RequestTimeout time.Duration
+}
+
+// Client : some configuration for network communication.
+type Client struct {
+	addr           string
+	opts           Options
+	conf           ClientConfig
+	pool           *gettyRPCClientPool
+	codec          remoting.Codec
+	ExchangeClient *remoting.ExchangeClient
+}
+
+// create client
+func NewClient(opt Options) *Client {
+	switch {
+	case opt.ConnectTimeout == 0:
+		opt.ConnectTimeout = 3 * time.Second
+		fallthrough
+	case opt.RequestTimeout == 0:
+		opt.RequestTimeout = 3 * time.Second
+	}
+
+	c := &Client{
+		opts: opt,
+	}
+	return c
+}
+
+func (c *Client) SetExchangeClient(client *remoting.ExchangeClient) {
+	c.ExchangeClient = client
+}
+
+// init client and try to connection.
+func (c *Client) Connect(url *common.URL) error {
+	initClient(url.Protocol)
+	c.conf = *clientConf
+	// new client
+	c.pool = newGettyRPCClientConnPool(c, clientConf.PoolSize, time.Duration(int(time.Second)*clientConf.PoolTTL))
+	c.pool.sslEnabled = url.GetParamBool(constant.SSL_ENABLED_KEY, false)
+
+	// codec
+	c.codec = remoting.GetCodec(url.Protocol)
+	c.addr = url.Location
+	_, _, err := c.selectSession(c.addr)
+	if err != nil {
+		logger.Errorf("try to connect server %v failed for : %v", url.Location, err)
+	}
+	return err
+}
+
+// close network connection
+func (c *Client) Close() {
+	if c.pool != nil {
+		c.pool.close()
+	}
+	c.pool = nil
+}
+
+// send request
+func (c *Client) Request(request *remoting.Request, timeout time.Duration, response *remoting.PendingResponse) error {
+	_, session, err := c.selectSession(c.addr)
+	if err != nil {
+		return perrors.WithStack(err)
+	}
+	if session == nil {
+		return errSessionNotExist
+	}
+
+	if err = c.transfer(session, request, timeout); err != nil {
+		return perrors.WithStack(err)
+	}
+
+	if !request.TwoWay || response.Callback != nil {
+		return nil
+	}
+
+	select {
+	case <-getty.GetTimeWheel().After(timeout):
+		return perrors.WithStack(errClientReadTimeout)
+	case <-response.Done:
+		err = response.Err
+	}
+
+	return perrors.WithStack(err)
+}
+
+// isAvailable returns true if the connection is available, or it can be re-established.
+func (c *Client) IsAvailable() bool {
+	client, _, err := c.selectSession(c.addr)
+	return err == nil &&
+		// defensive check
+		client != nil
+}
+
+func (c *Client) selectSession(addr string) (*gettyRPCClient, getty.Session, error) {
+	rpcClient, err := c.pool.getGettyRpcClient(addr)
+	if err != nil {
+		return nil, nil, perrors.WithStack(err)
+	}
+	return rpcClient, rpcClient.selectSession(), nil
+}
+
+func (c *Client) transfer(session getty.Session, request *remoting.Request, timeout time.Duration) error {
+	err := session.WritePkg(request, timeout)
+	return perrors.WithStack(err)
+}
diff --git a/remoting/getty/getty_client_test.go b/remoting/getty/getty_client_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..0b18e973cd2ea7a3f6aae59e822aaf68ee983331
--- /dev/null
+++ b/remoting/getty/getty_client_test.go
@@ -0,0 +1,493 @@
+/*
+ * 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 getty
+
+import (
+	"bytes"
+	"context"
+	"reflect"
+	"sync"
+	"testing"
+	"time"
+)
+
+import (
+	hessian "github.com/apache/dubbo-go-hessian2"
+	perrors "github.com/pkg/errors"
+	"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/proxy/proxy_factory"
+	"github.com/apache/dubbo-go/config"
+	"github.com/apache/dubbo-go/protocol"
+	"github.com/apache/dubbo-go/protocol/invocation"
+	"github.com/apache/dubbo-go/remoting"
+)
+
+func TestRunSuite(t *testing.T) {
+	svr, url := InitTest(t)
+	client := getClient(url)
+	testRequestOneWay(t, svr, url, client)
+	testClient_Call(t, svr, url, client)
+	testClient_AsyncCall(t, svr, url, client)
+	svr.Stop()
+}
+
+func testRequestOneWay(t *testing.T, svr *Server, url *common.URL, client *Client) {
+
+	request := remoting.NewRequest("2.0.2")
+	up := &UserProvider{}
+	invocation := createInvocation("GetUser", nil, nil, []interface{}{[]interface{}{"1", "username"}, up},
+		[]reflect.Value{reflect.ValueOf([]interface{}{"1", "username"}), reflect.ValueOf(up)})
+	attachment := map[string]string{INTERFACE_KEY: "com.ikurento.user.UserProvider"}
+	setAttachment(invocation, attachment)
+	request.Data = invocation
+	request.Event = false
+	request.TwoWay = false
+	//user := &User{}
+	err := client.Request(request, 3*time.Second, nil)
+	assert.NoError(t, err)
+}
+
+func createInvocation(methodName string, callback interface{}, reply interface{}, arguments []interface{},
+	parameterValues []reflect.Value) *invocation.RPCInvocation {
+	return invocation.NewRPCInvocationWithOptions(invocation.WithMethodName(methodName),
+		invocation.WithArguments(arguments), invocation.WithReply(reply),
+		invocation.WithCallBack(callback), invocation.WithParameterValues(parameterValues))
+}
+
+func setAttachment(invocation *invocation.RPCInvocation, attachments map[string]string) {
+	for key, value := range attachments {
+		invocation.SetAttachments(key, value)
+	}
+}
+
+func getClient(url *common.URL) *Client {
+	client := NewClient(Options{
+		ConnectTimeout: config.GetConsumerConfig().ConnectTimeout,
+	})
+
+	exchangeClient := remoting.NewExchangeClient(url, client, 5*time.Second, false)
+	client.SetExchangeClient(exchangeClient)
+	client.Connect(url)
+	return client
+}
+
+func testClient_Call(t *testing.T, svr *Server, url *common.URL, c *Client) {
+	c.pool = newGettyRPCClientConnPool(c, clientConf.PoolSize, time.Duration(int(time.Second)*clientConf.PoolTTL))
+
+	testGetBigPkg(t, c)
+	testGetUser(t, c)
+	testGetUser0(t, c)
+	testGetUser1(t, c)
+	testGetUser2(t, c)
+	testGetUser3(t, c)
+	testGetUser4(t, c)
+	testGetUser5(t, c)
+	testGetUser6(t, c)
+	testGetUser61(t, c)
+}
+
+func testGetBigPkg(t *testing.T, c *Client) {
+	user := &User{}
+	request := remoting.NewRequest("2.0.2")
+	invocation := createInvocation("GetBigPkg", nil, nil, []interface{}{[]interface{}{nil}, user},
+		[]reflect.Value{reflect.ValueOf([]interface{}{nil}), reflect.ValueOf(user)})
+	attachment := map[string]string{INTERFACE_KEY: "com.ikurento.user.UserProvider"}
+	setAttachment(invocation, attachment)
+	request.Data = invocation
+	request.Event = false
+	request.TwoWay = true
+	pendingResponse := remoting.NewPendingResponse(request.ID)
+	pendingResponse.Reply = user
+	remoting.AddPendingResponse(pendingResponse)
+	err := c.Request(request, 8*time.Second, pendingResponse)
+	assert.NoError(t, err)
+	assert.NotEqual(t, "", user.Id)
+	assert.NotEqual(t, "", user.Name)
+}
+
+func testGetUser(t *testing.T, c *Client) {
+	user := &User{}
+	request := remoting.NewRequest("2.0.2")
+	invocation := createInvocation("GetUser", nil, nil, []interface{}{"1", "username"},
+		[]reflect.Value{reflect.ValueOf("1"), reflect.ValueOf("username")})
+	attachment := map[string]string{INTERFACE_KEY: "com.ikurento.user.UserProvider"}
+	setAttachment(invocation, attachment)
+	request.Data = invocation
+	request.Event = false
+	request.TwoWay = true
+	pendingResponse := remoting.NewPendingResponse(request.ID)
+	pendingResponse.Reply = user
+	remoting.AddPendingResponse(pendingResponse)
+	err := c.Request(request, 3*time.Second, pendingResponse)
+	assert.NoError(t, err)
+	assert.Equal(t, User{Id: "1", Name: "username"}, *user)
+}
+
+func testGetUser0(t *testing.T, c *Client) {
+	var (
+		user *User
+		err  error
+	)
+	user = &User{}
+	request := remoting.NewRequest("2.0.2")
+	invocation := createInvocation("GetUser0", nil, nil, []interface{}{"1", nil, "username"},
+		[]reflect.Value{reflect.ValueOf("1"), reflect.ValueOf(nil), reflect.ValueOf("username")})
+	attachment := map[string]string{INTERFACE_KEY: "com.ikurento.user.UserProvider"}
+	setAttachment(invocation, attachment)
+	request.Data = invocation
+	request.Event = false
+	request.TwoWay = true
+	rsp := remoting.NewPendingResponse(request.ID)
+	rsp.SetResponse(remoting.NewResponse(request.ID, "2.0.2"))
+	remoting.AddPendingResponse(rsp)
+	rsp.Reply = user
+	err = c.Request(request, 3*time.Second, rsp)
+	assert.NoError(t, err)
+	assert.Equal(t, User{Id: "1", Name: "username"}, *user)
+}
+
+func testGetUser1(t *testing.T, c *Client) {
+	var (
+		err error
+	)
+	request := remoting.NewRequest("2.0.2")
+	invocation := createInvocation("GetUser1", nil, nil, []interface{}{},
+		[]reflect.Value{})
+	attachment := map[string]string{INTERFACE_KEY: "com.ikurento.user.UserProvider"}
+	setAttachment(invocation, attachment)
+	request.Data = invocation
+	request.Event = false
+	request.TwoWay = true
+	pendingResponse := remoting.NewPendingResponse(request.ID)
+	user := &User{}
+	pendingResponse.Reply = user
+	remoting.AddPendingResponse(pendingResponse)
+	err = c.Request(request, 3*time.Second, pendingResponse)
+	assert.NoError(t, err)
+}
+
+func testGetUser2(t *testing.T, c *Client) {
+	var (
+		err error
+	)
+	request := remoting.NewRequest("2.0.2")
+	invocation := createInvocation("GetUser2", nil, nil, []interface{}{},
+		[]reflect.Value{})
+	attachment := map[string]string{INTERFACE_KEY: "com.ikurento.user.UserProvider"}
+	setAttachment(invocation, attachment)
+	request.Data = invocation
+	request.Event = false
+	request.TwoWay = true
+	pendingResponse := remoting.NewPendingResponse(request.ID)
+	remoting.AddPendingResponse(pendingResponse)
+	err = c.Request(request, 3*time.Second, pendingResponse)
+	assert.EqualError(t, err, "error")
+}
+
+func testGetUser3(t *testing.T, c *Client) {
+	var (
+		err error
+	)
+	request := remoting.NewRequest("2.0.2")
+	invocation := createInvocation("GetUser3", nil, nil, []interface{}{},
+		[]reflect.Value{})
+	attachment := map[string]string{
+		INTERFACE_KEY: "com.ikurento.user.UserProvider",
+	}
+	setAttachment(invocation, attachment)
+	request.Data = invocation
+	request.Event = false
+	request.TwoWay = true
+	pendingResponse := remoting.NewPendingResponse(request.ID)
+	user2 := []interface{}{}
+	pendingResponse.Reply = &user2
+	remoting.AddPendingResponse(pendingResponse)
+	err = c.Request(request, 3*time.Second, pendingResponse)
+	assert.NoError(t, err)
+	assert.Equal(t, &User{Id: "1", Name: "username"}, user2[0])
+}
+
+func testGetUser4(t *testing.T, c *Client) {
+	var (
+		err error
+	)
+	request := remoting.NewRequest("2.0.2")
+	invocation := invocation.NewRPCInvocation("GetUser4", []interface{}{[]interface{}{"1", "username"}}, nil)
+	attachment := map[string]string{INTERFACE_KEY: "com.ikurento.user.UserProvider"}
+	setAttachment(invocation, attachment)
+	request.Data = invocation
+	request.Event = false
+	request.TwoWay = true
+	pendingResponse := remoting.NewPendingResponse(request.ID)
+	user2 := []interface{}{}
+	pendingResponse.Reply = &user2
+	remoting.AddPendingResponse(pendingResponse)
+	err = c.Request(request, 3*time.Second, pendingResponse)
+	assert.NoError(t, err)
+	assert.Equal(t, &User{Id: "1", Name: "username"}, user2[0])
+}
+
+func testGetUser5(t *testing.T, c *Client) {
+	var (
+		err error
+	)
+	request := remoting.NewRequest("2.0.2")
+	invocation := invocation.NewRPCInvocation("GetUser5", []interface{}{map[interface{}]interface{}{"id": "1", "name": "username"}}, nil)
+	attachment := map[string]string{INTERFACE_KEY: "com.ikurento.user.UserProvider"}
+	setAttachment(invocation, attachment)
+	request.Data = invocation
+	request.Event = false
+	request.TwoWay = true
+	pendingResponse := remoting.NewPendingResponse(request.ID)
+	user3 := map[interface{}]interface{}{}
+	pendingResponse.Reply = &user3
+	remoting.AddPendingResponse(pendingResponse)
+	err = c.Request(request, 3*time.Second, pendingResponse)
+	assert.NoError(t, err)
+	assert.NotNil(t, user3)
+	assert.Equal(t, &User{Id: "1", Name: "username"}, user3["key"])
+}
+
+func testGetUser6(t *testing.T, c *Client) {
+	var (
+		user *User
+		err  error
+	)
+	user = &User{}
+	request := remoting.NewRequest("2.0.2")
+	invocation := invocation.NewRPCInvocation("GetUser6", []interface{}{0}, nil)
+	attachment := map[string]string{INTERFACE_KEY: "com.ikurento.user.UserProvider"}
+	setAttachment(invocation, attachment)
+	request.Data = invocation
+	request.Event = false
+	request.TwoWay = true
+	pendingResponse := remoting.NewPendingResponse(request.ID)
+	pendingResponse.Reply = user
+	remoting.AddPendingResponse(pendingResponse)
+	err = c.Request(request, 3*time.Second, pendingResponse)
+	assert.NoError(t, err)
+	assert.Equal(t, User{Id: "", Name: ""}, *user)
+}
+
+func testGetUser61(t *testing.T, c *Client) {
+	var (
+		user *User
+		err  error
+	)
+	user = &User{}
+	request := remoting.NewRequest("2.0.2")
+	invocation := invocation.NewRPCInvocation("GetUser6", []interface{}{1}, nil)
+	attachment := map[string]string{INTERFACE_KEY: "com.ikurento.user.UserProvider"}
+	setAttachment(invocation, attachment)
+	request.Data = invocation
+	request.Event = false
+	request.TwoWay = true
+	pendingResponse := remoting.NewPendingResponse(request.ID)
+	pendingResponse.Reply = user
+	remoting.AddPendingResponse(pendingResponse)
+	err = c.Request(request, 3*time.Second, pendingResponse)
+	assert.NoError(t, err)
+	assert.Equal(t, User{Id: "1", Name: ""}, *user)
+}
+
+func testClient_AsyncCall(t *testing.T, svr *Server, url *common.URL, client *Client) {
+	user := &User{}
+	lock := sync.Mutex{}
+	request := remoting.NewRequest("2.0.2")
+	invocation := createInvocation("GetUser0", nil, nil, []interface{}{"4", nil, "username"},
+		[]reflect.Value{reflect.ValueOf("4"), reflect.ValueOf(nil), reflect.ValueOf("username")})
+	attachment := map[string]string{INTERFACE_KEY: "com.ikurento.user.UserProvider"}
+	setAttachment(invocation, attachment)
+	request.Data = invocation
+	request.Event = false
+	request.TwoWay = true
+	rsp := remoting.NewPendingResponse(request.ID)
+	rsp.SetResponse(remoting.NewResponse(request.ID, "2.0.2"))
+	remoting.AddPendingResponse(rsp)
+	rsp.Reply = user
+	rsp.Callback = func(response common.CallbackResponse) {
+		r := response.(remoting.AsyncCallbackResponse)
+		rst := *r.Reply.(*remoting.Response).Result.(*protocol.RPCResult)
+		assert.Equal(t, User{Id: "4", Name: "username"}, *(rst.Rest.(*User)))
+		lock.Unlock()
+	}
+	lock.Lock()
+	err := client.Request(request, 3*time.Second, rsp)
+	assert.NoError(t, err)
+	assert.Equal(t, User{}, *user)
+	time.Sleep(1 * time.Second)
+}
+
+func InitTest(t *testing.T) (*Server, *common.URL) {
+
+	hessian.RegisterPOJO(&User{})
+	remoting.RegistryCodec("dubbo", &DubboTestCodec{})
+
+	methods, err := common.ServiceMap.Register("com.ikurento.user.UserProvider", "dubbo", "", "", &UserProvider{})
+	assert.NoError(t, err)
+	assert.Equal(t, "GetBigPkg,GetUser,GetUser0,GetUser1,GetUser2,GetUser3,GetUser4,GetUser5,GetUser6", methods)
+
+	// config
+	SetClientConf(ClientConfig{
+		ConnectionNum:   2,
+		HeartbeatPeriod: "5s",
+		SessionTimeout:  "20s",
+		PoolTTL:         600,
+		PoolSize:        64,
+		GettySessionParam: GettySessionParam{
+			CompressEncoding: false,
+			TcpNoDelay:       true,
+			TcpKeepAlive:     true,
+			KeepAlivePeriod:  "120s",
+			TcpRBufSize:      262144,
+			TcpWBufSize:      65536,
+			PkgWQSize:        512,
+			TcpReadTimeout:   "4s",
+			TcpWriteTimeout:  "5s",
+			WaitTimeout:      "1s",
+			MaxMsgLen:        10240000000,
+			SessionName:      "client",
+		},
+	})
+	assert.NoError(t, clientConf.CheckValidity())
+	SetServerConfig(ServerConfig{
+		SessionNumber:  700,
+		SessionTimeout: "20s",
+		GettySessionParam: GettySessionParam{
+			CompressEncoding: false,
+			TcpNoDelay:       true,
+			TcpKeepAlive:     true,
+			KeepAlivePeriod:  "120s",
+			TcpRBufSize:      262144,
+			TcpWBufSize:      65536,
+			PkgWQSize:        512,
+			TcpReadTimeout:   "1s",
+			TcpWriteTimeout:  "5s",
+			WaitTimeout:      "1s",
+			MaxMsgLen:        10240000000,
+			SessionName:      "server",
+		}})
+	assert.NoError(t, srvConf.CheckValidity())
+
+	url, err := common.NewURL("dubbo://127.0.0.1:20060/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=127.0.0.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")
+	// init server
+	userProvider := &UserProvider{}
+	common.ServiceMap.Register("", url.Protocol, "", "0.0.1", userProvider)
+	invoker := &proxy_factory.ProxyInvoker{
+		BaseInvoker: *protocol.NewBaseInvoker(url),
+	}
+	handler := func(invocation *invocation.RPCInvocation) protocol.RPCResult {
+		//result := protocol.RPCResult{}
+		r := invoker.Invoke(context.Background(), invocation)
+		result := protocol.RPCResult{
+			Err:   r.Error(),
+			Rest:  r.Result(),
+			Attrs: r.Attachments(),
+		}
+		return result
+	}
+	server := NewServer(url, handler)
+	server.Start()
+
+	time.Sleep(time.Second * 2)
+
+	return server, url
+}
+
+//////////////////////////////////
+// provider
+//////////////////////////////////
+
+type (
+	User struct {
+		Id   string `json:"id"`
+		Name string `json:"name"`
+	}
+
+	UserProvider struct {
+		user map[string]User
+	}
+)
+
+// size:4801228
+func (u *UserProvider) GetBigPkg(ctx context.Context, req []interface{}, rsp *User) error {
+	argBuf := new(bytes.Buffer)
+	for i := 0; i < 400; i++ {
+		argBuf.WriteString("鍑婚紦鍏堕晽锛岃笂璺冪敤鍏点€傚湡鍥藉煄婕曪紝鎴戠嫭鍗楄銆備粠瀛欏瓙浠诧紝骞抽檲涓庡畫銆備笉鎴戜互褰掞紝蹇у績鏈夊俊銆傜埌灞呯埌澶勶紵鐖颁抚鍏堕┈锛熶簬浠ユ眰涔嬶紵浜庢灄涔嬩笅銆傛鐢熷闃旓紝涓庡瓙鎴愯銆傛墽瀛愪箣鎵嬶紝涓庡瓙鍋曡€併€備簬鍡熼様鍏紝涓嶆垜娲诲叜銆備簬鍡熸吹鍏紝涓嶆垜淇″叜銆�")
+		argBuf.WriteString("鍑婚紦鍏堕晽锛岃笂璺冪敤鍏点€傚湡鍥藉煄婕曪紝鎴戠嫭鍗楄銆備粠瀛欏瓙浠诧紝骞抽檲涓庡畫銆備笉鎴戜互褰掞紝蹇у績鏈夊俊銆傜埌灞呯埌澶勶紵鐖颁抚鍏堕┈锛熶簬浠ユ眰涔嬶紵浜庢灄涔嬩笅銆傛鐢熷闃旓紝涓庡瓙鎴愯銆傛墽瀛愪箣鎵嬶紝涓庡瓙鍋曡€併€備簬鍡熼様鍏紝涓嶆垜娲诲叜銆備簬鍡熸吹鍏紝涓嶆垜淇″叜銆�")
+	}
+	rsp.Id = argBuf.String()
+	rsp.Name = argBuf.String()
+	return nil
+}
+
+func (u *UserProvider) GetUser(ctx context.Context, req []interface{}, rsp *User) error {
+	rsp.Id = req[0].(string)
+	rsp.Name = req[1].(string)
+	return nil
+}
+
+func (u *UserProvider) GetUser0(id string, k *User, name string) (User, error) {
+	return User{Id: id, Name: name}, nil
+}
+
+func (u *UserProvider) GetUser1() error {
+	return nil
+}
+
+func (u *UserProvider) GetUser2() error {
+	return perrors.New("error")
+}
+
+func (u *UserProvider) GetUser3(rsp *[]interface{}) error {
+	*rsp = append(*rsp, User{Id: "1", Name: "username"})
+	return nil
+}
+
+func (u *UserProvider) GetUser4(ctx context.Context, req []interface{}) ([]interface{}, error) {
+
+	return []interface{}{User{Id: req[0].([]interface{})[0].(string), Name: req[0].([]interface{})[1].(string)}}, nil
+}
+
+func (u *UserProvider) GetUser5(ctx context.Context, req []interface{}) (map[interface{}]interface{}, error) {
+	return map[interface{}]interface{}{"key": User{Id: req[0].(map[interface{}]interface{})["id"].(string), Name: req[0].(map[interface{}]interface{})["name"].(string)}}, nil
+}
+
+func (u *UserProvider) GetUser6(id int64) (*User, error) {
+	if id == 0 {
+		return nil, nil
+	}
+	return &User{Id: "1"}, nil
+}
+
+func (u *UserProvider) Reference() string {
+	return "UserProvider"
+}
+
+func (u User) JavaClassName() string {
+	return "com.ikurento.user.User"
+}
diff --git a/protocol/dubbo/server.go b/remoting/getty/getty_server.go
similarity index 60%
rename from protocol/dubbo/server.go
rename to remoting/getty/getty_server.go
index 4ad4796c5409e39984af3ff336c2556507d15d93..620a01d83b11e4dd4db0dc25bc8f5759d3a64579 100644
--- a/protocol/dubbo/server.go
+++ b/remoting/getty/getty_server.go
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package dubbo
+package getty
 
 import (
 	"crypto/tls"
@@ -25,7 +25,8 @@ import (
 
 import (
 	"github.com/apache/dubbo-getty"
-	"github.com/dubbogo/gost/sync"
+	gxsync "github.com/dubbogo/gost/sync"
+	perrors "github.com/pkg/errors"
 	"gopkg.in/yaml.v2"
 )
 
@@ -34,6 +35,9 @@ import (
 	"github.com/apache/dubbo-go/common/constant"
 	"github.com/apache/dubbo-go/common/logger"
 	"github.com/apache/dubbo-go/config"
+	"github.com/apache/dubbo-go/protocol"
+	"github.com/apache/dubbo-go/protocol/invocation"
+	"github.com/apache/dubbo-go/remoting"
 )
 
 var (
@@ -41,8 +45,7 @@ var (
 	srvGrpool *gxsync.TaskPool
 )
 
-func init() {
-
+func initServer(protocol string) {
 	// load clientconfig from provider_config
 	// default use dubbo
 	providerConfig := config.GetProviderConfig()
@@ -54,7 +57,7 @@ func init() {
 	if protocolConf == nil {
 		logger.Info("protocol_conf default use dubbo config")
 	} else {
-		dubboConf := protocolConf.(map[interface{}]interface{})[DUBBO]
+		dubboConf := protocolConf.(map[interface{}]interface{})[protocol]
 		if dubboConf == nil {
 			logger.Warnf("dubboConf is nil")
 			return
@@ -73,7 +76,7 @@ func init() {
 	if err := srvConf.CheckValidity(); err != nil {
 		panic(err)
 	}
-	setServerGrpool()
+	SetServerGrpool()
 }
 
 // SetServerConfig set dubbo server config.
@@ -84,36 +87,50 @@ func SetServerConfig(s ServerConfig) {
 		logger.Warnf("[ServerConfig CheckValidity] error: %v", err)
 		return
 	}
-	setServerGrpool()
+	SetServerGrpool()
 }
 
-// GetServerConfig get dubbo server config.
+// GetServerConfig get getty server config.
 func GetServerConfig() ServerConfig {
 	return *srvConf
 }
 
-func setServerGrpool() {
+// SetServerGrpool set getty server GrPool
+func SetServerGrpool() {
 	if srvConf.GrPoolSize > 1 {
-		srvGrpool = gxsync.NewTaskPool(gxsync.WithTaskPoolTaskPoolSize(srvConf.GrPoolSize), gxsync.WithTaskPoolTaskQueueLength(srvConf.QueueLen),
-			gxsync.WithTaskPoolTaskQueueNumber(srvConf.QueueNumber))
+		srvGrpool = gxsync.NewTaskPool(
+			gxsync.WithTaskPoolTaskPoolSize(srvConf.GrPoolSize),
+			gxsync.WithTaskPoolTaskQueueLength(srvConf.QueueLen),
+			gxsync.WithTaskPoolTaskQueueNumber(srvConf.QueueNumber),
+		)
 	}
 }
 
-// Server is dubbo protocol server.
+// Server define getty server
 type Server struct {
-	conf       ServerConfig
-	tcpServer  getty.Server
-	rpcHandler *RpcServerHandler
+	conf           ServerConfig
+	addr           string
+	codec          remoting.Codec
+	tcpServer      getty.Server
+	rpcHandler     *RpcServerHandler
+	requestHandler func(*invocation.RPCInvocation) protocol.RPCResult
 }
 
-// NewServer create a new Server.
-func NewServer() *Server {
+// NewServer create a new Server
+func NewServer(url *common.URL, handlers func(*invocation.RPCInvocation) protocol.RPCResult) *Server {
+	//init
+	initServer(url.Protocol)
+
+	srvConf.SSLEnabled = url.GetParamBool(constant.SSL_ENABLED_KEY, false)
 
 	s := &Server{
-		conf: *srvConf,
+		conf:           *srvConf,
+		addr:           url.Location,
+		codec:          remoting.GetCodec(url.Protocol),
+		requestHandler: handlers,
 	}
 
-	s.rpcHandler = NewRpcServerHandler(s.conf.SessionNumber, s.conf.sessionTimeout)
+	s.rpcHandler = NewRpcServerHandler(s.conf.SessionNumber, s.conf.sessionTimeout, s)
 
 	return s
 }
@@ -122,6 +139,7 @@ func (s *Server) newSession(session getty.Session) error {
 	var (
 		ok      bool
 		tcpConn *net.TCPConn
+		err     error
 	)
 	conf := s.conf
 
@@ -131,12 +149,12 @@ func (s *Server) newSession(session getty.Session) error {
 	if _, ok = session.Conn().(*tls.Conn); ok {
 		session.SetName(conf.GettySessionParam.SessionName)
 		session.SetMaxMsgLen(conf.GettySessionParam.MaxMsgLen)
-		session.SetPkgHandler(rpcServerPkgHandler)
+		session.SetPkgHandler(NewRpcServerPackageHandler(s))
 		session.SetEventListener(s.rpcHandler)
 		session.SetWQLen(conf.GettySessionParam.PkgWQSize)
 		session.SetReadTimeout(conf.GettySessionParam.tcpReadTimeout)
 		session.SetWriteTimeout(conf.GettySessionParam.tcpWriteTimeout)
-		session.SetCronPeriod((int)(conf.sessionTimeout.Nanoseconds() / 1e6))
+		session.SetCronPeriod((int)(conf.heartbeatPeriod.Nanoseconds() / 1e6))
 		session.SetWaitTime(conf.GettySessionParam.waitTimeout)
 		logger.Debugf("server accepts new session:%s\n", session.Stat())
 		session.SetTaskPool(srvGrpool)
@@ -146,57 +164,69 @@ func (s *Server) newSession(session getty.Session) error {
 		panic(fmt.Sprintf("%s, session.conn{%#v} is not tcp connection\n", session.Stat(), session.Conn()))
 	}
 
-	tcpConn.SetNoDelay(conf.GettySessionParam.TcpNoDelay)
-	tcpConn.SetKeepAlive(conf.GettySessionParam.TcpKeepAlive)
-	if conf.GettySessionParam.TcpKeepAlive {
-		tcpConn.SetKeepAlivePeriod(conf.GettySessionParam.keepAlivePeriod)
+	if _, ok = session.Conn().(*tls.Conn); !ok {
+		if tcpConn, ok = session.Conn().(*net.TCPConn); !ok {
+			return perrors.New(fmt.Sprintf("%s, session.conn{%#v} is not tcp connection", session.Stat(), session.Conn()))
+		}
+
+		if err = tcpConn.SetNoDelay(conf.GettySessionParam.TcpNoDelay); err != nil {
+			return err
+		}
+		if err = tcpConn.SetKeepAlive(conf.GettySessionParam.TcpKeepAlive); err != nil {
+			return err
+		}
+		if conf.GettySessionParam.TcpKeepAlive {
+			if err = tcpConn.SetKeepAlivePeriod(conf.GettySessionParam.keepAlivePeriod); err != nil {
+				return err
+			}
+		}
+		if err = tcpConn.SetReadBuffer(conf.GettySessionParam.TcpRBufSize); err != nil {
+			return err
+		}
+		if err = tcpConn.SetWriteBuffer(conf.GettySessionParam.TcpWBufSize); err != nil {
+			return err
+		}
 	}
-	tcpConn.SetReadBuffer(conf.GettySessionParam.TcpRBufSize)
-	tcpConn.SetWriteBuffer(conf.GettySessionParam.TcpWBufSize)
 
 	session.SetName(conf.GettySessionParam.SessionName)
 	session.SetMaxMsgLen(conf.GettySessionParam.MaxMsgLen)
-	session.SetPkgHandler(rpcServerPkgHandler)
+	session.SetPkgHandler(NewRpcServerPackageHandler(s))
 	session.SetEventListener(s.rpcHandler)
 	session.SetWQLen(conf.GettySessionParam.PkgWQSize)
 	session.SetReadTimeout(conf.GettySessionParam.tcpReadTimeout)
 	session.SetWriteTimeout(conf.GettySessionParam.tcpWriteTimeout)
-	session.SetCronPeriod((int)(conf.sessionTimeout.Nanoseconds() / 1e6))
+	session.SetCronPeriod((int)(conf.heartbeatPeriod.Nanoseconds() / 1e6))
 	session.SetWaitTime(conf.GettySessionParam.waitTimeout)
-	logger.Debugf("app accepts new session:%s\n", session.Stat())
-
+	logger.Debugf("server accepts new session: %s", session.Stat())
 	session.SetTaskPool(srvGrpool)
-
 	return nil
 }
 
-// Start start dubbo server.
-func (s *Server) Start(url common.URL) {
+// Start dubbo server.
+func (s *Server) Start() {
 	var (
 		addr      string
 		tcpServer getty.Server
 	)
 
-	addr = url.Location
-	if url.GetParamBool(constant.SSL_ENABLED_KEY, false) {
+	addr = s.addr
+	if s.conf.SSLEnabled {
 		tcpServer = getty.NewTCPServer(
 			getty.WithLocalAddress(addr),
-			getty.WithServerSslEnabled(url.GetParamBool(constant.SSL_ENABLED_KEY, false)),
+			getty.WithServerSslEnabled(s.conf.SSLEnabled),
 			getty.WithServerTlsConfigBuilder(config.GetServerTlsConfigBuilder()),
 		)
-
 	} else {
 		tcpServer = getty.NewTCPServer(
 			getty.WithLocalAddress(addr),
 		)
 	}
 	tcpServer.RunEventLoop(s.newSession)
-	logger.Debugf("s bind addr{%s} ok!", addr)
+	logger.Debugf("s bind addr{%s} ok!", s.addr)
 	s.tcpServer = tcpServer
-
 }
 
-// Stop stop dubbo server.
+// Stop dubbo server
 func (s *Server) Stop() {
 	s.tcpServer.Close()
 }
diff --git a/remoting/getty/listener.go b/remoting/getty/listener.go
new file mode 100644
index 0000000000000000000000000000000000000000..b2f7790f2ffcab6832224e07219016cb46a3fa4d
--- /dev/null
+++ b/remoting/getty/listener.go
@@ -0,0 +1,383 @@
+/*
+ * 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 getty
+
+import (
+	"fmt"
+	"sync"
+	"sync/atomic"
+	"time"
+)
+
+import (
+	"github.com/apache/dubbo-getty"
+	hessian "github.com/apache/dubbo-go-hessian2"
+	perrors "github.com/pkg/errors"
+)
+
+import (
+	"github.com/apache/dubbo-go/common/constant"
+	"github.com/apache/dubbo-go/common/logger"
+	"github.com/apache/dubbo-go/protocol/invocation"
+	"github.com/apache/dubbo-go/remoting"
+)
+
+// todo: WritePkg_Timeout will entry *.yml
+const (
+	// WritePkg_Timeout the timeout of write pkg
+	WritePkg_Timeout = 5 * time.Second
+)
+
+var (
+	errTooManySessions      = perrors.New("too many sessions")
+	errHeartbeatReadTimeout = perrors.New("heartbeat read timeout")
+)
+
+type rpcSession struct {
+	session getty.Session
+	reqNum  int32
+}
+
+func (s *rpcSession) AddReqNum(num int32) {
+	atomic.AddInt32(&s.reqNum, num)
+}
+
+func (s *rpcSession) GetReqNum() int32 {
+	return atomic.LoadInt32(&s.reqNum)
+}
+
+// //////////////////////////////////////////
+// RpcClientHandler
+// //////////////////////////////////////////
+
+// nolint
+type RpcClientHandler struct {
+	conn         *gettyRPCClient
+	timeoutTimes int
+}
+
+// nolint
+func NewRpcClientHandler(client *gettyRPCClient) *RpcClientHandler {
+	return &RpcClientHandler{conn: client}
+}
+
+// OnOpen call the getty client session opened, add the session to getty client session list
+func (h *RpcClientHandler) OnOpen(session getty.Session) error {
+	h.conn.addSession(session)
+	return nil
+}
+
+// OnError the getty client session has errored, so remove the session from the getty client session list
+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 close the session, remove it from the getty session list
+func (h *RpcClientHandler) OnClose(session getty.Session) {
+	logger.Infof("session{%s} is closing......", session.Stat())
+	h.conn.removeSession(session)
+}
+
+// OnMessage get response from getty server, and update the session to the getty client session list
+func (h *RpcClientHandler) OnMessage(session getty.Session, pkg interface{}) {
+	result, ok := pkg.(remoting.DecodeResult)
+	if !ok {
+		logger.Errorf("illegal package")
+		return
+	}
+	// get heartbeart request from server
+	if result.IsRequest {
+		req := result.Result.(*remoting.Request)
+		if req.Event {
+			logger.Debugf("get rpc heartbeat request{%#v}", req)
+			resp := remoting.NewResponse(req.ID, req.Version)
+			resp.Status = hessian.Response_OK
+			resp.Event = req.Event
+			resp.SerialID = req.SerialID
+			resp.Version = "2.0.2"
+			reply(session, resp)
+			return
+		}
+		logger.Errorf("illegal request but not heartbeart. {%#v}", req)
+		return
+	}
+	h.timeoutTimes = 0
+	p := result.Result.(*remoting.Response)
+	// get heartbeart
+	if p.Event {
+		logger.Debugf("get rpc heartbeat response{%#v}", p)
+		if p.Error != nil {
+			logger.Errorf("rpc heartbeat response{error: %#v}", p.Error)
+		}
+		p.Handle()
+		return
+	}
+
+	logger.Debugf("get rpc response{%#v}", p)
+
+	h.conn.updateSession(session)
+
+	p.Handle()
+}
+
+// OnCron check the session health periodic. if the session's sessionTimeout has reached, just close the session
+func (h *RpcClientHandler) OnCron(session getty.Session) {
+	rpcSession, err := h.conn.getClientRpcSession(session)
+	if err != nil {
+		logger.Errorf("client.getClientSession(session{%s}) = error{%v}",
+			session.Stat(), perrors.WithStack(err))
+		return
+	}
+	if h.conn.pool.rpcClient.conf.sessionTimeout.Nanoseconds() < time.Since(session.GetActive()).Nanoseconds() {
+		logger.Warnf("session{%s} timeout{%s}, reqNum{%d}",
+			session.Stat(), time.Since(session.GetActive()).String(), rpcSession.reqNum)
+		h.conn.removeSession(session) // -> h.conn.close() -> h.conn.pool.remove(h.conn)
+		return
+	}
+
+	heartbeatCallBack := func(err error) {
+		if err != nil {
+			logger.Warnf("failed to send heartbeat, error{%v}", err)
+			if h.timeoutTimes >= 3 {
+				h.conn.removeSession(session)
+				return
+			}
+			h.timeoutTimes++
+			return
+		}
+		h.timeoutTimes = 0
+	}
+
+	if err := heartbeat(session, h.conn.pool.rpcClient.conf.heartbeatTimeout, heartbeatCallBack); err != nil {
+		logger.Warnf("failed to send heartbeat, error{%v}", err)
+	}
+}
+
+// //////////////////////////////////////////
+// RpcServerHandler
+// //////////////////////////////////////////
+
+// nolint
+type RpcServerHandler struct {
+	maxSessionNum  int
+	sessionTimeout time.Duration
+	sessionMap     map[getty.Session]*rpcSession
+	rwlock         sync.RWMutex
+	server         *Server
+	timeoutTimes   int
+}
+
+// nolint
+func NewRpcServerHandler(maxSessionNum int, sessionTimeout time.Duration, serverP *Server) *RpcServerHandler {
+	return &RpcServerHandler{
+		maxSessionNum:  maxSessionNum,
+		sessionTimeout: sessionTimeout,
+		sessionMap:     make(map[getty.Session]*rpcSession),
+		server:         serverP,
+	}
+}
+
+// OnOpen call server session opened, add the session to getty server session list. also onOpen
+// will check the max getty server session number
+func (h *RpcServerHandler) OnOpen(session getty.Session) error {
+	var err error
+	h.rwlock.RLock()
+	if h.maxSessionNum <= len(h.sessionMap) {
+		err = errTooManySessions
+	}
+	h.rwlock.RUnlock()
+	if err != nil {
+		return perrors.WithStack(err)
+	}
+
+	logger.Infof("got session:%s", session.Stat())
+	h.rwlock.Lock()
+	h.sessionMap[session] = &rpcSession{session: session}
+	h.rwlock.Unlock()
+	return nil
+}
+
+// OnError the getty server session has errored, so remove the session from the getty server session list
+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()
+	delete(h.sessionMap, session)
+	h.rwlock.Unlock()
+}
+
+// OnClose close the session, remove it from the getty server list
+func (h *RpcServerHandler) OnClose(session getty.Session) {
+	logger.Infof("session{%s} is closing......", session.Stat())
+	h.rwlock.Lock()
+	delete(h.sessionMap, session)
+	h.rwlock.Unlock()
+}
+
+// OnMessage get request from getty client, update the session reqNum and reply response to client
+func (h *RpcServerHandler) OnMessage(session getty.Session, pkg interface{}) {
+	h.rwlock.Lock()
+	if _, ok := h.sessionMap[session]; ok {
+		h.sessionMap[session].reqNum++
+	}
+	h.rwlock.Unlock()
+
+	decodeResult, ok := pkg.(remoting.DecodeResult)
+	if !ok {
+		logger.Errorf("illegal package{%#v}", pkg)
+		return
+	}
+	if !decodeResult.IsRequest {
+		res := decodeResult.Result.(*remoting.Response)
+		if res.Event {
+			logger.Debugf("get rpc heartbeat response{%#v}", res)
+			if res.Error != nil {
+				logger.Errorf("rpc heartbeat response{error: %#v}", res.Error)
+			}
+			res.Handle()
+			return
+		}
+		logger.Errorf("illegal package but not heartbeart. {%#v}", pkg)
+		return
+	}
+	req := decodeResult.Result.(*remoting.Request)
+
+	resp := remoting.NewResponse(req.ID, req.Version)
+	resp.Status = hessian.Response_OK
+	resp.Event = req.Event
+	resp.SerialID = req.SerialID
+	resp.Version = "2.0.2"
+
+	// heartbeat
+	if req.Event {
+		logger.Debugf("get rpc heartbeat request{%#v}", resp)
+		reply(session, resp)
+		return
+	}
+
+	defer func() {
+		if e := recover(); e != nil {
+			resp.Status = hessian.Response_SERVER_ERROR
+			if err, ok := e.(error); ok {
+				logger.Errorf("OnMessage panic: %+v", perrors.WithStack(err))
+				resp.Error = perrors.WithStack(err)
+			} else if err, ok := e.(string); ok {
+				logger.Errorf("OnMessage panic: %+v", perrors.New(err))
+				resp.Error = perrors.New(err)
+			} else {
+				logger.Errorf("OnMessage panic: %+v, this is impossible.", e)
+				resp.Error = fmt.Errorf("OnMessage panic unknow exception. %+v", e)
+			}
+
+			if !req.TwoWay {
+				return
+			}
+			reply(session, resp)
+		}
+
+	}()
+
+	invoc, ok := req.Data.(*invocation.RPCInvocation)
+	if !ok {
+		panic("create invocation occur some exception for the type is not suitable one.")
+	}
+	attachments := invoc.Attachments()
+	attachments[constant.LOCAL_ADDR] = session.LocalAddr()
+	attachments[constant.REMOTE_ADDR] = session.RemoteAddr()
+
+	result := h.server.requestHandler(invoc)
+	if !req.TwoWay {
+		return
+	}
+	resp.Result = result
+	reply(session, resp)
+}
+
+// OnCron check the session health periodic. if the session's sessionTimeout has reached, just close the session
+func (h *RpcServerHandler) OnCron(session getty.Session) {
+	var (
+		flag   bool
+		active time.Time
+	)
+
+	h.rwlock.RLock()
+	if _, ok := h.sessionMap[session]; ok {
+		active = session.GetActive()
+		if h.sessionTimeout.Nanoseconds() < time.Since(active).Nanoseconds() {
+			flag = true
+			logger.Warnf("session{%s} timeout{%s}, reqNum{%d}",
+				session.Stat(), time.Since(active).String(), h.sessionMap[session].reqNum)
+		}
+	}
+	h.rwlock.RUnlock()
+
+	if flag {
+		h.rwlock.Lock()
+		delete(h.sessionMap, session)
+		h.rwlock.Unlock()
+		session.Close()
+	}
+
+	heartbeatCallBack := func(err error) {
+		if err != nil {
+			logger.Warnf("failed to send heartbeat, error{%v}", err)
+			if h.timeoutTimes >= 3 {
+				h.rwlock.Lock()
+				delete(h.sessionMap, session)
+				h.rwlock.Unlock()
+				session.Close()
+				return
+			}
+			h.timeoutTimes++
+			return
+		}
+		h.timeoutTimes = 0
+	}
+
+	if err := heartbeat(session, h.server.conf.heartbeatTimeout, heartbeatCallBack); err != nil {
+		logger.Warnf("failed to send heartbeat, error{%v}", err)
+	}
+}
+
+func reply(session getty.Session, resp *remoting.Response) {
+	if err := session.WritePkg(resp, WritePkg_Timeout); err != nil {
+		logger.Errorf("WritePkg error: %#v, %#v", perrors.WithStack(err), resp)
+	}
+}
+
+func heartbeat(session getty.Session, timeout time.Duration, callBack func(err error)) error {
+	req := remoting.NewRequest("2.0.2")
+	req.TwoWay = true
+	req.Event = true
+	resp := remoting.NewPendingResponse(req.ID)
+	remoting.AddPendingResponse(resp)
+	err := session.WritePkg(req, 3*time.Second)
+
+	go func() {
+		var err1 error
+		select {
+		case <-getty.GetTimeWheel().After(timeout):
+			err1 = errHeartbeatReadTimeout
+		case <-resp.Done:
+			err1 = resp.Err
+		}
+		callBack(err1)
+	}()
+
+	return perrors.WithStack(err)
+}
diff --git a/protocol/dubbo/listener_test.go b/remoting/getty/listener_test.go
similarity index 69%
rename from protocol/dubbo/listener_test.go
rename to remoting/getty/listener_test.go
index 5f809814607558650e09934019db96dbb2ceeeae..7e7ac5fed440a02188057d520a944b48c8bf7b64 100644
--- a/protocol/dubbo/listener_test.go
+++ b/remoting/getty/listener_test.go
@@ -15,27 +15,28 @@
  * limitations under the License.
  */
 
-package dubbo
+package getty
 
 import (
+	"context"
 	"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"
+	"github.com/opentracing/opentracing-go"
+	"github.com/opentracing/opentracing-go/mocktracer"
 )
 
 // test rebuild the ctx
 func TestRebuildCtx(t *testing.T) {
 	opentracing.SetGlobalTracer(mocktracer.New())
-	attach := make(map[string]string, 10)
+	attach := make(map[string]interface{}, 10)
 	attach[constant.VERSION_KEY] = "1.0"
 	attach[constant.GROUP_KEY] = "MyGroup"
 	inv := invocation.NewRPCInvocation("MethodName", []interface{}{"OK", "Hello"}, attach)
@@ -47,8 +48,9 @@ func TestRebuildCtx(t *testing.T) {
 
 	span, ctx := opentracing.StartSpanFromContext(ctx, "Test-Client")
 
-	opentracing.GlobalTracer().Inject(span.Context(), opentracing.TextMap,
-		opentracing.TextMapCarrier(inv.Attachments()))
+	err := injectTraceCtx(span, inv)
+	assert.NoError(t, err)
+
 	// rebuild the context success
 	inv = invocation.NewRPCInvocation("MethodName", []interface{}{"OK", "Hello"}, attach)
 	ctx = rebuildCtx(inv)
@@ -56,3 +58,18 @@ func TestRebuildCtx(t *testing.T) {
 	assert.NotNil(t, ctx)
 	assert.NotNil(t, ctx.Value(constant.TRACING_REMOTE_SPAN_CTX))
 }
+
+// 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.WithValue(context.Background(), "attachment", inv.Attachments())
+
+	// actually, if user do not use any opentracing framework, the err will not be nil.
+	spanCtx, err := opentracing.GlobalTracer().Extract(opentracing.TextMap,
+		opentracing.TextMapCarrier(filterContext(inv.Attachments())))
+	if err == nil {
+		ctx = context.WithValue(ctx, constant.TRACING_REMOTE_SPAN_CTX, spanCtx)
+	}
+	return ctx
+}
diff --git a/remoting/getty/opentracing.go b/remoting/getty/opentracing.go
new file mode 100644
index 0000000000000000000000000000000000000000..7db733cbe919f2bef46cfc477bda836dc2da0d45
--- /dev/null
+++ b/remoting/getty/opentracing.go
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package getty
+
+import (
+	"github.com/opentracing/opentracing-go"
+)
+import (
+	invocation_impl "github.com/apache/dubbo-go/protocol/invocation"
+)
+
+func injectTraceCtx(currentSpan opentracing.Span, inv *invocation_impl.RPCInvocation) error {
+	// inject opentracing ctx
+	traceAttachments := filterContext(inv.Attachments())
+	carrier := opentracing.TextMapCarrier(traceAttachments)
+	err := opentracing.GlobalTracer().Inject(currentSpan.Context(), opentracing.TextMap, carrier)
+	if err == nil {
+		fillTraceAttachments(inv.Attachments(), traceAttachments)
+	}
+	return err
+}
+
+func extractTraceCtx(inv *invocation_impl.RPCInvocation) (opentracing.SpanContext, error) {
+	traceAttachments := filterContext(inv.Attachments())
+	// actually, if user do not use any opentracing framework, the err will not be nil.
+	spanCtx, err := opentracing.GlobalTracer().Extract(opentracing.TextMap,
+		opentracing.TextMapCarrier(traceAttachments))
+	return spanCtx, err
+}
+
+func filterContext(attachments map[string]interface{}) map[string]string {
+	var traceAttchment = make(map[string]string)
+	for k, v := range attachments {
+		if r, ok := v.(string); ok {
+			traceAttchment[k] = r
+		}
+	}
+	return traceAttchment
+}
+
+func fillTraceAttachments(attachments map[string]interface{}, traceAttachment map[string]string) {
+	for k, v := range traceAttachment {
+		attachments[k] = v
+	}
+}
diff --git a/protocol/dubbo/pool.go b/remoting/getty/pool.go
similarity index 88%
rename from protocol/dubbo/pool.go
rename to remoting/getty/pool.go
index 6a7d211b496d3f6905c8a84e2f1bd648718ebb92..9689175bcf9838de595f292779b099ae9615d8e8 100644
--- a/protocol/dubbo/pool.go
+++ b/remoting/getty/pool.go
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package dubbo
+package getty
 
 import (
 	"crypto/tls"
@@ -38,10 +38,10 @@ import (
 )
 
 type gettyRPCClient struct {
-	once     sync.Once
-	protocol string
-	addr     string
-	active   int64 // zero, not create or be destroyed
+	once sync.Once
+	//protocol string
+	addr   string
+	active int64 // zero, not create or be destroyed
 
 	pool *gettyRPCClientPool
 
@@ -54,7 +54,7 @@ var (
 	errClientPoolClosed = perrors.New("client pool closed")
 )
 
-func newGettyRPCClientConn(pool *gettyRPCClientPool, protocol, addr string) (*gettyRPCClient, error) {
+func newGettyRPCClientConn(pool *gettyRPCClientPool, addr string) (*gettyRPCClient, error) {
 	var (
 		gettyClient getty.Client
 		sslEnabled  bool
@@ -76,25 +76,31 @@ func newGettyRPCClientConn(pool *gettyRPCClientPool, protocol, addr string) (*ge
 		)
 	}
 	c := &gettyRPCClient{
-		protocol:    protocol,
 		addr:        addr,
 		pool:        pool,
 		gettyClient: gettyClient,
 	}
 	go c.gettyClient.RunEventLoop(c.newSession)
+
 	idx := 1
-	times := int(pool.rpcClient.opts.ConnectTimeout / 1e6)
+	start := time.Now()
+	connectTimeout := pool.rpcClient.opts.ConnectTimeout
 	for {
 		idx++
 		if c.isAvailable() {
 			break
 		}
 
-		if idx > times {
+		if time.Now().Sub(start) > connectTimeout {
 			c.gettyClient.Close()
-			return nil, perrors.New(fmt.Sprintf("failed to create client connection to %s in %f seconds", addr, float32(times)/1000))
+			return nil, perrors.New(fmt.Sprintf("failed to create client connection to %s in %s", addr, connectTimeout))
+		}
+
+		interval := time.Millisecond * time.Duration(idx)
+		if interval > time.Duration(100e6) {
+			interval = 100e6 // 100 ms
 		}
-		time.Sleep(1e6)
+		time.Sleep(interval)
 	}
 	logger.Debug("client init ok")
 	c.updateActive(time.Now().Unix())
@@ -117,7 +123,6 @@ func (c *gettyRPCClient) newSession(session getty.Session) error {
 		conf       ClientConfig
 		sslEnabled bool
 	)
-
 	conf = c.pool.rpcClient.conf
 	sslEnabled = c.pool.sslEnabled
 	if conf.GettySessionParam.CompressEncoding {
@@ -255,25 +260,25 @@ func (c *gettyRPCClient) updateSession(session getty.Session) {
 
 func (c *gettyRPCClient) getClientRpcSession(session getty.Session) (rpcSession, error) {
 	var (
-		err              error
-		rpcClientSession rpcSession
+		err        error
+		rpcSession rpcSession
 	)
 	c.lock.RLock()
 	defer c.lock.RUnlock()
 	if c.sessions == nil {
-		return rpcClientSession, errClientClosed
+		return rpcSession, errClientClosed
 	}
 
 	err = errSessionNotExist
 	for _, s := range c.sessions {
 		if s.session == session {
-			rpcClientSession = *s
+			rpcSession = *s
 			err = nil
 			break
 		}
 	}
 
-	return rpcClientSession, perrors.WithStack(err)
+	return rpcSession, perrors.WithStack(err)
 }
 
 func (c *gettyRPCClient) isAvailable() bool {
@@ -338,7 +343,8 @@ func newGettyRPCClientConnPool(rpcClient *Client, size int, ttl time.Duration) *
 		rpcClient: rpcClient,
 		size:      size,
 		ttl:       int64(ttl.Seconds()),
-		conns:     make([]*gettyRPCClient, 0, 16),
+		// init capacity : 2
+		conns: make([]*gettyRPCClient, 0, 2),
 	}
 }
 
@@ -352,11 +358,15 @@ func (p *gettyRPCClientPool) close() {
 	}
 }
 
-func (p *gettyRPCClientPool) getGettyRpcClient(protocol, addr string) (*gettyRPCClient, error) {
+func (p *gettyRPCClientPool) getGettyRpcClient(addr string) (*gettyRPCClient, error) {
 	conn, err := p.get()
 	if err == nil && conn == nil {
 		// create new conn
-		conn, err = newGettyRPCClientConn(p, protocol, addr)
+		rpcClientConn, err := newGettyRPCClientConn(p, addr)
+		if err == nil {
+			p.put(rpcClientConn)
+		}
+		return rpcClientConn, perrors.WithStack(err)
 	}
 	return conn, perrors.WithStack(err)
 }
@@ -369,14 +379,20 @@ func (p *gettyRPCClientPool) get() (*gettyRPCClient, error) {
 	if p.conns == nil {
 		return nil, errClientPoolClosed
 	}
-
-	for len(p.conns) > 0 {
-		conn := p.conns[len(p.conns)-1]
-		p.conns = p.conns[:len(p.conns)-1]
+	for num := len(p.conns); num > 0; {
+		var conn *gettyRPCClient
+		if num != 1 {
+			conn = p.conns[rand.Int31n(int32(num))]
+		} else {
+			conn = p.conns[0]
+		}
+		// This will recreate gettyRpcClient for remove last position
+		//p.conns = p.conns[:len(p.conns)-1]
 
 		if d := now - conn.getActive(); d > p.ttl {
 			p.remove(conn)
 			go conn.close()
+			num = len(p.conns)
 			continue
 		}
 		conn.updateActive(now) //update active time
@@ -389,21 +405,17 @@ func (p *gettyRPCClientPool) put(conn *gettyRPCClient) {
 	if conn == nil || conn.getActive() == 0 {
 		return
 	}
-
 	p.Lock()
 	defer p.Unlock()
-
 	if p.conns == nil {
 		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)
diff --git a/remoting/getty/pool_test.go b/remoting/getty/pool_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..1115a490428da14a72f5c40e1b98b2a7fb3f80ad
--- /dev/null
+++ b/remoting/getty/pool_test.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 getty
+
+import (
+	"testing"
+	"time"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+func TestGetConnFromPool(t *testing.T) {
+	var rpcClient Client
+
+	clientPoll := newGettyRPCClientConnPool(&rpcClient, 1, time.Duration(5*time.Second))
+
+	var conn1 gettyRPCClient
+	conn1.active = time.Now().Unix()
+	clientPoll.put(&conn1)
+	assert.Equal(t, 1, len(clientPoll.conns))
+
+	var conn2 gettyRPCClient
+	conn2.active = time.Now().Unix()
+	clientPoll.put(&conn2)
+	assert.Equal(t, 1, len(clientPoll.conns))
+	conn, err := clientPoll.get()
+	assert.Nil(t, err)
+	assert.Equal(t, &conn1, conn)
+	time.Sleep(6 * time.Second)
+	conn, err = clientPoll.get()
+	assert.Nil(t, conn)
+	assert.Nil(t, err)
+	assert.Equal(t, 0, len(clientPoll.conns))
+}
diff --git a/remoting/getty/readwriter.go b/remoting/getty/readwriter.go
new file mode 100644
index 0000000000000000000000000000000000000000..072eb3e61538fc5fc358846dcb697ecff9552d4d
--- /dev/null
+++ b/remoting/getty/readwriter.go
@@ -0,0 +1,154 @@
+/*
+ * 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 getty
+
+import (
+	"reflect"
+)
+
+import (
+	"github.com/apache/dubbo-getty"
+	hessian "github.com/apache/dubbo-go-hessian2"
+	perrors "github.com/pkg/errors"
+)
+
+import (
+	"github.com/apache/dubbo-go/common/logger"
+	"github.com/apache/dubbo-go/remoting"
+)
+
+////////////////////////////////////////////
+// RpcClientPackageHandler
+////////////////////////////////////////////
+
+// RpcClientPackageHandler Read data from server and Write data to server
+type RpcClientPackageHandler struct {
+	client *Client
+}
+
+// NewRpcClientPackageHandler create a RpcClientPackageHandler
+func NewRpcClientPackageHandler(client *Client) *RpcClientPackageHandler {
+	return &RpcClientPackageHandler{client: client}
+}
+
+// Read data from server. if the package size from server is larger than 4096 byte, server will read 4096 byte
+// and send to client each time. the Read can assemble it.
+func (p *RpcClientPackageHandler) Read(ss getty.Session, data []byte) (interface{}, int, error) {
+	resp, length, err := (p.client.codec).Decode(data)
+	//err := pkg.Unmarshal(buf, p.client)
+	if err != nil {
+		err = perrors.Cause(err)
+		if err == hessian.ErrHeaderNotEnough || err == hessian.ErrBodyNotEnough {
+			return nil, 0, nil
+		}
+
+		logger.Errorf("pkg.Unmarshal(ss:%+v, len(@data):%d) = error:%+v", ss, len(data), err)
+
+		return nil, length, err
+	}
+
+	return resp, length, nil
+}
+
+// Write send the data to server
+func (p *RpcClientPackageHandler) Write(ss getty.Session, pkg interface{}) ([]byte, error) {
+	req, ok := pkg.(*remoting.Request)
+	if ok {
+		buf, err := (p.client.codec).EncodeRequest(req)
+		if err != nil {
+			logger.Warnf("binary.Write(req{%#v}) = err{%#v}", req, perrors.WithStack(err))
+			return nil, perrors.WithStack(err)
+		}
+		return buf.Bytes(), nil
+	}
+
+	res, ok := pkg.(*remoting.Response)
+	if ok {
+		buf, err := (p.client.codec).EncodeResponse(res)
+		if err != nil {
+			logger.Warnf("binary.Write(res{%#v}) = err{%#v}", req, perrors.WithStack(err))
+			return nil, perrors.WithStack(err)
+		}
+		return buf.Bytes(), nil
+	}
+
+	logger.Errorf("illegal pkg:%+v\n", pkg)
+	return nil, perrors.New("invalid rpc request")
+}
+
+////////////////////////////////////////////
+// RpcServerPackageHandler
+////////////////////////////////////////////
+
+//var (
+//	rpcServerPkgHandler = &RpcServerPackageHandler{}
+//)
+
+// RpcServerPackageHandler Read data from client and Write data to client
+type RpcServerPackageHandler struct {
+	server *Server
+}
+
+func NewRpcServerPackageHandler(server *Server) *RpcServerPackageHandler {
+	return &RpcServerPackageHandler{server: server}
+}
+
+// Read data from client. if the package size from client is larger than 4096 byte, client will read 4096 byte
+// and send to client each time. the Read can assemble it.
+func (p *RpcServerPackageHandler) Read(ss getty.Session, data []byte) (interface{}, int, error) {
+	req, length, err := (p.server.codec).Decode(data)
+	//resp,len, err := (*p.).DecodeResponse(buf)
+	if err != nil {
+		if err == hessian.ErrHeaderNotEnough || err == hessian.ErrBodyNotEnough {
+			return nil, 0, nil
+		}
+
+		logger.Errorf("pkg.Unmarshal(ss:%+v, len(@data):%d) = error:%+v", ss, len(data), err)
+
+		return nil, 0, err
+	}
+
+	return req, length, err
+}
+
+// Write send the data to client
+func (p *RpcServerPackageHandler) Write(ss getty.Session, pkg interface{}) ([]byte, error) {
+	res, ok := pkg.(*remoting.Response)
+	if ok {
+		buf, err := (p.server.codec).EncodeResponse(res)
+		if err != nil {
+			logger.Warnf("binary.Write(res{%#v}) = err{%#v}", res, perrors.WithStack(err))
+			return nil, perrors.WithStack(err)
+		}
+		return buf.Bytes(), nil
+	}
+
+	req, ok := pkg.(*remoting.Request)
+	if ok {
+		buf, err := (p.server.codec).EncodeRequest(req)
+		if err != nil {
+			logger.Warnf("binary.Write(req{%#v}) = err{%#v}", res, perrors.WithStack(err))
+			return nil, perrors.WithStack(err)
+		}
+		return buf.Bytes(), nil
+	}
+
+	logger.Errorf("illegal pkg:%+v\n, it is %+v", pkg, reflect.TypeOf(pkg))
+	return nil, perrors.New("invalid rpc response")
+
+}
diff --git a/remoting/kubernetes/client.go b/remoting/kubernetes/client.go
index 0a0548959a3e6d839321d03a627bb6aba66d8474..fce9e80eb88b73cfbe96a4aaebeb106a902fe41d 100644
--- a/remoting/kubernetes/client.go
+++ b/remoting/kubernetes/client.go
@@ -47,7 +47,7 @@ type Client struct {
 }
 
 // newClient returns Client instance for registry
-func newClient(url common.URL) (*Client, error) {
+func newClient(url *common.URL) (*Client, error) {
 
 	ctx, cancel := context.WithCancel(context.Background())
 
diff --git a/remoting/kubernetes/facade_test.go b/remoting/kubernetes/facade_test.go
index 65c5d715a38bd0862245255e0276ff5e959de3a3..00e2e1171c54c2b07973b66cb96cf64e67683f00 100644
--- a/remoting/kubernetes/facade_test.go
+++ b/remoting/kubernetes/facade_test.go
@@ -43,8 +43,8 @@ func (r *mockFacade) SetClient(client *Client) {
 	r.client = client
 }
 
-func (r *mockFacade) GetUrl() common.URL {
-	return *r.URL
+func (r *mockFacade) GetUrl() *common.URL {
+	return r.URL
 }
 
 func (r *mockFacade) Destroy() {
@@ -68,7 +68,7 @@ func Test_Facade(t *testing.T) {
 
 	mockClient := getTestClient(t)
 	m := &mockFacade{
-		URL:    &regUrl,
+		URL:    regUrl,
 		client: mockClient,
 	}
 
diff --git a/remoting/zookeeper/curator_discovery/service_discovery.go b/remoting/zookeeper/curator_discovery/service_discovery.go
index 9566b5494389325520b4eb6a8eb170e0b305bb47..acd43c0b92bd6220efc6527efc1748ed3021f7ac 100644
--- a/remoting/zookeeper/curator_discovery/service_discovery.go
+++ b/remoting/zookeeper/curator_discovery/service_discovery.go
@@ -154,7 +154,6 @@ func (sd *ServiceDiscovery) updateInternalService(name, id string) {
 		return
 	}
 	entry.instance = instance
-	return
 }
 
 // UnregisterService un-register service in zookeeper and delete service in cache
diff --git a/remoting/zookeeper/facade.go b/remoting/zookeeper/facade.go
index 2a034390c016864f95303b12b6c56533771075f3..4dc0a549f2ae53edcc7a645de4dfffc8b9da00a0 100644
--- a/remoting/zookeeper/facade.go
+++ b/remoting/zookeeper/facade.go
@@ -37,7 +37,7 @@ type ZkClientFacade interface {
 	WaitGroup() *sync.WaitGroup // for wait group control, zk client listener & zk client container
 	Done() chan struct{}        // for zk client control
 	RestartCallBack() bool
-	GetUrl() common.URL
+	GetUrl() *common.URL
 }
 
 // HandleClientRestart keeps the connection between client and server
diff --git a/remoting/zookeeper/facade_test.go b/remoting/zookeeper/facade_test.go
index 1cd8f064bb15a2ac48b0d62154309b27c55ab946..3d5798c947fb0fb33adce708e1bcdb8fb24e530f 100644
--- a/remoting/zookeeper/facade_test.go
+++ b/remoting/zookeeper/facade_test.go
@@ -68,8 +68,8 @@ func (r *mockFacade) Done() chan struct{} {
 	return r.done
 }
 
-func (r *mockFacade) GetUrl() common.URL {
-	return *r.URL
+func (r *mockFacade) GetUrl() *common.URL {
+	return r.URL
 }
 
 func (r *mockFacade) Destroy() {
@@ -90,7 +90,7 @@ func Test_Facade(t *testing.T) {
 	assert.NoError(t, err)
 	defer ts.Stop()
 	url, _ := common.NewURL("mock://127.0.0.1")
-	mock := newMockFacade(z, &url)
+	mock := newMockFacade(z, url)
 	go HandleClientRestart(mock)
 	states := []zk.State{zk.StateConnecting, zk.StateConnected, zk.StateHasSession}
 	verifyEventStateOrder(t, event, states, "event channel")
diff --git a/remoting/zookeeper/listener.go b/remoting/zookeeper/listener.go
index 486a67e20a0736be20813226b622f1bfa5bd87c0..e5ddcadeaca9c3ce972cbe781413ebec7393afce 100644
--- a/remoting/zookeeper/listener.go
+++ b/remoting/zookeeper/listener.go
@@ -301,6 +301,9 @@ func (l *ZkEventListener) listenDirEvent(conf *common.URL, zkPath string, listen
 			go func(zkPath string, listener remoting.DataListener) {
 				if l.listenServiceNodeEvent(zkPath) {
 					listener.DataChange(remoting.Event{Path: zkPath, Action: remoting.EventTypeDel})
+					l.pathMapLock.Lock()
+					defer l.pathMapLock.Unlock()
+					delete(l.pathMap, zkPath)
 				}
 				logger.Warnf("listenSelf(zk path{%s}) goroutine exit now", zkPath)
 			}(dubboPath, listener)
@@ -322,7 +325,7 @@ func (l *ZkEventListener) listenDirEvent(conf *common.URL, zkPath string, listen
 		for {
 			select {
 			case <-ticker.C:
-				l.handleZkNodeEvent(zkEvent.Path, children, listener)
+				l.handleZkNodeEvent(zkPath, children, listener)
 			case zkEvent = <-childEventCh:
 				logger.Warnf("get a zookeeper zkEvent{type:%s, server:%s, path:%s, state:%d-%s, err:%s}",
 					zkEvent.Type.String(), zkEvent.Server, zkEvent.Path, zkEvent.State, StateToString(zkEvent.State), zkEvent.Err)
diff --git a/test/integrate/dubbo/go-client/go.mod b/test/integrate/dubbo/go-client/go.mod
index 4708eb1f0f48c10acc254880ecb6dad3a03529f2..162f32ba9b7a217e7ae8bcfea5aa5b91d76b383c 100644
--- a/test/integrate/dubbo/go-client/go.mod
+++ b/test/integrate/dubbo/go-client/go.mod
@@ -1,3 +1,5 @@
 module github.com/apache/dubbo-go/test/integrate/dubbo/go-client
 
+require github.com/apache/dubbo-go-hessian2 v1.7.0
+
 go 1.13
diff --git a/test/integrate/dubbo/go-client/go.sum b/test/integrate/dubbo/go-client/go.sum
new file mode 100644
index 0000000000000000000000000000000000000000..fc378395782f0f7e8032cdaed2ec4c4b0266c149
--- /dev/null
+++ b/test/integrate/dubbo/go-client/go.sum
@@ -0,0 +1,11 @@
+github.com/apache/dubbo-go-hessian2 v1.6.0-rc1.0.20200906044240-6c1fb5c3bd44/go.mod h1:7rEw9guWABQa6Aqb8HeZcsYPHsOS7XT1qtJvkmI6c5w=
+github.com/apache/dubbo-go-hessian2 v1.7.0/go.mod h1:7rEw9guWABQa6Aqb8HeZcsYPHsOS7XT1qtJvkmI6c5w=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dubbogo/gost v1.9.0/go.mod h1:pPTjVyoJan3aPxBPNUX0ADkXjPibLo+/Ib0/fADXSG8=
+github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
diff --git a/test/integrate/dubbo/go-server/go.mod b/test/integrate/dubbo/go-server/go.mod
index 9e1162327de374fb131c2a0b89d1be3baa578a1b..f9d950e0d1b9b7e56922120772c98699f7b3acc9 100644
--- a/test/integrate/dubbo/go-server/go.mod
+++ b/test/integrate/dubbo/go-server/go.mod
@@ -1,3 +1,5 @@
 module github.com/apache/dubbo-go/test/integrate/dubbo/go-server
 
+require github.com/apache/dubbo-go-hessian2 v1.7.0
+
 go 1.13
diff --git a/test/integrate/dubbo/go-server/go.sum b/test/integrate/dubbo/go-server/go.sum
new file mode 100644
index 0000000000000000000000000000000000000000..fc378395782f0f7e8032cdaed2ec4c4b0266c149
--- /dev/null
+++ b/test/integrate/dubbo/go-server/go.sum
@@ -0,0 +1,11 @@
+github.com/apache/dubbo-go-hessian2 v1.6.0-rc1.0.20200906044240-6c1fb5c3bd44/go.mod h1:7rEw9guWABQa6Aqb8HeZcsYPHsOS7XT1qtJvkmI6c5w=
+github.com/apache/dubbo-go-hessian2 v1.7.0/go.mod h1:7rEw9guWABQa6Aqb8HeZcsYPHsOS7XT1qtJvkmI6c5w=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dubbogo/gost v1.9.0/go.mod h1:pPTjVyoJan3aPxBPNUX0ADkXjPibLo+/Ib0/fADXSG8=
+github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=