在我的项目开发中,配置文件的正当的获取和更新是一个根本的性能。我的项目上线后批改配置项,批改配置之后如果还重新启动我的项目能力失效,这样成果并不好。为了可能在不影响我的项目失常运行的状况下批改配置项,就须要用到配置热更新。例如:上线后想要批改日志的级别,能够间接批改配置文件,我的项目主动扫描配置文件,如果发现文件被批改,则从新获取配置信息。读取配置和配置热更新有两种形式:

  • 形式一:调用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())    }}