zookeeper 快速入门

分布式系统简介

在分布式系统中另一个需要解决的重要问题就是数据的复制。我们日常开发中,很多人会碰到一个问题:客户端C1更新了一个值K1由V1更新到V2.但是客户端C2无法立即读取到K的最新值。上面的例子就是常见的数据库之间复制的延时问题。

分布式系统对于数据的复制一般由于:

  1. 为了增加系统的可用性,以防止单点故障引起的系统不可用。
  2. 提高系统的整体性能,通过负载均衡,能够让分布在不同地方的数据副本都能够为用户提供服务。

为了解决复制延时的问题,可以等待所有复制都完成后,在进行下一次更新,但这会带来性能问题。
为了保证数据一致性,同时又不能影响系统运行的性能,于是,一致性级别由此诞生。

  • 强一致性: 系统写入什么,读出来就是什么,对系统的性能影响比较大。
  • 弱一致性: 系统在写入后,不承诺立即可以读取写入的值,但会尽可能的保证在某个时间级别后,达到一致性状态。
    • 会话一致性: 对于写入的值,同一个客户端会话中可以读取到一致的值。
    • 用户一致性: 对于写入的值,在同一个用户中可以读取到一致性的值,其他用户不能保证。
  • 最终一致性:跟弱一致性中非常重要的一种一致性模型,也是业界在大型分布式系统的数据一致性上比较推崇的模型。

zookeeper 产生的原因

Zookeeper是Hadoop分布式调度服务,用来构建分布式应用系统。构建一个分布式应用是一个很复杂的事情,主要的原因是我们需要合理有效的处理分布式集群中的部分失败的问题。例如,集群中的节点在相互通信时,A节点向B节点发送消息。A节点如果想知道消息是否发送成功,只能由B节点告诉A节点。那么如果B节点关机或者由于其他的原因脱离集群网络,问题就出现了。A节点不断的向B发送消息,并且无法获得B的响应。B也没有办法通知A节点已经离线或者关机。集群中其他的节点完全不知道B发生了什么情况,还在不断的向B发送消息。这时,你的整个集群就发生了部分失败的故障。

Zookeeper不能让部分失败的问题彻底消失,但是它提供了一些工具能够让你的分布式应用安全合理的处理部分失败的问题。

zookeeper工作原理

Zookeeper 的核心是广播,这个机制保证了各个Server之间的同步。实现这个机制的协议叫做Zab协议。Zab协议有两种模式,它们分别是恢复模式(选主)和广播 模式(同步)。当服务启动或者在领导者崩溃后,Zab就进入了恢复模式,当领导者被选举出来,且大多数Server完成了和leader的状态同步以后, 恢复模式就结束了。状态同步保证了leader和Server具有相同的系统状态。为了保证事务的顺序一致性,zookeeper采用了递增的事务id号 (zxid)来标识事务。所有的提议(proposal)都在被提出的时候加上了zxid。实现中zxid是一个64位的数字,它高32位是epoch用 来标识leader关系是否改变,每次一个leader被选出来,它都会有一个新的epoch,标识当前属于那个leader的统治时期。低32位用于递 增计数。

zab协议核心内容

zab协议的核心是定义了对于那些会改变zookeeper服务器数据状态的事务请求的处理方式,即:所有事务请求必须由一个全局唯一的服务器来协调处理,这样的服务器被称为Leader服务器,而余下的其他服务器则成为Follower服务器。Leader服务器负责将一个客户端事务请求转换成一个事务Proposal(提倡),并将该Proposal分发给集群中所有的Follower 服务器。之后Leader服务器需要等待所有Follower服务器的反馈,一旦超过半数的Follower服务器进行了正确的反馈后,那么Leader就会再次向所有的Follower服务器分发Commit消息,要求将其前一个Proposal进行提交。

每个Server在工作过程中有三种状态:

  • LOOKING:当前Server不知道leader是谁,正在搜寻。
  • LEADING:当前Server即为选举出来的leader。
  • FOLLOWING:leader已经选举出来,当前Server与之同步。

每个集群中的三种状态

  • Leader : 为客户端提供读和写服务。
  • Follower: 为客户端提供读服务。
  • observer: 为客户端提供读服务,但是不参与Leader选举,也不参与写操作的“过办写成功”策略。所以 observer可以在不影响写性能的情况下提升集群的读性能。

zookeeper 特性

  1. 顺序一致性:从同一个客户端发起的事务请求,最终将严格按照其发起顺序被应用到ZooKeeper中。
  2. 原子性:更新操作要么成功要么失败,没有中间状态。
  3. 单一视图(Single system image):不管客户端连接哪一个服务器,客户端看到服务端的数据模型都是一致的(the same view of service)。
  4. 可靠性(Reliability): 一旦一个更新成功,那么那就会被持久化,直到客户端用新的更新覆盖这个更新。
  5. 实时性(Timeliness):Zookeeper仅保证在一定时间内,客户端最终一定能够从服务端读到最新的数据状态。

基本概念

zookeeper数据模型

Zookeeper 会维护一个具有层次关系的数据结构,它非常类似于一个标准的文件系统. 名称叫做zNode。

