2021SC@SDUSC
DataTree函数方法
cachedApproximateDataSize:
//返回所有DataNode的路径和数据所占的空间大小
public long cachedApproximateDataSize() {
return nodeDataSize.get();
}
isSpecialPath:
//判断是否是特殊路径(/,/zookeeper,/zookeeper/config,/zookeeper/quota)
boolean isSpecialPath(String path) {
return rootZookeeper.equals(path)
|| procZookeeper.equals(path)
|| quotaZookeeper.equals(path)
|| configZookeeper.equals(path);
}
copyStatPersisted,copyStat:
//一个静态的工具方法,使得to存储的实例变量与from存储的实例变量完全一致
//即实现StatPersisted类型的拷贝
public static void copyStatPersisted(StatPersisted from, StatPersisted to) {
to.setAversion(from.getAversion());
to.setCtime(from.getCtime());
to.setCversion(from.getCversion());
to.setCzxid(from.getCzxid());
to.setMtime(from.getMtime());
to.setMzxid(from.getMzxid());
to.setPzxid(from.getPzxid());
to.setVersion(from.getVersion());
to.setEphemeralOwner(from.getEphemeralOwner());
}
//同上,是一个静态的工具方法,使得to存储的实例变量与from存储的实例变量完全一致
//即实现Stat类型的拷贝
public static void copyStat(Stat from, Stat to) {
to.setAversion(from.getAversion());
to.setCtime(from.getCtime());
to.setCversion(from.getCversion());
to.setCzxid(from.getCzxid());
to.setMtime(from.getMtime());
to.setMzxid(from.getMzxid());
to.setPzxid(from.getPzxid());
to.setVersion(from.getVersion());
to.setEphemeralOwner(from.getEphemeralOwner());
to.setDataLength(from.getDataLength());
to.setNumChildren(from.getNumChildren());
}
updateCountBytes:
public void updateCountBytes(String lastPrefix, long bytesDiff, int countDiff) {
//获取对应的Stat节点路径,通过路径由哈希表获取节点对象
String statNode = Quotas.statPath(lastPrefix);
DataNode node = nodes.get(statNode);
StatsTrack updatedStat = null;
if (node == null) {
//Stat节点不存在,报错
LOG.error("Missing count node for stat {}", statNode);
return;
}
synchronized (node) {
updatedStat = new StatsTrack(new String(node.data));
updatedStat.setCount(updatedStat.getCount() + countDiff);
updatedStat.setBytes(updatedStat.getBytes() + bytesDiff);
node.data = updatedStat.toString().getBytes();
}
//获取对应的limit节点路径,通过路径由哈希表获取节点对象
String quotaNode = Quotas.quotaPath(lastPrefix);
node = nodes.get(quotaNode);
StatsTrack thisStats = null;
if (node == null) {
//limit节点不存在,报错
LOG.error("Missing count node for quota {}", quotaNode);
return;
}
synchronized (node) {
thisStats = new StatsTrack(new String(node.data));
}
if (thisStats.getCount() > -1 && (thisStats.getCount() < updatedStat.getCount())) {
LOG.warn(
"Quota exceeded: {} count={} limit={}",
lastPrefix,
updatedStat.getCount(),
thisStats.getCount());
}
if (thisStats.getBytes() > -1 && (thisStats.getBytes() < updatedStat.getBytes())) {
LOG.warn(
"Quota exceeded: {} bytes={} limit={}",
lastPrefix,
updatedStat.getBytes(),
thisStats.getBytes());
}
}
createNode:
//给DataTree创建新节点,调用重载函数,outputStat参数为null
public void createNode(final String path, byte[] data, List<ACL> acl, long ephemeralOwner, int parentCVersion, long zxid, long time) throws NoNodeException, NodeExistsException {
createNode(path, data, acl, ephemeralOwner, parentCVersion, zxid, time, null);
}
//创建节点,参数依次为
//path:节点路径;data:节点存储的数据;acl:节点的acl值;
//ephemeralOwner:节点所属的会话id,-1表示不是临时节点;zxid:事务id;
//time:创建时间;outputStat:存储Stat输出结果
public void createNode(final String path, byte[] data, List<ACL> acl, long ephemeralOwner, int parentCVersion, long zxid, long time, Stat outputStat) throws KeeperException.NoNodeException, KeeperException.NodeExistsException {
//通过获取最后一个斜杆的下标,将path分割为parentName和childName
int lastSlash = path.lastIndexOf('/');
String parentName = path.substring(0, lastSlash);
String childName = path.substring(lastSlash + 1);
//用事务id,创建时间,所属会话id三个参数创建StatPersisted对象
StatPersisted stat = createStat(zxid, time, ephemeralOwner);
//获取我们要创建的节点的父节点对象
DataNode parent = nodes.get(parentName);
//如果没有父节点,抛出NoNodeException
if (parent == null) {
throw new KeeperException.NoNodeException();
}
synchronized (parent) {
//首先将ACL添加到ACL缓存中,以避免在模糊快照同步过程中ACL不被创建竞争条件。
// 这是最简单的修复方法,如果模糊快照的ACL映射中已经计入了ACL引用计数,则可能会再次添加ACL引用计数,deleteNode txn也可能发生这种情况,但至少不会导致ACL不存在问题。
// 稍后我们可以在从磁盘加载快照时从 ACL 映射中审计和删除所有未引用的 ACL
Long longval = aclCache.convertAcls(acl);
Set<String> children = parent.getChildren();
if (children.contains(childName)) {
throw new KeeperException.NodeExistsException();
}
nodes.preChange(parentName, parent);
if (parentCVersion == -1) {
parentCVersion = parent.stat.getCversion();
parentCVersion++;
}
//有可能我们会在模糊范围内重放创建然后删除的节点的txns,并且它不存在于快照中,
// 因此重放创建可能会恢复cversion和pzxid,需要检查并且只有在它出现时才更新更大。
if (parentCVersion > parent.stat.getCversion()) {
parent.stat.setCversion(parentCVersion);
parent.stat.setPzxid(zxid);
}
DataNode child = new DataNode(data, longval, stat);
parent.addChild(childName);
nodes.postChange(parentName, parent);
nodeDataSize.addAndGet(getNodeSize(path, child.data));
nodes.put(path, child);
EphemeralType ephemeralType = EphemeralType.get(ephemeralOwner);
if (ephemeralType == EphemeralType.CONTAINER) {
containers.add(path);
} else if (ephemeralType == EphemeralType.TTL) {
ttls.add(path);
} else if (ephemeralOwner != 0) {
HashSet<String> list = ephemerals.get(ephemeralOwner);
if (list == null) {
list = new HashSet<String>();
ephemerals.put(ephemeralOwner, list);
}
synchronized (list) {
list.add(path);
}
}
if (outputStat != null) {
child.copyStat(outputStat);
}
}
//检查它是不是zookeeper节点的一个子节点
if (parentName.startsWith(quotaZookeeper)) {
//检查它是不是limit节点
if (Quotas.limitNode.equals(childName)) {
//这是limit节点
//获取parentName中‘/zookeeper/quota’后面的部分,加入字典树(我们前文说过zookeeper用字典树记录配额节点)
pTrie.addPath(parentName.substring(quotaZookeeper.length()));
}
if (Quotas.statNode.equals(childName)) {
updateQuotaForPath(parentName.substring(quotaZookeeper.length()));
}
}
//检查是否更新节点配额
String lastPrefix = getMaxPrefixWithQuota(path);
long bytes = data == null ? 0 : data.length;
if (lastPrefix != null) {
updateCountBytes(lastPrefix, bytes, 1);
}
updateWriteStat(path, bytes);
dataWatches.triggerWatch(path, Event.EventType.NodeCreated);
childWatches.triggerWatch(parentName.equals("") ? "/" : parentName, Event.EventType.NodeChildrenChanged);
}
deleteNode:
public void deleteNode(String path, long zxid) throws KeeperException.NoNodeException {
int lastSlash = path.lastIndexOf('/');
String parentName = path.substring(0, lastSlash);
String childName = path.substring(lastSlash + 1);
//在拍摄模糊快照期间,孩子可能已经被删除,但我们仍然需要在这里更新 pzxid,然后再抛出异常,因为没有这样的孩子
DataNode parent = nodes.get(parentName);
if (parent == null) {
throw new KeeperException.NoNodeException();
}
synchronized (parent) {
nodes.preChange(parentName, parent);
parent.removeChild(childName);
//仅当 zxid 大于当前 pzxid 时才更新 pzxid,否则我们可能会覆盖一些由 create Txn 设置的更高的 pzxid,这可能导致 cversion 和 pzxid 不一致
if (zxid > parent.stat.getPzxid()) {
parent.stat.setPzxid(zxid);
}
nodes.postChange(parentName, parent);
}
DataNode node = nodes.get(path);
if (node == null) {
throw new KeeperException.NoNodeException();
}
nodes.remove(path);
synchronized (node) {
aclCache.removeUsage(node.acl);
nodeDataSize.addAndGet(-getNodeSize(path, node.data));
}
//同步容器和 ttls 修改
synchronized (parent) {
long eowner = node.stat.getEphemeralOwner();
EphemeralType ephemeralType = EphemeralType.get(eowner);
if (ephemeralType == EphemeralType.CONTAINER) {
containers.remove(path);
} else if (ephemeralType == EphemeralType.TTL) {
ttls.remove(path);
} else if (eowner != 0) {
Set<String> nodes = ephemerals.get(eowner);
if (nodes != null) {
synchronized (nodes) {
nodes.remove(path);
}
}
}
}
if (parentName.startsWith(procZookeeper) && Quotas.limitNode.equals(childName)) {
//删除字典树中的该节点
//更新这棵字典树
pTrie.deletePath(parentName.substring(quotaZookeeper.length()));
}
//检查是否更新该节点的配额
String lastPrefix = getMaxPrefixWithQuota(path);
if (lastPrefix != null) {
int bytes = 0;
synchronized (node) {
bytes = (node.data == null ? 0 : -(node.data.length));
}
updateCountBytes(lastPrefix, bytes, -1);
}
updateWriteStat(path, 0L);
if (LOG.isTraceEnabled()) {
ZooTrace.logTraceMessage(
LOG,
ZooTrace.EVENT_DELIVERY_TRACE_MASK,
"dataWatches.triggerWatch " + path);
ZooTrace.logTraceMessage(
LOG,
ZooTrace.EVENT_DELIVERY_TRACE_MASK,
"childWatches.triggerWatch " + parentName);
}
WatcherOrBitSet processed = dataWatches.triggerWatch(path, EventType.NodeDeleted);
childWatches.triggerWatch(path, EventType.NodeDeleted, processed);
childWatches.triggerWatch("".equals(parentName) ? "/" : parentName, EventType.NodeChildrenChanged);
}