Zookeeper 运维实践手册

 

Zookeeper是一个高可用的分布式数据管理与协调框架,该框架能很好地保证分布式环境中数据一致性。通常用来实现服务发现(相似DNS),配置管理,分布式锁,leader选举等。html

1、生产环境中Zookeeper安装部署规范java

生产环境建议zookeeper至少为三台集群,统一安装配置,版本号为近期新版本,好比版本为3.4.8
部署路径:/opt/业务模块名/zookeeper
配置文件:/opt/业务模块名/zookeeper/conf/zoo.cfg
存储快照文件snapshot的目录:/opt/业务模块名/zookeeper/data
事务日志输出目录:/var/log/业务模块名/zookeeper
运行日志输出目录:/var/log/业务模块名/zookeepernode

Zookeeper全部端口须要提早开通防火墙入站规则
对外服务端口:默认2181,可自定义
通讯端口:2888,可自定义
选举端口:3888,可自定义
autoperge默认关闭,建议自行编写脚本在业务低谷期清理快照和事务日志程序员

查询状态:sh /opt/业务模块名/zookeeper/zkServer.sh status
启动服务:sh /opt/业务模块名/zookeeper/zkServer.sh start
中止服务:sh /opt/业务模块名/zookeeper/zkServer.sh stopredis

配置文件conf/zoo.cfg示例以下:算法

# The number of milliseconds of each tick
tickTime=2000
# The number of ticks that the initial
# synchronization phase can take
initLimit=10
# The number of ticks that can pass between
# sending a request and getting an acknowledgement
syncLimit=5
# the directory where the snapshot is stored.
# do not use /tmp for storage, /tmp here is just
# example sakes.
dataDir=/opt/kevintest/zookeeper_22181/data
# the port at which the clients will connect
clientPort=22181
# the maximum number of client connections.
# increase this if you need to handle more clients
#maxClientCnxns=60
#
# Be sure to read the maintenance section of the
# administrator guide before turning on autopurge.
#
# http://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance
#
# The number of snapshots to retain in dataDir
#autopurge.snapRetainCount=3
# Purge task interval in hours
# Set to "0" to disable auto purge feature
#autopurge.purgeInterval=1
dataLogDir=/var/log/kevintest/zookeeper_22181
server.1=192.168.10.91:22888:23888
server.2=192.168.10.91:22988:23988
server.3=192.168.10.92:22888:23888

conf/log4j.properties配置示例以下:数据库

# Define some default values that can be overridden by system properties
zookeeper.root.logger=INFO,ROLLINGFILE
zookeeper.console.threshold=INFO
zookeeper.log.dir=.
zookeeper.log.file=zookeeper.log
zookeeper.log.threshold=DEBUG
zookeeper.tracelog.dir=.
zookeeper.tracelog.file=zookeeper_trace.log
 
#
# ZooKeeper Logging Configuration
#
 
# Format is "<default threshold> (, <appender>)+
 
# DEFAULT: console appender only
log4j.rootLogger=${zookeeper.root.logger}
 
# Example with rolling log file
#log4j.rootLogger=DEBUG, CONSOLE, ROLLINGFILE
 
# Example with rolling log file and tracing
#log4j.rootLogger=TRACE, CONSOLE, ROLLINGFILE, TRACEFILE
 
#
# Log INFO level and above messages to the console
#
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.Threshold=${zookeeper.console.threshold}
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} [myid:%X{myid}] - %-5p [%t:%C{1}@%L] - %m%n
 
#
# Add ROLLINGFILE to rootLogger to get log file output
#    Log DEBUG level and above messages to a log file
log4j.appender.ROLLINGFILE=org.apache.log4j.DailyRollingFileAppender
log4j.appender.ROLLINGFILE.Threshold=${zookeeper.log.threshold}
log4j.appender.ROLLINGFILE.File=${zookeeper.log.dir}/${zookeeper.log.file}
 
# Max log file size of 10MB
#log4j.appender.ROLLINGFILE.MaxFileSize=10MB
# uncomment the next line to limit number of backup files
log4j.appender.ROLLINGFILE.MaxBackupIndex=10
 
log4j.appender.ROLLINGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.ROLLINGFILE.layout.ConversionPattern=%d{ISO8601} [myid:%X{myid}] - %-5p [%t:%C{1}@%L] - %m%n
 
 
#
# Add TRACEFILE to rootLogger to get log file output
#    Log DEBUG level and above messages to a log file
log4j.appender.TRACEFILE=org.apache.log4j.FileAppender
log4j.appender.TRACEFILE.Threshold=TRACE
log4j.appender.TRACEFILE.File=${zookeeper.tracelog.dir}/${zookeeper.tracelog.file}
 
log4j.appender.TRACEFILE.layout=org.apache.log4j.PatternLayout
### Notice we are including log4j's NDC here (%x)
log4j.appender.TRACEFILE.layout.ConversionPattern=%d{ISO8601} [myid:%X{myid}] - %-5p [%t:%C{1}@%L][%x] - %m%n

conf/zookeeper-env.sh文件配置示例以下:apache

ZOO_LOG_DIR="/var/log/kevintest/zookeeper_22181/"
ZOO_LOG4J_PROP="INFO,ROLLINGFILE

2、Zookeeper最佳实践vim

必须了解如下ZK知识点,才能熟练地对ZK进行维护:
- zookeeper集群里分三种角色: Leader, Follower和Observer。Leader和Follower参与投票,Observer只会『听』投票的结果,不参与投票。
- 投票集群里的节点数要求是奇数
- 一个集群容忍挂掉的节点数的等式为 N = 2F + 1,N为投票集群节点数,F为能同时容忍失败节点数。好比一个三节点集群,能够挂掉一个节点,5节点集群能够挂掉两个...
- 一个写操做须要半数以上的节点ack,因此集群节点数越多,整个集群能够抗挂点的节点数越多(越可靠),可是吞吐量越差。
- Zookeeper里全部节点以及节点的数据都会放在内存里,造成一棵树的数据结构。而且定时的dump snapshot到磁盘。
- Zookeeper的Client与Zookeeper之间维持的是长链接,而且保持心跳,Client会与Zookeeper之间协商出一个Session超时时间出来(其实就是Zookeeper Server里配置了最小值,最大值,若是client的值在这两个值之间则采用client的,小于最小值就是最小值,大于最大值就用最大值),若是在Session超时时间内没有收到心跳,则该Session过时。
- Client能够watch Zookeeper那个树形数据结构里的某个节点或数据,当有变化的时候会获得通知。缓存

