# 配置
# 介绍
对于现代应用来说配置越来越重要,它是应用运行的基础,是其响应变化能力的体现。Box
为用户提供灵活、强大的配置功能。
系统会按照以下流程进行配置加载:
找到
启动配置文件 是指找到系统启动所需的配置文件(应用的配置信息并不存储在这里)。读取
启动配置文件 找到启动配置文件后,我们需要读取配置中心的接入信息,以便后续连接到配置中心。连接
配置中心 这时系统会自动连接到配置中心。读取
应用配置信息 这时用户就可以读取应用配置了。
启动配置文件的格式:
name: "应用名称" # 应用的名称(用于指标上报,链路跟踪,日志等)
version: "应用版本" # 应用的版本号
source: # 配置中心接入信息
- type: xxx # 配置源类型
key1: val1 # 配置源接入信息
key2: val2 # 配置源接入信息
...
keyN: valN # 配置源接入信息
2
3
4
5
6
7
8
为了避免混淆,在这里我们强调一下两个概念:启动配置文件,应用配置信息 这两个不是一回事。前者是存储在本地的配置文件(用于获取服务名称、版本,配置中心连接信息),后者是存储在远端配置中心的配置信息(用于应用配置)。
# 启动配置文件
# 默认情况
系统会按以下优先级,找到第一个存在的文件,加载配置中心的连接信息。
# "Work directory" 是应用的工作目录
{Work directory}/box.yml
{Work directory}/box.yaml
{Work directory}/box.toml
{Work directory}/box.json
{Work directory}/config/box.yml
{Work directory}/config/box.yaml
{Work directory}/config/box.toml
{Work directory}/config/box.json
2
3
4
5
6
7
8
9
# 自定义
如果你想自定义文件名或者目录,通过环境变量 BOX_BOOT_CONFIG
也是可以实现的。
- 当
BOX_BOOT_CONFIG
是文件路径时,系统将直接从该文件加载。 - 当
BOX_BOOT_CONFIG
是文件名时,系统将按照上面默认情况的优先级加载配置。区别是文件名不再是box,而是BOX_BOOT_CONFIG
的值。
# 配置源
系统内置 文件
, Etcd
, Redis
, Mongodb
四种配置源。如果你的配置信息存储在其他类型的配置源,可以通过实现 source.Source (opens new window) 接口来实现扩展。
# File
- 如果 启动配置文件 的
soruce
未设置,将从当前文件获取配置 - 如果 启动配置文件 的
source
设置如下,将从指定文件获取配置
source:
- type: file # 固定file
path: box.yaml # 应用的配置文件
2
3
# MongoDB
在 启动配置文件 的source
下如下填写:
source:
- type: mongodb # 固定mongodb
uri: "mongodb://127.0.0.1:27017" # mongodb uri
db: config # 数据库
collection: box_config # 表
service: testBox # 配置信息的key
2
3
4
5
6
# Redis
在 启动配置文件 的source
下如下填写:
source:
- type: redis
prefix: test # 配置信息的key的前缀
redis:
password: "your redis password" # redis密码
address: # redis ip地址列表
- 127.0.0.1:6379
db: 0 # 使用的db
poolSize: 10 # 连接池大小
minIdleConnCnt: 2 # 最小的空闲连接数
masterName: "The sentinel master name" # 哨兵模式时的master name
2
3
4
5
6
7
8
9
10
11
# Etcd
在 启动配置文件 的source
下如下填写:
source:
- type: etcd # 固定etcd,表示使用etcd作为源
address: localhost:2379 # etcd的连接地址
username: username # etcd的账号
password: password # etcd的密码
prefix: box/config # 配置信息的key(避免与其他应用冲突)
stripPrefix: true # 配置信息中是否包含prefix前缀,作为顶级的key。
2
3
4
5
6
7
PS:如果stripPrefix是false,配置信息中需增加prefix的值作为上级的key,如下:
{
"box/config": {
你的配置
}
}
2
3
4
5
# 应用配置信息
对于获取应用配置,我们提供了两种方案,每种有各自的优势,两者结合使用可满足绝大部分场景。
# 静态配置获取
系统运行过程中不会发生变化的配置,我们称为静态配置。
静态配置可以通过 config.Scan
方法在初始化时进行获取。Scan
的参数必须是一个实现 config.Config
接口的结构体。
Scan
不仅可以更新配置到变量中,还可以建立应用的配置信息表,稍后将会介绍。
#
Scan
规则如下:
- 只扫描导出字段。
- 结构体
Path()
方法返回的值,将作为该配置的顶级key(支持使用 "." 进行层级划分)。- 被扫描字段的
类型
,作为该字段的类型。- 被扫描字段的
值
,作为该字段的默认值。- 被扫描字段的
tag
中,config
作为该字段加载时的key。- 被扫描字段的
tag
中,desc
作为该字段的描述信息。- 被扫描字段是
struct
或者是struct ptr
,将递归扫描并将其字段作为下级。
# 举个栗子
配置中心配置如下:
foo:
default:
bar: you are bar
2
3
获取代码如下:
package main
import "log"
// 定义`FooConfig`结构体,用于声明我们需要获取的配置信息。
type FooConfig struct {
// Bar需要配置,key是应用配置信息中的:"foo.default.bar"。
Bar string `config:"bar" desc:"this is bar."`
}
func (cfg *Config) Path() string {
return "foo.default"
}
func main() {
// 声明配置变量
var cfg = FooConfig{
// 设置Bar的默认值,如果没有获取到或者获取失败,将使用该值作为默认值。
Bar: "I'm bar",
}
// 扫描cfg,并将配置中心的数据更新到cfg中。
err := config.Scan(cfg)
log.Println(err, cfg)
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
如果扫描成功cfg.Bar
的值是you are bar
,否者其值是I'm bar
。
# 动态配置获取
系统运行过程中可能发生变化的配置,我们称为动态配置。
动态配置可以通过config.Get
方法随时获取新值,然后按需转换数据类型。Get
的参数是一个配置项的访问路径字符串。
config.Get("foo.default.bar").String("undefined")
如上,我们获取foo.default.bar
的值,并转化成字符串,如果没有获取到将使用默认值undefined
。
# 监听配置变化
有时我们要求系统在配置发生变化之后做出相应,我们提供Watch
机制以供监听配置变化。
可以通过config.Watch
方法监听配置。
# 最佳实践
- 优先使用静态配置。
- 动态配置和Watch的key,不超出静态配置的范围。
- 配置名称要简洁,字段描述要清晰,初始值设置要合理。
- 模块要能复用,配置要能区分。
案例代码,请查看 box的logger模块 (opens new window)
# 配置信息表
配置信息表提供应用的配置信息总览界面,包括配置key、字段key、字段类型、默认值、初始值和描述。
有两种方式查看配置信息表:
- 创建Box启动器时,添加选项
WithSilent(false)
,将会在控制台显示配置表。 - 开启
insight
功能,访问 http://ip:port/config/table。
配置信息表演示:
+---------------------+-------------------------------+-------------------------+------------------+------------------+------------------------------------------+
| Name | Path | Type | Value | Default | Description |
+---------------------+-------------------------------+-------------------------+------------------+------------------+------------------------------------------+
| gin.default | addr | string | :9000 | :9000 | server listen addr, format is |
| | | | | | ip:port |
+ +-------------------------------+-------------------------+------------------+------------------+------------------------------------------+
| | basicAuth | map[string]string | map[] | map[] | basicAuth. key is username, |
| | | | | | value is password |
+ +-------------------------------+-------------------------+------------------+------------------+------------------------------------------+
| | idleTimeout | time.Duration | 5m0s | 5m0s | |
+ +-------------------------------+-------------------------+------------------+------------------+------------------------------------------+
| | mode | string | release | release | Gin mode: debug,release,test. |
| | | | | | default is release |
+ +-------------------------------+-------------------------+------------------+------------------+------------------------------------------+
| | readTimeout | time.Duration | 1m0s | 1m0s | |
+ +-------------------------------+-------------------------+------------------+------------------+------------------------------------------+
| | writeTimeout | time.Duration | 1m0s | 1m0s | |
+---------------------+-------------------------------+-------------------------+------------------+------------------+------------------------------------------+
| logger.default | development | bool | false | false | |
+ +-------------------------------+-------------------------+------------------+------------------+------------------------------------------+
| | disableCaller | bool | true | true | |
+ +-------------------------------+-------------------------+------------------+------------------+------------------------------------------+
| | disableStacktrace | bool | false | false | |
+ +-------------------------------+-------------------------+------------------+------------------+------------------------------------------+
| | encoderConfig.callerEncoder | string | short | short | |
+ +-------------------------------+-------------------------+------------------+------------------+------------------------------------------+
| | encoderConfig.callerKey | string | caller | caller | |
+ +-------------------------------+-------------------------+------------------+------------------+------------------------------------------+
| | encoderConfig.durationEncoder | string | ms | ms | |
+ +-------------------------------+-------------------------+------------------+------------------+------------------------------------------+
| | encoderConfig.levelEncoder | string | capitalColor | capitalColor | |
+ +-------------------------------+-------------------------+------------------+------------------+------------------------------------------+
| | encoderConfig.levelKey | string | level | level | |
+ +-------------------------------+-------------------------+------------------+------------------+------------------------------------------+
| | encoderConfig.lineEnding | string | | | |
| | | | | | |
+ +-------------------------------+-------------------------+------------------+------------------+------------------------------------------+
| | encoderConfig.messageKey | string | msg | msg | |
+ +-------------------------------+-------------------------+------------------+------------------+------------------------------------------+
| | encoderConfig.nameEncoder | string | | | |
+ +-------------------------------+-------------------------+------------------+------------------+------------------------------------------+
| | encoderConfig.nameKey | string | logger | logger | |
+ +-------------------------------+-------------------------+------------------+------------------+------------------------------------------+
| | encoderConfig.stacktraceKey | string | stacktrace | stacktrace | |
+ +-------------------------------+-------------------------+------------------+------------------+------------------------------------------+
| | encoderConfig.timeEncoder | string | iso8601 | iso8601 | |
+ +-------------------------------+-------------------------+------------------+------------------+------------------------------------------+
| | encoderConfig.timeKey | string | time | time | |
+ +-------------------------------+-------------------------+------------------+------------------+------------------------------------------+
| | encoding | string | console | console | |
+ +-------------------------------+-------------------------+------------------+------------------+------------------------------------------+
| | errorOutputPaths | []string | [stderr] | [stderr] | |
+ +-------------------------------+-------------------------+------------------+------------------+------------------------------------------+
| | initialFields | map[string]interface {} | map[] | map[] | |
+ +-------------------------------+-------------------------+------------------+------------------+------------------------------------------+
| | level | zap.AtomicLevel | info | info | debug,info,warn,error,dpanic,panic,fatal |
+ +-------------------------------+-------------------------+------------------+------------------+------------------------------------------+
| | outputPaths | []string | [stdout] | [stdout] | |
+ +-------------------------------+-------------------------+------------------+------------------+------------------------------------------+
| | sampling.initial | int | 100 | 100 | |
+ +-------------------------------+-------------------------+------------------+------------------+------------------------------------------+
| | sampling.thereafter | int | 100 | 100 | |
+---------------------+-------------------------------+-------------------------+------------------+------------------+------------------------------------------+
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63