1. 配置
Go Config 是一个可插拔的动态配置库
2. 概述
应用程序中的大多数配置都是静态配置的或者包括从多个源加载的复杂逻辑. Go-config 使此变得简单, 可插拔和可合并. 你再也不必以旧的的方式处理配置了.
2.1. 特性
- 动态加载 - 在需要时从多个源加载配置. Go Config 管理在后台监视配置源, 并自动合并和更新内存视图中的配置文件源.
- 可插拔的源 - 从任意数量的源中进行选择以加载和合并配置. 后端源被抽象为内部使用并通过编码器解码的标准格式. 源可以是 env vars, flags, etcd, k8s configmap, 等.
- 可合并配置 - 如果指定多个配置源, 无论格式如何, 它们都将合并并在单个视图中显示. 这极大地简化了基于环境的优先级顺序加载和更改.
- 观察更改 - 可选地观察配置, 以观察对特定值的更改. 使用 Go Config 的观察程序进行热重新加载你的应用. 不必处理临时关机重新加载或其他任何内容, 只需继续读取配置并监视需要通知的更改.
- 安全恢复 - 如果配置负载严重或由于未知原因完全擦除, 则可以在直接访问任何配置值时指定回退值. 这可确保在发生问题时始终读取某些合理的默认值.
2.2. 开始
- 源 - 配置从中加载的后端
- 编码器 - 处理编码/解码源配置
- 读取器 - 将多个编码源合并为单一格式
- 配置 - 管理多个源的配置管理器
- 使用 - go-config 的示例用法
- 常见问题 - 常见问题和回答
- 待办 - 待办任务/特性
2.3. 源
Source
是从加载配置的后端. 可以同时使用多个源.
支持以下源:
- cli - 从 CLI 标志读取
- consul - 从 consul 读取
- env - 从环境变量读取
- etcd - 从 etcd v3 读取
- file - 从文件读取
- flag - 从标志读取
- memory - 从内存读取
还有一些社区支持的插件, 支持以下源:
- configmap - 从 k8s configmap 读取
- grpc - 从 grpc 服务器读取
- runtimevar - 从 Go Cloud Development Kit 运行时变量读取
- url - 从 url 读取
- vault - 从 Vault 服务器读取
待办:
- git url
2.3.1. 更改集
源将配置作为一个 ChangeSet 返回. 这是多个后端的单一内部抽象.
type ChangeSet struct {
// Raw encoded config data
Data []byte
// MD5 checksum of the data
Checksum string
// Encoding format e.g json, yaml, toml, xml
Format string
// Source of the config e.g file, consul, etcd
Source string
// Time of loading or update
Timestamp time.Time
}
2.4. 编码器
Encoder
处理源配置编码/解码. 后端源可能以许多不同的格式存储配置. 编码器使我们能够处理任何格式. 如果未指定编码器, 则默认为 json.
支持以下编码格式:
- json
- yaml
- toml
- xml
- hcl
2.5. 读取器
Reader
将多个更改集表示为单个合并和可查询的值集.
type Reader interface {
// Merge multiple changeset into a single format
Merge(...*source.ChangeSet) (*source.ChangeSet, error)
// Return return Go assertable values
Values(*source.ChangeSet) (Values, error)
// Name of the reader e.g a json reader
String() string
}
读取器使用编码器将更改集解码为 map[string]interface{}``Values
, 然后将它们合并到单个更改集中. 它通过 Format 字段以确定编码器. 然后更改集表示为一组, 具有重新备份 Go 类型和无法加载值的回退功能.
// Values is returned by the reader
type Values interface {
// Return raw data
Bytes() []byte
// Retrieve a value
Get(path ...string) Value
// Return values as a map
Map() map[string]interface{}
// Scan config into a Go type
Scan(v interface{}) error
}
Value
接口允许强制转换/类型断言转到具有回退默认值的类型.
type Value interface {
Bool(def bool) bool
Int(def int) int
String(def string) string
Float64(def float64) float64
Duration(def time.Duration) time.Duration
StringSlice(def []string) []string
StringMap(def map[string]string) map[string]string
Scan(val interface{}) error
Bytes() []byte
}
2.6. Config
Config
管理所有配置, 抽象掉源, 编码器和读取器.
它管理从多个后端源读取, 同步, 监视, 并将它们表示为单个合并和可查询源.
// Config is an interface abstraction for dynamic configuration
type Config interface {
// provide the reader.Values interface
reader.Values
// Stop the config loader/watcher
Close() error
// Load config sources
Load(source ...source.Source) error
// Force a source changeset sync
Sync() error
// Watch a value for changes
Watch(path ...string) (Watcher, error)
}
2.7. 使用
2.7.1. 示例配置
只要我们有一个编码器来支持它, 配置文件就可以是任何格式的.
json 配置示例:
{
"hosts": {
"database": {
"address": "10.0.0.1",
"port": 3306
},
"cache": {
"address": "10.0.0.2",
"port": 6379
}
}
}
2.7.2. 新配置
创建一个新配置 (或者仅使用默认实例)
import "github.com/micro/go-micro/v2/config"
conf := config.NewConfig()
2.7.3. 加载文件
从文件源加载配置. 这使用文件扩展名来确定配置格式.
import (
"github.com/micro/go-micro/v2/config"
)
// 加载 json 配置文件
config.LoadFile("/tmp/config.json")
通过指定具有相应文件扩展名的文件来加载 yaml, toml 或 xml 文件
// 加载 yaml 配置文件
config.LoadFile("/tmp/config.yaml")
如果扩展不存在, 可指定编码器
import (
"github.com/micro/go-micro/v2/config"
"github.com/micro/go-micro/v2/config/source/file"
)
enc := toml.NewEncoder()
// 通过编码器加载 toml 文件
config.Load(file.NewSource(
file.WithPath("/tmp/config"),
source.WithEncoder(enc),
))
2.7.4. 读取配置
读取整个配置作为一个集合
// retrieve map[string]interface{}
conf := config.Map()
// map[cache:map[address:10.0.0.2 port:6379] database:map[address:10.0.0.1 port:3306]]
fmt.Println(conf["hosts"])
扫描配置到结构体
type Host struct {
Address string `json:"address"`
Port int `json:"port"`
}
type Config struct{
Hosts map[string]Host `json:"hosts"`
}
var conf Config
config.Scan(&conf)
// 10.0.0.1 3306
fmt.Println(conf.Hosts["database"].Address, conf.Hosts["database"].Port)
2.7.5. 读取值
从配置扫描值到结构体
type Host struct {
Address string `json:"address"`
Port int `json:"port"`
}
var host Host
config.Get("hosts", "database").Scan(&host)
// 10.0.0.1 3306
fmt.Println(host.Address, host.Port)
读取单个值作为 Go 类型
// Get address. Set default to localhost as fallback
address := config.Get("hosts", "database", "address").String("localhost")
// Get port. Set default to 3000 as fallback
port := config.Get("hosts", "database", "port").Int(3000)
2.7.6. 观察路径
观察路径以进行更改. 当文件更改时, 新值将可用.
w, err := config.Watch("hosts", "database")
if err != nil {
// do something
}
// wait for next value
v, err := w.Next()
if err != nil {
// do something
}
var host Host
v.Scan(&host)
2.7.7. 多源
可以加载和合并多个源. 合并优先级顺序相反.
config.Load(
// base config from env
env.NewSource(),
// override env with flags
flag.NewSource(),
// override flags with file
file.NewSource(
file.WithPath("/tmp/config.json"),
),
)
2.7.8. 设置源编码器
源要求编码器对数据进行编码/解码并指定更改集格式.
默认的编码器是 json. 要更改编码器可调整对应的类型选项.
e := yaml.NewEncoder()
s := consul.NewSource(
source.WithEncoder(e),
)
2.7.9. 添加读取器编码器
读取器使用编码器从不同格式的源解码数据.
默认的读取器支持 json, yaml, xml, toml 和 hcl. 它将合并的配置表示为 json.
可以通过指定的选项来添加一个新的编码器.
e := yaml.NewEncoder()
r := json.NewReader(
reader.WithEncoder(e),
)
2.8. 常见问题
2.8.1. 跟 Viper 有什么不一样?
Viper 和 go-config 都在解决相同的问题. Go-config 提供一个不一样的接口是大型 micro 生态系统工具中的一部分.
2.8.2. 编码器和读取器之间有什么不一样?
后端源使用编码器对数据进行编码/解码. 读取器使用编码器解码来自不同格式的多个源的数据, 然后将它们合并为单个编码格式.
对于文件源, 我们使用文件扩展名来确定配置格式 以便使用不同编码.
对于 consul, etcd 或 类似键值的源, 我们可能会从包含多个键的前缀加载, 这意味着源需要了解编码, 以便它可以返回单个更改集.
在有环境变量和标志的情况下, 我们还需要一种方法将值编码为字节并指定格式, 以便以后可以由读取器合并.
2.8.3. 为什么更改集数据不表示为 map[string]interface{}?
在某些情况下, 源数据实际上可能不是键值, 因此表示为字节更容易将其将解码及随后到读取器.