github:https://github.com/etcd-io/etcd/tree/master/clientv3
测试go版本
go 1.14
require (
go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489
google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc // indirect
google.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.13
google.golang.org/grpc => google.golang.org/grpc v1.27.1
)
或者
go 1.14
replace (
github.com/coreos/bbolt => go.etcd.io/bbolt v1.3.5
google.golang.org/grpc => google.golang.org/grpc v1.26.0
)
require (
github.com/coreos/bbolt v0.0.0-00010101000000-000000000000 // indirect
github.com/coreos/etcd v3.3.25+incompatible // indirect
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f // indirect
github.com/google/uuid v1.2.0 // indirect
github.com/prometheus/client_golang v1.9.0 // indirect
go.etcd.io/etcd v3.3.25+incompatible
go.uber.org/zap v1.16.0 // indirect
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 // indirect
google.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 by
github.com/coreos/etcd/clientv3.test imports
github.com/coreos/etcd/auth imports
github.com/coreos/etcd/mvcc/backend imports
github.com/coreos/bbolt: github.com/coreos/bbolt@v1.3.5: parsing go.mod:
module declares its path as: go.etcd.io/bbolt
but was required as: github.com/coreos/bbolt
解决
replace github.com/coreos/bbolt => go.etcd.io/bbolt v1.3.5
helloword
package main
import (
"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 main
import (
"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 main
import (
"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=etcd
Key=/config/hello/test,Value=world
watch
package main
import (
"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 WatchResponse
for 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:etcd
Type: DELETE Key:hello Value:
模拟操作方式
package main
import (
"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 main
import (
"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.GetResponse
for {
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/