golang工程配置解决方案——viper框架及使用

配置解决方案——viper框架

文件读取配置

package configimport ("github.com/spf13/viper"_ "github.com/spf13/viper/remote""io""log"
)// 从本地文件读取配置
func LoadFromFile(filepath string, typ ...string) (*viper.Viper, error) {v := viper.New()v.SetConfigFile(filepath)if len(typ) > 0 {v.SetConfigType(typ[0])}err := v.ReadInConfig()return v, err
}

环境变量读取配置

package configimport ("github.com/spf13/viper"_ "github.com/spf13/viper/remote""io""log"
)func LoadFromEnv() (*viper.Viper, error) {v := viper.New()//自动绑定环境变量//v.AutomaticEnv()//指定绑定的环境变量v.BindEnv("GOPATH")v.BindEnv("GOROOT")return v, nil
}

io.Reader读取

package configimport ("github.com/spf13/viper"_ "github.com/spf13/viper/remote""io""log"
)func LoadFromIoReader(reader io.Reader, typ string) (*viper.Viper, error) {v := viper.New()v.SetConfigType(typ)err := v.ReadConfig(reader)return v, err
}

etcd读取

etcd单节点集群运行

docker run -d \
-p 2379:2379 \
-p 2380:2380 \
--restart always \
--privileged \
--volume=/home/etcd:/etcd-data \
--name etcd quay.io/coreos/etcd:v3.5.7 \
/usr/local/bin/etcd \
--data-dir=/etcd-data --name node1 \
--initial-advertise-peer-urls http://10.74.18.61:2380 \
--listen-peer-urls http://0.0.0.0:2380 \
--advertise-client-urls http://10.74.18.61:2379 \
--listen-client-urls http://0.0.0.0:2379 \
--initial-cluster node1=http://10.74.18.61:2380 \
--initial-cluster-token tkn \
--initial-cluster-state new 
写入etcd配置文件, 读取etcd 配置文件

viper 读取etcd 配置

config.go

package configimport ("github.com/spf13/viper"_ "github.com/spf13/viper/remote""io""log"
)
func LoadFromEtcd(etcdAddr, key, typ string) (*viper.Viper, error) {v := viper.New()// viper读取etcd中的一个keyerr := v.AddRemoteProvider("etcd3", etcdAddr, key)if err != nil {log.Println(err)return nil, err}v.SetConfigType(typ)err = v.ReadRemoteConfig()return v, err
}
package mainimport ("bytes""context""fmt"clientv3 "go.etcd.io/etcd/client/v3""log""os"_ "os/signal""viper-practice/config"
)func loadEtcd() {//向etcd写入数据etcdAddr := "10.74.18.61:2379"key := "/test/viper/config.yaml"localFilepath := "config.yaml"writeConfToEtcd(etcdAddr, key, localFilepath)//从etcd加载配置,etcd充当了配置中心v, err := config.LoadFromEtcd(etcdAddr, key, "yaml")fmt.Println("etcd", err, v.Get("env"), v.Get("server.port"), v.Get("courses").([]interface{})[0], v.Get("list").([]interface{})[0].(map[string]interface{})["author"])
}
func writeConfToEtcd(etcdAddr, key, localFilepath string) {byteList, err := os.ReadFile(localFilepath)if err != nil {log.Fatal(err)}value := string(byteList)cli, err := clientv3.New(clientv3.Config{Endpoints: []string{etcdAddr},})if err != nil {log.Fatal(err)}_, err = cli.Put(context.Background(), key, value)if err != nil {log.Fatal(err)}
}func main() {//loadFile()//loadEnv()//loadReader()loadEtcd()
}

docker etcd查询

docker exec etcd etcdctl get [key]

汇总

source.go

package configimport ("github.com/spf13/viper"_ "github.com/spf13/viper/remote""io""log"
)// LoadFromFile 从本地文件读取配置
func LoadFromFile(filepath string, typ ...string) (*viper.Viper, error) {v := viper.New()v.SetConfigFile(filepath)if len(typ) > 0 {v.SetConfigType(typ[0])}err := v.ReadInConfig()return v, err
}func LoadFromEnv() (*viper.Viper, error) {v := viper.New()//自动绑定环境变量//v.AutomaticEnv()//指定绑定的环境变量v.BindEnv("GOPATH")v.BindEnv("GOROOT")return v, nil
}func LoadFromIoReader(reader io.Reader, typ string) (*viper.Viper, error) {v := viper.New()v.SetConfigType(typ)err := v.ReadConfig(reader)return v, err
}func LoadFromEtcd(etcdAddr, key, typ string) (*viper.Viper, error) {v := viper.New()// viper读取etcd中的一个keyerr := v.AddRemoteProvider("etcd3", etcdAddr, key)if err != nil {log.Println(err)return nil, err}v.SetConfigType(typ)err = v.ReadRemoteConfig()return v, err
}

