Add a new constructor without handler parameter to TrafficCounter

Related: #3476

Motivation:

Some users use TrafficCounter for other uses than we originally
intended, such as implementing their own traffic shaper.  In such a
case, a user does not want to specify an AbstractTrafficShapingHandler.

Modifications:

- Add a new constructor that does not require an
  AbstractTrafficShapingHandler, so that a user can use it without it.
- Simplify TrafficMonitoringTask
- Javadoc cleanup

Result:

We open the possibility of using TrafficCounter for other purposes than
just using it with AbstractTrafficShapingHandler.  Eventually, we could
generalize it a little bit more, so that we can potentially use it for
other uses.
This commit is contained in:
Trustin Lee 2015-03-09 15:14:47 +09:00
parent 96cb879054
commit 54293003ef

View File

@ -21,20 +21,16 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
/**
* TrafficCounter is associated with {@link AbstractTrafficShapingHandler}.
*
* Counts the number of read and written bytes for rate-limiting traffic.
* <p>
* A <tt>TrafficCounter</tt> counts the read and written bytes such that the {@link AbstractTrafficShapingHandler}
* can limit the traffic, globally or per channel.
* </p>
*
* <p>
* It computes the statistics for both read and written every {@link #checkInterval}, and calls back to its parent
* {@link AbstractTrafficShapingHandler#doAccounting} method. If the checkInterval is set to 0, no accounting will be
* done and statistics will only be computed at each receive or write operation.
* It computes the statistics for both inbound and outbound traffic periodically at the given
* {@code checkInterval}, and calls the {@link AbstractTrafficShapingHandler#doAccounting(TrafficCounter)} method back.
* If the {@code checkInterval} is {@code 0}, no accounting will be done and statistics will only be computed at each
* receive or write operation.
* </p>
*/
public class TrafficCounter {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(TrafficCounter.class);
/**
@ -163,39 +159,17 @@ public class TrafficCounter {
* Class to implement monitoring at fix delay
*
*/
private static class TrafficMonitoringTask implements Runnable {
/**
* The associated TrafficShapingHandler
*/
private final AbstractTrafficShapingHandler trafficShapingHandler1;
/**
* The associated TrafficCounter
*/
private final TrafficCounter counter;
/**
* @param trafficShapingHandler
* The parent handler to which this task needs to callback to for accounting.
* @param counter
* The parent TrafficCounter that we need to reset the statistics for.
*/
protected TrafficMonitoringTask(AbstractTrafficShapingHandler trafficShapingHandler, TrafficCounter counter) {
trafficShapingHandler1 = trafficShapingHandler;
this.counter = counter;
}
private final class TrafficMonitoringTask implements Runnable {
@Override
public void run() {
if (!counter.monitorActive) {
if (!monitorActive) {
return;
}
counter.resetAccounting(milliSecondFromNano());
if (trafficShapingHandler1 != null) {
trafficShapingHandler1.doAccounting(counter);
resetAccounting(milliSecondFromNano());
if (trafficShapingHandler != null) {
trafficShapingHandler.doAccounting(TrafficCounter.this);
}
counter.scheduledFuture = counter.executor.schedule(this, counter.checkInterval.get(),
TimeUnit.MILLISECONDS);
scheduledFuture = executor.schedule(this, checkInterval.get(), TimeUnit.MILLISECONDS);
}
}
@ -211,7 +185,7 @@ public class TrafficCounter {
// if executor is null, it means it is piloted by a GlobalChannelTrafficCounter, so no executor
if (localCheckInterval > 0 && executor != null) {
monitorActive = true;
monitor = new TrafficMonitoringTask(trafficShapingHandler, this);
monitor = new TrafficMonitoringTask();
scheduledFuture =
executor.schedule(monitor, localCheckInterval, TimeUnit.MILLISECONDS);
}
@ -259,6 +233,33 @@ public class TrafficCounter {
lastReadingTime = Math.max(lastReadingTime, readingTime);
}
/**
* Constructor with the {@link AbstractTrafficShapingHandler} that hosts it, the {@link ScheduledExecutorService}
* to use, its name, the checkInterval between two computations in milliseconds.
*
* @param executor
* the underlying executor service for scheduling checks, might be null when used
* from {@link GlobalChannelTrafficCounter}.
* @param name
* the name given to this monitor.
* @param checkInterval
* the checkInterval in millisecond between two computations.
*/
public TrafficCounter(ScheduledExecutorService executor, String name, long checkInterval) {
if (executor == null) {
throw new NullPointerException("executor");
}
if (name == null) {
throw new NullPointerException("name");
}
trafficShapingHandler = null;
this.executor = executor;
this.name = name;
init(checkInterval);
}
/**
* Constructor with the {@link AbstractTrafficShapingHandler} that hosts it, the Timer to use, its
* name, the checkInterval between two computations in millisecond.
@ -273,14 +274,28 @@ public class TrafficCounter {
* @param checkInterval
* the checkInterval in millisecond between two computations.
*/
public TrafficCounter(AbstractTrafficShapingHandler trafficShapingHandler, ScheduledExecutorService executor,
public TrafficCounter(
AbstractTrafficShapingHandler trafficShapingHandler, ScheduledExecutorService executor,
String name, long checkInterval) {
if (trafficShapingHandler == null) {
throw new IllegalArgumentException("TrafficShapingHandler must not be null");
throw new IllegalArgumentException("trafficShapingHandler");
}
if (executor == null) {
throw new NullPointerException("executor");
}
if (name == null) {
throw new NullPointerException("name");
}
this.trafficShapingHandler = trafficShapingHandler;
this.executor = executor;
this.name = name;
init(checkInterval);
}
private void init(long checkInterval) {
// absolute time: informative only
lastCumulativeTime = System.currentTimeMillis();
writingTime = milliSecondFromNano();
@ -337,8 +352,6 @@ public class TrafficCounter {
*
* @param write
* the size in bytes to write
* @param schedule
* the time when this write was scheduled
*/
void bytesRealWriteFlowControl(long write) {
realWrittenBytes.addAndGet(write);