zookeeper原理及集群部署

发布时间 2023-03-22 21:16:25作者: 海yo

背景

知识梳理

原理

ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。
Zookeeper的核心是原子广播,这个机制保证了各个Server之间的同步。实现这个机制的协议叫做Zab协议(ZooKeeper Atomic Broadcast protocol)。Zab协议有两种模式,它们分别是恢复模式(Recovery选主)和广播模式(Broadcast同步)。当服务启动或者在领导者崩溃后,Zab就进入了恢复模式,当领导者被选举出来,且大多数Server完成了和leader的状态同步以后,恢复模式就结束了。状态同步保证了leader和Server具有相同的系统状态。
为了保证事务的顺序一致性,zookeeper采用了递增的事务id号(zxid)来标识事务。所有的提议(proposal)都在被提出的时候加上了zxid。实现中zxid是一个64位的数字,它高32位是epoch用来标识leader关系是否改变,每次一个leader被选出来,它都会有一个新的epoch,标识当前属于那个leader的统治时期。低32位用于递增计数。
zookeeper的集群中,各个节点共有下面3种角色和4种状态:
角色:leader,follower,observer
状态:leading,following,observing,looking

LOOKING:当前Server不知道leader是谁,正在搜寻。
LEADING:当前Server即为选举出来的leader。
FOLLOWING:leader已经选举出来,当前Server与之同步。
OBSERVING:observer的行为在大多数情况下与follower完全一致,但是他们不参加选举和投票,而仅仅接受(observing)选举和投票的结果。

实验环境

IP Jdk Zookeeper集群
192.168.101.209 jdk1.8.0_333 Zk3
192.168.100.64 jdk1.8.0_333 Zk2
192.168.101.1 jdk1.8.0_333 Zk1

部署

1.安装jdk

#!/bin/bash
# auth:chenjf
# func:install jdk
# version:v1.1
# sys:CentOS Linux release 7.6.1810 (Core)
# jdk version:jdk1.8.0_333
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin
path=$(cd $(dirname $0); pwd)
install_path=/usr/local
jdk_package=jdk-8u333-linux-x64.tar.gz
jdk_dir=$install_path/jdk1.8.0_333
if [ ! -d $jdk_dir ];then
        echo "java start to install..."
else
        sudo rm -rf $jdk_dir
        echo "old java was removed,start to install..."
fi
sudo tar -zxf $path/$jdk_package -C $install_path/
grep 'JAVA_HOME' /etc/profile >& /dev/null
if [ $? -ne 0 ];then
        echo "#set jdk environment">>/etc/profile
        echo "export JAVA_HOME=$jdk_dir">>/etc/profile
        echo "export JRE_HOME=\${JAVA_HOME}/jre">>/etc/profile
        echo "export CLASSPATH=.:\${JAVA_HOME}/lib:\${JRE_HOME}/lib">>/etc/profile
        echo "export PATH=\${JAVA_HOME}/bin:\$PATH">>/etc/profile
        echo "set oracle jdk environment OK!"
else
        echo "oracle_jdk_environment already installed.."
fi
source /etc/profile
echo "jdk $jdk_dir install successfully!!!"

2.部署zookeeper

2.1 install.sh

