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.rhkb.cn/news/109569.html

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

相关文章

Linux驱动 - 20230828

练习. 驱动代码&#xff1a; #include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/cdev.h> #include <linux/device.h> #include <linux/slab.h> #include <linux/uaccess.h> #include <…

Viobot硬件组成和接口

Viobot主要由主板、传感器板和外壳组成。具体尺寸可以在用户手册上面找到。 传感器板上面是双目摄像头和IMU&#xff0c;摄像头已经打了胶固定在外壳的前脸&#xff0c;由于涉及到传感器外参标定&#xff0c;所以不可自行拆卸。 LED补光灯版本&#xff1a; TOF版本&#xff1a…

2023-8-28 n-皇后问题

题目链接&#xff1a;n-皇后问题 第一种搜索顺序 #include <iostream>using namespace std;const int N 20;int n; char g[N][N]; bool row[N], col[N], dg[N], udg[N];void dfs(int x, int y, int s) {if(y n) y 0, x ;if(x n){if(s n){for(int i 0; i < n;…

【 Python 全栈开发 - 人工智能篇 - 45 】集成算法与聚类算法

文章目录 一、集成算法1.1 概念1.2 常用集成算法1.2.1 Bagging1.2.2 Boosting1.2.2.1 AdaBoost1.2.2.2 GBDT1.2.2.3 XgBoost 1.2.3 Stacking 二、聚类算法2.1 概念2.2 常用聚类算法2.2.1 K-means2.2.2 层次聚类2.2.3 DBSCAN算法2.2.4 AP聚类算法2.2.5 高斯混合模型聚类算法 一、…

第三方系统访问微搭低代码的后端API

微搭低代码使用的是文档型数据库&#xff0c;有时候会有数据集成的需求&#xff0c;将低代码的数据库中的数据抽取到第三方系统中。本篇就介绍一下本地抽取数据以及云函数中抽取数据 1 本地抽取数据 本地抽取数据我们以nodejs作为后端服务&#xff0c;本机要求先安装好nodejs…

构建数据可视化(基于Echarts,python)

构建数据可视化&#xff08;基于Echarts,python) 本文目录&#xff1a; 一、写在前面的题外话 二、数据可视化概念 三、用Python matplotlib库绘制数据可视化图 四、基于Echarts构建大数据可视化 4.1、安装echarts.js 4.2、数据可视化折线图制作 4.2.1、基础折线图 4.2…

JWT-Token

一、JWT 需要在 HTTP 这种无状态的机制下&#xff0c;记录下&#xff08;标识&#xff09;出来是不是连续&#xff08;逻辑上的连续&#xff09;的请求。 思路&#xff1a;如果多次请求&#xff0c;携带了相同的标识型数据&#xff0c;则认为是逻辑上连续的。这个标识&#xff…

【数据结构】 栈与队列的相互实现

文章目录 &#x1f30f;引言&#x1f340;[队列实现栈](https://leetcode.cn/problems/implement-stack-using-queues/)&#x1f431;‍&#x1f3cd;题目描述&#xff1a;&#x1f4cc;注意事项&#xff1a;&#x1f4cc;示例与提示&#xff1a; &#x1f431;‍&#x1f409;…

云计算在线实训系统建设方案

一、 人工智能与云计算系统概述 人工智能&#xff08;Artificial Intelligence&#xff0c;简称AI&#xff09;是一种模拟人类智能的科学和工程&#xff0c;通过使用计算机系统来模拟、扩展和增强人类的智能能力。人工智能涉及多个领域&#xff0c;包括机器学习、深度学习、自然…

2023全国大学生数学建模竞赛B题思路模型代码

目录 一.思路模型见文末名片&#xff0c;比赛开始9.7晚上第一时间更新 二.国赛常用算法之随机森林 3.思路获取见此 一.思路模型见文末名片&#xff0c;比赛开始9.7晚上第一时间更新 二.国赛常用算法之随机森林 # -*- coding: utf-8 -*- """ author: Administ…

实用调试技巧(1)

