Viper-Go一站式配置管理工具
<svg xmlns="http://www.w3.org/2000/svg" style="display: none"><path stroke-linecap="round" d="M5,0 0,2.5 5,5z" id="raphael-marker-block" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0)"></path>
</svg>
<h4>什么是Viper</h4>
<p>Viper是一个方便Go语言应用程序处理配置信息的库。它可以处理多种格式的配置。它支持的特性:</p>
<ul>
<li>设置默认值</li>
<li>从JSON、TOML、YAML、HCL和Java properties文件中读取配置数据</li>
<li>可以监视配置文件的变动、重新读取配置文件</li>
<li>从环境变量中读取配置数据</li>
<li>从远端配置系统中读取数据,并监视它们(比如etcd、Consul)</li>
<li>从命令参数中读物配置</li>
<li>从buffer中读取</li>
<li>调用函数设置配置信息</li>
</ul>
<h4>为什么要使用Viper</h4>
<p>在构建现代应用程序时,您不必担心配置文件格式; 你可以专注于构建出色的软件。<br>
Viper 可以做如下工作:</p>
<ul>
<li>加载并解析JSON、TOML、YAML、HCL 或 Java properties 格式的配置文件</li>
<li>可以为各种配置项设置默认值</li>
<li>可以在命令行中指定配置项来覆盖配置值</li>
<li>提供了别名系统,可以不破坏现有代码来实现参数重命名</li>
<li>可以很容易地分辨出用户提供的命令行参数或配置文件与默认相同的区别</li>
</ul>
<p>Viper读取配置信息的优先级顺序,从高到低,如下:</p>
<ul>
<li>显式调用Set函数</li>
<li>命令行参数</li>
<li>环境变量</li>
<li>配置文件</li>
<li>key/value 存储系统</li>
<li>默认值</li>
</ul>
<p>Viper 的配置项的key不区分大小写。</p>
<p>项目地址:https://github.com/spf13/viper</p>
<h4>使用</h4>
<h5>设置默认值</h5>
<p>默认值不是必须的,如果配置文件、环境变量、远程配置系统、命令行参数、Set函数都没有指定时,默认值将起作用。</p>
<pre><code class="prism language-go">viper<span class="token punctuation">.</span><span class="token function">SetDefault</span><span class="token punctuation">(</span><span class="token string">"name"</span><span class="token punctuation">,</span> <span class="token string">"xiaoming"</span><span class="token punctuation">)</span>
viper<span class="token punctuation">.</span><span class="token function">SetDefault</span><span class="token punctuation">(</span><span class="token string">"age"</span><span class="token punctuation">,</span> <span class="token string">"12"</span><span class="token punctuation">)</span>
viper<span class="token punctuation">.</span><span class="token function">SetDefault</span><span class="token punctuation">(</span><span class="token string">"notifyList"</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token builtin">string</span><span class="token punctuation">{</span><span class="token string">"xiaohong"</span><span class="token punctuation">,</span><span class="token string">"xiaoli"</span><span class="token punctuation">,</span><span class="token string">"xiaowang"</span><span class="token punctuation">}</span><span class="token punctuation">)</span>
</code></pre>
<h5>读取配置文件</h5>
<p>Viper支持JSON、TOML、YAML、HCL和Java properties文件。<br>
Viper可以搜索多个路径,<strong>但目前单个Viper实例仅支持单个配置文件</strong>。<br>
Viper默认不搜索任何路径。<br>
以下是如何使用Viper搜索和读取配置文件的示例。<br>
路径不是必需的,但最好至少应提供一个路径,以便找到一个配置文件。</p>
<pre><code class="prism language-go">viper<span class="token punctuation">.</span><span class="token function">SetConfigName</span><span class="token punctuation">(</span><span class="token string">"dbConfig"</span><span class="token punctuation">)</span> <span class="token comment">//设置配置文件名 (不带后缀)</span>
viper<span class="token punctuation">.</span><span class="token function">AddConfigPath</span><span class="token punctuation">(</span><span class="token string">"/workspace/appName/"</span><span class="token punctuation">)</span> <span class="token comment">// 第一个搜索路径</span>
viper<span class="token punctuation">.</span><span class="token function">AddConfigPath</span><span class="token punctuation">(</span><span class="token string">"/workspace/appName1"</span><span class="token punctuation">)</span><span class="token comment">// 可以多次调用添加路径</span>
viper<span class="token punctuation">.</span><span class="token function">AddConfigPath</span><span class="token punctuation">(</span><span class="token string">"."</span><span class="token punctuation">)</span> <span class="token comment">// 比如添加当前目录</span>
err <span class="token operator">:=</span> viper<span class="token punctuation">.</span><span class="token function">ReadInConfig</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 搜索路径,并读取配置数据</span>
<span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>
<span class="token function">panic</span><span class="token punctuation">(</span>fmt<span class="token punctuation">.</span><span class="token function">Errorf</span><span class="token punctuation">(</span><span class="token string">"Fatal error config file: %s \n"</span><span class="token punctuation">,</span> err<span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre>
<h5>监视配置文件,重新读取配置数据</h5>
<p>Viper支持让你的应用程序在运行时拥有读取配置文件的能力。<br>
只需要调用viper实例的WatchConfig函数,你也可以指定一个回调函数来获得变动的通知。</p>
<pre><code class="prism language-go">viper<span class="token punctuation">.</span><span class="token function">WatchConfig</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
viper<span class="token punctuation">.</span><span class="token function">OnConfigChange</span><span class="token punctuation">(</span><span class="token keyword">func</span><span class="token punctuation">(</span>e fsnotify<span class="token punctuation">.</span>Event<span class="token punctuation">)</span> <span class="token punctuation">{</span>
fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"Config file changed:"</span><span class="token punctuation">,</span> e<span class="token punctuation">.</span>Name<span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
</code></pre>
<h5>从 io.Reader 中读取配置</h5>
<p>Viper预先定义了许多配置源,例如文件、环境变量、命令行参数和远程K / V存储系统。也可以实现自己的配置源,并提供给viper。</p>
<p>现在有如下yaml文件:</p>
<pre><code class="prism language-yaml">
<span class="token key atrule">userName</span><span class="token punctuation">:</span> <span class="token string">"xiaoming"</span>
<span class="token key atrule">address</span><span class="token punctuation">:</span> <span class="token string">"广州市XXX"</span>
<span class="token key atrule">sex</span><span class="token punctuation">:</span> <span class="token number">1</span>
<span class="token key atrule">company</span><span class="token punctuation">:</span>
<span class="token key atrule">name</span><span class="token punctuation">:</span> <span class="token string">"xxx"</span>
<span class="token key atrule">employeeId</span><span class="token punctuation">:</span> <span class="token number">1000</span>
<span class="token key atrule">department</span><span class="token punctuation">:</span>
<span class="token punctuation">-</span> <span class="token string">"技术部"</span>
</code></pre>
<p>读取文件的代码如下:</p>
<pre><code class="prism language-go"><span class="token keyword">package</span> main
<span class="token keyword">import</span> <span class="token punctuation">(</span>
<span class="token string">"fmt"</span>
<span class="token string">"github.com/spf13/viper"</span>
<span class="token punctuation">)</span>
<span class="token keyword">type</span> UserInfo <span class="token keyword">struct</span> <span class="token punctuation">{</span>
UserName <span class="token builtin">string</span>
Address <span class="token builtin">string</span>
Sex <span class="token builtin">byte</span>
Company Company
<span class="token punctuation">}</span>
<span class="token keyword">type</span> Company <span class="token keyword">struct</span> <span class="token punctuation">{</span>
Name <span class="token builtin">string</span>
EmployeeId <span class="token builtin">int</span>
Department <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token keyword">interface</span><span class="token punctuation">{</span><span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token comment">//读取yaml文件</span>
v <span class="token operator">:=</span> viper<span class="token punctuation">.</span><span class="token function">New</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token comment">//设置读取的配置文件名</span>
v<span class="token punctuation">.</span><span class="token function">SetConfigName</span><span class="token punctuation">(</span><span class="token string">"userInfo"</span><span class="token punctuation">)</span>
<span class="token comment">//windows环境下为%GOPATH,linux环境下为$GOPATH</span>
v<span class="token punctuation">.</span><span class="token function">AddConfigPath</span><span class="token punctuation">(</span><span class="token string">"/Users/yangyue/workspace/go/src/webDemo/"</span><span class="token punctuation">)</span>
<span class="token comment">//设置配置文件类型</span>
v<span class="token punctuation">.</span><span class="token function">SetConfigType</span><span class="token punctuation">(</span><span class="token string">"yaml"</span><span class="token punctuation">)</span>
<span class="token keyword">if</span> err <span class="token operator">:=</span> v<span class="token punctuation">.</span><span class="token function">ReadInConfig</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>
fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"err:%s\n"</span><span class="token punctuation">,</span>err<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"userName:%s sex:%s company.name:%s \n"</span><span class="token punctuation">,</span> v<span class="token punctuation">.</span><span class="token function">Get</span><span class="token punctuation">(</span><span class="token string">"userName"</span><span class="token punctuation">)</span><span class="token punctuation">,</span> v<span class="token punctuation">.</span><span class="token function">Get</span><span class="token punctuation">(</span><span class="token string">"sex"</span><span class="token punctuation">)</span><span class="token punctuation">,</span> v<span class="token punctuation">.</span><span class="token function">Get</span><span class="token punctuation">(</span><span class="token string">"company.name"</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token comment">//也可以直接反序列化为Struct</span>
<span class="token keyword">var</span> userInfo UserInfo
<span class="token keyword">if</span> err <span class="token operator">:=</span> v<span class="token punctuation">.</span><span class="token function">Unmarshal</span><span class="token punctuation">(</span><span class="token operator">&</span>userInfo<span class="token punctuation">)</span> <span class="token punctuation">;</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span><span class="token punctuation">{</span>
fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"err:%s"</span><span class="token punctuation">,</span>err<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span>userInfo<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre>
<p>上面的代码使用两种方式获取配置文件:第一种直接解析为key,value;第二种你可以手动的反序列化为Struct。</p>
<h5>从命令行参数中读取</h5>
<pre><code class="prism language-go"><span class="token keyword">package</span> main
<span class="token keyword">import</span> <span class="token punctuation">(</span>
<span class="token string">"fmt"</span>
<span class="token string">"github.com/spf13/pflag"</span>
<span class="token string">"github.com/spf13/viper"</span>
<span class="token punctuation">)</span>
<span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
pflag<span class="token punctuation">.</span><span class="token function">String</span><span class="token punctuation">(</span><span class="token string">"ip"</span><span class="token punctuation">,</span> <span class="token string">"127.0.0.1"</span><span class="token punctuation">,</span> <span class="token string">"Server running address"</span><span class="token punctuation">)</span>
pflag<span class="token punctuation">.</span><span class="token function">Int64</span><span class="token punctuation">(</span><span class="token string">"port"</span><span class="token punctuation">,</span> <span class="token number">8080</span><span class="token punctuation">,</span> <span class="token string">"Server running port"</span><span class="token punctuation">)</span>
pflag<span class="token punctuation">.</span><span class="token function">Parse</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
viper<span class="token punctuation">.</span><span class="token function">BindPFlags</span><span class="token punctuation">(</span>pflag<span class="token punctuation">.</span>CommandLine<span class="token punctuation">)</span>
fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"ip :%s , port:%s"</span><span class="token punctuation">,</span> viper<span class="token punctuation">.</span><span class="token function">GetString</span><span class="token punctuation">(</span><span class="token string">"ip"</span><span class="token punctuation">)</span><span class="token punctuation">,</span> viper<span class="token punctuation">.</span><span class="token function">GetString</span><span class="token punctuation">(</span><span class="token string">"port"</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre>
<p>命令行执行上面程序:</p>
<pre><code class="prism language-go"># <span class="token keyword">go</span> run test<span class="token punctuation">.</span><span class="token keyword">go</span> <span class="token operator">--</span>ip<span class="token operator">=</span><span class="token number">192.168</span><span class="token number">.7</span><span class="token number">.3</span> <span class="token operator">--</span>port<span class="token operator">=</span><span class="token number">3306</span>
</code></pre>
<p>可以看到输出的是我们自定义的参数。</p>
<h5>读取环境变量参数</h5>
<p>一般获取环境变量使用<code>os</code>包,比如:</p>
<pre><code class="prism language-go">getenv <span class="token operator">:=</span> os<span class="token punctuation">.</span><span class="token function">Getenv</span><span class="token punctuation">(</span><span class="token string">"JAVA_HOME"</span><span class="token punctuation">)</span>
fmt<span class="token punctuation">.</span><span class="token function">Print</span><span class="token punctuation">(</span>getenv<span class="token punctuation">)</span>
</code></pre>
<p>Viper也提供了一种方式:</p>
<pre><code class="prism language-go"><span class="token comment">//表示 先预加载匹配的环境变量</span>
viper<span class="token punctuation">.</span><span class="token function">AutomaticEnv</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token comment">//读取已经加载到default中的环境变量</span>
<span class="token keyword">if</span> env <span class="token operator">:=</span> viper<span class="token punctuation">.</span><span class="token function">Get</span><span class="token punctuation">(</span><span class="token string">"JAVA_HOME"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> env <span class="token operator">==</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>
<span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"error!"</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"%#v\n"</span><span class="token punctuation">,</span> env<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre>
<p>由获取环境变量我们是不是可以想到多环境参数配置呢?针对线上环境,开发环境分别加载不同yml中的参数。</p>
<pre><code class="prism language-go"><span class="token keyword">func</span> <span class="token function">initConfig</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">(</span>err <span class="token builtin">error</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
env <span class="token operator">:=</span> os<span class="token punctuation">.</span><span class="token function">Getenv</span><span class="token punctuation">(</span><span class="token string">"GO_ENV"</span><span class="token punctuation">)</span>
viper<span class="token punctuation">.</span><span class="token function">SetConfigName</span><span class="token punctuation">(</span>env<span class="token punctuation">)</span>
viper<span class="token punctuation">.</span><span class="token function">AddConfigPath</span><span class="token punctuation">(</span><span class="token string">"./configs"</span><span class="token punctuation">)</span>
viper<span class="token punctuation">.</span><span class="token function">SetConfigType</span><span class="token punctuation">(</span><span class="token string">"yml"</span><span class="token punctuation">)</span>
err <span class="token operator">=</span> viper<span class="token punctuation">.</span><span class="token function">ReadInConfig</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token keyword">return</span>
<span class="token punctuation">}</span>
</code></pre>
<p>因为无论是线上环境还是测试环境,肯定有一些参数是公共不变的,那么这一部分参数是否可以抽出来作为一个单独的配置文件呢。所以这样配置文件可以分为两个部分:</p>
<pre><code class="prism language-go"><span class="token string">"github.com/gobuffalo/packr"</span>
<span class="token keyword">func</span> <span class="token function">initConfig</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">(</span>err <span class="token builtin">error</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
box <span class="token operator">:=</span> packr<span class="token punctuation">.</span><span class="token function">NewBox</span><span class="token punctuation">(</span><span class="token string">"./configs"</span><span class="token punctuation">)</span>
configType <span class="token operator">:=</span> <span class="token string">"yml"</span>
defaultConfig<span class="token punctuation">,</span> <span class="token boolean">_</span> <span class="token operator">:=</span> box<span class="token punctuation">.</span><span class="token function">Find</span><span class="token punctuation">(</span><span class="token string">"default.yml"</span><span class="token punctuation">)</span>
v <span class="token operator">:=</span> viper<span class="token punctuation">.</span><span class="token function">New</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
v<span class="token punctuation">.</span><span class="token function">SetConfigType</span><span class="token punctuation">(</span>configType<span class="token punctuation">)</span>
err <span class="token operator">=</span> v<span class="token punctuation">.</span><span class="token function">ReadConfig</span><span class="token punctuation">(</span>bytes<span class="token punctuation">.</span><span class="token function">NewReader</span><span class="token punctuation">(</span>defaultConfig<span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>
<span class="token keyword">return</span>
<span class="token punctuation">}</span>
configs <span class="token operator">:=</span> v<span class="token punctuation">.</span><span class="token function">AllSettings</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token comment">// 将default中的配置全部以默认配置写入</span>
<span class="token keyword">for</span> k<span class="token punctuation">,</span> v <span class="token operator">:=</span> <span class="token keyword">range</span> configs <span class="token punctuation">{</span>
viper<span class="token punctuation">.</span><span class="token function">SetDefault</span><span class="token punctuation">(</span>k<span class="token punctuation">,</span> v<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
env <span class="token operator">:=</span> os<span class="token punctuation">.</span><span class="token function">Getenv</span><span class="token punctuation">(</span><span class="token string">"GO_ENV"</span><span class="token punctuation">)</span>
<span class="token comment">// 根据配置的env读取相应的配置信息</span>
<span class="token keyword">if</span> env <span class="token operator">!=</span> <span class="token string">""</span> <span class="token punctuation">{</span>
envConfig<span class="token punctuation">,</span> <span class="token boolean">_</span> <span class="token operator">:=</span> box<span class="token punctuation">.</span><span class="token function">Find</span><span class="token punctuation">(</span>env <span class="token operator">+</span> <span class="token string">".yml"</span><span class="token punctuation">)</span>
viper<span class="token punctuation">.</span><span class="token function">SetConfigType</span><span class="token punctuation">(</span>configType<span class="token punctuation">)</span>
err <span class="token operator">=</span> viper<span class="token punctuation">.</span><span class="token function">ReadConfig</span><span class="token punctuation">(</span>bytes<span class="token punctuation">.</span><span class="token function">NewReader</span><span class="token punctuation">(</span>envConfig<span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>
<span class="token keyword">return</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token keyword">return</span>
<span class="token punctuation">}</span>
</code></pre>
<p>首先读取default.yml中的参数,将其写入default中。然后再根据环境变量读取不同环境中的参数。</p>
<p>这里使用了packr包,packr包的作用在于将静态资源打包至应用程序中。</p><br><br>
来源:https://www.cnblogs.com/rickiyang/p/11074161.html
頁:
[1]