dubbo.apache.org/dubbo-go/v3@v3.1.1/protocol/dubbo/dubbo_protocol.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 "fmt" 23 "sync" 24 "time" 25 ) 26 27 import ( 28 "github.com/dubbogo/gost/log/logger" 29 30 "github.com/opentracing/opentracing-go" 31 ) 32 33 import ( 34 "dubbo.apache.org/dubbo-go/v3/common" 35 "dubbo.apache.org/dubbo-go/v3/common/constant" 36 "dubbo.apache.org/dubbo-go/v3/common/extension" 37 "dubbo.apache.org/dubbo-go/v3/protocol" 38 "dubbo.apache.org/dubbo-go/v3/protocol/invocation" 39 "dubbo.apache.org/dubbo-go/v3/remoting" 40 "dubbo.apache.org/dubbo-go/v3/remoting/getty" 41 ) 42 43 const ( 44 // DUBBO is dubbo protocol name 45 DUBBO = "dubbo" 46 ) 47 48 var ( 49 // Make the connection can be shared. 50 // It will create one connection for one address (ip+port) 51 exchangeClientMap = new(sync.Map) 52 exchangeLock = new(sync.Map) 53 ) 54 55 func init() { 56 extension.SetProtocol(DUBBO, GetProtocol) 57 } 58 59 var dubboProtocol *DubboProtocol 60 61 // DubboProtocol supports dubbo protocol. It implements Protocol interface for dubbo protocol. 62 type DubboProtocol struct { 63 protocol.BaseProtocol 64 // It is store relationship about serviceKey(group/interface:version) and ExchangeServer 65 // The ExchangeServer is introduced to replace of Server. Because Server is depend on getty directly. 66 serverMap map[string]*remoting.ExchangeServer 67 serverLock sync.Mutex 68 } 69 70 // NewDubboProtocol create a dubbo protocol. 71 func NewDubboProtocol() *DubboProtocol { 72 return &DubboProtocol{ 73 BaseProtocol: protocol.NewBaseProtocol(), 74 serverMap: make(map[string]*remoting.ExchangeServer), 75 } 76 } 77 78 // Export export dubbo service. 79 func (dp *DubboProtocol) Export(invoker protocol.Invoker) protocol.Exporter { 80 url := invoker.GetURL() 81 serviceKey := url.ServiceKey() 82 exporter := NewDubboExporter(serviceKey, invoker, dp.ExporterMap()) 83 dp.SetExporterMap(serviceKey, exporter) 84 logger.Infof("[DUBBO Protocol] Export service: %s", url.String()) 85 // start server 86 dp.openServer(url) 87 return exporter 88 } 89 90 // Refer create dubbo service reference. 91 func (dp *DubboProtocol) Refer(url *common.URL) protocol.Invoker { 92 exchangeClient := getExchangeClient(url) 93 if exchangeClient == nil { 94 logger.Warnf("can't dial the server: %+v", url.Location) 95 return nil 96 } 97 invoker := NewDubboInvoker(url, exchangeClient) 98 dp.SetInvokers(invoker) 99 logger.Infof("[DUBBO Protocol] Refer service: %s", url.String()) 100 return invoker 101 } 102 103 // Destroy destroy dubbo service. 104 func (dp *DubboProtocol) Destroy() { 105 logger.Infof("DubboProtocol destroy.") 106 107 dp.BaseProtocol.Destroy() 108 109 // stop server 110 for key, server := range dp.serverMap { 111 delete(dp.serverMap, key) 112 server.Stop() 113 } 114 } 115 116 func (dp *DubboProtocol) openServer(url *common.URL) { 117 _, ok := dp.serverMap[url.Location] 118 if !ok { 119 _, ok := dp.ExporterMap().Load(url.ServiceKey()) 120 if !ok { 121 panic("[DubboProtocol]" + url.Key() + "is not existing") 122 } 123 124 dp.serverLock.Lock() 125 _, ok = dp.serverMap[url.Location] 126 if !ok { 127 handler := func(invocation *invocation.RPCInvocation) protocol.RPCResult { 128 return doHandleRequest(invocation) 129 } 130 srv := remoting.NewExchangeServer(url, getty.NewServer(url, handler)) 131 dp.serverMap[url.Location] = srv 132 srv.Start() 133 } 134 dp.serverLock.Unlock() 135 } 136 } 137 138 // GetProtocol get a single dubbo protocol. 139 func GetProtocol() protocol.Protocol { 140 if dubboProtocol == nil { 141 dubboProtocol = NewDubboProtocol() 142 } 143 return dubboProtocol 144 } 145 146 func doHandleRequest(rpcInvocation *invocation.RPCInvocation) protocol.RPCResult { 147 exporter, _ := dubboProtocol.ExporterMap().Load(rpcInvocation.ServiceKey()) 148 result := protocol.RPCResult{} 149 if exporter == nil { 150 err := fmt.Errorf("don't have this exporter, key: %s", rpcInvocation.ServiceKey()) 151 logger.Errorf(err.Error()) 152 result.Err = err 153 // reply(session, p, hessian.PackageResponse) 154 return result 155 } 156 invoker := exporter.(protocol.Exporter).GetInvoker() 157 if invoker != nil { 158 // FIXME 159 ctx := rebuildCtx(rpcInvocation) 160 161 invokeResult := invoker.Invoke(ctx, rpcInvocation) 162 if err := invokeResult.Error(); err != nil { 163 result.Err = invokeResult.Error() 164 // p.Header.ResponseStatus = hessian.Response_OK 165 // p.Body = hessian.NewResponse(nil, err, result.Attachments()) 166 } else { 167 result.Rest = invokeResult.Result() 168 // p.Header.ResponseStatus = hessian.Response_OK 169 // p.Body = hessian.NewResponse(res, nil, result.Attachments()) 170 } 171 result.Attrs = invokeResult.Attachments() 172 } else { 173 result.Err = fmt.Errorf("don't have the invoker, key: %s", rpcInvocation.ServiceKey()) 174 } 175 return result 176 } 177 178 func getExchangeClient(url *common.URL) *remoting.ExchangeClient { 179 clientTmp, ok := exchangeClientMap.Load(url.Location) 180 if !ok { 181 var exchangeClientTmp *remoting.ExchangeClient 182 func() { 183 // lock for NewExchangeClient and store into map. 184 _, loaded := exchangeLock.LoadOrStore(url.Location, 0x00) 185 // unlock 186 defer exchangeLock.Delete(url.Location) 187 if loaded { 188 // retry for 5 times. 189 for i := 0; i < 5; i++ { 190 if clientTmp, ok = exchangeClientMap.Load(url.Location); ok { 191 break 192 } else { 193 // if cannot get, sleep a while. 194 time.Sleep(time.Duration(i*100) * time.Millisecond) 195 } 196 } 197 return 198 } 199 200 // todo set by config 201 exchangeClientTmp = remoting.NewExchangeClient(url, getty.NewClient(getty.Options{ 202 ConnectTimeout: 3 * time.Second, 203 RequestTimeout: 3 * time.Second, 204 }), 3*time.Second, false) 205 // input store 206 if exchangeClientTmp != nil { 207 exchangeClientMap.Store(url.Location, exchangeClientTmp) 208 } 209 }() 210 if exchangeClientTmp != nil { 211 return exchangeClientTmp 212 } 213 } 214 // cannot dial the server 215 if clientTmp == nil { 216 return nil 217 } 218 exchangeClient := clientTmp.(*remoting.ExchangeClient) 219 exchangeClient.IncreaseActiveNumber() 220 return exchangeClient 221 } 222 223 // rebuildCtx rebuild the context by attachment. 224 // Once we decided to transfer more context's key-value, we should change this. 225 // now we only support rebuild the tracing context 226 func rebuildCtx(inv *invocation.RPCInvocation) context.Context { 227 ctx := context.WithValue(context.Background(), constant.DubboCtxKey("attachment"), inv.Attachments()) 228 229 // actually, if user do not use any opentracing framework, the err will not be nil. 230 spanCtx, err := opentracing.GlobalTracer().Extract(opentracing.TextMap, 231 opentracing.TextMapCarrier(filterContext(inv.Attachments()))) 232 if err == nil { 233 ctx = context.WithValue(ctx, constant.DubboCtxKey(constant.TracingRemoteSpanCtx), spanCtx) 234 } 235 return ctx 236 }