构建Zookeeper集群(zkcluster) ~一篇文章玩转zk集群^.^

概念

Zookeeper集群是由一个leader(负责人)主机和多个follower(追随者)或observer(观察者)主机组成。
构建一个Zookeeper集群需要有一个leader和一个folloer或observer,共两台主机组成,建议最少三台,当一台leader故障时,集群还可以选举出新的leader,保证集群正常工作。

leader负责接受写请求并通知follower/observer同步数据。
所有客户端的写请求都会转发到leader(就算是客户端连接的是follower/observer),leader会进行原子广播(atomic broadcast)通知follower/observer从leader的复制数据库(replicated database)中同步数据,集群中所有的follower/observer都更新成功或失败后返回响应给客户端。
原子广播:原子性,全部都更新成功或者失败。

Zk集群角色说明

leader,领导者,接受所有写请求,存储并广播集群中的follower/observer同步数据;事务请求的唯一调度者和处理者,保证集群事务处理的顺序性。
follower,追随者,接受客户端所有请求,读请求就自己处理,写请求就转发给leader处理;参与事务请求提议的投票(客户端的一个事务请求,需要半数服务器投票通过以后才能通知leader提交,以保证集群中所有主机中数据实时同步);当leader故障时,参与leader选举投票,有可能成为新的leader。
observer,观察者,接受客户端所有请求,读请求就自己处理。写请求就转发给leader处理;不参与投票,不会被选举为leader。如果一个集群是由一个leader,一个follower,一个observer组成的话,当leader故障时,集群则不可用(一个可用集群需要有两台可用服务器参与投票)。但是当扩展了足够数量的主机节点时,若参与投票节点的数量过多,导致写入性能下降的时候,则可以考虑将主机节点设置为该类型,可以减少投票参与者数量。

leader的选举原则

(1)根据zxid选举leader,zxid越大则约优先,若所有服务器上的zxid都相同则根据(2)。
(2)根据myid选举leader,myid越大则越优先。

说明:
zxid:事务ID,服务器中存放的最新的数据ID,zxid越大说明数据最新。
myid:服务器ID,在集群中服务器的唯一标识。

举例:
由A,B,C三台主机组成集群,三台主机都是新建的,数据都为空,它们的zxid都是相同的。
A的myid=1,B的myid=2,C的myid=3。
则按顺位A,B,C启动服务,则A和B对比时,B的myid大,则B会被选举为leader。
在使用过程中,B(leader)故障,又会重新选举leader,若C的数据最新,zxid大则会被选举为新的leader,若A,C数据都相同,即zxid相同,则C会被选举为新的leader,因为它myid最大。

动态重新配置

在3.5.0版本发布之前,Zookeeper 的成员身份和所有其他配置参数都是静态的,在启动期间加载,在运行时是不可变。运维人员使用”滚动重启”方法来扩展或减少集群中主机节点的数量,这是一种手动密集型且容易出错的方法来更改导致数据丢失和生产不一致的配置(如果不停止Zk集群扩展或减少主机节点,则可能会出现无法恢复的异常问题)。
从3.5.0版本开始,Zookeeper完全支持自动配置的更改,Zookeeper支持动态的修改配置信息(增加或减少主机节点),无需服务中断,同时保持数据一致性。
重新配置”reconfig”只需要在集群中的任意一台主机上操作,集群中所有主机都会同步配置信息。
官方文档://zookeeper.apache.org/doc/r3.5.4-beta/zookeeperReconfig.html
在3.5.0~3版本,任意客户端都可以动态的使用”reconfig”来修改集群的配置信息,这可能会有服务器被攻破的恶意客户端随意的篡改配置信息,所以在3.5.3之后的版本Zookeeper引入了权限验证机制,要使用”reconfig”重新配置功能则客户端需要先使用”addauth”进行身份验证。


构建zk集群

Zk集群的构建非常简单,本次只是为了实验,所以我将在一台主机上创建不同的Zk程序构建成集群,若是在生产环境下,建议在不同的主机上运行Zk程序。
注:角色有participant和observer,这边将主机标记为participant即表示服务器为参与者,可通过选举投票成为leader或follower。

