Go几种读取配置文件的方式

简介: Go几种读取配置文件的方式

比较有名的方案有

使用viper管理配置


  • 支持多种配置文件格式,包括 JSON,TOML,YAML,HECL,envfile,甚至还包括Java properties
  • 支持为配置项设置默认值
  • 可以通过命令行参数覆盖指定的配置项
  • 支持参数别名


viper按照这个优先级(从高到低)获取配置项的取值:

  • explicit call to Set: 在代码逻辑中通过viper.Set()直接设置配置项的值
  • flag:命令行参数
  • env:环境变量
  • config:配置文件
  • key/value store:etcd或者consul
  • default:默认值


按照这个优先级(从高到低)获取配置项的取值:

  • explicit call to Set: 在代码逻辑中通过viper.Set()直接设置配置项的值
  • flag:命令行参数
  • env:环境变量
  • config:配置文件
  • key/value store:etcd或者consul
  • default:默认值


优先级


验证一下 viper.Set() 的优先级高于 配置文件

package main
import (
  "fmt"
  "github.com/spf13/viper"
)
func main() {
  loadConfig()
}
func loadConfig() {
  configVar := "shuang-config.yaml"
  configVar = "" // 这行如果注释掉,则从指定的configVar读取配置文件;否则就各种条件去找了
  viper.Set("Global.Source", "优先级最高")
  if configVar != "" {
    // SetConfigFile 显式定义配置文件的路径、名称和扩展名。
    // Viper 将使用它而不检查任何配置路径。
    viper.SetConfigFile(configVar)
  } else {
    // 如果没有显式指定配置文件,则
    // 会去下面的路径里找文件名`cui-config`的文件  name of config file (without extension)
    // 按照 []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl", "tfvars", "dotenv", "env", "ini"}的顺序(居然还支持Java用的properties)
    viper.SetConfigName("cui-config")
    viper.AddConfigPath("/etc/myapp") // 找寻的路径
    viper.AddConfigPath("$HOME/.myapp/")
    viper.AddConfigPath(".")
  }
  err := viper.ReadInConfig()
  if err != nil {
    panic(fmt.Errorf("error reading config: %s", err))
  }
  fmt.Printf("到底用的是哪个配置文件: '%s'\n", viper.ConfigFileUsed())
  fmt.Printf("Global.Source这个字段的值为: '%s'\n", viper.GetString("global.source"))
}

输出:

到底用的是哪个配置文件: '/Users/fliter/config-demo/cui-config.yaml'
Global.Source这个字段的值为: '优先级最高'

验证一下 环境变量 的优先级高于 配置文件

package main
import (
  "fmt"
  "github.com/spf13/viper"
)
func main() {
  loadConfig()
}
func loadConfig() {
  configVar := "shuang-config.yaml"
  configVar = "" // 这行如果注释掉,则从指定的configVar读取配置文件;否则就各种条件去找了
  viper.Set("Global.Source", "优先级最高")
  viper.AutomaticEnv()
  if configVar != "" {
    // SetConfigFile 显式定义配置文件的路径、名称和扩展名。
    // Viper 将使用它而不检查任何配置路径。
    viper.SetConfigFile(configVar)
  } else {
    // 如果没有显式指定配置文件,则
    // 会去下面的路径里找文件名`cui-config`的文件  name of config file (without extension)
    // 按照 []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl", "tfvars", "dotenv", "env", "ini"}的顺序(居然还支持Java用的properties)
    viper.SetConfigName("cui-config")
    viper.AddConfigPath("/etc/myapp") // 找寻的路径
    viper.AddConfigPath("$HOME/.myapp/")
    viper.AddConfigPath(".")
  }
  err := viper.ReadInConfig()
  if err != nil {
    panic(fmt.Errorf("error reading config: %s", err))
  }
  fmt.Printf("到底用的是哪个配置文件: '%s'\n", viper.ConfigFileUsed())
  fmt.Printf("LANG这个字段的值为: '%s'\n", viper.GetString("LANG"))
}

微信截图_20230925172326.png

viper.AutomaticEnv()会绑定所有环境变量,

如果只希望绑定特定的,可以使用SetEnvPrefix("global.source", "MYAPP_GLOAL_SOURCE"),注意这个函数不会自动加上MYAPP的前缀.




验证一下 命令行参数的优先级高于 配置文件


viper可以配合pflag来使用,pflag可以理解为标准库flag的一个增强版,viper可以绑定到pflag上

和cobra,viper一样,pflag也是同一作者的作品

微信截图_20230925172339.png

验证一下 默认值的优先级低于 配置文件

