重载配置,大部分主流的服务器软件都支持,例如 mysql nginx php-fpm 都支持 reload 操作。
如果我们自己写程序如何实现呢?
我将一个程序分为几个阶段:
-
初始化:加载配置文件,注册各种服务
-
运行:启动各种服务
-
退出:cancel所有goroutine,wg.Wait(),其他收尾工作
重载配置触发过程在第3步,我通过 kill -USR1
发送一个信号给进程,进程捕获到以后做如下事情:
4 、cancel所有goroutine ,wait, 释放资源(mysql、redis等连接) 5、goto 第1步,重新加载配置文件,重新初始化服务,然后运行
为了统一管理mysql这类服务,我使用了一个包:
github.com/keepeye/go-container
大致代码如下:
package main
import (
"context"
"os"
"syscall"
"os/signal"
"sync"
"github.com/keepeye/go-container/container"
"github.com/spf13/viper"
"github.com/jinzhu/gorm"
"time"
)
func registerServices() {
// 注册配置服务
container.Singleton("config", func(c *container.Container) interface{} {
viper.AddConfigPath("./build")
viper.SetConfigName("app")
err := viper.ReadInConfig()
if err != nil {
panic(err)
}
return viper.GetViper()
})
// 注册数据库服务
container.Singleton("db", func(c *container.Container) interface{} {
config := c.Get("config").(*viper.Viper)
//初始化数据库
db, err := gorm.Open("mysql", config.GetString("database.dsn"))
if err != nil {
panic(err)
}
db.DB().SetMaxOpenConns(10)
db.DB().SetMaxIdleConns(2)
// 是否打印sql
printSql := config.GetBool("database.print_sql")
if printSql {
db = db.LogMode(true)
}
// release的时候关闭db
c.BeforeRelease(func() {
db.Close()
})
return db
})
}
func main() {
//wg用来统一记录gorountine,以便程序结束时等待所有线程都退出
wg := &sync.WaitGroup{}
//使用context包,便于向所有goroutine发送退出信号
ctx,cancel := context.WithCancel(context.Background())
//注册所有服务
START: registerServices()
//模拟一个服务
wg.Add(1)
go func(ctx context.Context) {
defer wg.Done()
db := container.Get("mysql").(*gorm.DB)
tick := time.Tick(1 * time.Second)
for {
select {
case <-ctx.Done():
return
case <-tick:
user := struct {
ID int `json:"id"`
}{}
db.Where("status = 1").Last(&user)
//.....
}
}
}(ctx)
ch := make(chan os.Signal, 1)
signal.Notify(ch, syscall.SIGINT, syscall.SIGKILL, syscall.SIGTERM, syscall.SIGUSR1)
sig := <-ch
switch sig {
case syscall.SIGINT:
fallthrough
case syscall.SIGKILL:
fallthrough
case syscall.SIGTERM:
cancel()
wg.Wait()
container.Release()
// 重载配置
case syscall.SIGUSR1:
cancel()
wg.Wait()
//release释放所有旧的连接
container.Release()
//清空服务实例,下次重新初始化的时候会使用新的配置参数
container.Refresh()
goto START
}
}