ZooKeeper 是 Apache 软件基金会的一个顶级项目,为分布式应用提供高效、高可用的分布式协调服务;提供配置维护、域名服务、分布式同步、组服务等功能。
1.数据结构
ZooKeeper的数据结构和文件系统非常相似,每个节点叫做znode。每个节点可以通过路径来标识,可以增加、删除 znode,以及在一个znode下增加、删除子znode。结构图如下:
znode类型:
- PERSISTENT 持久化节点
客户端与zookeeper断开连接后,该节点依旧存在。 - PERSISTENT_SEQUENTIAL 持久化有序节点
和PERSISTENT节点类似,唯一区别是节点名会被追加一个10位数字的序列号,当序列号大于2^32-1会溢出。 - EPHEMERAL 临时节点
客户端与zookeeper断开连接后,该节点被删除。 - EPHEMERAL_SEQUENTIAL 临时有序节点
和EPHEMERAL节点类似,且会有序列号。 - CONTAINER 容器节点
如果Container节点下面没有子节点,则Container节点在未来会被Zookeeper自动清除,定时任务默认60s 检查一次。 - PERSISTENT_WITH_TTL 带过期时间的持久化节点(默认禁用)
跟PERSISTENT节点类似,但在过期时间内节点没有任何修改,并且没有任何子节点,则该节点会被删除。TTL时间单位为毫秒。 - PERSISTENT_SEQUENTIAL_WITH_TTL 带过期时间的持久化有序节点(默认禁用)
跟PERSISTENT_WITH_TTL节点类似
创建(持久化)节点命令:
create /node-test-01 abc
创建(持久化)有序节点:
create /seq‐node
create -s /seq‐node/ data
创建临时节点:
create ‐e /no de-ephemeral abc
查看节点数据:
get /node-test-01
修改节点数据:
set /node-test-01 efg
查看子节点:
ls /node-test-01
查看节点状态信息:
stat /node-test-01
cZxid = 0x2
ctime = Mon Nov 15 08:38:46 PST 2021
mZxid = 0x4f
mtime = Sat Nov 20 04:58:31 PST 2021
pZxid = 0x52
cversion = 2
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 4
numChildren = 2
状态信息说明:
- aclVersion:表示对此znode的acl版本。
- ephemeralOwner:znode是临时znode时,表示znode所有者的 session ID。 如果znode不是临时znode,则该字段为零。
- dataLength:znode数据字段的长度。
- cZxid:创建znode的事务ID(Zxid的值)。
- mZxid:最后修改znode的事务ID。
- pZxid:最后添加或删除子节点的事务ID(子节点列表发生变化才会发生改变)。
- ctime:znode创建时间。
- mtime:znode最近修改时间。
- dataVersion:znode的当前数据版本。
- cversion:znode的子节点结果集版本
- numChildren:znode的子znode的数量。
内存结构
Zookeeper 数据都是存储在内存(DataTree)中的,通过 NodeHashMap (ConcurrentHashMap)以路径为 key,节点为value存储。
public class DataTree {
/**
* This map provides a fast lookup to the datanodes. The tree is the
* source of truth and is where all the locking occurs
*/
private final NodeHashMap nodes;
}
/**
* a simple wrapper to ConcurrentHashMap that recalculates a digest after
* each mutation.
*/
public class NodeHashMapImpl implements NodeHashMap {
private final ConcurrentHashMap<String, DataNode> nodes;
private final boolean digestEnabled;
private final DigestCalculator digestCalculator;
}
DataNode 是 Zookeeper 存储节点数据的最小单位,包含了数据、权限、状态信息、子节点等数据。
/**
* This class contains the data for a node in the data tree.
* <p>
* A data node contains a reference to its parent, a byte array as its data, an
* array of ACLs, a stat object, and a set of its children's paths.
*
*/
@SuppressFBWarnings({"EI_EXPOSE_REP", "EI_EXPOSE_REP2"})
public class DataNode implements Record {
// the digest value of this node, calculated from path, data and stat
private volatile long digest;
// indicate if the digest of this node is up to date or not, used to
// optimize the performance.
volatile boolean digestCached;
/** the data for this datanode */
byte[] data;
/**
* the acl map long for this datanode. the datatree has the map
*/
Long acl;
/**
* the stat for this node that is persisted to disk.
*/
public StatPersisted stat;
/**
* the list of children for this node. note that the list of children string
* does not contain the parent path -- just the last part of the path. This
* should be synchronized on except deserializing (for speed up issues).
*/
private Set<String> children = null;
2.监听机制(watch)
客户端注册监听它关心的任意节点,或者目录节点及递归子目录节点。
如果注册的是对某个节点数据的监听,则当这个节点被删除,或者节点数据被修改,对应的客户端将被通知:
get -w /node-test-01
监听端收到的通知:
WatchedEvent state:SyncConnected type:NodeDataChanged path:/node-test-01
如果注册的是对某个节点目录的监听,则当这个目录有子节点被创建,或者有子节点被删除,对应的客户端将被通知:
ls -w /node-test-01
如果注册的是对某个目录的递归子节点进行监听,则当这个目录下面的任意子节点有子节点被创建,或被删除;或者根节点有数据变化时,对应的客户端将被通知:
ls -wR /node-test-01
注意:所有的通知都是一次性的(one-time trigger)
Zookeeper 事件类型:
- None: 连接建立事件
- NodeCreated: 节点创建
- NodeDeleted: 节点删除
- NodeDataChanged:节点数据变化
- NodeChildrenChanged:子节点列表变化
- DataWatchRemoved:节点监听被移除
- ChildWatchRemoved:子节点监听被移除
3.数据持久化
Zookeeper 中的数据都会被持久化到硬盘中。
事务日志
针对每一次客户端的事务操作,Zookeeper 都会将它们记录到事务日志中;Zookeeper也会将数据变更应用到内存数据库中。我们可以在 zookeeper 的主配置文件 zoo.cfg 中配置的数据持久化目录(事务日志的存储路径)dataLogDir 。如果没有配置 dataLogDir ,事务日志将存储到 dataDir 目录。
可以使用 zookeeper 提供的格式化工具查看事务日志数据:
java -classpath lib/slf4j-api-1.7.25.jar:lib/zookeeper-3.6.3.jar:lib/zookeeper-jute-3.6.3.jar org.apache.zookeeper.server.LogFormatter /usr/local/zookeeper/apache-zookeeper-3.6.3-bin/data/version-2/log.4b
日志结果:
ZooKeeper Transactional Log File with dbid 0 txnlog format version 2
2021/11/19 PST 下午7:33:52 session 0x100004dcff80000 cxid 0x0 zxid 0x4b createSession 30000
2,3769449293
2021/11/19 PST 下午7:34:09 session 0x10000192e590000 cxid 0x0 zxid 0x4c closeSession v{}
2,3769449293
2021/11/20 PST 上午4:56:53 session 0x100004dcff80001 cxid 0x0 zxid 0x4d createSession 30000
2,3769449293
2021/11/20 PST 上午4:58:15 session 0x100004dcff80001 cxid 0x5 zxid 0x4e create '/node-test-01/abc,,v{s{31,s{'world,'anyone}}},F,1
2,8164596078
2021/11/20 PST 上午4:58:31 session 0x100004dcff80001 cxid 0x7 zxid 0x4f setData '/node-test-01,#61626364,1
2,5409280852
每条日志包含了操作时间、客户端会话ID、CXID、ZXID、操作类型、节点路径、节点数据(用 #+ascii 码表示)、节点版本。
数据快照
数据快照用于记录 zookeeper 上某一时刻的全量数据,并将其写入到指定的磁盘文件中。 可以通过配置 snapCount 配置每间隔事务请求个数,生成快照,数据存储在 dataDir 指定的目录中,可以通过如下方式进行查看快照数据( 为了避免集群中所有机器在同一时间进行快照,实际的快照生成时机为事务数达到 [snapCount/2 + 随机数(随机数范围为1 ~ snapCount/2 )] 个数时开始快照)
java -classpath lib/slf4j-api-1.7.25.jar:lib/snappy-java-1.1.7.jar:lib/zookeeper-3.6.3.jar:lib/zookeeper-jute-3.6.3.jar org.apache.zookeeper.server.SnapshotFormatter /usr/local/zookeeper/apache-zookeeper-3.6.3-bin/data/version-2/snapshot.2
日志结果:
ZNode Details (count=6):
----
/
cZxid = 0x00000000000000
ctime = Wed Dec 31 16:00:00 PST 1969
mZxid = 0x00000000000000
mtime = Wed Dec 31 16:00:00 PST 1969
pZxid = 0x00000000000002
cversion = 1
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x00000000000000
dataLength = 0
----
/node-test-01
cZxid = 0x00000000000002
ctime = Mon Nov 15 08:38:46 PST 2021
mZxid = 0x00000000000002
mtime = Mon Nov 15 08:38:46 PST 2021
pZxid = 0x00000000000002
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x00000000000000
dataLength = 3
----
/zookeeper
cZxid = 0x00000000000000
ctime = Wed Dec 31 16:00:00 PST 1969
mZxid = 0x00000000000000
mtime = Wed Dec 31 16:00:00 PST 1969
pZxid = 0x00000000000000
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x00000000000000
dataLength = 0
----
/zookeeper/config
cZxid = 0x00000000000000
ctime = Wed Dec 31 16:00:00 PST 1969
mZxid = 0x00000000000000
mtime = Wed Dec 31 16:00:00 PST 1969
pZxid = 0x00000000000000
cversion = 0
dataVersion = 0
aclVersion = -1
ephemeralOwner = 0x00000000000000
dataLength = 0
----
/zookeeper/quota
cZxid = 0x00000000000000
ctime = Wed Dec 31 16:00:00 PST 1969
mZxid = 0x00000000000000
mtime = Wed Dec 31 16:00:00 PST 1969
pZxid = 0x00000000000000
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x00000000000000
dataLength = 0
----
Session Details (sid, timeout, ephemeralCount):
0x100004c57c30000, 30000, 0
----
Last zxid: 0x2
快照数据主要是为了快速恢复,事务日志文件是每次事务请求都会进行追加的操作,而快照是达到某种设定条件下的内存全量数据。所以通常快照数据是反应当时内存数据的状态。事务日志是更全面的数据,所以恢复数据的时候,可以先恢复快照数据,再通过增量恢复事务日志中的数据即可。
1 条评论
欢迎加入 Typecho 大家族