From 1eb4a83ffc0a23af94033ec454e8ac158c118723 Mon Sep 17 00:00:00 2001 From: cvictory Date: Fri, 24 Apr 2020 11:29:25 +0800 Subject: [PATCH 01/44] getty refactor --- protocol/dubbo/client.go | 696 ++++++++++++------------ protocol/dubbo/client_test.go | 564 +++++++++---------- protocol/dubbo/codec.go | 276 +++++----- protocol/dubbo/codec_test.go | 130 ++--- protocol/dubbo/config.go | 382 ++++++------- protocol/dubbo/dubbo_codec.go | 387 ++++++++++++++ protocol/dubbo/dubbo_invoker.go | 26 +- protocol/dubbo/dubbo_protocol.go | 77 ++- protocol/dubbo/listener.go | 702 ++++++++++++------------ protocol/dubbo/listener_test.go | 80 +-- protocol/dubbo/pool.go | 772 +++++++++++++-------------- protocol/dubbo/readwriter.go | 336 ++++++------ protocol/dubbo/server.go | 322 +++++------ protocol/invocation/rpcinvocation.go | 25 + remoting/codec.go | 28 + remoting/exchange.go | 131 +++++ remoting/exchange_client.go | 127 +++++ remoting/exchange_server.go | 29 + remoting/getty/client.go | 364 +++++++++++++ remoting/getty/client_test.go | 300 +++++++++++ remoting/getty/config.go | 209 ++++++++ remoting/getty/getty_client.go | 390 ++++++++++++++ remoting/getty/listener.go | 350 ++++++++++++ remoting/getty/listener_test.go | 58 ++ remoting/getty/pool.go | 404 ++++++++++++++ remoting/getty/readwriter.go | 136 +++++ remoting/getty/server.go | 188 +++++++ 27 files changed, 5336 insertions(+), 2153 deletions(-) create mode 100644 protocol/dubbo/dubbo_codec.go create mode 100644 remoting/codec.go create mode 100644 remoting/exchange.go create mode 100644 remoting/exchange_client.go create mode 100644 remoting/exchange_server.go create mode 100644 remoting/getty/client.go create mode 100644 remoting/getty/client_test.go create mode 100644 remoting/getty/config.go create mode 100644 remoting/getty/getty_client.go create mode 100644 remoting/getty/listener.go create mode 100644 remoting/getty/listener_test.go create mode 100644 remoting/getty/pool.go create mode 100644 remoting/getty/readwriter.go create mode 100644 remoting/getty/server.go diff --git a/protocol/dubbo/client.go b/protocol/dubbo/client.go index 5ec7db51af..630e9a7686 100644 --- a/protocol/dubbo/client.go +++ b/protocol/dubbo/client.go @@ -16,351 +16,351 @@ */ 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 -) - -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 ... -func SetClientConf(c ClientConfig) { - clientConf = &c - err := clientConf.CheckValidity() - if err != nil { - logger.Warnf("[ClientConfig CheckValidity] error: %v", err) - return - } - setClientGrpool() -} - -// GetClientConf ... -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 ... -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 ... -type Client struct { - opts Options - conf ClientConfig - pool *gettyRPCClientPool - sequence atomic.Uint64 - - pendingResponses *sync.Map -} - -// NewClient ... -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 ... -type Request struct { - addr string - svcUrl common.URL - method string - args interface{} - atta map[string]string -} - -// NewRequest ... -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 ... -type Response struct { - reply interface{} - atta map[string]string -} - -// NewResponse ... -func NewResponse(reply interface{}, atta map[string]string) *Response { - return &Response{ - reply: reply, - atta: atta, - } -} - -// CallOneway call one way -func (c *Client) CallOneway(request *Request) error { - - return perrors.WithStack(c.call(CT_OneWay, request, NewResponse(nil, nil), nil)) -} - -// 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 - } - - return perrors.WithStack(c.call(ct, request, response, nil)) -} - -// 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 - } - - 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 ... -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 -} +// +//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 +//) +// +//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 ... +//func SetClientConf(c ClientConfig) { +// clientConf = &c +// err := clientConf.CheckValidity() +// if err != nil { +// logger.Warnf("[ClientConfig CheckValidity] error: %v", err) +// return +// } +// setClientGrpool() +//} +// +//// GetClientConf ... +//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 ... +//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 ... +//type Client struct { +// opts Options +// conf ClientConfig +// pool *gettyRPCClientPool +// sequence atomic.Uint64 +// +// pendingResponses *sync.Map +//} +// +//// NewClient ... +//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 ... +//type Request struct { +// addr string +// svcUrl common.URL +// method string +// args interface{} +// atta map[string]string +//} +// +//// NewRequest ... +//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 ... +//type Response struct { +// reply interface{} +// atta map[string]string +//} +// +//// NewResponse ... +//func NewResponse(reply interface{}, atta map[string]string) *Response { +// return &Response{ +// reply: reply, +// atta: atta, +// } +//} +// +//// CallOneway call one way +//func (c *Client) CallOneway(request *Request) error { +// +// return perrors.WithStack(c.call(CT_OneWay, request, NewResponse(nil, nil), nil)) +//} +// +//// 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 +// } +// +// return perrors.WithStack(c.call(ct, request, response, nil)) +//} +// +//// 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 +// } +// +// 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 ... +//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 1e0a73fac1..4f5913c56f 100644 --- a/protocol/dubbo/client_test.go +++ b/protocol/dubbo/client_test.go @@ -16,285 +16,285 @@ */ package dubbo - -import ( - "bytes" - "context" - "sync" - "testing" - "time" -) - -import ( - hessian "github.com/apache/dubbo-go-hessian2" - perrors "github.com/pkg/errors" - "github.com/stretchr/testify/assert" -) - -import ( - "github.com/apache/dubbo-go/common" - "github.com/apache/dubbo-go/common/proxy/proxy_factory" - "github.com/apache/dubbo-go/protocol" -) - -func TestClient_CallOneway(t *testing.T) { - proto, url := InitTest(t) - - c := &Client{ - pendingResponses: new(sync.Map), - conf: *clientConf, - opts: Options{ - ConnectTimeout: 3e9, - RequestTimeout: 6e9, - }, - } - c.pool = newGettyRPCClientConnPool(c, clientConf.PoolSize, time.Duration(int(time.Second)*clientConf.PoolTTL)) - - //user := &User{} - err := c.CallOneway(NewRequest("127.0.0.1:20000", url, "GetUser", []interface{}{"1", "username"}, nil)) - assert.NoError(t, err) - - // destroy - proto.Destroy() -} - -func TestClient_Call(t *testing.T) { - proto, url := InitTest(t) - - c := &Client{ - pendingResponses: new(sync.Map), - conf: *clientConf, - opts: Options{ - ConnectTimeout: 3e9, - RequestTimeout: 10e9, - }, - } - c.pool = newGettyRPCClientConnPool(c, clientConf.PoolSize, time.Duration(int(time.Second)*clientConf.PoolTTL)) - - var ( - user *User - err error - ) - - user = &User{} - err = c.Call(NewRequest("127.0.0.1:20000", url, "GetBigPkg", []interface{}{nil}, nil), NewResponse(user, nil)) - assert.NoError(t, err) - assert.NotEqual(t, "", user.Id) - assert.NotEqual(t, "", user.Name) - - user = &User{} - err = c.Call(NewRequest("127.0.0.1:20000", url, "GetUser", []interface{}{"1", "username"}, nil), NewResponse(user, nil)) - assert.NoError(t, err) - assert.Equal(t, User{Id: "1", Name: "username"}, *user) - - user = &User{} - err = c.Call(NewRequest("127.0.0.1:20000", url, "GetUser0", []interface{}{"1", nil, "username"}, nil), NewResponse(user, nil)) - assert.NoError(t, err) - assert.Equal(t, User{Id: "1", Name: "username"}, *user) - - err = c.Call(NewRequest("127.0.0.1:20000", url, "GetUser1", []interface{}{}, nil), NewResponse(user, nil)) - assert.NoError(t, err) - - err = c.Call(NewRequest("127.0.0.1:20000", url, "GetUser2", []interface{}{}, nil), 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)) - 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)) - 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)) - 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)) - 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)) - assert.NoError(t, err) - assert.Equal(t, User{Id: "1", Name: ""}, *user) - - // destroy - proto.Destroy() -} - -func TestClient_AsyncCall(t *testing.T) { - proto, url := InitTest(t) - - c := &Client{ - pendingResponses: new(sync.Map), - conf: *clientConf, - opts: Options{ - ConnectTimeout: 3e9, - RequestTimeout: 6e9, - }, - } - c.pool = newGettyRPCClientConnPool(c, clientConf.PoolSize, time.Duration(int(time.Second)*clientConf.PoolTTL)) - - user := &User{} - lock := sync.Mutex{} - lock.Lock() - err := c.AsyncCall(NewRequest("127.0.0.1:20000", url, "GetUser", []interface{}{"1", "username"}, nil), func(response common.CallbackResponse) { - r := response.(AsyncCallbackResponse) - assert.Equal(t, User{Id: "1", Name: "username"}, *r.Reply.(*Response).reply.(*User)) - lock.Unlock() - }, NewResponse(user, nil)) - assert.NoError(t, err) - assert.Equal(t, User{}, *user) - - // destroy - lock.Lock() - proto.Destroy() - lock.Unlock() -} - -func InitTest(t *testing.T) (protocol.Protocol, common.URL) { - - hessian.RegisterPOJO(&User{}) - - methods, err := common.ServiceMap.Register("dubbo", &UserProvider{}) - assert.NoError(t, err) - assert.Equal(t, "GetBigPkg,GetUser,GetUser0,GetUser1,GetUser2,GetUser3,GetUser4,GetUser5,GetUser6", methods) - - // config - SetClientConf(ClientConfig{ - ConnectionNum: 2, - HeartbeatPeriod: "5s", - SessionTimeout: "20s", - PoolTTL: 600, - PoolSize: 64, - GettySessionParam: GettySessionParam{ - CompressEncoding: false, - TcpNoDelay: true, - TcpKeepAlive: true, - KeepAlivePeriod: "120s", - TcpRBufSize: 262144, - TcpWBufSize: 65536, - PkgWQSize: 512, - TcpReadTimeout: "4s", - TcpWriteTimeout: "5s", - WaitTimeout: "1s", - MaxMsgLen: 10240000000, - SessionName: "client", - }, - }) - assert.NoError(t, clientConf.CheckValidity()) - SetServerConfig(ServerConfig{ - SessionNumber: 700, - SessionTimeout: "20s", - GettySessionParam: GettySessionParam{ - CompressEncoding: false, - TcpNoDelay: true, - TcpKeepAlive: true, - KeepAlivePeriod: "120s", - TcpRBufSize: 262144, - TcpWBufSize: 65536, - PkgWQSize: 512, - TcpReadTimeout: "1s", - TcpWriteTimeout: "5s", - WaitTimeout: "1s", - MaxMsgLen: 10240000000, - SessionName: "server", - }}) - assert.NoError(t, srvConf.CheckValidity()) - - // Export - proto := GetProtocol() - url, err := common.NewURL("dubbo://127.0.0.1:20000/UserProvider?anyhost=true&" + - "application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&" + - "environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&" + - "module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&" + - "side=provider&timeout=3000×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 < 4000; i++ { - argBuf.WriteString("击鼓其镗,踊跃用兵。土国城漕,我独南行。从孙子仲,平陈与宋。不我以归,忧心有忡。爰居爰处?爰丧其马?于以求之?于林之下。死生契阔,与子成说。执子之手,与子偕老。于嗟阔兮,不我活兮。于嗟洵兮,不我信兮。") - argBuf.WriteString("击鼓其镗,踊跃用兵。土国城漕,我独南行。从孙子仲,平陈与宋。不我以归,忧心有忡。爰居爰处?爰丧其马?于以求之?于林之下。死生契阔,与子成说。执子之手,与子偕老。于嗟阔兮,不我活兮。于嗟洵兮,不我信兮。") - } - rsp.Id = argBuf.String() - rsp.Name = argBuf.String() - return nil -} - -func (u *UserProvider) GetUser(ctx context.Context, req []interface{}, rsp *User) error { - rsp.Id = req[0].(string) - rsp.Name = req[1].(string) - return nil -} - -func (u *UserProvider) GetUser0(id string, k *User, name string) (User, error) { - return User{Id: id, Name: name}, nil -} - -func (u *UserProvider) GetUser1() error { - return nil -} - -func (u *UserProvider) GetUser2() error { - return perrors.New("error") -} - -func (u *UserProvider) GetUser3(rsp *[]interface{}) error { - *rsp = append(*rsp, User{Id: "1", Name: "username"}) - return nil -} - -func (u *UserProvider) GetUser4(ctx context.Context, req []interface{}) ([]interface{}, error) { - - return []interface{}{User{Id: req[0].([]interface{})[0].(string), Name: req[0].([]interface{})[1].(string)}}, nil -} - -func (u *UserProvider) GetUser5(ctx context.Context, req []interface{}) (map[interface{}]interface{}, error) { - return map[interface{}]interface{}{"key": User{Id: req[0].(map[interface{}]interface{})["id"].(string), Name: req[0].(map[interface{}]interface{})["name"].(string)}}, nil -} - -func (u *UserProvider) GetUser6(id int64) (*User, error) { - if id == 0 { - return nil, nil - } - return &User{Id: "1"}, nil -} - -func (u *UserProvider) Reference() string { - return "UserProvider" -} - -func (u User) JavaClassName() string { - return "com.ikurento.user.User" -} +// +//import ( +// "bytes" +// "context" +// "sync" +// "testing" +// "time" +//) +// +//import ( +// hessian "github.com/apache/dubbo-go-hessian2" +// perrors "github.com/pkg/errors" +// "github.com/stretchr/testify/assert" +//) +// +//import ( +// "github.com/apache/dubbo-go/common" +// "github.com/apache/dubbo-go/common/proxy/proxy_factory" +// "github.com/apache/dubbo-go/protocol" +//) +// +//func TestClient_CallOneway(t *testing.T) { +// proto, url := InitTest(t) +// +// c := &Client{ +// pendingResponses: new(sync.Map), +// conf: *clientConf, +// opts: Options{ +// ConnectTimeout: 3e9, +// RequestTimeout: 6e9, +// }, +// } +// c.pool = newGettyRPCClientConnPool(c, clientConf.PoolSize, time.Duration(int(time.Second)*clientConf.PoolTTL)) +// +// //user := &User{} +// err := c.CallOneway(NewRequest("127.0.0.1:20000", url, "GetUser", []interface{}{"1", "username"}, nil)) +// assert.NoError(t, err) +// +// // destroy +// proto.Destroy() +//} +// +//func TestClient_Call(t *testing.T) { +// proto, url := InitTest(t) +// +// c := &Client{ +// pendingResponses: new(sync.Map), +// conf: *clientConf, +// opts: Options{ +// ConnectTimeout: 3e9, +// RequestTimeout: 10e9, +// }, +// } +// c.pool = newGettyRPCClientConnPool(c, clientConf.PoolSize, time.Duration(int(time.Second)*clientConf.PoolTTL)) +// +// var ( +// user *User +// err error +// ) +// +// user = &User{} +// err = c.Call(NewRequest("127.0.0.1:20000", url, "GetBigPkg", []interface{}{nil}, nil), NewResponse(user, nil)) +// assert.NoError(t, err) +// assert.NotEqual(t, "", user.Id) +// assert.NotEqual(t, "", user.Name) +// +// user = &User{} +// err = c.Call(NewRequest("127.0.0.1:20000", url, "GetUser", []interface{}{"1", "username"}, nil), NewResponse(user, nil)) +// assert.NoError(t, err) +// assert.Equal(t, User{Id: "1", Name: "username"}, *user) +// +// user = &User{} +// err = c.Call(NewRequest("127.0.0.1:20000", url, "GetUser0", []interface{}{"1", nil, "username"}, nil), NewResponse(user, nil)) +// assert.NoError(t, err) +// assert.Equal(t, User{Id: "1", Name: "username"}, *user) +// +// err = c.Call(NewRequest("127.0.0.1:20000", url, "GetUser1", []interface{}{}, nil), NewResponse(user, nil)) +// assert.NoError(t, err) +// +// err = c.Call(NewRequest("127.0.0.1:20000", url, "GetUser2", []interface{}{}, nil), 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)) +// 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)) +// 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)) +// 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)) +// 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)) +// assert.NoError(t, err) +// assert.Equal(t, User{Id: "1", Name: ""}, *user) +// +// // destroy +// proto.Destroy() +//} +// +//func TestClient_AsyncCall(t *testing.T) { +// proto, url := InitTest(t) +// +// c := &Client{ +// pendingResponses: new(sync.Map), +// conf: *clientConf, +// opts: Options{ +// ConnectTimeout: 3e9, +// RequestTimeout: 6e9, +// }, +// } +// c.pool = newGettyRPCClientConnPool(c, clientConf.PoolSize, time.Duration(int(time.Second)*clientConf.PoolTTL)) +// +// user := &User{} +// lock := sync.Mutex{} +// lock.Lock() +// err := c.AsyncCall(NewRequest("127.0.0.1:20000", url, "GetUser", []interface{}{"1", "username"}, nil), func(response common.CallbackResponse) { +// r := response.(AsyncCallbackResponse) +// assert.Equal(t, User{Id: "1", Name: "username"}, *r.Reply.(*Response).reply.(*User)) +// lock.Unlock() +// }, NewResponse(user, nil)) +// assert.NoError(t, err) +// assert.Equal(t, User{}, *user) +// +// // destroy +// lock.Lock() +// proto.Destroy() +// lock.Unlock() +//} +// +//func InitTest(t *testing.T) (protocol.Protocol, common.URL) { +// +// hessian.RegisterPOJO(&User{}) +// +// methods, err := common.ServiceMap.Register("dubbo", &UserProvider{}) +// assert.NoError(t, err) +// assert.Equal(t, "GetBigPkg,GetUser,GetUser0,GetUser1,GetUser2,GetUser3,GetUser4,GetUser5,GetUser6", methods) +// +// // config +// SetClientConf(ClientConfig{ +// ConnectionNum: 2, +// HeartbeatPeriod: "5s", +// SessionTimeout: "20s", +// PoolTTL: 600, +// PoolSize: 64, +// GettySessionParam: GettySessionParam{ +// CompressEncoding: false, +// TcpNoDelay: true, +// TcpKeepAlive: true, +// KeepAlivePeriod: "120s", +// TcpRBufSize: 262144, +// TcpWBufSize: 65536, +// PkgWQSize: 512, +// TcpReadTimeout: "4s", +// TcpWriteTimeout: "5s", +// WaitTimeout: "1s", +// MaxMsgLen: 10240000000, +// SessionName: "client", +// }, +// }) +// assert.NoError(t, clientConf.CheckValidity()) +// SetServerConfig(ServerConfig{ +// SessionNumber: 700, +// SessionTimeout: "20s", +// GettySessionParam: GettySessionParam{ +// CompressEncoding: false, +// TcpNoDelay: true, +// TcpKeepAlive: true, +// KeepAlivePeriod: "120s", +// TcpRBufSize: 262144, +// TcpWBufSize: 65536, +// PkgWQSize: 512, +// TcpReadTimeout: "1s", +// TcpWriteTimeout: "5s", +// WaitTimeout: "1s", +// MaxMsgLen: 10240000000, +// SessionName: "server", +// }}) +// assert.NoError(t, srvConf.CheckValidity()) +// +// // Export +// proto := GetProtocol() +// url, err := common.NewURL("dubbo://127.0.0.1:20000/UserProvider?anyhost=true&" + +// "application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&" + +// "environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&" + +// "module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&" + +// "side=provider&timeout=3000×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 < 4000; i++ { +// argBuf.WriteString("击鼓其镗,踊跃用兵。土国城漕,我独南行。从孙子仲,平陈与宋。不我以归,忧心有忡。爰居爰处?爰丧其马?于以求之?于林之下。死生契阔,与子成说。执子之手,与子偕老。于嗟阔兮,不我活兮。于嗟洵兮,不我信兮。") +// argBuf.WriteString("击鼓其镗,踊跃用兵。土国城漕,我独南行。从孙子仲,平陈与宋。不我以归,忧心有忡。爰居爰处?爰丧其马?于以求之?于林之下。死生契阔,与子成说。执子之手,与子偕老。于嗟阔兮,不我活兮。于嗟洵兮,不我信兮。") +// } +// rsp.Id = argBuf.String() +// rsp.Name = argBuf.String() +// return nil +//} +// +//func (u *UserProvider) GetUser(ctx context.Context, req []interface{}, rsp *User) error { +// rsp.Id = req[0].(string) +// rsp.Name = req[1].(string) +// return nil +//} +// +//func (u *UserProvider) GetUser0(id string, k *User, name string) (User, error) { +// return User{Id: id, Name: name}, nil +//} +// +//func (u *UserProvider) GetUser1() error { +// return nil +//} +// +//func (u *UserProvider) GetUser2() error { +// return perrors.New("error") +//} +// +//func (u *UserProvider) GetUser3(rsp *[]interface{}) error { +// *rsp = append(*rsp, User{Id: "1", Name: "username"}) +// return nil +//} +// +//func (u *UserProvider) GetUser4(ctx context.Context, req []interface{}) ([]interface{}, error) { +// +// return []interface{}{User{Id: req[0].([]interface{})[0].(string), Name: req[0].([]interface{})[1].(string)}}, nil +//} +// +//func (u *UserProvider) GetUser5(ctx context.Context, req []interface{}) (map[interface{}]interface{}, error) { +// return map[interface{}]interface{}{"key": User{Id: req[0].(map[interface{}]interface{})["id"].(string), Name: req[0].(map[interface{}]interface{})["name"].(string)}}, nil +//} +// +//func (u *UserProvider) GetUser6(id int64) (*User, error) { +// if id == 0 { +// return nil, nil +// } +// return &User{Id: "1"}, nil +//} +// +//func (u *UserProvider) Reference() string { +// return "UserProvider" +//} +// +//func (u User) JavaClassName() string { +// return "com.ikurento.user.User" +//} diff --git a/protocol/dubbo/codec.go b/protocol/dubbo/codec.go index 76416b2baf..8dd91c2d31 100644 --- a/protocol/dubbo/codec.go +++ b/protocol/dubbo/codec.go @@ -16,141 +16,141 @@ */ 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 -} - -func (p DubboPackage) String() string { - return fmt.Sprintf("DubboPackage: Header-%v, Path-%v, Body-%v", p.Header, p.Service, p.Body) -} - -// Marshal ... -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 ... -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 ... -type PendingResponse struct { - seq uint64 - err error - start time.Time - readStart time.Time - callback common.AsyncCallback - response *Response - done chan struct{} -} - -// NewPendingResponse ... -func NewPendingResponse() *PendingResponse { - return &PendingResponse{ - start: time.Now(), - response: &Response{}, - done: make(chan struct{}), - } -} - -// GetCallResponse ... -func (r PendingResponse) GetCallResponse() common.CallbackResponse { - return AsyncCallbackResponse{ - Cause: r.err, - Start: r.start, - ReadStart: r.readStart, - Reply: r.response, - } -} +// +//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 +//} +// +//func (p DubboPackage) String() string { +// return fmt.Sprintf("DubboPackage: Header-%v, Path-%v, Body-%v", p.Header, p.Service, p.Body) +//} +// +//// Marshal ... +//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 ... +//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 ... +//type PendingResponse struct { +// seq uint64 +// err error +// start time.Time +// readStart time.Time +// callback common.AsyncCallback +// response *Response +// done chan struct{} +//} +// +//// NewPendingResponse ... +//func NewPendingResponse() *PendingResponse { +// return &PendingResponse{ +// start: time.Now(), +// response: &Response{}, +// done: make(chan struct{}), +// } +//} +// +//// GetCallResponse ... +//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 index 5dc71f0d08..c9599da6c7 100644 --- a/protocol/dubbo/codec_test.go +++ b/protocol/dubbo/codec_test.go @@ -16,68 +16,68 @@ */ 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) -} +// +//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/config.go b/protocol/dubbo/config.go index dbc6989c54..f00931e00f 100644 --- a/protocol/dubbo/config.go +++ b/protocol/dubbo/config.go @@ -16,194 +16,194 @@ */ package dubbo - -import ( - "time" -) - -import ( - "github.com/dubbogo/getty" - perrors "github.com/pkg/errors" -) - -type ( - // GettySessionParam ... - GettySessionParam struct { - CompressEncoding bool `default:"false" yaml:"compress_encoding" json:"compress_encoding,omitempty"` - TcpNoDelay bool `default:"true" yaml:"tcp_no_delay" json:"tcp_no_delay,omitempty"` - 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 - 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 - TcpWriteTimeout string `default:"5s" yaml:"tcp_write_timeout" json:"tcp_write_timeout,omitempty"` - tcpWriteTimeout time.Duration - WaitTimeout string `default:"7s" yaml:"wait_timeout" json:"wait_timeout,omitempty"` - waitTimeout 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"` - } - - // ServerConfig - //Config 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"` - - // grpool - GrPoolSize int `default:"0" yaml:"gr_pool_size" json:"gr_pool_size,omitempty"` - QueueLen int `default:"0" yaml:"queue_len" json:"queue_len,omitempty"` - QueueNumber int `default:"0" yaml:"queue_number" json:"queue_number,omitempty"` - - // session tcp parameters - GettySessionParam GettySessionParam `required:"true" yaml:"getty_session_param" json:"getty_session_param,omitempty"` - } - - // ClientConfig - //Config holds supported types by the multiconfig package - ClientConfig struct { - ReconnectInterval int `default:"0" yaml:"reconnect_interval" json:"reconnect_interval,omitempty"` - - // 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 - - // session - SessionTimeout string `default:"60s" yaml:"session_timeout" json:"session_timeout,omitempty"` - sessionTimeout time.Duration - - // Connection Pool - PoolSize int `default:"2" yaml:"pool_size" json:"pool_size,omitempty"` - PoolTTL int `default:"180" yaml:"pool_ttl" json:"pool_ttl,omitempty"` - - // grpool - GrPoolSize int `default:"0" yaml:"gr_pool_size" json:"gr_pool_size,omitempty"` - QueueLen int `default:"0" yaml:"queue_len" json:"queue_len,omitempty"` - QueueNumber int `default:"0" yaml:"queue_number" json:"queue_number,omitempty"` - - // session tcp parameters - GettySessionParam GettySessionParam `required:"true" yaml:"getty_session_param" json:"getty_session_param,omitempty"` - } -) - -// GetDefaultClientConfig ... -func GetDefaultClientConfig() ClientConfig { - return ClientConfig{ - ReconnectInterval: 0, - ConnectionNum: 16, - HeartbeatPeriod: "30s", - SessionTimeout: "180s", - PoolSize: 4, - PoolTTL: 600, - GrPoolSize: 200, - QueueLen: 64, - QueueNumber: 10, - GettySessionParam: GettySessionParam{ - CompressEncoding: false, - TcpNoDelay: true, - TcpKeepAlive: true, - KeepAlivePeriod: "180s", - TcpRBufSize: 262144, - TcpWBufSize: 65536, - PkgWQSize: 512, - TcpReadTimeout: "1s", - TcpWriteTimeout: "5s", - WaitTimeout: "1s", - MaxMsgLen: 102400, - SessionName: "client", - }} -} - -// GetDefaultServerConfig ... -func GetDefaultServerConfig() ServerConfig { - return ServerConfig{ - SessionTimeout: "180s", - SessionNumber: 700, - GrPoolSize: 120, - QueueNumber: 6, - QueueLen: 64, - GettySessionParam: GettySessionParam{ - CompressEncoding: false, - TcpNoDelay: true, - TcpKeepAlive: true, - KeepAlivePeriod: "180s", - TcpRBufSize: 262144, - TcpWBufSize: 65536, - PkgWQSize: 512, - TcpReadTimeout: "1s", - TcpWriteTimeout: "5s", - WaitTimeout: "1s", - MaxMsgLen: 102400, - SessionName: "server", - }, - } -} - -// CheckValidity ... -func (c *GettySessionParam) CheckValidity() error { - var err error - - if c.keepAlivePeriod, 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 { - return perrors.WithMessagef(err, "time.ParseDuration(TcpReadTimeout{%#v})", c.TcpReadTimeout) - } - - if c.tcpWriteTimeout, 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 { - return perrors.WithMessagef(err, "time.ParseDuration(WaitTimeout{%#v})", c.WaitTimeout) - } - - return nil -} - -// CheckValidity ... -func (c *ClientConfig) CheckValidity() error { - var err error - - c.ReconnectInterval = c.ReconnectInterval * 1e6 - - if c.heartbeatPeriod, err = time.ParseDuration(c.HeartbeatPeriod); err != nil { - return perrors.WithMessagef(err, "time.ParseDuration(HeartbeatPeroid{%#v})", c.HeartbeatPeriod) - } - - if c.heartbeatPeriod >= time.Duration(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 { - return perrors.WithMessagef(err, "time.ParseDuration(SessionTimeout{%#v})", c.SessionTimeout) - } - - return perrors.WithStack(c.GettySessionParam.CheckValidity()) -} - -// CheckValidity ... -func (c *ServerConfig) CheckValidity() error { - var err error - - if c.sessionTimeout, err = time.ParseDuration(c.SessionTimeout); err != nil { - return perrors.WithMessagef(err, "time.ParseDuration(SessionTimeout{%#v})", c.SessionTimeout) - } - - if c.sessionTimeout >= time.Duration(getty.MaxWheelTimeSpan) { - return perrors.WithMessagef(err, "session_timeout %s should be less than %s", - c.SessionTimeout, time.Duration(getty.MaxWheelTimeSpan)) - } - - return perrors.WithStack(c.GettySessionParam.CheckValidity()) -} +// +//import ( +// "time" +//) +// +//import ( +// "github.com/dubbogo/getty" +// perrors "github.com/pkg/errors" +//) +// +//type ( +// // GettySessionParam ... +// GettySessionParam struct { +// CompressEncoding bool `default:"false" yaml:"compress_encoding" json:"compress_encoding,omitempty"` +// TcpNoDelay bool `default:"true" yaml:"tcp_no_delay" json:"tcp_no_delay,omitempty"` +// 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 +// 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 +// TcpWriteTimeout string `default:"5s" yaml:"tcp_write_timeout" json:"tcp_write_timeout,omitempty"` +// tcpWriteTimeout time.Duration +// WaitTimeout string `default:"7s" yaml:"wait_timeout" json:"wait_timeout,omitempty"` +// waitTimeout 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"` +// } +// +// // ServerConfig +// //Config 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"` +// +// // grpool +// GrPoolSize int `default:"0" yaml:"gr_pool_size" json:"gr_pool_size,omitempty"` +// QueueLen int `default:"0" yaml:"queue_len" json:"queue_len,omitempty"` +// QueueNumber int `default:"0" yaml:"queue_number" json:"queue_number,omitempty"` +// +// // session tcp parameters +// GettySessionParam GettySessionParam `required:"true" yaml:"getty_session_param" json:"getty_session_param,omitempty"` +// } +// +// // ClientConfig +// //Config holds supported types by the multiconfig package +// ClientConfig struct { +// ReconnectInterval int `default:"0" yaml:"reconnect_interval" json:"reconnect_interval,omitempty"` +// +// // 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 +// +// // session +// SessionTimeout string `default:"60s" yaml:"session_timeout" json:"session_timeout,omitempty"` +// sessionTimeout time.Duration +// +// // Connection Pool +// PoolSize int `default:"2" yaml:"pool_size" json:"pool_size,omitempty"` +// PoolTTL int `default:"180" yaml:"pool_ttl" json:"pool_ttl,omitempty"` +// +// // grpool +// GrPoolSize int `default:"0" yaml:"gr_pool_size" json:"gr_pool_size,omitempty"` +// QueueLen int `default:"0" yaml:"queue_len" json:"queue_len,omitempty"` +// QueueNumber int `default:"0" yaml:"queue_number" json:"queue_number,omitempty"` +// +// // session tcp parameters +// GettySessionParam GettySessionParam `required:"true" yaml:"getty_session_param" json:"getty_session_param,omitempty"` +// } +//) +// +//// GetDefaultClientConfig ... +//func GetDefaultClientConfig() ClientConfig { +// return ClientConfig{ +// ReconnectInterval: 0, +// ConnectionNum: 16, +// HeartbeatPeriod: "30s", +// SessionTimeout: "180s", +// PoolSize: 4, +// PoolTTL: 600, +// GrPoolSize: 200, +// QueueLen: 64, +// QueueNumber: 10, +// GettySessionParam: GettySessionParam{ +// CompressEncoding: false, +// TcpNoDelay: true, +// TcpKeepAlive: true, +// KeepAlivePeriod: "180s", +// TcpRBufSize: 262144, +// TcpWBufSize: 65536, +// PkgWQSize: 512, +// TcpReadTimeout: "1s", +// TcpWriteTimeout: "5s", +// WaitTimeout: "1s", +// MaxMsgLen: 102400, +// SessionName: "client", +// }} +//} +// +//// GetDefaultServerConfig ... +//func GetDefaultServerConfig() ServerConfig { +// return ServerConfig{ +// SessionTimeout: "180s", +// SessionNumber: 700, +// GrPoolSize: 120, +// QueueNumber: 6, +// QueueLen: 64, +// GettySessionParam: GettySessionParam{ +// CompressEncoding: false, +// TcpNoDelay: true, +// TcpKeepAlive: true, +// KeepAlivePeriod: "180s", +// TcpRBufSize: 262144, +// TcpWBufSize: 65536, +// PkgWQSize: 512, +// TcpReadTimeout: "1s", +// TcpWriteTimeout: "5s", +// WaitTimeout: "1s", +// MaxMsgLen: 102400, +// SessionName: "server", +// }, +// } +//} +// +//// CheckValidity ... +//func (c *GettySessionParam) CheckValidity() error { +// var err error +// +// if c.keepAlivePeriod, 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 { +// return perrors.WithMessagef(err, "time.ParseDuration(TcpReadTimeout{%#v})", c.TcpReadTimeout) +// } +// +// if c.tcpWriteTimeout, 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 { +// return perrors.WithMessagef(err, "time.ParseDuration(WaitTimeout{%#v})", c.WaitTimeout) +// } +// +// return nil +//} +// +//// CheckValidity ... +//func (c *ClientConfig) CheckValidity() error { +// var err error +// +// c.ReconnectInterval = c.ReconnectInterval * 1e6 +// +// if c.heartbeatPeriod, err = time.ParseDuration(c.HeartbeatPeriod); err != nil { +// return perrors.WithMessagef(err, "time.ParseDuration(HeartbeatPeroid{%#v})", c.HeartbeatPeriod) +// } +// +// if c.heartbeatPeriod >= time.Duration(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 { +// return perrors.WithMessagef(err, "time.ParseDuration(SessionTimeout{%#v})", c.SessionTimeout) +// } +// +// return perrors.WithStack(c.GettySessionParam.CheckValidity()) +//} +// +//// CheckValidity ... +//func (c *ServerConfig) CheckValidity() error { +// var err error +// +// if c.sessionTimeout, err = time.ParseDuration(c.SessionTimeout); err != nil { +// return perrors.WithMessagef(err, "time.ParseDuration(SessionTimeout{%#v})", c.SessionTimeout) +// } +// +// if c.sessionTimeout >= time.Duration(getty.MaxWheelTimeSpan) { +// return perrors.WithMessagef(err, "session_timeout %s should be less than %s", +// c.SessionTimeout, time.Duration(getty.MaxWheelTimeSpan)) +// } +// +// return perrors.WithStack(c.GettySessionParam.CheckValidity()) +//} diff --git a/protocol/dubbo/dubbo_codec.go b/protocol/dubbo/dubbo_codec.go new file mode 100644 index 0000000000..5dea37f870 --- /dev/null +++ b/protocol/dubbo/dubbo_codec.go @@ -0,0 +1,387 @@ +package dubbo + +import ( + "bufio" + "bytes" + "fmt" + hessian "github.com/apache/dubbo-go-hessian2" + "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" + perrors "github.com/pkg/errors" + "strconv" + "time" +) + +////SerialID serial ID +//type SerialID byte +//type SequenceType int64 +// +//const ( +// // S_Dubbo dubbo serial id +// S_Dubbo SerialID = 2 +//) +// +//// DubboPackage ... +//type DubboPackage struct { +// Header hessian.DubboHeader +// Service hessian.Service +// Body interface{} +// Err error +//} +// +//func (p DubboPackage) String() string { +// return fmt.Sprintf("DubboPackage: Header-%v, Path-%v, Body-%v", p.Header, p.Service, p.Body) +//} + +// +//// Marshal ... +//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 ... +//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, 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: resp.Reply} +// } +// } +// +// // read body +// err = codec.ReadBody(p.Body) +// return perrors.WithStack(err) +//} + +///////////////////////////////////////// +///////////////////////////////////////// +//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 +} + +func (p DubboPackage) String() string { + return fmt.Sprintf("DubboPackage: Header-%v, Path-%v, Body-%v", p.Header, p.Service, p.Body) +} + +// Marshal ... +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 ... +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) +} +type DubboCodec struct { +} + +func (c *DubboCodec) EncodeRequest(request *remoting.Request) (*bytes.Buffer, error) { + invocation := request.Data.(invocation.RPCInvocation) + + 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() + + timeout, err := strconv.Atoi(invocation.AttachmentsByKey(constant.TIMEOUT_KEY, "3000")) + if err != nil { + panic(err) + } + p.Service.Timeout = time.Duration(timeout) + //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.Header.ID = request.Id + if request.TwoWay { + p.Header.Type = hessian.PackageRequest_TwoWay + } else { + p.Header.Type = hessian.PackageRequest + } + + p.Body = hessian.NewRequest(invocation.Arguments(), invocation.Attachments()) + + 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 (c *DubboCodec) EncodeResponse(response *remoting.Response) (*bytes.Buffer, error) { + var ptype hessian.PackageType = hessian.PackageResponse + if response.IsHeartbeat() { + ptype = hessian.PackageHeartbeat + } + resp := &DubboPackage{ + Header: hessian.DubboHeader{ + SerialID: response.SerialID, + Type: ptype, + ID: response.Id, + ResponseStatus: response.Status, + }, + } + resp.Body = response.Result + //if response.Header.Type&hessian.PackageRequest != 0x00 { + // resp.Body = req.Body + //} else { + // resp.Body = nil + //} + codec := hessian.NewHessianCodec(nil) + + pkg, err := codec.Write(resp.Service, resp.Header, resp.Body) + if err != nil { + return nil, perrors.WithStack(err) + } + + return bytes.NewBuffer(pkg), nil +} +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) + if err != nil { + originErr := perrors.Cause(err) + if originErr == hessian.ErrHeaderNotEnough || originErr == hessian.ErrBodyNotEnough { + //FIXME + return request, 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: false, + } + 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 { + //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 { + //argsType + //invocation.ParameterTypes(constant., req[1].(string)) + //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] + //} + invoc := invocation.NewRPCInvocationWithOptions(invocation.WithAttachments(attachments), + invocation.WithArguments(args), invocation.WithMethodName(methodName)) + request.Data = invoc + //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 request, hessian.HEADER_LENGTH + pkg.Header.BodyLen, nil +} +func (c *DubboCodec) DecodeResponse(data []byte) (*remoting.Response, int, error) { + pkg := &DubboPackage{} + buf := bytes.NewBuffer(data) + var response *remoting.Response + err := pkg.Unmarshal(buf, response) + if err != nil { + originErr := perrors.Cause(err) + if originErr == hessian.ErrHeaderNotEnough || originErr == hessian.ErrBodyNotEnough { + return response, 0, nil + } + logger.Errorf("pkg.Unmarshal(len(@data):%d) = error:%+v", buf.Len(), err) + + return response, 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, + } + var error error + if pkg.Header.Type&hessian.PackageHeartbeat != 0x00 { + if pkg.Header.Type&hessian.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{} + if pkg.Header.Type&hessian.PackageRequest == 0x00 { + if pkg.Err != nil { + rpcResult.Err = pkg.Err + } + rpcResult.Attrs = pkg.Body.(*hessian.Response).Attachments + rpcResult.Rest = pkg.Body.(*hessian.Response).RspObj + } + + //h.conn.updateSession(session) + //pendingResponse := h.conn.pool.rpcClient.removePendingResponse(SequenceType(p.Header.ID)) + //if pendingResponse == nil { + // logger.Errorf("failed to get pending response context for response package %s", *p) + // return + //} + + return response, hessian.HEADER_LENGTH + pkg.Header.BodyLen, nil +} diff --git a/protocol/dubbo/dubbo_invoker.go b/protocol/dubbo/dubbo_invoker.go index 09c3725710..ddee734b1b 100644 --- a/protocol/dubbo/dubbo_invoker.go +++ b/protocol/dubbo/dubbo_invoker.go @@ -19,6 +19,8 @@ package dubbo import ( "context" + "github.com/apache/dubbo-go/config" + "github.com/apache/dubbo-go/remoting" "strconv" "sync" "sync/atomic" @@ -51,18 +53,26 @@ var ( // DubboInvoker ... type DubboInvoker struct { protocol.BaseInvoker - client *Client + client *remoting.ExchangeClient quitOnce sync.Once + timeout time.Duration // Used to record the number of requests. -1 represent this DubboInvoker is destroyed reqNum int64 } // NewDubboInvoker ... -func NewDubboInvoker(url common.URL, client *Client) *DubboInvoker { +func NewDubboInvoker(url common.URL, client *remoting.ExchangeClient) *DubboInvoker { + requestTimeout := config.GetConsumerConfig().RequestTimeout + + requestTimeoutStr := url.GetParam(constant.TIMEOUT_KEY, config.GetConsumerConfig().Request_Timeout) + if t, err := time.ParseDuration(requestTimeoutStr); err == nil { + requestTimeout = t + } return &DubboInvoker{ BaseInvoker: *protocol.NewBaseInvoker(url), client: client, reqNum: 0, + timeout: requestTimeout, } } @@ -99,23 +109,25 @@ func (di *DubboInvoker) Invoke(ctx context.Context, invocation protocol.Invocati logger.Errorf("ParseBool - error: %v", err) async = false } - response := NewResponse(inv.Reply(), nil) + //response := NewResponse(inv.Reply(), nil) + rest := &protocol.RPCResult{} if async { if callBack, ok := inv.CallBack().(func(response common.CallbackResponse)); ok { - result.Err = di.client.AsyncCall(NewRequest(url.Location, url, inv.MethodName(), inv.Arguments(), inv.Attachments()), callBack, response) + //result.Err = di.client.AsyncCall(NewRequest(url.Location, url, inv.MethodName(), inv.Arguments(), inv.Attachments()), callBack, response) + result.Err = di.client.AsyncRequest(&invocation, url, di.timeout, callBack, rest) } else { - result.Err = di.client.CallOneway(NewRequest(url.Location, url, inv.MethodName(), inv.Arguments(), inv.Attachments())) + result.Err = di.client.Send(&invocation, di.timeout) } } else { if inv.Reply() == nil { result.Err = ErrNoReply } else { - result.Err = di.client.Call(NewRequest(url.Location, url, inv.MethodName(), inv.Arguments(), inv.Attachments()), response) + result.Err = di.client.Request(&invocation, url, di.timeout, rest) } } if result.Err == nil { result.Rest = inv.Reply() - result.Attrs = response.atta + result.Attrs = result.Attachments() } logger.Debugf("result.Err: %v, result.Rest: %v", result.Err, result.Rest) diff --git a/protocol/dubbo/dubbo_protocol.go b/protocol/dubbo/dubbo_protocol.go index 355dbc8024..e98e6e2dde 100644 --- a/protocol/dubbo/dubbo_protocol.go +++ b/protocol/dubbo/dubbo_protocol.go @@ -18,13 +18,15 @@ package dubbo import ( + "fmt" + "github.com/apache/dubbo-go/protocol/invocation" + "github.com/apache/dubbo-go/remoting" + "github.com/apache/dubbo-go/remoting/getty" "sync" - "time" ) import ( "github.com/apache/dubbo-go/common" - "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/common/extension" "github.com/apache/dubbo-go/common/logger" "github.com/apache/dubbo-go/config" @@ -48,7 +50,7 @@ var ( // DubboProtocol ... type DubboProtocol struct { protocol.BaseProtocol - serverMap map[string]*Server + serverMap map[string]*remoting.ExchangeServer serverLock sync.Mutex } @@ -56,7 +58,7 @@ type DubboProtocol struct { func NewDubboProtocol() *DubboProtocol { return &DubboProtocol{ BaseProtocol: protocol.NewBaseProtocol(), - serverMap: make(map[string]*Server), + serverMap: make(map[string]*remoting.ExchangeServer), } } @@ -76,17 +78,20 @@ func (dp *DubboProtocol) Export(invoker protocol.Invoker) protocol.Exporter { // Refer ... 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 - } - - invoker := NewDubboInvoker(url, NewClient(Options{ + //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 + //} + + //invoker := NewDubboInvoker(url, NewClient(Options{ + // ConnectTimeout: config.GetConsumerConfig().ConnectTimeout, + // RequestTimeout: requestTimeout, + //})) + invoker := NewDubboInvoker(url, remoting.NewExchangeClient(url, getty.NewClient(getty.Options{ ConnectTimeout: config.GetConsumerConfig().ConnectTimeout, - RequestTimeout: requestTimeout, - })) + }), config.GetConsumerConfig().ConnectTimeout)) dp.SetInvokers(invoker) logger.Infof("Refer service: %s", url.String()) return invoker @@ -116,9 +121,12 @@ func (dp *DubboProtocol) openServer(url common.URL) { dp.serverLock.Lock() _, ok = dp.serverMap[url.Location] if !ok { - srv := NewServer() + handler := func(invocation *invocation.RPCInvocation) protocol.RPCResult { + return doHandleRequest(invocation) + } + srv := remoting.NewExchangeServer(url, handler) dp.serverMap[url.Location] = srv - srv.Start(url) + srv.Start() } dp.serverLock.Unlock() } @@ -131,3 +139,40 @@ func GetProtocol() protocol.Protocol { } return dubboProtocol } + +func doHandleRequest(rpcInvocation *invocation.RPCInvocation) protocol.RPCResult { + exporter, _ := dubboProtocol.ExporterMap().Load(rpcInvocation.ServiceKey()) + result := protocol.RPCResult{} + if exporter == nil { + err := fmt.Errorf("don't have this exporter, key: %s", rpcInvocation.ServiceKey()) + logger.Errorf(err.Error()) + result.Err = err + //reply(session, p, hessian.PackageResponse) + return result + } + invoker := exporter.(protocol.Exporter).GetInvoker() + if invoker != nil { + //attachments := rpcInvocation.Attachments() + //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(rpcInvocation) + + invokeResult := invoker.Invoke(ctx, rpcInvocation) + if err := invokeResult.Error(); err != nil { + result.Err = invokeResult.Error() + //p.Header.ResponseStatus = hessian.Response_OK + //p.Body = hessian.NewResponse(nil, err, result.Attachments()) + } else { + result.Rest = invokeResult.Result() + //p.Header.ResponseStatus = hessian.Response_OK + //p.Body = hessian.NewResponse(res, nil, result.Attachments()) + } + } else { + result.Err = fmt.Errorf("don't have the invoker, key: %s", rpcInvocation.ServiceKey()) + } + return result +} diff --git a/protocol/dubbo/listener.go b/protocol/dubbo/listener.go index 0251b78a2b..3d4db11f3d 100644 --- a/protocol/dubbo/listener.go +++ b/protocol/dubbo/listener.go @@ -16,354 +16,354 @@ */ package dubbo - -import ( - "context" - "fmt" - "net/url" - "sync" - "sync/atomic" - "time" -) - -import ( - "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" -) - -// todo: WritePkg_Timeout will entry *.yml -const ( - // WritePkg_Timeout ... - WritePkg_Timeout = 5 * time.Second -) - -var ( - errTooManySessions = perrors.New("too many sessions") -) - -type rpcSession struct { - session getty.Session - reqNum int32 -} - -func (s *rpcSession) AddReqNum(num int32) { - atomic.AddInt32(&s.reqNum, num) -} - -func (s *rpcSession) GetReqNum() int32 { - return atomic.LoadInt32(&s.reqNum) -} - -// ////////////////////////////////////////// -// RpcClientHandler -// ////////////////////////////////////////// - -// RpcClientHandler ... -type RpcClientHandler struct { - conn *gettyRPCClient -} - -// NewRpcClientHandler ... -func NewRpcClientHandler(client *gettyRPCClient) *RpcClientHandler { - return &RpcClientHandler{conn: client} -} - -// OnOpen ... -func (h *RpcClientHandler) OnOpen(session getty.Session) error { - h.conn.addSession(session) - return nil -} - -// OnError ... -func (h *RpcClientHandler) OnError(session getty.Session, err error) { - logger.Infof("session{%s} got error{%v}, will be closed.", session.Stat(), err) - h.conn.removeSession(session) -} - -// OnClose ... -func (h *RpcClientHandler) OnClose(session getty.Session) { - logger.Infof("session{%s} is closing......", session.Stat()) - h.conn.removeSession(session) -} - -// OnMessage ... -func (h *RpcClientHandler) OnMessage(session getty.Session, pkg interface{}) { - p, ok := pkg.(*DubboPackage) - if !ok { - logger.Errorf("illegal package") - return - } - - if p.Header.Type&hessian.PackageHeartbeat != 0x00 { - if p.Header.Type&hessian.PackageResponse != 0x00 { - logger.Debugf("get rpc heartbeat response{header: %#v, body: %#v}", p.Header, p.Body) - if p.Err != nil { - logger.Errorf("rpc heartbeat response{error: %#v}", p.Err) - } - h.conn.pool.rpcClient.removePendingResponse(SequenceType(p.Header.ID)) - } else { - logger.Debugf("get rpc heartbeat request{header: %#v, service: %#v, body: %#v}", p.Header, p.Service, p.Body) - p.Header.ResponseStatus = hessian.Response_OK - reply(session, p, hessian.PackageHeartbeat) - } - return - } - logger.Debugf("get rpc response{header: %#v, body: %#v}", p.Header, p.Body) - - h.conn.updateSession(session) - - pendingResponse := h.conn.pool.rpcClient.removePendingResponse(SequenceType(p.Header.ID)) - if pendingResponse == nil { - logger.Errorf("failed to get pending response context for response package %s", *p) - return - } - - if p.Err != nil { - pendingResponse.err = p.Err - } - - pendingResponse.response.atta = p.Body.(*Response).atta - - if pendingResponse.callback == nil { - pendingResponse.done <- struct{}{} - } else { - pendingResponse.callback(pendingResponse.GetCallResponse()) - } -} - -// OnCron ... -func (h *RpcClientHandler) OnCron(session getty.Session) { - rpcSession, err := h.conn.getClientRpcSession(session) - if err != nil { - logger.Errorf("client.getClientSession(session{%s}) = error{%v}", - session.Stat(), perrors.WithStack(err)) - return - } - if h.conn.pool.rpcClient.conf.sessionTimeout.Nanoseconds() < time.Since(session.GetActive()).Nanoseconds() { - logger.Warnf("session{%s} timeout{%s}, reqNum{%d}", - session.Stat(), time.Since(session.GetActive()).String(), rpcSession.reqNum) - h.conn.removeSession(session) // -> h.conn.close() -> h.conn.pool.remove(h.conn) - return - } - - h.conn.pool.rpcClient.heartbeat(session) -} - -// ////////////////////////////////////////// -// RpcServerHandler -// ////////////////////////////////////////// - -// RpcServerHandler ... -type RpcServerHandler struct { - maxSessionNum int - sessionTimeout time.Duration - sessionMap map[getty.Session]*rpcSession - rwlock sync.RWMutex -} - -// NewRpcServerHandler ... -func NewRpcServerHandler(maxSessionNum int, sessionTimeout time.Duration) *RpcServerHandler { - return &RpcServerHandler{ - maxSessionNum: maxSessionNum, - sessionTimeout: sessionTimeout, - sessionMap: make(map[getty.Session]*rpcSession), - } -} - -// OnOpen ... -func (h *RpcServerHandler) OnOpen(session getty.Session) error { - var err error - h.rwlock.RLock() - if h.maxSessionNum <= len(h.sessionMap) { - err = errTooManySessions - } - h.rwlock.RUnlock() - if err != nil { - return perrors.WithStack(err) - } - - logger.Infof("got session:%s", session.Stat()) - h.rwlock.Lock() - h.sessionMap[session] = &rpcSession{session: session} - h.rwlock.Unlock() - return nil -} - -// OnError ... -func (h *RpcServerHandler) OnError(session getty.Session, err error) { - logger.Infof("session{%s} got error{%v}, will be closed.", session.Stat(), err) - h.rwlock.Lock() - delete(h.sessionMap, session) - h.rwlock.Unlock() -} - -// OnClose ... -func (h *RpcServerHandler) OnClose(session getty.Session) { - logger.Infof("session{%s} is closing......", session.Stat()) - h.rwlock.Lock() - delete(h.sessionMap, session) - h.rwlock.Unlock() -} - -// OnMessage ... -func (h *RpcServerHandler) OnMessage(session getty.Session, pkg interface{}) { - h.rwlock.Lock() - if _, ok := h.sessionMap[session]; ok { - h.sessionMap[session].reqNum++ - } - h.rwlock.Unlock() - - p, ok := pkg.(*DubboPackage) - if !ok { - logger.Errorf("illegal package{%#v}", pkg) - return - } - p.Header.ResponseStatus = hessian.Response_OK - - // heartbeat - if p.Header.Type&hessian.PackageHeartbeat != 0x00 { - logger.Debugf("get rpc heartbeat request{header: %#v, service: %#v, body: %#v}", p.Header, p.Service, p.Body) - reply(session, p, hessian.PackageHeartbeat) - return - } - - twoway := true - // not twoway - if p.Header.Type&hessian.PackageRequest_TwoWay == 0x00 { - twoway = false - } - - defer func() { - if e := recover(); e != nil { - p.Header.ResponseStatus = hessian.Response_SERVER_ERROR - if err, ok := e.(error); ok { - logger.Errorf("OnMessage panic: %+v", perrors.WithStack(err)) - p.Body = perrors.WithStack(err) - } else if err, ok := e.(string); ok { - logger.Errorf("OnMessage panic: %+v", perrors.New(err)) - p.Body = perrors.New(err) - } else { - logger.Errorf("OnMessage panic: %+v, this is impossible.", e) - p.Body = e - } - - if !twoway { - return - } - reply(session, p, hessian.PackageResponse) - } - - }() - - u := common.NewURLWithOptions(common.WithPath(p.Service.Path), common.WithParams(url.Values{}), - common.WithParamsValue(constant.GROUP_KEY, p.Service.Group), - common.WithParamsValue(constant.INTERFACE_KEY, p.Service.Interface), - common.WithParamsValue(constant.VERSION_KEY, p.Service.Version)) - exporter, _ := dubboProtocol.ExporterMap().Load(u.ServiceKey()) - if exporter == nil { - err := fmt.Errorf("don't have this exporter, key: %s", u.ServiceKey()) - logger.Errorf(err.Error()) - p.Header.ResponseStatus = hessian.Response_OK - p.Body = err - reply(session, p, hessian.PackageResponse) - return - } - invoker := exporter.(protocol.Exporter).GetInvoker() - if invoker != nil { - attachments := p.Body.(map[string]interface{})["attachments"].(map[string]string) - attachments[constant.LOCAL_ADDR] = session.LocalAddr() - attachments[constant.REMOTE_ADDR] = session.RemoteAddr() - - args := p.Body.(map[string]interface{})["args"].([]interface{}) - inv := invocation.NewRPCInvocation(p.Service.Method, args, attachments) - - ctx := rebuildCtx(inv) - - result := invoker.Invoke(ctx, inv) - if err := result.Error(); err != nil { - p.Header.ResponseStatus = hessian.Response_OK - p.Body = hessian.NewResponse(nil, err, result.Attachments()) - } else { - res := result.Result() - p.Header.ResponseStatus = hessian.Response_OK - p.Body = hessian.NewResponse(res, nil, result.Attachments()) - } - } - - if !twoway { - return - } - reply(session, p, hessian.PackageResponse) -} - -// OnCron ... -func (h *RpcServerHandler) OnCron(session getty.Session) { - var ( - flag bool - active time.Time - ) - - h.rwlock.RLock() - if _, ok := h.sessionMap[session]; ok { - active = session.GetActive() - if h.sessionTimeout.Nanoseconds() < time.Since(active).Nanoseconds() { - flag = true - logger.Warnf("session{%s} timeout{%s}, reqNum{%d}", - session.Stat(), time.Since(active).String(), h.sessionMap[session].reqNum) - } - } - h.rwlock.RUnlock() - - if flag { - h.rwlock.Lock() - delete(h.sessionMap, session) - h.rwlock.Unlock() - session.Close() - } -} - -// rebuildCtx rebuild the context by attachment. -// Once we decided to transfer more context's key-value, we should change this. -// now we only support rebuild the tracing context -func rebuildCtx(inv *invocation.RPCInvocation) context.Context { - ctx := context.Background() - - // actually, if user do not use any opentracing framework, the err will not be nil. - spanCtx, err := opentracing.GlobalTracer().Extract(opentracing.TextMap, - opentracing.TextMapCarrier(inv.Attachments())) - if err == nil { - ctx = context.WithValue(ctx, constant.TRACING_REMOTE_SPAN_CTX, spanCtx) - } - return ctx -} - -func reply(session getty.Session, req *DubboPackage, tp hessian.PackageType) { - resp := &DubboPackage{ - Header: hessian.DubboHeader{ - SerialID: req.Header.SerialID, - Type: tp, - ID: req.Header.ID, - ResponseStatus: req.Header.ResponseStatus, - }, - } - - if req.Header.Type&hessian.PackageRequest != 0x00 { - resp.Body = req.Body - } else { - resp.Body = nil - } - - if err := session.WritePkg(resp, WritePkg_Timeout); err != nil { - logger.Errorf("WritePkg error: %#v, %#v", perrors.WithStack(err), req.Header) - } -} +// +//import ( +// "context" +// "fmt" +// "net/url" +// "sync" +// "sync/atomic" +// "time" +//) +// +//import ( +// "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" +//) +// +//// todo: WritePkg_Timeout will entry *.yml +//const ( +// // WritePkg_Timeout ... +// WritePkg_Timeout = 5 * time.Second +//) +// +//var ( +// errTooManySessions = perrors.New("too many sessions") +//) +// +//type rpcSession struct { +// session getty.Session +// reqNum int32 +//} +// +//func (s *rpcSession) AddReqNum(num int32) { +// atomic.AddInt32(&s.reqNum, num) +//} +// +//func (s *rpcSession) GetReqNum() int32 { +// return atomic.LoadInt32(&s.reqNum) +//} +// +//// ////////////////////////////////////////// +//// RpcClientHandler +//// ////////////////////////////////////////// +// +//// RpcClientHandler ... +//type RpcClientHandler struct { +// conn *gettyRPCClient +//} +// +//// NewRpcClientHandler ... +//func NewRpcClientHandler(client *gettyRPCClient) *RpcClientHandler { +// return &RpcClientHandler{conn: client} +//} +// +//// OnOpen ... +//func (h *RpcClientHandler) OnOpen(session getty.Session) error { +// h.conn.addSession(session) +// return nil +//} +// +//// OnError ... +//func (h *RpcClientHandler) OnError(session getty.Session, err error) { +// logger.Infof("session{%s} got error{%v}, will be closed.", session.Stat(), err) +// h.conn.removeSession(session) +//} +// +//// OnClose ... +//func (h *RpcClientHandler) OnClose(session getty.Session) { +// logger.Infof("session{%s} is closing......", session.Stat()) +// h.conn.removeSession(session) +//} +// +//// OnMessage ... +//func (h *RpcClientHandler) OnMessage(session getty.Session, pkg interface{}) { +// p, ok := pkg.(*DubboPackage) +// if !ok { +// logger.Errorf("illegal package") +// return +// } +// +// if p.Header.Type&hessian.PackageHeartbeat != 0x00 { +// if p.Header.Type&hessian.PackageResponse != 0x00 { +// logger.Debugf("get rpc heartbeat response{header: %#v, body: %#v}", p.Header, p.Body) +// if p.Err != nil { +// logger.Errorf("rpc heartbeat response{error: %#v}", p.Err) +// } +// h.conn.pool.rpcClient.removePendingResponse(SequenceType(p.Header.ID)) +// } else { +// logger.Debugf("get rpc heartbeat request{header: %#v, service: %#v, body: %#v}", p.Header, p.Service, p.Body) +// p.Header.ResponseStatus = hessian.Response_OK +// reply(session, p, hessian.PackageHeartbeat) +// } +// return +// } +// logger.Debugf("get rpc response{header: %#v, body: %#v}", p.Header, p.Body) +// +// h.conn.updateSession(session) +// +// pendingResponse := h.conn.pool.rpcClient.removePendingResponse(SequenceType(p.Header.ID)) +// if pendingResponse == nil { +// logger.Errorf("failed to get pending response context for response package %s", *p) +// return +// } +// +// if p.Err != nil { +// pendingResponse.err = p.Err +// } +// +// pendingResponse.response.atta = p.Body.(*Response).atta +// +// if pendingResponse.callback == nil { +// pendingResponse.done <- struct{}{} +// } else { +// pendingResponse.callback(pendingResponse.GetCallResponse()) +// } +//} +// +//// OnCron ... +//func (h *RpcClientHandler) OnCron(session getty.Session) { +// rpcSession, err := h.conn.getClientRpcSession(session) +// if err != nil { +// logger.Errorf("client.getClientSession(session{%s}) = error{%v}", +// session.Stat(), perrors.WithStack(err)) +// return +// } +// if h.conn.pool.rpcClient.conf.sessionTimeout.Nanoseconds() < time.Since(session.GetActive()).Nanoseconds() { +// logger.Warnf("session{%s} timeout{%s}, reqNum{%d}", +// session.Stat(), time.Since(session.GetActive()).String(), rpcSession.reqNum) +// h.conn.removeSession(session) // -> h.conn.close() -> h.conn.pool.remove(h.conn) +// return +// } +// +// h.conn.pool.rpcClient.heartbeat(session) +//} +// +//// ////////////////////////////////////////// +//// RpcServerHandler +//// ////////////////////////////////////////// +// +//// RpcServerHandler ... +//type RpcServerHandler struct { +// maxSessionNum int +// sessionTimeout time.Duration +// sessionMap map[getty.Session]*rpcSession +// rwlock sync.RWMutex +//} +// +//// NewRpcServerHandler ... +//func NewRpcServerHandler(maxSessionNum int, sessionTimeout time.Duration) *RpcServerHandler { +// return &RpcServerHandler{ +// maxSessionNum: maxSessionNum, +// sessionTimeout: sessionTimeout, +// sessionMap: make(map[getty.Session]*rpcSession), +// } +//} +// +//// OnOpen ... +//func (h *RpcServerHandler) OnOpen(session getty.Session) error { +// var err error +// h.rwlock.RLock() +// if h.maxSessionNum <= len(h.sessionMap) { +// err = errTooManySessions +// } +// h.rwlock.RUnlock() +// if err != nil { +// return perrors.WithStack(err) +// } +// +// logger.Infof("got session:%s", session.Stat()) +// h.rwlock.Lock() +// h.sessionMap[session] = &rpcSession{session: session} +// h.rwlock.Unlock() +// return nil +//} +// +//// OnError ... +//func (h *RpcServerHandler) OnError(session getty.Session, err error) { +// logger.Infof("session{%s} got error{%v}, will be closed.", session.Stat(), err) +// h.rwlock.Lock() +// delete(h.sessionMap, session) +// h.rwlock.Unlock() +//} +// +//// OnClose ... +//func (h *RpcServerHandler) OnClose(session getty.Session) { +// logger.Infof("session{%s} is closing......", session.Stat()) +// h.rwlock.Lock() +// delete(h.sessionMap, session) +// h.rwlock.Unlock() +//} +// +//// OnMessage ... +//func (h *RpcServerHandler) OnMessage(session getty.Session, pkg interface{}) { +// h.rwlock.Lock() +// if _, ok := h.sessionMap[session]; ok { +// h.sessionMap[session].reqNum++ +// } +// h.rwlock.Unlock() +// +// p, ok := pkg.(*DubboPackage) +// if !ok { +// logger.Errorf("illegal package{%#v}", pkg) +// return +// } +// p.Header.ResponseStatus = hessian.Response_OK +// +// // heartbeat +// if p.Header.Type&hessian.PackageHeartbeat != 0x00 { +// logger.Debugf("get rpc heartbeat request{header: %#v, service: %#v, body: %#v}", p.Header, p.Service, p.Body) +// reply(session, p, hessian.PackageHeartbeat) +// return +// } +// +// twoway := true +// // not twoway +// if p.Header.Type&hessian.PackageRequest_TwoWay == 0x00 { +// twoway = false +// } +// +// defer func() { +// if e := recover(); e != nil { +// p.Header.ResponseStatus = hessian.Response_SERVER_ERROR +// if err, ok := e.(error); ok { +// logger.Errorf("OnMessage panic: %+v", perrors.WithStack(err)) +// p.Body = perrors.WithStack(err) +// } else if err, ok := e.(string); ok { +// logger.Errorf("OnMessage panic: %+v", perrors.New(err)) +// p.Body = perrors.New(err) +// } else { +// logger.Errorf("OnMessage panic: %+v, this is impossible.", e) +// p.Body = e +// } +// +// if !twoway { +// return +// } +// reply(session, p, hessian.PackageResponse) +// } +// +// }() +// +// u := common.NewURLWithOptions(common.WithPath(p.Service.Path), common.WithParams(url.Values{}), +// common.WithParamsValue(constant.GROUP_KEY, p.Service.Group), +// common.WithParamsValue(constant.INTERFACE_KEY, p.Service.Interface), +// common.WithParamsValue(constant.VERSION_KEY, p.Service.Version)) +// exporter, _ := dubboProtocol.ExporterMap().Load(u.ServiceKey()) +// if exporter == nil { +// err := fmt.Errorf("don't have this exporter, key: %s", u.ServiceKey()) +// logger.Errorf(err.Error()) +// p.Header.ResponseStatus = hessian.Response_OK +// p.Body = err +// reply(session, p, hessian.PackageResponse) +// return +// } +// invoker := exporter.(protocol.Exporter).GetInvoker() +// if invoker != nil { +// attachments := p.Body.(map[string]interface{})["attachments"].(map[string]string) +// attachments[constant.LOCAL_ADDR] = session.LocalAddr() +// attachments[constant.REMOTE_ADDR] = session.RemoteAddr() +// +// args := p.Body.(map[string]interface{})["args"].([]interface{}) +// inv := invocation.NewRPCInvocation(p.Service.Method, args, attachments) +// +// ctx := rebuildCtx(inv) +// +// result := invoker.Invoke(ctx, inv) +// if err := result.Error(); err != nil { +// p.Header.ResponseStatus = hessian.Response_OK +// p.Body = hessian.NewResponse(nil, err, result.Attachments()) +// } else { +// res := result.Result() +// p.Header.ResponseStatus = hessian.Response_OK +// p.Body = hessian.NewResponse(res, nil, result.Attachments()) +// } +// } +// +// if !twoway { +// return +// } +// reply(session, p, hessian.PackageResponse) +//} +// +//// OnCron ... +//func (h *RpcServerHandler) OnCron(session getty.Session) { +// var ( +// flag bool +// active time.Time +// ) +// +// h.rwlock.RLock() +// if _, ok := h.sessionMap[session]; ok { +// active = session.GetActive() +// if h.sessionTimeout.Nanoseconds() < time.Since(active).Nanoseconds() { +// flag = true +// logger.Warnf("session{%s} timeout{%s}, reqNum{%d}", +// session.Stat(), time.Since(active).String(), h.sessionMap[session].reqNum) +// } +// } +// h.rwlock.RUnlock() +// +// if flag { +// h.rwlock.Lock() +// delete(h.sessionMap, session) +// h.rwlock.Unlock() +// session.Close() +// } +//} +// +//// rebuildCtx rebuild the context by attachment. +//// Once we decided to transfer more context's key-value, we should change this. +//// now we only support rebuild the tracing context +//func rebuildCtx(inv *invocation.RPCInvocation) context.Context { +// ctx := context.Background() +// +// // actually, if user do not use any opentracing framework, the err will not be nil. +// spanCtx, err := opentracing.GlobalTracer().Extract(opentracing.TextMap, +// opentracing.TextMapCarrier(inv.Attachments())) +// if err == nil { +// ctx = context.WithValue(ctx, constant.TRACING_REMOTE_SPAN_CTX, spanCtx) +// } +// return ctx +//} +// +//func reply(session getty.Session, req *DubboPackage, tp hessian.PackageType) { +// resp := &DubboPackage{ +// Header: hessian.DubboHeader{ +// SerialID: req.Header.SerialID, +// Type: tp, +// ID: req.Header.ID, +// ResponseStatus: req.Header.ResponseStatus, +// }, +// } +// +// if req.Header.Type&hessian.PackageRequest != 0x00 { +// resp.Body = req.Body +// } else { +// resp.Body = nil +// } +// +// if err := session.WritePkg(resp, WritePkg_Timeout); err != nil { +// logger.Errorf("WritePkg error: %#v, %#v", perrors.WithStack(err), req.Header) +// } +//} diff --git a/protocol/dubbo/listener_test.go b/protocol/dubbo/listener_test.go index 5f80981460..1057dbfa36 100644 --- a/protocol/dubbo/listener_test.go +++ b/protocol/dubbo/listener_test.go @@ -16,43 +16,43 @@ */ package dubbo - -import ( - "testing" -) - -import ( - "github.com/opentracing/opentracing-go" - "github.com/opentracing/opentracing-go/mocktracer" - "github.com/stretchr/testify/assert" -) - -import ( - "github.com/apache/dubbo-go/common/constant" - "github.com/apache/dubbo-go/protocol/invocation" -) - -// test rebuild the ctx -func TestRebuildCtx(t *testing.T) { - opentracing.SetGlobalTracer(mocktracer.New()) - attach := make(map[string]string, 10) - attach[constant.VERSION_KEY] = "1.0" - attach[constant.GROUP_KEY] = "MyGroup" - inv := invocation.NewRPCInvocation("MethodName", []interface{}{"OK", "Hello"}, attach) - - // attachment doesn't contains any tracing key-value pair, - ctx := rebuildCtx(inv) - assert.NotNil(t, ctx) - assert.Nil(t, ctx.Value(constant.TRACING_REMOTE_SPAN_CTX)) - - span, ctx := opentracing.StartSpanFromContext(ctx, "Test-Client") - - opentracing.GlobalTracer().Inject(span.Context(), opentracing.TextMap, - opentracing.TextMapCarrier(inv.Attachments())) - // rebuild the context success - inv = invocation.NewRPCInvocation("MethodName", []interface{}{"OK", "Hello"}, attach) - ctx = rebuildCtx(inv) - span.Finish() - assert.NotNil(t, ctx) - assert.NotNil(t, ctx.Value(constant.TRACING_REMOTE_SPAN_CTX)) -} +// +//import ( +// "testing" +//) +// +//import ( +// "github.com/opentracing/opentracing-go" +// "github.com/opentracing/opentracing-go/mocktracer" +// "github.com/stretchr/testify/assert" +//) +// +//import ( +// "github.com/apache/dubbo-go/common/constant" +// "github.com/apache/dubbo-go/protocol/invocation" +//) +// +//// test rebuild the ctx +//func TestRebuildCtx(t *testing.T) { +// opentracing.SetGlobalTracer(mocktracer.New()) +// attach := make(map[string]string, 10) +// attach[constant.VERSION_KEY] = "1.0" +// attach[constant.GROUP_KEY] = "MyGroup" +// inv := invocation.NewRPCInvocation("MethodName", []interface{}{"OK", "Hello"}, attach) +// +// // attachment doesn't contains any tracing key-value pair, +// ctx := rebuildCtx(inv) +// assert.NotNil(t, ctx) +// assert.Nil(t, ctx.Value(constant.TRACING_REMOTE_SPAN_CTX)) +// +// span, ctx := opentracing.StartSpanFromContext(ctx, "Test-Client") +// +// opentracing.GlobalTracer().Inject(span.Context(), opentracing.TextMap, +// opentracing.TextMapCarrier(inv.Attachments())) +// // rebuild the context success +// inv = invocation.NewRPCInvocation("MethodName", []interface{}{"OK", "Hello"}, attach) +// ctx = rebuildCtx(inv) +// span.Finish() +// assert.NotNil(t, ctx) +// assert.NotNil(t, ctx.Value(constant.TRACING_REMOTE_SPAN_CTX)) +//} diff --git a/protocol/dubbo/pool.go b/protocol/dubbo/pool.go index 918514c267..3dda895d55 100644 --- a/protocol/dubbo/pool.go +++ b/protocol/dubbo/pool.go @@ -16,389 +16,389 @@ */ package dubbo - -import ( - "fmt" - "math/rand" - "net" - "sync" - "sync/atomic" - "time" -) - -import ( - "github.com/dubbogo/getty" - perrors "github.com/pkg/errors" -) - -import ( - "github.com/apache/dubbo-go/common/logger" -) - -type gettyRPCClient struct { - once sync.Once - protocol string - addr string - active int64 // zero, not create or be destroyed - - pool *gettyRPCClientPool - - lock sync.RWMutex - gettyClient getty.Client - sessions []*rpcSession -} - -var ( - errClientPoolClosed = perrors.New("client pool closed") -) - -func newGettyRPCClientConn(pool *gettyRPCClientPool, protocol, addr string) (*gettyRPCClient, error) { - c := &gettyRPCClient{ - protocol: protocol, - addr: addr, - pool: pool, - gettyClient: getty.NewTCPClient( - getty.WithServerAddress(addr), - 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) - for { - idx++ - if c.isAvailable() { - break - } - - if idx > times { - c.gettyClient.Close() - return nil, perrors.New(fmt.Sprintf("failed to create client connection to %s in %f seconds", addr, float32(times)/1000)) - } - time.Sleep(1e6) - } - logger.Debug("client init ok") - c.updateActive(time.Now().Unix()) - - return c, nil -} - -func (c *gettyRPCClient) updateActive(active int64) { - atomic.StoreInt64(&c.active, active) -} - -func (c *gettyRPCClient) getActive() int64 { - return atomic.LoadInt64(&c.active) -} - -func (c *gettyRPCClient) newSession(session getty.Session) error { - var ( - ok bool - tcpConn *net.TCPConn - conf ClientConfig - ) - - conf = c.pool.rpcClient.conf - if conf.GettySessionParam.CompressEncoding { - session.SetCompressType(getty.CompressZip) - } - - if tcpConn, ok = session.Conn().(*net.TCPConn); !ok { - panic(fmt.Sprintf("%s, session.conn{%#v} is not tcp connection\n", session.Stat(), session.Conn())) - } - - tcpConn.SetNoDelay(conf.GettySessionParam.TcpNoDelay) - tcpConn.SetKeepAlive(conf.GettySessionParam.TcpKeepAlive) - if conf.GettySessionParam.TcpKeepAlive { - tcpConn.SetKeepAlivePeriod(conf.GettySessionParam.keepAlivePeriod) - } - tcpConn.SetReadBuffer(conf.GettySessionParam.TcpRBufSize) - tcpConn.SetWriteBuffer(conf.GettySessionParam.TcpWBufSize) - - session.SetName(conf.GettySessionParam.SessionName) - session.SetMaxMsgLen(conf.GettySessionParam.MaxMsgLen) - session.SetPkgHandler(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) - logger.Debugf("client new session:%s\n", session.Stat()) - - session.SetTaskPool(clientGrpool) - - return nil -} - -func (c *gettyRPCClient) selectSession() getty.Session { - c.lock.RLock() - defer c.lock.RUnlock() - - if c.sessions == nil { - return nil - } - - count := len(c.sessions) - if count == 0 { - return nil - } - return c.sessions[rand.Int31n(int32(count))].session -} - -func (c *gettyRPCClient) addSession(session getty.Session) { - logger.Debugf("add session{%s}", session.Stat()) - if session == nil { - return - } - - c.lock.Lock() - defer c.lock.Unlock() - if c.sessions == nil { - c.sessions = make([]*rpcSession, 0, 16) - } - c.sessions = append(c.sessions, &rpcSession{session: session}) -} - -func (c *gettyRPCClient) removeSession(session getty.Session) { - if session == nil { - return - } - - var removeFlag bool - func() { - c.lock.Lock() - defer c.lock.Unlock() - if c.sessions == nil { - return - } - - for i, s := range c.sessions { - if s.session == session { - c.sessions = append(c.sessions[:i], c.sessions[i+1:]...) - logger.Debugf("delete session{%s}, its index{%d}", session.Stat(), i) - break - } - } - logger.Infof("after remove session{%s}, left session number:%d", session.Stat(), len(c.sessions)) - if len(c.sessions) == 0 { - removeFlag = true - } - }() - if removeFlag { - c.pool.safeRemove(c) - c.close() - } -} - -func (c *gettyRPCClient) updateSession(session getty.Session) { - if session == nil { - return - } - - var rs *rpcSession - func() { - c.lock.RLock() - defer c.lock.RUnlock() - if c.sessions == nil { - return - } - - for i, s := range c.sessions { - if s.session == session { - rs = c.sessions[i] - break - } - } - }() - if rs != nil { - rs.AddReqNum(1) - } -} - -func (c *gettyRPCClient) getClientRpcSession(session getty.Session) (rpcSession, error) { - var ( - err error - rpcSession rpcSession - ) - c.lock.RLock() - defer c.lock.RUnlock() - if c.sessions == nil { - return rpcSession, errClientClosed - } - - err = errSessionNotExist - for _, s := range c.sessions { - if s.session == session { - rpcSession = *s - err = nil - break - } - } - - return rpcSession, perrors.WithStack(err) -} - -func (c *gettyRPCClient) isAvailable() bool { - if c.selectSession() == nil { - return false - } - - return true -} - -func (c *gettyRPCClient) close() error { - closeErr := perrors.Errorf("close gettyRPCClient{%#v} again", c) - c.once.Do(func() { - var ( - gettyClient getty.Client - sessions []*rpcSession - ) - func() { - c.lock.Lock() - defer c.lock.Unlock() - - gettyClient = c.gettyClient - c.gettyClient = nil - - sessions = make([]*rpcSession, 0, len(c.sessions)) - for _, s := range c.sessions { - sessions = append(sessions, s) - } - c.sessions = c.sessions[:0] - }() - - c.updateActive(0) - - go func() { - if gettyClient != nil { - gettyClient.Close() - } - for _, s := range sessions { - logger.Infof("close client session{%s, last active:%s, request number:%d}", - s.session.Stat(), s.session.GetActive().String(), s.GetReqNum()) - s.session.Close() - } - }() - - closeErr = nil - }) - return closeErr -} - -type gettyRPCClientPool struct { - rpcClient *Client - size int // size of []*gettyRPCClient - ttl int64 // ttl of every gettyRPCClient, it is checked when getConn - - sync.Mutex - conns []*gettyRPCClient -} - -func newGettyRPCClientConnPool(rpcClient *Client, size int, ttl time.Duration) *gettyRPCClientPool { - return &gettyRPCClientPool{ - rpcClient: rpcClient, - size: size, - ttl: int64(ttl.Seconds()), - conns: make([]*gettyRPCClient, 0, 16), - } -} - -func (p *gettyRPCClientPool) close() { - p.Lock() - conns := p.conns - p.conns = nil - p.Unlock() - for _, conn := range conns { - conn.close() - } -} - -func (p *gettyRPCClientPool) getGettyRpcClient(protocol, addr string) (*gettyRPCClient, error) { - conn, err := p.get() - if err == nil && conn == nil { - // create new conn - rpcClientConn, err := newGettyRPCClientConn(p, protocol, addr) - return rpcClientConn, perrors.WithStack(err) - } - return conn, perrors.WithStack(err) -} - -func (p *gettyRPCClientPool) get() (*gettyRPCClient, error) { - now := time.Now().Unix() - - p.Lock() - defer p.Unlock() - if p.conns == nil { - return nil, errClientPoolClosed - } - - for len(p.conns) > 0 { - conn := p.conns[len(p.conns)-1] - p.conns = p.conns[:len(p.conns)-1] - - if d := now - conn.getActive(); d > p.ttl { - p.remove(conn) - go conn.close() - continue - } - conn.updateActive(now) //update active time - return conn, nil - } - return nil, nil -} - -func (p *gettyRPCClientPool) put(conn *gettyRPCClient) { - if conn == nil || conn.getActive() == 0 { - return - } - - p.Lock() - defer p.Unlock() - - if p.conns == nil { - return - } - - // check whether @conn has existed in p.conns or not. - for i := range p.conns { - if p.conns[i] == conn { - return - } - } - - if len(p.conns) >= p.size { - // delete @conn from client pool - // p.remove(conn) - conn.close() - return - } - p.conns = append(p.conns, conn) -} - -func (p *gettyRPCClientPool) remove(conn *gettyRPCClient) { - if conn == nil || conn.getActive() == 0 { - return - } - - if p.conns == nil { - return - } - - if len(p.conns) > 0 { - for idx, c := range p.conns { - if conn == c { - p.conns = append(p.conns[:idx], p.conns[idx+1:]...) - break - } - } - } -} - -func (p *gettyRPCClientPool) safeRemove(conn *gettyRPCClient) { - p.Lock() - defer p.Unlock() - - p.remove(conn) -} +// +//import ( +// "fmt" +// "math/rand" +// "net" +// "sync" +// "sync/atomic" +// "time" +//) +// +//import ( +// "github.com/dubbogo/getty" +// perrors "github.com/pkg/errors" +//) +// +//import ( +// "github.com/apache/dubbo-go/common/logger" +//) +// +//type gettyRPCClient struct { +// once sync.Once +// protocol string +// addr string +// active int64 // zero, not create or be destroyed +// +// pool *gettyRPCClientPool +// +// lock sync.RWMutex +// gettyClient getty.Client +// sessions []*rpcSession +//} +// +//var ( +// errClientPoolClosed = perrors.New("client pool closed") +//) +// +//func newGettyRPCClientConn(pool *gettyRPCClientPool, protocol, addr string) (*gettyRPCClient, error) { +// c := &gettyRPCClient{ +// protocol: protocol, +// addr: addr, +// pool: pool, +// gettyClient: getty.NewTCPClient( +// getty.WithServerAddress(addr), +// 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) +// for { +// idx++ +// if c.isAvailable() { +// break +// } +// +// if idx > times { +// c.gettyClient.Close() +// return nil, perrors.New(fmt.Sprintf("failed to create client connection to %s in %f seconds", addr, float32(times)/1000)) +// } +// time.Sleep(1e6) +// } +// logger.Debug("client init ok") +// c.updateActive(time.Now().Unix()) +// +// return c, nil +//} +// +//func (c *gettyRPCClient) updateActive(active int64) { +// atomic.StoreInt64(&c.active, active) +//} +// +//func (c *gettyRPCClient) getActive() int64 { +// return atomic.LoadInt64(&c.active) +//} +// +//func (c *gettyRPCClient) newSession(session getty.Session) error { +// var ( +// ok bool +// tcpConn *net.TCPConn +// conf ClientConfig +// ) +// +// conf = c.pool.rpcClient.conf +// if conf.GettySessionParam.CompressEncoding { +// session.SetCompressType(getty.CompressZip) +// } +// +// if tcpConn, ok = session.Conn().(*net.TCPConn); !ok { +// panic(fmt.Sprintf("%s, session.conn{%#v} is not tcp connection\n", session.Stat(), session.Conn())) +// } +// +// tcpConn.SetNoDelay(conf.GettySessionParam.TcpNoDelay) +// tcpConn.SetKeepAlive(conf.GettySessionParam.TcpKeepAlive) +// if conf.GettySessionParam.TcpKeepAlive { +// tcpConn.SetKeepAlivePeriod(conf.GettySessionParam.keepAlivePeriod) +// } +// tcpConn.SetReadBuffer(conf.GettySessionParam.TcpRBufSize) +// tcpConn.SetWriteBuffer(conf.GettySessionParam.TcpWBufSize) +// +// session.SetName(conf.GettySessionParam.SessionName) +// session.SetMaxMsgLen(conf.GettySessionParam.MaxMsgLen) +// session.SetPkgHandler(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) +// logger.Debugf("client new session:%s\n", session.Stat()) +// +// session.SetTaskPool(clientGrpool) +// +// return nil +//} +// +//func (c *gettyRPCClient) selectSession() getty.Session { +// c.lock.RLock() +// defer c.lock.RUnlock() +// +// if c.sessions == nil { +// return nil +// } +// +// count := len(c.sessions) +// if count == 0 { +// return nil +// } +// return c.sessions[rand.Int31n(int32(count))].session +//} +// +//func (c *gettyRPCClient) addSession(session getty.Session) { +// logger.Debugf("add session{%s}", session.Stat()) +// if session == nil { +// return +// } +// +// c.lock.Lock() +// defer c.lock.Unlock() +// if c.sessions == nil { +// c.sessions = make([]*rpcSession, 0, 16) +// } +// c.sessions = append(c.sessions, &rpcSession{session: session}) +//} +// +//func (c *gettyRPCClient) removeSession(session getty.Session) { +// if session == nil { +// return +// } +// +// var removeFlag bool +// func() { +// c.lock.Lock() +// defer c.lock.Unlock() +// if c.sessions == nil { +// return +// } +// +// for i, s := range c.sessions { +// if s.session == session { +// c.sessions = append(c.sessions[:i], c.sessions[i+1:]...) +// logger.Debugf("delete session{%s}, its index{%d}", session.Stat(), i) +// break +// } +// } +// logger.Infof("after remove session{%s}, left session number:%d", session.Stat(), len(c.sessions)) +// if len(c.sessions) == 0 { +// removeFlag = true +// } +// }() +// if removeFlag { +// c.pool.safeRemove(c) +// c.close() +// } +//} +// +//func (c *gettyRPCClient) updateSession(session getty.Session) { +// if session == nil { +// return +// } +// +// var rs *rpcSession +// func() { +// c.lock.RLock() +// defer c.lock.RUnlock() +// if c.sessions == nil { +// return +// } +// +// for i, s := range c.sessions { +// if s.session == session { +// rs = c.sessions[i] +// break +// } +// } +// }() +// if rs != nil { +// rs.AddReqNum(1) +// } +//} +// +//func (c *gettyRPCClient) getClientRpcSession(session getty.Session) (rpcSession, error) { +// var ( +// err error +// rpcSession rpcSession +// ) +// c.lock.RLock() +// defer c.lock.RUnlock() +// if c.sessions == nil { +// return rpcSession, errClientClosed +// } +// +// err = errSessionNotExist +// for _, s := range c.sessions { +// if s.session == session { +// rpcSession = *s +// err = nil +// break +// } +// } +// +// return rpcSession, perrors.WithStack(err) +//} +// +//func (c *gettyRPCClient) isAvailable() bool { +// if c.selectSession() == nil { +// return false +// } +// +// return true +//} +// +//func (c *gettyRPCClient) close() error { +// closeErr := perrors.Errorf("close gettyRPCClient{%#v} again", c) +// c.once.Do(func() { +// var ( +// gettyClient getty.Client +// sessions []*rpcSession +// ) +// func() { +// c.lock.Lock() +// defer c.lock.Unlock() +// +// gettyClient = c.gettyClient +// c.gettyClient = nil +// +// sessions = make([]*rpcSession, 0, len(c.sessions)) +// for _, s := range c.sessions { +// sessions = append(sessions, s) +// } +// c.sessions = c.sessions[:0] +// }() +// +// c.updateActive(0) +// +// go func() { +// if gettyClient != nil { +// gettyClient.Close() +// } +// for _, s := range sessions { +// logger.Infof("close client session{%s, last active:%s, request number:%d}", +// s.session.Stat(), s.session.GetActive().String(), s.GetReqNum()) +// s.session.Close() +// } +// }() +// +// closeErr = nil +// }) +// return closeErr +//} +// +//type gettyRPCClientPool struct { +// rpcClient *Client +// size int // size of []*gettyRPCClient +// ttl int64 // ttl of every gettyRPCClient, it is checked when getConn +// +// sync.Mutex +// conns []*gettyRPCClient +//} +// +//func newGettyRPCClientConnPool(rpcClient *Client, size int, ttl time.Duration) *gettyRPCClientPool { +// return &gettyRPCClientPool{ +// rpcClient: rpcClient, +// size: size, +// ttl: int64(ttl.Seconds()), +// conns: make([]*gettyRPCClient, 0, 16), +// } +//} +// +//func (p *gettyRPCClientPool) close() { +// p.Lock() +// conns := p.conns +// p.conns = nil +// p.Unlock() +// for _, conn := range conns { +// conn.close() +// } +//} +// +//func (p *gettyRPCClientPool) getGettyRpcClient(protocol, addr string) (*gettyRPCClient, error) { +// conn, err := p.get() +// if err == nil && conn == nil { +// // create new conn +// rpcClientConn, err := newGettyRPCClientConn(p, protocol, addr) +// return rpcClientConn, perrors.WithStack(err) +// } +// return conn, perrors.WithStack(err) +//} +// +//func (p *gettyRPCClientPool) get() (*gettyRPCClient, error) { +// now := time.Now().Unix() +// +// p.Lock() +// defer p.Unlock() +// if p.conns == nil { +// return nil, errClientPoolClosed +// } +// +// for len(p.conns) > 0 { +// conn := p.conns[len(p.conns)-1] +// p.conns = p.conns[:len(p.conns)-1] +// +// if d := now - conn.getActive(); d > p.ttl { +// p.remove(conn) +// go conn.close() +// continue +// } +// conn.updateActive(now) //update active time +// return conn, nil +// } +// return nil, nil +//} +// +//func (p *gettyRPCClientPool) put(conn *gettyRPCClient) { +// if conn == nil || conn.getActive() == 0 { +// return +// } +// +// p.Lock() +// defer p.Unlock() +// +// if p.conns == nil { +// return +// } +// +// // check whether @conn has existed in p.conns or not. +// for i := range p.conns { +// if p.conns[i] == conn { +// return +// } +// } +// +// if len(p.conns) >= p.size { +// // delete @conn from client pool +// // p.remove(conn) +// conn.close() +// return +// } +// p.conns = append(p.conns, conn) +//} +// +//func (p *gettyRPCClientPool) remove(conn *gettyRPCClient) { +// if conn == nil || conn.getActive() == 0 { +// return +// } +// +// if p.conns == nil { +// return +// } +// +// if len(p.conns) > 0 { +// for idx, c := range p.conns { +// if conn == c { +// p.conns = append(p.conns[:idx], p.conns[idx+1:]...) +// break +// } +// } +// } +//} +// +//func (p *gettyRPCClientPool) safeRemove(conn *gettyRPCClient) { +// p.Lock() +// defer p.Unlock() +// +// p.remove(conn) +//} diff --git a/protocol/dubbo/readwriter.go b/protocol/dubbo/readwriter.go index b5c4f50919..00b79e47f3 100644 --- a/protocol/dubbo/readwriter.go +++ b/protocol/dubbo/readwriter.go @@ -16,171 +16,171 @@ */ package dubbo - -import ( - "bytes" - "reflect" -) - -import ( - "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" -) - -//////////////////////////////////////////// -// RpcClientPackageHandler -//////////////////////////////////////////// - -// RpcClientPackageHandler ... -type RpcClientPackageHandler struct { - client *Client -} - -// NewRpcClientPackageHandler ... -func NewRpcClientPackageHandler(client *Client) *RpcClientPackageHandler { - return &RpcClientPackageHandler{client: client} -} - -func (p *RpcClientPackageHandler) Read(ss getty.Session, data []byte) (interface{}, int, error) { - pkg := &DubboPackage{} - - buf := bytes.NewBuffer(data) - err := pkg.Unmarshal(buf, p.client) - if err != nil { - originErr := perrors.Cause(err) - if originErr == hessian.ErrHeaderNotEnough || originErr == hessian.ErrBodyNotEnough { - return nil, 0, nil - } - - logger.Errorf("pkg.Unmarshal(ss:%+v, len(@data):%d) = error:%+v", ss, len(data), err) - - return nil, 0, perrors.WithStack(err) - } - - if pkg.Header.Type&hessian.PackageRequest == 0x00 { - pkg.Err = pkg.Body.(*hessian.Response).Exception - pkg.Body = NewResponse(pkg.Body.(*hessian.Response).RspObj, pkg.Body.(*hessian.Response).Attachments) - } - - return pkg, hessian.HEADER_LENGTH + pkg.Header.BodyLen, nil -} - -func (p *RpcClientPackageHandler) Write(ss getty.Session, pkg interface{}) ([]byte, error) { - req, ok := pkg.(*DubboPackage) - if !ok { - logger.Errorf("illegal pkg:%+v\n", pkg) - return nil, perrors.New("invalid rpc request") - } - - buf, err := req.Marshal() - if err != nil { - logger.Warnf("binary.Write(req{%#v}) = err{%#v}", req, perrors.WithStack(err)) - return nil, perrors.WithStack(err) - } - - return buf.Bytes(), nil -} - -//////////////////////////////////////////// -// RpcServerPackageHandler -//////////////////////////////////////////// - -var ( - rpcServerPkgHandler = &RpcServerPackageHandler{} -) - -// RpcServerPackageHandler ... -type RpcServerPackageHandler struct{} - -func (p *RpcServerPackageHandler) Read(ss getty.Session, data []byte) (interface{}, int, error) { - pkg := &DubboPackage{ - Body: make([]interface{}, 7), - } - - buf := bytes.NewBuffer(data) - err := pkg.Unmarshal(buf) - if err != nil { - originErr := perrors.Cause(err) - if originErr == hessian.ErrHeaderNotEnough || originErr == hessian.ErrBodyNotEnough { - return nil, 0, nil - } - - logger.Errorf("pkg.Unmarshal(ss:%+v, len(@data):%d) = error:%+v", ss, len(data), err) - - return nil, 0, perrors.WithStack(err) - } - - if pkg.Header.Type&hessian.PackageHeartbeat == 0x00 { - // convert params of request - req := pkg.Body.([]interface{}) // length of body should be 7 - if len(req) > 0 { - var dubboVersion, argsTypes string - var args []interface{} - var attachments map[string]string - if req[0] != nil { - dubboVersion = req[0].(string) - } - if req[1] != nil { - pkg.Service.Path = req[1].(string) - } - if req[2] != nil { - pkg.Service.Version = req[2].(string) - } - if req[3] != nil { - pkg.Service.Method = req[3].(string) - } - if req[4] != nil { - argsTypes = req[4].(string) - } - if req[5] != nil { - args = req[5].([]interface{}) - } - if req[6] != nil { - attachments = req[6].(map[string]string) - } - if pkg.Service.Path == "" && len(attachments[constant.PATH_KEY]) > 0 { - pkg.Service.Path = attachments[constant.PATH_KEY] - } - if _, ok := attachments[constant.INTERFACE_KEY]; ok { - pkg.Service.Interface = attachments[constant.INTERFACE_KEY] - } else { - pkg.Service.Interface = pkg.Service.Path - } - if len(attachments[constant.GROUP_KEY]) > 0 { - pkg.Service.Group = attachments[constant.GROUP_KEY] - } - pkg.Body = map[string]interface{}{ - "dubboVersion": dubboVersion, - "argsTypes": argsTypes, - "args": args, - "service": common.ServiceMap.GetService(DUBBO, pkg.Service.Path), // path as a key - "attachments": attachments, - } - } - } - - return pkg, hessian.HEADER_LENGTH + pkg.Header.BodyLen, nil -} - -func (p *RpcServerPackageHandler) Write(ss getty.Session, pkg interface{}) ([]byte, error) { - res, ok := pkg.(*DubboPackage) - if !ok { - logger.Errorf("illegal pkg:%+v\n, it is %+v", pkg, reflect.TypeOf(pkg)) - return nil, perrors.New("invalid rpc response") - } - - buf, err := res.Marshal() - if err != nil { - logger.Warnf("binary.Write(res{%#v}) = err{%#v}", res, perrors.WithStack(err)) - return nil, perrors.WithStack(err) - } - - return buf.Bytes(), nil -} +// +//import ( +// "bytes" +// "reflect" +//) +// +//import ( +// "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" +//) +// +////////////////////////////////////////////// +//// RpcClientPackageHandler +////////////////////////////////////////////// +// +//// RpcClientPackageHandler ... +//type RpcClientPackageHandler struct { +// client *Client +//} +// +//// NewRpcClientPackageHandler ... +//func NewRpcClientPackageHandler(client *Client) *RpcClientPackageHandler { +// return &RpcClientPackageHandler{client: client} +//} +// +//func (p *RpcClientPackageHandler) Read(ss getty.Session, data []byte) (interface{}, int, error) { +// pkg := &DubboPackage{} +// +// buf := bytes.NewBuffer(data) +// err := pkg.Unmarshal(buf, p.client) +// if err != nil { +// originErr := perrors.Cause(err) +// if originErr == hessian.ErrHeaderNotEnough || originErr == hessian.ErrBodyNotEnough { +// return nil, 0, nil +// } +// +// logger.Errorf("pkg.Unmarshal(ss:%+v, len(@data):%d) = error:%+v", ss, len(data), err) +// +// return nil, 0, perrors.WithStack(err) +// } +// +// if pkg.Header.Type&hessian.PackageRequest == 0x00 { +// pkg.Err = pkg.Body.(*hessian.Response).Exception +// pkg.Body = NewResponse(pkg.Body.(*hessian.Response).RspObj, pkg.Body.(*hessian.Response).Attachments) +// } +// +// return pkg, hessian.HEADER_LENGTH + pkg.Header.BodyLen, nil +//} +// +//func (p *RpcClientPackageHandler) Write(ss getty.Session, pkg interface{}) ([]byte, error) { +// req, ok := pkg.(*DubboPackage) +// if !ok { +// logger.Errorf("illegal pkg:%+v\n", pkg) +// return nil, perrors.New("invalid rpc request") +// } +// +// buf, err := req.Marshal() +// if err != nil { +// logger.Warnf("binary.Write(req{%#v}) = err{%#v}", req, perrors.WithStack(err)) +// return nil, perrors.WithStack(err) +// } +// +// return buf.Bytes(), nil +//} +// +////////////////////////////////////////////// +//// RpcServerPackageHandler +////////////////////////////////////////////// +// +//var ( +// rpcServerPkgHandler = &RpcServerPackageHandler{} +//) +// +//// RpcServerPackageHandler ... +//type RpcServerPackageHandler struct{} +// +//func (p *RpcServerPackageHandler) Read(ss getty.Session, data []byte) (interface{}, int, error) { +// pkg := &DubboPackage{ +// Body: make([]interface{}, 7), +// } +// +// buf := bytes.NewBuffer(data) +// err := pkg.Unmarshal(buf) +// if err != nil { +// originErr := perrors.Cause(err) +// if originErr == hessian.ErrHeaderNotEnough || originErr == hessian.ErrBodyNotEnough { +// return nil, 0, nil +// } +// +// logger.Errorf("pkg.Unmarshal(ss:%+v, len(@data):%d) = error:%+v", ss, len(data), err) +// +// return nil, 0, perrors.WithStack(err) +// } +// +// if pkg.Header.Type&hessian.PackageHeartbeat == 0x00 { +// // convert params of request +// req := pkg.Body.([]interface{}) // length of body should be 7 +// if len(req) > 0 { +// var dubboVersion, argsTypes string +// var args []interface{} +// var attachments map[string]string +// if req[0] != nil { +// dubboVersion = req[0].(string) +// } +// if req[1] != nil { +// pkg.Service.Path = req[1].(string) +// } +// if req[2] != nil { +// pkg.Service.Version = req[2].(string) +// } +// if req[3] != nil { +// pkg.Service.Method = req[3].(string) +// } +// if req[4] != nil { +// argsTypes = req[4].(string) +// } +// if req[5] != nil { +// args = req[5].([]interface{}) +// } +// if req[6] != nil { +// attachments = req[6].(map[string]string) +// } +// if pkg.Service.Path == "" && len(attachments[constant.PATH_KEY]) > 0 { +// pkg.Service.Path = attachments[constant.PATH_KEY] +// } +// if _, ok := attachments[constant.INTERFACE_KEY]; ok { +// pkg.Service.Interface = attachments[constant.INTERFACE_KEY] +// } else { +// pkg.Service.Interface = pkg.Service.Path +// } +// if len(attachments[constant.GROUP_KEY]) > 0 { +// pkg.Service.Group = attachments[constant.GROUP_KEY] +// } +// pkg.Body = map[string]interface{}{ +// "dubboVersion": dubboVersion, +// "argsTypes": argsTypes, +// "args": args, +// "service": common.ServiceMap.GetService(DUBBO, pkg.Service.Path), // path as a key +// "attachments": attachments, +// } +// } +// } +// +// return pkg, hessian.HEADER_LENGTH + pkg.Header.BodyLen, nil +//} +// +//func (p *RpcServerPackageHandler) Write(ss getty.Session, pkg interface{}) ([]byte, error) { +// res, ok := pkg.(*DubboPackage) +// if !ok { +// logger.Errorf("illegal pkg:%+v\n, it is %+v", pkg, reflect.TypeOf(pkg)) +// return nil, perrors.New("invalid rpc response") +// } +// +// buf, err := res.Marshal() +// if err != nil { +// logger.Warnf("binary.Write(res{%#v}) = err{%#v}", res, perrors.WithStack(err)) +// return nil, perrors.WithStack(err) +// } +// +// return buf.Bytes(), nil +//} diff --git a/protocol/dubbo/server.go b/protocol/dubbo/server.go index bd2b37b7a9..35a162cc37 100644 --- a/protocol/dubbo/server.go +++ b/protocol/dubbo/server.go @@ -16,164 +16,164 @@ */ package dubbo - -import ( - "fmt" - "net" -) - -import ( - "github.com/dubbogo/getty" - "github.com/dubbogo/gost/sync" - "gopkg.in/yaml.v2" -) - -import ( - "github.com/apache/dubbo-go/common" - "github.com/apache/dubbo-go/common/logger" - "github.com/apache/dubbo-go/config" -) - -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 ... -func SetServerConfig(s ServerConfig) { - srvConf = &s - err := srvConf.CheckValidity() - if err != nil { - logger.Warnf("[ServerConfig CheckValidity] error: %v", err) - return - } - SetServerGrpool() -} - -// GetServerConfig ... -func GetServerConfig() ServerConfig { - return *srvConf -} - -// SetServerGrpool ... -func SetServerGrpool() { - if srvConf.GrPoolSize > 1 { - srvGrpool = gxsync.NewTaskPool(gxsync.WithTaskPoolTaskPoolSize(srvConf.GrPoolSize), gxsync.WithTaskPoolTaskQueueLength(srvConf.QueueLen), - gxsync.WithTaskPoolTaskQueueNumber(srvConf.QueueNumber)) - } -} - -// Server ... -type Server struct { - conf ServerConfig - tcpServer getty.Server - rpcHandler *RpcServerHandler -} - -// NewServer ... -func NewServer() *Server { - - s := &Server{ - conf: *srvConf, - } - - s.rpcHandler = NewRpcServerHandler(s.conf.SessionNumber, s.conf.sessionTimeout) - - return s -} - -func (s *Server) newSession(session getty.Session) error { - var ( - ok bool - tcpConn *net.TCPConn - ) - conf := s.conf - - if conf.GettySessionParam.CompressEncoding { - session.SetCompressType(getty.CompressZip) - } - - if tcpConn, ok = session.Conn().(*net.TCPConn); !ok { - panic(fmt.Sprintf("%s, session.conn{%#v} is not tcp connection\n", session.Stat(), session.Conn())) - } - - tcpConn.SetNoDelay(conf.GettySessionParam.TcpNoDelay) - tcpConn.SetKeepAlive(conf.GettySessionParam.TcpKeepAlive) - if conf.GettySessionParam.TcpKeepAlive { - tcpConn.SetKeepAlivePeriod(conf.GettySessionParam.keepAlivePeriod) - } - tcpConn.SetReadBuffer(conf.GettySessionParam.TcpRBufSize) - tcpConn.SetWriteBuffer(conf.GettySessionParam.TcpWBufSize) - - session.SetName(conf.GettySessionParam.SessionName) - session.SetMaxMsgLen(conf.GettySessionParam.MaxMsgLen) - session.SetPkgHandler(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 -} - -// Start ... -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 ... -func (s *Server) Stop() { - s.tcpServer.Close() -} +// +//import ( +// "fmt" +// "net" +//) +// +//import ( +// "github.com/dubbogo/getty" +// "github.com/dubbogo/gost/sync" +// "gopkg.in/yaml.v2" +//) +// +//import ( +// "github.com/apache/dubbo-go/common" +// "github.com/apache/dubbo-go/common/logger" +// "github.com/apache/dubbo-go/config" +//) +// +//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 ... +//func SetServerConfig(s ServerConfig) { +// srvConf = &s +// err := srvConf.CheckValidity() +// if err != nil { +// logger.Warnf("[ServerConfig CheckValidity] error: %v", err) +// return +// } +// SetServerGrpool() +//} +// +//// GetServerConfig ... +//func GetServerConfig() ServerConfig { +// return *srvConf +//} +// +//// SetServerGrpool ... +//func SetServerGrpool() { +// if srvConf.GrPoolSize > 1 { +// srvGrpool = gxsync.NewTaskPool(gxsync.WithTaskPoolTaskPoolSize(srvConf.GrPoolSize), gxsync.WithTaskPoolTaskQueueLength(srvConf.QueueLen), +// gxsync.WithTaskPoolTaskQueueNumber(srvConf.QueueNumber)) +// } +//} +// +//// Server ... +//type Server struct { +// conf ServerConfig +// tcpServer getty.Server +// rpcHandler *RpcServerHandler +//} +// +//// NewServer ... +//func NewServer() *Server { +// +// s := &Server{ +// conf: *srvConf, +// } +// +// s.rpcHandler = NewRpcServerHandler(s.conf.SessionNumber, s.conf.sessionTimeout) +// +// return s +//} +// +//func (s *Server) newSession(session getty.Session) error { +// var ( +// ok bool +// tcpConn *net.TCPConn +// ) +// conf := s.conf +// +// if conf.GettySessionParam.CompressEncoding { +// session.SetCompressType(getty.CompressZip) +// } +// +// if tcpConn, ok = session.Conn().(*net.TCPConn); !ok { +// panic(fmt.Sprintf("%s, session.conn{%#v} is not tcp connection\n", session.Stat(), session.Conn())) +// } +// +// tcpConn.SetNoDelay(conf.GettySessionParam.TcpNoDelay) +// tcpConn.SetKeepAlive(conf.GettySessionParam.TcpKeepAlive) +// if conf.GettySessionParam.TcpKeepAlive { +// tcpConn.SetKeepAlivePeriod(conf.GettySessionParam.keepAlivePeriod) +// } +// tcpConn.SetReadBuffer(conf.GettySessionParam.TcpRBufSize) +// tcpConn.SetWriteBuffer(conf.GettySessionParam.TcpWBufSize) +// +// session.SetName(conf.GettySessionParam.SessionName) +// session.SetMaxMsgLen(conf.GettySessionParam.MaxMsgLen) +// session.SetPkgHandler(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 +//} +// +//// Start ... +//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 ... +//func (s *Server) Stop() { +// s.tcpServer.Close() +//} diff --git a/protocol/invocation/rpcinvocation.go b/protocol/invocation/rpcinvocation.go index b207fd0b0c..e924a77da3 100644 --- a/protocol/invocation/rpcinvocation.go +++ b/protocol/invocation/rpcinvocation.go @@ -18,6 +18,8 @@ package invocation import ( + "bytes" + "github.com/apache/dubbo-go/common/constant" "reflect" "sync" ) @@ -141,6 +143,29 @@ func (r *RPCInvocation) SetCallBack(c interface{}) { r.callBack = c } +func (r *RPCInvocation) ServiceKey() string { + intf := r.AttachmentsByKey(constant.INTERFACE_KEY, "") + if intf == "" { + return "" + } + buf := &bytes.Buffer{} + group := r.AttachmentsByKey(constant.GROUP_KEY, "") + if group != "" { + buf.WriteString(group) + buf.WriteString("/") + } + + buf.WriteString(intf) + + version := r.AttachmentsByKey(constant.VERSION_KEY, "") + if version != "" && version != "0.0.0" { + buf.WriteString(":") + buf.WriteString(version) + } + + return buf.String() +} + // ///////////////////////// // option // ///////////////////////// diff --git a/remoting/codec.go b/remoting/codec.go new file mode 100644 index 0000000000..70071bdec5 --- /dev/null +++ b/remoting/codec.go @@ -0,0 +1,28 @@ +package remoting + +import ( + "bytes" +) + +type Codec interface { + EncodeRequest(request *Request) (*bytes.Buffer, error) + EncodeResponse(response *Response) (*bytes.Buffer, error) + DecodeRequest(*bytes.Buffer) (*Request, int, error) + DecodeResponse(*bytes.Buffer) (*Response, int, error) +} + +var ( + codec map[string]*Codec +) + +func init() { + codec = make(map[string]*Codec, 2) +} + +func NewCodec(protocol string, codecTmp *Codec) { + codec[protocol] = codecTmp +} + +func GetCodec(protocol string) *Codec { + return codec[protocol] +} diff --git a/remoting/exchange.go b/remoting/exchange.go new file mode 100644 index 0000000000..f63c9119f7 --- /dev/null +++ b/remoting/exchange.go @@ -0,0 +1,131 @@ +package remoting + +import ( + "github.com/apache/dubbo-go/common" + "go.uber.org/atomic" + "time" +) + +var ( + sequence atomic.Uint64 +) + +func init() { + sequence.Store(0) +} + +func SequenceId() uint64 { + return sequence.Add(2) +} + +// Request ... +type Request struct { + Id int64 + Version string + SerialID byte + Data interface{} + TwoWay bool + Event bool + broken bool +} + +// NewRequest ... +func NewRequest(version string) *Request { + return &Request{ + Id: int64(SequenceId()), + Version: version, + } +} + +func (request *Request) SetHeartbeat(isHeartbeat bool) { + if isHeartbeat { + + } +} + +// Response ... +type Response struct { + Id int64 + Version string + SerialID byte + Status uint8 + Event bool + Error error + Result interface{} + Reply interface{} +} + +// NewResponse ... +func NewResponse(id int64, version string) *Response { + return &Response{ + Id: id, + Version: version, + } +} + +func (response *Response) IsHeartbeat() bool { + return response.Event && response.Result == nil +} + +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{} +} + +type PendingResponse struct { + seq int64 + Err error + start time.Time + ReadStart time.Time + callback common.AsyncCallback + response *Response + Done chan struct{} +} + +// NewPendingResponse ... +func NewPendingResponse() *PendingResponse { + return &PendingResponse{ + start: time.Now(), + response: &Response{}, + Done: make(chan struct{}), + } +} + +// GetCallResponse ... +func (r PendingResponse) GetCallResponse() common.CallbackResponse { + return AsyncCallbackResponse{ + Cause: r.Err, + Start: r.start, + ReadStart: r.ReadStart, + Reply: r.response, + } +} + +type Client interface { + //invoke once for connection + Connect(url common.URL) + Close() + Request(request *Request, timeout time.Duration, callback common.AsyncCallback, response *PendingResponse) error +} + +type Server interface { + //invoke once for connection + Open(url common.URL) +} + +type ResponseHandler interface { + Handler(response *Response) +} + diff --git a/remoting/exchange_client.go b/remoting/exchange_client.go new file mode 100644 index 0000000000..962c324ee1 --- /dev/null +++ b/remoting/exchange_client.go @@ -0,0 +1,127 @@ +package remoting + +import ( + "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/common/logger" + "github.com/apache/dubbo-go/protocol" + "sync" + "time" +) + +type SequenceType int64 + +type ExchangeClient struct { + ConnectTimeout time.Duration + address string + client Client + pendingResponses *sync.Map +} + +func NewExchangeClient(url common.URL, client Client, connectTimeout time.Duration) *ExchangeClient { + exchangeClient := &ExchangeClient{ + ConnectTimeout: connectTimeout, + address: url.Location, + client: client, + } + client.Connect(url) + return exchangeClient +} + +func (client *ExchangeClient) Request(invocation *protocol.Invocation, url common.URL, timeout time.Duration, + result *protocol.RPCResult) error { + requestId := int64(SequenceId()) + request := NewRequest("2.0.2") + request.Data = invocation + request.Event = false + request.TwoWay = true + + rsp := NewPendingResponse() + rsp.response = NewResponse(requestId, "2.0.2") + //rsp.callback = invo + + err := client.client.Request(request, timeout, nil, rsp) + if err != nil { + result.Err = err + return err + } + result.Rest = rsp.response + //result.Attrs = rsp.response. + return nil +} + +func (client *ExchangeClient) AsyncRequest(invocation *protocol.Invocation, url common.URL, timeout time.Duration, + callback common.AsyncCallback, result *protocol.RPCResult) error { + requestId := int64(SequenceId()) + request := NewRequest("2.0.2") + request.Data = invocation + request.Event = false + request.TwoWay = true + + rsp := NewPendingResponse() + rsp.response = NewResponse(requestId, "2.0.2") + rsp.callback = callback + + err := client.client.Request(request, timeout, nil, rsp) + if err != nil { + result.Err = err + return err + } + result.Rest = rsp.response + //result.Attrs = rsp.response. + return nil +} + +// oneway +func (client *ExchangeClient) Send(invocation *protocol.Invocation, timeout time.Duration) error { + requestId := int64(SequenceId()) + request := NewRequest("2.0.2") + request.Data = invocation + request.Event = false + request.TwoWay = false + + rsp := NewPendingResponse() + rsp.response = NewResponse(requestId, "2.0.2") + + err := client.client.Request(request, timeout, nil, rsp) + if err != nil { + return err + } + //result.Attrs = rsp.response. + return nil +} + +func (client *ExchangeClient) Close() { + client.client.Close() +} + +func (client *ExchangeClient) Handler(response *Response) { + + pendingResponse := client.removePendingResponse(SequenceType(response.Id)) + if pendingResponse == nil { + logger.Errorf("failed to get pending response context for response package %s", *response) + return + } + + pendingResponse.response = response + + if pendingResponse.callback == nil { + pendingResponse.Done <- struct{}{} + } else { + pendingResponse.callback(pendingResponse.GetCallResponse()) + } +} + +func (client *ExchangeClient) addPendingResponse(pr *PendingResponse) { + client.pendingResponses.Store(SequenceType(pr.seq), pr) +} + +func (client *ExchangeClient) removePendingResponse(seq SequenceType) *PendingResponse { + if client.pendingResponses == nil { + return nil + } + if presp, ok := client.pendingResponses.Load(seq); ok { + client.pendingResponses.Delete(seq) + return presp.(*PendingResponse) + } + return nil +} diff --git a/remoting/exchange_server.go b/remoting/exchange_server.go new file mode 100644 index 0000000000..0036024205 --- /dev/null +++ b/remoting/exchange_server.go @@ -0,0 +1,29 @@ +package remoting + +import ( + "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/protocol" + "github.com/apache/dubbo-go/protocol/invocation" + "github.com/apache/dubbo-go/remoting/getty" +) + + +type ExchangeServer struct { + Server *getty.Server +} + +func NewExchangeServer(url common.URL, handler func(*invocation.RPCInvocation) protocol.RPCResult) *ExchangeServer { + server := getty.NewServer(url, handler) + exchangServer := &ExchangeServer{ + Server: server, + } + return exchangServer +} + +func (server *ExchangeServer) Start() { + server.Server.Start() +} + +func (server *ExchangeServer) Stop() { + server.Server.Stop() +} diff --git a/remoting/getty/client.go b/remoting/getty/client.go new file mode 100644 index 0000000000..b76033c6e9 --- /dev/null +++ b/remoting/getty/client.go @@ -0,0 +1,364 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +package getty +// +//import ( +// "github.com/apache/dubbo-go/remoting" +// "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 +//) +// +//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 ... +//func SetClientConf(c ClientConfig) { +// clientConf = &c +// err := clientConf.CheckValidity() +// if err != nil { +// logger.Warnf("[ClientConfig CheckValidity] error: %v", err) +// return +// } +// setClientGrpool() +//} +// +//// GetClientConf ... +//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 ... +//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 ... +//type Client struct { +// opts Options +// conf ClientConfig +// pool *gettyRPCClientPool +// sequence atomic.Uint64 +// +// pendingResponses *sync.Map +//} +// +//// NewClient ... +//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 ... +////type Request struct { +//// addr string +//// svcUrl common.URL +//// method string +//// args interface{} +//// atta map[string]string +////} +//// +////// NewRequest ... +////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 ... +////type Response struct { +//// reply interface{} +//// atta map[string]string +////} +//// +////// NewResponse ... +////func NewResponse(reply interface{}, atta map[string]string) *Response { +//// return &Response{ +//// reply: reply, +//// atta: atta, +//// } +////} +// +//// CallOneway call one way +////func (c *Client) CallOneway(request *Request) error { +//// +//// return perrors.WithStack(c.call(CT_OneWay, request, NewResponse(nil, nil), nil)) +////} +//// +////// 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 +//// } +//// +//// return perrors.WithStack(c.call(ct, request, response, nil)) +////} +//// +////// 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 +// } +// +// 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 ... +//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 { +// req := remoting.NewRequest("2.0.2") +// req.TwoWay = true +// req.Event = true +// resp := remoting.NewPendingResponse() +// return c.transfer(session, req, 3 * time.Second, resp) +//} +// +//func (c *Client) transfer(session getty.Session, request *remoting.Request, timeout time.Duration, +// rsp *remoting.PendingResponse) 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 { +// // c.addPendingResponse(rsp) +// //} +// +// err := session.WritePkg(request, timeout) +// 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/remoting/getty/client_test.go b/remoting/getty/client_test.go new file mode 100644 index 0000000000..d0a4c97e47 --- /dev/null +++ b/remoting/getty/client_test.go @@ -0,0 +1,300 @@ +/* + * 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" +// "sync" +// "testing" +// "time" +//) +// +//import ( +// hessian "github.com/apache/dubbo-go-hessian2" +// perrors "github.com/pkg/errors" +// "github.com/stretchr/testify/assert" +//) +// +//import ( +// "github.com/apache/dubbo-go/common" +// "github.com/apache/dubbo-go/common/proxy/proxy_factory" +// "github.com/apache/dubbo-go/protocol" +//) +// +//func TestClient_CallOneway(t *testing.T) { +// proto, url := InitTest(t) +// +// c := &Client{ +// pendingResponses: new(sync.Map), +// conf: *clientConf, +// opts: Options{ +// ConnectTimeout: 3e9, +// RequestTimeout: 6e9, +// }, +// } +// c.pool = newGettyRPCClientConnPool(c, clientConf.PoolSize, time.Duration(int(time.Second)*clientConf.PoolTTL)) +// +// //user := &User{} +// err := c.CallOneway(NewRequest("127.0.0.1:20000", url, "GetUser", []interface{}{"1", "username"}, nil)) +// assert.NoError(t, err) +// +// // destroy +// proto.Destroy() +//} +// +//func TestClient_Call(t *testing.T) { +// proto, url := InitTest(t) +// +// c := &Client{ +// pendingResponses: new(sync.Map), +// conf: *clientConf, +// opts: Options{ +// ConnectTimeout: 3e9, +// RequestTimeout: 10e9, +// }, +// } +// c.pool = newGettyRPCClientConnPool(c, clientConf.PoolSize, time.Duration(int(time.Second)*clientConf.PoolTTL)) +// +// var ( +// user *User +// err error +// ) +// +// user = &User{} +// err = c.Call(NewRequest("127.0.0.1:20000", url, "GetBigPkg", []interface{}{nil}, nil), NewResponse(user, nil)) +// assert.NoError(t, err) +// assert.NotEqual(t, "", user.Id) +// assert.NotEqual(t, "", user.Name) +// +// user = &User{} +// err = c.Call(NewRequest("127.0.0.1:20000", url, "GetUser", []interface{}{"1", "username"}, nil), NewResponse(user, nil)) +// assert.NoError(t, err) +// assert.Equal(t, User{Id: "1", Name: "username"}, *user) +// +// user = &User{} +// err = c.Call(NewRequest("127.0.0.1:20000", url, "GetUser0", []interface{}{"1", nil, "username"}, nil), NewResponse(user, nil)) +// assert.NoError(t, err) +// assert.Equal(t, User{Id: "1", Name: "username"}, *user) +// +// err = c.Call(NewRequest("127.0.0.1:20000", url, "GetUser1", []interface{}{}, nil), NewResponse(user, nil)) +// assert.NoError(t, err) +// +// err = c.Call(NewRequest("127.0.0.1:20000", url, "GetUser2", []interface{}{}, nil), 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)) +// 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)) +// 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)) +// 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)) +// 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)) +// assert.NoError(t, err) +// assert.Equal(t, User{Id: "1", Name: ""}, *user) +// +// // destroy +// proto.Destroy() +//} +// +//func TestClient_AsyncCall(t *testing.T) { +// proto, url := InitTest(t) +// +// c := &Client{ +// pendingResponses: new(sync.Map), +// conf: *clientConf, +// opts: Options{ +// ConnectTimeout: 3e9, +// RequestTimeout: 6e9, +// }, +// } +// c.pool = newGettyRPCClientConnPool(c, clientConf.PoolSize, time.Duration(int(time.Second)*clientConf.PoolTTL)) +// +// user := &User{} +// lock := sync.Mutex{} +// lock.Lock() +// err := c.AsyncCall(NewRequest("127.0.0.1:20000", url, "GetUser", []interface{}{"1", "username"}, nil), func(response common.CallbackResponse) { +// r := response.(AsyncCallbackResponse) +// assert.Equal(t, User{Id: "1", Name: "username"}, *r.Reply.(*Response).reply.(*User)) +// lock.Unlock() +// }, NewResponse(user, nil)) +// assert.NoError(t, err) +// assert.Equal(t, User{}, *user) +// +// // destroy +// lock.Lock() +// proto.Destroy() +// lock.Unlock() +//} +// +//func InitTest(t *testing.T) (protocol.Protocol, common.URL) { +// +// hessian.RegisterPOJO(&User{}) +// +// methods, err := common.ServiceMap.Register("dubbo", &UserProvider{}) +// assert.NoError(t, err) +// assert.Equal(t, "GetBigPkg,GetUser,GetUser0,GetUser1,GetUser2,GetUser3,GetUser4,GetUser5,GetUser6", methods) +// +// // config +// SetClientConf(ClientConfig{ +// ConnectionNum: 2, +// HeartbeatPeriod: "5s", +// SessionTimeout: "20s", +// PoolTTL: 600, +// PoolSize: 64, +// GettySessionParam: GettySessionParam{ +// CompressEncoding: false, +// TcpNoDelay: true, +// TcpKeepAlive: true, +// KeepAlivePeriod: "120s", +// TcpRBufSize: 262144, +// TcpWBufSize: 65536, +// PkgWQSize: 512, +// TcpReadTimeout: "4s", +// TcpWriteTimeout: "5s", +// WaitTimeout: "1s", +// MaxMsgLen: 10240000000, +// SessionName: "client", +// }, +// }) +// assert.NoError(t, clientConf.CheckValidity()) +// SetServerConfig(ServerConfig{ +// SessionNumber: 700, +// SessionTimeout: "20s", +// GettySessionParam: GettySessionParam{ +// CompressEncoding: false, +// TcpNoDelay: true, +// TcpKeepAlive: true, +// KeepAlivePeriod: "120s", +// TcpRBufSize: 262144, +// TcpWBufSize: 65536, +// PkgWQSize: 512, +// TcpReadTimeout: "1s", +// TcpWriteTimeout: "5s", +// WaitTimeout: "1s", +// MaxMsgLen: 10240000000, +// SessionName: "server", +// }}) +// assert.NoError(t, srvConf.CheckValidity()) +// +// // Export +// proto := GetProtocol() +// url, err := common.NewURL("dubbo://127.0.0.1:20000/UserProvider?anyhost=true&" + +// "application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&" + +// "environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&" + +// "module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&" + +// "side=provider&timeout=3000×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 < 4000; i++ { +// argBuf.WriteString("击鼓其镗,踊跃用兵。土国城漕,我独南行。从孙子仲,平陈与宋。不我以归,忧心有忡。爰居爰处?爰丧其马?于以求之?于林之下。死生契阔,与子成说。执子之手,与子偕老。于嗟阔兮,不我活兮。于嗟洵兮,不我信兮。") +// argBuf.WriteString("击鼓其镗,踊跃用兵。土国城漕,我独南行。从孙子仲,平陈与宋。不我以归,忧心有忡。爰居爰处?爰丧其马?于以求之?于林之下。死生契阔,与子成说。执子之手,与子偕老。于嗟阔兮,不我活兮。于嗟洵兮,不我信兮。") +// } +// rsp.Id = argBuf.String() +// rsp.Name = argBuf.String() +// return nil +//} +// +//func (u *UserProvider) GetUser(ctx context.Context, req []interface{}, rsp *User) error { +// rsp.Id = req[0].(string) +// rsp.Name = req[1].(string) +// return nil +//} +// +//func (u *UserProvider) GetUser0(id string, k *User, name string) (User, error) { +// return User{Id: id, Name: name}, nil +//} +// +//func (u *UserProvider) GetUser1() error { +// return nil +//} +// +//func (u *UserProvider) GetUser2() error { +// return perrors.New("error") +//} +// +//func (u *UserProvider) GetUser3(rsp *[]interface{}) error { +// *rsp = append(*rsp, User{Id: "1", Name: "username"}) +// return nil +//} +// +//func (u *UserProvider) GetUser4(ctx context.Context, req []interface{}) ([]interface{}, error) { +// +// return []interface{}{User{Id: req[0].([]interface{})[0].(string), Name: req[0].([]interface{})[1].(string)}}, nil +//} +// +//func (u *UserProvider) GetUser5(ctx context.Context, req []interface{}) (map[interface{}]interface{}, error) { +// return map[interface{}]interface{}{"key": User{Id: req[0].(map[interface{}]interface{})["id"].(string), Name: req[0].(map[interface{}]interface{})["name"].(string)}}, nil +//} +// +//func (u *UserProvider) GetUser6(id int64) (*User, error) { +// if id == 0 { +// return nil, nil +// } +// return &User{Id: "1"}, nil +//} +// +//func (u *UserProvider) Reference() string { +// return "UserProvider" +//} +// +//func (u User) JavaClassName() string { +// return "com.ikurento.user.User" +//} diff --git a/remoting/getty/config.go b/remoting/getty/config.go new file mode 100644 index 0000000000..d63a45a2e4 --- /dev/null +++ b/remoting/getty/config.go @@ -0,0 +1,209 @@ +/* + * 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 ( + "time" +) + +import ( + "github.com/dubbogo/getty" + perrors "github.com/pkg/errors" +) + +type ( + // GettySessionParam ... + GettySessionParam struct { + CompressEncoding bool `default:"false" yaml:"compress_encoding" json:"compress_encoding,omitempty"` + TcpNoDelay bool `default:"true" yaml:"tcp_no_delay" json:"tcp_no_delay,omitempty"` + 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 + 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 + TcpWriteTimeout string `default:"5s" yaml:"tcp_write_timeout" json:"tcp_write_timeout,omitempty"` + tcpWriteTimeout time.Duration + WaitTimeout string `default:"7s" yaml:"wait_timeout" json:"wait_timeout,omitempty"` + waitTimeout 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"` + } + + // ServerConfig + //Config 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"` + + // grpool + GrPoolSize int `default:"0" yaml:"gr_pool_size" json:"gr_pool_size,omitempty"` + QueueLen int `default:"0" yaml:"queue_len" json:"queue_len,omitempty"` + QueueNumber int `default:"0" yaml:"queue_number" json:"queue_number,omitempty"` + + // session tcp parameters + GettySessionParam GettySessionParam `required:"true" yaml:"getty_session_param" json:"getty_session_param,omitempty"` + } + + // ClientConfig + //Config holds supported types by the multiconfig package + ClientConfig struct { + ReconnectInterval int `default:"0" yaml:"reconnect_interval" json:"reconnect_interval,omitempty"` + + // 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 + + // session + SessionTimeout string `default:"60s" yaml:"session_timeout" json:"session_timeout,omitempty"` + sessionTimeout time.Duration + + // Connection Pool + PoolSize int `default:"2" yaml:"pool_size" json:"pool_size,omitempty"` + PoolTTL int `default:"180" yaml:"pool_ttl" json:"pool_ttl,omitempty"` + + // grpool + GrPoolSize int `default:"0" yaml:"gr_pool_size" json:"gr_pool_size,omitempty"` + QueueLen int `default:"0" yaml:"queue_len" json:"queue_len,omitempty"` + QueueNumber int `default:"0" yaml:"queue_number" json:"queue_number,omitempty"` + + // session tcp parameters + GettySessionParam GettySessionParam `required:"true" yaml:"getty_session_param" json:"getty_session_param,omitempty"` + } +) + +// GetDefaultClientConfig ... +func GetDefaultClientConfig() ClientConfig { + return ClientConfig{ + ReconnectInterval: 0, + ConnectionNum: 16, + HeartbeatPeriod: "30s", + SessionTimeout: "180s", + PoolSize: 4, + PoolTTL: 600, + GrPoolSize: 200, + QueueLen: 64, + QueueNumber: 10, + GettySessionParam: GettySessionParam{ + CompressEncoding: false, + TcpNoDelay: true, + TcpKeepAlive: true, + KeepAlivePeriod: "180s", + TcpRBufSize: 262144, + TcpWBufSize: 65536, + PkgWQSize: 512, + TcpReadTimeout: "1s", + TcpWriteTimeout: "5s", + WaitTimeout: "1s", + MaxMsgLen: 102400, + SessionName: "client", + }} +} + +// GetDefaultServerConfig ... +func GetDefaultServerConfig() ServerConfig { + return ServerConfig{ + SessionTimeout: "180s", + SessionNumber: 700, + GrPoolSize: 120, + QueueNumber: 6, + QueueLen: 64, + GettySessionParam: GettySessionParam{ + CompressEncoding: false, + TcpNoDelay: true, + TcpKeepAlive: true, + KeepAlivePeriod: "180s", + TcpRBufSize: 262144, + TcpWBufSize: 65536, + PkgWQSize: 512, + TcpReadTimeout: "1s", + TcpWriteTimeout: "5s", + WaitTimeout: "1s", + MaxMsgLen: 102400, + SessionName: "server", + }, + } +} + +// CheckValidity ... +func (c *GettySessionParam) CheckValidity() error { + var err error + + if c.keepAlivePeriod, 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 { + return perrors.WithMessagef(err, "time.ParseDuration(TcpReadTimeout{%#v})", c.TcpReadTimeout) + } + + if c.tcpWriteTimeout, 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 { + return perrors.WithMessagef(err, "time.ParseDuration(WaitTimeout{%#v})", c.WaitTimeout) + } + + return nil +} + +// CheckValidity ... +func (c *ClientConfig) CheckValidity() error { + var err error + + c.ReconnectInterval = c.ReconnectInterval * 1e6 + + if c.heartbeatPeriod, err = time.ParseDuration(c.HeartbeatPeriod); err != nil { + return perrors.WithMessagef(err, "time.ParseDuration(HeartbeatPeroid{%#v})", c.HeartbeatPeriod) + } + + if c.heartbeatPeriod >= time.Duration(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 { + return perrors.WithMessagef(err, "time.ParseDuration(SessionTimeout{%#v})", c.SessionTimeout) + } + + return perrors.WithStack(c.GettySessionParam.CheckValidity()) +} + +// CheckValidity ... +func (c *ServerConfig) CheckValidity() error { + var err error + + if c.sessionTimeout, err = time.ParseDuration(c.SessionTimeout); err != nil { + return perrors.WithMessagef(err, "time.ParseDuration(SessionTimeout{%#v})", c.SessionTimeout) + } + + if c.sessionTimeout >= time.Duration(getty.MaxWheelTimeSpan) { + return perrors.WithMessagef(err, "session_timeout %s should be less than %s", + c.SessionTimeout, time.Duration(getty.MaxWheelTimeSpan)) + } + + return perrors.WithStack(c.GettySessionParam.CheckValidity()) +} diff --git a/remoting/getty/getty_client.go b/remoting/getty/getty_client.go new file mode 100644 index 0000000000..8b79a77757 --- /dev/null +++ b/remoting/getty/getty_client.go @@ -0,0 +1,390 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package getty + +import ( + "github.com/apache/dubbo-go/remoting" + "math/rand" + "time" +) + +import ( + "github.com/dubbogo/getty" + gxsync "github.com/dubbogo/gost/sync" + perrors "github.com/pkg/errors" + "gopkg.in/yaml.v2" +) + +import ( + "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/common/logger" + "github.com/apache/dubbo-go/config" +) + +var ( + errInvalidCodecType = perrors.New("illegal CodecType") + errInvalidAddress = perrors.New("remote address invalid or empty") + errSessionNotExist = perrors.New("session not exist") + errClientClosed = perrors.New("client closed") + errClientReadTimeout = perrors.New("client read timeout") + + clientConf *ClientConfig + clientGrpool *gxsync.TaskPool +) + +func doInit(protocol string) { + if protocol == "" { + return + } + + // load clientconfig from consumer_config + // default use dubbo + consumerConfig := config.GetConsumerConfig() + if consumerConfig.ApplicationConfig == nil { + return + } + protocolConf := config.GetConsumerConfig().ProtocolConf + defaultClientConfig := GetDefaultClientConfig() + if protocolConf == nil { + logger.Info("protocol_conf default use dubbo config") + } else { + dubboConf := protocolConf.(map[interface{}]interface{})[protocol] + if dubboConf == nil { + logger.Warnf("dubboConf is nil") + return + } + dubboConfByte, err := yaml.Marshal(dubboConf) + if err != nil { + panic(err) + } + err = yaml.Unmarshal(dubboConfByte, &defaultClientConfig) + if err != nil { + panic(err) + } + } + clientConf = &defaultClientConfig + if err := clientConf.CheckValidity(); err != nil { + logger.Warnf("[CheckValidity] error: %v", err) + return + } + setClientGrpool() + + rand.Seed(time.Now().UnixNano()) +} + +// SetClientConf ... +func SetClientConf(c ClientConfig) { + clientConf = &c + err := clientConf.CheckValidity() + if err != nil { + logger.Warnf("[ClientConfig CheckValidity] error: %v", err) + return + } + setClientGrpool() +} + +// GetClientConf ... +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 ... +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 ... +type Client struct { + addr string + opts Options + conf ClientConfig + pool *gettyRPCClientPool + codec *remoting.Codec + responseHandler *remoting.ResponseHandler + //sequence atomic.Uint64 + //pendingResponses *sync.Map +} + +// NewClient ... +func NewClient(opt Options) *Client { + switch { + case opt.ConnectTimeout == 0: + opt.ConnectTimeout = 3 * time.Second + } + + c := &Client{ + opts: opt, + conf: *clientConf, + } + c.pool = newGettyRPCClientConnPool(c, clientConf.PoolSize, time.Duration(int(time.Second)*clientConf.PoolTTL)) + + return c +} + +func (c *Client) Connect(url common.URL) { + doInit(url.Protocol) + // codec + c.codec = remoting.GetCodec(url.Protocol) + c.addr = url.Ip + ":" + url.Port +} +func (c *Client) Close() { + if c.pool != nil { + c.pool.close() + } + c.pool = nil +} +func (c *Client) Request(request *remoting.Request, timeout time.Duration, callback common.AsyncCallback, response *remoting.PendingResponse) 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(c.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, request, timeout, response); err != nil { + return perrors.WithStack(err) + } + + if !request.TwoWay || callback != nil { + return nil + } + + select { + case <-getty.GetTimeWheel().After(timeout): + return perrors.WithStack(errClientReadTimeout) + case <-response.Done: + err = response.Err + } + + return perrors.WithStack(err) +} + +// CallOneway call one way +//func (c *Client) CallOneway(request *Request) error { +// +// return perrors.WithStack(c.call(CT_OneWay, request, NewResponse(nil, nil), nil)) +//} +// +//// 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 +// } +// +// return perrors.WithStack(c.call(ct, request, response, nil)) +//} +// +//// 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 +// } +// +// 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) +//} + +func (c *Client) selectSession(addr string) (*gettyRPCClient, getty.Session, error) { + rpcClient, err := c.pool.getGettyRpcClient(addr) + if err != nil { + return nil, nil, perrors.WithStack(err) + } + return rpcClient, rpcClient.selectSession(), nil +} + +func (c *Client) heartbeat(session getty.Session) error { + req := remoting.NewRequest("2.0.2") + req.TwoWay = true + req.Event = true + resp := remoting.NewPendingResponse() + return c.transfer(session, req, 3*time.Second, resp) +} + +func (c *Client) transfer(session getty.Session, request *remoting.Request, timeout time.Duration, + rsp *remoting.PendingResponse) 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 { + // c.addPendingResponse(rsp) + //} + + err := session.WritePkg(request, timeout) + 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/remoting/getty/listener.go b/remoting/getty/listener.go new file mode 100644 index 0000000000..daa1a583c1 --- /dev/null +++ b/remoting/getty/listener.go @@ -0,0 +1,350 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package getty + +import ( + "context" + "fmt" + "github.com/apache/dubbo-go/remoting" + "sync" + "sync/atomic" + "time" +) + +import ( + "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/constant" + "github.com/apache/dubbo-go/common/logger" + "github.com/apache/dubbo-go/protocol/invocation" +) + +// todo: WritePkg_Timeout will entry *.yml +const ( + // WritePkg_Timeout ... + WritePkg_Timeout = 5 * time.Second +) + +var ( + errTooManySessions = perrors.New("too many sessions") +) + +type rpcSession struct { + session getty.Session + reqNum int32 +} + +func (s *rpcSession) AddReqNum(num int32) { + atomic.AddInt32(&s.reqNum, num) +} + +func (s *rpcSession) GetReqNum() int32 { + return atomic.LoadInt32(&s.reqNum) +} + +// ////////////////////////////////////////// +// RpcClientHandler +// ////////////////////////////////////////// + +// RpcClientHandler ... +type RpcClientHandler struct { + conn *gettyRPCClient +} + +// NewRpcClientHandler ... +func NewRpcClientHandler(client *gettyRPCClient) *RpcClientHandler { + return &RpcClientHandler{conn: client} +} + +// OnOpen ... +func (h *RpcClientHandler) OnOpen(session getty.Session) error { + h.conn.addSession(session) + return nil +} + +// OnError ... +func (h *RpcClientHandler) OnError(session getty.Session, err error) { + logger.Infof("session{%s} got error{%v}, will be closed.", session.Stat(), err) + h.conn.removeSession(session) +} + +// OnClose ... +func (h *RpcClientHandler) OnClose(session getty.Session) { + logger.Infof("session{%s} is closing......", session.Stat()) + h.conn.removeSession(session) +} + +// OnMessage ... +func (h *RpcClientHandler) OnMessage(session getty.Session, pkg interface{}) { + p, ok := pkg.(*remoting.Response) + if !ok { + logger.Errorf("illegal package") + return + } + + if p.Event { + logger.Debugf("get rpc heartbeat response{%#v}", p) + if p.Error != nil { + logger.Errorf("rpc heartbeat response{error: %#v}", p.Error) + } + (*h.conn.pool.rpcClient.responseHandler).Handler(p) + //FIXME + //if p.Header.Type&hessian.PackageResponse != 0x00 { + // logger.Debugf("get rpc heartbeat response{header: %#v, body: %#v}", p.Header, p.Body) + // if p.Err != nil { + // logger.Errorf("rpc heartbeat response{error: %#v}", p.Err) + // } + // h.conn.pool.rpcClient.removePendingResponse(SequenceType(p.Header.ID)) + //} else { + // logger.Debugf("get rpc heartbeat request{header: %#v, service: %#v, body: %#v}", p.Header, p.Service, p.Body) + // p.Header.ResponseStatus = hessian.Response_OK + // reply(session, p, hessian.PackageHeartbeat) + //} + return + } + logger.Debugf("get rpc response{%#v}", p) + + h.conn.updateSession(session) + + (*h.conn.pool.rpcClient.responseHandler).Handler(p) + + // + //pendingResponse := h.conn.pool.rpcClient.removePendingResponse(SequenceType(p.Header.ID)) + //if pendingResponse == nil { + // logger.Errorf("failed to get pending response context for response package %s", *p) + // return + //} + // + //if p.Err != nil { + // pendingResponse.err = p.Err + //} + // + //pendingResponse.response.atta = p.Body.(*Response).atta + // + //if pendingResponse.callback == nil { + // pendingResponse.done <- struct{}{} + //} else { + // pendingResponse.callback(pendingResponse.GetCallResponse()) + //} +} + +// OnCron ... +func (h *RpcClientHandler) OnCron(session getty.Session) { + rpcSession, err := h.conn.getClientRpcSession(session) + if err != nil { + logger.Errorf("client.getClientSession(session{%s}) = error{%v}", + session.Stat(), perrors.WithStack(err)) + return + } + if h.conn.pool.rpcClient.conf.sessionTimeout.Nanoseconds() < time.Since(session.GetActive()).Nanoseconds() { + logger.Warnf("session{%s} timeout{%s}, reqNum{%d}", + session.Stat(), time.Since(session.GetActive()).String(), rpcSession.reqNum) + h.conn.removeSession(session) // -> h.conn.close() -> h.conn.pool.remove(h.conn) + return + } + + h.conn.pool.rpcClient.heartbeat(session) +} + +// ////////////////////////////////////////// +// RpcServerHandler +// ////////////////////////////////////////// + +// RpcServerHandler ... +type RpcServerHandler struct { + maxSessionNum int + sessionTimeout time.Duration + sessionMap map[getty.Session]*rpcSession + rwlock sync.RWMutex + server *Server +} + +// NewRpcServerHandler ... +func NewRpcServerHandler(maxSessionNum int, sessionTimeout time.Duration, serverP *Server) *RpcServerHandler { + return &RpcServerHandler{ + maxSessionNum: maxSessionNum, + sessionTimeout: sessionTimeout, + sessionMap: make(map[getty.Session]*rpcSession), + server: serverP, + } +} + +// OnOpen ... +func (h *RpcServerHandler) OnOpen(session getty.Session) error { + var err error + h.rwlock.RLock() + if h.maxSessionNum <= len(h.sessionMap) { + err = errTooManySessions + } + h.rwlock.RUnlock() + if err != nil { + return perrors.WithStack(err) + } + + logger.Infof("got session:%s", session.Stat()) + h.rwlock.Lock() + h.sessionMap[session] = &rpcSession{session: session} + h.rwlock.Unlock() + return nil +} + +// OnError ... +func (h *RpcServerHandler) OnError(session getty.Session, err error) { + logger.Infof("session{%s} got error{%v}, will be closed.", session.Stat(), err) + h.rwlock.Lock() + delete(h.sessionMap, session) + h.rwlock.Unlock() +} + +// OnClose ... +func (h *RpcServerHandler) OnClose(session getty.Session) { + logger.Infof("session{%s} is closing......", session.Stat()) + h.rwlock.Lock() + delete(h.sessionMap, session) + h.rwlock.Unlock() +} + +// OnMessage ... +func (h *RpcServerHandler) OnMessage(session getty.Session, pkg interface{}) { + h.rwlock.Lock() + if _, ok := h.sessionMap[session]; ok { + h.sessionMap[session].reqNum++ + } + h.rwlock.Unlock() + + req, ok := pkg.(*remoting.Request) + if !ok { + logger.Errorf("illegal package{%#v}", pkg) + return + } + resp := remoting.NewResponse(req.Id, req.Version) + resp.Status = hessian.Response_OK + + // heartbeat + if req.Event { + logger.Debugf("get rpc heartbeat request{%#v}", resp) + reply(session, resp, hessian.PackageHeartbeat) + return + } + + //twoway := true + //// not twoway + //if p.Header.Type&hessian.PackageRequest_TwoWay == 0x00 { + // twoway = false + //} + + defer func() { + if e := recover(); e != nil { + resp.Status = hessian.Response_SERVER_ERROR + if err, ok := e.(error); ok { + logger.Errorf("OnMessage panic: %+v", perrors.WithStack(err)) + resp.Error = perrors.WithStack(err) + } else if err, ok := e.(string); ok { + logger.Errorf("OnMessage panic: %+v", perrors.New(err)) + resp.Error = perrors.New(err) + } else { + logger.Errorf("OnMessage panic: %+v, this is impossible.", e) + resp.Error = fmt.Errorf("OnMessage panic unknow exception. %+v", e) + } + + if !req.TwoWay { + return + } + reply(session, resp, hessian.PackageResponse) + } + + }() + + invoc, ok := req.Data.(invocation.RPCInvocation) + if !ok { + + } + result := h.server.requestHandler(&invoc) + if !req.TwoWay { + return + } + resp.Result = result + reply(session, resp, hessian.PackageResponse) +} + +// OnCron ... +func (h *RpcServerHandler) OnCron(session getty.Session) { + var ( + flag bool + active time.Time + ) + + h.rwlock.RLock() + if _, ok := h.sessionMap[session]; ok { + active = session.GetActive() + if h.sessionTimeout.Nanoseconds() < time.Since(active).Nanoseconds() { + flag = true + logger.Warnf("session{%s} timeout{%s}, reqNum{%d}", + session.Stat(), time.Since(active).String(), h.sessionMap[session].reqNum) + } + } + h.rwlock.RUnlock() + + if flag { + h.rwlock.Lock() + delete(h.sessionMap, session) + h.rwlock.Unlock() + session.Close() + } +} + +// rebuildCtx rebuild the context by attachment. +// Once we decided to transfer more context's key-value, we should change this. +// now we only support rebuild the tracing context +func rebuildCtx(inv *invocation.RPCInvocation) context.Context { + ctx := context.Background() + + // actually, if user do not use any opentracing framework, the err will not be nil. + spanCtx, err := opentracing.GlobalTracer().Extract(opentracing.TextMap, + opentracing.TextMapCarrier(inv.Attachments())) + if err == nil { + ctx = context.WithValue(ctx, constant.TRACING_REMOTE_SPAN_CTX, spanCtx) + } + return ctx +} + +func reply(session getty.Session, resp *remoting.Response, tp hessian.PackageType) { + //resp := &DubboPackage{ + // Header: hessian.DubboHeader{ + // SerialID: req.Header.SerialID, + // Type: tp, + // ID: req.Header.ID, + // ResponseStatus: req.Header.ResponseStatus, + // }, + //} + // + //if resp.Event { + // resp.Result = req.Body + //} else { + // resp.Body = nil + //} + + if err := session.WritePkg(resp, WritePkg_Timeout); err != nil { + logger.Errorf("WritePkg error: %#v, %#v", perrors.WithStack(err), resp) + } +} diff --git a/remoting/getty/listener_test.go b/remoting/getty/listener_test.go new file mode 100644 index 0000000000..a7feeddce0 --- /dev/null +++ b/remoting/getty/listener_test.go @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package getty + +//import ( +// "testing" +//) +// +//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" +//) +// +//// test rebuild the ctx +//func TestRebuildCtx(t *testing.T) { +// opentracing.SetGlobalTracer(mocktracer.New()) +// attach := make(map[string]string, 10) +// attach[constant.VERSION_KEY] = "1.0" +// attach[constant.GROUP_KEY] = "MyGroup" +// inv := invocation.NewRPCInvocation("MethodName", []interface{}{"OK", "Hello"}, attach) +// +// // attachment doesn't contains any tracing key-value pair, +// ctx := rebuildCtx(inv) +// assert.NotNil(t, ctx) +// assert.Nil(t, ctx.Value(constant.TRACING_REMOTE_SPAN_CTX)) +// +// span, ctx := opentracing.StartSpanFromContext(ctx, "Test-Client") +// +// opentracing.GlobalTracer().Inject(span.Context(), opentracing.TextMap, +// opentracing.TextMapCarrier(inv.Attachments())) +// // rebuild the context success +// inv = invocation.NewRPCInvocation("MethodName", []interface{}{"OK", "Hello"}, attach) +// ctx = rebuildCtx(inv) +// span.Finish() +// assert.NotNil(t, ctx) +// assert.NotNil(t, ctx.Value(constant.TRACING_REMOTE_SPAN_CTX)) +//} diff --git a/remoting/getty/pool.go b/remoting/getty/pool.go new file mode 100644 index 0000000000..aaa3bbd70d --- /dev/null +++ b/remoting/getty/pool.go @@ -0,0 +1,404 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package getty + +import ( + "fmt" + "math/rand" + "net" + "sync" + "sync/atomic" + "time" +) + +import ( + "github.com/dubbogo/getty" + perrors "github.com/pkg/errors" +) + +import ( + "github.com/apache/dubbo-go/common/logger" +) + +type gettyRPCClient struct { + once sync.Once + //protocol string + addr string + active int64 // zero, not create or be destroyed + + pool *gettyRPCClientPool + + lock sync.RWMutex + gettyClient getty.Client + sessions []*rpcSession +} + +var ( + errClientPoolClosed = perrors.New("client pool closed") +) + +func newGettyRPCClientConn(pool *gettyRPCClientPool, addr string) (*gettyRPCClient, error) { + c := &gettyRPCClient{ + //protocol: protocol, + addr: addr, + pool: pool, + gettyClient: getty.NewTCPClient( + getty.WithServerAddress(addr), + 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) + for { + idx++ + if c.isAvailable() { + break + } + + if idx > times { + c.gettyClient.Close() + return nil, perrors.New(fmt.Sprintf("failed to create client connection to %s in %f seconds", addr, float32(times)/1000)) + } + time.Sleep(1e6) + } + logger.Debug("client init ok") + c.updateActive(time.Now().Unix()) + + return c, nil +} + +func (c *gettyRPCClient) updateActive(active int64) { + atomic.StoreInt64(&c.active, active) +} + +func (c *gettyRPCClient) getActive() int64 { + return atomic.LoadInt64(&c.active) +} + +func (c *gettyRPCClient) newSession(session getty.Session) error { + var ( + ok bool + tcpConn *net.TCPConn + conf ClientConfig + ) + + conf = c.pool.rpcClient.conf + if conf.GettySessionParam.CompressEncoding { + session.SetCompressType(getty.CompressZip) + } + + if tcpConn, ok = session.Conn().(*net.TCPConn); !ok { + panic(fmt.Sprintf("%s, session.conn{%#v} is not tcp connection\n", session.Stat(), session.Conn())) + } + + tcpConn.SetNoDelay(conf.GettySessionParam.TcpNoDelay) + tcpConn.SetKeepAlive(conf.GettySessionParam.TcpKeepAlive) + if conf.GettySessionParam.TcpKeepAlive { + tcpConn.SetKeepAlivePeriod(conf.GettySessionParam.keepAlivePeriod) + } + tcpConn.SetReadBuffer(conf.GettySessionParam.TcpRBufSize) + tcpConn.SetWriteBuffer(conf.GettySessionParam.TcpWBufSize) + + session.SetName(conf.GettySessionParam.SessionName) + session.SetMaxMsgLen(conf.GettySessionParam.MaxMsgLen) + session.SetPkgHandler(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) + logger.Debugf("client new session:%s\n", session.Stat()) + + session.SetTaskPool(clientGrpool) + + return nil +} + +func (c *gettyRPCClient) selectSession() getty.Session { + c.lock.RLock() + defer c.lock.RUnlock() + + if c.sessions == nil { + return nil + } + + count := len(c.sessions) + if count == 0 { + return nil + } + return c.sessions[rand.Int31n(int32(count))].session +} + +func (c *gettyRPCClient) addSession(session getty.Session) { + logger.Debugf("add session{%s}", session.Stat()) + if session == nil { + return + } + + c.lock.Lock() + defer c.lock.Unlock() + if c.sessions == nil { + c.sessions = make([]*rpcSession, 0, 16) + } + c.sessions = append(c.sessions, &rpcSession{session: session}) +} + +func (c *gettyRPCClient) removeSession(session getty.Session) { + if session == nil { + return + } + + var removeFlag bool + func() { + c.lock.Lock() + defer c.lock.Unlock() + if c.sessions == nil { + return + } + + for i, s := range c.sessions { + if s.session == session { + c.sessions = append(c.sessions[:i], c.sessions[i+1:]...) + logger.Debugf("delete session{%s}, its index{%d}", session.Stat(), i) + break + } + } + logger.Infof("after remove session{%s}, left session number:%d", session.Stat(), len(c.sessions)) + if len(c.sessions) == 0 { + removeFlag = true + } + }() + if removeFlag { + c.pool.safeRemove(c) + c.close() + } +} + +func (c *gettyRPCClient) updateSession(session getty.Session) { + if session == nil { + return + } + + var rs *rpcSession + func() { + c.lock.RLock() + defer c.lock.RUnlock() + if c.sessions == nil { + return + } + + for i, s := range c.sessions { + if s.session == session { + rs = c.sessions[i] + break + } + } + }() + if rs != nil { + rs.AddReqNum(1) + } +} + +func (c *gettyRPCClient) getClientRpcSession(session getty.Session) (rpcSession, error) { + var ( + err error + rpcSession rpcSession + ) + c.lock.RLock() + defer c.lock.RUnlock() + if c.sessions == nil { + return rpcSession, errClientClosed + } + + err = errSessionNotExist + for _, s := range c.sessions { + if s.session == session { + rpcSession = *s + err = nil + break + } + } + + return rpcSession, perrors.WithStack(err) +} + +func (c *gettyRPCClient) isAvailable() bool { + if c.selectSession() == nil { + return false + } + + return true +} + +func (c *gettyRPCClient) close() error { + closeErr := perrors.Errorf("close gettyRPCClient{%#v} again", c) + c.once.Do(func() { + var ( + gettyClient getty.Client + sessions []*rpcSession + ) + func() { + c.lock.Lock() + defer c.lock.Unlock() + + gettyClient = c.gettyClient + c.gettyClient = nil + + sessions = make([]*rpcSession, 0, len(c.sessions)) + for _, s := range c.sessions { + sessions = append(sessions, s) + } + c.sessions = c.sessions[:0] + }() + + c.updateActive(0) + + go func() { + if gettyClient != nil { + gettyClient.Close() + } + for _, s := range sessions { + logger.Infof("close client session{%s, last active:%s, request number:%d}", + s.session.Stat(), s.session.GetActive().String(), s.GetReqNum()) + s.session.Close() + } + }() + + closeErr = nil + }) + return closeErr +} + +type gettyRPCClientPool struct { + rpcClient *Client + size int // size of []*gettyRPCClient + ttl int64 // ttl of every gettyRPCClient, it is checked when getConn + + sync.Mutex + conns []*gettyRPCClient +} + +func newGettyRPCClientConnPool(rpcClient *Client, size int, ttl time.Duration) *gettyRPCClientPool { + return &gettyRPCClientPool{ + rpcClient: rpcClient, + size: size, + ttl: int64(ttl.Seconds()), + conns: make([]*gettyRPCClient, 0, 16), + } +} + +func (p *gettyRPCClientPool) close() { + p.Lock() + conns := p.conns + p.conns = nil + p.Unlock() + for _, conn := range conns { + conn.close() + } +} + +func (p *gettyRPCClientPool) getGettyRpcClient(addr string) (*gettyRPCClient, error) { + conn, err := p.get() + if err == nil && conn == nil { + // create new conn + rpcClientConn, err := newGettyRPCClientConn(p, addr) + return rpcClientConn, perrors.WithStack(err) + } + return conn, perrors.WithStack(err) +} + +func (p *gettyRPCClientPool) get() (*gettyRPCClient, error) { + now := time.Now().Unix() + + p.Lock() + defer p.Unlock() + if p.conns == nil { + return nil, errClientPoolClosed + } + + for len(p.conns) > 0 { + conn := p.conns[len(p.conns)-1] + p.conns = p.conns[:len(p.conns)-1] + + if d := now - conn.getActive(); d > p.ttl { + p.remove(conn) + go conn.close() + continue + } + conn.updateActive(now) //update active time + return conn, nil + } + return nil, nil +} + +func (p *gettyRPCClientPool) put(conn *gettyRPCClient) { + if conn == nil || conn.getActive() == 0 { + return + } + + p.Lock() + defer p.Unlock() + + if p.conns == nil { + return + } + + // check whether @conn has existed in p.conns or not. + for i := range p.conns { + if p.conns[i] == conn { + return + } + } + + if len(p.conns) >= p.size { + // delete @conn from client pool + // p.remove(conn) + conn.close() + return + } + p.conns = append(p.conns, conn) +} + +func (p *gettyRPCClientPool) remove(conn *gettyRPCClient) { + if conn == nil || conn.getActive() == 0 { + return + } + + if p.conns == nil { + return + } + + if len(p.conns) > 0 { + for idx, c := range p.conns { + if conn == c { + p.conns = append(p.conns[:idx], p.conns[idx+1:]...) + break + } + } + } +} + +func (p *gettyRPCClientPool) safeRemove(conn *gettyRPCClient) { + p.Lock() + defer p.Unlock() + + p.remove(conn) +} diff --git a/remoting/getty/readwriter.go b/remoting/getty/readwriter.go new file mode 100644 index 0000000000..721df105a8 --- /dev/null +++ b/remoting/getty/readwriter.go @@ -0,0 +1,136 @@ +/* + * 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" + "github.com/apache/dubbo-go/remoting" + "reflect" +) + +import ( + "github.com/apache/dubbo-go-hessian2" + "github.com/dubbogo/getty" + perrors "github.com/pkg/errors" +) + +import ( + "github.com/apache/dubbo-go/common/logger" +) + +//////////////////////////////////////////// +// RpcClientPackageHandler +//////////////////////////////////////////// + +// RpcClientPackageHandler ... +type RpcClientPackageHandler struct { + client *Client +} + +// NewRpcClientPackageHandler ... +func NewRpcClientPackageHandler(client *Client) *RpcClientPackageHandler { + return &RpcClientPackageHandler{client: client} +} + +func (p *RpcClientPackageHandler) Read(ss getty.Session, data []byte) (interface{}, int, error) { + //pkg := &DubboPackage{} + + buf := bytes.NewBuffer(data) + resp, length, err := (*p.client.codec).DecodeResponse(buf) + //err := pkg.Unmarshal(buf, p.client) + if err != nil { + originErr := perrors.Cause(err) + if originErr == hessian.ErrHeaderNotEnough || originErr == hessian.ErrBodyNotEnough { + return nil, 0, nil + } + + logger.Errorf("pkg.Unmarshal(ss:%+v, len(@data):%d) = error:%+v", ss, len(data), err) + + return nil, length, perrors.WithStack(err) + } + //if pkg.Header.Type&hessian.PackageRequest == 0x00 { + // pkg.Err = pkg.Body.(*hessian.Response).Exception + // pkg.Body = NewResponse(pkg.Body.(*hessian.Response).RspObj, pkg.Body.(*hessian.Response).Attachments) + //} + + return resp, length, nil +} + +func (p *RpcClientPackageHandler) Write(ss getty.Session, pkg interface{}) ([]byte, error) { + req, ok := pkg.(*remoting.Request) + if !ok { + logger.Errorf("illegal pkg:%+v\n", pkg) + return nil, perrors.New("invalid rpc request") + } + + buf, err := (*p.client.codec).EncodeRequest(req) + if err != nil { + logger.Warnf("binary.Write(req{%#v}) = err{%#v}", req, perrors.WithStack(err)) + return nil, perrors.WithStack(err) + } + + return buf.Bytes(), nil +} + +//////////////////////////////////////////// +// RpcServerPackageHandler +//////////////////////////////////////////// + +var ( + rpcServerPkgHandler = &RpcServerPackageHandler{} +) + +// RpcServerPackageHandler ... +type RpcServerPackageHandler struct { + server *Server +} + +func (p *RpcServerPackageHandler) Read(ss getty.Session, data []byte) (interface{}, int, error) { + buf := bytes.NewBuffer(data) + req, length, err := (*p.server.codec).DecodeRequest(buf) + //resp,len, err := (*p.).DecodeResponse(buf) + + if err != nil { + originErr := perrors.Cause(err) + if originErr == hessian.ErrHeaderNotEnough || originErr == hessian.ErrBodyNotEnough { + return nil, 0, nil + } + + logger.Errorf("pkg.Unmarshal(ss:%+v, len(@data):%d) = error:%+v", ss, len(data), err) + + return nil, 0, perrors.WithStack(err) + } + + return req, length, err +} + +func (p *RpcServerPackageHandler) Write(ss getty.Session, pkg interface{}) ([]byte, error) { + res, ok := pkg.(*remoting.Response) + if !ok { + logger.Errorf("illegal pkg:%+v\n, it is %+v", pkg, reflect.TypeOf(pkg)) + return nil, perrors.New("invalid rpc response") + } + + buf, err := (*p.server.codec).EncodeResponse(res) + if err != nil { + logger.Warnf("binary.Write(res{%#v}) = err{%#v}", res, perrors.WithStack(err)) + return nil, perrors.WithStack(err) + } + + return buf.Bytes(), nil +} diff --git a/remoting/getty/server.go b/remoting/getty/server.go new file mode 100644 index 0000000000..7ce9aafab1 --- /dev/null +++ b/remoting/getty/server.go @@ -0,0 +1,188 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package getty + +import ( + "fmt" + "github.com/apache/dubbo-go/protocol" + "github.com/apache/dubbo-go/protocol/invocation" + "github.com/apache/dubbo-go/remoting" + "net" +) + +import ( + "github.com/dubbogo/getty" + "github.com/dubbogo/gost/sync" + "gopkg.in/yaml.v2" +) + +import ( + "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/common/logger" + "github.com/apache/dubbo-go/config" +) + +var ( + srvConf *ServerConfig + srvGrpool *gxsync.TaskPool +) + +func initServer(protocol string) { + + // 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{})[protocol] + 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 ... +func SetServerConfig(s ServerConfig) { + srvConf = &s + err := srvConf.CheckValidity() + if err != nil { + logger.Warnf("[ServerConfig CheckValidity] error: %v", err) + return + } + SetServerGrpool() +} + +// GetServerConfig ... +func GetServerConfig() ServerConfig { + return *srvConf +} + +// SetServerGrpool ... +func SetServerGrpool() { + if srvConf.GrPoolSize > 1 { + srvGrpool = gxsync.NewTaskPool(gxsync.WithTaskPoolTaskPoolSize(srvConf.GrPoolSize), gxsync.WithTaskPoolTaskQueueLength(srvConf.QueueLen), + gxsync.WithTaskPoolTaskQueueNumber(srvConf.QueueNumber)) + } +} + +// Server ... +type Server struct { + conf ServerConfig + addr string + codec *remoting.Codec + tcpServer getty.Server + rpcHandler *RpcServerHandler + requestHandler func(*invocation.RPCInvocation) protocol.RPCResult +} + +// NewServer ... +func NewServer(url common.URL, handlers func(*invocation.RPCInvocation) protocol.RPCResult) *Server { + //init + doInit(url.Protocol) + + s := &Server{ + conf: *srvConf, + addr: url.Location, + codec: remoting.GetCodec(url.Protocol), + requestHandler: handlers, + } + + s.rpcHandler = NewRpcServerHandler(s.conf.SessionNumber, s.conf.sessionTimeout, s) + + return s +} + +func (s *Server) newSession(session getty.Session) error { + var ( + ok bool + tcpConn *net.TCPConn + ) + conf := s.conf + + if conf.GettySessionParam.CompressEncoding { + session.SetCompressType(getty.CompressZip) + } + + if tcpConn, ok = session.Conn().(*net.TCPConn); !ok { + panic(fmt.Sprintf("%s, session.conn{%#v} is not tcp connection\n", session.Stat(), session.Conn())) + } + + tcpConn.SetNoDelay(conf.GettySessionParam.TcpNoDelay) + tcpConn.SetKeepAlive(conf.GettySessionParam.TcpKeepAlive) + if conf.GettySessionParam.TcpKeepAlive { + tcpConn.SetKeepAlivePeriod(conf.GettySessionParam.keepAlivePeriod) + } + tcpConn.SetReadBuffer(conf.GettySessionParam.TcpRBufSize) + tcpConn.SetWriteBuffer(conf.GettySessionParam.TcpWBufSize) + + session.SetName(conf.GettySessionParam.SessionName) + session.SetMaxMsgLen(conf.GettySessionParam.MaxMsgLen) + session.SetPkgHandler(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 +} + +// Start ... +func (s *Server) Start() { + var ( + tcpServer getty.Server + ) + + tcpServer = getty.NewTCPServer( + getty.WithLocalAddress(s.addr), + ) + tcpServer.RunEventLoop(s.newSession) + logger.Debugf("s bind addr{%s} ok!", s.addr) + s.tcpServer = tcpServer + +} + +// Stop ... +func (s *Server) Stop() { + s.tcpServer.Close() +} From fa61e9de457577e8e1c5300b1caf39d03ae69789 Mon Sep 17 00:00:00 2001 From: cvictory Date: Sun, 26 Apr 2020 14:43:38 +0800 Subject: [PATCH 02/44] refactor getty --- protocol/dubbo/dubbo_codec.go | 159 +++++++++---------------- protocol/dubbo/dubbo_protocol.go | 36 +++++- protocol/dubbo/dubbo_protocol_test.go | 162 +++++++++++++------------- remoting/codec.go | 12 +- remoting/exchange.go | 32 ++--- remoting/exchange_client.go | 65 ++++++++--- remoting/exchange_server.go | 17 +-- remoting/getty/getty_client.go | 29 +++-- remoting/getty/listener.go | 13 ++- remoting/getty/readwriter.go | 23 ++-- remoting/getty/server.go | 6 +- 11 files changed, 273 insertions(+), 281 deletions(-) diff --git a/protocol/dubbo/dubbo_codec.go b/protocol/dubbo/dubbo_codec.go index 5dea37f870..899148a8e0 100644 --- a/protocol/dubbo/dubbo_codec.go +++ b/protocol/dubbo/dubbo_codec.go @@ -15,77 +15,6 @@ import ( "time" ) -////SerialID serial ID -//type SerialID byte -//type SequenceType int64 -// -//const ( -// // S_Dubbo dubbo serial id -// S_Dubbo SerialID = 2 -//) -// -//// DubboPackage ... -//type DubboPackage struct { -// Header hessian.DubboHeader -// Service hessian.Service -// Body interface{} -// Err error -//} -// -//func (p DubboPackage) String() string { -// return fmt.Sprintf("DubboPackage: Header-%v, Path-%v, Body-%v", p.Header, p.Service, p.Body) -//} - -// -//// Marshal ... -//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 ... -//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, 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: resp.Reply} -// } -// } -// -// // read body -// err = codec.ReadBody(p.Body) -// return perrors.WithStack(err) -//} - -///////////////////////////////////////// -///////////////////////////////////////// //SerialID serial ID type SerialID byte @@ -94,24 +23,10 @@ const ( 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 +func init() { + codec := &DubboCodec{} + remoting.NewCodec("dubbo", codec) +} // DubboPackage ... type DubboPackage struct { @@ -138,7 +53,7 @@ func (p *DubboPackage) Marshal() (*bytes.Buffer, error) { } // Unmarshal ... -func (p *DubboPackage) Unmarshal(buf *bytes.Buffer, opts ...interface{}) error { +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 { @@ -153,22 +68,17 @@ func (p *DubboPackage) Unmarshal(buf *bytes.Buffer, opts ...interface{}) error { 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 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, ok := client.pendingResponses.Load(SequenceType(p.Header.ID)) - if !ok { + 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.(*PendingResponse).response.reply} + p.Body = &hessian.Response{RspObj: pendingRsp.Reply} } } @@ -176,11 +86,21 @@ func (p *DubboPackage) Unmarshal(buf *bytes.Buffer, opts ...interface{}) error { err = codec.ReadBody(p.Body) return perrors.WithStack(err) } + type DubboCodec struct { } func (c *DubboCodec) EncodeRequest(request *remoting.Request) (*bytes.Buffer, error) { - invocation := request.Data.(invocation.RPCInvocation) + 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, "") @@ -220,8 +140,24 @@ func (c *DubboCodec) EncodeRequest(request *remoting.Request) (*bytes.Buffer, er return bytes.NewBuffer(pkg), nil } +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) + + byt, err := codec.Write(pkg.Service, pkg.Header, pkg.Body) + if err != nil { + return nil, perrors.WithStack(err) + } + + return bytes.NewBuffer(byt), nil +} func (c *DubboCodec) EncodeResponse(response *remoting.Response) (*bytes.Buffer, error) { - var ptype hessian.PackageType = hessian.PackageResponse + var ptype = hessian.PackageResponse if response.IsHeartbeat() { ptype = hessian.PackageHeartbeat } @@ -233,7 +169,13 @@ func (c *DubboCodec) EncodeResponse(response *remoting.Response) (*bytes.Buffer, ResponseStatus: response.Status, }, } - resp.Body = response.Result + if !response.IsHeartbeat() { + resp.Body = &hessian.Response{ + RspObj: response.Result.(protocol.RPCResult).Rest, + Exception: response.Result.(protocol.RPCResult).Err, + } + } + //if response.Header.Type&hessian.PackageRequest != 0x00 { // resp.Body = req.Body //} else { @@ -259,7 +201,7 @@ func (c *DubboCodec) DecodeRequest(data []byte) (*remoting.Request, int, error) originErr := perrors.Cause(err) if originErr == hessian.ErrHeaderNotEnough || originErr == hessian.ErrBodyNotEnough { //FIXME - return request, 0, originErr + return request, 0, originErr } logger.Errorf("pkg.Unmarshal(len(@data):%d) = error:%+v", buf.Len(), err) @@ -268,7 +210,8 @@ func (c *DubboCodec) DecodeRequest(data []byte) (*remoting.Request, int, error) request = &remoting.Request{ Id: pkg.Header.ID, SerialID: pkg.Header.SerialID, - TwoWay: false, + TwoWay: pkg.Header.Type&hessian.PackageRequest_TwoWay != 0x00, + Event: pkg.Header.Type&hessian.PackageHeartbeat != 0x00, } if pkg.Header.Type&hessian.PackageHeartbeat == 0x00 { // convert params of request @@ -330,15 +273,16 @@ func (c *DubboCodec) DecodeRequest(data []byte) (*remoting.Request, int, error) } return request, hessian.HEADER_LENGTH + pkg.Header.BodyLen, nil } + func (c *DubboCodec) DecodeResponse(data []byte) (*remoting.Response, int, error) { pkg := &DubboPackage{} buf := bytes.NewBuffer(data) - var response *remoting.Response + response := &remoting.Response{} err := pkg.Unmarshal(buf, response) if err != nil { originErr := perrors.Cause(err) if originErr == hessian.ErrHeaderNotEnough || originErr == hessian.ErrBodyNotEnough { - return response, 0, nil + return response, 0, nil } logger.Errorf("pkg.Unmarshal(len(@data):%d) = error:%+v", buf.Len(), err) @@ -349,7 +293,7 @@ func (c *DubboCodec) DecodeResponse(data []byte) (*remoting.Response, int, error //Version: pkg.Header., SerialID: pkg.Header.SerialID, Status: pkg.Header.ResponseStatus, - Event: (pkg.Header.Type | hessian.PackageHeartbeat) != 0, + Event: (pkg.Header.Type & hessian.PackageHeartbeat) != 0, } var error error if pkg.Header.Type&hessian.PackageHeartbeat != 0x00 { @@ -368,6 +312,7 @@ func (c *DubboCodec) DecodeResponse(data []byte) (*remoting.Response, int, 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.Err != nil { rpcResult.Err = pkg.Err diff --git a/protocol/dubbo/dubbo_protocol.go b/protocol/dubbo/dubbo_protocol.go index e98e6e2dde..20e54fa2ee 100644 --- a/protocol/dubbo/dubbo_protocol.go +++ b/protocol/dubbo/dubbo_protocol.go @@ -39,6 +39,10 @@ const ( DUBBO = "dubbo" ) +var ( + exchangeClientMap *sync.Map = new(sync.Map) +) + func init() { extension.SetProtocol(DUBBO, GetProtocol) } @@ -89,9 +93,7 @@ func (dp *DubboProtocol) Refer(url common.URL) protocol.Invoker { // ConnectTimeout: config.GetConsumerConfig().ConnectTimeout, // RequestTimeout: requestTimeout, //})) - invoker := NewDubboInvoker(url, remoting.NewExchangeClient(url, getty.NewClient(getty.Options{ - ConnectTimeout: config.GetConsumerConfig().ConnectTimeout, - }), config.GetConsumerConfig().ConnectTimeout)) + invoker := NewDubboInvoker(url, getExchangeClient(url)) dp.SetInvokers(invoker) logger.Infof("Refer service: %s", url.String()) return invoker @@ -124,7 +126,7 @@ func (dp *DubboProtocol) openServer(url common.URL) { handler := func(invocation *invocation.RPCInvocation) protocol.RPCResult { return doHandleRequest(invocation) } - srv := remoting.NewExchangeServer(url, handler) + srv := remoting.NewExchangeServer(url, getty.NewServer(url, handler)) dp.serverMap[url.Location] = srv srv.Start() } @@ -158,8 +160,8 @@ func doHandleRequest(rpcInvocation *invocation.RPCInvocation) protocol.RPCResult // //args := p.Body.(map[string]interface{})["args"].([]interface{}) //inv := invocation.NewRPCInvocation(p.Service.Method, args, attachments) - - ctx := rebuildCtx(rpcInvocation) + // FIXME + ctx := getty.RebuildCtx(rpcInvocation) invokeResult := invoker.Invoke(ctx, rpcInvocation) if err := invokeResult.Error(); err != nil { @@ -176,3 +178,25 @@ func doHandleRequest(rpcInvocation *invocation.RPCInvocation) protocol.RPCResult } return result } + +func getExchangeClient(url common.URL) *remoting.ExchangeClient { + clientTmp, ok := exchangeClientMap.Load(url.Location) + if !ok { + exchangeClientTmp := remoting.NewExchangeClient(url, getty.NewClient(getty.Options{ + ConnectTimeout: config.GetConsumerConfig().ConnectTimeout, + }), config.GetConsumerConfig().ConnectTimeout) + exchangeClientMap.Store(url.Location, exchangeClientTmp) + + return exchangeClientTmp + } + exchangeClient, ok := clientTmp.(*remoting.ExchangeClient) + if !ok { + exchangeClientTmp := remoting.NewExchangeClient(url, getty.NewClient(getty.Options{ + ConnectTimeout: config.GetConsumerConfig().ConnectTimeout, + }), config.GetConsumerConfig().ConnectTimeout) + exchangeClientMap.Store(url.Location, exchangeClientTmp) + + return exchangeClientTmp + } + return exchangeClient +} diff --git a/protocol/dubbo/dubbo_protocol_test.go b/protocol/dubbo/dubbo_protocol_test.go index 14f6868ad4..5fdcc69b5c 100644 --- a/protocol/dubbo/dubbo_protocol_test.go +++ b/protocol/dubbo/dubbo_protocol_test.go @@ -16,84 +16,84 @@ */ package dubbo - -import ( - "testing" -) - -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" -) - -func TestDubboProtocol_Export(t *testing.T) { - // 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&" + - "module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&" + - "side=provider&timeout=3000×tamp=1556509797245") - assert.NoError(t, err) - exporter := proto.Export(protocol.NewBaseInvoker(url)) - - // make sure url - eq := exporter.GetInvoker().GetUrl().URLEqual(url) - assert.True(t, eq) - - // second service: the same path and the different version - url2, 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&"+ - "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 - eq2 := exporter2.GetInvoker().GetUrl().URLEqual(url2) - assert.True(t, eq2) - - // make sure exporterMap after 'Unexport' - _, ok := proto.(*DubboProtocol).ExporterMap().Load(url.ServiceKey()) - assert.True(t, ok) - exporter.Unexport() - _, ok = proto.(*DubboProtocol).ExporterMap().Load(url.ServiceKey()) - assert.False(t, ok) - - // make sure serverMap after 'Destroy' - _, ok = proto.(*DubboProtocol).serverMap[url.Location] - assert.True(t, ok) - proto.Destroy() - _, ok = proto.(*DubboProtocol).serverMap[url.Location] - assert.False(t, ok) -} - -func TestDubboProtocol_Refer(t *testing.T) { - // Refer - proto := GetProtocol() - url, err := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&" + - "application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&" + - "environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&" + - "module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&" + - "side=provider&timeout=3000×tamp=1556509797245") - assert.NoError(t, err) - clientConf = &ClientConfig{} - invoker := proto.Refer(url) - - // make sure url - eq := invoker.GetUrl().URLEqual(url) - assert.True(t, eq) - - // make sure invokers after 'Destroy' - invokersLen := len(proto.(*DubboProtocol).Invokers()) - assert.Equal(t, 1, invokersLen) - proto.Destroy() - invokersLen = len(proto.(*DubboProtocol).Invokers()) - assert.Equal(t, 0, invokersLen) -} +// +//import ( +// "testing" +//) +// +//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" +//) +// +//func TestDubboProtocol_Export(t *testing.T) { +// // 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&" + +// "module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&" + +// "side=provider&timeout=3000×tamp=1556509797245") +// assert.NoError(t, err) +// exporter := proto.Export(protocol.NewBaseInvoker(url)) +// +// // make sure url +// eq := exporter.GetInvoker().GetUrl().URLEqual(url) +// assert.True(t, eq) +// +// // second service: the same path and the different version +// url2, 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&"+ +// "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 +// eq2 := exporter2.GetInvoker().GetUrl().URLEqual(url2) +// assert.True(t, eq2) +// +// // make sure exporterMap after 'Unexport' +// _, ok := proto.(*DubboProtocol).ExporterMap().Load(url.ServiceKey()) +// assert.True(t, ok) +// exporter.Unexport() +// _, ok = proto.(*DubboProtocol).ExporterMap().Load(url.ServiceKey()) +// assert.False(t, ok) +// +// // make sure serverMap after 'Destroy' +// _, ok = proto.(*DubboProtocol).serverMap[url.Location] +// assert.True(t, ok) +// proto.Destroy() +// _, ok = proto.(*DubboProtocol).serverMap[url.Location] +// assert.False(t, ok) +//} +// +//func TestDubboProtocol_Refer(t *testing.T) { +// // Refer +// proto := GetProtocol() +// url, err := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&" + +// "application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&" + +// "environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&" + +// "module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&" + +// "side=provider&timeout=3000×tamp=1556509797245") +// assert.NoError(t, err) +// clientConf = &ClientConfig{} +// invoker := proto.Refer(url) +// +// // make sure url +// eq := invoker.GetUrl().URLEqual(url) +// assert.True(t, eq) +// +// // make sure invokers after 'Destroy' +// invokersLen := len(proto.(*DubboProtocol).Invokers()) +// assert.Equal(t, 1, invokersLen) +// proto.Destroy() +// invokersLen = len(proto.(*DubboProtocol).Invokers()) +// assert.Equal(t, 0, invokersLen) +//} diff --git a/remoting/codec.go b/remoting/codec.go index 70071bdec5..972a00d12b 100644 --- a/remoting/codec.go +++ b/remoting/codec.go @@ -7,22 +7,22 @@ import ( type Codec interface { EncodeRequest(request *Request) (*bytes.Buffer, error) EncodeResponse(response *Response) (*bytes.Buffer, error) - DecodeRequest(*bytes.Buffer) (*Request, int, error) - DecodeResponse(*bytes.Buffer) (*Response, int, error) + DecodeRequest(data []byte) (*Request, int, error) + DecodeResponse(data []byte) (*Response, int, error) } var ( - codec map[string]*Codec + codec map[string]Codec ) func init() { - codec = make(map[string]*Codec, 2) + codec = make(map[string]Codec, 2) } -func NewCodec(protocol string, codecTmp *Codec) { +func NewCodec(protocol string, codecTmp Codec) { codec[protocol] = codecTmp } -func GetCodec(protocol string) *Codec { +func GetCodec(protocol string) Codec { return codec[protocol] } diff --git a/remoting/exchange.go b/remoting/exchange.go index f63c9119f7..498022926d 100644 --- a/remoting/exchange.go +++ b/remoting/exchange.go @@ -37,11 +37,11 @@ func NewRequest(version string) *Request { } } -func (request *Request) SetHeartbeat(isHeartbeat bool) { - if isHeartbeat { - - } -} +//func (request *Request) SetHeartbeat(isHeartbeat bool) { +// if isHeartbeat { +// +// } +//} // Response ... type Response struct { @@ -52,7 +52,6 @@ type Response struct { Event bool Error error Result interface{} - Reply interface{} } // NewResponse ... @@ -91,12 +90,14 @@ type PendingResponse struct { ReadStart time.Time callback common.AsyncCallback response *Response + Reply interface{} Done chan struct{} } // NewPendingResponse ... -func NewPendingResponse() *PendingResponse { +func NewPendingResponse(id int64) *PendingResponse { return &PendingResponse{ + seq: id, start: time.Now(), response: &Response{}, Done: make(chan struct{}), @@ -112,20 +113,3 @@ func (r PendingResponse) GetCallResponse() common.CallbackResponse { Reply: r.response, } } - -type Client interface { - //invoke once for connection - Connect(url common.URL) - Close() - Request(request *Request, timeout time.Duration, callback common.AsyncCallback, response *PendingResponse) error -} - -type Server interface { - //invoke once for connection - Open(url common.URL) -} - -type ResponseHandler interface { - Handler(response *Response) -} - diff --git a/remoting/exchange_client.go b/remoting/exchange_client.go index 962c324ee1..aff8bba22d 100644 --- a/remoting/exchange_client.go +++ b/remoting/exchange_client.go @@ -8,13 +8,29 @@ import ( "time" ) +var ( + pendingResponses *sync.Map = new(sync.Map) +) + type SequenceType int64 type ExchangeClient struct { - ConnectTimeout time.Duration - address string - client Client - pendingResponses *sync.Map + ConnectTimeout time.Duration + address string + client Client +} + +type Client interface { + SetExchangeClient(client *ExchangeClient) + SetResponseHandler(responseHandler ResponseHandler) + //invoke once for connection + Connect(url common.URL) + Close() + Request(request *Request, timeout time.Duration, callback common.AsyncCallback, response *PendingResponse) error +} + +type ResponseHandler interface { + Handler(response *Response) } func NewExchangeClient(url common.URL, client Client, connectTimeout time.Duration) *ExchangeClient { @@ -23,20 +39,23 @@ func NewExchangeClient(url common.URL, client Client, connectTimeout time.Durati address: url.Location, client: client, } + client.SetExchangeClient(exchangeClient) client.Connect(url) + client.SetResponseHandler(exchangeClient) return exchangeClient } func (client *ExchangeClient) Request(invocation *protocol.Invocation, url common.URL, timeout time.Duration, result *protocol.RPCResult) error { - requestId := int64(SequenceId()) request := NewRequest("2.0.2") request.Data = invocation request.Event = false request.TwoWay = true - rsp := NewPendingResponse() - rsp.response = NewResponse(requestId, "2.0.2") + rsp := NewPendingResponse(request.Id) + rsp.response = NewResponse(request.Id, "2.0.2") + rsp.Reply = (*invocation).Reply() + AddPendingResponse(rsp) //rsp.callback = invo err := client.client.Request(request, timeout, nil, rsp) @@ -44,22 +63,23 @@ func (client *ExchangeClient) Request(invocation *protocol.Invocation, url commo result.Err = err return err } - result.Rest = rsp.response + result.Rest = rsp.response.Result //result.Attrs = rsp.response. return nil } func (client *ExchangeClient) AsyncRequest(invocation *protocol.Invocation, url common.URL, timeout time.Duration, callback common.AsyncCallback, result *protocol.RPCResult) error { - requestId := int64(SequenceId()) request := NewRequest("2.0.2") request.Data = invocation request.Event = false request.TwoWay = true - rsp := NewPendingResponse() - rsp.response = NewResponse(requestId, "2.0.2") + rsp := NewPendingResponse(request.Id) + rsp.response = NewResponse(request.Id, "2.0.2") rsp.callback = callback + rsp.Reply = (*invocation).Reply() + AddPendingResponse(rsp) err := client.client.Request(request, timeout, nil, rsp) if err != nil { @@ -79,7 +99,7 @@ func (client *ExchangeClient) Send(invocation *protocol.Invocation, timeout time request.Event = false request.TwoWay = false - rsp := NewPendingResponse() + rsp := NewPendingResponse(request.Id) rsp.response = NewResponse(requestId, "2.0.2") err := client.client.Request(request, timeout, nil, rsp) @@ -96,7 +116,7 @@ func (client *ExchangeClient) Close() { func (client *ExchangeClient) Handler(response *Response) { - pendingResponse := client.removePendingResponse(SequenceType(response.Id)) + pendingResponse := removePendingResponse(SequenceType(response.Id)) if pendingResponse == nil { logger.Errorf("failed to get pending response context for response package %s", *response) return @@ -111,16 +131,23 @@ func (client *ExchangeClient) Handler(response *Response) { } } -func (client *ExchangeClient) addPendingResponse(pr *PendingResponse) { - client.pendingResponses.Store(SequenceType(pr.seq), pr) +func AddPendingResponse(pr *PendingResponse) { + pendingResponses.Store(SequenceType(pr.seq), pr) } -func (client *ExchangeClient) removePendingResponse(seq SequenceType) *PendingResponse { - if client.pendingResponses == nil { +func removePendingResponse(seq SequenceType) *PendingResponse { + if pendingResponses == nil { return nil } - if presp, ok := client.pendingResponses.Load(seq); ok { - client.pendingResponses.Delete(seq) + if presp, ok := pendingResponses.Load(seq); ok { + pendingResponses.Delete(seq) + return presp.(*PendingResponse) + } + return nil +} + +func GetPendingResponse(seq SequenceType) *PendingResponse { + if presp, ok := pendingResponses.Load(seq); ok { return presp.(*PendingResponse) } return nil diff --git a/remoting/exchange_server.go b/remoting/exchange_server.go index 0036024205..4aae39fd83 100644 --- a/remoting/exchange_server.go +++ b/remoting/exchange_server.go @@ -2,18 +2,19 @@ package remoting import ( "github.com/apache/dubbo-go/common" - "github.com/apache/dubbo-go/protocol" - "github.com/apache/dubbo-go/protocol/invocation" - "github.com/apache/dubbo-go/remoting/getty" ) +type Server interface { + //invoke once for connection + Start() + Stop() +} type ExchangeServer struct { - Server *getty.Server + Server Server } -func NewExchangeServer(url common.URL, handler func(*invocation.RPCInvocation) protocol.RPCResult) *ExchangeServer { - server := getty.NewServer(url, handler) +func NewExchangeServer(url common.URL, server Server) *ExchangeServer { exchangServer := &ExchangeServer{ Server: server, } @@ -21,9 +22,9 @@ func NewExchangeServer(url common.URL, handler func(*invocation.RPCInvocation) p } func (server *ExchangeServer) Start() { - server.Server.Start() + (server.Server).Start() } func (server *ExchangeServer) Stop() { - server.Server.Stop() + (server.Server).Stop() } diff --git a/remoting/getty/getty_client.go b/remoting/getty/getty_client.go index 8b79a77757..c22f576872 100644 --- a/remoting/getty/getty_client.go +++ b/remoting/getty/getty_client.go @@ -19,6 +19,7 @@ package getty import ( "github.com/apache/dubbo-go/remoting" + "gopkg.in/yaml.v2" "math/rand" "time" ) @@ -27,7 +28,6 @@ import ( "github.com/dubbogo/getty" gxsync "github.com/dubbogo/gost/sync" perrors "github.com/pkg/errors" - "gopkg.in/yaml.v2" ) import ( @@ -47,7 +47,7 @@ var ( clientGrpool *gxsync.TaskPool ) -func doInit(protocol string) { +func initClient(protocol string) { if protocol == "" { return } @@ -134,8 +134,9 @@ type Client struct { opts Options conf ClientConfig pool *gettyRPCClientPool - codec *remoting.Codec - responseHandler *remoting.ResponseHandler + codec remoting.Codec + responseHandler remoting.ResponseHandler + ExchangeClient *remoting.ExchangeClient //sequence atomic.Uint64 //pendingResponses *sync.Map } @@ -149,18 +150,24 @@ func NewClient(opt Options) *Client { c := &Client{ opts: opt, - conf: *clientConf, } - c.pool = newGettyRPCClientConnPool(c, clientConf.PoolSize, time.Duration(int(time.Second)*clientConf.PoolTTL)) - return c } +func (c *Client) SetExchangeClient(client *remoting.ExchangeClient) { + c.ExchangeClient = client +} +func (c *Client) SetResponseHandler(responseHandler remoting.ResponseHandler) { + c.responseHandler = responseHandler +} func (c *Client) Connect(url common.URL) { - doInit(url.Protocol) + initClient(url.Protocol) + c.conf = *clientConf + // new client + c.pool = newGettyRPCClientConnPool(c, clientConf.PoolSize, time.Duration(int(time.Second)*clientConf.PoolTTL)) // codec c.codec = remoting.GetCodec(url.Protocol) - c.addr = url.Ip + ":" + url.Port + c.addr = url.Location } func (c *Client) Close() { if c.pool != nil { @@ -340,13 +347,13 @@ func (c *Client) heartbeat(session getty.Session) error { req := remoting.NewRequest("2.0.2") req.TwoWay = true req.Event = true - resp := remoting.NewPendingResponse() + resp := remoting.NewPendingResponse(req.Id) + remoting.AddPendingResponse(resp) return c.transfer(session, req, 3*time.Second, resp) } func (c *Client) transfer(session getty.Session, request *remoting.Request, timeout time.Duration, rsp *remoting.PendingResponse) error { - //sequence = c.sequence.Add(1) // //if pkg == nil { diff --git a/remoting/getty/listener.go b/remoting/getty/listener.go index daa1a583c1..0dc2792a23 100644 --- a/remoting/getty/listener.go +++ b/remoting/getty/listener.go @@ -107,7 +107,7 @@ func (h *RpcClientHandler) OnMessage(session getty.Session, pkg interface{}) { if p.Error != nil { logger.Errorf("rpc heartbeat response{error: %#v}", p.Error) } - (*h.conn.pool.rpcClient.responseHandler).Handler(p) + (h.conn.pool.rpcClient.responseHandler).Handler(p) //FIXME //if p.Header.Type&hessian.PackageResponse != 0x00 { // logger.Debugf("get rpc heartbeat response{header: %#v, body: %#v}", p.Header, p.Body) @@ -126,7 +126,7 @@ func (h *RpcClientHandler) OnMessage(session getty.Session, pkg interface{}) { h.conn.updateSession(session) - (*h.conn.pool.rpcClient.responseHandler).Handler(p) + (h.conn.pool.rpcClient.responseHandler).Handler(p) // //pendingResponse := h.conn.pool.rpcClient.removePendingResponse(SequenceType(p.Header.ID)) @@ -239,6 +239,9 @@ func (h *RpcServerHandler) OnMessage(session getty.Session, pkg interface{}) { } resp := remoting.NewResponse(req.Id, req.Version) resp.Status = hessian.Response_OK + resp.Event = req.Event + resp.SerialID = req.SerialID + resp.Version = "2.0.2" // heartbeat if req.Event { @@ -275,11 +278,11 @@ func (h *RpcServerHandler) OnMessage(session getty.Session, pkg interface{}) { }() - invoc, ok := req.Data.(invocation.RPCInvocation) + invoc, ok := req.Data.(*invocation.RPCInvocation) if !ok { } - result := h.server.requestHandler(&invoc) + result := h.server.requestHandler(invoc) if !req.TwoWay { return } @@ -316,7 +319,7 @@ 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 { +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. diff --git a/remoting/getty/readwriter.go b/remoting/getty/readwriter.go index 721df105a8..b59d9b82fb 100644 --- a/remoting/getty/readwriter.go +++ b/remoting/getty/readwriter.go @@ -18,7 +18,6 @@ package getty import ( - "bytes" "github.com/apache/dubbo-go/remoting" "reflect" ) @@ -49,9 +48,8 @@ func NewRpcClientPackageHandler(client *Client) *RpcClientPackageHandler { func (p *RpcClientPackageHandler) Read(ss getty.Session, data []byte) (interface{}, int, error) { //pkg := &DubboPackage{} - - buf := bytes.NewBuffer(data) - resp, length, err := (*p.client.codec).DecodeResponse(buf) + //p.client.ExchangeClient.GetPendingResponse(remoting.SequenceType()) + resp, length, err := (p.client.codec).DecodeResponse(data) //err := pkg.Unmarshal(buf, p.client) if err != nil { originErr := perrors.Cause(err) @@ -78,7 +76,7 @@ func (p *RpcClientPackageHandler) Write(ss getty.Session, pkg interface{}) ([]by return nil, perrors.New("invalid rpc request") } - buf, err := (*p.client.codec).EncodeRequest(req) + buf, err := (p.client.codec).EncodeRequest(req) if err != nil { logger.Warnf("binary.Write(req{%#v}) = err{%#v}", req, perrors.WithStack(err)) return nil, perrors.WithStack(err) @@ -91,18 +89,21 @@ func (p *RpcClientPackageHandler) Write(ss getty.Session, pkg interface{}) ([]by // RpcServerPackageHandler //////////////////////////////////////////// -var ( - rpcServerPkgHandler = &RpcServerPackageHandler{} -) +//var ( +// rpcServerPkgHandler = &RpcServerPackageHandler{} +//) // RpcServerPackageHandler ... type RpcServerPackageHandler struct { server *Server } +func NewRpcServerPackageHandler(server *Server) *RpcServerPackageHandler { + return &RpcServerPackageHandler{server: server} +} + func (p *RpcServerPackageHandler) Read(ss getty.Session, data []byte) (interface{}, int, error) { - buf := bytes.NewBuffer(data) - req, length, err := (*p.server.codec).DecodeRequest(buf) + req, length, err := (p.server.codec).DecodeRequest(data) //resp,len, err := (*p.).DecodeResponse(buf) if err != nil { @@ -126,7 +127,7 @@ func (p *RpcServerPackageHandler) Write(ss getty.Session, pkg interface{}) ([]by return nil, perrors.New("invalid rpc response") } - buf, err := (*p.server.codec).EncodeResponse(res) + buf, err := (p.server.codec).EncodeResponse(res) if err != nil { logger.Warnf("binary.Write(res{%#v}) = err{%#v}", res, perrors.WithStack(err)) return nil, perrors.WithStack(err) diff --git a/remoting/getty/server.go b/remoting/getty/server.go index 7ce9aafab1..00fba5cae3 100644 --- a/remoting/getty/server.go +++ b/remoting/getty/server.go @@ -105,7 +105,7 @@ func SetServerGrpool() { type Server struct { conf ServerConfig addr string - codec *remoting.Codec + codec remoting.Codec tcpServer getty.Server rpcHandler *RpcServerHandler requestHandler func(*invocation.RPCInvocation) protocol.RPCResult @@ -114,7 +114,7 @@ type Server struct { // NewServer ... func NewServer(url common.URL, handlers func(*invocation.RPCInvocation) protocol.RPCResult) *Server { //init - doInit(url.Protocol) + initServer(url.Protocol) s := &Server{ conf: *srvConf, @@ -153,7 +153,7 @@ func (s *Server) newSession(session getty.Session) error { session.SetName(conf.GettySessionParam.SessionName) session.SetMaxMsgLen(conf.GettySessionParam.MaxMsgLen) - session.SetPkgHandler(rpcServerPkgHandler) + session.SetPkgHandler(NewRpcServerPackageHandler(s)) session.SetEventListener(s.rpcHandler) session.SetWQLen(conf.GettySessionParam.PkgWQSize) session.SetReadTimeout(conf.GettySessionParam.tcpReadTimeout) From 0ba15c85de6501f48c1300a0d9bd20b23c969a6f Mon Sep 17 00:00:00 2001 From: cvictory Date: Fri, 8 May 2020 17:37:55 +0800 Subject: [PATCH 03/44] refactor getty --- protocol/dubbo/client.go | 366 ------------- protocol/dubbo/client_test.go | 300 ----------- protocol/dubbo/codec.go | 156 ------ protocol/dubbo/codec_test.go | 83 --- protocol/dubbo/config.go | 209 -------- protocol/dubbo/dubbo_codec.go | 45 +- protocol/dubbo/dubbo_codec_test.go | 1 + protocol/dubbo/dubbo_invoker_test.go | 175 +++++- protocol/dubbo/dubbo_protocol.go | 34 +- protocol/dubbo/dubbo_protocol_test.go | 158 +++--- protocol/dubbo/listener.go | 369 ------------- protocol/dubbo/listener_test.go | 58 -- protocol/dubbo/pool.go | 404 -------------- protocol/dubbo/readwriter.go | 186 ------- protocol/dubbo/server.go | 179 ------- remoting/codec.go | 8 +- remoting/exchange.go | 9 +- remoting/exchange_client.go | 24 +- remoting/getty/client.go | 364 ------------- remoting/getty/client_test.go | 300 ----------- remoting/getty/dubbo_codec_for_test.go | 359 +++++++++++++ remoting/getty/getty_client.go | 213 +------- remoting/getty/getty_client_test.go | 497 ++++++++++++++++++ remoting/getty/{server.go => getty_server.go} | 12 +- remoting/getty/listener.go | 88 ++-- remoting/getty/listener_test.go | 89 ++-- remoting/getty/readwriter.go | 25 +- 27 files changed, 1309 insertions(+), 3402 deletions(-) delete mode 100644 protocol/dubbo/client.go delete mode 100644 protocol/dubbo/client_test.go delete mode 100644 protocol/dubbo/codec.go delete mode 100644 protocol/dubbo/codec_test.go delete mode 100644 protocol/dubbo/config.go create mode 100644 protocol/dubbo/dubbo_codec_test.go delete mode 100644 protocol/dubbo/listener.go delete mode 100644 protocol/dubbo/listener_test.go delete mode 100644 protocol/dubbo/pool.go delete mode 100644 protocol/dubbo/readwriter.go delete mode 100644 protocol/dubbo/server.go delete mode 100644 remoting/getty/client.go delete mode 100644 remoting/getty/client_test.go create mode 100644 remoting/getty/dubbo_codec_for_test.go create mode 100644 remoting/getty/getty_client_test.go rename remoting/getty/{server.go => getty_server.go} (98%) diff --git a/protocol/dubbo/client.go b/protocol/dubbo/client.go deleted file mode 100644 index 630e9a7686..0000000000 --- a/protocol/dubbo/client.go +++ /dev/null @@ -1,366 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dubbo -// -//import ( -// "math/rand" -// "strings" -// "sync" -// "time" -//) -// -//import ( -// 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 -//) -// -//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 ... -//func SetClientConf(c ClientConfig) { -// clientConf = &c -// err := clientConf.CheckValidity() -// if err != nil { -// logger.Warnf("[ClientConfig CheckValidity] error: %v", err) -// return -// } -// setClientGrpool() -//} -// -//// GetClientConf ... -//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 ... -//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 ... -//type Client struct { -// opts Options -// conf ClientConfig -// pool *gettyRPCClientPool -// sequence atomic.Uint64 -// -// pendingResponses *sync.Map -//} -// -//// NewClient ... -//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 ... -//type Request struct { -// addr string -// svcUrl common.URL -// method string -// args interface{} -// atta map[string]string -//} -// -//// NewRequest ... -//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 ... -//type Response struct { -// reply interface{} -// atta map[string]string -//} -// -//// NewResponse ... -//func NewResponse(reply interface{}, atta map[string]string) *Response { -// return &Response{ -// reply: reply, -// atta: atta, -// } -//} -// -//// CallOneway call one way -//func (c *Client) CallOneway(request *Request) error { -// -// return perrors.WithStack(c.call(CT_OneWay, request, NewResponse(nil, nil), nil)) -//} -// -//// 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 -// } -// -// return perrors.WithStack(c.call(ct, request, response, nil)) -//} -// -//// 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 -// } -// -// 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 ... -//func (c *Client) Close() { -// if c.pool != nil { -// c.pool.close() -// } -// c.pool = nil -//} -// -//func (c *Client) selectSession(addr string) (*gettyRPCClient, getty.Session, error) { -// rpcClient, err := c.pool.getGettyRpcClient(DUBBO, addr) -// if err != nil { -// return nil, nil, perrors.WithStack(err) -// } -// return rpcClient, rpcClient.selectSession(), nil -//} -// -//func (c *Client) heartbeat(session getty.Session) error { -// return c.transfer(session, nil, NewPendingResponse()) -//} -// -//func (c *Client) transfer(session getty.Session, pkg *DubboPackage, -// rsp *PendingResponse) error { -// -// var ( -// sequence uint64 -// err error -// ) -// -// sequence = c.sequence.Add(1) -// -// if pkg == nil { -// pkg = &DubboPackage{} -// pkg.Body = hessian.NewRequest([]interface{}{}, nil) -// pkg.Body = []interface{}{} -// pkg.Header.Type = hessian.PackageHeartbeat -// pkg.Header.SerialID = byte(S_Dubbo) -// } -// pkg.Header.ID = int64(sequence) -// -// // cond1 -// if rsp != nil { -// rsp.seq = sequence -// c.addPendingResponse(rsp) -// } -// -// err = session.WritePkg(pkg, c.opts.RequestTimeout) -// if err != nil { -// c.removePendingResponse(SequenceType(rsp.seq)) -// } else if rsp != nil { // cond2 -// // cond2 should not merged with cond1. cause the response package may be returned very -// // soon and it will be handled by other goroutine. -// rsp.readStart = time.Now() -// } -// -// return perrors.WithStack(err) -//} -// -//func (c *Client) addPendingResponse(pr *PendingResponse) { -// c.pendingResponses.Store(SequenceType(pr.seq), pr) -//} -// -//func (c *Client) removePendingResponse(seq SequenceType) *PendingResponse { -// if c.pendingResponses == nil { -// return nil -// } -// if presp, ok := c.pendingResponses.Load(seq); ok { -// c.pendingResponses.Delete(seq) -// return presp.(*PendingResponse) -// } -// return nil -//} diff --git a/protocol/dubbo/client_test.go b/protocol/dubbo/client_test.go deleted file mode 100644 index 4f5913c56f..0000000000 --- a/protocol/dubbo/client_test.go +++ /dev/null @@ -1,300 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dubbo -// -//import ( -// "bytes" -// "context" -// "sync" -// "testing" -// "time" -//) -// -//import ( -// hessian "github.com/apache/dubbo-go-hessian2" -// perrors "github.com/pkg/errors" -// "github.com/stretchr/testify/assert" -//) -// -//import ( -// "github.com/apache/dubbo-go/common" -// "github.com/apache/dubbo-go/common/proxy/proxy_factory" -// "github.com/apache/dubbo-go/protocol" -//) -// -//func TestClient_CallOneway(t *testing.T) { -// proto, url := InitTest(t) -// -// c := &Client{ -// pendingResponses: new(sync.Map), -// conf: *clientConf, -// opts: Options{ -// ConnectTimeout: 3e9, -// RequestTimeout: 6e9, -// }, -// } -// c.pool = newGettyRPCClientConnPool(c, clientConf.PoolSize, time.Duration(int(time.Second)*clientConf.PoolTTL)) -// -// //user := &User{} -// err := c.CallOneway(NewRequest("127.0.0.1:20000", url, "GetUser", []interface{}{"1", "username"}, nil)) -// assert.NoError(t, err) -// -// // destroy -// proto.Destroy() -//} -// -//func TestClient_Call(t *testing.T) { -// proto, url := InitTest(t) -// -// c := &Client{ -// pendingResponses: new(sync.Map), -// conf: *clientConf, -// opts: Options{ -// ConnectTimeout: 3e9, -// RequestTimeout: 10e9, -// }, -// } -// c.pool = newGettyRPCClientConnPool(c, clientConf.PoolSize, time.Duration(int(time.Second)*clientConf.PoolTTL)) -// -// var ( -// user *User -// err error -// ) -// -// user = &User{} -// err = c.Call(NewRequest("127.0.0.1:20000", url, "GetBigPkg", []interface{}{nil}, nil), NewResponse(user, nil)) -// assert.NoError(t, err) -// assert.NotEqual(t, "", user.Id) -// assert.NotEqual(t, "", user.Name) -// -// user = &User{} -// err = c.Call(NewRequest("127.0.0.1:20000", url, "GetUser", []interface{}{"1", "username"}, nil), NewResponse(user, nil)) -// assert.NoError(t, err) -// assert.Equal(t, User{Id: "1", Name: "username"}, *user) -// -// user = &User{} -// err = c.Call(NewRequest("127.0.0.1:20000", url, "GetUser0", []interface{}{"1", nil, "username"}, nil), NewResponse(user, nil)) -// assert.NoError(t, err) -// assert.Equal(t, User{Id: "1", Name: "username"}, *user) -// -// err = c.Call(NewRequest("127.0.0.1:20000", url, "GetUser1", []interface{}{}, nil), NewResponse(user, nil)) -// assert.NoError(t, err) -// -// err = c.Call(NewRequest("127.0.0.1:20000", url, "GetUser2", []interface{}{}, nil), 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)) -// 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)) -// 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)) -// 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)) -// 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)) -// assert.NoError(t, err) -// assert.Equal(t, User{Id: "1", Name: ""}, *user) -// -// // destroy -// proto.Destroy() -//} -// -//func TestClient_AsyncCall(t *testing.T) { -// proto, url := InitTest(t) -// -// c := &Client{ -// pendingResponses: new(sync.Map), -// conf: *clientConf, -// opts: Options{ -// ConnectTimeout: 3e9, -// RequestTimeout: 6e9, -// }, -// } -// c.pool = newGettyRPCClientConnPool(c, clientConf.PoolSize, time.Duration(int(time.Second)*clientConf.PoolTTL)) -// -// user := &User{} -// lock := sync.Mutex{} -// lock.Lock() -// err := c.AsyncCall(NewRequest("127.0.0.1:20000", url, "GetUser", []interface{}{"1", "username"}, nil), func(response common.CallbackResponse) { -// r := response.(AsyncCallbackResponse) -// assert.Equal(t, User{Id: "1", Name: "username"}, *r.Reply.(*Response).reply.(*User)) -// lock.Unlock() -// }, NewResponse(user, nil)) -// assert.NoError(t, err) -// assert.Equal(t, User{}, *user) -// -// // destroy -// lock.Lock() -// proto.Destroy() -// lock.Unlock() -//} -// -//func InitTest(t *testing.T) (protocol.Protocol, common.URL) { -// -// hessian.RegisterPOJO(&User{}) -// -// methods, err := common.ServiceMap.Register("dubbo", &UserProvider{}) -// assert.NoError(t, err) -// assert.Equal(t, "GetBigPkg,GetUser,GetUser0,GetUser1,GetUser2,GetUser3,GetUser4,GetUser5,GetUser6", methods) -// -// // config -// SetClientConf(ClientConfig{ -// ConnectionNum: 2, -// HeartbeatPeriod: "5s", -// SessionTimeout: "20s", -// PoolTTL: 600, -// PoolSize: 64, -// GettySessionParam: GettySessionParam{ -// CompressEncoding: false, -// TcpNoDelay: true, -// TcpKeepAlive: true, -// KeepAlivePeriod: "120s", -// TcpRBufSize: 262144, -// TcpWBufSize: 65536, -// PkgWQSize: 512, -// TcpReadTimeout: "4s", -// TcpWriteTimeout: "5s", -// WaitTimeout: "1s", -// MaxMsgLen: 10240000000, -// SessionName: "client", -// }, -// }) -// assert.NoError(t, clientConf.CheckValidity()) -// SetServerConfig(ServerConfig{ -// SessionNumber: 700, -// SessionTimeout: "20s", -// GettySessionParam: GettySessionParam{ -// CompressEncoding: false, -// TcpNoDelay: true, -// TcpKeepAlive: true, -// KeepAlivePeriod: "120s", -// TcpRBufSize: 262144, -// TcpWBufSize: 65536, -// PkgWQSize: 512, -// TcpReadTimeout: "1s", -// TcpWriteTimeout: "5s", -// WaitTimeout: "1s", -// MaxMsgLen: 10240000000, -// SessionName: "server", -// }}) -// assert.NoError(t, srvConf.CheckValidity()) -// -// // Export -// proto := GetProtocol() -// url, err := common.NewURL("dubbo://127.0.0.1:20000/UserProvider?anyhost=true&" + -// "application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&" + -// "environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&" + -// "module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&" + -// "side=provider&timeout=3000×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 < 4000; i++ { -// argBuf.WriteString("击鼓其镗,踊跃用兵。土国城漕,我独南行。从孙子仲,平陈与宋。不我以归,忧心有忡。爰居爰处?爰丧其马?于以求之?于林之下。死生契阔,与子成说。执子之手,与子偕老。于嗟阔兮,不我活兮。于嗟洵兮,不我信兮。") -// argBuf.WriteString("击鼓其镗,踊跃用兵。土国城漕,我独南行。从孙子仲,平陈与宋。不我以归,忧心有忡。爰居爰处?爰丧其马?于以求之?于林之下。死生契阔,与子成说。执子之手,与子偕老。于嗟阔兮,不我活兮。于嗟洵兮,不我信兮。") -// } -// rsp.Id = argBuf.String() -// rsp.Name = argBuf.String() -// return nil -//} -// -//func (u *UserProvider) GetUser(ctx context.Context, req []interface{}, rsp *User) error { -// rsp.Id = req[0].(string) -// rsp.Name = req[1].(string) -// return nil -//} -// -//func (u *UserProvider) GetUser0(id string, k *User, name string) (User, error) { -// return User{Id: id, Name: name}, nil -//} -// -//func (u *UserProvider) GetUser1() error { -// return nil -//} -// -//func (u *UserProvider) GetUser2() error { -// return perrors.New("error") -//} -// -//func (u *UserProvider) GetUser3(rsp *[]interface{}) error { -// *rsp = append(*rsp, User{Id: "1", Name: "username"}) -// return nil -//} -// -//func (u *UserProvider) GetUser4(ctx context.Context, req []interface{}) ([]interface{}, error) { -// -// return []interface{}{User{Id: req[0].([]interface{})[0].(string), Name: req[0].([]interface{})[1].(string)}}, nil -//} -// -//func (u *UserProvider) GetUser5(ctx context.Context, req []interface{}) (map[interface{}]interface{}, error) { -// return map[interface{}]interface{}{"key": User{Id: req[0].(map[interface{}]interface{})["id"].(string), Name: req[0].(map[interface{}]interface{})["name"].(string)}}, nil -//} -// -//func (u *UserProvider) GetUser6(id int64) (*User, error) { -// if id == 0 { -// return nil, nil -// } -// return &User{Id: "1"}, nil -//} -// -//func (u *UserProvider) Reference() string { -// return "UserProvider" -//} -// -//func (u User) JavaClassName() string { -// return "com.ikurento.user.User" -//} diff --git a/protocol/dubbo/codec.go b/protocol/dubbo/codec.go deleted file mode 100644 index 8dd91c2d31..0000000000 --- a/protocol/dubbo/codec.go +++ /dev/null @@ -1,156 +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 -//} -// -//func (p DubboPackage) String() string { -// return fmt.Sprintf("DubboPackage: Header-%v, Path-%v, Body-%v", p.Header, p.Service, p.Body) -//} -// -//// Marshal ... -//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 ... -//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 ... -//type PendingResponse struct { -// seq uint64 -// err error -// start time.Time -// readStart time.Time -// callback common.AsyncCallback -// response *Response -// done chan struct{} -//} -// -//// NewPendingResponse ... -//func NewPendingResponse() *PendingResponse { -// return &PendingResponse{ -// start: time.Now(), -// response: &Response{}, -// done: make(chan struct{}), -// } -//} -// -//// GetCallResponse ... -//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 c9599da6c7..0000000000 --- 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/config.go b/protocol/dubbo/config.go deleted file mode 100644 index f00931e00f..0000000000 --- a/protocol/dubbo/config.go +++ /dev/null @@ -1,209 +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 ( -// "time" -//) -// -//import ( -// "github.com/dubbogo/getty" -// perrors "github.com/pkg/errors" -//) -// -//type ( -// // GettySessionParam ... -// GettySessionParam struct { -// CompressEncoding bool `default:"false" yaml:"compress_encoding" json:"compress_encoding,omitempty"` -// TcpNoDelay bool `default:"true" yaml:"tcp_no_delay" json:"tcp_no_delay,omitempty"` -// 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 -// 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 -// TcpWriteTimeout string `default:"5s" yaml:"tcp_write_timeout" json:"tcp_write_timeout,omitempty"` -// tcpWriteTimeout time.Duration -// WaitTimeout string `default:"7s" yaml:"wait_timeout" json:"wait_timeout,omitempty"` -// waitTimeout 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"` -// } -// -// // ServerConfig -// //Config 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"` -// -// // grpool -// GrPoolSize int `default:"0" yaml:"gr_pool_size" json:"gr_pool_size,omitempty"` -// QueueLen int `default:"0" yaml:"queue_len" json:"queue_len,omitempty"` -// QueueNumber int `default:"0" yaml:"queue_number" json:"queue_number,omitempty"` -// -// // session tcp parameters -// GettySessionParam GettySessionParam `required:"true" yaml:"getty_session_param" json:"getty_session_param,omitempty"` -// } -// -// // ClientConfig -// //Config holds supported types by the multiconfig package -// ClientConfig struct { -// ReconnectInterval int `default:"0" yaml:"reconnect_interval" json:"reconnect_interval,omitempty"` -// -// // 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 -// -// // session -// SessionTimeout string `default:"60s" yaml:"session_timeout" json:"session_timeout,omitempty"` -// sessionTimeout time.Duration -// -// // Connection Pool -// PoolSize int `default:"2" yaml:"pool_size" json:"pool_size,omitempty"` -// PoolTTL int `default:"180" yaml:"pool_ttl" json:"pool_ttl,omitempty"` -// -// // grpool -// GrPoolSize int `default:"0" yaml:"gr_pool_size" json:"gr_pool_size,omitempty"` -// QueueLen int `default:"0" yaml:"queue_len" json:"queue_len,omitempty"` -// QueueNumber int `default:"0" yaml:"queue_number" json:"queue_number,omitempty"` -// -// // session tcp parameters -// GettySessionParam GettySessionParam `required:"true" yaml:"getty_session_param" json:"getty_session_param,omitempty"` -// } -//) -// -//// GetDefaultClientConfig ... -//func GetDefaultClientConfig() ClientConfig { -// return ClientConfig{ -// ReconnectInterval: 0, -// ConnectionNum: 16, -// HeartbeatPeriod: "30s", -// SessionTimeout: "180s", -// PoolSize: 4, -// PoolTTL: 600, -// GrPoolSize: 200, -// QueueLen: 64, -// QueueNumber: 10, -// GettySessionParam: GettySessionParam{ -// CompressEncoding: false, -// TcpNoDelay: true, -// TcpKeepAlive: true, -// KeepAlivePeriod: "180s", -// TcpRBufSize: 262144, -// TcpWBufSize: 65536, -// PkgWQSize: 512, -// TcpReadTimeout: "1s", -// TcpWriteTimeout: "5s", -// WaitTimeout: "1s", -// MaxMsgLen: 102400, -// SessionName: "client", -// }} -//} -// -//// GetDefaultServerConfig ... -//func GetDefaultServerConfig() ServerConfig { -// return ServerConfig{ -// SessionTimeout: "180s", -// SessionNumber: 700, -// GrPoolSize: 120, -// QueueNumber: 6, -// QueueLen: 64, -// GettySessionParam: GettySessionParam{ -// CompressEncoding: false, -// TcpNoDelay: true, -// TcpKeepAlive: true, -// KeepAlivePeriod: "180s", -// TcpRBufSize: 262144, -// TcpWBufSize: 65536, -// PkgWQSize: 512, -// TcpReadTimeout: "1s", -// TcpWriteTimeout: "5s", -// WaitTimeout: "1s", -// MaxMsgLen: 102400, -// SessionName: "server", -// }, -// } -//} -// -//// CheckValidity ... -//func (c *GettySessionParam) CheckValidity() error { -// var err error -// -// if c.keepAlivePeriod, 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 { -// return perrors.WithMessagef(err, "time.ParseDuration(TcpReadTimeout{%#v})", c.TcpReadTimeout) -// } -// -// if c.tcpWriteTimeout, 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 { -// return perrors.WithMessagef(err, "time.ParseDuration(WaitTimeout{%#v})", c.WaitTimeout) -// } -// -// return nil -//} -// -//// CheckValidity ... -//func (c *ClientConfig) CheckValidity() error { -// var err error -// -// c.ReconnectInterval = c.ReconnectInterval * 1e6 -// -// if c.heartbeatPeriod, err = time.ParseDuration(c.HeartbeatPeriod); err != nil { -// return perrors.WithMessagef(err, "time.ParseDuration(HeartbeatPeroid{%#v})", c.HeartbeatPeriod) -// } -// -// if c.heartbeatPeriod >= time.Duration(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 { -// return perrors.WithMessagef(err, "time.ParseDuration(SessionTimeout{%#v})", c.SessionTimeout) -// } -// -// return perrors.WithStack(c.GettySessionParam.CheckValidity()) -//} -// -//// CheckValidity ... -//func (c *ServerConfig) CheckValidity() error { -// var err error -// -// if c.sessionTimeout, err = time.ParseDuration(c.SessionTimeout); err != nil { -// return perrors.WithMessagef(err, "time.ParseDuration(SessionTimeout{%#v})", c.SessionTimeout) -// } -// -// if c.sessionTimeout >= time.Duration(getty.MaxWheelTimeSpan) { -// return perrors.WithMessagef(err, "session_timeout %s should be less than %s", -// c.SessionTimeout, time.Duration(getty.MaxWheelTimeSpan)) -// } -// -// return perrors.WithStack(c.GettySessionParam.CheckValidity()) -//} diff --git a/protocol/dubbo/dubbo_codec.go b/protocol/dubbo/dubbo_codec.go index 899148a8e0..c1f8231385 100644 --- a/protocol/dubbo/dubbo_codec.go +++ b/protocol/dubbo/dubbo_codec.go @@ -4,6 +4,9 @@ import ( "bufio" "bytes" "fmt" + "strconv" + "time" + hessian "github.com/apache/dubbo-go-hessian2" "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/common/logger" @@ -11,8 +14,6 @@ import ( "github.com/apache/dubbo-go/protocol/invocation" "github.com/apache/dubbo-go/remoting" perrors "github.com/pkg/errors" - "strconv" - "time" ) //SerialID serial ID @@ -171,8 +172,9 @@ func (c *DubboCodec) EncodeResponse(response *remoting.Response) (*bytes.Buffer, } if !response.IsHeartbeat() { resp.Body = &hessian.Response{ - RspObj: response.Result.(protocol.RPCResult).Rest, - Exception: response.Result.(protocol.RPCResult).Err, + RspObj: response.Result.(protocol.RPCResult).Rest, + Exception: response.Result.(protocol.RPCResult).Err, + Attachments: response.Result.(protocol.RPCResult).Attrs, } } @@ -190,7 +192,29 @@ func (c *DubboCodec) EncodeResponse(response *remoting.Response) (*bytes.Buffer, return bytes.NewBuffer(pkg), nil } -func (c *DubboCodec) DecodeRequest(data []byte) (*remoting.Request, int, error) { +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, err + } + return remoting.DecodeResult{IsRequest: true, Result: req}, len, err + } else { + resp, len, err := c.decodeResponse(data) + if err != nil { + return remoting.DecodeResult{}, len, err + } + return remoting.DecodeResult{IsRequest: false, Result: resp}, len, err + } +} +func (c *DubboCodec) isRequest(data []byte) bool { + if data[2]&byte(0x80) == 0x00 { + return false + } + return true +} + +func (c *DubboCodec) decodeRequest(data []byte) (*remoting.Request, int, error) { pkg := &DubboPackage{ Body: make([]interface{}, 7), } @@ -201,7 +225,7 @@ func (c *DubboCodec) DecodeRequest(data []byte) (*remoting.Request, int, error) originErr := perrors.Cause(err) if originErr == hessian.ErrHeaderNotEnough || originErr == hessian.ErrBodyNotEnough { //FIXME - return request, 0, originErr + return nil, 0, originErr } logger.Errorf("pkg.Unmarshal(len(@data):%d) = error:%+v", buf.Len(), err) @@ -274,7 +298,7 @@ func (c *DubboCodec) DecodeRequest(data []byte) (*remoting.Request, int, error) return request, hessian.HEADER_LENGTH + pkg.Header.BodyLen, nil } -func (c *DubboCodec) DecodeResponse(data []byte) (*remoting.Response, int, error) { +func (c *DubboCodec) decodeResponse(data []byte) (*remoting.Response, int, error) { pkg := &DubboPackage{} buf := bytes.NewBuffer(data) response := &remoting.Response{} @@ -282,11 +306,11 @@ func (c *DubboCodec) DecodeResponse(data []byte) (*remoting.Response, int, error if err != nil { originErr := perrors.Cause(err) if originErr == hessian.ErrHeaderNotEnough || originErr == hessian.ErrBodyNotEnough { - return response, 0, nil + return nil, 0, originErr } logger.Errorf("pkg.Unmarshal(len(@data):%d) = error:%+v", buf.Len(), err) - return response, 0, perrors.WithStack(err) + return nil, 0, perrors.WithStack(err) } response = &remoting.Response{ Id: pkg.Header.ID, @@ -316,6 +340,9 @@ func (c *DubboCodec) DecodeResponse(data []byte) (*remoting.Response, int, error if pkg.Header.Type&hessian.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 diff --git a/protocol/dubbo/dubbo_codec_test.go b/protocol/dubbo/dubbo_codec_test.go new file mode 100644 index 0000000000..f401318d86 --- /dev/null +++ b/protocol/dubbo/dubbo_codec_test.go @@ -0,0 +1 @@ +package dubbo diff --git a/protocol/dubbo/dubbo_invoker_test.go b/protocol/dubbo/dubbo_invoker_test.go index 1a64301f82..ddb7b783d1 100644 --- a/protocol/dubbo/dubbo_invoker_test.go +++ b/protocol/dubbo/dubbo_invoker_test.go @@ -18,35 +18,31 @@ package dubbo import ( + "bytes" "context" "sync" "testing" "time" -) -import ( - "github.com/opentracing/opentracing-go" - "github.com/stretchr/testify/assert" -) + "github.com/apache/dubbo-go/remoting" -import ( + "github.com/apache/dubbo-go/remoting/getty" + + hessian "github.com/apache/dubbo-go-hessian2" "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/opentracing/opentracing-go" + perrors "github.com/pkg/errors" + "github.com/stretchr/testify/assert" ) func TestDubboInvoker_Invoke(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{} @@ -58,7 +54,9 @@ func TestDubboInvoker_Invoke(t *testing.T) { res := invoker.Invoke(context.Background(), inv) assert.NoError(t, res.Error()) assert.Equal(t, User{Id: "1", Name: "username"}, *res.Result().(*User)) - assert.Equal(t, "test_value", res.Attachments()["test_key"]) // test attachments for request/response + //result will not contain attachment + // attachment in result it is useless. + //assert.Equal(t, "test_value", res.Attachments()["test_key"]) // test attachments for request/response // CallOneway inv.SetAttachments(constant.ASYNC_KEY, "true") @@ -69,8 +67,10 @@ func 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) + rst := *r.Reply.(*remoting.Response).Result.(*protocol.RPCResult) + assert.Equal(t, User{Id: "1", Name: "username"}, *(rst.Rest.(*User))) + //assert.Equal(t, User{Id: "1", Name: "username"}, *r.Reply.(*Response).reply.(*User)) lock.Unlock() }) res = invoker.Invoke(context.Background(), inv) @@ -92,3 +92,142 @@ func TestDubboInvoker_Invoke(t *testing.T) { proto.Destroy() lock.Unlock() } + +func InitTest(t *testing.T) (protocol.Protocol, common.URL) { + + hessian.RegisterPOJO(&User{}) + + methods, err := common.ServiceMap.Register("dubbo", &UserProvider{}) + assert.NoError(t, err) + assert.Equal(t, "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++ { + argBuf.WriteString("击鼓其镗,踊跃用兵。土国城漕,我独南行。从孙子仲,平陈与宋。不我以归,忧心有忡。爰居爰处?爰丧其马?于以求之?于林之下。死生契阔,与子成说。执子之手,与子偕老。于嗟阔兮,不我活兮。于嗟洵兮,不我信兮。") + argBuf.WriteString("击鼓其镗,踊跃用兵。土国城漕,我独南行。从孙子仲,平陈与宋。不我以归,忧心有忡。爰居爰处?爰丧其马?于以求之?于林之下。死生契阔,与子成说。执子之手,与子偕老。于嗟阔兮,不我活兮。于嗟洵兮,不我信兮。") + } + rsp.Id = argBuf.String() + rsp.Name = argBuf.String() + return nil +} + +func (u *UserProvider) GetUser(ctx context.Context, req []interface{}, rsp *User) error { + rsp.Id = req[0].(string) + rsp.Name = req[1].(string) + return nil +} + +func (u *UserProvider) GetUser0(id string, k *User, name string) (User, error) { + return User{Id: id, Name: name}, nil +} + +func (u *UserProvider) GetUser1() error { + return nil +} + +func (u *UserProvider) GetUser2() error { + return perrors.New("error") +} + +func (u *UserProvider) GetUser3(rsp *[]interface{}) error { + *rsp = append(*rsp, User{Id: "1", Name: "username"}) + return nil +} + +func (u *UserProvider) GetUser4(ctx context.Context, req []interface{}) ([]interface{}, error) { + + return []interface{}{User{Id: req[0].([]interface{})[0].(string), Name: req[0].([]interface{})[1].(string)}}, nil +} + +func (u *UserProvider) GetUser5(ctx context.Context, req []interface{}) (map[interface{}]interface{}, error) { + return map[interface{}]interface{}{"key": User{Id: req[0].(map[interface{}]interface{})["id"].(string), Name: req[0].(map[interface{}]interface{})["name"].(string)}}, nil +} + +func (u *UserProvider) GetUser6(id int64) (*User, error) { + if id == 0 { + return nil, nil + } + return &User{Id: "1"}, nil +} + +func (u *UserProvider) Reference() string { + return "UserProvider" +} + +func (u User) JavaClassName() string { + return "com.ikurento.user.User" +} diff --git a/protocol/dubbo/dubbo_protocol.go b/protocol/dubbo/dubbo_protocol.go index 20e54fa2ee..3bf0341923 100644 --- a/protocol/dubbo/dubbo_protocol.go +++ b/protocol/dubbo/dubbo_protocol.go @@ -18,19 +18,20 @@ package dubbo import ( + "context" "fmt" - "github.com/apache/dubbo-go/protocol/invocation" - "github.com/apache/dubbo-go/remoting" - "github.com/apache/dubbo-go/remoting/getty" "sync" -) -import ( "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/common/extension" "github.com/apache/dubbo-go/common/logger" "github.com/apache/dubbo-go/config" "github.com/apache/dubbo-go/protocol" + "github.com/apache/dubbo-go/protocol/invocation" + "github.com/apache/dubbo-go/remoting" + "github.com/apache/dubbo-go/remoting/getty" + "github.com/opentracing/opentracing-go" ) // dubbo protocol constant @@ -154,14 +155,8 @@ func doHandleRequest(rpcInvocation *invocation.RPCInvocation) protocol.RPCResult } invoker := exporter.(protocol.Exporter).GetInvoker() if invoker != nil { - //attachments := rpcInvocation.Attachments() - //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) // FIXME - ctx := getty.RebuildCtx(rpcInvocation) + ctx := rebuildCtx(rpcInvocation) invokeResult := invoker.Invoke(ctx, rpcInvocation) if err := invokeResult.Error(); err != nil { @@ -200,3 +195,18 @@ func getExchangeClient(url common.URL) *remoting.ExchangeClient { } return exchangeClient } + +// rebuildCtx rebuild the context by attachment. +// Once we decided to transfer more context's key-value, we should change this. +// now we only support rebuild the tracing context +func rebuildCtx(inv *invocation.RPCInvocation) context.Context { + ctx := context.WithValue(context.Background(), "attachment", inv.Attachments()) + + // actually, if user do not use any opentracing framework, the err will not be nil. + spanCtx, err := opentracing.GlobalTracer().Extract(opentracing.TextMap, + opentracing.TextMapCarrier(inv.Attachments())) + if err == nil { + ctx = context.WithValue(ctx, constant.TRACING_REMOTE_SPAN_CTX, spanCtx) + } + return ctx +} diff --git a/protocol/dubbo/dubbo_protocol_test.go b/protocol/dubbo/dubbo_protocol_test.go index 5fdcc69b5c..932e94862d 100644 --- a/protocol/dubbo/dubbo_protocol_test.go +++ b/protocol/dubbo/dubbo_protocol_test.go @@ -16,84 +16,80 @@ */ package dubbo -// -//import ( -// "testing" -//) -// -//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" -//) -// -//func TestDubboProtocol_Export(t *testing.T) { -// // 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&" + -// "module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&" + -// "side=provider&timeout=3000×tamp=1556509797245") -// assert.NoError(t, err) -// exporter := proto.Export(protocol.NewBaseInvoker(url)) -// -// // make sure url -// eq := exporter.GetInvoker().GetUrl().URLEqual(url) -// assert.True(t, eq) -// -// // second service: the same path and the different version -// url2, 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&"+ -// "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 -// eq2 := exporter2.GetInvoker().GetUrl().URLEqual(url2) -// assert.True(t, eq2) -// -// // make sure exporterMap after 'Unexport' -// _, ok := proto.(*DubboProtocol).ExporterMap().Load(url.ServiceKey()) -// assert.True(t, ok) -// exporter.Unexport() -// _, ok = proto.(*DubboProtocol).ExporterMap().Load(url.ServiceKey()) -// assert.False(t, ok) -// -// // make sure serverMap after 'Destroy' -// _, ok = proto.(*DubboProtocol).serverMap[url.Location] -// assert.True(t, ok) -// proto.Destroy() -// _, ok = proto.(*DubboProtocol).serverMap[url.Location] -// assert.False(t, ok) -//} -// -//func TestDubboProtocol_Refer(t *testing.T) { -// // Refer -// proto := GetProtocol() -// url, err := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&" + -// "application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&" + -// "environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&" + -// "module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&" + -// "side=provider&timeout=3000×tamp=1556509797245") -// assert.NoError(t, err) -// clientConf = &ClientConfig{} -// invoker := proto.Refer(url) -// -// // make sure url -// eq := invoker.GetUrl().URLEqual(url) -// assert.True(t, eq) -// -// // make sure invokers after 'Destroy' -// invokersLen := len(proto.(*DubboProtocol).Invokers()) -// assert.Equal(t, 1, invokersLen) -// proto.Destroy() -// invokersLen = len(proto.(*DubboProtocol).Invokers()) -// assert.Equal(t, 0, invokersLen) -//} + +import ( + "testing" + + "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" + "github.com/stretchr/testify/assert" +) + +func TestDubboProtocol_Export(t *testing.T) { + // Export + proto := GetProtocol() + getty.SetServerConfig(getty.ServerConfig{}) + url, err := common.NewURL("dubbo://127.0.0.1:20080/com.ikurento.user.UserProvider?anyhost=true&" + + "application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&" + + "environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&" + + "module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&" + + "side=provider&timeout=3000×tamp=1556509797245") + assert.NoError(t, err) + exporter := proto.Export(protocol.NewBaseInvoker(url)) + + // make sure url + eq := exporter.GetInvoker().GetUrl().URLEqual(url) + assert.True(t, eq) + + // second service: the same path and the different version + url2, err := common.NewURL("dubbo://127.0.0.1:20080/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 + eq2 := exporter2.GetInvoker().GetUrl().URLEqual(url2) + assert.True(t, eq2) + + // make sure exporterMap after 'Unexport' + _, ok := proto.(*DubboProtocol).ExporterMap().Load(url.ServiceKey()) + assert.True(t, ok) + exporter.Unexport() + _, ok = proto.(*DubboProtocol).ExporterMap().Load(url.ServiceKey()) + assert.False(t, ok) + + // make sure serverMap after 'Destroy' + _, ok = proto.(*DubboProtocol).serverMap[url.Location] + assert.True(t, ok) + proto.Destroy() + _, ok = proto.(*DubboProtocol).serverMap[url.Location] + assert.False(t, ok) +} + +func TestDubboProtocol_Refer(t *testing.T) { + // Refer + proto := GetProtocol() + url, err := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&" + + "application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&" + + "environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&" + + "module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&" + + "side=provider&timeout=3000×tamp=1556509797245") + assert.NoError(t, err) + getty.SetClientConf(getty.ClientConfig{}) + invoker := proto.Refer(url) + + // make sure url + eq := invoker.GetUrl().URLEqual(url) + assert.True(t, eq) + + // make sure invokers after 'Destroy' + invokersLen := len(proto.(*DubboProtocol).Invokers()) + assert.Equal(t, 1, invokersLen) + proto.Destroy() + invokersLen = len(proto.(*DubboProtocol).Invokers()) + assert.Equal(t, 0, invokersLen) +} diff --git a/protocol/dubbo/listener.go b/protocol/dubbo/listener.go deleted file mode 100644 index 3d4db11f3d..0000000000 --- a/protocol/dubbo/listener.go +++ /dev/null @@ -1,369 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dubbo -// -//import ( -// "context" -// "fmt" -// "net/url" -// "sync" -// "sync/atomic" -// "time" -//) -// -//import ( -// "github.com/apache/dubbo-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" -//) -// -//// todo: WritePkg_Timeout will entry *.yml -//const ( -// // WritePkg_Timeout ... -// WritePkg_Timeout = 5 * time.Second -//) -// -//var ( -// errTooManySessions = perrors.New("too many sessions") -//) -// -//type rpcSession struct { -// session getty.Session -// reqNum int32 -//} -// -//func (s *rpcSession) AddReqNum(num int32) { -// atomic.AddInt32(&s.reqNum, num) -//} -// -//func (s *rpcSession) GetReqNum() int32 { -// return atomic.LoadInt32(&s.reqNum) -//} -// -//// ////////////////////////////////////////// -//// RpcClientHandler -//// ////////////////////////////////////////// -// -//// RpcClientHandler ... -//type RpcClientHandler struct { -// conn *gettyRPCClient -//} -// -//// NewRpcClientHandler ... -//func NewRpcClientHandler(client *gettyRPCClient) *RpcClientHandler { -// return &RpcClientHandler{conn: client} -//} -// -//// OnOpen ... -//func (h *RpcClientHandler) OnOpen(session getty.Session) error { -// h.conn.addSession(session) -// return nil -//} -// -//// OnError ... -//func (h *RpcClientHandler) OnError(session getty.Session, err error) { -// logger.Infof("session{%s} got error{%v}, will be closed.", session.Stat(), err) -// h.conn.removeSession(session) -//} -// -//// OnClose ... -//func (h *RpcClientHandler) OnClose(session getty.Session) { -// logger.Infof("session{%s} is closing......", session.Stat()) -// h.conn.removeSession(session) -//} -// -//// OnMessage ... -//func (h *RpcClientHandler) OnMessage(session getty.Session, pkg interface{}) { -// p, ok := pkg.(*DubboPackage) -// if !ok { -// logger.Errorf("illegal package") -// return -// } -// -// if p.Header.Type&hessian.PackageHeartbeat != 0x00 { -// if p.Header.Type&hessian.PackageResponse != 0x00 { -// logger.Debugf("get rpc heartbeat response{header: %#v, body: %#v}", p.Header, p.Body) -// if p.Err != nil { -// logger.Errorf("rpc heartbeat response{error: %#v}", p.Err) -// } -// h.conn.pool.rpcClient.removePendingResponse(SequenceType(p.Header.ID)) -// } else { -// logger.Debugf("get rpc heartbeat request{header: %#v, service: %#v, body: %#v}", p.Header, p.Service, p.Body) -// p.Header.ResponseStatus = hessian.Response_OK -// reply(session, p, hessian.PackageHeartbeat) -// } -// return -// } -// logger.Debugf("get rpc response{header: %#v, body: %#v}", p.Header, p.Body) -// -// h.conn.updateSession(session) -// -// pendingResponse := h.conn.pool.rpcClient.removePendingResponse(SequenceType(p.Header.ID)) -// if pendingResponse == nil { -// logger.Errorf("failed to get pending response context for response package %s", *p) -// return -// } -// -// if p.Err != nil { -// pendingResponse.err = p.Err -// } -// -// pendingResponse.response.atta = p.Body.(*Response).atta -// -// if pendingResponse.callback == nil { -// pendingResponse.done <- struct{}{} -// } else { -// pendingResponse.callback(pendingResponse.GetCallResponse()) -// } -//} -// -//// OnCron ... -//func (h *RpcClientHandler) OnCron(session getty.Session) { -// rpcSession, err := h.conn.getClientRpcSession(session) -// if err != nil { -// logger.Errorf("client.getClientSession(session{%s}) = error{%v}", -// session.Stat(), perrors.WithStack(err)) -// return -// } -// if h.conn.pool.rpcClient.conf.sessionTimeout.Nanoseconds() < time.Since(session.GetActive()).Nanoseconds() { -// logger.Warnf("session{%s} timeout{%s}, reqNum{%d}", -// session.Stat(), time.Since(session.GetActive()).String(), rpcSession.reqNum) -// h.conn.removeSession(session) // -> h.conn.close() -> h.conn.pool.remove(h.conn) -// return -// } -// -// h.conn.pool.rpcClient.heartbeat(session) -//} -// -//// ////////////////////////////////////////// -//// RpcServerHandler -//// ////////////////////////////////////////// -// -//// RpcServerHandler ... -//type RpcServerHandler struct { -// maxSessionNum int -// sessionTimeout time.Duration -// sessionMap map[getty.Session]*rpcSession -// rwlock sync.RWMutex -//} -// -//// NewRpcServerHandler ... -//func NewRpcServerHandler(maxSessionNum int, sessionTimeout time.Duration) *RpcServerHandler { -// return &RpcServerHandler{ -// maxSessionNum: maxSessionNum, -// sessionTimeout: sessionTimeout, -// sessionMap: make(map[getty.Session]*rpcSession), -// } -//} -// -//// OnOpen ... -//func (h *RpcServerHandler) OnOpen(session getty.Session) error { -// var err error -// h.rwlock.RLock() -// if h.maxSessionNum <= len(h.sessionMap) { -// err = errTooManySessions -// } -// h.rwlock.RUnlock() -// if err != nil { -// return perrors.WithStack(err) -// } -// -// logger.Infof("got session:%s", session.Stat()) -// h.rwlock.Lock() -// h.sessionMap[session] = &rpcSession{session: session} -// h.rwlock.Unlock() -// return nil -//} -// -//// OnError ... -//func (h *RpcServerHandler) OnError(session getty.Session, err error) { -// logger.Infof("session{%s} got error{%v}, will be closed.", session.Stat(), err) -// h.rwlock.Lock() -// delete(h.sessionMap, session) -// h.rwlock.Unlock() -//} -// -//// OnClose ... -//func (h *RpcServerHandler) OnClose(session getty.Session) { -// logger.Infof("session{%s} is closing......", session.Stat()) -// h.rwlock.Lock() -// delete(h.sessionMap, session) -// h.rwlock.Unlock() -//} -// -//// OnMessage ... -//func (h *RpcServerHandler) OnMessage(session getty.Session, pkg interface{}) { -// h.rwlock.Lock() -// if _, ok := h.sessionMap[session]; ok { -// h.sessionMap[session].reqNum++ -// } -// h.rwlock.Unlock() -// -// p, ok := pkg.(*DubboPackage) -// if !ok { -// logger.Errorf("illegal package{%#v}", pkg) -// return -// } -// p.Header.ResponseStatus = hessian.Response_OK -// -// // heartbeat -// if p.Header.Type&hessian.PackageHeartbeat != 0x00 { -// logger.Debugf("get rpc heartbeat request{header: %#v, service: %#v, body: %#v}", p.Header, p.Service, p.Body) -// reply(session, p, hessian.PackageHeartbeat) -// return -// } -// -// twoway := true -// // not twoway -// if p.Header.Type&hessian.PackageRequest_TwoWay == 0x00 { -// twoway = false -// } -// -// defer func() { -// if e := recover(); e != nil { -// p.Header.ResponseStatus = hessian.Response_SERVER_ERROR -// if err, ok := e.(error); ok { -// logger.Errorf("OnMessage panic: %+v", perrors.WithStack(err)) -// p.Body = perrors.WithStack(err) -// } else if err, ok := e.(string); ok { -// logger.Errorf("OnMessage panic: %+v", perrors.New(err)) -// p.Body = perrors.New(err) -// } else { -// logger.Errorf("OnMessage panic: %+v, this is impossible.", e) -// p.Body = e -// } -// -// if !twoway { -// return -// } -// reply(session, p, hessian.PackageResponse) -// } -// -// }() -// -// u := common.NewURLWithOptions(common.WithPath(p.Service.Path), common.WithParams(url.Values{}), -// common.WithParamsValue(constant.GROUP_KEY, p.Service.Group), -// common.WithParamsValue(constant.INTERFACE_KEY, p.Service.Interface), -// common.WithParamsValue(constant.VERSION_KEY, p.Service.Version)) -// exporter, _ := dubboProtocol.ExporterMap().Load(u.ServiceKey()) -// if exporter == nil { -// err := fmt.Errorf("don't have this exporter, key: %s", u.ServiceKey()) -// logger.Errorf(err.Error()) -// p.Header.ResponseStatus = hessian.Response_OK -// p.Body = err -// reply(session, p, hessian.PackageResponse) -// return -// } -// invoker := exporter.(protocol.Exporter).GetInvoker() -// if invoker != nil { -// attachments := p.Body.(map[string]interface{})["attachments"].(map[string]string) -// attachments[constant.LOCAL_ADDR] = session.LocalAddr() -// attachments[constant.REMOTE_ADDR] = session.RemoteAddr() -// -// args := p.Body.(map[string]interface{})["args"].([]interface{}) -// inv := invocation.NewRPCInvocation(p.Service.Method, args, attachments) -// -// ctx := rebuildCtx(inv) -// -// result := invoker.Invoke(ctx, inv) -// if err := result.Error(); err != nil { -// p.Header.ResponseStatus = hessian.Response_OK -// p.Body = hessian.NewResponse(nil, err, result.Attachments()) -// } else { -// res := result.Result() -// p.Header.ResponseStatus = hessian.Response_OK -// p.Body = hessian.NewResponse(res, nil, result.Attachments()) -// } -// } -// -// if !twoway { -// return -// } -// reply(session, p, hessian.PackageResponse) -//} -// -//// OnCron ... -//func (h *RpcServerHandler) OnCron(session getty.Session) { -// var ( -// flag bool -// active time.Time -// ) -// -// h.rwlock.RLock() -// if _, ok := h.sessionMap[session]; ok { -// active = session.GetActive() -// if h.sessionTimeout.Nanoseconds() < time.Since(active).Nanoseconds() { -// flag = true -// logger.Warnf("session{%s} timeout{%s}, reqNum{%d}", -// session.Stat(), time.Since(active).String(), h.sessionMap[session].reqNum) -// } -// } -// h.rwlock.RUnlock() -// -// if flag { -// h.rwlock.Lock() -// delete(h.sessionMap, session) -// h.rwlock.Unlock() -// session.Close() -// } -//} -// -//// rebuildCtx rebuild the context by attachment. -//// Once we decided to transfer more context's key-value, we should change this. -//// now we only support rebuild the tracing context -//func rebuildCtx(inv *invocation.RPCInvocation) context.Context { -// ctx := context.Background() -// -// // actually, if user do not use any opentracing framework, the err will not be nil. -// spanCtx, err := opentracing.GlobalTracer().Extract(opentracing.TextMap, -// opentracing.TextMapCarrier(inv.Attachments())) -// if err == nil { -// ctx = context.WithValue(ctx, constant.TRACING_REMOTE_SPAN_CTX, spanCtx) -// } -// return ctx -//} -// -//func reply(session getty.Session, req *DubboPackage, tp hessian.PackageType) { -// resp := &DubboPackage{ -// Header: hessian.DubboHeader{ -// SerialID: req.Header.SerialID, -// Type: tp, -// ID: req.Header.ID, -// ResponseStatus: req.Header.ResponseStatus, -// }, -// } -// -// if req.Header.Type&hessian.PackageRequest != 0x00 { -// resp.Body = req.Body -// } else { -// resp.Body = nil -// } -// -// if err := session.WritePkg(resp, WritePkg_Timeout); err != nil { -// logger.Errorf("WritePkg error: %#v, %#v", perrors.WithStack(err), req.Header) -// } -//} diff --git a/protocol/dubbo/listener_test.go b/protocol/dubbo/listener_test.go deleted file mode 100644 index 1057dbfa36..0000000000 --- a/protocol/dubbo/listener_test.go +++ /dev/null @@ -1,58 +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 ( -// "testing" -//) -// -//import ( -// "github.com/opentracing/opentracing-go" -// "github.com/opentracing/opentracing-go/mocktracer" -// "github.com/stretchr/testify/assert" -//) -// -//import ( -// "github.com/apache/dubbo-go/common/constant" -// "github.com/apache/dubbo-go/protocol/invocation" -//) -// -//// test rebuild the ctx -//func TestRebuildCtx(t *testing.T) { -// opentracing.SetGlobalTracer(mocktracer.New()) -// attach := make(map[string]string, 10) -// attach[constant.VERSION_KEY] = "1.0" -// attach[constant.GROUP_KEY] = "MyGroup" -// inv := invocation.NewRPCInvocation("MethodName", []interface{}{"OK", "Hello"}, attach) -// -// // attachment doesn't contains any tracing key-value pair, -// ctx := rebuildCtx(inv) -// assert.NotNil(t, ctx) -// assert.Nil(t, ctx.Value(constant.TRACING_REMOTE_SPAN_CTX)) -// -// span, ctx := opentracing.StartSpanFromContext(ctx, "Test-Client") -// -// opentracing.GlobalTracer().Inject(span.Context(), opentracing.TextMap, -// opentracing.TextMapCarrier(inv.Attachments())) -// // rebuild the context success -// inv = invocation.NewRPCInvocation("MethodName", []interface{}{"OK", "Hello"}, attach) -// ctx = rebuildCtx(inv) -// span.Finish() -// assert.NotNil(t, ctx) -// assert.NotNil(t, ctx.Value(constant.TRACING_REMOTE_SPAN_CTX)) -//} diff --git a/protocol/dubbo/pool.go b/protocol/dubbo/pool.go deleted file mode 100644 index 3dda895d55..0000000000 --- a/protocol/dubbo/pool.go +++ /dev/null @@ -1,404 +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 ( -// "fmt" -// "math/rand" -// "net" -// "sync" -// "sync/atomic" -// "time" -//) -// -//import ( -// "github.com/dubbogo/getty" -// perrors "github.com/pkg/errors" -//) -// -//import ( -// "github.com/apache/dubbo-go/common/logger" -//) -// -//type gettyRPCClient struct { -// once sync.Once -// protocol string -// addr string -// active int64 // zero, not create or be destroyed -// -// pool *gettyRPCClientPool -// -// lock sync.RWMutex -// gettyClient getty.Client -// sessions []*rpcSession -//} -// -//var ( -// errClientPoolClosed = perrors.New("client pool closed") -//) -// -//func newGettyRPCClientConn(pool *gettyRPCClientPool, protocol, addr string) (*gettyRPCClient, error) { -// c := &gettyRPCClient{ -// protocol: protocol, -// addr: addr, -// pool: pool, -// gettyClient: getty.NewTCPClient( -// getty.WithServerAddress(addr), -// 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) -// for { -// idx++ -// if c.isAvailable() { -// break -// } -// -// if idx > times { -// c.gettyClient.Close() -// return nil, perrors.New(fmt.Sprintf("failed to create client connection to %s in %f seconds", addr, float32(times)/1000)) -// } -// time.Sleep(1e6) -// } -// logger.Debug("client init ok") -// c.updateActive(time.Now().Unix()) -// -// return c, nil -//} -// -//func (c *gettyRPCClient) updateActive(active int64) { -// atomic.StoreInt64(&c.active, active) -//} -// -//func (c *gettyRPCClient) getActive() int64 { -// return atomic.LoadInt64(&c.active) -//} -// -//func (c *gettyRPCClient) newSession(session getty.Session) error { -// var ( -// ok bool -// tcpConn *net.TCPConn -// conf ClientConfig -// ) -// -// conf = c.pool.rpcClient.conf -// if conf.GettySessionParam.CompressEncoding { -// session.SetCompressType(getty.CompressZip) -// } -// -// if tcpConn, ok = session.Conn().(*net.TCPConn); !ok { -// panic(fmt.Sprintf("%s, session.conn{%#v} is not tcp connection\n", session.Stat(), session.Conn())) -// } -// -// tcpConn.SetNoDelay(conf.GettySessionParam.TcpNoDelay) -// tcpConn.SetKeepAlive(conf.GettySessionParam.TcpKeepAlive) -// if conf.GettySessionParam.TcpKeepAlive { -// tcpConn.SetKeepAlivePeriod(conf.GettySessionParam.keepAlivePeriod) -// } -// tcpConn.SetReadBuffer(conf.GettySessionParam.TcpRBufSize) -// tcpConn.SetWriteBuffer(conf.GettySessionParam.TcpWBufSize) -// -// session.SetName(conf.GettySessionParam.SessionName) -// session.SetMaxMsgLen(conf.GettySessionParam.MaxMsgLen) -// session.SetPkgHandler(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) -// logger.Debugf("client new session:%s\n", session.Stat()) -// -// session.SetTaskPool(clientGrpool) -// -// return nil -//} -// -//func (c *gettyRPCClient) selectSession() getty.Session { -// c.lock.RLock() -// defer c.lock.RUnlock() -// -// if c.sessions == nil { -// return nil -// } -// -// count := len(c.sessions) -// if count == 0 { -// return nil -// } -// return c.sessions[rand.Int31n(int32(count))].session -//} -// -//func (c *gettyRPCClient) addSession(session getty.Session) { -// logger.Debugf("add session{%s}", session.Stat()) -// if session == nil { -// return -// } -// -// c.lock.Lock() -// defer c.lock.Unlock() -// if c.sessions == nil { -// c.sessions = make([]*rpcSession, 0, 16) -// } -// c.sessions = append(c.sessions, &rpcSession{session: session}) -//} -// -//func (c *gettyRPCClient) removeSession(session getty.Session) { -// if session == nil { -// return -// } -// -// var removeFlag bool -// func() { -// c.lock.Lock() -// defer c.lock.Unlock() -// if c.sessions == nil { -// return -// } -// -// for i, s := range c.sessions { -// if s.session == session { -// c.sessions = append(c.sessions[:i], c.sessions[i+1:]...) -// logger.Debugf("delete session{%s}, its index{%d}", session.Stat(), i) -// break -// } -// } -// logger.Infof("after remove session{%s}, left session number:%d", session.Stat(), len(c.sessions)) -// if len(c.sessions) == 0 { -// removeFlag = true -// } -// }() -// if removeFlag { -// c.pool.safeRemove(c) -// c.close() -// } -//} -// -//func (c *gettyRPCClient) updateSession(session getty.Session) { -// if session == nil { -// return -// } -// -// var rs *rpcSession -// func() { -// c.lock.RLock() -// defer c.lock.RUnlock() -// if c.sessions == nil { -// return -// } -// -// for i, s := range c.sessions { -// if s.session == session { -// rs = c.sessions[i] -// break -// } -// } -// }() -// if rs != nil { -// rs.AddReqNum(1) -// } -//} -// -//func (c *gettyRPCClient) getClientRpcSession(session getty.Session) (rpcSession, error) { -// var ( -// err error -// rpcSession rpcSession -// ) -// c.lock.RLock() -// defer c.lock.RUnlock() -// if c.sessions == nil { -// return rpcSession, errClientClosed -// } -// -// err = errSessionNotExist -// for _, s := range c.sessions { -// if s.session == session { -// rpcSession = *s -// err = nil -// break -// } -// } -// -// return rpcSession, perrors.WithStack(err) -//} -// -//func (c *gettyRPCClient) isAvailable() bool { -// if c.selectSession() == nil { -// return false -// } -// -// return true -//} -// -//func (c *gettyRPCClient) close() error { -// closeErr := perrors.Errorf("close gettyRPCClient{%#v} again", c) -// c.once.Do(func() { -// var ( -// gettyClient getty.Client -// sessions []*rpcSession -// ) -// func() { -// c.lock.Lock() -// defer c.lock.Unlock() -// -// gettyClient = c.gettyClient -// c.gettyClient = nil -// -// sessions = make([]*rpcSession, 0, len(c.sessions)) -// for _, s := range c.sessions { -// sessions = append(sessions, s) -// } -// c.sessions = c.sessions[:0] -// }() -// -// c.updateActive(0) -// -// go func() { -// if gettyClient != nil { -// gettyClient.Close() -// } -// for _, s := range sessions { -// logger.Infof("close client session{%s, last active:%s, request number:%d}", -// s.session.Stat(), s.session.GetActive().String(), s.GetReqNum()) -// s.session.Close() -// } -// }() -// -// closeErr = nil -// }) -// return closeErr -//} -// -//type gettyRPCClientPool struct { -// rpcClient *Client -// size int // size of []*gettyRPCClient -// ttl int64 // ttl of every gettyRPCClient, it is checked when getConn -// -// sync.Mutex -// conns []*gettyRPCClient -//} -// -//func newGettyRPCClientConnPool(rpcClient *Client, size int, ttl time.Duration) *gettyRPCClientPool { -// return &gettyRPCClientPool{ -// rpcClient: rpcClient, -// size: size, -// ttl: int64(ttl.Seconds()), -// conns: make([]*gettyRPCClient, 0, 16), -// } -//} -// -//func (p *gettyRPCClientPool) close() { -// p.Lock() -// conns := p.conns -// p.conns = nil -// p.Unlock() -// for _, conn := range conns { -// conn.close() -// } -//} -// -//func (p *gettyRPCClientPool) getGettyRpcClient(protocol, addr string) (*gettyRPCClient, error) { -// conn, err := p.get() -// if err == nil && conn == nil { -// // create new conn -// rpcClientConn, err := newGettyRPCClientConn(p, protocol, addr) -// return rpcClientConn, perrors.WithStack(err) -// } -// return conn, perrors.WithStack(err) -//} -// -//func (p *gettyRPCClientPool) get() (*gettyRPCClient, error) { -// now := time.Now().Unix() -// -// p.Lock() -// defer p.Unlock() -// if p.conns == nil { -// return nil, errClientPoolClosed -// } -// -// for len(p.conns) > 0 { -// conn := p.conns[len(p.conns)-1] -// p.conns = p.conns[:len(p.conns)-1] -// -// if d := now - conn.getActive(); d > p.ttl { -// p.remove(conn) -// go conn.close() -// continue -// } -// conn.updateActive(now) //update active time -// return conn, nil -// } -// return nil, nil -//} -// -//func (p *gettyRPCClientPool) put(conn *gettyRPCClient) { -// if conn == nil || conn.getActive() == 0 { -// return -// } -// -// p.Lock() -// defer p.Unlock() -// -// if p.conns == nil { -// return -// } -// -// // check whether @conn has existed in p.conns or not. -// for i := range p.conns { -// if p.conns[i] == conn { -// return -// } -// } -// -// if len(p.conns) >= p.size { -// // delete @conn from client pool -// // p.remove(conn) -// conn.close() -// return -// } -// p.conns = append(p.conns, conn) -//} -// -//func (p *gettyRPCClientPool) remove(conn *gettyRPCClient) { -// if conn == nil || conn.getActive() == 0 { -// return -// } -// -// if p.conns == nil { -// return -// } -// -// if len(p.conns) > 0 { -// for idx, c := range p.conns { -// if conn == c { -// p.conns = append(p.conns[:idx], p.conns[idx+1:]...) -// break -// } -// } -// } -//} -// -//func (p *gettyRPCClientPool) safeRemove(conn *gettyRPCClient) { -// p.Lock() -// defer p.Unlock() -// -// p.remove(conn) -//} diff --git a/protocol/dubbo/readwriter.go b/protocol/dubbo/readwriter.go deleted file mode 100644 index 00b79e47f3..0000000000 --- a/protocol/dubbo/readwriter.go +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dubbo -// -//import ( -// "bytes" -// "reflect" -//) -// -//import ( -// "github.com/apache/dubbo-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" -//) -// -////////////////////////////////////////////// -//// RpcClientPackageHandler -////////////////////////////////////////////// -// -//// RpcClientPackageHandler ... -//type RpcClientPackageHandler struct { -// client *Client -//} -// -//// NewRpcClientPackageHandler ... -//func NewRpcClientPackageHandler(client *Client) *RpcClientPackageHandler { -// return &RpcClientPackageHandler{client: client} -//} -// -//func (p *RpcClientPackageHandler) Read(ss getty.Session, data []byte) (interface{}, int, error) { -// pkg := &DubboPackage{} -// -// buf := bytes.NewBuffer(data) -// err := pkg.Unmarshal(buf, p.client) -// if err != nil { -// originErr := perrors.Cause(err) -// if originErr == hessian.ErrHeaderNotEnough || originErr == hessian.ErrBodyNotEnough { -// return nil, 0, nil -// } -// -// logger.Errorf("pkg.Unmarshal(ss:%+v, len(@data):%d) = error:%+v", ss, len(data), err) -// -// return nil, 0, perrors.WithStack(err) -// } -// -// if pkg.Header.Type&hessian.PackageRequest == 0x00 { -// pkg.Err = pkg.Body.(*hessian.Response).Exception -// pkg.Body = NewResponse(pkg.Body.(*hessian.Response).RspObj, pkg.Body.(*hessian.Response).Attachments) -// } -// -// return pkg, hessian.HEADER_LENGTH + pkg.Header.BodyLen, nil -//} -// -//func (p *RpcClientPackageHandler) Write(ss getty.Session, pkg interface{}) ([]byte, error) { -// req, ok := pkg.(*DubboPackage) -// if !ok { -// logger.Errorf("illegal pkg:%+v\n", pkg) -// return nil, perrors.New("invalid rpc request") -// } -// -// buf, err := req.Marshal() -// if err != nil { -// logger.Warnf("binary.Write(req{%#v}) = err{%#v}", req, perrors.WithStack(err)) -// return nil, perrors.WithStack(err) -// } -// -// return buf.Bytes(), nil -//} -// -////////////////////////////////////////////// -//// RpcServerPackageHandler -////////////////////////////////////////////// -// -//var ( -// rpcServerPkgHandler = &RpcServerPackageHandler{} -//) -// -//// RpcServerPackageHandler ... -//type RpcServerPackageHandler struct{} -// -//func (p *RpcServerPackageHandler) Read(ss getty.Session, data []byte) (interface{}, int, error) { -// pkg := &DubboPackage{ -// Body: make([]interface{}, 7), -// } -// -// buf := bytes.NewBuffer(data) -// err := pkg.Unmarshal(buf) -// if err != nil { -// originErr := perrors.Cause(err) -// if originErr == hessian.ErrHeaderNotEnough || originErr == hessian.ErrBodyNotEnough { -// return nil, 0, nil -// } -// -// logger.Errorf("pkg.Unmarshal(ss:%+v, len(@data):%d) = error:%+v", ss, len(data), err) -// -// return nil, 0, perrors.WithStack(err) -// } -// -// if pkg.Header.Type&hessian.PackageHeartbeat == 0x00 { -// // convert params of request -// req := pkg.Body.([]interface{}) // length of body should be 7 -// if len(req) > 0 { -// var dubboVersion, argsTypes string -// var args []interface{} -// var attachments map[string]string -// if req[0] != nil { -// dubboVersion = req[0].(string) -// } -// if req[1] != nil { -// pkg.Service.Path = req[1].(string) -// } -// if req[2] != nil { -// pkg.Service.Version = req[2].(string) -// } -// if req[3] != nil { -// pkg.Service.Method = req[3].(string) -// } -// if req[4] != nil { -// argsTypes = req[4].(string) -// } -// if req[5] != nil { -// args = req[5].([]interface{}) -// } -// if req[6] != nil { -// attachments = req[6].(map[string]string) -// } -// if pkg.Service.Path == "" && len(attachments[constant.PATH_KEY]) > 0 { -// pkg.Service.Path = attachments[constant.PATH_KEY] -// } -// if _, ok := attachments[constant.INTERFACE_KEY]; ok { -// pkg.Service.Interface = attachments[constant.INTERFACE_KEY] -// } else { -// pkg.Service.Interface = pkg.Service.Path -// } -// if len(attachments[constant.GROUP_KEY]) > 0 { -// pkg.Service.Group = attachments[constant.GROUP_KEY] -// } -// pkg.Body = map[string]interface{}{ -// "dubboVersion": dubboVersion, -// "argsTypes": argsTypes, -// "args": args, -// "service": common.ServiceMap.GetService(DUBBO, pkg.Service.Path), // path as a key -// "attachments": attachments, -// } -// } -// } -// -// return pkg, hessian.HEADER_LENGTH + pkg.Header.BodyLen, nil -//} -// -//func (p *RpcServerPackageHandler) Write(ss getty.Session, pkg interface{}) ([]byte, error) { -// res, ok := pkg.(*DubboPackage) -// if !ok { -// logger.Errorf("illegal pkg:%+v\n, it is %+v", pkg, reflect.TypeOf(pkg)) -// return nil, perrors.New("invalid rpc response") -// } -// -// buf, err := res.Marshal() -// if err != nil { -// logger.Warnf("binary.Write(res{%#v}) = err{%#v}", res, perrors.WithStack(err)) -// return nil, perrors.WithStack(err) -// } -// -// return buf.Bytes(), nil -//} diff --git a/protocol/dubbo/server.go b/protocol/dubbo/server.go deleted file mode 100644 index 35a162cc37..0000000000 --- a/protocol/dubbo/server.go +++ /dev/null @@ -1,179 +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 ( -// "fmt" -// "net" -//) -// -//import ( -// "github.com/dubbogo/getty" -// "github.com/dubbogo/gost/sync" -// "gopkg.in/yaml.v2" -//) -// -//import ( -// "github.com/apache/dubbo-go/common" -// "github.com/apache/dubbo-go/common/logger" -// "github.com/apache/dubbo-go/config" -//) -// -//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 ... -//func SetServerConfig(s ServerConfig) { -// srvConf = &s -// err := srvConf.CheckValidity() -// if err != nil { -// logger.Warnf("[ServerConfig CheckValidity] error: %v", err) -// return -// } -// SetServerGrpool() -//} -// -//// GetServerConfig ... -//func GetServerConfig() ServerConfig { -// return *srvConf -//} -// -//// SetServerGrpool ... -//func SetServerGrpool() { -// if srvConf.GrPoolSize > 1 { -// srvGrpool = gxsync.NewTaskPool(gxsync.WithTaskPoolTaskPoolSize(srvConf.GrPoolSize), gxsync.WithTaskPoolTaskQueueLength(srvConf.QueueLen), -// gxsync.WithTaskPoolTaskQueueNumber(srvConf.QueueNumber)) -// } -//} -// -//// Server ... -//type Server struct { -// conf ServerConfig -// tcpServer getty.Server -// rpcHandler *RpcServerHandler -//} -// -//// NewServer ... -//func NewServer() *Server { -// -// s := &Server{ -// conf: *srvConf, -// } -// -// s.rpcHandler = NewRpcServerHandler(s.conf.SessionNumber, s.conf.sessionTimeout) -// -// return s -//} -// -//func (s *Server) newSession(session getty.Session) error { -// var ( -// ok bool -// tcpConn *net.TCPConn -// ) -// conf := s.conf -// -// if conf.GettySessionParam.CompressEncoding { -// session.SetCompressType(getty.CompressZip) -// } -// -// if tcpConn, ok = session.Conn().(*net.TCPConn); !ok { -// panic(fmt.Sprintf("%s, session.conn{%#v} is not tcp connection\n", session.Stat(), session.Conn())) -// } -// -// tcpConn.SetNoDelay(conf.GettySessionParam.TcpNoDelay) -// tcpConn.SetKeepAlive(conf.GettySessionParam.TcpKeepAlive) -// if conf.GettySessionParam.TcpKeepAlive { -// tcpConn.SetKeepAlivePeriod(conf.GettySessionParam.keepAlivePeriod) -// } -// tcpConn.SetReadBuffer(conf.GettySessionParam.TcpRBufSize) -// tcpConn.SetWriteBuffer(conf.GettySessionParam.TcpWBufSize) -// -// session.SetName(conf.GettySessionParam.SessionName) -// session.SetMaxMsgLen(conf.GettySessionParam.MaxMsgLen) -// session.SetPkgHandler(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 -//} -// -//// Start ... -//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 ... -//func (s *Server) Stop() { -// s.tcpServer.Close() -//} diff --git a/remoting/codec.go b/remoting/codec.go index 972a00d12b..1c1e025762 100644 --- a/remoting/codec.go +++ b/remoting/codec.go @@ -7,8 +7,12 @@ import ( type Codec interface { EncodeRequest(request *Request) (*bytes.Buffer, error) EncodeResponse(response *Response) (*bytes.Buffer, error) - DecodeRequest(data []byte) (*Request, int, error) - DecodeResponse(data []byte) (*Response, int, error) + Decode(data []byte) (DecodeResult, int, error) +} + +type DecodeResult struct { + IsRequest bool + Result interface{} } var ( diff --git a/remoting/exchange.go b/remoting/exchange.go index 498022926d..eaa19f7195 100644 --- a/remoting/exchange.go +++ b/remoting/exchange.go @@ -1,9 +1,10 @@ package remoting import ( + "time" + "github.com/apache/dubbo-go/common" "go.uber.org/atomic" - "time" ) var ( @@ -88,7 +89,7 @@ type PendingResponse struct { Err error start time.Time ReadStart time.Time - callback common.AsyncCallback + Callback common.AsyncCallback response *Response Reply interface{} Done chan struct{} @@ -104,6 +105,10 @@ func NewPendingResponse(id int64) *PendingResponse { } } +func (r *PendingResponse) SetResponse(response *Response) { + r.response = response +} + // GetCallResponse ... func (r PendingResponse) GetCallResponse() common.CallbackResponse { return AsyncCallbackResponse{ diff --git a/remoting/exchange_client.go b/remoting/exchange_client.go index aff8bba22d..2a0e6cfd79 100644 --- a/remoting/exchange_client.go +++ b/remoting/exchange_client.go @@ -1,11 +1,12 @@ package remoting import ( + "sync" + "time" + "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common/logger" "github.com/apache/dubbo-go/protocol" - "sync" - "time" ) var ( @@ -24,9 +25,10 @@ type Client interface { SetExchangeClient(client *ExchangeClient) SetResponseHandler(responseHandler ResponseHandler) //invoke once for connection + //ConfigClient() Connect(url common.URL) Close() - Request(request *Request, timeout time.Duration, callback common.AsyncCallback, response *PendingResponse) error + Request(request *Request, timeout time.Duration, response *PendingResponse) error } type ResponseHandler interface { @@ -58,7 +60,7 @@ func (client *ExchangeClient) Request(invocation *protocol.Invocation, url commo AddPendingResponse(rsp) //rsp.callback = invo - err := client.client.Request(request, timeout, nil, rsp) + err := client.client.Request(request, timeout, rsp) if err != nil { result.Err = err return err @@ -77,11 +79,11 @@ func (client *ExchangeClient) AsyncRequest(invocation *protocol.Invocation, url rsp := NewPendingResponse(request.Id) rsp.response = NewResponse(request.Id, "2.0.2") - rsp.callback = callback + rsp.Callback = callback rsp.Reply = (*invocation).Reply() AddPendingResponse(rsp) - err := client.client.Request(request, timeout, nil, rsp) + err := client.client.Request(request, timeout, rsp) if err != nil { result.Err = err return err @@ -93,16 +95,15 @@ func (client *ExchangeClient) AsyncRequest(invocation *protocol.Invocation, url // oneway func (client *ExchangeClient) Send(invocation *protocol.Invocation, timeout time.Duration) error { - requestId := int64(SequenceId()) request := NewRequest("2.0.2") request.Data = invocation request.Event = false request.TwoWay = false rsp := NewPendingResponse(request.Id) - rsp.response = NewResponse(requestId, "2.0.2") + rsp.response = NewResponse(request.Id, "2.0.2") - err := client.client.Request(request, timeout, nil, rsp) + err := client.client.Request(request, timeout, rsp) if err != nil { return err } @@ -124,10 +125,11 @@ func (client *ExchangeClient) Handler(response *Response) { pendingResponse.response = response - if pendingResponse.callback == nil { + if pendingResponse.Callback == nil { + pendingResponse.Err = pendingResponse.response.Error pendingResponse.Done <- struct{}{} } else { - pendingResponse.callback(pendingResponse.GetCallResponse()) + pendingResponse.Callback(pendingResponse.GetCallResponse()) } } diff --git a/remoting/getty/client.go b/remoting/getty/client.go deleted file mode 100644 index b76033c6e9..0000000000 --- a/remoting/getty/client.go +++ /dev/null @@ -1,364 +0,0 @@ -/* -* Licensed to the Apache Software Foundation (ASF) under one or more -* contributor license agreements. See the NOTICE file distributed with -* this work for additional information regarding copyright ownership. -* The ASF licenses this file to You under the Apache License, Version 2.0 -* (the "License"); you may not use this file except in compliance with -* the License. You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -package getty -// -//import ( -// "github.com/apache/dubbo-go/remoting" -// "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 -//) -// -//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 ... -//func SetClientConf(c ClientConfig) { -// clientConf = &c -// err := clientConf.CheckValidity() -// if err != nil { -// logger.Warnf("[ClientConfig CheckValidity] error: %v", err) -// return -// } -// setClientGrpool() -//} -// -//// GetClientConf ... -//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 ... -//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 ... -//type Client struct { -// opts Options -// conf ClientConfig -// pool *gettyRPCClientPool -// sequence atomic.Uint64 -// -// pendingResponses *sync.Map -//} -// -//// NewClient ... -//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 ... -////type Request struct { -//// addr string -//// svcUrl common.URL -//// method string -//// args interface{} -//// atta map[string]string -////} -//// -////// NewRequest ... -////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 ... -////type Response struct { -//// reply interface{} -//// atta map[string]string -////} -//// -////// NewResponse ... -////func NewResponse(reply interface{}, atta map[string]string) *Response { -//// return &Response{ -//// reply: reply, -//// atta: atta, -//// } -////} -// -//// CallOneway call one way -////func (c *Client) CallOneway(request *Request) error { -//// -//// return perrors.WithStack(c.call(CT_OneWay, request, NewResponse(nil, nil), nil)) -////} -//// -////// 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 -//// } -//// -//// return perrors.WithStack(c.call(ct, request, response, nil)) -////} -//// -////// 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 -// } -// -// 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 ... -//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 { -// req := remoting.NewRequest("2.0.2") -// req.TwoWay = true -// req.Event = true -// resp := remoting.NewPendingResponse() -// return c.transfer(session, req, 3 * time.Second, resp) -//} -// -//func (c *Client) transfer(session getty.Session, request *remoting.Request, timeout time.Duration, -// rsp *remoting.PendingResponse) 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 { -// // c.addPendingResponse(rsp) -// //} -// -// err := session.WritePkg(request, timeout) -// 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/remoting/getty/client_test.go b/remoting/getty/client_test.go deleted file mode 100644 index d0a4c97e47..0000000000 --- a/remoting/getty/client_test.go +++ /dev/null @@ -1,300 +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 getty -// -//import ( -// "bytes" -// "context" -// "sync" -// "testing" -// "time" -//) -// -//import ( -// hessian "github.com/apache/dubbo-go-hessian2" -// perrors "github.com/pkg/errors" -// "github.com/stretchr/testify/assert" -//) -// -//import ( -// "github.com/apache/dubbo-go/common" -// "github.com/apache/dubbo-go/common/proxy/proxy_factory" -// "github.com/apache/dubbo-go/protocol" -//) -// -//func TestClient_CallOneway(t *testing.T) { -// proto, url := InitTest(t) -// -// c := &Client{ -// pendingResponses: new(sync.Map), -// conf: *clientConf, -// opts: Options{ -// ConnectTimeout: 3e9, -// RequestTimeout: 6e9, -// }, -// } -// c.pool = newGettyRPCClientConnPool(c, clientConf.PoolSize, time.Duration(int(time.Second)*clientConf.PoolTTL)) -// -// //user := &User{} -// err := c.CallOneway(NewRequest("127.0.0.1:20000", url, "GetUser", []interface{}{"1", "username"}, nil)) -// assert.NoError(t, err) -// -// // destroy -// proto.Destroy() -//} -// -//func TestClient_Call(t *testing.T) { -// proto, url := InitTest(t) -// -// c := &Client{ -// pendingResponses: new(sync.Map), -// conf: *clientConf, -// opts: Options{ -// ConnectTimeout: 3e9, -// RequestTimeout: 10e9, -// }, -// } -// c.pool = newGettyRPCClientConnPool(c, clientConf.PoolSize, time.Duration(int(time.Second)*clientConf.PoolTTL)) -// -// var ( -// user *User -// err error -// ) -// -// user = &User{} -// err = c.Call(NewRequest("127.0.0.1:20000", url, "GetBigPkg", []interface{}{nil}, nil), NewResponse(user, nil)) -// assert.NoError(t, err) -// assert.NotEqual(t, "", user.Id) -// assert.NotEqual(t, "", user.Name) -// -// user = &User{} -// err = c.Call(NewRequest("127.0.0.1:20000", url, "GetUser", []interface{}{"1", "username"}, nil), NewResponse(user, nil)) -// assert.NoError(t, err) -// assert.Equal(t, User{Id: "1", Name: "username"}, *user) -// -// user = &User{} -// err = c.Call(NewRequest("127.0.0.1:20000", url, "GetUser0", []interface{}{"1", nil, "username"}, nil), NewResponse(user, nil)) -// assert.NoError(t, err) -// assert.Equal(t, User{Id: "1", Name: "username"}, *user) -// -// err = c.Call(NewRequest("127.0.0.1:20000", url, "GetUser1", []interface{}{}, nil), NewResponse(user, nil)) -// assert.NoError(t, err) -// -// err = c.Call(NewRequest("127.0.0.1:20000", url, "GetUser2", []interface{}{}, nil), 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)) -// 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)) -// 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)) -// 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)) -// 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)) -// assert.NoError(t, err) -// assert.Equal(t, User{Id: "1", Name: ""}, *user) -// -// // destroy -// proto.Destroy() -//} -// -//func TestClient_AsyncCall(t *testing.T) { -// proto, url := InitTest(t) -// -// c := &Client{ -// pendingResponses: new(sync.Map), -// conf: *clientConf, -// opts: Options{ -// ConnectTimeout: 3e9, -// RequestTimeout: 6e9, -// }, -// } -// c.pool = newGettyRPCClientConnPool(c, clientConf.PoolSize, time.Duration(int(time.Second)*clientConf.PoolTTL)) -// -// user := &User{} -// lock := sync.Mutex{} -// lock.Lock() -// err := c.AsyncCall(NewRequest("127.0.0.1:20000", url, "GetUser", []interface{}{"1", "username"}, nil), func(response common.CallbackResponse) { -// r := response.(AsyncCallbackResponse) -// assert.Equal(t, User{Id: "1", Name: "username"}, *r.Reply.(*Response).reply.(*User)) -// lock.Unlock() -// }, NewResponse(user, nil)) -// assert.NoError(t, err) -// assert.Equal(t, User{}, *user) -// -// // destroy -// lock.Lock() -// proto.Destroy() -// lock.Unlock() -//} -// -//func InitTest(t *testing.T) (protocol.Protocol, common.URL) { -// -// hessian.RegisterPOJO(&User{}) -// -// methods, err := common.ServiceMap.Register("dubbo", &UserProvider{}) -// assert.NoError(t, err) -// assert.Equal(t, "GetBigPkg,GetUser,GetUser0,GetUser1,GetUser2,GetUser3,GetUser4,GetUser5,GetUser6", methods) -// -// // config -// SetClientConf(ClientConfig{ -// ConnectionNum: 2, -// HeartbeatPeriod: "5s", -// SessionTimeout: "20s", -// PoolTTL: 600, -// PoolSize: 64, -// GettySessionParam: GettySessionParam{ -// CompressEncoding: false, -// TcpNoDelay: true, -// TcpKeepAlive: true, -// KeepAlivePeriod: "120s", -// TcpRBufSize: 262144, -// TcpWBufSize: 65536, -// PkgWQSize: 512, -// TcpReadTimeout: "4s", -// TcpWriteTimeout: "5s", -// WaitTimeout: "1s", -// MaxMsgLen: 10240000000, -// SessionName: "client", -// }, -// }) -// assert.NoError(t, clientConf.CheckValidity()) -// SetServerConfig(ServerConfig{ -// SessionNumber: 700, -// SessionTimeout: "20s", -// GettySessionParam: GettySessionParam{ -// CompressEncoding: false, -// TcpNoDelay: true, -// TcpKeepAlive: true, -// KeepAlivePeriod: "120s", -// TcpRBufSize: 262144, -// TcpWBufSize: 65536, -// PkgWQSize: 512, -// TcpReadTimeout: "1s", -// TcpWriteTimeout: "5s", -// WaitTimeout: "1s", -// MaxMsgLen: 10240000000, -// SessionName: "server", -// }}) -// assert.NoError(t, srvConf.CheckValidity()) -// -// // Export -// proto := GetProtocol() -// url, err := common.NewURL("dubbo://127.0.0.1:20000/UserProvider?anyhost=true&" + -// "application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&" + -// "environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&" + -// "module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&" + -// "side=provider&timeout=3000×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 < 4000; i++ { -// argBuf.WriteString("击鼓其镗,踊跃用兵。土国城漕,我独南行。从孙子仲,平陈与宋。不我以归,忧心有忡。爰居爰处?爰丧其马?于以求之?于林之下。死生契阔,与子成说。执子之手,与子偕老。于嗟阔兮,不我活兮。于嗟洵兮,不我信兮。") -// argBuf.WriteString("击鼓其镗,踊跃用兵。土国城漕,我独南行。从孙子仲,平陈与宋。不我以归,忧心有忡。爰居爰处?爰丧其马?于以求之?于林之下。死生契阔,与子成说。执子之手,与子偕老。于嗟阔兮,不我活兮。于嗟洵兮,不我信兮。") -// } -// rsp.Id = argBuf.String() -// rsp.Name = argBuf.String() -// return nil -//} -// -//func (u *UserProvider) GetUser(ctx context.Context, req []interface{}, rsp *User) error { -// rsp.Id = req[0].(string) -// rsp.Name = req[1].(string) -// return nil -//} -// -//func (u *UserProvider) GetUser0(id string, k *User, name string) (User, error) { -// return User{Id: id, Name: name}, nil -//} -// -//func (u *UserProvider) GetUser1() error { -// return nil -//} -// -//func (u *UserProvider) GetUser2() error { -// return perrors.New("error") -//} -// -//func (u *UserProvider) GetUser3(rsp *[]interface{}) error { -// *rsp = append(*rsp, User{Id: "1", Name: "username"}) -// return nil -//} -// -//func (u *UserProvider) GetUser4(ctx context.Context, req []interface{}) ([]interface{}, error) { -// -// return []interface{}{User{Id: req[0].([]interface{})[0].(string), Name: req[0].([]interface{})[1].(string)}}, nil -//} -// -//func (u *UserProvider) GetUser5(ctx context.Context, req []interface{}) (map[interface{}]interface{}, error) { -// return map[interface{}]interface{}{"key": User{Id: req[0].(map[interface{}]interface{})["id"].(string), Name: req[0].(map[interface{}]interface{})["name"].(string)}}, nil -//} -// -//func (u *UserProvider) GetUser6(id int64) (*User, error) { -// if id == 0 { -// return nil, nil -// } -// return &User{Id: "1"}, nil -//} -// -//func (u *UserProvider) Reference() string { -// return "UserProvider" -//} -// -//func (u User) JavaClassName() string { -// return "com.ikurento.user.User" -//} diff --git a/remoting/getty/dubbo_codec_for_test.go b/remoting/getty/dubbo_codec_for_test.go new file mode 100644 index 0000000000..46ef7b939e --- /dev/null +++ b/remoting/getty/dubbo_codec_for_test.go @@ -0,0 +1,359 @@ +package getty + +import ( + "bufio" + "bytes" + "fmt" + "strconv" + "time" + + hessian "github.com/apache/dubbo-go-hessian2" + "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" + perrors "github.com/pkg/errors" +) + +//SerialID serial ID +type SerialID byte + +const ( + // S_Dubbo dubbo serial id + S_Dubbo SerialID = 2 +) + +func init() { + codec := &DubboTestCodec{} + remoting.NewCodec("dubbo", codec) +} + +// DubboPackage ... +type DubboPackage struct { + Header hessian.DubboHeader + Service hessian.Service + Body interface{} + Err error +} + +func (p DubboPackage) String() string { + return fmt.Sprintf("DubboPackage: Header-%v, Path-%v, Body-%v", p.Header, p.Service, p.Body) +} + +// Marshal ... +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 ... +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) +} + +type DubboTestCodec struct { +} + +func (c *DubboTestCodec) EncodeRequest(request *remoting.Request) (*bytes.Buffer, error) { + if request.Event { + return c.encodeHeartbeartReqeust(request) + } + + invoc, ok := request.Data.(*invocation.RPCInvocation) + if !ok { + 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() + + timeout, err := strconv.Atoi(invocation.AttachmentsByKey(constant.TIMEOUT_KEY, "3000")) + if err != nil { + panic(err) + } + p.Service.Timeout = time.Duration(timeout) + //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.Header.ID = request.Id + if request.TwoWay { + p.Header.Type = hessian.PackageRequest_TwoWay + } else { + p.Header.Type = hessian.PackageRequest + } + + p.Body = hessian.NewRequest(invocation.Arguments(), invocation.Attachments()) + + 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 (c *DubboTestCodec) 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) + + byt, err := codec.Write(pkg.Service, pkg.Header, pkg.Body) + if err != nil { + return nil, perrors.WithStack(err) + } + + return bytes.NewBuffer(byt), nil +} +func (c *DubboTestCodec) EncodeResponse(response *remoting.Response) (*bytes.Buffer, error) { + var ptype = hessian.PackageResponse + if response.IsHeartbeat() { + ptype = hessian.PackageHeartbeat + } + resp := &DubboPackage{ + Header: hessian.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, + } + } + + //if response.Header.Type&hessian.PackageRequest != 0x00 { + // resp.Body = req.Body + //} else { + // resp.Body = nil + //} + codec := hessian.NewHessianCodec(nil) + + pkg, err := codec.Write(resp.Service, resp.Header, resp.Body) + if err != nil { + return nil, perrors.WithStack(err) + } + + return bytes.NewBuffer(pkg), nil +} +func (c *DubboTestCodec) Decode(data []byte) (remoting.DecodeResult, int, error) { + if c.isRequest(data) { + req, len, err := c.decodeRequest(data) + if err != nil { + return remoting.DecodeResult{}, len, err + } + return remoting.DecodeResult{IsRequest: true, Result: req}, len, err + } else { + resp, len, err := c.decodeResponse(data) + if err != nil { + return remoting.DecodeResult{}, len, err + } + return remoting.DecodeResult{IsRequest: false, Result: resp}, len, err + } +} +func (c *DubboTestCodec) isRequest(data []byte) bool { + if data[2]&byte(0x80) == 0x00 { + return false + } + return true +} + +func (c *DubboTestCodec) 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) + 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, + } + 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 { + //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 { + //argsType + //invocation.ParameterTypes(constant., req[1].(string)) + //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] + //} + invoc := invocation.NewRPCInvocationWithOptions(invocation.WithAttachments(attachments), + invocation.WithArguments(args), invocation.WithMethodName(methodName)) + request.Data = invoc + //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 request, hessian.HEADER_LENGTH + pkg.Header.BodyLen, nil +} + +func (c *DubboTestCodec) decodeResponse(data []byte) (*remoting.Response, int, error) { + pkg := &DubboPackage{} + buf := bytes.NewBuffer(data) + response := &remoting.Response{} + err := pkg.Unmarshal(buf, response) + if err != nil { + originErr := perrors.Cause(err) + if originErr == hessian.ErrHeaderNotEnough || originErr == hessian.ErrBodyNotEnough { + return nil, 0, originErr + } + logger.Errorf("pkg.Unmarshal(len(@data):%d) = error:%+v", buf.Len(), err) + + return response, 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, + } + var error error + if pkg.Header.Type&hessian.PackageHeartbeat != 0x00 { + if pkg.Header.Type&hessian.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.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 + } + + //h.conn.updateSession(session) + //pendingResponse := h.conn.pool.rpcClient.removePendingResponse(SequenceType(p.Header.ID)) + //if pendingResponse == nil { + // logger.Errorf("failed to get pending response context for response package %s", *p) + // return + //} + + return response, hessian.HEADER_LENGTH + pkg.Header.BodyLen, nil +} diff --git a/remoting/getty/getty_client.go b/remoting/getty/getty_client.go index c22f576872..8544b8e17f 100644 --- a/remoting/getty/getty_client.go +++ b/remoting/getty/getty_client.go @@ -18,22 +18,19 @@ package getty import ( - "github.com/apache/dubbo-go/remoting" - "gopkg.in/yaml.v2" "math/rand" "time" -) -import ( + "github.com/apache/dubbo-go/remoting" "github.com/dubbogo/getty" + "gopkg.in/yaml.v2" + gxsync "github.com/dubbogo/gost/sync" - perrors "github.com/pkg/errors" -) -import ( "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common/logger" "github.com/apache/dubbo-go/config" + perrors "github.com/pkg/errors" ) var ( @@ -98,11 +95,6 @@ func SetClientConf(c ClientConfig) { setClientGrpool() } -// GetClientConf ... -func GetClientConf() ClientConfig { - return *clientConf -} - func setClientGrpool() { if clientConf.GrPoolSize > 1 { clientGrpool = gxsync.NewTaskPool(gxsync.WithTaskPoolTaskPoolSize(clientConf.GrPoolSize), gxsync.WithTaskPoolTaskQueueLength(clientConf.QueueLen), @@ -114,19 +106,17 @@ func setClientGrpool() { 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{} -} +////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 ... type Client struct { @@ -137,8 +127,6 @@ type Client struct { codec remoting.Codec responseHandler remoting.ResponseHandler ExchangeClient *remoting.ExchangeClient - //sequence atomic.Uint64 - //pendingResponses *sync.Map } // NewClient ... @@ -160,6 +148,7 @@ func (c *Client) SetExchangeClient(client *remoting.ExchangeClient) { func (c *Client) SetResponseHandler(responseHandler remoting.ResponseHandler) { c.responseHandler = responseHandler } + func (c *Client) Connect(url common.URL) { initClient(url.Protocol) c.conf = *clientConf @@ -169,41 +158,15 @@ func (c *Client) Connect(url common.URL) { c.codec = remoting.GetCodec(url.Protocol) c.addr = url.Location } + func (c *Client) Close() { if c.pool != nil { c.pool.close() } c.pool = nil } -func (c *Client) Request(request *remoting.Request, timeout time.Duration, callback common.AsyncCallback, response *remoting.PendingResponse) 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 - //} +func (c *Client) Request(request *remoting.Request, timeout time.Duration, response *remoting.PendingResponse) error { var ( err error @@ -225,11 +188,11 @@ func (c *Client) Request(request *remoting.Request, timeout time.Duration, callb conn.close() }() - if err = c.transfer(session, request, timeout, response); err != nil { + if err = c.transfer(session, request, timeout); err != nil { return perrors.WithStack(err) } - if !request.TwoWay || callback != nil { + if !request.TwoWay || response.Callback != nil { return nil } @@ -243,98 +206,6 @@ func (c *Client) Request(request *remoting.Request, timeout time.Duration, callb return perrors.WithStack(err) } -// CallOneway call one way -//func (c *Client) CallOneway(request *Request) error { -// -// return perrors.WithStack(c.call(CT_OneWay, request, NewResponse(nil, nil), nil)) -//} -// -//// 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 -// } -// -// return perrors.WithStack(c.call(ct, request, response, nil)) -//} -// -//// 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 -// } -// -// 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) -//} - func (c *Client) selectSession(addr string) (*gettyRPCClient, getty.Session, error) { rpcClient, err := c.pool.getGettyRpcClient(addr) if err != nil { @@ -349,49 +220,15 @@ func (c *Client) heartbeat(session getty.Session) error { req.Event = true resp := remoting.NewPendingResponse(req.Id) remoting.AddPendingResponse(resp) - return c.transfer(session, req, 3*time.Second, resp) + return c.transfer(session, req, 3*time.Second) } -func (c *Client) transfer(session getty.Session, request *remoting.Request, timeout time.Duration, - rsp *remoting.PendingResponse) 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 { - // c.addPendingResponse(rsp) - //} - +func (c *Client) transfer(session getty.Session, request *remoting.Request, timeout time.Duration) error { err := session.WritePkg(request, timeout) - 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() - } - + //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/remoting/getty/getty_client_test.go b/remoting/getty/getty_client_test.go new file mode 100644 index 0000000000..9fa46daaf3 --- /dev/null +++ b/remoting/getty/getty_client_test.go @@ -0,0 +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" + + "github.com/apache/dubbo-go/common/proxy/proxy_factory" + + "github.com/apache/dubbo-go/config" + + "github.com/apache/dubbo-go/remoting" + + "github.com/apache/dubbo-go/protocol/invocation" + + hessian "github.com/apache/dubbo-go-hessian2" + + "github.com/apache/dubbo-go/common" + . "github.com/apache/dubbo-go/common/constant" + "github.com/apache/dubbo-go/protocol" + perrors "github.com/pkg/errors" + "github.com/stretchr/testify/assert" +) + +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) + 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.NewCodec("dubbo", &DubboTestCodec{}) + + 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) + invoker := &proxy_factory.ProxyInvoker{ + BaseInvoker: *protocol.NewBaseInvoker(url), + } + handler := func(invocation *invocation.RPCInvocation) protocol.RPCResult { + //result := protocol.RPCResult{} + r := invoker.Invoke(context.Background(), invocation) + result := protocol.RPCResult{ + Err: r.Error(), + Rest: r.Result(), + Attrs: r.Attachments(), + } + return result + } + server := NewServer(url, handler) + server.Start() + + time.Sleep(time.Second * 2) + + return server, url +} + +////////////////////////////////// +// provider +////////////////////////////////// + +type ( + User struct { + Id string `json:"id"` + Name string `json:"name"` + } + + UserProvider struct { + user map[string]User + } +) + +// size:4801228 +func (u *UserProvider) GetBigPkg(ctx context.Context, req []interface{}, rsp *User) error { + argBuf := new(bytes.Buffer) + for i := 0; i < 400; i++ { + argBuf.WriteString("击鼓其镗,踊跃用兵。土国城漕,我独南行。从孙子仲,平陈与宋。不我以归,忧心有忡。爰居爰处?爰丧其马?于以求之?于林之下。死生契阔,与子成说。执子之手,与子偕老。于嗟阔兮,不我活兮。于嗟洵兮,不我信兮。") + argBuf.WriteString("击鼓其镗,踊跃用兵。土国城漕,我独南行。从孙子仲,平陈与宋。不我以归,忧心有忡。爰居爰处?爰丧其马?于以求之?于林之下。死生契阔,与子成说。执子之手,与子偕老。于嗟阔兮,不我活兮。于嗟洵兮,不我信兮。") + } + rsp.Id = argBuf.String() + rsp.Name = argBuf.String() + return nil +} + +func (u *UserProvider) GetUser(ctx context.Context, req []interface{}, rsp *User) error { + rsp.Id = req[0].(string) + rsp.Name = req[1].(string) + return nil +} + +func (u *UserProvider) GetUser0(id string, k *User, name string) (User, error) { + return User{Id: id, Name: name}, nil +} + +func (u *UserProvider) GetUser1() error { + return nil +} + +func (u *UserProvider) GetUser2() error { + return perrors.New("error") +} + +func (u *UserProvider) GetUser3(rsp *[]interface{}) error { + *rsp = append(*rsp, User{Id: "1", Name: "username"}) + return nil +} + +func (u *UserProvider) GetUser4(ctx context.Context, req []interface{}) ([]interface{}, error) { + + return []interface{}{User{Id: req[0].([]interface{})[0].(string), Name: req[0].([]interface{})[1].(string)}}, nil +} + +func (u *UserProvider) GetUser5(ctx context.Context, req []interface{}) (map[interface{}]interface{}, error) { + return map[interface{}]interface{}{"key": User{Id: req[0].(map[interface{}]interface{})["id"].(string), Name: req[0].(map[interface{}]interface{})["name"].(string)}}, nil +} + +func (u *UserProvider) GetUser6(id int64) (*User, error) { + if id == 0 { + return nil, nil + } + return &User{Id: "1"}, nil +} + +func (u *UserProvider) Reference() string { + return "UserProvider" +} + +func (u User) JavaClassName() string { + return "com.ikurento.user.User" +} diff --git a/remoting/getty/server.go b/remoting/getty/getty_server.go similarity index 98% rename from remoting/getty/server.go rename to remoting/getty/getty_server.go index 00fba5cae3..874d0401f5 100644 --- a/remoting/getty/server.go +++ b/remoting/getty/getty_server.go @@ -19,22 +19,18 @@ package getty import ( "fmt" + "net" + "github.com/apache/dubbo-go/protocol" "github.com/apache/dubbo-go/protocol/invocation" "github.com/apache/dubbo-go/remoting" - "net" -) - -import ( "github.com/dubbogo/getty" - "github.com/dubbogo/gost/sync" - "gopkg.in/yaml.v2" -) -import ( "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common/logger" "github.com/apache/dubbo-go/config" + gxsync "github.com/dubbogo/gost/sync" + "gopkg.in/yaml.v2" ) var ( diff --git a/remoting/getty/listener.go b/remoting/getty/listener.go index 0dc2792a23..156a4046cb 100644 --- a/remoting/getty/listener.go +++ b/remoting/getty/listener.go @@ -18,25 +18,20 @@ package getty import ( - "context" "fmt" - "github.com/apache/dubbo-go/remoting" "sync" "sync/atomic" "time" -) - -import ( - "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/constant" + + "github.com/apache/dubbo-go/remoting" + + hessian "github.com/apache/dubbo-go-hessian2" "github.com/apache/dubbo-go/common/logger" "github.com/apache/dubbo-go/protocol/invocation" + "github.com/dubbogo/getty" + perrors "github.com/pkg/errors" ) // todo: WritePkg_Timeout will entry *.yml @@ -96,37 +91,48 @@ func (h *RpcClientHandler) OnClose(session getty.Session) { // OnMessage ... func (h *RpcClientHandler) OnMessage(session getty.Session, pkg interface{}) { - p, ok := pkg.(*remoting.Response) + result, ok := pkg.(remoting.DecodeResult) if !ok { logger.Errorf("illegal package") return } + // get heartbeart request from server + if result.IsRequest { + req := result.Result.(*remoting.Request) + if req.Event { + logger.Debugf("get rpc heartbeat request{%#v}", req) + resp := remoting.NewResponse(req.Id, req.Version) + resp.Status = hessian.Response_OK + resp.Event = req.Event + resp.SerialID = req.SerialID + resp.Version = "2.0.2" + reply(session, resp, hessian.PackageHeartbeat) + return + } + logger.Errorf("illegal request but not heartbeart. {%#v}", req) + return + } + p := result.Result.(*remoting.Response) + // get heartbeart if p.Event { logger.Debugf("get rpc heartbeat response{%#v}", p) if p.Error != nil { logger.Errorf("rpc heartbeat response{error: %#v}", p.Error) } - (h.conn.pool.rpcClient.responseHandler).Handler(p) - //FIXME - //if p.Header.Type&hessian.PackageResponse != 0x00 { - // logger.Debugf("get rpc heartbeat response{header: %#v, body: %#v}", p.Header, p.Body) - // if p.Err != nil { - // logger.Errorf("rpc heartbeat response{error: %#v}", p.Err) - // } - // h.conn.pool.rpcClient.removePendingResponse(SequenceType(p.Header.ID)) - //} else { - // logger.Debugf("get rpc heartbeat request{header: %#v, service: %#v, body: %#v}", p.Header, p.Service, p.Body) - // p.Header.ResponseStatus = hessian.Response_OK - // reply(session, p, hessian.PackageHeartbeat) - //} + h.conn.pool.rpcClient.responseHandler.Handler(p) + return + } + if result.IsRequest { + logger.Errorf("illegal package for it is response type. {%#v}", pkg) return } + logger.Debugf("get rpc response{%#v}", p) h.conn.updateSession(session) - (h.conn.pool.rpcClient.responseHandler).Handler(p) + h.conn.pool.rpcClient.responseHandler.Handler(p) // //pendingResponse := h.conn.pool.rpcClient.removePendingResponse(SequenceType(p.Header.ID)) @@ -232,11 +238,17 @@ func (h *RpcServerHandler) OnMessage(session getty.Session, pkg interface{}) { } h.rwlock.Unlock() - req, ok := pkg.(*remoting.Request) + decodeResult, ok := pkg.(remoting.DecodeResult) if !ok { logger.Errorf("illegal package{%#v}", pkg) return } + if !decodeResult.IsRequest { + logger.Errorf("illegal package for it is response type. {%#v}", pkg) + return + } + req := decodeResult.Result.(*remoting.Request) + resp := remoting.NewResponse(req.Id, req.Version) resp.Status = hessian.Response_OK resp.Event = req.Event @@ -280,8 +292,13 @@ func (h *RpcServerHandler) OnMessage(session getty.Session, pkg interface{}) { invoc, ok := req.Data.(*invocation.RPCInvocation) if !ok { - + panic("create invocation occur some exception for the type is not suitable one.") + return } + attachments := invoc.Attachments() + attachments[constant.LOCAL_ADDR] = session.LocalAddr() + attachments[constant.REMOTE_ADDR] = session.RemoteAddr() + result := h.server.requestHandler(invoc) if !req.TwoWay { return @@ -316,21 +333,6 @@ 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) - } - return ctx -} - func reply(session getty.Session, resp *remoting.Response, tp hessian.PackageType) { //resp := &DubboPackage{ // Header: hessian.DubboHeader{ diff --git a/remoting/getty/listener_test.go b/remoting/getty/listener_test.go index a7feeddce0..49e13d39c9 100644 --- a/remoting/getty/listener_test.go +++ b/remoting/getty/listener_test.go @@ -17,42 +17,53 @@ package getty -//import ( -// "testing" -//) -// -//import ( -// "github.com/opentracing/opentracing-go" -// "github.com/opentracing/opentracing-go/mocktracer" -// "github.com/stretchr/testify/assert" -//) -// -//import ( -// "github.com/apache/dubbo-go/common/constant" -// "github.com/apache/dubbo-go/protocol/invocation" -//) -// -//// test rebuild the ctx -//func TestRebuildCtx(t *testing.T) { -// opentracing.SetGlobalTracer(mocktracer.New()) -// attach := make(map[string]string, 10) -// attach[constant.VERSION_KEY] = "1.0" -// attach[constant.GROUP_KEY] = "MyGroup" -// inv := invocation.NewRPCInvocation("MethodName", []interface{}{"OK", "Hello"}, attach) -// -// // attachment doesn't contains any tracing key-value pair, -// ctx := rebuildCtx(inv) -// assert.NotNil(t, ctx) -// assert.Nil(t, ctx.Value(constant.TRACING_REMOTE_SPAN_CTX)) -// -// span, ctx := opentracing.StartSpanFromContext(ctx, "Test-Client") -// -// opentracing.GlobalTracer().Inject(span.Context(), opentracing.TextMap, -// opentracing.TextMapCarrier(inv.Attachments())) -// // rebuild the context success -// inv = invocation.NewRPCInvocation("MethodName", []interface{}{"OK", "Hello"}, attach) -// ctx = rebuildCtx(inv) -// span.Finish() -// assert.NotNil(t, ctx) -// assert.NotNil(t, ctx.Value(constant.TRACING_REMOTE_SPAN_CTX)) -//} +import ( + "context" + "testing" + + "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" + "github.com/stretchr/testify/assert" +) + +// test rebuild the ctx +func TestRebuildCtx(t *testing.T) { + opentracing.SetGlobalTracer(mocktracer.New()) + attach := make(map[string]string, 10) + attach[constant.VERSION_KEY] = "1.0" + attach[constant.GROUP_KEY] = "MyGroup" + inv := invocation.NewRPCInvocation("MethodName", []interface{}{"OK", "Hello"}, attach) + + // attachment doesn't contains any tracing key-value pair, + ctx := rebuildCtx(inv) + assert.NotNil(t, ctx) + assert.Nil(t, ctx.Value(constant.TRACING_REMOTE_SPAN_CTX)) + + span, ctx := opentracing.StartSpanFromContext(ctx, "Test-Client") + + opentracing.GlobalTracer().Inject(span.Context(), opentracing.TextMap, + opentracing.TextMapCarrier(inv.Attachments())) + // rebuild the context success + inv = invocation.NewRPCInvocation("MethodName", []interface{}{"OK", "Hello"}, attach) + ctx = rebuildCtx(inv) + span.Finish() + assert.NotNil(t, ctx) + assert.NotNil(t, ctx.Value(constant.TRACING_REMOTE_SPAN_CTX)) +} + +// rebuildCtx rebuild the context by attachment. +// Once we decided to transfer more context's key-value, we should change this. +// now we only support rebuild the tracing context +func rebuildCtx(inv *invocation.RPCInvocation) context.Context { + ctx := context.WithValue(context.Background(), "attachment", inv.Attachments()) + + // actually, if user do not use any opentracing framework, the err will not be nil. + spanCtx, err := opentracing.GlobalTracer().Extract(opentracing.TextMap, + opentracing.TextMapCarrier(inv.Attachments())) + if err == nil { + ctx = context.WithValue(ctx, constant.TRACING_REMOTE_SPAN_CTX, spanCtx) + } + return ctx +} diff --git a/remoting/getty/readwriter.go b/remoting/getty/readwriter.go index b59d9b82fb..1aed516a3a 100644 --- a/remoting/getty/readwriter.go +++ b/remoting/getty/readwriter.go @@ -18,18 +18,15 @@ package getty import ( - "github.com/apache/dubbo-go/remoting" "reflect" -) -import ( - "github.com/apache/dubbo-go-hessian2" + "github.com/apache/dubbo-go/remoting" + + hessian "github.com/apache/dubbo-go-hessian2" "github.com/dubbogo/getty" - perrors "github.com/pkg/errors" -) -import ( "github.com/apache/dubbo-go/common/logger" + perrors "github.com/pkg/errors" ) //////////////////////////////////////////// @@ -49,17 +46,16 @@ func NewRpcClientPackageHandler(client *Client) *RpcClientPackageHandler { func (p *RpcClientPackageHandler) Read(ss getty.Session, data []byte) (interface{}, int, error) { //pkg := &DubboPackage{} //p.client.ExchangeClient.GetPendingResponse(remoting.SequenceType()) - resp, length, err := (p.client.codec).DecodeResponse(data) + resp, length, err := (p.client.codec).Decode(data) //err := pkg.Unmarshal(buf, p.client) if err != nil { - originErr := perrors.Cause(err) - if originErr == hessian.ErrHeaderNotEnough || originErr == hessian.ErrBodyNotEnough { + if err == hessian.ErrHeaderNotEnough || err == hessian.ErrBodyNotEnough { return nil, 0, nil } logger.Errorf("pkg.Unmarshal(ss:%+v, len(@data):%d) = error:%+v", ss, len(data), err) - return nil, length, perrors.WithStack(err) + return nil, length, err } //if pkg.Header.Type&hessian.PackageRequest == 0x00 { // pkg.Err = pkg.Body.(*hessian.Response).Exception @@ -103,18 +99,17 @@ func NewRpcServerPackageHandler(server *Server) *RpcServerPackageHandler { } func (p *RpcServerPackageHandler) Read(ss getty.Session, data []byte) (interface{}, int, error) { - req, length, err := (p.server.codec).DecodeRequest(data) + req, length, err := (p.server.codec).Decode(data) //resp,len, err := (*p.).DecodeResponse(buf) if err != nil { - originErr := perrors.Cause(err) - if originErr == hessian.ErrHeaderNotEnough || originErr == hessian.ErrBodyNotEnough { + if err == hessian.ErrHeaderNotEnough || err == hessian.ErrBodyNotEnough { return nil, 0, nil } logger.Errorf("pkg.Unmarshal(ss:%+v, len(@data):%d) = error:%+v", ss, len(data), err) - return nil, 0, perrors.WithStack(err) + return nil, 0, err } return req, length, err From c67d34a91067698e288f7318f2cee29b5534cd2d Mon Sep 17 00:00:00 2001 From: cvictory Date: Fri, 8 May 2020 20:02:22 +0800 Subject: [PATCH 04/44] fix review issue --- protocol/dubbo/dubbo_codec.go | 1 - protocol/dubbo/dubbo_protocol.go | 15 +++++++++++---- remoting/exchange_client.go | 10 ++++++++-- remoting/exchange_server.go | 4 ++-- remoting/getty/getty_client.go | 10 ++++------ 5 files changed, 25 insertions(+), 15 deletions(-) diff --git a/protocol/dubbo/dubbo_codec.go b/protocol/dubbo/dubbo_codec.go index c1f8231385..a7f265c3bb 100644 --- a/protocol/dubbo/dubbo_codec.go +++ b/protocol/dubbo/dubbo_codec.go @@ -82,7 +82,6 @@ func (p *DubboPackage) Unmarshal(buf *bytes.Buffer, resp *remoting.Response) err p.Body = &hessian.Response{RspObj: pendingRsp.Reply} } } - // read body err = codec.ReadBody(p.Body) return perrors.WithStack(err) diff --git a/protocol/dubbo/dubbo_protocol.go b/protocol/dubbo/dubbo_protocol.go index 3bf0341923..4fd2492a75 100644 --- a/protocol/dubbo/dubbo_protocol.go +++ b/protocol/dubbo/dubbo_protocol.go @@ -94,7 +94,11 @@ func (dp *DubboProtocol) Refer(url common.URL) protocol.Invoker { // ConnectTimeout: config.GetConsumerConfig().ConnectTimeout, // RequestTimeout: requestTimeout, //})) - invoker := NewDubboInvoker(url, getExchangeClient(url)) + exchangeClient := getExchangeClient(url) + if exchangeClient == nil { + return nil + } + invoker := NewDubboInvoker(url, exchangeClient) dp.SetInvokers(invoker) logger.Infof("Refer service: %s", url.String()) return invoker @@ -180,7 +184,9 @@ func getExchangeClient(url common.URL) *remoting.ExchangeClient { exchangeClientTmp := remoting.NewExchangeClient(url, getty.NewClient(getty.Options{ ConnectTimeout: config.GetConsumerConfig().ConnectTimeout, }), config.GetConsumerConfig().ConnectTimeout) - exchangeClientMap.Store(url.Location, exchangeClientTmp) + if exchangeClientTmp != nil { + exchangeClientMap.Store(url.Location, exchangeClientTmp) + } return exchangeClientTmp } @@ -189,8 +195,9 @@ func getExchangeClient(url common.URL) *remoting.ExchangeClient { exchangeClientTmp := remoting.NewExchangeClient(url, getty.NewClient(getty.Options{ ConnectTimeout: config.GetConsumerConfig().ConnectTimeout, }), config.GetConsumerConfig().ConnectTimeout) - exchangeClientMap.Store(url.Location, exchangeClientTmp) - + if exchangeClientTmp != nil { + exchangeClientMap.Store(url.Location, exchangeClientTmp) + } return exchangeClientTmp } return exchangeClient diff --git a/remoting/exchange_client.go b/remoting/exchange_client.go index 2a0e6cfd79..e1778ed3a4 100644 --- a/remoting/exchange_client.go +++ b/remoting/exchange_client.go @@ -26,7 +26,7 @@ type Client interface { SetResponseHandler(responseHandler ResponseHandler) //invoke once for connection //ConfigClient() - Connect(url common.URL) + Connect(url common.URL) error Close() Request(request *Request, timeout time.Duration, response *PendingResponse) error } @@ -42,7 +42,13 @@ func NewExchangeClient(url common.URL, client Client, connectTimeout time.Durati client: client, } client.SetExchangeClient(exchangeClient) - client.Connect(url) + if client.Connect(url) != nil { + //retry for a while + time.Sleep(1 * time.Second) + if client.Connect(url) != nil { + return nil + } + } client.SetResponseHandler(exchangeClient) return exchangeClient } diff --git a/remoting/exchange_server.go b/remoting/exchange_server.go index 4aae39fd83..dd6e76087f 100644 --- a/remoting/exchange_server.go +++ b/remoting/exchange_server.go @@ -22,9 +22,9 @@ func NewExchangeServer(url common.URL, server Server) *ExchangeServer { } func (server *ExchangeServer) Start() { - (server.Server).Start() + server.Server.Start() } func (server *ExchangeServer) Stop() { - (server.Server).Stop() + server.Server.Stop() } diff --git a/remoting/getty/getty_client.go b/remoting/getty/getty_client.go index 8544b8e17f..ccf8881bfe 100644 --- a/remoting/getty/getty_client.go +++ b/remoting/getty/getty_client.go @@ -149,7 +149,7 @@ func (c *Client) SetResponseHandler(responseHandler remoting.ResponseHandler) { c.responseHandler = responseHandler } -func (c *Client) Connect(url common.URL) { +func (c *Client) Connect(url common.URL) error { initClient(url.Protocol) c.conf = *clientConf // new client @@ -157,6 +157,9 @@ func (c *Client) Connect(url common.URL) { // codec c.codec = remoting.GetCodec(url.Protocol) c.addr = url.Location + _, _, err := c.selectSession(c.addr) + logger.Error("try to connect server %v failed for %v", url.Location, err) + return err } func (c *Client) Close() { @@ -225,10 +228,5 @@ func (c *Client) heartbeat(session getty.Session) error { func (c *Client) transfer(session getty.Session, request *remoting.Request, timeout time.Duration) error { err := session.WritePkg(request, timeout) - //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) } From acc7c7982a85f50e652355f4fcec34a2c3197bbc Mon Sep 17 00:00:00 2001 From: cvictory Date: Fri, 8 May 2020 21:25:33 +0800 Subject: [PATCH 05/44] fix review issue --- protocol/dubbo/dubbo_codec.go | 27 +---------- protocol/dubbo/dubbo_invoker.go | 33 +++++++++----- protocol/dubbo/dubbo_protocol.go | 12 ----- protocol/dubbo/dubbo_protocol_test.go | 64 ++++++++++++++++++++++++--- remoting/getty/getty_client.go | 2 +- 5 files changed, 84 insertions(+), 54 deletions(-) diff --git a/protocol/dubbo/dubbo_codec.go b/protocol/dubbo/dubbo_codec.go index a7f265c3bb..12ab408d08 100644 --- a/protocol/dubbo/dubbo_codec.go +++ b/protocol/dubbo/dubbo_codec.go @@ -177,11 +177,6 @@ func (c *DubboCodec) EncodeResponse(response *remoting.Response) (*bytes.Buffer, } } - //if response.Header.Type&hessian.PackageRequest != 0x00 { - // resp.Body = req.Body - //} else { - // resp.Body = nil - //} codec := hessian.NewHessianCodec(nil) pkg, err := codec.Write(resp.Service, resp.Header, resp.Body) @@ -261,9 +256,7 @@ func (c *DubboCodec) decodeRequest(data []byte) (*remoting.Request, int, error) methodName = req[3].(string) } if req[4] != nil { - //argsType - //invocation.ParameterTypes(constant., req[1].(string)) - //argsTypes = req[4].(string) + //ignore argTypes } if req[5] != nil { args = req[5].([]interface{}) @@ -271,27 +264,9 @@ func (c *DubboCodec) decodeRequest(data []byte) (*remoting.Request, int, error) 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] - //} invoc := invocation.NewRPCInvocationWithOptions(invocation.WithAttachments(attachments), invocation.WithArguments(args), invocation.WithMethodName(methodName)) request.Data = invoc - //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 request, hessian.HEADER_LENGTH + pkg.Header.BodyLen, nil diff --git a/protocol/dubbo/dubbo_invoker.go b/protocol/dubbo/dubbo_invoker.go index ddee734b1b..1b3ac6fc80 100644 --- a/protocol/dubbo/dubbo_invoker.go +++ b/protocol/dubbo/dubbo_invoker.go @@ -19,24 +19,22 @@ package dubbo import ( "context" - "github.com/apache/dubbo-go/config" - "github.com/apache/dubbo-go/remoting" "strconv" + "strings" "sync" "sync/atomic" "time" -) -import ( + "github.com/apache/dubbo-go/config" + "github.com/apache/dubbo-go/remoting" "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" + perrors "github.com/pkg/errors" + invocation_impl "github.com/apache/dubbo-go/protocol/invocation" ) @@ -111,18 +109,19 @@ func (di *DubboInvoker) Invoke(ctx context.Context, invocation protocol.Invocati } //response := NewResponse(inv.Reply(), nil) rest := &protocol.RPCResult{} + timeout := di.getTimeout(inv) if async { if callBack, ok := inv.CallBack().(func(response common.CallbackResponse)); ok { //result.Err = di.client.AsyncCall(NewRequest(url.Location, url, inv.MethodName(), inv.Arguments(), inv.Attachments()), callBack, response) - result.Err = di.client.AsyncRequest(&invocation, url, di.timeout, callBack, rest) + result.Err = di.client.AsyncRequest(&invocation, url, timeout, callBack, rest) } else { - result.Err = di.client.Send(&invocation, di.timeout) + result.Err = di.client.Send(&invocation, timeout) } } else { if inv.Reply() == nil { result.Err = ErrNoReply } else { - result.Err = di.client.Request(&invocation, url, di.timeout, rest) + result.Err = di.client.Request(&invocation, url, timeout, rest) } } if result.Err == nil { @@ -134,6 +133,20 @@ func (di *DubboInvoker) Invoke(ctx context.Context, invocation protocol.Invocati 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 + } + } + invocation.SetAttachments(constant.TIMEOUT_KEY, strconv.Itoa(int(di.timeout.Milliseconds()))) + return di.timeout +} + // Destroy ... func (di *DubboInvoker) Destroy() { di.quitOnce.Do(func() { diff --git a/protocol/dubbo/dubbo_protocol.go b/protocol/dubbo/dubbo_protocol.go index 4fd2492a75..8c28456045 100644 --- a/protocol/dubbo/dubbo_protocol.go +++ b/protocol/dubbo/dubbo_protocol.go @@ -82,18 +82,6 @@ func (dp *DubboProtocol) Export(invoker protocol.Invoker) protocol.Exporter { // Refer ... 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 - //} - - //invoker := NewDubboInvoker(url, NewClient(Options{ - // ConnectTimeout: config.GetConsumerConfig().ConnectTimeout, - // RequestTimeout: requestTimeout, - //})) exchangeClient := getExchangeClient(url) if exchangeClient == nil { return nil diff --git a/protocol/dubbo/dubbo_protocol_test.go b/protocol/dubbo/dubbo_protocol_test.go index 932e94862d..b2894ed7fa 100644 --- a/protocol/dubbo/dubbo_protocol_test.go +++ b/protocol/dubbo/dubbo_protocol_test.go @@ -27,11 +27,50 @@ import ( "github.com/stretchr/testify/assert" ) +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) { // Export proto := GetProtocol() - getty.SetServerConfig(getty.ServerConfig{}) - url, err := common.NewURL("dubbo://127.0.0.1:20080/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&" + @@ -44,7 +83,7 @@ func TestDubboProtocol_Export(t *testing.T) { assert.True(t, eq) // second service: the same path and the different version - url2, err := common.NewURL("dubbo://127.0.0.1:20080/com.ikurento.user.UserProvider?anyhost=true&"+ + 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&"+ @@ -70,16 +109,31 @@ func TestDubboProtocol_Export(t *testing.T) { 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) { // 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&" + "side=provider&timeout=3000×tamp=1556509797245") assert.NoError(t, err) - getty.SetClientConf(getty.ClientConfig{}) + proto.Export(protocol.NewBaseInvoker(url)) + invoker := proto.Refer(url) // make sure url diff --git a/remoting/getty/getty_client.go b/remoting/getty/getty_client.go index ccf8881bfe..9687c9cde9 100644 --- a/remoting/getty/getty_client.go +++ b/remoting/getty/getty_client.go @@ -158,7 +158,7 @@ func (c *Client) Connect(url common.URL) error { c.codec = remoting.GetCodec(url.Protocol) c.addr = url.Location _, _, err := c.selectSession(c.addr) - logger.Error("try to connect server %v failed for %v", url.Location, err) + logger.Errorf("try to connect server %v failed for : %v", url.Location, err) return err } From d48fd1e3260ff3d27fa5ba92d30960f9e3600670 Mon Sep 17 00:00:00 2001 From: cvictory Date: Sat, 9 May 2020 14:21:02 +0800 Subject: [PATCH 06/44] remove unused code, add license --- protocol/dubbo/dubbo_codec.go | 29 ++++++++++--------- protocol/dubbo/dubbo_codec_test.go | 1 - remoting/codec.go | 17 +++++++++++ remoting/exchange.go | 24 ++++++++++------ remoting/exchange_client.go | 22 +++++++++++---- remoting/exchange_server.go | 16 +++++++++++ remoting/getty/dubbo_codec_for_test.go | 17 +++++++++++ remoting/getty/getty_client.go | 11 +------- remoting/getty/listener.go | 39 -------------------------- 9 files changed, 99 insertions(+), 77 deletions(-) delete mode 100644 protocol/dubbo/dubbo_codec_test.go diff --git a/protocol/dubbo/dubbo_codec.go b/protocol/dubbo/dubbo_codec.go index 12ab408d08..f297d4c3d4 100644 --- a/protocol/dubbo/dubbo_codec.go +++ b/protocol/dubbo/dubbo_codec.go @@ -1,3 +1,19 @@ +/* + * 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 ( @@ -114,12 +130,6 @@ func (c *DubboCodec) EncodeRequest(request *remoting.Request) (*bytes.Buffer, er panic(err) } p.Service.Timeout = time.Duration(timeout) - //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.Header.ID = request.Id @@ -322,12 +332,5 @@ func (c *DubboCodec) decodeResponse(data []byte) (*remoting.Response, int, error rpcResult.Rest = pkg.Body.(*hessian.Response).RspObj } - //h.conn.updateSession(session) - //pendingResponse := h.conn.pool.rpcClient.removePendingResponse(SequenceType(p.Header.ID)) - //if pendingResponse == nil { - // logger.Errorf("failed to get pending response context for response package %s", *p) - // return - //} - return response, hessian.HEADER_LENGTH + pkg.Header.BodyLen, nil } diff --git a/protocol/dubbo/dubbo_codec_test.go b/protocol/dubbo/dubbo_codec_test.go deleted file mode 100644 index f401318d86..0000000000 --- a/protocol/dubbo/dubbo_codec_test.go +++ /dev/null @@ -1 +0,0 @@ -package dubbo diff --git a/remoting/codec.go b/remoting/codec.go index 1c1e025762..7ca7552554 100644 --- a/remoting/codec.go +++ b/remoting/codec.go @@ -1,9 +1,26 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package remoting import ( "bytes" ) +// codec for exchangeClient type Codec interface { EncodeRequest(request *Request) (*bytes.Buffer, error) EncodeResponse(response *Response) (*bytes.Buffer, error) diff --git a/remoting/exchange.go b/remoting/exchange.go index eaa19f7195..45babec64b 100644 --- a/remoting/exchange.go +++ b/remoting/exchange.go @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package remoting import ( @@ -38,12 +54,6 @@ func NewRequest(version string) *Request { } } -//func (request *Request) SetHeartbeat(isHeartbeat bool) { -// if isHeartbeat { -// -// } -//} - // Response ... type Response struct { Id int64 @@ -70,8 +80,6 @@ func (response *Response) IsHeartbeat() bool { type Options struct { // connect timeout ConnectTimeout time.Duration - // request timeout - //RequestTimeout time.Duration } //AsyncCallbackResponse async response for dubbo diff --git a/remoting/exchange_client.go b/remoting/exchange_client.go index e1778ed3a4..cd1e3a1017 100644 --- a/remoting/exchange_client.go +++ b/remoting/exchange_client.go @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package remoting import ( @@ -24,8 +40,6 @@ type ExchangeClient struct { type Client interface { SetExchangeClient(client *ExchangeClient) SetResponseHandler(responseHandler ResponseHandler) - //invoke once for connection - //ConfigClient() Connect(url common.URL) error Close() Request(request *Request, timeout time.Duration, response *PendingResponse) error @@ -64,7 +78,6 @@ func (client *ExchangeClient) Request(invocation *protocol.Invocation, url commo rsp.response = NewResponse(request.Id, "2.0.2") rsp.Reply = (*invocation).Reply() AddPendingResponse(rsp) - //rsp.callback = invo err := client.client.Request(request, timeout, rsp) if err != nil { @@ -72,7 +85,6 @@ func (client *ExchangeClient) Request(invocation *protocol.Invocation, url commo return err } result.Rest = rsp.response.Result - //result.Attrs = rsp.response. return nil } @@ -95,7 +107,6 @@ func (client *ExchangeClient) AsyncRequest(invocation *protocol.Invocation, url return err } result.Rest = rsp.response - //result.Attrs = rsp.response. return nil } @@ -113,7 +124,6 @@ func (client *ExchangeClient) Send(invocation *protocol.Invocation, timeout time if err != nil { return err } - //result.Attrs = rsp.response. return nil } diff --git a/remoting/exchange_server.go b/remoting/exchange_server.go index dd6e76087f..44f41622a2 100644 --- a/remoting/exchange_server.go +++ b/remoting/exchange_server.go @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package remoting import ( diff --git a/remoting/getty/dubbo_codec_for_test.go b/remoting/getty/dubbo_codec_for_test.go index 46ef7b939e..83f0359c38 100644 --- a/remoting/getty/dubbo_codec_for_test.go +++ b/remoting/getty/dubbo_codec_for_test.go @@ -1,5 +1,22 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package getty +// copy from dubbo/dubbo_codec.go import ( "bufio" "bytes" diff --git a/remoting/getty/getty_client.go b/remoting/getty/getty_client.go index 9687c9cde9..a7a0c2c6ab 100644 --- a/remoting/getty/getty_client.go +++ b/remoting/getty/getty_client.go @@ -108,16 +108,6 @@ type Options struct { ConnectTimeout time.Duration } -////AsyncCallbackResponse async response for dubbo -//type AsyncCallbackResponse struct { -// common.CallbackResponse -// Opts Options -// Cause error -// Start time.Time // invoke(call) start time == write start time -// ReadStart time.Time // read start time, write duration = ReadStart - Start -// Reply interface{} -//} - // Client ... type Client struct { addr string @@ -149,6 +139,7 @@ func (c *Client) SetResponseHandler(responseHandler remoting.ResponseHandler) { c.responseHandler = responseHandler } +// init client and try to connection. func (c *Client) Connect(url common.URL) error { initClient(url.Protocol) c.conf = *clientConf diff --git a/remoting/getty/listener.go b/remoting/getty/listener.go index 156a4046cb..9d4252915d 100644 --- a/remoting/getty/listener.go +++ b/remoting/getty/listener.go @@ -133,25 +133,6 @@ func (h *RpcClientHandler) OnMessage(session getty.Session, pkg interface{}) { h.conn.updateSession(session) h.conn.pool.rpcClient.responseHandler.Handler(p) - - // - //pendingResponse := h.conn.pool.rpcClient.removePendingResponse(SequenceType(p.Header.ID)) - //if pendingResponse == nil { - // logger.Errorf("failed to get pending response context for response package %s", *p) - // return - //} - // - //if p.Err != nil { - // pendingResponse.err = p.Err - //} - // - //pendingResponse.response.atta = p.Body.(*Response).atta - // - //if pendingResponse.callback == nil { - // pendingResponse.done <- struct{}{} - //} else { - // pendingResponse.callback(pendingResponse.GetCallResponse()) - //} } // OnCron ... @@ -262,12 +243,6 @@ func (h *RpcServerHandler) OnMessage(session getty.Session, pkg interface{}) { return } - //twoway := true - //// not twoway - //if p.Header.Type&hessian.PackageRequest_TwoWay == 0x00 { - // twoway = false - //} - defer func() { if e := recover(); e != nil { resp.Status = hessian.Response_SERVER_ERROR @@ -334,20 +309,6 @@ func (h *RpcServerHandler) OnCron(session getty.Session) { } func reply(session getty.Session, resp *remoting.Response, tp hessian.PackageType) { - //resp := &DubboPackage{ - // Header: hessian.DubboHeader{ - // SerialID: req.Header.SerialID, - // Type: tp, - // ID: req.Header.ID, - // ResponseStatus: req.Header.ResponseStatus, - // }, - //} - // - //if resp.Event { - // resp.Result = req.Body - //} else { - // resp.Body = nil - //} if err := session.WritePkg(resp, WritePkg_Timeout); err != nil { logger.Errorf("WritePkg error: %#v, %#v", perrors.WithStack(err), resp) From 58e7e86d6d7856bffa4e5eedf6f3d5f6a48be6a1 Mon Sep 17 00:00:00 2001 From: cvictory Date: Thu, 14 May 2020 16:53:58 +0800 Subject: [PATCH 07/44] fix review issue: add comment and some optimization --- config/application_config.go | 7 ++-- protocol/dubbo/dubbo_codec.go | 36 ++++++++++++++------- protocol/dubbo/dubbo_invoker.go | 9 ++++-- protocol/dubbo/dubbo_invoker_test.go | 2 +- protocol/dubbo/dubbo_protocol.go | 2 ++ protocol/invocation/rpcinvocation.go | 10 +++--- protocol/jsonrpc/http_test.go | 22 ++++++------- protocol/jsonrpc/jsonrpc_invoker.go | 5 ++- remoting/exchange.go | 31 +++++++++++------- remoting/exchange_client.go | 45 ++++++++++++++++++-------- remoting/exchange_server.go | 7 ++++ remoting/getty/dubbo_codec_for_test.go | 16 +++++---- remoting/getty/getty_client.go | 14 +++++--- remoting/getty/getty_client_test.go | 26 +++++++-------- remoting/getty/listener.go | 4 +-- 15 files changed, 142 insertions(+), 94 deletions(-) diff --git a/config/application_config.go b/config/application_config.go index 23ab7d34ac..4f747fb16a 100644 --- a/config/application_config.go +++ b/config/application_config.go @@ -17,12 +17,9 @@ package config -import ( - "github.com/creasty/defaults" -) - import ( "github.com/apache/dubbo-go/common/constant" + "github.com/creasty/defaults" ) // ApplicationConfig ... @@ -40,7 +37,7 @@ func (*ApplicationConfig) Prefix() string { return constant.DUBBO + ".application." } -// Id ... +// ID ... func (c *ApplicationConfig) Id() string { return "" } diff --git a/protocol/dubbo/dubbo_codec.go b/protocol/dubbo/dubbo_codec.go index f297d4c3d4..c233b104fd 100644 --- a/protocol/dubbo/dubbo_codec.go +++ b/protocol/dubbo/dubbo_codec.go @@ -42,10 +42,11 @@ const ( func init() { codec := &DubboCodec{} + // this is for registry dubboCodec of dubbo protocol remoting.NewCodec("dubbo", codec) } -// DubboPackage ... +// DubboPackage. this is for hessian encode/decode. If we refactor hessian, it will also be refactored. type DubboPackage struct { Header hessian.DubboHeader Service hessian.Service @@ -53,6 +54,7 @@ type DubboPackage struct { 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) } @@ -103,9 +105,11 @@ func (p *DubboPackage) Unmarshal(buf *bytes.Buffer, resp *remoting.Response) err 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) @@ -127,12 +131,13 @@ func (c *DubboCodec) EncodeRequest(request *remoting.Request) (*bytes.Buffer, er timeout, err := strconv.Atoi(invocation.AttachmentsByKey(constant.TIMEOUT_KEY, "3000")) if err != nil { - panic(err) + // it will be wrapped in readwrite.Write . + return nil, err } p.Service.Timeout = time.Duration(timeout) p.Header.SerialID = byte(S_Dubbo) - p.Header.ID = request.Id + p.Header.ID = request.ID if request.TwoWay { p.Header.Type = hessian.PackageRequest_TwoWay } else { @@ -150,10 +155,12 @@ func (c *DubboCodec) EncodeRequest(request *remoting.Request) (*bytes.Buffer, er return bytes.NewBuffer(pkg), nil } + +// 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.ID = request.ID pkg.Header.Type = hessian.PackageHeartbeat pkg.Header.SerialID = byte(S_Dubbo) @@ -166,6 +173,8 @@ func (c *DubboCodec) encodeHeartbeartReqeust(request *remoting.Request) (*bytes. return bytes.NewBuffer(byt), nil } + +// encode response func (c *DubboCodec) EncodeResponse(response *remoting.Response) (*bytes.Buffer, error) { var ptype = hessian.PackageResponse if response.IsHeartbeat() { @@ -175,7 +184,7 @@ func (c *DubboCodec) EncodeResponse(response *remoting.Response) (*bytes.Buffer, Header: hessian.DubboHeader{ SerialID: response.SerialID, Type: ptype, - ID: response.Id, + ID: response.ID, ResponseStatus: response.Status, }, } @@ -196,19 +205,21 @@ func (c *DubboCodec) EncodeResponse(response *remoting.Response) (*bytes.Buffer, 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, err + return remoting.DecodeResult{}, len, perrors.WithStack(err) } - return remoting.DecodeResult{IsRequest: true, Result: req}, len, 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, err + return remoting.DecodeResult{}, len, perrors.WithStack(err) } - return remoting.DecodeResult{IsRequest: false, Result: resp}, len, err + return remoting.DecodeResult{IsRequest: false, Result: resp}, len, perrors.WithStack(err) } } func (c *DubboCodec) isRequest(data []byte) bool { @@ -218,6 +229,7 @@ func (c *DubboCodec) isRequest(data []byte) bool { return true } +// decode request func (c *DubboCodec) decodeRequest(data []byte) (*remoting.Request, int, error) { pkg := &DubboPackage{ Body: make([]interface{}, 7), @@ -236,7 +248,7 @@ func (c *DubboCodec) decodeRequest(data []byte) (*remoting.Request, int, error) return request, 0, perrors.WithStack(err) } request = &remoting.Request{ - Id: pkg.Header.ID, + ID: pkg.Header.ID, SerialID: pkg.Header.SerialID, TwoWay: pkg.Header.Type&hessian.PackageRequest_TwoWay != 0x00, Event: pkg.Header.Type&hessian.PackageHeartbeat != 0x00, @@ -282,6 +294,7 @@ func (c *DubboCodec) decodeRequest(data []byte) (*remoting.Request, int, error) 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) @@ -289,6 +302,7 @@ func (c *DubboCodec) decodeResponse(data []byte) (*remoting.Response, int, error err := pkg.Unmarshal(buf, response) 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 } @@ -297,7 +311,7 @@ func (c *DubboCodec) decodeResponse(data []byte) (*remoting.Response, int, error return nil, 0, perrors.WithStack(err) } response = &remoting.Response{ - Id: pkg.Header.ID, + ID: pkg.Header.ID, //Version: pkg.Header., SerialID: pkg.Header.SerialID, Status: pkg.Header.ResponseStatus, diff --git a/protocol/dubbo/dubbo_invoker.go b/protocol/dubbo/dubbo_invoker.go index 1b3ac6fc80..a537f8fd9b 100644 --- a/protocol/dubbo/dubbo_invoker.go +++ b/protocol/dubbo/dubbo_invoker.go @@ -48,17 +48,19 @@ var ( attachmentKey = []string{constant.INTERFACE_KEY, constant.GROUP_KEY, constant.TOKEN_KEY, constant.TIMEOUT_KEY} ) -// DubboInvoker ... +// DubboInvoker. It is implement of protocol.Invoker. One dubboInvoker refer to one service and ip. type DubboInvoker struct { protocol.BaseInvoker + // the exchange layer, it is focus on network communication. client *remoting.ExchangeClient quitOnce sync.Once - timeout time.Duration + // timeout for service(interface) level. + timeout time.Duration // Used to record the number of requests. -1 represent this DubboInvoker is destroyed reqNum int64 } -// NewDubboInvoker ... +// NewDubboInvoker constructor func NewDubboInvoker(url common.URL, client *remoting.ExchangeClient) *DubboInvoker { requestTimeout := config.GetConsumerConfig().RequestTimeout @@ -143,6 +145,7 @@ func (di *DubboInvoker) getTimeout(invocation *invocation_impl.RPCInvocation) ti return t } } + // set timeout into invocation at method level invocation.SetAttachments(constant.TIMEOUT_KEY, strconv.Itoa(int(di.timeout.Milliseconds()))) return di.timeout } diff --git a/protocol/dubbo/dubbo_invoker_test.go b/protocol/dubbo/dubbo_invoker_test.go index ddb7b783d1..4c435d233f 100644 --- a/protocol/dubbo/dubbo_invoker_test.go +++ b/protocol/dubbo/dubbo_invoker_test.go @@ -70,7 +70,7 @@ func TestDubboInvoker_Invoke(t *testing.T) { r := response.(remoting.AsyncCallbackResponse) rst := *r.Reply.(*remoting.Response).Result.(*protocol.RPCResult) assert.Equal(t, User{Id: "1", Name: "username"}, *(rst.Rest.(*User))) - //assert.Equal(t, User{Id: "1", Name: "username"}, *r.Reply.(*Response).reply.(*User)) + //assert.Equal(t, User{ID: "1", Name: "username"}, *r.Reply.(*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 8c28456045..79607379e2 100644 --- a/protocol/dubbo/dubbo_protocol.go +++ b/protocol/dubbo/dubbo_protocol.go @@ -41,6 +41,8 @@ const ( ) var ( + // Make the connection can be shared. + // It will create one connection for one address (ip+port) exchangeClientMap *sync.Map = new(sync.Map) ) diff --git a/protocol/invocation/rpcinvocation.go b/protocol/invocation/rpcinvocation.go index e924a77da3..431e70a8c6 100644 --- a/protocol/invocation/rpcinvocation.go +++ b/protocol/invocation/rpcinvocation.go @@ -19,12 +19,10 @@ package invocation import ( "bytes" - "github.com/apache/dubbo-go/common/constant" "reflect" "sync" -) -import ( + "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/protocol" ) @@ -145,12 +143,12 @@ func (r *RPCInvocation) SetCallBack(c interface{}) { func (r *RPCInvocation) ServiceKey() string { intf := r.AttachmentsByKey(constant.INTERFACE_KEY, "") - if intf == "" { + if len(intf) == 0 { return "" } buf := &bytes.Buffer{} group := r.AttachmentsByKey(constant.GROUP_KEY, "") - if group != "" { + if len(group) != 0 { buf.WriteString(group) buf.WriteString("/") } @@ -158,7 +156,7 @@ func (r *RPCInvocation) ServiceKey() string { buf.WriteString(intf) version := r.AttachmentsByKey(constant.VERSION_KEY, "") - if version != "" && version != "0.0.0" { + if len(version) != 0 && version != "0.0.0" { buf.WriteString(":") buf.WriteString(version) } diff --git a/protocol/jsonrpc/http_test.go b/protocol/jsonrpc/http_test.go index 0cb88b36a8..8a43910524 100644 --- a/protocol/jsonrpc/http_test.go +++ b/protocol/jsonrpc/http_test.go @@ -22,19 +22,15 @@ import ( "strings" "testing" "time" -) -import ( "github.com/opentracing/opentracing-go" - perrors "github.com/pkg/errors" - "github.com/stretchr/testify/assert" -) -import ( "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/common/proxy/proxy_factory" "github.com/apache/dubbo-go/protocol" + perrors "github.com/pkg/errors" + "github.com/stretchr/testify/assert" ) type ( @@ -71,7 +67,7 @@ func TestHTTPClient_Call(t *testing.T) { // call GetUser ctx := context.WithValue(context.Background(), constant.DUBBOGO_CTX_KEY, map[string]string{ - "X-Proxy-Id": "dubbogo", + "X-Proxy-ID": "dubbogo", "X-Services": url.Path, "X-Method": "GetUser", }) @@ -85,7 +81,7 @@ func TestHTTPClient_Call(t *testing.T) { // call GetUser0 ctx = context.WithValue(context.Background(), constant.DUBBOGO_CTX_KEY, map[string]string{ - "X-Proxy-Id": "dubbogo", + "X-Proxy-ID": "dubbogo", "X-Services": url.Path, "X-Method": "GetUser0", }) @@ -98,7 +94,7 @@ func TestHTTPClient_Call(t *testing.T) { // call GetUser1 ctx = context.WithValue(context.Background(), constant.DUBBOGO_CTX_KEY, map[string]string{ - "X-Proxy-Id": "dubbogo", + "X-Proxy-ID": "dubbogo", "X-Services": url.Path, "X-Method": "GetUser1", }) @@ -110,7 +106,7 @@ func TestHTTPClient_Call(t *testing.T) { // call GetUser2 ctx = context.WithValue(context.Background(), constant.DUBBOGO_CTX_KEY, map[string]string{ - "X-Proxy-Id": "dubbogo", + "X-Proxy-ID": "dubbogo", "X-Services": url.Path, "X-Method": "GetUser2", }) @@ -122,7 +118,7 @@ func TestHTTPClient_Call(t *testing.T) { // call GetUser3 ctx = context.WithValue(context.Background(), constant.DUBBOGO_CTX_KEY, map[string]string{ - "X-Proxy-Id": "dubbogo", + "X-Proxy-ID": "dubbogo", "X-Services": url.Path, "X-Method": "GetUser3", }) @@ -134,7 +130,7 @@ func TestHTTPClient_Call(t *testing.T) { // call GetUser4 ctx = context.WithValue(context.Background(), constant.DUBBOGO_CTX_KEY, map[string]string{ - "X-Proxy-Id": "dubbogo", + "X-Proxy-ID": "dubbogo", "X-Services": url.Path, "X-Method": "GetUser4", }) @@ -145,7 +141,7 @@ func TestHTTPClient_Call(t *testing.T) { assert.Equal(t, &User{Id: "", Name: ""}, reply) ctx = context.WithValue(context.Background(), constant.DUBBOGO_CTX_KEY, map[string]string{ - "X-Proxy-Id": "dubbogo", + "X-Proxy-ID": "dubbogo", "X-Services": url.Path, "X-Method": "GetUser4", }) diff --git a/protocol/jsonrpc/jsonrpc_invoker.go b/protocol/jsonrpc/jsonrpc_invoker.go index b6e194ce0e..fd3011260d 100644 --- a/protocol/jsonrpc/jsonrpc_invoker.go +++ b/protocol/jsonrpc/jsonrpc_invoker.go @@ -19,13 +19,12 @@ package jsonrpc import ( "context" -) -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" + invocation_impl "github.com/apache/dubbo-go/protocol/invocation" ) @@ -54,7 +53,7 @@ func (ji *JsonrpcInvoker) Invoke(ctx context.Context, invocation protocol.Invoca url := ji.GetUrl() req := ji.client.NewRequest(url, inv.MethodName(), inv.Arguments()) ctxNew := context.WithValue(ctx, constant.DUBBOGO_CTX_KEY, map[string]string{ - "X-Proxy-Id": "dubbogo", + "X-Proxy-ID": "dubbogo", "X-Services": url.Path, "X-Method": inv.MethodName(), }) diff --git a/remoting/exchange.go b/remoting/exchange.go index 45babec64b..f665403907 100644 --- a/remoting/exchange.go +++ b/remoting/exchange.go @@ -24,39 +24,46 @@ import ( ) var ( + // generate request ID for global use sequence atomic.Uint64 ) func init() { + // init request ID sequence.Store(0) } func SequenceId() uint64 { + // increse 2 for every request. return sequence.Add(2) } // Request ... type Request struct { - Id int64 - Version string + ID int64 + // protocol version + Version string + // serial ID SerialID byte - Data interface{} - TwoWay bool - Event bool - broken bool + // Data + Data interface{} + TwoWay bool + Event bool + // it is used to judge the request is unbroken + // broken bool } -// NewRequest ... +// NewRequest func NewRequest(version string) *Request { return &Request{ - Id: int64(SequenceId()), + ID: int64(SequenceId()), Version: version, } } // Response ... type Response struct { - Id int64 + ID int64 Version string SerialID byte Status uint8 @@ -65,14 +72,15 @@ type Response struct { Result interface{} } -// NewResponse ... +// NewResponse func NewResponse(id int64, version string) *Response { return &Response{ - Id: id, + ID: id, Version: version, } } +// the response is heartbeat func (response *Response) IsHeartbeat() bool { return response.Event && response.Result == nil } @@ -92,6 +100,7 @@ type AsyncCallbackResponse struct { Reply interface{} } +// the client sends requst to server, there is one pendingResponse at client side to wait the response from server type PendingResponse struct { seq int64 Err error diff --git a/remoting/exchange_client.go b/remoting/exchange_client.go index cd1e3a1017..ccbd6f7a95 100644 --- a/remoting/exchange_client.go +++ b/remoting/exchange_client.go @@ -26,29 +26,39 @@ import ( ) var ( + // store requestID and response pendingResponses *sync.Map = new(sync.Map) ) type SequenceType int64 -type ExchangeClient struct { - ConnectTimeout time.Duration - address string - client Client -} - +// It is interface of client for network communication. +// If you use getty as network communication, you should define GettyClient that implements this interface. type Client interface { SetExchangeClient(client *ExchangeClient) + // responseHandler is used to deal with msg SetResponseHandler(responseHandler ResponseHandler) + // connect url Connect(url common.URL) error + // close Close() + // send request to server. Request(request *Request, timeout time.Duration, response *PendingResponse) error } +// This is abstraction level. it is like facade. +type ExchangeClient struct { + ConnectTimeout time.Duration + address string + client Client +} + +// handle the message from server type ResponseHandler interface { Handler(response *Response) } +// create ExchangeClient func NewExchangeClient(url common.URL, client Client, connectTimeout time.Duration) *ExchangeClient { exchangeClient := &ExchangeClient{ ConnectTimeout: connectTimeout, @@ -67,6 +77,7 @@ func NewExchangeClient(url common.URL, client Client, connectTimeout time.Durati return exchangeClient } +// two way request func (client *ExchangeClient) Request(invocation *protocol.Invocation, url common.URL, timeout time.Duration, result *protocol.RPCResult) error { request := NewRequest("2.0.2") @@ -74,8 +85,8 @@ func (client *ExchangeClient) Request(invocation *protocol.Invocation, url commo request.Event = false request.TwoWay = true - rsp := NewPendingResponse(request.Id) - rsp.response = NewResponse(request.Id, "2.0.2") + rsp := NewPendingResponse(request.ID) + rsp.response = NewResponse(request.ID, "2.0.2") rsp.Reply = (*invocation).Reply() AddPendingResponse(rsp) @@ -88,6 +99,7 @@ func (client *ExchangeClient) Request(invocation *protocol.Invocation, url commo return nil } +// async two way request func (client *ExchangeClient) AsyncRequest(invocation *protocol.Invocation, url common.URL, timeout time.Duration, callback common.AsyncCallback, result *protocol.RPCResult) error { request := NewRequest("2.0.2") @@ -95,8 +107,8 @@ func (client *ExchangeClient) AsyncRequest(invocation *protocol.Invocation, url request.Event = false request.TwoWay = true - rsp := NewPendingResponse(request.Id) - rsp.response = NewResponse(request.Id, "2.0.2") + rsp := NewPendingResponse(request.ID) + rsp.response = NewResponse(request.ID, "2.0.2") rsp.Callback = callback rsp.Reply = (*invocation).Reply() AddPendingResponse(rsp) @@ -110,15 +122,15 @@ func (client *ExchangeClient) AsyncRequest(invocation *protocol.Invocation, url return nil } -// oneway +// oneway request func (client *ExchangeClient) Send(invocation *protocol.Invocation, timeout time.Duration) error { request := NewRequest("2.0.2") request.Data = invocation request.Event = false request.TwoWay = false - rsp := NewPendingResponse(request.Id) - rsp.response = NewResponse(request.Id, "2.0.2") + rsp := NewPendingResponse(request.ID) + rsp.response = NewResponse(request.ID, "2.0.2") err := client.client.Request(request, timeout, rsp) if err != nil { @@ -127,13 +139,15 @@ func (client *ExchangeClient) Send(invocation *protocol.Invocation, timeout time return nil } +// close client func (client *ExchangeClient) Close() { client.client.Close() } +// handle the response from server func (client *ExchangeClient) Handler(response *Response) { - pendingResponse := removePendingResponse(SequenceType(response.Id)) + pendingResponse := removePendingResponse(SequenceType(response.ID)) if pendingResponse == nil { logger.Errorf("failed to get pending response context for response package %s", *response) return @@ -149,10 +163,12 @@ func (client *ExchangeClient) Handler(response *Response) { } } +// store response into map func AddPendingResponse(pr *PendingResponse) { pendingResponses.Store(SequenceType(pr.seq), pr) } +// get and remove response func removePendingResponse(seq SequenceType) *PendingResponse { if pendingResponses == nil { return nil @@ -164,6 +180,7 @@ func removePendingResponse(seq SequenceType) *PendingResponse { return nil } +// get response func GetPendingResponse(seq SequenceType) *PendingResponse { if presp, ok := pendingResponses.Load(seq); ok { return presp.(*PendingResponse) diff --git a/remoting/exchange_server.go b/remoting/exchange_server.go index 44f41622a2..c4538075b5 100644 --- a/remoting/exchange_server.go +++ b/remoting/exchange_server.go @@ -20,16 +20,21 @@ import ( "github.com/apache/dubbo-go/common" ) +// It is interface of server for network communication. +// If you use getty as network communication, you should define GettyServer that implements this interface. type Server interface { //invoke once for connection Start() + //it is for destroy Stop() } +// This is abstraction level. it is like facade. type ExchangeServer struct { Server Server } +// Create ExchangeServer func NewExchangeServer(url common.URL, server Server) *ExchangeServer { exchangServer := &ExchangeServer{ Server: server, @@ -37,10 +42,12 @@ func NewExchangeServer(url common.URL, server Server) *ExchangeServer { return exchangServer } +// start server func (server *ExchangeServer) Start() { server.Server.Start() } +// stop server func (server *ExchangeServer) Stop() { server.Server.Stop() } diff --git a/remoting/getty/dubbo_codec_for_test.go b/remoting/getty/dubbo_codec_for_test.go index 83f0359c38..4afb203343 100644 --- a/remoting/getty/dubbo_codec_for_test.go +++ b/remoting/getty/dubbo_codec_for_test.go @@ -16,7 +16,8 @@ */ package getty -// copy from dubbo/dubbo_codec.go +// copy from dubbo/dubbo_codec.go . +// it is used to unit test. import ( "bufio" "bytes" @@ -129,7 +130,8 @@ func (c *DubboTestCodec) EncodeRequest(request *remoting.Request) (*bytes.Buffer timeout, err := strconv.Atoi(invocation.AttachmentsByKey(constant.TIMEOUT_KEY, "3000")) if err != nil { - panic(err) + // it will be wrapped in readwrite.Write . + return nil, err } p.Service.Timeout = time.Duration(timeout) //var timeout = request.svcUrl.GetParam(strings.Join([]string{constant.METHOD_KEYS, request.method + constant.RETRIES_KEY}, "."), "") @@ -140,7 +142,7 @@ func (c *DubboTestCodec) EncodeRequest(request *remoting.Request) (*bytes.Buffer //} p.Header.SerialID = byte(S_Dubbo) - p.Header.ID = request.Id + p.Header.ID = request.ID if request.TwoWay { p.Header.Type = hessian.PackageRequest_TwoWay } else { @@ -161,7 +163,7 @@ func (c *DubboTestCodec) EncodeRequest(request *remoting.Request) (*bytes.Buffer func (c *DubboTestCodec) encodeHeartbeartReqeust(request *remoting.Request) (*bytes.Buffer, error) { pkg := &DubboPackage{} pkg.Body = []interface{}{} - pkg.Header.ID = request.Id + pkg.Header.ID = request.ID pkg.Header.Type = hessian.PackageHeartbeat pkg.Header.SerialID = byte(S_Dubbo) @@ -183,7 +185,7 @@ func (c *DubboTestCodec) EncodeResponse(response *remoting.Response) (*bytes.Buf Header: hessian.DubboHeader{ SerialID: response.SerialID, Type: ptype, - ID: response.Id, + ID: response.ID, ResponseStatus: response.Status, }, } @@ -249,7 +251,7 @@ func (c *DubboTestCodec) decodeRequest(data []byte) (*remoting.Request, int, err return request, 0, perrors.WithStack(err) } request = &remoting.Request{ - Id: pkg.Header.ID, + ID: pkg.Header.ID, SerialID: pkg.Header.SerialID, TwoWay: pkg.Header.Type&hessian.PackageRequest_TwoWay != 0x00, Event: pkg.Header.Type&hessian.PackageHeartbeat != 0x00, @@ -330,7 +332,7 @@ func (c *DubboTestCodec) decodeResponse(data []byte) (*remoting.Response, int, e return response, 0, perrors.WithStack(err) } response = &remoting.Response{ - Id: pkg.Header.ID, + ID: pkg.Header.ID, //Version: pkg.Header., SerialID: pkg.Header.SerialID, Status: pkg.Header.ResponseStatus, diff --git a/remoting/getty/getty_client.go b/remoting/getty/getty_client.go index a7a0c2c6ab..a4e3904a9a 100644 --- a/remoting/getty/getty_client.go +++ b/remoting/getty/getty_client.go @@ -44,6 +44,7 @@ var ( clientGrpool *gxsync.TaskPool ) +// it is init client for single protocol. func initClient(protocol string) { if protocol == "" { return @@ -84,7 +85,7 @@ func initClient(protocol string) { rand.Seed(time.Now().UnixNano()) } -// SetClientConf ... +// SetClientConf: config ClientConf func SetClientConf(c ClientConfig) { clientConf = &c err := clientConf.CheckValidity() @@ -102,13 +103,14 @@ func setClientGrpool() { } } -// Options ... +// Options : param config type Options struct { // connect timeout + // remove request timeout, it will be calulate for every request ConnectTimeout time.Duration } -// Client ... +// Client : some configuration for network communication. type Client struct { addr string opts Options @@ -119,7 +121,7 @@ type Client struct { ExchangeClient *remoting.ExchangeClient } -// NewClient ... +// create client func NewClient(opt Options) *Client { switch { case opt.ConnectTimeout == 0: @@ -153,6 +155,7 @@ func (c *Client) Connect(url common.URL) error { return err } +// close network connection func (c *Client) Close() { if c.pool != nil { c.pool.close() @@ -160,6 +163,7 @@ func (c *Client) Close() { c.pool = nil } +// send request func (c *Client) Request(request *remoting.Request, timeout time.Duration, response *remoting.PendingResponse) error { var ( @@ -212,7 +216,7 @@ func (c *Client) heartbeat(session getty.Session) error { req := remoting.NewRequest("2.0.2") req.TwoWay = true req.Event = true - resp := remoting.NewPendingResponse(req.Id) + resp := remoting.NewPendingResponse(req.ID) remoting.AddPendingResponse(resp) return c.transfer(session, req, 3*time.Second) } diff --git a/remoting/getty/getty_client_test.go b/remoting/getty/getty_client_test.go index 9fa46daaf3..f16c146e85 100644 --- a/remoting/getty/getty_client_test.go +++ b/remoting/getty/getty_client_test.go @@ -123,7 +123,7 @@ func testGetBigPkg(t *testing.T, c *Client) { request.Data = invocation request.Event = false request.TwoWay = true - pendingResponse := remoting.NewPendingResponse(request.Id) + pendingResponse := remoting.NewPendingResponse(request.ID) pendingResponse.Reply = user remoting.AddPendingResponse(pendingResponse) err = c.Request(request, 8*time.Second, pendingResponse) @@ -145,7 +145,7 @@ func testGetUser(t *testing.T, c *Client) { request.Data = invocation request.Event = false request.TwoWay = true - pendingResponse := remoting.NewPendingResponse(request.Id) + pendingResponse := remoting.NewPendingResponse(request.ID) pendingResponse.Reply = user remoting.AddPendingResponse(pendingResponse) err = c.Request(request, 3*time.Second, pendingResponse) @@ -167,8 +167,8 @@ func testGetUser0(t *testing.T, c *Client) { request.Data = invocation request.Event = false request.TwoWay = true - rsp := remoting.NewPendingResponse(request.Id) - rsp.SetResponse(remoting.NewResponse(request.Id, "2.0.2")) + 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) @@ -187,7 +187,7 @@ func testGetUser1(t *testing.T, c *Client) { request.Data = invocation request.Event = false request.TwoWay = true - pendingResponse := remoting.NewPendingResponse(request.Id) + pendingResponse := remoting.NewPendingResponse(request.ID) user := &User{} pendingResponse.Reply = user remoting.AddPendingResponse(pendingResponse) @@ -206,7 +206,7 @@ func testGetUser2(t *testing.T, c *Client) { request.Data = invocation request.Event = false request.TwoWay = true - pendingResponse := remoting.NewPendingResponse(request.Id) + pendingResponse := remoting.NewPendingResponse(request.ID) remoting.AddPendingResponse(pendingResponse) err = c.Request(request, 3*time.Second, pendingResponse) assert.EqualError(t, err, "error") @@ -223,7 +223,7 @@ func testGetUser3(t *testing.T, c *Client) { request.Data = invocation request.Event = false request.TwoWay = true - pendingResponse := remoting.NewPendingResponse(request.Id) + pendingResponse := remoting.NewPendingResponse(request.ID) user2 := []interface{}{} pendingResponse.Reply = &user2 remoting.AddPendingResponse(pendingResponse) @@ -242,7 +242,7 @@ func testGetUser4(t *testing.T, c *Client) { request.Data = invocation request.Event = false request.TwoWay = true - pendingResponse := remoting.NewPendingResponse(request.Id) + pendingResponse := remoting.NewPendingResponse(request.ID) user2 := []interface{}{} pendingResponse.Reply = &user2 remoting.AddPendingResponse(pendingResponse) @@ -262,7 +262,7 @@ func testGetUser5(t *testing.T, c *Client) { request.Data = invocation request.Event = false request.TwoWay = true - pendingResponse := remoting.NewPendingResponse(request.Id) + pendingResponse := remoting.NewPendingResponse(request.ID) user3 := map[interface{}]interface{}{} pendingResponse.Reply = &user3 remoting.AddPendingResponse(pendingResponse) @@ -285,7 +285,7 @@ func testGetUser6(t *testing.T, c *Client) { request.Data = invocation request.Event = false request.TwoWay = true - pendingResponse := remoting.NewPendingResponse(request.Id) + pendingResponse := remoting.NewPendingResponse(request.ID) pendingResponse.Reply = user remoting.AddPendingResponse(pendingResponse) err = c.Request(request, 3*time.Second, pendingResponse) @@ -306,7 +306,7 @@ func testGetUser61(t *testing.T, c *Client) { request.Data = invocation request.Event = false request.TwoWay = true - pendingResponse := remoting.NewPendingResponse(request.Id) + pendingResponse := remoting.NewPendingResponse(request.ID) pendingResponse.Reply = user remoting.AddPendingResponse(pendingResponse) err = c.Request(request, 3*time.Second, pendingResponse) @@ -325,8 +325,8 @@ func testClient_AsyncCall(t *testing.T, svr *Server, url common.URL, client *Cli request.Data = invocation request.Event = false request.TwoWay = true - rsp := remoting.NewPendingResponse(request.Id) - rsp.SetResponse(remoting.NewResponse(request.Id, "2.0.2")) + 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) { diff --git a/remoting/getty/listener.go b/remoting/getty/listener.go index 9d4252915d..5a9ab49999 100644 --- a/remoting/getty/listener.go +++ b/remoting/getty/listener.go @@ -101,7 +101,7 @@ func (h *RpcClientHandler) OnMessage(session getty.Session, pkg interface{}) { req := result.Result.(*remoting.Request) if req.Event { logger.Debugf("get rpc heartbeat request{%#v}", req) - resp := remoting.NewResponse(req.Id, req.Version) + resp := remoting.NewResponse(req.ID, req.Version) resp.Status = hessian.Response_OK resp.Event = req.Event resp.SerialID = req.SerialID @@ -230,7 +230,7 @@ func (h *RpcServerHandler) OnMessage(session getty.Session, pkg interface{}) { } req := decodeResult.Result.(*remoting.Request) - resp := remoting.NewResponse(req.Id, req.Version) + resp := remoting.NewResponse(req.ID, req.Version) resp.Status = hessian.Response_OK resp.Event = req.Event resp.SerialID = req.SerialID From adb55ce8c8b8756e4aaef4977bbcec8b5cef472a Mon Sep 17 00:00:00 2001 From: cvictory Date: Mon, 18 May 2020 16:10:03 +0800 Subject: [PATCH 08/44] split import sentense, fix some review --- protocol/dubbo/dubbo_codec.go | 11 +++-- protocol/dubbo/dubbo_invoker.go | 13 +++--- protocol/dubbo/dubbo_invoker_test.go | 17 ++++---- protocol/dubbo/dubbo_protocol.go | 17 +++++--- protocol/dubbo/dubbo_protocol_test.go | 7 +++- protocol/invocation/rpcinvocation.go | 27 +----------- .../protocol_filter_wrapper.go | 3 ++ remoting/codec.go | 8 +--- remoting/exchange.go | 7 +++- remoting/exchange_client.go | 41 ++++++++++++++++--- remoting/getty/dubbo_codec_for_test.go | 10 +++-- remoting/getty/getty_client.go | 38 +++++++++-------- remoting/getty/getty_client_test.go | 22 +++++----- remoting/getty/getty_server.go | 14 ++++--- remoting/getty/listener.go | 15 ++++--- remoting/getty/listener_test.go | 9 +++- remoting/getty/readwriter.go | 10 +++-- 17 files changed, 162 insertions(+), 107 deletions(-) diff --git a/protocol/dubbo/dubbo_codec.go b/protocol/dubbo/dubbo_codec.go index c233b104fd..c6ab1811e5 100644 --- a/protocol/dubbo/dubbo_codec.go +++ b/protocol/dubbo/dubbo_codec.go @@ -22,14 +22,19 @@ import ( "fmt" "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" - perrors "github.com/pkg/errors" ) //SerialID serial ID @@ -43,7 +48,7 @@ const ( func init() { codec := &DubboCodec{} // this is for registry dubboCodec of dubbo protocol - remoting.NewCodec("dubbo", codec) + remoting.RegistryCodec("dubbo", codec) } // DubboPackage. this is for hessian encode/decode. If we refactor hessian, it will also be refactored. @@ -88,7 +93,7 @@ func (p *DubboPackage) Unmarshal(buf *bytes.Buffer, resp *remoting.Response) err } if resp != nil { // for client - if p.Header.Type&hessian.PackageRequest != 0x00 { + 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) diff --git a/protocol/dubbo/dubbo_invoker.go b/protocol/dubbo/dubbo_invoker.go index a537f8fd9b..4d4cb36534 100644 --- a/protocol/dubbo/dubbo_invoker.go +++ b/protocol/dubbo/dubbo_invoker.go @@ -19,22 +19,25 @@ package dubbo import ( "context" + "github.com/apache/dubbo-go/config" + "github.com/apache/dubbo-go/remoting" "strconv" "strings" "sync" "sync/atomic" "time" +) - "github.com/apache/dubbo-go/config" - "github.com/apache/dubbo-go/remoting" +import ( "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" - perrors "github.com/pkg/errors" - invocation_impl "github.com/apache/dubbo-go/protocol/invocation" ) @@ -117,7 +120,7 @@ func (di *DubboInvoker) Invoke(ctx context.Context, invocation protocol.Invocati //result.Err = di.client.AsyncCall(NewRequest(url.Location, url, inv.MethodName(), inv.Arguments(), inv.Attachments()), callBack, response) result.Err = di.client.AsyncRequest(&invocation, url, timeout, callBack, rest) } else { - result.Err = di.client.Send(&invocation, timeout) + result.Err = di.client.Send(&invocation, url, timeout) } } else { if inv.Reply() == nil { diff --git a/protocol/dubbo/dubbo_invoker_test.go b/protocol/dubbo/dubbo_invoker_test.go index 4c435d233f..d363eb39a9 100644 --- a/protocol/dubbo/dubbo_invoker_test.go +++ b/protocol/dubbo/dubbo_invoker_test.go @@ -23,20 +23,23 @@ import ( "sync" "testing" "time" +) - "github.com/apache/dubbo-go/remoting" - - "github.com/apache/dubbo-go/remoting/getty" - +import ( hessian "github.com/apache/dubbo-go-hessian2" + "github.com/opentracing/opentracing-go" + perrors "github.com/pkg/errors" + "github.com/stretchr/testify/assert" +) + +import ( "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/common/proxy/proxy_factory" "github.com/apache/dubbo-go/protocol" "github.com/apache/dubbo-go/protocol/invocation" - "github.com/opentracing/opentracing-go" - perrors "github.com/pkg/errors" - "github.com/stretchr/testify/assert" + "github.com/apache/dubbo-go/remoting" + "github.com/apache/dubbo-go/remoting/getty" ) func TestDubboInvoker_Invoke(t *testing.T) { diff --git a/protocol/dubbo/dubbo_protocol.go b/protocol/dubbo/dubbo_protocol.go index 79607379e2..09a23bfdd4 100644 --- a/protocol/dubbo/dubbo_protocol.go +++ b/protocol/dubbo/dubbo_protocol.go @@ -21,7 +21,13 @@ import ( "context" "fmt" "sync" +) +import ( + "github.com/opentracing/opentracing-go" +) + +import ( "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/common/extension" @@ -31,7 +37,6 @@ import ( "github.com/apache/dubbo-go/protocol/invocation" "github.com/apache/dubbo-go/remoting" "github.com/apache/dubbo-go/remoting/getty" - "github.com/opentracing/opentracing-go" ) // dubbo protocol constant @@ -138,10 +143,10 @@ func GetProtocol() protocol.Protocol { } func doHandleRequest(rpcInvocation *invocation.RPCInvocation) protocol.RPCResult { - exporter, _ := dubboProtocol.ExporterMap().Load(rpcInvocation.ServiceKey()) + exporter, _ := dubboProtocol.ExporterMap().Load(rpcInvocation.Invoker().GetUrl().ServiceKey()) result := protocol.RPCResult{} if exporter == nil { - err := fmt.Errorf("don't have this exporter, key: %s", rpcInvocation.ServiceKey()) + err := fmt.Errorf("don't have this exporter, key: %s", rpcInvocation.Invoker().GetUrl().ServiceKey()) logger.Errorf(err.Error()) result.Err = err //reply(session, p, hessian.PackageResponse) @@ -163,7 +168,7 @@ func doHandleRequest(rpcInvocation *invocation.RPCInvocation) protocol.RPCResult //p.Body = hessian.NewResponse(res, nil, result.Attachments()) } } else { - result.Err = fmt.Errorf("don't have the invoker, key: %s", rpcInvocation.ServiceKey()) + result.Err = fmt.Errorf("don't have the invoker, key: %s", rpcInvocation.Invoker().GetUrl().ServiceKey()) } return result } @@ -173,7 +178,7 @@ func getExchangeClient(url common.URL) *remoting.ExchangeClient { if !ok { exchangeClientTmp := remoting.NewExchangeClient(url, getty.NewClient(getty.Options{ ConnectTimeout: config.GetConsumerConfig().ConnectTimeout, - }), config.GetConsumerConfig().ConnectTimeout) + }), config.GetConsumerConfig().ConnectTimeout, false) if exchangeClientTmp != nil { exchangeClientMap.Store(url.Location, exchangeClientTmp) } @@ -184,7 +189,7 @@ func getExchangeClient(url common.URL) *remoting.ExchangeClient { if !ok { exchangeClientTmp := remoting.NewExchangeClient(url, getty.NewClient(getty.Options{ ConnectTimeout: config.GetConsumerConfig().ConnectTimeout, - }), config.GetConsumerConfig().ConnectTimeout) + }), config.GetConsumerConfig().ConnectTimeout, false) if exchangeClientTmp != nil { exchangeClientMap.Store(url.Location, exchangeClientTmp) } diff --git a/protocol/dubbo/dubbo_protocol_test.go b/protocol/dubbo/dubbo_protocol_test.go index b2894ed7fa..55ab0fe455 100644 --- a/protocol/dubbo/dubbo_protocol_test.go +++ b/protocol/dubbo/dubbo_protocol_test.go @@ -19,12 +19,17 @@ package dubbo import ( "testing" +) +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/remoting/getty" - "github.com/stretchr/testify/assert" ) func init() { diff --git a/protocol/invocation/rpcinvocation.go b/protocol/invocation/rpcinvocation.go index 431e70a8c6..b207fd0b0c 100644 --- a/protocol/invocation/rpcinvocation.go +++ b/protocol/invocation/rpcinvocation.go @@ -18,11 +18,11 @@ package invocation import ( - "bytes" "reflect" "sync" +) - "github.com/apache/dubbo-go/common/constant" +import ( "github.com/apache/dubbo-go/protocol" ) @@ -141,29 +141,6 @@ func (r *RPCInvocation) SetCallBack(c interface{}) { r.callBack = c } -func (r *RPCInvocation) ServiceKey() string { - intf := r.AttachmentsByKey(constant.INTERFACE_KEY, "") - if len(intf) == 0 { - return "" - } - buf := &bytes.Buffer{} - group := r.AttachmentsByKey(constant.GROUP_KEY, "") - if len(group) != 0 { - buf.WriteString(group) - buf.WriteString("/") - } - - buf.WriteString(intf) - - version := r.AttachmentsByKey(constant.VERSION_KEY, "") - if len(version) != 0 && version != "0.0.0" { - buf.WriteString(":") - buf.WriteString(version) - } - - return buf.String() -} - // ///////////////////////// // option // ///////////////////////// diff --git a/protocol/protocolwrapper/protocol_filter_wrapper.go b/protocol/protocolwrapper/protocol_filter_wrapper.go index 70d2da0fae..3fee0f41a9 100644 --- a/protocol/protocolwrapper/protocol_filter_wrapper.go +++ b/protocol/protocolwrapper/protocol_filter_wrapper.go @@ -68,6 +68,9 @@ func (pfw *ProtocolFilterWrapper) Destroy() { } func buildInvokerChain(invoker protocol.Invoker, key string) protocol.Invoker { + if invoker == nil { + return nil + } filtName := invoker.GetUrl().GetParam(key, "") if filtName == "" { return invoker diff --git a/remoting/codec.go b/remoting/codec.go index 7ca7552554..607d1643cc 100644 --- a/remoting/codec.go +++ b/remoting/codec.go @@ -33,14 +33,10 @@ type DecodeResult struct { } var ( - codec map[string]Codec -) - -func init() { codec = make(map[string]Codec, 2) -} +) -func NewCodec(protocol string, codecTmp Codec) { +func RegistryCodec(protocol string, codecTmp Codec) { codec[protocol] = codecTmp } diff --git a/remoting/exchange.go b/remoting/exchange.go index f665403907..e85e2c8ad1 100644 --- a/remoting/exchange.go +++ b/remoting/exchange.go @@ -18,11 +18,16 @@ package remoting import ( "time" +) - "github.com/apache/dubbo-go/common" +import ( "go.uber.org/atomic" ) +import ( + "github.com/apache/dubbo-go/common" +) + var ( // generate request ID for global use sequence atomic.Uint64 diff --git a/remoting/exchange_client.go b/remoting/exchange_client.go index ccbd6f7a95..7ef8e54a2f 100644 --- a/remoting/exchange_client.go +++ b/remoting/exchange_client.go @@ -17,9 +17,12 @@ package remoting import ( + "errors" "sync" "time" +) +import ( "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common/logger" "github.com/apache/dubbo-go/protocol" @@ -51,6 +54,7 @@ type ExchangeClient struct { ConnectTimeout time.Duration address string client Client + init bool } // handle the message from server @@ -59,27 +63,46 @@ type ResponseHandler interface { } // create ExchangeClient -func NewExchangeClient(url common.URL, client Client, connectTimeout time.Duration) *ExchangeClient { +func NewExchangeClient(url common.URL, client Client, connectTimeout time.Duration, lazyInit bool) *ExchangeClient { exchangeClient := &ExchangeClient{ ConnectTimeout: connectTimeout, address: url.Location, client: client, } client.SetExchangeClient(exchangeClient) - if client.Connect(url) != nil { - //retry for a while - time.Sleep(1 * time.Second) - if client.Connect(url) != nil { + if !lazyInit { + if err := exchangeClient.doInit(url); err != nil { return nil } } + client.SetResponseHandler(exchangeClient) return exchangeClient } +func (cl *ExchangeClient) doInit(url common.URL) error { + if cl.init { + return nil + } + if cl.client.Connect(url) != nil { + //retry for a while + time.Sleep(100 * time.Millisecond) + if cl.client.Connect(url) != nil { + logger.Errorf("Failed to connect server %+v " + url.Location) + return errors.New("Failed to connect server " + url.Location) + } + } + //FIXME atomic operation + cl.init = true + return nil +} + // two way request func (client *ExchangeClient) Request(invocation *protocol.Invocation, url common.URL, timeout time.Duration, result *protocol.RPCResult) error { + if er := client.doInit(url); er != nil { + return er + } request := NewRequest("2.0.2") request.Data = invocation request.Event = false @@ -102,6 +125,9 @@ func (client *ExchangeClient) Request(invocation *protocol.Invocation, url commo // async two way request func (client *ExchangeClient) AsyncRequest(invocation *protocol.Invocation, url common.URL, timeout time.Duration, callback common.AsyncCallback, result *protocol.RPCResult) error { + if er := client.doInit(url); er != nil { + return er + } request := NewRequest("2.0.2") request.Data = invocation request.Event = false @@ -123,7 +149,10 @@ func (client *ExchangeClient) AsyncRequest(invocation *protocol.Invocation, url } // oneway request -func (client *ExchangeClient) Send(invocation *protocol.Invocation, timeout time.Duration) error { +func (client *ExchangeClient) Send(invocation *protocol.Invocation, url common.URL, timeout time.Duration) error { + if er := client.doInit(url); er != nil { + return er + } request := NewRequest("2.0.2") request.Data = invocation request.Event = false diff --git a/remoting/getty/dubbo_codec_for_test.go b/remoting/getty/dubbo_codec_for_test.go index 4afb203343..bb87323d54 100644 --- a/remoting/getty/dubbo_codec_for_test.go +++ b/remoting/getty/dubbo_codec_for_test.go @@ -24,14 +24,18 @@ import ( "fmt" "strconv" "time" +) +import ( + perrors "github.com/pkg/errors" +) +import ( hessian "github.com/apache/dubbo-go-hessian2" "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" - perrors "github.com/pkg/errors" ) //SerialID serial ID @@ -44,7 +48,7 @@ const ( func init() { codec := &DubboTestCodec{} - remoting.NewCodec("dubbo", codec) + remoting.RegistryCodec("dubbo", codec) } // DubboPackage ... @@ -88,7 +92,7 @@ func (p *DubboPackage) Unmarshal(buf *bytes.Buffer, resp *remoting.Response) err } if resp != nil { // for client - if p.Header.Type&hessian.PackageRequest != 0x00 { + 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) diff --git a/remoting/getty/getty_client.go b/remoting/getty/getty_client.go index a4e3904a9a..f264652a40 100644 --- a/remoting/getty/getty_client.go +++ b/remoting/getty/getty_client.go @@ -20,17 +20,20 @@ package getty import ( "math/rand" "time" +) - "github.com/apache/dubbo-go/remoting" +import ( "github.com/dubbogo/getty" - "gopkg.in/yaml.v2" - gxsync "github.com/dubbogo/gost/sync" + perrors "github.com/pkg/errors" + "gopkg.in/yaml.v2" +) +import ( "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common/logger" "github.com/apache/dubbo-go/config" - perrors "github.com/pkg/errors" + "github.com/apache/dubbo-go/remoting" ) var ( @@ -166,25 +169,26 @@ func (c *Client) Close() { // send request func (c *Client) Request(request *remoting.Request, timeout time.Duration, response *remoting.PendingResponse) error { - var ( - err error - session getty.Session - conn *gettyRPCClient - ) - conn, session, err = c.selectSession(c.addr) + //var ( + // err error + // session getty.Session + // conn *gettyRPCClient + //) + conn, session, err := c.selectSession(c.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() - }() + // FIXME remove temporarily + //defer func() { + // if err == nil { + // c.pool.put(conn) + // return + // } + // conn.close() + //}() if err = c.transfer(session, request, timeout); err != nil { return perrors.WithStack(err) diff --git a/remoting/getty/getty_client_test.go b/remoting/getty/getty_client_test.go index f16c146e85..516051d057 100644 --- a/remoting/getty/getty_client_test.go +++ b/remoting/getty/getty_client_test.go @@ -24,22 +24,22 @@ import ( "sync" "testing" "time" +) - "github.com/apache/dubbo-go/common/proxy/proxy_factory" - - "github.com/apache/dubbo-go/config" - - "github.com/apache/dubbo-go/remoting" - - "github.com/apache/dubbo-go/protocol/invocation" - +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" - perrors "github.com/pkg/errors" - "github.com/stretchr/testify/assert" + "github.com/apache/dubbo-go/protocol/invocation" + "github.com/apache/dubbo-go/remoting" ) func TestRunSuite(t *testing.T) { @@ -345,7 +345,7 @@ func testClient_AsyncCall(t *testing.T, svr *Server, url common.URL, client *Cli func InitTest(t *testing.T) (*Server, common.URL) { hessian.RegisterPOJO(&User{}) - remoting.NewCodec("dubbo", &DubboTestCodec{}) + remoting.RegistryCodec("dubbo", &DubboTestCodec{}) methods, err := common.ServiceMap.Register("dubbo", &UserProvider{}) assert.NoError(t, err) diff --git a/remoting/getty/getty_server.go b/remoting/getty/getty_server.go index 874d0401f5..6dc15d251f 100644 --- a/remoting/getty/getty_server.go +++ b/remoting/getty/getty_server.go @@ -20,17 +20,21 @@ package getty import ( "fmt" "net" +) - "github.com/apache/dubbo-go/protocol" - "github.com/apache/dubbo-go/protocol/invocation" - "github.com/apache/dubbo-go/remoting" +import ( "github.com/dubbogo/getty" + gxsync "github.com/dubbogo/gost/sync" + "gopkg.in/yaml.v2" +) +import ( "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common/logger" "github.com/apache/dubbo-go/config" - gxsync "github.com/dubbogo/gost/sync" - "gopkg.in/yaml.v2" + "github.com/apache/dubbo-go/protocol" + "github.com/apache/dubbo-go/protocol/invocation" + "github.com/apache/dubbo-go/remoting" ) var ( diff --git a/remoting/getty/listener.go b/remoting/getty/listener.go index 5a9ab49999..777e14c2ec 100644 --- a/remoting/getty/listener.go +++ b/remoting/getty/listener.go @@ -22,18 +22,21 @@ import ( "sync" "sync/atomic" "time" +) - "github.com/apache/dubbo-go/common/constant" - - "github.com/apache/dubbo-go/remoting" - +import ( hessian "github.com/apache/dubbo-go-hessian2" - "github.com/apache/dubbo-go/common/logger" - "github.com/apache/dubbo-go/protocol/invocation" "github.com/dubbogo/getty" perrors "github.com/pkg/errors" ) +import ( + "github.com/apache/dubbo-go/common/constant" + "github.com/apache/dubbo-go/common/logger" + "github.com/apache/dubbo-go/protocol/invocation" + "github.com/apache/dubbo-go/remoting" +) + // todo: WritePkg_Timeout will entry *.yml const ( // WritePkg_Timeout ... diff --git a/remoting/getty/listener_test.go b/remoting/getty/listener_test.go index 49e13d39c9..3bb659c231 100644 --- a/remoting/getty/listener_test.go +++ b/remoting/getty/listener_test.go @@ -20,14 +20,19 @@ package getty import ( "context" "testing" +) - "github.com/apache/dubbo-go/common/constant" - "github.com/apache/dubbo-go/protocol/invocation" +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" +) + // test rebuild the ctx func TestRebuildCtx(t *testing.T) { opentracing.SetGlobalTracer(mocktracer.New()) diff --git a/remoting/getty/readwriter.go b/remoting/getty/readwriter.go index 1aed516a3a..8842f1b8dc 100644 --- a/remoting/getty/readwriter.go +++ b/remoting/getty/readwriter.go @@ -19,16 +19,20 @@ package getty import ( "reflect" +) - "github.com/apache/dubbo-go/remoting" - +import ( hessian "github.com/apache/dubbo-go-hessian2" "github.com/dubbogo/getty" - "github.com/apache/dubbo-go/common/logger" perrors "github.com/pkg/errors" ) +import ( + "github.com/apache/dubbo-go/common/logger" + "github.com/apache/dubbo-go/remoting" +) + //////////////////////////////////////////// // RpcClientPackageHandler //////////////////////////////////////////// From 1bbe0b492ef490d1c1c9166678507576a3a33aa0 Mon Sep 17 00:00:00 2001 From: cvictory Date: Mon, 18 May 2020 17:20:39 +0800 Subject: [PATCH 09/44] modify log --- remoting/exchange.go | 2 +- remoting/getty/getty_client.go | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/remoting/exchange.go b/remoting/exchange.go index e85e2c8ad1..892bd82013 100644 --- a/remoting/exchange.go +++ b/remoting/exchange.go @@ -48,7 +48,7 @@ type Request struct { ID int64 // protocol version Version string - // serial ID + // serial ID (ignore) SerialID byte // Data Data interface{} diff --git a/remoting/getty/getty_client.go b/remoting/getty/getty_client.go index f264652a40..aea1bceeb8 100644 --- a/remoting/getty/getty_client.go +++ b/remoting/getty/getty_client.go @@ -154,7 +154,9 @@ func (c *Client) Connect(url common.URL) error { c.codec = remoting.GetCodec(url.Protocol) c.addr = url.Location _, _, err := c.selectSession(c.addr) - logger.Errorf("try to connect server %v failed for : %v", url.Location, err) + if err != nil { + logger.Errorf("try to connect server %v failed for : %v", url.Location, err) + } return err } @@ -174,7 +176,7 @@ func (c *Client) Request(request *remoting.Request, timeout time.Duration, respo // session getty.Session // conn *gettyRPCClient //) - conn, session, err := c.selectSession(c.addr) + _, session, err := c.selectSession(c.addr) if err != nil { return perrors.WithStack(err) } From dc2b68f2c3d35b4c687ddf2b600a62b4412c72bd Mon Sep 17 00:00:00 2001 From: cvictory Date: Tue, 19 May 2020 10:41:53 +0800 Subject: [PATCH 10/44] fix run issue --- protocol/dubbo/dubbo_invoker.go | 3 +++ protocol/dubbo/dubbo_protocol.go | 6 +++--- protocol/invocation/rpcinvocation.go | 25 +++++++++++++++++++++++++ 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/protocol/dubbo/dubbo_invoker.go b/protocol/dubbo/dubbo_invoker.go index 4d4cb36534..14197f1cd0 100644 --- a/protocol/dubbo/dubbo_invoker.go +++ b/protocol/dubbo/dubbo_invoker.go @@ -96,6 +96,9 @@ func (di *DubboInvoker) Invoke(ctx context.Context, invocation protocol.Invocati defer atomic.AddInt64(&(di.reqNum), -1) inv := invocation.(*invocation_impl.RPCInvocation) + // init param + inv.SetAttachments(constant.PATH_KEY, di.GetUrl().Path) + inv.SetAttachments(constant.VERSION_KEY, di.GetUrl().GetParam(constant.VERSION_KEY, "")) for _, k := range attachmentKey { if v := di.GetUrl().GetParam(k, ""); len(v) > 0 { inv.SetAttachments(k, v) diff --git a/protocol/dubbo/dubbo_protocol.go b/protocol/dubbo/dubbo_protocol.go index 09a23bfdd4..4bff575b33 100644 --- a/protocol/dubbo/dubbo_protocol.go +++ b/protocol/dubbo/dubbo_protocol.go @@ -143,10 +143,10 @@ func GetProtocol() protocol.Protocol { } func doHandleRequest(rpcInvocation *invocation.RPCInvocation) protocol.RPCResult { - exporter, _ := dubboProtocol.ExporterMap().Load(rpcInvocation.Invoker().GetUrl().ServiceKey()) + exporter, _ := dubboProtocol.ExporterMap().Load(rpcInvocation.ServiceKey()) result := protocol.RPCResult{} if exporter == nil { - err := fmt.Errorf("don't have this exporter, key: %s", rpcInvocation.Invoker().GetUrl().ServiceKey()) + err := fmt.Errorf("don't have this exporter, key: %s", rpcInvocation.ServiceKey()) logger.Errorf(err.Error()) result.Err = err //reply(session, p, hessian.PackageResponse) @@ -168,7 +168,7 @@ func doHandleRequest(rpcInvocation *invocation.RPCInvocation) protocol.RPCResult //p.Body = hessian.NewResponse(res, nil, result.Attachments()) } } else { - result.Err = fmt.Errorf("don't have the invoker, key: %s", rpcInvocation.Invoker().GetUrl().ServiceKey()) + result.Err = fmt.Errorf("don't have the invoker, key: %s", rpcInvocation.ServiceKey()) } return result } diff --git a/protocol/invocation/rpcinvocation.go b/protocol/invocation/rpcinvocation.go index b207fd0b0c..2494c39da2 100644 --- a/protocol/invocation/rpcinvocation.go +++ b/protocol/invocation/rpcinvocation.go @@ -18,11 +18,13 @@ package invocation import ( + "bytes" "reflect" "sync" ) import ( + "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/protocol" ) @@ -141,6 +143,29 @@ func (r *RPCInvocation) SetCallBack(c interface{}) { r.callBack = c } +func (r *RPCInvocation) ServiceKey() string { + intf := r.AttachmentsByKey(constant.INTERFACE_KEY, "") + if len(intf) == 0 { + return "" + } + buf := &bytes.Buffer{} + group := r.AttachmentsByKey(constant.GROUP_KEY, "") + if len(group) != 0 { + buf.WriteString(group) + buf.WriteString("/") + } + + buf.WriteString(intf) + + version := r.AttachmentsByKey(constant.VERSION_KEY, "") + if len(version) != 0 && version != "0.0.0" { + buf.WriteString(":") + buf.WriteString(version) + } + + return buf.String() +} + // ///////////////////////// // option // ///////////////////////// From fe3c5b04af4d54379196b578044558b5016af7a3 Mon Sep 17 00:00:00 2001 From: cvictory Date: Tue, 19 May 2020 15:25:20 +0800 Subject: [PATCH 11/44] fix issue: create gettyClientConn every time --- remoting/getty/pool.go | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/remoting/getty/pool.go b/remoting/getty/pool.go index aaa3bbd70d..fdefa23820 100644 --- a/remoting/getty/pool.go +++ b/remoting/getty/pool.go @@ -98,7 +98,6 @@ func (c *gettyRPCClient) newSession(session getty.Session) error { tcpConn *net.TCPConn conf ClientConfig ) - conf = c.pool.rpcClient.conf if conf.GettySessionParam.CompressEncoding { session.SetCompressType(getty.CompressZip) @@ -301,7 +300,8 @@ func newGettyRPCClientConnPool(rpcClient *Client, size int, ttl time.Duration) * rpcClient: rpcClient, size: size, ttl: int64(ttl.Seconds()), - conns: make([]*gettyRPCClient, 0, 16), + // init capacity : 2 + conns: make([]*gettyRPCClient, 0, 2), } } @@ -320,6 +320,9 @@ func (p *gettyRPCClientPool) getGettyRpcClient(addr string) (*gettyRPCClient, er if err == nil && conn == nil { // create new conn rpcClientConn, err := newGettyRPCClientConn(p, addr) + if err == nil { + p.put(rpcClientConn) + } return rpcClientConn, perrors.WithStack(err) } return conn, perrors.WithStack(err) @@ -333,10 +336,15 @@ func (p *gettyRPCClientPool) get() (*gettyRPCClient, error) { if p.conns == nil { return nil, errClientPoolClosed } - - for len(p.conns) > 0 { - conn := p.conns[len(p.conns)-1] - p.conns = p.conns[:len(p.conns)-1] + for num := len(p.conns); num > 0; { + var conn *gettyRPCClient + if num != 1 { + conn = p.conns[rand.Int31n(int32(num))] + } else { + conn = p.conns[0] + } + // This will recreate gettyRpcClient for remove last position + //p.conns = p.conns[:len(p.conns)-1] if d := now - conn.getActive(); d > p.ttl { p.remove(conn) @@ -353,21 +361,17 @@ func (p *gettyRPCClientPool) put(conn *gettyRPCClient) { if conn == nil || conn.getActive() == 0 { return } - p.Lock() defer p.Unlock() - if p.conns == nil { return } - // check whether @conn has existed in p.conns or not. for i := range p.conns { if p.conns[i] == conn { return } } - if len(p.conns) >= p.size { // delete @conn from client pool // p.remove(conn) From 1e0795244c27862e5421888d8e64fe7cee617370 Mon Sep 17 00:00:00 2001 From: cvictory Date: Tue, 19 May 2020 20:53:21 +0800 Subject: [PATCH 12/44] fix : url.getPath contains slash, but it don't in dubbo-java --- protocol/dubbo/dubbo_invoker.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocol/dubbo/dubbo_invoker.go b/protocol/dubbo/dubbo_invoker.go index 14197f1cd0..b91b120233 100644 --- a/protocol/dubbo/dubbo_invoker.go +++ b/protocol/dubbo/dubbo_invoker.go @@ -97,7 +97,7 @@ func (di *DubboInvoker) Invoke(ctx context.Context, invocation protocol.Invocati inv := invocation.(*invocation_impl.RPCInvocation) // init param - inv.SetAttachments(constant.PATH_KEY, di.GetUrl().Path) + inv.SetAttachments(constant.PATH_KEY, di.GetUrl().GetParam(constant.INTERFACE_KEY, "")) inv.SetAttachments(constant.VERSION_KEY, di.GetUrl().GetParam(constant.VERSION_KEY, "")) for _, k := range attachmentKey { if v := di.GetUrl().GetParam(k, ""); len(v) > 0 { From c6abc0d10b267bcc5f70095592d1a086cb55ea12 Mon Sep 17 00:00:00 2001 From: cvictory Date: Wed, 20 May 2020 10:49:31 +0800 Subject: [PATCH 13/44] unit test --- remoting/getty/getty_client_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/remoting/getty/getty_client_test.go b/remoting/getty/getty_client_test.go index 516051d057..3dcc6b30ea 100644 --- a/remoting/getty/getty_client_test.go +++ b/remoting/getty/getty_client_test.go @@ -86,7 +86,7 @@ func getClient(url common.URL) *Client { ConnectTimeout: config.GetConsumerConfig().ConnectTimeout, }) - exchangeClient := remoting.NewExchangeClient(url, client, 5*time.Second) + exchangeClient := remoting.NewExchangeClient(url, client, 5*time.Second, false) client.SetExchangeClient(exchangeClient) client.Connect(url) client.SetResponseHandler(exchangeClient) From 15608af48a68d06747daef78fd49a2fbef95b5bb Mon Sep 17 00:00:00 2001 From: cvictory Date: Wed, 27 May 2020 14:38:25 +0800 Subject: [PATCH 14/44] fix review issue --- config/application_config.go | 1 + protocol/dubbo/dubbo_invoker.go | 6 +++--- protocol/dubbo/dubbo_invoker_test.go | 3 --- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/config/application_config.go b/config/application_config.go index 4f747fb16a..c4c26bf8de 100644 --- a/config/application_config.go +++ b/config/application_config.go @@ -19,6 +19,7 @@ package config import ( "github.com/apache/dubbo-go/common/constant" + "github.com/creasty/defaults" ) diff --git a/protocol/dubbo/dubbo_invoker.go b/protocol/dubbo/dubbo_invoker.go index b91b120233..9c7473f35f 100644 --- a/protocol/dubbo/dubbo_invoker.go +++ b/protocol/dubbo/dubbo_invoker.go @@ -19,8 +19,6 @@ package dubbo import ( "context" - "github.com/apache/dubbo-go/config" - "github.com/apache/dubbo-go/remoting" "strconv" "strings" "sync" @@ -37,8 +35,10 @@ import ( "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/common/logger" + "github.com/apache/dubbo-go/config" "github.com/apache/dubbo-go/protocol" invocation_impl "github.com/apache/dubbo-go/protocol/invocation" + "github.com/apache/dubbo-go/remoting" ) var ( @@ -51,7 +51,7 @@ var ( attachmentKey = []string{constant.INTERFACE_KEY, constant.GROUP_KEY, constant.TOKEN_KEY, constant.TIMEOUT_KEY} ) -// DubboInvoker. It is implement of protocol.Invoker. One dubboInvoker refer to one service and ip. +// DubboInvoker is implement of protocol.Invoker. A dubboInvoker refer to one service and ip. type DubboInvoker struct { protocol.BaseInvoker // the exchange layer, it is focus on network communication. diff --git a/protocol/dubbo/dubbo_invoker_test.go b/protocol/dubbo/dubbo_invoker_test.go index d363eb39a9..6abd32d189 100644 --- a/protocol/dubbo/dubbo_invoker_test.go +++ b/protocol/dubbo/dubbo_invoker_test.go @@ -57,9 +57,6 @@ func TestDubboInvoker_Invoke(t *testing.T) { res := invoker.Invoke(context.Background(), inv) assert.NoError(t, res.Error()) assert.Equal(t, User{Id: "1", Name: "username"}, *res.Result().(*User)) - //result will not contain attachment - // attachment in result it is useless. - //assert.Equal(t, "test_value", res.Attachments()["test_key"]) // test attachments for request/response // CallOneway inv.SetAttachments(constant.ASYNC_KEY, "true") From cbac0c9902912001caf4e0e7ac66dc3a4f25f28c Mon Sep 17 00:00:00 2001 From: cvictory Date: Wed, 27 May 2020 15:19:13 +0800 Subject: [PATCH 15/44] remove unused code --- remoting/getty/getty_client.go | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/remoting/getty/getty_client.go b/remoting/getty/getty_client.go index aea1bceeb8..153a062673 100644 --- a/remoting/getty/getty_client.go +++ b/remoting/getty/getty_client.go @@ -170,12 +170,6 @@ func (c *Client) Close() { // send request func (c *Client) Request(request *remoting.Request, timeout time.Duration, response *remoting.PendingResponse) error { - - //var ( - // err error - // session getty.Session - // conn *gettyRPCClient - //) _, session, err := c.selectSession(c.addr) if err != nil { return perrors.WithStack(err) @@ -183,14 +177,6 @@ func (c *Client) Request(request *remoting.Request, timeout time.Duration, respo if session == nil { return errSessionNotExist } - // FIXME remove temporarily - //defer func() { - // if err == nil { - // c.pool.put(conn) - // return - // } - // conn.close() - //}() if err = c.transfer(session, request, timeout); err != nil { return perrors.WithStack(err) From 7f9c7a47d6d37699d1035233d8f303a4e3d3b264 Mon Sep 17 00:00:00 2001 From: cvictory Date: Thu, 28 May 2020 19:06:13 +0800 Subject: [PATCH 16/44] reactor import --- config/application_config.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/config/application_config.go b/config/application_config.go index c4c26bf8de..9c6f25be25 100644 --- a/config/application_config.go +++ b/config/application_config.go @@ -18,11 +18,13 @@ package config import ( - "github.com/apache/dubbo-go/common/constant" - "github.com/creasty/defaults" ) +import ( + "github.com/apache/dubbo-go/common/constant" +) + // ApplicationConfig ... type ApplicationConfig struct { Organization string `yaml:"organization" json:"organization,omitempty" property:"organization"` From 7c30fdcf1a88efbbef8c24a13e7578794712aacb Mon Sep 17 00:00:00 2001 From: cvictory Date: Fri, 29 May 2020 16:53:10 +0800 Subject: [PATCH 17/44] fix review issue --- config/application_config.go | 2 +- protocol/dubbo/dubbo_codec.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config/application_config.go b/config/application_config.go index 9c6f25be25..1d9306c5a2 100644 --- a/config/application_config.go +++ b/config/application_config.go @@ -40,7 +40,7 @@ func (*ApplicationConfig) Prefix() string { return constant.DUBBO + ".application." } -// ID ... +// nolint func (c *ApplicationConfig) Id() string { return "" } diff --git a/protocol/dubbo/dubbo_codec.go b/protocol/dubbo/dubbo_codec.go index c6ab1811e5..0134aab4ad 100644 --- a/protocol/dubbo/dubbo_codec.go +++ b/protocol/dubbo/dubbo_codec.go @@ -64,7 +64,7 @@ func (p DubboPackage) String() string { return fmt.Sprintf("DubboPackage: Header-%v, Path-%v, Body-%v", p.Header, p.Service, p.Body) } -// Marshal ... +// nolint func (p *DubboPackage) Marshal() (*bytes.Buffer, error) { codec := hessian.NewHessianCodec(nil) @@ -76,7 +76,7 @@ func (p *DubboPackage) Marshal() (*bytes.Buffer, error) { return bytes.NewBuffer(pkg), nil } -// Unmarshal ... +// 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() From dd36c9ced1616f4e462b9708a423bae44609725b Mon Sep 17 00:00:00 2001 From: cvictory Date: Sun, 31 May 2020 22:40:00 +0800 Subject: [PATCH 18/44] fix review issue --- protocol/dubbo/dubbo_codec.go | 3 ++- protocol/dubbo/dubbo_protocol.go | 12 +++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/protocol/dubbo/dubbo_codec.go b/protocol/dubbo/dubbo_codec.go index 0134aab4ad..707b9984b6 100644 --- a/protocol/dubbo/dubbo_codec.go +++ b/protocol/dubbo/dubbo_codec.go @@ -51,7 +51,7 @@ func init() { remoting.RegistryCodec("dubbo", codec) } -// DubboPackage. this is for hessian encode/decode. If we refactor hessian, it will also be refactored. +// DubboPackage is for hessian encode/decode. If we refactor hessian, it will also be refactored. type DubboPackage struct { Header hessian.DubboHeader Service hessian.Service @@ -227,6 +227,7 @@ func (c *DubboCodec) Decode(data []byte) (remoting.DecodeResult, int, error) { 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 diff --git a/protocol/dubbo/dubbo_protocol.go b/protocol/dubbo/dubbo_protocol.go index 4bff575b33..d1b2725541 100644 --- a/protocol/dubbo/dubbo_protocol.go +++ b/protocol/dubbo/dubbo_protocol.go @@ -59,14 +59,16 @@ var ( dubboProtocol *DubboProtocol ) -// DubboProtocol ... +// It support dubbo protocol. It implements Protocol interface for dubbo protocol. type DubboProtocol struct { protocol.BaseProtocol + // It is store relationship about serviceKey(group/interface:version) and ExchangeServer + // The ExchangeServer is introduced to replace of Server. Because Server is depend on getty directly. serverMap map[string]*remoting.ExchangeServer serverLock sync.Mutex } -// NewDubboProtocol ... +// nolint func NewDubboProtocol() *DubboProtocol { return &DubboProtocol{ BaseProtocol: protocol.NewBaseProtocol(), @@ -74,7 +76,7 @@ func NewDubboProtocol() *DubboProtocol { } } -// Export ... +// nolint func (dp *DubboProtocol) Export(invoker protocol.Invoker) protocol.Exporter { url := invoker.GetUrl() serviceKey := url.ServiceKey() @@ -87,7 +89,7 @@ func (dp *DubboProtocol) Export(invoker protocol.Invoker) protocol.Exporter { return exporter } -// Refer ... +// nolint func (dp *DubboProtocol) Refer(url common.URL) protocol.Invoker { exchangeClient := getExchangeClient(url) if exchangeClient == nil { @@ -99,7 +101,7 @@ func (dp *DubboProtocol) Refer(url common.URL) protocol.Invoker { return invoker } -// Destroy ... +// nolint func (dp *DubboProtocol) Destroy() { logger.Infof("DubboProtocol destroy.") From 713fcf9b43760a622a35f4b2805b1d503dda600d Mon Sep 17 00:00:00 2001 From: cvictory Date: Tue, 2 Jun 2020 11:06:05 +0800 Subject: [PATCH 19/44] fix review issue --- protocol/dubbo/dubbo_invoker.go | 7 ++--- protocol/dubbo/dubbo_protocol.go | 41 ++++++++++++++++++++++---- protocol/jsonrpc/jsonrpc_invoker.go | 2 +- remoting/exchange.go | 26 ++++++++-------- remoting/exchange_client.go | 17 ++++++++--- remoting/getty/dubbo_codec_for_test.go | 2 +- remoting/getty/getty_client.go | 2 +- 7 files changed, 68 insertions(+), 29 deletions(-) diff --git a/protocol/dubbo/dubbo_invoker.go b/protocol/dubbo/dubbo_invoker.go index 9c7473f35f..988353e393 100644 --- a/protocol/dubbo/dubbo_invoker.go +++ b/protocol/dubbo/dubbo_invoker.go @@ -48,7 +48,8 @@ var ( ) var ( - attachmentKey = []string{constant.INTERFACE_KEY, constant.GROUP_KEY, constant.TOKEN_KEY, constant.TIMEOUT_KEY} + attachmentKey = []string{constant.INTERFACE_KEY, constant.GROUP_KEY, constant.TOKEN_KEY, constant.TIMEOUT_KEY, + constant.PATH_KEY, constant.VERSION_KEY} ) // DubboInvoker is implement of protocol.Invoker. A dubboInvoker refer to one service and ip. @@ -97,8 +98,6 @@ func (di *DubboInvoker) Invoke(ctx context.Context, invocation protocol.Invocati inv := invocation.(*invocation_impl.RPCInvocation) // init param - inv.SetAttachments(constant.PATH_KEY, di.GetUrl().GetParam(constant.INTERFACE_KEY, "")) - inv.SetAttachments(constant.VERSION_KEY, di.GetUrl().GetParam(constant.VERSION_KEY, "")) for _, k := range attachmentKey { if v := di.GetUrl().GetParam(k, ""); len(v) > 0 { inv.SetAttachments(k, v) @@ -134,7 +133,7 @@ func (di *DubboInvoker) Invoke(ctx context.Context, invocation protocol.Invocati } if result.Err == nil { result.Rest = inv.Reply() - result.Attrs = result.Attachments() + result.Attrs = rest.Attrs } logger.Debugf("result.Err: %v, result.Rest: %v", result.Err, result.Rest) diff --git a/protocol/dubbo/dubbo_protocol.go b/protocol/dubbo/dubbo_protocol.go index d1b2725541..2d8a20c550 100644 --- a/protocol/dubbo/dubbo_protocol.go +++ b/protocol/dubbo/dubbo_protocol.go @@ -21,6 +21,7 @@ import ( "context" "fmt" "sync" + "time" ) import ( @@ -49,6 +50,7 @@ var ( // Make the connection can be shared. // It will create one connection for one address (ip+port) exchangeClientMap *sync.Map = new(sync.Map) + exchangeLock *sync.Map = new(sync.Map) ) func init() { @@ -93,6 +95,7 @@ func (dp *DubboProtocol) Export(invoker protocol.Invoker) protocol.Exporter { func (dp *DubboProtocol) Refer(url common.URL) protocol.Invoker { exchangeClient := getExchangeClient(url) if exchangeClient == nil { + logger.Warnf("can't dial the server: %+v", url.Location) return nil } invoker := NewDubboInvoker(url, exchangeClient) @@ -178,14 +181,40 @@ func doHandleRequest(rpcInvocation *invocation.RPCInvocation) protocol.RPCResult func getExchangeClient(url common.URL) *remoting.ExchangeClient { clientTmp, ok := exchangeClientMap.Load(url.Location) if !ok { - exchangeClientTmp := remoting.NewExchangeClient(url, getty.NewClient(getty.Options{ - ConnectTimeout: config.GetConsumerConfig().ConnectTimeout, - }), config.GetConsumerConfig().ConnectTimeout, false) + var exchangeClientTmp *remoting.ExchangeClient + func() { + // lock for NewExchangeClient and store into map. + _, loaded := exchangeLock.LoadOrStore(url.Location, 0x00) + // unlock + defer exchangeLock.Delete(url.Location) + if loaded { + // retry for 5 times. + for i := 0; i < 5; i++ { + if clientTmp, ok = exchangeClientMap.Load(url.Location); ok { + break + } else { + // if cannot get, sleep a while. + time.Sleep(time.Duration(i*100) * time.Millisecond) + } + } + return + } + // new ExchangeClient + exchangeClientTmp = remoting.NewExchangeClient(url, getty.NewClient(getty.Options{ + ConnectTimeout: config.GetConsumerConfig().ConnectTimeout, + }), config.GetConsumerConfig().ConnectTimeout, false) + // input store + if exchangeClientTmp != nil { + exchangeClientMap.Store(url.Location, exchangeClientTmp) + } + }() if exchangeClientTmp != nil { - exchangeClientMap.Store(url.Location, exchangeClientTmp) + return exchangeClientTmp } - - return exchangeClientTmp + } + // cannot dial the server + if clientTmp == nil { + return nil } exchangeClient, ok := clientTmp.(*remoting.ExchangeClient) if !ok { diff --git a/protocol/jsonrpc/jsonrpc_invoker.go b/protocol/jsonrpc/jsonrpc_invoker.go index fd3011260d..3bd2d3898f 100644 --- a/protocol/jsonrpc/jsonrpc_invoker.go +++ b/protocol/jsonrpc/jsonrpc_invoker.go @@ -53,7 +53,7 @@ func (ji *JsonrpcInvoker) Invoke(ctx context.Context, invocation protocol.Invoca url := ji.GetUrl() req := ji.client.NewRequest(url, inv.MethodName(), inv.Arguments()) ctxNew := context.WithValue(ctx, constant.DUBBOGO_CTX_KEY, map[string]string{ - "X-Proxy-ID": "dubbogo", + "X-Proxy-Id": "dubbogo", "X-Services": url.Path, "X-Method": inv.MethodName(), }) diff --git a/remoting/exchange.go b/remoting/exchange.go index 892bd82013..b97d6073c1 100644 --- a/remoting/exchange.go +++ b/remoting/exchange.go @@ -30,7 +30,7 @@ import ( var ( // generate request ID for global use - sequence atomic.Uint64 + sequence atomic.Int64 ) func init() { @@ -38,12 +38,13 @@ func init() { sequence.Store(0) } -func SequenceId() uint64 { - // increse 2 for every request. +func SequenceId() int64 { + // increse 2 for every request as the same before. + // We expect that the request from client to server, the requestId is even; but from server to client, the requestId is odd. return sequence.Add(2) } -// Request ... +// this is request for transport layer type Request struct { ID int64 // protocol version @@ -54,19 +55,18 @@ type Request struct { Data interface{} TwoWay bool Event bool - // it is used to judge the request is unbroken - // broken bool } -// NewRequest +// NewRequest aims to create Request. +// The ID is auto increase. func NewRequest(version string) *Request { return &Request{ - ID: int64(SequenceId()), + ID: SequenceId(), Version: version, } } -// Response ... +// this is response for transport layer type Response struct { ID int64 Version string @@ -77,7 +77,7 @@ type Response struct { Result interface{} } -// NewResponse +// NewResponse aims to create Response func NewResponse(id int64, version string) *Response { return &Response{ ID: id, @@ -117,7 +117,8 @@ type PendingResponse struct { Done chan struct{} } -// NewPendingResponse ... +// NewPendingResponse aims to create PendingResponse. +// Id is always from ID of Request func NewPendingResponse(id int64) *PendingResponse { return &PendingResponse{ seq: id, @@ -131,7 +132,8 @@ func (r *PendingResponse) SetResponse(response *Response) { r.response = response } -// GetCallResponse ... +// GetCallResponse is used for callback of async. +// It is will return AsyncCallbackResponse. func (r PendingResponse) GetCallResponse() common.CallbackResponse { return AsyncCallbackResponse{ Cause: r.Err, diff --git a/remoting/exchange_client.go b/remoting/exchange_client.go index 7ef8e54a2f..7c0520ab9c 100644 --- a/remoting/exchange_client.go +++ b/remoting/exchange_client.go @@ -51,10 +51,14 @@ type Client interface { // This is abstraction level. it is like facade. type ExchangeClient struct { + // connect server timeout ConnectTimeout time.Duration - address string - client Client - init bool + // to dial server address. The format: ip:port + address string + // the client that will deal with the transport. It is interface, and it will use gettyClient by default. + client Client + // the tag for init. + init bool } // handle the message from server @@ -114,11 +118,16 @@ func (client *ExchangeClient) Request(invocation *protocol.Invocation, url commo AddPendingResponse(rsp) err := client.client.Request(request, timeout, rsp) + // request error if err != nil { result.Err = err return err } - result.Rest = rsp.response.Result + if resultTmp, ok := rsp.response.Result.(*protocol.RPCResult); ok { + result.Rest = resultTmp.Rest + result.Attrs = resultTmp.Attrs + result.Err = resultTmp.Err + } return nil } diff --git a/remoting/getty/dubbo_codec_for_test.go b/remoting/getty/dubbo_codec_for_test.go index bb87323d54..f6460cde9d 100644 --- a/remoting/getty/dubbo_codec_for_test.go +++ b/remoting/getty/dubbo_codec_for_test.go @@ -26,11 +26,11 @@ import ( "time" ) import ( + hessian "github.com/apache/dubbo-go-hessian2" perrors "github.com/pkg/errors" ) import ( - hessian "github.com/apache/dubbo-go-hessian2" "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/common/logger" "github.com/apache/dubbo-go/protocol" diff --git a/remoting/getty/getty_client.go b/remoting/getty/getty_client.go index 153a062673..7055b98505 100644 --- a/remoting/getty/getty_client.go +++ b/remoting/getty/getty_client.go @@ -88,7 +88,7 @@ func initClient(protocol string) { rand.Seed(time.Now().UnixNano()) } -// SetClientConf: config ClientConf +// Config ClientConf func SetClientConf(c ClientConfig) { clientConf = &c err := clientConf.CheckValidity() From a08d2c79c270ace8b8d4271f2147d3915554c998 Mon Sep 17 00:00:00 2001 From: cvictory Date: Tue, 2 Jun 2020 11:57:10 +0800 Subject: [PATCH 20/44] fix review issue --- common/constant/key.go | 39 ++++++++++++++-------------- protocol/dubbo/dubbo_codec.go | 6 ++--- protocol/dubbo/dubbo_invoker_test.go | 1 + 3 files changed, 24 insertions(+), 22 deletions(-) diff --git a/common/constant/key.go b/common/constant/key.go index 07335bed59..d8eff3a479 100644 --- a/common/constant/key.go +++ b/common/constant/key.go @@ -22,25 +22,26 @@ 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" - PATH_SEPARATOR = "/" + 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 ) const ( diff --git a/protocol/dubbo/dubbo_codec.go b/protocol/dubbo/dubbo_codec.go index 707b9984b6..f4a39cb893 100644 --- a/protocol/dubbo/dubbo_codec.go +++ b/protocol/dubbo/dubbo_codec.go @@ -134,10 +134,10 @@ func (c *DubboCodec) EncodeRequest(request *remoting.Request) (*bytes.Buffer, er p.Service.Group = invocation.AttachmentsByKey(constant.GROUP_KEY, "") p.Service.Method = invocation.MethodName() - timeout, err := strconv.Atoi(invocation.AttachmentsByKey(constant.TIMEOUT_KEY, "3000")) + 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, err + return nil, perrors.WithStack(err) } p.Service.Timeout = time.Duration(timeout) @@ -259,7 +259,7 @@ func (c *DubboCodec) decodeRequest(data []byte) (*remoting.Request, int, error) TwoWay: pkg.Header.Type&hessian.PackageRequest_TwoWay != 0x00, Event: pkg.Header.Type&hessian.PackageHeartbeat != 0x00, } - if pkg.Header.Type&hessian.PackageHeartbeat == 0x00 { + 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 { diff --git a/protocol/dubbo/dubbo_invoker_test.go b/protocol/dubbo/dubbo_invoker_test.go index 6abd32d189..88c1910533 100644 --- a/protocol/dubbo/dubbo_invoker_test.go +++ b/protocol/dubbo/dubbo_invoker_test.go @@ -177,6 +177,7 @@ type ( 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("击鼓其镗,踊跃用兵。土国城漕,我独南行。从孙子仲,平陈与宋。不我以归,忧心有忡。爰居爰处?爰丧其马?于以求之?于林之下。死生契阔,与子成说。执子之手,与子偕老。于嗟阔兮,不我活兮。于嗟洵兮,不我信兮。") } From 5ba23079a9e578aa034f406c0d01543cd98f4d41 Mon Sep 17 00:00:00 2001 From: cvictory Date: Tue, 2 Jun 2020 21:51:17 +0800 Subject: [PATCH 21/44] refactor ServiceKey func --- common/url.go | 8 +++++--- protocol/invocation/rpcinvocation.go | 24 +++--------------------- 2 files changed, 8 insertions(+), 24 deletions(-) diff --git a/common/url.go b/common/url.go index ebb648db27..01c623ed5a 100644 --- a/common/url.go +++ b/common/url.go @@ -318,12 +318,15 @@ func (c URL) Key() string { // ServiceKey ... 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 "" } buf := &bytes.Buffer{} - group := c.GetParam(constant.GROUP_KEY, "") if group != "" { buf.WriteString(group) buf.WriteString("/") @@ -331,7 +334,6 @@ func (c URL) ServiceKey() string { buf.WriteString(intf) - version := c.GetParam(constant.VERSION_KEY, "") if version != "" && version != "0.0.0" { buf.WriteString(":") buf.WriteString(version) diff --git a/protocol/invocation/rpcinvocation.go b/protocol/invocation/rpcinvocation.go index 2494c39da2..cc93da4762 100644 --- a/protocol/invocation/rpcinvocation.go +++ b/protocol/invocation/rpcinvocation.go @@ -18,7 +18,7 @@ package invocation import ( - "bytes" + "github.com/apache/dubbo-go/common" "reflect" "sync" ) @@ -144,26 +144,8 @@ func (r *RPCInvocation) SetCallBack(c interface{}) { } func (r *RPCInvocation) ServiceKey() string { - intf := r.AttachmentsByKey(constant.INTERFACE_KEY, "") - if len(intf) == 0 { - return "" - } - buf := &bytes.Buffer{} - group := r.AttachmentsByKey(constant.GROUP_KEY, "") - if len(group) != 0 { - buf.WriteString(group) - buf.WriteString("/") - } - - buf.WriteString(intf) - - version := r.AttachmentsByKey(constant.VERSION_KEY, "") - if len(version) != 0 && version != "0.0.0" { - buf.WriteString(":") - buf.WriteString(version) - } - - return buf.String() + return common.ServiceKey(r.AttachmentsByKey(constant.INTERFACE_KEY, ""), + r.AttachmentsByKey(constant.GROUP_KEY, ""), r.AttachmentsByKey(constant.VERSION_KEY, "")) } // ///////////////////////// From 3d308e40694ee3cd34b3575e5cc9a67971df3b1e Mon Sep 17 00:00:00 2001 From: cvictory Date: Wed, 10 Jun 2020 19:37:04 +0800 Subject: [PATCH 22/44] fix: path is not in param --- protocol/dubbo/dubbo_invoker.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/protocol/dubbo/dubbo_invoker.go b/protocol/dubbo/dubbo_invoker.go index 988353e393..fa48718682 100644 --- a/protocol/dubbo/dubbo_invoker.go +++ b/protocol/dubbo/dubbo_invoker.go @@ -49,7 +49,7 @@ var ( var ( attachmentKey = []string{constant.INTERFACE_KEY, constant.GROUP_KEY, constant.TOKEN_KEY, constant.TIMEOUT_KEY, - constant.PATH_KEY, constant.VERSION_KEY} + constant.VERSION_KEY} ) // DubboInvoker is implement of protocol.Invoker. A dubboInvoker refer to one service and ip. @@ -98,6 +98,7 @@ func (di *DubboInvoker) Invoke(ctx context.Context, invocation protocol.Invocati inv := invocation.(*invocation_impl.RPCInvocation) // init param + inv.SetAttachments(constant.PATH_KEY, di.GetUrl().Path) for _, k := range attachmentKey { if v := di.GetUrl().GetParam(k, ""); len(v) > 0 { inv.SetAttachments(k, v) From 38c9c0910c35f9046485d8647ce50a70fb6438ab Mon Sep 17 00:00:00 2001 From: cvictory Date: Wed, 10 Jun 2020 19:55:45 +0800 Subject: [PATCH 23/44] fix: path is not in param --- protocol/dubbo/dubbo_invoker.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocol/dubbo/dubbo_invoker.go b/protocol/dubbo/dubbo_invoker.go index fa48718682..f967e95552 100644 --- a/protocol/dubbo/dubbo_invoker.go +++ b/protocol/dubbo/dubbo_invoker.go @@ -98,7 +98,7 @@ func (di *DubboInvoker) Invoke(ctx context.Context, invocation protocol.Invocati inv := invocation.(*invocation_impl.RPCInvocation) // init param - inv.SetAttachments(constant.PATH_KEY, di.GetUrl().Path) + inv.SetAttachments(constant.PATH_KEY, strings.TrimPrefix(di.GetUrl().Path, constant.PATH_SEPARATOR)) for _, k := range attachmentKey { if v := di.GetUrl().GetParam(k, ""); len(v) > 0 { inv.SetAttachments(k, v) From fff30118f7dd52e97be49d0912472113994be68d Mon Sep 17 00:00:00 2001 From: cvictory Date: Tue, 16 Jun 2020 14:25:08 +0800 Subject: [PATCH 24/44] fix: path is not in param --- protocol/dubbo/dubbo_invoker.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocol/dubbo/dubbo_invoker.go b/protocol/dubbo/dubbo_invoker.go index f967e95552..d94169d126 100644 --- a/protocol/dubbo/dubbo_invoker.go +++ b/protocol/dubbo/dubbo_invoker.go @@ -98,7 +98,7 @@ func (di *DubboInvoker) Invoke(ctx context.Context, invocation protocol.Invocati inv := invocation.(*invocation_impl.RPCInvocation) // init param - inv.SetAttachments(constant.PATH_KEY, strings.TrimPrefix(di.GetUrl().Path, constant.PATH_SEPARATOR)) + inv.SetAttachments(constant.PATH_KEY, di.GetUrl().GetParam(constant.INTERFACE_KEY, "")) for _, k := range attachmentKey { if v := di.GetUrl().GetParam(k, ""); len(v) > 0 { inv.SetAttachments(k, v) From 6b93bbc4f7ecf046098ac3beaaf10e4be5f40980 Mon Sep 17 00:00:00 2001 From: fangyincheng Date: Sat, 27 Jun 2020 18:14:50 +0800 Subject: [PATCH 25/44] 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 +++ protocol/dubbo/impl/remoting/client_impl.go | 391 ++++++++++++++ protocol/dubbo/{ => impl/remoting}/config.go | 48 +- protocol/dubbo/impl/remoting/errors.go | 17 + protocol/dubbo/{ => impl/remoting}/pool.go | 27 +- .../dubbo/{ => impl/remoting}/readwriter.go | 163 +++--- protocol/dubbo/impl/remoting/server_impl.go | 142 +++++ .../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 +++--- .../{listener_test.go => server_test.go} | 4 +- test/integrate/dubbo/go-server/server.go | 2 +- 36 files changed, 3417 insertions(+), 960 deletions(-) create mode 100644 common/constant/serializtion.go delete mode 100644 protocol/dubbo/codec.go delete mode 100644 protocol/dubbo/codec_test.go create mode 100644 protocol/dubbo/impl/codec.go create mode 100644 protocol/dubbo/impl/codec_test.go create mode 100644 protocol/dubbo/impl/const.go create mode 100644 protocol/dubbo/impl/hessian.go create mode 100644 protocol/dubbo/impl/package.go create mode 100644 protocol/dubbo/impl/proto.go create mode 100644 protocol/dubbo/impl/proto/payload.pb.go create mode 100644 protocol/dubbo/impl/proto/payload.proto create mode 100644 protocol/dubbo/impl/remoting/client_impl.go rename protocol/dubbo/{ => impl/remoting}/config.go (81%) create mode 100644 protocol/dubbo/impl/remoting/errors.go rename protocol/dubbo/{ => impl/remoting}/pool.go (91%) rename protocol/dubbo/{ => impl/remoting}/readwriter.go (52%) create mode 100644 protocol/dubbo/impl/remoting/server_impl.go rename protocol/dubbo/{listener.go => impl/remoting/server_listener.go} (60%) create mode 100644 protocol/dubbo/impl/request.go create mode 100644 protocol/dubbo/impl/response.go create mode 100644 protocol/dubbo/impl/serialization.go create mode 100644 protocol/dubbo/impl/serialize.go rename protocol/dubbo/{listener_test.go => server_test.go} (100%) diff --git a/common/constant/default.go b/common/constant/default.go index c69989b4fb..dd756917ce 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 5be63fe82c..6e73183adb 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 0000000000..f27598ccf5 --- /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 1cfa47ae28..51eabfff34 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 70a344c7b8..a49af18c21 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 887df01aa2..45d839dfb7 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 6d1b771bf4..0bd7aac90d 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 744ffa80d6..51e532e658 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 1f7d107544..0000000000 --- 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 5dc71f0d08..0000000000 --- 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 59202d5f49..1907c38a3e 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 1a64301f82..e96e859220 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 9eeefd0792..cc67569867 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 14f6868ad4..1915a072f0 100644 --- a/protocol/dubbo/dubbo_protocol_test.go +++ b/protocol/dubbo/dubbo_protocol_test.go @@ -21,20 +21,22 @@ import ( "testing" ) -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 0000000000..f527bbcff0 --- /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 0000000000..c93f307aa3 --- /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 0000000000..06e73fab1d --- /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 0000000000..513421b95f --- /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 0000000000..d40c4af88b --- /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 0000000000..ea1c55da96 --- /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 0000000000..337027e86d --- /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 0000000000..19f644ee91 --- /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 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/impl/remoting/client_impl.go b/protocol/dubbo/impl/remoting/client_impl.go new file mode 100644 index 0000000000..e5f7c7138b --- /dev/null +++ b/protocol/dubbo/impl/remoting/client_impl.go @@ -0,0 +1,391 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package remoting + +import ( + "math/rand" + "strings" + "sync" + "time" +) + +import ( + "github.com/dubbogo/getty" + gxsync "github.com/dubbogo/gost/sync" + perrors "github.com/pkg/errors" + "go.uber.org/atomic" +) + +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" +) + +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") +) + +var ( + clientConf *ClientConfig + clientGrpool *gxsync.TaskPool +) + +// 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 + codec impl.DubboCodec +} + +// 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, + codec: impl.DubboCodec{}, + } + 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 { + // 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, + 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 one way +func (c *Client) CallOneway(request *Request) error { + + return perrors.WithStack(c.call(impl.CT_OneWay, request, NewResponse(nil, nil), nil)) +} + +// 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 := impl.CT_TwoWay + if response.Reply == nil { + ct = impl.CT_OneWay + } + + return perrors.WithStack(c.call(ct, request, response, nil)) +} + +// AsyncCall ... +func (c *Client) AsyncCall(request *Request, callback common.AsyncCallback, response *Response) error { + + 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 + 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() + }() + + 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 == impl.CT_OneWay || callback != nil { + return nil + } + + select { + case <-getty.GetTimeWheel().After(c.Opts.RequestTimeout): + c.removePendingResponse(impl.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(impl.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 *impl.DubboPackage, + rsp *PendingResponse) error { + + var ( + sequence uint64 + err error + ) + + sequence = c.Sequence.Add(1) + + if pkg == nil { + // 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.SetID(int64(sequence)) + + // cond1 + if rsp != nil { + rsp.seq = sequence + c.addPendingResponse(rsp) + } + + err = session.WritePkg(pkg, c.Opts.RequestTimeout) + if err != nil { + 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. + rsp.readStart = time.Now() + } + + return perrors.WithStack(err) +} + +func (c *Client) addPendingResponse(pr *PendingResponse) { + c.PendingResponses.Store(impl.SequenceType(pr.seq), pr) +} + +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) + 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 635d12109a..1f26422f79 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 0000000000..8fda9b31ff --- /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 c9f5e34fad..dc8b1238cc 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 9cc7ea25cd..6d211f5ea5 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/impl/remoting/server_impl.go b/protocol/dubbo/impl/remoting/server_impl.go new file mode 100644 index 0000000000..6419ff685c --- /dev/null +++ b/protocol/dubbo/impl/remoting/server_impl.go @@ -0,0 +1,142 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package remoting + +import ( + "fmt" + "net" +) + +import ( + "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/common/logger" +) + +import ( + "github.com/dubbogo/getty" + gxsync "github.com/dubbogo/gost/sync" +) + +// TODO: 需要移动到 业务的实现 +var ( + srvConf *ServerConfig + srvGrpool *gxsync.TaskPool +) + +// SetServerConfig ... +func SetServerConfig(s ServerConfig) { + srvConf = &s + err := srvConf.CheckValidity() + if err != nil { + panic(err) + return + } + SetServerGrpool() +} + +// GetServerConfig ... +func GetServerConfig() *ServerConfig { + return srvConf +} + +// SetServerGrpool ... +func SetServerGrpool() { + if srvConf.GrPoolSize > 1 { + srvGrpool = gxsync.NewTaskPool(gxsync.WithTaskPoolTaskPoolSize(srvConf.GrPoolSize), gxsync.WithTaskPoolTaskQueueLength(srvConf.QueueLen), + gxsync.WithTaskPoolTaskQueueNumber(srvConf.QueueNumber)) + } +} + +// Server ... +type Server struct { + conf ServerConfig + tcpServer getty.Server + rpcHandler *RpcServerHandler +} + +// NewServer ... +func NewServer(handler StubHandler) *Server { + + s := &Server{ + conf: *srvConf, + } + + s.rpcHandler = NewRpcServerHandler(handler, s.conf.SessionNumber, s.conf.SessionTimeoutD) + + return s +} + +func (s *Server) newSession(session getty.Session) error { + var ( + ok bool + tcpConn *net.TCPConn + ) + conf := s.conf + + if conf.GettySessionParam.CompressEncoding { + session.SetCompressType(getty.CompressZip) + } + + if tcpConn, ok = session.Conn().(*net.TCPConn); !ok { + panic(fmt.Sprintf("%s, session.conn{%#v} is not tcp connection\n", session.Stat(), session.Conn())) + } + + tcpConn.SetNoDelay(conf.GettySessionParam.TcpNoDelay) + tcpConn.SetKeepAlive(conf.GettySessionParam.TcpKeepAlive) + if conf.GettySessionParam.TcpKeepAlive { + tcpConn.SetKeepAlivePeriod(conf.GettySessionParam.KeepAlivePeriodD) + } + 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.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) + + return nil +} + +// Start ... +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 ... +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 4834459390..f59e88e3f3 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 0000000000..0e770c3afb --- /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 0000000000..ea0a6efb23 --- /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 0000000000..7ce76a87c1 --- /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 0000000000..1f913f7caa --- /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 8de353a0b3..134a58c686 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 5f80981460..aa7d75056f 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 115bf0a4d7..a5d18dba3c 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") From ff4f2f74204ee1638b6e1969eaf3381052e66c71 Mon Sep 17 00:00:00 2001 From: fangyincheng Date: Wed, 5 Aug 2020 23:20:35 +0800 Subject: [PATCH 26/44] Mod: comment ut for protobuf of dubbo --- protocol/dubbo/impl/codec_test.go | 226 +++++++++++++++--------------- 1 file changed, 112 insertions(+), 114 deletions(-) diff --git a/protocol/dubbo/impl/codec_test.go b/protocol/dubbo/impl/codec_test.go index 92f2f2e9c0..77cc69a742 100644 --- a/protocol/dubbo/impl/codec_test.go +++ b/protocol/dubbo/impl/codec_test.go @@ -23,13 +23,11 @@ import ( ) 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) { @@ -83,115 +81,115 @@ func TestDubboPackage_MarshalAndUnmarshal(t *testing.T) { 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, 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"]) - } - -} +//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, true, ok) +// sv := pb.StringValue{} +// buf := proto.NewBuffer(argsBytes) +// err = buf.Unmarshal(&sv) +// assert.NoError(t, err) +// assert.Equal(t, "hello world", sv.Value) +//} + +//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"]) +// } +// +//} From 6338a824cd8ed43941c97fca6903253742dc1f11 Mon Sep 17 00:00:00 2001 From: fangyincheng Date: Mon, 10 Aug 2020 03:33:41 +0800 Subject: [PATCH 27/44] Fix: fix --- go.mod | 3 + go.sum | 2 +- protocol/dubbo/dubbo_codec.go | 135 +++------- protocol/dubbo/dubbo_protocol_test.go | 39 ++- protocol/dubbo/impl/codec.go | 42 ++-- protocol/dubbo/impl/const.go | 9 + protocol/dubbo/impl/package.go | 4 +- remoting/exchange_client.go | 2 +- remoting/getty/dubbo_codec_for_test.go | 335 +------------------------ remoting/getty/readwriter.go | 6 - 10 files changed, 87 insertions(+), 490 deletions(-) diff --git a/go.mod b/go.mod index c196273782..7c2ffed2b4 100644 --- a/go.mod +++ b/go.mod @@ -39,6 +39,7 @@ require ( github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8 // indirect github.com/juju/testing v0.0.0-20191001232224-ce9dec17d28b // indirect github.com/magiconair/properties v1.8.1 + github.com/matttproud/golang_protobuf_extensions v1.0.1 github.com/mitchellh/hashstructure v1.0.0 // indirect github.com/mitchellh/mapstructure v1.2.3 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd @@ -66,4 +67,6 @@ require ( k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a // indirect ) +replace launchpad.net/gocheck => github.com/go-check/check v0.0.0-20140225173054-eb6ee6f84d0a + go 1.13 diff --git a/go.sum b/go.sum index aa6ecc86e2..6df5fc5248 100644 --- a/go.sum +++ b/go.sum @@ -184,6 +184,7 @@ github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 h1:Mn26/9ZMNWSw9C9ER github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I= github.com/go-asn1-ber/asn1-ber v1.3.1 h1:gvPdv/Hr++TRFCl0UbPFHC54P9N9jgsRPnmnr419Uck= github.com/go-asn1-ber/asn1-ber v1.3.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= +github.com/go-check/check v0.0.0-20140225173054-eb6ee6f84d0a/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= github.com/go-co-op/gocron v0.1.1 h1:OfDmkqkCguFtFMsm6Eaayci3DADLa8pXvdmOlPU/JcU= github.com/go-co-op/gocron v0.1.1/go.mod h1:Y9PWlYqDChf2Nbgg7kfS+ZsXHDTZbMZYPEQ0MILqH+M= github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= @@ -898,7 +899,6 @@ k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a h1:UcxjrRMyNx/i/y8G7kPvLy k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= k8s.io/utils v0.0.0-20190801114015-581e00157fb1 h1:+ySTxfHnfzZb9ys375PXNlLhkJPLKgHajBU0N62BDvE= k8s.io/utils v0.0.0-20190801114015-581e00157fb1/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= -launchpad.net/gocheck v0.0.0-20140225173054-000000000087/go.mod h1:hj7XX3B/0A+80Vse0e+BUHsHMTEhd0O4cpUHr/e/BUM= sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= diff --git a/protocol/dubbo/dubbo_codec.go b/protocol/dubbo/dubbo_codec.go index e575c797b3..dbd4c48a69 100644 --- a/protocol/dubbo/dubbo_codec.go +++ b/protocol/dubbo/dubbo_codec.go @@ -39,76 +39,12 @@ import ( //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 -//} - -// String of DubboPackage -//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 -//} - -// 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) -//} - // DubboCodec. It is implements remoting.Codec type DubboCodec struct { } @@ -141,10 +77,10 @@ func (c *DubboCodec) EncodeRequest(request *remoting.Request) (*bytes.Buffer, er 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 { + if serialization == constant.PROTOBUF_SERIALIZATION { header.SerialID = constant.S_Proto + } else { + header.SerialID = constant.S_Hessian2 } header.ID = request.ID if request.TwoWay { @@ -205,7 +141,7 @@ func (c *DubboCodec) EncodeResponse(response *remoting.Response) (*bytes.Buffer, }, } if !response.IsHeartbeat() { - resp.Body = &hessian.Response{ + resp.Body = &impl.ResponsePayload{ RspObj: response.Result.(protocol.RPCResult).Rest, Exception: response.Result.(protocol.RPCResult).Err, Attachments: response.Result.(protocol.RPCResult).Attrs, @@ -271,41 +207,28 @@ func (c *DubboCodec) decodeRequest(data []byte) (*remoting.Request, int, error) } 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 + req := pkg.Body.(map[string]interface{}) + + //invocation := request.Data.(*invocation.RPCInvocation) + var methodName string + var args []interface{} + var attachments map[string]string = make(map[string]string) + if req[impl.DubboVersionKey] != nil { + //dubbo version + request.Version = req[impl.DubboVersionKey].(string) } + //path + attachments[constant.PATH_KEY] = pkg.Service.Path + //version + attachments[constant.VERSION_KEY] = pkg.Service.Version + //method + methodName = pkg.Service.Method + args = req[impl.ArgsKey].([]interface{}) + attachments = req[impl.AttachmentsKey].(map[string]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 } @@ -354,12 +277,12 @@ func (c *DubboCodec) decodeResponse(data []byte) (*remoting.Response, int, error 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 + } else if pkg.Body.(*impl.ResponsePayload).Exception != nil { + rpcResult.Err = pkg.Body.(*impl.ResponsePayload).Exception response.Error = rpcResult.Err } - rpcResult.Attrs = pkg.Body.(*hessian.Response).Attachments - rpcResult.Rest = pkg.Body.(*hessian.Response).RspObj + rpcResult.Attrs = pkg.Body.(*impl.ResponsePayload).Attachments + rpcResult.Rest = pkg.Body.(*impl.ResponsePayload).RspObj } return response, hessian.HEADER_LENGTH + pkg.Header.BodyLen, nil diff --git a/protocol/dubbo/dubbo_protocol_test.go b/protocol/dubbo/dubbo_protocol_test.go index 352d980017..3109d5ce32 100644 --- a/protocol/dubbo/dubbo_protocol_test.go +++ b/protocol/dubbo/dubbo_protocol_test.go @@ -32,6 +32,14 @@ import ( "github.com/apache/dubbo-go/remoting/getty" ) +const ( + mockCommonUrl = "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&" + + "application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&" + + "environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&" + + "module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&" + + "side=provider&timeout=3000×tamp=1556509797245" +) + func init() { getty.SetServerConfig(getty.ServerConfig{ SessionNumber: 700, @@ -77,16 +85,15 @@ func TestDubboProtocol_Export(t *testing.T) { getty.SetServerConfig(srvCfg) // Export proto := GetProtocol() - 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&" + - "side=provider&timeout=3000×tamp=1556509797245") - 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")) + 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")) assert.NoError(t, err) exporter2 := proto.Export(protocol.NewBaseInvoker(url2)) // make sure url @@ -111,11 +118,7 @@ func TestDubboProtocol_Export(t *testing.T) { 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") + url, err := common.NewURL(mockCommonUrl) assert.NoError(t, err) invoker := proto.Refer(url) assert.Nil(t, invoker) @@ -127,11 +130,7 @@ func TestDubboProtocol_Refer(t *testing.T) { // Refer proto := GetProtocol() - 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&" + - "side=provider&timeout=3000×tamp=1556509797245") + url, err := common.NewURL(mockCommonUrl) assert.NoError(t, err) invoker := proto.Refer(url) diff --git a/protocol/dubbo/impl/codec.go b/protocol/dubbo/impl/codec.go index f527bbcff0..40866d9642 100644 --- a/protocol/dubbo/impl/codec.go +++ b/protocol/dubbo/impl/codec.go @@ -20,6 +20,8 @@ package impl import ( "bufio" "encoding/binary" + "github.com/apache/dubbo-go/common/constant" + "github.com/apache/dubbo-go/remoting" ) import ( @@ -31,7 +33,7 @@ import ( "github.com/apache/dubbo-go/common/logger" ) -type DubboCodec struct { +type ProtocolCodec struct { reader *bufio.Reader pkgType PackageType bodyLen int @@ -39,24 +41,7 @@ type DubboCodec struct { 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 { +func (c *ProtocolCodec) ReadHeader(header *DubboHeader) error { var err error if c.reader.Size() < HEADER_LENGTH { return hessian.ErrHeaderNotEnough @@ -118,7 +103,7 @@ func (c *DubboCodec) ReadHeader(header *DubboHeader) error { return errors.WithStack(err) } -func (c *DubboCodec) EncodeHeader(p DubboPackage) []byte { +func (c *ProtocolCodec) EncodeHeader(p DubboPackage) []byte { header := p.Header bs := make([]byte, 0) switch header.Type { @@ -141,7 +126,7 @@ func (c *DubboCodec) EncodeHeader(p DubboPackage) []byte { return bs } -func (c *DubboCodec) Encode(p DubboPackage) ([]byte, error) { +func (c *ProtocolCodec) Encode(p DubboPackage) ([]byte, error) { // header if c.serializer == nil { return nil, errors.New("serializer should not be nil") @@ -165,7 +150,7 @@ func (c *DubboCodec) Encode(p DubboPackage) ([]byte, error) { } } -func (c *DubboCodec) Decode(p *DubboPackage) error { +func (c *ProtocolCodec) Decode(p *DubboPackage) error { if !c.headerRead { if err := c.ReadHeader(&p.Header); err != nil { return err @@ -195,10 +180,15 @@ func (c *DubboCodec) Decode(p *DubboPackage) error { if c.serializer == nil { return errors.New("Codec serializer is nil") } + if p.IsResponse() { + p.Body = &ResponsePayload{ + RspObj: remoting.GetPendingResponse(remoting.SequenceType(p.Header.ID)).Reply, + } + } return c.serializer.Unmarshal(body, p) } -func (c *DubboCodec) SetSerializer(serializer Serializer) { +func (c *ProtocolCodec) SetSerializer(serializer Serializer) { c.serializer = serializer } @@ -289,11 +279,13 @@ func packResponse(p DubboPackage, serializer Serializer) ([]byte, error) { return byteArray, nil } -func NewDubboCodec(reader *bufio.Reader) *DubboCodec { - return &DubboCodec{ +func NewDubboCodec(reader *bufio.Reader) *ProtocolCodec { + s, _ := GetSerializerById(constant.S_Hessian2) + return &ProtocolCodec{ reader: reader, pkgType: 0, bodyLen: 0, headerRead: false, + serializer: s.(Serializer), } } diff --git a/protocol/dubbo/impl/const.go b/protocol/dubbo/impl/const.go index c2f4006174..70d8bae6ca 100644 --- a/protocol/dubbo/impl/const.go +++ b/protocol/dubbo/impl/const.go @@ -241,3 +241,12 @@ var ( var DescRegex, _ = regexp.Compile(DESC_REGEX) var NilValue = reflect.Zero(reflect.TypeOf((*interface{})(nil)).Elem()) + +// Body map keys +var ( + DubboVersionKey = "dubboVersion" + ArgsTypesKey = "argsTypes" + ArgsKey = "args" + ServiceKey = "service" + AttachmentsKey = "attachments" +) diff --git a/protocol/dubbo/impl/package.go b/protocol/dubbo/impl/package.go index d40c4af88b..6f6d2ea975 100644 --- a/protocol/dubbo/impl/package.go +++ b/protocol/dubbo/impl/package.go @@ -64,7 +64,7 @@ type DubboPackage struct { Service Service Body interface{} Err error - Codec *DubboCodec + Codec *ProtocolCodec } func (p DubboPackage) String() string { @@ -155,7 +155,7 @@ func (p *DubboPackage) SetSerializer(serializer Serializer) { } func NewDubboPackage(data *bytes.Buffer) *DubboPackage { - var codec *DubboCodec + var codec *ProtocolCodec if data == nil { codec = NewDubboCodec(nil) } else { diff --git a/remoting/exchange_client.go b/remoting/exchange_client.go index 7c0520ab9c..efcfca5586 100644 --- a/remoting/exchange_client.go +++ b/remoting/exchange_client.go @@ -30,7 +30,7 @@ import ( var ( // store requestID and response - pendingResponses *sync.Map = new(sync.Map) + pendingResponses = new(sync.Map) ) type SequenceType int64 diff --git a/remoting/getty/dubbo_codec_for_test.go b/remoting/getty/dubbo_codec_for_test.go index f6460cde9d..8303bb782c 100644 --- a/remoting/getty/dubbo_codec_for_test.go +++ b/remoting/getty/dubbo_codec_for_test.go @@ -19,364 +19,41 @@ package getty // copy from dubbo/dubbo_codec.go . // it is used to unit test. import ( - "bufio" "bytes" - "fmt" - "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 := &DubboTestCodec{} remoting.RegistryCodec("dubbo", codec) } -// DubboPackage ... -type DubboPackage struct { - Header hessian.DubboHeader - Service hessian.Service - Body interface{} - Err error -} - -func (p DubboPackage) String() string { - return fmt.Sprintf("DubboPackage: Header-%v, Path-%v, Body-%v", p.Header, p.Service, p.Body) -} - -// Marshal ... -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 ... -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) -} - type DubboTestCodec struct { } func (c *DubboTestCodec) EncodeRequest(request *remoting.Request) (*bytes.Buffer, error) { - if request.Event { - return c.encodeHeartbeartReqeust(request) - } - - invoc, ok := request.Data.(*invocation.RPCInvocation) - if !ok { - 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() - - timeout, err := strconv.Atoi(invocation.AttachmentsByKey(constant.TIMEOUT_KEY, "3000")) - if err != nil { - // it will be wrapped in readwrite.Write . - return nil, err - } - p.Service.Timeout = time.Duration(timeout) - //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.Header.ID = request.ID - if request.TwoWay { - p.Header.Type = hessian.PackageRequest_TwoWay - } else { - p.Header.Type = hessian.PackageRequest - } - - p.Body = hessian.NewRequest(invocation.Arguments(), invocation.Attachments()) - - 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 + return &bytes.Buffer{}, nil } func (c *DubboTestCodec) 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) - - byt, err := codec.Write(pkg.Service, pkg.Header, pkg.Body) - if err != nil { - return nil, perrors.WithStack(err) - } - - return bytes.NewBuffer(byt), nil + return &bytes.Buffer{}, nil } func (c *DubboTestCodec) EncodeResponse(response *remoting.Response) (*bytes.Buffer, error) { - var ptype = hessian.PackageResponse - if response.IsHeartbeat() { - ptype = hessian.PackageHeartbeat - } - resp := &DubboPackage{ - Header: hessian.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, - } - } - - //if response.Header.Type&hessian.PackageRequest != 0x00 { - // resp.Body = req.Body - //} else { - // resp.Body = nil - //} - codec := hessian.NewHessianCodec(nil) - - pkg, err := codec.Write(resp.Service, resp.Header, resp.Body) - if err != nil { - return nil, perrors.WithStack(err) - } - - return bytes.NewBuffer(pkg), nil + return &bytes.Buffer{}, nil } func (c *DubboTestCodec) Decode(data []byte) (remoting.DecodeResult, int, error) { - if c.isRequest(data) { - req, len, err := c.decodeRequest(data) - if err != nil { - return remoting.DecodeResult{}, len, err - } - return remoting.DecodeResult{IsRequest: true, Result: req}, len, err - } else { - resp, len, err := c.decodeResponse(data) - if err != nil { - return remoting.DecodeResult{}, len, err - } - return remoting.DecodeResult{IsRequest: false, Result: resp}, len, err - } + return remoting.DecodeResult{}, 0, nil } func (c *DubboTestCodec) isRequest(data []byte) bool { - if data[2]&byte(0x80) == 0x00 { - return false - } return true } func (c *DubboTestCodec) 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) - 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, - } - 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 { - //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 { - //argsType - //invocation.ParameterTypes(constant., req[1].(string)) - //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] - //} - invoc := invocation.NewRPCInvocationWithOptions(invocation.WithAttachments(attachments), - invocation.WithArguments(args), invocation.WithMethodName(methodName)) - request.Data = invoc - //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 request, hessian.HEADER_LENGTH + pkg.Header.BodyLen, nil + return nil, 0, nil } func (c *DubboTestCodec) decodeResponse(data []byte) (*remoting.Response, int, error) { - pkg := &DubboPackage{} - buf := bytes.NewBuffer(data) - response := &remoting.Response{} - err := pkg.Unmarshal(buf, response) - if err != nil { - originErr := perrors.Cause(err) - if originErr == hessian.ErrHeaderNotEnough || originErr == hessian.ErrBodyNotEnough { - return nil, 0, originErr - } - logger.Errorf("pkg.Unmarshal(len(@data):%d) = error:%+v", buf.Len(), err) - - return response, 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, - } - var error error - if pkg.Header.Type&hessian.PackageHeartbeat != 0x00 { - if pkg.Header.Type&hessian.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.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 - } - - //h.conn.updateSession(session) - //pendingResponse := h.conn.pool.rpcClient.removePendingResponse(SequenceType(p.Header.ID)) - //if pendingResponse == nil { - // logger.Errorf("failed to get pending response context for response package %s", *p) - // return - //} - - return response, hessian.HEADER_LENGTH + pkg.Header.BodyLen, nil + return nil, 0, nil } diff --git a/remoting/getty/readwriter.go b/remoting/getty/readwriter.go index c8105fad0f..e290d439b2 100644 --- a/remoting/getty/readwriter.go +++ b/remoting/getty/readwriter.go @@ -47,8 +47,6 @@ func NewRpcClientPackageHandler(client *Client) *RpcClientPackageHandler { } func (p *RpcClientPackageHandler) Read(ss getty.Session, data []byte) (interface{}, int, error) { - //pkg := &DubboPackage{} - //p.client.ExchangeClient.GetPendingResponse(remoting.SequenceType()) resp, length, err := (p.client.codec).Decode(data) //err := pkg.Unmarshal(buf, p.client) if err != nil { @@ -60,10 +58,6 @@ func (p *RpcClientPackageHandler) Read(ss getty.Session, data []byte) (interface return nil, length, err } - //if pkg.Header.Type&hessian.PackageRequest == 0x00 { - // pkg.Err = pkg.Body.(*hessian.Response).Exception - // pkg.Body = NewResponse(pkg.Body.(*hessian.Response).RspObj, pkg.Body.(*hessian.Response).Attachments) - //} return resp, length, nil } From 5050d266e78e47d8aac089bb0dff4237723f7bb6 Mon Sep 17 00:00:00 2001 From: haohongfan Date: Sun, 16 Aug 2020 15:45:45 +0800 Subject: [PATCH 28/44] fix: fix TestDubboProtocol_Export test --- protocol/dubbo/dubbo_protocol_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocol/dubbo/dubbo_protocol_test.go b/protocol/dubbo/dubbo_protocol_test.go index 3109d5ce32..11bbac004c 100644 --- a/protocol/dubbo/dubbo_protocol_test.go +++ b/protocol/dubbo/dubbo_protocol_test.go @@ -104,7 +104,7 @@ func TestDubboProtocol_Export(t *testing.T) { _, ok := proto.(*DubboProtocol).ExporterMap().Load(url.ServiceKey()) assert.True(t, ok) exporter2.Unexport() - _, ok = proto.(*DubboProtocol).ExporterMap().Load(url.ServiceKey()) + _, ok = proto.(*DubboProtocol).ExporterMap().Load(url2.ServiceKey()) assert.False(t, ok) // make sure serverMap after 'Destroy' From f86865dbe299b151cd46543d4541745a05020f16 Mon Sep 17 00:00:00 2001 From: haohongfan Date: Sun, 16 Aug 2020 16:08:22 +0800 Subject: [PATCH 29/44] fix: testDubboProtocol_Export exporterMap after 'Unexport' url key --- protocol/dubbo/dubbo_protocol_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocol/dubbo/dubbo_protocol_test.go b/protocol/dubbo/dubbo_protocol_test.go index 11bbac004c..8b2723431f 100644 --- a/protocol/dubbo/dubbo_protocol_test.go +++ b/protocol/dubbo/dubbo_protocol_test.go @@ -101,7 +101,7 @@ func TestDubboProtocol_Export(t *testing.T) { assert.True(t, eq2) // make sure exporterMap after 'Unexport' - _, ok := proto.(*DubboProtocol).ExporterMap().Load(url.ServiceKey()) + _, ok := proto.(*DubboProtocol).ExporterMap().Load(url2.ServiceKey()) assert.True(t, ok) exporter2.Unexport() _, ok = proto.(*DubboProtocol).ExporterMap().Load(url2.ServiceKey()) From eba6a1353cb96033a9d0ff122f4aa9d7cb960594 Mon Sep 17 00:00:00 2001 From: haohongfan Date: Sun, 16 Aug 2020 15:45:45 +0800 Subject: [PATCH 30/44] fix: fix TestDubboProtocol_Export test --- protocol/dubbo/dubbo_protocol_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocol/dubbo/dubbo_protocol_test.go b/protocol/dubbo/dubbo_protocol_test.go index 3109d5ce32..11bbac004c 100644 --- a/protocol/dubbo/dubbo_protocol_test.go +++ b/protocol/dubbo/dubbo_protocol_test.go @@ -104,7 +104,7 @@ func TestDubboProtocol_Export(t *testing.T) { _, ok := proto.(*DubboProtocol).ExporterMap().Load(url.ServiceKey()) assert.True(t, ok) exporter2.Unexport() - _, ok = proto.(*DubboProtocol).ExporterMap().Load(url.ServiceKey()) + _, ok = proto.(*DubboProtocol).ExporterMap().Load(url2.ServiceKey()) assert.False(t, ok) // make sure serverMap after 'Destroy' From 2d4424153a0d5f41ac3d88db4159508563901a70 Mon Sep 17 00:00:00 2001 From: haohongfan Date: Sun, 16 Aug 2020 16:08:22 +0800 Subject: [PATCH 31/44] fix: testDubboProtocol_Export exporterMap after 'Unexport' url key --- protocol/dubbo/dubbo_protocol_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocol/dubbo/dubbo_protocol_test.go b/protocol/dubbo/dubbo_protocol_test.go index 11bbac004c..8b2723431f 100644 --- a/protocol/dubbo/dubbo_protocol_test.go +++ b/protocol/dubbo/dubbo_protocol_test.go @@ -101,7 +101,7 @@ func TestDubboProtocol_Export(t *testing.T) { assert.True(t, eq2) // make sure exporterMap after 'Unexport' - _, ok := proto.(*DubboProtocol).ExporterMap().Load(url.ServiceKey()) + _, ok := proto.(*DubboProtocol).ExporterMap().Load(url2.ServiceKey()) assert.True(t, ok) exporter2.Unexport() _, ok = proto.(*DubboProtocol).ExporterMap().Load(url2.ServiceKey()) From 3ec9e9f53250425df205d413a091518db8a7a1c2 Mon Sep 17 00:00:00 2001 From: haohongfan Date: Sat, 5 Sep 2020 21:30:37 +0800 Subject: [PATCH 32/44] feat: fix protocol, remoting/getty uint test failure --- protocol/dubbo/dubbo_protocol_test.go | 13 +- remoting/getty/dubbo_codec_for_test.go | 226 ++++++++++++++++++++++++- remoting/getty/getty_client_test.go | 44 +++-- 3 files changed, 249 insertions(+), 34 deletions(-) diff --git a/protocol/dubbo/dubbo_protocol_test.go b/protocol/dubbo/dubbo_protocol_test.go index 8b2723431f..06d4aece20 100644 --- a/protocol/dubbo/dubbo_protocol_test.go +++ b/protocol/dubbo/dubbo_protocol_test.go @@ -18,6 +18,7 @@ package dubbo import ( + "github.com/apache/dubbo-go/common/proxy/proxy_factory" "testing" ) @@ -33,14 +34,14 @@ import ( ) const ( - mockCommonUrl = "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&" + + mockCommonUrl = "dubbo://127.0.0.1:30000/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" ) -func init() { +func initDubboInvokerTest() { getty.SetServerConfig(getty.ServerConfig{ SessionNumber: 700, SessionTimeout: "20s", @@ -80,7 +81,9 @@ func init() { }, }) } + func TestDubboProtocol_Export(t *testing.T) { + initDubboInvokerTest() srvCfg := getty.GetDefaultServerConfig() getty.SetServerConfig(srvCfg) // Export @@ -117,6 +120,7 @@ func TestDubboProtocol_Export(t *testing.T) { func TestDubboProtocol_Refer_No_connect(t *testing.T) { // Refer + initDubboInvokerTest() proto := GetProtocol() url, err := common.NewURL(mockCommonUrl) assert.NoError(t, err) @@ -125,15 +129,18 @@ func TestDubboProtocol_Refer_No_connect(t *testing.T) { } func TestDubboProtocol_Refer(t *testing.T) { + initDubboInvokerTest() cliCfg := getty.GetDefaultClientConfig() getty.SetClientConf(cliCfg) // Refer proto := GetProtocol() url, err := common.NewURL(mockCommonUrl) + proto.Export(&proxy_factory.ProxyInvoker{ + BaseInvoker: *protocol.NewBaseInvoker(url), + }) assert.NoError(t, err) invoker := proto.Refer(url) - // make sure url eq := invoker.GetUrl().URLEqual(url) assert.True(t, eq) diff --git a/remoting/getty/dubbo_codec_for_test.go b/remoting/getty/dubbo_codec_for_test.go index 8303bb782c..6ae7117f56 100644 --- a/remoting/getty/dubbo_codec_for_test.go +++ b/remoting/getty/dubbo_codec_for_test.go @@ -20,6 +20,14 @@ package getty // it is used to unit test. import ( "bytes" + hessian "github.com/apache/dubbo-go-hessian2" + "github.com/apache/dubbo-go/common/constant" + "github.com/apache/dubbo-go/protocol" + "github.com/apache/dubbo-go/protocol/dubbo/impl" + "github.com/apache/dubbo-go/protocol/invocation" + perrors "github.com/pkg/errors" + "strconv" + "time" ) import ( @@ -34,26 +42,232 @@ func init() { type DubboTestCodec struct { } +// encode request for transport func (c *DubboTestCodec) EncodeRequest(request *remoting.Request) (*bytes.Buffer, error) { - return &bytes.Buffer{}, nil + if request.Event { + return c.encodeHeartbeartReqeust(request) + } + + invoc, ok := request.Data.(*invocation.RPCInvocation) + if !ok { + return nil, perrors.Errorf("encode request failed for parameter type :%+v", request) + } + invocation := *invoc + + svc := impl.Service{} + svc.Path = invocation.AttachmentsByKey(constant.PATH_KEY, "") + svc.Interface = invocation.AttachmentsByKey(constant.INTERFACE_KEY, "") + svc.Version = invocation.AttachmentsByKey(constant.VERSION_KEY, "") + svc.Group = invocation.AttachmentsByKey(constant.GROUP_KEY, "") + svc.Method = invocation.MethodName() + timeout, err := strconv.Atoi(invocation.AttachmentsByKey(constant.TIMEOUT_KEY, strconv.Itoa(constant.DEFAULT_REMOTING_TIMEOUT))) + if err != nil { + // it will be wrapped in readwrite.Write . + return nil, perrors.WithStack(err) + } + svc.Timeout = time.Duration(timeout) + + header := impl.DubboHeader{} + serialization := invocation.AttachmentsByKey(constant.SERIALIZATION_KEY, constant.HESSIAN2_SERIALIZATION) + if serialization == constant.PROTOBUF_SERIALIZATION { + header.SerialID = constant.S_Proto + } else { + header.SerialID = constant.S_Hessian2 + } + header.ID = request.ID + if request.TwoWay { + header.Type = impl.PackageRequest_TwoWay + } else { + header.Type = impl.PackageRequest + } + + pkg := &impl.DubboPackage{ + Header: header, + Service: svc, + Body: impl.NewRequestPayload(invocation.Arguments(), invocation.Attachments()), + Err: nil, + Codec: impl.NewDubboCodec(nil), + } + + if err := impl.LoadSerializer(pkg); err != nil { + return nil, perrors.WithStack(err) + } + + return pkg.Marshal() } + +// encode heartbeart request func (c *DubboTestCodec) encodeHeartbeartReqeust(request *remoting.Request) (*bytes.Buffer, error) { - return &bytes.Buffer{}, nil + header := impl.DubboHeader{ + Type: impl.PackageHeartbeat, + SerialID: constant.S_Hessian2, + ID: request.ID, + } + + pkg := &impl.DubboPackage{ + Header: header, + Service: impl.Service{}, + Body: impl.NewRequestPayload([]interface{}{}, nil), + Err: nil, + Codec: impl.NewDubboCodec(nil), + } + + if err := impl.LoadSerializer(pkg); err != nil { + return nil, err + } + return pkg.Marshal() } + +// encode response func (c *DubboTestCodec) EncodeResponse(response *remoting.Response) (*bytes.Buffer, error) { - return &bytes.Buffer{}, nil + var ptype = impl.PackageResponse + if response.IsHeartbeat() { + ptype = impl.PackageHeartbeat + } + resp := &impl.DubboPackage{ + Header: impl.DubboHeader{ + SerialID: response.SerialID, + Type: ptype, + ID: response.ID, + ResponseStatus: response.Status, + }, + } + if !response.IsHeartbeat() { + resp.Body = &impl.ResponsePayload{ + RspObj: response.Result.(protocol.RPCResult).Rest, + Exception: response.Result.(protocol.RPCResult).Err, + Attachments: response.Result.(protocol.RPCResult).Attrs, + } + } + + codec := impl.NewDubboCodec(nil) + + pkg, err := codec.Encode(*resp) + if err != nil { + return nil, perrors.WithStack(err) + } + + return bytes.NewBuffer(pkg), nil } + +// Decode data, including request and response. func (c *DubboTestCodec) Decode(data []byte) (remoting.DecodeResult, int, error) { - return remoting.DecodeResult{}, 0, nil + if c.isRequest(data) { + req, len, err := c.decodeRequest(data) + if err != nil { + return remoting.DecodeResult{}, len, perrors.WithStack(err) + } + return remoting.DecodeResult{IsRequest: true, Result: req}, len, perrors.WithStack(err) + } else { + resp, len, err := c.decodeResponse(data) + if err != nil { + return remoting.DecodeResult{}, len, perrors.WithStack(err) + } + return remoting.DecodeResult{IsRequest: false, Result: resp}, len, perrors.WithStack(err) + } } + func (c *DubboTestCodec) isRequest(data []byte) bool { + if data[2]&byte(0x80) == 0x00 { + return false + } return true } +// decode request func (c *DubboTestCodec) decodeRequest(data []byte) (*remoting.Request, int, error) { - return nil, 0, nil + var request *remoting.Request = nil + buf := bytes.NewBuffer(data) + pkg := impl.NewDubboPackage(buf) + pkg.SetBody(make([]interface{}, 7)) + err := pkg.Unmarshal() + if err != nil { + originErr := perrors.Cause(err) + if originErr == hessian.ErrHeaderNotEnough || originErr == hessian.ErrBodyNotEnough { + //FIXME + return nil, 0, originErr + } + return request, 0, perrors.WithStack(err) + } + request = &remoting.Request{ + ID: pkg.Header.ID, + SerialID: pkg.Header.SerialID, + TwoWay: pkg.Header.Type&impl.PackageRequest_TwoWay != 0x00, + Event: pkg.Header.Type&impl.PackageHeartbeat != 0x00, + } + if (pkg.Header.Type & impl.PackageHeartbeat) == 0x00 { + // convert params of request + req := pkg.Body.(map[string]interface{}) + + //invocation := request.Data.(*invocation.RPCInvocation) + var methodName string + var args []interface{} + var attachments map[string]string = make(map[string]string) + if req[impl.DubboVersionKey] != nil { + //dubbo version + request.Version = req[impl.DubboVersionKey].(string) + } + //path + attachments[constant.PATH_KEY] = pkg.Service.Path + //version + attachments[constant.VERSION_KEY] = pkg.Service.Version + //method + methodName = pkg.Service.Method + args = req[impl.ArgsKey].([]interface{}) + attachments = req[impl.AttachmentsKey].(map[string]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 *DubboTestCodec) decodeResponse(data []byte) (*remoting.Response, int, error) { - return nil, 0, nil + buf := bytes.NewBuffer(data) + pkg := impl.NewDubboPackage(buf) + response := &remoting.Response{} + err := pkg.Unmarshal() + if err != nil { + originErr := perrors.Cause(err) + // if the data is very big, so the receive need much times. + if originErr == hessian.ErrHeaderNotEnough || originErr == hessian.ErrBodyNotEnough { + return nil, 0, originErr + } + return nil, 0, perrors.WithStack(err) + } + response = &remoting.Response{ + ID: pkg.Header.ID, + //Version: pkg.Header., + SerialID: pkg.Header.SerialID, + Status: pkg.Header.ResponseStatus, + Event: (pkg.Header.Type & impl.PackageHeartbeat) != 0, + } + var error error + if pkg.Header.Type&impl.PackageHeartbeat != 0x00 { + if pkg.Header.Type&impl.PackageResponse != 0x00 { + if pkg.Err != nil { + error = pkg.Err + } + } else { + response.Status = hessian.Response_OK + //reply(session, p, hessian.PackageHeartbeat) + } + return response, hessian.HEADER_LENGTH + pkg.Header.BodyLen, error + } + rpcResult := &protocol.RPCResult{} + response.Result = rpcResult + if pkg.Header.Type&impl.PackageRequest == 0x00 { + if pkg.Err != nil { + rpcResult.Err = pkg.Err + } else if pkg.Body.(*impl.ResponsePayload).Exception != nil { + rpcResult.Err = pkg.Body.(*impl.ResponsePayload).Exception + response.Error = rpcResult.Err + } + rpcResult.Attrs = pkg.Body.(*impl.ResponsePayload).Attachments + rpcResult.Rest = pkg.Body.(*impl.ResponsePayload).RspObj + } + + return response, hessian.HEADER_LENGTH + pkg.Header.BodyLen, nil } diff --git a/remoting/getty/getty_client_test.go b/remoting/getty/getty_client_test.go index 12726547d6..f9e94809a5 100644 --- a/remoting/getty/getty_client_test.go +++ b/remoting/getty/getty_client_test.go @@ -18,7 +18,6 @@ package getty import ( - "bytes" "context" "reflect" "sync" @@ -42,13 +41,17 @@ import ( "github.com/apache/dubbo-go/remoting" ) +const ( + DubboCodecForTestUserID = "DubboCodecForTestUserID" + DubboCodecForTestUserName = "DubboCodecForTestUserName" +) + 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() } @@ -106,15 +109,10 @@ func testClient_Call(t *testing.T, svr *Server, url common.URL, c *Client) { testGetUser5(t, c) testGetUser6(t, c) testGetUser61(t, c) - } -func testGetBigPkg(t *testing.T, c *Client) { - var ( - user *User - err error - ) - user = &User{} +func testGetBigPkg(t *testing.T, c *Client) { + user := &User{} request := remoting.NewRequest("2.0.2") invocation := createInvocation("GetBigPkg", nil, nil, []interface{}{[]interface{}{nil}, user}, []reflect.Value{reflect.ValueOf([]interface{}{nil}), reflect.ValueOf(user)}) @@ -126,17 +124,14 @@ func testGetBigPkg(t *testing.T, c *Client) { pendingResponse := remoting.NewPendingResponse(request.ID) pendingResponse.Reply = user remoting.AddPendingResponse(pendingResponse) - err = c.Request(request, 8*time.Second, pendingResponse) + err := c.Request(request, 8*time.Second, pendingResponse) assert.NoError(t, err) - assert.NotEqual(t, "", user.Id) - assert.NotEqual(t, "", user.Name) + assert.Equal(t, DubboCodecForTestUserID, user.Id) + assert.Equal(t, DubboCodecForTestUserName, user.Name) } + func testGetUser(t *testing.T, c *Client) { - var ( - user *User - err error - ) - user = &User{} + user := &User{} request := remoting.NewRequest("2.0.2") invocation := createInvocation("GetUser", nil, nil, []interface{}{"1", "username"}, []reflect.Value{reflect.ValueOf("1"), reflect.ValueOf("username")}) @@ -148,7 +143,7 @@ func testGetUser(t *testing.T, c *Client) { pendingResponse := remoting.NewPendingResponse(request.ID) pendingResponse.Reply = user remoting.AddPendingResponse(pendingResponse) - err = c.Request(request, 3*time.Second, pendingResponse) + err := c.Request(request, 3*time.Second, pendingResponse) assert.NoError(t, err) assert.Equal(t, User{Id: "1", Name: "username"}, *user) } @@ -175,6 +170,7 @@ func testGetUser0(t *testing.T, c *Client) { assert.NoError(t, err) assert.Equal(t, User{Id: "1", Name: "username"}, *user) } + func testGetUser1(t *testing.T, c *Client) { var ( err error @@ -194,6 +190,7 @@ func testGetUser1(t *testing.T, c *Client) { err = c.Request(request, 3*time.Second, pendingResponse) assert.NoError(t, err) } + func testGetUser2(t *testing.T, c *Client) { var ( err error @@ -211,6 +208,7 @@ func testGetUser2(t *testing.T, c *Client) { err = c.Request(request, 3*time.Second, pendingResponse) assert.EqualError(t, err, "error") } + func testGetUser3(t *testing.T, c *Client) { var ( err error @@ -231,6 +229,7 @@ func testGetUser3(t *testing.T, c *Client) { assert.NoError(t, err) assert.Equal(t, &User{Id: "1", Name: "username"}, user2[0]) } + func testGetUser4(t *testing.T, c *Client) { var ( err error @@ -439,13 +438,8 @@ type ( // 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() + rsp.Id = DubboCodecForTestUserID + rsp.Name = DubboCodecForTestUserName return nil } From f9f1d7a6e4edd92b4eaf6c44dc89e801bf3f05f6 Mon Sep 17 00:00:00 2001 From: haohongfan Date: Sat, 5 Sep 2020 21:38:38 +0800 Subject: [PATCH 33/44] feat: format import --- protocol/dubbo/dubbo_protocol_test.go | 4 ++-- remoting/getty/dubbo_codec_for_test.go | 15 +++++++++------ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/protocol/dubbo/dubbo_protocol_test.go b/protocol/dubbo/dubbo_protocol_test.go index 06d4aece20..01d9a42861 100644 --- a/protocol/dubbo/dubbo_protocol_test.go +++ b/protocol/dubbo/dubbo_protocol_test.go @@ -18,7 +18,6 @@ package dubbo import ( - "github.com/apache/dubbo-go/common/proxy/proxy_factory" "testing" ) @@ -29,12 +28,13 @@ import ( import ( "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common/constant" + "github.com/apache/dubbo-go/common/proxy/proxy_factory" "github.com/apache/dubbo-go/protocol" "github.com/apache/dubbo-go/remoting/getty" ) const ( - mockCommonUrl = "dubbo://127.0.0.1:30000/com.ikurento.user.UserProvider?anyhost=true&" + + mockCommonUrl = "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&" + "application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&" + "environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&" + "module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&" + diff --git a/remoting/getty/dubbo_codec_for_test.go b/remoting/getty/dubbo_codec_for_test.go index 6ae7117f56..bde7d9e696 100644 --- a/remoting/getty/dubbo_codec_for_test.go +++ b/remoting/getty/dubbo_codec_for_test.go @@ -20,17 +20,20 @@ package getty // it is used to unit test. import ( "bytes" - hessian "github.com/apache/dubbo-go-hessian2" - "github.com/apache/dubbo-go/common/constant" - "github.com/apache/dubbo-go/protocol" - "github.com/apache/dubbo-go/protocol/dubbo/impl" - "github.com/apache/dubbo-go/protocol/invocation" - perrors "github.com/pkg/errors" "strconv" "time" ) import ( + hessian "github.com/apache/dubbo-go-hessian2" + perrors "github.com/pkg/errors" +) + +import ( + "github.com/apache/dubbo-go/common/constant" + "github.com/apache/dubbo-go/protocol" + "github.com/apache/dubbo-go/protocol/dubbo/impl" + "github.com/apache/dubbo-go/protocol/invocation" "github.com/apache/dubbo-go/remoting" ) From 2d8d2f027c5d21beae2132e1eb1b055257b9ff0c Mon Sep 17 00:00:00 2001 From: haohongfan Date: Sat, 5 Sep 2020 22:32:53 +0800 Subject: [PATCH 34/44] feat: rest GetBigPkg --- remoting/getty/getty_client_test.go | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/remoting/getty/getty_client_test.go b/remoting/getty/getty_client_test.go index f9e94809a5..41ca3108a8 100644 --- a/remoting/getty/getty_client_test.go +++ b/remoting/getty/getty_client_test.go @@ -18,6 +18,7 @@ package getty import ( + "bytes" "context" "reflect" "sync" @@ -41,11 +42,6 @@ import ( "github.com/apache/dubbo-go/remoting" ) -const ( - DubboCodecForTestUserID = "DubboCodecForTestUserID" - DubboCodecForTestUserName = "DubboCodecForTestUserName" -) - func TestRunSuite(t *testing.T) { svr, url := InitTest(t) client := getClient(url) @@ -126,8 +122,8 @@ func testGetBigPkg(t *testing.T, c *Client) { remoting.AddPendingResponse(pendingResponse) err := c.Request(request, 8*time.Second, pendingResponse) assert.NoError(t, err) - assert.Equal(t, DubboCodecForTestUserID, user.Id) - assert.Equal(t, DubboCodecForTestUserName, user.Name) + assert.NotEqual(t, "", user.Id) + assert.NotEqual(t, "", user.Name) } func testGetUser(t *testing.T, c *Client) { @@ -438,8 +434,13 @@ type ( // size:4801228 func (u *UserProvider) GetBigPkg(ctx context.Context, req []interface{}, rsp *User) error { - rsp.Id = DubboCodecForTestUserID - rsp.Name = DubboCodecForTestUserName + argBuf := new(bytes.Buffer) + for i := 0; i < 400; i++ { + argBuf.WriteString("击鼓其镗,踊跃用兵。土国城漕,我独南行。从孙子仲,平陈与宋。不我以归,忧心有忡。爰居爰处?爰丧其马?于以求之?于林之下。死生契阔,与子成说。执子之手,与子偕老。于嗟阔兮,不我活兮。于嗟洵兮,不我信兮。") + argBuf.WriteString("击鼓其镗,踊跃用兵。土国城漕,我独南行。从孙子仲,平陈与宋。不我以归,忧心有忡。爰居爰处?爰丧其马?于以求之?于林之下。死生契阔,与子成说。执子之手,与子偕老。于嗟阔兮,不我活兮。于嗟洵兮,不我信兮。") + } + rsp.Id = argBuf.String() + rsp.Name = argBuf.String() return nil } From e7191006158eb42d8657815f2c87b82dd9cfa742 Mon Sep 17 00:00:00 2001 From: fangyincheng Date: Sat, 5 Sep 2020 23:45:14 +0800 Subject: [PATCH 35/44] Fix:bug --- remoting/getty/readwriter.go | 1 + 1 file changed, 1 insertion(+) diff --git a/remoting/getty/readwriter.go b/remoting/getty/readwriter.go index ae96f35391..26623edd09 100644 --- a/remoting/getty/readwriter.go +++ b/remoting/getty/readwriter.go @@ -50,6 +50,7 @@ func (p *RpcClientPackageHandler) Read(ss getty.Session, data []byte) (interface resp, length, err := (p.client.codec).Decode(data) //err := pkg.Unmarshal(buf, p.client) if err != nil { + err = perrors.Cause(err) if err == hessian.ErrHeaderNotEnough || err == hessian.ErrBodyNotEnough { return nil, 0, nil } From 05f53c5b065263cb2ed627fc9fcba446894d1318 Mon Sep 17 00:00:00 2001 From: fangyincheng Date: Sat, 5 Sep 2020 23:52:21 +0800 Subject: [PATCH 36/44] fix comments --- protocol/dubbo/dubbo_protocol_test.go | 2 +- protocol/invocation/rpcinvocation.go | 2 +- protocol/jsonrpc/http_test.go | 8 ++++++-- protocol/jsonrpc/jsonrpc_invoker.go | 2 ++ 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/protocol/dubbo/dubbo_protocol_test.go b/protocol/dubbo/dubbo_protocol_test.go index 01d9a42861..9eba90e9da 100644 --- a/protocol/dubbo/dubbo_protocol_test.go +++ b/protocol/dubbo/dubbo_protocol_test.go @@ -118,7 +118,7 @@ func TestDubboProtocol_Export(t *testing.T) { assert.False(t, ok) } -func TestDubboProtocol_Refer_No_connect(t *testing.T) { +func TestDubboProtocolReferNoConnect(t *testing.T) { // Refer initDubboInvokerTest() proto := GetProtocol() diff --git a/protocol/invocation/rpcinvocation.go b/protocol/invocation/rpcinvocation.go index 477ebd79ac..103bf71f74 100644 --- a/protocol/invocation/rpcinvocation.go +++ b/protocol/invocation/rpcinvocation.go @@ -18,12 +18,12 @@ package invocation import ( - "github.com/apache/dubbo-go/common" "reflect" "sync" ) import ( + "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/protocol" ) diff --git a/protocol/jsonrpc/http_test.go b/protocol/jsonrpc/http_test.go index ae97cf5292..4a9645e828 100644 --- a/protocol/jsonrpc/http_test.go +++ b/protocol/jsonrpc/http_test.go @@ -22,15 +22,19 @@ import ( "strings" "testing" "time" +) +import ( "github.com/opentracing/opentracing-go" + perrors "github.com/pkg/errors" + "github.com/stretchr/testify/assert" +) +import ( "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/common/proxy/proxy_factory" "github.com/apache/dubbo-go/protocol" - perrors "github.com/pkg/errors" - "github.com/stretchr/testify/assert" ) type ( diff --git a/protocol/jsonrpc/jsonrpc_invoker.go b/protocol/jsonrpc/jsonrpc_invoker.go index 6b3df4a527..970d5f9a08 100644 --- a/protocol/jsonrpc/jsonrpc_invoker.go +++ b/protocol/jsonrpc/jsonrpc_invoker.go @@ -19,7 +19,9 @@ package jsonrpc import ( "context" +) +import ( "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/common/logger" From b33f44d15cbca125f67e5ccf5bdfb1285cc59e79 Mon Sep 17 00:00:00 2001 From: fangyincheng Date: Mon, 7 Sep 2020 02:02:09 +0800 Subject: [PATCH 37/44] fix comments --- common/url.go | 4 +-- .../exporter/configurable/exporter_test.go | 2 +- protocol/dubbo/dubbo_codec.go | 2 +- protocol/dubbo/impl/codec.go | 30 +++++++++---------- remoting/getty/listener.go | 2 +- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/common/url.go b/common/url.go index c343b9c9aa..35e56fef72 100644 --- a/common/url.go +++ b/common/url.go @@ -690,14 +690,14 @@ func mergeNormalParam(mergedUrl *URL, referenceUrl *URL, paramKeys []string) []f return methodConfigMergeFcn } -// doesn't encode url reserve character, url.QueryEscape will do this work +// ParamsUnescapeEncode 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)) + keys := make([]string, 0, len(params)) for k := range params { keys = append(keys, k) } diff --git a/metadata/service/exporter/configurable/exporter_test.go b/metadata/service/exporter/configurable/exporter_test.go index b85e0ac2ac..b304b9153f 100644 --- a/metadata/service/exporter/configurable/exporter_test.go +++ b/metadata/service/exporter/configurable/exporter_test.go @@ -18,7 +18,6 @@ package configurable import ( - "github.com/apache/dubbo-go/remoting/getty" "testing" ) @@ -32,6 +31,7 @@ import ( _ "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/remoting/getty" ) func TestConfigurableExporter(t *testing.T) { diff --git a/protocol/dubbo/dubbo_codec.go b/protocol/dubbo/dubbo_codec.go index dbd4c48a69..922a0bf983 100644 --- a/protocol/dubbo/dubbo_codec.go +++ b/protocol/dubbo/dubbo_codec.go @@ -18,7 +18,6 @@ package dubbo import ( "bytes" - "github.com/apache/dubbo-go/protocol/dubbo/impl" "strconv" "time" ) @@ -32,6 +31,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" "github.com/apache/dubbo-go/protocol/invocation" "github.com/apache/dubbo-go/remoting" ) diff --git a/protocol/dubbo/impl/codec.go b/protocol/dubbo/impl/codec.go index 40866d9642..c139f3547b 100644 --- a/protocol/dubbo/impl/codec.go +++ b/protocol/dubbo/impl/codec.go @@ -20,17 +20,17 @@ package impl import ( "bufio" "encoding/binary" - "github.com/apache/dubbo-go/common/constant" - "github.com/apache/dubbo-go/remoting" ) import ( hessian "github.com/apache/dubbo-go-hessian2" - "github.com/pkg/errors" + perrors "github.com/pkg/errors" ) import ( + "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/common/logger" + "github.com/apache/dubbo-go/remoting" ) type ProtocolCodec struct { @@ -48,11 +48,11 @@ func (c *ProtocolCodec) ReadHeader(header *DubboHeader) error { } buf, err := c.reader.Peek(HEADER_LENGTH) if err != nil { // this is impossible - return errors.WithStack(err) + return perrors.WithStack(err) } _, err = c.reader.Discard(HEADER_LENGTH) if err != nil { // this is impossible - return errors.WithStack(err) + return perrors.WithStack(err) } //// read header @@ -62,7 +62,7 @@ func (c *ProtocolCodec) ReadHeader(header *DubboHeader) error { // 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) + return perrors.Errorf("serialization ID:%v", header.SerialID) } flag := buf[2] & FLAG_EVENT @@ -100,7 +100,7 @@ func (c *ProtocolCodec) ReadHeader(header *DubboHeader) error { return hessian.ErrBodyNotEnough } c.headerRead = true - return errors.WithStack(err) + return perrors.WithStack(err) } func (c *ProtocolCodec) EncodeHeader(p DubboPackage) []byte { @@ -129,7 +129,7 @@ func (c *ProtocolCodec) EncodeHeader(p DubboPackage) []byte { func (c *ProtocolCodec) Encode(p DubboPackage) ([]byte, error) { // header if c.serializer == nil { - return nil, errors.New("serializer should not be nil") + return nil, perrors.New("serializer should not be nil") } header := p.Header switch header.Type { @@ -146,7 +146,7 @@ func (c *ProtocolCodec) Encode(p DubboPackage) ([]byte, error) { return packResponse(p, c.serializer) default: - return nil, errors.Errorf("Unrecognised message type: %v", header.Type) + return nil, perrors.Errorf("Unrecognised message type: %v", header.Type) } } @@ -165,20 +165,20 @@ func (c *ProtocolCodec) Decode(p *DubboPackage) error { decoder := hessian.NewDecoder(body) exception, err := decoder.Decode() if err != nil { - return errors.WithStack(err) + return perrors.WithStack(err) } rsp, ok := p.Body.(*ResponsePayload) if !ok { - return errors.Errorf("java exception:%s", exception.(string)) + return perrors.Errorf("java exception:%s", exception.(string)) } - rsp.Exception = errors.Errorf("java exception:%s", exception.(string)) + rsp.Exception = perrors.Errorf("java exception:%s", exception.(string)) return nil } else if p.IsHeartBeat() { // heartbeat no need to unmarshal contents return nil } if c.serializer == nil { - return errors.New("Codec serializer is nil") + return perrors.New("Codec serializer is nil") } if p.IsResponse() { p.Body = &ResponsePayload{ @@ -232,7 +232,7 @@ func packRequest(p DubboPackage, serializer Serializer) ([]byte, error) { } pkgLen = len(body) if pkgLen > int(DEFAULT_LEN) { // 8M - return nil, errors.Errorf("Data length %d too large, max payload %d", pkgLen, DEFAULT_LEN) + return nil, perrors.Errorf("Data length %d too large, max payload %d", pkgLen, DEFAULT_LEN) } byteArray = append(byteArray, body...) } @@ -271,7 +271,7 @@ func packResponse(p DubboPackage, serializer Serializer) ([]byte, error) { pkgLen := len(body) if pkgLen > int(DEFAULT_LEN) { // 8M - return nil, errors.Errorf("Data length %d too large, max payload %d", pkgLen, DEFAULT_LEN) + return nil, perrors.Errorf("Data length %d too large, max payload %d", pkgLen, DEFAULT_LEN) } // byteArray{body length} binary.BigEndian.PutUint32(byteArray[12:], uint32(pkgLen)) diff --git a/remoting/getty/listener.go b/remoting/getty/listener.go index 198ec51daa..8d1a63f1c4 100644 --- a/remoting/getty/listener.go +++ b/remoting/getty/listener.go @@ -160,7 +160,7 @@ func (h *RpcClientHandler) OnCron(session getty.Session) { // RpcServerHandler // ////////////////////////////////////////// -// RpcServerHandler ... +// RpcServerHandler implement EventListener of getty. type RpcServerHandler struct { maxSessionNum int sessionTimeout time.Duration From 6e8281fc67227d51c5aed1b36aa627d93a150d69 Mon Sep 17 00:00:00 2001 From: fangyincheng Date: Sat, 12 Sep 2020 16:36:40 +0800 Subject: [PATCH 38/44] Fix: fixed some comments --- common/url.go | 27 -- config/application_config.go | 10 - protocol/dubbo/dubbo_codec.go | 5 +- protocol/dubbo/dubbo_invoker.go | 4 +- protocol/dubbo/dubbo_protocol.go | 6 +- protocol/dubbo/impl/codec_test.go | 113 ------ protocol/dubbo/impl/proto.go | 454 ------------------------ protocol/dubbo/impl/proto/payload.pb.go | 345 ------------------ protocol/dubbo/impl/proto/payload.proto | 78 ---- remoting/getty/getty_server.go | 6 +- remoting/getty/listener_test.go | 12 +- test/integrate/dubbo/go-client/go.sum | 10 + test/integrate/dubbo/go-server/go.sum | 10 + 13 files changed, 42 insertions(+), 1038 deletions(-) delete mode 100644 protocol/dubbo/impl/proto.go delete mode 100644 protocol/dubbo/impl/proto/payload.pb.go delete mode 100644 protocol/dubbo/impl/proto/payload.proto create mode 100644 test/integrate/dubbo/go-client/go.sum create mode 100644 test/integrate/dubbo/go-server/go.sum diff --git a/common/url.go b/common/url.go index 35e56fef72..8354fda781 100644 --- a/common/url.go +++ b/common/url.go @@ -24,7 +24,6 @@ import ( "math" "net" "net/url" - "sort" "strconv" "strings" ) @@ -690,32 +689,6 @@ func mergeNormalParam(mergedUrl *URL, referenceUrl *URL, paramKeys []string) []f return methodConfigMergeFcn } -// ParamsUnescapeEncode 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, 0, 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 diff --git a/config/application_config.go b/config/application_config.go index 2b9b91c3b3..ef99664fa2 100644 --- a/config/application_config.go +++ b/config/application_config.go @@ -42,16 +42,6 @@ func (*ApplicationConfig) Prefix() string { return constant.DUBBO + ".application." } -// nolint -func (c *ApplicationConfig) Id() string { - return "" -} - -// SetId ... -func (c *ApplicationConfig) SetId(id string) { - -} - // UnmarshalYAML unmarshals the ApplicationConfig by @unmarshal function func (c *ApplicationConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { if err := defaults.Set(c); err != nil { diff --git a/protocol/dubbo/dubbo_codec.go b/protocol/dubbo/dubbo_codec.go index 922a0bf983..673588ef24 100644 --- a/protocol/dubbo/dubbo_codec.go +++ b/protocol/dubbo/dubbo_codec.go @@ -57,8 +57,9 @@ func (c *DubboCodec) EncodeRequest(request *remoting.Request) (*bytes.Buffer, er 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) + err := perrors.Errorf("encode request failed for parameter type :%+v", request) + logger.Errorf(err.Error()) + return nil, err } invocation := *invoc diff --git a/protocol/dubbo/dubbo_invoker.go b/protocol/dubbo/dubbo_invoker.go index fa7b0f8cc0..bce33508be 100644 --- a/protocol/dubbo/dubbo_invoker.go +++ b/protocol/dubbo/dubbo_invoker.go @@ -111,8 +111,8 @@ func (di *DubboInvoker) Invoke(ctx context.Context, invocation protocol.Invocati url := di.GetUrl() // default hessian2 serialization, compatible - if url.GetParam("serialization", "") == "" { - url.SetParam("serialization", constant.HESSIAN2_SERIALIZATION) + if url.GetParam(constant.SERIALIZATION_KEY, "") == "" { + url.SetParam(constant.SERIALIZATION_KEY, constant.HESSIAN2_SERIALIZATION) } // async async, err := strconv.ParseBool(inv.AttachmentsByKey(constant.ASYNC_KEY, "false")) diff --git a/protocol/dubbo/dubbo_protocol.go b/protocol/dubbo/dubbo_protocol.go index a92b9fc715..2826e64d92 100644 --- a/protocol/dubbo/dubbo_protocol.go +++ b/protocol/dubbo/dubbo_protocol.go @@ -69,7 +69,7 @@ type DubboProtocol struct { serverLock sync.Mutex } -// nolint +// NewDubboProtocol create a dubbo protocol. func NewDubboProtocol() *DubboProtocol { return &DubboProtocol{ BaseProtocol: protocol.NewBaseProtocol(), @@ -77,7 +77,7 @@ func NewDubboProtocol() *DubboProtocol { } } -// nolint +// Export export dubbo service. func (dp *DubboProtocol) Export(invoker protocol.Invoker) protocol.Exporter { url := invoker.GetUrl() serviceKey := url.ServiceKey() @@ -236,7 +236,7 @@ func rebuildCtx(inv *invocation.RPCInvocation) context.Context { // 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())) + opentracing.TextMapCarrier(filterContext(inv.Attachments()))) if err == nil { ctx = context.WithValue(ctx, constant.TRACING_REMOTE_SPAN_CTX, spanCtx) } diff --git a/protocol/dubbo/impl/codec_test.go b/protocol/dubbo/impl/codec_test.go index 77cc69a742..03e768dacd 100644 --- a/protocol/dubbo/impl/codec_test.go +++ b/protocol/dubbo/impl/codec_test.go @@ -80,116 +80,3 @@ func TestDubboPackage_MarshalAndUnmarshal(t *testing.T) { 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, true, ok) -// sv := pb.StringValue{} -// buf := proto.NewBuffer(argsBytes) -// err = buf.Unmarshal(&sv) -// assert.NoError(t, err) -// assert.Equal(t, "hello world", sv.Value) -//} - -//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/proto.go b/protocol/dubbo/impl/proto.go deleted file mode 100644 index 2622620b39..0000000000 --- a/protocol/dubbo/impl/proto.go +++ /dev/null @@ -1,454 +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 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) - 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 { - 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 --git a/protocol/dubbo/impl/proto/payload.pb.go b/protocol/dubbo/impl/proto/payload.pb.go deleted file mode 100644 index 337027e86d..0000000000 --- a/protocol/dubbo/impl/proto/payload.pb.go +++ /dev/null @@ -1,345 +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. - */ -// 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 deleted file mode 100644 index 19f644ee91..0000000000 --- a/protocol/dubbo/impl/proto/payload.proto +++ /dev/null @@ -1,78 +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. - */ - -syntax = "proto3"; - -// equivalent java StringValue -message StringValue { - string value = 1; -} - -// equivalent java Int32Value -message Int32Value { - int32 value = 1; -} - -// equivalent java MapValue -message Map { - map 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/remoting/getty/getty_server.go b/remoting/getty/getty_server.go index 731ad87952..98ca690cbf 100644 --- a/remoting/getty/getty_server.go +++ b/remoting/getty/getty_server.go @@ -79,7 +79,7 @@ func initServer(protocol string) { SetServerGrpool() } -// SetServerConfig ... +// SetServerConfig set dubbo server config. func SetServerConfig(s ServerConfig) { srvConf = &s err := srvConf.CheckValidity() @@ -90,7 +90,7 @@ func SetServerConfig(s ServerConfig) { SetServerGrpool() } -// GetServerConfig ... +// GetServerConfig get getty server config. func GetServerConfig() ServerConfig { return *srvConf } @@ -184,7 +184,7 @@ func (s *Server) newSession(session getty.Session) error { return nil } -// Start ... +// Start dubbo server. func (s *Server) Start() { var ( addr string diff --git a/remoting/getty/listener_test.go b/remoting/getty/listener_test.go index 3d1787a334..7a54323d19 100644 --- a/remoting/getty/listener_test.go +++ b/remoting/getty/listener_test.go @@ -65,9 +65,19 @@ func rebuildCtx(inv *invocation.RPCInvocation) context.Context { // 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())) + opentracing.TextMapCarrier(filterContext(inv.Attachments()))) if err == nil { ctx = context.WithValue(ctx, constant.TRACING_REMOTE_SPAN_CTX, spanCtx) } return ctx } + +func filterContext(attachments map[string]interface{}) map[string]string { + var traceAttchment = make(map[string]string) + for k, v := range attachments { + if r, ok := v.(string); ok { + traceAttchment[k] = r + } + } + return traceAttchment +} diff --git a/test/integrate/dubbo/go-client/go.sum b/test/integrate/dubbo/go-client/go.sum new file mode 100644 index 0000000000..7bb51161b1 --- /dev/null +++ b/test/integrate/dubbo/go-client/go.sum @@ -0,0 +1,10 @@ +github.com/apache/dubbo-go-hessian2 v1.6.0-rc1.0.20200906044240-6c1fb5c3bd44/go.mod h1:7rEw9guWABQa6Aqb8HeZcsYPHsOS7XT1qtJvkmI6c5w= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dubbogo/gost v1.9.0/go.mod h1:pPTjVyoJan3aPxBPNUX0ADkXjPibLo+/Ib0/fADXSG8= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/test/integrate/dubbo/go-server/go.sum b/test/integrate/dubbo/go-server/go.sum new file mode 100644 index 0000000000..7bb51161b1 --- /dev/null +++ b/test/integrate/dubbo/go-server/go.sum @@ -0,0 +1,10 @@ +github.com/apache/dubbo-go-hessian2 v1.6.0-rc1.0.20200906044240-6c1fb5c3bd44/go.mod h1:7rEw9guWABQa6Aqb8HeZcsYPHsOS7XT1qtJvkmI6c5w= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dubbogo/gost v1.9.0/go.mod h1:pPTjVyoJan3aPxBPNUX0ADkXjPibLo+/Ib0/fADXSG8= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= From 89db192b8a02e71ea5a54bde1d54b93cef192d92 Mon Sep 17 00:00:00 2001 From: haohongfan Date: Sat, 12 Sep 2020 23:59:07 +0800 Subject: [PATCH 39/44] fix: resove dev RPCResult struct change --- protocol/dubbo/dubbo_codec.go | 4 +-- protocol/dubbo/impl/codec_test.go | 9 +++++- protocol/dubbo/impl/hessian.go | 42 ++++++++++++++++++-------- protocol/dubbo/impl/request.go | 6 ++-- protocol/dubbo/impl/response.go | 6 ++-- remoting/getty/dubbo_codec_for_test.go | 4 +-- 6 files changed, 48 insertions(+), 23 deletions(-) diff --git a/protocol/dubbo/dubbo_codec.go b/protocol/dubbo/dubbo_codec.go index 673588ef24..b84952a125 100644 --- a/protocol/dubbo/dubbo_codec.go +++ b/protocol/dubbo/dubbo_codec.go @@ -213,7 +213,7 @@ func (c *DubboCodec) decodeRequest(data []byte) (*remoting.Request, int, error) //invocation := request.Data.(*invocation.RPCInvocation) var methodName string var args []interface{} - var attachments map[string]string = make(map[string]string) + attachments := make(map[string]interface{}) if req[impl.DubboVersionKey] != nil { //dubbo version request.Version = req[impl.DubboVersionKey].(string) @@ -225,7 +225,7 @@ func (c *DubboCodec) decodeRequest(data []byte) (*remoting.Request, int, error) //method methodName = pkg.Service.Method args = req[impl.ArgsKey].([]interface{}) - attachments = req[impl.AttachmentsKey].(map[string]string) + attachments = req[impl.AttachmentsKey].(map[string]interface{}) invoc := invocation.NewRPCInvocationWithOptions(invocation.WithAttachments(attachments), invocation.WithArguments(args), invocation.WithMethodName(methodName)) request.Data = invoc diff --git a/protocol/dubbo/impl/codec_test.go b/protocol/dubbo/impl/codec_test.go index 03e768dacd..1c37928238 100644 --- a/protocol/dubbo/impl/codec_test.go +++ b/protocol/dubbo/impl/codec_test.go @@ -78,5 +78,12 @@ func TestDubboPackage_MarshalAndUnmarshal(t *testing.T) { 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)) + tmpData := map[string]interface{}{ + "dubbo": "2.0.2", + "interface": "Service", + "path": "path", + "timeout": "1000", + "version": "2.6", + } + assert.Equal(t, tmpData, reassembleBody["attachments"]) } diff --git a/protocol/dubbo/impl/hessian.go b/protocol/dubbo/impl/hessian.go index 513421b95f..b686d5728d 100644 --- a/protocol/dubbo/impl/hessian.go +++ b/protocol/dubbo/impl/hessian.go @@ -67,7 +67,11 @@ func marshalResponse(encoder *hessian.Encoder, p DubboPackage) ([]byte, error) { if p.IsHeartBeat() { encoder.Encode(nil) } else { - atta := isSupportResponseAttachment(response.Attachments[DUBBO_VERSION_KEY]) + var version string + if attachmentVersion, ok := response.Attachments[DUBBO_VERSION_KEY]; ok { + version = attachmentVersion.(string) + } + atta := isSupportResponseAttachment(version) var resWithException, resValue, resNullValue int32 if atta { @@ -255,7 +259,7 @@ func unmarshalRequestBody(body []byte, p *DubboPackage) error { if v, ok := attachments.(map[interface{}]interface{}); ok { v[DUBBO_VERSION_KEY] = dubboVersion - req[6] = hessian.ToMapStringString(v) + req[6] = ToMapStringInterface(v) buildServerSidePackageBody(p) return nil } @@ -285,7 +289,7 @@ func unmarshalResponseBody(body []byte, p *DubboPackage) error { return perrors.WithStack(err) } if v, ok := attachments.(map[interface{}]interface{}); ok { - atta := hessian.ToMapStringString(v) + atta := ToMapStringInterface(v) response.Attachments = atta } else { return perrors.Errorf("get wrong attachments: %+v", attachments) @@ -310,7 +314,7 @@ func unmarshalResponseBody(body []byte, p *DubboPackage) error { return perrors.WithStack(err) } if v, ok := attachments.(map[interface{}]interface{}); ok { - atta := hessian.ToMapStringString(v) + atta := ToMapStringInterface(v) response.Attachments = atta } else { return perrors.Errorf("get wrong attachments: %+v", attachments) @@ -326,7 +330,7 @@ func unmarshalResponseBody(body []byte, p *DubboPackage) error { return perrors.WithStack(err) } if v, ok := attachments.(map[interface{}]interface{}); ok { - atta := hessian.ToMapStringString(v) + atta := ToMapStringInterface(v) response.Attachments = atta } else { return perrors.Errorf("get wrong attachments: %+v", attachments) @@ -342,7 +346,7 @@ func buildServerSidePackageBody(pkg *DubboPackage) { if len(req) > 0 { var dubboVersion, argsTypes string var args []interface{} - var attachments map[string]string + var attachments map[string]interface{} svc := Service{} if req[0] != nil { dubboVersion = req[0].(string) @@ -363,18 +367,18 @@ func buildServerSidePackageBody(pkg *DubboPackage) { args = req[5].([]interface{}) } if req[6] != nil { - attachments = req[6].(map[string]string) + attachments = req[6].(map[string]interface{}) } - if svc.Path == "" && len(attachments[constant.PATH_KEY]) > 0 { - svc.Path = attachments[constant.PATH_KEY] + if svc.Path == "" && attachments[constant.PATH_KEY] != nil && len(attachments[constant.PATH_KEY].(string)) > 0 { + svc.Path = attachments[constant.PATH_KEY].(string) } if _, ok := attachments[constant.INTERFACE_KEY]; ok { - svc.Interface = attachments[constant.INTERFACE_KEY] + svc.Interface = attachments[constant.INTERFACE_KEY].(string) } else { svc.Interface = svc.Path } - if len(attachments[constant.GROUP_KEY]) > 0 { - svc.Group = attachments[constant.GROUP_KEY] + if _, ok := attachments[constant.GROUP_KEY]; ok { + svc.Group = attachments[constant.GROUP_KEY].(string) } pkg.SetService(svc) pkg.SetBody(map[string]interface{}{ @@ -503,6 +507,20 @@ func getArgType(v interface{}) string { // return "java.lang.RuntimeException" } +func ToMapStringInterface(origin map[interface{}]interface{}) map[string]interface{} { + dest := make(map[string]interface{}, len(origin)) + for k, v := range origin { + if kv, ok := k.(string); ok { + if v == nil { + dest[kv] = "" + continue + } + dest[kv] = v + } + } + return dest +} + func init() { SetSerializer("hessian2", HessianSerializer{}) } diff --git a/protocol/dubbo/impl/request.go b/protocol/dubbo/impl/request.go index 0e770c3afb..ef520083e6 100644 --- a/protocol/dubbo/impl/request.go +++ b/protocol/dubbo/impl/request.go @@ -19,12 +19,12 @@ package impl type RequestPayload struct { Params interface{} - Attachments map[string]string + Attachments map[string]interface{} } -func NewRequestPayload(args interface{}, atta map[string]string) *RequestPayload { +func NewRequestPayload(args interface{}, atta map[string]interface{}) *RequestPayload { if atta == nil { - atta = make(map[string]string) + atta = make(map[string]interface{}) } return &RequestPayload{ Params: args, diff --git a/protocol/dubbo/impl/response.go b/protocol/dubbo/impl/response.go index ea0a6efb23..9fde1eb249 100644 --- a/protocol/dubbo/impl/response.go +++ b/protocol/dubbo/impl/response.go @@ -20,13 +20,13 @@ package impl type ResponsePayload struct { RspObj interface{} Exception error - Attachments map[string]string + Attachments map[string]interface{} } // NewResponse create a new ResponsePayload -func NewResponsePayload(rspObj interface{}, exception error, attachments map[string]string) *ResponsePayload { +func NewResponsePayload(rspObj interface{}, exception error, attachments map[string]interface{}) *ResponsePayload { if attachments == nil { - attachments = make(map[string]string) + attachments = make(map[string]interface{}) } return &ResponsePayload{ RspObj: rspObj, diff --git a/remoting/getty/dubbo_codec_for_test.go b/remoting/getty/dubbo_codec_for_test.go index bde7d9e696..b91fc9f4cc 100644 --- a/remoting/getty/dubbo_codec_for_test.go +++ b/remoting/getty/dubbo_codec_for_test.go @@ -205,7 +205,7 @@ func (c *DubboTestCodec) decodeRequest(data []byte) (*remoting.Request, int, err //invocation := request.Data.(*invocation.RPCInvocation) var methodName string var args []interface{} - var attachments map[string]string = make(map[string]string) + attachments := make(map[string]interface{}) if req[impl.DubboVersionKey] != nil { //dubbo version request.Version = req[impl.DubboVersionKey].(string) @@ -217,7 +217,7 @@ func (c *DubboTestCodec) decodeRequest(data []byte) (*remoting.Request, int, err //method methodName = pkg.Service.Method args = req[impl.ArgsKey].([]interface{}) - attachments = req[impl.AttachmentsKey].(map[string]string) + attachments = req[impl.AttachmentsKey].(map[string]interface{}) invoc := invocation.NewRPCInvocationWithOptions(invocation.WithAttachments(attachments), invocation.WithArguments(args), invocation.WithMethodName(methodName)) request.Data = invoc From a35e2b2bee4fb6aa4d7e522ea8b9840fa8d6bfb6 Mon Sep 17 00:00:00 2001 From: haohongfan Date: Sun, 13 Sep 2020 15:56:27 +0800 Subject: [PATCH 40/44] fix: add injectTraceCtx to getty --- remoting/getty/listener_test.go | 14 ++------ remoting/getty/opentracing.go | 60 +++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 11 deletions(-) create mode 100644 remoting/getty/opentracing.go diff --git a/remoting/getty/listener_test.go b/remoting/getty/listener_test.go index 7a54323d19..7e7ac5fed4 100644 --- a/remoting/getty/listener_test.go +++ b/remoting/getty/listener_test.go @@ -48,7 +48,9 @@ func TestRebuildCtx(t *testing.T) { span, ctx := opentracing.StartSpanFromContext(ctx, "Test-Client") - injectTraceCtx(span, inv) + err := injectTraceCtx(span, inv) + assert.NoError(t, err) + // rebuild the context success inv = invocation.NewRPCInvocation("MethodName", []interface{}{"OK", "Hello"}, attach) ctx = rebuildCtx(inv) @@ -71,13 +73,3 @@ func rebuildCtx(inv *invocation.RPCInvocation) context.Context { } return ctx } - -func filterContext(attachments map[string]interface{}) map[string]string { - var traceAttchment = make(map[string]string) - for k, v := range attachments { - if r, ok := v.(string); ok { - traceAttchment[k] = r - } - } - return traceAttchment -} diff --git a/remoting/getty/opentracing.go b/remoting/getty/opentracing.go new file mode 100644 index 0000000000..7db733cbe9 --- /dev/null +++ b/remoting/getty/opentracing.go @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package getty + +import ( + "github.com/opentracing/opentracing-go" +) +import ( + invocation_impl "github.com/apache/dubbo-go/protocol/invocation" +) + +func injectTraceCtx(currentSpan opentracing.Span, inv *invocation_impl.RPCInvocation) error { + // inject opentracing ctx + traceAttachments := filterContext(inv.Attachments()) + carrier := opentracing.TextMapCarrier(traceAttachments) + err := opentracing.GlobalTracer().Inject(currentSpan.Context(), opentracing.TextMap, carrier) + if err == nil { + fillTraceAttachments(inv.Attachments(), traceAttachments) + } + return err +} + +func extractTraceCtx(inv *invocation_impl.RPCInvocation) (opentracing.SpanContext, error) { + traceAttachments := filterContext(inv.Attachments()) + // actually, if user do not use any opentracing framework, the err will not be nil. + spanCtx, err := opentracing.GlobalTracer().Extract(opentracing.TextMap, + opentracing.TextMapCarrier(traceAttachments)) + return spanCtx, err +} + +func filterContext(attachments map[string]interface{}) map[string]string { + var traceAttchment = make(map[string]string) + for k, v := range attachments { + if r, ok := v.(string); ok { + traceAttchment[k] = r + } + } + return traceAttchment +} + +func fillTraceAttachments(attachments map[string]interface{}, traceAttachment map[string]string) { + for k, v := range traceAttachment { + attachments[k] = v + } +} From d3667059a04c7b558e89606f0980ade8b3699d8e Mon Sep 17 00:00:00 2001 From: haohongfan Date: Mon, 14 Sep 2020 13:13:38 +0800 Subject: [PATCH 41/44] feat: format DubboCodec --- protocol/dubbo/dubbo_codec.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/protocol/dubbo/dubbo_codec.go b/protocol/dubbo/dubbo_codec.go index b84952a125..5e859a7fa2 100644 --- a/protocol/dubbo/dubbo_codec.go +++ b/protocol/dubbo/dubbo_codec.go @@ -167,13 +167,13 @@ func (c *DubboCodec) Decode(data []byte) (remoting.DecodeResult, int, error) { 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) } + + 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 { From 827905820b34b1851ba5e0956e67e892d51c8d21 Mon Sep 17 00:00:00 2001 From: haohongfan Date: Mon, 14 Sep 2020 23:29:34 +0800 Subject: [PATCH 42/44] feat: add some comment --- remoting/getty/config.go | 18 ++++++++---------- remoting/getty/readwriter.go | 12 +++++++++--- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/remoting/getty/config.go b/remoting/getty/config.go index 4213a8705d..dcf59d0821 100644 --- a/remoting/getty/config.go +++ b/remoting/getty/config.go @@ -30,7 +30,7 @@ import ( ) type ( - // GettySessionParam ... + // GettySessionParam is session configuration for getty GettySessionParam struct { CompressEncoding bool `default:"false" yaml:"compress_encoding" json:"compress_encoding,omitempty"` TcpNoDelay bool `default:"true" yaml:"tcp_no_delay" json:"tcp_no_delay,omitempty"` @@ -50,8 +50,7 @@ type ( SessionName string `default:"rpc" yaml:"session_name" json:"session_name,omitempty"` } - // ServerConfig - //Config holds supported types by the multiconfig package + // ServerConfig holds supported types by the multiconfig package ServerConfig struct { SSLEnabled bool @@ -69,8 +68,7 @@ type ( GettySessionParam GettySessionParam `required:"true" yaml:"getty_session_param" json:"getty_session_param,omitempty"` } - // ClientConfig - //Config holds supported types by the multiconfig package + // ClientConfig holds supported types by the multiconfig package ClientConfig struct { ReconnectInterval int `default:"0" yaml:"reconnect_interval" json:"reconnect_interval,omitempty"` @@ -99,7 +97,7 @@ type ( } ) -// GetDefaultClientConfig ... +// GetDefaultClientConfig gets client default configuration func GetDefaultClientConfig() ClientConfig { return ClientConfig{ ReconnectInterval: 0, @@ -127,7 +125,7 @@ func GetDefaultClientConfig() ClientConfig { }} } -// GetDefaultServerConfig ... +// GetDefaultServerConfig gets server default configuration func GetDefaultServerConfig() ServerConfig { return ServerConfig{ SessionTimeout: "180s", @@ -152,7 +150,7 @@ func GetDefaultServerConfig() ServerConfig { } } -// CheckValidity ... +// CheckValidity confirm getty sessian params func (c *GettySessionParam) CheckValidity() error { var err error @@ -175,7 +173,7 @@ func (c *GettySessionParam) CheckValidity() error { return nil } -// CheckValidity ... +// CheckValidity confirm client params. func (c *ClientConfig) CheckValidity() error { var err error @@ -197,7 +195,7 @@ func (c *ClientConfig) CheckValidity() error { return perrors.WithStack(c.GettySessionParam.CheckValidity()) } -// CheckValidity ... +// CheckValidity confirm server params func (c *ServerConfig) CheckValidity() error { var err error diff --git a/remoting/getty/readwriter.go b/remoting/getty/readwriter.go index 26623edd09..c6585c2dc6 100644 --- a/remoting/getty/readwriter.go +++ b/remoting/getty/readwriter.go @@ -36,16 +36,18 @@ import ( // RpcClientPackageHandler //////////////////////////////////////////// -// RpcClientPackageHandler ... +// RpcClientPackageHandler Read data from server and Write data to server type RpcClientPackageHandler struct { client *Client } -// NewRpcClientPackageHandler ... +// NewRpcClientPackageHandler create a RpcClientPackageHandler func NewRpcClientPackageHandler(client *Client) *RpcClientPackageHandler { return &RpcClientPackageHandler{client: client} } +// Read data from server. if the package size from server is larger than 4096 byte, server will read 4096 byte +// and send to client each time. the Read can assemble it. func (p *RpcClientPackageHandler) Read(ss getty.Session, data []byte) (interface{}, int, error) { resp, length, err := (p.client.codec).Decode(data) //err := pkg.Unmarshal(buf, p.client) @@ -63,6 +65,7 @@ func (p *RpcClientPackageHandler) Read(ss getty.Session, data []byte) (interface return resp, length, nil } +// Write send the data to server func (p *RpcClientPackageHandler) Write(ss getty.Session, pkg interface{}) ([]byte, error) { req, ok := pkg.(*remoting.Request) if !ok { @@ -87,7 +90,7 @@ func (p *RpcClientPackageHandler) Write(ss getty.Session, pkg interface{}) ([]by // rpcServerPkgHandler = &RpcServerPackageHandler{} //) -// RpcServerPackageHandler ... +// RpcServerPackageHandler Read data from client and Write data to client type RpcServerPackageHandler struct { server *Server } @@ -96,6 +99,8 @@ func NewRpcServerPackageHandler(server *Server) *RpcServerPackageHandler { return &RpcServerPackageHandler{server: server} } +// Read data from client. if the package size from client is larger than 4096 byte, client will read 4096 byte +// and send to client each time. the Read can assemble it. func (p *RpcServerPackageHandler) Read(ss getty.Session, data []byte) (interface{}, int, error) { req, length, err := (p.server.codec).Decode(data) //resp,len, err := (*p.).DecodeResponse(buf) @@ -113,6 +118,7 @@ func (p *RpcServerPackageHandler) Read(ss getty.Session, data []byte) (interface return req, length, err } +// Write send the data to client func (p *RpcServerPackageHandler) Write(ss getty.Session, pkg interface{}) ([]byte, error) { res, ok := pkg.(*remoting.Response) if !ok { From 517f54d598518c16f309eca6313bba67b32c7628 Mon Sep 17 00:00:00 2001 From: haohongfan Date: Thu, 17 Sep 2020 00:11:01 +0800 Subject: [PATCH 43/44] feat: delete dubbo_protocol assert remoting.ExchangeClient --- protocol/dubbo/dubbo_protocol.go | 15 +++------------ protocol/jsonrpc/jsonrpc_invoker.go | 1 - remoting/getty/getty_server.go | 8 ++++---- 3 files changed, 7 insertions(+), 17 deletions(-) diff --git a/protocol/dubbo/dubbo_protocol.go b/protocol/dubbo/dubbo_protocol.go index 2826e64d92..b8e9b59eb7 100644 --- a/protocol/dubbo/dubbo_protocol.go +++ b/protocol/dubbo/dubbo_protocol.go @@ -48,8 +48,8 @@ const ( var ( // Make the connection can be shared. // It will create one connection for one address (ip+port) - exchangeClientMap *sync.Map = new(sync.Map) - exchangeLock *sync.Map = new(sync.Map) + exchangeClientMap = new(sync.Map) + exchangeLock = new(sync.Map) ) func init() { @@ -215,16 +215,7 @@ func getExchangeClient(url common.URL) *remoting.ExchangeClient { if clientTmp == nil { return nil } - exchangeClient, ok := clientTmp.(*remoting.ExchangeClient) - if !ok { - exchangeClientTmp := remoting.NewExchangeClient(url, getty.NewClient(getty.Options{ - ConnectTimeout: config.GetConsumerConfig().ConnectTimeout, - RequestTimeout: config.GetConsumerConfig().RequestTimeout}), config.GetConsumerConfig().ConnectTimeout, false) - if exchangeClientTmp != nil { - exchangeClientMap.Store(url.Location, exchangeClientTmp) - } - return exchangeClientTmp - } + exchangeClient := clientTmp.(*remoting.ExchangeClient) return exchangeClient } diff --git a/protocol/jsonrpc/jsonrpc_invoker.go b/protocol/jsonrpc/jsonrpc_invoker.go index 970d5f9a08..d84b980216 100644 --- a/protocol/jsonrpc/jsonrpc_invoker.go +++ b/protocol/jsonrpc/jsonrpc_invoker.go @@ -26,7 +26,6 @@ import ( "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/common/logger" "github.com/apache/dubbo-go/protocol" - invocation_impl "github.com/apache/dubbo-go/protocol/invocation" ) diff --git a/remoting/getty/getty_server.go b/remoting/getty/getty_server.go index 98ca690cbf..be2dfd6bed 100644 --- a/remoting/getty/getty_server.go +++ b/remoting/getty/getty_server.go @@ -95,7 +95,7 @@ func GetServerConfig() ServerConfig { return *srvConf } -// SetServerGrpool ... +// SetServerGrpool set getty server GrPool func SetServerGrpool() { if srvConf.GrPoolSize > 1 { srvGrpool = gxsync.NewTaskPool(gxsync.WithTaskPoolTaskPoolSize(srvConf.GrPoolSize), gxsync.WithTaskPoolTaskQueueLength(srvConf.QueueLen), @@ -103,7 +103,7 @@ func SetServerGrpool() { } } -// Server ... +// Server define getty server type Server struct { conf ServerConfig addr string @@ -113,7 +113,7 @@ type Server struct { requestHandler func(*invocation.RPCInvocation) protocol.RPCResult } -// NewServer ... +// NewServer create a new Server func NewServer(url common.URL, handlers func(*invocation.RPCInvocation) protocol.RPCResult) *Server { //init initServer(url.Protocol) @@ -210,7 +210,7 @@ func (s *Server) Start() { } -// Stop ... +// Stop dubbo server func (s *Server) Stop() { s.tcpServer.Close() } From a24034c4a26eff7c6698f52b086e005a932d9aeb Mon Sep 17 00:00:00 2001 From: haohongfan Date: Thu, 17 Sep 2020 10:13:28 +0800 Subject: [PATCH 44/44] feat: merge one line --- protocol/dubbo/dubbo_protocol.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/protocol/dubbo/dubbo_protocol.go b/protocol/dubbo/dubbo_protocol.go index b8e9b59eb7..8dda52b6b9 100644 --- a/protocol/dubbo/dubbo_protocol.go +++ b/protocol/dubbo/dubbo_protocol.go @@ -215,8 +215,7 @@ func getExchangeClient(url common.URL) *remoting.ExchangeClient { if clientTmp == nil { return nil } - exchangeClient := clientTmp.(*remoting.ExchangeClient) - return exchangeClient + return clientTmp.(*remoting.ExchangeClient) } // rebuildCtx rebuild the context by attachment.