dubbo.apache.org/dubbo-go/v3@v3.1.1/protocol/dubbo3/dubbo3_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 dubbo3 19 20 import ( 21 "context" 22 "fmt" 23 "reflect" 24 "sync" 25 ) 26 27 import ( 28 "github.com/dubbogo/gost/log/logger" 29 30 "github.com/dubbogo/grpc-go" 31 "github.com/dubbogo/grpc-go/metadata" 32 33 tripleConstant "github.com/dubbogo/triple/pkg/common/constant" 34 triConfig "github.com/dubbogo/triple/pkg/config" 35 "github.com/dubbogo/triple/pkg/triple" 36 37 "github.com/dustin/go-humanize" 38 ) 39 40 import ( 41 "dubbo.apache.org/dubbo-go/v3/common" 42 "dubbo.apache.org/dubbo-go/v3/common/constant" 43 "dubbo.apache.org/dubbo-go/v3/common/extension" 44 "dubbo.apache.org/dubbo-go/v3/config" 45 "dubbo.apache.org/dubbo-go/v3/protocol" 46 "dubbo.apache.org/dubbo-go/v3/protocol/invocation" 47 ) 48 49 var protocolOnce sync.Once 50 51 func init() { 52 extension.SetProtocol(tripleConstant.TRIPLE, GetProtocol) 53 protocolOnce = sync.Once{} 54 } 55 56 var ( 57 dubboProtocol *DubboProtocol 58 ) 59 60 // DubboProtocol supports dubbo 3.0 protocol. It implements Protocol interface for dubbo protocol. 61 type DubboProtocol struct { 62 protocol.BaseProtocol 63 serverLock sync.Mutex 64 serviceMap *sync.Map // serviceMap is used to export multiple service by one server 65 serverMap map[string]*triple.TripleServer // serverMap stores all exported server 66 } 67 68 // NewDubboProtocol create a dubbo protocol. 69 func NewDubboProtocol() *DubboProtocol { 70 return &DubboProtocol{ 71 BaseProtocol: protocol.NewBaseProtocol(), 72 serverMap: make(map[string]*triple.TripleServer), 73 serviceMap: &sync.Map{}, 74 } 75 } 76 77 // Export export dubbo3 service. 78 func (dp *DubboProtocol) Export(invoker protocol.Invoker) protocol.Exporter { 79 url := invoker.GetURL() 80 serviceKey := url.ServiceKey() 81 exporter := NewDubboExporter(serviceKey, invoker, dp.ExporterMap(), dp.serviceMap) 82 dp.SetExporterMap(serviceKey, exporter) 83 logger.Infof("[Triple Protocol] Export service: %s", url.String()) 84 85 key := url.GetParam(constant.BeanNameKey, "") 86 var service interface{} 87 service = config.GetProviderService(key) 88 89 serializationType := url.GetParam(constant.SerializationKey, constant.ProtobufSerialization) 90 var triSerializationType tripleConstant.CodecType 91 92 if serializationType == constant.ProtobufSerialization { 93 m, ok := reflect.TypeOf(service).MethodByName("XXX_SetProxyImpl") 94 if !ok { 95 logger.Errorf("PB service with key = %s is not support XXX_SetProxyImpl to pb."+ 96 "Please run go install github.com/dubbogo/dubbogo-cli/cmd/protoc-gen-go-triple@latest to update your "+ 97 "protoc-gen-go-triple and re-generate your pb file again.", key) 98 return nil 99 } 100 if invoker == nil { 101 panic(fmt.Sprintf("no invoker found for servicekey: %v", url.ServiceKey())) 102 } 103 in := []reflect.Value{reflect.ValueOf(service)} 104 in = append(in, reflect.ValueOf(invoker)) 105 m.Func.Call(in) 106 triSerializationType = tripleConstant.PBCodecName 107 } else { 108 valueOf := reflect.ValueOf(service) 109 typeOf := valueOf.Type() 110 numField := valueOf.NumMethod() 111 tripleService := &UnaryService{proxyImpl: invoker} 112 for i := 0; i < numField; i++ { 113 ft := typeOf.Method(i) 114 if ft.Name == "Reference" { 115 continue 116 } 117 // get all method params type 118 typs := make([]reflect.Type, 0) 119 for j := 2; j < ft.Type.NumIn(); j++ { 120 typs = append(typs, ft.Type.In(j)) 121 } 122 tripleService.setReqParamsTypes(ft.Name, typs) 123 } 124 service = tripleService 125 triSerializationType = tripleConstant.CodecType(serializationType) 126 } 127 128 dp.serviceMap.Store(url.GetParam(constant.InterfaceKey, ""), service) 129 130 // try start server 131 dp.openServer(url, triSerializationType) 132 return exporter 133 } 134 135 // Refer create dubbo3 service reference. 136 func (dp *DubboProtocol) Refer(url *common.URL) protocol.Invoker { 137 invoker, err := NewDubboInvoker(url) 138 if err != nil { 139 logger.Errorf("Refer url = %+v, with error = %s", url, err.Error()) 140 return nil 141 } 142 dp.SetInvokers(invoker) 143 logger.Infof("[Triple Protocol] Refer service: %s", url.String()) 144 return invoker 145 } 146 147 // Destroy destroy dubbo3 service. 148 func (dp *DubboProtocol) Destroy() { 149 dp.BaseProtocol.Destroy() 150 keyList := make([]string, 16) 151 152 dp.serverLock.Lock() 153 defer dp.serverLock.Unlock() 154 // Stop all server 155 for k, _ := range dp.serverMap { 156 keyList = append(keyList, k) 157 } 158 for _, v := range keyList { 159 if server := dp.serverMap[v]; server != nil { 160 server.Stop() 161 } 162 delete(dp.serverMap, v) 163 } 164 } 165 166 // Dubbo3GrpcService is gRPC service 167 type Dubbo3GrpcService interface { 168 // SetProxyImpl sets proxy. 169 XXX_SetProxyImpl(impl protocol.Invoker) 170 // GetProxyImpl gets proxy. 171 XXX_GetProxyImpl() protocol.Invoker 172 // ServiceDesc gets an RPC service's specification. 173 XXX_ServiceDesc() *grpc.ServiceDesc 174 } 175 176 type UnaryService struct { 177 proxyImpl protocol.Invoker 178 reqTypeMap sync.Map 179 } 180 181 func (d *UnaryService) setReqParamsTypes(methodName string, typ []reflect.Type) { 182 d.reqTypeMap.Store(methodName, typ) 183 } 184 185 func (d *UnaryService) GetReqParamsInterfaces(methodName string) ([]interface{}, bool) { 186 val, ok := d.reqTypeMap.Load(methodName) 187 if !ok { 188 return nil, false 189 } 190 typs := val.([]reflect.Type) 191 reqParamsInterfaces := make([]interface{}, 0, len(typs)) 192 for _, typ := range typs { 193 reqParamsInterfaces = append(reqParamsInterfaces, reflect.New(typ).Interface()) 194 } 195 return reqParamsInterfaces, true 196 } 197 198 func (d *UnaryService) InvokeWithArgs(ctx context.Context, methodName string, arguments []interface{}) (interface{}, error) { 199 dubboAttachment := make(map[string]interface{}) 200 md, ok := metadata.FromIncomingContext(ctx) 201 if ok { 202 for k := range md { 203 dubboAttachment[k] = md.Get(k)[0] 204 } 205 } 206 res := d.proxyImpl.Invoke(ctx, invocation.NewRPCInvocation(methodName, arguments, dubboAttachment)) 207 return res, res.Error() 208 } 209 210 // openServer open a dubbo3 server, if there is already a service using the same protocol, it returns directly. 211 func (dp *DubboProtocol) openServer(url *common.URL, tripleCodecType tripleConstant.CodecType) { 212 dp.serverLock.Lock() 213 defer dp.serverLock.Unlock() 214 _, ok := dp.serverMap[url.Location] 215 216 if ok { 217 dp.serverMap[url.Location].RefreshService() 218 return 219 } 220 221 opts := []triConfig.OptionFunction{ 222 triConfig.WithCodecType(tripleCodecType), 223 triConfig.WithLocation(url.Location), 224 triConfig.WithLogger(logger.GetLogger()), 225 } 226 tracingKey := url.GetParam(constant.TracingConfigKey, "") 227 if tracingKey != "" { 228 tracingConfig := config.GetTracingConfig(tracingKey) 229 if tracingConfig != nil { 230 if tracingConfig.ServiceName == "" { 231 tracingConfig.ServiceName = config.GetApplicationConfig().Name 232 } 233 switch tracingConfig.Name { 234 case "jaeger": 235 opts = append(opts, triConfig.WithJaegerConfig( 236 tracingConfig.Address, 237 tracingConfig.ServiceName, 238 *tracingConfig.UseAgent, 239 )) 240 default: 241 logger.Warnf("unsupported tracing name %s, now triple only support jaeger", tracingConfig.Name) 242 } 243 } 244 } 245 246 maxServerRecvMsgSize := constant.DefaultMaxServerRecvMsgSize 247 if recvMsgSize, err := humanize.ParseBytes(url.GetParam(constant.MaxServerRecvMsgSize, "")); err == nil && recvMsgSize != 0 { 248 maxServerRecvMsgSize = int(recvMsgSize) 249 } 250 maxServerSendMsgSize := constant.DefaultMaxServerSendMsgSize 251 if sendMsgSize, err := humanize.ParseBytes(url.GetParam(constant.MaxServerSendMsgSize, "")); err == nil && sendMsgSize != 0 { 252 maxServerSendMsgSize = int(sendMsgSize) 253 } 254 opts = append(opts, triConfig.WithGRPCMaxServerRecvMessageSize(maxServerRecvMsgSize)) 255 opts = append(opts, triConfig.WithGRPCMaxServerSendMessageSize(maxServerSendMsgSize)) 256 257 triOption := triConfig.NewTripleOption(opts...) 258 259 tlsConfig := config.GetRootConfig().TLSConfig 260 if tlsConfig != nil { 261 triOption.TLSCertFile = tlsConfig.TLSCertFile 262 triOption.TLSKeyFile = tlsConfig.TLSKeyFile 263 triOption.CACertFile = tlsConfig.CACertFile 264 triOption.TLSServerName = tlsConfig.TLSServerName 265 logger.Infof("Triple Server initialized the TLSConfig configuration") 266 } 267 268 _, ok = dp.ExporterMap().Load(url.ServiceKey()) 269 if !ok { 270 panic("[DubboProtocol]" + url.Key() + "is not existing") 271 } 272 273 srv := triple.NewTripleServer(dp.serviceMap, triOption) 274 dp.serverMap[url.Location] = srv 275 srv.Start() 276 } 277 278 // GetProtocol get a single dubbo3 protocol. 279 func GetProtocol() protocol.Protocol { 280 protocolOnce.Do(func() { 281 dubboProtocol = NewDubboProtocol() 282 }) 283 return dubboProtocol 284 }