原创

Zookeeper集群部署及原理分析

一、Zookeeper集群部署

1.下载最新链接

https://mirrors.tuna.tsinghua.edu.cn/apache/zookeeper/zookeeper-3.5.5/apache-zookeeper-3.5.5-bin.tar.gz

2.模拟一台机器上面部署3zk节点,分别解压放到

/data/zookeeper/node1 端口:2181

/data/zookeeper/node2 端口:3181

/data/zookeeper/node3 端口:4181

分别将conf目录下的zoo_sample.cfg文件copy一份命名为zoo.cfg,修改如下,三份的clientPort不一样,分别为2181,3181,4181dataDir也不一样,如下

cp zoo_sample.cfg zoo.cfg

node1配置

tickTime=2000
initLimit=10
syncLimit=5
dataDir=/data/zookeeper/node1/data
clientPort=2181
server.0=192.168.1.107:2188:2288
server.1=192.168.1.107:3188:3288
server.2=192.168.1.107:4188:4288
node2配置
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/data/zookeeper/node2/data
clientPort=3181
server.0=192.168.1.107:2188:2288
server.1=192.168.1.107:3188:3288
server.2=192.168.1.107:4188:4288
node3配置

tickTime=2000
initLimit=10
syncLimit=5
dataDir=/data/zookeeper/node3/data
clientPort=4181
server.0=192.168.1.107:2188:2288
server.1=192.168.1.107:3188:3288
server.2=192.168.1.107:4188:4288

这里的server.xx是一个数字,与myid文件中的id是一致的。右边可以配置两个端口,第一个端口用于FL之间的数据同步和其它通信,第二个端口用于Leader选举过程中投票通信。  

3.分别在node文件下面新建data目录,新加文件myid

node1/data/myid里面的信息为0

node2/data/myid里面的信息为1

node3/data/myid里面的信息为2

4.启动,进入bin目录下

#启动

./zkServer.sh start

#查看状态

./zkServer.sh status

PS:我启动第一个节点的时候,状态为not running,但是netstat -lnp查看端口是被占用着的,等到启动第二个节点的时候,就是可用状态,且第二个节点为leader节点,第一个节点为follower

5.命令行测试

可以通过指定ip和端口进入,不指定的话默认为2181

./zkCli.sh -server 192.168.1.107:2181,192.168.1.107:3181,192.168.1.107:4181

查找根目录

ls  /

创建节点并赋值

create /test abc

获取指定节点的值

get /test

设置已存在节点的值

set /test cb

递归删除节点

rmr /test

删除不存在子节点的节点

delete /test/test01

6.客户端可视化工具

zktools下载自行百度

7.测试结论

期间只关掉一个leader或者follower,服务都是ok的,当关掉leader或重新推举leader,关掉follwer则不会推选leader

8.zoo.cfg配置说明

参数名

说明

clientPort

客户端连接server的端口,即对外服务端口,一般设置为2181吧。

dataDir

存储快照文件snapshot的目录。默认情况下,事务日志也会存储在这里。建议同时配置参数dataLogDir, 事务日志的写性能直接影响zk性能。

tickTime

ZK中的一个时间单元。ZK中所有时间都是以这个时间单元为基础,进行整数倍配置的。例如,session的最小超时时间是2*tickTime

dataLogDir

事务日志输出目录。尽量给事务日志的输出配置单独的磁盘或是挂载点,这将极大的提升ZK性能。  

globalOutstandingLimit

最大请求堆积数。默认是1000ZK运行的时候, 尽管server已经没有空闲来处理更多的客户端请求了,但是还是允许客户端将请求提交到服务器上来,以提高吞吐性能。当然,为了防止Server内存溢出,这个请求堆积数还是需要限制下的。  

preAllocSize

预先开辟磁盘空间,用于后续写入事务日志。默认是64M,每个事务日志大小就是64M。如果ZK的快照频率较大的话,建议适当减小这个参数。

snapCount