测试

main.go

package mainimport ("bytes""context""fmt"clientv3 "go.etcd.io/etcd/client/v3""log""os"_ "os/signal""viper-practice/config"
)func main() {//loadFile()//loadEnv()//loadReader()loadEtcd()
}func loadFile() {// 获取viper对象,打印对应配置// viper对象里以键值对方式存储// 环境变量的值都是字符串处理,没有复杂的对象存储v1, err := config.LoadFromFile("config.env")fmt.Println("config.env", err, v1.Get("env"), v1.Get("server.port"), v1.Get("courses"))v2, err := config.LoadFromFile("config.json")fmt.Println("config.json", err, v2.Get("env"), v2.Get("server.port"), v2.Get("courses").([]interface{})[0], v2.Get("list").([]interface{})[0].(map[string]interface{})["author"])// 识别不了的格式,可以指定格式读取v3, err := config.LoadFromFile("config.noext", "yaml")fmt.Println("config.noext", err, v3.Get("env"), v3.Get("server.port"), v3.Get("courses").([]interface{})[0], v3.Get("list").([]interface{})[0].(map[string]interface{})["author"])v4, err := config.LoadFromFile("config.toml")fmt.Println("config.toml", err, v4.Get("env"), v4.Get("server.port"), v4.Get("courses").(map[string]interface{})["list"].([]interface{})[0], v4.Get("list").([]interface{})[0].(map[string]interface{})["author"])v5, err := config.LoadFromFile("config.yaml")fmt.Println("config.yaml", err, v5.Get("env"), v5.Get("server.port"), v5.Get("courses").([]interface{})[0], v5.Get("list").([]interface{})[0].(map[string]interface{})["author"])// 这种只是测试一下viper, 不推荐这么干, 一般是定义结构去读取配置文件比较方便
}
func loadEnv() {v, err := config.LoadFromEnv()fmt.Println(err, v.Get("GOROOT"), v.Get("GOPATH"))
}
func loadReader() {byteList, err := os.ReadFile("config.yaml")if err != nil {log.Fatal(err)}r := bytes.NewReader(byteList)v, err := config.LoadFromIoReader(r, "yaml")fmt.Println("io.reader", err, v.Get("env"), v.Get("server.port"), v.Get("courses").([]interface{})[0], v.Get("list").([]interface{})[0].(map[string]interface{})["author"])}func loadEtcd() {//向etcd写入数据etcdAddr := "10.74.18.61:2379"key := "/test/viper/config.yaml"localFilepath := "config.yaml"writeConfToEtcd(etcdAddr, key, localFilepath)//从etcd加载配置,etcd充当了配置中心v, err := config.LoadFromEtcd(etcdAddr, key, "yaml")fmt.Println("etcd", err, v.Get("env"), v.Get("server.port"), v.Get("courses").([]interface{})[0], v.Get("list").([]interface{})[0].(map[string]interface{})["author"])
}
func writeConfToEtcd(etcdAddr, key, localFilepath string) {byteList, err := os.ReadFile(localFilepath)if err != nil {log.Fatal(err)}value := string(byteList)cli, err := clientv3.New(clientv3.Config{Endpoints: []string{etcdAddr},})if err != nil {log.Fatal(err)}_, err = cli.Put(context.Background(), key, value)if err != nil {log.Fatal(err)}
}

配置映射到struct以及配置热更新

生产工作中,我们还是采用配置映射到struct的方式。同时在配置变化的时候,希望可以做到配置的热更新,不用重新启动程序

下面是一个配置文件结构定义,viper有预定义的标签tag mapstructure来标识配置项

