dubbo.apache.org/dubbo-go/v3@v3.1.1/protocol/dubbo3/dubbo3_invoker.go (about) 1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package dubbo3 19 20 import ( 21 "context" 22 "reflect" 23 "strconv" 24 "strings" 25 "sync" 26 "time" 27 ) 28 29 import ( 30 "github.com/dubbogo/gost/log/logger" 31 32 "github.com/dubbogo/grpc-go/metadata" 33 34 tripleConstant "github.com/dubbogo/triple/pkg/common/constant" 35 triConfig "github.com/dubbogo/triple/pkg/config" 36 "github.com/dubbogo/triple/pkg/triple" 37 38 "github.com/dustin/go-humanize" 39 ) 40 41 import ( 42 "dubbo.apache.org/dubbo-go/v3/common" 43 "dubbo.apache.org/dubbo-go/v3/common/constant" 44 "dubbo.apache.org/dubbo-go/v3/config" 45 "dubbo.apache.org/dubbo-go/v3/protocol" 46 invocation_impl "dubbo.apache.org/dubbo-go/v3/protocol/invocation" 47 ) 48 49 // same as dubbo_invoker.go attachmentKey 50 var attachmentKey = []string{ 51 constant.InterfaceKey, constant.GroupKey, constant.TokenKey, constant.TimeoutKey, 52 constant.VersionKey, tripleConstant.TripleServiceGroup, tripleConstant.TripleServiceVersion, 53 } 54 55 // DubboInvoker is implement of protocol.Invoker, a dubboInvoker refer to one service and ip. 56 type DubboInvoker struct { 57 protocol.BaseInvoker 58 // the net layer client, it is focus on network communication. 59 client *triple.TripleClient 60 // quitOnce is used to make sure DubboInvoker is only destroyed once 61 quitOnce sync.Once 62 // timeout for service(interface) level. 63 timeout time.Duration 64 // clientGuard is the client lock of dubbo invoker 65 clientGuard *sync.RWMutex 66 } 67 68 // NewDubboInvoker constructor 69 func NewDubboInvoker(url *common.URL) (*DubboInvoker, error) { 70 rt := config.GetConsumerConfig().RequestTimeout 71 72 timeout := url.GetParamDuration(constant.TimeoutKey, rt) 73 // for triple pb serialization. The bean name from provider is the provider reference key, 74 // which can't locate the target consumer stub, so we use interface key.. 75 interfaceKey := url.GetParam(constant.InterfaceKey, "") 76 consumerService := config.GetConsumerServiceByInterfaceName(interfaceKey) 77 78 dubboSerializerType := url.GetParam(constant.SerializationKey, constant.ProtobufSerialization) 79 triCodecType := tripleConstant.CodecType(dubboSerializerType) 80 // new triple client 81 opts := []triConfig.OptionFunction{ 82 triConfig.WithClientTimeout(timeout), 83 triConfig.WithCodecType(triCodecType), 84 triConfig.WithLocation(url.Location), 85 triConfig.WithHeaderAppVersion(url.GetParam(constant.AppVersionKey, "")), 86 triConfig.WithHeaderGroup(url.GetParam(constant.GroupKey, "")), 87 triConfig.WithLogger(logger.GetLogger()), 88 } 89 maxCallRecvMsgSize := constant.DefaultMaxCallRecvMsgSize 90 if maxCall, err := humanize.ParseBytes(url.GetParam(constant.MaxCallRecvMsgSize, "")); err == nil && maxCall != 0 { 91 maxCallRecvMsgSize = int(maxCall) 92 } 93 maxCallSendMsgSize := constant.DefaultMaxCallSendMsgSize 94 if maxCall, err := humanize.ParseBytes(url.GetParam(constant.MaxCallSendMsgSize, "")); err == nil && maxCall != 0 { 95 maxCallSendMsgSize = int(maxCall) 96 } 97 opts = append(opts, triConfig.WithGRPCMaxCallRecvMessageSize(maxCallRecvMsgSize)) 98 opts = append(opts, triConfig.WithGRPCMaxCallSendMessageSize(maxCallSendMsgSize)) 99 100 tracingKey := url.GetParam(constant.TracingConfigKey, "") 101 if tracingKey != "" { 102 tracingConfig := config.GetTracingConfig(tracingKey) 103 if tracingConfig != nil { 104 if tracingConfig.Name == "jaeger" { 105 if tracingConfig.ServiceName == "" { 106 tracingConfig.ServiceName = config.GetApplicationConfig().Name 107 } 108 opts = append(opts, triConfig.WithJaegerConfig( 109 tracingConfig.Address, 110 tracingConfig.ServiceName, 111 *tracingConfig.UseAgent, 112 )) 113 } else { 114 logger.Warnf("unsupported tracing name %s, now triple only support jaeger", tracingConfig.Name) 115 } 116 } 117 } 118 119 triOption := triConfig.NewTripleOption(opts...) 120 tlsConfig := config.GetRootConfig().TLSConfig 121 if tlsConfig != nil { 122 triOption.TLSCertFile = tlsConfig.TLSCertFile 123 triOption.TLSKeyFile = tlsConfig.TLSKeyFile 124 triOption.CACertFile = tlsConfig.CACertFile 125 triOption.TLSServerName = tlsConfig.TLSServerName 126 logger.Infof("Triple Client initialized the TLSConfig configuration") 127 } 128 client, err := triple.NewTripleClient(consumerService, triOption) 129 130 if err != nil { 131 return nil, err 132 } 133 134 return &DubboInvoker{ 135 BaseInvoker: *protocol.NewBaseInvoker(url), 136 client: client, 137 timeout: timeout, 138 clientGuard: &sync.RWMutex{}, 139 }, nil 140 } 141 142 func (di *DubboInvoker) setClient(client *triple.TripleClient) { 143 di.clientGuard.Lock() 144 defer di.clientGuard.Unlock() 145 146 di.client = client 147 } 148 149 func (di *DubboInvoker) getClient() *triple.TripleClient { 150 di.clientGuard.RLock() 151 defer di.clientGuard.RUnlock() 152 153 return di.client 154 } 155 156 // Invoke call remoting. 157 func (di *DubboInvoker) Invoke(ctx context.Context, invocation protocol.Invocation) protocol.Result { 158 var ( 159 result protocol.RPCResult 160 ) 161 162 if !di.BaseInvoker.IsAvailable() { 163 // Generally, the case will not happen, because the invoker has been removed 164 // from the invoker list before destroy,so no new request will enter the destroyed invoker 165 logger.Warnf("this dubboInvoker is destroyed") 166 result.Err = protocol.ErrDestroyedInvoker 167 return &result 168 } 169 170 di.clientGuard.RLock() 171 defer di.clientGuard.RUnlock() 172 173 if di.client == nil { 174 result.Err = protocol.ErrClientClosed 175 return &result 176 } 177 178 if !di.BaseInvoker.IsAvailable() { 179 // Generally, the case will not happen, because the invoker has been removed 180 // from the invoker list before destroy,so no new request will enter the destroyed invoker 181 logger.Warnf("this grpcInvoker is destroying") 182 result.Err = protocol.ErrDestroyedInvoker 183 return &result 184 } 185 186 for _, k := range attachmentKey { 187 var paramKey string 188 switch k { 189 case tripleConstant.TripleServiceGroup: 190 paramKey = constant.GroupKey 191 case tripleConstant.TripleServiceVersion: 192 paramKey = constant.VersionKey 193 default: 194 paramKey = k 195 } 196 197 if v := di.GetURL().GetParam(paramKey, ""); len(v) > 0 { 198 invocation.SetAttachment(k, v) 199 } 200 } 201 202 // append interface id to ctx 203 gRPCMD := make(metadata.MD, 0) 204 // triple will convert attachment value to []string 205 for k, v := range invocation.Attachments() { 206 if str, ok := v.(string); ok { 207 gRPCMD.Set(k, str) 208 continue 209 } 210 if str, ok := v.([]string); ok { 211 gRPCMD.Set(k, str...) 212 continue 213 } 214 logger.Warnf("[Triple Protocol]Triple attachment value with key = %s is invalid, which should be string or []string", k) 215 } 216 ctx = metadata.NewOutgoingContext(ctx, gRPCMD) 217 ctx = context.WithValue(ctx, tripleConstant.InterfaceKey, di.BaseInvoker.GetURL().GetParam(constant.InterfaceKey, "")) 218 in := make([]reflect.Value, 0, 16) 219 in = append(in, reflect.ValueOf(ctx)) 220 221 if len(invocation.ParameterValues()) > 0 { 222 in = append(in, invocation.ParameterValues()...) 223 } 224 225 methodName := invocation.MethodName() 226 triAttachmentWithErr := di.client.Invoke(methodName, in, invocation.Reply()) 227 result.Err = triAttachmentWithErr.GetError() 228 result.Attrs = make(map[string]interface{}) 229 for k, v := range triAttachmentWithErr.GetAttachments() { 230 result.Attrs[k] = v 231 } 232 result.Rest = invocation.Reply() 233 return &result 234 } 235 236 // get timeout including methodConfig 237 func (di *DubboInvoker) getTimeout(invocation *invocation_impl.RPCInvocation) time.Duration { 238 timeout := di.GetURL().GetParam(strings.Join([]string{constant.MethodKeys, invocation.MethodName(), constant.TimeoutKey}, "."), "") 239 if len(timeout) != 0 { 240 if t, err := time.ParseDuration(timeout); err == nil { 241 // config timeout into attachment 242 invocation.SetAttachment(constant.TimeoutKey, strconv.Itoa(int(t.Milliseconds()))) 243 return t 244 } 245 } 246 // set timeout into invocation at method level 247 invocation.SetAttachment(constant.TimeoutKey, strconv.Itoa(int(di.timeout.Milliseconds()))) 248 return di.timeout 249 } 250 251 // IsAvailable check if invoker is available, now it is useless 252 func (di *DubboInvoker) IsAvailable() bool { 253 client := di.getClient() 254 if client != nil { 255 // FIXME here can't check if tcp server is started now!!! 256 return client.IsAvailable() 257 } 258 return false 259 } 260 261 // Destroy destroy dubbo3 client invoker. 262 func (di *DubboInvoker) Destroy() { 263 di.quitOnce.Do(func() { 264 di.BaseInvoker.Destroy() 265 client := di.getClient() 266 if client != nil { 267 di.setClient(nil) 268 client.Close() 269 } 270 }) 271 }