基本介绍

GoFrame的配置管理由gcfg组件实现,gcfg组件的所有方法是并发安全的。gcfg组件采用接口化设计,默认提供的是基于文件系统的接口实现。

使用方式

import "github.com/gogf/gf/v2/os/gcfg"

接口文档

https://pkg.go.dev/github.com/gogf/gf/v2/os/gcfg

组件特性

gcfg组件具有以下显著特性:

  • 接口化设计,很高的灵活性及扩展性,默认提供文件系统接口实现
  • 支持多种常见配置文件格式:yaml/toml/json/xml/ini/properties
  • 支持配置项不存在时读取指定环境变量或命令行参数
  • 支持检索读取资源管理组件中的配置文件
  • 支持配置文件自动检测热更新特性
  • 支持层级访问配置项
  • 支持单例管理模式

注意事项

框架配置组件支持多种常用的数据格式,但在后续的示例代码中均使用yaml数据格式来做演示说明。在使用中,请随意使用习惯的数据格式不用局限于官网示例使用的yaml数据格式。例如,在业务项目模板中提供的是config.yaml配置文件模板(因为默认模板只能提供一种啊),您也可以直接修改为config.toml或者config.ini等支持的数据格式,配置组件也能自动根据文件名后缀识别读取

相关文档



Content Menu

  • No labels

