在项目开发中,配置文件的合理的获取和更新是一个基本的功能。项目上线后修改配置项,修改配置之后如果还重新启动项目才能生效,这样效果并不好。为了能够在不影响项目正常运行的情况下修改配置项,就需要用到配置热更新。例如:上线后想要修改日志的级别,可以直接修改配置文件,项目自动扫描配置文件,如果发现文件被修改,则重新获取配置信息。读取配置和配置热更新有两种方式:
- 方式一:调用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())
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。