package grpc import ( "context" "time" "github.com/go-kratos/kratos/v2/middleware" "github.com/go-kratos/kratos/v2/middleware/recovery" "github.com/go-kratos/kratos/v2/middleware/status" "github.com/go-kratos/kratos/v2/registry" "github.com/go-kratos/kratos/v2/transport" "github.com/go-kratos/kratos/v2/transport/grpc/resolver/discovery" "google.golang.org/grpc" ) // ClientOption is gRPC client option. type ClientOption func(o *clientOptions) // WithEndpoint with client endpoint. func WithEndpoint(endpoint string) ClientOption { return func(o *clientOptions) { o.endpoint = endpoint } } // WithTimeout with client timeout. func WithTimeout(timeout time.Duration) ClientOption { return func(o *clientOptions) { o.timeout = timeout } } // WithMiddleware with client middleware. func WithMiddleware(m middleware.Middleware) ClientOption { return func(o *clientOptions) { o.middleware = m } } // WithDiscoverer with client registry. func WithDiscoverer(d registry.Discoverer) ClientOption { return func(o *clientOptions) { o.discoverer = d } } // WithOptions with gRPC options. func WithOptions(opts ...grpc.DialOption) ClientOption { return func(o *clientOptions) { o.grpcOpts = opts } } // clientOptions is gRPC Client type clientOptions struct { endpoint string timeout time.Duration middleware middleware.Middleware discoverer registry.Discoverer grpcOpts []grpc.DialOption } // Dial returns a GRPC connection. func Dial(ctx context.Context, opts ...ClientOption) (*grpc.ClientConn, error) { return dial(ctx, false, opts...) } // DialInsecure returns an insecure GRPC connection. func DialInsecure(ctx context.Context, opts ...ClientOption) (*grpc.ClientConn, error) { return dial(ctx, true, opts...) } func dial(ctx context.Context, insecure bool, opts ...ClientOption) (*grpc.ClientConn, error) { options := clientOptions{ timeout: 500 * time.Millisecond, middleware: middleware.Chain( recovery.Recovery(), status.Client(), ), } for _, o := range opts { o(&options) } var grpcOpts = []grpc.DialOption{ grpc.WithTimeout(options.timeout), grpc.WithUnaryInterceptor(UnaryClientInterceptor(options.middleware)), } if options.discoverer != nil { grpc.WithResolvers(discovery.NewBuilder(options.discoverer)) } if insecure { grpcOpts = append(grpcOpts, grpc.WithInsecure()) } if len(options.grpcOpts) > 0 { grpcOpts = append(grpcOpts, options.grpcOpts...) } return grpc.DialContext(ctx, options.endpoint, grpcOpts...) } // UnaryClientInterceptor retruns a unary client interceptor. func UnaryClientInterceptor(m middleware.Middleware) grpc.UnaryClientInterceptor { return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { ctx = transport.NewContext(ctx, transport.Transport{Kind: "gRPC"}) ctx = NewClientContext(ctx, ClientInfo{FullMethod: method}) h := func(ctx context.Context, req interface{}) (interface{}, error) { return reply, invoker(ctx, method, req, reply, cc, opts...) } if m != nil { h = m(h) } _, err := h(ctx, req) return err } }