动态加载配置文件
var RootCmd = &cobra.Command{
Use: "dressup",
Short: "operation background",
Long: "no only book operation background",
Run: func(cmd *cobra.Command, args []string) {
app.Run(NewApp("gon", cmd))
},
}
func init() {
RootCmd.Flags().String("db", "configs/db.toml", "the path to databases config file")
RootCmd.Flags().String("redis", "configs/redis.toml", "the path to redis config file")
RootCmd.Flags().String("server", "configs/server.toml", "the path to server config file")
}
使用 github.com/BurntSushi/toml映射配置到 struct
confDBFile := b.Command.Flags().Lookup("db")
if confDBFile == nil {
panic("there is no configuration file [db]")
}
if _, err := toml.DecodeFile(confDBFile.Value.String(), &conf.DBConfig); err != nil {
panic(err)
}
confServerFile := b.Command.Flags().Lookup("server")
if confServerFile == nil {
panic("configuration file does not exist:[server]")
}
if _, err := toml.DecodeFile(confServerFile.Value.String(), &conf.ServerConfig); err != nil {
panic(err)
}
confRedisFile := b.Command.Flags().Lookup("redis")
if confRedisFile == nil {
panic("there is no configuration file [redis]")
}
if _, err := toml.DecodeFile(confRedisFile.Value.String(), &conf.RedisConfig); err != nil {
panic(err)
}
var paths = make([]string, 0)
// 把需要动态加载的配置文件路径写进 []string
paths = append(paths, confDBFile.Value.String())
// 动态加载配置使用goroutine
go func() {
err := watchFiles(paths)
if err != nil {
return
}
}()
func watchFiles(paths []string) error {
watcher, err := fsnotify.NewWatcher()
if err != nil {
panic(err)
}
defer func(watcher *fsnotify.Watcher) {
err = watcher.Close()
if err != nil {
return
}
}(watcher)
done := make(chan bool)
go func() {
for {
select {
case event, ok := <-watcher.Events:
if !ok {
return
}
parts := strings.Split(event.Name, "\\")
if len(parts) < 1 {
err = fmt.Errorf("")
}
name := strings.Split(parts[len(parts)-1], ".")
if len(name) == 0 {
err = fmt.Errorf("")
}
if event.Op&fsnotify.Write == fsnotify.Write {
fmt.Println(fmt.Sprintf("监听到配置文件[%s]发生改变", name[0]))
switch name[0] {
case "db":
if _, err = toml.DecodeFile(event.Name, &conf.DBConfig); err != nil {
panic(err)
}
case "redis":
if _, err = toml.DecodeFile(event.Name, &conf.RedisConfig); err != nil {
panic(err)
}
}
}
case err, ok := <-watcher.Errors:
if !ok {
return
}
log.Error(err)
}
}
}()
for _, path := range paths {
err = watcher.Add(path)
if err != nil {
log.Error(err)
}
}
<-done
return nil
}
优雅的重启
func (b *Base) Run() {
// This function is received after the kill command, wait for 5 s
f := func() error {
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
select {
case sig := <-c:
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := b.Srv.Shutdown(ctx); err != nil {
logger.Error(zap.String("shutdown error", zap.Error(err).String))
return err
}
return fmt.Errorf("received signal %s", sig)
}
}
logger.Infox("terminated", zap.Error(f()))
}
收到退出信号时 至多等待5s 一般5s 一个请求也完成了 不够的话可以修改长一点