Hi Chris, > > On 6/11/13 1:05 AM, ruxing bao wrote: > > Sorry,I can't get any more of the stack trace. > > > > We wrapped zookeepr client as a spring bean and invoked method > > "close" of zookeeper in "destory-method" of bean,in that method > > "close",zookeeper Send Thread was closed. When tomcat was shut > > down, Spring closed it's container and bean was destroyed, then > > "destroy-method" of bean was invoked. I'am confusing why is Send > > Thread of Zookeeper seemed to exit slower than the > > WebappClassLoader shedding its loaded classes. > > > Threads don't always shut down immediately... let's see how you are > telling the thread to shut down: > > > ps: details of zookeeper closing > > (Thanks!) > > > org.apache.zookeeper.Zookeeper public synchronized void close() > > throws InterruptedException { if (!cnxn.getState().isAlive()) { if > > (LOG.isDebugEnabled()) { LOG.debug("Close called on already closed > > client"); } return; } if (LOG.isDebugEnabled()) { > > LOG.debug("Closing session: 0x" + > > Long.toHexString(getSessionId())); } try { cnxn.close(); } catch > > (IOException e) { if (LOG.isDebugEnabled()) { LOG.debug("Ignoring > > unexpected exception during close", e); } } LOG.info("Session: 0x" > > + Long.toHexString(getSessionId()) + " closed"); } > > So, during webapp shutdown you should be getting the message "Closing > session 0x[...]", right? No errors? Yes, no errors.
> Is "cnxn" of type org.apache.zookeeped.ClientCnxn? That seems to be > the case, but it's worth checking. > > > ---------------------------------------------------------------------------------------------------------------------- > > > > > org.apache.zookeeper.ClientCnxn > > public void close() throws IOException { if (LOG.isDebugEnabled()) > > { LOG.debug("Closing client for session: 0x" + > > Long.toHexString(getSessionId())); } try { RequestHeader h = new > > RequestHeader(); h.setType(ZooDefs.OpCode.closeSession); > > submitRequest(h, null, null, null); } catch (InterruptedException > > e) { // ignore, close the send/event threads } finally { > > disconnect(); } } > > > > public void disconnect() { if (LOG.isDebugEnabled()) { > > LOG.debug("Disconnecting client for session: 0x" + > > Long.toHexString(getSessionId())); } sendThread.close(); > > eventThread.queueEventOfDeath(); } > > Okay, so calling "close" asks the thread to stop (somewhat indirectly). > > > org.apache.zookeeper.ClientCnxn.SendThread void close() { state = > > States.CLOSED; clientCnxnSocket.wakeupCnxn(); } > > > > > > @Override public void run() { > > clientCnxnSocket.introduce(this,sessionId); > > clientCnxnSocket.updateNow(); > > clientCnxnSocket.updateLastSendAndHeard(); int to; long > > lastPingRwServer = System.currentTimeMillis(); 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) { throw new SessionTimeoutException( "Client session > > timed out, have not heard from server in " + > > clientCnxnSocket.getIdleRecv() + "ms" + " for sessionid 0x" + > > Long.toHexString(sessionId)); } if (state.isConnected()) { int > > timeToNextPing = readTimeout / 2 - clientCnxnSocket.getIdleSend(); > > if (timeToNextPing <= 0) { 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 exitedloop.");//at this line, error occurs. } > > It seems you have a race condition, here: you are asking the thread to > stop (indirectly, by setting state=CLOSED). But, the thread requesting > the close continues running without checking to see what happens with > the SendThread. The SendThread has a Thread.sleep() call which means > that it might be waiting for a while before exiting its run() method. > > Can you modify the code somewhere to: > > a) Call SendThread.interrupt() > > and > > b) Do a short loop waiting for SendThread.getState() == TERMINATED > > I think if you do these two things, you'll have a clean shutdown. > I would try this. thanks, Bob