本文共 7346 字,大约阅读时间需要 24 分钟。
Zookeeper是Apache的一个java项目
,属于Hadoop系统
,扮演管理员
的角色。ZooKeeper 是一个开源的分布式协调服务
,由雅虎创建,是 Google Chubby 的开源实现。 分布式应用程序
可以基于 ZooKeeper 实现诸如数据发布/订阅
、负载均衡
、命名服务
、分布式协调/通知
、集群管理
、Master 选举
、配置维护
,名字服务
、分布式同步
、分布式锁和分布式队列
等功能。
分布式系统都有好多机器
,比如我在搭建hadoop的HDFS的时候,需要在一个主机器上(Master节点)配置好HDFS需要的各种配置文件,然后通过scp命令把这些配置文件拷贝到其他节点上,这样各个机器拿到的配置信息是一致的,才能成功运行起来HDFS服务。Zookeeper提供了这样的一种服务: 一种集中管理配置的方法,我们在这个集中的地方修改了配置
,所有对这个配置感兴趣的节点
都可以获得变更
。这样就省去手动拷贝配置了,还保证了可靠和一致性
。
这个可以简单理解为一个电话薄,电话号码不好记,但是人名好记,要打谁的电话,直接查人名就好了。
分布式环境下
,经常需要对应用/服务
进行统一命名
,便于识别不同服务
:
域名与ip
之间对应关系,域名容易记住
;通过名称来获取资源或服务的地址
、提供者等信息
单机程序
的各个进程
需要对互斥资源
进行访问时需要加锁
,那分布式程序分布在各个主机上的进程
对互斥资源进行访问时也需要加锁
。很多分布式系统有多个可服务的窗口,但是在某个时刻只让一个服务去干活
,当这台服务出问题的时候锁释放
,立即fail over到另外的服务
。这在很多分布式系统中都是这么做,这种设计有一个更好听的名字叫Leader Election(leader选举
)。举个通俗点的例子,比如银行取钱,有多个窗口,但是呢对你来说,只能有一个窗口对你服务,如果正在对你服务的窗口的柜员突然有急事走了,那咋办?找大堂经理(zookeeper)
!大堂经理指定另外的一个窗口继续为你服务!
在分布式的集群中
,经常会由于各种原因,比如硬件故障
,软件故障
,网络问题
,有些节点会进进出出
。有新的节点加入进来
,也有老的节点退出集群
。这个时候,集群中有些机器(比如Master
节点)需要感知到这种变化
,然后根据这种变化做出对应的决策
。我已经知道HDFS中namenode是通过datanode的心跳机制
来实现上述感知的,那么我们可以先假设Zookeeper其实也是实现了类似心跳机制的功能吧!
最终一致性
:为客户端展示同一视图,这是zookeeper最重要的功能。可靠性
:如果消息被到一台服务器接受,那么它将被所有的服务器接受。实时性
:Zookeeper不能保证
两个客户端能同时得到刚更新的数据
,如果需要最新数据,应该在读数据之前调用sync()接口
。等待无关
(wait-free):慢的或者失效的client不干预快速的client的请求。原子性
:更新只能成功或者失败
,没有中间状态。顺序性
:客户端和服务器建立TCP连接,该连接保证了请求的顺序性
;服务器保证按顺序执行这些请求
。内存中存储了一份数据
;选举一个leader
(Paxos协议);负责处理数据更新等操作
(Zab协议);当且仅当大多数Server在内存中成功修改
数据。 Zookeeper Server数目一般为奇数
。Leader选举算法采用了Paxos协议。Paxos核心思想
:当多数Server写成功,则任务数据写成功
。也就是说:
如果有3个Server,则两个写成功即可;如果有4或5个Server,则三个写成功即可。Server数目一般为奇数(3、5、7)如果有3个Server,则最多允许1个Server挂掉;如果有4个Server,则同样最多允许1个Server挂掉
既然如此,为啥要用4个Server?
3.3.0 以后 版本新增角色Observer
,增加原因
:
Zookeeper需要保证高可用和强一致性
,当集群节点数目逐渐增大为了支持更多的客户端,需要增加更多Server,然而Server增多,投票阶段延迟增大,影响性能
。为了权衡伸缩性和高吞吐率,引入Observer:
不参与投票
;接受客户端的连接
,并将写请求转发给leader节点
;加入更多Observer节点
,提高伸缩性
,同时不影响吞吐率。首先和一个Server或者Observe
(可以认为是一个Server的代理)通信
,发起写请求
,然后Server将写请求转发给Leader
,Leader再将写请求转发给其他Server
,Server在接收到写请求后写入数据
并响应Leader
,Leader在接收到大多数写成功回应
后,认为数据写成功
,响应Client
。 zookeeper采用层次化的目录结构
,命名符合常规文件系统规范
;
每个目录
在zookeeper中叫做znode
,并且其有一个唯一的路径标识
;
Znode可以包含数据
和子znode
(ephemeral(临时)类型的节点不能有子znode);
Znode中的数据可以有多个版本
,比如某一个znode下存有多个数据版本,那么查询这个路径下的数据需带上版本
;
客户端应用
可以在znode上设置监视器
(Watcher);
znode不支持部分读写,而是一次性完整读写
;
Znode有两种类型,短暂的
(ephemeral)和持久的
(persistent);
Znode的类型在创建时确定,并且之后不能再修改
;
ephemeral znode的客户端会话结束时
,zookeeper
会将该ephemeral znode删除
,ephemeral znode不可以有子节点
;
persistent znode不依赖于客户端会话
,只有当客户端明确要删除
该persistent znode时才会被删除
;
Znode有四种形式的目录节点:PERSISTENT持久节点
、PERSISTENT_SEQUENTIAL持久顺序节点
、EPHEMERAL临时节点
、PHEMERAL_SEQUENTIAL短暂顺序节点
。
下一篇转自:
一个 ZooKeeper 集群同一时刻只会有一个 Leader
,其他都是 Follower 或 Observer。
ZooKeeper 配置很简单,每个节点的配置文件(zoo.cfg)都是一样的
,只有 myid 文件不一样。myid 的值必须是 zoo.cfg中server.{数值} 的{数值}部分。
zoo.cfg配置文件示例
在装有 ZooKeeper 的机器的终端执行 zookeeper-server status 可以看当前节点的 ZooKeeper是什么角色(Leader or Follower)。
ZooKeeper 默认只有 Leader 和 Follower 两种角色,没有 Observer 角色。为了使用 Observer 模式,在任何想变成Observer的节点的配置文件中加入:peerType=observer 并在所有 server 的配置文件中,配置成 observer 模式的 server 的那行配置追加 :observer
ZooKeeper 集群的所有机器
通过一个 Leader 选举过程
来选定一台被称为Leader的机器
,Leader服务器
为客户端提供读和写服务
。
Follower 和 Observer 都能提供读服务
,不能提供写服务
。两者唯一的区别在于,Observer机器不参与 Leader 选举过程
,也不参与写操作的『过半写成功』策略
,因此 Observer 可以在不影响写性能的情况下,提升集群的读性能
。
Session 是指客户端会话
,在讲解客户端会话之前,我们先来了解下客户端连接。在ZooKeeper 中,一个客户端连接是指客户端和 ZooKeeper 服务器之间的TCP长连接
。
ZooKeeper 对外的服务端口默认是2181,客户端启动时,首先会与服务器建立一个TCP连接,从第一次连接建立开始
,客户端会话的生命周期也开始了
,通过这个连接,客户端能够通过心跳检测
和服务器保持有效的会话
,也能够向 ZooKeeper 服务器发送请求并接受响应,同时还能通过该连接接收来自服务器的 Watch 事件通知
。
Session 的 SessionTimeout 值用来设置一个客户端会话的超时时间
。当由于服务器压力太大
、网络故障
或是客户端主动断开连接
等各种原因导致客户端连接断开时
,只要在 SessionTimeout 规定的时间内能够重新连接
上集群中任意一台服务器
,那么之前创建的会话仍然有效
。
zookeeper的结构其实就是一个树形结构
,leader就相当于其中的根结点
,其它节点就相当于follow节点,每个节点都保留自己的内容。
zookeeper的节点分两类:持久节点和临时节点
持久节点
:
除非主动进行对树节点的移除
操作,否则这个 节点将一直保存在 ZooKeeper 上。 临时节点
:
一旦客户端会话失效
,那么这个客户端创建的所有临时节点都会被移除。 每个节点
除了存储数据内容
之外,还存储了 节点本身的一些状态信息
。用 get 命令可以同时获得某个节点的内容和状态信息
。在 ZooKeeper 中,version 属性****是用来实现乐观锁机制中的『写入校验』的(保证分布式数据原子性操作)。
在ZooKeeper中,能改变ZooKeeper服务器状态的操作称为事务操作。一般包括数据节点创建与删除
、数据内容更新
和客户端会话创建与失效
等操作。对应每一个事务请求
,ZooKeeper都会为其分配一个全局唯一的事务ID
,用 ZXID 表示,通常是一个64位的数字。每一个 ZXID对应一次更新操作
,从这些 ZXID 中可以间接地识别出 ZooKeeper 处理这些事务操作请求的全局顺序
。
是 ZooKeeper 中一个很重要的特性。ZooKeeper允许用户在指定节点上注册一些 Watcher
,并且在一些特定事件触发的时候
,ZooKeeper 服务端会将事件通知到感兴趣的客户端上去
。该机制是 ZooKeeper 实现分布式协调服务的重要特性。
ZooKeeper 是一个高可用的分布式数据管理与协调框架
。基于对ZAB
算法的实现,该框架能够很好地保证分布式环境中数据的一致性
。也是基于这样的特性,使得 ZooKeeper 成为了解决分布式一致性问题
的利器。
数据发布与订阅,即所谓的配置中心
,顾名思义就是发布者将数据发布到ZooKeeper 节点上
,供订阅者进行数据订阅
,进而达到动态获取数据的目的
,实现配置信息
的集中式管理
和动态更新
。
对于:数据量通常比较小、数据内容在运行时动态变化、集群中各机器共享、配置一致。这样的全局配置信息
就可以发布
到 ZooKeeper上,让客户端(集群的机器)去订阅该消息
。
发布/订阅系统一般有两种设计模式,分别是推(Push)和拉(Pull)模式
。
主动将数据更新发送
给所有订阅的客户端主动发起请求
来获取最新数据,通常客户端都采用定时轮询拉取
的方式ZooKeeper 采用的是推拉相结合
的方式:
向服务端注册自己需要关注的节点
,一旦该节点的数据发生变更
,那么服务端就会向相应的客户端发送Watcher事件通知
,客户端接收到这个消息通知后,需要主动到服务端获取最新的数据
。 命名服务也是分布式系统中比较常见的一类场景。在分布式系统中
,通过使用命名服务,客户端应用
能够根据指定名字来获取资源或服务的地址、提供者等信息
。被命名的实体
通常可以是集群中的机器
,提供的服务
,远程对象
等等——这些我们都可以统称他们为名字。
其中较为常见的就是一些分布式服务框架(如RPC)中的服务地址列表。通过在ZooKeepr里创建顺序节点,能够很容易创建一个全局唯一的路径,这个路径就可以作为一个名字。
ZooKeeper的命名服务即生成全局唯一的ID
。
ZooKeeper 中特有 Watcher 注册
与异步通知机制
,能够很好的实现分布式环境下
不同机器,甚至不同系统
之间的通知与协调
,从而实现对数据变更的实时处理
。使用方法通常是 不同的客户端如果 机器节点 发生了变化,那么所有订阅的客户端都能够接收到相应的Watcher通知
,并做出相应的处理
。
ZooKeeper的分布式协调/通知
,是一种通用的分布式系统机器间的通信方式
。
Master 选举可以说是 ZooKeeper 最典型的应用场景了
。比如 HDFS 中 Active NameNode 的选举、YARN 中 Active ResourceManager 的选举和 HBase 中 Active HMaster 的选举等。
针对 Master 选举的需求,通常情况下,我们可以选择常见的关系型数据库中的主键特性来实现:希望成为 Master 的机器都向数据库中插入一条相同主键ID的记录,数据库会帮我们进行主键冲突检查
,也就是说,只有一台机器能插入成功
——那么,我们就认为向数据库中成功插入数据的客户端机器成为Master
。
依靠关系型数据库的主键特性确实能够很好地保证在集群中选举出唯一的一个Master。
但是,如果当前选举出的 Master 挂了,那么该如何处理?谁来告诉我 Master 挂了呢?
显然,关系型数据库无法通知我们这个事件。但是,ZooKeeper 可以做到!
利用 ZooKeepr 的强一致性
,能够很好地保证在分布式高并发情况下
节点的创建一定能够保证全局唯一性
,即 ZooKeeper 将会保证客户端无法创建一个已经存在的数据单元节点
。
也就是说,如果同时有多个客户端请求创建同一个临时节点,那么最终一定只有一个客户端请求能够创建成功
。利用这个特性,就能很容易地在分布式环境中进行Master 选举了。
成功创建该节点的客户端所在的机器就成为了 Master。同时,其他没有成功创建该节点的客户端
,都会在该节点上注册一个子节点变更的 Watcher
,用于监控当前 Master 机器是否存活
,一旦发现当前的Master挂了
,那么其他客户端将会重新进行 Master 选举
。
这样就实现了 Master 的动态选举。
分布式锁是
控制分布式系统之间
同步访问共享资源
的一种方式。
分布式锁又分为排他锁和共享锁
两种:
排它锁
ZooKeeper如何实现排它锁? 定义锁
ZooKeeper 上的一个 机器节点 可以表示一个锁
获得锁
把ZooKeeper上的一个节点看作是一个锁,获得锁就通过创建临时节点
的方式来实现。ZooKeeper 会保证在所有客户端中
,最终只有一个客户端能够创建成功
,那么就可以认为该客户端获得了锁
。同时,所有没有获取到锁的客户端就需要到
/exclusive_lock节点上注册一个子节点变更的Watcher监听
,以便实时监听到lock节点的变更情况
。 释放锁
因为锁是一个临时节点
,释放锁有两种方式
生宕机或重启
,那么该临时节点就会被删除
,释放锁
执行完业务逻辑后
,客户端就会主动将自己创建的临时节点删除
,释放锁
无论在什么情况下移除了lock节点,ZooKeeper 都会通知
所有在 /exclusive_lock 节点上注册了节点变更 Watcher 监听的客户端
。这些客户端在接收到通知后
,再次重新发起分布式锁获取
,即重复『获取锁』
过程。
共享锁
要获得锁的 Server创建
一个 EPHEMERAL_SEQUENTIAL(临时顺序节点
) 目录节点
,然后调用 getChildren方法获取当前的目录节点列表中最小的
目录节点是不是就是自己创建的目录节点
,如果正是自己创建的,那么它就获得了这个锁
,如果不是
,那么它就调用 exists
(String path, boolean watch) 方法,监控 Zookeeper 上目录节点列表的变化
,一直到
自己创建的节点是列表中最小编号的目录节点
,从而获得锁
删除
前面它自己所创建的目录节点
就行了本文介绍的 Zookeeper 的基本知识,以及介绍了几个典型的应用场景。这些都是 Zookeeper的基本功能,最重要的是 Zoopkeeper 提供了一套很好的分布式集群管理的机制,就是它这种基于层次型的目录树的数据结构,并对树中的节点进行有效管理,从而可以设计出多种多样的分布式的数据管理模型,而不仅仅局限于上面提到的几个常用应用场景
转载地址:http://drbrb.baihongyu.com/