gcfg组件采用接口化设计,默认提供的是基于文件系统的接口实现。支持的数据文件格式包括: JSON/XML/YAML(YML)/TOML/INI/PROPERTIES,项目中开发者可以灵活地选择自己熟悉的配置文件格式来进行配置管理。

配置文件

默认配置文件

配置对象我们推荐使用单例方式获取,单例对象将会按照文件后缀toml/yaml/yml/json/ini/xml/properties文自动检索配置文件。默认情况下会自动检索配置文件config.toml/yaml/yml/json/ini/xml/properties并缓存,配置文件在外部被修改时将会自动刷新缓存。

如果想要自定义文件格式,可以通过SetFileName方法修改默认读取的配置文件名称(如:default.yaml, default.json, default.xml等等)。例如,我们可以通过以下方式读取default.yaml配置文件中的数据库database配置项。

// 设置默认配置文件,默认读取的配置文件设置为 default.yaml
g.Cfg().GetAdapter().(*gcfg.AdapterFile).SetFileName("default.yaml")

// 后续读取时将会读取到 default.yaml 配置文件内容
g.Cfg().Get(ctx, "database")

默认文件修改

文件可以是一个具体的文件名称或者完整的文件绝对路径。

我们可以通过多种方式修改默认文件名称:

  1. 通过配置管理方法SetFileName修改。
  2. 修改命令行启动参数 - gf.gcfg.file
  3. 修改指定的环境变量 - GF_GCFG_FILE

假如我们的执行程序文件为main,那么可以通过以下方式修改配置管理器的配置文件目录(Linux下):

  1. 通过单例模式

    g.Cfg().GetAdapter().(*gcfg.AdapterFile).SetFileName("default.yaml")
  2. 通过命令行启动参数
     ./main --gf.gcfg.file=config.prod.toml
  3. 通过环境变量(常用在容器中)
    • 启动时修改环境变量:
        GF_GCFG_FILE=config.prod.toml; ./main
    • 使用genv模块来修改环境变量:
        genv.Set("GF_GCFG_FILE", "config.prod.toml")

配置目录

目录配置方法

gcfg配置管理器支持非常灵活的多目录自动搜索功能,通过SetPath可以修改目录管理目录为唯一的目录地址,同时,我们推荐通过AddPath方法添加多个搜索目录,配置管理器底层将会按照添加目录的顺序作为优先级进行自动检索。直到检索到一个匹配的文件路径为止,如果在所有搜索目录下查找不到配置文件,那么会返回失败。

默认目录配置

gcfg配置管理对象初始化时,默认会自动添加以下配置文件搜索目录:

  1. 当前工作目录及其下的configmanifest/config目录:例如当前的工作目录为/home/www时,将会添加:
    1. /home/www
    2. /home/www/config
    3. /home/www/manifest/config
  2. 当前可执行文件所在目录及其下的configmanifest/config目录:例如二进制文件所在目录为/tmp时,将会添加:
    1. /tmp
    2. /tmp/config
    3. /tmp/manifest/config
  3. 当前main源代码包所在目录及其下的configmanifest/config目录(仅对源码开发环境有效):例如main包所在目录为/home/john/workspace/gf-app时,将会添加:
    1. /home/john/workspace/gf-app
    2. /home/john/workspace/gf-app/config
    3. /home/john/workspace/gf-app/manifest/config

默认目录修改

注意这里修改的参数必须是一个目录,不能是文件路径。

我们可以通过以下方式修改配置管理器的配置文件搜索目录,配置管理对象将会只在该指定目录执行配置文件检索:

  1. 通过配置管理器的SetPath方法手动修改;
  2. 修改命令行启动参数 - gf.gcfg.path
  3. 修改指定的环境变量 - GF_GCFG_PATH

假如我们的执行程序文件为main,那么可以通过以下方式修改配置管理器的配置文件目录(Linux下):

  1. 通过单例模式

    g.Cfg().GetAdapter().(*gcfg.AdapterFile).SetPath("/opt/config")
  2. 通过命令行启动参数
     ./main --gf.gcfg.path=/opt/config/
  3. 通过环境变量(常用在容器中)
    • 启动时修改环境变量:
        GF_GCFG_PATH=/opt/config/; ./main
    • 使用genv模块来修改环境变量:
        genv.Set("GF_GCFG_PATH", "/opt/config")

内容配置

gcfg包也支持直接内容配置,而不是读取配置文件,常用于程序内部动态修改配置内容。通过以下包配置方法实现全局的配置:

func (c *AdapterFile) SetContent(content string, file ...string)
func (c *AdapterFile) GetContent(file ...string) string
func (c *AdapterFile) RemoveContent(file ...string)
func (c *AdapterFile) ClearContent()

需要注意的是该配置是全局生效的,并且优先级会高于读取配置文件。因此,假如我们通过SetContent("v = 1", "config.toml")配置了config.toml的配置内容,并且也同时存在config.toml配置文件,那么只会使用到SetContent的配置内容,而配置文件内容将会被忽略。

层级访问

在默认提供的文件系统接口实现下,gcfg组件支持按层级获取配置数据,层级访问默认通过英文.号指定,其中pattern参数和 通用编解码-gjsonpattern参数一致。例如以下配置(config.yaml):