Zookeeper 这种数据结构有如下这些特点:

  • 每个子目录项如 NameService 都被称作为 znode,这个 znode 是被它所在的路径唯一标识,如 Server1 这个 znode 的标识为 /NameService/Server1。
  • znode 可以有子节点目录,并且每个 znode 可以存储数据,注意 EPHEMERAL 类型的目录节点不能有子节点目录
  • znode 是有版本的,每个 znode 中存储的数据可以有多个版本,也就是一个访问路径中可以存储多份数据
  • znode 可以是临时节点,一旦创建这个 znode 的客户端与服务器失去联系,这个 znode 也将自动删除,Zookeeper 的客户端和服务器通信采用长连接方式,每个客户端和服务器通过心跳来保持连接,这个连接状态称为 session,如果 znode 是临时节点,这个 session 失效,znode 也就删除了
  • znode 的目录名可以自动编号,如 App1 已经存在,再创建的话,将会自动命名为 App2
  • znode 可以被监控,包括这个目录节点中存储的数据的修改,子节点目录的变化等,一旦变化可以通知设置监控的客户端,这个是 Zookeeper 的核心特性,Zookeeper 的很多功能都是基于这个特性实现的。

Node可以分为持久节点(PERSISTENT)和临时节点(EPHEMERAL)两类。所谓持久节点是指一旦这个 ZNode被创建了,除非主动进行移除操作,否则这个节点将一直保存在 Zookeeper上。而临时节点的生命周期,是与客户端会话绑定的,一旦客户端会话失效,那么这个客户端创建的所有临时节点都会被移除。
另外,Zookeeper还有一种 顺序节点(SEQUENTIAL)。该节点被创建的时候,Zookeeper会自动在其子节点名上,加一个由父节点维护的、自增整数的后缀 (上限: Integer.MAX_VALUE)。该节点的特性,还可以应用到 持久/临时节点 上,组合成 持久顺序节点 (PERSISTENT_SEQUENTIAL) 和 临时顺序节点 ( EPHEMERAL_SEQUENTIAL)

会话

Session指客户端会话。在 Zookeeper中,一个客户端会话是指 客户端和服务器之间的一个TCP长连接。客户端启动的时候,会与服务端建立一个TCP连接,客户端会话的生命周期,则是从第一次连接建立开始算起。通过这个连接,客户端能够通过心跳检测与服务器保持有效的会话,并向 Zookeeper服务器发送请求并接收响应,以及接收来自服务端的 Watch事件通知。

Session的 sessionTimeout参数,用来控制一个客户端会话的超时时间。当服务器压力太大 或者是网络故障等各种原因导致客户端连接断开时,Client会自动从 Zookeeper地址列表中逐一尝试重连 (重试策略可使用 Curator来实现)。只要在 sessionTimeout规定的时间内能够重新连接上集群中任意一台服务器,那么之前创建的会话仍然有效。如果,在 sessionTimeout时间外重连了,就会因为 session已经被清除了,而被告知 SESSION_EXPIRED,此时需要程序去恢复临时数据;还有一种 Session重建后的在新节点上的数据,被之前节点上因网络延迟晚来的写请求所覆盖的情况,在 ZOOKEEPER-417中被提出,并在该 JIRA中新加入的 SessionMovedException,使得 用同一个sessionld/sessionPasswd 重建 Session的客户端能感知到,但是这个问题到 ZOOKEEPER-2219仍然没有得到很好的解决。

版本

Zookeeper的每个 ZNode上都会存储数据,对应于每个 ZNode,Zookeeper都会为其维护一个叫做 Stat的数据结构,Stat中记录了这个 ZNode的三个数据版本,分别是 version(当前 ZNode数据内容的版本),cversion(当前 ZNode子节点的版本)和 aversion (当前 ZNode的 ACL变更版本)。这里的版本起到了控制 Zookeeper操作原子性的作用 (详见 “源码分析 - 落脚点 - Zookeeper乐观锁”)

watch

Watcher(事件监听器)是 Zookeeper提供的一种 发布/订阅的机制。Zookeeper允许用户在指定节点上注册一些 Watcher,并且在一些特定事件触发的时候,Zookeeper服务端会将事件通知给订阅的客户端。该机制是 Zookeeper实现分布式协调的重要特性

ACL

类似于 Unix文件系统,Zookeeper采用 ACL(Access Control Lists)策略来进行权限控制 (使用格式
setAcl <path> <acl: scheme + id + permissions>)。
例子:

setAcl /zk world:anyone:cdrw
  • CREATE (c): 创建子节点的权限。
  • READ (r): 获取节点数据和子节点列表的权限。
  • WRITE (w): 更新节点数据的权限。
  • DELETE (d): 删除当前节点的权限。
  • ADMIN (a): 管理权限,可以设置当前节点的permission。
scheme ID comment
world anyone Zookeeper中对所有人有权限的结点就是属于 world:anyone
auth 不需要id 通过 authentication的 user都有权限
digest username:BASE64 (SHA1(password)) 需要先通过 username:password形式的 authentication
ip id为客户机的IP地址(或者 IP地址段) ip:192.168.1.0/14,表示匹配前 14个bit的 IP段
super 对应的 id拥有超级权限 (CRWDA)

Zookeeper支持通过 kerberos来进行 authencation,也支持 username/password形式的 authentication。

