Etcd教程 — 第六章 Etcd之核心API V3

前言

Etcd是云原生架构中重要的基础组件,由CNCF孵化托管。Etcd在微服务Kubernates集群中不仅可以作为服务注册与发现,还可以作为key-value存储的中间件

本文介绍的Etcd3 API的核心设计,主要针对常见的API接口服务。对于理解Etcd基本思想有很大的帮助。所有Etcd3 API均在 gRPC服务中定义,该服务对Etcd服务器可以理解的远程过程调用(RPC)进行分类。

1 Protocol Buffer

关于 Protocol Buffer 这里只是简单介绍了下,如果需要深入了解,请自行百度。同时在另一篇文章:gRPC教程 — 第一章 2、3、4 中也有部分protocol buffers的介绍。

etcd v3 的通信基于 gRPC,proto 文件是定义服务端客户端通讯接口的标准。即客户端该传什么样的参数,服务端该返回什么样子的参数,客户端该怎么调用,是阻塞还是非阻塞,是同步还是异步。在进行核心 API 的学习之前,gRPC 推荐使用 proto3,我们需要对 proto3 的基本语法有初步的了解。proto3 是原有 Protocol Buffer 2(被称为 proto2)的升级版本,删除了一部分特性,优化了对移动设备的支持,另外增加了对android和ios的支持,使得 gRPC 可以顺利的在移动设备上使用。

1.1 Protocol Buffer 是什么

Protocol buffer是一个灵活、高效、自动化的结构化数据序列化机制–类似于 xml,但是更小、更快并且更简单。一旦定义好数据如何构造,就可以使用特殊生成的源代码轻松地在各种数据流中使用各种语言编写和读取结构化数据。甚至可以更新之前定义的数据结构而不打破已部署的使用”旧有”格式编译的程序。

1.2 定义消息类型

定义一个搜索请求消息,每个消息都包含一个查询字符串,和你感兴趣的特定页面编号,以及每个页面的命中个数。

syntax = "proto3";

message SearchRequest {
  string query = 1; //查询条件
  int32 page_number = 2; //页码
  int32 result_per_page = 3; //希望返回的页码
}
  1. 文件的第一行定义了使用的是 proto3 的语法:如果你不指定,Protocol Buffer编译器就会认为你使用的是proto2的语法。这个语句必须出现在.proto文件的非空非注释的第一行。

  2. 搜索请求消息结构中定义指定了三个字段(name/value pairs)。每个字段都有一个名称和类型。

1.3 指定字段类型

在上述例子中,所有的字段都是值类型:两个整形(page_number and result_per_page) 和一个字符串 (query)。然而,你也可以指定你的字段的组合类型,包括枚举和其他消息类型。

1.4 分配标识 — tag

消息中的每个字段都有一个唯一的数字标识。这些标识用来在消息的二进制格式中识别字段,并且,一旦定义的消息投入使用,这些数字标识就不应该再被修改。

注意,标识是由1到15使用一个字节来编码,包括标识数字和字段类型(你可以在Protocol Buffer 编码中查看更多详细)。

标识16到2047占用两个字节。所以应该保留1到15,用作出现最频繁的消息类型的标识。记得为将来会继续增加并可能频繁出现的元素留一点儿标识区间,也就是说,不要一下子把1—15全部用完,为将来预留一点儿。

可以指定的最小的标识数字是1,最大是228,或者536,870,911。也不能使用19000 到 19999之间的数字,因为它们被Protocol Buffers保留使用——如果在.proto文件中使用了一个保留数字,Protocol Buffers编译器将会提示。同样的,不能使用任何之前保留的标识,因为标识的作用就是标定唯一性,如果重复使用则会发生冲突。

1.5 指定字段规则

消息字段可以是下边中的一种:

  1. singular(单个):符合语法规则的消息包含零个或者一个这样的字段(最多一个)。
  2. repeated(重复): 一个字段在合法的消息中可以重复出现一定次数(包括零次)。重复出现的值的次序将被保留。在proto3中,重复出现的值类型字段默认采用压缩编码。

注意:可以观察一下repeated定义的字段生成go后对应的字段类型及名称,会发现是切片类型,因为重复就意味着多个相同的数据,还要存到同一个字段中,所以切片类型符合。

1.6 添加更多消息类型

多个消息类型可以定义在一个.proto文件中。这个在定义多个关联的消息的时候非常有用,例如,如果想定义搜索消息类型的响应消息格式,可以在同一个.proto文件中添加如下的内容:

message SearchRequest {
  string query = 1;
  int32 page_number = 2;
  int32 result_per_page = 3;
}

message SearchResponse {
 ...
}

1.7 添加注释

在 .proto 文件中添加注释:

message SearchRequest {
  string query = 1;
  int32 page_number = 2;  // Which page number do we want?
  int32 result_per_page = 3;  // Number of results to return per page.
}

