- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
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包中定义的那个接口 。
测试效果如下,当我们更改配置文件后,程序中的配置文件也被重新加载 。
完整的测试代码地址:https://github.com/pythonsite/go_simple_code/tree/master/config_test 。
总结 。
以上所述是小编给大家介绍的使用Go语言实现配置文件热加载功能,希望对大家有所帮助,如果大家有任何疑问欢迎给我留言,小编会及时回复大家的! 。
原文链接:https://www.cnblogs.com/zhaof/p/8593204.html 。
最后此篇关于使用Go语言实现配置文件热加载功能的文章就讲到这里了,如果你想了解更多关于使用Go语言实现配置文件热加载功能的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我想要显示正在加载的 .gif,直到所有内容都已加载,包括嵌入的 iframe。但是,目前加载 gif 会在除 iframe 之外的所有内容都已加载后消失。我怎样才能让它等到 iframe 也加载完毕
首先,这是我第一次接触 Angular。 我想要实现的是,我有一个通知列表,我必须以某种方式限制 limitTo,因此元素被限制为三个,在我单击按钮后,其余的应该加载。 我不明白该怎么做: 设置“ V
我正在尝试在我的设备上运行这个非常简单的应用程序(使用 map API V2),并且出于某种原因尝试使用 MapView 时: 使用 java 文件: public class MainMap e
我正在使用 Python 2.6、Excel 2007 Professional 和最新版本的 PyXLL。在 PyXLL 中加载具有 import scipy 抛出异常,模块未加载。有没有人能够在
我想做这个: 创建并打包原始游戏。然后我想根据原始游戏中的蓝图创建具有新网格/声音/动画和蓝图的其他 PAK 文件。原始游戏不应该知道有关其他网格/动画/等的任何信息。因此,我需要在原始游戏中使用 A
**摘要:**在java项目中经常会使用到配置文件,这里就介绍几种加载配置文件的方法。 本文分享自华为云社区《【Java】读取/加载 properties配置文件的几种方法》,作者:Copy工程师。
在 Groovy 脚本中是否可以执行条件导入语句? if (test){ import this.package.class } else { import that.package.
我正在使用 NVidia 视觉分析器(来自 CUDA 5.0 beta 版本的基于 eclipse 的版本)和 Fermi 板,我不了解其中两个性能指标: 全局加载/存储效率表示实际内存事务数与请求事
有没有办法在通过 routeProvider 加载特定 View 时清除 Angular JS 存储的历史记录? ? 我正在使用 Angular 创建一个公共(public)安装,并且历史会积累很多,
使用 Xcode 4.2,在我的应用程序中, View 加载由 segue 事件触发。 在 View Controller 中首先调用什么方法? -(void) viewWillAppear:(BOO
我在某些Django模型中使用JSONField,并希望将此数据从Oracle迁移到Postgres。 到目前为止,当使用Django的dumpdata和loaddata命令时,我仍然没有运气来保持J
创建 Nib 时,我需要创建两种类型:WindowNib 或 ViewNib。我看到的区别是,窗口 Nib 有一个窗口和一个 View 。 如何将 View Nib 加载到另一个窗口中?我是否必须创建
我想将多个env.variables转换为静态结构。 我可以手动进行: Env { is_development: env::var("IS_DEVELOPMENT")
正如我从一个测试用例中看到的:https://godbolt.org/z/K477q1 生成的程序集加载/存储原子松弛与普通变量相同:ldr 和 str 那么,宽松的原子变量和普通变量之间有什么区别吗
我有一个重定向到外部网站的按钮/链接,但是外部网站需要一些时间来加载。所以我想添加一个加载屏幕,以便外部页面在显示之前完全加载。我无法控制外部网站,并且外部网站具有同源策略,因此我无法在 iFrame
我正在尝试为我的应用程序开发一个Dockerfile,该文件在初始化后加载大量环境变量。不知何故,当我稍后执行以下命令时,这些变量是不可用的: docker exec -it container_na
很难说出这里问的是什么。这个问题是含糊的、模糊的、不完整的、过于宽泛的或修辞性的,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开它,visit the help center 。 已关
我刚刚遇到一个问题,我有一个带有一些不同选项的选择标签。 现在我想检查用户选择了哪些选项。 然后我想将一个新的 html 文件加载到该网站(取决于用户选中的选项)宽度 javascript,我该怎么做
我知道两种保存/加载应用程序设置的方法: 使用PersistentStore 使用文件系统(存储,因为 SDCard 是可选的) 我想知道您使用应用程序设置的做法是什么? 使用 PersistentS
我开始使用 Vulkan 时偶然发现了我的第一个问题。尝试创建调试报告回调时(验证层和调试扩展在我的英特尔 hd vulkan 驱动程序上可用,至少它是这么说的),它没有告诉我 vkCreateDeb
我是一名优秀的程序员,十分优秀!