在golang应用中优雅的使用配置文件,并且简单优雅的快速接入分布式配置中心
Contents
在golang应用中优雅的使用配置文件,并且简单优雅的快速接入分布式配置中心
配置文件类型有很多种,像常用的properties、yaml、ini、xml、json等,还有不太常见的plist(xml)、toml、 HOCON等,还有很多特别自定义的格式,甚至有些就直接使用脚本语言来替代,像python、js,groovy等,无论是哪种配置文件,基本上都是三种类型:
-
结构化:yaml,xml,json
-
非结构化扁平格式:properties
-
半结构化和扁平结合
那么多的配置文件,如何选择呢?可以尝试给配置文件做个排序,来指导选择;对于常用的配置文件格式中,按照人类理解和方便编写配置来排序:
ini > properties > json > xml > yaml > toml
如果按照能配置复杂数据的复杂度来排序:
xml > yaml > toml/json/plist > ini > properties
对于这么多格式的配置文件如何选择呢?如果又或者应用了一个三方库,使用了和自己应用不一样的格式的配置文件,就不能统一了,不仅要引用各种格式解析的文件和获取配置项的三方库,而且各个三方库api又不一样,在程序中使用这些api,既混乱又得不到统一,有些库api支持的特性,另一种lib又不支持。
那么出现这种情况时,如何优雅的解决这个问题呢?这里今天的主角就要亮场了:props
props简介
props是一个统一的配置工具库,将各种配置源抽象或转换为类似properties格式的key/value,并提供统一的API来访问这些key/value。支持 properties 文件、ini 文件、zookeeper k/v、zookeeper k/props、consul k/v、consul k/props等配置源,并且支持通过 Unmarshal从配置中抽出struct;支持上下文环境变量的eval,${}形式;支持多种配置源组合使用。主要特性如下:
支持的配置源:
- properties格式文件
- ini格式文件
- yaml格式文件
- Apollok/v,k/props,k/ini,k/yaml
- Nacosk/props[properties],k/yaml,k/ini,k/ini_props
- zookeeper k/v
- zookeeper k/props[properties],k/yaml,k/ini,k/ini_props
- consul k/v
- consul k/props[properties],k/yaml,k/ini,k/ini_props
- etcd API V2 k/v
- etcd API V2 k/props
- etcd API V3 k/v
- etcd API V3 k/props
key/value支持的数据类型:
- key只支持string
- value 5种数据类型的支持:
- string
- int
- float64
- bool
- time.Time: 支持常见的格式和毫秒数
- time.Duration:
- 比如 “300ms”, “-1.5h” or “2h45m”.
- 合法的时间单位: “ns”, “us” (or “µs”), “ms”, “s”, “m”, “h”.
其他特性(非常有用的特性)
- Unmarshal支持, 可以将配置项注入到结构体中
- 上下文变量eval支持,
${}
形式 - 支持多配置源组合,包括本地和分布式中心的自由组合
- 默认添加了系统环境变量,优先级最低
props思路
props通过其名称就可以知道,基于properties格式的配置项作为基础,将各种配置源抽象或转换为类似properties格式的key/value,其思路来源于spring的PropertySource和PropertySources。无论哪种配置格式的文件,在读取后都已key/value形式存储在map中,需要读取配置项的地方可以定义的配置key获取配置值。因此不同的配资源,不管是本地文件还是远程配置中心,只要将读取的配置内容转换为key/value即可,那么对于props不支持的配置文件或者格式也可以轻松扩展。由于使用key/value作为基础配置,缺点就是对复杂数据格式支持不够,但换个角度,对于大部分应用来说无需太复杂的配置格式,太复杂了容易出错,另外确实要用到,golang api本身就内置了xml和json的解析库,支持Unmarshal,很容易使用。
在这个key/value基本的基础之上,拓展了Unmarshal、变量引用、配置组合、系统环境变量等功能,更方便的来维护配置。
在props中统一将配置文件或者分布式配置中心统一抽象为配置源,任何本地或者通过网络能读取到的文本配置并且可以转换为key/value的配置载体,都可以作为props的配置源,扩展也很简单,只要实现kvs.ConfigSource接口即可。
总之,就是围绕优雅、简洁、易用、可靠几个特点来构建,花里胡哨的功能也无需多加。
下面就来看看如何使用props。
安装:
go get -u github.com/tietang/props/v3
先睹为快:
例子1:
main.go
|
|
配置文件config.ini
:
|
|
运行结果如下:
|
|
从例子中可以看到,只需要一行代码就可以加载配置:
1
conf := ini.NewIniFileConfigSource("config.ini")
通过kvs.ConfigSource
提供的api进行配置项读取:
- Get(key string) (string, error)
- GetDefault(key, defaultValue string) string
- GetInt(key string) (int, error)
- GetIntDefault(key string, defaultValue int) int
- GetDuration(key string) (time.Duration, error)
- GetDurationDefault(key string, defaultValue time.Duration) time.Duration
- GetTime(key string) (time.Time, error)
- GetTimeDefault(key string, defaultValue time.Time) time.Time
- GetBool(key string) (bool, error)
- GetBoolDefault(key string, defaultValue bool) bool
- GetFloat64(key string) (float64, error)
- GetFloat64Default(key string, defaultValue float64) float64
要读取数组:
- Strings(key string) []string
- Ints(key string) []int
- Float64s(key string) []float64
- Durations(key string) []time.Duration
要配置数组,可以用:”|“,”,“,” “(空格),进行分割。
组合多个配置源(建议使用):
props内置了配置源组合:kvs.CompositeConfigSource, 可以组合任意多个任意形式的kvs.ConfigSource配置源。
比如,多个Properties文件组合:
NewPropertiesCompositeConfigSource(fileNames …string)
组合多个kvs.ConfigSource:
NewDefaultCompositeConfigSource(configSources …ConfigSource)
在已有的 kvs.CompositeConfigSource追加kvs.ConfigSource配置源:
Add(css …ConfigSource)
AddAll(css []ConfigSource)
更多功能查看:https://github.com/tietang/props
props使用多种配置格式文件和分布式配置中心
在props简介中已经知道,props支持的配置源,使用不同的配置源,只需要使用对应的包名+New函数进行构建即可。
properties格式文件
- 空内存Properties:kvs.NewMapProperties()
- 单个Properties文件:kvs.NewPropertiesConfigSource(“config.props”)
- 多个Properties文件:kvs.NewPropertiesCompositeConfigSource(files…)
ini格式文件
- 单个ini文件:ini.NewIniFileConfigSource()
- 多个ini文件:ini.NewIniFileCompositeConfigSource()
yaml格式文件
- 通过yaml文本:yam.ByYaml()
- 单个yaml文件:yam.NewYamlConfigSource()
- 多个yaml文件:yam.NewYamlFileCompositeConfigSource()
其他配置中心载体(zk,consul,etcd)
以下都类同,具体查看godoc或源码例子:
- zookeeper k/v
- zookeeper k/props[properties],k/yaml,k/ini,k/ini_props
- consul k/v
- consul k/props[properties],k/yaml,k/ini,k/ini_props
- etcd API V2 k/v
- etcd API V2 k/props
- etcd API V3 k/v
- etcd API V3 k/props
使用apollo作为配置源:
Apollo:支持多种格式k/v,k/props,k/ini,k/yaml
- 多namespace:apollo.NewApolloConfigSource()
- 多namespace+系统环境变量:apollo.NewApolloCompositeConfigSource()
只需要执行configService地址、appId,多个namespace即可构建一个kvs.ConfigSource配置源
|
|
如果configService暴露在公网且配置了应用访问密钥,可以使用WithSecret来指定访问密钥:
|
|
默认会监听该conf配置的所有Namespace, 如果不想监听某个namespace的配置更新,可以使用remove方法移除:
|
|
使用nacos作为配置源:
[Nacos:支持多种格式 k/props[properties],k/yaml,k/ini,k/ini_props
- 单个dataid:nacos.NewNacosClientConfigSource()
- 多dataid:nacos.NewNacosClientCompositeConfigSource()
- 单个dataid和kv格式:nacos.NewNacosClientPropsConfigSource()
- 多dataid和kv格式:nacos.NewNacosClientPropsCompositeConfigSource()
指定nacos服务地址、命名空间、1个或多个dataId,多个dataId会合并
|
|
如果一个应用有多个dataId,可以使用 NewNacosClientCompositeConfigSourcel
来构建:
|
|
默认会自动监听配置的dataId的配置更新,如果不希望某个dataIdId的配置不被热更新,可以调用cancel方法:
NacosClientConfigSource.CancelListening()
关于配置格式定义说明
配置格式支持:properties/props、yaml/yml/yam、ini 3种格式。
不同的配置中心对于配置格式的配置方法和获取不一样,apollo的配置格式可以通过namespace来获取,apollo namespace命名上默认是Properties格式,非Properties格式都要带上格式扩展名称,因此在props中通过apollo namespace扩展名来区分配置格式,默认为Properties,行为和apollo保持一致。对于nacos,虽然nacos控制台UI中支持text、json、xml、yaml、html、properties几种格式的编辑,但是由于nacos 配置获取的api中无法区分配置格式,仅仅在控制台编辑和展示时用到。另外,像ini格式的配置apollo和nacos都不支持,所以需要用一种方法在配置中标识所配置的内容是什么格式。其他配置中心也有类似的问题。
下面就目前比较流行的2中配置中心apollo和nacos进行配置格式标识的设计,通过2种方法来标识:
- 在关键名称上来标识,nacos中的dataId和apollo中的namespace都可以来标识
- 在配置内容文本中通过注释来标识
在关键名称上来标识
将格式类型作为后缀追加在关键名称上,nacos
追加在dataId上,apollo可以追加在namespace,一名称的后缀形式来标识,使用.
, -
, _
任意一个分割符分割即可,比如:
命名为:
test_ini
test-ini
test.ini
都可以,那么:
apollo中对应完整namespace名称为:
test_ini.txt
test-ini.txt
test.ini.txt
naco中对应完整dataId名称为:
test_ini
test-ini
test.ini
在配置内容文本中通过注释来标识
在配置文件首行通过注释的形式来标识配置格式:
#@, ;@, //@, @,并定义配置内容格式的信息,
比如:;@ini , #@yaml, #@yml等.
支持的格式标识有:;@ini,#@yaml, #@yml, #@yam,#@props,#@properties,
对于yaml和Properties格式,在界面上选择对应的格式或者text即可,对于ini格式选择text,并加标识;@ini即可。
需要注意不同格式的注释符号。
ini格式的标识:
apollo和nacos都不支持ini格式的配置,apollo中有txt,nacos中有text,2种标识方法都可以使用,选择配置格式是选择txt或text即可。
多配置的配置加载的优先级
-
后加载优先逻辑,也就是说后面加载的覆盖先加载的。注意后加载逻辑不等于配置顺序。
-
仅仅适用于kvs.CompositeConfigSource
对于kvs.CompositeConfigSource
,加载优先级顺序和配置顺序相同,先配置的优先级最高,后配置的优先级最低,也就是说通过NewDefaultCompositeConfigSource和NewCompositeConfigSource创建实例时,以及Add方法增加kvs.ConfigSource时,优先级是从左往右,从前往后。比如下面的代码中:
|
|
conf0优先级最高,conf2优先级最低,优先级依次为:conf0>conf1>conf2。
如果3个conf中存在同样的key,则以conf0中的为准;如果conf1和conf2中存在同样的key,则以conf1中的为准。
上下文变量引用${x.y.key}
在配置中使用变量引用:${x.y.key}。
props是支持配置上下文变量引用,在引用上下文中不区分优先级,仅仅依赖于配置加载的优先级。
目前仅仅kvs.CompositeConfigSource支持变量引用,建议使用New***CompositeConfigSource函数进行构建kvs.ConfigSource。
配置变量引用通过${x.y.key}
形式来引用。
下面是一个变量引用的例子:
config.ini
|
|
也支持多个变量组合,在字符串中嵌入变量引用即可:
|
|
Unmarshal为自定义结构体:
props支持将配置Unmarshal一个结构体实例。
先看看下面的例子
定义结构体:
|
|
配置文件:
|
|
Unmarshal:
|
|
使用时需要注意:
- 需要指定一个配置前缀,前缀在Unmarshal时会作为key的匹配和过滤中要使用到。
- 过滤掉key前缀后,剩余的key需要和结构体字段名称对应,匹配2种对应格式:
- 小写字母开头的驼峰命名方式,比如:Name>name,LogName > logName
- 中划线分割的全小写命名方式, 比如:LogName > log-name
支持嵌套结构体,对应key使用.
分割
比如配置文件如下:
|
|
对应于结构体:
|
|
使用系统环境变量
kvs.CompositeConfigSource构建默认都添加了系统环境变量,可以通过kvs.ConfigSource来读取系统环境变量。
如果不需要系统环境变量,可以通过:kvs.NewEmptyNoSystemEnvCompositeConfigSource()进行构建实例。
下面的例子可以查看加载了那些系统环境变量:
|
|
更多用法和特性查看:https://github.com/tietang/props
Author 铁汤
LastMod 2023-01-12