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(&registry.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  }