项目配置文件获取及更新热更新

【项目配置文件获取及更新热更新】在项目开发中,配置文件的合理的获取和更新是一个基本的功能。项目上线后修改配置项,修改配置之后如果还重新启动项目才能生效,这样效果并不好。为了能够在不影响项目正常运行的情况下修改配置项,就需要用到配置热更新。例如:上线后想要修改日志的级别,可以直接修改配置文件,项目自动扫描配置文件,如果发现文件被修改,则重新获取配置信息。读取配置和配置热更新有两种方式:

  • 方式一:调用github.com/fsnotify/fsnotify包,监控配置变化,并使用其他的包来读取文件
  • 方式二:调用github.com/spf13/viper包,这个包是由Steve Francia开发,提供了监控和配置的设置、获取的方法
一、方式一的使用
  • main.go文件
package mainimport ( "file-store/handler" "file-store/models" "file-store/utils" "fmt" "log" "net/http""github.com/fsnotify/fsnotify" )func main() { configPath := "/Users/apple/workplace/file-store/config/config.yaml" configDir := "/Users/apple/workplace/file-store/config"// 加载配置文件 _ = utils.InitConfig(configPath, "config") fmt.Printf("配置为:%s\n", utils.Conf.Token.Salt)watcher, err := fsnotify.NewWatcher() if err != nil { log.Fatal(err) } defer watcher.Close()done := make(chan bool) go func() { for { select { case event := <-watcher.Events: if event.Op&fsnotify.Write == fsnotify.Write { _ = utils.InitConfig(configPath, "config") fmt.Printf("更新配置为:%s\n", utils.Conf.Token.Salt) } case err := <-watcher.Errors: log.Println("error:", err) } } }() fmt.Printf("数据库配置为:%s, %s\n", utils.Conf.Mysql.Drive, utils.Conf.Mysql.Address) // 数据库操作 models.MysqlInit(utils.Conf.Mysql.Drive, utils.Conf.Mysql.Address) // 监听端口 err = http.ListenAndServe("127.0.0.1:8000", nil) if err != nil { fmt.Printf("Failed to start server, err %s", err.Error()) }// 监控文件 err = watcher.Add(configPath) if err != nil { log.Fatal(err) } // 监控文件夹 err = watcher.Add(configDir) if err != nil { log.Fatal(err) } <-done }

  • config.go:该文件用于获取配置信息,读取配置用到两个包:ioutil读取配置,gopkg.in/yaml.v2用于数据的转换
package utilsimport ( "fmt" "io/ioutil" "sync""gopkg.in/yaml.v2" )type Config struct { File*File`yaml:"file"` Mysql *Mysql `yaml:"mysql"` Token *Token `yaml:"token"` }type File struct { Path string `yaml:"path"` }type Mysql struct { Drivestring `yaml:"drive"` Address string `yaml:"address"` }type Token struct { Saltstring `yaml:"salt"` Issue string `yaml:"issue"` }var Conf *Config// InitConfig 读取yaml配置文件 func InitConfig(configPath, configName string) error { var locker = new(sync.RWMutex) yamlFile, err := ioutil.ReadFile(configPath) if err != nil { panic(err) } locker.Lock() err1 := yaml.Unmarshal(yamlFile, &Conf) if err1 != nil { panic(err) } locker.Unlock() fmt.Println(Conf.Token.Salt) return nil }

二、方式二的使用
  • config.go文件
package utilsimport ( "fmt""github.com/spf13/viper" )type Config struct { File*File`yaml:"file"` Mysql *Mysql `yaml:"mysql"` Token *Token `yaml:"token"` }type File struct { Path string `yaml:"path"` }type Mysql struct { Drivestring `yaml:"drive"` Address string `yaml:"address"` }type Token struct { Saltstring `yaml:"salt"` Issue string `yaml:"issue"` }// 全局配置 var config = new(Config)// InitConfig 读取yaml配置文件 func InitConfig(configPath, configName, configType string) error { viper.SetConfigName(configName) // 配置文件名 viper.SetConfigType(configType) // 配置文件类型,例如:toml、yaml等 viper.AddConfigPath(configPath) // 查找配置文件所在的路径,多次调用可以添加多个配置文件搜索的目录 // 读取配置文件配置,并处理错误 if err := viper.ReadInConfig(); err != nil { if _, ok := err.(viper.ConfigFileNotFoundError); ok { return err } } // 监控配置文件变化 viper.WatchConfig() viper.Unmarshal(config) if err := validateConfig(config); err != nil { return err } return nil }// 获取全局配置 func GetConfig() *Config { return config }// validateConfig:校验配置信息 func validateConfig(conf *Config) error { var ( file= conf.File.Path drive= conf.Mysql.Drive address = conf.Mysql.Address salt= conf.Token.Salt issue= conf.Token.Issue ) if file == "" { return fmt.Errorf("invalid file path: %s\n", file) } if drive == "" { return fmt.Errorf("invalid drive: %s\n", drive) } if address == "" { return fmt.Errorf("invalid address: %s\n", address) } if salt == "" { return fmt.Errorf("invalid salt: %s\n", salt) } if issue == "" { return fmt.Errorf("invalid issue: %s\n", issue) } return nil }

  • main.go文件
package mainimport ( "file-store/handler" "file-store/models" "file-store/utils" "fmt" "net/http" )func main() { configPath := "/Users/apple/workplace/file-store/config" // 配置初始化 err := utils.InitConfig(configPath, "config", "yaml") if err != nil { fmt.Printf("Failed to init config, err is %s\n", err) } // 获取全局配置 conf := utils.GetConfig() fmt.Println(conf.File.Path) // 数据库操作 models.MysqlInit(conf.Mysql.Drive, conf.Mysql.Address) // 监听端口 err = http.ListenAndServe("127.0.0.1:8000", nil) if err != nil { fmt.Printf("Failed to start server, err %s", err.Error()) } }

    推荐阅读