ETCD持久化监听数据丢失小记
etcd是CoreOS开发的分布式高可用键值存储系统。随着CoreOS和K8s等项目在开源社区日益火热,etcd组件也渐渐为开发人员所关注。etcd也是受到ZooKeeper与doozer启发而催生的项目,除了拥有类似功能,更专注于以下四点。简单:基于HTTP+JSON的API让你用curl就可以轻松使用(V3版本不再使用JSON)。安全:可选SSL客户认证机制。快速:每个实例每秒支持一...
etcd是CoreOS开发的分布式高可用键值存储系统。随着CoreOS和K8s等项目在开源社区日益火热,etcd组件也渐渐为开发人员所关注。
etcd也是受到ZooKeeper与doozer启发而催生的项目,除了拥有类似功能,更专注于以下四点。
- 简单:基于HTTP+JSON的API让你用curl就可以轻松使用(V3版本不再使用JSON)。
- 安全:可选SSL客户认证机制。
- 快速:每个实例每秒支持一千次写操作。
- 可信:使用Raft算法充分实现了分布式。
在项目对比etcd和zookeeper之后,etcd更轻型容易部署安装使用,zk特性比较丰富,但已老态龙钟,需要点新鲜选择。在去年我党生日迎来了etcd v3(使用gRPC、改变key ttl使用租约等),蛋疼的发现java客户端etcd4j不支持v3版本,v2版本目前可以满足我们需求,继续使用etcd,后续会关注etcd4j更新。本文基于etcd v2版本使用。
etcd事件监听
etcd没有提供被动监听的实现,我们可以主动轮训监听key的变化。如果想监听其子节点可以通过recursive=true
参数
curl http://127.0.0.1:2379/v2/keys/foo?wait=true
对/foo的改变会受到通知和返回相关变化事件
HTTP/1.1 200 OK
Content-Type: application/json
X-Etcd-Cluster-Id: e88d54f6225f06ad
X-Etcd-Index: 271
X-Raft-Index: 872202
X-Raft-Term: 5
Date: Sat, 26 Sep 2015 08:43:17 GMT
Transfer-Encoding: chunked
{
"action":
"set",
"node": {
"createdIndex": 7,
"key": "/foo",
"modifiedIndex": 7,
"value": "bar"
},
"prevNode": {
"createdIndex": 6,
"key": "/foo",
"modifiedIndex": 6,
"value": "bar"
}
}
etcd中的数据变化(包括目录和key、value变化)相关类型事件:set
、get
、create
、update
、delete
、expire
、compareAndSwap
、compareAndDelete
。在返回的http response
中的action
属性就是事件类型,如上图所示。
etcd记录下最近1000次事件变化,使用index我们可以watch其key在过去发生的变化。使用node的modifiedIndex+1就可以监听下一次事件:curl 'http://127.0.0.1:2379/v2/keys/foo?wait=true&waitIndex=8'
但当事件突发比如1秒内产生几千条事件,事件监听处理比较慢或者未监听是发生了客户端事件丢失。当我们index超过etcd记录的返回,就返回如下消息:
{
"errorCode" : 401 ,
"message" : "The event in requested index is outdated and cleared" ,
"cause" : "the requested history has been cleared [1008/8]" ,
"index" : 2007
}
官方文档中推荐使用X-Etcd-Index+1作为waitIndex代替使用node的modifiedIndex+1:
- X-Etcd-Index代表etcd当前index,为所有key共享。单挑递增总是大于或等于modifiedIndex,而这个modifiedIndex是etcd已经存储事件的index
- modifiedIndex和X-Etcd-Index之间不代表有事件发生,当fetch 相关key不会有事件返回。
使用modifiedIndex+1
只是功能表示后续监听,它比X-Etcd-Index+1
小,很可能相关事件已经被清除,可能会受到401EventIndexCleared
错误。在初始监听或断开重新监听,index应该使用X-Etcd-Index+1
,而不是modifiedIndex+1
。
上述只能监听一次事件变化,Etcd还提供流式监听,在curl的时候加stream=true
参数,会和etcd服务端建议http长连接,后续的每个事件都会通过这个http chunk推送给客户端。相对比一次性监听,更简洁,可靠性更高。但有一问题,流式监听监听从命令发出之后的时间,先前的时间是监听不到的,如果再加waitIndex参数,waitIndex小于或等于启动监听时etcd的X-Etcd-Index,只能接受到满足条件的事件,后续不再接收,只能收到一个事件。waitIndex大于X-Etcd-Index无效。
Etcd4j并没有支持流式监听,而且流式监听无法做到监听时的客户端和服务端数据同步。我们使用Etcd4j实现监听持久化,实现如下特性:
- 事件类型简单化,etcd的set、get、create、update、delete、expire、compareAndSwap、compareAndDelete事件类型,简化成Add、Upate、Removed三种类型
- 支持目录、key全量变化监听,支持子节点变化监听。例如etcd删除目录时子节点也随之删除,只产生删除目录事件,而子节点删除也需要通知其删除事件。
- 需要解决事件突发(超过etcd记录event数量)造成监听index丢失,重新再同步需要保证数据一致、同步前后的变化通知上层监听。
更多推荐
所有评论(0)