1)最小生产集群
要确保Zookeeper可以稳定运行,那么就须要确保投票可以正常进行,最好不要挂一个节点整个就不work了,因此咱们通常要求生产环境最少3个节点部署。

2)网络
除了节点外,还要看不能一台物理机器,一个机柜或一个交换机挂掉而后影响了整个集群,因此节点网络结构也要考虑,这个可能就比不少应用服务器的要求更加严格。

3)分Group,保护核心Group
要确保Zookeeper整个集群可靠运行,就是要确保投票集群可靠。那在咱们这里,将一个Zookeeper集群划分为多个小的Group,咱们称Leader+Follower为核心Group,核心Group咱们通常是不向外提供服务的,而后咱们会根据不一样的业务再加一些Observer,好比一个Zookeeper集群为服务发现,消息,定时任务三个不一样的组件提供服务,那么咱们为创建三个Observer Group,分别给这三个组件使用,而Client只会链接分配给它的Observer Group,不去链接核心Group。这样核心Group就不会给Client提供长链接服务,也不负责长链接的心跳,这大大的减轻了核心Group的压力,由于在实际环境中,一个Zookeeper集群要为上万台机器提供服务,维持长链接和心跳仍是要消耗必定的资源的。由于Observer是不参与投票的因此加Observer并不会下降总体的吞吐量,并且Observer挂掉不会影响整个集群的健康。

可是这里要注意的是,分Observer Group只能解决部分问题,由于毕竟全部的写入仍是要交给核心Group来处理的,因此对于写入量特别大的应用来讲,仍是须要进行集群上的隔离,好比Storm和Kafka就对Zookeeper压力比较大,你就不能将其与服务发现的集群放在一块儿。

4)内存
由于Zookeeper将全部数据都放在内存里,因此对JVM以及机器的内存也要预先计划,若是出现Swap那将严重的影响Zookeeper集群的性能,因此我通常不怎么推荐将Zookeeper用做通用的配置管理服务。由于通常配置数据仍是挺大的,这些所有放在内存里不太可控。

5)日志清理
由于Zookeeper要频繁的写txlog (Zookeeper写的一种顺序日志) 以及按期dump内存snapshot到磁盘,这样磁盘占用就愈来愈大,因此Zookeeper提供了清理这些文件的机制,可是这种机制并不太合理,它只能设置间隔多久清理,而不能设置具体的时间段。那么就有可能碰到高峰期间清理,因此建议将其关闭:autopurge.purgeInterval=0。而后使用crontab等机制,在业务低谷的时候清理。

6)日志,jvm配置
从官网直接下载的包若是直接启动运行是很糟糕的,这个包默认的配置日志是不会轮转的,并且是直接输出到终端。咱们最开始并不了解这点,而后运行一段时间后发现生成一个庞大的zookeeper.out的日志文件。除此以外,这个默认配置尚未设置任何jvm相关的参数(因此堆大小是个默认值),这也是不可取的。那么有的同窗说那我去修改Zookeeper的启动脚本吧。最好不要这样作,Zookeeper会加载conf文件夹下一个名为zookeeper-env.sh的脚本,因此你能够将一些定制化的配置写在这里,而不是直接去修改Zookeeper自带的脚本。

#!/usr/bin/env bash
ZOO_LOG_DIR=/var/log/kevintest/zookeeper_22181/       #日志文件放置的路径
ZOO_LOG4J_PROP="INFO,ROLLINGFILE"                     #设置日志轮转

新版本的zk中是用java.env这个参数文件来配置的

if [ -f "$ZOOCFGDIR/java.env" ]
then
    . "$ZOOCFGDIR/java.env"
fi

其中$ZOOCFGDIR/java.env就是设置jvm内存大小的文件,这个文件默认状况下是没有的,须要手动建立
vim /usr/local/services/zookeeper-3.4.8/conf/java.env

#!/bin/sh
export JAVA_HOME=/usr/java/jdk
# heap size MUST be modified according to cluster environment
export JVMFLAGS="-Xms4096m -Xmx4096m $JVMFLAGS"

7)地址
在实际环境中,咱们可能由于各类缘由好比机器过保,硬件故障等须要迁移Zookeeper集群,因此Zookeeper的地址是一个很头痛的事情。这个地址有两方面,第一个是提供给Client的地址,建议这个地址经过配置的方式下发,不要让使用方直接使用,这一点咱们前期作的很差。另一个是集群配置里,集群之间须要通信,也须要地址。咱们的处理方式是设置hosts:

192.168.1.20 zk1
192.168.1.21 zk2
192.168.1.22 zk3

在zoo.cfg配置里:

server.1=zk1:2081:3801
server.2=zk2:2801:3801
server.3=zk3:2801:3801

这样在须要迁移的时候,咱们停老的节点,起新的节点只须要修改hosts映射就能够了。好比如今server.3须要迁移,那咱们在hosts里将zk3映射到新的ip地址。可是对于java有一个问题是,java默认会永久缓存DNS cache,即便你将zk3映射到别的ip,若是并不重启server.1, server.2,它是不会解析到新的ip的,这个须要修改$JAVA_HOME/jre/lib/security/java.security文件里的networkaddress.cache.ttl=60,将其修改成一个比较小的数。对于这个迁移的问题,咱们还遇到一个比较尴尬的状况,在最后的坑里会有说起。

