commit
8fb5e20a22
@ -10,8 +10,10 @@ import (
|
|||||||
func TestBackoff(t *testing.T) {
|
func TestBackoff(t *testing.T) {
|
||||||
delta := time.Duration(0)
|
delta := time.Duration(0)
|
||||||
|
|
||||||
|
c := NewClient()
|
||||||
|
|
||||||
for i := 0; i < 5; i++ {
|
for i := 0; i < 5; i++ {
|
||||||
d, err := exponentialBackoff(context.TODO(), NewJsonRequest("test", "test", nil), i)
|
d, err := exponentialBackoff(context.TODO(), c.NewRequest("test", "test", nil), i)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -12,22 +12,18 @@ import (
|
|||||||
type Client interface {
|
type Client interface {
|
||||||
Init(...Option) error
|
Init(...Option) error
|
||||||
Options() Options
|
Options() Options
|
||||||
NewPublication(topic string, msg interface{}) Publication
|
NewMessage(topic string, msg interface{}) Message
|
||||||
NewRequest(service, method string, req interface{}, reqOpts ...RequestOption) Request
|
NewRequest(service, method string, req interface{}, reqOpts ...RequestOption) Request
|
||||||
NewProtoRequest(service, method string, req interface{}, reqOpts ...RequestOption) Request
|
|
||||||
NewJsonRequest(service, method string, req interface{}, reqOpts ...RequestOption) Request
|
|
||||||
Call(ctx context.Context, req Request, rsp interface{}, opts ...CallOption) error
|
Call(ctx context.Context, req Request, rsp interface{}, opts ...CallOption) error
|
||||||
CallRemote(ctx context.Context, addr string, req Request, rsp interface{}, opts ...CallOption) error
|
Stream(ctx context.Context, req Request, opts ...CallOption) (Stream, error)
|
||||||
Stream(ctx context.Context, req Request, opts ...CallOption) (Streamer, error)
|
Publish(ctx context.Context, msg Message, opts ...PublishOption) error
|
||||||
StreamRemote(ctx context.Context, addr string, req Request, opts ...CallOption) (Streamer, error)
|
|
||||||
Publish(ctx context.Context, p Publication, opts ...PublishOption) error
|
|
||||||
String() string
|
String() string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Publication is the interface for a message published asynchronously
|
// Message is the interface for publishing asynchronously
|
||||||
type Publication interface {
|
type Message interface {
|
||||||
Topic() string
|
Topic() string
|
||||||
Message() interface{}
|
Payload() interface{}
|
||||||
ContentType() string
|
ContentType() string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,8 +37,8 @@ type Request interface {
|
|||||||
Stream() bool
|
Stream() bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Streamer is the inteface for a bidirectional synchronous stream
|
// Stream is the inteface for a bidirectional synchronous stream
|
||||||
type Streamer interface {
|
type Stream interface {
|
||||||
Context() context.Context
|
Context() context.Context
|
||||||
Request() Request
|
Request() Request
|
||||||
Send(interface{}) error
|
Send(interface{}) error
|
||||||
@ -85,26 +81,15 @@ func Call(ctx context.Context, request Request, response interface{}, opts ...Ca
|
|||||||
return DefaultClient.Call(ctx, request, response, opts...)
|
return DefaultClient.Call(ctx, request, response, opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Makes a synchronous call to the specified address using the default client
|
|
||||||
func CallRemote(ctx context.Context, address string, request Request, response interface{}, opts ...CallOption) error {
|
|
||||||
return DefaultClient.CallRemote(ctx, address, request, response, opts...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creates a streaming connection with a service and returns responses on the
|
|
||||||
// channel passed in. It's up to the user to close the streamer.
|
|
||||||
func Stream(ctx context.Context, request Request, opts ...CallOption) (Streamer, error) {
|
|
||||||
return DefaultClient.Stream(ctx, request, opts...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creates a streaming connection to the address specified.
|
|
||||||
func StreamRemote(ctx context.Context, address string, request Request, opts ...CallOption) (Streamer, error) {
|
|
||||||
return DefaultClient.StreamRemote(ctx, address, request, opts...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Publishes a publication using the default client. Using the underlying broker
|
// Publishes a publication using the default client. Using the underlying broker
|
||||||
// set within the options.
|
// set within the options.
|
||||||
func Publish(ctx context.Context, p Publication) error {
|
func Publish(ctx context.Context, msg Message) error {
|
||||||
return DefaultClient.Publish(ctx, p)
|
return DefaultClient.Publish(ctx, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates a new message using the default client
|
||||||
|
func NewMessage(topic string, payload interface{}) Message {
|
||||||
|
return DefaultClient.NewMessage(topic, payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates a new client with the options passed in
|
// Creates a new client with the options passed in
|
||||||
@ -112,25 +97,16 @@ func NewClient(opt ...Option) Client {
|
|||||||
return newRpcClient(opt...)
|
return newRpcClient(opt...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates a new publication using the default client
|
|
||||||
func NewPublication(topic string, message interface{}) Publication {
|
|
||||||
return DefaultClient.NewPublication(topic, message)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creates a new request using the default client. Content Type will
|
// Creates a new request using the default client. Content Type will
|
||||||
// be set to the default within options and use the appropriate codec
|
// be set to the default within options and use the appropriate codec
|
||||||
func NewRequest(service, method string, request interface{}, reqOpts ...RequestOption) Request {
|
func NewRequest(service, method string, request interface{}, reqOpts ...RequestOption) Request {
|
||||||
return DefaultClient.NewRequest(service, method, request, reqOpts...)
|
return DefaultClient.NewRequest(service, method, request, reqOpts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates a new protobuf request using the default client
|
// Creates a streaming connection with a service and returns responses on the
|
||||||
func NewProtoRequest(service, method string, request interface{}, reqOpts ...RequestOption) Request {
|
// channel passed in. It's up to the user to close the streamer.
|
||||||
return DefaultClient.NewProtoRequest(service, method, request, reqOpts...)
|
func NewStream(ctx context.Context, request Request, opts ...CallOption) (Stream, error) {
|
||||||
}
|
return DefaultClient.Stream(ctx, request, opts...)
|
||||||
|
|
||||||
// Creates a new json request using the default client
|
|
||||||
func NewJsonRequest(service, method string, request interface{}, reqOpts ...RequestOption) Request {
|
|
||||||
return DefaultClient.NewJsonRequest(service, method, request, reqOpts...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func String() string {
|
func String() string {
|
||||||
|
@ -49,22 +49,14 @@ func (m *MockClient) Options() client.Options {
|
|||||||
return m.Opts
|
return m.Opts
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockClient) NewPublication(topic string, msg interface{}) client.Publication {
|
func (m *MockClient) NewMessage(topic string, msg interface{}) client.Message {
|
||||||
return m.Client.NewPublication(topic, msg)
|
return m.Client.NewMessage(topic, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockClient) NewRequest(service, method string, req interface{}, reqOpts ...client.RequestOption) client.Request {
|
func (m *MockClient) NewRequest(service, method string, req interface{}, reqOpts ...client.RequestOption) client.Request {
|
||||||
return m.Client.NewRequest(service, method, req, reqOpts...)
|
return m.Client.NewRequest(service, method, req, reqOpts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockClient) NewProtoRequest(service, method string, req interface{}, reqOpts ...client.RequestOption) client.Request {
|
|
||||||
return m.Client.NewProtoRequest(service, method, req, reqOpts...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MockClient) NewJsonRequest(service, method string, req interface{}, reqOpts ...client.RequestOption) client.Request {
|
|
||||||
return m.Client.NewJsonRequest(service, method, req, reqOpts...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MockClient) Call(ctx context.Context, req client.Request, rsp interface{}, opts ...client.CallOption) error {
|
func (m *MockClient) Call(ctx context.Context, req client.Request, rsp interface{}, opts ...client.CallOption) error {
|
||||||
m.Lock()
|
m.Lock()
|
||||||
defer m.Unlock()
|
defer m.Unlock()
|
||||||
@ -97,39 +89,7 @@ func (m *MockClient) Call(ctx context.Context, req client.Request, rsp interface
|
|||||||
return fmt.Errorf("rpc: can't find service %s", req.Method())
|
return fmt.Errorf("rpc: can't find service %s", req.Method())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockClient) CallRemote(ctx context.Context, addr string, req client.Request, rsp interface{}, opts ...client.CallOption) error {
|
func (m *MockClient) Stream(ctx context.Context, req client.Request, opts ...client.CallOption) (client.Stream, error) {
|
||||||
m.Lock()
|
|
||||||
defer m.Unlock()
|
|
||||||
|
|
||||||
response, ok := m.Response[req.Service()]
|
|
||||||
if !ok {
|
|
||||||
return errors.NotFound("go.micro.client.mock", "service not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, r := range response {
|
|
||||||
if r.Method != req.Method() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.Error != nil {
|
|
||||||
return r.Error
|
|
||||||
}
|
|
||||||
|
|
||||||
v := reflect.ValueOf(rsp)
|
|
||||||
|
|
||||||
if t := reflect.TypeOf(rsp); t.Kind() == reflect.Ptr {
|
|
||||||
v = reflect.Indirect(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
v.Set(reflect.ValueOf(r.Response))
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Errorf("rpc: can't find service %s", req.Method())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MockClient) Stream(ctx context.Context, req client.Request, opts ...client.CallOption) (client.Streamer, error) {
|
|
||||||
m.Lock()
|
m.Lock()
|
||||||
defer m.Unlock()
|
defer m.Unlock()
|
||||||
|
|
||||||
@ -137,15 +97,7 @@ func (m *MockClient) Stream(ctx context.Context, req client.Request, opts ...cli
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockClient) StreamRemote(ctx context.Context, addr string, req client.Request, opts ...client.CallOption) (client.Streamer, error) {
|
func (m *MockClient) Publish(ctx context.Context, p client.Message, opts ...client.PublishOption) error {
|
||||||
m.Lock()
|
|
||||||
defer m.Unlock()
|
|
||||||
|
|
||||||
// TODO: mock stream
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MockClient) Publish(ctx context.Context, p client.Publication, opts ...client.PublishOption) error {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ func TestClient(t *testing.T) {
|
|||||||
c := NewClient(Response("go.mock", response))
|
c := NewClient(Response("go.mock", response))
|
||||||
|
|
||||||
for _, r := range response {
|
for _, r := range response {
|
||||||
req := c.NewJsonRequest("go.mock", r.Method, map[string]interface{}{"foo": "bar"})
|
req := c.NewRequest("go.mock", r.Method, map[string]interface{}{"foo": "bar"})
|
||||||
var rsp interface{}
|
var rsp interface{}
|
||||||
|
|
||||||
err := c.Call(context.TODO(), req, &rsp)
|
err := c.Call(context.TODO(), req, &rsp)
|
||||||
|
@ -40,6 +40,8 @@ type Options struct {
|
|||||||
type CallOptions struct {
|
type CallOptions struct {
|
||||||
SelectOptions []selector.SelectOption
|
SelectOptions []selector.SelectOption
|
||||||
|
|
||||||
|
// Address of remote host
|
||||||
|
Address string
|
||||||
// Backoff func
|
// Backoff func
|
||||||
Backoff BackoffFunc
|
Backoff BackoffFunc
|
||||||
// Check if retriable func
|
// Check if retriable func
|
||||||
@ -66,7 +68,8 @@ type PublishOptions struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type RequestOptions struct {
|
type RequestOptions struct {
|
||||||
Stream bool
|
ContentType string
|
||||||
|
Stream bool
|
||||||
|
|
||||||
// Other options for implementations of the interface
|
// Other options for implementations of the interface
|
||||||
// can be stored in a context
|
// can be stored in a context
|
||||||
@ -226,6 +229,13 @@ func DialTimeout(d time.Duration) Option {
|
|||||||
|
|
||||||
// Call Options
|
// Call Options
|
||||||
|
|
||||||
|
// WithAddress sets the remote address to use rather than using service discovery
|
||||||
|
func WithAddress(a string) CallOption {
|
||||||
|
return func(o *CallOptions) {
|
||||||
|
o.Address = a
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func WithSelectOption(so ...selector.SelectOption) CallOption {
|
func WithSelectOption(so ...selector.SelectOption) CallOption {
|
||||||
return func(o *CallOptions) {
|
return func(o *CallOptions) {
|
||||||
o.SelectOptions = append(o.SelectOptions, so...)
|
o.SelectOptions = append(o.SelectOptions, so...)
|
||||||
@ -281,6 +291,12 @@ func WithDialTimeout(d time.Duration) CallOption {
|
|||||||
|
|
||||||
// Request Options
|
// Request Options
|
||||||
|
|
||||||
|
func WithContentType(ct string) RequestOption {
|
||||||
|
return func(o *RequestOptions) {
|
||||||
|
o.ContentType = ct
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func StreamingRequest() RequestOption {
|
func StreamingRequest() RequestOption {
|
||||||
return func(o *RequestOptions) {
|
return func(o *RequestOptions) {
|
||||||
o.Stream = true
|
o.Stream = true
|
||||||
|
@ -11,6 +11,7 @@ import (
|
|||||||
"github.com/micro/go-micro/codec"
|
"github.com/micro/go-micro/codec"
|
||||||
"github.com/micro/go-micro/errors"
|
"github.com/micro/go-micro/errors"
|
||||||
"github.com/micro/go-micro/metadata"
|
"github.com/micro/go-micro/metadata"
|
||||||
|
"github.com/micro/go-micro/registry"
|
||||||
"github.com/micro/go-micro/selector"
|
"github.com/micro/go-micro/selector"
|
||||||
"github.com/micro/go-micro/transport"
|
"github.com/micro/go-micro/transport"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
@ -134,7 +135,7 @@ func (r *rpcClient) call(ctx context.Context, address string, req Request, resp
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *rpcClient) stream(ctx context.Context, address string, req Request, opts CallOptions) (Streamer, error) {
|
func (r *rpcClient) stream(ctx context.Context, address string, req Request, opts CallOptions) (Stream, error) {
|
||||||
msg := &transport.Message{
|
msg := &transport.Message{
|
||||||
Header: make(map[string]string),
|
Header: make(map[string]string),
|
||||||
}
|
}
|
||||||
@ -213,13 +214,25 @@ func (r *rpcClient) Options() Options {
|
|||||||
return r.opts
|
return r.opts
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *rpcClient) CallRemote(ctx context.Context, address string, request Request, response interface{}, opts ...CallOption) error {
|
func (r *rpcClient) next(request Request, opts CallOptions) (selector.Next, error) {
|
||||||
// make a copy of call opts
|
// return remote address
|
||||||
callOpts := r.opts.CallOptions
|
if len(opts.Address) > 0 {
|
||||||
for _, opt := range opts {
|
return func() (*registry.Node, error) {
|
||||||
opt(&callOpts)
|
return ®istry.Node{
|
||||||
|
Address: opts.Address,
|
||||||
|
}, nil
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
return r.call(ctx, address, request, response, callOpts)
|
|
||||||
|
// get next nodes from the selector
|
||||||
|
next, err := r.opts.Selector.Select(request.Service(), opts.SelectOptions...)
|
||||||
|
if err != nil && err == selector.ErrNotFound {
|
||||||
|
return nil, errors.NotFound("go.micro.client", err.Error())
|
||||||
|
} else if err != nil {
|
||||||
|
return nil, errors.InternalServerError("go.micro.client", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return next, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *rpcClient) Call(ctx context.Context, request Request, response interface{}, opts ...CallOption) error {
|
func (r *rpcClient) Call(ctx context.Context, request Request, response interface{}, opts ...CallOption) error {
|
||||||
@ -229,12 +242,9 @@ func (r *rpcClient) Call(ctx context.Context, request Request, response interfac
|
|||||||
opt(&callOpts)
|
opt(&callOpts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// get next nodes from the selector
|
next, err := r.next(request, callOpts)
|
||||||
next, err := r.opts.Selector.Select(request.Service(), callOpts.SelectOptions...)
|
if err != nil {
|
||||||
if err != nil && err == selector.ErrNotFound {
|
return err
|
||||||
return errors.NotFound("go.micro.client", err.Error())
|
|
||||||
} else if err != nil {
|
|
||||||
return errors.InternalServerError("go.micro.client", err.Error())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if we already have a deadline
|
// check if we already have a deadline
|
||||||
@ -330,28 +340,16 @@ func (r *rpcClient) Call(ctx context.Context, request Request, response interfac
|
|||||||
return gerr
|
return gerr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *rpcClient) StreamRemote(ctx context.Context, address string, request Request, opts ...CallOption) (Streamer, error) {
|
func (r *rpcClient) Stream(ctx context.Context, request Request, opts ...CallOption) (Stream, error) {
|
||||||
// make a copy of call opts
|
|
||||||
callOpts := r.opts.CallOptions
|
|
||||||
for _, opt := range opts {
|
|
||||||
opt(&callOpts)
|
|
||||||
}
|
|
||||||
return r.stream(ctx, address, request, callOpts)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *rpcClient) Stream(ctx context.Context, request Request, opts ...CallOption) (Streamer, error) {
|
|
||||||
// make a copy of call opts
|
// make a copy of call opts
|
||||||
callOpts := r.opts.CallOptions
|
callOpts := r.opts.CallOptions
|
||||||
for _, opt := range opts {
|
for _, opt := range opts {
|
||||||
opt(&callOpts)
|
opt(&callOpts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// get next nodes from the selector
|
next, err := r.next(request, callOpts)
|
||||||
next, err := r.opts.Selector.Select(request.Service(), callOpts.SelectOptions...)
|
if err != nil {
|
||||||
if err != nil && err == selector.ErrNotFound {
|
return nil, err
|
||||||
return nil, errors.NotFound("go.micro.client", err.Error())
|
|
||||||
} else if err != nil {
|
|
||||||
return nil, errors.InternalServerError("go.micro.client", err.Error())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if we already have a deadline
|
// check if we already have a deadline
|
||||||
@ -373,7 +371,7 @@ func (r *rpcClient) Stream(ctx context.Context, request Request, opts ...CallOpt
|
|||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
call := func(i int) (Streamer, error) {
|
call := func(i int) (Stream, error) {
|
||||||
// call backoff first. Someone may want an initial start delay
|
// call backoff first. Someone may want an initial start delay
|
||||||
t, err := callOpts.Backoff(ctx, request, i)
|
t, err := callOpts.Backoff(ctx, request, i)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -403,7 +401,7 @@ func (r *rpcClient) Stream(ctx context.Context, request Request, opts ...CallOpt
|
|||||||
}
|
}
|
||||||
|
|
||||||
type response struct {
|
type response struct {
|
||||||
stream Streamer
|
stream Stream
|
||||||
err error
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -441,49 +439,38 @@ func (r *rpcClient) Stream(ctx context.Context, request Request, opts ...CallOpt
|
|||||||
return nil, grr
|
return nil, grr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *rpcClient) Publish(ctx context.Context, p Publication, opts ...PublishOption) error {
|
func (r *rpcClient) Publish(ctx context.Context, msg Message, opts ...PublishOption) error {
|
||||||
md, ok := metadata.FromContext(ctx)
|
md, ok := metadata.FromContext(ctx)
|
||||||
if !ok {
|
if !ok {
|
||||||
md = make(map[string]string)
|
md = make(map[string]string)
|
||||||
}
|
}
|
||||||
md["Content-Type"] = p.ContentType()
|
md["Content-Type"] = msg.ContentType()
|
||||||
|
|
||||||
// encode message body
|
// encode message body
|
||||||
cf, err := r.newCodec(p.ContentType())
|
cf, err := r.newCodec(msg.ContentType())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.InternalServerError("go.micro.client", err.Error())
|
return errors.InternalServerError("go.micro.client", err.Error())
|
||||||
}
|
}
|
||||||
b := &buffer{bytes.NewBuffer(nil)}
|
b := &buffer{bytes.NewBuffer(nil)}
|
||||||
if err := cf(b).Write(&codec.Message{Type: codec.Publication}, p.Message()); err != nil {
|
if err := cf(b).Write(&codec.Message{Type: codec.Publication}, msg.Payload()); err != nil {
|
||||||
return errors.InternalServerError("go.micro.client", err.Error())
|
return errors.InternalServerError("go.micro.client", err.Error())
|
||||||
}
|
}
|
||||||
r.once.Do(func() {
|
r.once.Do(func() {
|
||||||
r.opts.Broker.Connect()
|
r.opts.Broker.Connect()
|
||||||
})
|
})
|
||||||
|
|
||||||
return r.opts.Broker.Publish(p.Topic(), &broker.Message{
|
return r.opts.Broker.Publish(msg.Topic(), &broker.Message{
|
||||||
Header: md,
|
Header: md,
|
||||||
Body: b.Bytes(),
|
Body: b.Bytes(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *rpcClient) NewPublication(topic string, message interface{}) Publication {
|
func (r *rpcClient) NewMessage(topic string, message interface{}) Message {
|
||||||
return newRpcPublication(topic, message, r.opts.ContentType)
|
return newMessage(topic, message, r.opts.ContentType)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *rpcClient) NewProtoPublication(topic string, message interface{}) Publication {
|
|
||||||
return newRpcPublication(topic, message, "application/octet-stream")
|
|
||||||
}
|
|
||||||
func (r *rpcClient) NewRequest(service, method string, request interface{}, reqOpts ...RequestOption) Request {
|
func (r *rpcClient) NewRequest(service, method string, request interface{}, reqOpts ...RequestOption) Request {
|
||||||
return newRpcRequest(service, method, request, r.opts.ContentType, reqOpts...)
|
return newRequest(service, method, request, r.opts.ContentType, reqOpts...)
|
||||||
}
|
|
||||||
|
|
||||||
func (r *rpcClient) NewProtoRequest(service, method string, request interface{}, reqOpts ...RequestOption) Request {
|
|
||||||
return newRpcRequest(service, method, request, "application/octet-stream", reqOpts...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *rpcClient) NewJsonRequest(service, method string, request interface{}, reqOpts ...RequestOption) Request {
|
|
||||||
return newRpcRequest(service, method, request, "application/json", reqOpts...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *rpcClient) String() string {
|
func (r *rpcClient) String() string {
|
||||||
|
@ -10,6 +10,52 @@ import (
|
|||||||
"github.com/micro/go-micro/selector"
|
"github.com/micro/go-micro/selector"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestCallAddress(t *testing.T) {
|
||||||
|
var called bool
|
||||||
|
service := "test.service"
|
||||||
|
method := "Test.Method"
|
||||||
|
address := "10.1.10.1:8080"
|
||||||
|
|
||||||
|
wrap := func(cf CallFunc) CallFunc {
|
||||||
|
return func(ctx context.Context, addr string, req Request, rsp interface{}, opts CallOptions) error {
|
||||||
|
called = true
|
||||||
|
|
||||||
|
if req.Service() != service {
|
||||||
|
return fmt.Errorf("expected service: %s got %s", service, req.Service())
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.Method() != method {
|
||||||
|
return fmt.Errorf("expected service: %s got %s", method, req.Method())
|
||||||
|
}
|
||||||
|
|
||||||
|
if addr != address {
|
||||||
|
return fmt.Errorf("expected address: %s got %s", address, addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// don't do the call
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
r := mock.NewRegistry()
|
||||||
|
c := NewClient(
|
||||||
|
Registry(r),
|
||||||
|
WrapCall(wrap),
|
||||||
|
)
|
||||||
|
c.Options().Selector.Init(selector.Registry(r))
|
||||||
|
|
||||||
|
req := c.NewRequest(service, method, nil)
|
||||||
|
|
||||||
|
// test calling remote address
|
||||||
|
if err := c.Call(context.Background(), req, nil, WithAddress(address)); err != nil {
|
||||||
|
t.Fatal("call with address error", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !called {
|
||||||
|
t.Fatal("wrapper not called")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
func TestCallWrapper(t *testing.T) {
|
func TestCallWrapper(t *testing.T) {
|
||||||
var called bool
|
var called bool
|
||||||
id := "test.1"
|
id := "test.1"
|
||||||
|
27
client/rpc_message.go
Normal file
27
client/rpc_message.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package client
|
||||||
|
|
||||||
|
type message struct {
|
||||||
|
topic string
|
||||||
|
contentType string
|
||||||
|
payload interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMessage(topic string, payload interface{}, contentType string) Message {
|
||||||
|
return &message{
|
||||||
|
payload: payload,
|
||||||
|
topic: topic,
|
||||||
|
contentType: contentType,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *message) ContentType() string {
|
||||||
|
return m.contentType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *message) Topic() string {
|
||||||
|
return m.topic
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *message) Payload() interface{} {
|
||||||
|
return m.payload
|
||||||
|
}
|
@ -1,27 +0,0 @@
|
|||||||
package client
|
|
||||||
|
|
||||||
type rpcPublication struct {
|
|
||||||
topic string
|
|
||||||
contentType string
|
|
||||||
message interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func newRpcPublication(topic string, message interface{}, contentType string) Publication {
|
|
||||||
return &rpcPublication{
|
|
||||||
message: message,
|
|
||||||
topic: topic,
|
|
||||||
contentType: contentType,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *rpcPublication) ContentType() string {
|
|
||||||
return r.contentType
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *rpcPublication) Topic() string {
|
|
||||||
return r.topic
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *rpcPublication) Message() interface{} {
|
|
||||||
return r.message
|
|
||||||
}
|
|
@ -8,13 +8,18 @@ type rpcRequest struct {
|
|||||||
opts RequestOptions
|
opts RequestOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
func newRpcRequest(service, method string, request interface{}, contentType string, reqOpts ...RequestOption) Request {
|
func newRequest(service, method string, request interface{}, contentType string, reqOpts ...RequestOption) Request {
|
||||||
var opts RequestOptions
|
var opts RequestOptions
|
||||||
|
|
||||||
for _, o := range reqOpts {
|
for _, o := range reqOpts {
|
||||||
o(&opts)
|
o(&opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// set the content-type specified
|
||||||
|
if len(opts.ContentType) > 0 {
|
||||||
|
contentType = opts.ContentType
|
||||||
|
}
|
||||||
|
|
||||||
return &rpcRequest{
|
return &rpcRequest{
|
||||||
service: service,
|
service: service,
|
||||||
method: method,
|
method: method,
|
||||||
|
23
client/rpc_request_test.go
Normal file
23
client/rpc_request_test.go
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRequestOptions(t *testing.T) {
|
||||||
|
r := newRequest("service", "method", nil, "application/json")
|
||||||
|
if r.Service() != "service" {
|
||||||
|
t.Fatalf("expected 'service' got %s", r.Service())
|
||||||
|
}
|
||||||
|
if r.Method() != "method" {
|
||||||
|
t.Fatalf("expected 'method' got %s", r.Method())
|
||||||
|
}
|
||||||
|
if r.ContentType() != "application/json" {
|
||||||
|
t.Fatalf("expected 'method' got %s", r.ContentType())
|
||||||
|
}
|
||||||
|
|
||||||
|
r2 := newRequest("service", "method", nil, "application/json", WithContentType("application/protobuf"))
|
||||||
|
if r2.ContentType() != "application/protobuf" {
|
||||||
|
t.Fatalf("expected 'method' got %s", r2.ContentType())
|
||||||
|
}
|
||||||
|
}
|
@ -14,4 +14,4 @@ type CallWrapper func(CallFunc) CallFunc
|
|||||||
type Wrapper func(Client) Client
|
type Wrapper func(Client) Client
|
||||||
|
|
||||||
// StreamWrapper wraps a Stream and returns the equivalent
|
// StreamWrapper wraps a Stream and returns the equivalent
|
||||||
type StreamWrapper func(Streamer) Streamer
|
type StreamWrapper func(Stream) Stream
|
@ -23,7 +23,7 @@ func fnHandlerWrapper(f Function) server.HandlerWrapper {
|
|||||||
|
|
||||||
func fnSubWrapper(f Function) server.SubscriberWrapper {
|
func fnSubWrapper(f Function) server.SubscriberWrapper {
|
||||||
return func(s server.SubscriberFunc) server.SubscriberFunc {
|
return func(s server.SubscriberFunc) server.SubscriberFunc {
|
||||||
return func(ctx context.Context, msg server.Publication) error {
|
return func(ctx context.Context, msg server.Message) error {
|
||||||
defer f.Done()
|
defer f.Done()
|
||||||
return s(ctx, msg)
|
return s(ctx, msg)
|
||||||
}
|
}
|
||||||
|
@ -12,5 +12,5 @@ type publisher struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *publisher) Publish(ctx context.Context, msg interface{}, opts ...client.PublishOption) error {
|
func (p *publisher) Publish(ctx context.Context, msg interface{}, opts ...client.PublishOption) error {
|
||||||
return p.c.Publish(ctx, p.c.NewPublication(p.topic, msg))
|
return p.c.Publish(ctx, p.c.NewMessage(p.topic, msg))
|
||||||
}
|
}
|
||||||
|
@ -8,10 +8,10 @@ type rpcRequest struct {
|
|||||||
stream bool
|
stream bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type rpcPublication struct {
|
type rpcMessage struct {
|
||||||
topic string
|
topic string
|
||||||
contentType string
|
contentType string
|
||||||
message interface{}
|
payload interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *rpcRequest) ContentType() string {
|
func (r *rpcRequest) ContentType() string {
|
||||||
@ -34,14 +34,14 @@ func (r *rpcRequest) Stream() bool {
|
|||||||
return r.stream
|
return r.stream
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *rpcPublication) ContentType() string {
|
func (r *rpcMessage) ContentType() string {
|
||||||
return r.contentType
|
return r.contentType
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *rpcPublication) Topic() string {
|
func (r *rpcMessage) Topic() string {
|
||||||
return r.topic
|
return r.topic
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *rpcPublication) Message() interface{} {
|
func (r *rpcMessage) Payload() interface{} {
|
||||||
return r.message
|
return r.payload
|
||||||
}
|
}
|
||||||
|
@ -119,9 +119,9 @@ func prepareMethod(method reflect.Method) *methodType {
|
|||||||
|
|
||||||
if stream {
|
if stream {
|
||||||
// check stream type
|
// check stream type
|
||||||
streamType := reflect.TypeOf((*Streamer)(nil)).Elem()
|
streamType := reflect.TypeOf((*Stream)(nil)).Elem()
|
||||||
if !argType.Implements(streamType) {
|
if !argType.Implements(streamType) {
|
||||||
log.Log(mname, "argument does not implement Streamer interface:", argType)
|
log.Log(mname, "argument does not implement Stream interface:", argType)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -25,9 +25,9 @@ type Server interface {
|
|||||||
String() string
|
String() string
|
||||||
}
|
}
|
||||||
|
|
||||||
type Publication interface {
|
type Message interface {
|
||||||
Topic() string
|
Topic() string
|
||||||
Message() interface{}
|
Payload() interface{}
|
||||||
ContentType() string
|
ContentType() string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,11 +40,11 @@ type Request interface {
|
|||||||
Stream() bool
|
Stream() bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Streamer represents a stream established with a client.
|
// Stream represents a stream established with a client.
|
||||||
// A stream can be bidirectional which is indicated by the request.
|
// A stream can be bidirectional which is indicated by the request.
|
||||||
// The last error will be left in Error().
|
// The last error will be left in Error().
|
||||||
// EOF indicated end of the stream.
|
// EOF indicated end of the stream.
|
||||||
type Streamer interface {
|
type Stream interface {
|
||||||
Context() context.Context
|
Context() context.Context
|
||||||
Request() Request
|
Request() Request
|
||||||
Send(interface{}) error
|
Send(interface{}) error
|
||||||
|
@ -204,7 +204,7 @@ func (s *rpcServer) createSubHandler(sb *subscriber, opts Options) broker.Handle
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
fn := func(ctx context.Context, msg Publication) error {
|
fn := func(ctx context.Context, msg Message) error {
|
||||||
var vals []reflect.Value
|
var vals []reflect.Value
|
||||||
if sb.typ.Kind() != reflect.Func {
|
if sb.typ.Kind() != reflect.Func {
|
||||||
vals = append(vals, sb.rcvr)
|
vals = append(vals, sb.rcvr)
|
||||||
@ -213,7 +213,7 @@ func (s *rpcServer) createSubHandler(sb *subscriber, opts Options) broker.Handle
|
|||||||
vals = append(vals, reflect.ValueOf(ctx))
|
vals = append(vals, reflect.ValueOf(ctx))
|
||||||
}
|
}
|
||||||
|
|
||||||
vals = append(vals, reflect.ValueOf(msg.Message()))
|
vals = append(vals, reflect.ValueOf(msg.Payload()))
|
||||||
|
|
||||||
returnValues := handler.method.Call(vals)
|
returnValues := handler.method.Call(vals)
|
||||||
if err := returnValues[0].Interface(); err != nil {
|
if err := returnValues[0].Interface(); err != nil {
|
||||||
@ -229,10 +229,10 @@ func (s *rpcServer) createSubHandler(sb *subscriber, opts Options) broker.Handle
|
|||||||
s.wg.Add(1)
|
s.wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
defer s.wg.Done()
|
defer s.wg.Done()
|
||||||
fn(ctx, &rpcPublication{
|
fn(ctx, &rpcMessage{
|
||||||
topic: sb.topic,
|
topic: sb.topic,
|
||||||
contentType: ct,
|
contentType: ct,
|
||||||
message: req.Interface(),
|
payload: req.Interface(),
|
||||||
})
|
})
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ type HandlerFunc func(ctx context.Context, req Request, rsp interface{}) error
|
|||||||
// SubscriberFunc represents a single method of a subscriber. It's used primarily
|
// SubscriberFunc represents a single method of a subscriber. It's used primarily
|
||||||
// for the wrappers. What's handed to the actual method is the concrete
|
// for the wrappers. What's handed to the actual method is the concrete
|
||||||
// publication message.
|
// publication message.
|
||||||
type SubscriberFunc func(ctx context.Context, msg Publication) error
|
type SubscriberFunc func(ctx context.Context, msg Message) error
|
||||||
|
|
||||||
// HandlerWrapper wraps the HandlerFunc and returns the equivalent
|
// HandlerWrapper wraps the HandlerFunc and returns the equivalent
|
||||||
type HandlerWrapper func(HandlerFunc) HandlerFunc
|
type HandlerWrapper func(HandlerFunc) HandlerFunc
|
||||||
@ -20,8 +20,8 @@ type HandlerWrapper func(HandlerFunc) HandlerFunc
|
|||||||
// SubscriberWrapper wraps the SubscriberFunc and returns the equivalent
|
// SubscriberWrapper wraps the SubscriberFunc and returns the equivalent
|
||||||
type SubscriberWrapper func(SubscriberFunc) SubscriberFunc
|
type SubscriberWrapper func(SubscriberFunc) SubscriberFunc
|
||||||
|
|
||||||
// StreamerWrapper wraps a Streamer interface and returns the equivalent.
|
// StreamWrapper wraps a Stream interface and returns the equivalent.
|
||||||
// Because streams exist for the lifetime of a method invocation this
|
// Because streams exist for the lifetime of a method invocation this
|
||||||
// is a convenient way to wrap a Stream as its in use for trace, monitoring,
|
// is a convenient way to wrap a Stream as its in use for trace, monitoring,
|
||||||
// metrics, etc.
|
// metrics, etc.
|
||||||
type StreamerWrapper func(Streamer) Streamer
|
type StreamWrapper func(Stream) Stream
|
@ -50,15 +50,3 @@ var (
|
|||||||
func NewTransport(opts ...Option) Transport {
|
func NewTransport(opts ...Option) Transport {
|
||||||
return newHTTPTransport(opts...)
|
return newHTTPTransport(opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Dial(addr string, opts ...DialOption) (Client, error) {
|
|
||||||
return DefaultTransport.Dial(addr, opts...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Listen(addr string, opts ...ListenOption) (Listener, error) {
|
|
||||||
return DefaultTransport.Listen(addr, opts...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func String() string {
|
|
||||||
return DefaultTransport.String()
|
|
||||||
}
|
|
||||||
|
@ -36,12 +36,12 @@ func (c *clientWrapper) Call(ctx context.Context, req client.Request, rsp interf
|
|||||||
return c.Client.Call(ctx, req, rsp, opts...)
|
return c.Client.Call(ctx, req, rsp, opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *clientWrapper) Stream(ctx context.Context, req client.Request, opts ...client.CallOption) (client.Streamer, error) {
|
func (c *clientWrapper) Stream(ctx context.Context, req client.Request, opts ...client.CallOption) (client.Stream, error) {
|
||||||
ctx = c.setHeaders(ctx)
|
ctx = c.setHeaders(ctx)
|
||||||
return c.Client.Stream(ctx, req, opts...)
|
return c.Client.Stream(ctx, req, opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *clientWrapper) Publish(ctx context.Context, p client.Publication, opts ...client.PublishOption) error {
|
func (c *clientWrapper) Publish(ctx context.Context, p client.Message, opts ...client.PublishOption) error {
|
||||||
ctx = c.setHeaders(ctx)
|
ctx = c.setHeaders(ctx)
|
||||||
return c.Client.Publish(ctx, p, opts...)
|
return c.Client.Publish(ctx, p, opts...)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user