不管是单个服务还是微服务,读取文件在每个项目系统中是必不可少的部分。
大多数项目中都是静态加载项目配置文件的,有时候可能需要从各种源中读取配置数据,这让配置读取复杂化,不易于快速开发。而go-micro中,不管是从动态读取配置,还是从多元读取配置都很简单,唯一难点就是需要读取源码来了解他的工作机制。
Go-Micro支持多种源的读取,包括命令行参数、文件(json、yaml)、etcd、consul、k8s等。
Go-Micro 配置核心模块有四个,分别是 Source、Encoder、Reader、Config。
对于Source,它表示读取的配置源,比如文件、命令行、consul、etcd等。Go-Micro官方支持的源有:
除了官方支持的源,还有一些是Go-Micro社区支持的:
configmap:从 k8s 的configmap中读取
grpc:从grpc服务器读取
url:从URL读取
runtimevar:read from Go Cloud Development Kit runtime variable
vault:read from Vault server
go-micro初始化config,config.DefaultConfig
,在config/config.go中
var (
// Default Config Manager
DefaultConfig, _ = NewConfig()
)
// NewConfig returns new config
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
}
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
}
func (c *config) run() {
watch := func(w loader.Watcher) error {
for {
// get changeset
snap, err := w.Next()
if err != nil {
return err
}
c.Lock()
if c.snap.Version >= snap.Version {
c.Unlock()
continue
}
// save
c.snap = snap
// set values
c.vals, _ = c.opts.Reader.Values(snap.ChangeSet)
c.Unlock()
}
}
for {
w, err := c.opts.Loader.Watch()
if err != nil {
time.Sleep(time.Second)
continue
}
done := make(chan bool)
// the stop watch func
go func() {
select {
case <-done:
case <-c.exit:
}
w.Stop()
}()
// block watch
if err := watch(w); err != nil {
// do something better
time.Sleep(time.Second)
}
// close done chan
close(done)
// if the config is closed exit
select {
case <-c.exit:
return
default:
}
}
}
看看Init()
做了什么
初始化并设置opts,创建exit用于监听退出信号,设置opts
设置默认loader,c.opts.Loader默认是memory memory.NewLoader()
[config/loader/memory/memory.go]
watch()
每个options.Source,看看watch()
做了什么
调用c.opts.Loader.Load()
reload()
调用c.opts.Loader.Snapshot()
Sync()
同步配置调用c.opts.Reader.Values()
,赋值config.vals【reader.Values
类型】
go-micro支持从读取配置有以下方法:
type Config interface {
// provide the reader.Values interface
reader.Values
// Init the config
Init(opts ...Option) error
// Options in the config
Options() Options
// 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)
}
主要使用的是
// Load config sources
Load(source ...source.Source) error
// Watch a value for changes
Watch(path ...string) (Watcher, error)
本次展示仅使用etcd读取配置,为了能够读取到数据,我们需要先往etcd中写入数据,如果你对命令不熟悉,建议使用etcdkeeper
来添加配置
$ etcdctl put /micro/config/demo "{ \"network\": \"172.30.0.0/16\", \"backend\": \"vxlan\"}"
OK
$ etcdctl get /micro/config/demo
/micro/config/demo #key
{ "network": "172.30.0.0/16", "backend": "vxlan"} #value
type Demo struct {
NetWork string `json:"netWork"`
Backend string `json:"backend"`
}
func getConfig() Demo {
getJSON("demo")
conf := Demo{}
config.Scan(&conf)
return conf
}
func getJSON(pr string) error {
//配置中心使用etcd key/value 模式
etcdSource := etcd.NewSource(
//设置配置中心地址
etcd.WithAddress("127.0.0.1:2379"),
//设置前缀,不设置默认为 /micro/config
etcd.WithPrefix("/micro/config/"+pr),
//是否移除前缀,这里设置为true 表示可以不带前缀直接获取对应配置,StripPrefix 从语义上看是去掉前缀的意思,如果没有去掉前缀,则会保留micro、etcd这两个key
etcd.StripPrefix(true),
)
//加载配置
return config.Load(etcdSource)
}
w, err := config.Watch("/micro/config/", "demo")
if err != nil {
return err
}
// wait for next value
v, err := w.Next()
if err != nil {
return err
}
var demo Demo
v.Scan(&demo)
return nil
监控/micro/config/
下的demo
节点,当节点数据发生改变时,就会触发监控
好了,以上就是本文的主要内容。
老规矩,代码已经上传到Github,欢迎访问: https://github.com/casiphia/go-micro-examples