[Golang] Viper原理以及详细使用案例

news2024/11/25 15:26:37

在这里插入图片描述

文章目录

  • 什么是 Viper?
  • 基础配置
    • 引入依赖:
    • 动态监听原理分析:
    • 监听原理分析
  • Config.yaml文件配置
  • Viper文件配置

什么是 Viper?

介绍:用于处理配置文件中解析和读取配置文件
优点:支持多种配置格式,json,yaml 等等
作用:配置文件解析和读取 默认值支持 环境变量支持 命令行标志支持 配置监听和热加载

基础配置

引入依赖:

go get github.com/spf13/viper

读取文件方法:

    viper.SetConfigFile("haha")      //配置文件名,不需要后缀
 	viper.SetConfigType("yaml")      //设置配置文件格式
	viper.AddConfigPath("../config") //查找路径
 	err := viper.ReadInConfig()      //读取配置文件

读取文件方法二:

 	viper.SetConfigFile("./config/config.yaml")
    err := viper.ReadInConfig()

动态监听原理分析:

//监听配置文件
viper.WatchConfig()
//监听是否更改配置文件
viper.OnConfigChange(func(e fsnotify.Event) {
   fmt.Println("配置文件被人修改了...")
   err := viper.Unmarshal(&Conf)
   if err != nil {
      panic(fmt.Errorf("配置文件修改以后,报错啦,err:%v", err))
   }
})


监听原理分析

分析WatchConfig()方法
// 监听文件变化
func (v *Viper) WatchConfig() {
    //开启一个协程  相当于开启一个任务
   initWG := sync.WaitGroup{}
   initWG.Add(1)
   go func() {
       //创建一个文件监听器
      watcher, err := newWatcher()
      if err != nil {
         v.logger.Error(fmt.Sprintf("failed to create watcher: %s", err))
         os.Exit(1)
      }
      defer watcher.Close()
      // we have to watch the entire directory to pick up renames/atomic saves in a cross-platform way
         //获得配置文件名
       filename, err := v.getConfigFile()
      if err != nil {
         v.logger.Error(fmt.Sprintf("get config file: %s", err))
         initWG.Done()
         return
      }
        //规范化配置文件路径信息
      configFile := filepath.Clean(filename)
      configDir, _ := filepath.Split(configFile)
      //获得文件的真实路径
      realConfigFile, _ := filepath.EvalSymlinks(filename)
    //再开启一个协程 去监听监听器的状态
      eventsWG := sync.WaitGroup{}
      eventsWG.Add(1)
      go func() {
         for {
            select {
                //监听监听器的事务
            case event, ok := <-watcher.Events:
               if !ok { // 'Events' channel is closed
                  eventsWG.Done()
                  return
               }
               currentConfigFile, _ := filepath.EvalSymlinks(filename)
               // we only care about the config file with the following cases:
               // 1 - if the config file was modified or created
               // 2 - if the real path to the config file changed (eg: k8s ConfigMap replacement)
               //判断文件是否修改,创建 配置文件的真实路径是否发生变化
               if (filepath.Clean(event.Name) == configFile &&
                  (event.Has(fsnotify.Write) || event.Has(fsnotify.Create))) ||
                  (currentConfigFile != "" && currentConfigFile != realConfigFile) {
                  realConfigFile = currentConfigFile
                  //变化之后 重新进行文件读取
                  err := v.ReadInConfig()
                  if err != nil {
                     v.logger.Error(fmt.Sprintf("read config file: %s", err))
                  }
                  //调用回调函数去重新将内容写进结构体中
                  if v.onConfigChange != nil {
                     v.onConfigChange(event)
                  }
               } else if filepath.Clean(event.Name) == configFile && event.Has(fsnotify.Remove) {
                  eventsWG.Done()
                  return
               }

            case err, ok := <-watcher.Errors:
               if ok { // 'Errors' channel is not closed
                  v.logger.Error(fmt.Sprintf("watcher error: %s", err))
               }
               eventsWG.Done()
               return
            }
         }
      }()
      //如果发生文件找不到或者监听过程中出错就会退出内外两层协程,然后监听停止
      watcher.Add(configDir)
      initWG.Done()   // done initializing the watch in this go routine, so the parent routine can move on...
      eventsWG.Wait() // now, wait for event loop to end in this go-routine...
   }()
   initWG.Wait() // make sure that the go routine above fully ended before returning
}

同时通过mapstructure将配置文件中的信息(键值对)映射到结构体中,实现随时拿取

Config.yaml文件配置

mode: "dev"
port: 8080

log:
  level: "debug"
  filename: "./log/logfile.log"
  max_size: 1000
  max_age: 3600
  max_backups: 5

mysql:
  host: localhost
  port: 3306
  user: root
  password: root
  db: library
  max_open_conns: 100
  max_idle_conns: 20

