Session什么时候创建
在ZooKeeper中,客户端和服务端建立连接后会创建一个session(会话),每一个session对应一个全局唯一的会话ID(Session ID)。就像浏览器和Web服务器一样,首次连接Web服务器的时候为了跟踪客户端,Web服务器会创建一个Session。
Session超时
zookeeper服务器和客户端之间维持的是一个长连接,客户端会定时向服务器发送心跳来维持Session的有效性(每次发送心跳就会刷新session超时时间)。正常情况下Session一直有效,并且ZK集群所有机器上都保存这个Session信息。当出现网络中断或zookeeper服务挂掉时,客户端会主动在地址列表(实例化ZK对象的时候传入构造方法的那个参数connectString)中选择新的地址进行连接(如果没有备选地址,会不断重试之前的那个地址),如果有新的地址可以连接,在中断时长小于sessionTimeout值时,zookeeper客户端自动重连。如果中断时长大于sessionTimeout值时,将出现session expired的异常,这个时候就需要开发者手动做一些处理,比如使用zookeeper的create重建连接。
如何监听Session超时事件
在创建zookeeper对象时,指定的Watcher是zookeeper客户端默认的Watcher,可以在它的process方法中监听到Session超时事件,具体做法:
/**
* 当type=None(-1)时,有四种情况:
* 1. zookeeper已连接
* 2. 会话超时!
* 3. zookeeper连接已关闭
* 4. zookeeper认证失败
* @param watchedEvent zookeeper event
*/
@Override
public void process(WatchedEvent watchedEvent) {
Event.EventType type = watchedEvent.getType();
Event.KeeperState state = watchedEvent.getState();
int stateValue = state.getIntValue();
logger.info("监听到变化,type=" + type.getIntValue() + ",name=" + type.name());
logger.info("state=" + state.getIntValue() + ",name=" + state.name());
if (type.getIntValue() == Event.EventType.None.getIntValue()) {
if (stateValue == Event.KeeperState.SyncConnected.getIntValue()) {
logger.info("zookeeper已连接!");
countDownLatch.countDown();
}else if (stateValue == Watcher.Event.KeeperState.Expired.getIntValue()) {
logger.info("zookeeper会话超时!");
//zookeeper连接是异步的,所以用CountDownLatch保证zookeeper连接成功后再通知Listeners
countDownLatch = new CountDownLatch(1);
ZooKeeper zooKeeper = createNewZookeeper();
try {
countDownLatch.await();
}
catch (InterruptedException e) {
e.printStackTrace();
}
noticeListener(zooKeeper);
}else if (stateValue == Watcher.Event.KeeperState.Closed.getIntValue()) {
logger.info("zookeeper连接已关闭!");
}else if (stateValue == Watcher.Event.KeeperState.AuthFailed.getIntValue()) {
logger.info("zookeeper认证失败!");
}
}
Seession超时后如何重建连接,需要注意什么
重新new 一个zookeeper即可,使用zookeeper提供的API,如下:
public ZooKeeper(String connectString, int sessionTimeout, Watcher watcher) throws IOException{
this(connectString, sessionTimeout, watcher, false);
}
但是需要注意session超时之后,之前使用Java API注册的Watcher将丢失,比如:
public void addWatch(String basePath, AddWatchMode mode)
throws KeeperException, InterruptedException {
addWatch(basePath, watchManager.defaultWatcher, mode);
}
所以在重建连接之后,你需要重新添加监听。