工作原理:

Zookeeper的核心是原子广播,这个机制保证了各个Server之间的同步。实现这个机制的协议叫做Zab协议。Zab协议有两种模式,它们分别是恢复模式(选举)和广播模式(同步)。当服务启动或者在领导者崩溃后,Zab就进入了恢复模式,当领导者被选举出来,且大多数Server完成了和leader的状态同步以后,恢复模式就结束了。状态同步保证了leader和Server具有相同的系统状态。


Paxos算法 :解决的问题是如何在分布式环境中保证数据的一致性;


角色分工:

Leader: 负责进行投票的发起和决议,更新系统状态

Follower:接收客户请求并向客户端返回结果,在选举过程中参与投票

Observer:接收客户端连接,将写请求转发给leader节点,不参与投票过程,只同步leader状态;主要为扩展系统,提高读取速度


各个角色的工作流程:

Leader主要有三个功能:

1 .恢复数据;

2 .维持与Learner的心跳,接收Learner请求并判断Learner的请求消息类型;

3 .Learner的消息类型主要有PING消息、REQUEST消息、ACK消息、REVALIDATE消息,根据不同的消息类型,进行不同的处理;PING消息是指Learner的心跳信息;REQUEST消息是Follower发送的提议信息,包括写请求及同步请求;ACK消息是Follower的对提议的回复,超过半数的Follower通过,则commit该提议;REVALIDATE消息是用来延长SESSION有效时间


Follower主要有四个功能:

1. 向Leader发送请求(PING消息、REQUEST消息、ACK消息、REVALIDATE消息);

2 .接收Leader消息并进行处理;

3 .接收Client的请求,如果为写请求,发送给Leader进行投票;

4 .返回Client结果。

选举阶段:

Leader选举处理过程:

1.将自己的server.id和zxid信息发送出去

2.如果收到的信息中receive.zxid>= my.zxid,但receive.server.id> my.server.id,则将自己的选票修改为(received.zxid,receive.server.id),否则是(my.zxid,my.server.id)

3.一旦收到的选票超过半数的服务器节点数,便将状态修改为LEADING,并通知投票者将状态设置为FOLLOWING状态

server.id:是节点的服务标识符

zxid:是zookeeper事物ID,zookeeper状态的每一次改变,都会对应一个递增的Transaction id,即zxid,zxid=epoch(32bit)+counter(32bit) ;epoch表示集群中leader所改变的次数


广播阶段:

同一时刻存在一个Leader节点,其他的节点为Follower,如果是更新请求,客户端连接到leader节点,由leader节点执行请求,如果连接到Follower节点,则需将转发请求到leader执行,但对于读请求,客户端可以直接从Follower上读取数据,如果需要读取到最新的数据,在发起读操作时设置强制同步,则会从Leader节点获取


配置文件说明:

tickTime:服务器和客户端之间的心跳机制,用于检测对方的健康状态,这个时间是健康检测频率,每隔tickTime会发送一个心跳,单位为ms
dataDir:zookeeper所产生的数据存放目录,目录下的myid文件要和集群的server ID对应
dataLogDir:事物日志文件存放目录
clientPort:和客户端交互时所使用的服务端口
initLimit:集群中的其他节点在于Leader建立连接,以及建立完成后同步数据所运行的最大时间,它是以tickTime的倍数计算 即initLimit*tickTime
syncTime:集群中Leader与其他节点进行数据同步所运行的最大时间,它是以tickTime的倍数计算,即syncTime*tickTime
server.x=[hostname]:port1:port2[:observer] :x表示服务器ID,即为myid文件中对应;port1 集群内各个节点进行数据交互的端口;port2 进行选举leader时所使用的端口,如果该节点为observer,可以在最后追加一个observer标识
peerType=observer 如果该节点是一个observer节点,则需要添加该配置


zookeeper监控(四字命令):