程序名称 myid 主机地址 客户端端口 API端口 集群通讯端口 集群选举端口 角色
zk01 1 127.0.0.1 1888 1889 2181 8081 participant
zk02 2 127.0.0.1 2888 2889 2182 8082 participant
zk03 3 127.0.0.1 3888 3889 2183 8083 participant

1、安装JDK

Zookeeper需要JDK的支持。
注:需要先去JDK官网下载安装包。

root@ubuntu:~# mkdir /usr/local/jdk  # 创建程序安装目录
root@ubuntu:~# tar -xvzf jdk-8u211-linux-x64.tar.gz -C /usr/local/jdk/  # 解压安装包
root@ubuntu:~# vim /etc/profile  # 配置环境变量
# jdk
export JAVA_HOME="/usr/local/jdk/jdk1.8.0_211/"
export PATH="$JAVA_HOME/bin:$PATH"
root@ubuntu:~# source /etc/profile  # 使系统环境变量立即生效
root@ubuntu:~# java -version  # 查看java版本以验证jdk安装成功
java version "1.8.0_211"
Java(TM) SE Runtime Environment (build 1.8.0_211-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.211-b12, mixed mode)

2、创建集群程序目录

root@ubuntu:~# mkdir /opt/zk-cluster
root@ubuntu:~# mkdir -p /opt/zk-cluster/zk01
root@ubuntu:~# mkdir -p /opt/zk-cluster/zk02
root@ubuntu:~# mkdir -p /opt/zk-cluster/zk03

3、下载二进制包并解压

root@ubuntu:~# wget //mirror.bit.edu.cn/apache/zookeeper/stable/apache-zookeeper-3.5.8-bin.tar.gz
root@ubuntu:~# tar xzvf apache-zookeeper-3.5.8-bin.tar.gz -C /opt/zk-cluster/zk01/
root@ubuntu:~# tar xzvf apache-zookeeper-3.5.8-bin.tar.gz -C /opt/zk-cluster/zk02/
root@ubuntu:~# tar xzvf apache-zookeeper-3.5.8-bin.tar.gz -C /opt/zk-cluster/zk03/

4、创建数据目录和myid文件

root@ubuntu:~# mkdir /opt/zk-cluster/zk01/apache-zookeeper-3.5.8-bin/data
root@ubuntu:~# mkdir /opt/zk-cluster/zk02/apache-zookeeper-3.5.8-bin/data
root@ubuntu:~# mkdir /opt/zk-cluster/zk03/apache-zookeeper-3.5.8-bin/data
root@ubuntu:~# echo 1 > /opt/zk-cluster/zk01/apache-zookeeper-3.5.8-bin/data/myid
root@ubuntu:~# echo 2 > /opt/zk-cluster/zk02/apache-zookeeper-3.5.8-bin/data/myid
root@ubuntu:~# echo 3 > /opt/zk-cluster/zk03/apache-zookeeper-3.5.8-bin/data/myid

5、配置Zk

root@ubuntu:~# vim /opt/zk-cluster/zk01/apache-zookeeper-3.5.8-bin/conf/zoo.cfg  # zk01-主配置
tickTime=2000
# 心跳超时时间,单位为毫秒。客户端与服务端,服务端与服务端之间的通信超时时间。
initLimit=10
# 集群初始化时,follower服务器和leader服务器之间超时连接次数(tickTime超时多次)。
syncLimit=5 
# 集群同步数据时,follower服务器和leader服务器之间超时连接次数(tickTime超时多次)。
dataDir=/opt/zk-cluster/zk01/apache-zookeeper-3.5.8-bin/data
# 数据存储目录。
# clientPort=2181
# 客户端连接所使用的端口。支持旧版本使用的配置,建议在独立的动态配置文件的主机节点配置条目中绑定客户端
# 地址和端口。
maxClientCnxns=100
# 允许接受的最大客户端连接数。
admin.serverPort=8081
# API监听端口。

