1. 集群搭建
- 本次实验基于三台CentOS主机搭建,稍改单机版搭建过程即可。
- 首先,修改
conf/zoo.conf文件,检查一下有没有这些配置信息。dataDir是用于存放数据文件的地方,按照实际情况来。下面的server用来配置集群中所有服务器的信息。该文件集群中所有文件都可以一样,只要目的地址没变就可以。#服务器对应端口号 clientPort=2181 #数据快照文件所在路径 dataDir=/home/zookeeper/zookeeper-3.4.10/data #集群配置信息 #server.A=B:C:D #A:是一个数字,表示这个是服务器的编号 #B:是这个服务器的ip地址 #C:Zookeeper服务器之间的通信端口 #D:Leader选举的端口 server.1=192.168.60.130:2287:3387 server.2=192.168.60.131:2288:3388 server.3=192.168.60.132:2289:3389 - 在
data目录中,写一个文件,用来标识自己的主机id,应该与上面的配置文件相对应。主机是1就写1,2就写2cd data echo "1" > myid - 至此,就配置好了。各台服务器分别启动即可。
./zkServer.sh start - 查看本机角色
./zkServer.sh status - 测试,可以在一台服务器上修改数据,另一台服务器看数据是否有修改。
2. 一致性协议:zab协议
- zab协议的全称是 Zookeeper Atomic Broadcast (zookeeper原子广播)。
- zookeeper 是通过 zab协议来保证分布式事务的最终一致性
- 基于zab协议,zookeeper 集群中的角色主要由三类:
- zab广播模式工作原理:
- 所有数据的写请求,都要由Leader角色来完成。如果是Follower接收到写请求,则会转发到Leader,由Leader来完成。
- 所有机器都可以完成读请求。
- 当Leader收到一个写请求之后,会首先为这个事务生成唯一的新的事务节点。
- 然后Leader 会将这个事务提议(propose)发送给所有的Followers节点。
- Follower 节点收到事务请求之后,会将其加入到历史队列(history queue)中,并回ack给Leader
- 当Leader收到超过半数的ack之后,就会发送commit请求,将数据commit,并发送commit请求给所有Follower,让它们也commit。
3. Leader 选举
3.1 服务器状态
- 服务器一共有几种状态
- looking:寻找leader状态。当服务器处于该状态时,它会认为当前集群中没有 leader,因此需要进入leader选举状态。
- leading: 领导者状态。表明当前服务器角色是leader。
- following: 跟随者状态。表明当前服务器角色是follower。
- observing:观察者状态。表明当前服务器角色是observer。
3.2 服务器启动时期的leader选举
- 在集群初始化初期,当有两台服务器启动时,两台服务器可以相互通信,两台机器都在试图找到leader,因此进入了选举过程。
- 各台服务器会将自己的id以及本机最大的事务id作为自己的投票信息。比如server1的投票信息是
(1, 0),表示自己的id是1,最大的事务id是0; - 集群中的每台服务器都会接收到各台服务器的投票信息。
- 然后就可以进行处理了。首先比较事务id,事务id最大的服务器作为leader;如果事务id都相同,那就比较服务器id,服务器id最大的作为leader
- 统计过后,大家都知道谁是leader,谁是follower了,那就修改自己的状态。
3.3 服务器运行时期的Leader选举
- 一旦leader宕机了,那就要发起新一轮选举了。选举过程如下:
- 所有机器进入looking状态,然后进入leader选举过程
- 与启动过程相同,比较事务id,找最大的。没有就比较服务器id。
- 处理结果,修改状态。
4. observer 角色及其配置
- 观察者角色只是为了提高集群性能,负载能力。它不参与leader的选举。
- 想要当观察者的机器,在其配置文件上面加上一句话
peerType=observer - 并在所有机器的配置文件上,声明服务器的时候,追加一个
:observerserver.3=192.168.60.130:2289:3389:observer
5. Java API 连接zookeeper集群
- 其实操作和单机的没什么不同,只是在配置地址的时候,添加多几个地址
ZooKeeper("192.168.60.130:2181,192.168.60.130:2182,192.168.60.130:2183", 5000, new Watcher() {...});