欢迎访问 生活随笔!

生活随笔

当前位置: 首页 >

etcd 笔记(06)— Client 结构定义、客户端(初始化、KV存储Get、Put、事务 Txn、压缩 Compact、Watch、Lease

发布时间:2023/11/28 46 豆豆
生活随笔 收集整理的这篇文章主要介绍了 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,比如主动迁移 etcdleader 节点,属于管理员操作;

2. gRPC 服务

etcd v3 的通信基于 gRPCproto文件是定义服务端和客户端通信接口的标准。包括:

  • 客户端该传什么样的参数
  • 服务端该返回什么参数
  • 客户端该怎么调用
  • 是阻塞还是非阻塞
  • 是同步还是异步

gRPC 推荐使用 proto3 消息格式,proto3 是原有 Protocol Buffer 2(被称为 proto2)的升级版本,删除了一部分特性,优化了对移动设备的支持。

发送到 etcd 服务器的每个 API 请求都是一个 gRPC 远程过程调用。etcd 中的 RPC 接口定义根据功能分类到服务中。

处理 etcd 键值的重要服务包括:

  • KV Service:创建、更新、获取和删除键值对;
  • Watch Service:监视键的更改;
  • Lease Service:实现键值对过期,客户端用来续租、保持心跳;
  • Lock Serviceetcd 提供分布式共享锁的支持;
  • 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_IDMember_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,这里需要传入两个参数。

  • Endpointsetcd 的多个节点服务地址;

  • 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)

其中的参数

  • ctxContext 包对象,用来跟踪上下文,比如超时控制;
  • 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 返回 PutResponseGet 返回 GetResponse。注意:不同的 KV 操作对应不同的 Response 结构,定义如下:

type (CompactResponse pb.CompactionResponsePutResponse     pb.PutResponseGetResponse     pb.RangeResponseDeleteResponse  pb.DeleteRangeResponseTxnResponse     pb.TxnResponse
)

下面我们分别来看一看 PutResponseGetResponse 映射的 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.MoreCount,当我们使用 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) {}

请求的消息体是 CompactionRequestCompactionRequest 压缩键值对存储到给定修订版本,所有修订版本比压缩修订版本小的键都将被删除。

4.5 Watch

Watch API 提供了一个基于事件的接口,用于异步监视键的更改。etcd 监视程序通过给定的修订版本(当前版本或历史版本)持续监视 key 更改,并将 key 更新流回客户端。

rpc.protoWatch 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.protoLease 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 ServicegRPC 接口的方式暴露客户端锁机制。

v3lock.protoLock 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的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。