dubbo.apache.org/dubbo-go/v3@v3.1.1/metadata/service/local/service.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 local
    19  
    20  import (
    21  	"sort"
    22  	"sync"
    23  )
    24  
    25  import (
    26  	"github.com/Workiva/go-datastructures/slice/skip"
    27  
    28  	"github.com/dubbogo/gost/log/logger"
    29  )
    30  
    31  import (
    32  	"dubbo.apache.org/dubbo-go/v3/common"
    33  	"dubbo.apache.org/dubbo-go/v3/common/constant"
    34  	"dubbo.apache.org/dubbo-go/v3/common/extension"
    35  	"dubbo.apache.org/dubbo-go/v3/config"
    36  	"dubbo.apache.org/dubbo-go/v3/metadata/definition"
    37  	"dubbo.apache.org/dubbo-go/v3/metadata/service"
    38  )
    39  
    40  func init() {
    41  	extension.SetLocalMetadataService(constant.DefaultKey, GetLocalMetadataService)
    42  }
    43  
    44  // version will be used by Version func
    45  const (
    46  	version = "1.0.0"
    47  )
    48  
    49  // MetadataService is store and query the metadata info in memory when each service registry
    50  type MetadataService struct {
    51  	service.BaseMetadataService
    52  	exportedServiceURLs   *sync.Map
    53  	subscribedServiceURLs *sync.Map
    54  	serviceDefinitions    *sync.Map
    55  	lock                  *sync.RWMutex
    56  	mOnce                 *sync.Once
    57  	metadataInfo          *common.MetadataInfo
    58  	metadataServiceURL    *common.URL
    59  }
    60  
    61  var (
    62  	metadataServiceInstance *MetadataService
    63  	metadataServiceInitOnce sync.Once
    64  )
    65  
    66  // GetLocalMetadataService which should be singleton initiates a metadata service
    67  func GetLocalMetadataService() (service.MetadataService, error) {
    68  	metadataServiceInitOnce.Do(func() {
    69  		metadataServiceInstance = &MetadataService{
    70  			BaseMetadataService:   service.NewBaseMetadataService(config.GetApplicationConfig().Name),
    71  			exportedServiceURLs:   &sync.Map{},
    72  			subscribedServiceURLs: &sync.Map{},
    73  			serviceDefinitions:    &sync.Map{},
    74  			lock:                  &sync.RWMutex{},
    75  			metadataInfo:          nil,
    76  			mOnce:                 &sync.Once{},
    77  		}
    78  	})
    79  	return metadataServiceInstance, nil
    80  }
    81  
    82  // addURL will add URL in memory
    83  func (mts *MetadataService) addURL(targetMap *sync.Map, url *common.URL) bool {
    84  	var (
    85  		urlSet interface{}
    86  		loaded bool
    87  	)
    88  	logger.Debug(url.ServiceKey())
    89  	if urlSet, loaded = targetMap.LoadOrStore(url.ServiceKey(), skip.New(uint64(0))); loaded {
    90  		mts.lock.RLock()
    91  		wantedUrl := urlSet.(*skip.SkipList).Get(url)
    92  		if len(wantedUrl) > 0 && wantedUrl[0] != nil {
    93  			mts.lock.RUnlock()
    94  			return false
    95  		}
    96  		mts.lock.RUnlock()
    97  	}
    98  	mts.lock.Lock()
    99  	// double chk
   100  	wantedUrl := urlSet.(*skip.SkipList).Get(url)
   101  	if len(wantedUrl) > 0 && wantedUrl[0] != nil {
   102  		mts.lock.Unlock()
   103  		return false
   104  	}
   105  	urlSet.(*skip.SkipList).Insert(url)
   106  	mts.lock.Unlock()
   107  	return true
   108  }
   109  
   110  // removeURL is used to remove specified url
   111  func (mts *MetadataService) removeURL(targetMap *sync.Map, url *common.URL) {
   112  	if value, loaded := targetMap.Load(url.ServiceKey()); loaded {
   113  		mts.lock.Lock()
   114  		value.(*skip.SkipList).Delete(url)
   115  		mts.lock.Unlock()
   116  		mts.lock.RLock()
   117  		defer mts.lock.RUnlock()
   118  		if value.(*skip.SkipList).Len() == 0 {
   119  			targetMap.Delete(url.ServiceKey())
   120  		}
   121  	}
   122  }
   123  
   124  // getAllService can return all the exportedUrlString except for metadataService
   125  func (mts *MetadataService) getAllService(services *sync.Map) []*common.URL {
   126  	// using skip list to dedup and sorting
   127  	var res []*common.URL
   128  	services.Range(func(key, value interface{}) bool {
   129  		urls := value.(*skip.SkipList)
   130  		for i := uint64(0); i < urls.Len(); i++ {
   131  			url := urls.ByPosition(i).(*common.URL)
   132  			if url.Service() != constant.MetadataServiceName {
   133  				res = append(res, url)
   134  			}
   135  		}
   136  		return true
   137  	})
   138  	sort.Sort(common.URLSlice(res))
   139  	return res
   140  }
   141  
   142  // getSpecifiedService can return specified service url by serviceKey
   143  func (mts *MetadataService) getSpecifiedService(services *sync.Map, serviceKey string, protocol string) []*common.URL {
   144  	var res []*common.URL
   145  	serviceList, loaded := services.Load(serviceKey)
   146  	if loaded {
   147  		urls := serviceList.(*skip.SkipList)
   148  		for i := uint64(0); i < urls.Len(); i++ {
   149  			url := urls.ByPosition(i).(*common.URL)
   150  			if len(protocol) == 0 || protocol == constant.AnyValue || url.Protocol == protocol || url.GetParam(constant.ProtocolKey, "") == protocol {
   151  				res = append(res, url)
   152  			}
   153  		}
   154  		sort.Stable(common.URLSlice(res))
   155  	}
   156  	return res
   157  }
   158  
   159  // ExportURL can store the in memory
   160  func (mts *MetadataService) ExportURL(url *common.URL) (bool, error) {
   161  	if constant.MetadataServiceName == url.GetParam(constant.InterfaceKey, "") {
   162  		mts.metadataServiceURL = url
   163  		return true, nil
   164  	}
   165  	mts.mOnce.Do(func() {
   166  		mts.metadataInfo = common.NewMetadataInfWithApp(config.GetApplicationConfig().Name)
   167  	})
   168  	mts.metadataInfo.AddService(common.NewServiceInfoWithURL(url))
   169  	return mts.addURL(mts.exportedServiceURLs, url), nil
   170  }
   171  
   172  // UnexportURL can remove the url store in memory
   173  func (mts *MetadataService) UnexportURL(url *common.URL) error {
   174  	if constant.MetadataServiceName == url.GetParam(constant.InterfaceKey, "") {
   175  		mts.metadataServiceURL = nil
   176  		return nil
   177  	}
   178  	if mts.metadataInfo != nil {
   179  		mts.metadataInfo.RemoveService(common.NewServiceInfoWithURL(url))
   180  	}
   181  	mts.removeURL(mts.exportedServiceURLs, url)
   182  	return nil
   183  }
   184  
   185  // SubscribeURL can store the in memory
   186  func (mts *MetadataService) SubscribeURL(url *common.URL) (bool, error) {
   187  	return mts.addURL(mts.subscribedServiceURLs, url), nil
   188  }
   189  
   190  // UnsubscribeURL can remove the url store in memory
   191  func (mts *MetadataService) UnsubscribeURL(url *common.URL) error {
   192  	mts.removeURL(mts.subscribedServiceURLs, url)
   193  	return nil
   194  }
   195  
   196  // PublishServiceDefinition publish url's service metadata info, and write into memory
   197  func (mts *MetadataService) PublishServiceDefinition(url *common.URL) error {
   198  	if common.RoleType(common.CONSUMER).Role() == url.GetParam(constant.SideKey, "") {
   199  		return nil
   200  	}
   201  	interfaceName := url.GetParam(constant.InterfaceKey, "")
   202  	isGeneric := url.GetParamBool(constant.GenericKey, false)
   203  	if len(interfaceName) > 0 && !isGeneric {
   204  		tmpService := common.ServiceMap.GetServiceByServiceKey(url.Protocol, url.ServiceKey())
   205  		sd := definition.BuildFullDefinition(*tmpService, url)
   206  		data, err := sd.ToBytes()
   207  		if err != nil {
   208  			logger.Errorf("publishProvider getServiceDescriptor error. providerUrl:%v , error:%v ", url, err)
   209  			return nil
   210  		}
   211  		mts.serviceDefinitions.Store(url.ServiceKey(), string(data))
   212  		return nil
   213  	}
   214  	logger.Errorf("publishProvider interfaceName is empty . providerUrl:%v ", url)
   215  	return nil
   216  }
   217  
   218  // GetExportedURLs get all exported urls
   219  func (mts *MetadataService) GetExportedURLs(serviceInterface string, group string, version string, protocol string) ([]*common.URL, error) {
   220  	if serviceInterface == constant.AnyValue {
   221  		return mts.getAllService(mts.exportedServiceURLs), nil
   222  	} else {
   223  		serviceKey := definition.ServiceDescriperBuild(serviceInterface, group, version)
   224  		return mts.getSpecifiedService(mts.exportedServiceURLs, serviceKey, protocol), nil
   225  	}
   226  }
   227  
   228  // GetSubscribedURLs get all subscribedUrl
   229  func (mts *MetadataService) GetSubscribedURLs() ([]*common.URL, error) {
   230  	return mts.getAllService(mts.subscribedServiceURLs), nil
   231  }
   232  
   233  // GetServiceDefinition can get service definition by interfaceName, group and version
   234  func (mts *MetadataService) GetServiceDefinition(interfaceName string, group string, version string) (string, error) {
   235  	serviceKey := definition.ServiceDescriperBuild(interfaceName, group, version)
   236  	v, _ := mts.serviceDefinitions.Load(serviceKey)
   237  	return v.(string), nil
   238  }
   239  
   240  // GetServiceDefinitionByServiceKey can get service definition by serviceKey
   241  func (mts *MetadataService) GetServiceDefinitionByServiceKey(serviceKey string) (string, error) {
   242  	v, _ := mts.serviceDefinitions.Load(serviceKey)
   243  	return v.(string), nil
   244  }
   245  
   246  // GetMetadataInfo can get metadata in memory
   247  func (mts *MetadataService) GetMetadataInfo(revision string) (*common.MetadataInfo, error) {
   248  	if revision == "" {
   249  		return mts.metadataInfo, nil
   250  	}
   251  	if mts.metadataInfo.CalAndGetRevision() != revision {
   252  		return nil, nil
   253  	}
   254  	return mts.metadataInfo, nil
   255  }
   256  
   257  // GetExportedServiceURLs get exported service urls
   258  func (mts *MetadataService) GetExportedServiceURLs() ([]*common.URL, error) {
   259  	return mts.getAllService(mts.exportedServiceURLs), nil
   260  }
   261  
   262  // RefreshMetadata will always return true because it will be implement by remote service
   263  func (mts *MetadataService) RefreshMetadata(string, string) (bool, error) {
   264  	return true, nil
   265  }
   266  
   267  // Version will return the version of metadata service
   268  func (mts *MetadataService) Version() (string, error) {
   269  	return version, nil
   270  }
   271  
   272  // GetMetadataServiceURL get url of MetadataService
   273  func (mts *MetadataService) GetMetadataServiceURL() (*common.URL, error) {
   274  	return mts.metadataServiceURL, nil
   275  }
   276  
   277  // SetMetadataServiceURL save url of MetadataService
   278  func (mts *MetadataService) SetMetadataServiceURL(url *common.URL) error {
   279  	mts.metadataServiceURL = url
   280  	return nil
   281  }