From 1eb4a83ffc0a23af94033ec454e8ac158c118723 Mon Sep 17 00:00:00 2001 From: cvictory Date: Fri, 24 Apr 2020 11:29:25 +0800 Subject: [PATCH 01/21] 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/21] 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/21] 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/21] 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/21] 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/21] 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/21] 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/21] 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/21] 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/21] 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/21] 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/21] 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/21] 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/21] 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/21] 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/21] 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/21] 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/21] 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/21] 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/21] 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/21] 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, "")) } // /////////////////////////