package main
import (
  "fmt"
  "github.com/spf13/viper"
)
func main() {
  loadConfig()
}
func loadConfig() {
  configVar := "shuang-config.yaml"
  configVar = "" // 这行如果注释掉,则从指定的configVar读取配置文件;否则就各种条件去找了
  //viper.Set("Global.Source", "优先级最高")
  viper.AutomaticEnv()
  viper.SetDefault("Global.Source", "优先级最低")
  if configVar != "" {
    // SetConfigFile 显式定义配置文件的路径、名称和扩展名。
    // Viper 将使用它而不检查任何配置路径。
    viper.SetConfigFile(configVar)
  } else {
    // 如果没有显式指定配置文件,则
    // 会去下面的路径里找文件名`cui-config`的文件  name of config file (without extension)
    // 按照 []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl", "tfvars", "dotenv", "env", "ini"}的顺序(居然还支持Java用的properties)
    viper.SetConfigName("cui-config")
    viper.AddConfigPath("/etc/myapp") // 找寻的路径
    viper.AddConfigPath("$HOME/.myapp/")
    viper.AddConfigPath(".")
  }
  err := viper.ReadInConfig()
  if err != nil {
    panic(fmt.Errorf("error reading config: %s", err))
  }
  fmt.Printf("到底用的是哪个配置文件: '%s'\n", viper.ConfigFileUsed())
  fmt.Printf("Global.Source这个字段的值为: '%s'\n", viper.GetString("Global.Source"))
}

微信截图_20230925172431.png

Watch机制(配置更新后 热加载)


该机制可以监听配置文件的修改, 这样就实现了热加载,修改配置后,无需重启服务

  • 对于本地文件,是通过fsnotify实现的,然后通过一个回调函数去通知应用来reload;
  • 对于Remote KV Store,目前只支持etcd,做法比较ugly,(5秒钟)轮询一次 而不是watch api
package main
import (
  "fmt"
  "time"
  "github.com/fsnotify/fsnotify"
  "github.com/spf13/viper"
)
func main() {
  loadConfig()
}
func loadConfig() {
  configVar := "shuang-config.yaml"
  configVar = "" // 这行如果注释掉,则从指定的configVar读取配置文件;否则就各种条件去找了
  if configVar != "" {
    // SetConfigFile 显式定义配置文件的路径、名称和扩展名。
    // Viper 将使用它而不检查任何配置路径。
    viper.SetConfigFile(configVar)
  } else {
    // 如果没有显式指定配置文件,则
    // 会去下面的路径里找文件名`cui-config`的文件  name of config file (without extension)
    // 按照 []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl", "tfvars", "dotenv", "env", "ini"}的顺序(居然还支持Java用的properties)
    viper.SetConfigName("cui-config")
    viper.AddConfigPath("/etc/myapp") // 找寻的路径
    viper.AddConfigPath("$HOME/.myapp/")
    viper.AddConfigPath(".")
  }
  viper.WatchConfig()
  viper.OnConfigChange(func(e fsnotify.Event) {
    fmt.Printf("配置文件 %s 发生了更改!!! 最新的Global.Source这个字段的值为 %s:", e.Name, viper.GetString("Global.Source"))
  })
  err := viper.ReadInConfig()
  if err != nil {
    panic(fmt.Errorf("error reading config: %s", err))
  }
  fmt.Printf("到底用的是哪个配置文件: '%s'\n", viper.ConfigFileUsed())
  fmt.Printf("Global.Source这个字段的值为: '%s'\n", viper.GetString("Global.Source"))
  time.Sleep(10000e9)
}

微信截图_20230925172522.png

Go viper 配置文件读取工具

动态获取配置文件(viper)




configor


Configor: 一个Golang配置工具,支持YAML,JSON,TOML,Shell环境,支持热加载

出自jinzhu大佬

微信截图_20230925172536.png

package main
import (
  "fmt"
  "github.com/jinzhu/configor"
)
type Config struct {
  APPName string `default:"app name"`
  DB      struct {
    Name     string
    User     string `default:"root"`
    Password string `required:"true" env:"DBPassword"`
    Port     uint   `default:"3306"`
  }
  Contacts []struct {
    Name  string
    Email string `required:"true"`
  }
}
func main() {
  var conf = Config{}
  err := configor.Load(&conf, "config.yml")
  // err := configor.New(&configor.Config{Debug: true}).Load(&conf, "config.yml")  // 测试模式,也可以通过环境变量开启测试模式(CONFIGOR_DEBUG_MODE=true go run main.go ),这样就无需修改代码
  //err := configor.New(&configor.Config{Verbose: true}).Load(&conf, "config.yml") // 模式,也可以通过环境变量开启详细模式(CONFIGOR_VERBOSE_MODE=true go run main.go ),这样就无需修改代码
  if err != nil {
    panic(err)
  }
  fmt.Printf("%v \n", conf)
}

