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 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 }