1
0
mirror of https://github.com/go-micro/go-micro.git synced 2024-11-24 08:02:32 +02:00
go-micro/client/rpc_client.go

741 lines
16 KiB
Go
Raw Permalink Normal View History

2015-01-14 01:31:27 +02:00
package client
import (
2018-03-03 13:53:52 +02:00
"context"
2015-01-14 01:31:27 +02:00
"fmt"
"sync"
"sync/atomic"
2019-01-18 14:30:39 +02:00
"time"
"github.com/google/uuid"
"github.com/pkg/errors"
2024-06-04 22:40:43 +02:00
"go-micro.dev/v5/broker"
"go-micro.dev/v5/codec"
raw "go-micro.dev/v5/codec/bytes"
merrors "go-micro.dev/v5/errors"
log "go-micro.dev/v5/logger"
"go-micro.dev/v5/metadata"
"go-micro.dev/v5/registry"
"go-micro.dev/v5/selector"
"go-micro.dev/v5/transport"
"go-micro.dev/v5/transport/headers"
"go-micro.dev/v5/util/buf"
"go-micro.dev/v5/util/net"
"go-micro.dev/v5/util/pool"
2015-01-14 01:31:27 +02:00
)
const (
packageID = "go.micro.client"
)
2015-05-23 18:40:53 +02:00
type rpcClient struct {
opts Options
2023-04-26 02:16:34 +02:00
once atomic.Value
2019-07-28 19:56:18 +02:00
pool pool.Pool
2023-04-26 02:16:34 +02:00
seq uint64
mu sync.RWMutex
2015-05-20 23:57:19 +02:00
}
2015-01-14 01:31:27 +02:00
func newRPCClient(opt ...Option) Client {
opts := NewOptions(opt...)
2019-07-28 19:56:18 +02:00
p := pool.NewPool(
pool.Size(opts.PoolSize),
pool.TTL(opts.PoolTTL),
pool.Transport(opts.Transport),
pool.CloseTimeout(opts.PoolCloseTimeout),
2019-07-28 19:56:18 +02:00
)
2015-11-26 22:36:42 +02:00
rc := &rpcClient{
2015-05-23 18:40:53 +02:00
opts: opts,
2019-07-28 19:56:18 +02:00
pool: p,
2018-03-21 05:17:38 +02:00
seq: 0,
2015-05-23 18:40:53 +02:00
}
rc.once.Store(false)
2015-11-26 22:36:42 +02:00
c := Client(rc)
// wrap in reverse
for i := len(opts.Wrappers); i > 0; i-- {
c = opts.Wrappers[i-1](c)
2015-11-26 22:36:42 +02:00
}
return c
2015-05-23 18:40:53 +02:00
}
func (r *rpcClient) newCodec(contentType string) (codec.NewCodec, error) {
if c, ok := r.opts.Codecs[contentType]; ok {
return c, nil
2015-11-25 21:50:05 +02:00
}
if cf, ok := DefaultCodecs[contentType]; ok {
2015-11-25 21:50:05 +02:00
return cf, nil
}
return nil, fmt.Errorf("unsupported Content-Type: %s", contentType)
2015-11-25 21:50:05 +02:00
}
func (r *rpcClient) call(
ctx context.Context,
node *registry.Node,
req Request,
resp interface{},
opts CallOptions,
) error {
2019-01-18 14:30:39 +02:00
address := node.Address
logger := r.Options().Logger
2019-01-18 14:30:39 +02:00
2015-05-20 23:57:19 +02:00
msg := &transport.Message{
Header: make(map[string]string),
}
2015-01-14 01:31:27 +02:00
2016-01-28 19:55:28 +02:00
md, ok := metadata.FromContext(ctx)
2015-05-23 12:53:40 +02:00
if ok {
for k, v := range md {
// Don't copy Micro-Topic header, that is used for pub/sub
// this is fixes the case when the client uses the same context that
// is received in the subscriber.
if k == headers.Message {
continue
}
2015-05-23 12:53:40 +02:00
msg.Header[k] = v
2015-05-20 23:57:19 +02:00
}
2015-01-14 01:31:27 +02:00
}
// Set connection timeout for single requests to the server. Should be > 0
// as otherwise requests can't be made.
cTimeout := opts.ConnectionTimeout
if cTimeout == 0 {
logger.Log(log.DebugLevel, "connection timeout was set to 0, overridng to default connection timeout")
cTimeout = DefaultConnectionTimeout
}
2016-05-13 00:32:58 +02:00
// set timeout in nanoseconds
msg.Header["Timeout"] = fmt.Sprintf("%d", cTimeout)
2016-05-13 00:32:58 +02:00
// set the content type for the request
2016-01-05 23:13:20 +02:00
msg.Header["Content-Type"] = req.ContentType()
// set the accept header
msg.Header["Accept"] = req.ContentType()
2015-05-20 23:57:19 +02:00
2019-01-18 14:30:39 +02:00
// setup old protocol
reqCodec := setupProtocol(msg, node)
2019-01-18 14:30:39 +02:00
// no codec specified
if reqCodec == nil {
2019-01-18 14:30:39 +02:00
var err error
reqCodec, err = r.newCodec(req.ContentType())
2019-01-18 14:30:39 +02:00
if err != nil {
return merrors.InternalServerError("go.micro.client", err.Error())
2019-01-18 14:30:39 +02:00
}
2015-11-25 21:50:05 +02:00
}
2019-08-31 19:26:48 +02:00
dOpts := []transport.DialOption{
transport.WithStream(),
}
if opts.DialTimeout >= 0 {
dOpts = append(dOpts, transport.WithTimeout(opts.DialTimeout))
}
if opts.ConnClose {
dOpts = append(dOpts, transport.WithConnClose())
}
2019-08-31 19:26:48 +02:00
c, err := r.pool.Get(address, dOpts...)
2015-01-14 01:31:27 +02:00
if err != nil {
if c == nil {
return merrors.InternalServerError("go.micro.client", "connection error: %v", err)
}
logger.Log(log.ErrorLevel, "failed to close pool", err)
2015-01-14 01:31:27 +02:00
}
2020-05-19 15:44:46 +02:00
seq := atomic.AddUint64(&r.seq, 1) - 1
codec := newRPCCodec(msg, c, reqCodec, "")
2019-01-14 23:30:43 +02:00
rsp := &rpcResponse{
socket: c,
codec: codec,
}
2018-03-21 05:17:38 +02:00
releaseFunc := func(err error) {
if err = r.pool.Release(c, err); err != nil {
logger.Log(log.ErrorLevel, "failed to release pool", err)
}
}
2016-01-05 23:13:20 +02:00
stream := &rpcStream{
2019-08-16 17:46:29 +02:00
id: fmt.Sprintf("%v", seq),
2019-01-14 23:30:43 +02:00
context: ctx,
request: req,
response: rsp,
codec: codec,
closed: make(chan bool),
close: opts.ConnClose,
release: releaseFunc,
sendEOS: false,
2016-01-05 23:13:20 +02:00
}
2019-08-16 17:46:29 +02:00
// close the stream on exiting this function
defer func() {
if err := stream.Close(); err != nil {
logger.Log(log.ErrorLevel, "failed to close stream", err)
}
}()
2016-01-03 23:14:33 +02:00
2019-08-16 17:46:29 +02:00
// wait for error response
2016-01-03 23:14:33 +02:00
ch := make(chan error, 1)
go func() {
2016-06-30 17:19:02 +02:00
defer func() {
if r := recover(); r != nil {
ch <- merrors.InternalServerError("go.micro.client", "panic recovered: %v", r)
2016-06-30 17:19:02 +02:00
}
}()
2016-01-05 23:13:20 +02:00
// send request
2019-01-10 13:39:39 +02:00
if err := stream.Send(req.Body()); err != nil {
2016-01-05 23:13:20 +02:00
ch <- err
return
2016-01-03 23:14:33 +02:00
}
2016-01-05 23:13:20 +02:00
2024-03-26 05:39:45 +02:00
// recv response
2016-01-05 23:13:20 +02:00
if err := stream.Recv(resp); err != nil {
ch <- err
return
}
// success
ch <- nil
2016-01-03 23:14:33 +02:00
}()
2019-08-16 17:46:29 +02:00
var grr error
2016-01-03 23:14:33 +02:00
select {
2016-05-13 00:32:58 +02:00
case err := <-ch:
return err
case <-time.After(cTimeout):
grr = merrors.Timeout("go.micro.client", fmt.Sprintf("%v", ctx.Err()))
2015-10-22 16:14:56 +02:00
}
2019-08-16 17:46:29 +02:00
// set the stream error
if grr != nil {
stream.Lock()
stream.err = grr
stream.Unlock()
return grr
}
return nil
}
2015-01-14 01:31:27 +02:00
2019-01-18 14:30:39 +02:00
func (r *rpcClient) stream(ctx context.Context, node *registry.Node, req Request, opts CallOptions) (Stream, error) {
address := node.Address
logger := r.Options().Logger
2019-01-18 14:30:39 +02:00
msg := &transport.Message{
Header: make(map[string]string),
2015-01-14 01:31:27 +02:00
}
2016-01-28 19:55:28 +02:00
md, ok := metadata.FromContext(ctx)
if ok {
for k, v := range md {
msg.Header[k] = v
}
2015-01-14 01:31:27 +02:00
}
2016-05-13 00:32:58 +02:00
// set timeout in nanoseconds
if opts.StreamTimeout > time.Duration(0) {
msg.Header["Timeout"] = fmt.Sprintf("%d", opts.StreamTimeout)
}
2016-05-13 00:32:58 +02:00
// set the content type for the request
2015-12-17 22:37:35 +02:00
msg.Header["Content-Type"] = req.ContentType()
// set the accept header
msg.Header["Accept"] = req.ContentType()
2015-01-14 01:31:27 +02:00
2019-01-18 14:30:39 +02:00
// set old codecs
nCodec := setupProtocol(msg, node)
2019-01-18 14:30:39 +02:00
// no codec specified
if nCodec == nil {
2019-01-18 14:30:39 +02:00
var err error
nCodec, err = r.newCodec(req.ContentType())
2019-01-18 14:30:39 +02:00
if err != nil {
return nil, merrors.InternalServerError("go.micro.client", err.Error())
2019-01-18 14:30:39 +02:00
}
2015-11-25 21:50:05 +02:00
}
2018-07-18 01:32:35 +02:00
dOpts := []transport.DialOption{
transport.WithStream(),
}
if opts.DialTimeout >= 0 {
dOpts = append(dOpts, transport.WithTimeout(opts.DialTimeout))
}
c, err := r.opts.Transport.Dial(address, dOpts...)
2015-01-14 01:31:27 +02:00
if err != nil {
return nil, merrors.InternalServerError("go.micro.client", "connection error: %v", err)
2015-01-14 01:31:27 +02:00
}
2019-08-15 21:08:49 +02:00
// increment the sequence number
2020-05-19 15:44:46 +02:00
seq := atomic.AddUint64(&r.seq, 1) - 1
2019-08-15 21:08:49 +02:00
id := fmt.Sprintf("%v", seq)
// create codec with stream id
codec := newRPCCodec(msg, c, nCodec, id)
2019-01-14 23:30:43 +02:00
rsp := &rpcResponse{
socket: c,
codec: codec,
}
2019-01-30 20:42:11 +02:00
// set request codec
if r, ok := req.(*rpcRequest); ok {
r.codec = codec
}
2015-12-18 03:21:56 +02:00
stream := &rpcStream{
2019-08-16 17:46:29 +02:00
id: id,
2019-01-14 23:30:43 +02:00
context: ctx,
request: req,
response: rsp,
2019-01-30 20:42:11 +02:00
codec: codec,
2019-08-16 17:46:29 +02:00
// used to close the stream
closed: make(chan bool),
2019-08-15 21:08:49 +02:00
// signal the end of stream,
sendEOS: true,
release: func(_ error) {},
2015-12-18 03:21:56 +02:00
}
2019-08-16 17:46:29 +02:00
// wait for error response
2016-01-03 23:14:33 +02:00
ch := make(chan error, 1)
go func() {
2019-08-16 17:46:29 +02:00
// send the first message
2019-01-10 13:39:39 +02:00
ch <- stream.Send(req.Body())
2016-01-03 23:14:33 +02:00
}()
2016-05-13 00:32:58 +02:00
var grr error
2016-01-03 23:14:33 +02:00
select {
2016-05-13 00:32:58 +02:00
case err := <-ch:
grr = err
case <-ctx.Done():
grr = merrors.Timeout("go.micro.client", fmt.Sprintf("%v", ctx.Err()))
2016-01-03 23:14:33 +02:00
}
2016-05-13 00:32:58 +02:00
if grr != nil {
2019-08-16 17:46:29 +02:00
// set the error
stream.Lock()
stream.err = grr
stream.Unlock()
// close the stream
if err := stream.Close(); err != nil {
logger.Logf(log.ErrorLevel, "failed to close stream: %v", err)
}
2016-05-13 00:32:58 +02:00
return nil, grr
}
return stream, nil
2015-01-14 01:31:27 +02:00
}
2016-01-02 21:12:17 +02:00
func (r *rpcClient) Init(opts ...Option) error {
r.mu.Lock()
defer r.mu.Unlock()
2016-06-07 01:46:14 +02:00
size := r.opts.PoolSize
ttl := r.opts.PoolTTL
2019-07-28 19:56:18 +02:00
tr := r.opts.Transport
2016-06-07 01:46:14 +02:00
2016-01-02 21:12:17 +02:00
for _, o := range opts {
o(&r.opts)
}
2016-06-07 01:46:14 +02:00
// update pool configuration if the options changed
2019-07-28 19:56:18 +02:00
if size != r.opts.PoolSize || ttl != r.opts.PoolTTL || tr != r.opts.Transport {
// close existing pool
if err := r.pool.Close(); err != nil {
return errors.Wrap(err, "failed to close pool")
}
2019-07-28 19:56:18 +02:00
// create new pool
r.pool = pool.NewPool(
pool.Size(r.opts.PoolSize),
pool.TTL(r.opts.PoolTTL),
pool.Transport(r.opts.Transport),
)
2016-06-07 01:46:14 +02:00
}
2016-01-02 21:12:17 +02:00
return nil
}
// Options retrives the options.
2016-01-02 21:12:17 +02:00
func (r *rpcClient) Options() Options {
r.mu.RLock()
defer r.mu.RUnlock()
2016-01-02 21:12:17 +02:00
return r.opts
}
2022-09-30 16:27:07 +02:00
// next returns an iterator for the next nodes to call.
2018-04-14 17:16:58 +02:00
func (r *rpcClient) next(request Request, opts CallOptions) (selector.Next, error) {
// try get the proxy
service, address, _ := net.Proxy(request.Service(), opts.Address)
2019-01-30 20:42:11 +02:00
2018-04-14 17:16:58 +02:00
// return remote address
if len(address) > 0 {
nodes := make([]*registry.Node, len(address))
2019-01-18 14:30:39 +02:00
for i, addr := range address {
nodes[i] = &registry.Node{
Address: addr,
// Set the protocol
Metadata: map[string]string{
"protocol": "mucp",
},
}
2019-06-26 21:51:13 +02:00
}
// crude return method
return func() (*registry.Node, error) {
return nodes[time.Now().Unix()%int64(len(nodes))], nil
2018-04-14 17:16:58 +02:00
}, nil
}
// get next nodes from the selector
2019-11-30 23:20:33 +02:00
next, err := r.opts.Selector.Select(service, opts.SelectOptions...)
if err != nil {
if errors.Is(err, selector.ErrNotFound) {
return nil, merrors.InternalServerError("go.micro.client", "service %s: %s", service, err.Error())
}
return nil, merrors.InternalServerError("go.micro.client", "error selecting %s node: %s", service, err.Error())
}
2018-04-14 17:16:58 +02:00
return next, nil
2015-01-14 01:31:27 +02:00
}
2015-12-08 21:25:42 +02:00
func (r *rpcClient) Call(ctx context.Context, request Request, response interface{}, opts ...CallOption) error {
// TODO: further validate these mutex locks. full lock would prevent
// parallel calls. Maybe we can set individual locks for secctions.
r.mu.RLock()
defer r.mu.RUnlock()
// make a copy of call opts
callOpts := r.opts.CallOptions
2015-12-09 02:02:45 +02:00
for _, opt := range opts {
opt(&callOpts)
2015-12-09 02:02:45 +02:00
}
2018-04-14 17:16:58 +02:00
next, err := r.next(request, callOpts)
if err != nil {
return err
2015-12-09 02:02:45 +02:00
}
2016-05-13 00:32:58 +02:00
// check if we already have a deadline
d, ok := ctx.Deadline()
if !ok {
// no deadline so we create a new one
2019-12-03 09:25:58 +02:00
var cancel context.CancelFunc
ctx, cancel = context.WithTimeout(ctx, callOpts.RequestTimeout)
2019-12-03 09:25:58 +02:00
defer cancel()
2016-05-13 00:32:58 +02:00
} else {
// got a deadline so no need to setup context
// but we need to set the timeout we pass along
opt := WithRequestTimeout(time.Until(d))
2016-05-13 00:32:58 +02:00
opt(&callOpts)
}
2016-01-03 23:14:33 +02:00
// should we noop right here?
select {
case <-ctx.Done():
return merrors.Timeout("go.micro.client", fmt.Sprintf("%v", ctx.Err()))
default:
}
// make copy of call method
rcall := r.call
// wrap the call in reverse
for i := len(callOpts.CallWrappers); i > 0; i-- {
rcall = callOpts.CallWrappers[i-1](rcall)
}
2016-05-13 00:32:58 +02:00
// return errors.New("go.micro.client", "request timeout", 408)
call := func(i int) error {
2016-04-05 21:04:37 +02:00
// call backoff first. Someone may want an initial start delay
t, err := callOpts.Backoff(ctx, request, i)
if err != nil {
return merrors.InternalServerError("go.micro.client", "backoff error: %v", err.Error())
2016-04-05 21:04:37 +02:00
}
// only sleep if greater than 0
if t.Seconds() > 0 {
time.Sleep(t)
}
2016-05-13 00:32:58 +02:00
// select next node
2016-01-03 01:16:15 +02:00
node, err := next()
service := request.Service()
if err != nil {
if errors.Is(err, selector.ErrNotFound) {
return merrors.InternalServerError("go.micro.client", "service %s: %s", service, err.Error())
}
return merrors.InternalServerError("go.micro.client",
"error getting next %s node: %s",
service,
err.Error())
2016-01-03 01:16:15 +02:00
}
2016-05-13 00:32:58 +02:00
// make the call
2019-01-18 14:30:39 +02:00
err = rcall(ctx, node, request, response, callOpts)
r.opts.Selector.Mark(service, node, err)
2016-05-13 00:32:58 +02:00
return err
}
// get the retries
retries := callOpts.Retries
// disable retries when using a proxy
// Note: I don't see why we should disable retries for proxies, so commenting out.
// if _, _, ok := net.Proxy(request.Service(), callOpts.Address); ok {
// retries = 0
// }
ch := make(chan error, retries+1)
2016-05-13 00:32:58 +02:00
var gerr error
2015-01-14 01:31:27 +02:00
for i := 0; i <= retries; i++ {
go func(i int) {
2016-05-13 00:32:58 +02:00
ch <- call(i)
}(i)
2016-05-13 00:32:58 +02:00
select {
case <-ctx.Done():
return merrors.Timeout("go.micro.client", fmt.Sprintf("call timeout: %v", ctx.Err()))
2016-05-13 00:32:58 +02:00
case err := <-ch:
// if the call succeeded lets bail early
if err == nil {
return nil
}
retry, rerr := callOpts.Retry(ctx, request, i, err)
if rerr != nil {
return rerr
}
if !retry {
return err
}
r.opts.Logger.Logf(log.DebugLevel, "Retrying request. Previous attempt failed with: %v", err)
2016-05-13 00:32:58 +02:00
gerr = err
2016-01-03 01:16:15 +02:00
}
2015-05-21 20:24:57 +02:00
}
2016-05-13 00:32:58 +02:00
return gerr
2015-01-14 01:31:27 +02:00
}
2018-04-14 19:15:09 +02:00
func (r *rpcClient) Stream(ctx context.Context, request Request, opts ...CallOption) (Stream, error) {
r.mu.RLock()
defer r.mu.RUnlock()
// make a copy of call opts
callOpts := r.opts.CallOptions
2015-12-09 02:02:45 +02:00
for _, opt := range opts {
opt(&callOpts)
2015-12-09 02:02:45 +02:00
}
2018-04-14 17:16:58 +02:00
next, err := r.next(request, callOpts)
if err != nil {
return nil, err
2015-12-09 02:02:45 +02:00
}
select {
case <-ctx.Done():
return nil, merrors.Timeout("go.micro.client", fmt.Sprintf("%v", ctx.Err()))
default:
}
2018-04-14 19:15:09 +02:00
call := func(i int) (Stream, error) {
2016-04-05 21:04:37 +02:00
// call backoff first. Someone may want an initial start delay
t, err := callOpts.Backoff(ctx, request, i)
if err != nil {
return nil, merrors.InternalServerError("go.micro.client", "backoff error: %v", err.Error())
2016-04-05 21:04:37 +02:00
}
// only sleep if greater than 0
if t.Seconds() > 0 {
time.Sleep(t)
}
2016-01-03 01:16:15 +02:00
node, err := next()
service := request.Service()
if err != nil {
if errors.Is(err, selector.ErrNotFound) {
return nil, merrors.InternalServerError("go.micro.client", "service %s: %s", service, err.Error())
}
return nil, merrors.InternalServerError("go.micro.client",
"error getting next %s node: %s",
service,
err.Error())
2016-01-03 01:16:15 +02:00
}
2019-01-18 14:30:39 +02:00
stream, err := r.stream(ctx, node, request, callOpts)
r.opts.Selector.Mark(service, node, err)
2016-05-13 00:32:58 +02:00
return stream, err
}
type response struct {
2018-04-14 19:15:09 +02:00
stream Stream
2016-05-13 00:32:58 +02:00
err error
}
2016-01-03 01:16:15 +02:00
// get the retries
retries := callOpts.Retries
// disable retries when using a proxy
if _, _, ok := net.Proxy(request.Service(), callOpts.Address); ok {
retries = 0
}
ch := make(chan response, retries+1)
2016-05-13 00:32:58 +02:00
var grr error
for i := 0; i <= retries; i++ {
go func(i int) {
2016-05-13 00:32:58 +02:00
s, err := call(i)
ch <- response{s, err}
}(i)
2016-05-13 00:32:58 +02:00
select {
case <-ctx.Done():
return nil, merrors.Timeout("go.micro.client", fmt.Sprintf("call timeout: %v", ctx.Err()))
2016-05-13 00:32:58 +02:00
case rsp := <-ch:
// if the call succeeded lets bail early
if rsp.err == nil {
return rsp.stream, nil
}
retry, rerr := callOpts.Retry(ctx, request, i, rsp.err)
if rerr != nil {
return nil, rerr
}
if !retry {
return nil, rsp.err
}
2016-11-07 18:46:12 +02:00
grr = rsp.err
2016-01-03 01:16:15 +02:00
}
}
2016-05-13 00:32:58 +02:00
return nil, grr
}
2018-04-14 19:06:52 +02:00
func (r *rpcClient) Publish(ctx context.Context, msg Message, opts ...PublishOption) error {
2019-02-23 12:50:53 +02:00
options := PublishOptions{
Context: context.Background(),
}
for _, o := range opts {
o(&options)
}
metadata, ok := metadata.FromContext(ctx)
if !ok {
metadata = make(map[string]string)
}
2019-01-10 22:35:10 +02:00
id := uuid.New().String()
metadata["Content-Type"] = msg.ContentType()
metadata[headers.Message] = msg.Topic()
metadata[headers.ID] = id
2019-02-23 19:06:17 +02:00
// set the topic
2019-02-23 12:50:53 +02:00
topic := msg.Topic()
2019-02-23 19:06:17 +02:00
// get the exchange
2019-02-23 12:50:53 +02:00
if len(options.Exchange) > 0 {
topic = options.Exchange
}
// encode message body
2018-04-14 19:06:52 +02:00
cf, err := r.newCodec(msg.ContentType())
if err != nil {
return merrors.InternalServerError(packageID, err.Error())
}
var body []byte
// passed in raw data
if d, ok := msg.Payload().(*raw.Frame); ok {
body = d.Data
} else {
b := buf.New(nil)
if err = cf(b).Write(&codec.Message{
Target: topic,
Type: codec.Event,
Header: map[string]string{
headers.ID: id,
headers.Message: msg.Topic(),
},
}, msg.Payload()); err != nil {
return merrors.InternalServerError(packageID, err.Error())
}
// set the body
body = b.Bytes()
}
l, ok := r.once.Load().(bool)
if !ok {
return fmt.Errorf("failed to cast to bool")
}
if !l {
if err = r.opts.Broker.Connect(); err != nil {
return merrors.InternalServerError(packageID, err.Error())
}
r.once.Store(true)
}
2019-02-23 12:50:53 +02:00
return r.opts.Broker.Publish(topic, &broker.Message{
Header: metadata,
Body: body,
}, broker.PublishContext(options.Context))
}
2018-05-10 18:33:54 +02:00
func (r *rpcClient) NewMessage(topic string, message interface{}, opts ...MessageOption) Message {
return newMessage(topic, message, r.opts.ContentType, opts...)
}
2015-12-17 22:37:35 +02:00
func (r *rpcClient) NewRequest(service, method string, request interface{}, reqOpts ...RequestOption) Request {
2018-04-14 19:06:52 +02:00
return newRequest(service, method, request, r.opts.ContentType, reqOpts...)
2015-01-14 01:31:27 +02:00
}
2015-12-19 23:56:14 +02:00
func (r *rpcClient) String() string {
2019-06-08 20:40:44 +02:00
return "mucp"
2015-12-19 23:56:14 +02:00
}