1 /*
2 * Copyright 2012 The Netty Project
3 *
4 * The Netty Project licenses this file to you under the Apache License,
5 * version 2.0 (the "License"); you may not use this file except in compliance
6 * with the License. You may obtain a copy of the License at:
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations
14 * under the License.
15 */
16 package org.jboss.netty.handler.traffic;
17
18 import java.util.concurrent.TimeUnit;
19 import java.util.concurrent.atomic.AtomicBoolean;
20 import java.util.concurrent.atomic.AtomicLong;
21
22 import org.jboss.netty.channel.ChannelHandlerContext;
23 import org.jboss.netty.util.Timeout;
24 import org.jboss.netty.util.Timer;
25 import org.jboss.netty.util.TimerTask;
26
27 /**
28 * TrafficCounter is associated with {@link AbstractTrafficShapingHandler}.<br>
29 * <br>
30 * A TrafficCounter has for goal to count the traffic in order to enable to limit the traffic or not,
31 * globally or per channel. It compute statistics on read and written bytes at the specified
32 * interval and call back the {@link AbstractTrafficShapingHandler} doAccounting method at every
33 * specified interval. If this interval is set to 0, therefore no accounting will be done and only
34 * statistics will be computed at each receive or write operations.
35 */
36 public class TrafficCounter {
37 /**
38 * Current written bytes
39 */
40 private final AtomicLong currentWrittenBytes = new AtomicLong();
41
42 /**
43 * Current read bytes
44 */
45 private final AtomicLong currentReadBytes = new AtomicLong();
46
47 /**
48 * Long life written bytes
49 */
50 private final AtomicLong cumulativeWrittenBytes = new AtomicLong();
51
52 /**
53 * Long life read bytes
54 */
55 private final AtomicLong cumulativeReadBytes = new AtomicLong();
56
57 /**
58 * Last Time where cumulative bytes where reset to zero
59 */
60 private long lastCumulativeTime;
61
62 /**
63 * Last writing bandwidth
64 */
65 private long lastWriteThroughput;
66
67 /**
68 * Last reading bandwidth
69 */
70 private long lastReadThroughput;
71
72 /**
73 * Last Time Check taken
74 */
75 private final AtomicLong lastTime = new AtomicLong();
76
77 /**
78 * Last written bytes number during last check interval
79 */
80 private long lastWrittenBytes;
81
82 /**
83 * Last read bytes number during last check interval
84 */
85 private long lastReadBytes;
86
87 /**
88 * Delay between two captures
89 */
90 AtomicLong checkInterval = new AtomicLong(
91 AbstractTrafficShapingHandler.DEFAULT_CHECK_INTERVAL);
92
93 // default 1 s
94
95 /**
96 * Name of this Monitor
97 */
98 final String name;
99
100 /**
101 * The associated TrafficShapingHandler
102 */
103 private final AbstractTrafficShapingHandler trafficShapingHandler;
104
105 /**
106 * One Timer for all Counter
107 */
108 private final Timer timer; // replace executor
109 /**
110 * Monitor created once in start()
111 */
112 private TimerTask timerTask;
113 /**
114 * used in stop() to cancel the timer
115 */
116 private volatile Timeout timeout;
117
118 /**
119 * Is Monitor active
120 */
121 AtomicBoolean monitorActive = new AtomicBoolean();
122
123 /**
124 * Class to implement monitoring at fix delay
125 *
126 */
127 private static class TrafficMonitoringTask implements TimerTask {
128 /**
129 * The associated TrafficShapingHandler
130 */
131 private final AbstractTrafficShapingHandler trafficShapingHandler1;
132
133 /**
134 * The associated TrafficCounter
135 */
136 private final TrafficCounter counter;
137
138 /**
139 * @param trafficShapingHandler
140 * @param counter
141 */
142 protected TrafficMonitoringTask(
143 AbstractTrafficShapingHandler trafficShapingHandler,
144 TrafficCounter counter) {
145 trafficShapingHandler1 = trafficShapingHandler;
146 this.counter = counter;
147 }
148
149 public void run(Timeout timeout) throws Exception {
150 if (!counter.monitorActive.get()) {
151 return;
152 }
153 long endTime = System.currentTimeMillis();
154 counter.resetAccounting(endTime);
155 if (trafficShapingHandler1 != null) {
156 trafficShapingHandler1.doAccounting(counter);
157 }
158 timeout =
159 counter.timer.newTimeout(this, counter.checkInterval.get(), TimeUnit.MILLISECONDS);
160 }
161 }
162
163 /**
164 * Start the monitoring process
165 */
166 public void start() {
167 synchronized (lastTime) {
168 if (monitorActive.get()) {
169 return;
170 }
171 lastTime.set(System.currentTimeMillis());
172 if (checkInterval.get() > 0) {
173 monitorActive.set(true);
174 timerTask = new TrafficMonitoringTask(trafficShapingHandler, this);
175 timeout =
176 timer.newTimeout(timerTask, checkInterval.get(), TimeUnit.MILLISECONDS);
177 }
178 }
179 }
180
181 /**
182 * Stop the monitoring process
183 */
184 public void stop() {
185 synchronized (lastTime) {
186 if (!monitorActive.get()) {
187 return;
188 }
189 monitorActive.set(false);
190 resetAccounting(System.currentTimeMillis());
191 if (trafficShapingHandler != null) {
192 trafficShapingHandler.doAccounting(this);
193 }
194 if (timeout != null) {
195 timeout.cancel();
196 }
197 }
198 }
199
200 /**
201 * Reset the accounting on Read and Write
202 *
203 * @param newLastTime
204 */
205 void resetAccounting(long newLastTime) {
206 synchronized (lastTime) {
207 long interval = newLastTime - lastTime.getAndSet(newLastTime);
208 if (interval == 0) {
209 // nothing to do
210 return;
211 }
212 lastReadBytes = currentReadBytes.getAndSet(0);
213 lastWrittenBytes = currentWrittenBytes.getAndSet(0);
214 lastReadThroughput = lastReadBytes / interval * 1000;
215 // nb byte / checkInterval in ms * 1000 (1s)
216 lastWriteThroughput = lastWrittenBytes / interval * 1000;
217 // nb byte / checkInterval in ms * 1000 (1s)
218 }
219 }
220
221 /**
222 * Constructor with the {@link AbstractTrafficShapingHandler} that hosts it, the Timer to use, its
223 * name, the checkInterval between two computations in millisecond
224 * @param trafficShapingHandler the associated AbstractTrafficShapingHandler
225 * @param timer
226 * Could be a HashedWheelTimer
227 * @param name
228 * the name given to this monitor
229 * @param checkInterval
230 * the checkInterval in millisecond between two computations
231 */
232 public TrafficCounter(AbstractTrafficShapingHandler trafficShapingHandler,
233 Timer timer, String name, long checkInterval) {
234 this.trafficShapingHandler = trafficShapingHandler;
235 this.timer = timer;
236 this.name = name;
237 lastCumulativeTime = System.currentTimeMillis();
238 configure(checkInterval);
239 }
240
241 /**
242 * Change checkInterval between
243 * two computations in millisecond
244 *
245 * @param newcheckInterval
246 */
247 public void configure(long newcheckInterval) {
248 long newInterval = newcheckInterval / 10 * 10;
249 if (checkInterval.get() != newInterval) {
250 checkInterval.set(newInterval);
251 if (newInterval <= 0) {
252 stop();
253 // No more active monitoring
254 lastTime.set(System.currentTimeMillis());
255 } else {
256 // Start if necessary
257 start();
258 }
259 }
260 }
261
262 /**
263 * Computes counters for Read.
264 *
265 * @param ctx
266 * the associated channelHandlerContext
267 * @param recv
268 * the size in bytes to read
269 */
270 void bytesRecvFlowControl(ChannelHandlerContext ctx, long recv) {
271 currentReadBytes.addAndGet(recv);
272 cumulativeReadBytes.addAndGet(recv);
273 }
274
275 /**
276 * Computes counters for Write.
277 *
278 * @param write
279 * the size in bytes to write
280 */
281 void bytesWriteFlowControl(long write) {
282 currentWrittenBytes.addAndGet(write);
283 cumulativeWrittenBytes.addAndGet(write);
284 }
285
286 /**
287 *
288 * @return the current checkInterval between two computations of traffic counter
289 * in millisecond
290 */
291 public long getCheckInterval() {
292 return checkInterval.get();
293 }
294
295 /**
296 *
297 * @return the Read Throughput in bytes/s computes in the last check interval
298 */
299 public long getLastReadThroughput() {
300 return lastReadThroughput;
301 }
302
303 /**
304 *
305 * @return the Write Throughput in bytes/s computes in the last check interval
306 */
307 public long getLastWriteThroughput() {
308 return lastWriteThroughput;
309 }
310
311 /**
312 *
313 * @return the number of bytes read during the last check Interval
314 */
315 public long getLastReadBytes() {
316 return lastReadBytes;
317 }
318
319 /**
320 *
321 * @return the number of bytes written during the last check Interval
322 */
323 public long getLastWrittenBytes() {
324 return lastWrittenBytes;
325 }
326
327 /**
328 *
329 * @return the current number of bytes read since the last checkInterval
330 */
331 public long getCurrentReadBytes() {
332 return currentReadBytes.get();
333 }
334
335 /**
336 *
337 * @return the current number of bytes written since the last check Interval
338 */
339 public long getCurrentWrittenBytes() {
340 return currentWrittenBytes.get();
341 }
342
343 /**
344 * @return the Time in millisecond of the last check as of System.currentTimeMillis()
345 */
346 public long getLastTime() {
347 return lastTime.get();
348 }
349
350 /**
351 * @return the cumulativeWrittenBytes
352 */
353 public long getCumulativeWrittenBytes() {
354 return cumulativeWrittenBytes.get();
355 }
356
357 /**
358 * @return the cumulativeReadBytes
359 */
360 public long getCumulativeReadBytes() {
361 return cumulativeReadBytes.get();
362 }
363
364 /**
365 * @return the lastCumulativeTime in millisecond as of System.currentTimeMillis()
366 * when the cumulative counters were reset to 0.
367 */
368 public long getLastCumulativeTime() {
369 return lastCumulativeTime;
370 }
371
372 /**
373 * Reset both read and written cumulative bytes counters and the associated time.
374 */
375 public void resetCumulativeTime() {
376 lastCumulativeTime = System.currentTimeMillis();
377 cumulativeReadBytes.set(0);
378 cumulativeWrittenBytes.set(0);
379 }
380
381 /**
382 * @return the name
383 */
384 public String getName() {
385 return name;
386 }
387
388 /**
389 * String information
390 */
391 @Override
392 public String toString() {
393 return "Monitor " + name + " Current Speed Read: " +
394 (lastReadThroughput >> 10) + " KB/s, Write: " +
395 (lastWriteThroughput >> 10) + " KB/s Current Read: " +
396 (currentReadBytes.get() >> 10) + " KB Current Write: " +
397 (currentWrittenBytes.get() >> 10) + " KB";
398 }
399 }