diff --git a/.gitignore b/.gitignore
index b8d1f6332ba31faa57814d3e62f869fd42441e8a..6e2013d1b448542ad653bb0febf98a6277f5d864 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,7 +18,7 @@ classes
 
 
 # Gopkg.lock
-# vendor/
+vendor/
 
 logs/
 
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000000000000000000000000000000000000..2038d8ecc81ce319906b66333458eb6eda9afc30
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,17 @@
+language: go
+
+go:
+  - "1.11"
+  - "1.12"
+
+env:
+  - GO111MODULE=on
+
+install: true
+
+script:
+  - go fmt ./... && [[ -z `git status -s` ]]
+  - go mod vendor && go test ./... -coverprofile=coverage.txt -covermode=atomic
+
+after_success:
+  - bash <(curl -s https://codecov.io/bash)
diff --git a/README.md b/README.md
index 93847b161b40680e98cd8143ae0594bc4baa8316..9870a7f3aa3ab8e0b22719c9d21c4857504f4f59 100644
--- a/README.md
+++ b/README.md
@@ -1,32 +1,61 @@
-# GO for Apache Dubbo #
+# Go for Apache Dubbo [涓枃](./README_CN.md) #
+
+[![Build Status](https://travis-ci.com/dubbo/go-for-apache-dubbo.svg?branch=master)](https://travis-ci.com/dubbo/go-for-apache-dubbo)
+[![codecov](https://codecov.io/gh/dubbo/go-for-apache-dubbo/branch/master/graph/badge.svg)](https://codecov.io/gh/dubbo/go-for-apache-dubbo)
+
 ---
-Apache Dubbo Golang Implementation.
+Apache Dubbo Go Implementation.
 
 ## License
 
 Apache License, Version 2.0
 
+## Code design ##
+
+Extension module and layered code design based on dubbo (include protocol layer,registry layer,cluster layer,config layer and so on), Our goal is: you can implement these layered interfaces in a new way, and override the default implementation of dubbo-go[same go-for-apache-dubbo] by calling 'extension.SetXXX' of extension, and complete your special needs without modifying the source code. At the same time, you are welcome to contribute implementation of useful expansion to the community.
+
+![frame design](https://raw.githubusercontent.com/wiki/dubbo/dubbo-go/dubbo-go%E4%BB%A3%E7%A0%81%E5%88%86%E5%B1%82%E8%AE%BE%E8%AE%A1.png)
+
+About detail design please refer to [code layered design](https://github.com/dubbo/go-for-apache-dubbo/wiki/dubbo-go-V2.6-design)
+
 ## Feature list ##
 
-+ 1 Transport: HTTP(鈭�)
-+ 2 Codec:  JsonRPC(鈭�), Hessian(鈭�)
-+ 3 Service discovery锛歋ervice Register(鈭�), Service Watch(鈭�)
-+ 4 Registry: ZooKeeper(鈭�), Etcd(X), Redis(X)
-+ 5 Strategy: Failover(鈭�), Failfast(鈭�)
-+ 6 Load Balance: Random(鈭�), RoundRobin(鈭�)
-+ 7 Role: Consumer(鈭�), Provider(鈭�)
+Finished List:
+
+- Role: Consumer(鈭�), Provider(鈭�)
+- Transport: HTTP(鈭�), TCP(鈭�)
+- Codec: JsonRPC v2(鈭�), Hessian v2(鈭�)
+- Registry: ZooKeeper(鈭�)
+- Cluster Strategy: Failover(鈭�)
+- Load Balance: Random(鈭�)
+- Filter: Echo Health Check(鈭�)
+
+Working List:
+
+- Cluster Strategy: Failfast/Failsafe/Failback/Forking
+- Load Balance: RoundRobin/LeastActive/ConsistentHash
+- Filter: TokenFilter/AccessLogFilter/CountFilter/ActiveLimitFilter/ExecuteLimitFilter/GenericFilter/TpsLimitFilter
+- Registry: etcd/k8s/consul
+
+Todo List:
+
+- routing rule (dubbo v2.6.x)
+- monitoring (dubbo v2.6.x)
+- dynamic configuration (dubbo v2.7.x)
+- metrics (dubbo v2.7.x) waiting dubbo's quota
+
+You can know more about dubbo-go by its [roadmap](https://github.com/dubbo/go-for-apache-dubbo/wiki/Roadmap).
+
+## Quick Start
+
+The subdirectory examples shows how to use dubbo-go. Please read the [examples/README.md](https://github.com/dubbo/go-for-apache-dubbo/blob/develop/examples/README.md) carefully to learn how to dispose the configuration and compile the program.
 
-## Code Example
+## Benchmark
 
-The subdirectory examples shows how to use dubbo-go. Please read the examples/readme.md carefully to learn how to dispose the configuration and compile the program.
+Benchmark project please refer to [go-for-apache-dubbo-benchmark](https://github.com/dubbogo/go-for-apache-dubbo-benchmark)
 
+About dubbo-go benchmarking report, please refer to [dubbo benchmarking report](https://github.com/dubbo/go-for-apache-dubbo/wiki/pressure-test-report-for-dubbo) & [jsonrpc benchmarking report](https://github.com/dubbo/go-for-apache-dubbo/wiki/pressure-test-report-for-jsonrpc)
 
-## Todo list
+## [User List](https://github.com/dubbo/go-for-apache-dubbo/issues/2)
 
-- [ ] Tcp Transport and Hessian2 protocol
-- [ ] Network
-  - [ ] Fuse
-  - [ ] Rate Limit
-  - [ ] Trace
-  - [ ] Metrics
-  - [ ] Load Balance
+![ctrip](https://pic.c-ctrip.com/common/c_logo2013.png)
diff --git a/README_CN.md b/README_CN.md
new file mode 100644
index 0000000000000000000000000000000000000000..f50408582ffbfddf413c4843d63ef67ac1c6bbef
--- /dev/null
+++ b/README_CN.md
@@ -0,0 +1,61 @@
+# Go for Apache Dubbo [English](./README.md) #
+
+[![Build Status](https://travis-ci.com/dubbo/go-for-apache-dubbo.svg?branch=master)](https://travis-ci.com/dubbo/go-for-apache-dubbo)
+[![codecov](https://codecov.io/gh/dubbo/go-for-apache-dubbo/branch/master/graph/badge.svg)](https://codecov.io/gh/dubbo/go-for-apache-dubbo)
+
+---
+Apache Dubbo Go 璇█瀹炵幇
+
+## 璇佷功 ##
+
+Apache License, Version 2.0
+
+## 浠g爜璁捐 ##
+
+鍩轰簬dubbo鐨別xtension妯″潡鍜屽垎灞傜殑浠g爜璁捐(鍖呮嫭 protocol layer, registry layer, cluster layer, config 绛夌瓑)銆傛垜浠殑鐩爣鏄細浣犲彲浠ュ杩欎簺鍒嗗眰鎺ュ彛杩涜鏂扮殑瀹炵幇锛屽苟閫氳繃璋冪敤 extension 妯″潡鐨勨€� extension.SetXXX 鈥濇柟娉曟潵瑕嗙洊 dubbo-go [鍚� go-for-apache-dubbo ]鐨勯粯璁ゅ疄鐜帮紝浠ュ畬鎴愯嚜宸辩殑鐗规畩闇€姹傝€屾棤闇€淇敼婧愪唬鐮併€傚悓鏃讹紝娆㈣繋浣犱负绀惧尯璐$尞鏈夌敤鐨勬嫇灞曞疄鐜般€�
+
+![妗嗘灦璁捐](https://raw.githubusercontent.com/wiki/dubbo/dubbo-go/dubbo-go%E4%BB%A3%E7%A0%81%E5%88%86%E5%B1%82%E8%AE%BE%E8%AE%A1.png)
+
+鍏充簬璇︾粏璁捐璇烽槄璇� [code layered design](https://github.com/dubbo/go-for-apache-dubbo/wiki/dubbo-go-V2.6-design)
+
+## 鍔熻兘鍒楄〃 ##
+
+瀹炵幇鍒楄〃:
+
+- Role: Consumer(鈭�), Provider(鈭�)
+- Transport: HTTP(鈭�), TCP(鈭�)
+- Codec: JsonRPC v2(鈭�), Hessian v2(鈭�)
+- Registry: ZooKeeper(鈭�)
+- Cluster Strategy: Failover(鈭�)
+- Load Balance: Random(鈭�)
+- Filter: Echo Health Check(鈭�)
+
+寮€鍙戜腑鍒楄〃:
+
+- Cluster Strategy: Failfast/Failsafe/Failback/Forking
+- Load Balance: RoundRobin/LeastActive/ConsistentHash
+- Filter: TokenFilter/AccessLogFilter/CountFilter/ActiveLimitFilter/ExecuteLimitFilter/GenericFilter/TpsLimitFilter
+- Registry: etcd/k8s/consul
+
+浠诲姟鍒楄〃:
+
+- routing rule (dubbo v2.6.x)
+- monitoring (dubbo v2.6.x)
+- dynamic configuration (dubbo v2.7.x)
+- metrics (dubbo v2.7.x) waiting dubbo's quota
+
+浣犲彲浠ラ€氳繃璁块棶 [roadmap](https://github.com/dubbo/go-for-apache-dubbo/wiki/Roadmap) 鐭ラ亾鏇村鍏充簬 dubbo-go 鐨勪俊鎭�
+
+## 蹇€熷紑濮� ##
+
+杩欎釜瀛愮洰褰曚笅鐨勪緥瀛愬睍绀轰簡濡備綍浣跨敤 dubbo-go 銆傝浠旂粏闃呰 [examples/README.md](https://github.com/dubbo/go-for-apache-dubbo/blob/develop/examples/README.md) 瀛︿範濡備綍澶勭悊閰嶇疆骞剁紪璇戠▼搴忋€�
+
+## 鎬ц兘娴嬭瘯 ##
+
+鎬ц兘娴嬭瘯椤圭洰鏄� [go-for-apache-dubbo-benchmark](https://github.com/dubbogo/go-for-apache-dubbo-benchmark)
+
+鍏充簬 dubbo-go 鎬ц兘娴嬭瘯鎶ュ憡锛岃闃呰 [dubbo benchmarking report](https://github.com/dubbo/go-for-apache-dubbo/wiki/pressure-test-report-for-dubbo) & [jsonrpc benchmarking report](https://github.com/dubbo/go-for-apache-dubbo/wiki/pressure-test-report-for-jsonrpc)
+
+## [User List](https://github.com/dubbo/go-for-apache-dubbo/issues/2)
+
+![ctrip](https://pic.c-ctrip.com/common/c_logo2013.png)
diff --git a/client/client_transport.go b/client/client_transport.go
deleted file mode 100644
index 25dd6506abc8bf3155d9d12a3809cc8a55a0563b..0000000000000000000000000000000000000000
--- a/client/client_transport.go
+++ /dev/null
@@ -1,22 +0,0 @@
-package client
-
-import (
-	"context"
-)
-
-import (
-	"github.com/dubbo/go-for-apache-dubbo/registry"
-)
-
-type Transport interface {
-	Call(ctx context.Context, url registry.ServiceURL, request Request, resp interface{}) error
-	NewRequest(conf registry.ServiceConfig, method string, args interface{}) (Request, error)
-}
-
-//////////////////////////////////////////////
-// Request
-//////////////////////////////////////////////
-
-type Request interface {
-	ServiceConfig() registry.ServiceConfig
-}
diff --git a/client/invoker/invoker.go b/client/invoker/invoker.go
deleted file mode 100644
index 075be029b171ddebfdd37faff767b1dd746753fd..0000000000000000000000000000000000000000
--- a/client/invoker/invoker.go
+++ /dev/null
@@ -1,224 +0,0 @@
-package invoker
-
-import (
-	"context"
-	"sync"
-	"time"
-)
-
-import (
-	log "github.com/AlexStocks/log4go"
-	jerrors "github.com/juju/errors"
-)
-
-import (
-	"github.com/dubbo/go-for-apache-dubbo/client"
-	"github.com/dubbo/go-for-apache-dubbo/client/selector"
-	"github.com/dubbo/go-for-apache-dubbo/dubbo"
-	"github.com/dubbo/go-for-apache-dubbo/jsonrpc"
-	"github.com/dubbo/go-for-apache-dubbo/registry"
-)
-
-const RegistryConnDelay = 3
-
-type Options struct {
-	ServiceTTL time.Duration
-	selector   selector.Selector
-	//TODO:we should provider a transport client interface
-	HttpClient  *jsonrpc.HTTPClient
-	DubboClient *dubbo.Client
-}
-type Option func(*Options)
-
-func WithServiceTTL(ttl time.Duration) Option {
-	return func(o *Options) {
-		o.ServiceTTL = ttl
-	}
-}
-
-func WithHttpClient(client *jsonrpc.HTTPClient) Option {
-	return func(o *Options) {
-		o.HttpClient = client
-	}
-}
-func WithDubboClient(client *dubbo.Client) Option {
-	return func(o *Options) {
-		o.DubboClient = client
-	}
-}
-
-func WithLBSelector(selector selector.Selector) Option {
-	return func(o *Options) {
-		o.selector = selector
-	}
-}
-
-type Invoker struct {
-	Options
-	cacheServiceMap map[string]*ServiceArray
-	registry        registry.Registry
-	listenerLock    sync.Mutex
-}
-
-func NewInvoker(registry registry.Registry, opts ...Option) (*Invoker, error) {
-	options := Options{
-		//default 300s
-		ServiceTTL: time.Duration(300e9),
-		selector:   selector.NewRandomSelector(),
-	}
-	for _, opt := range opts {
-		opt(&options)
-	}
-	if options.HttpClient == nil && options.DubboClient == nil {
-		return nil, jerrors.New("Must specify the transport client!")
-	}
-	invoker := &Invoker{
-		Options:         options,
-		cacheServiceMap: make(map[string]*ServiceArray),
-		registry:        registry,
-	}
-	go invoker.listen()
-	return invoker, nil
-}
-
-func (ivk *Invoker) listen() {
-	for {
-		if ivk.registry.IsClosed() {
-			log.Warn("event listener game over.")
-			return
-		}
-
-		listener, err := ivk.registry.Subscribe()
-		if err != nil {
-			if ivk.registry.IsClosed() {
-				log.Warn("event listener game over.")
-				return
-			}
-			log.Warn("getListener() = err:%s", jerrors.ErrorStack(err))
-			time.Sleep(time.Duration(RegistryConnDelay) * time.Second)
-			continue
-		}
-
-		for {
-			if serviceEvent, err := listener.Next(); err != nil {
-				log.Warn("Selector.watch() = error{%v}", jerrors.ErrorStack(err))
-				listener.Close()
-				time.Sleep(time.Duration(RegistryConnDelay) * time.Second)
-				return
-			} else {
-				ivk.update(serviceEvent)
-			}
-
-		}
-
-	}
-}
-
-func (ivk *Invoker) update(res *registry.ServiceEvent) {
-	if res == nil || res.Service == nil {
-		return
-	}
-
-	log.Debug("registry update, result{%s}", res)
-	registryKey := res.Service.ServiceConfig().Key()
-
-	ivk.listenerLock.Lock()
-	defer ivk.listenerLock.Unlock()
-
-	svcArr, ok := ivk.cacheServiceMap[registryKey]
-	log.Debug("registry name:%s, its current member lists:%+v", registryKey, svcArr)
-
-	switch res.Action {
-	case registry.ServiceAdd:
-		if ok {
-			svcArr.add(res.Service, ivk.ServiceTTL)
-		} else {
-			ivk.cacheServiceMap[registryKey] = newServiceArray([]registry.ServiceURL{res.Service})
-		}
-	case registry.ServiceDel:
-		if ok {
-			svcArr.del(res.Service, ivk.ServiceTTL)
-			if len(svcArr.arr) == 0 {
-				delete(ivk.cacheServiceMap, registryKey)
-				log.Warn("delete registry %s from registry map", registryKey)
-			}
-		}
-		log.Error("selector delete registryURL{%s}", res.Service)
-	}
-}
-
-func (ivk *Invoker) getService(registryConf registry.ServiceConfig) (*ServiceArray, error) {
-	defer ivk.listenerLock.Unlock()
-
-	registryKey := registryConf.Key()
-
-	ivk.listenerLock.Lock()
-	svcArr, sok := ivk.cacheServiceMap[registryKey]
-	log.Debug("r.svcArr[registryString{%v}] = svcArr{%s}", registryKey, svcArr)
-	if sok && time.Since(svcArr.birth) < ivk.Options.ServiceTTL {
-		return svcArr, nil
-	}
-	ivk.listenerLock.Unlock()
-
-	svcs, err := ivk.registry.GetService(registryConf)
-	ivk.listenerLock.Lock()
-
-	if err != nil {
-		log.Error("Registry.get(conf:%+v) = {err:%s, svcs:%+v}",
-			registryConf, jerrors.ErrorStack(err), svcs)
-
-		return nil, jerrors.Trace(err)
-	}
-
-	newSvcArr := newServiceArray(svcs)
-	ivk.cacheServiceMap[registryKey] = newSvcArr
-	return newSvcArr, nil
-}
-
-func (ivk *Invoker) HttpCall(ctx context.Context, reqId int64, req client.Request, resp interface{}) error {
-
-	serviceConf := req.ServiceConfig()
-	registryArray, err := ivk.getService(serviceConf)
-	if err != nil {
-		return err
-	}
-	if len(registryArray.arr) == 0 {
-		return jerrors.New("cannot find svc " + serviceConf.String())
-	}
-	url, err := ivk.selector.Select(reqId, registryArray)
-	if err != nil {
-		return err
-	}
-	if err = ivk.HttpClient.Call(ctx, url, req, resp); err != nil {
-		log.Error("client.Call() return error:%+v", jerrors.ErrorStack(err))
-		return err
-	}
-	log.Info("response result:%s", resp)
-	return nil
-}
-
-func (ivk *Invoker) DubboCall(reqId int64, registryConf registry.ServiceConfig, method string, args, reply interface{}, opts ...dubbo.CallOption) error {
-
-	registryArray, err := ivk.getService(registryConf)
-	if err != nil {
-		return err
-	}
-	if len(registryArray.arr) == 0 {
-		return jerrors.New("cannot find svc " + registryConf.String())
-	}
-	url, err := ivk.selector.Select(reqId, registryArray)
-	if err != nil {
-		return err
-	}
-	//TODO:杩欓噷瑕佹敼涓€涓媍all鏂规硶鏀逛负鎺ユ敹鎸囬拡绫诲瀷
-	if err = ivk.DubboClient.Call(url.Ip()+":"+url.Port(), url, method, args, reply, opts...); err != nil {
-		log.Error("client.Call() return error:%+v", jerrors.ErrorStack(err))
-		return err
-	}
-	log.Info("response result:%s", reply)
-	return nil
-}
-
-func (ivk *Invoker) Close() {
-	ivk.DubboClient.Close()
-}
diff --git a/client/invoker/service_array.go b/client/invoker/service_array.go
deleted file mode 100644
index 3ffb9615f026ede443046c69e08de7c5074018c6..0000000000000000000000000000000000000000
--- a/client/invoker/service_array.go
+++ /dev/null
@@ -1,76 +0,0 @@
-package invoker
-
-import (
-	"fmt"
-	"strings"
-	"time"
-)
-
-import (
-	jerrors "github.com/juju/errors"
-)
-
-import (
-	"github.com/dubbo/go-for-apache-dubbo/registry"
-)
-
-//////////////////////////////////////////
-// registry array
-// should be returned by registry ,will be used by client & waiting to selector
-//////////////////////////////////////////
-
-var (
-	ErrServiceArrayEmpty   = jerrors.New("registryArray empty")
-	ErrServiceArrayTimeout = jerrors.New("registryArray timeout")
-)
-
-type ServiceArray struct {
-	arr   []registry.ServiceURL
-	birth time.Time
-	idx   int64
-}
-
-func newServiceArray(arr []registry.ServiceURL) *ServiceArray {
-	return &ServiceArray{
-		arr:   arr,
-		birth: time.Now(),
-	}
-}
-
-func (s *ServiceArray) GetIdx() *int64 {
-	return &s.idx
-}
-
-func (s *ServiceArray) GetSize() int64 {
-	return int64(len(s.arr))
-}
-
-func (s *ServiceArray) GetService(i int64) registry.ServiceURL {
-	return s.arr[i]
-}
-
-func (s *ServiceArray) String() string {
-	var builder strings.Builder
-	builder.WriteString(fmt.Sprintf("birth:%s, idx:%d, arr len:%d, arr:{", s.birth, s.idx, len(s.arr)))
-	for i := range s.arr {
-		builder.WriteString(fmt.Sprintf("%d:%s, ", i, s.arr[i]))
-	}
-	builder.WriteString("}")
-
-	return builder.String()
-}
-
-func (s *ServiceArray) add(registry registry.ServiceURL, ttl time.Duration) {
-	s.arr = append(s.arr, registry)
-	s.birth = time.Now().Add(ttl)
-}
-
-func (s *ServiceArray) del(registry registry.ServiceURL, ttl time.Duration) {
-	for i, svc := range s.arr {
-		if svc.PrimitiveURL() == registry.PrimitiveURL() {
-			s.arr = append(s.arr[:i], s.arr[i+1:]...)
-			s.birth = time.Now().Add(ttl)
-			break
-		}
-	}
-}
diff --git a/client/selector/random.go b/client/selector/random.go
deleted file mode 100644
index 170ef5b489d59e5d7abfe76034ab97d9aad6fc9f..0000000000000000000000000000000000000000
--- a/client/selector/random.go
+++ /dev/null
@@ -1,27 +0,0 @@
-package selector
-
-import (
-	"math/rand"
-	"sync/atomic"
-)
-
-import (
-	"github.com/dubbo/go-for-apache-dubbo/client"
-	"github.com/dubbo/go-for-apache-dubbo/registry"
-)
-
-type RandomSelector struct{}
-
-func NewRandomSelector() Selector {
-	return &RandomSelector{}
-}
-
-func (s *RandomSelector) Select(ID int64, array client.ServiceArrayIf) (registry.ServiceURL, error) {
-	if array.GetSize() == 0 {
-		return nil, ServiceArrayEmpty
-	}
-
-	idx := atomic.AddInt64(array.GetIdx(), 1)
-	idx = ((int64)(rand.Int()) + ID) % array.GetSize()
-	return array.GetService(idx), nil
-}
diff --git a/client/selector/round_robin.go b/client/selector/round_robin.go
deleted file mode 100644
index cc6a5d078e62d09998da1a291d6751e91b566ccf..0000000000000000000000000000000000000000
--- a/client/selector/round_robin.go
+++ /dev/null
@@ -1,26 +0,0 @@
-package selector
-
-import (
-	"sync/atomic"
-)
-
-import (
-	"github.com/dubbo/go-for-apache-dubbo/client"
-	"github.com/dubbo/go-for-apache-dubbo/registry"
-)
-
-type RoundRobinSelector struct{}
-
-func NewRoundRobinSelector() Selector {
-	return &RoundRobinSelector{}
-}
-
-func (s *RoundRobinSelector) Select(ID int64, array client.ServiceArrayIf) (registry.ServiceURL, error) {
-	if array.GetSize() == 0 {
-		return nil, ServiceArrayEmpty
-	}
-
-	idx := atomic.AddInt64(array.GetIdx(), 1)
-	idx = (ID + idx) % array.GetSize()
-	return array.GetService(idx), nil
-}
diff --git a/client/selector/selector.go b/client/selector/selector.go
deleted file mode 100644
index a1d58dd85ef05f0c5752724dbe396dfec7360108..0000000000000000000000000000000000000000
--- a/client/selector/selector.go
+++ /dev/null
@@ -1,18 +0,0 @@
-package selector
-
-import (
-	"fmt"
-)
-
-import (
-	"github.com/dubbo/go-for-apache-dubbo/client"
-	"github.com/dubbo/go-for-apache-dubbo/registry"
-)
-
-var (
-	ServiceArrayEmpty = fmt.Errorf("emtpy service array")
-)
-
-type Selector interface {
-	Select(ID int64, array client.ServiceArrayIf) (registry.ServiceURL, error)
-}
diff --git a/client/service_array.go b/client/service_array.go
deleted file mode 100644
index d16d2d996ea9054f675bd4b037af7088ca615b02..0000000000000000000000000000000000000000
--- a/client/service_array.go
+++ /dev/null
@@ -1,9 +0,0 @@
-package client
-
-import "github.com/dubbo/go-for-apache-dubbo/registry"
-
-type ServiceArrayIf interface {
-	GetIdx() *int64
-	GetSize() int64
-	GetService(i int64) registry.ServiceURL
-}
diff --git a/cluster/cluster.go b/cluster/cluster.go
new file mode 100644
index 0000000000000000000000000000000000000000..a9a6c2e5972f44c50180f39c42f27b6aa681546f
--- /dev/null
+++ b/cluster/cluster.go
@@ -0,0 +1,21 @@
+// Copyright 2016-2019 hxmhlt
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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
+
+import "github.com/dubbo/go-for-apache-dubbo/protocol"
+
+type Cluster interface {
+	Join(Directory) protocol.Invoker
+}
diff --git a/cluster/cluster_impl/base_cluster_invoker.go b/cluster/cluster_impl/base_cluster_invoker.go
new file mode 100644
index 0000000000000000000000000000000000000000..3b0c587cce9eba4825fdb566c3c790c41c805c09
--- /dev/null
+++ b/cluster/cluster_impl/base_cluster_invoker.go
@@ -0,0 +1,123 @@
+// Copyright 2016-2019 hxmhlt
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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 (
+	perrors "github.com/pkg/errors"
+	"go.uber.org/atomic"
+)
+
+import (
+	"github.com/dubbo/go-for-apache-dubbo/cluster"
+	"github.com/dubbo/go-for-apache-dubbo/common"
+	"github.com/dubbo/go-for-apache-dubbo/common/utils"
+	"github.com/dubbo/go-for-apache-dubbo/protocol"
+	"github.com/dubbo/go-for-apache-dubbo/version"
+)
+
+type baseClusterInvoker struct {
+	directory      cluster.Directory
+	availablecheck bool
+	destroyed      *atomic.Bool
+}
+
+func newBaseClusterInvoker(directory cluster.Directory) baseClusterInvoker {
+	return baseClusterInvoker{
+		directory:      directory,
+		availablecheck: true,
+		destroyed:      atomic.NewBool(false),
+	}
+}
+func (invoker *baseClusterInvoker) GetUrl() common.URL {
+	return invoker.directory.GetUrl()
+}
+
+func (invoker *baseClusterInvoker) Destroy() {
+	//this is must atom operation
+	if invoker.destroyed.CAS(false, true) {
+		invoker.directory.Destroy()
+	}
+}
+
+func (invoker *baseClusterInvoker) IsAvailable() bool {
+	//TODO:sticky connection
+	return invoker.directory.IsAvailable()
+}
+
+//check invokers availables
+func (invoker *baseClusterInvoker) checkInvokers(invokers []protocol.Invoker, invocation protocol.Invocation) error {
+	if len(invokers) == 0 {
+		ip, _ := utils.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, version.Version)
+	}
+	return nil
+
+}
+
+//check cluster invoker is destroyed or not
+func (invoker *baseClusterInvoker) checkWhetherDestroyed() error {
+	if invoker.destroyed.Load() {
+		ip, _ := utils.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, version.Version)
+	}
+	return nil
+}
+
+func (invoker *baseClusterInvoker) doSelect(lb cluster.LoadBalance, invocation protocol.Invocation, invokers []protocol.Invoker, invoked []protocol.Invoker) protocol.Invoker {
+	//todo:ticky connect 绮樼焊杩炴帴
+	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) {
+		// do reselect
+		var reslectInvokers []protocol.Invoker
+
+		for _, invoker := range invokers {
+			if !invoker.IsAvailable() {
+				continue
+			}
+
+			if !isInvoked(invoker, invoked) {
+				reslectInvokers = append(reslectInvokers, invoker)
+			}
+		}
+
+		if len(reslectInvokers) > 0 {
+			return lb.Select(reslectInvokers, invocation)
+		} else {
+			return nil
+		}
+	}
+	return selectedInvoker
+
+}
+
+func isInvoked(selectedInvoker protocol.Invoker, invoked []protocol.Invoker) bool {
+	if len(invoked) > 0 {
+		for _, i := range invoked {
+			if i == selectedInvoker {
+				return true
+			}
+		}
+	}
+	return false
+}
diff --git a/cluster/cluster_impl/failover_cluster.go b/cluster/cluster_impl/failover_cluster.go
new file mode 100644
index 0000000000000000000000000000000000000000..2f2bfdfe36e2943a9757b46c3218116e583a01d6
--- /dev/null
+++ b/cluster/cluster_impl/failover_cluster.go
@@ -0,0 +1,38 @@
+// Copyright 2016-2019 hxmhlt
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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 (
+	"github.com/dubbo/go-for-apache-dubbo/cluster"
+	"github.com/dubbo/go-for-apache-dubbo/common/extension"
+	"github.com/dubbo/go-for-apache-dubbo/protocol"
+)
+
+type failoverCluster struct {
+}
+
+const name = "failover"
+
+func init() {
+	extension.SetCluster(name, NewFailoverCluster)
+}
+
+func NewFailoverCluster() cluster.Cluster {
+	return &failoverCluster{}
+}
+
+func (cluster *failoverCluster) Join(directory cluster.Directory) protocol.Invoker {
+	return newFailoverClusterInvoker(directory)
+}
diff --git a/cluster/cluster_impl/failover_cluster_invoker.go b/cluster/cluster_impl/failover_cluster_invoker.go
new file mode 100644
index 0000000000000000000000000000000000000000..5bb2471e06c967159196d754fe04158ad3a96c6b
--- /dev/null
+++ b/cluster/cluster_impl/failover_cluster_invoker.go
@@ -0,0 +1,100 @@
+// Copyright 2016-2019 hxmhlt
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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 (
+	perrors "github.com/pkg/errors"
+)
+
+import (
+	"github.com/dubbo/go-for-apache-dubbo/cluster"
+	"github.com/dubbo/go-for-apache-dubbo/common/constant"
+	"github.com/dubbo/go-for-apache-dubbo/common/extension"
+	"github.com/dubbo/go-for-apache-dubbo/common/utils"
+	"github.com/dubbo/go-for-apache-dubbo/protocol"
+	"github.com/dubbo/go-for-apache-dubbo/version"
+)
+
+type failoverClusterInvoker struct {
+	baseClusterInvoker
+}
+
+func newFailoverClusterInvoker(directory cluster.Directory) protocol.Invoker {
+	return &failoverClusterInvoker{
+		baseClusterInvoker: newBaseClusterInvoker(directory),
+	}
+}
+
+func (invoker *failoverClusterInvoker) Invoke(invocation protocol.Invocation) protocol.Result {
+
+	invokers := invoker.directory.List(invocation)
+	err := invoker.checkInvokers(invokers, invocation)
+
+	if err != nil {
+		return &protocol.RPCResult{Err: err}
+	}
+	url := invokers[0].GetUrl()
+
+	methodName := invocation.MethodName()
+	//Get the service loadbalance config
+	lb := url.GetParam(constant.LOADBALANCE_KEY, constant.DEFAULT_LOADBALANCE)
+
+	//Get the service method loadbalance config if have
+	if v := url.GetMethodParam(methodName, constant.LOADBALANCE_KEY, ""); v != "" {
+		lb = v
+	}
+	loadbalance := extension.GetLoadbalance(lb)
+
+	//get reties
+	retries := url.GetParamInt(constant.RETRIES_KEY, constant.DEFAULT_RETRIES)
+
+	//Get the service method loadbalance config if have
+	if v := url.GetMethodParamInt(methodName, constant.RETRIES_KEY, 0); v != 0 {
+		retries = v
+	}
+	invoked := []protocol.Invoker{}
+	providers := []string{}
+	var result protocol.Result
+	for i := int64(0); i < retries; i++ {
+		//Reselect before retry to avoid a change of candidate `invokers`.
+		//NOTE: if `invokers` changed, then `invoked` also lose accuracy.
+		if i > 0 {
+			err := invoker.checkWhetherDestroyed()
+			if err != nil {
+				return &protocol.RPCResult{Err: err}
+			}
+			invokers = invoker.directory.List(invocation)
+			err = invoker.checkInvokers(invokers, invocation)
+			if err != nil {
+				return &protocol.RPCResult{Err: err}
+			}
+		}
+		ivk := invoker.doSelect(loadbalance, invocation, invokers, invoked)
+		invoked = append(invoked, ivk)
+		//DO INVOKE
+		result = ivk.Invoke(invocation)
+		if result.Error() != nil {
+			providers = append(providers, ivk.GetUrl().Key())
+			continue
+		} else {
+			return result
+		}
+	}
+	ip, _ := utils.GetLocalIP()
+	return &protocol.RPCResult{Err: perrors.Errorf("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. Last error is %v.",
+		methodName, invoker.GetUrl().Service(), retries, providers, len(providers), len(invokers), invoker.directory.GetUrl(), ip, version.Version, result.Error().Error(),
+	)}
+}
diff --git a/cluster/cluster_impl/failover_cluster_test.go b/cluster/cluster_impl/failover_cluster_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..5f0a28f0a679c42b6cd5fd2b9a9052a4722c0737
--- /dev/null
+++ b/cluster/cluster_impl/failover_cluster_test.go
@@ -0,0 +1,169 @@
+// Copyright 2016-2019 hxmhlt
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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"
+	"net/url"
+	"testing"
+)
+import (
+	perrors "github.com/pkg/errors"
+	"github.com/stretchr/testify/assert"
+)
+
+import (
+	"github.com/dubbo/go-for-apache-dubbo/cluster/directory"
+	"github.com/dubbo/go-for-apache-dubbo/cluster/loadbalance"
+	"github.com/dubbo/go-for-apache-dubbo/common"
+	"github.com/dubbo/go-for-apache-dubbo/common/constant"
+	"github.com/dubbo/go-for-apache-dubbo/common/extension"
+	"github.com/dubbo/go-for-apache-dubbo/common/logger"
+	"github.com/dubbo/go-for-apache-dubbo/protocol"
+	"github.com/dubbo/go-for-apache-dubbo/protocol/invocation"
+)
+
+/////////////////////////////
+// mock invoker
+/////////////////////////////
+
+type MockInvoker struct {
+	url       common.URL
+	available bool
+	destroyed bool
+
+	successCount int
+}
+
+func NewMockInvoker(url common.URL, successCount int) *MockInvoker {
+	return &MockInvoker{
+		url:          url,
+		available:    true,
+		destroyed:    false,
+		successCount: successCount,
+	}
+}
+
+func (bi *MockInvoker) GetUrl() common.URL {
+	return bi.url
+}
+
+func (bi *MockInvoker) IsAvailable() bool {
+	return bi.available
+}
+
+func (bi *MockInvoker) IsDestroyed() bool {
+	return bi.destroyed
+}
+
+type rest struct {
+	tried   int
+	success bool
+}
+
+func (bi *MockInvoker) Invoke(invocation protocol.Invocation) protocol.Result {
+	count++
+	var success bool
+	var err error = nil
+	if count >= bi.successCount {
+		success = true
+	} else {
+		err = perrors.New("error")
+	}
+	result := &protocol.RPCResult{Err: err, Rest: rest{tried: count, success: success}}
+
+	return result
+}
+
+func (bi *MockInvoker) Destroy() {
+	logger.Infof("Destroy invoker: %v", bi.GetUrl().String())
+	bi.destroyed = true
+	bi.available = false
+}
+
+var count int
+
+func normalInvoke(t *testing.T, successCount int, urlParam url.Values, invocations ...*invocation.RPCInvocation) protocol.Result {
+	extension.SetLoadbalance("random", loadbalance.NewRandomLoadBalance)
+	failoverCluster := NewFailoverCluster()
+
+	invokers := []protocol.Invoker{}
+	for i := 0; i < 10; i++ {
+		url, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i), common.WithParams(urlParam))
+		invokers = append(invokers, NewMockInvoker(url, successCount))
+	}
+
+	staticDir := directory.NewStaticDirectory(invokers)
+	clusterInvoker := failoverCluster.Join(staticDir)
+	if len(invocations) > 0 {
+		return clusterInvoker.Invoke(invocations[0])
+	}
+	return clusterInvoker.Invoke(&invocation.RPCInvocation{})
+}
+func Test_FailoverInvokeSuccess(t *testing.T) {
+	urlParams := url.Values{}
+	result := normalInvoke(t, 2, urlParams)
+	assert.NoError(t, result.Error())
+	count = 0
+}
+
+func Test_FailoverInvokeFail(t *testing.T) {
+	urlParams := url.Values{}
+	result := normalInvoke(t, 3, urlParams)
+	assert.Errorf(t, result.Error(), "error")
+	count = 0
+}
+
+func Test_FailoverInvoke1(t *testing.T) {
+	urlParams := url.Values{}
+	urlParams.Set(constant.RETRIES_KEY, "3")
+	result := normalInvoke(t, 3, urlParams)
+	assert.NoError(t, result.Error())
+	count = 0
+}
+
+func Test_FailoverInvoke2(t *testing.T) {
+	urlParams := url.Values{}
+	urlParams.Set(constant.RETRIES_KEY, "2")
+	urlParams.Set("methods.test."+constant.RETRIES_KEY, "3")
+
+	ivc := &invocation.RPCInvocation{}
+	ivc.SetMethod("test")
+	result := normalInvoke(t, 3, urlParams, ivc)
+	assert.NoError(t, result.Error())
+	count = 0
+}
+
+func Test_FailoverDestroy(t *testing.T) {
+	extension.SetLoadbalance("random", loadbalance.NewRandomLoadBalance)
+	failoverCluster := NewFailoverCluster()
+
+	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))
+		invokers = append(invokers, NewMockInvoker(url, 1))
+	}
+
+	staticDir := directory.NewStaticDirectory(invokers)
+	clusterInvoker := failoverCluster.Join(staticDir)
+	assert.Equal(t, true, clusterInvoker.IsAvailable())
+	result := clusterInvoker.Invoke(&invocation.RPCInvocation{})
+	assert.NoError(t, result.Error())
+	count = 0
+	clusterInvoker.Destroy()
+	assert.Equal(t, false, clusterInvoker.IsAvailable())
+
+}
diff --git a/cluster/cluster_impl/mock_cluster.go b/cluster/cluster_impl/mock_cluster.go
new file mode 100644
index 0000000000000000000000000000000000000000..f913eb6827b56aa1795df593fee38aaedf4bded6
--- /dev/null
+++ b/cluster/cluster_impl/mock_cluster.go
@@ -0,0 +1,31 @@
+// Copyright 2016-2019 hxmhlt
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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 (
+	"github.com/dubbo/go-for-apache-dubbo/cluster"
+	"github.com/dubbo/go-for-apache-dubbo/protocol"
+)
+
+type mockCluster struct {
+}
+
+func NewMockCluster() cluster.Cluster {
+	return &mockCluster{}
+}
+
+func (cluster *mockCluster) Join(directory cluster.Directory) protocol.Invoker {
+	return protocol.NewBaseInvoker(directory.GetUrl())
+}
diff --git a/cluster/cluster_impl/registry_aware_cluster.go b/cluster/cluster_impl/registry_aware_cluster.go
new file mode 100644
index 0000000000000000000000000000000000000000..9f897bdf59c9b32794a7fe3038ee27e272f891e2
--- /dev/null
+++ b/cluster/cluster_impl/registry_aware_cluster.go
@@ -0,0 +1,36 @@
+// Copyright 2016-2019 hxmhlt
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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 (
+	"github.com/dubbo/go-for-apache-dubbo/cluster"
+	"github.com/dubbo/go-for-apache-dubbo/common/extension"
+	"github.com/dubbo/go-for-apache-dubbo/protocol"
+)
+
+type registryAwareCluster struct {
+}
+
+func init() {
+	extension.SetCluster("registryAware", NewRegistryAwareCluster)
+}
+
+func NewRegistryAwareCluster() cluster.Cluster {
+	return &registryAwareCluster{}
+}
+
+func (cluster *registryAwareCluster) Join(directory cluster.Directory) protocol.Invoker {
+	return newRegistryAwareClusterInvoker(directory)
+}
diff --git a/cluster/cluster_impl/registry_aware_cluster_invoker.go b/cluster/cluster_impl/registry_aware_cluster_invoker.go
new file mode 100644
index 0000000000000000000000000000000000000000..975ff8bdad63243188130b5772f2293c0b4e24de
--- /dev/null
+++ b/cluster/cluster_impl/registry_aware_cluster_invoker.go
@@ -0,0 +1,49 @@
+// Copyright 2016-2019 hxmhlt
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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 (
+	"github.com/dubbo/go-for-apache-dubbo/cluster"
+	"github.com/dubbo/go-for-apache-dubbo/common/constant"
+	"github.com/dubbo/go-for-apache-dubbo/protocol"
+)
+
+type registryAwareClusterInvoker struct {
+	baseClusterInvoker
+}
+
+func newRegistryAwareClusterInvoker(directory cluster.Directory) protocol.Invoker {
+	return &registryAwareClusterInvoker{
+		baseClusterInvoker: newBaseClusterInvoker(directory),
+	}
+}
+
+func (invoker *registryAwareClusterInvoker) Invoke(invocation protocol.Invocation) protocol.Result {
+	invokers := invoker.directory.List(invocation)
+	//First, pick the invoker (XXXClusterInvoker) that comes from the local registry, distinguish by a 'default' key.
+	for _, invoker := range invokers {
+		if invoker.IsAvailable() && invoker.GetUrl().GetParam(constant.REGISTRY_DEFAULT_KEY, "false") == "true" {
+			return invoker.Invoke(invocation)
+		}
+	}
+
+	//If none of the invokers has a local signal, pick the first one available.
+	for _, invoker := range invokers {
+		if invoker.IsAvailable() {
+			return invoker.Invoke(invocation)
+		}
+	}
+	return nil
+}
diff --git a/cluster/cluster_impl/registry_aware_cluster_test.go b/cluster/cluster_impl/registry_aware_cluster_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..51734116a696303f4079c06ff361b200234e3aa9
--- /dev/null
+++ b/cluster/cluster_impl/registry_aware_cluster_test.go
@@ -0,0 +1,68 @@
+// Copyright 2016-2019 hxmhlt
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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/dubbo/go-for-apache-dubbo/cluster/directory"
+	"github.com/dubbo/go-for-apache-dubbo/common"
+	"github.com/dubbo/go-for-apache-dubbo/protocol"
+	"github.com/dubbo/go-for-apache-dubbo/protocol/invocation"
+)
+
+func Test_RegAwareInvokeSuccess(t *testing.T) {
+
+	regAwareCluster := NewRegistryAwareCluster()
+
+	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))
+		invokers = append(invokers, NewMockInvoker(url, 1))
+	}
+
+	staticDir := directory.NewStaticDirectory(invokers)
+	clusterInvoker := regAwareCluster.Join(staticDir)
+	result := clusterInvoker.Invoke(&invocation.RPCInvocation{})
+	assert.NoError(t, result.Error())
+	count = 0
+}
+
+func TestDestroy(t *testing.T) {
+	regAwareCluster := NewRegistryAwareCluster()
+
+	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))
+		invokers = append(invokers, NewMockInvoker(url, 1))
+	}
+
+	staticDir := directory.NewStaticDirectory(invokers)
+	clusterInvoker := regAwareCluster.Join(staticDir)
+	assert.Equal(t, true, clusterInvoker.IsAvailable())
+	result := clusterInvoker.Invoke(&invocation.RPCInvocation{})
+	assert.NoError(t, result.Error())
+	count = 0
+	clusterInvoker.Destroy()
+	assert.Equal(t, false, clusterInvoker.IsAvailable())
+
+}
diff --git a/cluster/directory.go b/cluster/directory.go
new file mode 100644
index 0000000000000000000000000000000000000000..9a055d6f580e622a68e648de0a0b2e3999773752
--- /dev/null
+++ b/cluster/directory.go
@@ -0,0 +1,26 @@
+// Copyright 2016-2019 hxmhlt
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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
+
+import (
+	"github.com/dubbo/go-for-apache-dubbo/common"
+	"github.com/dubbo/go-for-apache-dubbo/protocol"
+)
+
+// Extension - Directory
+type Directory interface {
+	common.Node
+	List(invocation protocol.Invocation) []protocol.Invoker
+}
diff --git a/cluster/directory/base_directory.go b/cluster/directory/base_directory.go
new file mode 100644
index 0000000000000000000000000000000000000000..ef74c3511ca1b57fac91249c9b02aa94cb8a1f78
--- /dev/null
+++ b/cluster/directory/base_directory.go
@@ -0,0 +1,53 @@
+// Copyright 2016-2019 hxmhlt
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package directory
+
+import (
+	"sync"
+)
+import (
+	"go.uber.org/atomic"
+)
+import (
+	"github.com/dubbo/go-for-apache-dubbo/common"
+)
+
+type BaseDirectory struct {
+	url       *common.URL
+	destroyed *atomic.Bool
+	mutex     sync.Mutex
+}
+
+func NewBaseDirectory(url *common.URL) BaseDirectory {
+	return BaseDirectory{
+		url:       url,
+		destroyed: atomic.NewBool(false),
+	}
+}
+func (dir *BaseDirectory) GetUrl() common.URL {
+	return *dir.url
+}
+
+func (dir *BaseDirectory) Destroy(doDestroy func()) {
+	if dir.destroyed.CAS(false, true) {
+		dir.mutex.Lock()
+		doDestroy()
+		dir.mutex.Unlock()
+	}
+}
+
+func (dir *BaseDirectory) IsAvailable() bool {
+	return !dir.destroyed.Load()
+}
diff --git a/cluster/directory/static_directory.go b/cluster/directory/static_directory.go
new file mode 100644
index 0000000000000000000000000000000000000000..3ac567b00e37fae3923753a7577121e7a341ae1f
--- /dev/null
+++ b/cluster/directory/static_directory.go
@@ -0,0 +1,64 @@
+// Copyright 2016-2019 hxmhlt
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package directory
+
+import (
+	"github.com/dubbo/go-for-apache-dubbo/common"
+	"github.com/dubbo/go-for-apache-dubbo/protocol"
+)
+
+type staticDirectory struct {
+	BaseDirectory
+	invokers []protocol.Invoker
+}
+
+func NewStaticDirectory(invokers []protocol.Invoker) *staticDirectory {
+	var url common.URL
+
+	if len(invokers) > 0 {
+		url = invokers[0].GetUrl()
+	}
+	return &staticDirectory{
+		BaseDirectory: NewBaseDirectory(&url),
+		invokers:      invokers,
+	}
+}
+
+//for-loop invokers ,if all invokers is available ,then it means directory is available
+func (dir *staticDirectory) IsAvailable() bool {
+	if len(dir.invokers) == 0 {
+		return false
+	}
+	for _, invoker := range dir.invokers {
+		if !invoker.IsAvailable() {
+			return false
+		}
+	}
+	return true
+}
+
+func (dir *staticDirectory) List(invocation protocol.Invocation) []protocol.Invoker {
+	//TODO:Here should add router
+	return dir.invokers
+}
+
+func (dir *staticDirectory) Destroy() {
+	dir.BaseDirectory.Destroy(func() {
+		for _, ivk := range dir.invokers {
+			ivk.Destroy()
+		}
+		dir.invokers = []protocol.Invoker{}
+	})
+}
diff --git a/cluster/directory/static_directory_test.go b/cluster/directory/static_directory_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..929d23edefd3821ae8f6c48a3706f6036b9238d8
--- /dev/null
+++ b/cluster/directory/static_directory_test.go
@@ -0,0 +1,55 @@
+// Copyright 2016-2019 hxmhlt
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package directory
+
+import (
+	"context"
+	"fmt"
+	"testing"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+import (
+	"github.com/dubbo/go-for-apache-dubbo/common"
+	"github.com/dubbo/go-for-apache-dubbo/protocol"
+	"github.com/dubbo/go-for-apache-dubbo/protocol/invocation"
+)
+
+func Test_StaticDirList(t *testing.T) {
+	invokers := []protocol.Invoker{}
+	for i := 0; i < 10; i++ {
+		url, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i))
+		invokers = append(invokers, protocol.NewBaseInvoker(url))
+	}
+
+	staticDir := NewStaticDirectory(invokers)
+	assert.Len(t, staticDir.List(&invocation.RPCInvocation{}), 10)
+}
+
+func Test_StaticDirDestroy(t *testing.T) {
+	invokers := []protocol.Invoker{}
+	for i := 0; i < 10; i++ {
+		url, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i))
+		invokers = append(invokers, protocol.NewBaseInvoker(url))
+	}
+
+	staticDir := NewStaticDirectory(invokers)
+	assert.Equal(t, true, staticDir.IsAvailable())
+	staticDir.Destroy()
+	assert.Equal(t, false, staticDir.IsAvailable())
+}
diff --git a/cluster/loadbalance.go b/cluster/loadbalance.go
new file mode 100644
index 0000000000000000000000000000000000000000..897c7952f63047eb2a2237a0cd6baba3c6cb2dc2
--- /dev/null
+++ b/cluster/loadbalance.go
@@ -0,0 +1,24 @@
+// Copyright 2016-2019 hxmhlt
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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
+
+import (
+	"github.com/dubbo/go-for-apache-dubbo/protocol"
+)
+
+// Extension - LoadBalance
+type LoadBalance interface {
+	Select([]protocol.Invoker, protocol.Invocation) protocol.Invoker
+}
diff --git a/cluster/loadbalance/random.go b/cluster/loadbalance/random.go
new file mode 100644
index 0000000000000000000000000000000000000000..992a8dfc35be600d29663aebf59e665a681025e3
--- /dev/null
+++ b/cluster/loadbalance/random.go
@@ -0,0 +1,75 @@
+// Copyright 2016-2019 hxmhlt
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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 (
+	"math/rand"
+)
+
+import (
+	"github.com/dubbo/go-for-apache-dubbo/cluster"
+	"github.com/dubbo/go-for-apache-dubbo/common/extension"
+	"github.com/dubbo/go-for-apache-dubbo/protocol"
+)
+
+const name = "random"
+
+func init() {
+	extension.SetLoadbalance(name, NewRandomLoadBalance)
+}
+
+type randomLoadBalance struct {
+}
+
+func NewRandomLoadBalance() cluster.LoadBalance {
+	return &randomLoadBalance{}
+}
+
+func (lb *randomLoadBalance) Select(invokers []protocol.Invoker, invocation protocol.Invocation) protocol.Invoker {
+	var length int
+	if length = len(invokers); length == 1 {
+		return invokers[0]
+	}
+	sameWeight := true
+	weights := make([]int64, length)
+
+	firstWeight := GetWeight(invokers[0], invocation)
+	totalWeight := firstWeight
+	weights[0] = firstWeight
+
+	for i := 1; i < length; i++ {
+		weight := GetWeight(invokers[i], invocation)
+		weights[i] = weight
+
+		totalWeight += weight
+		if sameWeight && weight != firstWeight {
+			sameWeight = false
+		}
+	}
+
+	if totalWeight > 0 && !sameWeight {
+		// If (not every invoker has the same weight & at least one invoker's weight>0), select randomly based on totalWeight.
+		offset := rand.Int63n(totalWeight)
+
+		for i := 0; i < length; i++ {
+			offset -= weights[i]
+			if offset < 0 {
+				return invokers[i]
+			}
+		}
+	}
+	// If all invokers have the same weight value or totalWeight=0, return evenly.
+	return invokers[rand.Intn(length)]
+}
diff --git a/cluster/loadbalance/random_test.go b/cluster/loadbalance/random_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..5b94f6b27218cf7a7b8000ecd2c2c1121b5a8ffd
--- /dev/null
+++ b/cluster/loadbalance/random_test.go
@@ -0,0 +1,114 @@
+// Copyright 2016-2019 hxmhlt
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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"
+	"fmt"
+	"net/url"
+	"strconv"
+	"testing"
+	"time"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+import (
+	"github.com/dubbo/go-for-apache-dubbo/common"
+	"github.com/dubbo/go-for-apache-dubbo/common/constant"
+	"github.com/dubbo/go-for-apache-dubbo/protocol"
+	"github.com/dubbo/go-for-apache-dubbo/protocol/invocation"
+)
+
+func Test_RandomlbSelect(t *testing.T) {
+	randomlb := NewRandomLoadBalance()
+
+	invokers := []protocol.Invoker{}
+
+	url, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", 0))
+	invokers = append(invokers, protocol.NewBaseInvoker(url))
+	i := randomlb.Select(invokers, &invocation.RPCInvocation{})
+	assert.True(t, i.GetUrl().URLEqual(url))
+
+	for i := 1; i < 10; i++ {
+		url, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i))
+		invokers = append(invokers, protocol.NewBaseInvoker(url))
+	}
+	randomlb.Select(invokers, &invocation.RPCInvocation{})
+}
+
+func Test_RandomlbSelectWeight(t *testing.T) {
+	randomlb := NewRandomLoadBalance()
+
+	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))
+		invokers = append(invokers, protocol.NewBaseInvoker(url))
+	}
+
+	urlParams := url.Values{}
+	urlParams.Set("methods.test."+constant.WEIGHT_KEY, "10000000000000")
+	urll, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://192.168.1.100:20000/com.ikurento.user.UserProvider"), common.WithParams(urlParams))
+	invokers = append(invokers, protocol.NewBaseInvoker(urll))
+	ivc := &invocation.RPCInvocation{}
+	ivc.SetMethod("test")
+
+	var selectedInvoker []protocol.Invoker
+	var selected float64
+	for i := 0; i < 10000; i++ {
+		s := randomlb.Select(invokers, ivc)
+		if s.GetUrl().Ip == "192.168.1.100" {
+			selected++
+		}
+		selectedInvoker = append(selectedInvoker, s)
+	}
+
+	assert.Condition(t, func() bool {
+		//really is 0.9999999999999
+		return selected/10000 > 0.9
+	})
+}
+
+func Test_RandomlbSelectWarmup(t *testing.T) {
+	randomlb := NewRandomLoadBalance()
+
+	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))
+		invokers = append(invokers, protocol.NewBaseInvoker(url))
+	}
+
+	urlParams := url.Values{}
+	urlParams.Set(constant.REMOTE_TIMESTAMP_KEY, strconv.FormatInt(time.Now().Add(time.Minute*(-9)).Unix(), 10))
+	urll, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://192.168.1.100:20000/com.ikurento.user.UserProvider"), common.WithParams(urlParams))
+	invokers = append(invokers, protocol.NewBaseInvoker(urll))
+	ivc := &invocation.RPCInvocation{}
+	ivc.SetMethod("test")
+
+	var selectedInvoker []protocol.Invoker
+	var selected float64
+	for i := 0; i < 10000; i++ {
+		s := randomlb.Select(invokers, ivc)
+		if s.GetUrl().Ip == "192.168.1.100" {
+			selected++
+		}
+		selectedInvoker = append(selectedInvoker, s)
+	}
+	assert.Condition(t, func() bool {
+		return selected/10000 < 0.1
+	})
+}
diff --git a/cluster/loadbalance/util.go b/cluster/loadbalance/util.go
new file mode 100644
index 0000000000000000000000000000000000000000..fdbeb14dd31fa8b2581f46d3adacf5355b12878b
--- /dev/null
+++ b/cluster/loadbalance/util.go
@@ -0,0 +1,44 @@
+// Copyright 2016-2019 hxmhlt
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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 (
+	"time"
+)
+import (
+	"github.com/dubbo/go-for-apache-dubbo/common/constant"
+	"github.com/dubbo/go-for-apache-dubbo/protocol"
+)
+
+func GetWeight(invoker protocol.Invoker, invocation protocol.Invocation) int64 {
+	url := invoker.GetUrl()
+	weight := url.GetMethodParamInt(invocation.MethodName(), constant.WEIGHT_KEY, constant.DEFAULT_WEIGHT)
+	if weight > 0 {
+		//get service register time an do warm up time
+		now := time.Now().Unix()
+		timestamp := url.GetParamInt(constant.REMOTE_TIMESTAMP_KEY, now)
+		if uptime := now - timestamp; uptime > 0 {
+			warmup := url.GetParamInt(constant.WARMUP_KEY, constant.DEFAULT_WARMUP)
+			if uptime < warmup {
+				if ww := float64(uptime) / float64(warmup) / float64(weight); ww < 1 {
+					weight = 1
+				} else if int64(ww) <= weight {
+					weight = int64(ww)
+				}
+			}
+		}
+	}
+	return weight
+}
diff --git a/cluster/router.go b/cluster/router.go
new file mode 100644
index 0000000000000000000000000000000000000000..a80efaa0b99ad51c1ad5a83b5cff6d3996d2b220
--- /dev/null
+++ b/cluster/router.go
@@ -0,0 +1,38 @@
+// Copyright 2016-2019 hxmhlt
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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
+
+import (
+	"github.com/dubbo/go-for-apache-dubbo/common"
+	"github.com/dubbo/go-for-apache-dubbo/protocol"
+)
+
+// Extension - Router
+
+type RouterFactory interface {
+	Router(common.URL) Router
+}
+
+type Router interface {
+	Route([]protocol.Invoker, common.URL, protocol.Invocation) []protocol.Invoker
+}
+
+type RouterChain struct {
+	routers []Router
+}
+
+func NewRouterChain(url common.URL) {
+
+}
diff --git a/cluster/router/.gitkeep b/cluster/router/.gitkeep
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/common/constant/default.go b/common/constant/default.go
new file mode 100644
index 0000000000000000000000000000000000000000..3ee4b1b1a5d607cdd4a6663366f1e6f20f82d0a6
--- /dev/null
+++ b/common/constant/default.go
@@ -0,0 +1,36 @@
+// Copyright 2016-2019 Yincheng Fang, hxmhlt
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package constant
+
+const (
+	DEFAULT_WEIGHT = 100     //
+	DEFAULT_WARMUP = 10 * 60 // in java here is 10*60*1000 because of System.currentTimeMillis() is measured in milliseconds & in go time.Unix() is second
+)
+
+const (
+	DEFAULT_LOADBALANCE = "random"
+	DEFAULT_RETRIES     = 2
+	DEFAULT_PROTOCOL    = "dubbo"
+	DEFAULT_VERSION     = ""
+	DEFAULT_REG_TIMEOUT = "10s"
+	DEFAULT_CLUSTER     = "failover"
+)
+
+const (
+	DEFAULT_KEY               = "default"
+	DEFAULT_SERVICE_FILTERS   = "echo"
+	DEFAULT_REFERENCE_FILTERS = ""
+	ECHO                      = "$echo"
+)
diff --git a/common/constant/env.go b/common/constant/env.go
new file mode 100644
index 0000000000000000000000000000000000000000..23fd9880afc932a99a411b099582459dfe1f7d16
--- /dev/null
+++ b/common/constant/env.go
@@ -0,0 +1,21 @@
+// Copyright 2016-2019 Yincheng Fang
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package constant
+
+const (
+	CONF_CONSUMER_FILE_PATH        = "CONF_CONSUMER_FILE_PATH"
+	CONF_PROVIDER_FILE_PATH        = "CONF_PROVIDER_FILE_PATH"
+	APP_LOG_CONF_FILE       string = "APP_LOG_CONF_FILE"
+)
diff --git a/common/constant/key.go b/common/constant/key.go
new file mode 100644
index 0000000000000000000000000000000000000000..5352c00f67f9d4fa7a6ce54e8c424ccd2cabd3d8
--- /dev/null
+++ b/common/constant/key.go
@@ -0,0 +1,66 @@
+// Copyright 2016-2019 hxmhlt, Yincheg Fang
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package constant
+
+const (
+	ASYNC_KEY = "async" // it's value should be "true" or "false" of string type
+)
+
+const (
+	GROUP_KEY     = "group"
+	VERSION_KEY   = "version"
+	INTERFACE_KEY = "interface"
+	PATH_KEY      = "path"
+	SERVICE_KEY   = "service"
+	METHODS_KEY   = "methods"
+	TIMEOUT_KEY   = "timeout"
+)
+
+const (
+	SERVICE_FILTER_KEY   = "service.filter"
+	REFERENCE_FILTER_KEY = "reference.filter"
+)
+
+const (
+	TIMESTAMP_KEY        = "timestamp"
+	REMOTE_TIMESTAMP_KEY = "remote.timestamp"
+	CLUSTER_KEY          = "cluster"
+	LOADBALANCE_KEY      = "loadbalance"
+	WEIGHT_KEY           = "weight"
+	WARMUP_KEY           = "warmup"
+	RETRIES_KEY          = "retries"
+)
+
+const (
+	DUBBOGO_CTX_KEY = "dubbogo-ctx"
+)
+
+const (
+	REGISTRY_KEY         = "registry"
+	REGISTRY_PROTOCOL    = "registry"
+	ROLE_KEY             = "registry.role"
+	REGISTRY_DEFAULT_KEY = "registry.default"
+	REGISTRY_TIMEOUT_KEY = "registry.timeout"
+)
+
+const (
+	APPLICATION_KEY  = "application"
+	ORGANIZATION_KEY = "organization"
+	NAME_KEY         = "name"
+	MODULE_KEY       = "module"
+	APP_VERSION_KEY  = "app.version"
+	OWNER_KEY        = "owner"
+	ENVIRONMENT_KEY  = "environment"
+)
diff --git a/common/extension/cluster.go b/common/extension/cluster.go
new file mode 100644
index 0000000000000000000000000000000000000000..54b5ac59d31950c7ac42d81856f11c28f4716a19
--- /dev/null
+++ b/common/extension/cluster.go
@@ -0,0 +1,34 @@
+// Copyright 2016-2019 hxmhlt
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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/dubbo/go-for-apache-dubbo/cluster"
+)
+
+var (
+	clusters = make(map[string]func() cluster.Cluster)
+)
+
+func SetCluster(name string, fcn func() cluster.Cluster) {
+	clusters[name] = fcn
+}
+
+func GetCluster(name string) cluster.Cluster {
+	if clusters[name] == nil {
+		panic("cluster for " + name + " is not existing, make sure you have import the package.")
+	}
+	return clusters[name]()
+}
diff --git a/common/extension/filter.go b/common/extension/filter.go
new file mode 100644
index 0000000000000000000000000000000000000000..263b67eae4023bdca49af4f1197af7f19ba3dab0
--- /dev/null
+++ b/common/extension/filter.go
@@ -0,0 +1,34 @@
+// Copyright 2016-2019 Yincheng Fang
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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/dubbo/go-for-apache-dubbo/filter"
+)
+
+var (
+	filters = make(map[string]func() filter.Filter)
+)
+
+func SetFilter(name string, v func() filter.Filter) {
+	filters[name] = v
+}
+
+func GetFilter(name string) filter.Filter {
+	if filters[name] == nil {
+		panic("filter for " + name + " is not existing, make sure you have import the package.")
+	}
+	return filters[name]()
+}
diff --git a/common/extension/loadbalance.go b/common/extension/loadbalance.go
new file mode 100644
index 0000000000000000000000000000000000000000..bb46c8283386351bfb8f789bedabd0fbd5e7b43d
--- /dev/null
+++ b/common/extension/loadbalance.go
@@ -0,0 +1,32 @@
+// Copyright 2016-2019 hxmhlt
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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/dubbo/go-for-apache-dubbo/cluster"
+
+var (
+	loadbalances = make(map[string]func() cluster.LoadBalance)
+)
+
+func SetLoadbalance(name string, fcn func() cluster.LoadBalance) {
+	loadbalances[name] = fcn
+}
+
+func GetLoadbalance(name string) cluster.LoadBalance {
+	if loadbalances[name] == nil {
+		panic("loadbalance for " + name + " is not existing, make sure you have import the package.")
+	}
+	return loadbalances[name]()
+}
diff --git a/common/extension/protocol.go b/common/extension/protocol.go
new file mode 100644
index 0000000000000000000000000000000000000000..f1ddbdeef8bb3de0207befaf3ec7063571723513
--- /dev/null
+++ b/common/extension/protocol.go
@@ -0,0 +1,34 @@
+// Copyright 2016-2019 Yincheng Fang
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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/dubbo/go-for-apache-dubbo/protocol"
+)
+
+var (
+	protocols = make(map[string]func() protocol.Protocol)
+)
+
+func SetProtocol(name string, v func() protocol.Protocol) {
+	protocols[name] = v
+}
+
+func GetProtocol(name string) protocol.Protocol {
+	if protocols[name] == nil {
+		panic("protocol for " + name + " is not existing, make sure you have import the package.")
+	}
+	return protocols[name]()
+}
diff --git a/common/extension/proxy_factory.go b/common/extension/proxy_factory.go
new file mode 100644
index 0000000000000000000000000000000000000000..8795ea8f975237c4d263053fb67052d5b98cff84
--- /dev/null
+++ b/common/extension/proxy_factory.go
@@ -0,0 +1,20 @@
+package extension
+
+import "github.com/dubbo/go-for-apache-dubbo/common/proxy"
+
+var (
+	proxy_factories = make(map[string]func(...proxy.Option) proxy.ProxyFactory)
+)
+
+func SetProxyFactory(name string, f func(...proxy.Option) proxy.ProxyFactory) {
+	proxy_factories[name] = f
+}
+func GetProxyFactory(name string) proxy.ProxyFactory {
+	if name == "" {
+		name = "default"
+	}
+	if proxy_factories[name] == nil {
+		panic("proxy factory for " + name + " is not existing, make sure you have import the package.")
+	}
+	return proxy_factories[name]()
+}
diff --git a/common/extension/registry.go b/common/extension/registry.go
new file mode 100644
index 0000000000000000000000000000000000000000..d6cacd88028bf96697e11525098454d741784351
--- /dev/null
+++ b/common/extension/registry.go
@@ -0,0 +1,36 @@
+// Copyright 2016-2019 hxmhlt
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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/dubbo/go-for-apache-dubbo/common"
+	"github.com/dubbo/go-for-apache-dubbo/registry"
+)
+
+var (
+	registrys = make(map[string]func(config *common.URL) (registry.Registry, error))
+)
+
+func SetRegistry(name string, v func(config *common.URL) (registry.Registry, error)) {
+	registrys[name] = v
+}
+
+func GetRegistry(name string, config *common.URL) (registry.Registry, error) {
+	if registrys[name] == nil {
+		panic("registry for " + name + " is not existing, make sure you have import the package.")
+	}
+	return registrys[name](config)
+
+}
diff --git a/common/logger/log.yml b/common/logger/log.yml
new file mode 100644
index 0000000000000000000000000000000000000000..427308d52b028d1740dac56b66b2e54fa76c6fe2
--- /dev/null
+++ b/common/logger/log.yml
@@ -0,0 +1,28 @@
+
+level: "debug"
+development: true
+disableCaller: false
+disableStacktrace: false
+sampling:
+encoding: "console"
+
+# encoder
+encoderConfig:
+  messageKey: "message"
+  levelKey: "level"
+  timeKey: "time"
+  nameKey: "logger"
+  callerKey: "caller"
+  stacktraceKey: "stacktrace"
+  lineEnding: ""
+  levelEncoder: "capitalColor"
+  timeEncoder: "iso8601"
+  durationEncoder: "seconds"
+  callerEncoder: "short"
+  nameEncoder: ""
+
+outputPaths:
+- "stderr"
+errorOutputPaths:
+- "stderr"
+initialFields:
diff --git a/common/logger/logger.go b/common/logger/logger.go
new file mode 100644
index 0000000000000000000000000000000000000000..22fe7329e7601b9e3701ad51c9da2eaa937a7d27
--- /dev/null
+++ b/common/logger/logger.go
@@ -0,0 +1,117 @@
+// Copyright 2016-2019 Yincheng Fang
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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 logger
+
+import (
+	"io/ioutil"
+	"log"
+	"os"
+	"path"
+)
+
+import (
+	"github.com/dubbogo/getty"
+	perrors "github.com/pkg/errors"
+	"go.uber.org/zap"
+	"go.uber.org/zap/zapcore"
+	"gopkg.in/yaml.v2"
+)
+
+import (
+	"github.com/dubbo/go-for-apache-dubbo/common/constant"
+)
+
+var (
+	logger Logger
+)
+
+type Logger interface {
+	Info(args ...interface{})
+	Warn(args ...interface{})
+	Error(args ...interface{})
+	Debug(args ...interface{})
+
+	Infof(fmt string, args ...interface{})
+	Warnf(fmt string, args ...interface{})
+	Errorf(fmt string, args ...interface{})
+	Debugf(fmt string, args ...interface{})
+}
+
+func init() {
+	logConfFile := os.Getenv(constant.APP_LOG_CONF_FILE)
+	err := InitLog(logConfFile)
+	if err != nil {
+		log.Printf("[InitLog] error: %v", err)
+	}
+}
+
+func InitLog(logConfFile string) error {
+	if logConfFile == "" {
+		InitLogger(nil)
+		return perrors.New("log configure file name is nil")
+	}
+	if path.Ext(logConfFile) != ".yml" {
+		InitLogger(nil)
+		return perrors.Errorf("log configure file name{%s} suffix must be .yml", logConfFile)
+	}
+
+	confFileStream, err := ioutil.ReadFile(logConfFile)
+	if err != nil {
+		InitLogger(nil)
+		return perrors.Errorf("ioutil.ReadFile(file:%s) = error:%v", logConfFile, err)
+	}
+
+	conf := &zap.Config{}
+	err = yaml.Unmarshal(confFileStream, conf)
+	if err != nil {
+		InitLogger(nil)
+		return perrors.Errorf("[Unmarshal]init logger error: %v", err)
+	}
+
+	InitLogger(conf)
+
+	// set getty log
+	getty.SetLogger(logger)
+	return nil
+}
+
+func InitLogger(conf *zap.Config) {
+	var zapLoggerConfig zap.Config
+	if conf == nil {
+		zapLoggerConfig = zap.NewDevelopmentConfig()
+		zapLoggerEncoderConfig := zapcore.EncoderConfig{
+			TimeKey:        "time",
+			LevelKey:       "level",
+			NameKey:        "logger",
+			CallerKey:      "caller",
+			MessageKey:     "message",
+			StacktraceKey:  "stacktrace",
+			EncodeLevel:    zapcore.CapitalColorLevelEncoder,
+			EncodeTime:     zapcore.ISO8601TimeEncoder,
+			EncodeDuration: zapcore.SecondsDurationEncoder,
+			EncodeCaller:   zapcore.ShortCallerEncoder,
+		}
+		zapLoggerConfig.EncoderConfig = zapLoggerEncoderConfig
+	} else {
+		zapLoggerConfig = *conf
+	}
+	zapLogger, _ := zapLoggerConfig.Build()
+	logger = zapLogger.Sugar()
+}
+
+func SetLogger(log Logger) {
+	logger = log
+	getty.SetLogger(logger)
+}
diff --git a/common/logger/logger_test.go b/common/logger/logger_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..9256aa417ae44d1ccdcc82a90abafebdf3581e79
--- /dev/null
+++ b/common/logger/logger_test.go
@@ -0,0 +1,39 @@
+package logger
+
+import (
+	"github.com/stretchr/testify/assert"
+	"path/filepath"
+	"testing"
+)
+
+func TestInitLog(t *testing.T) {
+	var (
+		err  error
+		path string
+	)
+
+	err = InitLog("")
+	assert.EqualError(t, err, "log configure file name is nil")
+
+	path, err = filepath.Abs("./log.xml")
+	assert.NoError(t, err)
+	err = InitLog(path)
+	assert.EqualError(t, err, "log configure file name{"+path+"} suffix must be .yml")
+
+	path, err = filepath.Abs("./logger.yml")
+	assert.NoError(t, err)
+	err = InitLog(path)
+	assert.EqualError(t, err, "ioutil.ReadFile(file:"+path+") = error:open "+path+": no such file or directory")
+
+	err = InitLog("./log.yml")
+	assert.NoError(t, err)
+
+	Debug("debug")
+	Info("info")
+	Warn("warn")
+	Error("error")
+	Debugf("%s", "debug")
+	Infof("%s", "info")
+	Warnf("%s", "warn")
+	Errorf("%s", "error")
+}
diff --git a/common/logger/logging.go b/common/logger/logging.go
new file mode 100644
index 0000000000000000000000000000000000000000..57a0e04e42252d343391cbfe04fd4edef92da3f0
--- /dev/null
+++ b/common/logger/logging.go
@@ -0,0 +1,40 @@
+// Copyright 2016-2019 Yincheng Fang
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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 logger
+
+func Info(args ...interface{}) {
+	logger.Info(args...)
+}
+func Warn(args ...interface{}) {
+	logger.Warn(args...)
+}
+func Error(args ...interface{}) {
+	logger.Error(args...)
+}
+func Debug(args ...interface{}) {
+	logger.Debug(args...)
+}
+func Infof(fmt string, args ...interface{}) {
+	logger.Infof(fmt, args...)
+}
+func Warnf(fmt string, args ...interface{}) {
+	logger.Warnf(fmt, args...)
+}
+func Errorf(fmt string, args ...interface{}) {
+	logger.Errorf(fmt, args...)
+}
+func Debugf(fmt string, args ...interface{}) {
+	logger.Debugf(fmt, args...)
+}
diff --git a/common/node.go b/common/node.go
new file mode 100644
index 0000000000000000000000000000000000000000..fb409e5cee177f2b927afd84bda7b333549511eb
--- /dev/null
+++ b/common/node.go
@@ -0,0 +1,21 @@
+// Copyright 2016-2019 hxmhlt
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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
+
+type Node interface {
+	GetUrl() URL
+	IsAvailable() bool
+	Destroy()
+}
diff --git a/common/proxy/proxy.go b/common/proxy/proxy.go
new file mode 100644
index 0000000000000000000000000000000000000000..033c8687ee25d4ded826b9a6a1315608b7b8c4ab
--- /dev/null
+++ b/common/proxy/proxy.go
@@ -0,0 +1,172 @@
+// Copyright 2016-2019 Yincheng Fang
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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 proxy
+
+import (
+	"reflect"
+)
+
+import (
+	"github.com/dubbo/go-for-apache-dubbo/common"
+	"github.com/dubbo/go-for-apache-dubbo/common/logger"
+	"github.com/dubbo/go-for-apache-dubbo/protocol"
+	invocation_impl "github.com/dubbo/go-for-apache-dubbo/protocol/invocation"
+)
+
+// Proxy struct
+type Proxy struct {
+	rpc         common.RPCService
+	invoke      protocol.Invoker
+	callBack    interface{}
+	attachments map[string]string
+}
+
+var typError = reflect.Zero(reflect.TypeOf((*error)(nil)).Elem()).Type()
+
+func NewProxy(invoke protocol.Invoker, callBack interface{}, attachments map[string]string) *Proxy {
+	return &Proxy{
+		invoke:      invoke,
+		callBack:    callBack,
+		attachments: attachments,
+	}
+}
+
+// proxy implement
+// In consumer, RPCService like:
+// 		type XxxProvider struct {
+//  		Yyy func(ctx context.Context, args []interface{}, rsp *Zzz) error
+// 		}
+func (p *Proxy) Implement(v common.RPCService) {
+
+	// check parameters, incoming interface must be a elem's pointer.
+	valueOf := reflect.ValueOf(v)
+	logger.Debugf("[Implement] reflect.TypeOf: %s", valueOf.String())
+
+	valueOfElem := valueOf.Elem()
+	typeOf := valueOfElem.Type()
+
+	// check incoming interface, incoming interface's elem must be a struct.
+	if typeOf.Kind() != reflect.Struct {
+		logger.Errorf("%s must be a struct ptr", valueOf.String())
+		return
+	}
+
+	makeDubboCallProxy := func(methodName string, outs []reflect.Type) func(in []reflect.Value) []reflect.Value {
+		return func(in []reflect.Value) []reflect.Value {
+			var (
+				err   error
+				inv   *invocation_impl.RPCInvocation
+				inArr []interface{}
+				reply reflect.Value
+			)
+			if methodName == "Echo" {
+				methodName = "$echo"
+			}
+
+			start := 0
+			end := len(in)
+			if in[0].Type().String() == "context.Context" {
+				start += 1
+			}
+			if len(outs) == 1 {
+				end -= 1
+				reply = in[len(in)-1]
+			} else {
+				if outs[0].Kind() == reflect.Ptr {
+					reply = reflect.New(outs[0].Elem())
+				} else {
+					reply = reflect.New(outs[0])
+				}
+			}
+
+			if v, ok := in[start].Interface().([]interface{}); ok && end-start == 1 {
+				inArr = v
+			} else {
+				inArr = make([]interface{}, end-start)
+				index := 0
+				for i := start; i < end; i++ {
+					inArr[index] = in[i].Interface()
+					index++
+				}
+			}
+
+			inv = invocation_impl.NewRPCInvocationForConsumer(methodName, nil, inArr, reply.Interface(), p.callBack, common.URL{}, nil)
+
+			for k, value := range p.attachments {
+				inv.SetAttachments(k, value)
+			}
+
+			result := p.invoke.Invoke(inv)
+
+			err = result.Error()
+			logger.Infof("[makeDubboCallProxy] result: %v, err: %v", result.Result(), err)
+			if len(outs) == 1 {
+				return []reflect.Value{reflect.ValueOf(&err).Elem()}
+			}
+			if len(outs) == 2 && outs[0].Kind() != reflect.Ptr {
+				return []reflect.Value{reply.Elem(), reflect.ValueOf(&err).Elem()}
+			}
+			return []reflect.Value{reply, reflect.ValueOf(&err).Elem()}
+		}
+	}
+
+	numField := valueOfElem.NumField()
+	for i := 0; i < numField; i++ {
+		t := typeOf.Field(i)
+		methodName := t.Tag.Get("dubbo")
+		if methodName == "" {
+			methodName = t.Name
+		}
+		f := valueOfElem.Field(i)
+		if f.Kind() == reflect.Func && f.IsValid() && f.CanSet() {
+			inNum := t.Type.NumIn()
+			outNum := t.Type.NumOut()
+
+			if outNum != 1 && outNum != 2 {
+				logger.Warnf("method %s of mtype %v has wrong number of in out parameters %d; needs exactly 1/2",
+					t.Name, t.Type.String(), outNum)
+				continue
+			}
+
+			// The latest return type of the method must be error.
+			if returnType := t.Type.Out(outNum - 1); returnType != typError {
+				logger.Warnf("the latest return type %s of method %q is not error", returnType, t.Name)
+				continue
+			}
+
+			// reply must be Ptr when outNum == 1
+			if outNum == 1 && t.Type.In(inNum-1).Kind() != reflect.Ptr {
+				logger.Warnf("reply type of method %q is not a pointer", t.Name)
+				continue
+			}
+
+			var funcOuts = make([]reflect.Type, outNum)
+			for i := 0; i < outNum; i++ {
+				funcOuts[i] = t.Type.Out(i)
+			}
+
+			// do method proxy here:
+			f.Set(reflect.MakeFunc(f.Type(), makeDubboCallProxy(methodName, funcOuts)))
+			logger.Debugf("set method [%s]", methodName)
+		}
+	}
+
+	p.rpc = v
+
+}
+
+func (p *Proxy) Get() common.RPCService {
+	return p.rpc
+}
diff --git a/common/proxy/proxy_factory.go b/common/proxy/proxy_factory.go
new file mode 100644
index 0000000000000000000000000000000000000000..4d7fa52b7451b0c014d2ffdc3f0383acb6c777d4
--- /dev/null
+++ b/common/proxy/proxy_factory.go
@@ -0,0 +1,26 @@
+// Copyright 2016-2019 hxmhlt
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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 proxy
+
+import (
+	"github.com/dubbo/go-for-apache-dubbo/common"
+	"github.com/dubbo/go-for-apache-dubbo/protocol"
+)
+
+type ProxyFactory interface {
+	GetProxy(invoker protocol.Invoker, url *common.URL) *Proxy
+	GetInvoker(url common.URL) protocol.Invoker
+}
+
+type Option func(ProxyFactory)
diff --git a/common/proxy/proxy_factory/default.go b/common/proxy/proxy_factory/default.go
new file mode 100644
index 0000000000000000000000000000000000000000..54ba45392bea7bcbb578178845ca781dbffe3cb5
--- /dev/null
+++ b/common/proxy/proxy_factory/default.go
@@ -0,0 +1,52 @@
+// Copyright 2016-2019 hxmhlt
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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 proxy_factory
+
+import (
+	"github.com/dubbo/go-for-apache-dubbo/common"
+	"github.com/dubbo/go-for-apache-dubbo/common/constant"
+	"github.com/dubbo/go-for-apache-dubbo/common/extension"
+	"github.com/dubbo/go-for-apache-dubbo/common/proxy"
+	"github.com/dubbo/go-for-apache-dubbo/protocol"
+)
+
+func init() {
+	extension.SetProxyFactory("default", NewDefaultProxyFactory)
+}
+
+type DefaultProxyFactory struct {
+	//delegate ProxyFactory
+}
+
+//you can rewrite DefaultProxyFactory in extension and delegate the default proxy factory like below
+
+//func WithDelegate(delegateProxyFactory ProxyFactory) Option {
+//	return func(proxy ProxyFactory) {
+//		proxy.(*DefaultProxyFactory).delegate = delegateProxyFactory
+//	}
+//}
+
+func NewDefaultProxyFactory(options ...proxy.Option) proxy.ProxyFactory {
+	return &DefaultProxyFactory{}
+}
+func (factory *DefaultProxyFactory) GetProxy(invoker protocol.Invoker, 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)
+}
+func (factory *DefaultProxyFactory) GetInvoker(url common.URL) protocol.Invoker {
+	//TODO:yincheng need to do the service invoker refactor
+	return protocol.NewBaseInvoker(url)
+}
diff --git a/common/proxy/proxy_factory/default_test.go b/common/proxy/proxy_factory/default_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..385dfee636a5422ca81deee39b1195201c6edd3c
--- /dev/null
+++ b/common/proxy/proxy_factory/default_test.go
@@ -0,0 +1,35 @@
+// Copyright 2016-2019 hxmhlt
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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 proxy_factory
+
+import (
+	"github.com/dubbo/go-for-apache-dubbo/common"
+	"github.com/dubbo/go-for-apache-dubbo/protocol"
+	"github.com/stretchr/testify/assert"
+	"testing"
+)
+
+func Test_GetProxy(t *testing.T) {
+	proxyFactory := NewDefaultProxyFactory()
+	url := common.NewURLWithOptions("testservice")
+	proxy := proxyFactory.GetProxy(protocol.NewBaseInvoker(*url), url)
+	assert.NotNil(t, proxy)
+}
+
+func Test_GetInvoker(t *testing.T) {
+	proxyFactory := NewDefaultProxyFactory()
+	url := common.NewURLWithOptions("testservice")
+	invoker := proxyFactory.GetInvoker(*url)
+	assert.True(t, invoker.IsAvailable())
+}
diff --git a/common/proxy/proxy_test.go b/common/proxy/proxy_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..6a12af8a70552ec5348d75f9c57bec31670806ad
--- /dev/null
+++ b/common/proxy/proxy_test.go
@@ -0,0 +1,128 @@
+// Copyright 2016-2019 Yincheng Fang
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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 proxy
+
+import (
+	"context"
+	"reflect"
+	"testing"
+)
+
+import (
+	perrors "github.com/pkg/errors"
+	"github.com/stretchr/testify/assert"
+)
+
+import (
+	"github.com/dubbo/go-for-apache-dubbo/common"
+	"github.com/dubbo/go-for-apache-dubbo/common/constant"
+	"github.com/dubbo/go-for-apache-dubbo/protocol"
+)
+
+type TestService struct {
+	MethodOne   func(context.Context, int, bool, *interface{}) error
+	MethodTwo   func([]interface{}, *interface{}) error
+	MethodThree func(int, bool) (interface{}, error)
+	MethodFour  func(int, bool) (*interface{}, error) `dubbo:"methodFour"`
+	Echo        func(interface{}, *interface{}) error
+}
+
+func (s *TestService) Service() string {
+	return "com.test.Path"
+}
+func (s *TestService) Version() string {
+	return ""
+}
+
+type TestServiceInt int
+
+func (s *TestServiceInt) Service() string {
+	return "com.test.TestServiceInt"
+}
+func (s *TestServiceInt) Version() string {
+	return ""
+}
+
+func TestProxy_Implement(t *testing.T) {
+
+	invoker := protocol.NewBaseInvoker(common.URL{})
+	p := NewProxy(invoker, nil, map[string]string{constant.ASYNC_KEY: "false"})
+	s := &TestService{}
+	p.Implement(s)
+	err := p.Get().(*TestService).MethodOne(nil, 0, false, nil)
+	assert.NoError(t, err)
+	err = p.Get().(*TestService).MethodTwo(nil, nil)
+	assert.NoError(t, err)
+	ret, err := p.Get().(*TestService).MethodThree(0, false)
+	assert.NoError(t, err)
+	assert.Nil(t, ret) // ret is nil, because it doesn't be injection yet
+	ret2, err := p.Get().(*TestService).MethodFour(0, false)
+	assert.NoError(t, err)
+	assert.Equal(t, "*interface {}", reflect.TypeOf(ret2).String())
+	err = p.Get().(*TestService).Echo(nil, nil)
+	assert.NoError(t, err)
+
+	// inherit & lowercase
+	p.rpc = nil
+	type S1 struct {
+		TestService
+		methodOne func(context.Context, interface{}, *struct{}) error
+	}
+	s1 := &S1{TestService: *s, methodOne: func(i context.Context, i2 interface{}, i3 *struct{}) error {
+		return perrors.New("errors")
+	}}
+	p.Implement(s1)
+	err = s1.MethodOne(nil, 0, false, nil)
+	assert.NoError(t, err)
+	err = s1.methodOne(nil, nil, nil)
+	assert.EqualError(t, err, "errors")
+
+	// no struct
+	p.rpc = nil
+	it := TestServiceInt(1)
+	p.Implement(&it)
+	assert.Nil(t, p.rpc)
+
+	// return number
+	p.rpc = nil
+	type S2 struct {
+		TestService
+		MethodOne func([]interface{}) (*struct{}, int, error)
+	}
+	s2 := &S2{TestService: *s}
+	p.Implement(s2)
+	assert.Nil(t, s2.MethodOne)
+
+	// reply type
+	p.rpc = nil
+	type S3 struct {
+		TestService
+		MethodOne func(context.Context, []interface{}, struct{}) error
+	}
+	s3 := &S3{TestService: *s}
+	p.Implement(s3)
+	assert.Nil(t, s3.MethodOne)
+
+	// returns type
+	p.rpc = nil
+	type S4 struct {
+		TestService
+		MethodOne func(context.Context, []interface{}, *struct{}) interface{}
+	}
+	s4 := &S4{TestService: *s}
+	p.Implement(s4)
+	assert.Nil(t, s4.MethodOne)
+
+}
diff --git a/common/rpc_service.go b/common/rpc_service.go
new file mode 100644
index 0000000000000000000000000000000000000000..3a215ab17a7c156fda7d19be3b86255d8ef06138
--- /dev/null
+++ b/common/rpc_service.go
@@ -0,0 +1,287 @@
+// Copyright 2016-2019 Yincheng Fang
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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 (
+	"context"
+	"reflect"
+	"strings"
+	"sync"
+	"unicode"
+	"unicode/utf8"
+)
+
+import (
+	perrors "github.com/pkg/errors"
+)
+
+import (
+	"github.com/dubbo/go-for-apache-dubbo/common/logger"
+)
+
+// rpc service interface
+type RPCService interface {
+	Service() string // Path InterfaceName
+	Version() string
+}
+
+var (
+	// Precompute the reflect type for error. Can't use error directly
+	// because Typeof takes an empty interface value. This is annoying.
+	typeOfError = reflect.TypeOf((*error)(nil)).Elem()
+
+	// todo: lowerecas?
+	ServiceMap = &serviceMap{
+		serviceMap: make(map[string]map[string]*Service),
+	}
+)
+
+//////////////////////////
+// info of method
+//////////////////////////
+
+type MethodType struct {
+	method    reflect.Method
+	ctxType   reflect.Type   // request context
+	argsType  []reflect.Type // args except ctx, include replyType if existing
+	replyType reflect.Type   // return value, otherwise it is nil
+}
+
+func (m *MethodType) Method() reflect.Method {
+	return m.method
+}
+func (m *MethodType) CtxType() reflect.Type {
+	return m.ctxType
+}
+func (m *MethodType) ArgsType() []reflect.Type {
+	return m.argsType
+}
+func (m *MethodType) ReplyType() reflect.Type {
+	return m.replyType
+}
+func (m *MethodType) SuiteContext(ctx context.Context) reflect.Value {
+	if contextv := reflect.ValueOf(ctx); contextv.IsValid() {
+		return contextv
+	}
+	return reflect.Zero(m.ctxType)
+}
+
+//////////////////////////
+// info of service interface
+//////////////////////////
+
+type Service struct {
+	name     string
+	rcvr     reflect.Value
+	rcvrType reflect.Type
+	methods  map[string]*MethodType
+}
+
+func (s *Service) Method() map[string]*MethodType {
+	return s.methods
+}
+func (s *Service) RcvrType() reflect.Type {
+	return s.rcvrType
+}
+func (s *Service) Rcvr() reflect.Value {
+	return s.rcvr
+}
+
+//////////////////////////
+// serviceMap
+//////////////////////////
+
+type serviceMap struct {
+	mutex      sync.RWMutex                   // protects the serviceMap
+	serviceMap map[string]map[string]*Service // protocol -> service name -> service
+}
+
+func (sm *serviceMap) GetService(protocol, name string) *Service {
+	sm.mutex.RLock()
+	defer sm.mutex.RUnlock()
+	if s, ok := sm.serviceMap[protocol]; ok {
+		if srv, ok := s[name]; ok {
+			return srv
+		}
+		return nil
+	}
+	return nil
+}
+
+func (sm *serviceMap) Register(protocol string, rcvr RPCService) (string, error) {
+	if sm.serviceMap[protocol] == nil {
+		sm.serviceMap[protocol] = make(map[string]*Service)
+	}
+
+	s := new(Service)
+	s.rcvrType = reflect.TypeOf(rcvr)
+	s.rcvr = reflect.ValueOf(rcvr)
+	sname := reflect.Indirect(s.rcvr).Type().Name()
+	if sname == "" {
+		s := "no service name for type " + s.rcvrType.String()
+		logger.Errorf(s)
+		return "", perrors.New(s)
+	}
+	if !isExported(sname) {
+		s := "type " + sname + " is not exported"
+		logger.Errorf(s)
+		return "", perrors.New(s)
+	}
+
+	sname = rcvr.Service()
+	if server := sm.GetService(protocol, sname); server != nil {
+		return "", perrors.New("service already defined: " + sname)
+	}
+	s.name = sname
+	s.methods = make(map[string]*MethodType)
+
+	// Install the methods
+	methods := ""
+	methods, s.methods = suitableMethods(s.rcvrType)
+
+	if len(s.methods) == 0 {
+		s := "type " + sname + " has no exported methods of suitable type"
+		logger.Errorf(s)
+		return "", perrors.New(s)
+	}
+	sm.mutex.Lock()
+	sm.serviceMap[protocol][s.name] = s
+	sm.mutex.Unlock()
+
+	return strings.TrimSuffix(methods, ","), nil
+}
+
+func (sm *serviceMap) UnRegister(protocol, serviceName string) error {
+	if protocol == "" || serviceName == "" {
+		return perrors.New("protocol or serviceName is nil")
+	}
+	sm.mutex.RLock()
+	svcs, ok := sm.serviceMap[protocol]
+	if !ok {
+		sm.mutex.RUnlock()
+		return perrors.New("no services for " + protocol)
+	}
+	_, ok = svcs[serviceName]
+	if !ok {
+		sm.mutex.RUnlock()
+		return perrors.New("no service for " + serviceName)
+	}
+	sm.mutex.RUnlock()
+
+	sm.mutex.Lock()
+	defer sm.mutex.Unlock()
+	delete(svcs, serviceName)
+	delete(sm.serviceMap, protocol)
+
+	return nil
+}
+
+// Is this an exported - upper case - name
+func isExported(name string) bool {
+	rune, _ := utf8.DecodeRuneInString(name)
+	return unicode.IsUpper(rune)
+}
+
+// Is this type exported or a builtin?
+func isExportedOrBuiltinType(t reflect.Type) bool {
+	for t.Kind() == reflect.Ptr {
+		t = t.Elem()
+	}
+	// PkgPath will be non-empty even for an exported type,
+	// so we need to check the type name as well.
+	return isExported(t.Name()) || t.PkgPath() == ""
+}
+
+// suitableMethods returns suitable Rpc methods of typ
+func suitableMethods(typ reflect.Type) (string, map[string]*MethodType) {
+	methods := make(map[string]*MethodType)
+	mts := ""
+	logger.Debugf("[%s] NumMethod is %d", typ.String(), typ.NumMethod())
+	for m := 0; m < typ.NumMethod(); m++ {
+		method := typ.Method(m)
+		if mt := suiteMethod(method); mt != nil {
+			methods[method.Name] = mt
+			if m == 0 {
+				mts += method.Name
+			} else {
+				mts += "," + method.Name
+			}
+		}
+	}
+	return mts, methods
+}
+
+// suiteMethod returns a suitable Rpc methodType
+func suiteMethod(method reflect.Method) *MethodType {
+	mtype := method.Type
+	mname := method.Name
+	inNum := mtype.NumIn()
+	outNum := mtype.NumOut()
+
+	// Method must be exported.
+	if method.PkgPath != "" {
+		return nil
+	}
+
+	var (
+		replyType, ctxType reflect.Type
+		argsType           []reflect.Type
+	)
+
+	if outNum != 1 && outNum != 2 {
+		logger.Warnf("method %s of mtype %v has wrong number of in out parameters %d; needs exactly 1/2",
+			mname, mtype.String(), outNum)
+		return nil
+	}
+
+	// The latest return type of the method must be error.
+	if returnType := mtype.Out(outNum - 1); returnType != typeOfError {
+		logger.Warnf("the latest return type %s of method %q is not error", returnType, mname)
+		return nil
+	}
+
+	// replyType
+	if outNum == 1 {
+		if mtype.In(inNum-1).Kind() != reflect.Ptr {
+			logger.Errorf("reply type of method %q is not a pointer %v", mname, replyType)
+			return nil
+		}
+	} else {
+		replyType = mtype.Out(0)
+		if !isExportedOrBuiltinType(replyType) {
+			logger.Errorf("reply type of method %s not exported{%v}", mname, replyType)
+			return nil
+		}
+	}
+
+	index := 1
+
+	// ctxType
+	if mtype.In(1).String() == "context.Context" {
+		ctxType = mtype.In(1)
+		index = 2
+	}
+
+	for ; index < inNum; index++ {
+		argsType = append(argsType, mtype.In(index))
+		// need not be a pointer.
+		if !isExportedOrBuiltinType(mtype.In(index)) {
+			logger.Errorf("argument type of method %q is not exported %v", mname, mtype.In(index))
+			return nil
+		}
+	}
+
+	return &MethodType{method: method, argsType: argsType, replyType: replyType, ctxType: ctxType}
+}
diff --git a/common/rpc_service_test.go b/common/rpc_service_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..41476149c09d0d44140717076c7fc8c13152b348
--- /dev/null
+++ b/common/rpc_service_test.go
@@ -0,0 +1,195 @@
+// Copyright 2016-2019 Yincheng Fang
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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 (
+	"context"
+	"reflect"
+	"testing"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+type TestService struct {
+}
+
+func (s *TestService) MethodOne(ctx context.Context, args []interface{}, rsp *struct{}) error {
+	return nil
+}
+func (s *TestService) MethodTwo(args []interface{}) (struct{}, error) {
+	return struct{}{}, nil
+}
+func (s *TestService) Service() string {
+	return "com.test.Path"
+}
+func (s *TestService) Version() string {
+	return ""
+}
+
+type testService struct {
+}
+
+func (s *testService) Method1(ctx context.Context, args testService, rsp *struct{}) error {
+	return nil
+}
+func (s *testService) Method2(ctx context.Context, args []interface{}, rsp struct{}) error {
+	return nil
+}
+func (s *testService) Method3(ctx context.Context, args []interface{}) (testService, error) {
+	return testService{}, nil
+}
+func (s *testService) Method4(ctx context.Context, args []interface{}, rsp *struct{}) {
+}
+func (s *testService) Method5(ctx context.Context, args []interface{}, rsp *struct{}) *testService {
+	return nil
+}
+func (s *testService) Service() string {
+	return "com.test.Path"
+}
+func (s *testService) Version() string {
+	return ""
+}
+
+type TestService1 struct {
+}
+
+func (s *TestService1) Service() string {
+	return "com.test.Path1"
+}
+func (s *TestService1) Version() string {
+	return ""
+}
+
+func TestServiceMap_Register(t *testing.T) {
+	// lowercase
+	s0 := &testService{}
+	methods, err := ServiceMap.Register("testporotocol", s0)
+	assert.EqualError(t, err, "type testService is not exported")
+
+	// succ
+	s := &TestService{}
+	methods, err = ServiceMap.Register("testporotocol", s)
+	assert.NoError(t, err)
+	assert.Equal(t, "MethodOne,MethodTwo", methods)
+
+	// repeat
+	methods, err = ServiceMap.Register("testporotocol", s)
+	assert.EqualError(t, err, "service already defined: com.test.Path")
+
+	// no method
+	s1 := &TestService1{}
+	methods, err = ServiceMap.Register("testporotocol", s1)
+	assert.EqualError(t, err, "type com.test.Path1 has no exported methods of suitable type")
+
+	ServiceMap = &serviceMap{
+		serviceMap: make(map[string]map[string]*Service),
+	}
+}
+
+func TestServiceMap_UnRegister(t *testing.T) {
+	s := &TestService{}
+	_, err := ServiceMap.Register("testprotocol", s)
+	assert.NoError(t, err)
+	assert.NotNil(t, ServiceMap.GetService("testprotocol", "com.test.Path"))
+
+	err = ServiceMap.UnRegister("", "com.test.Path")
+	assert.EqualError(t, err, "protocol or serviceName is nil")
+
+	err = ServiceMap.UnRegister("protocol", "com.test.Path")
+	assert.EqualError(t, err, "no services for protocol")
+
+	err = ServiceMap.UnRegister("testprotocol", "com.test.Path1")
+	assert.EqualError(t, err, "no service for com.test.Path1")
+
+	// succ
+	err = ServiceMap.UnRegister("testprotocol", "com.test.Path")
+	assert.NoError(t, err)
+}
+
+func TestMethodType_SuiteContext(t *testing.T) {
+	mt := &MethodType{ctxType: reflect.TypeOf(context.TODO())}
+	c := context.TODO()
+	c = context.WithValue(c, "key", "value")
+	assert.Equal(t, reflect.ValueOf(c), mt.SuiteContext(c))
+
+	assert.Equal(t, reflect.Zero(mt.ctxType), mt.SuiteContext(nil))
+}
+
+func TestSuiteMethod(t *testing.T) {
+
+	s := &TestService{}
+	method, ok := reflect.TypeOf(s).MethodByName("MethodOne")
+	assert.True(t, ok)
+	methodType := suiteMethod(method)
+	method = methodType.Method()
+	assert.Equal(t, "func(*common.TestService, context.Context, []interface {}, *struct {}) error", method.Type.String())
+	at := methodType.ArgsType()
+	assert.Equal(t, "[]interface {}", at[0].String())
+	assert.Equal(t, "*struct {}", at[1].String())
+	ct := methodType.CtxType()
+	assert.Equal(t, "context.Context", ct.String())
+	rt := methodType.ReplyType()
+	assert.Nil(t, rt)
+
+	method, ok = reflect.TypeOf(s).MethodByName("MethodTwo")
+	assert.True(t, ok)
+	methodType = suiteMethod(method)
+	method = methodType.Method()
+	assert.Equal(t, "func(*common.TestService, []interface {}) (struct {}, error)", method.Type.String())
+	at = methodType.ArgsType()
+	assert.Equal(t, "[]interface {}", at[0].String())
+	assert.Nil(t, methodType.CtxType())
+	rt = methodType.ReplyType()
+	assert.Equal(t, "struct {}", rt.String())
+
+	// wrong number of in return
+	s1 := &testService{}
+	method, ok = reflect.TypeOf(s1).MethodByName("Version")
+	assert.True(t, ok)
+	methodType = suiteMethod(method)
+	assert.Nil(t, methodType)
+
+	// args not exported
+	method, ok = reflect.TypeOf(s1).MethodByName("Method1")
+	assert.True(t, ok)
+	methodType = suiteMethod(method)
+	assert.Nil(t, methodType)
+
+	// replyType != Ptr
+	method, ok = reflect.TypeOf(s1).MethodByName("Method2")
+	assert.True(t, ok)
+	methodType = suiteMethod(method)
+	assert.Nil(t, methodType)
+
+	// Reply not exported
+	method, ok = reflect.TypeOf(s1).MethodByName("Method3")
+	assert.True(t, ok)
+	methodType = suiteMethod(method)
+	assert.Nil(t, methodType)
+
+	// no return
+	method, ok = reflect.TypeOf(s1).MethodByName("Method4")
+	assert.True(t, ok)
+	methodType = suiteMethod(method)
+	assert.Nil(t, methodType)
+
+	// return value is not error
+	method, ok = reflect.TypeOf(s1).MethodByName("Method5")
+	assert.True(t, ok)
+	methodType = suiteMethod(method)
+	assert.Nil(t, methodType)
+}
diff --git a/common/url.go b/common/url.go
new file mode 100644
index 0000000000000000000000000000000000000000..9b0c6352ae781a4f6eacb36e3f727e2b7ac55232
--- /dev/null
+++ b/common/url.go
@@ -0,0 +1,294 @@
+// Copyright 2016-2019 hxmhlt
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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 (
+	"context"
+	"fmt"
+	"net"
+	"net/url"
+	"strconv"
+	"strings"
+)
+
+import (
+	perrors "github.com/pkg/errors"
+)
+
+import (
+	"github.com/dubbo/go-for-apache-dubbo/common/constant"
+)
+
+/////////////////////////////////
+// dubbo role type
+/////////////////////////////////
+
+const (
+	CONSUMER = iota
+	CONFIGURATOR
+	ROUTER
+	PROVIDER
+)
+
+var (
+	DubboNodes = [...]string{"consumers", "configurators", "routers", "providers"}
+	DubboRole  = [...]string{"consumer", "", "", "provider"}
+)
+
+type RoleType int
+
+func (t RoleType) String() string {
+	return DubboNodes[t]
+}
+
+func (t RoleType) Role() string {
+	return DubboRole[t]
+}
+
+type baseUrl struct {
+	Protocol     string
+	Location     string // ip+port
+	Ip           string
+	Port         string
+	Params       url.Values
+	PrimitiveURL string
+	ctx          context.Context
+}
+
+type URL struct {
+	baseUrl
+	Path     string // like  /com.ikurento.dubbo.UserProvider3
+	Username string
+	Password string
+	Methods  []string
+	//special for registry
+	SubURL *URL
+}
+
+type option func(*URL)
+
+func WithUsername(username string) option {
+	return func(url *URL) {
+		url.Username = username
+	}
+}
+
+func WithPassword(pwd string) option {
+	return func(url *URL) {
+		url.Password = pwd
+	}
+}
+
+func WithMethods(methods []string) option {
+	return func(url *URL) {
+		url.Methods = methods
+	}
+}
+
+func WithParams(params url.Values) option {
+	return func(url *URL) {
+		url.Params = params
+	}
+}
+func WithParamsValue(key, val string) option {
+	return func(url *URL) {
+		url.Params.Set(key, val)
+	}
+}
+func WithProtocol(proto string) option {
+	return func(url *URL) {
+		url.Protocol = proto
+	}
+}
+func WithIp(ip string) option {
+	return func(url *URL) {
+		url.Ip = ip
+	}
+}
+
+func WithPort(port string) option {
+	return func(url *URL) {
+		url.Port = port
+	}
+}
+
+//func WithPath(path string) option {
+//	return func(url *URL) {
+//		url.Path = path
+//	}
+//}
+
+func NewURLWithOptions(service string, opts ...option) *URL {
+	url := &URL{
+		Path: "/" + service,
+	}
+	for _, opt := range opts {
+		opt(url)
+	}
+	url.Location = url.Ip + ":" + url.Port
+	return url
+}
+
+func NewURL(ctx context.Context, urlString string, opts ...option) (URL, error) {
+
+	var (
+		err          error
+		rawUrlString string
+		serviceUrl   *url.URL
+		s            = URL{baseUrl: baseUrl{ctx: ctx}}
+	)
+
+	// new a null instance
+	if urlString == "" {
+		return s, nil
+	}
+
+	rawUrlString, err = url.QueryUnescape(urlString)
+	if err != nil {
+		return s, perrors.Errorf("url.QueryUnescape(%s),  error{%v}", urlString, err)
+	}
+
+	//rawUrlString = "//" + rawUrlString
+	serviceUrl, err = url.Parse(rawUrlString)
+	if err != 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)
+	}
+
+	s.PrimitiveURL = urlString
+	s.Protocol = serviceUrl.Scheme
+	s.Username = serviceUrl.User.Username()
+	s.Password, _ = serviceUrl.User.Password()
+	s.Location = serviceUrl.Host
+	s.Path = serviceUrl.Path
+	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)
+		}
+	}
+	//
+	//timeoutStr := s.Params.Get("timeout")
+	//if len(timeoutStr) == 0 {
+	//	timeoutStr = s.Params.Get("default.timeout")
+	//}
+	//if len(timeoutStr) != 0 {
+	//	timeout, err := strconv.Atoi(timeoutStr)
+	//	if err == nil && timeout != 0 {
+	//		s.Timeout = time.Duration(timeout * 1e6) // timeout unit is millisecond
+	//	}
+	//}
+	for _, opt := range opts {
+		opt(&s)
+	}
+	//fmt.Println(s.String())
+	return s, nil
+}
+
+//
+//func (c URL) Key() string {
+//	return fmt.Sprintf(
+//		"%s://%s:%s@%s:%s/%s",
+//		c.Protocol, c.Username, c.Password, c.Ip, c.Port, c.Path)
+//}
+
+func (c URL) URLEqual(url URL) bool {
+	c.Ip = ""
+	c.Port = ""
+	url.Ip = ""
+	url.Port = ""
+	if c.Key() != url.Key() {
+		return false
+	}
+	return true
+}
+
+//func (c SubURL) String() string {
+//	return fmt.Sprintf(
+//		"DefaultServiceURL{protocol:%s, Location:%s, Path:%s, Ip:%s, Port:%s, "+
+//			"Timeout:%s, Version:%s, Group:%s,  Params:%+v}",
+//		c.protocol, c.Location, c.Path, c.Ip, c.Port,
+//		c.Timeout, c.Version, c.Group, c.Params)
+//}
+
+func (c URL) String() string {
+	buildString := fmt.Sprintf(
+		"%s://%s:%s@%s:%s%s?",
+		c.Protocol, c.Username, c.Password, c.Ip, c.Port, c.Path)
+	buildString += c.Params.Encode()
+	return buildString
+}
+
+func (c URL) Key() string {
+	buildString := fmt.Sprintf(
+		"%s://%s:%s@%s:%s/%s?group=%s&version=%s",
+		c.Protocol, c.Username, c.Password, c.Ip, c.Port, c.GetParam(constant.INTERFACE_KEY, strings.TrimPrefix(c.Path, "/")), c.GetParam(constant.GROUP_KEY, ""), c.GetParam(constant.VERSION_KEY, constant.DEFAULT_VERSION))
+
+	return buildString
+}
+
+func (c URL) Context() context.Context {
+	return c.ctx
+}
+
+func (c URL) Service() string {
+	service := strings.TrimPrefix(c.Path, "/")
+	if service != "" {
+		return service
+	} else if c.SubURL != nil {
+		service = strings.TrimPrefix(c.SubURL.Path, "/")
+		if service != "" { //if url.path is "" then return suburl's path, special for registry Url
+			return service
+		}
+	}
+	return ""
+}
+func (c URL) GetParam(s string, d string) string {
+	var r string
+	if r = c.Params.Get(s); r == "" {
+		r = d
+	}
+	return r
+}
+
+func (c URL) GetParamInt(s string, d int64) int64 {
+	var r int
+	var err error
+	if r, err = strconv.Atoi(c.Params.Get(s)); r == 0 || err != nil {
+		return d
+	}
+	return int64(r)
+}
+
+func (c URL) GetMethodParamInt(method string, key string, d int64) int64 {
+	var r int
+	var err error
+	if r, err = strconv.Atoi(c.Params.Get("methods." + method + "." + key)); r == 0 || err != nil {
+		return d
+	}
+	return int64(r)
+}
+
+func (c URL) GetMethodParam(method string, key string, d string) string {
+	var r string
+	if r = c.Params.Get("methods." + method + "." + key); r == "" {
+		r = d
+	}
+	return r
+}
diff --git a/common/url_test.go b/common/url_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..cfac4703ef8c2c6443efbc0562c98add8acd5466
--- /dev/null
+++ b/common/url_test.go
@@ -0,0 +1,135 @@
+// Copyright 2016-2019 hxmhlt
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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 (
+	"context"
+	"net/url"
+	"testing"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+func TestNewURLWithOptions(t *testing.T) {
+	methods := []string{"Methodone,methodtwo"}
+	params := url.Values{}
+	params.Set("key", "value")
+	u := NewURLWithOptions("com.test.Service",
+		WithUsername("username"),
+		WithPassword("password"),
+		WithProtocol("testprotocol"),
+		WithIp("127.0.0.1"),
+		WithPort("8080"),
+		WithMethods(methods),
+		WithParams(params),
+		WithParamsValue("key2", "value2"))
+	assert.Equal(t, "/com.test.Service", u.Path)
+	assert.Equal(t, "username", u.Username)
+	assert.Equal(t, "password", u.Password)
+	assert.Equal(t, "testprotocol", u.Protocol)
+	assert.Equal(t, "127.0.0.1", u.Ip)
+	assert.Equal(t, "8080", u.Port)
+	assert.Equal(t, methods, u.Methods)
+	assert.Equal(t, params, u.Params)
+}
+
+func TestURL(t *testing.T) {
+	u, err := NewURL(context.TODO(), "dubbo://:@127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&"+
+		"application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&"+
+		"environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&"+
+		"module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&"+
+		"side=provider&timeout=3000&timestamp=1556509797245")
+	assert.NoError(t, err)
+
+	assert.Equal(t, "/com.ikurento.user.UserProvider", u.Path)
+	assert.Equal(t, "127.0.0.1:20000", u.Location)
+	assert.Equal(t, "dubbo", u.Protocol)
+	assert.Equal(t, "127.0.0.1", u.Ip)
+	assert.Equal(t, "20000", u.Port)
+	assert.Equal(t, URL{}.Methods, u.Methods)
+	assert.Equal(t, "", u.Username)
+	assert.Equal(t, "", u.Password)
+	assert.Equal(t, "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&t"+
+		"imestamp=1556509797245", u.Params.Encode())
+
+	assert.Equal(t, "dubbo://:@127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&application=BDTServi"+
+		"ce&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&environment=dev&interface=com.ikure"+
+		"nto.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", u.String())
+}
+
+func TestURL_URLEqual(t *testing.T) {
+	u1, err := NewURL(context.TODO(), "dubbo://:@127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=&version=2.6.0")
+	assert.NoError(t, err)
+	u2, err := NewURL(context.TODO(), "dubbo://:@127.0.0.2:20001/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=&version=2.6.0")
+	assert.NoError(t, err)
+	assert.True(t, u1.URLEqual(u2))
+
+	u3, err := NewURL(context.TODO(), "dubbo://:@127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=gg&version=2.6.0")
+	assert.NoError(t, err)
+	assert.False(t, u1.URLEqual(u3))
+}
+
+func TestURL_GetParam(t *testing.T) {
+	params := url.Values{}
+	params.Set("key", "value")
+	u := URL{baseUrl: baseUrl{Params: params}}
+	v := u.GetParam("key", "default")
+	assert.Equal(t, "value", v)
+
+	u = URL{}
+	v = u.GetParam("key", "default")
+	assert.Equal(t, "default", v)
+}
+
+func TestURL_GetParamInt(t *testing.T) {
+	params := url.Values{}
+	params.Set("key", "3")
+	u := URL{baseUrl: baseUrl{Params: params}}
+	v := u.GetParamInt("key", 1)
+	assert.Equal(t, int64(3), v)
+
+	u = URL{}
+	v = u.GetParamInt("key", 1)
+	assert.Equal(t, int64(1), v)
+}
+
+func TestURL_GetMethodParamInt(t *testing.T) {
+	params := url.Values{}
+	params.Set("methods.GetValue.timeout", "3")
+	u := URL{baseUrl: baseUrl{Params: params}}
+	v := u.GetMethodParamInt("GetValue", "timeout", 1)
+	assert.Equal(t, int64(3), v)
+
+	u = URL{}
+	v = u.GetMethodParamInt("GetValue", "timeout", 1)
+	assert.Equal(t, int64(1), v)
+}
+
+func TestURL_GetMethodParam(t *testing.T) {
+	params := url.Values{}
+	params.Set("methods.GetValue.timeout", "3s")
+	u := URL{baseUrl: baseUrl{Params: params}}
+	v := u.GetMethodParam("GetValue", "timeout", "1s")
+	assert.Equal(t, "3s", v)
+
+	u = URL{}
+	v = u.GetMethodParam("GetValue", "timeout", "1s")
+	assert.Equal(t, "1s", v)
+}
diff --git a/common/utils/net.go b/common/utils/net.go
new file mode 100644
index 0000000000000000000000000000000000000000..b9f1786e2bfea4337d28c0d963940558f6c3ecc2
--- /dev/null
+++ b/common/utils/net.go
@@ -0,0 +1,81 @@
+// Copyright 2016-2019 Alex Stocks
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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 (
+	"net"
+)
+
+import (
+	perrors "github.com/pkg/errors"
+)
+
+var (
+	privateBlocks []*net.IPNet
+)
+
+func init() {
+	for _, b := range []string{"10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"} {
+		if _, block, err := net.ParseCIDR(b); err == nil {
+			privateBlocks = append(privateBlocks, block)
+		}
+	}
+}
+
+// ref: https://stackoverflow.com/questions/23558425/how-do-i-get-the-local-ip-address-in-go
+func GetLocalIP() (string, error) {
+	ifs, err := net.Interfaces()
+	if err != nil {
+		return "", perrors.WithStack(err)
+	}
+
+	var ipAddr []byte
+	for _, i := range ifs {
+		addrs, err := i.Addrs()
+		if err != nil {
+			return "", perrors.WithStack(err)
+		}
+		var ip net.IP
+		for _, addr := range addrs {
+			switch v := addr.(type) {
+			case *net.IPNet:
+				ip = v.IP
+			case *net.IPAddr:
+				ip = v.IP
+			}
+
+			if !ip.IsLoopback() && ip.To4() != nil && isPrivateIP(ip.String()) {
+				ipAddr = ip
+				break
+			}
+		}
+	}
+
+	if ipAddr == nil {
+		return "", perrors.Errorf("can not get local IP")
+	}
+
+	return net.IP(ipAddr).String(), nil
+}
+
+func isPrivateIP(ipAddr string) bool {
+	ip := net.ParseIP(ipAddr)
+	for _, priv := range privateBlocks {
+		if priv.Contains(ip) {
+			return true
+		}
+	}
+	return false
+}
diff --git a/common/utils/net_test.go b/common/utils/net_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..5230d8d9c91f37d79e2d846f243ca1fcb68d758a
--- /dev/null
+++ b/common/utils/net_test.go
@@ -0,0 +1,15 @@
+package utils
+
+import (
+	"testing"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+func TestGetLocalIP(t *testing.T) {
+	ip, err := GetLocalIP()
+	assert.NoError(t, err)
+	t.Log(ip)
+}
diff --git a/config/application_config.go b/config/application_config.go
new file mode 100644
index 0000000000000000000000000000000000000000..f12fdea9f1d332931d646a3a124ef522f6aef5e8
--- /dev/null
+++ b/config/application_config.go
@@ -0,0 +1,24 @@
+// Copyright 2016-2019 hxmhlt
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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
+
+type ApplicationConfig struct {
+	Organization string `yaml:"organization"  json:"organization,omitempty"`
+	Name         string `yaml:"name" json:"name,omitempty"`
+	Module       string `yaml:"module" json:"module,omitempty"`
+	Version      string `yaml:"version" json:"version,omitempty"`
+	Owner        string `yaml:"owner" json:"owner,omitempty"`
+	Environment  string `yaml:"environment" json:"environment,omitempty"`
+}
diff --git a/config/config_loader.go b/config/config_loader.go
new file mode 100644
index 0000000000000000000000000000000000000000..32dde08bd1a867f84b937bc1de83456073e563a8
--- /dev/null
+++ b/config/config_loader.go
@@ -0,0 +1,276 @@
+// Copyright 2016-2019 Yincheng Fang
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package config
+
+import (
+	"fmt"
+	"io/ioutil"
+	"log"
+	"os"
+	"path"
+	"strings"
+	"time"
+)
+
+import (
+	perrors "github.com/pkg/errors"
+	"gopkg.in/yaml.v2"
+)
+
+import (
+	"github.com/dubbo/go-for-apache-dubbo/common/constant"
+	"github.com/dubbo/go-for-apache-dubbo/common/logger"
+	"github.com/dubbo/go-for-apache-dubbo/version"
+)
+
+var (
+	consumerConfig *ConsumerConfig
+	providerConfig *ProviderConfig
+	maxWait        = 3
+)
+
+// loaded comsumer & provider config from xxx.yml, and log config from xxx.xml
+// Namely: dubbo.comsumer.xml & dubbo.provider.xml in java dubbo
+func init() {
+
+	var (
+		confConFile, confProFile string
+	)
+
+	confConFile = os.Getenv(constant.CONF_CONSUMER_FILE_PATH)
+	confProFile = os.Getenv(constant.CONF_PROVIDER_FILE_PATH)
+
+	if errCon := consumerInit(confConFile); errCon != nil {
+		log.Printf("[consumerInit] %#v", errCon)
+		consumerConfig = nil
+	}
+	if errPro := providerInit(confProFile); errPro != nil {
+		log.Printf("[providerInit] %#v", errPro)
+		providerConfig = nil
+	}
+}
+
+func consumerInit(confConFile string) error {
+	if confConFile == "" {
+		return perrors.Errorf("application configure(consumer) file name is nil")
+	}
+
+	if path.Ext(confConFile) != ".yml" {
+		return perrors.Errorf("application configure file name{%v} suffix must be .yml", confConFile)
+	}
+
+	confFileStream, err := ioutil.ReadFile(confConFile)
+	if err != nil {
+		return perrors.Errorf("ioutil.ReadFile(file:%s) = error:%v", confConFile, perrors.WithStack(err))
+	}
+	consumerConfig = &ConsumerConfig{}
+	err = yaml.Unmarshal(confFileStream, consumerConfig)
+	if err != nil {
+		return perrors.Errorf("yaml.Unmarshal() = error:%v", perrors.WithStack(err))
+	}
+
+	if consumerConfig.RequestTimeout, err = time.ParseDuration(consumerConfig.Request_Timeout); err != nil {
+		return perrors.WithMessagef(err, "time.ParseDuration(Request_Timeout{%#v})", consumerConfig.Request_Timeout)
+	}
+	if consumerConfig.ConnectTimeout, err = time.ParseDuration(consumerConfig.Connect_Timeout); err != nil {
+		return perrors.WithMessagef(err, "time.ParseDuration(Connect_Timeout{%#v})", consumerConfig.Connect_Timeout)
+	}
+
+	logger.Debugf("consumer config{%#v}\n", consumerConfig)
+	return nil
+}
+
+func providerInit(confProFile string) error {
+	if confProFile == "" {
+		return perrors.Errorf("application configure(provider) file name is nil")
+	}
+
+	if path.Ext(confProFile) != ".yml" {
+		return perrors.Errorf("application configure file name{%v} suffix must be .yml", confProFile)
+	}
+
+	confFileStream, err := ioutil.ReadFile(confProFile)
+	if err != nil {
+		return perrors.Errorf("ioutil.ReadFile(file:%s) = error:%v", confProFile, perrors.WithStack(err))
+	}
+	providerConfig = &ProviderConfig{}
+	err = yaml.Unmarshal(confFileStream, providerConfig)
+	if err != nil {
+		return perrors.Errorf("yaml.Unmarshal() = error:%v", perrors.WithStack(err))
+	}
+
+	logger.Debugf("provider config{%#v}\n", providerConfig)
+	return nil
+}
+
+/////////////////////////
+// consumerConfig
+/////////////////////////
+
+type ConsumerConfig struct {
+	Filter string `yaml:"filter" json:"filter,omitempty"`
+
+	// client
+	Connect_Timeout string `default:"100ms"  yaml:"connect_timeout" json:"connect_timeout,omitempty"`
+	ConnectTimeout  time.Duration
+
+	Request_Timeout string `yaml:"request_timeout" default:"5s" json:"request_timeout,omitempty"`
+	RequestTimeout  time.Duration
+	ProxyFactory    string `yaml:"proxy_factory" default:"default" json:"proxy_factory,omitempty"`
+	Check           *bool  `yaml:"check"  json:"check,omitempty"`
+	// application
+	ApplicationConfig ApplicationConfig `yaml:"application_config" json:"application_config,omitempty"`
+	Registries        []RegistryConfig  `yaml:"registries" json:"registries,omitempty"`
+	References        []ReferenceConfig `yaml:"references" json:"references,omitempty"`
+	ProtocolConf      interface{}       `yaml:"protocol_conf" json:"protocol_conf,omitempty"`
+}
+
+type ReferenceConfigTmp struct {
+	Service    string           `required:"true"  yaml:"service"  json:"service,omitempty"`
+	Registries []RegistryConfig `required:"true"  yaml:"registries"  json:"registries,omitempty"`
+	URLs       []map[string]string
+}
+
+func SetConsumerConfig(c ConsumerConfig) {
+	consumerConfig = &c
+}
+func GetConsumerConfig() ConsumerConfig {
+	if consumerConfig == nil {
+		logger.Warnf("consumerConfig is nil!")
+		return ConsumerConfig{}
+	}
+	return *consumerConfig
+}
+
+/////////////////////////
+// providerConfig
+/////////////////////////
+
+type ProviderConfig struct {
+	Filter       string `yaml:"filter" json:"filter,omitempty"`
+	ProxyFactory string `yaml:"proxy_factory" default:"default" json:"proxy_factory,omitempty"`
+
+	ApplicationConfig ApplicationConfig `yaml:"application_config" json:"application_config,omitempty"`
+	Registries        []RegistryConfig  `yaml:"registries" json:"registries,omitempty"`
+	Services          []ServiceConfig   `yaml:"services" json:"services,omitempty"`
+	Protocols         []ProtocolConfig  `yaml:"protocols" json:"protocols,omitempty"`
+	ProtocolConf      interface{}       `yaml:"protocol_conf" json:"protocol_conf,omitempty"`
+}
+
+func GetProviderConfig() ProviderConfig {
+	if providerConfig == nil {
+		logger.Warnf("providerConfig is nil!")
+		return ProviderConfig{}
+	}
+	return *providerConfig
+}
+
+type ProtocolConfig struct {
+	Name        string `required:"true" yaml:"name"  json:"name,omitempty"`
+	Ip          string `required:"true" yaml:"ip"  json:"ip,omitempty"`
+	Port        string `required:"true" yaml:"port"  json:"port,omitempty"`
+	ContextPath string `required:"true" yaml:"contextPath"  json:"contextPath,omitempty"`
+}
+
+func loadProtocol(protocolsIds string, protocols []ProtocolConfig) []ProtocolConfig {
+	returnProtocols := []ProtocolConfig{}
+	for _, v := range strings.Split(protocolsIds, ",") {
+		for _, prot := range protocols {
+			if v == prot.Name {
+				returnProtocols = append(returnProtocols, prot)
+			}
+		}
+
+	}
+	return returnProtocols
+}
+
+// Dubbo Init
+func Load() (map[string]*ReferenceConfig, map[string]*ServiceConfig) {
+	var refMap map[string]*ReferenceConfig
+	var srvMap map[string]*ServiceConfig
+
+	// reference config
+	if consumerConfig == nil {
+		logger.Warnf("consumerConfig is nil!")
+	} else {
+		refMap = make(map[string]*ReferenceConfig)
+		length := len(consumerConfig.References)
+		for index := 0; index < length; index++ {
+			con := &consumerConfig.References[index]
+			rpcService := GetConsumerService(con.InterfaceName)
+			if rpcService == nil {
+				logger.Warnf("%s is not exsist!", con.InterfaceName)
+				continue
+			}
+			con.Refer()
+			con.Implement(rpcService)
+			refMap[con.InterfaceName] = con
+		}
+
+		//wait for invoker is available, if wait over default 3s, then panic
+		var count int
+		checkok := true
+		for {
+			for _, refconfig := range consumerConfig.References {
+				if (refconfig.Check != nil && *refconfig.Check) ||
+					(refconfig.Check == nil && consumerConfig.Check != nil && *consumerConfig.Check) ||
+					(refconfig.Check == nil && consumerConfig.Check == nil) { //default to true
+
+					if refconfig.invoker != nil &&
+						!refconfig.invoker.IsAvailable() {
+						checkok = false
+						count++
+						if count > maxWait {
+							panic(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, version.Version))
+						}
+						time.Sleep(time.Second * 1)
+						break
+					}
+					if refconfig.invoker == nil {
+						logger.Warnf("The interface %s invoker not exsist , may you should check your interface config.", refconfig.InterfaceName)
+					}
+				}
+			}
+			if checkok {
+				break
+			}
+			checkok = true
+		}
+	}
+
+	// service config
+	if providerConfig == nil {
+		logger.Warnf("providerConfig is nil!")
+	} else {
+		srvMap = make(map[string]*ServiceConfig)
+		length := len(providerConfig.Services)
+		for index := 0; index < length; index++ {
+			pro := &providerConfig.Services[index]
+			rpcService := GetProviderService(pro.InterfaceName)
+			if rpcService == nil {
+				logger.Warnf("%s is not exsist!", pro.InterfaceName)
+				continue
+			}
+			pro.Implement(rpcService)
+			if err := pro.Export(); err != nil {
+				panic(fmt.Sprintf("service %s export failed! ", pro.InterfaceName))
+			}
+			srvMap[pro.InterfaceName] = pro
+		}
+	}
+
+	return refMap, srvMap
+}
diff --git a/config/config_loader_test.go b/config/config_loader_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..0fcd76b95535c09770a0a72e6a8ca8f5f5007d7a
--- /dev/null
+++ b/config/config_loader_test.go
@@ -0,0 +1,76 @@
+// Copyright 2016-2019 Yincheng Fang
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package config
+
+import (
+	"github.com/dubbo/go-for-apache-dubbo/common/proxy/proxy_factory"
+	"path/filepath"
+	"testing"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+import (
+	"github.com/dubbo/go-for-apache-dubbo/cluster/cluster_impl"
+	"github.com/dubbo/go-for-apache-dubbo/common"
+	"github.com/dubbo/go-for-apache-dubbo/common/extension"
+)
+
+func TestConfigLoader(t *testing.T) {
+	conPath, err := filepath.Abs("./testdata/consumer_config.yml")
+	assert.NoError(t, err)
+	proPath, err := filepath.Abs("./testdata/provider_config.yml")
+	assert.NoError(t, err)
+
+	assert.Nil(t, consumerConfig)
+	assert.Equal(t, ConsumerConfig{}, GetConsumerConfig())
+	assert.Nil(t, providerConfig)
+	assert.Equal(t, ProviderConfig{}, GetProviderConfig())
+
+	err = consumerInit(conPath)
+	assert.NoError(t, err)
+	err = providerInit(proPath)
+	assert.NoError(t, err)
+
+	assert.NotNil(t, consumerConfig)
+	assert.NotEqual(t, ConsumerConfig{}, GetConsumerConfig())
+	assert.NotNil(t, providerConfig)
+	assert.NotEqual(t, ProviderConfig{}, GetProviderConfig())
+}
+
+func TestLoad(t *testing.T) {
+	doInit()
+	doinit()
+
+	SetConsumerService(&MockService{})
+	SetProviderService(&MockService{})
+
+	extension.SetProtocol("registry", GetProtocol)
+	extension.SetCluster("registryAware", cluster_impl.NewRegistryAwareCluster)
+	extension.SetProxyFactory("default", proxy_factory.NewDefaultProxyFactory)
+	consumerConfig.References[0].Registries = []ConfigRegistry{"shanghai_reg1"}
+
+	refConfigs, svcConfigs := Load()
+	assert.NotEqual(t, 0, len(refConfigs))
+	assert.NotEqual(t, 0, len(svcConfigs))
+
+	conServices = map[string]common.RPCService{}
+	proServices = map[string]common.RPCService{}
+	common.ServiceMap.UnRegister("mock", "MockService")
+	consumerConfig = nil
+	providerConfig = nil
+}
diff --git a/config/config_utils.go b/config/config_utils.go
new file mode 100644
index 0000000000000000000000000000000000000000..5b2e2053ce5c2aac9312c8112e5c234764b394d1
--- /dev/null
+++ b/config/config_utils.go
@@ -0,0 +1,61 @@
+package config
+
+import (
+	"regexp"
+	"strings"
+)
+import (
+	"github.com/dubbo/go-for-apache-dubbo/common/constant"
+)
+
+func mergeValue(str1, str2, def string) string {
+	if str1 == "" && str2 == "" {
+		return def
+	}
+	str := "," + strings.Trim(str1, ",")
+	if str1 == "" {
+		str = "," + strings.Trim(str2, ",")
+	} else if str2 != "" {
+		str = str + "," + strings.Trim(str2, ",")
+	}
+	defKey := strings.Contains(str, ","+constant.DEFAULT_KEY)
+	if !defKey {
+		str = "," + constant.DEFAULT_KEY + str
+	}
+	str = strings.TrimPrefix(strings.Replace(str, ","+constant.DEFAULT_KEY, ","+def, -1), ",")
+
+	strArr := strings.Split(str, ",")
+	strMap := make(map[string][]int)
+	for k, v := range strArr {
+		add := true
+		if strings.HasPrefix(v, "-") {
+			v = v[1:]
+			add = false
+		}
+		if _, ok := strMap[v]; !ok {
+			if add {
+				strMap[v] = []int{1, k}
+			}
+		} else {
+			if add {
+				strMap[v][0] += 1
+				strMap[v] = append(strMap[v], k)
+			} else {
+				strMap[v][0] -= 1
+				strMap[v] = strMap[v][:len(strMap[v])-1]
+			}
+		}
+	}
+	strArr = make([]string, len(strArr))
+	for key, value := range strMap {
+		if value[0] == 0 {
+			continue
+		}
+		for i := 1; i < len(value); i++ {
+			strArr[value[i]] = key
+		}
+	}
+	reg := regexp.MustCompile("[,]+")
+	str = reg.ReplaceAllString(strings.Join(strArr, ","), ",")
+	return strings.Trim(str, ",")
+}
diff --git a/config/config_utils_test.go b/config/config_utils_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..2c80f9da4f7d5fcc5537acc50cbd307180a0032a
--- /dev/null
+++ b/config/config_utils_test.go
@@ -0,0 +1,26 @@
+package config
+
+import (
+	"testing"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+func TestMergeValue(t *testing.T) {
+	str := mergeValue("", "", "a,b")
+	assert.Equal(t, "a,b", str)
+
+	str = mergeValue("c,d", "e,f", "a,b")
+	assert.Equal(t, "a,b,c,d,e,f", str)
+
+	str = mergeValue("c,d", "e,d,f", "a,b")
+	assert.Equal(t, "a,b,c,d,e,d,f", str)
+
+	str = mergeValue("c,default,d", "-c,-a,e,f", "a,b")
+	assert.Equal(t, "b,d,e,f", str)
+
+	str = mergeValue("", "default,-b,e,f", "a,b")
+	assert.Equal(t, "a,e,f", str)
+}
diff --git a/config/mock_rpcservice.go b/config/mock_rpcservice.go
new file mode 100644
index 0000000000000000000000000000000000000000..87059ac007d736f19bbeea579a18a41b407a9560
--- /dev/null
+++ b/config/mock_rpcservice.go
@@ -0,0 +1,33 @@
+// Copyright 2016-2019 hxmhlt
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package config
+
+import "context"
+
+type MockService struct {
+}
+
+func (*MockService) Service() string {
+	return "MockService"
+}
+func (*MockService) Version() string {
+	return "1.0"
+}
+func (*MockService) GetUser(ctx context.Context, itf []interface{}, str *struct{}) error {
+	return nil
+}
+func (*MockService) GetUser1(ctx context.Context, itf []interface{}, str *struct{}) error {
+	return nil
+}
diff --git a/config/reference_config.go b/config/reference_config.go
new file mode 100644
index 0000000000000000000000000000000000000000..af57cc5114a744c417848500bfd25784880f33c7
--- /dev/null
+++ b/config/reference_config.go
@@ -0,0 +1,138 @@
+// Copyright 2016-2019 hxmhlt
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package config
+
+import (
+	"context"
+	"net/url"
+	"strconv"
+	"time"
+)
+
+import (
+	"github.com/dubbo/go-for-apache-dubbo/cluster/directory"
+	"github.com/dubbo/go-for-apache-dubbo/common"
+	"github.com/dubbo/go-for-apache-dubbo/common/constant"
+	"github.com/dubbo/go-for-apache-dubbo/common/extension"
+	"github.com/dubbo/go-for-apache-dubbo/common/proxy"
+	"github.com/dubbo/go-for-apache-dubbo/protocol"
+)
+
+type ReferenceConfig struct {
+	context       context.Context
+	pxy           *proxy.Proxy
+	InterfaceName string           `required:"true"  yaml:"interface"  json:"interface,omitempty"`
+	Check         *bool            `yaml:"check"  json:"check,omitempty"`
+	Filter        string           `yaml:"filter" json:"filter,omitempty"`
+	Protocol      string           `yaml:"protocol"  json:"protocol,omitempty"`
+	Registries    []ConfigRegistry `required:"true"  yaml:"registries"  json:"registries,omitempty"`
+	Cluster       string           `yaml:"cluster"  json:"cluster,omitempty"`
+	Loadbalance   string           `yaml:"loadbalance"  json:"loadbalance,omitempty"`
+	Retries       int64            `yaml:"retries"  json:"retries,omitempty"`
+	Group         string           `yaml:"group"  json:"group,omitempty"`
+	Version       string           `yaml:"version"  json:"version,omitempty"`
+	Methods       []struct {
+		Name        string `yaml:"name"  json:"name,omitempty"`
+		Retries     int64  `yaml:"retries"  json:"retries,omitempty"`
+		Loadbalance string `yaml:"loadbalance"  json:"loadbalance,omitempty"`
+	} `yaml:"methods"  json:"methods,omitempty"`
+	async   bool `yaml:"async"  json:"async,omitempty"`
+	invoker protocol.Invoker
+}
+
+type ConfigRegistry string
+
+func NewReferenceConfig(ctx context.Context) *ReferenceConfig {
+	return &ReferenceConfig{context: ctx}
+}
+func (refconfig *ReferenceConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
+	type rf ReferenceConfig
+	raw := rf{} // Put your defaults here
+	if err := unmarshal(&raw); err != nil {
+		return err
+	}
+
+	*refconfig = ReferenceConfig(raw)
+	return nil
+}
+func (refconfig *ReferenceConfig) Refer() {
+	//1. user specified SubURL, could be peer-to-peer address, or register center's address.
+
+	//2. assemble SubURL from register center's configuration妯″紡
+	regUrls := loadRegistries(refconfig.Registries, consumerConfig.Registries, common.CONSUMER)
+	url := common.NewURLWithOptions(refconfig.InterfaceName, common.WithProtocol(refconfig.Protocol), common.WithParams(refconfig.getUrlMap()))
+
+	//set url to regUrls
+	for _, regUrl := range regUrls {
+		regUrl.SubURL = url
+	}
+
+	if len(regUrls) == 1 {
+		refconfig.invoker = extension.GetProtocol("registry").Refer(*regUrls[0])
+
+	} else {
+		invokers := []protocol.Invoker{}
+		for _, regUrl := range regUrls {
+			invokers = append(invokers, extension.GetProtocol("registry").Refer(*regUrl))
+		}
+		cluster := extension.GetCluster("registryAware")
+		refconfig.invoker = cluster.Join(directory.NewStaticDirectory(invokers))
+	}
+
+	//create proxy
+	refconfig.pxy = extension.GetProxyFactory(consumerConfig.ProxyFactory).GetProxy(refconfig.invoker, url)
+}
+
+// @v is service provider implemented RPCService
+func (refconfig *ReferenceConfig) Implement(v common.RPCService) {
+	refconfig.pxy.Implement(v)
+}
+
+func (refconfig *ReferenceConfig) GetRPCService() common.RPCService {
+	return refconfig.pxy.Get()
+}
+
+func (refconfig *ReferenceConfig) getUrlMap() url.Values {
+	urlMap := url.Values{}
+	urlMap.Set(constant.INTERFACE_KEY, refconfig.InterfaceName)
+	urlMap.Set(constant.TIMESTAMP_KEY, strconv.FormatInt(time.Now().Unix(), 10))
+	urlMap.Set(constant.CLUSTER_KEY, refconfig.Cluster)
+	urlMap.Set(constant.LOADBALANCE_KEY, refconfig.Loadbalance)
+	urlMap.Set(constant.RETRIES_KEY, strconv.FormatInt(refconfig.Retries, 10))
+	urlMap.Set(constant.GROUP_KEY, refconfig.Group)
+	urlMap.Set(constant.VERSION_KEY, refconfig.Version)
+	//getty invoke async or sync
+	urlMap.Set(constant.ASYNC_KEY, strconv.FormatBool(refconfig.async))
+
+	//application info
+	urlMap.Set(constant.APPLICATION_KEY, consumerConfig.ApplicationConfig.Name)
+	urlMap.Set(constant.ORGANIZATION_KEY, consumerConfig.ApplicationConfig.Organization)
+	urlMap.Set(constant.NAME_KEY, consumerConfig.ApplicationConfig.Name)
+	urlMap.Set(constant.MODULE_KEY, consumerConfig.ApplicationConfig.Module)
+	urlMap.Set(constant.APP_VERSION_KEY, consumerConfig.ApplicationConfig.Version)
+	urlMap.Set(constant.OWNER_KEY, consumerConfig.ApplicationConfig.Owner)
+	urlMap.Set(constant.ENVIRONMENT_KEY, consumerConfig.ApplicationConfig.Environment)
+
+	//filter
+	urlMap.Set(constant.REFERENCE_FILTER_KEY, mergeValue(consumerConfig.Filter, refconfig.Filter, constant.DEFAULT_REFERENCE_FILTERS))
+
+	for _, v := range refconfig.Methods {
+		urlMap.Set("methods."+v.Name+"."+constant.LOADBALANCE_KEY, v.Loadbalance)
+		urlMap.Set("methods."+v.Name+"."+constant.RETRIES_KEY, strconv.FormatInt(v.Retries, 10))
+	}
+
+	return urlMap
+
+}
diff --git a/config/reference_config_test.go b/config/reference_config_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..8d1c718f830923a63b79439cd0ba865d23e9ddca
--- /dev/null
+++ b/config/reference_config_test.go
@@ -0,0 +1,176 @@
+// Copyright 2016-2019 hxmhlt
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package config
+
+import (
+	"sync"
+	"testing"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+import (
+	"github.com/dubbo/go-for-apache-dubbo/cluster/cluster_impl"
+	"github.com/dubbo/go-for-apache-dubbo/common"
+	"github.com/dubbo/go-for-apache-dubbo/common/extension"
+	"github.com/dubbo/go-for-apache-dubbo/protocol"
+)
+
+var regProtocol protocol.Protocol
+
+func doInit() {
+	consumerConfig = &ConsumerConfig{
+		ApplicationConfig: ApplicationConfig{
+			Organization: "dubbo_org",
+			Name:         "dubbo",
+			Module:       "module",
+			Version:      "2.6.0",
+			Owner:        "dubbo",
+			Environment:  "test"},
+		Registries: []RegistryConfig{
+			{
+				Id:         "shanghai_reg1",
+				Type:       "mock",
+				TimeoutStr: "2s",
+				Group:      "shanghai_idc",
+				Address:    "127.0.0.1:2181",
+				Username:   "user1",
+				Password:   "pwd1",
+			},
+			{
+				Id:         "shanghai_reg2",
+				Type:       "mock",
+				TimeoutStr: "2s",
+				Group:      "shanghai_idc",
+				Address:    "127.0.0.2:2181",
+				Username:   "user1",
+				Password:   "pwd1",
+			},
+			{
+				Id:         "hangzhou_reg1",
+				Type:       "mock",
+				TimeoutStr: "2s",
+				Group:      "hangzhou_idc",
+				Address:    "127.0.0.3:2181",
+				Username:   "user1",
+				Password:   "pwd1",
+			},
+			{
+				Id:         "hangzhou_reg2",
+				Type:       "mock",
+				TimeoutStr: "2s",
+				Group:      "hangzhou_idc",
+				Address:    "127.0.0.4:2181",
+				Username:   "user1",
+				Password:   "pwd1",
+			},
+		},
+		References: []ReferenceConfig{
+			{
+				InterfaceName: "MockService",
+				Protocol:      "mock",
+				Registries:    []ConfigRegistry{"shanghai_reg1", "shanghai_reg2", "hangzhou_reg1", "hangzhou_reg2"},
+				Cluster:       "failover",
+				Loadbalance:   "random",
+				Retries:       3,
+				Group:         "huadong_idc",
+				Version:       "1.0.0",
+				Methods: []struct {
+					Name        string `yaml:"name"  json:"name,omitempty"`
+					Retries     int64  `yaml:"retries"  json:"retries,omitempty"`
+					Loadbalance string `yaml:"loadbalance"  json:"loadbalance,omitempty"`
+				}{
+					{
+						Name:        "GetUser",
+						Retries:     2,
+						Loadbalance: "random",
+					},
+					{
+						Name:        "GetUser1",
+						Retries:     2,
+						Loadbalance: "random",
+					},
+				},
+			},
+		},
+	}
+}
+
+func Test_ReferMultireg(t *testing.T) {
+	doInit()
+	extension.SetProtocol("registry", GetProtocol)
+	extension.SetCluster("registryAware", cluster_impl.NewRegistryAwareCluster)
+
+	for _, reference := range consumerConfig.References {
+		reference.Refer()
+		assert.NotNil(t, reference.invoker)
+		assert.NotNil(t, reference.pxy)
+	}
+	consumerConfig = nil
+}
+
+func Test_Refer(t *testing.T) {
+	doInit()
+	extension.SetProtocol("registry", GetProtocol)
+	extension.SetCluster("registryAware", cluster_impl.NewRegistryAwareCluster)
+	consumerConfig.References[0].Registries = []ConfigRegistry{"shanghai_reg1"}
+
+	for _, reference := range consumerConfig.References {
+		reference.Refer()
+		assert.NotNil(t, reference.invoker)
+		assert.NotNil(t, reference.pxy)
+	}
+	consumerConfig = nil
+}
+
+func Test_Implement(t *testing.T) {
+	doInit()
+	extension.SetProtocol("registry", GetProtocol)
+	extension.SetCluster("registryAware", cluster_impl.NewRegistryAwareCluster)
+	for _, reference := range consumerConfig.References {
+		reference.Refer()
+		reference.Implement(&MockService{})
+		assert.NotNil(t, reference.GetRPCService())
+
+	}
+	consumerConfig = nil
+}
+func GetProtocol() protocol.Protocol {
+	if regProtocol != nil {
+		return regProtocol
+	}
+	return newRegistryProtocol()
+}
+
+func newRegistryProtocol() protocol.Protocol {
+	return &mockRegistryProtocol{}
+}
+
+type mockRegistryProtocol struct {
+}
+
+func (*mockRegistryProtocol) Refer(url common.URL) protocol.Invoker {
+	return protocol.NewBaseInvoker(url)
+}
+
+func (*mockRegistryProtocol) Export(invoker protocol.Invoker) protocol.Exporter {
+	return protocol.NewBaseExporter("test", invoker, &sync.Map{})
+}
+
+func (*mockRegistryProtocol) Destroy() {
+
+}
diff --git a/config/registry_config.go b/config/registry_config.go
new file mode 100644
index 0000000000000000000000000000000000000000..550b73544fa055b044d5bb4305bdd3624beefd67
--- /dev/null
+++ b/config/registry_config.go
@@ -0,0 +1,70 @@
+// Copyright 2016-2019 hxmhlt
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package config
+
+import (
+	"context"
+	"net/url"
+	"strconv"
+)
+
+import (
+	"github.com/dubbo/go-for-apache-dubbo/common"
+	"github.com/dubbo/go-for-apache-dubbo/common/constant"
+	"github.com/dubbo/go-for-apache-dubbo/common/logger"
+)
+
+type RegistryConfig struct {
+	Id         string `required:"true" yaml:"id"  json:"id,omitempty"`
+	Type       string `required:"true" yaml:"type"  json:"type,omitempty"`
+	TimeoutStr string `yaml:"timeout" default:"5s" json:"timeout,omitempty"` // unit: second
+	Group      string `yaml:"group" json:"group,omitempty"`
+	//for registry
+	Address  string `yaml:"address" json:"address,omitempty"`
+	Username string `yaml:"username" json:"address,omitempty"`
+	Password string `yaml:"password" json:"address,omitempty"`
+}
+
+func loadRegistries(registriesIds []ConfigRegistry, registries []RegistryConfig, roleType common.RoleType) []*common.URL {
+	var urls []*common.URL
+	for _, registry := range registriesIds {
+		for _, registryConf := range registries {
+			if string(registry) == registryConf.Id {
+
+				url, err := common.NewURL(context.TODO(), constant.REGISTRY_PROTOCOL+"://"+registryConf.Address, common.WithParams(registryConf.getUrlMap(roleType)),
+					common.WithUsername(registryConf.Username), common.WithPassword(registryConf.Password),
+				)
+
+				if err != nil {
+					logger.Errorf("The registry id:%s url is invalid ,and will skip the registry, error: %#v", registryConf.Id, err)
+				} else {
+					urls = append(urls, &url)
+				}
+
+			}
+		}
+
+	}
+	return urls
+}
+
+func (regconfig *RegistryConfig) getUrlMap(roleType common.RoleType) url.Values {
+	urlMap := url.Values{}
+	urlMap.Set(constant.GROUP_KEY, regconfig.Group)
+	urlMap.Set(constant.ROLE_KEY, strconv.Itoa(int(roleType)))
+	urlMap.Set(constant.REGISTRY_KEY, regconfig.Type)
+	urlMap.Set(constant.REGISTRY_TIMEOUT_KEY, regconfig.TimeoutStr)
+	return urlMap
+}
diff --git a/config/service.go b/config/service.go
new file mode 100644
index 0000000000000000000000000000000000000000..507bee4542a666de63d946509c15e320a876ed8b
--- /dev/null
+++ b/config/service.go
@@ -0,0 +1,42 @@
+// Copyright 2016-2019 Yincheng Fang
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package config
+
+import (
+	"github.com/dubbo/go-for-apache-dubbo/common"
+)
+
+var (
+	conServices = map[string]common.RPCService{} // service name -> service
+	proServices = map[string]common.RPCService{} // service name -> service
+)
+
+// SetConService is called by init() of implement of RPCService
+func SetConsumerService(service common.RPCService) {
+	conServices[service.Service()] = service
+}
+
+// SetProService is called by init() of implement of RPCService
+func SetProviderService(service common.RPCService) {
+	proServices[service.Service()] = service
+}
+
+func GetConsumerService(name string) common.RPCService {
+	return conServices[name]
+}
+
+func GetProviderService(name string) common.RPCService {
+	return proServices[name]
+}
diff --git a/config/service_config.go b/config/service_config.go
new file mode 100644
index 0000000000000000000000000000000000000000..0391a17dd1ef397870ad2fb8288a9b5cbab35bfd
--- /dev/null
+++ b/config/service_config.go
@@ -0,0 +1,163 @@
+// Copyright 2016-2019 hxmhlt
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package config
+
+import (
+	"context"
+	"net/url"
+	"strconv"
+	"strings"
+	"sync"
+	"time"
+)
+import (
+	perrors "github.com/pkg/errors"
+	"go.uber.org/atomic"
+)
+import (
+	"github.com/dubbo/go-for-apache-dubbo/common"
+	"github.com/dubbo/go-for-apache-dubbo/common/constant"
+	"github.com/dubbo/go-for-apache-dubbo/common/extension"
+	"github.com/dubbo/go-for-apache-dubbo/common/logger"
+	"github.com/dubbo/go-for-apache-dubbo/protocol"
+)
+
+type ServiceConfig struct {
+	context       context.Context
+	Filter        string           `yaml:"filter" json:"filter,omitempty"`
+	Protocol      string           `required:"true"  yaml:"protocol"  json:"protocol,omitempty"` //multi protocol support, split by ','
+	InterfaceName string           `required:"true"  yaml:"interface"  json:"interface,omitempty"`
+	Registries    []ConfigRegistry `required:"true"  yaml:"registries"  json:"registries,omitempty"`
+	Cluster       string           `default:"failover" yaml:"cluster"  json:"cluster,omitempty"`
+	Loadbalance   string           `default:"random" yaml:"loadbalance"  json:"loadbalance,omitempty"`
+	Group         string           `yaml:"group"  json:"group,omitempty"`
+	Version       string           `yaml:"version"  json:"version,omitempty"`
+	Methods       []struct {
+		Name        string `yaml:"name"  json:"name,omitempty"`
+		Retries     int64  `yaml:"retries"  json:"retries,omitempty"`
+		Loadbalance string `yaml:"loadbalance"  json:"loadbalance,omitempty"`
+		Weight      int64  `yaml:"weight"  json:"weight,omitempty"`
+	} `yaml:"methods"  json:"methods,omitempty"`
+	Warmup        string `yaml:"warmup"  json:"warmup,omitempty"`
+	Retries       int64  `yaml:"retries"  json:"retries,omitempty"`
+	unexported    *atomic.Bool
+	exported      *atomic.Bool
+	rpcService    common.RPCService
+	exporters     []protocol.Exporter
+	cacheProtocol protocol.Protocol
+	cacheMutex    sync.Mutex
+}
+
+func NewServiceConfig() *ServiceConfig {
+	return &ServiceConfig{
+		unexported: atomic.NewBool(false),
+		exported:   atomic.NewBool(false),
+	}
+
+}
+
+func (srvconfig *ServiceConfig) Export() error {
+	//TODO: config center start here
+
+	//TODO:delay export
+	if srvconfig.unexported != nil && srvconfig.unexported.Load() {
+		err := perrors.Errorf("The service %v has already unexported! ", srvconfig.InterfaceName)
+		logger.Errorf(err.Error())
+		return err
+	}
+	if srvconfig.unexported != nil && srvconfig.exported.Load() {
+		logger.Warnf("The service %v has already exported! ", srvconfig.InterfaceName)
+		return nil
+	}
+
+	regUrls := loadRegistries(srvconfig.Registries, providerConfig.Registries, common.PROVIDER)
+	urlMap := srvconfig.getUrlMap()
+
+	for _, proto := range loadProtocol(srvconfig.Protocol, providerConfig.Protocols) {
+		//registry the service reflect
+		methods, err := common.ServiceMap.Register(proto.Name, srvconfig.rpcService)
+		if err != nil {
+			err := perrors.Errorf("The service %v  export the protocol %v error! Error message is %v .", srvconfig.InterfaceName, proto.Name, err.Error())
+			logger.Errorf(err.Error())
+			return err
+		}
+		//contextPath := proto.ContextPath
+		//if contextPath == "" {
+		//	contextPath = providerConfig.Path
+		//}
+		url := common.NewURLWithOptions(srvconfig.InterfaceName,
+			common.WithProtocol(proto.Name),
+			common.WithIp(proto.Ip),
+			common.WithPort(proto.Port),
+			common.WithParams(urlMap),
+			common.WithMethods(strings.Split(methods, ",")))
+
+		for _, regUrl := range regUrls {
+			regUrl.SubURL = url
+
+			srvconfig.cacheMutex.Lock()
+			if srvconfig.cacheProtocol == nil {
+				logger.Infof("First load the registry protocol!")
+				srvconfig.cacheProtocol = extension.GetProtocol("registry")
+			}
+			srvconfig.cacheMutex.Unlock()
+
+			invoker := extension.GetProxyFactory(providerConfig.ProxyFactory).GetInvoker(*regUrl)
+			exporter := srvconfig.cacheProtocol.Export(invoker)
+			if exporter == nil {
+				panic(perrors.New("New exporter error"))
+			}
+			srvconfig.exporters = append(srvconfig.exporters, exporter)
+		}
+	}
+	return nil
+
+}
+
+func (srvconfig *ServiceConfig) Implement(s common.RPCService) {
+	srvconfig.rpcService = s
+}
+
+func (srvconfig *ServiceConfig) getUrlMap() url.Values {
+	urlMap := url.Values{}
+	urlMap.Set(constant.INTERFACE_KEY, srvconfig.InterfaceName)
+	urlMap.Set(constant.TIMESTAMP_KEY, strconv.FormatInt(time.Now().Unix(), 10))
+	urlMap.Set(constant.CLUSTER_KEY, srvconfig.Cluster)
+	urlMap.Set(constant.LOADBALANCE_KEY, srvconfig.Loadbalance)
+	urlMap.Set(constant.WARMUP_KEY, srvconfig.Warmup)
+	urlMap.Set(constant.RETRIES_KEY, strconv.FormatInt(srvconfig.Retries, 10))
+	urlMap.Set(constant.GROUP_KEY, srvconfig.Group)
+	urlMap.Set(constant.VERSION_KEY, srvconfig.Version)
+	//application info
+	urlMap.Set(constant.APPLICATION_KEY, providerConfig.ApplicationConfig.Name)
+	urlMap.Set(constant.ORGANIZATION_KEY, providerConfig.ApplicationConfig.Organization)
+	urlMap.Set(constant.NAME_KEY, providerConfig.ApplicationConfig.Name)
+	urlMap.Set(constant.MODULE_KEY, providerConfig.ApplicationConfig.Module)
+	urlMap.Set(constant.APP_VERSION_KEY, providerConfig.ApplicationConfig.Version)
+	urlMap.Set(constant.OWNER_KEY, providerConfig.ApplicationConfig.Owner)
+	urlMap.Set(constant.ENVIRONMENT_KEY, providerConfig.ApplicationConfig.Environment)
+
+	//filter
+	urlMap.Set(constant.SERVICE_FILTER_KEY, mergeValue(providerConfig.Filter, srvconfig.Filter, constant.DEFAULT_SERVICE_FILTERS))
+
+	for _, v := range srvconfig.Methods {
+		urlMap.Set("methods."+v.Name+"."+constant.LOADBALANCE_KEY, v.Loadbalance)
+		urlMap.Set("methods."+v.Name+"."+constant.RETRIES_KEY, strconv.FormatInt(v.Retries, 10))
+		urlMap.Set("methods."+v.Name+"."+constant.WEIGHT_KEY, strconv.FormatInt(v.Weight, 10))
+	}
+
+	return urlMap
+
+}
diff --git a/config/service_config_test.go b/config/service_config_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..bed7cc5b954c21f9700bff6424ac7c73e7f69df3
--- /dev/null
+++ b/config/service_config_test.go
@@ -0,0 +1,129 @@
+// Copyright 2016-2019 hxmhlt
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package config
+
+import (
+	"testing"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+import (
+	"github.com/dubbo/go-for-apache-dubbo/common/extension"
+)
+
+func doinit() {
+	providerConfig = &ProviderConfig{
+		ApplicationConfig: ApplicationConfig{
+			Organization: "dubbo_org",
+			Name:         "dubbo",
+			Module:       "module",
+			Version:      "2.6.0",
+			Owner:        "dubbo",
+			Environment:  "test"},
+		Registries: []RegistryConfig{
+			{
+				Id:         "shanghai_reg1",
+				Type:       "mock",
+				TimeoutStr: "2s",
+				Group:      "shanghai_idc",
+				Address:    "127.0.0.1:2181",
+				Username:   "user1",
+				Password:   "pwd1",
+			},
+			{
+				Id:         "shanghai_reg2",
+				Type:       "mock",
+				TimeoutStr: "2s",
+				Group:      "shanghai_idc",
+				Address:    "127.0.0.2:2181",
+				Username:   "user1",
+				Password:   "pwd1",
+			},
+			{
+				Id:         "hangzhou_reg1",
+				Type:       "mock",
+				TimeoutStr: "2s",
+				Group:      "hangzhou_idc",
+				Address:    "127.0.0.3:2181",
+				Username:   "user1",
+				Password:   "pwd1",
+			},
+			{
+				Id:         "hangzhou_reg2",
+				Type:       "mock",
+				TimeoutStr: "2s",
+				Group:      "hangzhou_idc",
+				Address:    "127.0.0.4:2181",
+				Username:   "user1",
+				Password:   "pwd1",
+			},
+		},
+		Services: []ServiceConfig{
+			{
+				InterfaceName: "MockService",
+				Protocol:      "mock",
+				Registries:    []ConfigRegistry{"shanghai_reg1", "shanghai_reg2", "hangzhou_reg1", "hangzhou_reg2"},
+				Cluster:       "failover",
+				Loadbalance:   "random",
+				Retries:       3,
+				Group:         "huadong_idc",
+				Version:       "1.0.0",
+				Methods: []struct {
+					Name        string `yaml:"name"  json:"name,omitempty"`
+					Retries     int64  `yaml:"retries"  json:"retries,omitempty"`
+					Loadbalance string `yaml:"loadbalance"  json:"loadbalance,omitempty"`
+					Weight      int64  `yaml:"weight"  json:"weight,omitempty"`
+				}{
+					{
+						Name:        "GetUser",
+						Retries:     2,
+						Loadbalance: "random",
+						Weight:      200,
+					},
+					{
+						Name:        "GetUser1",
+						Retries:     2,
+						Loadbalance: "random",
+						Weight:      200,
+					},
+				},
+			},
+		},
+		Protocols: []ProtocolConfig{
+			{
+				Name:        "mock",
+				Ip:          "127.0.0.1",
+				Port:        "20000",
+				ContextPath: "/xxx",
+			},
+		},
+	}
+}
+func Test_Export(t *testing.T) {
+	doinit()
+	extension.SetProtocol("registry", GetProtocol)
+
+	for _, service := range providerConfig.Services {
+		service.Implement(&MockService{})
+		service.Export()
+		assert.Condition(t, func() bool {
+			return len(service.exporters) > 0
+		})
+	}
+	providerConfig = nil
+}
diff --git a/config/testdata/consumer_config.yml b/config/testdata/consumer_config.yml
new file mode 100644
index 0000000000000000000000000000000000000000..8a9aeafac99da3a51a58a2dc7815b089987c867f
--- /dev/null
+++ b/config/testdata/consumer_config.yml
@@ -0,0 +1,68 @@
+# dubbo client yaml configure file
+
+filter: ""
+
+# client
+request_timeout : "100ms"
+# connect timeout
+connect_timeout : "100ms"
+check: true
+# application config
+application_config:
+  organization : "ikurento.com"
+  name  : "BDTService"
+  module : "dubbogo user-info client"
+  version : "0.0.1"
+  owner : "ZX"
+  environment : "dev"
+
+registries :
+- id: "hangzhouzk"
+  type: "zookeeper"
+  timeout	: "3s"
+  address: "127.0.0.1:2181"
+  username: ""
+  password: ""
+
+- id: "shanghaizk"
+  type: "zookeeper"
+  timeout	: "3s"
+  address: "127.0.0.1:2182"
+  username: ""
+  password: ""
+
+references:
+- registries :
+  - "hangzhouzk"
+  - "shanghaizk"
+  filter: ""
+  protocol : "dubbo"
+  interface : "com.ikurento.user.UserProvider"
+  cluster: "failover"
+  methods :
+  - name: "GetUser"
+    retries: 3
+
+protocol_conf:
+  dubbo:
+    reconnect_interval: 0
+    connection_number: 2
+    heartbeat_period: "5s"
+    session_timeout: "20s"
+    fail_fast_timeout: "5s"
+    pool_size: 64
+    pool_ttl: 600
+    getty_session_param:
+      compress_encoding: false
+      tcp_no_delay: true
+      tcp_keep_alive: true
+      keep_alive_period: "120s"
+      tcp_r_buf_size: 262144
+      tcp_w_buf_size: 65536
+      pkg_rq_size: 1024
+      pkg_wq_size: 512
+      tcp_read_timeout: "1s"
+      tcp_write_timeout: "5s"
+      wait_timeout: "1s"
+      max_msg_len: 1024
+      session_name: "client"
diff --git a/config/testdata/provider_config.yml b/config/testdata/provider_config.yml
new file mode 100644
index 0000000000000000000000000000000000000000..6c7da063695886c934eb92a300135f5374a0aa7f
--- /dev/null
+++ b/config/testdata/provider_config.yml
@@ -0,0 +1,72 @@
+# dubbo server yaml configure file
+
+filter: ""
+# application config
+application_config:
+  organization : "ikurento.com"
+  name : "BDTService"
+  module : "dubbogo user-info server"
+  version : "0.0.1"
+  owner : "ZX"
+  environment : "dev"
+
+registries :
+- id: "hangzhouzk"
+  type: "zookeeper"
+  timeout	: "3s"
+  address: "127.0.0.1:2181"
+  username: ""
+  password: ""
+
+- id: "shanghaizk"
+  type: "zookeeper"
+  timeout	: "3s"
+  address: "127.0.0.1:2182"
+  username: ""
+  password: ""
+
+
+services:
+- registries:
+  - "hangzhouzk"
+  - "shanghaizk"
+  filter: ""
+  protocol : "dubbo"
+  # equivalent to interface of dubbo.xml
+  interface : "com.ikurento.user.UserProvider"
+  loadbalance: "random"
+  warmup: "100"
+  cluster: "failover"
+  methods:
+  - name: "GetUser"
+    retries: 1
+    loadbalance: "random"
+
+protocols:
+-   name: "dubbo"
+    # while using dubbo protocol, ip cannot is 127.0.0.1, because client of java-dubbo will get 'connection refuse'
+    ip : "127.0.0.1"
+    port : 20000
+#-   name: "jsonrpc"
+#    ip: "127.0.0.1"
+#    port: 20001
+
+protocol_conf:
+  dubbo:
+    session_number: 700
+    fail_fast_timeout: "5s"
+    session_timeout: "20s"
+    getty_session_param:
+      compress_encoding: false
+      tcp_no_delay: true
+      tcp_keep_alive: true
+      keep_alive_period: "120s"
+      tcp_r_buf_size: 262144
+      tcp_w_buf_size: 65536
+      pkg_rq_size: 1024
+      pkg_wq_size: 512
+      tcp_read_timeout: "1s"
+      tcp_write_timeout: "5s"
+      wait_timeout: "1s"
+      max_msg_len: 1024
+      session_name: "server"
diff --git a/contributing.md b/contributing.md
index cce54951db45b5c8e892ed9e00ef9b3ab27f84be..b1265c2351789d4929d81556d72234806aed6afa 100644
--- a/contributing.md
+++ b/contributing.md
@@ -1,7 +1,7 @@
 Contributing to Dubbogo
 
 ## 1. Branch
- 
+
   >- The name of branches `SHOULD` be in the format of `feature/xxx`.
   >- You `SHOULD` checkout a new branch after a feature branch already being merged into upstream, `DO NOT` commit in the old branch.
 
@@ -24,3 +24,8 @@ The title format of the pull request `MUST` follow the following rules:
   >- Start with `Dep:` for adding depending libs.
   >- Start with `Rem:` for removing feature/struct/function/member/files.
 
+## 3. Code Style
+
+### 3.1 log
+
+> 1 when logging the function's input parameter, you should add '@' before input parameter name.
diff --git a/dubbo/listener.go b/dubbo/listener.go
deleted file mode 100644
index d97d88b00015111bfaf9778a1d31d00bc8dfe3cc..0000000000000000000000000000000000000000
--- a/dubbo/listener.go
+++ /dev/null
@@ -1,289 +0,0 @@
-package dubbo
-
-import (
-	"context"
-	"reflect"
-	"sync"
-	"time"
-)
-
-import (
-	"github.com/AlexStocks/getty"
-	log "github.com/AlexStocks/log4go"
-	"github.com/dubbogo/hessian2"
-	jerrors "github.com/juju/errors"
-)
-
-// todo: WritePkg_Timeout will entry *.yml
-const WritePkg_Timeout = 5 * time.Second
-
-var (
-	errTooManySessions = jerrors.New("too many sessions")
-)
-
-type rpcSession struct {
-	session getty.Session
-	reqNum  int32
-}
-
-////////////////////////////////////////////
-// RpcClientHandler
-////////////////////////////////////////////
-
-type RpcClientHandler struct {
-	conn *gettyRPCClient
-}
-
-func NewRpcClientHandler(client *gettyRPCClient) *RpcClientHandler {
-	return &RpcClientHandler{conn: client}
-}
-
-func (h *RpcClientHandler) OnOpen(session getty.Session) error {
-	h.conn.addSession(session)
-	return nil
-}
-
-func (h *RpcClientHandler) OnError(session getty.Session, err error) {
-	log.Info("session{%s} got error{%v}, will be closed.", session.Stat(), err)
-	h.conn.removeSession(session)
-}
-
-func (h *RpcClientHandler) OnClose(session getty.Session) {
-	log.Info("session{%s} is closing......", session.Stat())
-	h.conn.removeSession(session)
-}
-
-func (h *RpcClientHandler) OnMessage(session getty.Session, pkg interface{}) {
-	p, ok := pkg.(*DubboPackage)
-	if !ok {
-		log.Error("illegal package")
-		return
-	}
-
-	if p.Header.Type&hessian.Heartbeat != 0x00 {
-		log.Debug("get rpc heartbeat response{header: %#v, body: %#v}", p.Header, p.Body)
-		return
-	}
-	log.Debug("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 {
-		return
-	}
-
-	if p.Err != nil {
-		pendingResponse.err = p.Err
-	}
-
-	if pendingResponse.callback == nil {
-		pendingResponse.done <- struct{}{}
-	} else {
-		pendingResponse.callback(pendingResponse.GetCallResponse())
-	}
-}
-
-func (h *RpcClientHandler) OnCron(session getty.Session) {
-	rpcSession, err := h.conn.getClientRpcSession(session)
-	if err != nil {
-		log.Error("client.getClientSession(session{%s}) = error{%s}",
-			session.Stat(), jerrors.ErrorStack(err))
-		return
-	}
-	if h.conn.pool.rpcClient.conf.sessionTimeout.Nanoseconds() < time.Since(session.GetActive()).Nanoseconds() {
-		log.Warn("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
-	}
-
-	h.conn.pool.rpcClient.heartbeat(session)
-}
-
-////////////////////////////////////////////
-// RpcServerHandler
-////////////////////////////////////////////
-
-type RpcServerHandler struct {
-	maxSessionNum  int
-	sessionTimeout time.Duration
-	sessionMap     map[getty.Session]*rpcSession
-	rwlock         sync.RWMutex
-}
-
-func NewRpcServerHandler(maxSessionNum int, sessionTimeout time.Duration) *RpcServerHandler {
-	return &RpcServerHandler{
-		maxSessionNum:  maxSessionNum,
-		sessionTimeout: sessionTimeout,
-		sessionMap:     make(map[getty.Session]*rpcSession),
-	}
-}
-
-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 jerrors.Trace(err)
-	}
-
-	log.Info("got session:%s", session.Stat())
-	h.rwlock.Lock()
-	h.sessionMap[session] = &rpcSession{session: session}
-	h.rwlock.Unlock()
-	return nil
-}
-
-func (h *RpcServerHandler) OnError(session getty.Session, err error) {
-	log.Info("session{%s} got error{%v}, will be closed.", session.Stat(), err)
-	h.rwlock.Lock()
-	delete(h.sessionMap, session)
-	h.rwlock.Unlock()
-}
-
-func (h *RpcServerHandler) OnClose(session getty.Session) {
-	log.Info("session{%s} is closing......", session.Stat())
-	h.rwlock.Lock()
-	delete(h.sessionMap, session)
-	h.rwlock.Unlock()
-}
-
-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 {
-		log.Error("illegal packge{%#v}", pkg)
-		return
-	}
-	p.Header.ResponseStatus = hessian.Response_OK
-
-	// heartbeat
-	if p.Header.Type&hessian.Heartbeat != 0x00 {
-		log.Debug("get rpc heartbeat request{header: %#v, service: %#v, body: %#v}", p.Header, p.Service, p.Body)
-		h.reply(session, p, hessian.Heartbeat)
-		return
-	}
-
-	// twoway
-	if p.Header.Type&hessian.Request_TwoWay == 0x00 {
-		h.reply(session, p, hessian.Response)
-		h.callService(p, nil)
-		return
-	}
-
-	h.callService(p, nil)
-	h.reply(session, p, hessian.Response)
-}
-
-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
-			log.Warn("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()
-	}
-}
-
-func (h *RpcServerHandler) callService(req *DubboPackage, ctx context.Context) {
-
-	defer func() {
-		if e := recover(); e != nil {
-			req.Header.ResponseStatus = hessian.Response_BAD_REQUEST
-			if err, ok := e.(error); ok {
-				log.Error("callService panic: %#v", err)
-				req.Body = e.(error)
-			} else if err, ok := e.(string); ok {
-				log.Error("callService panic: %#v", jerrors.New(err))
-				req.Body = jerrors.New(err)
-			} else {
-				log.Error("callService panic: %#v", e)
-				req.Body = e
-			}
-		}
-	}()
-
-	svc := req.Body.(map[string]interface{})["service"].(*service)
-	method := svc.method[req.Service.Method]
-
-	// prepare argv
-	var argv reflect.Value
-	argIsValue := false // if true, need to indirect before calling.
-	if method.ArgType.Kind() == reflect.Ptr {
-		argv = reflect.New(method.ArgType.Elem())
-	} else {
-		argv = reflect.New(method.ArgType)
-		argIsValue = true
-	}
-	argvTmp := argv.Interface()
-	argvTmp = req.Body.(map[string]interface{})["args"] // type is []interface
-	if argIsValue {
-		argv = argv.Elem()
-	}
-
-	// prepare replyv
-	replyv := reflect.New(method.ReplyType.Elem())
-	var returnValues []reflect.Value
-	if method.CtxType == nil {
-		returnValues = method.method.Func.Call([]reflect.Value{svc.rcvr, reflect.ValueOf(argvTmp), reflect.ValueOf(replyv.Interface())})
-	} else {
-		if contextv := reflect.ValueOf(ctx); contextv.IsValid() {
-			returnValues = method.method.Func.Call([]reflect.Value{svc.rcvr, contextv, reflect.ValueOf(argvTmp), reflect.ValueOf(replyv.Interface())})
-		} else {
-			returnValues = method.method.Func.Call([]reflect.Value{svc.rcvr, reflect.Zero(method.CtxType), reflect.ValueOf(argvTmp), reflect.ValueOf(replyv.Interface())})
-		}
-	}
-
-	// The return value for the method is an error.
-	if retErr := returnValues[0].Interface(); retErr != nil {
-		req.Header.ResponseStatus = hessian.Response_SERVER_ERROR
-		req.Body = retErr.(error)
-	} else {
-		req.Body = replyv.Interface()
-	}
-}
-
-func (h *RpcServerHandler) reply(session getty.Session, req *DubboPackage, tp hessian.PackgeType) {
-	resp := &DubboPackage{
-		Header: hessian.DubboHeader{
-			SerialID:       req.Header.SerialID,
-			Type:           tp,
-			ID:             req.Header.ID,
-			ResponseStatus: req.Header.ResponseStatus,
-		},
-	}
-
-	if req.Header.Type&hessian.Request != 0x00 {
-		resp.Body = req.Body
-	} else {
-		resp.Body = nil
-	}
-
-	if err := session.WritePkg(resp, WritePkg_Timeout); err != nil {
-		log.Error("WritePkg error: %#v, %#v", jerrors.Trace(err), req.Header)
-	}
-}
diff --git a/dubbo/rpc.go b/dubbo/rpc.go
deleted file mode 100644
index f20fc9603eeb630a525ee8a87e638eec95aead96..0000000000000000000000000000000000000000
--- a/dubbo/rpc.go
+++ /dev/null
@@ -1,119 +0,0 @@
-package dubbo
-
-import (
-	"reflect"
-	"sync"
-	"unicode"
-	"unicode/utf8"
-)
-
-import (
-	log "github.com/AlexStocks/log4go"
-)
-
-var (
-	typeOfError = reflect.TypeOf((*error)(nil)).Elem()
-)
-
-type GettyRPCService interface {
-	Service() string // Service Interface
-	Version() string
-}
-
-type methodType struct {
-	sync.Mutex
-	method    reflect.Method
-	CtxType   reflect.Type // type of the request context
-	ArgType   reflect.Type
-	ReplyType reflect.Type
-}
-
-type service struct {
-	name     string
-	rcvr     reflect.Value
-	rcvrType reflect.Type
-	method   map[string]*methodType
-}
-
-// Is this an exported - upper case - name
-func isExported(name string) bool {
-	rune, _ := utf8.DecodeRuneInString(name)
-	return unicode.IsUpper(rune)
-}
-
-// Is this type exported or a builtin?
-func isExportedOrBuiltinType(t reflect.Type) bool {
-	for t.Kind() == reflect.Ptr {
-		t = t.Elem()
-	}
-	// PkgPath will be non-empty even for an exported type,
-	// so we need to check the type name as well.
-	return isExported(t.Name()) || t.PkgPath() == ""
-}
-
-// suitableMethods returns suitable Rpc methods of typ
-func suitableMethods(typ reflect.Type) (string, map[string]*methodType) {
-	methods := make(map[string]*methodType)
-	mts := ""
-	for m := 0; m < typ.NumMethod(); m++ {
-		method := typ.Method(m)
-		if mt := suiteMethod(method); mt != nil {
-			methods[method.Name] = mt
-			mts += method.Name + ","
-		}
-	}
-	return mts, methods
-}
-
-// suiteMethod returns a suitable Rpc methodType
-func suiteMethod(method reflect.Method) *methodType {
-	mtype := method.Type
-	mname := method.Name
-
-	// Method must be exported.
-	if method.PkgPath != "" {
-		return nil
-	}
-
-	var replyType, argType, ctxType reflect.Type
-	switch mtype.NumIn() {
-	case 3:
-		argType = mtype.In(1)
-		replyType = mtype.In(2)
-	case 4:
-		ctxType = mtype.In(1)
-		argType = mtype.In(2)
-		replyType = mtype.In(3)
-	default:
-		log.Error("method %s of mtype %v has wrong number of in parameters %d; needs exactly 3/4",
-			mname, mtype, mtype.NumIn())
-		return nil
-	}
-	// First arg need not be a pointer.
-	if !isExportedOrBuiltinType(argType) {
-		log.Error("argument type of method %q is not exported %v", mname, argType)
-		return nil
-	}
-	// Second arg must be a pointer.
-	if replyType.Kind() != reflect.Ptr {
-		log.Error("reply type of method %q is not a pointer %v", mname, replyType)
-		return nil
-	}
-	// Reply type must be exported.
-	if !isExportedOrBuiltinType(replyType) {
-		log.Error("reply type of method %s not exported{%v}", mname, replyType)
-		return nil
-	}
-	// Method needs one out.
-	if mtype.NumOut() != 1 {
-		log.Error("method %q has %d out parameters; needs exactly 1", mname, mtype.NumOut())
-		return nil
-	}
-	// The return type of the method must be error.
-	if returnType := mtype.Out(0); returnType != typeOfError {
-		log.Error("return type %s of method %q is not error", returnType, mname)
-		return nil
-	}
-
-	return &methodType{method: method, ArgType: argType, ReplyType: replyType, CtxType: ctxType}
-}
diff --git a/dubbo/server.go b/dubbo/server.go
deleted file mode 100644
index 5e2ba20f05347f83c51fa541312c870df02e262f..0000000000000000000000000000000000000000
--- a/dubbo/server.go
+++ /dev/null
@@ -1,252 +0,0 @@
-package dubbo
-
-import (
-	"fmt"
-	"net"
-	"reflect"
-	"strconv"
-)
-
-import (
-	"github.com/AlexStocks/getty"
-	"github.com/AlexStocks/goext/net"
-	log "github.com/AlexStocks/log4go"
-	jerrors "github.com/juju/errors"
-)
-
-import (
-	"github.com/dubbo/go-for-apache-dubbo/registry"
-	"github.com/dubbo/go-for-apache-dubbo/plugins"
-)
-
-type Option func(*Options)
-
-type Options struct {
-	Registry        registry.Registry
-	ConfList        []ServerConfig
-	ServiceConfList []registry.ServiceConfig
-}
-
-func newOptions(opt ...Option) Options {
-	opts := Options{}
-	for _, o := range opt {
-		o(&opts)
-	}
-
-	if opts.Registry == nil {
-		panic("server.Options.Registry is nil")
-	}
-
-	return opts
-}
-
-// Registry used for discovery
-func Registry(r registry.Registry) Option {
-	return func(o *Options) {
-		o.Registry = r
-	}
-}
-
-func ConfList(confList []ServerConfig) Option {
-	return func(o *Options) {
-		o.ConfList = confList
-		for i := 0; i < len(o.ConfList); i++ {
-			if err := o.ConfList[i].CheckValidity(); err != nil {
-				log.Error("ServerConfig check failed: ", err)
-				o.ConfList = []ServerConfig{}
-				return
-			}
-			if o.ConfList[i].IP == "" {
-				o.ConfList[i].IP, _ = gxnet.GetLocalIP()
-			}
-		}
-	}
-}
-
-func ServiceConfList(confList []registry.ServiceConfig) Option {
-	return func(o *Options) {
-		o.ServiceConfList = confList
-		if o.ServiceConfList == nil {
-			o.ServiceConfList = []registry.ServiceConfig{}
-		}
-	}
-}
-
-type serviceMap map[string]*service
-
-type Server struct {
-	opts            Options
-	indexOfConfList int
-	srvs            []serviceMap
-	tcpServerList   []getty.Server
-}
-
-func NewServer(opts ...Option) *Server {
-	options := newOptions(opts...)
-	num := len(options.ConfList)
-	servers := make([]serviceMap, len(options.ConfList))
-
-	for i := 0; i < num; i++ {
-		servers[i] = map[string]*service{}
-	}
-
-	s := &Server{
-		opts: options,
-		srvs: servers,
-	}
-
-	return s
-}
-
-// Register export services and register with the registry
-func (s *Server) Register(rcvr GettyRPCService) error {
-
-	serviceConf := plugins.DefaultProviderServiceConfig()()
-
-	opts := s.opts
-
-	serviceConf.SetService(rcvr.Service())
-	serviceConf.SetVersion(rcvr.Version())
-
-	flag := false
-	serviceNum := len(opts.ServiceConfList)
-	serverNum := len(opts.ConfList)
-	for i := 0; i < serviceNum; i++ {
-		if opts.ServiceConfList[i].Service() == serviceConf.Service() &&
-			opts.ServiceConfList[i].Version() == serviceConf.Version() {
-
-			serviceConf.SetProtocol(opts.ServiceConfList[i].Protocol())
-			serviceConf.SetGroup(opts.ServiceConfList[i].Group())
-
-			for j := 0; j < serverNum; j++ {
-				if opts.ConfList[j].Protocol == serviceConf.Protocol() {
-					rcvrName := reflect.Indirect(reflect.ValueOf(rcvr)).Type().Name()
-					svc := &service{
-						rcvrType: reflect.TypeOf(rcvr),
-						rcvr:     reflect.ValueOf(rcvr),
-					}
-					if rcvrName == "" {
-						s := "rpc.Register: no service name for type " + svc.rcvrType.String()
-						log.Error(s)
-						return jerrors.New(s)
-					}
-					if !isExported(rcvrName) {
-						s := "rpc.Register: type " + rcvrName + " is not exported"
-						log.Error(s)
-						return jerrors.New(s)
-					}
-
-					svc.name = rcvr.Service() // service name is from 'Service()'
-					if _, present := s.srvs[j][svc.name]; present {
-						return jerrors.New("rpc: service already defined: " + svc.name)
-					}
-
-					// Install the methods
-					mts, methods := suitableMethods(svc.rcvrType)
-					svc.method = methods
-
-					if len(svc.method) == 0 {
-						// To help the user, see if a pointer receiver would work.
-						mts, methods = suitableMethods(reflect.PtrTo(svc.rcvrType))
-						str := "rpc.Register: type " + rcvrName + " has no exported methods of suitable type"
-						if len(methods) != 0 {
-							str = "rpc.Register: type " + rcvrName + " has no exported methods of suitable type (" +
-								"hint: pass a pointer to value of that type)"
-						}
-						log.Error(str)
-
-						return jerrors.New(str)
-					}
-
-					s.srvs[j][svc.name] = svc
-
-					serviceConf.SetMethods(mts)
-					serviceConf.SetPath(opts.ConfList[j].Address())
-
-					err := opts.Registry.Register(serviceConf)
-					if err != nil {
-						return err
-					}
-					flag = true
-				}
-			}
-		}
-	}
-
-	if !flag {
-		return jerrors.Errorf("fail to register Handler{service:%s, version:%s}",
-			serviceConf.Service, serviceConf.Version)
-	}
-	return nil
-}
-
-func (s *Server) newSession(session getty.Session) error {
-	var (
-		ok      bool
-		tcpConn *net.TCPConn
-	)
-	conf := s.opts.ConfList[s.indexOfConfList]
-
-	if conf.GettySessionParam.CompressEncoding {
-		session.SetCompressType(getty.CompressZip)
-	}
-
-	if tcpConn, ok = session.Conn().(*net.TCPConn); !ok {
-		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)
-	}
-	tcpConn.SetReadBuffer(conf.GettySessionParam.TcpRBufSize)
-	tcpConn.SetWriteBuffer(conf.GettySessionParam.TcpWBufSize)
-
-	session.SetName(conf.GettySessionParam.SessionName)
-	session.SetMaxMsgLen(conf.GettySessionParam.MaxMsgLen)
-	session.SetPkgHandler(NewRpcServerPackageHandler(s, s.srvs[s.indexOfConfList]))
-	session.SetEventListener(NewRpcServerHandler(conf.SessionNumber, conf.sessionTimeout))
-	session.SetRQLen(conf.GettySessionParam.PkgRQSize)
-	session.SetWQLen(conf.GettySessionParam.PkgWQSize)
-	session.SetReadTimeout(conf.GettySessionParam.tcpReadTimeout)
-	session.SetWriteTimeout(conf.GettySessionParam.tcpWriteTimeout)
-	session.SetCronPeriod((int)(conf.sessionTimeout.Nanoseconds() / 1e6))
-	session.SetWaitTime(conf.GettySessionParam.waitTimeout)
-	log.Debug("app accepts new session:%s\n", session.Stat())
-
-	return nil
-}
-
-func (s *Server) Start() {
-	var (
-		addr      string
-		tcpServer getty.Server
-	)
-
-	if len(s.opts.ConfList) == 0 {
-		panic("ConfList is nil")
-	}
-
-	for i := 0; i < len(s.opts.ConfList); i++ {
-		addr = gxnet.HostAddress2(s.opts.ConfList[i].IP, strconv.Itoa(s.opts.ConfList[i].Port))
-		tcpServer = getty.NewTCPServer(
-			getty.WithLocalAddress(addr),
-		)
-		s.indexOfConfList = i
-		tcpServer.RunEventLoop(s.newSession)
-		log.Debug("s bind addr{%s} ok!", addr)
-		s.tcpServerList = append(s.tcpServerList, tcpServer)
-	}
-
-}
-
-func (s *Server) Stop() {
-	list := s.tcpServerList
-	s.tcpServerList = nil
-	if list != nil {
-		for _, tcpServer := range list {
-			tcpServer.Close()
-		}
-	}
-}
diff --git a/examples/README.md b/examples/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..c71e843e060a198f23b747fa80adee81554bcc0d
--- /dev/null
+++ b/examples/README.md
@@ -0,0 +1,76 @@
+# examples
+
+Examples of go-for-apache-dubbo
+
+## dubbo
+
+#### Build by these command
+
+java server
+```bash
+cd dubbo/java-server
+sh build.sh
+```
+
+java client
+```bash
+cd dubbo/java-client
+sh build.sh
+```
+
+go server
+```bash
+cd dubbo/go-server
+#linux, mac windows represent the os
+#release, dev and test represent the environment
+sh ./assembly/linux/release.sh
+```
+
+go client
+```bash
+cd dubbo/go-client
+#linux, mac windows represent the os
+#release, dev and test represent the environment
+sh ./assembly/linux/release.sh
+```
+
+#### Run by these command:
+
+java server
+```bash
+cd dubbo/java-server/target
+tar -zxvf user-info-server-0.2.0-assembly.tar.gz
+cd ./user-info-server-0.2.0
+sh ./bin/server.sh start
+```
+
+java client
+```bash
+cd dubbo/java-client/target
+tar -zxvf user-info-client-0.2.0-assembly.tar.gz
+cd ./user-info-client-0.2.0
+sh ./bin/server.sh start
+```
+
+go server
+> It must not listen on IP 127.0.0.1 when called by java-client.
+> You should change IP in dubbo/go-server/target/linux/user_info_server-0.3.1-20190517-0930-release/conf/server.yml
+```bash
+cd dubbo/go-server/target/linux/user_info_server-0.3.1-20190517-0930-release
+#conf suffix appoint config file, 
+#such as server_zookeeper.yml when "sh ./bin/load.sh start is zookeeper", 
+#default server.yml
+sh ./bin/load.sh start [conf suffix]
+```
+
+go client
+```bash
+cd dubbo/go-client/target/linux/user_info_client-0.3.1-20190517-0921-release
+#conf suffix appoint config file, 
+#such as client_zookeeper.yml when "sh ./bin/load.sh start is zookeeper", 
+#default client.yml
+sh ./bin/load_user_info_client.sh start [conf suffix]
+```
+
+## jsonrpc
+Similar to dubbo
diff --git a/examples/client_config.go b/examples/client_config.go
deleted file mode 100644
index dfec7cb99285477eea6ce8cb4922658cd979fb45..0000000000000000000000000000000000000000
--- a/examples/client_config.go
+++ /dev/null
@@ -1,121 +0,0 @@
-package examples
-
-import (
-	"fmt"
-	"io/ioutil"
-	"os"
-	"path"
-	"time"
-)
-
-import (
-	"github.com/AlexStocks/goext/log"
-	log "github.com/AlexStocks/log4go"
-	jerrors "github.com/juju/errors"
-	"gopkg.in/yaml.v2"
-)
-
-import (
-	"github.com/dubbo/go-for-apache-dubbo/plugins"
-	"github.com/dubbo/go-for-apache-dubbo/registry"
-	"github.com/dubbo/go-for-apache-dubbo/registry/zookeeper"
-)
-
-const (
-	APP_CONF_FILE     = "APP_CONF_FILE"
-	APP_LOG_CONF_FILE = "APP_LOG_CONF_FILE"
-)
-
-type (
-	// Client holds supported types by the multiconfig package
-	ClientConfig struct {
-		// pprof
-		Pprof_Enabled bool `default:"false" yaml:"pprof_enabled" json:"pprof_enabled,omitempty"`
-		Pprof_Port    int  `default:"10086"  yaml:"pprof_port" json:"pprof_port,omitempty"`
-
-		// client
-		Connect_Timeout string `default:"100ms"  yaml:"connect_timeout" json:"connect_timeout,omitempty"`
-		ConnectTimeout  time.Duration
-
-		Request_Timeout string `yaml:"request_timeout" default:"5s" json:"request_timeout,omitempty"` // 500ms, 1m
-		RequestTimeout  time.Duration
-
-		// codec & selector & transport & registry
-		Selector     string `default:"cache"  yaml:"selector" json:"selector,omitempty"`
-		Selector_TTL string `default:"10m"  yaml:"selector_ttl" json:"selector_ttl,omitempty"`
-		//client load balance algorithm
-		ClientLoadBalance string `default:"round_robin"  yaml:"client_load_balance" json:"client_load_balance,omitempty"`
-		Registry          string `default:"zookeeper"  yaml:"registry" json:"registry,omitempty"`
-		// application
-		Application_Config registry.ApplicationConfig `yaml:"application_config" json:"application_config,omitempty"`
-		ZkRegistryConfig   zookeeper.ZkRegistryConfig `yaml:"zk_registry_config" json:"zk_registry_config,omitempty"`
-		// 涓€涓鎴风鍙厑璁镐娇鐢ㄤ竴涓猻ervice鐨勫叾涓竴涓猤roup鍜屽叾涓竴涓獀ersion
-		ServiceConfigType    string                   `default:"default" yaml:"service_config_type" json:"service_config_type,omitempty"`
-		ServiceConfigList    []registry.ServiceConfig `yaml:"-"`
-		ServiceConfigMapList []map[string]string      `yaml:"service_list" json:"service_list,omitempty"`
-	}
-)
-
-func InitClientConfig() *ClientConfig {
-
-	var (
-		clientConfig *ClientConfig
-		confFile     string
-	)
-
-	// configure
-	confFile = os.Getenv(APP_CONF_FILE)
-	if confFile == "" {
-		panic(fmt.Sprintf("application configure file name is nil"))
-		return nil // I know it is of no usage. Just Err Protection.
-	}
-	if path.Ext(confFile) != ".yml" {
-		panic(fmt.Sprintf("application configure file name{%v} suffix must be .yml", confFile))
-		return nil
-	}
-	clientConfig = new(ClientConfig)
-
-	confFileStream, err := ioutil.ReadFile(confFile)
-	if err != nil {
-		panic(fmt.Sprintf("ioutil.ReadFile(file:%s) = error:%s", confFile, jerrors.ErrorStack(err)))
-		return nil
-	}
-	err = yaml.Unmarshal(confFileStream, clientConfig)
-	if err != nil {
-		panic(fmt.Sprintf("yaml.Unmarshal() = error:%s", jerrors.ErrorStack(err)))
-		return nil
-	}
-
-	//鍔ㄦ€佸姞杞絪ervice config
-	//璁剧疆榛樿ProviderServiceConfig绫�
-	plugins.SetDefaultServiceConfig(clientConfig.ServiceConfigType)
-
-	for _, service := range clientConfig.ServiceConfigMapList {
-		svc := plugins.DefaultServiceConfig()()
-		svc.SetProtocol(service["protocol"])
-		svc.SetService(service["service"])
-		clientConfig.ServiceConfigList = append(clientConfig.ServiceConfigList, svc)
-	}
-	//鍔ㄦ€佸姞杞絪ervice config  end
-
-	if clientConfig.ZkRegistryConfig.Timeout, err = time.ParseDuration(clientConfig.ZkRegistryConfig.TimeoutStr); err != nil {
-		panic(fmt.Sprintf("time.ParseDuration(Registry_Config.Timeout:%#v) = error:%s", clientConfig.ZkRegistryConfig.TimeoutStr, err))
-		return nil
-	}
-
-	gxlog.CInfo("config{%#v}\n", clientConfig)
-
-	// log
-	confFile = os.Getenv(APP_LOG_CONF_FILE)
-	if confFile == "" {
-		panic(fmt.Sprintf("log configure file name is nil"))
-		return nil
-	}
-	if path.Ext(confFile) != ".xml" {
-		panic(fmt.Sprintf("log configure file name{%v} suffix must be .xml", confFile))
-		return nil
-	}
-	log.LoadConfiguration(confFile)
-
-	return clientConfig
-}
diff --git a/examples/dubbo/go-client/app/client.go b/examples/dubbo/go-client/app/client.go
index b143e9a724ccd1d07dc61a6dff8d5b870c7f3f32..c95654267dd6ba2a8400888e0fcb6d299fad0cf8 100644
--- a/examples/dubbo/go-client/app/client.go
+++ b/examples/dubbo/go-client/app/client.go
@@ -1,170 +1,111 @@
+// Copyright 2016-2019 Yincheng Fang, Alex Stocks
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package main
 
 import (
+	"context"
 	"fmt"
-	"net/http"
-	_ "net/http/pprof"
 	"os"
 	"os/signal"
-	"strconv"
 	"syscall"
 	"time"
 )
 
 import (
-	"github.com/AlexStocks/goext/log"
-	"github.com/AlexStocks/goext/net"
-	log "github.com/AlexStocks/log4go"
-	jerrors "github.com/juju/errors"
+	"github.com/dubbo/go-for-apache-dubbo/common/logger"
+	"github.com/dubbogo/hessian2"
 )
 
 import (
-	"github.com/dubbo/go-for-apache-dubbo/client/invoker"
-	"github.com/dubbo/go-for-apache-dubbo/examples"
-	"github.com/dubbo/go-for-apache-dubbo/public"
-	"github.com/dubbo/go-for-apache-dubbo/registry"
-	"github.com/dubbo/go-for-apache-dubbo/dubbo"
-	"github.com/dubbo/go-for-apache-dubbo/plugins"
-	"github.com/dubbo/go-for-apache-dubbo/registry/zookeeper"
+	_ "github.com/dubbo/go-for-apache-dubbo/common/proxy/proxy_factory"
+	"github.com/dubbo/go-for-apache-dubbo/config"
+	_ "github.com/dubbo/go-for-apache-dubbo/protocol/dubbo"
+	_ "github.com/dubbo/go-for-apache-dubbo/registry/protocol"
+
+	_ "github.com/dubbo/go-for-apache-dubbo/filter/impl"
+
+	_ "github.com/dubbo/go-for-apache-dubbo/cluster/cluster_impl"
+	_ "github.com/dubbo/go-for-apache-dubbo/cluster/loadbalance"
+	_ "github.com/dubbo/go-for-apache-dubbo/registry/zookeeper"
 )
 
 var (
 	survivalTimeout int = 10e9
-	clientInvoker   *invoker.Invoker
 )
 
+// they are necessary:
+// 		export CONF_CONSUMER_FILE_PATH="xxx"
+// 		export APP_LOG_CONF_FILE="xxx"
 func main() {
 
-	clientConfig := examples.InitClientConfig()
-	initProfiling(clientConfig)
-	initClient(clientConfig)
-
-	time.Sleep(3e9)
-
-	gxlog.CInfo("\n\n\nstart to test dubbo")
-	testDubborpc(clientConfig, "A003")
-
-	time.Sleep(3e9)
-
-	initSignal()
-}
+	hessian.RegisterJavaEnum(Gender(MAN))
+	hessian.RegisterJavaEnum(Gender(WOMAN))
+	hessian.RegisterPOJO(&User{})
 
-func initClient(clientConfig *examples.ClientConfig) {
-	var (
-		err       error
-		codecType public.CodecType
-	)
-
-	if clientConfig == nil {
-		panic(fmt.Sprintf("clientConfig is nil"))
-		return
+	conMap, _ := config.Load()
+	if conMap == nil {
+		panic("conMap is nil")
 	}
 
-	// registry
-	clientRegistry, err := plugins.PluggableRegistries[clientConfig.Registry](
-		registry.WithDubboType(registry.CONSUMER),
-		registry.WithApplicationConf(clientConfig.Application_Config),
-		zookeeper.WithRegistryConf(clientConfig.ZkRegistryConfig),
-	)
+	println("\n\n\necho")
+	res, err := conMap["com.ikurento.user.UserProvider"].GetRPCService().(*UserProvider).Echo(context.TODO(), "OK")
 	if err != nil {
-		panic(fmt.Sprintf("fail to init registry.Registy, err:%s", jerrors.ErrorStack(err)))
-		return
+		panic(err)
 	}
+	println("res: %v\n", res)
 
-	// consumer
-	clientConfig.RequestTimeout, err = time.ParseDuration(clientConfig.Request_Timeout)
-	if err != nil {
-		panic(fmt.Sprintf("time.ParseDuration(Request_Timeout{%#v}) = error{%v}",
-			clientConfig.Request_Timeout, err))
-		return
-	}
-	clientConfig.ConnectTimeout, err = time.ParseDuration(clientConfig.Connect_Timeout)
+	time.Sleep(3e9)
+
+	println("\n\n\nstart to test dubbo")
+	user := &User{}
+	err = conMap["com.ikurento.user.UserProvider"].GetRPCService().(*UserProvider).GetUser(context.TODO(), []interface{}{"A003"}, user)
 	if err != nil {
-		panic(fmt.Sprintf("time.ParseDuration(Connect_Timeout{%#v}) = error{%v}",
-			clientConfig.Connect_Timeout, err))
-		return
+		panic(err)
 	}
+	println("response result: %v", user)
 
-	for idx := range clientConfig.ServiceConfigList {
-		codecType = public.GetCodecType(clientConfig.ServiceConfigList[idx].Protocol())
-		if codecType == public.CODECTYPE_UNKNOWN {
-			panic(fmt.Sprintf("unknown protocol %s", clientConfig.ServiceConfigList[idx].Protocol()))
-		}
+	println("\n\n\nstart to test dubbo - GetUser0")
+	ret, err := conMap["com.ikurento.user.UserProvider"].GetRPCService().(*UserProvider).GetUser0("A003", "Moorse")
+	if err != nil {
+		panic(err)
 	}
+	println("response result: %v", ret)
 
-	for _, service := range clientConfig.ServiceConfigList {
-		err = clientRegistry.Register(service)
-		if err != nil {
-			panic(fmt.Sprintf("registry.Register(service{%#v}) = error{%v}", service, jerrors.ErrorStack(err)))
-			return
-		}
+	println("\n\n\nstart to test dubbo - GetUsers")
+	ret1, err := conMap["com.ikurento.user.UserProvider"].GetRPCService().(*UserProvider).GetUsers([]interface{}{[]interface{}{"A002", "A003"}})
+	if err != nil {
+		panic(err)
 	}
+	println("response result: %v", ret1)
 
-	//read the client lb config in config.yml
-	configClientLB := plugins.PluggableLoadbalance[clientConfig.ClientLoadBalance]()
-
-	//init dubbo rpc client & init invoker
-	var cltD *dubbo.Client
-
-	cltD, err = dubbo.NewClient(&dubbo.ClientConfig{
-		PoolSize:        64,
-		PoolTTL:         600,
-		ConnectionNum:   2, // 涓嶈兘澶ぇ
-		FailFastTimeout: "5s",
-		SessionTimeout:  "20s",
-		HeartbeatPeriod: "5s",
-		GettySessionParam: dubbo.GettySessionParam{
-			CompressEncoding: false, // 蹇呴』false
-			TcpNoDelay:       true,
-			KeepAlivePeriod:  "120s",
-			TcpRBufSize:      262144,
-			TcpKeepAlive:     true,
-			TcpWBufSize:      65536,
-			PkgRQSize:        1024,
-			PkgWQSize:        512,
-			TcpReadTimeout:   "1s",
-			TcpWriteTimeout:  "5s",
-			WaitTimeout:      "1s",
-			MaxMsgLen:        1024,
-			SessionName:      "client",
-		},
-	})
+	println("\n\n\nstart to test dubbo - getUser")
+	user = &User{}
+	err = conMap["com.ikurento.user.UserProvider"].GetRPCService().(*UserProvider).GetUser2(context.TODO(), []interface{}{1}, user)
 	if err != nil {
-		log.Error("hessian.NewClient(conf) = error:%s", jerrors.ErrorStack(err))
-		return
+		println("getUser - error: %v", err)
+	} else {
+		println("response result: %v", user)
 	}
-	clientInvoker, err = invoker.NewInvoker(clientRegistry,
-		invoker.WithDubboClient(cltD),
-		invoker.WithLBSelector(configClientLB))
-}
-
-func uninitClient() {
-	log.Close()
-}
 
-func initProfiling(clientConfig *examples.ClientConfig) {
-	if !clientConfig.Pprof_Enabled {
-		return
-	}
-	const (
-		PprofPath = "/debug/pprof/"
-	)
-	var (
-		err  error
-		ip   string
-		addr string
-	)
-
-	ip, err = gxnet.GetLocalIP()
+	println("\n\n\nstart to test dubbo illegal method")
+	err = conMap["com.ikurento.user.UserProvider"].GetRPCService().(*UserProvider).GetUser1(context.TODO(), []interface{}{"A003"}, user)
 	if err != nil {
-		panic("cat not get local ip!")
+		panic(err)
 	}
-	addr = ip + ":" + strconv.Itoa(clientConfig.Pprof_Port)
-	log.Info("App Profiling startup on address{%v}", addr+PprofPath)
 
-	go func() {
-		log.Info(http.ListenAndServe(addr, nil))
-	}()
+	initSignal()
 }
 
 func initSignal() {
@@ -174,20 +115,23 @@ func initSignal() {
 		syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)
 	for {
 		sig := <-signals
-		log.Info("get signal %s", sig.String())
+		logger.Infof("get signal %s", sig.String())
 		switch sig {
 		case syscall.SIGHUP:
-		// reload()
+			// reload()
 		default:
 			go time.AfterFunc(time.Duration(survivalTimeout)*time.Second, func() {
-				log.Warn("app exit now by force...")
+				logger.Warnf("app exit now by force...")
 				os.Exit(1)
 			})
 
 			// 瑕佷箞fastFailTimeout鏃堕棿鍐呮墽琛屽畬姣曚笅闈㈢殑閫昏緫鐒跺悗绋嬪簭閫€鍑猴紝瑕佷箞鎵ц涓婇潰鐨勮秴鏃跺嚱鏁扮▼搴忓己琛岄€€鍑�
-			uninitClient()
 			fmt.Println("app exit now...")
 			return
 		}
 	}
 }
+
+func println(format string, args ...interface{}) {
+	fmt.Printf("\033[32;40m"+format+"\033[0m\n", args...)
+}
diff --git a/examples/dubbo/go-client/app/test.go b/examples/dubbo/go-client/app/test.go
deleted file mode 100644
index 507f3e592bb4c7288cc31dbb9589979541ede91a..0000000000000000000000000000000000000000
--- a/examples/dubbo/go-client/app/test.go
+++ /dev/null
@@ -1,59 +0,0 @@
-package main
-
-import (
-	"fmt"
-	_ "net/http/pprof"
-)
-
-import (
-	// "github.com/AlexStocks/goext/log"
-	log "github.com/AlexStocks/log4go"
-	"github.com/dubbogo/hessian2"
-	jerrors "github.com/juju/errors"
-)
-
-import (
-	"github.com/dubbo/go-for-apache-dubbo/dubbo"
-	"github.com/dubbo/go-for-apache-dubbo/examples"
-	"github.com/dubbo/go-for-apache-dubbo/public"
-)
-
-func testDubborpc(clientConfig *examples.ClientConfig, userKey string) {
-	var (
-		err        error
-		svc        string
-		method     string
-		serviceIdx int
-		user       *DubboUser
-	)
-	serviceIdx = -1
-	svc = "com.ikurento.user.UserProvider"
-	for i := range clientConfig.ServiceConfigList {
-		if clientConfig.ServiceConfigList[i].Service() == svc && clientConfig.ServiceConfigList[i].Protocol() == public.CODECTYPE_DUBBO.String() {
-			serviceIdx = i
-			break
-		}
-	}
-	if serviceIdx == -1 {
-		panic(fmt.Sprintf("can not find service in config service list:%#v", clientConfig.ServiceConfigList))
-	}
-
-	// Create request
-	method = string("GetUser")
-
-	// registry pojo
-	hessian.RegisterJavaEnum(Gender(MAN))
-	hessian.RegisterJavaEnum(Gender(WOMAN))
-	hessian.RegisterPOJO(&DubboUser{})
-
-	user = new(DubboUser)
-	defer clientInvoker.DubboClient.Close()
-	err = clientInvoker.DubboCall(1, clientConfig.ServiceConfigList[serviceIdx], method, []interface{}{userKey}, user, dubbo.WithCallRequestTimeout(10e9), dubbo.WithCallResponseTimeout(10e9), dubbo.WithCallSerialID(dubbo.S_Dubbo))
-	// Call service
-	if err != nil {
-		log.Error("client.Call() return error:%+v", jerrors.ErrorStack(err))
-		return
-	}
-
-	log.Info("response result:%s", user)
-}
diff --git a/examples/dubbo/go-client/app/user.go b/examples/dubbo/go-client/app/user.go
index bc2212f532e9e4d349d92df6fe55d2432931b07a..7e9d3daedf756b79318160995ace0d8222b39b05 100644
--- a/examples/dubbo/go-client/app/user.go
+++ b/examples/dubbo/go-client/app/user.go
@@ -1,6 +1,21 @@
+// Copyright 2016-2019 Yincheng Fang, Alex Stocks
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package main
 
 import (
+	"context"
 	"fmt"
 	"strconv"
 	"time"
@@ -10,8 +25,16 @@ import (
 	"github.com/dubbogo/hessian2"
 )
 
+import (
+	"github.com/dubbo/go-for-apache-dubbo/config"
+)
+
 type Gender hessian.JavaEnum
 
+func init() {
+	config.SetConsumerService(new(UserProvider))
+}
+
 const (
 	MAN hessian.JavaEnum = iota
 	WOMAN
@@ -49,7 +72,8 @@ func (g Gender) EnumValue(s string) hessian.JavaEnum {
 	return hessian.InvalidJavaEnum
 }
 
-type DubboUser struct {
+type User struct {
+	// !!! Cannot define lowercase names of variable
 	Id   string
 	Name string
 	Age  int32
@@ -57,13 +81,30 @@ type DubboUser struct {
 	Sex  Gender // 娉ㄦ剰姝ゅ锛宩ava enum Object <--> go string
 }
 
-func (u DubboUser) String() string {
+func (u User) String() string {
 	return fmt.Sprintf(
 		"User{Id:%s, Name:%s, Age:%d, Time:%s, Sex:%s}",
 		u.Id, u.Name, u.Age, u.Time, u.Sex,
 	)
 }
 
-func (DubboUser) JavaClassName() string {
+func (User) JavaClassName() string {
 	return "com.ikurento.user.User"
 }
+
+type UserProvider struct {
+	GetUsers func(req []interface{}) ([]interface{}, error)
+	GetUser  func(ctx context.Context, req []interface{}, rsp *User) error
+	GetUser0 func(id string, name string) (User, error)
+	GetUser1 func(ctx context.Context, req []interface{}, rsp *User) error
+	GetUser2 func(ctx context.Context, req []interface{}, rsp *User) error   `dubbo:"getUser"`
+	Echo     func(ctx context.Context, req interface{}) (interface{}, error) // Echo represent EchoFilter will be used
+}
+
+func (u *UserProvider) Service() string {
+	return "com.ikurento.user.UserProvider"
+}
+
+func (u *UserProvider) Version() string {
+	return ""
+}
diff --git a/examples/dubbo/go-client/assembly/bin/load.sh b/examples/dubbo/go-client/assembly/bin/load.sh
index 72127283082e62d9d2bdf2a4b4934db56674535b..07d5d15eac7b7974845e36c3807e7ec55875de65 100644
--- a/examples/dubbo/go-client/assembly/bin/load.sh
+++ b/examples/dubbo/go-client/assembly/bin/load.sh
@@ -23,13 +23,13 @@ else
     APP_NAME="APPLICATION_NAME.exe"
 fi
 
-export APP_CONF_FILE=${PROJECT_HOME}"TARGET_CONF_FILE"
+export CONF_CONSUMER_FILE_PATH=${PROJECT_HOME}"TARGET_CONF_FILE"
 export APP_LOG_CONF_FILE=${PROJECT_HOME}"TARGET_LOG_CONF_FILE"
 # export GOTRACEBACK=system
 # export GODEBUG=gctrace=1
 
 usage() {
-    echo "Usage: $0 start"
+    echo "Usage: $0 start [conf suffix]"
     echo "       $0 stop"
     echo "       $0 term"
     echo "       $0 restart"
@@ -40,6 +40,16 @@ usage() {
 }
 
 start() {
+    arg=$1
+    if [ "$arg" = "" ];then
+        echo "No registry type! Default client.yml!"
+    else
+        export CONF_CONSUMER_FILE_PATH=${CONF_CONSUMER_FILE_PATH//\.yml/\_$arg\.yml}
+    fi
+    if [ ! -f "${CONF_CONSUMER_FILE_PATH}" ];then
+        echo $CONF_CONSUMER_FILE_PATH" is not existing!"
+        return
+    fi
     APP_LOG_PATH=${PROJECT_HOME}"logs/"
     mkdir -p ${APP_LOG_PATH}
     APP_BIN=${PROJECT_HOME}sbin/${APP_NAME}
@@ -158,7 +168,7 @@ crontab() {
 opt=$1
 case C"$opt" in
     Cstart)
-        start
+        start $2
         ;;
     Cstop)
         stop
@@ -168,7 +178,7 @@ case C"$opt" in
         ;;
     Crestart)
         term
-        start
+        start $2
         ;;
     Clist)
         list
diff --git a/examples/dubbo/go-client/assembly/common/app.properties b/examples/dubbo/go-client/assembly/common/app.properties
index a4fe0dc49c83e7c180408b02010ebf4bbefc98a9..6bbd6db850ceeaf5ff873fee01a3578237cbd557 100644
--- a/examples/dubbo/go-client/assembly/common/app.properties
+++ b/examples/dubbo/go-client/assembly/common/app.properties
@@ -14,4 +14,4 @@ export TARGET_EXEC_NAME="user_info_client"
 export BUILD_PACKAGE="app"
 
 export TARGET_CONF_FILE="conf/client.yml"
-export TARGET_LOG_CONF_FILE="conf/log.xml"
+export TARGET_LOG_CONF_FILE="conf/log.yml"
diff --git a/examples/dubbo/go-client/profiles/dev/client.yml b/examples/dubbo/go-client/profiles/dev/client.yml
index 9e7f65f36870b13ef9cd433e98480e0bf4ff8d4c..93ce354c9c65682d393619a6afa1ae449b96660d 100644
--- a/examples/dubbo/go-client/profiles/dev/client.yml
+++ b/examples/dubbo/go-client/profiles/dev/client.yml
@@ -1,20 +1,10 @@
 # dubbo client yaml configure file
 
-# pprof
-pprof_enabled : true
-pprof_port : 10086
-
 # client
-request_timeout : "3500ms"
-net_io_timeout : "2s"
-retries : 1
+request_timeout : "100ms"
 # connect timeout
 connect_timeout : "100ms"
-selector : "cache"
-selector_ttl : "10m"
-registry : "zookeeper"
-client_load_balance: "round_robin"
-
+check: true
 # application config
 application_config:
     organization : "ikurento.com"
@@ -24,12 +14,53 @@ application_config:
     owner : "ZX"
     environment : "dev"
 
-zk_registry_config:
-    timeout	: "3s"
-    address:
-        - "127.0.0.1:2181"
-service_config_type: "default"
-service_list:
-    -
-        protocol : "dubbo"
-        service : "com.ikurento.user.UserProvider"
+registries :
+- id: "hangzhouzk"
+  type: "zookeeper"
+  timeout	: "3s"
+  address: "127.0.0.1:2181"
+  username: ""
+  password: ""
+
+- id: "shanghaizk"
+  type: "zookeeper"
+  timeout	: "3s"
+  address: "127.0.0.1:2182"
+  username: ""
+  password: ""
+
+references:
+- registries :
+  - "hangzhouzk"
+  - "shanghaizk"
+
+  protocol : "dubbo"
+  interface : "com.ikurento.user.UserProvider"
+  cluster: "failover"
+  methods :
+  - name: "GetUser"
+    retries: 3
+
+protocol_conf:
+  dubbo:
+    reconnect_interval: 0
+    connection_number: 2
+    heartbeat_period: "5s"
+    session_timeout: "20s"
+    fail_fast_timeout: "5s"
+    pool_size: 64
+    pool_ttl: 600
+    getty_session_param:
+      compress_encoding: false
+      tcp_no_delay: true
+      tcp_keep_alive: true
+      keep_alive_period: "120s"
+      tcp_r_buf_size: 262144
+      tcp_w_buf_size: 65536
+      pkg_rq_size: 1024
+      pkg_wq_size: 512
+      tcp_read_timeout: "1s"
+      tcp_write_timeout: "5s"
+      wait_timeout: "1s"
+      max_msg_len: 10240
+      session_name: "client"
diff --git a/examples/dubbo/go-client/profiles/dev/log.xml b/examples/dubbo/go-client/profiles/dev/log.xml
deleted file mode 100644
index d2a0d89394aa2b5a882924752d9b7bab7f424dc7..0000000000000000000000000000000000000000
--- a/examples/dubbo/go-client/profiles/dev/log.xml
+++ /dev/null
@@ -1,73 +0,0 @@
-<logging>
-  <filter enabled="true">
-    <tag>stdout</tag>
-    <type>console</type>
-    <!-- level is (:?FINEST|FINE|DEBUG|TRACE|INFO|WARNING|ERROR) -->
-    <level>DEBUG</level>
-    <property name="json">false</property> <!-- true enables json log format, its priority is high than format -->
-    <property name="format">[%D %T] [%L] (%S) %M</property> <!-- log format, if json is false this option is enable -->
-  </filter>
-  <filter enabled="true">
-    <tag>debug_file</tag>
-    <type>file</type>
-    <level>DEBUG</level>
-    <property name="filename">logs/debug.log</property>
-    <property name="json">false</property> <!-- true enables json log format, its priority is high than format -->
-    <property name="format">[%D %T] [%L] [%S] %M</property>
-    <property name="rotate">true</property> <!-- true enables log rotation, otherwise append -->
-    <property name="maxsize">0M</property> <!-- \d+[KMG]? Suffixes are in terms of 2**10 -->
-    <property name="maxlines">0K</property> <!-- \d+[KMG]? Suffixes are in terms of thousands -->
-    <property name="maxbackup">16</property> <!-- \d+ -->
-    <property name="daily">true</property> <!-- Automatically rotates when a log message is written after midnight -->
-  </filter>
-  <filter enabled="true">
-    <tag>info_file</tag>
-    <type>file</type>
-    <level>INFO</level>
-    <property name="filename">logs/info.log</property>
-    <!--
-       %T - Time (15:04:05 MST)
-       %t - Time (15:04)
-       %D - Date (2006/01/02)
-       %d - Date (01/02/06)
-       %L - Level (FNST, FINE, DEBG, TRAC, WARN, EROR, CRIT)
-       %S - Source
-       %M - Message
-       It ignores unknown format strings (and removes them)
-       Recommended: "[%D %T] [%L] (%S) %M"
-    -->
-    <property name="json">false</property> <!-- true enables json log format, its priority is high than format -->
-    <property name="format">[%D %T] [%L] [%S] %M</property>
-    <property name="rotate">true</property> <!-- true enables log rotation, otherwise append -->
-    <property name="maxsize">0M</property> <!-- \d+[KMG]? Suffixes are in terms of 2**10 -->
-    <property name="maxlines">0K</property> <!-- \d+[KMG]? Suffixes are in terms of thousands -->
-    <property name="maxbackup">16</property> <!-- \d+ -->
-    <property name="daily">true</property> <!-- Automatically rotates when a log message is written after midnight -->
-  </filter>
-  <filter enabled="true">
-    <tag>warn_file</tag>
-    <type>file</type>
-    <level>WARNING</level>
-    <property name="filename">logs/warn.log</property>
-    <property name="json">false</property> <!-- true enables json log format, its priority is high than format -->
-    <property name="format">[%D %T] [%L] [%S] %M</property>
-    <property name="rotate">true</property> <!-- true enables log rotation, otherwise append -->
-    <property name="maxsize">0M</property> <!-- \d+[KMG]? Suffixes are in terms of 2**10 -->
-    <property name="maxlines">0K</property> <!-- \d+[KMG]? Suffixes are in terms of thousands -->
-    <property name="maxbackup">16</property> <!-- \d+ -->
-    <property name="daily">true</property> <!-- Automatically rotates when a log message is written after midnight -->
-  </filter>
-  <filter enabled="true">
-    <tag>error_file</tag>
-    <type>file</type>
-    <level>ERROR</level>
-    <property name="filename">logs/error.log</property>
-    <property name="json">false</property> <!-- true enables json log format, its priority is high than format -->
-    <property name="format">[%D %T] [%L] [%S] %M</property>
-    <property name="rotate">true</property> <!-- true enables log rotation, otherwise append -->
-    <property name="maxsize">0M</property> <!-- \d+[KMG]? Suffixes are in terms of 2**10 -->
-    <property name="maxlines">0K</property> <!-- \d+[KMG]? Suffixes are in terms of thousands -->
-    <property name="maxbackup">16</property> <!-- \d+ -->
-    <property name="daily">true</property> <!-- Automatically rotates when a log message is written after midnight -->
-  </filter>
-</logging>
diff --git a/examples/dubbo/go-client/profiles/dev/log.yml b/examples/dubbo/go-client/profiles/dev/log.yml
new file mode 100644
index 0000000000000000000000000000000000000000..427308d52b028d1740dac56b66b2e54fa76c6fe2
--- /dev/null
+++ b/examples/dubbo/go-client/profiles/dev/log.yml
@@ -0,0 +1,28 @@
+
+level: "debug"
+development: true
+disableCaller: false
+disableStacktrace: false
+sampling:
+encoding: "console"
+
+# encoder
+encoderConfig:
+  messageKey: "message"
+  levelKey: "level"
+  timeKey: "time"
+  nameKey: "logger"
+  callerKey: "caller"
+  stacktraceKey: "stacktrace"
+  lineEnding: ""
+  levelEncoder: "capitalColor"
+  timeEncoder: "iso8601"
+  durationEncoder: "seconds"
+  callerEncoder: "short"
+  nameEncoder: ""
+
+outputPaths:
+- "stderr"
+errorOutputPaths:
+- "stderr"
+initialFields:
diff --git a/examples/dubbo/go-client/profiles/release/client.yml b/examples/dubbo/go-client/profiles/release/client.yml
index 4ce8a0d68ebf17fd464b785f8f652aff52a04c09..cb36b6946c3d5c637e291c47e34a779aaf427553 100644
--- a/examples/dubbo/go-client/profiles/release/client.yml
+++ b/examples/dubbo/go-client/profiles/release/client.yml
@@ -1,35 +1,66 @@
 # dubbo client yaml configure file
 
-# pprof
-pprof_enabled : true
-pprof_port : 10086
-
 # client
-request_timeout : "3500ms"
-net_io_timeout : "2s"
-retries : 1
+request_timeout : "100ms"
 # connect timeout
 connect_timeout : "100ms"
-selector : "cache"
-selector_ttl : "10m"
-registry : "zookeeper"
-client_load_balance: "round_robin"
-
+check: true
 # application config
 application_config:
-    organization : "ikurento.com"
-    name  : "BDTService"
-    module : "dubbogo user-info client"
-    version : "0.0.1"
-    owner : "ZX"
-    environment : "product"
+  organization : "ikurento.com"
+  name  : "BDTService"
+  module : "dubbogo user-info client"
+  version : "0.0.1"
+  owner : "ZX"
+  environment : "dev"
+
+registries :
+- id: "hangzhouzk"
+  type: "zookeeper"
+  timeout	: "3s"
+  address: "127.0.0.1:2181"
+  username: ""
+  password: ""
+
+- id: "shanghaizk"
+  type: "zookeeper"
+  timeout	: "3s"
+  address: "127.0.0.1:2182"
+  username: ""
+  password: ""
+
+references:
+- registries :
+  - "hangzhouzk"
+  - "shanghaizk"
+
+  protocol : "dubbo"
+  interface : "com.ikurento.user.UserProvider"
+  cluster: "failover"
+  methods :
+  - name: "GetUser"
+    retries: 3
 
-zk_registry_config:
-    timeout	: "3s"
-    address:
-        - "127.0.0.1:2181"
-service_config_type: "default"
-service_list:
-    -
-        protocol : "dubbo"
-        service : "com.ikurento.user.UserProvider"
+protocol_conf:
+  dubbo:
+    reconnect_interval: 0
+    connection_number: 2
+    heartbeat_period: "5s"
+    session_timeout: "20s"
+    fail_fast_timeout: "5s"
+    pool_size: 64
+    pool_ttl: 600
+    getty_session_param:
+      compress_encoding: false
+      tcp_no_delay: true
+      tcp_keep_alive: true
+      keep_alive_period: "120s"
+      tcp_r_buf_size: 262144
+      tcp_w_buf_size: 65536
+      pkg_rq_size: 1024
+      pkg_wq_size: 512
+      tcp_read_timeout: "1s"
+      tcp_write_timeout: "5s"
+      wait_timeout: "1s"
+      max_msg_len: 10240
+      session_name: "client"
diff --git a/examples/dubbo/go-client/profiles/release/log.xml b/examples/dubbo/go-client/profiles/release/log.xml
deleted file mode 100644
index ce8f7acee7b5dd296725a5d9d9c95477c38c29dd..0000000000000000000000000000000000000000
--- a/examples/dubbo/go-client/profiles/release/log.xml
+++ /dev/null
@@ -1,73 +0,0 @@
-<logging>
-  <filter enabled="false">
-    <tag>stdout</tag>
-    <type>console</type>
-    <!-- level is (:?FINEST|FINE|DEBUG|TRACE|INFO|WARNING|ERROR) -->
-    <level>DEBUG</level>
-    <property name="json">false</property> <!-- true enables json log format, its priority is high than format -->
-    <property name="format">[%D %T] [%L] (%S) %M</property> <!-- log format, if json is false this option is enable -->
-  </filter>
-  <filter enabled="true">
-    <tag>debug_file</tag>
-    <type>file</type>
-    <level>DEBUG</level>
-    <property name="filename">logs/debug.log</property>
-    <property name="json">false</property> <!-- true enables json log format, its priority is high than format -->
-    <property name="format">[%D %T] [%L] [%S] %M</property>
-    <property name="rotate">true</property> <!-- true enables log rotation, otherwise append -->
-    <property name="maxsize">0M</property> <!-- \d+[KMG]? Suffixes are in terms of 2**10 -->
-    <property name="maxlines">0K</property> <!-- \d+[KMG]? Suffixes are in terms of thousands -->
-    <property name="maxbackup">16</property> <!-- \d+ -->
-    <property name="daily">true</property> <!-- Automatically rotates when a log message is written after midnight -->
-  </filter>
-  <filter enabled="true">
-    <tag>info_file</tag>
-    <type>file</type>
-    <level>INFO</level>
-    <property name="filename">logs/info.log</property>
-    <!--
-       %T - Time (15:04:05 MST)
-       %t - Time (15:04)
-       %D - Date (2006/01/02)
-       %d - Date (01/02/06)
-       %L - Level (FNST, FINE, DEBG, TRAC, WARN, EROR, CRIT)
-       %S - Source
-       %M - Message
-       It ignores unknown format strings (and removes them)
-       Recommended: "[%D %T] [%L] (%S) %M"
-    -->
-    <property name="json">false</property> <!-- true enables json log format, its priority is high than format -->
-    <property name="format">[%D %T] [%L] [%S] %M</property>
-    <property name="rotate">true</property> <!-- true enables log rotation, otherwise append -->
-    <property name="maxsize">0M</property> <!-- \d+[KMG]? Suffixes are in terms of 2**10 -->
-    <property name="maxlines">0K</property> <!-- \d+[KMG]? Suffixes are in terms of thousands -->
-    <property name="maxbackup">16</property> <!-- \d+ -->
-    <property name="daily">true</property> <!-- Automatically rotates when a log message is written after midnight -->
-  </filter>
-  <filter enabled="true">
-    <tag>warn_file</tag>
-    <type>file</type>
-    <level>WARNING</level>
-    <property name="filename">logs/warn.log</property>
-    <property name="json">false</property> <!-- true enables json log format, its priority is high than format -->
-    <property name="format">[%D %T] [%L] [%S] %M</property>
-    <property name="rotate">true</property> <!-- true enables log rotation, otherwise append -->
-    <property name="maxsize">0M</property> <!-- \d+[KMG]? Suffixes are in terms of 2**10 -->
-    <property name="maxlines">0K</property> <!-- \d+[KMG]? Suffixes are in terms of thousands -->
-    <property name="maxbackup">16</property> <!-- \d+ -->
-    <property name="daily">true</property> <!-- Automatically rotates when a log message is written after midnight -->
-  </filter>
-  <filter enabled="true">
-    <tag>error_file</tag>
-    <type>file</type>
-    <level>ERROR</level>
-    <property name="filename">logs/error.log</property>
-    <property name="json">false</property> <!-- true enables json log format, its priority is high than format -->
-    <property name="format">[%D %T] [%L] [%S] %M</property>
-    <property name="rotate">true</property> <!-- true enables log rotation, otherwise append -->
-    <property name="maxsize">0M</property> <!-- \d+[KMG]? Suffixes are in terms of 2**10 -->
-    <property name="maxlines">0K</property> <!-- \d+[KMG]? Suffixes are in terms of thousands -->
-    <property name="maxbackup">16</property> <!-- \d+ -->
-    <property name="daily">true</property> <!-- Automatically rotates when a log message is written after midnight -->
-  </filter>
-</logging>
diff --git a/examples/dubbo/go-client/profiles/release/log.yml b/examples/dubbo/go-client/profiles/release/log.yml
new file mode 100644
index 0000000000000000000000000000000000000000..b9139c2e9cb21d5f557eb53e5d6909fca64ac205
--- /dev/null
+++ b/examples/dubbo/go-client/profiles/release/log.yml
@@ -0,0 +1,28 @@
+
+level: "warn"
+development: true
+disableCaller: true
+disableStacktrace: true
+sampling:
+encoding: "console"
+
+# encoder
+encoderConfig:
+  messageKey: "message"
+  levelKey: "level"
+  timeKey: "time"
+  nameKey: "logger"
+  callerKey: "caller"
+  stacktraceKey: "stacktrace"
+  lineEnding: ""
+  levelEncoder: "capitalColor"
+  timeEncoder: "iso8601"
+  durationEncoder: "seconds"
+  callerEncoder: "short"
+  nameEncoder: ""
+
+outputPaths:
+- "stderr"
+errorOutputPaths:
+- "stderr"
+initialFields:
diff --git a/examples/dubbo/go-client/profiles/test/client.yml b/examples/dubbo/go-client/profiles/test/client.yml
index 4e7e7a572bd2faea22fdee48f53dd50671eeb16b..cb36b6946c3d5c637e291c47e34a779aaf427553 100644
--- a/examples/dubbo/go-client/profiles/test/client.yml
+++ b/examples/dubbo/go-client/profiles/test/client.yml
@@ -1,35 +1,66 @@
 # dubbo client yaml configure file
 
-# pprof
-pprof_enabled : true
-pprof_port : 10086
-
 # client
-request_timeout : "3500ms"
-net_io_timeout : "2s"
-retries : 1
+request_timeout : "100ms"
 # connect timeout
 connect_timeout : "100ms"
-selector : "cache"
-selector_ttl : "10m"
-registry : "zookeeper"
-client_load_balance: "round_robin"
-
+check: true
 # application config
 application_config:
-    organization : "ikurento.com"
-    name  : "BDTService"
-    module : "dubbogo user-info client"
-    version : "0.0.1"
-    owner : "ZX"
-    environment : "test"
+  organization : "ikurento.com"
+  name  : "BDTService"
+  module : "dubbogo user-info client"
+  version : "0.0.1"
+  owner : "ZX"
+  environment : "dev"
+
+registries :
+- id: "hangzhouzk"
+  type: "zookeeper"
+  timeout	: "3s"
+  address: "127.0.0.1:2181"
+  username: ""
+  password: ""
+
+- id: "shanghaizk"
+  type: "zookeeper"
+  timeout	: "3s"
+  address: "127.0.0.1:2182"
+  username: ""
+  password: ""
+
+references:
+- registries :
+  - "hangzhouzk"
+  - "shanghaizk"
+
+  protocol : "dubbo"
+  interface : "com.ikurento.user.UserProvider"
+  cluster: "failover"
+  methods :
+  - name: "GetUser"
+    retries: 3
 
-zk_registry_config:
-    timeout	: "3s"
-    address:
-        - "127.0.0.1:2181"
-service_config_type: "default"
-service_list:
-    -
-        protocol : "dubbo"
-        service : "com.ikurento.user.UserProvider"
+protocol_conf:
+  dubbo:
+    reconnect_interval: 0
+    connection_number: 2
+    heartbeat_period: "5s"
+    session_timeout: "20s"
+    fail_fast_timeout: "5s"
+    pool_size: 64
+    pool_ttl: 600
+    getty_session_param:
+      compress_encoding: false
+      tcp_no_delay: true
+      tcp_keep_alive: true
+      keep_alive_period: "120s"
+      tcp_r_buf_size: 262144
+      tcp_w_buf_size: 65536
+      pkg_rq_size: 1024
+      pkg_wq_size: 512
+      tcp_read_timeout: "1s"
+      tcp_write_timeout: "5s"
+      wait_timeout: "1s"
+      max_msg_len: 10240
+      session_name: "client"
diff --git a/examples/dubbo/go-client/profiles/test/log.xml b/examples/dubbo/go-client/profiles/test/log.xml
deleted file mode 100644
index eab84b3e6972e5b87fc9c0d3abaae78e30ab5e7c..0000000000000000000000000000000000000000
--- a/examples/dubbo/go-client/profiles/test/log.xml
+++ /dev/null
@@ -1,73 +0,0 @@
-<logging>
-  <filter enabled="true">
-    <tag>stdout</tag>
-    <type>console</type>
-    <!-- level is (:?FINEST|FINE|DEBUG|TRACE|INFO|WARNING|ERROR) -->
-    <level>INFO</level>
-    <property name="json">false</property> <!-- true enables json log format, its priority is high than format -->
-    <property name="format">[%D %T] [%L] (%S) %M</property> <!-- log format, if json is false this option is enable -->
-  </filter>
-  <filter enabled="true">
-    <tag>debug_file</tag>
-    <type>file</type>
-    <level>DEBUG</level>
-    <property name="filename">logs/debug.log</property>
-    <property name="json">false</property> <!-- true enables json log format, its priority is high than format -->
-    <property name="format">[%D %T] [%L] [%S] %M</property>
-    <property name="rotate">true</property> <!-- true enables log rotation, otherwise append -->
-    <property name="maxsize">0M</property> <!-- \d+[KMG]? Suffixes are in terms of 2**10 -->
-    <property name="maxlines">0K</property> <!-- \d+[KMG]? Suffixes are in terms of thousands -->
-    <property name="maxbackup">16</property> <!-- \d+ -->
-    <property name="daily">true</property> <!-- Automatically rotates when a log message is written after midnight -->
-  </filter>
-  <filter enabled="true">
-    <tag>info_file</tag>
-    <type>file</type>
-    <level>INFO</level>
-    <property name="filename">logs/info.log</property>
-    <!--
-       %T - Time (15:04:05 MST)
-       %t - Time (15:04)
-       %D - Date (2006/01/02)
-       %d - Date (01/02/06)
-       %L - Level (FNST, FINE, DEBG, TRAC, WARN, EROR, CRIT)
-       %S - Source
-       %M - Message
-       It ignores unknown format strings (and removes them)
-       Recommended: "[%D %T] [%L] (%S) %M"
-    -->
-    <property name="json">false</property> <!-- true enables json log format, its priority is high than format -->
-    <property name="format">[%D %T] [%L] [%S] %M</property>
-    <property name="rotate">true</property> <!-- true enables log rotation, otherwise append -->
-    <property name="maxsize">0M</property> <!-- \d+[KMG]? Suffixes are in terms of 2**10 -->
-    <property name="maxlines">0K</property> <!-- \d+[KMG]? Suffixes are in terms of thousands -->
-    <property name="maxbackup">16</property> <!-- \d+ -->
-    <property name="daily">true</property> <!-- Automatically rotates when a log message is written after midnight -->
-  </filter>
-  <filter enabled="true">
-    <tag>warn_file</tag>
-    <type>file</type>
-    <level>WARNING</level>
-    <property name="filename">logs/warn.log</property>
-    <property name="json">false</property> <!-- true enables json log format, its priority is high than format -->
-    <property name="format">[%D %T] [%L] [%S] %M</property>
-    <property name="rotate">true</property> <!-- true enables log rotation, otherwise append -->
-    <property name="maxsize">0M</property> <!-- \d+[KMG]? Suffixes are in terms of 2**10 -->
-    <property name="maxlines">0K</property> <!-- \d+[KMG]? Suffixes are in terms of thousands -->
-    <property name="maxbackup">16</property> <!-- \d+ -->
-    <property name="daily">true</property> <!-- Automatically rotates when a log message is written after midnight -->
-  </filter>
-  <filter enabled="true">
-    <tag>error_file</tag>
-    <type>file</type>
-    <level>ERROR</level>
-    <property name="filename">logs/error.log</property>
-    <property name="json">false</property> <!-- true enables json log format, its priority is high than format -->
-    <property name="format">[%D %T] [%L] [%S] %M</property>
-    <property name="rotate">true</property> <!-- true enables log rotation, otherwise append -->
-    <property name="maxsize">0M</property> <!-- \d+[KMG]? Suffixes are in terms of 2**10 -->
-    <property name="maxlines">0K</property> <!-- \d+[KMG]? Suffixes are in terms of thousands -->
-    <property name="maxbackup">16</property> <!-- \d+ -->
-    <property name="daily">true</property> <!-- Automatically rotates when a log message is written after midnight -->
-  </filter>
-</logging>
diff --git a/examples/dubbo/go-client/profiles/test/log.yml b/examples/dubbo/go-client/profiles/test/log.yml
new file mode 100644
index 0000000000000000000000000000000000000000..d2e1d05f3f46bc4ec6c7b8a16211c13d2189219d
--- /dev/null
+++ b/examples/dubbo/go-client/profiles/test/log.yml
@@ -0,0 +1,28 @@
+
+level: "info"
+development: false
+disableCaller: false
+disableStacktrace: true
+sampling:
+encoding: "console"
+
+# encoder
+encoderConfig:
+  messageKey: "message"
+  levelKey: "level"
+  timeKey: "time"
+  nameKey: "logger"
+  callerKey: "caller"
+  stacktraceKey: "stacktrace"
+  lineEnding: ""
+  levelEncoder: "capitalColor"
+  timeEncoder: "iso8601"
+  durationEncoder: "seconds"
+  callerEncoder: "short"
+  nameEncoder: ""
+
+outputPaths:
+- "stderr"
+errorOutputPaths:
+- "stderr"
+initialFields:
diff --git a/examples/dubbo/go-server/app/config.go b/examples/dubbo/go-server/app/config.go
deleted file mode 100644
index 4fb460816fafa61e986f80d787c28279938193a3..0000000000000000000000000000000000000000
--- a/examples/dubbo/go-server/app/config.go
+++ /dev/null
@@ -1,129 +0,0 @@
-package main
-
-import (
-	"fmt"
-	"io/ioutil"
-	"os"
-	"path"
-	"time"
-)
-
-import (
-	"github.com/AlexStocks/goext/log"
-	log "github.com/AlexStocks/log4go"
-	jerrors "github.com/juju/errors"
-	yaml "gopkg.in/yaml.v2"
-)
-
-import (
-	"github.com/dubbo/go-for-apache-dubbo/registry"
-	"github.com/dubbo/go-for-apache-dubbo/registry/zookeeper"
-	"github.com/dubbo/go-for-apache-dubbo/server"
-	"github.com/dubbo/go-for-apache-dubbo/plugins"
-)
-
-const (
-	APP_CONF_FILE     string = "APP_CONF_FILE"
-	APP_LOG_CONF_FILE string = "APP_LOG_CONF_FILE"
-)
-
-var (
-	conf *ServerConfig
-)
-
-type (
-	ServerConfig struct {
-		// pprof
-		Pprof_Enabled bool `default:"false" yaml:"pprof_enabled"  json:"pprof_enabled,omitempty"`
-		Pprof_Port    int  `default:"10086"  yaml:"pprof_port" json:"pprof_port,omitempty"`
-
-		// transport & registry
-		Transport  string `default:"http"  yaml:"transport" json:"transport,omitempty"`
-		NetTimeout string `default:"100ms"  yaml:"net_timeout" json:"net_timeout,omitempty"` // in ms
-		netTimeout time.Duration
-		// application
-		Application_Config registry.ApplicationConfig `yaml:"application_config" json:"application_config,omitempty"`
-		// Registry_Address  string `default:"192.168.35.3:2181"`
-		Registry         string                     `default:"zookeeper"  yaml:"registry" json:"registry,omitempty"`
-		ZkRegistryConfig zookeeper.ZkRegistryConfig `yaml:"zk_registry_config" json:"zk_registry_config,omitempty"`
-
-		ServiceConfigType    string                   `default:"default" yaml:"service_config_type" json:"service_config_type,omitempty"`
-		ServiceConfigList    []registry.ServiceConfig `yaml:"-"`
-		ServiceConfigMapList []map[string]string      `yaml:"service_list" json:"service_list,omitempty"`
-		Server_List          []server.ServerConfig    `yaml:"server_list" json:"server_list,omitempty"`
-	}
-)
-
-func initServerConf() *ServerConfig {
-	var (
-		err      error
-		confFile string
-	)
-
-	confFile = os.Getenv(APP_CONF_FILE)
-	if confFile == "" {
-		panic(fmt.Sprintf("application configure file name is nil"))
-		return nil
-	}
-	if path.Ext(confFile) != ".yml" {
-		panic(fmt.Sprintf("application configure file name{%v} suffix must be .yml", confFile))
-		return nil
-	}
-
-	conf = &ServerConfig{}
-	confFileStream, err := ioutil.ReadFile(confFile)
-	if err != nil {
-		panic(fmt.Sprintf("ioutil.ReadFile(file:%s) = error:%s", confFile, jerrors.ErrorStack(err)))
-		return nil
-	}
-	err = yaml.Unmarshal(confFileStream, conf)
-	if err != nil {
-		panic(fmt.Sprintf("yaml.Unmarshal() = error:%s", jerrors.ErrorStack(err)))
-		return nil
-	}
-	if conf.netTimeout, err = time.ParseDuration(conf.NetTimeout); err != nil {
-		panic(fmt.Sprintf("time.ParseDuration(NetTimeout:%#v) = error:%s", conf.NetTimeout, err))
-		return nil
-	}
-	if conf.ZkRegistryConfig.Timeout, err = time.ParseDuration(conf.ZkRegistryConfig.TimeoutStr); err != nil {
-		panic(fmt.Sprintf("time.ParseDuration(Registry_Config.Timeout:%#v) = error:%s",
-			conf.ZkRegistryConfig.TimeoutStr, err))
-		return nil
-	}
-
-	// set designated service_config_type to default
-	plugins.SetDefaultProviderServiceConfig(conf.ServiceConfigType)
-	for _, service := range conf.ServiceConfigMapList {
-
-		svc := plugins.DefaultProviderServiceConfig()()
-		svc.SetProtocol(service["protocol"])
-		svc.SetService(service["service"])
-		conf.ServiceConfigList = append(conf.ServiceConfigList, svc)
-	}
-
-	gxlog.CInfo("config{%#v}\n", conf)
-
-	return conf
-}
-
-func configInit() error {
-	var (
-		confFile string
-	)
-
-	initServerConf()
-
-	confFile = os.Getenv(APP_LOG_CONF_FILE)
-	if confFile == "" {
-		panic(fmt.Sprintf("log configure file name is nil"))
-		return nil
-	}
-	if path.Ext(confFile) != ".xml" {
-		panic(fmt.Sprintf("log configure file name{%v} suffix must be .xml", confFile))
-		return nil
-	}
-
-	log.LoadConfiguration(confFile)
-
-	return nil
-}
diff --git a/examples/dubbo/go-server/app/server.go b/examples/dubbo/go-server/app/server.go
index 1affcd50116845212210a4376866532a5d9b8b12..1c0d2c2c7391a417177332ab7341d80ade7a4faf 100644
--- a/examples/dubbo/go-server/app/server.go
+++ b/examples/dubbo/go-server/app/server.go
@@ -1,173 +1,85 @@
+// Copyright 2016-2019 Yincheng Fang, Alex Stocks
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package main
 
 import (
 	"fmt"
-	"net/http"
-	_ "net/http/pprof"
 	"os"
 	"os/signal"
-	"strconv"
 	"syscall"
+	"time"
 )
 
 import (
-	"github.com/AlexStocks/goext/net"
-	"github.com/AlexStocks/goext/time"
-	log "github.com/AlexStocks/log4go"
+	"github.com/dubbo/go-for-apache-dubbo/common/logger"
 	"github.com/dubbogo/hessian2"
-	jerrors "github.com/juju/errors"
 )
 
 import (
-	"github.com/dubbo/go-for-apache-dubbo/dubbo"
-	"github.com/dubbo/go-for-apache-dubbo/plugins"
-	"github.com/dubbo/go-for-apache-dubbo/registry"
-	"github.com/dubbo/go-for-apache-dubbo/registry/zookeeper"
+	"github.com/dubbo/go-for-apache-dubbo/config"
+	_ "github.com/dubbo/go-for-apache-dubbo/protocol/dubbo"
+	_ "github.com/dubbo/go-for-apache-dubbo/registry/protocol"
+
+	_ "github.com/dubbo/go-for-apache-dubbo/common/proxy/proxy_factory"
+	_ "github.com/dubbo/go-for-apache-dubbo/filter/impl"
+
+	_ "github.com/dubbo/go-for-apache-dubbo/cluster/cluster_impl"
+	_ "github.com/dubbo/go-for-apache-dubbo/cluster/loadbalance"
+	_ "github.com/dubbo/go-for-apache-dubbo/registry/zookeeper"
 )
 
 var (
 	survivalTimeout = int(3e9)
-	servo           *dubbo.Server
 )
 
+// they are necessary:
+// 		export CONF_PROVIDER_FILE_PATH="xxx"
+// 		export APP_LOG_CONF_FILE="xxx"
 func main() {
-	var (
-		err error
-	)
-
-	err = configInit()
-	if err != nil {
-		log.Error("configInit() = error{%#v}", err)
-		return
-	}
-	initProfiling()
 
+	// ------for hessian2------
 	hessian.RegisterJavaEnum(Gender(MAN))
 	hessian.RegisterJavaEnum(Gender(WOMAN))
-	hessian.RegisterPOJO(&DubboUser{})
+	hessian.RegisterPOJO(&User{})
+	// ------------
 
-	servo = initServer()
-	err = servo.Register(&UserProvider{})
-	if err != nil {
-		panic(err)
-		return
+	_, proMap := config.Load()
+	if proMap == nil {
+		panic("proMap is nil")
 	}
-	servo.Start()
 
 	initSignal()
 }
 
-func initServer() *dubbo.Server {
-	var (
-		srv *dubbo.Server
-	)
-
-	if conf == nil {
-		panic(fmt.Sprintf("conf is nil"))
-		return nil
-	}
-
-	// registry
-
-	regs, err := plugins.PluggableRegistries[conf.Registry](
-		registry.WithDubboType(registry.PROVIDER),
-		registry.WithApplicationConf(conf.Application_Config),
-		zookeeper.WithRegistryConf(conf.ZkRegistryConfig),
-	)
-
-	if err != nil || regs == nil {
-		panic(fmt.Sprintf("fail to init registry.Registy, err:%s", jerrors.ErrorStack(err)))
-		return nil
-	}
-
-	// generate server config
-	serverConfig := make([]dubbo.ServerConfig, len(conf.Server_List))
-	for i := 0; i < len(conf.Server_List); i++ {
-		serverConfig[i] = dubbo.ServerConfig{
-			SessionNumber:   700,
-			FailFastTimeout: "5s",
-			SessionTimeout:  "20s",
-			GettySessionParam: dubbo.GettySessionParam{
-				CompressEncoding: false, // 蹇呴』false
-				TcpNoDelay:       true,
-				KeepAlivePeriod:  "120s",
-				TcpRBufSize:      262144,
-				TcpKeepAlive:     true,
-				TcpWBufSize:      65536,
-				PkgRQSize:        1024,
-				PkgWQSize:        512,
-				TcpReadTimeout:   "1s",
-				TcpWriteTimeout:  "5s",
-				WaitTimeout:      "1s",
-				MaxMsgLen:        1024,
-				SessionName:      "server",
-			},
-		}
-		serverConfig[i].IP = conf.Server_List[i].IP
-		serverConfig[i].Port = conf.Server_List[i].Port
-		serverConfig[i].Protocol = conf.Server_List[i].Protocol
-	}
-
-	// provider
-	srv = dubbo.NewServer(
-		dubbo.Registry(regs),
-		dubbo.ConfList(serverConfig),
-		dubbo.ServiceConfList(conf.ServiceConfigList),
-	)
-
-	return srv
-}
-
-func uninitServer() {
-	if servo != nil {
-		servo.Stop()
-	}
-	log.Close()
-}
-
-func initProfiling() {
-	if !conf.Pprof_Enabled {
-		return
-	}
-	const (
-		PprofPath = "/debug/pprof/"
-	)
-	var (
-		err  error
-		ip   string
-		addr string
-	)
-
-	ip, err = gxnet.GetLocalIP()
-	if err != nil {
-		panic("cat not get local ip!")
-	}
-	addr = ip + ":" + strconv.Itoa(conf.Pprof_Port)
-	log.Info("App Profiling startup on address{%v}", addr+PprofPath)
-
-	go func() {
-		log.Info(http.ListenAndServe(addr, nil))
-	}()
-}
-
 func initSignal() {
 	signals := make(chan os.Signal, 1)
 	// It is not possible to block SIGKILL or syscall.SIGSTOP
 	signal.Notify(signals, os.Interrupt, os.Kill, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)
 	for {
 		sig := <-signals
-		log.Info("get signal %s", sig.String())
+		logger.Infof("get signal %s", sig.String())
 		switch sig {
 		case syscall.SIGHUP:
-		// reload()
+			// reload()
 		default:
-			go gxtime.Future(survivalTimeout, func() {
-				log.Warn("app exit now by force...")
+			go time.AfterFunc(time.Duration(float64(survivalTimeout)*float64(time.Second)), func() {
+				logger.Warnf("app exit now by force...")
 				os.Exit(1)
 			})
 
 			// 瑕佷箞fastFailTimeout鏃堕棿鍐呮墽琛屽畬姣曚笅闈㈢殑閫昏緫鐒跺悗绋嬪簭閫€鍑猴紝瑕佷箞鎵ц涓婇潰鐨勮秴鏃跺嚱鏁扮▼搴忓己琛岄€€鍑�
-			uninitServer()
 			fmt.Println("provider app exit now...")
 			return
 		}
diff --git a/examples/dubbo/go-server/app/user.go b/examples/dubbo/go-server/app/user.go
index fa4f9f992eb8d50ee962527c6b7551125ec2da02..427125bd1329668c24e9dc46a94d55cfb362a472 100644
--- a/examples/dubbo/go-server/app/user.go
+++ b/examples/dubbo/go-server/app/user.go
@@ -1,7 +1,20 @@
+// Copyright 2016-2019 Yincheng Fang, Alex Stocks
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package main
 
 import (
-	// "encoding/json"
 	"context"
 	"fmt"
 	"strconv"
@@ -9,12 +22,20 @@ import (
 )
 
 import (
-	"github.com/AlexStocks/goext/log"
 	"github.com/dubbogo/hessian2"
+	perrors "github.com/pkg/errors"
+)
+
+import (
+	"github.com/dubbo/go-for-apache-dubbo/config"
 )
 
 type Gender hessian.JavaEnum
 
+func init() {
+	config.SetProviderService(new(UserProvider))
+}
+
 const (
 	MAN hessian.JavaEnum = iota
 	WOMAN
@@ -53,7 +74,7 @@ func (g Gender) EnumValue(s string) hessian.JavaEnum {
 }
 
 type (
-	DubboUser struct {
+	User struct {
 		// !!! Cannot define lowercase names of variable
 		Id   string
 		Name string
@@ -63,42 +84,42 @@ type (
 	}
 
 	UserProvider struct {
-		user map[string]DubboUser
+		user map[string]User
 	}
 )
 
-func (u DubboUser) String() string {
-	return fmt.Sprintf(
-		"User{Id:%s, Name:%s, Age:%d, Time:%s, Sex:%s}",
-		u.Id, u.Name, u.Age, u.Time, u.Sex,
-	)
-}
-
-func (DubboUser) JavaClassName() string {
-	return "com.ikurento.user.User"
-}
-
 var (
-	DefaultUser = DubboUser{
+	DefaultUser = User{
 		Id: "0", Name: "Alex Stocks", Age: 31,
 		Sex: Gender(MAN),
 	}
 
-	userMap = UserProvider{user: make(map[string]DubboUser)}
+	userMap = UserProvider{user: make(map[string]User)}
 )
 
 func init() {
-	//DefaultUser.Sex = DefaultUser.sex.String()
 	userMap.user["A000"] = DefaultUser
-	userMap.user["A001"] = DubboUser{Id: "001", Name: "ZhangSheng", Age: 18, Sex: Gender(MAN)}
-	userMap.user["A002"] = DubboUser{Id: "002", Name: "Lily", Age: 20, Sex: Gender(WOMAN)}
-	userMap.user["A003"] = DubboUser{Id: "113", Name: "Moorse", Age: 30, Sex: Gender(WOMAN)}
+	userMap.user["A001"] = User{Id: "001", Name: "ZhangSheng", Age: 18, Sex: Gender(MAN)}
+	userMap.user["A002"] = User{Id: "002", Name: "Lily", Age: 20, Sex: Gender(WOMAN)}
+	userMap.user["A003"] = User{Id: "113", Name: "Moorse", Age: 30, Sex: Gender(WOMAN)}
 	for k, v := range userMap.user {
+		v.Time = time.Now()
 		userMap.user[k] = v
 	}
 }
 
-func (u *UserProvider) getUser(userId string) (*DubboUser, error) {
+func (u User) String() string {
+	return fmt.Sprintf(
+		"User{Id:%s, Name:%s, Age:%d, Time:%s, Sex:%s}",
+		u.Id, u.Name, u.Age, u.Time, u.Sex,
+	)
+}
+
+func (u User) JavaClassName() string {
+	return "com.ikurento.user.User"
+}
+
+func (u *UserProvider) getUser(userId string) (*User, error) {
 	if user, ok := userMap.user[userId]; ok {
 		return &user, nil
 	}
@@ -106,24 +127,54 @@ func (u *UserProvider) getUser(userId string) (*DubboUser, error) {
 	return nil, fmt.Errorf("invalid user id:%s", userId)
 }
 
-/*
-	!!! req must be []interface{}
-*/
-func (u *UserProvider) GetUser(ctx context.Context, req []interface{}, rsp *DubboUser) error {
+func (u *UserProvider) GetUser(ctx context.Context, req []interface{}, rsp *User) error {
 	var (
 		err  error
-		user *DubboUser
+		user *User
 	)
 
-	gxlog.CInfo("req:%#v", req)
+	println("req:%#v", req)
 	user, err = u.getUser(req[0].(string))
 	if err == nil {
 		*rsp = *user
-		gxlog.CInfo("rsp:%#v", rsp)
+		println("rsp:%#v", rsp)
 	}
 	return err
 }
 
+func (u *UserProvider) GetUser0(id string, name string) (User, error) {
+	var err error
+
+	println("id:%s, name:%s", id, name)
+	user, err := u.getUser(id)
+	if err != nil {
+		return User{}, err
+	}
+	if user.Name != name {
+		return User{}, perrors.New("name is not " + user.Name)
+	}
+	return *user, err
+}
+
+func (u *UserProvider) GetUsers(req []interface{}) ([]interface{}, error) {
+	var err error
+
+	println("req:%s", req)
+	t := req[0].([]interface{})
+	user, err := u.getUser(t[0].(string))
+	if err != nil {
+		return nil, err
+	}
+	println("user:%v", user)
+	user1, err := u.getUser(t[1].(string))
+	if err != nil {
+		return nil, err
+	}
+	println("user1:%v", user1)
+
+	return []interface{}{user, user1}, err
+}
+
 func (u *UserProvider) Service() string {
 	return "com.ikurento.user.UserProvider"
 }
@@ -131,3 +182,7 @@ func (u *UserProvider) Service() string {
 func (u *UserProvider) Version() string {
 	return ""
 }
+
+func println(format string, args ...interface{}) {
+	fmt.Printf("\033[32;40m"+format+"\033[0m\n", args...)
+}
diff --git a/examples/dubbo/go-server/assembly/bin/load.sh b/examples/dubbo/go-server/assembly/bin/load.sh
index e202ff65f436f08191ae5364378f659de858777a..47fc5e38ded155a43c30b8cbf4d2a5ae04b58d0c 100644
--- a/examples/dubbo/go-server/assembly/bin/load.sh
+++ b/examples/dubbo/go-server/assembly/bin/load.sh
@@ -20,11 +20,11 @@ if [[ ${OS_NAME} != "Windows" ]]; then
     PROJECT_HOME=${PROJECT_HOME}"/"
 fi
 
-export APP_CONF_FILE=${PROJECT_HOME}"TARGET_CONF_FILE"
+export CONF_PROVIDER_FILE_PATH=${PROJECT_HOME}"TARGET_CONF_FILE"
 export APP_LOG_CONF_FILE=${PROJECT_HOME}"TARGET_LOG_CONF_FILE"
 
 usage() {
-    echo "Usage: $0 start"
+    echo "Usage: $0 start [conf suffix]"
     echo "       $0 stop"
     echo "       $0 term"
     echo "       $0 restart"
@@ -35,6 +35,16 @@ usage() {
 }
 
 start() {
+    arg=$1
+    if [ "$arg" = "" ];then
+        echo "No registry type! Default server.yml!"
+    else
+        export CONF_PROVIDER_FILE_PATH=${CONF_PROVIDER_FILE_PATH//\.yml/\_$arg\.yml}
+    fi
+    if [ ! -f "${CONF_PROVIDER_FILE_PATH}" ];then
+        echo $CONF_PROVIDER_FILE_PATH" is not existing!"
+        return
+    fi
     APP_LOG_PATH="${PROJECT_HOME}logs/"
     mkdir -p ${APP_LOG_PATH}
     APP_BIN=${PROJECT_HOME}sbin/${APP_NAME}
@@ -112,7 +122,7 @@ list() {
 opt=$1
 case C"$opt" in
     Cstart)
-        start
+        start $2
         ;;
     Cstop)
         stop
@@ -122,7 +132,7 @@ case C"$opt" in
         ;;
     Crestart)
         term
-        start
+        start $2
         ;;
     Clist)
         list
diff --git a/examples/dubbo/go-server/assembly/common/app.properties b/examples/dubbo/go-server/assembly/common/app.properties
index d230d5efc4ee84c4a99e1b27e7b49d97046d91a3..dffb755b0811dd140d3f04e232f5f80ff60181df 100644
--- a/examples/dubbo/go-server/assembly/common/app.properties
+++ b/examples/dubbo/go-server/assembly/common/app.properties
@@ -14,4 +14,4 @@ TARGET_EXEC_NAME="user_info_server"
 BUILD_PACKAGE="app"
 
 TARGET_CONF_FILE="conf/server.yml"
-TARGET_LOG_CONF_FILE="conf/log.xml"
+TARGET_LOG_CONF_FILE="conf/log.yml"
diff --git a/examples/dubbo/go-server/profiles/dev/log.xml b/examples/dubbo/go-server/profiles/dev/log.xml
deleted file mode 100644
index d2a0d89394aa2b5a882924752d9b7bab7f424dc7..0000000000000000000000000000000000000000
--- a/examples/dubbo/go-server/profiles/dev/log.xml
+++ /dev/null
@@ -1,73 +0,0 @@
-<logging>
-  <filter enabled="true">
-    <tag>stdout</tag>
-    <type>console</type>
-    <!-- level is (:?FINEST|FINE|DEBUG|TRACE|INFO|WARNING|ERROR) -->
-    <level>DEBUG</level>
-    <property name="json">false</property> <!-- true enables json log format, its priority is high than format -->
-    <property name="format">[%D %T] [%L] (%S) %M</property> <!-- log format, if json is false this option is enable -->
-  </filter>
-  <filter enabled="true">
-    <tag>debug_file</tag>
-    <type>file</type>
-    <level>DEBUG</level>
-    <property name="filename">logs/debug.log</property>
-    <property name="json">false</property> <!-- true enables json log format, its priority is high than format -->
-    <property name="format">[%D %T] [%L] [%S] %M</property>
-    <property name="rotate">true</property> <!-- true enables log rotation, otherwise append -->
-    <property name="maxsize">0M</property> <!-- \d+[KMG]? Suffixes are in terms of 2**10 -->
-    <property name="maxlines">0K</property> <!-- \d+[KMG]? Suffixes are in terms of thousands -->
-    <property name="maxbackup">16</property> <!-- \d+ -->
-    <property name="daily">true</property> <!-- Automatically rotates when a log message is written after midnight -->
-  </filter>
-  <filter enabled="true">
-    <tag>info_file</tag>
-    <type>file</type>
-    <level>INFO</level>
-    <property name="filename">logs/info.log</property>
-    <!--
-       %T - Time (15:04:05 MST)
-       %t - Time (15:04)
-       %D - Date (2006/01/02)
-       %d - Date (01/02/06)
-       %L - Level (FNST, FINE, DEBG, TRAC, WARN, EROR, CRIT)
-       %S - Source
-       %M - Message
-       It ignores unknown format strings (and removes them)
-       Recommended: "[%D %T] [%L] (%S) %M"
-    -->
-    <property name="json">false</property> <!-- true enables json log format, its priority is high than format -->
-    <property name="format">[%D %T] [%L] [%S] %M</property>
-    <property name="rotate">true</property> <!-- true enables log rotation, otherwise append -->
-    <property name="maxsize">0M</property> <!-- \d+[KMG]? Suffixes are in terms of 2**10 -->
-    <property name="maxlines">0K</property> <!-- \d+[KMG]? Suffixes are in terms of thousands -->
-    <property name="maxbackup">16</property> <!-- \d+ -->
-    <property name="daily">true</property> <!-- Automatically rotates when a log message is written after midnight -->
-  </filter>
-  <filter enabled="true">
-    <tag>warn_file</tag>
-    <type>file</type>
-    <level>WARNING</level>
-    <property name="filename">logs/warn.log</property>
-    <property name="json">false</property> <!-- true enables json log format, its priority is high than format -->
-    <property name="format">[%D %T] [%L] [%S] %M</property>
-    <property name="rotate">true</property> <!-- true enables log rotation, otherwise append -->
-    <property name="maxsize">0M</property> <!-- \d+[KMG]? Suffixes are in terms of 2**10 -->
-    <property name="maxlines">0K</property> <!-- \d+[KMG]? Suffixes are in terms of thousands -->
-    <property name="maxbackup">16</property> <!-- \d+ -->
-    <property name="daily">true</property> <!-- Automatically rotates when a log message is written after midnight -->
-  </filter>
-  <filter enabled="true">
-    <tag>error_file</tag>
-    <type>file</type>
-    <level>ERROR</level>
-    <property name="filename">logs/error.log</property>
-    <property name="json">false</property> <!-- true enables json log format, its priority is high than format -->
-    <property name="format">[%D %T] [%L] [%S] %M</property>
-    <property name="rotate">true</property> <!-- true enables log rotation, otherwise append -->
-    <property name="maxsize">0M</property> <!-- \d+[KMG]? Suffixes are in terms of 2**10 -->
-    <property name="maxlines">0K</property> <!-- \d+[KMG]? Suffixes are in terms of thousands -->
-    <property name="maxbackup">16</property> <!-- \d+ -->
-    <property name="daily">true</property> <!-- Automatically rotates when a log message is written after midnight -->
-  </filter>
-</logging>
diff --git a/examples/dubbo/go-server/profiles/dev/log.yml b/examples/dubbo/go-server/profiles/dev/log.yml
new file mode 100644
index 0000000000000000000000000000000000000000..427308d52b028d1740dac56b66b2e54fa76c6fe2
--- /dev/null
+++ b/examples/dubbo/go-server/profiles/dev/log.yml
@@ -0,0 +1,28 @@
+
+level: "debug"
+development: true
+disableCaller: false
+disableStacktrace: false
+sampling:
+encoding: "console"
+
+# encoder
+encoderConfig:
+  messageKey: "message"
+  levelKey: "level"
+  timeKey: "time"
+  nameKey: "logger"
+  callerKey: "caller"
+  stacktraceKey: "stacktrace"
+  lineEnding: ""
+  levelEncoder: "capitalColor"
+  timeEncoder: "iso8601"
+  durationEncoder: "seconds"
+  callerEncoder: "short"
+  nameEncoder: ""
+
+outputPaths:
+- "stderr"
+errorOutputPaths:
+- "stderr"
+initialFields:
diff --git a/examples/dubbo/go-server/profiles/dev/server.yml b/examples/dubbo/go-server/profiles/dev/server.yml
index 3eb4df6112275fd9ffebe7a416c39a1259867512..6477a6638540e757452c737173088b2af7fe3323 100644
--- a/examples/dubbo/go-server/profiles/dev/server.yml
+++ b/examples/dubbo/go-server/profiles/dev/server.yml
@@ -1,13 +1,5 @@
 # dubbo server yaml configure file
 
-# pprof
-pprof_enabled : true
-pprof_port : 20080
-
-# server
-transport : "http"
-net_timeout : "3s"
-
 # application config
 application_config:
     organization : "ikurento.com"
@@ -17,23 +9,61 @@ application_config:
     owner : "ZX"
     environment : "dev"
 
-registry: "zookeeper"
+registries :
+- id: "hangzhouzk"
+  type: "zookeeper"
+  timeout	: "3s"
+  address: "127.0.0.1:2181"
+  username: ""
+  password: ""
+
+- id: "shanghaizk"
+  type: "zookeeper"
+  timeout	: "3s"
+  address: "127.0.0.1:2182"
+  username: ""
+  password: ""
+
+
+services:
+- registries:
+  - "hangzhouzk"
+  - "shanghaizk"
+  protocol : "dubbo"
+    # 鐩稿綋浜巇ubbo.xml涓殑interface
+  interface : "com.ikurento.user.UserProvider"
+  loadbalance: "random"
+  warmup: "100"
+  cluster: "failover"
+  methods:
+  - name: "GetUser"
+    retries: 1
+    loadbalance: "random"
 
-zk_registry_config:
-    timeout	: "3s"
-    address:
-        - "127.0.0.1:2181"
-service_config_type: "default"
-service_list:
-    -
-        protocol : "dubbo"
-        # 鐩稿綋浜巇ubbo.xml涓殑interface
-        service : "com.ikurento.user.UserProvider"
+protocols:
+-   name: "dubbo"
+    ip : "127.0.0.1"
+    port : 20000
+#-   name: "jsonrpc"
+#    ip: "127.0.0.1"
+#    port: 20001
 
-server_list:
-    -
-        # 濡傛灉鏄�127.0.0.1, java-client灏嗘棤娉曡繛鎺ュ埌go-server
-        ip : "192.168.56.1"
-        port : 20000
-        # 鏈瑂erver鑳藉鎻愪緵鎵€鏈夋敮鎸佸悓鏍风殑Protocol鐨剆ervicelist鐨勬湇鍔�
-        protocol : "dubbo"
+protocol_conf:
+  dubbo:
+    session_number: 700
+    fail_fast_timeout: "5s"
+    session_timeout: "20s"
+    getty_session_param:
+      compress_encoding: false
+      tcp_no_delay: true
+      tcp_keep_alive: true
+      keep_alive_period: "120s"
+      tcp_r_buf_size: 262144
+      tcp_w_buf_size: 65536
+      pkg_rq_size: 1024
+      pkg_wq_size: 512
+      tcp_read_timeout: "1s"
+      tcp_write_timeout: "5s"
+      wait_timeout: "1s"
+      max_msg_len: 1024
+      session_name: "server"
diff --git a/examples/dubbo/go-server/profiles/release/log.xml b/examples/dubbo/go-server/profiles/release/log.xml
deleted file mode 100644
index 834bab5b07e72f1c250d500b60fe3af25e74cfc1..0000000000000000000000000000000000000000
--- a/examples/dubbo/go-server/profiles/release/log.xml
+++ /dev/null
@@ -1,73 +0,0 @@
-<logging>
-  <filter enabled="false">
-    <tag>stdout</tag>
-    <type>console</type>
-    <!-- level is (:?FINEST|FINE|DEBUG|TRACE|INFO|WARNING|ERROR) -->
-    <level>DEBUG</level>
-    <property name="json">false</property> <!-- true enables json log format, its priority is high than format -->
-    <property name="format">[%D %T] [%L] (%S) %M</property> <!-- log format, if json is false this option is enable -->
-  </filter>
-  <filter enabled="false">
-    <tag>debug_file</tag>
-    <type>file</type>
-    <level>DEBUG</level>
-    <property name="filename">logs/debug.log</property>
-    <property name="json">false</property> <!-- true enables json log format, its priority is high than format -->
-    <property name="format">[%D %T] [%L] [%S] %M</property>
-    <property name="rotate">true</property> <!-- true enables log rotation, otherwise append -->
-    <property name="maxsize">0M</property> <!-- \d+[KMG]? Suffixes are in terms of 2**10 -->
-    <property name="maxlines">0K</property> <!-- \d+[KMG]? Suffixes are in terms of thousands -->
-    <property name="maxbackup">16</property> <!-- \d+ -->
-    <property name="daily">true</property> <!-- Automatically rotates when a log message is written after midnight -->
-  </filter>
-  <filter enabled="false">
-    <tag>info_file</tag>
-    <type>file</type>
-    <level>INFO</level>
-    <property name="filename">logs/info.log</property>
-    <!--
-       %T - Time (15:04:05 MST)
-       %t - Time (15:04)
-       %D - Date (2006/01/02)
-       %d - Date (01/02/06)
-       %L - Level (FNST, FINE, DEBG, TRAC, WARN, EROR, CRIT)
-       %S - Source
-       %M - Message
-       It ignores unknown format strings (and removes them)
-       Recommended: "[%D %T] [%L] (%S) %M"
-    -->
-    <property name="json">false</property> <!-- true enables json log format, its priority is high than format -->
-    <property name="format">[%D %T] [%L] [%S] %M</property>
-    <property name="rotate">true</property> <!-- true enables log rotation, otherwise append -->
-    <property name="maxsize">0M</property> <!-- \d+[KMG]? Suffixes are in terms of 2**10 -->
-    <property name="maxlines">0K</property> <!-- \d+[KMG]? Suffixes are in terms of thousands -->
-    <property name="maxbackup">16</property> <!-- \d+ -->
-    <property name="daily">true</property> <!-- Automatically rotates when a log message is written after midnight -->
-  </filter>
-  <filter enabled="true">
-    <tag>warn_file</tag>
-    <type>file</type>
-    <level>WARNING</level>
-    <property name="filename">logs/warn.log</property>
-    <property name="json">false</property> <!-- true enables json log format, its priority is high than format -->
-    <property name="format">[%D %T] [%L] [%S] %M</property>
-    <property name="rotate">true</property> <!-- true enables log rotation, otherwise append -->
-    <property name="maxsize">0M</property> <!-- \d+[KMG]? Suffixes are in terms of 2**10 -->
-    <property name="maxlines">0K</property> <!-- \d+[KMG]? Suffixes are in terms of thousands -->
-    <property name="maxbackup">16</property> <!-- \d+ -->
-    <property name="daily">true</property> <!-- Automatically rotates when a log message is written after midnight -->
-  </filter>
-  <filter enabled="true">
-    <tag>error_file</tag>
-    <type>file</type>
-    <level>ERROR</level>
-    <property name="filename">logs/error.log</property>
-    <property name="json">false</property> <!-- true enables json log format, its priority is high than format -->
-    <property name="format">[%D %T] [%L] [%S] %M</property>
-    <property name="rotate">true</property> <!-- true enables log rotation, otherwise append -->
-    <property name="maxsize">0M</property> <!-- \d+[KMG]? Suffixes are in terms of 2**10 -->
-    <property name="maxlines">0K</property> <!-- \d+[KMG]? Suffixes are in terms of thousands -->
-    <property name="maxbackup">16</property> <!-- \d+ -->
-    <property name="daily">true</property> <!-- Automatically rotates when a log message is written after midnight -->
-  </filter>
-</logging>
diff --git a/examples/dubbo/go-server/profiles/release/log.yml b/examples/dubbo/go-server/profiles/release/log.yml
new file mode 100644
index 0000000000000000000000000000000000000000..b9139c2e9cb21d5f557eb53e5d6909fca64ac205
--- /dev/null
+++ b/examples/dubbo/go-server/profiles/release/log.yml
@@ -0,0 +1,28 @@
+
+level: "warn"
+development: true
+disableCaller: true
+disableStacktrace: true
+sampling:
+encoding: "console"
+
+# encoder
+encoderConfig:
+  messageKey: "message"
+  levelKey: "level"
+  timeKey: "time"
+  nameKey: "logger"
+  callerKey: "caller"
+  stacktraceKey: "stacktrace"
+  lineEnding: ""
+  levelEncoder: "capitalColor"
+  timeEncoder: "iso8601"
+  durationEncoder: "seconds"
+  callerEncoder: "short"
+  nameEncoder: ""
+
+outputPaths:
+- "stderr"
+errorOutputPaths:
+- "stderr"
+initialFields:
diff --git a/examples/dubbo/go-server/profiles/release/server.yml b/examples/dubbo/go-server/profiles/release/server.yml
index e61274ea0977f2a193572cddc2ec996e07c220b0..728e8802dd5d49aef4336597a6810724d0836c16 100644
--- a/examples/dubbo/go-server/profiles/release/server.yml
+++ b/examples/dubbo/go-server/profiles/release/server.yml
@@ -1,38 +1,69 @@
 # dubbo server yaml configure file
 
-# pprof
-pprof_enabled : true
-pprof_port : 20080
-
-# server
-transport : "http"
-net_timeout : "3s"
-
 # application config
 application_config:
-    organization : "ikurento.com"
-    name : "BDTService"
-    module : "dubbogo user-info server"
-    version : "0.0.1"
-    owner : "ZX"
-    environment : "product"
+  organization : "ikurento.com"
+  name : "BDTService"
+  module : "dubbogo user-info server"
+  version : "0.0.1"
+  owner : "ZX"
+  environment : "dev"
+
+registries :
+- id: "hangzhouzk"
+  type: "zookeeper"
+  timeout	: "3s"
+  address: "127.0.0.1:2181"
+  username: ""
+  password: ""
+
+- id: "shanghaizk"
+  type: "zookeeper"
+  timeout	: "3s"
+  address: "127.0.0.1:2182"
+  username: ""
+  password: ""
+
 
-registry: "zookeeper"
+services:
+- registries:
+  - "hangzhouzk"
+  - "shanghaizk"
+  protocol : "dubbo"
+  # 鐩稿綋浜巇ubbo.xml涓殑interface
+  interface : "com.ikurento.user.UserProvider"
+  loadbalance: "random"
+  warmup: "100"
+  cluster: "failover"
+  methods:
+  - name: "GetUser"
+    retries: 1
+    loadbalance: "random"
 
-zk_registry_config:
-    timeout	: "3s"
-    address:
-        - "127.0.0.1:2181"
-service_config_type: "default"
-service_list:
-    -
-        protocol : "dubbo"
-        # 鐩稿綋浜巇ubbo.xml涓殑interface
-        service : "com.ikurento.user.UserProvider"
+protocols:
+-   name: "dubbo"
+    ip : "127.0.0.1"
+    port : 20000
+#-   name: "jsonrpc"
+#    ip: "127.0.0.1"
+#    port: 20001
 
-server_list:
-    -
-        ip : "127.0.0.1"
-        port : 20000
-        # 鏈瑂erver鑳藉鎻愪緵鎵€鏈夋敮鎸佸悓鏍风殑Protocol鐨剆ervicelist鐨勬湇鍔�
-        protocol : "dubbo"
+protocol_conf:
+  dubbo:
+    session_number: 700
+    fail_fast_timeout: "5s"
+    session_timeout: "20s"
+    getty_session_param:
+      compress_encoding: false
+      tcp_no_delay: true
+      tcp_keep_alive: true
+      keep_alive_period: "120s"
+      tcp_r_buf_size: 262144
+      tcp_w_buf_size: 65536
+      pkg_rq_size: 1024
+      pkg_wq_size: 512
+      tcp_read_timeout: "1s"
+      tcp_write_timeout: "5s"
+      wait_timeout: "1s"
+      max_msg_len: 1024
+      session_name: "server"
diff --git a/examples/dubbo/go-server/profiles/test/log.xml b/examples/dubbo/go-server/profiles/test/log.xml
deleted file mode 100644
index 834bab5b07e72f1c250d500b60fe3af25e74cfc1..0000000000000000000000000000000000000000
--- a/examples/dubbo/go-server/profiles/test/log.xml
+++ /dev/null
@@ -1,73 +0,0 @@
-<logging>
-  <filter enabled="false">
-    <tag>stdout</tag>
-    <type>console</type>
-    <!-- level is (:?FINEST|FINE|DEBUG|TRACE|INFO|WARNING|ERROR) -->
-    <level>DEBUG</level>
-    <property name="json">false</property> <!-- true enables json log format, its priority is high than format -->
-    <property name="format">[%D %T] [%L] (%S) %M</property> <!-- log format, if json is false this option is enable -->
-  </filter>
-  <filter enabled="false">
-    <tag>debug_file</tag>
-    <type>file</type>
-    <level>DEBUG</level>
-    <property name="filename">logs/debug.log</property>
-    <property name="json">false</property> <!-- true enables json log format, its priority is high than format -->
-    <property name="format">[%D %T] [%L] [%S] %M</property>
-    <property name="rotate">true</property> <!-- true enables log rotation, otherwise append -->
-    <property name="maxsize">0M</property> <!-- \d+[KMG]? Suffixes are in terms of 2**10 -->
-    <property name="maxlines">0K</property> <!-- \d+[KMG]? Suffixes are in terms of thousands -->
-    <property name="maxbackup">16</property> <!-- \d+ -->
-    <property name="daily">true</property> <!-- Automatically rotates when a log message is written after midnight -->
-  </filter>
-  <filter enabled="false">
-    <tag>info_file</tag>
-    <type>file</type>
-    <level>INFO</level>
-    <property name="filename">logs/info.log</property>
-    <!--
-       %T - Time (15:04:05 MST)
-       %t - Time (15:04)
-       %D - Date (2006/01/02)
-       %d - Date (01/02/06)
-       %L - Level (FNST, FINE, DEBG, TRAC, WARN, EROR, CRIT)
-       %S - Source
-       %M - Message
-       It ignores unknown format strings (and removes them)
-       Recommended: "[%D %T] [%L] (%S) %M"
-    -->
-    <property name="json">false</property> <!-- true enables json log format, its priority is high than format -->
-    <property name="format">[%D %T] [%L] [%S] %M</property>
-    <property name="rotate">true</property> <!-- true enables log rotation, otherwise append -->
-    <property name="maxsize">0M</property> <!-- \d+[KMG]? Suffixes are in terms of 2**10 -->
-    <property name="maxlines">0K</property> <!-- \d+[KMG]? Suffixes are in terms of thousands -->
-    <property name="maxbackup">16</property> <!-- \d+ -->
-    <property name="daily">true</property> <!-- Automatically rotates when a log message is written after midnight -->
-  </filter>
-  <filter enabled="true">
-    <tag>warn_file</tag>
-    <type>file</type>
-    <level>WARNING</level>
-    <property name="filename">logs/warn.log</property>
-    <property name="json">false</property> <!-- true enables json log format, its priority is high than format -->
-    <property name="format">[%D %T] [%L] [%S] %M</property>
-    <property name="rotate">true</property> <!-- true enables log rotation, otherwise append -->
-    <property name="maxsize">0M</property> <!-- \d+[KMG]? Suffixes are in terms of 2**10 -->
-    <property name="maxlines">0K</property> <!-- \d+[KMG]? Suffixes are in terms of thousands -->
-    <property name="maxbackup">16</property> <!-- \d+ -->
-    <property name="daily">true</property> <!-- Automatically rotates when a log message is written after midnight -->
-  </filter>
-  <filter enabled="true">
-    <tag>error_file</tag>
-    <type>file</type>
-    <level>ERROR</level>
-    <property name="filename">logs/error.log</property>
-    <property name="json">false</property> <!-- true enables json log format, its priority is high than format -->
-    <property name="format">[%D %T] [%L] [%S] %M</property>
-    <property name="rotate">true</property> <!-- true enables log rotation, otherwise append -->
-    <property name="maxsize">0M</property> <!-- \d+[KMG]? Suffixes are in terms of 2**10 -->
-    <property name="maxlines">0K</property> <!-- \d+[KMG]? Suffixes are in terms of thousands -->
-    <property name="maxbackup">16</property> <!-- \d+ -->
-    <property name="daily">true</property> <!-- Automatically rotates when a log message is written after midnight -->
-  </filter>
-</logging>
diff --git a/examples/dubbo/go-server/profiles/test/log.yml b/examples/dubbo/go-server/profiles/test/log.yml
new file mode 100644
index 0000000000000000000000000000000000000000..d2e1d05f3f46bc4ec6c7b8a16211c13d2189219d
--- /dev/null
+++ b/examples/dubbo/go-server/profiles/test/log.yml
@@ -0,0 +1,28 @@
+
+level: "info"
+development: false
+disableCaller: false
+disableStacktrace: true
+sampling:
+encoding: "console"
+
+# encoder
+encoderConfig:
+  messageKey: "message"
+  levelKey: "level"
+  timeKey: "time"
+  nameKey: "logger"
+  callerKey: "caller"
+  stacktraceKey: "stacktrace"
+  lineEnding: ""
+  levelEncoder: "capitalColor"
+  timeEncoder: "iso8601"
+  durationEncoder: "seconds"
+  callerEncoder: "short"
+  nameEncoder: ""
+
+outputPaths:
+- "stderr"
+errorOutputPaths:
+- "stderr"
+initialFields:
diff --git a/examples/dubbo/go-server/profiles/test/server.yml b/examples/dubbo/go-server/profiles/test/server.yml
index cc73b9b642f2ba7f36c781f72e153b0547e7e8ce..728e8802dd5d49aef4336597a6810724d0836c16 100644
--- a/examples/dubbo/go-server/profiles/test/server.yml
+++ b/examples/dubbo/go-server/profiles/test/server.yml
@@ -1,38 +1,69 @@
 # dubbo server yaml configure file
 
-# pprof
-pprof_enabled : true
-pprof_port : 20080
-
-# server
-transport : "http"
-net_timeout : "3s"
-
 # application config
 application_config:
-    organization : "ikurento.com"
-    name : "BDTService"
-    module : "dubbogo user-info server"
-    version : "0.0.1"
-    owner : "ZX"
-    environment : "test"
+  organization : "ikurento.com"
+  name : "BDTService"
+  module : "dubbogo user-info server"
+  version : "0.0.1"
+  owner : "ZX"
+  environment : "dev"
+
+registries :
+- id: "hangzhouzk"
+  type: "zookeeper"
+  timeout	: "3s"
+  address: "127.0.0.1:2181"
+  username: ""
+  password: ""
+
+- id: "shanghaizk"
+  type: "zookeeper"
+  timeout	: "3s"
+  address: "127.0.0.1:2182"
+  username: ""
+  password: ""
+
 
-registry: "zookeeper"
+services:
+- registries:
+  - "hangzhouzk"
+  - "shanghaizk"
+  protocol : "dubbo"
+  # 鐩稿綋浜巇ubbo.xml涓殑interface
+  interface : "com.ikurento.user.UserProvider"
+  loadbalance: "random"
+  warmup: "100"
+  cluster: "failover"
+  methods:
+  - name: "GetUser"
+    retries: 1
+    loadbalance: "random"
 
-zk_registry_config:
-    timeout	: "3s"
-    address:
-        - "127.0.0.1:2181"
-service_config_type: "default"
-service_list:
-    -
-        protocol : "dubbo"
-        # 鐩稿綋浜巇ubbo.xml涓殑interface
-        service : "com.ikurento.user.UserProvider"
+protocols:
+-   name: "dubbo"
+    ip : "127.0.0.1"
+    port : 20000
+#-   name: "jsonrpc"
+#    ip: "127.0.0.1"
+#    port: 20001
 
-server_list:
-    -
-        ip : "127.0.0.1"
-        port : 20000
-        # 鏈瑂erver鑳藉鎻愪緵鎵€鏈夋敮鎸佸悓鏍风殑Protocol鐨剆ervicelist鐨勬湇鍔�
-        protocol : "dubbo"
+protocol_conf:
+  dubbo:
+    session_number: 700
+    fail_fast_timeout: "5s"
+    session_timeout: "20s"
+    getty_session_param:
+      compress_encoding: false
+      tcp_no_delay: true
+      tcp_keep_alive: true
+      keep_alive_period: "120s"
+      tcp_r_buf_size: 262144
+      tcp_w_buf_size: 65536
+      pkg_rq_size: 1024
+      pkg_wq_size: 512
+      tcp_read_timeout: "1s"
+      tcp_write_timeout: "5s"
+      wait_timeout: "1s"
+      max_msg_len: 1024
+      session_name: "server"
diff --git a/examples/dubbo/java-client/src/main/java/com/ikurento/user/Consumer.java b/examples/dubbo/java-client/src/main/java/com/ikurento/user/Consumer.java
index 5830ac716e8d2ea6a72b3c5921f29a734796a997..b410813c74a7432cbbe1f2e7627faba9ebb17e70 100644
--- a/examples/dubbo/java-client/src/main/java/com/ikurento/user/Consumer.java
+++ b/examples/dubbo/java-client/src/main/java/com/ikurento/user/Consumer.java
@@ -13,6 +13,7 @@ package com.ikurento.user;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Date;
+import com.alibaba.dubbo.rpc.service.EchoService;
 import java.util.List;
 
 public class Consumer {
@@ -39,10 +40,17 @@ public class Consumer {
 
     private void testGetUser() throws Exception {
         try {
+            EchoService echoService = (EchoService)userProvider;
+            Object status = echoService.$echo("OK");
+            System.out.println("echo: "+status);
             User user1 = userProvider.GetUser("A003");
             System.out.println("[" + new SimpleDateFormat("HH:mm:ss").format(new Date()) + "] " +
                     " UserInfo, Id:" + user1.getId() + ", name:" + user1.getName() + ", sex:" + user1.getSex().toString()
                     + ", age:" + user1.getAge() + ", time:" + user1.getTime().toString());
+            User user2 = userProvider.GetUser0("A003","Moorse");
+            System.out.println("[" + new SimpleDateFormat("HH:mm:ss").format(new Date()) + "] " +
+                     " UserInfo, Id:" + user2.getId() + ", name:" + user2.getName() + ", sex:" + user2.getSex().toString()
+                     + ", age:" + user2.getAge() + ", time:" + user2.getTime().toString());
         } catch (Exception e) {
             e.printStackTrace();
         }
@@ -71,7 +79,7 @@ public class Consumer {
     //鍚姩consumer鐨勫叆鍙e嚱鏁�(鍦ㄩ厤缃枃浠朵腑鎸囧畾)
     public void start() throws Exception {
         testGetUser();
-        // testGetUsers();
+        testGetUsers();
 //        Thread.sleep(120000);
 Thread.sleep(2000);
     }
diff --git a/examples/dubbo/java-client/src/main/java/com/ikurento/user/UserProvider.java b/examples/dubbo/java-client/src/main/java/com/ikurento/user/UserProvider.java
index add09807e7673805bf18553a74941466b3138203..d5bce8105673a24d78ddd3a636788d1ccf8e57a6 100644
--- a/examples/dubbo/java-client/src/main/java/com/ikurento/user/UserProvider.java
+++ b/examples/dubbo/java-client/src/main/java/com/ikurento/user/UserProvider.java
@@ -1,12 +1,12 @@
 /*
  * Copyright 1999-2011 Alibaba Group.
- *  
+ *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
- *  
+ *
  *      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.
@@ -22,4 +22,5 @@ public interface UserProvider {
 	User GetUser(String userId);
 
 	List<User> GetUsers(List<String> userIdList);
+	User GetUser0(String userId, String name);
 }
diff --git a/examples/dubbo/java-server/src/main/java/com/ikurento/user/UserProvider.java b/examples/dubbo/java-server/src/main/java/com/ikurento/user/UserProvider.java
index 55ec64c24ff0e0b79894a4f67fb85fb489d38b77..b75740bbcd26a6438d22f7d3bf08fa5e316f7aa7 100644
--- a/examples/dubbo/java-server/src/main/java/com/ikurento/user/UserProvider.java
+++ b/examples/dubbo/java-server/src/main/java/com/ikurento/user/UserProvider.java
@@ -12,6 +12,8 @@ public interface UserProvider {
 
     List<User> GetUsers(List<String> userIdList);
 
+    User GetUser0(String userId, String name);
+
     Map<String, User> GetUserMap(List<String> userIdList);
 
     User getUser(int usercode);
diff --git a/examples/dubbo/java-server/src/main/java/com/ikurento/user/UserProviderAnotherImpl.java b/examples/dubbo/java-server/src/main/java/com/ikurento/user/UserProviderAnotherImpl.java
index ff051a97e5c50e760cc9e29aca79276295caff2f..f0d7faf3df40c3d422407ddf4d2f2c501e1efdf7 100644
--- a/examples/dubbo/java-server/src/main/java/com/ikurento/user/UserProviderAnotherImpl.java
+++ b/examples/dubbo/java-server/src/main/java/com/ikurento/user/UserProviderAnotherImpl.java
@@ -32,6 +32,10 @@ public class UserProviderAnotherImpl implements UserProvider {
         return new User(userId, "Joe", 48);
     }
 
+    public User GetUser0(String userId, String name) {
+                return new User(userId, name, 48);
+    }
+
     public List<User> GetUsers(ArrayList<String> userIdList) {
         Iterator it = userIdList.iterator();
         List<User> userList = new ArrayList<User>();
diff --git a/examples/dubbo/java-server/src/main/java/com/ikurento/user/UserProviderImpl.java b/examples/dubbo/java-server/src/main/java/com/ikurento/user/UserProviderImpl.java
index 2464ca1c2c5dbb3b615315ba91decae2cd6d7166..1311bd807d59d8336f4ada1296d3a05abd31a548 100644
--- a/examples/dubbo/java-server/src/main/java/com/ikurento/user/UserProviderImpl.java
+++ b/examples/dubbo/java-server/src/main/java/com/ikurento/user/UserProviderImpl.java
@@ -32,6 +32,9 @@ public class UserProviderImpl implements UserProvider {
     public User GetUser(String userId) {
         return new User(userId, "zhangsan", 18);
     }
+    public User GetUser0(String userId, String name) {
+            return new User(userId, name, 18);
+    }
 
     public List<User> GetUsers(List<String> userIdList) {
         Iterator it = userIdList.iterator();
diff --git a/examples/dubbo/java-server/src/main/resources/META-INF/spring/dubbo.provider.xml b/examples/dubbo/java-server/src/main/resources/META-INF/spring/dubbo.provider.xml
index bdbd84417e55ee8c53e5e5d4cc011514ad70663a..b3a1b19d6764ca6db895719709412c07b348f13e 100644
--- a/examples/dubbo/java-server/src/main/resources/META-INF/spring/dubbo.provider.xml
+++ b/examples/dubbo/java-server/src/main/resources/META-INF/spring/dubbo.provider.xml
@@ -24,12 +24,13 @@
 	<dubbo:application name="user-info-server"/>
 	<!-- 杩炴帴鍒板摢涓湰鍦版敞鍐屼腑蹇� -->
 	<dubbo:registry id="ikurento"  address="zookeeper://127.0.0.1:2181" />
+	<dubbo:registry id="ikurento2"  address="zookeeper://127.0.0.1:2182" />
 	<!-- 鐢╠ubbo鍗忚鍦�20880绔彛鏆撮湶鏈嶅姟 -->
     <!-- dubbo:protocol host="127.0.0.1" / -->
 	<dubbo:protocol id="dubbo" name="dubbo" host="127.0.0.1" port="20010" />
 	<dubbo:protocol id="jsonrpc" name="jsonrpc" host="127.0.0.1" port="10010" />
 	<!-- 澹版槑闇€瑕佹毚闇茬殑鏈嶅姟鎺ュ彛 -->
-	<dubbo:service registry="ikurento" timeout="3000" interface="com.ikurento.user.UserProvider" ref="demoService"/>
+	<dubbo:service registry="ikurento,ikurento2" timeout="3000" interface="com.ikurento.user.UserProvider" ref="demoService"/>
 	<dubbo:service registry="ikurento" timeout="3000" interface="com.ikurento.user.UserProvider" ref="otherService" version="2.0"/>
 	<dubbo:service registry="ikurento" timeout="3000" interface="com.ikurento.user.UserProvider" ref="otherService" group="as" version="2.0"/>
 
diff --git a/examples/jsonrpc/go-client/app/client.go b/examples/jsonrpc/go-client/app/client.go
index 26f39a21700ea811b4bac367f7ea8f03086a6ed7..b71f0efa20243ddf2635c56ee0eca7340db6ce03 100644
--- a/examples/jsonrpc/go-client/app/client.go
+++ b/examples/jsonrpc/go-client/app/client.go
@@ -1,152 +1,107 @@
+// Copyright 2016-2019 Yincheng Fang, Alex Stocks
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package main
 
 import (
+	"context"
 	"fmt"
-	"net/http"
-	_ "net/http/pprof"
 	"os"
 	"os/signal"
-	"strconv"
 	"syscall"
 	"time"
 )
 
 import (
-	"github.com/AlexStocks/goext/log"
-	"github.com/AlexStocks/goext/net"
-	log "github.com/AlexStocks/log4go"
-	jerrors "github.com/juju/errors"
+	"github.com/dubbo/go-for-apache-dubbo/common/logger"
 )
 
 import (
-	"github.com/dubbo/go-for-apache-dubbo/client/invoker"
-	"github.com/dubbo/go-for-apache-dubbo/examples"
-	"github.com/dubbo/go-for-apache-dubbo/jsonrpc"
-	"github.com/dubbo/go-for-apache-dubbo/plugins"
-	"github.com/dubbo/go-for-apache-dubbo/public"
-	"github.com/dubbo/go-for-apache-dubbo/registry"
-	"github.com/dubbo/go-for-apache-dubbo/registry/zookeeper"
+	_ "github.com/dubbo/go-for-apache-dubbo/common/proxy/proxy_factory"
+	"github.com/dubbo/go-for-apache-dubbo/config"
+	_ "github.com/dubbo/go-for-apache-dubbo/protocol/jsonrpc"
+	_ "github.com/dubbo/go-for-apache-dubbo/registry/protocol"
+
+	_ "github.com/dubbo/go-for-apache-dubbo/filter/impl"
+
+	_ "github.com/dubbo/go-for-apache-dubbo/cluster/cluster_impl"
+	_ "github.com/dubbo/go-for-apache-dubbo/cluster/loadbalance"
+	_ "github.com/dubbo/go-for-apache-dubbo/registry/zookeeper"
 )
 
 var (
 	survivalTimeout int = 10e9
-	clientInvoker   *invoker.Invoker
 )
 
+// they are necessary:
+// 		export CONF_CONSUMER_FILE_PATH="xxx"
+// 		export APP_LOG_CONF_FILE="xxx"
 func main() {
 
-	clientConfig := examples.InitClientConfig()
-
-	initProfiling(clientConfig)
-	initClient(clientConfig)
-
-	time.Sleep(10e9)
-
-	gxlog.CInfo("\n\n\nstart to test jsonrpc")
-	testJsonrpc(clientConfig, "A003", "GetUser")
-	time.Sleep(3e9)
-
-	gxlog.CInfo("\n\n\nstart to test jsonrpc illegal method")
-
-	testJsonrpc(clientConfig, "A003", "GetUser1")
-
-	initSignal()
-}
-
-func initClient(clientConfig *examples.ClientConfig) {
-	var (
-		codecType public.CodecType
-	)
-
-	if clientConfig == nil {
-		panic(fmt.Sprintf("clientConfig is nil"))
-		return
+	conMap, _ := config.Load()
+	if conMap == nil {
+		panic("conMap is nil")
 	}
 
-	// registry
-	clientRegistry, err := plugins.PluggableRegistries[clientConfig.Registry](
-		registry.WithDubboType(registry.CONSUMER),
-		registry.WithApplicationConf(clientConfig.Application_Config),
-		zookeeper.WithRegistryConf(clientConfig.ZkRegistryConfig),
-	)
+	println("\n\n\necho")
+	res, err := conMap["com.ikurento.user.UserProvider"].GetRPCService().(*UserProvider).Echo(context.TODO(), "OK")
 	if err != nil {
-		panic(fmt.Sprintf("fail to init registry.Registy, err:%s", jerrors.ErrorStack(err)))
-		return
+		println("echo - error: %v", err)
+	} else {
+		println("res: %v", res)
 	}
 
-	// consumer
-	clientConfig.RequestTimeout, err = time.ParseDuration(clientConfig.Request_Timeout)
+	time.Sleep(3e9)
+
+	println("\n\n\nstart to test jsonrpc")
+	user := &JsonRPCUser{}
+	err = conMap["com.ikurento.user.UserProvider"].GetRPCService().(*UserProvider).GetUser(context.TODO(), []interface{}{"A003"}, user)
 	if err != nil {
-		panic(fmt.Sprintf("time.ParseDuration(Request_Timeout{%#v}) = error{%v}",
-			clientConfig.Request_Timeout, err))
-		return
+		panic(err)
 	}
-	clientConfig.ConnectTimeout, err = time.ParseDuration(clientConfig.Connect_Timeout)
+	println("response result: %v", user)
+
+	println("\n\n\nstart to test jsonrpc - GetUser0")
+	ret, err := conMap["com.ikurento.user.UserProvider"].GetRPCService().(*UserProvider).GetUser0("A003", "Moorse")
 	if err != nil {
-		panic(fmt.Sprintf("time.ParseDuration(Connect_Timeout{%#v}) = error{%v}",
-			clientConfig.Connect_Timeout, err))
-		return
+		panic(err)
 	}
+	println("response result: %v", ret)
 
-	for idx := range clientConfig.ServiceConfigList {
-		codecType = public.GetCodecType(clientConfig.ServiceConfigList[idx].Protocol())
-		if codecType == public.CODECTYPE_UNKNOWN {
-			panic(fmt.Sprintf("unknown protocol %s", clientConfig.ServiceConfigList[idx].Protocol()))
-		}
+	println("\n\n\nstart to test jsonrpc - GetUsers")
+	ret1, err := conMap["com.ikurento.user.UserProvider"].GetRPCService().(*UserProvider).GetUsers([]interface{}{[]interface{}{"A002", "A003"}})
+	if err != nil {
+		panic(err)
 	}
+	println("response result: %v", ret1)
 
-	for _, service := range clientConfig.ServiceConfigList {
-		err = clientRegistry.Register(service)
-		if err != nil {
-			panic(fmt.Sprintf("registry.Register(service{%#v}) = error{%v}", service, jerrors.ErrorStack(err)))
-			return
-		}
+	println("\n\n\nstart to test jsonrpc - getUser")
+	user = &JsonRPCUser{}
+	err = conMap["com.ikurento.user.UserProvider"].GetRPCService().(*UserProvider).GetUser2(context.TODO(), []interface{}{1}, user)
+	if err != nil {
+		println("getUser - error: %v", err)
+	} else {
+		println("response result: %v", user)
 	}
 
-	//read the client lb config in config.yml
-	configClientLB := plugins.PluggableLoadbalance[clientConfig.ClientLoadBalance]()
-
-	//init http client & init invoker
-	clt := jsonrpc.NewHTTPClient(
-		&jsonrpc.HTTPOptions{
-			HandshakeTimeout: clientConfig.ConnectTimeout,
-			HTTPTimeout:      clientConfig.RequestTimeout,
-		},
-	)
-
-	clientInvoker, err = invoker.NewInvoker(clientRegistry,
-		invoker.WithHttpClient(clt),
-		invoker.WithLBSelector(configClientLB))
-
-}
-
-func uninitClient() {
-	log.Close()
-}
-
-func initProfiling(clientConfig *examples.ClientConfig) {
-	if !clientConfig.Pprof_Enabled {
-		return
-	}
-	const (
-		PprofPath = "/debug/pprof/"
-	)
-	var (
-		err  error
-		ip   string
-		addr string
-	)
-
-	ip, err = gxnet.GetLocalIP()
+	println("\n\n\nstart to test jsonrpc illegal method")
+	err = conMap["com.ikurento.user.UserProvider"].GetRPCService().(*UserProvider).GetUser1(context.TODO(), []interface{}{"A003"}, user)
 	if err != nil {
-		panic("cat not get local ip!")
+		panic(err)
 	}
-	addr = ip + ":" + strconv.Itoa(clientConfig.Pprof_Port)
-	log.Info("App Profiling startup on address{%v}", addr+PprofPath)
 
-	go func() {
-		log.Info(http.ListenAndServe(addr, nil))
-	}()
+	initSignal()
 }
 
 func initSignal() {
@@ -156,20 +111,23 @@ func initSignal() {
 		syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)
 	for {
 		sig := <-signals
-		log.Info("get signal %s", sig.String())
+		logger.Infof("get signal %s", sig.String())
 		switch sig {
 		case syscall.SIGHUP:
 		// reload()
 		default:
 			go time.AfterFunc(time.Duration(survivalTimeout)*time.Second, func() {
-				log.Warn("app exit now by force...")
+				logger.Warnf("app exit now by force...")
 				os.Exit(1)
 			})
 
 			// 瑕佷箞fastFailTimeout鏃堕棿鍐呮墽琛屽畬姣曚笅闈㈢殑閫昏緫鐒跺悗绋嬪簭閫€鍑猴紝瑕佷箞鎵ц涓婇潰鐨勮秴鏃跺嚱鏁扮▼搴忓己琛岄€€鍑�
-			uninitClient()
 			fmt.Println("app exit now...")
 			return
 		}
 	}
 }
+
+func println(format string, args ...interface{}) {
+	fmt.Printf("\033[32;40m"+format+"\033[0m\n", args...)
+}
diff --git a/examples/jsonrpc/go-client/app/test.go b/examples/jsonrpc/go-client/app/test.go
deleted file mode 100644
index a0128c010dc980d05094dac342e03cb4e193568f..0000000000000000000000000000000000000000
--- a/examples/jsonrpc/go-client/app/test.go
+++ /dev/null
@@ -1,66 +0,0 @@
-package main
-
-import (
-	"context"
-	"fmt"
-	_ "net/http/pprof"
-)
-
-import (
-	"github.com/AlexStocks/goext/log"
-)
-
-import (
-	"github.com/dubbo/go-for-apache-dubbo/client"
-	"github.com/dubbo/go-for-apache-dubbo/examples"
-	"github.com/dubbo/go-for-apache-dubbo/public"
-)
-
-func testJsonrpc(clientConfig *examples.ClientConfig, userKey string, method string) {
-	var (
-		err        error
-		svc        string
-		serviceIdx int
-		user       *JsonRPCUser
-		ctx        context.Context
-		req        client.Request
-	)
-
-	serviceIdx = -1
-	svc = "com.ikurento.user.UserProvider"
-	for i := range clientConfig.ServiceConfigList {
-		if clientConfig.ServiceConfigList[i].Service() == svc && clientConfig.ServiceConfigList[i].Protocol() == public.CODECTYPE_JSONRPC.String() {
-			serviceIdx = i
-			break
-		}
-	}
-	if serviceIdx == -1 {
-		panic(fmt.Sprintf("can not find service in config service list:%#v", clientConfig.ServiceConfigList))
-	}
-
-	// Create request
-	// gxlog.CInfo("jsonrpc selected service %#v", clientConfig.ServiceConfigList[serviceIdx])
-
-	// Attention the last parameter : []UserKey{userKey}
-	req, err = clientInvoker.HttpClient.NewRequest(clientConfig.ServiceConfigList[serviceIdx], method, []string{userKey})
-
-	if err != nil {
-		panic(err)
-	}
-
-	ctx = context.WithValue(context.Background(), public.DUBBOGO_CTX_KEY, map[string]string{
-		"X-Proxy-Id": "dubbogo",
-		"X-Services": svc,
-		"X-Method":   method,
-	})
-
-	user = new(JsonRPCUser)
-
-	err = clientInvoker.HttpCall(ctx, 1, req, user)
-	if err != nil {
-		panic(err)
-	} else {
-		gxlog.CInfo("response result:%s", user)
-	}
-
-}
diff --git a/examples/jsonrpc/go-client/app/user.go b/examples/jsonrpc/go-client/app/user.go
index 4ff080343887e9027d099639a9580ed4d93dfe3a..0d159876125a19bb9c75fbd21d893942763a67c8 100644
--- a/examples/jsonrpc/go-client/app/user.go
+++ b/examples/jsonrpc/go-client/app/user.go
@@ -1,13 +1,33 @@
+// Copyright 2016-2019 Yincheng Fang, Alex Stocks
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package main
 
 import (
+	"context"
 	"fmt"
+	"time"
 )
 
 import (
-	"github.com/AlexStocks/goext/time"
+	"github.com/dubbo/go-for-apache-dubbo/config"
 )
 
+func init() {
+	config.SetConsumerService(new(UserProvider))
+}
+
 type JsonRPCUser struct {
 	ID   string `json:"id"`
 	Name string `json:"name"`
@@ -19,6 +39,23 @@ type JsonRPCUser struct {
 func (u JsonRPCUser) String() string {
 	return fmt.Sprintf(
 		"User{ID:%s, Name:%s, Age:%d, Time:%s, Sex:%s}",
-		u.ID, u.Name, u.Age, gxtime.YMDPrint(int(u.Time), 0), u.Sex,
+		u.ID, u.Name, u.Age, time.Unix(u.Time, 0).Format("2006-01-02 15:04:05.99999"), u.Sex,
 	)
 }
+
+type UserProvider struct {
+	GetUsers func(req []interface{}) ([]JsonRPCUser, error)
+	GetUser  func(ctx context.Context, req []interface{}, rsp *JsonRPCUser) error
+	GetUser0 func(id string, name string) (JsonRPCUser, error)
+	GetUser1 func(ctx context.Context, req []interface{}, rsp *JsonRPCUser) error
+	GetUser2 func(ctx context.Context, req []interface{}, rsp *JsonRPCUser) error `dubbo:"getUser"`
+	Echo     func(ctx context.Context, req interface{}) (interface{}, error)      // Echo represent EchoFilter will be used
+}
+
+func (u *UserProvider) Service() string {
+	return "com.ikurento.user.UserProvider"
+}
+
+func (u *UserProvider) Version() string {
+	return ""
+}
diff --git a/examples/jsonrpc/go-client/assembly/bin/load.sh b/examples/jsonrpc/go-client/assembly/bin/load.sh
index 72127283082e62d9d2bdf2a4b4934db56674535b..07d5d15eac7b7974845e36c3807e7ec55875de65 100644
--- a/examples/jsonrpc/go-client/assembly/bin/load.sh
+++ b/examples/jsonrpc/go-client/assembly/bin/load.sh
@@ -23,13 +23,13 @@ else
     APP_NAME="APPLICATION_NAME.exe"
 fi
 
-export APP_CONF_FILE=${PROJECT_HOME}"TARGET_CONF_FILE"
+export CONF_CONSUMER_FILE_PATH=${PROJECT_HOME}"TARGET_CONF_FILE"
 export APP_LOG_CONF_FILE=${PROJECT_HOME}"TARGET_LOG_CONF_FILE"
 # export GOTRACEBACK=system
 # export GODEBUG=gctrace=1
 
 usage() {
-    echo "Usage: $0 start"
+    echo "Usage: $0 start [conf suffix]"
     echo "       $0 stop"
     echo "       $0 term"
     echo "       $0 restart"
@@ -40,6 +40,16 @@ usage() {
 }
 
 start() {
+    arg=$1
+    if [ "$arg" = "" ];then
+        echo "No registry type! Default client.yml!"
+    else
+        export CONF_CONSUMER_FILE_PATH=${CONF_CONSUMER_FILE_PATH//\.yml/\_$arg\.yml}
+    fi
+    if [ ! -f "${CONF_CONSUMER_FILE_PATH}" ];then
+        echo $CONF_CONSUMER_FILE_PATH" is not existing!"
+        return
+    fi
     APP_LOG_PATH=${PROJECT_HOME}"logs/"
     mkdir -p ${APP_LOG_PATH}
     APP_BIN=${PROJECT_HOME}sbin/${APP_NAME}
@@ -158,7 +168,7 @@ crontab() {
 opt=$1
 case C"$opt" in
     Cstart)
-        start
+        start $2
         ;;
     Cstop)
         stop
@@ -168,7 +178,7 @@ case C"$opt" in
         ;;
     Crestart)
         term
-        start
+        start $2
         ;;
     Clist)
         list
diff --git a/examples/jsonrpc/go-client/assembly/common/app.properties b/examples/jsonrpc/go-client/assembly/common/app.properties
index a4fe0dc49c83e7c180408b02010ebf4bbefc98a9..6bbd6db850ceeaf5ff873fee01a3578237cbd557 100644
--- a/examples/jsonrpc/go-client/assembly/common/app.properties
+++ b/examples/jsonrpc/go-client/assembly/common/app.properties
@@ -14,4 +14,4 @@ export TARGET_EXEC_NAME="user_info_client"
 export BUILD_PACKAGE="app"
 
 export TARGET_CONF_FILE="conf/client.yml"
-export TARGET_LOG_CONF_FILE="conf/log.xml"
+export TARGET_LOG_CONF_FILE="conf/log.yml"
diff --git a/examples/jsonrpc/go-client/profiles/dev/client.yml b/examples/jsonrpc/go-client/profiles/dev/client.yml
index 47a364f90dc90ba03914d2e6bfffd690368edad1..50ed8d5011cbbd08b133f040eb0d71edb970f146 100644
--- a/examples/jsonrpc/go-client/profiles/dev/client.yml
+++ b/examples/jsonrpc/go-client/profiles/dev/client.yml
@@ -1,19 +1,10 @@
 # dubbo client yaml configure file
 
-# pprof
-pprof_enabled : true
-pprof_port : 10086
-
+check: true
 # client
-request_timeout : "3500ms"
-net_io_timeout : "2s"
-retries : 1
+request_timeout : "3s"
 # connect timeout
-connect_timeout : "100ms"
-selector : "cache"
-selector_ttl : "10m"
-registry : "zookeeper"
-client_load_balance: "round_robin"
+connect_timeout : "3s"
 
 # application config
 application_config:
@@ -24,13 +15,29 @@ application_config:
     owner : "ZX"
     environment : "dev"
 
-zk_registry_config:
-    timeout	: "3s"
-    address:
-        - "127.0.0.1:2181"
+registries :
+- id: "hangzhouzk"
+  type: "zookeeper"
+  timeout	: "3s"
+  address: "127.0.0.1:2181"
+  username: ""
+  password: ""
+
+- id: "shanghaizk"
+  type: "zookeeper"
+  timeout	: "3s"
+  address: "127.0.0.1:2182"
+  username: ""
+  password: ""
+
+references:
+- registries :
+  - "hangzhouzk"
+  - "shanghaizk"
 
-service_config_type: "default"
-service_list:
-    -
-        protocol : "jsonrpc"
-        service : "com.ikurento.user.UserProvider"
+  protocol : "jsonrpc"
+  interface : "com.ikurento.user.UserProvider"
+  cluster: "failover"
+  methods :
+  - name: "GetUser"
+    retries: 3
diff --git a/examples/jsonrpc/go-client/profiles/dev/log.xml b/examples/jsonrpc/go-client/profiles/dev/log.xml
deleted file mode 100644
index d2a0d89394aa2b5a882924752d9b7bab7f424dc7..0000000000000000000000000000000000000000
--- a/examples/jsonrpc/go-client/profiles/dev/log.xml
+++ /dev/null
@@ -1,73 +0,0 @@
-<logging>
-  <filter enabled="true">
-    <tag>stdout</tag>
-    <type>console</type>
-    <!-- level is (:?FINEST|FINE|DEBUG|TRACE|INFO|WARNING|ERROR) -->
-    <level>DEBUG</level>
-    <property name="json">false</property> <!-- true enables json log format, its priority is high than format -->
-    <property name="format">[%D %T] [%L] (%S) %M</property> <!-- log format, if json is false this option is enable -->
-  </filter>
-  <filter enabled="true">
-    <tag>debug_file</tag>
-    <type>file</type>
-    <level>DEBUG</level>
-    <property name="filename">logs/debug.log</property>
-    <property name="json">false</property> <!-- true enables json log format, its priority is high than format -->
-    <property name="format">[%D %T] [%L] [%S] %M</property>
-    <property name="rotate">true</property> <!-- true enables log rotation, otherwise append -->
-    <property name="maxsize">0M</property> <!-- \d+[KMG]? Suffixes are in terms of 2**10 -->
-    <property name="maxlines">0K</property> <!-- \d+[KMG]? Suffixes are in terms of thousands -->
-    <property name="maxbackup">16</property> <!-- \d+ -->
-    <property name="daily">true</property> <!-- Automatically rotates when a log message is written after midnight -->
-  </filter>
-  <filter enabled="true">
-    <tag>info_file</tag>
-    <type>file</type>
-    <level>INFO</level>
-    <property name="filename">logs/info.log</property>
-    <!--
-       %T - Time (15:04:05 MST)
-       %t - Time (15:04)
-       %D - Date (2006/01/02)
-       %d - Date (01/02/06)
-       %L - Level (FNST, FINE, DEBG, TRAC, WARN, EROR, CRIT)
-       %S - Source
-       %M - Message
-       It ignores unknown format strings (and removes them)
-       Recommended: "[%D %T] [%L] (%S) %M"
-    -->
-    <property name="json">false</property> <!-- true enables json log format, its priority is high than format -->
-    <property name="format">[%D %T] [%L] [%S] %M</property>
-    <property name="rotate">true</property> <!-- true enables log rotation, otherwise append -->
-    <property name="maxsize">0M</property> <!-- \d+[KMG]? Suffixes are in terms of 2**10 -->
-    <property name="maxlines">0K</property> <!-- \d+[KMG]? Suffixes are in terms of thousands -->
-    <property name="maxbackup">16</property> <!-- \d+ -->
-    <property name="daily">true</property> <!-- Automatically rotates when a log message is written after midnight -->
-  </filter>
-  <filter enabled="true">
-    <tag>warn_file</tag>
-    <type>file</type>
-    <level>WARNING</level>
-    <property name="filename">logs/warn.log</property>
-    <property name="json">false</property> <!-- true enables json log format, its priority is high than format -->
-    <property name="format">[%D %T] [%L] [%S] %M</property>
-    <property name="rotate">true</property> <!-- true enables log rotation, otherwise append -->
-    <property name="maxsize">0M</property> <!-- \d+[KMG]? Suffixes are in terms of 2**10 -->
-    <property name="maxlines">0K</property> <!-- \d+[KMG]? Suffixes are in terms of thousands -->
-    <property name="maxbackup">16</property> <!-- \d+ -->
-    <property name="daily">true</property> <!-- Automatically rotates when a log message is written after midnight -->
-  </filter>
-  <filter enabled="true">
-    <tag>error_file</tag>
-    <type>file</type>
-    <level>ERROR</level>
-    <property name="filename">logs/error.log</property>
-    <property name="json">false</property> <!-- true enables json log format, its priority is high than format -->
-    <property name="format">[%D %T] [%L] [%S] %M</property>
-    <property name="rotate">true</property> <!-- true enables log rotation, otherwise append -->
-    <property name="maxsize">0M</property> <!-- \d+[KMG]? Suffixes are in terms of 2**10 -->
-    <property name="maxlines">0K</property> <!-- \d+[KMG]? Suffixes are in terms of thousands -->
-    <property name="maxbackup">16</property> <!-- \d+ -->
-    <property name="daily">true</property> <!-- Automatically rotates when a log message is written after midnight -->
-  </filter>
-</logging>
diff --git a/examples/jsonrpc/go-client/profiles/dev/log.yml b/examples/jsonrpc/go-client/profiles/dev/log.yml
new file mode 100644
index 0000000000000000000000000000000000000000..427308d52b028d1740dac56b66b2e54fa76c6fe2
--- /dev/null
+++ b/examples/jsonrpc/go-client/profiles/dev/log.yml
@@ -0,0 +1,28 @@
+
+level: "debug"
+development: true
+disableCaller: false
+disableStacktrace: false
+sampling:
+encoding: "console"
+
+# encoder
+encoderConfig:
+  messageKey: "message"
+  levelKey: "level"
+  timeKey: "time"
+  nameKey: "logger"
+  callerKey: "caller"
+  stacktraceKey: "stacktrace"
+  lineEnding: ""
+  levelEncoder: "capitalColor"
+  timeEncoder: "iso8601"
+  durationEncoder: "seconds"
+  callerEncoder: "short"
+  nameEncoder: ""
+
+outputPaths:
+- "stderr"
+errorOutputPaths:
+- "stderr"
+initialFields:
diff --git a/examples/jsonrpc/go-client/profiles/release/client.yml b/examples/jsonrpc/go-client/profiles/release/client.yml
index fe4828022bf56344896375716328e5cd7f4c96ae..5aae8b717dc448162e0495e4b24d079f43351f4d 100644
--- a/examples/jsonrpc/go-client/profiles/release/client.yml
+++ b/examples/jsonrpc/go-client/profiles/release/client.yml
@@ -1,36 +1,44 @@
 # dubbo client yaml configure file
 
-# pprof
-pprof_enabled : true
-pprof_port : 10086
 
+check: true
 # client
-request_timeout : "3500ms"
-net_io_timeout : "2s"
-retries : 1
+request_timeout : "3s"
 # connect timeout
-connect_timeout : "100ms"
-selector : "cache"
-selector_ttl : "10m"
-registry : "zookeeper"
-client_load_balance: "round_robin"
+connect_timeout : "3s"
 
 # application config
 application_config:
-    organization : "ikurento.com"
-    name  : "BDTService"
-    module : "dubbogo user-info client"
-    version : "0.0.1"
-    owner : "ZX"
-    environment : "product"
+  organization : "ikurento.com"
+  name  : "BDTService"
+  module : "dubbogo user-info client"
+  version : "0.0.1"
+  owner : "ZX"
+  environment : "dev"
 
-zk_registry_config:
-    timeout	: "3s"
-    address:
-        - "127.0.0.1:2181"
+registries :
+- id: "hangzhouzk"
+  type: "zookeeper"
+  timeout	: "3s"
+  address: "127.0.0.1:2181"
+  username: ""
+  password: ""
 
-service_config_type: "default"
-service_list:
-    -
-        protocol : "jsonrpc"
-        service : "com.ikurento.user.UserProvider"
+- id: "shanghaizk"
+  type: "zookeeper"
+  timeout	: "3s"
+  address: "127.0.0.1:2182"
+  username: ""
+  password: ""
+
+references:
+- registries :
+  - "hangzhouzk"
+  - "shanghaizk"
+
+  protocol : "jsonrpc"
+  interface : "com.ikurento.user.UserProvider"
+  cluster: "failover"
+  methods :
+  - name: "GetUser"
+    retries: 3
diff --git a/examples/jsonrpc/go-client/profiles/release/log.xml b/examples/jsonrpc/go-client/profiles/release/log.xml
deleted file mode 100644
index ce8f7acee7b5dd296725a5d9d9c95477c38c29dd..0000000000000000000000000000000000000000
--- a/examples/jsonrpc/go-client/profiles/release/log.xml
+++ /dev/null
@@ -1,73 +0,0 @@
-<logging>
-  <filter enabled="false">
-    <tag>stdout</tag>
-    <type>console</type>
-    <!-- level is (:?FINEST|FINE|DEBUG|TRACE|INFO|WARNING|ERROR) -->
-    <level>DEBUG</level>
-    <property name="json">false</property> <!-- true enables json log format, its priority is high than format -->
-    <property name="format">[%D %T] [%L] (%S) %M</property> <!-- log format, if json is false this option is enable -->
-  </filter>
-  <filter enabled="true">
-    <tag>debug_file</tag>
-    <type>file</type>
-    <level>DEBUG</level>
-    <property name="filename">logs/debug.log</property>
-    <property name="json">false</property> <!-- true enables json log format, its priority is high than format -->
-    <property name="format">[%D %T] [%L] [%S] %M</property>
-    <property name="rotate">true</property> <!-- true enables log rotation, otherwise append -->
-    <property name="maxsize">0M</property> <!-- \d+[KMG]? Suffixes are in terms of 2**10 -->
-    <property name="maxlines">0K</property> <!-- \d+[KMG]? Suffixes are in terms of thousands -->
-    <property name="maxbackup">16</property> <!-- \d+ -->
-    <property name="daily">true</property> <!-- Automatically rotates when a log message is written after midnight -->
-  </filter>
-  <filter enabled="true">
-    <tag>info_file</tag>
-    <type>file</type>
-    <level>INFO</level>
-    <property name="filename">logs/info.log</property>
-    <!--
-       %T - Time (15:04:05 MST)
-       %t - Time (15:04)
-       %D - Date (2006/01/02)
-       %d - Date (01/02/06)
-       %L - Level (FNST, FINE, DEBG, TRAC, WARN, EROR, CRIT)
-       %S - Source
-       %M - Message
-       It ignores unknown format strings (and removes them)
-       Recommended: "[%D %T] [%L] (%S) %M"
-    -->
-    <property name="json">false</property> <!-- true enables json log format, its priority is high than format -->
-    <property name="format">[%D %T] [%L] [%S] %M</property>
-    <property name="rotate">true</property> <!-- true enables log rotation, otherwise append -->
-    <property name="maxsize">0M</property> <!-- \d+[KMG]? Suffixes are in terms of 2**10 -->
-    <property name="maxlines">0K</property> <!-- \d+[KMG]? Suffixes are in terms of thousands -->
-    <property name="maxbackup">16</property> <!-- \d+ -->
-    <property name="daily">true</property> <!-- Automatically rotates when a log message is written after midnight -->
-  </filter>
-  <filter enabled="true">
-    <tag>warn_file</tag>
-    <type>file</type>
-    <level>WARNING</level>
-    <property name="filename">logs/warn.log</property>
-    <property name="json">false</property> <!-- true enables json log format, its priority is high than format -->
-    <property name="format">[%D %T] [%L] [%S] %M</property>
-    <property name="rotate">true</property> <!-- true enables log rotation, otherwise append -->
-    <property name="maxsize">0M</property> <!-- \d+[KMG]? Suffixes are in terms of 2**10 -->
-    <property name="maxlines">0K</property> <!-- \d+[KMG]? Suffixes are in terms of thousands -->
-    <property name="maxbackup">16</property> <!-- \d+ -->
-    <property name="daily">true</property> <!-- Automatically rotates when a log message is written after midnight -->
-  </filter>
-  <filter enabled="true">
-    <tag>error_file</tag>
-    <type>file</type>
-    <level>ERROR</level>
-    <property name="filename">logs/error.log</property>
-    <property name="json">false</property> <!-- true enables json log format, its priority is high than format -->
-    <property name="format">[%D %T] [%L] [%S] %M</property>
-    <property name="rotate">true</property> <!-- true enables log rotation, otherwise append -->
-    <property name="maxsize">0M</property> <!-- \d+[KMG]? Suffixes are in terms of 2**10 -->
-    <property name="maxlines">0K</property> <!-- \d+[KMG]? Suffixes are in terms of thousands -->
-    <property name="maxbackup">16</property> <!-- \d+ -->
-    <property name="daily">true</property> <!-- Automatically rotates when a log message is written after midnight -->
-  </filter>
-</logging>
diff --git a/examples/jsonrpc/go-client/profiles/release/log.yml b/examples/jsonrpc/go-client/profiles/release/log.yml
new file mode 100644
index 0000000000000000000000000000000000000000..b9139c2e9cb21d5f557eb53e5d6909fca64ac205
--- /dev/null
+++ b/examples/jsonrpc/go-client/profiles/release/log.yml
@@ -0,0 +1,28 @@
+
+level: "warn"
+development: true
+disableCaller: true
+disableStacktrace: true
+sampling:
+encoding: "console"
+
+# encoder
+encoderConfig:
+  messageKey: "message"
+  levelKey: "level"
+  timeKey: "time"
+  nameKey: "logger"
+  callerKey: "caller"
+  stacktraceKey: "stacktrace"
+  lineEnding: ""
+  levelEncoder: "capitalColor"
+  timeEncoder: "iso8601"
+  durationEncoder: "seconds"
+  callerEncoder: "short"
+  nameEncoder: ""
+
+outputPaths:
+- "stderr"
+errorOutputPaths:
+- "stderr"
+initialFields:
diff --git a/examples/jsonrpc/go-client/profiles/test/client.yml b/examples/jsonrpc/go-client/profiles/test/client.yml
index 71d48fd2a06d9580d476fb882b5d0fc22eef09e3..5e0c0cea0a37672e9e24f5b0f9a7faeca9754f2a 100644
--- a/examples/jsonrpc/go-client/profiles/test/client.yml
+++ b/examples/jsonrpc/go-client/profiles/test/client.yml
@@ -1,36 +1,43 @@
 # dubbo client yaml configure file
 
-# pprof
-pprof_enabled : true
-pprof_port : 10086
-
+check: true
 # client
-request_timeout : "3500ms"
-net_io_timeout : "2s"
-retries : 1
+request_timeout : "3s"
 # connect timeout
-connect_timeout : "100ms"
-selector : "cache"
-selector_ttl : "10m"
-registry : "zookeeper"
-client_load_balance: "round_robin"
+connect_timeout : "3s"
 
 # application config
 application_config:
-    organization : "ikurento.com"
-    name  : "BDTService"
-    module : "dubbogo user-info client"
-    version : "0.0.1"
-    owner : "ZX"
-    environment : "test"
+  organization : "ikurento.com"
+  name  : "BDTService"
+  module : "dubbogo user-info client"
+  version : "0.0.1"
+  owner : "ZX"
+  environment : "dev"
+
+registries :
+- id: "hangzhouzk"
+  type: "zookeeper"
+  timeout	: "3s"
+  address: "127.0.0.1:2181"
+  username: ""
+  password: ""
+
+- id: "shanghaizk"
+  type: "zookeeper"
+  timeout	: "3s"
+  address: "127.0.0.1:2182"
+  username: ""
+  password: ""
 
-zk_registry_config:
-    timeout	: "3s"
-    address:
-        - "127.0.0.1:2181"
+references:
+- registries :
+  - "hangzhouzk"
+  - "shanghaizk"
 
-service_config_type: "default"
-service_list:
-    -
-        protocol : "jsonrpc"
-        service : "com.ikurento.user.UserProvider"
+  protocol : "jsonrpc"
+  interface : "com.ikurento.user.UserProvider"
+  cluster: "failover"
+  methods :
+  - name: "GetUser"
+    retries: 3
diff --git a/examples/jsonrpc/go-client/profiles/test/log.xml b/examples/jsonrpc/go-client/profiles/test/log.xml
deleted file mode 100644
index eab84b3e6972e5b87fc9c0d3abaae78e30ab5e7c..0000000000000000000000000000000000000000
--- a/examples/jsonrpc/go-client/profiles/test/log.xml
+++ /dev/null
@@ -1,73 +0,0 @@
-<logging>
-  <filter enabled="true">
-    <tag>stdout</tag>
-    <type>console</type>
-    <!-- level is (:?FINEST|FINE|DEBUG|TRACE|INFO|WARNING|ERROR) -->
-    <level>INFO</level>
-    <property name="json">false</property> <!-- true enables json log format, its priority is high than format -->
-    <property name="format">[%D %T] [%L] (%S) %M</property> <!-- log format, if json is false this option is enable -->
-  </filter>
-  <filter enabled="true">
-    <tag>debug_file</tag>
-    <type>file</type>
-    <level>DEBUG</level>
-    <property name="filename">logs/debug.log</property>
-    <property name="json">false</property> <!-- true enables json log format, its priority is high than format -->
-    <property name="format">[%D %T] [%L] [%S] %M</property>
-    <property name="rotate">true</property> <!-- true enables log rotation, otherwise append -->
-    <property name="maxsize">0M</property> <!-- \d+[KMG]? Suffixes are in terms of 2**10 -->
-    <property name="maxlines">0K</property> <!-- \d+[KMG]? Suffixes are in terms of thousands -->
-    <property name="maxbackup">16</property> <!-- \d+ -->
-    <property name="daily">true</property> <!-- Automatically rotates when a log message is written after midnight -->
-  </filter>
-  <filter enabled="true">
-    <tag>info_file</tag>
-    <type>file</type>
-    <level>INFO</level>
-    <property name="filename">logs/info.log</property>
-    <!--
-       %T - Time (15:04:05 MST)
-       %t - Time (15:04)
-       %D - Date (2006/01/02)
-       %d - Date (01/02/06)
-       %L - Level (FNST, FINE, DEBG, TRAC, WARN, EROR, CRIT)
-       %S - Source
-       %M - Message
-       It ignores unknown format strings (and removes them)
-       Recommended: "[%D %T] [%L] (%S) %M"
-    -->
-    <property name="json">false</property> <!-- true enables json log format, its priority is high than format -->
-    <property name="format">[%D %T] [%L] [%S] %M</property>
-    <property name="rotate">true</property> <!-- true enables log rotation, otherwise append -->
-    <property name="maxsize">0M</property> <!-- \d+[KMG]? Suffixes are in terms of 2**10 -->
-    <property name="maxlines">0K</property> <!-- \d+[KMG]? Suffixes are in terms of thousands -->
-    <property name="maxbackup">16</property> <!-- \d+ -->
-    <property name="daily">true</property> <!-- Automatically rotates when a log message is written after midnight -->
-  </filter>
-  <filter enabled="true">
-    <tag>warn_file</tag>
-    <type>file</type>
-    <level>WARNING</level>
-    <property name="filename">logs/warn.log</property>
-    <property name="json">false</property> <!-- true enables json log format, its priority is high than format -->
-    <property name="format">[%D %T] [%L] [%S] %M</property>
-    <property name="rotate">true</property> <!-- true enables log rotation, otherwise append -->
-    <property name="maxsize">0M</property> <!-- \d+[KMG]? Suffixes are in terms of 2**10 -->
-    <property name="maxlines">0K</property> <!-- \d+[KMG]? Suffixes are in terms of thousands -->
-    <property name="maxbackup">16</property> <!-- \d+ -->
-    <property name="daily">true</property> <!-- Automatically rotates when a log message is written after midnight -->
-  </filter>
-  <filter enabled="true">
-    <tag>error_file</tag>
-    <type>file</type>
-    <level>ERROR</level>
-    <property name="filename">logs/error.log</property>
-    <property name="json">false</property> <!-- true enables json log format, its priority is high than format -->
-    <property name="format">[%D %T] [%L] [%S] %M</property>
-    <property name="rotate">true</property> <!-- true enables log rotation, otherwise append -->
-    <property name="maxsize">0M</property> <!-- \d+[KMG]? Suffixes are in terms of 2**10 -->
-    <property name="maxlines">0K</property> <!-- \d+[KMG]? Suffixes are in terms of thousands -->
-    <property name="maxbackup">16</property> <!-- \d+ -->
-    <property name="daily">true</property> <!-- Automatically rotates when a log message is written after midnight -->
-  </filter>
-</logging>
diff --git a/examples/jsonrpc/go-client/profiles/test/log.yml b/examples/jsonrpc/go-client/profiles/test/log.yml
new file mode 100644
index 0000000000000000000000000000000000000000..d2e1d05f3f46bc4ec6c7b8a16211c13d2189219d
--- /dev/null
+++ b/examples/jsonrpc/go-client/profiles/test/log.yml
@@ -0,0 +1,28 @@
+
+level: "info"
+development: false
+disableCaller: false
+disableStacktrace: true
+sampling:
+encoding: "console"
+
+# encoder
+encoderConfig:
+  messageKey: "message"
+  levelKey: "level"
+  timeKey: "time"
+  nameKey: "logger"
+  callerKey: "caller"
+  stacktraceKey: "stacktrace"
+  lineEnding: ""
+  levelEncoder: "capitalColor"
+  timeEncoder: "iso8601"
+  durationEncoder: "seconds"
+  callerEncoder: "short"
+  nameEncoder: ""
+
+outputPaths:
+- "stderr"
+errorOutputPaths:
+- "stderr"
+initialFields:
diff --git a/examples/jsonrpc/go-server/app/config.go b/examples/jsonrpc/go-server/app/config.go
deleted file mode 100644
index 3a4ef05428117a762f5dd54c214cdbe70c1d692a..0000000000000000000000000000000000000000
--- a/examples/jsonrpc/go-server/app/config.go
+++ /dev/null
@@ -1,132 +0,0 @@
-package main
-
-import (
-	"fmt"
-	"io/ioutil"
-	"os"
-	"path"
-	"time"
-)
-
-import (
-	"github.com/AlexStocks/goext/log"
-	log "github.com/AlexStocks/log4go"
-	jerrors "github.com/juju/errors"
-	"gopkg.in/yaml.v2"
-)
-
-import (
-	"github.com/dubbo/go-for-apache-dubbo/plugins"
-	"github.com/dubbo/go-for-apache-dubbo/registry"
-	"github.com/dubbo/go-for-apache-dubbo/registry/zookeeper"
-	"github.com/dubbo/go-for-apache-dubbo/server"
-)
-
-const (
-	APP_CONF_FILE     string = "APP_CONF_FILE"
-	APP_LOG_CONF_FILE string = "APP_LOG_CONF_FILE"
-)
-
-var (
-	conf *ServerConfig
-)
-
-type (
-	ServerConfig struct {
-		// pprof
-		Pprof_Enabled bool `default:"false" yaml:"pprof_enabled"  json:"pprof_enabled,omitempty"`
-		Pprof_Port    int  `default:"10086"  yaml:"pprof_port" json:"pprof_port,omitempty"`
-
-		// transport & registry
-		Transport  string `default:"http"  yaml:"transport" json:"transport,omitempty"`
-		NetTimeout string `default:"100ms"  yaml:"net_timeout" json:"net_timeout,omitempty"` // in ms
-		netTimeout time.Duration
-		// application
-		Application_Config registry.ApplicationConfig `yaml:"application_config" json:"application_config,omitempty"`
-		// Registry_Address  string `default:"192.168.35.3:2181"`
-		Registry         string                     `default:"zookeeper"  yaml:"registry" json:"registry,omitempty"`
-		ZkRegistryConfig zookeeper.ZkRegistryConfig `yaml:"zk_registry_config" json:"zk_registry_config,omitempty"`
-
-		ServiceConfigType    string                           `default:"default" yaml:"service_config_type" json:"service_config_type,omitempty"`
-		ServiceConfigList    []registry.ProviderServiceConfig `yaml:"-"`
-		ServiceConfigMapList []map[string]string              `yaml:"service_list" json:"service_list,omitempty"`
-
-		ServerConfigList []server.ServerConfig `yaml:"server_list" json:"server_list,omitempty"`
-	}
-)
-
-func initServerConf() *ServerConfig {
-	var (
-		err      error
-		confFile string
-	)
-
-	confFile = os.Getenv(APP_CONF_FILE)
-	if confFile == "" {
-		panic(fmt.Sprintf("application configure file name is nil"))
-		return nil
-	}
-	if path.Ext(confFile) != ".yml" {
-		panic(fmt.Sprintf("application configure file name{%v} suffix must be .yml", confFile))
-		return nil
-	}
-
-	conf = &ServerConfig{}
-	confFileStream, err := ioutil.ReadFile(confFile)
-	if err != nil {
-		panic(fmt.Sprintf("ioutil.ReadFile(file:%s) = error:%s", confFile, jerrors.ErrorStack(err)))
-		return nil
-	}
-	err = yaml.Unmarshal(confFileStream, conf)
-
-	//鍔ㄦ€佸姞杞絪ervice config
-	//璁剧疆榛樿ProviderServiceConfig绫�
-	plugins.SetDefaultProviderServiceConfig(conf.ServiceConfigType)
-	for _, service := range conf.ServiceConfigMapList {
-
-		svc := plugins.DefaultProviderServiceConfig()()
-		svc.SetProtocol(service["protocol"])
-		svc.SetService(service["service"])
-		conf.ServiceConfigList = append(conf.ServiceConfigList, svc)
-	}
-	//鍔ㄦ€佸姞杞絪ervice config  end
-	if err != nil {
-		panic(fmt.Sprintf("yaml.Unmarshal() = error:%s", jerrors.ErrorStack(err)))
-		return nil
-	}
-	if conf.netTimeout, err = time.ParseDuration(conf.NetTimeout); err != nil {
-		panic(fmt.Sprintf("time.ParseDuration(NetTimeout:%#v) = error:%s", conf.NetTimeout, err))
-		return nil
-	}
-	if conf.ZkRegistryConfig.Timeout, err = time.ParseDuration(conf.ZkRegistryConfig.TimeoutStr); err != nil {
-		panic(fmt.Sprintf("time.ParseDuration(Registry_Config.Timeout:%#v) = error:%s",
-			conf.ZkRegistryConfig.TimeoutStr, err))
-		return nil
-	}
-
-	gxlog.CInfo("config{%#v}\n", conf)
-
-	return conf
-}
-
-func configInit() error {
-	var (
-		confFile string
-	)
-
-	initServerConf()
-
-	confFile = os.Getenv(APP_LOG_CONF_FILE)
-	if confFile == "" {
-		panic(fmt.Sprintf("log configure file name is nil"))
-		return nil
-	}
-	if path.Ext(confFile) != ".xml" {
-		panic(fmt.Sprintf("log configure file name{%v} suffix must be .xml", confFile))
-		return nil
-	}
-
-	log.LoadConfiguration(confFile)
-
-	return nil
-}
diff --git a/examples/jsonrpc/go-server/app/server.go b/examples/jsonrpc/go-server/app/server.go
index 2969feb173d8afb3b45117fb3b376dad363cd7c1..90c78413c45b54aa883868b140290f22802ebd15 100644
--- a/examples/jsonrpc/go-server/app/server.go
+++ b/examples/jsonrpc/go-server/app/server.go
@@ -1,140 +1,75 @@
+// Copyright 2016-2019 Yincheng Fang, Alex Stocks
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package main
 
 import (
 	"fmt"
-	"net/http"
-	_ "net/http/pprof"
 	"os"
 	"os/signal"
-	"strconv"
 	"syscall"
+	"time"
 )
 
 import (
-	"github.com/AlexStocks/goext/net"
-	"github.com/AlexStocks/goext/time"
-	log "github.com/AlexStocks/log4go"
-	jerrors "github.com/juju/errors"
-)
+	"github.com/dubbo/go-for-apache-dubbo/common/logger"
+	_ "github.com/dubbo/go-for-apache-dubbo/common/proxy/proxy_factory"
+	"github.com/dubbo/go-for-apache-dubbo/config"
+	_ "github.com/dubbo/go-for-apache-dubbo/protocol/jsonrpc"
+	_ "github.com/dubbo/go-for-apache-dubbo/registry/protocol"
 
-import (
-	"github.com/dubbo/go-for-apache-dubbo/jsonrpc"
-	"github.com/dubbo/go-for-apache-dubbo/plugins"
-	registry2 "github.com/dubbo/go-for-apache-dubbo/registry"
-	"github.com/dubbo/go-for-apache-dubbo/registry/zookeeper"
+	_ "github.com/dubbo/go-for-apache-dubbo/filter/impl"
+
+	_ "github.com/dubbo/go-for-apache-dubbo/cluster/cluster_impl"
+	_ "github.com/dubbo/go-for-apache-dubbo/cluster/loadbalance"
+	_ "github.com/dubbo/go-for-apache-dubbo/registry/zookeeper"
 )
 
 var (
 	survivalTimeout = int(3e9)
-	servo           *jsonrpc.Server
 )
 
+// they are necessary:
+// 		export CONF_PROVIDER_FILE_PATH="xxx"
+// 		export APP_LOG_CONF_FILE="xxx"
 func main() {
-	var (
-		err error
-	)
 
-	err = configInit()
-	if err != nil {
-		log.Error("configInit() = error{%#v}", err)
-		return
+	_, proMap := config.Load()
+	if proMap == nil {
+		panic("proMap is nil")
 	}
-	initProfiling()
-
-	servo = initServer()
-	err = servo.Handle(&UserProvider{})
-	if err != nil {
-		panic(err)
-		return
-	}
-	servo.Start()
 
 	initSignal()
 }
 
-func initServer() *jsonrpc.Server {
-	var (
-		srv *jsonrpc.Server
-	)
-
-	if conf == nil {
-		panic(fmt.Sprintf("conf is nil"))
-		return nil
-	}
-
-	// registry
-
-	registry, err := plugins.PluggableRegistries[conf.Registry](
-		registry2.WithDubboType(registry2.PROVIDER),
-		registry2.WithApplicationConf(conf.Application_Config),
-		zookeeper.WithRegistryConf(conf.ZkRegistryConfig),
-	)
-
-	if err != nil || registry == nil {
-		panic(fmt.Sprintf("fail to init registry.Registy, err:%s", jerrors.ErrorStack(err)))
-		return nil
-	}
-
-	// provider
-	srv = jsonrpc.NewServer(
-		jsonrpc.Registry(registry),
-		jsonrpc.ConfList(conf.ServerConfigList),
-		jsonrpc.ServiceConfList(conf.ServiceConfigList),
-	)
-
-	return srv
-}
-
-func uninitServer() {
-	if servo != nil {
-		servo.Stop()
-	}
-	log.Close()
-}
-
-func initProfiling() {
-	if !conf.Pprof_Enabled {
-		return
-	}
-	const (
-		PprofPath = "/debug/pprof/"
-	)
-	var (
-		err  error
-		ip   string
-		addr string
-	)
-
-	ip, err = gxnet.GetLocalIP()
-	if err != nil {
-		panic("cat not get local ip!")
-	}
-	addr = ip + ":" + strconv.Itoa(conf.Pprof_Port)
-	log.Info("App Profiling startup on address{%v}", addr+PprofPath)
-
-	go func() {
-		log.Info(http.ListenAndServe(addr, nil))
-	}()
-}
-
 func initSignal() {
 	signals := make(chan os.Signal, 1)
 	// It is not possible to block SIGKILL or syscall.SIGSTOP
 	signal.Notify(signals, os.Interrupt, os.Kill, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)
 	for {
 		sig := <-signals
-		log.Info("get signal %s", sig.String())
+		logger.Infof("get signal %s", sig.String())
 		switch sig {
 		case syscall.SIGHUP:
 		// reload()
 		default:
-			go gxtime.Future(survivalTimeout, func() {
-				log.Warn("app exit now by force...")
+			go time.AfterFunc(time.Duration(float64(survivalTimeout)*float64(time.Second)), func() {
+				logger.Warnf("app exit now by force...")
 				os.Exit(1)
 			})
 
 			// 瑕佷箞fastFailTimeout鏃堕棿鍐呮墽琛屽畬姣曚笅闈㈢殑閫昏緫鐒跺悗绋嬪簭閫€鍑猴紝瑕佷箞鎵ц涓婇潰鐨勮秴鏃跺嚱鏁扮▼搴忓己琛岄€€鍑�
-			uninitServer()
 			fmt.Println("provider app exit now...")
 			return
 		}
diff --git a/examples/jsonrpc/go-server/app/user.go b/examples/jsonrpc/go-server/app/user.go
index d8f307549b3d7faab1c9404e01048f7cbd1b4a87..9170de431a719351ee1e1af4e5c6bbcd4cbfde62 100644
--- a/examples/jsonrpc/go-server/app/user.go
+++ b/examples/jsonrpc/go-server/app/user.go
@@ -1,19 +1,39 @@
+// Copyright 2016-2019 Yincheng Fang, Alex Stocks
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package main
 
 import (
-	// "encoding/json"
 	"context"
 	"fmt"
 	"time"
 )
 
 import (
-	"github.com/AlexStocks/goext/log"
-	"github.com/AlexStocks/goext/time"
+	perrors "github.com/pkg/errors"
+)
+
+import (
+	"github.com/dubbo/go-for-apache-dubbo/config"
 )
 
 type Gender int
 
+func init() {
+	config.SetProviderService(new(UserProvider))
+}
+
 const (
 	MAN = iota
 	WOMAN
@@ -38,10 +58,6 @@ type (
 		Sex   string `json:"sex"`
 	}
 
-	UserId struct {
-		Id string
-	}
-
 	UserProvider struct {
 		user map[string]User
 	}
@@ -51,7 +67,7 @@ var (
 	DefaultUser = User{
 		Id: "0", Name: "Alex Stocks", Age: 31,
 		// Birth: int(time.Date(1985, time.November, 10, 23, 0, 0, 0, time.UTC).Unix()),
-		Birth: gxtime.YMD(1985, 11, 24, 15, 15, 0),
+		Birth: int(time.Date(1985, 11, 24, 15, 15, 0, 0, time.Local).Unix()),
 		sex:   Gender(MAN),
 	}
 
@@ -71,17 +87,6 @@ func init() {
 	}
 }
 
-/*
-// you can define your json unmarshal function here
-func (u *UserId) UnmarshalJSON(value []byte) error {
-	u.Id = string(value)
-	u.Id = strings.TrimPrefix(u.Id, "\"")
-	u.Id = strings.TrimSuffix(u.Id, `"`)
-
-	return nil
-}
-*/
-
 func (u *UserProvider) getUser(userId string) (*User, error) {
 	if user, ok := userMap.user[userId]; ok {
 		return &user, nil
@@ -90,68 +95,52 @@ func (u *UserProvider) getUser(userId string) (*User, error) {
 	return nil, fmt.Errorf("invalid user id:%s", userId)
 }
 
-/*
-// can not work
-func (u *UserProvider) GetUser(ctx context.Context, req *UserId, rsp *User) error {
+func (u *UserProvider) GetUser(ctx context.Context, req []interface{}, rsp *User) error {
 	var (
 		err  error
 		user *User
 	)
-	user, err = u.getUser(req.Id)
+
+	println("req:%#v", req)
+	user, err = u.getUser(req[0].(string))
 	if err == nil {
 		*rsp = *user
-		gxlog.CInfo("rsp:%#v", rsp)
-		// s, _ := json.Marshal(rsp)
-		// fmt.Println(string(s))
-
-		// s, _ = json.Marshal(*rsp)
-		// fmt.Println(string(s))
+		println("rsp:%#v", rsp)
 	}
 	return err
 }
-*/
 
-/*
-// work
-func (u *UserProvider) GetUser(ctx context.Context, req *string, rsp *User) error {
-	var (
-		err  error
-		user *User
-	)
+func (u *UserProvider) GetUser0(id string, name string) (User, error) {
+	var err error
 
-	gxlog.CInfo("req:%#v", *req)
-	user, err = u.getUser(*req)
-	if err == nil {
-		*rsp = *user
-		gxlog.CInfo("rsp:%#v", rsp)
-		// s, _ := json.Marshal(rsp)
-		// fmt.Println(string(s))
-
-		// s, _ = json.Marshal(*rsp)
-		// fmt.Println(string(s))
+	println("id:%s, name:%s", id, name)
+	user, err := u.getUser(id)
+	if err != nil {
+		return User{}, err
 	}
-	return err
+	if user.Name != name {
+		return User{}, perrors.New("name is not " + user.Name)
+	}
+	return *user, err
 }
-*/
 
-func (u *UserProvider) GetUser(ctx context.Context, req []string, rsp *User) error {
-	var (
-		err  error
-		user *User
-	)
+func (u *UserProvider) GetUsers(req []interface{}) ([]User, error) {
+	var err error
 
-	gxlog.CInfo("req:%#v", req)
-	user, err = u.getUser(req[0])
-	if err == nil {
-		*rsp = *user
-		gxlog.CInfo("rsp:%#v", rsp)
-		// s, _ := json.Marshal(rsp)
-		// fmt.Println("hello0:", string(s))
-
-		// s, _ = json.Marshal(*rsp)
-		// fmt.Println("hello1:", string(s))
+	println("req:%s", req)
+	t := req[0].([]interface{})
+	user, err := u.getUser(t[0].(string))
+	if err != nil {
+		return nil, err
 	}
-	return err
+	println("user:%v", user)
+	user1, err := u.getUser(t[1].(string))
+	if err != nil {
+		return nil, err
+	}
+	println("user1:%v", user1)
+
+	return []User{*user, *user1}, err
 }
 
 func (u *UserProvider) Service() string {
@@ -161,3 +150,7 @@ func (u *UserProvider) Service() string {
 func (u *UserProvider) Version() string {
 	return ""
 }
+
+func println(format string, args ...interface{}) {
+	fmt.Printf("\033[32;40m"+format+"\033[0m\n", args...)
+}
diff --git a/examples/jsonrpc/go-server/assembly/bin/load.sh b/examples/jsonrpc/go-server/assembly/bin/load.sh
index e202ff65f436f08191ae5364378f659de858777a..47fc5e38ded155a43c30b8cbf4d2a5ae04b58d0c 100644
--- a/examples/jsonrpc/go-server/assembly/bin/load.sh
+++ b/examples/jsonrpc/go-server/assembly/bin/load.sh
@@ -20,11 +20,11 @@ if [[ ${OS_NAME} != "Windows" ]]; then
     PROJECT_HOME=${PROJECT_HOME}"/"
 fi
 
-export APP_CONF_FILE=${PROJECT_HOME}"TARGET_CONF_FILE"
+export CONF_PROVIDER_FILE_PATH=${PROJECT_HOME}"TARGET_CONF_FILE"
 export APP_LOG_CONF_FILE=${PROJECT_HOME}"TARGET_LOG_CONF_FILE"
 
 usage() {
-    echo "Usage: $0 start"
+    echo "Usage: $0 start [conf suffix]"
     echo "       $0 stop"
     echo "       $0 term"
     echo "       $0 restart"
@@ -35,6 +35,16 @@ usage() {
 }
 
 start() {
+    arg=$1
+    if [ "$arg" = "" ];then
+        echo "No registry type! Default server.yml!"
+    else
+        export CONF_PROVIDER_FILE_PATH=${CONF_PROVIDER_FILE_PATH//\.yml/\_$arg\.yml}
+    fi
+    if [ ! -f "${CONF_PROVIDER_FILE_PATH}" ];then
+        echo $CONF_PROVIDER_FILE_PATH" is not existing!"
+        return
+    fi
     APP_LOG_PATH="${PROJECT_HOME}logs/"
     mkdir -p ${APP_LOG_PATH}
     APP_BIN=${PROJECT_HOME}sbin/${APP_NAME}
@@ -112,7 +122,7 @@ list() {
 opt=$1
 case C"$opt" in
     Cstart)
-        start
+        start $2
         ;;
     Cstop)
         stop
@@ -122,7 +132,7 @@ case C"$opt" in
         ;;
     Crestart)
         term
-        start
+        start $2
         ;;
     Clist)
         list
diff --git a/examples/jsonrpc/go-server/assembly/common/app.properties b/examples/jsonrpc/go-server/assembly/common/app.properties
index d230d5efc4ee84c4a99e1b27e7b49d97046d91a3..dffb755b0811dd140d3f04e232f5f80ff60181df 100644
--- a/examples/jsonrpc/go-server/assembly/common/app.properties
+++ b/examples/jsonrpc/go-server/assembly/common/app.properties
@@ -14,4 +14,4 @@ TARGET_EXEC_NAME="user_info_server"
 BUILD_PACKAGE="app"
 
 TARGET_CONF_FILE="conf/server.yml"
-TARGET_LOG_CONF_FILE="conf/log.xml"
+TARGET_LOG_CONF_FILE="conf/log.yml"
diff --git a/examples/jsonrpc/go-server/profiles/dev/log.xml b/examples/jsonrpc/go-server/profiles/dev/log.xml
deleted file mode 100644
index d2a0d89394aa2b5a882924752d9b7bab7f424dc7..0000000000000000000000000000000000000000
--- a/examples/jsonrpc/go-server/profiles/dev/log.xml
+++ /dev/null
@@ -1,73 +0,0 @@
-<logging>
-  <filter enabled="true">
-    <tag>stdout</tag>
-    <type>console</type>
-    <!-- level is (:?FINEST|FINE|DEBUG|TRACE|INFO|WARNING|ERROR) -->
-    <level>DEBUG</level>
-    <property name="json">false</property> <!-- true enables json log format, its priority is high than format -->
-    <property name="format">[%D %T] [%L] (%S) %M</property> <!-- log format, if json is false this option is enable -->
-  </filter>
-  <filter enabled="true">
-    <tag>debug_file</tag>
-    <type>file</type>
-    <level>DEBUG</level>
-    <property name="filename">logs/debug.log</property>
-    <property name="json">false</property> <!-- true enables json log format, its priority is high than format -->
-    <property name="format">[%D %T] [%L] [%S] %M</property>
-    <property name="rotate">true</property> <!-- true enables log rotation, otherwise append -->
-    <property name="maxsize">0M</property> <!-- \d+[KMG]? Suffixes are in terms of 2**10 -->
-    <property name="maxlines">0K</property> <!-- \d+[KMG]? Suffixes are in terms of thousands -->
-    <property name="maxbackup">16</property> <!-- \d+ -->
-    <property name="daily">true</property> <!-- Automatically rotates when a log message is written after midnight -->
-  </filter>
-  <filter enabled="true">
-    <tag>info_file</tag>
-    <type>file</type>
-    <level>INFO</level>
-    <property name="filename">logs/info.log</property>
-    <!--
-       %T - Time (15:04:05 MST)
-       %t - Time (15:04)
-       %D - Date (2006/01/02)
-       %d - Date (01/02/06)
-       %L - Level (FNST, FINE, DEBG, TRAC, WARN, EROR, CRIT)
-       %S - Source
-       %M - Message
-       It ignores unknown format strings (and removes them)
-       Recommended: "[%D %T] [%L] (%S) %M"
-    -->
-    <property name="json">false</property> <!-- true enables json log format, its priority is high than format -->
-    <property name="format">[%D %T] [%L] [%S] %M</property>
-    <property name="rotate">true</property> <!-- true enables log rotation, otherwise append -->
-    <property name="maxsize">0M</property> <!-- \d+[KMG]? Suffixes are in terms of 2**10 -->
-    <property name="maxlines">0K</property> <!-- \d+[KMG]? Suffixes are in terms of thousands -->
-    <property name="maxbackup">16</property> <!-- \d+ -->
-    <property name="daily">true</property> <!-- Automatically rotates when a log message is written after midnight -->
-  </filter>
-  <filter enabled="true">
-    <tag>warn_file</tag>
-    <type>file</type>
-    <level>WARNING</level>
-    <property name="filename">logs/warn.log</property>
-    <property name="json">false</property> <!-- true enables json log format, its priority is high than format -->
-    <property name="format">[%D %T] [%L] [%S] %M</property>
-    <property name="rotate">true</property> <!-- true enables log rotation, otherwise append -->
-    <property name="maxsize">0M</property> <!-- \d+[KMG]? Suffixes are in terms of 2**10 -->
-    <property name="maxlines">0K</property> <!-- \d+[KMG]? Suffixes are in terms of thousands -->
-    <property name="maxbackup">16</property> <!-- \d+ -->
-    <property name="daily">true</property> <!-- Automatically rotates when a log message is written after midnight -->
-  </filter>
-  <filter enabled="true">
-    <tag>error_file</tag>
-    <type>file</type>
-    <level>ERROR</level>
-    <property name="filename">logs/error.log</property>
-    <property name="json">false</property> <!-- true enables json log format, its priority is high than format -->
-    <property name="format">[%D %T] [%L] [%S] %M</property>
-    <property name="rotate">true</property> <!-- true enables log rotation, otherwise append -->
-    <property name="maxsize">0M</property> <!-- \d+[KMG]? Suffixes are in terms of 2**10 -->
-    <property name="maxlines">0K</property> <!-- \d+[KMG]? Suffixes are in terms of thousands -->
-    <property name="maxbackup">16</property> <!-- \d+ -->
-    <property name="daily">true</property> <!-- Automatically rotates when a log message is written after midnight -->
-  </filter>
-</logging>
diff --git a/examples/jsonrpc/go-server/profiles/dev/log.yml b/examples/jsonrpc/go-server/profiles/dev/log.yml
new file mode 100644
index 0000000000000000000000000000000000000000..427308d52b028d1740dac56b66b2e54fa76c6fe2
--- /dev/null
+++ b/examples/jsonrpc/go-server/profiles/dev/log.yml
@@ -0,0 +1,28 @@
+
+level: "debug"
+development: true
+disableCaller: false
+disableStacktrace: false
+sampling:
+encoding: "console"
+
+# encoder
+encoderConfig:
+  messageKey: "message"
+  levelKey: "level"
+  timeKey: "time"
+  nameKey: "logger"
+  callerKey: "caller"
+  stacktraceKey: "stacktrace"
+  lineEnding: ""
+  levelEncoder: "capitalColor"
+  timeEncoder: "iso8601"
+  durationEncoder: "seconds"
+  callerEncoder: "short"
+  nameEncoder: ""
+
+outputPaths:
+- "stderr"
+errorOutputPaths:
+- "stderr"
+initialFields:
diff --git a/examples/jsonrpc/go-server/profiles/dev/server.yml b/examples/jsonrpc/go-server/profiles/dev/server.yml
index 2e0abd5151ca8fcc93375f227379e766d034aabf..945df75c1018280d70c9e78f48d86bc3dc3453ae 100644
--- a/examples/jsonrpc/go-server/profiles/dev/server.yml
+++ b/examples/jsonrpc/go-server/profiles/dev/server.yml
@@ -1,13 +1,5 @@
 # dubbo server yaml configure file
 
-# pprof
-pprof_enabled : true
-pprof_port : 20080
-
-# server
-transport : "http"
-net_timeout : "3s"
-
 # application config
 application_config:
     organization : "ikurento.com"
@@ -17,22 +9,42 @@ application_config:
     owner : "ZX"
     environment : "dev"
 
-registry: "zookeeper"
+registries :
+- id: "hangzhouzk"
+  type: "zookeeper"
+  timeout	: "3s"
+  address: "127.0.0.1:2181"
+  username: ""
+  password: ""
+
+- id: "shanghaizk"
+  type: "zookeeper"
+  timeout	: "3s"
+  address: "127.0.0.1:2182"
+  username: ""
+  password: ""
+
+
+services:
+- registries:
+  - "hangzhouzk"
+  - "shanghaizk"
+  protocol : "jsonrpc"
+    # 鐩稿綋浜巇ubbo.xml涓殑interface
+  interface : "com.ikurento.user.UserProvider"
+  loadbalance: "random"
+  warmup: "100"
+  cluster: "failover"
+  methods:
+  - name: "GetUser"
+    retries: 1
+    loadbalance: "random"
 
-zk_registry_config:
-    timeout	: "3s"
-    address:
-        - "127.0.0.1:2181"
-service_config_type: "default"
-service_list:
-    -
-        protocol : "jsonrpc"
-        # 鐩稿綋浜巇ubbo.xml涓殑interface
-        service : "com.ikurento.user.UserProvider"
+protocols:
+#-   name: "dubbo"
+#    ip : "127.0.0.1"
+#    port : 20000
+-   name: "jsonrpc"
+    ip: "127.0.0.1"
+    port: 20001
 
-server_list:
-    -
-        ip : "127.0.0.1"
-        port : 20000
-        # 鏈瑂erver鑳藉鎻愪緵鎵€鏈夋敮鎸佸悓鏍风殑Protocol鐨剆ervicelist鐨勬湇鍔�
-        protocol : "jsonrpc"
diff --git a/examples/jsonrpc/go-server/profiles/release/log.xml b/examples/jsonrpc/go-server/profiles/release/log.xml
deleted file mode 100644
index 834bab5b07e72f1c250d500b60fe3af25e74cfc1..0000000000000000000000000000000000000000
--- a/examples/jsonrpc/go-server/profiles/release/log.xml
+++ /dev/null
@@ -1,73 +0,0 @@
-<logging>
-  <filter enabled="false">
-    <tag>stdout</tag>
-    <type>console</type>
-    <!-- level is (:?FINEST|FINE|DEBUG|TRACE|INFO|WARNING|ERROR) -->
-    <level>DEBUG</level>
-    <property name="json">false</property> <!-- true enables json log format, its priority is high than format -->
-    <property name="format">[%D %T] [%L] (%S) %M</property> <!-- log format, if json is false this option is enable -->
-  </filter>
-  <filter enabled="false">
-    <tag>debug_file</tag>
-    <type>file</type>
-    <level>DEBUG</level>
-    <property name="filename">logs/debug.log</property>
-    <property name="json">false</property> <!-- true enables json log format, its priority is high than format -->
-    <property name="format">[%D %T] [%L] [%S] %M</property>
-    <property name="rotate">true</property> <!-- true enables log rotation, otherwise append -->
-    <property name="maxsize">0M</property> <!-- \d+[KMG]? Suffixes are in terms of 2**10 -->
-    <property name="maxlines">0K</property> <!-- \d+[KMG]? Suffixes are in terms of thousands -->
-    <property name="maxbackup">16</property> <!-- \d+ -->
-    <property name="daily">true</property> <!-- Automatically rotates when a log message is written after midnight -->
-  </filter>
-  <filter enabled="false">
-    <tag>info_file</tag>
-    <type>file</type>
-    <level>INFO</level>
-    <property name="filename">logs/info.log</property>
-    <!--
-       %T - Time (15:04:05 MST)
-       %t - Time (15:04)
-       %D - Date (2006/01/02)
-       %d - Date (01/02/06)
-       %L - Level (FNST, FINE, DEBG, TRAC, WARN, EROR, CRIT)
-       %S - Source
-       %M - Message
-       It ignores unknown format strings (and removes them)
-       Recommended: "[%D %T] [%L] (%S) %M"
-    -->
-    <property name="json">false</property> <!-- true enables json log format, its priority is high than format -->
-    <property name="format">[%D %T] [%L] [%S] %M</property>
-    <property name="rotate">true</property> <!-- true enables log rotation, otherwise append -->
-    <property name="maxsize">0M</property> <!-- \d+[KMG]? Suffixes are in terms of 2**10 -->
-    <property name="maxlines">0K</property> <!-- \d+[KMG]? Suffixes are in terms of thousands -->
-    <property name="maxbackup">16</property> <!-- \d+ -->
-    <property name="daily">true</property> <!-- Automatically rotates when a log message is written after midnight -->
-  </filter>
-  <filter enabled="true">
-    <tag>warn_file</tag>
-    <type>file</type>
-    <level>WARNING</level>
-    <property name="filename">logs/warn.log</property>
-    <property name="json">false</property> <!-- true enables json log format, its priority is high than format -->
-    <property name="format">[%D %T] [%L] [%S] %M</property>
-    <property name="rotate">true</property> <!-- true enables log rotation, otherwise append -->
-    <property name="maxsize">0M</property> <!-- \d+[KMG]? Suffixes are in terms of 2**10 -->
-    <property name="maxlines">0K</property> <!-- \d+[KMG]? Suffixes are in terms of thousands -->
-    <property name="maxbackup">16</property> <!-- \d+ -->
-    <property name="daily">true</property> <!-- Automatically rotates when a log message is written after midnight -->
-  </filter>
-  <filter enabled="true">
-    <tag>error_file</tag>
-    <type>file</type>
-    <level>ERROR</level>
-    <property name="filename">logs/error.log</property>
-    <property name="json">false</property> <!-- true enables json log format, its priority is high than format -->
-    <property name="format">[%D %T] [%L] [%S] %M</property>
-    <property name="rotate">true</property> <!-- true enables log rotation, otherwise append -->
-    <property name="maxsize">0M</property> <!-- \d+[KMG]? Suffixes are in terms of 2**10 -->
-    <property name="maxlines">0K</property> <!-- \d+[KMG]? Suffixes are in terms of thousands -->
-    <property name="maxbackup">16</property> <!-- \d+ -->
-    <property name="daily">true</property> <!-- Automatically rotates when a log message is written after midnight -->
-  </filter>
-</logging>
diff --git a/examples/jsonrpc/go-server/profiles/release/log.yml b/examples/jsonrpc/go-server/profiles/release/log.yml
new file mode 100644
index 0000000000000000000000000000000000000000..b9139c2e9cb21d5f557eb53e5d6909fca64ac205
--- /dev/null
+++ b/examples/jsonrpc/go-server/profiles/release/log.yml
@@ -0,0 +1,28 @@
+
+level: "warn"
+development: true
+disableCaller: true
+disableStacktrace: true
+sampling:
+encoding: "console"
+
+# encoder
+encoderConfig:
+  messageKey: "message"
+  levelKey: "level"
+  timeKey: "time"
+  nameKey: "logger"
+  callerKey: "caller"
+  stacktraceKey: "stacktrace"
+  lineEnding: ""
+  levelEncoder: "capitalColor"
+  timeEncoder: "iso8601"
+  durationEncoder: "seconds"
+  callerEncoder: "short"
+  nameEncoder: ""
+
+outputPaths:
+- "stderr"
+errorOutputPaths:
+- "stderr"
+initialFields:
diff --git a/examples/jsonrpc/go-server/profiles/release/server.yml b/examples/jsonrpc/go-server/profiles/release/server.yml
index ca33e45bccf08d09bc39338132ebfbfb13bd6fdc..597c493d932a63497d265ac37d2d0715751d1005 100644
--- a/examples/jsonrpc/go-server/profiles/release/server.yml
+++ b/examples/jsonrpc/go-server/profiles/release/server.yml
@@ -1,38 +1,50 @@
 # dubbo server yaml configure file
 
-# pprof
-pprof_enabled : true
-pprof_port : 20080
-
-# server
-transport : "http"
-net_timeout : "3s"
-
 # application config
 application_config:
-    organization : "ikurento.com"
-    name : "BDTService"
-    module : "dubbogo user-info server"
-    version : "0.0.1"
-    owner : "ZX"
-    environment : "product"
+  organization : "ikurento.com"
+  name : "BDTService"
+  module : "dubbogo user-info server"
+  version : "0.0.1"
+  owner : "ZX"
+  environment : "dev"
+
+registries :
+- id: "hangzhouzk"
+  type: "zookeeper"
+  timeout	: "3s"
+  address: "127.0.0.1:2181"
+  username: ""
+  password: ""
+
+- id: "shanghaizk"
+  type: "zookeeper"
+  timeout	: "3s"
+  address: "127.0.0.1:2182"
+  username: ""
+  password: ""
+
 
-registry: "zookeeper"
+services:
+- registries:
+  - "hangzhouzk"
+  - "shanghaizk"
+  protocol : "jsonrpc"
+  # 鐩稿綋浜巇ubbo.xml涓殑interface
+  interface : "com.ikurento.user.UserProvider"
+  loadbalance: "random"
+  warmup: "100"
+  cluster: "failover"
+  methods:
+  - name: "GetUser"
+    retries: 1
+    loadbalance: "random"
 
-zk_registry_config:
-    timeout	: "3s"
-    address:
-        - "127.0.0.1:2181"
-service_config_type: "default"
-service_list:
-    -
-        protocol : "jsonrpc"
-        # 鐩稿綋浜巇ubbo.xml涓殑interface
-        service : "com.ikurento.user.UserProvider"
+protocols:
+#-   name: "dubbo"
+#    ip : "127.0.0.1"
+#    port : 20000
+-   name: "jsonrpc"
+    ip: "127.0.0.1"
+    port: 20001
 
-server_list:
-    -
-        ip : "127.0.0.1"
-        port : 20000
-        # 鏈瑂erver鑳藉鎻愪緵鎵€鏈夋敮鎸佸悓鏍风殑Protocol鐨剆ervicelist鐨勬湇鍔�
-        protocol : "jsonrpc"
diff --git a/examples/jsonrpc/go-server/profiles/test/log.xml b/examples/jsonrpc/go-server/profiles/test/log.xml
deleted file mode 100644
index 834bab5b07e72f1c250d500b60fe3af25e74cfc1..0000000000000000000000000000000000000000
--- a/examples/jsonrpc/go-server/profiles/test/log.xml
+++ /dev/null
@@ -1,73 +0,0 @@
-<logging>
-  <filter enabled="false">
-    <tag>stdout</tag>
-    <type>console</type>
-    <!-- level is (:?FINEST|FINE|DEBUG|TRACE|INFO|WARNING|ERROR) -->
-    <level>DEBUG</level>
-    <property name="json">false</property> <!-- true enables json log format, its priority is high than format -->
-    <property name="format">[%D %T] [%L] (%S) %M</property> <!-- log format, if json is false this option is enable -->
-  </filter>
-  <filter enabled="false">
-    <tag>debug_file</tag>
-    <type>file</type>
-    <level>DEBUG</level>
-    <property name="filename">logs/debug.log</property>
-    <property name="json">false</property> <!-- true enables json log format, its priority is high than format -->
-    <property name="format">[%D %T] [%L] [%S] %M</property>
-    <property name="rotate">true</property> <!-- true enables log rotation, otherwise append -->
-    <property name="maxsize">0M</property> <!-- \d+[KMG]? Suffixes are in terms of 2**10 -->
-    <property name="maxlines">0K</property> <!-- \d+[KMG]? Suffixes are in terms of thousands -->
-    <property name="maxbackup">16</property> <!-- \d+ -->
-    <property name="daily">true</property> <!-- Automatically rotates when a log message is written after midnight -->
-  </filter>
-  <filter enabled="false">
-    <tag>info_file</tag>
-    <type>file</type>
-    <level>INFO</level>
-    <property name="filename">logs/info.log</property>
-    <!--
-       %T - Time (15:04:05 MST)
-       %t - Time (15:04)
-       %D - Date (2006/01/02)
-       %d - Date (01/02/06)
-       %L - Level (FNST, FINE, DEBG, TRAC, WARN, EROR, CRIT)
-       %S - Source
-       %M - Message
-       It ignores unknown format strings (and removes them)
-       Recommended: "[%D %T] [%L] (%S) %M"
-    -->
-    <property name="json">false</property> <!-- true enables json log format, its priority is high than format -->
-    <property name="format">[%D %T] [%L] [%S] %M</property>
-    <property name="rotate">true</property> <!-- true enables log rotation, otherwise append -->
-    <property name="maxsize">0M</property> <!-- \d+[KMG]? Suffixes are in terms of 2**10 -->
-    <property name="maxlines">0K</property> <!-- \d+[KMG]? Suffixes are in terms of thousands -->
-    <property name="maxbackup">16</property> <!-- \d+ -->
-    <property name="daily">true</property> <!-- Automatically rotates when a log message is written after midnight -->
-  </filter>
-  <filter enabled="true">
-    <tag>warn_file</tag>
-    <type>file</type>
-    <level>WARNING</level>
-    <property name="filename">logs/warn.log</property>
-    <property name="json">false</property> <!-- true enables json log format, its priority is high than format -->
-    <property name="format">[%D %T] [%L] [%S] %M</property>
-    <property name="rotate">true</property> <!-- true enables log rotation, otherwise append -->
-    <property name="maxsize">0M</property> <!-- \d+[KMG]? Suffixes are in terms of 2**10 -->
-    <property name="maxlines">0K</property> <!-- \d+[KMG]? Suffixes are in terms of thousands -->
-    <property name="maxbackup">16</property> <!-- \d+ -->
-    <property name="daily">true</property> <!-- Automatically rotates when a log message is written after midnight -->
-  </filter>
-  <filter enabled="true">
-    <tag>error_file</tag>
-    <type>file</type>
-    <level>ERROR</level>
-    <property name="filename">logs/error.log</property>
-    <property name="json">false</property> <!-- true enables json log format, its priority is high than format -->
-    <property name="format">[%D %T] [%L] [%S] %M</property>
-    <property name="rotate">true</property> <!-- true enables log rotation, otherwise append -->
-    <property name="maxsize">0M</property> <!-- \d+[KMG]? Suffixes are in terms of 2**10 -->
-    <property name="maxlines">0K</property> <!-- \d+[KMG]? Suffixes are in terms of thousands -->
-    <property name="maxbackup">16</property> <!-- \d+ -->
-    <property name="daily">true</property> <!-- Automatically rotates when a log message is written after midnight -->
-  </filter>
-</logging>
diff --git a/examples/jsonrpc/go-server/profiles/test/log.yml b/examples/jsonrpc/go-server/profiles/test/log.yml
new file mode 100644
index 0000000000000000000000000000000000000000..d2e1d05f3f46bc4ec6c7b8a16211c13d2189219d
--- /dev/null
+++ b/examples/jsonrpc/go-server/profiles/test/log.yml
@@ -0,0 +1,28 @@
+
+level: "info"
+development: false
+disableCaller: false
+disableStacktrace: true
+sampling:
+encoding: "console"
+
+# encoder
+encoderConfig:
+  messageKey: "message"
+  levelKey: "level"
+  timeKey: "time"
+  nameKey: "logger"
+  callerKey: "caller"
+  stacktraceKey: "stacktrace"
+  lineEnding: ""
+  levelEncoder: "capitalColor"
+  timeEncoder: "iso8601"
+  durationEncoder: "seconds"
+  callerEncoder: "short"
+  nameEncoder: ""
+
+outputPaths:
+- "stderr"
+errorOutputPaths:
+- "stderr"
+initialFields:
diff --git a/examples/jsonrpc/go-server/profiles/test/server.yml b/examples/jsonrpc/go-server/profiles/test/server.yml
index 0c8be2383a37277713d7702d6b10aa366fedf2fd..597c493d932a63497d265ac37d2d0715751d1005 100644
--- a/examples/jsonrpc/go-server/profiles/test/server.yml
+++ b/examples/jsonrpc/go-server/profiles/test/server.yml
@@ -1,38 +1,50 @@
 # dubbo server yaml configure file
 
-# pprof
-pprof_enabled : true
-pprof_port : 20080
-
-# server
-transport : "http"
-net_timeout : "3s"
-
 # application config
 application_config:
-    organization : "ikurento.com"
-    name : "BDTService"
-    module : "dubbogo user-info server"
-    version : "0.0.1"
-    owner : "ZX"
-    environment : "test"
+  organization : "ikurento.com"
+  name : "BDTService"
+  module : "dubbogo user-info server"
+  version : "0.0.1"
+  owner : "ZX"
+  environment : "dev"
+
+registries :
+- id: "hangzhouzk"
+  type: "zookeeper"
+  timeout	: "3s"
+  address: "127.0.0.1:2181"
+  username: ""
+  password: ""
+
+- id: "shanghaizk"
+  type: "zookeeper"
+  timeout	: "3s"
+  address: "127.0.0.1:2182"
+  username: ""
+  password: ""
+
 
-registry: "zookeeper"
+services:
+- registries:
+  - "hangzhouzk"
+  - "shanghaizk"
+  protocol : "jsonrpc"
+  # 鐩稿綋浜巇ubbo.xml涓殑interface
+  interface : "com.ikurento.user.UserProvider"
+  loadbalance: "random"
+  warmup: "100"
+  cluster: "failover"
+  methods:
+  - name: "GetUser"
+    retries: 1
+    loadbalance: "random"
 
-zk_registry_config:
-    timeout	: "3s"
-    address:
-        - "127.0.0.1:2181"
-service_config_type: "default"
-service_list:
-    -
-        protocol : "jsonrpc"
-        # 鐩稿綋浜巇ubbo.xml涓殑interface
-        service : "com.ikurento.user.UserProvider"
+protocols:
+#-   name: "dubbo"
+#    ip : "127.0.0.1"
+#    port : 20000
+-   name: "jsonrpc"
+    ip: "127.0.0.1"
+    port: 20001
 
-server_list:
-    -
-        ip : "127.0.0.1"
-        port : 20000
-        # 鏈瑂erver鑳藉鎻愪緵鎵€鏈夋敮鎸佸悓鏍风殑Protocol鐨剆ervicelist鐨勬湇鍔�
-        protocol : "jsonrpc"
diff --git a/examples/jsonrpc/java-client/src/main/java/com/ikurento/user/Consumer.java b/examples/jsonrpc/java-client/src/main/java/com/ikurento/user/Consumer.java
index 5830ac716e8d2ea6a72b3c5921f29a734796a997..b2b8e95f94b5112721e12bf738b05bdd3bd9c419 100644
--- a/examples/jsonrpc/java-client/src/main/java/com/ikurento/user/Consumer.java
+++ b/examples/jsonrpc/java-client/src/main/java/com/ikurento/user/Consumer.java
@@ -13,6 +13,7 @@ package com.ikurento.user;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Date;
+import com.alibaba.dubbo.rpc.service.EchoService;
 import java.util.List;
 
 public class Consumer {
@@ -38,11 +39,22 @@ public class Consumer {
     }
 
     private void testGetUser() throws Exception {
+        try {
+            EchoService echoService = (EchoService)userProvider;
+            Object status = echoService.$echo("OK");
+            System.out.println("echo: "+status);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
         try {
             User user1 = userProvider.GetUser("A003");
             System.out.println("[" + new SimpleDateFormat("HH:mm:ss").format(new Date()) + "] " +
                     " UserInfo, Id:" + user1.getId() + ", name:" + user1.getName() + ", sex:" + user1.getSex().toString()
                     + ", age:" + user1.getAge() + ", time:" + user1.getTime().toString());
+            User user2 = userProvider.GetUser0("A003","Moorse");
+            System.out.println("[" + new SimpleDateFormat("HH:mm:ss").format(new Date()) + "] " +
+                     " UserInfo, Id:" + user2.getId() + ", name:" + user2.getName() + ", sex:" + user2.getSex().toString()
+                     + ", age:" + user2.getAge() + ", time:" + user2.getTime().toString());
         } catch (Exception e) {
             e.printStackTrace();
         }
@@ -71,7 +83,7 @@ public class Consumer {
     //鍚姩consumer鐨勫叆鍙e嚱鏁�(鍦ㄩ厤缃枃浠朵腑鎸囧畾)
     public void start() throws Exception {
         testGetUser();
-        // testGetUsers();
+        testGetUsers();
 //        Thread.sleep(120000);
 Thread.sleep(2000);
     }
diff --git a/examples/jsonrpc/java-client/src/main/java/com/ikurento/user/UserProvider.java b/examples/jsonrpc/java-client/src/main/java/com/ikurento/user/UserProvider.java
index add09807e7673805bf18553a74941466b3138203..d5bce8105673a24d78ddd3a636788d1ccf8e57a6 100644
--- a/examples/jsonrpc/java-client/src/main/java/com/ikurento/user/UserProvider.java
+++ b/examples/jsonrpc/java-client/src/main/java/com/ikurento/user/UserProvider.java
@@ -1,12 +1,12 @@
 /*
  * Copyright 1999-2011 Alibaba Group.
- *  
+ *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
- *  
+ *
  *      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.
@@ -22,4 +22,5 @@ public interface UserProvider {
 	User GetUser(String userId);
 
 	List<User> GetUsers(List<String> userIdList);
+	User GetUser0(String userId, String name);
 }
diff --git a/examples/jsonrpc/java-client/src/main/resources/dubbo.properties b/examples/jsonrpc/java-client/src/main/resources/dubbo.properties
deleted file mode 100644
index fc7b9aedd4260e82ab5f88327dca8728092c51b4..0000000000000000000000000000000000000000
--- a/examples/jsonrpc/java-client/src/main/resources/dubbo.properties
+++ /dev/null
@@ -1,13 +0,0 @@
-### dubbo注锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷 ###
-dubbo.container = log4j,spring
-dubbo.application.name = user-info-client
-dubbo.application.owner = AlexStocks
-dubbo.application.environment  =  product
-dubbo.registry.address = zookeeper://127.0.0.1:2181
-dubbo.monitor.protocol = zookeeper
-dubbo.consumer.timeout = 10000
-dubbo.provider.timeout = 10000
-dubbo.protocol.name = jsonrpc
-
-dubbo.log4j.file = logs/client.log
-dubbo.log4j.level = WARN
diff --git a/examples/jsonrpc/java-server/src/main/java/com/ikurento/user/UserProvider.java b/examples/jsonrpc/java-server/src/main/java/com/ikurento/user/UserProvider.java
index 55ec64c24ff0e0b79894a4f67fb85fb489d38b77..b75740bbcd26a6438d22f7d3bf08fa5e316f7aa7 100644
--- a/examples/jsonrpc/java-server/src/main/java/com/ikurento/user/UserProvider.java
+++ b/examples/jsonrpc/java-server/src/main/java/com/ikurento/user/UserProvider.java
@@ -12,6 +12,8 @@ public interface UserProvider {
 
     List<User> GetUsers(List<String> userIdList);
 
+    User GetUser0(String userId, String name);
+
     Map<String, User> GetUserMap(List<String> userIdList);
 
     User getUser(int usercode);
diff --git a/examples/jsonrpc/java-server/src/main/java/com/ikurento/user/UserProviderAnotherImpl.java b/examples/jsonrpc/java-server/src/main/java/com/ikurento/user/UserProviderAnotherImpl.java
index ff051a97e5c50e760cc9e29aca79276295caff2f..157253575b9e5e75dadaaeaffa1e256374fefa5d 100644
--- a/examples/jsonrpc/java-server/src/main/java/com/ikurento/user/UserProviderAnotherImpl.java
+++ b/examples/jsonrpc/java-server/src/main/java/com/ikurento/user/UserProviderAnotherImpl.java
@@ -32,6 +32,10 @@ public class UserProviderAnotherImpl implements UserProvider {
         return new User(userId, "Joe", 48);
     }
 
+     public User GetUser0(String userId, String name) {
+         return new User(userId, name, 48);
+     }
+
     public List<User> GetUsers(ArrayList<String> userIdList) {
         Iterator it = userIdList.iterator();
         List<User> userList = new ArrayList<User>();
diff --git a/examples/jsonrpc/java-server/src/main/java/com/ikurento/user/UserProviderImpl.java b/examples/jsonrpc/java-server/src/main/java/com/ikurento/user/UserProviderImpl.java
index 2464ca1c2c5dbb3b615315ba91decae2cd6d7166..25e97dd1c4482f2ff6ae7acb1ecb01a5ed66b328 100644
--- a/examples/jsonrpc/java-server/src/main/java/com/ikurento/user/UserProviderImpl.java
+++ b/examples/jsonrpc/java-server/src/main/java/com/ikurento/user/UserProviderImpl.java
@@ -33,6 +33,10 @@ public class UserProviderImpl implements UserProvider {
         return new User(userId, "zhangsan", 18);
     }
 
+    public User GetUser0(String userId, String name) {
+        return new User(userId, name, 18);
+    }
+
     public List<User> GetUsers(List<String> userIdList) {
         Iterator it = userIdList.iterator();
         List<User> userList = new ArrayList<User>();
diff --git a/examples/jsonrpc/java-server/src/main/resources/META-INF/spring/dubbo.provider.xml b/examples/jsonrpc/java-server/src/main/resources/META-INF/spring/dubbo.provider.xml
index bdbd84417e55ee8c53e5e5d4cc011514ad70663a..b3a1b19d6764ca6db895719709412c07b348f13e 100644
--- a/examples/jsonrpc/java-server/src/main/resources/META-INF/spring/dubbo.provider.xml
+++ b/examples/jsonrpc/java-server/src/main/resources/META-INF/spring/dubbo.provider.xml
@@ -24,12 +24,13 @@
 	<dubbo:application name="user-info-server"/>
 	<!-- 杩炴帴鍒板摢涓湰鍦版敞鍐屼腑蹇� -->
 	<dubbo:registry id="ikurento"  address="zookeeper://127.0.0.1:2181" />
+	<dubbo:registry id="ikurento2"  address="zookeeper://127.0.0.1:2182" />
 	<!-- 鐢╠ubbo鍗忚鍦�20880绔彛鏆撮湶鏈嶅姟 -->
     <!-- dubbo:protocol host="127.0.0.1" / -->
 	<dubbo:protocol id="dubbo" name="dubbo" host="127.0.0.1" port="20010" />
 	<dubbo:protocol id="jsonrpc" name="jsonrpc" host="127.0.0.1" port="10010" />
 	<!-- 澹版槑闇€瑕佹毚闇茬殑鏈嶅姟鎺ュ彛 -->
-	<dubbo:service registry="ikurento" timeout="3000" interface="com.ikurento.user.UserProvider" ref="demoService"/>
+	<dubbo:service registry="ikurento,ikurento2" timeout="3000" interface="com.ikurento.user.UserProvider" ref="demoService"/>
 	<dubbo:service registry="ikurento" timeout="3000" interface="com.ikurento.user.UserProvider" ref="otherService" version="2.0"/>
 	<dubbo:service registry="ikurento" timeout="3000" interface="com.ikurento.user.UserProvider" ref="otherService" group="as" version="2.0"/>
 
diff --git a/filter/filter.go b/filter/filter.go
new file mode 100644
index 0000000000000000000000000000000000000000..384dbf1a5771ed5ddac2b4863d317616421d45f9
--- /dev/null
+++ b/filter/filter.go
@@ -0,0 +1,25 @@
+// Copyright 2016-2019 Yincheng Fang
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package filter
+
+import (
+	"github.com/dubbo/go-for-apache-dubbo/protocol"
+)
+
+// Extension - Filter
+type Filter interface {
+	Invoke(protocol.Invoker, protocol.Invocation) protocol.Result
+	OnResponse(protocol.Result, protocol.Invoker, protocol.Invocation) protocol.Result
+}
diff --git a/filter/impl/echo_filter.go b/filter/impl/echo_filter.go
new file mode 100644
index 0000000000000000000000000000000000000000..22ac6abeef6981c50fcec46ca55a7e6edf47bc08
--- /dev/null
+++ b/filter/impl/echo_filter.go
@@ -0,0 +1,57 @@
+// Copyright 2016-2019 Yincheng Fang
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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/dubbo/go-for-apache-dubbo/common/logger"
+)
+
+import (
+	"github.com/dubbo/go-for-apache-dubbo/common/constant"
+	"github.com/dubbo/go-for-apache-dubbo/common/extension"
+	"github.com/dubbo/go-for-apache-dubbo/filter"
+	"github.com/dubbo/go-for-apache-dubbo/protocol"
+)
+
+const ECHO = "echo"
+
+func init() {
+	extension.SetFilter(ECHO, GetFilter)
+}
+
+// RPCService need a Echo method in consumer, if you want to use EchoFilter
+// eg:
+//		Echo func(ctx context.Context, arg interface{}, rsp *Xxx) error
+type EchoFilter struct {
+}
+
+func (ef *EchoFilter) Invoke(invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
+	logger.Infof("invoking echo filter.")
+	logger.Debugf("%v,%v", invocation.MethodName(), len(invocation.Arguments()))
+	if invocation.MethodName() == constant.ECHO && len(invocation.Arguments()) == 1 {
+		return &protocol.RPCResult{
+			Rest: invocation.Arguments()[0],
+		}
+	}
+	return invoker.Invoke(invocation)
+}
+
+func (ef *EchoFilter) OnResponse(result protocol.Result, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
+	return result
+}
+
+func GetFilter() filter.Filter {
+	return &EchoFilter{}
+}
diff --git a/filter/impl/echo_filter_test.go b/filter/impl/echo_filter_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..f8774edb8960df9f26e1b41608f408618f4cc343
--- /dev/null
+++ b/filter/impl/echo_filter_test.go
@@ -0,0 +1,41 @@
+// Copyright 2016-2019 Yincheng Fang
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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 (
+	"testing"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+import (
+	"github.com/dubbo/go-for-apache-dubbo/common"
+	"github.com/dubbo/go-for-apache-dubbo/protocol"
+	"github.com/dubbo/go-for-apache-dubbo/protocol/invocation"
+)
+
+func TestEchoFilter_Invoke(t *testing.T) {
+	filter := GetFilter()
+	result := filter.Invoke(protocol.NewBaseInvoker(common.URL{}),
+		invocation.NewRPCInvocationForProvider("$echo", []interface{}{"OK"}, nil))
+	assert.Equal(t, "OK", result.Result())
+
+	result = filter.Invoke(protocol.NewBaseInvoker(common.URL{}),
+		invocation.NewRPCInvocationForProvider("MethodName", []interface{}{"OK"}, nil))
+	assert.Nil(t, result.Error())
+	assert.Nil(t, result.Result())
+}
diff --git a/go.mod b/go.mod
index 44d857f66138ba855dccd7ed1ae553409de560a4..b03fcd5e5850d0b292089d2c7cbe89dcd943bbef 100644
--- a/go.mod
+++ b/go.mod
@@ -1,11 +1,12 @@
 module github.com/dubbo/go-for-apache-dubbo
 
 require (
-	github.com/AlexStocks/getty v0.0.0-20190331201845-1ca64ac5a589
-	github.com/AlexStocks/goext v0.3.2
-	github.com/AlexStocks/log4go v1.0.2
-	github.com/dubbogo/hessian2 v0.0.0-20190410112310-f093e4436e31
-	github.com/juju/errors v0.0.0-20190207033735-e65537c515d7
+	github.com/dubbogo/getty v0.0.0-20190523180329-bdf5e640ea53
+	github.com/dubbogo/hessian2 v0.0.0-20190525165532-d994415a90c3
+	github.com/pkg/errors v0.8.1
 	github.com/samuel/go-zookeeper v0.0.0-20180130194729-c4fab1ac1bec
+	github.com/stretchr/testify v1.3.0
+	go.uber.org/atomic v1.4.0
+	go.uber.org/zap v1.10.0
 	gopkg.in/yaml.v2 v2.2.2
 )
diff --git a/go.sum b/go.sum
index c4f8086db8b059460c5a0565dbf57de62d8d2391..0a1a01f0f9b67a0bec600f54bd138f0ef84d86db 100644
--- a/go.sum
+++ b/go.sum
@@ -1,11 +1,6 @@
 cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
-github.com/AlexStocks/getty v0.0.0-20190331201845-1ca64ac5a589 h1:iat4jfMomN+G0SqwLJRUM2iha0LHwX+VpdT8PR8NihA=
 github.com/AlexStocks/getty v0.0.0-20190331201845-1ca64ac5a589/go.mod h1:n25mdqPgFi06sWL6mZTjm1hBIZuKwgXUVXAX+KGB97U=
-github.com/AlexStocks/goext v0.3.2 h1:Bn4C+R6/E5Yjk2Uc/voawtbGv91x9aCid92xwYL2AS0=
-github.com/AlexStocks/goext v0.3.2 h1:Bn4C+R6/E5Yjk2Uc/voawtbGv91x9aCid92xwYL2AS0=
 github.com/AlexStocks/goext v0.3.2/go.mod h1:3M5j9Pjge4CdkNg2WIjRLUeoPedJHHKwkkglDGSl3Hc=
-github.com/AlexStocks/goext v0.3.2/go.mod h1:3M5j9Pjge4CdkNg2WIjRLUeoPedJHHKwkkglDGSl3Hc=
-github.com/AlexStocks/log4go v1.0.2 h1:1K5WM8KjSUECaoXUl8FSF05KGeCJDfBrhKUBsxwUvhk=
 github.com/AlexStocks/log4go v1.0.2/go.mod h1:6kCCRo/orDo8mh5CEDOeuSSM674wBQ8M6E0K8dVOIz4=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
@@ -23,8 +18,11 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
-github.com/dubbogo/hessian2 v0.0.0-20190410112310-f093e4436e31 h1:qgwFcVu63dt6cKbMICPQN3NKo6m9fC/6FdJbffnmXOU=
+github.com/dubbogo/getty v0.0.0-20190523180329-bdf5e640ea53 h1:bniSNoC4xnAbrx4estwc9F0qkWnh6ZDsAS0y9d7mPos=
+github.com/dubbogo/getty v0.0.0-20190523180329-bdf5e640ea53/go.mod h1:cRMSuoCmwc5lULFFnYZTxyCfZhObmRTNbS7XRnPNHSo=
 github.com/dubbogo/hessian2 v0.0.0-20190410112310-f093e4436e31/go.mod h1:v+gfInE8fm/k3Fjkb2oUCKSO9LKbWvf+PtweEI89BmI=
+github.com/dubbogo/hessian2 v0.0.0-20190525165532-d994415a90c3 h1:qK0t4cWXFAng5VoPRdvnUyRyB9TdE+Q7G3ROOEY86tE=
+github.com/dubbogo/hessian2 v0.0.0-20190525165532-d994415a90c3/go.mod h1:XFGDn4oSZX26zkcfhkM/fCJrOqwQJxk/xgWW1KMJBKM=
 github.com/fatih/set v0.2.1/go.mod h1:+RKtMCH+favT2+3YecHGxcc0b4KyVWA1QWWJUs4E0CI=
 github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
 github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
@@ -44,41 +42,27 @@ github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t
 github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
 github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
 github.com/juju/clock v0.0.0-20190205081909-9c5c9712527c/go.mod h1:nD0vlnrUjcjJhqN5WuCWZyzfd5AHZAC9/ajvbSx69xA=
-github.com/juju/errors v0.0.0-20190207033735-e65537c515d7 h1:dMIPRDg6gi7CUp0Kj2+HxqJ5kTr1iAdzsXYIrLCNSmU=
 github.com/juju/errors v0.0.0-20190207033735-e65537c515d7/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q=
-github.com/juju/loggo v0.0.0-20190212223446-d976af380377 h1:n6QjW3g5JNY3xPmIjFt6z1H6tFQA6BhwOC2bvTAm1YU=
 github.com/juju/loggo v0.0.0-20190212223446-d976af380377/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U=
 github.com/juju/retry v0.0.0-20180821225755-9058e192b216/go.mod h1:OohPQGsr4pnxwD5YljhQ+TZnuVRYpa5irjugL1Yuif4=
-github.com/juju/testing v0.0.0-20180920084828-472a3e8b2073 h1:WQM1NildKThwdP7qWrNAFGzp4ijNLw8RlgENkaI4MJs=
-github.com/juju/testing v0.0.0-20180920084828-472a3e8b2073 h1:WQM1NildKThwdP7qWrNAFGzp4ijNLw8RlgENkaI4MJs=
-github.com/juju/testing v0.0.0-20180920084828-472a3e8b2073/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA=
 github.com/juju/testing v0.0.0-20180920084828-472a3e8b2073/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA=
 github.com/juju/utils v0.0.0-20180820210520-bf9cc5bdd62d/go.mod h1:6/KLg8Wz/y2KVGWEpkK9vMNGkOnu4k/cqs8Z1fKjTOk=
 github.com/juju/version v0.0.0-20180108022336-b64dbd566305/go.mod h1:kE8gK5X0CImdr7qpSKl3xB2PmpySSmfj7zVbkZFs81U=
-github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 h1:uC1QfSlInpQF+M0ao65imhwqKnz3Q2z/d8PWZRMQvDM=
 github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k=
-github.com/k0kubun/pp v3.0.0+incompatible h1:I00lq/ALERE8g5dW2th1jnjtJ/J4vautUNyHR3IRj7c=
 github.com/k0kubun/pp v3.0.0+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg=
 github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
 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/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
-github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
-github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe h1:W/GaMY0y69G4cFlmsC6B9sbuo2fP8OFP1ABjt4kPz+w=
 github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
-github.com/mattn/go-colorable v0.1.1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg=
 github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
 github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
-github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc=
 github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
 github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
-github.com/name5566/leaf v0.0.0-20181103040206-1364c176dfbd h1:22rhYEzttbrnKjgYh5pifnDluXHHcJ3uSOi2l8Nw+9A=
-github.com/name5566/leaf v0.0.0-20181103040206-1364c176dfbd/go.mod h1:JrOIxq3vDxvtuEI7Kmm2yqkuBfuT9DMLFMnCyYHLaKM=
 github.com/name5566/leaf v0.0.0-20181103040206-1364c176dfbd/go.mod h1:JrOIxq3vDxvtuEI7Kmm2yqkuBfuT9DMLFMnCyYHLaKM=
 github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -112,8 +96,13 @@ github.com/ugorji/go/codec v0.0.0-20190320090025-2dc34c0b8780/go.mod h1:iT03XoTw
 github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
 go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
 go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
+go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU=
+go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
+go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=
 go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
 go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
+go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM=
+go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
 golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -129,9 +118,6 @@ golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5h
 golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8=
-golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8=
-golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -143,11 +129,7 @@ google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZi
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce h1:xcEWjVhvbDy+nHP67nPDDpbYrY+ILlfndk4bRioVHaU=
-gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce h1:xcEWjVhvbDy+nHP67nPDDpbYrY+ILlfndk4bRioVHaU=
-gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
 gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
 gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
 gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
diff --git a/jsonrpc/map.go b/jsonrpc/map.go
deleted file mode 100644
index 43c083330d082a6e75d3501663fc81bbac6b26ec..0000000000000000000000000000000000000000
--- a/jsonrpc/map.go
+++ /dev/null
@@ -1,285 +0,0 @@
-package jsonrpc
-
-import (
-	"bytes"
-	"context"
-	"io"
-	"io/ioutil"
-	"net"
-	"net/http"
-	"reflect"
-	"strings"
-	"sync"
-	"unicode"
-	"unicode/utf8"
-)
-
-import (
-	log "github.com/AlexStocks/log4go"
-	jerrors "github.com/juju/errors"
-)
-
-var (
-	// A value sent as a placeholder for the server's response value when the server
-	// receives an invalid request. It is never decoded by the client since the Response
-	// contains an error when it is used.
-	invalidRequest = struct{}{}
-
-	// Precompute the reflect type for error. Can't use error directly
-	// because Typeof takes an empty interface value. This is annoying.
-	typeOfError = reflect.TypeOf((*error)(nil)).Elem()
-)
-
-type serviceMethod struct {
-	method    reflect.Method // receiver method
-	ctxType   reflect.Type   // type of the request context
-	argsType  reflect.Type   // type of the request argument
-	replyType reflect.Type   // type of the response argument
-}
-
-func (m *serviceMethod) suiteContext(ctx context.Context) reflect.Value {
-	if contextv := reflect.ValueOf(ctx); contextv.IsValid() {
-		return contextv
-	}
-	return reflect.Zero(m.ctxType)
-}
-
-type svc struct {
-	name     string                    // name of service
-	rcvr     reflect.Value             // receiver of methods for the service
-	rcvrType reflect.Type              // type of the receiver
-	methods  map[string]*serviceMethod // registered methods, function name -> reflect.function
-}
-
-type serviceMap struct {
-	mutex      sync.Mutex      // protects the serviceMap
-	serviceMap map[string]*svc // service name -> service
-}
-
-func initServer() *serviceMap {
-	return &serviceMap{
-		serviceMap: make(map[string]*svc),
-	}
-}
-
-// isExported returns true of a string is an exported (upper case) name.
-func isExported(name string) bool {
-	rune, _ := utf8.DecodeRuneInString(name)
-	return unicode.IsUpper(rune)
-}
-
-// isExportedOrBuiltin returns true if a type is exported or a builtin.
-func isExportedOrBuiltin(t reflect.Type) bool {
-	for t.Kind() == reflect.Ptr {
-		t = t.Elem()
-	}
-	// PkgPath will be non-empty even for an exported type,
-	// so we need to check the type name as well.
-	return isExported(t.Name()) || t.PkgPath() == ""
-}
-
-func suiteMethod(method reflect.Method) *serviceMethod {
-	mtype := method.Type
-	mname := method.Name
-
-	// Method must be exported.
-	if method.PkgPath != "" {
-		return nil
-	}
-
-	var replyType, argType, ctxType reflect.Type
-	switch mtype.NumIn() {
-	case 3:
-		argType = mtype.In(1)
-		replyType = mtype.In(2)
-	case 4:
-		ctxType = mtype.In(1)
-		argType = mtype.In(2)
-		replyType = mtype.In(3)
-	default:
-		log.Error("method %s of mtype %v has wrong number of in parameters %d; needs exactly 3/4",
-			mname, mtype, mtype.NumIn())
-		return nil
-	}
-	// First arg need not be a pointer.
-	if !isExportedOrBuiltin(argType) {
-		log.Error("argument type of method %q is not exported %v", mname, argType)
-		return nil
-	}
-	// Second arg must be a pointer.
-	if replyType.Kind() != reflect.Ptr {
-		log.Error("reply type of method %q is not a pointer %v", mname, replyType)
-		return nil
-	}
-	// Reply type must be exported.
-	if !isExportedOrBuiltin(replyType) {
-		log.Error("reply type of method %s not exported{%v}", mname, replyType)
-		return nil
-	}
-	// Method needs one out.
-	if mtype.NumOut() != 1 {
-		log.Error("method %q has %d out parameters; needs exactly 1", mname, mtype.NumOut())
-		return nil
-	}
-	// The return type of the method must be error.
-	if returnType := mtype.Out(0); returnType != typeOfError {
-		log.Error("return type %s of method %q is not error", returnType, mname)
-		return nil
-	}
-
-	return &serviceMethod{method: method, argsType: argType, replyType: replyType, ctxType: ctxType}
-}
-
-func (server *serviceMap) register(rcvr Handler) (string, error) {
-	server.mutex.Lock()
-	defer server.mutex.Unlock()
-	if server.serviceMap == nil {
-		server.serviceMap = make(map[string]*svc)
-	}
-
-	s := new(svc)
-	s.rcvrType = reflect.TypeOf(rcvr)
-	s.rcvr = reflect.ValueOf(rcvr)
-	sname := reflect.Indirect(s.rcvr).Type().Name()
-	if sname == "" {
-		s := "no service name for type " + s.rcvrType.String()
-		log.Error(s)
-		return "", jerrors.New(s)
-	}
-	if !isExported(sname) {
-		s := "type " + sname + " is not exported"
-		log.Error(s)
-		return "", jerrors.New(s)
-	}
-
-	sname = rcvr.Service()
-	if _, dup := server.serviceMap[sname]; dup {
-		return "", jerrors.New("service already defined: " + sname)
-	}
-	s.name = sname
-	s.methods = make(map[string]*serviceMethod)
-
-	// Install the methods
-	methods := ""
-	num := s.rcvrType.NumMethod()
-	for m := 0; m < num; m++ {
-		method := s.rcvrType.Method(m)
-		if mt := suiteMethod(method); mt != nil {
-			s.methods[method.Name] = mt
-			methods += method.Name + ","
-		}
-	}
-
-	if len(s.methods) == 0 {
-		s := "type " + sname + " has no exported methods of suitable type"
-		log.Error(s)
-		return "", jerrors.New(s)
-	}
-	server.serviceMap[s.name] = s
-
-	return strings.TrimSuffix(methods, ","), nil
-}
-
-func (server *serviceMap) serveRequest(ctx context.Context,
-	header map[string]string, body []byte, conn net.Conn) error {
-
-	// read request header
-	codec := newServerCodec()
-	err := codec.ReadHeader(header, body)
-	if err != nil {
-		if err == io.EOF || err == io.ErrUnexpectedEOF {
-			return jerrors.Trace(err)
-		}
-
-		return jerrors.New("server cannot decode request: " + err.Error())
-	}
-	serviceName := header["Path"]
-	methodName := codec.req.Method
-	if len(serviceName) == 0 || len(methodName) == 0 {
-		codec.ReadBody(nil)
-		return jerrors.New("service/method request ill-formed: " + serviceName + "/" + methodName)
-	}
-
-	// get method
-	server.mutex.Lock()
-	svc := server.serviceMap[serviceName]
-	server.mutex.Unlock()
-	if svc == nil {
-		codec.ReadBody(nil)
-		return jerrors.New("cannot find svc " + serviceName)
-	}
-	mtype := svc.methods[methodName]
-	if mtype == nil {
-		codec.ReadBody(nil)
-		return jerrors.New("cannot find method " + methodName + " of svc " + serviceName)
-	}
-
-	// get args
-	var argv reflect.Value
-	argIsValue := false
-	if mtype.argsType.Kind() == reflect.Ptr {
-		argv = reflect.New(mtype.argsType.Elem())
-	} else {
-		argv = reflect.New(mtype.argsType)
-		argIsValue = true
-	}
-	// argv guaranteed to be a pointer now.
-	if err = codec.ReadBody(argv.Interface()); err != nil {
-		return jerrors.Trace(err)
-	}
-	if argIsValue {
-		argv = argv.Elem()
-	}
-
-	replyv := reflect.New(mtype.replyType.Elem())
-
-	//  call service.method(args)
-	var errMsg string
-	returnValues := mtype.method.Func.Call([]reflect.Value{
-		svc.rcvr,
-		mtype.suiteContext(ctx),
-		reflect.ValueOf(argv.Interface()),
-		reflect.ValueOf(replyv.Interface()),
-	})
-	// The return value for the method is an error.
-	if retErr := returnValues[0].Interface(); retErr != nil {
-		errMsg = retErr.(error).Error()
-	}
-
-	// write response
-	code := 200
-	rspReply := replyv.Interface()
-	if len(errMsg) != 0 {
-		code = 500
-		rspReply = invalidRequest
-	}
-	rspStream, err := codec.Write(errMsg, rspReply)
-	if err != nil {
-		return jerrors.Trace(err)
-	}
-	rsp := &http.Response{
-		StatusCode:    code,
-		ProtoMajor:    1,
-		ProtoMinor:    1,
-		Header:        make(http.Header),
-		ContentLength: int64(len(rspStream)),
-		Body:          ioutil.NopCloser(bytes.NewReader(rspStream)),
-	}
-	delete(header, "Content-Type")
-	delete(header, "Content-Length")
-	delete(header, "Timeout")
-	for k, v := range header {
-		rsp.Header.Set(k, v)
-	}
-
-	rspBuf := bytes.NewBuffer(make([]byte, DefaultHTTPRspBufferSize))
-	rspBuf.Reset()
-	if err = rsp.Write(rspBuf); err != nil {
-		log.Warn("rsp.Write(rsp:%#v) = error:%s", rsp, err)
-		return nil
-	}
-	if _, err = rspBuf.WriteTo(conn); err != nil {
-		log.Warn("rspBuf.WriteTo(conn:%#v) = error:%s", conn, err)
-	}
-	return nil
-}
diff --git a/jsonrpc/server.go b/jsonrpc/server.go
deleted file mode 100644
index b85fd12a972b852a64c005b4f8fe8b85dd773abc..0000000000000000000000000000000000000000
--- a/jsonrpc/server.go
+++ /dev/null
@@ -1,383 +0,0 @@
-package jsonrpc
-
-import (
-	"bufio"
-	"bytes"
-	"context"
-	"io/ioutil"
-	"net"
-	"net/http"
-	"runtime"
-	"runtime/debug"
-	"sync"
-	"time"
-)
-
-import (
-	"github.com/AlexStocks/goext/net"
-	log "github.com/AlexStocks/log4go"
-	"github.com/dubbo/go-for-apache-dubbo/registry"
-	jerrors "github.com/juju/errors"
-)
-
-import (
-	"github.com/dubbo/go-for-apache-dubbo/server"
-	"github.com/dubbo/go-for-apache-dubbo/plugins"
-)
-
-const (
-	DefaultMaxSleepTime      = 1 * time.Second // accept涓棿鏈€澶leep interval
-	DefaultHTTPRspBufferSize = 1024
-	PathPrefix               = byte('/')
-)
-
-// Handler interface represents a Service request handler. It's generated
-// by passing any type of public concrete object with methods into server.NewHandler.
-// Most will pass in a struct.
-//
-// Example:
-//
-//	type Hello struct {}
-//
-//	func (s *Hello) Method(context, request, response) error {
-//		return nil
-//	}
-//
-//  func (s *Hello) Service() string {
-//      return "com.youni.service"
-//  }
-//
-//  func (s *Hello) Version() string {
-//      return "1.0.0"
-//  }
-
-type Handler interface {
-	Service() string // Service Interface
-	Version() string
-}
-
-type Option func(*Options)
-
-type Options struct {
-	Registry        registry.Registry
-	ConfList        []server.ServerConfig
-	ServiceConfList []registry.ProviderServiceConfig
-	Timeout         time.Duration
-}
-
-func newOptions(opt ...Option) Options {
-	opts := Options{}
-	for _, o := range opt {
-		o(&opts)
-	}
-
-	if opts.Registry == nil {
-		panic("server.Options.Registry is nil")
-	}
-
-	return opts
-}
-
-// Registry used for discovery
-func Registry(r registry.Registry) Option {
-	return func(o *Options) {
-		o.Registry = r
-	}
-}
-
-func ConfList(confList []server.ServerConfig) Option {
-	return func(o *Options) {
-		o.ConfList = confList
-		for i := 0; i < len(o.ConfList); i++ {
-			if o.ConfList[i].IP == "" {
-				o.ConfList[i].IP, _ = gxnet.GetLocalIP()
-			}
-		}
-	}
-}
-
-func ServiceConfList(confList []registry.ProviderServiceConfig) Option {
-	return func(o *Options) {
-		o.ServiceConfList = confList
-	}
-}
-
-type Server struct {
-	rpc  []*serviceMap
-	done chan struct{}
-	once sync.Once
-
-	sync.RWMutex
-	opts     Options
-	handlers map[string]Handler
-	wg       sync.WaitGroup
-}
-
-func NewServer(opts ...Option) *Server {
-	var (
-		num int
-	)
-	options := newOptions(opts...)
-	Servers := make([]*serviceMap, len(options.ConfList))
-	num = len(options.ConfList)
-	for i := 0; i < num; i++ {
-		Servers[i] = initServer()
-	}
-	return &Server{
-		opts:     options,
-		rpc:      Servers,
-		handlers: make(map[string]Handler),
-		done:     make(chan struct{}),
-	}
-}
-
-func (s *Server) handlePkg(rpc *serviceMap, conn net.Conn) {
-	defer func() {
-		if r := recover(); r != nil {
-			log.Warn("connection{local:%v, remote:%v} panic error:%#v, debug stack:%s",
-				conn.LocalAddr(), conn.RemoteAddr(), r, string(debug.Stack()))
-		}
-
-		conn.Close()
-	}()
-
-	setReadTimeout := func(conn net.Conn, timeout time.Duration) {
-		t := time.Time{}
-		if timeout > time.Duration(0) {
-			t = time.Now().Add(timeout)
-		}
-
-		conn.SetDeadline(t)
-	}
-
-	sendErrorResp := func(header http.Header, body []byte) error {
-		rsp := &http.Response{
-			Header:        header,
-			StatusCode:    500,
-			ContentLength: int64(len(body)),
-			Body:          ioutil.NopCloser(bytes.NewReader(body)),
-		}
-		rsp.Header.Del("Content-Type")
-		rsp.Header.Del("Content-Length")
-		rsp.Header.Del("Timeout")
-
-		rspBuf := bytes.NewBuffer(make([]byte, DefaultHTTPRspBufferSize))
-		rspBuf.Reset()
-		err := rsp.Write(rspBuf)
-		if err != nil {
-			return jerrors.Trace(err)
-		}
-		_, err = rspBuf.WriteTo(conn)
-		return jerrors.Trace(err)
-	}
-
-	for {
-		bufReader := bufio.NewReader(conn)
-		r, err := http.ReadRequest(bufReader)
-		if err != nil {
-			return
-		}
-
-		reqBody, err := ioutil.ReadAll(r.Body)
-		if err != nil {
-			return
-		}
-		r.Body.Close()
-
-		reqHeader := make(map[string]string)
-		for k := range r.Header {
-			reqHeader[k] = r.Header.Get(k)
-		}
-		reqHeader["Path"] = r.URL.Path[1:] // to get service name
-		if r.URL.Path[0] != PathPrefix {
-			reqHeader["Path"] = r.URL.Path
-		}
-		reqHeader["HttpMethod"] = r.Method
-
-		httpTimeout := s.Options().Timeout
-		contentType := reqHeader["Content-Type"]
-		if contentType != "application/json" && contentType != "application/json-rpc" {
-			setReadTimeout(conn, httpTimeout)
-			r.Header.Set("Content-Type", "text/plain")
-			if errRsp := sendErrorResp(r.Header, []byte(jerrors.ErrorStack(err))); errRsp != nil {
-				log.Warn("sendErrorResp(header:%#v, error:%s) = error:%s",
-					r.Header, jerrors.ErrorStack(err), errRsp)
-			}
-			return
-		}
-
-		ctx := context.Background()
-		if len(reqHeader["Timeout"]) > 0 {
-			timeout, err := time.ParseDuration(reqHeader["Timeout"])
-			if err == nil {
-				httpTimeout = timeout
-				ctx, _ = context.WithTimeout(ctx, httpTimeout)
-			}
-			delete(reqHeader, "Timeout")
-		}
-		setReadTimeout(conn, httpTimeout)
-
-		if err := rpc.serveRequest(ctx, reqHeader, reqBody, conn); err != nil {
-			if errRsp := sendErrorResp(r.Header, []byte(jerrors.ErrorStack(err))); errRsp != nil {
-				log.Warn("sendErrorResp(header:%#v, error:%s) = error:%s",
-					r.Header, jerrors.ErrorStack(err), errRsp)
-			}
-
-			log.Info("Unexpected error serving request, closing socket: %v", err)
-			return
-		}
-	}
-}
-
-func (s *Server) Options() Options {
-	s.RLock()
-	defer s.RUnlock()
-	return s.opts
-}
-
-func (s *Server) Handle(h Handler) error {
-	var (
-		err error
-	)
-
-	opts := s.Options()
-	serviceConf := plugins.DefaultProviderServiceConfig()()
-
-	serviceConf.SetService(h.Service())
-	serviceConf.SetVersion(h.Version())
-
-	flag := 0
-	serviceNum := len(opts.ServiceConfList)
-	ServerNum := len(opts.ConfList)
-	for i := 0; i < serviceNum; i++ {
-		if opts.ServiceConfList[i].Service() == serviceConf.Service() &&
-			opts.ServiceConfList[i].Version() == serviceConf.Version() {
-
-			serviceConf.SetProtocol(opts.ServiceConfList[i].Protocol())
-			serviceConf.SetGroup(opts.ServiceConfList[i].Group())
-			// serviceConf.Version = opts.ServiceConfList[i].Version
-			var methods, path string
-			for j := 0; j < ServerNum; j++ {
-				if opts.ConfList[j].Protocol == serviceConf.Protocol() {
-					s.Lock()
-					methods, err = s.rpc[j].register(h)
-					s.Unlock()
-					if err != nil {
-						return err
-					}
-					serviceConf.SetMethods(methods)
-
-					path = opts.ConfList[j].Address()
-					serviceConf.SetPath(path)
-					err = opts.Registry.Register(serviceConf)
-					if err != nil {
-						return err
-					}
-					flag = 1
-				}
-			}
-		}
-	}
-
-	if flag == 0 {
-		return jerrors.Errorf("fail to register Handler{service:%s, version:%s}",
-			serviceConf.Service(), serviceConf.Version())
-	}
-
-	s.Lock()
-	s.handlers[h.Service()] = h
-	s.Unlock()
-
-	return nil
-}
-
-func accept(listener net.Listener, fn func(net.Conn)) error {
-	var (
-		err      error
-		c        net.Conn
-		ok       bool
-		ne       net.Error
-		tmpDelay time.Duration
-	)
-
-	for {
-		c, err = listener.Accept()
-		if err != nil {
-			if ne, ok = err.(net.Error); ok && ne.Temporary() {
-				if tmpDelay != 0 {
-					tmpDelay <<= 1
-				} else {
-					tmpDelay = 5 * time.Millisecond
-				}
-				if tmpDelay > DefaultMaxSleepTime {
-					tmpDelay = DefaultMaxSleepTime
-				}
-				log.Info("http: Accept error: %v; retrying in %v\n", err, tmpDelay)
-				time.Sleep(tmpDelay)
-				continue
-			}
-			return jerrors.Trace(err)
-		}
-
-		go func() {
-			defer func() {
-				if r := recover(); r != nil {
-					const size = 64 << 10
-					buf := make([]byte, size)
-					buf = buf[:runtime.Stack(buf, false)]
-					log.Error("http: panic serving %v: %v\n%s", c.RemoteAddr(), r, buf)
-					c.Close()
-				}
-			}()
-
-			fn(c)
-		}()
-	}
-}
-
-func (s *Server) Start() error {
-	config := s.Options()
-
-	ServerNum := len(config.ConfList)
-	for i := 0; i < ServerNum; i++ {
-		listener, err := net.Listen("tcp", config.ConfList[i].Address())
-		if err != nil {
-			return err
-		}
-		log.Info("rpc server start to listen on %s", listener.Addr())
-
-		s.Lock()
-		rpc := s.rpc[i]
-		s.Unlock()
-
-		s.wg.Add(1)
-		go func(servo *serviceMap) {
-			accept(listener, func(conn net.Conn) { s.handlePkg(rpc, conn) })
-			s.wg.Done()
-		}(rpc)
-
-		s.wg.Add(1)
-		go func(servo *serviceMap) { // Server done goroutine
-			var err error
-			<-s.done               // step1: block to wait for done channel(wait Server.Stop step2)
-			err = listener.Close() // step2: and then close listener
-			if err != nil {
-				log.Warn("listener{addr:%s}.Close() = error{%#v}", listener.Addr(), err)
-			}
-			s.wg.Done()
-		}(rpc)
-	}
-
-	return nil
-}
-
-func (s *Server) Stop() {
-	s.once.Do(func() {
-		close(s.done)
-		s.wg.Wait()
-		if s.opts.Registry != nil {
-			s.opts.Registry.Close()
-			s.opts.Registry = nil
-		}
-	})
-}
diff --git a/plugins/plugins.go b/plugins/plugins.go
deleted file mode 100644
index 1584996e0bc9a74c7c3f17fbf720c303a24c1eb7..0000000000000000000000000000000000000000
--- a/plugins/plugins.go
+++ /dev/null
@@ -1,53 +0,0 @@
-package plugins
-
-import (
-	"github.com/dubbo/go-for-apache-dubbo/client/selector"
-	"github.com/dubbo/go-for-apache-dubbo/registry"
-)
-
-var PluggableRegistries = map[string]func(...registry.RegistryOption) (registry.Registry, error){}
-
-var PluggableLoadbalance = map[string]func() selector.Selector{
-	"round_robin": selector.NewRoundRobinSelector,
-	"random":      selector.NewRandomSelector,
-}
-
-// service configuration plugins , related to SeviceConfig for consumer paramters / ProviderSeviceConfig for provider parameters /
-
-// TODO:ServiceEven & ServiceURL subscribed by consumer from provider's listener shoud abstract to interface
-var PluggableServiceConfig = map[string]func() registry.ServiceConfig{
-	"default": registry.NewDefaultServiceConfig,
-}
-var PluggableProviderServiceConfig = map[string]func() registry.ProviderServiceConfig{
-	"default": registry.NewDefaultProviderServiceConfig,
-}
-
-var PluggableServiceURL = map[string]func(string) (registry.ServiceURL, error){
-	"default": registry.NewDefaultServiceURL,
-}
-
-var defaultServiceConfig = registry.NewDefaultServiceConfig
-var defaultProviderServiceConfig = registry.NewDefaultProviderServiceConfig
-
-var defaultServiceURL = registry.NewDefaultServiceURL
-
-func SetDefaultServiceConfig(s string) {
-	defaultServiceConfig = PluggableServiceConfig[s]
-}
-func DefaultServiceConfig() func() registry.ServiceConfig {
-	return defaultServiceConfig
-}
-
-func SetDefaultProviderServiceConfig(s string) {
-	defaultProviderServiceConfig = PluggableProviderServiceConfig[s]
-}
-func DefaultProviderServiceConfig() func() registry.ProviderServiceConfig {
-	return defaultProviderServiceConfig
-}
-
-func SetDefaultServiceURL(s string) {
-	defaultServiceURL = PluggableServiceURL[s]
-}
-func DefaultServiceURL() func(string) (registry.ServiceURL, error) {
-	return defaultServiceURL
-}
diff --git a/dubbo/client.go b/protocol/dubbo/client.go
similarity index 50%
rename from dubbo/client.go
rename to protocol/dubbo/client.go
index bfac058316711ca5b8bbf9708104fbb00da2b991..ff094eb5aaa1af8d2ffa6a6c210574303e9052f2 100644
--- a/dubbo/client.go
+++ b/protocol/dubbo/client.go
@@ -1,34 +1,88 @@
+// Copyright 2016-2019 Yincheng Fang, Alex Stocks
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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/AlexStocks/getty"
-	"github.com/AlexStocks/goext/sync/atomic"
+	"github.com/dubbogo/getty"
 	"github.com/dubbogo/hessian2"
-	jerrors "github.com/juju/errors"
+	perrors "github.com/pkg/errors"
+	"go.uber.org/atomic"
+	"gopkg.in/yaml.v2"
 )
 
 import (
-	"github.com/dubbo/go-for-apache-dubbo/public"
-	"github.com/dubbo/go-for-apache-dubbo/registry"
+	"github.com/dubbo/go-for-apache-dubbo/common"
+	"github.com/dubbo/go-for-apache-dubbo/common/constant"
+	"github.com/dubbo/go-for-apache-dubbo/common/logger"
+	"github.com/dubbo/go-for-apache-dubbo/config"
 )
 
 var (
-	errInvalidCodecType  = jerrors.New("illegal CodecType")
-	errInvalidAddress    = jerrors.New("remote address invalid or empty")
-	errSessionNotExist   = jerrors.New("session not exist")
-	errClientClosed      = jerrors.New("client closed")
-	errClientReadTimeout = jerrors.New("client read timeout")
+	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
 )
 
 func init() {
-	rand.Seed(time.Now().UnixNano())
+
+	// load clientconfig from consumer_config
+	protocolConf := config.GetConsumerConfig().ProtocolConf
+	if protocolConf == nil {
+		logger.Warnf("protocol_conf is nil")
+		return
+	}
+	dubboConf := protocolConf.(map[interface{}]interface{})[DUBBO]
+	if protocolConf == nil {
+		logger.Warnf("dubboConf is nil")
+		return
+	}
+
+	dubboConfByte, err := yaml.Marshal(dubboConf)
+	if err != nil {
+		panic(err)
+	}
+	conf := &ClientConfig{}
+	err = yaml.Unmarshal(dubboConfByte, conf)
+	if err != nil {
+		panic(err)
+	}
+
+	if err := conf.CheckValidity(); err != nil {
+		logger.Warnf("[CheckValidity] error: %v", err)
+		return
+	}
+
+	clientConf = conf
+}
+
+func SetClientConf(c ClientConfig) {
+	clientConf = &c
+}
+
+func GetClientConf() ClientConfig {
+	return *clientConf
 }
 
 type CallOptions struct {
@@ -43,32 +97,38 @@ type CallOptions struct {
 
 type CallOption func(*CallOptions)
 
-func WithCallRequestTimeout(d time.Duration) CallOption {
-	return func(o *CallOptions) {
-		o.RequestTimeout = d
-	}
-}
-
-func WithCallResponseTimeout(d time.Duration) CallOption {
-	return func(o *CallOptions) {
-		o.ResponseTimeout = d
-	}
-}
-
-func WithCallSerialID(s SerialID) CallOption {
-	return func(o *CallOptions) {
-		o.SerialID = s
-	}
-}
-
-func WithCallMeta(k, v interface{}) CallOption {
-	return func(o *CallOptions) {
-		if o.Meta == nil {
-			o.Meta = make(map[interface{}]interface{})
-		}
-		o.Meta[k] = v
-	}
-}
+//func WithCallRequestTimeout(d time.Duration) CallOption {
+//	return func(o *CallOptions) {
+//		o.RequestTimeout = d
+//	}
+//}
+//
+//func WithCallResponseTimeout(d time.Duration) CallOption {
+//	return func(o *CallOptions) {
+//		o.ResponseTimeout = d
+//	}
+//}
+//
+//func WithCallSerialID(s SerialID) CallOption {
+//	return func(o *CallOptions) {
+//		o.SerialID = s
+//	}
+//}
+//
+//func WithCallMeta_All(callMeta map[interface{}]interface{}) CallOption {
+//	return func(o *CallOptions) {
+//		o.Meta = callMeta
+//	}
+//}
+
+//func WithCallMeta(k, v interface{}) CallOption {
+//	return func(o *CallOptions) {
+//		if o.Meta == nil {
+//			o.Meta = make(map[interface{}]interface{})
+//		}
+//		o.Meta[k] = v
+//	}
+//}
 
 type CallResponse struct {
 	Opts      CallOptions
@@ -83,39 +143,36 @@ type AsyncCallback func(response CallResponse)
 type Client struct {
 	conf     ClientConfig
 	pool     *gettyRPCClientPool
-	sequence gxatomic.Uint64
+	sequence atomic.Uint64
 
 	pendingLock      sync.RWMutex
 	pendingResponses map[SequenceType]*PendingResponse
 }
 
-func NewClient(conf *ClientConfig) (*Client, error) {
-	if err := conf.CheckValidity(); err != nil {
-		return nil, jerrors.Trace(err)
-	}
+func NewClient() *Client {
 
 	c := &Client{
 		pendingResponses: make(map[SequenceType]*PendingResponse),
-		conf:             *conf,
+		conf:             *clientConf,
 	}
-	c.pool = newGettyRPCClientConnPool(c, conf.PoolSize, time.Duration(int(time.Second)*conf.PoolTTL))
+	c.pool = newGettyRPCClientConnPool(c, clientConf.PoolSize, time.Duration(int(time.Second)*clientConf.PoolTTL))
 
-	return c, nil
+	return c
 }
 
 // call one way
-func (c *Client) CallOneway(addr string, svcUrl registry.ServiceURL, method string, args interface{}, opts ...CallOption) error {
+func (c *Client) CallOneway(addr string, svcUrl common.URL, method string, args interface{}, opts ...CallOption) error {
 	var copts CallOptions
 
 	for _, o := range opts {
 		o(&copts)
 	}
 
-	return jerrors.Trace(c.call(CT_OneWay, addr, svcUrl, method, args, nil, nil, copts))
+	return perrors.WithStack(c.call(CT_OneWay, addr, svcUrl, method, args, nil, nil, copts))
 }
 
 // if @reply is nil, the transport layer will get the response without notify the invoker.
-func (c *Client) Call(addr string, svcUrl registry.ServiceURL, method string, args, reply interface{}, opts ...CallOption) error {
+func (c *Client) Call(addr string, svcUrl common.URL, method string, args, reply interface{}, opts ...CallOption) error {
 	var copts CallOptions
 
 	for _, o := range opts {
@@ -127,10 +184,10 @@ func (c *Client) Call(addr string, svcUrl registry.ServiceURL, method string, ar
 		ct = CT_OneWay
 	}
 
-	return jerrors.Trace(c.call(ct, addr, svcUrl, method, args, reply, nil, copts))
+	return perrors.WithStack(c.call(ct, addr, svcUrl, method, args, reply, nil, copts))
 }
 
-func (c *Client) AsyncCall(addr string, svcUrl registry.ServiceURL, method string, args interface{},
+func (c *Client) AsyncCall(addr string, svcUrl common.URL, method string, args interface{},
 	callback AsyncCallback, reply interface{}, opts ...CallOption) error {
 
 	var copts CallOptions
@@ -138,10 +195,10 @@ func (c *Client) AsyncCall(addr string, svcUrl registry.ServiceURL, method strin
 		o(&copts)
 	}
 
-	return jerrors.Trace(c.call(CT_TwoWay, addr, svcUrl, method, args, reply, callback, copts))
+	return perrors.WithStack(c.call(CT_TwoWay, addr, svcUrl, method, args, reply, callback, copts))
 }
 
-func (c *Client) call(ct CallType, addr string, svcUrl registry.ServiceURL, method string,
+func (c *Client) call(ct CallType, addr string, svcUrl common.URL, method string,
 	args, reply interface{}, callback AsyncCallback, opts CallOptions) error {
 
 	if opts.RequestTimeout == 0 {
@@ -152,9 +209,10 @@ func (c *Client) call(ct CallType, addr string, svcUrl registry.ServiceURL, meth
 	}
 
 	p := &DubboPackage{}
-	p.Service.Path = strings.TrimPrefix(svcUrl.Path(), "/")
-	p.Service.Target = strings.TrimPrefix(svcUrl.Path(), "/")
-	p.Service.Version = svcUrl.Version()
+	p.Service.Path = strings.TrimPrefix(svcUrl.Path, "/")
+	p.Service.Target = svcUrl.GetParam(constant.INTERFACE_KEY, "")
+	p.Service.Interface = svcUrl.GetParam(constant.INTERFACE_KEY, "")
+	p.Service.Version = svcUrl.GetParam(constant.VERSION_KEY, constant.DEFAULT_VERSION)
 	p.Service.Method = method
 	p.Service.Timeout = opts.RequestTimeout
 	if opts.SerialID == 0 {
@@ -166,10 +224,13 @@ func (c *Client) call(ct CallType, addr string, svcUrl registry.ServiceURL, meth
 
 	var rsp *PendingResponse
 	if ct != CT_OneWay {
+		p.Header.Type = hessian.PackageRequest_TwoWay
 		rsp = NewPendingResponse()
 		rsp.reply = reply
 		rsp.callback = callback
 		rsp.opts = opts
+	} else {
+		p.Header.Type = hessian.PackageRequest
 	}
 
 	var (
@@ -179,12 +240,13 @@ func (c *Client) call(ct CallType, addr string, svcUrl registry.ServiceURL, meth
 	)
 	conn, session, err = c.selectSession(addr)
 	if err != nil || session == nil {
+		logger.Warnf("%s, %v", errSessionNotExist.Error(), err)
 		return errSessionNotExist
 	}
 	defer c.pool.release(conn, err)
 
 	if err = c.transfer(session, p, rsp, opts); err != nil {
-		return jerrors.Trace(err)
+		return perrors.WithStack(err)
 	}
 
 	if ct == CT_OneWay || callback != nil {
@@ -199,7 +261,7 @@ func (c *Client) call(ct CallType, addr string, svcUrl registry.ServiceURL, meth
 		err = rsp.err
 	}
 
-	return jerrors.Trace(err)
+	return perrors.WithStack(err)
 }
 
 func (c *Client) Close() {
@@ -210,9 +272,9 @@ func (c *Client) Close() {
 }
 
 func (c *Client) selectSession(addr string) (*gettyRPCClient, getty.Session, error) {
-	rpcClient, err := c.pool.getGettyRpcClient(public.CODECTYPE_DUBBO.String(), addr)
+	rpcClient, err := c.pool.getGettyRpcClient(DUBBO, addr)
 	if err != nil {
-		return nil, nil, jerrors.Trace(err)
+		return nil, nil, perrors.WithStack(err)
 	}
 	return rpcClient, rpcClient.selectSession(), nil
 }
@@ -234,10 +296,8 @@ func (c *Client) transfer(session getty.Session, pkg *DubboPackage,
 	if pkg == nil {
 		pkg = &DubboPackage{}
 		pkg.Body = []interface{}{}
-		pkg.Header.Type = hessian.Heartbeat
+		pkg.Header.Type = hessian.PackageHeartbeat
 		pkg.Header.SerialID = byte(S_Dubbo)
-	} else {
-		pkg.Header.Type = hessian.Request
 	}
 	pkg.Header.ID = int64(sequence)
 
@@ -256,7 +316,7 @@ func (c *Client) transfer(session getty.Session, pkg *DubboPackage,
 		rsp.readStart = time.Now()
 	}
 
-	return jerrors.Trace(err)
+	return perrors.WithStack(err)
 }
 
 func (c *Client) addPendingResponse(pr *PendingResponse) {
diff --git a/protocol/dubbo/client_test.go b/protocol/dubbo/client_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..216d2e10dd2fafb5decb08856ea2258b661004c7
--- /dev/null
+++ b/protocol/dubbo/client_test.go
@@ -0,0 +1,238 @@
+// Copyright 2016-2019 Yincheng Fang
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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"
+	"github.com/dubbogo/hessian2"
+	"sync"
+	"testing"
+	"time"
+)
+
+import (
+	perrors "github.com/pkg/errors"
+	"github.com/stretchr/testify/assert"
+)
+
+import (
+	"github.com/dubbo/go-for-apache-dubbo/common"
+	"github.com/dubbo/go-for-apache-dubbo/protocol"
+)
+
+type (
+	User struct {
+		Id   string `json:"id"`
+		Name string `json:"name"`
+	}
+
+	UserProvider struct {
+		user map[string]User
+	}
+)
+
+func TestClient_CallOneway(t *testing.T) {
+	proto, url := InitTest(t)
+
+	c := &Client{
+		pendingResponses: make(map[SequenceType]*PendingResponse),
+		conf:             *clientConf,
+	}
+	c.pool = newGettyRPCClientConnPool(c, clientConf.PoolSize, time.Duration(int(time.Second)*clientConf.PoolTTL))
+
+	//user := &User{}
+	err := c.CallOneway("127.0.0.1:20000", url, "GetUser", []interface{}{"1", "username"})
+	assert.NoError(t, err)
+
+	// destroy
+	proto.Destroy()
+}
+
+func TestClient_Call(t *testing.T) {
+	proto, url := InitTest(t)
+
+	c := &Client{
+		pendingResponses: make(map[SequenceType]*PendingResponse),
+		conf:             *clientConf,
+	}
+	c.pool = newGettyRPCClientConnPool(c, clientConf.PoolSize, time.Duration(int(time.Second)*clientConf.PoolTTL))
+
+	user := &User{}
+	err := c.Call("127.0.0.1:20000", url, "GetUser", []interface{}{"1", "username"}, user)
+	assert.NoError(t, err)
+	assert.Equal(t, User{Id: "1", Name: "username"}, *user)
+
+	user = &User{}
+	err = c.Call("127.0.0.1:20000", url, "GetUser0", []interface{}{"1", "username"}, user)
+	assert.NoError(t, err)
+	assert.Equal(t, User{Id: "1", Name: "username"}, *user)
+
+	user = &User{}
+	err = c.Call("127.0.0.1:20000", url, "GetUser1", []interface{}{"1", "username"}, user)
+	assert.EqualError(t, err, "java exception:error")
+
+	user2 := []interface{}{}
+	err = c.Call("127.0.0.1:20000", url, "GetUser2", []interface{}{"1", "username"}, &user2)
+	assert.NoError(t, err)
+	assert.Equal(t, &User{Id: "1", Name: "username"}, user2[0])
+
+	user2 = []interface{}{}
+	err = c.Call("127.0.0.1:20000", url, "GetUser3", []interface{}{[]interface{}{"1", "username"}}, &user2)
+	assert.NoError(t, err)
+	assert.Equal(t, &User{Id: "1", Name: "username"}, user2[0])
+
+	user3 := map[interface{}]interface{}{}
+	err = c.Call("127.0.0.1:20000", url, "GetUser4", []interface{}{map[interface{}]interface{}{"id": "1", "name": "username"}}, &user3)
+	assert.NoError(t, err)
+	assert.NotNil(t, user3)
+	assert.Equal(t, &User{Id: "1", Name: "username"}, user3["key"])
+
+	// destroy
+	proto.Destroy()
+}
+
+func TestClient_AsyncCall(t *testing.T) {
+	proto, url := InitTest(t)
+
+	c := &Client{
+		pendingResponses: make(map[SequenceType]*PendingResponse),
+		conf:             *clientConf,
+	}
+	c.pool = newGettyRPCClientConnPool(c, clientConf.PoolSize, time.Duration(int(time.Second)*clientConf.PoolTTL))
+
+	user := &User{}
+	lock := sync.Mutex{}
+	lock.Lock()
+	err := c.AsyncCall("127.0.0.1:20000", url, "GetUser", []interface{}{"1", "username"}, func(response CallResponse) {
+		assert.Equal(t, User{Id: "1", Name: "username"}, *response.Reply.(*User))
+		lock.Unlock()
+	}, user)
+	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("dubbo", &UserProvider{})
+	assert.NoError(t, err)
+	assert.Equal(t, "GetUser,GetUser0,GetUser1,GetUser2,GetUser3,GetUser4", methods)
+
+	// config
+	SetClientConf(ClientConfig{
+		ConnectionNum:   2,
+		HeartbeatPeriod: "5s",
+		SessionTimeout:  "20s",
+		FailFastTimeout: "5s",
+		PoolTTL:         600,
+		PoolSize:        64,
+		GettySessionParam: GettySessionParam{
+			CompressEncoding: false,
+			TcpNoDelay:       true,
+			TcpKeepAlive:     true,
+			KeepAlivePeriod:  "120s",
+			TcpRBufSize:      262144,
+			TcpWBufSize:      65536,
+			PkgRQSize:        1024,
+			PkgWQSize:        512,
+			TcpReadTimeout:   "1s",
+			TcpWriteTimeout:  "5s",
+			WaitTimeout:      "1s",
+			MaxMsgLen:        1024,
+			SessionName:      "client",
+		},
+	})
+	assert.NoError(t, clientConf.CheckValidity())
+	SetServerConfig(ServerConfig{
+		SessionNumber:   700,
+		SessionTimeout:  "20s",
+		FailFastTimeout: "5s",
+		GettySessionParam: GettySessionParam{
+			CompressEncoding: false,
+			TcpNoDelay:       true,
+			TcpKeepAlive:     true,
+			KeepAlivePeriod:  "120s",
+			TcpRBufSize:      262144,
+			TcpWBufSize:      65536,
+			PkgRQSize:        1024,
+			PkgWQSize:        512,
+			TcpReadTimeout:   "1s",
+			TcpWriteTimeout:  "5s",
+			WaitTimeout:      "1s",
+			MaxMsgLen:        1024,
+			SessionName:      "server",
+		}})
+	assert.NoError(t, srvConf.CheckValidity())
+
+	// Export
+	proto := GetProtocol()
+	url, err := common.NewURL(context.Background(), "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&"+
+		"application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&"+
+		"environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&"+
+		"module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&"+
+		"side=provider&timeout=3000&timestamp=1556509797245")
+	assert.NoError(t, err)
+	proto.Export(protocol.NewBaseInvoker(url))
+
+	time.Sleep(time.Second * 2)
+
+	return proto, url
+}
+
+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, name string) (User, error) {
+	return User{Id: id, Name: name}, nil
+}
+
+func (u *UserProvider) GetUser1(ctx context.Context, req []interface{}, rsp *User) error {
+	return perrors.New("error")
+}
+
+func (u *UserProvider) GetUser2(ctx context.Context, req []interface{}, rsp *[]interface{}) error {
+	*rsp = append(*rsp, User{Id: req[0].(string), Name: req[1].(string)})
+	return nil
+}
+
+func (u *UserProvider) GetUser3(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) GetUser4(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) Service() string {
+	return "com.ikurento.user.UserProvider"
+}
+
+func (u *UserProvider) Version() string {
+	return ""
+}
+
+func (u User) JavaClassName() string {
+	return "com.ikurento.user.User"
+}
diff --git a/dubbo/codec.go b/protocol/dubbo/codec.go
similarity index 54%
rename from dubbo/codec.go
rename to protocol/dubbo/codec.go
index d1a96940a4606377f6e42a122bbb8f91c789c556..ba4d01ab0fbe49745cfd23646596cebaa2ae619e 100644
--- a/dubbo/codec.go
+++ b/protocol/dubbo/codec.go
@@ -1,3 +1,17 @@
+// Copyright 2016-2019 Yincheng Fang, Alex Stocks
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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 (
@@ -9,7 +23,7 @@ import (
 
 import (
 	"github.com/dubbogo/hessian2"
-	jerrors "github.com/juju/errors"
+	perrors "github.com/pkg/errors"
 )
 
 // serial ID
@@ -42,7 +56,7 @@ type DubboPackage struct {
 }
 
 func (p DubboPackage) String() string {
-	return fmt.Sprintf("DubboPackage: Header-%v, Service-%v, Body-%v", p.Header, p.Service, p.Body)
+	return fmt.Sprintf("DubboPackage: Header-%v, Path-%v, Body-%v", p.Header, p.Service, p.Body)
 }
 
 func (p *DubboPackage) Marshal() (*bytes.Buffer, error) {
@@ -50,28 +64,41 @@ func (p *DubboPackage) Marshal() (*bytes.Buffer, error) {
 
 	pkg, err := codec.Write(p.Service, p.Header, p.Body)
 	if err != nil {
-		return nil, jerrors.Trace(err)
+		return nil, perrors.WithStack(err)
 	}
 
 	return bytes.NewBuffer(pkg), nil
 }
 
-func (p *DubboPackage) Unmarshal(buf *bytes.Buffer) error {
+func (p *DubboPackage) Unmarshal(buf *bytes.Buffer, opts ...interface{}) error {
 	codec := hessian.NewHessianCodec(bufio.NewReader(buf))
 
 	// read header
 	err := codec.ReadHeader(&p.Header)
 	if err != nil {
-		return jerrors.Trace(err)
+		return perrors.WithStack(err)
+	}
+
+	if len(opts) != 0 { // for client
+		if client, ok := opts[0].(*Client); ok {
+
+			r := client.pendingResponses[SequenceType(p.Header.ID)]
+			if r == nil {
+				return perrors.Errorf("pendingResponses[%v] = nil", p.Header.ID)
+			}
+			p.Body = client.pendingResponses[SequenceType(p.Header.ID)].reply
+		} else {
+			return perrors.Errorf("opts[0] is not *Client")
+		}
 	}
 
-	if p.Header.Type&hessian.Heartbeat != 0x00 {
+	if p.Header.Type&hessian.PackageHeartbeat != 0x00 {
 		return nil
 	}
 
 	// read body
 	err = codec.ReadBody(p.Body)
-	return jerrors.Trace(err)
+	return perrors.WithStack(err)
 }
 
 ////////////////////////////////////////////
diff --git a/protocol/dubbo/codec_test.go b/protocol/dubbo/codec_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..7ae227379afb79056b70564d1f3204d0606b1625
--- /dev/null
+++ b/protocol/dubbo/codec_test.go
@@ -0,0 +1,71 @@
+// Copyright 2016-2019 Yincheng Fang
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package dubbo
+
+import (
+	"testing"
+	"time"
+)
+
+import (
+	"github.com/dubbogo/hessian2"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestDubboPackage_MarshalAndUnmarshal(t *testing.T) {
+	pkg := &DubboPackage{}
+	pkg.Body = []interface{}{"a"}
+	pkg.Header.Type = hessian.PackageHeartbeat
+	pkg.Header.SerialID = byte(S_Dubbo)
+	pkg.Header.ID = 10086
+
+	// heartbeat
+	data, err := pkg.Marshal()
+	assert.NoError(t, err)
+
+	pkgres := &DubboPackage{}
+	pkgres.Body = []interface{}{}
+	err = pkgres.Unmarshal(data)
+	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, int64(10086), pkgres.Header.ID)
+	assert.Equal(t, 0, len(pkgres.Body.([]interface{})))
+
+	// request
+	pkg.Header.Type = hessian.PackageRequest
+	pkg.Service.Interface = "Service"
+	pkg.Service.Target = "Service"
+	pkg.Service.Version = "2.6"
+	pkg.Service.Method = "Method"
+	pkg.Service.Timeout = time.Second
+	data, err = pkg.Marshal()
+	assert.NoError(t, err)
+
+	pkgres = &DubboPackage{}
+	pkgres.Body = make([]interface{}, 7)
+	err = pkgres.Unmarshal(data)
+	assert.NoError(t, err)
+	assert.Equal(t, hessian.PackageRequest, pkgres.Header.Type)
+	assert.Equal(t, byte(S_Dubbo), pkgres.Header.SerialID)
+	assert.Equal(t, int64(10086), pkgres.Header.ID)
+	assert.Equal(t, "2.5.4", pkgres.Body.([]interface{})[0])
+	assert.Equal(t, "Service", 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[interface{}]interface{}{"interface": "Service", "path": "", "timeout": "1000"}, pkgres.Body.([]interface{})[6])
+}
diff --git a/dubbo/config.go b/protocol/dubbo/config.go
similarity index 72%
rename from dubbo/config.go
rename to protocol/dubbo/config.go
index 71769541e1d4b2937550d9b93087509b2536c516..fa47118db82f5418d4d5f6703e212d82efebe83a 100644
--- a/dubbo/config.go
+++ b/protocol/dubbo/config.go
@@ -1,3 +1,17 @@
+// Copyright 2016-2019 Alex Stocks
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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 (
@@ -5,8 +19,7 @@ import (
 )
 
 import (
-	"github.com/dubbo/go-for-apache-dubbo/server"
-	jerrors "github.com/juju/errors"
+	perrors "github.com/pkg/errors"
 )
 
 type (
@@ -32,7 +45,6 @@ type (
 
 	// Config holds supported types by the multiconfig package
 	ServerConfig struct {
-		server.ServerConfig
 		// local address
 		//AppName     string   `default:"rpc-server" yaml:"app_name" json:"app_name,omitempty"`
 		//Host        string   `default:"127.0.0.1" yaml:"host" json:"host,omitempty"`
@@ -59,6 +71,8 @@ type (
 		//Host        string `default:"127.0.0.1" yaml:"host" json:"host,omitempty"`
 		//ProfilePort int    `default:"10086" yaml:"profile_port" json:"profile_port,omitempty"`
 
+		ReconnectInterval int `default:"0" yaml:"reconnect_interval" json:"reconnect_interval,omitempty"`
+
 		// session pool
 		ConnectionNum int `default:"16" yaml:"connection_number" json:"connection_number,omitempty"`
 
@@ -87,19 +101,19 @@ func (c *GettySessionParam) CheckValidity() error {
 	var err error
 
 	if c.keepAlivePeriod, err = time.ParseDuration(c.KeepAlivePeriod); err != nil {
-		return jerrors.Annotatef(err, "time.ParseDuration(KeepAlivePeriod{%#v})", c.KeepAlivePeriod)
+		return perrors.WithMessagef(err, "time.ParseDuration(KeepAlivePeriod{%#v})", c.KeepAlivePeriod)
 	}
 
 	if c.tcpReadTimeout, err = time.ParseDuration(c.TcpReadTimeout); err != nil {
-		return jerrors.Annotatef(err, "time.ParseDuration(TcpReadTimeout{%#v})", c.TcpReadTimeout)
+		return perrors.WithMessagef(err, "time.ParseDuration(TcpReadTimeout{%#v})", c.TcpReadTimeout)
 	}
 
 	if c.tcpWriteTimeout, err = time.ParseDuration(c.TcpWriteTimeout); err != nil {
-		return jerrors.Annotatef(err, "time.ParseDuration(TcpWriteTimeout{%#v})", c.TcpWriteTimeout)
+		return perrors.WithMessagef(err, "time.ParseDuration(TcpWriteTimeout{%#v})", c.TcpWriteTimeout)
 	}
 
 	if c.waitTimeout, err = time.ParseDuration(c.WaitTimeout); err != nil {
-		return jerrors.Annotatef(err, "time.ParseDuration(WaitTimeout{%#v})", c.WaitTimeout)
+		return perrors.WithMessagef(err, "time.ParseDuration(WaitTimeout{%#v})", c.WaitTimeout)
 	}
 
 	return nil
@@ -109,30 +123,30 @@ func (c *ClientConfig) CheckValidity() error {
 	var err error
 
 	if c.heartbeatPeriod, err = time.ParseDuration(c.HeartbeatPeriod); err != nil {
-		return jerrors.Annotatef(err, "time.ParseDuration(HeartbeatPeroid{%#v})", c.HeartbeatPeriod)
+		return perrors.WithMessagef(err, "time.ParseDuration(HeartbeatPeroid{%#v})", c.HeartbeatPeriod)
 	}
 
 	if c.sessionTimeout, err = time.ParseDuration(c.SessionTimeout); err != nil {
-		return jerrors.Annotatef(err, "time.ParseDuration(SessionTimeout{%#v})", c.SessionTimeout)
+		return perrors.WithMessagef(err, "time.ParseDuration(SessionTimeout{%#v})", c.SessionTimeout)
 	}
 
 	if c.failFastTimeout, err = time.ParseDuration(c.FailFastTimeout); err != nil {
-		return jerrors.Annotatef(err, "time.ParseDuration(FailFastTimeout{%#v})", c.FailFastTimeout)
+		return perrors.WithMessagef(err, "time.ParseDuration(FailFastTimeout{%#v})", c.FailFastTimeout)
 	}
 
-	return jerrors.Trace(c.GettySessionParam.CheckValidity())
+	return perrors.WithStack(c.GettySessionParam.CheckValidity())
 }
 
 func (c *ServerConfig) CheckValidity() error {
 	var err error
 
 	if c.sessionTimeout, err = time.ParseDuration(c.SessionTimeout); err != nil {
-		return jerrors.Annotatef(err, "time.ParseDuration(SessionTimeout{%#v})", c.SessionTimeout)
+		return perrors.WithMessagef(err, "time.ParseDuration(SessionTimeout{%#v})", c.SessionTimeout)
 	}
 
 	if c.failFastTimeout, err = time.ParseDuration(c.FailFastTimeout); err != nil {
-		return jerrors.Annotatef(err, "time.ParseDuration(FailFastTimeout{%#v})", c.FailFastTimeout)
+		return perrors.WithMessagef(err, "time.ParseDuration(FailFastTimeout{%#v})", c.FailFastTimeout)
 	}
 
-	return jerrors.Trace(c.GettySessionParam.CheckValidity())
+	return perrors.WithStack(c.GettySessionParam.CheckValidity())
 }
diff --git a/protocol/dubbo/dubbo_exporter.go b/protocol/dubbo/dubbo_exporter.go
new file mode 100644
index 0000000000000000000000000000000000000000..d76ee812f1f21a29d391e404b2d68517f40bd4bc
--- /dev/null
+++ b/protocol/dubbo/dubbo_exporter.go
@@ -0,0 +1,45 @@
+// Copyright 2016-2019 Yincheng Fang
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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 (
+	"sync"
+)
+
+import (
+	"github.com/dubbo/go-for-apache-dubbo/common"
+	"github.com/dubbo/go-for-apache-dubbo/common/constant"
+	"github.com/dubbo/go-for-apache-dubbo/common/logger"
+	"github.com/dubbo/go-for-apache-dubbo/protocol"
+)
+
+type DubboExporter struct {
+	protocol.BaseExporter
+}
+
+func NewDubboExporter(key string, invoker protocol.Invoker, exporterMap *sync.Map) *DubboExporter {
+	return &DubboExporter{
+		BaseExporter: *protocol.NewBaseExporter(key, invoker, exporterMap),
+	}
+}
+
+func (de *DubboExporter) Unexport() {
+	service := de.GetInvoker().GetUrl().GetParam(constant.INTERFACE_KEY, "")
+	de.BaseExporter.Unexport()
+	err := common.ServiceMap.UnRegister(DUBBO, service)
+	if err != nil {
+		logger.Errorf("[DubboExporter.Unexport] error: %v", err)
+	}
+}
diff --git a/protocol/dubbo/dubbo_invoker.go b/protocol/dubbo/dubbo_invoker.go
new file mode 100644
index 0000000000000000000000000000000000000000..690e4d9b9a476eb832adcedd41f7ddb5ba52939f
--- /dev/null
+++ b/protocol/dubbo/dubbo_invoker.go
@@ -0,0 +1,101 @@
+// Copyright 2016-2019 Yincheng Fang
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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 (
+	"strconv"
+	"sync"
+)
+
+import (
+	perrors "github.com/pkg/errors"
+)
+
+import (
+	"github.com/dubbo/go-for-apache-dubbo/common"
+	"github.com/dubbo/go-for-apache-dubbo/common/constant"
+	"github.com/dubbo/go-for-apache-dubbo/common/logger"
+	"github.com/dubbo/go-for-apache-dubbo/protocol"
+	invocation_impl "github.com/dubbo/go-for-apache-dubbo/protocol/invocation"
+)
+
+var Err_No_Reply = perrors.New("request need @reply")
+
+type DubboInvoker struct {
+	protocol.BaseInvoker
+	client      *Client
+	destroyLock sync.Mutex
+}
+
+func NewDubboInvoker(url common.URL, client *Client) *DubboInvoker {
+	return &DubboInvoker{
+		BaseInvoker: *protocol.NewBaseInvoker(url),
+		client:      client,
+	}
+}
+
+func (di *DubboInvoker) Invoke(invocation protocol.Invocation) protocol.Result {
+
+	var (
+		err    error
+		result protocol.RPCResult
+	)
+
+	inv := invocation.(*invocation_impl.RPCInvocation)
+	url := di.GetUrl()
+	// async
+	async, err := strconv.ParseBool(inv.AttachmentsByKey(constant.ASYNC_KEY, "false"))
+	if err != nil {
+		logger.Errorf("ParseBool - error: %v", err)
+		async = false
+	}
+	if async {
+		if callBack, ok := inv.CallBack().(func(response CallResponse)); ok {
+			result.Err = di.client.AsyncCall(url.Location, url, inv.MethodName(), inv.Arguments(), callBack, inv.Reply())
+		} else {
+			result.Err = di.client.CallOneway(url.Location, url, inv.MethodName(), inv.Arguments())
+		}
+	} else {
+		if inv.Reply() == nil {
+			result.Err = Err_No_Reply
+		} else {
+			result.Err = di.client.Call(url.Location, url, inv.MethodName(), inv.Arguments(), inv.Reply())
+		}
+	}
+	if result.Err == nil {
+		result.Rest = inv.Reply()
+	}
+	logger.Debugf("result.Err: %v, result.Rest: %v", result.Err, result.Rest)
+
+	return &result
+}
+
+func (di *DubboInvoker) Destroy() {
+	if di.IsDestroyed() {
+		return
+	}
+	di.destroyLock.Lock()
+	defer di.destroyLock.Unlock()
+
+	if di.IsDestroyed() {
+		return
+	}
+
+	di.BaseInvoker.Destroy()
+
+	if di.client != nil {
+		di.client.Close() // close client
+	}
+}
diff --git a/protocol/dubbo/dubbo_invoker_test.go b/protocol/dubbo/dubbo_invoker_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..3931279ef5dff9a9ffd450a114a63dbcab223611
--- /dev/null
+++ b/protocol/dubbo/dubbo_invoker_test.go
@@ -0,0 +1,76 @@
+// Copyright 2016-2019 Yincheng Fang
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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 (
+	"sync"
+	"testing"
+	"time"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+import (
+	"github.com/dubbo/go-for-apache-dubbo/common/constant"
+	"github.com/dubbo/go-for-apache-dubbo/protocol/invocation"
+)
+
+func TestDubboInvoker_Invoke(t *testing.T) {
+	proto, url := InitTest(t)
+
+	c := &Client{
+		pendingResponses: make(map[SequenceType]*PendingResponse),
+		conf:             *clientConf,
+	}
+	c.pool = newGettyRPCClientConnPool(c, clientConf.PoolSize, time.Duration(int(time.Second)*clientConf.PoolTTL))
+
+	invoker := NewDubboInvoker(url, c)
+	user := &User{}
+
+	inv := invocation.NewRPCInvocationForConsumer("GetUser", nil, []interface{}{"1", "username"}, user, nil, url, nil)
+
+	// Call
+	res := invoker.Invoke(inv)
+	assert.NoError(t, res.Error())
+	assert.Equal(t, User{Id: "1", Name: "username"}, *res.Result().(*User))
+
+	// CallOneway
+	inv.SetAttachments(constant.ASYNC_KEY, "true")
+	res = invoker.Invoke(inv)
+	assert.NoError(t, res.Error())
+
+	// AsyncCall
+	lock := sync.Mutex{}
+	lock.Lock()
+	inv.SetCallBack(func(response CallResponse) {
+		assert.Equal(t, User{Id: "1", Name: "username"}, *response.Reply.(*User))
+		lock.Unlock()
+	})
+	res = invoker.Invoke(inv)
+	assert.NoError(t, res.Error())
+
+	// Err_No_Reply
+	inv.SetAttachments(constant.ASYNC_KEY, "false")
+	inv.SetReply(nil)
+	res = invoker.Invoke(inv)
+	assert.EqualError(t, res.Error(), "request need @reply")
+
+	// destroy
+	lock.Lock()
+	proto.Destroy()
+	lock.Unlock()
+}
diff --git a/protocol/dubbo/dubbo_protocol.go b/protocol/dubbo/dubbo_protocol.go
new file mode 100644
index 0000000000000000000000000000000000000000..512ca099dd8138261e7b50e5cfc07abec273e2d3
--- /dev/null
+++ b/protocol/dubbo/dubbo_protocol.go
@@ -0,0 +1,90 @@
+// Copyright 2016-2019 Yincheng Fang
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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/dubbo/go-for-apache-dubbo/common"
+	"github.com/dubbo/go-for-apache-dubbo/common/extension"
+	"github.com/dubbo/go-for-apache-dubbo/common/logger"
+	"github.com/dubbo/go-for-apache-dubbo/protocol"
+)
+
+const DUBBO = "dubbo"
+
+func init() {
+	extension.SetProtocol(DUBBO, GetProtocol)
+}
+
+var dubboProtocol *DubboProtocol
+
+type DubboProtocol struct {
+	protocol.BaseProtocol
+	serverMap map[string]*Server
+}
+
+func NewDubboProtocol() *DubboProtocol {
+	return &DubboProtocol{
+		BaseProtocol: protocol.NewBaseProtocol(),
+		serverMap:    make(map[string]*Server),
+	}
+}
+
+func (dp *DubboProtocol) Export(invoker protocol.Invoker) protocol.Exporter {
+	url := invoker.GetUrl()
+	serviceKey := url.Key()
+	exporter := NewDubboExporter(serviceKey, invoker, dp.ExporterMap())
+	dp.SetExporterMap(serviceKey, exporter)
+	logger.Infof("Export service: %s", url.String())
+
+	// start server
+	dp.openServer(url)
+	return exporter
+}
+
+func (dp *DubboProtocol) Refer(url common.URL) protocol.Invoker {
+	invoker := NewDubboInvoker(url, NewClient())
+	dp.SetInvokers(invoker)
+	logger.Infof("Refer service: %s", url.String())
+	return invoker
+}
+
+func (dp *DubboProtocol) Destroy() {
+	logger.Infof("DubboProtocol destroy.")
+
+	dp.BaseProtocol.Destroy()
+
+	// stop server
+	for key, server := range dp.serverMap {
+		delete(dp.serverMap, key)
+		server.Stop()
+	}
+}
+
+func (dp *DubboProtocol) openServer(url common.URL) {
+	exporter, ok := dp.ExporterMap().Load(url.Key())
+	if !ok {
+		panic("[DubboProtocol]" + url.Key() + "is not existing")
+	}
+	srv := NewServer(exporter.(protocol.Exporter))
+	dp.serverMap[url.Location] = srv
+	srv.Start(url)
+}
+
+func GetProtocol() protocol.Protocol {
+	if dubboProtocol != nil {
+		return dubboProtocol
+	}
+	return NewDubboProtocol()
+}
diff --git a/protocol/dubbo/dubbo_protocol_test.go b/protocol/dubbo/dubbo_protocol_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..9c89667923214248e940b3f98a602a8d4a3e66af
--- /dev/null
+++ b/protocol/dubbo/dubbo_protocol_test.go
@@ -0,0 +1,84 @@
+// Copyright 2016-2019 Yincheng Fang
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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"
+	"testing"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+import (
+	"github.com/dubbo/go-for-apache-dubbo/common"
+	"github.com/dubbo/go-for-apache-dubbo/protocol"
+)
+
+func TestDubboProtocol_Export(t *testing.T) {
+	// Export
+	proto := GetProtocol()
+	url, err := common.NewURL(context.Background(), "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&"+
+		"application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&"+
+		"environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&"+
+		"module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&"+
+		"side=provider&timeout=3000&timestamp=1556509797245")
+	assert.NoError(t, err)
+	srvConf = &ServerConfig{}
+	exporter := proto.Export(protocol.NewBaseInvoker(url))
+
+	// make sure url
+	eq := exporter.GetInvoker().GetUrl().URLEqual(url)
+	assert.True(t, eq)
+
+	// make sure exporterMap after 'Unexport'
+	_, ok := proto.(*DubboProtocol).ExporterMap().Load(url.Key())
+	assert.True(t, ok)
+	exporter.Unexport()
+	_, ok = proto.(*DubboProtocol).ExporterMap().Load(url.Key())
+	assert.False(t, ok)
+
+	// make sure serverMap after 'Destroy'
+	_, ok = proto.(*DubboProtocol).serverMap[url.Location]
+	assert.True(t, ok)
+	proto.Destroy()
+	_, ok = proto.(*DubboProtocol).serverMap[url.Location]
+	assert.False(t, ok)
+}
+
+func TestDubboProtocol_Refer(t *testing.T) {
+	// Refer
+	proto := GetProtocol()
+	url, err := common.NewURL(context.Background(), "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&"+
+		"application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&"+
+		"environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&"+
+		"module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&"+
+		"side=provider&timeout=3000&timestamp=1556509797245")
+	assert.NoError(t, err)
+	clientConf = &ClientConfig{}
+	invoker := proto.Refer(url)
+
+	// make sure url
+	eq := invoker.GetUrl().URLEqual(url)
+	assert.True(t, eq)
+
+	// make sure invokers after 'Destroy'
+	invokersLen := len(proto.(*DubboProtocol).Invokers())
+	assert.Equal(t, 1, invokersLen)
+	proto.Destroy()
+	invokersLen = len(proto.(*DubboProtocol).Invokers())
+	assert.Equal(t, 0, invokersLen)
+}
diff --git a/protocol/dubbo/listener.go b/protocol/dubbo/listener.go
new file mode 100644
index 0000000000000000000000000000000000000000..fc56050c12a196753aac606dbbc8a801e7e79040
--- /dev/null
+++ b/protocol/dubbo/listener.go
@@ -0,0 +1,352 @@
+// Copyright 2016-2019 Yincheng Fang, Alex Stocks
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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"
+	"reflect"
+	"sync"
+	"time"
+)
+
+import (
+	"github.com/dubbogo/getty"
+	"github.com/dubbogo/hessian2"
+	perrors "github.com/pkg/errors"
+)
+
+import (
+	"github.com/dubbo/go-for-apache-dubbo/common"
+	"github.com/dubbo/go-for-apache-dubbo/common/constant"
+	"github.com/dubbo/go-for-apache-dubbo/common/logger"
+	"github.com/dubbo/go-for-apache-dubbo/protocol"
+	"github.com/dubbo/go-for-apache-dubbo/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
+}
+
+////////////////////////////////////////////
+// RpcClientHandler
+////////////////////////////////////////////
+
+type RpcClientHandler struct {
+	conn *gettyRPCClient
+}
+
+func NewRpcClientHandler(client *gettyRPCClient) *RpcClientHandler {
+	return &RpcClientHandler{conn: client}
+}
+
+func (h *RpcClientHandler) OnOpen(session getty.Session) error {
+	h.conn.addSession(session)
+	return nil
+}
+
+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)
+}
+
+func (h *RpcClientHandler) OnClose(session getty.Session) {
+	logger.Infof("session{%s} is closing......", session.Stat())
+	h.conn.removeSession(session)
+}
+
+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 {
+		logger.Debugf("get rpc heartbeat response{header: %#v, body: %#v}", p.Header, p.Body)
+		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 {
+		return
+	}
+
+	if p.Err != nil {
+		pendingResponse.err = p.Err
+	}
+
+	if pendingResponse.callback == nil {
+		pendingResponse.done <- struct{}{}
+	} else {
+		pendingResponse.callback(pendingResponse.GetCallResponse())
+	}
+}
+
+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
+	}
+
+	h.conn.pool.rpcClient.heartbeat(session)
+}
+
+////////////////////////////////////////////
+// RpcServerHandler
+////////////////////////////////////////////
+
+type RpcServerHandler struct {
+	exporter       protocol.Exporter
+	maxSessionNum  int
+	sessionTimeout time.Duration
+	sessionMap     map[getty.Session]*rpcSession
+	rwlock         sync.RWMutex
+}
+
+func NewRpcServerHandler(exporter protocol.Exporter, maxSessionNum int, sessionTimeout time.Duration) *RpcServerHandler {
+	return &RpcServerHandler{
+		exporter:       exporter,
+		maxSessionNum:  maxSessionNum,
+		sessionTimeout: sessionTimeout,
+		sessionMap:     make(map[getty.Session]*rpcSession),
+	}
+}
+
+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
+}
+
+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()
+}
+
+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()
+}
+
+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 packge{%#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)
+		h.reply(session, p, hessian.PackageHeartbeat)
+		return
+	}
+
+	twoway := true
+	// not twoway
+	if p.Header.Type&hessian.PackageRequest_TwoWay == 0x00 {
+		twoway = false
+		h.reply(session, p, hessian.PackageResponse)
+	}
+
+	invoker := h.exporter.GetInvoker()
+	if invoker != nil {
+		result := invoker.Invoke(invocation.NewRPCInvocationForProvider(p.Service.Method, p.Body.(map[string]interface{})["args"].([]interface{}), map[string]string{
+			constant.PATH_KEY: p.Service.Path,
+			//attachments[constant.GROUP_KEY] = url.GetParam(constant.GROUP_KEY, "")
+			constant.INTERFACE_KEY: p.Service.Interface,
+			constant.VERSION_KEY:   p.Service.Version,
+		}))
+		if err := result.Error(); err != nil {
+			p.Header.ResponseStatus = hessian.Response_SERVER_ERROR
+			p.Body = err
+			h.reply(session, p, hessian.PackageResponse)
+			return
+		}
+		if res := result.Result(); res != nil {
+			p.Header.ResponseStatus = hessian.Response_OK
+			p.Body = res
+			h.reply(session, p, hessian.PackageResponse)
+			return
+		}
+	}
+
+	h.callService(p, nil)
+	if !twoway {
+		return
+	}
+	h.reply(session, p, hessian.PackageResponse)
+}
+
+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()
+	}
+}
+
+func (h *RpcServerHandler) callService(req *DubboPackage, ctx context.Context) {
+
+	defer func() {
+		if e := recover(); e != nil {
+			req.Header.ResponseStatus = hessian.Response_BAD_REQUEST
+			if err, ok := e.(error); ok {
+				logger.Errorf("callService panic: %#v", err)
+				req.Body = e.(error)
+			} else if err, ok := e.(string); ok {
+				logger.Errorf("callService panic: %#v", perrors.New(err))
+				req.Body = perrors.New(err)
+			} else {
+				logger.Errorf("callService panic: %#v", e)
+				req.Body = e
+			}
+		}
+	}()
+
+	svcIf := req.Body.(map[string]interface{})["service"]
+	if svcIf == nil {
+		logger.Errorf("service not found!")
+		req.Header.ResponseStatus = hessian.Response_SERVICE_NOT_FOUND
+		req.Body = perrors.New("service not found")
+		return
+	}
+	svc := svcIf.(*common.Service)
+	method := svc.Method()[req.Service.Method]
+	if method == nil {
+		logger.Errorf("method not found!")
+		req.Header.ResponseStatus = hessian.Response_SERVICE_NOT_FOUND
+		req.Body = perrors.New("method not found")
+		return
+	}
+
+	in := []reflect.Value{svc.Rcvr()}
+	if method.CtxType() != nil {
+		in = append(in, method.SuiteContext(ctx))
+	}
+
+	// prepare argv
+	argv := req.Body.(map[string]interface{})["args"]
+	if (len(method.ArgsType()) == 1 || len(method.ArgsType()) == 2 && method.ReplyType() == nil) && method.ArgsType()[0].String() == "[]interface {}" {
+		in = append(in, reflect.ValueOf(argv))
+	} else {
+		for i := 0; i < len(argv.([]interface{})); i++ {
+			in = append(in, reflect.ValueOf(argv.([]interface{})[i]))
+		}
+	}
+
+	// prepare replyv
+	var replyv reflect.Value
+	if method.ReplyType() == nil {
+		replyv = reflect.New(method.ArgsType()[len(method.ArgsType())-1].Elem())
+		in = append(in, replyv)
+	}
+
+	returnValues := method.Method().Func.Call(in)
+
+	var retErr interface{}
+	if len(returnValues) == 1 {
+		retErr = returnValues[0].Interface()
+	} else {
+		replyv = returnValues[0]
+		retErr = returnValues[1].Interface()
+	}
+	if retErr != nil {
+		req.Header.ResponseStatus = hessian.Response_SERVER_ERROR
+		req.Body = retErr.(error)
+	} else {
+		req.Body = replyv.Interface()
+	}
+}
+
+func (h *RpcServerHandler) 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/dubbo/pool.go b/protocol/dubbo/pool.go
similarity index 72%
rename from dubbo/pool.go
rename to protocol/dubbo/pool.go
index 2cc9cc7ac99142b2ad32beea6cc39722d0dd9a39..60a65e45fe386a027c6e1556949730cf9ef050f4 100644
--- a/dubbo/pool.go
+++ b/protocol/dubbo/pool.go
@@ -1,3 +1,17 @@
+// Copyright 2016-2019 Yincheng Fang, Alex Stocks
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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 (
@@ -10,9 +24,12 @@ import (
 )
 
 import (
-	"github.com/AlexStocks/getty"
-	log "github.com/AlexStocks/log4go"
-	jerrors "github.com/juju/errors"
+	"github.com/dubbogo/getty"
+	perrors "github.com/pkg/errors"
+)
+
+import (
+	"github.com/dubbo/go-for-apache-dubbo/common/logger"
 )
 
 type gettyRPCClient struct {
@@ -29,7 +46,7 @@ type gettyRPCClient struct {
 }
 
 var (
-	errClientPoolClosed = jerrors.New("client pool closed")
+	errClientPoolClosed = perrors.New("client pool closed")
 )
 
 func newGettyRPCClientConn(pool *gettyRPCClientPool, protocol, addr string) (*gettyRPCClient, error) {
@@ -40,6 +57,7 @@ func newGettyRPCClientConn(pool *gettyRPCClientPool, protocol, addr string) (*ge
 		gettyClient: getty.NewTCPClient(
 			getty.WithServerAddress(addr),
 			getty.WithConnectionNumber((int)(pool.rpcClient.conf.ConnectionNum)),
+			getty.WithReconnectInterval(pool.rpcClient.conf.ReconnectInterval),
 		),
 	}
 	c.gettyClient.RunEventLoop(c.newSession)
@@ -51,11 +69,11 @@ func newGettyRPCClientConn(pool *gettyRPCClientPool, protocol, addr string) (*ge
 		}
 
 		if idx > 5000 {
-			return nil, jerrors.New(fmt.Sprintf("failed to create client connection to %s in 5 seconds", addr))
+			return nil, perrors.New(fmt.Sprintf("failed to create client connection to %s in 5 seconds", addr))
 		}
 		time.Sleep(1e6)
 	}
-	log.Info("client init ok")
+	logger.Infof("client init ok")
 	c.created = time.Now().Unix()
 
 	return c, nil
@@ -95,7 +113,7 @@ func (c *gettyRPCClient) newSession(session getty.Session) error {
 	session.SetWriteTimeout(conf.GettySessionParam.tcpWriteTimeout)
 	session.SetCronPeriod((int)(conf.heartbeatPeriod.Nanoseconds() / 1e6))
 	session.SetWaitTime(conf.GettySessionParam.waitTimeout)
-	log.Debug("client new session:%s\n", session.Stat())
+	logger.Debugf("client new session:%s\n", session.Stat())
 
 	return nil
 }
@@ -116,7 +134,7 @@ func (c *gettyRPCClient) selectSession() getty.Session {
 }
 
 func (c *gettyRPCClient) addSession(session getty.Session) {
-	log.Debug("add session{%s}", session.Stat())
+	logger.Debugf("add session{%s}", session.Stat())
 	if session == nil {
 		return
 	}
@@ -140,11 +158,11 @@ func (c *gettyRPCClient) removeSession(session getty.Session) {
 	for i, s := range c.sessions {
 		if s.session == session {
 			c.sessions = append(c.sessions[:i], c.sessions[i+1:]...)
-			log.Debug("delete session{%s}, its index{%d}", session.Stat(), i)
+			logger.Debugf("delete session{%s}, its index{%d}", session.Stat(), i)
 			break
 		}
 	}
-	log.Info("after remove session{%s}, left session number:%d", session.Stat(), len(c.sessions))
+	logger.Infof("after remove session{%s}, left session number:%d", session.Stat(), len(c.sessions))
 	if len(c.sessions) == 0 {
 		c.close() // -> pool.remove(c)
 	}
@@ -188,7 +206,7 @@ func (c *gettyRPCClient) getClientRpcSession(session getty.Session) (rpcSession,
 		}
 	}
 
-	return rpcSession, jerrors.Trace(err)
+	return rpcSession, perrors.WithStack(err)
 }
 
 func (c *gettyRPCClient) isAvailable() bool {
@@ -200,12 +218,12 @@ func (c *gettyRPCClient) isAvailable() bool {
 }
 
 func (c *gettyRPCClient) close() error {
-	err := jerrors.Errorf("close gettyRPCClient{%#v} again", c)
+	err := perrors.Errorf("close gettyRPCClient{%#v} again", c)
 	c.once.Do(func() {
 		// delete @c from client pool
 		c.pool.remove(c)
 		for _, s := range c.sessions {
-			log.Info("close client session{%s, last active:%s, request number:%d}",
+			logger.Infof("close client session{%s, last active:%s, request number:%d}",
 				s.session.Stat(), s.session.GetActive().String(), s.reqNum)
 			s.session.Close()
 		}
@@ -225,7 +243,7 @@ type gettyRPCClientPool struct {
 	ttl       int64 // 姣忎釜gettyRPCClient鐨勬湁鏁堟湡鏃堕棿. pool瀵硅薄浼氬湪getConn鏃舵墽琛宼tl妫€鏌�
 
 	sync.Mutex
-	connMap map[string][]*gettyRPCClient // 浠嶽]*gettyRPCClient 鍙key鏄繛鎺ュ湴鍧€锛岃€寁alue鏄搴旇繖涓湴鍧€鐨勮繛鎺ユ暟缁�
+	conns []*gettyRPCClient // 浠嶽]*gettyRPCClient 鍙key鏄繛鎺ュ湴鍧€锛岃€寁alue鏄搴旇繖涓湴鍧€鐨勮繛鎺ユ暟缁�
 }
 
 func newGettyRPCClientConnPool(rpcClient *Client, size int, ttl time.Duration) *gettyRPCClientPool {
@@ -233,49 +251,39 @@ func newGettyRPCClientConnPool(rpcClient *Client, size int, ttl time.Duration) *
 		rpcClient: rpcClient,
 		size:      size,
 		ttl:       int64(ttl.Seconds()),
-		connMap:   make(map[string][]*gettyRPCClient),
+		conns:     []*gettyRPCClient{},
 	}
 }
 
 func (p *gettyRPCClientPool) close() {
 	p.Lock()
-	connMap := p.connMap
-	p.connMap = nil
+	conns := p.conns
+	p.conns = nil
 	p.Unlock()
-	for _, connArray := range connMap {
-		for _, conn := range connArray {
-			conn.close()
-		}
+	for _, conn := range conns {
+		conn.close()
 	}
 }
 
 func (p *gettyRPCClientPool) getGettyRpcClient(protocol, addr string) (*gettyRPCClient, error) {
-	var builder strings.Builder
-
-	builder.WriteString(addr)
-	builder.WriteString("@")
-	builder.WriteString(protocol)
-
-	key := builder.String()
 
 	p.Lock()
 	defer p.Unlock()
-	if p.connMap == nil {
+	if p.conns == nil {
 		return nil, errClientPoolClosed
 	}
 
-	connArray := p.connMap[key]
 	now := time.Now().Unix()
 
-	for len(connArray) > 0 {
-		conn := connArray[len(connArray)-1]
-		connArray = connArray[:len(connArray)-1]
-		p.connMap[key] = connArray
+	for len(p.conns) > 0 {
+		conn := p.conns[len(p.conns)-1]
+		p.conns = p.conns[:len(p.conns)-1]
 
 		if d := now - conn.created; d > p.ttl {
 			conn.close() // -> pool.remove(c)
 			continue
 		}
+		conn.created = now //update created time
 
 		return conn, nil
 	}
@@ -293,26 +301,17 @@ func (p *gettyRPCClientPool) release(conn *gettyRPCClient, err error) {
 		return
 	}
 
-	var builder strings.Builder
-
-	builder.WriteString(conn.addr)
-	builder.WriteString("@")
-	builder.WriteString(conn.protocol)
-
-	key := builder.String()
-
 	p.Lock()
 	defer p.Unlock()
-	if p.connMap == nil {
+	if p.conns == nil {
 		return
 	}
 
-	connArray := p.connMap[key]
-	if len(connArray) >= p.size {
+	if len(p.conns) >= p.size {
 		conn.close()
 		return
 	}
-	p.connMap[key] = append(connArray, conn)
+	p.conns = append(p.conns, conn)
 }
 
 func (p *gettyRPCClientPool) remove(conn *gettyRPCClient) {
@@ -320,27 +319,28 @@ func (p *gettyRPCClientPool) remove(conn *gettyRPCClient) {
 		return
 	}
 
-	var builder strings.Builder
-
-	builder.WriteString(conn.addr)
-	builder.WriteString("@")
-	builder.WriteString(conn.protocol)
-
-	key := builder.String()
-
-	p.Lock()
-	defer p.Unlock()
-	if p.connMap == nil {
+	//p.Lock()
+	//defer p.Unlock()
+	if p.conns == nil {
 		return
 	}
 
-	connArray := p.connMap[key]
-	if len(connArray) > 0 {
-		for idx, c := range connArray {
+	if len(p.conns) > 0 {
+		for idx, c := range p.conns {
 			if conn == c {
-				p.connMap[key] = append(connArray[:idx], connArray[idx+1:]...)
+				p.conns = append(p.conns[:idx], p.conns[idx+1:]...)
 				break
 			}
 		}
 	}
 }
+
+func GenerateEndpointAddr(protocol, addr string) string {
+	var builder strings.Builder
+
+	builder.WriteString(protocol)
+	builder.WriteString("://")
+	builder.WriteString(addr)
+
+	return builder.String()
+}
diff --git a/dubbo/readwriter.go b/protocol/dubbo/readwriter.go
similarity index 53%
rename from dubbo/readwriter.go
rename to protocol/dubbo/readwriter.go
index e14cf201009a98992d57ed1e714a7cb3252bd862..87b73aed91d5232975b6f6b1513e3d728c0f2175 100644
--- a/dubbo/readwriter.go
+++ b/protocol/dubbo/readwriter.go
@@ -1,3 +1,17 @@
+// Copyright 2016-2019 Yincheng Fang, Alex Stocks
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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 (
@@ -6,9 +20,13 @@ import (
 )
 
 import (
-	"github.com/AlexStocks/getty"
-	log "github.com/AlexStocks/log4go"
-	jerrors "github.com/juju/errors"
+	"github.com/dubbogo/getty"
+	perrors "github.com/pkg/errors"
+)
+import (
+	"github.com/dubbo/go-for-apache-dubbo/common"
+	"github.com/dubbo/go-for-apache-dubbo/common/constant"
+	"github.com/dubbo/go-for-apache-dubbo/common/logger"
 )
 
 ////////////////////////////////////////////
@@ -24,14 +42,14 @@ func NewRpcClientPackageHandler(client *Client) *RpcClientPackageHandler {
 }
 
 func (p *RpcClientPackageHandler) Read(ss getty.Session, data []byte) (interface{}, int, error) {
-	pkg := &DubboPackage{
-		Body: p.client.pendingResponses[SequenceType(int64(p.client.sequence.Load()))].reply,
-	}
+	p.client.pendingLock.RLock()
+	defer p.client.pendingLock.RUnlock()
+	pkg := &DubboPackage{}
 
 	buf := bytes.NewBuffer(data)
-	err := pkg.Unmarshal(buf)
+	err := pkg.Unmarshal(buf, p.client)
 	if err != nil {
-		pkg.Err = jerrors.Trace(err) // client will get this err
+		pkg.Err = perrors.WithStack(err) // client will get this err
 		return pkg, len(data), nil
 	}
 
@@ -41,17 +59,17 @@ func (p *RpcClientPackageHandler) Read(ss getty.Session, data []byte) (interface
 func (p *RpcClientPackageHandler) Write(ss getty.Session, pkg interface{}) error {
 	req, ok := pkg.(*DubboPackage)
 	if !ok {
-		log.Error("illegal pkg:%+v\n", pkg)
-		return jerrors.New("invalid rpc request")
+		logger.Errorf("illegal pkg:%+v\n", pkg)
+		return perrors.New("invalid rpc request")
 	}
 
 	buf, err := req.Marshal()
 	if err != nil {
-		log.Warn("binary.Write(req{%#v}) = err{%#v}", req, jerrors.ErrorStack(err))
-		return jerrors.Trace(err)
+		logger.Warnf("binary.Write(req{%#v}) = err{%#v}", req, perrors.WithStack(err))
+		return perrors.WithStack(err)
 	}
 
-	return jerrors.Trace(ss.WriteBytes(buf.Bytes()))
+	return perrors.WithStack(ss.WriteBytes(buf.Bytes()))
 }
 
 ////////////////////////////////////////////
@@ -59,15 +77,10 @@ func (p *RpcClientPackageHandler) Write(ss getty.Session, pkg interface{}) error
 ////////////////////////////////////////////
 
 type RpcServerPackageHandler struct {
-	server *Server
-	srvMap serviceMap
 }
 
-func NewRpcServerPackageHandler(server *Server, srvMap serviceMap) *RpcServerPackageHandler {
-	return &RpcServerPackageHandler{
-		server: server,
-		srvMap: srvMap,
-	}
+func NewRpcServerPackageHandler() *RpcServerPackageHandler {
+	return &RpcServerPackageHandler{}
 }
 
 func (p *RpcServerPackageHandler) Read(ss getty.Session, data []byte) (interface{}, int, error) {
@@ -78,7 +91,7 @@ func (p *RpcServerPackageHandler) Read(ss getty.Session, data []byte) (interface
 	buf := bytes.NewBuffer(data)
 	err := pkg.Unmarshal(buf)
 	if err != nil {
-		return nil, 0, jerrors.Trace(err)
+		return nil, 0, perrors.WithStack(err)
 	}
 	// convert params of request
 	req := pkg.Body.([]interface{}) // length of body should be 7
@@ -90,7 +103,7 @@ func (p *RpcServerPackageHandler) Read(ss getty.Session, data []byte) (interface
 			dubboVersion = req[0].(string)
 		}
 		if req[1] != nil {
-			pkg.Service.Target = req[1].(string)
+			pkg.Service.Path = req[1].(string)
 		}
 		if req[2] != nil {
 			pkg.Service.Version = req[2].(string)
@@ -107,11 +120,12 @@ func (p *RpcServerPackageHandler) Read(ss getty.Session, data []byte) (interface
 		if req[6] != nil {
 			attachments = req[6].(map[interface{}]interface{})
 		}
+		pkg.Service.Interface = attachments[constant.INTERFACE_KEY].(string)
 		pkg.Body = map[string]interface{}{
 			"dubboVersion": dubboVersion,
 			"argsTypes":    argsTypes,
 			"args":         args,
-			"service":      p.srvMap[pkg.Service.Target],
+			"service":      common.ServiceMap.GetService(DUBBO, pkg.Service.Interface),
 			"attachments":  attachments,
 		}
 	}
@@ -122,15 +136,15 @@ func (p *RpcServerPackageHandler) Read(ss getty.Session, data []byte) (interface
 func (p *RpcServerPackageHandler) Write(ss getty.Session, pkg interface{}) error {
 	res, ok := pkg.(*DubboPackage)
 	if !ok {
-		log.Error("illegal pkg:%+v\n, it is %+v", pkg, reflect.TypeOf(pkg))
-		return jerrors.New("invalid rpc response")
+		logger.Errorf("illegal pkg:%+v\n, it is %+v", pkg, reflect.TypeOf(pkg))
+		return perrors.New("invalid rpc response")
 	}
 
 	buf, err := res.Marshal()
 	if err != nil {
-		log.Warn("binary.Write(res{%#v}) = err{%#v}", res, jerrors.ErrorStack(err))
-		return jerrors.Trace(err)
+		logger.Warnf("binary.Write(res{%#v}) = err{%#v}", res, perrors.WithStack(err))
+		return perrors.WithStack(err)
 	}
 
-	return jerrors.Trace(ss.WriteBytes(buf.Bytes()))
+	return perrors.WithStack(ss.WriteBytes(buf.Bytes()))
 }
diff --git a/protocol/dubbo/server.go b/protocol/dubbo/server.go
new file mode 100644
index 0000000000000000000000000000000000000000..f8de070bcc42bf0f81ad2a2e12658f2283bdf210
--- /dev/null
+++ b/protocol/dubbo/server.go
@@ -0,0 +1,147 @@
+// Copyright 2016-2019 Yincheng Fang, Alex Stocks
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package dubbo
+
+import (
+	"fmt"
+	"net"
+)
+
+import (
+	"github.com/dubbogo/getty"
+	"gopkg.in/yaml.v2"
+)
+
+import (
+	"github.com/dubbo/go-for-apache-dubbo/common"
+	"github.com/dubbo/go-for-apache-dubbo/common/logger"
+	"github.com/dubbo/go-for-apache-dubbo/config"
+	"github.com/dubbo/go-for-apache-dubbo/protocol"
+)
+
+var srvConf *ServerConfig
+
+func init() {
+
+	// load clientconfig from provider_config
+	protocolConf := config.GetProviderConfig().ProtocolConf
+	if protocolConf == nil {
+		logger.Warnf("protocol_conf is nil")
+		return
+	}
+	dubboConf := protocolConf.(map[interface{}]interface{})[DUBBO]
+	if protocolConf == nil {
+		logger.Warnf("dubboConf is nil")
+		return
+	}
+
+	dubboConfByte, err := yaml.Marshal(dubboConf)
+	if err != nil {
+		panic(err)
+	}
+	conf := &ServerConfig{}
+	err = yaml.Unmarshal(dubboConfByte, conf)
+	if err != nil {
+		panic(err)
+	}
+
+	if err := conf.CheckValidity(); err != nil {
+		panic(err)
+	}
+
+	srvConf = conf
+}
+
+func SetServerConfig(s ServerConfig) {
+	srvConf = &s
+}
+
+func GetServerConfig() ServerConfig {
+	return *srvConf
+}
+
+type Server struct {
+	conf      ServerConfig
+	tcpServer getty.Server
+	exporter  protocol.Exporter
+}
+
+func NewServer(exporter protocol.Exporter) *Server {
+
+	s := &Server{
+		exporter: exporter,
+		conf:     *srvConf,
+	}
+
+	return s
+}
+
+func (s *Server) newSession(session getty.Session) error {
+	var (
+		ok      bool
+		tcpConn *net.TCPConn
+	)
+	conf := s.conf
+
+	if conf.GettySessionParam.CompressEncoding {
+		session.SetCompressType(getty.CompressZip)
+	}
+
+	if tcpConn, ok = session.Conn().(*net.TCPConn); !ok {
+		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)
+	}
+	tcpConn.SetReadBuffer(conf.GettySessionParam.TcpRBufSize)
+	tcpConn.SetWriteBuffer(conf.GettySessionParam.TcpWBufSize)
+
+	session.SetName(conf.GettySessionParam.SessionName)
+	session.SetMaxMsgLen(conf.GettySessionParam.MaxMsgLen)
+	session.SetPkgHandler(NewRpcServerPackageHandler())
+	session.SetEventListener(NewRpcServerHandler(s.exporter, conf.SessionNumber, conf.sessionTimeout))
+	session.SetRQLen(conf.GettySessionParam.PkgRQSize)
+	session.SetWQLen(conf.GettySessionParam.PkgWQSize)
+	session.SetReadTimeout(conf.GettySessionParam.tcpReadTimeout)
+	session.SetWriteTimeout(conf.GettySessionParam.tcpWriteTimeout)
+	session.SetCronPeriod((int)(conf.sessionTimeout.Nanoseconds() / 1e6))
+	session.SetWaitTime(conf.GettySessionParam.waitTimeout)
+	logger.Debugf("app accepts new session:%s\n", session.Stat())
+
+	return nil
+}
+
+func (s *Server) Start(url common.URL) {
+	var (
+		addr      string
+		tcpServer getty.Server
+	)
+
+	addr = url.Location
+	tcpServer = getty.NewTCPServer(
+		getty.WithLocalAddress(addr),
+	)
+	tcpServer.RunEventLoop(s.newSession)
+	logger.Debugf("s bind addr{%s} ok!", addr)
+	s.tcpServer = tcpServer
+
+}
+
+func (s *Server) Stop() {
+	s.tcpServer.Close()
+}
diff --git a/protocol/invocation.go b/protocol/invocation.go
new file mode 100644
index 0000000000000000000000000000000000000000..0297a556c4708292d7d5706718af596804d788f4
--- /dev/null
+++ b/protocol/invocation.go
@@ -0,0 +1,29 @@
+// Copyright 2016-2019 Yincheng Fang
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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 protocol
+
+import (
+	"reflect"
+)
+
+type Invocation interface {
+	MethodName() string
+	ParameterTypes() []reflect.Type
+	Arguments() []interface{}
+	Reply() interface{}
+	Attachments() map[string]string
+	AttachmentsByKey(string, string) string
+	Invoker() Invoker
+}
diff --git a/protocol/invocation/rpcinvocation.go b/protocol/invocation/rpcinvocation.go
new file mode 100644
index 0000000000000000000000000000000000000000..87ab0c67838ba028eab81185eb6d4ccae5679966
--- /dev/null
+++ b/protocol/invocation/rpcinvocation.go
@@ -0,0 +1,129 @@
+// Copyright 2016-2019 Yincheng Fang
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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 invocation
+
+import (
+	"reflect"
+)
+
+import (
+	"github.com/dubbo/go-for-apache-dubbo/common"
+	"github.com/dubbo/go-for-apache-dubbo/common/constant"
+	"github.com/dubbo/go-for-apache-dubbo/protocol"
+)
+
+/////////////////////////////
+// Invocation Impletment of RPC
+/////////////////////////////
+// todo: is it necessary to separate fields of consumer(provider) from RPCInvocation
+type RPCInvocation struct {
+	methodName     string
+	parameterTypes []reflect.Type
+	arguments      []interface{}
+	reply          interface{}
+	callBack       interface{}
+	attachments    map[string]string
+	invoker        protocol.Invoker
+}
+
+func NewRPCInvocationForConsumer(methodName string, parameterTypes []reflect.Type, arguments []interface{},
+	reply interface{}, callBack interface{}, url common.URL, invoker protocol.Invoker) *RPCInvocation {
+
+	attachments := map[string]string{}
+	attachments[constant.PATH_KEY] = url.Path
+	attachments[constant.GROUP_KEY] = url.GetParam(constant.GROUP_KEY, "")
+	attachments[constant.INTERFACE_KEY] = url.GetParam(constant.INTERFACE_KEY, "")
+	attachments[constant.VERSION_KEY] = url.GetParam(constant.VERSION_KEY, constant.DEFAULT_VERSION)
+
+	return &RPCInvocation{
+		methodName:     methodName,
+		parameterTypes: parameterTypes,
+		arguments:      arguments,
+		reply:          reply,
+		callBack:       callBack,
+		attachments:    attachments,
+		invoker:        invoker,
+	}
+}
+
+func NewRPCInvocationForProvider(methodName string, arguments []interface{}, attachments map[string]string) *RPCInvocation {
+	return &RPCInvocation{
+		methodName:  methodName,
+		arguments:   arguments,
+		attachments: attachments,
+	}
+}
+
+func (r *RPCInvocation) MethodName() string {
+	return r.methodName
+}
+
+func (r *RPCInvocation) ParameterTypes() []reflect.Type {
+	return r.parameterTypes
+}
+
+func (r *RPCInvocation) Arguments() []interface{} {
+	return r.arguments
+}
+
+func (r *RPCInvocation) Reply() interface{} {
+	return r.reply
+}
+
+func (r *RPCInvocation) SetReply(reply interface{}) {
+	r.reply = reply
+}
+
+func (r *RPCInvocation) Attachments() map[string]string {
+	return r.attachments
+}
+
+func (r *RPCInvocation) AttachmentsByKey(key string, defaultValue string) string {
+	if r.attachments == nil {
+		return defaultValue
+	}
+	value, ok := r.attachments[key]
+	if ok {
+		return value
+	}
+	return defaultValue
+}
+
+func (r *RPCInvocation) SetAttachments(key string, value string) {
+	if r.attachments == nil {
+		r.attachments = make(map[string]string)
+	}
+	r.attachments[key] = value
+}
+
+func (r *RPCInvocation) Invoker() protocol.Invoker {
+	return r.invoker
+}
+
+func (r *RPCInvocation) SetInvoker() protocol.Invoker {
+	return r.invoker
+}
+
+func (r *RPCInvocation) SetCallBack(c interface{}) {
+	r.callBack = c
+}
+
+func (r *RPCInvocation) CallBack() interface{} {
+	return r.callBack
+}
+
+func (r *RPCInvocation) SetMethod(method string) {
+	r.methodName = method
+}
diff --git a/protocol/invoker.go b/protocol/invoker.go
new file mode 100644
index 0000000000000000000000000000000000000000..bedbb10442031fe8b723286f824c03f321623ca1
--- /dev/null
+++ b/protocol/invoker.go
@@ -0,0 +1,66 @@
+// Copyright 2016-2019 Yincheng Fang
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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 protocol
+
+import (
+	"github.com/dubbo/go-for-apache-dubbo/common"
+	"github.com/dubbo/go-for-apache-dubbo/common/logger"
+)
+
+// Extension - Invoker
+type Invoker interface {
+	common.Node
+	Invoke(Invocation) Result
+}
+
+/////////////////////////////
+// base invoker
+/////////////////////////////
+
+type BaseInvoker struct {
+	url       common.URL
+	available bool
+	destroyed bool
+}
+
+func NewBaseInvoker(url common.URL) *BaseInvoker {
+	return &BaseInvoker{
+		url:       url,
+		available: true,
+		destroyed: false,
+	}
+}
+
+func (bi *BaseInvoker) GetUrl() common.URL {
+	return bi.url
+}
+
+func (bi *BaseInvoker) IsAvailable() bool {
+	return bi.available
+}
+
+func (bi *BaseInvoker) IsDestroyed() bool {
+	return bi.destroyed
+}
+
+func (bi *BaseInvoker) Invoke(invocation Invocation) Result {
+	return &RPCResult{}
+}
+
+func (bi *BaseInvoker) Destroy() {
+	logger.Infof("Destroy invoker: %s", bi.GetUrl().String())
+	bi.destroyed = true
+	bi.available = false
+}
diff --git a/jsonrpc/http.go b/protocol/jsonrpc/http.go
similarity index 65%
rename from jsonrpc/http.go
rename to protocol/jsonrpc/http.go
index 919527b71fefb8b7e12a8d3b00eb00ece4a3aeee..60257ce027c1434bfd5cc06249f380d3511957dd 100644
--- a/jsonrpc/http.go
+++ b/protocol/jsonrpc/http.go
@@ -1,3 +1,17 @@
+// Copyright 2016-2019 Alex Stocks
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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 jsonrpc
 
 import (
@@ -16,13 +30,12 @@ import (
 )
 
 import (
-	jerrors "github.com/juju/errors"
+	perrors "github.com/pkg/errors"
 )
 
 import (
-	"github.com/dubbo/go-for-apache-dubbo/client"
-	"github.com/dubbo/go-for-apache-dubbo/public"
-	"github.com/dubbo/go-for-apache-dubbo/registry"
+	"github.com/dubbo/go-for-apache-dubbo/common"
+	"github.com/dubbo/go-for-apache-dubbo/common/constant"
 )
 
 //////////////////////////////////////////////
@@ -38,11 +51,6 @@ type Request struct {
 	method      string
 	args        interface{}
 	contentType string
-	conf        registry.ServiceConfig
-}
-
-func (r *Request) ServiceConfig() registry.ServiceConfig {
-	return r.conf
 }
 
 //////////////////////////////////////////////
@@ -83,36 +91,31 @@ func NewHTTPClient(opt *HTTPOptions) *HTTPClient {
 	}
 }
 
-func (c *HTTPClient) NewRequest(conf registry.ServiceConfig, method string, args interface{}) (client.Request, error) {
+func (c *HTTPClient) NewRequest(service common.URL, method string, args interface{}) *Request {
 
 	return &Request{
 		ID:       atomic.AddInt64(&c.ID, 1),
-		group:    conf.Group(),
-		protocol: conf.Protocol(),
-		version:  conf.Version(),
-		service:  conf.Service(),
+		group:    service.GetParam(constant.GROUP_KEY, ""),
+		protocol: service.Protocol,
+		version:  service.GetParam(constant.VERSION_KEY, constant.DEFAULT_VERSION),
+		service:  service.Path,
 		method:   method,
 		args:     args,
-		conf:     conf,
-	}, nil
+	}
 }
 
-func (c *HTTPClient) Call(ctx context.Context, service registry.ServiceURL, request client.Request, rsp interface{}) error {
+func (c *HTTPClient) Call(ctx context.Context, service common.URL, req *Request, rsp interface{}) error {
 	// header
-	req := request.(*Request)
 	httpHeader := http.Header{}
 	httpHeader.Set("Content-Type", "application/json")
 	httpHeader.Set("Accept", "application/json")
 
 	reqTimeout := c.options.HTTPTimeout
-	if service.Timeout() != 0 && service.Timeout() < reqTimeout {
-		reqTimeout = time.Duration(service.Timeout())
-	}
 	if reqTimeout <= 0 {
 		reqTimeout = 1e8
 	}
 	httpHeader.Set("Timeout", reqTimeout.String())
-	if md, ok := ctx.Value(public.DUBBOGO_CTX_KEY).(map[string]string); ok {
+	if md, ok := ctx.Value(constant.DUBBOGO_CTX_KEY).(map[string]string); ok {
 		for k := range md {
 			httpHeader.Set(k, md[k])
 		}
@@ -127,15 +130,15 @@ func (c *HTTPClient) Call(ctx context.Context, service registry.ServiceURL, requ
 	}
 	reqBody, err := codec.Write(&codecData)
 	if err != nil {
-		return jerrors.Trace(err)
+		return perrors.WithStack(err)
 	}
 
-	rspBody, err := c.Do(service.Location(), service.Query().Get("interface"), httpHeader, reqBody)
+	rspBody, err := c.Do(service.Location, service.Params.Get("interface"), httpHeader, reqBody)
 	if err != nil {
-		return jerrors.Trace(err)
+		return perrors.WithStack(err)
 	}
 
-	return jerrors.Trace(codec.Read(rspBody, rsp))
+	return perrors.WithStack(codec.Read(rspBody, rsp))
 }
 
 // !!The high level of complexity and the likelihood that the fasthttp client has not been extensively used
@@ -145,19 +148,19 @@ func (c *HTTPClient) Do(addr, path string, httpHeader http.Header, body []byte)
 	u := url.URL{Host: strings.TrimSuffix(addr, ":"), Path: path}
 	httpReq, err := http.NewRequest("POST", u.String(), bytes.NewBuffer(body))
 	if err != nil {
-		return nil, jerrors.Trace(err)
+		return nil, perrors.WithStack(err)
 	}
 	httpReq.Header = httpHeader
 	httpReq.Close = true
 
 	reqBuf := bytes.NewBuffer(make([]byte, 0))
 	if err := httpReq.Write(reqBuf); err != nil {
-		return nil, jerrors.Trace(err)
+		return nil, perrors.WithStack(err)
 	}
 
 	tcpConn, err := net.DialTimeout("tcp", addr, c.options.HandshakeTimeout)
 	if err != nil {
-		return nil, jerrors.Trace(err)
+		return nil, perrors.WithStack(err)
 	}
 	defer tcpConn.Close()
 	setNetConnTimeout := func(conn net.Conn, timeout time.Duration) {
@@ -166,27 +169,27 @@ func (c *HTTPClient) Do(addr, path string, httpHeader http.Header, body []byte)
 			t = time.Now().Add(timeout)
 		}
 
-		conn.SetReadDeadline(t)
+		conn.SetDeadline(t)
 	}
 	setNetConnTimeout(tcpConn, c.options.HTTPTimeout)
 
 	if _, err := reqBuf.WriteTo(tcpConn); err != nil {
-		return nil, jerrors.Trace(err)
+		return nil, perrors.WithStack(err)
 	}
 
 	httpRsp, err := http.ReadResponse(bufio.NewReader(tcpConn), httpReq)
 	if err != nil {
-		return nil, jerrors.Trace(err)
+		return nil, perrors.WithStack(err)
 	}
 	defer httpRsp.Body.Close()
 
 	b, err := ioutil.ReadAll(httpRsp.Body)
 	if err != nil {
-		return nil, jerrors.Trace(err)
+		return nil, perrors.WithStack(err)
 	}
 
 	if httpRsp.StatusCode != http.StatusOK {
-		return nil, jerrors.New(fmt.Sprintf("http status:%q, error string:%q", httpRsp.Status, string(b)))
+		return nil, perrors.New(fmt.Sprintf("http status:%q, error string:%q", httpRsp.Status, string(b)))
 	}
 
 	return b, nil
diff --git a/protocol/jsonrpc/http_test.go b/protocol/jsonrpc/http_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..55f86f454e89bc16f409abfa141508207b749279
--- /dev/null
+++ b/protocol/jsonrpc/http_test.go
@@ -0,0 +1,161 @@
+// Copyright 2016-2019 Yincheng Fang
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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 jsonrpc
+
+import (
+	"context"
+	"strings"
+	"testing"
+	"time"
+)
+
+import (
+	perrors "github.com/pkg/errors"
+	"github.com/stretchr/testify/assert"
+)
+
+import (
+	"github.com/dubbo/go-for-apache-dubbo/common"
+	"github.com/dubbo/go-for-apache-dubbo/common/constant"
+	"github.com/dubbo/go-for-apache-dubbo/protocol"
+)
+
+type (
+	User struct {
+		Id   string `json:"id"`
+		Name string `json:"name"`
+	}
+
+	UserProvider struct {
+		user map[string]User
+	}
+)
+
+func TestHTTPClient_Call(t *testing.T) {
+
+	methods, err := common.ServiceMap.Register("jsonrpc", &UserProvider{})
+	assert.NoError(t, err)
+	assert.Equal(t, "GetUser,GetUser0,GetUser1,GetUser2,GetUser3", methods)
+
+	// Export
+	proto := GetProtocol()
+	url, err := common.NewURL(context.Background(), "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&"+
+		"side=provider&timeout=3000&timestamp=1556509797245")
+	assert.NoError(t, err)
+	proto.Export(protocol.NewBaseInvoker(url))
+	time.Sleep(time.Second * 2)
+
+	client := NewHTTPClient(&HTTPOptions{})
+
+	// call GetUser
+	ctx := context.WithValue(context.Background(), constant.DUBBOGO_CTX_KEY, map[string]string{
+		"X-Proxy-Id": "dubbogo",
+		"X-Services": url.Path,
+		"X-Method":   "GetUser",
+	})
+	req := client.NewRequest(url, "GetUser", []interface{}{"1", "username"})
+	reply := &User{}
+	err = client.Call(ctx, url, req, reply)
+	assert.NoError(t, err)
+	assert.Equal(t, "1", reply.Id)
+	assert.Equal(t, "username", reply.Name)
+
+	// call GetUser0
+	ctx = context.WithValue(context.Background(), constant.DUBBOGO_CTX_KEY, map[string]string{
+		"X-Proxy-Id": "dubbogo",
+		"X-Services": url.Path,
+		"X-Method":   "GetUser",
+	})
+	req = client.NewRequest(url, "GetUser0", []interface{}{"1", "username"})
+	reply = &User{}
+	err = client.Call(ctx, url, req, reply)
+	assert.NoError(t, err)
+	assert.Equal(t, "1", reply.Id)
+	assert.Equal(t, "username", reply.Name)
+
+	// call GetUser1
+	ctx = context.WithValue(context.Background(), constant.DUBBOGO_CTX_KEY, map[string]string{
+		"X-Proxy-Id": "dubbogo",
+		"X-Services": url.Path,
+		"X-Method":   "GetUser1",
+	})
+	req = client.NewRequest(url, "GetUser1", []interface{}{""})
+	reply = &User{}
+	err = client.Call(ctx, url, req, reply)
+	assert.True(t, strings.Contains(err.Error(), "500 Internal Server Error"))
+	assert.True(t, strings.Contains(err.Error(), "\\\"result\\\":{},\\\"error\\\":{\\\"code\\\":-32000,\\\"message\\\":\\\"error\\\"}"))
+
+	// call GetUser2
+	ctx = context.WithValue(context.Background(), constant.DUBBOGO_CTX_KEY, map[string]string{
+		"X-Proxy-Id": "dubbogo",
+		"X-Services": url.Path,
+		"X-Method":   "GetUser",
+	})
+	req = client.NewRequest(url, "GetUser2", []interface{}{"1", "username"})
+	reply1 := []User{}
+	err = client.Call(ctx, url, req, &reply1)
+	assert.NoError(t, err)
+	assert.Equal(t, User{Id: "1", Name: "username"}, reply1[0])
+
+	// call GetUser3
+	ctx = context.WithValue(context.Background(), constant.DUBBOGO_CTX_KEY, map[string]string{
+		"X-Proxy-Id": "dubbogo",
+		"X-Services": url.Path,
+		"X-Method":   "GetUser",
+	})
+	req = client.NewRequest(url, "GetUser3", []interface{}{"1", "username"})
+	reply1 = []User{}
+	err = client.Call(ctx, url, req, &reply1)
+	assert.NoError(t, err)
+	assert.Equal(t, User{Id: "1", Name: "username"}, reply1[0])
+
+	// destroy
+	proto.Destroy()
+
+}
+
+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, name string) (User, error) {
+	return User{Id: id, Name: name}, nil
+}
+
+func (u *UserProvider) GetUser1(ctx context.Context, req []interface{}, rsp *User) error {
+	return perrors.New("error")
+}
+
+func (u *UserProvider) GetUser2(ctx context.Context, req []interface{}, rsp *[]User) error {
+	*rsp = append(*rsp, User{Id: req[0].(string), Name: req[1].(string)})
+	return nil
+}
+
+func (u *UserProvider) GetUser3(ctx context.Context, req []interface{}) ([]User, error) {
+	return []User{{Id: req[0].(string), Name: req[1].(string)}}, nil
+}
+
+func (u *UserProvider) Service() string {
+	return "com.ikurento.user.UserProvider"
+}
+
+func (u *UserProvider) Version() string {
+	return ""
+}
diff --git a/jsonrpc/json.go b/protocol/jsonrpc/json.go
similarity index 82%
rename from jsonrpc/json.go
rename to protocol/jsonrpc/json.go
index 8ff48882f46bbaa303ca388fe6ef5c2aa7c481e7..c5c26d3f93172f563bb89642ef0664c904c3e4e8 100644
--- a/jsonrpc/json.go
+++ b/protocol/jsonrpc/json.go
@@ -1,25 +1,16 @@
-// Copyright (c) 2016 ~ 2018, Alex Stocks.
-// Copyright (c) 2015 Alex Efros.
+// Copyright 2016-2019 Alex Stocks
 //
-// The MIT License (MIT)
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
 //
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
+// http://www.apache.org/licenses/LICENSE-2.0
 //
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
+// 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 jsonrpc
 
@@ -33,7 +24,7 @@ import (
 )
 
 import (
-	jerrors "github.com/juju/errors"
+	perrors "github.com/pkg/errors"
 )
 
 const (
@@ -149,10 +140,10 @@ func (c *jsonClientCodec) Write(d *CodecData) ([]byte, error) {
 				}
 			case reflect.Array, reflect.Struct:
 			default:
-				return nil, jerrors.New("unsupported param type: Ptr to " + k.String())
+				return nil, perrors.New("unsupported param type: Ptr to " + k.String())
 			}
 		default:
-			return nil, jerrors.New("unsupported param type: " + k.String())
+			return nil, perrors.New("unsupported param type: " + k.String())
 		}
 	}
 
@@ -167,7 +158,7 @@ func (c *jsonClientCodec) Write(d *CodecData) ([]byte, error) {
 	defer buf.Reset()
 	enc := json.NewEncoder(buf)
 	if err := enc.Encode(&c.req); err != nil {
-		return nil, jerrors.Trace(err)
+		return nil, perrors.WithStack(err)
 	}
 
 	return buf.Bytes(), nil
@@ -181,24 +172,24 @@ func (c *jsonClientCodec) Read(streamBytes []byte, x interface{}) error {
 	dec := json.NewDecoder(buf)
 	if err := dec.Decode(&c.rsp); err != nil {
 		if err != io.EOF {
-			err = jerrors.Trace(err)
+			err = perrors.WithStack(err)
 		}
 		return err
 	}
 
 	_, ok := c.pending[c.rsp.ID]
 	if !ok {
-		err := jerrors.Errorf("can not find method of rsponse id %v, rsponse error:%v", c.rsp.ID, c.rsp.Error)
+		err := perrors.Errorf("can not find method of rsponse id %v, rsponse error:%v", c.rsp.ID, c.rsp.Error)
 		return err
 	}
 	delete(c.pending, c.rsp.ID)
 
 	// c.rsp.ID
 	if c.rsp.Error != nil {
-		return jerrors.New(c.rsp.Error.Error())
+		return perrors.New(c.rsp.Error.Error())
 	}
 
-	return jerrors.Trace(json.Unmarshal(*c.rsp.Result, x))
+	return perrors.WithStack(json.Unmarshal(*c.rsp.Result, x))
 }
 
 //////////////////////////////////////////
@@ -230,32 +221,32 @@ func (r *serverRequest) UnmarshalJSON(raw []byte) error {
 	// Attention: if do not define a new struct named @req, the json.Unmarshal will invoke
 	// (*serverRequest)UnmarshalJSON recursively.
 	if err := json.Unmarshal(raw, req(r)); err != nil {
-		return jerrors.New("bad request")
+		return perrors.New("bad request")
 	}
 
 	var o = make(map[string]*json.RawMessage)
 	if err := json.Unmarshal(raw, &o); err != nil {
-		return jerrors.New("bad request")
+		return perrors.New("bad request")
 	}
 	if o["jsonrpc"] == nil || o["method"] == nil {
-		return jerrors.New("bad request")
+		return perrors.New("bad request")
 	}
 	_, okID := o["id"]
 	_, okParams := o["params"]
 	if len(o) == 3 && !(okID || okParams) || len(o) == 4 && !(okID && okParams) || len(o) > 4 {
-		return jerrors.New("bad request")
+		return perrors.New("bad request")
 	}
 	if r.Version != Version {
-		return jerrors.New("bad request")
+		return perrors.New("bad request")
 	}
 	if okParams {
 		if r.Params == nil || len(*r.Params) == 0 {
-			return jerrors.New("bad request")
+			return perrors.New("bad request")
 		}
 		switch []byte(*r.Params)[0] {
 		case '[', '{':
 		default:
-			return jerrors.New("bad request")
+			return perrors.New("bad request")
 		}
 	}
 	if okID && r.ID == nil {
@@ -263,11 +254,11 @@ func (r *serverRequest) UnmarshalJSON(raw []byte) error {
 	}
 	if okID {
 		if len(*r.ID) == 0 {
-			return jerrors.New("bad request")
+			return perrors.New("bad request")
 		}
 		switch []byte(*r.ID)[0] {
 		case 't', 'f', '{', '[':
-			return jerrors.New("bad request")
+			return perrors.New("bad request")
 		}
 	}
 
@@ -403,7 +394,7 @@ func (c *ServerCodec) Write(errMsg string, x interface{}) ([]byte, error) {
 	defer buf.Reset()
 	enc := json.NewEncoder(buf)
 	if err := enc.Encode(resp); err != nil {
-		return nil, jerrors.Trace(err)
+		return nil, perrors.WithStack(err)
 	}
 
 	return buf.Bytes(), nil
diff --git a/protocol/jsonrpc/json_test.go b/protocol/jsonrpc/json_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..c821cc25f1ff93d8af4fd4cf7d405a6ad798a2ff
--- /dev/null
+++ b/protocol/jsonrpc/json_test.go
@@ -0,0 +1,92 @@
+// Copyright 2016-2019 Yincheng Fang
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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 jsonrpc
+
+import (
+	"encoding/json"
+	"testing"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+type TestData struct {
+	Test string
+}
+
+func TestJsonClientCodec_Write(t *testing.T) {
+	cd := &CodecData{
+		ID:     1,
+		Method: "GetUser",
+		Args:   []interface{}{"args", 2},
+	}
+	codec := newJsonClientCodec()
+	data, err := codec.Write(cd)
+	assert.NoError(t, err)
+	assert.Equal(t, "{\"jsonrpc\":\"2.0\",\"method\":\"GetUser\",\"params\":[\"args\",2],\"id\":1}\n", string(data))
+
+	cd.Args = 1
+	data, err = codec.Write(cd)
+	assert.EqualError(t, err, "unsupported param type: int")
+}
+
+func TestJsonClientCodec_Read(t *testing.T) {
+	codec := newJsonClientCodec()
+	codec.pending[1] = "GetUser"
+	rsp := &TestData{}
+	err := codec.Read([]byte("{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":{\"Test\":\"test\"}}\n"), rsp)
+	assert.NoError(t, err)
+	assert.Equal(t, "test", rsp.Test)
+
+	//error
+	codec.pending[1] = "GetUser"
+	err = codec.Read([]byte("{\"jsonrpc\":\"2.0\",\"id\":1,\"error\":{\"code\":-32000,\"message\":\"error\"}}\n"), rsp)
+	assert.EqualError(t, err, "{\"code\":-32000,\"message\":\"error\"}")
+}
+
+func TestServerCodec_Write(t *testing.T) {
+	codec := newServerCodec()
+	a := json.RawMessage([]byte("1"))
+	codec.req = serverRequest{Version: "1.0", Method: "GetUser", ID: &a}
+	data, err := codec.Write("error", &TestData{Test: "test"})
+	assert.NoError(t, err)
+	assert.Equal(t, "{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":{\"Test\":\"test\"},\"error\":{\"code\":-32000,\"message\":\"error\"}}\n", string(data))
+
+	data, err = codec.Write("{\"code\":-32000,\"message\":\"error\"}", &TestData{Test: "test"})
+	assert.NoError(t, err)
+	assert.Equal(t, "{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":{\"Test\":\"test\"},\"error\":{\"code\":-32000,\"message\":\"error\"}}\n", string(data))
+}
+
+func TestServerCodec_Read(t *testing.T) {
+	codec := newServerCodec()
+	header := map[string]string{}
+	err := codec.ReadHeader(header, []byte("{\"jsonrpc\":\"2.0\",\"method\":\"GetUser\",\"params\":[\"args\",2],\"id\":1}\n"))
+	assert.EqualError(t, err, "{\"code\":-32601,\"message\":\"Method not found\"}")
+
+	header["HttpMethod"] = "POST"
+	err = codec.ReadHeader(header, []byte("{\"jsonrpc\":\"2.0\",\"method\":\"GetUser\",\"params\":[\"args\",2],\"id\":1}\n"))
+	assert.NoError(t, err)
+	assert.Equal(t, "1", string([]byte(*codec.req.ID)))
+	assert.Equal(t, "GetUser", codec.req.Method)
+	assert.Equal(t, "2.0", codec.req.Version)
+	assert.Equal(t, "[\"args\",2]", string([]byte(*codec.req.Params)))
+
+	req := []interface{}{}
+	err = codec.ReadBody(&req)
+	assert.NoError(t, err)
+	assert.Equal(t, "args", req[0])
+	assert.Equal(t, float64(2), req[1])
+}
diff --git a/protocol/jsonrpc/jsonrpc_exporter.go b/protocol/jsonrpc/jsonrpc_exporter.go
new file mode 100644
index 0000000000000000000000000000000000000000..144437c10402962b3356706b88f1e2701524a97b
--- /dev/null
+++ b/protocol/jsonrpc/jsonrpc_exporter.go
@@ -0,0 +1,45 @@
+// Copyright 2016-2019 Yincheng Fang
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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 jsonrpc
+
+import (
+	"sync"
+)
+
+import (
+	"github.com/dubbo/go-for-apache-dubbo/common"
+	"github.com/dubbo/go-for-apache-dubbo/common/constant"
+	"github.com/dubbo/go-for-apache-dubbo/common/logger"
+	"github.com/dubbo/go-for-apache-dubbo/protocol"
+)
+
+type JsonrpcExporter struct {
+	protocol.BaseExporter
+}
+
+func NewJsonrpcExporter(key string, invoker protocol.Invoker, exporterMap *sync.Map) *JsonrpcExporter {
+	return &JsonrpcExporter{
+		BaseExporter: *protocol.NewBaseExporter(key, invoker, exporterMap),
+	}
+}
+
+func (je *JsonrpcExporter) Unexport() {
+	service := je.GetInvoker().GetUrl().GetParam(constant.INTERFACE_KEY, "")
+	je.BaseExporter.Unexport()
+	err := common.ServiceMap.UnRegister(JSONRPC, service)
+	if err != nil {
+		logger.Errorf("[JsonrpcExporter.Unexport] error: %v", err)
+	}
+}
diff --git a/protocol/jsonrpc/jsonrpc_invoker.go b/protocol/jsonrpc/jsonrpc_invoker.go
new file mode 100644
index 0000000000000000000000000000000000000000..75ac88085ff396cde95e1ec2a43a7026777569de
--- /dev/null
+++ b/protocol/jsonrpc/jsonrpc_invoker.go
@@ -0,0 +1,62 @@
+// Copyright 2016-2019 Yincheng Fang
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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 jsonrpc
+
+import (
+	"context"
+)
+
+import (
+	"github.com/dubbo/go-for-apache-dubbo/common"
+	"github.com/dubbo/go-for-apache-dubbo/common/constant"
+	"github.com/dubbo/go-for-apache-dubbo/common/logger"
+	"github.com/dubbo/go-for-apache-dubbo/protocol"
+	invocation_impl "github.com/dubbo/go-for-apache-dubbo/protocol/invocation"
+)
+
+type JsonrpcInvoker struct {
+	protocol.BaseInvoker
+	client *HTTPClient
+}
+
+func NewJsonrpcInvoker(url common.URL, client *HTTPClient) *JsonrpcInvoker {
+	return &JsonrpcInvoker{
+		BaseInvoker: *protocol.NewBaseInvoker(url),
+		client:      client,
+	}
+}
+
+func (ji *JsonrpcInvoker) Invoke(invocation protocol.Invocation) protocol.Result {
+
+	var (
+		result protocol.RPCResult
+	)
+
+	inv := invocation.(*invocation_impl.RPCInvocation)
+	url := ji.GetUrl()
+	req := ji.client.NewRequest(url, inv.MethodName(), inv.Arguments())
+	ctx := context.WithValue(context.Background(), constant.DUBBOGO_CTX_KEY, map[string]string{
+		"X-Proxy-Id": "dubbogo",
+		"X-Services": url.Path,
+		"X-Method":   inv.MethodName(),
+	})
+	result.Err = ji.client.Call(ctx, url, req, inv.Reply())
+	if result.Err == nil {
+		result.Rest = inv.Reply()
+	}
+	logger.Debugf("result.Err: %v, result.Rest: %v", result.Err, result.Rest)
+
+	return &result
+}
diff --git a/protocol/jsonrpc/jsonrpc_invoker_test.go b/protocol/jsonrpc/jsonrpc_invoker_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..2d248469e2bb35edb1708901615c444a0d712dd0
--- /dev/null
+++ b/protocol/jsonrpc/jsonrpc_invoker_test.go
@@ -0,0 +1,63 @@
+// Copyright 2016-2019 Yincheng Fang
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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 jsonrpc
+
+import (
+	"context"
+	"testing"
+	"time"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+import (
+	"github.com/dubbo/go-for-apache-dubbo/common"
+	"github.com/dubbo/go-for-apache-dubbo/protocol"
+	"github.com/dubbo/go-for-apache-dubbo/protocol/invocation"
+)
+
+func TestJsonrpcInvoker_Invoke(t *testing.T) {
+
+	methods, err := common.ServiceMap.Register("jsonrpc", &UserProvider{})
+	assert.NoError(t, err)
+	assert.Equal(t, "GetUser,GetUser0,GetUser1,GetUser2,GetUser3", methods)
+
+	// Export
+	proto := GetProtocol()
+	url, err := common.NewURL(context.Background(), "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&"+
+		"side=provider&timeout=3000&timestamp=1556509797245")
+	assert.NoError(t, err)
+	proto.Export(protocol.NewBaseInvoker(url))
+	time.Sleep(time.Second * 2)
+
+	client := NewHTTPClient(&HTTPOptions{
+		HandshakeTimeout: time.Second,
+		HTTPTimeout:      time.Second,
+	})
+
+	jsonInvoker := NewJsonrpcInvoker(url, client)
+	user := &User{}
+	res := jsonInvoker.Invoke(invocation.NewRPCInvocationForConsumer("GetUser", nil, []interface{}{"1", "username"}, user, nil, url, nil))
+	assert.NoError(t, res.Error())
+	assert.Equal(t, User{Id: "1", Name: "username"}, *res.Result().(*User))
+
+	// destroy
+	proto.Destroy()
+}
diff --git a/protocol/jsonrpc/jsonrpc_protocol.go b/protocol/jsonrpc/jsonrpc_protocol.go
new file mode 100644
index 0000000000000000000000000000000000000000..737c9cb175cedaae081abf6a43dfc605fb906d40
--- /dev/null
+++ b/protocol/jsonrpc/jsonrpc_protocol.go
@@ -0,0 +1,95 @@
+// Copyright 2016-2019 Yincheng Fang
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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 jsonrpc
+
+import (
+	"github.com/dubbo/go-for-apache-dubbo/common"
+	"github.com/dubbo/go-for-apache-dubbo/common/extension"
+	"github.com/dubbo/go-for-apache-dubbo/common/logger"
+	"github.com/dubbo/go-for-apache-dubbo/config"
+	"github.com/dubbo/go-for-apache-dubbo/protocol"
+)
+
+const JSONRPC = "jsonrpc"
+
+func init() {
+	extension.SetProtocol(JSONRPC, GetProtocol)
+}
+
+var jsonrpcProtocol *JsonrpcProtocol
+
+type JsonrpcProtocol struct {
+	protocol.BaseProtocol
+	serverMap map[string]*Server
+}
+
+func NewDubboProtocol() *JsonrpcProtocol {
+	return &JsonrpcProtocol{
+		BaseProtocol: protocol.NewBaseProtocol(),
+		serverMap:    make(map[string]*Server),
+	}
+}
+
+func (jp *JsonrpcProtocol) Export(invoker protocol.Invoker) protocol.Exporter {
+	url := invoker.GetUrl()
+	serviceKey := url.Key()
+	exporter := NewJsonrpcExporter(serviceKey, invoker, jp.ExporterMap())
+	jp.SetExporterMap(serviceKey, exporter)
+	logger.Infof("Export service: %s", url.String())
+
+	// start server
+	jp.openServer(url)
+
+	return exporter
+}
+
+func (jp *JsonrpcProtocol) Refer(url common.URL) protocol.Invoker {
+	invoker := NewJsonrpcInvoker(url, NewHTTPClient(&HTTPOptions{
+		HandshakeTimeout: config.GetConsumerConfig().ConnectTimeout,
+		HTTPTimeout:      config.GetConsumerConfig().RequestTimeout,
+	}))
+	jp.SetInvokers(invoker)
+	logger.Infof("Refer service: %s", url.String())
+	return invoker
+}
+
+func (jp *JsonrpcProtocol) Destroy() {
+	logger.Infof("jsonrpcProtocol destroy.")
+
+	jp.BaseProtocol.Destroy()
+
+	// stop server
+	for key, server := range jp.serverMap {
+		delete(jp.serverMap, key)
+		server.Stop()
+	}
+}
+
+func (jp *JsonrpcProtocol) openServer(url common.URL) {
+	exporter, ok := jp.ExporterMap().Load(url.Key())
+	if !ok {
+		panic("[JsonrpcProtocol]" + url.Key() + "is not existing")
+	}
+	srv := NewServer(exporter.(protocol.Exporter))
+	jp.serverMap[url.Location] = srv
+	srv.Start(url)
+}
+
+func GetProtocol() protocol.Protocol {
+	if jsonrpcProtocol != nil {
+		return jsonrpcProtocol
+	}
+	return NewDubboProtocol()
+}
diff --git a/protocol/jsonrpc/jsonrpc_protocol_test.go b/protocol/jsonrpc/jsonrpc_protocol_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..09e43400cdc51431ccd3bcad1389b88f73fac485
--- /dev/null
+++ b/protocol/jsonrpc/jsonrpc_protocol_test.go
@@ -0,0 +1,89 @@
+// Copyright 2016-2019 Yincheng Fang
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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 jsonrpc
+
+import (
+	"context"
+	"testing"
+	"time"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+import (
+	"github.com/dubbo/go-for-apache-dubbo/common"
+	"github.com/dubbo/go-for-apache-dubbo/config"
+	"github.com/dubbo/go-for-apache-dubbo/protocol"
+)
+
+func TestJsonrpcProtocol_Export(t *testing.T) {
+	// Export
+	proto := GetProtocol()
+	url, err := common.NewURL(context.Background(), "jsonrpc://127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&"+
+		"application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&"+
+		"environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&"+
+		"module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&"+
+		"side=provider&timeout=3000&timestamp=1556509797245")
+	assert.NoError(t, err)
+	exporter := proto.Export(protocol.NewBaseInvoker(url))
+
+	// make sure url
+	eq := exporter.GetInvoker().GetUrl().URLEqual(url)
+	assert.True(t, eq)
+
+	// make sure exporterMap after 'Unexport'
+	_, ok := proto.(*JsonrpcProtocol).ExporterMap().Load(url.Key())
+	assert.True(t, ok)
+	exporter.Unexport()
+	_, ok = proto.(*JsonrpcProtocol).ExporterMap().Load(url.Key())
+	assert.False(t, ok)
+
+	// make sure serverMap after 'Destroy'
+	_, ok = proto.(*JsonrpcProtocol).serverMap[url.Location]
+	assert.True(t, ok)
+	proto.Destroy()
+	_, ok = proto.(*JsonrpcProtocol).serverMap[url.Location]
+	assert.False(t, ok)
+}
+
+func TestJsonrpcProtocol_Refer(t *testing.T) {
+	// Refer
+	proto := GetProtocol()
+	url, err := common.NewURL(context.Background(), "jsonrpc://127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&"+
+		"application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&"+
+		"environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&"+
+		"module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&"+
+		"side=provider&timeout=3000&timestamp=1556509797245")
+	assert.NoError(t, err)
+	con := config.ConsumerConfig{
+		ConnectTimeout: 5 * time.Second,
+		RequestTimeout: 5 * time.Second,
+	}
+	config.SetConsumerConfig(con)
+	invoker := proto.Refer(url)
+
+	// make sure url
+	eq := invoker.GetUrl().URLEqual(url)
+	assert.True(t, eq)
+
+	// make sure invokers after 'Destroy'
+	invokersLen := len(proto.(*JsonrpcProtocol).Invokers())
+	assert.Equal(t, 1, invokersLen)
+	proto.Destroy()
+	invokersLen = len(proto.(*JsonrpcProtocol).Invokers())
+	assert.Equal(t, 0, invokersLen)
+}
diff --git a/protocol/jsonrpc/server.go b/protocol/jsonrpc/server.go
new file mode 100644
index 0000000000000000000000000000000000000000..1be5c54d239910c7da7b3fd955512e4120fed4da
--- /dev/null
+++ b/protocol/jsonrpc/server.go
@@ -0,0 +1,435 @@
+// Copyright 2016-2019 Yincheng Fang, Alex Stocks
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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 jsonrpc
+
+import (
+	"bufio"
+	"bytes"
+	"context"
+	"io"
+	"io/ioutil"
+	"net"
+	"net/http"
+	"reflect"
+	"runtime"
+	"runtime/debug"
+	"sync"
+	"time"
+)
+
+import (
+	perrors "github.com/pkg/errors"
+)
+
+import (
+	"github.com/dubbo/go-for-apache-dubbo/common"
+	"github.com/dubbo/go-for-apache-dubbo/common/constant"
+	"github.com/dubbo/go-for-apache-dubbo/common/logger"
+	"github.com/dubbo/go-for-apache-dubbo/protocol"
+	"github.com/dubbo/go-for-apache-dubbo/protocol/invocation"
+)
+
+var (
+	// A value sent as a placeholder for the server's response value when the server
+	// receives an invalid request. It is never decoded by the client since the Response
+	// contains an error when it is used.
+	invalidRequest = struct{}{}
+)
+
+const (
+	DefaultMaxSleepTime      = 1 * time.Second // accept涓棿鏈€澶leep interval
+	DefaultHTTPRspBufferSize = 1024
+	PathPrefix               = byte('/')
+)
+
+type Server struct {
+	exporter protocol.Exporter
+	done     chan struct{}
+	once     sync.Once
+
+	sync.RWMutex
+	wg      sync.WaitGroup
+	timeout time.Duration
+}
+
+func NewServer(exporter protocol.Exporter) *Server {
+	return &Server{
+		exporter: exporter,
+		done:     make(chan struct{}),
+	}
+}
+
+func (s *Server) handlePkg(conn net.Conn) {
+	defer func() {
+		if r := recover(); r != nil {
+			logger.Warnf("connection{local:%v, remote:%v} panic error:%#v, debug stack:%s",
+				conn.LocalAddr(), conn.RemoteAddr(), r, string(debug.Stack()))
+		}
+
+		conn.Close()
+	}()
+
+	setTimeout := func(conn net.Conn, timeout time.Duration) {
+		t := time.Time{}
+		if timeout > time.Duration(0) {
+			t = time.Now().Add(timeout)
+		}
+
+		conn.SetDeadline(t)
+	}
+
+	sendErrorResp := func(header http.Header, body []byte) error {
+		rsp := &http.Response{
+			Header:        header,
+			StatusCode:    500,
+			ContentLength: int64(len(body)),
+			Body:          ioutil.NopCloser(bytes.NewReader(body)),
+		}
+		rsp.Header.Del("Content-Type")
+		rsp.Header.Del("Content-Length")
+		rsp.Header.Del("Timeout")
+
+		rspBuf := bytes.NewBuffer(make([]byte, DefaultHTTPRspBufferSize))
+		rspBuf.Reset()
+		err := rsp.Write(rspBuf)
+		if err != nil {
+			return perrors.WithStack(err)
+		}
+		_, err = rspBuf.WriteTo(conn)
+		return perrors.WithStack(err)
+	}
+
+	for {
+		bufReader := bufio.NewReader(conn)
+		r, err := http.ReadRequest(bufReader)
+		if err != nil {
+			logger.Warnf("[ReadRequest] error: %v", err)
+			return
+		}
+
+		reqBody, err := ioutil.ReadAll(r.Body)
+		if err != nil {
+			return
+		}
+		r.Body.Close()
+
+		reqHeader := make(map[string]string)
+		for k := range r.Header {
+			reqHeader[k] = r.Header.Get(k)
+		}
+		reqHeader["Path"] = r.URL.Path[1:] // to get service name
+		if r.URL.Path[0] != PathPrefix {
+			reqHeader["Path"] = r.URL.Path
+		}
+		reqHeader["HttpMethod"] = r.Method
+
+		httpTimeout := s.timeout
+		contentType := reqHeader["Content-Type"]
+		if contentType != "application/json" && contentType != "application/json-rpc" {
+			setTimeout(conn, httpTimeout)
+			r.Header.Set("Content-Type", "text/plain")
+			if errRsp := sendErrorResp(r.Header, []byte(perrors.WithStack(err).Error())); errRsp != nil {
+				logger.Warnf("sendErrorResp(header:%#v, error:%v) = error:%s",
+					r.Header, perrors.WithStack(err), errRsp)
+			}
+			return
+		}
+
+		ctx := context.Background()
+		if len(reqHeader["Timeout"]) > 0 {
+			timeout, err := time.ParseDuration(reqHeader["Timeout"])
+			if err == nil {
+				httpTimeout = timeout
+				ctx, _ = context.WithTimeout(ctx, httpTimeout)
+			}
+			delete(reqHeader, "Timeout")
+		}
+		setTimeout(conn, httpTimeout)
+
+		if err := serveRequest(ctx, reqHeader, reqBody, conn, s.exporter); err != nil {
+			if errRsp := sendErrorResp(r.Header, []byte(perrors.WithStack(err).Error())); errRsp != nil {
+				logger.Warnf("sendErrorResp(header:%#v, error:%v) = error:%s",
+					r.Header, perrors.WithStack(err), errRsp)
+			}
+
+			logger.Infof("Unexpected error serving request, closing socket: %v", err)
+			return
+		}
+	}
+}
+
+func accept(listener net.Listener, fn func(net.Conn)) error {
+	var (
+		ok       bool
+		ne       net.Error
+		tmpDelay time.Duration
+	)
+
+	for {
+		c, err := listener.Accept()
+		if err != nil {
+			if ne, ok = err.(net.Error); ok && ne.Temporary() {
+				if tmpDelay != 0 {
+					tmpDelay <<= 1
+				} else {
+					tmpDelay = 5 * time.Millisecond
+				}
+				if tmpDelay > DefaultMaxSleepTime {
+					tmpDelay = DefaultMaxSleepTime
+				}
+				logger.Infof("http: Accept error: %v; retrying in %v\n", err, tmpDelay)
+				time.Sleep(tmpDelay)
+				continue
+			}
+			return perrors.WithStack(err)
+		}
+
+		go func() {
+			defer func() {
+				if r := recover(); r != nil {
+					const size = 64 << 10
+					buf := make([]byte, size)
+					buf = buf[:runtime.Stack(buf, false)]
+					logger.Errorf("http: panic serving %v: %v\n%s", c.RemoteAddr(), r, buf)
+					c.Close()
+				}
+			}()
+
+			fn(c)
+		}()
+	}
+}
+
+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)
+		return
+	}
+	logger.Infof("rpc server start to listen on %s", listener.Addr())
+
+	s.wg.Add(1)
+	go func() {
+		accept(listener, func(conn net.Conn) { s.handlePkg(conn) })
+		s.wg.Done()
+	}()
+
+	s.wg.Add(1)
+	go func() { // Server done goroutine
+		var err error
+		<-s.done               // step1: block to wait for done channel(wait Server.Stop step2)
+		err = listener.Close() // step2: and then close listener
+		if err != nil {
+			logger.Warnf("listener{addr:%s}.Close() = error{%#v}", listener.Addr(), err)
+		}
+		s.wg.Done()
+	}()
+}
+
+func (s *Server) Stop() {
+	s.once.Do(func() {
+		close(s.done)
+		s.wg.Wait()
+	})
+}
+
+func serveRequest(ctx context.Context,
+	header map[string]string, body []byte, conn net.Conn, exporter protocol.Exporter) error {
+
+	sendErrorResp := func(header map[string]string, body []byte) error {
+		rsp := &http.Response{
+			Header:        make(http.Header),
+			StatusCode:    500,
+			ContentLength: int64(len(body)),
+			Body:          ioutil.NopCloser(bytes.NewReader(body)),
+		}
+		rsp.Header.Del("Content-Type")
+		rsp.Header.Del("Content-Length")
+		rsp.Header.Del("Timeout")
+		for k, v := range header {
+			rsp.Header.Set(k, v)
+		}
+
+		rspBuf := bytes.NewBuffer(make([]byte, DefaultHTTPRspBufferSize))
+		rspBuf.Reset()
+		err := rsp.Write(rspBuf)
+		if err != nil {
+			return perrors.WithStack(err)
+		}
+		_, err = rspBuf.WriteTo(conn)
+		return perrors.WithStack(err)
+	}
+
+	sendResp := func(header map[string]string, body []byte) error {
+		rsp := &http.Response{
+			Header:        make(http.Header),
+			StatusCode:    200,
+			ContentLength: int64(len(body)),
+			Body:          ioutil.NopCloser(bytes.NewReader(body)),
+		}
+		rsp.Header.Del("Content-Type")
+		rsp.Header.Del("Content-Length")
+		rsp.Header.Del("Timeout")
+		for k, v := range header {
+			rsp.Header.Set(k, v)
+		}
+
+		rspBuf := bytes.NewBuffer(make([]byte, DefaultHTTPRspBufferSize))
+		rspBuf.Reset()
+		err := rsp.Write(rspBuf)
+		if err != nil {
+			return perrors.WithStack(err)
+		}
+		_, err = rspBuf.WriteTo(conn)
+		return perrors.WithStack(err)
+	}
+
+	// read request header
+	codec := newServerCodec()
+	err := codec.ReadHeader(header, body)
+	if err != nil {
+		if err == io.EOF || err == io.ErrUnexpectedEOF {
+			return perrors.WithStack(err)
+		}
+
+		return perrors.New("server cannot decode request: " + err.Error())
+	}
+	serviceName := header["Path"]
+	methodName := codec.req.Method
+	if len(serviceName) == 0 || len(methodName) == 0 {
+		codec.ReadBody(nil)
+		return perrors.New("service/method request ill-formed: " + serviceName + "/" + methodName)
+	}
+
+	// read body
+	var args interface{}
+	if err = codec.ReadBody(&args); err != nil {
+		return perrors.WithStack(err)
+	}
+	logger.Debugf("args: %v", args)
+
+	// exporter invoke
+	invoker := exporter.GetInvoker()
+	if invoker != nil {
+		result := invoker.Invoke(invocation.NewRPCInvocationForProvider(methodName, args.([]interface{}), map[string]string{
+			//attachments[constant.PATH_KEY] = url.Path
+			//attachments[constant.GROUP_KEY] = url.GetParam(constant.GROUP_KEY, "")
+			//attachments[constant.INTERFACE_KEY] = url.GetParam(constant.INTERFACE_KEY, "")
+			constant.VERSION_KEY: codec.req.Version,
+		}))
+		if err := result.Error(); err != nil {
+			if errRsp := sendErrorResp(header, []byte(err.Error())); errRsp != nil {
+				logger.Warnf("Exporter: sendErrorResp(header:%#v, error:%v) = error:%s",
+					header, err, errRsp)
+				return perrors.WithStack(errRsp)
+			}
+		}
+		if res := result.Result(); res != nil {
+			rspStream, err := codec.Write("", res)
+			if err != nil {
+				return perrors.WithStack(err)
+			}
+			if errRsp := sendResp(header, rspStream); errRsp != nil {
+				logger.Warnf("Exporter: sendResp(header:%#v, error:%v) = error:%s",
+					header, err, errRsp)
+				return perrors.WithStack(errRsp)
+			}
+		}
+	}
+
+	// get method
+	svc := common.ServiceMap.GetService(JSONRPC, serviceName)
+	if svc == nil {
+		return perrors.New("cannot find svc " + serviceName)
+	}
+	method := svc.Method()[methodName]
+	if method == nil {
+		return perrors.New("cannot find method " + methodName + " of svc " + serviceName)
+	}
+
+	in := []reflect.Value{svc.Rcvr()}
+	if method.CtxType() != nil {
+		in = append(in, method.SuiteContext(ctx))
+	}
+
+	// prepare argv
+	if (len(method.ArgsType()) == 1 || len(method.ArgsType()) == 2 && method.ReplyType() == nil) && method.ArgsType()[0].String() == "[]interface {}" {
+		in = append(in, reflect.ValueOf(args))
+	} else {
+		for i := 0; i < len(args.([]interface{})); i++ {
+			in = append(in, reflect.ValueOf(args.([]interface{})[i]))
+		}
+	}
+
+	// prepare replyv
+	var replyv reflect.Value
+	if method.ReplyType() == nil {
+		replyv = reflect.New(method.ArgsType()[len(method.ArgsType())-1].Elem())
+		in = append(in, replyv)
+	}
+
+	returnValues := method.Method().Func.Call(in)
+
+	var (
+		retErr interface{}
+		errMsg string
+	)
+	if len(returnValues) == 1 {
+		retErr = returnValues[0].Interface()
+	} else {
+		replyv = returnValues[0]
+		retErr = returnValues[1].Interface()
+	}
+	if retErr != nil {
+		errMsg = retErr.(error).Error()
+	}
+
+	// write response
+	code := 200
+	rspReply := replyv.Interface()
+	if len(errMsg) != 0 {
+		code = 500
+		rspReply = invalidRequest
+	}
+	rspStream, err := codec.Write(errMsg, rspReply)
+	if err != nil {
+		return perrors.WithStack(err)
+	}
+	rsp := &http.Response{
+		StatusCode:    code,
+		ProtoMajor:    1,
+		ProtoMinor:    1,
+		Header:        make(http.Header),
+		ContentLength: int64(len(rspStream)),
+		Body:          ioutil.NopCloser(bytes.NewReader(rspStream)),
+	}
+	delete(header, "Content-Type")
+	delete(header, "Content-Length")
+	delete(header, "Timeout")
+	for k, v := range header {
+		rsp.Header.Set(k, v)
+	}
+
+	rspBuf := bytes.NewBuffer(make([]byte, DefaultHTTPRspBufferSize))
+	rspBuf.Reset()
+	if err = rsp.Write(rspBuf); err != nil {
+		logger.Warnf("rsp.Write(rsp:%#v) = error:%s", rsp, err)
+		return nil
+	}
+	if _, err = rspBuf.WriteTo(conn); err != nil {
+		logger.Warnf("rspBuf.WriteTo(conn:%#v) = error:%s", conn, err)
+	}
+	return nil
+}
diff --git a/protocol/protocol.go b/protocol/protocol.go
new file mode 100644
index 0000000000000000000000000000000000000000..03b2cfbe50c6757a0e86cf4d10385fef97cd8104
--- /dev/null
+++ b/protocol/protocol.go
@@ -0,0 +1,126 @@
+// Copyright 2016-2019 Yincheng Fang
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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 protocol
+
+import (
+	"sync"
+)
+
+import (
+	"github.com/dubbo/go-for-apache-dubbo/common"
+	"github.com/dubbo/go-for-apache-dubbo/common/logger"
+)
+
+// Extension - protocol
+type Protocol interface {
+	Export(invoker Invoker) Exporter
+	Refer(url common.URL) Invoker
+	Destroy()
+}
+
+// wrapping invoker
+type Exporter interface {
+	GetInvoker() Invoker
+	Unexport()
+}
+
+/////////////////////////////
+// base protocol
+/////////////////////////////
+
+type BaseProtocol struct {
+	exporterMap *sync.Map
+	invokers    []Invoker
+}
+
+func NewBaseProtocol() BaseProtocol {
+	return BaseProtocol{
+		exporterMap: new(sync.Map),
+	}
+}
+
+func (bp *BaseProtocol) SetExporterMap(key string, exporter Exporter) {
+	bp.exporterMap.Store(key, exporter)
+}
+
+func (bp *BaseProtocol) ExporterMap() *sync.Map {
+	return bp.exporterMap
+}
+
+func (bp *BaseProtocol) SetInvokers(invoker Invoker) {
+	bp.invokers = append(bp.invokers, invoker)
+}
+
+func (bp *BaseProtocol) Invokers() []Invoker {
+	return bp.invokers
+}
+
+func (bp *BaseProtocol) Export(invoker Invoker) Exporter {
+	return NewBaseExporter("base", invoker, bp.exporterMap)
+}
+
+func (bp *BaseProtocol) Refer(url common.URL) Invoker {
+	return NewBaseInvoker(url)
+}
+
+// Destroy will destroy all invoker and exporter, so it only is called once.
+func (bp *BaseProtocol) Destroy() {
+	// destroy invokers
+	for _, invoker := range bp.invokers {
+		if invoker != nil {
+			invoker.Destroy()
+		}
+	}
+	bp.invokers = []Invoker{}
+
+	// unexport exporters
+	bp.exporterMap.Range(func(key, exporter interface{}) bool {
+		if exporter != nil {
+			exporter.(Exporter).Unexport()
+		} else {
+			bp.exporterMap.Delete(key)
+		}
+		return true
+	})
+}
+
+/////////////////////////////
+// base exporter
+/////////////////////////////
+
+type BaseExporter struct {
+	key         string
+	invoker     Invoker
+	exporterMap *sync.Map
+}
+
+func NewBaseExporter(key string, invoker Invoker, exporterMap *sync.Map) *BaseExporter {
+	return &BaseExporter{
+		key:         key,
+		invoker:     invoker,
+		exporterMap: exporterMap,
+	}
+}
+
+func (de *BaseExporter) GetInvoker() Invoker {
+	return de.invoker
+
+}
+
+func (de *BaseExporter) Unexport() {
+	logger.Infof("Exporter unexport.")
+	de.invoker.Destroy()
+	de.exporterMap.Delete(de.key)
+}
diff --git a/protocol/protocolwrapper/mock_protocol_filter.go b/protocol/protocolwrapper/mock_protocol_filter.go
new file mode 100644
index 0000000000000000000000000000000000000000..902ccefaa974a003b0eaf45c35d65c5c930b81f7
--- /dev/null
+++ b/protocol/protocolwrapper/mock_protocol_filter.go
@@ -0,0 +1,42 @@
+// Copyright 2016-2019 hxmhlt
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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 protocolwrapper
+
+import (
+	"sync"
+)
+import (
+	"github.com/dubbo/go-for-apache-dubbo/common"
+	"github.com/dubbo/go-for-apache-dubbo/protocol"
+)
+
+type mockProtocolFilter struct {
+}
+
+func NewMockProtocolFilter() protocol.Protocol {
+	return &mockProtocolFilter{}
+}
+
+func (pfw *mockProtocolFilter) Export(invoker protocol.Invoker) protocol.Exporter {
+	return protocol.NewBaseExporter("key", invoker, &sync.Map{})
+}
+
+func (pfw *mockProtocolFilter) Refer(url common.URL) protocol.Invoker {
+	return protocol.NewBaseInvoker(url)
+}
+
+func (pfw *mockProtocolFilter) Destroy() {
+
+}
diff --git a/protocol/protocolwrapper/protocol_filter_wrapper.go b/protocol/protocolwrapper/protocol_filter_wrapper.go
new file mode 100644
index 0000000000000000000000000000000000000000..e7cb227d51215b7a0bfc8e56fde86f1a5bf18777
--- /dev/null
+++ b/protocol/protocolwrapper/protocol_filter_wrapper.go
@@ -0,0 +1,109 @@
+// Copyright 2016-2019 Yincheng Fang
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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 protocolwrapper
+
+import (
+	"github.com/dubbo/go-for-apache-dubbo/filter"
+	"strings"
+)
+
+import (
+	"github.com/dubbo/go-for-apache-dubbo/common"
+	"github.com/dubbo/go-for-apache-dubbo/common/constant"
+	"github.com/dubbo/go-for-apache-dubbo/common/extension"
+	"github.com/dubbo/go-for-apache-dubbo/protocol"
+)
+
+const (
+	FILTER = "filter"
+)
+
+func init() {
+	extension.SetProtocol(FILTER, GetProtocol)
+}
+
+// protocol in url decide who ProtocolFilterWrapper.protocol is
+type ProtocolFilterWrapper struct {
+	protocol protocol.Protocol
+}
+
+func (pfw *ProtocolFilterWrapper) Export(invoker protocol.Invoker) protocol.Exporter {
+	if pfw.protocol == nil {
+		pfw.protocol = extension.GetProtocol(invoker.GetUrl().Protocol)
+	}
+	invoker = buildInvokerChain(invoker, constant.SERVICE_FILTER_KEY)
+	return pfw.protocol.Export(invoker)
+}
+
+func (pfw *ProtocolFilterWrapper) Refer(url common.URL) protocol.Invoker {
+	if pfw.protocol == nil {
+		pfw.protocol = extension.GetProtocol(url.Protocol)
+	}
+	return buildInvokerChain(pfw.protocol.Refer(url), constant.REFERENCE_FILTER_KEY)
+}
+
+func (pfw *ProtocolFilterWrapper) Destroy() {
+	pfw.protocol.Destroy()
+}
+
+func buildInvokerChain(invoker protocol.Invoker, key string) protocol.Invoker {
+	filtName := invoker.GetUrl().Params.Get(key)
+	if filtName == "" {
+		return invoker
+	}
+	filtNames := strings.Split(filtName, ",")
+	next := invoker
+
+	// The order of filters is from left to right, so loading from right to left
+
+	for i := len(filtNames) - 1; i >= 0; i-- {
+		filter := extension.GetFilter(filtNames[i])
+		fi := &FilterInvoker{next: next, invoker: invoker, filter: filter}
+		next = fi
+	}
+
+	return next
+}
+
+func GetProtocol() protocol.Protocol {
+	return &ProtocolFilterWrapper{}
+}
+
+///////////////////////////
+// filter invoker
+///////////////////////////
+
+type FilterInvoker struct {
+	next    protocol.Invoker
+	invoker protocol.Invoker
+	filter  filter.Filter
+}
+
+func (fi *FilterInvoker) GetUrl() common.URL {
+	return fi.invoker.GetUrl()
+}
+
+func (fi *FilterInvoker) IsAvailable() bool {
+	return fi.invoker.IsAvailable()
+}
+
+func (fi *FilterInvoker) Invoke(invocation protocol.Invocation) protocol.Result {
+	result := fi.filter.Invoke(fi.next, invocation)
+	return fi.filter.OnResponse(result, fi.invoker, invocation)
+}
+
+func (fi *FilterInvoker) Destroy() {
+	fi.invoker.Destroy()
+}
diff --git a/protocol/protocolwrapper/protocol_filter_wrapper_test.go b/protocol/protocolwrapper/protocol_filter_wrapper_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..49015cdc4b0ce813f7d46d34b3e8ff2a52db80b9
--- /dev/null
+++ b/protocol/protocolwrapper/protocol_filter_wrapper_test.go
@@ -0,0 +1,56 @@
+// Copyright 2016-2019 Yincheng Fang
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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 protocolwrapper
+
+import (
+	"net/url"
+	"testing"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+import (
+	"github.com/dubbo/go-for-apache-dubbo/common"
+	"github.com/dubbo/go-for-apache-dubbo/common/constant"
+	"github.com/dubbo/go-for-apache-dubbo/common/extension"
+	"github.com/dubbo/go-for-apache-dubbo/filter/impl"
+	"github.com/dubbo/go-for-apache-dubbo/protocol"
+)
+
+func TestProtocolFilterWrapper_Export(t *testing.T) {
+	filtProto := extension.GetProtocol(FILTER)
+	filtProto.(*ProtocolFilterWrapper).protocol = &protocol.BaseProtocol{}
+
+	u := common.NewURLWithOptions("Service",
+		common.WithParams(url.Values{}),
+		common.WithParamsValue(constant.SERVICE_FILTER_KEY, impl.ECHO))
+	exporter := filtProto.Export(protocol.NewBaseInvoker(*u))
+	_, ok := exporter.GetInvoker().(*FilterInvoker)
+	assert.True(t, ok)
+}
+
+func TestProtocolFilterWrapper_Refer(t *testing.T) {
+	filtProto := extension.GetProtocol(FILTER)
+	filtProto.(*ProtocolFilterWrapper).protocol = &protocol.BaseProtocol{}
+
+	u := common.NewURLWithOptions("Service",
+		common.WithParams(url.Values{}),
+		common.WithParamsValue(constant.REFERENCE_FILTER_KEY, impl.ECHO))
+	invoker := filtProto.Refer(*u)
+	_, ok := invoker.(*FilterInvoker)
+	assert.True(t, ok)
+}
diff --git a/protocol/result.go b/protocol/result.go
new file mode 100644
index 0000000000000000000000000000000000000000..adcce8a5ec04ddd390d6eeea365fe9fe2553e123
--- /dev/null
+++ b/protocol/result.go
@@ -0,0 +1,37 @@
+// Copyright 2016-2019 Yincheng Fang
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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 protocol
+
+type Result interface {
+	Error() error
+	Result() interface{}
+}
+
+/////////////////////////////
+// Result Impletment of RPC
+/////////////////////////////
+
+type RPCResult struct {
+	Err  error
+	Rest interface{}
+}
+
+func (r *RPCResult) Error() error {
+	return r.Err
+}
+
+func (r *RPCResult) Result() interface{} {
+	return r.Rest
+}
diff --git a/public/codec.go b/public/codec.go
deleted file mode 100644
index e88b9829c577dca11a7a3ffdc2e4d97536d45035..0000000000000000000000000000000000000000
--- a/public/codec.go
+++ /dev/null
@@ -1,44 +0,0 @@
-package public
-
-//////////////////////////////////////////
-// codec type
-//////////////////////////////////////////
-
-type CodecType int
-
-const (
-	CODECTYPE_UNKNOWN CodecType = iota
-	CODECTYPE_JSONRPC
-	CODECTYPE_DUBBO
-)
-
-var codecTypeStrings = [...]string{
-	"unknown",
-	"jsonrpc",
-	"dubbo",
-}
-
-func (c CodecType) String() string {
-	typ := CODECTYPE_UNKNOWN
-	switch c {
-	case CODECTYPE_JSONRPC:
-		typ = c
-	case CODECTYPE_DUBBO:
-		typ = c
-	}
-
-	return codecTypeStrings[typ]
-}
-
-func GetCodecType(t string) CodecType {
-	var typ = CODECTYPE_UNKNOWN
-
-	switch t {
-	case codecTypeStrings[CODECTYPE_JSONRPC]:
-		typ = CODECTYPE_JSONRPC
-	case codecTypeStrings[CODECTYPE_DUBBO]:
-		typ = CODECTYPE_DUBBO
-	}
-
-	return typ
-}
diff --git a/public/const.go b/public/const.go
deleted file mode 100644
index d7f385ecf4530ebc2174c7162a44a88e65106319..0000000000000000000000000000000000000000
--- a/public/const.go
+++ /dev/null
@@ -1,5 +0,0 @@
-package public
-
-const (
-	DUBBOGO_CTX_KEY = "dubbogo-ctx"
-)
diff --git a/registry/directory/directory.go b/registry/directory/directory.go
new file mode 100644
index 0000000000000000000000000000000000000000..c86bc2697edee6686a2e6af33c3d0554d5439212
--- /dev/null
+++ b/registry/directory/directory.go
@@ -0,0 +1,276 @@
+// Copyright 2016-2019 hxmhlt
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package directory
+
+import (
+	"sync"
+	"time"
+)
+
+import (
+	"github.com/dubbo/go-for-apache-dubbo/common/logger"
+	perrors "github.com/pkg/errors"
+)
+
+import (
+	"github.com/dubbo/go-for-apache-dubbo/cluster/directory"
+	"github.com/dubbo/go-for-apache-dubbo/common"
+	"github.com/dubbo/go-for-apache-dubbo/common/constant"
+	"github.com/dubbo/go-for-apache-dubbo/common/extension"
+	"github.com/dubbo/go-for-apache-dubbo/protocol"
+	"github.com/dubbo/go-for-apache-dubbo/protocol/protocolwrapper"
+	"github.com/dubbo/go-for-apache-dubbo/registry"
+)
+
+const RegistryConnDelay = 3
+
+type Options struct {
+	serviceTTL time.Duration
+}
+type Option func(*Options)
+
+type registryDirectory struct {
+	directory.BaseDirectory
+	cacheInvokers    []protocol.Invoker
+	listenerLock     sync.Mutex
+	serviceType      string
+	registry         registry.Registry
+	cacheInvokersMap *sync.Map //use sync.map
+	//cacheInvokersMap map[string]protocol.Invoker
+	Options
+}
+
+func NewRegistryDirectory(url *common.URL, registry registry.Registry, opts ...Option) (*registryDirectory, error) {
+	options := Options{
+		//default 300s
+		serviceTTL: time.Duration(300e9),
+	}
+	for _, opt := range opts {
+		opt(&options)
+	}
+	if url.SubURL == nil {
+		return nil, perrors.Errorf("url is invalid, suburl can not be nil")
+	}
+	return &registryDirectory{
+		BaseDirectory:    directory.NewBaseDirectory(url),
+		cacheInvokers:    []protocol.Invoker{},
+		cacheInvokersMap: &sync.Map{},
+		serviceType:      url.SubURL.Service(),
+		registry:         registry,
+		Options:          options,
+	}, nil
+}
+
+//subscibe from registry
+func (dir *registryDirectory) Subscribe(url common.URL) {
+	for {
+		if !dir.registry.IsAvailable() {
+			logger.Warnf("event listener game over.")
+			return
+		}
+
+		listener, err := dir.registry.Subscribe(url)
+		if err != nil {
+			if !dir.registry.IsAvailable() {
+				logger.Warnf("event listener game over.")
+				return
+			}
+			logger.Warnf("getListener() = err:%v", perrors.WithStack(err))
+			time.Sleep(time.Duration(RegistryConnDelay) * time.Second)
+			continue
+		}
+
+		for {
+			if serviceEvent, err := listener.Next(); err != nil {
+				logger.Warnf("Selector.watch() = error{%v}", perrors.WithStack(err))
+				listener.Close()
+				time.Sleep(time.Duration(RegistryConnDelay) * time.Second)
+				return
+			} else {
+				logger.Infof("update begin, service event: %v", serviceEvent.String())
+				go dir.update(serviceEvent)
+			}
+
+		}
+
+	}
+}
+
+//subscribe service from registry , and update the cacheServices
+func (dir *registryDirectory) update(res *registry.ServiceEvent) {
+	if res == nil {
+		return
+	}
+
+	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) {
+
+	switch res.Action {
+	case registry.ServiceAdd:
+		//dir.cacheService.Add(res.Path, dir.serviceTTL)
+		dir.cacheInvoker(res.Service)
+	case registry.ServiceDel:
+		//dir.cacheService.Del(res.Path, dir.serviceTTL)
+		dir.uncacheInvoker(res.Service)
+		logger.Infof("selector delete service url{%s}", res.Service)
+	default:
+		return
+	}
+
+	newInvokers := dir.toGroupInvokers()
+
+	dir.listenerLock.Lock()
+	defer dir.listenerLock.Unlock()
+	dir.cacheInvokers = newInvokers
+}
+
+func (dir *registryDirectory) toGroupInvokers() []protocol.Invoker {
+
+	newInvokersList := []protocol.Invoker{}
+	groupInvokersMap := make(map[string][]protocol.Invoker)
+	groupInvokersList := []protocol.Invoker{}
+
+	dir.cacheInvokersMap.Range(func(key, value interface{}) bool {
+		newInvokersList = append(newInvokersList, value.(protocol.Invoker))
+		return true
+	})
+
+	for _, invoker := range newInvokersList {
+		group := invoker.GetUrl().GetParam(constant.GROUP_KEY, "")
+
+		if _, ok := groupInvokersMap[group]; ok {
+			groupInvokersMap[group] = append(groupInvokersMap[group], invoker)
+		} else {
+			groupInvokersMap[group] = []protocol.Invoker{invoker}
+		}
+	}
+	if len(groupInvokersMap) == 1 {
+		//len is 1 it means no group setting ,so do not need cluster again
+		groupInvokersList = groupInvokersMap[""]
+	} else {
+		for _, invokers := range groupInvokersMap {
+			staticDir := directory.NewStaticDirectory(invokers)
+			cluster := extension.GetCluster(dir.GetUrl().SubURL.GetParam(constant.CLUSTER_KEY, constant.DEFAULT_CLUSTER))
+			groupInvokersList = append(groupInvokersList, cluster.Join(staticDir))
+		}
+	}
+
+	return groupInvokersList
+}
+
+func (dir *registryDirectory) uncacheInvoker(url common.URL) {
+	logger.Debugf("service will be deleted in cache invokers: invokers key is  %s!", url.Key())
+	dir.cacheInvokersMap.Delete(url.Key())
+}
+
+func (dir *registryDirectory) cacheInvoker(url common.URL) {
+	referenceUrl := dir.GetUrl().SubURL
+	//check the url's protocol is equal to the protocol which is configured in reference config or referenceUrl is not care about protocol
+	if url.Protocol == referenceUrl.Protocol || referenceUrl.Protocol == "" {
+		url = mergeUrl(url, referenceUrl)
+
+		if _, ok := dir.cacheInvokersMap.Load(url.Key()); !ok {
+			logger.Debugf("service will be added in cache invokers: invokers key is  %s!", url.Key())
+			newInvoker := extension.GetProtocol(protocolwrapper.FILTER).Refer(url)
+			if newInvoker != nil {
+				dir.cacheInvokersMap.Store(url.Key(), newInvoker)
+			}
+		}
+	}
+}
+
+//select the protocol invokers from the directory
+func (dir *registryDirectory) List(invocation protocol.Invocation) []protocol.Invoker {
+	//TODO:router
+	return dir.cacheInvokers
+}
+
+func (dir *registryDirectory) IsAvailable() bool {
+	if !dir.BaseDirectory.IsAvailable() {
+		return dir.BaseDirectory.IsAvailable()
+	} else {
+		for _, ivk := range dir.cacheInvokers {
+			if ivk.IsAvailable() {
+				return true
+			}
+		}
+	}
+	return false
+}
+
+func (dir *registryDirectory) Destroy() {
+	//TODO:unregister & unsubscribe
+	dir.BaseDirectory.Destroy(func() {
+		for _, ivk := range dir.cacheInvokers {
+			ivk.Destroy()
+		}
+		dir.cacheInvokers = []protocol.Invoker{}
+	})
+}
+
+// configuration  > reference config >service config
+//  in this function we should merge the reference local url config into the service url from registry.
+//TODO configuration merge, in the future , the configuration center's config should merge too.
+func mergeUrl(serviceUrl common.URL, referenceUrl *common.URL) common.URL {
+	mergedUrl := serviceUrl
+	var methodConfigMergeFcn = []func(method string){}
+	//iterator the referenceUrl if serviceUrl not have the key ,merge in
+
+	for k, v := range referenceUrl.Params {
+		if _, ok := mergedUrl.Params[k]; !ok {
+			mergedUrl.Params.Set(k, v[0])
+		}
+	}
+	//loadBalance strategy config
+	if v := referenceUrl.Params.Get(constant.LOADBALANCE_KEY); v != "" {
+		mergedUrl.Params.Set(constant.LOADBALANCE_KEY, v)
+	}
+	methodConfigMergeFcn = append(methodConfigMergeFcn, func(method string) {
+		if v := referenceUrl.Params.Get(method + "." + constant.LOADBALANCE_KEY); v != "" {
+			mergedUrl.Params.Set(method+"."+constant.LOADBALANCE_KEY, v)
+		}
+	})
+
+	//cluster strategy config
+	if v := referenceUrl.Params.Get(constant.CLUSTER_KEY); v != "" {
+		mergedUrl.Params.Set(constant.CLUSTER_KEY, v)
+	}
+	methodConfigMergeFcn = append(methodConfigMergeFcn, func(method string) {
+		if v := referenceUrl.Params.Get(method + "." + constant.CLUSTER_KEY); v != "" {
+			mergedUrl.Params.Set(method+"."+constant.CLUSTER_KEY, v)
+		}
+	})
+
+	//remote timestamp
+	if v := serviceUrl.Params.Get(constant.TIMESTAMP_KEY); v != "" {
+		mergedUrl.Params.Set(constant.REMOTE_TIMESTAMP_KEY, v)
+		mergedUrl.Params.Set(constant.TIMESTAMP_KEY, referenceUrl.Params.Get(constant.TIMESTAMP_KEY))
+	}
+
+	//finally execute methodConfigMergeFcn
+	for _, method := range referenceUrl.Methods {
+		for _, fcn := range methodConfigMergeFcn {
+			fcn("methods." + method)
+		}
+	}
+
+	return mergedUrl
+}
diff --git a/registry/directory/directory_test.go b/registry/directory/directory_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..685e96a685bb3af2c6577d1ab24f1a87bef34a28
--- /dev/null
+++ b/registry/directory/directory_test.go
@@ -0,0 +1,145 @@
+// Copyright 2016-2019 hxmhlt
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package directory
+
+import (
+	"context"
+	"net/url"
+	"strconv"
+	"testing"
+	"time"
+)
+import (
+	"github.com/stretchr/testify/assert"
+)
+import (
+	"github.com/dubbo/go-for-apache-dubbo/cluster/cluster_impl"
+	"github.com/dubbo/go-for-apache-dubbo/common"
+	"github.com/dubbo/go-for-apache-dubbo/common/constant"
+	"github.com/dubbo/go-for-apache-dubbo/common/extension"
+	"github.com/dubbo/go-for-apache-dubbo/protocol/invocation"
+	"github.com/dubbo/go-for-apache-dubbo/protocol/protocolwrapper"
+	"github.com/dubbo/go-for-apache-dubbo/registry"
+)
+
+func TestSubscribe(t *testing.T) {
+	registryDirectory, _ := normalRegistryDir()
+
+	time.Sleep(1e9)
+	assert.Len(t, registryDirectory.cacheInvokers, 3)
+}
+
+func TestSubscribe_Delete(t *testing.T) {
+	registryDirectory, mockRegistry := normalRegistryDir()
+	time.Sleep(1e9)
+	assert.Len(t, registryDirectory.cacheInvokers, 3)
+	mockRegistry.MockEvent(&registry.ServiceEvent{Action: registry.ServiceDel, Service: *common.NewURLWithOptions("TEST0", common.WithProtocol("dubbo"))})
+	time.Sleep(1e9)
+	assert.Len(t, registryDirectory.cacheInvokers, 2)
+
+}
+func TestSubscribe_InvalidUrl(t *testing.T) {
+	url, _ := common.NewURL(context.TODO(), "mock://127.0.0.1:1111")
+	mockRegistry, _ := registry.NewMockRegistry(&common.URL{})
+	_, err := NewRegistryDirectory(&url, mockRegistry)
+	assert.Error(t, err)
+}
+
+func TestSubscribe_Group(t *testing.T) {
+	extension.SetProtocol(protocolwrapper.FILTER, protocolwrapper.NewMockProtocolFilter)
+	extension.SetCluster("mock", cluster_impl.NewMockCluster)
+
+	regurl, _ := common.NewURL(context.TODO(), "mock://127.0.0.1:1111")
+	suburl, _ := common.NewURL(context.TODO(), "dubbo://127.0.0.1:20000")
+	suburl.Params.Set(constant.CLUSTER_KEY, "mock")
+	regurl.SubURL = &suburl
+	mockRegistry, _ := registry.NewMockRegistry(&common.URL{})
+	registryDirectory, _ := NewRegistryDirectory(&regurl, mockRegistry)
+
+	go registryDirectory.Subscribe(*common.NewURLWithOptions("testservice"))
+
+	//for group1
+	urlmap := url.Values{}
+	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: registry.ServiceAdd, Service: *common.NewURLWithOptions("TEST"+strconv.FormatInt(int64(i), 10), common.WithProtocol("dubbo"),
+			common.WithParams(urlmap))})
+	}
+	//for group2
+	urlmap2 := url.Values{}
+	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: registry.ServiceAdd, Service: *common.NewURLWithOptions("TEST"+strconv.FormatInt(int64(i), 10), common.WithProtocol("dubbo"),
+			common.WithParams(urlmap2))})
+	}
+
+	time.Sleep(1e9)
+	assert.Len(t, registryDirectory.cacheInvokers, 2)
+}
+
+func Test_Destroy(t *testing.T) {
+	registryDirectory, _ := normalRegistryDir()
+
+	time.Sleep(1e9)
+	assert.Len(t, registryDirectory.cacheInvokers, 3)
+	assert.Equal(t, true, registryDirectory.IsAvailable())
+
+	registryDirectory.Destroy()
+	assert.Len(t, registryDirectory.cacheInvokers, 0)
+	assert.Equal(t, false, registryDirectory.IsAvailable())
+}
+
+func Test_List(t *testing.T) {
+	registryDirectory, _ := normalRegistryDir()
+
+	time.Sleep(1e9)
+	assert.Len(t, registryDirectory.List(&invocation.RPCInvocation{}), 3)
+	assert.Equal(t, true, registryDirectory.IsAvailable())
+
+}
+
+func normalRegistryDir() (*registryDirectory, *registry.MockRegistry) {
+	extension.SetProtocol(protocolwrapper.FILTER, protocolwrapper.NewMockProtocolFilter)
+
+	url, _ := common.NewURL(context.TODO(), "mock://127.0.0.1:1111")
+	suburl, _ := common.NewURL(context.TODO(), "dubbo://127.0.0.1:20000")
+	url.SubURL = &suburl
+	mockRegistry, _ := registry.NewMockRegistry(&common.URL{})
+	registryDirectory, _ := NewRegistryDirectory(&url, mockRegistry)
+
+	go registryDirectory.Subscribe(*common.NewURLWithOptions("testservice"))
+	for i := 0; i < 3; i++ {
+		mockRegistry.(*registry.MockRegistry).MockEvent(&registry.ServiceEvent{Action: registry.ServiceAdd, Service: *common.NewURLWithOptions("TEST"+strconv.FormatInt(int64(i), 10), common.WithProtocol("dubbo"))})
+	}
+	return registryDirectory, mockRegistry.(*registry.MockRegistry)
+}
+
+func TestMergeUrl(t *testing.T) {
+	referenceUrlParams := url.Values{}
+	referenceUrlParams.Set(constant.CLUSTER_KEY, "random")
+	referenceUrlParams.Set("test3", "1")
+	serviceUrlParams := url.Values{}
+	serviceUrlParams.Set("test2", "1")
+	serviceUrlParams.Set(constant.CLUSTER_KEY, "roundrobin")
+	referenceUrl, _ := common.NewURL(context.TODO(), "mock1://127.0.0.1:1111", common.WithParams(referenceUrlParams))
+	serviceUrl, _ := common.NewURL(context.TODO(), "mock2://127.0.0.1:20000", common.WithParams(serviceUrlParams))
+
+	mergedUrl := mergeUrl(serviceUrl, &referenceUrl)
+	assert.Equal(t, "random", mergedUrl.GetParam(constant.CLUSTER_KEY, ""))
+	assert.Equal(t, "1", mergedUrl.GetParam("test2", ""))
+	assert.Equal(t, "1", mergedUrl.GetParam("test3", ""))
+}
diff --git a/registry/event.go b/registry/event.go
index 13bb4f73fd0e3870706523e2e7a44360fbc7aa22..51a21ac37ce0d2b7fbb82a4d88ad636dfec3ee9d 100644
--- a/registry/event.go
+++ b/registry/event.go
@@ -1,3 +1,17 @@
+// Copyright 2016-2019 hxmhlt
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package registry
 
 import (
@@ -5,6 +19,9 @@ import (
 	"math/rand"
 	"time"
 )
+import (
+	"github.com/dubbo/go-for-apache-dubbo/common"
+)
 
 func init() {
 	rand.Seed(time.Now().UnixNano())
@@ -36,9 +53,9 @@ func (t ServiceEventType) String() string {
 
 type ServiceEvent struct {
 	Action  ServiceEventType
-	Service ServiceURL
+	Service common.URL
 }
 
 func (e ServiceEvent) String() string {
-	return fmt.Sprintf("ServiceEvent{Action{%s}, Service{%s}}", e.Action, e.Service)
+	return fmt.Sprintf("ServiceEvent{Action{%s}, Path{%s}}", e.Action, e.Service)
 }
diff --git a/registry/mock_registry.go b/registry/mock_registry.go
new file mode 100644
index 0000000000000000000000000000000000000000..16a0942b76918fc7bdd012d35dbb5a435d8d0a95
--- /dev/null
+++ b/registry/mock_registry.go
@@ -0,0 +1,75 @@
+// Copyright 2016-2019 hxmhlt
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package registry
+
+import (
+	"go.uber.org/atomic"
+)
+import (
+	"github.com/dubbo/go-for-apache-dubbo/common"
+)
+
+type MockRegistry struct {
+	listener  *listener
+	destroyed *atomic.Bool
+}
+
+func NewMockRegistry(url *common.URL) (Registry, error) {
+	registry := &MockRegistry{
+		destroyed: atomic.NewBool(false),
+	}
+	listener := &listener{count: 0, registry: registry, listenChan: make(chan *ServiceEvent)}
+	registry.listener = listener
+	return registry, nil
+}
+func (*MockRegistry) Register(url common.URL) error {
+	return nil
+}
+
+func (r *MockRegistry) Destroy() {
+	if r.destroyed.CAS(false, true) {
+	}
+}
+func (r *MockRegistry) IsAvailable() bool {
+	return !r.destroyed.Load()
+}
+func (r *MockRegistry) GetUrl() common.URL {
+	return common.URL{}
+}
+
+func (r *MockRegistry) Subscribe(common.URL) (Listener, error) {
+	return r.listener, nil
+}
+
+type listener struct {
+	count      int64
+	registry   *MockRegistry
+	listenChan chan *ServiceEvent
+}
+
+func (l *listener) Next() (*ServiceEvent, error) {
+	select {
+	case e := <-l.listenChan:
+		return e, nil
+	}
+}
+
+func (*listener) Close() {
+
+}
+
+func (r *MockRegistry) MockEvent(event *ServiceEvent) {
+	r.listener.listenChan <- event
+}
diff --git a/registry/options.go b/registry/options.go
deleted file mode 100644
index 28197a7af04972c0893d3ee2d685d9b1eaab77b2..0000000000000000000000000000000000000000
--- a/registry/options.go
+++ /dev/null
@@ -1,76 +0,0 @@
-package registry
-
-import (
-	"fmt"
-)
-
-/////////////////////////////////
-// dubbo role type
-/////////////////////////////////
-
-const (
-	CONSUMER = iota
-	CONFIGURATOR
-	ROUTER
-	PROVIDER
-)
-
-var (
-	DubboNodes = [...]string{"consumers", "configurators", "routers", "providers"}
-	DubboRole  = [...]string{"consumer", "", "", "provider"}
-)
-
-type DubboType int
-
-func (t DubboType) String() string {
-	return DubboNodes[t]
-}
-
-func (t DubboType) Role() string {
-	return DubboRole[t]
-}
-
-/////////////////////////////////
-// dubbo config & options
-/////////////////////////////////
-
-type RegistryOption interface {
-	Name() string
-}
-
-type ApplicationConfig struct {
-	Organization string `yaml:"organization"  json:"organization,omitempty"`
-	Name         string `yaml:"name" json:"name,omitempty"`
-	Module       string `yaml:"module" json:"module,omitempty"`
-	Version      string `yaml:"version" json:"version,omitempty"`
-	Owner        string `yaml:"owner" json:"owner,omitempty"`
-	Environment  string `yaml:"environment" json:"environment,omitempty"`
-}
-
-type Options struct {
-	ApplicationConfig
-	DubboType DubboType
-}
-
-func (o *Options) String() string {
-	return fmt.Sprintf("name:%s, version:%s, owner:%s, module:%s, organization:%s, type:%s",
-		o.Name, o.Version, o.Owner, o.Module, o.Organization, o.DubboType)
-}
-
-type Option func(*Options)
-
-func (Option) Name() string {
-	return "dubbogo-registry-option"
-}
-
-func WithDubboType(typ DubboType) Option {
-	return func(o *Options) {
-		o.DubboType = typ
-	}
-}
-
-func WithApplicationConf(conf ApplicationConfig) Option {
-	return func(o *Options) {
-		o.ApplicationConfig = conf
-	}
-}
diff --git a/registry/protocol/protocol.go b/registry/protocol/protocol.go
new file mode 100644
index 0000000000000000000000000000000000000000..0c1789ac6593d33289d66d2d63689f95f534c1e4
--- /dev/null
+++ b/registry/protocol/protocol.go
@@ -0,0 +1,200 @@
+// Copyright 2016-2019 hxmhlt
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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 protocol
+
+import (
+	"sync"
+)
+
+import (
+	"github.com/dubbo/go-for-apache-dubbo/common/logger"
+)
+
+import (
+	"github.com/dubbo/go-for-apache-dubbo/common"
+	"github.com/dubbo/go-for-apache-dubbo/common/constant"
+	"github.com/dubbo/go-for-apache-dubbo/common/extension"
+	"github.com/dubbo/go-for-apache-dubbo/protocol"
+	"github.com/dubbo/go-for-apache-dubbo/protocol/protocolwrapper"
+	"github.com/dubbo/go-for-apache-dubbo/registry"
+	directory2 "github.com/dubbo/go-for-apache-dubbo/registry/directory"
+)
+
+var regProtocol *registryProtocol
+
+type registryProtocol struct {
+	invokers []protocol.Invoker
+	// 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.
+	//providerurl <--> exporter
+	bounds sync.Map
+}
+
+func init() {
+	extension.SetProtocol("registry", GetProtocol)
+}
+
+func newRegistryProtocol() *registryProtocol {
+	return &registryProtocol{
+		registries: sync.Map{},
+		bounds:     sync.Map{},
+	}
+}
+func getRegistry(regUrl *common.URL) registry.Registry {
+	reg, err := extension.GetRegistry(regUrl.Protocol, regUrl)
+	if err != nil {
+		logger.Errorf("Registry can not connect success, program is going to panic.Error message is %s", err.Error())
+		panic(err.Error())
+	}
+	return reg
+}
+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
+	}
+	var reg registry.Registry
+
+	if regI, loaded := proto.registries.Load(registryUrl.Key()); !loaded {
+		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 := directory2.NewRegistryDirectory(&registryUrl, reg)
+	if err != nil {
+		logger.Errorf("consumer service %v  create registry directory  error, error message is %s, and will return nil invoker!", serviceUrl.String(), err.Error())
+		return nil
+	}
+	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())
+	}
+	go directory.Subscribe(*serviceUrl)
+
+	//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
+}
+
+func (proto *registryProtocol) Export(invoker protocol.Invoker) protocol.Exporter {
+	registryUrl := proto.getRegistryUrl(invoker)
+	providerUrl := proto.getProviderUrl(invoker)
+
+	var reg registry.Registry
+
+	if regI, loaded := proto.registries.Load(registryUrl.Key()); !loaded {
+		reg = getRegistry(&registryUrl)
+		proto.registries.Store(registryUrl.Key(), reg)
+	} else {
+		reg = regI.(registry.Registry)
+	}
+
+	err := reg.Register(providerUrl)
+	if err != nil {
+		logger.Errorf("provider service %v register registry %v error, error message is %s", providerUrl.Key(), registryUrl.Key(), err.Error())
+		return nil
+	}
+
+	key := providerUrl.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!")
+	} else {
+		wrappedInvoker := newWrappedInvoker(invoker, providerUrl)
+		cachedExporter = extension.GetProtocol(protocolwrapper.FILTER).Export(wrappedInvoker)
+		proto.bounds.Store(key, cachedExporter)
+		logger.Infof("The exporter has not been cached, and will return a new  exporter!")
+	}
+
+	return cachedExporter.(protocol.Exporter)
+
+}
+
+func (proto *registryProtocol) Destroy() {
+	for _, ivk := range proto.invokers {
+		ivk.Destroy()
+	}
+	proto.invokers = []protocol.Invoker{}
+
+	proto.bounds.Range(func(key, value interface{}) bool {
+		exporter := value.(protocol.Exporter)
+		exporter.Unexport()
+		proto.bounds.Delete(key)
+		return true
+	})
+
+	proto.registries.Range(func(key, value interface{}) bool {
+		reg := value.(registry.Registry)
+		if reg.IsAvailable() {
+			reg.Destroy()
+		}
+		proto.registries.Delete(key)
+		return true
+	})
+}
+
+func (*registryProtocol) 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 url.Protocol == constant.REGISTRY_PROTOCOL {
+		protocol := url.GetParam(constant.REGISTRY_KEY, "")
+		url.Protocol = protocol
+	}
+	return url
+}
+
+func (*registryProtocol) getProviderUrl(invoker protocol.Invoker) common.URL {
+	url := invoker.GetUrl()
+	return *url.SubURL
+}
+
+func GetProtocol() protocol.Protocol {
+	if regProtocol != nil {
+		return regProtocol
+	}
+	return newRegistryProtocol()
+}
+
+type wrappedInvoker struct {
+	invoker protocol.Invoker
+	url     common.URL
+	protocol.BaseInvoker
+}
+
+func newWrappedInvoker(invoker protocol.Invoker, url common.URL) *wrappedInvoker {
+	return &wrappedInvoker{
+		invoker:     invoker,
+		url:         url,
+		BaseInvoker: *protocol.NewBaseInvoker(common.URL{}),
+	}
+}
+func (ivk *wrappedInvoker) GetUrl() common.URL {
+	return ivk.url
+}
+func (ivk *wrappedInvoker) getInvoker() protocol.Invoker {
+	return ivk.invoker
+}
diff --git a/registry/protocol/protocol_test.go b/registry/protocol/protocol_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..f1bb2fc5a7e3125933c75e9786cfbffa0c5353fd
--- /dev/null
+++ b/registry/protocol/protocol_test.go
@@ -0,0 +1,181 @@
+// Copyright 2016-2019 hxmhlt
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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 protocol
+
+import (
+	"context"
+	"testing"
+)
+import (
+	"github.com/stretchr/testify/assert"
+)
+import (
+	cluster "github.com/dubbo/go-for-apache-dubbo/cluster/cluster_impl"
+	"github.com/dubbo/go-for-apache-dubbo/common"
+	"github.com/dubbo/go-for-apache-dubbo/common/constant"
+	"github.com/dubbo/go-for-apache-dubbo/common/extension"
+	"github.com/dubbo/go-for-apache-dubbo/protocol"
+	"github.com/dubbo/go-for-apache-dubbo/protocol/protocolwrapper"
+	"github.com/dubbo/go-for-apache-dubbo/registry"
+)
+
+func referNormal(t *testing.T, regProtocol *registryProtocol) {
+	extension.SetProtocol("registry", GetProtocol)
+	extension.SetRegistry("mock", registry.NewMockRegistry)
+	extension.SetProtocol(protocolwrapper.FILTER, protocolwrapper.NewMockProtocolFilter)
+	extension.SetCluster("mock", cluster.NewMockCluster)
+
+	url, _ := common.NewURL(context.TODO(), "mock://127.0.0.1:1111")
+	suburl, _ := common.NewURL(context.TODO(), "dubbo://127.0.0.1:20000//", common.WithParamsValue(constant.CLUSTER_KEY, "mock"))
+
+	url.SubURL = &suburl
+
+	invoker := regProtocol.Refer(url)
+	assert.IsType(t, &protocol.BaseInvoker{}, invoker)
+	assert.Equal(t, invoker.GetUrl().String(), url.String())
+}
+func TestRefer(t *testing.T) {
+	regProtocol := newRegistryProtocol()
+	referNormal(t, regProtocol)
+}
+
+func TestMultiRegRefer(t *testing.T) {
+	regProtocol := newRegistryProtocol()
+	referNormal(t, regProtocol)
+	url2, _ := common.NewURL(context.TODO(), "mock://127.0.0.1:2222")
+	suburl2, _ := common.NewURL(context.TODO(), "dubbo://127.0.0.1:20000//", common.WithParamsValue(constant.CLUSTER_KEY, "mock"))
+
+	url2.SubURL = &suburl2
+
+	regProtocol.Refer(url2)
+	var count int
+	regProtocol.registries.Range(func(key, value interface{}) bool {
+		count++
+		return true
+	})
+	assert.Equal(t, count, 2)
+}
+
+func TestOneRegRefer(t *testing.T) {
+	regProtocol := newRegistryProtocol()
+	referNormal(t, regProtocol)
+
+	url2, _ := common.NewURL(context.TODO(), "mock://127.0.0.1:1111")
+	suburl2, _ := common.NewURL(context.TODO(), "dubbo://127.0.0.1:20000//", common.WithParamsValue(constant.CLUSTER_KEY, "mock"))
+
+	url2.SubURL = &suburl2
+
+	regProtocol.Refer(url2)
+	var count int
+	regProtocol.registries.Range(func(key, value interface{}) bool {
+		count++
+		return true
+	})
+	assert.Equal(t, count, 1)
+}
+func exporterNormal(t *testing.T, regProtocol *registryProtocol) {
+	extension.SetProtocol("registry", GetProtocol)
+	extension.SetRegistry("mock", registry.NewMockRegistry)
+	extension.SetProtocol(protocolwrapper.FILTER, protocolwrapper.NewMockProtocolFilter)
+	url, _ := common.NewURL(context.TODO(), "mock://127.0.0.1:1111")
+	suburl, _ := common.NewURL(context.TODO(), "dubbo://127.0.0.1:20000//", common.WithParamsValue(constant.CLUSTER_KEY, "mock"))
+
+	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())
+}
+
+func TestExporter(t *testing.T) {
+	regProtocol := newRegistryProtocol()
+	exporterNormal(t, regProtocol)
+}
+
+func TestMultiRegAndMultiProtoExporter(t *testing.T) {
+	regProtocol := newRegistryProtocol()
+	exporterNormal(t, regProtocol)
+
+	url2, _ := common.NewURL(context.TODO(), "mock://127.0.0.1:2222")
+	suburl2, _ := common.NewURL(context.TODO(), "jsonrpc://127.0.0.1:20000//", common.WithParamsValue(constant.CLUSTER_KEY, "mock"))
+
+	url2.SubURL = &suburl2
+	invoker2 := protocol.NewBaseInvoker(url2)
+	regProtocol.Export(invoker2)
+
+	var count int
+	regProtocol.registries.Range(func(key, value interface{}) bool {
+		count++
+		return true
+	})
+	assert.Equal(t, count, 2)
+
+	var count2 int
+	regProtocol.bounds.Range(func(key, value interface{}) bool {
+		count2++
+		return true
+	})
+	assert.Equal(t, count2, 2)
+}
+
+func TestOneRegAndProtoExporter(t *testing.T) {
+	regProtocol := newRegistryProtocol()
+	exporterNormal(t, regProtocol)
+
+	url2, _ := common.NewURL(context.TODO(), "mock://127.0.0.1:1111")
+	suburl2, _ := common.NewURL(context.TODO(), "dubbo://127.0.0.1:20000//", common.WithParamsValue(constant.CLUSTER_KEY, "mock"))
+
+	url2.SubURL = &suburl2
+	invoker2 := protocol.NewBaseInvoker(url2)
+	regProtocol.Export(invoker2)
+
+	var count int
+	regProtocol.registries.Range(func(key, value interface{}) bool {
+		count++
+		return true
+	})
+	assert.Equal(t, count, 1)
+
+	var count2 int
+	regProtocol.bounds.Range(func(key, value interface{}) bool {
+		count2++
+		return true
+	})
+	assert.Equal(t, count2, 1)
+}
+
+func TestDestry(t *testing.T) {
+	regProtocol := newRegistryProtocol()
+	referNormal(t, regProtocol)
+	exporterNormal(t, regProtocol)
+
+	regProtocol.Destroy()
+	assert.Equal(t, len(regProtocol.invokers), 0)
+
+	var count int
+	regProtocol.registries.Range(func(key, value interface{}) bool {
+		count++
+		return true
+	})
+	assert.Equal(t, count, 0)
+
+	var count2 int
+	regProtocol.bounds.Range(func(key, value interface{}) bool {
+		count2++
+		return true
+	})
+	assert.Equal(t, count2, 0)
+}
diff --git a/registry/registry.go b/registry/registry.go
index 8408b14e0a2cf5339901d0b954b6854f9d01233a..28ca1a1c19d7e2ffa72797be7bdf221338c19a45 100644
--- a/registry/registry.go
+++ b/registry/registry.go
@@ -1,25 +1,32 @@
+// Copyright 2016-2019 hxmhlt
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package registry
 
-//////////////////////////////////////////////
-// Registry Interface
-//////////////////////////////////////////////
+import (
+	"github.com/dubbo/go-for-apache-dubbo/common"
+)
 
-// for service discovery/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(ServiceConfig) error
+	Register(url common.URL) error
 
 	//used for service consumer ,start subscribe service event from registry
-	Subscribe() (Listener, error)
-
-	//input the serviceConfig , registry should return serviceUrlArray with multi location(provider nodes) available
-	GetService(ServiceConfig) ([]ServiceURL, error)
-	//close the registry for Elegant closing
-	Close()
-	//return if the registry is closed for consumer subscribing
-	IsClosed() bool
+	Subscribe(common.URL) (Listener, error)
 }
 
 type Listener interface {
diff --git a/registry/service.go b/registry/service.go
deleted file mode 100644
index 7d89796aed1a1bb21ab297b553e75123c9e494cc..0000000000000000000000000000000000000000
--- a/registry/service.go
+++ /dev/null
@@ -1,293 +0,0 @@
-package registry
-
-import (
-	"fmt"
-	"net"
-	"net/url"
-	"strconv"
-	"strings"
-	"time"
-)
-
-import (
-	jerrors "github.com/juju/errors"
-)
-
-//////////////////////////////////////////////
-// service config
-//////////////////////////////////////////////
-
-type ServiceConfig interface {
-	Key() string
-	String() string
-	ServiceEqual(url ServiceURL) bool
-	//your service config implements must contain properties below
-	Service() string
-	Protocol() string
-	Version() string
-	Group() string
-	SetProtocol(string)
-	SetService(string)
-	SetVersion(string)
-	SetGroup(string)
-}
-
-type ProviderServiceConfig interface {
-	//your service config implements must contain properties below
-	ServiceConfig
-	Methods() string
-	Path() string
-	SetMethods(string)
-	SetPath(string)
-}
-
-type DefaultServiceConfig struct {
-	Protocol_ string `required:"true",default:"dubbo"  yaml:"protocol"  json:"protocol,omitempty"`
-	Service_  string `required:"true"  yaml:"service"  json:"service,omitempty"`
-	Group_    string `yaml:"group" json:"group,omitempty"`
-	Version_  string `yaml:"version" json:"version,omitempty"`
-}
-
-func NewDefaultServiceConfig() ServiceConfig {
-	return &DefaultServiceConfig{}
-}
-
-func (c *DefaultServiceConfig) Key() string {
-	return fmt.Sprintf("%s@%s", c.Service_, c.Protocol_)
-}
-
-func (c *DefaultServiceConfig) String() string {
-	return fmt.Sprintf("%s@%s-%s-%s", c.Service_, c.Protocol_, c.Group_, c.Version_)
-}
-
-func (c *DefaultServiceConfig) ServiceEqual(url ServiceURL) bool {
-	if c.Protocol_ != url.Protocol() {
-		return false
-	}
-
-	if c.Service_ != url.Query().Get("interface") {
-		return false
-	}
-
-	if c.Group_ != url.Group() {
-		return false
-	}
-
-	if c.Version_ != url.Version() {
-		return false
-	}
-
-	return true
-}
-
-func (c *DefaultServiceConfig) Service() string {
-	return c.Service_
-}
-
-func (c *DefaultServiceConfig) Protocol() string {
-	return c.Protocol_
-}
-
-func (c *DefaultServiceConfig) Version() string {
-	return c.Version_
-}
-
-func (c *DefaultServiceConfig) Group() string {
-	return c.Group_
-}
-func (c *DefaultServiceConfig) SetProtocol(s string) {
-	c.Protocol_ = s
-}
-
-func (c *DefaultServiceConfig) SetService(s string) {
-	c.Service_ = s
-}
-func (c *DefaultServiceConfig) SetVersion(s string) {
-	c.Version_ = s
-}
-
-func (c *DefaultServiceConfig) SetGroup(s string) {
-	c.Group_ = s
-}
-
-type DefaultProviderServiceConfig struct {
-	*DefaultServiceConfig
-	Path_    string `yaml:"path" json:"path,omitempty"`
-	Methods_ string `yaml:"methods" json:"methods,omitempty"`
-}
-
-func NewDefaultProviderServiceConfig() ProviderServiceConfig {
-	return &DefaultProviderServiceConfig{
-		DefaultServiceConfig: NewDefaultServiceConfig().(*DefaultServiceConfig),
-	}
-}
-
-func (c *DefaultProviderServiceConfig) Methods() string {
-	return c.Methods_
-}
-
-func (c *DefaultProviderServiceConfig) Path() string {
-	return c.Path_
-}
-
-func (c *DefaultProviderServiceConfig) SetMethods(s string) {
-	c.Methods_ = s
-}
-
-func (c *DefaultProviderServiceConfig) SetPath(s string) {
-	c.Path_ = s
-}
-
-//////////////////////////////////////////
-// service url
-//////////////////////////////////////////
-
-type ServiceURL interface {
-	ServiceConfig() ServiceConfig
-	CheckMethod(string) bool
-	PrimitiveURL() string
-	Query() url.Values
-	Location() string
-	Timeout() time.Duration
-	Group() string
-	Protocol() string
-	Version() string
-	Ip() string
-	Port() string
-	Path() string
-}
-
-type DefaultServiceURL struct {
-	Protocol_     string
-	Location_     string // ip+port
-	Path_         string // like  /com.ikurento.dubbo.UserProvider3
-	Ip_           string
-	Port_         string
-	Timeout_      time.Duration
-	Version_      string
-	Group_        string
-	Query_        url.Values
-	Weight_       int32
-	PrimitiveURL_ string
-}
-
-func NewDefaultServiceURL(urlString string) (ServiceURL, error) {
-	var (
-		err          error
-		rawUrlString string
-		serviceUrl   *url.URL
-		s            = &DefaultServiceURL{}
-	)
-
-	rawUrlString, err = url.QueryUnescape(urlString)
-	if err != nil {
-		return nil, jerrors.Errorf("url.QueryUnescape(%s),  error{%v}", urlString, err)
-	}
-
-	serviceUrl, err = url.Parse(rawUrlString)
-	if err != nil {
-		return nil, jerrors.Errorf("url.Parse(url string{%s}),  error{%v}", rawUrlString, err)
-	}
-
-	s.Query_, err = url.ParseQuery(serviceUrl.RawQuery)
-	if err != nil {
-		return nil, jerrors.Errorf("url.ParseQuery(raw url string{%s}),  error{%v}", serviceUrl.RawQuery, err)
-	}
-
-	s.PrimitiveURL_ = urlString
-	s.Protocol_ = serviceUrl.Scheme
-	s.Location_ = serviceUrl.Host
-	s.Path_ = serviceUrl.Path
-	if strings.Contains(s.Location_, ":") {
-		s.Ip_, s.Port_, err = net.SplitHostPort(s.Location_)
-		if err != nil {
-			return nil, jerrors.Errorf("net.SplitHostPort(Url.Host{%s}), error{%v}", s.Location_, err)
-		}
-	}
-	s.Group_ = s.Query_.Get("group")
-	s.Version_ = s.Query_.Get("version")
-	timeoutStr := s.Query_.Get("timeout")
-	if len(timeoutStr) == 0 {
-		timeoutStr = s.Query_.Get("default.timeout")
-	}
-	if len(timeoutStr) != 0 {
-		timeout, err := strconv.Atoi(timeoutStr)
-		if err == nil && timeout != 0 {
-			s.Timeout_ = time.Duration(timeout * 1e6) // timeout unit is millisecond
-		}
-	}
-
-	return s, nil
-}
-
-func (s DefaultServiceURL) String() string {
-	return fmt.Sprintf(
-		"DefaultServiceURL{Protocol:%s, Location:%s, Path:%s, Ip:%s, Port:%s, "+
-			"Timeout:%s, Version:%s, Group:%s, Weight_:%d, Query:%+v}",
-		s.Protocol_, s.Location_, s.Path_, s.Ip_, s.Port_,
-		s.Timeout_, s.Version_, s.Group_, s.Weight_, s.Query_)
-}
-
-func (s *DefaultServiceURL) ServiceConfig() ServiceConfig {
-	interfaceName := s.Query_.Get("interface")
-	return &DefaultServiceConfig{
-		Protocol_: s.Protocol_,
-		Service_:  interfaceName,
-		Group_:    s.Group_,
-		Version_:  s.Version_,
-	}
-}
-
-func (s *DefaultServiceURL) CheckMethod(method string) bool {
-	var (
-		methodArray []string
-	)
-
-	methodArray = strings.Split(s.Query_.Get("methods"), ",")
-	for _, m := range methodArray {
-		if m == method {
-			return true
-		}
-	}
-
-	return false
-}
-
-func (s *DefaultServiceURL) PrimitiveURL() string {
-	return s.PrimitiveURL_
-}
-
-func (s *DefaultServiceURL) Timeout() time.Duration {
-	return s.Timeout_
-}
-func (s *DefaultServiceURL) Location() string {
-	return s.Location_
-}
-
-func (s *DefaultServiceURL) Query() url.Values {
-	return s.Query_
-}
-
-func (s *DefaultServiceURL) Group() string {
-	return s.Group_
-}
-
-func (s *DefaultServiceURL) Protocol() string {
-	return s.Protocol_
-}
-
-func (s *DefaultServiceURL) Version() string {
-	return s.Version_
-}
-
-func (s *DefaultServiceURL) Ip() string {
-	return s.Ip_
-}
-
-func (s *DefaultServiceURL) Port() string {
-	return s.Port_
-}
-
-func (s *DefaultServiceURL) Path() string {
-	return s.Path_
-}
diff --git a/registry/zookeeper/consumer.go b/registry/zookeeper/consumer.go
deleted file mode 100644
index 5905fb8d1fbdedfe9f04cf331582bda0c16ffefc..0000000000000000000000000000000000000000
--- a/registry/zookeeper/consumer.go
+++ /dev/null
@@ -1,127 +0,0 @@
-package zookeeper
-
-import (
-	"fmt"
-)
-
-import (
-	log "github.com/AlexStocks/log4go"
-	jerrors "github.com/juju/errors"
-)
-
-import (
-	"github.com/dubbo/go-for-apache-dubbo/plugins"
-	"github.com/dubbo/go-for-apache-dubbo/registry"
-)
-
-// name: service@protocol
-func (r *ZkRegistry) GetService(conf registry.ServiceConfig) ([]registry.ServiceURL, error) {
-
-	var (
-		err         error
-		dubboPath   string
-		nodes       []string
-		listener    *zkEventListener
-		serviceURL  registry.ServiceURL
-		serviceConf registry.ServiceConfig
-		ok          bool
-	)
-	r.listenerLock.Lock()
-	listener = r.listener
-	r.listenerLock.Unlock()
-
-	if listener != nil {
-		listener.listenServiceEvent(conf)
-	}
-
-	r.cltLock.Lock()
-	serviceConf, ok = r.services[conf.Key()]
-	r.cltLock.Unlock()
-	if !ok {
-		return nil, jerrors.Errorf("Service{%s} has not been registered", conf.Key())
-	}
-	if !ok {
-		return nil, jerrors.Errorf("Service{%s}: failed to get serviceConfigIf type", conf.Key())
-	}
-
-	dubboPath = fmt.Sprintf("/dubbo/%s/providers", conf.Service())
-	err = r.validateZookeeperClient()
-	if err != nil {
-		return nil, jerrors.Trace(err)
-	}
-	r.cltLock.Lock()
-	nodes, err = r.client.getChildren(dubboPath)
-	r.cltLock.Unlock()
-	if err != nil {
-		log.Warn("getChildren(dubboPath{%s}) = error{%v}", dubboPath, err)
-		return nil, jerrors.Trace(err)
-	}
-
-	var listenerServiceMap = make(map[string]registry.ServiceURL)
-	for _, n := range nodes {
-
-		serviceURL, err = plugins.DefaultServiceURL()(n)
-		if err != nil {
-			log.Error("NewDefaultServiceURL({%s}) = error{%v}", n, err)
-			continue
-		}
-		if !serviceConf.ServiceEqual(serviceURL) {
-			log.Warn("serviceURL{%s} is not compatible with ServiceConfig{%#v}", serviceURL, serviceConf)
-			continue
-		}
-
-		_, ok := listenerServiceMap[serviceURL.Query().Get(serviceURL.Location())]
-		if !ok {
-			listenerServiceMap[serviceURL.Location()] = serviceURL
-			continue
-		}
-	}
-
-	var services []registry.ServiceURL
-	for _, service := range listenerServiceMap {
-		services = append(services, service)
-	}
-
-	return services, nil
-}
-
-func (r *ZkRegistry) Subscribe() (registry.Listener, error) {
-	r.wg.Add(1)
-	return r.getListener()
-}
-
-func (r *ZkRegistry) getListener() (*zkEventListener, error) {
-	var (
-		zkListener *zkEventListener
-	)
-
-	r.listenerLock.Lock()
-	zkListener = r.listener
-	r.listenerLock.Unlock()
-	if zkListener != nil {
-		return zkListener, nil
-	}
-
-	r.cltLock.Lock()
-	client := r.client
-	r.cltLock.Unlock()
-	if client == nil {
-		return nil, jerrors.New("zk connection broken")
-	}
-
-	// new client & listener
-	zkListener = newZkEventListener(r, client)
-
-	r.listenerLock.Lock()
-	r.listener = zkListener
-	r.listenerLock.Unlock()
-
-	// listen
-	r.cltLock.Lock()
-	for _, svs := range r.services {
-		go zkListener.listenServiceEvent(svs)
-	}
-	r.cltLock.Unlock()
-
-	return zkListener, nil
-}
diff --git a/registry/zookeeper/listener.go b/registry/zookeeper/listener.go
index 697038608b342343c9bbe12167acf82af3e8d645..b6446e7fcc3dd46515e9e50ad2cdbc323263078e 100644
--- a/registry/zookeeper/listener.go
+++ b/registry/zookeeper/listener.go
@@ -1,6 +1,21 @@
+// Copyright 2016-2019 hxmhlt
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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 zookeeper
 
 import (
+	"context"
 	"fmt"
 	"path"
 	"sync"
@@ -8,13 +23,13 @@ import (
 )
 
 import (
-	log "github.com/AlexStocks/log4go"
-	jerrors "github.com/juju/errors"
+	"github.com/dubbo/go-for-apache-dubbo/common/logger"
+	perrors "github.com/pkg/errors"
 	"github.com/samuel/go-zookeeper/zk"
 )
 
 import (
-	"github.com/dubbo/go-for-apache-dubbo/plugins"
+	"github.com/dubbo/go-for-apache-dubbo/common"
 	"github.com/dubbo/go-for-apache-dubbo/registry"
 )
 
@@ -37,10 +52,10 @@ type zkEventListener struct {
 	serviceMapLock sync.Mutex
 	serviceMap     map[string]struct{}
 	wg             sync.WaitGroup
-	registry       *ZkRegistry
+	registry       *zkRegistry
 }
 
-func newZkEventListener(registry *ZkRegistry, client *zookeeperClient) *zkEventListener {
+func newZkEventListener(registry *zkRegistry, client *zookeeperClient) *zkEventListener {
 	return &zkEventListener{
 		client:     client,
 		registry:   registry,
@@ -56,23 +71,23 @@ func (l *zkEventListener) listenServiceNodeEvent(zkPath string) bool {
 	for {
 		keyEventCh, err := l.client.existW(zkPath)
 		if err != nil {
-			log.Error("existW{key:%s} = error{%v}", zkPath, err)
+			logger.Errorf("existW{key:%s} = error{%v}", zkPath, err)
 			return false
 		}
 
 		select {
 		case zkEvent = <-keyEventCh:
-			log.Warn("get a zookeeper zkEvent{type:%s, server:%s, path:%s, state:%d-%s, err:%s}",
+			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)
 			switch zkEvent.Type {
 			case zk.EventNodeDataChanged:
-				log.Warn("zk.ExistW(key{%s}) = event{EventNodeDataChanged}", zkPath)
+				logger.Warnf("zk.ExistW(key{%s}) = event{EventNodeDataChanged}", zkPath)
 			case zk.EventNodeCreated:
-				log.Warn("zk.ExistW(key{%s}) = event{EventNodeCreated}", zkPath)
+				logger.Warnf("zk.ExistW(key{%s}) = event{EventNodeCreated}", zkPath)
 			case zk.EventNotWatching:
-				log.Warn("zk.ExistW(key{%s}) = event{EventNotWatching}", zkPath)
+				logger.Warnf("zk.ExistW(key{%s}) = event{EventNotWatching}", zkPath)
 			case zk.EventNodeDeleted:
-				log.Warn("zk.ExistW(key{%s}) = event{EventNodeDeleted}", zkPath)
+				logger.Warnf("zk.ExistW(key{%s}) = event{EventNodeDeleted}", zkPath)
 				return true
 			}
 		case <-l.client.done():
@@ -83,7 +98,7 @@ func (l *zkEventListener) listenServiceNodeEvent(zkPath string) bool {
 	return false
 }
 
-func (l *zkEventListener) handleZkNodeEvent(zkPath string, children []string, conf registry.ServiceConfig) {
+func (l *zkEventListener) handleZkNodeEvent(zkPath string, children []string, conf common.URL) {
 	contains := func(s []string, e string) bool {
 		for _, a := range s {
 			if a == e {
@@ -96,14 +111,14 @@ func (l *zkEventListener) handleZkNodeEvent(zkPath string, children []string, co
 
 	newChildren, err := l.client.getChildren(zkPath)
 	if err != nil {
-		log.Error("path{%s} child nodes changed, zk.Children() = error{%v}", zkPath, jerrors.ErrorStack(err))
+		logger.Errorf("path{%s} child nodes changed, zk.Children() = error{%v}", zkPath, perrors.WithStack(err))
 		return
 	}
 
 	// a node was added -- listen the new node
 	var (
 		newNode    string
-		serviceURL registry.ServiceURL
+		serviceURL common.URL
 	)
 	for _, n := range newChildren {
 		if contains(children, n) {
@@ -111,26 +126,27 @@ func (l *zkEventListener) handleZkNodeEvent(zkPath string, children []string, co
 		}
 
 		newNode = path.Join(zkPath, n)
-		log.Info("add zkNode{%s}", newNode)
-		serviceURL, err = plugins.DefaultServiceURL()(n)
+		logger.Infof("add zkNode{%s}", newNode)
+		//context.TODO
+		serviceURL, err = common.NewURL(context.TODO(), n)
 		if err != nil {
-			log.Error("NewDefaultServiceURL(%s) = error{%v}", n, jerrors.ErrorStack(err))
+			logger.Errorf("NewURL(%s) = error{%v}", n, perrors.WithStack(err))
 			continue
 		}
-		if !conf.ServiceEqual(serviceURL) {
-			log.Warn("serviceURL{%s} is not compatible with ServiceConfig{%#v}", serviceURL, conf)
+		if !conf.URLEqual(serviceURL) {
+			logger.Warnf("serviceURL{%s} is not compatible with SubURL{%#v}", serviceURL.Key(), conf.Key())
 			continue
 		}
-		log.Info("add serviceURL{%s}", serviceURL)
+		logger.Infof("add serviceURL{%s}", serviceURL)
 		l.events <- zkEvent{&registry.ServiceEvent{Action: registry.ServiceAdd, Service: serviceURL}, nil}
 		// listen l service node
-		go func(node string, serviceURL registry.ServiceURL) {
-			log.Info("delete zkNode{%s}", node)
+		go func(node string, serviceURL common.URL) {
+			logger.Infof("delete zkNode{%s}", node)
 			if l.listenServiceNodeEvent(node) {
-				log.Info("delete serviceURL{%s}", serviceURL)
+				logger.Infof("delete serviceURL{%s}", serviceURL)
 				l.events <- zkEvent{&registry.ServiceEvent{Action: registry.ServiceDel, Service: serviceURL}, nil}
 			}
-			log.Warn("listenSelf(zk path{%s}) goroutine exit now", zkPath)
+			logger.Warnf("listenSelf(zk path{%s}) goroutine exit now", zkPath)
 		}(newNode, serviceURL)
 	}
 
@@ -142,22 +158,22 @@ func (l *zkEventListener) handleZkNodeEvent(zkPath string, children []string, co
 		}
 
 		oldNode = path.Join(zkPath, n)
-		log.Warn("delete zkPath{%s}", oldNode)
-		serviceURL, err = registry.NewDefaultServiceURL(n)
-		if !conf.ServiceEqual(serviceURL) {
-			log.Warn("serviceURL{%s} has been deleted is not compatible with ServiceConfig{%#v}", serviceURL, conf)
+		logger.Warnf("delete zkPath{%s}", oldNode)
+		serviceURL, err = common.NewURL(context.TODO(), n)
+		if !conf.URLEqual(serviceURL) {
+			logger.Warnf("serviceURL{%s} has been deleted is not compatible with SubURL{%#v}", serviceURL.Key(), conf.Key())
 			continue
 		}
-		log.Warn("delete serviceURL{%s}", serviceURL)
+		logger.Warnf("delete serviceURL{%s}", serviceURL)
 		if err != nil {
-			log.Error("NewDefaultServiceURL(i{%s}) = error{%v}", n, jerrors.ErrorStack(err))
+			logger.Errorf("NewURL(i{%s}) = error{%v}", n, perrors.WithStack(err))
 			continue
 		}
 		l.events <- zkEvent{&registry.ServiceEvent{Action: registry.ServiceDel, Service: serviceURL}, nil}
 	}
 }
 
-func (l *zkEventListener) listenDirEvent(zkPath string, conf registry.ServiceConfig) {
+func (l *zkEventListener) listenDirEvent(zkPath string, conf common.URL) {
 	l.wg.Add(1)
 	defer l.wg.Done()
 
@@ -176,7 +192,7 @@ func (l *zkEventListener) listenDirEvent(zkPath string, conf registry.ServiceCon
 			if MaxFailTimes <= failTimes {
 				failTimes = MaxFailTimes
 			}
-			log.Error("listenDirEvent(path{%s}) = error{%v}", zkPath, err)
+			logger.Errorf("listenDirEvent(path{%s}) = error{%v}", zkPath, err)
 			// clear the event channel
 		CLEAR:
 			for {
@@ -193,10 +209,10 @@ func (l *zkEventListener) listenDirEvent(zkPath string, conf registry.ServiceCon
 				continue
 			case <-l.client.done():
 				l.client.unregisterEvent(zkPath, &event)
-				log.Warn("client.done(), listen(path{%s}, ServiceConfig{%#v}) goroutine exit now...", zkPath, conf)
+				logger.Warnf("client.done(), listen(path{%s}, ReferenceConfig{%#v}) goroutine exit now...", zkPath, conf)
 				return
 			case <-event:
-				log.Info("get zk.EventNodeDataChange notify event")
+				logger.Infof("get zk.EventNodeDataChange notify event")
 				l.client.unregisterEvent(zkPath, &event)
 				l.handleZkNodeEvent(zkPath, nil, conf)
 				continue
@@ -206,14 +222,14 @@ func (l *zkEventListener) listenDirEvent(zkPath string, conf registry.ServiceCon
 
 		select {
 		case zkEvent = <-childEventCh:
-			log.Warn("get a zookeeper zkEvent{type:%s, server:%s, path:%s, state:%d-%s, err:%s}",
+			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)
 			if zkEvent.Type != zk.EventNodeChildrenChanged {
 				continue
 			}
 			l.handleZkNodeEvent(zkEvent.Path, children, conf)
 		case <-l.client.done():
-			log.Warn("client.done(), listen(path{%s}, ServiceConfig{%#v}) goroutine exit now...", zkPath, conf)
+			logger.Warnf("client.done(), listen(path{%s}, ReferenceConfig{%#v}) goroutine exit now...", zkPath, conf)
 			return
 		}
 	}
@@ -223,22 +239,22 @@ func (l *zkEventListener) listenDirEvent(zkPath string, conf registry.ServiceCon
 // registry.go:Listen -> listenServiceEvent -> listenDirEvent -> listenServiceNodeEvent
 //                            |
 //                            --------> listenServiceNodeEvent
-func (l *zkEventListener) listenServiceEvent(conf registry.ServiceConfig) {
+func (l *zkEventListener) listenServiceEvent(conf common.URL) {
 	var (
 		err        error
 		zkPath     string
 		dubboPath  string
 		children   []string
-		serviceURL registry.ServiceURL
+		serviceURL common.URL
 	)
 
-	zkPath = fmt.Sprintf("/dubbo/%s/providers", conf.Service())
+	zkPath = fmt.Sprintf("/dubbo%s/providers", conf.Path)
 
 	l.serviceMapLock.Lock()
 	_, ok := l.serviceMap[zkPath]
 	l.serviceMapLock.Unlock()
 	if ok {
-		log.Warn("@zkPath %s has already been listened.", zkPath)
+		logger.Warnf("@zkPath %s has already been listened.", zkPath)
 		return
 	}
 
@@ -246,43 +262,42 @@ func (l *zkEventListener) listenServiceEvent(conf registry.ServiceConfig) {
 	l.serviceMap[zkPath] = struct{}{}
 	l.serviceMapLock.Unlock()
 
-	log.Info("listen dubbo provider path{%s} event and wait to get all provider zk nodes", zkPath)
+	logger.Infof("listen dubbo provider path{%s} event and wait to get all provider zk nodes", zkPath)
 	children, err = l.client.getChildren(zkPath)
 	if err != nil {
 		children = nil
-		log.Error("fail to get children of zk path{%s}", zkPath)
+		logger.Errorf("fail to get children of zk path{%s}", zkPath)
 	}
 
 	for _, c := range children {
-
-		serviceURL, err = plugins.DefaultServiceURL()(c)
+		serviceURL, err = common.NewURL(context.TODO(), c)
 		if err != nil {
-			log.Error("NewDefaultServiceURL(r{%s}) = error{%v}", c, err)
+			logger.Errorf("NewURL(r{%s}) = error{%v}", c, err)
 			continue
 		}
-		if !conf.ServiceEqual(serviceURL) {
-			log.Warn("serviceURL{%s} is not compatible with ServiceConfig{%#v}", serviceURL, conf)
+		if !conf.URLEqual(serviceURL) {
+			logger.Warnf("serviceURL %v is not compatible with SubURL %v", serviceURL.Key(), conf.Key())
 			continue
 		}
-		log.Debug("add serviceUrl{%s}", serviceURL)
+		logger.Debugf("add serviceUrl{%s}", serviceURL)
 		l.events <- zkEvent{&registry.ServiceEvent{Action: registry.ServiceAdd, Service: serviceURL}, nil}
 
 		// listen l service node
 		dubboPath = path.Join(zkPath, c)
-		log.Info("listen dubbo service key{%s}", dubboPath)
-		go func(zkPath string, serviceURL registry.ServiceURL) {
+		logger.Infof("listen dubbo service key{%s}", dubboPath)
+		go func(zkPath string, serviceURL common.URL) {
 			if l.listenServiceNodeEvent(dubboPath) {
-				log.Debug("delete serviceUrl{%s}", serviceURL)
+				logger.Debugf("delete serviceUrl{%s}", serviceURL)
 				l.events <- zkEvent{&registry.ServiceEvent{Action: registry.ServiceDel, Service: serviceURL}, nil}
 			}
-			log.Warn("listenSelf(zk path{%s}) goroutine exit now", zkPath)
+			logger.Warnf("listenSelf(zk path{%s}) goroutine exit now", zkPath)
 		}(dubboPath, serviceURL)
 	}
 
-	log.Info("listen dubbo path{%s}", zkPath)
-	go func(zkPath string, conf registry.ServiceConfig) {
+	logger.Infof("listen dubbo path{%s}", zkPath)
+	go func(zkPath string, conf common.URL) {
 		l.listenDirEvent(zkPath, conf)
-		log.Warn("listenDirEvent(zkPath{%s}) goroutine exit now", zkPath)
+		logger.Warnf("listenDirEvent(zkPath{%s}) goroutine exit now", zkPath)
 	}(zkPath, conf)
 }
 
@@ -290,20 +305,20 @@ func (l *zkEventListener) Next() (*registry.ServiceEvent, error) {
 	for {
 		select {
 		case <-l.client.done():
-			log.Warn("listener's zk client connection is broken, so zk event listener exit now.")
-			return nil, jerrors.New("listener stopped")
+			logger.Warnf("listener's zk client connection is broken, so zk event listener exit now.")
+			return nil, perrors.New("listener stopped")
 
 		case <-l.registry.done:
-			log.Warn("zk consumer register has quit, so zk event listener exit asap now.")
-			return nil, jerrors.New("listener stopped")
+			logger.Warnf("zk consumer register has quit, so zk event listener exit asap now.")
+			return nil, perrors.New("listener stopped")
 
 		case e := <-l.events:
-			log.Debug("got zk event %s", e)
+			logger.Debugf("got zk event %s", e)
 			if e.err != nil {
-				return nil, jerrors.Trace(e.err)
+				return nil, perrors.WithStack(e.err)
 			}
 			if e.res.Action == registry.ServiceDel && !l.valid() {
-				log.Warn("update @result{%s}. But its connection to registry is invalid", e.res)
+				logger.Warnf("update @result{%s}. But its connection to registry is invalid", e.res)
 				continue
 			}
 			//r.update(e.res)
diff --git a/registry/zookeeper/registry.go b/registry/zookeeper/registry.go
index 82c752ac77571930a023e173ec1ed985bec4a08c..4d553a1658a15416cbcf00c107aeffca11b5031b 100644
--- a/registry/zookeeper/registry.go
+++ b/registry/zookeeper/registry.go
@@ -1,23 +1,41 @@
+// Copyright 2016-2019 hxmhlt
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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 zookeeper
 
 import (
+	"context"
 	"fmt"
 	"net/url"
 	"os"
 	"strconv"
+	"strings"
 	"sync"
 	"time"
 )
 
 import (
-	"github.com/AlexStocks/goext/net"
-	log "github.com/AlexStocks/log4go"
-	jerrors "github.com/juju/errors"
+	"github.com/dubbo/go-for-apache-dubbo/common/logger"
+	perrors "github.com/pkg/errors"
 	"github.com/samuel/go-zookeeper/zk"
 )
 
 import (
-	"github.com/dubbo/go-for-apache-dubbo/plugins"
+	"github.com/dubbo/go-for-apache-dubbo/common"
+	"github.com/dubbo/go-for-apache-dubbo/common/constant"
+	"github.com/dubbo/go-for-apache-dubbo/common/extension"
+	"github.com/dubbo/go-for-apache-dubbo/common/utils"
 	"github.com/dubbo/go-for-apache-dubbo/registry"
 	"github.com/dubbo/go-for-apache-dubbo/version"
 )
@@ -35,53 +53,25 @@ var (
 
 func init() {
 	processID = fmt.Sprintf("%d", os.Getpid())
-	localIP, _ = gxnet.GetLocalIP()
-	plugins.PluggableRegistries["zookeeper"] = NewZkRegistry
-}
-
-type ZkRegistryConfig struct {
-	Address    []string      `required:"true" yaml:"address"  json:"address,omitempty"`
-	UserName   string        `yaml:"user_name" json:"user_name,omitempty"`
-	Password   string        `yaml:"password" json:"password,omitempty"`
-	TimeoutStr string        `yaml:"timeout" default:"5s" json:"timeout,omitempty"` // unit: second
-	Timeout    time.Duration `yaml:"-"  json:"-"`
-}
-
-type Options struct {
-	registry.Options
-	ZkRegistryConfig
-}
-
-func (o Options) ToString() string {
-	return fmt.Sprintf("%s, address:%+v, user:%s, password:%s, conn-timeout:%s",
-		o.Options, o.Address, o.UserName, o.Password, o.Timeout)
-}
-
-type Option func(*Options)
-
-func (Option) Name() string {
-	return "dubbogo-zookeeper-registry-option"
-}
-
-func WithRegistryConf(conf ZkRegistryConfig) Option {
-	return func(o *Options) {
-		o.ZkRegistryConfig = conf
-	}
+	localIP, _ = utils.GetLocalIP()
+	//plugins.PluggableRegistries["zookeeper"] = newZkRegistry
+	extension.SetRegistry("zookeeper", newZkRegistry)
 }
 
 /////////////////////////////////////
 // zookeeper registry
 /////////////////////////////////////
 
-type ZkRegistry struct {
-	Options
+type zkRegistry struct {
+	context context.Context
+	*common.URL
 	birth int64          // time of file birth, seconds since Epoch; 0 if unknown
 	wg    sync.WaitGroup // wg+done for zk restart
 	done  chan struct{}
 
 	cltLock  sync.Mutex
 	client   *zookeeperClient
-	services map[string]registry.ServiceConfig // service name + protocol -> service config
+	services map[string]common.URL // service name + protocol -> service config
 
 	listenerLock sync.Mutex
 	listener     *zkEventListener
@@ -90,65 +80,88 @@ type ZkRegistry struct {
 	zkPath map[string]int // key = protocol://ip:port/interface
 }
 
-func NewZkRegistry(opts ...registry.RegistryOption) (registry.Registry, error) {
+func newZkRegistry(url *common.URL) (registry.Registry, error) {
 	var (
 		err error
-		r   *ZkRegistry
+		r   *zkRegistry
 	)
 
-	r = &ZkRegistry{
+	r = &zkRegistry{
+		URL:      url,
 		birth:    time.Now().UnixNano(),
 		done:     make(chan struct{}),
-		services: make(map[string]registry.ServiceConfig),
+		services: make(map[string]common.URL),
 		zkPath:   make(map[string]int),
 	}
 
-	for _, opt := range opts {
-		if o, ok := opt.(Option); ok {
-			o(&r.Options)
-		} else if o, ok := opt.(registry.Option); ok {
-			o(&r.Options.Options)
-		} else {
-			return nil, jerrors.New("option is not available")
-		}
+	//if r.SubURL.Name == "" {
+	//	r.SubURL.Name = RegistryZkClient
+	//}
+	//if r.Version == "" {
+	//	r.Version = version.Version
+	//}
 
+	err = r.validateZookeeperClient()
+	if err != nil {
+		return nil, err
 	}
-	//if r.DubboType == 0{
-	//	return nil ,errors.New("Dubbo type should be specified.")
+
+	r.wg.Add(1)
+	go r.handleZkRestart()
+
+	//if r.RoleType == registry.CONSUMER {
+	//	r.wg.Add(1)
+	//	go r.listen()
 	//}
-	if r.Name == "" {
-		r.Name = RegistryZkClient
-	}
-	if r.Version == "" {
-		r.Version = version.Version
-	}
 
-	if r.ZkRegistryConfig.Timeout == 0 {
-		r.ZkRegistryConfig.Timeout = 1e9
+	return r, nil
+}
+
+func newMockZkRegistry(url *common.URL) (*zk.TestCluster, *zkRegistry, error) {
+	var (
+		err error
+		r   *zkRegistry
+		c   *zk.TestCluster
+		//event <-chan zk.Event
+	)
+
+	r = &zkRegistry{
+		URL:      url,
+		birth:    time.Now().UnixNano(),
+		done:     make(chan struct{}),
+		services: make(map[string]common.URL),
+		zkPath:   make(map[string]int),
 	}
-	err = r.validateZookeeperClient()
+
+	c, r.client, _, err = newMockZookeeperClient("test", 15*time.Second)
 	if err != nil {
-		return nil, jerrors.Trace(err)
+		return nil, nil, err
 	}
 
 	r.wg.Add(1)
 	go r.handleZkRestart()
 
-	//if r.DubboType == registry.CONSUMER {
+	//if r.RoleType == registry.CONSUMER {
 	//	r.wg.Add(1)
 	//	go r.listen()
 	//}
 
-	return r, nil
+	return c, r, nil
+}
+func (r *zkRegistry) GetUrl() common.URL {
+	return *r.URL
 }
 
-func (r *ZkRegistry) Close() {
+func (r *zkRegistry) Destroy() {
+	if r.listener != nil {
+		r.listener.Close()
+	}
 	close(r.done)
 	r.wg.Wait()
 	r.closeRegisters()
 }
 
-func (r *ZkRegistry) validateZookeeperClient() error {
+func (r *zkRegistry) validateZookeeperClient() error {
 	var (
 		err error
 	)
@@ -157,11 +170,18 @@ func (r *ZkRegistry) validateZookeeperClient() error {
 	r.cltLock.Lock()
 	defer r.cltLock.Unlock()
 	if r.client == nil {
-		r.client, err = newZookeeperClient(RegistryZkClient, r.Address, r.ZkRegistryConfig.Timeout)
+		//in dubbp ,every registry only connect one node ,so this is []string{r.Address}
+		timeout, err := time.ParseDuration(r.GetParam(constant.REGISTRY_TIMEOUT_KEY, constant.DEFAULT_REG_TIMEOUT))
 		if err != nil {
-			log.Warn("newZookeeperClient(name{%s}, zk addresss{%v}, timeout{%d}) = error{%v}",
-				RegistryZkClient, r.Address, r.Timeout.String(), err)
-			return jerrors.Annotatef(err, "newZookeeperClient(address:%+v)", r.Address)
+			logger.Errorf("timeout config %v is invalid ,err is %v",
+				r.GetParam(constant.REGISTRY_TIMEOUT_KEY, constant.DEFAULT_REG_TIMEOUT), err.Error())
+			return perrors.WithMessagef(err, "newZookeeperClient(address:%+v)", r.Location)
+		}
+		r.client, err = newZookeeperClient(RegistryZkClient, []string{r.Location}, timeout)
+		if err != nil {
+			logger.Warnf("newZookeeperClient(name{%s}, zk addresss{%v}, timeout{%d}) = error{%v}",
+				RegistryZkClient, r.Location, timeout.String(), err)
+			return perrors.WithMessagef(err, "newZookeeperClient(address:%+v)", r.Location)
 		}
 	}
 	if r.client.conn == nil {
@@ -173,16 +193,15 @@ func (r *ZkRegistry) validateZookeeperClient() error {
 		}
 	}
 
-	return jerrors.Annotatef(err, "newZookeeperClient(address:%+v)", r.Address)
+	return perrors.WithMessagef(err, "newZookeeperClient(address:%+v)", r.PrimitiveURL)
 }
 
-func (r *ZkRegistry) handleZkRestart() {
+func (r *zkRegistry) handleZkRestart() {
 	var (
 		err       error
 		flag      bool
 		failTimes int
-		confIf    registry.ServiceConfig
-		services  []registry.ServiceConfig
+		confIf    common.URL
 	)
 
 	defer r.wg.Done()
@@ -190,7 +209,7 @@ LOOP:
 	for {
 		select {
 		case <-r.done:
-			log.Warn("(ZkProviderRegistry)reconnectZkRegistry goroutine exit now...")
+			logger.Warnf("(ZkProviderRegistry)reconnectZkRegistry goroutine exit now...")
 			break LOOP
 			// re-register all services
 		case <-r.client.done():
@@ -204,30 +223,30 @@ LOOP:
 			for {
 				select {
 				case <-r.done:
-					log.Warn("(ZkProviderRegistry)reconnectZkRegistry goroutine exit now...")
+					logger.Warnf("(ZkProviderRegistry)reconnectZkRegistry goroutine exit now...")
 					break LOOP
 				case <-time.After(time.Duration(1e9 * failTimes * RegistryConnDelay)): // 闃叉鐤媯閲嶈繛zk
 				}
 				err = r.validateZookeeperClient()
-				log.Info("ZkProviderRegistry.validateZookeeperClient(zkAddr{%s}) = error{%#v}",
-					r.client.zkAddrs, jerrors.ErrorStack(err))
+				logger.Infof("ZkProviderRegistry.validateZookeeperClient(zkAddr{%s}) = error{%#v}",
+					r.client.zkAddrs, perrors.WithStack(err))
 				if err == nil {
 					// copy r.services
-					r.cltLock.Lock()
+					services := []common.URL{}
 					for _, confIf = range r.services {
 						services = append(services, confIf)
 					}
-					r.cltLock.Unlock()
 
 					flag = true
 					for _, confIf = range services {
 						err = r.register(confIf)
 						if err != nil {
-							log.Error("(ZkProviderRegistry)register(conf{%#v}) = error{%#v}",
-								confIf, jerrors.ErrorStack(err))
+							logger.Errorf("(ZkProviderRegistry)register(conf{%#v}) = error{%#v}",
+								confIf, perrors.WithStack(err))
 							flag = false
 							break
 						}
+						logger.Infof("success to re-register service :%v", confIf.Key())
 					}
 					if flag {
 						break
@@ -242,31 +261,32 @@ LOOP:
 	}
 }
 
-func (r *ZkRegistry) Register(conf registry.ServiceConfig) error {
+func (r *zkRegistry) Register(conf common.URL) error {
 	var (
 		ok       bool
 		err      error
 		listener *zkEventListener
 	)
-	switch r.DubboType {
-	case registry.CONSUMER:
+	role, _ := strconv.Atoi(r.URL.GetParam(constant.ROLE_KEY, ""))
+	switch role {
+	case common.CONSUMER:
 		ok = false
 		r.cltLock.Lock()
 		_, ok = r.services[conf.Key()]
 		r.cltLock.Unlock()
 		if ok {
-			return jerrors.Errorf("Service{%s} has been registered", conf.Service())
+			return perrors.Errorf("Path{%s} has been registered", conf.Path)
 		}
 
 		err = r.register(conf)
 		if err != nil {
-			return jerrors.Trace(err)
+			return perrors.WithStack(err)
 		}
 
 		r.cltLock.Lock()
 		r.services[conf.Key()] = conf
 		r.cltLock.Unlock()
-		log.Debug("(consumerZkConsumerRegistry)Register(conf{%#v})", conf)
+		logger.Debugf("(consumerZkConsumerRegistry)Register(conf{%#v})", conf)
 
 		r.listenerLock.Lock()
 		listener = r.listener
@@ -274,178 +294,150 @@ func (r *ZkRegistry) Register(conf registry.ServiceConfig) error {
 		if listener != nil {
 			go listener.listenServiceEvent(conf)
 		}
-	case registry.PROVIDER:
+	case common.PROVIDER:
 
 		// 妫€楠屾湇鍔℃槸鍚﹀凡缁忔敞鍐岃繃
 		ok = false
 		r.cltLock.Lock()
-		// 娉ㄦ剰姝ゅ涓巆onsumerZookeeperRegistry鐨勫樊寮傦紝consumer鐢ㄧ殑鏄痗onf.Service锛�
+		// 娉ㄦ剰姝ゅ涓巆onsumerZookeeperRegistry鐨勫樊寮傦紝consumer鐢ㄧ殑鏄痗onf.Path锛�
 		// 鍥犱负consumer瑕佹彁渚泈atch鍔熻兘缁檚elector浣跨敤, provider鍏佽娉ㄥ唽鍚屼竴涓猻ervice鐨勫涓猤roup or version
-		_, ok = r.services[conf.String()]
+		_, ok = r.services[conf.Key()]
 		r.cltLock.Unlock()
 		if ok {
-			return jerrors.Errorf("Service{%s} has been registered", conf.String())
+			return perrors.Errorf("Path{%s} has been registered", conf.Key())
 		}
 
 		err = r.register(conf)
 		if err != nil {
-			return jerrors.Annotatef(err, "register(conf:%+v)", conf)
+			return perrors.WithMessagef(err, "register(conf:%+v)", conf)
 		}
 
 		r.cltLock.Lock()
-		r.services[conf.String()] = conf
+		r.services[conf.Key()] = conf
 		r.cltLock.Unlock()
 
-		log.Debug("(ZkProviderRegistry)Register(conf{%#v})", conf)
+		logger.Debugf("(ZkProviderRegistry)Register(conf{%#v})", conf)
 	}
 
 	return nil
 }
 
-func (r *ZkRegistry) register(c registry.ServiceConfig) error {
+func (r *zkRegistry) register(c common.URL) error {
 	var (
-		err        error
-		revision   string
+		err error
+		//revision   string
 		params     url.Values
 		urlPath    string
 		rawURL     string
 		encodedURL string
 		dubboPath  string
-		conf       registry.ProviderServiceConfig
-		ok         bool
+		//conf       config.URL
 	)
 
 	err = r.validateZookeeperClient()
 	if err != nil {
-		return jerrors.Trace(err)
+		return perrors.WithStack(err)
 	}
 	params = url.Values{}
+	for k, v := range c.Params {
+		params[k] = v
+	}
 
-	params.Add("application", r.ApplicationConfig.Name)
-	params.Add("default.timeout", fmt.Sprintf("%d", defaultTimeout/1e6))
-	params.Add("environment", r.ApplicationConfig.Environment)
-	params.Add("org", r.ApplicationConfig.Organization)
-	params.Add("module", r.ApplicationConfig.Module)
-	params.Add("owner", r.ApplicationConfig.Owner)
 	params.Add("pid", processID)
 	params.Add("ip", localIP)
-	params.Add("timeout", fmt.Sprintf("%d", int64(r.Timeout)/1e6))
-	params.Add("timestamp", fmt.Sprintf("%d", r.birth/1e6))
+	//params.Add("timeout", fmt.Sprintf("%d", int64(r.Timeout)/1e6))
 
-	revision = r.ApplicationConfig.Version
-	if revision == "" {
-		revision = "0.1.0"
-	}
-	params.Add("revision", revision) // revision鏄痯ox.xml涓璦pplication鐨剉ersion灞炴€х殑鍊�
+	role, _ := strconv.Atoi(r.URL.GetParam(constant.ROLE_KEY, ""))
+	switch role {
 
-	switch r.DubboType {
+	case common.PROVIDER:
 
-	case registry.PROVIDER:
-		if conf, ok = c.(registry.ProviderServiceConfig); !ok {
-			return jerrors.Errorf("conf is not ProviderServiceConfig")
-		}
-		if conf.Service() == "" || conf.Methods() == "" {
-			return jerrors.Errorf("conf{Service:%s, Methods:%s}", conf.Service(), conf.Methods())
+		if c.Path == "" || len(c.Methods) == 0 {
+			return perrors.Errorf("conf{Path:%s, Methods:%s}", c.Path, c.Methods)
 		}
 		// 鍏堝垱寤烘湇鍔′笅闈㈢殑provider node
-		dubboPath = fmt.Sprintf("/dubbo/%s/%s", conf.Service(), registry.DubboNodes[registry.PROVIDER])
+		dubboPath = fmt.Sprintf("/dubbo%s/%s", c.Path, common.DubboNodes[common.PROVIDER])
 		r.cltLock.Lock()
 		err = r.client.Create(dubboPath)
 		r.cltLock.Unlock()
 		if err != nil {
-			log.Error("zkClient.create(path{%s}) = error{%#v}", dubboPath, jerrors.ErrorStack(err))
-			return jerrors.Annotatef(err, "zkclient.Create(path:%s)", dubboPath)
+			logger.Errorf("zkClient.create(path{%s}) = error{%#v}", dubboPath, perrors.WithStack(err))
+			return perrors.WithMessagef(err, "zkclient.Create(path:%s)", dubboPath)
 		}
 		params.Add("anyhost", "true")
-		params.Add("interface", conf.Service())
 
-		if conf.Group() != "" {
-			params.Add("group", conf.Group())
-		}
 		// dubbo java consumer鏉ュ惎鍔ㄦ壘provider url鏃讹紝鍥犱负category涓嶅尮閰嶏紝浼氭壘涓嶅埌provider锛屽鑷碿onsumer鍚姩涓嶄簡,鎵€浠ヤ娇鐢╟onsumers&providers
 		// DubboRole               = [...]string{"consumer", "", "", "provider"}
-		// params.Add("category", (DubboType(PROVIDER)).Role())
-		params.Add("category", (registry.DubboType(registry.PROVIDER)).String())
+		// params.Add("category", (RoleType(PROVIDER)).Role())
+		params.Add("category", (common.RoleType(common.PROVIDER)).String())
 		params.Add("dubbo", "dubbo-provider-golang-"+version.Version)
 
-		params.Add("side", (registry.DubboType(registry.PROVIDER)).Role())
+		params.Add("side", (common.RoleType(common.PROVIDER)).Role())
 
-		if conf.Version() != "" {
-			params.Add("version", conf.Version())
-		}
-		if conf.Methods() != "" {
-			params.Add("methods", conf.Methods())
+		if len(c.Methods) == 0 {
+			params.Add("methods", strings.Join(c.Methods, ","))
 		}
-		log.Debug("provider zk url params:%#v", params)
-		var path = conf.Path()
-		if path == "" {
-			path = localIP
+		logger.Debugf("provider zk url params:%#v", params)
+		var host string
+		if c.Ip == "" {
+			host = localIP + ":" + c.Port
+		} else {
+			host = c.Ip + ":" + c.Port
 		}
 
-		urlPath = conf.Service()
+		urlPath = c.Path
 		if r.zkPath[urlPath] != 0 {
 			urlPath += strconv.Itoa(r.zkPath[urlPath])
 		}
 		r.zkPath[urlPath]++
-		rawURL = fmt.Sprintf("%s://%s/%s?%s", conf.Protocol(), path, urlPath, params.Encode())
+		rawURL = fmt.Sprintf("%s://%s%s?%s", c.Protocol, host, urlPath, params.Encode())
 		encodedURL = url.QueryEscape(rawURL)
 
 		// 鎶婅嚜宸辨敞鍐宻ervice providers
-		dubboPath = fmt.Sprintf("/dubbo/%s/%s", conf.Service(), (registry.DubboType(registry.PROVIDER)).String())
-		log.Debug("provider path:%s, url:%s", dubboPath, rawURL)
+		dubboPath = fmt.Sprintf("/dubbo%s/%s", c.Path, (common.RoleType(common.PROVIDER)).String())
+		logger.Debugf("provider path:%s, url:%s", dubboPath, rawURL)
 
-	case registry.CONSUMER:
-		dubboPath = fmt.Sprintf("/dubbo/%s/%s", c.Service(), registry.DubboNodes[registry.CONSUMER])
+	case common.CONSUMER:
+		dubboPath = fmt.Sprintf("/dubbo%s/%s", c.Path, common.DubboNodes[common.CONSUMER])
 		r.cltLock.Lock()
 		err = r.client.Create(dubboPath)
 		r.cltLock.Unlock()
 		if err != nil {
-			log.Error("zkClient.create(path{%s}) = error{%v}", dubboPath, jerrors.ErrorStack(err))
-			return jerrors.Trace(err)
+			logger.Errorf("zkClient.create(path{%s}) = error{%v}", dubboPath, perrors.WithStack(err))
+			return perrors.WithStack(err)
 		}
-		dubboPath = fmt.Sprintf("/dubbo/%s/%s", c.Service(), registry.DubboNodes[registry.PROVIDER])
+		dubboPath = fmt.Sprintf("/dubbo%s/%s", c.Path, common.DubboNodes[common.PROVIDER])
 		r.cltLock.Lock()
 		err = r.client.Create(dubboPath)
 		r.cltLock.Unlock()
 		if err != nil {
-			log.Error("zkClient.create(path{%s}) = error{%v}", dubboPath, jerrors.ErrorStack(err))
-			return jerrors.Trace(err)
+			logger.Errorf("zkClient.create(path{%s}) = error{%v}", dubboPath, perrors.WithStack(err))
+			return perrors.WithStack(err)
 		}
 
-		params.Add("protocol", c.Protocol())
-		params.Add("interface", c.Service())
-		revision = r.ApplicationConfig.Version
-		if revision == "" {
-			revision = "0.1.0"
-		}
-		params.Add("revision", revision)
-		if c.Group() != "" {
-			params.Add("group", c.Group())
-		}
-		params.Add("category", (registry.DubboType(registry.CONSUMER)).String())
+		params.Add("protocol", c.Protocol)
+
+		params.Add("category", (common.RoleType(common.CONSUMER)).String())
 		params.Add("dubbo", "dubbogo-consumer-"+version.Version)
 
-		if c.Version() != "" {
-			params.Add("version", c.Version())
-		}
-		rawURL = fmt.Sprintf("consumer://%s/%s?%s", localIP, c.Service()+c.Version(), params.Encode())
+		rawURL = fmt.Sprintf("consumer://%s%s?%s", localIP, c.Path, params.Encode())
 		encodedURL = url.QueryEscape(rawURL)
 
-		dubboPath = fmt.Sprintf("/dubbo/%s/%s", c.Service(), (registry.DubboType(registry.CONSUMER)).String())
-		log.Debug("consumer path:%s, url:%s", dubboPath, rawURL)
+		dubboPath = fmt.Sprintf("/dubbo%s/%s", c.Path, (common.RoleType(common.CONSUMER)).String())
+		logger.Debugf("consumer path:%s, url:%s", dubboPath, rawURL)
 	default:
-		return jerrors.Errorf("@c{%v} type is not DefaultServiceConfig or DefaultProviderServiceConfig", c)
+		return perrors.Errorf("@c{%v} type is not referencer or provider", c)
 	}
 
 	err = r.registerTempZookeeperNode(dubboPath, encodedURL)
 
 	if err != nil {
-		return jerrors.Annotatef(err, "registerTempZookeeperNode(path:%s, url:%s)", dubboPath, rawURL)
+		return perrors.WithMessagef(err, "registerTempZookeeperNode(path:%s, url:%s)", dubboPath, rawURL)
 	}
 	return nil
 }
 
-func (r *ZkRegistry) registerTempZookeeperNode(root string, node string) error {
+func (r *zkRegistry) registerTempZookeeperNode(root string, node string) error {
 	var (
 		err    error
 		zkPath string
@@ -455,34 +447,77 @@ func (r *ZkRegistry) registerTempZookeeperNode(root string, node string) error {
 	defer r.cltLock.Unlock()
 	err = r.client.Create(root)
 	if err != nil {
-		log.Error("zk.Create(root{%s}) = err{%v}", root, jerrors.ErrorStack(err))
-		return jerrors.Trace(err)
+		logger.Errorf("zk.Create(root{%s}) = err{%v}", root, perrors.WithStack(err))
+		return perrors.WithStack(err)
 	}
 	zkPath, err = r.client.RegisterTemp(root, node)
 	if err != nil {
-		log.Error("RegisterTempNode(root{%s}, node{%s}) = error{%v}", root, node, jerrors.ErrorStack(err))
-		return jerrors.Annotatef(err, "RegisterTempNode(root{%s}, node{%s})", root, node)
+		logger.Errorf("RegisterTempNode(root{%s}, node{%s}) = error{%v}", root, node, perrors.WithStack(err))
+		return perrors.WithMessagef(err, "RegisterTempNode(root{%s}, node{%s})", root, node)
 	}
-	log.Debug("create a zookeeper node:%s", zkPath)
+	logger.Debugf("create a zookeeper node:%s", zkPath)
 
 	return nil
 }
 
-func (r *ZkRegistry) closeRegisters() {
+func (r *zkRegistry) Subscribe(conf common.URL) (registry.Listener, error) {
+	r.wg.Add(1)
+	return r.getListener(conf)
+}
+
+func (r *zkRegistry) getListener(conf common.URL) (*zkEventListener, error) {
+	var (
+		zkListener *zkEventListener
+	)
+
+	r.listenerLock.Lock()
+	zkListener = r.listener
+	r.listenerLock.Unlock()
+	if zkListener != nil {
+		return zkListener, nil
+	}
+
+	r.cltLock.Lock()
+	client := r.client
+	r.cltLock.Unlock()
+	if client == nil {
+		return nil, perrors.New("zk connection broken")
+	}
+
+	// new client & listener
+	zkListener = newZkEventListener(r, client)
+
+	r.listenerLock.Lock()
+	r.listener = zkListener
+	r.listenerLock.Unlock()
+
+	// listen
+	r.cltLock.Lock()
+	for _, svs := range r.services {
+		if svs.URLEqual(conf) {
+			go zkListener.listenServiceEvent(svs)
+		}
+	}
+	r.cltLock.Unlock()
+
+	return zkListener, nil
+}
+
+func (r *zkRegistry) closeRegisters() {
 	r.cltLock.Lock()
 	defer r.cltLock.Unlock()
-	log.Info("begin to close provider zk client")
+	logger.Infof("begin to close provider zk client")
 	// 鍏堝叧闂棫client锛屼互鍏抽棴tmp node
 	r.client.Close()
 	r.client = nil
 	r.services = nil
 }
 
-func (r *ZkRegistry) IsClosed() bool {
+func (r *zkRegistry) IsAvailable() bool {
 	select {
 	case <-r.done:
-		return true
-	default:
 		return false
+	default:
+		return true
 	}
 }
diff --git a/registry/zookeeper/registry_test.go b/registry/zookeeper/registry_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..3d230bb0a98eef74087f30e1adcf10427433ef7d
--- /dev/null
+++ b/registry/zookeeper/registry_test.go
@@ -0,0 +1,107 @@
+// Copyright 2016-2019 hxmhlt
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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 zookeeper
+
+import (
+	"context"
+	"strconv"
+	"testing"
+	"time"
+)
+import (
+	"github.com/stretchr/testify/assert"
+)
+import (
+	"github.com/dubbo/go-for-apache-dubbo/common"
+	"github.com/dubbo/go-for-apache-dubbo/common/constant"
+)
+
+func Test_Register(t *testing.T) {
+	regurl, _ := common.NewURL(context.TODO(), "registry://127.0.0.1:1111", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER)))
+	url, _ := common.NewURL(context.TODO(), "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParamsValue(constant.CLUSTER_KEY, "mock"), common.WithMethods([]string{"GetUser", "AddUser"}))
+
+	ts, reg, err := newMockZkRegistry(&regurl)
+	defer ts.Stop()
+	err = reg.Register(url)
+	children, _ := reg.client.getChildren("/dubbo/com.ikurento.user.UserProvider/providers")
+	assert.Regexp(t, ".*dubbo%3A%2F%2F127.0.0.1%3A20000%2Fcom.ikurento.user.UserProvider%3Fanyhost%3Dtrue%26category%3Dproviders%26cluster%3Dmock%26dubbo%3Ddubbo-provider-golang-2.6.0%26.*provider", children)
+	assert.NoError(t, err)
+}
+
+func Test_Subscribe(t *testing.T) {
+	regurl, _ := common.NewURL(context.TODO(), "registry://127.0.0.1:1111", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER)))
+	url, _ := common.NewURL(context.TODO(), "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParamsValue(constant.CLUSTER_KEY, "mock"), common.WithMethods([]string{"GetUser", "AddUser"}))
+	ts, reg, err := newMockZkRegistry(&regurl)
+	defer ts.Stop()
+
+	//provider register
+	err = reg.Register(url)
+	assert.NoError(t, err)
+
+	if err != nil {
+		return
+	}
+
+	//consumer register
+	regurl.Params.Set(constant.ROLE_KEY, strconv.Itoa(common.CONSUMER))
+	_, reg2, err := newMockZkRegistry(&regurl)
+	reg2.client = reg.client
+	err = reg2.Register(url)
+	listener, err := reg2.Subscribe(url)
+
+	serviceEvent, err := listener.Next()
+	assert.NoError(t, err)
+	if err != nil {
+		return
+	}
+	assert.Regexp(t, ".*ServiceEvent{Action{add service}.*", serviceEvent.String())
+
+}
+
+func Test_ConsumerDestory(t *testing.T) {
+	regurl, _ := common.NewURL(context.TODO(), "registry://127.0.0.1:1111", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.CONSUMER)))
+	url, _ := common.NewURL(context.TODO(), "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParamsValue(constant.CLUSTER_KEY, "mock"), common.WithMethods([]string{"GetUser", "AddUser"}))
+
+	ts, reg, err := newMockZkRegistry(&regurl)
+	defer ts.Stop()
+
+	assert.NoError(t, err)
+	err = reg.Register(url)
+	assert.NoError(t, err)
+	_, err = reg.Subscribe(url)
+	assert.NoError(t, err)
+
+	//listener.Close()
+	time.Sleep(1e9)
+	reg.Destroy()
+	assert.Equal(t, false, reg.IsAvailable())
+
+}
+
+func Test_ProviderDestory(t *testing.T) {
+	regurl, _ := common.NewURL(context.TODO(), "registry://127.0.0.1:1111", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER)))
+	url, _ := common.NewURL(context.TODO(), "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParamsValue(constant.CLUSTER_KEY, "mock"), common.WithMethods([]string{"GetUser", "AddUser"}))
+
+	ts, reg, err := newMockZkRegistry(&regurl)
+	defer ts.Stop()
+
+	assert.NoError(t, err)
+	err = reg.Register(url)
+
+	//listener.Close()
+	time.Sleep(1e9)
+	reg.Destroy()
+	assert.Equal(t, false, reg.IsAvailable())
+}
diff --git a/registry/zookeeper/zk_client.go b/registry/zookeeper/zk_client.go
index c1cd2c938928e77ea1d0626311c52a33c55d4379..3721cc7b3223178f17507f761e9a8add69241188 100644
--- a/registry/zookeeper/zk_client.go
+++ b/registry/zookeeper/zk_client.go
@@ -1,7 +1,20 @@
+// Copyright 2016-2019 hxmhlt
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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 zookeeper
 
 import (
-	"errors"
 	"path"
 	"strings"
 	"sync"
@@ -9,13 +22,13 @@ import (
 )
 
 import (
-	log "github.com/AlexStocks/log4go"
-	jerrors "github.com/juju/errors"
+	"github.com/dubbo/go-for-apache-dubbo/common/logger"
+	perrors "github.com/pkg/errors"
 	"github.com/samuel/go-zookeeper/zk"
 )
 
 var (
-	errNilZkClientConn = errors.New("zookeeperclient{conn} is nil")
+	errNilZkClientConn = perrors.New("zookeeperclient{conn} is nil")
 )
 
 type zookeeperClient struct {
@@ -44,7 +57,7 @@ func stateToString(state zk.State) string {
 	case zk.StateExpired:
 		return "zookeeper connection expired"
 	case zk.StateConnected:
-		return "zookeeper conneced"
+		return "zookeeper connected"
 	case zk.StateHasSession:
 		return "zookeeper has session"
 	case zk.StateUnknown:
@@ -81,7 +94,7 @@ func newZookeeperClient(name string, zkAddrs []string, timeout time.Duration) (*
 	// connect to zookeeper
 	z.conn, event, err = zk.Connect(zkAddrs, timeout)
 	if err != nil {
-		return nil, jerrors.Annotatef(err, "zk.Connect(zkAddrs:%+v)", zkAddrs)
+		return nil, perrors.WithMessagef(err, "zk.Connect(zkAddrs:%+v)", zkAddrs)
 	}
 
 	z.wait.Add(1)
@@ -89,6 +102,40 @@ func newZookeeperClient(name string, zkAddrs []string, timeout time.Duration) (*
 
 	return z, nil
 }
+func newMockZookeeperClient(name string, timeout time.Duration) (*zk.TestCluster, *zookeeperClient, <-chan zk.Event, error) {
+	var (
+		err   error
+		event <-chan zk.Event
+		z     *zookeeperClient
+	)
+
+	z = &zookeeperClient{
+		name:          name,
+		zkAddrs:       []string{},
+		timeout:       timeout,
+		exit:          make(chan struct{}),
+		eventRegistry: make(map[string][]*chan struct{}),
+	}
+	// connect to zookeeper
+
+	ts, err := zk.StartTestCluster(1, nil, nil)
+	if err != nil {
+		return nil, nil, nil, perrors.WithMessagef(err, "zk.Connect")
+	}
+
+	//callbackChan := make(chan zk.Event)
+	//f := func(event zk.Event) {
+	//	callbackChan <- event
+	//}
+
+	z.conn, event, err = ts.ConnectWithOptions(timeout)
+	if err != nil {
+		return nil, nil, nil, perrors.WithMessagef(err, "zk.Connect")
+	}
+	//z.wait.Add(1)
+
+	return ts, z, event, nil
+}
 
 func (z *zookeeperClient) handleZkEvent(session <-chan zk.Event) {
 	var (
@@ -98,7 +145,7 @@ func (z *zookeeperClient) handleZkEvent(session <-chan zk.Event) {
 
 	defer func() {
 		z.wait.Done()
-		log.Info("zk{path:%v, name:%s} connection goroutine game over.", z.zkAddrs, z.name)
+		logger.Infof("zk{path:%v, name:%s} connection goroutine game over.", z.zkAddrs, z.name)
 	}()
 
 LOOP:
@@ -107,11 +154,11 @@ LOOP:
 		case <-z.exit:
 			break LOOP
 		case event = <-session:
-			log.Warn("client{%s} get a zookeeper event{type:%s, server:%s, path:%s, state:%d-%s, err:%v}",
+			logger.Warnf("client{%s} get a zookeeper event{type:%s, server:%s, path:%s, state:%d-%s, err:%v}",
 				z.name, event.Type, event.Server, event.Path, event.State, stateToString(event.State), event.Err)
 			switch (int)(event.State) {
 			case (int)(zk.StateDisconnected):
-				log.Warn("zk{addr:%s} state is StateDisconnected, so close the zk client{name:%s}.", z.zkAddrs, z.name)
+				logger.Warnf("zk{addr:%s} state is StateDisconnected, so close the zk client{name:%s}.", z.zkAddrs, z.name)
 				z.stop()
 				z.Lock()
 				if z.conn != nil {
@@ -121,11 +168,11 @@ LOOP:
 				z.Unlock()
 				break LOOP
 			case (int)(zk.EventNodeDataChanged), (int)(zk.EventNodeChildrenChanged):
-				log.Info("zkClient{%s} get zk node changed event{path:%s}", z.name, event.Path)
+				logger.Infof("zkClient{%s} get zk node changed event{path:%s}", z.name, event.Path)
 				z.Lock()
 				for p, a := range z.eventRegistry {
 					if strings.HasPrefix(p, event.Path) {
-						log.Info("send event{state:zk.EventNodeDataChange, Path:%s} notify event to path{%s} related listener",
+						logger.Infof("send event{state:zk.EventNodeDataChange, Path:%s} notify event to path{%s} related listener",
 							event.Path, p)
 						for _, e := range a {
 							*e <- struct{}{}
@@ -134,7 +181,7 @@ LOOP:
 				}
 				z.Unlock()
 			case (int)(zk.StateConnecting), (int)(zk.StateConnected), (int)(zk.StateHasSession):
-				if state != (int)(zk.StateConnecting) || state != (int)(zk.StateDisconnected) {
+				if state == (int)(zk.StateHasSession) {
 					continue
 				}
 				if a, ok := z.eventRegistry[event.Path]; ok && 0 < len(a) {
@@ -157,7 +204,7 @@ func (z *zookeeperClient) registerEvent(zkPath string, event *chan struct{}) {
 	a := z.eventRegistry[zkPath]
 	a = append(a, event)
 	z.eventRegistry[zkPath] = a
-	log.Debug("zkClient{%s} register event{path:%s, ptr:%p}", z.name, zkPath, event)
+	logger.Debugf("zkClient{%s} register event{path:%s, ptr:%p}", z.name, zkPath, event)
 	z.Unlock()
 }
 
@@ -176,17 +223,16 @@ func (z *zookeeperClient) unregisterEvent(zkPath string, event *chan struct{}) {
 			if e == event {
 				arr := a
 				a = append(arr[:i], arr[i+1:]...)
-				log.Debug("zkClient{%s} unregister event{path:%s, event:%p}", z.name, zkPath, event)
+				logger.Debugf("zkClient{%s} unregister event{path:%s, event:%p}", z.name, zkPath, event)
 			}
 		}
-		log.Debug("after zkClient{%s} unregister event{path:%s, event:%p}, array length %d",
+		logger.Debugf("after zkClient{%s} unregister event{path:%s, event:%p}, array length %d",
 			z.name, zkPath, event, len(a))
 		if len(a) == 0 {
 			delete(z.eventRegistry, zkPath)
 		} else {
 			z.eventRegistry[zkPath] = a
 		}
-		break
 	}
 	z.Unlock()
 }
@@ -224,6 +270,10 @@ func (z *zookeeperClient) zkConnValid() bool {
 }
 
 func (z *zookeeperClient) Close() {
+	if z == nil {
+		return
+	}
+
 	z.stop()
 	z.wait.Wait()
 	z.Lock()
@@ -232,7 +282,7 @@ func (z *zookeeperClient) Close() {
 		z.conn = nil
 	}
 	z.Unlock()
-	log.Warn("zkClient{name:%s, zk addr:%s} exit now.", z.name, z.zkAddrs)
+	logger.Warnf("zkClient{name:%s, zk addr:%s} exit now.", z.name, z.zkAddrs)
 }
 
 func (z *zookeeperClient) Create(basePath string) error {
@@ -241,7 +291,7 @@ func (z *zookeeperClient) Create(basePath string) error {
 		tmpPath string
 	)
 
-	log.Debug("zookeeperClient.Create(basePath{%s})", basePath)
+	logger.Debugf("zookeeperClient.Create(basePath{%s})", basePath)
 	for _, str := range strings.Split(basePath, "/")[1:] {
 		tmpPath = path.Join(tmpPath, "/", str)
 		err = errNilZkClientConn
@@ -252,10 +302,10 @@ func (z *zookeeperClient) Create(basePath string) error {
 		z.Unlock()
 		if err != nil {
 			if err == zk.ErrNodeExists {
-				log.Error("zk.create(\"%s\") exists\n", tmpPath)
+				logger.Errorf("zk.create(\"%s\") exists\n", tmpPath)
 			} else {
-				log.Error("zk.create(\"%s\") error(%v)\n", tmpPath, jerrors.ErrorStack(err))
-				return jerrors.Annotatef(err, "zk.Create(path:%s)", basePath)
+				logger.Errorf("zk.create(\"%s\") error(%v)\n", tmpPath, perrors.WithStack(err))
+				return perrors.WithMessagef(err, "zk.Create(path:%s)", basePath)
 			}
 		}
 	}
@@ -275,7 +325,7 @@ func (z *zookeeperClient) Delete(basePath string) error {
 	}
 	z.Unlock()
 
-	return jerrors.Annotatef(err, "Delete(basePath:%s)", basePath)
+	return perrors.WithMessagef(err, "Delete(basePath:%s)", basePath)
 }
 
 func (z *zookeeperClient) RegisterTemp(basePath string, node string) (string, error) {
@@ -296,10 +346,10 @@ func (z *zookeeperClient) RegisterTemp(basePath string, node string) (string, er
 	z.Unlock()
 	//if err != nil && err != zk.ErrNodeExists {
 	if err != nil {
-		log.Error("conn.Create(\"%s\", zk.FlagEphemeral) = error(%v)\n", zkPath, jerrors.ErrorStack(err))
-		return "", jerrors.Trace(err)
+		logger.Warnf("conn.Create(\"%s\", zk.FlagEphemeral) = error(%v)\n", zkPath, perrors.WithStack(err))
+		return "", perrors.WithStack(err)
 	}
-	log.Debug("zkClient{%s} create a temp zookeeper node:%s\n", z.name, tmpPath)
+	logger.Debugf("zkClient{%s} create a temp zookeeper node:%s\n", z.name, tmpPath)
 
 	return tmpPath, nil
 }
@@ -321,13 +371,13 @@ func (z *zookeeperClient) RegisterTempSeq(basePath string, data []byte) (string,
 		)
 	}
 	z.Unlock()
-	log.Debug("zookeeperClient.RegisterTempSeq(basePath{%s}) = tempPath{%s}", basePath, tmpPath)
+	logger.Debugf("zookeeperClient.RegisterTempSeq(basePath{%s}) = tempPath{%s}", basePath, tmpPath)
 	if err != nil && err != zk.ErrNodeExists {
-		log.Error("zkClient{%s} conn.Create(\"%s\", \"%s\", zk.FlagEphemeral|zk.FlagSequence) error(%v)\n",
+		logger.Errorf("zkClient{%s} conn.Create(\"%s\", \"%s\", zk.FlagEphemeral|zk.FlagSequence) error(%v)\n",
 			z.name, basePath, string(data), err)
-		return "", jerrors.Trace(err)
+		return "", perrors.WithStack(err)
 	}
-	log.Debug("zkClient{%s} create a temp zookeeper node:%s\n", z.name, tmpPath)
+	logger.Debugf("zkClient{%s} create a temp zookeeper node:%s\n", z.name, tmpPath)
 
 	return tmpPath, nil
 }
@@ -348,16 +398,16 @@ func (z *zookeeperClient) getChildrenW(path string) ([]string, <-chan zk.Event,
 	z.Unlock()
 	if err != nil {
 		if err == zk.ErrNoNode {
-			return nil, nil, jerrors.Errorf("path{%s} has none children", path)
+			return nil, nil, perrors.Errorf("path{%s} has none children", path)
 		}
-		log.Error("zk.ChildrenW(path{%s}) = error(%v)", path, err)
-		return nil, nil, jerrors.Annotatef(err, "zk.ChildrenW(path:%s)", path)
+		logger.Errorf("zk.ChildrenW(path{%s}) = error(%v)", path, err)
+		return nil, nil, perrors.WithMessagef(err, "zk.ChildrenW(path:%s)", path)
 	}
 	if stat == nil {
-		return nil, nil, jerrors.Errorf("path{%s} has none children", path)
+		return nil, nil, perrors.Errorf("path{%s} has none children", path)
 	}
 	if len(children) == 0 {
-		return nil, nil, jerrors.Errorf("path{%s} has none children", path)
+		return nil, nil, perrors.Errorf("path{%s} has none children", path)
 	}
 
 	return children, event, nil
@@ -378,16 +428,16 @@ func (z *zookeeperClient) getChildren(path string) ([]string, error) {
 	z.Unlock()
 	if err != nil {
 		if err == zk.ErrNoNode {
-			return nil, jerrors.Errorf("path{%s} has none children", path)
+			return nil, perrors.Errorf("path{%s} has none children", path)
 		}
-		log.Error("zk.Children(path{%s}) = error(%v)", path, jerrors.ErrorStack(err))
-		return nil, jerrors.Annotatef(err, "zk.Children(path:%s)", path)
+		logger.Errorf("zk.Children(path{%s}) = error(%v)", path, perrors.WithStack(err))
+		return nil, perrors.WithMessagef(err, "zk.Children(path:%s)", path)
 	}
 	if stat == nil {
-		return nil, jerrors.Errorf("path{%s} has none children", path)
+		return nil, perrors.Errorf("path{%s} has none children", path)
 	}
 	if len(children) == 0 {
-		return nil, jerrors.Errorf("path{%s} has none children", path)
+		return nil, perrors.Errorf("path{%s} has none children", path)
 	}
 
 	return children, nil
@@ -407,12 +457,12 @@ func (z *zookeeperClient) existW(zkPath string) (<-chan zk.Event, error) {
 	}
 	z.Unlock()
 	if err != nil {
-		log.Error("zkClient{%s}.ExistsW(path{%s}) = error{%v}.", z.name, zkPath, jerrors.ErrorStack(err))
-		return nil, jerrors.Annotatef(err, "zk.ExistsW(path:%s)", zkPath)
+		logger.Errorf("zkClient{%s}.ExistsW(path{%s}) = error{%v}.", z.name, zkPath, perrors.WithStack(err))
+		return nil, perrors.WithMessagef(err, "zk.ExistsW(path:%s)", zkPath)
 	}
 	if !exist {
-		log.Warn("zkClient{%s}'s App zk path{%s} does not exist.", z.name, zkPath)
-		return nil, jerrors.Errorf("zkClient{%s} App zk path{%s} does not exist.", z.name, zkPath)
+		logger.Warnf("zkClient{%s}'s App zk path{%s} does not exist.", z.name, zkPath)
+		return nil, perrors.Errorf("zkClient{%s} App zk path{%s} does not exist.", z.name, zkPath)
 	}
 
 	return event, nil
diff --git a/registry/zookeeper/zk_client_test.go b/registry/zookeeper/zk_client_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..808b6fcff9d173e406adbc669a8c0b82b41ea3a3
--- /dev/null
+++ b/registry/zookeeper/zk_client_test.go
@@ -0,0 +1,146 @@
+// Copyright 2016-2019 hxmhlt
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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 zookeeper
+
+import (
+	"fmt"
+	"testing"
+	"time"
+)
+import (
+	"github.com/samuel/go-zookeeper/zk"
+	"github.com/stretchr/testify/assert"
+)
+
+func verifyEventStateOrder(t *testing.T, c <-chan zk.Event, expectedStates []zk.State, source string) {
+	for _, state := range expectedStates {
+		for {
+			event, ok := <-c
+			if !ok {
+				t.Fatalf("unexpected channel close for %s", source)
+			}
+			fmt.Println(event)
+			if event.Type != zk.EventSession {
+				continue
+			}
+
+			if event.State != state {
+				t.Fatalf("mismatched state order from %s, expected %v, received %v", source, state, event.State)
+			}
+			break
+		}
+	}
+}
+func verifyEventOrder(t *testing.T, c <-chan zk.Event, expectedEvent []zk.EventType, source string) {
+	for _, e := range expectedEvent {
+		for {
+			event, ok := <-c
+			if !ok {
+				t.Fatalf("unexpected channel close for %s", source)
+			}
+
+			if event.Type != e {
+				t.Fatalf("mismatched state order from %s, expected %v, received %v", source, event, event.Type)
+			}
+
+			break
+		}
+	}
+}
+
+//func Test_newZookeeperClient(t *testing.T) {
+//	ts, err := zk.StartTestCluster(1, nil, nil)
+//	if err != nil {
+//		t.Fatal(err)
+//	}
+//	defer ts.Stop()
+//
+//	callbackChan := make(chan zk.Event)
+//	f := func(event zk.Event) {
+//		callbackChan <- event
+//	}
+//
+//	zook, eventChan, err := ts.ConnectWithOptions(15*time.Second, zk.WithEventCallback(f))
+//	if err != nil {
+//		t.Fatalf("Connect returned error: %+v", err)
+//	}
+//
+//	states := []zk.State{zk.StateConnecting, zk.StateConnected, zk.StateHasSession}
+//	verifyEventStateOrder(t, callbackChan, states, "callback")
+//	verifyEventStateOrder(t, eventChan, states, "event channel")
+//
+//	zook.Close()
+//	verifyEventStateOrder(t, callbackChan, []zk.State{zk.StateDisconnected}, "callback")
+//	verifyEventStateOrder(t, eventChan, []zk.State{zk.StateDisconnected}, "event channel")
+//
+//}
+
+func Test_newMockZookeeperClient(t *testing.T) {
+	ts, z, event, _ := newMockZookeeperClient("test", 15*time.Second)
+	defer ts.Stop()
+	states := []zk.State{zk.StateConnecting, zk.StateConnected, zk.StateHasSession}
+	verifyEventStateOrder(t, event, states, "event channel")
+
+	z.Close()
+	verifyEventStateOrder(t, event, []zk.State{zk.StateDisconnected}, "event channel")
+}
+
+func TestCreate(t *testing.T) {
+	ts, z, event, _ := newMockZookeeperClient("test", 15*time.Second)
+	defer ts.Stop()
+	err := z.Create("test1/test2/test3/test4")
+	assert.NoError(t, err)
+
+	states := []zk.State{zk.StateConnecting, zk.StateConnected, zk.StateHasSession}
+	verifyEventStateOrder(t, event, states, "event channel")
+}
+
+func TestCreateDelete(t *testing.T) {
+	ts, z, event, _ := newMockZookeeperClient("test", 15*time.Second)
+	defer ts.Stop()
+
+	states := []zk.State{zk.StateConnecting, zk.StateConnected, zk.StateHasSession}
+	verifyEventStateOrder(t, event, states, "event channel")
+	err := z.Create("/test1/test2/test3/test4")
+	assert.NoError(t, err)
+	err2 := z.Delete("/test1/test2/test3/test4")
+	assert.NoError(t, err2)
+	//verifyEventOrder(t, event, []zk.EventType{zk.EventNodeCreated}, "event channel")
+}
+
+func TestRegisterTemp(t *testing.T) {
+	ts, z, event, _ := newMockZookeeperClient("test", 15*time.Second)
+	defer ts.Stop()
+	err := z.Create("/test1/test2/test3")
+	assert.NoError(t, err)
+
+	tmpath, err := z.RegisterTemp("/test1/test2/test3", "test4")
+	assert.NoError(t, err)
+	assert.Equal(t, "/test1/test2/test3/test4", tmpath)
+	states := []zk.State{zk.StateConnecting, zk.StateConnected, zk.StateHasSession}
+	verifyEventStateOrder(t, event, states, "event channel")
+}
+
+func TestRegisterTempSeq(t *testing.T) {
+	ts, z, event, _ := newMockZookeeperClient("test", 15*time.Second)
+	defer ts.Stop()
+	err := z.Create("/test1/test2/test3")
+	assert.NoError(t, err)
+	tmpath, err := z.RegisterTempSeq("/test1/test2/test3", []byte("test"))
+	assert.NoError(t, err)
+	assert.Equal(t, "/test1/test2/test3/0000000000", tmpath)
+	states := []zk.State{zk.StateConnecting, zk.StateConnected, zk.StateHasSession}
+	verifyEventStateOrder(t, event, states, "event channel")
+}
diff --git a/registry/zookeeper/zookeeper-4unitest/contrib/fatjar/zookeeper-3.4.9-fatjar.jar b/registry/zookeeper/zookeeper-4unitest/contrib/fatjar/zookeeper-3.4.9-fatjar.jar
new file mode 100644
index 0000000000000000000000000000000000000000..839531b8b8762a9c19e334a5cbf79314cb16f945
Binary files /dev/null and b/registry/zookeeper/zookeeper-4unitest/contrib/fatjar/zookeeper-3.4.9-fatjar.jar differ
diff --git a/server/config.go b/server/config.go
deleted file mode 100644
index cf6eb1788b754ff6c493e69d9177546940294a3d..0000000000000000000000000000000000000000
--- a/server/config.go
+++ /dev/null
@@ -1,13 +0,0 @@
-package server
-
-import "github.com/AlexStocks/goext/net"
-
-type ServerConfig struct {
-	Protocol string `required:"true",default:"dubbo" yaml:"protocol" json:"protocol,omitempty"` // codec string, jsonrpc  etc
-	IP       string `yaml:"ip" json:"ip,omitempty"`
-	Port     int    `required:"true" yaml:"port" json:"port,omitempty"`
-}
-
-func (c *ServerConfig) Address() string {
-	return gxnet.HostAddress(c.IP, c.Port)
-}
diff --git a/version/version.go b/version/version.go
index 7c36ca6a75aec24dc2932b940536fbffe3a16364..ff30d03190da19411333ecceb857690d462bb10b 100644
--- a/version/version.go
+++ b/version/version.go
@@ -1,7 +1,21 @@
+// Copyright 2016-2019 Alex Stocks
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 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 version
 
 const (
-	Version = "1.0.0"
+	Version = "2.6.0"
 	Name    = "dubbogo"
-	DATE    = "2019/04/17"
+	DATE    = "2019/05/06"
 )