在我的项目开发中,配置文件的正当的获取和更新是一个根本的性能。我的项目上线后批改配置项,批改配置之后如果还重新启动我的项目能力失效,这样成果并不好。为了可能在不影响我的项目失常运行的状况下批改配置项,就须要用到配置热更新。例如:上线后想要批改日志的级别,能够间接批改配置文件,我的项目主动扫描配置文件,如果发现文件被批改,则从新获取配置信息。读取配置和配置热更新有两种形式:
- 形式一:调用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 { Drive string `yaml:"drive"` Address string `yaml:"address"`}type Token struct { Salt string `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 { Drive string `yaml:"drive"` Address string `yaml:"address"`}type Token struct { Salt string `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()) }}