21

彻底搞懂 etcd 系列文章(八):etcd 事务 API

 3 years ago
source link: http://blueskykong.com/2020/09/09/etcd-8/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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

《彻底搞懂 etcd 系列文章》将会从 etcd 的基本功能实践、API 接口、实现原理、源码分析,以及实现中的踩坑经验等几方面具体展开介绍 etcd。预计会有 20 篇左右的文章,笔者将会每周持续更新,欢迎关注。

1 etcd 的事务 Transaction

事务是键值存储中的原子 If/Then/Else 结构体。etcd 提供了原语,用于将请求按原子块(即then/else)分组在一起,这些原子块(即分组)根据键值存储的内容来保护执行(即if)。事务可用于实现并发更新的一致性,构建 CAS 以及开发级别的并发控制。

事务可以使得 etcd 服务端在单个请求中自动处理多个外部请求。对于键值存储库的修改,这意味着该存储库的修订版仅对事务增加一次,并且该事务生成的所有事件都将具有相同的修订版。需要注意的是,禁止在单个事务中多次修改同一 key。

事务中的每个比较都会检查存储中的单个 key,类似于 If 操作,检查是否存在值,与给定值进行比较或检查键的修订或版本。两种不同的比较可能适用于相同或不同的 key。所有比较都是原子操作。如果所有比较都为真,则表示事务成功,而 etcd 则应用事务的 then/success 请求块,否则,则认为失败并应用 else/failure 请求块。

2 Txn 的定义

Txn 方法在单个事务中处理多个请求。txn 请求增加键值存储的修订版本并为每个完成的请求生成带有相同修订版本的事件。etcd 不容许在一个 txn 中多次修改同一个 key。

rpc Txn(TxnRequest) returns (TxnResponse) {}

以下这段内容来自 google paxosdb 论文 proto 文件中 TxnRequest 的注释,解释了 Txn 请求的工作方式:

我们的实现围绕强大的我们称为 MultiOp 的原生(primitive)。所有除了循环外的其他数据库操作被实现为对 MultiOp 的单一调用。MultiOp 被原子性的应用并由三个部分组成:

  • 被称为guard的测试列表。在guard中每个测试检查数据库中的单个项(entry)。它可能检查某个值的存在或者缺失,或者和给定的值比较。在guard中两个不同的测试可能应用于数据库中相同或者不同的项。guard中的所有测试被应用然后 MultiOp 返回结果。如果所有测试是true,MultiOp 执行 t 操作 (见下面的第二项), 否则它执行 f 操作 (见下面的第三项)。
  • 被称为 t 操作的数据库操作列表。列表中的每个操作是插入,删除,或者查找操作,并应用到单个数据库项。列表中的两个不同操作可能应用到数据库中相同或者不同的项。如果 guard 评价为true 这些操作将被执行
  • 被称为 f 操作的数据库操作列表。类似 t 操作, 但是是在 guard 评价为 false 时执行。

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

message TxnRequest {
  // compare 是断言列表,体现为条件的联合
  repeated Compare compare = 1;

  // 成功请求列表,当比较评估为 true 时将被应用。
  repeated RequestOp success = 2;

  // 失败请求列表,当比较评估为 false 时将被应用。
  repeated RequestOp failure = 3;
}

compare 如果比较成功,那么成功请求将被按顺序处理,而应答将按顺序包含他们对应的应答;如果比较失败,那么失败请求将被按顺序处理,而应答将按顺序包含他们对应的应答。应答的消息体 TxnResponse 定义如下:

message TxnResponse {
  ResponseHeader header = 1;

  // 如果比较评估为true则succeeded被设置为true,否则是false
  bool succeeded = 2;

  repeated ResponseOp responses = 3;
}

header 代表通用的响应头。responses 为应答列表,如果 succeeded 为 true 则对应成功请求,如果 succeeded 是 false 则对应失败请求。Compare 消息体:

message Compare {
  enum CompareResult {
    EQUAL = 0;
    GREATER = 1;
    LESS = 2;
    NOT_EQUAL = 3;
  }
  enum CompareTarget {
    VERSION = 0;
    CREATE = 1;
    MOD = 2;
    VALUE= 3;
  }

  CompareResult result = 1;

  CompareTarget target = 2;

  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;
  }
}

result 是这个比较的逻辑比较操作。target 是比较要检查的键值字段。key 是用于比较操作的主题 key。在 .proto 中定义一个oneof 关键字后跟着oneof 名称,设置 oneof 字段将自动清除oneof字段的所有其他成员。在生成的代码中,oneof 字段具有与常规字段相同的 setter 和 getter 方法。

RequestOp 消息体定义如下:

message RequestOp {
  oneof request {
    RangeRequest request_range = 1;
    PutRequest request_put = 2;
    DeleteRangeRequest request_delete_range = 3;
  }
}

request 是可以被事务接受的请求类型的联合。ResponseOp 消息体:

message ResponseOp {
  oneof response {
    RangeResponse response_range = 1;
    PutResponse response_put = 2;
    DeleteRangeResponse response_delete_range = 3;
  }
}

response 是事务返回的应答类型的集合。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK