整个系统中分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<String> getChildren(finalString path, Watcher watcher)
publicList<String> getChildren(String path, booleanwatch)
2.watcher的使用注意
下面几个点需要在使用的时候多加注意
###获取的时候把watcher重新加一次 List<String> 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); } }
因为网络环境等原因,这两个异常还是会时不时出现一下,需要对这些情况有所考虑。