1.8 保留字段

如果是通过删除整个字段更新了消息类型,或者将整个字段其注释掉,未来在编写新的类型的时候能够复用这些注释掉的标识数字。然而,这会引起一些严重的问题,如果后来加载了同一个.proto的旧版,包括数据损坏,安全隐私bug等等。一个确保这种问题不会发生的办法是,保留要删除的字段的标识。Protocol buffer 编译器将会提示以后用户使用这些保留的字段标识。

message Foo {
  reserved 2, 15, 9 to 11;
  reserved "foo", "bar";
}

注意:不要混淆同一个保留语句中的字段名称和标识。

1.9 值类型

值类型的消息字段可以是一下类型中的一种——这个表格展示了可以在.proto文件中使用的类型,以及自动生成的相应语言的类型:

在这里插入图片描述

1.10 proto文件编译

一个 .proto 文件的编译之后,编译器会为你选择的语言生成代码。你在文件中描述的消息类型,包括获取和设置字段的值,序列化你的消息到一个输出流,以及从一个输入流中转换出你的消息。

  • 对于 C++,编译器会为每个 .proto 文件生成一个 .h 和一个 .cc 的文件,为每一个给出的消息类型生成一个类。
  • 对于 Java,编译器会生成一个java文件,其中为每一个消息类型生成一个类,还有特殊的用来创建这些消息类实例的Builder类,
  • Python编译器生成一个模块,其中为每一个消息类型生成一个静态的描述器,在运行时,和一个 metaclass 一起使用来创建必要的 Python 数据访问类。
  • 对于 Go,编译器为每个消息类型生成一个 .pb.go 文件。

2 gRPC 服务

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

  • 处理 Etcd 键值的重要服务包括:
  • KV 服务,创建,更新,获取和删除键值对。
  • 监视,监视键的更改。
  • 租约,消耗客户端保持活动消息的基元。
  • 锁,etcd 提供分布式共享锁的支持。
  • 选举,暴露客户端选举机制。
    在这里插入图片描述

2.1 请求

etcd3 中的所有 RPC 都遵循相同的格式。每个 RPC 都有一个函数名,该函数将 NameRequest 作为参数并返回 NameResponse 作为响应。例如,这是 Range RPC 描述:

service KV {
  Range(RangeRequest) returns (RangeResponse)
  ...
}

2.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。
  • Revision : 产生响应时键值存储的修订版本号。
  • Raft_Term : 产生响应时,成员的 Raft 称谓。

应用服务可以通过 Cluster_ID 和 Member_ID 字段来分辨当前与之通信的正是预期的那个集群或者成员。

应用服务可以使用修订号字段来知悉当前键值存储库最新的修订号。当应用程序指定历史修订版进行查询并希望在请求时知道最新修订版时,此功能特别有用。

应用服务可以使用 Raft_Term 来检测集群何时完成一个新的 leader 选举。

3 键值对服务

3.1 KV service 定义

大多数对Etcd的请求通常是键值请求。KV service提供对键值对操作的支持。在 rpc.proto 文件中 KV service 定义如下:

//rpc.proto
service KV {
  rpc Range(RangeRequest) returns (RangeResponse) {}

  rpc Put(PutRequest) returns (PutResponse) {}

  rpc DeleteRange(DeleteRangeRequest) returns (DeleteRangeResponse) {}

  rpc Txn(TxnRequest) returns (TxnResponse) {}

  rpc Compact(CompactionRequest) returns (CompactionResponse) {}
}

在这里插入图片描述
各个函数的介绍如下:

  • Range:从键值存储中获取范围内的key;
  • Put:设定key-value到Etcd键值存储,put 请求增加键值存储的修订版本并在事件历史中生成一个事件;
  • DeleteRange:从键值存储中删除给定范围,删除请求增加键值存储的修订版本,并在事件历史中为每个被删除的key生成一个删除事件;
  • Txn:在单个事务中处理多个请求,一个 txn请求增加键值存储的修订版本并为每个完成的请求生成带有相同修订版本的事件。不容许在一个txn中多次修改同一个key;
  • Compact:压缩在etcd键值存储中的事件历史。键值存储应该定期压缩,否则事件历史会无限制的持续增长。

下面我们具体分析每个接口方法。

3.2 Range方法

Range 方法从键值存储中获取范围内的 key,定义如下:

rpc Range(RangeRequest) returns (RangeResponse) {}

需要注意的是没有操作单个 key 的方法,即使是存取单个 key,也需要使用 Range 方法。

请求的消息体 RangeRequest 如下所示:

