当前位置: 首页 > news >正文

【Go】-viper库的使用

目录

viper简介

viper使用

通过viper.Set设置值

读取配置文件说明

读取配置文件

读取多个配置文件

读取配置项的值

读取命令行的值

io.Reader中读取值

写配置文件

WriteConfig() 和 SafeWriteConfig() 区别:


viper简介

         配置管理解析库,是由大神 Steve Francia 开发,他在google领导着 golang 的产品开发,他也是 gohugo.io 的创始人之一,命令行解析库 cobra 开发者。总之,他在golang领域是专家,很牛的一个人。

        viper是一个配置管理的解决方案,它能够从 json,toml,ini,yaml,hcl,env 等多种格式文件中,读取配置内容,它还能从一些远程配置中心读取配置文件,如consul,etcd等;它还能够监听文件的内容变化。

功能介绍

  • 读取 json,toml,ini,yaml,hcl,env 等格式的文件内容
  • 读取远程配置文件,如 consul,etcd 等和监控配置文件变化
  • 读取命令行 flag 的值
  • 从 buffer 中读取值

        配置文件又可以分为不同的环境,比如dev,test,prod等。读取配置文件的优先顺序,从高到低,如下:

  • 显式设置的Set函数
  • 命令行参数
  • 环境变量
  • 配置文件
  • 远程k-v 存储系统,如consul,etcd等
  • 默认值

Viper 配置key是不区分大小写的。

其实,上面的每一种文件格式,都有一些比较有名的解析库,如:

  • toml :GitHub - BurntSushi/toml: TOML parser for Golang with reflection.
  • json :json的解析库比较多,下面列出几个常用的
    • https://github.com/json-iterator/go
    • GitHub - mailru/easyjson: Fast JSON serializer for golang.
    • GitHub - bitly/go-simplejson: a Go package to interact with arbitrary JSON
    • GitHub - tidwall/gjson: Get JSON values quickly - JSON parser for Go
  • ini : GitHub - go-ini/ini: Package ini provides INI file read and write functionality in Go
    等等单独文件格式解析库。

        但是为什么要用viper,因为它是一个综合文件解析库,包含了上面所有的文件格式解析,是一个集合体,少了配置多个库的烦恼。


viper使用

安装viper命令:

go get github.com/spf13/viper

文档: viper/README.md at master · spf13/viper · GitHub

通过viper.Set设置值

        如果某个键通过viper.Set设置了值,那么这个值读取的优先级最高

viper.Set("mysql.info", "this is mysql info")

        同时还可以设置默认值

viper.SetDefault("ContentDir", "content") 
viper.SetDefault("LayoutDir", "layouts") 
viper.SetDefault("Taxonomies", map[string]string{"tag": "tags", "category": "categories"})

读取配置文件说明

        读取配置文件要求:最少要知道从哪个位置查找配置文件。用户一定要设置这个路径。viper可以从多个路径搜索配置文件,单个viper实例只支持单个配置文件。viper本身没有设置默认的搜索路径,需要用户自己设置默认路径。

viper.SetConfigName("config") // 配置文件的文件名,没有扩展名,如 .yaml, .toml 这样的扩展名 viper.SetConfigType("yaml") // 设置扩展名。在这里设置文件的扩展名。另外,如果配置文件的名称没有扩展名,则需要配置这个选项 
viper.AddConfigPath("/etc/appname/") // 查找配置文件所在路径 viper.AddConfigPath("$HOME/.appname") // 多次调用AddConfigPath,可以添加多个搜索路径 viper.AddConfigPath(".") // 还可以在工作目录中搜索配置文件 
err := viper.ReadInConfig() // 搜索并读取配置文件 
if err != nil { // 处理错误 panic(fmt.Errorf("Fatal error config file: %s \n", err)) 
}

说明:
        这里执行viper.ReadInConfig()之后,viper才能确定到底用哪个文件,viper按照上面的AddConfigPath() 进行搜索,找到第一个名为 config.ext (这里的ext代表扩展名: 如 json,toml,yaml,yml,ini,prop 等扩展名) 的文件后即停止搜索。

