Etcd教程 — 第八章 Etcd之Compact、Watch和Lease API
1 Compact 压缩
Compact方法是压缩Etcd键值对存储中的事件历史。键值对存储应该定期压缩,否则事件历史会无限制的持续增长。
rpc Compact(CompactionRequest) returns (CompactionResponse) {}
请求的消息体是 CompactionRequest
, CompactionRequest
是压缩键值对存储到给定修订版本。所有修订版本比压缩修订版本小的键都将被删除:
message CompactionRequest {
// 键值存储的修订版本,用于比较操作
int64 revision = 1;
bool physical = 2;
}
physical 设置为 true 时 RPC 将会等待直到压缩物理性的应用到本地数据库,到这程度被压缩的项将完全从后端数据库中移除。应答的消息体 CompactionResponse 定义为:
message CompactionResponse {
ResponseHeader header = 1;
}
CompactionResponse 只有一个通用的响应头。
2 Watch 服务
Watch API 提供了一个基于事件的接口,用于异步监视键的更改。Etcd3监视程序通过从给定的修订版本(当前版本或历史版本)持续监视 key 更改,并将 key 更新流回客户端。
事件
每个键的更改都用事件消息表示。 事件消息会同时提供更新数据和更新类型,mvccpb.Event 的消息体定义如下:
message Event {
enum EventType {
PUT = 0;
DELETE = 1;
}
EventType type = 1;
KeyValue kv = 2;
// prev_kv 持有在事件发生前的键值对
KeyValue prev_kv = 3;
}
type 是事件的类型。如果类型是 PUT,表明新的数据已经存储到 key;如果类型是 DELETE, 表明 key 已经被删除。
kv 为事件持有 KeyValue。PUT 事件包含当前的 kv 键值对。kv.Version=1 的 PUT 事件表明 key 的创建。DELETE/EXPIRE 事件包含被删除的 key,它的修改修订版本设置为删除的修订版本。
监视流
Watch API 提供了一个基于事件的接口,用于异步监视键的更改。etcd 监视程序通过从给定的修订版本(当前版本或历史版本)连续监视来等待密钥更改,并将密钥更新流回客户端。
监视持续运行,并使用 gRPC 来流式传输事件数据。监视流是双向的,客户端写入流以建立监视事件,并读取以接收监视事件。单个监视流可以通过使用每个观察器标识符标记事件来复用许多不同的观察。这种多路复用有助于减少 etcd 群集上的内存占用量和连接开销。
Watch 事件具有如下三个特性:
- 有序,事件按修订顺序排序;如果事件早于已发布的事件,它将永远不会出现在列表上。
- 可靠,事件序列永远不会丢弃任何事件子序列;如果按时间顺序为 a < b < c 三个事件,那么如果 Watch 接收到事件 a 和 c,则可以保证接收到 b。
- 原子,保证事件清单包含完整的修订版;同一修订版中通过多个键进行的更新不会拆分为多个事件列表。
Watch service 定义
在 rpc.proto 中 Watch service 定义如下:
service Watch {
rpc Watch(stream WatchRequest) returns (stream WatchResponse) {}
}
Watch 观察将要发生或者已经发生的事件。输入和输出都是流;输入流用于创建和取消观察,而输出流发送事件。一个观察 RPC 可以在一次性在多个 key 范围上观察,并为多个观察流化事件。整个事件历史可以从最后压缩修订版本开始观察。
WatchService 只有一个 Watch 方法。
请求的消息体 WatchRequest 定义如下:
message WatchRequest {
oneof request_union {
WatchCreateRequest create_request = 1;
WatchCancelRequest cancel_request = 2;
}
}
request_union 要么是创建新的观察者的请求,要么是取消一个已经存在的观察者的请求。创建新的观察者的请求 WatchCreateRequest:
message WatchCreateRequest {
// key 是注册要观察的 key
bytes key = 1;
bytes range_end = 2;
// start_revision 是可选的开始(包括)观察的修订版本。不设置 start_revision 则表示 "现在".
int64 start_revision = 3;
bool progress_notify = 4;
enum FilterType {
// 过滤掉 put 事件
NOPUT = 0;
// 过滤掉 delete 事件
NODELETE = 1;
}
// 过滤器,在服务器端发送事件给回观察者之前,过滤掉事件。
repeated FilterType filters = 5;
// 如果 prev_kv 被设置,被创建的观察者在事件发生前获取上一次的KV。
// 如果上一次的KV已经被压缩,则不会返回任何东西
bool prev_kv = 6;
}
-
range_end 是要观察的范围 [key, range_end) 的终点。如果 range_end 没有设置,则只有参数 key 被观察;如果 range_end 等同于 ‘\0’, 则大于等于参数 key 的所有 key 都将被观察;如果 range_end 比给定 key 大1, 则所有以给定 key 为前缀的 key 都将被观察。
-
progress_notify 设置,这样如果最近没有事件,etcd 服务器将定期的发送不带任何事件的 WatchResponse 给新的观察者。当客户端希望从最近已知的修订版本开始恢复断开的观察者时有用。etcd 服务器将基于当前负载决定它发送通知的频率。
取消已有观察者的 WatchCancelRequest:
message WatchCancelRequest {
int64 watch_id = 1;
}
watch_id 是要取消的观察者的id,这样就不再有更多事件传播过来了。
应答的消息体 WatchResponse:
message WatchResponse {
ResponseHeader header = 1;
// watch_id 是和应答相关的观察者的ID
int64 watch_id = 2;
bool created = 3;
bool canceled = 4;
int64 compact_revision = 5;
// cancel_reason 指出取消观察者的理由.
string cancel_reason = 6;
repeated mvccpb.Event events = 11;
}
- 如果应答是用于创建观察者请求的,则 created 设置为 true。客户端应该记录 watch_id 并期待从同样的流中为创建的观察者接收事件。所有发送给被创建的观察者的事件将附带同样的 watch_id;如果应答是用于取消观察者请求的,则 canceled 设置为true。不会再有事件发送给被取消的观察者。
- compact_revision 被设置为最小 index,如果观察者试图观察被压缩的 index。当在被压缩的修订版本上创建观察者或者观察者无法追上键值对存储的进展时发生。客户端应该视观察者为被取消,并不应该试图再次创建任何带有相同 start_revision 的观察者。
3 Lease 服务
Lease service 提供租约的支持。Lease 是一种检测客户端存活状况的机制。集群授予具有生存时间的租约。如果Etcd集群在给定的 TTL 时间内未收到 keepAlive,则租约到期。
为了将租约绑定到键值上,每个 key 最多可以附加一个租约。当租约到期或被撤销时,该租约所附的所有 key 都将被删除。每个过期的密钥都会在事件历史记录中生成一个删除事件。
在 rpc.proto 中 Lease 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:获取租约信息
3.1 LeaseGrant 方法
LeaseGrant 方法创建一个租约。当服务器在给定 time to live 时间内没有接收到 keepAlive 时租约过期。如果租约过期则所有附加在租约上的 key 将过期并被删除。每个过期的 key 在事件历史中生成一个删除事件。
方法定义如下:
rpc LeaseGrant(LeaseGrantRequest) returns (LeaseGrantResponse) {}
请求的消息体是 LeaseGrantRequest:
message LeaseGrantRequest {
int64 TTL = 1;
int64 ID = 2;
}
TTL 建议以秒为单位的 time-to-live。ID 是租约的请求 ID,如果 ID 设置为 0,则出租人(也就是 etcd server)选择一个 ID。
应答的消息体 LeaseGrantResponse 定义如下:
message LeaseGrantResponse {
ResponseHeader header = 1;
int64 ID = 2;
int64 TTL = 3;
string error = 4;
}
ID 是承认的租约的 ID。TTL 是服务器选择的以秒为单位的租约 time-to-live。
3.2 LeaseRevoke 方法
LeaseRevoke 撤销一个租约,此时所有附加到租约的 key 将过期并被删除。
rpc LeaseRevoke(LeaseRevokeRequest) returns (LeaseRevokeResponse) {}
请求的消息体 LeaseRevokeRequest 定义如下:
message LeaseRevokeRequest {
int64 ID = 1;
}
ID 是要取消的租约的 ID。当租约被取消时,所有关联的 key 将被删除。
应答的消息体 LeaseRevokeResponse 定义如下:
message LeaseRevokeResponse {
ResponseHeader header = 1;
}
LeaseRevokeResponse 中只有一个通用的响应头字段。
3.3 LeaseKeepAlive 方法
LeaseKeepAlive 方法维持一个租约。LeaseKeepAlive 通过从客户端到服务器端的流化的 keep alive 请求和从服务器端到客户端的流化的 keep alive 应答来维持租约。
rpc LeaseKeepAlive(stream LeaseKeepAliveRequest) returns (stream LeaseKeepAliveResponse) {}
请求的消息体 LeaseKeepAliveRequest 定义如下:
message LeaseKeepAliveRequest {
int64 ID = 1;
}
ID 是要继续存活的租约的 ID。
应答的消息体 LeaseKeepAliveResponse:
message LeaseKeepAliveResponse {
ResponseHeader header = 1;
int64 ID = 2;
int64 TTL = 3;
}
ID 是从继续存活请求中得来的租约 ID。TTL 是租约新的 time-to-live。
3.4 LeaseTimeToLive 方法
LeaseTimeToLive 方法获取租约的信息。
rpc LeaseTimeToLive(LeaseTimeToLiveRequest) returns (LeaseTimeToLiveResponse) {}
请求的消息体 LeaseTimeToLiveRequest 定义如下:
message LeaseTimeToLiveRequest {
// ID 是租约的 ID
int64 ID = 1;
bool keys = 2;
}
keys 设置为 true 可以查询附加到这个租约上的所有 key。
应答的消息体 LeaseTimeToLiveResponse 定义如下:
message LeaseTimeToLiveResponse {
ResponseHeader header = 1;
// ID 是来自请求的 ID
int64 ID = 2;
int64 TTL = 3;
int64 grantedTTL = 4;
repeated bytes keys = 5;
}
其中,TTL 是租约剩余的 TTL,单位为秒;租约将在接下来的 TTL + 1 秒之后过期。GrantedTTL 是租约创建/续约时初始授予的时间,单位为秒。keys 是附加到这个租约的 key 的列表。