blob: 7ecac6325cf9ec47959c463aeec7997cc7829a7a [file] [log] [blame]
/*
* 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 registry
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"
commonCfg "dubbo.apache.org/dubbo-go/v3/common/config"
"dubbo.apache.org/dubbo-go/v3/common/constant"
"dubbo.apache.org/dubbo-go/v3/common/extension"
"dubbo.apache.org/dubbo-go/v3/config/instance"
)
// 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 && commonCfg.IsValid(c.Address) {
if tmpUrl, err := c.toMetadataReportUrl(); err == nil {
instance.SetMetadataReportInstanceByReg(tmpUrl)
} else {
return perrors.Wrap(err, "Start RegistryConfig failed.")
}
}
return commonCfg.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, 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 !commonCfg.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
}
// ClientNameID unique identifier id for client
func ClientNameID(config *RegistryConfig, protocol, address string) string {
return strings.Join([]string{config.Prefix(), protocol, address}, "-")
}
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),
)
}
// 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)
}
}