8)日志位置
Zookeeper主要产生三种IO: txlog(每一个写操做,包括新Session都会记录一条log),Snapshot以及运行的应用日志。通常建议将这三个IO分散到三个不一样的盘上。不过咱们却是一直没有这么实验过,咱们的Zookeeper也是运行在虚拟机(通常认为虚拟机IO较差)上。

9)监控
咱们对Zookeeper作了这样一些监控:
a)是否可写。 就是一个定时任务定时的去建立节点,删节点等操做。这里要注意的是Zookeeper是一个集群,咱们监控的时候我仍是但愿对单个节点作监控,因此这些操做的时候不要链接整个集群,而是直接去链接单个节点。
b)监控watcher数和链接数 特别是这两个数据有较大波动的时候,能够发现使用方是否有误用的状况
c)网络流量以及client ip 这个会记录到监控系统里,这样很快能发现『害群之马』

10)一些使用建议
a)不要强依赖Zookeeper,也就是Zookeeper出现问题业务已然能够正常运行。Zookeeper是一个分布式的协调框架,主要作的事情就是分布式环境的一致性。这是一个很是苛刻的事情,因此它的稳定性受不少方面的影响。好比咱们经常使用Zookeeper作服务发现,那么服务发现实际上是不须要严格的一致性的,咱们能够缓存server list,当Zookeeper出现问题的时候已然能够正常工做,在这方面etcd要作的更好一些,Zookeeper若是出现分区,少数派是不能提供任何服务的,读都不能够,而etcd的少数派仍然能够提供读服务,这在服务发现的时候仍是不错的。
b)不要将不少东西塞到Zookeeper里,这个上面已经提到过。
c)不要使用Zookeeper作细粒度锁,好比不少业务在订单这个粒度上使用Zookeeper作分布式锁,这会频繁的和Zookeeper交互,对Zookeeper压力较大,并且一旦出现问题影响面广。可是可使用粗粒度的锁(其实leader选举也是一种锁)。
d)不建议作通用配置的第二个理由是,通用配置要提供给特别多特别多系统使用,并且一些公共配置甚至全部系统都会使用,一旦这样的配置发生变动,Zookeeper会广播给全部的watcher,而后全部Client都来拉取,瞬间形成很是大的网络流量,引发所谓的『惊群』。而本身实现通用配置系统的时候,通常会对这种配置采起排队或分批通知的方式。

3、Zookeeper操做命令手册

1)Zookeeper客户端命令

zkCli.sh -server 127.0.0.1:2181
[zk: localhost:2182(CONNECTED) 0] help
ZooKeeper -server host:port cmd args
connect host:port
get path [watch]
ls path [watch]
set path data [version]
rmr path
delquota [-n|-b] path
quit
printwatches on|off
create [-s] [-e] path data acl
stat path [watch]
close
ls2 path [watch]
history
listquota path
setAcl path acl
getAcl path
sync path
redo cmdno
addauth scheme auth
delete path [version]
setquota -n|-b val path

登陆后命令行里面的一些简单操做以下:
- 显示根目录下、文件: ls / #使用 ls 命令来查看当前 ZooKeeper 中所包含的内容
- 显示根目录下、文件: ls2 / #查看当前节点数据并能看到更新次数等数据
- 建立文件,并设置初始内容: create /zk "test" #建立一个新的 znode节点“ zk ”以及与它关联的字符串
- 获取文件内容: get /zk #确认 znode 是否包含咱们所建立的字符串
- 修改文件内容: set /zk "zkbak" #对 zk 所关联的字符串进行设置
- 删除文件: delete /zk #将刚才建立的 znode 删除
- 退出客户端: quit
- 帮助命令: help

2)Zookeeper服务端命令
在准备好相应的配置以后,能够直接经过zkServer.sh 这个脚本进行服务的相关操做
- 启动ZK服务:  sh bin/zkServer.sh start
- 查看ZK服务状态:  sh bin/zkServer.sh status
- 中止ZK服务:  sh bin/zkServer.sh stop
- 重启ZK服务:  sh bin/zkServer.sh restart

3)Zookeeper经常使用四字命令
ZooKeeper 支持某些特定的四字命令字母与其的交互。它们大可能是查询命令,用来获取 ZooKeeper 服务的当前状态及相关信息。用户在客户端能够经过telnet 或 nc 向 ZooKeeper 提交相应的命令。命令语义:

 命令操做实例:(机器上要安装netcat-0.7.1-1.i386.rpm服务)

# echo stat |nc 127.0.0.1 2182      来查看哪一个节点被选择做为follower或者leader
# echo ruok |nc 127.0.0.1 2182      测试是否启动了该Server,若回复imok表示已经启动。
# echo dump | nc 127.0.0.1 2182     列出未经处理的会话和临时节点。
# echo kill | nc 127.0.0.1 2182     关掉server
# echo conf | nc 127.0.0.1 2182     输出相关服务配置的详细信息。
# echo cons | nc 127.0.0.1 2183     列出全部链接到服务器的客户端的彻底的链接 / 会话的详细信息。
# echo envi | nc 127.0.0.1 2182     输出关于服务环境的详细信息(区别于 conf 命令)。
# echo reqs | nc 127.0.0.1 2183     列出未经处理的请求。
# echo wchs | nc 127.0.0.1 2183     列出服务器 watch 的详细信息。
# echo wchc | nc 127.0.0.1 2183     经过 session 列出服务器 watch 的详细信息,它的输出是一个与 watch 相关的会话的列表。
# echo wchp | nc 127.0.0.1 2183     经过路径列出服务器 watch 的详细信息。它输出一个与 session 相关的路径。

4、Zookeeper运维手册

对于长期运行的ZooKeeper ensemble来讲, 运维工做是必须作的, 运维人员须要注意如下几点:

