gitee.com/liuxuezhan/go-micro-v1.18.0@v1.0.0/client/rpc_client.go (about)

     1  package client
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os"
     7  	"sync"
     8  	"sync/atomic"
     9  	"time"
    10  
    11  	"github.com/google/uuid"
    12  	"gitee.com/liuxuezhan/go-micro-v1.18.0/broker"
    13  	"gitee.com/liuxuezhan/go-micro-v1.18.0/client/pool"
    14  	"gitee.com/liuxuezhan/go-micro-v1.18.0/client/selector"
    15  	"gitee.com/liuxuezhan/go-micro-v1.18.0/codec"
    16  	raw "gitee.com/liuxuezhan/go-micro-v1.18.0/codec/bytes"
    17  	"gitee.com/liuxuezhan/go-micro-v1.18.0/errors"
    18  	"gitee.com/liuxuezhan/go-micro-v1.18.0/metadata"
    19  	"gitee.com/liuxuezhan/go-micro-v1.18.0/registry"
    20  	"gitee.com/liuxuezhan/go-micro-v1.18.0/transport"
    21  	"gitee.com/liuxuezhan/go-micro-v1.18.0/util/buf"
    22  )
    23  
    24  type rpcClient struct {
    25  	once sync.Once
    26  	opts Options
    27  	pool pool.Pool
    28  	seq  uint64
    29  }
    30  
    31  func newRpcClient(opt ...Option) Client {
    32  	opts := newOptions(opt...)
    33  
    34  	p := pool.NewPool(
    35  		pool.Size(opts.PoolSize),
    36  		pool.TTL(opts.PoolTTL),
    37  		pool.Transport(opts.Transport),
    38  	)
    39  
    40  	rc := &rpcClient{
    41  		once: sync.Once{},
    42  		opts: opts,
    43  		pool: p,
    44  		seq:  0,
    45  	}
    46  
    47  	c := Client(rc)
    48  
    49  	// wrap in reverse
    50  	for i := len(opts.Wrappers); i > 0; i-- {
    51  		c = opts.Wrappers[i-1](c)
    52  	}
    53  
    54  	return c
    55  }
    56  
    57  func (r *rpcClient) newCodec(contentType string) (codec.NewCodec, error) {
    58  	if c, ok := r.opts.Codecs[contentType]; ok {
    59  		return c, nil
    60  	}
    61  	if cf, ok := DefaultCodecs[contentType]; ok {
    62  		return cf, nil
    63  	}
    64  	return nil, fmt.Errorf("Unsupported Content-Type: %s", contentType)
    65  }
    66  
    67  func (r *rpcClient) call(ctx context.Context, node *registry.Node, req Request, resp interface{}, opts CallOptions) error {
    68  	address := node.Address
    69  
    70  	msg := &transport.Message{
    71  		Header: make(map[string]string),
    72  	}
    73  
    74  	md, ok := metadata.FromContext(ctx)
    75  	if ok {
    76  		for k, v := range md {
    77  			msg.Header[k] = v
    78  		}
    79  	}
    80  
    81  	// set timeout in nanoseconds
    82  	msg.Header["Timeout"] = fmt.Sprintf("%d", opts.RequestTimeout)
    83  	// set the content type for the request
    84  	msg.Header["Content-Type"] = req.ContentType()
    85  	// set the accept header
    86  	msg.Header["Accept"] = req.ContentType()
    87  
    88  	// setup old protocol
    89  	cf := setupProtocol(msg, node)
    90  
    91  	// no codec specified
    92  	if cf == nil {
    93  		var err error
    94  		cf, err = r.newCodec(req.ContentType())
    95  		if err != nil {
    96  			return errors.InternalServerError("go.micro.client", err.Error())
    97  		}
    98  	}
    99  
   100  	dOpts := []transport.DialOption{
   101  		transport.WithStream(),
   102  	}
   103  
   104  	if opts.DialTimeout >= 0 {
   105  		dOpts = append(dOpts, transport.WithTimeout(opts.DialTimeout))
   106  	}
   107  
   108  	c, err := r.pool.Get(address, dOpts...)
   109  	if err != nil {
   110  		return errors.InternalServerError("go.micro.client", "connection error: %v", err)
   111  	}
   112  
   113  	seq := atomic.LoadUint64(&r.seq)
   114  	atomic.AddUint64(&r.seq, 1)
   115  	codec := newRpcCodec(msg, c, cf, "")
   116  
   117  	rsp := &rpcResponse{
   118  		socket: c,
   119  		codec:  codec,
   120  	}
   121  
   122  	stream := &rpcStream{
   123  		id:       fmt.Sprintf("%v", seq),
   124  		context:  ctx,
   125  		request:  req,
   126  		response: rsp,
   127  		codec:    codec,
   128  		closed:   make(chan bool),
   129  		release:  func(err error) { r.pool.Release(c, err) },
   130  		sendEOS:  false,
   131  	}
   132  	// close the stream on exiting this function
   133  	defer stream.Close()
   134  
   135  	// wait for error response
   136  	ch := make(chan error, 1)
   137  
   138  	go func() {
   139  		defer func() {
   140  			if r := recover(); r != nil {
   141  				ch <- errors.InternalServerError("go.micro.client", "panic recovered: %v", r)
   142  			}
   143  		}()
   144  
   145  		// send request
   146  		if err := stream.Send(req.Body()); err != nil {
   147  			ch <- err
   148  			return
   149  		}
   150  
   151  		// recv request
   152  		if err := stream.Recv(resp); err != nil {
   153  			ch <- err
   154  			return
   155  		}
   156  
   157  		// success
   158  		ch <- nil
   159  	}()
   160  
   161  	var grr error
   162  
   163  	select {
   164  	case err := <-ch:
   165  		return err
   166  	case <-ctx.Done():
   167  		grr = errors.Timeout("go.micro.client", fmt.Sprintf("%v", ctx.Err()))
   168  	}
   169  
   170  	// set the stream error
   171  	if grr != nil {
   172  		stream.Lock()
   173  		stream.err = grr
   174  		stream.Unlock()
   175  
   176  		return grr
   177  	}
   178  
   179  	return nil
   180  }
   181  
   182  func (r *rpcClient) stream(ctx context.Context, node *registry.Node, req Request, opts CallOptions) (Stream, error) {
   183  	address := node.Address
   184  
   185  	msg := &transport.Message{
   186  		Header: make(map[string]string),
   187  	}
   188  
   189  	md, ok := metadata.FromContext(ctx)
   190  	if ok {
   191  		for k, v := range md {
   192  			msg.Header[k] = v
   193  		}
   194  	}
   195  
   196  	// set timeout in nanoseconds
   197  	msg.Header["Timeout"] = fmt.Sprintf("%d", opts.RequestTimeout)
   198  	// set the content type for the request
   199  	msg.Header["Content-Type"] = req.ContentType()
   200  	// set the accept header
   201  	msg.Header["Accept"] = req.ContentType()
   202  
   203  	// set old codecs
   204  	cf := setupProtocol(msg, node)
   205  
   206  	// no codec specified
   207  	if cf == nil {
   208  		var err error
   209  		cf, err = r.newCodec(req.ContentType())
   210  		if err != nil {
   211  			return nil, errors.InternalServerError("go.micro.client", err.Error())
   212  		}
   213  	}
   214  
   215  	dOpts := []transport.DialOption{
   216  		transport.WithStream(),
   217  	}
   218  
   219  	if opts.DialTimeout >= 0 {
   220  		dOpts = append(dOpts, transport.WithTimeout(opts.DialTimeout))
   221  	}
   222  
   223  	c, err := r.opts.Transport.Dial(address, dOpts...)
   224  	if err != nil {
   225  		return nil, errors.InternalServerError("go.micro.client", "connection error: %v", err)
   226  	}
   227  
   228  	// increment the sequence number
   229  	seq := atomic.LoadUint64(&r.seq)
   230  	atomic.AddUint64(&r.seq, 1)
   231  	id := fmt.Sprintf("%v", seq)
   232  
   233  	// create codec with stream id
   234  	codec := newRpcCodec(msg, c, cf, id)
   235  
   236  	rsp := &rpcResponse{
   237  		socket: c,
   238  		codec:  codec,
   239  	}
   240  
   241  	// set request codec
   242  	if r, ok := req.(*rpcRequest); ok {
   243  		r.codec = codec
   244  	}
   245  
   246  	stream := &rpcStream{
   247  		id:       id,
   248  		context:  ctx,
   249  		request:  req,
   250  		response: rsp,
   251  		codec:    codec,
   252  		// used to close the stream
   253  		closed: make(chan bool),
   254  		// signal the end of stream,
   255  		sendEOS: true,
   256  		// release func
   257  		release: func(err error) { c.Close() },
   258  	}
   259  
   260  	// wait for error response
   261  	ch := make(chan error, 1)
   262  
   263  	go func() {
   264  		// send the first message
   265  		ch <- stream.Send(req.Body())
   266  	}()
   267  
   268  	var grr error
   269  
   270  	select {
   271  	case err := <-ch:
   272  		grr = err
   273  	case <-ctx.Done():
   274  		grr = errors.Timeout("go.micro.client", fmt.Sprintf("%v", ctx.Err()))
   275  	}
   276  
   277  	if grr != nil {
   278  		// set the error
   279  		stream.Lock()
   280  		stream.err = grr
   281  		stream.Unlock()
   282  
   283  		// close the stream
   284  		stream.Close()
   285  		return nil, grr
   286  	}
   287  
   288  	return stream, nil
   289  }
   290  
   291  func (r *rpcClient) Init(opts ...Option) error {
   292  	size := r.opts.PoolSize
   293  	ttl := r.opts.PoolTTL
   294  	tr := r.opts.Transport
   295  
   296  	for _, o := range opts {
   297  		o(&r.opts)
   298  	}
   299  
   300  	// update pool configuration if the options changed
   301  	if size != r.opts.PoolSize || ttl != r.opts.PoolTTL || tr != r.opts.Transport {
   302  		// close existing pool
   303  		r.pool.Close()
   304  		// create new pool
   305  		r.pool = pool.NewPool(
   306  			pool.Size(r.opts.PoolSize),
   307  			pool.TTL(r.opts.PoolTTL),
   308  			pool.Transport(r.opts.Transport),
   309  		)
   310  	}
   311  
   312  	return nil
   313  }
   314  
   315  func (r *rpcClient) Options() Options {
   316  	return r.opts
   317  }
   318  
   319  // hasProxy checks if we have proxy set in the environment
   320  func (r *rpcClient) hasProxy() bool {
   321  	// get proxy
   322  	if prx := os.Getenv("MICRO_PROXY"); len(prx) > 0 {
   323  		return true
   324  	}
   325  
   326  	// get proxy address
   327  	if prx := os.Getenv("MICRO_PROXY_ADDRESS"); len(prx) > 0 {
   328  		return true
   329  	}
   330  
   331  	return false
   332  }
   333  
   334  // next returns an iterator for the next nodes to call
   335  func (r *rpcClient) next(request Request, opts CallOptions) (selector.Next, error) {
   336  	service := request.Service()
   337  
   338  	// get proxy
   339  	if prx := os.Getenv("MICRO_PROXY"); len(prx) > 0 {
   340  		service = prx
   341  	}
   342  
   343  	// get proxy address
   344  	if prx := os.Getenv("MICRO_PROXY_ADDRESS"); len(prx) > 0 {
   345  		opts.Address = []string{prx}
   346  	}
   347  
   348  	// return remote address
   349  	if len(opts.Address) > 0 {
   350  		nodes := make([]*registry.Node, len(opts.Address))
   351  
   352  		for i, address := range opts.Address {
   353  			nodes[i] = &registry.Node{
   354  				Address: address,
   355  				// Set the protocol
   356  				Metadata: map[string]string{
   357  					"protocol": "mucp",
   358  				},
   359  			}
   360  		}
   361  
   362  		// crude return method
   363  		return func() (*registry.Node, error) {
   364  			return nodes[time.Now().Unix()%int64(len(nodes))], nil
   365  		}, nil
   366  	}
   367  
   368  	// get next nodes from the selector
   369  	next, err := r.opts.Selector.Select(service, opts.SelectOptions...)
   370  	if err != nil {
   371  		if err == selector.ErrNotFound {
   372  			return nil, errors.InternalServerError("go.micro.client", "service %s: %s", service, err.Error())
   373  		}
   374  		return nil, errors.InternalServerError("go.micro.client", "error selecting %s node: %s", service, err.Error())
   375  	}
   376  
   377  	return next, nil
   378  }
   379  
   380  func (r *rpcClient) Call(ctx context.Context, request Request, response interface{}, opts ...CallOption) error {
   381  	// make a copy of call opts
   382  	callOpts := r.opts.CallOptions
   383  	for _, opt := range opts {
   384  		opt(&callOpts)
   385  	}
   386  
   387  	next, err := r.next(request, callOpts)
   388  	if err != nil {
   389  		return err
   390  	}
   391  
   392  	// check if we already have a deadline
   393  	d, ok := ctx.Deadline()
   394  	if !ok {
   395  		// no deadline so we create a new one
   396  		var cancel context.CancelFunc
   397  		ctx, cancel = context.WithTimeout(ctx, callOpts.RequestTimeout)
   398  		defer cancel()
   399  	} else {
   400  		// got a deadline so no need to setup context
   401  		// but we need to set the timeout we pass along
   402  		opt := WithRequestTimeout(d.Sub(time.Now()))
   403  		opt(&callOpts)
   404  	}
   405  
   406  	// should we noop right here?
   407  	select {
   408  	case <-ctx.Done():
   409  		return errors.Timeout("go.micro.client", fmt.Sprintf("%v", ctx.Err()))
   410  	default:
   411  	}
   412  
   413  	// make copy of call method
   414  	rcall := r.call
   415  
   416  	// wrap the call in reverse
   417  	for i := len(callOpts.CallWrappers); i > 0; i-- {
   418  		rcall = callOpts.CallWrappers[i-1](rcall)
   419  	}
   420  
   421  	// return errors.New("go.micro.client", "request timeout", 408)
   422  	call := func(i int) error {
   423  		// call backoff first. Someone may want an initial start delay
   424  		t, err := callOpts.Backoff(ctx, request, i)
   425  		if err != nil {
   426  			return errors.InternalServerError("go.micro.client", "backoff error: %v", err.Error())
   427  		}
   428  
   429  		// only sleep if greater than 0
   430  		if t.Seconds() > 0 {
   431  			time.Sleep(t)
   432  		}
   433  
   434  		// select next node
   435  		node, err := next()
   436  		service := request.Service()
   437  		if err != nil {
   438  			if err == selector.ErrNotFound {
   439  				return errors.InternalServerError("go.micro.client", "service %s: %s", service, err.Error())
   440  			}
   441  			return errors.InternalServerError("go.micro.client", "error getting next %s node: %s", service, err.Error())
   442  		}
   443  
   444  		// make the call
   445  		err = rcall(ctx, node, request, response, callOpts)
   446  		r.opts.Selector.Mark(service, node, err)
   447  		return err
   448  	}
   449  
   450  	// get the retries
   451  	retries := callOpts.Retries
   452  
   453  	// disable retries when using a proxy
   454  	if r.hasProxy() {
   455  		retries = 0
   456  	}
   457  
   458  	ch := make(chan error, retries+1)
   459  	var gerr error
   460  
   461  	for i := 0; i <= retries; i++ {
   462  		go func(i int) {
   463  			ch <- call(i)
   464  		}(i)
   465  
   466  		select {
   467  		case <-ctx.Done():
   468  			return errors.Timeout("go.micro.client", fmt.Sprintf("call timeout: %v", ctx.Err()))
   469  		case err := <-ch:
   470  			// if the call succeeded lets bail early
   471  			if err == nil {
   472  				return nil
   473  			}
   474  
   475  			retry, rerr := callOpts.Retry(ctx, request, i, err)
   476  			if rerr != nil {
   477  				return rerr
   478  			}
   479  
   480  			if !retry {
   481  				return err
   482  			}
   483  
   484  			gerr = err
   485  		}
   486  	}
   487  
   488  	return gerr
   489  }
   490  
   491  func (r *rpcClient) Stream(ctx context.Context, request Request, opts ...CallOption) (Stream, error) {
   492  	// make a copy of call opts
   493  	callOpts := r.opts.CallOptions
   494  	for _, opt := range opts {
   495  		opt(&callOpts)
   496  	}
   497  
   498  	next, err := r.next(request, callOpts)
   499  	if err != nil {
   500  		return nil, err
   501  	}
   502  
   503  	// should we noop right here?
   504  	select {
   505  	case <-ctx.Done():
   506  		return nil, errors.Timeout("go.micro.client", fmt.Sprintf("%v", ctx.Err()))
   507  	default:
   508  	}
   509  
   510  	call := func(i int) (Stream, error) {
   511  		// call backoff first. Someone may want an initial start delay
   512  		t, err := callOpts.Backoff(ctx, request, i)
   513  		if err != nil {
   514  			return nil, errors.InternalServerError("go.micro.client", "backoff error: %v", err.Error())
   515  		}
   516  
   517  		// only sleep if greater than 0
   518  		if t.Seconds() > 0 {
   519  			time.Sleep(t)
   520  		}
   521  
   522  		node, err := next()
   523  		service := request.Service()
   524  		if err != nil {
   525  			if err == selector.ErrNotFound {
   526  				return nil, errors.InternalServerError("go.micro.client", "service %s: %s", service, err.Error())
   527  			}
   528  			return nil, errors.InternalServerError("go.micro.client", "error getting next %s node: %s", service, err.Error())
   529  		}
   530  
   531  		stream, err := r.stream(ctx, node, request, callOpts)
   532  		r.opts.Selector.Mark(service, node, err)
   533  		return stream, err
   534  	}
   535  
   536  	type response struct {
   537  		stream Stream
   538  		err    error
   539  	}
   540  
   541  	// get the retries
   542  	retries := callOpts.Retries
   543  
   544  	// disable retries when using a proxy
   545  	if r.hasProxy() {
   546  		retries = 0
   547  	}
   548  
   549  	ch := make(chan response, retries+1)
   550  	var grr error
   551  
   552  	for i := 0; i <= retries; i++ {
   553  		go func(i int) {
   554  			s, err := call(i)
   555  			ch <- response{s, err}
   556  		}(i)
   557  
   558  		select {
   559  		case <-ctx.Done():
   560  			return nil, errors.Timeout("go.micro.client", fmt.Sprintf("call timeout: %v", ctx.Err()))
   561  		case rsp := <-ch:
   562  			// if the call succeeded lets bail early
   563  			if rsp.err == nil {
   564  				return rsp.stream, nil
   565  			}
   566  
   567  			retry, rerr := callOpts.Retry(ctx, request, i, rsp.err)
   568  			if rerr != nil {
   569  				return nil, rerr
   570  			}
   571  
   572  			if !retry {
   573  				return nil, rsp.err
   574  			}
   575  
   576  			grr = rsp.err
   577  		}
   578  	}
   579  
   580  	return nil, grr
   581  }
   582  
   583  func (r *rpcClient) Publish(ctx context.Context, msg Message, opts ...PublishOption) error {
   584  	options := PublishOptions{
   585  		Context: context.Background(),
   586  	}
   587  	for _, o := range opts {
   588  		o(&options)
   589  	}
   590  
   591  	md, ok := metadata.FromContext(ctx)
   592  	if !ok {
   593  		md = make(map[string]string)
   594  	}
   595  
   596  	id := uuid.New().String()
   597  	md["Content-Type"] = msg.ContentType()
   598  	md["Micro-Topic"] = msg.Topic()
   599  	md["Micro-Id"] = id
   600  
   601  	// set the topic
   602  	topic := msg.Topic()
   603  
   604  	// get proxy
   605  	if prx := os.Getenv("MICRO_PROXY"); len(prx) > 0 {
   606  		options.Exchange = prx
   607  	}
   608  
   609  	// get the exchange
   610  	if len(options.Exchange) > 0 {
   611  		topic = options.Exchange
   612  	}
   613  
   614  	// encode message body
   615  	cf, err := r.newCodec(msg.ContentType())
   616  	if err != nil {
   617  		return errors.InternalServerError("go.micro.client", err.Error())
   618  	}
   619  
   620  	var body []byte
   621  
   622  	// passed in raw data
   623  	if d, ok := msg.Payload().(*raw.Frame); ok {
   624  		body = d.Data
   625  	} else {
   626  		// new buffer
   627  		b := buf.New(nil)
   628  
   629  		if err := cf(b).Write(&codec.Message{
   630  			Target: topic,
   631  			Type:   codec.Event,
   632  			Header: map[string]string{
   633  				"Micro-Id":    id,
   634  				"Micro-Topic": msg.Topic(),
   635  			},
   636  		}, msg.Payload()); err != nil {
   637  			return errors.InternalServerError("go.micro.client", err.Error())
   638  		}
   639  
   640  		// set the body
   641  		body = b.Bytes()
   642  	}
   643  
   644  	r.once.Do(func() {
   645  		r.opts.Broker.Connect()
   646  	})
   647  
   648  	return r.opts.Broker.Publish(topic, &broker.Message{
   649  		Header: md,
   650  		Body:   body,
   651  	})
   652  }
   653  
   654  func (r *rpcClient) NewMessage(topic string, message interface{}, opts ...MessageOption) Message {
   655  	return newMessage(topic, message, r.opts.ContentType, opts...)
   656  }
   657  
   658  func (r *rpcClient) NewRequest(service, method string, request interface{}, reqOpts ...RequestOption) Request {
   659  	return newRequest(service, method, request, r.opts.ContentType, reqOpts...)
   660  }
   661  
   662  func (r *rpcClient) String() string {
   663  	return "mucp"
   664  }