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


比较有名的方案有

使用viper管理配置[1]


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

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

  • 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"))
}
alt

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

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




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


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

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

alt



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


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"))
}
alt



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)
}
alt

Go viper 配置文件读取工具[3]

动态获取配置文件(viper)[4]




configor[5]


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

出自jinzhu大佬[6]

alt

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 配置文件工具[7]


热更新


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)
}
alt

完整代码[8]

参考资料

[1]

使用viper管理配置: https://cloud.tencent.com/developer/article/1540672

[2]

viper: https://github.com/spf13/viper

[3]

Go viper 配置文件读取工具: https://cloud.tencent.com/developer/article/1677426

[4]

动态获取配置文件(viper): https://segmentfault.com/a/1190000022828484

[5]

configor: https://github.com/jinzhu/configor

[6]

jinzhu大佬: https://github.com/jinzhu

[7]

Golang Configor 配置文件工具: https://www.jianshu.com/p/f826d2cc361b

[8]

完整代码: https://github.com/cuishuang/config-demo

本文由 mdnice 多平台发布

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/96432.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

学生宿舍水电费自动缴费系统/基于javaweb的水电缴费系统

摘 要 “互联网”的战略实施后,很多行业的信息化水平都有了很大的提升。但是目前很多学校日常工作仍是通过人工管理的方式进行,需要在各个岗位投入大量的人力进行很多重复性工作,这样就浪费了许多的人力物力,工作效率较低&#x…

Vue框架--Vue中el和data的两种写法

data与el的2种写法 1.el有2种写法 (1).new Vue时候配置el属性。 (2).先创建Vue实例,随后再通过vm.$mount(#root)指定el的值。 2.data有2种写法 (1).对象式 (2).函数式 如何选择:目前哪种写法都可以,以后学习到组件时&#xff…

前端面试中Vue的有经典面试题二

7. Vue中给data中的对象属性添加一个新的属性时会发生什么,如何解决? 示例: 点击button会发现, obj.b 已经成功添加,但是视图并未刷新: 原因在于在Vue实例创建时, obj.b 并未声明,因…

IIR滤波器

IIR滤波器原理 IIR的特点是:非线性相位、消耗资源少。 IIR滤波器的系统函数与差分方程如下所示: 由差分方程可知IIR滤波器存在反馈,因此在FPGA设计时要考虑到有限字长效应带来的影响。差分方程中包括两个部分:输入信号x(n)的M节…

Swift 中的动态成员查找

文章目录 前言基础介绍基础示例1. 定义一个动态成员访问类:2. 访问嵌套动态成员: 使用 KeyPath 的编译时安全性KeyPath 用法示例KeyPath 进阶使用示例1. 动态访问属性:2. 结合可选属性和 KeyPath:3. 动态 KeyPath 和字典&#xff…

文本标注技术方案(NLP标注工具)

Doccano doccano 是一个面向人类的开源文本注释工具。它为文本分类、序列标记和序列到序列任务提供注释功能。您可以创建用于情感分析、命名实体识别、文本摘要等的标记数据。只需创建一个项目,上传数据,然后开始注释。您可以在数小时内构建数据集。 支持…

深入了解Kubernetes(k8s):安装、使用和Java部署指南(持续更新中)

目录 Docker 和 k8s 简介1、kubernetes 组件及其联系1.1 Node1.2 Pod1.3 Service 2、安装docker3、单节点 kubernetes 和 KubeSphere 安装3.1 安装KubeKey3.2 安装 kubernetes 和 KubeSphere3.3 验证安装结果 4、集群版 kubernetes 和 KubeSphere 安装5、kubectl 常用命令6、资…

如何在小红书进行学习直播

诸神缄默不语-个人CSDN博文目录 因为我是从B站开始的,所以一些直播常识型的东西请见我之前写的如何在B站进行学习直播这一篇。 本篇主要介绍一些小红书之与B站不同之处。 小红书在手机端是可以直接点击“”选择直播的。 文章目录 1. 电脑直播-小红书直播软件2. 电…

每日一题 1921. 消灭怪物的最大数量

难度:中等 思路: 已知速度和距离,可求时间必定先消灭时间最短的怪物求得时间数组排序,只要在第 i 秒时,time[i] > i ,那么就可以消灭第 i 个怪物 代码: class Solution:def eliminateMax…

deque容器

1 deque容器基本概念 功能: 双端数组,可以对头端进行插入删除操作 deque与vector区别: vector对于头部的插入删除效率低,数据量越大,效率越低deque相对而言,对头部的插入删除速度回比vector快vector访问…

如何利用开源工具搭建AI大模型底座

开源社区是技术发展的一个重要部分,对于AI大模型来说,也是如此。 我们在这篇文章中来尝试通过开源工具来构建AI大模型的底座,涉及到的技术包括: LangchainOpenAIFlowiseLocalAILlama 使用Langchain构建第一个对话应用 如果你使…

udig下载、安装及汉化,生成geoserver图层样式sld文件

uDig是一款开源免费的桌面地理信息系统框架软件。uDig汉化版主要采用RCP技术构建,内置的多专业的水文工具,拥有复杂专业的分析能力,既可以作为独立程序运行,还可以作为插件使用。 uDig是一个 open source (EPL and BSD) 桌面应用程…