1.1 ZK特性与集群架构设计

ZK特性:

Zookeeper是分布式协调框架,ZK从设计上有哪些特性?

  • 顺序一致性
  • 实时性
  • 原子性

ZK集群架构设计:

ZK主要分为三种角色:

  • Leader(领导者)
  • Follower(跟随者)
  • Observer(观察者)

1.2 ZK的应用场景

1.2.1 服务注册发现

分布式服务架构中,服务的注册与发现是最核心的基础服务之一,注册中心可以看做是分布式服务架构的通信中心。

Zookeeper是采用ZAB协议保证了数据的强一致性。ZAB协议的实现原理是怎样?ZK是如何实现选举, Paxos算法又是如何运用的?

1.2.2 分布式锁

分布式锁的实现需要注意的:

  • 锁的可重入性。
  • 锁的超时。
  • 锁的阻塞。
  • 锁的特性支持。

分布式锁的使用需要注意:

  • 分布式锁的开销
  • 加锁的粒度
  • 加锁的方式

基于ZK的分布式锁实现:

整体思想:每个客户端发起加锁时,生成一个唯一的临时有序节点。 如何判断锁是否创建呢?只需要判断是否存在临时节点(若存在, 则根据序号判断)。

ZK的分布式锁的优缺点

优点: 可以有效的解决单点问题,不可重入问题,非阻塞问题以及锁无法释放的问题。

缺点: 性能上不如基于缓存实现的分布式锁。

ZK是如何实现分布式锁?

  1. 排它锁
    排它锁实现流程:

  • 定义锁:通过Zookeeper上的数据节点来表示一个锁
  • 获取锁:客户端通过调用create方法创建表示锁的临时节点,可以认为创建成功的客户端获得了锁,同时可以让没有获得锁的节点在该节点上注册Watcher监听,以便实时监听到lock节点的变更情况
  • 释放锁
    • 符合以下两种情况都可以让锁释放
    • 当前获得锁的客户端发生宕机或异常,那么Zookeeper上这个临时节点就会被删除
    • 正常执行完业务逻辑,客户端主动删除自己创建的临时节点

2.共享锁

基于ZK的共享锁实现流程:

1)定义锁

2)获取锁

3)读写顺序判断处理

4)释放锁

3.共享锁产生的羊群效应解决方案

羊群效应该如何解决呢?

其实只需要改进Watch的监听处理流程:

1.3 ZK数据结构与存储

1.3.1 ZK数据结构模型

ZNode节点类型:

Znode的分为四类:

持久节点(persistent node):节点会被持久化处理,执行命令:create /apps/app1 "order"
临时节点(ephemeral node):客户端断开连接后,ZooKeeper会自动删除临时节点, 执行命令:create -e /apps/app1 "order"
顺序节点(sequential node):每次创建顺序节点时,ZooKeeper都会在路径后面自动添加上10位的数字,从1开始,最大值为2147483647 (2^32-1),每个顺序节点都有一个单独的计数器,并且单调递增的,由leader实例维护,执行命令:create -s /apps/app1 "order"
临时顺序节点(EPHEMERAL_SEQUENTIAL):基本特性与临时节点一致,创建节点的过程中,zookeeper会在其名字后自动追加一个单调增长的数字后缀,作为新的节点名。

ZNode节点属性:

[zk: localhost:2181(CONNECTED) 1] get /testNodetestcZxid = 0x2ctime = Fri Aug 06 22:28:23 CST 2020mZxid = 0x2mtime = Fri Aug 06 22:28:23 CST 2020pZxid = 0x2cversion = 0dataVersion = 0aclVersion = 0ephemeralOwner = 0x0dataLength = 4numChildren = 0

1.3.2 ZK数据存储方式

数据存储方式分为三类:

  1. 内存数据

  1. 内存数据结构分为三类:

DataTree
DataNode
ZKDatabase

2.事务日志

事务日志处理流程:

事务日志文件内容示例:

查看日志命令:

java -classpath .:./lib/slf4j-api-1.7.25.jar:./zookeeper-3.4.14.jar org.apache.zookeeper.server.LogFormatter /data/zookeeper/version-2/log.100000001 > log1.log

产生的日志内容:

3/23/20 8:55:19 PM EDT session 0x20003a778cc0012 cxid 0xa9 zxid 0x1000001bc delete '/lock-namespace/shared_lock/order/W-00000000163/23/20 8:55:29 PM EDT session 0x20003a778cc0012 cxid 0xb0 zxid 0x1000001bd delete '/lock-namespace/shared_lock/order/W-00000000173/23/20 9:46:18 PM EDT session 0x20003a778cc0012 cxid 0xb1 zxid 0x1000001be create '/lock-namespace/shared_lock/order/W-0000000018,#3139322e3136382e3132332e313033,v{s{31,s{'world,'anyone}}},T,193/23/20 9:46:38 PM EDT session 0x20003a778cc0012 cxid 0xb4 zxid 0x1000001bf delete '/lock-namespace/shared_lock/order/W-0000000018

3.数据快照(snapshot)

快照查看命令:

java -classpath .:./lib/slf4j-api-1.7.25.jar:./zookeeper-3.4.14.jar org.apache.zookeeper.server.SnapshotFormatter /data/zookeeper/version-2/snapshot.100000000 > snap1.log

其处理步骤如下:

1)检查是否需要进行数据快照,每进行一次事务日志记录之后,Zookeeper都会检测当前是否需要进行数据快照,考虑到数据快照对于Zookeeper机器的影响,需要尽量避免ZK集群中的所有机器在同一时刻进行数据快照。采用过半随机策略进行数据快照操作。

2)切换事务日志文件,表示当前的事务日志已经写满,需要重新创建一个新的事务日志。

3)创建数据快照的异步线程,创建单独的异步线程来进行数据快照以避免影响Zookeeper主线程的运行状态。

4)获取全量数据和会话信息,从ZKDatabase数据库中获取到DataTree和会话信息。

5)生成快照数据文件名,ZK根据当前已经提交的最大ZXID来生成数据快照文件名。

6)数据序列化,首先序列化文件头信息,然后再对会话信息和DataTree分别进行序列化,同时生成一个Checksum校验文件,一并写入快照文件中。