ZooKeeper 典型的应用场景

Zookeeper 作为一个分布式的服务框架,主要用来解决分布式集群中应用系统的一致性问题,它能提供基于类似于文件系统的目录节点树方式的数据存储,但是 Zookeeper 并不是用来专门存储数据的,它的作用主要是用来维护和监控你存储的数据的状态变化。通过监控这些数据状态的变化,从而可以达到基于数据的集群管理。

Zookeeper 从设计模式角度来看,是一个基于观察者模式设计的分布式服务管理框架,它负责存储和管理大家都关心的数据,然后接受观察者的注册,一旦这些数据的状态发生变化,Zookeeper 就将负责通知已经在 Zookeeper 上注册的那些观察者做出相应的反应,从而实现集群中类似 Master/Slave 管理模式,关于 Zookeeper 的详细架构等内部细节可以阅读 Zookeeper 的源码

统一命名服务(Name Service)

分布式应用中,通常需要有一套完整的命名规则,既能够产生唯一的名称又便于人识别和记住,通常情况下用树形的名称结构是一个理想的选择,树形的名称结构是一个有层次的目录结构,既对人友好又不会重复。说到这里你可能想到了 JNDI,没错 Zookeeper 的 Name Service 与 JNDI 能够完成的功能是差不多的,它们都是将有层次的目录结构关联到一定资源上,但是 Zookeeper 的 Name Service 更加是广泛意义上的关联,也许你并不需要将名称关联到特定资源上,你可能只需要一个不会重复名称,就像数据库中产生一个唯一的数字主键一样。
Name Service 已经是 Zookeeper 内置的功能,你只要调用 Zookeeper 的 API 就能实现。如调用 create 接口就可以很容易创建一个目录节点。

配置管理(Configuration Management)

配置的管理在分布式应用环境中很常见,例如同一个应用系统需要多台 PC Server 运行,但是它们运行的应用系统的某些配置项是相同的,如果要修改这些相同的配置项,那么就必须同时修改每台运行这个应用系统的 PC Server,这样非常麻烦而且容易出错。
像这样的配置信息完全可以交给 Zookeeper 来管理,将配置信息保存在 Zookeeper 的某个目录节点中,然后将所有需要修改的应用机器监控配置信息的状态,一旦配置信息发生变化,每台应用机器就会收到 Zookeeper 的通知,然后从 Zookeeper 获取新的配置信息应用到系统中。

集群管理(Group Membership)

Zookeeper 能够很容易的实现集群管理的功能,如有多台 Server 组成一个服务集群,那么必须要一个“总管”知道当前集群中每台机器的服务状态,一旦有机器不能提供服务,集群中其它集群必须知道,从而做出调整重新分配服务策略。同样当增加集群的服务能力时,就会增加一台或多台 Server,同样也必须让“总管”知道。
Zookeeper 不仅能够帮你维护当前的集群中机器的服务状态,而且能够帮你选出一个“总管”,让这个总管来管理集群,这就是 Zookeeper 的另一个功能 Leader Election。
它们的实现方式都是在 Zookeeper 上创建一个 EPHEMERAL 类型的目录节点,然后每个 Server 在它们创建目录节点的父目录节点上调用 getChildren(String path, boolean watch) 方法并设置 watch 为 true,由于是 EPHEMERAL 目录节点,当创建它的 Server 死去,这个目录节点也随之被删除,所以 Children 将会变化,这时 getChildren上的 Watch 将会被调用,所以其它 Server 就知道已经有某台 Server 死去了。新增 Server 也是同样的原理。
Zookeeper 如何实现 Leader Election,也就是选出一个 Master Server。和前面的一样每台 Server 创建一个 EPHEMERAL 目录节点,不同的是它还是一个 SEQUENTIAL 目录节点,所以它是个 EPHEMERAL_SEQUENTIAL 目录节点。之所以它是 EPHEMERAL_SEQUENTIAL 目录节点,是因为我们可以给每台 Server 编号,我们可以选择当前是最小编号的 Server 为 Master,假如这个最小编号的 Server 死去,由于是 EPHEMERAL 节点,死去的 Server 对应的节点也被删除,所以当前的节点列表中又出现一个最小编号的节点,我们就选择这个节点为当前 Master。这样就实现了动态选择 Master,避免了传统意义上单 Master 容易出现单点故障的问题。

集群管理结构图

共享锁(Locks)

共享锁在同一个进程中很容易实现,但是在跨进程或者在不同 Server 之间就不好实现了。Zookeeper 却很容易实现这个功能,实现方式也是需要获得锁的 Server 创建一个 EPHEMERAL_SEQUENTIAL 目录节点,然后调用 getChildren方法获取当前的目录节点列表中最小的目录节点是不是就是自己创建的目录节点,如果正是自己创建的,那么它就获得了这个锁,如果不是那么它就调用 exists(String path, boolean watch) 方法并监控 Zookeeper 上目录节点列表的变化,一直到自己创建的节点是列表中最小编号的目录节点,从而获得锁,释放锁很简单,只要删除前面它自己所创建的目录节点就行了。

Zookeeper 实现 Locks 的流程图

队列管理