文章目录 目录1. 什么是bug&#xff1f;2. 调试是什么&#xff1f;有多重要&#xff1f;2.1 调试是什么&#xff1f;2.2 调试的基本步骤2.3 Debug和Release的介绍 3. Windows环境调试介绍3.1 调试环境的准备3.2 学会快捷键3.3 调试的时候查看程序当前信息3.3.1 查看临时变量的值…

身为程序员,你有哪些提高写代码效率的工具?

首先&#xff0c;每个程序员都是会利用工具的人&#xff0c;也有自己囊里私藏的好物。独乐乐不如众乐乐&#xff0c;今天笔者整理了3个辅助我们写代码的黑科技&#xff0c;仅供参考。如果你有更好的工具&#xff0c;欢迎评论区分享。 1、Google/Stackoverflow——搜索解决方案的…

作为Android 开发,该如何写好自己的简历……

不管在那个行业、那家公司、那个岗位&#xff0c;你去应聘面试&#xff0c;首先人家第一眼看的肯定是你的简历&#xff0c;先通过简历来初步判断你的能力&#xff0c;再到通知面试进行考察。所以简历是一张很重要牌&#xff0c;就看你怎么打了&#xff1f; 笔者本科毕业&#…

Vue2向Vue3过度Vuex核心概念mutations

目录 1 核心概念-mutations1.定义mutations2.格式说明3.组件中提交 mutations4.练习5.总结 2 带参数的 mutations1.目标&#xff1a;2.语法2.1 提供mutation函数&#xff08;带参数&#xff09;2.2 提交mutation 3 练习-mutations的减法功能1.步骤2.代码实现 4 练习-Vuex中的值…

EXSI技术--Exsi简介与安装

1.EXSI简介 了解可直接安装到您的物理服务器的、可靠的裸机 Hypervisor。通过直接访问并控制底层资源,VMware ESXi可有效地对硬件进行分区,以便整合应用并降低成本。它是业界领先的高效体系架构,在可靠性、性能和支持方面树立了行业标杆。(裸金属架构) VMware vSphere的虚拟…

Python科研绘图--Task05

目录 SciencePlots 安装SciencePlots 安装LaTeX ① 安装 MikTex 和 Ghostscript ② 将软件的安装路径添加到系统环境变量中 SciencePlots 绘图示例 SciencePlots 虽然 Matplotlib 或 ProPlot 库能够绘制出插图结果&#xff0c;但用户还需要根据期刊的配图绘制要求进行…

Ubuntu22.04安装中文输入法►由踩坑到上岸版◄

Ubuntu22.04安装中文输入法►由踩坑到上岸版◄ 了解入坑上岸 更新一发&#xff1a;Gedit中文乱码问题的解决 为了方便回忆和记录甚至后面继续重装系统&#xff0c;我还是写一下以便将来用到或参考&#xff5e; 了解 安装Ubuntu22.04&#xff08;截至2023年08月26日11&#xff…

打开软件报错mfc100u.dll缺失是什么意思?简单式修复mfc100u.dll问题

首先&#xff0c;我们需要了解什么是MFC100U.dll文件以及它的作用。MFC100U.dll是一个Microsoft Foundation Class (MFC)库文件&#xff0c;它是Visual C应用程序开发的一部分。MFC库提供了许多通用的功能&#xff0c;如窗口管理、消息处理等&#xff0c;可以帮助开发者更快速地…

CC++ 常用技巧

C 中的C C 是面向过程的是把整个大程序分为一个个的子函数&#xff1b;C 是面向对象的是把整个程序划分为一个个的类。C 是完全兼容C 的&#xff0c;C 是C 的子集&#xff0c;C 是C 的超集。C 又对C 做了很多补充和提升&#xff0c;因此使用C 会比使用纯C 更方便。混用C和C&am…

springboot的mybatis问题

自动映射 在数据库列名和java类属性名相同的情况&#xff0c;mybatis会自动将数据库的值自动匹配到java类的属性当中。 java的price等变量 mysql的price等字段 mybatis会自动将数据库的值自动匹配到java类的属性当中。 开启驼峰命名 在application中配置 mybatis:type-…