diff --git a/config_center/apollo/impl.go b/config_center/apollo/impl.go index 4dc19817846fe5c9c0552738f2058a15d20efabc..bc8538f2e745874f357d6444704cb7e9c3711ef5 100644 --- a/config_center/apollo/impl.go +++ b/config_center/apollo/impl.go @@ -25,7 +25,7 @@ import ( ) import ( - "github.com/pkg/errors" + perrors "github.com/pkg/errors" "github.com/zouyx/agollo" ) @@ -119,7 +119,7 @@ func getNamespaceName(namespace string, configFileFormat agollo.ConfigFileFormat func (c *apolloConfiguration) GetInternalProperty(key string, opts ...cc.Option) (string, error) { config := agollo.GetConfig(c.appConf.NamespaceName) if config == nil { - return "", errors.New(fmt.Sprintf("nothing in namespace:%s ", key)) + return "", perrors.New(fmt.Sprintf("nothing in namespace:%s ", key)) } return config.GetStringValue(key, ""), nil } @@ -128,6 +128,11 @@ func (c *apolloConfiguration) GetRule(key string, opts ...cc.Option) (string, er return c.GetInternalProperty(key, opts...) } +// PublishConfig will publish the config with the (key, group, value) pair +func (c *apolloConfiguration) PublishConfig(string, string, string) error { + return 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: @@ -135,7 +140,7 @@ func (c *apolloConfiguration) GetProperties(key string, opts ...cc.Option) (stri */ config := agollo.GetConfig(key) if config == nil { - return "", errors.New(fmt.Sprintf("nothing in namespace:%s ", key)) + return "", perrors.New(fmt.Sprintf("nothing in namespace:%s ", key)) } return config.GetContent(agollo.Properties), nil } diff --git a/config_center/dynamic_configuration.go b/config_center/dynamic_configuration.go index d6c3b06b327f16c709b09121e589db6694d3663e..e643e011fb0dfabef163153745b71afba31d5a7c 100644 --- a/config_center/dynamic_configuration.go +++ b/config_center/dynamic_configuration.go @@ -50,6 +50,9 @@ type DynamicConfiguration interface { //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 } // Options ... diff --git a/config_center/mock_dynamic_config.go b/config_center/mock_dynamic_config.go index 4d972b629abb7abd7cc0d0018026e4ccc04a1e4f..9f5bbe934c2a7c2a8a30ba42d024cacedc92d084 100644 --- a/config_center/mock_dynamic_config.go +++ b/config_center/mock_dynamic_config.go @@ -81,6 +81,10 @@ func (f *MockDynamicConfigurationFactory) GetDynamicConfiguration(_ *common.URL) } +func (c *MockDynamicConfiguration) PublishConfig(string, string, string) error { + return nil +} + // MockDynamicConfiguration ... type MockDynamicConfiguration struct { parser parser.ConfigurationParser diff --git a/config_center/nacos/impl.go b/config_center/nacos/impl.go index 60ab89b003ff62016b9137223425c1051356975f..54523cae711ec59f97577f6ea4ab501e17884743 100644 --- a/config_center/nacos/impl.go +++ b/config_center/nacos/impl.go @@ -18,6 +18,7 @@ package nacos import ( + "strings" "sync" ) @@ -74,7 +75,7 @@ func (n *nacosDynamicConfiguration) RemoveListener(key string, listener config_c n.removeListener(key, listener) } -//nacos distinguishes configuration files based on group and dataId. defalut group = "dubbo" and dataId = key +// GetProperties nacos distinguishes configuration files based on group and dataId. defalut group = "dubbo" and dataId = key func (n *nacosDynamicConfiguration) GetProperties(key string, opts ...config_center.Option) (string, error) { return n.GetRule(key, opts...) } @@ -84,6 +85,26 @@ func (n *nacosDynamicConfiguration) GetInternalProperty(key string, opts ...conf return n.GetProperties(key, opts...) } +// PublishConfig will publish the config with the (key, group, value) pair +func (n *nacosDynamicConfiguration) PublishConfig(key string, group string, value string) error { + + group = n.resolvedGroup(group) + + ok, err := (*n.client.Client()).PublishConfig(vo.ConfigParam{ + DataId: key, + Group: group, + Content: value, + }) + + if err != nil { + return perrors.WithStack(err) + } + if !ok { + return perrors.New("publish config to Nocos failed") + } + return nil +} + // GetRule Get router rule func (n *nacosDynamicConfiguration) GetRule(key string, opts ...config_center.Option) (string, error) { tmpOpts := &config_center.Options{} @@ -92,12 +113,12 @@ func (n *nacosDynamicConfiguration) GetRule(key string, opts ...config_center.Op } content, err := (*n.client.Client()).GetConfig(vo.ConfigParam{ DataId: key, - Group: tmpOpts.Group, + Group: n.resolvedGroup(tmpOpts.Group), }) if err != nil { return "", perrors.WithStack(err) } else { - return string(content), nil + return content, nil } } @@ -145,6 +166,15 @@ func (n *nacosDynamicConfiguration) Destroy() { n.closeConfigs() } +// resolvedGroup will regular the group. Now, it will replace the '/' with '-'. +// '/' is a special character for nacos +func (n *nacosDynamicConfiguration) resolvedGroup(group string) string { + if len(group) <= 0 { + return group + } + return strings.ReplaceAll(group, "/", "-") +} + // IsAvailable Get available status func (n *nacosDynamicConfiguration) IsAvailable() bool { select { @@ -155,12 +185,12 @@ func (n *nacosDynamicConfiguration) IsAvailable() bool { } } -func (r *nacosDynamicConfiguration) closeConfigs() { - r.cltLock.Lock() - client := r.client - r.client = nil - r.cltLock.Unlock() +func (n *nacosDynamicConfiguration) closeConfigs() { + n.cltLock.Lock() + client := n.client + n.client = nil + n.cltLock.Unlock() // Close the old client first to close the tmp node client.Close() - logger.Infof("begin to close provider nacos client") + logger.Infof("begin to close provider n client") } diff --git a/config_center/nacos/impl_test.go b/config_center/nacos/impl_test.go index b4e6f1d0259979eba28dd81e8f480ab4ae03a39f..4032c91cda512b649140db6eea3dc11eeb482f27 100644 --- a/config_center/nacos/impl_test.go +++ b/config_center/nacos/impl_test.go @@ -60,12 +60,7 @@ func runMockConfigServer(configHandler func(http.ResponseWriter, *http.Request), func mockCommonNacosServer() *httptest.Server { return runMockConfigServer(func(writer http.ResponseWriter, request *http.Request) { - data := ` - dubbo.service.com.ikurento.user.UserProvider.cluster=failback - dubbo.service.com.ikurento.user.UserProvider.protocol=myDubbo1 - dubbo.protocols.myDubbo.port=20000 - dubbo.protocols.myDubbo.name=dubbo -` + data := "true" fmt.Fprintf(writer, "%s", data) }, func(writer http.ResponseWriter, request *http.Request) { data := `dubbo.properties%02dubbo%02dubbo.service.com.ikurento.user.UserProvider.cluster=failback` @@ -93,6 +88,16 @@ func Test_GetConfig(t *testing.T) { assert.NoError(t, err) } +func TestNacosDynamicConfiguration_PublishConfig(t *testing.T) { + nacos, err := initNacosData(t) + assert.Nil(t, err) + key := "myKey" + group := "/custom/a/b" + value := "MyValue" + err = nacos.PublishConfig(key, group, value) + assert.Nil(t, err) +} + func Test_AddListener(t *testing.T) { nacos, err := initNacosData(t) assert.NoError(t, err) diff --git a/config_center/nacos/listener.go b/config_center/nacos/listener.go index 25c586586c7202e42ff44d6104e8132961add25a..de74cff8f64683a47278825b670352a04b69b791 100644 --- a/config_center/nacos/listener.go +++ b/config_center/nacos/listener.go @@ -35,11 +35,11 @@ func callback(listener config_center.ConfigurationListener, namespace, group, da listener.Process(&config_center.ConfigChangeEvent{Key: dataId, Value: data, ConfigType: remoting.EventTypeUpdate}) } -func (l *nacosDynamicConfiguration) addListener(key string, listener config_center.ConfigurationListener) { - _, loaded := l.keyListeners.Load(key) +func (n *nacosDynamicConfiguration) addListener(key string, listener config_center.ConfigurationListener) { + _, loaded := n.keyListeners.Load(key) if !loaded { _, cancel := context.WithCancel(context.Background()) - err := (*l.client.Client()).ListenConfig(vo.ConfigParam{ + err := (*n.client.Client()).ListenConfig(vo.ConfigParam{ DataId: key, Group: "dubbo", OnChange: func(namespace, group, dataId, data string) { @@ -49,14 +49,14 @@ func (l *nacosDynamicConfiguration) addListener(key string, listener config_cent logger.Errorf("nacos : listen config fail, error:%v ", err) newListener := make(map[config_center.ConfigurationListener]context.CancelFunc) newListener[listener] = cancel - l.keyListeners.Store(key, newListener) + n.keyListeners.Store(key, newListener) } else { // TODO check goroutine alive, but this version of go_nacos_sdk is not support. logger.Infof("profile:%s. this profile is already listening", key) } } -func (l *nacosDynamicConfiguration) removeListener(key string, listener config_center.ConfigurationListener) { +func (n *nacosDynamicConfiguration) removeListener(key string, listener config_center.ConfigurationListener) { // TODO: not supported in current go_nacos_sdk version logger.Warn("not supported in current go_nacos_sdk version") } diff --git a/config_center/zookeeper/impl.go b/config_center/zookeeper/impl.go index 404243d4751146d1edc9a61d51cbb81d73c2ffb1..fc87eaee5faac8a510bf19f072a53baebeb377a4 100644 --- a/config_center/zookeeper/impl.go +++ b/config_center/zookeeper/impl.go @@ -40,7 +40,8 @@ import ( const ( // ZkClient //zookeeper client name - ZkClient = "zk config_center" + ZkClient = "zk config_center" + pathSeparator = "/" ) type zookeeperDynamicConfiguration struct { @@ -143,11 +144,21 @@ func (c *zookeeperDynamicConfiguration) GetProperties(key string, opts ...config return string(content), nil } -//For zookeeper, getConfig and getConfigs have the same meaning. +// GetInternalProperty For zookeeper, getConfig and getConfigs have the same meaning. func (c *zookeeperDynamicConfiguration) GetInternalProperty(key string, opts ...config_center.Option) (string, error) { return c.GetProperties(key, opts...) } +// PublishConfig will put the value into Zk with specific path +func (c *zookeeperDynamicConfiguration) PublishConfig(key string, group string, value string) error { + path := c.getPath(key, group) + err := c.client.CreateWithValue(path, []byte(value)) + if err != nil { + return perrors.WithStack(err) + } + return nil +} + func (c *zookeeperDynamicConfiguration) GetRule(key string, opts ...config_center.Option) (string, error) { return c.GetProperties(key, opts...) } @@ -214,3 +225,17 @@ func (c *zookeeperDynamicConfiguration) closeConfigs() { func (c *zookeeperDynamicConfiguration) RestartCallBack() bool { return true } + +func (c *zookeeperDynamicConfiguration) getPath(key string, group string) string { + if len(key) == 0 { + return c.buildPath(group) + } + return c.buildPath(group) + pathSeparator + key +} + +func (c *zookeeperDynamicConfiguration) buildPath(group string) string { + 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 22e15193cba1b533a2b1b965a44bf9665a6a4e5e..e6349d4c3de4614aef4ebdefcb76f773d9418eb2 100644 --- a/config_center/zookeeper/impl_test.go +++ b/config_center/zookeeper/impl_test.go @@ -156,6 +156,18 @@ func Test_RemoveListener(t *testing.T) { assert.Equal(t, "", listener.event) } +func TestZookeeperDynamicConfiguration_PublishConfig(t *testing.T) { + value := "Test Data" + customGroup := "Custom Group" + ts, zk := initZkData(config_center.DEFAULT_GROUP, t) + defer ts.Stop() + err := zk.PublishConfig("myKey", customGroup, value) + assert.Nil(t, err) + result, err := zk.GetInternalProperty("myKey", config_center.WithGroup(customGroup)) + assert.Nil(t, err) + assert.Equal(t, value, result) +} + type mockDataListener struct { wg sync.WaitGroup event string diff --git a/remoting/zookeeper/client.go b/remoting/zookeeper/client.go index 21486aab59c3f9b44c25b68d7433f864a990149a..7dac6146fa2aab88771ef5b7cb0205736e6478a9 100644 --- a/remoting/zookeeper/client.go +++ b/remoting/zookeeper/client.go @@ -392,8 +392,16 @@ func (z *ZookeeperClient) Close() { logger.Warnf("zkClient{name:%s, zk addr:%s} exit now.", z.name, z.ZkAddrs) } -// Create ... +// Create will create the node recursively, which means that if the parent node is absent, +// it will create parent node first. +// And the value for the basePath is "" func (z *ZookeeperClient) Create(basePath string) error { + return z.CreateWithValue(basePath, []byte("")) +} + +// CreateWithValue will create the node recursively, which means that if the parent node is absent, +// it will create parent node first. +func (z *ZookeeperClient) CreateWithValue(basePath string, value []byte) error { var ( err error tmpPath string @@ -407,7 +415,7 @@ func (z *ZookeeperClient) Create(basePath string) error { conn := z.Conn z.Unlock() if conn != nil { - _, err = conn.Create(tmpPath, []byte(""), 0, zk.WorldACL(zk.PermAll)) + _, err = conn.Create(tmpPath, value, 0, zk.WorldACL(zk.PermAll)) } if err != nil {