Zookeeper 可以处理两种类型的队列:
当一个队列的成员都聚齐时,这个队列才可用,否则一直等待所有成员到达,这种是同步队列。
队列按照 FIFO 方式进行入队和出队操作,例如实现生产者和消费者模型。
同步队列用 Zookeeper 实现的实现思路如下:
创建一个父目录 /synchronizing,每个成员都监控标志(Set Watch)位目录 /synchronizing/start 是否存在,然后每个成员都加入这个队列,加入队列的方式就是创建 /synchronizing/member_i 的临时目录节点,然后每个成员获取 / synchronizing 目录的所有目录节点,也就是 member_i。判断 i 的值是否已经是成员的个数,如果小于成员个数等待 /synchronizing/start 的出现,如果已经相等就创建 /synchronizing/start。
用下面的流程图更容易理解:

同步队列流程图
FIFO 队列用 Zookeeper 实现思路如下:
实现的思路也非常简单,就是在特定的目录下创建 SEQUENTIAL 类型的子目录 /queue_i,这样就能保证所有成员加入队列时都是有编号的,出队列时通过 getChildren( ) 方法可以返回当前所有的队列中的元素,然后消费其中最小的一个,这样就能保证 FIFO。

zookeeper安装

zookeeper 集群分为:单机模式,伪集群,集群模式。

zookeeper配置文件解释

单机模式下需要修改的配置

tickTime=2000  
dataDir=/data/zk 
dataLogDir=/var/log/zookeeper/logs  
clientPort=2181  
  • tickTime(CS通信心跳数): Zookeeper 服务器之间或客户端与服务器之间维持心跳的时间间隔,也就是每个 tickTime 时间就会发送一个心跳。tickTime以毫秒为单位。
  • dataDir(数据文件目录):Zookeeper保存数据的目录,默认情况下,Zookeeper将写数据的日志文件也保存在这个目录里。
  • dataLogDir(日志文件目录):Zookeeper保存事务日志文件的目录。如果没有配置则存放在dataDir目录下。
  • clientPort(客户端连接端口):客户端连接 Zookeeper 服务器的端口,Zookeeper 会监听这个端口,接受客户端的访问请求。

集群模式下需要增加的配置

