分布式学习(7)etcd@3@ API v3 gRPC_事务,租约

一、ETCD 之 事务

在etcdv3中,事务就是一个原子的、针对key-value存储操作的If / Then / Else 结构,事务提供了一个原语,用于请求归并到一起放在原子块中,例如then/else,这些原子块的执行条件,例如if以key-value存储里的内容为依据。事务可以用来保护key不受其他并发更新操作的修改,也可以构建CAS(Compare And Swap)操作,并以此作为更高层次(应用层)并发控制的基础。在一个事务请求中,etcd可以自动处理多个普通的请求。若对于那些修改key-value存储的请求,若用同一个事物所有操作产生的事件都拥有同样的revesion。然而,在一个事务中多次修改同一个key是被禁止的。

所有的事务都由一个比较“连接”来守护,类似于“if”声明。每个比较会检查后台存储的一个key。这个检查可以是如下内容:该key在后台存储是否有value?该key的value是否等同于某个给定的值?除了value,还可以检查这个key的revision或者version。有的比较都是原子的执行的。如果所有的比较都返回true,那么就说该事物成功了,并且会执行该事务success请求块里面的操作,反之则代表该事务失败了,转而执行该事务failure请求块里面的操作。

一个事务由一个  比较块  和  比较后的操作  两个部分组成,数据结构如下:

# Request
message TxnRequest {
  repeated Compare compare = 1;
  repeated RequestOp success = 2;
  repeated RequestOp failure = 3;
}
# Response
message TxnResponse {
  ResponseHeader header = 1;
  bool succeeded = 2;
  repeated ResponseOp responses = 3;
}

所有的比较都由下面的message表示:

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;
  // target is the key-value field to inspect for the comparison.
  CompareTarget target = 2;
  // key is the subject key for the comparison operation.
  bytes key = 3;
  oneof target_union {
    int64 version = 4;
    int64 create_revision = 5;
    int64 mod_revision = 6;
    bytes value = 7;
  }
}

在处理完上述比较块后会进行下一步的操作,比如说CRUD,如下所示:

message RequestOp {
  // request is a union of request types accepted by a transaction.
  oneof request {
    RangeRequest request_range = 1;
    PutRequest request_put = 2;
    DeleteRangeRequest request_delete_range = 3;
  }
}

二、ETCD 之 watch API

watch api 提供了基于事件的接口,用于异步检测key的变换,etcd v3的watch机制会针对某个key 下的某个特定revision进行连续的检测,等待key发生变化后将更新信息返回给client

每个变换的数据结构都如下所示:

message Event {
  enum EventType {
    PUT = 0;
    DELETE = 1;
  }
  EventType type = 1;
  KeyValue kv = 2;
  KeyValue prev_kv = 3;
}

watch是长期持续的操作,并且它使用gRPC中的流的方式进行传输Event数据,(这里的流是双向流);一个方面,client通过写入流来创建watch,另一个方面,client通过读取流来接受到watch的Event,单个watch流可以通过使用pre-watch标志Event,以达到在一个流中多路传输多个不同的watch event的目的,如下是request和response的数据结构

# Request
message WatchCreateRequest {
  bytes key = 1;
  bytes range_end = 2;
  int64 start_revision = 3;
  bool progress_notify = 4;

  enum FilterType {
    NOPUT = 0;
    NODELETE = 1;
  }
  repeated FilterType filters = 5;
  bool prev_kv = 6;
}

# Response 
message WatchResponse {
  ResponseHeader header = 1;
  int64 watch_id = 2;
  bool created = 3;
  bool canceled = 4;
  int64 compact_revision = 5;

  repeated mvccpb.Event events = 11;
}

三、ETCD 之 Lease API

租约是一种检测客户端活跃度的机制,Lease机制的应用比较广泛,如用于授权进行同步等操作,用于分布式锁等场景。租约是由生存限制的,集群为租约授予一个TTL,当key被授予某个Lease时,它的生存时间为Lease的生存时间。Lease的实际TTL值不低于最小的TTL,而该最小值是由etcd集群选择的。当Lease的TTL到期时,所有与之关联的key都将被删除。如果在TTL结束前没有收到租约及KeepAlive消息来维持租约,那么该租约将过期。etcd3所支持的Lease机制可以为etcd集群中的某个key或者多个key所关联,一个key最多关联一个Lease。

租约request和租约过期收到的response,以及撤销租约的数据结构如下:

# request
message LeaseGrantRequest {
  int64 TTL = 1;
  int64 ID = 2;
}

# response
message LeaseGrantResponse {
  ResponseHeader header = 1;
  int64 ID = 2;
  int64 TTL = 3;
}

# 撤销
message LeaseRevokeRequest {
  int64 ID = 1;
}

下面时租约,KeepAlive的数据结构:

# request
message LeaseKeepAliveRequest {
  int64 ID = 1;
}

# response
message LeaseKeepAliveResponse {
  ResponseHeader header = 1;
  int64 ID = 2;
  int64 TTL = 3;
}

四、ETCD 之 gRPC gateway & pure python 

#watch
curl http://localhost:2379/v3alpha/watch \
        -X POST -d '{"create_request": {"key":"Zm9v"} }' &
# {"result":{"header":{"cluster_id":"12585971608760269493","member_id":"13847567121247652255","revision":"1","raft_term":"2"},"created":true}}
curl -L http://localhost:2379/v3alpha/kv/put \
    -X POST -d '{"key": "Zm9v", "value": "YmFy"}' >/dev/null 2>&1
# {"result":{"header":{"cluster_id":"12585971608760269493","member_id":"13847567121247652255","revision":"2","raft_term":"2"},"events":[{"kv":{"key":"Zm9v","create_revision":"2","mod_revision":"2","version":"1","value":"YmFy"}}]}}

# 事务
curl -L http://localhost:2379/v3alpha/kv/txn \
    -X POST \
    -d '{"compare":[{"target":"CREATE","key":"Zm9v","createRevision":"2"}],"success":[{"requestPut":{"key":"Zm9v","value":"YmFy"}}]}'
# {"header":{"cluster_id":"12585971608760269493","member_id":"13847567121247652255","revision":"3","raft_term":"2"},"succeeded":true,"responses":[{"response_put":{"header":{"revision":"3"}}}]}
# 事务

from etcd3 import Client
client = Client('127.0.0.1', 12379)
#事务 start
txn = client.Txn()
txn.compare(txn.key('foo').value == 'bar')
txn.success(txn.put('foo2', 'bar2'))
txn.commit()
#事务 end
print(client.range('foo2').kvs)


#租约
from etcd3 import Client
client = Client('127.0.0.1', 12379)
myleaseId = 303030
mylease = client.lease_grant(30, myleaseId)
client.put('foo3', 'bar3', myleaseId)
lease_info = client.lease_time_to_live(myleaseId)
# lease_keep_alive(myleaseId)
print(lease_info)

猜你喜欢

转载自blog.csdn.net/qq_36336522/article/details/105719420