zookeeper安装及使用
什么是Zookeeper
根据官方介绍,Zookeeper提供配置信息管理,命名,分布式协作任务和组服务的中心式服务。主要用在分布式的环境中,简化分布式开发协作任务,让开发人员主要关注业务。
Zookeeper从文件系统的API得到启发,并提供一组API。Zookeeper的文件树结构如下图所示:Zookeeper的文件树结构

上图中的节点称为znode,采用类似文件系统的层次的树状结构进行元数据的管理。并且可以创建,删除节点或者更新节点内容。Zookeeper提供了四种不同类型的znode。
持久persistent节点:持久节点是非常有用的节点,即使创建者不属于应用系统了,节点数据也不会丢失。节点只能通过删除才能清除。持久有序persistent-sequential节点:一个有序节点被分配一个唯一的单调递增的整数,当节点被创建时,会在路径后面追加一个序号。临时ephemeral节点:当创建此节点的客户端会话超时或者主动关闭时,节点会被删除。可以像持久节点一样被主动删除。临时有序ephemeral-sequential节点:一个有序节点被分配一个唯一的单调递增的整数,当节点被创建时,会在路径后面追加一个序号。但像临时节点一样超时或者主动关闭时,节点会被删除。Zookeeper使用监视和通知模式避免客户端轮询获取节点变更。我们创建一个监听,当阶段更新或者删除时,会通知客户端。
Zookeeper不适用的场景
Zookeeper管理协作的关键数据,不适合管理海量的数据存储。我们应该把存储数据和协作数据分开管理,充分发挥zookeeper的优势。
Zookeeper架构
应用通过客户端对zookeeper服务实现调用,应用程序通过客户端和zookeeper服务器之间进行交互。Zookeeper架构

Zookeeper有两种运行模式,一种是独立模式,另一种是仲裁模式。独立模式就是一个独立的服务器,就像一个单体系统一样。仲裁模式下,数据在各个服务器中进行复制。在仲裁模式下需要一个法人数量,就是使zookeeper能正常工作的最小服务器的数量。通常用多数原则,最小法人数量大于总数量的一半。
建议集群的数量为奇数,比如5个,法人数量为3,可以运行崩溃的服务器为2,如果集群为4,法人数量也是3,此时允许服务器崩溃的服务器为1个,系统会更脆弱。
Zookeeper会话,在客户端和服务器进行连接是需要创建一个会话,客户端和服务器的所有操作都建立在这个会话的基础之上。
客户端在创建一个zookeeper句柄时,就会建立一个会话,客户端通过TCP协议和服务器进行通信。当客户端连接的当前服务器故障时,会话会转移到其他服务器上,应用并不需要知道。
下载
Apache官方最新版本为:3.4.8下载地址:http://mirrors.cnnic.cn/apache/zookeeper/zookeeper-3.4.8/zookeeper-3.4.8.tar.gz
安装过程
1.下载
wget https://mirrors.bfsu.edu.cn/apache/zookeeper/zookeeper-3.6.3/apache-zookeeper-3.6.3-bin.tar.gz
2.解压
tar -zxvf zookeeper-3.4.14.tar.gz
3.做软连接,方便后期更新版本
ln -s zookeeper-3.4.14 zookeeper
4.进入conf目录,创建一个zookeeper的配置文件zoo.cfg,可复制conf/zoo_sample.cfg作为配置文件
cp zoo_sample.cfg zoo.cfg
配置文件说明:见补充说明
5.
6.
3.5之后增加Zookeeper AdminServe,默认使用8080端口
有一个坑,AdminServer端口默认使用8080,
要么更换端口,可以在zoo.cfg中添加配置admin.serverPort=没有占用的端口
要么启动脚本中增加-Dzookeeper.admin.enableServer=false
7.
zoo.cfg配置文件详解
1 # The number of milliseconds of each tick
2 # tickTime:CS通信心跳数
3 # Zookeeper 服务器之间或客户端与服务器之间维持心跳的时间间隔,也就是每个 tickTime 时间就会发送一个心跳。tickTime以毫秒为单位。
4 tickTime=2000
5
6 # The number of ticks that the initial
7 # synchronization phase can take
8 # initLimit:LF初始通信时限
9 # 集群中的follower服务器(F)与leader服务器(L)之间初始连接时能容忍的最多心跳数(tickTime的数量)。
10 initLimit=5
11
12 # The number of ticks that can pass between
13 # sending a request and getting an acknowledgement
14 # syncLimit:LF同步通信时限
15 # 集群中的follower服务器与leader服务器之间请求和应答之间能容忍的最多心跳数(tickTime的数量)。
16 syncLimit=2
17
18 # the directory where the snapshot is stored.
19 # do not use /tmp for storage, /tmp here is just
20 # example sakes.
21 # dataDir:数据文件目录
22 # Zookeeper保存数据的目录,默认情况下,Zookeeper将写数据的日志文件也保存在这个目录里。
23 dataDir=/data/soft/zookeeper-3.4.12/data
24
25
26 # dataLogDir:日志文件目录
27 # Zookeeper保存日志文件的目录。
28 dataLogDir=/data/soft/zookeeper-3.4.12/logs
29
30 # the port at which the clients will connect
31 # clientPort:客户端连接端口
32 # 客户端连接 Zookeeper 服务器的端口,Zookeeper 会监听这个端口,接受客户端的访问请求。
33 clientPort=2181
34
35 # the maximum number of client connections.
36 # increase this if you need to handle more clients
37 #maxClientCnxns=60
38 #
39 # Be sure to read the maintenance section of the
40 # administrator guide before turning on autopurge.
41 #
42 # http://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance
43 #
44 # The number of snapshots to retain in dataDir
45 #autopurge.snapRetainCount=3
46 # Purge task interval in hours
47 # Set to "0" to disable auto purge feature
48 #autopurge.purgeInterval=1
49
50
51 # 服务器名称与地址:集群信息(服务器编号,服务器地址,LF通信端口,选举端口)
52 # 这个配置项的书写格式比较特殊,规则如下:
53
54 # server.N=YYY:A:B
55
56 # 其中N表示服务器编号,YYY表示服务器的IP地址,A为LF通信端口,表示该服务器与集群中的leader交换的信息的端口。B为选举端口,表示选举新leader时服务器间相互通信的端口(当leader挂掉时,其余服务器会相互通信,选择出新的leader)。一般来说,集群中每个服务器的A端口都是一样,每个服务器的B端口也是一样。但是当所采用的为伪集群时,IP地址都一样,只能时A端口和B端口不一样。
启动服务/etc/systemd/system/zookeeper.service
[Unit]
Description=ZooKeeper Service
Documentation=http://zookeeper.apache.org
Requires=network.target
After=network.target
[Service]
Type=forking
User=zookeeper
Group=zookeeper
Environment=JAVA_HOME=/usr/local/jdk
ExecStart=/home/zookeeper/zookeeper/bin/zkServer.sh start
ExecStop=/home/zookeeper/zookeeper/bin/zkServer.sh stop
ExecReload=/home/zookeeper/zookeeper/bin/zkServer.sh restart
WorkingDirectory=/home/zookeeper/zookeeper
[Install]
WantedBy=default.target
数据与存储
事物日志
ZooKeeper 在运行过程中会在事物日志目录下建立一个名字为version-2的子目录,该目录确定了当前ZooKeeper使用的事务日志格式版本号。也就足说,等到下次某个ZooKeeper版本对事务日志格式进行变更时,这个目录也会有所变更

