View Javadoc

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 }