设为首页收藏本站
网站公告 | 这是第一条公告
     

 找回密码
 立即注册
缓存时间15 现在时间15 缓存数据 哪怕他只唱了几句,哪怕别人不仔细听都听不到他的声音,他还是要固执的在自己的名字后面写上他的名字,从他们两个名字写在一起的第一天起,就注定了一辈子不会分开[爱心]

哪怕他只唱了几句,哪怕别人不仔细听都听不到他的声音,他还是要固执的在自己的名字后面写上他的名字,从他们两个名字写在一起的第一天起,就注定了一辈子不会分开[爱心] -- 怎么了,没什么

查看: 1016|回复: 4

Go中的应用配置管理详解

[复制链接]

  离线 

TA的专栏

  • 打卡等级:热心大叔
  • 打卡总天数:226
  • 打卡月天数:0
  • 打卡总奖励:3523
  • 最近打卡:2025-03-13 12:07:03
等级头衔

等級:晓枫资讯-上等兵

在线时间
0 小时

积分成就
威望
0
贡献
429
主题
394
精华
0
金钱
4788
积分
880
注册时间
2023-1-6
最后登录
2025-6-1

发表于 2023-3-21 14:48:24 | 显示全部楼层 |阅读模式
问题


  • Go语言在编译时不会将配置文件这类第三方文件打包进二进制文件中
  • 它既受当前路径的影响,也会因所填写的不同而改变,并非是绝对可靠的

解决


命令行参数

在Go语言中,可以直接通过flag标准库来实现该功能。实现逻辑为,如果存在命令行参数,则优先使用命令行参数,否则使用配置文件中的配置参数。
如下:
  1. var (
  2.         port    string
  3.         runMode string
  4.         config  string
  5. )
  6. func init() {       
  7.         // 获取命令行参数
  8.         err = setupFlag()
  9.         if err != nil {
  10.                 log.Fatalf("init.setupFlag err: %v", err)
  11.         }
  12.     ......
  13. }
  14. // 获取命令行参数
  15. func setupFlag() error {
  16.         flag.StringVar(&port, "port", "", "启动端口")
  17.         flag.StringVar(&runMode, "mode", "", "启动模式")
  18.         flag.StringVar(&config, "config", "config/", "指定要使用的配置文件路径")
  19.         flag.Parse()
  20.         return nil
  21. }
复制代码
通过上述代码,我们可以通过标准库flag读取命令行参数,然后根据其默认值判断配置文件是否存在。若存在,则对读取配置的路径进行变更,代码如下:
  1. package setting
  2. import "github.com/spf13/viper"
  3. type Setting struct {
  4.         vp *viper.Viper
  5. }
  6. // 初始化配置文件的基础属性
  7. func NewSetting(configs ...string) (*Setting, error) {
  8.         vp := viper.New()
  9.         vp.SetConfigName("config")
  10.         if len(configs) != 0 {
  11.                 for _, config := range configs {
  12.                         if config != "" {
  13.                                 vp.AddConfigPath(config)
  14.                         }
  15.                 }
  16.         } else {
  17.                 vp.AddConfigPath("configs/")
  18.         }
  19.         vp.SetConfigType("yaml")
  20.         err := vp.ReadInConfig()
  21.         if err != nil {
  22.                 return nil, err
  23.         }
  24.         return &Setting{vp}, nil
  25. }
复制代码
接下来,对ServerSetting配置项进行覆写。如果存在,则覆盖原有的文件配置,使其优先级更高,代码如下:
  1. // 初始化配置文件
  2. func setupSetting() error {
  3.         setting, err := setting2.NewSetting(strings.Split(config, ",")...)
  4.         if err != nil {
  5.                 return err
  6.         }
  7.         ......
  8.         if port != "" {
  9.                 global.ServerSetting.HttpPort = port
  10.         }
  11.         if runMode != "" {
  12.                 global.ServerSetting.RunMode = runMode
  13.         }
  14.         return nil
  15. }
复制代码
然后在运行的时候传入参数即可:
  1. go run main.go -port=8081 -mode=debug -config=configs/
复制代码
系统环境变量

可以将配置文件存放在系统自带的全局变量中,如$HOME/conf或/etc/conf中,这样做的好处是不需要重新自定义一个新的系统环境变量。
一般来说,我们会在程序中内置一些系统环境变量的读取,其优先级低于命令行参数,但高于文件配置。

打包进二进制文件

