Go语言项目中使用Viper获取配置信息详解

 更新时间:2024年04月02日 08:15:24   作者:仍沫  
Viper是Go应用的完整配置解决方案,它能处理所有类型的配置需求和配置格式,这篇文章主要介绍了Go项目中使用Viper获取配置信息,需要的可以参考下

Viper是Go应用的完整配置解决方案,它能处理所有类型的配置需求和配置格式,支持:

  • 设置默认值。
  • 读取JSON、TOML、YAML、HCL、envfile和Java属性配置文件。
  • 实时观察和重新读取配置(可选)。
  • 读取环境变量。
  • 从远程配置系统(etcd或Consul)读取配置,并观察其变化。
  • 从命令行标志读取配置。
  • 从缓冲区读取配置。
  • 设置显式值。

Viper的优先级顺序是:显式调用Set > 命令行标志 > 环境变量 > 配置文件 > 键/值 存储(etcd或Consul) > 默认值。

本文主要记录从yaml文件、环境变量中获取配置。

使用的go的版本和viper的版本如下:

go 1.20
github.com/spf13/viper v1.18.2

从配置文件中获取配置

执行以下指令初始化项目:

go mod init github.com/rengmo/practicego

执行以下语句安装viper包:

go get github.com/spf13/viper

创建yaml文件config/dev.yaml,内容如下:

redis:
  host: localhost
  port: 6379
  user:     # 本地redis的用户名为空
  password: abc123
  db: 0

创建Go文件infrastructure/config.go,内容如下:

package config

import (
	"fmt"

	"github.com/spf13/viper"
)

func init() {
	// 设置配置文件的名字
	viper.SetConfigName("dev")
	// 设置文件的格式
	viper.SetConfigType("yaml")
	// 设置查找配置文件的路径为当前路径 . 表示项目的工作目录,也就是main.go同级的那个目录
	viper.AddConfigPath("./config")

	// 读取配置文件中的数据到viper中
	err := viper.ReadInConfig()
	if err != nil {
		panic(err)
	}
	// 从viper中获取配置数据
	redisPort := viper.Get("redis.port")
	fmt.Printf("redisPort: %v \n", redisPort) // 打印结果:redisPort: 6379 
}

还可以使用GetInt()、GetString()等方法,获取指定类型的数据,viper.GetString("redis.port")就是获取字符串类型的数据。

创建main.go文件,内容如下:

package main

import (
	_ "github.com/rengmo/practicego/infrastructure"
)

func main() {

}

运行代码,会打印出获取到的端口号。

	// 设置配置文件的名字
	viper.SetConfigName("dev")
	// 设置文件的格式
	viper.SetConfigType("yaml")
	// 设置查找配置文件的路径为当前路径 . 表示项目的工作目录,也就是main.go同级的那个目录
	viper.AddConfigPath("./config")

换成:

// 设置配置文件的路径
viper.SetConfigFile("./config/dev.yaml")

同样能取到配置文件中的数据。

除了使用Get方法viper.Get("redis.port")获取配置数据,还可以将配置数据反序列化成Go对象。

func init() {
  ...
	var config *Config
	err = viper.Unmarshal(&config)
	if err != nil {
		panic(err)
	}
	redisConfig := config.Redis
	fmt.Printf("redisConfig %+v\n", redisConfig) 
  // 打印的结果:redisConfig {Host:localhost Port:6379 User: Password:abc123 DB:0}
}

type Config struct {
	Redis RedisConfig
}

type RedisConfig struct {
	Host     string
	Port     int
	User     string
	Password string
	DB       int
}

Viper对于配置项的键是不区分大小写的,比如把yaml文件中的键改成首字母大写:

Redis:
  Host: localhost
  Port: 6379

使用viper.Get("redis.port")一样能获取到配置的值,使用viper.Get("REDIS.PORT")同样能获取配置的值。

从环境变量中获取配置

AutomaticEnv会检查环境变量中是否有和已经存在的键匹配的环境变量,如果有,就会把环境变量加载到viper中。

// AutomaticEnv makes Viper check if environment variables match any of the existing keys
// (config, default or flags). If matching env vars are found, they are loaded into Viper.
func AutomaticEnv() { v.AutomaticEnv() }

先在终端执行指令添加环境变量: export env=PROD,然后在终端执行go run .

光看这个函数名称,是自动将环境变量添加到viper中的意思,那么执行下面的语句应该就能获取环境变量,但是无效:

	viper.AutomaticEnv()
	env := viper.Get("env")
	fmt.Println("env: ", env) // env:  <nil>

根据函数注释,需要确认viper中是否已经有对应的键,那么执行下面的语句应该就能获取环境变量,但是依然无效:

	viper.SetDefault("env", "DEV")
	viper.AutomaticEnv()
	env := viper.Get("env")
	fmt.Println("env: ", env) // env:  DEV