#!/bin/bash
# auth:chenjf
# func:install zookeeper-cluster
# version:v2.0
# sys:CentOS Linux release 7.6.1810 (Core)
# redis version:zookeeper-3.6.3
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin
path=$(cd $(dirname $0); pwd)
source  $path/config.ini
#set -e
##要用root安装
[ $(id -u) -gt 0 ] && echo "please use root to execute the script!" && exit 1
##用yum安装依赖包
sudo yum repolist
sudo yum install make -y
sudo yum install gcc -y
##创建zookeeper组和用户
sudo groupadd zk
sudo useradd -r -g zk -s /bin/false zk
##编译安装zookeeper
sudo tar -zxf $path/$zk_pak
cp -a  $path/$zk_pak_file $install_path/$zookeeper_home
##创建zk的data和log目录
mkdir $zk_data_path -p
mkdir $zk_log_path -p
##拷贝zookeeper启动脚本到zookeeper家目录下,并授权
\cp -av $path/run.sh_default  $path/run.sh
sed -i "s!zookeeper_home!$zookeeper_home!g" $path/run.sh
\cp -av $path/run.sh  $zookeeper_home/run.sh
sudo chmod a+x $zookeeper_home/run.sh
##拷贝zoo.cfg配置文件到conf目录下
\cp -a $path/zoo.cfg_default $path/zoo.cfg
sed -i "s!zookeeper_home!$zookeeper_home!g" $path/zoo.cfg
sed -i "s!zk_port!$zk_port!g" $path/zoo.cfg
sed -i "s!zk1_ip!$zk1_ip!g" $path/zoo.cfg
sed -i "s!zk2_ip!$zk2_ip!g" $path/zoo.cfg
sed -i "s!zk3_ip!$zk3_ip!g" $path/zoo.cfg
sed -i "s!leader_swith_port!$leader_swith_port!g" $path/zoo.cfg
sed -i "s!leader_get_port!$leader_get_port!g" $path/zoo.cfg
\cp -a $path/zoo.cfg  $zookeeper_home/conf
##添加myid
echo "$zk_id" > $zk_data_path/myid
##zookeeper文件归属
sudo chown -R zk:zk $zookeeper_home
##拷贝zookeeper-systemctl启动文件
\cp -av $path/zookeeper.service_default $path/zookeeper.service
sed -i "s!zookeeper_home!$zookeeper_home!g" $path/zookeeper.service
sed -i "s!java_home!$java_home!g" $path/zookeeper.service
\cp -av $path/zookeeper.service /etc/systemd/system/
systemctl daemon-reload
systemctl enable zookeeper.service
echo "zookeeper install successfully!!!"
echo "After modifying the configuration of each node, run the following command:"
echo "systemctl start zookeeper.service"
echo "systemctl status zookeeper.service"

2.2 config.ini

#!/bin/bash
# auth:chenjf
# func:install zookeeper-cluster
# version:v2.0
# sys:CentOS Linux release 7.6.1810 (Core)
# zk version:zookeeper 3.6.3
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin
#shell_dir=$(cd $(dirname $0); pwd)
##根据用户环境,填写如下变量==================================
java_home=/usr/local/jdk1.8.0_333
zk_port=2281
install_dir=/app
shell_dir=$(cd $(dirname $0); pwd)
zk_pak=apache-zookeeper-3.6.3-bin.tar.gz
leader_swith_port=2888
leader_get_port=3888

##以下默认定义不做修改========================================
#zk_pak_file=apache-zookeeper-3.6.3-bin
zk_pak_file=`echo $zk_pak |cut -d '.' -f1-3`
zookeeper_home=$install_dir/zookeeper-${zk_port}
zk_log_path=${zookeeper_home}/logs
zk_data_path=${zookeeper_home}/data
ipaddr=`ifconfig -a|grep inet|grep -v 127.0.0.1|grep -v inet6|awk '{print $2}'|tr -d "addr:"`
zk_id=`cat $shell_dir/ip.list |grep -w $ipaddr|awk '{print $1}'|cut -d "_" -f2`
zk1_ip=`cat $shell_dir/ip.list |grep -w zk_1|awk '{print $2}'`
zk2_ip=`cat $shell_dir/ip.list |grep -w zk_2|awk '{print $2}'`
zk3_ip=`cat $shell_dir/ip.list |grep -w zk_3|awk '{print $2}'`

2.3 ip.list

zk_1 192.168.101.1
zk_2 192.168.100.64
zk_3 192.168.101.209

2.4 run.sh_default

#!/bin/bash
export JVMFLAGS="-Xms512m -Xmx1024m $JVMFLAGS"
zookeeper_home/bin/zkServer.sh start

2.5 zoo.cfg_default

