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 }