日志写入
1.创建事物日志文件
当ZooKeeper服务器启动完成需要进行第一次事务日志的写入,或是上一个事务日志写满的时候,都会处于与事务日志文件断开的状态,即ZooKeeper服务器没有和任意一个日志文件相关联。因此,在进行事务日志写入前,Zookeeper首先会判断FileTxnLog组件是否已经关联了一个可写的事物日志文件。如果没有关联上事务日志文件,那么就会使用与该事务操作关联的ZXID作为后缀创建一个事物日志文件,同时构建事务日志文件头信息(包含魔数magic、事务日志格式版本version和dbid),并立即写入这个事物日志文件中去。
2.确定事务日志文件是否需要扩容(预分配)。
当检测到当前务日志文件剩余空间不足4096字节(4KB)时,就会开始进行文件空间扩容。在现行文件大小的基础上,将文件大小增加65536KB (64MB),然后使用“0”(\0)填充这些被扩容的文件空间。
那么ZooKeeper为什么要进行事务日志文件的磁盘空间预分配呢?对客户端的每一次事务操作,ZooKeeper都会将其写人事务日志文件中。因此,事物日志的写入性能直接决定了ZooKeeper服务器对事务请求的响应,也就是说,事务写入近似可以被看作是一个磁盘I/O的过程。严格地讲,文件的不断追加写入操作会触发底层磁盘I/O为文件开辟新的磁盘块,即磁盘Seek。因此,为了避免磁盘Seek的频率,提高磁盘I/O的效率,ZooKeeper在创建事物日志的时候就会进行文件空间“预分配”——在文件创建之初就向操作系统预分配一个很大的磁盘块,默认是64MB,而一旦已分配的文件空间不足4KB 时,那么将会再次“预分配”,以避免随着每次事物的写入过程中文件大小增长带来的Seek开销,直至创建新的事务日志。
3.事物序列化
事务序列化包括对事务头和事务体的序列化,分别是对TxnHeader(事务头)和Record (事务体)的序列化。其中事务体又可分为会话创建事务(CreateSessionTxn)、节点创建事务(CreateTxn)、节点删除事务(DeleteTxn)和节点数据更新事务 (SetDataTxn)等。
4.生成Checksum。
为了保证事务日志文件的完整性和数据的准确性,ZooKeeper在将事务日志写入文件前,会根据步骤3中序列化产生的字节数组来计算Checksum。ZooKeeper默认使用Adler32算法来计算Checksum值。
5.写入事务日志文件流。
将序列化后的事务头、事务体及Checksum值写入到文件流中去。此时由于 ZooKeeper使用的足BufferedOutputStream,因此写人的数据并非真正被写入到磁盘文件上。
6.事务日志刷入磁盘。
在步骤5中,已经将事务操作写人文件流中,但是由于缓存的原因,无法实时地写入磁盘文件中,因此我们需要将缓存数据强制刷入磁盘中,在步骤1中我们已经将每个事物日志文件对应的文件流放入streamsToFlush,因此这里会从 streamsToFlush 中提取出文件流,并调用FileChannel.force(boolean metaData)接口来强制将数据刷入磁盘文件中去。force接口对应的其实是底层的fsync接口,是一个比较耗费磁盘I/O资源的接口,因此ZooKeeper允许用户控制是否需要主动调用该接口,以通过系统属性zookeeper.forceSync来设置。
初始化
数据加载的整体流程
快照文件加载流程
参考文章
Last updated