diff --git a/common/constant/key.go b/common/constant/key.go index 5769ccfe6c8d2e296de190259d85821dd92dd8f4..27b77879b35b2d85c572853949cb26f40a2325ca 100644 --- a/common/constant/key.go +++ b/common/constant/key.go @@ -215,6 +215,9 @@ const ( KEY_SEPARATOR = ":" DEFAULT_PATH_TAG = "metadata" KEY_REVISON_PREFIX = "revision" + + // metadata service + METADATA_SERVICE_NAME = "org.apache.dubbo.metadata.MetadataService" ) // HealthCheck Router diff --git a/config_center/apollo/impl.go b/config_center/apollo/impl.go index bc8538f2e745874f357d6444704cb7e9c3711ef5..9045c2864933fcfc79d5be32d160324805605595 100644 --- a/config_center/apollo/impl.go +++ b/config_center/apollo/impl.go @@ -25,6 +25,7 @@ import ( ) import ( + gxset "github.com/dubbogo/gost/container/set" perrors "github.com/pkg/errors" "github.com/zouyx/agollo" ) @@ -133,6 +134,11 @@ func (c *apolloConfiguration) PublishConfig(string, string, string) error { return perrors.New("unsupport operation") } +// GetConfigKeysByGroup will return all keys with the group +func (c *apolloConfiguration) GetConfigKeysByGroup(group string) (*gxset.HashSet, error) { + return nil, perrors.New("unsupport operation") +} + func (c *apolloConfiguration) GetProperties(key string, opts ...cc.Option) (string, error) { /** * when group is not null, we are getting startup configs(config file) from Config Center, for example: diff --git a/config_center/dynamic_configuration.go b/config_center/dynamic_configuration.go index e643e011fb0dfabef163153745b71afba31d5a7c..9013d7140e757520f2e8f048ce53a5ac2a13f982 100644 --- a/config_center/dynamic_configuration.go +++ b/config_center/dynamic_configuration.go @@ -21,14 +21,18 @@ import ( "time" ) +import ( + gxset "github.com/dubbogo/gost/container/set" +) + import ( "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/config_center/parser" ) -////////////////////////////////////////// +// //////////////////////////////////////// // DynamicConfiguration -////////////////////////////////////////// +// //////////////////////////////////////// const ( // DEFAULT_GROUP: default group DEFAULT_GROUP = "dubbo" @@ -42,17 +46,20 @@ type DynamicConfiguration interface { SetParser(parser.ConfigurationParser) AddListener(string, ConfigurationListener, ...Option) RemoveListener(string, ConfigurationListener, ...Option) - //GetProperties get properties file + // GetProperties get properties file GetProperties(string, ...Option) (string, error) - //GetRule get Router rule properties file + // GetRule get Router rule properties file GetRule(string, ...Option) (string, error) - //GetInternalProperty get value by key in Default properties file(dubbo.properties) + // GetInternalProperty get value by key in Default properties file(dubbo.properties) GetInternalProperty(string, ...Option) (string, error) // PublishConfig will publish the config with the (key, group, value) pair PublishConfig(string, string, string) error + + // GetConfigKeysByGroup will return all keys with the group + GetConfigKeysByGroup(group string) (*gxset.HashSet, error) } // Options ... @@ -78,7 +85,7 @@ func WithTimeout(time time.Duration) Option { } } -//GetRuleKey The format is '{interfaceName}:[version]:[group]' +// GetRuleKey The format is '{interfaceName}:[version]:[group]' func GetRuleKey(url common.URL) string { return url.ColonSeparatedKey() } diff --git a/config_center/mock_dynamic_config.go b/config_center/mock_dynamic_config.go index 9f5bbe934c2a7c2a8a30ba42d024cacedc92d084..59c788b65bce2a4773975ea1a96a314649781832 100644 --- a/config_center/mock_dynamic_config.go +++ b/config_center/mock_dynamic_config.go @@ -22,6 +22,7 @@ import ( ) import ( + gxset "github.com/dubbogo/gost/container/set" "gopkg.in/yaml.v2" ) @@ -81,10 +82,16 @@ func (f *MockDynamicConfigurationFactory) GetDynamicConfiguration(_ *common.URL) } +// PublishConfig will publish the config with the (key, group, value) pair func (c *MockDynamicConfiguration) PublishConfig(string, string, string) error { return nil } +// GetConfigKeysByGroup will return all keys with the group +func (c *MockDynamicConfiguration) GetConfigKeysByGroup(group string) (*gxset.HashSet, error) { + return gxset.NewSet(c.content), nil +} + // MockDynamicConfiguration ... type MockDynamicConfiguration struct { parser parser.ConfigurationParser diff --git a/config_center/nacos/impl.go b/config_center/nacos/impl.go index 54523cae711ec59f97577f6ea4ab501e17884743..007b8be142274b63ceb56dd00399cdaf29c3746d 100644 --- a/config_center/nacos/impl.go +++ b/config_center/nacos/impl.go @@ -23,6 +23,7 @@ import ( ) import ( + gxset "github.com/dubbogo/gost/container/set" "github.com/nacos-group/nacos-sdk-go/vo" perrors "github.com/pkg/errors" ) @@ -105,6 +106,13 @@ func (n *nacosDynamicConfiguration) PublishConfig(key string, group string, valu return nil } +// GetConfigKeysByGroup will return all keys with the group +func (n *nacosDynamicConfiguration) GetConfigKeysByGroup(group string) (*gxset.HashSet, error) { + // TODO (the golang client of nacos does not support batch API) + // we should build a issue and then think about how to resolve this problem + return nil, perrors.New("unsupport operation, wait for implement") +} + // GetRule Get router rule func (n *nacosDynamicConfiguration) GetRule(key string, opts ...config_center.Option) (string, error) { tmpOpts := &config_center.Options{} diff --git a/config_center/zookeeper/impl.go b/config_center/zookeeper/impl.go index fc87eaee5faac8a510bf19f072a53baebeb377a4..937c56eef0b29f6fee52717f7a43c37ec35c7e95 100644 --- a/config_center/zookeeper/impl.go +++ b/config_center/zookeeper/impl.go @@ -25,6 +25,7 @@ import ( import ( "github.com/dubbogo/go-zookeeper/zk" + gxset "github.com/dubbogo/gost/container/set" perrors "github.com/pkg/errors" ) @@ -39,7 +40,7 @@ import ( const ( // ZkClient - //zookeeper client name + // zookeeper client name ZkClient = "zk config_center" pathSeparator = "/" ) @@ -159,6 +160,24 @@ func (c *zookeeperDynamicConfiguration) PublishConfig(key string, group string, return nil } +// GetConfigKeysByGroup will return all keys with the group +func (c *zookeeperDynamicConfiguration) GetConfigKeysByGroup(group string) (*gxset.HashSet, error) { + path := c.getPath("", group) + result, err := c.client.GetChildren(path) + if err != nil { + return nil, perrors.WithStack(err) + } + + if len(result) == 0 { + return nil, perrors.New("could not find keys with group: " + group) + } + set := gxset.NewSet() + for _, e := range result { + set.Add(e) + } + return set, nil +} + func (c *zookeeperDynamicConfiguration) GetRule(key string, opts ...config_center.Option) (string, error) { return c.GetProperties(key, opts...) } @@ -234,7 +253,7 @@ func (c *zookeeperDynamicConfiguration) getPath(key string, group string) string } func (c *zookeeperDynamicConfiguration) buildPath(group string) string { - if len(group) <= 0 { + if len(group) == 0 { group = config_center.DEFAULT_GROUP } return c.rootPath + pathSeparator + group diff --git a/config_center/zookeeper/impl_test.go b/config_center/zookeeper/impl_test.go index e6349d4c3de4614aef4ebdefcb76f773d9418eb2..30389122a3a06ee260f2ed8b21057523137995d5 100644 --- a/config_center/zookeeper/impl_test.go +++ b/config_center/zookeeper/impl_test.go @@ -24,6 +24,7 @@ import ( import ( "github.com/dubbogo/go-zookeeper/zk" + gxset "github.com/dubbogo/gost/container/set" "github.com/stretchr/testify/assert" ) @@ -159,13 +160,21 @@ func Test_RemoveListener(t *testing.T) { func TestZookeeperDynamicConfiguration_PublishConfig(t *testing.T) { value := "Test Data" customGroup := "Custom Group" + key := "myKey" ts, zk := initZkData(config_center.DEFAULT_GROUP, t) defer ts.Stop() - err := zk.PublishConfig("myKey", customGroup, value) + err := zk.PublishConfig(key, customGroup, value) assert.Nil(t, err) result, err := zk.GetInternalProperty("myKey", config_center.WithGroup(customGroup)) assert.Nil(t, err) assert.Equal(t, value, result) + + var keys *gxset.HashSet + keys, err = zk.GetConfigKeysByGroup(customGroup) + assert.Nil(t, err) + assert.Equal(t, 1, keys.Size()) + assert.True(t, keys.Contains(key)) + } type mockDataListener struct { diff --git a/metadata/namemapping/dynamic/service_name_mapping.go b/metadata/namemapping/dynamic/service_name_mapping.go new file mode 100644 index 0000000000000000000000000000000000000000..e93c256fe093b4a3e3c431e1d012038b2bb7976b --- /dev/null +++ b/metadata/namemapping/dynamic/service_name_mapping.go @@ -0,0 +1,82 @@ +/* + * 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 dynamic + +import ( + "strconv" + "time" +) + +import ( + "github.com/dubbogo/gost/container/set" + perrors "github.com/pkg/errors" +) + +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" +) + +const ( + defaultGroup = config_center.DEFAULT_GROUP + slash = "/" +) + +// DynamicConfigurationServiceNameMapping is the implementation based on config center +type DynamicConfigurationServiceNameMapping struct { + dc config_center.DynamicConfiguration +} + +// Map will map the service to this application-level service +func (d *DynamicConfigurationServiceNameMapping) Map(serviceInterface string, group string, version string, protocol string) error { + // metadata service is admin service, should not be mapped + if constant.METADATA_SERVICE_NAME == serviceInterface { + return perrors.New("try to map the metadata service, will be ignored") + } + + appName := config.GetApplicationConfig().Name + value := time.Now().UnixNano() + + err := d.dc.PublishConfig(appName, + d.buildGroup(serviceInterface), + strconv.FormatInt(value, 10)) + if err != nil { + return perrors.WithStack(err) + } + return nil +} + +// Get will return the application-level services. If not found, the empty set will be returned. +// if the dynamic configuration got error, the error will return +func (d *DynamicConfigurationServiceNameMapping) Get(serviceInterface string, group string, version string, protocol string) (*gxset.HashSet, error) { + return d.dc.GetConfigKeysByGroup(d.buildGroup(serviceInterface)) +} + +// buildGroup will return group, now it looks like defaultGroup/serviceInterface +func (d *DynamicConfigurationServiceNameMapping) buildGroup(serviceInterface string) string { + // the issue : https://github.com/apache/dubbo/issues/4671 + // so other params are ignored and remove, including group string, version string, protocol string + return defaultGroup + slash + serviceInterface +} + +// NewServiceNameMapping will create an instance of DynamicConfigurationServiceNameMapping +func NewServiceNameMapping(dc config_center.DynamicConfiguration) metadata.ServiceNameMapping { + return &DynamicConfigurationServiceNameMapping{dc: dc} +} diff --git a/metadata/namemapping/dynamic/service_name_mapping_test.go b/metadata/namemapping/dynamic/service_name_mapping_test.go new file mode 100644 index 0000000000000000000000000000000000000000..e3d620cd738421c256d8fd232b1afcfd425ca989 --- /dev/null +++ b/metadata/namemapping/dynamic/service_name_mapping_test.go @@ -0,0 +1,61 @@ +/* + * 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 dynamic + +import ( + "testing" +) + +import ( + gxset "github.com/dubbogo/gost/container/set" + "github.com/stretchr/testify/assert" +) + +import ( + "github.com/apache/dubbo-go/common/constant" + "github.com/apache/dubbo-go/config" + "github.com/apache/dubbo-go/config_center" +) + +func TestDynamicConfigurationServiceNameMapping(t *testing.T) { + + // mock data + appName := "myApp" + dc, err := (&config_center.MockDynamicConfigurationFactory{ + Content: appName, + }).GetDynamicConfiguration(nil) + config.GetApplicationConfig().Name = appName + + mapping := NewServiceNameMapping(dc) + intf := constant.METADATA_SERVICE_NAME + group := "myGroup" + version := "myVersion" + protocol := "myProtocol" + + err = mapping.Map(intf, group, version, protocol) + assert.NotNil(t, err) + intf = "MyService" + err = mapping.Map(intf, group, version, protocol) + assert.Nil(t, err) + + var result *gxset.HashSet + result, err = mapping.Get(intf, group, version, protocol) + assert.Nil(t, err) + assert.Equal(t, 1, result.Size()) + assert.True(t, result.Contains(appName)) +} diff --git a/metadata/namemapping/memory/service_name_mapping.go b/metadata/namemapping/memory/service_name_mapping.go new file mode 100644 index 0000000000000000000000000000000000000000..8a891491bdb97808b77422092a1043c1c0ffafbf --- /dev/null +++ b/metadata/namemapping/memory/service_name_mapping.go @@ -0,0 +1,36 @@ +/* + * 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 memory + +import ( + gxset "github.com/dubbogo/gost/container/set" +) + +import ( + "github.com/apache/dubbo-go/config" +) + +type InMemoryServiceNameMapping struct{} + +func (i InMemoryServiceNameMapping) Map(serviceInterface string, group string, version string, protocol string) error { + return nil +} + +func (i InMemoryServiceNameMapping) Get(serviceInterface string, group string, version string, protocol string) (*gxset.HashSet, error) { + return gxset.NewSet(config.GetApplicationConfig().Name), nil +} diff --git a/metadata/service_name_mapping.go b/metadata/service_name_mapping.go new file mode 100644 index 0000000000000000000000000000000000000000..c14e8ce2e7c40d1573897dfd6ba64c16e18acac7 --- /dev/null +++ b/metadata/service_name_mapping.go @@ -0,0 +1,32 @@ +/* + * 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 metadata + +import ( + gxset "github.com/dubbogo/gost/container/set" +) + +// ServiceNameMapping try to build the mapping between application-level service and interface-level service. +type ServiceNameMapping interface { + + // Map will map the service to this application-level service + Map(serviceInterface string, group string, version string, protocol string) error + + // Get will return the application-level services + Get(serviceInterface string, group string, version string, protocol string) (*gxset.HashSet, error) +}