blob: 2824ec68ddc94f27373476a0866ef01d92a72ecc [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 common
import (
"bytes"
"encoding/base64"
"fmt"
"math"
"net"
"net/url"
"strconv"
"strings"
"sync"
"time"
)
import (
cm "github.com/Workiva/go-datastructures/common"
gxset "github.com/dubbogo/gost/container/set"
"github.com/google/uuid"
"github.com/jinzhu/copier"
perrors "github.com/pkg/errors"
)
import (
"dubbo.apache.org/dubbo-go/v3/common/constant"
)
// dubbo role type constant
const (
CONSUMER = iota
CONFIGURATOR
ROUTER
PROVIDER
PROTOCOL = "protocol"
)
var (
DubboNodes = [...]string{"consumers", "configurators", "routers", "providers"} // Dubbo service node
DubboRole = [...]string{"consumer", "", "routers", "provider"} // Dubbo service role
compareURLEqualFunc CompareURLEqualFunc // function to compare two URL is equal
)
func init() {
compareURLEqualFunc = defaultCompareURLEqual
}
// nolint
type RoleType int
func (t RoleType) String() string {
return DubboNodes[t]
}
// Role returns role by @RoleType
func (t RoleType) Role() string {
return DubboRole[t]
}
type baseURL struct {
Protocol string
Location string // ip+port
Ip string
Port string
PrimitiveURL string
}
// noCopy may be embedded into structs which must not be copied
// after the first use.
//
// See https://golang.org/issues/8005#issuecomment-190753527
// for details.
type noCopy struct{}
// Lock is a no-op used by -copylocks checker from `go vet`.
func (*noCopy) Lock() {}
func (*noCopy) Unlock() {}
// URL thread-safe. but this URL should not be copied.
// we fail to define this struct to be immutable object.
// but, those method which will update the URL, including SetParam, SetParams
// are only allowed to be invoked in creating URL instance
// Please keep in mind that this struct is immutable after it has been created and initialized.
type URL struct {
noCopy noCopy
baseURL
// url.Values is not safe map, add to avoid concurrent map read and map write error
paramsLock sync.RWMutex
params url.Values
Path string // like /com.ikurento.dubbo.UserProvider
Username string
Password string
Methods []string
// special for registry
SubURL *URL
}
// JavaClassName POJO for URL
func (c *URL) JavaClassName() string {
return "org.apache.dubbo.common.URL"
}
// Option accepts URL
// Option will define a function of handling URL
type Option func(*URL)
// WithUsername sets username for URL
func WithUsername(username string) Option {
return func(url *URL) {
url.Username = username
}
}
// WithPassword sets password for URL
func WithPassword(pwd string) Option {
return func(url *URL) {
url.Password = pwd
}
}
// WithMethods sets methods for URL
func WithMethods(methods []string) Option {
return func(url *URL) {
url.Methods = methods
}
}
// WithParams deep copy the params in the argument into params of the target URL
func WithParams(params url.Values) Option {
return func(url *URL) {
url.SetParams(params)
}
}
// WithParamsValue sets params field for URL
func WithParamsValue(key, val string) Option {
return func(url *URL) {
url.SetParam(key, val)
}
}
// WithProtocol sets protocol for URL
func WithProtocol(proto string) Option {
return func(url *URL) {
url.Protocol = proto
}
}
// WithIp sets ip for URL
func WithIp(ip string) Option {
return func(url *URL) {
url.Ip = ip
}
}
// WithPort sets port for URL
func WithPort(port string) Option {
return func(url *URL) {
url.Port = port
}
}
// WithPath sets path for URL
func WithPath(path string) Option {
return func(url *URL) {
url.Path = "/" + strings.TrimPrefix(path, "/")
}
}
// WithInterface sets interface param for URL
func WithInterface(v string) Option {
return func(url *URL) {
url.SetParam(constant.InterfaceKey, v)
}
}
// WithLocation sets location for URL
func WithLocation(location string) Option {
return func(url *URL) {
url.Location = location
}
}
// WithToken sets token for URL
func WithToken(token string) Option {
return func(url *URL) {
if len(token) > 0 {
value := token
if strings.ToLower(token) == "true" || strings.ToLower(token) == "default" {
u, _ := uuid.NewUUID()
value = u.String()
}
url.SetParam(constant.TokenKey, value)
}
}
}
// NewURLWithOptions will create a new URL with options
func NewURLWithOptions(opts ...Option) *URL {
newURL := &URL{}
for _, opt := range opts {
opt(newURL)
}
newURL.Location = newURL.Ip + ":" + newURL.Port
return newURL
}
// NewURL will create a new URL
// the urlString should not be empty
func NewURL(urlString string, opts ...Option) (*URL, error) {
s := URL{baseURL: baseURL{}}
if urlString == "" {
return &s, nil
}
rawURLString, err := url.QueryUnescape(urlString)
if err != nil {
return &s, perrors.Errorf("URL.QueryUnescape(%s), error{%v}", urlString, err)
}
// rawURLString = "//" + rawURLString
if !strings.Contains(rawURLString, "//") {
t := URL{baseURL: baseURL{}}
for _, opt := range opts {
opt(&t)
}
rawURLString = t.Protocol + "://" + rawURLString
}
serviceURL, urlParseErr := url.Parse(rawURLString)
if urlParseErr != nil {
return &s, perrors.Errorf("URL.Parse(URL string{%s}), error{%v}", rawURLString, err)
}
s.params, err = url.ParseQuery(serviceURL.RawQuery)
if err != nil {
return &s, perrors.Errorf("URL.ParseQuery(raw URL string{%s}), error{%v}", serviceURL.RawQuery, err)
}
s.PrimitiveURL = urlString
s.Protocol = serviceURL.Scheme
s.Username = serviceURL.User.Username()
s.Password, _ = serviceURL.User.Password()
s.Location = serviceURL.Host
s.Path = serviceURL.Path
for _, location := range strings.Split(s.Location, ",") {
location = strings.Trim(location, " ")
if strings.Contains(location, ":") {
s.Ip, s.Port, err = net.SplitHostPort(location)
if err != nil {
return &s, perrors.Errorf("net.SplitHostPort(url.Host{%s}), error{%v}", s.Location, err)
}
break
}
}
for _, opt := range opts {
opt(&s)
}
return &s, nil
}
func MatchKey(serviceKey string, protocol string) string {
return serviceKey + ":" + protocol
}
// Group get group
func (c *URL) Group() string {
return c.GetParam(constant.GroupKey, "")
}
// Interface get interface
func (c *URL) Interface() string {
return c.GetParam(constant.InterfaceKey, "")
}
// Version get group
func (c *URL) Version() string {
return c.GetParam(constant.VersionKey, "")
}
// Address with format "ip:port"
func (c *URL) Address() string {
if c.Port == "" {
return c.Ip
}
return c.Ip + ":" + c.Port
}
// URLEqual judge @URL and @c is equal or not.
func (c *URL) URLEqual(url *URL) bool {
tmpC := c.Clone()
tmpC.Ip = ""
tmpC.Port = ""
tmpURL := url.Clone()
tmpURL.Ip = ""
tmpURL.Port = ""
cGroup := tmpC.GetParam(constant.GroupKey, "")
urlGroup := tmpURL.GetParam(constant.GroupKey, "")
cKey := tmpC.Key()
urlKey := tmpURL.Key()
if cGroup == constant.AnyValue {
cKey = strings.Replace(cKey, "group=*", "group="+urlGroup, 1)
} else if urlGroup == constant.AnyValue {
urlKey = strings.Replace(urlKey, "group=*", "group="+cGroup, 1)
}
// 1. protocol, username, password, ip, port, service name, group, version should be equal
if cKey != urlKey {
return false
}
// 2. if URL contains enabled key, should be true, or *
if tmpURL.GetParam(constant.EnabledKey, "true") != "true" && tmpURL.GetParam(constant.EnabledKey, "") != constant.AnyValue {
return false
}
// TODO :may need add interface key any value condition
return isMatchCategory(tmpURL.GetParam(constant.CategoryKey, constant.DefaultCategory), tmpC.GetParam(constant.CategoryKey, constant.DefaultCategory))
}
func isMatchCategory(category1 string, category2 string) bool {
if len(category2) == 0 {
return category1 == constant.DefaultCategory
} else if strings.Contains(category2, constant.AnyValue) {
return true
} else if strings.Contains(category2, constant.RemoveValuePrefix) {
return !strings.Contains(category2, constant.RemoveValuePrefix+category1)
} else {
return strings.Contains(category2, category1)
}
}
func (c *URL) String() string {
c.paramsLock.Lock()
defer c.paramsLock.Unlock()
var buf strings.Builder
if len(c.Username) == 0 && len(c.Password) == 0 {
buf.WriteString(fmt.Sprintf("%s://%s:%s%s?", c.Protocol, c.Ip, c.Port, c.Path))
} else {
buf.WriteString(fmt.Sprintf("%s://%s:%s@%s:%s%s?", c.Protocol, c.Username, c.Password, c.Ip, c.Port, c.Path))
}
buf.WriteString(c.params.Encode())
return buf.String()
}
// Key gets key
func (c *URL) Key() string {
buildString := fmt.Sprintf("%s://%s:%s@%s:%s/?interface=%s&group=%s&version=%s",
c.Protocol, c.Username, c.Password, c.Ip, c.Port, c.Service(), c.GetParam(constant.GroupKey, ""), c.GetParam(constant.VersionKey, ""))
return buildString
}
// GetCacheInvokerMapKey get directory cacheInvokerMap key
func (c *URL) GetCacheInvokerMapKey() string {
urlNew, _ := NewURL(c.PrimitiveURL)
buildString := fmt.Sprintf("%s://%s:%s@%s:%s/?interface=%s&group=%s&version=%s&timestamp=%s&"+constant.MeshClusterIDKey+"=%s",
c.Protocol, c.Username, c.Password, c.Ip, c.Port, c.Service(), c.GetParam(constant.GroupKey, ""),
c.GetParam(constant.VersionKey, ""), urlNew.GetParam(constant.TimestampKey, ""),
c.GetParam(constant.MeshClusterIDKey, ""))
return buildString
}
// ServiceKey gets a unique key of a service.
func (c *URL) ServiceKey() string {
return ServiceKey(c.GetParam(constant.InterfaceKey, strings.TrimPrefix(c.Path, constant.PathSeparator)),
c.GetParam(constant.GroupKey, ""), c.GetParam(constant.VersionKey, ""))
}
func ServiceKey(intf string, group string, version string) string {
if intf == "" {
return ""
}
buf := &bytes.Buffer{}
if group != "" {
buf.WriteString(group)
buf.WriteString("/")
}
buf.WriteString(intf)
if version != "" && version != "0.0.0" {
buf.WriteString(":")
buf.WriteString(version)
}
return buf.String()
}
// ParseServiceKey gets interface, group and version from service key
func ParseServiceKey(serviceKey string) (string, string, string) {
var (
group string
version string
)
if serviceKey == "" {
return "", "", ""
}
// get group if it exists
sepIndex := strings.Index(serviceKey, constant.PathSeparator)
if sepIndex != -1 {
group = serviceKey[:sepIndex]
serviceKey = serviceKey[sepIndex+1:]
}
// get version if it exists
sepIndex = strings.LastIndex(serviceKey, constant.KeySeparator)
if sepIndex != -1 {
version = serviceKey[sepIndex+1:]
serviceKey = serviceKey[:sepIndex]
}
return serviceKey, group, version
}
// IsAnyCondition judges if is any condition
func IsAnyCondition(intf, group, version string, serviceURL *URL) bool {
return intf == constant.AnyValue && (group == constant.AnyValue ||
group == serviceURL.Group()) && (version == constant.AnyValue || version == serviceURL.Version())
}
// ColonSeparatedKey
// The format is "{interface}:[version]:[group]"
func (c *URL) ColonSeparatedKey() string {
intf := c.GetParam(constant.InterfaceKey, strings.TrimPrefix(c.Path, "/"))
if intf == "" {
return ""
}
var buf strings.Builder
buf.WriteString(intf)
buf.WriteString(":")
version := c.GetParam(constant.VersionKey, "")
if version != "" && version != "0.0.0" {
buf.WriteString(version)
}
group := c.GetParam(constant.GroupKey, "")
buf.WriteString(":")
if group != "" {
buf.WriteString(group)
}
return buf.String()
}
// EncodedServiceKey encode the service key
func (c *URL) EncodedServiceKey() string {
serviceKey := c.ServiceKey()
return strings.Replace(serviceKey, "/", "*", 1)
}
// Service gets service
func (c *URL) Service() string {
service := c.GetParam(constant.InterfaceKey, strings.TrimPrefix(c.Path, "/"))
if service != "" {
return service
} else if c.SubURL != nil {
service = c.SubURL.GetParam(constant.InterfaceKey, strings.TrimPrefix(c.Path, "/"))
if service != "" { // if URL.path is "" then return suburl's path, special for registry URL
return service
}
}
return ""
}
// AddParam will add the key-value pair
func (c *URL) AddParam(key string, value string) {
c.paramsLock.Lock()
defer c.paramsLock.Unlock()
if c.params == nil {
c.params = url.Values{}
}
c.params.Add(key, value)
}
// AddParamAvoidNil will add key-value pair
func (c *URL) AddParamAvoidNil(key string, value string) {
c.paramsLock.Lock()
defer c.paramsLock.Unlock()
if c.params == nil {
c.params = url.Values{}
}
c.params.Add(key, value)
}
// SetParam will put the key-value pair into URL
// usually it should only be invoked when you want to initialized an URL
func (c *URL) SetParam(key string, value string) {
c.paramsLock.Lock()
defer c.paramsLock.Unlock()
if c.params == nil {
c.params = url.Values{}
}
c.params.Set(key, value)
}
// DelParam will delete the given key from the URL
func (c *URL) DelParam(key string) {
c.paramsLock.Lock()
defer c.paramsLock.Unlock()
if c.params != nil {
c.params.Del(key)
}
}
// ReplaceParams will replace the URL.params
// usually it should only be invoked when you want to modify an URL, such as MergeURL
func (c *URL) ReplaceParams(param url.Values) {
c.paramsLock.Lock()
defer c.paramsLock.Unlock()
c.params = param
}
// RangeParams will iterate the params
func (c *URL) RangeParams(f func(key, value string) bool) {
c.paramsLock.RLock()
defer c.paramsLock.RUnlock()
for k, v := range c.params {
if !f(k, v[0]) {
break
}
}
}
// GetParam gets value by key
func (c *URL) GetParam(s string, d string) string {
c.paramsLock.RLock()
defer c.paramsLock.RUnlock()
var r string
if len(c.params) > 0 {
r = c.params.Get(s)
}
if len(r) == 0 {
r = d
}
return r
}
// GetNonDefaultParam gets value by key, return nil,false if no value found mapping to the key
func (c *URL) GetNonDefaultParam(s string) (string, bool) {
c.paramsLock.RLock()
defer c.paramsLock.RUnlock()
var r string
if len(c.params) > 0 {
r = c.params.Get(s)
}
return r, r != ""
}
// GetParams gets values
func (c *URL) GetParams() url.Values {
return c.params
}
// GetParamAndDecoded gets values and decode
func (c *URL) GetParamAndDecoded(key string) (string, error) {
ruleDec, err := base64.URLEncoding.DecodeString(c.GetParam(key, ""))
value := string(ruleDec)
return value, err
}
// GetRawParam gets raw param
func (c *URL) GetRawParam(key string) string {
switch key {
case PROTOCOL:
return c.Protocol
case "username":
return c.Username
case "host":
return strings.Split(c.Location, ":")[0]
case "password":
return c.Password
case "port":
return c.Port
case "path":
return c.Path
default:
return c.GetParam(key, "")
}
}
// GetParamBool judge whether @key exists or not
func (c *URL) GetParamBool(key string, d bool) bool {
r, err := strconv.ParseBool(c.GetParam(key, ""))
if err != nil {
return d
}
return r
}
// GetParamInt gets int64 value by @key
func (c *URL) GetParamInt(key string, d int64) int64 {
r, err := strconv.ParseInt(c.GetParam(key, ""), 10, 64)
if err != nil {
return d
}
return r
}
// GetParamInt32 gets int32 value by @key
func (c *URL) GetParamInt32(key string, d int32) int32 {
r, err := strconv.ParseInt(c.GetParam(key, ""), 10, 32)
if err != nil {
return d
}
return int32(r)
}
// GetParamByIntValue gets int value by @key
func (c *URL) GetParamByIntValue(key string, d int) int {
r, err := strconv.ParseInt(c.GetParam(key, ""), 10, 0)
if err != nil {
return d
}
return int(r)
}
// GetMethodParamInt gets int method param
func (c *URL) GetMethodParamInt(method string, key string, d int64) int64 {
r, err := strconv.ParseInt(c.GetParam("methods."+method+"."+key, ""), 10, 64)
if err != nil {
return d
}
return r
}
// GetMethodParamIntValue gets int method param
func (c *URL) GetMethodParamIntValue(method string, key string, d int) int {
r, err := strconv.ParseInt(c.GetParam("methods."+method+"."+key, ""), 10, 0)
if err != nil {
return d
}
return int(r)
}
// GetMethodParamInt64 gets int64 method param
func (c *URL) GetMethodParamInt64(method string, key string, d int64) int64 {
r := c.GetMethodParamInt(method, key, math.MinInt64)
if r == math.MinInt64 {
return c.GetParamInt(key, d)
}
return r
}
// GetMethodParam gets method param
func (c *URL) GetMethodParam(method string, key string, d string) string {
r := c.GetParam("methods."+method+"."+key, "")
if r == "" {
r = d
}
return r
}
// GetMethodParamBool judge whether @method param exists or not
func (c *URL) GetMethodParamBool(method string, key string, d bool) bool {
r := c.GetParamBool("methods."+method+"."+key, d)
return r
}
// SetParams will put all key-value pair into URL.
// 1. if there already has same key, the value will be override
// 2. it's not thread safe
// 3. think twice when you want to invoke this method
func (c *URL) SetParams(m url.Values) {
for k := range m {
c.SetParam(k, m.Get(k))
}
}
// ToMap transfer URL to Map
func (c *URL) ToMap() map[string]string {
paramsMap := make(map[string]string)
c.RangeParams(func(key, value string) bool {
paramsMap[key] = value
return true
})
if c.Protocol != "" {
paramsMap[PROTOCOL] = c.Protocol
}
if c.Username != "" {
paramsMap["username"] = c.Username
}
if c.Password != "" {
paramsMap["password"] = c.Password
}
if c.Location != "" {
paramsMap["host"] = strings.Split(c.Location, ":")[0]
var port string
if strings.Contains(c.Location, ":") {
port = strings.Split(c.Location, ":")[1]
} else {
port = "0"
}
paramsMap["port"] = port
}
if c.Protocol != "" {
paramsMap[PROTOCOL] = c.Protocol
}
if c.Path != "" {
paramsMap["path"] = c.Path
}
if len(paramsMap) == 0 {
return nil
}
return paramsMap
}
// configuration > reference config >service config
// in this function we should merge the reference local URL config into the service URL from registry.
// TODO configuration merge, in the future , the configuration center's config should merge too.
// MergeURL will merge those two URL
// the result is based on serviceURL, and the key which si only contained in referenceURL
// will be added into result.
// for example, if serviceURL contains params (a1->v1, b1->v2) and referenceURL contains params(a2->v3, b1 -> v4)
// the params of result will be (a1->v1, b1->v2, a2->v3).
// You should notice that the value of b1 is v2, not v4
// except constant.LoadbalanceKey, constant.ClusterKey, constant.RetriesKey, constant.TimeoutKey.
// due to URL is not thread-safe, so this method is not thread-safe
func MergeURL(serviceURL *URL, referenceURL *URL) *URL {
// After Clone, it is a new URL that there is no thread safe issue.
mergedURL := serviceURL.Clone()
params := mergedURL.GetParams()
// iterator the referenceURL if serviceURL not have the key ,merge in
// referenceURL usually will not changed. so change RangeParams to GetParams to avoid the string value copy.// Group get group
for key, value := range referenceURL.GetParams() {
if _, ok := mergedURL.GetNonDefaultParam(key); !ok {
if len(value) > 0 {
params[key] = value
}
params[key] = make([]string, len(value))
copy(params[key], value)
}
}
// remote timestamp
if v, ok := serviceURL.GetNonDefaultParam(constant.TimestampKey); !ok {
params[constant.RemoteTimestampKey] = []string{v}
params[constant.TimestampKey] = []string{referenceURL.GetParam(constant.TimestampKey, "")}
}
// finally execute methodConfigMergeFcn
for _, method := range referenceURL.Methods {
for _, paramKey := range []string{constant.LoadbalanceKey, constant.ClusterKey, constant.RetriesKey, constant.TimeoutKey} {
if v := referenceURL.GetParam(paramKey, ""); len(v) > 0 {
params[paramKey] = []string{v}
}
methodsKey := "methods." + method + "." + paramKey
//if len(mergedURL.GetParam(methodsKey, "")) == 0 {
if v := referenceURL.GetParam(methodsKey, ""); len(v) > 0 {
params[methodsKey] = []string{v}
}
//}
}
}
// In this way, we will raise some performance.
mergedURL.ReplaceParams(params)
return mergedURL
}
// Clone will copy the URL
func (c *URL) Clone() *URL {
newURL := &URL{}
if err := copier.Copy(newURL, c); err != nil {
// this is impossible
return newURL
}
newURL.params = url.Values{}
c.RangeParams(func(key, value string) bool {
newURL.SetParam(key, value)
return true
})
return newURL
}
func (c *URL) CloneExceptParams(excludeParams *gxset.HashSet) *URL {
newURL := &URL{}
if err := copier.Copy(newURL, c); err != nil {
// this is impossible
return newURL
}
newURL.params = url.Values{}
c.RangeParams(func(key, value string) bool {
if !excludeParams.Contains(key) {
newURL.SetParam(key, value)
}
return true
})
return newURL
}
func (c *URL) Compare(comp cm.Comparator) int {
a := c.String()
b := comp.(*URL).String()
switch {
case a > b:
return 1
case a < b:
return -1
default:
return 0
}
}
// CloneWithParams Copy URL based on the reserved parameter's keys.
func (c *URL) CloneWithParams(reserveParams []string) *URL {
params := url.Values{}
for _, reserveParam := range reserveParams {
v := c.GetParam(reserveParam, "")
if len(v) != 0 {
params.Set(reserveParam, v)
}
}
return NewURLWithOptions(
WithProtocol(c.Protocol),
WithUsername(c.Username),
WithPassword(c.Password),
WithIp(c.Ip),
WithPort(c.Port),
WithPath(c.Path),
WithMethods(c.Methods),
WithParams(params),
)
}
// IsEquals compares if two URLs equals with each other. Excludes are all parameter keys which should ignored.
func IsEquals(left *URL, right *URL, excludes ...string) bool {
if (left == nil && right != nil) || (right == nil && left != nil) {
return false
}
if left.Ip != right.Ip || left.Port != right.Port {
return false
}
leftMap := left.ToMap()
rightMap := right.ToMap()
for _, exclude := range excludes {
delete(leftMap, exclude)
delete(rightMap, exclude)
}
if len(leftMap) != len(rightMap) {
return false
}
for lk, lv := range leftMap {
if rv, ok := rightMap[lk]; !ok {
return false
} else if lv != rv {
return false
}
}
return true
}
// URLSlice will be used to sort URL instance
// Instances will be order by URL.String()
type URLSlice []*URL
// nolint
func (s URLSlice) Len() int {
return len(s)
}
// nolint
func (s URLSlice) Less(i, j int) bool {
return s[i].String() < s[j].String()
}
// nolint
func (s URLSlice) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
type CompareURLEqualFunc func(l *URL, r *URL, excludeParam ...string) bool
func defaultCompareURLEqual(l *URL, r *URL, excludeParam ...string) bool {
return IsEquals(l, r, excludeParam...)
}
func SetCompareURLEqualFunc(f CompareURLEqualFunc) {
compareURLEqualFunc = f
}
func GetCompareURLEqualFunc() CompareURLEqualFunc {
return compareURLEqualFunc
}
// GetParamDuration get duration if param is invalid or missing will return 3s
func (c *URL) GetParamDuration(s string, d string) time.Duration {
if t, err := time.ParseDuration(c.GetParam(s, d)); err == nil {
return t
}
return 3 * time.Second
}
func GetSubscribeName(url *URL) string {
var buffer bytes.Buffer
buffer.Write([]byte(DubboNodes[PROVIDER]))
appendParam(&buffer, url, constant.InterfaceKey)
appendParam(&buffer, url, constant.VersionKey)
appendParam(&buffer, url, constant.GroupKey)
return buffer.String()
}
func appendParam(target *bytes.Buffer, url *URL, key string) {
value := url.GetParam(key, "")
target.Write([]byte(constant.NacosServiceNameSeparator))
if strings.TrimSpace(value) != "" {
target.Write([]byte(value))
}
}