Go Config作为配置库,它也是动态的可插拔的。

大多数的应用配置都是放在静态文件中或者用复杂的逻辑从不同的文件中加载。

Go-config就简单得多,可插拔且可合并。

特性

  • 动态加载 - 动态按时按需从多资源加载配置。Go Config会在后台监视配置资源,动态在内存中合并、更新。

  • 可插拔资源 - 可选择从任意数量的资源中加载、合并配置,后台资源在内部被抽象成标准格式并通过编码器解码。资源可以是环境变量、参数flag、文件、etcd、k8s configmap等等。

  • 可合并配置 - 假设指定了多个配置源,格式不限,它们会被合并划一。 这样大大简化了配置的优先级与环境的变动。

  • 观察变动 - 可以选择观测指定配置值的变动。使用Go Config观测器热加载,可以随时查看配置值的变动情况。

  • 安全修复 - 某些情况如配置加载失败或者被擦除时,可以指定回退值。这可以保证在发生事故时,我们能读取完整的默认值。

开始

  • Source - 后台获取加载的位置
  • Encoder - 负责处理资源配置编码、解码
  • Reader - 将多个编码处理后的资源合并成单一的格式
  • Config - 配置管理器,负责管理多资源
  • Usage - go-config使用示例
  • FAQ - 常见问题及回答
  • TODO - 将来要开发的任务或特性

Sources

Source也即后台加载的配置,同时可以使用多资源。

支持以下格式:

ChangeSet变更集

Sources以变更集的方式返回配置。对于多个后台配置,变更集是单一的内部抽象。

  1. type ChangeSet struct {
  2. // Raw encoded config data
  3. Data []byte
  4. // MD5 checksum of the data
  5. Checksum string
  6. // Encoding format e.g json, yaml, toml, xml
  7. Format string
  8. // Source of the config e.g file, consul, etcd
  9. Source string
  10. // Time of loading or update
  11. Timestamp time.Time
  12. }

Encoder

Encoder负责资源配置编码、解码。后台资源可能会存在不同的格式,编码器负责处理不同的格式,默认的格式是Json。

编码器支持以下格式:

  • json
  • yaml
  • toml
  • xml
  • hcl

Reader

Reader负责把多个changeset集合并成一个可查询的值集。

  1. type Reader interface {
  2. // Merge multiple changeset into a single format
  3. Merge(...*source.ChangeSet) (*source.ChangeSet, error)
  4. // Return return Go assertable values
  5. Values(*source.ChangeSet) (Values, error)
  6. // Name of the reader e.g a json reader
  7. String() string
  8. }

Reader复用Encoder编码器将changeset集解码成map[string]interface{}格式,然后将它们合成一个changeset。它通过格式来确定使用哪个解码器。合成的changeset中的Values可以转成Go类型值,而如果有值不能加载时,其中的值也可以作为回退值使用。

  1. // Values is returned by the reader
  2. type Values interface {
  3. // Return raw data
  4. Bytes() []byte
  5. // Retrieve a value
  6. Get(path ...string) Value
  7. // Return values as a map
  8. Map() map[string]interface{}
  9. // Scan config into a Go type
  10. Scan(v interface{}) error
  11. }

Value接口支持使用构建、类型断言转化成go类型的值,默认使用回退值。

  1. type Value interface {
  2. Bool(def bool) bool
  3. Int(def int) int
  4. String(def string) string
  5. Float64(def float64) float64
  6. Duration(def time.Duration) time.Duration
  7. StringSlice(def []string) []string
  8. StringMap(def map[string]string) map[string]string
  9. Scan(val interface{}) error
  10. Bytes() []byte
  11. }

Config

Config管理所有配置、抽象后的资源、编码器及reader。

读取、同步、监视多个后台资源,把资源合并成单一集合以供查询。

  1. // Config is an interface abstraction for dynamic configuration
  2. type Config interface {
  3. // provide the reader.Values interface
  4. reader.Values
  5. // Stop the config loader/watcher
  6. Close() error
  7. // Load config sources
  8. Load(source ...source.Source) error
  9. // Force a source changeset sync
  10. Sync() error
  11. // Watch a value for changes
  12. Watch(path ...string) (Watcher, error)
  13. }

使用方式

配置样式

配置文件可以是Encoder解码器支持的任何格式:

JSON json config:

  1. {
  2. "hosts": {
  3. "database": {
  4. "address": "10.0.0.1",
  5. "port": 3306
  6. },
  7. "cache": {
  8. "address": "10.0.0.2",
  9. "port": 6379
  10. }
  11. }
  12. }

