diff --git a/README.md b/README.md
index 72f59413200d289fac865d328468a9712b0a7f2f..694f0091533f644b58438c6487dc53348add6e60 100644
--- a/README.md
+++ b/README.md
@@ -15,6 +15,10 @@ Apache License, Version 2.0
 
 [v1.0.0 - May 29, 2019 compatible with dubbo v2.6.5](https://github.com/apache/dubbo-go/releases/tag/v1.0.0)
 
+[v1.1.0 - Sep 7, 2019 the first release after transferred to apache](https://github.com/apache/dubbo-go/releases/tag/v1.1.0)
+
+[v1.2.0 - Nov 15, 2019](https://github.com/apache/dubbo-go/releases/tag/v1.2.0)
+
 ## Project Architecture ##
 
 Both extension module and layered project architecture is according to Apache Dubbo (including protocol layer, registry layer, cluster layer, config layer and so on), the advantage of this arch is as following: you can implement these layered interfaces in your own way, override the default implementation of dubbo-go by calling 'extension.SetXXX' of extension, complete your special needs without modifying the source code. At the same time, you are welcome to contribute implementation of useful extension to the community.
@@ -90,13 +94,15 @@ Working List:
 
 You can know more about dubbo-go by its [roadmap](https://github.com/apache/dubbo-go/wiki/Roadmap).
 
+![feature](https://raw.githubusercontent.com/wiki/apache/dubbo-go/arch.png)
+
 ## Document
 
-TODO
+https://dubbogo.github.io/dubbo-go-website(**Improving**)
 
 ## Quick Start
 
-[dubbogo-samples](https://github.com/dubbogo/dubbogo-samples) shows how to use dubbo-go. Please read the [dubbogo-samples/README.md](https://github.com/dubbogo/dubbogo-samples/blob/master/README.md) carefully to learn how to dispose the configuration and compile the program.
+[dubbo-samples/golang](https://github.com/dubbogo/dubbo-samples) shows how to use dubbo-go. Please read the [dubbo-samples/golang/README.md](https://github.com/dubbogo/dubbo-samples/blob/master/golang/README.md) carefully to learn how to dispose the configuration and compile the program.
 
 ## Running unit tests
 
diff --git a/README_CN.md b/README_CN.md
index 86eeb8dbe2ae8c8209c7135402aaaf626e06b9cf..99b26c5357ddb0482faf5a95b5935b0d9603c40b 100644
--- a/README_CN.md
+++ b/README_CN.md
@@ -14,6 +14,10 @@ Apache License, Version 2.0
 
 [v1.0.0 - 2019骞�5鏈�29鏃� 鍏煎dubbo v2.6.5 鐗堟湰](https://github.com/apache/dubbo-go/releases/tag/v1.0.0)
 
+[v1.1.0 - 2019骞�9鏈�7鏃� 鎹愮尞缁橝pache涔嬪悗鐨勭涓€娆elease](https://github.com/apache/dubbo-go/releases/tag/v1.1.0)
+
+[v1.2.0 - 2019骞�11鏈�15鏃(https://github.com/apache/dubbo-go/releases/tag/v1.2.0)
+
 ## 宸ョ▼鏋舵瀯 ##
 
 鍩轰簬dubbo鐨別xtension妯″潡鍜屽垎灞傜殑浠g爜璁捐(鍖呮嫭 protocol layer, registry layer, cluster layer, config 绛夌瓑)銆傛垜浠殑鐩爣鏄細浣犲彲浠ュ杩欎簺鍒嗗眰鎺ュ彛杩涜鏂扮殑瀹炵幇锛屽苟閫氳繃璋冪敤 extension 妯″潡鐨勨€� extension.SetXXX 鈥濇柟娉曟潵瑕嗙洊 dubbo-go [鍚� go-for-apache-dubbo ]鐨勯粯璁ゅ疄鐜帮紝浠ュ畬鎴愯嚜宸辩殑鐗规畩闇€姹傝€屾棤闇€淇敼婧愪唬鐮併€傚悓鏃讹紝娆㈣繋浣犱负绀惧尯璐$尞鏈夌敤鐨勬嫇灞曞疄鐜般€�
@@ -89,13 +93,15 @@ Apache License, Version 2.0
 
 浣犲彲浠ラ€氳繃璁块棶 [roadmap](https://github.com/apache/dubbo-go/wiki/Roadmap) 鐭ラ亾鏇村鍏充簬 dubbo-go 鐨勪俊鎭€�
 
+![feature](https://raw.githubusercontent.com/wiki/apache/dubbo-go/arch.png)
+
 ## 鏂囨。
 
-TODO
+https://dubbogo.github.io/dubbo-go-website(**瀹屽杽涓�**)
 
 ## 蹇€熷紑濮� ##
 
-[dubbogo-samples](https://github.com/dubbogo/dubbogo-samples)杩欎釜椤圭洰鐨勪簨渚嬪睍绀轰簡濡備綍浣跨敤 dubbo-go 銆傝浠旂粏闃呰 [dubbogo-samples/README.md](https://github.com/dubbogo/dubbogo-samples/blob/master/README.md) 瀛︿範濡備綍澶勭悊閰嶇疆骞剁紪璇戠▼搴忋€�
+[dubbo-samples/golang](https://github.com/dubbogo/dubbo-samples)杩欎釜椤圭洰鐨勪簨渚嬪睍绀轰簡濡備綍浣跨敤 dubbo-go 銆傝浠旂粏闃呰 [dubbo-samples/golang/README.md](https://github.com/dubbogo/dubbo-samples/blob/master/golang/README.md) 瀛︿範濡備綍澶勭悊閰嶇疆骞剁紪璇戠▼搴忋€�
 
 ## 杩愯鍗曟祴
 
diff --git a/cluster/cluster_impl/base_cluster_invoker.go b/cluster/cluster_impl/base_cluster_invoker.go
index d93e9a6a98a8cbf7ee2cb97abd0248353e0c3154..644f67c5244350897bbc3e291e66e2421758fce5 100644
--- a/cluster/cluster_impl/base_cluster_invoker.go
+++ b/cluster/cluster_impl/base_cluster_invoker.go
@@ -35,6 +35,7 @@ type baseClusterInvoker struct {
 	directory      cluster.Directory
 	availablecheck bool
 	destroyed      *atomic.Bool
+	stickyInvoker  protocol.Invoker
 }
 
 func newBaseClusterInvoker(directory cluster.Directory) baseClusterInvoker {
@@ -56,7 +57,9 @@ func (invoker *baseClusterInvoker) Destroy() {
 }
 
 func (invoker *baseClusterInvoker) IsAvailable() bool {
-	//TODO:sticky connection
+	if invoker.stickyInvoker != nil {
+		return invoker.stickyInvoker.IsAvailable()
+	}
 	return invoker.directory.IsAvailable()
 }
 
@@ -83,15 +86,42 @@ func (invoker *baseClusterInvoker) checkWhetherDestroyed() error {
 }
 
 func (invoker *baseClusterInvoker) doSelect(lb cluster.LoadBalance, invocation protocol.Invocation, invokers []protocol.Invoker, invoked []protocol.Invoker) protocol.Invoker {
-	//todo:sticky connect
+
+	var selectedInvoker protocol.Invoker
+	url := invokers[0].GetUrl()
+	sticky := url.GetParamBool(constant.STICKY_KEY, false)
+	//Get the service method sticky config if have
+	sticky = url.GetMethodParamBool(invocation.MethodName(), constant.STICKY_KEY, sticky)
+
+	if invoker.stickyInvoker != nil && !isInvoked(invoker.stickyInvoker, invokers) {
+		invoker.stickyInvoker = nil
+	}
+
+	if sticky && invoker.stickyInvoker != nil && (invoked == nil || !isInvoked(invoker.stickyInvoker, invoked)) {
+		if invoker.availablecheck && invoker.stickyInvoker.IsAvailable() {
+			return invoker.stickyInvoker
+		}
+	}
+
+	selectedInvoker = invoker.doSelectInvoker(lb, invocation, invokers, invoked)
+
+	if sticky {
+		invoker.stickyInvoker = selectedInvoker
+	}
+	return selectedInvoker
+
+}
+
+func (invoker *baseClusterInvoker) doSelectInvoker(lb cluster.LoadBalance, invocation protocol.Invocation, invokers []protocol.Invoker, invoked []protocol.Invoker) protocol.Invoker {
 	if len(invokers) == 1 {
 		return invokers[0]
 	}
+
 	selectedInvoker := lb.Select(invokers, invocation)
 
 	//judge to if the selectedInvoker is invoked
 
-	if !selectedInvoker.IsAvailable() || !invoker.availablecheck || isInvoked(selectedInvoker, invoked) {
+	if (!selectedInvoker.IsAvailable() && invoker.availablecheck) || isInvoked(selectedInvoker, invoked) {
 		// do reselect
 		var reslectInvokers []protocol.Invoker
 
@@ -106,13 +136,12 @@ func (invoker *baseClusterInvoker) doSelect(lb cluster.LoadBalance, invocation p
 		}
 
 		if len(reslectInvokers) > 0 {
-			return lb.Select(reslectInvokers, invocation)
+			selectedInvoker = lb.Select(reslectInvokers, invocation)
 		} else {
 			return nil
 		}
 	}
 	return selectedInvoker
-
 }
 
 func isInvoked(selectedInvoker protocol.Invoker, invoked []protocol.Invoker) bool {
diff --git a/cluster/cluster_impl/base_cluster_invoker_test.go b/cluster/cluster_impl/base_cluster_invoker_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..d06d3cc23e75cf2227fa22894475f141ffe09a96
--- /dev/null
+++ b/cluster/cluster_impl/base_cluster_invoker_test.go
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cluster_impl
+
+import (
+	"context"
+	"fmt"
+	"testing"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+import (
+	"github.com/apache/dubbo-go/cluster/loadbalance"
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/protocol"
+	"github.com/apache/dubbo-go/protocol/invocation"
+)
+
+func Test_StickyNormal(t *testing.T) {
+	invokers := []protocol.Invoker{}
+	for i := 0; i < 10; i++ {
+		url, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i))
+		url.SetParam("sticky", "true")
+		invokers = append(invokers, NewMockInvoker(url, 1))
+	}
+	base := &baseClusterInvoker{}
+	base.availablecheck = true
+	invoked := []protocol.Invoker{}
+	result := base.doSelect(loadbalance.NewRandomLoadBalance(), invocation.NewRPCInvocation("getUser", nil, nil), invokers, invoked)
+	result1 := base.doSelect(loadbalance.NewRandomLoadBalance(), invocation.NewRPCInvocation("getUser", nil, nil), invokers, invoked)
+	assert.Equal(t, result, result1)
+}
+func Test_StickyNormalWhenError(t *testing.T) {
+	invokers := []protocol.Invoker{}
+	for i := 0; i < 10; i++ {
+		url, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i))
+		url.SetParam("sticky", "true")
+		invokers = append(invokers, NewMockInvoker(url, 1))
+	}
+	base := &baseClusterInvoker{}
+	base.availablecheck = true
+
+	invoked := []protocol.Invoker{}
+	result := base.doSelect(loadbalance.NewRandomLoadBalance(), invocation.NewRPCInvocation("getUser", nil, nil), invokers, invoked)
+	invoked = append(invoked, result)
+	result1 := base.doSelect(loadbalance.NewRandomLoadBalance(), invocation.NewRPCInvocation("getUser", nil, nil), invokers, invoked)
+	assert.NotEqual(t, result, result1)
+}
diff --git a/cluster/cluster_impl/failback_cluster_test.go b/cluster/cluster_impl/failback_cluster_test.go
index c94347a1251a69a10c0a4d50007ef569bd6dd996..1d2266cabebf591b09188fb723f02126a3f1e0ec 100644
--- a/cluster/cluster_impl/failback_cluster_test.go
+++ b/cluster/cluster_impl/failback_cluster_test.go
@@ -67,7 +67,7 @@ func Test_FailbackSuceess(t *testing.T) {
 	invoker := mock.NewMockInvoker(ctrl)
 	clusterInvoker := registerFailback(t, invoker).(*failbackClusterInvoker)
 
-	invoker.EXPECT().GetUrl().Return(failbackUrl).Times(1)
+	invoker.EXPECT().GetUrl().Return(failbackUrl).AnyTimes()
 
 	mockResult := &protocol.RPCResult{Rest: rest{tried: 0, success: true}}
 	invoker.EXPECT().Invoke(gomock.Any()).Return(mockResult)
diff --git a/cluster/cluster_impl/failfast_cluster_test.go b/cluster/cluster_impl/failfast_cluster_test.go
index 7a19e80ccda15aa13a1c4fcf250e05a6effa7f0b..1a4342e6c2b74fd6b1359646eeb463bb6dc17d0a 100644
--- a/cluster/cluster_impl/failfast_cluster_test.go
+++ b/cluster/cluster_impl/failfast_cluster_test.go
@@ -64,7 +64,7 @@ func Test_FailfastInvokeSuccess(t *testing.T) {
 	invoker := mock.NewMockInvoker(ctrl)
 	clusterInvoker := registerFailfast(t, invoker)
 
-	invoker.EXPECT().GetUrl().Return(failfastUrl)
+	invoker.EXPECT().GetUrl().Return(failfastUrl).AnyTimes()
 
 	mockResult := &protocol.RPCResult{Rest: rest{tried: 0, success: true}}
 
@@ -84,7 +84,7 @@ func Test_FailfastInvokeFail(t *testing.T) {
 	invoker := mock.NewMockInvoker(ctrl)
 	clusterInvoker := registerFailfast(t, invoker)
 
-	invoker.EXPECT().GetUrl().Return(failfastUrl)
+	invoker.EXPECT().GetUrl().Return(failfastUrl).AnyTimes()
 
 	mockResult := &protocol.RPCResult{Err: perrors.New("error")}
 
diff --git a/cluster/cluster_impl/failsafe_cluster_test.go b/cluster/cluster_impl/failsafe_cluster_test.go
index 9ee9d9fee31b0cb24d877ab3dc0e24fb552f5f11..7888b97c3a02bd4679f8ec5267637b8d2a7c12e4 100644
--- a/cluster/cluster_impl/failsafe_cluster_test.go
+++ b/cluster/cluster_impl/failsafe_cluster_test.go
@@ -64,7 +64,7 @@ func Test_FailSafeInvokeSuccess(t *testing.T) {
 	invoker := mock.NewMockInvoker(ctrl)
 	clusterInvoker := register_failsafe(t, invoker)
 
-	invoker.EXPECT().GetUrl().Return(failsafeUrl)
+	invoker.EXPECT().GetUrl().Return(failsafeUrl).AnyTimes()
 
 	mockResult := &protocol.RPCResult{Rest: rest{tried: 0, success: true}}
 
@@ -83,7 +83,7 @@ func Test_FailSafeInvokeFail(t *testing.T) {
 	invoker := mock.NewMockInvoker(ctrl)
 	clusterInvoker := register_failsafe(t, invoker)
 
-	invoker.EXPECT().GetUrl().Return(failsafeUrl)
+	invoker.EXPECT().GetUrl().Return(failsafeUrl).AnyTimes()
 
 	mockResult := &protocol.RPCResult{Err: perrors.New("error")}
 
diff --git a/cluster/loadbalance/consistent_hash.go b/cluster/loadbalance/consistent_hash.go
new file mode 100644
index 0000000000000000000000000000000000000000..365e6a66242e4a4618ab922f80b4b4247076484d
--- /dev/null
+++ b/cluster/loadbalance/consistent_hash.go
@@ -0,0 +1,165 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package loadbalance
+
+import (
+	"crypto/md5"
+	"encoding/json"
+	"fmt"
+	"hash/crc32"
+	"regexp"
+	"sort"
+	"strconv"
+	"strings"
+)
+
+import (
+	"github.com/apache/dubbo-go/cluster"
+	"github.com/apache/dubbo-go/common/constant"
+	"github.com/apache/dubbo-go/common/extension"
+	"github.com/apache/dubbo-go/protocol"
+)
+
+const (
+	ConsistentHash = "consistenthash"
+	HashNodes      = "hash.nodes"
+	HashArguments  = "hash.arguments"
+)
+
+var (
+	selectors = make(map[string]*ConsistentHashSelector)
+	re        = regexp.MustCompile(constant.COMMA_SPLIT_PATTERN)
+)
+
+func init() {
+	extension.SetLoadbalance(ConsistentHash, NewConsistentHashLoadBalance)
+}
+
+type ConsistentHashLoadBalance struct {
+}
+
+func NewConsistentHashLoadBalance() cluster.LoadBalance {
+	return &ConsistentHashLoadBalance{}
+}
+
+func (lb *ConsistentHashLoadBalance) Select(invokers []protocol.Invoker, invocation protocol.Invocation) protocol.Invoker {
+	methodName := invocation.MethodName()
+	key := invokers[0].GetUrl().ServiceKey() + "." + methodName
+
+	// hash the invokers
+	bs := make([]byte, 0)
+	for _, invoker := range invokers {
+		b, err := json.Marshal(invoker)
+		if err != nil {
+			return nil
+		}
+		bs = append(bs, b...)
+	}
+	hashCode := crc32.ChecksumIEEE(bs)
+	selector, ok := selectors[key]
+	if !ok || selector.hashCode != hashCode {
+		selectors[key] = newConsistentHashSelector(invokers, methodName, hashCode)
+		selector = selectors[key]
+	}
+	return selector.Select(invocation)
+}
+
+type Uint32Slice []uint32
+
+func (s Uint32Slice) Len() int {
+	return len(s)
+}
+
+func (s Uint32Slice) Less(i, j int) bool {
+	return s[i] < s[j]
+}
+
+func (s Uint32Slice) Swap(i, j int) {
+	s[i], s[j] = s[j], s[i]
+}
+
+type ConsistentHashSelector struct {
+	hashCode        uint32
+	replicaNum      int
+	virtualInvokers map[uint32]protocol.Invoker
+	keys            Uint32Slice
+	argumentIndex   []int
+}
+
+func newConsistentHashSelector(invokers []protocol.Invoker, methodName string,
+	hashCode uint32) *ConsistentHashSelector {
+
+	selector := &ConsistentHashSelector{}
+	selector.virtualInvokers = make(map[uint32]protocol.Invoker)
+	selector.hashCode = hashCode
+	url := invokers[0].GetUrl()
+	selector.replicaNum = int(url.GetMethodParamInt(methodName, HashNodes, 160))
+	indices := re.Split(url.GetMethodParam(methodName, HashArguments, "0"), -1)
+	for _, index := range indices {
+		i, err := strconv.Atoi(index)
+		if err != nil {
+			return nil
+		}
+		selector.argumentIndex = append(selector.argumentIndex, i)
+	}
+	for _, invoker := range invokers {
+		u := invoker.GetUrl()
+		address := u.Ip + ":" + u.Port
+		for i := 0; i < selector.replicaNum/4; i++ {
+			digest := md5.Sum([]byte(address + strconv.Itoa(i)))
+			for j := 0; j < 4; j++ {
+				key := selector.hash(digest, j)
+				selector.keys = append(selector.keys, key)
+				selector.virtualInvokers[key] = invoker
+			}
+		}
+	}
+	sort.Sort(selector.keys)
+	return selector
+}
+
+func (c *ConsistentHashSelector) Select(invocation protocol.Invocation) protocol.Invoker {
+	key := c.toKey(invocation.Arguments())
+	digest := md5.Sum([]byte(key))
+	return c.selectForKey(c.hash(digest, 0))
+}
+
+func (c *ConsistentHashSelector) toKey(args []interface{}) string {
+	var sb strings.Builder
+	for i := range c.argumentIndex {
+		if i >= 0 && i < len(args) {
+			fmt.Fprint(&sb, args[i].(string))
+		}
+	}
+	return sb.String()
+}
+
+func (c *ConsistentHashSelector) selectForKey(hash uint32) protocol.Invoker {
+	idx := sort.Search(len(c.keys), func(i int) bool {
+		return c.keys[i] >= hash
+	})
+	if idx == len(c.keys) {
+		idx = 0
+	}
+	return c.virtualInvokers[c.keys[idx]]
+}
+
+func (c *ConsistentHashSelector) hash(digest [16]byte, i int) uint32 {
+	return uint32((digest[3+i*4]&0xFF)<<24) | uint32((digest[2+i*4]&0xFF)<<16) |
+		uint32((digest[1+i*4]&0xFF)<<8) | uint32(digest[i*4]&0xFF)&0xFFFFFFF
+}
diff --git a/cluster/loadbalance/consistent_hash_test.go b/cluster/loadbalance/consistent_hash_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..174d5715dd1258d329f40251e76ca47d98791ea9
--- /dev/null
+++ b/cluster/loadbalance/consistent_hash_test.go
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package loadbalance
+
+import (
+	"context"
+	"testing"
+)
+
+import (
+	"github.com/stretchr/testify/suite"
+)
+
+import (
+	"github.com/apache/dubbo-go/cluster"
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/protocol"
+	"github.com/apache/dubbo-go/protocol/invocation"
+)
+
+func TestConsistentHashSelectorSuite(t *testing.T) {
+	suite.Run(t, new(consistentHashSelectorSuite))
+}
+
+type consistentHashSelectorSuite struct {
+	suite.Suite
+	selector *ConsistentHashSelector
+}
+
+func (s *consistentHashSelectorSuite) SetupTest() {
+	var invokers []protocol.Invoker
+	url, _ := common.NewURL(context.TODO(),
+		"dubbo://192.168.1.0:20000/org.apache.demo.HelloService?methods.echo.hash.arguments=0,1")
+	invokers = append(invokers, protocol.NewBaseInvoker(url))
+	s.selector = newConsistentHashSelector(invokers, "echo", 999944)
+}
+
+func (s *consistentHashSelectorSuite) TestToKey() {
+	result := s.selector.toKey([]interface{}{"username", "age"})
+	s.Equal(result, "usernameage")
+}
+
+func (s *consistentHashSelectorSuite) TestSelectForKey() {
+	url1, _ := common.NewURL(context.TODO(), "dubbo://192.168.1.0:8080")
+	url2, _ := common.NewURL(context.TODO(), "dubbo://192.168.1.0:8081")
+	s.selector.virtualInvokers = make(map[uint32]protocol.Invoker)
+	s.selector.virtualInvokers[99874] = protocol.NewBaseInvoker(url1)
+	s.selector.virtualInvokers[9999945] = protocol.NewBaseInvoker(url2)
+	s.selector.keys = []uint32{99874, 9999945}
+	result := s.selector.selectForKey(9999944)
+	s.Equal(result.GetUrl().String(), "dubbo://192.168.1.0:8081?")
+}
+
+func TestConsistentHashLoadBalanceSuite(t *testing.T) {
+	suite.Run(t, new(consistentHashLoadBalanceSuite))
+}
+
+type consistentHashLoadBalanceSuite struct {
+	suite.Suite
+	url1     common.URL
+	url2     common.URL
+	url3     common.URL
+	invokers []protocol.Invoker
+	invoker1 protocol.Invoker
+	invoker2 protocol.Invoker
+	invoker3 protocol.Invoker
+	lb       cluster.LoadBalance
+}
+
+func (s *consistentHashLoadBalanceSuite) SetupTest() {
+	var err error
+	s.url1, err = common.NewURL(context.TODO(), "dubbo://192.168.1.0:8080/org.apache.demo.HelloService?methods.echo.hash.arguments=0,1")
+	s.NoError(err)
+	s.url2, err = common.NewURL(context.TODO(), "dubbo://192.168.1.0:8081/org.apache.demo.HelloService?methods.echo.hash.arguments=0,1")
+	s.NoError(err)
+	s.url3, err = common.NewURL(context.TODO(), "dubbo://192.168.1.0:8082/org.apache.demo.HelloService?methods.echo.hash.arguments=0,1")
+	s.NoError(err)
+
+	s.invoker1 = protocol.NewBaseInvoker(s.url1)
+	s.invoker2 = protocol.NewBaseInvoker(s.url2)
+	s.invoker3 = protocol.NewBaseInvoker(s.url3)
+
+	s.invokers = append(s.invokers, s.invoker1, s.invoker2, s.invoker3)
+	s.lb = NewConsistentHashLoadBalance()
+}
+
+func (s *consistentHashLoadBalanceSuite) TestSelect() {
+	args := []interface{}{"name", "password", "age"}
+	invoker := s.lb.Select(s.invokers, invocation.NewRPCInvocation("echo", args, nil))
+	s.Equal(invoker.GetUrl().Location, "192.168.1.0:8080")
+
+	args = []interface{}{"ok", "abc"}
+	invoker = s.lb.Select(s.invokers, invocation.NewRPCInvocation("echo", args, nil))
+	s.Equal(invoker.GetUrl().Location, "192.168.1.0:8082")
+}
diff --git a/common/constant/default.go b/common/constant/default.go
index cb66f5f0ab1cd917278b71103f34a341a1e598d6..6e0f8488783ebe66939436ca14670395e2719be7 100644
--- a/common/constant/default.go
+++ b/common/constant/default.go
@@ -67,3 +67,7 @@ const (
 	APP_DYNAMIC_CONFIGURATORS_CATEGORY = "appdynamicconfigurators"
 	PROVIDER_CATEGORY                  = "providers"
 )
+
+const (
+	COMMA_SPLIT_PATTERN = "\\s*[,]+\\s*"
+)
diff --git a/common/constant/key.go b/common/constant/key.go
index ff17ee1ad8e76b256b4dbee7f09f8720c92e3b6d..7117a7021a8d0f4ed930c0c4dcd890cea6ace5cd 100644
--- a/common/constant/key.go
+++ b/common/constant/key.go
@@ -55,6 +55,7 @@ const (
 	WEIGHT_KEY                             = "weight"
 	WARMUP_KEY                             = "warmup"
 	RETRIES_KEY                            = "retries"
+	STICKY_KEY                             = "sticky"
 	BEAN_NAME                              = "bean.name"
 	FAIL_BACK_TASKS_KEY                    = "failbacktasks"
 	FORKS_KEY                              = "forks"
diff --git a/common/proxy/proxy.go b/common/proxy/proxy.go
index 1c079f6bca52bf8f6e8c5ebb168da82ab8ccb5f2..d13646dba86eea04adb3726d33ee9d20457276b6 100644
--- a/common/proxy/proxy.go
+++ b/common/proxy/proxy.go
@@ -181,3 +181,7 @@ func (p *Proxy) Implement(v common.RPCService) {
 func (p *Proxy) Get() common.RPCService {
 	return p.rpc
 }
+
+func (p *Proxy) GetCallback() interface{} {
+	return p.callBack
+}
diff --git a/common/proxy/proxy_factory.go b/common/proxy/proxy_factory.go
index 2567e0ee09cf7fa5aef7fde46872eb88205d8e45..116cfe06693b6923ca10e0df6964317dabd91d0e 100644
--- a/common/proxy/proxy_factory.go
+++ b/common/proxy/proxy_factory.go
@@ -24,6 +24,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
 }
 
diff --git a/common/proxy/proxy_factory/default.go b/common/proxy/proxy_factory/default.go
index bafba60b400ec59d99e2d68ecf4d067c906ba6fb..06824fdc1e27cde5e1905be3277451dd4395049c 100644
--- a/common/proxy/proxy_factory/default.go
+++ b/common/proxy/proxy_factory/default.go
@@ -55,11 +55,16 @@ func NewDefaultProxyFactory(options ...proxy.Option) proxy.ProxyFactory {
 	return &DefaultProxyFactory{}
 }
 func (factory *DefaultProxyFactory) GetProxy(invoker protocol.Invoker, url *common.URL) *proxy.Proxy {
+	return factory.GetAsyncProxy(invoker, nil, url)
+}
+
+func (factory *DefaultProxyFactory) GetAsyncProxy(invoker protocol.Invoker, callBack interface{}, url *common.URL) *proxy.Proxy {
 	//create proxy
 	attachments := map[string]string{}
 	attachments[constant.ASYNC_KEY] = url.GetParam(constant.ASYNC_KEY, "false")
-	return proxy.NewProxy(invoker, nil, attachments)
+	return proxy.NewProxy(invoker, callBack, attachments)
 }
+
 func (factory *DefaultProxyFactory) GetInvoker(url common.URL) protocol.Invoker {
 	return &ProxyInvoker{
 		BaseInvoker: *protocol.NewBaseInvoker(url),
diff --git a/common/proxy/proxy_factory/default_test.go b/common/proxy/proxy_factory/default_test.go
index b6a6b675baf992b2d64ffd19291ee2dc009bd1e3..7159b4b00eb2fcddb0f20f701f56b3179e57c4a0 100644
--- a/common/proxy/proxy_factory/default_test.go
+++ b/common/proxy/proxy_factory/default_test.go
@@ -18,6 +18,7 @@
 package proxy_factory
 
 import (
+	"fmt"
 	"testing"
 )
 
@@ -37,6 +38,21 @@ func Test_GetProxy(t *testing.T) {
 	assert.NotNil(t, proxy)
 }
 
+type TestAsync struct {
+}
+
+func (u *TestAsync) CallBack(res common.CallbackResponse) {
+	fmt.Println("CallBack res:", res)
+}
+
+func Test_GetAsyncProxy(t *testing.T) {
+	proxyFactory := NewDefaultProxyFactory()
+	url := common.NewURLWithOptions()
+	async := &TestAsync{}
+	proxy := proxyFactory.GetAsyncProxy(protocol.NewBaseInvoker(*url), async.CallBack, url)
+	assert.NotNil(t, proxy)
+}
+
 func Test_GetInvoker(t *testing.T) {
 	proxyFactory := NewDefaultProxyFactory()
 	url := common.NewURLWithOptions()
diff --git a/common/rpc_service.go b/common/rpc_service.go
index 4741a6fa3c0daef97f044f639a5e64a38fe4a187..4c9f083dd0850c3f110491ef820c7b677c8009aa 100644
--- a/common/rpc_service.go
+++ b/common/rpc_service.go
@@ -39,6 +39,18 @@ type RPCService interface {
 	Reference() string // rpc service id or reference id
 }
 
+//AsyncCallbackService callback interface for async
+type AsyncCallbackService interface {
+	CallBack(response CallbackResponse) // callback
+}
+
+//CallbackResponse for different protocol
+type CallbackResponse interface {
+}
+
+//AsyncCallback async callback method
+type AsyncCallback func(response CallbackResponse)
+
 // for lowercase func
 // func MethodMapper() map[string][string] {
 //     return map[string][string]{}
diff --git a/common/url.go b/common/url.go
index 03d6b9cb14cb684458ca568d83b7d607d84097fd..577aaa4c5d0fe4cb0df1db4b2f4fcb79912c33c4 100644
--- a/common/url.go
+++ b/common/url.go
@@ -447,6 +447,11 @@ func (c URL) GetMethodParam(method string, key string, d string) string {
 	return r
 }
 
+func (c URL) GetMethodParamBool(method string, key string, d bool) bool {
+	r := c.GetParamBool("methods."+method+"."+key, d)
+	return r
+}
+
 func (c *URL) RemoveParams(set *gxset.HashSet) {
 	c.paramsLock.Lock()
 	defer c.paramsLock.Unlock()
diff --git a/common/url_test.go b/common/url_test.go
index ad56aa2d525438c308ee922cd7a5d41122192155..c70c58bc215b6449311d43f9f9cffeb89623f80c 100644
--- a/common/url_test.go
+++ b/common/url_test.go
@@ -217,6 +217,18 @@ func TestURL_GetMethodParam(t *testing.T) {
 	assert.Equal(t, "1s", v)
 }
 
+func TestURL_GetMethodParamBool(t *testing.T) {
+	params := url.Values{}
+	params.Set("methods.GetValue.async", "true")
+	u := URL{baseUrl: baseUrl{params: params}}
+	v := u.GetMethodParamBool("GetValue", "async", false)
+	assert.Equal(t, true, v)
+
+	u = URL{}
+	v = u.GetMethodParamBool("GetValue2", "async", false)
+	assert.Equal(t, false, v)
+}
+
 func TestMergeUrl(t *testing.T) {
 	referenceUrlParams := url.Values{}
 	referenceUrlParams.Set(constant.CLUSTER_KEY, "random")
diff --git a/config/method_config.go b/config/method_config.go
index 32b14a610e5c31f1107eb734ff79c186f3c36218..e10548e667e6a16d33690f011ebc9958af1eea71 100644
--- a/config/method_config.go
+++ b/config/method_config.go
@@ -36,6 +36,7 @@ type MethodConfig struct {
 	TpsLimitStrategy            string `yaml:"tps.limit.strategy" json:"tps.limit.strategy,omitempty" property:"tps.limit.strategy"`
 	ExecuteLimit                string `yaml:"execute.limit" json:"execute.limit,omitempty" property:"execute.limit"`
 	ExecuteLimitRejectedHandler string `yaml:"execute.limit.rejected.handler" json:"execute.limit.rejected.handler,omitempty" property:"execute.limit.rejected.handler"`
+	Sticky                      bool   `yaml:"sticky"   json:"sticky,omitempty" property:"sticky"`
 	RequestTimeout              string `yaml:"timeout"  json:"timeout,omitempty" property:"timeout"`
 }
 
diff --git a/config/reference_config.go b/config/reference_config.go
index a31ae22da8f0b8483d8c73c89b20217625932a51..7b1358db4394a27318b313d2262ade1da704e22f 100644
--- a/config/reference_config.go
+++ b/config/reference_config.go
@@ -60,6 +60,7 @@ type ReferenceConfig struct {
 	invoker        protocol.Invoker
 	urls           []*common.URL
 	Generic        bool   `yaml:"generic"  json:"generic,omitempty" property:"generic"`
+	Sticky        bool `yaml:"sticky"   json:"sticky,omitempty" property:"sticky"`
 	RequestTimeout string `yaml:"timeout"  json:"timeout,omitempty" property:"timeout"`
 }
 
@@ -169,10 +170,6 @@ func (refconfig *ReferenceConfig) getUrlMap() url.Values {
 	urlMap.Set(constant.VERSION_KEY, refconfig.Version)
 	urlMap.Set(constant.GENERIC_KEY, strconv.FormatBool(refconfig.Generic))
 	urlMap.Set(constant.ROLE_KEY, strconv.Itoa(common.CONSUMER))
-	if len(refconfig.RequestTimeout) != 0 {
-		urlMap.Set(constant.TIMEOUT_KEY, refconfig.RequestTimeout)
-	}
-
 	//getty invoke async or sync
 	urlMap.Set(constant.ASYNC_KEY, strconv.FormatBool(refconfig.async))
 
@@ -195,7 +192,8 @@ func (refconfig *ReferenceConfig) getUrlMap() url.Values {
 	for _, v := range refconfig.Methods {
 		urlMap.Set("methods."+v.Name+"."+constant.LOADBALANCE_KEY, v.Loadbalance)
 		urlMap.Set("methods."+v.Name+"."+constant.RETRIES_KEY, v.Retries)
-		if v.RequestTimeout != "" {
+		urlMap.Set("methods."+v.Name+"."+constant.STICKY_KEY, strconv.FormatBool(v.Sticky))
+		if len(v.RequestTimeout) != 0 {
 			urlMap.Set("methods."+v.Name+"."+constant.TIMEOUT_KEY, v.RequestTimeout)
 		}
 	}
diff --git a/config/reference_config_test.go b/config/reference_config_test.go
index a81dbf06cef7d275cf6af4a7f651ff8d1600a3c9..e689c471ed12b58a40d4416efaa16abfe107e09b 100644
--- a/config/reference_config_test.go
+++ b/config/reference_config_test.go
@@ -81,10 +81,12 @@ func doInitConsumer() {
 		},
 		References: map[string]*ReferenceConfig{
 			"MockService": {
+				id: "MockProvider",
 				Params: map[string]string{
 					"serviceid": "soa.mock",
 					"forks":     "5",
 				},
+				Sticky:        false,
 				Registry:      "shanghai_reg1,shanghai_reg2,hangzhou_reg1,hangzhou_reg2",
 				InterfaceName: "com.MockService",
 				Protocol:      "mock",
@@ -103,6 +105,7 @@ func doInitConsumer() {
 						Name:        "GetUser1",
 						Retries:     "2",
 						Loadbalance: "random",
+						Sticky:      true,
 					},
 				},
 			},
@@ -110,6 +113,26 @@ func doInitConsumer() {
 	}
 }
 
+var mockProvider = new(MockProvider)
+
+type MockProvider struct {
+}
+
+func (m *MockProvider) Reference() string {
+	return "MockProvider"
+}
+
+func (m *MockProvider) CallBack(res common.CallbackResponse) {
+}
+
+func doInitConsumerAsync() {
+	doInitConsumer()
+	SetConsumerService(mockProvider)
+	for _, v := range consumerConfig.References {
+		v.Async = true
+	}
+}
+
 func doInitConsumerWithSingleRegistry() {
 	consumerConfig = &ConsumerConfig{
 		ApplicationConfig: &ApplicationConfig{
@@ -181,6 +204,22 @@ func Test_Refer(t *testing.T) {
 	}
 	consumerConfig = nil
 }
+
+func Test_ReferAsync(t *testing.T) {
+	doInitConsumerAsync()
+	extension.SetProtocol("registry", GetProtocol)
+	extension.SetCluster("registryAware", cluster_impl.NewRegistryAwareCluster)
+
+	for _, reference := range consumerConfig.References {
+		reference.Refer()
+		assert.Equal(t, "soa.mock", reference.Params["serviceid"])
+		assert.NotNil(t, reference.invoker)
+		assert.NotNil(t, reference.pxy)
+		assert.NotNil(t, reference.pxy.GetCallback())
+	}
+	consumerConfig = nil
+}
+
 func Test_ReferP2P(t *testing.T) {
 	doInitConsumer()
 	extension.SetProtocol("dubbo", GetProtocol)
@@ -254,6 +293,24 @@ func Test_Forking(t *testing.T) {
 	consumerConfig = nil
 }
 
+func Test_Sticky(t *testing.T) {
+	doInitConsumer()
+	extension.SetProtocol("dubbo", GetProtocol)
+	extension.SetProtocol("registry", GetProtocol)
+	m := consumerConfig.References["MockService"]
+	m.Url = "dubbo://127.0.0.1:20000;registry://127.0.0.2:20000"
+
+	reference := consumerConfig.References["MockService"]
+	reference.Refer()
+	referenceSticky := reference.invoker.GetUrl().GetParam(constant.STICKY_KEY, "false")
+	assert.Equal(t, "false", referenceSticky)
+
+	method0StickKey := reference.invoker.GetUrl().GetMethodParam(reference.Methods[0].Name, constant.STICKY_KEY, "false")
+	assert.Equal(t, "false", method0StickKey)
+	method1StickKey := reference.invoker.GetUrl().GetMethodParam(reference.Methods[1].Name, constant.STICKY_KEY, "false")
+	assert.Equal(t, "true", method1StickKey)
+}
+
 func GetProtocol() protocol.Protocol {
 	if regProtocol != nil {
 		return regProtocol
diff --git a/config/service.go b/config/service.go
index 2bceac4a8c20bb598dc2607c90c8206e4a448808..f1b51790ca13df0534882837397181e45e56ffa3 100644
--- a/config/service.go
+++ b/config/service.go
@@ -43,3 +43,11 @@ func GetConsumerService(name string) common.RPCService {
 func GetProviderService(name string) common.RPCService {
 	return proServices[name]
 }
+
+func GetCallback(name string) func(response common.CallbackResponse) {
+	service := GetConsumerService(name)
+	if sv, ok := service.(common.AsyncCallbackService); ok {
+		return sv.CallBack
+	}
+	return nil
+}
diff --git a/filter/impl/generic_filter.go b/filter/impl/generic_filter.go
index 35aadb11a444bda56109e238b17267f71ec2606b..067939a34b889198a25b08af23892ad4037e642e 100644
--- a/filter/impl/generic_filter.go
+++ b/filter/impl/generic_filter.go
@@ -47,22 +47,21 @@ type GenericFilter struct{}
 func (ef *GenericFilter) Invoke(invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
 	if invocation.MethodName() == constant.GENERIC && len(invocation.Arguments()) == 3 {
 		oldArguments := invocation.Arguments()
-		var newParams []hessian.Object
+
 		if oldParams, ok := oldArguments[2].([]interface{}); ok {
+			newParams := make([]hessian.Object, 0, len(oldParams))
 			for i := range oldParams {
 				newParams = append(newParams, hessian.Object(struct2MapAll(oldParams[i])))
 			}
-		} else {
-			return invoker.Invoke(invocation)
-		}
-		newArguments := []interface{}{
-			oldArguments[0],
-			oldArguments[1],
-			newParams,
+			newArguments := []interface{}{
+				oldArguments[0],
+				oldArguments[1],
+				newParams,
+			}
+			newInvocation := invocation2.NewRPCInvocation(invocation.MethodName(), newArguments, invocation.Attachments())
+			newInvocation.SetReply(invocation.Reply())
+			return invoker.Invoke(newInvocation)
 		}
-		newInvocation := invocation2.NewRPCInvocation(invocation.MethodName(), newArguments, invocation.Attachments())
-		newInvocation.SetReply(invocation.Reply())
-		return invoker.Invoke(newInvocation)
 	}
 	return invoker.Invoke(invocation)
 }
diff --git a/filter/impl/tps/impl/tps_limiter_method_service_test.go b/filter/impl/tps/impl/tps_limiter_method_service_test.go
index a21d3428398355e92b304c12d27637f00a3730f7..e747d4682d0a8bdee03da6f012fb76b7bd1e02af 100644
--- a/filter/impl/tps/impl/tps_limiter_method_service_test.go
+++ b/filter/impl/tps/impl/tps_limiter_method_service_test.go
@@ -50,7 +50,7 @@ func TestMethodServiceTpsLimiterImpl_IsAllowable_Only_Service_Level(t *testing.T
 	mockStrategyImpl.EXPECT().IsAllowable().Return(true).Times(1)
 
 	extension.SetTpsLimitStrategy(constant.DEFAULT_KEY, &mockStrategyCreator{
-		rate:     40,
+		rate:     20,
 		interval: 60000,
 		t:        t,
 		strategy: mockStrategyImpl,
diff --git a/go.mod b/go.mod
index 6a9128af0cbf1218adeecd7b273a9bbc2ba0c632..c2a61f2db1484338bba7dd1bf00a9ff9de2125df 100644
--- a/go.mod
+++ b/go.mod
@@ -52,3 +52,5 @@ require (
 	google.golang.org/grpc v1.22.1
 	gopkg.in/yaml.v2 v2.2.2
 )
+
+go 1.13
diff --git a/protocol/dubbo/client.go b/protocol/dubbo/client.go
index ca04c2db8c51f1f1c9b8c4a01ee868522e473af0..3e2a243103b888d8b94c2e50fe00daabb3d5a032 100644
--- a/protocol/dubbo/client.go
+++ b/protocol/dubbo/client.go
@@ -113,7 +113,9 @@ type Options struct {
 	RequestTimeout time.Duration
 }
 
-type CallResponse struct {
+//AsyncCallbackResponse async response for dubbo
+type AsyncCallbackResponse struct {
+	common.CallbackResponse
 	Opts      Options
 	Cause     error
 	Start     time.Time // invoke(call) start time == write start time
@@ -121,8 +123,6 @@ type CallResponse struct {
 	Reply     interface{}
 }
 
-type AsyncCallback func(response CallResponse)
-
 type Client struct {
 	opts     Options
 	conf     ClientConfig
@@ -199,12 +199,12 @@ func (c *Client) Call(request *Request, response *Response) error {
 	return perrors.WithStack(c.call(ct, request, response, nil))
 }
 
-func (c *Client) AsyncCall(request *Request, callback AsyncCallback, response *Response) error {
+func (c *Client) AsyncCall(request *Request, callback common.AsyncCallback, response *Response) error {
 
 	return perrors.WithStack(c.call(CT_TwoWay, request, response, callback))
 }
 
-func (c *Client) call(ct CallType, request *Request, response *Response, callback AsyncCallback) error {
+func (c *Client) call(ct CallType, request *Request, response *Response, callback common.AsyncCallback) error {
 
 	p := &DubboPackage{}
 	p.Service.Path = strings.TrimPrefix(request.svcUrl.Path, "/")
diff --git a/protocol/dubbo/client_test.go b/protocol/dubbo/client_test.go
index eb1f15c862a910120e118c06bf9b572e93f58832..3f8a8ee98c3b2d8b87e2d5469a18d1792578d1d6 100644
--- a/protocol/dubbo/client_test.go
+++ b/protocol/dubbo/client_test.go
@@ -144,8 +144,9 @@ func TestClient_AsyncCall(t *testing.T) {
 	user := &User{}
 	lock := sync.Mutex{}
 	lock.Lock()
-	err := c.AsyncCall(NewRequest("127.0.0.1:20000", url, "GetUser", []interface{}{"1", "username"}, nil), func(response CallResponse) {
-		assert.Equal(t, User{Id: "1", Name: "username"}, *response.Reply.(*Response).reply.(*User))
+	err := c.AsyncCall(NewRequest("127.0.0.1:20000", url, "GetUser", []interface{}{"1", "username"}, nil), func(response common.CallbackResponse) {
+		r := response.(AsyncCallbackResponse)
+		assert.Equal(t, User{Id: "1", Name: "username"}, *r.Reply.(*Response).reply.(*User))
 		lock.Unlock()
 	}, NewResponse(user, nil))
 	assert.NoError(t, err)
diff --git a/protocol/dubbo/codec.go b/protocol/dubbo/codec.go
index a878ffd91e29d6949870ec25fed9481f301b435a..6b41d5e7d76d31ea23f08b77c841d0f87986bef7 100644
--- a/protocol/dubbo/codec.go
+++ b/protocol/dubbo/codec.go
@@ -26,6 +26,7 @@ import (
 
 import (
 	"github.com/apache/dubbo-go-hessian2"
+	"github.com/apache/dubbo-go/common"
 	perrors "github.com/pkg/errors"
 )
 
@@ -88,11 +89,17 @@ func (p *DubboPackage) Unmarshal(buf *bytes.Buffer, opts ...interface{}) error {
 			return perrors.Errorf("opts[0] is not of type *Client")
 		}
 
-		pendingRsp, ok := client.pendingResponses.Load(SequenceType(p.Header.ID))
-		if !ok {
-			return perrors.Errorf("client.GetPendingResponse(%v) = nil", p.Header.ID)
+		if p.Header.Type&hessian.PackageRequest != 0x00 {
+			// size of this array must be '7'
+			// https://github.com/apache/dubbo-go-hessian2/blob/master/request.go#L272
+			p.Body = make([]interface{}, 7)
+		} else {
+			pendingRsp, ok := client.pendingResponses.Load(SequenceType(p.Header.ID))
+			if !ok {
+				return perrors.Errorf("client.GetPendingResponse(%v) = nil", p.Header.ID)
+			}
+			p.Body = &hessian.Response{RspObj: pendingRsp.(*PendingResponse).response.reply}
 		}
-		p.Body = &hessian.Response{RspObj: pendingRsp.(*PendingResponse).response.reply}
 	}
 
 	// read body
@@ -109,7 +116,7 @@ type PendingResponse struct {
 	err       error
 	start     time.Time
 	readStart time.Time
-	callback  AsyncCallback
+	callback  common.AsyncCallback
 	response  *Response
 	done      chan struct{}
 }
@@ -122,8 +129,8 @@ func NewPendingResponse() *PendingResponse {
 	}
 }
 
-func (r PendingResponse) GetCallResponse() CallResponse {
-	return CallResponse{
+func (r PendingResponse) GetCallResponse() common.CallbackResponse {
+	return AsyncCallbackResponse{
 		Cause:     r.err,
 		Start:     r.start,
 		ReadStart: r.readStart,
diff --git a/protocol/dubbo/dubbo_invoker.go b/protocol/dubbo/dubbo_invoker.go
index 4582e54c2158a43509b26138a8d414d2f34e052a..6dcf2568fa8c88a864c567486a501c2ad7feb3f7 100644
--- a/protocol/dubbo/dubbo_invoker.go
+++ b/protocol/dubbo/dubbo_invoker.go
@@ -75,7 +75,7 @@ func (di *DubboInvoker) Invoke(invocation protocol.Invocation) protocol.Result {
 	}
 	response := NewResponse(inv.Reply(), nil)
 	if async {
-		if callBack, ok := inv.CallBack().(func(response CallResponse)); ok {
+		if callBack, ok := inv.CallBack().(func(response common.CallbackResponse)); ok {
 			result.Err = di.client.AsyncCall(NewRequest(url.Location, url, inv.MethodName(), inv.Arguments(), inv.Attachments()), callBack, response)
 		} else {
 			result.Err = di.client.CallOneway(NewRequest(url.Location, url, inv.MethodName(), inv.Arguments(), inv.Attachments()))
diff --git a/protocol/dubbo/dubbo_invoker_test.go b/protocol/dubbo/dubbo_invoker_test.go
index 0a765356f7353829c8486fddba986e3a444441a1..7d60090e2d81bcb750d1e6d79a08059687c7937d 100644
--- a/protocol/dubbo/dubbo_invoker_test.go
+++ b/protocol/dubbo/dubbo_invoker_test.go
@@ -28,6 +28,7 @@ import (
 )
 
 import (
+	"github.com/apache/dubbo-go/common"
 	"github.com/apache/dubbo-go/common/constant"
 	"github.com/apache/dubbo-go/protocol/invocation"
 )
@@ -65,8 +66,9 @@ func TestDubboInvoker_Invoke(t *testing.T) {
 	// AsyncCall
 	lock := sync.Mutex{}
 	lock.Lock()
-	inv.SetCallBack(func(response CallResponse) {
-		assert.Equal(t, User{Id: "1", Name: "username"}, *response.Reply.(*Response).reply.(*User))
+	inv.SetCallBack(func(response common.CallbackResponse) {
+		r := response.(AsyncCallbackResponse)
+		assert.Equal(t, User{Id: "1", Name: "username"}, *r.Reply.(*Response).reply.(*User))
 		lock.Unlock()
 	})
 	res = invoker.Invoke(inv)
diff --git a/protocol/dubbo/listener.go b/protocol/dubbo/listener.go
index df9ab28e0e4b896b11b2345a83cae14401a70759..2e4b3999dfc08262a2cfb80f29c9a9e7bc2decf8 100644
--- a/protocol/dubbo/listener.go
+++ b/protocol/dubbo/listener.go
@@ -85,11 +85,17 @@ func (h *RpcClientHandler) OnMessage(session getty.Session, pkg interface{}) {
 	}
 
 	if p.Header.Type&hessian.PackageHeartbeat != 0x00 {
-		logger.Debugf("get rpc heartbeat response{header: %#v, body: %#v}", p.Header, p.Body)
-		if p.Err != nil {
-			logger.Errorf("rpc heartbeat response{error: %#v}", p.Err)
+		if p.Header.Type&hessian.PackageResponse != 0x00 {
+			logger.Debugf("get rpc heartbeat response{header: %#v, body: %#v}", p.Header, p.Body)
+			if p.Err != nil {
+				logger.Errorf("rpc heartbeat response{error: %#v}", p.Err)
+			}
+			h.conn.pool.rpcClient.removePendingResponse(SequenceType(p.Header.ID))
+		} else {
+			logger.Debugf("get rpc heartbeat request{header: %#v, service: %#v, body: %#v}", p.Header, p.Service, p.Body)
+			p.Header.ResponseStatus = hessian.Response_OK
+			reply(session, p, hessian.PackageHeartbeat)
 		}
-		h.conn.pool.rpcClient.removePendingResponse(SequenceType(p.Header.ID))
 		return
 	}
 	logger.Debugf("get rpc response{header: %#v, body: %#v}", p.Header, p.Body)
@@ -199,7 +205,7 @@ func (h *RpcServerHandler) OnMessage(session getty.Session, pkg interface{}) {
 	// heartbeat
 	if p.Header.Type&hessian.PackageHeartbeat != 0x00 {
 		logger.Debugf("get rpc heartbeat request{header: %#v, service: %#v, body: %#v}", p.Header, p.Service, p.Body)
-		h.reply(session, p, hessian.PackageHeartbeat)
+		reply(session, p, hessian.PackageHeartbeat)
 		return
 	}
 
@@ -226,7 +232,7 @@ func (h *RpcServerHandler) OnMessage(session getty.Session, pkg interface{}) {
 			if !twoway {
 				return
 			}
-			h.reply(session, p, hessian.PackageResponse)
+			reply(session, p, hessian.PackageResponse)
 		}
 
 	}()
@@ -241,7 +247,7 @@ func (h *RpcServerHandler) OnMessage(session getty.Session, pkg interface{}) {
 		logger.Errorf(err.Error())
 		p.Header.ResponseStatus = hessian.Response_OK
 		p.Body = err
-		h.reply(session, p, hessian.PackageResponse)
+		reply(session, p, hessian.PackageResponse)
 		return
 	}
 	invoker := exporter.(protocol.Exporter).GetInvoker()
@@ -266,7 +272,7 @@ func (h *RpcServerHandler) OnMessage(session getty.Session, pkg interface{}) {
 	if !twoway {
 		return
 	}
-	h.reply(session, p, hessian.PackageResponse)
+	reply(session, p, hessian.PackageResponse)
 }
 
 func (h *RpcServerHandler) OnCron(session getty.Session) {
@@ -294,7 +300,7 @@ func (h *RpcServerHandler) OnCron(session getty.Session) {
 	}
 }
 
-func (h *RpcServerHandler) reply(session getty.Session, req *DubboPackage, tp hessian.PackageType) {
+func reply(session getty.Session, req *DubboPackage, tp hessian.PackageType) {
 	resp := &DubboPackage{
 		Header: hessian.DubboHeader{
 			SerialID:       req.Header.SerialID,
diff --git a/protocol/dubbo/pool.go b/protocol/dubbo/pool.go
index d619a2f8fe78524b3d704cb9de280ebbf534eb12..b5bf040c67c2e0071222466e59db4de67d9e1ca2 100644
--- a/protocol/dubbo/pool.go
+++ b/protocol/dubbo/pool.go
@@ -289,7 +289,17 @@ func (p *gettyRPCClientPool) close() {
 }
 
 func (p *gettyRPCClientPool) getGettyRpcClient(protocol, addr string) (*gettyRPCClient, error) {
-
+	var (
+		conn *gettyRPCClient
+		err  error
+	)
+	if conn, err = p.selectGettyRpcClient(protocol, addr); err == nil && conn == nil {
+		// create new conn
+		return newGettyRPCClientConn(p, protocol, addr)
+	}
+	return conn, err
+}
+func (p *gettyRPCClientPool) selectGettyRpcClient(protocol, addr string) (*gettyRPCClient, error) {
 	p.Lock()
 	defer p.Unlock()
 	if p.conns == nil {
@@ -308,13 +318,10 @@ func (p *gettyRPCClientPool) getGettyRpcClient(protocol, addr string) (*gettyRPC
 			continue
 		}
 		conn.updateActive(now) //update active time
-
 		return conn, nil
 	}
-	// create new conn
-	return newGettyRPCClientConn(p, protocol, addr)
+	return nil, nil
 }
-
 func (p *gettyRPCClientPool) release(conn *gettyRPCClient, err error) {
 	if conn == nil || conn.getActive() == 0 {
 		return
diff --git a/protocol/dubbo/readwriter.go b/protocol/dubbo/readwriter.go
index 930382cca8bac6955b516a88e93ce26d73e235fe..e9dff1cfc77fb34ba75e604334d9c7ab5cfa36d7 100644
--- a/protocol/dubbo/readwriter.go
+++ b/protocol/dubbo/readwriter.go
@@ -62,8 +62,10 @@ func (p *RpcClientPackageHandler) Read(ss getty.Session, data []byte) (interface
 		return nil, 0, perrors.WithStack(err)
 	}
 
-	pkg.Err = pkg.Body.(*hessian.Response).Exception
-	pkg.Body = NewResponse(pkg.Body.(*hessian.Response).RspObj, pkg.Body.(*hessian.Response).Attachments)
+	if pkg.Header.Type&hessian.PackageRequest == 0x00 {
+		pkg.Err = pkg.Body.(*hessian.Response).Exception
+		pkg.Body = NewResponse(pkg.Body.(*hessian.Response).RspObj, pkg.Body.(*hessian.Response).Attachments)
+	}
 
 	return pkg, hessian.HEADER_LENGTH + pkg.Header.BodyLen, nil
 }
diff --git a/protocol/invocation/rpcinvocation.go b/protocol/invocation/rpcinvocation.go
index 2124a22f1611b24d7f4370de64b117c58c4f7e7b..bddd83b5db60cc3ccaa1ab0c43aaeec28e77855d 100644
--- a/protocol/invocation/rpcinvocation.go
+++ b/protocol/invocation/rpcinvocation.go
@@ -19,6 +19,7 @@ package invocation
 
 import (
 	"reflect"
+	"sync"
 )
 
 import (
@@ -37,6 +38,7 @@ type RPCInvocation struct {
 	callBack       interface{}
 	attachments    map[string]string
 	invoker        protocol.Invoker
+	lock           sync.RWMutex
 }
 
 func NewRPCInvocation(methodName string, arguments []interface{}, attachments map[string]string) *RPCInvocation {
@@ -80,6 +82,8 @@ func (r *RPCInvocation) Attachments() map[string]string {
 }
 
 func (r *RPCInvocation) AttachmentsByKey(key string, defaultValue string) string {
+	r.lock.RLock()
+	defer r.lock.RUnlock()
 	if r.attachments == nil {
 		return defaultValue
 	}
@@ -91,6 +95,8 @@ func (r *RPCInvocation) AttachmentsByKey(key string, defaultValue string) string
 }
 
 func (r *RPCInvocation) SetAttachments(key string, value string) {
+	r.lock.Lock()
+	defer r.lock.Unlock()
 	if r.attachments == nil {
 		r.attachments = make(map[string]string)
 	}
diff --git a/registry/protocol/protocol.go b/registry/protocol/protocol.go
index ffdb2753d6bfa0712b8fb9c962c8433a5c281083..534a4b945965f332e49ff343557fa20355921454 100644
--- a/registry/protocol/protocol.go
+++ b/registry/protocol/protocol.go
@@ -338,10 +338,10 @@ func setProviderUrl(regURL *common.URL, providerURL *common.URL) {
 }
 
 func GetProtocol() protocol.Protocol {
-	if regProtocol == nil {
-		regProtocol = newRegistryProtocol()
+	if regProtocol != nil {
+		return regProtocol
 	}
-	return regProtocol
+	return newRegistryProtocol()
 }
 
 type wrappedInvoker struct {
diff --git a/registry/protocol/protocol_test.go b/registry/protocol/protocol_test.go
index 761d14006680a3e0f3a111458d32155b19c26968..0c19da59df6e4fd2f663f9e8d541165fe26c3ffa 100644
--- a/registry/protocol/protocol_test.go
+++ b/registry/protocol/protocol_test.go
@@ -291,8 +291,3 @@ func TestExportWithApplicationConfig(t *testing.T) {
 	v2, _ := regProtocol.bounds.Load(getCacheKey(newUrl))
 	assert.NotNil(t, v2)
 }
-
-func TestGetProtocol(t *testing.T) {
-	singleton := GetProtocol()
-	assert.True(t, singleton == GetProtocol())
-}
diff --git a/registry/zookeeper/listener.go b/registry/zookeeper/listener.go
index a464da88eb23334701f8b76ad7bb5aa3d3a0974f..53a592609153003d7d6c24881bccde0dfe6cdde6 100644
--- a/registry/zookeeper/listener.go
+++ b/registry/zookeeper/listener.go
@@ -20,6 +20,7 @@ package zookeeper
 import (
 	"context"
 	"strings"
+	"sync"
 )
 
 import (
@@ -71,10 +72,11 @@ func (l *RegistryDataListener) DataChange(eventType remoting.Event) bool {
 }
 
 type RegistryConfigurationListener struct {
-	client   *zk.ZookeeperClient
-	registry *zkRegistry
-	events   chan *config_center.ConfigChangeEvent
-	isClosed bool
+	client    *zk.ZookeeperClient
+	registry  *zkRegistry
+	events    chan *config_center.ConfigChangeEvent
+	isClosed  bool
+	closeOnce sync.Once
 }
 
 func NewRegistryConfigurationListener(client *zk.ZookeeperClient, reg *zkRegistry) *RegistryConfigurationListener {
@@ -110,14 +112,11 @@ func (l *RegistryConfigurationListener) Next() (*registry.ServiceEvent, error) {
 	}
 }
 func (l *RegistryConfigurationListener) Close() {
-	if l.registry.IsAvailable() {
-		/**
-		 * if the registry is not available, it means that the registry has been destroy
-		 * so we don't need to call Done(), or it will cause the negative count panic for registry.wg
-		 */
+	// ensure that the listener will be closed at most once.
+	l.closeOnce.Do(func() {
 		l.isClosed = true
 		l.registry.wg.Done()
-	}
+	})
 }
 
 func (l *RegistryConfigurationListener) valid() bool {
diff --git a/registry/zookeeper/registry.go b/registry/zookeeper/registry.go
index e41991556a199a537fdc3265e9149ea0fba57c87..1defedc28a2d42183be8c2e5d77441d8831c1d30 100644
--- a/registry/zookeeper/registry.go
+++ b/registry/zookeeper/registry.go
@@ -261,6 +261,10 @@ func (r *zkRegistry) Register(conf common.URL) error {
 	return nil
 }
 
+func (r *zkRegistry) service(c common.URL) string {
+	return url.QueryEscape(c.Service())
+}
+
 func (r *zkRegistry) register(c common.URL) error {
 	var (
 		err error
@@ -296,7 +300,7 @@ func (r *zkRegistry) register(c common.URL) error {
 			return perrors.Errorf("conf{Path:%s, Methods:%s}", c.Path, c.Methods)
 		}
 		// 鍏堝垱寤烘湇鍔′笅闈㈢殑provider node
-		dubboPath = fmt.Sprintf("/dubbo/%s/%s", c.Service(), common.DubboNodes[common.PROVIDER])
+		dubboPath = fmt.Sprintf("/dubbo/%s/%s", r.service(c), common.DubboNodes[common.PROVIDER])
 		r.cltLock.Lock()
 		err = r.client.Create(dubboPath)
 		r.cltLock.Unlock()
@@ -330,11 +334,11 @@ func (r *zkRegistry) register(c common.URL) error {
 		encodedURL = url.QueryEscape(rawURL)
 
 		// Print your own registration service providers.
-		dubboPath = fmt.Sprintf("/dubbo/%s/%s", c.Service(), (common.RoleType(common.PROVIDER)).String())
+		dubboPath = fmt.Sprintf("/dubbo/%s/%s", r.service(c), (common.RoleType(common.PROVIDER)).String())
 		logger.Debugf("provider path:%s, url:%s", dubboPath, rawURL)
 
 	case common.CONSUMER:
-		dubboPath = fmt.Sprintf("/dubbo/%s/%s", c.Service(), common.DubboNodes[common.CONSUMER])
+		dubboPath = fmt.Sprintf("/dubbo/%s/%s", r.service(c), common.DubboNodes[common.CONSUMER])
 		r.cltLock.Lock()
 		err = r.client.Create(dubboPath)
 		r.cltLock.Unlock()
@@ -342,7 +346,7 @@ func (r *zkRegistry) register(c common.URL) error {
 			logger.Errorf("zkClient.create(path{%s}) = error{%v}", dubboPath, perrors.WithStack(err))
 			return perrors.WithStack(err)
 		}
-		dubboPath = fmt.Sprintf("/dubbo/%s/%s", c.Service(), common.DubboNodes[common.PROVIDER])
+		dubboPath = fmt.Sprintf("/dubbo/%s/%s", r.service(c), common.DubboNodes[common.PROVIDER])
 		r.cltLock.Lock()
 		err = r.client.Create(dubboPath)
 		r.cltLock.Unlock()
@@ -359,7 +363,7 @@ func (r *zkRegistry) register(c common.URL) error {
 		rawURL = fmt.Sprintf("consumer://%s%s?%s", localIP, c.Path, params.Encode())
 		encodedURL = url.QueryEscape(rawURL)
 
-		dubboPath = fmt.Sprintf("/dubbo/%s/%s", c.Service(), (common.RoleType(common.CONSUMER)).String())
+		dubboPath = fmt.Sprintf("/dubbo/%s/%s", r.service(c), (common.RoleType(common.CONSUMER)).String())
 		logger.Debugf("consumer path:%s, url:%s", dubboPath, rawURL)
 
 	default:
@@ -479,7 +483,7 @@ func (r *zkRegistry) getListener(conf *common.URL) (*RegistryConfigurationListen
 	//Interested register to dataconfig.
 	r.dataListener.AddInterestedURL(conf)
 	for _, v := range strings.Split(conf.GetParam(constant.CATEGORY_KEY, constant.DEFAULT_CATEGORY), ",") {
-		go r.listener.ListenServiceEvent(fmt.Sprintf("/dubbo/%s/"+v, conf.Service()), r.dataListener)
+		go r.listener.ListenServiceEvent(fmt.Sprintf("/dubbo/%s/"+v, url.QueryEscape(conf.Service())), r.dataListener)
 	}
 
 	return zkListener, nil
diff --git a/remoting/zookeeper/listener_test.go b/remoting/zookeeper/listener_test.go
index a90fbad05ae787f36d38607b0a73374d874e6994..aa627c7e8a53ef87fb39446b05d4001bcf18cf3f 100644
--- a/remoting/zookeeper/listener_test.go
+++ b/remoting/zookeeper/listener_test.go
@@ -18,6 +18,7 @@
 package zookeeper
 
 import (
+	"net/url"
 	"sync"
 	"testing"
 	"time"
@@ -122,3 +123,9 @@ func (m *mockDataListener) DataChange(eventType remoting.Event) bool {
 	}
 	return true
 }
+
+func TestZkPath(t *testing.T) {
+	zkPath := "io.grpc.examples.helloworld.GreeterGrpc$IGreeter"
+	zkPath = url.QueryEscape(zkPath)
+	assert.Equal(t, zkPath, "io.grpc.examples.helloworld.GreeterGrpc%24IGreeter")
+}