dubbo.apache.org/dubbo-go/v3@v3.1.1/registry/servicediscovery/service_discovery_registry.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 servicediscovery 19 20 import ( 21 "bytes" 22 "strings" 23 "sync" 24 "time" 25 ) 26 27 import ( 28 gxset "github.com/dubbogo/gost/container/set" 29 "github.com/dubbogo/gost/log/logger" 30 31 perrors "github.com/pkg/errors" 32 33 "go.uber.org/atomic" 34 ) 35 36 import ( 37 "dubbo.apache.org/dubbo-go/v3/common" 38 "dubbo.apache.org/dubbo-go/v3/common/constant" 39 "dubbo.apache.org/dubbo-go/v3/common/extension" 40 "dubbo.apache.org/dubbo-go/v3/metadata/mapping" 41 "dubbo.apache.org/dubbo-go/v3/metadata/service" 42 "dubbo.apache.org/dubbo-go/v3/metadata/service/local" 43 "dubbo.apache.org/dubbo-go/v3/metrics" 44 metricMetadata "dubbo.apache.org/dubbo-go/v3/metrics/metadata" 45 metricsRegistry "dubbo.apache.org/dubbo-go/v3/metrics/registry" 46 "dubbo.apache.org/dubbo-go/v3/registry" 47 _ "dubbo.apache.org/dubbo-go/v3/registry/event" 48 "dubbo.apache.org/dubbo-go/v3/registry/servicediscovery/synthesizer" 49 ) 50 51 func init() { 52 extension.SetRegistry(constant.ServiceRegistryProtocol, newServiceDiscoveryRegistry) 53 } 54 55 // ServiceDiscoveryRegistry is the implementation of application-level registry. 56 // It's completely different from other registry implementations 57 // This implementation is based on ServiceDiscovery abstraction and ServiceNameMapping 58 // In order to keep compatible with interface-level registry, 59 // this implementation is 60 type ServiceDiscoveryRegistry struct { 61 lock sync.RWMutex 62 url *common.URL 63 serviceDiscovery registry.ServiceDiscovery 64 subscribedServices *gxset.HashSet 65 serviceNameMapping mapping.ServiceNameMapping 66 metaDataService service.MetadataService 67 registeredListeners *gxset.HashSet 68 subscribedURLsSynthesizers []synthesizer.SubscribedURLsSynthesizer 69 serviceRevisionExportedURLsCache map[string]map[string][]*common.URL 70 serviceListeners map[string]registry.ServiceInstancesChangedListener 71 serviceMappingListeners map[string]registry.MappingListener 72 } 73 74 func newServiceDiscoveryRegistry(url *common.URL) (registry.Registry, error) { 75 serviceDiscovery, err := creatServiceDiscovery(url) 76 if err != nil { 77 return nil, err 78 } 79 subscribedServices := parseServices(url.GetParam(constant.SubscribedServiceNamesKey, "")) 80 subscribedURLsSynthesizers := synthesizer.GetAllSynthesizer() 81 serviceNameMapping := extension.GetGlobalServiceNameMapping() 82 metaDataService, err := local.GetLocalMetadataService() 83 if err != nil { 84 return nil, perrors.WithMessage(err, "could not init metadata service") 85 } 86 return &ServiceDiscoveryRegistry{ 87 url: url, 88 serviceDiscovery: serviceDiscovery, 89 subscribedServices: subscribedServices, 90 subscribedURLsSynthesizers: subscribedURLsSynthesizers, 91 registeredListeners: gxset.NewSet(), 92 serviceRevisionExportedURLsCache: make(map[string]map[string][]*common.URL, 8), 93 serviceNameMapping: serviceNameMapping, 94 metaDataService: metaDataService, 95 serviceListeners: make(map[string]registry.ServiceInstancesChangedListener), 96 // cache for mapping listener 97 serviceMappingListeners: make(map[string]registry.MappingListener), 98 }, nil 99 } 100 101 func (s *ServiceDiscoveryRegistry) UnRegister(url *common.URL) error { 102 if !shouldRegister(url) { 103 return nil 104 } 105 return s.metaDataService.UnexportURL(url) 106 } 107 108 func (s *ServiceDiscoveryRegistry) UnSubscribe(url *common.URL, listener registry.NotifyListener) error { 109 if !shouldSubscribe(url) { 110 return nil 111 } 112 err := s.metaDataService.UnsubscribeURL(url) 113 if err != nil { 114 return err 115 } 116 services := s.getServices(url, nil) 117 if services == nil { 118 return nil 119 } 120 // FIXME ServiceNames.String() is not good 121 serviceNamesKey := services.String() 122 l := s.serviceListeners[serviceNamesKey] 123 l.RemoveListener(url.ServiceKey()) 124 s.stopListen(url) 125 err = s.serviceNameMapping.Remove(url) 126 if err != nil { 127 return err 128 } 129 return nil 130 } 131 132 func creatServiceDiscovery(url *common.URL) (registry.ServiceDiscovery, error) { 133 originServiceDiscovery, err := extension.GetServiceDiscovery(url) 134 if err != nil { 135 return nil, perrors.WithMessage(err, "Create service discovery fialed") 136 } 137 return originServiceDiscovery, nil 138 } 139 140 func parseServices(literalServices string) *gxset.HashSet { 141 set := gxset.NewSet() 142 if len(literalServices) == 0 { 143 return set 144 } 145 var splitServices = strings.Split(literalServices, ",") 146 for _, s := range splitServices { 147 if len(s) != 0 { 148 set.Add(s) 149 } 150 } 151 return set 152 } 153 154 func (s *ServiceDiscoveryRegistry) GetServiceDiscovery() registry.ServiceDiscovery { 155 return s.serviceDiscovery 156 } 157 158 func (s *ServiceDiscoveryRegistry) GetURL() *common.URL { 159 return s.url 160 } 161 162 func (s *ServiceDiscoveryRegistry) IsAvailable() bool { 163 if s.serviceDiscovery.GetServices() == nil { 164 return false 165 } 166 return len(s.serviceDiscovery.GetServices().Values()) > 0 167 } 168 169 func (s *ServiceDiscoveryRegistry) Destroy() { 170 err := s.serviceDiscovery.Destroy() 171 if err != nil { 172 logger.Errorf("destroy serviceDiscovery catch error:%s", err.Error()) 173 } 174 } 175 176 func (s *ServiceDiscoveryRegistry) Register(url *common.URL) error { 177 if !shouldRegister(url) { 178 return nil 179 } 180 common.HandleRegisterIPAndPort(url) 181 182 start := time.Now() 183 ok, err := s.metaDataService.ExportURL(url) 184 metrics.Publish(metricsRegistry.NewServerRegisterEvent(ok && err == nil, start)) 185 186 if err != nil { 187 logger.Errorf("The URL[%s] registry catch error:%s!", url.String(), err.Error()) 188 return err 189 } 190 if !ok { 191 logger.Warnf("The URL[%s] has been registry!", url.String()) 192 } 193 194 return s.serviceNameMapping.Map(url) 195 } 196 197 func shouldRegister(url *common.URL) bool { 198 side := url.GetParam(constant.SideKey, "") 199 if side == constant.ProviderProtocol { 200 return true 201 } 202 logger.Debugf("The URL should not be register.", url.String()) 203 return false 204 } 205 206 func (s *ServiceDiscoveryRegistry) Subscribe(url *common.URL, notify registry.NotifyListener) error { 207 if !shouldSubscribe(url) { 208 logger.Infof("Service %s is set to not subscribe to instances.", url.ServiceKey()) 209 return nil 210 } 211 _, err := s.metaDataService.SubscribeURL(url) 212 if err != nil { 213 return perrors.WithMessage(err, "subscribe url error: "+url.String()) 214 } 215 216 mappingListener := NewMappingListener(s.url, url, s.subscribedServices, notify) 217 services := s.getServices(url, mappingListener) 218 if services.Empty() { 219 return perrors.Errorf("Should has at least one way to know which services this interface belongs to,"+ 220 " either specify 'provided-by' for reference or enable metadata-report center subscription url:%s", url.String()) 221 } 222 logger.Infof("Find initial mapping applications %q for service %s.", services, url.ServiceKey()) 223 // first notify 224 mappingListener.OnEvent(registry.NewServiceMappingChangedEvent(url.ServiceKey(), services)) 225 return nil 226 } 227 228 func (s *ServiceDiscoveryRegistry) SubscribeURL(url *common.URL, notify registry.NotifyListener, services *gxset.HashSet) { 229 // FIXME ServiceNames.String() is not good 230 var err error 231 serviceNamesKey := services.String() 232 protocol := "tri" // consume "tri" protocol by default, other protocols need to be specified on reference/consumer explicitly 233 if url.Protocol != "" { 234 protocol = url.Protocol 235 } 236 protocolServiceKey := url.ServiceKey() + ":" + protocol 237 listener := s.serviceListeners[serviceNamesKey] 238 if listener == nil { 239 listener = NewServiceInstancesChangedListener(url.GetParam(constant.ApplicationKey, ""), services) 240 for _, serviceNameTmp := range services.Values() { 241 serviceName := serviceNameTmp.(string) 242 instances := s.serviceDiscovery.GetInstances(serviceName) 243 logger.Infof("Synchronized instance notification on application %s subscription, instance list size %s", serviceName, len(instances)) 244 err = listener.OnEvent(®istry.ServiceInstancesChangedEvent{ 245 ServiceName: serviceName, 246 Instances: instances, 247 }) 248 if err != nil { 249 logger.Warnf("[ServiceDiscoveryRegistry] ServiceInstancesChangedListenerImpl handle error:%v", err) 250 } 251 } 252 } 253 s.serviceListeners[serviceNamesKey] = listener 254 listener.AddListenerAndNotify(protocolServiceKey, notify) 255 event := metricMetadata.NewMetadataMetricTimeEvent(metricMetadata.SubscribeServiceRt) 256 257 logger.Infof("Start subscribing to registry for applications :%s with a new go routine.", serviceNamesKey) 258 go func() { 259 err = s.serviceDiscovery.AddListener(listener) 260 event.Succ = err != nil 261 event.End = time.Now() 262 event.Attachment[constant.InterfaceKey] = url.Interface() 263 metrics.Publish(event) 264 metrics.Publish(metricsRegistry.NewServerSubscribeEvent(err == nil)) 265 if err != nil { 266 logger.Errorf("add instance listener catch error,url:%s err:%s", url.String(), err.Error()) 267 } 268 }() 269 } 270 271 // LoadSubscribeInstances load subscribe instance 272 func (s *ServiceDiscoveryRegistry) LoadSubscribeInstances(url *common.URL, notify registry.NotifyListener) error { 273 return nil 274 } 275 276 func getUrlKey(url *common.URL) string { 277 var bf bytes.Buffer 278 if len(url.Protocol) != 0 { 279 bf.WriteString(url.Protocol) 280 bf.WriteString("://") 281 } 282 if len(url.Location) != 0 { 283 bf.WriteString(url.Location) 284 bf.WriteString(":") 285 bf.WriteString(url.Port) 286 } 287 if len(url.Path) != 0 { 288 bf.WriteString("/") 289 bf.WriteString(url.Path) 290 } 291 bf.WriteString("?") 292 appendParam(bf, constant.VersionKey, url) 293 appendParam(bf, constant.GroupKey, url) 294 appendParam(bf, constant.NacosProtocolKey, url) 295 return bf.String() 296 } 297 298 func appendParam(buffer bytes.Buffer, paramKey string, url *common.URL) { 299 buffer.WriteString(paramKey) 300 buffer.WriteString("=") 301 buffer.WriteString(url.GetParam(paramKey, "")) 302 } 303 304 func (s *ServiceDiscoveryRegistry) synthesizeSubscribedURLs(subscribedURL *common.URL, serviceInstances []registry.ServiceInstance) []*common.URL { 305 var urls []*common.URL 306 for _, syn := range s.subscribedURLsSynthesizers { 307 if syn.Support(subscribedURL) { 308 urls = append(urls, syn.Synthesize(subscribedURL, serviceInstances)...) 309 } 310 } 311 return urls 312 } 313 314 func shouldSubscribe(url *common.URL) bool { 315 return !shouldRegister(url) 316 } 317 318 func (s *ServiceDiscoveryRegistry) getServices(url *common.URL, listener registry.MappingListener) *gxset.HashSet { 319 services := gxset.NewSet() 320 serviceNames := url.GetParam(constant.ProvidedBy, "") 321 if len(serviceNames) > 0 { 322 services = parseServices(serviceNames) 323 } 324 if services.Empty() { 325 services = s.findMappedServices(url, listener) 326 if services.Empty() { 327 return s.subscribedServices 328 } 329 } 330 return services 331 } 332 333 func (s *ServiceDiscoveryRegistry) findMappedServices(url *common.URL, listener registry.MappingListener) *gxset.HashSet { 334 serviceNames, err := s.serviceNameMapping.Get(url, listener) 335 if err != nil { 336 logger.Errorf("get service names catch error, url:%s, err:%s ", url.String(), err.Error()) 337 return gxset.NewSet() 338 } 339 if listener != nil { 340 protocolServiceKey := url.ServiceKey() + ":" + url.Protocol 341 s.serviceMappingListeners[protocolServiceKey] = listener 342 } 343 return serviceNames 344 } 345 346 var ( 347 exporting = &atomic.Bool{} 348 ) 349 350 func (s *ServiceDiscoveryRegistry) stopListen(url *common.URL) { 351 protocolServiceKey := url.ServiceKey() + ":" + url.Protocol 352 listener := s.serviceMappingListeners[protocolServiceKey] 353 if listener != nil { 354 delete(s.serviceMappingListeners, protocolServiceKey) 355 listener.Stop() 356 } 357 }