尝试etcd做配置中心,就用了Viper。没想到踩了一堆坑,记录一下。
一开始的代码是这么写的,想着先监听再读取:
func initViperRemote() {err := viper.AddRemoteProvider("etcd3","http://127.0.0.1:12379", "webook")if err != nil {panic(err)}viper.SetConfigType("yaml")// 先watcherr = viper.WatchRemoteConfig()if err != nil {panic(err)}// 再readerr = viper.ReadRemoteConfig()if err != nil {panic(err)}
}
直接panic。
第一个坑:invalid memory address or nil pointer dereference
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x20 pc=0x9a8d6b]goroutine 1 [running]:
github.com/spf13/viper.(*Viper).watchRemoteConfig(0xc000162a80, {0xf653d0?, 0xc0000b5240?})
...
查了下,原来是顺序搞反了,必须先成功Read
一次,才能Watch
。
func initViperRemote() {err := viper.AddRemoteProvider("etcd3","http://127.0.0.1:12379", "webook")if err != nil {panic(err)}viper.SetConfigType("yaml")// 先readerr = viper.ReadRemoteConfig()if err != nil {panic(err)}// 再watcherr = viper.WatchRemoteConfig()if err != nil {panic(err)}
}
又panic了。
第二个坑:Enable the remote features by doing a blank import
panic: Remote Configurations Error: Enable the remote features by doing a blank import of the viper/remote package: '_ github.com/spf13/viper/remote'
...
这个错误信息倒是很直接,让干啥就干啥。要用远程功能得先匿名导入它的包。
import ("bytes""fmt""net/http""github.com/fsnotify/fsnotify""github.com/gin-gonic/gin""github.com/spf13/pflag""github.com/spf13/viper"_ "github.com/spf13/viper/remote" // 加上这行
)
再跑,还是panic。
第三个坑:No Files Found
panic: Remote Configurations Error: No Files Found
...
这就奇怪了,我明明用etcdctl把配置放进去了。
$ etcdctl --endpoints=127.0.0.1:12379 put /webook "$(<dev.yaml)"
OK
$ etcdctl --endpoints=127.0.0.1:12379 get /webook
/webook
db:dsn: "root:root@tcp(localhost:13316)/webook"
...
看了一下代码和命令,发现了问题。etcd里的key是/webook
,代码里写的是webook
。差一个斜杠。
改代码:
func initViperRemote() {err := viper.AddRemoteProvider("etcd3","http://127.0.0.1:12379", "/webook") // 这里加上斜杠// ...
}
再跑,总算成功了。
kbz@DESKTOP-PCAC9DA:~/BackendDEV/geektime-basic-go/webook$ go run .
[db.dsn redis.addr]
map[db:map[dsn:root:root@tcp(localhost:13316)/webook] redis:map[addr:localhost:6379]]
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
...
[GIN-debug] Listening and serving HTTP on :7070
感觉Viper的接口设计得有点恶心,用起来比较折腾。不过在要求灵活配置优先级的场景下确实,使用状态机模型会契合一点