每进行snapCount次事务日志输出后,触发一次快照(snapshot), 此时,ZK会生成一个snapshot.*文件,同时创建一个新的事务日志文件log.*。默认是100000.(真正的代码实现中,会进行一定的随机数处理,以避免所有服务器在同一时间进行快照而影响性能)

traceFile

用于记录所有请求的log,一般调试过程中可以使用,但是生产环境不建议使用,会严重影响性能。

maxClientCnxns

单个客户端与单台服务器之间的连接数的限制,是ip级别的,默认是60,如果设置为0,那么表明不作任何限制。请注意这个限制的使用范围,仅仅是单台客户端机器与单台ZK服务器之间的连接数限制,不是针对指定客户端IP,也不是ZK集群的连接数限制,也不是单台ZK对所有客户端的连接数限制。

clientPortAddress

对于多网卡的机器,可以为每个IP指定不同的监听端口。默认情况是所有IP都监听 clientPort 指定的端口。

minSessionTimeoutmaxSessionTimeout

Session超时时间限制,如果客户端设置的超时时间不在这个范围,那么会被强制设置为最大或最小时间。默认的Session超时时间是在2 *  tickTime ~ 20 * tickTime 这个范围 New in 3.3.0

fsync.warningthresholdms

事务日志输出时,如果调用fsync方法超过指定的超时时间,那么会在日志中输出警告信息。默认是1000ms(Java system property:  fsync.warningthresholdms )New in 3.3.4

autopurge.purgeInterval

在上文中已经提到,3.4.0及之后版本,ZK提供了自动清理事务日志和快照文件的功能,这个参数指定了清理频率,单位是小时,需要配置一个1或更大的整数,默认是0,表示不开启自动清理功能。

autopurge.snapRetainCount

这个参数和上面的参数搭配使用,这个参数指定了需要保留的文件数目。默认是保留3个。

electionAlg

在之前的版本中, 这个参数配置是允许我们选择leader选举算法,但是由于在以后的版本中,只会留下一种“TCP-based version of fast leader election”算法,所以这个参数目前看来没有用了,这里也不详细展开说了。

initLimit

Follower在启动过程中,会从Leader同步所有最新数据,然后确定自己能够对外服务的起始状态。Leader允许F initLimit 时间内完成这个工作。通常情况下,我们不用太在意这个参数的设置。如果ZK集群的数据量确实很大了,F在启动的时候,从Leader上同步数据的时间也会相应变长,因此在这种情况下,有必要适当调大这个参数了。

syncLimit

在运行过程中,Leader负责与ZK集群中所有机器进行通信,例如通过一些心跳检测机制,来检测机器的存活状态。如果L发出心跳包在syncLimit之后,还没有从F那里收到响应,那么就认为这个F已经不在线了。注意:不要把这个参数设置得过大,否则可能会掩盖一些问题。

leaderServes

默认情况下,Leader是会接受客户端连接,并提供正常的读写服务。但是,如果你想让Leader专注于集群中机器的协调,那么可以将这个参数设置为no,这样一来,会大大提高写操作的性能。

server.x=[hostname]:nnnnn[:nnnnn]

这里的x是一个数字,与myid文件中的id是一致的。右边可以配置两个端口,第一个端口用于FL之间的数据同步和其它通信,第二个端口用于Leader选举过程中投票通信。  

group.x=nnnnn[:nnnnn]weight.x=nnnnn

对机器分组和权重设置

cnxTimeout

Leader选举过程中,打开一次连接的超时时间,默认是5s

zookeeper.DigestAuthenticationProvider
.superDigest

ZK权限设置相关,具体参见  《  使用super  身份对有权限的节点进行操作 》    《 ZooKeeper   权限控制 》

skipACL

对所有客户端请求都不作ACL检查。如果之前节点上设置有权限限制,一旦服务器上打开这个开头,那么也将失效。

forceSync

这个参数确定了是否需要在事务日志提交的时候调用 FileChannel .force来保证数据完全同步到磁盘。

jute.maxbuffer

每个节点最大数据量,是默认是1M。这个限制必须在serverclient端都进行设置才会生效。

