本文簡單介紹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\n", key, err)
return
}
if len(resp.Kvs) == 0 {
fmt.Printf("Key[%s] not exists.\n", 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\n", prefix, err)
return
}
if len(resp.Kvs) == 0 {
fmt.Printf("Key prefix[%s] not exists.\n", 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\n", 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\n", 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\n", 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\n", 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\n", 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\n", 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\n", 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\n", 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\n", err)
}
fmt.Println(resp.Succeeded)
這是使用etcd事務實現的一個先比較再賦值的簡單例子:若 number 的值大於0,則將number賦值為100,否則賦值為99。