initLimit=5  
syncLimit=2  
server.N=YYY:A:B  
  • initLimit(LF初始通信时限): 集群中的follower服务器(F)与leader服务器(L)之间初始连接时能容忍的最多心跳数(tickTime的数量)。
  • syncLimit(LF同步通信时限): 集群中的follower服务器与leader服务器之间请求和应答之间能容忍的最多心跳数(tickTime的数量)。
  • server.N=YYY:A:B(服务器名称与地址:集群信息(服务器编号,服务器地址,LF通信端口,选举端口): 这个配置项的书写格式比较特殊,规则如下:

其中N表示服务器编号,YYY表示服务器的IP地址,A为LF通信端口,表示该服务器与集群中的leader交换的信息的端口。B为选举端口,表示选举新leader时服务器间相互通信的端口(当leader挂掉时,其余服务器会相互通信,选择出新的leader)。一般来说,集群中每个服务器的A端口都是一样,每个服务器的B端口也是一样。但是当所采用的为伪集群时,IP地址都一样,只能时A端口和B端口不一样。

正常集群的例子:

server.1=192.168.56.11:2181:2182
server.2=192.168.56.12:2181:2182
server.3=192.168.56.13:2181:2182

伪集群的例子:

server.1=192.168.56.11:2181:3181
server.2=192.168.56.11:2182:3182
server.3=192.168.56.11:2183:3183

单实例安装

安装zookeeper服务

cd /usr/local/src/
tar -zxf zookeeper-3.4.7.tar.gz 
mv zookeeper-3.4.7 /usr/local/
ln -s /usr/local/zookeeper-3.4.7/ /usr/local/zookeeper
cd /usr/local/zookeeper
cd conf/
cp zoo_sample.cfg zoo.cfg 

编辑配置文件

vim zoo.cfg 
tickTime=2000
dataDir=/data/zk
clientPort=2181

启动zookeeper服务

/usr/local/zookeeper/bin/zkServer.sh start 

检查服务是否正常启动

bin/zkCli.sh -server 127.0.0.1:2181

伪集群配置

cd /usr/local/src
wget http://mirrors.cnnic.cn/apache/zookeeper/stable/zookeeper-3.4.6.tar.gz
tar zxf zookeeper-3.4.6.tar.gz
mv zookeeper-3.4.6 /usr/local/
ln -s /usr/local/zookeeper-3.4.6/ /usr/local/zookeeper
cd /usr/local/zookeeper/conf/
mv zoo_sample.cfg zoo.cfg
vim zoo.cfg
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/data/zk1
clientPort=2181
server.1=192.168.56.11:3181:4181
server.2=192.168.56.11:3182:4182
server.3=192.168.56.11:3183:4183

创建三个目录用来存放zookeeper数据

mkdir /data/{zk1,zk2,zk3} -p
echo "1" >/data/zk1/myid
echo "2" >/data/zk2/myid
echo "3" >/data/zk3/myid

生成三份zookeeper配置文件

cp zoo.cfg zk1.cfg
cp zoo.cfg zk2.cfg
cp zoo.cfg zk3.cfg

修改zk2和zk3的配置,使用对应的数据目录和端口。

sed -i 's/zk1/zk2/g' zk2.cfg
sed -i 's/2181/2182/g' zk2.cfg
sed -i 's/zk1/zk3/g' zk3.cfg
sed -i 's/2181/2183/g' zk3.cfg 

启动Zookeeper

/usr/local/zookeeper/bin/zkServer.sh start /usr/local/zookeeper/conf/zk1.cfg
/usr/local/zookeeper/bin/zkServer.sh start /usr/local/zookeeper/conf/zk2.cfg
/usr/local/zookeeper/bin/zkServer.sh start /usr/local/zookeeper/conf/zk3.cfg

查看Zookeeper角色

/usr/local/zookeeper/bin/zkServer.sh status /usr/local/zookeeper/conf/zk1.cfg
#JMX enabled by default
#Using config: /usr/local/zookeeper/conf/zk1.cfg
#Mode: follower
/usr/local/zookeeper/bin/zkServer.sh status /usr/local/zookeeper/conf/zk2.cfg
#JMX enabled by default
#Using config: /usr/local/zookeeper/conf/zk2.cfg
#Mode: `leader`
/usr/local/zookeeper/bin/zkServer.sh status /usr/local/zookeeper/conf/zk3.cfg
#JMX enabled by default
#Using config: /usr/local/zookeeper/conf/zk3.cfg
#Mode: follower

连接Zookeeper

/usr/local/zookeeper/bin/zkCli.sh -server192.168.56.11:2181

通过上面的例子可以看到,目前zk2是leader,其它两个节点是follower。
本文由于实验环境局限使用的是伪分布式。
生产环境不建议使用。

集群模式

集群模式的配置和伪集群基本一致.
由于集群模式下, 各server部署在不同的机器上, 因此各server的conf/zoo.cfg文件可以完全一样.

下面是一个示例:

tickTime=2000 
initLimit=5 
syncLimit=2 
dataDir=/home/zookeeper/data 
dataLogDir=/home/zookeeper/logs 
clientPort=4180 
server.1=192.168.56.11:3181:4182
server.2=192.168.56.12:3181:4182
server.3=192.168.56.13:3181:4182

示例中部署了3台zookeeper server, 分别部署在192.168.56.11, 192.168.56.12, 1192.168.56.13上. 需要注意的是, 各server的dataDir目录下的myid文件中的数字必须不同,192.168.56.11 server的myid为1, 192.168.56.12 server的myid为2, 192.168.56.13server的myid为3。

java优化

看了你的问题, 我还特意的查看了ZooKeeper的启动脚本代码。ZooKeeper启动脚本没有加任何参数,也就是使用jvm默认的。
如果想要加大ZooKeeper的JVM使用内存。可以在更改{ZK_HOME}/bin/zkServer.sh,大约在109-110行。
nohup $JAVA "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" \
    -cp "$CLASSPATH" $JVMFLAGS $ZOOMAIN "$ZOOCFG" > "$_ZOO_DAEMON_OUT" 2>&1 < /dev/null &
改为
nohup $JAVA "-Xmx1G -Xms1G -Dzookeeper.log.dir=${ZOO_LOG_DIR}"...

jmap -heap 查看设置是否成功

zookeeper 运维

zookeeper高级配置参数

系统属性方式配置:即在Java中,可以通过在启动的命令行参数中添加-D参数来达到配置系统属性的目的,例如-Djava.library.path=/home/admin/jdk/lib, 就是通过系统属性来配置java.library.path的。

  • dataLogDir: 该参数有默认值:dataDir,可以不配置,不支持系统属性方式配置。参数dataLogDir用于配置Zookeeper服务器存储事务日志文件的目录。默认情况下,Zookeeper会将事务日志文件和快照数据存储在同一个目录中,使用者应尽量将这两者的目录区分开来。另外,如果条件允许,可以将事务日志的存储配置在一个单独的磁盘上。事务日志记录对于磁盘的性能要求非常高,为了保证数据的一致性,Zookeeper在返回客户端事务请求响应之前,必须将本次请求对应的事务日志写入到磁盘中。因此事务日志写入的性能直接决定了Zookeeper在处理事务请求时的吞吐。针对同一块磁盘的其他并发读写操作(例如Zookeeper运行时日志输出和操作系统自身的读写等),尤其是数据快照操作,会极大的影响事务日志的写性能。因此尽量给事务日志的输出配置一个单独的磁盘或是挂载点,将极大地提升Zookeeper的整体性能
  • initLimit: 该参数有默认值:10,即表示是参数tickTime值的10倍,必须配置,且需要配置一个正整数,不支持系统属性方式配置。该参数用于配置Leader服务器等待Follower启动,并完成数据同步的时间。Follower服务器在启动过程中,会与Leader建立连接并完成对数据的同步,从而确定自己对外提供服务的起始状态。通常情况下,运维人员不用太在意这个参数的配置,使用期默认值即可。但如果随着Zookeeper集群管理的数据量增大,Follower服务器在启动的时候,从Leader上进行同步数据的时间也会相应变长,于是无法在较短的时间完成数据同步。因此,在这种情况下,有必要适当调大这个参数。
  • syncLimit: 该参数有默认值:5,即表示是参数tickTime值的5倍,必须配置,且需要配置一个正整数,不支持系统属性当时配置。该参数用于配置Leader服务器和Follower之间进行心跳检测的最大延时事件。在Zookeeper集群运行过程中,Leader服务器会与所有的Follower进行心跳检测来确定该服务器是否存活。如果Leader服务器在syncLimit时间内无法获取到Follower的心跳检测响应,那么Leader就会认为该Follower已经脱离了和自己的同步。通常情况下,运维人员使用该参数的默认值即可,但如果部署Zookeeper集群的网络环境质量较低(例如网络延时较大或丢包严重),那么可以适当调大这个参数。
  • snapCount: 该参数有默认值:100000,可以不配置,仅支持系统属性方式配置:zookeeper.snapCount。参数snapCount用于配置相邻两次数据快照之间的事务操作次数,即Zookeeper会在snapCount次事务操作之后进行一次数据快照。
  • preAllocSize: 该参数有默认值:65536,单位是KB,即64MB,可以不配置,仅支持系统属性方式配置:zookeeper.preAllocSize。参数preAlloSize用于配置Zookeeper事务日志文件预分配的磁盘空间大小。通常情况下,我们使用Zookeeper的默认配置65536KB即可,但是如果我们将参数snapCount设置得比默认值更小或更大,那么preAllocSize参数也要随之做出变更。举例来说:如果我们将snapCount的值设置为500,同时预估每次事务操作的数据量大小至多1KB,那么参数preAllocSize设置为500就足够了。
  • maxClientCnxns: 该参数有默认值:60,可以不配置,不支持系统属性方式配置。从Socket层面限制单个客户端与单台服务器之间的并发连接数,即以IP地址粒度来进行连接数的限制。如果将该参数设置为0,则表示对连接数不做任何限制。需要注意该连接数限制选项的适用范围,其仅仅是对单台客户端机器与单台Zookeeper服务器之间的连接数限制,并不能控制所有客户端的连接数总和。另外,在3.4.0版本以前该参数的默认值都是10,从3.4.0版本开始变成了60,因此运维人员尤其需要注意这个变化,以防Zookeeper版本变化带来服务器连接数限制变化的隐患。
  • clientPortAddress: 该参数没有默认值:可以不配置,不支持系统属性方式配置。针对那些多网卡的机器,该参数允许为每个IP地址制定不同的监听端口。
  • forceSync: 该参数有默认值:yes,可以不配置,可选配置项为yes和no,仅支持系统属性方式配置:zookeeper.forceSync。该参数用于配置Zookeeper服务器是否在事务提交的时候,将日志写入操作强制刷入磁盘(即调用java.nio.channels.FileChannel.force接口),默认情况下是yes,即每次事务日志写入操作都会实时刷入磁盘。如果将其设置为no。则能一定程度的提高Zookeeper的写性能,但同事也会存在类似于机器断电这样的安全风险。
  • globalOutstandingLimit: 该参数有默认值:1000,可以不配置,仅支持系统属性方式配置:zookeeper.globalOutstandingLimit。参数globalOutstandingLimit用于配置Zookeeper服务器最大请求堆积数量。在Zookeeper服务器运行的过程中,客户端会源源不断的将请求发送到服务端,为了防止服务端资源(包括CPU,内存和网络等)耗尽,服务端必须限制同时处理的请求数,即最大请求堆积数量。
  • leaderServes: 该参数有默认值:yes,可以不配置,可选配置项为yes和no,仅支持系统属性方式配置:zookeeper.leaderServes。该参数用于配置Leader服务器是否能够接受客户端的连接,即是否允许Leader向客户端提供服务,默认情况下,Leader服务器能够接受并处理客户端的所有读写请求。在Zookeeper的架构设计中,Leader服务器主要用来进行对事务更新请求的协调以及集群本身的运行时协调,因此,可以设置让Leader服务器不接受客户端的连接,以使其专注于进行分布式协调。
  • SkipAcl: 该参数有默认值:no,可以不配置,可选配置项为yes和no,仅支持系统属性方式配置:zookeeper.skipACL。该参数用于配置Zookeeper服务器是否跳过ACL权限检查,默认情况下是no,即会对每一个客户端请求进行权限检查。如果将其设置为yes,则能一定程度的提高Zookeeper的读写性能,但同时也会向所有客户端开放Zookeeper的数据,包括那些之前设置过ACL权限的数据节点,也将不再接收权限控制。
  • xncTimeout: 该参数有默认值:5000,单位是毫秒,可以不配置,仅支持系统属性方式配置:zookeeper.cnxTimeout。该参数用于配置在Leader选举过程中,各服务器之间进行TCP连接创建的超时时间。
  • electionAlg: 在之前的版本中,可以使用该参数来配置选择Zookeeper进行Leader选举时所使用的算法,但从3.4.0版本开始,Zookeeper废弃了其他选举算法,只留下了FastLeaderElection算法,因此该参数目前看来没有用了。
  • jute.maxbuffer: 该参数有默认值:1048575,单位是字节,可以不配置,仅支持系统属性方式配置:jute.maxbuffer。该参数用于配置单个数据节点(ZNode)上可以存储的最大数据量大小。通常情况下,运维人员不需要改动该参数,同时考虑到Zookeeper上不适宜存储太多的数据,往往还需要将该参数设置得更小。需要注意的是,在变更该参数的时候,需要在Zookeeper集群的所有机器以及所有的客户端上均设置才能生效。
  • minSessionTimeout/maxSessionTimeout: 这两个参数有默认值,分别是参数tickTime值的2倍和20倍,即默认的会话超时时间在2tickTime~20tickTime范围内,单位毫秒,可以不配置,不支持系统属性方式配置。这两个参数用于服务端对客户端会话的超时时间进行限制,如果客户端设置的超时事件不再该范围内,那么会被服务端强制设置为最大或最小超时时间
  • autopurge.snapRetainCount: 该参数有默认值:3,可以不配置,不支持系统属性方式配置。从3.4.0版本开始,Zookeeper提供了对历史事务日志和快照数据自动清理的支持。参数autopurge.snapRetainCount用于配置Zookeeper在自动清理的时候需要保留的快照数据文件数量和对应的事务日志万能键。需要注意的是,并不是磁盘上的所有事务日志和快照数据文件都可以被清理掉–那样的话将无法恢复数据。因此参数autopurge.snapRetainCount的最小值是3,如果配置的autopurge.snapRetainCount值小于3的话,那么会被自动调整到3,即至少需要保留3个快照数据文件和对应的事务日志文件
  • autopurge.purgeInteval: 该参数有默认值:0,单位是小时,可以不配置,不支持系统属性方式配置。参数autopurge.purgeInterval和参数autopurge.snapRetainCount配套使用,用于配置Zookeeper进行历史文件自动清理的频率。如果配置该值为0或者负数,那么就表明不需要开启定时清理功能。Zookeeper默认不开启这项功能。
  • fsync.warningthresholdms: 该参数有默认值:1000,单位是毫秒,可以不配置,仅支持系统属性方式配置:fsync.warningthresholdms。参数fsync.warningthresholdms用于配置Zookeeper进行事务日志fsync操作时消耗时间的报警阈值。一旦进行一个fsync操作消耗的事件大于参数fsync.warningthresholdms指定的值,那么就在日志中打印出报警日志。

zookeeper常用四字命令

  • echo conf|nc 192.168.56.12 2181: 输出相关服务配置的详细信息。
  • echo cons|nc 192.168.56.12 2181: 列出所有连接到服务器的客户端会话的详细信息。客户端IP,会话ID和最后一次与服务器交互的操作类型等。
  • echo crst|nc 192.168.56.12 2181: 功能性命令,用于重置所有的客户端连接统计信息。
  • echo dump|nc 192.168.56.12 2181: 用于输出当前集群的所有会话信息,包括未经处理的会话和临时节点。
  • echo envi|nc 192.168.56.12 2181: 输出关于服务环境的详细信息(区别于 conf 命令)包括:os.version,java.version,user.home等。
  • echo ruok|nc 192.168.56.12 2181: 测试是否启动了该Server,若回复imok表示已经启动。
  • echo stat|nc 192.168.56.12 2181: 来查看哪个节点被选择作为follower或者leader。
  • echo srvr|nc 192.168.56.12 2181: 和stat命令的功能一致,唯一的区别是srvr不会将客户端的连接情况输出。
  • echo reqs|nc 192.168.56.12 2181: 列出未经处理的请求。
  • echo srst|nc 192.168.56.12 2181: 功能性,用于重置所有服务器的统计信息。
  • echo wchs|nc 192.168.56.12 2181: 列出服务器 watch 的详细信息。
  • echo wchc|nc 192.168.56.12 2181: 通过 session 列出服务器 watch 的详细信息,它的输出是一个与 watch 相关的会话的列表。
  • echo wchp|nc 192.168.56.12 2181: 通过路径列出服务器 watch 的详细信息。它输出一个与 session 相关的路径。
  • echo mntr|nc 192.168.56.12 2181: 用于输出比stat命令更为详细的服务器统计信息,包括请求处理的延迟情况,服务器内存数据库大小和集群同步情况,每一行都是一个kv结构。可以用于监控。
  • echo kill|nc 192.168.56.12 2181: 关掉server

zookeeper 使用JMX监控

`vim bin/zkServer.sh`

#修改前
#ZOOMAIN="-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.local.only=$JMXLOCALONLY org.apache.zookeeper.server.quorum.QuorumPeerMa
in"

#修改后
ZOOMAIN="-Dcom.sun.management.jmxremote  -Dcom.sun.management.jmxremote.local.only=false  
 -Djava.rmi.server.hostname=192.168.56.22  
 -Dcom.sun.management.jmxremote.port=9991  
 -Dcom.sun.management.jmxremote.ssl=false
 -Dcom.sun.management.jmxremote.authenticate=false  
 -Dzookeeper.jmx.log4j.disable=true  
 org.apache.zookeeper.server.quorum.QuorumPeerMain"

更改完以后重启zookeeper服务器,使用JMX客户端连接服务器:

Mbean标签页,可以看到org.apache.Zookeeper节点,可以看到服务器的基本信息,当前使用的选举算法(ElectionType),单个客户的最大连接数(MaxClientCnxnsPerHost),会话的超时时间和启动时间等。

阿里使用taokafka进行监控。

集群数量的补充

zookeeper任意集群数量都可以成功。官方之所以说奇数是对zookeeper的过半数存活即可用有误解。
比如:我们想要搭建允许F台服务down掉的集群,那么就要部署一个有2xF+1台服务器构成的zookeeper集群。因此一个3台机器构成的zookeeper集群,能够在挂掉1台机器后依然正常工作,而对于一个由5台服务器构成的zookeeper集群,能够对2台机器挂掉的情况下进行容灾,但是如果是一个由6台机器构成的zookeeper集群,同样只能挂掉2台机器,如果挂掉3台,剩下的机器就无法实现过半了。
但是6台跟5台容灾能力没有任何显著的优势,所有官方建议奇数。

扩容和缩容

由于zookeeper对于水平扩容的支持不是特别好,所有,水平扩容只能通过整体集群重启,或逐台进行重启。

整体集群重启前建立的客户端会话,并不会因为重启而失效。也就是说整体重启期间花费的时间将不计入会话超时时间的计算中。

数据和日志管理

zookeeper会不断写入数据快照和事务日志,zookeeper本身不会自己删除过期的数据,需要自己手动清除。

方法一:写shell脚本

datalogdir=//data/app/zookeeper/zk_log/version-2
datadir=/data/app/zookeeper/zk_data/version-2
logdir=/var/log/zookeeper
count=60
count=$[$count+1]
ls -t $datalogdir/log.* |tail -n _$count |xargs rm -f 
ls -t $datadir/snapshot.* |tail -n _$count |xargs rm -f 
ls -t $logdir/zookeeper.log.* |tail -n _$count |xargs rm -f 

方法二:使用官方提供的日志清理jar包
PurgeTxnLog 它的实现了一种简单的历史文件清理策略,可以在这里看一下他的使用方法:http://zookeeper.apache.org/doc/r3.4.3/api/index.html,可以指定要清理的目录和需要保留的文件数目

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

方法三:使用自带的cleanup.sh脚本

zkCleanup.sh -n 15 

方法四:配置文件中增加自动删除的参数。
3.4 以后增加了自动清空历史快照数据和事务日志文件的机制,参考:autopurge.snapRetainCount 和 autopurge.purgeInterval 。

  • autopurge.purgeInterval 这个参数指定了清理频率,单位是小时,需要填写一个1或更大的整数,默认是0,表示不开启自己清理功能。
  • autopurge.snapRetainCount 这个参数和上面的参数搭配使用,这个参数指定了需要保留的文件数目。默认是保留3个。

磁盘选择

使用单独的磁盘作为事务日志的输出目录。一方面事务日志的写性能对zookeeper处理客户端请求,尤其是更新操作的出来性能影响很大。另一方面,zookeeper事务日志输出是一个顺序写文件的过程,因此本身性能是非常高的,所有尽量保证不要和应用程序共享一块磁盘,以避免对磁盘的竞争。一般zookeeper事务日志和快照数据分别配置在两块单独挂载的硬盘上。
尽量避免内存与磁盘空间的交换。如果希望zookeeper能够提供完全实时的服务,那么就不能出现此类内存与磁盘空间交换的现象。因此在分配JVM堆大小的时候一定要非常小心。

zookeeper client 命令

创建

create 可以创建一个zookeeper节点

create [-s] [-e] path data acl
  • -s 或 -e 分别指定节点特性:顺序和临时节点。默认情况下不添加-s或-e参数的,创建的是持久节点。
create /zk-book 123 

读取

读取可以用ls或set命令。
ls 可以列出zookeeper指定节点下的所有子节点。当然,这个命令只能看到指定节点下第一级的所有子节点。

 ls path [watch] 
  • path表示的是指定节点数据的节点路径。
ls /  

第一次不是的zookeeper集群,默认在根节点下面有一个叫做zookeeper的保留节点。

get命令可以获取zookeeper指定节点的数据内容和属性信息。

get path [watch]
get /zk-book

第一行是节点/zk-book的数据内容,其他机会则是创建改节点的事务id(cZxid),最后一次更新改节点的事务ID(mZxid)和最后一次更新改节点的时间(mtime)等属性信息。

更新

使用set命令可以更新指定节点的数据内容。

set path data [version]

data 就是要更新的新内容。set命令后面还有一个version参数,在zookeeper中,节点的数据是有版本概念的,这个参数用于指定本次更新操作是基于ZNode的哪一个数据版本进行的。

set /zk-book 456
  • 更新完毕后,dataVersion的值由原来的0变成1,这是因为更新操作导致该节点的数据版本也发生了变更。

删除

使用delete 命令,可以删除zookeeper上的指定节点

delete path [version]

此命令中的version参数跟set命令中的version参数的作用是一致的。

delete /zk-book

要想删除某一个指定节点,改节点必须没有子节点存在,可以通过执行如下命令来进行验证

create /zk-book 123
create /zk-book/child 12345
delete /zk-book

参考文档

分布式服务框架 Zookeeper -- 管理分布式环境中的数据
zookeeper深入浅出
从PAXOS到ZOOKEEPER分布式一致性原理与实践
Zookeeper原理与优化
zookeeper 可视化管理工具

posted @ 2017-11-02 09:56  biglittleant  阅读(598)  评论(0编辑  收藏  举报