二、ZK leader选举过程

Leader选举是保证分布式数据一致性的关键所在。Leader选举分为Zookeeper集群初始化启动时选举和Zookeeper集群运行期间Leader重新选举两种情况。在讲解Leader选举前先了解一下Zookeeper节点4种可能状态和事务ID概念。

1Zookeeper节点状态

LOOKING:寻找Leader状态,处于该状态需要进入选举流程

LEADING:领导者状态,处于该状态的节点说明是角色已经是Leader

FOLLOWING:跟随者状态,表示Leader已经选举出来,当前节点角色是follower

OBSERVER:观察者状态,表明当前节点角色是observer

2、事务ID

ZooKeeper状态的每次变化都接收一个ZXIDZooKeeper事务id)形式的标记。ZXID是一个64位的数字,由Leader统一分配,全局唯一,不断递增。

ZXID展示了所有的ZooKeeper的变更顺序。每次变更会有一个唯一的zxid,如果zxid1小于zxid2说明zxid1zxid2之前发生。

3Zookeeper集群初始化启动时Leader选举

若进行Leader选举,则至少需要两台机器,这里选取3台机器组成的服务器集群为例。

初始化启动期间Leader选举流程如下图所示。


 

在集群初始化阶段,当有一台服务器ZK1启动时,其单独无法进行和完成Leader选举,当第二台服务器ZK2启动时,此时两台机器可以相互通信,每台机器都试图找到Leader,于是进入Leader选举过程。选举过程开始,过程如下:

(1)每个Server发出一个投票。由于是初始情况,ZK1ZK2都会将自己作为Leader服务器来进行投票,每次投票会包含所推举的服务器的myidZXID,使用(myid, ZXID)来表示,此时ZK1的投票为(1, 0)ZK2的投票为(2, 0),然后各自将这个投票发给集群中其他机器。

(2)接受来自各个服务器的投票。集群的每个服务器收到投票后,首先判断该投票的有效性,如检查是否是本轮投票、是否来自LOOKING状态的服务器。

(3)处理投票。针对每一个投票,服务器都需要将别人的投票和自己的投票进行比较,规则如下

· 优先检查ZXIDZXID比较大的服务器优先作为Leader

· 如果ZXID相同,那么就比较myidmyid较大的服务器作为Leader服务器。

对于ZK1而言,它的投票是(1, 0),接收ZK2的投票为(2, 0),首先会比较两者的ZXID,均为0,再比较myid,此时ZK2myid最大,于是ZK2胜。ZK1更新自己的投票为(2, 0),并将投票重新发送给ZK2

(4)统计投票。每次投票后,服务器都会统计投票信息,判断是否已经有过半机器接受到相同的投票信息,对于ZK1ZK2而言,都统计出集群中已经有两台机器接受了(2, 0)的投票信息,此时便认为已经选出ZK2作为Leader

(5)改变服务器状态。一旦确定了Leader,每个服务器就会更新自己的状态,如果是Follower,那么就变更为FOLLOWING,如果是Leader,就变更为LEADING。当新的Zookeeper节点ZK3启动时,发现已经有Leader了,不再选举,直接将直接的状态从LOOKING改为FOLLOWING

4Zookeeper集群运行期间Leader重新选

Zookeeper运行期间,如果Leader节点挂了,那么整个Zookeeper集群将暂停对外服务,进入新一轮Leader选举。

假设正在运行的有ZK1ZK2ZK3三台服务器,当前LeaderZK2,若某一时刻Leader挂了,此时便开始Leader选举。选举过程如下图所示。


 

 

(1) 变更状态。Leader挂后,余下的非Observer服务器都会将自己的服务器状态变更为LOOKING,然后开始进入Leader选举过程。

(2) 每个Server会发出一个投票。在运行期间,每个服务器上的ZXID可能不同,此时假定ZK1ZXID124ZK3ZXID123;在第一轮投票中,ZK1ZK3都会投自己,产生投票(1, 124)(3, 123),然后各自将投票发送给集群中所有机器。

