gpt4 book ai didi

使用Go语言实现配置文件热加载功能

转载 作者:qq735679552 更新时间:2022-09-29 22:32:09 25 4
gpt4 key购买 nike

CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.

这篇CFSDN的博客文章使用Go语言实现配置文件热加载功能由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.

 说到配置文件热加载,这个功能在很多框架中都提供了,如beego,实现的效果就是当你修改文件后,会把你修改后的配置重新加载到配置文件中,而不用重启程序,这个功能在日常中还是非常实用的,毕竟很多时候,线上的配置文件不是想改就能改的.

这次就自己实现一个配置文件的热加载功能的包,并通过一个简单的例子对完成的包进行使用验证 。

配置文件热加载包的是实现 。

其实整体的思路还是比较简单的,当获取配置文件内容后,会开启一个goroutine,去 循环读配置文件,当然这里不可能不限制的一直循环,而是设置了一个定时器,定时去读文件,根据文件的修改时间是否变化,从而确定是否重新reload配置文件 。

实现的config 包的文件结构为:

?
1
2
├── config.go
└── config_notify.go

config.go:代码的主要处理逻辑 config_notify.go:主要定义了一个接口,用于当文件修改时间变化的时候执行回调 。

config_notify.go的代码相对来说比较简单,我们先看看这个代码:

?
1
2
3
4
5
package config
// 定义一个通知的接口
type Notifyer interface {
  Callback(*Config)
}

这样当我们实现了Callback这个方法的时候,我们就实现了Notifyer这个接口,具体的调用在后面会说 。

在config.go中我们顶一个了一个结构体:

?
1
2
3
4
5
6
7
type Config struct {
  filename string
  lastModifyTime int64
  data map[string]string
  rwLock sync.RWMutex
  notifyList []Notifyer
}

结构体中主要包含几个字段:

filename:配置文件名字 lastModifyTime:配置文件的最后修改时间 data:用于将从配置文件中读取的内容存储为map rwlock:读写锁 notifyList:用于将调用该包的程序追加到切片中,用于通知调用上面在config_notify.go定义的callback回调函数 。

关于读取配置文件中的内容并存储到map中,这里定义了一个方法实现:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
func (c *Config) parse()(m map[string]string,err error){
  // 读文件并或将文件中的数据以k/v的形式存储到map中
  m = make(map[string]string,1024)
  file,err := os.Open(c.filename)
  if err != nil{
   return
  }
  var lineNo int
  reader := bufio.NewReader(file)
  for {
   // 一行行的读文件
   line,errRet := reader.ReadString( '\n' )
   if errRet == io.EOF{
    // 表示读到文件的末尾
    break
   }
   if errRet != nil{
    // 表示读文件出问题
    err = errRet
    return
   }
   lineNo++
   line = strings.TrimSpace(line) // 取出空格
   if len(line) == 0 || line[0] == '\n' || line[0] == '+' || line[0] == ';' {
    // 当前行为空行或者是注释行等
    continue
   }
   arr := strings.Split(line, "=" ) // 通过=进行切割取出k/v结构
   if len(arr) == 0{
    fmt.Printf( "invalid config,line:%d\n" ,lineNo)
    continue
   }
   key := strings.TrimSpace(arr[0])
   if len(key) == 0{
    fmt.Printf( "invalid config,line:%d\n" ,lineNo)
    continue
   }
   if len(arr) == 1{
    m[key] = ""
    continue
   }
   value := strings.TrimSpace(arr[1])
   m[key] = value
  }
  return
}