新增配置

新增配置(直接使用默认的配置对象也可)

  1. import "github.com/micro/go-config"
  2. conf := config.NewConfig()

加载配置文件

加载文件配置,文件的扩展名即为配置的格式。

  1. import (
  2. "github.com/micro/go-config"
  3. )
  4. // Load json config file
  5. config.LoadFile("/tmp/config.json")

也可以是其它如yaml、toml或者xml,看适当情况使用。

  1. // Load yaml config file
  2. config.LoadFile("/tmp/config.yaml")

如果没有扩展名时,则指定编码器

  1. import (
  2. "github.com/micro/go-config"
  3. "github.com/micro/go-config/source/file"
  4. )
  5. enc := toml.NewEncoder()
  6. // Load toml file with encoder
  7. config.Load(file.NewSource(
  8. file.WithPath("/tmp/config"),
  9. source.WithEncoder(enc),
  10. ))

读取配置

读取全部配置

  1. // retrieve map[string]interface{}
  2. conf := config.Map()
  3. // map[cache:map[address:10.0.0.2 port:6379] database:map[address:10.0.0.1 port:3306]]
  4. fmt.Println(conf["hosts"])

将配置写入结构

  1. type Host struct {
  2. Address string `json:"address"`
  3. Port int `json:"port"`
  4. }
  5. type Config struct{
  6. Hosts map[string]Host `json:"hosts"`
  7. }
  8. var conf Config
  9. config.Scan(&conf)
  10. // 10.0.0.1 3306
  11. fmt.Println(conf.Hosts["database"].Address, conf.Hosts["database"].Port)

读取多个值

如果将配置写入结构

  1. type Host struct {
  2. Address string `json:"address"`
  3. Port int `json:"port"`
  4. }
  5. var host Host
  6. config.Get("hosts", "database").Scan(&host)
  7. // 10.0.0.1 3306
  8. fmt.Println(host.Address, host.Port)

读取独立的值

  1. // 获取address值,缺省值使用 “localhost”
  2. address := config.Get("hosts", "database", "address").String("localhost")
  3. // 获取port值,缺省值使用 3000
  4. port := config.Get("hosts", "database", "port").Int(3000)

观测目录

观测目录的变化。当文件有改动时,新值便可生效。

  1. w, err := config.Watch("hosts", "database")
  2. if err != nil {
  3. // do something
  4. }
  5. // wait for next value
  6. v, err := w.Next()
  7. if err != nil {
  8. // do something
  9. }
  10. var host Host
  11. v.Scan(&host)

多资源

多资源可以加载并合并,合并优先级顺序则是反向的,即后面导入的优先级高。

  1. config.Load(
  2. // base config from env
  3. env.NewSource(),
  4. // override env with flags
  5. flag.NewSource(),
  6. // override flags with file
  7. file.NewSource(
  8. file.WithPath("/tmp/config.json"),
  9. ),
  10. )

设置资源编码器

资源需要编码器才能将配置编码与解码成所需的changeset格式。

默认编码器是JSON格式,也可以使用yaml、xml、toml等选项。

  1. e := yaml.NewEncoder()
  2. s := consul.NewSource(
  3. source.WithEncoder(e),
  4. )

新增Reader编码器

Reader使用各种编码器来解码不同格式源的数据。

默认的Reader支持json、yaml、xml、toml、hcl,合并配置后会把其转成json格式。

也可指定给其特定的编码器:

  1. e := yaml.NewEncoder()
  2. r := json.NewReader(
  3. reader.WithEncoder(e),
  4. )

常见问题

与Viper有哪些不同?

Viper与go-config都着力解决同样的问题,只是Go-confi使用不同的接口,并且它是micro工具生态中的一员。

Encoder和Reader有什么不同?

Encoder编码器负责的是后台资源数据的编解码工作。而Reader则使用不同的encoder解码,解码的配置源可能有不同的格式,而这些encoder会解决这个事情并合并成单一编码格式。

如果是文件资源,则配置的解码器取决于文件的扩展名。

如果是基于consul的配置、etcd或类似的键值对资源,则可能会从前缀中带有多个键(特定规则)的加载,也就是说资源需要明白编码,才能返回单一的变更集。

如果是环境变量、参数flag,则会把这些值编码成byte数组,指定格式以备reader合并。

为什么changeset变更集不是map[string]interface{}的格式?

总有配置源数据不是键值对的情况,而byte数组的方式再解码给Reader更简单些。