前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >读猿码系列——2. 搞懂Etcd核心API

读猿码系列——2. 搞懂Etcd核心API

作者头像
才浅Coding攻略
发布2022-12-12 18:06:23
4360
发布2022-12-12 18:06:23
举报
文章被收录于专栏:才浅coding攻略才浅coding攻略

阿巩

防范区的骄傲

小区从管控区调整为防范区了,40多天的封闭后终于可以光明正大地下楼遛狗了!许愿能尽快吃上平价麦当劳,而且每顿都有可口可乐!日拱一卒,让我们开始吧!(长文预警哦)

Etcd是一个高可用的分布式键值(key-value)数据库,Etcd也是云原生架构中重要的基础组件之一,它在微服务和Kubernates集群中不仅可以作为服务注册与发现,还可以作为key-value存储的中间件,供应用程序读取和写入数据。

作为一个高度一致的分布式键值存储,它提供了一种可靠的方式来存储需要由分布式系统或机器集群访问的数据。它可以优雅地处理网络分区期间的 leader 选举,以应对机器的故障,即使是在 leader 节点发生故障时。

Etcd的存储有如下特点:

  • 采用键值对数据存储,读写性能一般高于关系型数据库;
  • Etcd 集群分布式存储,多节点集群更加可靠;
  • Etcd 的存储采用类似文件目录的结构:
  1. 叶子节点存储数据,其他节点不存储,这些数据相当于文件;
  2. 非叶节点一定是目录,这些节点不能存储数据。

在分布式环境中,我们发现对于服务注册与发现涉及到三个主要的角色:服务请求者、服务提供者和服务注册中心。

  1. 首先服务提供者启动服务,并到注册中心注册自己的服务名、主机地址、端口等信息;
  2. 当服务请求者调用对应的服务时,一般通过服务名请求服务注册中心,服务注册中心返回对应的实例地址和端口;
  3. 服务请求者获取到实例地址、端口之后,绑定对应的服务提供者,实现远程调用。

我们将各个服务启动时注册到 Etcd 上,同时为这些服务配置键的 TTL 时间,定时保持服务的心跳以达到监控健康状态的效果。为了确保连接,我们可以在每个服务机器上都部署一个 Proxy 模式的 Etcd,这样就可以确保访问 Etcd 集群的服务都能够互相连接。由于Etcd 基于 Raft 算法,实现分布式集群的一致性,存储到 Etcd 集群中的值必然是全局一致的。

对于分布式锁有两种使用方式:保持独占和控制时序。

  • 保持独占:多个请求获取资源,只有一个成功获取锁。Etcd 通过分布式锁原子操作 CAS 的 API,设置 prevExist 值,从而保证在多个节点同时去创建某个目录时,最后只有一个成功,创建成功的请求获取到锁;
  • 控制时序:执行锁的顺序全局唯一,按先后顺序获得锁。Etcd 提供了一套自动创建有序键的 API,对一个目录的建值操作,这样 Etcd 会自动生成一个当前最大的值为键,并存储该值。

在上一期的最后,我们看到了watcher调用Etcd提供的clientv3.Watcher接口的方法。

这期接着上期来深入探究下Etcd的核心API是如何实现的。

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

  • KV 服务(KV service):创建、更新、获取和删除键值对;
  • 监视(Watch service):监视键的更改;
  • 租约(Lease service):消耗客户端保持活动的基元;
  • 锁(Lock service):Etcd提供分布式共享锁的支持;
  • 选举(Election service):暴露客户端选举机制。

发送到Etcd服务的每个API请求都是一个gRPC远程过程调用,Etcd3中的所有RPC都遵循相同的格式。

KV service

我们拿出etcdserver/etcdserverpb下的rpc.proto来看下,首先是KV service:

