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 }