在kratos项目中使用etcd有两种方式
一种是使用kratos官方集成好的etcd客户端进行使用,还有一种是自己集成,下面我使用自定义的集成方式来实现配置的动态变化。
go
type EtcdSource struct {
client *clientv3.Client
key string
}
func NewEtcdSource(endpoints []string, key string) config.Source {
cli, err := clientv3.New(clientv3.Config{
Endpoints: endpoints,
DialTimeout: 5 * time.Second,
})
if err != nil {
log.Fatalf("failed to connect to etcd: %v", err)
}
return &EtcdSource{client: cli, key: key}
}
func (s *EtcdSource) Load() ([]*config.KeyValue, error) {
resp, err := s.client.Get(context.Background(), s.key)
if err != nil {
return nil, err
}
if len(resp.Kvs) == 0 {
log.Infof("key %s not found during load", s.key)
return nil, nil
}
return []*config.KeyValue{{
Key: s.key,
Value: resp.Kvs[0].Value,
Format: "yaml",
}}, nil
}
这个是读取指定key的etcd数据,实现 config.Source 还需要一个watch方法
go
func (s *EtcdSource) Watch() (config.Watcher, error) {
return NewEtcdWatcher(s.client, s.key), nil
}
具体的监听key的变化代码:
go
type EtcdWatcher struct {
client *clientv3.Client
key string
ch clientv3.WatchChan
once sync.Once
}
func NewEtcdWatcher(client *clientv3.Client, key string) config.Watcher {
w := &EtcdWatcher{
client: client,
key: key,
ch: client.Watch(context.Background(), key),
}
log.Infof("start watching key: %s", key)
return w
}
func (w *EtcdWatcher) Next() ([]*config.KeyValue, error) {
log.Infof("waiting for events on key: %s", w.key)
var kvs []*config.KeyValue
// 第一次调用时返回当前值
w.once.Do(func() {
resp, err := w.client.Get(context.Background(), w.key)
if err != nil {
log.Infof("failed to get initial value: %v", err)
return
}
if len(resp.Kvs) > 0 {
kvs = append(kvs, &config.KeyValue{
Key: w.key,
Value: resp.Kvs[0].Value,
Format: "yaml",
})
}
})
if len(kvs) > 0 {
log.Infof("returning initial value for key: %s", w.key)
return kvs, nil
}
// 监听后续变化
wr := <-w.ch
if wr.Canceled {
log.Infof("watch canceled: %v", wr.Err())
return nil, wr.Err()
}
log.Infof("received watch events: %d", len(wr.Events))
for _, ev := range wr.Events {
log.Infof("event type: %v, key: %s, value: %s", ev.Type, ev.Kv.Key, ev.Kv.Value)
if ev.Type == clientv3.EventTypePut {
kvs = append(kvs, &config.KeyValue{
Key: w.key,
Value: ev.Kv.Value,
Format: "yaml",
})
}
}
return kvs, nil
}
func (w *EtcdWatcher) Stop() error {
return w.client.Close()
}
具体的使用是
go
func InitConfig() *conf.Bootstrap {
key := "/config/rwService"
source := r1.NewEtcdSource([]string{"localhost:4000"}, key)
c := config.New(config.WithSource(source))
defer c.Close()
if err := c.Load(); err != nil {
log.Fatalf("failed to load config: %v", err)
}
var cfg conf.Bootstrap
if err := c.Scan(&cfg); err != nil {
log.Fatalf("failed to scan config: %v", err)
}
w, err := source.Watch()
if err != nil {
log.Fatalf("failed to watch: %v", err)
}
defer func() {
if err := w.Stop(); err != nil {
log.Error("failed to stop watcher: %v", err)
}
}()
for {
select {
default:
kvs, err := w.Next()
if err != nil {
log.Fatalf("next error: %v", err)
}
if len(kvs) == 0 {
log.Infof("no events received, continuing")
continue
}
for _, kv := range kvs {
log.Infof(">>> key: %s, value: %s", kv.Key, string(kv.Value))
}
}
}
return &cfg
}
以上就实现了etcd监听配置中心的key变化实时获取数据。 也可以从官方使用数据来监听数据
arduino
go get github.com/go-kratos/kratos/contrib/config/etcd/v2