代码语言:javascript
复制
service KV {
  // 从键值存储中获取范围内的key.
  rpc Range(RangeRequest) returns (RangeResponse) {
      option (google.api.http) = {
        post: "/v3beta/kv/range"
        body: "*"
    };
  }

  // 放置给定key到键值存储.
  // put请求增加键值存储的修订版本并在事件历史中生成一个事件.
  rpc Put(PutRequest) returns (PutResponse) {
      option (google.api.http) = {
        post: "/v3beta/kv/put"
        body: "*"
    };
  }

  // 从键值存储中删除给定范围。
  // 删除请求增加键值存储的修订版本并在事件历史中为每个被删除的key生成一个删除事件.
  rpc DeleteRange(DeleteRangeRequest) returns (DeleteRangeResponse) {
      option (google.api.http) = {
        post: "/v3beta/kv/deleterange"
        body: "*"
    };
  }

  // 在单个事务中处理多个请求。
  // 一个 txn 请求增加键值存储的修订版本并为每个完成的请求生成带有相同修订版本的事件。
  // 不容许在一个txn中多次修改同一个key.
  rpc Txn(TxnRequest) returns (TxnResponse) {
      option (google.api.http) = {
        post: "/v3beta/kv/txn"
        body: "*"
    };
  }

  // 压缩在etcd键值存储中的事件历史。
  // 键值存储应该定期压缩,否则事件历史会无限制的持续增长.
  rpc Compact(CompactionRequest) returns (CompactionResponse) {
      option (google.api.http) = {
        post: "/v3beta/kv/compaction"
        body: "*"
    };
  }
}
  • Range方法从键值存储中获取范围内的key,请求RangeRequest结构如下:
代码语言:javascript
复制
message RangeRequest {
  enum SortOrder {
    NONE = 0; // 默认,不排序
    ASCEND = 1; // 正序,低的值在前
    DESCEND = 2; // 倒序,高的值在前
  }
  enum SortTarget {
    KEY = 0;
    VERSION = 1;
    CREATE = 2;
    MOD = 3;
    VALUE = 4;
  }

  // key 是 range 的第一个 key。如果 range_end 没有给定,请求仅查找这个 key
  bytes key = 1;
  // range_end 代表请求的上限,如果 range_end 是 '\0',范围是大于等于 key 的所有key
  bytes range_end = 2;
  // 请求返回的key的数量限制
  int64 limit = 3;
  // revision 修订版本作于 range 键值对存储的时间点。如果 revision 小于或等于零,范围是在最新的键值对存储上。
  int64 revision = 4;

  // 指定返回结果的排序顺序
  SortOrder sort_order = 5;

  // 用于排序的键值字段
  SortTarget sort_target = 6;

  // serializable 设置 range 请求使用串行化成员本地读。
  bool serializable = 7;

  // 设置仅返回key而不需要value
  bool keys_only = 8;

  // 设置仅仅返回范围内key的数量
  bool count_only = 9;

  // min_mod_revision 是返回 key 的 mod revision 的下限;更低 mod revision 的所有 key 都将被过滤掉
  int64 min_mod_revision = 10;

  // max_mod_revision 是返回 key 的 mod revision 的上限;更高 mod revision 的所有 key 都将被过滤掉
  int64 max_mod_revision = 11;

  // min_create_revision 是返回 key 的 create revision 的下限;更低 create revision 的所有 key 都将被过滤掉
  int64 min_create_revision = 12;

  // max_create_revision 是返回 key 的 create revision 的上限;更高 create revision 的所有 key 都将被过滤掉
  int64 max_create_revision = 13;
}

响应RangeResponse的结构如下:

代码语言:javascript
复制
message RangeResponse {
  ResponseHeader header = 1;
  // kvs 是匹配 range 请求的键值对列表
  // 当 count 时是空的
  repeated mvccpb.KeyValue kvs = 2;
  // more 代表在被请求的范围内是否还有更多的 key
  bool more = 3;
  // count 被设置为在范围内的 key 的数量
  int64 count = 4;
}

Etcd所有响应都包含一个响应头ResponseHeader,其结构如下:

代码语言:javascript
复制
message ResponseHeader {
  // 产生响应的集群的ID
  uint64 cluster_id = 1;
  // 产生响应的成员的ID
  uint64 member_id = 2;
  // 产生响应时键值存储的修订版本号
  int64 revision = 3;
  // 产生响应时,成员的Raft称谓
  uint64 raft_term = 4;
}

应用服务可以通过 Cluster_ID 和 Member_ID 字段来确保,当前与之通信的正是预期的那个集群或者成员。应用服务可以使用 Raft_Term 来检测集群何时完成一个新的 leader 选举。

mvccpb.KeyValue结构如下:

