乐趣区

Zookeeper-学习归整

Zookeeper 的简介

Zookeeper 是一个分布式服务框架,是 Apache Hadoop 的一个子项目,本质上可以认为是一个文件存储系统。在目前的项目实践中,Zookeeper 的角色一般是服务的注册中心,也可能会作为分布式锁的实践方案。

Zookeeper 的内部机理

1 CAP 原则

Consistency:

一致性,数据一致更新,数据变动都要求同步。

Availability:

可用性,系统具有好的响应性能。

Partition tolerance:

分区容错性,系统在出现部分故障的情况下任然能执行服务。

具体解释:

对于目前的分布式服务来说,容错原则几乎是必选的一种原则,因为多台服务器很容易出现部分宕机的情况,不能因为个别服务而影响了整个服务,不然分布式就没有意义了。剩下二者中,基本上是不能够兼顾的。原因在于,如果一个服务确保了高一致性,则数据同步必然花费时间,响应速度就会下降;同理如果要高可用,那么一致性就很难保证。在 Zookeeper 中,业务的计算都是由 Leader 完成的,并且将结果分发到每台 Follower 中,确保了数据一致;但是由于 选举 和 分发 这两个过程,Zookeeper 集群的可用性就相对较弱。故可以简单认为,Zookeeper 是 CP 式设计的注册中心;与之相对的是 Eureka 采用了 AP 式的设计理念,集群中的节点互相较为独立,没有 选举 和 分发 过程,保证了高可用性,但是舍弃了数据的一致性。

2 ZAB 协议

ZAB(Zookeeper Atomic Broadcas [Zookeeper 原子消息广播协议]) 是 Paxos 算法的优化版本,用于保证其数据的一致性。ZAB 协议有两种基本模式,分别是崩溃恢复和消息广播。崩溃恢复的出现场景是 Leader 不存在或者断连的情况下,需要重新进行选举。但凡是 Leader 不存在,Zookeeper 集群就会认为这是一个崩溃的场景,所以在刚开启集群的时候也会触发崩溃恢复。消息广播则存在于需要进行数据同步的阶段。比如 Leader 通过了一项客户端的数据写入请求,需要通知所有的节点进行数据更新。ZAB 协议的核心是 zxid(事务编号)。这是一个 64 位的数字。低 32 位数字是事务计数器,每次客户端有一个增删改数据的请求,计数器就会加一;高 32 位数字是 Leader 周期 epoch 的编号计数器,每次选举产生一个 Leader 就会加一。

3 节点管理

一般可以认为 Zookeeper 就是一个文件系统,是一个 url path 树,每个节点又是一个 key – value 结构。例如下图:

/
|
|—— /znode_1  (key = znode_1  value = znode_1_value)
|      |
|      |—— /znode_1_1  (key = /znode_1/znode_1_1  value = znode_1_1_value)
|      |
|      |—— /znode_1_2  (key = /znode_1/znode_1_2  value = znode_1_2_value)
| 
|—— /znode_2  (key = znode_2  value = znode_2_value)
       |
       |—— /znode_2_1  (key = /znode_2/znode_2_1  value = znode_2_1_value)
       |
       |—— /znode_2_2  (key = /znode_2/znode_2_2  value = znode_2_2_value)

4 集群管理

4.1 集群角色区分

Leader

Leader 主要负责接收 Follower 的心跳信息 (PING) 和 Session 信息(REVALIDATE),并且用来处理需要改动节点数据的请求。Leader 由选举流程选出,可以发起投票。

Follower

Follower 会连接 Client,处理 Client 发来的请求。Follower 一般认为只有处理读数据相关请求的权限,在没有 Leader 授权情况下没有修改和增删的权限。Follower 能够参与选举流程,会参与 Leader 发起的投票。

Observer

Observer 和 Follower 的功能类似,但是不参与选举和投票的流程。要将节点配置为 Observer,需要在配置文件中加入:peerType=observer
server.1:localhost:2181:3181:observer  # 格式 server.[myid]=[ip]:[数据共享端口]:observer

Client

客户端,数据读写的请求发起方。

4.2 选举流程

[快速选举阶段]
step 1 - 集群内所有有资格投票的节点会默认把选票投给自己,然后发送选票结果给其它节点;step 2 - 各个节点开始根据规则修改自己的选票结果:No 1 - 比较 zxid 的 epoch 部分,选最大的,No 2 - 如果 zxid 的 epoch 部分相同,则比较事务计数器,选最大的,No 3 - 如果都相等,选 serverID 最大的,也就是使用者配置的 myid。step 3 - 当有一个节点的得票超过半数,它会把自己的状态修改成 Leader,其它节点修改为 Follower。[恢复阶段]
step 4 - 所有 Follower 会把它们的 zxid 都发送给 Leader,Leader 去判断如何同步数据。[广播阶段]
strp 5 - Leader 会将最新的结果广播给所有节点,节点收到广播后同步数据。

4.3 集群工作流程

step 1 - Follower 或 ObServer 会接收 Client 的请求。如果是一般的读数据请求,那么会直接返回给客户端。如果是写数据或者删数据请求,则会进入 step 2。step 2 - 对于需要修改节点数据的请求(REQUEST),Follower 会发送给 Leader 去处理。step 3 - Leader 会将这一消息转发给其它 Follower 去确认消息(投票)。step 4 - 当有半数以上的 Follower 回复 Leader 同意信息更新 (ACK) 之后,Leader 会开始写入数据,并通知所有 Follower 同步数据。step 5 - 最开始的那个接收了 Client 请求的节点会告诉 Client 请求已经成功或者失败。

Zookeeper 的应用场景

1 数据的发布和订阅

客户端可以将数据注册到 Zookeeper 上,其它的客户端则可以拉取这部分数据。即为配置中心的功能。Zookeeper 采用推拉结合的模式:某个客户端将数据注册到 Zookeeper 之后(拉,Pull),如果该数据发生了变动,Zookeeper 则会主动通知该客户端(推,Push)

2 服务注册中心

本质上 Zookeeper 会把注册进来的服务的 ip / 服务名 / 版本 等信息写到一个树状的结构里。一般来说服务的请求方会在请求完 Zookeeper 的信息之后缓存在本地,以便下次调用。Zookeeper 会和所有注册进来的服务建立 socket 长连接,并且保持心跳检测,如果服务长时间不响应心跳,Zookeeper 就会认为这个服务已经下线,会修改服务数据,并以推送的方式去变更服务端的缓存数据。

3 分布式锁

Zookeeper 对于所有的节点创建,都可以根据创建时间的从早到晚给与一个排序值。基本步骤为:step 1 - 无数个 Client 同时到 Zookeeper 的一个目录下创建节点,那么第一个创建节点 (节点排序值最小,假设为 Root A) 的 Client(假设为 Client A) 就获得了锁;其它的 Client 则会监听 (Watch) 这个排序值最小的节点。step 2 - 拿到锁的 Client A 删除了它创建的节点 Root A(如果 Client A 创建的是零时节点的话,其断开连接也会导致 Root A 消失),所有监听的节点又被激活。step 3 - 重复 step 1。

Zookeeper 的安装和配置

0 安装 java

略,在 CentOs 7 上安装 jdk 8 即可。

1 从官网上下载 Zookeeper

此处笔者下载的是 Zookeeper-3.5.4-beta。

2 配置文件

在 Zookeeper 目录下的 conf 文件夹下有创建一个 zoo.zfg 文件(或者将 zoo_sample.zfg 文件改个名字)。这个 zoo.zfg 文件就是 Zookeeper 的配置文件。内容如下:

# 这个时间是作为 Zookeeper 服务器之间或客户端与服务器之间维持心跳的时间间隔,也就是每个 tickTime 时间就会发送一个心跳
# 值的意思是 N 毫秒
tickTime=2000

# 初始化的时候 Follow 连接到 Leader 能够接受的时间
# 这个值的意思是 N 个 tickTime 的时间
initLimit=10

# 标识 Leader 与 Follower 之间发送消息,请求和应答时间长度
# 值的意思是最长不能超过 N 个 tickTime 的时间长度
syncLimit=5

# Zookeeper 保存数据的目录,默认情况下,Zookeeper 将写数据的日志文件也保存在这个目录里
# myid 文件也存放在这个目录下
dataDir=/root/zookeepers/zookeeper-1/data

# Zookeeper 保存日志文件的目录
dataLogDir=/root/zookeepers/zookeeper-1/logData

#这个端口就是客户端连接 Zookeeper 服务器的端口,Zookeeper 会监听这个端口,接受客户端的访问请求
clientPort=2101

# Zookeeper 的底层驱动是 jetty,所以启动时除了占用 clientPort 之外还会占用一个端口
# 不设置该值得情况下默认 8080 端口
admin.serverPort=9101

# 限制连接到 Zookeeper 的客户端数量
maxClientCnxns=60

# 日志的保留数量
autopurge.snapRetainCount=3

# 日志的保留时间
autopurge.purgeInterval=24


# 集群模式下才需要配置的选项
# 配置的意义:server.[myid]=[ip]:[数据共享端口]:[选举端口]
# 需要在 dataDir 目录下创建 myid 文件,文件中只需要输入一个数字即可,即为 server 后的数字
server.1=localhost:8101:8091
server.2=localhost:8102:8092
server.3=localhost:8103:8093

需要注意的是,集群模式下的一个 Zookeeper 实例其实是需要四个端口的。分别为客户端访问端口、jetty 启动端口、数据共享端口、选举端口。

3 启动

进入到 Zookeeper 的 ./bin 目录下,Linux 下需要使用 zkServer.sh 脚本文件去启动项目,具体命令:

./zkServer.sh start

启动之后可以使用这个脚本文件去查看 Zookeeper 的状态:

./zkServer.sh status

启动成功的打印信息:

ZooKeeper JMX enabled by default
Using config: /root/zookeepers/zookeeper-1/bin/../conf/zoo.cfg
Client port found: 2101. Client address: localhost.
Mode: follower

Mode 的值代表的是该节点的角色。

4 关闭

./zkServer.sh stop
退出移动版