Introduce HashedWheelTimer.pendingTimeouts()

Motivation:

Fixes #6681.

Modification:

For the sake of better timer observability, expose the number of pending timeouts through the new HashedWheelTimer.pendingTimeouts method .

Result:

It's now ridiculously easy to observe Netty timer's very basic and yet important metric, the number of pending tasks/timeouts.
This commit is contained in:
Vladimir Kostyukov 2017-04-27 23:42:18 -07:00 committed by Norman Maurer
parent 6915ec3bb9
commit ed37cf20ef
2 changed files with 32 additions and 15 deletions

View File

@ -16,7 +16,6 @@
package io.netty.util; package io.netty.util;
import io.netty.util.internal.PlatformDependent; import io.netty.util.internal.PlatformDependent;
import io.netty.util.internal.StringUtil;
import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory; import io.netty.util.internal.logging.InternalLoggerFactory;
@ -405,15 +404,15 @@ public class HashedWheelTimer implements Timer {
if (unit == null) { if (unit == null) {
throw new NullPointerException("unit"); throw new NullPointerException("unit");
} }
if (shouldLimitTimeouts()) {
long pendingTimeoutsCount = pendingTimeouts.incrementAndGet(); long pendingTimeoutsCount = pendingTimeouts.incrementAndGet();
if (pendingTimeoutsCount > maxPendingTimeouts) {
if (maxPendingTimeouts > 0 && pendingTimeoutsCount > maxPendingTimeouts) {
pendingTimeouts.decrementAndGet(); pendingTimeouts.decrementAndGet();
throw new RejectedExecutionException("Number of pending timeouts (" throw new RejectedExecutionException("Number of pending timeouts ("
+ pendingTimeoutsCount + ") is greater than or equal to maximum allowed pending " + pendingTimeoutsCount + ") is greater than or equal to maximum allowed pending "
+ "timeouts (" + maxPendingTimeouts + ")"); + "timeouts (" + maxPendingTimeouts + ")");
} }
}
start(); start();
@ -425,8 +424,11 @@ public class HashedWheelTimer implements Timer {
return timeout; return timeout;
} }
private boolean shouldLimitTimeouts() { /**
return maxPendingTimeouts > 0; * Returns the number of pending timeouts of this {@link Timer}.
*/
public long pendingTimeouts() {
return pendingTimeouts.get();
} }
private static void reportTooManyInstances() { private static void reportTooManyInstances() {
@ -629,7 +631,7 @@ public class HashedWheelTimer implements Timer {
HashedWheelBucket bucket = this.bucket; HashedWheelBucket bucket = this.bucket;
if (bucket != null) { if (bucket != null) {
bucket.remove(this); bucket.remove(this);
} else if (timer.shouldLimitTimeouts()) { } else {
timer.pendingTimeouts.decrementAndGet(); timer.pendingTimeouts.decrementAndGet();
} }
} }
@ -774,9 +776,7 @@ public class HashedWheelTimer implements Timer {
timeout.prev = null; timeout.prev = null;
timeout.next = null; timeout.next = null;
timeout.bucket = null; timeout.bucket = null;
if (timeout.timer.shouldLimitTimeouts()) {
timeout.timer.pendingTimeouts.decrementAndGet(); timeout.timer.pendingTimeouts.decrementAndGet();
}
return next; return next;
} }

View File

@ -213,6 +213,23 @@ public class HashedWheelTimerTest {
timer.stop(); timer.stop();
} }
@Test()
public void reportPendingTimeouts() throws InterruptedException {
final CountDownLatch latch = new CountDownLatch(1);
final HashedWheelTimer timer = new HashedWheelTimer();
final Timeout t1 = timer.newTimeout(createNoOpTimerTask(), 100, TimeUnit.MINUTES);
final Timeout t2 = timer.newTimeout(createNoOpTimerTask(), 100, TimeUnit.MINUTES);
timer.newTimeout(createCountDownLatchTimerTask(latch), 90, TimeUnit.MILLISECONDS);
assertEquals(3, timer.pendingTimeouts());
t1.cancel();
t2.cancel();
latch.await();
assertEquals(0, timer.pendingTimeouts());
timer.stop();
}
private static TimerTask createNoOpTimerTask() { private static TimerTask createNoOpTimerTask() {
return new TimerTask() { return new TimerTask() {
@Override @Override