001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018 package org.apache.hadoop.hdfs.util;
019
020 import static org.apache.hadoop.util.Time.monotonicNow;
021
022 /**
023 * a class to throttle the data transfers.
024 * This class is thread safe. It can be shared by multiple threads.
025 * The parameter bandwidthPerSec specifies the total bandwidth shared by
026 * threads.
027 */
028 public class DataTransferThrottler {
029 private long period; // period over which bw is imposed
030 private long periodExtension; // Max period over which bw accumulates.
031 private long bytesPerPeriod; // total number of bytes can be sent in each period
032 private long curPeriodStart; // current period starting time
033 private long curReserve; // remaining bytes can be sent in the period
034 private long bytesAlreadyUsed;
035
036 /** Constructor
037 * @param bandwidthPerSec bandwidth allowed in bytes per second.
038 */
039 public DataTransferThrottler(long bandwidthPerSec) {
040 this(500, bandwidthPerSec); // by default throttling period is 500ms
041 }
042
043 /**
044 * Constructor
045 * @param period in milliseconds. Bandwidth is enforced over this
046 * period.
047 * @param bandwidthPerSec bandwidth allowed in bytes per second.
048 */
049 public DataTransferThrottler(long period, long bandwidthPerSec) {
050 this.curPeriodStart = monotonicNow();
051 this.period = period;
052 this.curReserve = this.bytesPerPeriod = bandwidthPerSec*period/1000;
053 this.periodExtension = period*3;
054 }
055
056 /**
057 * @return current throttle bandwidth in bytes per second.
058 */
059 public synchronized long getBandwidth() {
060 return bytesPerPeriod*1000/period;
061 }
062
063 /**
064 * Sets throttle bandwidth. This takes affect latest by the end of current
065 * period.
066 *
067 * @param bytesPerSecond
068 */
069 public synchronized void setBandwidth(long bytesPerSecond) {
070 if ( bytesPerSecond <= 0 ) {
071 throw new IllegalArgumentException("" + bytesPerSecond);
072 }
073 bytesPerPeriod = bytesPerSecond*period/1000;
074 }
075
076 /** Given the numOfBytes sent/received since last time throttle was called,
077 * make the current thread sleep if I/O rate is too fast
078 * compared to the given bandwidth.
079 *
080 * @param numOfBytes
081 * number of bytes sent/received since last time throttle was called
082 */
083 public synchronized void throttle(long numOfBytes) {
084 if ( numOfBytes <= 0 ) {
085 return;
086 }
087
088 curReserve -= numOfBytes;
089 bytesAlreadyUsed += numOfBytes;
090
091 while (curReserve <= 0) {
092 long now = monotonicNow();
093 long curPeriodEnd = curPeriodStart + period;
094
095 if ( now < curPeriodEnd ) {
096 // Wait for next period so that curReserve can be increased.
097 try {
098 wait( curPeriodEnd - now );
099 } catch (InterruptedException e) {
100 // Abort throttle and reset interrupted status to make sure other
101 // interrupt handling higher in the call stack executes.
102 Thread.currentThread().interrupt();
103 break;
104 }
105 } else if ( now < (curPeriodStart + periodExtension)) {
106 curPeriodStart = curPeriodEnd;
107 curReserve += bytesPerPeriod;
108 } else {
109 // discard the prev period. Throttler might not have
110 // been used for a long time.
111 curPeriodStart = now;
112 curReserve = bytesPerPeriod - bytesAlreadyUsed;
113 }
114 }
115
116 bytesAlreadyUsed -= numOfBytes;
117 }
118 }