在我的项目开发中,配置文件的正当的获取和更新是一个根本的性能。我的项目上线后批改配置项,批改配置之后如果还重新启动我的项目能力失效,这样成果并不好。为了可能在不影响我的项目失常运行的状况下批改配置项,就须要用到配置热更新。例如:上线后想要批改日志的级别,能够间接批改配置文件,我的项目主动扫描配置文件,如果发现文件被批改,则从新获取配置信息。读取配置和配置热更新有两种形式:
- 形式一:调用 github.com/fsnotify/fsnotify 包,监控配置变动,并应用其余的包来读取文件
- 形式二:调用 github.com/spf13/viper 包,这个包是由 Steve Francia 开发,提供了监控和配置的设置、获取的办法
一、形式一的应用
- main.go 文件
package main
import (
"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 utils
import (
"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 utils
import (
"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 main
import (
"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())
}
}