go.temporal.io/server@v1.23.0/common/rpc/grpc.go (about)

     1  // The MIT License
     2  //
     3  // Copyright (c) 2020 Temporal Technologies Inc.  All rights reserved.
     4  //
     5  // Copyright (c) 2020 Uber Technologies, Inc.
     6  //
     7  // Permission is hereby granted, free of charge, to any person obtaining a copy
     8  // of this software and associated documentation files (the "Software"), to deal
     9  // in the Software without restriction, including without limitation the rights
    10  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    11  // copies of the Software, and to permit persons to whom the Software is
    12  // furnished to do so, subject to the following conditions:
    13  //
    14  // The above copyright notice and this permission notice shall be included in
    15  // all copies or substantial portions of the Software.
    16  //
    17  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    18  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    19  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    20  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    21  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    22  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    23  // THE SOFTWARE.
    24  
    25  package rpc
    26  
    27  import (
    28  	"context"
    29  	"crypto/tls"
    30  	"time"
    31  
    32  	"go.temporal.io/api/serviceerror"
    33  	"google.golang.org/grpc"
    34  	"google.golang.org/grpc/backoff"
    35  	"google.golang.org/grpc/credentials"
    36  	"google.golang.org/grpc/credentials/insecure"
    37  	"google.golang.org/grpc/status"
    38  
    39  	"go.temporal.io/server/common/headers"
    40  	"go.temporal.io/server/common/log"
    41  	"go.temporal.io/server/common/metrics"
    42  	"go.temporal.io/server/common/rpc/interceptor"
    43  	serviceerrors "go.temporal.io/server/common/serviceerror"
    44  )
    45  
    46  const (
    47  	// DefaultServiceConfig is a default gRPC connection service config which enables DNS round robin between IPs.
    48  	// To use DNS resolver, a "dns:///" prefix should be applied to the hostPort.
    49  	// https://github.com/grpc/grpc/blob/master/doc/naming.md
    50  	DefaultServiceConfig = `{"loadBalancingConfig": [{"round_robin":{}}]}`
    51  
    52  	// MaxBackoffDelay is a maximum interval between reconnect attempts.
    53  	MaxBackoffDelay = 10 * time.Second
    54  
    55  	// MaxHTTPAPIRequestBytes is the maximum number of bytes an HTTP API request
    56  	// can have. This is currently set to the max gRPC request size.
    57  	MaxHTTPAPIRequestBytes = 4 * 1024 * 1024
    58  
    59  	// minConnectTimeout is the minimum amount of time we are willing to give a connection to complete.
    60  	minConnectTimeout = 20 * time.Second
    61  
    62  	// maxInternodeRecvPayloadSize indicates the internode max receive payload size.
    63  	maxInternodeRecvPayloadSize = 128 * 1024 * 1024 // 128 Mb
    64  )
    65  
    66  // Dial creates a client connection to the given target with default options.
    67  // The hostName syntax is defined in
    68  // https://github.com/grpc/grpc/blob/master/doc/naming.md.
    69  // e.g. to use dns resolver, a "dns:///" prefix should be applied to the target.
    70  func Dial(hostName string, tlsConfig *tls.Config, logger log.Logger, interceptors ...grpc.UnaryClientInterceptor) (*grpc.ClientConn, error) {
    71  	var grpcSecureOpt grpc.DialOption
    72  	if tlsConfig == nil {
    73  		grpcSecureOpt = grpc.WithTransportCredentials(insecure.NewCredentials())
    74  	} else {
    75  		grpcSecureOpt = grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig))
    76  	}
    77  
    78  	// gRPC maintains connection pool inside grpc.ClientConn.
    79  	// This connection pool has auto reconnect feature.
    80  	// If connection goes down, gRPC will try to reconnect using exponential backoff strategy:
    81  	// https://github.com/grpc/grpc/blob/master/doc/connection-backoff.md.
    82  	// Default MaxDelay is 120 seconds which is too high.
    83  	var cp = grpc.ConnectParams{
    84  		Backoff:           backoff.DefaultConfig,
    85  		MinConnectTimeout: minConnectTimeout,
    86  	}
    87  	cp.Backoff.MaxDelay = MaxBackoffDelay
    88  
    89  	dialOptions := []grpc.DialOption{
    90  		grpcSecureOpt,
    91  		grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(maxInternodeRecvPayloadSize)),
    92  		grpc.WithChainUnaryInterceptor(
    93  			append(
    94  				interceptors,
    95  				headersInterceptor,
    96  				metrics.NewClientMetricsTrailerPropagatorInterceptor(logger),
    97  				errorInterceptor,
    98  			)...,
    99  		),
   100  		grpc.WithChainStreamInterceptor(
   101  			interceptor.StreamErrorInterceptor,
   102  		),
   103  		grpc.WithDefaultServiceConfig(DefaultServiceConfig),
   104  		grpc.WithDisableServiceConfig(),
   105  		grpc.WithConnectParams(cp),
   106  	}
   107  
   108  	return grpc.Dial(
   109  		hostName,
   110  		dialOptions...,
   111  	)
   112  }
   113  
   114  func errorInterceptor(
   115  	ctx context.Context,
   116  	method string,
   117  	req, reply interface{},
   118  	cc *grpc.ClientConn,
   119  	invoker grpc.UnaryInvoker,
   120  	opts ...grpc.CallOption,
   121  ) error {
   122  	err := invoker(ctx, method, req, reply, cc, opts...)
   123  	err = serviceerrors.FromStatus(status.Convert(err))
   124  	return err
   125  }
   126  
   127  func headersInterceptor(
   128  	ctx context.Context,
   129  	method string,
   130  	req, reply interface{},
   131  	cc *grpc.ClientConn,
   132  	invoker grpc.UnaryInvoker,
   133  	opts ...grpc.CallOption,
   134  ) error {
   135  	ctx = headers.Propagate(ctx)
   136  	return invoker(ctx, method, req, reply, cc, opts...)
   137  }
   138  
   139  func ServiceErrorInterceptor(
   140  	ctx context.Context,
   141  	req interface{},
   142  	_ *grpc.UnaryServerInfo,
   143  	handler grpc.UnaryHandler,
   144  ) (interface{}, error) {
   145  	resp, err := handler(ctx, req)
   146  	return resp, serviceerror.ToStatus(err).Err()
   147  }