直接使用config包中函数,例如Get/Load...等函数,我们使用的是config包,默认变量DefaultConfig

初始化过程为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
config/config.go

func NewConfig(opts ...Option) (Config, error) {
return newConfig(opts...)
}

func newConfig(opts ...Option) (Config, error) {
var c config

c.Init(opts...)
go c.run()

return &c, nil
}

// 重要函数
c.Init(opts...)
go c.run()

重要的函数:Initrun。不过这里主要讲解Init即可,run为监听配置改变函数。

1
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
func (c *config) Init(opts ...Option) error {
c.opts = Options{
Reader: json.NewReader(),
}
c.exit = make(chan bool)
for _, o := range opts {
o(&c.opts)
}

// default loader uses the configured reader
if c.opts.Loader == nil {
c.opts.Loader = memory.NewLoader(memory.WithReader(c.opts.Reader))
}

err := c.opts.Loader.Load(c.opts.Source...)
if err != nil {
return err
}

c.snap, err = c.opts.Loader.Snapshot()
if err != nil {
return err
}

c.vals, err = c.opts.Reader.Values(c.snap.ChangeSet)
if err != nil {
return err
}

return nil
}

// config/reader/json/values.go
func (j *jsonReader) Values(ch *source.ChangeSet) (reader.Values, error) {
.......

return newValues(ch)
}
func newValues(ch *source.ChangeSet) (reader.Values, error) {
sj := simple.New()
data, _ := reader.ReplaceEnvVars(ch.Data)
if err := sj.UnmarshalJSON(data); err != nil {
sj.SetPath(nil, string(ch.Data))
}
return &jsonValues{ch, sj}, nil
}

看看Init()中最重要的事就是设置了Load,这里也就是详细讲一下Load的操作,一般load是由我们在后期进行调用的。

  1. 初始化并设置opts,创建exit用于监听退出信号,设置opts

  2. 设置默认loader,c.opts.Loader默认是memory memory.NewLoader()[config/loader/memory/memory.go],这是后续关键函数。

  3. 调用c.opts.Loader.Load(),[config/loader/memory/memory.go],详细讲解下Load

    1. 循环所有source,根据配置读取数据,更新m.sources,m.sets(Format为传入的编码),并watch()所有source
    2. 调用reload()
      1. 合并所有sets,函数m.opts.Reader.Merge(m.sets...)进行合并。
      2. 设置m.vals,m.snap
      3. 调用m.update(),更新之前的数据。
1
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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
source.ChangeSet
type ChangeSet struct {
Data []byte // 读取的数据
Checksum string
Format string // WithEncoder设置的Encoder,然后讲Encoder.String()设置给它
Source string // 来自哪个数据源,`apollo\file`等
Timestamp time.Time // 此次数据什么时候读取的时间戳
}

// Source is the source from which config is loaded
// 这是每一个读取源需要实现的接口,例如Apollo\file等
type Source interface {
Read() (*ChangeSet, error)
Write(*ChangeSet) error
Watch() (Watcher, error)
String() string
}

// go-micro/config/source/options.go
// WithEncoder sets the source encoder
// 设置不同的数据源的编码格式,在Load函数调用时,传入第三方包进行编码
func WithEncoder(e encoder.Encoder) Option {
return func(o *Options) {
o.Encoder = e
}
}

func (m *memory) Load(sources ...source.Source) error {
var gerrors []string

for _, source := range sources {
set, err := source.Read() // 调用的第三方包实现的read函数,读取数据
......

m.Lock()
m.sources = append(m.sources, source)
m.sets = append(m.sets, set)
idx := len(m.sets) - 1
m.Unlock()
go m.watch(idx, source)
}

if err := m.reload(); err != nil {
gerrors = append(gerrors, err.Error())
}

.....

return nil
}

// reload reads the sets and creates new values
func (m *memory) reload() error {
m.Lock()

// merge sets,不管什么其他格式都将转换为json数据
set, err := m.opts.Reader.Merge(m.sets...)
if err != nil {
m.Unlock()
return err
}

// set values
m.vals, _ = m.opts.Reader.Values(set)
m.snap = &loader.Snapshot{
ChangeSet: set,
Version: genVer(),
}

m.Unlock()

// update watchers
m.update()

return nil
}

// config/reader/options.go
func NewOptions(opts ...Option) Options {
options := Options{
Encoding: map[string]encoder.Encoder{
"json": json.NewEncoder(),
"yaml": yaml.NewEncoder(),
"toml": toml.NewEncoder(),
"xml": xml.NewEncoder(),
"hcl": hcl.NewEncoder(),
"yml": yaml.NewEncoder(),
},
}
}
// 聚合各个source的数据 `config/reader/json/json.go`
func (j *jsonReader) Merge(changes ...*source.ChangeSet) (*source.ChangeSet, error) {
var merged map[string]interface{}

for _, m := range changes {
......

codec, ok := j.opts.Encoding[m.Format]

var data map[string]interface{}
if err := codec.Decode(m.Data, &data); err != nil {
return nil, err
}

......
}

b, err := j.json.Encode(merged)
if err != nil {
return nil, err
}

cs := &source.ChangeSet{
Timestamp: time.Now(),
Data: b,
Source: "json",
Format: j.json.String(),
}
cs.Checksum = cs.Sum()

return cs, nil
}

source上的所有数据均转化为Json数据,故最终都会调用到config提供的读取json的方法。config/reader/json/values.go

1
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
type jsonValues struct {
ch *source.ChangeSet
sj *simplejson.Json
}
Methods:
Get(path ...string) reader.Value
Del(path ...string)
Set(val interface{}, path ...string)
Bytes() []byte
Map() map[string]interface{}
Scan(v interface{}) error
String() string

type jsonValue struct {
*simplejson.Json
}
Methods:
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(v interface{}) error
Bytes() []byte

json的读取库采用的是:go-simplejson

另外,在实际使用中,Get函数中其调用的是j.sj.GetPath(path...)。它可以读取到嵌套的json数据。没有数据的话,不会报错,只能检测结构体里面的参数是否为nil,但Get返回的是一个reader.Value的接口,不能根据判断其为nil来确定其有无数据(其结构体不为nil,是里面的data字段为nil)。

可以查看value的方法,可以调用Interface(),获取data字段,判断是否有值。

注意

这里将引出一个问题,可以看到config目录中,有很多将Get的返回值对比nil的代码,其返回值是reader.Value,是一个接口。

若是返回的是一个接口,此接口是nil,则intr只是内部value被赋值为nil了, 其本身不为nil的,想判断这种情况,必须是:

1
2
3
if v != reader.Value(nil) {
......
}

config包config/default.go中,出现了此错误。

if c.vals == nil