而最后我们就需要一个定时器,每隔一段时间判断配置文件的最后修改时间是否变化,如果变化则重新读取一次文件并将文件内容存储到map中.

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
func (c *Config) reload(){
  // 这里启动一个定时器,每5秒重新加载一次配置文件
  ticker := time.NewTicker(time.Second*5)
  for _ = range ticker.C{
   func(){
    file,err := os.Open(c.filename)
    if err != nil{
     fmt.Printf( "open %s failed,err:%v\n" ,c.filename,err)
     return
    }
    defer file.Close()
    fileInfo,err := file.Stat()
    if err != nil{
     fmt.Printf( "stat %s failed,err:%v\n" ,c.filename,err)
     return
    }
    curModifyTime := fileInfo.ModTime().Unix()
    fmt.Printf( "%v --- %v\n" ,curModifyTime,c.lastModifyTime)
    //判断文件的修改时间是否大于最后一次修改时间
    if curModifyTime > c.lastModifyTime{
     m,err := c.parse()
     if err != nil{
      fmt.Println( "parse failed,err:" ,err)
      return
     }
     c.rwLock.Lock()
     c.data = m
     c.rwLock.Unlock()
     for _, n:=range c.notifyList{
      n.Callback(c)
     }
     c.lastModifyTime = curModifyTime
    }
   }()
  }

关于config完整的代码地址:https://github.com/pythonsite/go_simple_code/tree/master/config 。

一个演示上述包的例子 。

这里一个简单的例子,代码的逻辑也非常简单就是写一个循环从配置文件读取配置信息,当然这里是为了测试效果,写成了循环。这里有个问题需要注意,就是在配置文件中存放数据的时候应该是如下格式存储 。

?
1
2
3
4
listen_addr = localhost
server_port = 1000
# Nginx addr
nginx_addr = 192.168.1.2:9090

测试代码的主要结构如下:

├── config.conf └── main.go 。

config.conf为配置文件 main.go 为主要测试代码 。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
type AppConfig struct {
  port int
  nginxAddr string
}
type AppconfigMgr struct {
  config atomic.Value
}
var appConfigMgr = &AppconfigMgr{}
func(a *AppconfigMgr)Callback(conf *config.Config){
  var appConfig = &AppConfig{}
  port,err := conf.GetInt( "server_port" )
  if err != nil{
   fmt.Println( "get port failed,err:" ,err)
   return
  }
  appConfig.port = port
  fmt.Println( "port:" ,appConfig.port)
  nginxAddr,err := conf.GetString( "nginx_addr" )
  if err != nil{
   fmt.Println( "get nginx addr failed,err:" ,err)
   return
  }
  appConfig.nginxAddr = nginxAddr
  fmt.Println( "nginx addr :" ,appConfig.nginxAddr)
  appConfigMgr.config.Store(appConfig)
}
func run(){
  for {
   // 每5秒打印一次数据,查看自己更改配置文件后是否可以热刷新
   appConfig := appConfigMgr.config.Load().(*AppConfig)
   fmt.Println( "port:" ,appConfig.port)
   fmt.Println( "nginx addr:" ,appConfig.nginxAddr)
   time.Sleep(5* time.Second)
  }
}
func main() {
  conf,err := config.NewConfig( "/Users/zhaofan/go_project/src/go_dev/13/config_test/config.conf" )
  if err != nil{
   fmt.Println( "parse config failed,err:" ,err)
   return
  }
  //打开文件获取内容后,将自己加入到被通知的切片中
  conf.AddNotifyer(appConfigMgr)
  var appConfig = &AppConfig{}
  appConfig.port,err = conf.GetInt( "server_port" )
  if err != nil{
   fmt.Println( "get port failed,err:" ,err)
   return
  }
  fmt.Println( "port:" ,appConfig.port)
  appConfig.nginxAddr,err = conf.GetString( "nginx_addr" )
  if err != nil{
   fmt.Println( "get nginx addr failed,err:" ,err)
   return
  }
  fmt.Println( "nginx addr:" ,appConfig.nginxAddr)
  appConfigMgr.config.Store(appConfig)
  run()
}

上面代码中有一段代码非常重要:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func(a *AppconfigMgr)Callback(conf *config.Config){
  var appConfig = &AppConfig{}
  port,err := conf.GetInt( "server_port" )
  if err != nil{
   fmt.Println( "get port failed,err:" ,err)
   return
  }
  appConfig.port = port
  fmt.Println( "port:" ,appConfig.port)
  nginxAddr,err := conf.GetString( "nginx_addr" )
  if err != nil{
   fmt.Println( "get nginx addr failed,err:" ,err)
   return
  }
  appConfig.nginxAddr = nginxAddr
  fmt.Println( "nginx addr :" ,appConfig.nginxAddr)
  appConfigMgr.config.Store(appConfig)
}

这里我们实现了Callback方法,同时就实现了我们在config包中定义的那个接口 。

测试效果如下,当我们更改配置文件后,程序中的配置文件也被重新加载 。

使用Go语言实现配置文件热加载功能

完整的测试代码地址:https://github.com/pythonsite/go_simple_code/tree/master/config_test 。

总结 。

以上所述是小编给大家介绍的使用Go语言实现配置文件热加载功能,希望对大家有所帮助,如果大家有任何疑问欢迎给我留言,小编会及时回复大家的! 。

原文链接:https://www.cnblogs.com/zhaof/p/8593204.html 。

最后此篇关于使用Go语言实现配置文件热加载功能的文章就讲到这里了,如果你想了解更多关于使用Go语言实现配置文件热加载功能的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com