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 final long period; // period over which bw is imposed 030 private final 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 }