可以将配置文件这种第三方文件打包进二进制文件中,这样就不需要过度关注这些第三方文件了。但这样做是有一定代价的,因此要注意使用的应用场景,即并非所有的项目都能这样操作。
首先安装go-bindata库,安装命令如下:
  1. go get -u github.com/go-bindata/go-bindata/...
复制代码
通过go-bindata库可以将数据文件转换为Go代码。例如,常见的配置文件、资源文件(如Swagger UI)等都可以打包进Go代码中,这样就可以“摆脱”静态资源文件了。接下来在项目根目录下执行生成命令:
  1. go-bindata -o configs/config.go -pkg-configs configs/config.yaml
复制代码
执行这条命令后,会将 configs/config.yaml 文件打包,并通过-o 选项指定的路径输出到configs/config.go文件中,再通过设置的-pkg选项指定生成的packagename为configs,接下来只需执行下述代码,就可以读取对应的文件内容了:
  1. b,_:=configs.Asset("configs/config.yaml")
复制代码
把第三方文件打包进二进制文件后,二进制文件必然增大,而且在常规方法下无法做文件的热更新和监听,必须要重启并且重新打包才能使用最新的内容,因此这种方式是有利有弊的。

配置热更新


开源的fsnotify

既然要做配置热更新,那么首先要知道配置是什么时候修改的,做了哪些事?因此我们需要对所配置的文件进行监听,只有监听到了,才能知道它做了哪些变更。
开源库 fsnotify 是用Go语言编写的跨平台文件系统监听事件库,常用于文件监听,因此我们可以借助该库来实现这个功能。

(1)安装
  1. go get -u golang.org/x/sys/...
  2. go get -u github.com/fsnotify/fsnotify
复制代码
fsnotify是基于golang.org/x/sys实现的,并非syscall标准库,因此在安装的同时需要更新其版本,确保版本是最新的。

(2)案例
  1. package main
  2. import (
  3.         "gopkg.in/fsnotify.v1"
  4.         "log"
  5. )
  6. func main() {
  7.         watcher, _ := fsnotify.NewWatcher()
  8.         defer watcher.Close()
  9.         done := make(chan bool)
  10.         go func() {
  11.                 for {
  12.                         select {
  13.                         case event, ok := <-watcher.Events:
  14.                                 if !ok {
  15.                                         return
  16.                                 }
  17.                                 log.Fatal("event: ", event)
  18.                                 if event.Op&fsnotify.Write == fsnotify.Write {
  19.                                         log.Println("modified file:", event.Name)
  20.                                 }
  21.                         case err, ok := <-watcher.Errors:
  22.                                 if !ok {
  23.                                         return
  24.                                 }
  25.                                 log.Fatal("error:", err)
  26.                         }
  27.                 }
  28.         }()
  29.         path := "configs/config.yaml"
  30.         _ = watcher.Add(path)
  31.         <-done
  32. }
复制代码
通过监听,我们可以很便捷地知道文件做了哪些变更。进一步来说,我们可以通过对其进行二次封装,在它的上层实现一些变更动作来完成配置文件的热更新。

使用viper开源库实现热更新

viper开源库能够很便捷地实现对文件的监听和热更新。
打开pkg/setting/section.go文件,针对重载应用配置项,新增如下处理方法:
  1. var sections = make(map[string]interface{})
  2. // 解析配置文件
  3. func (s *Setting) ReadSection(k string, v interface{}) error {
  4.         err := s.vp.UnmarshalKey(k, v)
  5.         if err != nil {
  6.                 return err
  7.         }
  8.         if _,ok:=sections[k];!ok{
  9.                 sections[k] = v
  10.         }
  11.         return nil
  12. }
复制代码
首先修改ReadSection方法,增加读取section的存储记录,以便在重新加载配置的方法中进行二次处理。接下来新增ReloadAllSection方法,重新读取配置,代码如下:
  1. // 读取所有配置
  2. func (s *Setting) ReadAllSections() error {
  3.         for k, v := range sections {
  4.                 err := s.ReadSection(k, v)
  5.                 if err != nil {
  6.                         return err
  7.                 }
  8.         }
  9.         return nil
  10. }
  11. // 监听配置变化
  12. func (s *Setting) WatchSettingChange()  {
  13.         go func() {
  14.                 s.vp.WatchConfig()
  15.                 s.vp.OnConfigChange(func(in fsnotify.Event) {
  16.                         _ = s.ReloadAllSections()
  17.                 })
  18.         }()
  19. }
