| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package config |
| |
| import ( |
| "net/url" |
| "strconv" |
| "strings" |
| ) |
| |
| import ( |
| "github.com/creasty/defaults" |
| |
| "github.com/dubbogo/gost/log/logger" |
| |
| perrors "github.com/pkg/errors" |
| ) |
| |
| import ( |
| "dubbo.apache.org/dubbo-go/v3/common" |
| "dubbo.apache.org/dubbo-go/v3/common/constant" |
| "dubbo.apache.org/dubbo-go/v3/common/extension" |
| "dubbo.apache.org/dubbo-go/v3/config/instance" |
| "dubbo.apache.org/dubbo-go/v3/registry" |
| ) |
| |
| // RegistryConfig is the configuration of the registry center |
| type RegistryConfig struct { |
| Protocol string `validate:"required" yaml:"protocol" json:"protocol,omitempty" property:"protocol"` |
| Timeout string `default:"5s" validate:"required" yaml:"timeout" json:"timeout,omitempty" property:"timeout"` // unit: second |
| Group string `yaml:"group" json:"group,omitempty" property:"group"` |
| Namespace string `yaml:"namespace" json:"namespace,omitempty" property:"namespace"` |
| TTL string `default:"15m" yaml:"ttl" json:"ttl,omitempty" property:"ttl"` // unit: minute |
| Address string `validate:"required" yaml:"address" json:"address,omitempty" property:"address"` |
| Username string `yaml:"username" json:"username,omitempty" property:"username"` |
| Password string `yaml:"password" json:"password,omitempty" property:"password"` |
| Simplified bool `yaml:"simplified" json:"simplified,omitempty" property:"simplified"` |
| Preferred bool `yaml:"preferred" json:"preferred,omitempty" property:"preferred"` // Always use this registry first if set to true, useful when subscribe to multiple registriesConfig |
| Zone string `yaml:"zone" json:"zone,omitempty" property:"zone"` // The region where the registry belongs, usually used to isolate traffics |
| Weight int64 `yaml:"weight" json:"weight,omitempty" property:"weight"` // Affects traffic distribution among registriesConfig, useful when subscribe to multiple registriesConfig Take effect only when no preferred registry is specified. |
| Params map[string]string `yaml:"params" json:"params,omitempty" property:"params"` |
| RegistryType string `yaml:"registry-type"` |
| UseAsMetaReport bool `default:"true" yaml:"use-as-meta-report" json:"use-as-meta-report,omitempty" property:"use-as-meta-report"` |
| UseAsConfigCenter bool `default:"true" yaml:"use-as-config-center" json:"use-as-config-center,omitempty" property:"use-as-config-center"` |
| } |
| |
| // Prefix dubbo.registries |
| func (RegistryConfig) Prefix() string { |
| return constant.RegistryConfigPrefix |
| } |
| |
| func (c *RegistryConfig) Init() error { |
| if err := defaults.Set(c); err != nil { |
| return err |
| } |
| return c.startRegistryConfig() |
| } |
| |
| func (c *RegistryConfig) getUrlMap(roleType common.RoleType) url.Values { |
| urlMap := url.Values{} |
| urlMap.Set(constant.RegistryGroupKey, c.Group) |
| urlMap.Set(constant.RegistryRoleKey, strconv.Itoa(int(roleType))) |
| urlMap.Set(constant.RegistryKey, c.Protocol) |
| urlMap.Set(constant.RegistryTimeoutKey, c.Timeout) |
| // multi registry invoker weight label for load balance |
| urlMap.Set(constant.RegistryKey+"."+constant.RegistryLabelKey, strconv.FormatBool(true)) |
| urlMap.Set(constant.RegistryKey+"."+constant.PreferredKey, strconv.FormatBool(c.Preferred)) |
| urlMap.Set(constant.RegistryKey+"."+constant.RegistryZoneKey, c.Zone) |
| urlMap.Set(constant.RegistryKey+"."+constant.WeightKey, strconv.FormatInt(c.Weight, 10)) |
| urlMap.Set(constant.RegistryTTLKey, c.TTL) |
| urlMap.Set(constant.ClientNameKey, clientNameID(c, c.Protocol, c.Address)) |
| |
| for k, v := range c.Params { |
| urlMap.Set(k, v) |
| } |
| return urlMap |
| } |
| |
| func (c *RegistryConfig) startRegistryConfig() error { |
| c.translateRegistryAddress() |
| if c.UseAsMetaReport && isValid(c.Address) { |
| if tmpUrl, err := c.toMetadataReportUrl(); err == nil { |
| instance.SetMetadataReportInstanceByReg(tmpUrl) |
| } else { |
| return perrors.Wrap(err, "Start RegistryConfig failed.") |
| } |
| } |
| return verify(c) |
| } |
| |
| // toMetadataReportUrl translate the registry configuration to the metadata reporting url |
| func (c *RegistryConfig) toMetadataReportUrl() (*common.URL, error) { |
| res, err := common.NewURL(c.Address, |
| common.WithLocation(c.Address), |
| common.WithProtocol(c.Protocol), |
| common.WithUsername(c.Username), |
| common.WithPassword(c.Password), |
| common.WithParamsValue(constant.TimeoutKey, c.Timeout), |
| common.WithParamsValue(constant.ClientNameKey, clientNameID(c, c.Protocol, c.Address)), |
| common.WithParamsValue(constant.MetadataReportGroupKey, c.Group), |
| common.WithParamsValue(constant.MetadataReportNamespaceKey, c.Namespace), |
| ) |
| if err != nil || len(res.Protocol) == 0 { |
| return nil, perrors.New("Invalid Registry Config.") |
| } |
| return res, nil |
| } |
| |
| // translateRegistryAddress translate registry address |
| // |
| // eg:address=nacos://127.0.0.1:8848 will return 127.0.0.1:8848 and protocol will set nacos |
| func (c *RegistryConfig) translateRegistryAddress() string { |
| if strings.Contains(c.Address, "://") { |
| u, err := url.Parse(c.Address) |
| if err != nil { |
| logger.Errorf("The registry url is invalid, error: %#v", err) |
| panic(err) |
| } |
| c.Protocol = u.Scheme |
| c.Address = strings.Join([]string{u.Host, u.Path}, "") |
| } |
| return c.Address |
| } |
| |
| func (c *RegistryConfig) GetInstance(roleType common.RoleType) (registry.Registry, error) { |
| u, err := c.toURL(roleType) |
| if err != nil { |
| return nil, err |
| } |
| // if the protocol == registry, set protocol the registry value in url.params |
| if u.Protocol == constant.RegistryProtocol { |
| u.Protocol = u.GetParam(constant.RegistryKey, "") |
| } |
| return extension.GetRegistry(u.Protocol, u) |
| } |
| |
| func (c *RegistryConfig) toURL(roleType common.RoleType) (*common.URL, error) { |
| address := c.translateRegistryAddress() |
| var registryURLProtocol string |
| if c.RegistryType == constant.RegistryTypeService { |
| // service discovery protocol |
| registryURLProtocol = constant.ServiceRegistryProtocol |
| } else if c.RegistryType == constant.RegistryTypeInterface { |
| registryURLProtocol = constant.RegistryProtocol |
| } else { |
| registryURLProtocol = constant.ServiceRegistryProtocol |
| } |
| return common.NewURL(registryURLProtocol+"://"+address, |
| common.WithParams(c.getUrlMap(roleType)), |
| common.WithParamsValue(constant.RegistrySimplifiedKey, strconv.FormatBool(c.Simplified)), |
| common.WithParamsValue(constant.RegistryKey, c.Protocol), |
| common.WithParamsValue(constant.RegistryNamespaceKey, c.Namespace), |
| common.WithParamsValue(constant.RegistryTimeoutKey, c.Timeout), |
| common.WithUsername(c.Username), |
| common.WithPassword(c.Password), |
| common.WithLocation(c.Address), |
| ) |
| } |
| |
| func (c *RegistryConfig) toURLs(roleType common.RoleType) ([]*common.URL, error) { |
| address := c.translateRegistryAddress() |
| var urls []*common.URL |
| var err error |
| var registryURL *common.URL |
| |
| if !isValid(c.Address) { |
| logger.Infof("Empty or N/A registry address found, the process will work with no registry enabled " + |
| "which means that the address of this instance will not be registered and not able to be found by other consumer instances.") |
| return urls, nil |
| } |
| |
| if c.RegistryType == constant.RegistryTypeService { |
| // service discovery protocol |
| if registryURL, err = c.createNewURL(constant.ServiceRegistryProtocol, address, roleType); err == nil { |
| urls = append(urls, registryURL) |
| } |
| } else if c.RegistryType == constant.RegistryTypeInterface { |
| if registryURL, err = c.createNewURL(constant.RegistryProtocol, address, roleType); err == nil { |
| urls = append(urls, registryURL) |
| } |
| } else if c.RegistryType == constant.RegistryTypeAll { |
| if registryURL, err = c.createNewURL(constant.ServiceRegistryProtocol, address, roleType); err == nil { |
| urls = append(urls, registryURL) |
| } |
| if registryURL, err = c.createNewURL(constant.RegistryProtocol, address, roleType); err == nil { |
| urls = append(urls, registryURL) |
| } |
| } else { |
| if registryURL, err = c.createNewURL(constant.ServiceRegistryProtocol, address, roleType); err == nil { |
| urls = append(urls, registryURL) |
| } |
| } |
| return urls, err |
| } |
| |
| func LoadRegistries(registryIds []string, registries map[string]*RegistryConfig, roleType common.RoleType) []*common.URL { |
| var registryURLs []*common.URL |
| //trSlice := strings.Split(targetRegistries, ",") |
| |
| for k, registryConf := range registries { |
| target := false |
| |
| // if user not config targetRegistries, default load all |
| // Notice: in func "func Split(s, sep string) []string" comment: |
| // if s does not contain sep and sep is not empty, SplitAfter returns |
| // a slice of length 1 whose only element is s. So we have to add the |
| // condition when targetRegistries string is not set (it will be "" when not set) |
| if len(registryIds) == 0 || (len(registryIds) == 1 && registryIds[0] == "") { |
| target = true |
| } else { |
| // else if user config targetRegistries |
| for _, tr := range registryIds { |
| if tr == k { |
| target = true |
| break |
| } |
| } |
| } |
| |
| if target { |
| if urls, err := registryConf.toURLs(roleType); err != nil { |
| logger.Errorf("The registry id: %s url is invalid, error: %#v", k, err) |
| panic(err) |
| } else { |
| registryURLs = append(registryURLs, urls...) |
| } |
| } |
| } |
| |
| return registryURLs |
| } |
| |
| func (c *RegistryConfig) createNewURL(protocol string, address string, roleType common.RoleType) (*common.URL, error) { |
| return common.NewURL(protocol+"://"+address, |
| common.WithParams(c.getUrlMap(roleType)), |
| common.WithParamsValue(constant.RegistrySimplifiedKey, strconv.FormatBool(c.Simplified)), |
| common.WithParamsValue(constant.RegistryKey, c.Protocol), |
| common.WithParamsValue(constant.RegistryNamespaceKey, c.Namespace), |
| common.WithParamsValue(constant.RegistryTimeoutKey, c.Timeout), |
| common.WithUsername(c.Username), |
| common.WithPassword(c.Password), |
| common.WithLocation(c.Address), |
| ) |
| } |
| |
| const ( |
| defaultZKAddr = "127.0.0.1:2181" // default registry address of zookeeper |
| defaultNacosAddr = "127.0.0.1:8848" // the default registry address of nacos |
| defaultRegistryTimeout = "3s" // the default registry timeout |
| ) |
| |
| type RegistryConfigOpt func(config *RegistryConfig) *RegistryConfig |
| |
| // NewRegistryConfigWithProtocolDefaultPort New default registry config |
| // the input @protocol can only be: |
| // "zookeeper" with default addr "127.0.0.1:2181" |
| // "nacos" with default addr "127.0.0.1:8848" |
| func NewRegistryConfigWithProtocolDefaultPort(protocol string) *RegistryConfig { |
| switch protocol { |
| case "zookeeper": |
| return &RegistryConfig{ |
| Protocol: protocol, |
| Address: defaultZKAddr, |
| Timeout: defaultRegistryTimeout, |
| } |
| case "nacos": |
| return &RegistryConfig{ |
| Protocol: protocol, |
| Address: defaultNacosAddr, |
| Timeout: defaultRegistryTimeout, |
| } |
| default: |
| return &RegistryConfig{ |
| Protocol: protocol, |
| } |
| } |
| } |
| |
| // NewRegistryConfig creates New RegistryConfig with @opts |
| func NewRegistryConfig(opts ...RegistryConfigOpt) *RegistryConfig { |
| newRegistryConfig := NewRegistryConfigWithProtocolDefaultPort("") |
| for _, v := range opts { |
| newRegistryConfig = v(newRegistryConfig) |
| } |
| return newRegistryConfig |
| } |
| |
| // WithRegistryProtocol returns RegistryConfigOpt with given @regProtocol name |
| func WithRegistryProtocol(regProtocol string) RegistryConfigOpt { |
| return func(config *RegistryConfig) *RegistryConfig { |
| config.Protocol = regProtocol |
| return config |
| } |
| } |
| |
| // WithRegistryAddress returns RegistryConfigOpt with given @addr registry address |
| func WithRegistryAddress(addr string) RegistryConfigOpt { |
| return func(config *RegistryConfig) *RegistryConfig { |
| config.Address = addr |
| return config |
| } |
| } |
| |
| // WithRegistryTimeOut returns RegistryConfigOpt with given @timeout registry config |
| func WithRegistryTimeOut(timeout string) RegistryConfigOpt { |
| return func(config *RegistryConfig) *RegistryConfig { |
| config.Timeout = timeout |
| return config |
| } |
| } |
| |
| // WithRegistryGroup returns RegistryConfigOpt with given @group registry group |
| func WithRegistryGroup(group string) RegistryConfigOpt { |
| return func(config *RegistryConfig) *RegistryConfig { |
| config.Group = group |
| return config |
| } |
| } |
| |
| // WithRegistryTTL returns RegistryConfigOpt with given @ttl registry ttl |
| func WithRegistryTTL(ttl string) RegistryConfigOpt { |
| return func(config *RegistryConfig) *RegistryConfig { |
| config.TTL = ttl |
| return config |
| } |
| } |
| |
| // WithRegistryUserName returns RegistryConfigOpt with given @userName registry userName |
| func WithRegistryUserName(userName string) RegistryConfigOpt { |
| return func(config *RegistryConfig) *RegistryConfig { |
| config.Username = userName |
| return config |
| } |
| } |
| |
| // WithRegistryPassword returns RegistryConfigOpt with given @psw registry password |
| func WithRegistryPassword(psw string) RegistryConfigOpt { |
| return func(config *RegistryConfig) *RegistryConfig { |
| config.Password = psw |
| return config |
| } |
| } |
| |
| // WithRegistrySimplified returns RegistryConfigOpt with given @simplified registry simplified flag |
| func WithRegistrySimplified(simplified bool) RegistryConfigOpt { |
| return func(config *RegistryConfig) *RegistryConfig { |
| config.Simplified = simplified |
| return config |
| } |
| } |
| |
| // WithRegistryPreferred returns RegistryConfig with given @preferred registry preferred flag |
| func WithRegistryPreferred(preferred bool) RegistryConfigOpt { |
| return func(config *RegistryConfig) *RegistryConfig { |
| config.Preferred = preferred |
| return config |
| } |
| } |
| |
| // WithRegistryWeight returns RegistryConfigOpt with given @weight registry weight flag |
| func WithRegistryWeight(weight int64) RegistryConfigOpt { |
| return func(config *RegistryConfig) *RegistryConfig { |
| config.Weight = weight |
| return config |
| } |
| } |
| |
| // WithRegistryParams returns RegistryConfigOpt with given registry @params |
| func WithRegistryParams(params map[string]string) RegistryConfigOpt { |
| return func(config *RegistryConfig) *RegistryConfig { |
| config.Params = params |
| return config |
| } |
| } |
| |
| func NewRegistryConfigBuilder() *RegistryConfigBuilder { |
| return &RegistryConfigBuilder{ |
| registryConfig: &RegistryConfig{}, |
| } |
| } |
| |
| type RegistryConfigBuilder struct { |
| registryConfig *RegistryConfig |
| } |
| |
| func (rcb *RegistryConfigBuilder) SetProtocol(protocol string) *RegistryConfigBuilder { |
| rcb.registryConfig.Protocol = protocol |
| return rcb |
| } |
| |
| func (rcb *RegistryConfigBuilder) SetTimeout(timeout string) *RegistryConfigBuilder { |
| rcb.registryConfig.Timeout = timeout |
| return rcb |
| } |
| |
| func (rcb *RegistryConfigBuilder) SetGroup(group string) *RegistryConfigBuilder { |
| rcb.registryConfig.Group = group |
| return rcb |
| } |
| |
| func (rcb *RegistryConfigBuilder) SetNamespace(namespace string) *RegistryConfigBuilder { |
| rcb.registryConfig.Namespace = namespace |
| return rcb |
| } |
| |
| func (rcb *RegistryConfigBuilder) SetTTL(ttl string) *RegistryConfigBuilder { |
| rcb.registryConfig.TTL = ttl |
| return rcb |
| } |
| |
| func (rcb *RegistryConfigBuilder) SetAddress(address string) *RegistryConfigBuilder { |
| rcb.registryConfig.Address = address |
| return rcb |
| } |
| |
| func (rcb *RegistryConfigBuilder) SetUsername(username string) *RegistryConfigBuilder { |
| rcb.registryConfig.Username = username |
| return rcb |
| } |
| |
| func (rcb *RegistryConfigBuilder) SetPassword(password string) *RegistryConfigBuilder { |
| rcb.registryConfig.Password = password |
| return rcb |
| } |
| |
| func (rcb *RegistryConfigBuilder) SetSimplified(simplified bool) *RegistryConfigBuilder { |
| rcb.registryConfig.Simplified = simplified |
| return rcb |
| } |
| |
| func (rcb *RegistryConfigBuilder) SetPreferred(preferred bool) *RegistryConfigBuilder { |
| rcb.registryConfig.Preferred = preferred |
| return rcb |
| } |
| |
| func (rcb *RegistryConfigBuilder) SetZone(zone string) *RegistryConfigBuilder { |
| rcb.registryConfig.Zone = zone |
| return rcb |
| } |
| |
| func (rcb *RegistryConfigBuilder) SetWeight(weight int64) *RegistryConfigBuilder { |
| rcb.registryConfig.Weight = weight |
| return rcb |
| } |
| |
| func (rcb *RegistryConfigBuilder) SetParams(params map[string]string) *RegistryConfigBuilder { |
| rcb.registryConfig.Params = params |
| return rcb |
| } |
| |
| func (rcb *RegistryConfigBuilder) AddParam(key, value string) *RegistryConfigBuilder { |
| if rcb.registryConfig.Params == nil { |
| rcb.registryConfig.Params = make(map[string]string) |
| } |
| rcb.registryConfig.Params[key] = value |
| return rcb |
| } |
| |
| func (rcb *RegistryConfigBuilder) SetRegistryType(registryType string) *RegistryConfigBuilder { |
| rcb.registryConfig.RegistryType = registryType |
| return rcb |
| } |
| |
| func (rcb *RegistryConfigBuilder) Build() *RegistryConfig { |
| if err := rcb.registryConfig.Init(); err != nil { |
| panic(err) |
| } |
| return rcb.registryConfig |
| } |
| |
| // DynamicUpdateProperties update registry |
| func (c *RegistryConfig) DynamicUpdateProperties(updateRegistryConfig *RegistryConfig) { |
| // if nacos's registry timeout not equal local root config's registry timeout , update. |
| if updateRegistryConfig != nil && updateRegistryConfig.Timeout != c.Timeout { |
| c.Timeout = updateRegistryConfig.Timeout |
| logger.Infof("RegistryConfigs Timeout was dynamically updated, new value:%v", c.Timeout) |
| } |
| } |