(3) 接收来自各个服务器的投票。与启动时过程相同。

(4) 处理投票。与启动时过程相同,由于ZK1事务ID大,ZK1将会成为Leader

(5) 统计投票。与启动时过程相同。改变服务器的状态。与启动时过程相同。

三、zookeeper的其他特性

1.Zookeeper的读写机制

Zookeeper是一个由多个server组成的集群一个leader,多个follower

每个server保存一份数据副本

全局数据一致

分布式读写

更新请求转发,由leader实施

2.Zookeeper的保证

更新请求顺序进行,来自同一个client的更新请求按其发送顺序依次执行

数据更新原子性,一次数据更新要么成功,要么失败

全局唯一数据视图,client无论连接到哪个server,数据视图都是一致的

实时性,在一定事件范围内,client能读到最新数据

3.Zookeeper节点数据操作流程

(1)ClientFollwer发出一个写的请求

(2)Follwer把请求发送给Leader

(3)Leader接收到以后开始发起投票并通知Follwer进行投票

(4)Follwer把投票结果发送给Leader

(5)Leader将结果汇总后如果需要写入,则开始写入同时把写入操作通知给Leader,然后commit;

(6)Follwer把请求结果返回给Client

4.Follower主要有四个功能:

(1)Leader发送请求(PING消息、REQUEST消息、ACK消息、REVALIDATE消息);

(2)接收Leader消息并进行处理;

(3)接收Client的请求,如果为写请求,发送给Leader进行投票;
(4)返回Client结果。

5.Follower的消息循环处理如下几种来自Leader的消息:

(1)PING消息: 心跳消息;

(2)PROPOSAL消息:Leader发起的提案,要求Follower投票;

(3)COMMIT消息:服务器端最新一次提案的信息;

(4)UPTODATE消息:表明同步完成;

(5)REVALIDATE消息:根据LeaderREVALIDATE结果,关闭待revalidatesession还是允许其接受消息;
(6)SYNC消息:返回SYNC结果到客户端,这个消息最初由客户端发起,用来强制得到最新的更新。

6Zxid解析


 

图示是通过zktools.exe软件查看的zk状态,图中:

1为:节点被创建时的事务ID

2为:节点最后一次被修改时的事务ID

3为:节点的子节点最后一次被更新时的事务ID,只有子节点列表变更才会更新pZxid,子节点内容变更不会更新pZxid

4为:子节点数量

7.数据一致性与paxos 算法

参考博客

8.Zookeeper的数据模型

维基中这样定义Zookeeper,ZooKever允许分布式进程通过数据寄存器的共享层次命名空间进行相互协调。这个命名空间和Unix文件系统非常相似,如图:


可以看到znode是分层次管理的,更像是一棵树,或者是标准的文件系统,一些关键点:

根结点只有一个child znode/zoo,反之,/zoo有三个child znode

ZooKeeper树中的每个结点都是以path为标识,并且path是通过/分割

znode被称为是数据寄存器,因为可以存储数据,因此一个znode可以是children也可以和数据相关联,就像文件系统中的文件可以是路径

znode中的数据通常以二进制的格式存储,并且znode的数据大小不能超过1MB,ZooKeeper被设计成用来协调的,并且所通知协调的数据量相对较小,所以对数据量的限制是强制的,强烈推荐实际数据量大小应小于这个限制。

9.Zookeeper的节点类型

Znode有四种形式的目录节点:PERSISTENT(持久的)、PHEMERAL(暂时的)PERSISTENT_SEQUENTIAL(持久化顺序编号目录节点)、EPHEMERAL_SEQUENTIAL(暂时化顺序编号目录节点

Znode有两种类型,短暂的(ephemeral)和持久的(persistent),Znode的类型在创建时确定并且之后不能再修改,短暂znode的客户端会话结束时,zookeeper会将该短暂znode删除,短暂znode不可以有子节点,持久znode不依赖于客户端会话,只有当客户端明确要删除该持久znode时才会被删除

正文到此结束