一、安装配置zookeeper单节点模式
Zookeeper分布式服务框架,是Apache Hadoop的一个子项目,它主要是用来解决分布式应用中经常遇到的一些数据管理问题,如:统一命名服务、状态同步服务、集群管理、分布式应用配置项的管理等。
安装Zookeeper的单机模式非常简单。服务包含在一个单独的压缩文件中,所以安装只需要创建配置文件。一旦你下载了一个Zookeeper的稳定的发布版本之后,解压并进入根目录。启动Zookeeper之前需要一个配置文件。
首先安装开发工具及openjdk,zookeeper是由Java语言开发的,所以需要openjdk环境。
1 2 3 |
$ yum groupinstall "Development tools" "Compatibility libraries" -y $ yum install openssl-devel openssl -y $ yum install java-1.8.0-openjdk-devel java-1.8.0-openjdk -y |
确定Java运行环境正常
1 2 3 4 |
$ java -version openjdk version "1.8.0_101" OpenJDK Runtime Environment (build 1.8.0_101-b13) OpenJDK 64-Bit Server VM (build 25.101-b13, mixed mode) |
设置Java堆大小。这对于避免swapping非常重要,它将严重降低Zookeeper的性能。确定正确值,使用负载测试,并确定低于引起swap的的限制。保守的 – 对于4GB的机器使用最大堆大小3GB。
安装二进制版本的zookeeper
1 2 3 4 |
$ tar xvf zookeeper-3.4.9.tar.gz -C /usr/local/ $ ln -s /usr/localzookeeper-3.4.9/ /usr/local/zookeeper $ cd /usr/local/zookeeper/conf $ cp zoo_sample.cfg zoo.cfg |
编辑zookeeper配置文件/usr/local/zookeeper/conf/zoo.cfg
1 2 3 4 5 |
maxClientCnxns=60 tickTime=2000 dataDir=/data/zookeeper/db dataLogDir=/data/zookeeper/log clientPort=2181 |
1 |
$ mkdir /data/zookeeper/{db,log} -p |
参数解释:
tickTime:Zookeeper使用的基本时间,时间单位为毫秒。它用于心跳机制,并且设置最小的session超时时间为两倍心跳时间
dataDir:用于存储ZK的快照文件(snapshot)。另外,默认情况下,ZK的事务日志也会存储在这个目录中。在完成若干次事务日志之后(在ZK中,凡是对数据有更新的操作,比如创建节点,删除节点或是对节点数据内容进行更新等,都会记录事务日志),ZK会触发一次快照(snapshot),将当前server上所有节点的状态以快照文件的形式dump到磁盘上去,即snapshot文件。这里的若干次事务日志是可以配置的,默认是100000,具体参看下文中关于配置参数“snapCount”的介绍。
dataLogDir:日志保存目录。Zookeeper的日志信息使用log4j。更多详细信息可以在开发人员指南的 Logging 模块获取。你可以根据log4j的配置进入控制台查看日志信息(或日志文件)。
clientPort:监听客户端连接的端口。
然后输出环境变量。
1 2 |
$ export PATH=$PATH:/usr/local/zookeeper/bin/ $ source /etc/profile |
然后就可以启动zookeeper了。
1 |
$ zkServer.sh start |
查看但实例zookeeper节点的状态。
1 2 |
$ zkServer.sh status Mode: standlone |
客户端连接,可以查看相关信息。
1 |
$ zkCli.sh -server 127.0.0.1:2181 |
至此,单实例zookeeper已经搞定了。这里列出了Zookeeper独立运行模式的步骤,没有主从复制,所以如果Zookeeper进程故障,服务就会停止。这对于大多数的开发情况是可以的,但生产是不建议的,所以下面就介绍zk的集群模式。
二、安装配置zookeeper集群模式
运行Zookeeper的独立模式方便评估、开发和测试。但是在生产中,对于可靠的Zookeeper服务,你应该部署Zookeeper在一个集群环境里。只要集群的多数服务可用,集群服务就是可用的。因为Zookeeper要求一个大多数,进群最好使用奇数个机器。例如,4台主机的Zookeeper只可以处理单机的故障;如果两个主机故障,剩下的两个机器不能成为大多数。然而,5台主机的Zookeeper可以处理两个机器的故障。
所有服务器有相同的配置文件副本,配置文件和使用独立模式相似,但有一点点的区别,如:
1 2 3 4 5 6 7 8 9 10 11 |
maxClientCnxns=60 tickTime=2000 initLimit=10 syncLimit=5 dataDir=/data/zookeeper/db dataLogDir=/data/zookeeper/log clientPort=2181 # cluster configure server.1=10.0.60.152:2888:3888 server.2=10.0.60.153:2888:3888 server.3=10.0.60.154:2888:3888 |
1 |
$ mkdir /data/zookeeper/{db,log} -p |
新的条目,initLimit是Zookeeper用它来限定quorum中的Zookeeper服务器连接到Leader的超时时间。syncLimit限制了一个服务器从Leader多长时间超时。使用这两种超时,你指定的时间单位使用tickTime.在这个例子中,initLimit的超时时间是5个标记号,2000毫秒一个标记,就是10秒。
条目server.x列出了构成Zookeeper服务的服务器,当服务启动时,它通过查找data目录中的myid文件知道是哪个服务,这个myid个文件包含了服务号,用ASCII。
最后,注意每个服务器名称(用IP或主机名,最好使用主机名,然后再hosts文件中做绑定)后面的两个端口号:”2888″和”3888″。其中2888表示zookeeper程序监听端口,3888表示zookeeper选举通信端口。节点使用前面的端口连接到其他节点。这样的一个连接非常重要,以便于节点之间可以通讯,例如,对更新的顺序取得统一的意见。更具体的说,一个Zookeeper的服务器用这个端口连接follower到leader。当一个新的leader产生时,follower使用这个端口打开一个TCP连接,连接到leader。因为默认的leader选举也使用TCP。我们现在需要另一个端口用来leader选举。这是在服务器条目的第二个端口。
下面需要生成ID,这里需要注意,myid对应的zoo.cfg的server.ID,比如第二台zookeeper主机对应的myid应该是2,以此类推,三个主机分别为:
1 2 3 |
10.0.60.152$ echo 1 > /data/zookeeper/db/myid 10.0.60.153$ echo 2 > /data/zookeeper/db/myid 10.0.60.154$ echo 3 > /data/zookeeper/db/myid |
然后输出环境变量。
1 2 |
$ export PATH=$PATH:/usr/local/zookeeper/bin/ $ source /etc/profile |
然后就可以在集群中的每个主机上启动zookeeper了。
1 |
$ zkServer.sh start |
查看各个zookeeper节点的状态(会有一个leader节点,两个follower节点)。
1 2 3 4 5 6 |
[root@node1 ~]# zkServer.sh status Mode: follower [root@node2 ~]# zkServer.sh status Mode: leader [root@node3 ~]# zkServer.sh status Mode: follower |
客户端连接,可以查看相关信息。
1 |
$ zkCli.sh -server 127.0.0.1:2181 |
至此,zookeeper集群模式已经搞定了。
三、ZooKeeper操作命令
从shell脚本,输入help,可以获取一个从客户端可执行的命令列表:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
[zk: 127.0.0.1(CONNECTED) 0] help ZooKeeper -server host:port cmd args stat path [watch] set path data [version] ls path [watch] delquota [-n|-b] path ls2 path [watch] setAcl path acl setquota -n|-b val path history redo cmdno printwatches on|off delete path [version] sync path listquota path rmr path get path [watch] create [-s] [-e] path data acl addauth scheme auth quit getAcl path close connect host:port |
从这里,你可以尝试一些简单的命令行接口找到一些感觉。第一,通过发行的列表命令开始,像ls :
1 2 |
[zk: 127.0.0.1(CONNECTED) 1] ls / [zookeeper] |
下一步,通过运行create /zk_test my_data,创建一个新的znode。这将创建一个新的znode节点和一个相关联的字符串”my_data”:
1 2 |
[zk: 127.0.0.1(CONNECTED) 2] create /zk_test my_data Created /zk_test |
使用 ls / 命令查看目录:
1 2 |
[zk: 127.0.0.1(CONNECTED) 3] ls / [zookeeper, zk_test] |
注意zk_test目录已经被创建了。
接下来,使用get命令验证数据是否与znode关联上了:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
[zk: 127.0.0.1(CONNECTED) 4] get /zk_test my_data cZxid = 0x50000001a ctime = Thu Oct 27 14:33:47 CST 2016 mZxid = 0x50000001a mtime = Thu Oct 27 14:33:47 CST 2016 pZxid = 0x50000001a cversion = 0 dataVersion = 0 aclVersion = 0 ephemeralOwner = 0x0 dataLength = 7 numChildren = 0 |
Zookeeper中的每个znode的stat机构都由下面的字段组成:
czxid – 引起这个znode创建的zxid。
mzxid – znode最后更新的zxid。
ctime – znode被创建的毫秒数(从1970年开始)。
mtime – znode最后修改的毫秒数(从1970年开始)。
version – znode数据变化号。
cversion – znode子节点变化号。
aversion – znode访问控制列表的变化号。
ephemeralOwner – 如果是临时节点这个是znode拥有者的session id。如果不是临时节点则是0。
dataLength – znode的数据长度。
numChildren – znode子节点数量。
我们可以是用set命令改变数据与zk_test的关联,像:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
[zk: 127.0.0.1(CONNECTED) 5] set /zk_test junk cZxid = 0x50000001a ctime = Thu Oct 27 14:33:47 CST 2016 mZxid = 0x50000001b mtime = Thu Oct 27 14:35:20 CST 2016 pZxid = 0x50000001a cversion = 0 dataVersion = 1 aclVersion = 0 ephemeralOwner = 0x0 dataLength = 4 numChildren = 0 [zk: 127.0.0.1(CONNECTED) 6] get /zk_test junk cZxid = 0x50000001a ctime = Thu Oct 27 14:33:47 CST 2016 mZxid = 0x50000001b mtime = Thu Oct 27 14:35:20 CST 2016 pZxid = 0x50000001a cversion = 0 dataVersion = 1 aclVersion = 0 ephemeralOwner = 0x0 dataLength = 4 numChildren = 0 |
(注意我们使用get在setting data之后,并且它确实改变了。)
最后,让我们delete节点:
1 2 3 |
[zk: 127.0.0.1(CONNECTED) 7] delete /zk_test [zk: 127.0.0.1(CONNECTED) 8] ls / [zookeeper] |
四、Zookeeper命令:四个字母的单词
Zookeeper支持一小组命令,每个命令由四个字母组成,你通过telnet或nc在客户端端口发行命令到Zookeeper。
三个比较有趣的命令:”stat”给出了服务器和连接的客户端的一般信息,”srvr”和”cons”分别提供服务器上和连接的扩展细节。
conf:3.3.0加入,打印服务配置详情。
cons:3.3.0加入,列出所有连接到这个服务器的客户端完整的connection/session详情。包括接收/发送数据包的数量,session id,操作延迟,最后执行的操作,等等。
crst:3.3.9加入,重置所有连接的统计信息。
dump:列出未交付的session和临时节点,这只适用于领导者。
envi:打印服务环境详情。
ruok:测试服务器是否正常运行在一个无错误状态,如果正在运行服务器回复imok,否则不响应。”imok”的回复并不表明服务器已经加入quorum,至少说服务进程是活动的并绑定到指定的客户端端口。使用”stat”获取详细信息。
srst:重置服务器统计。
srvr:3.3.0加入,列出服务器的完整详情。
stat:列出服务器和连接的客户端摘要信息。
wchs:3.3.0加入,列出服务器watch的摘要信息。
wchc:3.3.0加入,列出服务器watch的详细信息,通过session。这个输出相关watch的session清单。注意,这个选项取决于watch的数量可能开销比较大,使用时要小心。
wchp:3.3.0加入,列出服务器watch的详细信息,通过路径。这个输出相关session的路径清单。注意,这个选项取决于watch的数量可能开销比较大,使用时要小心。
mntr:3.4.0加入,输出可以监控集群状态的变量清单。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
$ echo mntr | nc localhost 2181 zk_version 3.4.9 zk_avg_latency 0 zk_max_latency 0 zk_min_latency 0 zk_packets_received 70 zk_packets_sent 69 zk_outstanding_requests 0 zk_server_state leader zk_znode_count 4 zk_watch_count 0 zk_ephemerals_count 0 zk_approximate_data_size 27 zk_followers 4 - only exposed by the Leader zk_synced_followers 4 - only exposed by the Leader zk_pending_syncs 0 - only exposed by the Leader zk_open_file_descriptor_count 23 - only available on Unix platforms zk_max_file_descriptor_count 1024 - only available on Unix platforms |
五、数据文件管理
- 数据目录
ZK的数据目录包含两类文件:
myid – 这个文件只包含一个数字,和server id对应。
snapshot – 按zxid先后顺序的生成的数据快照。
集群中的每台ZK server都会有一个用于惟一标识自己的id,有两个地方会使用到这个id:myid文件和zoo.cfg文件中。myid文件存储在dataDir目录中,指定了当前server的server id。在zoo.cfg文件中,根据server id,配置了每个server的ip和相应端口。Zookeeper启动的时候,读取myid文件中的server id,然后去zoo.cfg 中查找对应的配置。
ZK在进行数据快照过程中,会生成snapshot文件,存储在dataDir目录中。文件后缀是zxid,也就是事务id。(这个zxid代表了zk触发快照那个瞬间,提交的最后一个事务id)。注意,一个快照文件中的数据内容和提交第zxid个事务时内存中数据近似相同。仅管如此,由于更新操作的幂等性,ZK还是能够从快照文件中恢复数据。数据恢复过程中,将事务日志和快照文件中的数据对应起来,就能够恢复最后一次更新后的数据了。
- 事务日志目录
dataLogDir目录是ZK的事务日志目录,包含了所有ZK的事务日志。正常运行过程中,针对所有更新操作,在返回客户端“更新成功”的响应前,ZK会确保已经将本次更新操作的事务日志写到磁盘上,只有这样,整个更新操作才会生效。每触发一次数据快照,就会生成一个新的事务日志。事务日志的文件名是log.,zxid是写入这个文件的第一个事务id。
- 文件管理
不同的zookeeper server生成的snapshot文件和事务日志文件的格式都是一致的(无论是什么环境,或是什么样的zoo.cfg 配置)。因此,如果某一天生产环境中出现一些古怪的问题,你就可以把这些文件下载到开发环境的zookeeper中加载起来,便于调试发现问题,而不会影响生产运行。另外,使用这些较旧的snapshot和事务日志,我们还能够方便的让ZK回滚到一个历史状态。
另外,ZK提供的工具类LogFormatter能够帮助可视化ZK的事务日志,帮助我们排查问题,关于事务日志的可以化,请查看这个文章《可视化zookeeper的事务日志》。
需要注意的一点是,zookeeper在运行过程中,不断地生成snapshot文件和事务日志。考虑到ZK运行环境的差异性,以及对于这些历史文件,不同的管理员可能有自己的用途(例如作为数据备份),因此默认ZK是不会自动清理快照和事务日志,需要交给管理员自己来处理。ZK本身只需要使用最新的snapshot和事务日志即可。
六、要避免的事情
下面的问题可以通过Zookeeper正确的配置避免:
- 不一致的服务器清单
客户端使用的Zookeeper服务器清单必须和每个Zookeeper服务的一致。每个Zookeeper服务配置文件的服务器清单应该和其他的一致。
- 不正确的事务日志放置
Zookeeper性能的关键部分是事务日志。对于每个更新操作,ZK都会在确保事务日志已经落盘后,才会返回客户端响应。因此事务日志的输出性能在很大程度上影响ZK的整体吞吐性能。强烈建议是给事务日志的输出分配一个单独的磁盘。
- 不正确的Java堆大小
你应该特别注意正确的设置Java最大堆大小。特别的是,你不应该营造Zookeeper交换磁盘的情况。磁盘可让Zookeeper死亡。每个事情都是有序的,所以如果处理一个请求交换磁盘,所有其他队列里的请求很可能会做同样的事情。磁盘,不要SWAP。
保守估计:如果你有4G的RAM,不用设置Java最大堆大小为6G或4G。例如,4G的机器更建议你使用3G的堆,因为操作系统和缓存也需要内存。堆大小的最佳推荐,你的系统需要运行负载测试,然后确保使用在引起系统交换的限制以下。