2021SC@SDUSC
前言
经过前文对DataTree中所用到的各种类的源码的分析后,我们再次回到DataTree类,深入了解DataTree究竟维护了哪些变量,实现了哪些功能。
静态变量
//日志
private static final Logger LOG = LoggerFactory.getLogger(DataTree.class);
//根节点路径
private static final String rootZookeeper = "/";
//管理和状态节点路径
private static final String procZookeeper = Quotas.procZookeeper;
//管理和状态节点路径的最后一段
private static final String procChildZookeeper = procZookeeper.substring(1);
//配额节点路径
private static final String quotaZookeeper = Quotas.quotaZookeeper;
//配额节点路径的最后一段
private static final String quotaChildZookeeper = quotaZookeeper.substring(procZookeeper.length() + 1);
//配置节点路径
private static final String configZookeeper = ZooDefs.CONFIG_NODE;
//配置节点路径的最后一段
private static final String configChildZookeeper = configZookeeper.substring(procZookeeper.length() + 1);
//记录Stat类里变量占的字节数,6个long,5个int,故计算如下
public static final int STAT_OVERHEAD_BYTES = (6 * 8) + (5 * 4);
//树的digest记录上限
public static final int DIGEST_LOG_LIMIT = 1024;
//zookeeper更新digestLog(见下方)的间隔,简而言之,需要每128个事务才更新一次
//这有利于服务器的对齐与比较
public static final int DIGEST_LOG_INTERVAL = 128;
实例变量
//日志
private final RateLogger RATE_LOGGER = new RateLogger(LOG, 15 * 60 * 1000);
//实现类是并发哈希表的简单封装
private final NodeHashMap nodes;
private IWatchManager dataWatches;
private IWatchManager childWatches;
//存储所有节点的路径及数据占的空间大小
private final AtomicLong nodeDataSize = new AtomicLong(0);
//记录配额节点的字典树
private final PathTrie pTrie = new PathTrie();
//用并发哈希表记录一段会话的临时节点的路径
private final Map<Long, HashSet<String>> ephemerals = new ConcurrentHashMap<Long, HashSet<String>>();
//用集合存储所有容器节点的路径
private final Set<String> containers = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
//用集合存储所有TTL节点的路径
private final Set<String> ttls = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
//该变量用于完成List<ACL>与Long互相转换,DataNode中acl是一个Long值,并不是ACL列表
private final ReferenceCountedACLCache aclCache = new ReferenceCountedACLCache();
//如果这不为空,zookeeper会积极寻找这个事务id digest对应的事务id
private ZxidDigest digestFromLoadedSnapshot;
//最新的事务的事务id的digest
private volatile ZxidDigest lastProcessedZxidDigest;
private boolean firstMismatchTxn = true;
//当digest不匹配事件储发时会被通知
private final List<DigestWatcher> digestWatchers = new ArrayList<>();
//用链表来记录一系列的事务id的digest
private LinkedList<ZxidDigest> digestLog = new LinkedList<>();
//digest计算器,内部规定了digest计算方式
private final DigestCalculator digestCalculator;
//dataTree的根节点
private DataNode root = new DataNode(new byte[0], -1L, new StatPersisted());
//根节点下的一个节点,路径为/zookeeper
private final DataNode procDataNode = new DataNode(new byte[0], -1L, new StatPersisted());
//dataTree的配额节点,路径为/zookeeper/quota
private final DataNode quotaDataNode = new DataNode(new byte[0], -1L, new StatPersisted());
//最新事务id
public volatile long lastProcessedZxid = 0;
总结
ZooKeeper服务端支持7种节点类型分别是:持久、持久顺序、临时、临时顺序、容器、持久 TTL、持久顺序TTL。
①持久、临时节点
持久是用的最多的一种类型也是默认的节点类型,临时节点相较于持久节点来说就是它会随着客户端会话结束而被删除,通常可以用在一些特定的场景,如分布式锁释放、健康检查等。
②持久顺序、临时顺序节点
持久顺序、临时顺序节点相对于持久、临时节点的特性就是zookeeper会自动在这两种节点之后增加一个数字的后缀,而路径 + 数字后缀是能保证唯一的,这数字后缀的应用场景可以实现诸如分布式队列,分布式公平锁等。
③容器节点
容器节点是 3.5 以后新增的节点类型,只要在调用create 方法时,指定CreateMode 为CONTAINER 即可创建容器的节点类型,容器节点的表现形式和持久节点是一样的,但是区别是zookeeper服务端启动后,会有一个单独的线程去扫描,所有的容器节点,当发现容器节点的子节点数量为 0 时,会自动删除该节点,除此之外和持久节点没有区别。 官方注释给出的使用场景是Container nodes are special purpose nodes useful for recipes such as leader, lock, etc. 说可以用在 leader 或者锁的场景中。
④持久TTL、持久顺序TTL节点
TTL 是time to live 的缩写,指带有存活时间,简单来说就是当该节点下面没有子节点的话,超过了TTL 指定时间后就会被自动删除,特性跟上面的容器节点很像,只是容器节点没有超时时间而已,但是TTL 启用是需要额外的配置,配置是zookeeper.extendedTypesEnabled 需要配置成true,否则的话创建TTL 时会收到Unimplemented 的报错。
??
|