diff --git a/handler/src/main/java/io/netty/handler/traffic/AbstractTrafficShapingHandler.java b/handler/src/main/java/io/netty/handler/traffic/AbstractTrafficShapingHandler.java index 4457a52761..51784beaf7 100644 --- a/handler/src/main/java/io/netty/handler/traffic/AbstractTrafficShapingHandler.java +++ b/handler/src/main/java/io/netty/handler/traffic/AbstractTrafficShapingHandler.java @@ -18,7 +18,9 @@ package io.netty.handler.traffic; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufHolder; import io.netty.channel.ChannelHandlerAdapter; +import io.netty.channel.ChannelConfig; import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelOutboundBuffer; import io.netty.channel.ChannelPromise; import io.netty.util.Attribute; import io.netty.util.AttributeKey; @@ -28,16 +30,15 @@ import io.netty.util.internal.logging.InternalLoggerFactory; import java.util.concurrent.TimeUnit; /** - * AbstractTrafficShapingHandler allows to limit the global bandwidth + *
AbstractTrafficShapingHandler allows to limit the global bandwidth
* (see {@link GlobalTrafficShapingHandler}) or per session
* bandwidth (see {@link ChannelTrafficShapingHandler}), as traffic shaping.
* It allows you to implement an almost real time monitoring of the bandwidth using
* the monitors from {@link TrafficCounter} that will call back every checkInterval
- * the method doAccounting of this handler.
- *
+ * the method doAccounting of this handler.
If you want for any particular reasons to stop the monitoring (accounting) or to change + * the read/write limit or the check interval, several methods allow that for you:
*Note the change will be taken as best effort, meaning + * that all already scheduled traffics will not be + * changed, but only applied to new traffics.
+ *So the expected usage of this method is to be used not too often, + * accordingly to the traffic shaping configuration.
* * @param newWriteLimit The new write limit (in bytes) * @param newReadLimit The new read limit (in bytes) @@ -180,6 +246,11 @@ public abstract class AbstractTrafficShapingHandler extends ChannelHandlerAdapte /** * Change the underlying limitations. + *Note the change will be taken as best effort, meaning + * that all already scheduled traffics will not be + * changed, but only applied to new traffics.
+ *So the expected usage of this method is to be used not too often, + * accordingly to the traffic shaping configuration.
* * @param newWriteLimit The new write limit (in bytes) * @param newReadLimit The new read limit (in bytes) @@ -188,7 +259,7 @@ public abstract class AbstractTrafficShapingHandler extends ChannelHandlerAdapte writeLimit = newWriteLimit; readLimit = newReadLimit; if (trafficCounter != null) { - trafficCounter.resetAccounting(System.currentTimeMillis() + 1); + trafficCounter.resetAccounting(TrafficCounter.milliSecondFromNano()); } } @@ -212,12 +283,18 @@ public abstract class AbstractTrafficShapingHandler extends ChannelHandlerAdapte } /** + *Note the change will be taken as best effort, meaning + * that all already scheduled traffics will not be + * changed, but only applied to new traffics.
+ *So the expected usage of this method is to be used not too often, + * accordingly to the traffic shaping configuration.
+ * * @param writeLimit the writeLimit to set */ public void setWriteLimit(long writeLimit) { this.writeLimit = writeLimit; if (trafficCounter != null) { - trafficCounter.resetAccounting(System.currentTimeMillis() + 1); + trafficCounter.resetAccounting(TrafficCounter.milliSecondFromNano()); } } @@ -229,12 +306,18 @@ public abstract class AbstractTrafficShapingHandler extends ChannelHandlerAdapte } /** + *Note the change will be taken as best effort, meaning + * that all already scheduled traffics will not be + * changed, but only applied to new traffics.
+ *So the expected usage of this method is to be used not too often, + * accordingly to the traffic shaping configuration.
+ * * @param readLimit the readLimit to set */ public void setReadLimit(long readLimit) { this.readLimit = readLimit; if (trafficCounter != null) { - trafficCounter.resetAccounting(System.currentTimeMillis() + 1); + trafficCounter.resetAccounting(TrafficCounter.milliSecondFromNano()); } } @@ -246,7 +329,7 @@ public abstract class AbstractTrafficShapingHandler extends ChannelHandlerAdapte } /** - * @param checkInterval the checkInterval to set + * @param checkInterval the interval in ms between each step check to set, default value beeing 1000 ms. */ public void setCheckInterval(long checkInterval) { this.checkInterval = checkInterval; @@ -256,21 +339,77 @@ public abstract class AbstractTrafficShapingHandler extends ChannelHandlerAdapte } /** + *Note the change will be taken as best effort, meaning + * that all already scheduled traffics will not be + * changed, but only applied to new traffics.
+ *So the expected usage of this method is to be used not too often, + * accordingly to the traffic shaping configuration.
* * @param maxTime - * Max delay in wait, shall be less than TIME OUT in related protocol + * Max delay in wait, shall be less than TIME OUT in related protocol. + * Must be positive. */ public void setMaxTimeWait(long maxTime) { + if (maxTime <= 0) { + throw new IllegalArgumentException("maxTime must be positive"); + } this.maxTime = maxTime; } /** - * @return the max delay in wait + * @return the max delay in wait to prevent TIME OUT */ public long getMaxTimeWait() { return maxTime; } + /** + * @return the maxWriteDelay + */ + public long getMaxWriteDelay() { + return maxWriteDelay; + } + + /** + *Note the change will be taken as best effort, meaning + * that all already scheduled traffics will not be + * changed, but only applied to new traffics.
+ *So the expected usage of this method is to be used not too often, + * accordingly to the traffic shaping configuration.
+ * + * @param maxWriteDelay the maximum Write Delay in ms in the buffer allowed before write suspension is set. + * Must be positive. + */ + public void setMaxWriteDelay(long maxWriteDelay) { + if (maxWriteDelay <= 0) { + throw new IllegalArgumentException("maxWriteDelay must be positive"); + } + this.maxWriteDelay = maxWriteDelay; + } + + /** + * @return the maxWriteSize default being {@value #DEFAULT_MAX_SIZE} bytes. + */ + public long getMaxWriteSize() { + return maxWriteSize; + } + + /** + *Note that this limit is a best effort on memory limitation to prevent Out Of + * Memory Exception. To ensure it works, the handler generating the write should + * use one of the way provided by Netty to handle the capacity:
+ *- the Channel.isWritable()
property and the corresponding
+ * channelWritabilityChanged()
- the ChannelFuture.addListener(new GenericFutureListener())
This implementation of the {@link AbstractTrafficShapingHandler} is for channel + * traffic shaping, that is to say a per channel limitation of the bandwidth.
+ *Note the index used in OutboundBuffer.setUserDefinedWritability(index, boolean)
is 1.
The general use should be as follow:
*Add in your pipeline a new ChannelTrafficShapingHandler.
+ *ChannelTrafficShapingHandler myHandler = new ChannelTrafficShapingHandler();
+ *pipeline.addLast(myHandler);
* - * Note that this handler has a Pipeline Coverage of "one" which means a new handler must be created - * for each new channel as the counter cannot be shared among all channels..Note that this handler has a Pipeline Coverage of "one" which means a new handler must be created + * for each new channel as the counter cannot be shared among all channels..
* - * Other arguments can be passed like write or read limitation (in bytes/s where 0 means no limitation) + *Other arguments can be passed like write or read limitation (in bytes/s where 0 means no limitation)
* or the check interval (in millisecond) that represents the delay between two computations of the
- * bandwidth and so the call back of the doAccounting method (0 means no accounting at all).
+ * bandwidth and so the call back of the doAccounting method (0 means no accounting at all).
A value of 0 means no accounting for checkInterval. If you need traffic shaping but no such accounting,
* it is recommended to set a positive value, even if it is high since the precision of the
* Traffic Shaping depends on the period where the traffic is computed. The highest the interval,
* the less precise the traffic shaping will be. It is suggested as higher value something close
- * to 5 or 10 minutes.
+ * to 5 or 10 minutes.
maxTimeToWait, by default set to 15s, allows to specify an upper bound of time shaping.
*channel.isWritable()
and
+ * channelWritabilityChanged(ctx)
to handle writability, or through
+ * future.addListener(new GenericFutureListener())
on the future returned by
+ * ctx.write()
.You shall also consider to have object size in read or write operations relatively adapted to + * the bandwidth you required: for instance having 10 MB objects for 10KB/s will lead to burst effect, + * while having 100 KB objects for 1 MB/s should be smoothly handle by this TrafficShaping handler.
Some configuration methods will be taken as best effort, meaning + * that all already scheduled traffics will not be + * changed, but only applied to new traffics.
+ *So the expected usage of those methods are to be used not too often, + * accordingly to the traffic shaping configuration.
channel.isWritable()
and
+ * channelWritabilityChanged(ctx)
to handle writability, or through
+ * future.addListener(new GenericFutureListener())
on the future returned by
+ * ctx.write()
.This implementation of the {@link AbstractTrafficShapingHandler} is for global
* traffic shaping, that is to say a global limitation of the bandwidth, whatever
- * the number of opened channels.
+ * the number of opened channels.
Note the index used in OutboundBuffer.setUserDefinedWritability(index, boolean)
is 2.
The general use should be as follow:
*Create your unique GlobalTrafficShapingHandler like:
+ *GlobalTrafficShapingHandler myHandler = new GlobalTrafficShapingHandler(executor);
+ *The executor could be the underlying IO worker pool
+ *pipeline.addLast(myHandler);
* - * Note that this handler has a Pipeline Coverage of "all" which means only one such handler must be created - * and shared among all channels as the counter must be shared among all channels.Note that this handler has a Pipeline Coverage of "all" which means only one such handler must be created + * and shared among all channels as the counter must be shared among all channels.
* - * Other arguments can be passed like write or read limitation (in bytes/s where 0 means no limitation) + *Other arguments can be passed like write or read limitation (in bytes/s where 0 means no limitation)
* or the check interval (in millisecond) that represents the delay between two computations of the
- * bandwidth and so the call back of the doAccounting method (0 means no accounting at all).
+ * bandwidth and so the call back of the doAccounting method (0 means no accounting at all).
A value of 0 means no accounting for checkInterval. If you need traffic shaping but no such accounting,
* it is recommended to set a positive value, even if it is high since the precision of the
* Traffic Shaping depends on the period where the traffic is computed. The highest the interval,
* the less precise the traffic shaping will be. It is suggested as higher value something close
- * to 5 or 10 minutes.
+ * to 5 or 10 minutes.
maxTimeToWait, by default set to 15s, allows to specify an upper bound of time shaping.
*channel.isWritable()
and
+ * channelWritabilityChanged(ctx)
to handle writability, or through
+ * future.addListener(new GenericFutureListener())
on the future returned by
+ * ctx.write()
.You shall also consider to have object size in read or write operations relatively adapted to + * the bandwidth you required: for instance having 10 MB objects for 10KB/s will lead to burst effect, + * while having 100 KB objects for 1 MB/s should be smoothly handle by this TrafficShaping handler.
Some configuration methods will be taken as best effort, meaning + * that all already scheduled traffics will not be + * changed, but only applied to new traffics.
+ * So the expected usage of those methods are to be used not too often, + * accordingly to the traffic shaping configuration.