Golang配置模块viper

什么是Viper

Viper是Go应用程序的完整配置解决方案,包括12-Factor应用程序。它旨在在应用程序中工作,并可以处理所有类型的配置需求和格式。它支持:

  • 设置默认值
  • 从JSON,TOML,YAML,HCL和Java属性配置文件中读取
  • 实时观看和重新读取配置文件(可选)
  • 从环境变量中读取
  • 从远程配置系统(etcd或Consul)读取,并观察变化
  • 从命令行标志读取
  • 从缓冲区读取
  • 设置显式值
    Viper可以被认为是所有应用程序配置需求的注册表。

为什么要使用Viper

在构建现代应用程序时,您不必担心配置文件格式; 你可以专注于构建出色的软件。 Viper 可以做如下工作:

  • 加载并解析JSON、TOML、YAML、HCL 或 Java properties 格式的配置文件
  • 可以为各种配置项设置默认值
  • 可以在命令行中指定配置项来覆盖配置值
  • 提供了别名系统,可以不破坏现有代码来实现参数重命名
  • 可以很容易地分辨出用户提供的命令行参数或配置文件与默认相同的区别

Viper读取配置信息的优先级顺序,从高到低,如下:

  • 显式调用Set函数
  • 命令行参数
  • 环境变量
  • 配置文件
  • key/value 存储系统
  • 默认值
  • Viper 的配置项的key不区分大小写。

快速使用

安装:

 go get github.com/spf13/viper

示例代码:
定义一个类型:

type config struct {
	v  *viper.Viper;
}

用于测试的Yaml配置文件 config.yaml

TimeStamp: "2021-07-21 15:23:19"
Author: "GK"
Information:
   Name: "Harry"
   Age: "37"
   Alise:
   - "xx"
   - "NK"
   - "GGGG"
   Image: "/path/header.rpg"
 
Favorite:
  Sport:
  - "swimming"
  - "baskteball"
  LuckyNumber: 99

读取yaml配置文件

func LoadConfigFromYaml (c *config) error  {
	c.v = viper.New();

	//设置配置文件的名字
	c.v.SetConfigName("config")

	//添加配置文件所在的路径,注意在Linux环境下%GOPATH要替换为$GOPATH
	c.v.AddConfigPath("%GOPATH/src/")
	c.v.AddConfigPath("./")

	//设置配置文件类型
	c.v.SetConfigType("yaml");

	if err := c.v.ReadInConfig(); err != nil{
		return  err;
	}

	log.Printf("age: %s, name: %s \n", c.v.Get("information.age"), c.v.Get("information.name"));
	return nil;
}

注意:如果不用AddConfigPath去指定路径,它会在程序执行的目录去寻找config.yaml

从IO中读取配置

//由IO读取配置
func ReadConfigFormIo(c *config) error {
	c.v = viper.New()
	if f, err := os.Open("config.yaml"); err != nil{
		log.Printf("filure: %s", err.Error());
		return err;
	}else {

		confLength, _ :=f.Seek(0,2);
		//注意,通常写c++的习惯害怕读取字符串的时候越界,都会多留出一个NULL在末尾,但是在这里不行,会报出如下错误:
		//While parsing config: yaml: control characters are not allowed
		//错误参考网址:https://stackoverflow.com/questions/33717799/go-yaml-control-characters-are-not-allowed-error
		configData := make([]byte, confLength);
		f.Seek(0, 0);
		f.Read(configData);
		log.Printf("%s\n", string(configData))

		c.v.SetConfigType("yaml");
		if err := c.v.ReadConfig(bytes.NewBuffer(configData)); err != nil{
			log.Fatalf(err.Error());
		}
	}
	log.Printf("age: %s, name: %s \n", c.v.Get("information.age"), c.v.Get("information.name"));

	return nil;
}

从环境变量中读取配置

//读取本地的环境变量
func EnvConfigPrefix(c *config) error {
	c.v = viper.New();

	//BindEnv($1,$2)
	// 如果只传入一个参数,则会提取指定的环境变量$1,如果设置了前缀,则会自动补全 前缀_$1
	//如果传入两个参数则不会补全前缀,直接获取第二参数中传入的环境变量$2
	os.Setenv("LOG_LEVEL", "INFO");
	if nil == c.v.Get("LOG_LEVEL ") {
		log.Printf("LOG_LEVEL is nil");
	}else {
		return ErrorNotMacth;
	}
        
        //必须要绑定后才能获取
	c.v.BindEnv("LOG_LEVEL");
	log.Printf("LOG_LEVEL is %s", os.Getenv("log_level"));


	//会获取所有的环境变量,同时如果过设置了前缀则会自动补全前缀名
	c.v.AutomaticEnv();
	//环境变量前缀大小写不区分
	os.Setenv("DEV_ADDONES","none");
	log.Printf("DEV_ADDONES: %s", c.v.Get("dev_addones"));

	//SetEnvPrefix会设置一个环境变量的前缀名
	c.v.SetEnvPrefix("DEV");

	os.Setenv("DEV_MODE", "true");
	//此时会自动补全前缀,实际去获取的是DEV_DEV_MODE
	if nil ==  c.v.Get("dev_mode"){
		log.Printf("DEV_MODE is nil") ;
	}else {
		return ErrorNotMacth;
	}
        
        //此时我们直接指定了loglevel所对应的环境变量,则不会去补全前缀
	c.v.BindEnv("loglevel", "LOG_LEVEL");
	log.Printf("LOG_LEVEL: %s", c.v.Get("loglevel")) ;

	return nil
}