42 Comments

  1. 强哥我在看示例代码,没有找到加载config下的config.example.toml的相关逻辑,这块怎么实现的啊。还有就是也没看到数据库相关的配置在哪初始化连接的。

    1. 你这家伙,分明就是不专心看书嘛,示例代码第一行不就是了吗, SetFileName()

  2. 还有个问题,配置文件之间有没有继承关系,比如我有一套基础配置,又根据ENV不同会重写一些IP或者端口之类的配置,例如base.toml 和 dev.toml,base里定义了各个环境中通用的配置, env.toml定义各个环境中需要重写的配置,这样有办法支持么

    1. 目前不支持呢。不过有考虑后续会将gcfg的核心功能做成接口化,这样开发者可以自定义配置读取。

    2. 这个配置文件继承    你看这样行不行?

      1.读取base.toml

      2.读取dev.toml

      3.合并2个配置文件的内容


      1. 我们做法是只读dev.toml ,里面有一个关键字作为他的父文件,以此类推。感觉也还好

        1. 能具体说说是怎么操作的吗?谢谢🙏

      2. 可以参考 viper,不过它的实现机制是按优先级逐个读每个文件里的 key,匹配到就退出,不利于整结构读取。


  3. 反馈一个文档错误


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

    1. 通过单例模式
       g.Cfg().SetFileName("config.prod.toml")


    看这段文字描述,我在main.go文件中加入了,这行代码,然而并不生效

    然后我在boot.go中添加,是可以正常工作的

    // 用于应用初始化。
    func init() {
    	g.Config().SetFileName("config.prod.toml")
    	
    	s := g.Server()
    
    	//开启debug模式,打印更多调试信息,建议在开发环境开启
    	g.SetDebug(true)
    	s.Plugin(&swagger.Swagger{})
    }



    1. 文件的相对路径没有设置对的, 它会报这样的错:

      [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...


      1. 支持相对路径和绝对路径,任何开发语言在设置相对路径的时候新手往往容易出错。

      2. 是你配置错了。

        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

        1. 好的,我看看,因为是初次接触所以有很多知其然不知其所以然的地方,哈哈

  4. 请问在程序运行中切换了配置文件。能刷新已经使用的 g.DB()的配置吗?

    1. 配置会自动刷新到内存中。但数据库比较特殊一些,如果采用dao操作连接对象不会刷新,需要重新进程。如果是采用g.DB()操作数据库,会刷新连接对象。

      1. 在做自动化安装功能,将 server 和 生成工具做再一个exe里,出现dao起始加载不到config的问题,才有此疑问。

        目前打包默认config到exe。在install时生成默认config 并用shell 重新执行程序解决问题

      2. 请问平滑重启功能应该可以刷新 DAO 连接对象吧?我看平滑重启那一部分说的是重启后 pid 会改变。

  5. 有老哥解答下吗,怎么这么难用啊。在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的路径。


    1. 算了,我歇逼了

      1. 新建一个public空目录试试

    2.  C:/Users/Administrator/IdeaProjects/xxx/svn/office_view/router/router.go:10

      这个路径是错误堆栈信息,是打包时候的文件路径,用来告诉你在哪个文件哪一行出问题了。

      把心静下来慢慢学。

      1. 这代码都是自动生成的
        func
        init() {
        s := g.Server() //第10行
        s.Group("/", func(group *ghttp.RouterGroup) {
        group.GET("/hello", api.Hello.Index)
        group.GET("/officeView/:name", api.Office.Get)
        })
        }
        1. 你可能没细看文档项目配置

          也需要好好学习一下如何阅读错误信息,错误处理-堆栈特性

          其实错误提示也挺明显的,就是在linux下运行的时候没有找到你的配置文件中所指定的serverRoot文件夹。

          serverRoot     = "public"
          1. 等于我打包的时候还得把config下的配置文件拷贝到public下边呀?

            1. 在运行的目录下面建一个public目录

              1. 我去,怎么端口还是80啊,没效果

                 SERVER  | DOMAIN  | ADDRESS | METHOD |       ROUTE       |                 HANDLER                  | MIDDLEWARE  
                ----------|---------|---------|--------|-------------------|------------------------------------------|-------------
                  default | default | :80     | GET    | /hello            | office_view/app/api.(*helloApi).Index-fm |    

                1. 你这没改动端口, 可不就是默认80

  6. yzl

    请问配置文件中的数据库密码,能像java那样,加密吗?

  7. 试了下,Linux环境下要使用 export GF_GCFG_FILE=config.prod.toml; ./main  配置文件才生效。

  8. 我刚开始学习GoFrame,请问现在支不支持在配置文件里使用环境变量?谢谢

    1. v2可以,哈哈哈


  9. 		if len(masterList) < 1 {
    			return nil, gerror.NewCode(gcode.CodeInvalidConfiguration, "at least one master node configuration's need to make sense")
    		}
    		if len(slaveList) < 1 {
    			slaveList = masterList
    		}
    		if master {
    			return getConfigNodeByWeight(masterList), nil
    		} else {
    			return getConfigNodeByWeight(slaveList), nil
    		}
    
    
    调用地方
    if node, err := getConfigNodeByGroup(groupName, true); err == nil {  
    这里调用上面代码块的函数,传入的master参数始终为true,没法获取slave的配置吧?
  10. 支持配置文件自动检测热更新特性,这个 热更新 怎么配置,我搜索了一下没找到示例,是我的姿势不对吗?

    1. 直接改配置文件的内容就可以

  11. k8s部署的,直接GF_GCFG_FILE注入到deployment即可

    1. 看起来不难,欢迎一起参与开源贡献哈。

  12. 反馈一个配置热更比较严重的bug,gf在运行过程中,配置被copy覆盖,会出现取到第三种状态数据的问题。用例如下

    当前目录
    config.toml     config.toml.bak go.mod          go.sum          main.go
    main.go
    import (
    "context"
    "fmt"
    "time"

    "github.com/fsnotify/fsnotify"
    "github.com/gogf/gf/v2/frame/g"
    "github.com/spf13/viper"
    )

    func main() {
    //testViper()
    testGfConfig()
    }

    func testViper() {
    logger := g.Log()
    viper.SetConfigName("config") // 配置文件名字,注意没有扩展名
    viper.SetConfigType("toml") // 如果配置文件的名称中没有包含扩展名,那么该字段是必需的
    viper.AddConfigPath("./") // 配置文件的路径
    err := viper.ReadInConfig() // 查找并读取配置文件
    if err != nil {
    panic(fmt.Errorf("Fatal error config file: %w \n", err))
    }
    viper.WatchConfig()
    viper.OnConfigChange(func(e fsnotify.Event) {
    logger.Info(context.TODO(), "Config file changed:", e.Name)
    })
    tk := time.NewTicker(time.Nanosecond)
    defer tk.Stop()

    for range tk.C {
    version := viper.GetString("version")
    logger.Info(context.TODO(), version)
    }
    }

    func testGfConfig() {
    logger := g.Log()
    tk := time.NewTicker(time.Nanosecond)
    defer tk.Stop()

    for range tk.C {
    version, err := g.Cfg().Get(context.TODO(), "version")
    if err != nil {
    panic(err)
    }
    logger.Info(context.TODO(), version)
    }
    }
    config.toml

    version=1
    [logger]
    Path = "console.log"
    Level = "all"
    Stdout = true
    CtxKeys = ["TraceId"]


    config.toml.bak的version=2
    go.mod

    go 1.18

    require (
    github.com/fsnotify/fsnotify v1.7.0
    github.com/gogf/gf/v2 v2.5.6
    github.com/spf13/viper v1.17.0
    )
    运行代码,然后执行以下命令
    $: cp -rf config.toml.bak config.toml
    你猜怎么着?
    2023-11-09 19:10:50.089 [INFO] 1
    2023-11-09 19:10:50.089 [INFO]
    2023-11-09 19:10:50.089 [INFO]
    2023-11-09 19:10:50.089 [INFO]
    2023-11-09 19:10:50.089 [INFO]
    2023-11-09 19:10:50.089 [INFO]
    2023-11-09 19:10:50.089 [INFO]
    2023-11-09 19:10:50.089 [INFO]
    2023-11-09 19:10:50.089 [INFO]
    2023-11-09 19:10:50.089 [INFO]
    2023-11-09 19:10:50.089 [INFO] 2
    2023-11-09 19:10:50.089 [INFO] 2
    2023-11-09 19:10:50.090 [INFO] 2
    程序居然读出了第三种状态。
    viper是没有这个问题的。
    1. 请提个issue或者pr哈。

  13. 缺少个保存配置功能, 虽然说保存配置会重载配置, 可能会影响性能, 但是这个需求还是有的. 

    1. 通过 inotify 接收文件变化通知,而非轮询,性能应该没什么影响吧

      1. 不知道, 反正挺缺的, 我现在配置只能用 go-ini/ini, 不过用了gf, 就更加喜欢啥都用gf自带的.  要是有就更好了.