1.rewrite some code

2.write doc.go
This commit is contained in:
maru 2019-04-10 16:28:10 +08:00
parent a93eb7a85a
commit f06cfd8785
2 changed files with 120 additions and 23 deletions

View file

@ -6,45 +6,39 @@ import (
"github.com/hashicorp/consul/watch" "github.com/hashicorp/consul/watch"
"github.com/spf13/viper" "github.com/spf13/viper"
"io" "io"
"sync"
) )
type consulConfigProvider struct{} type consulConfigProvider struct {
mu sync.Mutex
idxMap map[string]uint64
}
func (rc consulConfigProvider) Get(rp viper.RemoteProvider) (io.Reader, error) { func (rc *consulConfigProvider) Get(rp viper.RemoteProvider) (io.Reader, error) {
config := api.DefaultConfig() config := api.DefaultConfig()
config.Address = rp.Endpoint() config.Address = rp.Endpoint()
client, err := api.NewClient(config) client, err := api.NewClient(config)
if err != nil { if err != nil {
return nil, err return nil, err
} }
kv, _, err := client.KV().Get(rp.Path(), nil) kv, meta, err := client.KV().Get(rp.Path(), nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
rc.updateIndex(rp, meta.LastIndex)
return bytes.NewReader(kv.Value), nil return bytes.NewReader(kv.Value), nil
} }
func (rc consulConfigProvider) Watch(rp viper.RemoteProvider) (io.Reader, error) { func (rc *consulConfigProvider) Watch(rp viper.RemoteProvider) (io.Reader, error) {
// TODO same as Get(), but behave like before(viper/remote), maybe record LastIndex in rp? return rc.watch(rp)
resp, quit := newWatcher(rp.Path(), rp.Endpoint())
r := <-resp
close(quit)
return bytes.NewReader(r.Value), r.Error
} }
func (rc consulConfigProvider) WatchChannel( func (rc *consulConfigProvider) watch(rp viper.RemoteProvider) (r io.Reader, err error) {
rp viper.RemoteProvider) (resp <-chan *viper.RemoteResponse, quit chan bool) { p, err := watch.Parse(map[string]interface{}{"type": "key", "key": rp.Path()})
return newWatcher(rp.Path(), rp.Endpoint())
}
// To stop watch, just close(quit)
func newWatcher(key, addr string) (<-chan *viper.RemoteResponse, chan bool) {
p, err := watch.Parse(map[string]interface{}{"type": "key", "key": key})
if err != nil { if err != nil {
return nil, nil return nil, err
} }
quit := make(chan bool) // handler
viperResponseCh := make(chan *viper.RemoteResponse)
p.Handler = func(index uint64, data interface{}) { p.Handler = func(index uint64, data interface{}) {
if data == nil { if data == nil {
return return
@ -53,12 +47,49 @@ func newWatcher(key, addr string) (<-chan *viper.RemoteResponse, chan bool) {
if !ok { if !ok {
return return
} }
if !rc.updateIndex(rp, index) {
return
}
r = bytes.NewReader(kv.Value)
p.Stop()
}
// start watch
p.Run(rp.Endpoint())
return
}
func (rc *consulConfigProvider) WatchChannel(
rp viper.RemoteProvider) (resp <-chan *viper.RemoteResponse, quit chan bool) {
return rc.watchChannel(rp)
}
func (rc *consulConfigProvider) watchChannel(rp viper.RemoteProvider) (<-chan *viper.RemoteResponse, chan bool) {
p, err := watch.Parse(map[string]interface{}{"type": "key", "key": rp.Path()})
if err != nil {
// this should not happen
return nil, nil
}
quit := make(chan bool)
viperResponseCh := make(chan *viper.RemoteResponse)
// handler
p.Handler = func(index uint64, data interface{}) {
if data == nil {
return
}
kv, ok := data.(*api.KVPair)
if !ok {
return
}
if !rc.updateIndex(rp, index) {
return
}
select { select {
case viperResponseCh <- &viper.RemoteResponse{Value: kv.Value}: case viperResponseCh <- &viper.RemoteResponse{Value: kv.Value}:
case <-quit: case <-quit:
} }
} }
go p.Run(addr) // start watcher
go p.Run(rp.Endpoint())
// wait quit // wait quit
go func() { go func() {
<-quit <-quit
@ -67,6 +98,22 @@ func newWatcher(key, addr string) (<-chan *viper.RemoteResponse, chan bool) {
return viperResponseCh, quit return viperResponseCh, quit
} }
func init() { func makeIndexKey(rp viper.RemoteProvider) string {
viper.RemoteConfig = &consulConfigProvider{} return rp.Endpoint() + "_" + rp.Path()
}
func (rc *consulConfigProvider) updateIndex(
rp viper.RemoteProvider, lastIndex uint64) (updated bool) {
rc.mu.Lock()
oldLastIndex := rc.idxMap[makeIndexKey(rp)]
if oldLastIndex < lastIndex {
rc.idxMap[makeIndexKey(rp)] = lastIndex
updated = true
}
rc.mu.Unlock()
return
}
func init() {
viper.RemoteConfig = &consulConfigProvider{idxMap: make(map[string]uint64)}
} }

50
remote/consul/doc.go Normal file
View file

@ -0,0 +1,50 @@
package consul
// Integrates the consul's remote features of Viper.
// viper/remote's problems:
// 1.consul watch do not work
// 2.outdated consul's lib
// use this ConsulRemoteConfigProvider, just replace
// _ "github.com/spf13/viper/remote" => _ "github.com/spf13/viper/remote/consul"
// usage example 1:
//func main() {
// v := viper.New()
// err := v.AddRemoteProvider("consul", "127.0.0.1:8500", "foo/bar")
// if err != nil {
// panic(err)
// }
// v.SetConfigType("json")
// err = v.ReadRemoteConfig()
// if err != nil {
// panic(err)
// }
// for {
// // blocking until consul kv updated
// err = v.WatchRemoteConfig()
// if err != nil {
// panic(err)
// }
// // your config is updated now.
// // ...
// }
//}
// usage example 2:
//func main() {
// v := viper.New()
// err := v.AddRemoteProvider("consul", "127.0.0.1:8500", "foo/bar")
// if err != nil {
// panic(err)
// }
// v.SetConfigType("json")
// err = v.ReadRemoteConfig()
// if err != nil {
// panic(err)
// }
// // your config will be update async
// v.WatchRemoteConfigOnChannel()
// // ....
//}