背景介绍
ZooKeeper是一个分布式、开源的应用程序协调服务。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等
应用场景
分布式锁:分布式的java业务系统中使用
元数据管理:kafka、Canal,本身都是分布式架构,分布式集群运行需要一个地方集中式存储和管理分布式集群的核心元数据。例如:Dubbo的注册中心
分布式协调:如果有人对zk中的数据做了变更,zk反过来去通知其他监听这个数据的人。例如:kafka有多个broker,多个broker会竞争成为一个controller角色
master选举:HA架构。HDFS,NameNode的HA架构
部署架构
一个ZooKeeper集群中一般分为Leader、Follower和Observer(不是必须)。所有节点均对外提供读服务,对外提供写服务的只有Leader,当客户端向Leader中写入数据后,Leader会通过2PC将数据同步到Follower中,当超过半数的Follower返回成功时Leader会再发起commit请求,让Follower正式提交数据。当Leader宕机时Follower会进行投票选举新的Leader。Observer节点不参与ack和leader选举,仅仅从Leader中同步数据,从而提高ZooKeeper集群的负载能力。
其存储的数据结构为znode树(类似于文件系统),分为持久节点和临时节点
特性
连接
客户端与ZooKeeper之间是建立的长连接,通过心跳机制确定节点是否健康。ZooKeeper也支持watcher机制,客户端可以基于长连接监听某个节点的回调通知
顺序一致性
一致性保障:ZooKeeper通过ZAB协议(ZooKeeper Atomic Broadcast)保证集群中各个节点的数据的一致性。当客户端发起一个写请求给Leader后,Leader会将该请求转化为事务proposal提案然后发送给所有的Follower,Follower接收到请求后将proposal存入本地磁盘然后向Leader返回ack。此时Leader收到超过半数的ack就认为消息发送成功,进而发送commit消息。收到commit消息的Followe将数据存入znode节点中
有序性:当客户端向ZooKeeper提交一个事务请求时(例如:插入一个节点),此时Leader会将此请求转化为一个事务提案proposal,并生成一个全局唯一的zxid(64位)。zxid的高32位为epoch,用于表示Leader关系是否改变,每次一个新的Leader被选举出来后epoch就会+1。zxid的低32位采用递增的事务id号
高可用CP
当客户端通过心跳机制发现Follower宕机时,可以自动与其他健康的节点进行连接。当Leader宕机时,所有Follower就要开始投票选举新的Leader。每个Follower会把自己的最大zxid作为选票在集群中广播,收到投票的Follower判断收到的选票的zxid如果大于自己当前的zxid就同意,否则就不同意。如果2台Follower的zxid相同,就需要判断myid大小(每台Follower启动时分配的id),选择myid大的作为Leader(ZooKeeper集群第一次启动时也需要这么判断)。当新的Leader产生后,还需要将新Leader中存在的而其他Follower中不存在的数据进行同步
高性能
ZooKeeper中的数据是存储在内存中的,其结构为znode树。由于其将数据存储在内存中,所以性能较好,单台16核32G 服务器TPS可达几万。同时由于Follower和Observer是提供只读服务的,所以ZooKeeper集群的读能力较强,适合读多写少的场景。服务发现秒级
脑裂问题
正常情况下ZooKeeper集群中只有一个Leader,但是当发生网络故障时,可能会产生2个Leader。例如:机房A和机房B各部署了3台ZooKeeper组成了一个集群,但是由于机房A和机房B的网络通信出现故障,导致机房B中的Follower连接不上Leader,从而进行Leader选举,最终机房B也选出了一个Leader

