You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
go-zero/zrpc/internal/client.go

192 lines
5.2 KiB
Go

4 years ago
package internal
4 years ago
import (
"context"
"errors"
4 years ago
"fmt"
"strings"
4 years ago
"time"
"github.com/zeromicro/go-zero/zrpc/internal/balancer/p2c"
"github.com/zeromicro/go-zero/zrpc/internal/clientinterceptors"
"github.com/zeromicro/go-zero/zrpc/resolver"
4 years ago
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/insecure"
4 years ago
)
const (
dialTimeout = time.Second * 3
separator = '/'
)
4 years ago
func init() {
resolver.Register()
}
4 years ago
type (
// Client interface wraps the Conn method.
Client interface {
Conn() *grpc.ClientConn
}
// A ClientOptions is a client options.
4 years ago
ClientOptions struct {
NonBlock bool
4 years ago
Timeout time.Duration
Secure bool
4 years ago
DialOptions []grpc.DialOption
}
// ClientOption defines the method to customize a ClientOptions.
4 years ago
ClientOption func(options *ClientOptions)
client struct {
conn *grpc.ClientConn
middlewares ClientMiddlewaresConf
}
4 years ago
)
// NewClient returns a Client.
func NewClient(target string, middlewares ClientMiddlewaresConf, opts ...ClientOption) (Client, error) {
cli := client{
middlewares: middlewares,
}
svcCfg := fmt.Sprintf(`{"loadBalancingPolicy":"%s"}`, p2c.Name)
balancerOpt := WithDialOption(grpc.WithDefaultServiceConfig(svcCfg))
opts = append([]ClientOption{balancerOpt}, opts...)
if err := cli.dial(target, opts...); err != nil {
return nil, err
}
return &cli, nil
}
func (c *client) Conn() *grpc.ClientConn {
return c.conn
4 years ago
}
func (c *client) buildDialOptions(opts ...ClientOption) []grpc.DialOption {
var cliOpts ClientOptions
4 years ago
for _, opt := range opts {
opt(&cliOpts)
4 years ago
}
var options []grpc.DialOption
if !cliOpts.Secure {
options = append([]grpc.DialOption(nil),
grpc.WithTransportCredentials(insecure.NewCredentials()))
}
if !cliOpts.NonBlock {
options = append(options, grpc.WithBlock())
}
options = append(options,
grpc.WithChainUnaryInterceptor(c.buildUnaryInterceptors(cliOpts.Timeout)...),
grpc.WithChainStreamInterceptor(c.buildStreamInterceptors()...),
)
4 years ago
return append(options, cliOpts.DialOptions...)
4 years ago
}
func (c *client) buildStreamInterceptors() []grpc.StreamClientInterceptor {
var interceptors []grpc.StreamClientInterceptor
if c.middlewares.Trace {
interceptors = append(interceptors, clientinterceptors.StreamTracingInterceptor)
}
return interceptors
}
func (c *client) buildUnaryInterceptors(timeout time.Duration) []grpc.UnaryClientInterceptor {
var interceptors []grpc.UnaryClientInterceptor
if c.middlewares.Trace {
interceptors = append(interceptors, clientinterceptors.UnaryTracingInterceptor)
}
if c.middlewares.Duration {
interceptors = append(interceptors, clientinterceptors.DurationInterceptor)
}
if c.middlewares.Prometheus {
interceptors = append(interceptors, clientinterceptors.PrometheusInterceptor)
}
if c.middlewares.Breaker {
interceptors = append(interceptors, clientinterceptors.BreakerInterceptor)
}
if c.middlewares.Timeout {
interceptors = append(interceptors, clientinterceptors.TimeoutInterceptor(timeout))
}
return interceptors
}
func (c *client) dial(server string, opts ...ClientOption) error {
options := c.buildDialOptions(opts...)
4 years ago
timeCtx, cancel := context.WithTimeout(context.Background(), dialTimeout)
defer cancel()
conn, err := grpc.DialContext(timeCtx, server, options...)
if err != nil {
service := server
if errors.Is(err, context.DeadlineExceeded) {
pos := strings.LastIndexByte(server, separator)
// len(server) - 1 is the index of last char
if 0 < pos && pos < len(server)-1 {
service = server[pos+1:]
}
}
return fmt.Errorf("rpc dial: %s, error: %s, make sure rpc service %q is already started",
server, err.Error(), service)
4 years ago
}
c.conn = conn
return nil
}
// WithDialOption returns a func to customize a ClientOptions with given dial option.
func WithDialOption(opt grpc.DialOption) ClientOption {
return func(options *ClientOptions) {
options.DialOptions = append(options.DialOptions, opt)
}
}
// WithNonBlock sets the dialing to be nonblock.
func WithNonBlock() ClientOption {
return func(options *ClientOptions) {
options.NonBlock = true
}
}
// WithStreamClientInterceptor returns a func to customize a ClientOptions with given interceptor.
func WithStreamClientInterceptor(interceptor grpc.StreamClientInterceptor) ClientOption {
return func(options *ClientOptions) {
options.DialOptions = append(options.DialOptions,
grpc.WithChainStreamInterceptor(interceptor))
}
}
// WithTimeout returns a func to customize a ClientOptions with given timeout.
func WithTimeout(timeout time.Duration) ClientOption {
return func(options *ClientOptions) {
options.Timeout = timeout
}
4 years ago
}
// WithTransportCredentials return a func to make the gRPC calls secured with given credentials.
func WithTransportCredentials(creds credentials.TransportCredentials) ClientOption {
return func(options *ClientOptions) {
options.Secure = true
options.DialOptions = append(options.DialOptions, grpc.WithTransportCredentials(creds))
}
}
// WithUnaryClientInterceptor returns a func to customize a ClientOptions with given interceptor.
func WithUnaryClientInterceptor(interceptor grpc.UnaryClientInterceptor) ClientOption {
return func(options *ClientOptions) {
options.DialOptions = append(options.DialOptions,
grpc.WithChainUnaryInterceptor(interceptor))
}
}