代码语言:javascript
复制
message KeyValue {
  // key 是 bytes 格式的 key。不容许 key 为空。
  bytes key = 1;
  // create_revision 是这个 key 最后一次创建的修订版本
  int64 create_revision = 2;
  // mod_revision 是这个 key 最后一次修改的修订版本
  int64 mod_revision = 3;
  // version 是 key 的版本。删除会重置版本为0,而任何 key 的修改会增加它的版本。
  int64 version = 4;
  // value 是 key 持有的值,bytes 格式。
  bytes value = 5;
  // lease 是附加给 key 的租约 id。
  // 当附加的租约过期时,key 将被删除。
  // 如果 lease 为0,则没有租约附加到 key。
  int64 lease = 6;
}
  • Put方法存储key到数据库,Put 方法增加键值存储的修订版本并在事件历史中生成一个事件。

PutRequest结构如下:

代码语言:javascript
复制
message PutRequest {
  // byte 数组形式的 key,用来保存到键值对存储
  bytes key = 1;
  // byte 数组形式的 value,在键值对存储中和 key 关联
  bytes value = 2;
  // 在键值存储中和 key 关联的租约id。0代表没有租约。
  int64 lease = 3;
  // 如果 prev_kv 被设置,etcd 获取改变之前的上一个键值对。
  // 上一个键值对将在 put 应答中被返回
  bool prev_kv = 4;
  // 如果 ignore_value 被设置, etcd 使用它当前的 value 更新 key.
  // 如果 key 不存在,返回错误.
  bool ignore_value = 5;
  // 如果 ignore_lease 被设置, etcd 使用它当前的租约更新 key.
  // 如果 key 不存在,返回错误.
  bool ignore_lease = 6;
}

PutResponse结构如下:

代码语言:javascript
复制
message PutResponse {
  ResponseHeader header = 1;
  // 如果请求中的 prev_kv 被设置,将会返回上一个键值对
  mvccpb.KeyValue prev_kv = 2;
}
  • DeleteRange 方法从键值存储中删除给定范围,删除请求增加键值存储的修订版本,并在事件历史中为每个被删除的 key 生成一个删除事件。
代码语言:javascript
复制
message DeleteRangeRequest {
  // key是要删除的范围的第一个key
  bytes key = 1;
  // range_end 是要删除范围[key, range_end)的最后一个key
  // 如果 range_end 没有给定,范围定义为仅包含 key 参数
  // 如果 range_end 比给定的 key 大1,则 range 是以给定 key 为前缀的所有 key
  // 如果 range_end 是 '\0', 范围是所有大于等于参数 key 的所有 key。
  bytes range_end = 2;
  // 如果 prev_kv 被设置,etcd获取删除之前的上一个键值对。
  // 上一个键值对将在 delete 应答中被返回
  bool prev_kv = 3;
}
代码语言:javascript
复制
message DeleteRangeResponse {
  ResponseHeader header = 1;
  // 被范围删除请求删除的 key 的数量
  int64 deleted = 2;
  // 如果请求中的 prev_kv 被设置,将会返回上一个键值对
  repeated mvccpb.KeyValue prev_kvs = 3;
}
  • Txn 方法在单个事务中处理多个请求,txn 请求增加键值存储的修订版本并为每个完成的请求生成带有相同修订版本的事件。不容许在一个 txn 中多次修改同一个 key。
代码语言:javascript
复制
message TxnRequest {
  // compare 是断言列表,体现为条件的联合。
  // 如果比较成功,那么成功请求将被按顺序处理,而应答将按顺序包含他们对应的应答。
  // 如果比较失败,那么失败请求将被按顺序处理,而应答将按顺序包含他们对应的应答。
  repeated Compare compare = 1;
  // 成功请求列表,当比较评估为 true 时将被应用。
  repeated RequestOp success = 2;
  // 失败请求列表,当比较评估为 false 时将被应用。
  repeated RequestOp failure = 3;
}

其中Compare消息体:

代码语言:javascript
复制
message Compare {
  enum CompareResult {
    EQUAL = 0;
    GREATER = 1;
    LESS = 2;
    NOT_EQUAL = 3;
  }
  enum CompareTarget {
    VERSION = 0;
    CREATE = 1;
    MOD = 2;
    VALUE= 3;
  }
  // result 是这个比较的逻辑比较操作
  CompareResult result = 1;
  // target 是比较要检查的键值字段
  CompareTarget target = 2;
  // key 是用于比较操作的主题key
  bytes key = 3;
  oneof target_union {
    // version 是给定 key 的版本
    int64 version = 4;
    // create_revision 是给定 key 的创建修订版本
    int64 create_revision = 5;
    // mod_revision 是给定 key 的最后修改修订版本
    int64 mod_revision = 6;
    // value 是给定 key 的值,以 bytes 的形式
    bytes value = 7;
  }
}

