鸽爷 發表於 2024-8-20 08:00:00

Viper:强大的Go配置解析库

<h1 id="1-介绍">1 介绍</h1>
<p>Viper是适用于Go应用程序的完整配置解决方案。它被设计用于在应用程序中工作,并且可以处理所有类型的配置需求和格式。目前Star 26.6k, 它支持以下特性:</p>
<ul>
<li>设置默认值</li>
<li>从JSON、TOML、YAML、HCL、envfile和Java properties格式的配置文件读取配置信息</li>
<li>实时监控和重新读取配置文件(可选)</li>
<li>从环境变量中读取</li>
<li>从远程配置系统(etcd或Consul)读取并监控配置变化</li>
<li>从命令行参数读取配置</li>
<li>从buffer读取配置</li>
<li>显式配置值</li>
</ul>
<h1 id="2-golang项目中的使用">2 Golang项目中的使用</h1>
<h2 id="21-在go中安装viper">2.1 在go中安装Viper</h2>
<pre><code class="language-BASH"># 终端中输入如下命令
ArchitePlus@MacBook-Air traffic.demo % go get github.com/spf13/viper
</code></pre>
<h2 id="22-编写通用配置文件">2.2 编写通用配置文件</h2>
<p>因为能支持多重配置文件格式,包含 JSON、TOML、YAML、HCL、INI、envfile 和 Java 属性文件,方便开发者根据项目需求选择合适的格式。<br>
我们这边使用yaml做示例。</p>
<p>创建一个<code>conf</code>文件夹,添加子文件夹<code>files</code>,然后在下面添加<code>config.yaml</code>,里面可以放一些基本的、通用的配置信息。</p>
<pre><code class="language-YAML">app: # 应用基本配置
env: local # 环境名称
port: 8888 # 服务监听端口号
app_name: traffic-demo # 应用名称
app_url: http://localhost # 应用访问地址


MySQL: # MySQL配置
host: 127.0.0.1 # MySQL主机地址
port: 3306 # MySQL端口号
user: root # MySQL用户名
password: &lt;PASSWORD&gt;
db_name: traffic # MySQL数据库名
</code></pre>
<p>可以看到,我们有两个配置信息,一个是 app,一个是MySQL。</p>
<h2 id="23-编写用户自定义配置文件">2.3 编写用户自定义配置文件</h2>
<p>还有一些用户自定义的配置文件(可能有多个), 是需要根据不同的运行环境(local、dev、beta、prod)来进行区分的.所以我们在<code>config/files/</code>下面创建四个文件夹 <code>local</code>、<code>dev</code>、<code>beta</code>、<code>prod</code> 四个文件夹,每个文件夹都有一个<code>custom.yaml</code>文件,当 <code>app.env</code> 的值变化的时候,读取的文件也跟着变化,下面是local的信息</p>
<pre><code class="language-YAML">white_list:
user_id: # 用户列表
- 063105015
- 063105024
- 063105028
request_path: # 访问路径
- /api/v1/users
- /api/v1/ops
</code></pre>
<h2 id="24-配置映射的结构体">2.4 配置映射的结构体</h2>
<p>我们需要配置结构体(实体对象)来映射这俩配置,这样的话,后面在调用的时候非常方便。<br>
在<code>conf</code>文件夹下面添加子文件夹<code>model</code>,存放解析映射的结构体,这边新增一个<code>config.go</code>和一个<code>custom.go</code>文件,内容如下:</p>
<h3 id="241-configgo">2.4.1 config.go</h3>
<pre><code class="language-GO">package config

// 配置文件解析汇总
type Configuration struct {
        App   App   `mapstructure:"app" json:"app" yaml:"app"`
        MYSQL MYSQL `mapstructure:"mysql" json:"mysql" yaml:"mysql"`
}

// 配置文件解析:app
type App struct {
        Env   string `mapstructure:"env" json:"env" yaml:"env"`
        Port    string `mapstructure:"port" json:"port" yaml:"port"`
        AppName string `mapstructure:"app_name" json:"app_name" yaml:"app_name"`
        AppUrlstring `mapstructure:"app_url" json:"app_url" yaml:"app_url"`
}

// 配置文件解析:mysql
type MYSQL struct {
        Host   string `mapstructure:"host" json:"host" yaml:"host"`
        Port   string `mapstructure:"poet" json:"port" yaml:"port"`
        User   string `mapstructure:"user" json:"user" yaml:"user"`
        Password string `mapstructure:"password" json:"password" yaml:"password"`
        DbName   string `mapstructure:"db_name" json:"db_name" yaml:"db_name"`
}

</code></pre>
<h3 id="242-customgo">2.4.2 custom.go</h3>
<pre><code class="language-GO">package config

type Custom struct {
        WhiteList whiteList `mapstructure:"white_list" json:"white_list" yaml:"white_list"`
}

// 配置文件解析汇总
type whiteList struct {
        UserId      []string `mapstructure:"user_id" json:"user_id" yaml:"user_id"`
        RequestPath []string `mapstructure:"request_path" json:"request_path" yaml:"request_path"`
}

