zkwatch数量与什么有关
zkWatcher的数量与Zookeeper集群中注册在一个节点上的客户端的数目有关。因为zkWatcher指的是客户端和ZooKeeper服务器之间的通信,当客户端在ZooKeeper的节点上注册watcher时会与ZooKeeper服务器保持连接,从而导致zkWatcher的数量增加。如果ZooKeeper集群中注册的客户端数量很多,那么zkWatcher的数量也会相应地增加。这可能会导致ZooKeeper服务器的性能问题,因为服务器必须同时处理所有客户端的请求,包括watcher的事件通知。因此,需要合理控制客户端的请求量和watcher的数量,以避免ZooKeeper服务器性能的问题。
Tomcat启动,初始化webcontext;
初始化spring, spring初始某些些bean,这些bean包括了zookeeper的连接相关的bean;
这时zkClient(独立线程)已经连接上服务器了,但是classloader没有加载到org/apache/zookeeper/proto/SetWatches类;
spring初始化失败,导致Tomcat webcontext初始化也失败,应用在挂起状态,但zkClient线程还是正常的;
zookeeper服务器重启,zkClient开始重连,连接上zookeeper服务器;
zkClient触发watch的一些代码,ClassLoader尝试加载org/apache/zookeeper/proto/SetWatches类,但是发现找不到类,于是抛出异常;
zkClient捕获到异常,认为重连失败,close掉connection,休眠几秒之后,再次重连;
于是出现了zkClient反复重试连接zookeeper服务器,而且都是秒连秒断的情况。转载,仅供参考。
(1)解压为zookeepertar -xf -C /home/myuser/zookeeper/
复制zookeeper文件夹3份,分别重名名为zookeeperA,zookeeperB,zookeeperC。 并且创建数据快照以及日志存放文件夹,命名为zooA,zooB,zooC。 (2)编辑对应的zookeeper配置文件,复制zookeeperconf下zoo_samplecfg为zoocfgcd /home/myuser/zookeeperA/conf
cp zoo_samplecfg zoocfg
(3)修改zoocfg# 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=/home/myuser/zooA/data
# the port at which the clients will connect
clientPort=2181
# ZooKeeper server and its port no # ZooKeeper ensemble should know about every other machine in the ensemble # specify server id by creating 'myid' file in the dataDir # use hostname instead of IP address for convenient maintenance
server1=127001:2888:3888
server2=127001:2988:3988
server3=127001:2088:3088
#
# Be sure to read the maintenance section of the
# administrator guide before turning>tickTime:心跳时间,为了确保连接存在的,以毫秒为单位,最小超时时间为两个心跳时间
initLimit:多少个心跳时间内,允许其他server连接并初始化数据,如果ZooKeeper管理的数据较大,则应相应增大这个值
clientPort:服务的监听端口
dataDir:用于存放内存数据库快照的文件夹,同时用于集群的myid文件也存在这个文件夹里(注意:一个配置文件只能包含一个dataDir字样,即使它被注释掉了。)
dataLogDir:用于单独设置transaction log的目录,transaction log分离可以避免和普通log还有快照的竞争
syncLimit:多少个tickTime内,允许follower同步,如果follower落后太多,则会被丢弃。
(4)创建myid文件
cd /home/myuser/zooA/data
sudo sh -c 'echo "1" >> myid'
其他文件夹类似创建myid文件,zookeeperB为2,zookeeperC为3
(5)启动zookeeper
cd /home/myuser/zookeeperA/bin
sudo sh zkServersh start
查看zookeeper状态[root@weibo bin]# sh zkServersh status
JMX enabled by default
Using config: /home/weibo/zookeeperA/bin//conf/zoocfg
Mode: follower
启动OK,依次启动另外两台zookeeper,启动第一台zookeeper后,你可以观察bin下的zookeeperout可以看到报错,connection
refused,没有关系,zookeeper需要等待其他另个节点的加入,全部启动之后就正常了。
(6)客户端连接zookeeper
[root@weibo bin]# sh zkClish
Connecting to localhost:2181
2013-05-10 15:00:25,363 [myid:] - INFO [main:Environment@100] - Client environment:zookeeperversion=345-1392090, built>configs:保存上传的配置文件信息
clusterstatejson:集群状态json
aliases:别名json
live_node:当solr服务器启动的时候,会注册到这里
overseer:保存shard信息
overseer_elect:节点选举
collections:所有的collection
Zab协议全称是 Zookeeper Atomic BroadCast (Zookeeper 原子广播),Zookeeper是通过Zab协议来保证分布式事务的一致性。
1Zab协议是zookeeper专门设计的一种 支持崩溃恢复 的 原子广播协议, 是Zookeeper保证数据一致性的核心算法。
2在Zookeeper当中依赖Zab协议来保证数据的一致性,基于这个协议,zookeeper实现了一种主备模型,(Leader+Follower)的架构在保证集群中各个副本之间数据的一致性。 Leader负责处理写事务请求,然后Leader将数据同步到Follower节点上。
3zookeeper客户端会随机连接到集群中的一个节点上,如果是读请求,就会从当前节点进行读取数据,如果是写的请求,就会将事务请求提交到Leader节点,leader节点接收到事务提交,就会广播该事务,如果超过一半节点写入成功,那么该事务就会被提交。
1Zab协议需要确保那些已经在leader服务器上提交的事务最终被所有服务器提交。
2Zab协议需要确保那些在leader服务器上被提出而没有被提交的事务。
1使用主进程(leader)来接受客户端并处理客户端的事务请求,并采用Zab的原子广播协议,将服务器数据变更的状态以事务提议的形式广播到所有的follower副本上去。
2当主进程出现异常,整个zk集群依然能够正常运行。
Zab协议每个leader需要经过三个阶段:发现、同步、广播
发现 :要求Zookeeper集群必须选取出一个leader进程,同时leader需要维护一个follower可用客户端列表,将来客户端可以和这些follower进行通信。
同步 :Leader要将本身的数据与follower进行同步,实现多副本存储,也体现了CAP中的高可用和分区容错。follower将队列中未处理完的消息消费完成后,写入到本地日志中。
广播 :leader接受客户端提出的事务请求,将新的事务请求广播给follower节点。
Zab协议核心:定义了事务请求的处理方式。
1所有的事务请求必须由一个全局唯一的服务器来协调处理(Leader 服务器),其余的服务器是follower服务器。
2leader服务器负责将客户端提出的事务请求,转换成一个事务proposal,并将事务proposal分发给集群中follower服务器,也就是向所有follower节点发送数据广播请求。
3分发之后leader服务器需要等待follower服务器的反馈,在Zab协议中,只要超过半数的follower服务器进行确认了,那么leader就会再次向所有的follower发送commit消息,要求将上一个事务进行提交。
Zab协议包括两种模式: 崩溃恢复 和 消息广播
协议过程
当整个集群启动过程中,或者leader服务器出现宕机或者网络终端等异常时,Zab协议就会进入崩溃恢复模式,选举出新的leader。
当选举出新的leader之后,同时集群中有过半的服务器与该leader服务器完成了状态同步(数据同步),Zab协议就会退出崩溃恢复模型,进入消息广播模式。
如果新增一台服务器加入集群中,当前集群中已经选举出leader,那么加入进来的服务器自动进入恢复模式,找到leader服务器进行状态同步,完成同步后,与其他follower一起参与到广播流程中。
Zookeeper集群中,数据副本的传递策略采用的是消息广播模式。Zab协议中Leader等待follower 的ACK反馈消息,当到达半数以上follower成功反馈即可,不需要等所有的follower全部反馈。
leader服务器出现宕机和网络原因等导致leader与过半的follower服务器不能联系,就会自动进入崩溃恢复模式。
在Zab协议中,为了保证程序的正常运行,整个恢复过程结束后需要重新选出一个leader服务器,因此Zab协议需要一个高效且可靠的算法,来保证快速选举出leader。
Leader算法不仅让leader自己知道自己已经被选取为leader,还需要让集群中的所有服务器快速的感知到选举出的新leader服务器。
崩溃恢复包括两个部分: Leader选举 和 数据恢复
Zab 协议如何保证数据一致性
假设两种异常情况:
1、一个事务在 Leader 上提交了,并且过半的 Folower 都响应 Ack 了,但是 Leader 在 Commit 消息发出之前挂了。
2、假设一个事务在 Leader 提出之后,Leader 挂了。
要确保如果发生上述两种情况,数据还能保持一致性,那么 Zab 协议选举算法必须满足以下要求:
Zab 协议崩溃恢复要求满足以下两个要求 :
1) 确保已经被 Leader 提交的 Proposal 必须最终被所有的 Follower 服务器提交 。
2) 确保丢弃已经被 Leader 提出的但是没有被提交的 Proposal 。
根据上述要求
Zab协议需要保证选举出来的Leader需要满足以下条件:
1) 新选举出来的 Leader 不能包含未提交的 Proposal 。
即新选举的 Leader 必须都是已经提交了 Proposal 的 Follower 服务器节点。
2) 新选举的 Leader 节点中含有最大的 zxid 。
这样做的好处是可以避免 Leader 服务器检查 Proposal 的提交和丢弃工作。
Zab 如何数据同步
1)完成 Leader 选举后(新的 Leader 具有最高的zxid),在正式开始工作之前(接收事务请求,然后提出新的 Proposal),Leader 服务器会首先确认事务日志中的所有的 Proposal 是否已经被集群中过半的服务器 Commit。
2)Leader 服务器需要确保所有的 Follower 服务器能够接收到每一条事务的 Proposal ,并且能将所有已经提交的事务 Proposal 应用到内存数据中。等到 Follower 将所有尚未同步的事务 Proposal 都从 Leader 服务器上同步过啦并且应用到内存数据中以后,Leader 才会把该 Follower 加入到真正可用的 Follower 列表中。
Zab 数据同步过程中,如何处理需要丢弃的 Proposal
在 Zab 的事务编号 zxid 设计中,zxid是一个64位的数字。
其中低32位可以看成一个简单的单增计数器,针对客户端每一个事务请求,Leader 在产生新的 Proposal 事务时,都会对该计数器加1。而高32位则代表了 Leader 周期的 epoch 编号。
epoch 编号可以理解为当前集群所处的年代,或者周期。每次Leader变更之后都会在 epoch 的基础上加1,这样旧的 Leader 崩溃恢复之后,其他Follower 也不会听它的了,因为 Follower 只服从epoch最高的 Leader 命令。
每当选举产生一个新的 Leader ,就会从这个 Leader 服务器上取出本地事务日志充最大编号 Proposal 的 zxid,并从 zxid 中解析得到对应的 epoch 编号,然后再对其加1,之后该编号就作为新的 epoch 值,并将低32位数字归零,由0开始重新生成zxid。
Zab 协议通过 epoch 编号来区分 Leader 变化周期 ,能够有效避免不同的 Leader 错误的使用了相同的 zxid 编号提出了不一样的 Proposal 的异常情况。
基于以上策略
当一个包含了上一个 Leader 周期中尚未提交过的事务 Proposal 的服务器启动时,当这台机器加入集群中,以 Follower 角色连上 Leader 服务器后,Leader 服务器会根据自己服务器上最后提交的 Proposal 来和 Follower 服务器的 Proposal 进行比对,比对的结果肯定是 Leader 要求 Follower 进行一个回退操作,回退到一个确实已经被集群中过半机器 Commit 的最新 Proposal 。
本文根据https://wwwjianshucom/p/2bceacd60b8a进行编写。
(1)配置管理
集中式的配置管理在应用集群中是非常常见的,一般商业公司内部都会实现一套集中的配置管理中心,应对不同的应用集群对于共享各自配置的需求,并且在配置变更时能够通知到集群中的每一个机器。
Zookeeper很容易实现这种集中式的配置管理,比如将APP1的所有配置配置到/APP1 znode下,APP1所有机器一启动就对/APP1这个节点进行监控(zkexist("/APP1",true)),并且实现回调方法Watcher,那么在zookeeper上/APP1 znode节点下数据发生变化的时候,每个机器都会收到通知,Watcher方法将会被执行,那么应用再取下数据即可(zkgetData("/APP1",false,null));
以上这个例子只是简单的粗颗粒度配置监控,细颗粒度的数据可以进行分层级监控,这一切都是可以设计和控制的。
(2)集群管理
应用集群中,我们常常需要让每一个机器知道集群中(或依赖的其他某一个集群)哪些机器是活着的,并且在集群机器因为宕机,网络断链等原因能够不在人工介入的情况下迅速通知到每一个机器。
Zookeeper同样很容易实现这个功能,比如我在zookeeper服务器端有一个znode叫/APP1SERVERS,那么集群中每一个机器启动的时候都去这个节点下创建一个EPHEMERAL类型的节点,比如server1创建/APP1SERVERS/SERVER1(可以使用ip,保证不重复),server2创建/APP1SERVERS/SERVER2,然后SERVER1和SERVER2都watch /APP1SERVERS这个父节点,那么也就是这个父节点下数据或者子节点变化都会通知对该节点进行watch的客户端。因为EPHEMERAL类型节点有一个很重要的特性,就是客户端和服务器端连接断掉或者session过期就会使节点消失,那么在某一个机器挂掉或者断链的时候,其对应的节点就会消失,然后集群中所有对/APP1SERVERS进行watch的客户端都会收到通知,然后取得最新列表即可。
另外有一个应用场景就是集群选master,一旦master挂掉能够马上能从slave中选出一个master,实现步骤和前者一样,只是机器在启动的时候在APP1SERVERS创建的节点类型变为EPHEMERAL_SEQUENTIAL类型,这样每个节点会自动被编号,例如 zkcreate("/testRootPath/testChildPath1","1"getBytes(), IdsOPEN_ACL_UNSAFE,CreateModeEPHEMERAL_SEQUENTIAL);
zkcreate("/testRootPath/testChildPath2","2"getBytes(), IdsOPEN_ACL_UNSAFE,CreateModeEPHEMERAL_SEQUENTIAL);
zkcreate("/testRootPath/testChildPath3","3"getBytes(), IdsOPEN_ACL_UNSAFE,CreateModeEPHEMERAL_SEQUENTIAL);
// 创建一个子目录节点
zkcreate("/testRootPath/testChildPath4","4"getBytes(), IdsOPEN_ACL_UNSAFE,CreateModeEPHEMERAL_SEQUENTIAL);
Systemoutprintln(zkgetChildren("/testRootPath", false));
打印结果:[testChildPath10000000000, testChildPath20000000001, testChildPath40000000003, testChildPath30000000002]
zkcreate("/testRootPath", "testRootData"getBytes(),IdsOPEN_ACL_UNSAFE, CreateModePERSISTENT);
// 创建一个子目录节点
zkcreate("/testRootPath/testChildPath1","1"getBytes(), IdsOPEN_ACL_UNSAFE,CreateModeEPHEMERAL);
zkcreate("/testRootPath/testChildPath2","2"getBytes(), IdsOPEN_ACL_UNSAFE,CreateModeEPHEMERAL);
zkcreate("/testRootPath/testChildPath3","3"getBytes(), IdsOPEN_ACL_UNSAFE,CreateModeEPHEMERAL);
// 创建一个子目录节点
zkcreate("/testRootPath/testChildPath4","4"getBytes(), IdsOPEN_ACL_UNSAFE,CreateModeEPHEMERAL);
Systemoutprintln(zkgetChildren("/testRootPath", false));
打印结果:[testChildPath2, testChildPath1, testChildPath4, testChildPath3]
我们默认规定编号最小的为master,所以当我们对/APP1SERVERS节点做监控的时候,得到服务器列表,只要所有集群机器逻辑认为最小编号节点为master,那么master就被选出,而这个master宕机的时候,相应的znode会消失,然后新的服务器列表就被推送到客户端,然后每个节点逻辑认为最小编号节点为master,这样就做到动态master选举。
0条评论