### zk 集群 ###
standaloneEnabled=false
# 禁用以独立的方式启动Zk。将此项值设置为"false"则表示以集群分布式模式(Distributed)启用Zk。要禁用此项
# 的前提需要集群节点主机数量是大于>2。
# 默认情况下,standaloneEnabled=true,如果一开始就有多个服务器,则它将不允许缩减到包含少于两个参与者
# (participant)。
reconfigEnabled=true
# 启用重新配置功能,允许使用"reconfig"指令动态配置集群,动态的增加和减少主机节点,启用此功能后,配置将
# 由Zk集群托管,配置会发生重写,集群的配置会独立到一个单独的动态文件中,由配置项"dynamicConfigFile"关
# 联。使用"reconfig"指令需要启用权限验证功能"-
# Dzookeeper.DigestAuthenticationProvider.superDigest=su:gACzJ4L2A0F2ygTno5HQnfabuik="。
# 注:此功能在3.5.3版本后加入的新功能,如果是之前的版本则不支持。
dynamicConfigFile=/opt/zk-cluster/zk01/apache-zookeeper-3.5.8-bin/conf/zoo.cfg.dynamic
# 关联动态配置文件,独立的集群主机节点配置文件。
root@ubuntu:~# vim /opt/zk-cluster/zk01/apache-zookeeper-3.5.8-bin/conf/zoo.cfg.dynamic # zk01-动态配置
# 配置格式:
# server.myid=主机地址:集群通信端口:集群选举端口:角色{参与者(participant)或观察者(observer)};客
# 户端监听地址:监听端口。
server.1=127.0.0.1:1888:1889:participant;2181
server.2=127.0.0.1:2888:2889:participant;2182
server.3=127.0.0.1:3888:3889:participant;2183
-------------------------------------------------------------------------------------------------------
root@ubuntu:~# vim /opt/zk-cluster/zk02/apache-zookeeper-3.5.8-bin/conf/zoo.cfg # zk02-主配置
tickTime=2000
initLimit=10
syncLimit=5 
dataDir=/opt/zk-cluster/zk02/apache-zookeeper-3.5.8-bin/data
maxClientCnxns=100
admin.serverPort=8082
standaloneEnabled=false
reconfigEnabled=true
dynamicConfigFile=/opt/zk-cluster/zk02/apache-zookeeper-3.5.8-bin/conf/zoo.cfg.dynamic
root@ubuntu:~# vim /opt/zk-cluster/zk02/apache-zookeeper-3.5.8-bin/conf/zoo.cfg.dynamic # zk02-动态配置
server.1=127.0.0.1:1888:1889:participant;2181
server.2=127.0.0.1:2888:2889:participant;2182
server.3=127.0.0.1:3888:3889:participant;2183
-------------------------------------------------------------------------------------------------------
root@ubuntu:~# vim /opt/zk-cluster/zk03/apache-zookeeper-3.5.8-bin/conf/zoo.cfg # zk03-主配置
tickTime=2000
initLimit=10
syncLimit=5 
dataDir=/opt/zk-cluster/zk03/apache-zookeeper-3.5.8-bin/data
maxClientCnxns=100
admin.serverPort=8083
standaloneEnabled=false
reconfigEnabled=true
dynamicConfigFile=/opt/zk-cluster/zk03/apache-zookeeper-3.5.8-bin/conf/zoo.cfg.dynamic
root@ubuntu:~# vim /opt/zk-cluster/zk03/apache-zookeeper-3.5.8-bin/conf/zoo.cfg.dynamic # zk03-动态配置
server.1=127.0.0.1:1888:1889:participant;2181
server.2=127.0.0.1:2888:2889:participant;2182
server.3=127.0.0.1:3888:3889:participant;2183

6、配置快捷命令

将集群中的服务配置成快捷指令,这就会操作更便捷。

