整个系统中分master和server两种角色,每个master、server都需要知道集群内当前可用的server列表。
原处理方式:master负责集群server信息的收集和分发。server启动后和master建立长连接并心跳检测,master收集server的新增和disconnect,并将收集到的信息分发给所有server。
原存在问题:master和server耦合较重;master比较难扩展。
每个server对应一个Zookeeper的一个子节点,所有master和push通过获取所有子节点取得server列表,当server disconnect时Zookeeper通知所有机器重新获取Server列表。
根节点路劲:/service/serverlist
1.初始化Zookeeper连接
publicstatic void initZk() {
try{
#####初始化,如果当前有alive的zk连接就不初始化了
if(zk == null|| !zk.getState().isAlive()) {
synchronized(obj) {
if(zk == null|| !zk.getState().isAlive()) {
###把之前的close掉
closeZk();
PropertyReader p = newPropertyReader(ZK_PROPERTIES_FILE);
String zkServerStr = p.getValueAsString("zookeeper.server", ZK_SERVER_STR_DEFAULT);
intsessionTimeout = p.getValueAsInt("zookeeper.sessionTimeout", ZK_SESSION_TIMEOUT);
###重新建立连接
zk = newZooKeeper(zkServerStr, sessionTimeout,newDefaultWatcher());
}
}
}
}catch(Exception e) {
thrownew RuntimeException("Zookeeper Init Error!", e);
}
}
2.server在启动时添加代表自己的临时节点并watch其父节点
####如果之前myNodeName已经存在,则删除
ZkUtils.deleteNode(myNodeName);
####先看看父节点有没有,没有就创建下,父节点得是持久的
ZkUtils.createNode(rootNode,CreateMode.PERSISTENT);
####添加对父节点child变动的watcher
ZkUtils.addChildWatch(rootNode,ServerListWatcher.getInstance());
###创建自己的节点mynode
ZkUtils.createNode(myNodeName,CreateMode.EPHEMERAL);
3.master在启动时watch父节点
####先看看父节点有没有,没有就创建下,父节点得是持久的
ZkUtils.createNode(rootNode,CreateMode.PERSISTENT);
####添加对父节点child变动的watcher
ZkUtils.addChildWatch(rootNode,ServerListWatcher.getInstance());
4.监听该父节点的child变化并处理
publicvoid process(WatchedEvent event) {
try{
###如果是expired,需要重新初始化一遍
if(event.getState() == KeeperState.Expired) {
while(true) {
try{
// 重新初始化zk连接
ZkUtils.initZk();
// 重新初始化相关zk节点
PushZkUtils.initPushZkNodes();
// 获取nodeList
getAndSaveNodeList();
// 如果成功,则break;
break;
}catch(RuntimeException e) {
AsyncLogger.getInstance().info(LogManager.SERVICE_LOG,"ServerListWatcher:Init Error!");
// 5s后重新执行
Thread.sleep(5* 1000);
}
}
}
###如果是断开重连,需要重新获取下list,以免丢失事件
if(event.getState() == KeeperState.SyncConnected) {
getAndSaveNodeList();
}
###如果是child变化,及PushServer列表有变,则获取列表,保存本地
if(event.getType() == EventType.NodeChildrenChanged) {
AsyncLogger.getInstance().info(LogManager.SERVICE_LOG,"ServerListWatcher:Node Child Changed Event Received");
// 保存到本地,等待同步线程检查
getAndSaveNodeList();
}
}catch(Exception e) {
}
}
1.watcher分类
watcher主要有以下添加方式,不同添加方式监控不同的动作。( 设置watcher时,如果对应服务端已经不存在node,watcher是不会留在服务端)
###获取节点数据,第一个方法中watch=true代表使用zookeeper创建时传入的默认watcher,监控动作sets data on the node, or deletes the node.
publicbyte[] getData(String path, booleanwatch, Stat stat)
publicbyte[] getData(finalString path, Watcher watcher, Stat stat)
###判断节点是否存在,第一个方法中watch=true代表使用zookeeper创建时传入的默认watcher,监控动作creates/delete the node or sets the data on the node.
publicStat exists(finalString path, Watcher watcher)
publicStat exists(String path, booleanwatch)
###获取节点child,第一个方法中watch=true代表使用zookeeper创建时传入的默认watcher,监控动作deletes the node of the given path or creates/delete a child under the node
publicList getChildren(finalString path, Watcher watcher)
publicList getChildren(String path, booleanwatch)
2.watcher的使用注意
下面几个点需要在使用的时候多加注意
###获取的时候把watcher重新加一次
List nodes = ZkUtils.getChildren(ZkUtils.ZK_PUSH_SERVER_LIST_ROOT_NODE,ServerListWatcher.getInstance());
###如果是断开重连,需要重新获取下list,以免丢失事件
if(event.getState() == KeeperState.SyncConnected) {
getAndSaveNodeList();
}
if(event.getState() == KeeperState.Expired) {
while(true) {
try{
// 重新初始化zk连接
ZkUtils.initZk();
// 重新初始化相关zk节点
PushZkUtils.initPushZkNodes();
// 获取nodeList
getAndSaveNodeList();
// 如果成功,则break;
break;
}catch(RuntimeException e) {
AsyncLogger.getInstance().info(LogManager.SERVICE_LOG,"ServerListWatcher:Init Error!");
// 5s后重新执行
Thread.sleep(5*1000);
}
}
因为网络环境等原因,这两个异常还是会时不时出现一下,需要对这些情况有所考虑。