kratos集成etcd配置中心

在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
相关推荐
小满zs11 小时前
Go语言第十八章(架构设计)
后端·go
FungLeo1 天前
成为全栈·Go 后端篇·三轮代码审阅之后:把可读性和证据做成工程规则
golang·go·ast·静态检查·成为全栈·代码审阅
我不会起名字3221 天前
Redis 从 0 到 1(二十八):Redis 抖一下,重试把它打挂了——超时/重试/熔断的 Go 封装
redis·go·超时·熔断·重试
xyzzklk2 天前
变相解决华为Mate 8锁屏后无法建立入站网络连接
linux·运维·服务器·网络·华为·go·安卓
vipxieliang2 天前
语言对比卡:PHP vs Go vs Java 的优缺点、适用场景与「为什么选 PHP」
java·go·php
我不会起名字3223 天前
Redis 从 0 到 1(二十七):JSON 体积是 Protobuf 的 3.7 倍?3 种序列化的 Go 基准测试
redis·go·序列化·protobuf·messagepack
我不会起名字3223 天前
Redis 从 0 到 1(二十三):一个热 key 打到 10 万 QPS——hotkey 与本地缓存的 Go 实现
redis·缓存·go·qps·hotkey
Patrick_Wilson4 天前
Go 为什么需要 defer?从函数怎么收尾说起
后端·go·编程语言
Patrick_Wilson4 天前
从文本输出理解 Go 方法与接口
后端·go·编程语言
Patrick_Wilson4 天前
读懂一个 Go JSON 处理函数:从输入数据追踪结构体、校验与错误返回
go·json·编程语言