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 }