本文简单介绍Go语言对etcd v3的基本操作。
1. Import package
import (
"github.com/coreos/etcd/clientv3"
"github.com/coreos/etcd/mvcc/mvccpb"
)
2. Declare Variables
var cli *clientv3.Client
var serverList = []string{
"192.168.3.102:2379",
"192.168.3.105:2379",
"192.168.3.103:2379",
}
var userName = "root"
var password = "shiajun666"
var dialTimeout = 5
var opTimeout = 5
3. Connect to etcd server
var err error
cli, err := clientv3.New(clientv3.Config{
Endpoints: serverList,
DialTimeout: time.Duration(dialTimeout) * time.Second,
Username: userName,
Password: password,
})
if err != nil {
fmt.Println("Connect etcd server failed: ", err)
return
}
4. Get
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(opTimeout)*time.Second)
defer cancel()
key := "name"
resp, err := cli.Get(ctx, key)
if err != nil {
fmt.Printf("Get key[%s] failed: %v
", key, err)
return
}
if len(resp.Kvs) == 0 {
fmt.Printf("Key[%s] not exists.
", key)
return
}
fmt.Println("value: ", string(resp.Kvs[0].Value))
Get 方法的返回值如下:
其中 Header *ResponseHeader 几乎是etcd所有方法返回值中都会包含的,它包含了集群ID、etcd节点成员ID、key全局版本号、raft任期号:
Kvs []*mvccpb.KeyValue 包含多个键值对的信息,几乎etcd所有方法返回的键值对信息都由 mvccpb.KeyValue 表示,其结构如下:
5. Get values according to key prefix
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(opTimeout)*time.Second)
defer cancel()
prefix := "name"
resp, err := cli.Get(ctx, prefix, clientv3.WithPrefix())
if err != nil {
fmt.Printf("Get key prefix[%s] failed: %v
", prefix, err)
return
}
if len(resp.Kvs) == 0 {
fmt.Printf("Key prefix[%s] not exists.
", prefix)
return
}
fmt.Println("values: ", resp.Kvs)
6. put
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(opTimeout)*time.Second)
defer cancel()
key := "name"
value := "shiajun"
_, err = cli.Put(ctx, key, value)
if err != nil {
fmt.Printf("Put key[%s] value[%s] failed: %v
", key, value, err)
return
}
fmt.Println("put success")
Put 方法返回值如下:
prevKv 表示当前Put操作执行之前该key的键值对信息,Put方法第四个参数加上 clientv3.WithPrevKV() 即可返回该信息,如下一个例子所示。
7. put new value and get prev value
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(opTimeout)*time.Second)
defer cancel()
key := "name"
value := "shiajun"
opts := []clientv3.OpOption{clientv3.WithPrevKV()}
resp, err := cli.Put(ctx, key, value, opts...)
if err != nil {
fmt.Printf("Put key[%s] value[%s] failed: %v
", key, value, err)
return
}
fmt.Println("prev value: ", string(resp.PrevKv.Value))
8. put with lease
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(opTimeout)*time.Second)
defer cancel()
//grant lease
var ttl int64 = 10
lease, err := cli.Grant(ctx, ttl)
if err != nil {
fmt.Printf("Grant lease failed: %v
", err)
}
//put with lease
key := "name"
value := "shiajun"
_, err = cli.Put(ctx, key, value, clientv3.WithLease(lease.ID))
if err != nil {
fmt.Printf("Put key[%s] value[%s] with lease[%s] failed: %v
", key, value, lease.ID, err)
}
fmt.Println("put with lease success")
Grant 方法返回值如下:
9. put with lease and keep alive
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(opTimeout)*time.Second)
defer cancel()
//grant lease
var ttl int64 = 10
lease, err := cli.Grant(ctx, ttl)
if err != nil {
fmt.Printf("Grant lease failed: %v
", err)
}
//put with lease
key := "name"
value := "shiajun"
_, err = cli.Put(ctx, key, value, clientv3.WithLease(lease.ID))
if err != nil {
fmt.Printf("Put key[%s] value[%s] with lease[%s] failed: %v
", key, value, lease.ID, err)
}
//keep alive
kaCh, err := cli.KeepAlive(context.Background(), lease.ID)
if err != nil {
fmt.Printf("Keep alive key[%s] value[%s] with lease[%s] failed: %v
", key, value, lease.ID, err)
}
for {
kaResp := <-kaCh
fmt.Println("ttl: ", kaResp.TTL)
}
KeepAlive 方法返回值为一个channel:<-chan *LeaseKeepAliveResponse,接收每一次 keep alive 的结果返回:
10. delete
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(opTimeout)*time.Second)
defer cancel()
key := "name"
_, err = cli.Delete(ctx, key)
if err != nil {
fmt.Printf("Delete key[%s] failed: %v
", key, err)
return
}
fmt.Println("delete success")
Delete 方法返回值如下:
11. watch
key := "name"
opts := []clientv3.OpOption{clientv3.WithPrevKV(), clientv3.WithPrefix()}
wCh := cli.Watch(context.Background(), key, opts...)
for resp := range wCh {
for _, event := range resp.Events {
if event.Type == mvccpb.PUT {
fmt.Println("put happens")
} else if event.Type == mvccpb.DELETE {
fmt.Println("delete happens")
}
fmt.Println("prev value: ", event.PrevKv)
fmt.Println("value: ", event.Kv)
}
}
Watch 方法返回值为一个Channel:<-chan WatchResponse,接收watch的结果信息:
对watch的key进行put、delete操作,watch操作即可获得变更结果,示例如下:
12. compare and set - transation
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(opTimeout)*time.Second)
defer cancel()
key := "number"
kvc := clientv3.NewKV(cli)
resp, err := kvc.Txn(ctx).
If(clientv3.Compare(clientv3.Value(key), ">", "0")).
Then(clientv3.OpPut(key, "100")).
Else(clientv3.OpPut(key, "99")).
Commit()
if err != nil {
fmt.Errorf("Compare and set transation failed: %v
", err)
}
fmt.Println(resp.Succeeded)
这是使用etcd事务实现的一个先比较再赋值的简单例子:若 number 的值大于0,则将number赋值为100,否则赋值为99。