Zookeeper核心知识全解:节点类型、集群架构与选举机制
一、部署Zookeeper
1.1 解压zookeeper压缩包, 进入conf目录,复制zoo_sample.cfg,修改为zoo.cfg
cp zoo_sample.cfg zoo.cfg
修改 zoo.cfg 配置文件,将 dataDir=/tmp/zookeeper 修改为指定的data目录。
1.2 启动zookeeper server
可以通过 bin/zkServer.sh 来查看都支持哪些参数,默认加载配置路径conf/zoo.cfg
bin/zkServer.sh start
bin/zkServer.sh start conf/zoo.cfg
1.3 查看zookeeper状态
bin/zkServer.sh status ,启动zookeeper client连接Zookeeper server
bin/zkCli.sh#连接远程的zookeeper server
bin/zkCli.sh -server ip:port
二、节点类型
2.1 持久节点(PERSISTENT)
这样的znode在创建之后即使发生ZooKeeper集群宕机或者client宕机也不会丢失。
create /locks
2.2 临时节点(EPHEMERAL )
client宕机或者client在指定的timeout时间内没有给ZooKeeper集群发消息,这样的znode就会消失
create -e /locks
2.3 持久顺序节点(PERSISTENT_SEQUENTIAL)
znode除了具备持久性znode的特点之外,znode的名字具备顺序性。
create -s /locks
2.4 临时顺序节点(EPHEMERAL_SEQUENTIAL)
znode除了具备临时性znode的特点之外,zorde的名字具备顺序性。zookeeper主要用到的是以上4种节点。
create -e -s /locks
2.5 Container节点 (3.5.3版本新增)
Container容器节点,当容器中没有任何子节点,该容器节点会被zk定期删除(定时任务默认60s 检查一次)。
和持久节点的区别是 ZK 服务端启动后,会有一个单 独的线程去扫描,所有的容器节点,当发现容器节点的子节点数量为 0 时,
会自动删除该节点。可以 用于 leader 或者锁的场景中。
create -c /work
2.6 TTL节点
带过期时间节点,默认禁用,需要在zoo.cfg中添加 extendedTypesEnabled=true 开启。注意:TTL不能用于临时节点个节点有序的性质。
create -t 3000 /ttl_node
三、Stat信息介绍
- cZxid :Znode创建的事务id。
- ctime:节点创建时的时间戳。
- mZxid :Znode被修改的事务id,即每次对znode的修改都会更新mZxid。对于zk来说,每次的变化都会产生一个唯一的事务id,
zxid(ZooKeeper Transaction Id),通过zxid,可以确定更新操作的先后顺序。例如,如果zxid1小于zxid2,
说明zxid1操作先于zxid2发生,zxid对于整个zk都是唯一的,即使操作的是不同的znode。 - pZxid:表示该节点的子节点列表最后一次修改的事务ID,添加子节点或删除子节点就会影响子节点列表,
但是修改子节点的数据内容则不影响该ID(注意:只有子节点列表变更了才会变更pzxid,子节点内容变更不会影响pzxid) - mtime:节点最新一次更新发生时的时间戳.
- cversion :子节点的版本号。当znode的子节点有变化时,cversion 的值就会增加1。
- dataVersion:数据版本号,每次对节点进行set操作,dataVersion的值都会增加1(即使设置的是相同的数据),可有效避免了数据更新时出现的先后顺序问题。
- ephemeralOwner:如果该节点为临时节点, ephemeralOwner值表示与该节点绑定的session id。
如果不是, ephemeralOwner值为0(持久节点)。 在client和server通信之前,首先需要建立连接,该连接称为session。
连接建立后,如果发生连接超时、授权失败,或者显式关闭连接,连接便处于closed状态, 此时session结束。 - dataLength : 数据的长度
- numChildren :子节点的数量(只统计直接子节点的数量)
四、Watch机制详解
4.1 watch机制
顾名思义是一个监听机制。Zookeeper中的watch机制,必须客户端先去服务端注册监听,
这样事件发送才会触发监听,通知给客户端。监听的对象是事件,支持的事件类型如下:
- None: 连接建立事件
- NodeCreated: 节点创建
- NodeDeleted: 节点删除
- NodeDataChanged:节点数据变化
- NodeChildrenChanged:子节点列表变化
- DataWatchRemoved:节点监听被移除
- ChildWatchRemoved:子节点监听被移除
4.2 永久性Watch
在被触发之后,仍然保留,可以继续监听ZNode上的变更,是Zookeeper 3.6.0版本新增的功能
addWatch [-m mode] path
addWatch的作用是针对指定节点添加事件监听,支持两种模式:
- PERSISTENT,持久化订阅,针对当前节点的修改和删除事件,以及当前节点的子节点的删除和新增事件。
- PERSISTENT_RECURSIVE,持久化递归订阅(默认),在PERSISTENT的基础上,增加了子节点修改的事件触发,
以及子节点的子节点的数据变化都会触发相关事件(满足递归订阅特性)
五、Zookeeper集群架构
5.1 Leader: 领导者
事务请求(写操作)的唯一调度者和处理者,保证集群事务处理的顺序性;集群内部各个服务器的调度者。
对于create、setData、delete等有写操作的请求,则要统一转发给leader处理,leader需要决定编号、执行操作,这个过程称为事务。
5.2 Follower: 跟随者
处理客户端非事务(读操作)请求(可以直接响应),转发事务请求给Leader;参与集群Leader选举投票。
5.3 Observer: 观察者
对于非事务请求可以独立处理(读操作),对于事务性请求会转发给leader处理。Observer节点接收来自leader的inform信息,
更新自己的本地存储,不参与提交和选举投票。通常在不影响集群事务处理能力的前提下提升集群的非事务处理能力。
#配置一个ID为3的观察者节点:
server.3=192.168.0.3:2888:3888:observer
Observer应用场景:
- 提升集群的读性能。因为Observer和不参与提交和选举的投票过程,所以可以通过往集群里面添加observer节点来提高整个集群的读性能。
- 跨数据中心部署。 比如需要部署一个北京和香港两地都可以使用的zookeeper集群服务,
并且要求北京和香港客户的读请求延迟都很低。解决方案就是把香港的节点都设置为observer。
六、Zookeeper选举原理
ZooKeeper的Leader选举过程是基于投票和对比规则的,确保集群中选出一个具有最高优先级的服务器作为Leader来处理客户端请求。以服务启动期间选举为例:
投票对比规则如下:
- 首先比较epoch,选取具有最大epoch的服务器。epoch用于区分不同的选举轮次,每次重新选举时都会增加epoch。
- 如果epoch相同,则比较zxid(事务ID),选取事务ID最大的服务器。zxid表示最后一次提交的事务ID。
- 如果zxid也相同,则比较myid(服务器ID),选取服务器ID最大的服务器。
protected boolean totalOrderPredicate(long newId, long newZxid, long newEpoch, long curId, long curZxid, long curEpoch) {LOG.debug("id: {}, proposed id: {}, zxid: 0x{}, proposed zxid: 0x{}",newId,curId,Long.toHexString(newZxid),Long.toHexString(curZxid));if (self.getQuorumVerifier().getWeight(newId) == 0) {return false;}/** We return true if one of the following three cases hold:* 1- New epoch is higher* 2- New epoch is the same as current epoch, but new zxid is higher* 3- New epoch is the same as current epoch, new zxid is the same* as current zxid, but server id is higher.*/return ((newEpoch > curEpoch)|| ((newEpoch == curEpoch)&& ((newZxid > curZxid)|| ((newZxid == curZxid)&& (newId > curId)))));
}
zxid的数据结构
根据这个工具类,可以得出zxid的数据结构的一些信息。
1.zxid是一个64位的整数,由高32位的epoch和低32位的counter组成。
2.epoch表示ZooKeeper服务器的逻辑时期(logical epoch),它是一个相对时间的概念,用于区分不同的Leader选举周期。
3.counter是一个在每个时期(epoch)内递增的计数器,用于标识事务的顺序。
public class ZxidUtils {public static long getEpochFromZxid(long zxid) {return zxid >> 32L;}public static long getCounterFromZxid(long zxid) {return zxid & 0xffffffffL;}public static long makeZxid(long epoch, long counter) {return (epoch << 32L) | (counter & 0xffffffffL);}public static String zxidToString(long zxid) {return Long.toHexString(zxid);}}