一、什么是 ZooKeeper
ZooKeeper 到底是什么,为什么它在分布式系统里有着如此无可替代的地位?
其实学任何一项技术,首先都要弄明白,为什么需要这项技术。
例如为了解决大数据存储问题,因此有了 HDFS;为了解决大数据计算问题,因此有了 MapReduce;为了简化 MapReduce 编程,因此有了 Hive。
那 Zookeeper 的作用是什么呢?官方文档上这么解释:它是一个分布式服务框架,是 Apache Hadoop 的一个子项目,它主要是用来解决分布式应用中经常遇到的一些数据管理问题,如:统一命名服务、状态同步服务、集群管理、分布式应用配置项的管理等。
在之前的文章中,我们搭建了 Hadoop 集群,里面有一个 Master,多个 Slave Worker,我们需要一个系统,来告诉客户端,哪个是 Master,哪个是 Worker,如果掉线如何处理等。因此 ZooKeeper 隆重登场。
那它们是如何实现的呢,我相信这才是大家关心的东西。
ZooKeeper 在实现这些服务时,首先它设计一种新的数据结构——Znode,然后在该数据结构的基础上定义了一些关于该结构的操作。有了这些还不够,因为 ZooKeeper 是工作在一个分布式的环境下,我们的服务是通过消息以网络的形式发送给我们的分布式应用程序,所以还需要一个通知机制——Watcher 监听机制。
简单来说,虽然不准确,Zookeeper = 文件系统 + 监听通知机制。
二、Zookeeper 安装与使用
1、在 Apache ZooKeeper 下载并解压。
$ tar -xzvf apache-zookeeper-x.y.z-bin.tar.gz
2、复制 zookeeper 中 conf 文件夹下的 zoo_sample.cfg
文件并命名为 zoo.cfg
。
$ cp zoo_sample.cfg zoo.cfg
3、修改 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=/Users/terry-jri/Downloads/soft_tmp/zookeeper
# the port at which the clients will connect
clientPort=2181
4、配置环境变量
$ vim /etc/profile
加入 zookeeper 环境变量
export ZOOKEEPER_HOME=/Users/terry-jri/Downloads/soft/zookeeper
export PATH=$PATH:$ZOOKEEPER_HOME/bin
5、启动 zk 服务器
$ zkServer.sh start
6、验证 zk 是否启动成功
$ zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /Users/terry-jri/Downloads/soft/zookeeper/bin/../conf/zoo.cfg
Client port found: 2181. Client address: localhost.
Mode: standalone
7、启动客户端连接到服务器
$ zkCli.sh -server 127.0.0.1:2181
8、zk 常用命令
[zk] help //查看帮助
[zk] quit //退出
[zk] create /a tom //创建节点且节点 a 且数据为 tom
[zk] get /a //查看数据
[zk] ls / //列出节点
[zk] set /a tom //设置数据
[zk] delete /a //删除一个节点
[zk] rmr /a
三、ZooKeeper 架构
从图中我们可以看出 ZooKeeper 的数据模型,在结构上和标准文件系统的非常相似,都是采用树形层次结构,ZooKeeper 树中的每个节点被称为 Znode。和文件系统的目录树一样,ZooKeeper 树中的每个节点可以拥有子节点。
Znode 兼具文件和目录两种特点。既像文件一样维护着数据、元信息、ACL、时间戳等数据结构,又像目录一样可以作为路径标识的一部分。图中的每个节点称为一个 Znode。 每个 Znode 由 3 部分组成:
(1) stat:此为状态信息,描述该 Znode 的版本,权限等信息
(2) data:与该 Znode 关联的数据
(3) children:该 Znode 下的子节点
Znode 虽然可以关联一些数据,但并没有被设计为数据库,相反的是,它用来管理调度数据,比如,分布式应用中的配置文件信息、状态信息、汇集位置等等。这些数据的共同特性就是它们都是很小的数据,通常以 KB 为大小单位。每个 Znode 的数据大小至多 1M,但常规使用中应该远小于此值。
四、观察者
客户端可以在节点上设置 watch,称之为观察者。当节点状态发生改变时 (增、删、改) 将会触发 watch 所对应的操作。当 watch 被触发时,ZooKeeper 将会向客户端发送且仅发送一条通知,因为 watch 只能被触发一次。
那如何反复收到通知呢?
可以在收到通知时,再注册通知,这样就可以反复收到通知。
五、zk 工作流程
zk 集群启动后,client 连接到其中的一个节点,这个节点可以是 leader,也可以是 follower。连通后,该节点将分配一个 session ID 给 client,并发送 ack 信息。如果客户端没有收到 ack,则会尝试连接到另一个节点。client 也会周期性发送心跳信息给节点保证连接不会丢失。
当 client 读取 zk 集群数据时,将发送读请求给节点,节点直接读取自己数据库,返回节点数据给 client。这也是 zk 集群读取数据非常快的原因。
当 client 写入数据时,将 znode 路径和数据发送给 server,server 转发给 leader。leader 再补发请求给所有 follower。只有大多数 (超过半数) 节点成功响应,则写操作成功。因此 zk 集群一般由奇数个节点构成。
六、Znode
Znode 可分为 3 种,分别是持久节点、临时节点、序列节点。
(1)持久节点 (persistence)
该节点的生命周期不依赖于会话,并且只有在客户端显示执行删除操作的时候,他们才能被删除。默认情况下,创建的就是持久节点。
(2)临时节点 (ephemeral)
临时节点生命周期依赖于创建它们的会话。一旦会话 (Session) 结束,临时节点将被自动删除,当然也可以手动删除,且临时节点不能有子节点。
虽然每个临时的Znode都会绑定到一个客户端会话,但他们对所有的客户端还是可见的。
leader推选是使用。
(3)序列节点 (sequential)
在节点名之后附加 10 位数字,主要用于同步和锁。例如创建一个 /myapp
的序列节点,zk 将会改变路径为 /myapp0000000001
七、Leader 推选过程 (最小号选举法)
在分布式服务中,有一种典型场景,就是通过集群 Master 选举,来解决分布式系统中的单点故障 (SPOF)。
什么是分布式系统中的单点故障:通常分布式系统采用主从模式,就是一个主控机连接多个处理节点。主节点负责分发任务,从节点负责处理任务,当我们的主节点发生故障时,那么整个系统就都瘫痪了,那么我们把这种故障叫作单点故障。
传统方式是采用一个备用节点,这个备用节点定期给当前主节点发送 ping 包,主节点收到 ping 包以后向备用节点发送回复 ack,当备用节点收到回复的时候就会认为当前主节点还活着,让他继续提供服务。
但是这种方式就是有一个隐患,如果主节点的并没有挂,只是在与备节点通信时,网络发生故障,这样备用节点同样收不到回复,就会认为主节点挂了,然后备用节点将他的 Master 实例启动起来,这样我们的分布式系统当中就有了两个主节点,也就是双 Master。
Master 以后我们的从节点就会将它所做的事一部分汇报给了主节点,一部分汇报给了备节点,这样服务就全乱了。
那么 zk 是如何解决这个问题的呢?
所有节点将在同一目录下创建临时序列节点,则节点下会生成 /xxx/xx000000001、/xxx/xx000000002 等节点。序号最小的节点就是leader,其余就是follower.
每个节点观察小于自己节点的主机。(注册观察者)
如果 leader 挂了,对应 znode 删除了,观察者将会收到通知,然后再次发出选举,这时候第二小的节点将成为 leader。
如果主节点恢复了,他会再次向 ZooKeeper 注册一个节点,这时候他注册的节点将会是 /xxx/xx000000003,直到前面的主机都挂掉,该节点将再次成为 leader。