View Javadoc

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 }