github:https://github.com/etcd-io/etcd/tree/master/clientv3
测试go版本
go 1.14require (go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc // indirectgoogle.golang.org/grpc v1.34.0 // indirect)replace (go.etcd.io/etcd => go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489 // ae9734ed278b is the SHA for git tag v3.4.13google.golang.org/grpc => google.golang.org/grpc v1.27.1)
或者
go 1.14replace (github.com/coreos/bbolt => go.etcd.io/bbolt v1.3.5google.golang.org/grpc => google.golang.org/grpc v1.26.0)require (github.com/coreos/bbolt v0.0.0-00010101000000-000000000000 // indirectgithub.com/coreos/etcd v3.3.25+incompatible // indirectgithub.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f // indirectgithub.com/google/uuid v1.2.0 // indirectgithub.com/prometheus/client_golang v1.9.0 // indirectgo.etcd.io/etcd v3.3.25+incompatiblego.uber.org/zap v1.16.0 // indirectgolang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 // indirectgoogle.golang.org/grpc v1.35.0 // indirect)
grpc和etcd
https://github.com/etcd-io/etcd/issues/12124#issuecomment-674368288
bblt
github.com/coreos/etcd/clientv3 tested bygithub.com/coreos/etcd/clientv3.test importsgithub.com/coreos/etcd/auth importsgithub.com/coreos/etcd/mvcc/backend importsgithub.com/coreos/bbolt: github.com/coreos/bbolt@v1.3.5: parsing go.mod:module declares its path as: go.etcd.io/bboltbut was required as: github.com/coreos/bbolt
解决
replace github.com/coreos/bbolt => go.etcd.io/bbolt v1.3.5
helloword
package mainimport ("context""log""time""go.etcd.io/etcd/clientv3")func main() {client, err := clientv3.New(clientv3.Config{Endpoints: []string{"127.0.0.1:2379"}, // 集群列表DialTimeout: 5 * time.Second,})if err != nil {log.Fatal(err)}kv := clientv3.NewKV(client)res, err := kv.Put(context.TODO(), "/config/dev/hello", "world", clientv3.WithPrevKV())if err != nil {log.Fatal(err)}log.Println("Revision:", res.Header.Revision)if res.PrevKv != nil {log.Println("PrevValue:", string(res.PrevKv.Value))}}
基本操作
put命令用来设置键值对数据,
get命令用来根据key获取值。
delete命令删除key
package mainimport ("context""fmt""time""go.etcd.io/etcd/clientv3")func main() {cli, err := clientv3.New(clientv3.Config{Endpoints: []string{"127.0.0.1:2379"},DialTimeout: 2 * time.Second,})if err != nil {fmt.Printf("connect etcd failed,%v\n", err)return}defer cli.Close()_, err = cli.Put(context.TODO(), "hello", "etcd")if err != nil {fmt.Printf("put etcd failed, %v\n", err)return}resp, err := cli.Get(context.TODO(), "hello")if err != nil {fmt.Printf("get etcd failed, %v\n", err)return}for _, kv := range resp.Kvs {fmt.Printf("%s:%s\n", kv.Key, kv.Value)}_, err = cli.Delete(context.TODO(), "hello")if err != nil {fmt.Printf("get etcd failed, %v\n", err)return}}
前缀搜索
package mainimport ("context""log""time""go.etcd.io/etcd/clientv3")func main() {client, err := clientv3.New(clientv3.Config{Endpoints: []string{"127.0.0.1:2379"}, // 集群列表DialTimeout: 5 * time.Second,})if err != nil {log.Fatal(err)}kv := clientv3.NewKV(client)_, err = kv.Put(context.TODO(), "/config/hello/dev", "etcd")if err != nil {log.Fatal(err)}_, err = kv.Put(context.TODO(), "/config/hello/test", "world")if err != nil {log.Fatal(err)}res, err := kv.Get(context.TODO(), "/config/hello/", clientv3.WithPrefix())if err != nil {log.Fatal(err)}log.Println("Revision:", res.Header.Revision)for _, v := range res.Kvs {log.Printf("Key=%s,Value=%s\n", string(v.Key), string(v.Value))}}
打印结果
Key=/config/hello/dev,Value=etcdKey=/config/hello/test,Value=world
watch
package mainimport ("context""fmt""time""go.etcd.io/etcd/clientv3")func main() {cli, err := clientv3.New(clientv3.Config{Endpoints: []string{"127.0.0.1:2379"},DialTimeout: 2 * time.Second,})if err != nil {fmt.Printf("connect to etcd failed, err:%v\n", err)return}defer cli.Close()wcs := cli.Watch(context.Background(), "hello") // <-chan WatchResponsefor c := range wcs {for _, ev := range c.Events {fmt.Printf("Type: %s Key:%s Value:%s\n", ev.Type, ev.Kv.Key, ev.Kv.Value)}}}
执行go run watch/main.go
接着执行 go run crud/main.go,在watch/main.go中会出现如下打印
Type: PUT Key:hello Value:etcdType: DELETE Key:hello Value:
模拟操作方式
package mainimport ("context""fmt""log""time""go.etcd.io/etcd/clientv3""go.etcd.io/etcd/mvcc/mvccpb")func main() {client, err := clientv3.New(clientv3.Config{Endpoints: []string{"127.0.0.1:2379"}, // 集群列表DialTimeout: 5 * time.Second,})if err != nil {log.Fatal(err)}// 模拟etcd中kv的变化go func() {for {client.Put(context.TODO(), "hello", "world")client.Delete(context.TODO(), "hello")time.Sleep(2 * time.Second)}}()// 启动监听 5秒后关闭ctx, cancelFunc := context.WithCancel(context.TODO())time.AfterFunc(10*time.Second, func() {cancelFunc()})watchRespChan := client.Watch(ctx, "hello")// 处理kv变化事件for watchResp := range watchRespChan {for _, event := range watchResp.Events {switch event.Type {case mvccpb.PUT:fmt.Println("put action", string(event.Kv.Value))case mvccpb.DELETE:fmt.Println("delete action", string(event.Kv.Key))}}}}
租约
package mainimport ("context""log""time""go.etcd.io/etcd/clientv3")func main() {client, err := clientv3.New(clientv3.Config{Endpoints: []string{"127.0.0.1:2379"}, // 集群列表DialTimeout: 5 * time.Second,})if err != nil {log.Fatal(err)}grant, err := client.Grant(context.TODO(), 10)if err != nil {log.Fatal(err)}k := "config/dev/hello"_, err = client.Put(context.TODO(),k,"etcd",clientv3.WithLease(grant.ID),)if err != nil {log.Fatal(err)}var res *clientv3.GetResponsefor {if res, err = client.Get(context.TODO(), k); err != nil {log.Println(err)return}if res.Count == 0 {log.Println("kv已过期")return}log.Println("key未过期")time.Sleep(2 * time.Second)}}
参考
https://github.com/etcd-io/etcd/issues/12464
https://github.com/etcd-io/etcd/issues/11772
https://colobu.com/2020/04/09/accidents-of-etcd-and-go-module/
