- Created by 海亮, last modified on Jun 11, 2021
GoFrame
的配置管理由gcfg
模块实现,gcfg
模块是并发安全的,仅提供配置文件读取功能,不提供数据写入/修改功能,支持的数据文件格式包括: JSON/XML/YAML(YML)/TOML/INI
,项目中开发者可以灵活地选择自己熟悉的配置文件格式来进行配置管理。
使用方式:
import "github.com/gogf/gf/os/gcfg"
接口文档:
https://godoc.org/github.com/gogf/gf/os/gcfg
配置管理
配置文件
默认配置文件
默认读取的配置文件为config.toml
,toml
类型文件也是默认的、推荐的配置文件格式(语法参考 配置管理-TOML格式 章节),如果想要自定义文件格式,可以通过SetFileName
方法修改默认读取的配置文件名称(如:config.json
, cfg.yaml
, cfg.xml
, cfg.ini
等等)。
如果是单例获取,有自动化文件类型检索的特性,具体请参考 配置管理-单例对象 章节。
例如,我们可以通过以下方式读取config.json
配置文件中的数据库database
配置项。
// 设置默认配置文件,默认的 config.toml 将会被覆盖
g.Cfg().SetFileName("config.json")
// 后续读取时将会读取到 config.json 配置文件内容,
g.Cfg().Get("database")
我们有两种方式进行配置文件的管理,使用全局的g.Cfg()
获取单例对象(推荐),或者单独使用gcfg
模块进行管理。
示例配置文件 config.toml
:
# 模板引擎目录
viewpath = "/home/www/templates/"
# MySQL数据库配置
[database]
[[database.default]]
host = "127.0.0.1"
port = "3306"
user = "root"
pass = "123456"
name = "test1"
type = "mysql"
role = "master"
charset = "utf8"
priority = "1"
[[database.default]]
host = "127.0.0.1"
port = "3306"
user = "root"
pass = "123456"
name = "test2"
type = "mysql"
role = "master"
charset = "utf8"
priority = "1"
# Redis数据库配置
[redis]
disk = "127.0.0.1:6379,0"
cache = "127.0.0.1:6379,1"
注意:以上toml
配置文件中的密码字段值123456
使用了双引号进行包含,用以标识该密码字段为字符串类型,防止配置文件读取时自动转换为整型引起歧义。
默认文件修改
我们可以通过多种方式修改默认文件名称:
- 通过配置管理方法
SetFileName
修改。 - 修改命令行启动参数 -
gf.gcfg.file
。 - 修改指定的环境变量 -
GF_GCFG_FILE
。
假如我们的执行程序文件为main
,那么可以通过以下方式修改配置管理器的配置文件目录(Linux
下):
- 通过单例模式
g.Cfg().SetFileName("config.prod.toml")
- 通过命令行启动参数
./main --gf.gcfg.file=config.prod.toml
- 通过环境变量(常用在容器中)
- 启动时修改环境变量:
GF_GCFG_FILE=config.prod.toml; ./main
- 使用
genv
模块来修改环境变量:genv.Set("GF_GCFG_FILE", "config.prod.toml")
- 启动时修改环境变量:
配置读取
gcfg
包最大的特点是支持按层级获取配置数据,层级访问默认通过英文”.
“号指定,其中pattern
参数和 gjson (数据动态编解码) 的pattern
参数一致。
例如针对以上config.toml
配置文件内容的层级读取:
// /home/www/templates/
g.Cfg().Get("viewpath")
// 127.0.0.1:6379,1
g.Cfg().Get("redis.cache")
// test2
g.Cfg().Get("database.default.1.name")
配置修改
我们可以通过Set
方法动态修改配置对象中的配置数据,Set
方法定义如下:
func (c *Config) Set(pattern string, value interface{}) error
使用方式同 gjson (数据动态编解码) 模块。
配置目录
目录配置方法
gcfg
配置管理器支持非常灵活的多目录自动搜索功能,通过SetPath
可以修改目录管理目录为唯一的一个目录地址,同时,我们推荐通过AddPath
方法添加多个搜索目录,配置管理器底层将会按照添加目录的顺序作为优先级进行自动检索。直到检索到一个匹配的文件路径为止,如果在所有搜索目录下查找不到配置文件,那么会返回失败。
默认目录配置
gcfg
配置管理对象初始化时,默认会自动添加以下配置文件搜索目录:
- 当前工作目录及其下的
config
目录:例如当前的工作目录为/home/www
时,将会添加/home/www
及/home/www/config
; - 当前可执行文件所在目录及其下的
config
目录:例如二进制文件所在目录为/tmp
时,将会添加/tmp
及/tmp/config
; - 当前
main
源代码包所在目录及其下的config
目录(仅对源码开发环境有效):例如main
包所在目录为/home/john/workspace/gf-app
时,将会添加/home/john/workspace/gf-app
及/home/john/workspace/gf-app/config
;
默认目录修改
我们可以通过以下方式修改配置管理器的配置文件搜索目录,配置管理对象将会只在该指定目录执行配置文件检索:
- 通过配置管理器的
SetPath
方法手动修改; - 修改命令行启动参数 -
gf.gcfg.path
; - 修改指定的环境变量 -
GF_GCFG_PATH
;
假如我们的执行程序文件为main
,那么可以通过以下方式修改配置管理器的配置文件目录(Linux下):
- 通过单例模式
g.Cfg().SetPath("/opt/config")
- 通过命令行启动参数
./main --gf.gcfg.path=/opt/config/
- 通过环境变量(常用在容器中)
- 启动时修改环境变量:
GF_GCFG_PATH=/opt/config/; ./main
- 使用
genv
模块来修改环境变量:genv.Set("GF_GCFG_PATH", "/opt/config")
- 启动时修改环境变量:
注意事项
大家都知道,在Golang
里面,map/slice
类型其实是一个”引用类型”(也叫”指针类型”),因此当你对这种类型的变量 键值对/索引项 进行修改时,会同时修改到其对应的底层数据。
从效率上考虑,gcfg
包某些获取方法返回的数据类型为map/slice
时,没有对其做值拷贝,因此当你对返回的数据进行修改时,会同时修改gcfg
对应的底层数据。
例如:
// For testing/example only.
content := `{"map":{"key":"value"}, "slice":[59,90]}`
gcfg.SetContent(content)
defer gcfg.RemoveContent()
m := g.Cfg().GetMap("map")
fmt.Println(m)
// Change the key-value pair.
m["key"] = "john"
// It changes the underlying key-value pair.
fmt.Println(g.Cfg().GetMap("map"))
s := g.Cfg().GetArray("slice")
fmt.Println(s)
// Change the value of specified index.
s[0] = 100
// It changes the underlying slice.
fmt.Println(g.Cfg().GetArray("slice"))
// output:
// map[key:value]
// map[key:john]
// [59 90]
// [100 90]
检测更新
配置管理器使用了缓存机制,当配置文件第一次被读取后会被缓存到内存中,下一次读取时将会直接从缓存中获取,以提高性能。同时,配置管理器提供了对配置文件的自动检测更新机制,当配置文件在外部被修改后,配置管理器能够即时地刷新配置文件的缓存内容。
- No labels
27 Comments
ayamzh
强哥我在看示例代码,没有找到加载config下的config.example.toml的相关逻辑,这块怎么实现的啊。还有就是也没看到数据库相关的配置在哪初始化连接的。
黄昌淮
你这家伙,分明就是不专心看书嘛,示例代码第一行不就是了吗, SetFileName()
ayamzh
还有个问题,配置文件之间有没有继承关系,比如我有一套基础配置,又根据ENV不同会重写一些IP或者端口之类的配置,例如base.toml 和 dev.toml,base里定义了各个环境中通用的配置, env.toml定义各个环境中需要重写的配置,这样有办法支持么
郭强
目前不支持呢。不过有考虑后续会将
gcfg
的核心功能做成接口化,这样开发者可以自定义配置读取。刘羽禅
这个配置文件继承 你看这样行不行?
1.读取base.toml
2.读取dev.toml
3.合并2个配置文件的内容
ayamzh
我们做法是只读dev.toml ,里面有一个关键字作为他的父文件,以此类推。感觉也还好
LeeEcho
能具体说说是怎么操作的吗?谢谢🙏
杨延庆
可以参考 viper,不过它的实现机制是按优先级逐个读每个文件里的 key,匹配到就退出,不利于整结构读取。
刘羽禅
反馈一个文档错误
假如我们的执行程序文件为
main
,那么可以通过以下方式修改配置管理器的配置文件目录(Linux
下):看这段文字描述,我在main.go文件中加入了,这行代码,然而并不生效
然后我在boot.go中添加,是可以正常工作的
黄昌淮
文件的相对路径没有设置对的, 它会报这样的错:
[ERRO] [gcfg] cannot find config file "config.fuck.toml" in resource manager or the following paths
1. C:\Users\huang\Desktop\GFdemo2
2. C:\Users\huang\Desktop\GFdemo2\config
我的配置文件是gfdemo2/config/fuck.toml, 灵活思考,把
// var ConfigObj2 = g.Cfg().SetFileName("config.fuck.toml")
改成
var ConfigObj2 = g.Cfg().SetFileName("fuck.toml")
就好了.
另外, 好像这个方法不能添加绝对路径的配置文件来着?
评论区编辑器没有markdown...
郭强
支持相对路径和绝对路径,任何开发语言在设置相对路径的时候新手往往容易出错。
海亮
是你配置错了。
g.Cfg().SetFileName("config.fuck.toml") 是找Desktop\GFdemo2\config.fuck.toml 或 Desktop\GFdemo2\config\config.fuck.toml
g.Cfg().SetFileName("fuck.toml") 是找Desktop\GFdemo2\fuck.toml 或 Desktop\GFdemo2\config\fuck.toml
黄昌淮
好的,我看看,因为是初次接触所以有很多知其然不知其所以然的地方,哈哈
杨率帅
请问在程序运行中切换了配置文件。能刷新已经使用的
g.DB()
的配置吗?郭强
配置会自动刷新到内存中。但数据库比较特殊一些,如果采用
dao
操作连接对象不会刷新,需要重新进程。如果是采用g.DB()
操作数据库,会刷新连接对象。杨率帅
在做自动化安装功能,将 server 和 生成工具做再一个exe里,出现dao起始加载不到config的问题,才有此疑问。
目前打包默认config到exe。在install时生成默认config 并用shell 重新执行程序解决问题
王一飞
我现在修改配置之后dao配置不会刷新,有解决的办法吗?比如说某个函数?
鼻屎拌饭加蛋
有老哥解答下吗,怎么这么难用啊。在win打了win,linux的包,win双击能正常启动,端口没问题。linux启动端口是80。找了一遍说什么配置,然后又启动加配置。如下:
[root@localhost office]# ./officeView --gf.gcfg.file /root/app/office/config.toml
2021/07/27 10:11:24 init ...
2021-07-27 10:11:24.223 [FATA] SetServerRoot failed: cannot find file/folder "public" in following paths:
1. /root/app/office
Stack:
1. office_view/router.init.0
C:/Users/Administrator/IdeaProjects/xxx/svn/office_view/router/router.go:10
特么的,烦死了,不想学了,gf版本为v1.6.4,
1.我想问问如何改端口呀,打出来的win双击端口是配置里边的。linux该怎么改
2.还有我想问问在win上打的包,linux运行为啥会出现win的路径。
鼻屎拌饭加蛋
算了,我歇逼了
LeeEcho
新建一个public空目录试试
海亮
C:/Users/Administrator/IdeaProjects/xxx/svn/office_view/router/router.go:10
这个路径是错误堆栈信息,是打包时候的文件路径,用来告诉你在哪个文件哪一行出问题了。
把心静下来慢慢学。
鼻屎拌饭加蛋
海亮
你可能没细看文档项目配置。
也需要好好学习一下如何阅读错误信息,错误处理-堆栈特性。
其实错误提示也挺明显的,就是在linux下运行的时候没有找到你的配置文件中所指定的serverRoot文件夹。
鼻屎拌饭加蛋
等于我打包的时候还得把config下的配置文件拷贝到public下边呀?
海亮
在运行的目录下面建一个public目录
鼻屎拌饭加蛋
我去,怎么端口还是80啊,没效果
SERVER | DOMAIN | ADDRESS | METHOD | ROUTE | HANDLER | MIDDLEWARE
----------|---------|---------|--------|-------------------|------------------------------------------|-------------
default | default | :80 | GET | /hello | office_view/app/api.(*helloApi).Index-fm |
Wilson liu
请教一下 ,关于自动检测更新的部分。如果在运行中更新了配置文件。是否有callback function 来通知更新而触发重新读配置的动作?目前看,只是更新后再用g.Cfg().* 可以拿到更新后的值。还是说只能采用gfsnotify去实现呢 ?