1)清理磁盘文件
ZooKeeper中有两处使用到了磁盘事务日志内存数据库快照.。ZooKeeper名称空间里的节点发生变动的时候, 就会有内容写入事务日志. 一般状况下, 当单个事务日志文件变的愈来愈大的时候, 事务日志就须要建立一个新的文件. 但在建立新的事务日志文件以前, ZooKeeper会先把当前的内存数据库的状态写入磁盘先作快照, 而后再生成一个新的事务日志文件. 这样就保证了快照文件和事务日志文件是一一对应的. 但快照落地须要时间, 在快照落地期间若是还有事务来临, 那么这部分事务的日志依然会写向旧的事务日志文件里. 这就致使, 快照文件对应的那个事务日志文件里, 存储的事务日志可能要比当前快照文件要新.

ZooKeeper server进程在默认启动的状况下, 是不会自动删除事务日志文件和快照文件的,固然这是可配置的, 配置项分别是 autopurge.snapRetainCount 和 autopurge.purgeInterval. 这两个配置项的具体含义在有详细描述. 但须要注意:若是你要这样作, 那么最好为每台部署的机器提供不一样的配置值, 除非这些机器的规格是彻底一摸同样的!

除过在配置文件中设定, 还有一种方法就是调用一个ZooKeeper提供的小工具, 大体以下:

java -cp zookeeper.jar:lib/log4j-1.2.15.jar:conf org.apache.zookeeper.server.PurgeTxnLog <dataDir> <snapDir> -n <count>

其中<dataDir>是事务日志的保存目录,<snapDir>是快照文件的保存目录,<count>是要保留的个数. 建议大于3.。运行该命令后, 除了最近的<count>对事务日志文件与快照文件, 其它文件都将被删除. 这是一个一次性命令. 若是你想按期清理, 那么只能本身写个脚本咯。

注意如下几点:
永远不建议手动删除事务日志文件与快照文件;
-  经过配置项使ZooKeeper server自动删除, 只有在ZooKeeper版本大于3.4后才可用;
-  PrugeTxnLog工具是一个一次性工具, 若是须要按期清理, 你须要本身写一个脚本;
-  当机器规格不一样的时候, 建议按照不一样规格定制不一样的清楚阈值;

2)清理运行日志
ZooKeeper 用 log4j 来输出运行日志。若是要更改运行日志的相关配置,你须要独立为log4j提供配置文件。建议使用log4j提供的滚动日志特性,这样就免去了清理运行日志的问题。

3)监控ZooKeeper server进程的死活
ZooKeeper的server进程在错误发生的时候会当即自杀,ZooKeeper的设计哲学是这样的:
-  单个实例挂掉, 或少许实例挂掉不影响总体服务
-  当单个实例遇到错误的时候, 实例会当即挂掉
-  实例被重启后会自动加入ensemble
-  但实例不会自动重启

因此搞一个监控进程, 在实例进程挂掉以后将其当即拉起是一个很好的作法. 好比daemontools或SMF.

4)监控ZooKeeper server服务的状态
要监控ZooKeeper服务的状态, 有两个选择
-  用4字命令去检查。这个在上面的 ZooKeeper4字命令中有详情
-  JMX。

5)运行日志
ZooKeeper使用log4j 1.2来输出运行日志,默认的配置文件在zookeeper/conf/log4j.properties中。log4j的配置文件要求要么放在工做目录里, 要么放在类路径里。

6)问题定位
[ 因为文件损坏致使实例不能启动 ]
ZooKeeper的server进程在事务日志文件被损坏的状况下是起不来的。这时运行日志会说在载入ZooKeeper database时出现IOException。这种状况下,你须要作的是:
-  经过四字命令stat检查ensemble中的其它实例是否正常工做
-  若是其它实例正常, 那么把当前实例dataDir目录下的version-2子目录中的全部文件删除, 再把dataLogDir下的version-2子目录下的全部文件删除, 而后重启就能够了。

这种状况是当前实例的事务文件损坏, 不能重建内存数据库, 删除掉事务日志和数据库快照后, 当前的实例在重启后会经过其它实例拉取内在数据库, 重建事务日志和快照文件.

7)配置参数
ZooKeeper的行为受配置文件影响. 全部同一个ensemble中的实例建议使用彻底相同的配置文件. 但使用彻底相同的配置文件有一个前提条件: 就是全部实例所属的机器上的磁盘布局是相同的. 磁盘布局不一样意味着不一样的机器下的实例在配置dataDir和dataLogDir的时候配置值可能有差别, 但除此以外, 一个ensemble中的全部实例的配置文件必须保证server.x=xxxx这些配置值是彻底一致的。

7.1)最小配置
下面列出来的是要让ensemble正常工做, 每一个实例都须要配置的配置项。配置项含义:

7.2)高级可选配置
下面列出来的是一此可选配置, 属于高级选项. 你能够用这些配置项进一步个性化ZooKeeper server的行为. 其中一些配置项的值能够经过在启动server进程的时候写入Java 系统属性来设置。配置项对应的Java系统属性名含义:

7.3)多实例模式下的配置项
下面列出来的配置项是多实例模式下的一些配置项. 有一些配置项能够经过在启动server进程的时候写入Java系统属性来设置。配置项对应的Java系统属性名含义

7.4)身份认证与受权相关的配置项
为了不看不懂下面的配置项都在干吗,先大体说一下Zookeeper里的认证与受权。在ZooKeeper server端, 每一个znode存储两部份内容: 数据和状态. 状态中包含ACL信息. 建立一个znode会产生一个ACL表, 每一个ACL记录有如下内容:
验证模式(scheme)
-  具体内容(id). 好比当scheme=="digest"的时候, id为是用户名和密码, 好比"root:J0sTy9BCUKubtK1y8pkbL7qoxSw="
-  这个ACL拥有的权限

ZooKeeper提供了以下几种验证模式(scheme)
- digest    就是用户名+密码.
- auth       不使用任何id, 表示任何已确认用户
- ip.          用client链接至server时使用的IP地址进行验证
- world     固定ID为"anyone", 为全部client端开放权限
- super     在这种scheme下, 对应的id拥有超级权限.

