Reduce calls to System.nanoTime() and object creation in IdleStateHandler. Related to [#3808]
Motivation: Calling System.nanoTime() for each channelRead(...) is very expensive. See [#3808] for more detailed description. Also we always do extra work for each write and read even if read or write idle states should not be handled. Modifications: - Move System.nanoTime() call to channelReadComplete(...). - Reuse ChannelFutureListener for writes - Only add ChannelFutureListener to writes if write and all idle states should be handled. - Only call System.nanoTime() for reads if idle state events for read and all states should be handled. Result: Less overhead when using the IdleStateHandler.
This commit is contained in:
parent
6fce3b79c3
commit
bac2e3a6d2
@ -97,6 +97,15 @@ import java.util.concurrent.TimeUnit;
|
|||||||
public class IdleStateHandler extends ChannelDuplexHandler {
|
public class IdleStateHandler extends ChannelDuplexHandler {
|
||||||
private static final long MIN_TIMEOUT_NANOS = TimeUnit.MILLISECONDS.toNanos(1);
|
private static final long MIN_TIMEOUT_NANOS = TimeUnit.MILLISECONDS.toNanos(1);
|
||||||
|
|
||||||
|
// Not create a new ChannelFutureListener per write operation to reduce GC pressure.
|
||||||
|
private final ChannelFutureListener writeListener = new ChannelFutureListener() {
|
||||||
|
@Override
|
||||||
|
public void operationComplete(ChannelFuture future) throws Exception {
|
||||||
|
lastWriteTime = System.nanoTime();
|
||||||
|
firstWriterIdleEvent = firstAllIdleEvent = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
private final long readerIdleTimeNanos;
|
private final long readerIdleTimeNanos;
|
||||||
private final long writerIdleTimeNanos;
|
private final long writerIdleTimeNanos;
|
||||||
private final long allIdleTimeNanos;
|
private final long allIdleTimeNanos;
|
||||||
@ -113,6 +122,7 @@ public class IdleStateHandler extends ChannelDuplexHandler {
|
|||||||
private boolean firstAllIdleEvent = true;
|
private boolean firstAllIdleEvent = true;
|
||||||
|
|
||||||
private volatile int state; // 0 - none, 1 - initialized, 2 - destroyed
|
private volatile int state; // 0 - none, 1 - initialized, 2 - destroyed
|
||||||
|
private volatile boolean reading;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance firing {@link IdleStateEvent}s.
|
* Creates a new instance firing {@link IdleStateEvent}s.
|
||||||
@ -249,23 +259,28 @@ public class IdleStateHandler extends ChannelDuplexHandler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
|
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||||
lastReadTime = System.nanoTime();
|
if (readerIdleTimeNanos > 0 || allIdleTimeNanos > 0) {
|
||||||
|
reading = true;
|
||||||
firstReaderIdleEvent = firstAllIdleEvent = true;
|
firstReaderIdleEvent = firstAllIdleEvent = true;
|
||||||
|
}
|
||||||
ctx.fireChannelRead(msg);
|
ctx.fireChannelRead(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
|
||||||
|
if (readerIdleTimeNanos > 0 || allIdleTimeNanos > 0) {
|
||||||
|
lastReadTime = System.nanoTime();
|
||||||
|
reading = false;
|
||||||
|
}
|
||||||
|
ctx.fireChannelReadComplete();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
|
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
|
||||||
// Allow writing with void promise if handler is only configured for read timeout events.
|
// Allow writing with void promise if handler is only configured for read timeout events.
|
||||||
if (writerIdleTimeNanos > 0 || allIdleTimeNanos > 0) {
|
if (writerIdleTimeNanos > 0 || allIdleTimeNanos > 0) {
|
||||||
ChannelPromise unvoid = promise.unvoid();
|
ChannelPromise unvoid = promise.unvoid();
|
||||||
unvoid.addListener(new ChannelFutureListener() {
|
unvoid.addListener(writeListener);
|
||||||
@Override
|
|
||||||
public void operationComplete(ChannelFuture future) throws Exception {
|
|
||||||
lastWriteTime = System.nanoTime();
|
|
||||||
firstWriterIdleEvent = firstAllIdleEvent = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
ctx.write(msg, unvoid);
|
ctx.write(msg, unvoid);
|
||||||
} else {
|
} else {
|
||||||
ctx.write(msg, promise);
|
ctx.write(msg, promise);
|
||||||
@ -342,9 +357,11 @@ public class IdleStateHandler extends ChannelDuplexHandler {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
long currentTime = System.nanoTime();
|
long nextDelay = readerIdleTimeNanos;
|
||||||
long lastReadTime = IdleStateHandler.this.lastReadTime;
|
if (!reading) {
|
||||||
long nextDelay = readerIdleTimeNanos - (currentTime - lastReadTime);
|
nextDelay -= System.nanoTime() - lastReadTime;
|
||||||
|
}
|
||||||
|
|
||||||
if (nextDelay <= 0) {
|
if (nextDelay <= 0) {
|
||||||
// Reader is idle - set a new timeout and notify the callback.
|
// Reader is idle - set a new timeout and notify the callback.
|
||||||
readerIdleTimeout =
|
readerIdleTimeout =
|
||||||
@ -382,9 +399,8 @@ public class IdleStateHandler extends ChannelDuplexHandler {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
long currentTime = System.nanoTime();
|
|
||||||
long lastWriteTime = IdleStateHandler.this.lastWriteTime;
|
long lastWriteTime = IdleStateHandler.this.lastWriteTime;
|
||||||
long nextDelay = writerIdleTimeNanos - (currentTime - lastWriteTime);
|
long nextDelay = writerIdleTimeNanos - (System.nanoTime() - lastWriteTime);
|
||||||
if (nextDelay <= 0) {
|
if (nextDelay <= 0) {
|
||||||
// Writer is idle - set a new timeout and notify the callback.
|
// Writer is idle - set a new timeout and notify the callback.
|
||||||
writerIdleTimeout = ctx.executor().schedule(
|
writerIdleTimeout = ctx.executor().schedule(
|
||||||
@ -422,9 +438,10 @@ public class IdleStateHandler extends ChannelDuplexHandler {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
long currentTime = System.nanoTime();
|
long nextDelay = allIdleTimeNanos;
|
||||||
long lastIoTime = Math.max(lastReadTime, lastWriteTime);
|
if (!reading) {
|
||||||
long nextDelay = allIdleTimeNanos - (currentTime - lastIoTime);
|
nextDelay -= System.nanoTime() - Math.max(lastReadTime, lastWriteTime);
|
||||||
|
}
|
||||||
if (nextDelay <= 0) {
|
if (nextDelay <= 0) {
|
||||||
// Both reader and writer are idle - set a new timeout and
|
// Both reader and writer are idle - set a new timeout and
|
||||||
// notify the callback.
|
// notify the callback.
|
||||||
|
Loading…
Reference in New Issue
Block a user