root@ubuntu:~# vim /etc/profile.d/alias_zk_cluster.sh
alias zkclient='/opt/zk-cluster/zk01/apache-zookeeper-3.5.8-bin/bin/zkCli.sh'
alias zkserver01='/opt/zk-cluster/zk01/apache-zookeeper-3.5.8-bin/bin/zkServer.sh'
alias zkserver02='/opt/zk-cluster/zk02/apache-zookeeper-3.5.8-bin/bin/zkServer.sh'
alias zkserver03='/opt/zk-cluster/zk03/apache-zookeeper-3.5.8-bin/bin/zkServer.sh'
root@ubuntu:~# source /etc/profile

7、开启四字指令和超级管理员权限认证功能

Zk采用插件的方式控制一些特殊功能的启用,所以只需要在启动的命令中添加以下参数即可。

"-Dzookeeper.4lw.commands.whitelist=*"表示启用所有四字指令。
"-Dzookeeper.DigestAuthenticationProvider.superDigest=zk:MsjFltVWzlTGMoDYclrWdyEQ9KU="表示设置超级管理员的账号密码,使用动态配置功能需要拥有超级管理员的权限。
root@ubuntu:~# echo -n zk:123|openssl dgst -binary -sha1 |openssl base64  # 基于用户名和密码组合生成密文密码(sha1加密+base64编码后的密码)
MsjFltVWzlTGMoDYclrWdyEQ9KU=
root@ubuntu:~# vim /opt/zk-cluster/zk01/apache-zookeeper-3.5.8-bin/bin/zkServer.sh
start)
# 省略...
    nohup "$JAVA" $ZOO_DATADIR_AUTOCREATE "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" \
    "-Dzookeeper.4lw.commands.whitelist=*" "-Dzookeeper.DigestAuthenticationProvider.superDigest=zk:MsjFltVWzlTGMoDYclrWdyEQ9KU=" \
    "-Dzookeeper.log.file=${ZOO_LOG_FILE}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" \
    -XX:+HeapDumpOnOutOfMemoryError -XX:OnOutOfMemoryError='kill -9 %p' \
    -cp "$CLASSPATH" $JVMFLAGS $ZOOMAIN "$ZOOCFG" > "$_ZOO_DAEMON_OUT" 2>&1 < /dev/null &
root@ubuntu:~# vim /opt/zk-cluster/zk02/apache-zookeeper-3.5.8-bin/bin/zkServer.sh
# 同上
root@ubuntu:~# vim /opt/zk-cluster/zk03/apache-zookeeper-3.5.8-bin/bin/zkServer.sh
# 同上

8、启动Zk集群

root@ubuntu:~# zkserver01 start
root@ubuntu:~# zkserver02 start
root@ubuntu:~# zkserver03 start

9、查看运行状态

root@ubuntu:~# zkserver01 status
Mode: follower
root@ubuntu:~# echo conf | nc 127.0.0.1 2181  # 使用四字指令
root@ubuntu:~# echo stat| nc 127.0.0.1 2181
root@ubuntu:~# zkserver02 status
Mode: leader
root@ubuntu:~# echo conf | nc 127.0.0.1 2181  # 使用四字指令
root@ubuntu:~# echo stat| nc 127.0.0.1 2181
root@ubuntu:~# zkserver03 status
Mode: follower
root@ubuntu:~# echo stat| nc 127.0.0.1 2181  # 使用四字指令
root@ubuntu:~# echo conf | nc 127.0.0.1 2181

10、测试集群同步

在Zk01上创建/test,在zk02、zk03上查看。
root@ubuntu:~# zkclient -server 127.0.0.1:2181 create /test ‘hello world’
Created /test
root@ubuntu:~# zkclient -server 127.0.0.1:2182 get /test
hello world
root@ubuntu:~# zkclient -server 127.0.0.1:2183 get /test
hello world


动态扩展Zk主机节点

Zookeeper3.5.0版本以后支持动态的扩展和减少主机节点(动态配置功能),新的主机节点加入到集群中,无需整个集群停止。
我这边在之前构建的Zk集群的基础上扩展一个主机节点zk04。

1、创建程序安装目录

root@ubuntu:~# mkdir -p /opt/zk-cluster/zk04 # 创建程序工作目录

2、解压安装包