复制代码
最后在pkg/setting/setting.go文件中新增文件热更新的监听和变更处理,代码如下:
  1. // 初始化配置文件的基础属性
  2. func NewSetting(configs ...string) (*Setting, error) {
  3.         vp := viper.New()
  4.         vp.SetConfigName("config")
  5.         for _, config := range configs {
  6.                 if config != "" {
  7.                         vp.AddConfigPath(config)
  8.                 }
  9.         }
  10.         vp.SetConfigType("yaml")
  11.         err := vp.ReadInConfig()
  12.         if err != nil {
  13.                 return nil, err
  14.         }
  15.     // 加入热更新
  16.         s := &Setting{vp: vp}
  17.         s.WatchSettingChange()
  18.         return s, nil
  19. }
复制代码
在上述代码中,首先在NewSetting方法中起一个协程,再在里面通过WatchConfig方法对文件配置进行监听,并在OnConfigChange方法中调用刚刚编写的重载方法ReloadAllSection来处理热更新的文件监听事件回调,这样就可以“悄无声息”地实现一个文件配置热更新了。
OnConfigChange方法的回调方法形参,其实就是fsnotify。
以上就是Go中的应用配置管理详解的详细内容,更多关于Go应用配置管理的资料请关注晓枫资讯其它相关文章!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
晓枫资讯-科技资讯社区-免责声明
免责声明:以上内容为本网站转自其它媒体,相关信息仅为传递更多信息之目的,不代表本网观点,亦不代表本网站赞同其观点或证实其内容的真实性。
      1、注册用户在本社区发表、转载的任何作品仅代表其个人观点,不代表本社区认同其观点。
      2、管理员及版主有权在不事先通知或不经作者准许的情况下删除其在本社区所发表的文章。
      3、本社区的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,举报反馈:点击这里给我发消息进行删除处理。
      4、本社区一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
      5、以上声明内容的最终解释权归《晓枫资讯-科技资讯社区》所有。
http://bbs.yzwlo.com 晓枫资讯--游戏IT新闻资讯~~~

  离线 

TA的专栏

等级头衔

等級:晓枫资讯-列兵

在线时间
0 小时

积分成就
威望
0
贡献
0
主题
0
精华
0
金钱
21
积分
22
注册时间
2022-12-25
最后登录
2022-12-25

发表于 2024-3-4 15:26:03 | 显示全部楼层
感谢楼主,顶。
http://bbs.yzwlo.com 晓枫资讯--游戏IT新闻资讯~~~

  离线 

TA的专栏

等级头衔

等級:晓枫资讯-列兵

在线时间
0 小时

积分成就
威望
0
贡献
0
主题
0
精华
0
金钱
16
积分
12
注册时间
2022-12-28
最后登录
2022-12-28

发表于 2024-5-24 08:26:32 | 显示全部楼层
感谢楼主分享。
http://bbs.yzwlo.com 晓枫资讯--游戏IT新闻资讯~~~

  离线 

TA的专栏

等级头衔

等級:晓枫资讯-列兵

在线时间
0 小时

积分成就
威望
0
贡献
0
主题
0
精华
0
金钱
14
积分
8
注册时间
2022-12-30
最后登录
2022-12-30

发表于 2025-2-14 07:50:47 | 显示全部楼层
路过,支持一下
http://bbs.yzwlo.com 晓枫资讯--游戏IT新闻资讯~~~

  离线 

TA的专栏

  • 打卡等级:即来则安
  • 打卡总天数:23
  • 打卡月天数:0
  • 打卡总奖励:265
  • 最近打卡:2025-04-10 03:20:18
等级头衔

等級:晓枫资讯-列兵

在线时间
0 小时

积分成就
威望
0
贡献
0
主题
0
精华
0
金钱
307
积分
48
注册时间
2023-1-1
最后登录
2025-4-10

发表于 4 天前 | 显示全部楼层
顶顶更健康!!!
http://bbs.yzwlo.com 晓枫资讯--游戏IT新闻资讯~~~
严禁发布广告,淫秽、色情、赌博、暴力、凶杀、恐怖、间谍及其他违反国家法律法规的内容。!晓枫资讯-社区
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

1楼
2楼
3楼
4楼
5楼

手机版|晓枫资讯--科技资讯社区 本站已运行

CopyRight © 2022-2025 晓枫资讯--科技资讯社区 ( BBS.yzwlo.com ) . All Rights Reserved .

晓枫资讯--科技资讯社区

本站内容由用户自主分享和转载自互联网,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。

如有侵权、违反国家法律政策行为,请联系我们,我们会第一时间及时清除和处理! 举报反馈邮箱:点击这里给我发消息

Powered by Discuz! X3.5

快速回复 返回顶部 返回列表