当前位置: 首页 > 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;我们经常需要设置元素的高…...

【网络】每天掌握一个Linux命令 - iftop

在Linux系统中&#xff0c;iftop是网络管理的得力助手&#xff0c;能实时监控网络流量、连接情况等&#xff0c;帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...

XCTF-web-easyupload

试了试php&#xff0c;php7&#xff0c;pht&#xff0c;phtml等&#xff0c;都没有用 尝试.user.ini 抓包修改将.user.ini修改为jpg图片 在上传一个123.jpg 用蚁剑连接&#xff0c;得到flag...

从零实现富文本编辑器#5-编辑器选区模型的状态结构表达

先前我们总结了浏览器选区模型的交互策略&#xff0c;并且实现了基本的选区操作&#xff0c;还调研了自绘选区的实现。那么相对的&#xff0c;我们还需要设计编辑器的选区表达&#xff0c;也可以称为模型选区。编辑器中应用变更时的操作范围&#xff0c;就是以模型选区为基准来…...

JVM垃圾回收机制全解析

Java虚拟机&#xff08;JVM&#xff09;中的垃圾收集器&#xff08;Garbage Collector&#xff0c;简称GC&#xff09;是用于自动管理内存的机制。它负责识别和清除不再被程序使用的对象&#xff0c;从而释放内存空间&#xff0c;避免内存泄漏和内存溢出等问题。垃圾收集器在Ja…...

质量体系的重要

质量体系是为确保产品、服务或过程质量满足规定要求&#xff0c;由相互关联的要素构成的有机整体。其核心内容可归纳为以下五个方面&#xff1a; &#x1f3db;️ 一、组织架构与职责 质量体系明确组织内各部门、岗位的职责与权限&#xff0c;形成层级清晰的管理网络&#xf…...

如何在网页里填写 PDF 表格?

有时候&#xff0c;你可能希望用户能在你的网站上填写 PDF 表单。然而&#xff0c;这件事并不简单&#xff0c;因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件&#xff0c;但原生并不支持编辑或填写它们。更糟的是&#xff0c;如果你想收集表单数据&#xff…...

C#学习第29天:表达式树(Expression Trees)

目录 什么是表达式树&#xff1f; 核心概念 1.表达式树的构建 2. 表达式树与Lambda表达式 3.解析和访问表达式树 4.动态条件查询 表达式树的优势 1.动态构建查询 2.LINQ 提供程序支持&#xff1a; 3.性能优化 4.元数据处理 5.代码转换和重写 适用场景 代码复杂性…...

嵌入式学习之系统编程(九)OSI模型、TCP/IP模型、UDP协议网络相关编程(6.3)

目录 一、网络编程--OSI模型 二、网络编程--TCP/IP模型 三、网络接口 四、UDP网络相关编程及主要函数 ​编辑​编辑 UDP的特征 socke函数 bind函数 recvfrom函数&#xff08;接收函数&#xff09; sendto函数&#xff08;发送函数&#xff09; 五、网络编程之 UDP 用…...

相关类相关的可视化图像总结

目录 一、散点图 二、气泡图 三、相关图 四、热力图 五、二维密度图 六、多模态二维密度图 七、雷达图 八、桑基图 九、总结 一、散点图 特点 通过点的位置展示两个连续变量之间的关系&#xff0c;可直观判断线性相关、非线性相关或无相关关系&#xff0c;点的分布密…...

ThreadLocal 源码

ThreadLocal 源码 此类提供线程局部变量。这些变量不同于它们的普通对应物&#xff0c;因为每个访问一个线程局部变量的线程&#xff08;通过其 get 或 set 方法&#xff09;都有自己独立初始化的变量副本。ThreadLocal 实例通常是类中的私有静态字段&#xff0c;这些类希望将…...