root@ubuntu:~# tar xzvf apache-zookeeper-3.5.8-bin.tar.gz -C /opt/zk-cluster/zk04 # 解压安装包

3、创建数据目录和myid文件

root@ubuntu:~# mkdir /opt/zk-cluster/zk04/apache-zookeeper-3.5.8-bin/data  # 创建数据目录
root@ubuntu:~# echo 4 > /opt/zk-cluster/zk04/apache-zookeeper-3.5.8-bin/data/myid  # 创建myid文件

4、配置新主机节点

root@ubuntu:~# vim /opt/zk-cluster/zk04/apache-zookeeper-3.5.8-bin/conf/zoo.cfg  # zk04主配置
tickTime=2000
initLimit=10
syncLimit=5 
dataDir=/opt/zk-cluster/zk04/apache-zookeeper-3.5.8-bin/data
maxClientCnxns=100
admin.serverPort=8084
standaloneEnabled=false
reconfigEnabled=true
dynamicConfigFile=/opt/zk-cluster/zk04/apache-zookeeper-3.5.8-bin/conf/zoo.cfg.dynamic
root@ubuntu:~# vim /opt/zk-cluster/zk04/apache-zookeeper-3.5.8-bin/conf/zoo.cfg.dynamic # zk04动态配置
server.1=127.0.0.1:1888:1889:participant;2181
server.2=127.0.0.1:2888:2889:participant;2182
server.3=127.0.0.1:3888:3889:participant;2183
# 添加以下配置只是为了成功启动服务,当服务加入到集群中,动态配置会自动同步,与集群中其他节点配置信息保
# 持一致。
server.4=127.0.0.1:4888:4889:participant;2184

5、开启四字指令和超级管理员权限认证功能

Zk采用插件的方式控制一些特殊功能的启用,所以只需要在启动的命令中添加以下参数即可。

"-Dzookeeper.4lw.commands.whitelist=*"表示启用所有四字指令。
"-Dzookeeper.DigestAuthenticationProvider.superDigest=zk:MsjFltVWzlTGMoDYclrWdyEQ9KU="表示设置超级管理员的账号密码,使用动态配置功能需要拥有超级管理员的权限。
root@ubuntu:~# echo -n zk:123|openssl dgst -binary -sha1 |openssl base64  # 基于用户名和密码组合生成密文密码(sha1加密+base64编码后的密码)
MsjFltVWzlTGMoDYclrWdyEQ9KU=
root@ubuntu:~# vim /opt/zk-cluster/zk04/apache-zookeeper-3.5.8-bin/bin/zkServer.sh  # 开启四字指令和动态配置功能
start)
# 省略...
    nohup "$JAVA" $ZOO_DATADIR_AUTOCREATE "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" \
    "-Dzookeeper.4lw.commands.whitelist=*" "-Dzookeeper.DigestAuthenticationProvider.superDigest=zk:MsjFltVWzlTGMoDYclrWdyEQ9KU=" \
    "-Dzookeeper.log.file=${ZOO_LOG_FILE}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" \
    -XX:+HeapDumpOnOutOfMemoryError -XX:OnOutOfMemoryError='kill -9 %p' \
    -cp "$CLASSPATH" $JVMFLAGS $ZOOMAIN "$ZOOCFG" > "$_ZOO_DAEMON_OUT" 2>&1 < /dev/null &

6、添加快捷命令

root@ubuntu:~# vim /etc/profile.d/alias_zk_cluster.sh  # 添加快捷命令
alias zkclient='/opt/zk-cluster/zk01/apache-zookeeper-3.5.8-bin/bin/zkCli.sh'
# 省略...
alias zkserver04='/opt/zk-cluster/zk04/apache-zookeeper-3.5.8-bin/bin/zkServer.sh'

7、启动新的Zk服务

root@ubuntu:~# zkserver04 start # 启动服务

8、查看运行状态和配置信息

通过四字指令”stat”查看运行状态,看到新节点的运行模式为”follower”,说明新主机节点已经成功加入集群。
通过四字指令”conf”查看配置信息,发现配置已自动同步集群配置,与集群中其他主机节点配置信息保持一致性。

