wjy0 2019-07-01
具体将在后续的文章中给出具体的应用介绍。
在介绍数据节点之前,先给大家说明一下zk中事务的概念。zk中的事务指的是能够改变zk服务器状态的操作,包括
zk中还有一个事务id的概念,会为每次事务请求分配一个全局唯一的id,关于事务id的特点简要说明如下:
zk中的数据节点是有生命周期的概念的,根据生命周期的不同可以划分为两种类型的节点
最常见的一种节点类型,也叫做regular节点。数据节点一旦被创建,就会一直存在于zk服务器上,直到有删除事务操作主动清除该节点
临时节点的生命周期是和客户端会话绑定在一起的,只要客户端会话失效,那么临时节点就会被清除。这里的客户端会话失效和客户端tcp连接断开是不一样的。
临时节点只能是leaf节点
zk会给创建的节点自动加上一个数字后缀,作为一个完整的节点名称,这种方式可以维护节点之间的一种顺序性
数据节点的类结构代码如下:
public class DataNode implements Record { byte data[]; //节点上存储的数据 Long acl; //权限控制列表版本号 public StatPersisted stat; //节点状态信息 private Set<String> children = null; //节点的子节点列表 }
StatPersisted stat是该节点的状态信息类,类结构代码如下:
public class StatPersisted implements Record { private long czxid; //创建该节点的事务id private long mzxid; //最后一次修改节点的事务id private long ctime; //节点的创建时间 private long mtime; //最后一次更新节点内容的时间 private int version; //数据节点的版本号,version=0表示该节点被创建之后,更新过0次,在实现乐观锁的时候有用 private int cversion; //子节点的版本号,只有当子节点列表被更改后,才会+1 private int aversion; //子节点的acl列表版本号 private long ephemeralOwner; //如果是临时节点,该值表示创建该节点的临时会话的sessionID,如果是持久节点的话,该值为0 private long pzxid; //子节点列表最后一次被修改时的事务id,一定是子节点列表被修改,子节点的内容被修改是不会改变这个属性的值的 }
zk中的事件监听机制由三部分组成:
客户端线程在zk服务器上注册事件监听之后,会将这个watcher对象存储在WatcherManager中,当zk服务器触发这个事件之后,会向客户端发送通知,客户端线程会从WatcherManager中取出对应的Watcher对象来执行回调逻辑。
public interface Watcher{ public interface Event{ public enum KeeperState{ Disconnected(0), //断开连接 SyncConnected(3), //此时客户端与zk服务器处于连接状态 AuthFailed(4), //权限检查失败 ConnectedReadOnly(5) //客户端和一台只读server处于连接状态,what is readonly server? 就是只和集群中的一小部分服务器处于连接的服务器 Expired(-122) //会话超时 } public enum EventType{ None (-1), NodeCreated (1), //watcher监听的数据节点被创建 NodeDeleted (2), //watcher监听的数据节点被删除 NodeDataChanged (3), //watcher监听的数据节点内容被更新 NodeChildrenChanged (4), //watcher监听的数据节点的子节点列表被更新了 DataWatchRemoved (5), ChildWatchRemoved (6); } } public enum WatcherType{ Children (1), Data (2), Any (3); } abstract public void process(WatchedEvent event); }
主要有以下几个部分组成
在处理监听事件的回调函数process中,参数event是一个org.apache.zookeeper.WatchedEvent类型的参数。WatchedEvent说明如下
public class WatchedEvent { final private KeeperState keeperState; //通知的状态 final private EventType eventType; //事件类型 private String path; //参与该事件的节点路径 /** *实现WatchedEvent到WatcherEvent的转换 */ public WatcherEvent getWrapper() { return new WatcherEvent(eventType.getIntValue(), keeperState.getIntValue(), path); } /** *实现WatcherEvent到WatchedEvent的转换 */ public WatchedEvent(WatcherEvent eventMessage) { keeperState = KeeperState.fromInt(eventMessage.getState()); eventType = EventType.fromInt(eventMessage.getType()); path = eventMessage.getPath(); } }
从WatchedEvent的代码中可以看出一个监听事件由三个部分组成的:
并且,与WatchedEvent对应的还有一个类WatcherEvent,这个WatcherEvent和WatchedEvent相比,只是实现了序列化,方便通过网络在服务端和客户端之间传输。
序列化使用的是jute
客户端接受到的服务器传送过来的监听事件的格式如下:
KeeperState:SyncConnected EventType:NodeDataChanged Path: /zk-book
显然,从接收到的事件中,客户端是无法获取到该节点在更新前后的数据的,因此客户端是要主动再向服务器获取数据。因此,使用zk的监听机制实现的发布订阅系统使用的"推拉"结合的方式。
ZooKeeper支持某些特定的四字命令字母与其的交互。它们大多是查询命令,用来获取ZooKeeper服务的当前状态及相关信息。用户在客户端可以通过telnet或nc向ZooKeeper提交相应的命令