diff --git a/common/constant/key.go b/common/constant/key.go index 16986c7421345318d3477adca59de800b9d9c415..da21a3a9e1254d5a22d670a11c5c01022892e096 100644 --- a/common/constant/key.go +++ b/common/constant/key.go @@ -26,6 +26,7 @@ const ( VERSION_KEY = "version" INTERFACE_KEY = "interface" PATH_KEY = "path" + PROTOCOL_KEY = "protocol" SERVICE_KEY = "service" METHODS_KEY = "methods" TIMEOUT_KEY = "timeout" diff --git a/common/extension/metadata_report_factory.go b/common/extension/metadata_report_factory.go index 0ae0793bb4459767cb42fb1860fc484388aae1a3..89dab0409987d8959b5761771c8893abaf88673b 100644 --- a/common/extension/metadata_report_factory.go +++ b/common/extension/metadata_report_factory.go @@ -18,20 +18,20 @@ package extension import ( - "github.com/apache/dubbo-go/metadata" + "github.com/apache/dubbo-go/metadata/report/factory" ) var ( - metaDataReportFactories = make(map[string]func() metadata.MetadataReportFactory, 8) + metaDataReportFactories = make(map[string]func() factory.MetadataReportFactory, 8) ) // SetMetadataReportFactory ... -func SetMetadataReportFactory(name string, v func() metadata.MetadataReportFactory) { +func SetMetadataReportFactory(name string, v func() factory.MetadataReportFactory) { metaDataReportFactories[name] = v } // GetMetadataReportFactory ... -func GetMetadataReportFactory(name string) metadata.MetadataReportFactory { +func GetMetadataReportFactory(name string) factory.MetadataReportFactory { if metaDataReportFactories[name] == nil { panic("metadata report for " + name + " is not existing, make sure you have import the package.") } diff --git a/common/rpc_service.go b/common/rpc_service.go index ebd1d02f843bc339c3a37d977e2138798307475d..3a25d322391bf3ee52fabadd511677a52dcb169a 100644 --- a/common/rpc_service.go +++ b/common/rpc_service.go @@ -133,6 +133,11 @@ func (s *Service) Method() map[string]*MethodType { return s.methods } +// Name will return service name +func (s *Service) Name() string { + return s.name +} + // RcvrType ... func (s *Service) RcvrType() reflect.Type { return s.rcvrType diff --git a/config/base_config_test.go b/config/base_config_test.go index d16b2420922ece60ef2135729cd47d5aa73a3760..60eccfb1836dccbec0e8dc593a0954005117c28e 100644 --- a/config/base_config_test.go +++ b/config/base_config_test.go @@ -53,15 +53,6 @@ func Test_refresh(t *testing.T) { Owner: "dubbo", Environment: "test"}, Registries: map[string]*RegistryConfig{ - //"shanghai_reg1": { - // id: "shanghai_reg1", - // Protocol: "mock", - // TimeoutStr: "2s", - // Group: "shanghai_idc", - // Address: "127.0.0.1:2181", - // Username: "user1", - // Password: "pwd1", - //}, "shanghai_reg2": { Protocol: "mock", TimeoutStr: "2s", @@ -156,15 +147,6 @@ func Test_appExternal_refresh(t *testing.T) { Owner: "dubbo", Environment: "test"}, Registries: map[string]*RegistryConfig{ - //"shanghai_reg1": { - // id: "shanghai_reg1", - // Protocol: "mock", - // TimeoutStr: "2s", - // Group: "shanghai_idc", - // Address: "127.0.0.1:2181", - // Username: "user1", - // Password: "pwd1", - //}, "shanghai_reg2": { Protocol: "mock", TimeoutStr: "2s", @@ -251,15 +233,6 @@ func Test_appExternalWithoutId_refresh(t *testing.T) { Owner: "dubbo", Environment: "test"}, Registries: map[string]*RegistryConfig{ - //"shanghai_reg1": { - // id: "shanghai_reg1", - // Protocol: "mock", - // TimeoutStr: "2s", - // Group: "shanghai_idc", - // Address: "127.0.0.1:2181", - // Username: "user1", - // Password: "pwd1", - //}, "shanghai_reg2": { Protocol: "mock", TimeoutStr: "2s", @@ -408,15 +381,6 @@ func Test_refreshProvider(t *testing.T) { Owner: "dubbo", Environment: "test"}, Registries: map[string]*RegistryConfig{ - //"shanghai_reg1": { - // id: "shanghai_reg1", - // Protocol: "mock", - // TimeoutStr: "2s", - // Group: "shanghai_idc", - // Address: "127.0.0.1:2181", - // Username: "user1", - // Password: "pwd1", - //}, "shanghai_reg2": { Protocol: "mock", TimeoutStr: "2s", diff --git a/config/config_loader.go b/config/config_loader.go index 11107e83e1c6660c5265bdbe31851e2380dfdd00..6666409dd3f6e9eabb9b858e7691b44c3f9041bd 100644 --- a/config/config_loader.go +++ b/config/config_loader.go @@ -210,6 +210,7 @@ func Load() { } svs.id = key svs.Implement(rpcService) + svs.Protocols = providerConfig.Protocols if err := svs.Export(); err != nil { panic(fmt.Sprintf("service %s export failed! err: %#v", key, err)) } diff --git a/config/config_loader_test.go b/config/config_loader_test.go index 6368fcbd2c7bc675231e7b7835750f26743708af..0192b4c8a06263266cb80b344a0792ea2f6af8c8 100644 --- a/config/config_loader_test.go +++ b/config/config_loader_test.go @@ -24,6 +24,7 @@ import ( import ( "github.com/stretchr/testify/assert" + "go.uber.org/atomic" ) import ( @@ -90,7 +91,7 @@ func TestLoad(t *testing.T) { func TestLoadWithSingleReg(t *testing.T) { doInitConsumerWithSingleRegistry() - doInitProviderWithSingleRegistry() + mockInitProviderWithSingleRegistry() ms := &MockService{} SetConsumerService(ms) @@ -233,3 +234,55 @@ func TestConfigLoaderWithConfigCenterSingleRegistry(t *testing.T) { assert.Equal(t, "mock://127.0.0.1:2182", consumerConfig.Registries[constant.DEFAULT_KEY].Address) } + +// mockInitProviderWithSingleRegistry will init a mocked providerConfig +func mockInitProviderWithSingleRegistry() { + providerConfig = &ProviderConfig{ + ApplicationConfig: &ApplicationConfig{ + Organization: "dubbo_org", + Name: "dubbo", + Module: "module", + Version: "1.0.0", + Owner: "dubbo", + Environment: "test"}, + Registry: &RegistryConfig{ + Address: "mock://127.0.0.1:2181", + Username: "user1", + Password: "pwd1", + }, + Registries: map[string]*RegistryConfig{}, + Services: map[string]*ServiceConfig{ + "MockService": { + InterfaceName: "com.MockService", + Protocol: "mock", + Cluster: "failover", + Loadbalance: "random", + Retries: "3", + Group: "huadong_idc", + Version: "1.0.0", + Methods: []*MethodConfig{ + { + Name: "GetUser", + Retries: "2", + Loadbalance: "random", + Weight: 200, + }, + { + Name: "GetUser1", + Retries: "2", + Loadbalance: "random", + Weight: 200, + }, + }, + exported: new(atomic.Bool), + }, + }, + Protocols: map[string]*ProtocolConfig{ + "mock": { + Name: "mock", + Ip: "127.0.0.1", + Port: "20000", + }, + }, + } +} diff --git a/config/instance/metedata_report.go b/config/instance/metedata_report.go index cd54b0a7940df166c88f02234ab1a4e3bf384163..9cf435bc9debf09d0707d0a3b5d699c79d2cafa7 100644 --- a/config/instance/metedata_report.go +++ b/config/instance/metedata_report.go @@ -24,16 +24,16 @@ import ( import ( "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common/extension" - "github.com/apache/dubbo-go/metadata" + "github.com/apache/dubbo-go/metadata/report" ) var ( - instance metadata.MetadataReport + instance report.MetadataReport once sync.Once ) // GetMetadataReportInstance ... -func GetMetadataReportInstance(url *common.URL) metadata.MetadataReport { +func GetMetadataReportInstance(url *common.URL) report.MetadataReport { once.Do(func() { instance = extension.GetMetadataReportFactory(url.Protocol).CreateMetadataReport(url) }) diff --git a/config/service_config.go b/config/service_config.go index 50bf5e12c3247340f177a84c72446383ec5c3450..9cf60c8fb30f74e78f44e736604394fda779eb88 100644 --- a/config/service_config.go +++ b/config/service_config.go @@ -71,11 +71,15 @@ type ServiceConfig struct { ParamSign string `yaml:"param.sign" json:"param.sign,omitempty" property:"param.sign"` Tag string `yaml:"tag" json:"tag,omitempty" property:"tag"` + Protocols map[string]*ProtocolConfig unexported *atomic.Bool exported *atomic.Bool rpcService common.RPCService - cacheProtocol protocol.Protocol cacheMutex sync.Mutex + cacheProtocol protocol.Protocol + + exportersLock sync.Mutex + exporters []protocol.Exporter } // Prefix ... @@ -92,6 +96,8 @@ func (c *ServiceConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { if err := unmarshal((*plain)(c)); err != nil { return err } + c.exported = atomic.NewBool(false) + c.unexported = atomic.NewBool(false) return nil } @@ -105,6 +111,16 @@ func NewServiceConfig(id string, context context.Context) *ServiceConfig { } } +// InitExported will set exported as false atom bool +func (c *ServiceConfig) InitExported() { + c.exported = atomic.NewBool(false) +} + +// IsExport will return whether the service config is exported or not +func (c *ServiceConfig) IsExport() bool { + return c.exported.Load() +} + // Export ... func (c *ServiceConfig) Export() error { // TODO: config center start here @@ -122,7 +138,7 @@ func (c *ServiceConfig) Export() error { regUrls := loadRegistries(c.Registry, providerConfig.Registries, common.PROVIDER) urlMap := c.getUrlMap() - protocolConfigs := loadProtocol(c.Protocol, providerConfig.Protocols) + protocolConfigs := loadProtocol(c.Protocol, c.Protocols) if len(protocolConfigs) == 0 { logger.Warnf("The service %v's '%v' protocols don't has right protocolConfigs ", c.InterfaceName, c.Protocol) return nil @@ -148,6 +164,9 @@ func (c *ServiceConfig) Export() error { if len(c.Tag) > 0 { ivkURL.AddParam(constant.Tagkey, c.Tag) } + + var exporter protocol.Exporter + if len(regUrls) > 0 { for _, regUrl := range regUrls { regUrl.SubURL = ivkURL @@ -160,22 +179,46 @@ func (c *ServiceConfig) Export() error { c.cacheMutex.Unlock() invoker := extension.GetProxyFactory(providerConfig.ProxyFactory).GetInvoker(*regUrl) - exporter := c.cacheProtocol.Export(invoker) + exporter = c.cacheProtocol.Export(invoker) if exporter == nil { panic(perrors.New(fmt.Sprintf("Registry protocol new exporter error,registry is {%v},url is {%v}", regUrl, ivkURL))) } } } else { invoker := extension.GetProxyFactory(providerConfig.ProxyFactory).GetInvoker(*ivkURL) - exporter := extension.GetProtocol(protocolwrapper.FILTER).Export(invoker) + exporter = extension.GetProtocol(protocolwrapper.FILTER).Export(invoker) if exporter == nil { panic(perrors.New(fmt.Sprintf("Filter protocol without registry new exporter error,url is {%v}", ivkURL))) } } + c.exporters = append(c.exporters, exporter) } + c.exported.Store(true) return nil } +// Unexport will call unexport of all exporters service config exported +func (c *ServiceConfig) Unexport() { + if !c.exported.Load() { + return + } + if c.unexported.Load() { + return + } + + func() { + c.exportersLock.Lock() + defer c.exportersLock.Unlock() + for _, exporter := range c.exporters { + exporter.Unexport() + } + c.exporters = nil + }() + + c.exported.Store(false) + c.unexported.Store(true) +} + // Implement ... func (c *ServiceConfig) Implement(s common.RPCService) { c.rpcService = s @@ -245,3 +288,16 @@ func (c *ServiceConfig) getUrlMap() url.Values { return urlMap } + +// GetExportedUrls will return the url in service config's exporter +func (c *ServiceConfig) GetExportedUrls() []*common.URL { + if c.exported.Load() { + var urls []*common.URL + for _, exporter := range c.exporters { + url := exporter.GetInvoker().GetUrl() + urls = append(urls, &url) + } + return urls + } + return nil +} diff --git a/config/service_config_test.go b/config/service_config_test.go index 6f3230890348e77ea26c9c0eaf9165090c8cd09f..fb3b4a7dc20e32fbcaf6f9514991fba9a7af0d95 100644 --- a/config/service_config_test.go +++ b/config/service_config_test.go @@ -21,6 +21,10 @@ import ( "testing" ) +import ( + "go.uber.org/atomic" +) + import ( "github.com/apache/dubbo-go/common/extension" ) @@ -92,6 +96,7 @@ func doInitProvider() { Weight: 200, }, }, + exported: new(atomic.Bool), }, "MockServiceNoRightProtocol": { InterfaceName: "com.MockService", @@ -116,56 +121,7 @@ func doInitProvider() { Weight: 200, }, }, - }, - }, - Protocols: map[string]*ProtocolConfig{ - "mock": { - Name: "mock", - Ip: "127.0.0.1", - Port: "20000", - }, - }, - } -} - -func doInitProviderWithSingleRegistry() { - providerConfig = &ProviderConfig{ - ApplicationConfig: &ApplicationConfig{ - Organization: "dubbo_org", - Name: "dubbo", - Module: "module", - Version: "2.6.0", - Owner: "dubbo", - Environment: "test"}, - Registry: &RegistryConfig{ - Address: "mock://127.0.0.1:2181", - Username: "user1", - Password: "pwd1", - }, - Registries: map[string]*RegistryConfig{}, - Services: map[string]*ServiceConfig{ - "MockService": { - InterfaceName: "com.MockService", - Protocol: "mock", - Cluster: "failover", - Loadbalance: "random", - Retries: "3", - Group: "huadong_idc", - Version: "1.0.0", - Methods: []*MethodConfig{ - { - Name: "GetUser", - Retries: "2", - Loadbalance: "random", - Weight: 200, - }, - { - Name: "GetUser1", - Retries: "2", - Loadbalance: "random", - Weight: 200, - }, - }, + exported: new(atomic.Bool), }, }, Protocols: map[string]*ProtocolConfig{ diff --git a/go.mod b/go.mod index 54d532eac06a56057f815e3c8e91fdd267c9c6ad..fe1891ea6e70ee3f8c605243e4d464d4b22dc73f 100644 --- a/go.mod +++ b/go.mod @@ -1,7 +1,7 @@ module github.com/apache/dubbo-go require ( - github.com/Workiva/go-datastructures v1.0.50 + github.com/Workiva/go-datastructures v1.0.52 github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5 github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190802083043-4cd0c391755e // indirect github.com/apache/dubbo-go-hessian2 v1.4.0 diff --git a/go.sum b/go.sum index e499992eb0a0335ca0a1e1f746caca3418af7655..73c3da87a0bb784ef17fed34687a024cb18b7f22 100644 --- a/go.sum +++ b/go.sum @@ -27,8 +27,8 @@ github.com/SermoDigital/jose v0.0.0-20180104203859-803625baeddc h1:LkkwnbY+S8Wmw github.com/SermoDigital/jose v0.0.0-20180104203859-803625baeddc/go.mod h1:ARgCUhI1MHQH+ONky/PAtmVHQrP5JlGY0F3poXOp/fA= github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 h1:fLjPD/aNc3UIOA6tDi6QXUemppXK3P9BI7mr2hd6gx8= github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= -github.com/Workiva/go-datastructures v1.0.50 h1:slDmfW6KCHcC7U+LP3DDBbm4fqTwZGn1beOFPfGaLvo= -github.com/Workiva/go-datastructures v1.0.50/go.mod h1:Z+F2Rca0qCsVYDS8z7bAGm8f3UkzuWYS/oBZz5a7VVA= +github.com/Workiva/go-datastructures v1.0.52 h1:PLSK6pwn8mYdaoaCZEMsXBpBotr4HHn9abU0yMQt0NI= +github.com/Workiva/go-datastructures v1.0.52/go.mod h1:Z+F2Rca0qCsVYDS8z7bAGm8f3UkzuWYS/oBZz5a7VVA= github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af h1:DBNMBMuMiWYu0b+8KMJuWmfCkcxl09JwdlqwDZZ6U14= github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af/go.mod h1:5Jv4cbFiHJMsVxt52+i0Ha45fjshj6wxYr1r19tB9bw= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5 h1:rFw4nCn9iMW+Vajsk51NtYIcwSTkXr+JGrMd36kTDJw= @@ -111,10 +111,7 @@ github.com/dubbogo/getty v1.3.3/go.mod h1:U92BDyJ6sW9Jpohr2Vlz8w2uUbIbNZ3d+6rJvF github.com/dubbogo/go-zookeeper v1.0.0 h1:RsYdlGwhDW+iKXM3eIIcvt34P2swLdmQfuIJxsHlGoM= github.com/dubbogo/go-zookeeper v1.0.0/go.mod h1:fn6n2CAEer3novYgk9ULLwAjuV8/g4DdC2ENwRb6E+c= github.com/dubbogo/gost v1.5.1/go.mod h1:pPTjVyoJan3aPxBPNUX0ADkXjPibLo+/Ib0/fADXSG8= -github.com/dubbogo/gost v1.5.2 h1:ri/03971hdpnn3QeCU+4UZgnRNGDXLDGDucR/iozZm8= github.com/dubbogo/gost v1.5.2/go.mod h1:pPTjVyoJan3aPxBPNUX0ADkXjPibLo+/Ib0/fADXSG8= -github.com/dubbogo/gost v1.7.0 h1:lWNBIE2hk1Aj2be2uXkyRTpZG0RQZj0/xbXnkIq6EHE= -github.com/dubbogo/gost v1.7.0/go.mod h1:pPTjVyoJan3aPxBPNUX0ADkXjPibLo+/Ib0/fADXSG8= github.com/dubbogo/gost v1.8.0 h1:9ACbQe5OwMjqtinQcNJC5xp16kky27OsfSGw5L9A6vw= github.com/dubbogo/gost v1.8.0/go.mod h1:pPTjVyoJan3aPxBPNUX0ADkXjPibLo+/Ib0/fADXSG8= github.com/duosecurity/duo_api_golang v0.0.0-20190308151101-6c680f768e74 h1:2MIhn2R6oXQbgW5yHfS+d6YqyMfXiu2L55rFZC4UD/M= @@ -385,8 +382,6 @@ github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9 github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/nacos-group/nacos-sdk-go v0.0.0-20190723125407-0242d42e3dbb h1:lbmvw8r9W55w+aQgWn35W1nuleRIECMoqUrmwAOAvoI= -github.com/nacos-group/nacos-sdk-go v0.0.0-20190723125407-0242d42e3dbb/go.mod h1:CEkSvEpoveoYjA81m4HNeYQ0sge0LFGKSEqO3JKHllo= github.com/nacos-group/nacos-sdk-go v0.0.0-20191128082542-fe1b325b125c h1:WoCa3AvgQMVKNs+RIFlWPRgY9QVJwUxJDrGxHs0fcRo= github.com/nacos-group/nacos-sdk-go v0.0.0-20191128082542-fe1b325b125c/go.mod h1:CEkSvEpoveoYjA81m4HNeYQ0sge0LFGKSEqO3JKHllo= github.com/nicolai86/scaleway-sdk v1.10.2-0.20180628010248-798f60e20bb2 h1:BQ1HW7hr4IVovMwWg0E0PYcyW8CzqDcVmaew9cujU4s= diff --git a/metadata/definition/definition.go b/metadata/definition/definition.go index ead984345efde1ddd1d54b7599fd9d5584947ea2..8d4a584ee53c7c5ebbc4cc2222134ca9e957068f 100644 --- a/metadata/definition/definition.go +++ b/metadata/definition/definition.go @@ -17,6 +17,16 @@ package definition +import ( + "bytes" +) + +import ( + "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/common/constant" +) + +// ServiceDefinition is the describer of service definition type ServiceDefinition struct { CanonicalName string CodeSource string @@ -24,6 +34,7 @@ type ServiceDefinition struct { Types []TypeDefinition } +// MethodDefinition is the describer of method definition type MethodDefinition struct { Name string ParameterTypes []string @@ -31,6 +42,7 @@ type MethodDefinition struct { Parameters []TypeDefinition } +// TypeDefinition is the describer of type definition type TypeDefinition struct { Id string Type string @@ -39,3 +51,39 @@ type TypeDefinition struct { Properties map[string]TypeDefinition TypeBuilderName string } + +// BuildServiceDefinition can build service definition which will be used to describe a service +func BuildServiceDefinition(service common.Service, url common.URL) ServiceDefinition { + sd := ServiceDefinition{} + sd.CanonicalName = url.Service() + + for k, m := range service.Method() { + var paramTypes []string + for _, t := range m.ArgsType() { + paramTypes = append(paramTypes, t.Kind().String()) + } + methodD := MethodDefinition{ + Name: k, + ParameterTypes: paramTypes, + ReturnType: m.ReplyType().Kind().String(), + } + sd.Methods = append(sd.Methods, methodD) + } + + return sd +} + +// ServiceDescriperBuild: build the service key, format is `group/serviceName:version` which be same as URL's service key +func ServiceDescriperBuild(serviceName string, group string, version string) string { + buf := &bytes.Buffer{} + if group != "" { + buf.WriteString(group) + buf.WriteString(constant.PATH_SEPARATOR) + } + buf.WriteString(serviceName) + if version != "" && version != "0.0.0" { + buf.WriteString(constant.KEY_SEPARATOR) + buf.WriteString(version) + } + return buf.String() +} diff --git a/metadata/identifier/base_metadata_identifier.go b/metadata/identifier/base_metadata_identifier.go index a314671055be523844fd7d8f9589b8b6031632bc..64290c668f14277a5f2c8b9e7603ca50e7713fd6 100644 --- a/metadata/identifier/base_metadata_identifier.go +++ b/metadata/identifier/base_metadata_identifier.go @@ -25,19 +25,21 @@ import ( "github.com/apache/dubbo-go/common/constant" ) -type BaseMetadataIdentifier interface { - getFilePathKey(params ...string) string - getIdentifierKey(params ...string) string +// BaseMetadataIdentifier defined for describe the Metadata base identify +type IMetadataIdentifier interface { + GetFilePathKey() string + GetIdentifierKey() string } -type BaseServiceMetadataIdentifier struct { - serviceInterface string - version string - group string - side string +// BaseMetadataIdentifier is the base implement of BaseMetadataIdentifier interface +type BaseMetadataIdentifier struct { + ServiceInterface string + Version string + Group string + Side string } -// joinParams... +// joinParams will join the specified char in slice, and build as string func joinParams(joinChar string, params []string) string { var joinedStr string for _, param := range params { @@ -47,24 +49,24 @@ func joinParams(joinChar string, params []string) string { return joinedStr } -// getIdentifierKey... -func (mdi *BaseServiceMetadataIdentifier) getIdentifierKey(params ...string) string { - return mdi.serviceInterface + - constant.KEY_SEPARATOR + mdi.version + - constant.KEY_SEPARATOR + mdi.group + - constant.KEY_SEPARATOR + mdi.side + +// getIdentifierKey will return string format as service:Version:Group:Side:param1:param2... +func (mdi *BaseMetadataIdentifier) getIdentifierKey(params ...string) string { + return mdi.ServiceInterface + + constant.KEY_SEPARATOR + mdi.Version + + constant.KEY_SEPARATOR + mdi.Group + + constant.KEY_SEPARATOR + mdi.Side + joinParams(constant.KEY_SEPARATOR, params) } -// getFilePathKey... -func (mdi *BaseServiceMetadataIdentifier) getFilePathKey(params ...string) string { - path := serviceToPath(mdi.serviceInterface) +// getFilePathKey will return string format as metadata/path/Version/Group/Side/param1/param2... +func (mdi *BaseMetadataIdentifier) getFilePathKey(params ...string) string { + path := serviceToPath(mdi.ServiceInterface) return constant.DEFAULT_PATH_TAG + withPathSeparator(path) + - withPathSeparator(mdi.version) + - withPathSeparator(mdi.group) + - withPathSeparator(mdi.side) + + withPathSeparator(mdi.Version) + + withPathSeparator(mdi.Group) + + withPathSeparator(mdi.Side) + joinParams(constant.PATH_SEPARATOR, params) } diff --git a/metadata/service.go b/metadata/identifier/base_metadata_identifier_test.go similarity index 55% rename from metadata/service.go rename to metadata/identifier/base_metadata_identifier_test.go index d85703c95a57183d5c0a5b2445839e946dc6a59b..5b60992ab6132ecb306245af31bba7e3d0f09117 100644 --- a/metadata/service.go +++ b/metadata/identifier/base_metadata_identifier_test.go @@ -15,23 +15,27 @@ * limitations under the License. */ -package metadata +package identifier import ( - "github.com/apache/dubbo-go/common" - gxset "github.com/dubbogo/gost/container/set" + "testing" ) -type MetadataService interface { - ServiceName() string - ExportURL(url *common.URL) bool - UnexportURL(url *common.URL) bool - RefreshMetadata(exportedRevision string, subscribedRevision string) bool - SubscribeURL(url *common.URL) bool - UnsubscribeURL(url *common.URL) bool - PublishServiceDefinition(url *common.URL) +import ( + "github.com/stretchr/testify/assert" +) + +var baseId = &BaseMetadataIdentifier{ + ServiceInterface: "org.apache.pkg.mockService", + Version: "1.0.0", + Group: "Group", + Side: "provider", +} + +func TestBaseGetFilePathKey(t *testing.T) { + assert.Equal(t, "metadata/1.0.0/Group/provider/a/b/c", baseId.getFilePathKey("a", "b", "c")) +} - GetExportedURLs(serviceInterface string, group string, version string, protocol string) gxset.HashSet - GetServiceDefinition(interfaceName string, version string, group string) string - GetServiceDefinitionByServiceKey(serviceKey string) string +func TestBaseGetIdentifierKey(t *testing.T) { + assert.Equal(t, "org.apache.pkg.mockService:1.0.0:Group:provider:a:b:c", baseId.getIdentifierKey("a", "b", "c")) } diff --git a/metadata/identifier/metadata_identifier.go b/metadata/identifier/metadata_identifier.go index f3df8f36546093a826279c4e9ec1546f78d444bd..18b330ae083d55cf77330d19c144b2d4a6bde862 100644 --- a/metadata/identifier/metadata_identifier.go +++ b/metadata/identifier/metadata_identifier.go @@ -17,17 +17,18 @@ package identifier +// MetadataIdentifier is inherit baseMetaIdentifier with Application name type MetadataIdentifier struct { - application string + Application string BaseMetadataIdentifier } -// getIdentifierKey... -func (mdi *MetadataIdentifier) getIdentifierKey(params ...string) string { - return mdi.BaseMetadataIdentifier.getIdentifierKey(mdi.application) +// GetIdentifierKey will return string format as service:Version:Group:Side:Application +func (mdi *MetadataIdentifier) GetIdentifierKey() string { + return mdi.BaseMetadataIdentifier.getIdentifierKey(mdi.Application) } -// getIdentifierKey... -func (mdi *MetadataIdentifier) getFilePathKey(params ...string) string { - return mdi.BaseMetadataIdentifier.getFilePathKey(mdi.application) +// GetFilePathKey will return string format as metadata/path/Version/Group/Side/Application +func (mdi *MetadataIdentifier) GetFilePathKey() string { + return mdi.BaseMetadataIdentifier.getFilePathKey(mdi.Application) } diff --git a/metadata/identifier/metadata_identifier_test.go b/metadata/identifier/metadata_identifier_test.go new file mode 100644 index 0000000000000000000000000000000000000000..cba3c0dd76a01f2125b87db4478f99501bf2c284 --- /dev/null +++ b/metadata/identifier/metadata_identifier_test.go @@ -0,0 +1,44 @@ +/* + * 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 identifier + +import ( + "testing" +) + +import ( + "github.com/stretchr/testify/assert" +) + +var metadataId = &MetadataIdentifier{ + Application: "app", + BaseMetadataIdentifier: BaseMetadataIdentifier{ + ServiceInterface: "org.apache.pkg.mockService", + Version: "1.0.0", + Group: "Group", + Side: "provider", + }, +} + +func TestGetFilePathKey(t *testing.T) { + assert.Equal(t, "metadata/1.0.0/Group/provider/app", metadataId.GetFilePathKey()) +} + +func TestGetIdentifierKey(t *testing.T) { + assert.Equal(t, "org.apache.pkg.mockService:1.0.0:Group:provider:app", metadataId.GetIdentifierKey()) +} diff --git a/metadata/identifier/service_metadata_identifier.go b/metadata/identifier/service_metadata_identifier.go index 373df0130dd1f87e3175918bde50060c4be89616..92c15704db3bb873b3aff26297643119f8835f45 100644 --- a/metadata/identifier/service_metadata_identifier.go +++ b/metadata/identifier/service_metadata_identifier.go @@ -21,18 +21,19 @@ import ( "github.com/apache/dubbo-go/common/constant" ) +// ServiceMetadataIdentifier is inherit baseMetaIdentifier with service params: Revision and Protocol type ServiceMetadataIdentifier struct { - revision string - protocol string + Revision string + Protocol string BaseMetadataIdentifier } -// getIdentifierKey... -func (mdi *ServiceMetadataIdentifier) getIdentifierKey(params ...string) string { - return mdi.BaseMetadataIdentifier.getIdentifierKey(mdi.protocol + constant.KEY_REVISON_PREFIX + mdi.revision) +// GetIdentifierKey will return string format as service:Version:Group:Side:Protocol:"revision"+Revision +func (mdi *ServiceMetadataIdentifier) GetIdentifierKey() string { + return mdi.BaseMetadataIdentifier.getIdentifierKey(mdi.Protocol, constant.KEY_REVISON_PREFIX+mdi.Revision) } -// getIdentifierKey... -func (mdi *ServiceMetadataIdentifier) getFilePathKey(params ...string) string { - return mdi.BaseMetadataIdentifier.getFilePathKey(mdi.protocol + constant.KEY_REVISON_PREFIX + mdi.revision) +// GetFilePathKey will return string format as metadata/path/Version/Group/Side/Protocol/"revision"+Revision +func (mdi *ServiceMetadataIdentifier) GetFilePathKey() string { + return mdi.BaseMetadataIdentifier.getFilePathKey(mdi.Protocol, constant.KEY_REVISON_PREFIX+mdi.Revision) } diff --git a/metadata/identifier/service_metadata_identifier_test.go b/metadata/identifier/service_metadata_identifier_test.go new file mode 100644 index 0000000000000000000000000000000000000000..d7ef44a4bbc7611b6391122f8f5841db349eb036 --- /dev/null +++ b/metadata/identifier/service_metadata_identifier_test.go @@ -0,0 +1,45 @@ +/* + * 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 identifier + +import ( + "testing" +) + +import ( + "github.com/stretchr/testify/assert" +) + +var serviceMetadataId = &ServiceMetadataIdentifier{ + Revision: "1.0", + Protocol: "dubbo", + BaseMetadataIdentifier: BaseMetadataIdentifier{ + ServiceInterface: "org.apache.pkg.mockService", + Version: "1.0.0", + Group: "Group", + Side: "provider", + }, +} + +func TestServiceGetFilePathKey(t *testing.T) { + assert.Equal(t, "metadata/1.0.0/Group/provider/dubbo/revision1.0", serviceMetadataId.GetFilePathKey()) +} + +func TestServiceGetIdentifierKey(t *testing.T) { + assert.Equal(t, "org.apache.pkg.mockService:1.0.0:Group:provider:dubbo:revision1.0", serviceMetadataId.GetIdentifierKey()) +} diff --git a/metadata/identifier/subscribe_metadata_identifier.go b/metadata/identifier/subscribe_metadata_identifier.go index 321a216a3e3ad3f2390ab832782924a81e226160..e599fc9e0da1962d60d0bde2646eed552e26e95d 100644 --- a/metadata/identifier/subscribe_metadata_identifier.go +++ b/metadata/identifier/subscribe_metadata_identifier.go @@ -17,17 +17,18 @@ package identifier +// SubscriberMetadataIdentifier is inherit baseMetaIdentifier with service params: Revision type SubscriberMetadataIdentifier struct { - revision string + Revision string BaseMetadataIdentifier } -// getIdentifierKey... -func (mdi *SubscriberMetadataIdentifier) getIdentifierKey(params ...string) string { - return mdi.BaseMetadataIdentifier.getIdentifierKey(mdi.revision) +// GetIdentifierKey will return string format as service:Version:Group:Side:Revision +func (mdi *SubscriberMetadataIdentifier) GetIdentifierKey() string { + return mdi.BaseMetadataIdentifier.getIdentifierKey(mdi.Revision) } -// getIdentifierKey... -func (mdi *SubscriberMetadataIdentifier) getFilePathKey(params ...string) string { - return mdi.BaseMetadataIdentifier.getFilePathKey(mdi.revision) +// GetFilePathKey will return string format as metadata/path/Version/Group/Side/Revision +func (mdi *SubscriberMetadataIdentifier) GetFilePathKey() string { + return mdi.BaseMetadataIdentifier.getFilePathKey(mdi.Revision) } diff --git a/metadata/identifier/subscribe_metadata_identifier_test.go b/metadata/identifier/subscribe_metadata_identifier_test.go new file mode 100644 index 0000000000000000000000000000000000000000..9c9ef70641c52222a09475e97a2afbb604a467ff --- /dev/null +++ b/metadata/identifier/subscribe_metadata_identifier_test.go @@ -0,0 +1,44 @@ +/* + * 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 identifier + +import ( + "testing" +) + +import ( + "github.com/stretchr/testify/assert" +) + +var subscribeMetadataId = &SubscriberMetadataIdentifier{ + Revision: "1.0", + BaseMetadataIdentifier: BaseMetadataIdentifier{ + ServiceInterface: "org.apache.pkg.mockService", + Version: "1.0.0", + Group: "Group", + Side: "provider", + }, +} + +func TestSubscribeGetFilePathKey(t *testing.T) { + assert.Equal(t, "metadata/1.0.0/Group/provider/1.0", subscribeMetadataId.GetFilePathKey()) +} + +func TestSubscribeGetIdentifierKey(t *testing.T) { + assert.Equal(t, "org.apache.pkg.mockService:1.0.0:Group:provider:1.0", subscribeMetadataId.GetIdentifierKey()) +} diff --git a/metadata/namemapping/dynamic/service_name_mapping.go b/metadata/mapping/dynamic/service_name_mapping.go similarity index 97% rename from metadata/namemapping/dynamic/service_name_mapping.go rename to metadata/mapping/dynamic/service_name_mapping.go index e93c256fe093b4a3e3c431e1d012038b2bb7976b..4cfac8f82887d0b101beaf55ae9ca84c124d30b3 100644 --- a/metadata/namemapping/dynamic/service_name_mapping.go +++ b/metadata/mapping/dynamic/service_name_mapping.go @@ -31,7 +31,7 @@ import ( "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/config" "github.com/apache/dubbo-go/config_center" - "github.com/apache/dubbo-go/metadata" + "github.com/apache/dubbo-go/metadata/mapping" ) const ( @@ -77,6 +77,6 @@ func (d *DynamicConfigurationServiceNameMapping) buildGroup(serviceInterface str } // NewServiceNameMapping will create an instance of DynamicConfigurationServiceNameMapping -func NewServiceNameMapping(dc config_center.DynamicConfiguration) metadata.ServiceNameMapping { +func NewServiceNameMapping(dc config_center.DynamicConfiguration) mapping.ServiceNameMapping { return &DynamicConfigurationServiceNameMapping{dc: dc} } diff --git a/metadata/namemapping/dynamic/service_name_mapping_test.go b/metadata/mapping/dynamic/service_name_mapping_test.go similarity index 100% rename from metadata/namemapping/dynamic/service_name_mapping_test.go rename to metadata/mapping/dynamic/service_name_mapping_test.go diff --git a/metadata/namemapping/memory/service_name_mapping.go b/metadata/mapping/memory/service_name_mapping.go similarity index 100% rename from metadata/namemapping/memory/service_name_mapping.go rename to metadata/mapping/memory/service_name_mapping.go diff --git a/metadata/service_name_mapping.go b/metadata/mapping/service_name_mapping.go similarity index 98% rename from metadata/service_name_mapping.go rename to metadata/mapping/service_name_mapping.go index c14e8ce2e7c40d1573897dfd6ba64c16e18acac7..6caed9f0b48c1bb9c2f0f1026eb642f69bb31113 100644 --- a/metadata/service_name_mapping.go +++ b/metadata/mapping/service_name_mapping.go @@ -15,7 +15,7 @@ * limitations under the License. */ -package metadata +package mapping import ( gxset "github.com/dubbogo/gost/container/set" diff --git a/metadata/report_factory.go b/metadata/report/factory/report_factory.go similarity index 76% rename from metadata/report_factory.go rename to metadata/report/factory/report_factory.go index 19b1004eee57073acec13c7f114179c47c73f145..8769ebdd2fd1f088415232bd4463d02f7ebd730f 100644 --- a/metadata/report_factory.go +++ b/metadata/report/factory/report_factory.go @@ -15,16 +15,21 @@ * limitations under the License. */ -package metadata +package factory import ( "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/metadata/report" ) var ( - MetadataReportInstance MetadataReport + MetadataReportInstance report.MetadataReport ) +// MetadataReportFactory interface will create metadata report type MetadataReportFactory interface { - CreateMetadataReport(*common.URL) MetadataReport + CreateMetadataReport(*common.URL) report.MetadataReport +} + +type BaseMetadataReportFactory struct { } diff --git a/metadata/report.go b/metadata/report/report.go similarity index 90% rename from metadata/report.go rename to metadata/report/report.go index 3fcc71241411d4a8f9577bb5fb3233e67942cd52..81227e0c765b61df7edc8a5d025b9cd1921d1113 100644 --- a/metadata/report.go +++ b/metadata/report/report.go @@ -15,7 +15,7 @@ * limitations under the License. */ -package metadata +package report import ( "github.com/apache/dubbo-go/common" @@ -23,9 +23,10 @@ import ( "github.com/apache/dubbo-go/metadata/identifier" ) +// MetadataReport is an interface of remote metadata report type MetadataReport interface { StoreProviderMetadata(*identifier.MetadataIdentifier, *definition.ServiceDefinition) - StoreConsumeretadata(*identifier.MetadataIdentifier, map[string]string) + StoreConsumerMetadata(*identifier.MetadataIdentifier, map[string]string) SaveServiceMetadata(*identifier.ServiceMetadataIdentifier, *common.URL) RemoveServiceMetadata(*identifier.ServiceMetadataIdentifier) GetExportedURLs(*identifier.ServiceMetadataIdentifier) []string diff --git a/metadata/service/exporter/configurable/exporter.go b/metadata/service/exporter/configurable/exporter.go new file mode 100644 index 0000000000000000000000000000000000000000..3d12e0ecd4def4b9d99f346a4f556fc3d781d1b2 --- /dev/null +++ b/metadata/service/exporter/configurable/exporter.go @@ -0,0 +1,103 @@ +/* + * 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 configurable + +import ( + "context" + "sync" +) + +import ( + "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/common/constant" + "github.com/apache/dubbo-go/common/logger" + "github.com/apache/dubbo-go/config" + "github.com/apache/dubbo-go/metadata/service" + "github.com/apache/dubbo-go/metadata/service/exporter" +) + +// MetadataServiceExporter is the ConfigurableMetadataServiceExporter which implement MetadataServiceExporter interface +type MetadataServiceExporter struct { + serviceConfig *config.ServiceConfig + lock sync.RWMutex + metadataService service.MetadataService +} + +// NewMetadataServiceExporter will return a service_exporter.MetadataServiceExporter with the specified metadata service +func NewMetadataServiceExporter(metadataService service.MetadataService) exporter.MetadataServiceExporter { + return &MetadataServiceExporter{ + metadataService: metadataService, + } +} + +// Export will export the metadataService +func (exporter *MetadataServiceExporter) Export() error { + if !exporter.IsExported() { + + serviceConfig := config.NewServiceConfig("MetadataService", context.Background()) + serviceConfig.Protocol = constant.DEFAULT_PROTOCOL + serviceConfig.Protocols = map[string]*config.ProtocolConfig{ + constant.DEFAULT_PROTOCOL: generateMetadataProtocol(), + } + serviceConfig.InterfaceName = constant.METADATA_SERVICE_NAME + serviceConfig.Group = config.GetApplicationConfig().Name + serviceConfig.Version = exporter.metadataService.Version() + + var err error + func() { + exporter.lock.Lock() + defer exporter.lock.Unlock() + exporter.serviceConfig = serviceConfig + exporter.serviceConfig.Implement(exporter.metadataService) + err = exporter.serviceConfig.Export() + }() + + logger.Infof("The MetadataService exports urls : %v ", exporter.serviceConfig.GetExportedUrls()) + return err + } + logger.Warnf("The MetadataService has been exported : %v ", exporter.serviceConfig.GetExportedUrls()) + return nil +} + +// Unexport will unexport the metadataService +func (exporter *MetadataServiceExporter) Unexport() { + if exporter.IsExported() { + exporter.serviceConfig.Unexport() + } +} + +// GetExportedURLs will return the urls that export use. +// Notice锛乀he exported url is not same as url in registry , for example it lack the ip. +func (exporter *MetadataServiceExporter) GetExportedURLs() []*common.URL { + return exporter.serviceConfig.GetExportedUrls() +} + +// isExported will return is metadataServiceExporter exported or not +func (exporter *MetadataServiceExporter) IsExported() bool { + exporter.lock.RLock() + defer exporter.lock.RUnlock() + return exporter.serviceConfig != nil && exporter.serviceConfig.IsExport() +} + +// generateMetadataProtocol will return a default ProtocolConfig +func generateMetadataProtocol() *config.ProtocolConfig { + return &config.ProtocolConfig{ + Name: constant.DEFAULT_PROTOCOL, + Port: "20000", + } +} diff --git a/metadata/service/exporter/configurable/exporter_test.go b/metadata/service/exporter/configurable/exporter_test.go new file mode 100644 index 0000000000000000000000000000000000000000..220ef71daca47f46bdcd4b88b215970399a5da31 --- /dev/null +++ b/metadata/service/exporter/configurable/exporter_test.go @@ -0,0 +1,117 @@ +/* + * 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 configurable + +import ( + "testing" +) + +import ( + "github.com/stretchr/testify/assert" +) + +import ( + _ "github.com/apache/dubbo-go/common/proxy/proxy_factory" + "github.com/apache/dubbo-go/config" + _ "github.com/apache/dubbo-go/filter/filter_impl" + "github.com/apache/dubbo-go/metadata/service/inmemory" + "github.com/apache/dubbo-go/protocol/dubbo" + _ "github.com/apache/dubbo-go/protocol/dubbo" +) + +func TestConfigurableExporter(t *testing.T) { + dubbo.SetServerConfig(dubbo.ServerConfig{ + SessionNumber: 700, + SessionTimeout: "20s", + GettySessionParam: dubbo.GettySessionParam{ + CompressEncoding: false, + TcpNoDelay: true, + TcpKeepAlive: true, + KeepAlivePeriod: "120s", + TcpRBufSize: 262144, + TcpWBufSize: 65536, + PkgWQSize: 512, + TcpReadTimeout: "1s", + TcpWriteTimeout: "5s", + WaitTimeout: "1s", + MaxMsgLen: 10240000000, + SessionName: "server", + }}) + mockInitProviderWithSingleRegistry() + metadataService := inmemory.NewMetadataService() + exported := NewMetadataServiceExporter(metadataService) + assert.Equal(t, false, exported.IsExported()) + assert.NoError(t, exported.Export()) + assert.Equal(t, true, exported.IsExported()) + assert.Regexp(t, "dubbo://:20000/MetadataService*", exported.GetExportedURLs()[0].String()) + exported.Unexport() + assert.Equal(t, false, exported.IsExported()) +} + +// mockInitProviderWithSingleRegistry will init a mocked providerConfig +func mockInitProviderWithSingleRegistry() { + providerConfig := &config.ProviderConfig{ + ApplicationConfig: &config.ApplicationConfig{ + Organization: "dubbo_org", + Name: "dubbo", + Module: "module", + Version: "1.0.0", + Owner: "dubbo", + Environment: "test"}, + Registry: &config.RegistryConfig{ + Address: "mock://127.0.0.1:2181", + Username: "user1", + Password: "pwd1", + }, + Registries: map[string]*config.RegistryConfig{}, + Services: map[string]*config.ServiceConfig{ + "MockService": { + InterfaceName: "com.MockService", + Protocol: "mock", + Cluster: "failover", + Loadbalance: "random", + Retries: "3", + Group: "huadong_idc", + Version: "1.0.0", + Methods: []*config.MethodConfig{ + { + Name: "GetUser", + Retries: "2", + Loadbalance: "random", + Weight: 200, + }, + { + Name: "GetUser1", + Retries: "2", + Loadbalance: "random", + Weight: 200, + }, + }, + }, + }, + Protocols: map[string]*config.ProtocolConfig{ + "mock": { + Name: "mock", + Ip: "127.0.0.1", + Port: "20000", + }, + }, + } + providerConfig.Services["MockService"].InitExported() + config.SetProviderConfig(*providerConfig) +} diff --git a/metadata/exporter.go b/metadata/service/exporter/exporter.go similarity index 81% rename from metadata/exporter.go rename to metadata/service/exporter/exporter.go index 5d47f8bd808ec802ba73c7db73d22c78c675d12a..cfdef3a0e79d29ce31717c0fc3c575e9e4ba1759 100644 --- a/metadata/exporter.go +++ b/metadata/service/exporter/exporter.go @@ -15,15 +15,16 @@ * limitations under the License. */ -package metadata +package exporter import ( "github.com/apache/dubbo-go/common" ) -type MetadataExporter interface { - Export() MetadataExporter - Unexport() MetadataExporter +// MetadataServiceExporter will export & unexport the metadata service, get exported url, and return is exported or not +type MetadataServiceExporter interface { + Export() error + Unexport() GetExportedURLs() []*common.URL IsExported() bool } diff --git a/metadata/service/inmemory/service.go b/metadata/service/inmemory/service.go new file mode 100644 index 0000000000000000000000000000000000000000..c59949401f419b44ce155a914a7afff7c327a8fe --- /dev/null +++ b/metadata/service/inmemory/service.go @@ -0,0 +1,232 @@ +/* + * 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 inmemory + +import ( + "encoding/json" + "sync" +) + +import ( + cm "github.com/Workiva/go-datastructures/common" + "github.com/Workiva/go-datastructures/slice/skip" +) + +import ( + "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/common/constant" + "github.com/apache/dubbo-go/common/logger" + "github.com/apache/dubbo-go/metadata/definition" + "github.com/apache/dubbo-go/metadata/service" +) + +// MetadataService is store and query the metadata info in memory when each service registry +type MetadataService struct { + service.BaseMetadataService + exportedServiceURLs *sync.Map + subscribedServiceURLs *sync.Map + serviceDefinitions *sync.Map + lock *sync.RWMutex +} + +// NewMetadataService: initiate a metadata service +func NewMetadataService() *MetadataService { + return &MetadataService{ + exportedServiceURLs: &sync.Map{}, + subscribedServiceURLs: &sync.Map{}, + serviceDefinitions: &sync.Map{}, + lock: &sync.RWMutex{}, + } +} + +// comparator is defined as Comparator for skip list to compare the URL +type comparator common.URL + +// Compare is defined as Comparator for skip list to compare the URL +func (c comparator) Compare(comp cm.Comparator) int { + a := common.URL(c).String() + b := common.URL(comp.(comparator)).String() + switch { + case a > b: + return 1 + case a < b: + return -1 + default: + return 0 + } +} + +// addURL will add URL in memory +func (mts *MetadataService) addURL(targetMap *sync.Map, url *common.URL) bool { + var ( + urlSet interface{} + loaded bool + ) + logger.Debug(url.ServiceKey()) + if urlSet, loaded = targetMap.LoadOrStore(url.ServiceKey(), skip.New(uint64(0))); loaded { + mts.lock.RLock() + wantedUrl := urlSet.(*skip.SkipList).Get(comparator(*url)) + if len(wantedUrl) > 0 && wantedUrl[0] != nil { + mts.lock.RUnlock() + return false + } + mts.lock.RUnlock() + } + mts.lock.Lock() + //double chk + wantedUrl := urlSet.(*skip.SkipList).Get(comparator(*url)) + if len(wantedUrl) > 0 && wantedUrl[0] != nil { + mts.lock.Unlock() + return false + } + urlSet.(*skip.SkipList).Insert(comparator(*url)) + mts.lock.Unlock() + return true +} + +// removeURL is used to remove specified url +func (mts *MetadataService) removeURL(targetMap *sync.Map, url *common.URL) { + if value, loaded := targetMap.Load(url.ServiceKey()); loaded { + mts.lock.Lock() + value.(*skip.SkipList).Delete(comparator(*url)) + mts.lock.Unlock() + mts.lock.RLock() + defer mts.lock.RUnlock() + if value.(*skip.SkipList).Len() == 0 { + targetMap.Delete(url.ServiceKey()) + } + } +} + +// getAllService can return all the exportedUrlString except for metadataService +func (mts *MetadataService) getAllService(services *sync.Map) *skip.SkipList { + skipList := skip.New(uint64(0)) + services.Range(func(key, value interface{}) bool { + urls := value.(*skip.SkipList) + for i := uint64(0); i < urls.Len(); i++ { + url := common.URL(urls.ByPosition(i).(comparator)) + if url.GetParam(constant.INTERFACE_KEY, url.Path) != "MetadataService" { + skipList.Insert(comparator(url)) + } + } + return true + }) + return skipList +} + +// getSpecifiedService can return specified service url by serviceKey +func (mts *MetadataService) getSpecifiedService(services *sync.Map, serviceKey string, protocol string) *skip.SkipList { + skipList := skip.New(uint64(0)) + serviceList, loaded := services.Load(serviceKey) + if loaded { + urls := serviceList.(*skip.SkipList) + for i := uint64(0); i < urls.Len(); i++ { + url := common.URL(urls.ByPosition(i).(comparator)) + if len(protocol) == 0 || url.Protocol == protocol || url.GetParam(constant.PROTOCOL_KEY, "") == protocol { + skipList.Insert(comparator(url)) + } + } + } + return skipList +} + +// ExportURL can store the in memory +func (mts *MetadataService) ExportURL(url common.URL) (bool, error) { + return mts.addURL(mts.exportedServiceURLs, &url), nil +} + +// UnexportURL can remove the url store in memory +func (mts *MetadataService) UnexportURL(url common.URL) error { + mts.removeURL(mts.exportedServiceURLs, &url) + return nil +} + +// SubscribeURL can store the in memory +func (mts *MetadataService) SubscribeURL(url common.URL) (bool, error) { + return mts.addURL(mts.subscribedServiceURLs, &url), nil +} + +// UnsubscribeURL can remove the url store in memory +func (mts *MetadataService) UnsubscribeURL(url common.URL) error { + mts.removeURL(mts.subscribedServiceURLs, &url) + return nil +} + +// PublishServiceDefinition: publish url's service metadata info, and write into memory +func (mts *MetadataService) PublishServiceDefinition(url common.URL) error { + interfaceName := url.GetParam(constant.INTERFACE_KEY, "") + isGeneric := url.GetParamBool(constant.GENERIC_KEY, false) + if len(interfaceName) > 0 && !isGeneric { + //judge is consumer or provider + //side := url.GetParam(constant.SIDE_KEY, "") + //var service common.RPCService + service := common.ServiceMap.GetService(url.Protocol, url.GetParam(constant.BEAN_NAME_KEY, url.Service())) + //if side == common.RoleType(common.CONSUMER).Role() { + // //TODO:generate the service definition and store it + // + //} else if side == common.RoleType(common.PROVIDER).Role() { + // //TODO:generate the service definition and store it + //} + sd := definition.BuildServiceDefinition(*service, url) + data, err := json.Marshal(sd) + if err != nil { + logger.Errorf("publishProvider getServiceDescriptor error. providerUrl:%v , error: ", url, err) + } + mts.serviceDefinitions.Store(url.ServiceKey(), string(data)) + return nil + } + logger.Errorf("publishProvider interfaceName is empty . providerUrl:%v ", url) + return nil +} + +// GetExportedURLs get all exported urls +func (mts *MetadataService) GetExportedURLs(serviceInterface string, group string, version string, protocol string) (*skip.SkipList, error) { + if serviceInterface == constant.ANY_VALUE { + return mts.getAllService(mts.exportedServiceURLs), nil + } else { + serviceKey := definition.ServiceDescriperBuild(serviceInterface, group, version) + return mts.getSpecifiedService(mts.exportedServiceURLs, serviceKey, protocol), nil + } +} + +// GetSubscribedURLs get all subscribedUrl +func (mts *MetadataService) GetSubscribedURLs() (*skip.SkipList, error) { + return mts.getAllService(mts.subscribedServiceURLs), nil +} + +// GetServiceDefinition can get service definition by interfaceName, group and version +func (mts *MetadataService) GetServiceDefinition(interfaceName string, group string, version string) (string, error) { + serviceKey := definition.ServiceDescriperBuild(interfaceName, group, version) + v, _ := mts.serviceDefinitions.Load(serviceKey) + return v.(string), nil +} + +// GetServiceDefinition can get service definition by serviceKey +func (mts *MetadataService) GetServiceDefinitionByServiceKey(serviceKey string) (string, error) { + v, _ := mts.serviceDefinitions.Load(serviceKey) + return v.(string), nil +} + +// Version will return the version of metadata service +func (mts *MetadataService) Version() string { + return "1.0.0" +} + +// Version will return the version of metadata service +func (mts *MetadataService) Reference() string { + return "MetadataService" +} diff --git a/metadata/service/inmemory/service_test.go b/metadata/service/inmemory/service_test.go new file mode 100644 index 0000000000000000000000000000000000000000..9e593db282e7f4fa55d52c49129a15a9b389c67f --- /dev/null +++ b/metadata/service/inmemory/service_test.go @@ -0,0 +1,125 @@ +/* + * 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 inmemory + +import ( + "context" + "fmt" + "testing" + "time" +) + +import ( + "github.com/stretchr/testify/assert" +) + +import ( + "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/metadata/definition" +) + +type User struct { + Id string + Name string + Age int32 + Time time.Time +} + +type UserProvider struct { +} + +func (u *UserProvider) GetUser(ctx context.Context, req []interface{}) (*User, error) { + rsp := User{"A001", "Alex Stocks", 18, time.Now()} + return &rsp, nil +} + +func (u *UserProvider) Reference() string { + return "UserProvider" +} + +func (u User) JavaClassName() string { + return "com.ikurento.user.User" +} + +func TestMetadataService(t *testing.T) { + mts := NewMetadataService() + serviceName := "com.ikurento.user.UserProvider" + group := "group1" + version := "0.0.1" + protocol := "dubbo" + beanName := "UserProvider" + + u2, err := common.NewURL(fmt.Sprintf( + "%v://127.0.0.1:20000/com.ikurento.user.UserProvider2?anyhost=true&"+ + "application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&"+ + "environment=dev&interface=%v&ip=192.168.56.1&methods=GetUser&module=dubbogo+user-info+server&org=ikurento.com&"+ + "owner=ZX&pid=1447&revision=0.0.1&side=provider&timeout=3000×tamp=1556509797245&group=%v&version=%v&bean.name=%v", + protocol, serviceName, group, version, beanName)) + assert.NoError(t, err) + mts.ExportURL(u2) + + u3, err := common.NewURL(fmt.Sprintf( + "%v://127.0.0.1:20000/com.ikurento.user.UserProvider3?anyhost=true&"+ + "application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&"+ + "environment=dev&interface=%v&ip=192.168.56.1&methods=GetUser&module=dubbogo+user-info+server&org=ikurento.com&"+ + "owner=ZX&pid=1447&revision=0.0.1&side=provider&timeout=3000×tamp=1556509797245&group=%v&version=%v&bean.name=%v", + protocol, serviceName, group, version, beanName)) + assert.NoError(t, err) + mts.ExportURL(u3) + + u, err := common.NewURL(fmt.Sprintf( + "%v://127.0.0.1:20000/com.ikurento.user.UserProvider1?anyhost=true&"+ + "application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&"+ + "environment=dev&interface=%v&ip=192.168.56.1&methods=GetUser&module=dubbogo+user-info+server&org=ikurento.com&"+ + "owner=ZX&pid=1447&revision=0.0.1&side=provider&timeout=3000×tamp=1556509797245&group=%v&version=%v&bean.name=%v", + protocol, serviceName, group, version, beanName)) + assert.NoError(t, err) + mts.ExportURL(u) + list, _ := mts.GetExportedURLs(serviceName, group, version, protocol) + assert.Equal(t, uint64(3), list.Len()) + iter := list.IterAtPosition(0) + for iter.Next() { + comparator := iter.Value() + fmt.Println(comparator) + } + mts.SubscribeURL(u) + + mts.SubscribeURL(u) + list2, _ := mts.GetSubscribedURLs() + assert.Equal(t, uint64(1), list2.Len()) + + mts.UnexportURL(u) + list3, _ := mts.GetExportedURLs(serviceName, group, version, protocol) + assert.Equal(t, uint64(2), list3.Len()) + + mts.UnsubscribeURL(u) + list4, _ := mts.GetSubscribedURLs() + assert.Equal(t, uint64(0), list4.Len()) + + userProvider := &UserProvider{} + common.ServiceMap.Register(serviceName, protocol, userProvider) + mts.PublishServiceDefinition(u) + expected := "{\"CanonicalName\":\"com.ikurento.user.UserProvider\",\"CodeSource\":\"\"," + + "\"Methods\":[{\"Name\":\"GetUser\",\"ParameterTypes\":[\"slice\"],\"ReturnType\":\"ptr\"," + + "\"Parameters\":null}],\"Types\":null}" + def1, _ := mts.GetServiceDefinition(serviceName, group, version) + assert.Equal(t, expected, def1) + serviceKey := definition.ServiceDescriperBuild(serviceName, group, version) + def2, _ := mts.GetServiceDefinitionByServiceKey(serviceKey) + assert.Equal(t, expected, def2) +} diff --git a/metadata/service/service.go b/metadata/service/service.go new file mode 100644 index 0000000000000000000000000000000000000000..bc526c5411383f0d5cee971cef4f84d6f4f48f59 --- /dev/null +++ b/metadata/service/service.go @@ -0,0 +1,63 @@ +/* + * 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 service + +import ( + "github.com/Workiva/go-datastructures/slice/skip" +) + +import ( + "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/config" +) + +// Metadataservice is used to define meta data related behaviors +type MetadataService interface { + common.RPCService + // ServiceName will get the service's name in meta service , which is application name + ServiceName() (string, error) + // ExportURL will store the exported url in metadata + ExportURL(url common.URL) (bool, error) + // UnexportURL will delete the exported url in metadata + UnexportURL(url common.URL) error + // SubscribeURL will store the subscribed url in metadata + SubscribeURL(url common.URL) (bool, error) + // UnsubscribeURL will delete the subscribed url in metadata + UnsubscribeURL(url common.URL) error + // PublishServiceDefinition will generate the target url's code info + PublishServiceDefinition(url common.URL) error + // GetExportedURLs will get the target exported url in metadata + GetExportedURLs(serviceInterface string, group string, version string, protocol string) (*skip.SkipList, error) + // GetExportedURLs will get the target subscribed url in metadata + GetSubscribedURLs() (*skip.SkipList, error) + // GetServiceDefinition will get the target service info store in metadata + GetServiceDefinition(interfaceName string, group string, version string) (string, error) + // GetServiceDefinition will get the target service info store in metadata by service key + GetServiceDefinitionByServiceKey(serviceKey string) (string, error) + // Version will return the metadata service version + Version() string +} + +// BaseMetadataService is used for the common logic for struct who will implement interface MetadataService +type BaseMetadataService struct { +} + +// ServiceName can get the service's name in meta service , which is application name +func (mts *BaseMetadataService) ServiceName() (string, error) { + return config.GetApplicationConfig().Name, nil +}