dubbo.apache.org/dubbo-go/v3@v3.1.1/protocol/dubbo/dubbo_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 dubbo 19 20 import ( 21 "context" 22 "strconv" 23 "strings" 24 "sync" 25 "time" 26 ) 27 28 import ( 29 "github.com/dubbogo/gost/log/logger" 30 31 "github.com/opentracing/opentracing-go" 32 ) 33 34 import ( 35 "dubbo.apache.org/dubbo-go/v3/common" 36 "dubbo.apache.org/dubbo-go/v3/common/constant" 37 "dubbo.apache.org/dubbo-go/v3/config" 38 "dubbo.apache.org/dubbo-go/v3/protocol" 39 "dubbo.apache.org/dubbo-go/v3/protocol/invocation" 40 "dubbo.apache.org/dubbo-go/v3/remoting" 41 ) 42 43 var attachmentKey = []string{ 44 constant.InterfaceKey, constant.GroupKey, constant.TokenKey, constant.TimeoutKey, 45 constant.VersionKey, 46 } 47 48 // DubboInvoker is implement of protocol.Invoker. A dubboInvoker refers to one service and ip. 49 type DubboInvoker struct { 50 protocol.BaseInvoker 51 clientGuard *sync.RWMutex // the exchange layer, it is focus on network communication. 52 client *remoting.ExchangeClient 53 quitOnce sync.Once 54 timeout time.Duration // timeout for service(interface) level. 55 } 56 57 // NewDubboInvoker constructor 58 func NewDubboInvoker(url *common.URL, client *remoting.ExchangeClient) *DubboInvoker { 59 rt := config.GetConsumerConfig().RequestTimeout 60 61 timeout := url.GetParamDuration(constant.TimeoutKey, rt) 62 di := &DubboInvoker{ 63 BaseInvoker: *protocol.NewBaseInvoker(url), 64 clientGuard: &sync.RWMutex{}, 65 client: client, 66 timeout: timeout, 67 } 68 69 return di 70 } 71 72 func (di *DubboInvoker) setClient(client *remoting.ExchangeClient) { 73 di.clientGuard.Lock() 74 defer di.clientGuard.Unlock() 75 76 di.client = client 77 } 78 79 func (di *DubboInvoker) getClient() *remoting.ExchangeClient { 80 di.clientGuard.RLock() 81 defer di.clientGuard.RUnlock() 82 83 return di.client 84 } 85 86 // Invoke call remoting. 87 func (di *DubboInvoker) Invoke(ctx context.Context, ivc protocol.Invocation) protocol.Result { 88 var ( 89 err error 90 result protocol.RPCResult 91 ) 92 if !di.BaseInvoker.IsAvailable() { 93 // Generally, the case will not happen, because the invoker has been removed 94 // from the invoker list before destroy,so no new request will enter the destroyed invoker 95 logger.Warnf("this dubboInvoker is destroyed") 96 result.Err = protocol.ErrDestroyedInvoker 97 return &result 98 } 99 100 di.clientGuard.RLock() 101 defer di.clientGuard.RUnlock() 102 103 if di.client == nil { 104 result.Err = protocol.ErrClientClosed 105 logger.Debugf("result.Err: %v", result.Err) 106 return &result 107 } 108 109 if !di.BaseInvoker.IsAvailable() { 110 // Generally, the case will not happen, because the invoker has been removed 111 // from the invoker list before destroy,so no new request will enter the destroyed invoker 112 logger.Warnf("this dubboInvoker is destroying") 113 result.Err = protocol.ErrDestroyedInvoker 114 return &result 115 } 116 117 inv := ivc.(*invocation.RPCInvocation) 118 // init param 119 inv.SetAttachment(constant.PathKey, di.GetURL().GetParam(constant.InterfaceKey, "")) 120 for _, k := range attachmentKey { 121 if v := di.GetURL().GetParam(k, ""); len(v) > 0 { 122 inv.SetAttachment(k, v) 123 } 124 } 125 126 // put the ctx into attachment 127 di.appendCtx(ctx, inv) 128 129 url := di.GetURL() 130 // default hessian2 serialization, compatible 131 if url.GetParam(constant.SerializationKey, "") == "" { 132 url.SetParam(constant.SerializationKey, constant.Hessian2Serialization) 133 } 134 // async 135 async, err := strconv.ParseBool(inv.GetAttachmentWithDefaultValue(constant.AsyncKey, "false")) 136 if err != nil { 137 logger.Errorf("ParseBool - error: %v", err) 138 async = false 139 } 140 // response := NewResponse(inv.Reply(), nil) 141 rest := &protocol.RPCResult{} 142 timeout := di.getTimeout(inv) 143 if async { 144 if callBack, ok := inv.CallBack().(func(response common.CallbackResponse)); ok { 145 result.Err = di.client.AsyncRequest(&ivc, url, timeout, callBack, rest) 146 } else { 147 result.Err = di.client.Send(&ivc, url, timeout) 148 } 149 } else { 150 if inv.Reply() == nil { 151 result.Err = protocol.ErrNoReply 152 } else { 153 result.Err = di.client.Request(&ivc, url, timeout, rest) 154 } 155 } 156 if result.Err == nil { 157 result.Rest = inv.Reply() 158 result.Attrs = rest.Attrs 159 } 160 161 return &result 162 } 163 164 // get timeout including methodConfig 165 func (di *DubboInvoker) getTimeout(ivc *invocation.RPCInvocation) time.Duration { 166 timeout := di.timeout //default timeout 167 if attachTimeout, ok := ivc.GetAttachment(constant.TimeoutKey); ok { //check invocation timeout 168 timeout, _ = time.ParseDuration(attachTimeout) 169 } else { // check method timeout 170 methodName := ivc.MethodName() 171 if di.GetURL().GetParamBool(constant.GenericKey, false) { 172 methodName = ivc.Arguments()[0].(string) 173 } 174 mTimeout := di.GetURL().GetParam(strings.Join([]string{constant.MethodKeys, methodName, constant.TimeoutKey}, "."), "") 175 if len(mTimeout) != 0 { 176 timeout, _ = time.ParseDuration(mTimeout) 177 } 178 } 179 // set timeout into invocation 180 ivc.SetAttachment(constant.TimeoutKey, strconv.Itoa(int(timeout.Nanoseconds()))) 181 return timeout 182 } 183 184 func (di *DubboInvoker) IsAvailable() bool { 185 client := di.getClient() 186 if client != nil { 187 return client.IsAvailable() 188 } 189 190 return false 191 } 192 193 // Destroy destroy dubbo client invoker. 194 func (di *DubboInvoker) Destroy() { 195 di.quitOnce.Do(func() { 196 di.BaseInvoker.Destroy() 197 client := di.getClient() 198 if client != nil { 199 activeNumber := client.DecreaseActiveNumber() 200 di.setClient(nil) 201 if activeNumber == 0 { 202 exchangeClientMap.Delete(di.GetURL().Location) 203 client.Close() 204 } 205 } 206 }) 207 } 208 209 // Finally, I made the decision that I don't provide a general way to transfer the whole context 210 // because it could be misused. If the context contains to many key-value pairs, the performance will be much lower. 211 func (di *DubboInvoker) appendCtx(ctx context.Context, ivc *invocation.RPCInvocation) { 212 // inject opentracing ctx 213 currentSpan := opentracing.SpanFromContext(ctx) 214 if currentSpan != nil { 215 err := injectTraceCtx(currentSpan, ivc) 216 if err != nil { 217 logger.Errorf("Could not inject the span context into attachments: %v", err) 218 } 219 } 220 }