From bcd01f97cc679940859d1a1413de26cb0dc355a4 Mon Sep 17 00:00:00 2001 From: yoyofx Date: Thu, 29 Apr 2021 19:54:59 +0800 Subject: [PATCH] nacos viper remote provider --- example/main.go | 74 +++++++++++++++++++++++++++++++++++ example_config.yaml | 20 ++++++++++ go.mod | 8 ++++ nacos_manager.go | 94 +++++++++++++++++++++++++++++++++++++++++++++ nacos_options.go | 20 ++++++++++ viper_manager.go | 8 ++++ viper_remote.go | 66 +++++++++++++++++++++++++++++++ 7 files changed, 290 insertions(+) create mode 100644 example/main.go create mode 100644 example_config.yaml create mode 100644 go.mod create mode 100644 nacos_manager.go create mode 100644 nacos_options.go create mode 100644 viper_manager.go create mode 100644 viper_remote.go diff --git a/example/main.go b/example/main.go new file mode 100644 index 0000000..e3b46a4 --- /dev/null +++ b/example/main.go @@ -0,0 +1,74 @@ +package main + +import ( + "fmt" + "github.com/spf13/viper" + remote "github.com/yoyofxteam/nacos-viper-remote" + "os" + "os/signal" + "syscall" + "time" +) + +func main() { + config_viper := viper.New() + runtime_viper := config_viper + runtime_viper.SetConfigFile("./example_config.yaml") + _ = runtime_viper.ReadInConfig() + var option *remote.Option + _ = runtime_viper.Sub("yoyogo.cloud.discovery.metadata").Unmarshal(&option) + + remote.SetOptions(option) + + //remote.SetOptions(&remote.Option{ + // Url: "localhost", + // Port: 80, + // NamespaceId: "public", + // GroupName: "DEFAULT_GROUP", + // Config: remote.Config{ DataId: "config_dev" }, + // Auth: nil, + //}) + //localSetting := runtime_viper.AllSettings() + remote_viper := viper.New() + err := remote_viper.AddRemoteProvider("nacos", "localhost", "") + remote_viper.SetConfigType("yaml") + + err = remote_viper.ReadRemoteConfig() + if err == nil { + err = remote_viper.WatchRemoteConfigOnChannel() + if err == nil { + config_viper = remote_viper + fmt.Println("used remote viper") + } + } + + appName := config_viper.GetString("yoyogo.application.name") + + fmt.Println(appName) + + go func() { + for { + time.Sleep(time.Second * 30) // delay after each request + appName = config_viper.GetString("yoyogo.application.name") + fmt.Println(appName) + } + }() + + onExit() +} + +func onExit() { + c := make(chan os.Signal) + signal.Notify(c, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, + syscall.SIGQUIT, syscall.SIGUSR1, syscall.SIGUSR2) + + for s := range c { + switch s { + case syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT: + fmt.Println("Program Exit...", s) + + default: + fmt.Println("other signal", s) + } + } +} diff --git a/example_config.yaml b/example_config.yaml new file mode 100644 index 0000000..eb8fbd2 --- /dev/null +++ b/example_config.yaml @@ -0,0 +1,20 @@ +yoyogo: + application: + name: yoyogo_demo_dev + cloud: + discovery: + cache: + ttl: 30 # seconds + strategy: "round-robin" # round-robin , weight-time , random + type: "nacos" + metadata: + url: "120.53.133.30" + port: 80 + namespace: "public" + group: "DEFAULT_GROUP" + configserver: + dataId: "config_dev" + auth: + enable: true + username: "root" + password: "1234" \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..c0e2617 --- /dev/null +++ b/go.mod @@ -0,0 +1,8 @@ +module github.com/yoyofxteam/nacos-viper-remote + +go 1.14 + +require ( + github.com/spf13/viper v1.7.1 + github.com/nacos-group/nacos-sdk-go v1.0.7 +) diff --git a/nacos_manager.go b/nacos_manager.go new file mode 100644 index 0000000..e30d566 --- /dev/null +++ b/nacos_manager.go @@ -0,0 +1,94 @@ +package nacos_viper_remote + +import ( + "fmt" + "github.com/nacos-group/nacos-sdk-go/clients" + "github.com/nacos-group/nacos-sdk-go/clients/config_client" + "github.com/nacos-group/nacos-sdk-go/common/constant" + "github.com/nacos-group/nacos-sdk-go/common/logger" + "github.com/nacos-group/nacos-sdk-go/vo" + "github.com/spf13/viper" + "strings" +) + +type nacosConfigManager struct { + client config_client.IConfigClient + option *Option +} + +func NewNacosConfigManager(option *Option) (*nacosConfigManager, error) { + var serverConfigs []constant.ServerConfig + urls := strings.Split(option.Url, ";") + for _, url := range urls { + serverConfigs = append(serverConfigs, constant.ServerConfig{ + ContextPath: "/nacos", + IpAddr: url, + Port: option.Port, + }) + } + clientConfig := constant.ClientConfig{ + NamespaceId: option.NamespaceId, + TimeoutMs: 5000, + NotLoadCacheAtStart: true, + RotateTime: "1h", + MaxAge: 3, + LogLevel: "info", + } + if option.Auth != nil && option.Auth.Enable { + clientConfig.Username = option.Auth.User + clientConfig.Password = option.Auth.Password + } + client, err := clients.CreateConfigClient(map[string]interface{}{ + "serverConfigs": serverConfigs, + "clientConfig": clientConfig, + }) + if err != nil { + logger.Error(err.Error()) + return nil, err + } + + manager := &nacosConfigManager{client: client, option: option} + + return manager, err +} + +func (cm *nacosConfigManager) Get(dataId string) ([]byte, error) { + //get config + content, err := cm.client.GetConfig(vo.ConfigParam{ + DataId: cm.option.Config.DataId, + Group: cm.option.GroupName, + }) + return []byte(content), err +} + +func (cm *nacosConfigManager) Watch(dataId string, stop chan bool) <-chan *viper.RemoteResponse { + resp := make(chan *viper.RemoteResponse) + + configParams := vo.ConfigParam{ + DataId: cm.option.Config.DataId, + Group: cm.option.GroupName, + OnChange: func(namespace, group, dataId, data string) { + fmt.Println("config changed group:" + group + ", dataId:" + dataId) + resp <- &viper.RemoteResponse{ + Value: []byte(data), + Error: nil, + } + }, + } + err := cm.client.ListenConfig(configParams) + if err != nil { + return nil + } + + go func() { + for { + select { + case <-stop: + _ = cm.client.CancelListenConfig(configParams) + return + } + } + }() + + return resp +} diff --git a/nacos_options.go b/nacos_options.go new file mode 100644 index 0000000..cba5200 --- /dev/null +++ b/nacos_options.go @@ -0,0 +1,20 @@ +package nacos_viper_remote + +type Option struct { + Url string `mapstructure:"url"` + Port uint64 `mapstructure:"port"` + NamespaceId string `mapstructure:"namespace"` + GroupName string `mapstructure:"group"` + Config Config `mapstructure:"configserver"` + Auth *Auth `mapstructure:"auth"` +} + +type Config struct { + DataId string `mapstructure:"dataId"` +} + +type Auth struct { + Enable bool `mapstructure:"enable"` + User string `mapstructure:"username"` + Password string `mapstructure:"password"` +} diff --git a/viper_manager.go b/viper_manager.go new file mode 100644 index 0000000..a3c589a --- /dev/null +++ b/viper_manager.go @@ -0,0 +1,8 @@ +package nacos_viper_remote + +import "github.com/spf13/viper" + +type viperConfigManager interface { + Get(key string) ([]byte, error) + Watch(key string, stop chan bool) <-chan *viper.RemoteResponse +} diff --git a/viper_remote.go b/viper_remote.go new file mode 100644 index 0000000..4bad50d --- /dev/null +++ b/viper_remote.go @@ -0,0 +1,66 @@ +package nacos_viper_remote + +import ( + "bytes" + "errors" + "github.com/spf13/viper" + "io" +) + +var nacosOptions = &Option{} + +func SetOptions(option *Option) { + nacosOptions = option +} + +type remoteConfigProvider struct { +} + +func (rc *remoteConfigProvider) Get(rp viper.RemoteProvider) (io.Reader, error) { + cmt, err := getConfigManager(rp) + if err != nil { + return nil, err + } + var b []byte + switch cm := cmt.(type) { + case viperConfigManager: + b, err = cm.Get(rp.Path()) + } + if err != nil { + return nil, err + } + return bytes.NewReader(b), nil +} + +func (rc *remoteConfigProvider) Watch(rp viper.RemoteProvider) (io.Reader, error) { + return rc.Get(rp) +} + +func (rc *remoteConfigProvider) WatchChannel(rp viper.RemoteProvider) (<-chan *viper.RemoteResponse, chan bool) { + cmt, err := getConfigManager(rp) + if err != nil { + return nil, nil + } + + switch cm := cmt.(type) { + case viperConfigManager: + quit := make(chan bool) + viperResponseCh := cm.Watch("dataId", quit) + return viperResponseCh, quit + } + + return nil, nil +} + +func getConfigManager(rp viper.RemoteProvider) (interface{}, error) { + if rp.Provider() == "nacos" { + return NewNacosConfigManager(nacosOptions) + } else { + return nil, errors.New("The Nacos configuration manager is not supported!") + } +} + +func init() { + viper.SupportedRemoteProviders = []string{"nacos"} + viper.RemoteConfig = &remoteConfigProvider{} +}