方便的替换符

func EnvCongiReplacer(c *config, setPerfix bool) error {
	c.v = viper.New();
	c.v.AutomaticEnv();
	c.v.SetEnvKeyReplacer(strings.NewReplacer(".","_"));

	os.Setenv("API_VERSION","v0.1.0");
	//Replacer和prefix一起使用可能会冲突,比如我下面的例子
	//因为会自动补全前缀最终由获取API_VERSION变成API_API_VERSION
	if setPerfix{ c.v.SetEnvPrefix("api");}
	if s := c.v.Get("api.version"); s==nil{
		return ErrorNoxExistKey
	}else {
		log.Printf("%s", c.v.Get("api.version"));
	}

	return nil;
}

别名功能

//设置重载 和别名
func SetAndAliases(c *config) error {
	c.v = viper.New();
	c.v.Set("Name","wzp");
	c.v.RegisterAlias("id","Name");
	c.v.Set("id","Mr.Wang");

	//我们可以发现当别名对应的值修改之后,原本的key也发生变化
	log.Printf("id %s, name %s",c.v.Get("id"),c.v.Get("name") );
	return nil;
}

我们可以为key设置别名,当别名的值被重置后,原key对应的值也会发生变化。

序列化和反序列化

type favorite struct {
	Sports []string;
	Music []string;
	LuckyNumber int;
}

type information struct {
	Name string;
	Age  int;
	Alise []string;
	Image string;
	Public bool
}

type YamlConfig struct {
	TimeStamp string
	Author string
	PassWd string
	Information information
	Favorite favorite;
}



//将配置解析为Struct对象
func UmshalStruct(c *config) error  {
	LoadConfigFromYaml(c);
	var cf YamlConfig
	if err := c.v.Unmarshal(&cf); err != nil{
		return err;
	}
        
        
	return nil;
}

func YamlStringSettings(c *config) string {
	c.v = viper.New();
	c.v.Set("name", "wzp");
	c.v.Set("age", 18);
	c.v.Set("aliase",[]string{"one","two","three"})

	cf := c.v.AllSettings()
	bs, err := yaml.Marshal(cf)
	if err != nil {
		log.Fatalf("unable to marshal config to YAML: %v", err)
	}
	return string(bs)
}

func JsonStringSettings(c *config) string {
	c.v = viper.New();
	c.v.Set("name", "wzp");
	c.v.Set("age", 18);
	c.v.Set("aliase",[]string{"one","two","three"})

	cf := c.v.AllSettings()
	bs, err := json.Marshal(cf)
	if err != nil {
		log.Fatalf("unable to marshal config to YAML: %v", err)
	}
	return string(bs)
}

从command Line中读取配置

func main()  {
	flag.String("mode","RUN","please input the mode: RUN or DEBUG");
	pflag.Int("port",1080,"please input the listen port");
	pflag.String("ip","127.0.0.1","please input the bind ip");
	//获取标准包的flag
	pflag.CommandLine.AddGoFlagSet(flag.CommandLine);
	pflag.Parse();

	//BindFlag
	//在pflag.Init key后面使用
	viper.BindPFlag("port", pflag.Lookup("port"));
	log.Printf("set port: %d", viper.GetInt("port"));

	viper.BindPFlags(pflag.CommandLine);
	log.Printf("set ip: %s", viper.GetString("ip"));
}

监听配置文件

//监听配置文件的修改和变动
func WatchConfig(c *config) error {
	if err := LoadConfigFromYaml(c); err !=nil{
		return err;
	}
	ctx, cancel := context.WithCancel(context.Background());

	c.v.WatchConfig()
        
        //监听回调函数
	watch := func(e fsnotify.Event) {
		log.Printf("Config file is changed: %s \n", e.String())
		cancel();
	}

	c.v.OnConfigChange(watch);
	<-ctx.Done();
	return nil;
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
根据提供的引用内容,可以得出以下结论: 根据引用,Node.js在性能上存在一些缺陷,并且其提供的模板引擎不够友好,对SEO也不友好。因此,如果您希望在前端友好的情况下轻松运行和部署博客,可能需要考虑其他选择。 根据引用,在选择Golang和Node.js之间时,需要根据项目的具体要求和优先级进行权衡。Golang在原始速度、并发和资源效率方面表现出优越性能,适用于需要快速响应时间和可扩展并发的高性能应用。而Node.js则提供了开发者生产力高、生态系统支持广泛以及与JavaScript前端框架无缝集成的优势。 关于如何配置Golang和Node.js,具体步骤如下: 1. 首先,确保您已经安装了Golang和Node.js的运行环境。 2. 在您的项目目录下,创建一个名为`main.go`的文件,用于编写Golang代码。 3. 在`main.go`文件中,导入所需的Golang包,并编写您的Golang代码逻辑。 4. 在终端中,使用以下命令运行Golang代码: ```shell go run main.go ``` 5. 接下来,您可以在Golang代码中调用Node.js的相关功能。为此,您可以使用`os/exec`包来执行Node.js命令。 6. 在Golang代码中,使用以下代码来执行Node.js命令: ```go package main import ( "fmt" "os/exec" ) func main() { cmd := exec.Command("node", "your_node_script.js") output, err := cmd.Output() if err != nil { fmt.Println(err) return } fmt.Println(string(output)) } ``` 7. 在终端中,使用以下命令运行Golang代码: ```shell go run main.go ``` 通过以上步骤,您可以配置Golang和Node.js,并在Golang代码中调用Node.js的功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

kuokay

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值