You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dubbo.apache.org by fa...@apache.org on 2020/07/26 08:28:50 UTC
[dubbo-go] branch refact-seri created (now 14564b9)
This is an automated email from the ASF dual-hosted git repository.
fangyc pushed a change to branch refact-seri
in repository https://gitbox.apache.org/repos/asf/dubbo-go.git.
at 14564b9 Mrg: merge develop
This branch includes the following new commits:
new 6b93bbc Fix: resolve conflicts
new 0e2de75 Merge remote-tracking branch 'apache/develop' into refact-seri
new 9b260cf Fix: merge stash
new 14564b9 Mrg: merge develop
The 4 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
[dubbo-go] 02/04: Merge remote-tracking branch 'apache/develop'
into refact-seri
Posted by fa...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
fangyc pushed a commit to branch refact-seri
in repository https://gitbox.apache.org/repos/asf/dubbo-go.git
commit 0e2de75263320719dec9faba2cf341ecd87e17b9
Merge: 6b93bbc ab31c0d
Author: fangyincheng <fa...@sina.com>
AuthorDate: Sat Jun 27 18:31:04 2020 +0800
Merge remote-tracking branch 'apache/develop' into refact-seri
cluster/directory/base_directory.go | 4 +-
cluster/router/chain/chain.go | 30 +++++++----
cluster/router/chain/chain_test.go | 4 +-
cluster/router/{tag/factory_test.go => chan.go} | 26 +++------
cluster/router/condition/factory.go | 14 ++---
cluster/router/condition/factory_test.go | 50 ++++++++---------
cluster/router/healthcheck/factory.go | 6 +--
cluster/router/healthcheck/health_check_route.go | 2 +-
cluster/router/router.go | 32 +++++------
cluster/router/tag/factory.go | 8 +--
cluster/router/tag/factory_test.go | 2 +-
common/config/environment.go | 2 +-
common/config/environment_test.go | 6 +--
common/extension/health_checker.go | 2 +-
common/extension/health_checker_test.go | 2 +-
common/extension/metrics_test.go | 1 +
common/extension/registry.go | 2 +-
common/extension/rest_client.go | 2 +-
common/extension/router_factory.go | 14 ++---
common/extension/service_discovery.go | 2 +-
common/proxy/proxy_factory/default.go | 2 +-
common/proxy/proxy_factory/default_test.go | 6 +--
common/proxy/proxy_test.go | 4 +-
common/rpc_service.go | 1 -
common/rpc_service_test.go | 54 +++++++++++--------
common/url.go | 7 +--
common/url_test.go | 52 ++++++++++--------
common/yaml/yaml_test.go | 2 +-
config_center/apollo/impl_test.go | 10 ++--
config_center/configuration_listener.go | 1 +
config_center/configurator/override.go | 66 ++++++++++++-----------
config_center/configurator/override_test.go | 37 +++++++------
config_center/mock_dynamic_config.go | 15 ++++--
config_center/nacos/client_test.go | 6 +--
config_center/nacos/impl_test.go | 12 ++---
config_center/nacos/listener.go | 2 +-
config_center/parser/configuration_parser_test.go | 6 +--
config_center/zookeeper/impl_test.go | 41 +++++++-------
go.mod | 10 ++--
go.sum | 48 ++++++++++++-----
protocol/invocation/rpcinvocation.go | 5 ++
registry/base_configuration_listener.go | 12 ++---
registry/consul/listener.go | 2 +
registry/consul/registry.go | 11 ++--
registry/directory/directory.go | 2 +
registry/etcdv3/listener.go | 7 ++-
registry/etcdv3/registry.go | 6 +++
registry/event.go | 2 +-
registry/event_listener.go | 1 +
registry/kubernetes/listener.go | 8 ++-
registry/kubernetes/registry.go | 10 ++++
registry/mock_registry.go | 16 +++---
registry/nacos/listener.go | 5 +-
registry/nacos/registry.go | 6 ++-
registry/nacos/service_discovery.go | 7 +--
registry/nacos/service_discovery_test.go | 2 +-
registry/protocol/protocol.go | 7 +++
registry/registry.go | 5 +-
registry/service_discovery.go | 3 +-
registry/service_instance.go | 1 +
registry/zookeeper/registry.go | 13 ++++-
61 files changed, 421 insertions(+), 303 deletions(-)
[dubbo-go] 03/04: Fix: merge stash
Posted by fa...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
fangyc pushed a commit to branch refact-seri
in repository https://gitbox.apache.org/repos/asf/dubbo-go.git
commit 9b260cf13b369e4797c35528fc8268b735768ff4
Merge: 0e2de75 1a42f33
Author: fangyincheng <fa...@sina.com>
AuthorDate: Sun Jul 26 04:49:12 2020 +0800
Fix: merge stash
common/constant/key.go | 42 +-
common/url.go | 11 +-
config/application_config.go | 10 +
protocol/dubbo/client.go | 70 ---
protocol/dubbo/client_test.go | 301 -------------
protocol/dubbo/dubbo_codec.go | 366 +++++++++++++++
protocol/dubbo/dubbo_invoker.go | 55 ++-
protocol/dubbo/dubbo_invoker_test.go | 163 ++++++-
protocol/dubbo/dubbo_protocol.go | 149 +++++-
protocol/dubbo/dubbo_protocol_test.go | 78 +++-
protocol/dubbo/impl/codec_test.go | 2 +-
protocol/dubbo/impl/const.go | 18 +-
protocol/dubbo/impl/proto.go | 12 +-
protocol/dubbo/impl/remoting/client_impl.go | 391 ----------------
protocol/dubbo/server.go | 125 ------
protocol/invocation/rpcinvocation.go | 7 +
protocol/jsonrpc/http_test.go | 22 +-
protocol/jsonrpc/jsonrpc_invoker.go | 3 +-
.../protocolwrapper/protocol_filter_wrapper.go | 3 +
.../impl/remoting/errors.go => remoting/codec.go | 28 ++
remoting/exchange.go | 144 ++++++
remoting/exchange_client.go | 227 ++++++++++
.../errors.go => remoting/exchange_server.go | 36 ++
.../impl/remoting => remoting/getty}/config.go | 66 ++-
remoting/getty/dubbo_codec_for_test.go | 382 ++++++++++++++++
remoting/getty/getty_client.go | 219 +++++++++
remoting/getty/getty_client_test.go | 497 +++++++++++++++++++++
.../getty/getty_server.go | 98 ++--
.../getty/listener.go | 204 ++++-----
.../getty/listener_test.go | 18 +-
.../dubbo/impl/remoting => remoting/getty}/pool.go | 82 ++--
.../impl/remoting => remoting/getty}/readwriter.go | 124 ++---
32 files changed, 2667 insertions(+), 1286 deletions(-)
diff --cc common/constant/key.go
index 6e73183,d8eff3a..06b37cf
--- a/common/constant/key.go
+++ b/common/constant/key.go
@@@ -22,27 -22,26 +22,27 @@@ const
)
const (
- GROUP_KEY = "group"
- VERSION_KEY = "version"
- INTERFACE_KEY = "interface"
- PATH_KEY = "path"
- SERVICE_KEY = "service"
- METHODS_KEY = "methods"
- TIMEOUT_KEY = "timeout"
- CATEGORY_KEY = "category"
- CHECK_KEY = "check"
- ENABLED_KEY = "enabled"
- SIDE_KEY = "side"
- OVERRIDE_PROVIDERS_KEY = "providerAddresses"
- BEAN_NAME_KEY = "bean.name"
- GENERIC_KEY = "generic"
- CLASSIFIER_KEY = "classifier"
- TOKEN_KEY = "token"
- LOCAL_ADDR = "local-addr"
- REMOTE_ADDR = "remote-addr"
- DUBBO_KEY = "dubbo"
- RELEASE_KEY = "release"
- ANYHOST_KEY = "anyhost"
+ GROUP_KEY = "group"
+ VERSION_KEY = "version"
+ INTERFACE_KEY = "interface"
+ PATH_KEY = "path"
+ SERVICE_KEY = "service"
+ METHODS_KEY = "methods"
+ TIMEOUT_KEY = "timeout"
+ CATEGORY_KEY = "category"
+ CHECK_KEY = "check"
+ ENABLED_KEY = "enabled"
+ SIDE_KEY = "side"
+ OVERRIDE_PROVIDERS_KEY = "providerAddresses"
+ BEAN_NAME_KEY = "bean.name"
+ GENERIC_KEY = "generic"
+ CLASSIFIER_KEY = "classifier"
+ TOKEN_KEY = "token"
+ LOCAL_ADDR = "local-addr"
+ REMOTE_ADDR = "remote-addr"
- PATH_SEPARATOR = "/"
+ DEFAULT_REMOTING_TIMEOUT = 3000
++ RELEASE_KEY = "release"
++ ANYHOST_KEY = "anyhost"
)
const (
diff --cc common/url.go
index e58f652,01c623e..5a3e69f
--- a/common/url.go
+++ b/common/url.go
@@@ -316,11 -313,16 +317,15 @@@ func (c URL) Key() string
"%s://%s:%s@%s:%s/?interface=%s&group=%s&version=%s",
c.Protocol, c.Username, c.Password, c.Ip, c.Port, c.Service(), c.GetParam(constant.GROUP_KEY, ""), c.GetParam(constant.VERSION_KEY, ""))
return buildString
- //return c.ServiceKey()
}
-// ServiceKey ...
+// ServiceKey gets a unique key of a service.
func (c URL) ServiceKey() string {
- intf := c.GetParam(constant.INTERFACE_KEY, strings.TrimPrefix(c.Path, "/"))
+ return ServiceKey(c.GetParam(constant.INTERFACE_KEY, strings.TrimPrefix(c.Path, "/")),
+ c.GetParam(constant.GROUP_KEY, ""), c.GetParam(constant.VERSION_KEY, ""))
+ }
+
+ func ServiceKey(intf string, group string, version string) string {
if intf == "" {
return ""
}
diff --cc config/application_config.go
index 16e8417,1d9306c..cc02da3
--- a/config/application_config.go
+++ b/config/application_config.go
@@@ -41,7 -40,17 +41,17 @@@ func (*ApplicationConfig) Prefix() stri
return constant.DUBBO + ".application."
}
+ // nolint
+ func (c *ApplicationConfig) Id() string {
+ return ""
+ }
+
+ // SetId ...
+ func (c *ApplicationConfig) SetId(id string) {
+
+ }
+
-// UnmarshalYAML ...
+// UnmarshalYAML unmarshals the ApplicationConfig by @unmarshal function
func (c *ApplicationConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
if err := defaults.Set(c); err != nil {
return err
diff --cc protocol/dubbo/dubbo_codec.go
index 0000000,f4a39cb..e575c79
mode 000000,100644..100644
--- a/protocol/dubbo/dubbo_codec.go
+++ b/protocol/dubbo/dubbo_codec.go
@@@ -1,0 -1,356 +1,366 @@@
+ /*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ package dubbo
+
+ import (
- "bufio"
+ "bytes"
- "fmt"
++ "github.com/apache/dubbo-go/protocol/dubbo/impl"
+ "strconv"
+ "time"
+ )
+
+ import (
+ hessian "github.com/apache/dubbo-go-hessian2"
+ perrors "github.com/pkg/errors"
+ )
+
+ import (
+ "github.com/apache/dubbo-go/common/constant"
+ "github.com/apache/dubbo-go/common/logger"
+ "github.com/apache/dubbo-go/protocol"
+ "github.com/apache/dubbo-go/protocol/invocation"
+ "github.com/apache/dubbo-go/remoting"
+ )
+
+ //SerialID serial ID
+ type SerialID byte
+
+ const (
+ // S_Dubbo dubbo serial id
+ S_Dubbo SerialID = 2
+ )
+
+ func init() {
+ codec := &DubboCodec{}
+ // this is for registry dubboCodec of dubbo protocol
+ remoting.RegistryCodec("dubbo", codec)
+ }
+
+ // DubboPackage is for hessian encode/decode. If we refactor hessian, it will also be refactored.
-type DubboPackage struct {
- Header hessian.DubboHeader
- Service hessian.Service
- Body interface{}
- Err error
-}
++//type DubboPackage struct {
++// Header hessian.DubboHeader
++// Service hessian.Service
++// Body interface{}
++// Err error
++//}
+
+ // String of DubboPackage
-func (p DubboPackage) String() string {
- return fmt.Sprintf("DubboPackage: Header-%v, Path-%v, Body-%v", p.Header, p.Service, p.Body)
-}
++//func (p DubboPackage) String() string {
++// return fmt.Sprintf("DubboPackage: Header-%v, Path-%v, Body-%v", p.Header, p.Service, p.Body)
++//}
+
+ // nolint
-func (p *DubboPackage) Marshal() (*bytes.Buffer, error) {
- codec := hessian.NewHessianCodec(nil)
-
- pkg, err := codec.Write(p.Service, p.Header, p.Body)
- if err != nil {
- return nil, perrors.WithStack(err)
- }
-
- return bytes.NewBuffer(pkg), nil
-}
++//func (p *DubboPackage) Marshal() (*bytes.Buffer, error) {
++// codec := hessian.NewHessianCodec(nil)
++//
++// pkg, err := codec.Write(p.Service, p.Header, p.Body)
++// if err != nil {
++// return nil, perrors.WithStack(err)
++// }
++//
++// return bytes.NewBuffer(pkg), nil
++//}
+
+ // nolint
-func (p *DubboPackage) Unmarshal(buf *bytes.Buffer, resp *remoting.Response) error {
- // fix issue https://github.com/apache/dubbo-go/issues/380
- bufLen := buf.Len()
- if bufLen < hessian.HEADER_LENGTH {
- return perrors.WithStack(hessian.ErrHeaderNotEnough)
- }
-
- codec := hessian.NewHessianCodec(bufio.NewReaderSize(buf, bufLen))
-
- // read header
- err := codec.ReadHeader(&p.Header)
- if err != nil {
- return perrors.WithStack(err)
- }
-
- if resp != nil { // for client
- if (p.Header.Type & hessian.PackageRequest) != 0x00 {
- // size of this array must be '7'
- // https://github.com/apache/dubbo-go-hessian2/blob/master/request.go#L272
- p.Body = make([]interface{}, 7)
- } else {
- pendingRsp := remoting.GetPendingResponse(remoting.SequenceType(p.Header.ID))
- if pendingRsp == nil {
- return perrors.Errorf("client.GetPendingResponse(%v) = nil", p.Header.ID)
- }
- p.Body = &hessian.Response{RspObj: pendingRsp.Reply}
- }
- }
- // read body
- err = codec.ReadBody(p.Body)
- return perrors.WithStack(err)
-}
++//func (p *DubboPackage) Unmarshal(buf *bytes.Buffer, resp *remoting.Response) error {
++// // fix issue https://github.com/apache/dubbo-go/issues/380
++// bufLen := buf.Len()
++// if bufLen < hessian.HEADER_LENGTH {
++// return perrors.WithStack(hessian.ErrHeaderNotEnough)
++// }
++//
++// codec := hessian.NewHessianCodec(bufio.NewReaderSize(buf, bufLen))
++//
++// // read header
++// err := codec.ReadHeader(&p.Header)
++// if err != nil {
++// return perrors.WithStack(err)
++// }
++//
++// if resp != nil { // for client
++// if (p.Header.Type & hessian.PackageRequest) != 0x00 {
++// // size of this array must be '7'
++// // https://github.com/apache/dubbo-go-hessian2/blob/master/request.go#L272
++// p.Body = make([]interface{}, 7)
++// } else {
++// pendingRsp := remoting.GetPendingResponse(remoting.SequenceType(p.Header.ID))
++// if pendingRsp == nil {
++// return perrors.Errorf("client.GetPendingResponse(%v) = nil", p.Header.ID)
++// }
++// p.Body = &hessian.Response{RspObj: pendingRsp.Reply}
++// }
++// }
++// // read body
++// err = codec.ReadBody(p.Body)
++// return perrors.WithStack(err)
++//}
+
+ // DubboCodec. It is implements remoting.Codec
+ type DubboCodec struct {
+ }
+
+ // encode request for transport
+ func (c *DubboCodec) EncodeRequest(request *remoting.Request) (*bytes.Buffer, error) {
+ if request.Event {
+ return c.encodeHeartbeartReqeust(request)
+ }
+
+ invoc, ok := request.Data.(*protocol.Invocation)
+ if !ok {
+ logger.Errorf("encode request failed for parameter type :%+v", request)
+ return nil, perrors.Errorf("encode request failed for parameter type :%+v", request)
+ }
+ invocation := *invoc
+
- p := &DubboPackage{}
- p.Service.Path = invocation.AttachmentsByKey(constant.PATH_KEY, "")
- p.Service.Interface = invocation.AttachmentsByKey(constant.INTERFACE_KEY, "")
- p.Service.Version = invocation.AttachmentsByKey(constant.VERSION_KEY, "")
- p.Service.Group = invocation.AttachmentsByKey(constant.GROUP_KEY, "")
- p.Service.Method = invocation.MethodName()
-
++ svc := impl.Service{}
++ svc.Path = invocation.AttachmentsByKey(constant.PATH_KEY, "")
++ svc.Interface = invocation.AttachmentsByKey(constant.INTERFACE_KEY, "")
++ svc.Version = invocation.AttachmentsByKey(constant.VERSION_KEY, "")
++ svc.Group = invocation.AttachmentsByKey(constant.GROUP_KEY, "")
++ svc.Method = invocation.MethodName()
+ timeout, err := strconv.Atoi(invocation.AttachmentsByKey(constant.TIMEOUT_KEY, strconv.Itoa(constant.DEFAULT_REMOTING_TIMEOUT)))
+ if err != nil {
+ // it will be wrapped in readwrite.Write .
+ return nil, perrors.WithStack(err)
+ }
- p.Service.Timeout = time.Duration(timeout)
-
- p.Header.SerialID = byte(S_Dubbo)
- p.Header.ID = request.ID
++ svc.Timeout = time.Duration(timeout)
++
++ header := impl.DubboHeader{}
++ serialization := invocation.AttachmentsByKey(constant.SERIALIZATION_KEY, constant.HESSIAN2_SERIALIZATION)
++ if serialization == constant.HESSIAN2_SERIALIZATION {
++ header.SerialID = constant.S_Hessian2
++ } else if serialization == constant.PROTOBUF_SERIALIZATION {
++ header.SerialID = constant.S_Proto
++ }
++ header.ID = request.ID
+ if request.TwoWay {
- p.Header.Type = hessian.PackageRequest_TwoWay
++ header.Type = impl.PackageRequest_TwoWay
+ } else {
- p.Header.Type = hessian.PackageRequest
++ header.Type = impl.PackageRequest
+ }
+
- p.Body = hessian.NewRequest(invocation.Arguments(), invocation.Attachments())
-
- codec := hessian.NewHessianCodec(nil)
++ pkg := &impl.DubboPackage{
++ Header: header,
++ Service: svc,
++ Body: impl.NewRequestPayload(invocation.Arguments(), invocation.Attachments()),
++ Err: nil,
++ Codec: impl.NewDubboCodec(nil),
++ }
+
- pkg, err := codec.Write(p.Service, p.Header, p.Body)
- if err != nil {
++ if err := impl.LoadSerializer(pkg); err != nil {
+ return nil, perrors.WithStack(err)
+ }
+
- return bytes.NewBuffer(pkg), nil
++ return pkg.Marshal()
+ }
+
+ // encode heartbeart request
+ func (c *DubboCodec) encodeHeartbeartReqeust(request *remoting.Request) (*bytes.Buffer, error) {
- pkg := &DubboPackage{}
- pkg.Body = []interface{}{}
- pkg.Header.ID = request.ID
- pkg.Header.Type = hessian.PackageHeartbeat
- pkg.Header.SerialID = byte(S_Dubbo)
-
- codec := hessian.NewHessianCodec(nil)
++ header := impl.DubboHeader{
++ Type: impl.PackageHeartbeat,
++ SerialID: constant.S_Hessian2,
++ ID: request.ID,
++ }
+
- byt, err := codec.Write(pkg.Service, pkg.Header, pkg.Body)
- if err != nil {
- return nil, perrors.WithStack(err)
++ pkg := &impl.DubboPackage{
++ Header: header,
++ Service: impl.Service{},
++ Body: impl.NewRequestPayload([]interface{}{}, nil),
++ Err: nil,
++ Codec: impl.NewDubboCodec(nil),
+ }
+
- return bytes.NewBuffer(byt), nil
++ if err := impl.LoadSerializer(pkg); err != nil {
++ return nil, err
++ }
++ return pkg.Marshal()
+ }
+
+ // encode response
+ func (c *DubboCodec) EncodeResponse(response *remoting.Response) (*bytes.Buffer, error) {
- var ptype = hessian.PackageResponse
++ var ptype = impl.PackageResponse
+ if response.IsHeartbeat() {
- ptype = hessian.PackageHeartbeat
++ ptype = impl.PackageHeartbeat
+ }
- resp := &DubboPackage{
- Header: hessian.DubboHeader{
++ resp := &impl.DubboPackage{
++ Header: impl.DubboHeader{
+ SerialID: response.SerialID,
+ Type: ptype,
+ ID: response.ID,
+ ResponseStatus: response.Status,
+ },
+ }
+ if !response.IsHeartbeat() {
+ resp.Body = &hessian.Response{
+ RspObj: response.Result.(protocol.RPCResult).Rest,
+ Exception: response.Result.(protocol.RPCResult).Err,
+ Attachments: response.Result.(protocol.RPCResult).Attrs,
+ }
+ }
+
- codec := hessian.NewHessianCodec(nil)
++ codec := impl.NewDubboCodec(nil)
+
- pkg, err := codec.Write(resp.Service, resp.Header, resp.Body)
++ pkg, err := codec.Encode(*resp)
+ if err != nil {
+ return nil, perrors.WithStack(err)
+ }
+
+ return bytes.NewBuffer(pkg), nil
+ }
+
+ // Decode data, including request and response.
+ func (c *DubboCodec) Decode(data []byte) (remoting.DecodeResult, int, error) {
+ if c.isRequest(data) {
+ req, len, err := c.decodeRequest(data)
+ if err != nil {
+ return remoting.DecodeResult{}, len, perrors.WithStack(err)
+ }
+ return remoting.DecodeResult{IsRequest: true, Result: req}, len, perrors.WithStack(err)
+ } else {
+ resp, len, err := c.decodeResponse(data)
+ if err != nil {
+ return remoting.DecodeResult{}, len, perrors.WithStack(err)
+ }
+ return remoting.DecodeResult{IsRequest: false, Result: resp}, len, perrors.WithStack(err)
+ }
+ }
+
+ func (c *DubboCodec) isRequest(data []byte) bool {
+ if data[2]&byte(0x80) == 0x00 {
+ return false
+ }
+ return true
+ }
+
+ // decode request
+ func (c *DubboCodec) decodeRequest(data []byte) (*remoting.Request, int, error) {
- pkg := &DubboPackage{
- Body: make([]interface{}, 7),
- }
+ var request *remoting.Request = nil
+ buf := bytes.NewBuffer(data)
- err := pkg.Unmarshal(buf, nil)
++ pkg := impl.NewDubboPackage(buf)
++ pkg.SetBody(make([]interface{}, 7))
++ err := pkg.Unmarshal()
+ if err != nil {
+ originErr := perrors.Cause(err)
+ if originErr == hessian.ErrHeaderNotEnough || originErr == hessian.ErrBodyNotEnough {
+ //FIXME
+ return nil, 0, originErr
+ }
+ logger.Errorf("pkg.Unmarshal(len(@data):%d) = error:%+v", buf.Len(), err)
+
+ return request, 0, perrors.WithStack(err)
+ }
+ request = &remoting.Request{
+ ID: pkg.Header.ID,
+ SerialID: pkg.Header.SerialID,
- TwoWay: pkg.Header.Type&hessian.PackageRequest_TwoWay != 0x00,
- Event: pkg.Header.Type&hessian.PackageHeartbeat != 0x00,
++ TwoWay: pkg.Header.Type&impl.PackageRequest_TwoWay != 0x00,
++ Event: pkg.Header.Type&impl.PackageHeartbeat != 0x00,
+ }
- if (pkg.Header.Type & hessian.PackageHeartbeat) == 0x00 {
++ if (pkg.Header.Type & impl.PackageHeartbeat) == 0x00 {
+ // convert params of request
+ req := pkg.Body.([]interface{}) // length of body should be 7
+ if len(req) > 0 {
+ //invocation := request.Data.(*invocation.RPCInvocation)
+ var methodName string
+ var args []interface{}
+ var attachments map[string]string = make(map[string]string)
+ if req[0] != nil {
+ //dubbo version
+ request.Version = req[0].(string)
+ }
+ if req[1] != nil {
+ //path
+ attachments[constant.PATH_KEY] = req[1].(string)
+ }
+ if req[2] != nil {
+ //version
+ attachments[constant.VERSION_KEY] = req[2].(string)
+ }
+ if req[3] != nil {
+ //method
+ methodName = req[3].(string)
+ }
+ if req[4] != nil {
+ //ignore argTypes
+ }
+ if req[5] != nil {
+ args = req[5].([]interface{})
+ }
+ if req[6] != nil {
+ attachments = req[6].(map[string]string)
+ }
+ invoc := invocation.NewRPCInvocationWithOptions(invocation.WithAttachments(attachments),
+ invocation.WithArguments(args), invocation.WithMethodName(methodName))
+ request.Data = invoc
+ }
+ }
+ return request, hessian.HEADER_LENGTH + pkg.Header.BodyLen, nil
+ }
+
+ // decode response
+ func (c *DubboCodec) decodeResponse(data []byte) (*remoting.Response, int, error) {
- pkg := &DubboPackage{}
+ buf := bytes.NewBuffer(data)
++ pkg := impl.NewDubboPackage(buf)
+ response := &remoting.Response{}
- err := pkg.Unmarshal(buf, response)
++ err := pkg.Unmarshal()
+ if err != nil {
+ originErr := perrors.Cause(err)
+ // if the data is very big, so the receive need much times.
+ if originErr == hessian.ErrHeaderNotEnough || originErr == hessian.ErrBodyNotEnough {
+ return nil, 0, originErr
+ }
+ logger.Errorf("pkg.Unmarshal(len(@data):%d) = error:%+v", buf.Len(), err)
+
+ return nil, 0, perrors.WithStack(err)
+ }
+ response = &remoting.Response{
+ ID: pkg.Header.ID,
+ //Version: pkg.Header.,
+ SerialID: pkg.Header.SerialID,
+ Status: pkg.Header.ResponseStatus,
- Event: (pkg.Header.Type & hessian.PackageHeartbeat) != 0,
++ Event: (pkg.Header.Type & impl.PackageHeartbeat) != 0,
+ }
+ var error error
- if pkg.Header.Type&hessian.PackageHeartbeat != 0x00 {
- if pkg.Header.Type&hessian.PackageResponse != 0x00 {
++ if pkg.Header.Type&impl.PackageHeartbeat != 0x00 {
++ if pkg.Header.Type&impl.PackageResponse != 0x00 {
+ logger.Debugf("get rpc heartbeat response{header: %#v, body: %#v}", pkg.Header, pkg.Body)
+ if pkg.Err != nil {
+ logger.Errorf("rpc heartbeat response{error: %#v}", pkg.Err)
+ error = pkg.Err
+ }
+ } else {
+ logger.Debugf("get rpc heartbeat request{header: %#v, service: %#v, body: %#v}", pkg.Header, pkg.Service, pkg.Body)
+ response.Status = hessian.Response_OK
+ //reply(session, p, hessian.PackageHeartbeat)
+ }
+ return response, hessian.HEADER_LENGTH + pkg.Header.BodyLen, error
+ }
+ logger.Debugf("get rpc response{header: %#v, body: %#v}", pkg.Header, pkg.Body)
+ rpcResult := &protocol.RPCResult{}
+ response.Result = rpcResult
- if pkg.Header.Type&hessian.PackageRequest == 0x00 {
++ if pkg.Header.Type&impl.PackageRequest == 0x00 {
+ if pkg.Err != nil {
+ rpcResult.Err = pkg.Err
+ } else if pkg.Body.(*hessian.Response).Exception != nil {
+ rpcResult.Err = pkg.Body.(*hessian.Response).Exception
+ response.Error = rpcResult.Err
+ }
+ rpcResult.Attrs = pkg.Body.(*hessian.Response).Attachments
+ rpcResult.Rest = pkg.Body.(*hessian.Response).RspObj
+ }
+
+ return response, hessian.HEADER_LENGTH + pkg.Header.BodyLen, nil
+ }
diff --cc protocol/dubbo/dubbo_invoker.go
index 1907c38,d94169d..e51cc5f
--- a/protocol/dubbo/dubbo_invoker.go
+++ b/protocol/dubbo/dubbo_invoker.go
@@@ -128,7 -141,22 +146,22 @@@ func (di *DubboInvoker) Invoke(ctx cont
return &result
}
+ // get timeout including methodConfig
+ func (di *DubboInvoker) getTimeout(invocation *invocation_impl.RPCInvocation) time.Duration {
+ var timeout = di.GetUrl().GetParam(strings.Join([]string{constant.METHOD_KEYS, invocation.MethodName(), constant.TIMEOUT_KEY}, "."), "")
+ if len(timeout) != 0 {
+ if t, err := time.ParseDuration(timeout); err == nil {
+ // config timeout into attachment
+ invocation.SetAttachments(constant.TIMEOUT_KEY, strconv.Itoa(int(t.Milliseconds())))
+ return t
+ }
+ }
+ // set timeout into invocation at method level
+ invocation.SetAttachments(constant.TIMEOUT_KEY, strconv.Itoa(int(di.timeout.Milliseconds())))
+ return di.timeout
+ }
+
-// Destroy ...
+// Destroy destroy dubbo client invoker.
func (di *DubboInvoker) Destroy() {
di.quitOnce.Do(func() {
for {
diff --cc protocol/dubbo/dubbo_invoker_test.go
index e96e859,88c1910..9585461
--- a/protocol/dubbo/dubbo_invoker_test.go
+++ b/protocol/dubbo/dubbo_invoker_test.go
@@@ -93,3 -92,143 +92,143 @@@ func TestDubboInvoker_Invoke(t *testing
proto.Destroy()
lock.Unlock()
}
+
+ func InitTest(t *testing.T) (protocol.Protocol, common.URL) {
+
+ hessian.RegisterPOJO(&User{})
+
- methods, err := common.ServiceMap.Register("dubbo", &UserProvider{})
++ methods, err := common.ServiceMap.Register("", "dubbo", &UserProvider{})
+ assert.NoError(t, err)
+ assert.Equal(t, "GetBigPkg,GetUser,GetUser0,GetUser1,GetUser2,GetUser3,GetUser4,GetUser5,GetUser6", methods)
+
+ // config
+ getty.SetClientConf(getty.ClientConfig{
+ ConnectionNum: 2,
+ HeartbeatPeriod: "5s",
+ SessionTimeout: "20s",
+ PoolTTL: 600,
+ PoolSize: 64,
+ GettySessionParam: getty.GettySessionParam{
+ CompressEncoding: false,
+ TcpNoDelay: true,
+ TcpKeepAlive: true,
+ KeepAlivePeriod: "120s",
+ TcpRBufSize: 262144,
+ TcpWBufSize: 65536,
+ PkgWQSize: 512,
+ TcpReadTimeout: "4s",
+ TcpWriteTimeout: "5s",
+ WaitTimeout: "1s",
+ MaxMsgLen: 10240000000,
+ SessionName: "client",
+ },
+ })
+ getty.SetServerConfig(getty.ServerConfig{
+ SessionNumber: 700,
+ SessionTimeout: "20s",
+ GettySessionParam: getty.GettySessionParam{
+ CompressEncoding: false,
+ TcpNoDelay: true,
+ TcpKeepAlive: true,
+ KeepAlivePeriod: "120s",
+ TcpRBufSize: 262144,
+ TcpWBufSize: 65536,
+ PkgWQSize: 512,
+ TcpReadTimeout: "1s",
+ TcpWriteTimeout: "5s",
+ WaitTimeout: "1s",
+ MaxMsgLen: 10240000000,
+ SessionName: "server",
+ }})
+
+ // Export
+ proto := GetProtocol()
+ url, err := common.NewURL("dubbo://127.0.0.1:20702/UserProvider?anyhost=true&" +
+ "application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&" +
+ "environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&" +
+ "module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&" +
+ "side=provider&timeout=3000×tamp=1556509797245&bean.name=UserProvider")
+ assert.NoError(t, err)
+ proto.Export(&proxy_factory.ProxyInvoker{
+ BaseInvoker: *protocol.NewBaseInvoker(url),
+ })
+
+ time.Sleep(time.Second * 2)
+
+ return proto, url
+ }
+
+ //////////////////////////////////
+ // provider
+ //////////////////////////////////
+
+ type (
+ User struct {
+ Id string `json:"id"`
+ Name string `json:"name"`
+ }
+
+ UserProvider struct {
+ user map[string]User
+ }
+ )
+
+ // size:4801228
+ func (u *UserProvider) GetBigPkg(ctx context.Context, req []interface{}, rsp *User) error {
+ argBuf := new(bytes.Buffer)
+ for i := 0; i < 400; i++ {
+ // use chinese for test
+ argBuf.WriteString("击鼓其镗,踊跃用兵。土国城漕,我独南行。从孙子仲,平陈与宋。不我以归,忧心有忡。爰居爰处?爰丧其马?于以求之?于林之下。死生契阔,与子成说。执子之手,与子偕老。于嗟阔兮,不我活兮。于嗟洵兮,不我信兮。")
+ argBuf.WriteString("击鼓其镗,踊跃用兵。土国城漕,我独南行。从孙子仲,平陈与宋。不我以归,忧心有忡。爰居爰处?爰丧其马?于以求之?于林之下。死生契阔,与子成说。执子之手,与子偕老。于嗟阔兮,不我活兮。于嗟洵兮,不我信兮。")
+ }
+ rsp.Id = argBuf.String()
+ rsp.Name = argBuf.String()
+ return nil
+ }
+
+ func (u *UserProvider) GetUser(ctx context.Context, req []interface{}, rsp *User) error {
+ rsp.Id = req[0].(string)
+ rsp.Name = req[1].(string)
+ return nil
+ }
+
+ func (u *UserProvider) GetUser0(id string, k *User, name string) (User, error) {
+ return User{Id: id, Name: name}, nil
+ }
+
+ func (u *UserProvider) GetUser1() error {
+ return nil
+ }
+
+ func (u *UserProvider) GetUser2() error {
+ return perrors.New("error")
+ }
+
+ func (u *UserProvider) GetUser3(rsp *[]interface{}) error {
+ *rsp = append(*rsp, User{Id: "1", Name: "username"})
+ return nil
+ }
+
+ func (u *UserProvider) GetUser4(ctx context.Context, req []interface{}) ([]interface{}, error) {
+
+ return []interface{}{User{Id: req[0].([]interface{})[0].(string), Name: req[0].([]interface{})[1].(string)}}, nil
+ }
+
+ func (u *UserProvider) GetUser5(ctx context.Context, req []interface{}) (map[interface{}]interface{}, error) {
+ return map[interface{}]interface{}{"key": User{Id: req[0].(map[interface{}]interface{})["id"].(string), Name: req[0].(map[interface{}]interface{})["name"].(string)}}, nil
+ }
+
+ func (u *UserProvider) GetUser6(id int64) (*User, error) {
+ if id == 0 {
+ return nil, nil
+ }
+ return &User{Id: "1"}, nil
+ }
+
+ func (u *UserProvider) Reference() string {
+ return "UserProvider"
+ }
+
+ func (u User) JavaClassName() string {
+ return "com.ikurento.user.User"
+ }
diff --cc protocol/dubbo/dubbo_protocol.go
index cc67569,2d8a20c..915ee74
--- a/protocol/dubbo/dubbo_protocol.go
+++ b/protocol/dubbo/dubbo_protocol.go
@@@ -29,11 -35,14 +35,13 @@@ import
"github.com/apache/dubbo-go/common/logger"
"github.com/apache/dubbo-go/config"
"github.com/apache/dubbo-go/protocol"
- "github.com/apache/dubbo-go/protocol/dubbo/impl/remoting"
+ "github.com/apache/dubbo-go/protocol/invocation"
+ "github.com/apache/dubbo-go/remoting"
+ "github.com/apache/dubbo-go/remoting/getty"
)
-// dubbo protocol constant
const (
- // DUBBO ...
+ // DUBBO is dubbo protocol name
DUBBO = "dubbo"
)
@@@ -72,20 -91,14 +89,14 @@@ func (dp *DubboProtocol) Export(invoke
return exporter
}
-// nolint
+// Refer create dubbo service reference.
func (dp *DubboProtocol) Refer(url common.URL) protocol.Invoker {
- //default requestTimeout
- var requestTimeout = config.GetConsumerConfig().RequestTimeout
-
- requestTimeoutStr := url.GetParam(constant.TIMEOUT_KEY, config.GetConsumerConfig().Request_Timeout)
- if t, err := time.ParseDuration(requestTimeoutStr); err == nil {
- requestTimeout = t
+ exchangeClient := getExchangeClient(url)
+ if exchangeClient == nil {
+ logger.Warnf("can't dial the server: %+v", url.Location)
+ return nil
}
-
- invoker := NewDubboInvoker(url, remoting.NewClient(remoting.Options{
- ConnectTimeout: config.GetConsumerConfig().ConnectTimeout,
- RequestTimeout: requestTimeout,
- }))
+ invoker := NewDubboInvoker(url, exchangeClient)
dp.SetInvokers(invoker)
logger.Infof("Refer service: %s", url.String())
return invoker
diff --cc protocol/dubbo/dubbo_protocol_test.go
index 1915a07,55ab0fe..07b890f
--- a/protocol/dubbo/dubbo_protocol_test.go
+++ b/protocol/dubbo/dubbo_protocol_test.go
@@@ -29,15 -26,56 +26,58 @@@ import
)
import (
- "github.com/stretchr/testify/assert"
+ "github.com/apache/dubbo-go/common"
+ "github.com/apache/dubbo-go/common/constant"
+ "github.com/apache/dubbo-go/protocol"
+ "github.com/apache/dubbo-go/remoting/getty"
)
+ func init() {
+ getty.SetServerConfig(getty.ServerConfig{
+ SessionNumber: 700,
+ SessionTimeout: "20s",
+ GettySessionParam: getty.GettySessionParam{
+ CompressEncoding: false,
+ TcpNoDelay: true,
+ TcpKeepAlive: true,
+ KeepAlivePeriod: "120s",
+ TcpRBufSize: 262144,
+ TcpWBufSize: 65536,
+ PkgWQSize: 512,
+ TcpReadTimeout: "1s",
+ TcpWriteTimeout: "5s",
+ WaitTimeout: "1s",
+ MaxMsgLen: 10240000000,
+ SessionName: "server",
+ }})
+ getty.SetClientConf(getty.ClientConfig{
+ ConnectionNum: 1,
+ HeartbeatPeriod: "3s",
+ SessionTimeout: "20s",
+ PoolTTL: 600,
+ PoolSize: 64,
+ GettySessionParam: getty.GettySessionParam{
+ CompressEncoding: false,
+ TcpNoDelay: true,
+ TcpKeepAlive: true,
+ KeepAlivePeriod: "120s",
+ TcpRBufSize: 262144,
+ TcpWBufSize: 65536,
+ PkgWQSize: 512,
+ TcpReadTimeout: "4s",
+ TcpWriteTimeout: "5s",
+ WaitTimeout: "1s",
+ MaxMsgLen: 10240000000,
+ SessionName: "client",
+ },
+ })
+ }
func TestDubboProtocol_Export(t *testing.T) {
- srvCfg := remoting.GetDefaultServerConfig()
- remoting.SetServerConfig(srvCfg)
++ srvCfg := getty.GetDefaultServerConfig()
++ getty.SetServerConfig(srvCfg)
// Export
proto := GetProtocol()
- url, err := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&" +
+ url, err := common.NewURL("dubbo://127.0.0.1:20094/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&" +
@@@ -76,12 -114,24 +116,26 @@@
assert.False(t, ok)
}
+ func TestDubboProtocol_Refer_No_connect(t *testing.T) {
+ // Refer
+ proto := GetProtocol()
+ url, err := common.NewURL("dubbo://127.0.0.1:20096/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)
+ invoker := proto.Refer(url)
+ assert.Nil(t, invoker)
+ }
+
func TestDubboProtocol_Refer(t *testing.T) {
- cliCfg := remoting.GetDefaultClientConfig()
- remoting.SetClientConf(cliCfg)
++ cliCfg := getty.GetDefaultClientConfig()
++ getty.SetClientConf(cliCfg)
// Refer
proto := GetProtocol()
- url, err := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&" +
+
+ url, err := common.NewURL("dubbo://127.0.0.1:20091/com.ikurento.user.UserProvider?anyhost=true&" +
"application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&" +
"environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&" +
"module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&" +
diff --cc protocol/dubbo/impl/codec_test.go
index c93f307,0000000..92f2f2e
mode 100644,000000..100644
--- a/protocol/dubbo/impl/codec_test.go
+++ b/protocol/dubbo/impl/codec_test.go
@@@ -1,197 -1,0 +1,197 @@@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package impl
+
+import (
+ "testing"
+ "time"
+)
+
+import (
+ "github.com/golang/protobuf/proto"
+ "github.com/stretchr/testify/assert"
+)
+
+import (
+ "github.com/apache/dubbo-go/common/constant"
+ pb "github.com/apache/dubbo-go/protocol/dubbo/impl/proto"
+)
+
+func TestDubboPackage_MarshalAndUnmarshal(t *testing.T) {
+ pkg := NewDubboPackage(nil)
+ pkg.Body = []interface{}{"a"}
+ pkg.Header.Type = PackageHeartbeat
+ pkg.Header.SerialID = constant.S_Hessian2
+ pkg.Header.ID = 10086
+ pkg.SetSerializer(HessianSerializer{})
+
+ // heartbeat
+ data, err := pkg.Marshal()
+ assert.NoError(t, err)
+
+ pkgres := NewDubboPackage(data)
+ pkgres.SetSerializer(HessianSerializer{})
+
+ pkgres.Body = []interface{}{}
+ err = pkgres.Unmarshal()
+ assert.NoError(t, err)
+ assert.Equal(t, PackageHeartbeat|PackageRequest|PackageRequest_TwoWay, pkgres.Header.Type)
+ assert.Equal(t, constant.S_Hessian2, pkgres.Header.SerialID)
+ assert.Equal(t, int64(10086), pkgres.Header.ID)
+ assert.Equal(t, 0, len(pkgres.Body.([]interface{})))
+
+ // request
+ pkg.Header.Type = PackageRequest
+ pkg.Service.Interface = "Service"
+ pkg.Service.Path = "path"
+ pkg.Service.Version = "2.6"
+ pkg.Service.Method = "Method"
+ pkg.Service.Timeout = time.Second
+ data, err = pkg.Marshal()
+ assert.NoError(t, err)
+
+ pkgres = NewDubboPackage(data)
+ pkgres.SetSerializer(HessianSerializer{})
+ pkgres.Body = make([]interface{}, 7)
+ err = pkgres.Unmarshal()
+ reassembleBody := pkgres.GetBody().(map[string]interface{})
+ assert.NoError(t, err)
+ assert.Equal(t, PackageRequest, pkgres.Header.Type)
+ assert.Equal(t, constant.S_Hessian2, pkgres.Header.SerialID)
+ assert.Equal(t, int64(10086), pkgres.Header.ID)
+ assert.Equal(t, "2.0.2", reassembleBody["dubboVersion"].(string))
+ assert.Equal(t, "path", pkgres.Service.Path)
+ assert.Equal(t, "2.6", pkgres.Service.Version)
+ assert.Equal(t, "Method", pkgres.Service.Method)
+ assert.Equal(t, "Ljava/lang/String;", reassembleBody["argsTypes"].(string))
+ assert.Equal(t, []interface{}{"a"}, reassembleBody["args"])
+ assert.Equal(t, map[string]string{"dubbo": "2.0.2", "interface": "Service", "path": "path", "timeout": "1000", "version": "2.6"}, reassembleBody["attachments"].(map[string]string))
+}
+
+func TestDubboPackage_Protobuf_Serialization_Request(t *testing.T) {
+ pkg := NewDubboPackage(nil)
+ pkg.Body = []interface{}{"a"}
+ pkg.Header.Type = PackageHeartbeat
+ pkg.Header.SerialID = constant.S_Proto
+ pkg.Header.ID = 10086
+ pkg.SetSerializer(ProtoSerializer{})
+
+ // heartbeat
+ data, err := pkg.Marshal()
+ assert.NoError(t, err)
+
+ pkgres := NewDubboPackage(data)
+ pkgres.SetSerializer(HessianSerializer{})
+
+ pkgres.Body = []interface{}{}
+ err = pkgres.Unmarshal()
+ assert.NoError(t, err)
+ assert.Equal(t, PackageHeartbeat|PackageRequest|PackageRequest_TwoWay, pkgres.Header.Type)
+ assert.Equal(t, constant.S_Proto, pkgres.Header.SerialID)
+ assert.Equal(t, int64(10086), pkgres.Header.ID)
+ assert.Equal(t, 0, len(pkgres.Body.([]interface{})))
+
+ // request
+ pkg.Header.Type = PackageRequest
+ pkg.Service.Interface = "Service"
+ pkg.Service.Path = "path"
+ pkg.Service.Version = "2.6"
+ pkg.Service.Method = "Method"
+ pkg.Service.Timeout = time.Second
+ pkg.SetBody([]interface{}{&pb.StringValue{Value: "hello world"}})
+ data, err = pkg.Marshal()
+ assert.NoError(t, err)
+
+ pkgres = NewDubboPackage(data)
+ pkgres.SetSerializer(ProtoSerializer{})
+ err = pkgres.Unmarshal()
+ assert.NoError(t, err)
+ body, ok := pkgres.Body.(map[string]interface{})
+ assert.Equal(t, ok, true)
+ req, ok := body["args"].([]interface{})
+ assert.Equal(t, ok, true)
+ // protobuf rpc just has exact one parameter
+ assert.Equal(t, len(req), 1)
+ argsBytes, ok := req[0].([]byte)
- assert.Equal(t, ok, true)
++ assert.Equal(t, true, ok)
+ sv := pb.StringValue{}
+ buf := proto.NewBuffer(argsBytes)
+ err = buf.Unmarshal(&sv)
+ assert.NoError(t, err)
+ assert.Equal(t, sv.Value, "hello world")
+}
+
+func TestDubboCodec_Protobuf_Serialization_Response(t *testing.T) {
+ {
+ pkg := NewDubboPackage(nil)
+ pkg.Header.Type = PackageResponse
+ pkg.Header.SerialID = constant.S_Proto
+ pkg.Header.ID = 10086
+ pkg.SetSerializer(ProtoSerializer{})
+ pkg.SetBody(&pb.StringValue{Value: "hello world"})
+
+ // heartbeat
+ data, err := pkg.Marshal()
+ assert.NoError(t, err)
+
+ pkgres := NewDubboPackage(data)
+ pkgres.SetSerializer(ProtoSerializer{})
+
+ pkgres.SetBody(&pb.StringValue{})
+ err = pkgres.Unmarshal()
+
+ assert.NoError(t, err)
+ assert.Equal(t, pkgres.Header.Type, PackageResponse)
+ assert.Equal(t, constant.S_Proto, pkgres.Header.SerialID)
+ assert.Equal(t, int64(10086), pkgres.Header.ID)
+
+ res, ok := pkgres.Body.(*pb.StringValue)
+ assert.Equal(t, ok, true)
+ assert.Equal(t, res.Value, "hello world")
+ }
+
+ // with attachments
+ {
+ attas := make(map[string]string)
+ attas["k1"] = "test"
+ resp := NewResponsePayload(&pb.StringValue{Value: "attachments"}, nil, attas)
+ p := NewDubboPackage(nil)
+ p.Header.Type = PackageResponse
+ p.Header.SerialID = constant.S_Proto
+ p.SetSerializer(ProtoSerializer{})
+ p.SetBody(resp)
+ data, err := p.Marshal()
+ assert.NoError(t, err)
+
+ pkgres := NewDubboPackage(data)
+ pkgres.Header.Type = PackageResponse
+ pkgres.Header.SerialID = constant.S_Proto
+ pkgres.Header.ID = 10086
+ pkgres.SetSerializer(ProtoSerializer{})
+
+ resAttachment := make(map[string]string)
+ resBody := &pb.StringValue{}
+ pkgres.SetBody(NewResponsePayload(resBody, nil, resAttachment))
+
+ err = pkgres.Unmarshal()
+ assert.NoError(t, err)
+ assert.Equal(t, "attachments", resBody.Value)
+ assert.Equal(t, "test", resAttachment["k1"])
+ }
+
+}
diff --cc protocol/dubbo/impl/const.go
index 06e73fa,0000000..c2f4006
mode 100644,000000..100644
--- a/protocol/dubbo/impl/const.go
+++ b/protocol/dubbo/impl/const.go
@@@ -1,243 -1,0 +1,243 @@@
- package impl
-
- import (
- "reflect"
- "regexp"
-
- "github.com/pkg/errors"
- )
-
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
++package impl
++
++import (
++ "reflect"
++ "regexp"
++
++ "github.com/pkg/errors"
++)
++
+const (
+ DUBBO = "dubbo"
+)
+
+const (
+ mask = byte(127)
+ flag = byte(128)
+)
+
+const (
+ // Zero : byte zero
+ Zero = byte(0x00)
+)
+
+// constansts
+const (
+ TAG_READ = int32(-1)
+ ASCII_GAP = 32
+ CHUNK_SIZE = 4096
+ BC_BINARY = byte('B') // final chunk
+ BC_BINARY_CHUNK = byte('A') // non-final chunk
+
+ BC_BINARY_DIRECT = byte(0x20) // 1-byte length binary
+ BINARY_DIRECT_MAX = byte(0x0f)
+ BC_BINARY_SHORT = byte(0x34) // 2-byte length binary
+ BINARY_SHORT_MAX = 0x3ff // 0-1023 binary
+
+ BC_DATE = byte(0x4a) // 64-bit millisecond UTC date
+ BC_DATE_MINUTE = byte(0x4b) // 32-bit minute UTC date
+
+ BC_DOUBLE = byte('D') // IEEE 64-bit double
+
+ BC_DOUBLE_ZERO = byte(0x5b)
+ BC_DOUBLE_ONE = byte(0x5c)
+ BC_DOUBLE_BYTE = byte(0x5d)
+ BC_DOUBLE_SHORT = byte(0x5e)
+ BC_DOUBLE_MILL = byte(0x5f)
+
+ BC_FALSE = byte('F') // boolean false
+
+ BC_INT = byte('I') // 32-bit int
+
+ INT_DIRECT_MIN = -0x10
+ INT_DIRECT_MAX = byte(0x2f)
+ BC_INT_ZERO = byte(0x90)
+
+ INT_BYTE_MIN = -0x800
+ INT_BYTE_MAX = 0x7ff
+ BC_INT_BYTE_ZERO = byte(0xc8)
+
+ BC_END = byte('Z')
+
+ INT_SHORT_MIN = -0x40000
+ INT_SHORT_MAX = 0x3ffff
+ BC_INT_SHORT_ZERO = byte(0xd4)
+
+ BC_LIST_VARIABLE = byte(0x55)
+ BC_LIST_FIXED = byte('V')
+ BC_LIST_VARIABLE_UNTYPED = byte(0x57)
+ BC_LIST_FIXED_UNTYPED = byte(0x58)
+ _listFixedTypedLenTagMin = byte(0x70)
+ _listFixedTypedLenTagMax = byte(0x77)
+ _listFixedUntypedLenTagMin = byte(0x78)
+ _listFixedUntypedLenTagMax = byte(0x7f)
+
+ BC_LIST_DIRECT = byte(0x70)
+ BC_LIST_DIRECT_UNTYPED = byte(0x78)
+ LIST_DIRECT_MAX = byte(0x7)
+
+ BC_LONG = byte('L') // 64-bit signed integer
+ LONG_DIRECT_MIN = -0x08
+ LONG_DIRECT_MAX = byte(0x0f)
+ BC_LONG_ZERO = byte(0xe0)
+
+ LONG_BYTE_MIN = -0x800
+ LONG_BYTE_MAX = 0x7ff
+ BC_LONG_BYTE_ZERO = byte(0xf8)
+
+ LONG_SHORT_MIN = -0x40000
+ LONG_SHORT_MAX = 0x3ffff
+ BC_LONG_SHORT_ZERO = byte(0x3c)
+
+ BC_LONG_INT = byte(0x59)
+
+ BC_MAP = byte('M')
+ BC_MAP_UNTYPED = byte('H')
+
+ BC_NULL = byte('N') // x4e
+
+ BC_OBJECT = byte('O')
+ BC_OBJECT_DEF = byte('C')
+
+ BC_OBJECT_DIRECT = byte(0x60)
+ OBJECT_DIRECT_MAX = byte(0x0f)
+
+ BC_REF = byte(0x51)
+
+ BC_STRING = byte('S') // final string
+ BC_STRING_CHUNK = byte('R') // non-final string
+
+ BC_STRING_DIRECT = byte(0x00)
+ STRING_DIRECT_MAX = byte(0x1f)
+ BC_STRING_SHORT = byte(0x30)
+ STRING_SHORT_MAX = 0x3ff
+
+ BC_TRUE = byte('T')
+
+ P_PACKET_CHUNK = byte(0x4f)
+ P_PACKET = byte('P')
+
+ P_PACKET_DIRECT = byte(0x80)
+ PACKET_DIRECT_MAX = byte(0x7f)
+
+ P_PACKET_SHORT = byte(0x70)
+ PACKET_SHORT_MAX = 0xfff
+ ARRAY_STRING = "[string"
+ ARRAY_INT = "[int"
+ ARRAY_DOUBLE = "[double"
+ ARRAY_FLOAT = "[float"
+ ARRAY_BOOL = "[boolean"
+ ARRAY_LONG = "[long"
+
+ PATH_KEY = "path"
+ GROUP_KEY = "group"
+ INTERFACE_KEY = "interface"
+ VERSION_KEY = "version"
+ TIMEOUT_KEY = "timeout"
+
+ STRING_NIL = ""
+ STRING_TRUE = "true"
+ STRING_FALSE = "false"
+ STRING_ZERO = "0.0"
+ STRING_ONE = "1.0"
+)
+
+// ResponsePayload related consts
+const (
+ Response_OK byte = 20
+ Response_CLIENT_TIMEOUT byte = 30
+ Response_SERVER_TIMEOUT byte = 31
+ Response_BAD_REQUEST byte = 40
+ Response_BAD_RESPONSE byte = 50
+ Response_SERVICE_NOT_FOUND byte = 60
+ Response_SERVICE_ERROR byte = 70
+ Response_SERVER_ERROR byte = 80
+ Response_CLIENT_ERROR byte = 90
+
+ // According to "java dubbo" There are two cases of response:
+ // 1. with attachments
+ // 2. no attachments
+ RESPONSE_WITH_EXCEPTION int32 = 0
+ RESPONSE_VALUE int32 = 1
+ RESPONSE_NULL_VALUE int32 = 2
+ RESPONSE_WITH_EXCEPTION_WITH_ATTACHMENTS int32 = 3
+ RESPONSE_VALUE_WITH_ATTACHMENTS int32 = 4
+ RESPONSE_NULL_VALUE_WITH_ATTACHMENTS int32 = 5
+)
+
+/**
+ * the dubbo protocol header length is 16 Bytes.
+ * the first 2 Bytes is magic code '0xdabb'
+ * the next 1 Byte is message flags, in which its 16-20 bit is serial id, 21 for event, 22 for two way, 23 for request/response flag
+ * the next 1 Bytes is response state.
+ * the next 8 Bytes is package DI.
+ * the next 4 Bytes is package length.
+ **/
+const (
+ // header length.
+ HEADER_LENGTH = 16
+
+ // magic header
+ MAGIC = uint16(0xdabb)
+ MAGIC_HIGH = byte(0xda)
+ MAGIC_LOW = byte(0xbb)
+
+ // message flag.
+ FLAG_REQUEST = byte(0x80)
+ FLAG_TWOWAY = byte(0x40)
+ FLAG_EVENT = byte(0x20) // for heartbeat
+ SERIAL_MASK = 0x1f
+
+ DUBBO_VERSION = "2.5.4"
+ DUBBO_VERSION_KEY = "dubbo"
+ DEFAULT_DUBBO_PROTOCOL_VERSION = "2.0.2" // Dubbo RPC protocol version, for compatibility, it must not be between 2.0.10 ~ 2.6.2
+ LOWEST_VERSION_FOR_RESPONSE_ATTACHMENT = 2000200
+ DEFAULT_LEN = 8388608 // 8 * 1024 * 1024 default body max length
+)
+
+// regular
+const (
+ JAVA_IDENT_REGEX = "(?:[_$a-zA-Z][_$a-zA-Z0-9]*)"
+ CLASS_DESC = "(?:L" + JAVA_IDENT_REGEX + "(?:\\/" + JAVA_IDENT_REGEX + ")*;)"
+ ARRAY_DESC = "(?:\\[+(?:(?:[VZBCDFIJS])|" + CLASS_DESC + "))"
+ DESC_REGEX = "(?:(?:[VZBCDFIJS])|" + CLASS_DESC + "|" + ARRAY_DESC + ")"
+)
+
+// Dubbo request response related consts
+var (
+ DubboRequestHeaderBytesTwoWay = [HEADER_LENGTH]byte{MAGIC_HIGH, MAGIC_LOW, FLAG_REQUEST | FLAG_TWOWAY}
+ DubboRequestHeaderBytes = [HEADER_LENGTH]byte{MAGIC_HIGH, MAGIC_LOW, FLAG_REQUEST}
+ DubboResponseHeaderBytes = [HEADER_LENGTH]byte{MAGIC_HIGH, MAGIC_LOW, Zero, Response_OK}
+ DubboRequestHeartbeatHeader = [HEADER_LENGTH]byte{MAGIC_HIGH, MAGIC_LOW, FLAG_REQUEST | FLAG_TWOWAY | FLAG_EVENT}
+ DubboResponseHeartbeatHeader = [HEADER_LENGTH]byte{MAGIC_HIGH, MAGIC_LOW, FLAG_EVENT}
+)
+
+// Error part
+var (
+ ErrHeaderNotEnough = errors.New("header buffer too short")
+ ErrBodyNotEnough = errors.New("body buffer too short")
+ ErrJavaException = errors.New("got java exception")
+ ErrIllegalPackage = errors.New("illegal package!")
+)
+
+// DescRegex ...
+var DescRegex, _ = regexp.Compile(DESC_REGEX)
+
+var NilValue = reflect.Zero(reflect.TypeOf((*interface{})(nil)).Elem())
diff --cc protocol/dubbo/impl/proto.go
index ea1c55d,0000000..2622620
mode 100644,000000..100644
--- a/protocol/dubbo/impl/proto.go
+++ b/protocol/dubbo/impl/proto.go
@@@ -1,450 -1,0 +1,454 @@@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package impl
+
+import (
+ "bytes"
+ "encoding/binary"
+ "fmt"
++ "github.com/apache/dubbo-go/common/logger"
+ "io"
+ "reflect"
+ "strconv"
+ "strings"
+ "sync"
+ "time"
+)
+
+import (
+ "github.com/golang/protobuf/proto"
+ "github.com/matttproud/golang_protobuf_extensions/pbutil"
+ "github.com/pkg/errors"
+)
+
+import (
+ "github.com/apache/dubbo-go/common"
+ "github.com/apache/dubbo-go/common/constant"
+ pb "github.com/apache/dubbo-go/protocol/dubbo/impl/proto"
+)
+
+type ProtoSerializer struct{}
+
+func (p ProtoSerializer) Marshal(pkg DubboPackage) ([]byte, error) {
+ if pkg.IsHeartBeat() {
+ return []byte{byte('N')}, nil
+ }
+ if pkg.Body == nil {
+ return nil, errors.New("package body should not be nil")
+ }
+ if pkg.IsRequest() {
+ return marshalRequestProto(pkg)
+ }
+ return marshalResponseProto(pkg)
+}
+
+func (p ProtoSerializer) Unmarshal(data []byte, pkg *DubboPackage) error {
+ if pkg.IsRequest() {
+ return unmarshalRequestProto(data, pkg)
+ }
+ return unmarshalResponseProto(data, pkg)
+}
+
+func unmarshalResponseProto(data []byte, pkg *DubboPackage) error {
+ if pkg.Body == nil {
+ pkg.SetBody(NewResponsePayload(nil, nil, nil))
+ }
+ response := EnsureResponsePayload(pkg.Body)
+ buf := bytes.NewBuffer(data)
+
+ var responseType int32
+ if err := readByte(buf, &responseType); err != nil {
+ return err
+ }
+
+ hasAttachments := false
+ hasException := false
+ switch responseType {
+ case RESPONSE_VALUE_WITH_ATTACHMENTS:
+ hasAttachments = true
+ case RESPONSE_WITH_EXCEPTION:
+ hasException = true
+ case RESPONSE_WITH_EXCEPTION_WITH_ATTACHMENTS:
+ hasAttachments = true
+ hasException = true
+ }
+ if hasException {
+ throwable := pb.ThrowableProto{}
+ if err := readObject(buf, &throwable); err != nil {
+ return err
+ }
+ // generate error only with error message
+ response.Exception = errors.New(throwable.OriginalMessage)
+ } else {
+ // read response body
+ protoMsg, ok := response.RspObj.(proto.Message)
+ if !ok {
+ return errors.New("response rspobj not protobuf message")
+ }
+ if err := readObject(buf, protoMsg); err != nil {
+ return err
+ }
+ }
+
+ if hasAttachments {
+ atta := pb.Map{}
+ if err := readObject(buf, &atta); err != nil {
+ return err
+ }
+ if response.Attachments == nil {
+ response.Attachments = atta.Attachments
+ } else {
+ for k, v := range atta.Attachments {
+ response.Attachments[k] = v
+ }
+ }
+
+ }
+ return nil
+}
+
+func unmarshalRequestProto(data []byte, pkg *DubboPackage) error {
+ var dubboVersion string
+ var svcPath string
+ var svcVersion string
+ var svcMethod string
+ buf := bytes.NewBuffer(data)
+ if err := readUTF(buf, &dubboVersion); err != nil {
+ return err
+ }
+ if err := readUTF(buf, &svcPath); err != nil {
+ return err
+ }
+ if err := readUTF(buf, &svcVersion); err != nil {
+ return err
+ }
+ if err := readUTF(buf, &svcMethod); err != nil {
+ return err
+ }
+ // NOTE: protobuf rpc just have exact one parameter, while golang doesn't need this field
+ var argsType string
+ if err := readUTF(buf, &argsType); err != nil {
+ return err
+ }
+ // get raw body bytes for proxy methods to unmarshal
+ var protoMsgLength int
+ if err := readDelimitedLength(buf, &protoMsgLength); err != nil {
+ return err
+ }
+ argBytes := make([]byte, protoMsgLength)
+ if n, err := buf.Read(argBytes); err != nil {
+ if n != protoMsgLength {
+ return errors.New("illegal msg length")
+ }
+ return err
+ }
+ arg := getRegisterMessage(argsType)
- err := proto.Unmarshal(argBytes, arg.Interface().(JavaProto))
- if err != nil {
- panic(err)
++ if !arg.IsZero() {
++ err := proto.Unmarshal(argBytes, arg.Interface().(JavaProto))
++ if err != nil {
++ panic(err)
++ }
+ }
+
+ m := &pb.Map{}
+ if err := readObject(buf, m); err != nil {
+ return err
+ }
+ svc := Service{}
+ svc.Version = svcVersion
+ svc.Method = svcMethod
+ // just as hessian
+ svc.Path = svcPath
+ if svc.Path == "" && len(m.Attachments[constant.PATH_KEY]) > 0 {
+ svc.Path = m.Attachments[constant.PATH_KEY]
+ }
+
+ if _, ok := m.Attachments[constant.INTERFACE_KEY]; ok {
+ svc.Interface = m.Attachments[constant.INTERFACE_KEY]
+ } else {
+ svc.Interface = svc.Path
+ }
+ pkg.SetService(svc)
+ pkg.SetBody(map[string]interface{}{
+ "dubboVersion": dubboVersion,
+ "args": []interface{}{arg.Interface()},
+ "service": common.ServiceMap.GetService(DUBBO, svc.Path), // path as a key
+ "attachments": m.Attachments,
+ })
+
+ return nil
+}
+
+func marshalRequestProto(pkg DubboPackage) ([]byte, error) {
+ request := EnsureRequestPayload(pkg.Body)
+ args, ok := request.Params.([]interface{})
+ buf := bytes.NewBuffer(make([]byte, 0))
+ if !ok {
+ return nil, errors.New("proto buffer args should be marshaled in []byte")
+ }
+ // NOTE: protobuf rpc just has exact one parameter
+ if len(args) != 1 {
+ return nil, errors.New("illegal protobuf service, len(arg) should equal 1")
+ }
+ // dubbo version
+ if err := writeUTF(buf, DUBBO_VERSION); err != nil {
+ return nil, err
+ }
+ // service path
+ if err := writeUTF(buf, pkg.Service.Path); err != nil {
+ return nil, err
+ }
+ // service version
+ if err := writeUTF(buf, pkg.Service.Version); err != nil {
+ return nil, err
+ }
+ // service method
+ if err := writeUTF(buf, pkg.Service.Method); err != nil {
+ return nil, err
+ }
+ // parameter types desc
+ v := reflect.ValueOf(args[0])
+ mv := v.MethodByName("JavaClassName")
+ if mv.IsValid() {
+ javaCls := mv.Call([]reflect.Value{})
+ if len(javaCls) != 1 {
+ return nil, errors.New("JavaStringName method should return exact 1 result")
+ }
+ javaClsStr, ok := javaCls[0].Interface().(string)
+ if !ok {
+ return nil, errors.New("JavaClassName method should return string")
+ }
+ if err := writeUTF(buf, getJavaArgType(javaClsStr)); err != nil {
+ return nil, err
+ }
+ } else {
+ // defensive code
+ if err := writeUTF(buf, ""); err != nil {
+ return nil, err
+ }
+ }
+ // consumer args
+ protoMsg := args[0].(proto.Message)
+ if err := writeObject(buf, protoMsg); err != nil {
+ return nil, err
+ }
+ // attachments
+ atta := make(map[string]string)
+ atta[PATH_KEY] = pkg.Service.Path
+ atta[VERSION_KEY] = pkg.Service.Version
+ if len(pkg.Service.Group) > 0 {
+ atta[GROUP_KEY] = pkg.Service.Group
+ }
+ if len(pkg.Service.Interface) > 0 {
+ atta[INTERFACE_KEY] = pkg.Service.Interface
+ }
+ if pkg.Service.Timeout != 0 {
+ atta[TIMEOUT_KEY] = strconv.Itoa(int(pkg.Service.Timeout / time.Millisecond))
+ }
+ m := pb.Map{Attachments: atta}
+ if err := writeObject(buf, &m); err != nil {
+ return nil, err
+ }
+ return buf.Bytes(), nil
+}
+
+func marshalResponseProto(pkg DubboPackage) ([]byte, error) {
+ response := EnsureResponsePayload(pkg.Body)
+ buf := bytes.NewBuffer(make([]byte, 0))
+ responseType := RESPONSE_VALUE
+ hasAttachments := false
+ if response.Attachments != nil {
+ responseType = RESPONSE_VALUE_WITH_ATTACHMENTS
+ hasAttachments = true
+ } else {
+ responseType = RESPONSE_VALUE
+ }
+ if response.Exception != nil {
+ if hasAttachments {
+ responseType = RESPONSE_WITH_EXCEPTION_WITH_ATTACHMENTS
+ } else {
+ responseType = RESPONSE_WITH_EXCEPTION
+ }
+ }
+ // write response type
+ if err := writeByte(buf, responseType); err != nil {
+ return nil, err
+ }
+ if response.Exception != nil {
+ // deal with exception
+ throwable := pb.ThrowableProto{OriginalMessage: response.Exception.Error()}
+ if err := writeObject(buf, &throwable); err != nil {
+ return nil, err
+ }
+ } else {
+ res, ok := response.RspObj.(proto.Message)
+ if !ok {
+ return nil, errors.New("proto buffer params should be marshaled in proto.Message")
+ }
+ // response body
+ if err := writeObject(buf, res); err != nil {
+ return nil, err
+ }
+ }
+
+ if hasAttachments {
+ attachments := pb.Map{Attachments: response.Attachments}
+ if err := writeObject(buf, &attachments); err != nil {
+ return nil, err
+ }
+ }
+ return buf.Bytes(), nil
+}
+
+func init() {
+ SetSerializer("protobuf", ProtoSerializer{})
+}
+
+func getJavaArgType(javaClsName string) string {
+ return fmt.Sprintf("L%s;", strings.ReplaceAll(javaClsName, ".", "/"))
+}
+
+func writeUTF(writer io.Writer, value string) error {
+ _, err := pbutil.WriteDelimited(writer, &pb.StringValue{Value: value})
+ return err
+}
+
+func writeObject(writer io.Writer, value proto.Message) error {
+ _, err := pbutil.WriteDelimited(writer, value)
+ return err
+}
+
+func writeByte(writer io.Writer, v int32) error {
+ i32v := &pb.Int32Value{Value: v}
+ _, err := pbutil.WriteDelimited(writer, i32v)
+ return err
+}
+
+func readUTF(reader io.Reader, value *string) error {
+ sv := &pb.StringValue{}
+ _, err := pbutil.ReadDelimited(reader, sv)
+ if err != nil {
+ return err
+ }
+ *value = sv.Value
+ return nil
+}
+
+func readObject(reader io.Reader, value proto.Message) error {
+ _, err := pbutil.ReadDelimited(reader, value)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+// just as java protobuf serialize
+func readByte(reader io.Reader, value *int32) error {
+ i32v := &pb.Int32Value{}
+ _, err := pbutil.ReadDelimited(reader, i32v)
+ if err != nil {
+ return err
+ }
+ *value = i32v.Value
+ return nil
+}
+
+//
+func readDelimitedLength(reader io.Reader, length *int) error {
+ var headerBuf [binary.MaxVarintLen32]byte
+ var bytesRead, varIntBytes int
+ var messageLength uint64
+ for varIntBytes == 0 { // i.e. no varint has been decoded yet.
+ if bytesRead >= len(headerBuf) {
+ return errors.New("invalid varint32 encountered")
+ }
+ // We have to read byte by byte here to avoid reading more bytes
+ // than required. Each read byte is appended to what we have
+ // read before.
+ newBytesRead, err := reader.Read(headerBuf[bytesRead : bytesRead+1])
+ if newBytesRead == 0 {
+ if err != nil {
+ return err
+ }
+ // A Reader should not return (0, nil), but if it does,
+ // it should be treated as no-op (according to the
+ // Reader contract). So let's go on...
+ continue
+ }
+ bytesRead += newBytesRead
+ // Now present everything read so far to the varint decoder and
+ // see if a varint can be decoded already.
+ messageLength, varIntBytes = proto.DecodeVarint(headerBuf[:bytesRead])
+ }
+ *length = int(messageLength)
+ return nil
+}
+
+type JavaProto interface {
+ JavaClassName() string
+ proto.Message
+}
+
+type Register struct {
+ sync.RWMutex
+ registry map[string]reflect.Type
+}
+
+var (
+ register = Register{
+ registry: make(map[string]reflect.Type),
+ }
+)
+
+func RegisterMessage(msg JavaProto) {
+ register.Lock()
+ defer register.Unlock()
+
+ name := msg.JavaClassName()
+ name = getJavaArgType(name)
+
+ if e, ok := register.registry[name]; ok {
+ panic(fmt.Sprintf("msg: %v has been registered. existed: %v", msg.JavaClassName(), e))
+ }
+
+ register.registry[name] = typeOfMessage(msg)
+}
+
+func getRegisterMessage(sig string) reflect.Value {
+ register.Lock()
+ defer register.Unlock()
+
+ t, ok := register.registry[sig]
+ if !ok {
- panic(fmt.Sprintf("registry dose not have for svc: %v", sig))
++ logger.Error(fmt.Sprintf("registry dose not have for svc: %v", sig))
++ return NilValue
+ }
+ return reflect.New(t)
+}
+
+func typeOfMessage(o proto.Message) reflect.Type {
+ v := reflect.ValueOf(o)
+ switch v.Kind() {
+ case reflect.Struct:
+ return v.Type()
+ case reflect.Ptr:
+ return v.Elem().Type()
+ }
+
+ return reflect.TypeOf(o)
+}
diff --cc remoting/getty/getty_client_test.go
index 0000000,3dcc6b3..1272654
mode 000000,100644..100644
--- a/remoting/getty/getty_client_test.go
+++ b/remoting/getty/getty_client_test.go
@@@ -1,0 -1,497 +1,497 @@@
+ /*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ package getty
+
+ import (
+ "bytes"
+ "context"
+ "reflect"
+ "sync"
+ "testing"
+ "time"
+ )
+
+ import (
+ hessian "github.com/apache/dubbo-go-hessian2"
+ perrors "github.com/pkg/errors"
+ "github.com/stretchr/testify/assert"
+ )
+
+ import (
+ "github.com/apache/dubbo-go/common"
+ . "github.com/apache/dubbo-go/common/constant"
+ "github.com/apache/dubbo-go/common/proxy/proxy_factory"
+ "github.com/apache/dubbo-go/config"
+ "github.com/apache/dubbo-go/protocol"
+ "github.com/apache/dubbo-go/protocol/invocation"
+ "github.com/apache/dubbo-go/remoting"
+ )
+
+ func TestRunSuite(t *testing.T) {
+ svr, url := InitTest(t)
+ client := getClient(url)
+ testRequestOneWay(t, svr, url, client)
+ testClient_Call(t, svr, url, client)
+ testClient_AsyncCall(t, svr, url, client)
+
+ svr.Stop()
+ }
+
+ func testRequestOneWay(t *testing.T, svr *Server, url common.URL, client *Client) {
+
+ request := remoting.NewRequest("2.0.2")
+ up := &UserProvider{}
+ invocation := createInvocation("GetUser", nil, nil, []interface{}{[]interface{}{"1", "username"}, up},
+ []reflect.Value{reflect.ValueOf([]interface{}{"1", "username"}), reflect.ValueOf(up)})
+ attachment := map[string]string{INTERFACE_KEY: "com.ikurento.user.UserProvider"}
+ setAttachment(invocation, attachment)
+ request.Data = invocation
+ request.Event = false
+ request.TwoWay = false
+ //user := &User{}
+ err := client.Request(request, 3*time.Second, nil)
+ assert.NoError(t, err)
+ }
+
+ func createInvocation(methodName string, callback interface{}, reply interface{}, arguments []interface{},
+ parameterValues []reflect.Value) *invocation.RPCInvocation {
+ return invocation.NewRPCInvocationWithOptions(invocation.WithMethodName(methodName),
+ invocation.WithArguments(arguments), invocation.WithReply(reply),
+ invocation.WithCallBack(callback), invocation.WithParameterValues(parameterValues))
+ }
+
+ func setAttachment(invocation *invocation.RPCInvocation, attachments map[string]string) {
+ for key, value := range attachments {
+ invocation.SetAttachments(key, value)
+ }
+ }
+
+ func getClient(url common.URL) *Client {
+ client := NewClient(Options{
+ ConnectTimeout: config.GetConsumerConfig().ConnectTimeout,
+ })
+
+ exchangeClient := remoting.NewExchangeClient(url, client, 5*time.Second, false)
+ client.SetExchangeClient(exchangeClient)
+ client.Connect(url)
+ client.SetResponseHandler(exchangeClient)
+ return client
+ }
+
+ func testClient_Call(t *testing.T, svr *Server, url common.URL, c *Client) {
+ c.pool = newGettyRPCClientConnPool(c, clientConf.PoolSize, time.Duration(int(time.Second)*clientConf.PoolTTL))
+
+ testGetBigPkg(t, c)
+ testGetUser(t, c)
+ testGetUser0(t, c)
+ testGetUser1(t, c)
+ testGetUser2(t, c)
+ testGetUser3(t, c)
+ testGetUser4(t, c)
+ testGetUser5(t, c)
+ testGetUser6(t, c)
+ testGetUser61(t, c)
+
+ }
+ func testGetBigPkg(t *testing.T, c *Client) {
+ var (
+ user *User
+ err error
+ )
+
+ user = &User{}
+ request := remoting.NewRequest("2.0.2")
+ invocation := createInvocation("GetBigPkg", nil, nil, []interface{}{[]interface{}{nil}, user},
+ []reflect.Value{reflect.ValueOf([]interface{}{nil}), reflect.ValueOf(user)})
+ attachment := map[string]string{INTERFACE_KEY: "com.ikurento.user.UserProvider"}
+ setAttachment(invocation, attachment)
+ request.Data = invocation
+ request.Event = false
+ request.TwoWay = true
+ pendingResponse := remoting.NewPendingResponse(request.ID)
+ pendingResponse.Reply = user
+ remoting.AddPendingResponse(pendingResponse)
+ err = c.Request(request, 8*time.Second, pendingResponse)
+ assert.NoError(t, err)
+ assert.NotEqual(t, "", user.Id)
+ assert.NotEqual(t, "", user.Name)
+ }
+ func testGetUser(t *testing.T, c *Client) {
+ var (
+ user *User
+ err error
+ )
+ user = &User{}
+ request := remoting.NewRequest("2.0.2")
+ invocation := createInvocation("GetUser", nil, nil, []interface{}{"1", "username"},
+ []reflect.Value{reflect.ValueOf("1"), reflect.ValueOf("username")})
+ attachment := map[string]string{INTERFACE_KEY: "com.ikurento.user.UserProvider"}
+ setAttachment(invocation, attachment)
+ request.Data = invocation
+ request.Event = false
+ request.TwoWay = true
+ pendingResponse := remoting.NewPendingResponse(request.ID)
+ pendingResponse.Reply = user
+ remoting.AddPendingResponse(pendingResponse)
+ err = c.Request(request, 3*time.Second, pendingResponse)
+ assert.NoError(t, err)
+ assert.Equal(t, User{Id: "1", Name: "username"}, *user)
+ }
+
+ func testGetUser0(t *testing.T, c *Client) {
+ var (
+ user *User
+ err error
+ )
+ user = &User{}
+ request := remoting.NewRequest("2.0.2")
+ invocation := createInvocation("GetUser0", nil, nil, []interface{}{"1", nil, "username"},
+ []reflect.Value{reflect.ValueOf("1"), reflect.ValueOf(nil), reflect.ValueOf("username")})
+ attachment := map[string]string{INTERFACE_KEY: "com.ikurento.user.UserProvider"}
+ setAttachment(invocation, attachment)
+ request.Data = invocation
+ request.Event = false
+ request.TwoWay = true
+ rsp := remoting.NewPendingResponse(request.ID)
+ rsp.SetResponse(remoting.NewResponse(request.ID, "2.0.2"))
+ remoting.AddPendingResponse(rsp)
+ rsp.Reply = user
+ err = c.Request(request, 3*time.Second, rsp)
+ assert.NoError(t, err)
+ assert.Equal(t, User{Id: "1", Name: "username"}, *user)
+ }
+ func testGetUser1(t *testing.T, c *Client) {
+ var (
+ err error
+ )
+ request := remoting.NewRequest("2.0.2")
+ invocation := createInvocation("GetUser1", nil, nil, []interface{}{},
+ []reflect.Value{})
+ attachment := map[string]string{INTERFACE_KEY: "com.ikurento.user.UserProvider"}
+ setAttachment(invocation, attachment)
+ request.Data = invocation
+ request.Event = false
+ request.TwoWay = true
+ pendingResponse := remoting.NewPendingResponse(request.ID)
+ user := &User{}
+ pendingResponse.Reply = user
+ remoting.AddPendingResponse(pendingResponse)
+ err = c.Request(request, 3*time.Second, pendingResponse)
+ assert.NoError(t, err)
+ }
+ func testGetUser2(t *testing.T, c *Client) {
+ var (
+ err error
+ )
+ request := remoting.NewRequest("2.0.2")
+ invocation := createInvocation("GetUser2", nil, nil, []interface{}{},
+ []reflect.Value{})
+ attachment := map[string]string{INTERFACE_KEY: "com.ikurento.user.UserProvider"}
+ setAttachment(invocation, attachment)
+ request.Data = invocation
+ request.Event = false
+ request.TwoWay = true
+ pendingResponse := remoting.NewPendingResponse(request.ID)
+ remoting.AddPendingResponse(pendingResponse)
+ err = c.Request(request, 3*time.Second, pendingResponse)
+ assert.EqualError(t, err, "error")
+ }
+ func testGetUser3(t *testing.T, c *Client) {
+ var (
+ err error
+ )
+ request := remoting.NewRequest("2.0.2")
+ invocation := createInvocation("GetUser3", nil, nil, []interface{}{},
+ []reflect.Value{})
+ attachment := map[string]string{INTERFACE_KEY: "com.ikurento.user.UserProvider"}
+ setAttachment(invocation, attachment)
+ request.Data = invocation
+ request.Event = false
+ request.TwoWay = true
+ pendingResponse := remoting.NewPendingResponse(request.ID)
+ user2 := []interface{}{}
+ pendingResponse.Reply = &user2
+ remoting.AddPendingResponse(pendingResponse)
+ err = c.Request(request, 3*time.Second, pendingResponse)
+ assert.NoError(t, err)
+ assert.Equal(t, &User{Id: "1", Name: "username"}, user2[0])
+ }
+ func testGetUser4(t *testing.T, c *Client) {
+ var (
+ err error
+ )
+ request := remoting.NewRequest("2.0.2")
+ invocation := invocation.NewRPCInvocation("GetUser4", []interface{}{[]interface{}{"1", "username"}}, nil)
+ attachment := map[string]string{INTERFACE_KEY: "com.ikurento.user.UserProvider"}
+ setAttachment(invocation, attachment)
+ request.Data = invocation
+ request.Event = false
+ request.TwoWay = true
+ pendingResponse := remoting.NewPendingResponse(request.ID)
+ user2 := []interface{}{}
+ pendingResponse.Reply = &user2
+ remoting.AddPendingResponse(pendingResponse)
+ err = c.Request(request, 3*time.Second, pendingResponse)
+ assert.NoError(t, err)
+ assert.Equal(t, &User{Id: "1", Name: "username"}, user2[0])
+ }
+
+ func testGetUser5(t *testing.T, c *Client) {
+ var (
+ err error
+ )
+ request := remoting.NewRequest("2.0.2")
+ invocation := invocation.NewRPCInvocation("GetUser5", []interface{}{map[interface{}]interface{}{"id": "1", "name": "username"}}, nil)
+ attachment := map[string]string{INTERFACE_KEY: "com.ikurento.user.UserProvider"}
+ setAttachment(invocation, attachment)
+ request.Data = invocation
+ request.Event = false
+ request.TwoWay = true
+ pendingResponse := remoting.NewPendingResponse(request.ID)
+ user3 := map[interface{}]interface{}{}
+ pendingResponse.Reply = &user3
+ remoting.AddPendingResponse(pendingResponse)
+ err = c.Request(request, 3*time.Second, pendingResponse)
+ assert.NoError(t, err)
+ assert.NotNil(t, user3)
+ assert.Equal(t, &User{Id: "1", Name: "username"}, user3["key"])
+ }
+
+ func testGetUser6(t *testing.T, c *Client) {
+ var (
+ user *User
+ err error
+ )
+ user = &User{}
+ request := remoting.NewRequest("2.0.2")
+ invocation := invocation.NewRPCInvocation("GetUser6", []interface{}{0}, nil)
+ attachment := map[string]string{INTERFACE_KEY: "com.ikurento.user.UserProvider"}
+ setAttachment(invocation, attachment)
+ request.Data = invocation
+ request.Event = false
+ request.TwoWay = true
+ pendingResponse := remoting.NewPendingResponse(request.ID)
+ pendingResponse.Reply = user
+ remoting.AddPendingResponse(pendingResponse)
+ err = c.Request(request, 3*time.Second, pendingResponse)
+ assert.NoError(t, err)
+ assert.Equal(t, User{Id: "", Name: ""}, *user)
+ }
+
+ func testGetUser61(t *testing.T, c *Client) {
+ var (
+ user *User
+ err error
+ )
+ user = &User{}
+ request := remoting.NewRequest("2.0.2")
+ invocation := invocation.NewRPCInvocation("GetUser6", []interface{}{1}, nil)
+ attachment := map[string]string{INTERFACE_KEY: "com.ikurento.user.UserProvider"}
+ setAttachment(invocation, attachment)
+ request.Data = invocation
+ request.Event = false
+ request.TwoWay = true
+ pendingResponse := remoting.NewPendingResponse(request.ID)
+ pendingResponse.Reply = user
+ remoting.AddPendingResponse(pendingResponse)
+ err = c.Request(request, 3*time.Second, pendingResponse)
+ assert.NoError(t, err)
+ assert.Equal(t, User{Id: "1", Name: ""}, *user)
+ }
+
+ func testClient_AsyncCall(t *testing.T, svr *Server, url common.URL, client *Client) {
+ user := &User{}
+ lock := sync.Mutex{}
+ request := remoting.NewRequest("2.0.2")
+ invocation := createInvocation("GetUser0", nil, nil, []interface{}{"4", nil, "username"},
+ []reflect.Value{reflect.ValueOf("4"), reflect.ValueOf(nil), reflect.ValueOf("username")})
+ attachment := map[string]string{INTERFACE_KEY: "com.ikurento.user.UserProvider"}
+ setAttachment(invocation, attachment)
+ request.Data = invocation
+ request.Event = false
+ request.TwoWay = true
+ rsp := remoting.NewPendingResponse(request.ID)
+ rsp.SetResponse(remoting.NewResponse(request.ID, "2.0.2"))
+ remoting.AddPendingResponse(rsp)
+ rsp.Reply = user
+ rsp.Callback = func(response common.CallbackResponse) {
+ r := response.(remoting.AsyncCallbackResponse)
+ rst := *r.Reply.(*remoting.Response).Result.(*protocol.RPCResult)
+ assert.Equal(t, User{Id: "4", Name: "username"}, *(rst.Rest.(*User)))
+ lock.Unlock()
+ }
+ lock.Lock()
+ err := client.Request(request, 3*time.Second, rsp)
+ assert.NoError(t, err)
+ assert.Equal(t, User{}, *user)
+ time.Sleep(1 * time.Second)
+ }
+
+ func InitTest(t *testing.T) (*Server, common.URL) {
+
+ hessian.RegisterPOJO(&User{})
+ remoting.RegistryCodec("dubbo", &DubboTestCodec{})
+
- methods, err := common.ServiceMap.Register("dubbo", &UserProvider{})
++ methods, err := common.ServiceMap.Register("", "dubbo", &UserProvider{})
+ assert.NoError(t, err)
+ assert.Equal(t, "GetBigPkg,GetUser,GetUser0,GetUser1,GetUser2,GetUser3,GetUser4,GetUser5,GetUser6", methods)
+
+ // config
+ SetClientConf(ClientConfig{
+ ConnectionNum: 2,
+ HeartbeatPeriod: "5s",
+ SessionTimeout: "20s",
+ PoolTTL: 600,
+ PoolSize: 64,
+ GettySessionParam: GettySessionParam{
+ CompressEncoding: false,
+ TcpNoDelay: true,
+ TcpKeepAlive: true,
+ KeepAlivePeriod: "120s",
+ TcpRBufSize: 262144,
+ TcpWBufSize: 65536,
+ PkgWQSize: 512,
+ TcpReadTimeout: "4s",
+ TcpWriteTimeout: "5s",
+ WaitTimeout: "1s",
+ MaxMsgLen: 10240000000,
+ SessionName: "client",
+ },
+ })
+ assert.NoError(t, clientConf.CheckValidity())
+ SetServerConfig(ServerConfig{
+ SessionNumber: 700,
+ SessionTimeout: "20s",
+ GettySessionParam: GettySessionParam{
+ CompressEncoding: false,
+ TcpNoDelay: true,
+ TcpKeepAlive: true,
+ KeepAlivePeriod: "120s",
+ TcpRBufSize: 262144,
+ TcpWBufSize: 65536,
+ PkgWQSize: 512,
+ TcpReadTimeout: "1s",
+ TcpWriteTimeout: "5s",
+ WaitTimeout: "1s",
+ MaxMsgLen: 10240000000,
+ SessionName: "server",
+ }})
+ assert.NoError(t, srvConf.CheckValidity())
+
+ url, err := common.NewURL("dubbo://127.0.0.1:20060/UserProvider?anyhost=true&" +
+ "application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&" +
+ "environment=dev&interface=com.ikurento.user.UserProvider&ip=127.0.0.1&methods=GetUser%2C&" +
+ "module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&" +
+ "side=provider&timeout=3000×tamp=1556509797245&bean.name=UserProvider")
+ // init server
+ userProvider := &UserProvider{}
- common.ServiceMap.Register(url.Protocol, userProvider)
++ common.ServiceMap.Register("", url.Protocol, userProvider)
+ invoker := &proxy_factory.ProxyInvoker{
+ BaseInvoker: *protocol.NewBaseInvoker(url),
+ }
+ handler := func(invocation *invocation.RPCInvocation) protocol.RPCResult {
+ //result := protocol.RPCResult{}
+ r := invoker.Invoke(context.Background(), invocation)
+ result := protocol.RPCResult{
+ Err: r.Error(),
+ Rest: r.Result(),
+ Attrs: r.Attachments(),
+ }
+ return result
+ }
+ server := NewServer(url, handler)
+ server.Start()
+
+ time.Sleep(time.Second * 2)
+
+ return server, url
+ }
+
+ //////////////////////////////////
+ // provider
+ //////////////////////////////////
+
+ type (
+ User struct {
+ Id string `json:"id"`
+ Name string `json:"name"`
+ }
+
+ UserProvider struct {
+ user map[string]User
+ }
+ )
+
+ // size:4801228
+ func (u *UserProvider) GetBigPkg(ctx context.Context, req []interface{}, rsp *User) error {
+ argBuf := new(bytes.Buffer)
+ for i := 0; i < 400; i++ {
+ argBuf.WriteString("击鼓其镗,踊跃用兵。土国城漕,我独南行。从孙子仲,平陈与宋。不我以归,忧心有忡。爰居爰处?爰丧其马?于以求之?于林之下。死生契阔,与子成说。执子之手,与子偕老。于嗟阔兮,不我活兮。于嗟洵兮,不我信兮。")
+ argBuf.WriteString("击鼓其镗,踊跃用兵。土国城漕,我独南行。从孙子仲,平陈与宋。不我以归,忧心有忡。爰居爰处?爰丧其马?于以求之?于林之下。死生契阔,与子成说。执子之手,与子偕老。于嗟阔兮,不我活兮。于嗟洵兮,不我信兮。")
+ }
+ rsp.Id = argBuf.String()
+ rsp.Name = argBuf.String()
+ return nil
+ }
+
+ func (u *UserProvider) GetUser(ctx context.Context, req []interface{}, rsp *User) error {
+ rsp.Id = req[0].(string)
+ rsp.Name = req[1].(string)
+ return nil
+ }
+
+ func (u *UserProvider) GetUser0(id string, k *User, name string) (User, error) {
+ return User{Id: id, Name: name}, nil
+ }
+
+ func (u *UserProvider) GetUser1() error {
+ return nil
+ }
+
+ func (u *UserProvider) GetUser2() error {
+ return perrors.New("error")
+ }
+
+ func (u *UserProvider) GetUser3(rsp *[]interface{}) error {
+ *rsp = append(*rsp, User{Id: "1", Name: "username"})
+ return nil
+ }
+
+ func (u *UserProvider) GetUser4(ctx context.Context, req []interface{}) ([]interface{}, error) {
+
+ return []interface{}{User{Id: req[0].([]interface{})[0].(string), Name: req[0].([]interface{})[1].(string)}}, nil
+ }
+
+ func (u *UserProvider) GetUser5(ctx context.Context, req []interface{}) (map[interface{}]interface{}, error) {
+ return map[interface{}]interface{}{"key": User{Id: req[0].(map[interface{}]interface{})["id"].(string), Name: req[0].(map[interface{}]interface{})["name"].(string)}}, nil
+ }
+
+ func (u *UserProvider) GetUser6(id int64) (*User, error) {
+ if id == 0 {
+ return nil, nil
+ }
+ return &User{Id: "1"}, nil
+ }
+
+ func (u *UserProvider) Reference() string {
+ return "UserProvider"
+ }
+
+ func (u User) JavaClassName() string {
+ return "com.ikurento.user.User"
+ }
[dubbo-go] 01/04: Fix: resolve conflicts
Posted by fa...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
fangyc pushed a commit to branch refact-seri
in repository https://gitbox.apache.org/repos/asf/dubbo-go.git
commit 6b93bbc4f7ecf046098ac3beaaf10e4be5f40980
Author: fangyincheng <fa...@sina.com>
AuthorDate: Sat Jun 27 18:14:50 2020 +0800
Fix: resolve conflicts
---
common/constant/default.go | 1 +
common/constant/key.go | 1 +
common/constant/serializtion.go | 28 ++
common/url.go | 27 ++
config/service_config.go | 4 +-
go.mod | 1 +
protocol/dubbo/client.go | 301 +-----------
protocol/dubbo/client_test.go | 101 ++--
protocol/dubbo/codec.go | 157 -------
protocol/dubbo/codec_test.go | 83 ----
protocol/dubbo/dubbo_invoker.go | 21 +-
protocol/dubbo/dubbo_invoker_test.go | 15 +-
protocol/dubbo/dubbo_protocol.go | 11 +-
protocol/dubbo/dubbo_protocol_test.go | 15 +-
protocol/dubbo/impl/codec.go | 299 ++++++++++++
protocol/dubbo/impl/codec_test.go | 197 ++++++++
protocol/dubbo/impl/const.go | 243 ++++++++++
protocol/dubbo/impl/hessian.go | 508 +++++++++++++++++++++
protocol/dubbo/impl/package.go | 171 +++++++
protocol/dubbo/impl/proto.go | 450 ++++++++++++++++++
protocol/dubbo/impl/proto/payload.pb.go | 345 ++++++++++++++
protocol/dubbo/impl/proto/payload.proto | 78 ++++
.../{client.go => impl/remoting/client_impl.go} | 266 ++++++-----
protocol/dubbo/{ => impl/remoting}/config.go | 48 +-
protocol/dubbo/impl/remoting/errors.go | 17 +
protocol/dubbo/{ => impl/remoting}/pool.go | 27 +-
protocol/dubbo/{ => impl/remoting}/readwriter.go | 163 +++----
.../{server.go => impl/remoting/server_impl.go} | 86 +---
.../remoting/server_listener.go} | 173 +++----
protocol/dubbo/impl/request.go | 40 ++
protocol/dubbo/impl/response.go | 46 ++
protocol/dubbo/impl/serialization.go | 54 +++
protocol/dubbo/impl/serialize.go | 40 ++
protocol/dubbo/server.go | 173 +++----
.../dubbo/{listener_test.go => server_test.go} | 4 +-
test/integrate/dubbo/go-server/server.go | 2 +-
36 files changed, 3056 insertions(+), 1140 deletions(-)
diff --git a/common/constant/default.go b/common/constant/default.go
index c69989b..dd75691 100644
--- a/common/constant/default.go
+++ b/common/constant/default.go
@@ -44,6 +44,7 @@ const (
DEFAULT_REST_CLIENT = "resty"
DEFAULT_REST_SERVER = "go-restful"
DEFAULT_PORT = 20000
+ DEFAULT_SERIALIZATION = HESSIAN2_SERIALIZATION
)
const (
diff --git a/common/constant/key.go b/common/constant/key.go
index 5be63fe..6e73183 100644
--- a/common/constant/key.go
+++ b/common/constant/key.go
@@ -77,6 +77,7 @@ const (
EXECUTE_REJECTED_EXECUTION_HANDLER_KEY = "execute.limit.rejected.handler"
PROVIDER_SHUTDOWN_FILTER = "pshutdown"
CONSUMER_SHUTDOWN_FILTER = "cshutdown"
+ SERIALIZATION_KEY = "serialization"
)
const (
diff --git a/common/constant/serializtion.go b/common/constant/serializtion.go
new file mode 100644
index 0000000..f27598c
--- /dev/null
+++ b/common/constant/serializtion.go
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package constant
+
+const (
+ S_Hessian2 byte = 2
+ S_Proto byte = 21
+)
+
+const (
+ HESSIAN2_SERIALIZATION = "hessian2"
+ PROTOBUF_SERIALIZATION = "protobuf"
+)
diff --git a/common/url.go b/common/url.go
index 1cfa47a..51eabff 100644
--- a/common/url.go
+++ b/common/url.go
@@ -23,6 +23,7 @@ import (
"math"
"net"
"net/url"
+ "sort"
"strconv"
"strings"
)
@@ -650,3 +651,29 @@ func mergeNormalParam(mergedUrl *URL, referenceUrl *URL, paramKeys []string) []f
}
return methodConfigMergeFcn
}
+
+// doesn't encode url reserve character, url.QueryEscape will do this work
+// reference: https://github.com/golang/go.git, src/net/url/url.go, Encode method
+func ParamsUnescapeEncode(params url.Values) string {
+ if params == nil {
+ return ""
+ }
+ var buf strings.Builder
+ keys := make([]string, len(params))
+ for k := range params {
+ keys = append(keys, k)
+ }
+ sort.Strings(keys)
+ for _, k := range keys {
+ vs := params[k]
+ for _, v := range vs {
+ if buf.Len() > 0 {
+ buf.WriteByte('&')
+ }
+ buf.WriteString(k)
+ buf.WriteByte('=')
+ buf.WriteString(v)
+ }
+ }
+ return buf.String()
+}
diff --git a/config/service_config.go b/config/service_config.go
index 70a344c..a49af18 100644
--- a/config/service_config.go
+++ b/config/service_config.go
@@ -59,6 +59,7 @@ type ServiceConfig struct {
Methods []*MethodConfig `yaml:"methods" json:"methods,omitempty" property:"methods"`
Warmup string `yaml:"warmup" json:"warmup,omitempty" property:"warmup"`
Retries string `yaml:"retries" json:"retries,omitempty" property:"retries"`
+ Serialization string `yaml:"serialization" json:"serialization" property:"serialization"`
Params map[string]string `yaml:"params" json:"params,omitempty" property:"params"`
Token string `yaml:"token" json:"token,omitempty" property:"token"`
AccessLog string `yaml:"accesslog" json:"accesslog,omitempty" property:"accesslog"`
@@ -228,7 +229,8 @@ func (c *ServiceConfig) getUrlMap() url.Values {
urlMap.Set(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER))
urlMap.Set(constant.RELEASE_KEY, "dubbo-golang-"+constant.Version)
urlMap.Set(constant.SIDE_KEY, (common.RoleType(common.PROVIDER)).Role())
-
+ // todo: move
+ urlMap.Set(constant.SERIALIZATION_KEY, c.Serialization)
// application info
urlMap.Set(constant.APPLICATION_KEY, providerConfig.ApplicationConfig.Name)
urlMap.Set(constant.ORGANIZATION_KEY, providerConfig.ApplicationConfig.Organization)
diff --git a/go.mod b/go.mod
index 887df01..45d839d 100644
--- a/go.mod
+++ b/go.mod
@@ -38,6 +38,7 @@ require (
github.com/lestrrat/go-file-rotatelogs v0.0.0-20180223000712-d3151e2a480f // indirect
github.com/lestrrat/go-strftime v0.0.0-20180220042222-ba3bf9c1d042 // indirect
github.com/magiconair/properties v1.8.1
+ github.com/matttproud/golang_protobuf_extensions v1.0.1
github.com/mitchellh/mapstructure v1.1.2
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd
github.com/nacos-group/nacos-sdk-go v0.0.0-20191128082542-fe1b325b125c
diff --git a/protocol/dubbo/client.go b/protocol/dubbo/client.go
index 6d1b771..0bd7aac 100644
--- a/protocol/dubbo/client.go
+++ b/protocol/dubbo/client.go
@@ -19,36 +19,17 @@ package dubbo
import (
"math/rand"
- "strings"
- "sync"
"time"
)
import (
- hessian "github.com/apache/dubbo-go-hessian2"
- "github.com/dubbogo/getty"
- gxsync "github.com/dubbogo/gost/sync"
- perrors "github.com/pkg/errors"
- "go.uber.org/atomic"
"gopkg.in/yaml.v2"
)
import (
- "github.com/apache/dubbo-go/common"
- "github.com/apache/dubbo-go/common/constant"
"github.com/apache/dubbo-go/common/logger"
"github.com/apache/dubbo-go/config"
-)
-
-var (
- errInvalidCodecType = perrors.New("illegal CodecType")
- errInvalidAddress = perrors.New("remote address invalid or empty")
- errSessionNotExist = perrors.New("session not exist")
- errClientClosed = perrors.New("client closed")
- errClientReadTimeout = perrors.New("client read timeout")
-
- clientConf *ClientConfig
- clientGrpool *gxsync.TaskPool
+ "github.com/apache/dubbo-go/protocol/dubbo/impl/remoting"
)
func init() {
@@ -60,7 +41,7 @@ func init() {
return
}
protocolConf := config.GetConsumerConfig().ProtocolConf
- defaultClientConfig := GetDefaultClientConfig()
+ defaultClientConfig := remoting.GetDefaultClientConfig()
if protocolConf == nil {
logger.Info("protocol_conf default use dubbo config")
} else {
@@ -78,286 +59,12 @@ func init() {
panic(err)
}
}
- clientConf = &defaultClientConfig
+ clientConf := &defaultClientConfig
if err := clientConf.CheckValidity(); err != nil {
logger.Warnf("[CheckValidity] error: %v", err)
return
}
- setClientGrpool()
+ remoting.SetClientConf(*clientConf)
rand.Seed(time.Now().UnixNano())
}
-
-// SetClientConf set dubbo client config.
-func SetClientConf(c ClientConfig) {
- clientConf = &c
- err := clientConf.CheckValidity()
- if err != nil {
- logger.Warnf("[ClientConfig CheckValidity] error: %v", err)
- return
- }
- setClientGrpool()
-}
-
-// GetClientConf get dubbo client config.
-func GetClientConf() ClientConfig {
- return *clientConf
-}
-
-func setClientGrpool() {
- if clientConf.GrPoolSize > 1 {
- clientGrpool = gxsync.NewTaskPool(gxsync.WithTaskPoolTaskPoolSize(clientConf.GrPoolSize), gxsync.WithTaskPoolTaskQueueLength(clientConf.QueueLen),
- gxsync.WithTaskPoolTaskQueueNumber(clientConf.QueueNumber))
- }
-}
-
-// Options is option for create dubbo client
-type Options struct {
- // connect timeout
- ConnectTimeout time.Duration
- // request timeout
- RequestTimeout time.Duration
-}
-
-//AsyncCallbackResponse async response for dubbo
-type AsyncCallbackResponse struct {
- common.CallbackResponse
- Opts Options
- Cause error
- Start time.Time // invoke(call) start time == write start time
- ReadStart time.Time // read start time, write duration = ReadStart - Start
- Reply interface{}
-}
-
-// Client is dubbo protocol client.
-type Client struct {
- opts Options
- conf ClientConfig
- pool *gettyRPCClientPool
- sequence atomic.Uint64
-
- pendingResponses *sync.Map
-}
-
-// NewClient create a new Client.
-func NewClient(opt Options) *Client {
-
- switch {
- case opt.ConnectTimeout == 0:
- opt.ConnectTimeout = 3 * time.Second
- fallthrough
- case opt.RequestTimeout == 0:
- opt.RequestTimeout = 3 * time.Second
- }
-
- // make sure that client request sequence is an odd number
- initSequence := uint64(rand.Int63n(time.Now().UnixNano()))
- if initSequence%2 == 0 {
- initSequence++
- }
-
- c := &Client{
- opts: opt,
- pendingResponses: new(sync.Map),
- conf: *clientConf,
- }
- c.sequence.Store(initSequence)
- c.pool = newGettyRPCClientConnPool(c, clientConf.PoolSize, time.Duration(int(time.Second)*clientConf.PoolTTL))
-
- return c
-}
-
-// Request is dubbo protocol request.
-type Request struct {
- addr string
- svcUrl common.URL
- method string
- args interface{}
- atta map[string]string
-}
-
-// NewRequest create a new Request.
-func NewRequest(addr string, svcUrl common.URL, method string, args interface{}, atta map[string]string) *Request {
- return &Request{
- addr: addr,
- svcUrl: svcUrl,
- method: method,
- args: args,
- atta: atta,
- }
-}
-
-// Response is dubbo protocol response.
-type Response struct {
- reply interface{}
- atta map[string]string
-}
-
-// NewResponse create a new Response.
-func NewResponse(reply interface{}, atta map[string]string) *Response {
- return &Response{
- reply: reply,
- atta: atta,
- }
-}
-
-// CallOneway call by one way
-func (c *Client) CallOneway(request *Request) error {
-
- return perrors.WithStack(c.call(CT_OneWay, request, NewResponse(nil, nil), nil))
-}
-
-// Call call remoting by two way or one way, if @response.reply is nil, the way of call is one way.
-func (c *Client) Call(request *Request, response *Response) error {
- ct := CT_TwoWay
- if response.reply == nil {
- ct = CT_OneWay
- }
-
- return perrors.WithStack(c.call(ct, request, response, nil))
-}
-
-// AsyncCall call remoting by async with callback.
-func (c *Client) AsyncCall(request *Request, callback common.AsyncCallback, response *Response) error {
- return perrors.WithStack(c.call(CT_TwoWay, request, response, callback))
-}
-
-func (c *Client) call(ct CallType, request *Request, response *Response, callback common.AsyncCallback) error {
- p := &DubboPackage{}
- p.Service.Path = strings.TrimPrefix(request.svcUrl.Path, "/")
- p.Service.Interface = request.svcUrl.GetParam(constant.INTERFACE_KEY, "")
- p.Service.Version = request.svcUrl.GetParam(constant.VERSION_KEY, "")
- p.Service.Group = request.svcUrl.GetParam(constant.GROUP_KEY, "")
- p.Service.Method = request.method
-
- p.Service.Timeout = c.opts.RequestTimeout
- var timeout = request.svcUrl.GetParam(strings.Join([]string{constant.METHOD_KEYS, request.method + constant.RETRIES_KEY}, "."), "")
- if len(timeout) != 0 {
- if t, err := time.ParseDuration(timeout); err == nil {
- p.Service.Timeout = t
- }
- }
-
- p.Header.SerialID = byte(S_Dubbo)
- p.Body = hessian.NewRequest(request.args, request.atta)
-
- var rsp *PendingResponse
- if ct != CT_OneWay {
- p.Header.Type = hessian.PackageRequest_TwoWay
- rsp = NewPendingResponse()
- rsp.response = response
- rsp.callback = callback
- } else {
- p.Header.Type = hessian.PackageRequest
- }
-
- var (
- err error
- session getty.Session
- conn *gettyRPCClient
- )
- conn, session, err = c.selectSession(request.addr)
- if err != nil {
- return perrors.WithStack(err)
- }
- if session == nil {
- return errSessionNotExist
- }
- defer func() {
- if err == nil {
- c.pool.put(conn)
- return
- }
- conn.close()
- }()
-
- if err = c.transfer(session, p, rsp); err != nil {
- return perrors.WithStack(err)
- }
-
- if ct == CT_OneWay || callback != nil {
- return nil
- }
-
- select {
- case <-getty.GetTimeWheel().After(c.opts.RequestTimeout):
- c.removePendingResponse(SequenceType(rsp.seq))
- return perrors.WithStack(errClientReadTimeout)
- case <-rsp.done:
- err = rsp.err
- }
-
- return perrors.WithStack(err)
-}
-
-// Close close the client pool.
-func (c *Client) Close() {
- if c.pool != nil {
- c.pool.close()
- }
- c.pool = nil
-}
-
-func (c *Client) selectSession(addr string) (*gettyRPCClient, getty.Session, error) {
- rpcClient, err := c.pool.getGettyRpcClient(DUBBO, addr)
- if err != nil {
- return nil, nil, perrors.WithStack(err)
- }
- return rpcClient, rpcClient.selectSession(), nil
-}
-
-func (c *Client) heartbeat(session getty.Session) error {
- return c.transfer(session, nil, NewPendingResponse())
-}
-
-func (c *Client) transfer(session getty.Session, pkg *DubboPackage,
- rsp *PendingResponse) error {
-
- var (
- sequence uint64
- err error
- )
-
- sequence = c.sequence.Add(1)
-
- if pkg == nil {
- pkg = &DubboPackage{}
- pkg.Body = hessian.NewRequest([]interface{}{}, nil)
- pkg.Body = []interface{}{}
- pkg.Header.Type = hessian.PackageHeartbeat
- pkg.Header.SerialID = byte(S_Dubbo)
- }
- pkg.Header.ID = int64(sequence)
-
- // cond1
- if rsp != nil {
- rsp.seq = sequence
- c.addPendingResponse(rsp)
- }
-
- err = session.WritePkg(pkg, c.opts.RequestTimeout)
- if err != nil {
- c.removePendingResponse(SequenceType(rsp.seq))
- } else if rsp != nil { // cond2
- // cond2 should not merged with cond1. cause the response package may be returned very
- // soon and it will be handled by other goroutine.
- rsp.readStart = time.Now()
- }
-
- return perrors.WithStack(err)
-}
-
-func (c *Client) addPendingResponse(pr *PendingResponse) {
- c.pendingResponses.Store(SequenceType(pr.seq), pr)
-}
-
-func (c *Client) removePendingResponse(seq SequenceType) *PendingResponse {
- if c.pendingResponses == nil {
- return nil
- }
- if presp, ok := c.pendingResponses.Load(seq); ok {
- c.pendingResponses.Delete(seq)
- return presp.(*PendingResponse)
- }
- return nil
-}
diff --git a/protocol/dubbo/client_test.go b/protocol/dubbo/client_test.go
index 744ffa8..51e532e 100644
--- a/protocol/dubbo/client_test.go
+++ b/protocol/dubbo/client_test.go
@@ -1,18 +1,18 @@
/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements. See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You under the Apache License, Version 2.0
+* (the "License"); you may not use this file except in compliance with
+* the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
*/
package dubbo
@@ -35,23 +35,24 @@ import (
"github.com/apache/dubbo-go/common"
"github.com/apache/dubbo-go/common/proxy/proxy_factory"
"github.com/apache/dubbo-go/protocol"
+ "github.com/apache/dubbo-go/protocol/dubbo/impl/remoting"
)
func TestClient_CallOneway(t *testing.T) {
proto, url := InitTest(t)
- c := &Client{
- pendingResponses: new(sync.Map),
- conf: *clientConf,
- opts: Options{
+ c := &remoting.Client{
+ PendingResponses: new(sync.Map),
+ Conf: *remoting.GetClientConf(),
+ Opts: remoting.Options{
ConnectTimeout: 3e9,
RequestTimeout: 6e9,
},
}
- c.pool = newGettyRPCClientConnPool(c, clientConf.PoolSize, time.Duration(int(time.Second)*clientConf.PoolTTL))
+ c.Pool = remoting.NewGettyRPCClientConnPool(c, remoting.GetClientConf().PoolSize, time.Duration(int(time.Second)*remoting.GetClientConf().PoolTTL))
//user := &User{}
- err := c.CallOneway(NewRequest("127.0.0.1:20000", url, "GetUser", []interface{}{"1", "username"}, nil))
+ err := c.CallOneway(remoting.NewRequest("127.0.0.1:20000", url, "GetUser", []interface{}{"1", "username"}, nil))
assert.NoError(t, err)
// destroy
@@ -61,15 +62,15 @@ func TestClient_CallOneway(t *testing.T) {
func TestClient_Call(t *testing.T) {
proto, url := InitTest(t)
- c := &Client{
- pendingResponses: new(sync.Map),
- conf: *clientConf,
- opts: Options{
+ c := &remoting.Client{
+ PendingResponses: new(sync.Map),
+ Conf: *remoting.GetClientConf(),
+ Opts: remoting.Options{
ConnectTimeout: 3e9,
RequestTimeout: 10e9,
},
}
- c.pool = newGettyRPCClientConnPool(c, clientConf.PoolSize, time.Duration(int(time.Second)*clientConf.PoolTTL))
+ c.Pool = remoting.NewGettyRPCClientConnPool(c, remoting.GetClientConf().PoolSize, time.Duration(int(time.Second)*remoting.GetClientConf().PoolTTL))
var (
user *User
@@ -77,50 +78,50 @@ func TestClient_Call(t *testing.T) {
)
user = &User{}
- err = c.Call(NewRequest("127.0.0.1:20000", url, "GetBigPkg", []interface{}{nil}, nil), NewResponse(user, nil))
+ err = c.Call(remoting.NewRequest("127.0.0.1:20000", url, "GetBigPkg", []interface{}{nil}, nil), remoting.NewResponse(user, nil))
assert.NoError(t, err)
assert.NotEqual(t, "", user.Id)
assert.NotEqual(t, "", user.Name)
user = &User{}
- err = c.Call(NewRequest("127.0.0.1:20000", url, "GetUser", []interface{}{"1", "username"}, nil), NewResponse(user, nil))
+ err = c.Call(remoting.NewRequest("127.0.0.1:20000", url, "GetUser", []interface{}{"1", "username"}, nil), remoting.NewResponse(user, nil))
assert.NoError(t, err)
assert.Equal(t, User{Id: "1", Name: "username"}, *user)
user = &User{}
- err = c.Call(NewRequest("127.0.0.1:20000", url, "GetUser0", []interface{}{"1", nil, "username"}, nil), NewResponse(user, nil))
+ err = c.Call(remoting.NewRequest("127.0.0.1:20000", url, "GetUser0", []interface{}{"1", nil, "username"}, nil), remoting.NewResponse(user, nil))
assert.NoError(t, err)
assert.Equal(t, User{Id: "1", Name: "username"}, *user)
- err = c.Call(NewRequest("127.0.0.1:20000", url, "GetUser1", []interface{}{}, nil), NewResponse(user, nil))
+ err = c.Call(remoting.NewRequest("127.0.0.1:20000", url, "GetUser1", []interface{}{}, nil), remoting.NewResponse(user, nil))
assert.NoError(t, err)
- err = c.Call(NewRequest("127.0.0.1:20000", url, "GetUser2", []interface{}{}, nil), NewResponse(user, nil))
+ err = c.Call(remoting.NewRequest("127.0.0.1:20000", url, "GetUser2", []interface{}{}, nil), remoting.NewResponse(user, nil))
assert.EqualError(t, err, "error")
user2 := []interface{}{}
- err = c.Call(NewRequest("127.0.0.1:20000", url, "GetUser3", []interface{}{}, nil), NewResponse(&user2, nil))
+ err = c.Call(remoting.NewRequest("127.0.0.1:20000", url, "GetUser3", []interface{}{}, nil), remoting.NewResponse(&user2, nil))
assert.NoError(t, err)
assert.Equal(t, &User{Id: "1", Name: "username"}, user2[0])
user2 = []interface{}{}
- err = c.Call(NewRequest("127.0.0.1:20000", url, "GetUser4", []interface{}{[]interface{}{"1", "username"}}, nil), NewResponse(&user2, nil))
+ err = c.Call(remoting.NewRequest("127.0.0.1:20000", url, "GetUser4", []interface{}{[]interface{}{"1", "username"}}, nil), remoting.NewResponse(&user2, nil))
assert.NoError(t, err)
assert.Equal(t, &User{Id: "1", Name: "username"}, user2[0])
user3 := map[interface{}]interface{}{}
- err = c.Call(NewRequest("127.0.0.1:20000", url, "GetUser5", []interface{}{map[interface{}]interface{}{"id": "1", "name": "username"}}, nil), NewResponse(&user3, nil))
+ err = c.Call(remoting.NewRequest("127.0.0.1:20000", url, "GetUser5", []interface{}{map[interface{}]interface{}{"id": "1", "name": "username"}}, nil), remoting.NewResponse(&user3, nil))
assert.NoError(t, err)
assert.NotNil(t, user3)
assert.Equal(t, &User{Id: "1", Name: "username"}, user3["key"])
user = &User{}
- err = c.Call(NewRequest("127.0.0.1:20000", url, "GetUser6", []interface{}{0}, nil), NewResponse(user, nil))
+ err = c.Call(remoting.NewRequest("127.0.0.1:20000", url, "GetUser6", []interface{}{0}, nil), remoting.NewResponse(user, nil))
assert.NoError(t, err)
assert.Equal(t, User{Id: "", Name: ""}, *user)
user = &User{}
- err = c.Call(NewRequest("127.0.0.1:20000", url, "GetUser6", []interface{}{1}, nil), NewResponse(user, nil))
+ err = c.Call(remoting.NewRequest("127.0.0.1:20000", url, "GetUser6", []interface{}{1}, nil), remoting.NewResponse(user, nil))
assert.NoError(t, err)
assert.Equal(t, User{Id: "1", Name: ""}, *user)
@@ -131,24 +132,24 @@ func TestClient_Call(t *testing.T) {
func TestClient_AsyncCall(t *testing.T) {
proto, url := InitTest(t)
- c := &Client{
- pendingResponses: new(sync.Map),
- conf: *clientConf,
- opts: Options{
+ c := &remoting.Client{
+ PendingResponses: new(sync.Map),
+ Conf: *remoting.GetClientConf(),
+ Opts: remoting.Options{
ConnectTimeout: 3e9,
RequestTimeout: 6e9,
},
}
- c.pool = newGettyRPCClientConnPool(c, clientConf.PoolSize, time.Duration(int(time.Second)*clientConf.PoolTTL))
+ c.Pool = remoting.NewGettyRPCClientConnPool(c, remoting.GetClientConf().PoolSize, time.Duration(int(time.Second)*remoting.GetClientConf().PoolTTL))
user := &User{}
lock := sync.Mutex{}
lock.Lock()
- err := c.AsyncCall(NewRequest("127.0.0.1:20000", url, "GetUser", []interface{}{"1", "username"}, nil), func(response common.CallbackResponse) {
- r := response.(AsyncCallbackResponse)
- assert.Equal(t, User{Id: "1", Name: "username"}, *r.Reply.(*Response).reply.(*User))
+ err := c.AsyncCall(remoting.NewRequest("127.0.0.1:20000", url, "GetUser", []interface{}{"1", "username"}, nil), func(response common.CallbackResponse) {
+ r := response.(remoting.AsyncCallbackResponse)
+ assert.Equal(t, User{Id: "1", Name: "username"}, *r.Reply.(*remoting.Response).Reply.(*User))
lock.Unlock()
- }, NewResponse(user, nil))
+ }, remoting.NewResponse(user, nil))
assert.NoError(t, err)
assert.Equal(t, User{}, *user)
@@ -167,13 +168,13 @@ func InitTest(t *testing.T) (protocol.Protocol, common.URL) {
assert.Equal(t, "GetBigPkg,GetUser,GetUser0,GetUser1,GetUser2,GetUser3,GetUser4,GetUser5,GetUser6", methods)
// config
- SetClientConf(ClientConfig{
+ remoting.SetClientConf(remoting.ClientConfig{
ConnectionNum: 2,
HeartbeatPeriod: "5s",
SessionTimeout: "20s",
PoolTTL: 600,
PoolSize: 64,
- GettySessionParam: GettySessionParam{
+ GettySessionParam: remoting.GettySessionParam{
CompressEncoding: false,
TcpNoDelay: true,
TcpKeepAlive: true,
@@ -188,11 +189,11 @@ func InitTest(t *testing.T) (protocol.Protocol, common.URL) {
SessionName: "client",
},
})
- assert.NoError(t, clientConf.CheckValidity())
- SetServerConfig(ServerConfig{
+ assert.NoError(t, remoting.GetClientConf().CheckValidity())
+ remoting.SetServerConfig(remoting.ServerConfig{
SessionNumber: 700,
SessionTimeout: "20s",
- GettySessionParam: GettySessionParam{
+ GettySessionParam: remoting.GettySessionParam{
CompressEncoding: false,
TcpNoDelay: true,
TcpKeepAlive: true,
@@ -206,7 +207,7 @@ func InitTest(t *testing.T) (protocol.Protocol, common.URL) {
MaxMsgLen: 10240000000,
SessionName: "server",
}})
- assert.NoError(t, srvConf.CheckValidity())
+ assert.NoError(t, remoting.GetServerConfig().CheckValidity())
// Export
proto := GetProtocol()
diff --git a/protocol/dubbo/codec.go b/protocol/dubbo/codec.go
deleted file mode 100644
index 1f7d107..0000000
--- a/protocol/dubbo/codec.go
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dubbo
-
-import (
- "bufio"
- "bytes"
- "fmt"
- "time"
-)
-
-import (
- "github.com/apache/dubbo-go-hessian2"
- "github.com/apache/dubbo-go/common"
- perrors "github.com/pkg/errors"
-)
-
-//SerialID serial ID
-type SerialID byte
-
-const (
- // S_Dubbo dubbo serial id
- S_Dubbo SerialID = 2
-)
-
-//CallType call type
-type CallType int32
-
-const (
- // CT_UNKNOWN unknown call type
- CT_UNKNOWN CallType = 0
- // CT_OneWay call one way
- CT_OneWay CallType = 1
- // CT_TwoWay call in request/response
- CT_TwoWay CallType = 2
-)
-
-////////////////////////////////////////////
-// dubbo package
-////////////////////////////////////////////
-
-// SequenceType ...
-type SequenceType int64
-
-// DubboPackage ...
-type DubboPackage struct {
- Header hessian.DubboHeader
- Service hessian.Service
- Body interface{}
- Err error
-}
-
-// String prints dubbo package detail include header、path、body etc.
-func (p DubboPackage) String() string {
- return fmt.Sprintf("DubboPackage: Header-%v, Path-%v, Body-%v", p.Header, p.Service, p.Body)
-}
-
-// Marshal encode hessian package.
-func (p *DubboPackage) Marshal() (*bytes.Buffer, error) {
- codec := hessian.NewHessianCodec(nil)
-
- pkg, err := codec.Write(p.Service, p.Header, p.Body)
- if err != nil {
- return nil, perrors.WithStack(err)
- }
-
- return bytes.NewBuffer(pkg), nil
-}
-
-// Unmarshal dncode hessian package.
-func (p *DubboPackage) Unmarshal(buf *bytes.Buffer, opts ...interface{}) error {
- // fix issue https://github.com/apache/dubbo-go/issues/380
- bufLen := buf.Len()
- if bufLen < hessian.HEADER_LENGTH {
- return perrors.WithStack(hessian.ErrHeaderNotEnough)
- }
-
- codec := hessian.NewHessianCodec(bufio.NewReaderSize(buf, bufLen))
-
- // read header
- err := codec.ReadHeader(&p.Header)
- if err != nil {
- return perrors.WithStack(err)
- }
-
- if len(opts) != 0 { // for client
- client, ok := opts[0].(*Client)
- if !ok {
- return perrors.Errorf("opts[0] is not of type *Client")
- }
-
- if p.Header.Type&hessian.PackageRequest != 0x00 {
- // size of this array must be '7'
- // https://github.com/apache/dubbo-go-hessian2/blob/master/request.go#L272
- p.Body = make([]interface{}, 7)
- } else {
- pendingRsp, ok := client.pendingResponses.Load(SequenceType(p.Header.ID))
- if !ok {
- return perrors.Errorf("client.GetPendingResponse(%v) = nil", p.Header.ID)
- }
- p.Body = &hessian.Response{RspObj: pendingRsp.(*PendingResponse).response.reply}
- }
- }
-
- // read body
- err = codec.ReadBody(p.Body)
- return perrors.WithStack(err)
-}
-
-////////////////////////////////////////////
-// PendingResponse
-////////////////////////////////////////////
-
-// PendingResponse is a pending response.
-type PendingResponse struct {
- seq uint64
- err error
- start time.Time
- readStart time.Time
- callback common.AsyncCallback
- response *Response
- done chan struct{}
-}
-
-// NewPendingResponse create a PendingResponses.
-func NewPendingResponse() *PendingResponse {
- return &PendingResponse{
- start: time.Now(),
- response: &Response{},
- done: make(chan struct{}),
- }
-}
-
-// GetCallResponse get AsyncCallbackResponse.
-func (r PendingResponse) GetCallResponse() common.CallbackResponse {
- return AsyncCallbackResponse{
- Cause: r.err,
- Start: r.start,
- ReadStart: r.readStart,
- Reply: r.response,
- }
-}
diff --git a/protocol/dubbo/codec_test.go b/protocol/dubbo/codec_test.go
deleted file mode 100644
index 5dc71f0..0000000
--- a/protocol/dubbo/codec_test.go
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package dubbo
-
-import (
- "bytes"
- "testing"
- "time"
-)
-
-import (
- hessian "github.com/apache/dubbo-go-hessian2"
- perrors "github.com/pkg/errors"
- "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.Path = "path"
- 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.0.2", pkgres.Body.([]interface{})[0])
- assert.Equal(t, "path", pkgres.Body.([]interface{})[1])
- assert.Equal(t, "2.6", pkgres.Body.([]interface{})[2])
- assert.Equal(t, "Method", pkgres.Body.([]interface{})[3])
- assert.Equal(t, "Ljava/lang/String;", pkgres.Body.([]interface{})[4])
- assert.Equal(t, []interface{}{"a"}, pkgres.Body.([]interface{})[5])
- assert.Equal(t, map[string]string{"dubbo": "2.0.2", "interface": "Service", "path": "path", "timeout": "1000", "version": "2.6"}, pkgres.Body.([]interface{})[6])
-}
-
-func TestIssue380(t *testing.T) {
- pkg := &DubboPackage{}
- buf := bytes.NewBuffer([]byte("hello"))
- err := pkg.Unmarshal(buf)
- assert.True(t, perrors.Cause(err) == hessian.ErrHeaderNotEnough)
-}
diff --git a/protocol/dubbo/dubbo_invoker.go b/protocol/dubbo/dubbo_invoker.go
index 59202d5..1907c38 100644
--- a/protocol/dubbo/dubbo_invoker.go
+++ b/protocol/dubbo/dubbo_invoker.go
@@ -35,6 +35,7 @@ import (
"github.com/apache/dubbo-go/common/constant"
"github.com/apache/dubbo-go/common/logger"
"github.com/apache/dubbo-go/protocol"
+ "github.com/apache/dubbo-go/protocol/dubbo/impl/remoting"
invocation_impl "github.com/apache/dubbo-go/protocol/invocation"
)
@@ -52,14 +53,14 @@ var (
// DubboInvoker is dubbo client invoker.
type DubboInvoker struct {
protocol.BaseInvoker
- client *Client
+ client *remoting.Client
quitOnce sync.Once
// Used to record the number of requests. -1 represent this DubboInvoker is destroyed
reqNum int64
}
-// NewDubboInvoker create dubbo client invoker.
-func NewDubboInvoker(url common.URL, client *Client) *DubboInvoker {
+// NewDubboInvoker ...
+func NewDubboInvoker(url common.URL, client *remoting.Client) *DubboInvoker {
return &DubboInvoker{
BaseInvoker: *protocol.NewBaseInvoker(url),
client: client,
@@ -94,29 +95,33 @@ func (di *DubboInvoker) Invoke(ctx context.Context, invocation protocol.Invocati
di.appendCtx(ctx, inv)
url := di.GetUrl()
+ // default hessian2 serialization, compatible
+ if url.GetParam("serialization", "") == "" {
+ url.SetParam("serialization", constant.HESSIAN2_SERIALIZATION)
+ }
// async
async, err := strconv.ParseBool(inv.AttachmentsByKey(constant.ASYNC_KEY, "false"))
if err != nil {
logger.Errorf("ParseBool - error: %v", err)
async = false
}
- response := NewResponse(inv.Reply(), nil)
+ response := remoting.NewResponse(inv.Reply(), nil)
if async {
if callBack, ok := inv.CallBack().(func(response common.CallbackResponse)); ok {
- result.Err = di.client.AsyncCall(NewRequest(url.Location, url, inv.MethodName(), inv.Arguments(), inv.Attachments()), callBack, response)
+ result.Err = di.client.AsyncCall(remoting.NewRequest(url.Location, url, inv.MethodName(), inv.Arguments(), inv.Attachments()), callBack, response)
} else {
- result.Err = di.client.CallOneway(NewRequest(url.Location, url, inv.MethodName(), inv.Arguments(), inv.Attachments()))
+ result.Err = di.client.CallOneway(remoting.NewRequest(url.Location, url, inv.MethodName(), inv.Arguments(), inv.Attachments()))
}
} else {
if inv.Reply() == nil {
result.Err = ErrNoReply
} else {
- result.Err = di.client.Call(NewRequest(url.Location, url, inv.MethodName(), inv.Arguments(), inv.Attachments()), response)
+ result.Err = di.client.Call(remoting.NewRequest(url.Location, url, inv.MethodName(), inv.Arguments(), inv.Attachments()), response)
}
}
if result.Err == nil {
result.Rest = inv.Reply()
- result.Attrs = response.atta
+ result.Attrs = response.Atta
}
logger.Debugf("result.Err: %v, result.Rest: %v", result.Err, result.Rest)
diff --git a/protocol/dubbo/dubbo_invoker_test.go b/protocol/dubbo/dubbo_invoker_test.go
index 1a64301..e96e859 100644
--- a/protocol/dubbo/dubbo_invoker_test.go
+++ b/protocol/dubbo/dubbo_invoker_test.go
@@ -32,21 +32,22 @@ import (
import (
"github.com/apache/dubbo-go/common"
"github.com/apache/dubbo-go/common/constant"
+ "github.com/apache/dubbo-go/protocol/dubbo/impl/remoting"
"github.com/apache/dubbo-go/protocol/invocation"
)
func TestDubboInvoker_Invoke(t *testing.T) {
proto, url := InitTest(t)
- c := &Client{
- pendingResponses: new(sync.Map),
- conf: *clientConf,
- opts: Options{
+ c := &remoting.Client{
+ PendingResponses: new(sync.Map),
+ Conf: *remoting.GetClientConf(),
+ Opts: remoting.Options{
ConnectTimeout: 3 * time.Second,
RequestTimeout: 6 * time.Second,
},
}
- c.pool = newGettyRPCClientConnPool(c, clientConf.PoolSize, time.Duration(int(time.Second)*clientConf.PoolTTL))
+ c.Pool = remoting.NewGettyRPCClientConnPool(c, remoting.GetClientConf().PoolSize, time.Duration(int(time.Second)*remoting.GetClientConf().PoolTTL))
invoker := NewDubboInvoker(url, c)
user := &User{}
@@ -69,8 +70,8 @@ func TestDubboInvoker_Invoke(t *testing.T) {
lock := sync.Mutex{}
lock.Lock()
inv.SetCallBack(func(response common.CallbackResponse) {
- r := response.(AsyncCallbackResponse)
- assert.Equal(t, User{Id: "1", Name: "username"}, *r.Reply.(*Response).reply.(*User))
+ r := response.(remoting.AsyncCallbackResponse)
+ assert.Equal(t, User{Id: "1", Name: "username"}, *r.Reply.(*remoting.Response).Reply.(*User))
lock.Unlock()
})
res = invoker.Invoke(context.Background(), inv)
diff --git a/protocol/dubbo/dubbo_protocol.go b/protocol/dubbo/dubbo_protocol.go
index 9eeefd0..cc67569 100644
--- a/protocol/dubbo/dubbo_protocol.go
+++ b/protocol/dubbo/dubbo_protocol.go
@@ -29,9 +29,9 @@ import (
"github.com/apache/dubbo-go/common/logger"
"github.com/apache/dubbo-go/config"
"github.com/apache/dubbo-go/protocol"
+ "github.com/apache/dubbo-go/protocol/dubbo/impl/remoting"
)
-// dubbo protocol constant
const (
// DUBBO is dubbo protocol name
DUBBO = "dubbo"
@@ -48,7 +48,7 @@ var (
// DubboProtocol is a dubbo protocol implement.
type DubboProtocol struct {
protocol.BaseProtocol
- serverMap map[string]*Server
+ serverMap map[string]*remoting.Server
serverLock sync.Mutex
}
@@ -56,7 +56,7 @@ type DubboProtocol struct {
func NewDubboProtocol() *DubboProtocol {
return &DubboProtocol{
BaseProtocol: protocol.NewBaseProtocol(),
- serverMap: make(map[string]*Server),
+ serverMap: make(map[string]*remoting.Server),
}
}
@@ -67,7 +67,6 @@ func (dp *DubboProtocol) Export(invoker protocol.Invoker) protocol.Exporter {
exporter := NewDubboExporter(serviceKey, invoker, dp.ExporterMap())
dp.SetExporterMap(serviceKey, exporter)
logger.Infof("Export service: %s", url.String())
-
// start server
dp.openServer(url)
return exporter
@@ -83,7 +82,7 @@ func (dp *DubboProtocol) Refer(url common.URL) protocol.Invoker {
requestTimeout = t
}
- invoker := NewDubboInvoker(url, NewClient(Options{
+ invoker := NewDubboInvoker(url, remoting.NewClient(remoting.Options{
ConnectTimeout: config.GetConsumerConfig().ConnectTimeout,
RequestTimeout: requestTimeout,
}))
@@ -116,7 +115,7 @@ func (dp *DubboProtocol) openServer(url common.URL) {
dp.serverLock.Lock()
_, ok = dp.serverMap[url.Location]
if !ok {
- srv := NewServer()
+ srv := remoting.NewServer(NewStubHandler())
dp.serverMap[url.Location] = srv
srv.Start(url)
}
diff --git a/protocol/dubbo/dubbo_protocol_test.go b/protocol/dubbo/dubbo_protocol_test.go
index 14f6868..1915a07 100644
--- a/protocol/dubbo/dubbo_protocol_test.go
+++ b/protocol/dubbo/dubbo_protocol_test.go
@@ -22,19 +22,21 @@ import (
)
import (
- "github.com/stretchr/testify/assert"
-)
-
-import (
"github.com/apache/dubbo-go/common"
"github.com/apache/dubbo-go/common/constant"
"github.com/apache/dubbo-go/protocol"
+ "github.com/apache/dubbo-go/protocol/dubbo/impl/remoting"
+)
+
+import (
+ "github.com/stretchr/testify/assert"
)
func TestDubboProtocol_Export(t *testing.T) {
+ srvCfg := remoting.GetDefaultServerConfig()
+ remoting.SetServerConfig(srvCfg)
// Export
proto := GetProtocol()
- srvConf = &ServerConfig{}
url, err := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&" +
"application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&" +
"environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&" +
@@ -75,6 +77,8 @@ func TestDubboProtocol_Export(t *testing.T) {
}
func TestDubboProtocol_Refer(t *testing.T) {
+ cliCfg := remoting.GetDefaultClientConfig()
+ remoting.SetClientConf(cliCfg)
// Refer
proto := GetProtocol()
url, err := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&" +
@@ -83,7 +87,6 @@ func TestDubboProtocol_Refer(t *testing.T) {
"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
diff --git a/protocol/dubbo/impl/codec.go b/protocol/dubbo/impl/codec.go
new file mode 100644
index 0000000..f527bbc
--- /dev/null
+++ b/protocol/dubbo/impl/codec.go
@@ -0,0 +1,299 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package impl
+
+import (
+ "bufio"
+ "encoding/binary"
+)
+
+import (
+ hessian "github.com/apache/dubbo-go-hessian2"
+ "github.com/pkg/errors"
+)
+
+import (
+ "github.com/apache/dubbo-go/common/logger"
+)
+
+type DubboCodec struct {
+ reader *bufio.Reader
+ pkgType PackageType
+ bodyLen int
+ serializer Serializer
+ headerRead bool
+}
+
+//CallType call type
+type CallType int32
+
+const (
+ // CT_UNKNOWN unknown call type
+ CT_UNKNOWN CallType = 0
+ // CT_OneWay call one way
+ CT_OneWay CallType = 1
+ // CT_TwoWay call in request/response
+ CT_TwoWay CallType = 2
+)
+
+////////////////////////////////////////////
+// dubbo package
+////////////////////////////////////////////
+type SequenceType int64
+
+func (c *DubboCodec) ReadHeader(header *DubboHeader) error {
+ var err error
+ if c.reader.Size() < HEADER_LENGTH {
+ return hessian.ErrHeaderNotEnough
+ }
+ buf, err := c.reader.Peek(HEADER_LENGTH)
+ if err != nil { // this is impossible
+ return errors.WithStack(err)
+ }
+ _, err = c.reader.Discard(HEADER_LENGTH)
+ if err != nil { // this is impossible
+ return errors.WithStack(err)
+ }
+
+ //// read header
+ if buf[0] != MAGIC_HIGH && buf[1] != MAGIC_LOW {
+ return hessian.ErrIllegalPackage
+ }
+
+ // Header{serialization id(5 bit), event, two way, req/response}
+ if header.SerialID = buf[2] & SERIAL_MASK; header.SerialID == Zero {
+ return errors.Errorf("serialization ID:%v", header.SerialID)
+ }
+
+ flag := buf[2] & FLAG_EVENT
+ if flag != Zero {
+ header.Type |= PackageHeartbeat
+ }
+ flag = buf[2] & FLAG_REQUEST
+ if flag != Zero {
+ header.Type |= PackageRequest
+ flag = buf[2] & FLAG_TWOWAY
+ if flag != Zero {
+ header.Type |= PackageRequest_TwoWay
+ }
+ } else {
+ header.Type |= PackageResponse
+ header.ResponseStatus = buf[3]
+ if header.ResponseStatus != Response_OK {
+ header.Type |= PackageResponse_Exception
+ }
+ }
+
+ // Header{req id}
+ header.ID = int64(binary.BigEndian.Uint64(buf[4:]))
+
+ // Header{body len}
+ header.BodyLen = int(binary.BigEndian.Uint32(buf[12:]))
+ if header.BodyLen < 0 {
+ return hessian.ErrIllegalPackage
+ }
+
+ c.pkgType = header.Type
+ c.bodyLen = header.BodyLen
+
+ if c.reader.Buffered() < c.bodyLen {
+ return hessian.ErrBodyNotEnough
+ }
+ c.headerRead = true
+ return errors.WithStack(err)
+}
+
+func (c *DubboCodec) EncodeHeader(p DubboPackage) []byte {
+ header := p.Header
+ bs := make([]byte, 0)
+ switch header.Type {
+ case PackageHeartbeat:
+ if header.ResponseStatus == Zero {
+ bs = append(bs, hessian.DubboRequestHeartbeatHeader[:]...)
+ } else {
+ bs = append(bs, hessian.DubboResponseHeartbeatHeader[:]...)
+ }
+ case PackageResponse:
+ bs = append(bs, hessian.DubboResponseHeaderBytes[:]...)
+ if header.ResponseStatus != 0 {
+ bs[3] = header.ResponseStatus
+ }
+ case PackageRequest_TwoWay:
+ bs = append(bs, hessian.DubboRequestHeaderBytesTwoWay[:]...)
+ }
+ bs[2] |= header.SerialID & hessian.SERIAL_MASK
+ binary.BigEndian.PutUint64(bs[4:], uint64(header.ID))
+ return bs
+}
+
+func (c *DubboCodec) Encode(p DubboPackage) ([]byte, error) {
+ // header
+ if c.serializer == nil {
+ return nil, errors.New("serializer should not be nil")
+ }
+ header := p.Header
+ switch header.Type {
+ case PackageHeartbeat:
+ if header.ResponseStatus == Zero {
+ return packRequest(p, c.serializer)
+ }
+ return packResponse(p, c.serializer)
+
+ case PackageRequest, PackageRequest_TwoWay:
+ return packRequest(p, c.serializer)
+
+ case PackageResponse:
+ return packResponse(p, c.serializer)
+
+ default:
+ return nil, errors.Errorf("Unrecognised message type: %v", header.Type)
+ }
+}
+
+func (c *DubboCodec) Decode(p *DubboPackage) error {
+ if !c.headerRead {
+ if err := c.ReadHeader(&p.Header); err != nil {
+ return err
+ }
+ }
+ body, err := c.reader.Peek(p.GetBodyLen())
+ if err != nil {
+ return err
+ }
+ if p.IsResponseWithException() {
+ logger.Infof("response with exception: %+v", p.Header)
+ decoder := hessian.NewDecoder(body)
+ exception, err := decoder.Decode()
+ if err != nil {
+ return errors.WithStack(err)
+ }
+ rsp, ok := p.Body.(*ResponsePayload)
+ if !ok {
+ return errors.Errorf("java exception:%s", exception.(string))
+ }
+ rsp.Exception = errors.Errorf("java exception:%s", exception.(string))
+ return nil
+ } else if p.IsHeartBeat() {
+ // heartbeat no need to unmarshal contents
+ return nil
+ }
+ if c.serializer == nil {
+ return errors.New("Codec serializer is nil")
+ }
+ return c.serializer.Unmarshal(body, p)
+}
+
+func (c *DubboCodec) SetSerializer(serializer Serializer) {
+ c.serializer = serializer
+}
+
+func packRequest(p DubboPackage, serializer Serializer) ([]byte, error) {
+ var (
+ byteArray []byte
+ pkgLen int
+ )
+
+ header := p.Header
+
+ //////////////////////////////////////////
+ // byteArray
+ //////////////////////////////////////////
+ // magic
+ switch header.Type {
+ case PackageHeartbeat:
+ byteArray = append(byteArray, DubboRequestHeartbeatHeader[:]...)
+ case PackageRequest_TwoWay:
+ byteArray = append(byteArray, DubboRequestHeaderBytesTwoWay[:]...)
+ default:
+ byteArray = append(byteArray, DubboRequestHeaderBytes[:]...)
+ }
+
+ // serialization id, two way flag, event, request/response flag
+ // SerialID is id of serialization approach in java dubbo
+ byteArray[2] |= header.SerialID & SERIAL_MASK
+ // request id
+ binary.BigEndian.PutUint64(byteArray[4:], uint64(header.ID))
+
+ //////////////////////////////////////////
+ // body
+ //////////////////////////////////////////
+ if p.IsHeartBeat() {
+ byteArray = append(byteArray, byte('N'))
+ pkgLen = 1
+ } else {
+ body, err := serializer.Marshal(p)
+ if err != nil {
+ return nil, err
+ }
+ pkgLen = len(body)
+ if pkgLen > int(DEFAULT_LEN) { // 8M
+ return nil, errors.Errorf("Data length %d too large, max payload %d", pkgLen, DEFAULT_LEN)
+ }
+ byteArray = append(byteArray, body...)
+ }
+ binary.BigEndian.PutUint32(byteArray[12:], uint32(pkgLen))
+ return byteArray, nil
+}
+
+func packResponse(p DubboPackage, serializer Serializer) ([]byte, error) {
+ var (
+ byteArray []byte
+ )
+ header := p.Header
+ hb := p.IsHeartBeat()
+
+ // magic
+ if hb {
+ byteArray = append(byteArray, DubboResponseHeartbeatHeader[:]...)
+ } else {
+ byteArray = append(byteArray, DubboResponseHeaderBytes[:]...)
+ }
+ // set serialID, identify serialization types, eg: fastjson->6, hessian2->2
+ byteArray[2] |= header.SerialID & SERIAL_MASK
+ // response status
+ if header.ResponseStatus != 0 {
+ byteArray[3] = header.ResponseStatus
+ }
+
+ // request id
+ binary.BigEndian.PutUint64(byteArray[4:], uint64(header.ID))
+
+ // body
+ body, err := serializer.Marshal(p)
+ if err != nil {
+ return nil, err
+ }
+
+ pkgLen := len(body)
+ if pkgLen > int(DEFAULT_LEN) { // 8M
+ return nil, errors.Errorf("Data length %d too large, max payload %d", pkgLen, DEFAULT_LEN)
+ }
+ // byteArray{body length}
+ binary.BigEndian.PutUint32(byteArray[12:], uint32(pkgLen))
+ byteArray = append(byteArray, body...)
+ return byteArray, nil
+}
+
+func NewDubboCodec(reader *bufio.Reader) *DubboCodec {
+ return &DubboCodec{
+ reader: reader,
+ pkgType: 0,
+ bodyLen: 0,
+ headerRead: false,
+ }
+}
diff --git a/protocol/dubbo/impl/codec_test.go b/protocol/dubbo/impl/codec_test.go
new file mode 100644
index 0000000..c93f307
--- /dev/null
+++ b/protocol/dubbo/impl/codec_test.go
@@ -0,0 +1,197 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package impl
+
+import (
+ "testing"
+ "time"
+)
+
+import (
+ "github.com/golang/protobuf/proto"
+ "github.com/stretchr/testify/assert"
+)
+
+import (
+ "github.com/apache/dubbo-go/common/constant"
+ pb "github.com/apache/dubbo-go/protocol/dubbo/impl/proto"
+)
+
+func TestDubboPackage_MarshalAndUnmarshal(t *testing.T) {
+ pkg := NewDubboPackage(nil)
+ pkg.Body = []interface{}{"a"}
+ pkg.Header.Type = PackageHeartbeat
+ pkg.Header.SerialID = constant.S_Hessian2
+ pkg.Header.ID = 10086
+ pkg.SetSerializer(HessianSerializer{})
+
+ // heartbeat
+ data, err := pkg.Marshal()
+ assert.NoError(t, err)
+
+ pkgres := NewDubboPackage(data)
+ pkgres.SetSerializer(HessianSerializer{})
+
+ pkgres.Body = []interface{}{}
+ err = pkgres.Unmarshal()
+ assert.NoError(t, err)
+ assert.Equal(t, PackageHeartbeat|PackageRequest|PackageRequest_TwoWay, pkgres.Header.Type)
+ assert.Equal(t, constant.S_Hessian2, pkgres.Header.SerialID)
+ assert.Equal(t, int64(10086), pkgres.Header.ID)
+ assert.Equal(t, 0, len(pkgres.Body.([]interface{})))
+
+ // request
+ pkg.Header.Type = PackageRequest
+ pkg.Service.Interface = "Service"
+ pkg.Service.Path = "path"
+ pkg.Service.Version = "2.6"
+ pkg.Service.Method = "Method"
+ pkg.Service.Timeout = time.Second
+ data, err = pkg.Marshal()
+ assert.NoError(t, err)
+
+ pkgres = NewDubboPackage(data)
+ pkgres.SetSerializer(HessianSerializer{})
+ pkgres.Body = make([]interface{}, 7)
+ err = pkgres.Unmarshal()
+ reassembleBody := pkgres.GetBody().(map[string]interface{})
+ assert.NoError(t, err)
+ assert.Equal(t, PackageRequest, pkgres.Header.Type)
+ assert.Equal(t, constant.S_Hessian2, pkgres.Header.SerialID)
+ assert.Equal(t, int64(10086), pkgres.Header.ID)
+ assert.Equal(t, "2.0.2", reassembleBody["dubboVersion"].(string))
+ assert.Equal(t, "path", pkgres.Service.Path)
+ assert.Equal(t, "2.6", pkgres.Service.Version)
+ assert.Equal(t, "Method", pkgres.Service.Method)
+ assert.Equal(t, "Ljava/lang/String;", reassembleBody["argsTypes"].(string))
+ assert.Equal(t, []interface{}{"a"}, reassembleBody["args"])
+ assert.Equal(t, map[string]string{"dubbo": "2.0.2", "interface": "Service", "path": "path", "timeout": "1000", "version": "2.6"}, reassembleBody["attachments"].(map[string]string))
+}
+
+func TestDubboPackage_Protobuf_Serialization_Request(t *testing.T) {
+ pkg := NewDubboPackage(nil)
+ pkg.Body = []interface{}{"a"}
+ pkg.Header.Type = PackageHeartbeat
+ pkg.Header.SerialID = constant.S_Proto
+ pkg.Header.ID = 10086
+ pkg.SetSerializer(ProtoSerializer{})
+
+ // heartbeat
+ data, err := pkg.Marshal()
+ assert.NoError(t, err)
+
+ pkgres := NewDubboPackage(data)
+ pkgres.SetSerializer(HessianSerializer{})
+
+ pkgres.Body = []interface{}{}
+ err = pkgres.Unmarshal()
+ assert.NoError(t, err)
+ assert.Equal(t, PackageHeartbeat|PackageRequest|PackageRequest_TwoWay, pkgres.Header.Type)
+ assert.Equal(t, constant.S_Proto, pkgres.Header.SerialID)
+ assert.Equal(t, int64(10086), pkgres.Header.ID)
+ assert.Equal(t, 0, len(pkgres.Body.([]interface{})))
+
+ // request
+ pkg.Header.Type = PackageRequest
+ pkg.Service.Interface = "Service"
+ pkg.Service.Path = "path"
+ pkg.Service.Version = "2.6"
+ pkg.Service.Method = "Method"
+ pkg.Service.Timeout = time.Second
+ pkg.SetBody([]interface{}{&pb.StringValue{Value: "hello world"}})
+ data, err = pkg.Marshal()
+ assert.NoError(t, err)
+
+ pkgres = NewDubboPackage(data)
+ pkgres.SetSerializer(ProtoSerializer{})
+ err = pkgres.Unmarshal()
+ assert.NoError(t, err)
+ body, ok := pkgres.Body.(map[string]interface{})
+ assert.Equal(t, ok, true)
+ req, ok := body["args"].([]interface{})
+ assert.Equal(t, ok, true)
+ // protobuf rpc just has exact one parameter
+ assert.Equal(t, len(req), 1)
+ argsBytes, ok := req[0].([]byte)
+ assert.Equal(t, ok, true)
+ sv := pb.StringValue{}
+ buf := proto.NewBuffer(argsBytes)
+ err = buf.Unmarshal(&sv)
+ assert.NoError(t, err)
+ assert.Equal(t, sv.Value, "hello world")
+}
+
+func TestDubboCodec_Protobuf_Serialization_Response(t *testing.T) {
+ {
+ pkg := NewDubboPackage(nil)
+ pkg.Header.Type = PackageResponse
+ pkg.Header.SerialID = constant.S_Proto
+ pkg.Header.ID = 10086
+ pkg.SetSerializer(ProtoSerializer{})
+ pkg.SetBody(&pb.StringValue{Value: "hello world"})
+
+ // heartbeat
+ data, err := pkg.Marshal()
+ assert.NoError(t, err)
+
+ pkgres := NewDubboPackage(data)
+ pkgres.SetSerializer(ProtoSerializer{})
+
+ pkgres.SetBody(&pb.StringValue{})
+ err = pkgres.Unmarshal()
+
+ assert.NoError(t, err)
+ assert.Equal(t, pkgres.Header.Type, PackageResponse)
+ assert.Equal(t, constant.S_Proto, pkgres.Header.SerialID)
+ assert.Equal(t, int64(10086), pkgres.Header.ID)
+
+ res, ok := pkgres.Body.(*pb.StringValue)
+ assert.Equal(t, ok, true)
+ assert.Equal(t, res.Value, "hello world")
+ }
+
+ // with attachments
+ {
+ attas := make(map[string]string)
+ attas["k1"] = "test"
+ resp := NewResponsePayload(&pb.StringValue{Value: "attachments"}, nil, attas)
+ p := NewDubboPackage(nil)
+ p.Header.Type = PackageResponse
+ p.Header.SerialID = constant.S_Proto
+ p.SetSerializer(ProtoSerializer{})
+ p.SetBody(resp)
+ data, err := p.Marshal()
+ assert.NoError(t, err)
+
+ pkgres := NewDubboPackage(data)
+ pkgres.Header.Type = PackageResponse
+ pkgres.Header.SerialID = constant.S_Proto
+ pkgres.Header.ID = 10086
+ pkgres.SetSerializer(ProtoSerializer{})
+
+ resAttachment := make(map[string]string)
+ resBody := &pb.StringValue{}
+ pkgres.SetBody(NewResponsePayload(resBody, nil, resAttachment))
+
+ err = pkgres.Unmarshal()
+ assert.NoError(t, err)
+ assert.Equal(t, "attachments", resBody.Value)
+ assert.Equal(t, "test", resAttachment["k1"])
+ }
+
+}
diff --git a/protocol/dubbo/impl/const.go b/protocol/dubbo/impl/const.go
new file mode 100644
index 0000000..06e73fa
--- /dev/null
+++ b/protocol/dubbo/impl/const.go
@@ -0,0 +1,243 @@
+package impl
+
+import (
+ "reflect"
+ "regexp"
+
+ "github.com/pkg/errors"
+)
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+const (
+ DUBBO = "dubbo"
+)
+
+const (
+ mask = byte(127)
+ flag = byte(128)
+)
+
+const (
+ // Zero : byte zero
+ Zero = byte(0x00)
+)
+
+// constansts
+const (
+ TAG_READ = int32(-1)
+ ASCII_GAP = 32
+ CHUNK_SIZE = 4096
+ BC_BINARY = byte('B') // final chunk
+ BC_BINARY_CHUNK = byte('A') // non-final chunk
+
+ BC_BINARY_DIRECT = byte(0x20) // 1-byte length binary
+ BINARY_DIRECT_MAX = byte(0x0f)
+ BC_BINARY_SHORT = byte(0x34) // 2-byte length binary
+ BINARY_SHORT_MAX = 0x3ff // 0-1023 binary
+
+ BC_DATE = byte(0x4a) // 64-bit millisecond UTC date
+ BC_DATE_MINUTE = byte(0x4b) // 32-bit minute UTC date
+
+ BC_DOUBLE = byte('D') // IEEE 64-bit double
+
+ BC_DOUBLE_ZERO = byte(0x5b)
+ BC_DOUBLE_ONE = byte(0x5c)
+ BC_DOUBLE_BYTE = byte(0x5d)
+ BC_DOUBLE_SHORT = byte(0x5e)
+ BC_DOUBLE_MILL = byte(0x5f)
+
+ BC_FALSE = byte('F') // boolean false
+
+ BC_INT = byte('I') // 32-bit int
+
+ INT_DIRECT_MIN = -0x10
+ INT_DIRECT_MAX = byte(0x2f)
+ BC_INT_ZERO = byte(0x90)
+
+ INT_BYTE_MIN = -0x800
+ INT_BYTE_MAX = 0x7ff
+ BC_INT_BYTE_ZERO = byte(0xc8)
+
+ BC_END = byte('Z')
+
+ INT_SHORT_MIN = -0x40000
+ INT_SHORT_MAX = 0x3ffff
+ BC_INT_SHORT_ZERO = byte(0xd4)
+
+ BC_LIST_VARIABLE = byte(0x55)
+ BC_LIST_FIXED = byte('V')
+ BC_LIST_VARIABLE_UNTYPED = byte(0x57)
+ BC_LIST_FIXED_UNTYPED = byte(0x58)
+ _listFixedTypedLenTagMin = byte(0x70)
+ _listFixedTypedLenTagMax = byte(0x77)
+ _listFixedUntypedLenTagMin = byte(0x78)
+ _listFixedUntypedLenTagMax = byte(0x7f)
+
+ BC_LIST_DIRECT = byte(0x70)
+ BC_LIST_DIRECT_UNTYPED = byte(0x78)
+ LIST_DIRECT_MAX = byte(0x7)
+
+ BC_LONG = byte('L') // 64-bit signed integer
+ LONG_DIRECT_MIN = -0x08
+ LONG_DIRECT_MAX = byte(0x0f)
+ BC_LONG_ZERO = byte(0xe0)
+
+ LONG_BYTE_MIN = -0x800
+ LONG_BYTE_MAX = 0x7ff
+ BC_LONG_BYTE_ZERO = byte(0xf8)
+
+ LONG_SHORT_MIN = -0x40000
+ LONG_SHORT_MAX = 0x3ffff
+ BC_LONG_SHORT_ZERO = byte(0x3c)
+
+ BC_LONG_INT = byte(0x59)
+
+ BC_MAP = byte('M')
+ BC_MAP_UNTYPED = byte('H')
+
+ BC_NULL = byte('N') // x4e
+
+ BC_OBJECT = byte('O')
+ BC_OBJECT_DEF = byte('C')
+
+ BC_OBJECT_DIRECT = byte(0x60)
+ OBJECT_DIRECT_MAX = byte(0x0f)
+
+ BC_REF = byte(0x51)
+
+ BC_STRING = byte('S') // final string
+ BC_STRING_CHUNK = byte('R') // non-final string
+
+ BC_STRING_DIRECT = byte(0x00)
+ STRING_DIRECT_MAX = byte(0x1f)
+ BC_STRING_SHORT = byte(0x30)
+ STRING_SHORT_MAX = 0x3ff
+
+ BC_TRUE = byte('T')
+
+ P_PACKET_CHUNK = byte(0x4f)
+ P_PACKET = byte('P')
+
+ P_PACKET_DIRECT = byte(0x80)
+ PACKET_DIRECT_MAX = byte(0x7f)
+
+ P_PACKET_SHORT = byte(0x70)
+ PACKET_SHORT_MAX = 0xfff
+ ARRAY_STRING = "[string"
+ ARRAY_INT = "[int"
+ ARRAY_DOUBLE = "[double"
+ ARRAY_FLOAT = "[float"
+ ARRAY_BOOL = "[boolean"
+ ARRAY_LONG = "[long"
+
+ PATH_KEY = "path"
+ GROUP_KEY = "group"
+ INTERFACE_KEY = "interface"
+ VERSION_KEY = "version"
+ TIMEOUT_KEY = "timeout"
+
+ STRING_NIL = ""
+ STRING_TRUE = "true"
+ STRING_FALSE = "false"
+ STRING_ZERO = "0.0"
+ STRING_ONE = "1.0"
+)
+
+// ResponsePayload related consts
+const (
+ Response_OK byte = 20
+ Response_CLIENT_TIMEOUT byte = 30
+ Response_SERVER_TIMEOUT byte = 31
+ Response_BAD_REQUEST byte = 40
+ Response_BAD_RESPONSE byte = 50
+ Response_SERVICE_NOT_FOUND byte = 60
+ Response_SERVICE_ERROR byte = 70
+ Response_SERVER_ERROR byte = 80
+ Response_CLIENT_ERROR byte = 90
+
+ // According to "java dubbo" There are two cases of response:
+ // 1. with attachments
+ // 2. no attachments
+ RESPONSE_WITH_EXCEPTION int32 = 0
+ RESPONSE_VALUE int32 = 1
+ RESPONSE_NULL_VALUE int32 = 2
+ RESPONSE_WITH_EXCEPTION_WITH_ATTACHMENTS int32 = 3
+ RESPONSE_VALUE_WITH_ATTACHMENTS int32 = 4
+ RESPONSE_NULL_VALUE_WITH_ATTACHMENTS int32 = 5
+)
+
+/**
+ * the dubbo protocol header length is 16 Bytes.
+ * the first 2 Bytes is magic code '0xdabb'
+ * the next 1 Byte is message flags, in which its 16-20 bit is serial id, 21 for event, 22 for two way, 23 for request/response flag
+ * the next 1 Bytes is response state.
+ * the next 8 Bytes is package DI.
+ * the next 4 Bytes is package length.
+ **/
+const (
+ // header length.
+ HEADER_LENGTH = 16
+
+ // magic header
+ MAGIC = uint16(0xdabb)
+ MAGIC_HIGH = byte(0xda)
+ MAGIC_LOW = byte(0xbb)
+
+ // message flag.
+ FLAG_REQUEST = byte(0x80)
+ FLAG_TWOWAY = byte(0x40)
+ FLAG_EVENT = byte(0x20) // for heartbeat
+ SERIAL_MASK = 0x1f
+
+ DUBBO_VERSION = "2.5.4"
+ DUBBO_VERSION_KEY = "dubbo"
+ DEFAULT_DUBBO_PROTOCOL_VERSION = "2.0.2" // Dubbo RPC protocol version, for compatibility, it must not be between 2.0.10 ~ 2.6.2
+ LOWEST_VERSION_FOR_RESPONSE_ATTACHMENT = 2000200
+ DEFAULT_LEN = 8388608 // 8 * 1024 * 1024 default body max length
+)
+
+// regular
+const (
+ JAVA_IDENT_REGEX = "(?:[_$a-zA-Z][_$a-zA-Z0-9]*)"
+ CLASS_DESC = "(?:L" + JAVA_IDENT_REGEX + "(?:\\/" + JAVA_IDENT_REGEX + ")*;)"
+ ARRAY_DESC = "(?:\\[+(?:(?:[VZBCDFIJS])|" + CLASS_DESC + "))"
+ DESC_REGEX = "(?:(?:[VZBCDFIJS])|" + CLASS_DESC + "|" + ARRAY_DESC + ")"
+)
+
+// Dubbo request response related consts
+var (
+ DubboRequestHeaderBytesTwoWay = [HEADER_LENGTH]byte{MAGIC_HIGH, MAGIC_LOW, FLAG_REQUEST | FLAG_TWOWAY}
+ DubboRequestHeaderBytes = [HEADER_LENGTH]byte{MAGIC_HIGH, MAGIC_LOW, FLAG_REQUEST}
+ DubboResponseHeaderBytes = [HEADER_LENGTH]byte{MAGIC_HIGH, MAGIC_LOW, Zero, Response_OK}
+ DubboRequestHeartbeatHeader = [HEADER_LENGTH]byte{MAGIC_HIGH, MAGIC_LOW, FLAG_REQUEST | FLAG_TWOWAY | FLAG_EVENT}
+ DubboResponseHeartbeatHeader = [HEADER_LENGTH]byte{MAGIC_HIGH, MAGIC_LOW, FLAG_EVENT}
+)
+
+// Error part
+var (
+ ErrHeaderNotEnough = errors.New("header buffer too short")
+ ErrBodyNotEnough = errors.New("body buffer too short")
+ ErrJavaException = errors.New("got java exception")
+ ErrIllegalPackage = errors.New("illegal package!")
+)
+
+// DescRegex ...
+var DescRegex, _ = regexp.Compile(DESC_REGEX)
+
+var NilValue = reflect.Zero(reflect.TypeOf((*interface{})(nil)).Elem())
diff --git a/protocol/dubbo/impl/hessian.go b/protocol/dubbo/impl/hessian.go
new file mode 100644
index 0000000..513421b
--- /dev/null
+++ b/protocol/dubbo/impl/hessian.go
@@ -0,0 +1,508 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package impl
+
+import (
+ "math"
+ "reflect"
+ "strconv"
+ "strings"
+ "time"
+)
+
+import (
+ hessian "github.com/apache/dubbo-go-hessian2"
+ "github.com/apache/dubbo-go-hessian2/java_exception"
+ perrors "github.com/pkg/errors"
+)
+
+import (
+ "github.com/apache/dubbo-go/common"
+ "github.com/apache/dubbo-go/common/constant"
+ "github.com/apache/dubbo-go/common/logger"
+)
+
+type Object interface{}
+
+type HessianSerializer struct {
+}
+
+func (h HessianSerializer) Marshal(p DubboPackage) ([]byte, error) {
+ encoder := hessian.NewEncoder()
+ if p.IsRequest() {
+ return marshalRequest(encoder, p)
+ }
+ return marshalResponse(encoder, p)
+}
+
+func (h HessianSerializer) Unmarshal(input []byte, p *DubboPackage) error {
+ if p.IsHeartBeat() {
+ return nil
+ }
+ if p.IsRequest() {
+ return unmarshalRequestBody(input, p)
+ }
+ return unmarshalResponseBody(input, p)
+}
+
+func marshalResponse(encoder *hessian.Encoder, p DubboPackage) ([]byte, error) {
+ header := p.Header
+ response := EnsureResponsePayload(p.Body)
+ if header.ResponseStatus == Response_OK {
+ if p.IsHeartBeat() {
+ encoder.Encode(nil)
+ } else {
+ atta := isSupportResponseAttachment(response.Attachments[DUBBO_VERSION_KEY])
+
+ var resWithException, resValue, resNullValue int32
+ if atta {
+ resWithException = RESPONSE_WITH_EXCEPTION_WITH_ATTACHMENTS
+ resValue = RESPONSE_VALUE_WITH_ATTACHMENTS
+ resNullValue = RESPONSE_NULL_VALUE_WITH_ATTACHMENTS
+ } else {
+ resWithException = RESPONSE_WITH_EXCEPTION
+ resValue = RESPONSE_VALUE
+ resNullValue = RESPONSE_NULL_VALUE
+ }
+
+ if response.Exception != nil { // throw error
+ encoder.Encode(resWithException)
+ if t, ok := response.Exception.(java_exception.Throwabler); ok {
+ encoder.Encode(t)
+ } else {
+ encoder.Encode(java_exception.NewThrowable(response.Exception.Error()))
+ }
+ } else {
+ if response.RspObj == nil {
+ encoder.Encode(resNullValue)
+ } else {
+ encoder.Encode(resValue)
+ encoder.Encode(response.RspObj) // result
+ }
+ }
+
+ if atta {
+ encoder.Encode(response.Attachments) // attachments
+ }
+ }
+ } else {
+ if response.Exception != nil { // throw error
+ encoder.Encode(response.Exception.Error())
+ } else {
+ encoder.Encode(response.RspObj)
+ }
+ }
+ bs := encoder.Buffer()
+ // encNull
+ bs = append(bs, byte('N'))
+ return bs, nil
+}
+
+func marshalRequest(encoder *hessian.Encoder, p DubboPackage) ([]byte, error) {
+ service := p.Service
+ request := EnsureRequestPayload(p.Body)
+ encoder.Encode(DEFAULT_DUBBO_PROTOCOL_VERSION)
+ encoder.Encode(service.Path)
+ encoder.Encode(service.Version)
+ encoder.Encode(service.Method)
+
+ args, ok := request.Params.([]interface{})
+
+ if !ok {
+ logger.Infof("request args are: %+v", request.Params)
+ return nil, perrors.Errorf("@params is not of type: []interface{}")
+ }
+ types, err := getArgsTypeList(args)
+ if err != nil {
+ return nil, perrors.Wrapf(err, " PackRequest(args:%+v)", args)
+ }
+ encoder.Encode(types)
+ for _, v := range args {
+ encoder.Encode(v)
+ }
+
+ request.Attachments[PATH_KEY] = service.Path
+ request.Attachments[VERSION_KEY] = service.Version
+ if len(service.Group) > 0 {
+ request.Attachments[GROUP_KEY] = service.Group
+ }
+ if len(service.Interface) > 0 {
+ request.Attachments[INTERFACE_KEY] = service.Interface
+ }
+ if service.Timeout != 0 {
+ request.Attachments[TIMEOUT_KEY] = strconv.Itoa(int(service.Timeout / time.Millisecond))
+ }
+
+ encoder.Encode(request.Attachments)
+ return encoder.Buffer(), nil
+
+}
+
+var versionInt = make(map[string]int)
+
+// https://github.com/apache/dubbo/blob/dubbo-2.7.1/dubbo-common/src/main/java/org/apache/dubbo/common/Version.java#L96
+// isSupportResponseAttachment is for compatibility among some dubbo version
+func isSupportResponseAttachment(version string) bool {
+ if version == "" {
+ return false
+ }
+
+ v, ok := versionInt[version]
+ if !ok {
+ v = version2Int(version)
+ if v == -1 {
+ return false
+ }
+ }
+
+ if v >= 2001000 && v <= 2060200 { // 2.0.10 ~ 2.6.2
+ return false
+ }
+ return v >= LOWEST_VERSION_FOR_RESPONSE_ATTACHMENT
+}
+
+func version2Int(version string) int {
+ var v = 0
+ varr := strings.Split(version, ".")
+ length := len(varr)
+ for key, value := range varr {
+ v0, err := strconv.Atoi(value)
+ if err != nil {
+ return -1
+ }
+ v += v0 * int(math.Pow10((length-key-1)*2))
+ }
+ if length == 3 {
+ return v * 100
+ }
+ return v
+}
+
+func unmarshalRequestBody(body []byte, p *DubboPackage) error {
+ if p.Body == nil {
+ p.SetBody(make([]interface{}, 7))
+ }
+ decoder := hessian.NewDecoder(body)
+ var (
+ err error
+ dubboVersion, target, serviceVersion, method, argsTypes interface{}
+ args []interface{}
+ )
+ req, ok := p.Body.([]interface{})
+ if !ok {
+ return perrors.Errorf("@reqObj is not of type: []interface{}")
+ }
+ dubboVersion, err = decoder.Decode()
+ if err != nil {
+ return perrors.WithStack(err)
+ }
+ req[0] = dubboVersion
+
+ target, err = decoder.Decode()
+ if err != nil {
+ return perrors.WithStack(err)
+ }
+ req[1] = target
+
+ serviceVersion, err = decoder.Decode()
+ if err != nil {
+ return perrors.WithStack(err)
+ }
+ req[2] = serviceVersion
+
+ method, err = decoder.Decode()
+ if err != nil {
+ return perrors.WithStack(err)
+ }
+ req[3] = method
+
+ argsTypes, err = decoder.Decode()
+ if err != nil {
+ return perrors.WithStack(err)
+ }
+ req[4] = argsTypes
+
+ ats := hessian.DescRegex.FindAllString(argsTypes.(string), -1)
+ var arg interface{}
+ for i := 0; i < len(ats); i++ {
+ arg, err = decoder.Decode()
+ if err != nil {
+ return perrors.WithStack(err)
+ }
+ args = append(args, arg)
+ }
+ req[5] = args
+
+ attachments, err := decoder.Decode()
+ if err != nil {
+ return perrors.WithStack(err)
+ }
+
+ if v, ok := attachments.(map[interface{}]interface{}); ok {
+ v[DUBBO_VERSION_KEY] = dubboVersion
+ req[6] = hessian.ToMapStringString(v)
+ buildServerSidePackageBody(p)
+ return nil
+ }
+ return perrors.Errorf("get wrong attachments: %+v", attachments)
+}
+
+func unmarshalResponseBody(body []byte, p *DubboPackage) error {
+ decoder := hessian.NewDecoder(body)
+ rspType, err := decoder.Decode()
+ if p.Body == nil {
+ p.SetBody(&ResponsePayload{})
+ }
+ if err != nil {
+ return perrors.WithStack(err)
+ }
+ response := EnsureResponsePayload(p.Body)
+
+ switch rspType {
+ case RESPONSE_WITH_EXCEPTION, RESPONSE_WITH_EXCEPTION_WITH_ATTACHMENTS:
+ expt, err := decoder.Decode()
+ if err != nil {
+ return perrors.WithStack(err)
+ }
+ if rspType == RESPONSE_WITH_EXCEPTION_WITH_ATTACHMENTS {
+ attachments, err := decoder.Decode()
+ if err != nil {
+ return perrors.WithStack(err)
+ }
+ if v, ok := attachments.(map[interface{}]interface{}); ok {
+ atta := hessian.ToMapStringString(v)
+ response.Attachments = atta
+ } else {
+ return perrors.Errorf("get wrong attachments: %+v", attachments)
+ }
+ }
+
+ if e, ok := expt.(error); ok {
+ response.Exception = e
+ } else {
+ response.Exception = perrors.Errorf("got exception: %+v", expt)
+ }
+ return nil
+
+ case RESPONSE_VALUE, RESPONSE_VALUE_WITH_ATTACHMENTS:
+ rsp, err := decoder.Decode()
+ if err != nil {
+ return perrors.WithStack(err)
+ }
+ if rspType == RESPONSE_VALUE_WITH_ATTACHMENTS {
+ attachments, err := decoder.Decode()
+ if err != nil {
+ return perrors.WithStack(err)
+ }
+ if v, ok := attachments.(map[interface{}]interface{}); ok {
+ atta := hessian.ToMapStringString(v)
+ response.Attachments = atta
+ } else {
+ return perrors.Errorf("get wrong attachments: %+v", attachments)
+ }
+ }
+
+ return perrors.WithStack(hessian.ReflectResponse(rsp, response.RspObj))
+
+ case RESPONSE_NULL_VALUE, RESPONSE_NULL_VALUE_WITH_ATTACHMENTS:
+ if rspType == RESPONSE_NULL_VALUE_WITH_ATTACHMENTS {
+ attachments, err := decoder.Decode()
+ if err != nil {
+ return perrors.WithStack(err)
+ }
+ if v, ok := attachments.(map[interface{}]interface{}); ok {
+ atta := hessian.ToMapStringString(v)
+ response.Attachments = atta
+ } else {
+ return perrors.Errorf("get wrong attachments: %+v", attachments)
+ }
+ }
+ return nil
+ }
+ return nil
+}
+
+func buildServerSidePackageBody(pkg *DubboPackage) {
+ req := pkg.GetBody().([]interface{}) // length of body should be 7
+ if len(req) > 0 {
+ var dubboVersion, argsTypes string
+ var args []interface{}
+ var attachments map[string]string
+ svc := Service{}
+ if req[0] != nil {
+ dubboVersion = req[0].(string)
+ }
+ if req[1] != nil {
+ svc.Path = req[1].(string)
+ }
+ if req[2] != nil {
+ svc.Version = req[2].(string)
+ }
+ if req[3] != nil {
+ svc.Method = req[3].(string)
+ }
+ if req[4] != nil {
+ argsTypes = req[4].(string)
+ }
+ if req[5] != nil {
+ args = req[5].([]interface{})
+ }
+ if req[6] != nil {
+ attachments = req[6].(map[string]string)
+ }
+ if svc.Path == "" && len(attachments[constant.PATH_KEY]) > 0 {
+ svc.Path = attachments[constant.PATH_KEY]
+ }
+ if _, ok := attachments[constant.INTERFACE_KEY]; ok {
+ svc.Interface = attachments[constant.INTERFACE_KEY]
+ } else {
+ svc.Interface = svc.Path
+ }
+ if len(attachments[constant.GROUP_KEY]) > 0 {
+ svc.Group = attachments[constant.GROUP_KEY]
+ }
+ pkg.SetService(svc)
+ pkg.SetBody(map[string]interface{}{
+ "dubboVersion": dubboVersion,
+ "argsTypes": argsTypes,
+ "args": args,
+ "service": common.ServiceMap.GetService(DUBBO, svc.Path), // path as a key
+ "attachments": attachments,
+ })
+ }
+}
+
+func getArgsTypeList(args []interface{}) (string, error) {
+ var (
+ typ string
+ types string
+ )
+
+ for i := range args {
+ typ = getArgType(args[i])
+ if typ == "" {
+ return types, perrors.Errorf("cat not get arg %#v type", args[i])
+ }
+ if !strings.Contains(typ, ".") {
+ types += typ
+ } else if strings.Index(typ, "[") == 0 {
+ types += strings.Replace(typ, ".", "/", -1)
+ } else {
+ // java.util.List -> Ljava/util/List;
+ types += "L" + strings.Replace(typ, ".", "/", -1) + ";"
+ }
+ }
+
+ return types, nil
+}
+
+func getArgType(v interface{}) string {
+ if v == nil {
+ return "V"
+ }
+
+ switch v.(type) {
+ // Serialized tags for base types
+ case nil:
+ return "V"
+ case bool:
+ return "Z"
+ case []bool:
+ return "[Z"
+ case byte:
+ return "B"
+ case []byte:
+ return "[B"
+ case int8:
+ return "B"
+ case []int8:
+ return "[B"
+ case int16:
+ return "S"
+ case []int16:
+ return "[S"
+ case uint16: // Equivalent to Char of Java
+ return "C"
+ case []uint16:
+ return "[C"
+ // case rune:
+ // return "C"
+ case int:
+ return "J"
+ case []int:
+ return "[J"
+ case int32:
+ return "I"
+ case []int32:
+ return "[I"
+ case int64:
+ return "J"
+ case []int64:
+ return "[J"
+ case time.Time:
+ return "java.util.Date"
+ case []time.Time:
+ return "[Ljava.util.Date"
+ case float32:
+ return "F"
+ case []float32:
+ return "[F"
+ case float64:
+ return "D"
+ case []float64:
+ return "[D"
+ case string:
+ return "java.lang.String"
+ case []string:
+ return "[Ljava.lang.String;"
+ case []Object:
+ return "[Ljava.lang.Object;"
+ case map[interface{}]interface{}:
+ // return "java.util.HashMap"
+ return "java.util.Map"
+ case hessian.POJOEnum:
+ return v.(hessian.POJOEnum).JavaClassName()
+ // Serialized tags for complex types
+ default:
+ t := reflect.TypeOf(v)
+ if reflect.Ptr == t.Kind() {
+ t = reflect.TypeOf(reflect.ValueOf(v).Elem())
+ }
+ switch t.Kind() {
+ case reflect.Struct:
+ return "java.lang.Object"
+ case reflect.Slice, reflect.Array:
+ if t.Elem().Kind() == reflect.Struct {
+ return "[Ljava.lang.Object;"
+ }
+ // return "java.util.ArrayList"
+ return "java.util.List"
+ case reflect.Map: // Enter here, map may be map[string]int
+ return "java.util.Map"
+ default:
+ return ""
+ }
+ }
+
+ // unreachable
+ // return "java.lang.RuntimeException"
+}
+
+func init() {
+ SetSerializer("hessian2", HessianSerializer{})
+}
diff --git a/protocol/dubbo/impl/package.go b/protocol/dubbo/impl/package.go
new file mode 100644
index 0000000..d40c4af
--- /dev/null
+++ b/protocol/dubbo/impl/package.go
@@ -0,0 +1,171 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package impl
+
+import (
+ "bufio"
+ "bytes"
+ "fmt"
+ "time"
+)
+
+import (
+ "github.com/pkg/errors"
+)
+
+type PackageType int
+
+// enum part
+const (
+ PackageError = PackageType(0x01)
+ PackageRequest = PackageType(0x02)
+ PackageResponse = PackageType(0x04)
+ PackageHeartbeat = PackageType(0x08)
+ PackageRequest_TwoWay = PackageType(0x10)
+ PackageResponse_Exception = PackageType(0x20)
+ PackageType_BitSize = 0x2f
+)
+
+type DubboHeader struct {
+ SerialID byte
+ Type PackageType
+ ID int64
+ BodyLen int
+ ResponseStatus byte
+}
+
+// Service defines service instance
+type Service struct {
+ Path string
+ Interface string
+ Group string
+ Version string
+ Method string
+ Timeout time.Duration // request timeout
+}
+
+type DubboPackage struct {
+ Header DubboHeader
+ Service Service
+ Body interface{}
+ Err error
+ Codec *DubboCodec
+}
+
+func (p DubboPackage) String() string {
+ return fmt.Sprintf("HessianPackage: Header-%v, Path-%v, Body-%v", p.Header, p.Service, p.Body)
+}
+
+func (p *DubboPackage) ReadHeader() error {
+ return p.Codec.ReadHeader(&p.Header)
+}
+
+func (p *DubboPackage) Marshal() (*bytes.Buffer, error) {
+ if p.Codec == nil {
+ return nil, errors.New("Codec is nil")
+ }
+ pkg, err := p.Codec.Encode(*p)
+ if err != nil {
+ return nil, errors.WithStack(err)
+ }
+ return bytes.NewBuffer(pkg), nil
+}
+
+func (p *DubboPackage) Unmarshal() error {
+ if p.Codec == nil {
+ return errors.New("Codec is nil")
+ }
+ return p.Codec.Decode(p)
+}
+
+func (p DubboPackage) IsHeartBeat() bool {
+ return p.Header.Type&PackageHeartbeat != 0
+}
+
+func (p DubboPackage) IsRequest() bool {
+ return p.Header.Type&(PackageRequest_TwoWay|PackageRequest) != 0
+}
+
+func (p DubboPackage) IsResponse() bool {
+ return p.Header.Type == PackageResponse
+}
+
+func (p DubboPackage) IsResponseWithException() bool {
+ flag := PackageResponse | PackageResponse_Exception
+ return p.Header.Type&flag == flag
+}
+
+func (p DubboPackage) GetBodyLen() int {
+ return p.Header.BodyLen
+}
+
+func (p DubboPackage) GetLen() int {
+ return HEADER_LENGTH + p.Header.BodyLen
+}
+
+func (p DubboPackage) GetBody() interface{} {
+ return p.Body
+}
+
+func (p *DubboPackage) SetBody(body interface{}) {
+ p.Body = body
+}
+
+func (p *DubboPackage) SetHeader(header DubboHeader) {
+ p.Header = header
+}
+
+func (p *DubboPackage) SetService(svc Service) {
+ p.Service = svc
+}
+
+func (p *DubboPackage) SetID(id int64) {
+ p.Header.ID = id
+}
+
+func (p DubboPackage) GetHeader() DubboHeader {
+ return p.Header
+}
+
+func (p DubboPackage) GetService() Service {
+ return p.Service
+}
+
+func (p *DubboPackage) SetResponseStatus(status byte) {
+ p.Header.ResponseStatus = status
+}
+
+func (p *DubboPackage) SetSerializer(serializer Serializer) {
+ p.Codec.SetSerializer(serializer)
+}
+
+func NewDubboPackage(data *bytes.Buffer) *DubboPackage {
+ var codec *DubboCodec
+ if data == nil {
+ codec = NewDubboCodec(nil)
+ } else {
+ codec = NewDubboCodec(bufio.NewReaderSize(data, len(data.Bytes())))
+ }
+ return &DubboPackage{
+ Header: DubboHeader{},
+ Service: Service{},
+ Body: nil,
+ Err: nil,
+ Codec: codec,
+ }
+}
diff --git a/protocol/dubbo/impl/proto.go b/protocol/dubbo/impl/proto.go
new file mode 100644
index 0000000..ea1c55d
--- /dev/null
+++ b/protocol/dubbo/impl/proto.go
@@ -0,0 +1,450 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package impl
+
+import (
+ "bytes"
+ "encoding/binary"
+ "fmt"
+ "io"
+ "reflect"
+ "strconv"
+ "strings"
+ "sync"
+ "time"
+)
+
+import (
+ "github.com/golang/protobuf/proto"
+ "github.com/matttproud/golang_protobuf_extensions/pbutil"
+ "github.com/pkg/errors"
+)
+
+import (
+ "github.com/apache/dubbo-go/common"
+ "github.com/apache/dubbo-go/common/constant"
+ pb "github.com/apache/dubbo-go/protocol/dubbo/impl/proto"
+)
+
+type ProtoSerializer struct{}
+
+func (p ProtoSerializer) Marshal(pkg DubboPackage) ([]byte, error) {
+ if pkg.IsHeartBeat() {
+ return []byte{byte('N')}, nil
+ }
+ if pkg.Body == nil {
+ return nil, errors.New("package body should not be nil")
+ }
+ if pkg.IsRequest() {
+ return marshalRequestProto(pkg)
+ }
+ return marshalResponseProto(pkg)
+}
+
+func (p ProtoSerializer) Unmarshal(data []byte, pkg *DubboPackage) error {
+ if pkg.IsRequest() {
+ return unmarshalRequestProto(data, pkg)
+ }
+ return unmarshalResponseProto(data, pkg)
+}
+
+func unmarshalResponseProto(data []byte, pkg *DubboPackage) error {
+ if pkg.Body == nil {
+ pkg.SetBody(NewResponsePayload(nil, nil, nil))
+ }
+ response := EnsureResponsePayload(pkg.Body)
+ buf := bytes.NewBuffer(data)
+
+ var responseType int32
+ if err := readByte(buf, &responseType); err != nil {
+ return err
+ }
+
+ hasAttachments := false
+ hasException := false
+ switch responseType {
+ case RESPONSE_VALUE_WITH_ATTACHMENTS:
+ hasAttachments = true
+ case RESPONSE_WITH_EXCEPTION:
+ hasException = true
+ case RESPONSE_WITH_EXCEPTION_WITH_ATTACHMENTS:
+ hasAttachments = true
+ hasException = true
+ }
+ if hasException {
+ throwable := pb.ThrowableProto{}
+ if err := readObject(buf, &throwable); err != nil {
+ return err
+ }
+ // generate error only with error message
+ response.Exception = errors.New(throwable.OriginalMessage)
+ } else {
+ // read response body
+ protoMsg, ok := response.RspObj.(proto.Message)
+ if !ok {
+ return errors.New("response rspobj not protobuf message")
+ }
+ if err := readObject(buf, protoMsg); err != nil {
+ return err
+ }
+ }
+
+ if hasAttachments {
+ atta := pb.Map{}
+ if err := readObject(buf, &atta); err != nil {
+ return err
+ }
+ if response.Attachments == nil {
+ response.Attachments = atta.Attachments
+ } else {
+ for k, v := range atta.Attachments {
+ response.Attachments[k] = v
+ }
+ }
+
+ }
+ return nil
+}
+
+func unmarshalRequestProto(data []byte, pkg *DubboPackage) error {
+ var dubboVersion string
+ var svcPath string
+ var svcVersion string
+ var svcMethod string
+ buf := bytes.NewBuffer(data)
+ if err := readUTF(buf, &dubboVersion); err != nil {
+ return err
+ }
+ if err := readUTF(buf, &svcPath); err != nil {
+ return err
+ }
+ if err := readUTF(buf, &svcVersion); err != nil {
+ return err
+ }
+ if err := readUTF(buf, &svcMethod); err != nil {
+ return err
+ }
+ // NOTE: protobuf rpc just have exact one parameter, while golang doesn't need this field
+ var argsType string
+ if err := readUTF(buf, &argsType); err != nil {
+ return err
+ }
+ // get raw body bytes for proxy methods to unmarshal
+ var protoMsgLength int
+ if err := readDelimitedLength(buf, &protoMsgLength); err != nil {
+ return err
+ }
+ argBytes := make([]byte, protoMsgLength)
+ if n, err := buf.Read(argBytes); err != nil {
+ if n != protoMsgLength {
+ return errors.New("illegal msg length")
+ }
+ return err
+ }
+ arg := getRegisterMessage(argsType)
+ err := proto.Unmarshal(argBytes, arg.Interface().(JavaProto))
+ if err != nil {
+ panic(err)
+ }
+
+ m := &pb.Map{}
+ if err := readObject(buf, m); err != nil {
+ return err
+ }
+ svc := Service{}
+ svc.Version = svcVersion
+ svc.Method = svcMethod
+ // just as hessian
+ svc.Path = svcPath
+ if svc.Path == "" && len(m.Attachments[constant.PATH_KEY]) > 0 {
+ svc.Path = m.Attachments[constant.PATH_KEY]
+ }
+
+ if _, ok := m.Attachments[constant.INTERFACE_KEY]; ok {
+ svc.Interface = m.Attachments[constant.INTERFACE_KEY]
+ } else {
+ svc.Interface = svc.Path
+ }
+ pkg.SetService(svc)
+ pkg.SetBody(map[string]interface{}{
+ "dubboVersion": dubboVersion,
+ "args": []interface{}{arg.Interface()},
+ "service": common.ServiceMap.GetService(DUBBO, svc.Path), // path as a key
+ "attachments": m.Attachments,
+ })
+
+ return nil
+}
+
+func marshalRequestProto(pkg DubboPackage) ([]byte, error) {
+ request := EnsureRequestPayload(pkg.Body)
+ args, ok := request.Params.([]interface{})
+ buf := bytes.NewBuffer(make([]byte, 0))
+ if !ok {
+ return nil, errors.New("proto buffer args should be marshaled in []byte")
+ }
+ // NOTE: protobuf rpc just has exact one parameter
+ if len(args) != 1 {
+ return nil, errors.New("illegal protobuf service, len(arg) should equal 1")
+ }
+ // dubbo version
+ if err := writeUTF(buf, DUBBO_VERSION); err != nil {
+ return nil, err
+ }
+ // service path
+ if err := writeUTF(buf, pkg.Service.Path); err != nil {
+ return nil, err
+ }
+ // service version
+ if err := writeUTF(buf, pkg.Service.Version); err != nil {
+ return nil, err
+ }
+ // service method
+ if err := writeUTF(buf, pkg.Service.Method); err != nil {
+ return nil, err
+ }
+ // parameter types desc
+ v := reflect.ValueOf(args[0])
+ mv := v.MethodByName("JavaClassName")
+ if mv.IsValid() {
+ javaCls := mv.Call([]reflect.Value{})
+ if len(javaCls) != 1 {
+ return nil, errors.New("JavaStringName method should return exact 1 result")
+ }
+ javaClsStr, ok := javaCls[0].Interface().(string)
+ if !ok {
+ return nil, errors.New("JavaClassName method should return string")
+ }
+ if err := writeUTF(buf, getJavaArgType(javaClsStr)); err != nil {
+ return nil, err
+ }
+ } else {
+ // defensive code
+ if err := writeUTF(buf, ""); err != nil {
+ return nil, err
+ }
+ }
+ // consumer args
+ protoMsg := args[0].(proto.Message)
+ if err := writeObject(buf, protoMsg); err != nil {
+ return nil, err
+ }
+ // attachments
+ atta := make(map[string]string)
+ atta[PATH_KEY] = pkg.Service.Path
+ atta[VERSION_KEY] = pkg.Service.Version
+ if len(pkg.Service.Group) > 0 {
+ atta[GROUP_KEY] = pkg.Service.Group
+ }
+ if len(pkg.Service.Interface) > 0 {
+ atta[INTERFACE_KEY] = pkg.Service.Interface
+ }
+ if pkg.Service.Timeout != 0 {
+ atta[TIMEOUT_KEY] = strconv.Itoa(int(pkg.Service.Timeout / time.Millisecond))
+ }
+ m := pb.Map{Attachments: atta}
+ if err := writeObject(buf, &m); err != nil {
+ return nil, err
+ }
+ return buf.Bytes(), nil
+}
+
+func marshalResponseProto(pkg DubboPackage) ([]byte, error) {
+ response := EnsureResponsePayload(pkg.Body)
+ buf := bytes.NewBuffer(make([]byte, 0))
+ responseType := RESPONSE_VALUE
+ hasAttachments := false
+ if response.Attachments != nil {
+ responseType = RESPONSE_VALUE_WITH_ATTACHMENTS
+ hasAttachments = true
+ } else {
+ responseType = RESPONSE_VALUE
+ }
+ if response.Exception != nil {
+ if hasAttachments {
+ responseType = RESPONSE_WITH_EXCEPTION_WITH_ATTACHMENTS
+ } else {
+ responseType = RESPONSE_WITH_EXCEPTION
+ }
+ }
+ // write response type
+ if err := writeByte(buf, responseType); err != nil {
+ return nil, err
+ }
+ if response.Exception != nil {
+ // deal with exception
+ throwable := pb.ThrowableProto{OriginalMessage: response.Exception.Error()}
+ if err := writeObject(buf, &throwable); err != nil {
+ return nil, err
+ }
+ } else {
+ res, ok := response.RspObj.(proto.Message)
+ if !ok {
+ return nil, errors.New("proto buffer params should be marshaled in proto.Message")
+ }
+ // response body
+ if err := writeObject(buf, res); err != nil {
+ return nil, err
+ }
+ }
+
+ if hasAttachments {
+ attachments := pb.Map{Attachments: response.Attachments}
+ if err := writeObject(buf, &attachments); err != nil {
+ return nil, err
+ }
+ }
+ return buf.Bytes(), nil
+}
+
+func init() {
+ SetSerializer("protobuf", ProtoSerializer{})
+}
+
+func getJavaArgType(javaClsName string) string {
+ return fmt.Sprintf("L%s;", strings.ReplaceAll(javaClsName, ".", "/"))
+}
+
+func writeUTF(writer io.Writer, value string) error {
+ _, err := pbutil.WriteDelimited(writer, &pb.StringValue{Value: value})
+ return err
+}
+
+func writeObject(writer io.Writer, value proto.Message) error {
+ _, err := pbutil.WriteDelimited(writer, value)
+ return err
+}
+
+func writeByte(writer io.Writer, v int32) error {
+ i32v := &pb.Int32Value{Value: v}
+ _, err := pbutil.WriteDelimited(writer, i32v)
+ return err
+}
+
+func readUTF(reader io.Reader, value *string) error {
+ sv := &pb.StringValue{}
+ _, err := pbutil.ReadDelimited(reader, sv)
+ if err != nil {
+ return err
+ }
+ *value = sv.Value
+ return nil
+}
+
+func readObject(reader io.Reader, value proto.Message) error {
+ _, err := pbutil.ReadDelimited(reader, value)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+// just as java protobuf serialize
+func readByte(reader io.Reader, value *int32) error {
+ i32v := &pb.Int32Value{}
+ _, err := pbutil.ReadDelimited(reader, i32v)
+ if err != nil {
+ return err
+ }
+ *value = i32v.Value
+ return nil
+}
+
+//
+func readDelimitedLength(reader io.Reader, length *int) error {
+ var headerBuf [binary.MaxVarintLen32]byte
+ var bytesRead, varIntBytes int
+ var messageLength uint64
+ for varIntBytes == 0 { // i.e. no varint has been decoded yet.
+ if bytesRead >= len(headerBuf) {
+ return errors.New("invalid varint32 encountered")
+ }
+ // We have to read byte by byte here to avoid reading more bytes
+ // than required. Each read byte is appended to what we have
+ // read before.
+ newBytesRead, err := reader.Read(headerBuf[bytesRead : bytesRead+1])
+ if newBytesRead == 0 {
+ if err != nil {
+ return err
+ }
+ // A Reader should not return (0, nil), but if it does,
+ // it should be treated as no-op (according to the
+ // Reader contract). So let's go on...
+ continue
+ }
+ bytesRead += newBytesRead
+ // Now present everything read so far to the varint decoder and
+ // see if a varint can be decoded already.
+ messageLength, varIntBytes = proto.DecodeVarint(headerBuf[:bytesRead])
+ }
+ *length = int(messageLength)
+ return nil
+}
+
+type JavaProto interface {
+ JavaClassName() string
+ proto.Message
+}
+
+type Register struct {
+ sync.RWMutex
+ registry map[string]reflect.Type
+}
+
+var (
+ register = Register{
+ registry: make(map[string]reflect.Type),
+ }
+)
+
+func RegisterMessage(msg JavaProto) {
+ register.Lock()
+ defer register.Unlock()
+
+ name := msg.JavaClassName()
+ name = getJavaArgType(name)
+
+ if e, ok := register.registry[name]; ok {
+ panic(fmt.Sprintf("msg: %v has been registered. existed: %v", msg.JavaClassName(), e))
+ }
+
+ register.registry[name] = typeOfMessage(msg)
+}
+
+func getRegisterMessage(sig string) reflect.Value {
+ register.Lock()
+ defer register.Unlock()
+
+ t, ok := register.registry[sig]
+ if !ok {
+ panic(fmt.Sprintf("registry dose not have for svc: %v", sig))
+ }
+ return reflect.New(t)
+}
+
+func typeOfMessage(o proto.Message) reflect.Type {
+ v := reflect.ValueOf(o)
+ switch v.Kind() {
+ case reflect.Struct:
+ return v.Type()
+ case reflect.Ptr:
+ return v.Elem().Type()
+ }
+
+ return reflect.TypeOf(o)
+}
diff --git a/protocol/dubbo/impl/proto/payload.pb.go b/protocol/dubbo/impl/proto/payload.pb.go
new file mode 100644
index 0000000..337027e
--- /dev/null
+++ b/protocol/dubbo/impl/proto/payload.pb.go
@@ -0,0 +1,345 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: proto/payload.proto
+
+package payload
+
+import (
+ fmt "fmt"
+ math "math"
+
+ proto "github.com/golang/protobuf/proto"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
+
+// equivalent java StringValue
+type StringValue struct {
+ Value string `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *StringValue) Reset() { *m = StringValue{} }
+func (m *StringValue) String() string { return proto.CompactTextString(m) }
+func (*StringValue) ProtoMessage() {}
+func (*StringValue) Descriptor() ([]byte, []int) {
+ return fileDescriptor_434bbf44284586dc, []int{0}
+}
+
+func (m *StringValue) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_StringValue.Unmarshal(m, b)
+}
+func (m *StringValue) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_StringValue.Marshal(b, m, deterministic)
+}
+func (m *StringValue) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_StringValue.Merge(m, src)
+}
+func (m *StringValue) XXX_Size() int {
+ return xxx_messageInfo_StringValue.Size(m)
+}
+func (m *StringValue) XXX_DiscardUnknown() {
+ xxx_messageInfo_StringValue.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_StringValue proto.InternalMessageInfo
+
+func (m *StringValue) GetValue() string {
+ if m != nil {
+ return m.Value
+ }
+ return ""
+}
+
+// equivalent java Int32Value
+type Int32Value struct {
+ Value int32 `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *Int32Value) Reset() { *m = Int32Value{} }
+func (m *Int32Value) String() string { return proto.CompactTextString(m) }
+func (*Int32Value) ProtoMessage() {}
+func (*Int32Value) Descriptor() ([]byte, []int) {
+ return fileDescriptor_434bbf44284586dc, []int{1}
+}
+
+func (m *Int32Value) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_Int32Value.Unmarshal(m, b)
+}
+func (m *Int32Value) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_Int32Value.Marshal(b, m, deterministic)
+}
+func (m *Int32Value) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_Int32Value.Merge(m, src)
+}
+func (m *Int32Value) XXX_Size() int {
+ return xxx_messageInfo_Int32Value.Size(m)
+}
+func (m *Int32Value) XXX_DiscardUnknown() {
+ xxx_messageInfo_Int32Value.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Int32Value proto.InternalMessageInfo
+
+func (m *Int32Value) GetValue() int32 {
+ if m != nil {
+ return m.Value
+ }
+ return 0
+}
+
+// equivalent java MapValue
+type Map struct {
+ Attachments map[string]string `protobuf:"bytes,1,rep,name=attachments,proto3" json:"attachments,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *Map) Reset() { *m = Map{} }
+func (m *Map) String() string { return proto.CompactTextString(m) }
+func (*Map) ProtoMessage() {}
+func (*Map) Descriptor() ([]byte, []int) {
+ return fileDescriptor_434bbf44284586dc, []int{2}
+}
+
+func (m *Map) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_Map.Unmarshal(m, b)
+}
+func (m *Map) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_Map.Marshal(b, m, deterministic)
+}
+func (m *Map) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_Map.Merge(m, src)
+}
+func (m *Map) XXX_Size() int {
+ return xxx_messageInfo_Map.Size(m)
+}
+func (m *Map) XXX_DiscardUnknown() {
+ xxx_messageInfo_Map.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Map proto.InternalMessageInfo
+
+func (m *Map) GetAttachments() map[string]string {
+ if m != nil {
+ return m.Attachments
+ }
+ return nil
+}
+
+// copied from dubbo GenericProtobufObjectOutput.java
+// Messages used for transporting debug information between server and client.
+// An element in a stack trace, based on the Java type of the same name.
+//
+// See: https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/StackTraceElement.html
+type StackTraceElementProto struct {
+ // The fully qualified name of the class containing the execution point
+ // represented by the stack trace element.
+ ClassName string `protobuf:"bytes,1,opt,name=class_name,json=className,proto3" json:"class_name,omitempty"`
+ // The name of the method containing the execution point represented by the
+ // stack trace element
+ MethodName string `protobuf:"bytes,2,opt,name=method_name,json=methodName,proto3" json:"method_name,omitempty"`
+ // The name of the file containing the execution point represented by the
+ // stack trace element, or null if this information is unavailable.
+ FileName string `protobuf:"bytes,3,opt,name=file_name,json=fileName,proto3" json:"file_name,omitempty"`
+ // The line number of the source line containing the execution point represented
+ // by this stack trace element, or a negative number if this information is
+ // unavailable.
+ LineNumber int32 `protobuf:"varint,4,opt,name=line_number,json=lineNumber,proto3" json:"line_number,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *StackTraceElementProto) Reset() { *m = StackTraceElementProto{} }
+func (m *StackTraceElementProto) String() string { return proto.CompactTextString(m) }
+func (*StackTraceElementProto) ProtoMessage() {}
+func (*StackTraceElementProto) Descriptor() ([]byte, []int) {
+ return fileDescriptor_434bbf44284586dc, []int{3}
+}
+
+func (m *StackTraceElementProto) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_StackTraceElementProto.Unmarshal(m, b)
+}
+func (m *StackTraceElementProto) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_StackTraceElementProto.Marshal(b, m, deterministic)
+}
+func (m *StackTraceElementProto) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_StackTraceElementProto.Merge(m, src)
+}
+func (m *StackTraceElementProto) XXX_Size() int {
+ return xxx_messageInfo_StackTraceElementProto.Size(m)
+}
+func (m *StackTraceElementProto) XXX_DiscardUnknown() {
+ xxx_messageInfo_StackTraceElementProto.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_StackTraceElementProto proto.InternalMessageInfo
+
+func (m *StackTraceElementProto) GetClassName() string {
+ if m != nil {
+ return m.ClassName
+ }
+ return ""
+}
+
+func (m *StackTraceElementProto) GetMethodName() string {
+ if m != nil {
+ return m.MethodName
+ }
+ return ""
+}
+
+func (m *StackTraceElementProto) GetFileName() string {
+ if m != nil {
+ return m.FileName
+ }
+ return ""
+}
+
+func (m *StackTraceElementProto) GetLineNumber() int32 {
+ if m != nil {
+ return m.LineNumber
+ }
+ return 0
+}
+
+// An exception that was thrown by some code, based on the Java type of the same name.
+//
+// See: https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Throwable.html
+type ThrowableProto struct {
+ // The name of the class of the exception that was actually thrown. Downstream readers
+ // of this message may or may not have the actual class available to initialize, so
+ // this is just used to prefix the message of a generic exception type.
+ OriginalClassName string `protobuf:"bytes,1,opt,name=original_class_name,json=originalClassName,proto3" json:"original_class_name,omitempty"`
+ // The message of this throwable. Not filled if there is no message.
+ OriginalMessage string `protobuf:"bytes,2,opt,name=original_message,json=originalMessage,proto3" json:"original_message,omitempty"`
+ // The stack trace of this Throwable.
+ StackTrace []*StackTraceElementProto `protobuf:"bytes,3,rep,name=stack_trace,json=stackTrace,proto3" json:"stack_trace,omitempty"`
+ // The cause of this Throwable. Not filled if there is no cause.
+ Cause *ThrowableProto `protobuf:"bytes,4,opt,name=cause,proto3" json:"cause,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *ThrowableProto) Reset() { *m = ThrowableProto{} }
+func (m *ThrowableProto) String() string { return proto.CompactTextString(m) }
+func (*ThrowableProto) ProtoMessage() {}
+func (*ThrowableProto) Descriptor() ([]byte, []int) {
+ return fileDescriptor_434bbf44284586dc, []int{4}
+}
+
+func (m *ThrowableProto) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_ThrowableProto.Unmarshal(m, b)
+}
+func (m *ThrowableProto) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_ThrowableProto.Marshal(b, m, deterministic)
+}
+func (m *ThrowableProto) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_ThrowableProto.Merge(m, src)
+}
+func (m *ThrowableProto) XXX_Size() int {
+ return xxx_messageInfo_ThrowableProto.Size(m)
+}
+func (m *ThrowableProto) XXX_DiscardUnknown() {
+ xxx_messageInfo_ThrowableProto.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_ThrowableProto proto.InternalMessageInfo
+
+func (m *ThrowableProto) GetOriginalClassName() string {
+ if m != nil {
+ return m.OriginalClassName
+ }
+ return ""
+}
+
+func (m *ThrowableProto) GetOriginalMessage() string {
+ if m != nil {
+ return m.OriginalMessage
+ }
+ return ""
+}
+
+func (m *ThrowableProto) GetStackTrace() []*StackTraceElementProto {
+ if m != nil {
+ return m.StackTrace
+ }
+ return nil
+}
+
+func (m *ThrowableProto) GetCause() *ThrowableProto {
+ if m != nil {
+ return m.Cause
+ }
+ return nil
+}
+
+func init() {
+ proto.RegisterType((*StringValue)(nil), "StringValue")
+ proto.RegisterType((*Int32Value)(nil), "Int32Value")
+ proto.RegisterType((*Map)(nil), "Map")
+ proto.RegisterMapType((map[string]string)(nil), "Map.AttachmentsEntry")
+ proto.RegisterType((*StackTraceElementProto)(nil), "StackTraceElementProto")
+ proto.RegisterType((*ThrowableProto)(nil), "ThrowableProto")
+}
+
+func init() { proto.RegisterFile("proto/payload.proto", fileDescriptor_434bbf44284586dc) }
+
+var fileDescriptor_434bbf44284586dc = []byte{
+ // 353 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x92, 0x4f, 0x4f, 0xea, 0x40,
+ 0x14, 0xc5, 0x53, 0xfa, 0x78, 0x79, 0xdc, 0x26, 0x0f, 0x1c, 0xfc, 0xd3, 0x68, 0x8c, 0xa4, 0xc6,
+ 0x04, 0x37, 0x35, 0x81, 0x85, 0xc4, 0x85, 0x89, 0x31, 0x2c, 0x5c, 0x40, 0x4c, 0x21, 0x6e, 0x9b,
+ 0x4b, 0x19, 0xa1, 0x61, 0x3a, 0x6d, 0x66, 0x06, 0x0d, 0x1b, 0x3f, 0x86, 0x9f, 0xca, 0x0f, 0x65,
+ 0x66, 0xc6, 0x02, 0x2a, 0xbb, 0x99, 0xdf, 0x39, 0xbd, 0x3d, 0xf7, 0x64, 0xa0, 0x59, 0x88, 0x5c,
+ 0xe5, 0x57, 0x05, 0xae, 0x58, 0x8e, 0xd3, 0xd0, 0xdc, 0x82, 0x73, 0xf0, 0x46, 0x4a, 0xa4, 0x7c,
+ 0xf6, 0x84, 0x6c, 0x49, 0xc9, 0x3e, 0x54, 0x5f, 0xf4, 0xc1, 0x77, 0x5a, 0x4e, 0xbb, 0x16, 0xd9,
+ 0x4b, 0x10, 0x00, 0x3c, 0x70, 0xd5, 0xed, 0xec, 0xf0, 0x54, 0x4b, 0xcf, 0x1b, 0xb8, 0x03, 0x2c,
+ 0xc8, 0x35, 0x78, 0xa8, 0x14, 0x26, 0xf3, 0x8c, 0x72, 0x25, 0x7d, 0xa7, 0xe5, 0xb6, 0xbd, 0xce,
+ 0x41, 0x38, 0xc0, 0x22, 0xbc, 0xdb, 0xf0, 0x3e, 0x57, 0x62, 0x15, 0x6d, 0x3b, 0x8f, 0x6f, 0xa1,
+ 0xf1, 0xd3, 0x40, 0x1a, 0xe0, 0x2e, 0xe8, 0xea, 0x2b, 0x8b, 0x3e, 0x6e, 0xfe, 0x5d, 0xd9, 0xca,
+ 0x77, 0x53, 0xe9, 0x39, 0xc1, 0xbb, 0x03, 0x87, 0x23, 0x85, 0xc9, 0x62, 0x2c, 0x30, 0xa1, 0x7d,
+ 0x46, 0xf5, 0x9c, 0x47, 0xbd, 0x23, 0x39, 0x05, 0x48, 0x18, 0x4a, 0x19, 0x73, 0xcc, 0xca, 0xcd,
+ 0x6a, 0x86, 0x0c, 0x31, 0xa3, 0xe4, 0x0c, 0xbc, 0x8c, 0xaa, 0x79, 0x3e, 0xb5, 0xba, 0x9d, 0x0c,
+ 0x16, 0x19, 0xc3, 0x09, 0xd4, 0x9e, 0x53, 0x46, 0xad, 0xec, 0x1a, 0xf9, 0x9f, 0x06, 0xe5, 0xd7,
+ 0x2c, 0xe5, 0x34, 0xe6, 0xcb, 0x6c, 0x42, 0x85, 0xff, 0xc7, 0x74, 0x02, 0x1a, 0x0d, 0x0d, 0x09,
+ 0x3e, 0x1c, 0xf8, 0x3f, 0x9e, 0x8b, 0xfc, 0x15, 0x27, 0x8c, 0xda, 0x40, 0x21, 0x34, 0x73, 0x91,
+ 0xce, 0x52, 0x8e, 0x2c, 0xfe, 0x95, 0x6c, 0xaf, 0x94, 0xee, 0xd7, 0x09, 0x2f, 0xa1, 0xb1, 0xf6,
+ 0x67, 0x54, 0x4a, 0x9c, 0x95, 0x31, 0xeb, 0x25, 0x1f, 0x58, 0x4c, 0x7a, 0xe0, 0x49, 0xdd, 0x42,
+ 0xac, 0x74, 0x0d, 0xbe, 0x6b, 0xfa, 0x3f, 0x0a, 0x77, 0x37, 0x13, 0x81, 0x5c, 0x73, 0x72, 0x01,
+ 0xd5, 0x04, 0x97, 0x92, 0x9a, 0x15, 0xbc, 0x4e, 0x3d, 0xfc, 0x1e, 0x3a, 0xb2, 0xea, 0xe4, 0xaf,
+ 0x79, 0x37, 0xdd, 0xcf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x04, 0x4d, 0x68, 0x3a, 0x4e, 0x02, 0x00,
+ 0x00,
+}
diff --git a/protocol/dubbo/impl/proto/payload.proto b/protocol/dubbo/impl/proto/payload.proto
new file mode 100644
index 0000000..19f644e
--- /dev/null
+++ b/protocol/dubbo/impl/proto/payload.proto
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto3";
+
+// equivalent java StringValue
+message StringValue {
+ string value = 1;
+}
+
+// equivalent java Int32Value
+message Int32Value {
+ int32 value = 1;
+}
+
+// equivalent java MapValue
+message Map {
+ map<string, string> attachments = 1;
+}
+
+// copied from dubbo GenericProtobufObjectOutput.java
+// Messages used for transporting debug information between server and client.
+// An element in a stack trace, based on the Java type of the same name.
+//
+// See: https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/StackTraceElement.html
+message StackTraceElementProto {
+ // The fully qualified name of the class containing the execution point
+ // represented by the stack trace element.
+ string class_name = 1;
+
+ // The name of the method containing the execution point represented by the
+ // stack trace element
+ string method_name = 2;
+
+ // The name of the file containing the execution point represented by the
+ // stack trace element, or null if this information is unavailable.
+ string file_name = 3;
+
+ // The line number of the source line containing the execution point represented
+ // by this stack trace element, or a negative number if this information is
+ // unavailable.
+ int32 line_number = 4;
+}
+
+// An exception that was thrown by some code, based on the Java type of the same name.
+//
+// See: https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Throwable.html
+message ThrowableProto {
+ // The name of the class of the exception that was actually thrown. Downstream readers
+ // of this message may or may not have the actual class available to initialize, so
+ // this is just used to prefix the message of a generic exception type.
+ string original_class_name = 1;
+
+ // The message of this throwable. Not filled if there is no message.
+ string original_message = 2;
+
+ // The stack trace of this Throwable.
+ repeated StackTraceElementProto stack_trace = 3;
+
+ // The cause of this Throwable. Not filled if there is no cause.
+ ThrowableProto cause = 4;
+}
+
+
diff --git a/protocol/dubbo/client.go b/protocol/dubbo/impl/remoting/client_impl.go
similarity index 57%
copy from protocol/dubbo/client.go
copy to protocol/dubbo/impl/remoting/client_impl.go
index 6d1b771..e5f7c71 100644
--- a/protocol/dubbo/client.go
+++ b/protocol/dubbo/impl/remoting/client_impl.go
@@ -14,8 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-package dubbo
+package remoting
import (
"math/rand"
@@ -25,19 +24,17 @@ import (
)
import (
- hessian "github.com/apache/dubbo-go-hessian2"
"github.com/dubbogo/getty"
gxsync "github.com/dubbogo/gost/sync"
perrors "github.com/pkg/errors"
"go.uber.org/atomic"
- "gopkg.in/yaml.v2"
)
import (
"github.com/apache/dubbo-go/common"
"github.com/apache/dubbo-go/common/constant"
"github.com/apache/dubbo-go/common/logger"
- "github.com/apache/dubbo-go/config"
+ "github.com/apache/dubbo-go/protocol/dubbo/impl"
)
var (
@@ -46,48 +43,13 @@ var (
errSessionNotExist = perrors.New("session not exist")
errClientClosed = perrors.New("client closed")
errClientReadTimeout = perrors.New("client read timeout")
+)
+var (
clientConf *ClientConfig
clientGrpool *gxsync.TaskPool
)
-func init() {
-
- // load clientconfig from consumer_config
- // default use dubbo
- consumerConfig := config.GetConsumerConfig()
- if consumerConfig.ApplicationConfig == nil {
- return
- }
- protocolConf := config.GetConsumerConfig().ProtocolConf
- defaultClientConfig := GetDefaultClientConfig()
- if protocolConf == nil {
- logger.Info("protocol_conf default use dubbo config")
- } else {
- dubboConf := protocolConf.(map[interface{}]interface{})[DUBBO]
- if dubboConf == nil {
- logger.Warnf("dubboConf is nil")
- return
- }
- dubboConfByte, err := yaml.Marshal(dubboConf)
- if err != nil {
- panic(err)
- }
- err = yaml.Unmarshal(dubboConfByte, &defaultClientConfig)
- if err != nil {
- panic(err)
- }
- }
- clientConf = &defaultClientConfig
- if err := clientConf.CheckValidity(); err != nil {
- logger.Warnf("[CheckValidity] error: %v", err)
- return
- }
- setClientGrpool()
-
- rand.Seed(time.Now().UnixNano())
-}
-
// SetClientConf set dubbo client config.
func SetClientConf(c ClientConfig) {
clientConf = &c
@@ -100,8 +62,8 @@ func SetClientConf(c ClientConfig) {
}
// GetClientConf get dubbo client config.
-func GetClientConf() ClientConfig {
- return *clientConf
+func GetClientConf() *ClientConfig {
+ return clientConf
}
func setClientGrpool() {
@@ -131,12 +93,13 @@ type AsyncCallbackResponse struct {
// Client is dubbo protocol client.
type Client struct {
- opts Options
- conf ClientConfig
- pool *gettyRPCClientPool
- sequence atomic.Uint64
+ Opts Options
+ Conf ClientConfig
+ Pool *gettyRPCClientPool
+ Sequence atomic.Uint64
- pendingResponses *sync.Map
+ PendingResponses *sync.Map
+ codec impl.DubboCodec
}
// NewClient create a new Client.
@@ -150,19 +113,20 @@ func NewClient(opt Options) *Client {
opt.RequestTimeout = 3 * time.Second
}
- // make sure that client request sequence is an odd number
+ // make sure that client request Sequence is an odd number
initSequence := uint64(rand.Int63n(time.Now().UnixNano()))
if initSequence%2 == 0 {
initSequence++
}
c := &Client{
- opts: opt,
- pendingResponses: new(sync.Map),
- conf: *clientConf,
+ Opts: opt,
+ PendingResponses: new(sync.Map),
+ Conf: *clientConf,
+ codec: impl.DubboCodec{},
}
- c.sequence.Store(initSequence)
- c.pool = newGettyRPCClientConnPool(c, clientConf.PoolSize, time.Duration(int(time.Second)*clientConf.PoolTTL))
+ c.Sequence.Store(initSequence)
+ c.Pool = NewGettyRPCClientConnPool(c, clientConf.PoolSize, time.Duration(int(time.Second)*clientConf.PoolTTL))
return c
}
@@ -178,6 +142,10 @@ type Request struct {
// NewRequest create a new Request.
func NewRequest(addr string, svcUrl common.URL, method string, args interface{}, atta map[string]string) *Request {
+ // NOTE: compatible with old versions
+ if svcUrl.GetParam(constant.SERIALIZATION_KEY, "") == "" {
+ svcUrl.SetParam(constant.SERIALIZATION_KEY, constant.DEFAULT_SERIALIZATION)
+ }
return &Request{
addr: addr,
svcUrl: svcUrl,
@@ -189,68 +157,42 @@ func NewRequest(addr string, svcUrl common.URL, method string, args interface{},
// Response is dubbo protocol response.
type Response struct {
- reply interface{}
- atta map[string]string
+ Reply interface{}
+ Atta map[string]string
}
// NewResponse create a new Response.
func NewResponse(reply interface{}, atta map[string]string) *Response {
return &Response{
- reply: reply,
- atta: atta,
+ Reply: reply,
+ Atta: atta,
}
}
-// CallOneway call by one way
+// CallOneway call one way
func (c *Client) CallOneway(request *Request) error {
- return perrors.WithStack(c.call(CT_OneWay, request, NewResponse(nil, nil), nil))
+ return perrors.WithStack(c.call(impl.CT_OneWay, request, NewResponse(nil, nil), nil))
}
-// Call call remoting by two way or one way, if @response.reply is nil, the way of call is one way.
+// Call if @response is nil, the transport layer will get the response without notify the invoker.
func (c *Client) Call(request *Request, response *Response) error {
- ct := CT_TwoWay
- if response.reply == nil {
- ct = CT_OneWay
+
+ ct := impl.CT_TwoWay
+ if response.Reply == nil {
+ ct = impl.CT_OneWay
}
return perrors.WithStack(c.call(ct, request, response, nil))
}
-// AsyncCall call remoting by async with callback.
+// AsyncCall ...
func (c *Client) AsyncCall(request *Request, callback common.AsyncCallback, response *Response) error {
- return perrors.WithStack(c.call(CT_TwoWay, request, response, callback))
-}
-
-func (c *Client) call(ct CallType, request *Request, response *Response, callback common.AsyncCallback) error {
- p := &DubboPackage{}
- p.Service.Path = strings.TrimPrefix(request.svcUrl.Path, "/")
- p.Service.Interface = request.svcUrl.GetParam(constant.INTERFACE_KEY, "")
- p.Service.Version = request.svcUrl.GetParam(constant.VERSION_KEY, "")
- p.Service.Group = request.svcUrl.GetParam(constant.GROUP_KEY, "")
- p.Service.Method = request.method
-
- p.Service.Timeout = c.opts.RequestTimeout
- var timeout = request.svcUrl.GetParam(strings.Join([]string{constant.METHOD_KEYS, request.method + constant.RETRIES_KEY}, "."), "")
- if len(timeout) != 0 {
- if t, err := time.ParseDuration(timeout); err == nil {
- p.Service.Timeout = t
- }
- }
-
- p.Header.SerialID = byte(S_Dubbo)
- p.Body = hessian.NewRequest(request.args, request.atta)
- var rsp *PendingResponse
- if ct != CT_OneWay {
- p.Header.Type = hessian.PackageRequest_TwoWay
- rsp = NewPendingResponse()
- rsp.response = response
- rsp.callback = callback
- } else {
- p.Header.Type = hessian.PackageRequest
- }
+ return perrors.WithStack(c.call(impl.CT_TwoWay, request, response, callback))
+}
+func (c *Client) call(ct impl.CallType, request *Request, response *Response, callback common.AsyncCallback) error {
var (
err error
session getty.Session
@@ -265,23 +207,60 @@ func (c *Client) call(ct CallType, request *Request, response *Response, callbac
}
defer func() {
if err == nil {
- c.pool.put(conn)
+ c.Pool.put(conn)
return
}
conn.close()
}()
+ var rsp *PendingResponse
+ svc := impl.Service{}
+ header := impl.DubboHeader{}
+ svc.Path = strings.TrimPrefix(request.svcUrl.Path, "/")
+ svc.Interface = request.svcUrl.GetParam(constant.INTERFACE_KEY, "")
+ svc.Version = request.svcUrl.GetParam(constant.VERSION_KEY, "")
+ svc.Group = request.svcUrl.GetParam(constant.GROUP_KEY, "")
+ svc.Method = request.method
+ svc.Timeout = c.Opts.RequestTimeout
+ var timeout = request.svcUrl.GetParam(strings.Join([]string{constant.METHOD_KEYS, request.method + constant.RETRIES_KEY}, "."), "")
+ if len(timeout) != 0 {
+ if t, err := time.ParseDuration(timeout); err == nil {
+ svc.Timeout = t
+ }
+ }
+ p := NewClientRequestPackage(header, svc)
+
+ serialization := request.svcUrl.GetParam(constant.SERIALIZATION_KEY, c.Conf.Serialization)
+ if serialization == constant.HESSIAN2_SERIALIZATION {
+ p.Header.SerialID = constant.S_Hessian2
+ } else if serialization == constant.PROTOBUF_SERIALIZATION {
+ p.Header.SerialID = constant.S_Proto
+ }
+ p.SetBody(impl.NewRequestPayload(request.args, request.atta))
+
+ if err := impl.LoadSerializer(p); err != nil {
+ return err
+ }
+
+ if ct != impl.CT_OneWay {
+ p.Header.Type = impl.PackageRequest_TwoWay
+ rsp = NewPendingResponse()
+ rsp.response = response
+ rsp.callback = callback
+ } else {
+ p.Header.Type = impl.PackageRequest
+ }
if err = c.transfer(session, p, rsp); err != nil {
return perrors.WithStack(err)
}
- if ct == CT_OneWay || callback != nil {
+ if ct == impl.CT_OneWay || callback != nil {
return nil
}
select {
- case <-getty.GetTimeWheel().After(c.opts.RequestTimeout):
- c.removePendingResponse(SequenceType(rsp.seq))
+ case <-getty.GetTimeWheel().After(c.Opts.RequestTimeout):
+ c.removePendingResponse(impl.SequenceType(rsp.seq))
return perrors.WithStack(errClientReadTimeout)
case <-rsp.done:
err = rsp.err
@@ -292,14 +271,14 @@ func (c *Client) call(ct CallType, request *Request, response *Response, callbac
// Close close the client pool.
func (c *Client) Close() {
- if c.pool != nil {
- c.pool.close()
+ if c.Pool != nil {
+ c.Pool.close()
}
- c.pool = nil
+ c.Pool = nil
}
func (c *Client) selectSession(addr string) (*gettyRPCClient, getty.Session, error) {
- rpcClient, err := c.pool.getGettyRpcClient(DUBBO, addr)
+ rpcClient, err := c.Pool.getGettyRpcClient(impl.DUBBO, addr)
if err != nil {
return nil, nil, perrors.WithStack(err)
}
@@ -310,7 +289,7 @@ func (c *Client) heartbeat(session getty.Session) error {
return c.transfer(session, nil, NewPendingResponse())
}
-func (c *Client) transfer(session getty.Session, pkg *DubboPackage,
+func (c *Client) transfer(session getty.Session, pkg *impl.DubboPackage,
rsp *PendingResponse) error {
var (
@@ -318,16 +297,24 @@ func (c *Client) transfer(session getty.Session, pkg *DubboPackage,
err error
)
- sequence = c.sequence.Add(1)
+ sequence = c.Sequence.Add(1)
if pkg == nil {
- pkg = &DubboPackage{}
- pkg.Body = hessian.NewRequest([]interface{}{}, nil)
- pkg.Body = []interface{}{}
- pkg.Header.Type = hessian.PackageHeartbeat
- pkg.Header.SerialID = byte(S_Dubbo)
+ // make heartbeat package
+ header := impl.DubboHeader{
+ Type: impl.PackageHeartbeat,
+ SerialID: constant.S_Hessian2,
+ }
+ pkg = NewClientRequestPackage(header, impl.Service{})
+ // SetBody
+ reqPayload := impl.NewRequestPayload([]interface{}{}, nil)
+ pkg.SetBody(reqPayload)
+ // set serializer
+ if err := impl.LoadSerializer(pkg); err != nil {
+ return err
+ }
}
- pkg.Header.ID = int64(sequence)
+ pkg.SetID(int64(sequence))
// cond1
if rsp != nil {
@@ -335,9 +322,9 @@ func (c *Client) transfer(session getty.Session, pkg *DubboPackage,
c.addPendingResponse(rsp)
}
- err = session.WritePkg(pkg, c.opts.RequestTimeout)
+ err = session.WritePkg(pkg, c.Opts.RequestTimeout)
if err != nil {
- c.removePendingResponse(SequenceType(rsp.seq))
+ c.removePendingResponse(impl.SequenceType(rsp.seq))
} else if rsp != nil { // cond2
// cond2 should not merged with cond1. cause the response package may be returned very
// soon and it will be handled by other goroutine.
@@ -348,16 +335,57 @@ func (c *Client) transfer(session getty.Session, pkg *DubboPackage,
}
func (c *Client) addPendingResponse(pr *PendingResponse) {
- c.pendingResponses.Store(SequenceType(pr.seq), pr)
+ c.PendingResponses.Store(impl.SequenceType(pr.seq), pr)
}
-func (c *Client) removePendingResponse(seq SequenceType) *PendingResponse {
- if c.pendingResponses == nil {
+func (c *Client) removePendingResponse(seq impl.SequenceType) *PendingResponse {
+ if c.PendingResponses == nil {
return nil
}
- if presp, ok := c.pendingResponses.Load(seq); ok {
- c.pendingResponses.Delete(seq)
+ if presp, ok := c.PendingResponses.Load(seq); ok {
+ c.PendingResponses.Delete(seq)
return presp.(*PendingResponse)
}
return nil
}
+
+// PendingResponse ...
+type PendingResponse struct {
+ seq uint64
+ err error
+ start time.Time
+ readStart time.Time
+ callback common.AsyncCallback
+ response *Response
+ done chan struct{}
+}
+
+// NewPendingResponse create a PendingResponses.
+func NewPendingResponse() *PendingResponse {
+ return &PendingResponse{
+ start: time.Now(),
+ response: &Response{},
+ done: make(chan struct{}),
+ }
+}
+
+// GetCallResponse get AsyncCallbackResponse.
+func (r PendingResponse) GetCallResponse() common.CallbackResponse {
+ return AsyncCallbackResponse{
+ Cause: r.err,
+ Start: r.start,
+ ReadStart: r.readStart,
+ Reply: r.response,
+ }
+}
+
+// client side request package, just for serialization
+func NewClientRequestPackage(header impl.DubboHeader, svc impl.Service) *impl.DubboPackage {
+ return &impl.DubboPackage{
+ Header: header,
+ Service: svc,
+ Body: nil,
+ Err: nil,
+ Codec: impl.NewDubboCodec(nil),
+ }
+}
diff --git a/protocol/dubbo/config.go b/protocol/dubbo/impl/remoting/config.go
similarity index 81%
rename from protocol/dubbo/config.go
rename to protocol/dubbo/impl/remoting/config.go
index 635d121..1f26422 100644
--- a/protocol/dubbo/config.go
+++ b/protocol/dubbo/impl/remoting/config.go
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-package dubbo
+package remoting
import (
"time"
@@ -33,16 +33,16 @@ type (
TcpNoDelay bool `default:"true" yaml:"tcp_no_delay" json:"tcp_no_delay,omitempty"`
TcpKeepAlive bool `default:"true" yaml:"tcp_keep_alive" json:"tcp_keep_alive,omitempty"`
KeepAlivePeriod string `default:"180s" yaml:"keep_alive_period" json:"keep_alive_period,omitempty"`
- keepAlivePeriod time.Duration
+ KeepAlivePeriodD time.Duration
TcpRBufSize int `default:"262144" yaml:"tcp_r_buf_size" json:"tcp_r_buf_size,omitempty"`
TcpWBufSize int `default:"65536" yaml:"tcp_w_buf_size" json:"tcp_w_buf_size,omitempty"`
PkgWQSize int `default:"1024" yaml:"pkg_wq_size" json:"pkg_wq_size,omitempty"`
TcpReadTimeout string `default:"1s" yaml:"tcp_read_timeout" json:"tcp_read_timeout,omitempty"`
- tcpReadTimeout time.Duration
+ TcpReadTimeoutD time.Duration
TcpWriteTimeout string `default:"5s" yaml:"tcp_write_timeout" json:"tcp_write_timeout,omitempty"`
- tcpWriteTimeout time.Duration
+ TcpWriteTimeoutD time.Duration
WaitTimeout string `default:"7s" yaml:"wait_timeout" json:"wait_timeout,omitempty"`
- waitTimeout time.Duration
+ WaitTimeoutD time.Duration
MaxMsgLen int `default:"1024" yaml:"max_msg_len" json:"max_msg_len,omitempty"`
SessionName string `default:"rpc" yaml:"session_name" json:"session_name,omitempty"`
}
@@ -50,9 +50,9 @@ type (
// ServerConfig holds supported types by the multiconfig package
ServerConfig struct {
// session
- SessionTimeout string `default:"60s" yaml:"session_timeout" json:"session_timeout,omitempty"`
- sessionTimeout time.Duration
- SessionNumber int `default:"1000" yaml:"session_number" json:"session_number,omitempty"`
+ SessionTimeout string `default:"60s" yaml:"session_timeout" json:"session_timeout,omitempty"`
+ SessionTimeoutD time.Duration
+ SessionNumber int `default:"1000" yaml:"session_number" json:"session_number,omitempty"`
// grpool
GrPoolSize int `default:"0" yaml:"gr_pool_size" json:"gr_pool_size,omitempty"`
@@ -67,16 +67,16 @@ type (
ClientConfig struct {
ReconnectInterval int `default:"0" yaml:"reconnect_interval" json:"reconnect_interval,omitempty"`
- // session pool
+ // session Pool
ConnectionNum int `default:"16" yaml:"connection_number" json:"connection_number,omitempty"`
// heartbeat
- HeartbeatPeriod string `default:"15s" yaml:"heartbeat_period" json:"heartbeat_period,omitempty"`
- heartbeatPeriod time.Duration
+ HeartbeatPeriod string `default:"15s" yaml:"heartbeat_period" json:"heartbeat_period,omitempty"`
+ HeartbeatPeriodD time.Duration
// session
- SessionTimeout string `default:"60s" yaml:"session_timeout" json:"session_timeout,omitempty"`
- sessionTimeout time.Duration
+ SessionTimeout string `default:"60s" yaml:"session_timeout" json:"session_timeout,omitempty"`
+ SessionTimeoutD time.Duration
// Connection Pool
PoolSize int `default:"2" yaml:"pool_size" json:"pool_size,omitempty"`
@@ -89,6 +89,9 @@ type (
// session tcp parameters
GettySessionParam GettySessionParam `required:"true" yaml:"getty_session_param" json:"getty_session_param,omitempty"`
+
+ // serialization
+ Serialization string `default:"hessian2" yaml:"serialization" json:"serialization"`
}
)
@@ -104,6 +107,7 @@ func GetDefaultClientConfig() ClientConfig {
GrPoolSize: 200,
QueueLen: 64,
QueueNumber: 10,
+ Serialization: "hessian2",
GettySessionParam: GettySessionParam{
CompressEncoding: false,
TcpNoDelay: true,
@@ -149,19 +153,19 @@ func GetDefaultServerConfig() ServerConfig {
func (c *GettySessionParam) CheckValidity() error {
var err error
- if c.keepAlivePeriod, err = time.ParseDuration(c.KeepAlivePeriod); err != nil {
+ if c.KeepAlivePeriodD, err = time.ParseDuration(c.KeepAlivePeriod); err != nil {
return perrors.WithMessagef(err, "time.ParseDuration(KeepAlivePeriod{%#v})", c.KeepAlivePeriod)
}
- if c.tcpReadTimeout, err = time.ParseDuration(c.TcpReadTimeout); err != nil {
+ if c.TcpReadTimeoutD, err = time.ParseDuration(c.TcpReadTimeout); err != nil {
return perrors.WithMessagef(err, "time.ParseDuration(TcpReadTimeout{%#v})", c.TcpReadTimeout)
}
- if c.tcpWriteTimeout, err = time.ParseDuration(c.TcpWriteTimeout); err != nil {
+ if c.TcpWriteTimeoutD, err = time.ParseDuration(c.TcpWriteTimeout); err != nil {
return perrors.WithMessagef(err, "time.ParseDuration(TcpWriteTimeout{%#v})", c.TcpWriteTimeout)
}
- if c.waitTimeout, err = time.ParseDuration(c.WaitTimeout); err != nil {
+ if c.WaitTimeoutD, err = time.ParseDuration(c.WaitTimeout); err != nil {
return perrors.WithMessagef(err, "time.ParseDuration(WaitTimeout{%#v})", c.WaitTimeout)
}
@@ -174,16 +178,16 @@ func (c *ClientConfig) CheckValidity() error {
c.ReconnectInterval = c.ReconnectInterval * 1e6
- if c.heartbeatPeriod, err = time.ParseDuration(c.HeartbeatPeriod); err != nil {
+ if c.HeartbeatPeriodD, err = time.ParseDuration(c.HeartbeatPeriod); err != nil {
return perrors.WithMessagef(err, "time.ParseDuration(HeartbeatPeroid{%#v})", c.HeartbeatPeriod)
}
- if c.heartbeatPeriod >= time.Duration(getty.MaxWheelTimeSpan) {
+ if c.HeartbeatPeriodD >= time.Duration(getty.MaxWheelTimeSpan) {
return perrors.WithMessagef(err, "heartbeat_period %s should be less than %s",
c.HeartbeatPeriod, time.Duration(getty.MaxWheelTimeSpan))
}
- if c.sessionTimeout, err = time.ParseDuration(c.SessionTimeout); err != nil {
+ if c.SessionTimeoutD, err = time.ParseDuration(c.SessionTimeout); err != nil {
return perrors.WithMessagef(err, "time.ParseDuration(SessionTimeout{%#v})", c.SessionTimeout)
}
@@ -194,11 +198,11 @@ func (c *ClientConfig) CheckValidity() error {
func (c *ServerConfig) CheckValidity() error {
var err error
- if c.sessionTimeout, err = time.ParseDuration(c.SessionTimeout); err != nil {
+ if c.SessionTimeoutD, err = time.ParseDuration(c.SessionTimeout); err != nil {
return perrors.WithMessagef(err, "time.ParseDuration(SessionTimeout{%#v})", c.SessionTimeout)
}
- if c.sessionTimeout >= time.Duration(getty.MaxWheelTimeSpan) {
+ if c.SessionTimeoutD >= time.Duration(getty.MaxWheelTimeSpan) {
return perrors.WithMessagef(err, "session_timeout %s should be less than %s",
c.SessionTimeout, time.Duration(getty.MaxWheelTimeSpan))
}
diff --git a/protocol/dubbo/impl/remoting/errors.go b/protocol/dubbo/impl/remoting/errors.go
new file mode 100644
index 0000000..8fda9b3
--- /dev/null
+++ b/protocol/dubbo/impl/remoting/errors.go
@@ -0,0 +1,17 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package remoting
diff --git a/protocol/dubbo/pool.go b/protocol/dubbo/impl/remoting/pool.go
similarity index 91%
rename from protocol/dubbo/pool.go
rename to protocol/dubbo/impl/remoting/pool.go
index c9f5e34..dc8b123 100644
--- a/protocol/dubbo/pool.go
+++ b/protocol/dubbo/impl/remoting/pool.go
@@ -15,10 +15,11 @@
* limitations under the License.
*/
-package dubbo
+package remoting
import (
"fmt"
+
"math/rand"
"net"
"sync"
@@ -49,7 +50,7 @@ type gettyRPCClient struct {
}
var (
- errClientPoolClosed = perrors.New("client pool closed")
+ errClientPoolClosed = perrors.New("client Pool closed")
)
func newGettyRPCClientConn(pool *gettyRPCClientPool, protocol, addr string) (*gettyRPCClient, error) {
@@ -59,13 +60,13 @@ func newGettyRPCClientConn(pool *gettyRPCClientPool, protocol, addr string) (*ge
pool: pool,
gettyClient: getty.NewTCPClient(
getty.WithServerAddress(addr),
- getty.WithConnectionNumber((int)(pool.rpcClient.conf.ConnectionNum)),
- getty.WithReconnectInterval(pool.rpcClient.conf.ReconnectInterval),
+ getty.WithConnectionNumber((int)(pool.rpcClient.Conf.ConnectionNum)),
+ getty.WithReconnectInterval(pool.rpcClient.Conf.ReconnectInterval),
),
}
go c.gettyClient.RunEventLoop(c.newSession)
idx := 1
- times := int(pool.rpcClient.opts.ConnectTimeout / 1e6)
+ times := int(pool.rpcClient.Opts.ConnectTimeout / 1e6)
for {
idx++
if c.isAvailable() {
@@ -99,7 +100,7 @@ func (c *gettyRPCClient) newSession(session getty.Session) error {
conf ClientConfig
)
- conf = c.pool.rpcClient.conf
+ conf = c.pool.rpcClient.Conf
if conf.GettySessionParam.CompressEncoding {
session.SetCompressType(getty.CompressZip)
}
@@ -111,7 +112,7 @@ func (c *gettyRPCClient) newSession(session getty.Session) error {
tcpConn.SetNoDelay(conf.GettySessionParam.TcpNoDelay)
tcpConn.SetKeepAlive(conf.GettySessionParam.TcpKeepAlive)
if conf.GettySessionParam.TcpKeepAlive {
- tcpConn.SetKeepAlivePeriod(conf.GettySessionParam.keepAlivePeriod)
+ tcpConn.SetKeepAlivePeriod(conf.GettySessionParam.KeepAlivePeriodD)
}
tcpConn.SetReadBuffer(conf.GettySessionParam.TcpRBufSize)
tcpConn.SetWriteBuffer(conf.GettySessionParam.TcpWBufSize)
@@ -121,10 +122,10 @@ func (c *gettyRPCClient) newSession(session getty.Session) error {
session.SetPkgHandler(NewRpcClientPackageHandler(c.pool.rpcClient))
session.SetEventListener(NewRpcClientHandler(c))
session.SetWQLen(conf.GettySessionParam.PkgWQSize)
- session.SetReadTimeout(conf.GettySessionParam.tcpReadTimeout)
- session.SetWriteTimeout(conf.GettySessionParam.tcpWriteTimeout)
- session.SetCronPeriod((int)(conf.heartbeatPeriod.Nanoseconds() / 1e6))
- session.SetWaitTime(conf.GettySessionParam.waitTimeout)
+ session.SetReadTimeout(conf.GettySessionParam.TcpReadTimeoutD)
+ session.SetWriteTimeout(conf.GettySessionParam.TcpWriteTimeoutD)
+ session.SetCronPeriod((int)(conf.HeartbeatPeriodD.Nanoseconds() / 1e6))
+ session.SetWaitTime(conf.GettySessionParam.WaitTimeoutD)
logger.Debugf("client new session:%s\n", session.Stat())
session.SetTaskPool(clientGrpool)
@@ -296,7 +297,7 @@ type gettyRPCClientPool struct {
conns []*gettyRPCClient
}
-func newGettyRPCClientConnPool(rpcClient *Client, size int, ttl time.Duration) *gettyRPCClientPool {
+func NewGettyRPCClientConnPool(rpcClient *Client, size int, ttl time.Duration) *gettyRPCClientPool {
return &gettyRPCClientPool{
rpcClient: rpcClient,
size: size,
@@ -368,7 +369,7 @@ func (p *gettyRPCClientPool) put(conn *gettyRPCClient) {
}
if len(p.conns) >= p.size {
- // delete @conn from client pool
+ // delete @conn from client Pool
// p.remove(conn)
conn.close()
return
diff --git a/protocol/dubbo/readwriter.go b/protocol/dubbo/impl/remoting/readwriter.go
similarity index 52%
rename from protocol/dubbo/readwriter.go
rename to protocol/dubbo/impl/remoting/readwriter.go
index 9cc7ea2..6d211f5 100644
--- a/protocol/dubbo/readwriter.go
+++ b/protocol/dubbo/impl/remoting/readwriter.go
@@ -15,81 +15,100 @@
* limitations under the License.
*/
-package dubbo
+// TODO: zero.xu readwrite 中将client/server handler 分开
+package remoting
import (
+ "bufio"
"bytes"
"reflect"
)
import (
- "github.com/apache/dubbo-go-hessian2"
+ hessian "github.com/apache/dubbo-go-hessian2"
"github.com/dubbogo/getty"
perrors "github.com/pkg/errors"
)
import (
- "github.com/apache/dubbo-go/common"
- "github.com/apache/dubbo-go/common/constant"
"github.com/apache/dubbo-go/common/logger"
+ "github.com/apache/dubbo-go/protocol/dubbo/impl"
)
////////////////////////////////////////////
// RpcClientPackageHandler
////////////////////////////////////////////
-// RpcClientPackageHandler handle package for client in getty.
+// RpcClientPackageHandler ...
type RpcClientPackageHandler struct {
client *Client
}
-// NewRpcClientPackageHandler create a RpcClientPackageHandler.
+// NewRpcClientPackageHandler ...
func NewRpcClientPackageHandler(client *Client) *RpcClientPackageHandler {
return &RpcClientPackageHandler{client: client}
}
-// Read decode @data to DubboPackage.
func (p *RpcClientPackageHandler) Read(ss getty.Session, data []byte) (interface{}, int, error) {
- pkg := &DubboPackage{}
-
- buf := bytes.NewBuffer(data)
- err := pkg.Unmarshal(buf, p.client)
- if err != nil {
+ pkg := NewClientResponsePackage(data)
+ if err := pkg.ReadHeader(); err != nil {
originErr := perrors.Cause(err)
if originErr == hessian.ErrHeaderNotEnough || originErr == hessian.ErrBodyNotEnough {
return nil, 0, nil
}
-
- logger.Errorf("pkg.Unmarshal(ss:%+v, len(@data):%d) = error:%+v", ss, len(data), err)
-
+ logger.Errorf("[RpcClientPackageHandler.Read] ss:%+v, len(@data):%d) = error:%+v ", ss, len(data), err)
return nil, 0, perrors.WithStack(err)
}
+ if pkg.IsHeartBeat() {
+ // heartbeat package doesn't need deserialize
+ return pkg, pkg.GetLen(), nil
+ }
- if pkg.Header.Type&hessian.PackageRequest == 0x00 {
- pkg.Err = pkg.Body.(*hessian.Response).Exception
- pkg.Body = NewResponse(pkg.Body.(*hessian.Response).RspObj, pkg.Body.(*hessian.Response).Attachments)
+ if err := impl.LoadSerializer(pkg); err != nil {
+ return nil, 0, err
}
- return pkg, hessian.HEADER_LENGTH + pkg.Header.BodyLen, nil
+ // load response
+ pendingRsp, ok := p.client.PendingResponses.Load(impl.SequenceType(pkg.GetHeader().ID))
+ if !ok {
+ return nil, 0, perrors.Errorf("client.GetPendingResopnse(%v) = nil", pkg.GetHeader().ID)
+ }
+ // set package body
+ body := impl.NewResponsePayload(pendingRsp.(*PendingResponse).response.Reply, nil, nil)
+ pkg.SetBody(body)
+ err := pkg.Unmarshal()
+ if err != nil {
+ return nil, 0, perrors.WithStack(err)
+ }
+ resp := pkg.Body.(*impl.ResponsePayload)
+ pkg.Err = resp.Exception
+ pkg.Body = NewResponse(resp.RspObj, resp.Attachments)
+ return pkg, pkg.GetLen(), nil
}
-// Write encode @pkg.
func (p *RpcClientPackageHandler) Write(ss getty.Session, pkg interface{}) ([]byte, error) {
- req, ok := pkg.(*DubboPackage)
+ req, ok := pkg.(*impl.DubboPackage)
if !ok {
- logger.Errorf("illegal pkg:%+v\n", pkg)
return nil, perrors.New("invalid rpc request")
}
-
buf, err := req.Marshal()
if err != nil {
logger.Warnf("binary.Write(req{%#v}) = err{%#v}", req, perrors.WithStack(err))
return nil, perrors.WithStack(err)
}
-
return buf.Bytes(), nil
}
+func NewClientResponsePackage(data []byte) *impl.DubboPackage {
+ return &impl.DubboPackage{
+ Header: impl.DubboHeader{},
+ Service: impl.Service{},
+ Body: &impl.ResponsePayload{},
+ Err: nil,
+ Codec: impl.NewDubboCodec(bufio.NewReaderSize(bytes.NewBuffer(data), len(data))),
+ }
+}
+
////////////////////////////////////////////
// RpcServerPackageHandler
////////////////////////////////////////////
@@ -98,17 +117,29 @@ var (
rpcServerPkgHandler = &RpcServerPackageHandler{}
)
-// RpcServerPackageHandler handle package for server in getty.
-type RpcServerPackageHandler struct{}
+// RpcServerPackageHandler ...
+type RpcServerPackageHandler struct {
+}
-// Read decode @data to DubboPackage.
func (p *RpcServerPackageHandler) Read(ss getty.Session, data []byte) (interface{}, int, error) {
- pkg := &DubboPackage{
- Body: make([]interface{}, 7),
+ pkg := NewServerRequestPackage(data)
+ if err := pkg.ReadHeader(); err != nil {
+ originErr := perrors.Cause(err)
+ if originErr == hessian.ErrHeaderNotEnough || originErr == hessian.ErrBodyNotEnough {
+ return nil, 0, nil
+ }
+ return nil, 0, perrors.WithStack(err)
+ }
+
+ if pkg.IsHeartBeat() {
+ return pkg, pkg.GetLen(), nil
}
- buf := bytes.NewBuffer(data)
- err := pkg.Unmarshal(buf)
+ if err := impl.LoadSerializer(pkg); err != nil {
+ return nil, 0, err
+ }
+
+ err := pkg.Unmarshal()
if err != nil {
originErr := perrors.Cause(err)
if originErr == hessian.ErrHeaderNotEnough || originErr == hessian.ErrBodyNotEnough {
@@ -116,75 +147,33 @@ func (p *RpcServerPackageHandler) Read(ss getty.Session, data []byte) (interface
}
logger.Errorf("pkg.Unmarshal(ss:%+v, len(@data):%d) = error:%+v", ss, len(data), err)
-
return nil, 0, perrors.WithStack(err)
}
-
- if pkg.Header.Type&hessian.PackageHeartbeat == 0x00 {
- // convert params of request
- req := pkg.Body.([]interface{}) // length of body should be 7
- if len(req) > 0 {
- var dubboVersion, argsTypes string
- var args []interface{}
- var attachments map[string]string
- if req[0] != nil {
- dubboVersion = req[0].(string)
- }
- if req[1] != nil {
- pkg.Service.Path = req[1].(string)
- }
- if req[2] != nil {
- pkg.Service.Version = req[2].(string)
- }
- if req[3] != nil {
- pkg.Service.Method = req[3].(string)
- }
- if req[4] != nil {
- argsTypes = req[4].(string)
- }
- if req[5] != nil {
- args = req[5].([]interface{})
- }
- if req[6] != nil {
- attachments = req[6].(map[string]string)
- }
- if pkg.Service.Path == "" && len(attachments[constant.PATH_KEY]) > 0 {
- pkg.Service.Path = attachments[constant.PATH_KEY]
- }
- if _, ok := attachments[constant.INTERFACE_KEY]; ok {
- pkg.Service.Interface = attachments[constant.INTERFACE_KEY]
- } else {
- pkg.Service.Interface = pkg.Service.Path
- }
- if len(attachments[constant.GROUP_KEY]) > 0 {
- pkg.Service.Group = attachments[constant.GROUP_KEY]
- }
- pkg.Body = map[string]interface{}{
- "dubboVersion": dubboVersion,
- "argsTypes": argsTypes,
- "args": args,
- "service": common.ServiceMap.GetService(DUBBO, pkg.Service.Path), // path as a key
- "attachments": attachments,
- }
- }
- }
-
- return pkg, hessian.HEADER_LENGTH + pkg.Header.BodyLen, nil
+ return pkg, pkg.GetLen(), nil
}
-// Write encode @pkg.
func (p *RpcServerPackageHandler) Write(ss getty.Session, pkg interface{}) ([]byte, error) {
- res, ok := pkg.(*DubboPackage)
+ res, ok := pkg.(*impl.DubboPackage)
if !ok {
logger.Errorf("illegal pkg:%+v\n, it is %+v", pkg, reflect.TypeOf(pkg))
return nil, perrors.New("invalid rpc response")
}
-
buf, err := res.Marshal()
if err != nil {
logger.Warnf("binary.Write(res{%#v}) = err{%#v}", res, perrors.WithStack(err))
return nil, perrors.WithStack(err)
}
-
return buf.Bytes(), nil
}
+
+// server side receive request package, just for deserialization
+func NewServerRequestPackage(data []byte) *impl.DubboPackage {
+ return &impl.DubboPackage{
+ Header: impl.DubboHeader{},
+ Service: impl.Service{},
+ Body: make([]interface{}, 7),
+ Err: nil,
+ Codec: impl.NewDubboCodec(bufio.NewReaderSize(bytes.NewBuffer(data), len(data))),
+ }
+
+}
diff --git a/protocol/dubbo/server.go b/protocol/dubbo/impl/remoting/server_impl.go
similarity index 62%
copy from protocol/dubbo/server.go
copy to protocol/dubbo/impl/remoting/server_impl.go
index 8de353a..6419ff6 100644
--- a/protocol/dubbo/server.go
+++ b/protocol/dubbo/impl/remoting/server_impl.go
@@ -14,8 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-package dubbo
+package remoting
import (
"fmt"
@@ -23,95 +22,60 @@ import (
)
import (
- "github.com/dubbogo/getty"
- "github.com/dubbogo/gost/sync"
- "gopkg.in/yaml.v2"
+ "github.com/apache/dubbo-go/common"
+ "github.com/apache/dubbo-go/common/logger"
)
import (
- "github.com/apache/dubbo-go/common"
- "github.com/apache/dubbo-go/common/logger"
- "github.com/apache/dubbo-go/config"
+ "github.com/dubbogo/getty"
+ gxsync "github.com/dubbogo/gost/sync"
)
+// TODO: 需要移动到 业务的实现
var (
srvConf *ServerConfig
srvGrpool *gxsync.TaskPool
)
-func init() {
-
- // load clientconfig from provider_config
- // default use dubbo
- providerConfig := config.GetProviderConfig()
- if providerConfig.ApplicationConfig == nil {
- return
- }
- protocolConf := providerConfig.ProtocolConf
- defaultServerConfig := GetDefaultServerConfig()
- if protocolConf == nil {
- logger.Info("protocol_conf default use dubbo config")
- } else {
- dubboConf := protocolConf.(map[interface{}]interface{})[DUBBO]
- if dubboConf == nil {
- logger.Warnf("dubboConf is nil")
- return
- }
-
- dubboConfByte, err := yaml.Marshal(dubboConf)
- if err != nil {
- panic(err)
- }
- err = yaml.Unmarshal(dubboConfByte, &defaultServerConfig)
- if err != nil {
- panic(err)
- }
- }
- srvConf = &defaultServerConfig
- if err := srvConf.CheckValidity(); err != nil {
- panic(err)
- }
- setServerGrpool()
-}
-
-// SetServerConfig set dubbo server config.
+// SetServerConfig ...
func SetServerConfig(s ServerConfig) {
srvConf = &s
err := srvConf.CheckValidity()
if err != nil {
- logger.Warnf("[ServerConfig CheckValidity] error: %v", err)
+ panic(err)
return
}
- setServerGrpool()
+ SetServerGrpool()
}
-// GetServerConfig get dubbo server config.
-func GetServerConfig() ServerConfig {
- return *srvConf
+// GetServerConfig ...
+func GetServerConfig() *ServerConfig {
+ return srvConf
}
-func setServerGrpool() {
+// SetServerGrpool ...
+func SetServerGrpool() {
if srvConf.GrPoolSize > 1 {
srvGrpool = gxsync.NewTaskPool(gxsync.WithTaskPoolTaskPoolSize(srvConf.GrPoolSize), gxsync.WithTaskPoolTaskQueueLength(srvConf.QueueLen),
gxsync.WithTaskPoolTaskQueueNumber(srvConf.QueueNumber))
}
}
-// Server is dubbo protocol server.
+// Server ...
type Server struct {
conf ServerConfig
tcpServer getty.Server
rpcHandler *RpcServerHandler
}
-// NewServer create a new Server.
-func NewServer() *Server {
+// NewServer ...
+func NewServer(handler StubHandler) *Server {
s := &Server{
conf: *srvConf,
}
- s.rpcHandler = NewRpcServerHandler(s.conf.SessionNumber, s.conf.sessionTimeout)
+ s.rpcHandler = NewRpcServerHandler(handler, s.conf.SessionNumber, s.conf.SessionTimeoutD)
return s
}
@@ -134,7 +98,7 @@ func (s *Server) newSession(session getty.Session) error {
tcpConn.SetNoDelay(conf.GettySessionParam.TcpNoDelay)
tcpConn.SetKeepAlive(conf.GettySessionParam.TcpKeepAlive)
if conf.GettySessionParam.TcpKeepAlive {
- tcpConn.SetKeepAlivePeriod(conf.GettySessionParam.keepAlivePeriod)
+ tcpConn.SetKeepAlivePeriod(conf.GettySessionParam.KeepAlivePeriodD)
}
tcpConn.SetReadBuffer(conf.GettySessionParam.TcpRBufSize)
tcpConn.SetWriteBuffer(conf.GettySessionParam.TcpWBufSize)
@@ -144,10 +108,10 @@ func (s *Server) newSession(session getty.Session) error {
session.SetPkgHandler(rpcServerPkgHandler)
session.SetEventListener(s.rpcHandler)
session.SetWQLen(conf.GettySessionParam.PkgWQSize)
- session.SetReadTimeout(conf.GettySessionParam.tcpReadTimeout)
- session.SetWriteTimeout(conf.GettySessionParam.tcpWriteTimeout)
- session.SetCronPeriod((int)(conf.sessionTimeout.Nanoseconds() / 1e6))
- session.SetWaitTime(conf.GettySessionParam.waitTimeout)
+ session.SetReadTimeout(conf.GettySessionParam.TcpReadTimeoutD)
+ session.SetWriteTimeout(conf.GettySessionParam.TcpWriteTimeoutD)
+ session.SetCronPeriod((int)(conf.SessionTimeoutD.Nanoseconds() / 1e6))
+ session.SetWaitTime(conf.GettySessionParam.WaitTimeoutD)
logger.Debugf("app accepts new session:%s\n", session.Stat())
session.SetTaskPool(srvGrpool)
@@ -155,7 +119,7 @@ func (s *Server) newSession(session getty.Session) error {
return nil
}
-// Start start dubbo server.
+// Start ...
func (s *Server) Start(url common.URL) {
var (
addr string
@@ -172,7 +136,7 @@ func (s *Server) Start(url common.URL) {
}
-// Stop stop dubbo server.
+// Stop ...
func (s *Server) Stop() {
s.tcpServer.Close()
}
diff --git a/protocol/dubbo/listener.go b/protocol/dubbo/impl/remoting/server_listener.go
similarity index 60%
rename from protocol/dubbo/listener.go
rename to protocol/dubbo/impl/remoting/server_listener.go
index 4834459..f59e88e 100644
--- a/protocol/dubbo/listener.go
+++ b/protocol/dubbo/impl/remoting/server_listener.go
@@ -15,30 +15,23 @@
* limitations under the License.
*/
-package dubbo
+package remoting
import (
- "context"
- "fmt"
- "net/url"
"sync"
"sync/atomic"
"time"
)
import (
- "github.com/apache/dubbo-go-hessian2"
+ hessian "github.com/apache/dubbo-go-hessian2"
"github.com/dubbogo/getty"
- "github.com/opentracing/opentracing-go"
perrors "github.com/pkg/errors"
)
import (
- "github.com/apache/dubbo-go/common"
- "github.com/apache/dubbo-go/common/constant"
"github.com/apache/dubbo-go/common/logger"
- "github.com/apache/dubbo-go/protocol"
- "github.com/apache/dubbo-go/protocol/invocation"
+ "github.com/apache/dubbo-go/protocol/dubbo/impl"
)
// todo: writePkg_Timeout will entry *.yml
@@ -99,23 +92,16 @@ func (h *RpcClientHandler) OnClose(session getty.Session) {
// OnMessage notified when RPC client session got any message in connection
func (h *RpcClientHandler) OnMessage(session getty.Session, pkg interface{}) {
- p, ok := pkg.(*DubboPackage)
+ p, ok := pkg.(*impl.DubboPackage)
if !ok {
logger.Errorf("illegal package")
return
}
- if p.Header.Type&hessian.PackageHeartbeat != 0x00 {
- if p.Header.Type&hessian.PackageResponse != 0x00 {
- logger.Debugf("get rpc heartbeat response{header: %#v, body: %#v}", p.Header, p.Body)
- if p.Err != nil {
- logger.Errorf("rpc heartbeat response{error: %#v}", p.Err)
- }
- h.conn.pool.rpcClient.removePendingResponse(SequenceType(p.Header.ID))
- } else {
- logger.Debugf("get rpc heartbeat request{header: %#v, service: %#v, body: %#v}", p.Header, p.Service, p.Body)
- p.Header.ResponseStatus = hessian.Response_OK
- reply(session, p, hessian.PackageHeartbeat)
+ if p.Header.Type&impl.PackageHeartbeat != 0x00 {
+ logger.Debugf("get rpc heartbeat response{header: %#v, body: %#v}", p.Header, p.Body)
+ if p.Err != nil {
+ logger.Errorf("rpc heartbeat response{error: %#v}", p.Err)
}
return
}
@@ -123,7 +109,7 @@ func (h *RpcClientHandler) OnMessage(session getty.Session, pkg interface{}) {
h.conn.updateSession(session)
- pendingResponse := h.conn.pool.rpcClient.removePendingResponse(SequenceType(p.Header.ID))
+ pendingResponse := h.conn.pool.rpcClient.removePendingResponse(impl.SequenceType(p.Header.ID))
if pendingResponse == nil {
logger.Errorf("failed to get pending response context for response package %s", *p)
return
@@ -133,27 +119,28 @@ func (h *RpcClientHandler) OnMessage(session getty.Session, pkg interface{}) {
pendingResponse.err = p.Err
}
- pendingResponse.response.atta = p.Body.(*Response).atta
+ pendingResponse.response.Atta = p.Body.(*Response).Atta
if pendingResponse.callback == nil {
pendingResponse.done <- struct{}{}
} else {
+ logger.Info("proxy service callback")
pendingResponse.callback(pendingResponse.GetCallResponse())
}
}
// OnCron notified when RPC client session got any message in cron job
func (h *RpcClientHandler) OnCron(session getty.Session) {
- clientRpcSession, err := h.conn.getClientRpcSession(session)
+ 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() {
+ if h.conn.pool.rpcClient.Conf.SessionTimeoutD.Nanoseconds() < time.Since(session.GetActive()).Nanoseconds() {
logger.Warnf("session{%s} timeout{%s}, reqNum{%d}",
- session.Stat(), time.Since(session.GetActive()).String(), clientRpcSession.reqNum)
- h.conn.removeSession(session) // -> h.conn.close() -> h.conn.pool.remove(h.conn)
+ session.Stat(), time.Since(session.GetActive()).String(), rpcSession.reqNum)
+ h.conn.removeSession(session) // -> h.conn.close() -> h.conn.Pool.remove(h.conn)
return
}
@@ -164,20 +151,31 @@ func (h *RpcClientHandler) OnCron(session getty.Session) {
// RpcServerHandler
// //////////////////////////////////////////
-// RpcServerHandler is handler of RPC Server
+type StubHandler interface {
+ OnPackage(session getty.Session, pkg *impl.DubboPackage)
+}
+
+type StubFunc func(session getty.Session, pkg *impl.DubboPackage)
+
+func (f StubFunc) OnPackage(session getty.Session, pkg *impl.DubboPackage) {
+ f(session, pkg)
+}
+
type RpcServerHandler struct {
maxSessionNum int
sessionTimeout time.Duration
sessionMap map[getty.Session]*rpcSession
rwlock sync.RWMutex
+ stub StubHandler
}
// NewRpcServerHandler creates RpcServerHandler with @maxSessionNum and @sessionTimeout
-func NewRpcServerHandler(maxSessionNum int, sessionTimeout time.Duration) *RpcServerHandler {
+func NewRpcServerHandler(stubHandler StubHandler, maxSessionNum int, sessionTimeout time.Duration) *RpcServerHandler {
return &RpcServerHandler{
maxSessionNum: maxSessionNum,
sessionTimeout: sessionTimeout,
sessionMap: make(map[getty.Session]*rpcSession),
+ stub: stubHandler,
}
}
@@ -224,87 +222,51 @@ func (h *RpcServerHandler) OnMessage(session getty.Session, pkg interface{}) {
}
h.rwlock.Unlock()
- p, ok := pkg.(*DubboPackage)
+ p, ok := pkg.(*impl.DubboPackage)
if !ok {
logger.Errorf("illegal package{%#v}", pkg)
return
}
- p.Header.ResponseStatus = hessian.Response_OK
+ p.SetResponseStatus(hessian.Response_OK)
+ //p.Header.ResponseStatus = hessian.Response_OK
// heartbeat
- if p.Header.Type&hessian.PackageHeartbeat != 0x00 {
- logger.Debugf("get rpc heartbeat request{header: %#v, service: %#v, body: %#v}", p.Header, p.Service, p.Body)
- reply(session, p, hessian.PackageHeartbeat)
+ if p.GetHeader().Type&impl.PackageHeartbeat != 0x00 {
+ logger.Debugf("get rpc heartbeat request{header: %#v, service: %#v, body: %#v}", p.GetHeader(), p.GetService(), p.GetBody())
+ h.reply(session, p, impl.PackageHeartbeat)
return
}
twoway := true
// not twoway
- if p.Header.Type&hessian.PackageRequest_TwoWay == 0x00 {
+ if p.GetHeader().Type&impl.PackageRequest_TwoWay == 0x00 {
twoway = false
}
defer func() {
if e := recover(); e != nil {
- p.Header.ResponseStatus = hessian.Response_SERVER_ERROR
+ p.SetResponseStatus(hessian.Response_SERVER_ERROR)
if err, ok := e.(error); ok {
logger.Errorf("OnMessage panic: %+v", perrors.WithStack(err))
- p.Body = perrors.WithStack(err)
+ p.SetBody(perrors.WithStack(err))
} else if err, ok := e.(string); ok {
logger.Errorf("OnMessage panic: %+v", perrors.New(err))
- p.Body = perrors.New(err)
+ p.SetBody(perrors.New(err))
} else {
logger.Errorf("OnMessage panic: %+v, this is impossible.", e)
- p.Body = e
+ p.SetBody(e)
}
if !twoway {
return
}
- reply(session, p, hessian.PackageResponse)
+ h.reply(session, p, impl.PackageResponse)
}
}()
- u := common.NewURLWithOptions(common.WithPath(p.Service.Path), common.WithParams(url.Values{}),
- common.WithParamsValue(constant.GROUP_KEY, p.Service.Group),
- common.WithParamsValue(constant.INTERFACE_KEY, p.Service.Interface),
- common.WithParamsValue(constant.VERSION_KEY, p.Service.Version))
- exporter, _ := dubboProtocol.ExporterMap().Load(u.ServiceKey())
- if exporter == nil {
- err := fmt.Errorf("don't have this exporter, key: %s", u.ServiceKey())
- logger.Errorf(err.Error())
- p.Header.ResponseStatus = hessian.Response_OK
- p.Body = err
- reply(session, p, hessian.PackageResponse)
- return
- }
- invoker := exporter.(protocol.Exporter).GetInvoker()
- if invoker != nil {
- attachments := p.Body.(map[string]interface{})["attachments"].(map[string]string)
- attachments[constant.LOCAL_ADDR] = session.LocalAddr()
- attachments[constant.REMOTE_ADDR] = session.RemoteAddr()
-
- args := p.Body.(map[string]interface{})["args"].([]interface{})
- inv := invocation.NewRPCInvocation(p.Service.Method, args, attachments)
-
- ctx := rebuildCtx(inv)
-
- result := invoker.Invoke(ctx, inv)
- if err := result.Error(); err != nil {
- p.Header.ResponseStatus = hessian.Response_OK
- p.Body = hessian.NewResponse(nil, err, result.Attachments())
- } else {
- res := result.Result()
- p.Header.ResponseStatus = hessian.Response_OK
- p.Body = hessian.NewResponse(res, nil, result.Attachments())
- }
- }
-
- if !twoway {
- return
- }
- reply(session, p, hessian.PackageResponse)
+ h.stub.OnPackage(session, p)
+ h.reply(session, p, impl.PackageResponse)
}
// OnCron notified when RPC server session got any message in cron job
@@ -333,38 +295,35 @@ func (h *RpcServerHandler) OnCron(session getty.Session) {
}
}
-// rebuildCtx rebuild the context by attachment.
-// Once we decided to transfer more context's key-value, we should change this.
-// now we only support rebuild the tracing context
-func rebuildCtx(inv *invocation.RPCInvocation) context.Context {
- ctx := context.Background()
-
- // actually, if user do not use any opentracing framework, the err will not be nil.
- spanCtx, err := opentracing.GlobalTracer().Extract(opentracing.TextMap,
- opentracing.TextMapCarrier(inv.Attachments()))
- if err == nil {
- ctx = context.WithValue(ctx, constant.TRACING_REMOTE_SPAN_CTX, spanCtx)
+func (h *RpcServerHandler) reply(session getty.Session, req *impl.DubboPackage, tp impl.PackageType) {
+ header := impl.DubboHeader{
+ SerialID: req.GetHeader().SerialID,
+ Type: tp,
+ ID: req.GetHeader().ID,
+ BodyLen: 0,
+ ResponseStatus: req.GetHeader().ResponseStatus,
}
- return ctx
-}
-
-func reply(session getty.Session, req *DubboPackage, tp hessian.PackageType) {
- resp := &DubboPackage{
- Header: hessian.DubboHeader{
- SerialID: req.Header.SerialID,
- Type: tp,
- ID: req.Header.ID,
- ResponseStatus: req.Header.ResponseStatus,
- },
+ resp := NewServerResponsePackage(header)
+ if err := impl.LoadSerializer(resp); err != nil {
+ logger.Errorf("Reply error %v", err)
+ return
}
- if req.Header.Type&hessian.PackageRequest != 0x00 {
- resp.Body = req.Body
- } else {
- resp.Body = nil
+ if req.GetHeader().Type&impl.PackageRequest != 0x00 {
+ resp.SetBody(req.GetBody())
}
if err := session.WritePkg(resp, writePkg_Timeout); err != nil {
- logger.Errorf("WritePkg error: %#v, %#v", perrors.WithStack(err), req.Header)
+ logger.Errorf("WritePkg error: %#v, %#v", perrors.WithStack(err), req.GetHeader())
+ }
+}
+
+// server side response package, just for serialization
+func NewServerResponsePackage(header impl.DubboHeader) *impl.DubboPackage {
+ return &impl.DubboPackage{
+ Header: header,
+ Body: nil,
+ Err: nil,
+ Codec: impl.NewDubboCodec(nil),
}
}
diff --git a/protocol/dubbo/impl/request.go b/protocol/dubbo/impl/request.go
new file mode 100644
index 0000000..0e770c3
--- /dev/null
+++ b/protocol/dubbo/impl/request.go
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package impl
+
+type RequestPayload struct {
+ Params interface{}
+ Attachments map[string]string
+}
+
+func NewRequestPayload(args interface{}, atta map[string]string) *RequestPayload {
+ if atta == nil {
+ atta = make(map[string]string)
+ }
+ return &RequestPayload{
+ Params: args,
+ Attachments: atta,
+ }
+}
+
+func EnsureRequestPayload(body interface{}) *RequestPayload {
+ if req, ok := body.(*RequestPayload); ok {
+ return req
+ }
+ return NewRequestPayload(body, nil)
+}
diff --git a/protocol/dubbo/impl/response.go b/protocol/dubbo/impl/response.go
new file mode 100644
index 0000000..ea0a6ef
--- /dev/null
+++ b/protocol/dubbo/impl/response.go
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package impl
+
+type ResponsePayload struct {
+ RspObj interface{}
+ Exception error
+ Attachments map[string]string
+}
+
+// NewResponse create a new ResponsePayload
+func NewResponsePayload(rspObj interface{}, exception error, attachments map[string]string) *ResponsePayload {
+ if attachments == nil {
+ attachments = make(map[string]string)
+ }
+ return &ResponsePayload{
+ RspObj: rspObj,
+ Exception: exception,
+ Attachments: attachments,
+ }
+}
+
+func EnsureResponsePayload(body interface{}) *ResponsePayload {
+ if res, ok := body.(*ResponsePayload); ok {
+ return res
+ }
+ if exp, ok := body.(error); ok {
+ return NewResponsePayload(nil, exp, nil)
+ }
+ return NewResponsePayload(body, nil, nil)
+}
diff --git a/protocol/dubbo/impl/serialization.go b/protocol/dubbo/impl/serialization.go
new file mode 100644
index 0000000..7ce76a8
--- /dev/null
+++ b/protocol/dubbo/impl/serialization.go
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package impl
+
+import (
+ "fmt"
+)
+
+import (
+ "github.com/apache/dubbo-go/common/constant"
+)
+
+var (
+ serializers = make(map[string]interface{})
+ nameMaps = make(map[byte]string)
+)
+
+func init() {
+ nameMaps = map[byte]string{
+ constant.S_Hessian2: constant.HESSIAN2_SERIALIZATION,
+ constant.S_Proto: constant.PROTOBUF_SERIALIZATION,
+ }
+}
+
+func SetSerializer(name string, serializer interface{}) {
+ serializers[name] = serializer
+}
+
+func GetSerializerById(id byte) (interface{}, error) {
+ name, ok := nameMaps[id]
+ if !ok {
+ panic(fmt.Sprintf("serialId %d not found", id))
+ }
+ serializer, ok := serializers[name]
+ if !ok {
+ panic(fmt.Sprintf("serialization %s not found", name))
+ }
+ return serializer, nil
+}
diff --git a/protocol/dubbo/impl/serialize.go b/protocol/dubbo/impl/serialize.go
new file mode 100644
index 0000000..1f913f7
--- /dev/null
+++ b/protocol/dubbo/impl/serialize.go
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package impl
+
+import (
+ "github.com/apache/dubbo-go/common/constant"
+)
+
+type Serializer interface {
+ Marshal(p DubboPackage) ([]byte, error)
+ Unmarshal([]byte, *DubboPackage) error
+}
+
+func LoadSerializer(p *DubboPackage) error {
+ // NOTE: default serialID is S_Hessian
+ serialID := p.Header.SerialID
+ if serialID == 0 {
+ serialID = constant.S_Hessian2
+ }
+ serializer, err := GetSerializerById(serialID)
+ if err != nil {
+ panic(err)
+ }
+ p.SetSerializer(serializer.(Serializer))
+ return nil
+}
diff --git a/protocol/dubbo/server.go b/protocol/dubbo/server.go
index 8de353a..134a58c 100644
--- a/protocol/dubbo/server.go
+++ b/protocol/dubbo/server.go
@@ -18,25 +18,25 @@
package dubbo
import (
+ "context"
"fmt"
- "net"
+ "net/url"
)
-
import (
- "github.com/dubbogo/getty"
- "github.com/dubbogo/gost/sync"
+ "github.com/opentracing/opentracing-go"
"gopkg.in/yaml.v2"
)
import (
"github.com/apache/dubbo-go/common"
+ "github.com/apache/dubbo-go/common/constant"
"github.com/apache/dubbo-go/common/logger"
"github.com/apache/dubbo-go/config"
-)
-
-var (
- srvConf *ServerConfig
- srvGrpool *gxsync.TaskPool
+ "github.com/apache/dubbo-go/protocol"
+ "github.com/apache/dubbo-go/protocol/dubbo/impl"
+ "github.com/apache/dubbo-go/protocol/dubbo/impl/remoting"
+ "github.com/apache/dubbo-go/protocol/invocation"
+ "github.com/dubbogo/getty"
)
func init() {
@@ -48,7 +48,7 @@ func init() {
return
}
protocolConf := providerConfig.ProtocolConf
- defaultServerConfig := GetDefaultServerConfig()
+ defaultServerConfig := remoting.GetDefaultServerConfig()
if protocolConf == nil {
logger.Info("protocol_conf default use dubbo config")
} else {
@@ -67,112 +67,59 @@ func init() {
panic(err)
}
}
- srvConf = &defaultServerConfig
- if err := srvConf.CheckValidity(); err != nil {
- panic(err)
- }
- setServerGrpool()
-}
-
-// SetServerConfig set dubbo server config.
-func SetServerConfig(s ServerConfig) {
- srvConf = &s
- err := srvConf.CheckValidity()
- if err != nil {
- logger.Warnf("[ServerConfig CheckValidity] error: %v", err)
- return
- }
- setServerGrpool()
-}
-
-// GetServerConfig get dubbo server config.
-func GetServerConfig() ServerConfig {
- return *srvConf
-}
-
-func setServerGrpool() {
- if srvConf.GrPoolSize > 1 {
- srvGrpool = gxsync.NewTaskPool(gxsync.WithTaskPoolTaskPoolSize(srvConf.GrPoolSize), gxsync.WithTaskPoolTaskQueueLength(srvConf.QueueLen),
- gxsync.WithTaskPoolTaskQueueNumber(srvConf.QueueNumber))
- }
-}
-
-// Server is dubbo protocol server.
-type Server struct {
- conf ServerConfig
- tcpServer getty.Server
- rpcHandler *RpcServerHandler
-}
-
-// NewServer create a new Server.
-func NewServer() *Server {
-
- s := &Server{
- conf: *srvConf,
- }
-
- s.rpcHandler = NewRpcServerHandler(s.conf.SessionNumber, s.conf.sessionTimeout)
-
- return s
+ remoting.SetServerConfig(defaultServerConfig)
}
-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)
+// rebuildCtx rebuild the context by attachment.
+// Once we decided to transfer more context's key-value, we should change this.
+// now we only support rebuild the tracing context
+func rebuildCtx(inv *invocation.RPCInvocation) context.Context {
+ ctx := context.Background()
+
+ // actually, if user do not use any opentracing framework, the err will not be nil.
+ spanCtx, err := opentracing.GlobalTracer().Extract(opentracing.TextMap,
+ opentracing.TextMapCarrier(inv.Attachments()))
+ if err == nil {
+ ctx = context.WithValue(ctx, constant.TRACING_REMOTE_SPAN_CTX, spanCtx)
}
-
- 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(rpcServerPkgHandler)
- session.SetEventListener(s.rpcHandler)
- session.SetWQLen(conf.GettySessionParam.PkgWQSize)
- session.SetReadTimeout(conf.GettySessionParam.tcpReadTimeout)
- session.SetWriteTimeout(conf.GettySessionParam.tcpWriteTimeout)
- session.SetCronPeriod((int)(conf.sessionTimeout.Nanoseconds() / 1e6))
- session.SetWaitTime(conf.GettySessionParam.waitTimeout)
- logger.Debugf("app accepts new session:%s\n", session.Stat())
-
- session.SetTaskPool(srvGrpool)
-
- return nil
+ return ctx
}
-// Start start dubbo server.
-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
-
-}
-
-// Stop stop dubbo server.
-func (s *Server) Stop() {
- s.tcpServer.Close()
+func NewStubHandler() remoting.StubHandler {
+ return remoting.StubFunc(func(session getty.Session, p *impl.DubboPackage) {
+ u := common.NewURLWithOptions(common.WithPath(p.GetService().Path), common.WithParams(url.Values{}),
+ common.WithParamsValue(constant.GROUP_KEY, p.GetService().Group),
+ common.WithParamsValue(constant.INTERFACE_KEY, p.GetService().Interface),
+ common.WithParamsValue(constant.VERSION_KEY, p.GetService().Version))
+
+ exporter, _ := dubboProtocol.ExporterMap().Load(u.ServiceKey())
+ if exporter == nil {
+ err := fmt.Errorf("don't have this exporter, key: %s", u.ServiceKey())
+ logger.Errorf(err.Error())
+ p.SetResponseStatus(impl.Response_OK)
+ p.SetBody(err)
+ return
+ }
+ invoker := exporter.(protocol.Exporter).GetInvoker()
+ if invoker != nil {
+ attachments := p.GetBody().(map[string]interface{})["attachments"].(map[string]string)
+ attachments[constant.LOCAL_ADDR] = session.LocalAddr()
+ attachments[constant.REMOTE_ADDR] = session.RemoteAddr()
+
+ args := p.GetBody().(map[string]interface{})["args"].([]interface{})
+ inv := invocation.NewRPCInvocation(p.GetService().Method, args, attachments)
+
+ ctx := rebuildCtx(inv)
+ result := invoker.Invoke(ctx, inv)
+ logger.Debugf("invoker result: %+v", result)
+ if err := result.Error(); err != nil {
+ p.SetResponseStatus(impl.Response_OK)
+ p.SetBody(&impl.ResponsePayload{nil, err, result.Attachments()})
+ } else {
+ res := result.Result()
+ p.SetResponseStatus(impl.Response_OK)
+ p.SetBody(&impl.ResponsePayload{res, nil, result.Attachments()})
+ }
+ }
+ })
}
diff --git a/protocol/dubbo/listener_test.go b/protocol/dubbo/server_test.go
similarity index 100%
rename from protocol/dubbo/listener_test.go
rename to protocol/dubbo/server_test.go
index 5f80981..aa7d750 100644
--- a/protocol/dubbo/listener_test.go
+++ b/protocol/dubbo/server_test.go
@@ -22,14 +22,14 @@ import (
)
import (
- "github.com/opentracing/opentracing-go"
- "github.com/opentracing/opentracing-go/mocktracer"
"github.com/stretchr/testify/assert"
)
import (
"github.com/apache/dubbo-go/common/constant"
"github.com/apache/dubbo-go/protocol/invocation"
+ "github.com/opentracing/opentracing-go"
+ "github.com/opentracing/opentracing-go/mocktracer"
)
// test rebuild the ctx
diff --git a/test/integrate/dubbo/go-server/server.go b/test/integrate/dubbo/go-server/server.go
index 115bf0a..a5d18db 100644
--- a/test/integrate/dubbo/go-server/server.go
+++ b/test/integrate/dubbo/go-server/server.go
@@ -48,7 +48,7 @@ func main() {
select {
case <-stopC:
// wait getty send resp to consumer
- time.Sleep(3*time.Second)
+ time.Sleep(3 * time.Second)
return
case <-time.After(time.Minute):
panic("provider already running 1 min, but can't be call by consumer")
[dubbo-go] 04/04: Mrg: merge develop
Posted by fa...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
fangyc pushed a commit to branch refact-seri
in repository https://gitbox.apache.org/repos/asf/dubbo-go.git
commit 14564b90bdff5bd7c71710cf4b3d40b3bc2a89e1
Merge: 9b260cf 053aab5
Author: fangyincheng <fa...@sina.com>
AuthorDate: Sun Jul 26 16:26:13 2020 +0800
Mrg: merge develop
.gitignore | 10 +-
CHANGE.md | 50 ++
NOTICE | 2 +-
README.md | 36 +-
README_CN.md | 35 +-
before_ut.bat | 5 +-
before_ut.sh | 9 +-
cluster/cluster_impl/available_cluster.go | 1 +
cluster/cluster_impl/base_cluster_invoker_test.go | 15 +-
cluster/cluster_impl/broadcast_cluster.go | 1 +
cluster/cluster_impl/broadcast_cluster_invoker.go | 1 +
cluster/cluster_impl/failback_cluster.go | 1 +
cluster/cluster_impl/failback_cluster_invoker.go | 52 +-
cluster/cluster_impl/failfast_cluster.go | 1 +
cluster/cluster_impl/failfast_cluster_invoker.go | 1 +
cluster/cluster_impl/failover_cluster.go | 1 +
cluster/cluster_impl/failover_cluster_invoker.go | 8 +-
cluster/cluster_impl/failover_cluster_test.go | 15 +
cluster/cluster_impl/failsafe_cluster.go | 1 +
cluster/cluster_impl/failsafe_cluster_invoker.go | 1 +
cluster/cluster_impl/forking_cluster.go | 1 +
cluster/cluster_impl/forking_cluster_invoker.go | 2 +-
cluster/cluster_impl/mock_cluster.go | 1 +
cluster/cluster_impl/registry_aware_cluster.go | 1 +
.../cluster_impl/registry_aware_cluster_invoker.go | 1 +
cluster/directory/base_directory.go | 14 -
cluster/loadbalance/consistent_hash.go | 5 +-
cluster/loadbalance/consistent_hash_test.go | 33 +-
cluster/loadbalance/least_active.go | 1 +
cluster/loadbalance/random_test.go | 22 +-
cluster/loadbalance/round_robin.go | 7 +-
cluster/router/chain/chain_test.go | 88 +--
cluster/router/condition/app_router_test.go | 45 +-
cluster/router/condition/factory_test.go | 113 ++--
cluster/router/condition/file.go | 8 +-
cluster/router/condition/router.go | 4 +-
.../healthcheck/default_health_check_test.go | 65 +-
cluster/router/healthcheck/factory_test.go | 9 +
.../router/healthcheck/health_check_route_test.go | 40 +-
cluster/router/tag/factory_test.go | 10 +-
cluster/router/tag/file_test.go | 18 +-
cluster/router/tag/tag_router.go | 8 +
cluster/router/tag/tag_router_test.go | 59 +-
common/constant/default.go | 9 +
common/constant/env.go | 1 +
common/constant/key.go | 37 +-
common/extension/event_dispatcher.go | 77 +++
common/extension/event_dispatcher_test.go | 111 ++++
common/extension/metadata_report_factory.go | 8 +-
common/extension/metadata_service.go | 47 ++
common/extension/metadata_service_proxy_factory.go | 48 ++
common/extension/service_discovery.go | 19 +-
...discovery.go => service_instance_customizer.go} | 39 +-
...ery.go => service_instance_selector_factory.go} | 25 +-
.../extension/service_name_mapping.go | 16 +-
.../observer/dispatcher/direct_event_dispatcher.go | 69 ++
.../dispatcher/direct_event_dispatcher_test.go | 77 +++
.../observer/dispatcher/mock_event_dispatcher.go | 58 ++
common/observer/event.go | 68 ++
.../observer/event_dispatcher.go | 19 +-
{registry => common/observer}/event_listener.go | 17 +-
common/observer/listenable.go | 142 ++++
.../observer/listenable_test.go | 52 +-
common/proxy/proxy.go | 18 +-
common/rpc_service.go | 17 +-
common/url.go | 27 +-
config/application_config.go | 3 +-
config/base_config.go | 142 ++--
config/base_config_test.go | 424 ++++--------
config/config_loader.go | 111 +++-
config/config_loader_test.go | 77 ++-
config/config_utils.go | 54 +-
config/config_utils_test.go | 20 +
config/consumer_config.go | 8 +-
config/graceful_shutdown_config_test.go | 2 +-
config/graceful_shutdown_signal_darwin.go | 2 +-
config/graceful_shutdown_signal_linux.go | 2 +-
config/graceful_shutdown_signal_windows.go | 2 +-
config/instance/metadata_report.go | 29 +-
config/instance/metadata_report_test.go | 85 +++
config/metadata_report_config.go | 39 +-
config/metadata_report_config_test.go | 7 +-
config/method_config.go | 4 +-
config/provider_config.go | 37 +-
config/reference_config.go | 6 +-
config/reference_config_test.go | 67 +-
config/registry_config_test.go | 4 +-
config/remote_config.go | 58 ++
...{config_utils_test.go => remote_config_test.go} | 25 +-
config/router_config.go | 12 +-
config/router_config_test.go | 5 +-
config/service_config.go | 72 ++-
config/service_config_test.go | 144 ++---
...g_utils_test.go => service_discovery_config.go} | 33 +-
config_center/apollo/factory.go | 1 +
config_center/apollo/listener.go | 2 +-
config_center/mock_dynamic_config.go | 8 +-
config_center/nacos/client.go | 95 ++-
config_center/nacos/client_test.go | 4 +-
config_center/nacos/impl.go | 30 +-
config_center/nacos/impl_test.go | 30 +-
config_center/nacos/listener.go | 4 +-
config_center/parser/configuration_parser.go | 17 +-
config_center/zookeeper/listener.go | 2 +-
filter/filter.go | 2 +-
filter/filter_impl/access_log_filter.go | 12 +-
filter/filter_impl/access_log_filter_test.go | 4 +-
filter/filter_impl/active_filter_test.go | 4 +-
filter/filter_impl/echo_filter.go | 2 +-
filter/filter_impl/echo_filter_test.go | 2 +-
filter/filter_impl/execute_limit_filter.go | 2 +-
filter/filter_impl/execute_limit_filter_test.go | 6 +-
filter/filter_impl/generic_filter.go | 2 +-
filter/filter_impl/generic_filter_test.go | 6 +-
filter/filter_impl/generic_service_filter.go | 12 +-
filter/filter_impl/generic_service_filter_test.go | 10 +-
.../filter_impl/graceful_shutdown_filter_test.go | 2 +-
filter/filter_impl/hystrix_filter.go | 16 +-
filter/filter_impl/hystrix_filter_test.go | 14 +-
filter/filter_impl/metrics_filter_test.go | 2 +-
.../{token_filter.go => seata_filter.go} | 44 +-
.../{echo_filter_test.go => seata_filter_test.go} | 28 +-
filter/filter_impl/token_filter.go | 4 +-
filter/filter_impl/token_filter_test.go | 8 +-
.../tps/tps_limit_fix_window_strategy.go | 2 +-
.../tps/tps_limit_fix_window_strategy_test.go | 2 +-
.../tps/tps_limit_sliding_window_strategy_test.go | 2 +-
filter/filter_impl/tps/tps_limit_strategy_mock.go | 3 +
...s_limit_thread_safe_fix_window_strategy_test.go | 2 +-
.../tps/tps_limiter_method_service_test.go | 8 +-
filter/filter_impl/tps_limit_filter_test.go | 6 +-
filter/filter_impl/tracing_filter_test.go | 7 +-
go.mod | 22 +-
go.sum | 51 +-
metadata/definition/definition.go | 96 +++
metadata/definition/definition_test.go | 52 ++
metadata/{report_factory.go => definition/mock.go} | 30 +-
metadata/identifier/base_metadata_identifier.go | 42 +-
.../identifier/base_metadata_identifier_test.go | 26 +-
metadata/identifier/metadata_identifier.go | 10 +-
.../identifier/metadata_identifier_test.go | 29 +-
metadata/identifier/service_metadata_identifier.go | 28 +-
.../identifier/service_metadata_identifier_test.go | 30 +-
.../identifier/subscribe_metadata_identifier.go | 12 +-
.../subscribe_metadata_identifier_test.go | 31 +-
.../dynamic/service_name_mapping.go | 31 +-
.../dynamic/service_name_mapping_test.go | 4 +-
.../memory/service_name_mapping.go | 25 +-
metadata/{ => mapping}/service_name_mapping.go | 2 +-
metadata/report.go | 35 -
metadata/report/consul/report.go | 125 ++++
metadata/report/consul/report_test.go | 165 +++++
metadata/report/delegate/delegate_report.go | 287 +++++++++
metadata/report/delegate/delegate_report_test.go | 123 ++++
metadata/report/etcd/report.go | 142 ++++
metadata/report/etcd/report_test.go | 133 ++++
metadata/{ => report/factory}/report_factory.go | 13 +-
metadata/report/nacos/report.go | 187 ++++++
metadata/report/nacos/report_test.go | 116 ++++
metadata/report/report.go | 60 ++
metadata/report/zookeeper/report.go | 133 ++++
metadata/report/zookeeper/report_test.go | 166 +++++
metadata/service.go | 40 --
metadata/service/exporter/configurable/exporter.go | 106 +++
.../service/exporter/configurable/exporter_test.go | 122 ++++
metadata/{ => service/exporter}/exporter.go | 9 +-
.../inmemory/metadata_service_proxy_factory.go | 97 +++
.../metadata_service_proxy_factory_test.go | 100 +++
metadata/service/inmemory/service.go | 249 +++++++
metadata/service/inmemory/service_proxy.go | 139 ++++
metadata/service/inmemory/service_proxy_test.go | 82 +++
metadata/service/inmemory/service_test.go | 95 +++
.../remote/metadata_service_proxy_factory.go} | 16 +-
metadata/service/remote/service.go | 208 ++++++
metadata/service/remote/service_proxy.go | 162 +++++
metadata/service/remote/service_proxy_test.go | 135 ++++
metadata/service/remote/service_test.go | 143 +++++
metadata/service/service.go | 135 ++++
protocol/dubbo/dubbo_invoker_test.go | 2 +-
protocol/dubbo/dubbo_protocol_test.go | 10 +-
protocol/grpc/common_test.go | 2 +-
protocol/grpc/grpc_exporter.go | 2 +-
protocol/grpc/grpc_invoker.go | 12 +-
protocol/grpc/grpc_invoker_test.go | 10 +-
protocol/grpc/grpc_protocol_test.go | 8 +-
protocol/invocation/rpcinvocation.go | 2 +
protocol/jsonrpc/http.go | 2 +-
protocol/jsonrpc/http_test.go | 16 +-
protocol/jsonrpc/json.go | 14 +-
protocol/jsonrpc/json_test.go | 8 +-
protocol/jsonrpc/jsonrpc_invoker_test.go | 2 +-
protocol/jsonrpc/jsonrpc_protocol_test.go | 4 +-
protocol/protocolwrapper/mock_protocol_filter.go | 2 +-
.../protocolwrapper/protocol_filter_wrapper.go | 12 +-
.../protocol_filter_wrapper_test.go | 4 +-
protocol/rest/client/client_impl/resty_client.go | 2 +-
.../rest/config/reader/rest_config_reader_test.go | 4 +-
protocol/rest/config/rest_config.go | 28 +-
protocol/rest/rest_exporter.go | 3 +
protocol/rest/rest_invoker.go | 5 +
protocol/rest/rest_invoker_test.go | 16 +-
protocol/rest/rest_protocol.go | 9 +
protocol/rest/rest_protocol_test.go | 16 +-
protocol/rpc_status.go | 4 +-
protocol/rpc_status_test.go | 20 +-
registry/base_configuration_listener.go | 7 +-
registry/base_registry.go | 136 +++-
registry/consul/registry.go | 22 +-
registry/consul/registry_test.go | 2 +-
registry/consul/utils_test.go | 55 +-
registry/directory/directory.go | 16 +-
registry/directory/directory_test.go | 8 +-
registry/etcdv3/registry.go | 9 +
registry/etcdv3/service_discovery.go | 322 ++++++++++
registry/etcdv3/service_discovery_test.go | 80 +++
registry/event.go | 47 +-
.../customizable_service_instance_listener.go | 73 +++
.../customizable_service_instance_listener_test.go | 73 ++-
.../event_publishing_service_deiscovery_test.go | 190 ++++++
.../event/event_publishing_service_discovery.go | 157 +++++
.../event/log_event_listener.go | 43 +-
.../event/log_event_listener_test.go | 14 +-
.../metadata_service_url_params_customizer.go | 105 +++
.../metadata_service_url_params_customizer_test.go | 124 ++++
.../event/protocol_ports_metadata_customizer.go | 106 +++
.../event/service_config_exported_event.go | 24 +-
registry/event/service_discovery_event.go | 103 +++
registry/event/service_instance_event.go | 87 +++
registry/event/service_name_mapping_listener.go | 89 +++
registry/event/service_revision_customizer.go | 137 ++++
registry/event_listener.go | 45 +-
registry/kubernetes/registry.go | 9 +
registry/mock_registry.go | 13 +-
registry/nacos/registry.go | 99 ++-
registry/nacos/service_discovery.go | 89 ++-
registry/nacos/service_discovery_test.go | 81 ++-
registry/protocol/protocol_test.go | 9 +-
registry/registry.go | 17 +-
registry/service_discovery.go | 2 +
registry/service_instance.go | 18 +-
.../random/random_service_instance_selector.go | 36 +-
.../random_service_instance_selector_test.go | 57 +-
.../instance/service_instance_selector.go | 11 +-
.../servicediscovery/service_discovery_registry.go | 713 +++++++++++++++++++++
.../service_discovery_registry_test.go | 246 +++++++
.../rest/rest_subscribed_urls_synthesizer.go | 65 ++
.../rest/rest_subscribed_urls_synthesizer_test.go | 75 +++
.../synthesizer/subscribed_urls_synthesizer.go | 15 +-
.../subscribed_urls_synthesizer_factory.go | 21 +-
registry/zookeeper/listener.go | 43 +-
registry/zookeeper/registry.go | 67 +-
registry/zookeeper/registry_test.go | 58 ++
registry/zookeeper/service_discovery.go | 351 ++++++++++
registry/zookeeper/service_discovery_test.go | 197 ++++++
.../consul/test_agent.go | 50 +-
.../consul/test_agent_test.go | 14 +-
remoting/etcdv3/client.go | 71 +-
remoting/etcdv3/client_test.go | 8 +-
remoting/etcdv3/facade.go | 6 +-
remoting/etcdv3/listener.go | 6 +-
remoting/kubernetes/client_test.go | 15 +-
remoting/kubernetes/facade_test.go | 1 +
remoting/listener.go | 2 +
.../base_registry.go => remoting/nacos/builder.go | 66 +-
.../nacos/builder_test.go | 29 +-
remoting/zookeeper/client.go | 194 +++---
remoting/zookeeper/client_test.go | 32 +-
.../curator_discovery/service_discovery.go | 279 ++++++++
.../curator_discovery/service_instance.go | 20 +-
remoting/zookeeper/facade.go | 16 +-
remoting/zookeeper/facade_test.go | 2 +-
remoting/zookeeper/listener.go | 55 +-
remoting/zookeeper/listener_test.go | 10 +-
273 files changed, 11086 insertions(+), 2102 deletions(-)
diff --cc .gitignore
index 568e9f2,fabff68..8158b49
--- a/.gitignore
+++ b/.gitignore
@@@ -30,5 -27,10 +27,12 @@@ logs
remoting/zookeeper/zookeeper-4unittest/
config_center/zookeeper/zookeeper-4unittest/
registry/zookeeper/zookeeper-4unittest/
+ metadata/report/zookeeper/zookeeper-4unittest/
registry/consul/agent*
++metadata/report/consul/agent*
++remoting/consul/agent*
config_center/apollo/mockDubbog.properties.json
+
+ # vim stuff
+ *~
+ .*.sw?
diff --cc common/constant/key.go
index 06b37cf,cd23dd0..c3fe0e7
--- a/common/constant/key.go
+++ b/common/constant/key.go
@@@ -22,27 -22,30 +22,31 @@@ const
)
const (
- PORT_KEY = "port"
- GROUP_KEY = "group"
- VERSION_KEY = "version"
- INTERFACE_KEY = "interface"
- PATH_KEY = "path"
- PROTOCOL_KEY = "protocol"
- SERVICE_KEY = "service"
- METHODS_KEY = "methods"
- TIMEOUT_KEY = "timeout"
- CATEGORY_KEY = "category"
- CHECK_KEY = "check"
- ENABLED_KEY = "enabled"
- SIDE_KEY = "side"
- OVERRIDE_PROVIDERS_KEY = "providerAddresses"
- BEAN_NAME_KEY = "bean.name"
- GENERIC_KEY = "generic"
- CLASSIFIER_KEY = "classifier"
- TOKEN_KEY = "token"
- LOCAL_ADDR = "local-addr"
- REMOTE_ADDR = "remote-addr"
- PATH_SEPARATOR = "/"
- DUBBO_KEY = "dubbo"
- RELEASE_KEY = "release"
- ANYHOST_KEY = "anyhost"
+ GROUP_KEY = "group"
+ VERSION_KEY = "version"
+ INTERFACE_KEY = "interface"
+ PATH_KEY = "path"
+ SERVICE_KEY = "service"
+ METHODS_KEY = "methods"
+ TIMEOUT_KEY = "timeout"
+ CATEGORY_KEY = "category"
+ CHECK_KEY = "check"
+ ENABLED_KEY = "enabled"
+ SIDE_KEY = "side"
+ OVERRIDE_PROVIDERS_KEY = "providerAddresses"
+ BEAN_NAME_KEY = "bean.name"
+ GENERIC_KEY = "generic"
+ CLASSIFIER_KEY = "classifier"
+ TOKEN_KEY = "token"
+ LOCAL_ADDR = "local-addr"
+ REMOTE_ADDR = "remote-addr"
+ DEFAULT_REMOTING_TIMEOUT = 3000
+ RELEASE_KEY = "release"
+ ANYHOST_KEY = "anyhost"
++ PORT_KEY = "port"
++ PROTOCOL_KEY = "protocol"
++ PATH_SEPARATOR = "/"
++ DUBBO_KEY = "dubbo"
)
const (
@@@ -77,7 -80,11 +81,12 @@@ const
EXECUTE_REJECTED_EXECUTION_HANDLER_KEY = "execute.limit.rejected.handler"
PROVIDER_SHUTDOWN_FILTER = "pshutdown"
CONSUMER_SHUTDOWN_FILTER = "cshutdown"
+ SERIALIZATION_KEY = "serialization"
+ PID_KEY = "pid"
+ SYNC_REPORT_KEY = "sync.report"
+ RETRY_PERIOD_KEY = "retry.period"
+ RETRY_TIMES_KEY = "retry.times"
+ CYCLE_REPORT_KEY = "cycle.report"
)
const (
diff --cc common/url.go
index 5a3e69f,807d0ed..7c2c39c
--- a/common/url.go
+++ b/common/url.go
@@@ -656,28 -658,21 +662,47 @@@ func mergeNormalParam(mergedUrl *URL, r
return methodConfigMergeFcn
}
+// doesn't encode url reserve character, url.QueryEscape will do this work
+// reference: https://github.com/golang/go.git, src/net/url/url.go, Encode method
+func ParamsUnescapeEncode(params url.Values) string {
+ if params == nil {
+ return ""
+ }
+ var buf strings.Builder
+ keys := make([]string, len(params))
+ for k := range params {
+ keys = append(keys, k)
+ }
+ sort.Strings(keys)
+ for _, k := range keys {
+ vs := params[k]
+ for _, v := range vs {
+ if buf.Len() > 0 {
+ buf.WriteByte('&')
+ }
+ buf.WriteString(k)
+ buf.WriteByte('=')
+ buf.WriteString(v)
+ }
+ }
+ return buf.String()
+}
++
+ // URLSlice will be used to sort URL instance
+ // Instances will be order by URL.String()
+ type URLSlice []URL
+
+ // nolint
+ func (s URLSlice) Len() int {
+ return len(s)
+ }
+
+ // nolint
+ func (s URLSlice) Less(i, j int) bool {
+ return s[i].String() < s[j].String()
+ }
+
+ // nolint
+ func (s URLSlice) Swap(i, j int) {
+ s[i], s[j] = s[j], s[i]
+ }
diff --cc go.mod
index 7e4b8d9,a16b906..44f73d3
--- a/go.mod
+++ b/go.mod
@@@ -29,19 -26,14 +26,15 @@@ require
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645
github.com/hashicorp/consul v1.5.3
github.com/hashicorp/consul/api v1.1.0
- github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 // indirect
+ github.com/hashicorp/vault v0.10.3
github.com/jinzhu/copier v0.0.0-20190625015134-976e0346caa8
- github.com/jonboulle/clockwork v0.1.0 // indirect
github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8 // indirect
github.com/juju/testing v0.0.0-20191001232224-ce9dec17d28b // indirect
- github.com/lestrrat/go-envload v0.0.0-20180220120943-6ed08b54a570 // indirect
- github.com/lestrrat/go-file-rotatelogs v0.0.0-20180223000712-d3151e2a480f // indirect
- github.com/lestrrat/go-strftime v0.0.0-20180220042222-ba3bf9c1d042 // indirect
github.com/magiconair/properties v1.8.1
+ github.com/matttproud/golang_protobuf_extensions v1.0.1
github.com/mitchellh/mapstructure v1.1.2
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd
- github.com/nacos-group/nacos-sdk-go v0.0.0-20191128082542-fe1b325b125c
+ github.com/nacos-group/nacos-sdk-go v0.3.3-0.20200617023039-50c7537d6a5f
github.com/opentracing/opentracing-go v1.1.0
github.com/pkg/errors v0.9.1
github.com/prometheus/client_golang v1.1.0
diff --cc metadata/service/exporter/configurable/exporter_test.go
index 0000000,9fdbd76..b85e0ac
mode 000000,100644..100644
--- a/metadata/service/exporter/configurable/exporter_test.go
+++ b/metadata/service/exporter/configurable/exporter_test.go
@@@ -1,0 -1,122 +1,122 @@@
+ /*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ package configurable
+
+ import (
++ "github.com/apache/dubbo-go/remoting/getty"
+ "testing"
+ )
+
+ import (
+ "github.com/stretchr/testify/assert"
+ )
+
+ import (
+ _ "github.com/apache/dubbo-go/common/proxy/proxy_factory"
+ "github.com/apache/dubbo-go/config"
+ _ "github.com/apache/dubbo-go/filter/filter_impl"
+ "github.com/apache/dubbo-go/metadata/service/inmemory"
- "github.com/apache/dubbo-go/protocol/dubbo"
+ _ "github.com/apache/dubbo-go/protocol/dubbo"
+ )
+
+ func TestConfigurableExporter(t *testing.T) {
- dubbo.SetServerConfig(dubbo.ServerConfig{
++ getty.SetServerConfig(getty.ServerConfig{
+ SessionNumber: 700,
+ SessionTimeout: "20s",
- GettySessionParam: dubbo.GettySessionParam{
++ GettySessionParam: getty.GettySessionParam{
+ CompressEncoding: false,
+ TcpNoDelay: true,
+ TcpKeepAlive: true,
+ KeepAlivePeriod: "120s",
+ TcpRBufSize: 262144,
+ TcpWBufSize: 65536,
+ PkgWQSize: 512,
+ TcpReadTimeout: "1s",
+ TcpWriteTimeout: "5s",
+ WaitTimeout: "1s",
+ MaxMsgLen: 10240000000,
+ SessionName: "server",
+ }})
+ mockInitProviderWithSingleRegistry()
+ metadataService, _ := inmemory.NewMetadataService()
+ exported := NewMetadataServiceExporter(metadataService)
+ assert.Equal(t, false, exported.IsExported())
+ assert.NoError(t, exported.Export())
+ assert.Equal(t, true, exported.IsExported())
+ assert.Regexp(t, "dubbo://:20000/MetadataService*", exported.GetExportedURLs()[0].String())
+ exported.Unexport()
+ assert.Equal(t, false, exported.IsExported())
+ }
+
+ // mockInitProviderWithSingleRegistry will init a mocked providerConfig
+ func mockInitProviderWithSingleRegistry() {
+ providerConfig := &config.ProviderConfig{
+
+ BaseConfig: config.BaseConfig{
+ ApplicationConfig: &config.ApplicationConfig{
+ Organization: "dubbo_org",
+ Name: "dubbo",
+ Module: "module",
+ Version: "1.0.0",
+ Owner: "dubbo",
+ Environment: "test"},
+ },
+
+ Registry: &config.RegistryConfig{
+ Address: "mock://127.0.0.1:2181",
+ Username: "user1",
+ Password: "pwd1",
+ },
+ Registries: map[string]*config.RegistryConfig{},
+
+ Services: map[string]*config.ServiceConfig{
+ "MockService": {
+ InterfaceName: "com.MockService",
+ Protocol: "mock",
+ Cluster: "failover",
+ Loadbalance: "random",
+ Retries: "3",
+ Group: "huadong_idc",
+ Version: "1.0.0",
+ Methods: []*config.MethodConfig{
+ {
+ Name: "GetUser",
+ Retries: "2",
+ LoadBalance: "random",
+ Weight: 200,
+ },
+ {
+ Name: "GetUser1",
+ Retries: "2",
+ LoadBalance: "random",
+ Weight: 200,
+ },
+ },
+ },
+ },
+ Protocols: map[string]*config.ProtocolConfig{
+ "mock": {
+ Name: "mock",
+ Ip: "127.0.0.1",
+ Port: "20000",
+ },
+ },
+ }
+ providerConfig.Services["MockService"].InitExported()
+ config.SetProviderConfig(*providerConfig)
+ }
diff --cc protocol/dubbo/dubbo_invoker_test.go
index 9585461,c0640d5..3c68b63
--- a/protocol/dubbo/dubbo_invoker_test.go
+++ b/protocol/dubbo/dubbo_invoker_test.go
@@@ -35,17 -32,21 +35,17 @@@ import
import (
"github.com/apache/dubbo-go/common"
"github.com/apache/dubbo-go/common/constant"
+ "github.com/apache/dubbo-go/common/proxy/proxy_factory"
+ "github.com/apache/dubbo-go/protocol"
"github.com/apache/dubbo-go/protocol/invocation"
+ "github.com/apache/dubbo-go/remoting"
+ "github.com/apache/dubbo-go/remoting/getty"
)
- func TestDubboInvoker_Invoke(t *testing.T) {
+ func TestDubboInvokerInvoke(t *testing.T) {
proto, url := InitTest(t)
- c := &Client{
- pendingResponses: new(sync.Map),
- conf: *clientConf,
- opts: Options{
- ConnectTimeout: 3 * time.Second,
- RequestTimeout: 6 * time.Second,
- },
- }
- c.pool = newGettyRPCClientConnPool(c, clientConf.PoolSize, time.Duration(int(time.Second)*clientConf.PoolTTL))
+ c := getExchangeClient(url)
invoker := NewDubboInvoker(url, c)
user := &User{}
diff --cc protocol/dubbo/dubbo_protocol_test.go
index 07b890f,6f3892b..352d980
--- a/protocol/dubbo/dubbo_protocol_test.go
+++ b/protocol/dubbo/dubbo_protocol_test.go
@@@ -81,20 -36,23 +81,12 @@@ func TestDubboProtocol_Export(t *testin
"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"
-)
-
-func TestDubboProtocolExport(t *testing.T) {
- // Export
- proto := GetProtocol()
- srvConf = &ServerConfig{}
- url, err := common.NewURL(mockCommonUrl)
- assert.NoError(t, err)
- exporter := proto.Export(protocol.NewBaseInvoker(url))
-
- // make sure url
- eq := exporter.GetInvoker().GetUrl().URLEqual(url)
- assert.True(t, eq)
-
- // second service: the same path and the different version
- url2, err := common.NewURL(mockCommonUrl, common.WithParamsValue(constant.VERSION_KEY, "v1.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)
-
- // second service: the same path and the different version
+ url2, err := common.NewURL("dubbo://127.0.0.1:20095/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", common.WithParamsValue(constant.VERSION_KEY, "v1.1"))
assert.NoError(t, err)
exporter2 := proto.Export(protocol.NewBaseInvoker(url2))
// make sure url
@@@ -104,7 -62,7 +96,7 @@@
// make sure exporterMap after 'Unexport'
_, ok := proto.(*DubboProtocol).ExporterMap().Load(url.ServiceKey())
assert.True(t, ok)
-- exporter.Unexport()
++ exporter2.Unexport()
_, ok = proto.(*DubboProtocol).ExporterMap().Load(url.ServiceKey())
assert.False(t, ok)