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

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