etcd 笔记(06)— Client 结构定义、客户端(初始化、KV存储Get、Put、事务 Txn、压缩 Compact、Watch、Lease
1. Client 定義
Client 定義如下:
type Client struct {ClusterKVLeaseWatcherAuthMaintenance// 認(rèn)證的用戶名Username string// 認(rèn)證的密碼Password string
}
這里顯示的都是可導(dǎo)出的模塊結(jié)構(gòu)字段,代表了客戶端能夠使用的幾大核心模塊,具體功能介紹如下。
Cluster:向集群里增加etcd服務(wù)端節(jié)點(diǎn)之類,屬于管理員操作;KV:我們主要使用的功能,即操作K-V;Lease:租約相關(guān)操作,比如申請(qǐng)一個(gè)TTL=10秒的租約;Watcher:觀察訂閱,從而監(jiān)聽最新的數(shù)據(jù)變化;Auth:管理etcd的用戶和權(quán)限,屬于管理員操作;Maintenance:維護(hù)etcd,比如主動(dòng)遷移etcd的leader節(jié)點(diǎn),屬于管理員操作;
2. gRPC 服務(wù)
etcd v3 的通信基于 gRPC,proto文件是定義服務(wù)端和客戶端通信接口的標(biāo)準(zhǔn)。包括:
- 客戶端該傳什么樣的參數(shù)
- 服務(wù)端該返回什么參數(shù)
- 客戶端該怎么調(diào)用
- 是阻塞還是非阻塞
- 是同步還是異步
gRPC 推薦使用 proto3 消息格式,proto3 是原有 Protocol Buffer 2(被稱為 proto2)的升級(jí)版本,刪除了一部分特性,優(yōu)化了對(duì)移動(dòng)設(shè)備的支持。
發(fā)送到 etcd 服務(wù)器的每個(gè) API 請(qǐng)求都是一個(gè) gRPC 遠(yuǎn)程過程調(diào)用。etcd 中的 RPC 接口定義根據(jù)功能分類到服務(wù)中。
處理 etcd 鍵值的重要服務(wù)包括:
KV Service:創(chuàng)建、更新、獲取和刪除鍵值對(duì);Watch Service:監(jiān)視鍵的更改;Lease Service:實(shí)現(xiàn)鍵值對(duì)過期,客戶端用來續(xù)租、保持心跳;Lock Service:etcd提供分布式共享鎖的支持;Election Service:暴露客戶端選舉機(jī)制;
3. 請(qǐng)求和響應(yīng)
3.1 請(qǐng)求
etcd3 中的所有 RPC 都遵循相同的格式。每個(gè) RPC 都有一個(gè)函數(shù)名,該函數(shù)將 NameRequest 作為參數(shù)并返回 NameResponse 作為響應(yīng)。例如,這是 Range RPC 描述:
service KV {Range(RangeRequest) returns (RangeResponse)...
}
3.2 響應(yīng)頭
etcd API 的所有響應(yīng)都有一個(gè)附加的響應(yīng)標(biāo)頭,其中包括響應(yīng)的集群元數(shù)據(jù):
message ResponseHeader {uint64 cluster_id = 1;uint64 member_id = 2;int64 revision = 3;uint64 raft_term = 4;
}
其中:
Cluster_ID:產(chǎn)生響應(yīng)的集群的ID;Member_ID:產(chǎn)生響應(yīng)的成員的 ID;
應(yīng)用服務(wù)可以通過 Cluster_ID 和 Member_ID 字段來確保當(dāng)前與之通信的正是預(yù)期的那個(gè)集群或者成員。
Revision:產(chǎn)生響應(yīng)時(shí)鍵值存儲(chǔ)的修訂版本號(hào);
應(yīng)用服務(wù)可以使用修訂號(hào)字段來獲得當(dāng)前鍵值存儲(chǔ)庫最新的修訂號(hào)。應(yīng)用程序指定歷史修訂版以進(jìn)行查詢,如果希望在請(qǐng)求時(shí)知道最新修訂版,此功能特別有用。
Raft_Term:產(chǎn)生響應(yīng)時(shí),成員的Raft稱謂。
應(yīng)用服務(wù)可以使用 Raft_Term 來檢測(cè)集群何時(shí)完成一個(gè)新的 leader 選舉。
4. etcd clientv3 客戶端
4.1 初始化
我們根據(jù)指定的 etcd 節(jié)點(diǎn),建立客戶端與 etcd 集群的連接:
config := clientv3.Config{Endpoints:[]string{"localhost:2379", "localhost:2379"},DialTimeout: 5 * time.Second,
}
client, err := clientv3.New(config)// etcd clientv3 >= v3.2.10, grpc/grpc-go >= v1.7.3
if client == nil || err == context.DeadlineExceeded {// handle errorsfmt.Println(err)panic("invalid connection!")
}
// 客戶端斷開連接
defer client.Close()
如上的代碼實(shí)例化了一個(gè) client,這里需要傳入兩個(gè)參數(shù)。
-
Endpoints:etcd的多個(gè)節(jié)點(diǎn)服務(wù)地址; -
DialTimeout:創(chuàng)建client的首次連接超時(shí)時(shí)間,這里傳了 5 秒,如果 5 秒都沒有連接成功就會(huì)返回err。
需要注意的是,一旦 client 創(chuàng)建成功,我們就不用再關(guān)心后續(xù)底層連接的狀態(tài)了,client 內(nèi)部會(huì)重連。
4.2 KV 存儲(chǔ)
KV 對(duì)象的實(shí)例獲取通過如下的方式:
kv := clientv3.NewKV(client)
KV 接口的具體定義:
type KV interface {Put(ctx context.Context, key, val string, opts ...OpOption) (*PutResponse, error)// 檢索 keysGet(ctx context.Context, key string, opts ...OpOption) (*GetResponse, error)// 刪除 key,可以使用 WithRange(end), [key, end) 的方式Delete(ctx context.Context, key string, opts ...OpOption) (*DeleteResponse, error)// 壓縮給定版本之前的 KV 歷史Compact(ctx context.Context, rev int64, opts ...CompactOption) (*CompactResponse, error)// 指定某種沒有事務(wù)的操作Do(ctx context.Context, op Op) (OpResponse, error)// Txn 創(chuàng)建一個(gè)事務(wù)Txn(ctx context.Context) Txn
}
從 KV 對(duì)象的定義我們可知,它就是一個(gè)接口對(duì)象,包含以下幾個(gè)主要的 KV 操作方法。
4.2.1 Put
Put 的定義如下:
Put(ctx context.Context, key, val string, opts ...OpOption) (*PutResponse, error)
其中的參數(shù)
ctx:Context包對(duì)象,用來跟蹤上下文,比如超時(shí)控制;key:存儲(chǔ)對(duì)象的key;val:存儲(chǔ)對(duì)象的value;opts:可變參數(shù),額外選項(xiàng);
使用示例:
putResp, err := kv.Put(context.TODO(),"aa", "hello-world!")
4.2.2 Get
Get 的定義如下:
Get(ctx context.Context, key string, opts ...OpOption) (*GetResponse, error)
OpOption 為可選的函數(shù)傳參:
- 傳參為
WithRange(end)時(shí),Get將返回[key,end)范圍內(nèi)的鍵; - 傳參為
WithFromKey()時(shí),Get返回大于或等于key的鍵; - 當(dāng)通過
rev> 0傳遞WithRev(rev)時(shí),Get查詢給定修訂版本的鍵; - 如果壓縮了所查找的修訂版本,則返回請(qǐng)求失敗,并顯示
ErrCompacted; - 傳遞
WithLimit(limit)時(shí),返回的key數(shù)量受limit限制; - 傳參為
WithSort時(shí),將對(duì)鍵進(jìn)行排序;
對(duì)應(yīng)的使用方法如下:
getResp, err := kv.Get(context.TODO(), "aa")
從上面可以看出,Put 返回 PutResponse,Get 返回 GetResponse。注意:不同的 KV 操作對(duì)應(yīng)不同的 Response 結(jié)構(gòu),定義如下:
type (CompactResponse pb.CompactionResponsePutResponse pb.PutResponseGetResponse pb.RangeResponseDeleteResponse pb.DeleteRangeResponseTxnResponse pb.TxnResponse
)
下面我們分別來看一看 PutResponse 和 GetResponse 映射的 RangeResponse 結(jié)構(gòu)的定義:
type PutResponse struct {Header *ResponseHeader `protobuf:"bytes,1,opt,name=header" json:"header,omitempty"`// 請(qǐng)求中如有 prev_kv,響應(yīng)時(shí)也會(huì)攜帶 prev_kv PrevKv *mvccpb.KeyValue `protobuf:"bytes,2,opt,name=prev_kv,json=prevKv" json:"prev_kv,omitempty"`
}
//Header 里保存的主要是本次更新的 revision 信息
type RangeResponse struct {Header *ResponseHeader `protobuf:"bytes,1,opt,name=header" json:"header,omitempty"`// kvs 是一個(gè)匹配 range 請(qǐng)求的鍵值對(duì)列表Kvs []*mvccpb.KeyValue `protobuf:"bytes,2,rep,name=kvs" json:"kvs,omitempty"`// more 用以分頁 More bool `protobuf:"varint,3,opt,name=more,proto3" json:"more,omitempty"`// count 表示 range 的鍵值對(duì)數(shù)量Count int64 `protobuf:"varint,4,opt,name=count,proto3" json:"count,omitempty"`
}
Kvs 字段,保存了本次 Get 查詢到的所有 KV 對(duì),我們繼續(xù)看一下 mvccpb.KeyValue 對(duì)象的定義:
type KeyValue struct {Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`// create_revision 是當(dāng)前 key 的最后創(chuàng)建版本CreateRevision int64 `protobuf:"varint,2,opt,name=create_revision,json=createRevision,proto3" json:"create_revision,omitempty"`// mod_revision 是指當(dāng)前 key 的最新修訂版本ModRevision int64 `protobuf:"varint,3,opt,name=mod_revision,json=modRevision,proto3" json:"mod_revision,omitempty"`// key 的版本,每次更新都會(huì)增加版本號(hào)Version int64 `protobuf:"varint,4,opt,name=version,proto3" json:"version,omitempty"`Value []byte `protobuf:"bytes,5,opt,name=value,proto3" json:"value,omitempty"`// 綁定了 key 的租期 Id,當(dāng) lease 為 0 ,則表明沒有綁定 key;租期過期,則會(huì)刪除 keyLease int64 `protobuf:"varint,6,opt,name=lease,proto3" json:"lease,omitempty"`
}
至于 RangeResponse.More 和 Count,當(dāng)我們使用 withLimit() 選項(xiàng)進(jìn)行 Get 時(shí)會(huì)發(fā)揮作用,相當(dāng)于分頁查詢。
通過一個(gè)特別的 Get 選項(xiàng),獲取 aa 目錄下的所有子目錄:
rangeResp, err := kv.Get(context.TODO(), "/aa", clientv3.WithPrefix())
WithPrefix() 用于查找以 /aa為前綴的所有 key,因此可以模擬出查找子目錄的效果。我們知道 etcd 是一個(gè)有序的 KV 存儲(chǔ),因此 /aa 為前綴的 key 總是順序排列在一起。
WithPrefix 實(shí)際上會(huì)轉(zhuǎn)化為范圍查詢,它根據(jù)前綴 /aa 生成了一個(gè) key range,[“/aa/”, “/aa0”),這是因?yàn)楸?/大的字符是 0,所以以 /aa0 作為范圍的末尾,就可以掃描到所有的 /aa/ 打頭的 key 了。
4.3 事務(wù) Txn
Txn 方法在單個(gè)事務(wù)中處理多個(gè)請(qǐng)求。Txn 請(qǐng)求增加鍵值存儲(chǔ)的修訂版本,并為每個(gè)完成的請(qǐng)求生成帶有相同修訂版本的事件,etcd 不容許在一個(gè) Txn 中多次修改同一個(gè) key。
Txn 接口定義如下:
rpc Txn(TxnRequest) returns (TxnResponse) {}
4.4 Compact
Compact 方法壓縮 etcd 鍵值對(duì)存儲(chǔ)中的事件歷史。鍵值對(duì)存儲(chǔ)應(yīng)該定期壓縮,否則事件歷史會(huì)無限制地持續(xù)增長(zhǎng)。
Compact 接口定義如下:
rpc Compact(CompactionRequest) returns (CompactionResponse) {}
請(qǐng)求的消息體是 CompactionRequest, CompactionRequest 壓縮鍵值對(duì)存儲(chǔ)到給定修訂版本,所有修訂版本比壓縮修訂版本小的鍵都將被刪除。
4.5 Watch
Watch API 提供了一個(gè)基于事件的接口,用于異步監(jiān)視鍵的更改。etcd 監(jiān)視程序通過給定的修訂版本(當(dāng)前版本或歷史版本)持續(xù)監(jiān)視 key 更改,并將 key 更新流回客戶端。
在 rpc.proto 中 Watch Service 定義如下:
service Watch {rpc Watch(stream WatchRequest) returns (stream WatchResponse) {}
}
Watch 觀察將要發(fā)生或者已經(jīng)發(fā)生的事件。輸入和輸出都是流,輸入流用于創(chuàng)建和取消觀察,而輸出流發(fā)送事件。一個(gè)觀察 RPC 可以一次性在多個(gè) key 范圍上觀察,并為多個(gè)觀察流化事件。整個(gè)事件歷史可以從最后壓縮修訂版本開始觀察。Watch Service 只有一個(gè) Watch 方法。
4.6 Lease Service
Lease Service 提供租約的支持。Lease 是一種檢測(cè)客戶端存活狀況的機(jī)制。集群授予客戶端具有生存時(shí)間的租約。如果 etcd 集群在給定的 TTL 時(shí)間內(nèi)未收到 keepAlive,則租約到期。
為了將租約綁定到鍵值存儲(chǔ)中,每個(gè) key 最多可以附加一個(gè)租約。當(dāng)租約到期或被撤銷時(shí),該租約依附的所有 key 都將被刪除,每個(gè)過期的密鑰都會(huì)在事件歷史記錄中生成一個(gè)刪除事件。
在 rpc.proto 中 Lease Service 定義的接口如下:
service Lease {rpc LeaseGrant(LeaseGrantRequest) returns (LeaseGrantResponse) {}rpc LeaseRevoke(LeaseRevokeRequest) returns (LeaseRevokeResponse) {}rpc LeaseKeepAlive(stream LeaseKeepAliveRequest) returns (stream LeaseKeepAliveResponse) {}rpc LeaseTimeToLive(LeaseTimeToLiveRequest) returns (LeaseTimeToLiveResponse) {}
}
其中:
LeaseGrant創(chuàng)建一個(gè)租約;LeaseRevoke撤銷一個(gè)租約;LeaseKeepAlive用于維持租約;LeaseTimeToLive獲取租約信息;
4.7 Lock Service
Lock Service 提供分布式共享鎖的支持。Lock Service 以 gRPC 接口的方式暴露客戶端鎖機(jī)制。
在 v3lock.proto 中 Lock Service 定義如下:
service Lock {rpc Lock(LockRequest) returns (LockResponse) {}rpc Unlock(UnlockRequest) returns (UnlockResponse) {}
}
其中:
Lock方法,在給定命令鎖上獲得分布式共享鎖;Unlock使用Lock返回的key并釋放對(duì)鎖的持有;
參考:
https://kaiwu.lagou.com/course/courseInfo.htm?courseId=613#/detail/pc?id=6403
總結(jié)
以上是生活随笔為你收集整理的etcd 笔记(06)— Client 结构定义、客户端(初始化、KV存储Get、Put、事务 Txn、压缩 Compact、Watch、Lease的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2022-2028年中国硅酸钙板行业市场
- 下一篇: 2022-2028年中国汽车橡胶件行业市