> For the complete documentation index, see [llms.txt](https://darren.gitbook.io/project/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://darren.gitbook.io/project/devops/zookeeper-an-zhuang-ji-shi-yong.md).

# zookeeper安装及使用

## 什么是Zookeeper

根据官方介绍，Zookeeper提供配置信息管理，命名，分布式协作任务和组服务的中心式服务。主要用在分布式的环境中，简化分布式开发协作任务，让开发人员主要关注业务。

Zookeeper从文件系统的API得到启发，并提供一组API。Zookeeper的文件树结构如下图所示：![](https://pics4.baidu.com/feed/8cb1cb1349540923959153575970580fb2de49c9.jpeg?token=33fe4f82278c338e8d567f0356c59ed6)Zookeeper的文件树结构

![](https://55104664-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LFq8o_9x6ob05ww-sNi%2F-M5PPeZrG9-K2JgD5K-V%2F-M5PQlNVrw-qOhQsffz_%2Fimage.png?alt=media\&token=176f3a15-d62e-4d98-b5a7-124d10a24376)

上图中的节点称为znode，采用类似文件系统的层次的树状结构进行元数据的管理。并且可以创建，删除节点或者更新节点内容。Zookeeper提供了四种不同类型的znode。

持久persistent节点：持久节点是非常有用的节点，即使创建者不属于应用系统了，节点数据也不会丢失。节点只能通过删除才能清除。持久有序persistent-sequential节点：一个有序节点被分配一个唯一的单调递增的整数，当节点被创建时，会在路径后面追加一个序号。临时ephemeral节点：当创建此节点的客户端会话超时或者主动关闭时，节点会被删除。可以像持久节点一样被主动删除。临时有序ephemeral-sequential节点：一个有序节点被分配一个唯一的单调递增的整数，当节点被创建时，会在路径后面追加一个序号。但像临时节点一样超时或者主动关闭时，节点会被删除。Zookeeper使用监视和通知模式避免客户端轮询获取节点变更。我们创建一个监听，当阶段更新或者删除时，会通知客户端。

## Zookeeper不适用的场景

Zookeeper管理协作的关键数据，不适合管理海量的数据存储。我们应该把存储数据和协作数据分开管理，充分发挥zookeeper的优势。

## Zookeeper架构

应用通过客户端对zookeeper服务实现调用，应用程序通过客户端和zookeeper服务器之间进行交互。![](https://pics3.baidu.com/feed/2cf5e0fe9925bc31aca2637694f704b7ca137014.jpeg?token=760f4c60045a884a228a0ded2df0e2a6)Zookeeper架构

![](https://55104664-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LFq8o_9x6ob05ww-sNi%2F-M5PPeZrG9-K2JgD5K-V%2F-M5PQf5fsu0e4jlQzSpB%2Fimage.png?alt=media\&token=61d23526-77b2-4691-af2d-2bc19657b3c2)

Zookeeper有两种运行模式，一种是独立模式，另一种是仲裁模式。独立模式就是一个独立的服务器，就像一个单体系统一样。仲裁模式下，数据在各个服务器中进行复制。在仲裁模式下需要一个法人数量，就是使zookeeper能正常工作的最小服务器的数量。通常用多数原则，最小法人数量大于总数量的一半。

建议集群的数量为奇数，比如5个，法人数量为3，可以运行崩溃的服务器为2，如果集群为4，法人数量也是3，此时允许服务器崩溃的服务器为1个，系统会更脆弱。

Zookeeper会话，在客户端和服务器进行连接是需要创建一个会话，客户端和服务器的所有操作都建立在这个会话的基础之上。

客户端在创建一个zookeeper句柄时，就会建立一个会话，客户端通过TCP协议和服务器进行通信。当客户端连接的当前服务器故障时，会话会转移到其他服务器上，应用并不需要知道。

## 下载

&#x20;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版本对事务日志格式进行变更时，这个目录也会有所变更

![](https://55104664-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LFq8o_9x6ob05ww-sNi%2F-M5PeLQDMqedtjW9EfO9%2F-M5QYSkMpOUqJdn-uJcn%2Fimage.png?alt=media\&token=b2f14e84-74be-4a44-9a38-6c59451523cc)

**日志写入**

&#x20;       1.创建事物日志文件

&#x20;       当ZooKeeper服务器启动完成需要进行第一次事务日志的写入，或是上一个事务日志写满的时候，都会处于与事务日志文件断开的状态，即ZooKeeper服务器没有和任意一个日志文件相关联。因此，在进行事务日志写入前，Zookeeper首先会判断FileTxnLog组件是否已经关联了一个可写的事物日志文件。如果没有关联上事务日志文件，那么就会使用与该事务操作关联的ZXID作为后缀创建一个事物日志文件，同时构建事务日志文件头信息（包含魔数magic、事务日志格式版本version和dbid)，并立即写入这个事物日志文件中去。

&#x20;       2.确定事务日志文件是否需要扩容（预分配)。

当检测到当前务日志文件剩余空间不足4096字节(4KB)时，就会开始进行文件空间扩容。在现行文件大小的基础上，将文件大小增加65536KB (64MB),然后使用“0”(\0）填充这些被扩容的文件空间。

&#x20;       那么ZooKeeper为什么要进行事务日志文件的磁盘空间预分配呢？对客户端的每一次事务操作，ZooKeeper都会将其写人事务日志文件中。因此，事物日志的写入性能直接决定了ZooKeeper服务器对事务请求的响应，也就是说，事务写入近似可以被看作是一个磁盘I/O的过程。严格地讲，文件的不断追加写入操作会触发底层磁盘I/O为文件开辟新的磁盘块，即磁盘Seek。因此，为了避免磁盘Seek的频率，提高磁盘I/O的效率，ZooKeeper在创建事物日志的时候就会进行文件空间“预分配”——在文件创建之初就向[操作系统](http://lib.csdn.net/base/operatingsystem)预分配一个很大的磁盘块，默认是64MB,而一旦已分配的文件空间不足4KB 时，那么将会再次“预分配”，以避免随着每次事物的写入过程中文件大小增长带来的Seek开销，直至创建新的事务日志。

&#x20;       3.事物序列化

&#x20;       事务序列化包括对事务头和事务体的序列化，分别是对TxnHeader(事务头)和Record (事务体）的序列化。其中事务体又可分为会话创建事务（CreateSessionTxn)、节点创建事务（CreateTxn)、节点删除事务（DeleteTxn)和节点数据更新事务 (SetDataTxn)等。

&#x20;       4.生成Checksum。

&#x20;       为了保证事务日志文件的完整性和数据的准确性，ZooKeeper在将事务日志写入文件前，会根据步骤3中序列化产生的字节数组来计算Checksum。ZooKeeper默认使用Adler32[算法](http://lib.csdn.net/base/datastructure)来计算Checksum值。

&#x20;       5.写入事务日志文件流。

&#x20;       将序列化后的事务头、事务体及Checksum值写入到文件流中去。此时由于 ZooKeeper使用的足BufferedOutputStream，因此写人的数据并非真正被写入到磁盘文件上。

&#x20;       6.事务日志刷入磁盘。

&#x20;       在步骤5中，已经将事务操作写人文件流中，但是由于缓存的原因，无法实时地写入磁盘文件中，因此我们需要将缓存数据强制刷入磁盘中，在步骤1中我们已经将每个事物日志文件对应的文件流放入streamsToFlush,因此这里会从 streamsToFlush 中提取出文件流，并调用FileChannel.force(boolean metaData)接口来强制将数据刷入磁盘文件中去。force接口对应的其实是底层的fsync接口，是一个比较耗费磁盘I/O资源的接口，因此ZooKeeper允许用户控制是否需要主动调用该接口，以通过系统属性zookeeper.forceSync来设置。

#### 初始化

&#x20;       数据加载的整体流程

![](https://img-blog.csdn.net/20161217154647391)

&#x20;       快照文件加载流程

![](https://img-blog.csdn.net/20161217154727876)

![](https://img-blog.csdn.net/20161217154817681)

## 参考文章

### [一文入门Apache ZooKeeper](https://baijiahao.baidu.com/s?id=1664379324795538778\&wfr=spider\&for=pc)

### [数据存储](https://blog.csdn.net/abountwinter/article/details/55188820)