ruok:最简单的健康检查命令
stat:输出详细的状态信息,如当前服务的客户端连接数,服务角色和最新的事务zxid等
srvr:提供connection信息
dump:提供所有session相关信息,包括其超时时间及相关的暂时性znode节点,该命令只对leader生效
conf:列出所有服务器启动时加载的基本配置信息
envi:列出zookeeper进程的所有环境变量
mntr:提供比stat更详细的报表信息,以key-value键值显示
wchs:列出服务器相关监听信息
wchc:列出服务器上相关的监听信息,通过会话进行排序
wchp:列出服务器上相关的监听信息,通过znode path进行排序

可通过nc 工具 发送四字命令,如:

echo stat | nc localhost 2181
Clients:
 /0:0:0:0:0:0:0:1:38686[0](queued=0,recved=1,sent=0)
Latency min/avg/max: 0/0/0
Received: 1
Sent: 0
Connections: 1
Outstanding: 0
Zxid: 0x100000007
Mode: follower
Node count: 5

zk常用命令:

服务端

zkServer.sh status --查看状态
zkServer.sh start --启动
zkServer.sh restart --重启
zkServer.sh stop --停止
zkServer.sh start-foreground --前台启动

客户端

zkCli.sh -server ip:2181 (create/ls/get/set/delete)


zk集群演示:

IP:192.168.229.128 上启3个zk docker

zk 基础配置

# The number of milliseconds of each tick
tickTime=2000
# The number of ticks that the initial
initLimit=10
# The number of ticks that can pass between
syncLimit=5
# the directory where the snapshot is stored.
dataDir=/opt/zookeeper/data

zk start 脚本

#!/bin/sh
#Identify the JRE
export JAVA_HOME=/usr/java/jdk7
export PATH=$JAVA_HOME/bin:$PATH
export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
#Identify the myid of zk node
echo "${MYID}" > /opt/zookeeper/data/myid
#Identify the service port
echo "clientPort=${ZK_PORT}" >> /opt/zookeeper/conf/zoo.cfg
#Identify the additional zk nodes
echo "${ZK_NODE_1}" >> /opt/zookeeper/conf/zoo.cfg
echo "${ZK_NODE_2}" >> /opt/zookeeper/conf/zoo.cfg
echo "${ZK_NODE_3}" >> /opt/zookeeper/conf/zoo.cfg
#Start zk
exec /opt/zookeeper/bin/zkServer.sh start-foreground

zk Dockerfile

FROM ttxsgoto.com/jdk7
MAINTAINER SA
RUN locale-gen en_US.UTF-8
ENV LANG en_US.UTF-8
RUN /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
ADD ./zookeeper /opt/zookeeper
ADD ./zoo.cfg /opt/zookeeper/conf/
ADD ./zk_start.sh /opt/zookeeper/bin/
RUN chmod +x /opt/zookeeper/bin/zk_start.sh
RUN mkdir -p /opt/zookeeper/data
CMD /opt/zookeeper/bin/zk_start.sh

zk Build

docker build --no-cache=true -t "ttxsgoto.com/zk:v2016" .

zk Run

#zk1
docker run --net=host  --name zk1 \
-d \
-e MYID=1 \
-e ZK_PORT=2181 \
-e ZK_NODE_1=server.1=192.168.229.128:2888:3888 \
-e ZK_NODE_2=server.2=192.168.229.128:2889:3889 \
-e ZK_NODE_3=server.3=192.168.229.128:2890:3890 \
ttxsgoto.com/zk:v2016

#zk2
docker run --net=host  --name zk2 \
-d \
-e MYID=2 \
-e ZK_PORT=2182 \
-e ZK_NODE_1=server.1=192.168.229.128:2888:3888 \
-e ZK_NODE_2=server.2=192.168.229.128:2889:3889 \
-e ZK_NODE_3=server.3=192.168.229.128:2890:3890 \
ttxsgoto.com/zk:v2016

#zk3
docker run --net=host  --name zk3 \
-d \
-e MYID=3 \
-e ZK_PORT=2183 \
-e ZK_NODE_1=server.1=192.168.229.128:2888:3888 \
-e ZK_NODE_2=server.2=192.168.229.128:2889:3889 \
-e ZK_NODE_3=server.3=192.168.229.128:2890:3890 \
ttxsgoto.com/zk:v2016