redis:
  host: 127.0.0.1
  port: 6379
  db: 0

Viper文件配置

package config

import (
	"fmt"
	"github.com/fsnotify/fsnotify"
	"github.com/spf13/viper"
)

var Conf = new(LibraryConfig)

type MysqlConfig struct {
	Host         string `mapstructure:"host"`
	User         string `mapstructure:"user"`
	Password     string `mapstructure:"password"`
	DB           string `mapstructure:"db"`
	Port         int    `mapstructure:"port"`
	MaxOpenConns int    `mapstructure:"max_open_conns"`
	MaxIdleConns int    `mapstructure:"max_idle_conns"`
}
type RedisConfig struct {
	Host         string `mapstructure:"host"`
	Port         int    `mapstructure:"port"`
	DB           int    `mapstructure:"db"`
	Password     string `mapstructure:"password"`
	PollSize     int    `mapstructure:"PollSize"`
	MinIdleConns int    `mapstructure:"min_idle_cons"`
}
type LogConfig struct {
	Level      string `mapstructure:"level"`
	FileName   string `mapstructure:"filename"`
	MaxSize    int    `mapstructure:"max_size"`
	MaxAge     int    `mapstructure:"max_age"`
	MaxBackUps int    `mapstructure:"max_backups"`
}

type LibraryConfig struct {
	Mode         string `mapstructure:"mode"`
	Port         int    `mapstructure:"port"`
	*LogConfig   `mapstructure:"log"`
	*MysqlConfig `mapstructure:"mysql"`
	*RedisConfig `mapstructure:"redis"`
}

