github.com/shuguocloud/go-zero@v1.3.0/zrpc/internal/client.go (about)

     1  package internal
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"strings"
     8  	"time"
     9  
    10  	"github.com/shuguocloud/go-zero/zrpc/internal/balancer/p2c"
    11  	"github.com/shuguocloud/go-zero/zrpc/internal/clientinterceptors"
    12  	"github.com/shuguocloud/go-zero/zrpc/resolver"
    13  	"google.golang.org/grpc"
    14  	"google.golang.org/grpc/credentials"
    15  )
    16  
    17  const (
    18  	dialTimeout = time.Second * 3
    19  	separator   = '/'
    20  )
    21  
    22  func init() {
    23  	resolver.Register()
    24  }
    25  
    26  type (
    27  	// Client interface wraps the Conn method.
    28  	Client interface {
    29  		Conn() *grpc.ClientConn
    30  	}
    31  
    32  	// A ClientOptions is a client options.
    33  	ClientOptions struct {
    34  		NonBlock    bool
    35  		Timeout     time.Duration
    36  		Secure      bool
    37  		DialOptions []grpc.DialOption
    38  	}
    39  
    40  	// ClientOption defines the method to customize a ClientOptions.
    41  	ClientOption func(options *ClientOptions)
    42  
    43  	client struct {
    44  		conn *grpc.ClientConn
    45  	}
    46  )
    47  
    48  // NewClient returns a Client.
    49  func NewClient(target string, opts ...ClientOption) (Client, error) {
    50  	var cli client
    51  	opts = append([]ClientOption{WithDialOption(grpc.WithBalancerName(p2c.Name))}, opts...)
    52  	if err := cli.dial(target, opts...); err != nil {
    53  		return nil, err
    54  	}
    55  
    56  	return &cli, nil
    57  }
    58  
    59  func (c *client) Conn() *grpc.ClientConn {
    60  	return c.conn
    61  }
    62  
    63  func (c *client) buildDialOptions(opts ...ClientOption) []grpc.DialOption {
    64  	var cliOpts ClientOptions
    65  	for _, opt := range opts {
    66  		opt(&cliOpts)
    67  	}
    68  
    69  	var options []grpc.DialOption
    70  	if !cliOpts.Secure {
    71  		options = append([]grpc.DialOption(nil), grpc.WithInsecure())
    72  	}
    73  
    74  	if !cliOpts.NonBlock {
    75  		options = append(options, grpc.WithBlock())
    76  	}
    77  
    78  	options = append(options,
    79  		WithUnaryClientInterceptors(
    80  			clientinterceptors.UnaryTracingInterceptor,
    81  			clientinterceptors.DurationInterceptor,
    82  			clientinterceptors.PrometheusInterceptor,
    83  			clientinterceptors.BreakerInterceptor,
    84  			clientinterceptors.TimeoutInterceptor(cliOpts.Timeout),
    85  		),
    86  		WithStreamClientInterceptors(
    87  			clientinterceptors.StreamTracingInterceptor,
    88  		),
    89  	)
    90  
    91  	return append(options, cliOpts.DialOptions...)
    92  }
    93  
    94  func (c *client) dial(server string, opts ...ClientOption) error {
    95  	options := c.buildDialOptions(opts...)
    96  	timeCtx, cancel := context.WithTimeout(context.Background(), dialTimeout)
    97  	defer cancel()
    98  	conn, err := grpc.DialContext(timeCtx, server, options...)
    99  	if err != nil {
   100  		service := server
   101  		if errors.Is(err, context.DeadlineExceeded) {
   102  			pos := strings.LastIndexByte(server, separator)
   103  			// len(server) - 1 is the index of last char
   104  			if 0 < pos && pos < len(server)-1 {
   105  				service = server[pos+1:]
   106  			}
   107  		}
   108  		return fmt.Errorf("rpc dial: %s, error: %s, make sure rpc service %q is already started",
   109  			server, err.Error(), service)
   110  	}
   111  
   112  	c.conn = conn
   113  	return nil
   114  }
   115  
   116  // WithDialOption returns a func to customize a ClientOptions with given dial option.
   117  func WithDialOption(opt grpc.DialOption) ClientOption {
   118  	return func(options *ClientOptions) {
   119  		options.DialOptions = append(options.DialOptions, opt)
   120  	}
   121  }
   122  
   123  // WithNonBlock sets the dialing to be nonblock.
   124  func WithNonBlock() ClientOption {
   125  	return func(options *ClientOptions) {
   126  		options.NonBlock = true
   127  	}
   128  }
   129  
   130  // WithTimeout returns a func to customize a ClientOptions with given timeout.
   131  func WithTimeout(timeout time.Duration) ClientOption {
   132  	return func(options *ClientOptions) {
   133  		options.Timeout = timeout
   134  	}
   135  }
   136  
   137  // WithTransportCredentials return a func to make the gRPC calls secured with given credentials.
   138  func WithTransportCredentials(creds credentials.TransportCredentials) ClientOption {
   139  	return func(options *ClientOptions) {
   140  		options.Secure = true
   141  		options.DialOptions = append(options.DialOptions, grpc.WithTransportCredentials(creds))
   142  	}
   143  }
   144  
   145  // WithUnaryClientInterceptor returns a func to customize a ClientOptions with given interceptor.
   146  func WithUnaryClientInterceptor(interceptor grpc.UnaryClientInterceptor) ClientOption {
   147  	return func(options *ClientOptions) {
   148  		options.DialOptions = append(options.DialOptions, WithUnaryClientInterceptors(interceptor))
   149  	}
   150  }