1 Zookeeper 介绍
-
Zookeeper本身是由Yahoo!开发的后来贡献给了Apache的一套用于进行分布式协调的框架,原本是Apache Hadoop的子组件,后来独立出来成为了顶级的项目。Zookeeper不仅仅支持Hadoop,还支持绝大部分的分布式集群
- Zookeeper是根据谷歌的论文《The Chubby Lock Service for Loosely Couple Distribute System》所做的开源实现
- Zookeeper本身是一个中心化服务,提供了统一配置、统一命名、分布式锁和分布式组服务等
2 Zookeeper安装
2.1 规划
nn1.hadoop、nn2.hadoop、s1.hadoop 。
其中:一个是leader,剩余的是Follower
2.2 制作 zookeeper 机器批量分布脚本
用之前的多机脚本拷贝一套zookeeper 多机操作脚本,然后修改成 zookeeper 用的多机操作脚本。
1)拷贝并修改带有ip的文件
cp ips ips_zookeeper
修改 ips_zookeeper 文件 的主机名称只留下nn1.hadoop、nn2.hadoop、s1.hadoop 三台。
2)拷贝脚本
cp scp_all.sh scp_all_zookeeper.sh
cp ssh_all.sh ssh_all_zookeeper.sh
cp ssh_root.sh ssh_root_zookeeper.sh
2.3 把zookeeper的安装包分发到每个zookeeper机器上
#将zookeeper的安装包【zookeeper-3.4.8.tar.gz】上传到/home/hadoop/up 目录下
rz
#将安装包分发到每个zookeeper 机器的 /tmp/ 目录下
./scp_all_zookeeper.sh ~/up/zookeeper-3.4.8.tar.gz /tmp/
#查看是否分发成功
./ssh_all_zookeeper.sh "ls /tmp | grep zo*"
2.4 在所有机器上把zookeeper的tar包解压到/usr/local目录下
#将/tmp/zookeeper-3.4.8.tar.gz 解压到 /usr/local/ 目录
./ssh_root_zookeeper.sh tar -xzf /tmp/zookeeper-3.4.8.tar.gz -C /usr/local/
#查看是否解压成功
./ssh_root_zookeeper.sh "ls -l /usr/local/ | grep zoo"
2.5 修改zookeeper目录所属用户和组为Hadoop,创建软链接
#修改每台机器的/usr/local/zookeeper-3.4.8,所属用户为hadoop
./ssh_root_zookeeper.sh chown -R hadoop:hadoop /usr/local/zookeeper-3.4.8
#在每台机器上给/usr/local/zookeeper-3.4.8创建zookeeper软链接
./ssh_root_zookeeper.sh ln -s /usr/local/zookeeper-3.4.8/ /usr/local/zookeeper
2.6 修改每个机器上的zookeeper 配置
2.6.1 zookeeper脚本所在目录——bin目录**
2.6.2 zookeeper配置文件目录——conf目录**下面为默认配置,就是个例子,实际使用需要修改
1)常用配置文件 conf/zoo.cfg,常用配置说明及示例**
配置示例:**
4)备份zookeeper的conf 目录
#备份原来的配置目录
./ssh_all_zookeeper.sh cp -a /usr/local/zookeeper/conf /usr/local/zookeeper/conf_back
**5)删除原有zoo_sample.cfg 配置文件,上传zoo.cfg 配置文件,并批量拷贝到每台机器**
#批量删除zoo_sample.cfg 配置文件
./ssh_root_zookeeper.sh rm -f /usr/local/zookeeper/conf/zoo_sample.cfg
#批量检查是否删除成功
./ssh_root_zookeeper.sh ls /usr/local/zookeeper/conf/
#上传zoo.cfg 配置文件到 /home/hadoop/up
cd ~hadoop/up
rz
#将zoo.cfg 配置文件批量拷贝到每台机器上
./scp_all_zookeeper.sh ~/up/zoo.cfg /usr/local/zookeeper/conf/
#检查是否拷贝成功
./ssh_root_zookeeper.sh ls /usr/local/zookeeper/conf/zoo.cfg
zoo.cfg 配置文件内容:
2.6.3 修改输出日志配置文件所在目录并分发配置修改zookeeper/bin/zkEnv.sh 脚本,在脚本中给 ZOO_LOG_DIR 设置日志所在目录 修改后
#拷贝zkEnv.sh到每台机器的zookeeper的bin目录下
./scp_all_zookeeper.sh /usr/local/zookeeper/bin/zkEnv.sh /usr/local/zookeeper/bin/
2.7 在每个机器上创建/data目录,并把data目录所有者修改为hadoop用户
#5个机器一起创建 /data目录,因为以后安装hadoop的时候也使用
./ssh_root.sh mkdir /data
#把每个机器上的/data目录所有者修改为hadoop用户
./ssh_root.sh chown hadoop:hadoop /data/zookeeper
#批量验证
./ssh_root.sh "ls -l / | grep data"
2.8 在新建的/data目录下生成myid文件(这个只能手动,不能使用批量脚本)
#在每个机器的/data目录下创建myid文件
./ssh_all_zookeeper.sh touch /data/zookeeper/myid
每台机器都需要单独执行
#第一台:
echo "1" > /data/myid
#第二台:
echo "2" > /data/myid
#第三台:
echo "3" > /data/myid
#查看
cat /data/myid
补充:
2.9 给5个机器设置好环境变量env 查看当前shell环境下已有环境变量
1./etc/profile整个系统的2.~/.bash_profile当前用户的。加载顺序是先加载系统,再加载自己的。
export JAVA_HOME=/usr/java/jdk1.8.0_144
#/usr/bin/java 连接到 /usr/java/jdk1.8.0_144,可用/usr/bin/java 配置JAVA_HOME
#set Hadoop Path
export HADOOP_HOME=/usr/local/hadoop
export HADOOP_COMMON_HOME=${HADOOP_HOME}
export HADOOP_HDFS_HOME=${HADOOP_HOME}
export HADOOP_MAPRED_HOME=${HADOOP_HOME}
export HADOOP_YARN_HOME=${HADOOP_HOME}
export HADOOP_CONF_DIR=${HADOOP_HOME}/etc/hadoop
export HDFS_CONF_DIR=${HADOOP_HOME}/etc/hadoop
export YARN_CONF_DIR=${HADOOP_HOME}/etc/hadoop
export LD_LIBRARY_PATH=$HADOOP_HOME/lib/native:/usr/lib64
export HBASE_HOME=/usr/local/hbase
export HIVE_HOME=/usr/local/hive
export PATH=$PATH:$JAVA_HOME/bin:$HADOOP_HOME/bin:$HADOOP_HOME/sbin:$HBASE_HOME/bin:$HIVE_HOME/bin:/usr/local/zookeeper/bin
加到/etc/profile文件的底部,并且确认保存,注意使用root用户操作,再切换一个用户或者退出就会重新加载刚才的设置,再加env验证一下是否配置正确
#1)分发/etc/profile到5台机器的/tmp
./scp_all.sh /etc/profile /tmp
#批量验证
./ssh_all.sh ls -l /tmp/
#2)批量拷贝到/etc/profile
./ssh_root.sh cp -f /tmp/profile /etc/profile
#批量验证
./ssh_root.sh tail /etc/profile
#批量source
./ssh_root.sh source /etc/profile
查看每个机器的JAVA 版本
./ssh_root.sh java -version
2.10 在每个机器上启动zookeeper服务并查看启动结果
2.10.1 在每个机器上启动zookeeper服务
#批量启动
./ssh_all_zookeeper.sh /usr/local/zookeeper/bin/zkServer.sh start
#批量查看java进程用jps
./ssh_all_zookeeper.sh jps
#或者用ps 查看
ps aux|grep zookeeper 或 ps -ef|grep zookeeper
查看状态 status
前台运行 vs 后台运行 前台运行:ctrl+c 可以关闭的,也就是说 shell 客户端 和 服务器连着呢。
后台运行:直接就提交到服务器, shell 客户端和服务器断开了。
语法: nohup 执行脚本 > .xxx.log 2>&1 & ----> 你关心日志
nohup 执行脚本 > /dev/null 2>&1 & ----> 你不关心日志, 可以输出到无底洞(/dev/null)
2.10.2 查看ZK输出日志和进程信息
#日志输出文件
/data/zookeeper.out
由于ZooKeeper集群启动的时候,每个结点都试图去连接集群中的其它结点,先启动的肯定连不上后面还没启动的,所以上面日志前面部分的异常是可以忽略的。通过后面部分可以看到,集群在选出一个Leader后,最后稳定了。
其他结点可能也出现类似问题,属于正常。
通过 JPS 查看进程ID去查看进程ID文件,再到FD目录就能查看到当前进程所使用的管道信息cd /proc/进程id
zookeeper_server.pid 存当前ZK服务的进程ID的
3 zookeeper使用 3.1 ZooKeeper服务命令1)启动ZK服务: sh bin/zkServer.sh start
2)查看ZK服务状态: sh bin/zkServer.sh status
3)停止ZK服务: sh bin/zkServer.sh stop
4)重启ZK服务: sh bin/zkServer.sh restart
#查看每个机器ZK运行的状态
./ssh_all_zookeeper.sh /usr/local/zookeeper/bin/zkServer.sh status
#整体停止服务
./ssh_all_zookeeper.sh /usr/local/zookeeper/bin/zkServer.sh stop
查看状态结果:
3.2 zk客户端命令1. 显示根节点下的子节点: ls / 2. 显示根目录下的子节点和说明信息: ls2 3. 创建znode,并设置初始内容 创建永久节点:create /zk "节点内容" 创建临时节点:create -e /zk/app "节点内容" 创建时序节点:create -s /zk/app/s1 "节点内容"
\4. 获取节点内容: get /zk 5. 修改节点内容: set /zk "zkbak" 对 zk 所关联的字符串进行设置6. 删除节点(此种方式该节点下不能有子节点): delete /zk 7. 删除节点和所有子节点:rmr /zk7. 退出客户端: quit8. 帮助命令: help
#启动zkclient,并连接zookeeper集群
/usr/local/zookeeper/bin/zkCli.sh -server nn1.hadoop:2181,nn2.hadoop:2181,s1.hadoop:2181
nn1机器客户端登录
cZxid:创建节点时的事务idpZxid:子节点列表最后一次被修改的事务idcversion:节点版本号dataCersion:数据版本号aclVerson:acl权限版本号如何查看是临时节点还是永久节点? 当get 节点信息时,其中有一个字段是ephemeralOwner意思是这个节点的临时拥有者。 当ephemeralOwner 值不为0时,表明这个节点是临时节点,值为会话id。 当ephemeralOwner 值为0时,表明这个节点是永久节点。.
3 zookeeper特点
- ZooKeeper本质上是一个分布式的小文件存储系统;
- Zookeeper是一个树状结构,每一个节点都称之为Znode节点,因此这棵树也称之为Znode树,根节点为/
在Zookeeper中,没有相对路径,所有节点路径都是从根节点开始计算
3.每一个节点都必须存储数据,这个数据可以是对节点的描述信息或者是集群的统一配置信息
4.每一个持久节点都可以挂载子节点,但是临时节点不能挂载子节点
5.每一个节点的路径都是唯一的,所以基于这一个特点,可以做集群的统一命名服务
6.Zookeeper的树状结构是维系在内存中的,即每一个节点中的数据也是维系在内存中,这样做的目的是方便快速查找;同时Zookeeper的树状结构也会以快照的形式维系在磁盘上,在磁盘上的存储位置由dataDir属性来决定
7.在Zookeeper中,会将每一次的写操作(创建节点,删除节点 ,修改节点)看作是一个事务,会为每一个事务分配一个全局递增的编号,这个编号称之为事务id,简写为Zxid
属性 | 解释 |
---|---|
cZxid | 节点被创建的事务id |
ctime | 节点被创建的时间 |
mZxid | 节点被修改的事务id |
mtime | 节点被修改的时间 |
pZxid | 子节点个数变化的事务id |
cversion | 子节点个数的变化次数 |
dataVersion | 节点数据的变化次数 |
aclVersion | 节点的权限变化次数 |
ephemeralOwner | 如果是持久节点,则此项值为0 如果是临时节点,则此项值为sessionid |
dataLength | 节点数据的字节个数 |
numChildren | 子节点个数 |
常见命令 | 说明 |
---|---|
create /video '' | 创建一个/vedio的持久节点,数据为空 |
get /video | 查看/video节点的信息 |
set /video 'hello' | 修改节点数据为‘hello’ |
create -e /demo01 | 创建一个临时节点/demo01 |
create -s /demo02 | 创建一个顺序节点 |
ls / | 查看子节点 |
rmr /test | 删除节点 |
ls /demo04 watch | 监听 /demo04下字节的变化 |
get /demo04 watch | 监听/demo04数据的变化 |
节点类型:
持久节点 -- create /test ''
临时节点--create -e /test ''
持久顺序节点 --create -s /test ''
临时顺序节点 --create -s -e /test ''
4 选举机制
-
在选举刚开始的时候,每一个节点都会进入选举状态,并且都会推荐自己成为leader,然后将自己的选举信息发送给其他的节点
-
节点之间进行两两比较,经过多轮比较之后,最终胜出的节点成为leader
-
选举信息:
-
- 自己所拥有的最大事务id - Zxid
- 自己的选举id - myid
- 逻辑时钟值,作用是确保每一台Zookeeper服务器都会处在同一轮选举中
-
比较原则
-
- 先比较两个节点的最大事务id,谁大谁赢
- 如果最大事务id一致,则比较myid,谁大谁赢
- 经过多轮比较之后,一个节点如果胜过了一半及以上的节点,则这个节点就会成为leader - 过半性
-
在Zookeeper集群中,一旦选举出来leader,那么新添的节点的事务id或者myid是多大,都只能成为follower
-
在Zookeeper集群中,当leader宕机之后,会自动的重新选举出一个新的leader,所以不存在单点故障
-
如果在Zookeeper集群中出现了2个及以上的leader,则这种现象称之为脑裂
-
脑裂产生的原因
-
- 集群产生了分裂
- 分裂之后进行了选举
-
如果一个Zookeeper集群中,存活(能够相互通信)的节点个数不足一半,则剩余的存活节点则停止服务(对外停止服务,对内停止投票)
-
在Zookeeper集群中,会对每次选举出来的leader分配一个唯一的全局递增的编号,称之为epochid。如果Zookeeper集群中存在了多个leader,那么会自动的将epochid较低的节点切换为follower状态
-
节点状态
-
- Looking - 选举状态
- follower - 追随者
- leader - 领导者
- observer - 观察者
5 ZAB协议
-
ZAB(Zookeeper Atomic Broadcast)协议是为分布式协调服务ZooKeeper专门设计的一种支持崩溃恢复的原子广播协议
-
ZAB协议是一种特别为ZooKeeper设计的崩溃可恢复的原子消息广播算法。这个算法是一种类2PC算法,在2PC基础上做的改进
-
2PC算法就是两阶段提交算法
-
- 正常提交
- 中止提交
- 中止提交
-
ZAB协议包括两种基本的模式,分别是:
-
- 消息原子广播(保证数据一致性)
- 崩溃恢复(解决2pc算法的单点问题)
5.1消息原子广播
-
在ZooKeeper中,主要依赖ZAB协议来实现分布式数据一致性,基于该协议,ZooKeeper实现了一种主备模式的系统架构来保持集群中各副本之间数据的一致性,实现分布式数据一致性的这一过程称为消息广播(原子广播)
- ZAB协议的消息广播过程使用的是原子广播协议,类似于一个二阶段提交过程。但是相较于2PC算法,不同的是ZAB协议引入了过半性思想
- ZooKeeper使用一个单一的主进程(leader服务器)来接收并处理客户端的所有事务请求,并采用ZAB的原子广播协议,将服务器数据的状态变更以事务Proposal的形式广播到所有的副本进程(follower或observer)上去。即:所有事务请求必须由一个全局唯一的服务器来协调处理,这样的服务器被称为leader服务器,而余下的其他服务器则成为follower服务器或observer
-
具体流程:
-
- 客户端的事务请求,leader服务器会先将该事务写到本地的log文件中
- 然后,leader服务器会为这次请求生成对应的事务Proposal并且为这个事务Proposal分配一个全局递增的唯一的事务ID,即Zxid
- leader服务器会为每一个follower服务器都各自分配一个单独的队列,将需要广播的事务Proposal依次放入队列中,发送给每一个follower
- 每一个follower在收到队列之后,会从队列中依次取出事务Proposal,写道本地的事务日志中。如果写成功了,则给leader返回一个ACK消息
- 当leader服务器接收到半数的follower的ACK相应之后,就会广播一个Commit消息给所有的follower以通知其进行事务提交,同时leader自身也进行事务提交
- leader在收到Commit消息后完成事务提交
- 然后,leader服务器会为这次请求生成对应的事务Proposal并且为这个事务Proposal分配一个全局递增的唯一的事务ID,即Zxid
- 客户端的事务请求,leader服务器会先将该事务写到本地的log文件中
-
5.当然,在这种简化了的二阶段提交模型下,是无法处理Leader服务器崩溃退出而带来的数据不一致问题的,因此在ZAB协议中添加了另一个模式,即采用崩溃恢复模式来解决这个问题
-
6.整个消息广播协议是基于具有FIFO特性的TCP协议来进行网络通信的,因此能够很容易地保证消息广播过程中消息接收与发送的顺序性
5.2 崩溃恢复
-
- 当leader服务器出现崩溃、重启等场景,或者因为网络问题导致过半的follower不能与leader服务器保持正常通信的时候,Zookeeper集群就会进入崩溃恢复模式
- 进入崩溃恢复模式后,只要集群中存在过半的服务器能够彼此正常通信,那么就可以选举产生一个新的leader
- 每次新选举的leader会自动分配一个全局递增的编号,即epochid
- 当选举产生了新的leader服务器,同时集群中已经有过半的机器与该leader服务器完成了状态同步之后,ZAB协议就会退出恢复模式。其中,所谓的状态同步是指数据同步,用来保证集群中存在过半的机器能够和leader服务器的数据保持一致
- 当集群中已经有过半的follower服务器完成了和leader服务器的状态同步,那么整个服务框架就可以进入消息广播模式了
- 当一台同样遵守ZAB协议的服务器启动后加入到集群中时,如果此时集群中已经存在一个Leader服务器在负责进行消息广播,那么新加入的服务器就会自觉地进入数据恢复模式:找到leader所在的服务器,并与其进行数据同步,然后一起参与到消息广播流程中
6 观察者
一、概述
- 观察者(observer)在zookeeper中即不参与选举也不参与投票,但是会监听选举和投票的结果,根据结果做相应的操作。
- observer可以看成一个 没有选举权和投票权的follower。只有干活的义务,没有选举的权利。
- 在实际开发过程中,如果集群规模比较大,受网络波动影响的考虑,一般会把集群的90%-97%的节点设置为observer。
- 对于当前的Zookeeper架构而言,如果添加了更多的投票成员,则会导致写入性能下降:一个写入操作要求共识协议至少是整体的一半,因此投票的成本随着投票者越多会显著增加
- 在Zookeeper中引入了一个新的Zookeeper节点类型叫做observer(观察者),它帮助处理这个问题并进一步完善了Zookeeper的可扩展性
- 观察者不参与投票,它只监听投票的结果,但是观察者可以和追随者一样运行 ,即客户端可能链接他们并发送读取和写入请求。 观察者像追随者一样转发这些请求到领导者,而他们只是简单的等待监听投票的结果
- 观察者的数量的多少不影响投票的性能。因此观察者不是Zookeeper集群整体的主要组件。 因此如果观察者产生故障或者从集群断开连接都不会影响Zookeeper服务的可用性
- 在实际使用中,观察者可以连接到比追随者更不可靠的网络。事实上,观察者可以用于从其他数据中心和Zookeeper服务通信。观察者的客户端会看到快速的读取,因为所有的读取都在本地,并且写入导致最小的网络开销,因为投票协议所需的消息数量更小
二、配置
- 进入到Zookeeper安装目录下的conf目录:cd zookeeper-3.4.8/conf
- 编辑zoo.cfg文件:vim zoo.cfg
- 在zoo.cfg中添加如下属性:peerType=observer
- 在要配置为观察者的主机后添加观察者标记。例如:
server.1=10.8.42.133:2888:3888
server.2=10.8.42.134:2888:3888
server.3=10.8.42.135:2888:3888:observer #表示将该节点设置为观察者
- 进入到Zookeeper的bin目录下启动该服务器端
cd ../bin
sh zkServer.sh start
过半性:
- 过半选举
- 过半存活
- 过半操作
7 zookeeper特性总结
- 数据一致性:客户端不论连接到哪个Zookeeper节点上,展示给它都是同一个视图,即查询的数据都是一样的。这是Zookeeper最重要的性能
- 原子性:对于事务决议的更新,只能是成功或者失败两种可能,没有中间状态。 要么都更新成功,要么都不更新。即,要么整个集群中所有机器都成功应用了某一事务,要么都没有应用,一定不会出现集群中部分机器应用了改事务,另外一部分没有应用的情况
- 可靠性:一旦Zookeeper服务端成功的应用了一个事务,并完成对客户端的响应,那么该事务所引起的服务端状态变更将会一直保留下来,除非有另一个事务又对其进行了改变
- 实时性:Zookeeper保证客户端将在非常短的时间间隔范围内获得服务器的更新信息,或者服务器失效的信息,或者指定监听事件的变化信息。(前提条件是:网络状况良好)
- 顺序性:如果在一台服务器上消息a在消息b前发布,则在所有服务器上消息a都将在消息b前被发布。客户端在发起请求时,都会跟一个递增的命令号,根据这个机制,Zookeeper会确保客户端执行的顺序性。底层指的是Zxid
- 过半性:Zookeeper集群必须有半数以上的机器存活才能正常工作。因为只有满足过半性,才能满足选举机制选出leader。因为只有过半,在做事务决议时,事务才能更新。所以一般来说,Zookeeper集群的数量最好是奇数个