metadata/info/metadata_info.go (239 lines of code) (raw):
/*
* 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 info
import (
"fmt"
"hash/crc32"
"net/url"
"sort"
"strconv"
"strings"
)
import (
hessian "github.com/apache/dubbo-go-hessian2"
gxset "github.com/dubbogo/gost/container/set"
)
import (
"dubbo.apache.org/dubbo-go/v3/common"
"dubbo.apache.org/dubbo-go/v3/common/constant"
)
func init() {
hessian.RegisterPOJO(&MetadataInfo{})
hessian.RegisterPOJO(&ServiceInfo{})
}
var IncludeKeys = gxset.NewSet(
constant.ApplicationKey,
constant.GroupKey,
constant.TimestampKey,
constant.SerializationKey,
constant.ClusterKey,
constant.LoadbalanceKey,
constant.PathKey,
constant.TimeoutKey,
constant.TokenKey,
constant.VersionKey,
constant.WarmupKey,
constant.WeightKey,
constant.ReleaseKey)
// MetadataInfo the metadata information of instance
type MetadataInfo struct {
App string `json:"app,omitempty" hessian:"app"`
Revision string `json:"revision,omitempty" hessian:"revision"`
Tag string
Services map[string]*ServiceInfo `json:"services,omitempty" hessian:"services"`
exportedServiceURLs map[string][]*common.URL `hessian:"-"` // server exported service urls
subscribedServiceURLs map[string][]*common.URL `hessian:"-"` // client subscribed service urls
}
func NewAppMetadataInfo(app string) *MetadataInfo {
return NewMetadataInfo(app, "")
}
func NewMetadataInfo(app, tag string) *MetadataInfo {
return &MetadataInfo{
App: app,
Tag: tag,
Services: make(map[string]*ServiceInfo),
exportedServiceURLs: make(map[string][]*common.URL),
subscribedServiceURLs: make(map[string][]*common.URL),
}
}
func NewMetadataInfoWithParams(app string, revision string, services map[string]*ServiceInfo) *MetadataInfo {
return &MetadataInfo{
App: app,
Revision: revision,
Services: services,
exportedServiceURLs: make(map[string][]*common.URL),
subscribedServiceURLs: make(map[string][]*common.URL),
}
}
func (info *MetadataInfo) JavaClassName() string {
return "org.apache.dubbo.metadata.MetadataInfo"
}
// CalAndGetRevision is different from Dubbo because golang doesn't support overload
// so that we should use interface + method name as identifier and ignore the method params
// in my opinion, it's enough because Dubbo actually ignore the URL params.
// please refer org.apache.dubbo.common.URL#toParameterString(java.lang.String...)
func (info *MetadataInfo) CalAndGetRevision() string {
if info.Revision != "" {
return info.Revision
}
if len(info.Services) == 0 {
return "0"
}
candidates := make([]string, 0, 8)
for _, s := range info.Services {
iface := s.URL.GetParam(constant.InterfaceKey, "")
ms := s.URL.Methods
if len(ms) == 0 {
candidates = append(candidates, iface)
} else {
for _, m := range ms {
// methods are part of candidates
candidates = append(candidates, iface+constant.KeySeparator+m)
}
}
// append URL params if we need it
}
sort.Strings(candidates)
// it's nearly impossible to be overflow
res := uint64(0)
for _, c := range candidates {
res += uint64(crc32.ChecksumIEEE([]byte(c)))
}
info.Revision = fmt.Sprint(res)
return info.Revision
}
// AddService add provider service info to MetadataInfo
func (info *MetadataInfo) AddService(url *common.URL) {
service := NewServiceInfoWithURL(url)
info.Services[service.GetMatchKey()] = service
addUrl(info.exportedServiceURLs, url)
if info.App == "" {
info.App = url.GetParam(constant.ApplicationKey, "")
}
}
func addUrl(m map[string][]*common.URL, url *common.URL) {
if _, ok := m[url.ServiceKey()]; !ok {
m[url.ServiceKey()] = make([]*common.URL, 0)
}
m[url.ServiceKey()] = append(m[url.ServiceKey()], url)
}
func removeUrl(m map[string][]*common.URL, url *common.URL) {
if urls, ok := m[url.ServiceKey()]; ok {
for i, u := range urls {
if u == url {
m[url.ServiceKey()] = deleteItem(urls, i)
break
}
}
if len(m[url.ServiceKey()]) == 0 {
delete(m, url.ServiceKey())
}
}
}
func deleteItem(slice []*common.URL, index int) []*common.URL {
copy(slice[index:], slice[index+1:])
slice = slice[:len(slice)-1]
return slice
}
func (info *MetadataInfo) RemoveService(url *common.URL) {
service := NewServiceInfoWithURL(url)
delete(info.Services, service.GetMatchKey())
removeUrl(info.exportedServiceURLs, url)
}
// AddSubscribeURL client subscribe a service url
func (info *MetadataInfo) AddSubscribeURL(url *common.URL) {
addUrl(info.subscribedServiceURLs, url)
}
// RemoveSubscribeURL client unsubscribe a service url
func (info *MetadataInfo) RemoveSubscribeURL(url *common.URL) {
removeUrl(info.subscribedServiceURLs, url)
}
func (info *MetadataInfo) GetExportedServiceURLs() []*common.URL {
res := make([]*common.URL, 0)
for _, urls := range info.exportedServiceURLs {
res = append(res, urls...)
}
return res
}
func (info *MetadataInfo) GetSubscribedURLs() []*common.URL {
res := make([]*common.URL, 0)
for _, urls := range info.subscribedServiceURLs {
res = append(res, urls...)
}
return res
}
// ServiceInfo the information of service
type ServiceInfo struct {
Name string `json:"name,omitempty" hessian:"name"`
Group string `json:"group,omitempty" hessian:"group"`
Version string `json:"version,omitempty" hessian:"version"`
Protocol string `json:"protocol,omitempty" hessian:"protocol"`
Port int `json:"port,omitempty" hessian:"port"`
Path string `json:"path,omitempty" hessian:"path"`
Params map[string]string `json:"params,omitempty" hessian:"params"`
ServiceKey string `json:"-" hessian:"-"`
MatchKey string `json:"-" hessian:"-"`
URL *common.URL `json:"-" hessian:"-"`
}
func NewServiceInfoWithURL(url *common.URL) *ServiceInfo {
service := NewServiceInfo(url.Service(), url.Group(), url.Version(), url.Protocol, url.Path, nil)
service.Port, _ = strconv.Atoi(url.Port)
service.URL = url
// TODO includeKeys load dynamic
p := make(map[string]string, 8)
for _, keyInter := range IncludeKeys.Values() {
key := keyInter.(string)
value := url.GetParam(key, "")
if len(value) != 0 {
p[key] = value
}
for _, method := range url.Methods {
value = url.GetMethodParam(method, key, "")
if len(value) != 0 {
p[method+"."+key] = value
}
}
}
p[constant.MethodsKey] = strings.Join(url.Methods, ",")
service.Params = p
return service
}
func NewServiceInfo(name, group, version, protocol, path string, params map[string]string) *ServiceInfo {
serviceKey := common.ServiceKey(name, group, version)
matchKey := common.MatchKey(serviceKey, protocol)
return &ServiceInfo{
Name: name,
Group: group,
Version: version,
Protocol: protocol,
Path: strings.TrimPrefix(path, "/"),
Params: params,
ServiceKey: serviceKey,
MatchKey: matchKey,
}
}
func (si *ServiceInfo) JavaClassName() string {
return "org.apache.dubbo.metadata.MetadataInfo$ServiceInfo"
}
func (si *ServiceInfo) GetMethods() []string {
s := si.Params[constant.MethodsKey]
return strings.Split(s, ",")
}
func (si *ServiceInfo) GetParams() url.Values {
v := url.Values{}
methods := gxset.NewSet()
if methodNames, ok := si.Params[constant.MethodsKey]; ok {
for _, method := range strings.Split(methodNames, ",") {
methods.Add(method)
}
}
for k, p := range si.Params {
ms := strings.Index(k, ".")
if ms > 0 && methods.Contains(k[0:ms]) {
v.Set("methods."+k, p)
} else {
v.Set(k, p)
}
}
return v
}
func (si *ServiceInfo) GetMatchKey() string {
if si.MatchKey != "" {
return si.MatchKey
}
serviceKey := si.GetServiceKey()
si.MatchKey = common.MatchKey(serviceKey, si.Protocol)
return si.MatchKey
}
func (si *ServiceInfo) GetServiceKey() string {
if si.ServiceKey != "" {
return si.ServiceKey
}
si.ServiceKey = common.ServiceKey(si.Name, si.Group, si.Version)
return si.ServiceKey
}