RequestOp消息体:

代码语言:javascript
复制
message RequestOp {
  // request 是可以被事务接受的请求类型的联合
  oneof request {
    RangeRequest request_range = 1;
    PutRequest request_put = 2;
    DeleteRangeRequest request_delete_range = 3;
  }
}

应答的消息体TnxResponse:

代码语言:javascript
复制
message TxnResponse {
  ResponseHeader header = 1;
  // 如果比较评估为true则succeeded被设置为true,否则是false
  bool succeeded = 2;
  // 应答列表,如果 succeeded 是 true 则对应成功请求,如果 succeeded 是 false 则对应失败请求
  repeated ResponseOp responses = 3;
}

ResponseOp消息体:

代码语言:javascript
复制
message ResponseOp {
  // response 是事务返回的应答类型的联合
  oneof response {
    RangeResponse response_range = 1;
    PutResponse response_put = 2;
    DeleteRangeResponse response_delete_range = 3;
  }
}
  • Compact 方法压缩 etcd 键值对存储中的事件历史,键值对存储应该定期压缩,否则事件历史会无限制的持续增长。

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

代码语言:javascript
复制
message CompactionRequest {
  // 键值存储的修订版本,用于比较操作
  int64 revision = 1;
  // physical设置为 true 时 RPC 将会等待直到压缩物理性的应用到本地数据库,到这程度被压缩的项将完全从后端数据库中移除。
  bool physical = 2;
}
代码语言:javascript
复制
message CompactionResponse {
  ResponseHeader header = 1;
}

Watch service

Watch service提供观察键值对变化的支持。在 rpc.proto 中 Watch service 定义如下:

代码语言:javascript
复制
service Watch {
  // Watch 观察将要发生或者已经发生的事件。
  // 输入和输出都是流;输入流用于创建和取消观察,而输出流发送事件。
  // 一个观察 RPC 可以在一次性在多个key范围上观察,并为多个观察流化事件。
  // 整个事件历史可以从最后压缩修订版本开始观察。
  rpc Watch(stream WatchRequest) returns (stream WatchResponse) {
      option (google.api.http) = {
        post: "/v3beta/watch"
        body: "*"
    };
  }
}

WatchRequest请求体:

代码语言:javascript
复制
message WatchRequest {
  // request_union 要么是创建新的观察者的请求,要么是取消一个已经存在的观察者的请求
  oneof request_union {
    WatchCreateRequest create_request = 1;
    WatchCancelRequest cancel_request = 2;
  }
}

创建新的观察者的请求 WatchCreateRequest:

代码语言:javascript
复制
message WatchCreateRequest {
  // key 是注册要观察的 key
  bytes key = 1;
  // range_end 是要观察的范围 [key, range_end) 的终点。
  // 如果 range_end 没有设置,则只有参数 key 被观察。
  // 如果 range_end 等同于 '\0', 则大于等于参数 key 的所有 key 都将被观察
  // 如果 range_end 比给定 key 大1, 则所有以给定 key 为前缀的 key 都将被观察
  bytes range_end = 2;
  // start_revision 是可选的开始(包括)观察的修订版本。不设置 start_revision 则表示 "现在".
  int64 start_revision = 3;
  // 设置 progress_notify ,这样如果最近没有事件,etcd 服务器将定期的发送不带任何事件的 WatchResponse 给新的观察者。
  // 当客户端希望从最近已知的修订版本开始恢复断开的观察者时有用。
  // etcd 服务器将基于当前负载决定它发送通知的频率。
  bool progress_notify = 4;
  enum FilterType {
  // 过滤掉 put 事件
  NOPUT = 0;
  // 过滤掉 delete 事件
  NODELETE = 1;
  }
  // 过滤器,在服务器端发送事件给回观察者之前,过滤掉事件。
  repeated FilterType filters = 5;
  // 如果 prev_kv 被设置,被创建的观察者在事件发生前获取上一次的KV。
  // 如果上一次的KV已经被压缩,则不会返回任何东西
  bool prev_kv = 6;
}