</code></pre>
<h2 id="25-创建global全局变量解析">2.5 创建Global全局变量解析</h2>
<p>新建一个 <code>global/app.go</code> 文件,定义 Application 结构体,用来存放一些项目启动时的变量,方便调用。<br>
目前先将 viper 结构体和 Configuration 结构体放入,后续会陆续添加其他配置信息。</p>
<pre><code class="language-GO">package global

import (
        "github.com/spf13/viper"
        config "traffic.demo/config/model"
)

// 定义一个全局的Application
type Application struct {
        ConfigViper *viper.Viper
        Config      config.Configuration
        Custom      config.Custom
}

// 初始化Application
var App = new(Application)

</code></pre>
<h2 id="25-关键步骤结构体映射逻辑">2.5 关键步骤:结构体映射逻辑</h2>
<p>配置文件要映射到结构体,这样才能把配置数据提取出来,这边创建 <code>bootstrap/config.go</code> 文件,作为核心解析代码的载体,代码如下(代码中的解释已经很清楚了):</p>
<pre><code class="language-GO">package bootstrap

import (
        "fmt"

        "github.com/fsnotify/fsnotify"
        "github.com/spf13/viper"
        "traffic.demo/global"
)

// configAssemble 是一个泛型函数,用于组装配置文件并返回 viper.Viper 指针
//
// 参数:
//
//        configPath string - 配置文件路径
//        viperStruct T - 用来接收配置文件的结构体
//
// 返回值:
//
//        *viper.Viper - viper.Viper 指针
func configAssemble(configPath string, viperStruct T) *viper.Viper {
        // 初始化 viper
        v := viper.New()
        // 配置文件地址
        v.SetConfigFile(configPath)
        // 配置文件类型,yaml
        v.SetConfigType("yaml")
        if err := v.ReadInConfig(); err != nil {
                panic(fmt.Errorf("read config failed: %s \n", err))
        }

        // 监听配置文件
        v.WatchConfig()
        v.OnConfigChange(func(in fsnotify.Event) {
                fmt.Println("config file changed:", in.Name)
                // 重载配置 &amp;global.App.Config
                if err := v.Unmarshal(viperStruct); err != nil {
                        fmt.Println(err)
                }
        })
        // 将配置赋值给全局变量 &amp;global.App.Config
        if err := v.Unmarshal(viperStruct); err != nil {
                fmt.Println(err)
        }

        return v
}

// InitializeConfig 初始化配置函数
func InitializeConfig() {
        // 全局应用文件配置路径,这边是我们的具体global config文件地址
        config := "conf/files/config.yaml"
        configAssemble(config, &amp;global.App.Config)

        // 用户自定义的配置(根据不同的运行环境,加载不同的配置文件)
        customConfig := fmt.Sprintf("%s%s%s", "conf/files/", global.App.Config.App.Env, "/custom.yaml")
        configAssemble(customConfig, &amp;global.App.Custom)

}
</code></pre>
<h2 id="26-整体文件结构如下">2.6 整体文件结构如下</h2>
<p><img src="https://img2024.cnblogs.com/blog/167509/202408/167509-20240818175921437-1947480766.png" alt="image" loading="lazy"></p>
<h2 id="27-运行结果">2.7 运行结果</h2>
<p>main.go 代码如下:</p>
<pre><code class="language-GO">package main

import (
        "fmt"
        "traffic.demo/global"
)

// main 函数是程序的入口点
func main() {

    bootstrap.InitializeConfig()
    fmt.Println("Traffic Service Started...!")

        var globalCong = global.App.Config
        fmt.Printf("globalCong: %+v\n", globalCong)
        var customCong = global.App.Custom
        fmt.Printf("customCong: %+v\n", customCong)
}

</code></pre>
<p>效果如下:<br>
<img src="https://img2024.cnblogs.com/blog/167509/202408/167509-20240818180225727-991508105.png" alt="image" loading="lazy"></p>
<h1 id="3-总结">3 总结</h1>
<p>Viper 是一个功能强大、简洁、易于的 Go 配置库,帮助开发者轻松管理应用程序的配置,并提供灵活的接入方式</p>


</div>
<div id="MySignature" role="contentinfo">
   
<div id="singturebox" >

    <img src="https://images.cnblogs.com/cnblogs_com/wzh2010/2053950/o_240115020114_qrcode_for_gh_52dedf6e01c8_1280.jpg"
alt="架构与思维公众号" class="img_avatar" width="180px" style="border-radius:1%;float:left;margin-right:16px;">

<div id="officAccMemo" >
架构与思维·公众号:撰稿者为bat、字节、美团的几位高阶研发/架构,努力分享优质技术
</div>
<div id="officAccMemoS" >
★ 加公众号获取学习资料和面试集锦
</div>
<div>码字不易,欢迎关注,欢迎转载</div>
<div>作者:Brand</div>
<div>出处:https://www.cnblogs.com/wzh2010/</div>
<div>本文采用「CC BY 4.0」知识共享协议进行许可,转载请注明作者及出处。</div>
</div><br><br>
来源:https://www.cnblogs.com/wzh2010/p/18364344
頁: [1]
查看完整版本: Viper:强大的Go配置解析库