须要注意:exists操做的getAcl操做不受ACL控制, 任何client均可以执行这两个操做.

znode的权限主要有如下几种:
-  create
-  read
-  write
-  delete
-  admin       容许对本节点执行setAcl操做

配置项对应的Java系统属性名含义:

7.5)实验性的配置项
配置项对应的Java系统属性名含义

7.6)不安全的配置项
配置项对应的Java系统属性名含义

7.7)使用Netty框架进行通讯
这是3.4版本后的一个特性。Netty是一个基于NIO的客户端-服务器通讯框架, 这个框架简化了Java在网络通讯层上的不少繁操做, 而且内置支持SSL和认证受权, 固然SSL和认证受权是额外的可选功能.

3.4版本以前,ZooKeeper是直接用NIO的,在3.4以后,NIO只是一个可选项,但依然是默认选项,若是要使用Netty的话,须要把zookeeper.serverCnxnFactory替换为org.apache.zookeeper.server.NettyServerCnxnFactory。能够只在client上用Netty,也可在server上用Netty,但一般状况下,建议要改一块儿改。蛋疼的是相关的文档官方尚未写!

7.8)四字命令
ZooKeeper支持一系列的四字命令, 你能够在client上经过telnte或nc直接向server发送这些四字命令.使用一个四字命令以下所示, 下面使用echo和nc将四字命令ruok发送给本机的server:

echo ruok | nc 127.0.0.1 2182

下表是全部支持的四字命令, 注意有些命令仅在特定版本以后才受支持。命令含义:

这里须要注意:
mntr命令的输出大体长下面这样. 输出格式符合java属性格式, 若是你要写个脚本定时发送这个命令以监控ensemble的运行状态, 注意输出的字段的数量可能会有变化, 写脚本的时候注意这一点。另外有一些字段是与操做系统平台相关的,  输出每一行的格式是key \t value, 下面是一个示例:

# echo mntr | nc localhost 2185

zk_version  3.4.0
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

8)数据文件管理
将事务日志文件和快照文件存储在不一样的物理磁盘上, 能够提高系统性能.

8.1)快照存储目录
配置项dataDir指向的目录路径中主要存储两种文件:
-  myid: 这个文件里写着当前server实例的编号
-  snapshot.<zxid>: 这里存储着内存数据库的快照

server实例的编号用在两个场合:myid文件里, 以及配置文件里的server.X配置项中。当前server实例在启动的时候, 先去配置文件里看dataDir的值, 而后去找dataDir/myid这个文件, 查看文件内容, 得知本身的编号, 而后在配置文件里再找对应的server.x查看要开的端口号。

快照文件的后缀, <zxid>, 是一个事务ID. 这是在落地内存数据库这个过程开始时, 成功执行的最后一个事务的ID号, 但蛋疼的是, 在落地快照的过程当中, server还在接受请求, 执行事务, 也就是在落地的过程当中, 内存数据库中的数据还处于一个变更的过程当中, 这就致使落地后的快照文件像是一个扭曲的文件. 像是你在用手机拍摄全景照片的过程当中, 有一只猫随着你的镜头走, 而后最终拍摄出来的照片里有一只长度为17米的猫. 最终落地生成的快照文件里的数据状态可能和任何一个时刻内存数据库的状态都对不上, 就是由于这个缘由. 但ZooKeeper依然能够用这种扭曲的快照文件重建内存数据库, 这是由于ZooKeeper中的update操做是幂等的, 这就保证了在扭曲的快照文件之上重放事务日志里的日志, 就能够将进程的内存状态恢复到日志结束时的那个时刻。

8.2)事务日志目录
在有update请求的时候, server的默认行为是先写事务日志, 再执行update操做. 单个事务日志里存储的事务个数超过一个阈值的时候, 就会致使事务日志新开一个文件, 同时会致使内在数据库落地快照, 这个阈值在上面的配置项中有提. 日志文件的后缀是日志文件里第一个日志的ID

8.3)文件管理
快照文件的格式和事务日志文件的格式是死的,这就容许你从现网的server机器上将事务日志和内存快照拷贝至你的开发机,在你的开发环境重现现网的情景, 从而进行一些调试或问题定位操做。

使用旧的事务日志文件和快照文件还能重建过去某个指定时刻server的状态, LogFormatter类能够用来访问事务日志文件, 以获取可读的信息. 固然使用的时候须要有管理员权限, 由于数据是加密的。

server进程原本是没有删除事务日志和快照文件的能力的, 但这在3.4版本中也随着新的配置项autopurge.snapRetainCount和autopurge.purgeInterval添加上了。

9)要避免的事情
下面是几个你应当在部署运维的时候极力避免的事情:
ensemble中各个server使用的配置文件中,server.X配置表不一致. 全部的配置文件中, 都要以server.X配置项的形式列出当前ensemble中的全部server, 包括本身. 若是这个东西不一致, 会炸。
- 事务日志目录设置不合理。将事务日志目录指向一个IO繁忙的磁盘, 会致使server始终处于一个半死不活的状态;
- 不正确的java heap size。频繁的swap操做会严重拖慢性能. 保守起见, 若是你的机器有4G内存, 把java heap size设置为3G就行了;
- 部署的时候不考虑安全性。建议在生产环境中合理配置防火墙;

5、Zookeeper常见问题汇总
1)zookeeper client 3.4.5 ping时间间隔算法有问题,在遇到网络抖动等缘由致使一次ping失败后会断开链接。3.4.6解决了这个问题 Bug1751。

2)zookeeper client若是由于网络抖动断开了链接,若是后来又重连上了,zookeeper client会自动的将以前订阅的watcher等又所有订阅一遍,而Zookeeper默认对单个数据包的大小有个1M的限制,这每每就会超限,最后致使一直不断地的重试。这个问题在较新的版本获得了修复。Bug706

