在zookeeper系列的前三篇,介绍分布式数据一致性的相关原理及经典的分布式一致性算法,比如:2PC,3PC,Paxos算法。在本篇,我们正式开始介绍Zookeeper,Zookeeper是分布式一致性问题的工业解决方案,是常用的分布式协调框架。本篇,会介绍Zookeeper的基本概念,数据模型,节点特性,Watcher机制及ACL等机制,在后边我们会介绍Zookeeper为了保证一致性使用的算法ZAB,以及Zookeeper的应用场景。
在ZooKeeper中,有三种角色:
一个ZooKeeper集群同一时刻只会有一个Leader,其他都是Follower或Observer。2181端口。
Zookeeper集群中的任何一台机器都可以响应客户端的读操作,且全量数据都存在于内存中,因此Zookeeper更适合以读操作为主的应用场景。注意,当不是leader的服务器收到客户端事务操作,他会将其转发到Leader,让Leader进行处理。
ZooKeeper集群的所有机器通过一个 Leader选举过程 来选定一台被称为 『Leader』 的机器, Leader服务器 为客户端提供 读 和 写 服务。Follower和Observer都能提供读服务,不能提供写服务。两者唯一的区别在于,Observer机器不参与Leader选举过程,也不参与写操作的『过半写成功』策略,因此Observer可以在不影响写性能的情况下提升集群的读性能。持久节点(PERSISTENT)
所谓持久节点,是指在节点创建后,就一直存在,直到有删除操作来主动清除这个节点——不会因为创建该节点的客户端会话失效而消失。
持久顺序节点(PERSISTENT_SEQUENTIAL)
在ZK中,每个父节点会为他的第一级子节点维护一份时序,会记录每个子节点创建的先后顺序。基于这个特性,在创建子节点的时候(注意:在此节点下的子节点是由顺序的),可以设置这个属性,那么在创建节点过程中,ZK会自动为给定节点名加上一个数字后缀,作为新的节点名。这个数字后缀的上限是整型的最大值。
临时节点(EPHEMERAL)
和持久节点不同的是,临时节点的生命周期和客户端会话绑定。也就是说,如果客户端会话失效,那么这个节点就会自动被清除掉。注意,这里提到的是会话失效,而非连接断开。另外,在临时节点下面不能创建子节点,注意是更具Session会话的失效时间来设定的。
临时顺序节点(EPHEMERAL_SEQUENTIAL)
临时顺序节点的特性和临时节点一致,同时是在临时节点的基础上,添加了顺序的特性。
ZooKeeper中每个znode的Stat结构体由下述字段构成:
在介绍version时,我们可以简单的了解在数据库技术中,通常提到的“悲观锁”和“乐观锁”:
悲观锁:具有严格的独占和排他特性,能偶有效的避免不同事务在同一数据并发更新而造成的数据一致性问题。实现原理就是:假设A事务正在对数据进行处理,那么在整个处理过程中,都会将数据处于锁定的状态,在这期间,其他事务将无法对这个数据进行更新操作,直到事务A完成对該数据的处理,释放对应的锁。一份数据只会分配一把钥匙,如数据库的表锁或者行锁(for update).
乐观锁:具体实现是,表中有一个版本字段,第一次读的时候,获取到这个字段。处理完业务逻辑开始更新的时候,需要再次查看该字段的值是否和第一次的一样。如果一样更新,反之拒绝。乐观锁就是假定多个事务在处理过程中不会影响彼此,悲观锁正好相反,因此乐观锁在事务处理的绝大部分时间里不需要进行加锁处理。乐观锁非常适用于在数据竞争不大,事务冲突较少的应用场景中。最经典的应用就是:JDK中的CAS处理。
Zookeeper的版本作用就是类似于乐观锁机制,用于实现乐观锁机制的“写入校验”.
1.一次性触发 数据发生改变时,一个watcher event会被发送到client,但是client只会收到一次这样的信息。
2.watcher event异步发送 watcher 的通知事件从server发送到client是异步的,这就存在一个问题,不同的客户端和服务器之间通过socket进行通信,由于网络延迟或其他因素导致客户端在不通的时刻监听到事件,由于Zookeeper本身提供了ordering guarantee,即客户端监听事件后,才会感知它所监视znode发生了变化。
3.数据监视 Zookeeper有数据监视和子数据监视 getdata() and exists() 设置数据监视,getchildren()设置了子节点监视
其监听的事件有:
NodeDataChanged事件:此处的变更包括数据节点内容和数据的版本号DateVersion。因此,对于Zookeeper来说,无论数据内容是否更改,还是会触发这个事件的通知,一旦客户端调用了数据更新接口,且更新成功,就会更新dataversion值。
username:BASE64(SHA1(password))
,scheme为ip时,id的值为客户端的ip地址。scheme为world时,id的值为anyone
。digest:username:BASE64(SHA1(password)):cdrwa
## 创建节点/node_05
shell> create /node_05 data
Created /node_05
## 设置权限
shell> setAcl /node_05 digest:yangxin:ACFm5rWnnKn9K9RN/Oc8qEYGYDs=:cdrwa
cZxid = 0x8e
ctime = Mon Nov 14 21:38:52 CST 2016
mZxid = 0x8e
mtime = Mon Nov 14 21:38:52 CST 2016
pZxid = 0x8e
cversion = 0
dataVersion = 0
aclVersion = 1
ephemeralOwner = 0x0
dataLength = 3
numChildren = 0
## 获取节点刚刚设置的权限
shell> getAcl /node_05
'digest,'yangxin:ACFm5rWnnKn9K9RN/Oc8qEYGYDs=
: cdrwa
## 没有授权,创建节点失败
shell> create /node_05/node_05_01 data
Authentication is not valid : /node_05/node_05_01
## 添加授权信息
shell> addauth digest yangxin:123456
## 添加授权信息后,就可以正常操作了
shell> create /node_05/node_05_01 data
Created /node_05/node_05_01
2. IP
192.168.1.100
的客户端能读写该写节点的数据:ip:192.168.1.100:rw
world:anyone:cdrwa
digest:username:BASE64(SHA1(password)):cdrwa
中的cdrwa即是permission。 Zookeeper的数据模型是树结构,在内存数据库中,存储了整棵树的内容,包括所有的节点路径、节点数据、ACL信息,Zookeeper会定时将这个数据存储到磁盘上。
1. DataTree
DataTree是内存数据存储的核心,是一个树结构,代表了内存中一份完整的数据。DataTree不包含任何与网络、客户端连接及请求处理相关的业务逻辑,是一个独立的组件。
2. DataNode
DataNode是数据存储的最小单元,其内部除了保存了结点的数据内容、ACL列表、节点状态之外,还记录了父节点的引用和子节点列表两个属性,其也提供了对子节点列表进行操作的接口。
3. ZKDatabase
Zookeeper的内存数据库,管理Zookeeper的所有会话、DataTree存储和事务日志。ZKDatabase会定时向磁盘dump快照数据,同时在Zookeeper启动时,会通过磁盘的事务日志和快照文件恢复成一个完整的内存数据库。