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

news2024/11/26 4:45:01

比较有名的方案有

使用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.coloradmin.cn/o/941107.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

博客系统后台前端UI设计

效果展示 API编写 index.js import axios from "./request"const fastdfs {delete: file/fastdfs/delete } const permission {search: "/sys/permission/search",add: "/sys/permission/add",update: "/sys/permission/update",d…

适合新手程序员的体质,一键代码审查轻松搞定

很多刚入行的程序员会面临一个问题,写完代码进行运行会出现很多bug但是不能准确的定位问题的所在,很多人对于自己的代码结构和层次也摸不着头脑,为了提高代码的质量经常会消耗大量的人力物力来做这件事情。 在(软件工程的事实与谬…

阻塞io读取内核驱动变量值

应用程序&#xff1a; #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <string.h> #include <sys/ioctl.h> #include "head.…

Spring MVC 四:Context层级

这一节我们来回答上篇文章中避而不谈的有关什么是RootApplicationContext的问题。 这就需要引入Spring MVC的有关Context Hierarchy的问题。Context Hierarchy意思就是Context层级&#xff0c;既然说到Context层级&#xff0c;说明在Spring MVC项目中&#xff0c;可能存在不止…

Linux学习之DNS服务的原理

DNS服务一些理论 域名系统&#xff08;Domain Name System&#xff0c;DNS&#xff09;是互联网的核心应用服务&#xff0c;可以通过IP地址查询到域名&#xff0c;也可以通过域名查询到IP地址。 FQDN&#xff08;Full Qualified Domain Name&#xff09;是完全限定域名&#xf…

解决FreeRTOS程序跑不起来,打印调试却提示“Error:..\FreeRTOS\port\RVDS\ARM_CM3\port.c,244“的方法

前言 今天来分享一个不会造成程序编译报错&#xff0c;但会使程序一直跑不起来&#xff0c;并且通过调试会发现有输出错误提示的错误例子分析&#xff0c;话不多说&#xff0c;我们就直接开始分析~ 首先&#xff0c;我们说过这个例子在编译时候没有明示的错误提示&#xff0c…

Excel·VBA二维数组组合函数、组合求和

目录 1&#xff0c;二维数组组合函数举例 2&#xff0c;组合求和 之前的文章《ExcelVBA数组组合函数、组合求和》和《ExcelVBA数组排列函数》&#xff0c;都是针对一维数组的组合和排列 二维数组组合&#xff1a;对一个m行*n列的二维数组&#xff0c;每行抽取1个元素进行组合&a…

[管理与领导-56]:IT基层管理者 - 扩展技能 - 1 - 时间管理 -3- 帮助下属提升效能(辅导与激励)

前言&#xff1a; 对下属的辅导是管理者一个重要的职责&#xff0c;帮助下属者提升时间效能也辅导下属的一个职责。 管理者与下属&#xff0c;管理者与团队是“共生”的关系&#xff0c;管理者提升下属的时间效能&#xff0c;就是提升团队的效能&#xff0c;也就是提升了自己…

计算机视觉-LeNet

目录 LeNet LeNet在手写数字识别上的应用 LeNet在眼疾识别数据集iChallenge-PM上的应用 LeNet LeNet是最早的卷积神经网络之一。1998年&#xff0c;Yann LeCun第一次将LeNet卷积神经网络应用到图像分类上&#xff0c;在手写数字识别任务中取得了巨大成功。LeNet通过连续使用…

Linux各类性能分析工具用法详解

文章目录 静态性能分析工具文件系统观测工具虚拟文件系统(VFS)分析工具磁盘管理工具进程资源占用监测系统库调用分析工具网络配置防火墙配置多路径配置进程调度系统命令操作查看硬件信息磁盘管理网络端口硬件信息 监测工具内核调用监测系统调用监测系统函数调用监测系统性能监测…

【VRTK4.0运动专题】轴移动AxisMove(真实身体的移动)

文章目录 1、概览2、释义3、属性设置 1、概览 2、释义 “竖直轴”控制的行为“水平轴”控制的行为1Vertical-Slide 滑动Horizontal-Slide 滑动2Vertical-Slide 滑动Horizontal-SmoothRotate 转动3Vertical-Slide 滑动Horizontal-SnapRotate 转动&#xff08;不连续&#xff09…

PDF制作成翻页电子书

在日常工作中&#xff0c;大部分人使用的都是PDF文档发送给客户&#xff0c;但是PDF文档通常是静态的&#xff0c;缺乏交互性和视觉吸引力。那你有没有想过把它转换成翻页的电子书呢&#xff1f; 小编将告诉你操作步骤&#xff0c;非常简单 1.搜索FLBOOK在线制作电子杂志平台 …

零基础搭建个人影音媒体平台,实现远程访问Jellyfin播放器的简易方法

文章目录 1. 前言2. Jellyfin服务网站搭建2.1. Jellyfin下载和安装2.2. Jellyfin网页测试 3.本地网页发布3.1 cpolar的安装和注册3.2 Cpolar云端设置3.3 Cpolar本地设置 4.公网访问测试5. 结语 1. 前言 随着移动智能设备的普及&#xff0c;各种各样的使用需求也被开发出来&…

静态树提升对Vue生态系统的影响和发展

文章目录 1. 了解Vue 3的静态树提升介绍Vue 3的基本概念和优势解释静态树提升的作用和目标 2. 什么是静态树&#xff1f;解释静态树的概念和特点比较静态树和动态树的区别 3. Vue 3中的静态树提升解释Vue 3中静态树提升的原理和工作方式强调静态树提升对性能的影响和优化效果 4…

【Vue3】transition 组件

1. 基础用法 <template><div class"content"><button click"flag !flag">switch</button><transition name"fade"><div v-if"flag" class"box"></div></transition><…

javacv基础04-图像色彩空间转换函数Imgproc.cvtColor()(彩图转灰度图示例)

opencv python 实现方式参考 opencv-19 图像色彩空间转换函数cv2.cvtColor() javacv 中的函数 Imgproc.cvtColor(image, grey, Imgproc.COLOR_BGR2GRAY); 参数说明&#xff1a; image: 原始图像新灰度图转换参数&#xff1a;多种转换方式参考上面链接地址内容 javacv 实现方式…

K8s的Pod出现Init:ImagePullBackOff问题的解决(以calico为例)

对于这类问题的解决思路应该都差不多&#xff0c;本文以calico插件安装为例&#xff0c;发现有个Pod的镜像没有pull成功 第一步&#xff1a;查看这个pod的描述信息 kubectl describe pod calico-node-wmhrw -n kube-system 从上图发现是docker拉取"calico/cni:v3.15.1&q…

鸿蒙是一个怎么样的操作系统,真的是安卓套壳吗?

从鸿蒙项目正式推出以来&#xff0c;就一直有各自声音&#xff0c;有看好的&#xff0c;认为鸿蒙的出现将会成为一个智能终端设备操作系统的框架和平台&#xff0c;促进万物互联产业的繁荣发展&#xff1b;也有的人在唱衰&#xff0c;觉得鸿蒙发展不起来&#xff0c;甚至认为鸿…

rknn_toolkit以及rknpu环境搭建-rv1126

rknn_toolkit安装------------------------------------------------------------------------------- 环境要求&#xff1a;ubutu18.04 建议使用docker镜像 安装docker 参考https://zhuanlan.zhihu.com/p/143156163 镜像地址 百度企业网盘-企业云盘-企业云存储解决方案-同…

http请求方式过滤器与拦截器的区别

get:获取查询数据(查询)post:数据的提交&#xff0c;新增操作(增加)put:向服务端发送数据、改变信息&#xff0c;侧重点在于对数据的修改操作delete:数据库数据的删除head:一般用来判断类型、根据返回状态确定资源是否存在、资源是否更新以及更新的时间等 过滤器与拦截器的区别…