dubbo.apache.org/dubbo-go/v3@v3.1.1/config/reference_config.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 config
    19  
    20  import (
    21  	"fmt"
    22  	"net/url"
    23  	"os"
    24  	"strconv"
    25  	"time"
    26  )
    27  
    28  import (
    29  	"github.com/creasty/defaults"
    30  
    31  	"github.com/dubbogo/gost/log/logger"
    32  	gxstrings "github.com/dubbogo/gost/strings"
    33  
    34  	constant2 "github.com/dubbogo/triple/pkg/common/constant"
    35  )
    36  
    37  import (
    38  	"dubbo.apache.org/dubbo-go/v3/cluster/directory/static"
    39  	"dubbo.apache.org/dubbo-go/v3/common"
    40  	"dubbo.apache.org/dubbo-go/v3/common/constant"
    41  	"dubbo.apache.org/dubbo-go/v3/common/extension"
    42  	"dubbo.apache.org/dubbo-go/v3/config/generic"
    43  	"dubbo.apache.org/dubbo-go/v3/protocol"
    44  	"dubbo.apache.org/dubbo-go/v3/protocol/protocolwrapper"
    45  	"dubbo.apache.org/dubbo-go/v3/proxy"
    46  )
    47  
    48  // ReferenceConfig is the configuration of service consumer
    49  type ReferenceConfig struct {
    50  	pxy              *proxy.Proxy
    51  	id               string
    52  	InterfaceName    string            `yaml:"interface"  json:"interface,omitempty" property:"interface"`
    53  	Check            *bool             `yaml:"check"  json:"check,omitempty" property:"check"`
    54  	URL              string            `yaml:"url"  json:"url,omitempty" property:"url"`
    55  	Filter           string            `yaml:"filter" json:"filter,omitempty" property:"filter"`
    56  	Protocol         string            `yaml:"protocol"  json:"protocol,omitempty" property:"protocol"`
    57  	RegistryIDs      []string          `yaml:"registry-ids"  json:"registry-ids,omitempty"  property:"registry-ids"`
    58  	Cluster          string            `yaml:"cluster"  json:"cluster,omitempty" property:"cluster"`
    59  	Loadbalance      string            `yaml:"loadbalance"  json:"loadbalance,omitempty" property:"loadbalance"`
    60  	Retries          string            `yaml:"retries"  json:"retries,omitempty" property:"retries"`
    61  	Group            string            `yaml:"group"  json:"group,omitempty" property:"group"`
    62  	Version          string            `yaml:"version"  json:"version,omitempty" property:"version"`
    63  	Serialization    string            `yaml:"serialization" json:"serialization" property:"serialization"`
    64  	ProvidedBy       string            `yaml:"provided_by"  json:"provided_by,omitempty" property:"provided_by"`
    65  	Methods          []*MethodConfig   `yaml:"methods"  json:"methods,omitempty" property:"methods"`
    66  	Async            bool              `yaml:"async"  json:"async,omitempty" property:"async"`
    67  	Params           map[string]string `yaml:"params"  json:"params,omitempty" property:"params"`
    68  	invoker          protocol.Invoker
    69  	urls             []*common.URL
    70  	Generic          string `yaml:"generic"  json:"generic,omitempty" property:"generic"`
    71  	Sticky           bool   `yaml:"sticky"   json:"sticky,omitempty" property:"sticky"`
    72  	RequestTimeout   string `yaml:"timeout"  json:"timeout,omitempty" property:"timeout"`
    73  	ForceTag         bool   `yaml:"force.tag"  json:"force.tag,omitempty" property:"force.tag"`
    74  	TracingKey       string `yaml:"tracing-key" json:"tracing-key,omitempty" propertiy:"tracing-key"`
    75  	rootConfig       *RootConfig
    76  	metaDataType     string
    77  	metricsEnable    bool
    78  	MeshProviderPort int `yaml:"mesh-provider-port" json:"mesh-provider-port,omitempty" propertiy:"mesh-provider-port"`
    79  }
    80  
    81  func (rc *ReferenceConfig) Prefix() string {
    82  	return constant.ReferenceConfigPrefix + rc.InterfaceName + "."
    83  }
    84  
    85  func (rc *ReferenceConfig) Init(root *RootConfig) error {
    86  	for _, method := range rc.Methods {
    87  		if err := method.Init(); err != nil {
    88  			return err
    89  		}
    90  	}
    91  	if err := defaults.Set(rc); err != nil {
    92  		return err
    93  	}
    94  	rc.rootConfig = root
    95  	if root.Application != nil {
    96  		rc.metaDataType = root.Application.MetadataType
    97  		if rc.Group == "" {
    98  			rc.Group = root.Application.Group
    99  		}
   100  		if rc.Version == "" {
   101  			rc.Version = root.Application.Version
   102  		}
   103  	}
   104  	rc.RegistryIDs = translateIds(rc.RegistryIDs)
   105  	if root.Consumer != nil {
   106  		if rc.Filter == "" {
   107  			rc.Filter = root.Consumer.Filter
   108  		}
   109  		if len(rc.RegistryIDs) <= 0 {
   110  			rc.RegistryIDs = root.Consumer.RegistryIDs
   111  		}
   112  		if rc.Protocol == "" {
   113  			rc.Protocol = root.Consumer.Protocol
   114  		}
   115  		if rc.TracingKey == "" {
   116  			rc.TracingKey = root.Consumer.TracingKey
   117  		}
   118  		if rc.Check == nil {
   119  			rc.Check = &root.Consumer.Check
   120  		}
   121  	}
   122  	if rc.Cluster == "" {
   123  		rc.Cluster = "failover"
   124  	}
   125  	if root.Metric.Enable != nil {
   126  		rc.metricsEnable = *root.Metric.Enable
   127  	}
   128  
   129  	return verify(rc)
   130  }
   131  
   132  func getEnv(key, fallback string) string {
   133  	if value, ok := os.LookupEnv(key); ok {
   134  		return value
   135  	}
   136  	return fallback
   137  }
   138  
   139  func updateOrCreateMeshURL(rc *ReferenceConfig) {
   140  	if rc.URL != "" {
   141  		logger.Infof("URL specified explicitly %v", rc.URL)
   142  	}
   143  
   144  	if !rc.rootConfig.Consumer.MeshEnabled {
   145  		return
   146  	}
   147  	if rc.Protocol != constant2.TRIPLE {
   148  		panic(fmt.Sprintf("Mesh mode enabled, Triple protocol expected but %v protocol found!", rc.Protocol))
   149  	}
   150  	if rc.ProvidedBy == "" {
   151  		panic("Mesh mode enabled, provided-by should not be empty!")
   152  	}
   153  
   154  	podNamespace := getEnv(constant.PodNamespaceEnvKey, constant.DefaultNamespace)
   155  	clusterDomain := getEnv(constant.ClusterDomainKey, constant.DefaultClusterDomain)
   156  
   157  	var meshPort int
   158  	if rc.MeshProviderPort > 0 {
   159  		meshPort = rc.MeshProviderPort
   160  	} else {
   161  		meshPort = constant.DefaultMeshPort
   162  	}
   163  
   164  	rc.URL = "tri://" + rc.ProvidedBy + "." + podNamespace + constant.SVC + clusterDomain + ":" + strconv.Itoa(meshPort)
   165  }
   166  
   167  // Refer retrieves invokers from urls.
   168  func (rc *ReferenceConfig) Refer(srv interface{}) {
   169  	// If adaptive service is enabled,
   170  	// the cluster and load balance should be overridden to "adaptivesvc" and "p2c" respectively.
   171  	if rc.rootConfig.Consumer.AdaptiveService {
   172  		rc.Cluster = constant.ClusterKeyAdaptiveService
   173  		rc.Loadbalance = constant.LoadBalanceKeyP2C
   174  	}
   175  
   176  	// cfgURL is an interface-level invoker url, in the other words, it represents an interface.
   177  	cfgURL := common.NewURLWithOptions(
   178  		common.WithPath(rc.InterfaceName),
   179  		common.WithProtocol(rc.Protocol),
   180  		common.WithParams(rc.getURLMap()),
   181  		common.WithParamsValue(constant.BeanNameKey, rc.id),
   182  		common.WithParamsValue(constant.MetadataTypeKey, rc.metaDataType),
   183  	)
   184  
   185  	SetConsumerServiceByInterfaceName(rc.InterfaceName, srv)
   186  	if rc.ForceTag {
   187  		cfgURL.AddParam(constant.ForceUseTag, "true")
   188  	}
   189  	rc.postProcessConfig(cfgURL)
   190  
   191  	// if mesh-enabled is set
   192  	updateOrCreateMeshURL(rc)
   193  
   194  	// retrieving urls from config, and appending the urls to rc.urls
   195  	if rc.URL != "" { // use user-specific urls
   196  		/*
   197  			 Two types of URL are allowed for rc.URL:
   198  				1. direct url: server IP, that is, no need for a registry anymore
   199  				2. registry url
   200  			 They will be handled in different ways:
   201  			 For example, we have a direct url and a registry url:
   202  				1. "tri://localhost:10000" is a direct url
   203  				2. "registry://localhost:2181" is a registry url.
   204  			 Then, rc.URL looks like a string separated by semicolon: "tri://localhost:10000;registry://localhost:2181".
   205  			 The result of urlStrings is a string array: []string{"tri://localhost:10000", "registry://localhost:2181"}.
   206  		*/
   207  		urlStrings := gxstrings.RegSplit(rc.URL, "\\s*[;]+\\s*")
   208  		for _, urlStr := range urlStrings {
   209  			serviceURL, err := common.NewURL(urlStr)
   210  			if err != nil {
   211  				panic(fmt.Sprintf("url configuration error,  please check your configuration, user specified URL %v refer error, error message is %v ", urlStr, err.Error()))
   212  			}
   213  			if serviceURL.Protocol == constant.RegistryProtocol { // serviceURL in this branch is a registry protocol
   214  				serviceURL.SubURL = cfgURL
   215  				rc.urls = append(rc.urls, serviceURL)
   216  			} else { // serviceURL in this branch is the target endpoint IP address
   217  				if serviceURL.Path == "" {
   218  					serviceURL.Path = "/" + rc.InterfaceName
   219  				}
   220  				// replace params of serviceURL with params of cfgUrl
   221  				// other stuff, e.g. IP, port, etc., are same as serviceURL
   222  				newURL := common.MergeURL(serviceURL, cfgURL)
   223  				newURL.AddParam("peer", "true")
   224  				rc.urls = append(rc.urls, newURL)
   225  			}
   226  		}
   227  	} else { // use registry configs
   228  		rc.urls = loadRegistries(rc.RegistryIDs, rc.rootConfig.Registries, common.CONSUMER)
   229  		// set url to regURLs
   230  		for _, regURL := range rc.urls {
   231  			regURL.SubURL = cfgURL
   232  		}
   233  	}
   234  
   235  	// Get invokers according to rc.urls
   236  	var (
   237  		invoker protocol.Invoker
   238  		regURL  *common.URL
   239  	)
   240  	invokers := make([]protocol.Invoker, len(rc.urls))
   241  	for i, u := range rc.urls {
   242  		if u.Protocol == constant.ServiceRegistryProtocol {
   243  			invoker = extension.GetProtocol(constant.RegistryProtocol).Refer(u)
   244  		} else {
   245  			invoker = extension.GetProtocol(u.Protocol).Refer(u)
   246  		}
   247  
   248  		if rc.URL != "" {
   249  			invoker = protocolwrapper.BuildInvokerChain(invoker, constant.ReferenceFilterKey)
   250  		}
   251  
   252  		invokers[i] = invoker
   253  		if u.Protocol == constant.RegistryProtocol {
   254  			regURL = u
   255  		}
   256  	}
   257  
   258  	// TODO(hxmhlt): decouple from directory, config should not depend on directory module
   259  	if len(invokers) == 1 {
   260  		rc.invoker = invokers[0]
   261  		if rc.URL != "" {
   262  			hitClu := constant.ClusterKeyFailover
   263  			if u := rc.invoker.GetURL(); u != nil {
   264  				hitClu = u.GetParam(constant.ClusterKey, constant.ClusterKeyZoneAware)
   265  			}
   266  			cluster, err := extension.GetCluster(hitClu)
   267  			if err != nil {
   268  				panic(err)
   269  			} else {
   270  				rc.invoker = cluster.Join(static.NewDirectory(invokers))
   271  			}
   272  		}
   273  	} else {
   274  		var hitClu string
   275  		if regURL != nil {
   276  			// for multi-subscription scenario, use 'zone-aware' policy by default
   277  			hitClu = constant.ClusterKeyZoneAware
   278  		} else {
   279  			// not a registry url, must be direct invoke.
   280  			hitClu = constant.ClusterKeyFailover
   281  			if u := invokers[0].GetURL(); u != nil {
   282  				hitClu = u.GetParam(constant.ClusterKey, constant.ClusterKeyZoneAware)
   283  			}
   284  		}
   285  		cluster, err := extension.GetCluster(hitClu)
   286  		if err != nil {
   287  			panic(err)
   288  		} else {
   289  			rc.invoker = cluster.Join(static.NewDirectory(invokers))
   290  		}
   291  	}
   292  
   293  	// publish consumer's metadata
   294  	publishServiceDefinition(cfgURL)
   295  	// create proxy
   296  	if rc.Async {
   297  		callback := GetCallback(rc.id)
   298  		rc.pxy = extension.GetProxyFactory(rc.rootConfig.Consumer.ProxyFactory).GetAsyncProxy(rc.invoker, callback, cfgURL)
   299  	} else {
   300  		rc.pxy = extension.GetProxyFactory(rc.rootConfig.Consumer.ProxyFactory).GetProxy(rc.invoker, cfgURL)
   301  	}
   302  }
   303  
   304  // Implement
   305  // @v is service provider implemented RPCService
   306  func (rc *ReferenceConfig) Implement(v common.RPCService) {
   307  	rc.pxy.Implement(v)
   308  }
   309  
   310  // GetRPCService gets RPCService from proxy
   311  func (rc *ReferenceConfig) GetRPCService() common.RPCService {
   312  	return rc.pxy.Get()
   313  }
   314  
   315  // GetProxy gets proxy
   316  func (rc *ReferenceConfig) GetProxy() *proxy.Proxy {
   317  	return rc.pxy
   318  }
   319  
   320  func (rc *ReferenceConfig) getURLMap() url.Values {
   321  	urlMap := url.Values{}
   322  	// first set user params
   323  	for k, v := range rc.Params {
   324  		urlMap.Set(k, v)
   325  	}
   326  	urlMap.Set(constant.InterfaceKey, rc.InterfaceName)
   327  	urlMap.Set(constant.TimestampKey, strconv.FormatInt(time.Now().Unix(), 10))
   328  	urlMap.Set(constant.ClusterKey, rc.Cluster)
   329  	urlMap.Set(constant.LoadbalanceKey, rc.Loadbalance)
   330  	urlMap.Set(constant.RetriesKey, rc.Retries)
   331  	urlMap.Set(constant.GroupKey, rc.Group)
   332  	urlMap.Set(constant.VersionKey, rc.Version)
   333  	urlMap.Set(constant.GenericKey, rc.Generic)
   334  	urlMap.Set(constant.RegistryRoleKey, strconv.Itoa(common.CONSUMER))
   335  	urlMap.Set(constant.ProvidedBy, rc.ProvidedBy)
   336  	urlMap.Set(constant.SerializationKey, rc.Serialization)
   337  	urlMap.Set(constant.TracingConfigKey, rc.TracingKey)
   338  
   339  	urlMap.Set(constant.ReleaseKey, "dubbo-golang-"+constant.Version)
   340  	urlMap.Set(constant.SideKey, (common.RoleType(common.CONSUMER)).Role())
   341  
   342  	if len(rc.RequestTimeout) != 0 {
   343  		urlMap.Set(constant.TimeoutKey, rc.RequestTimeout)
   344  	}
   345  	// getty invoke async or sync
   346  	urlMap.Set(constant.AsyncKey, strconv.FormatBool(rc.Async))
   347  	urlMap.Set(constant.StickyKey, strconv.FormatBool(rc.Sticky))
   348  
   349  	// applicationConfig info
   350  	urlMap.Set(constant.ApplicationKey, rc.rootConfig.Application.Name)
   351  	urlMap.Set(constant.OrganizationKey, rc.rootConfig.Application.Organization)
   352  	urlMap.Set(constant.NameKey, rc.rootConfig.Application.Name)
   353  	urlMap.Set(constant.ModuleKey, rc.rootConfig.Application.Module)
   354  	urlMap.Set(constant.AppVersionKey, rc.rootConfig.Application.Version)
   355  	urlMap.Set(constant.OwnerKey, rc.rootConfig.Application.Owner)
   356  	urlMap.Set(constant.EnvironmentKey, rc.rootConfig.Application.Environment)
   357  
   358  	// filter
   359  	defaultReferenceFilter := constant.DefaultReferenceFilters
   360  	if rc.Generic != "" {
   361  		defaultReferenceFilter = constant.GenericFilterKey + "," + defaultReferenceFilter
   362  	}
   363  	if rc.metricsEnable {
   364  		defaultReferenceFilter += fmt.Sprintf(",%s", constant.MetricsFilterKey)
   365  	}
   366  	urlMap.Set(constant.ReferenceFilterKey, mergeValue(rc.Filter, "", defaultReferenceFilter))
   367  
   368  	for _, v := range rc.Methods {
   369  		urlMap.Set("methods."+v.Name+"."+constant.LoadbalanceKey, v.LoadBalance)
   370  		urlMap.Set("methods."+v.Name+"."+constant.RetriesKey, v.Retries)
   371  		urlMap.Set("methods."+v.Name+"."+constant.StickyKey, strconv.FormatBool(v.Sticky))
   372  		if len(v.RequestTimeout) != 0 {
   373  			urlMap.Set("methods."+v.Name+"."+constant.TimeoutKey, v.RequestTimeout)
   374  		}
   375  	}
   376  
   377  	return urlMap
   378  }
   379  
   380  // GenericLoad ...
   381  func (rc *ReferenceConfig) GenericLoad(id string) {
   382  	genericService := generic.NewGenericService(rc.id)
   383  	SetConsumerService(genericService)
   384  	rc.id = id
   385  	rc.Refer(genericService)
   386  	rc.Implement(genericService)
   387  }
   388  
   389  // GetInvoker get invoker from ReferenceConfig
   390  func (rc *ReferenceConfig) GetInvoker() protocol.Invoker {
   391  	return rc.invoker
   392  }
   393  
   394  // postProcessConfig asks registered ConfigPostProcessor to post-process the current ReferenceConfig.
   395  func (rc *ReferenceConfig) postProcessConfig(url *common.URL) {
   396  	for _, p := range extension.GetConfigPostProcessors() {
   397  		p.PostProcessReferenceConfig(url)
   398  	}
   399  }
   400  
   401  //////////////////////////////////// reference config api
   402  
   403  // newEmptyReferenceConfig returns empty ReferenceConfig
   404  func newEmptyReferenceConfig() *ReferenceConfig {
   405  	newReferenceConfig := &ReferenceConfig{}
   406  	newReferenceConfig.Methods = make([]*MethodConfig, 0, 8)
   407  	newReferenceConfig.Params = make(map[string]string, 8)
   408  	return newReferenceConfig
   409  }
   410  
   411  type ReferenceConfigBuilder struct {
   412  	referenceConfig *ReferenceConfig
   413  }
   414  
   415  func NewReferenceConfigBuilder() *ReferenceConfigBuilder {
   416  	return &ReferenceConfigBuilder{referenceConfig: newEmptyReferenceConfig()}
   417  }
   418  
   419  func (pcb *ReferenceConfigBuilder) SetInterface(interfaceName string) *ReferenceConfigBuilder {
   420  	pcb.referenceConfig.InterfaceName = interfaceName
   421  	return pcb
   422  }
   423  
   424  func (pcb *ReferenceConfigBuilder) SetRegistryIDs(registryIDs ...string) *ReferenceConfigBuilder {
   425  	pcb.referenceConfig.RegistryIDs = registryIDs
   426  	return pcb
   427  }
   428  
   429  func (pcb *ReferenceConfigBuilder) SetGeneric(generic bool) *ReferenceConfigBuilder {
   430  	if generic {
   431  		pcb.referenceConfig.Generic = "true"
   432  	} else {
   433  		pcb.referenceConfig.Generic = "false"
   434  	}
   435  	return pcb
   436  }
   437  
   438  func (pcb *ReferenceConfigBuilder) SetCluster(cluster string) *ReferenceConfigBuilder {
   439  	pcb.referenceConfig.Cluster = cluster
   440  	return pcb
   441  }
   442  
   443  func (pcb *ReferenceConfigBuilder) SetSerialization(serialization string) *ReferenceConfigBuilder {
   444  	pcb.referenceConfig.Serialization = serialization
   445  	return pcb
   446  }
   447  
   448  func (pcb *ReferenceConfigBuilder) SetProtocol(protocol string) *ReferenceConfigBuilder {
   449  	pcb.referenceConfig.Protocol = protocol
   450  	return pcb
   451  }
   452  
   453  func (pcb *ReferenceConfigBuilder) SetURL(url string) *ReferenceConfigBuilder {
   454  	pcb.referenceConfig.URL = url
   455  	return pcb
   456  }
   457  
   458  func (pcb *ReferenceConfigBuilder) SetFilter(filter string) *ReferenceConfigBuilder {
   459  	pcb.referenceConfig.Filter = filter
   460  	return pcb
   461  }
   462  
   463  func (pcb *ReferenceConfigBuilder) SetLoadbalance(loadbalance string) *ReferenceConfigBuilder {
   464  	pcb.referenceConfig.Loadbalance = loadbalance
   465  	return pcb
   466  }
   467  
   468  func (pcb *ReferenceConfigBuilder) SetRetries(retries string) *ReferenceConfigBuilder {
   469  	pcb.referenceConfig.Retries = retries
   470  	return pcb
   471  }
   472  
   473  func (pcb *ReferenceConfigBuilder) SetGroup(group string) *ReferenceConfigBuilder {
   474  	pcb.referenceConfig.Group = group
   475  	return pcb
   476  }
   477  
   478  func (pcb *ReferenceConfigBuilder) SetVersion(version string) *ReferenceConfigBuilder {
   479  	pcb.referenceConfig.Version = version
   480  	return pcb
   481  }
   482  
   483  func (pcb *ReferenceConfigBuilder) SetProvidedBy(providedBy string) *ReferenceConfigBuilder {
   484  	pcb.referenceConfig.ProvidedBy = providedBy
   485  	return pcb
   486  }
   487  
   488  func (pcb *ReferenceConfigBuilder) SetMethodConfig(methodConfigs []*MethodConfig) *ReferenceConfigBuilder {
   489  	pcb.referenceConfig.Methods = methodConfigs
   490  	return pcb
   491  }
   492  
   493  func (pcb *ReferenceConfigBuilder) AddMethodConfig(methodConfig *MethodConfig) *ReferenceConfigBuilder {
   494  	pcb.referenceConfig.Methods = append(pcb.referenceConfig.Methods, methodConfig)
   495  	return pcb
   496  }
   497  
   498  func (pcb *ReferenceConfigBuilder) SetAsync(async bool) *ReferenceConfigBuilder {
   499  	pcb.referenceConfig.Async = async
   500  	return pcb
   501  }
   502  
   503  func (pcb *ReferenceConfigBuilder) SetParams(params map[string]string) *ReferenceConfigBuilder {
   504  	pcb.referenceConfig.Params = params
   505  	return pcb
   506  }
   507  
   508  func (pcb *ReferenceConfigBuilder) SetSticky(sticky bool) *ReferenceConfigBuilder {
   509  	pcb.referenceConfig.Sticky = sticky
   510  	return pcb
   511  }
   512  
   513  func (pcb *ReferenceConfigBuilder) SetRequestTimeout(requestTimeout string) *ReferenceConfigBuilder {
   514  	pcb.referenceConfig.RequestTimeout = requestTimeout
   515  	return pcb
   516  }
   517  
   518  func (pcb *ReferenceConfigBuilder) SetForceTag(forceTag bool) *ReferenceConfigBuilder {
   519  	pcb.referenceConfig.ForceTag = forceTag
   520  	return pcb
   521  }
   522  
   523  func (pcb *ReferenceConfigBuilder) SetTracingKey(tracingKey string) *ReferenceConfigBuilder {
   524  	pcb.referenceConfig.TracingKey = tracingKey
   525  	return pcb
   526  }
   527  
   528  func (pcb *ReferenceConfigBuilder) Build() *ReferenceConfig {
   529  	return pcb.referenceConfig
   530  }