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