diff --git a/client.go b/client.go index 9b0a708..96b9e2c 100644 --- a/client.go +++ b/client.go @@ -43,11 +43,6 @@ import ( "github.com/apolloconfig/agollo/v4/utils/parse/yml" ) -var ( - //next try connect period - 60 second - nextTryConnectPeriod int64 = 60 -) - func init() { extension.SetCacheFactory(&memory.DefaultCacheFactory{}) extension.SetLoadBalance(&roundrobin.RoundRobin{}) @@ -63,32 +58,52 @@ func init() { var syncApolloConfig = remote.CreateSyncApolloConfig() -// Client apollo 客户端实例 -type Client struct { +//Client apollo 客户端接口 +type Client interface { + GetConfig(namespace string) *storage.Config + GetConfigAndInit(namespace string) *storage.Config + GetConfigCache(namespace string) agcache.CacheInterface + GetDefaultConfigCache() agcache.CacheInterface + GetApolloConfigCache() agcache.CacheInterface + GetValue(key string) string + GetStringValue(key string, defaultValue string) string + GetIntValue(key string, defaultValue int) int + GetFloatValue(key string, defaultValue float64) float64 + GetBoolValue(key string, defaultValue bool) bool + GetStringSliceValue(key string, defaultValue []string) []string + GetIntSliceValue(key string, defaultValue []int) []int + AddChangeListener(listener storage.ChangeListener) + RemoveChangeListener(listener storage.ChangeListener) + GetChangeListeners() *list.List + UseEventDispatch() +} + +// internalClient apollo 客户端实例 +type internalClient struct { initAppConfigFunc func() (*config.AppConfig, error) appConfig *config.AppConfig cache *storage.Cache } -func (c *Client) getAppConfig() config.AppConfig { +func (c *internalClient) getAppConfig() config.AppConfig { return *c.appConfig } -func create() *Client { +func create() *internalClient { appConfig := env.InitFileConfig() - return &Client{ + return &internalClient{ appConfig: appConfig, } } // Start 根据默认文件启动 -func Start() (*Client, error) { +func Start() (Client, error) { return StartWithConfig(nil) } // StartWithConfig 根据配置启动 -func StartWithConfig(loadAppConfig func() (*config.AppConfig, error)) (*Client, error) { +func StartWithConfig(loadAppConfig func() (*config.AppConfig, error)) (Client, error) { // 有了配置之后才能进行初始化 appConfig, err := env.InitConfig(loadAppConfig) if err != nil { @@ -127,12 +142,12 @@ func StartWithConfig(loadAppConfig func() (*config.AppConfig, error)) (*Client, } //GetConfig 根据namespace获取apollo配置 -func (c *Client) GetConfig(namespace string) *storage.Config { +func (c *internalClient) GetConfig(namespace string) *storage.Config { return c.GetConfigAndInit(namespace) } //GetConfigAndInit 根据namespace获取apollo配置 -func (c *Client) GetConfigAndInit(namespace string) *storage.Config { +func (c *internalClient) GetConfigAndInit(namespace string) *storage.Config { if namespace == "" { return nil } @@ -153,7 +168,7 @@ func (c *Client) GetConfigAndInit(namespace string) *storage.Config { } //GetConfigCache 根据namespace获取apollo配置的缓存 -func (c *Client) GetConfigCache(namespace string) agcache.CacheInterface { +func (c *internalClient) GetConfigCache(namespace string) agcache.CacheInterface { config := c.GetConfigAndInit(namespace) if config == nil { return nil @@ -163,7 +178,7 @@ func (c *Client) GetConfigCache(namespace string) agcache.CacheInterface { } //GetDefaultConfigCache 获取默认缓存 -func (c *Client) GetDefaultConfigCache() agcache.CacheInterface { +func (c *internalClient) GetDefaultConfigCache() agcache.CacheInterface { config := c.GetConfigAndInit(storage.GetDefaultNamespace()) if config != nil { return config.GetCache() @@ -172,12 +187,12 @@ func (c *Client) GetDefaultConfigCache() agcache.CacheInterface { } //GetApolloConfigCache 获取默认namespace的apollo配置 -func (c *Client) GetApolloConfigCache() agcache.CacheInterface { +func (c *internalClient) GetApolloConfigCache() agcache.CacheInterface { return c.GetDefaultConfigCache() } //GetValue 获取配置 -func (c *Client) GetValue(key string) string { +func (c *internalClient) GetValue(key string) string { value := c.getConfigValue(key) if value == nil { return utils.Empty @@ -187,7 +202,7 @@ func (c *Client) GetValue(key string) string { } //GetStringValue 获取string配置值 -func (c *Client) GetStringValue(key string, defaultValue string) string { +func (c *internalClient) GetStringValue(key string, defaultValue string) string { value := c.GetValue(key) if value == utils.Empty { return defaultValue @@ -197,7 +212,7 @@ func (c *Client) GetStringValue(key string, defaultValue string) string { } //GetIntValue 获取int配置值 -func (c *Client) GetIntValue(key string, defaultValue int) int { +func (c *internalClient) GetIntValue(key string, defaultValue int) int { value := c.GetValue(key) i, err := strconv.Atoi(value) @@ -210,7 +225,7 @@ func (c *Client) GetIntValue(key string, defaultValue int) int { } //GetFloatValue 获取float配置值 -func (c *Client) GetFloatValue(key string, defaultValue float64) float64 { +func (c *internalClient) GetFloatValue(key string, defaultValue float64) float64 { value := c.GetValue(key) i, err := strconv.ParseFloat(value, 64) @@ -223,7 +238,7 @@ func (c *Client) GetFloatValue(key string, defaultValue float64) float64 { } //GetBoolValue 获取bool 配置值 -func (c *Client) GetBoolValue(key string, defaultValue bool) bool { +func (c *internalClient) GetBoolValue(key string, defaultValue bool) bool { value := c.GetValue(key) b, err := strconv.ParseBool(value) @@ -236,7 +251,7 @@ func (c *Client) GetBoolValue(key string, defaultValue bool) bool { } //GetStringSliceValue 获取[]string 配置值 -func (c *Client) GetStringSliceValue(key string, defaultValue []string) []string { +func (c *internalClient) GetStringSliceValue(key string, defaultValue []string) []string { value := c.getConfigValue(key) if value == nil { @@ -250,7 +265,7 @@ func (c *Client) GetStringSliceValue(key string, defaultValue []string) []string } //GetIntSliceValue 获取[]int 配置值 -func (c *Client) GetIntSliceValue(key string, defaultValue []int) []int { +func (c *internalClient) GetIntSliceValue(key string, defaultValue []int) []int { value := c.getConfigValue(key) if value == nil { @@ -263,7 +278,7 @@ func (c *Client) GetIntSliceValue(key string, defaultValue []int) []int { return s } -func (c *Client) getConfigValue(key string) interface{} { +func (c *internalClient) getConfigValue(key string) interface{} { cache := c.GetDefaultConfigCache() if cache == nil { return utils.Empty @@ -279,21 +294,21 @@ func (c *Client) getConfigValue(key string) interface{} { } // AddChangeListener 增加变更监控 -func (c *Client) AddChangeListener(listener storage.ChangeListener) { +func (c *internalClient) AddChangeListener(listener storage.ChangeListener) { c.cache.AddChangeListener(listener) } // RemoveChangeListener 增加变更监控 -func (c *Client) RemoveChangeListener(listener storage.ChangeListener) { +func (c *internalClient) RemoveChangeListener(listener storage.ChangeListener) { c.cache.RemoveChangeListener(listener) } // GetChangeListeners 获取配置修改监听器列表 -func (c *Client) GetChangeListeners() *list.List { +func (c *internalClient) GetChangeListeners() *list.List { return c.cache.GetChangeListeners() } // UseEventDispatch 添加为某些key分发event功能 -func (c *Client) UseEventDispatch() { +func (c *internalClient) UseEventDispatch() { c.AddChangeListener(storage.UseEventDispatch()) } diff --git a/client_test.go b/client_test.go index eda8151..7f65fcf 100644 --- a/client_test.go +++ b/client_test.go @@ -42,7 +42,7 @@ func init() { extension.SetCacheFactory(&memory.DefaultCacheFactory{}) } -func createMockApolloConfig(expireTime int) *Client { +func createMockApolloConfig(expireTime int) *internalClient { client := create() client.cache = storage.CreateNamespaceConfig(client.appConfig.NamespaceName) configs := make(map[string]interface{}, 0) @@ -242,7 +242,7 @@ func createApolloConfigWithJSON(b []byte) (o interface{}, err error) { return apolloConfig, nil } -func checkBackupFile(client *Client, t *testing.T) { +func checkBackupFile(client *internalClient, t *testing.T) { newConfig, e := extension.GetFileHandler().LoadConfigFile(client.appConfig.GetBackupConfigPath(), client.appConfig.AppID, testDefaultNamespace) Assert(t, newConfig, NotNilVal()) Assert(t, e, NilVal()) diff --git a/component/remote/abs.go b/component/remote/abs.go index 7083a70..76379ef 100644 --- a/component/remote/abs.go +++ b/component/remote/abs.go @@ -18,6 +18,9 @@ package remote import ( + "strconv" + "time" + "github.com/apolloconfig/agollo/v4/component/log" "github.com/apolloconfig/agollo/v4/env" "github.com/apolloconfig/agollo/v4/env/config" @@ -36,12 +39,23 @@ func (a *AbsApolloConfig) SyncWithNamespace(namespace string, appConfigFunc func appConfig := appConfigFunc() urlSuffix := a.remoteApollo.GetSyncURI(appConfig, namespace) + c := &env.ConnectConfig{ + URI: urlSuffix, + AppID: appConfig.AppID, + Secret: appConfig.Secret, + Timeout: notifyConnectTimeout, + } + if appConfig.SyncServerTimeout > 0 { + duration, err := time.ParseDuration(strconv.Itoa(appConfig.SyncServerTimeout) + "s") + if err != nil { + log.Errorf("parse sync server timeout %s fail, error:%v", err) + return nil + } + c.Timeout = duration + } + callback := a.remoteApollo.CallBack(namespace) - apolloConfig, err := http.RequestRecovery(appConfig, &env.ConnectConfig{ - URI: urlSuffix, - AppID: appConfig.AppID, - Secret: appConfig.Secret, - }, &callback) + apolloConfig, err := http.RequestRecovery(appConfig, c, &callback) if err != nil { log.Errorf("request %s fail, error:%v", urlSuffix, err) return nil diff --git a/env/file/json/json.go b/env/file/json/json.go index f7cb13d..a77c2e3 100644 --- a/env/file/json/json.go +++ b/env/file/json/json.go @@ -22,6 +22,7 @@ import ( "encoding/json" "fmt" "github.com/apolloconfig/agollo/v4/env/config" + "os" "sync" "github.com/apolloconfig/agollo/v4/component/log" @@ -38,14 +39,41 @@ var ( configFileMap = make(map[string]string, 1) //configFileMapLock configFileMap的读取锁 configFileMapLock sync.Mutex + + //configFileDirMap 存取 configFileDir 文件所在地址,用于判断是否已经存在目录 + configFileDirMap = make(map[string]bool, 1) + //configFileDirMapLock configFileDirMap的读取锁 + configFileDirMapLock sync.Mutex ) // FileHandler 默认备份文件读写 type FileHandler struct { } +// WriteConfigFile write config to file +func (fileHandler *FileHandler) createDir(configPath string) error { + if configPath == "" { + return nil + } + + configFileDirMapLock.Lock() + defer configFileDirMapLock.Unlock() + if !configFileDirMap[configPath] { + err := os.Mkdir(configPath, os.ModePerm) + if err != nil { + log.Errorf("Create backup dir:%s fail,error:&s", configPath, err) + return err + } + } + return nil +} + // WriteConfigFile write config to file func (fileHandler *FileHandler) WriteConfigFile(config *config.ApolloConfig, configPath string) error { + err := fileHandler.createDir(configPath) + if err != nil { + return err + } return jsonFileConfig.Write(config, fileHandler.GetConfigFile(configPath, config.AppID, config.NamespaceName)) } diff --git a/env/file/json/json_test.go b/env/file/json/json_test.go index f459da9..354a2eb 100644 --- a/env/file/json/json_test.go +++ b/env/file/json/json_test.go @@ -28,6 +28,46 @@ import ( . "github.com/tevid/gohamcrest" ) +func TestCreateDir(t *testing.T) { + configPath := "conf" + f := &FileHandler{} + err := f.createDir(configPath) + Assert(t, err, NilVal()) + err = os.Mkdir(configPath, os.ModePerm) + Assert(t, os.IsExist(err), Equal(true)) + + err = f.createDir("") + Assert(t, err, NilVal()) + + os.RemoveAll(configPath) +} + +func TestJSONFileHandler_WriteConfigDirFile(t *testing.T) { + extension.SetFileHandler(&FileHandler{}) + configPath := "conf" + jsonStr := `{ + "appId": "100004458", + "cluster": "default", + "namespaceName": "application", + "configurations": { + "key1":"value1", + "key2":"value2", + "test": [1, 2] + }, + "releaseKey": "20170430092936-dee2d58e74515ff3" +}` + + config, err := createApolloConfigWithJSON([]byte(jsonStr)) + os.RemoveAll(configPath) + os.Remove(extension.GetFileHandler().GetConfigFile(configPath, config.AppID, config.NamespaceName)) + + Assert(t, err, NilVal()) + e := extension.GetFileHandler().WriteConfigFile(config, configPath) + Assert(t, e, NilVal()) + os.RemoveAll(configPath) + os.Remove(extension.GetFileHandler().GetConfigFile(configPath, config.AppID, config.NamespaceName)) +} + func TestJSONFileHandler_WriteConfigFile(t *testing.T) { extension.SetFileHandler(&FileHandler{}) configPath := "" diff --git a/env/file/json/raw.go b/env/file/json/raw.go index 25fca5e..50d6c78 100644 --- a/env/file/json/raw.go +++ b/env/file/json/raw.go @@ -19,6 +19,7 @@ package json import ( "fmt" + "github.com/apolloconfig/agollo/v4/component/log" "github.com/apolloconfig/agollo/v4/env/config" "os" "sync" @@ -60,7 +61,15 @@ func writeWithRaw(config *config.ApolloConfig, configDir string) error { //WriteConfigFile write config to file func (fileHandler *rawFileHandler) WriteConfigFile(config *config.ApolloConfig, configPath string) error { - writeWithRaw(config, configPath) + err := fileHandler.createDir(configPath) + if err != nil { + return err + } + + err = writeWithRaw(config, configPath) + if err != nil { + log.Errorf("writeWithRaw fail! ", err) + } return jsonFileConfig.Write(config, fileHandler.GetConfigFile(configPath, config.AppID, config.NamespaceName)) } diff --git a/env/file/json/raw_test.go b/env/file/json/raw_test.go index ff22152..231edae 100644 --- a/env/file/json/raw_test.go +++ b/env/file/json/raw_test.go @@ -25,6 +25,30 @@ import ( . "github.com/tevid/gohamcrest" ) +func TestRawHandler_WriteConfigDirFile(t *testing.T) { + extension.SetFileHandler(&rawFileHandler{}) + configPath := "conf" + jsonStr := `{ + "appId": "100004458", + "cluster": "default", + "namespaceName": "application.json", + "configurations": { + "key1":"value1", + "key2":"value2", + "test": ["a", "b"] + }, + "releaseKey": "20170430092936-dee2d58e74515ff3" +}` + + config, err := createApolloConfigWithJSON([]byte(jsonStr)) + os.RemoveAll(configPath) + + Assert(t, err, NilVal()) + e := extension.GetFileHandler().WriteConfigFile(config, configPath) + Assert(t, e, NilVal()) + os.RemoveAll(configPath) +} + func TestRawHandler_WriteConfigFile(t *testing.T) { extension.SetFileHandler(&rawFileHandler{}) configPath := "" diff --git a/start_test.go b/start_test.go index 967880e..5bbd801 100644 --- a/start_test.go +++ b/start_test.go @@ -154,7 +154,7 @@ func TestStructInit(t *testing.T) { time.Sleep(1 * time.Second) - c := client.appConfig + c := client.(*internalClient).appConfig Assert(t, c, NotNilVal()) Assert(t, "test1", Equal(c.AppID)) Assert(t, "dev1", Equal(c.Cluster))