Zookeeper是Google的Chubby一个开源的实现是高有效和可靠的协同工作系统Zookeeper能够用来leader选举,配置信息维护等在一个分布式的环境中,我们需要一个Master实例或存储一些配置信息,确保文件写入的一致性等[1]
ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,包含一个简单的原语集,是Hadoop和Hbase的重要组件。
说白了是hadoop的组件之一,用来管理hadoop。首先需要安装JdK,从Oracle的Java网站下载,安装很简单,就不再详述。
单机模式
单机安装非常简单,只要获取到 Zookeeper 的压缩包并解压到某个目录如:C:\zookeeper-345\下,Zookeeper 的启动脚本在 bin 目录下,Windows 下的启动脚本是 zkServercmd。
在你执行启动脚本之前,还有几个基本的配置项需要配置一下,Zookeeper 的配置文件在 conf 目录下,这个目录下有 zoo_samplecfg 和 log4jproperties,你需要做的就是将 zoo_samplecfg 改名为 zoocfg,因为 Zookeeper 在启动时会找这个文件作为默认配置文件。下面详细介绍一下,这个配置文件中各个配置项的意义。
# 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=C:\\zookeeper-345\\data
dataLogDir=C:\\zookeeper-345\\log
# the port at which the clients will connect
clientPort=2181
#
# Be sure to read the maintenance section of the
# administrator guide before turning on autopurge
#
# >Apache Zookeeper是我最近遇到的最酷的技术,我是在研究Solr Cloud功能的时候发现的。Solr的分布式计算让我印象深刻。你只要开启一个新的实例就能自动在Solr Cloud中找到。它会将自己分派到某个分片中,并确定出自己是一个Leader(源)还是一个副本。不一会儿,你就可以在你的那些服务器上查询到了。即便某些服务器宕机了也可以继续工作。非常动态、聪明、酷。
将运行多个应用程序作为一个逻辑程序并不是什么新玩意。事实上,我在几年前就已写过类似的软件。这种架构比较让人迷惑,使用起来也费劲。为此Apache Zookeeper提供了一套工具用于管理这种软件。
为什么叫Zoo?“因为要协调的分布式系统是一个动物园”。
在本篇文章中,我将说明如何使用PHP安装和集成Apache ZooKeeper。我们将通过service来协调各个独立的PHP脚本,并让它们同意某个成为Leader(所以称作Leader选举)。当Leader退出(或崩溃)时,worker可检测到并再选出新的leader。
ZooKeeper是一个中性化的Service,用于管理配置信息、命名、提供分布式同步,还能组合Service。所有这些种类的Service都会在分布式应用程序中使用到。每次编写这些Service都会涉及大量的修bug和竞争情况。正因为这种编写这些Service有一定难度,所以通常都会忽视它们,这就使得在应用程序有变化时变得难以管理应用程序。即使处理得当,实现这些服务的不同方法也会使得部署应用程序变得难以管理。
虽然ZooKeeper是一个Java应用程序,但C也可以使用。这里就有个PHP的扩展,由Andrei Zmievski在2009创建并维护。你可以从PECL中下载,或从GitHub中直接获取PHP-ZooKeeper。
要使用该扩展你首先要安装ZooKeeper。可以从官方网站下载。
$ tar zxfv zookeeper-345targz
$ cd zookeeper-345/src/c
$ /configure --prefix=/usr/
$ make
$ sudo make install
这样就会安装ZooKeeper的库和头文件。现在准备编译PHP扩展。
$ cd$ git clone >在之前的Zookeeper系列基本介绍里有提到 ZK 的角色,那篇文章只是简单介绍 Leader 、 Follower 和 Observer 这三种角色。那么在一个 ZK 集群中,我怎么知道 ZK 服务是哪一个角色呢?角色是怎么分配的?为什么某个 ZK 是 Follower,而不是 Leader ?结论先行,先简单回答上面的问题。
ZXID:当发生写请求的时候,在 ZK 内会发起一个事务,每个事务会分配一个 zxid 作为标识符。zxid 是64位的 long 类型,高32位代表时间戳,低32位可以认为是事务ID,用来标识服务器状态的变更次数
SID:服务器ID。用来标识一台在 ZooKeeper 集群中的机器,每台机器不能重复。注意:这里和启动时指定的myid一致
Epoch:每个 Leader 任期的代号。每次新选举一个 Leader 就会递增1
Observer:不参与投票和选举换句话说,不参与 Leader 的选举,也不会投票给某个节点
节点状态:
好了,前置知识大概这么多,接下来咱们看一下,上面这些东西和选举有什么关系。
选举目的是为了从一堆 ZK 的服务节点中找一个大佬(Leader),然后所有非 Observer 节点都会成为小弟(Follower)。Leader负责读写数据,而 Follower 会分担大佬的负担,分担部分的读请求。
那么问题来了,根据什么条件判断,认为某一台 ZK 服务能成为 Leader 呢?
实际上,选举的规则是按照 Epoch > 事务Id > SID 依次由大到小排序,值最大作为 Leader。
简单来说,选举的时候会比较 epoch 的大小,如果所有 ZK 节点的 Leader 任期大小一样,则继续比较 ZXID 的低32位,也就是事务Id大小,如果事务ID大小都一样的话,就会比较服务器ID(SID)
ps 由于我是基于 Docker 部署 ZK 集群,进入其中的ZK节点,执行 cat /data/version-2/currentEpoch 既可以看到epoch的大小
接下来,看下 ZK 集群是怎样选举 Leader 的,但值得注意的是,选举区分了第一次启动和非第一次启动。第一次选举流程比较简单,但非第一次选举的时候,会涉及到 Leader 宕机或假死的情况,这样的话细节就会多一些。
背景:ZK集群是由5台ZK节点组成,只要集群中过半的节点投票就可选出 Leader
在集群节点启动之初,所有的epoch都是一样的,此时 Leader 还没选举出来,此时不会有写请求进来,所以事务Id也是没有的,所以最终的判断条件是根据SID,也就是节点的 ZOO_MY_ID 决定,理论上来说,ZOO_MY_ID 值最大的就会成为Leader,但为什么说是理论上呢?咱们来看下实际的效果。
基于docker-compose一次性部署5台ZK节点,参考我另外一篇博客 Zookeeper系列基于docker-compose快速搭建Zookeeper集群
最终结果是例子中的zoo5成为Leader,符合上面 ZOO_MY_ID 值最大的就会成为 Leader 的说法。
先把5台节点都关掉执行docker-compose stop,模拟集群的节点准备启动的状态,然后依次执行
集群中有5台节点,先启动3台,符合过半投票的情况,这时候已经可以选举出 Leader,盲猜一下,应该是 zoo3 这台机器成为Leader 了吧?进入 zoo3 容器,执行 zkServersh status 查看节点角色。果然,此时的 zoo3 是 Leader
此时,再依次执行 docker start zoo4 和 docker start zoo5,可以发现,Leader依旧是 zoo3。这是因为,当成功选举出 Leader 后,后续启动的 zk 节点都会成为 Follower(现在先不讨论Observer的情况)。
但这种结果并不与选举规则冲突,当 zk 集群内的机器不是同一时刻启动的时候,其大致选举流程是:
但为什么在场景1的时候,不是 zoo3 成为 Leader 而是 zoo5 成为 Leader 呢?这是因为在近乎同一时刻,zk 集群所有的服务都启动了,此时所有节点都是先投票给自己,然后再与其他节点交换信息,发现 zoo5 的 Sid 最大,接着所有的节点都投票给了 zoo5 ,其余节点就都处于 FOLLOWING 状态,而 zoo5 是 LEADING 状态。
假设 zoo3 是 Leader,其余都是 Follower。当 zoo3 宕机,其余节点都变为 FOLLOWING 状态,重新参与选举。
为降低复杂度,将真实的 zxid 简化为只有事务Id。假设 zoo1、zoo2、zoo4、zoo5 的 (epoch,zxid,sid) 分别是 (1,13,1),(1,10,2),(1,13,4),(1,12,5)
此时选举流程是怎样呢?
zoo3 为 LEADING 状态,此时 zoo3 假死。如果 zoo3 忽然网络出问题,断开与其他 follower 节点的连接。其他节点以为 zoo3 宕机,则重新选举新 Leader,假设此时 zoo1 成为大佬,此时 zoo3 忽然正常了,那么剩下的 follower 节点会不会蒙圈?怎么会有两个大佬,正所谓一山不能容二虎,我应该听谁?
实际上,zk 有考虑到这种情况,还记得上面说的 epoch 的定义吗?这是每个 Leader 的代号,每更新一次 Leader,epoch 就会递增1。假如 zoo3 假死前,所有的节点的 epoch 都是2。zoo3 假死,zoo3 的 epoch 不会变,因为没想到有其他节点想替代自己的位置嘛。但其他节点选举 zoo1 为 Leader 后,除了 zoo3 ,其他的节点的 epoch 都是3了。此时 zoo3 恢复正常,即使向其他节点同步事务消息,但其余节点发现 zoo3 的 epoch 和自己不一样,就不会认这个大佬,而是认准 zoo1 才是自己的老大。
实际上,zk 集群的选举并不简单,底层选举算法使用到的 ZAB 协议保证分布式消息一致性,本篇文章并没过多描述。对于初学者来说,了解其选举的规则和某些场景下是如何选举,大概了解流程足矣。当实际开发中,为了保证高可用,需要注意的是 ZK 集群节点为奇数。另外,感兴趣的读者可以再去关注 ZAB 协议的细节。
参考资料:
《从Paxos到Zookeeper分布式一致性原理与实践》
如果觉得文章不错的话,麻烦点个赞哈,你的鼓励就是我的动力!对于文章有哪里不清楚或者有误的地方,欢迎在评论区留言~
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)