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)
}
AddParam will add the key-value pair
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/jinzhu/copier"
	perrors "github.com/pkg/errors"
	"github.com/satori/go.uuid"
)
import (
	"dubbo.apache.org/dubbo-go/v3/common/constant"
)
const (
	CONSUMER = iota
	CONFIGURATOR
	ROUTER
	PROVIDER
	PROTOCOL = "protocol"
)
var (
	DubboNodes = [...]string{"consumers", "configurators", "routers", "providers"}
	DubboRole = [...]string{"consumer", "", "routers", "provider"}
	compareURLEqualFunc CompareURLEqualFunc
)
func init() {
	compareURLEqualFunc = defaultCompareURLEqual
}
type RoleType int
func (t RoleType) String() string {
	return DubboNodes[t]
}
func (t RoleType) Role() string {
	return DubboRole[t]
}
type baseURL struct {
	Protocol string
	Location string  
	Ip       string
	Port     string
	PrimitiveURL string
}
type noCopy struct{}
func (*noCopy) Lock()   {}
func (*noCopy) Unlock() {}
type URL struct {
	noCopy noCopy
	baseURL
	paramsLock sync.RWMutex
	params     url.Values
	Path     string  
	Username string
	Password string
	Methods  []string
	SubURL *URL
}
func (c *URL) JavaClassName() string {
	return "org.apache.dubbo.common.URL"
}
type Option func(*URL)
func WithUsername(username string) Option {
	return func(url *URL) {
		url.Username = username
	}
}
func WithPassword(pwd string) Option {
	return func(url *URL) {
		url.Password = pwd
	}
}
func WithMethods(methods []string) Option {
	return func(url *URL) {
		url.Methods = methods
	}
}
func WithParams(params url.Values) Option {
	return func(url *URL) {
		url.params = params
	}
}
func WithParamsValue(key, val string) Option {
	return func(url *URL) {
		url.SetParam(key, val)
	}
}
func WithProtocol(proto string) Option {
	return func(url *URL) {
		url.Protocol = proto
	}
}
func WithIp(ip string) Option {
	return func(url *URL) {
		url.Ip = ip
	}
}
func WithPort(port string) Option {
	return func(url *URL) {
		url.Port = port
	}
}
func WithPath(path string) Option {
	return func(url *URL) {
		url.Path = "/" + strings.TrimPrefix(path, "/")
	}
}
func WithLocation(location string) Option {
	return func(url *URL) {
		url.Location = location
	}
}
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.NewV4()
				value = u.String()
			}
			url.SetParam(constant.TOKEN_KEY, value)
		}
	}
}
func NewURLWithOptions(opts ...Option) *URL {
	newURL := &URL{}
	for _, opt := range opts {
		opt(newURL)
	}
	newURL.Location = newURL.Ip + ":" + newURL.Port
	return newURL
}
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)
	}
	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, ",") {
		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
}
func (c *URL) Group() string {
	return c.GetParam(constant.GROUP_KEY, "")
}
func (c *URL) Version() string {
	return c.GetParam(constant.VERSION_KEY, "")
}
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.GROUP_KEY, "")
	urlGroup := tmpURL.GetParam(constant.GROUP_KEY, "")
	cKey := tmpC.Key()
	urlKey := tmpURL.Key()
	if cGroup == constant.ANY_VALUE {
		cKey = strings.Replace(cKey, "group=*", "group="+urlGroup, 1)
	} else if urlGroup == constant.ANY_VALUE {
		urlKey = strings.Replace(urlKey, "group=*", "group="+cGroup, 1)
	}
	if cKey != urlKey {
		return false
	}
	if tmpURL.GetParam(constant.ENABLED_KEY, "true") != "true" && tmpURL.GetParam(constant.ENABLED_KEY, "") != constant.ANY_VALUE {
		return false
	}
	return isMatchCategory(tmpURL.GetParam(constant.CATEGORY_KEY, constant.DEFAULT_CATEGORY), tmpC.GetParam(constant.CATEGORY_KEY, constant.DEFAULT_CATEGORY))
}
func isMatchCategory(category1 string, category2 string) bool {
	if len(category2) == 0 {
		return category1 == constant.DEFAULT_CATEGORY
	} else if strings.Contains(category2, constant.ANY_VALUE) {
		return true
	} else if strings.Contains(category2, constant.REMOVE_VALUE_PREFIX) {
		return !strings.Contains(category2, constant.REMOVE_VALUE_PREFIX+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()
}
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.GROUP_KEY, ""), c.GetParam(constant.VERSION_KEY, ""))
	return buildString
}
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",
		c.Protocol, c.Username, c.Password, c.Ip, c.Port, c.Service(), c.GetParam(constant.GROUP_KEY, ""),
		c.GetParam(constant.VERSION_KEY, ""), urlNew.GetParam(constant.TIMESTAMP_KEY, ""))
	return buildString
}
func (c *URL) ServiceKey() string {
	return ServiceKey(c.GetParam(constant.INTERFACE_KEY, strings.TrimPrefix(c.Path, "/")),
		c.GetParam(constant.GROUP_KEY, ""), c.GetParam(constant.VERSION_KEY, ""))
}
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()
}
func (c *URL) ColonSeparatedKey() string {
	intf := c.GetParam(constant.INTERFACE_KEY, strings.TrimPrefix(c.Path, "/"))
	if intf == "" {
		return ""
	}
	var buf strings.Builder
	buf.WriteString(intf)
	buf.WriteString(":")
	version := c.GetParam(constant.VERSION_KEY, "")
	if version != "" && version != "0.0.0" {
		buf.WriteString(version)
	}
	group := c.GetParam(constant.GROUP_KEY, "")
	buf.WriteString(":")
	if group != "" {
		buf.WriteString(group)
	}
	return buf.String()
}
func (c *URL) EncodedServiceKey() string {
	serviceKey := c.ServiceKey()
	return strings.Replace(serviceKey, "/", "*", 1)
}
func (c *URL) Service() string {
	service := c.GetParam(constant.INTERFACE_KEY, strings.TrimPrefix(c.Path, "/"))
	if service != "" {
		return service
	} else if c.SubURL != nil {
		service = c.SubURL.GetParam(constant.INTERFACE_KEY, strings.TrimPrefix(c.Path, "/"))
		if service != "" {  
			return service
		}
	}
	return ""
}