root@ubuntu:~# echo stat | nc 127.0.0.1 2184
Mode: follower
root@ubuntu:~# echo conf | nc 127.0.0.1 2184
# 省略...
membership: 
server.1=127.0.0.1:1888:1889:participant;0.0.0.0:2181
server.2=127.0.0.1:2888:2889:participant;0.0.0.0:2182
server.3=127.0.0.1:3888:3889:participant;0.0.0.0:2183
version=100000000

9、动态添加新主机节点

只需要使用客户端连接集群中任意主机节点,动态添加新的主机节点即可,所有主机节点中的配置信息会自动同步。

root@ubuntu:~# zkclient -server 127.0.0.1:2181
[zk: 127.0.0.1:2181(CONNECTED) 0] addauth digest zk:123  # 使用超级管理员登录Zk
[zk: 127.0.0.1:2181(CONNECTED) 1] reconfig -add server.4=127.0.0.1:4888:4889:participant;2184  # 添加新的主机节点
[zk: 127.0.0.1:2181(CONNECTED) 2] config  # 查看当前主机节点配置信息
server.1=127.0.0.1:1888:1889:participant;0.0.0.0:2181
server.2=127.0.0.1:2888:2889:participant;0.0.0.0:2182
server.3=127.0.0.1:3888:3889:participant;0.0.0.0:2183
server.4=127.0.0.1:4888:4889:participant;0.0.0.0:2184
version=200000002
[zk: 127.0.0.1:2181(CONNECTED) 3] quit

10、查看配置同步情况

依次查看集群中各个主机节点配置信息,查看是否一致。
若都一致则说明新的主机节点已成功加入到集群中!

root@ubuntu:~# echo conf | nc 127.0.0.1 2181
# 省略...
membership: 
server.1=127.0.0.1:1888:1889:participant;0.0.0.0:2181
server.2=127.0.0.1:2888:2889:participant;0.0.0.0:2182
server.3=127.0.0.1:3888:3889:participant;0.0.0.0:2183
server.4=127.0.0.1:4888:4889:participant;0.0.0.0:2184
version=200000002
root@ubuntu:~# echo conf | nc 127.0.0.1 2182
# 同上
root@ubuntu:~# echo conf | nc 127.0.0.1 2183
# 同上
root@ubuntu:~# echo conf | nc 127.0.0.1 2184
# 同上

动态减少Zk主机节点

Zookeeper3.5.0版本以后支持动态的扩展和减少主机节点(动态配置功能),减少集群中的主机节点,只需要从集群的配置中移除该节点即可,操作非常简单。
执行移除操作需要超级管理员操作,超级管理员账号密码是”zk:123″,超级管理员用户名和密码设置请查看”构建Zk集群”。

1、动态移除主机节点

使用客户端连接任意主机节点,重新配置集群,移除主机节点即可。

root@ubuntu:~# zkclient -server 127.0.0.1:2181
[zk: 127.0.0.1:2181(CONNECTED) 1] config  # 查看集群配置
server.1=127.0.0.1:1888:1889:participant;0.0.0.0:2181
server.2=127.0.0.1:2888:2889:participant;0.0.0.0:2182
server.3=127.0.0.1:3888:3889:participant;0.0.0.0:2183
server.4=127.0.0.1:4888:4889:participant;0.0.0.0:2184
version=200000002
[zk: 127.0.0.1:2181(CONNECTED) 2] addauth digest zk:123  # 使用超级管理员身份登录Zk
[zk: 127.0.0.1:2181(CONNECTED) 4] reconfig -remove 4  # 移除myid=4的主机节点
Committed new configuration:
server.1=127.0.0.1:1888:1889:participant;0.0.0.0:2181
server.2=127.0.0.1:2888:2889:participant;0.0.0.0:2182
server.3=127.0.0.1:3888:3889:participant;0.0.0.0:2183
version=200000006
[zk: 127.0.0.1:2181(CONNECTED) 5] quit

2、停止已移除的主机节点

root@ubuntu:~# zkserver04 stop