配置中心的使用呢,之前在项目中使用的是viper(现在又给改回去了,误解了viper的操作),但根据实际测试以及在GitHub的issue中看到的帖子,看到viper是线程不安全的(确实是不安全的)。

viper

函数WatchRemoteConfig可以监听ETCD KEY,但实际上这些操作在源码中都没出现加锁的操作,这里我对viper这么受欢迎的库在远端监听时不支持线程安全表示怀疑,因为我在官方README中并没有见到有特地提到说不能在并发环境中使用的注意事项,我想,事情可能并不是那么简单,现实是,事情可能就是这个样子的,监听远程key确实只能在一个协程中进行(为什么需要开多个协程去监听呢?)。

使用WatchRemoteConfig(),viper会重新获取key中的所有数据,我们需使用Unmarshal将数据进行序列化保存,但这里可以不去Unmarshal,直接使用类似于viper.GetString("logfile")去获取key-value。

注意:这里就是问题出现的点了,使用viper.GetString("logfile")获取数据,相当于从一个map中直接拿数据,要知道,map是不能并发读和并发写的,所以会引发concurrent map read and map write

问题找到了,那么viper的用法呢,其实也没有错,但需要注意的是,我们确实只能在一个协程中使用viper,且我们在获取到数据后,最好是使用带锁的map或是并发sync.Map将数据保存起来,而不是使用viper.GetString去从viper的缓存中获取数据,我们不应该信任viper。

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
var runtime_viper = viper.New()

runtime_viper.AddRemoteProvider("etcd", "http://127.0.0.1:4001", "/config/hugo.yml")
runtime_viper.SetConfigType("yaml") // because there is no file extension in a stream of bytes, supported extensions are "json", "toml", "yaml", "yml", "properties", "props", "prop"

// read from remote config the first time.
err := runtime_viper.ReadRemoteConfig()

// unmarshal config
runtime_viper.Unmarshal(&runtime_conf)

// open a goroutine to watch remote changes forever
go func(){
for {
time.Sleep(time.Second * 5) // delay after each request

// currently, only tested with etcd support
err := runtime_viper.WatchRemoteConfig()
if err != nil {
log.Errorf("unable to read remote config: %v", err)
continue
}

// unmarshal new config into our runtime config struct. you can also use channel
// to implement a signal to notify the system of the changes
runtime_viper.Unmarshal(&runtime_conf)

// 使用线程安全的sync.Map进行保存
SyncCfgMap.Store("config", &runtime_conf)
}
}()

confd安装

confd的安装与使用

confd网上资料很多了,基本上算是比较好配置的了,没有什么坑可以占,麻烦点的就是对于解析的配置。

一般的,confd两个目录:conf.d,templates。

conf.d目录下的一个配置文件,代表将输出一个配置。而输出这些配置的依据或者是规则,放置于templates目录下,语法需要参考confd官方的文档