message RangeRequest {
  enum SortOrder {
    NONE = 0; // 默认,不排序
    ASCEND = 1; // 正序,低的值在前
    DESCEND = 2; // 倒序,高的值在前
  }
  enum SortTarget {
    KEY = 0;
    VERSION = 1;
    CREATE = 2;
    MOD = 3;
    VALUE = 4;
  }

  bytes key = 1;

  // range_end 是请求范围的上限[key, range_end)
  bytes range_end = 2;

  // 请求返回的key的数量限制
  int64 limit = 3;

  int64 revision = 4;

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

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

  bool serializable = 7;

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

  // 设置仅仅返回范围内key的数量
  bool count_only = 9;
}
  1. 在上面定义的请求结构体中,需要注意 key 是 range 的第一个 key【字段】
  2. range_end 如果没有给定,请求仅查找这个 key。range_end 代表请求的上限,如果 range_end 是 ‘\0’,范围是大于等于 key 的所有key;如果 range_end 比给定的 key 长一个 bit,那么 range 请求获取所有带有前缀(给定的 key)的 key;如果 key 和 range_end 都是’\0’,则范围查询返回所有key。
  3. revision 修订版本作于 range 键值对存储的时间点。如果 revision 小于或等于零,范围是在最新的键值对存储上。如果修订版本已经被压缩,返回 ErrCompacted 作为应答。
  4. serializable 设置 range 请求使用串行化成员本地读。range 请求默认是线性化的,线性化请求相比串行化请求有更高的延迟和低吞吐量,但是反映集群当前的一致性。为了更好的性能,以可能脏读为代价,串行化 range 请求在本地处理,无需和集群中的其他节点达到一致。

应答的消息体 RangeResponse 定义如下:

message RangeResponse {
  ResponseHeader header = 1;

  // kvs是匹配范围请求的键值对列表
  // 当请求数量时是空的
  repeated mvccpb.KeyValue kvs = 2;

  // more代表在被请求的范围内是否还有更多的 key
  bool more = 3;

  // 被请求范围内 key 的数量
  int64 count = 4;
}

header 就是在前面小结提及的通用响应头。其中 mvccpb.KeyValue 的消息体定义如下:

message KeyValue {
  // key 是 bytes 格式的 key。不容许 key 为空
  bytes key = 1;

  // create_revision 是这个 key 最后创建的修订版本
  int64 create_revision = 2;

  // mod_revision 是这个 key 最后修改的修订版本
  int64 mod_revision = 3;

  int64 version = 4;

  // value 是 key 持有的值,bytes 格式。
  bytes value = 5;

  int64 lease = 6;
}

version 是 key 的版本。删除会重置版本为 0,任何 key 的修改会增加它的版本。lease 是附加给 key 的租约 id。当附加的租约过期时,key 将被删除。如果 lease 为 0,则没有租约附加到 key。

3.3 Put 方法

Put 方法,用于存储给定 key-value 到数据库。Put 方法增加键值存储的修订版本并在事件历史中生成一个事件。

rpc Put(PutRequest) returns (PutResponse) {}

请求的消息体是 PutRequest:

message PutRequest {
  // byte数组形式的key,用来放置到键值对存储
  bytes key = 1;

  // byte 数组形式的 value,在键值对存储中和 key 关联
  bytes value = 2;

  int64 lease = 3;

  bool prev_kv = 4;
}

lease,在键值存储中和 key 关联的租约 id,0 代表没有租约。如果 prev_kv 被设置,etcd 获取改变之前的上一个键值对。上一个键值对将在 put 应答中被返回。

应答的消息体 PutResponse 定义如下:

message PutResponse {
  ResponseHeader header = 1;

  mvccpb.KeyValue prev_kv = 2;
}

header 代表通用的响应头,如果请求中的 prev_kv 被设置,将会返回上一个键值对。

3.4 DeleteRange 方法

DeleteRange 方法从键值存储中删除给定范围。删除请求增加键值存储的修订版本并在事件历史中为每个被删除的key生成一个删除事件。

rpc DeleteRange(DeleteRangeRequest) returns (DeleteRangeResponse)

请求的消息体 DeleteRangeRequest 定义如下:

message DeleteRangeRequest { 
  bytes key = 1;

  bytes range_end = 2;

  bool prev_kv = 3;
}
  • key 代表要删除的范围的开始。
  • range_end 是要删除范围[key, range_end)的最后一个key。如果 range_end 没有给定,范围定义为仅包含key参数;如果 range_end 是 ‘\0’, 范围是所有大于等于参数key的所有key。
  • 如果 prev_kv 被设置,etcd 获取删除之前的上一个键值对。上一个键值对将在 delete 应答中被返回。应答的消息体

DeleteRangeResponse 定义如下:

message DeleteRangeResponse {
  ResponseHeader header = 1;

  // 被范围删除请求删除的key的数量
  int64 deleted = 2;

  repeated mvccpb.KeyValue prev_kvs = 3;
}

如果请求中的 prev_kv 被设置,将会返回上一个键值对。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值