如果有多个名称为config的配置文件,viper怎么搜索呢?它会按照如下顺序搜索

  • config.json
  • config.toml
  • config.yaml
  • config.yml
  • config.properties (这种一般是java中的配置文件名)
  • config.props (这种一般是java中的配置文件名)

        你还可以处理一些特殊情况:

if err := viper.ReadInConfig(); err != nil { if _, ok := err.(viper.ConfigFileNotFoundError); ok { // 配置文件没有找到; 如果需要可以忽略 } else { // 查找到了配置文件但是产生了其它的错误 } 
} // 查找到配置文件并解析成功

        注意[自1.6起]:  你也可以有不带扩展名的文件,并以编程方式指定其格式。对于位于用户$HOME目录中的配置文件没有任何扩展名,如.bashrc。


读取配置文件

        config.toml 配置文件:

# this is a toml 
title = "toml exaples" 
redis = "127.0.0.1:3300" # redis 
[mysql] host = "192.168.1.1" ports = 3306 username = "root" password = "root123456"
package main 
import( "fmt" "github.com/spf13/viper" ) 
// 读取配置文件
config type Config struct { Redis string MySQL MySQLConfig 
} type MySQLConfig struct { Port int Host string Username string Password string 
} func main() { // 把配置文件读取到结构体上 var config Config viper.SetConfigName("config") viper.AddConfigPath(".") err := viper.ReadInConfig() if err != nil { fmt.Println(err) return } viper.Unmarshal(&config) //将配置文件绑定到config上 fmt.Println("config: ", config, "redis: ", config.Redis) 
}

读取多个配置文件

        在例子1基础上多增加一个json的配置文件,config3.json 配置文件:

{ "redis": "127.0.0.1:33000", "mysql": { "port": 3306, "host": "127.0.0.1", "username": "root", "password": "123456"                     } 
}
package main 
import ( "fmt" "github.com/spf13/viper" ) 
type Config struct { Redis string MySQL MySQLConfig 
} type MySQLConfig struct { Port int Host string Username string Password string 
} func main() { // 读取 toml 配置文件 var config1 Config vtoml := viper.New() vtoml.SetConfigName("config") vtoml.SetConfigType("toml") vtoml.AddConfigPath(".") if err := vtoml.ReadInConfig(); err != nil { fmt.Println(err) return } vtoml.Unmarshal(&config1) fmt.Println("read config.toml") fmt.Println("config: ", config1, "redis: ", config1.Redis) // 读取 json 配置文件 var config2 Config vjson := viper.New() vjson.SetConfigName("config3") vjson.SetConfigType("json") vjson.AddConfigPath(".") if err := vjson.ReadInConfig(); err != nil { fmt.Println(err) return } vjson.Unmarshal(&config2) fmt.Println("read config3.json") fmt.Println("config: ", config1, "redis: ", config1.Redis) 
}

运行:

$ go run viper_multi.go

read config.toml
config:  {127.0.0.1:33000 {0 192.168.1.1 root 123456}} redis:  127.0.0.1:33000
read config3.json
config:  {127.0.0.1:33000 {0 192.168.1.1 root 123456}} redis:  127.0.0.1:33000


读取配置项的值

        新建文件夹 item, 在里面创建文件 config.json,内容如下:

{ "redis": "127.0.0.1:33000", "mysql": { "port": 3306, "host": "127.0.0.1", "username": "root", "password": "123456", "ports": [ 5799, 6029 ], "metric": { "host": "127.0.0.1", "port": 2112 } } 
}
package mainimport ("fmt""github.com/spf13/viper"
)func main() {viper.SetConfigName("config")   // 配置文件名称(不包括扩展名)viper.SetConfigType("json")    // 配置文件类型viper.AddConfigPath(".")       // 配置文件搜索路径// 根据配置加载文件err := viper.ReadInConfig()if err != nil {fmt.Println(err)return}// 读取配置项host := viper.Get("mysql.host")username := viper.GetString("mysql.username")port := viper.GetInt("mysql.port")portsSlice := viper.GetIntSlice("mysql.ports")metricPort := viper.GetInt("mysql.metric.port")redis := viper.Get("redis")mysqlMap := viper.GetStringMapString("mysql")// 检查配置项是否设置if viper.IsSet("mysql.host") {fmt.Println("[IsSet()] mysql.host is set")} else {fmt.Println("[IsSet()] mysql.host is not set")}// 打印配置信息fmt.Println("mysql - host:", host, ", username:", username, ", port:", port)fmt.Println("mysql ports:", portsSlice)fmt.Println("metric port:", metricPort)fmt.Println("redis -", redis)fmt.Println("mysqlmap -", mysqlMap, ", username:", mysqlMap["username"])
}

运行:

$ go run viper_get_item.go

[IsSet()]mysql.host is set
mysql - host:  127.0.0.1 , username:  root , port:  3306
mysql ports : [5799 6029]
metric port:  2112
redis -  127.0.0.1:33000
mysqlmap -  map[host:127.0.0.1 metric: password:123456 port:3306 ports: username:root] , username:  root


读取命令行的值

viper获取值的方法:

  • Get(key string) : interface{}
  • GetBool(key string) : bool
  • GetFloat64(key string) : float64
  • GetInt(key string) : int
  • GetIntSlice(key string) : []int
  • GetString(key string) : string
  • GetStringMap(key string) : map[string]interface{}
  • GetStringMapString(key string) : map[string]string
  • GetStringSlice(key string) : []string
  • GetTime(key string) : time.Time
  • GetDuration(key string) : time.Duration
  • IsSet(key string) : bool
  • AllSettings() : map[string]interface{}

        新建文件夹 cmd,然后cmd文件夹里新建config.json文件:

{ 
"redis":{ "port": 3301, "host": "127.0.0.1" }, 
"mysql": { "port": 3306, "host": "127.0.0.1", "username": "root", "password": "123456" } 
}
package mainimport ("fmt""github.com/spf13/pflag""github.com/spf13/viper"
)func main() {// 使用pflag定义命令行参数pflag.Int("redis.port", 3302, "redis port")viper.BindPFlags(pflag.CommandLine) // 将pflag绑定到viperpflag.Parse()                     // 解析命令行参数// 使用viper加载配置文件viper.SetConfigName("config")   // 配置文件名称(不包括扩展名)viper.SetConfigType("json")    // 配置文件类型viper.AddConfigPath(".")       // 配置文件搜索路径// 根据配置加载文件err := viper.ReadInConfig()if err != nil {fmt.Println(err)return}// 读取配置项host := viper.Get("mysql.host")username := viper.GetString("mysql.username")port := viper.GetInt("mysql.port")redisHost := viper.GetString("redis.host")redisPort := viper.GetInt("redis.port")// 打印配置信息fmt.Println("mysql - host:", host, ", username:", username, ", port:", port)fmt.Println("redis - host:", redisHost, ", port:", redisPort)
}

1.不加命令行参数运行:

$ go run viper_pflag.go

mysql - host:  127.0.0.1 , username:  root , port:  3306
redis - host:  127.0.0.1 , port:  3301

        说明:redis.port 的值是 3301,是 config.json 配置文件里的值。

2.加命令行参数运行

$ go run viper_pflag.go --redis.port 6666

mysql - host:  127.0.0.1 , username:  root , port:  3306
redis - host:  127.0.0.1 , port:  6666

        说明:加了命令行参数 --redis.port 6666,这时候redis.port输出的值为 6666,读取的是cmd命令行的值


io.Reader中读取值

package mainimport ("bytes""fmt""github.com/spf13/viper"
)func main() {viper.SetConfigType("yaml") // 设置配置文件类型为yaml// 定义YAML配置内容var yaml = []byte(`Hacker: true
name: steve
hobbies:
- skateboarding
- snowboarding
- go
clothing:jacket: leathertrousers: denim
age: 35
eyes: brown
beard: true`)// 从字节切片中读取配置err := viper.ReadConfig(bytes.NewBuffer(yaml))if err != nil {fmt.Println(err)return}// 读取配置项hacker := viper.GetBool("Hacker")hobbies := viper.GetStringSlice("hobbies")jacket := viper.Get("clothing.jacket")age := viper.GetInt("age")// 打印配置信息fmt.Println("Hacker:", hacker, ", hobbies:", hobbies, ", jacket:", jacket, ", age:", age)
}

写配置文件

package mainimport ("fmt""github.com/spf13/viper"
)func main() {viper.SetConfigName("config") // 设置配置文件名称(不包括扩展名)viper.SetConfigType("yaml")  // 设置配置文件类型为yamlviper.AddConfigPath(".")     // 添加配置文件搜索路径// 直接设置配置项viper.Set("yaml", "this is an example of yaml")viper.Set("redis.port", 4405)viper.Set("redis.host", "127.0.0.1")viper.Set("mysql.port", 3306)viper.Set("mysql.host", "192.168.1.0")viper.Set("mysql.username", "root123")viper.Set("mysql.password", "root123")// 将配置写入文件if err := viper.WriteConfig(); err != nil {fmt.Println(err)}
}

运行:

$ go run viper_write_config.go

        没有任何输出表示生成配置文件成功


WriteConfig() 和 SafeWriteConfig() 区别:

        如果待生成的文件已经存在,那么SafeWriteConfig()就会报错,Config File "config.yaml" Already Exists, 而WriteConfig()则会直接覆盖同名文件。

相关文章:

【Go】-viper库的使用

目录 viper简介 viper使用 通过viper.Set设置值 读取配置文件说明 读取配置文件 读取多个配置文件 读取配置项的值 读取命令行的值 io.Reader中读取值 写配置文件 WriteConfig() 和 SafeWriteConfig() 区别: viper简介 配置管理解析库,是由大神 Steve Fr…...

JavaWeb酒店管理系统(详细版)

✅作者简介:2022年博客新星 第八。热爱国学的Java后端开发者,修心和技术同步精进。 🍎个人主页:Java Fans的博客 🍊个人信条:不迁怒,不贰过。小知识,大智慧。 💞当前专栏…...

C++ | 定长内存池 | 对象池

文章目录 C | 定长内存池 | 对象池一、内存池的引入二、代码中的内存池实现 - ObjectPool类(一)整体结构(二)内存分配 - New函数(三)内存回收 - Delete函数 三、内存池在TreeNode示例中的性能测试演示四、脱…...

python画图|自制渐变柱状图

在前述学习过程中,我们已经通过官网学习了如何绘制渐变的柱状图及其背景。 掌握一门技能的最佳检验方式就是通过实战,因此,本文尝试做一些渐变设计。 前述学习记录可查看链接: Python画图|渐变背景-CSDN博客 【1】柱状图渐变 …...

基于RPA+BERT的文档辅助“悦读”系统 | OPENAIGC开发者大赛高校组AI创作力奖

在第二届拯救者杯OPENAIGC开发者大赛中,涌现出一批技术突出、创意卓越的作品。为了让这些优秀项目被更多人看到,我们特意开设了优秀作品报道专栏,旨在展示其独特之处和开发者的精彩故事。 无论您是技术专家还是爱好者,希望能带给…...

K8S部署流程

一、war打包镜像(survey,analytics,trac系统) 代码打包成war准备tomcat的server.xml文件&#xff0c;修改connector中8080端口为项目的端口 修改前&#xff1a; <Connector port"8080" protocol"HTTP/1.1"connectionTimeout"20000"redirect…...

DevExpress WinForms中文教程:Data Grid - 如何添加或删除行?

本教程介绍DevExpress WinForm的Data Grid控件UI元素和API&#xff0c;它们使您和最终用户能够添加或删除数据行。您将首选学习如何启用内置的数据导航器&#xff0c;然后学习如何使用Microsoft Outlook启发的New Item行添加新记录。最后教程将向您展示基本的API&#xff0c;它…...

u盘格式化后数据能恢复吗?2024年Top4恢复神器来帮忙

在这个电脑和手机满天飞的时代&#xff0c;U盘是我们用来存东西和传文件的得力助手&#xff0c;特别重要。但是&#xff0c;有时候U盘可能会不小心被格式化了&#xff0c;里面的重要文件就不见了。那么&#xff0c;U盘格式化后的数据还能恢复吗&#xff1f;当然可以。今天会告诉…...

深度学习·Argparse

Argparse 命令行选项、参数和子命令解析器 ArgumentParser 命令行传参数->解析参数->获得对应参数 初始化&#xff1a;parser argparse.ArgumentParser(descriptionxxx)添加命令行参数&#xff1a; parser.add_argument("--training_filepath", typestr, he…...

制造企业为何需要PLM系统?PLM系统解决方案对制造业重要性分析

制造企业为何需要PLM系统&#xff1f;PLM系统解决方案对制造业重要性分析 新华社9月23日消息&#xff0c;据全国组织机构统一社会信用代码数据服务中心统计&#xff0c;我国制造业企业总量突破600万家。数据显示&#xff0c;2024年1至8月&#xff0c;我国制造业企业数量呈现稳…...

http协议中的header详细讲解

http协议中的header详细讲解 HTTP 协议和 TCP/IP 协议族内的其他众多的协议相同&#xff0c;用于客户端和服务器之间的通信。 请求访问文本或图像等资源的一端称为客户端&#xff0c;而提供资源响应的一端称为服务器端。 HTTP 协议规定&#xff0c;请求从客户端发出&#xf…...

探索后量子安全:基于格加密技术的未来密码学展望

在信息技术日新月异的今天&#xff0c;量子计算作为下一代计算技术的代表&#xff0c;正逐步从理论走向实践。量子计算的出现对现有的加密体系构成了严重威胁&#xff0c;尤其是基于大数分解和离散对数难题的传统密码学&#xff08;如RSA和Diffie-Hellman协议&#xff09;。为了…...

WPF之UI进阶--完整了解wpf的控件和布局容器及应用

前面三篇有关WPF的基础介绍&#xff0c;分别介绍了wpf与winform的异同&#xff0c;wpf的事件生成和使用以及数据绑定。但我们还缺乏一副好的“皮囊”&#xff0c;所以从这篇开始我们来开始学习wpf的UI相关的内容&#xff0c;首当其冲的就是布局容器。 其实我们知道&#xff0c;…...

unity一键注释日志和反注释日志

开发背景&#xff1a;游戏中日志也是很大的开销&#xff0c;虽然有些日志不打印但是毕竟有字符串的开销&#xff0c;甚至有字符串拼接的开销&#xff0c;有些还有装箱和拆箱的开销&#xff0c;比如Debug.Log(1) 这种 因此需要注释掉&#xff0c;当然还需要提供反注释的功能&am…...

VBA数据库解决方案第十五讲:Recordset集合中单个数据的精确处理

《VBA数据库解决方案》教程&#xff08;版权10090845&#xff09;是我推出的第二套教程&#xff0c;目前已经是第二版修订了。这套教程定位于中级&#xff0c;是学完字典后的另一个专题讲解。数据库是数据处理的利器&#xff0c;教程中详细介绍了利用ADO连接ACCDB和EXCEL的方法…...

甄选范文“论软件需求管理”,软考高级论文,系统架构设计师论文

论文真题 软件需求管理是一个对系统需求变更了解和控制的过程。需求管理过程与需求开发过程相互关联,初始需求导出的同时就要形成需求管理规划,一旦启动了软件开发过程,需求管理活动就紧密相伴。 需求管理过程中主要包含变更控制、版本控制、需求跟踪和需求状态跟踪等4项活…...

Android Studio Dolphin 中Gradle下载慢的解决方法

我用的版本Android Studio Dolphin | 2021.3.1 Patch 1 1.Gradle自身的版本下载慢 解决办法&#xff1a;修改gradle\wrapper\gradle-wrapper.properties中的distributionUrl 将https\://services.gradle.org/distributions为https\://mirrors.cloud.tencent.com/gradle dis…...

Excel实现省-市-区/县级联

数据准备 准备省份-城市映射数据&#xff0c;如下&#xff1a; 新建sheet页&#xff0c;命名为&#xff1a;省-市数据源&#xff0c;然后准备数据&#xff0c;如下所示&#xff1a; 准备城市-区|县映射数据&#xff0c;如下&#xff1a; 新建sheet页&#xff0c;命名为&#x…...

【优化代码结构】函数的参数归一化

某些封装的函数&#xff0c;其参数具有多样性&#xff0c;会导致函数中会增加非常多的分支&#xff0c;比如下面这个 format 函数有如下几种参数方式&#xff0c;其中 formatter 会有很多种情况 date&#xff1a;日期对象formatter&#xff1a; ‘date’&#xff1a;格式化日期…...

CSS中height设置100vh和100%的区别

文章目录 CSS中height设置100vh和100%的区别一、引言二、高度设置的区别1、100%1.1、父元素高度固定1.2、父元素高度未定义 2、100vh2.1、视口高度2.2、不受父元素限制 三、总结 CSS中height设置100vh和100%的区别 一、引言 在前端开发中&#xff0c;我们经常需要设置元素的高…...

[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?

&#x1f9e0; 智能合约中的数据是如何在区块链中保持一致的&#xff1f; 为什么所有区块链节点都能得出相同结果&#xff1f;合约调用这么复杂&#xff0c;状态真能保持一致吗&#xff1f;本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里&#xf…...

逻辑回归:给不确定性划界的分类大师

想象你是一名医生。面对患者的检查报告&#xff08;肿瘤大小、血液指标&#xff09;&#xff0c;你需要做出一个**决定性判断**&#xff1a;恶性还是良性&#xff1f;这种“非黑即白”的抉择&#xff0c;正是**逻辑回归&#xff08;Logistic Regression&#xff09;** 的战场&a…...

《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》

在注意力分散、内容高度同质化的时代&#xff0c;情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现&#xff0c;消费者对内容的“有感”程度&#xff0c;正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中&#xff0…...

linux 错误码总结

1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...

屋顶变身“发电站” ,中天合创屋面分布式光伏发电项目顺利并网!

5月28日&#xff0c;中天合创屋面分布式光伏发电项目顺利并网发电&#xff0c;该项目位于内蒙古自治区鄂尔多斯市乌审旗&#xff0c;项目利用中天合创聚乙烯、聚丙烯仓库屋面作为场地建设光伏电站&#xff0c;总装机容量为9.96MWp。 项目投运后&#xff0c;每年可节约标煤3670…...

跨链模式:多链互操作架构与性能扩展方案

跨链模式&#xff1a;多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈&#xff1a;模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展&#xff08;H2Cross架构&#xff09;&#xff1a; 适配层&#xf…...

【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】

1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件&#xff08;System Property Definition File&#xff09;&#xff0c;用于声明和管理 Bluetooth 模块相…...

【Web 进阶篇】优雅的接口设计:统一响应、全局异常处理与参数校验

系列回顾&#xff1a; 在上一篇中&#xff0c;我们成功地为应用集成了数据库&#xff0c;并使用 Spring Data JPA 实现了基本的 CRUD API。我们的应用现在能“记忆”数据了&#xff01;但是&#xff0c;如果你仔细审视那些 API&#xff0c;会发现它们还很“粗糙”&#xff1a;有…...

ElasticSearch搜索引擎之倒排索引及其底层算法

文章目录 一、搜索引擎1、什么是搜索引擎?2、搜索引擎的分类3、常用的搜索引擎4、搜索引擎的特点二、倒排索引1、简介2、为什么倒排索引不用B+树1.创建时间长,文件大。2.其次,树深,IO次数可怕。3.索引可能会失效。4.精准度差。三. 倒排索引四、算法1、Term Index的算法2、 …...

力扣-35.搜索插入位置

题目描述 给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 class Solution {public int searchInsert(int[] nums, …...