3)抛出UnresolvedAddressException异常致使Zookeeper选举线程退出,整个集群没法再选举,处于崩溃的边缘。这个问题是,某次OPS迁移机器,将老的机器回收了,因此老的机器的IP和机器名不复存在,最后抛出UnresolvedAddressException这个异常,而Zookeeper的选举线程(QuorumCnxManager类里的Listener)只捕获了IOException,致使该线程退出,该线程一旦退出只要如今的leader出现问题,须要从新选举,则不会选出新的leader来,整个集群就会崩溃。Bug2319

4)因为文件损坏致使实例不能启动
ZooKeeper的server进程在事务日志文件被损坏的状况下是起不来的. 这时运行日志会说在载入ZooKeeper database时出现IOException. 这种状况下, 你须要作的是:
经过四字命令stat检查ensemble中的其它实例是否正常工做
- 若是其它实例正常, 那么把当前实例dataDir目录下的version-2子目录中的全部文件删除, 再把dataLogDir下的version-2子目录下的全部文件删除, 而后重启就能够了.

这种状况是当前实例的事务文件损坏, 不能重建内存数据库, 删除掉事务日志和数据库快照后, 当前的实例在重启后会经过其它实例拉取内在数据库, 重建事务日志和快照文件.

6、Zookeeper中的Znode特性说明
在ZooKeeper中,节点也称为znode。因为对于程序员来讲,对zookeeper的操做主要是对znode的操做,所以,有必要对znode进行深刻的了解。 ZooKeeper采用了相似文件系统的的数据模型,其节点构成了一个具备层级关系的树状结构。Zookeeper拥有一个命名空间就像一个精简的文件系统,不一样的是它的命名空间中的每一个节点拥有它本身或者它下面子节点相关联的数据。Zookeeper中必须使用绝对路径也就是使用“/”开头。例如,下图展现了zookeeper节点的层级树状结构。

图中,根节点 / 包含了两个字节点 /module1,/module2,而节点 /module1 又包含了三个字节点 /module1/app1,/module1/app2,/module1/app3。在zookeeper中,节点以绝对路径表示,不存在相对路径,且路径最后不能以 / 结尾(根节点除外)。

ZooKeeper以一种相似于文件系统的树形数据结构实现名称空间。名称空间中的每一个节点都是一个znode。znode和文件系统的路径不同,在文件系统中,路径只是一个名称,不包含数据。而znode不只是一个路径,还携带数据。

此外,znode还维护了包括版本号和时间戳的状态信息。经过版本号和时间戳信息,可让ZooKeeper验证缓存、协调每次的更改操做。每当znode数据发生更改时,版本号都会递增。客户端检索znode时,同时也会收到关于该节点的状态信息。当客户端执行更改、删除操做时,它必须提供它正在更改的znode数据的版本,若是它提供的版本与数据的实际版本不匹配,则更新将失败。

Zookeeper目录树中每一个节点对应一个Znode。每一个Znode维护这一个属性,当前版本、数据版本、创建时间和修改时间等,看下图:

Zookeeper就是使用这些属性来实现特殊功能的。当一个客户端要对某个节点进行修改时,必须提供该数据的版本号,当节点数据发生变化是其版本号就会增长。以下图:

0)Znode节点特性
-> Watches:客户端能够在节点上设置Watches(能够叫作监视器)。当节点状态发生变化时,就会触发监视器对应的操做,当监视器被触发时,zookeeper服务器会向客户端发送且只发送一个通知
-> 数据访问:zookeeper上存储的数据须要被原子性的操做(要么修改为功要么回到原样),也是就读操做将会读取节点相关全部数据,写操做也会修改节点相关全部数据,,并且每一个节点都有本身的ACL。

节点类型:zookeeper中有几种节点类型,节点类型在节点建立的时候就被肯定且不可改变
-> 临时节点(EPHEMERAL):临时建立的,会话结束节点自动被删除,也能够手动删除,临时节点不能拥有子节点
-> 临时顺序节点(EPHEMERAL_SEQUENTIAL):具备临时节点特征,可是它会有序列号,分布式锁中会用到该类型节点
-> 持久节点(PERSISTENT):建立后永久存在,除非主动删除。
-> 持久顺序节点(PERSISTENT_SEQUENTIAL):该节点建立后持久存在,相对于持久节点它会在节点名称后面自动增长一个10位数字的序列号,这个计数对于此节点的父节点是惟一,若是这个序列号大于2^32-1就会溢出。

1)znode节点类型
根据节点的存活时间,能够对节点划分为持久节点临时节点。节点的类型在建立时就被肯定下来,而且不能改变。
-  持久节点的存活时间不依赖于客户端会话,只有客户端在显式执行删除节点操做时,节点才消失。
-  临时节点的存活时间依赖于客户端会话,当会话结束,临时节点将会被自动删除(固然也能够手动删除临时节点)。利用临时节点的这一特性,咱们可使用临时节点来进行集群管理,包括发现服务的上下线等。  ZooKeeper规定,临时节点不能拥有子节点。

持久节点
使用命令create能够建立一个持久节点

create /module1 module1

这样,便建立了一个持久节点/module1,且其数据为"module1"。

临时节点
使用create命令,并加上-e参数,能够建立一个临时节点

create -e /module1/app1 app1

这样,便建立了一个临时节点 /module1/app1,数据为"app1"。关闭会话,而后输入命令

get /module1/app1

能够看到有如下提示,说明临时节点已经被删除

Node does not exist: /module1/app1

顺序节点
ZooKeeper中还提供了一种顺序节点的节点类型。每次建立顺序节点时,zk都会在路径后面自动添加上10位的数字(计数器),例如 < path >0000000001,< path >0000000002,……这个计数器能够保证在同一个父节点下是惟一的。在zk内部使用了4个字节的有符号整形来表示这个计数器,也就是说当计数器的大小超过2147483647时,将会发生溢出。
顺序节点为节点的一种特性,也就是,持久节点和临时节点均可以设置为顺序节点。这样一来,znode一共有4种类型:持久的临时的持久顺序的临时顺序的