监控本地文件
package configimport ("fmt""github.com/fsnotify/fsnotify""github.com/spf13/viper""log""time"
)
// mapstructure 是viper特有的tag
type Config struct {Env    string `mapstructure:"env"`Server struct {IP   string `mapstructure:"ip"`Port string `mapstructure:"port"`}Courses []string `mapstructure:"courses"`List    []struct {Name   string `mapstructure:"name"`Author string `mapstructure:"author"`} `mapstructure:"list"`
}var conf *Config
var etcdConf *Configfunc InitConf(filepath string, typ ...string) {v := viper.New()v.SetConfigFile(filepath)if len(typ) > 0 {v.SetConfigType(typ[0])}err := v.ReadInConfig()if err != nil {log.Fatal(err)}conf = &Config{}err = v.Unmarshal(conf)if err != nil {log.Fatal(err)}v.OnConfigChange(func(in fsnotify.Event) {// 重新加载配置文件到对象v.Unmarshal(conf)// 打印新的配置信息,验证结果fmt.Printf("%+v\n", conf)})v.WatchConfig()
}
监控etcd配置热更

如何将配置写入etcd在前面已经提到,生产工作中一般有三方的程序会做这个事情。

// etcd配置热更新
func InitEtcdConf(etcdAddr, key, typ string) {v := viper.New()v.AddRemoteProvider("etcd3", etcdAddr, key)v.SetConfigType(typ)err := v.ReadRemoteConfig()if err != nil {log.Fatal(err)}etcdConf = &Config{}err = v.Unmarshal(etcdConf)if err != nil {log.Fatal(err)}go func() {i := 0for {<-time.After(time.Second * 3)err = v.WatchRemoteConfig()if err != nil {log.Println(err)continue}etcdConf = &Config{}err = v.Unmarshal(etcdConf)if err != nil {log.Println(err)continue}i++fmt.Printf("%d, %+v\n", i, etcdConf)}}()
}

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

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

相关文章

Java毕业设计 SpringBoot 新能源充电桩管理系统

Java毕业设计 SpringBoot 新能源充电桩管理系统 SpringBoot 新能源充电桩管理系统 功能介绍 管理员 登录 验证码 注册 系统用户管理 普通用户管理 通知公告管理 留言管理 充电站管理 充电桩管理 充电桩预约 充电管理 订单管理 修改密码 普通用户 登录 修改个人资料 通知公告…

虹科分享|确保冻干工艺开发中精确测量和数据完整性的5步指南

文章来源&#xff1a;虹科环境监测技术 阅读原文&#xff1a;虹科分享 | 确保冻干工艺开发中精确测量和数据完整性的5步指南 一、介绍 冻干周期的工艺开发在冻干中起着至关重要的作用&#xff0c;因为它可以优化关键工艺参数&#xff0c;以实现理想的产品质量和工艺一致性。优…

MappingMongoConverter原生mongo 枚举类ENUM映射使用的是name

j.l.IllegalArgumentException: No enum constant com.xxx.valobj.TypeEnum.stringat java.lang.Enum.valueOf

*Django中的Ajax 纯js的书写样式1

搭建项目 建立一个Djano项目&#xff0c;建立一个app&#xff0c;建立路径&#xff0c;视图函数大多为render, Ajax的创建 urls.py path(index/,views.index), path(index2/,views.index2), views.py def index(request):return render(request,01.html) def index2(requ…

Python 读取 Word 详解(python-docx)

文章目录 1 概述1.1 第三方库&#xff1a;python-docx 2 新建文档2.1 空白文档2.2 标题2.3 段落2.4 文本2.5 字体2.6 图片2.7 表格 3 扩展3.1 修改文档3.2 读取文档 1 概述 1.1 第三方库&#xff1a;python-docx > pip install python-docx2 新建文档 2.1 空白文档 impo…

Python运维学习Day02-subprocess/threading/psutil

文章目录 1. 检测网段在线主机2. 获取系统变量的模块 psutil 1. 检测网段在线主机 import subprocessdef checkIP(ip):cmd fping -n 1 -w 1 {ip}null open(nlll,modewb)status subprocess.call(cmd,shellTrue,stdoutnull,stderrnull)if status 0:print(f"主机[{ip}]在…

【数据结构】模拟实现priority_queue

namespace my_priority_queue {// 仿函数/函数对象template<class T>struct greater{bool operator()(const T& x, const T& y){return x > y;}};template<class T>struct less{bool operator()(const T& x, const T& y){return x < y;}};/…

uniapp 常见的问题以及解决办法

当开发UniApp时&#xff0c;可能会遇到一些常见问题。以下是一些常见问题及其解决办法&#xff1a; 1. 页面或组件无法正常显示 确保页面或组件的路径和文件名的大小写正确。检查模板代码中是否存在错误或不完整的标签闭合。使用调试工具&#xff08;如Chrome开发者工具&…

ue5 右击.uproject generator vs project file 错误

出现如下错误 Unable to find valid 14.31.31103 C toolchain for VisualStudio2022 x64 就算你升级了你的 vs installer 也不好使 那是因为 在C:\Users\{YourUserName}\AppData\Roaming\Unreal Engine\UnrealBuildTool\BuildConfiguration.xml 这个缓存配置文件中写死了 14…

乌兹别克斯坦公司如何注册 乌兹别克斯坦公司年审 乌兹别克斯坦公司注册流程

注册乌兹别克斯坦公司优势&#xff1a; 1、政 府进行了一系列经济和商业改 革&#xff0c;以推动国内外投资。努力提高商业环境的透明度和效率。 2、 乌兹别克斯坦是“一 带 一路”倡议的重要一环&#xff0c;这为公司在区域范围内的贸易和合作提供了机遇。 3、工业和农业方…

leetcode-链表

链表是一个用指针串联起来的线性结构&#xff0c;每个结点由数据域和指针域构成&#xff0c;指针域存放的是指向下一个节点的指针&#xff0c;最后一个节点指向NULL&#xff0c;第一个结点称为头节点head。 常见的链表有单链表、双向链表、循环链表。双向链表就是多了一个pre指…

​轻量应用服务器有什么优势?如何评价亚马逊云科技轻量应用服务器?

什么是轻量应用服务器&#xff1f; 随着如今各行各业对云计算的需求越来越多&#xff0c;云服务器也被越来越多的企业所广泛采用。其中&#xff0c;轻量应用服务器是一种简单、高效、可靠的云计算服务&#xff0c;能够为开发人员、企业和个人提供轻量级的虚拟专用服务器&#…

AWTK 液体流动效果控件发布

液体流动效果控件。 主要特色&#xff1a; 支持水平和垂直方向。支持正向和反向流动。支持设置头尾的图片。支持设置流动的图片。支持设置速度的快慢。支持启停操作。 准备 获取 awtk 并编译 git clone https://github.com/zlgopen/awtk.git cd awtk; scons; cd -运行 生成…

JMeter简单使用

JMeter是一个功能强大的开源性能测试工具&#xff0c;用于对各种应用程序、协议和服务器进行性能和负载测试。它被广泛用于测试Web应用程序的性能&#xff0c;并可以模拟多种负载条件和行为。 JMeter使用 添加线程组 设置线程组的配置 设置请求 配置请求 添加监听器 查看压…

JVM虚拟机:Java对象的头信息有什么?

本文重点 在前面的课程中,我们学习了对象头,其中对象头包含Mark Word和class pointer,当然数组还会有一个数组长度。本文主要分析Mark Work中包含的信息。 Mark Word 以下两张图是一个意思: 32位 32位 64位 以上就是Mark Word会存储的信息,这个意思是说Java对象在不同…

计网小题题库整理第一轮(面向期末基础)(2)

该系列第二期&#xff0c;第一期链接在这~ 计网小题题库整理第一轮&#xff08;面向期末基础&#xff09;&#xff08;1&#xff09;https://blog.csdn.net/jsl123x/article/details/134030486?spm1001.2014.3001.5501 一.选择题 1、Internet的前身是 &#xff08;C &#x…

2015年亚太杯APMCM数学建模大赛B题城市公共交通服务水平动态评价模型求解全过程文档及程序

2015年亚太杯APMCM数学建模大赛 B题 城市公共交通服务水平动态评价模型 原题再现 城市公共交通服务评价是城市公共交通系统建设和提高公共交通运营效率的重要组成部分。对于公交企业&#xff0c;管理和规划部门&#xff0c;传统公交车站、线路和换乘枢纽的规划数据只是基于主…

【MySQL索引与优化篇】InnoDB数据存储结构

文章目录 1. 数据库的存储结构:页1.1 磁盘与内存交互基本单位:页1.2 页结构概述1.3 页的上层结构 2. 页的内部结构3. InnoDB行格式(或记录格式)3.1 Compact行格式3.2 Dynamic和Compressed行格式3.3 Redundant行格式 4. 区、段与碎片区4.1 为什么要有区&#xff1f;4.2 为什么要…

Ortec974A EPICS IOC程序

1&#xff09; 创建一个用户存放这个IOC程序结构的目录&#xff1a; rootorangepi4-lts:/usr/local/EPICS/program# mkdir ortec974A rootorangepi4-lts:/usr/local/EPICS/program# cd ortec974A/ rootorangepi4-lts:/usr/local/EPICS/program/ortec974A# ls2&#xff09;使用…

Java中的队列:各种类型及使用场景

在Java中&#xff0c;队列是一种重要的数据结构&#xff0c;用于存储按特定顺序排列的元素。队列在多线程环境中特别有用&#xff0c;因为它们可以用来解决并发问题。在Java中&#xff0c;队列主要分为以下几种类型&#xff1a; 接口&#xff1a; Queue: 这是Java Queue接口&…