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年中国汽车橡胶件行业市