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