ETCD持久化监听数据丢失小记

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变化)相关类型事件:setgetcreateupdatedeleteexpirecompareAndSwapcompareAndDelete。在返回的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:

  1. X-Etcd-Index代表etcd当前index,为所有key共享。单挑递增总是大于或等于modifiedIndex,而这个modifiedIndex是etcd已经存储事件的index
  2. 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丢失,重新再同步需要保证数据一致、同步前后的变化通知上层监听。

你可能感兴趣的:(ETCD)