基于 Go 语言构建在线论坛增补篇:通过 Viper 读取配置文件并实现热加载


viper

简介

之前我们在论坛项目中使用了单例模式全局加载配置文件,这样做有一个弊端,就是不支持热加载,每次修改配置文件,需要重启应用,不太灵活,所以这篇教程我们引入 Viper 重构配置读取逻辑,并支持配置文件的热加载(所谓热加载指的是配置文件修改后无需重启应用即可生效)。

Viper 是 Go 语言的完整配置解决方案,支持多个数据源和丰富的功能:

  • 支持设置默认配置值
  • 从 JSON、YAML、TOML、HCL 等格式配置文件读取配置值
  • 支持从 OS 中读取环境变量
  • 支持读取命令行参数
  • 支持从远程 KV 存储系统读取配置值,包括 Etcd、Consul 等
  • 可以监听配置值变化,支持热加载

配置好数据源,初始化并启动 Viper 后,就可以通过 viper.Get 获取任意数据源的配置值,非常方便,还可以调用 viper.Unmarshal 方法将配置值映射到指定结构体指针。

目前已经有很多知名 Go 项目在使用 Viper 作为配置解决方案,包括 HugoDocker NotaryNanoboxEMC REX-RayBloomApidoctlMercure 等。

话不多说,下面我们在论坛项目中引入 Viper 来加载配置文件。

使用 Viper 加载配置

开始之前,先安装 Viper 扩展包:

然后,我们在 config 目录下创建 viper.go 来定义基于 Viper 的配置初始化逻辑:

这里,我们基于项目根目录下的 config.json 作为配置文件,在 init 方法中对应的配置文件设置代码是如下这三行:

. 表示项目根目录,config 表示配置文件名(不含格式后缀),json 表示配置文件格式,然后通过 runtimeViper.ReadInConfig() 从配置文件读取所有配置,再通过 runtimeViper.Unmarshal(&ViperConfig) 将其映射到之前定义的位于 config.go 中的 Configuration 结构体变量 ViperConfig。接下来,就可以通过 ViperConfig 变量访问系统配置了,不过,由于本地化 LocaleBundle 属性需要额外初始化,所以我们参照单例模式中的实现对其进行初始化。ViperConfig 变量对外可见,所以只要引入 config 包的地方,都可以直接访问 ViperConfig 属性来读取配置值(init 方法会自动调用并且全局执行一次,所以无需考虑性能问题)。

重构配置读取代码

接下来,我们可以重构之前业务代码中的配置读取代码,首先是 main.go

然后是 handlers/helper.go

最后是 models/db.go

业务逻辑非常简单:取消了之前通过 LoadConfig 方法以单例模式初始化全局配置,改为直接调用 ViperConfig 对象属性获取配置值。

通过 Viper 实现热加载

但是现在配置文件依然不支持热加载,不过 Viper 提供了对应的 API 方法实现该功能,我们打开 config/viper.go,在 init 方法最后加上如下这段代码:

我们通过 runtimeViper.WatchConfig() 方法监听配置文件变更(该监听会开启新的协程执行,不影响和阻塞当前协程),一旦配置文件有变更,即可通过定义在 runtimeViper.OnConfigChange 中的匿名回调函数重新加载配置文件并将配置值映射到 ViperConfig 指针,同时再次加载新的语言文件。

这样,我们就实现了在 Go 项目中通过 Viper 实现配置文件的热加载,当然,这里只是一个简单的示例,Viper 还支持更丰富的数据源和配置操作,如果你想要了解更多可以参考 Viper 官方文档,这里就不一一介绍了。

测试配置文件读取和热加载

接下来,我们启动应用:

启动应用

启动成功,则表示通过 Viper 可以正确读取 App 配置,然后访问应用首页:

在线论坛首页

一切都正常,表示数据库配置读取也是 OK 的,本地化设置也正常,为了测试配置文件的热加载,我们将 App.Language 配置值设置为 en

在不重启应用的情况下,刷新论坛首页:

在线论坛首页

页面变成通过英文模板渲染的了,表明配置文件热加载生效。


<< 上一篇: 基于 Go 语言构建在线论坛(九):部署 Go Web 应用

>> 下一篇: 没有下一篇了