server:
  address:    ":8199"
  serverRoot: "resource/public"

database:
  default:
    link:   "mysql:root:12345678@tcp(127.0.0.1:3306)/focus"
    debug:  true

例如针对以上配置文件内容的层级读取:

// :8199
g.Cfg().Get(ctx, "server.address")

// true
g.Cfg().Get(ctx, "database.default.debug")

注意事项

大家都知道,在Golang里面,map/slice类型其实是一个”引用类型”(也叫”指针类型”),因此当你对这种类型的变量 键值对/索引项 进行修改时,会同时修改到其对应的底层数据。从效率上考虑,gcfg包某些获取方法返回的数据类型为map/slice时,没有对其做值拷贝,因此当你对返回的数据进行修改时,会同时修改gcfg对应的底层数据。

例如:

配置文件:

// config.json:
`{"map":{"key":"value"}, "slice":[59,90]}`

示例代码:

var ctx = gctx.New()

m := g.Cfg().MustGet(ctx, "map").Map()
fmt.Println(m)

// Change the key-value pair.
m["key"] = "john"

// It changes the underlying key-value pair.
fmt.Println(g.Cfg().MustGet(ctx, "map").Map())

s := g.Cfg().MustGet(ctx, "slice").Slice()
fmt.Println(s)

// Change the value of specified index.
s[0] = 100

// It changes the underlying slice.
fmt.Println(g.Cfg().MustGet(ctx, "slice").Slice())

// output:
// map[key:value]
// map[key:john]
// [59 90]
// [100 90]

检测更新

配置管理器使用了缓存机制,当配置文件第一次被读取后会被缓存到内存中,下一次读取时将会直接从缓存中获取,以提高性能。同时,配置管理器提供了对配置文件的自动检测更新机制,当配置文件在外部被修改后,配置管理器能够即时地刷新配置文件的缓存内容。


Content Menu

  • No labels

15 Comments

  1. 为什么我使用SetContent方法设置配置文件内容之后,用g.Cfg().Dump()打印出的配置还是我本地config.toml文件的内容

  2. 多个配置文件做合并重设Content ,用*Config 拿不到最新的配置的。而且 *AdapterFile 内的 SetContent 方法只能修改存在的文件的。因为在Config 初始化的时候 用的  adapter.Available 方法验证的就只有存在的文件。*Config 和 *AdapterFile 在配置文件初始化的时候出现了逻辑互斥。如果要想得到动态的配置内容,只能直接使用 *AdapterFile 内的 SetContent  config.toml 然后用 gcfg.Instance("config.toml") ,也就是 config.toml  这个是系统默认的 AdapterFile 的文件名称;现在我可以确定了,这个*AdapterFile 内的 SetContent 只能修改  默认的配置文件名称 也就是 config.toml  这个名称的

  3. 使用gcfg.New().Get()  默认读取的是 config.toml  ,已经有config.yaml 怎么不读?没有config.toml这个文件啊

    1. 你使用最新版本或者master分支试试,之前有个版本有点问题。

  4. 郭强 配置文件的默认搜索目录优先级应该还有一个 gres优先,所以完整的 配置文件默认搜索目录优先级应该是:

    1. gres下的
      1. config
      2. manifest/config
    2. 当前工作目录及其下的configmanifest/config目录:例如当前的工作目录为/home/www时,将会添加:
      1. /home/www
      2. /home/www/config
      3. /home/www/manifest/config
    3. 当前可执行文件所在目录及其下的configmanifest/config目录:例如二进制文件所在目录为/tmp时,将会添加:
      1. /tmp
      2. /tmp/config
      3. /tmp/manifest/config
    4. 当前main源代码包所在目录及其下的configmanifest/config目录(仅对源码开发环境有效):例如main包所在目录为/home/john/workspace/gf-app时,将会添加:
      1. /home/john/workspace/gf-app
      2. /home/john/workspace/gf-app/config
      3. /home/john/workspace/gf-app/manifest/config
  5. go 版本 go1.17.11
    gf 版本  v2.1.2
    使用gcfg.SetContent();时报undefined: gcfg.SetContent
  6. 配置文件修改不生效,我把配置文件删了,他居然还能读取之前的数据。有没有开发讨论群啊。拉我进下群。好多问题。呜呜

    1. 你应该是配置不对吧.初始化出来的.有个manifest/config/config.yaml,你在里面修改就行了.框架会依次从外往里找.找到就停止.

      1. 我是 gf init下载的模板。版本是2.0.6。所以配置文件都有。

  7. s := g.Cfg().GetArray("slice")
    fmt.Println(s)

    这里已经过期了,v2.0以上版本,现在应该怎么调用的?

    1. var s *gvar.Var
      s, err := g.Cfg().Get(context.Background(), "slice")
      fmt.Println(s[0])

      通过这种方式实现了,不知道是否最优解

  8. wyy

    set完如何写入配置文件

  9. 修改配置文件不生效,居然还能读取未修改前的数据。这是有什么缓存机制吗?

    1. 我也遇到类似问题,请问你的解决了吗?

  10. 郭强 大佬,gf 版本2.5.7,读取配置文件manifest/config/config.yaml,即使清空了文件内容,启动程序时,仍然读取到之前的历史内容,不知为何?