工作原理:
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