dubbo.apache.org/dubbo-go/v3@v3.1.1/metadata/report/delegate/delegate_report.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 delegate
    19  
    20  import (
    21  	"encoding/json"
    22  	"runtime/debug"
    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  	"github.com/go-co-op/gocron"
    32  
    33  	perrors "github.com/pkg/errors"
    34  
    35  	"go.uber.org/atomic"
    36  )
    37  
    38  import (
    39  	"dubbo.apache.org/dubbo-go/v3/common"
    40  	"dubbo.apache.org/dubbo-go/v3/common/constant"
    41  	"dubbo.apache.org/dubbo-go/v3/config/instance"
    42  	"dubbo.apache.org/dubbo-go/v3/metadata/definition"
    43  	"dubbo.apache.org/dubbo-go/v3/metadata/identifier"
    44  )
    45  
    46  const (
    47  	defaultMetadataReportRetryTimes  int64 = 100  // max  times to retry
    48  	defaultMetadataReportRetryPeriod int64 = 3    // cycle interval to retry, the unit is second
    49  	defaultMetadataReportCycleReport bool  = true // cycle report or not
    50  )
    51  
    52  // metadataReportRetry is a scheduler for retrying task
    53  type metadataReportRetry struct {
    54  	retryPeriod  int64
    55  	retryLimit   int64
    56  	scheduler    *gocron.Scheduler
    57  	job          *gocron.Job
    58  	retryCounter *atomic.Int64
    59  	// if no failed report, wait how many times to run retry task.
    60  	retryTimesIfNonFail int64
    61  }
    62  
    63  // newMetadataReportRetry will create a scheduler for retry task
    64  func newMetadataReportRetry(retryPeriod int64, retryLimit int64, retryFunc func() bool) (*metadataReportRetry, error) {
    65  	s1 := gocron.NewScheduler(time.UTC)
    66  
    67  	mrr := &metadataReportRetry{
    68  		retryPeriod:         retryPeriod,
    69  		retryLimit:          retryLimit,
    70  		scheduler:           s1,
    71  		retryCounter:        atomic.NewInt64(0),
    72  		retryTimesIfNonFail: 600,
    73  	}
    74  
    75  	newJob, err := mrr.scheduler.Every(int(mrr.retryPeriod)).Seconds().Do(
    76  		func() {
    77  			mrr.retryCounter.Inc()
    78  			logger.Infof("start to retry task for metadata report. retry times: %v", mrr.retryCounter.Load())
    79  			if mrr.retryCounter.Load() > mrr.retryLimit {
    80  				mrr.scheduler.Clear()
    81  			} else if retryFunc() && mrr.retryCounter.Load() > mrr.retryTimesIfNonFail {
    82  				mrr.scheduler.Clear() // may not interrupt the running job
    83  			}
    84  		})
    85  
    86  	mrr.job = newJob
    87  	return mrr, err
    88  }
    89  
    90  // startRetryTask will make scheduler with retry task run
    91  func (mrr *metadataReportRetry) startRetryTask() {
    92  	mrr.scheduler.StartAt(time.Now().Add(500 * time.Millisecond))
    93  	mrr.scheduler.StartAsync()
    94  }
    95  
    96  // MetadataReport is a absolute delegate for MetadataReport
    97  type MetadataReport struct {
    98  	reportUrl           *common.URL
    99  	syncReport          bool
   100  	metadataReportRetry *metadataReportRetry
   101  
   102  	failedReports     map[*identifier.MetadataIdentifier]interface{}
   103  	failedReportsLock sync.RWMutex
   104  
   105  	// allMetadataReports store all the metdadata reports records in memory
   106  	allMetadataReports     map[*identifier.MetadataIdentifier]interface{}
   107  	allMetadataReportsLock sync.RWMutex
   108  }
   109  
   110  // NewMetadataReport will create a MetadataReport with initiation
   111  func NewMetadataReport() (*MetadataReport, error) {
   112  	url := instance.GetMetadataReportUrl()
   113  	if url == nil {
   114  		logger.Warn("the metadataReport URL is not configured, you should configure it.")
   115  		return nil, perrors.New("the metadataReport URL is not configured, you should configure it.")
   116  	}
   117  	bmr := &MetadataReport{
   118  		reportUrl:          url,
   119  		syncReport:         url.GetParamBool(constant.SyncReportKey, false),
   120  		failedReports:      make(map[*identifier.MetadataIdentifier]interface{}, 4),
   121  		allMetadataReports: make(map[*identifier.MetadataIdentifier]interface{}, 4),
   122  	}
   123  
   124  	mrr, err := newMetadataReportRetry(
   125  		url.GetParamInt(constant.RetryPeriodKey, defaultMetadataReportRetryPeriod),
   126  		url.GetParamInt(constant.RetryTimesKey, defaultMetadataReportRetryTimes),
   127  		bmr.retry,
   128  	)
   129  	if err != nil {
   130  		return nil, err
   131  	}
   132  
   133  	bmr.metadataReportRetry = mrr
   134  	if url.GetParamBool(constant.CycleReportKey, defaultMetadataReportCycleReport) {
   135  		scheduler := gocron.NewScheduler(time.UTC)
   136  		_, err := scheduler.Every(1).Day().Do(
   137  			func() {
   138  				logger.Infof("start to publish all metadata in metadata report %s.", url.String())
   139  				bmr.allMetadataReportsLock.RLock()
   140  				bmr.doHandlerMetadataCollection(bmr.allMetadataReports)
   141  				bmr.allMetadataReportsLock.RUnlock()
   142  			})
   143  		if err != nil {
   144  			return nil, err
   145  		}
   146  		scheduler.StartAt(time.Now().Add(500 * time.Millisecond))
   147  		scheduler.StartAsync()
   148  	}
   149  	return bmr, nil
   150  }
   151  
   152  // PublishAppMetadata delegate publish metadata info
   153  func (mr *MetadataReport) PublishAppMetadata(identifier *identifier.SubscriberMetadataIdentifier, info *common.MetadataInfo) error {
   154  	report := instance.GetMetadataReportInstance()
   155  	return report.PublishAppMetadata(identifier, info)
   156  }
   157  
   158  // GetAppMetadata delegate get metadata info
   159  func (mr *MetadataReport) GetAppMetadata(identifier *identifier.SubscriberMetadataIdentifier) (*common.MetadataInfo, error) {
   160  	report := instance.GetMetadataReportInstance()
   161  	return report.GetAppMetadata(identifier)
   162  }
   163  
   164  // retry will do metadata failed reports collection by call metadata report sdk
   165  func (mr *MetadataReport) retry() bool {
   166  	mr.failedReportsLock.RLock()
   167  	defer mr.failedReportsLock.RUnlock()
   168  	return mr.doHandlerMetadataCollection(mr.failedReports)
   169  }
   170  
   171  // StoreProviderMetadata will delegate to call remote metadata's sdk to store provider service definition
   172  func (mr *MetadataReport) StoreProviderMetadata(identifier *identifier.MetadataIdentifier, definer definition.ServiceDefiner) {
   173  	if mr.syncReport {
   174  		mr.storeMetadataTask(common.PROVIDER, identifier, definer)
   175  	}
   176  	go mr.storeMetadataTask(common.PROVIDER, identifier, definer)
   177  }
   178  
   179  // storeMetadataTask will delegate to call remote metadata's sdk to store
   180  func (mr *MetadataReport) storeMetadataTask(role int, identifier *identifier.MetadataIdentifier, definer interface{}) {
   181  	logger.Infof("publish provider identifier and definition:  Identifier :%v ; definition: %v .", identifier, definer)
   182  	mr.allMetadataReportsLock.Lock()
   183  	mr.allMetadataReports[identifier] = definer
   184  	mr.allMetadataReportsLock.Unlock()
   185  
   186  	mr.failedReportsLock.Lock()
   187  	delete(mr.failedReports, identifier)
   188  	mr.failedReportsLock.Unlock()
   189  	// data is store the json marshaled definition
   190  	var (
   191  		data []byte
   192  		err  error
   193  	)
   194  
   195  	defer func() {
   196  		if r := recover(); r != nil {
   197  			mr.failedReportsLock.Lock()
   198  			mr.failedReports[identifier] = definer
   199  			mr.failedReportsLock.Unlock()
   200  			mr.metadataReportRetry.startRetryTask()
   201  			logger.Errorf("Failed to put provider metadata %v in %v, cause: %v\n%s\n",
   202  				identifier, string(data), r, string(debug.Stack()))
   203  		}
   204  	}()
   205  
   206  	data, err = json.Marshal(definer)
   207  	if err != nil {
   208  		logger.Errorf("storeProviderMetadataTask error in stage json.Marshal, msg is %+v", err)
   209  		panic(err)
   210  	}
   211  	report := instance.GetMetadataReportInstance()
   212  	if role == common.PROVIDER {
   213  		err = report.StoreProviderMetadata(identifier, string(data))
   214  	} else if role == common.CONSUMER {
   215  		err = report.StoreConsumerMetadata(identifier, string(data))
   216  	}
   217  
   218  	if err != nil {
   219  		logger.Errorf("storeProviderMetadataTask error in stage call  metadata report to StoreProviderMetadata, msg is %+v", err)
   220  	}
   221  }
   222  
   223  // StoreConsumerMetadata will delegate to call remote metadata's sdk to store consumer side service definition
   224  func (mr *MetadataReport) StoreConsumerMetadata(identifier *identifier.MetadataIdentifier, definer map[string]string) {
   225  	if mr.syncReport {
   226  		mr.storeMetadataTask(common.CONSUMER, identifier, definer)
   227  	}
   228  	go mr.storeMetadataTask(common.CONSUMER, identifier, definer)
   229  }
   230  
   231  // SaveServiceMetadata will delegate to call remote metadata's sdk to save service metadata
   232  func (mr *MetadataReport) SaveServiceMetadata(identifier *identifier.ServiceMetadataIdentifier, url *common.URL) error {
   233  	report := instance.GetMetadataReportInstance()
   234  	if mr.syncReport {
   235  		return report.SaveServiceMetadata(identifier, url)
   236  	}
   237  	go func() {
   238  		if err := report.SaveServiceMetadata(identifier, url); err != nil {
   239  			logger.Warnf("report.SaveServiceMetadata(identifier:%v, url:%v) = error:%v", identifier, url, err)
   240  		}
   241  	}()
   242  	return nil
   243  }
   244  
   245  // RemoveServiceMetadata will delegate to call remote metadata's sdk to remove service metadata
   246  func (mr *MetadataReport) RemoveServiceMetadata(identifier *identifier.ServiceMetadataIdentifier) error {
   247  	report := instance.GetMetadataReportInstance()
   248  	if mr.syncReport {
   249  		return report.RemoveServiceMetadata(identifier)
   250  	}
   251  	go func() {
   252  		if err := report.RemoveServiceMetadata(identifier); err != nil {
   253  			logger.Warnf("report.RemoveServiceMetadata(identifier:%v) = error:%v", identifier, err)
   254  		}
   255  	}()
   256  	return nil
   257  }
   258  
   259  // GetExportedURLs will delegate to call remote metadata's sdk to get exported urls
   260  func (mr *MetadataReport) GetExportedURLs(identifier *identifier.ServiceMetadataIdentifier) ([]string, error) {
   261  	report := instance.GetMetadataReportInstance()
   262  	return report.GetExportedURLs(identifier)
   263  }
   264  
   265  // SaveSubscribedData will delegate to call remote metadata's sdk to save subscribed data
   266  func (mr *MetadataReport) SaveSubscribedData(identifier *identifier.SubscriberMetadataIdentifier, urls []*common.URL) error {
   267  	urlStrList := make([]string, 0, len(urls))
   268  	for _, url := range urls {
   269  		urlStrList = append(urlStrList, url.String())
   270  	}
   271  	bytes, err := json.Marshal(urlStrList)
   272  	if err != nil {
   273  		return perrors.WithMessage(err, "Could not convert the array to json")
   274  	}
   275  
   276  	report := instance.GetMetadataReportInstance()
   277  	if mr.syncReport {
   278  		return report.SaveSubscribedData(identifier, string(bytes))
   279  	}
   280  	go func() {
   281  		if err := report.SaveSubscribedData(identifier, string(bytes)); err != nil {
   282  			logger.Warnf("report.SaveSubscribedData(identifier:%v, string(bytes):%v) = error: %v",
   283  				identifier, string(bytes), err)
   284  		}
   285  	}()
   286  	return nil
   287  }
   288  
   289  // GetSubscribedURLs will delegate to call remote metadata's sdk to get subscribed urls
   290  func (mr *MetadataReport) GetSubscribedURLs(identifier *identifier.SubscriberMetadataIdentifier) ([]string, error) {
   291  	report := instance.GetMetadataReportInstance()
   292  	return report.GetSubscribedURLs(identifier)
   293  }
   294  
   295  // GetServiceDefinition will delegate to call remote metadata's sdk to get service definitions
   296  func (mr *MetadataReport) GetServiceDefinition(identifier *identifier.MetadataIdentifier) (string, error) {
   297  	report := instance.GetMetadataReportInstance()
   298  	return report.GetServiceDefinition(identifier)
   299  }
   300  
   301  // doHandlerMetadataCollection will store metadata to metadata support with given metadataMap
   302  func (mr *MetadataReport) doHandlerMetadataCollection(metadataMap map[*identifier.MetadataIdentifier]interface{}) bool {
   303  	if len(metadataMap) == 0 {
   304  		return true
   305  	}
   306  	for e := range metadataMap {
   307  		if common.RoleType(common.PROVIDER).Role() == e.Side {
   308  			mr.StoreProviderMetadata(e, metadataMap[e].(*definition.FullServiceDefinition))
   309  		} else if common.RoleType(common.CONSUMER).Role() == e.Side {
   310  			mr.StoreConsumerMetadata(e, metadataMap[e].(map[string]string))
   311  		}
   312  	}
   313  	return false
   314  }
   315  
   316  func (mr *MetadataReport) GetConfigKeysByGroup(group string) (*gxset.HashSet, error) {
   317  	//TODO implement me
   318  	panic("implement me")
   319  }