ZooKeeper是一个开放源码的分布式应用程序协调服务,它包含一个简单的原语集,分布式应用程序可以基于它实现同步服务,配置维护和命名服务等。
Zookeeper设计目的
- 最终一致性:client不论连接到那个Server,展示给它的都是同一个视图。
- 可靠性:具有简单、健壮、良好的性能、如果消息m被到一台服务器接收,那么消息m将被所有服务器接收。
- 实时性:Zookeeper保证客户端将在一个时间间隔范围内获得服务器的更新信息,或者服务器失效的信息。但由于网络延时等原因,Zookeeper不能保证两个客户端能同时得到刚更新的数据,如果需要最新数据,应该在读数据之前调用sync()接口。
- 等待无关(wait-free):慢的或者失效的client不得干预快速的client的请求,使得每个client都能有效的等待。
- 原子性:更新只能成功或者失败,没有中间状态。
- 顺序性:包括全局有序和偏序两种:全局有序是指如果在一台服务器上消息a在消息b前发布,则在所有Server上消息a都将在消息b前被发布;偏序是指如果一个消息b在消息a后被同一个发送者发布,a必将排在b前面。
Zookeeper工作原理
在zookeeper的集群中,各个节点共有下面3种角色和4种状态:
角色:leader、follower、observer
状态:leading、following、observing、looking
Zookeeper的核心是原子广播,这个机制保证了各个Server之间的同步。实现这个机制的协议叫做Zab协议(ZooKeeper Atomic Broadcast protocol)。Zab协议有两种模式,它们分别是恢复模式(Recovery选主)和广播模式(Broadcast同步)。当服务启动或者在领导者崩溃后,Zab就进入了恢复模式,当领导者被选举出来,且大多数Server完成了和leader的状态同步以后,恢复模式就结束了。状态同步保证了leader和Server具有相同的系统状态。
为了保证事务的顺序一致性,zookeeper采用了递增的事务id号(zxid)来标识事务。所有的提议(proposal)都在被提出的时候加上了zxid。实现中zxid是一个64位的数字,它高32位是epoch用来标识leader关系是否改变,每次一个leader被选出来,它都会有一个新的epoch,标识当前属于那个leader的统治时期。低32位用于递增计数。
每个Server在工作过程中有4种状态:
- LOOKING:当前Server不知道leader是谁,正在搜寻。
- LEADING:当前Server即为选举出来的leader。
- FOLLOWING:leader已经选举出来,当前Server与之同步。
- OBSERVING:observer的行为在大多数情况下与follower完全一致,但是他们不参加选举和投票,而仅仅接受(observing)选举和投票的结果。
Zookeeper数据与存储
1. 内存数据
Zookeeper的数据模型是树结构,在内存数据库中,存储了整棵树的内容,包括所有的节点路径、节点数据、ACL信息,Zookeeper会定时将这个数据存储到磁盘上。
1) DataTree
DataTree是内存数据存储的核心,是一个树结构,代表了内存中一份完整的数据。DataTree不包含任何与网络、客户端连接及请求处理相关的业务逻辑,是一个独立的组件。
2) DataNode
DataNode是数据存储的最小单元,其内部除了保存了结点的数据内容、ACL列表、节点状态之外,还记录了父节点的引用和子节点列表两个属性,其也提供了对子节点列表进行操作的接口。
3) KDatabase
Zookeeper的内存数据库,管理Zookeeper的所有会话、DataTree存储和事务日志。ZKDatabase会定时向磁盘dump快照数据,同时在Zookeeper启动时,会通过磁盘的事务日志和快照文件恢复成一个完整的内存数据库。
2. 事务日志
1) 文件存储
在配置Zookeeper集群时需要配置dataDir目录,其用来存储事务日志文件。也可以为事务日志单独分配一个文件存储目录:dataLogDir。若配置dataLogDir为/data/zookeeper-3.5.3-beta/logs,那么Zookeeper在运行过程中会在该目录下建立一个名字为version-2的子目录,该目录确定了当前Zookeeper使用的事务日志格式版本号,当下次某个Zookeeper版本对事务日志格式进行变更时,此目录也会变更,即在version-2子目录下会生成一系列文件大小一致(64MB)的文件。
2) 日志格式
在配置好日志文件目录,启动Zookeeper后, 会在zoo.cfg文件里配置的日志目录/data/zookeeper-3.5.3-beta/logs/version-2下产生日志文件, 如:
3) 日志写入
FileTxnLog负责维护事务日志对外的接口,包括事务日志的写入和读取等。Zookeeper的事务日志写入过程大体可以分为如下6个步骤:
a) 确定是否有事务日志可写。当Zookeeper服务器启动完成需要进行第一次事务日志的写入,或是上一次事务日志写满时,都会处于与事务日志文件断开的状态,即Zookeeper服务器没有和任意一个日志文件相关联。因此在进行事务日志写入前,Zookeeper首先会判断FileTxnLog组件是否已经关联上一个可写的事务日志文件。若没有,则会使用该事务操作关联的ZXID作为后缀创建一个事务日志文件,同时构建事务日志的文件头信息,并立即写入这个事务日志文件中去,同时将该文件的文件流放入streamToFlush集合,该集合用来记录当前需要强制进行数据落盘的文件流。
b) 确定事务日志文件是否需要扩容(预分配)。Zookeeper会采用磁盘空间预分配策略。当检测到当前事务日志文件剩余空间不足4096字节时,就会开始进行文件空间扩容,即在现有文件大小上,将文件增加65536KB(64MB),然后使用"0"填充被扩容的文件空间。
c) 事务序列化。对事务头和事务体的序列化,其中事务体又可分为会话创建事务、节点创建事务、节点删除事务、节点数据更新事务等。
d) 生成Checksum。为保证日志文件的完整性和数据的准确性,Zookeeper在将事务日志写入文件前,会计算生成Checksum。
e) 写入事务日志文件流。将序列化后的事务头、事务体和Checksum写入文件流中,此时并为写入到磁盘上。
f) 事务日志刷入磁盘。由于步骤5中的缓存原因,无法实时地写入磁盘文件中,因此需要将缓存数据强制刷入磁盘。
4) 日志截断
在Zookeeper运行过程中,可能出现非Leader记录的事务ID比Leader上大,这是非法运行状态。此时,需要保证所有机器必须与该Leader的数据保持同步,即Leader会发送TRUNC命令给该机器,要求进行日志截断,Learner收到该命令后,就会删除所有包含或大于该事务ID的事务日志文件。
3. snapshot-数据快照
数据快照是Zookeeper数据存储中非常核心的运行机制,数据快照用来记录Zookeeper服务器上某一时刻的全量内存数据内容,并将其写入指定的磁盘文件中。
1) 文件存储
与事务文件类似,Zookeeper快照文件也可以指定特定磁盘目录,通过dataDir属性来配置。若指定dataDir为/home/admin/zkData/zk_data,则在运行过程中会在该目录下创建version-2的目录,该目录确定了当前Zookeeper使用的快照数据格式版本号。在Zookeeper运行时,会生成一系列文件。例如:
2) 数据快照
FileSnap负责维护快照数据对外的接口,包括快照数据的写入和读取等,将内存数据库写入快照数据文件其实是一个序列化过程。针对客户端的每一次事务操作,Zookeeper都会将他们记录到事务日志中,同时也会将数据变更应用到内存数据库中,Zookeeper在进行若干次事务日志记录后,将内存数据库的全量数据Dump到本地文件中,这就是数据快照。其步骤如下:
a) 确定是否需要进行数据快照。每进行一次事务日志记录之后,Zookeeper都会检测当前是否需要进行数据快照,考虑到数据快照对于Zookeeper机器的影响,需要尽量避免Zookeeper集群中的所有机器在同一时刻进行数据快照。采用过半随机策略进行数据快照操作。
b) 切换事务日志文件。表示当前的事务日志已经写满,需要重新创建一个新的事务日志。
c) 创建数据快照异步线程。创建单独的异步线程来进行数据快照以避免影响Zookeeper主流程。
d) 获取全量数据和会话信息。从ZKDatabase中获取到DataTree和会话信息。
e) 生成快照数据文件名。Zookeeper根据当前已经提交的最大ZXID来生成数据快照文件名。
f) 数据序列化。首先序列化文件头信息,然后再对会话信息和DataTree分别进行序列化,同时生成一个Checksum,一并写入快照数据文件中去。
4. 初始化
在Zookeeper服务器启动期间,首先会进行数据初始化工作,用于将存储在磁盘上的数据文件加载到Zookeeper服务器内存中。
1) 初始化流程
Zookeeper的初始化过程如下图所示:
数据的初始化工作是从磁盘上加载数据的过程,主要包括了从快照文件中加载快照数据和根据实物日志进行数据修正两个过程。
初始化FileTxnSnapLog: FileTxnSnapLog是Zookeeper事务日志和快照数据访问层,用于衔接上层业务和底层数据存储,底层数据包含了事务日志和快照数据两部分。FileTxnSnapLog中对应FileTxnLog和FileSnap。
初始化ZKDatabase: 首先构建DataTree,同时将FileTxnSnapLog交付ZKDatabase,以便内存数据库能够对事务日志和快照数据进行访问。在ZKDatabase初始化时,DataTree也会进行相应的初始化工作,如创建一些默认结点,如/、/zookeeper、/zookeeper/quota三个节点。
创建PlayBackListener: 其主要用来接收事务应用过程中的回调,在Zookeeper数据恢复后期,会有事务修正过程,此过程会回调PlayBackListener来进行对应的数据修正。
处理快照文件: 此时可以从磁盘中恢复数据了,首先从快照文件开始加载。
获取最新的100个快照文件: 更新时间最晚的快照文件包含了最新的全量数据。
解析快照文件: 逐个解析快照文件,此时需要进行反序列化,生成DataTree和sessionsWithTimeouts,同时还会校验Checksum及快照文件的正确性。对于100个快找文件,如果正确性校验通过时,通常只会解析最新的那个快照文件。只有最新快照文件不可用时,才会逐个进行解析,直至100个快照文件全部解析完。若将100个快照文件解析完后还是无法成功恢复一个完整的DataTree和sessionWithTimeouts,此时服务器启动失败。
获取最新的ZXID: 此时根据快照文件的文件名即可解析出最新的ZXID:zxid_for_snap。该ZXID代表了Zookeeper开始进行数据快照的时刻。
处理事务日志: 此时服务器内存中已经有了一份近似全量的数据,现在开始通过事务日志来更新增量数据。
获取所有zxid_for_snap之后提交的事务: 此时,已经可以获取快照数据的最新ZXID。只需要从事务日志中获取所有ZXID比步骤7得到的ZXID大的事务操作。
事务应用: 获取大于zxid_for_snap的事务后,将其逐个应用到之前基于快照数据文件恢复出来的DataTree和sessionsWithTimeouts。每当有一个事务被应用到内存数据库中后,Zookeeper同时会回调PlayBackListener,将这事务操作记录转换成Proposal,并保存到ZKDatabase的committedLog中,以便Follower进行快速同步。
获取最新的ZXID: 待所有的事务都被完整地应用到内存数据库中后,也就基本上完成了数据的初始化过程,此时再次获取ZXID,用来标识上次服务器正常运行时提交的最大事务ID。
校验epoch: epoch标识了当前Leader周期,集群机器相互通信时,会带上这个epoch以确保彼此在同一个Leader周期中。完成数据加载后,Zookeeper会从步骤11中确定ZXID中解析出事务处理的Leader周期:epochOfZxid。同时也会从磁盘的currentEpoch和acceptedEpoch文件中读取上次记录的最新的epoch值,进行校验。
5. 数据同步
整个集群完成Leader选举后,Learner会向Leader进行注册,当Learner向Leader完成注册后,就进入数据同步环节,同步过程就是Leader将那些没有在Learner服务器上提交过的事务请求同步给Learner服务器,大体过程如下:
获取Learner状态: 在注册Learner的最后阶段,Learner服务器会发送给Leader服务器一个ACKEPOCH数据包,Leader会从这个数据包中解析出该Learner的currentEpoch和lastZxid。
数据同步初始化: 首先从Zookeeper内存数据库中提取出事务请求对应的提议缓存队列proposals,同时完成peerLastZxid(该Learner最后处理的ZXID)、minCommittedLog(Leader提议缓存队列commitedLog中最小的ZXID)、maxCommittedLog(Leader提议缓存队列commitedLog中的最大ZXID)三个ZXID值的初始化。
对于集群数据同步而言,通常分为四类: 直接差异化同步(DIFF同步)、先回滚再差异化同步(TRUNC+DIFF同步)、仅回滚同步(TRUNC同步)、全量同步(SNAP同步),在初始化阶段,Leader会优先以全量同步方式来同步数据。同时,会根据Leader和Learner之间的数据差异情况来决定最终的数据同步方式。
-> 直接差异化同步(DIFF同步: peerLastZxid介于minCommittedLog和maxCommittedLog之间)。Leader首先向这个Learner发送一个DIFF指令,用于通知Learner进入差异化数据同步阶段,Leader即将把一些Proposal同步给自己,针对每个Proposal,Leader都会通过发送PROPOSAL内容数据包和COMMIT指令数据包来完成,
-> 先回滚再差异化同步(TRUNC+DIFF同步: Leader已经将事务记录到本地事务日志中,但是没有成功发起Proposal流程)。当Leader发现某个Learner包含了一条自己没有的事务记录,那么就需要该Learner进行事务回滚,回滚到Leader服务器上存在的,同时也是最接近于peerLastZxid的ZXID。
-> 仅回滚同步(TRUNC同步: peerLastZxid大于maxCommittedLog)。Leader要求Learner回滚到ZXID值为maxCommittedLog对应的事务操作。
-> 全量同步(SNAP同步: peerLastZxid小于minCommittedLog或peerLastZxid不等于lastProcessedZxid)。Leader无法直接使用提议缓存队列和Learner进行同步,因此只能进行全量同步。Leader将本机的全量内存数据同步给Learner。Leader首先向Learner发送一个SNAP指令,通知Learner即将进行全量同步,随后,Leader会从内存数据库中获取到全量的数据节点和会话超时时间记录器,将他们序列化后传输给Learner。Learner接收到该全量数据后,会对其反序列化后载入到内存数据库中。
Zookeeper集群节点
- Zookeeper节点部署越多,服务的可靠性越高,建议部署奇数个节点,因为zookeeper集群是以宕机个数过半才会让整个集群宕机的。
- 需要给每个zookeeper 1G左右的内存,如果可能的话,最好有独立的磁盘,因为独立磁盘可以确保zookeeper是高性能的。如果你的集群负载很重,不要把zookeeper和RegionServer运行在同一台机器上面,就像DataNodes和TaskTrackers一样。
ZooKeeper常用命令
Zookeeper服务端命令
启动ZK服务: sh bin/zkServer.sh start
查看ZK服务状态: sh bin/zkServer.sh status
停止ZK服务: sh bin/zkServer.sh stop
重启ZK服务: sh bin/zkServer.sh restart
Zookeeper客户端命令
客户端登录Zookeeper: sh bin/zkCli.sh -server 127.0.0.1:2181
显示根目录下、文件: ls / #使用 ls 命令来查看当前ZooKeeper中所包含的内容。即查看Zookeeper当前注册了哪些服务?
显示根目录下、文件: ls2 / #查看当前节点数据并能看到更新次数等数据
创建文件,并设置初始内容: create /zk "test" #创建一个新的znode节点"zk"以及与它关联的字符串
获取文件内容: get /zk #确认znode是否包含我们所创建的字符串
修改文件内容: set /zk "zkbak" #对zk所关联的字符串进行设置
删除文件: delete /zk #将刚才创建的znode删除
退出客户端: quit
帮助命令: help
ZooKeeper的"watch"机制说明
zookeeper作为一款成熟的分布式协调框架,订阅-发布功能是很重要的一个。所谓订阅发布功能,其实说白了就是观察者模式。观察者会订阅一些感兴趣的主题,然后这些主题一旦变化了,就会自动通知到这些观察者。
1) zookeeper的watch机制原理
zookeeper的订阅发布也就是watch机制,是一个轻量级的设计,因为它采用了一种推拉结合的模式。一旦服务端感知主题变了,那么只会发送一个事件类型和节点信息给关注的客户端,而不会包括具体的变更内容,所以事件本身是轻量级的,这就是所谓的"推"部分,然后收到变更通知的客户端需要自己去拉变更的数据,这就是"拉"部分。znode以某种方式发生变化时,"观察"(watch)机制可以让客户端得到通知.可以针对ZooKeeper服务的“操作”来设置观察,该服务的其他 操作可以触发观察。
订阅-发布在zookeeper中是通过事件注册和回调机制实现的,整个注册回调过程分为三个大的部分:客户端注册,服务端发回事件,客户端回调。
2) zookeeper的watch架构
watch的整体流程如下图所示,客户端先向zookeeper服务端成功注册想要监听的节点状态,同时客户端本地会存储该监听器相关的信息在WatchManager中,当zookeeper服务端监听的数据状态发生变化时,zookeeper就会主动通知发送相应事件信息给相关会话客户端,客户端就会在本地响应式的回调相关watcher的Handler。
3) zookeeper的watch特性
watch是一次性的,每次都需要重新注册,并且客户端在会话异常结束时不会收到任何通知,而快速重连接时仍不影响接收通知。watch的回调执行都是顺序执行的,并且客户端在没有收到关注数据的变化事件通知之前是不会看到最新的数据,另外需要注意不要在Watch回调逻辑中阻塞整个客户端的watch回调。watch是轻量级的,WatchEvent是最小的通信单元,结构上只包含通知状态、事件类型和节点路径。ZooKeeper服务端只会通知客户端发生了什么,并不会告诉具体内容。
Zookeeper集群部署记录
1)服务器信息 主机名 系统 IP地址 sign-zk01.wangshibo.cn CentOS release 6.8 172.16.51.198 sign-zk02.wangshibo.cn CentOS release 6.8 172.16.51.199 sign-zk03.wangshibo.cn CentOS release 6.8 172.16.51.200 2)安装过程 先记录sign-zk01.wangshibo.cn节点机的部署过程(其他两个节点的部署过程基本一致) 安装jdk 可以yum安装自带的jdk [root@sign-zk01 ~]# yum -y install java-1.7.0-openjdk* [root@sign-zk01 ~]# java -version java version "1.7.0_151" OpenJDK Runtime Environment (rhel-2.6.11.0.el6_9-x86_64 u151-b00) OpenJDK 64-Bit Server VM (build 24.151-b00, mixed mode) 不过建议卸载掉自带的jdk,安装Java OpenJDK [root@sign-zk01 ~]# yum -y remove java-1.7.0-openjdk* [root@sign-zk01 ~]# yum -y remove tzdata-java.noarch [root@sign-zk01 ~]# java -version -bash: java: command not found [root@sign-zk01 ~]# rpm -ivh jdk-7u79-linux-x64.rpm --force [root@sign-zk01 ~]# java -version java version "1.7.0_79" Java(TM) SE Runtime Environment (build 1.7.0_79-b15) Java HotSpot(TM) 64-Bit Server VM (build 24.79-b02, mixed mode) 安装并部署zookeeper集群 [app@sign-zk01 ~]$ cd /usr/local/src/ [app@sign-zk01 src]$ wget http://mirror.bit.edu.cn/apache/zookeeper/zookeeper-3.4.11/zookeeper-3.4.11.tar.gz [app@sign-zk01 src]$ tar -zvxf zookeeper-3.4.11.tar.gz [app@sign-zk01 src]$ mv zookeeper-3.4.11.tar.gz /data/ [app@sign-zk01 src]$ cd /data/zookeeper-3.4.11/conf/ [app@sign-zk01 conf]$ cp zoo_sample.cfg zoo.cfg [app@sign-zk01 conf]$ cat zoo.cfg |grep -v "#" tickTime=2000 initLimit=10 syncLimit=5 dataDir=/data/zookeeper-3.4.11/data dataLogDir=/data/zookeeper-3.4.11/logs clientPort=2181 server.1=sign-zk01.wangshibo.cn:2888:3888 server.2=sign-zk02.wangshibo.cn:2888:3888 server.3=sign-zk03.wangshibo.cn:2888:3888 参数说明: tickTime这个时间是作为zookeeper服务器之间或客户端与服务器之间维持心跳的时间间隔,也就是说每个tickTime时间就会发送一个心跳。 initLimit这个配置项是用来配置zookeeper接受客户端(这里所说的客户端不是用户连接zookeeper服务器的客户端,而是zookeeper服务器集群中连接到leader的follower 服务器)初始化连接时最长能忍受多少个心跳时间间隔数。 当已经超过10个心跳的时间(也就是tickTime)长度后 zookeeper 服务器还没有收到客户端的返回信息,那么表明这个客户端连接失败。总的时间长度就是 10*2000=20秒。 syncLimit这个配置项标识leader与follower之间发送消息,请求和应答时间长度,最长不能超过多少个tickTime的时间长度,总的时间长度就是5*2000=10秒。 dataDir顾名思义就是zookeeper保存数据的目录,默认情况下zookeeper将写数据的日志文件也保存在这个目录里; clientPort这个端口就是客户端连接Zookeeper服务器的端口,Zookeeper会监听这个端口接受客户端的访问请求; server.A=B:C:D中的A是一个数字,表示这个是第几号服务器,B是这个服务器的IP地址,C第一个端口用来集群成员的信息交换,表示这个服务器与集群中的leader服务器交换信息的端口,D是在leader挂掉时专门用来进行选举leader所用的端口。 [app@sign-zk01 conf]$ mkdir /data/zookeeper-3.4.11/data [app@sign-zk01 conf]$ mkdir /data/zookeeper-3.4.11/logs 创建ServerID标识(这是三个节点机器唯一配置不一样的地方,三个节点的myid文件配置的ServerID不能重复,建议分别配置成1、2、3) 除了修改zoo.cfg配置文件外,zookeeper集群模式下还要配置一个myid文件,这个文件需要放在dataDir目录下。 这个文件里面有一个数据就是A的值(该A就是zoo.cfg文件中server.A=B:C:D中的A),在zoo.cfg文件中配置的dataDir路径中创建myid文件。 [app@sign-zk01 ~]$ echo "1" > /data/zookeeper-3.4.11/data/myid [app@sign-zk02 ~]$ echo "2" > /data/zookeeper-3.4.11/data/myid [app@sign-zk03 ~]$ echo "3" > /data/zookeeper-3.4.11/data/myid 然后分别重启三个节点机器的zookeeper(stop/start/restart) [app@sign-zk01 ~]$ /data/zookeeper-3.4.11/bin/zkServer.sh start [app@sign-zk02 ~]$ /data/zookeeper-3.4.11/bin/zkServer.sh start [app@sign-zk03 ~]$ /data/zookeeper-3.4.11/bin/zkServer.sh start [app@sign-zk01 ~]$ lsof -i:2181 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME java 7071 app 20u IPv6 30989 0t0 TCP *:eforward (LISTEN) 查看zookeeper集群状态 [app@sign-zk01 ~]$ /data/zookeeper-3.4.11/bin/zkServer.sh status ZooKeeper JMX enabled by default Using config: /data/zookeeper-3.4.11/bin/../conf/zoo.cfg Mode: follower [app@sign-zk02 ~]$ /data/zookeeper-3.4.11/bin/zkServer.sh status ZooKeeper JMX enabled by default Using config: /data/zookeeper-3.4.11/bin/../conf/zoo.cfg Mode: leader [app@sign-zk03 ~]$ /data/zookeeper-3.4.11/bin/zkServer.sh status ZooKeeper JMX enabled by default Using config: /data/zookeeper-3.4.11/bin/../conf/zoo.cfg Mode: follower 从上面可以看出,sign-zk01.wangshibo.cn,sign-zk03.wangshibo.cn两台服务器zookeeper的状态是follow模式, sign-zk02.wangshibo.cn这台服务器zookeeper的状态是leader模式。 Zookeeper集群连接 Zookeeper集群搭建完毕之后,可以通过客户端脚本连接到zookeeper集群上面,对客户端来说,zookeeper集群是一个整体,连接到zookeeper集群实际上感觉在独享整个集群的服务。 [app@sign-zk01 ~]$ /data/zookeeper-3.4.11/bin/zkCli.sh -server sign-zk02.wangshibo.cn:2181 Connecting to sign-zk02.wangshibo.cn:2181 2017-11-22 15:22:55,110 [myid:] - INFO [main:Environment@100] - Client environment:zookeeper.version=3.4.11-37e277162d567b55a07d1755f0b31c32e93c01a0, built on 11/01/2017 18:06 GMT 2017-11-22 15:22:55,113 [myid:] - INFO [main:Environment@100] - Client environment:host.name=sign-zk01.wangshibo.cn 2017-11-22 15:22:55,113 [myid:] - INFO [main:Environment@100] - Client environment:java.version=1.7.0_79 2017-11-22 15:22:55,114 [myid:] - INFO [main:Environment@100] - Client environment:java.vendor=Oracle Corporation 2017-11-22 15:22:55,114 [myid:] - INFO [main:Environment@100] - Client environment:java.home=/usr/java/jdk1.7.0_79/jre 2017-11-22 15:22:55,114 [myid:] - INFO [main:Environment@100] - Client environment:java.class.path=/data/zookeeper-3.4.11/bin/../build/classes:/data/zookeeper-3.4.11/bin/../build/lib/*.jar:/data/zookeeper-3.4.11/bin/../lib/slf4j-log4j12-1.6.1.jar:/data/zookeeper-3.4.11/bin/../lib/slf4j-api-1.6.1.jar:/data/zookeeper-3.4.11/bin/../lib/netty-3.10.5.Final.jar:/data/zookeeper-3.4.11/bin/../lib/log4j-1.2.16.jar:/data/zookeeper-3.4.11/bin/../lib/jline-0.9.94.jar:/data/zookeeper-3.4.11/bin/../lib/audience-annotations-0.5.0.jar:/data/zookeeper-3.4.11/bin/../zookeeper-3.4.11.jar:/data/zookeeper-3.4.11/bin/../src/java/lib/*.jar:/data/zookeeper-3.4.11/bin/../conf: 2017-11-22 15:22:55,115 [myid:] - INFO [main:Environment@100] - Client environment:java.library.path=/usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib 2017-11-22 15:22:55,115 [myid:] - INFO [main:Environment@100] - Client environment:java.io.tmpdir=/tmp 2017-11-22 15:22:55,115 [myid:] - INFO [main:Environment@100] - Client environment:java.compiler=<NA> 2017-11-22 15:22:55,115 [myid:] - INFO [main:Environment@100] - Client environment:os.name=Linux 2017-11-22 15:22:55,115 [myid:] - INFO [main:Environment@100] - Client environment:os.arch=amd64 2017-11-22 15:22:55,115 [myid:] - INFO [main:Environment@100] - Client environment:os.version=2.6.32-696.13.2.el6.x86_64 2017-11-22 15:22:55,115 [myid:] - INFO [main:Environment@100] - Client environment:user.name=app 2017-11-22 15:22:55,115 [myid:] - INFO [main:Environment@100] - Client environment:user.home=/home/app 2017-11-22 15:22:55,115 [myid:] - INFO [main:Environment@100] - Client environment:user.dir=/home/app 2017-11-22 15:22:55,116 [myid:] - INFO [main:ZooKeeper@441] - Initiating client connection, connectString=sign-zk02.wangshibo.cn:2181 sessionTimeout=30000 watcher=org.apache.zookeeper.ZooKeeperMain$MyWatcher@594b7042 Welcome to ZooKeeper! 2017-11-22 15:22:55,136 [myid:] - INFO [main-SendThread(sign-zk02.wangshibo.cn:2181):ClientCnxn$SendThread@1035] - Opening socket connection to server sign-zk02.wangshibo.cn/172.16.51.199:2181. Will not attempt to authenticate using SASL (unknown error) 2017-11-22 15:22:55,140 [myid:] - INFO [main-SendThread(sign-zk02.wangshibo.cn:2181):ClientCnxn$SendThread@877] - Socket connection established to sign-zk02.wangshibo.cn/172.16.51.199:2181, initiating session JLine support is enabled [zk: sign-zk02.wangshibo.cn:2181(CONNECTING) 0] 2017-11-22 15:22:55,287 [myid:] - INFO [main-SendThread(sign-zk02.wangshibo.cn:2181):ClientCnxn$SendThread@1302] - Session establishment complete on server sign-zk02.wangshibo.cn/172.16.51.199:2181, sessionid = 0x2000495ce4f0000, negotiated timeout = 30000 WATCHER:: WatchedEvent state:SyncConnected type:None path:null //这一步按Enter [zk: sign-zk02.wangshibo.cn:2181(CONNECTED) 0] ls / [zookeeper] [zk: sign-zk02.wangshibo.cn:2181(CONNECTED) 1] quit Quitting... 2017-11-22 15:23:39,255 [myid:] - INFO [main:ZooKeeper@687] - Session: 0x2000495ce4f0000 closed 2017-11-22 15:23:39,257 [myid:] - INFO [main-EventThread:ClientCnxn$EventThread@520] - EventThread shut down for session: 0x2000495ce4f0000 通过上图可以看出整个zookeeper集群已经搭建并测试完成。
问题记录
客户端连接zookeeper,发生重连,拒绝连接,如下截图(172.16.50.86/87/88是zk集群的三个节点):
官网解释:
解决办法:
修改三个节点的zookeeper里conf的zoo.cfg.dynamic.*文件 [app@uatsign-zk01 ~]$ cat /data/zookeeper-3.5.3-beta/conf/zoo.cfg.dynamic.800000000 server.1=172.16.50.86:2888:3888:participant; server.2=172.16.50.87:2888:3888:participant; server.3=172.16.50.88:2888:3888:participant; 修改为 [app@uatsign-zk01 ~]$ cat /data/zookeeper-3.5.3-beta/conf/zoo.cfg.dynamic.800000000 server.1=172.16.50.86:2888:3888:participant;172.16.50.86:2181 server.2=172.16.50.87:2888:3888:participant;172.16.50.87:2181 server.3=172.16.50.88:2888:3888:participant;172.16.50.88:2181 然后重启每个节点的zookeeper服务即可 注意: 上面修改中的172.16.50.86/87/88:2181不能配置成0.0.0.0:2181,得加具体的客户端ip(即具体的每个zk节点的ip),如果加全0,重连的时候就会用全0的ip,可能会出错。
Zookeeper日志清理机制 (清理zookeeper的历史数据)
问题记录:
在服务器上部署了zookeeper集群(三个节点), 在一次断电重启后发现, zookeeper服务在启动后不就就自动关闭.
后面发现是集群中的三个zk节点的数据盘使用率100%导致的! 发现是zoo.cfg文件里配置的dataDir目录 (即/data/zookeeper-3.5.3-beta/data) 占用了数据盘很大空间!
解决办法: 清理Zookeeper的历史数据
1) 使用zookeeper自带的清理脚本zkCleanup.sh, 手动清理zookeeper历史数据
[app@uatsign-zk03 ~]$ /data/zookeeper-3.5.3-beta/bin/zkCleanup.sh -n 5 ....................... Removing file: Jul 17, 2018 3:07:46 PM /data/zookeeper-3.5.3-beta/data/version-2/snapshot.b0022de4e 2019-04-01 15:45:22,943 [myid:] - INFO [main:PurgeTxnLog@154] - Removing file: Oct 7, 2018 7:20:30 AM /data/zookeeper-3.5.3-beta/data/version-2/snapshot.c0022e577 Removing file: Oct 7, 2018 7:20:30 AM /data/zookeeper-3.5.3-beta/data/version-2/snapshot.c0022e577 2019-04-01 15:45:22,944 [myid:] - INFO [main:PurgeTxnLog@154] - Removing file: Mar 13, 2019 11:55:47 AM /data/zookeeper-3.5.3-beta/data/version-2/snapshot.23000cc8ff Removing file: Mar 13, 2019 11:55:47 AM /data/zookeeper-3.5.3-beta/data/version-2/snapshot.23000cc8ff 2019-04-01 15:45:22,948 [myid:] - INFO [main:PurgeTxnLog@154] - Removing file: Mar 12, 2019 6:50:44 PM /data/zookeeper-3.5.3-beta/data/version-2/snapshot.2300056f0c Removing file: Mar 12, 2019 6:50:44 PM /data/zookeeper-3.5.3-beta/data/version-2/snapshot.2300056f0c 2019-04-01 15:45:22,951 [myid:] - INFO [main:PurgeTxnLog@154] - Removing file: Nov 24, 2017 9:41:31 AM /data/zookeeper-3.5.3-beta/data/version-2/snapshot.0 ........................... [app@uatsign-zk03 ~]$ ll /data/zookeeper-3.5.3-beta/data/version-2/ total 2914360 -rw-rw-r-- 1 app app 2 Apr 1 15:46 acceptedEpoch -rw-rw-r-- 1 app app 2 Apr 1 15:46 currentEpoch -rw-rw-r-- 1 app app 334514719 Apr 1 11:45 snapshot.2c00013653 -rw-rw-r-- 1 app app 94838784 Apr 1 11:55 snapshot.2c00022aea -rw-rw-r-- 1 app app 186249216 Apr 1 14:36 snapshot.2d0001116d -rw-r--r-- 1 root root 338225939 Apr 1 15:31 snapshot.2e00001294 -rw-r--r-- 1 root root 338224028 Apr 1 15:41 snapshot.2e000148d5
其中: -n 5 表示清理后保留5份最新的历史数据
手动清理zookeeper历史数据的命令总结
# /data/zookeeper-3.5.3-beta/bin/zkCleanup.sh -n 5 之前版本, 需要在清理命令中添加zookeeper的数据目录 # /data/zookeeper-3.5.3-beta/bin/zkCleanup.sh /data/zookeeper-3.5.3-beta/data -n 5
2) 设置zookeeper历史数据的自动清理
修改zoo.cfg配置文件中的 autopurge.snapRetainCount 和 autopurge.purgeInterval 两个参数实现定时清理
依次修改zookeeper集群节点的zoo.cfg文件, 添加下面两行内容: [app@uatsign-zk03 ~]$ cat /data/zookeeper-3.5.3-beta/conf/zoo.cfg ............. autopurge.snapRetainCount=10 autopurge.purgeInterval=1 然后依次重启集群节点的zookeeper服务即可 [app@uatsign-zk03 ~]$ ps -ef|grep zookeeper|awk '{print $2}'|xargs kill -9 [app@uatsign-zk03 ~]$ /data/zookeeper-3.5.3-beta/bin/zkServer.sh start
autopurge.purgeInterval: 这个参数指定了清理频率,单位是小时,需要填写一个1或更大的整数,默认是0,表示不开启自动清理功能。
autopurge.snapRetainCount: 这个参数和上面的参数搭配使用,这个参数指定了需要保留的快照文件数目,默认是保留3个。