找了一下源码,判断键是否存在的地方使用了envkeys, exists := v.env[lcaseKey],而BindEnv方法中有设置v.env

func (v *Viper) BindEnv(input ...string) error {
	if len(input) == 0 {
		return fmt.Errorf("missing key to bind to")
	}

	key := strings.ToLower(input[0])

	if len(input) == 1 {
		v.env[key] = append(v.env[key], v.mergeWithEnvPrefix(key))
	} else {
		v.env[key] = append(v.env[key], input[1:]...)
	}

	return nil
}

所以需要这样使用:

	viper.BindEnv("env", "DEV")
	viper.AutomaticEnv()
	env := viper.Get("env")
	fmt.Println("env: ", env) // env:  PROD

要注意在比较过程中,Viper会把key转换成大写字母与环境变量进行比较,所以环境变量的名称必须为大写,不能这样设置环境变量:export env=PROD,必须这样设置环境变量:export ENV=PROD

其他

如果是使用编辑器来运行代码,需要在编辑器中设置环境变量,我用的是VSCode编辑器,所以是在launch.json中添加了环境变量:

{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Launch Package",
            "type": "go",
            "request": "launch",
            "mode": "auto",
            "program": "${workspaceFolder}",
            "env": {
                "env": "PROD"
            }
        }
    ]
}

序列化和反序列化时,可以使用 mapstructure指定键的名称。

比如yaml文件内容:

mysql:
  host: localhost
  port: 3306
  user: root
  password: 666666Aa_
  db_name: practicego

go文件内容:

type Config struct {
	Redis    RedisConfig
	MySQL    MySQLConfig
	Priority string
}

type MySQLConfig struct {
	Host     string
	Port     int
	User     string
	Password string
	DBName   string `mapstructure:"db_name"`
}

func init() {
	viper.SetConfigFile("./config/dev.yaml")
	err := viper.ReadInConfig()
	if err != nil {
		panic(err)
	}
  
  var config *Config
	err = viper.Unmarshal(&config)
	if err != nil {
		panic(err)
	}
  
  mysqlConfig := config.MySQL
	fmt.Printf("mysqlConfig %+v\n", mysqlConfig)
}

以上就是Go语言项目中使用Viper获取配置信息详解的详细内容,更多关于Go Viper获取配置信息的资料请关注脚本之家其它相关文章!

相关文章

  • 深入理解Go语言unsafe包

    深入理解Go语言unsafe包

    Go语言通过类型和内存安全设计保障可靠性,但unsafe包允许底层内存操作以实现性能优化与特殊交互,适用于结构体字段访问、零拷贝转换等场景,感兴趣的可以了解一下
    2025-08-08
  • 举例讲解Go语言中函数的闭包使用

    举例讲解Go语言中函数的闭包使用

    这篇文章主要介绍了Go语言中函数的闭包使用示例,函数闭包closure是编程语言中十分重要的特性,需要的朋友可以参考下
    2016-03-03
  • Go singleflight使用以及原理

    Go singleflight使用以及原理

    singleflight官方解释其为:singleflight提供了一个重复的函数调用抑制机制。通俗的解释其作用是,若有多个协程运行某函数时,只让一个协程去处理,然后批量返回。非常适合来做并发控制。常见用于缓存穿透的情况
    2023-01-01
  • golang实现并发控制的方法和技巧

    golang实现并发控制的方法和技巧

    golang 是一门支持并发的编程语言,它提供了 goroutine 和 channel 等强大的特性,让我们可以轻松地创建和管理多个执行单元,实现高效的任务处理,在本文中,我们将介绍一些 golang 的并发控制的方法和技巧,希望对你有所帮助
    2024-03-03
  • 一文详解Golang中字符串的常见错误

    一文详解Golang中字符串的常见错误

    这篇文章主要来和大家深入讨论一下Golang 中的字符串,并查看一些不同的场景,以避免常见错误,对大家掌握golang有一定的帮助,需要的可以了解下
    2023-10-10
  • golang连接mysql数据库操作使用示例

    golang连接mysql数据库操作使用示例

    这篇文章主要为大家介绍了golang连接mysql数据库操作使用示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步早日升职加薪
    2022-04-04
  • goland 导入github包报红问题解决

    goland 导入github包报红问题解决

    本文主要介绍了Go项目在GoLand中导入依赖标红问题解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-08-08
  • golang socket断点续传大文件的实现方法

    golang socket断点续传大文件的实现方法

    今天小编就为大家分享一篇golang socket断点续传大文件的实现方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-07-07
  • golang 如何用反射reflect操作结构体

    golang 如何用反射reflect操作结构体

    这篇文章主要介绍了golang 用反射reflect操作结构体的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-04-04
  • 使用Go语言实现配置文件热加载功能

    使用Go语言实现配置文件热加载功能

    这篇文章主要介绍了使用Go语言实现配置文件热加载功能,以及配置文件热加载包的实现思路,需要的朋友可以参考下
    2018-03-03

最新评论