ARTEMIS-497 Prevent 10 second stalls when closing an SSL connection

When NettyConnection.classSSLAndChannel is called from the EventLoop,
waiting for the SSL handler to close will always take 10 seconds, because
the sslCloseFuture is from a task that is scheduled with the same
EventLoop. But since the EventLoop is a single threaded executor, it
will only be executed after the current task is completed.

Due to the single threaded nature of the EventLoop, all blocking calls
should be avoided. Therefore, I removed both awaitUninterruptibly calls
if the closing happens within an event loop tasks. As a side effect,
the annoying server log timeout warnings will go away.
This commit is contained in:
Bernd Gutjahr 2016-04-22 08:22:41 +02:00 committed by Martyn Taylor
parent 224e049696
commit 971a0a13bd
1 changed files with 17 additions and 9 deletions

View File

@ -29,6 +29,8 @@ import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelPromise; import io.netty.channel.ChannelPromise;
import io.netty.channel.EventLoop; import io.netty.channel.EventLoop;
import io.netty.handler.ssl.SslHandler; import io.netty.handler.ssl.SslHandler;
import io.netty.util.concurrent.GenericFutureListener;
import org.apache.activemq.artemis.api.core.ActiveMQBuffer; import org.apache.activemq.artemis.api.core.ActiveMQBuffer;
import org.apache.activemq.artemis.api.core.ActiveMQBuffers; import org.apache.activemq.artemis.api.core.ActiveMQBuffers;
import org.apache.activemq.artemis.api.core.ActiveMQInterruptedException; import org.apache.activemq.artemis.api.core.ActiveMQInterruptedException;
@ -193,13 +195,13 @@ public class NettyConnection implements Connection {
boolean inEventLoop = eventLoop.inEventLoop(); boolean inEventLoop = eventLoop.inEventLoop();
//if we are in an event loop we need to close the channel after the writes have finished //if we are in an event loop we need to close the channel after the writes have finished
if (!inEventLoop) { if (!inEventLoop) {
closeSSLAndChannel(sslHandler, channel); closeSSLAndChannel(sslHandler, channel, false);
} }
else { else {
eventLoop.execute(new Runnable() { eventLoop.execute(new Runnable() {
@Override @Override
public void run() { public void run() {
closeSSLAndChannel(sslHandler, channel); closeSSLAndChannel(sslHandler, channel, true);
} }
}); });
} }
@ -412,12 +414,17 @@ public class NettyConnection implements Connection {
// Private ------------------------------------------------------- // Private -------------------------------------------------------
private void closeSSLAndChannel(SslHandler sslHandler, Channel channel) { private void closeSSLAndChannel(SslHandler sslHandler, final Channel channel, boolean inEventLoop) {
if (sslHandler != null) { if (sslHandler != null) {
try { try {
ChannelFuture sslCloseFuture = sslHandler.close(); ChannelFuture sslCloseFuture = sslHandler.close();
sslCloseFuture.addListener(new GenericFutureListener<ChannelFuture>() {
if (!sslCloseFuture.awaitUninterruptibly(10000)) { @Override
public void operationComplete(ChannelFuture future) throws Exception {
channel.close();
}
});
if (!inEventLoop && !sslCloseFuture.awaitUninterruptibly(10000)) {
ActiveMQClientLogger.LOGGER.timeoutClosingSSL(); ActiveMQClientLogger.LOGGER.timeoutClosingSSL();
} }
} }
@ -425,10 +432,11 @@ public class NettyConnection implements Connection {
// ignore // ignore
} }
} }
else {
ChannelFuture closeFuture = channel.close(); ChannelFuture closeFuture = channel.close();
if (!closeFuture.awaitUninterruptibly(10000)) { if (!inEventLoop && !closeFuture.awaitUninterruptibly(10000)) {
ActiveMQClientLogger.LOGGER.timeoutClosingNettyChannel(); ActiveMQClientLogger.LOGGER.timeoutClosingNettyChannel();
}
} }
} }
// Inner classes ------------------------------------------------- // Inner classes -------------------------------------------------