取消已有观察者的 WatchCancelRequest :

代码语言:javascript
复制
message WatchCancelRequest {
  // watch_id 是要取消的观察者的id,这样就不再有更多事件传播过来了。
  int64 watch_id = 1;
}

WatchResponse响应消息体:

代码语言:javascript
复制
message WatchResponse {
  ResponseHeader header = 1;
  // watch_id 是和应答相关的观察者的ID
  int64 watch_id = 2;
  // 如果应答是用于创建观察者请求的,则 created 设置为 true。
  // 客户端应该记录 watch_id 并期待从同样的流中为创建的观察者接收事件。
  // 所有发送给被创建的观察者的事件将附带同样的 watch_id
  bool created = 3;
  // 如果应答是用于取消观察者请求的,则 canceled 设置为true。
  // 不会再有事件发送给被取消的观察者。
  bool canceled = 4;
  // compact_revision 被设置为最小 index,如果观察者试图观察被压缩的 index。
  // 当在被压缩的修订版本上创建观察者或者观察者无法追上键值对存储的进展时发生。
  // 客户端应该视观察者为被取消,并不应该试图再次创建任何带有相同 start_revision 的观察者。
  int64 compact_revision  = 5;
  // cancel_reason 指出取消观察者的理由.
  string cancel_reason = 6;
  repeated mvccpb.Event events = 11;
}

mvccpb.Event 的消息体:

代码语言:javascript
复制
message Event {
  enum EventType {
    PUT = 0;
    DELETE = 1;
  }
  // type 是事件的类型。
  // 如果类型是 PUT,表明新的数据已经存储到 key。
  // 如果类型是 DELETE, 表明 key 已经被删除。
  EventType type = 1;
  // kv 为事件持有 KeyValue。
  // PUT 事件包含当前的kv键值对
  // kv.Version=1 的 PUT 事件表明 key 的创建
  // DELETE/EXPIRE 事件包含被删除的 key,它的修改修订版本设置为删除的修订版本
  KeyValue kv = 2;
  // prev_kv 持有在事件发生前的键值对
  KeyValue prev_kv = 3;
}

Lease service

Lease service 提供租约的支持,在 rpc.proto 文件中 Lease service 定义如下:

代码语言:javascript
复制
service Lease {
  // LeaseGrant 创建一个租约,当服务器在给定 time to live 时间内没有接收到 keepAlive 时租约过期。
  // 如果租约过期则所有附加在租约上的key将过期并被删除。
  // 每个过期的key在事件历史中生成一个删除事件。
  rpc LeaseGrant(LeaseGrantRequest) returns (LeaseGrantResponse) {}
  // LeaseRevoke 撤销一个租约。
  // 所有附加到租约的key将过期并被删除。
  rpc LeaseRevoke(LeaseRevokeRequest) returns (LeaseRevokeResponse) {}
  // LeaseKeepAlive 通过从客户端到服务器端的流化的 keep alive 请求和从服务器端到客户端的流化的 keep alive 应答来维持租约.
  rpc LeaseKeepAlive(stream LeaseKeepAliveRequest) returns (stream LeaseKeepAliveResponse) {}
  // LeaseTimeToLive 获取租约信息。
  rpc LeaseTimeToLive(LeaseTimeToLiveRequest) returns (LeaseTimeToLiveResponse) {}
}
  • 其中LeaseGrant 方法创建一个租约:
代码语言:javascript
复制
message LeaseGrantRequest {
  // TTL 是建议的以秒为单位的 time-to-live
  int64 TTL = 1;
  // ID 是租约的请求ID。如果ID设置为0, 则出租人(也就是etcd server)选择一个ID。
  int64 ID = 2;
}
代码语言:javascript
复制
message LeaseGrantResponse {
  ResponseHeader header = 1;
  // ID 是承认的租约的ID
  int64 ID = 2;
  // TTL 是服务器选择的以秒为单位的租约time-to-live
  int64 TTL = 3;
  string error = 4;
}
  • LeaseRevoke 方法取消一个租约:
代码语言:javascript
复制
message LeaseRevokeRequest {
  // ID是要取消的租约的ID。
  // 当租约被取消时,所有关联的key将被删除
  int64 ID = 1;
}
代码语言:javascript
复制
message LeaseRevokeResponse {
  ResponseHeader header = 1;
}
  • LeaseKeepAlive 方法维持一个租约:
代码语言:javascript
复制
message LeaseKeepAliveRequest {
  // ID 是要继续存活的租约的 ID
  int64 ID = 1;
}
代码语言:javascript
复制
message LeaseKeepAliveResponse {
  ResponseHeader header = 1;
  // ID 是从继续存活请求中得来的租约 ID
  int64 ID = 2;
  // TTL是租约新的 time-to-live
  int64 TTL = 3;
}
  • LeaseTimeToLive 方法获取租约的信息:
代码语言:javascript
复制
message LeaseTimeToLiveRequest {
  // ID 是租约的 ID.
  int64 ID = 1;
  // keys 设置为 true 可以查询附加到这个租约上的所有 key
  bool keys = 2;
}
代码语言:javascript
复制
message LeaseTimeToLiveResponse {
  ResponseHeader header = 1;
  // ID 是来自请求的 ID.
  int64 ID = 2;
  // TTL 是租约剩余的 TTL,单位为秒;租约将在接下来的 TTL + 1 秒之后过期
  int64 TTL = 3;
  // GrantedTTL 是租约创建/续约时初始授予的时间,单位为秒
  int64 grantedTTL = 4;
  // keys 是附加到这个租约的 key 的列表
  repeated bytes keys = 5;
}

Lock service

Lock service 提供分布式共享锁的支持,在v3lock.proto 中 Lock service 定义如下:

代码语言:javascript
复制
// Lock service 以 gRPC 接口的方式暴露客户端锁机制。
service Lock {
  // 在给定命令锁上获得分布式共享锁。
  // 成功时,将返回一个唯一 key,在调用者持有锁期间会一直存在。
  // 这个 key 可以和事务一起工作,以安全的确保对 etcd 的更新仅仅发生在持有锁时。
  // 锁被持有直到在 key 上调用解锁或者和所有者关联的租约过期。
  rpc Lock(LockRequest) returns (LockResponse) {}
  // Unloke 使用 Lock 返回的 key 并释放对锁的持有。
  // 下一个在等待这个锁的 Lock 的调用者将被唤醒并给予锁的所有权。
  rpc Unlock(UnlockRequest) returns (UnlockResponse) {}
}
  • Lock方法用于在给定命令锁上获得分布式共享锁,其中LockRequest请求消息体如下:
代码语言:javascript
复制
message LockRequest {
  // name 是要获取的分布式共享锁的标识
  bytes name = 1;
  // lease 是将要附加到锁所有权的租约的 ID。如果租约过期或者撤销时正持有锁,则锁将自动释放。
  // 使用相同的租约调用锁将视为单次获取;使用同样租约的第二次锁定将是空操作。
  int64 lease = 2;
}

应答的信息体 LockResponse:

代码语言:javascript
复制
message LockResponse {
  etcdserverpb.ResponseHeader header = 1;
  // key 是在 Lock 调用者拥有锁期间存在于 etcd 上的 key。
  // 用户不可以修改这个 key,否者锁将不能正常工作
  bytes key = 2;
}
  • Unloke 使用 Lock 返回的 key 并释放锁,其中UnlockRequest请求消息体如下:
代码语言:javascript
复制
message UnlockRequest {
  // key 是通过 Lock 方法得到的锁所有权 key
  bytes key = 1;
}

应答的信息体UnlockResponse:

代码语言:javascript
复制
message UnlockResponse {
  etcdserverpb.ResponseHeader header = 1;
}

Election service

Election service 提供观察键值对变化的支持,在 v3election.proto 中 Election service 定义如下:

代码语言:javascript
复制
// Election service 以 gRPC 接口的方式暴露客户端选举机制。
service Election {
  // Campaign 等待获得选举的领导地位,如果成功返回 LeaderKey 代表领导地位。
  // 然后 LeaderKey 可以用来在选举时发起新的值,在依然持有领导地位时事务性的守护 API 请求,
  // 还有从选举中辞职。
  rpc Campaign(CampaignRequest) returns (CampaignResponse) {}
  // Proclaim 用新值更新领导者的旧值
  rpc Proclaim(ProclaimRequest) returns (ProclaimResponse) {}
  // Leader 返回当前的选举公告,如果有。
  rpc Leader(LeaderRequest) returns (LeaderResponse) {}
  // Observe 以流的方式返回选举公告,和被选举的领导者发布的顺序一致。
  rpc Observe(LeaderRequest) returns (stream LeaderResponse) {}
  // Resign 放弃选举领导地位,以便其他参选人可以在选举中获得领导地位。
  rpc Resign(ResignRequest) returns (ResignResponse) {}
}
  • 其中Campaign 方法用于参加选举以期获得领导地位:
代码语言:javascript
复制
message CampaignRequest {
  // name 是选举的标识符,用来参加竞选
  bytes name = 1;
  // lease is the ID of the lease attached to leadership of the election. If the
  // lease expires or is revoked before resigning leadership, then the
  // leadership is transferred to the next campaigner, if any.
  // lease 是附加到选举领导地位的租约的ID。如果租约过期或者在放弃领导地位之前取消,
  // 则领导地位转移到下一个竞选者,如果有。
  int64 lease = 2;
  // value 是竞选者赢得选举时设置的初始化公告值。
  bytes value = 3;
}
代码语言:javascript
复制
message CampaignResponse {
  etcdserverpb.ResponseHeader header = 1;
  // leader 描述用于持有选举的领导地位的资源
  LeaderKey leader = 2;
}

其中LeaderKey 消息体的内容如下:

代码语言:javascript
复制
message LeaderKey {
  // name 是选举标识符,和领导地位 key 对应
  bytes name = 1;
  // key 是不透明的 key ,代表选举的领导地位。
  // 如果 key 被删除,则领导地位丢失
  bytes key = 2;
  // rev 是 key 的创建修订版本。它可以用来在事务期间测验选举的领导地位,通过测验 key 的创建修订版本匹配 rev
  int64 rev = 3;
  // lease 是选举领导者的租约 ID
  int64 lease = 4;
}
  • Proclaim 方法用于更新值:
代码语言:javascript
复制
message ProclaimRequest {
  // leader 是在选举上持有的领导地位
  LeaderKey leader = 1;
  // value 是打算用于覆盖领导者当前值的更新。
  bytes value = 2;
}
代码语言:javascript
复制
message ProclaimResponse {
  etcdserverpb.ResponseHeader header = 1;
}
  • Leader 方法用于信息查询:
代码语言:javascript
复制
message LeaderRequest {
  // name 是选举标识符,用于查询领导地位信息的
  bytes name = 1;
}
代码语言:javascript
复制
message LeaderResponse {
  etcdserverpb.ResponseHeader header = 1;
  // kv 是键值对,体现最后的领导者更新
  mvccpb.KeyValue kv = 2;
}

其中mvccpb.KeyValue 来自 kv.proto,消息体定义为:

代码语言:javascript
复制
message KeyValue {
  // key 是 bytes 格式的 key。不容许 key 为空。
  bytes key = 1;
  // create_revision 是这个 key 最后一次创建的修订版本
  int64 create_revision = 2;
  // mod_revision 是这个 key 最后一次修改的修订版本
  int64 mod_revision = 3;
  // version 是 key 的版本。删除会重置版本为0,而任何 key 的修改会增加它的版本。
  int64 version = 4;
  // value 是 key 持有的值,bytes 格式。
  bytes value = 5;
  // lease 是附加给 key 的租约 id。
  // 当附加的租约过期时,key 将被删除。
  // 如果 lease 为0,则没有租约附加到 key。
  int64 lease = 6;
}
  • Observe 方法是Leader 方法的stream 形式,消息体和Leader 方法相同,都是 LeaderRequest和LeaderResponse。
  • Resign方法用于放弃选举领导地位:
代码语言:javascript
复制
message ResignRequest {
  // leader 是要放弃的领导地位
  LeaderKey leader = 1;
}
代码语言:javascript
复制
message ResignResponse {
  etcdserverpb.ResponseHeader header = 1;
}

参考:

etcd doc

https://etcd.io/docs/v3.5/

还不了解etcd?一文带你快速入门

https://mp.weixin.qq.com/s/f9loVQr7jbjnDKqRWXz8sQ

彻底搞懂etcd系列文章(一):初识etcd

https://mp.weixin.qq.com/s/cD9NZ3vPIY9nF8jGTgT7Cw

Etcd官方文档

https://www.bookstack.cn/read/etcd/documentation-dev-guide-embed_etcd.md

the end

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-05-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 才浅coding攻略 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
对象存储
对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档