1.首先是创建zookeeper实例
zk = new ZooKeeper("192.168.0.170:2181", 5000, new zkDemo());
下面是zk的构造函数
public ZooKeeper(String connectString, int sessionTimeout, Watcher watcher,
boolean canBeReadOnly)
throws IOException
{
LOG.info("Initiating client connection, connectString=" + connectString
+ " sessionTimeout=" + sessionTimeout + " watcher=" + watcher);
watchManager.defaultWatcher = watcher;
ConnectStringParser connectStringParser = new ConnectStringParser(
connectString);
HostProvider hostProvider = new StaticHostProvider(
connectStringParser.getServerAddresses());
cnxn = new ClientCnxn(connectStringParser.getChrootPath(),
hostProvider, sessionTimeout, this, watchManager,
getClientCnxnSocket(), canBeReadOnly);
cnxn.start();
}
最主要的是ClientCnxn这个类。其中参数中有getClientCnxnSocket()这个是整个client创建网络传输的地方。
private static ClientCnxnSocket getClientCnxnSocket() throws IOException {
String clientCnxnSocketName = System
.getProperty(ZOOKEEPER_CLIENT_CNXN_SOCKET);
if (clientCnxnSocketName == null) {
clientCnxnSocketName = ClientCnxnSocketNIO.class.getName();
}
try {
return (ClientCnxnSocket) Class.forName(clientCnxnSocketName)
.newInstance();
} catch (Exception e) {
IOException ioe = new IOException("Couldn't instantiate "
+ clientCnxnSocketName);
ioe.initCause(e);
throw ioe;
}
}
所以一般的话是通过ClientCnxnSocketNIO来创建实例。这个说明服务端是异步io服务器。
之后是 cnxn.start();启动客户端连接。
在启动啥的时候回创建两个线程,一个是sendthread(clientCnxn的内部类sendthread.class),另一个是eventThread。
其中先看sendthread的run方法。
public void run() {
clientCnxnSocket.introduce(this,sessionId);
clientCnxnSocket.updateNow();
clientCnxnSocket.updateLastSendAndHeard();
int to;
long lastPingRwServer = System.currentTimeMillis();
final int MAX_SEND_PING_INTERVAL = 10000; //10 seconds
while (state.isAlive()) {
try {
if (!clientCnxnSocket.isConnected()) {
if(!isFirstConnect){
try {
Thread.sleep(r.nextInt(1000));
} catch (InterruptedException e) {
LOG.warn("Unexpected exception", e);
}
}
// don't re-establish connection if we are closing
if (closing || !state.isAlive()) {
break;
}
startConnect();
clientCnxnSocket.updateLastSendAndHeard();
}
if (state.isConnected()) {
// determine whether we need to send an AuthFailed event.
if (zooKeeperSaslClient != null) {
boolean sendAuthEvent = false;
if (zooKeeperSaslClient.getSaslState() == ZooKeeperSaslClient.SaslState.INITIAL) {
try {
zooKeeperSaslClient.initialize(ClientCnxn.this);
} catch (SaslException e) {
LOG.error("SASL authentication with Zookeeper Quorum member failed: " + e);
state = States.AUTH_FAILED;
sendAuthEvent = true;
}
}
KeeperState authState = zooKeeperSaslClient.getKeeperState();
if (authState != null) {
if (authState == KeeperState.AuthFailed) {
// An authentication error occurred during authentication with the Zookeeper Server.
state = States.AUTH_FAILED;
sendAuthEvent = true;
} else {
if (authState == KeeperState.SaslAuthenticated) {
sendAuthEvent = true;
}
}
}
if (sendAuthEvent == true) {
eventThread.queueEvent(new WatchedEvent(
Watcher.Event.EventType.None,
authState,null));
}
}
to = readTimeout - clientCnxnSocket.getIdleRecv();
} else {
to = connectTimeout - clientCnxnSocket.getIdleRecv();
}
if (to <= 0) {
String warnInfo;
warnInfo = "Client session timed out, have not heard from server in "
+ clientCnxnSocket.getIdleRecv()
+ "ms"
+ " for sessionid 0x"
+ Long.toHexString(sessionId);
LOG.warn(warnInfo);
throw new SessionTimeoutException(warnInfo);
}
if (state.isConnected()) {
//1000(1 second) is to prevent race condition missing to send the second ping
//also make sure not to send too many pings when readTimeout is small
int timeToNextPing = readTimeout / 2 - clientCnxnSocket.getIdleSend() -
((clientCnxnSocket.getIdleSend() > 1000) ? 1000 : 0);
//send a ping request either time is due or no packet sent out within MAX_SEND_PING_INTERVAL
if (timeToNextPing <= 0 || clientCnxnSocket.getIdleSend() > MAX_SEND_PING_INTERVAL) {
sendPing();
clientCnxnSocket.updateLastSend();
} else {
if (timeToNextPing < to) {
to = timeToNextPing;
}
}
}
// If we are in read-only mode, seek for read/write server
if (state == States.CONNECTEDREADONLY) {
long now = System.currentTimeMillis();
int idlePingRwServer = (int) (now - lastPingRwServer);
if (idlePingRwServer >= pingRwTimeout) {
lastPingRwServer = now;
idlePingRwServer = 0;
pingRwTimeout =
Math.min(2*pingRwTimeout, maxPingRwTimeout);
pingRwServer();
}
to = Math.min(to, pingRwTimeout - idlePingRwServer);
}
clientCnxnSocket.doTransport(to, pendingQueue, outgoingQueue, ClientCnxn.this);
} catch (Throwable e) {
if (closing) {
if (LOG.isDebugEnabled()) {
// closing so this is expected
LOG.debug("An exception was thrown while closing send thread for session 0x"
+ Long.toHexString(getSessionId())
+ " : " + e.getMessage());
}
break;
} else {
// this is ugly, you have a better way speak up
if (e instanceof SessionExpiredException) {
LOG.info(e.getMessage() + ", closing socket connection");
} else if (e instanceof SessionTimeoutException) {
LOG.info(e.getMessage() + RETRY_CONN_MSG);
} else if (e instanceof EndOfStreamException) {
LOG.info(e.getMessage() + RETRY_CONN_MSG);
} else if (e instanceof RWServerFoundException) {
LOG.info(e.getMessage());
} else {
LOG.warn(
"Session 0x"
+ Long.toHexString(getSessionId())
+ " for server "
+ clientCnxnSocket.getRemoteSocketAddress()
+ ", unexpected error"
+ RETRY_CONN_MSG, e);
}
cleanup();
if (state.isAlive()) {
eventThread.queueEvent(new WatchedEvent(
Event.EventType.None,
Event.KeeperState.Disconnected,
null));
}
clientCnxnSocket.updateNow();
clientCnxnSocket.updateLastSendAndHeard();
}
}
}
cleanup();
clientCnxnSocket.close();
if (state.isAlive()) {
eventThread.queueEvent(new WatchedEvent(Event.EventType.None,
Event.KeeperState.Disconnected, null));
}
ZooTrace.logTraceMessage(LOG, ZooTrace.getTextTraceLevel(),
"SendThread exited loop for session: 0x"
+ Long.toHexString(getSessionId()));
}
其中 clientCnxnSocket.doTransport(to, pendingQueue, outgoingQueue, ClientCnxn.this);为数据传输的关键点。
pendingQueue和outgoingQueue是clientCnxn类的两个全局变量。内部类sendthread.class和eventthread.class都是通过这两个queue来交换数据的。
回到clientCnxnSocket.doTransport(to, pendingQueue, outgoingQueue, ClientCnxn.this)
这个其实是调用clientCnxnSocket的子类ClientCnxnSocketNIO的方法。
sockKey.isReadable()是获取服务器处理完的数据,接下来调用了 SendThread.readResponse(**)
方法里面调用了
eventThread.queueEvent( we );把服务器返回的数据构造成一个watcherevent给eventThread的queue,方便eventThread的异步调用。
public void queueEvent(WatchedEvent event) {
if (event.getType() == EventType.None
&& sessionState == event.getState()) {
return;
}
sessionState = event.getState();
// materialize the watchers based on the event
WatcherSetEventPair pair = new WatcherSetEventPair(
watcher.materialize(event.getState(), event.getType(),
event.getPath()),
event);
// queue the pair (watch set & event) for later processing
waitingEvents.add(pair);
}
上述代码主要调用了new WatcherSetEventPair( watcher.materialize(event.getState(), event.getType(), event.getPath()), event)构造一个watcher和watchedevent的对象,将这个对象add到queue里面面给eventThread线程不断处理event。
其中watcher.materialize(event.getState(), event.getType(), event.getPath())调用的是zookeeper.java的内部静态类
private static class ZKWatchManager implements ClientWatchManager
这个类是zk的session范围内的watcher管理器。通过这个类来维护不同事件的watcher集合。
所以在sendThread每次获取到response的时候都会 通过这个wathermanager获取znode注册好的watcher(set<watcher>),然后封装成WatcherSetEventPair放到eventqueue中,然后异步执行。
example:
public class zkDemo implements Watcher { private static final Logger LOG; static { //Keep these two lines together to keep the initialization order explicit LOG = LoggerFactory.getLogger(ZooKeeper.class); } private static final CountDownLatch cdl = new CountDownLatch(1); static ZooKeeper zk; public static void main(String[] args) throws IOException { zk = new ZooKeeper("192.168.0.170:2181", 20000, new zkDemo()); //zk的连接状态 System.out.println(zk.getState()); Stat st = new Stat(); try { cdl.await(); } catch (Exception e) { System.out.println("ZK Session established."); } String path1 = null; try { Stat stat; if((stat = zk.exists( "/zk-test-abz",new zkDemo() )) != null){ zk.setData( "/zk-test-abz","567".getBytes(),stat.getVersion() ); } //四个参数 znode路径 data,acl,节点模式(瞬息还是持久还是带自增) // zk.close(); // ZooKeeper zk1 = new ZooKeeper("192.168.0.170:2181", 20000, new zkDemo()); // System.out.println(new String(zk.getData(path1, true, st))); // System.out.println( st.getCzxid() +"," +st.getAversion() ); // String path2 = zk.create("/zk-test-", "456".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL); // System.out.println(new String(zk.getData(path2, true, st))); // System.out.println( st.getCzxid() +"," +st.getAversion() ); } catch (KeeperException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } try { Thread.sleep( 5000L ); } catch (InterruptedException e) { e.printStackTrace(); } } //监听到事件时进行处理 public void process(WatchedEvent event) { LOG.info( "事件的路径为:" +event.getPath() ); if (Watcher.Event.KeeperState.SyncConnected == event.getState()) { cdl.countDown(); } if (event.getType() == Event.EventType.NodeCreated) { System.out.println( event.getPath() + "创建了"); } if (event.getType() == Event.EventType.NodeDataChanged) { System.out.println( event.getPath() + "数据改变了"); } } }