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