开启 测试模式 or 详细模式


既可以在代码中显式开启,如 err := configor.New(&configor.Config{Debug: true}).Load(&conf, "config.yml")

也可以通过环境变量开启,如 CONFIGOR_DEBUG_MODE=true go run main.go


加载多个配置文件

// application.yml 的优先级 大于 database.json, 排在前面的配置文件优先级大于排在后的的配置
configor.Load(&Config, "application.yml", "database.json")

根据环境变量加载配置文件 or 从shell加载配置项


详细可参考 Golang Configor 配置文件工具


热更新

package main
import (
  "fmt"
  "time"
  "github.com/jinzhu/configor"
)
type Config struct {
  APPName string `default:"app name"`
  DB      struct {
    Name     string
    User     string `default:"root"`
    Password string `required:"true" env:"DBPassword"`
    Port     uint   `default:"3306"`
  }
  Contacts []struct {
    Name  string
    Email string `required:"true"`
  }
}
func main() {
  var conf = Config{}
  // reload模式,可实现热加载
  err := configor.New(&configor.Config{
    AutoReload:         true,
    AutoReloadInterval: time.Second,
    AutoReloadCallback: func(config interface{}) {
      // config发生变化后出发什么操作
      fmt.Printf("配置文件发生了变更%#v\n", config)
    },
  }).Load(&conf, "config.yml")
  // 无reload模式
  //err := configor.Load(&conf, "config.yml")
  // err := configor.New(&configor.Config{Debug: true}).Load(&conf, "config.yml")  // 测试模式,也可以通过环境变量开启测试模式(CONFIGOR_DEBUG_MODE=true go run main.go ),这样就无需修改代码
  //err := configor.New(&configor.Config{Verbose: true}).Load(&conf, "config.yml") // 模式,也可以通过环境变量开启详细模式(CONFIGOR_VERBOSE_MODE=true go run main.go ),这样就无需修改代码
  if err != nil {
    panic(err)
  }
  fmt.Printf("%v \n", conf)
  time.Sleep(100000e9)
}

微信截图_20230925172707.png

目录
相关文章
|
3月前
|
JSON 运维 Go
Go 项目配置文件的定义和读取
Go 项目配置文件的定义和读取
|
2月前
|
关系型数据库 MySQL Go
go抽取mysql配置到yaml配置文件
go抽取mysql配置到yaml配置文件
|
3月前
|
Java Go
go如何读取yaml配置文件?
本文介绍了如何在Go项目中利用YAML文件进行配置管理,以简化变量更改及维护工作。首先,通过`go get gopkg.in/yaml.v3`命令安装YAML处理库。接着,展示了如何创建并解析YAML配置文件,包括定义结构体映射YAML字段、读取文件内容以及错误处理等步骤。此外,还提供了通过Go代码生成YAML文件的方法。使用`gopkg.in/yaml.v3`库能够有效提升项目的可维护性和开发效率。
281 1
go如何读取yaml配置文件?
|
4月前
|
JSON Go API
go项目实现通过配置文件进行配置项统一管理
go项目实现通过配置文件进行配置项统一管理
30 0
|
5月前
|
监控 Go
go语言并发实战——日志收集系统(十一)基于etcd来监视配置文件的变化
go语言并发实战——日志收集系统(十一)基于etcd来监视配置文件的变化
|
5月前
|
消息中间件 Kafka Go
go语言并发实战——日志收集系统(五) 基于go-ini包读取日志收集服务的配置文件
go语言并发实战——日志收集系统(五) 基于go-ini包读取日志收集服务的配置文件
|
6月前
|
JSON Java Go
Go语言读取多种格式配置文件
Go语言读取多种格式配置文件
52 0
|
JSON Go 数据格式
Go 读取 YAML 配置文件的两种方式
本文介绍了读取 YAML 配置文件的两种方式,第一种是通过 yaml.v3 包,第二种是通过 viper 包。如果是在项目里解析配置文件,推荐使用 viper 包,它支持解析多种格式的配置文件,监听配置文件的更新,修改配置文件等。
1516 1
Go 读取 YAML 配置文件的两种方式
|
存储 JSON Go
Go语言微服务框架 - 2.实现加载静态配置文件
今天,我们先将重点放到加载配置文件库的技术选型,顺便分享一些常见的问题。
103 1
|
存储 XML JSON
嵌入式linux之go语言开发(十二)参数配置文件存储模块开发
嵌入式linux之go语言开发(十二)参数配置文件存储模块开发