使用命令create加上-s参数,能够建立顺序节点(-e参数为建立临时节点,若是不带参数则建立持久节点)。例如

create -s /module1/app app

输出

Created /module1/app0000000001

便建立了一个持久顺序节点 /module1/app0000000001。若是再执行此命令,则会生成节点 /module1/app0000000002。 
若是在create -s再添加-e参数,则能够建立一个临时顺序节点。以下示例:

2)znode节点数据
在建立节点时能够指定节点中存储的数据。ZooKeeper保证读和写都是原子操做,且每次读写操做都是对数据的完整读取或完整写入,并不提供对数据进行部分读取或者写入操做。
如下命令建立一个节点/module1/app2,且其存储的数据为app2。

create /module1/app2 app2

ZooKeeper虽然提供了在节点存储数据的功能,但它并不将本身定位为一个通用的数据库,也就是说,你不该该在节点存储过多的数据。Zk规定节点的数据大小不能超过1M,但实际上咱们在znode的数据量应该尽量小,由于数据过大会致使zk的性能明显降低。若是确实须要存储大量的数据,通常解决方法是在另外的分布式数据库(例如redis)中保存这部分数据,而后在znode中咱们只保留这个数据库中保存位置的索引便可。

3)znode节点属性
每一个znode都包含了一系列的属性,经过命令get就能够得到节点的属性。

get /module1/app2 

app2 
cZxid = 0x20000000e 
ctime = Thu Jun 30 20:41:55 HKT 2016 
mZxid = 0x20000000e 
mtime = Thu Jun 30 20:41:55 HKT 2016 
pZxid = 0x20000000e 
cversion = 0 
dataVersion = 0 
aclVersion = 0 
ephemeralOwner = 0x0 
dataLength = 4 
numChildren = 0

4)zookeeper中的版本号
version含义对节点的每次操做都会使节点的版本号增长,有三个版本号dataversion(数据版本号)、cversion(子节点版本号)、aclversion(节点所拥有的ACL版本号)。

对于每一个znode来讲,均存在三个版本号:
- dataVersion
数据版本号,每次对节点进行set操做,dataVersion的值都会增长1(即便设置的是相同的数据)。
- cversion
子节点的版本号。当znode的子节点有变化时,cversion 的值就会增长1。
- aclVersion

ACL的版本号,关于znode的ACL(Access Control List,访问控制),能够参考 参考资料1 有关ACL的描述。
以数据版本号来讲明zk中版本号的做用。每个znode都有一个数据版本号,它随着每次数据变化而自增。ZooKeeper提供的一些API例如setData和delete根据版本号有条件地执行。多个客户端对同一个znode进行操做时,版本号的使用就会显得尤其重要。例如,假设客户端C1对znode /config写入一些配置信息,若是另外一个客户端C2同时更新了这个znode,此时C1的版本号已通过期,C1调用setData必定不会成功。这正是版本机制有效避免了数据更新时出现的前后顺序问题。在这个例子中,C1在写入数据时使用的版本号没法匹配,使得操做失败。下图描述了这个状况。下面表示使用版本号来阻止并行操做的不一致性:

zookeeper中版本号的做用
Zookeeper里面的版本号和咱们理解的版本号不一样,它表示的是对数据节点的内容、子节点列表或者ACL信息的修改次数。节点建立时dataversion、aclversion,cversion都为0,每次修改响应内容其对应的版本号加1。

这个版本号的用途就和分布式场景的一个锁概念有关。好比演出售票中的一个座位,显然每一个场次中的每一个座位都只有一个,不可能卖出2次。若是A下单的时候显示可售,他想买,那么为了保证他能够下单成功,此时别人就不能买。这时候就须要有一种机制来保证同一时刻只能有一我的去修改该座位的库存。这就用到了锁,锁有悲观锁和乐观锁。
-  悲观锁:它会假定全部不一样事务的处理必定会出现干扰,数据库中最严格的并发控制策略,若是一个事务A正在对数据处理,那么在整个事务过程当中,其余事务都没法对这个数据进行更新操做,直到A事务释放了这个锁。
-  乐观锁:它假定全部不一样事务的处理不必定会出现干扰,因此在大部分操做里不准加锁,可是既然是并发就有出现干扰的可能,如何解决冲突就是一个问题。在乐观锁中当你在提交更新请求以前,你要先去检查你读取这个数据以后该数据是否发生了变化,若是有那么你这次的提交就要放弃,若是没有就能够提交。

Zookeeper中的版本号就是乐观锁,你修改节点数据以前会读取这个数据并记录该数据版本号,当你须要更新时会携带这个版本号去提交,若是你此时携带的版本号(就是你上次读取出来的)和当前节点的版本号相同则说明该数据没有被修改过,那么你的提交就会成功,若是提交失败说明该数据在你读取以后和提交以前这段时间内被修改了。

这里经过set命令并携带版本号提交更新,版本号相同更新就会成功。

若是你再次更新并使用以前的版本号那么就会失败。

5)zookeeper中的事务ID
对于zookeeper来讲,每次的变化都会产生一个惟一的事务id,zxid(ZooKeeper Transaction Id)。经过zxid,能够肯定更新操做的前后顺序。例如,若是zxid1小于zxid2,说明zxid1操做先于zxid2发生。须要指出的是,zxid对于整个zookeeper都是惟一的,即便操做的是不一样的znode。
- cZxid     即Znode建立的事务id。
- mZxid    Znode被修改的事务id,即每次对znode的修改都会更新mZxid。

下面表示Zxid在客户端重连中的做用:

在集群模式下,客户端有多个服务器能够链接,当尝试链接到一个不一样的服务器时,这个服务器的状态要与最后链接的服务器的状态要保持一致。Zk正是使用zxid来标识这个状态,图3描述了客户端在重连状况下zxid的做用。当客户端因超时与S1断开链接后,客户端开始尝试链接S2,但S2延迟于客户端所识别的状态。然而,S3的状态与客户端所识别的状态一致,因此客户端能够安全链接上S3。

ZXID含义:ZooKeeper节点状态改变会致使该节点收到一个zxid格式的时间戳,这个时间戳是全局有序的,每次更新都会产生一个新的。若是zxid1的值小于zxid2,那么说明zxid2发生的改变在zxid1以后。zxid是一个惟一的事务ID,具备递增性,一个znode的创建或者更新都会产生一个新的zxid值,具体时间有3个cZxid(节点建立时间)、mZxid(该节点修改时间,与子节点无关)、pZxid(该节点的子节点的最后一次建立或者修改时间,孙子节点无关)

下面对zookeeper的时间和版本号作一说明:

6)zookeeper中的时间戳
包括znode的建立时间和修改时间,建立时间是znode建立时的时间,建立后就不会改变;修改时间在每次更新znode时都会发生变化。如下命令建立了一个 /module2 节点

create /module2 module2 
Created /module2

经过 get 命令,能够看到 /module2的 ctime和mtime均为Sat Jul 02 11:18:32 CST 2018

get /module2 
module2 
cZxid = 0x2 
ctime = Sat Jul 02 11:18:32 CST 2018 
mZxid = 0x2 
mtime = Sat Jul 02 11:18:32 CST 2018 
pZxid = 0x2 
cversion = 0 
dataVersion = 0 
aclVersion = 0 
ephemeralOwner = 0x0 
dataLength = 7 
numChildren = 0

修改 /module2,能够看到 ctime 没有发生变化,mtime已更新为最新的时间

set /module2 module2_1 
cZxid = 0x2 
ctime = Sat Jul 02 11:18:32 CST 2018 
mZxid = 0x3 
mtime = Sat Jul 02 11:18:50 CST 2018 
pZxid = 0x2 
cversion = 0 
dataVersion = 1 
aclVersion = 0 
ephemeralOwner = 0x0 
dataLength = 9 
numChildren = 0

7)znode须要关注的点:
-  Watches
客户端可在znode上设置watchs。每当该znode发生改变时,就会触发设置在这个znode上的watch。当触发了watch,ZooKeeper会发送一个通知给客户端。

-  Data Access
在每一个znode名称空间中存储的数据的读、写操做都是原子性的。读操做将获取与znode关联的全部数据(包括数据的状态信息),写操做将替换该znode所携带的全部数据。每一个节点都有一个访问控制列表(ACL)来限制谁能够作什么。

ZooKeeper并无被设计成通常的数据库或大型对象存储。相反,它只是管理协调数据。这些数据能够以配置、状态信息等形式出现。各类形式的协调数据的一个共同特色是它们相对较小,通常以kb做为度量度量。ZooKeeper客户端和服务器实现都有完整的检查功能,以确保znode的数据少于1M,通常来讲,协调数据占用的空间都远远小于1M。在相对较大的数据大小上操做会致使一些操做比其余操做花费更多的时间,而且会影响一些操做的延迟,由于它要在网络上传输更多数据。若是须要存储较大数据,能够将它们存储在大型存储系统(如NFS或HDFS)上,而后在ZooKeeper中使用指针指向这些较大数据。

-  Ephemeral Nodes
ZooKeeper容许使用临时(ephemeral)节点。只要建立临时znode的会话还存在,临时znode就存在。会话退出,这个会话上建立的临时节点都会删除。所以,临时节点上不容许出现子节点。

-  Sequence Nodes -- Unique Naming
建立znode时,还能够请求ZooKeeper将单调递增的计数器追加到znode路径的末尾。这个计数器是父znode独有的。计数器的格式为%010d,即便用0来填充的10位数字(计数器以这种方式进行格式化以简化排序),例如<path>0000000001。注意:用于存储下一个序列号的计数器是由父节点维护的有符号整数(4bytes),当计数器的增量超过2147483647时,计数器将溢出。

8)ZooKeeper中的时间
-  Zxid
每次更改ZooKeeper的状态,都会设置到一个zxid(ZooKeeper的事务id)格式的版本戳。zxid暴露了ZooKeeper中全部更改操做的总顺序。由于每次更改都会设置一个全局惟一的zxid值,若是zxid1小于zxid2,说明zxid1对应的操做比zxid2对应的事务先发生。

-  Version numbers
每次对某节点进行更改,都会递增这个节点的版本号。有三种版本号:
dataVersion:znode的更改次数。
cversion:子节点的更改次数。
aversion:节点的ACL的更改次数。

-  Ticks
当使用多节点(这个节点表明的是组成ZooKeeper的server,而非znode)的ZooKeeper集群时,各节点使用ticks来定义事件的时间。例如传播状态、会话超时时间、节点间链接超时时间等。tick时间间接设置了会话链接的最小超时时长(tick的两倍时长)。若是客户端在2倍tick时间内尚未成功链接server,那么链接失败。

-  Real time
除了在建立和修改znode时会将当前实时时间戳放入stat结构以外,ZooKeeper根本不使用实时时间或时钟时间。

9)znode的状态
czxid      建立znode的zxid
mzxid     最近一次修改znode的zxid(建立、删除、set直系子节点、set自身节点都会计数)
pzxid     最近一次修改子节点的zxid(建立、删除直系子节点都会计数,set子节点不会计数)
ctime     建立znode的时间,单位毫秒
mtime    最近一次修改znode的时间,单位毫秒
version     修改znode的次数
cversion   修改子节点的次数(建立、删除直系子节点都会计数,set子节点不会计数)
aversion   该znode的ACL修改次数
ephemeralOwner      临时znode节点的session id,若是不是临时节点,值为0
dataLength znode     携带的数据长度,单位字节
numChildren     直系子节点的数量(不会递归计算孙节点)

相关文章
相关标签/搜索