4. Zookeeper概念
?
Zookeeper数据模型(Zookeeper Data Model) Zookeeper的数据模型是一个树形结构(Znode Tree),znode是该数据模型中的数据单元,/是整棵树的root节点,每个znode上都会保持自己的数据并持有一份stat属性信息,分为两种类型临时节点(Ephemeral Nodes)和永久节点(Persistent Nodes)。 每种节点上都可以设置一个特殊属性--SEQUENTIAL。znode名字是有约束的,尽量不要用非字符串和包含.的字符串作为path名字,/zookeeper是zk特有节点,不能被用户创建。
? 临时节点:znode生命周期与客户端会话绑定,一旦客户端会话失效,那么该客户端
会话期间创建所有临时节点都将被删除
? 永久节点:除非主动进行针对该znode的删除操作,无论客户端会话是否失效,都会
删掉该znode
? 顺序属性(SEQUENTIAL):一旦创建节点时带上该属性,那么该节点后缀都会加上一
个由父节点维度下的自增整形数字,这样能有效防止两个client创建同一个名字的znode而导致其中一个client收到创建错误的响应。
?
zk状态信息(Zookeeper Stat Structure)
cxid ctime mzxid mtime pzxid 该数据节点被创建时的事务id 该节点被创建时的时间 节点最后一次被更新时的事务id 节点最后一次被更新的时间 该节点子列表最后一次被修改时的事务id,子节点内容的变化不会影响pzxid,从上图我们看出,由于没有子节点,所以pzxid==cxid cversion aversion dataversion 当前数据节点子节点变更的版本号,每变一次数值+1 当前数据节点ACL变更版本号,每变一次数值+1 当前数据内容变更的版本号,每变一次数值+1 emphemeralOwner 创建该临时节点的会话sessionId,如果是永久节点那么值为0 datalength 数据内容的长度 numchildren 当前节点的子节点个数
Version版本
每个节点都有一个版本号,每次数据更新都会加1.基于此,可以实现各个不同客户端CAS原子更新。
像redis、memcached都支持CAS操作来实现数据原子操作,数据库基于CAS实现乐观锁,java concurret 包基于CAS实现高性能并发操作。
zk的API中有两种操作支持基于版本做CAS操作----setData和delete操作。如果传递版本-1,则忽略原子操作强制进行更新和修改。 如果delete传版本-1,对于具有ACL权限控制的节点可以强制删除,对于具有ACL权限控制的子节点不能强制删除,只能给自己添加权限的才能删除 setData传版本-1,对于具有ACL权限控制的节点无法更新
?
1)client1 更新/config成功,version变成2 2)client2 获取/config的version,并且
setData(/config,data=\变成3,并返回给client2 3)client1 传递version2来更新/config,结果返回错误,因为版本不对
? ZXID 事务id
每次对Zookeeper状态的改变都会生成一个自增id叫做zxid来表示该次更新的事务id,高32位代表leader的epoch,低32位是一个自增id。zxid是uniqe的,如果zxid1>zxid2,那么zxid1肯定发生在zxid2之后。 事务操作包括(要申请zxid) create delete setData multi setACL
createSession closeSession 非事务操作包括
sync(唯一一个需要转发给leader的非事务操作) exists getData getACL getChildren
getChildren2
ping
auth(每次重连时发送)
?
?
setWatchs(每次重连时发送)
事务操作不管成功与否,都会申请一个zxid,都会记录到事务日志文件中。所有事务ID都是leader自增分配的。
https://issues.apache.org/jira/browse/ZOOKEEPER-1277 由于zxid低32位是自增id,假如每秒写入500次,3个月后,zxid将使高32位的epoch发生rollover,这是不允许的,于是zk修复了这个bug,leader在发现zxid低位达到0xffffffffL时就shutdown强制做一次leader选举。
EPOCH ---Leader周期
epoch中文是纪元世纪的意思,实际上很形象,每次选举出一个新leader,就是开启一个新纪元,直到下次遇到故障重新进行leader选举,这段时间所有的事务id的高32位都是该epoch。下次leader选举成功后,epoch加1形成新的epoch。
epoch在各个分布式系统都有用到,用于做leader选举(先比较epoch)以及fencing,比如kafka中基于epoch做fencing(防止集群间过期消息造成影响)。 Zookeeper由于只保持一个连接,并且只有一个线程处理集群间的消息传递,故不需要使用epoch做集群间的fencing,只在leader选举时用。
Watcher--数据变更通知 KeeperState EventType 触发条件 说明 SyncConnected NodeDataChanged(3) NodeDelete(2) NodeCreated(1) None(-1) 客户端与服务端建立会话成功 Watcher监听的对应数据节点被创建 Watcher监听的对应数据节点被删除 Watcher监听的对应数据节点数据内容发生变更 NodeChildrenChanged Watcher监听(4) 的对应数据节点的子节点列表发生变化 Disconnected None(-1) 客户端与服务器断开连接 客户端未及时发送ping请求或者在 读超时的时间内未能接收到来自server的响应那么就关闭连接(不会关闭session) 所有pending request收到 SessionTimeout 客户端与服务器处于连接状态 Exception响应 Expired None(-1) 会话超时 收到SessionExpired Exception AuthFailed None(-1) 使用错误的schema 或者SASL权限检查失败
使用默认watcher,建立会话成功、Disconnected、Expired、Auth_failed通知都会收到,其中遇到Expired和AUTH_FAILED,会话会失效是不可恢复的。
只能重新建立新session,client使用新的Zookeeper实例 watch存在放client端,server端只存放哪些client要对哪些path做哪种类型watch(datawatch和childrenwatch)。 getData注册节点删除、内容更新
exsit 注册节点创建、删除、内容更新 getChildren注册节点删除、子列表变化
watcheEvent包括KeeperState,eventTpye,path
watch有如下特性 1、一次触发
client每次设置watch,收到通知后必须再次注册watch才能收到下次事件通知,比如 getData(\收到通知以后需要再次注册watch
getData(\可以在每次通知时注册上watch并获取一次最新数据,所以不会漏掉任何一次更新。
2、顺序执行
所有的watch的处理都在event线程顺序的执行 3、轻量
网络开销和内存开销很廉价
watch导致的羊群效应
由于多个client都会watch同一个path。当该path遇到事件发生时,所有client都将收到通知,并同时发送getData或getChildren等读请求,导致server端压力变大,集群数量没有太大的情况是不会出现,避免羊群效应的方法是不让所有client监听同一个path,比如分布式锁每次抢占锁创建一个临时有序znode,每个client只需要监听比自己小的那个znode,如果比自己小的那个znode得到释放,那么就获取锁
watch的触发机制
1、watcher对象保存在client中,注册器保存了多个不同事件类型的map,map中kv结构是(path->wacher)
2、client所连的server会保存(path--事件类型)的映射关系,绑定到连接对象。 3、当client进行重连时,由于client有watch的缓存,在发送connectRequest请求前会发送setWatch请求,将watch注册到server,在server端保存映射关系 与此类似的是auth
watchEvent不一定是server发起的
watchEvent除了notifcation,还有client自己也可以发起,比如disconnect事件是在client发现server网络通信超时。 还有auth_failed等
通常也会收到 AuthFailed Exception
相关推荐: