image.png

1. zookeeper 介绍

ZooKeeperYahoo 开发,后来捐赠给了 Apache ,现已成为 Apache 顶级项目。ZooKeeper 是一个开源的分布式应用程序协调服务器,其为分布式系统提供一致性服务。其一致性是通过基于 Paxos 算法的 ZAB 协议完成的。

其主要功能包括:配置维护、分布式同步、集群管理、分布式事务等。

ZooKeeper 是一个 分布式协调服务框架ZooKeeper 保证了 **CP**(数据一致性)。之前的 Eureka 保证了 **AP**(可用性)。

zookeeper 在分布式系统中的应用

  • 分布式配置中心
  • 分布式 job(xxl-job)
  • 服务注册和发现

1.1 集群角色介绍

  • Leader
    • Leader 作为整个 ZooKeeper 集群的主节点,负责响应所有对 ZooKeeper 状态变更的请求。它会将每个状态更新请求进行排序和编号,以便保证整个集群内部消息处理的 FIFO,写操作都走 leader,zk 里面 leader 只有一个。
  • Follower :
    • Follower 的逻辑就比较简单了。除了响应本服务器上的读请求外,follower 还要处理 leader 的提议,并在 leader 提交该提议时在本地也进行提交。
    • 另外需要注意的是,leader 和 follower 构成 ZooKeeper 集群的法定人数,也就是说,只有他们才参与新 leader 的选举、响应 leader 的提议。 帮助 leader 处理读请求,投票权。
  • Observer :
    • 如果 ZooKeeper 集群的读取负载很高,或者客户端多到跨机房,可以设置一些 observer 服务器,以提高读取的吞吐量。Observer 和 Follower 比较相似,只有一些小区别:
      • 首先 observer 不属于法定人数,即不参加选举也不响应提议;
      • 其次是 observer 不需要将事务持久化到磁盘,一旦 observer 被重启,需要从 leader 重新同步整个名字空间。
      • 没有投票权利,可以处理读请求。

1.2 zookeeper 命令介绍

image.png

注意:set 的时候不支持值的中间有空格,如果要加空格\r\n的方式。

1.3 zookeeper 特性

  • zk 是一个由多个 slave 组成的集群,一个 leader,多个 follow
  • 每个 slave 保存一份数据副本
  • 全局数据一致
  • 分布式读 follower,写由 leader 实施更新请求转发
  • 更新请求顺序进行,来自同一个 client 的更新请求按其发送顺序依次执行数据更新原子性,一次数据更新要么成功,要么失败
  • 全局唯一数据视图,client 无论连接到哪个 server,数据视图都是一致的
  • 实时性,在一定事件范围内,client 能读到最新数据

2. zookeeper 核心概念

2.1 znode

image.png

ZooKeeper 操作和维护的为一个个数据节点,称为 znode,采用类似文件系统的层级树状结构进行管理。

创建 znode 时需要指定节点类型,znode 共有 4 种类型,分别为:

  • 持久(无序)(PERSISTENT )
    • 如果不手动删除 是一直存在的
    • **create /zcq**
  • 临时(无序)(EPHEMERAL )
    • 客户端 session 失效就会随着删除节点 没有子节点,退出重启后节点消失
    • **create -e /zcq 2020**
  • 持久有序(PERSISTENT_SEQUENTIAL)

image.png

  • 临时有序(EPHEMERAL_SEQUENTIAL)

:Dubbo 的注册中心使用的是 zookeeper 的临时节点类型,即服务挂了节点就没了。

2.2 Stat 数据结构

Stat 即执行 ls2 /zcq时看到的信息,它记录了 zk 里面数据的信息

  1. [zk: localhost:2181(CONNECTED) 2] ls2 /zcq
  2. [zcq1]
  3. cZxid = 0x6ba
  4. ctime = Sun Jan 12 21:23:01 CST 2020
  5. mZxid = 0x6ba
  6. mtime = Sun Jan 12 21:23:01 CST 2020
  7. pZxid = 0x6bb
  8. cversion = 1
  9. dataVersion = 0
  10. aclVersion = 0
  11. ephemeralOwner = 0x0
  12. dataLength = 4
  13. numChildren = 1

Stat中记录了这个 ZNode 的三个数据版本,分别是:

  • version(当前 ZNode 的版本)
  • cversion(当前 ZNode 子节点的版本)
  • aclVersion(当前ZNode的ACL版本)。
状态属性 说明
czxid 节点创建时的 zxid
mzxid 节点最新一次更新发生时的 zxid
ctime 节点创建时的时间戳
mtime 节点最新一次更新发生时的时间戳.
dataVersion 节点数据的更新次数.
cversion 其子节点的更新次数
aclVersion 节点ACL(授权信息)的更新次数.
ephemeralOwner 如果该节点为ephemeral节点, ephemeralOwner值表示与该节点绑定的session id. 如果该节点不是ephemeral节点, ephemeralOwner值为0. 至于什么是ephemeral节点
dataLength 节点数据的字节数.
numChildren 子节点个数.


2.3 Session会话

客户端来创建一个和 zk 服务端连接的句柄。表示某个客户系统(例如 Batch Job)和 ZooKeeper 之间的连接会话, Batch Job 连上 ZooKeeper 以后会周期性地发送心跳信息, 如果 Zookeepr 在特定时间内收不到心跳,就会认为这个 Batch Job 已经死掉了, Session 就会结束。

连接状态:CONNECTING`CONNECTED\CLOSED`

2.4 watcher

Watcher(事件监听器),是 Zookeeper 中的一个很重要的特性。Zookeeper 允许用户在指定节点上注册一些 Watcher,并且在一些特定事件触发的时候,ZooKeeper 服务端会将事件通知到感兴趣的客户端上去,该机制是 Zookeeper 实现分布式协调服务的重要特性。Watcher:

KeeperState EventType 触发条件 说明 操作
SyncConnected
(3)
None
(-1)
客户端与服务端成功建立连接 此时客户端和服务器处于连接状态
NodeCreated
(1)
Watcher监听的对应数据节点被创建 Create
NodeDeleted
(2)
Watcher监听的对应数据节点被删除 Delete/znode
NodeDataChanged
(3)
Watcher监听的对应数据节点的数据内容发生变更 setDate/znode
NodeChildChanged
(4)
Wather监听的对应数据节点的子节点列表发生变更 Create/child
Disconnected
(0)
None
(-1)
客户端与ZooKeeper服务器断开连接 此时客户端和服务器处于断开连接状态
Expired
(-112)
None
(-1)
会话超时 此时客户端会话失效,通常同时也会受到SessionExpiredException异常
AuthFailed
(4)
None
(-1)
通常有两种情况,1:使用错误的schema进行权限检查 2:SASL权限检查失败 通常同时也会收到AuthFailedException异常

2.5 ACL

ACL(Access Control List) 访问控制列表

内置的 ACL schemes:

  • world:默认方式,相当于全世界都能访问
  • auth:代表已经认证通过的用户 (cli中可以通过 addauth digest user:pwd 来添加当前上下文中的授权用户)
  • digest:即用户名:密码这种方式认证,这也是业务系统中最常用的
  • ip:使用Ip地址认证

ACL支持权限:

  • CREATE: 能创建子节点
  • READ:能获取节点数据和列出其子节点
  • WRITE: 能设置节点数据
  • DELETE: 能删除子节点
  • ADMIN: 能设置权限

image.png
注:cdrwa 就是上边 ACL 支持权限的首字母

2.6 高性能和顺序访问

高性能
ZooKeeper 是高性能的。 适合于读多写少的场景,因为「写」会导致所有服务间同步状态(「读」多于「写」是协调服务的典型场景

顺序访问
对于来自客户端的每个更新请求,ZooKeeper 都会分配一个全局唯一的递增编号,这个编号反应了所有事务操作的先后顺序,应用程序可以使用 ZooKeeper 这个特性来实现更高层次的同步原语。 这个编号也叫做时间戳 —— **zxid**(Zookeeper Transaction Id)

3. zookeeper 的 Java 客户端 API

3.1 原生

3.2 ZkClient

ZkClient 是由 Datameer 的工程师开发的开源客户端,对 Zookeeper 的原生 API 进行了包装,实现了超时重连、Watcher 反复注册等功能。

github源代码地址:https://github.com/sgroschupf/zkclient

3.2.1 maven 依赖

  1. <dependency>
  2. <groupId>org.apache.zookeeper</groupId>
  3. <artifactId>zookeeper</artifactId>
  4. <version>3.4.9</version>
  5. </dependency>
  6. <dependency>
  7. <groupId>com.101tec</groupId>
  8. <artifactId>zkclient</artifactId>
  9. <version>0.10</version>
  10. </dependency>

3.2.2 zkclient 使用

  1. public ZkClient(String serverstring)
  2. public ZkClient(String zkServers, int connectionTimeout)
  3. public ZkClient(String zkServers, int sessionTimeout, int connectionTimeout)
  4. public ZkClient(String zkServers, int sessionTimeout, int connectionTimeout, ZkSerializer zkSerializer)
  5. public ZkClient(final String zkServers, final int sessionTimeout, final int connectionTimeout, final ZkSerializer zkSerializer, final long operationRetryTimeout)
  6. public ZkClient(IZkConnection connection)
  7. public ZkClient(IZkConnection connection, int connectionTimeout)
  8. public ZkClient(IZkConnection zkConnection, int connectionTimeout, ZkSerializer zkSerializer)
  9. public ZkClient(final IZkConnection zkConnection, final int connectionTimeout, final ZkSerializer zkSerializer, final long operationRetryTimeout)

创建节点

public void createPersistent(String path)
public void createPersistent(String path, boolean createParents)
public void createPersistent(String path, boolean createParents, List acl)
public void createPersistent(String path, Object data)
public void createPersistent(String path, Object data, List acl)
public String createPersistentSequential(String path, Object data)
public String createPersistentSequential(String path, Object data, List acl)
public void createEphemeral(final String path)
public void createEphemeral(final String path, final List acl)
public String create(final String path, Object data, final CreateMode mode)
public String create(final String path, Object data, final List acl, final CreateMode mode)
public void createEphemeral(final String path, final Object data)
public void createEphemeral(final String path, final Object data, final List acl)
public String createEphemeralSequential(final String path, final Object data)
public String createEphemeralSequential(final String path, final Object data, final List acl)

删除节点

public boolean delete(final String path)
public boolean delete(final String path, final int version)
public boolean deleteRecursive(String path)

读取列表

public List getChildren(String path)

获取节点内容

public T readData(String path)
public T readData(String path, boolean returnNullIfPathNotExists)
public T readData(String path, Stat stat)

更新内容

public void writeData(String path, Object object)
public void writeData(final String path, Object datat, final int expectedVersion)
public Stat writeDataReturnStat(final String path, Object datat, final int expectedVersion)


监测节点是否存在

protected boolean exists(final String path, final boolean watch)


注册监听

接口类 注册监听方法 解除监听方法
IZkChildListener ZkClient的subscribeChildChanges方法 ZkClient的unsubscribeChildChanges方法
IZkDataListener ZkClient的subscribeDataChanges方法 ZkClient的subscribeChildChanges方法
IZkStateListener ZkClient的subscribeStateChanges方法 ZkClient的unsubscribeStateChanges方法

其中 ZkClient 还提供了一个 unsubscribeAll 方法,来解除所有监听。

3.3 Curator

4. zookeeper 的经典场景应用

4.1 分布式配置中心

image.png

保证高可用:zookeeper 集群 + 数据库存储 + 本地文件 + 缓存

思路:配置文件存储到 zookeeper:create trade:trade1:version1:data
然后去 watcher
服务端:znode path 的定义(包括存储内容)\修改功能
客户端:监听作用,本地缓存

4.2 分布式锁

image.png

利用 zookeeper 的 znode 不能重复的特性
问题:在并发量大的情况下,等待时间会很长,适用于并发不高的情况;选举消耗性能

4.3 服务注册

image.png

利用临时节点(一个注册服务在zookeeper 上就是一个临时节点 )
看 tuling-zk-service 代码(模拟dubbo 注册消费)

5. 源码原理

image.png

5.1 服务端

有两种方式查看入口
1. Jps
image.png
2. 查看启动脚本 zkService.sh

得到入口函数:org.apache.zookeeper.server.quorum.QuorumPeerMain
image.png

image.png

5.2 客户端

image.png

5.3 序列化

OutputArchive 和 InputArchive 分别是 Jute 底层的序列化器和反序列化器。
数据传输结构:
image.png

6. 运维

**

ZooKeeper四字命令 功能描述
conf 3.3.0版本引入的。打印出服务相关配置的详细信息。
cons 3.3.0版本引入的。列出所有连接到这台服务器的客户端全部连接/会话详细信息。包括”接受/发送”的包数量、会话id、操作延迟、最后的操作执行等等信息。
crst 3.3.0版本引入的。重置所有连接的连接和会话统计信息。
dump 列出那些比较重要的会话和临时节点。这个命令只能在leader节点上有用。
envi 打印出服务环境的详细信息。
reqs 列出未经处理的请求
ruok 测试服务是否处于正确状态。如果确实如此,那么服务返回”imok”,否则不做任何相应。
stat 输出关于性能和连接的客户端的列表。
srst 重置服务器的统计。
srvr 3.3.0版本引入的。列出连接服务器的详细信息
wchs 3.3.0版本引入的。列出服务器watch的详细信息。
wchc 3.3.0版本引入的。通过session列出服务器watch的详细信息,它的输出是一个与watch相关的会话的列表。
wchp 3.3.0版本引入的。通过路径列出服务器watch的详细信息。它输出一个与session相关的路径。
mntr 3.4.0版本引入的。输出可用于检测集群健康状态的变量列表

7. 一致性协议和算法

而为了解决数据一致性问题,在科学家和程序员的不断探索中,就出现了很多的一致性协议和算法。比如 2PC(两阶段提交),3PC(三阶段提交),Paxos 算法等等。

拜占庭将军问题 。它意指 在不可靠信道上试图通过消息传递的方式达到一致性是不可能的, 所以所有的一致性算法的 必要前提 就是安全可靠的消息通道。

而为什么要去解决数据一致性的问题?你想想,如果一个秒杀系统将服务拆分成了下订单和加积分服务,这两个服务部署在不同的机器上了,万一在消息的传播过程中积分系统宕机了,总不能你这边下了订单却没加积分吧?你总得保证两边的数据需要一致吧?