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) # + +[](https://travis-ci.com/dubbo/go-for-apache-dubbo) +[](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. + + + +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 + 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) # + +[](https://travis-ci.com/dubbo/go-for-apache-dubbo) +[](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 ]鐨勯粯璁ゅ疄鐜帮紝浠ュ畬鎴愯嚜宸辩殑鐗规畩闇€姹傝€屾棤闇€淇敼婧愪唬鐮併€傚悓鏃讹紝娆㈣繋浣犱负绀惧尯璐$尞鏈夌敤鐨勬嫇灞曞疄鐜般€� + + + +鍏充簬璇︾粏璁捐璇烽槄璇� [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) + + 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 ®istryAwareCluster{} +} + +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 ®istryAwareClusterInvoker{ + 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×tamp=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×tamp=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×tamp=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×tamp=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×tamp=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×tamp=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×tamp=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×tamp=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×tamp=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 ®istryDirectory{ + 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(®istry.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(®url, 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(®istry.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(®istry.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(®istry.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 ®istryProtocol{ + 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(®istryUrl) + proto.registries.Store(registryUrl.Key(), reg) + } else { + reg = regI.(registry.Registry) + } + + //new registry directory for store service url from registry + directory, err := directory2.NewRegistryDirectory(®istryUrl, 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(®istryUrl) + 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{®istry.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{®istry.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{®istry.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{®istry.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{®istry.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(®url) + 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(®url) + 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(®url) + 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(®url) + 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(®url) + 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" )