zookeeper-docker版
Zookeeper-docker版
1 zookeeper概述
1.1 什么是zookeeper
Zookeeper是一个分布式的、高性能的、开源的分布式系统的协调(Coordination)服务,它是一个为分布式应用提供一致性服务的软件。
1.2 zookeeper应用场景
zookeeper是一个经典的分布式数据一致性解决方案,致力于为分布式应用提供一个高性能,高可用,且具有严格属性访问控制能力的分布式协调存储服务。
1、维护配置信息
java编程经常会遇到配置项,比如数据库的url,schema,user和password等。通常这些配置项我们会放置在配置文件中,在将配置文件放置在服务器上当需要更改配置的时,需要去服务器上修改对应的配置信息文件。但是随着分布式系统的兴起,由于许多服务都需要使用到该配置文件,必须保证该配置服务的高可用性和各台服务器上配置数据的一致性。因此,通常会将配置文件部署在一个集群上,然而一个集群动辄上前台服务器,此时如果在一台一台服务器逐个的修改配置文件将是非常繁琐的一个操作。因此就需要一种服务,能够高效快速且可靠的完成配置项的更新等操作,并能够保证各个配置项在每一台服务器上的数据一致性。
zookeeper就可以提供这样一种服务,其使用Zab这种一致性协议来保证一致性。现在有很多开源项目使用zookeeper来维护配置,比如hhase中,客户端就是连接一个zookeeper,获得必要hbase集群的配置信息然后才可以进一步操作。还有开源的消息队列kafka中,也是用zookeeper来维护broker的信息。
2、分布式锁服务
一个集群是一个分布式系统,有多台服务器组成。为了提高并发度和可靠性,多台服务器运行着同一种服务。当多个服务在运行时就需要协调各服务的进度,有时候需要保证当某个服务在进行某个操作时,其他的服务都不能进行该操作,即对该操作进行加锁,如果当前机器挂掉后,并释放fail over到其他的机器继续执行该服务。
3、集群管理
一个集群优势会因为各种软硬件故障或者网络故障,出现某种服务器挂掉而被移除集群,而某些服务器加入到集群中的情况,zookeeper会将这些服务器加入/移出的情况下通知给集群汇总的其他正常工作的服务器,以及时调用存储和计算等任务的分配和执行等。此外zookeeper还会对故障的服务器做出诊断并尝试修复。
4、生成分布式唯一ID
在过去的单库单表型系统中,通常可以使用数据库字段自带的auto_increment属性来自动为每条记录生成一个唯一的ID。但是分库分表后,就无法再依靠数据库的auto_increatment属性来唯一标识一条记录了,此时我们就可以用zookeeper在分布式环境下生成全局唯一ID。做法如下:每一个生成一个新ID时,创建一个持久顺序节点,创建操作返回的节点序号,然后把比自己节点小的删除即可。
1.3 zookeeper的设计目标
zookeeper致力于为分布式应用提供一个高性能,高可用,具有严格顺序访问控制能力的分布式协调服务。
1、高性能
zookeeper将全量数据存储在内存中,并直接服务与客户端的所有非事务请求,尤其适合用于以读为主的应用场景。
2、高可用
zookeeper一般以集群的范式对外提供服务,一般3-5台机器就可以组成一个可用的zookeeper集群,每一台机器都会在内存中维护当前的服务器状态,并且每台机器之间都相互保持着通信。只要集群中超过一台的机器都在工作,那么这个集群就能够正常对外服务;
3、严格访问数据
对于客户端的每一个更新请求,Zookeeper都会分配一个全局唯一的递增编号,这个编号反映了所有事务操作的先后顺序。
2 Zookeeper的数据模型
2.1 zookeeper数据结构
Zookeeper数据模型的结构与Unix文件系统很类似,整体上可以看作是一颗树,每一个节点称做一个ZNode。每一个Znode默认能够存储1MB的数据,每个ZNode都可以通过其路径唯一标识。
如何来描述一个ZNode呢?一个znode大体上分为3部分:
(1)节点的数据:即znode data(节点path,节点data的关系)就像是java map中(key,value)的关系。
(2)节点的子节点children
(2)节点的状态stat:用来描述当前节点的创建,修改记录,包括czxid、ctime等。
2.2 zookeeper节点类型
zookeeper中的节点有两种类型,一种是临时节点和永久节点。节点类型在创建是即被确定,并且不能改变。
(1)临时节点:该节点的生命周期依赖于创建他们的会话。一旦会话(Session)结束,临时节点将会被自动删除,当然可以手动的进行删除。虽然每个临时的ZNode都会绑定到一个客户端会话,但他们对所有的客户端还是可见的。另外,Zookeeper的临时节点不允许拥有子节点。
(2)持久化节点:该节点的生命周期不依赖于会话,并且只有在客户点显示执行删除操作的时候,他们才能被删除。
3 docker部署Zookeeper
3.1 部署准备
1、下载镜像
[root@hadoop104 ~]# docker pull zookeeper:3.5.8
2、创建局域网
在集群部署在同一个局域网中。
[root@hadoop104 ~]# docker network create --subnet=192.168.10.0/24 zk_net
3、创建节点挂载目录
(1)集群规划,集群部署3台机器:
集群编号ZOO_MY_ID | 名称 | 映射本地端口 | ip | 存储路径 |
---|---|---|---|---|
1 | zk1 | 2181 | 192.168.10.101 | /usr/local zookeeper/zk1 |
2 | zk2 | 2182 | 192.168.10.102 | /usr/local zookeeper/zk2 |
3 | zk3 | 2183 | 192.168.10.103 | /usr/local zookeeper/zk3 |
(2)创建节点挂载目录
[root@hadoop104 ~]# cd /usr/local
#创建 zookeeper节点配置存放目录
[root@hadoop104 local]# mkdir -p zookeeper/zk1/conf zookeeper/zk2/conf zookeeper/zk3/conf
#创建 zookeeper节点数据存放目录
[root@hadoop104 local]# mkdir -p zookeeper/zk1/data zookeeper/zk2/data zookeeper/zk3/data
#创建 zookeeper节点数据日志存放目录
[root@hadoop104 local]# mkdir -p zookeeper/zk1/datalog zookeeper/zk2/datalog zookeeper/zk3/datalog
#创建 zookeeper节点日志存放目录
[root@hadoop104 local]# mkdir -p zookeeper/zk1/logs zookeeper/zk2/logs zookeeper/zk3/logs
4、创建配置文件
(1)在第1个节点挂载目录zookeeper/zk1/conf下分别创建配置文件zoo.cfg
[root@hadoop104 local]# vim zookeeper/zk1/conf/zoo.cfg
内容如下:
#Zookeeper保存数据的目录,默认情况下,Zookeeper将写数据的日志文件也保存在这个目录里
dataDir=/data
#事务日志存储地点,如果没提供的话使用的则是 dataDir
dataLogDir=/datalog
#服务器之间或客户端与服务器之间维持心跳的时间间隔,也就是每个 tickTime 时间就会发送一个心跳。tickTime以毫秒为单位
tickTime=2000
#集群中的follower服务器(F)与leader服务器(L)之间初始连接时能容忍的最多心跳数(tickTime的数量)
initLimit=5
#集群中的follower服务器与leader服务器之间请求和应答之间能容忍的最多心跳数(tickTime的数量)
syncLimit=2
#默认值为3,不支持以系统属性方式配置。用于配置Zookeeper在自动清理的时候需要保留的快照数据文件数量和对应的事务日志文件。此参数的最小值为3,如果配置的值小于3会自动调整到3
autopurge.snapRetainCount=3
#默认值为0,单位为小时,不支持以系统属性方式配置。用于配置Zookeeper进行历史文件自动清理的频率。如果配置为0或负数,表示不需要开启定时清理功能
autopurge.purgeInterval=0
#默认为60,不支持以系统属性方式配置。从Socket层面限制单个客户端与单台服务器之间的并发连接数,即以ip地址来进行连接数的限制。
#如果设置为0,表示不做任何限制。仅仅是单台客户端与单个Zookeeper服务器连接数的限制,不能控制所有客户端的连接数总和
maxClientCnxns=60
#3.5.0中的新功能:当设置为false时,可以在复制模式下启动单个服务器,单个参与者可以使用观察者运行,并且群集可以重新配置为一个节点,并且从一个节点。
#对于向后兼容性,默认值为true。可以使用QuorumPeerConfig的setStandaloneEnabled方法或通过将“standaloneEnabled = false”或“standaloneEnabled = true”添加到服务器的配置文件来设置它。
standaloneEnabled=false
#内嵌的管理控制台,停用这个服务
admin.enableServer=false
#开启四字命令,将所有命令添加到白名单中
4lw.commands.whitelist=*
#集群中服务的列表,ip设置为局域网zk_net的网段
server.1=192.168.10.101:2888:3888;2181
server.2=192.168.10.102:2888:3888;2181
server.3=192.168.10.103:2888:3888;2181
zoo.cfg配置文件中参数的说明 解释说明:
tickTime=2000 | zookeeper里面最小的时间单位为2000ms |
---|---|
initLimit=10 | Follower在启动过程中,会从Leader同步所有最新数据,然后确定自己能够对外服务的起始状态。Leader允许F在 initLimit 时间内完成这个工作。通常情况下,我们不用太在意这个参数的设置。如果ZK集群的数据量确实很大了,F在启动的时候,从Leader上同步数据的时间也会相应变长,因此在这种情况下,有必要适当调大这个参数了 |
syncLimit=5 | 在运行过程中,Leader负责与ZK集群中所有机器进行通信,例如通过一些心跳检测机制,来检测机器的存活状态。如果L发出心跳包在syncLimit之后,还没有从F那里收到响应,那么就认为这个F已经不在线了。注意:不要把这个参数设置得过大,否则可能会掩盖一些问题 |
dataDir | 存储快照文件snapshot的目录。默认情况下,事务日志也会存储在这里。建议同时配置参数dataLogDir, 事务日志的写性能直接影响zk性能 |
dataLogDir | 事务日志输出目录。尽量给事务日志的输出配置单独的磁盘或是挂载点,这将极大的提升ZK性能 |
clientPort | 客户端连接server的端口,即对外服务端口 ,默认是2181 |
server.1 | 配置集群节点 |
192.168.10.100:2888:3888 | 主机名, 心跳端口、数据端口 的格式 |
(2)分别创建第2,3个节点的配置文件
[root@hadoop104 local]# cp zookeeper/zk1/conf/zoo.cfg zookeeper/zk2/conf
[root@hadoop104 local]# cp zookeeper/zk1/conf/zoo.cfg zookeeper/zk3/conf
3.2 部署节点
(1)安装并启动第1个节点
[root@hadoop104 ~]# docker run -d --restart always --name zk1 --network zk_net --ip 192.168.10.101 -p 2181:2181 -e ZOO_MY_ID=1 -v /usr/local/zookeeper/zk1/data:/data -v /usr/local/zookeeper/zk1/datalog:/datalog -v /usr/local/zookeeper/zk1/logs:/logs -v /usr/local/zookeeper/zk1/conf/zoo.cfg:/conf/zoo.cfg zookeeper:3.5.8
(2)安装并启动第2个节点
[root@hadoop104 ~]# docker run -d --restart always --name zk2 --network zk_net --ip 192.168.10.102 -p 2182:2181 -e ZOO_MY_ID=2 -v /usr/local/zookeeper/zk2/data:/data -v /usr/local/zookeeper/zk2/datalog:/datalog -v /usr/local/zookeeper/zk2/logs:/logs -v /usr/local/zookeeper/zk2/conf/zoo.cfg:/conf/zoo.cfg zookeeper:3.5.8
(3)安装并启动第3个节点
[root@hadoop104 ~]# docker run -d --restart always --name zk3 --network zk_net --ip 192.168.10.103 -p 2183:2181 -e ZOO_MY_ID=3 -v /usr/local/zookeeper/zk3/data:/data -v /usr/local/zookeeper/zk3/datalog:/datalog -v /usr/local/zookeeper/zk3/logs:/logs -v /usr/local/zookeeper/zk3/conf/zoo.cfg:/conf/zoo.cfg zookeeper:3.5.8
(4)查看节点状态
集群中3个节点,只有一个是leader,其它节点都为flower。
#第1个节点状态
[root@hadoop104 ~]# docker exec -it zk1 /bin/bash
root@245b7b533b30:/apache-zookeeper-3.5.8-bin# zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /conf/zoo.cfg
Client port found: 2181. Client address: localhost.
Mode: follower
#第2个节点状态
[root@hadoop104 ~]# docker exec -it zk2 /bin/bash
root@36d3223cc495:/apache-zookeeper-3.5.8-bin# zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /conf/zoo.cfg
Client port found: 2181. Client address: localhost.
Mode: leader
#第2个节点状态
[root@hadoop104 ~]# docker exec -it zk3 /bin/bash
root@450e6cf10d4f:/apache-zookeeper-3.5.8-bin# zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /conf/zoo.cfg
Client port found: 2181. Client address: localhost.
Mode: follower
3个节点状态正常,集群搭建完成。
4 Zookeeper常用的Shell命令
4.1 基本操作
(1)连接ZooKeeper服务端:zkCli.sh –server ip:port
[root@hadoop104 ~]# docker exec -it zk1 /bin/bash
root@245b7b533b30:/apache-zookeeper-3.5.8-bin# zkCli.sh
[zk: localhost:2181(CONNECTED) 0]
(2)断开连接:quit
(3)查看命令帮助:help
(4)显示指定目录下节点:ls 目录
[zk: localhost:2181(CONNECTED) 7] ls /
[zookeeper]
4.2 新增节点
create [-s] [-e] path data # 其中 -s 为有序节点, -e 临时节点
(1)创建持久化节点并写入数据:
[zk: localhost:2181(CONNECTED) 0] create /test “123456”
Created /test
(2)创建持久化有序节,此时创建的节点名为指定节点名+自增序号
[zk: localhost:2181(CONNECTED) 2] create -s /test/sn “a”
Created /test/sn0000000001
[zk: localhost:2181(CONNECTED) 3] create -s /test/sn “b”
Created /test/sn0000000002
(3)创建临时节点,临时节点会在会话过期后被删除
[zk: localhost:2181(CONNECTED) 4] create -e /tmp “tmp”
Created /tmp
(4)创建临时有序节点,临时节点会在会话过期后被删除
[zk: localhost:2181(CONNECTED) 11] create -s -e /tmpsn “a”
Created /tmpsn0000000002
[zk: localhost:2181(CONNECTED) 12] create -s -e /tmpsn “b”
Created /tmpsn0000000003
4.3 获取节点数据
get -s path 或 stat path
(1)获取节点数据
[zk: localhost:2181(CONNECTED) 13] get /test
123456
(2)获取子节点数据
[zk: localhost:2181(CONNECTED) 15] get /test/sn0000000001
a
(3)查看详细信息
[zk: localhost:2181(CONNECTED) 20] get -s /test
123456
cZxid = 0x100000001
ctime = Thu Sep 12 07:33:53 UTC 2024
mZxid = 0x10000000b
mtime = Thu Sep 12 07:47:49 UTC 2024
pZxid = 0x100000005
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 4
numChildren = 2
节点各个属性如下表。一个重要的概念是Zxid(ZooKeeper Transaction Id),ZooKeeper节点的每一个更改都具唯一的Zxid,如果Zxid1小于Zxid2,则Zxid1的更改发生在Zxid2更改之前。
状态属性 | 节点说明 |
---|---|
cZxid | 数据节点创建时的事务ID |
ctime | 数据节点创建世的时间 |
mZxid | 数据节点最后一个更新是的事务ID |
mtime | 数据节点最后一个跟新时的时间 |
pZxid | 数据节点的子节点最后一个被修改时的事务ID |
cversion | 子节点的更改次数 |
dataVerion | 节点数据的更改次数 |
aclVersion | 节点ACL的更改次数 |
ephemeralOwner | 如果节点是临时节点,则表示创建该节点的会话的SeeesionID;如果是持久节点,则该属性值为0 |
dataLength | 数据内容的长度 |
numChildren | 数据节点当前的子节点个数 |
4.4 更新节点
set path data [version]
(1)使用set命令来更新节点
[zk: localhost:2181(CONNECTED) 16] set /test “7890”
[zk: localhost:2181(CONNECTED) 17] get /test
7890
(2)根据版本号来更新节点
[zk: localhost:2181(CONNECTED) 23] get -s /test
abcd
cZxid = 0x100000002
ctime = Thu Sep 12 07:33:53 UTC 2024
mZxid = 0x10000000c
mtime = Thu Sep 12 07:54:30 UTC 2024
pZxid = 0x100000005
cversion = 3
dataVersion = 2
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 4
numChildren = 3
[zk: localhost:2181(CONNECTED) 24] set /test “abcd” 3
[zk: localhost:2181(CONNECTED) 25] get /test
abcd
也可以基于版本号来进行更改,此时类似于乐观锁机制,当你传入的数据版本号(dataVersion)和当前节点的数据版本号不符合时,zookeeper会拒绝本次修改:
4.5 删除节点
delete path [version]
和更新节点数据一样,也可以传入版本号,当你传入的数据版本号(dataVersion)和当前节点的数据版本号不符合时,zookeeper不会执行删除操作。
(1)删除节点
[zk: localhost:2181(CONNECTED) 27] delete /tmp
(2)要想删除某个节点及其所有后代节点,可以使用递归删除,命令为 rmr path 或 eleteall path。
[zk: localhost:2181(CONNECTED) 30] deleteall /test
4.6 监听器
get -w path或 stat -w path
使用get path [watch] 注册的监听器能够在节点内容发生改变的时候,向客户点发出通知。需要注意的是zookeeper的触发器是一次性的(One-time trigger),触发一次后就会立即失效。
使用stat path [watch] 注册的监听器能够在节点抓哪个台发生改变的时候,向客户点发出通知。
(1)监听节点变化
客户端窗口1
[zk: localhost:2181(CONNECTED) 32] create /watch “123456”
[zk: localhost:2181(CONNECTED) 35] stat -w /watch
另开一个客户端窗口2
[root@hadoop104 ~]# docker exec -it zk2 /bin/bash
root@36d3223cc495:/apache-zookeeper-3.5.8-bin# zkCli
[zk: localhost:2181(CONNECTED) 0] set /watch “abcd”
在客户端窗口1,输出以下结果:
WATCHER::
WatchedEvent state:SyncConnected type:NodeDataChanged path:/watch
5 zookeeper事件监听机制
5.1 watcher概念
zookeeper提供了数据的发布/订阅功能,对个订阅者可同时监听某一特定主题对象,当该主题对象的自身状态发生变化时(例如节点内容改变,节点下的子节点列表改变等),会实时,主动通知所有订阅者;
zookeeper采用了watcher机制实现数据的发布/订阅功能。该机制在被订阅对象发生变化时会异步通知客户端,因此客户端不必在Watcher注册后轮询阻塞,从而减轻了客户点压力。
watcher机制实际上与观察者密室类似,也可以看作是一种观察者密室在分布式场景下的实现方式。
5.2 wathcer架构
Watcher实现由三个部分组成:
- Zookeeper服务端
- Zookeeper客户端
- 客户端的ZKWatchManager对象
客户端首先将Watcher注册到服务端,同时将Watcher对象保存到客户端的Watch管理器中。当Zookeeper服务端监听的数据状态发生变化时,服务端会主动通知客户端,接着客户端的Watch管理器会触发相关Watcher来回调相应处理逻辑,从而完成整体的数据发布/订阅流程。
5.3 wahcher特性
(1)一次性:wathcer是一次性的,一旦被触发就会移除,再次使用时需要重新注册。
(2)客户端顺序回调:watcher回调是顺序串行化执行的,只有回调后客户端才能看到最新的数据状态。一个watcher回调逻辑不用太多,以免影响别的watcher执行。
(3)轻量级:wathcerEvent是最小的通信单元,结构上只包含通知状态,事件类型和节点路径,并不会告诉数据节点变化前后的具体内容。
(4)时效性:watcher只有在当前session彻底失效时才会无效,若session有效期内快速重连成功。则wacher依然存在,然后可接收到通知。
6 zookeeper应用场景
6.1 配置中心案例
工作中有这样一个场景:数据库用户名和密码信息放在一个配置文件中,应用读取该配置文件,配置文件信息放入缓存。 若数据库的用户名和密码改变时候,还需要重新加载缓存,比较麻烦,通过Zookeeper可以轻松完成,当数据库发生变化时自动完成缓存同步。
设计思路:
(1)连接zookeeper服务器。
(2)读取zookeeper中的配置信息,注册watcher监听器,存入本地变量。
(3)当zookeeper中的配置信息发生变化时,通过watcher的回调方法捕获数据变化事件。
(4)重新获取配置信息。
6.2 生成分布式唯一ID
在过去的单库单表型系统中,通常可以使用数据库字段自带的auto_increment属性来自动为每条记录生成一个唯一的ID。但是分库分表后,就无法再依靠数据库的auto_increment属性唯一标识一条记录了。此时我们就可以用zookeeper在分布式环境下生成全局唯一ID。
设计思路:
(1)连接zookeeper服务器
(2)指定路径下生成临时有序节点
(3)取序号及为分布式环境下的唯一ID
6.3 分布式锁
分布式锁有多重实现方式,比如通过数据库,redis都可以实现。作为分布式协同工具Zookeeper,当然也有着标准的实现方式。下面介绍在zookeeper中如何实现排它锁。
设计思路:
(1)每个客户端往/Locks下创建临时有序节点/Locks/Lock_,创建成功后/Locks下面会有每个客户端对应的节点。如/Locks/Lock_0000001
(2)客户端取得/Locks下子节点,并进行排序,判断排在最前面的是否为自己,如果自己的锁节点在第一位,代表获取锁成功。
(3)如果自己的锁节点不在第一位,则监听自己前一位的锁节点。如果自己锁节点Lock_000002,那么则监听Lock_0000001。
(4)当前一位锁节点(Lock_000000001)对应的客户端执行完成,释放了锁,将会触发监听客户端(Lock_000002)的逻辑。
(5)监听客户端重新执行第2步逻辑,判断自己是否获得了锁。
7 zookeeper的leader选举
7.1一致性协议
zab协议的全称是Zookeeper Atomic Broadcast (zookeeper原子广播)。zookeeper是通过zab协议来保证分布式事务的最终一致性
基于zab协议,zookeeper集群中的角色主要有一下三类:
- 领导者(leader):领导者负责进行投票的发起和决议,更新系统状态。
- 学习者(Learner):
- Follower:用于接受客户请求并向客户端返回结果,将写请求转发给leader节点,在选举过程中参与投票。
- ObServer:用于接受客户请求并向客户端返回结果,但是ObServer不参加投票过程,只同步leader的状态。ObServer的目的是为了扩展系统,提高读取速度。
- 客户端(Client) 请求发起方。
zab广播模式工作原理,通过类似两阶段提交协议的方式解决数据一致性:
(1)leader从客户端收到一个写请求。
(2)leader生成一个新的事务并为这个事务生成一个唯一的ZXID。
(3)leader将这个事务提交(propose)发送给所有的follows节点。
(4)follower节点将收到的事务请求加入到历史队列(history queue)中,并发送ack给leader当leader收到大多数follower(半数以上节点)的ack消息,leader会发送commit请求。当follower收到commit请求时,从历史队列中将事务请求commit。
7.2 zookeeper的leader选举
1、服务器状态
(1)looking:寻找leader状态。当服务器处于该状态时,它会认为当前集群中没有leader,因此需要进入leader选举状态。
(2)leading:领导者状态。表明当前服务器角色是leader。
(3)followin:跟随者状态。表明当前服务器角色是follower。
(4)observing:观察者状态。表明当前服务器角色是observer。
2、服务器启动时期的leader选举
在集群初始化阶段,当有一台服务器server启动时,其单独无法进行完成leader选举,当第二台服务器server2启动时,此时两台机器可以相互通信,每台机器都试图找到leader。于是进入leader选举过程。选举过程如下:
(1)每个server发出一个投票。由于是初始情况,server1和server2都会将自己作为leader服务器进行投票,投票会包含所推举的服务器的myid(服务器的编号)和zxid(事务ID),使用(myid,zxid)来表示,此时server1的投票为(1,0),server2的投票为(2,0),然后各自将这个投票发给集群中其他机器。
(2)集群中的每一台服务器接受来自集群中各个服务器的投票。
(3)处理投票。针对每一个投票,服务器都需要将别人的投票和自己的投票进行pk,pk规则如下:
①优先检查zxid。zxid比较大的服务器优先为leader。
②如果zxid相同,那么就比较myid。myid比较大的服务器作为leader服务器。
对于Server1而言,它的投票是(1,0),接受Server2的投票为(2,0),首先会比较两者的zxid,均为0,再比较myid,此时server2的myid最大,于是更新自己的投票为(2,0),然后重新投票,对于server2而言,其无须更新自己的投票,只是再次向集群中所有机器发出上一次的投票信息即可。
(4)统计投票。每次投票后,服务器都会统计投票信息,判断是否已经有过半机器接受到相同的投票信息,对于server1、server2而言,都统计出集群中已经有两台机器接受了(2,0)的投票信息,此时便认为已经选出了leader。
(5)改变服务状态。一旦确定了leader,每个服务器就会更新自己的状态,如果是follower,那么久变更为following,如果是leader,就变更为leading。
2、服务器运行时期的Leader选举
在zookeeper运行期间,leader与非leader服务器各司其职,即便当有非leader服务器宕机或新加入,此时也不会影响leader,但是一旦leader服务器挂了,那么这个集群将暂停对外服务,进入新一轮leader选举,其过程和启动时期的Leader选举过程基本一致。
假设正在运行的server1、server2、server3三台服务器,当前leader是server2,若某一时刻leader挂了、此时开始Leader选举。选举过程如下:
(1)变更状态。leader挂后,剩余的服务器都会将自己的服务状态变更为looking,然后开始进入leader选举过程。
(2)每个server会发出一个投票。在运行期间,每个服务器上的zxid可能不同,此时假设server1的ZXID为122,server3的zxid为122,在第一轮投票中,server1和server3都会投自己、产生投票(1,122),(3,122),然后各自将投票发送给集群中的所有机器。
(3)接受来自各个服务器的投票。与启动时过程相同。
(4)处理投票。与启动时过程相同,此时,server3将会成为leader。
(5)统计投票。与启动时过程相同。
(6)改变服务器的状态。与启动时过程相同。
7.3 observer角色及其配置
1、observer角色特点
(1)不参与集群的leader选举
(2)不参与集群中写数据时的ack反馈
2、配置observer
为了使用observer角色,在任何想变成observer角色的配置文件中加入如下配置:
peerType=observer
并在所有的server的配置文件中,配置成observer模式的server的哪行配置追加:observer。
例如:
server.3=192.168.10.103:2181:2183:observer