# The number of milliseconds of each tick
#tickTime这个时间是作为zookeeper服务器之间或客户端与服务器之间维持心跳的时间间隔,也就是说每个tickTime时间就会发送一个心跳。单位是毫秒。
tickTime=2000
# The number of ticks that the initial
# synchronization phase can take
# initLimit这个配置项是用来配置zookeeper接受客户端(这里所说的客户端不是用户连接zookeeper服务器的客户端,而是zookeeper服务器集群中连接到leader的follower 服务器)初始化连接时最长能忍受多少个心跳时间间隔数。
# 当已经超过10个心跳的时间(也就是tickTime)长度后 zookeeper 服务器还没有收到客户端的返回信息,那么表明这个客户端连接失败。总的时间长度就是 10*2000=20秒。
initLimit=10
# The number of ticks that can pass between
# sending a request and getting an acknowledgement
# syncLimit这个配置项标识leader与follower之间发送消息,请求和应答时间长度,最长不能超过多少个tickTime的时间长度,总的时间长度就是5*2000=10秒
syncLimit=5
# the directory where the snapshot is stored.
# do not use /tmp for storage, /tmp here is just
# example sakes.
dataLogDir=zookeeper_home/logs
#zookeeper保存数据的目录,默认情况下zookeeper将写数据的日志文件也保存在这个目录里
dataDir=zookeeper_home/data
# the port at which the clients will connect
clientPort=zk_port
# the maximum number of client connections.
# increase this if you need to handle more clients
maxClientCnxns=1000
#服务器允许客户端会话的最小超时时间,毫秒单位。默认为2倍的tickTime,2000*2=4秒
minSessionTimeout=30000
#服务器允许客户端会话的最大超时时间,毫秒单位。默认为20倍的tickTime,2000*20=40秒
maxSessionTimeout=60000
#server.A=B:C:D中的A是一个数字,表示这个是第几号服务器,B是这个服务器的IP地址,C第一个端口用来集群成员的信息交换,表示这个服务器与集群中的leader服务器交换信息的端口,D是在leader挂掉时专门用来进行选举leader所用的端口。
server.1=zk1_ip:leader_swith_port:leader_get_port
server.2=zk2_ip:leader_swith_port:leader_get_port
server.3=zk3_ip:leader_swith_port:leader_get_port
#
# Be sure to read the maintenance section of the
# administrator guide before turning on autopurge.
#
# http://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance
#
# The number of snapshots to retain in dataDir
#当启动自动清理功能,zk只保留3个(默认3个)最近的数据快照和对应事务日志文件,其余将会删除。
autopurge.snapRetainCount=3
# Purge task interval in hours
# Set to "0" to disable auto purge feature
#用于触发清理任务的时间间隔,以小时为单位。默认是0,要开启自动清理,这个值要设为大于等于1的正整数。
#autopurge.purgeInterval=1
autopurge.purgeInterval=24

## Metrics Providers
#
# https://prometheus.io Metrics Exporter
#metricsProvider.className=org.apache.zookeeper.metrics.prometheus.PrometheusMetricsProvider
#metricsProvider.httpPort=7000
#metricsProvider.exportJvmInfo=true
admin.serverPort=27766

2.6 zookeeper.service_default

[Unit]
Description=Zookeeper
After=network.target

[Service]
Type=forking
LimitNOFILE=65535
LimitNPROC=65535
Environment=ZOO_LOG_DIR=zookeeper_home/logs
Environment=JAVA_HOME=java_home
User=zk
Group=zk
ExecStart=zookeeper_home/run.sh
Restart=always
[Install]
WantedBy=multi-user.target

验证

查看节点状态

./zkServer.sh status



打开客户端连接

./zkCli.sh -server 127.0.0.1:2281

help查看命令

查看节点状态:stat /zookeeper


cZxid:创建节点时的事务id
ctime:创建节点时的时间
mZxid:数据节点最后一次更新时的事务id
mtime:最后一次更新的时间
pZxid:子节点列表最后一次被修改的事务id
cversion:节点版本号
dataCersion:数据版本号
aclVerson:acl权限版本号
ephemeralOwner:如果节点是临时节点,则表示创建该节点的会话的 SessionID;如果节点是持久节点,则该属性值为 0
dataLength:数据内容的长度
numChildren:数据节点当前的子节点个数

获取文件内容:get

创建一个Zookeeper节点:create

创建一个Zookeeper(临时)节点: create -e /zk-temp 123

创建临时节点,重新客户端连接进去后,zk-temp临时节点会删除掉。

创建一个Zookeeper(顺序)节点:create -s /zk-test 123

看到创建的zk-test节点后面添加了一串数字以示区别

创建一个Zookeeper(永久)节点:create /zk-permanent 123

可以看到永久节点不同于顺序节点,不会自动在后面添加一串数字

查看节点的数据内容和属性 get /zk-permanent -s

更新节点数据内容 set /zk-permanent 456

删除指定节点 delete /zk-temp


注意:可以看到,已经成功删除/zk-tempt节点。值得注意的是,若删除节点存在子节点,那么无法删除该节点,必须先删除子节点,再删除父节点。

退出节点连接 quit

参考:
https://www.cnblogs.com/EasonJim/p/7488834.html
https://blog.csdn.net/qq_21383435/article/details/126880371
https://www.cnblogs.com/leesf456/p/6022357.html