Go语言实现配置热加载的方法分享
作者:tracy小猫
web项目,经常需要热启动各种各样的配置信息,一旦这些服务发生变更,我们需要重新启动web server,以使配置生效,实现配置热加载,本文为大家整理了几个方法实现这个需求,需要的可以参考下
概述
web项目,经常需要热启动各种各样的配置信息,一旦这些服务发生变更,我们需要重新启动web server,以使配置生效,实现配置热加载。这里有几种方法实现这个需求。
go 定时器协程实现
项目结构
首先来看一下整个项目的目录结构:
- dyconfig // 项目地址
- config // 配置文件目录
- api.yaml // 采用yaml格式文件
- global // 代码文件夹
- config
- config_define
- init
- reload
- go.mod // go package管理依赖的包文件
- go.sum // go package管理打包产生的文件
- main.go // web server的入口,主函数
代码细节
接下来依次看一下各文件的主体内容:
conf/api.yaml文件定义了配置项,包含server的host及port信息。
service: server: env: dev host: 127.0.0.1 port: 9902
global/init.go
package global import ( "context" "path" ) type Config struct { Conf struct { FilePath string LastModifyTime int64 } ctx context.Context cancel context.CancelFunc } func NewConfig() (*Config, error) { conf := new(Config) conf.ctx, conf.cancel = context.WithCancel(context.Background()) conf.Conf.FilePath = path.Join("./config", "api.yaml") APIconfig = conf.loadRoute() go conf.reload() //开启协程监听routeNotify go func() { for { select { case lastModifyTime, ok := <-routeNotify: if !ok { return } conf.Conf.LastModifyTime = lastModifyTime route := routeAtomic.Load().(*APIConf) if route != nil { APIconfig = route } } } }() return conf, nil } func (c *Config) Stop() { c.cancel() }
定义Config 根据LastModifyTime 判断是否有发生变化,FilePath为文件路径
go conf.reload()
开启协程监听routeNotify,routeNotify内容是文件修改时间的timestamp
global/reload.go
package global import ( "fmt" "gopkg.in/yaml.v3" "io/ioutil" "os" "sync/atomic" "time" ) const ( CheckInterval = 5 * time.Second ) var ( routeAtomic atomic.Value //原子性,解决并发问题 routeNotify = make(chan int64) //channel 放入timestamp ) func (c *Config) reload() { ticker := time.NewTicker(CheckInterval) defer ticker.Stop() for { select { case <-c.ctx.Done(): close(routeNotify) return case <-ticker.C: if f, err := os.Stat(c.Route.FilePath); err != nil { fmt.Println(err) } else if f.ModTime().Unix() != c.Route.LastModifyTime { if c.Route.LastModifyTime == 0 { c.Route.LastModifyTime = f.ModTime().Unix() } else { routeAtomic.Store(c.loadConfig()) routeNotify <- f.ModTime().Unix() fmt.Println("配置文件发生变化") } } } } } func (c *Config) loadConfig() *APIConf { if fp, err := ioutil.ReadFile(c.Route.FilePath); err != nil { fmt.Println(err) return nil } else { route := new(APIConf) if err := yaml.Unmarshal(fp, &route); err != nil { return nil } return route } }
定时器监听文件的修改时间与LastModifyTime是否相同,如果不同,则
package global var ( APIconfig = new(APIConf) )
package global type ServiceConf struct { Server struct { Env string `yaml:"env"` Host string `yaml:"host"` Port string `yaml:"port"` } `yaml:"server"` } type APIConf struct { Service ServiceConf `yaml:"service"` }
mian
package main import ( "dyconfig/global" "fmt" "github.com/gin-gonic/gin" ) func main() { if conf, err := global.NewConfig(); err != nil { // 初始化配置 fmt.Println(err) } else { defer conf.Stop() } gin.SetMode(gin.DebugMode) r := gin.Default() r.GET("/ping", func(context *gin.Context) { fmt.Println("当前host是: ", global.APIconfig.Service.Server.Host) fmt.Println("当前port是: ", global.APIconfig.Service.Server.Port) context.JSON( 200, gin.H{ "host": global.APIconfig.Service.Server.Host, "port": &global.APIconfig.Service.Server.Port, }) }) port := global.APIconfig.Service.Server.Port fmt.Println("当前host是: ", global.APIconfig.Service.Server.Host) fmt.Println("当前port是: ", global.APIconfig.Service.Server.Port) port = ":" + port _ = r.Run(port) }
调用示例
1.第一次调用,port为9902
2. 修改config ,port为9903
到此这篇关于Go语言实现配置热加载的方法分享的文章就介绍到这了,更多相关Go语言配置热加载内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!