diff --git a/config_center/apollo/impl.go b/config_center/apollo/impl.go index 4dc19817846fe5c9c0552738f2058a15d20efabc..d9c9ad2c301810148a0931c3667bc20d11ead822 100644 --- a/config_center/apollo/impl.go +++ b/config_center/apollo/impl.go @@ -128,6 +128,12 @@ 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 { + // todo(@zouyx) + return nil +} + 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 d6c3b06b327f16c709b09121e589db6694d3663e..e5284e89eb5bfade76378975338069de649dfcbb 100644 --- a/config_center/dynamic_configuration.go +++ b/config_center/dynamic_configuration.go @@ -50,6 +50,10 @@ 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..a1f9ff8cc65476ac6eb3381e01f8c0a43a3d8c68 100644 --- a/config_center/nacos/impl.go +++ b/config_center/nacos/impl.go @@ -18,6 +18,8 @@ package nacos import ( + "errors" + "strings" "sync" ) @@ -65,101 +67,130 @@ func newNacosDynamicConfiguration(url *common.URL) (*nacosDynamicConfiguration, } // AddListener Add listener -func (n *nacosDynamicConfiguration) AddListener(key string, listener config_center.ConfigurationListener, opions ...config_center.Option) { - n.addListener(key, listener) +func (nacos *nacosDynamicConfiguration) AddListener(key string, listener config_center.ConfigurationListener, opions ...config_center.Option) { + nacos.addListener(key, listener) } // RemoveListener Remove listener -func (n *nacosDynamicConfiguration) RemoveListener(key string, listener config_center.ConfigurationListener, opions ...config_center.Option) { - n.removeListener(key, listener) +func (nacos *nacosDynamicConfiguration) RemoveListener(key string, listener config_center.ConfigurationListener, opions ...config_center.Option) { + nacos.removeListener(key, listener) } -//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...) +// GetProperties nacos distinguishes configuration files based on group and dataId. defalut group = "dubbo" and dataId = key +func (nacos *nacosDynamicConfiguration) GetProperties(key string, opts ...config_center.Option) (string, error) { + return nacos.GetRule(key, opts...) } // GetInternalProperty Get properties value by key -func (n *nacosDynamicConfiguration) GetInternalProperty(key string, opts ...config_center.Option) (string, error) { - return n.GetProperties(key, opts...) +func (nacos *nacosDynamicConfiguration) GetInternalProperty(key string, opts ...config_center.Option) (string, error) { + return nacos.GetProperties(key, opts...) +} + +// PublishConfig will publish the config with the (key, group, value) pair +func (nacos *nacosDynamicConfiguration) PublishConfig(key string, group string, value string) error { + + group = nacos.resolvedGroup(group) + + ok, err := (*nacos.client.Client()).PublishConfig(vo.ConfigParam{ + DataId: key, + Group: group, + Content: value, + }) + + if err != nil { + return err + } + if !ok { + return errors.New("publish config to Nocos failed") + } + return nil } // GetRule Get router rule -func (n *nacosDynamicConfiguration) GetRule(key string, opts ...config_center.Option) (string, error) { +func (nacos *nacosDynamicConfiguration) GetRule(key string, opts ...config_center.Option) (string, error) { tmpOpts := &config_center.Options{} for _, opt := range opts { opt(tmpOpts) } - content, err := (*n.client.Client()).GetConfig(vo.ConfigParam{ + content, err := (*nacos.client.Client()).GetConfig(vo.ConfigParam{ DataId: key, - Group: tmpOpts.Group, + Group: nacos.resolvedGroup(tmpOpts.Group), }) if err != nil { return "", perrors.WithStack(err) } else { - return string(content), nil + return content, nil } } // Parser Get Parser -func (n *nacosDynamicConfiguration) Parser() parser.ConfigurationParser { - return n.parser +func (nacos *nacosDynamicConfiguration) Parser() parser.ConfigurationParser { + return nacos.parser } // SetParser Set Parser -func (n *nacosDynamicConfiguration) SetParser(p parser.ConfigurationParser) { - n.parser = p +func (nacos *nacosDynamicConfiguration) SetParser(p parser.ConfigurationParser) { + nacos.parser = p } // NacosClient Get Nacos Client -func (n *nacosDynamicConfiguration) NacosClient() *NacosClient { - return n.client +func (nacos *nacosDynamicConfiguration) NacosClient() *NacosClient { + return nacos.client } // SetNacosClient Set Nacos Client -func (n *nacosDynamicConfiguration) SetNacosClient(client *NacosClient) { - n.cltLock.Lock() - n.client = client - n.cltLock.Unlock() +func (nacos *nacosDynamicConfiguration) SetNacosClient(client *NacosClient) { + nacos.cltLock.Lock() + nacos.client = client + nacos.cltLock.Unlock() } // WaitGroup for wait group control, zk client listener & zk client container -func (n *nacosDynamicConfiguration) WaitGroup() *sync.WaitGroup { - return &n.wg +func (nacos *nacosDynamicConfiguration) WaitGroup() *sync.WaitGroup { + return &nacos.wg } // GetDone For nacos client control RestartCallBack() bool -func (n *nacosDynamicConfiguration) GetDone() chan struct{} { - return n.done +func (nacos *nacosDynamicConfiguration) GetDone() chan struct{} { + return nacos.done } // GetUrl Get Url -func (n *nacosDynamicConfiguration) GetUrl() common.URL { - return *n.url +func (nacos *nacosDynamicConfiguration) GetUrl() common.URL { + return *nacos.url } // Destroy Destroy configuration instance -func (n *nacosDynamicConfiguration) Destroy() { - close(n.done) - n.wg.Wait() - n.closeConfigs() +func (nacos *nacosDynamicConfiguration) Destroy() { + close(nacos.done) + nacos.wg.Wait() + nacos.closeConfigs() +} + +// resolvedGroup will regular the group. Now, it will replace the '/' with '-'. +// '/' is a special character for nacos +func (nacos *nacosDynamicConfiguration) resolvedGroup(group string) string { + if len(group) <=0 { + return group + } + return strings.ReplaceAll(group, "/", "-") } // IsAvailable Get available status -func (n *nacosDynamicConfiguration) IsAvailable() bool { +func (nacos *nacosDynamicConfiguration) IsAvailable() bool { select { - case <-n.done: + case <-nacos.done: return false default: return true } } -func (r *nacosDynamicConfiguration) closeConfigs() { - r.cltLock.Lock() - client := r.client - r.client = nil - r.cltLock.Unlock() +func (nacos *nacosDynamicConfiguration) closeConfigs() { + nacos.cltLock.Lock() + client := nacos.client + nacos.client = nil + nacos.cltLock.Unlock() // Close the old client first to close the tmp node client.Close() logger.Infof("begin to close provider nacos 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..0f16727b60d20d4ec482f2b23c4eb4f53612cf6d 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 (nacos *nacosDynamicConfiguration) addListener(key string, listener config_center.ConfigurationListener) { + _, loaded := nacos.keyListeners.Load(key) if !loaded { _, cancel := context.WithCancel(context.Background()) - err := (*l.client.Client()).ListenConfig(vo.ConfigParam{ + err := (*nacos.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) + nacos.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 (nacos *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..f8263e47c7f31035231e4b6f2ab805096addf2ff 100644 --- a/config_center/zookeeper/impl.go +++ b/config_center/zookeeper/impl.go @@ -41,6 +41,7 @@ const ( // ZkClient //zookeeper client name 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..936b3b5acf39b103fbfcecd4bace4b6dbca8e944 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("")) +} + +// Create 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 { @@ -423,6 +431,8 @@ func (z *ZookeeperClient) Create(basePath string) error { return nil } + + // Delete ... func (z *ZookeeperClient) Delete(basePath string) error { var (