func Init() error {

	//加载配置文件位置
	viper.SetConfigFile("./config/config.yaml")
	//监听配置文件
	viper.WatchConfig()
	//监听是否更改配置文件
	viper.OnConfigChange(func(e fsnotify.Event) {
		fmt.Println("配置文件被人修改了...")
		err := viper.Unmarshal(&Conf)
		if err != nil {
			panic(fmt.Errorf("配置文件修改以后,报错啦,err:%v", err))
		}
	})
	// 读取配置文件内容
	err := viper.ReadInConfig()
	if err != nil {
		panic(fmt.Errorf("ReadInConfig failed,err:%v", err))
	}
	//将配置文件内容写入到Conf结构体
	if err1 := viper.Unmarshal(&Conf); err1 != nil {
		panic(fmt.Errorf("unmarshal data to Conf failed,err:%v", err))
	}
	return nil
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/794614.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

办公软件巨头CCED、WPS迎来新挑战,新款办公软件已形成普及之势

办公软件巨头CCED、WPS的成长经历 众所周知&#xff0c;CCED和WPS是中国办公软件行业的两大知名品牌。 但它们的成长经历不是一蹴而就的&#xff0c;都是经历了漫长的发展过程的。 CCED是中国大陆早期的一款文本编辑器&#xff0c;它在上个世纪80年代末和90年代初非常流行。 …

Flutter Widget Life Cycle 组件生命周期

Flutter Widget Life Cycle 组件生命周期 视频 前言 了解 widget 生命周期&#xff0c;对我们开发组件还是很重要的。 今天会把无状态、有状态组件的几个生命周期函数一起过下。 原文 https://ducafecat.com/blog/flutter-widget-life-cycle 参考 https://api.flutter.dev/f…

低代码和零代码有哪些区别?

低代码开发的概念 低代码开发是一种新兴的软件开发方法&#xff0c;其核心是通过使用图形用户界面和可视化建模工具&#xff0c;来减少编写代码的工作量和技能要求。低代码开发平台通常提供了丰富的预定义组件和模板&#xff0c;可以帮助开发人员快速构建应用程序。开发人员只…

【雕爷学编程】Arduino动手做(173)---SG90舵机双轴云台模块

37款传感器与执行器的提法&#xff0c;在网络上广泛流传&#xff0c;其实Arduino能够兼容的传感器模块肯定是不止这37种的。鉴于本人手头积累了一些传感器和执行器模块&#xff0c;依照实践出真知&#xff08;一定要动手做&#xff09;的理念&#xff0c;以学习和交流为目的&am…

新思的DW ECC计算模块

参考文件&#xff1a;synopsys学习资料-dw_ecc.pdf 设计的模块&#xff1a;

一些需要用到的网址

https://npm.taobao.org/mirrors/chromedriver/ (谷歌浏览器镜像网址) 欢迎使用Markdown编辑器 你好&#xff01; 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章&#xff0c;了解一下Markdown的基本语法知识。…

Neo4j 集群和负载均衡

Neo4j 集群和负载均衡 Neo4j是当前最流行的开源图DB。刚好读到了Neo4j的集群和负载均衡策略&#xff0c;记录一下。 1 集群 Neo4j 集群使用主从复制实现高可用性和水平读扩展。 1.1 复制 集群的写入都通过主节点协调完成的&#xff0c;数据先写入主机&#xff0c;再同步到…

word中将合并后的多行拆分为原先的行数

word中将已经合并的多行拆分为原先的行数&#xff0c;我们不用刻意去数应该是多少行&#xff0c; 只需将拆分的行数不断增加&#xff0c;word会默认最大增加到合并前的行数。

redis 第二章

目录 1.持久化 2.主从复制 3.总结 1.持久化 通过 aof 和 rdb 将内存里的数据放到磁盘中 aof: rdb: 2.主从复制 将一台 redis 服务器的数据&#xff0c;复制到其他的 redis 服务器 3.总结 主从复制是高可用 redis 的基础&#xff0c;哨兵和集群都是在主从复制基础上实现高可…

UE UMG补充

Widget Switcher&#xff08;UI选择&#xff09; 通过该组件可以在同一个地方显示不同UI 可以通过蓝图改变显示的UI 在UI界面直接设置 ProgressBar(进度条) 可以用动画实现增长&#xff0c;更加自然 Animation 有绑定功能

具身智能controller---RT-1(Robotics Transformer)(上---方法介绍)

具身智能controller---RT-1&#xff08;Robotics Transformer&#xff09;&#xff08;上---方法介绍&#xff09; 相关链接摘要和简介相关工作与预备知识系统概述模型 RT-1: ROBOTICS TRANSFORMER模型 相关链接 github链接 主页链接&#xff08;包括论文和训练数据集&#xf…

fpga开发——蜂鸣器

蜂鸣器的原理 有源蜂鸣器和无源蜂鸣器 无源蜂鸣器利用电磁感应现象&#xff0c;为音圈接入交变电流后形成的电磁铁与永磁铁相吸或相斥而推动振膜发声&#xff0c;接入直流电只能持续推动振膜而无法产生声音&#xff0c;只能在接通或断开时产生声音。无源蜂鸣器的工作原理与扬声…

LeetCode152.Maximum-Product-Subarray<乘积最大的子数组>

题目&#xff1a; 思路&#xff1a; 一开始是使用的每次乘积的最大值. 遍历. 然后因为有负数所以还是差点.看了答案,发现还需要保存一个负数的值.当当前的值是负数的时候 互换位置.然后获得当前最大值. 代码是&#xff1a; //codeclass Solution { public:int maxProduct(vec…

哈希表与布隆过滤器

文章目录 一、哈希函数是什么&#xff1f;哈希函数的应用场景哈希函数的构造方法 二、哈希表哈希表的底层设计题型 三、布隆过滤器布隆过滤器优点布隆过滤器缺陷布隆过滤器使用场景 一致性哈希算法.位图 3. 海量数据面试题 一、哈希函数是什么&#xff1f; 1 : 哈希函数没有随…

二.安装helm

1.什么是helm Kubernetes 包管理器 Helm 是查找、分享和使用软件构件 Kubernetes 的最优方式。 Helm 管理名为 chart 的 Kubernetes 包的工具。Helm 可以做以下的事情&#xff1a; 从头开始创建新的 chart将 chart 打包成归档(tgz)文件与存储 chart 的仓库进行交互在现有的 K…

【数据结构】_4.List接口实现类LinkedList与链表

目录 1.链表的结构与特点 1.1 链表的结构&#xff1a; 1.2 链表的特点&#xff1a; 2. 不带头单链表的模拟实现 3. 单链表OJ 3.1 题目1&#xff1a;移除链表元素: 3.2 题目2&#xff1a;反转一个单链表 3.3 题目3&#xff1a;返回链表的中间结点 3.4 题目4&#xff1…

Python连接MariaDB数据库

Python连接MariaDB数据库 一、安装mariadb库 pip install mariadb 二、连接数据库 connect()函数连接数据库。 import mariadbconn mariadb.connect(user"root", password"Root123", host"192.168.0.182", port3306, database"compan…

Linux环境搭建(XShell+云服务器)

好久不见啊&#xff0c;放假也有一周左右了&#xff0c;简单休息了下&#xff08;就是玩了几天~~&#xff09;&#xff0c;最近也是在学习Linux&#xff0c;现在正在初步的学习阶段&#xff0c;本篇将会简单的介绍一下Linux操作系统和介绍Linux环境的安装与配置&#xff0c;来帮…

【沁恒蓝牙mesh】组网方式选择与分析

本文主要介绍了【沁恒蓝牙mesh】组网方式选择与分析 1.开发环境搭建与程序烧录 参考博文&#xff1a;【经验】如何搭建沁恒蓝牙SoC CH58x开发环境 2. 组网方式 蓝牙mesh组网实践&#xff08;配网方式的选择&#xff09; 自配网&#xff1a;自己给自己配网&#xff0c;当节点…